I want to copy skin weights from one human model to another. The two models have the same topology, so I tried to directly copy the weight index by index using following code.
for idx, p in enumerate(points):
# skinValues save first model's skinning
if idx in skinValues.keys():
ns = skinValues[str_idx]
mc.skinPercent(skinClusterIm, p, tv = ns,zri = True)
But it works so slowly (for models with 50000 vertices and 300 joints takes 15 minutes). Is there any way to speed up the process?
Yes, this will be painfully slow sadly, because Maya is generating an undo step for every single iteration sadly. That's 50000 * 300 undo steps, which is quite a lot of memory allocation!
You have two options to speed this up.
Option 1: disable the undo queue!
// grab current undo state
current_state = mc.undoInfo(q = True, state=True)
// turn off the undo queue
mc.undoInfo(state=False)
// call skinPercent in your double nested for loop now
// restore the state of the undo queue
mc.undoInfo(state = current_state)
Option 2: Modify the skin cluster data directly.
The data on the skin cluster is addressed like so:
skinCluster1.wl[VERTEX_INDEX].w[JOINT_INDEX];
You can skip the need to skinPercent command entirely, by simply using setAttr to set the weights directly. I'm a bit old school, so generally use MEL, but in MEL you can set all weights for a single vertex in a single command, e.g.
// this assume the skin cluster has 4 weights per vertex!
// So we set vertex 0: wl[0],
//
// using joint indices 0, 1, 2, and 3; i.e. ".w[0:3]",
//
// We can then just list the weights we want to set afterwards.
//
setAttr "skinCluster1.wl[0].w[0:3]" 0.666 0.334 0 0;
Even with the undo stack enabled, this helps trim down the number of undo steps (However, even with this approach, I'd still disable the undo stack!!). By and large, I'd avoid using MFnSkinCluster in the Maya API, because it's stupidly slow. Sadly, the skinPercent command is clearly implemented using MFnSkinCluster :(
\edit
a little Mel script to extract the joint names from the skin cluster.
proc print_joints(string $cluster) {
// how big is the matrix array input on the cluster?
// this will map directly to the number of joints in
// the cluster
$num_joints = `getAttr -size ($cluster + ".matrix")`;
for($i = 0; $i < $num_joints; ++$i) {
// look for any source connections to the given array input
$joint = `listConnections -d 0 -s 1 ($cluster + ".matrix[" + $i + "]")`;
// print in a readable format
print($cluster + ".matrix[" + $i + "] --> connected to --> " + $joint[0] + "\n");
}
}
print_joints("skinCluster1");