Smooth Skinning

Ah, skin weighting. I have a confession, and I know I probably shouldn’t let this get out, but skin weighting doesn’t bother me that much. Yes it can get repeditive and boring, I can’t pretend to like that, but a lot of the time it can actually be a creative process. Almost like sculpting. And no, I wont skin your character for you.

When you first start, though, skin weighting sucks. A lot. You’ve got points shooting off to the origin, parts moving and you don’t know why, and it’s just a big ugly mess. It’s a lot to keep track of, so the trick is to really stay organized and meticulous. The character I’m working on right now has 320 bind joints, so I’ve been trying to be extra organized to avoid doing anything over. Here’s some tips:

Separate Skinning Mesh
Paint weights on what Paul Thuriot calls a watertight mesh. If your character has multiple parts, combine them into one big mesh, merge verts, and make it all clean-like. This mesh is just for weighting, and you can transfer the data later to the final mesh. If it’s really dense, you can delete parts of the mesh that are going to be mirrored, such as the right arm and leg. That will help performance, and it wont really matter because you can mirror it after you transfer the weights to a separate mesh.

Block Your Weights
I think it helps if the first quick pass at painting is just for blocking in. When you apply the smoothskin set the max influences to 1, then go through with a hard brush set to replace 1. This way you can make sure that you aren’t starting out with any random weights, and you know where all your influence is going.

Know Where Your Weights Are
Alright, now you’ve got to get meticulous. The key to not getting crazy verts is to know where your weights are going. Remember, with every paint stroke you’re always adding to something, and subtracting from something else, no matter what your settings are. Weights are normalized, so they’re always going to add up to 1. That means if you’re adding weight to a joint, you have to know where that weight is coming from. Subtracting weight can be especially dangerous, because sometimes Maya wont know where to put it.
Think about this: If you’re painting a vert that is totally weighted to the elbow, if you wanted to give it some influence from the shoulder you might think scaling or smoothing would be nice, since you’re already here and you want to subtract, right? Well if the weight is totally at 1 then that means the weight for all the other joints is at 0. So say you set the weight to 0.8. Where did that other 0.2 go? You wanted it to go to the shoulder, but maya doesn’t know that, since the shoulder is at 0 like everything else. So it might break it up into a 100 pieces or something and send a little bit to every joint. That sucks. Then if you try to fix the problem with prune small weights you’re back where you started.
(Side note: If you are meticulous you should never have to use Prune Small Weights)

Anyway, so how do we get weights going where we want? Well, for starters, it’s best to avoid subtracting weights unless you’re sure you know where they’re going. That goes for smoothing weights also, I know it’s really tempting to press that smooth flood button, but have some self control! The best way to control your weights is by holding them. If all the joint weights are held except the two you are painting on, the weights are corralled between the two. So if you add to one you know the weights are coming from the other, and vice versa. You can even smooth to your hearts content. Well, almost. If you end up subtracing weight and it has nowhere to go, it might get un-normalized, which will send the vert towards the origin. And that’s bad. But more on that later.
Going through and holding 320 joints one at a time is going to get really old really fast.

Hold All Weights Script
{
// ON OR OFF
int $val = 1;

// GET THE JOINTS IN THE SKIN CLUSTER
// ON THE SELECTED OBJECT
string $sel[] = `ls -sl`;
string $history[] = `listHistory $sel[0]`;
string $clusterName[] = `ls -type skinCluster $history`;
string $infs[] = `skinCluster -q -inf $clusterName[0]`;

for ($current in $infs)
{
setAttr ($current + “.liw”) $val;
}
// UPDATE COMPONENT EDITOR
compEdMenuCmd CEMIAutoUpdate componentEditorPanel1WindowComponEditor;
}

This came from someone on CGTalk, I forget who, but it’s helpful.

Workflow
Start at the extremeties and work your way up. Fingertips are a good place to start. Get them out of the way, most the joints in the fingers only have 2 influences. Once you’re done with an extremety, you can hold them and hopefully never worry about them again. So I think I usually end up doing the shoulders last.

Stray Verts
Stray verts shooting off in weird directions are undoubtably going to happen at some point. You will probably have to open up the component editor at some point, but that’s never fun. A better solution is being able to copy and paste weights from a selection of verts to the offending vert. If you’ve got a vert behaving strangely, you can just select the verts on either side of it, copy, and then paste. Ha.
Here’s the code, courtesy of John Homer:

///////////////////////////////////////////////////////////////////
///assign COPY to a hotkey:
// COPY WEIGHTS ON SELECTED VERTS AND AVERAGE THEM

float $weights[];
float $v2Weights[];
float $v2Weights[];
string $cmd;
string $vsel;
string $joints[];
float $tmpVal;
int $nJoints;
float $tmpArray0[];
float $tmpWeights;

string $expandedSel[0] = `ls -sl -flatten`; // gets the selected object components
// get the name of the skinCluster
string $history[] = `listHistory $expandedSel[0]`;
string $clusterNames[] = `ls -type skinCluster $history`;
string $skincl = $clusterNames[0];

int $nVerts = `size $expandedSel`;

// MAKE AN ARRAY FOR EACH COMPONENT SELECTED
for ($i=0; $i<$nVerts; $i++) { $cmd1 = "skinPercent -q -v "+$skincl+" "+$expandedSel[$i]; $weights = eval($cmd1); $arrayCmd = ("$tmpArray" +$i + " = " + "`" + $cmd1 + "`;" ); eval $arrayCmd; } /////////////////////////////////////////////////////////////////////
///Assign PASTE to another hotkey
{
// PASTE WEIGHTS ONTO SELECTED COMPONENTS
string $skincl = “tunic_skinCluster”;
string $vsel; string $sel[0] = `ls -sl`;
string $history[] = `listHistory $sel[0]`;
string $clusterNames[] = `ls -type skinCluster $history`;
string $skincl = $clusterNames[0];
global string $output;
string $expandedSel[0] = `filterExpand -ex 1 -sm 28 -sm 31 -sm 46 -sm 36 $sel`;
for ($vsel in $expandedSel)
{
string $cmd = (“skinPercent “+$output+” “+$skincl+” “+$vsel);
eval ($cmd);
}
// UPDATE COMPONENT EDITOR
compEdMenuCmd CEMIAutoUpdate componentEditorPanel1WindowComponEditor;
}
////////////////////////////////////////////////////////////////////

Alright, this post is too long, back to work.

EDIT 8/26/05
If you want to tranfer weights explicitly form one joint to another, I think the safest way to do it is to hold all the weights except the ones being tranferred between, and then replace with 0 on the one being transferred from. This way you know exacly where the weights are going, since you only have one option, and you’re not trying to pull weights away from held joints by adding.

Share Button
This entry was posted in Uncategorized. Bookmark the permalink.

3 Responses to Smooth Skinning

  1. rini says:

    good job morgan, this will be so helpful..
    too bad i don’t really understand it now.. but when i need it . at least i know where to ask 😛

  2. jeff says:

    for stray verts, i use the skin2selected.mel

    it’s handy in that you grab the vert, grab a bone or 2 hit the mel, and it skins equally to all joints selected-

    just thought i’d throw it out there as it’s saved me time. cool blog! keep it up!

  3. Morgan Loomis says:

    I’ll have to try that out, it sounds helpful. I couldn’t find it on Highend, though, do you remember where you got it?

Leave a Reply

Your email address will not be published. Required fields are marked *