[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