ludic/packages/ludic.anim/set_tick.ludic

53 lines
2 KiB
Text

# set_tick.ludic - a frame of a player: follow the graph, pick the clip, cross-fade on a change,
# advance both clips by the same step and play them into the skin (ludic.anim)
export function animset_tick(anim_st: mut AnimState, pl: AnimPlayer, sk: Skin, dt: float) -> bool {
if not pl.ready or sk == null { return false }
as_follow(pl)
let d = AnimSets[pl.set].states[pl.state]
let k = as_pick(pl, d)
if k < 0 { return false }
pl.pick = k
pl.rate = as_rate(pl, d, d.choices[k])
as_switch(pl, d, pl.clips[pl.base[pl.state] + k])
let adv = dt * pl.rate
pl.t = pl.t + adv
pl.pt = pl.pt + adv
as_one_shot(pl, d)
if pl.mix < 1.0 { pl.mix = Math.min(1.0, pl.mix + dt / pl.fade) }
# anim_play blends TOWARD its second clip, so the weight is how much of the old one is left
return anim_play(anim_st, sk, pl.clip, pl.t, pl.prev, pl.pt, 1.0 - pl.mix)
}
# a new clip fades in from the old one. Within a state it carries the PHASE across: two leg cycles
# restarted at zero put the far leg forward for the length of the fade, which reads as a stumble.
function as_switch(pl: AnimPlayer, d: AnimStateDef, want: Clip) -> void {
var fade = d.blend
if pl.fade_next > 0.0 { fade = pl.fade_next }
pl.fade_next = -1.0
if pl.clip == null {
pl.clip = want
pl.t = 0.0
pl.prev = null
pl.mix = 1.0
return
}
if want == pl.clip { return }
var ph = 0.0
if d.keep_phase and not pl.fresh and pl.clip.dur > 0.0 { ph = pl.t % pl.clip.dur / pl.clip.dur }
pl.prev = pl.clip
pl.pt = pl.t
pl.clip = want
pl.t = ph * want.dur
pl.fade = fade
pl.mix = 0.0
if not (fade > 0.0) { pl.mix = 1.0 }
}
# a clip that does not loop holds its last pose, and hands over to its `next` state when played
function as_one_shot(pl: AnimPlayer, d: AnimStateDef) -> void {
if d.loop or pl.clip.dur <= 0.0 or pl.t < pl.clip.dur { return }
pl.t = pl.clip.dur * 0.999
if d.next == "" { return }
let i = as_state(AnimSets[pl.set], d.next)
if i >= 0 { pl.want = i }
}