Animation evaluation and blending
Examine clip sampling, per-joint coverage, priority mixing, blend-space weights, transition continuity, and root-motion extraction.
Asterune evaluates animation as local joint transforms with a coverage value for each joint. Clips and graph layers can leave some joints uncovered, so an upper-body motion can combine with locomotion without replacing every limb. The runtime advances playback, samples contributions, mixes them by priority, applies procedural controls, and writes the resulting motor transforms.
Animation runs before world simulation. Its resulting pose describes rig articulation; extracted movement and network ownership have separate application rules. Retargeting and procedural rigs explains the spaces and solvers used after clip selection.
Compiled clips and rig bindings
A keyframe sequence becomes a clip containing sorted tracks, keyframe times, and named events. A track identifies both a parent name and a joint's child part name. The runtime caches the clip against the sequence ID and animation revision rather than rebuilding its sorted representation for every sample.
The rig layout stores joints in parent-first order. Clip binding first matches the track's parent and child names. A name-only fallback is available when the rig and clip each provide an unambiguous match. Repeated limb names under different parents therefore need the parent information to select the intended track.
Rig topology revisions invalidate cached layouts. Joint frame values refresh separately, while clip bindings depend on the clip, layout signature, and retarget binding. This separates changes to the skeleton's membership from changes to an active pose.
Key sampling and time crossings
Sampling finds the first key whose time is at least the requested time. Before the first key or after the last key, the sampler holds that endpoint. Between keys, it applies the destination key's easing style and direction to the normalized interval. Translation, pose weight, and mask weight interpolate linearly with that eased value; rotation uses normalized spherical interpolation.
The sampled joint coverage is
clamp(pose_weight × (1 - mask_weight), 0, 1). An unkeyed joint has zero
coverage. Its identity transform is a storage default, rather than a command
to reset that joint over another contribution.
Playback time and marker delivery use separate representations. A looping track wraps its sample time with Euclidean remainder, including negative playback. Marker detection examines the interval crossed by the advance. The old endpoint is excluded and the new endpoint is included, preventing the same marker from firing again merely because the next frame starts exactly there. Reverse playback traverses markers in the reverse order.
Marker traversal accounts for loop boundaries and bounds work across very large time jumps. The track's loop notification count is capped at 16 per step. Named-event traversal separately bounds its visited cycle span. A long stall therefore does not generate an unbounded sequence of old marker callbacks. Queued animation signals fire after the frame's pose composition, so a marker callback observes the newly evaluated rig rather than a partially mixed pose.
A nonlooping track holds its end sample while its weight fades out. Stopping playback and removing its contribution are separate state changes; the mixer continues to include the fading contribution until its weight becomes inactive.
Per-joint priority composition
Tracks and finished graph poses enter a common contribution list for each rig. The mixer groups contributions by priority rank and processes ranks from lowest to highest. Each rank first computes a weighted mean independently for every joint.
For one contribution, the joint's share is its playback weight multiplied by its sampled coverage, clamped to 0 through 1. Translation accumulates with that share. Quaternion signs are aligned to the accumulated hemisphere before the weighted components are added, preventing equivalent opposite quaternion representations from cancelling each other. The resulting quaternion is normalized.
The rank's total joint coverage is the sum of its shares capped at 1. The mixer overlays that rank's mean pose by this coverage. A high-priority motion with coverage 0.25 therefore leaves 75% of the lower-priority result at that joint. Two equal-priority motions that each fully cover the joint instead form one mean with full coverage. Priority is resolved per joint, rather than choosing one winning track for the whole character.
Procedural controls then modify the composed pose in their priority order. The runtime writes the final motor transforms and synchronizes the character rig or model. A rig whose last graph or control stops still receives a final composition pass, avoiding a pose left frozen by a contribution that disappeared.
Blend-space influence and cycle duration
A one-dimensional blend space uses linear weights between neighboring sorted thresholds. Inputs outside the threshold range select the nearest endpoint. Samples sharing a threshold divide that threshold's weight evenly.
Two-dimensional blending uses gradient-band interpolation, without a triangle mesh. For each sample, the algorithm projects the input toward every other sample and retains the smallest remaining influence. It clamps negative influence to zero, then normalizes all sample weights. An exact sample position selects that sample, sharing weight among duplicates. If every influence is zero, the nearest sample supplies the result.
Directional mode measures those bands in relative speed and signed angle, with angle weighted by 2. A central idle sample primarily constrains radial speed. Walking between 2 directions can therefore blend by direction without treating the sample layout as ordinary Cartesian coordinates.
Nested motions multiply influence and playback speed on their path to leaf clips. The state's cycle duration is the weighted mean of each active clip's length divided by its absolute leaf speed. The state advances normalized progress against that duration, allowing clips of different lengths to share one blend-space phase. Missing or negligible leaf contributions are excluded from that timing calculation.
State transitions and interruption
A graph compiles state, parameter, and clip names into indexes. Each update advances current plays, live transition sources, layer weight fades, and transition timers before evaluating another transition. A layer with a noninterruptible fade does not select a new transition during that fade.
Automatic selection checks any-state transitions before the current state's outgoing transitions, preserving definition order within each list. Every condition must hold, and an exit-time condition must be reached. For a looping state, an exit time within one cycle tests a phase crossing; an exit time above 1 tests a particular accumulated progress boundary. Triggers are consumed by the transition that fires rather than cleared merely because they were tested.
A normal transition retains a live source play and advances both source and destination. If another transition interrupts an existing fade, the runtime uses the preceding evaluated blended pose as a frozen source. That preserves the visible starting pose instead of returning abruptly to one of the old clips.
During evaluation, the source and destination form a weighted pose mean. Their coverages also blend, so a joint present on only one side fades in or out. The graph retains each layer's last evaluated pose for a possible later interruption.
Explicit graph travel finds a route through transition edges with breadth-first search. The queued route selects the required next edge and observes its exit time, rather than requiring its ordinary parameter conditions. Travel and automatic condition-based selection therefore have different purposes inside the same state machine.
Layer masks, additive poses, and synchronization
Graph layers compose in definition order. An override layer overlays each
joint by layer_weight × mask_weight × coverage. An additive layer first
subtracts a reference sample taken at clip time zero. It adds weighted
translation and multiplies the weighted rotational delta in the joint's local
frame. The additive reference is the clip's initial pose, rather than an
assumed universal bind pose.
A mask entry can propagate its weight through descendants. A descendant's own entry overrides that inherited value; joints without an applicable entry receive zero mask weight. Mask resolution uses the rig hierarchy rather than matching arbitrary substrings in joint names.
A synchronization group chooses its most heavily weighted live play as leader. Followers adopt the leader's phase. When clips share named events, the runtime maps progress between corresponding surrounding events, including repeated occurrences of a name. It falls back to normalized phase when marker alignment is unavailable.
Graph marker events come from the dominant clip of one selected play per layer. During a fade, the source supplies events before the midpoint and the destination supplies them afterward. The graph does not emit every leaf's markers independently, which would duplicate an event shared by blended clips.
Extracted root travel
Root extraction reads the first layer's live plays and weights their root-track travel. A forward loop crossing includes travel to the clip's endpoint, whole intervening cycles, and travel from the next cycle's start. Subtracting only wrapped sample positions would instead report a backwards jump at the boundary.
The runtime converts translation through the root joint frame, reports its horizontal X and Z movement, and reports yaw around Y. It removes that horizontal translation and yaw twist from the evaluated root pose while retaining vertical motion and swing. The extracted delta is available for the game's movement system to apply; it does not directly teleport an authoritative character through collision geometry.
After rig evaluation, mesh deformation consumes the resulting joint matrices. The GPU and previous-vertex paths are described in Geometry, depth, and transparency.