Search code examples
animationgraphics3dgltfskeletal-mesh

What is the correct mapping of inverse bind matrices?


A glTF file defines, for each skin, an array of inverse bind matrices and a list of joints.

Let's assume the list of joints is:

{2,5,7,4,3,6}

If we shift it so that the indices start at 0 we get

{0,3,5,2,1,4}

In this scenario inverse_matrix[2] can refer to one of two things. It can either refer to the second joint in the array, that is to say, joint 5, or to joint 2.

This exact same question applies to the weights array.

Put in a different way. If one takes the data as is from the gltf file and loads the buffers into a shader. I need to figure out how to map a vertex index in the shader to its corresponding 4 skin matrices.

So if joints[2] maps to (3,1,0,0)

I need to know whether I am supposed to be fetching the inverse bind matrices at ibm[3] and ibm[1] or ibm[2] and ibm[3] (Since the value at joints[3] is 2).

I hope this is not confusing.


Solution

  • glTF 2.0 specification § Skins says (emphasis mine)

    Each skin is defined by the inverseBindMatrices property (which points to an accessor with IBM data), used to bring coordinates being skinned into the same space as each joint; and a joints array property that lists the nodes indices used as joints to animate the skin. The order of joints is defined in the skin.joints array and it must match the order of inverseBindMatrices data.

    That directly answers your question. I'll give a more detailed explanation here:

    • There're two IDs: node ID and joint ID
    • Node ID: Index of a node in the nodes array
    • Joint ID: Index of a joint in the joints array
      • A joint in the joints array is specified with the node ID; the joint ID is different though
        "nodes" : [
            {                           // 0
                "name" : "Cave"
            },
            {                           // 1
                "name" : "TailBone",
            },
            // snipped for brievity
            {                           // 29
                "name" : "Root"
            }
        ],
        "skins" : [
            {
                "inverseBindMatrices" : 6,
                "joints" : [
                    29,
                    1,
                ]
            }
        ]
    

    For this .glTF, inverseBindMatrix[0] would be for joint[0], this joint is given by node 29; inverseBindMatrix[1] would be for joint[1], this joing is given by node 1.

    If we shift it so that the indices start at 0 we get

    {0,3,5,2,1,4}

    I don't think this is correct. Why do you shift? You basically just need a mapping between node ID to bone ID until you digest and load the glTF data into your own structures (after which node IDs have no use unlike bone IDs)

    Node   Bone
    29      0
    1       1
    

    When you calculate the skin matrix, make sure you follow the order of the joints array. In the above example, skin[0] should be for joints[0] i.e. for node 29.