- Home /
Weightless bone inside the SkinnedMeshRenderer's bone array?
Hi everybody. I've been developing game where characters need to wear tons of different clothing articles. Our team decided that making animations for every single clothing piece to match every character's body animation was out of the question so we decided to go for the solution presented here:
How to have swappable character clothing Thanks @Cherno & @pixels !!
The post solves the problem by basically copying the bone array in the body's skinned mesh renderer to the clothing's one (which should be the same skeleton, same bone names and everything). This works beautifully, however there are some caveats. There cannot be a bone that has no weights on the body, why? Because Unity will strip the bone from the sk renderer's array, thus creating mismatch of bones that may screw the meshes skinning. That's the important piece of knowledge here and it is also what I'm having trouble with right now.
When we first implemented this, the rigger in my team (who uses Maya) forgot to put weight on a bone and sure enough there was a mismatch and the skinning did not work. So we ended up deleting this bone and everything worked. Few weeks later we had to make modifications to the rigg and the problem came up again. There was one bone in a piece of clothing that the body did not contain (because Unity stripped it since it had no weight on it), I asked the rigger about this and he told me that, that bone wasn't the only weightless one, he had like 4 more. HOWEVER, only that specific bone was not found, meaning that there were weightless bones that Unity did not stripped which contradicts the most important piece of knowledge in this case. I verified this by code, looking for the bone index and searching every boneWeight and sure enough the bone was contained in the body's array but not a single boneWeight had a reference to that bone's index.
Here's the code:
public static void Stitch(SkinnedMeshRenderer attachment, SkinnedMeshRenderer target)
{
#region WeightlessBoneCheck
string weightlessBoneName = "jntBndHair_Ft_l_1";
int weightlessBoneIndex = -1;
for (int i = 0; i < target.bones.Length; i++)
{
if (!target.bones[i].name.Equals(weightlessBoneName)) continue;
weightlessBoneIndex = i;
break;
}
Debug.Log("Weightless Bone Index in Body: " + weightlessBoneIndex);
//Veryfy that it is actually weightless
BoneWeight[] bodyBoneWeights = target.sharedMesh.boneWeights;
bool vertexAffectedFound = false;
for (int i = 0; i < bodyBoneWeights.Length; i++)
{
BoneWeight bw = bodyBoneWeights[i];
if (bw.boneIndex0 == weightlessBoneIndex)
{
Debug.Log("Bone assigned to vertex: " + i + " with Weight: " + bw.weight0);
vertexAffectedFound = true;
}
if (bw.boneIndex1 == weightlessBoneIndex)
{
Debug.Log("Bone assigned to vertex: " + i + " with Weight: " + bw.weight1);
vertexAffectedFound = true;
}
if (bw.boneIndex2 == weightlessBoneIndex)
{
Debug.Log("Bone assigned to vertex: " + i + " with Weight: " + bw.weight2);
vertexAffectedFound = true;
}
if (bw.boneIndex3 == weightlessBoneIndex)
{
Debug.Log("Bone assigned to vertex: " + i + " with Weight: " + bw.weight3);
vertexAffectedFound = true;
}
}
if (vertexAffectedFound)
Debug.Log("Bone " + weightlessBoneName + " is not really weightless");
else
Debug.Log("Bone " + weightlessBoneName + " is weightless but it is also part of the skinned mesh renderer bone array. Wat?");
#endregion
#region ActualRiggStitching
Dictionary<string, Transform> boneMap = new Dictionary<string, Transform>();
foreach( Transform bone in target.bones )
boneMap[bone.name] = bone;
Transform[] boneArray = attachment.bones;
bool isError = false;
for(int idx = 0; idx < boneArray.Length; ++idx )
{
string boneName = boneArray[idx].name;
if( false == boneMap.TryGetValue(boneName, out boneArray[idx]) )
{
Debug.LogError("failed to get bone: " + boneName);
isError = true;
}
}
if (isError)
return;
attachment.bones = boneArray; //take effect
#endregion
}
And here's the log output
Sooo, what gives? Why are some weightless bones kept and others not? Can anybody shed some light on this?
Are you actually sure that it's Unity who stripped the unused bones? What export format do you use? Do you have any other way to verify that the bone actually got exported? What other properties does the weightless bone have that the others might not. Any subobjects or subbones? I'm not an artist so it's difficult for me to test this as i'm unable to operate a modelling tool to create a testing model and I also do not have $$anonymous$$aya ^^. I can create simple procedural things in Unity through scripting but since the problem is most likely somewhere in the export / import procedure this doesn't really help.
To me it sounds strange that bones get stripped. Do those bones which are stripped have any weight in any animation? The only case where i could imagine where a bone might be stripped if it's not used by any animation and by no vertices. $$anonymous$$aybe you can add a 1 frame animation that involves all bones? I don't think Unity would strip such bones.
I'm not an artist either n_n but I checked the bone hierarchy in maya with the rigger and it seems like there is no meaningful difference between those 2 bones. Both bones are weightless and according to @Cherno when weightless bones are imported on a skinned mesh renderer, the bone will appear in the hierarchy as a game object, but it will not be inside the Skinned$$anonymous$$eshRenderer.bones transform array. And that is indeed what's happening for the bone jntBndDressCrrct_1 but not for jntBndHair_Ft_l_1, which is weird.
Both bones are final in the hierarchy. They have no weight on the body mesh, they have weight on other meshes (one in a dress and the other one in the hair).
I can't answer the question but I wish to point out that it's important to remember that a bone that has at least one vertex with a weight of 0 still gets added as normal. It's really just the bones which have no vertices at all (no weights, as opposed to weights of 0) that are the problem.
Depending on the modeling application, if there is some sort of weight table for the vertices the cell needs to have any value even if it's 0, but the cell can't be empty.
Right, technically a bone with no "vertex link" is not a bone in the sense of Unity ^^. An imported model could have other child objects but only those which have a weight are considered "bones". So the only important thing is that the main rig has all bones. So you just need to make sure every bone has at least one vertex accociated with it. Since each vertex can only be related to 4 bones there's no generic or nice way to ensure "additional bones" always gets included. You would need to use rather "unimportant" vertices which may only have one bone (weight == 1) so you can add up to 3 additional bones to that vertex. As i said there is no nice way to organise this. I'm not sure what would happen when the model has some vertices without any triangles which you only use to map the additional bones to. It might be enough so Unity does include the bones, even the vertices may be optimised out on import. It would require some testing but you need an artist who creates the models for you ^^.
Good point, I almost forgot about that, the thing is that in $$anonymous$$aya there seems to be no distinction between bones with 0 vertex weight and bones with no vertex weight assigned, or at least I haven't been able to find it anywhere in $$anonymous$$aya's options and the rigger couldn't tell me either. I'll keep looking into it since to me this seems to be most likely the case.
Well in 3D Studio $$anonymous$$ax a weight field can be "null" (completely empty) or have any value from 0-1.
I dont have $$anonymous$$aya experience so I may be of no use to you but in Blender there is the option of either leaving a bone out of the weighting or selecting whether or not it is a 'Deform' bone. In $$anonymous$$aya is this the definition of a 'Weightless bone'? I'm not entirely sure where the distinction is made but its apparent there is one and perhaps therein lies the difference in the err'd and non-err'd bones. In addition to this, it is Blender's export options which decide whether or not to exclude the export of these 'Deform' bones and un-weighted bones will export anyway. Un-weighted vertices in the past have not thrown an error but ins$$anonymous$$d fix the vertices in space, stretching a mesh. I'm not sure if recent versions of Unity will give a warning or error on this. Unless specifically opted in Blender export to exclude Deform bones, all these bones are imported to Unity and are not stripped away by default. The obvious purpose of this is facilitation of Root concept etc An important realisation of detached bones was the parenting structure. Simply deleting a bone may be ok if it were detached and un-weighted but a bone within the parenting structure of the rig is going to throw up lots of problems; even if it was specifically left out of weight painting any normalisation will bring it in to consideration. Another strange concept in Blender is the concept of 'users' and 'Fake Users'. I havent quite got my head around it but it has to do with linking objects and such together. For example I can create an animation and perhaps make different rigs within my Blender scene use it. A lot of animation and rig problems that crop up on UA have to do with some mis-linkage of these Users. Again, I apologise to be chatting about a different piece of software and its functionalities but I'm hoping to find some similarities between the workings; after all they can all pack it down to the same .FBX.
Thanks @Cherno , @Bunny83 and @meat5000 for your input. Sorry for taking this long to respond, but unfortunately I haven't found the way to solve this yet, what we ended up doing was assigning a tiny weight to a vertex. It is hacky but it'll do for now. Nevertheless I'll keep the problematic version of the rigg to check it later and see if I can figure this one out and when that happens I'll come back here to post the findings. Thanks again!
@$$anonymous$$acDx
Did you ever make any progress with this? We are running into the same issue where Unity is stripping unused bones from our base character mesh that we want to use for accessories.