Tyler Thornock
Technical Animator
Home Tutorials Tools Rigs About/Resume
Maya – Query/Set SkinCluster Weights

There are a lot of things you can do to speed up the query and set of skinCluster weights in Maya, but you do have to delve into the API and deal with more complex code. Here I will show an example of using API 2.0.

People will often bring up MFnSkinCluster.getWeights since it is technically the fastest method to query a dense mesh with lots of influences. However, what it returns back is not a good way to work with weights. For each vertex it returns weights for all influences, and since most of these are zero, it is a lot of “useless” information you will have to iterate over. So if you are trying to iterate over the weights to modify, normalize, etc, this is especially slow in python.

However, you can combine using getWeights with MPlug. There is an attribute on the skinCluster called weightList, whose index is the vertex id, which has a child attribute called weights, whose index is the influence id, which stores the influence weight value. The great thing for skin weights is maya only “activates” the influence ids in this weights attribute that are non-zero for the vertex.

Enter the hero, MPlug.getExistingArrayAttributeIndices which returns the indices of these non-zero weights. Now we can use this to index into the gigantic flat list returned by getWeights. Technically a false positive could exist, but unless you set it manually that shouldn’t happen for skin weights (and could bloat your file size if you went around setting a bunch of zeroes).

However, to be useful, we need these indices to match up with specific influences. MFnSkinCluster.indexForInfluenceObject returns the setAttr index for the given dag path of an influence. Now you know which influence is associated with which index returned by the MPlug.

So here is some example code using this method to query weights:

from maya import cmds, mel
from maya.api import OpenMaya, OpenMayaAnim

# assuming you have a skinned pSphere1, then you can just run this code
# just basic code to get the needed shape/skin/component variables, mostly here for reference
shape = 'pSphere1'
skin_name = mel.eval('findRelatedSkinCluster "{}"'.format(shape))
num_verts = cmds.polyEvaluate(shape, v=True)

sel_list = OpenMaya.MSelectionList()
sel_list.add(shape)
sel_list.add(skin_name)

shape_dag = sel_list.getDagPath(0)
skin_dep = sel_list.getDependNode(1)
skin_fn = OpenMayaAnim.MFnSkinCluster(skin_dep)

# NOTE: this supports querying specific components, you dont have to do them all
comp_ids = [c for c in range(num_verts)]
single_fn = OpenMaya.MFnSingleIndexedComponent()
shape_comp = single_fn.create(OpenMaya.MFn.kMeshVertComponent)
single_fn.addElements(comp_ids)

# quickly get all weights for all comps and influences as a gigantic flat list 
flat_weights, inf_count = skin_fn.getWeights(shape_dag, shape_comp)

# get the plugs we will use to index into the flat weights to ignore the bazillion zero values
weight_plug = skin_fn.findPlug('weights', False)
list_plug = skin_fn.findPlug('weightList', False).attribute()

# removed influences can mess with the inf indexing so build a mapping
inf_dags = skin_fn.influenceObjects()
inf_count = len(inf_dags)
sparse_map = {skin_fn.indexForInfluenceObject(inf_dag): i for i, inf_dag in enumerate(inf_dags)}

# now store into a dictionary where {comp_id: {inf_id: weight}}
skin_weights = {}
for c, comp_id in enumerate(comp_ids):
    weight_plug.selectAncestorLogicalIndex(comp_id, list_plug)
    valid_ids = weight_plug.getExistingArrayAttributeIndices()

    # sometimes removed influences can give false positives, so ignore
    valid_ids = set(valid_ids) & sparse_map.keys()

    # now build up the inf weights
    comp_weights = {}
    flat_index = c * inf_count
    for valid_id in valid_ids:
        inf_index = sparse_map[valid_id]
        comp_weights[inf_index] = flat_weights[flat_index + inf_index]
    
    # finally store into main dict
    skin_weights[comp_id] = comp_weights

Now that you have the weights, it is quick and easy to modify them. If you are not used to dictionary they might seem weird at first, but I really like using them in this case since you may not be querying all vertices.

Once you are done modifying the weights, you can set them using MFnSkinCluster.setWeights by rebuilding the gigantic MDoubleArray. Here is an example of a fast way to do this.

# the components must be done in order
comp_ids = sorted(skin_weights)

# you'll need to know the total number of influences in the cluster
inf_count = 5

# populate the MDoubleArray entirely with zeroes first, then just update the needed indices
weights = OpenMaya.MDoubleArray(len(comp_ids) * inf_count, 0)
for c, comp_id in enumerate(comp_ids):
    start_id = c * inf_count
    for inf_id, weight in skin_weights[comp_id].viewitems():
        weights[start_id + inf_id] = weight

And finally, instead of making a plugin to apply vis setWeights, you can use a class created to use the plugin mentioned here. Maya – Easy Undoable Python / API Enjoy!