|
|
||
|---|---|---|
| .. | ||
| tests | ||
| bones.ludic | ||
| clip.ludic | ||
| index.ludic | ||
| mix.ludic | ||
| package.ludic | ||
| pose.ludic | ||
| read.ludic | ||
| README.md | ||
ludic.anim
The animation clips an animator keyed into a glTF, played on a render3d Skin and cross-faded two
at a time. ludic.render3d parses no animations and offers a pose api (skin_set_quat,
skin_set_offset, skin_pose) rather than a player, so the sampler lives here. Uses
ludic_render3d and nothing else: it reads the document gltf_load just parsed and writes a
Skin's pose. It asks the game nothing, so it has no port.
import "ludic.anim"
let m = gltf_load("assets/kit/hiker2", "hiker_m.gltf", "hiker_m")
anim_read_doc("assets/kit/hiker2") # straight after the load, before the next
let walk = anim_find("hiker_m_walk")
anim_play(sk, walk, t, idle, t2, 0.3) # 70% walk, 30% idle, into the joint matrices
API
Clip { name, dur, n, cnode, cpath, ckeys, coff, cvoff, times, vals }, AC_ROT, AC_POS |
a clip, its channels flattened: a channel's keys are times[coff..coff+ckeys], its values 4 (rotation) or 3 (translation) per key from cvoff |
anim_read_doc(dir) -> int |
every rotation and translation channel of every animation in gltf_doc, reading the .bin beside it in dir; how many clips were new |
anim_find(name) -> Clip, anim_count(), anim_add(c) -> bool, anim_clear() |
the table, by name; a name already held is skipped on read and refused on add |
anim_play(sk, a, ta, b, tb, blend) -> bool |
a at ta, cross-faded toward b at tb by blend (0 all a, 1 all b; b may be null), folded into the joint matrices |
anim_mix(c, sk, t, w, rot, pos, hit), anim_to_pose(sk, rot, pos, hit), anim_key_at(c, ch, t) |
the pieces anim_play is made of, for a caller that mixes more than two |
anim_set_positions(on), anim_positions() |
keyed translations applied or not (off: baking keys location on every bone, and rounding down an eighteen-bone chain is a collapsed animal) |
skin_joint(sk, name), skin_joint_quiet(sk, name) |
a bone by name in the skin's OWN joints: a kit packs several rigs with the same bone names, and render3d's skin_find answers the file's first, which belongs to another rig (the pose is then stored and never drawn) |
anim_first_key(c) -> float |
the earliest key; a read warns when it is not zero |
Rules it keeps
- Clips are named per rig. A channel addresses its FILE's node indices, two files number their
nodes differently, and a name already held is skipped - so one shared name would hand the
second body the first one's skeleton. Name them
hiker_m_walk/hiker_f_walk,bear_walk. - Read straight after the load.
gltf_docis whichever file was parsed last, andgltf_loadcloses the.bin, whichanim_read_docreopens (a read through the closed handle is zeros). - Key from frame zero. A clip wraps on its duration and holds below its first key, so a clip
keyed from frame 1 holds its opening pose for 1/30 s every cycle.
anim_read_docprints it. - The math.
skin_posetakes a delta D in the model frame,local = (G_p^-1 D G_p) R_rest, and a channel gives the absolute local L, so D = G_p (L R_rest^-1) G_p^-1. - The scratch is made once and grown: a call is per actor per frame.
Tests
ludic test packages/ludic.anim
A two-bone skin made from a glTF document with no GPU: sampling, wrapping, the cross-fade,
translations off by default, and clips read out of a .bin written to the temp directory.