//// FindSkinModifier /////////////////////////////////////////////////////// // Given an INode, gets the ISkin object of that node, or nil if there is // none. Taken from the Max4 SDK, ISkin.h ISkin* plMaxNodeBase::FindSkinModifier() { int modStackIndex; // Get object from node. Abort if no object. Object *pObj = GetObjectRef(); if( pObj == nil ) return nil; // Is derived object ? while( pObj->SuperClassID() == GEN_DERIVOB_CLASS_ID ) { IDerivedObject *pDerObj = (IDerivedObject *)pObj; // Iterate over all entries of the modifier stack. for( modStackIndex = 0; modStackIndex < pDerObj->NumModifiers(); modStackIndex++ ) { // Get current modifier. Modifier *mod = pDerObj->GetModifier( modStackIndex ); // Is this Skin ? if( mod->ClassID() == SKIN_CLASSID ) { ISkin* skin = (ISkin*)mod->GetInterface(I_SKIN); if( skin->GetNumBones() > 0 ) return skin; } } pObj = pDerObj->GetObjRef(); } // Not found. return nil; }
bool Exporter::makeSkin(NiTriBasedGeomRef shape, INode *node, FaceGroup &grp, TimeValue t) { if (!mExportSkin) return false; if (grp.verts.empty()) return false; //get the skin modifier Modifier *mod = GetSkin(node); if (!mod) return false; ISkin *skin = (ISkin *) mod->GetInterface(I_SKIN); if (!skin) return false; ISkinContextData *skinData = skin->GetContextInterface(node); if (!skinData) return false; if (grp.strips.empty()) strippify(grp); // Create new call back to finish export SkinInstance* si = new SkinInstance(this); mPostExportCallbacks.push_back(si); skin->GetSkinInitTM(node, si->bone_init_tm, false); skin->GetSkinInitTM(node, si->node_init_tm, true); si->shape = shape; // Get bone references (may not actually exist in proper structure at this time) int totalBones = skin->GetNumBones(); si->boneWeights.resize(totalBones); si->boneList.resize(totalBones); for (int i=0; i<totalBones; ++i) { si->boneList[i] = getNode(skin->GetBone(i)); } vector<int>& vidx = grp.vidx; int nv = vidx.size(); for (int i=0; i<nv; ++i) { int vi = vidx[i]; int nbones = skinData->GetNumAssignedBones(vi); for (int j=0; j<nbones; ++j) { SkinWeight sw; sw.index = i; sw.weight = skinData->GetBoneWeight(vi,j); int boneIndex = skinData->GetAssignedBone(vi,j); SkinInstance::SkinWeightList& weights = si->boneWeights[boneIndex]; weights.push_back(sw); } } // remove unused bones vector<NiNodeRef>::iterator bitr = si->boneList.begin(); SkinInstance::BoneWeightList::iterator switr = si->boneWeights.begin(); for (int i=0; i<totalBones; ++i) { vector<SkinWeight> &weights = (*switr); if (weights.empty()) { bitr = si->boneList.erase(bitr); switr = si->boneWeights.erase(switr); } else { ++bitr, ++switr; } } // Check for dismemberment if (IsFallout3() || IsSkyrim()) { Modifier *dismemberSkinMod = GetBSDismemberSkin(node); if (dismemberSkinMod) { if (IBSDismemberSkinModifier *disSkin = (IBSDismemberSkinModifier *) dismemberSkinMod->GetInterface(I_BSDISMEMBERSKINMODIFIER)){ Tab<IBSDismemberSkinModifierData*> modData = disSkin->GetModifierData(); if (modData.Count() >= 1) { IBSDismemberSkinModifierData* bsdsmd = modData[0]; si->SkinInstConstructor = BSDismemberSkinInstance::Create; Tab<BSDSPartitionData> &flags = bsdsmd->GetPartitionFlags(); GenericNamedSelSetList &fselSet = bsdsmd->GetFaceSelList(); FaceMap fmap; NiTriBasedGeomDataRef data = DynamicCast<NiTriBasedGeomData>(shape->GetData()); vector<Triangle> tris = data->GetTriangles(); for (int i=0; i<tris.size(); ++i) { Triangle tri = tris[i]; fmap[ rotate(tri) ] = i; } // Build up list of partitions and face to partition map si->partitions.resize(flags.Count()); si->facePartList.resize( grp.faces.size(), -1 ); for (int i=0; i<flags.Count(); ++i) { BodyPartList& bp = si->partitions[i]; bp.bodyPart = (BSDismemberBodyPartType)flags[i].bodyPart; bp.partFlag = (BSPartFlag)(flags[i].partFlag | PF_START_NET_BONESET); BitArray& fSelect = fselSet[i]; for (int j=0; j<fSelect.GetSize(); ++j){ if ( fSelect[j] ) { Triangle tri = grp.faces[grp.fidx[j]]; FaceMap::iterator fitr = fmap.find( rotate(tri) ); if (fitr != fmap.end()) si->facePartList[ (*fitr).second ] = i; } } } } } } } return true; }
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; } }
//---------------------------------------------------------------------------- void SceneBuilder::ProcessSkin(INode *node, Modifier *skinMod) { // 构造皮肤控制器。如果Max的网格被按照材质细分,每一个网格都需要自己的蒙皮 // 信息控制器。蒙皮信息中的offset,在动画起始时被计算,是骨骼的世界变换。 // // node: // 指向蒙皮修改器指向的Max中的节点。 // skinMod: // 指向蒙皮修改器 // 1. 获得max蒙皮信息中的骨骼,对应的在Phoenix的骨骼节点列表 // 2. 获得maxNode影响的Phoenix网格 // 3. 获得max中每个骨骼所影响的Phoenix网格中的顶点的数量,忽略不受蒙皮信息 // 影响的网格 // 4. 计算Phoenix mesh的蒙皮信息,生成SkinControl,AttachController到 // Phoenix mesh上。 // 1 bool needDel; TriObject *triObj = GetTriObject(node, &needDel); Mesh *maxMesh = &triObj->GetMesh(); // Max皮肤控制器接口 ISkin *skin = (ISkin*)skinMod->GetInterface(I_SKIN); ISkinContextData *skinData = skin->GetContextInterface(node); // max Skin Bones -> Phoenix2 Skin Bones int b, numSkinBone = skin->GetNumBones(); PX2::Node **bones = new1<PX2::Node*>(numSkinBone); for (b=0; b<numSkinBone; b++) { INode *boneNode = skin->GetBone(b); const std::string &boneName = boneNode->GetName(); PX2::Node *node = PX2::StaticCast<PX2::Node>(mScene->GetObjectByName(boneName)); bones[b] = node; } // 1 // 获得maxNode相关联的Phoenix mesh std::vector<PX2::TriMesh*> meshes; PX2::Object *object = mScene->GetObjectByName(node->GetName()); if (object->IsExactly(PX2::TriMesh::TYPE)) { meshes.push_back(PX2::StaticCast<PX2::TriMesh>(object)); } else { PX2::Node *node = PX2::StaticCast<PX2::Node>(object); const char *nName = node->GetName().c_str(); for (int c=0; c<node->GetNumChildren(); c++) { PX2::Movable *child = node->GetChild(c); const char *cName = child->GetName().c_str(); if (strncmp(cName, nName, strlen(nName)) == 0) // 这里必须是strlen(nName),因为子节点有_1,_2 { meshes.push_back(PX2::StaticCast<PX2::TriMesh>(child)); } } } // 为Phoenix2的每个网格建立相关的皮肤控制器 int *boneInfuseNumVert = new1<int>(numSkinBone); for (int m=0; m<(int)meshes.size(); m++) { PX2::TriMesh *mesh = meshes[m]; // Phoenix顶点在max顶点中的索引 PX2::VertexBuffer *vb = mesh->GetVertexBuffer(); int px2MeshVertexNum = vb->GetNumElements(); std::vector<int> MaxVertexIndex; // i->max索引 int v, i, j, k; PX2::VertexBufferAccessor vba(mesh->GetVertexFormat(), vb); // 3 for (int v=0; v<px2MeshVertexNum; ++v) { Float3 &position = vba.Position<Float3>(v); for (i=0; i<maxMesh->getNumVerts(); i++) { if (position[0] == maxMesh->verts[i].x && position[1] == maxMesh->verts[i].y && position[2] == maxMesh->verts[i].z) { MaxVertexIndex.push_back(i); break; } } } // 确定每个骨骼所影响的顶点数量 int maxVertexSize = (int)MaxVertexIndex.size(); memset(boneInfuseNumVert, 0, sizeof(int)*numSkinBone); for (i=0; i<maxVertexSize; i++) { v = MaxVertexIndex[i]; for (j=0; j<skinData->GetNumAssignedBones(v); j++) { // 这个max中的顶点受到几个骨骼影响啊?! b = skinData->GetAssignedBone(v, j); // 获得这个影响的骨骼索引(这个j是第几个) boneInfuseNumVert[b]++; // 这个骨骼影响的顶点数量++ } } // (通过PX2中的顶点找到Max中的顶点,找到Max中影响该顶点的骨骼) // 如果Max的网格是被按照材质分割的,可能一些骨骼对当前Phoenix2网格没有 // 影响 int bQuantity = 0; // 影响当前Phoenix2网格的骨骼数量 for (b=0; b<numSkinBone; b++) { if (boneInfuseNumVert[b] > 0) bQuantity++; } if (bQuantity == 0) { // Phoenix网格不被任何骨骼影响,进入下一个网格 continue; } // 4 PX2::Node **theBones = new1<PX2::Node*>(bQuantity); float **weight = new2<float>(bQuantity, maxVertexSize); memset(weight[0],0,bQuantity*maxVertexSize*sizeof(float)); PX2::APoint **offset = new2<PX2::APoint>(bQuantity, maxVertexSize); memset(offset[0],0,bQuantity*maxVertexSize*sizeof(PX2::APoint)); PX2::HMatrix *mats = new1<PX2::HMatrix>(bQuantity); // 计算max骨骼到Phoenix骨骼对应的索引(k) std::vector<int> bIArray(numSkinBone); for (b=0, k=0; b<numSkinBone; b++) { if (boneInfuseNumVert[b] > 0) { theBones[k] = bones[b]; // 获取对Mesh有影响的骨骼 bIArray[b] = k; // max bone index -> px2 可用bone index HMatrix boneWorldMat = theBones[k]->WorldTransform.Matrix(); HMatrix meshWorldMat = mesh->WorldTransform.Matrix(); mats[k] = boneWorldMat.Inverse() * meshWorldMat; k++; } } // 遍历顶点,计算顶点权重和offset for (i=0; i<maxVertexSize; i++) { v = MaxVertexIndex[i]; for (j=0; j<skinData->GetNumAssignedBones(v); j++) { // 遍历影响该Max顶点的骨骼 b = skinData->GetAssignedBone(v, j); k = bIArray[b]; float wit = skinData->GetBoneWeight(v, j); // 第j个骨骼的影响权重 weight[i][k] = wit; Float3 &position = vba.Position<Float3>(i); APoint point = theBones[k]->WorldTransform.Inverse() * (mesh->WorldTransform * APoint(position)); offset[i][k] = Float3(point.X(), point.Y(), point.Z()); // 在所受影响骨骼中的位置 } } PX2::SkinController *skinCtrl = new0 PX2::SkinController (maxVertexSize, bQuantity); skinCtrl->SetName("SkinController"); for (int i=0; i<bQuantity; i++) { skinCtrl->GetBones()[i] = theBones[i]; skinCtrl->GetTMMatrixs()[i] = mats[i]; } // offset for (int i=0; i<maxVertexSize; i++) { for (int j=0; j<bQuantity; j++) { skinCtrl->GetWeights()[i][j] = weight[i][j]; skinCtrl->GetOffsets()[i][j] = offset[i][j]; } } // index weights for (int i=0; i<maxVertexSize; i++) { Float4 inds = Float4(0.0f, 0.0f, 0.0f, 0.0f); Float4 wights = Float4(0.0f, 0.0f, 0.0f, 0.0f); In *pSortBone = new In[bQuantity]; for (int j=0; j<bQuantity; j++) { pSortBone[j].index = j; pSortBone[j].data = weight[i][j]; } qsort(pSortBone, bQuantity, sizeof(pSortBone[0]), cmp); int useBoneNum = bQuantity; if (useBoneNum > 4) useBoneNum = 4; float allWeight = 0.0f; for (int useBoneIndex=0; useBoneIndex<useBoneNum; useBoneIndex++) { allWeight += pSortBone[useBoneIndex].data; } if (allWeight <= 0.0f) allWeight = 1.0f; for (int useBoneIndex=0; useBoneIndex<useBoneNum; useBoneIndex++) { inds[useBoneIndex] = (float)(pSortBone[useBoneIndex].index); wights[useBoneIndex] = pSortBone[useBoneIndex].data/allWeight; } vba.TCoord<Float4>(1, i) = inds; vba.TCoord<Float4>(2, i) = wights; delete [] pSortBone; } skinCtrl->Repeat = Controller::RT_WRAP; skinCtrl->MinTime = 0.0f; skinCtrl->MaxTime = TicksToSec(mTimeEnd - mTimeStart); mesh->AttachController(skinCtrl); delete1(theBones); delete2(weight); delete2(offset); delete1(mats); } if (needDel) { delete0(triObj); } delete1(bones); delete1(boneInfuseNumVert); }