c++animationgraphicsgltfskinning

Can you get an explicit skeleton out of a gltf file?


For debugging purposes I want to render the skeleton of a skinned mesh. However, in glTF they seem to be defined in a very abstract way. That is to say, there is no "skeleton" as in, a set of vertices connected by lines. In other words this is not explicitly defined: enter image description here

Instead the "skeleton" is actually a collection of nodes that only specify matrices that convert the positions of the relevant vertices into the position of the joint.

Is it nonetheless possible to extract where the joint is? Maybe through inverting the inverseBindmatrix or something like that?

TL;DR I have a gltf model, I wanna render the skeleton of it like in blender.


Solution

  • I'm sure you're not looking for a "mostly" or "kinda-sorta" answer here, but indeed there are some complicating factors.

    I'll start with the basic strategy: glTF is a last-mile format, a GPU-ready format designed to get data pumped into rendering APIs with minimal fuss. As such, glTF has no compassion for "artist asset interchange" type features that would be meaningless to an end-user client. And in this case, we have a casualty of that philosophy: The bone's length.

    The bone's origin and orientation are well-known. They are (typically empty) nodes in the glTF scene hierarchy, and indeed we only know they count as "bones" when a skin comes along with a list of joints, that indicate which nodes are actually bones. Being a glTF node means that the bone has a known relationship to its parent, in the form of a transformation matrix, or more likely, in the form of translate, rotate, and scale parameters on the node. This gives you the bone's origin and orientation. It can even be animated, just like any glTF node can have an animation applied (but only when using TRS, not with the matrix).

    But the length of the bone isn't needed in glTF. Why not? Because for the sake of getting the model rendered quickly, what's needed is the bone's influence over the surrounding vertices. This is done with the JOINTS_0 and WEIGHTS_0 accessors (vertex attributes), so each vertex knows which bones can influence it, and by how much. The "length" of the bone is a useful visualization parameter for artists, but is not needed in the vertex shader to calculate where the skin ended up. A given 3D app may use the bone length to help calculate or seed the vertex joint weights, but once those are calculated, they can be hand-tweaked and changed, and the original bone length is moot.

    The inverseBindMatrix essentially holds the bind pose. It is the inverse of the bone's original rest/bind position relative to the root (not parent). Take that arm bone for example, it has some non-zero +X position. But when it sits there in its own rest position, it should have essentially no effect on the skin, it shouldn't be pulling the skin further +X from where it originally was. The fact that the arm bone is away from the model's origin makes it want to pull other vertices further away, and the inverseBindMatrix is there to un-do that effect. So when the bone is at rest (in the bind pose), the bone and its inverseBindMatrix will cancel each other out perfectly. As the bone starts to move away from that pose, it starts to exert influence.

    For what it's worth, the Blender glTF importer has some fancy shenanigans to make a best-effort guess at the bone length when importing a glTF. But it also imports the raw vertex weights, so, the bone length is merely for the artist's visual benefit, it has no effect on the skin.