Documentation
    Preparing search index...

    Class Skeleton

    A rig: an ordered set of Bones, their inverse bind matrices, and the joint-matrix palette derived from them every frame (§54; RFC 0003 §1b).

    const root = new Bone();
    const tip = new Bone();
    tip.transform.position.set(0, 1, 0);
    root.add(tip);

    const skeleton = new Skeleton([root, tip], inverseBindMatrices);
    mesh.skeleton = skeleton; // @fourjs/render's Mesh (§54)
    // per frame, after transform resolution (the render list does this):
    skeleton.update(mesh);
    upload(skeleton.jointMatrices);

    A geometry's joints attribute indexes this array, a §17 skeletal-joint track addresses a bone as bones.<i>.transform.<channel>, and Skeleton.update walks it in insertion order. Reordering the array is therefore re-skinning the mesh; the array is readonly so it cannot happen by accident.

    update reads bone world matrices (resolved, or a caller-supplied worldOf that composes interpolated poses) and writes only into Skeleton.jointMatrices; nothing reads the palette back into a transform. This is §43's "render interpolation never feeds back into physics state" applied one level down, and it is what keeps a ragdoll's §19 "blended" bones ordinary nodes with ordinary authorities.

    A skeleton is shared state about nodes — several meshes may skin against one rig — so it is a plain object referenced by Mesh.skeleton (§54), exactly as a BufferGeometry is referenced by Renderable.geometry. In a §79 document it is written inline on the mesh as bone ids plus the inverse bind matrices (intra-file references are by id, §79), and resolved against the reloaded bones on first read — see @fourjs/render's Mesh.

    Index
    • Builds a skeleton over bones (§85-validated: non-empty, no duplicate bone, every entry a Bone) with inverseBindMatrices (16 finite floats per bone; identity per bone when omitted).

      The bone array is copied (so later edits to the caller's array cannot silently re-skin a mesh); the matrices are held by reference, like geometry attributes — they are bind-pose data a loader hands over whole.

      Parameters

      • bones: readonly Bone[]
      • OptionalinverseBindMatrices: Float32Array<ArrayBufferLike>

      Returns Skeleton

      FourError INVALID_SCENE_GRAPH (§85) for an empty rig, a duplicate bone, a non-Bone entry, a mis-sized matrix array, or a non-finite matrix element.

    bones: readonly Bone[]

    The joints, in joint-index order. Insertion order is the ABI (§33): entry i is what joints[4 * v + k] === i refers to, for every vertex v.

    inverseBindMatrices: Float32Array

    One column-major inverse bind matrix per bone — 16 floats each, index-parallel with Skeleton.bones. Bind-pose data authored with the rig; the engine never writes into it. Defaults to the identity per bone, which is correct for a rig whose bind pose is the rest pose of its nodes expressed in the skin root's frame.

    jointMatrices: Float32Array

    The palette: 16 floats per bone, rewritten in place by every Skeleton.update. palette[i] = inverse(skinRootWorld) · bones[i].worldMatrix · inverseBind[i] — exactly what a skinned vertex stage consumes, in the layout uniformMatrix4fv uploads.

    Identity per bone until the first update, so a palette uploaded before one draws the bind pose rather than garbage.

    • get jointCount(): number

      Number of joints — bones.length, named for the palette's consumers.

      Returns number

    • Rewrites Skeleton.jointMatrices from the bones' current world matrices, relative to skinRoot — the mesh node whose worldMatrix the vertex stage applies after the palette, so the palette must be expressed in its frame:

      palette[i] = inverse(skinRootWorld) · bones[i].worldMatrix · inverseBind[i]
      

      Deterministic (§33): bones are visited in insertion order, the two products associate left-to-right, and world matrices come from worldOf when provided, otherwise from the same resolveWorldTransform every other node uses — version-cached, so after the frame's resolve pass each read is a few comparisons. Allocates nothing (§7b: module scratch).

      worldOf is how §43 interpolation reaches the palette without writing interpolated poses back into node.transform (§42): the render list supplies a provider that composes interpolated local poses the same way node world matrices are composed. The provider is used for both the skin root and every bone. The palette itself is never lerped — local poses interpolate, then this product runs. A returned matrix is copied immediately, so the provider may reuse one scratch across calls.

      Degenerate input follows the engine's transform policy rather than throwing on a per-frame path (§85, Matrix4.invert): a skinRoot whose world matrix is exactly singular (a zero scale somewhere up its chain) has no inverse, invert() leaves its input unchanged, and the palette that frame is well-defined arithmetic over a wrong matrix — finite, visible, and gone the moment the scale is. Nothing is written anywhere else.

      Parameters

      Returns void