Skeleton

SimVX provides skeletal animation support through the Bone dataclass and Skeleton class. Together they define a bone hierarchy and compute joint matrices for GPU vertex skinning.

Bone

A Bone represents a single bone in the hierarchy:

Field

Type

Description

name

str

Human-readable bone name

parent_index

int

Index of parent bone (-1 for root)

inverse_bind_matrix

np.ndarray (4x4)

Transforms from mesh space to bone-local space

local_transform

np.ndarray (4x4)

Default local-space transform relative to parent

Skeleton

Skeleton holds an ordered list of bones and computes the final joint matrices used by the GPU skinning shader.

Creating a Skeleton

Bones must be ordered so that every parent appears before its children:

import numpy as np
from simvx.core import Bone, Skeleton

# Build a simple arm: root -> upper_arm -> forearm -> hand
bones = [
    Bone(name="root",      parent_index=-1),
    Bone(name="upper_arm",  parent_index=0,
         local_transform=translate(0, 2, 0)),
    Bone(name="forearm",    parent_index=1,
         local_transform=translate(0, 1.5, 0)),
    Bone(name="hand",       parent_index=2,
         local_transform=translate(0, 1.0, 0)),
]

skeleton = Skeleton(bones)
print(skeleton.bone_count)  # 4

Computing a Pose

Call compute_pose() to walk the hierarchy and produce joint matrices. Optionally override individual bone transforms:

# Default pose (uses each bone's local_transform)
skeleton.compute_pose()

# Animated pose — override forearm rotation
import math
animated = {
    2: rotate((1, 0, 0), math.radians(45)) @ translate((0, 1.5, 0)),  # forearm rotated 45 deg
}
skeleton.compute_pose(bone_transforms=animated)

Joint Matrices

After compute_pose(), the joint_matrices property returns a (bone_count, 4, 4) NumPy array. Each matrix is world_transform @ inverse_bind_matrix – ready to upload to an SSBO.

matrices = skeleton.joint_matrices  # shape (4, 4, 4) for 4 bones

The vertex shader applies skinning by blending up to 4 joint matrices per vertex using bone weights:

vec4 skinned = vec4(0.0);
for (int i = 0; i < 4; i++) {
    skinned += joint_matrices[bone_ids[i]] * vec4(position, 1.0) * bone_weights[i];
}

Finding Bones

Look up a bone index by name:

idx = skeleton.find_bone("forearm")  # 2
idx = skeleton.find_bone("missing")  # -1

API Reference

See simvx.core.skeleton for the complete skeleton API.