How glTF Serializes Bone Hierarchies with Node Indices
This article provides an overview of how the glTF (GL Transmission Format) 2.0 specification serializes skeletal bone hierarchies using zero-based node indices. It details the structural relationship between the global node graph and skeletal skins, explains how parent-child bone relationships are encoded, clarifies the difference between global node indices and local joint indices, and outlines how inverse bind matrices and mesh vertex attributes interact to compute final skinned vertex positions.
The Role of Nodes as Bones
In glTF, there is no distinct "bone" data type. Instead, bones are
represented as standard scene graph elements within the top-level
nodes array. Each entry in the nodes array is
identified implicitly by its zero-based index (e.g., node
0, node 1, node 2).
A node acting as a bone contains:
- Transform properties:
translation,rotation, andscale(or a singlematrix), defining its local transform relative to its parent. - A
childrenarray: A list of integer indices referencing other nodes in the globalnodesarray that act as direct child bones.
Because children are stored as indices, the skeletal hierarchy is
formed as a directed acyclic graph directly inside the global
nodes array.
The skin Object
Definition
To bind a skeletal hierarchy to a deformable mesh, glTF uses a
skins array. A skin object aggregates the
relevant nodes into a cohesive skeleton through the following
properties:
joints: An array of integer indices referencing nodes in the top-levelnodesarray. Every node acting as a bone in that particular skeleton must be listed here.skeleton(optional): An integer index pointing to the root node of the skeletal hierarchy. If omitted, the root is determined by finding the topmost joint in the hierarchy that is not a child of another joint.inverseBindMatrices(optional): An accessor index pointing to a buffer of \(4 \times 4\) floating-point matrices. Each matrix corresponds directly to a joint in thejointsarray at the matching index.
Local Joint Indices vs. Global Node Indices
A crucial distinction in glTF skinning is the difference between global node indices and local joint indices:
- Global Node Indices: The actual index of an element
within the root-level
nodesarray. The bone hierarchy (parent-to-child links viachildren) is defined strictly using global node indices. - Local Joint Indices: The index of a joint within a
specific
skin.jointsarray.
When a mesh uses skinning, its geometry defines vertex attributes
such as JOINTS_0 and WEIGHTS_0:
WEIGHTS_0contains floating-point weights indicating how much each joint influences a vertex.JOINTS_0contains integer values that index into theskin.jointsarray, not the globalnodesarray.
For example, if a vertex references joint index 0 via
JOINTS_0, the runtime looks at skin.joints[0]
to retrieve the actual node index in the scene graph.
Hierarchy Traversal and Skinning Resolution
To evaluate a skeletal hierarchy during runtime, an engine processes the serialization as follows:
- Evaluate Local Transforms: Read each bone node's
translation,rotation, andscaleto construct local transformation matrices. - Compute Global Transforms: Traverse the hierarchy
from the root node (using the indices defined in the
childrenarrays) to calculate each bone's current global model-space matrix. - Skin Matrix Calculation: For each joint \(i\) in
skin.joints: \[\text{JointMatrix}_i = \text{GlobalTransform}(\text{joints}[i]) \times \text{InverseBindMatrix}_i\] - Vertex Deformation: During rendering, each vertex
is deformed by summing the products of its skinning weights and the
corresponding joint matrices resolved from
JOINTS_0.