flutter3d
Showcase Changelog 38 packages API reference

Layers and masks

since 0.5.2 Animation

A crossfade moves the whole skeleton from one clip to another. Sometimes only part of it should change: an arm waving while the body keeps walking. That is what a layer is for, and AnimationMask is how it says which joints it may touch.

Step 1: Two clips that both mention the arm #

The base clip, walk, animates the body and the arm both. The layer clip, wave, only has a track for the arm. Both tracks address the same node index, so they compete for the same joint.

final AnimationClip walk = AnimationClip(
  name: 'walk',
  tracks: <AnimationTrack>[
    _rotationTrack(nodeIndex: 0, axis: Vector3(0, 0, 1), peak: 0.12),
    _rotationTrack(nodeIndex: 1, axis: Vector3(1, 0, 0), peak: 0.5),
  ],
);
_waveTrack = _rotationTrack(
  nodeIndex: 1,
  axis: Vector3(0, 0, 1),
  peak: 1.1,
);
final AnimationClip wave = AnimationClip(
  name: 'wave',
  tracks: <AnimationTrack>[_waveTrack],
);

Step 2: A layer masked to one joint #

playLayer starts the wave clip as a layer over the base. The mask names node 1, the arm, and nothing else: the body keeps playing whatever the base clip says regardless of what the layer does.

_player = AnimationPlayer(
  clips: <AnimationClip>[walk, wave],
  targets: <AnimationTarget?>[bodyMesh, armMesh],
)..play(0);
_wave = _player.playLayer(
  1,
  mask: AnimationMask(<int>[1]),
  wrap: AnimationWrap.loop,
  fadeIn: 0.0,
);

Step 3: Advance the player #

A layer has its own playhead, but it only moves when the player is asked to.

_player.update(dt);

What to look at #

The body keeps its own small tilt from the walk clip the whole time: the mask never lets the layer near it. The arm, though, follows the wave clip and nothing of the walk clip's own arm swing shows through, because a layer with full weight replaces the base's value for the joints it covers rather than blending with it.