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.
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_.localToWorld * bindposes*;*_
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.
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.