[Maya C++ API] Set skinning weight attributes
Code snippet to set smooth skinning weights. - 03/2017 - #Maya
Some C++ Maya API code to set skin weights (multi attributes) of a skin cluster node.
Setting skin weights (multi attribute / array attribute) with Mplug. Intermediate speed
void set_skinning_weights( MObject skin_cluster_node, const std::vector< std::map<int/*joint id*/, float/*joint weight*/> >& weights) { MStatus status; MFnDependencyNode deformer_node(skin_cluster_node, &status); mayaCheck(status); MPlug weight_list_plug = deformer_node.findPlug("weightList", true, &status); mayaCheck(status); for( unsigned i = 0; i < weight_list_plug.numElements(); i++ )// For each vertex { // weightList[i] MPlug ith_weights_plug = weight_list_plug.elementByPhysicalIndex( i ); // weightList[i].weight MPlug plug_weights = ith_weights_plug.child(0); // access first compound child // First reset values to zero: int nb_weights = plug_weights.numElements(); for( int j = 0; j < nb_weights; j++ ) { // for each joint MPlug weight_plug = plug_weights.elementByPhysicalIndex( j ); // weightList[i].weight[j] mayaCheck( weight_plug.setValue( MObject() ) ); } for(const std::pair<anim::bone::Id, float>& value : weights[i]) { MPlug weight_plug = plug_weights.elementByLogicalIndex( value.first ); // weightList[i].weight[value.second] mayaCheck( weight_plug.setValue( value.second ) ); } } }
The fastest way to set multi attributes / array attributes is done through a DAG node. (100 times faster than MPlugs!)
void set_skinning_weights( const std::vector<std::map<int/*joint id*/, float/*skin weight*/> >& weights, MDataBlock& block) { MStatus status = MS::kSuccess; MArrayDataHandle array_hdl = block.outputArrayValue(_s_skin_weights, &status); mayaCheck(status); for(unsigned i = 0; i < weights.size(); i++) { mayaCheck( array_hdl.jumpToArrayElement( i ) ); // weightList[i] MDataHandle element_hdl = array_hdl.outputValue( &status ); mayaCheck(status); // weightList[i].weight MDataHandle child = element_hdl.child( _s_per_joint_weights ); MArrayDataHandle weight_list_hdl(child, &status); mayaCheck(status); MArrayDataBuilder weight_list_builder = weight_list_hdl.builder(&status); mayaCheck(status); unsigned handle_count = weight_list_hdl.elementCount(&status); mayaCheck(status); unsigned builder_count = weight_list_builder.elementCount(&status); mayaCheck(status); mayaAssert( builder_count == handle_count); std::map<int/*influence obj id / joint id*/, float> map = weights[i]; std::vector to_remove; to_remove.reserve( map.size() ); // Scan array, update existing element, remove unsused ones for(unsigned j = 0; j < handle_count; ++j) { // weightList[i].weight[j] mayaCheck( weight_list_hdl.jumpToArrayElement(j) ); unsigned index = weight_list_hdl.elementIndex(&status); mayaCheck(status); auto elt = map.find( index ); if( elt != map.end() ) { MDataHandle hdl = weight_list_builder.addElement(index, &status); mayaCheck(status); hdl.setDouble( (double)elt->second ); map.erase( elt ); } else { to_remove.push_back( index ); } } for( unsigned idx : to_remove ){ mayaCheck( weight_list_builder.removeElement( idx ) ); } mayaCheck( weight_list_hdl.set( weight_list_builder ) ); } } MStatus Custom_node::initialize() { // Initialize skin weights multi attributes MFnNumericAttribute numAtt; _s_per_joint_weights = numAtt.create("per_joint_weights", "jw", MFnNumericData::kDouble, 0.0, &status); mayaCheck(status); mayaCheck(numAtt.setKeyable(false)); mayaCheck(numAtt.setArray(true)); mayaCheck(numAtt.setReadable(true) ); mayaCheck(numAtt.setWritable(true) );//when true skin weights will be saved on file mayaCheck(numAtt.setUsesArrayDataBuilder(true) ); mayaCheck(addAttribute(_s_per_joint_weights)); MFnCompoundAttribute cmpAttr; _s_skin_weights = cmpAttr.create("skin_weight_list", "sw", &status); mayaCheck(status); mayaCheck(cmpAttr.setArray(true)); mayaCheck(cmpAttr.addChild(_s_per_joint_weights)); mayaCheck(cmpAttr.setKeyable(false)); mayaCheck(cmpAttr.setReadable(true)); mayaCheck(cmpAttr.setWritable(true)); mayaCheck(cmpAttr.setUsesArrayDataBuilder(true)); mayaCheck(addAttribute(_s_skin_weights) ); } // Don't forget to initialize and connect your attributes: MPlug get_plug(const MObject& node, const MObject& attribute) { MStatus status; MFnDependencyNode dg_fn ( node ); MPlug plug = dg_fn.findPlug ( attribute, true, &status ); mayaCheck(status); return plug; } std::vector< int /*bone id*/, float /*vertex weight*/> > _skin_weights; for(unsigned vidx = 0; vidx < _skin_weights.size(); ++vidx) { // weightList[i] MPlug weightList_elt = weight_list_plug.elementByLogicalIndex(vidx, &status); mayaCheck(status); MPlug child = weightList_elt.child(0, &status); // access first compound child mayaCheck(status); for(const std::pair<int, float>& value : _skin_weights[vidx]) { MPlug weight_plug = child.elementByLogicalIndex( value.first, &status ); mayaCheck(status); // weightList[i].weight[value.second] mayaCheck( weight_plug.setDouble( 0.0 ) ); } } MPlug weight_list_skin_clus = get_plug( _skin_cluster, "weightList"); MPlug weight_list_plug = get_plug( this->thisMObject(), _s_skin_weights); MDGModifier dg; mayaCheck( dg.connect(weight_list_plug, weight_list_skin_clus) ); mayaCheck( dg.doIt() );
Final note: on large meshes just disconnecting the compound attribute _s_skin_weights
can be 5 times longer than actually setting the weights through set_skinning_weights()
. It is best to keep the disconnection as minimal as possible.
No comments