Skip to content
View source

animationGenerated

Category: Packages · Manifest: use "kernels/";

A skeleton, the clips that pose it and the mesh that follows.

A skeleton, the clips that pose it and the mesh that follows: sampling and blending clips, walking, aiming, and skinning by the joint palette.

Layer: Simulation. Uses: geometry, mesh.

use "animation.entpkg";
  • kernels/aim.ent — Aiming. Kernels: animation.aim@1.
  • kernels/clip.ent — Clips. Kernels: animation.clock@1, animation.sample@1, animation.blend@1, animation.add@1.
  • kernels/gait.ent — Walking. Kernels: animation.stride@1, animation.travel@1.
  • kernels/skeleton.ent — A skeleton, the clips that pose it, and the mesh that follows.
  • kernels/skin.ent — Skinning. Kernels: animation.palette@1, animation.apply@1, animation.place@1.
  • graphs/pose.esn — One clip playing on one skeleton, as one node.

tests/ holds scenes and their replays, run on every backend: aim.replay, clips.replay. Every Kernel of the package is reached by one of them.

Used by the animation, voxel examples.

Kernel Mode Brief
animation.add@1 map A pose laid over another: what add does differently from rest is done to base, by weight. A clip authored as a difference — a flinch, a lean, a breath — plays over whatever the body is already doing, which is what a game blends on top of a walk.
animation.aim@1 map One joint turned to face a point, over whatever the poses already say: a head that follows what it looks at, an arm that reaches, a gun that tracks. The joint’s axis, in its own space (an imported bone often runs along its y), is turned the shortest way onto the target, and that turn is laid over the pose the joint already had, so the twist the clip gives it stays; weight blends between the two. Every other joint passes through, so this sits between sampling and the palette and the chain below the joint follows it.
animation.apply@1 map The mesh in the pose the palette says: four joints a vertex, weighted. A node sized instances * count(mesh) poses a crowd out of one mesh, each instance reading its own block of the palette.
animation.blend@1 map Two poses of one skeleton, blended: a walk into a run, a turn laid over either. A Kernel or the graph decides mix, so it can move every frame.
animation.clock@1 map The clock a clip is read at, as a row of one second: the simple case, where a clip plays at its own speed from the start of the session.
animation.palette@1 map The palette: each joint’s local transform composed up the parent chain and taken through its inverse bind, which is the matrix a vertex bound to that joint is moved by.
animation.place@1 map Where each instance stands: one Pose a instance, applied to the mesh it posed. A crowd is this over animation.apply@1’s output.
animation.sample@1 map Every joint’s local transform at the moment the clock says, blended between the two frames it falls between. A clip ends where its rows end: repeat wraps round to the start, and otherwise the last frame holds.
animation.stride@1 map A traveling clip played in place: every joint as animation.sample@1 would pose it, looping every frames - 1 frames as the clip closes on itself, and the root with its travel along the ground axes taken out. What stays in the root is what is not travel — a walk’s bob, its sway on an axis ground leaves out — and the travel is what animation.travel@1 gives the scene to carry.
animation.travel@1 map How far a traveling clip’s root moved between two moments, was and at, one row an instance: the travel animation.stride@1 took out of the pose, for the scene to add to where the body stands. It is the difference of where the root had got to at each, so a scene that adds it every evaluation stands where the clip says whatever its frames are, loops and all.
Function Brief
standing(poses: Rows<Pose>, skeleton: Skeleton, block: Int, joint: Int, depth: Int) -> Mat4
traveled(clip: Clip, joints: Int, joint: Int, moment: Float, ground: Vec3) -> Vec3
posing(p: Pose) -> Mat4
mixing(a: Pose, b: Pose, t: Float) -> Pose
Name Syntax
Walk struct Walk { matrix: Mat4; joint: Int; }
Joint struct Joint { parent: Int; bind: Mat4; }
Pose struct Pose { at: Vec3; turn: Vec4; grow: Vec3; }
Bound struct Bound { joints: [Int; 4]; weights: Vec4; }
Skeleton type Skeleton = Rows<Joint>;
Clip type Clip = Rows<Pose>;
Palette type Palette = Rows<Mat4>;
Name Interface Description
pose input mesh: Rows<MeshPoint>
input weights: Rows<Bound>
input skeleton: Skeleton
input clip: Clip
input at: Rows<Float>
param rate: Float = 30.0
param repeat: Bool = true
param depth: Int = 64
output result <- posed.result
output palette <- palette.result
One clip playing on one skeleton, as one node. Place it with node posed: graph “animation.entpkg/graphs/pose.esn” { mesh <- mesh; weights <- weights; skeleton <- skeleton; clip <- walk; at <- clock.result; rate = 30.0; } and hand posed.result to whatever projects it. Blending two clips wants the Kernels themselves: sample each, animation.blend@1, then animation.palette@1.

A pose laid over another: what add does differently from rest is done to base, by weight. A clip authored as a difference — a flinch, a lean, a breath — plays over whatever the body is already doing, which is what a game blends on top of a walk.

