74 lines
2.8 KiB
Text
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
|
|
}
|