ludic/packages/ludic.anim/pose.ludic

74 lines
2.8 KiB
Text

# pose.ludic - the sampled LOCAL transforms turned into the model-frame deltas `skin_pose` wants.
# skin_pose folds a delta D in as 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 (a root: D = L . R_rest^-1)
# eight quaternions of scratch, made with the state
function pose_parts_new() -> []floats {
let l = new []floats
for k in 0 .. 8 { push(l, floats(4)) }
return l
}
function pose_part(anim_st: AnimState, i: int) -> floats { return anim_st.parts[i] }
export function anim_to_pose(anim_st: mut AnimState, sk: Skin, rot: floats, pos: floats, hit: words) -> void {
let l = pose_part(anim_st, 0)
let rinv = pose_part(anim_st, 1)
let gp = pose_part(anim_st, 2)
let gpi = pose_part(anim_st, 3)
let tmp = pose_part(anim_st, 4)
let d = pose_part(anim_st, 5)
let v = pose_part(anim_st, 6)
for i in 0 .. sk.n_nodes {
let p = sk.par[i]
if (hit[i] & 1) != 0 {
q_load(l, rot, i)
q_load(rinv, sk.rest_r, i)
q_conj(rinv, rinv)
q_mul(tmp, l, rinv)
if p >= 0 {
q_load(gp, sk.rest_g, p)
q_conj(gpi, gp)
q_mul(d, gp, tmp)
q_mul(d, d, gpi)
} else { q_copy(d, tmp) }
q_normalize(d)
skin_set_quat(sk, i, d)
}
if (hit[i] & 2) != 0 and anim_st.positions {
let dx = pos[i * 3] - sk.rest_t[i * 3]
let dy = pos[i * 3 + 1] - sk.rest_t[i * 3 + 1]
let dz = pos[i * 3 + 2] - sk.rest_t[i * 3 + 2]
if p >= 0 {
q_load(gp, sk.rest_g, p)
v3_set(v, dx, dy, dz)
q_rotate(v, gp, v)
skin_set_offset(sk, i, v[0], v[1], v[2])
} else { skin_set_offset(sk, i, dx, dy, dz) }
}
}
}
# The scratch is made ONCE and grown, never allocated per call: a call is per actor per frame.
@alloc_ok("grown once to the largest skeleton posed, kept after")
function scratch(anim_st: mut AnimState, n: int) -> void {
if anim_st.cap >= n { return }
anim_st.rot_s = floats(n * 4)
anim_st.pos_s = floats(n * 3)
anim_st.hit_s = words(n)
anim_st.cap = n
}
# Play `a` at time `ta`, cross-faded toward `b` at `tb` by `blend` (0 is all a, 1 is all b), and
# fold it into the joint matrices. `b` may be null. False when there is no skin or no clip.
export function anim_play(anim_st: mut AnimState, sk: Skin, a: Clip, ta: float, b: Clip, tb: float, blend: float) -> bool {
if sk == null or a == null { return false }
let n = sk.n_nodes
scratch(anim_st, n)
for i in 0 .. n { anim_st.hit_s[i] = 0 }
skin_reset(sk)
anim_mix(anim_st, a, sk, ta, 1.0, anim_st.rot_s, anim_st.pos_s, anim_st.hit_s)
if b != null and blend > 0.001 { anim_mix(anim_st, b, sk, tb, blend, anim_st.rot_s, anim_st.pos_s, anim_st.hit_s) }
anim_to_pose(anim_st, sk, anim_st.rot_s, anim_st.pos_s, anim_st.hit_s)
skin_pose(sk)
return true
}