simvx.core.animation.skeletal¶
Skeletal animation: bone tracks and skeletal clips.
Module Contents¶
Classes¶
Animation track for a single bone: position, rotation, scale keyframes. |
|
Animation clip with bone tracks for skeletal animation. |
Functions¶
Compose translation, rotation (quat xyzw) and scale into a 4x4 matrix. |
|
Inverse of :func: |
|
Blend two TRS poses by factor t in [0, 1] -> composed 4x4 matrix. |
API¶
- class simvx.core.animation.skeletal.BoneTrack(bone_index: int)[source]¶
Animation track for a single bone: position, rotation, scale keyframes.
Rotation keyframes use quaternions [x, y, z, w] for spherical interpolation.
Initialization
- add_position_key(time: float, position: numpy.ndarray) None[source]¶
Insert a position (vec3) keyframe, keeping the list time-sorted.
- add_rotation_key(time: float, rotation: numpy.ndarray) None[source]¶
Insert a rotation (quaternion xyzw) keyframe, keeping the list time-sorted.
- add_scale_key(time: float, scale: numpy.ndarray) None[source]¶
Insert a scale (vec3) keyframe, keeping the list time-sorted.
- sample_trs(time: float, rest: tuple | None = None) tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray][source]¶
Interpolate keyframes at time -> (translation, rotation_xyzw, scale).
Components are returned in TRS space (not composed into a matrix) so callers can blend two poses correctly: SLERP rotation, LERP position and scale. :meth:
samplebuilds the matrix from these.glTF animation channels are sparse: a bone may animate only its rotation, leaving translation/scale at the bone’s BIND values for the whole clip. Pass
restas the bone’s bind(t, r, s)so a channel with no keys falls back to that rest value; without it, a missing channel falls back to the neutral default (0 / identity / 1), which collapses such rigs.
- sample(time: float, rest: tuple | None = None) numpy.ndarray[source]¶
Interpolate keyframes at given time -> 4x4 local transform matrix.
- classmethod from_dict(data: dict) simvx.core.animation.skeletal.BoneTrack[source]¶
Deserialize a bone track, restoring keys as float32 arrays.
- class simvx.core.animation.skeletal.SkeletalAnimationClip(name: str, duration: float)[source]¶
Animation clip with bone tracks for skeletal animation.
Initialization
- add_bone_track(track: simvx.core.animation.skeletal.BoneTrack) None[source]¶
- evaluate(time: float, rest_poses: dict | None = None) dict[int, numpy.ndarray][source]¶
Evaluate all bone tracks at given time.
Returns dict mapping bone_index -> 4x4 local transform.
rest_poses({bone_index: (t, r, s)}) supplies each bone’s bind TRS so sparse channels fall back to rest (see :meth:BoneTrack.sample_trs).
- evaluate_trs(time: float, rest_poses: dict | None = None) dict[int, tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]][source]¶
Evaluate all bone tracks at time as TRS components per bone.
Returns dict mapping bone_index -> (translation, rotation_xyzw, scale). Used by crossfade blending, which must operate in TRS space.
rest_posessupplies each bone’s bind TRS so sparse channels fall back to rest.
- classmethod from_dict(data: dict) simvx.core.animation.skeletal.SkeletalAnimationClip[source]¶
Deserialize a skeletal clip.
- simvx.core.animation.skeletal.compose_trs(translation: numpy.ndarray, rotation_xyzw: numpy.ndarray, scale: numpy.ndarray) numpy.ndarray[source]¶
Compose translation, rotation (quat xyzw) and scale into a 4x4 matrix.
- simvx.core.animation.skeletal.decompose_trs(mat: numpy.ndarray) tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray][source]¶
Inverse of :func:
compose_trs: a 4x4 matrix -> (translation, rotation_xyzw, scale).Used to recover a bone’s bind TRS from its rest
local_transformso sparse animation channels can fall back to the correct rest value.
- simvx.core.animation.skeletal.blend_trs(a: tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray], b: tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray], t: float) numpy.ndarray[source]¶
Blend two TRS poses by factor t in [0, 1] -> composed 4x4 matrix.
Position and scale are linearly interpolated; rotation uses SLERP. Blending happens in TRS space (never by lerping matrices) so the result stays a valid rigid-plus-scale transform.