76 lines
2.7 KiB
Text
76 lines
2.7 KiB
Text
# 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 = null
|
|
positions: bool = false
|
|
q_a: floats = null
|
|
q_b: floats = null
|
|
q_cur: floats = null
|
|
parts: []floats = null
|
|
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(anim_st: mut AnimState) -> void {
|
|
if anim_st.clips == null { anim_st.clips = new []Clip }
|
|
}
|
|
|
|
# the clip of that name, or null; names are per file (per body, per species), never shared
|
|
export function anim_find(anim_st: mut AnimState, name: string) -> Clip {
|
|
clips_init(anim_st)
|
|
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: mut AnimState) -> int {
|
|
clips_init(anim_st)
|
|
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
|
|
}
|