rest is the frame the difference is measured from, which is the additive clip’s own first frame: sample it at zero.

Mode map
Requires field.read, field.write
Inputs base: Rows<Pose>, add: Rows<Pose>, rest: Rows<Pose>, weight: Rows<Float>
Params —
Outputs result: Rows<Pose>

One joint turned to face a point, over whatever the poses already say: a head that follows what it looks at, an arm that reaches, a gun that tracks. The joint’s axis, in its own space (an imported bone often runs along its y), is turned the shortest way onto the target, and that turn is laid over the pose the joint already had, so the twist the clip gives it stays; weight blends between the two. Every other joint passes through, so this sits between sampling and the palette and the chain below the joint follows it.

target is in the skin’s space, one row an instance: a crowd sized instances * count(skeleton) has each instance look at its own.

Mode map
Requires field.read, field.write
Inputs poses: Rows<Pose>, skeleton: Rows<Joint>, target: Rows<Vec3>, weight: Rows<Float>
Params joint: Int = 0, axis: Vec3 = [0.0, 1.0, 0.0], depth: Int = 64
Outputs result: Rows<Pose>
Source packages/animation.entpkg/kernels/aim.ent

The mesh in the pose the palette says: four joints a vertex, weighted. A node sized instances * count(mesh) poses a crowd out of one mesh, each instance reading its own block of the palette.

A vertex bound to nothing — all four weights zero, which is what a model that mixes skinned and unskinned meshes writes — is left where it is.

Mode map
Requires field.read, field.write
Inputs mesh: Rows<MeshPoint>, weights: Rows<Bound>, palette: Rows<Mat4>, skeleton: Rows<Joint>
Params —
Outputs result: Rows<MeshPoint>

Two poses of one skeleton, blended: a walk into a run, a turn laid over either. A Kernel or the graph decides mix, so it can move every frame.

mix of one row is the whole skeleton. A row a joint is a mask: an upper body running while the legs walk is the same blend with ones down the arms and zeros down the legs.

Mode map
Requires field.read, field.write
Inputs first: Rows<Pose>, second: Rows<Pose>, mix: Rows<Float>
Params —
Outputs result: Rows<Pose>

The clock a clip is read at, as a row of one second: the simple case, where a clip plays at its own speed from the start of the session.

Mode map
Requires field.write, time.read
Inputs —
Params speed: Float = 1.0
Outputs result: Rows<Float>

The palette: each joint’s local transform composed up the parent chain and taken through its inverse bind, which is the matrix a vertex bound to that joint is moved by.

Mode map
Requires field.read, field.write
Inputs poses: Rows<Pose>, skeleton: Rows<Joint>
Params depth: Int = 64
Outputs result: Rows<Mat4>

Where each instance stands: one Pose a instance, applied to the mesh it posed. A crowd is this over animation.apply@1’s output.

Mode map
Requires field.read, field.write
Inputs mesh: Rows<MeshPoint>, places: Rows<Pose>, one: Rows<MeshPoint>
Params —
Outputs result: Rows<MeshPoint>

Every joint’s local transform at the moment the clock says, blended between the two frames it falls between. A clip ends where its rows end: repeat wraps round to the start, and otherwise the last frame holds.

A node sized instances * count(skeleton) poses a crowd instead of one: element e is joint e % joints of instance e / joints, and each instance reads its own row of at, so a hundred of them walk the same clip out of step. Sized for one — which is what a node with no domain of its own is — it is the same Kernel doing the same thing.

Mode map
Requires field.read, field.write
Inputs skeleton: Rows<Joint>, clip: Rows<Pose>, at: Rows<Float>
Params rate: Float = 30.0, repeat: Bool = True
Outputs result: Rows<Pose>

A traveling clip played in place: every joint as animation.sample@1 would pose it, looping every frames - 1 frames as the clip closes on itself, and the root with its travel along the ground axes taken out. What stays in the root is what is not travel — a walk’s bob, its sway on an axis ground leaves out — and the travel is what animation.travel@1 gives the scene to carry.

Mode map
Requires field.read, field.write
Inputs skeleton: Rows<Joint>, clip: Rows<Pose>, at: Rows<Float>
Params rate: Float = 30.0, joint: Int = 0, ground: Vec3 = [1.0, 0.0, 1.0]
Outputs result: Rows<Pose>
Source packages/animation.entpkg/kernels/gait.ent

How far a traveling clip’s root moved between two moments, was and at, one row an instance: the travel animation.stride@1 took out of the pose, for the scene to add to where the body stands. It is the difference of where the root had got to at each, so a scene that adds it every evaluation stands where the clip says whatever its frames are, loops and all.

Mode map
Requires field.read, field.write
Inputs skeleton: Rows<Joint>, clip: Rows<Pose>, at: Rows<Float>, was: Rows<Float>
Params rate: Float = 30.0, joint: Int = 0, ground: Vec3 = [1.0, 0.0, 1.0]
Outputs result: Rows<Vec3>
Source packages/animation.entpkg/kernels/gait.ent

aim.replay, clips.replay