- Home /
What space are the bones from an armature and the vertices of a mesh in?
I'm attempting to recreate the climbing from shadow of the colossus. I'm doing so by creating a collision model that also deforms with the armature. To do so I copy the vertices from the mesh class and update there position using the position and rotation of bones in the armature. But I'm running into some trouble updating the position correctly. The collision model is usually oriented and scaled differently then the visual model, when an animation is performed the collision model's child bones do not animate properly.
Here is an example.
The blue wireframe represents the collision model while the white one is the visual model.
The collision model is facing down and its ears are out of place.
I figure the cause of this some error in transforming the bones or the vertices from one space to another when calculating vertex position. But I can't find information on what space the bones or vertices are in. The bones seem like they would be in local space of their parent but I'm not sure how the visual model converts uses this to deform vertices.
Answer by Bunny83 · Sep 05, 2019 at 07:01 PM
That's pretty simple. You have to use the bindposes of the mesh. They are essentially just the combined matrices of the mesh transform and the inverse bone transform in binding position. As you might know vertices are always specified in localspace of the gameobject that has the skinnedmeshrenderer. So a bindpose matrix essentially brings the vertices directly into the local space of the bone gameobject. So after you multiply a vertex with this matrix you essentially have the vertex position in local space of the bone. So all you have to do now is use the current bone object and transform that vertex position back to worldspace. If you have several vertices that are bound to a bone it's probably worth to build the combined matrices for each bone by combining the bindpose matrix with the bone localToWorld matrix like this:
var m = bones[i].localToWorld * bindposes[i];
Note that the bones array has to be the bones array of the skinned mesh renderer. The order is important since each element of the bones array corresponds to each element in the bindposes array.
So with this matrix "m" you can directly transform a local space vertex coordinate from the mesh into the skinned worldspace position. Of course keep in mind that after this blending comes into play since a vertex can be bound to multiple bones. For this there's the boneWeights array. Each element belongs to one vertex. The bone weights are essentially another vertex attribute like the uvs or the normals. Unity supports up to 4 bones per vertex.
I'm not sure if Unity will normalize the weights for you. If not you may have to make sure that they add up to 1. So for example if the weight of bone0 is 0.1 and the weight of bone1 is 0.3 but bone2 and 3 are 0 the normalized weights would be 0.25 for bone0 and 0.75 for bone1 and 2 and 3 stay 0. To get the final position for a vertex you have to multiply each transformed vertex with the corresponding weigth and just add the results together.
edit
To break down the steps you have to do each time you want to update:
prepare the bone matrices by combining the current bone transform matrix with the bindpose matrix
iterate through all vertices of the mesh
grab the boneweights, for each non-zero weight lookup the corresponding precalculated bone matrix from step one and transform this vertex with each of the matrices.
multiply each of those transformed versions with the normalized weight and add them together to get the skinned vertex
Keep in mind that MeshColliders are not meant to update the mesh each frame. If you do you will probably run into performance issues. I haven't recently tried this but in the past you needed to set the sharedMesh of the MeshCollider to null before you assign the mesh again in order to actually update the mesh.
ps: Note that Unity now has the Bake$$anonymous$$esh method which does all the skinning for you and presents you a snapshot of the current skinnedmeshrenderer state. So you don't really need to do all this manually. The resulting mesh is in local space of the renderer.
First of all, I just want to thank you I'd been struggling trying to figure this out for the longest. I was so confused about what was in what space. I don't think its stated anywhere and if it is I couldnt find it.
Also I'm not using the $$anonymous$$eshCollider component, like you said they aren't meant to be updated every frame. I am using my own custom collision detection for the collisions with the mesh.
I have one more question. The bone transform bindpose matrix, does it take into account the parent and child bone relationship? I ask this because I'm using the matrix to bring vertices into worldspace but the vertices weighted to child bones are not properly positioned when the child bones are moved.
Your answer
Follow this Question
Related Questions
How to get the initial bone positions for a skinned mesh 0 Answers
Bones do not switch when called for (Find bones of player then replace clothes bones) 1 Answer
How can I get the original bone bind position ( and not the matrix)? 1 Answer
Transfer bones from one Object to another (same meshes) 0 Answers
Combine Skinned meshes 2 Answers