# clip.ludic - a clip: its channels flattened into one run of key times and one of values, and the # table of every clip read so far, found by name export const AC_ROT: int = 0 export const AC_POS: int = 1 export property Clip { name: string = "", dur: float = 0.0, # seconds: the last key of any channel n: int = 0, # channels cnode: words, # target node per channel (the FILE's node index) cpath: words, # AC_ROT or AC_POS ckeys: words, # keys per channel coff: words, # where this channel starts in `times` cvoff: words, # where it starts in `vals` times: floats, vals: floats } export state AnimState { clips: []Clip = new []Clip positions: bool = false q_a: floats = floats(4) q_b: floats = floats(4) q_cur: floats = floats(4) parts: []floats = pose_parts_new() rot_s: floats = null pos_s: floats = null hit_s: words = null cap: int = 0 } # Keyed translations are off unless asked for: baking keys location on every bone, and a skeleton # does not translate its joints, so applying them turns rounding into a pose. function clips_init() -> void { } # the clip of that name, or null; names are per file (per body, per species), never shared export function anim_find(anim_st: AnimState, name: string) -> Clip { clips_init() for i in 0 .. len(anim_st.clips) { if anim_st.clips[i].name == name { return anim_st.clips[i] } } return null } export function anim_count(anim_st: AnimState) -> int { clips_init() return len(anim_st.clips) } # a clip made outside a glTF (a test, a generated clip); a name already held is refused export function anim_add(anim_st: mut AnimState, c: Clip) -> bool { if c == null or anim_find(anim_st, c.name) != null { return false } push(anim_st.clips, c) return true } export function anim_clear(anim_st: mut AnimState) -> void { anim_st.clips = new []Clip } export function anim_set_positions(anim_st: mut AnimState, on: bool) -> void { anim_st.positions = on } export function anim_positions(anim_st: AnimState) -> bool { return anim_st.positions } # Which key pair straddles time t, by halving rather than walking: a grazing clip is three # hundred keys long and a valley can have dozens of animals posed in a frame. export function anim_key_at(c: Clip, ch: int, t: float) -> int { let n = c.ckeys[ch] let o = c.coff[ch] if n <= 1 { return 0 } if t < c.times[o] { return 0 } if t > c.times[o + n - 1] { return n - 2 } var lo = 0 var hi = n - 1 while hi - lo > 1 { let mid = (lo + hi) / 2 if c.times[o + mid] > t { hi = mid } else { lo = mid } } return lo }