///|
/// glTF animation interpolation modes supported by the CPU evaluator.
pub(all) enum AnimationInterpolation {
Step
Linear
CubicSpline
} derive(Eq, Debug, ToJson)
///|
pub extend AnimationInterpolation with Debug::{to_repr}
///|
pub extend AnimationInterpolation with Eq::{not_equal, equal}
///|
pub extend AnimationInterpolation with ToJson::{to_json}
///|
pub(all) enum AnimationPath {
Translation
Rotation
Scale
Weights
} derive(Eq, Debug, ToJson)
///|
pub extend AnimationPath with Debug::{to_repr}
///|
pub extend AnimationPath with Eq::{not_equal, equal}
///|
pub extend AnimationPath with ToJson::{to_json}
///|
pub(all) struct AnimationKeyframe {
time : Double
value : Array[Double]
in_tangent : Array[Double]
out_tangent : Array[Double]
} derive(Debug)
///|
pub extend AnimationKeyframe with Debug::{to_repr}
///|
pub fn AnimationKeyframe::new(
time~ : Double,
value~ : Array[Double],
in_tangent? : Array[Double] = [],
out_tangent? : Array[Double] = [],
) -> AnimationKeyframe {
{
time,
value: value.copy(),
in_tangent: in_tangent.copy(),
out_tangent: out_tangent.copy(),
}
}
///|
pub fn AnimationKeyframe::time(self : AnimationKeyframe) -> Double {
self.time
}
///|
pub fn AnimationKeyframe::value(self : AnimationKeyframe) -> Array[Double] {
self.value.copy()
}
///|
pub fn AnimationKeyframe::in_tangent(self : AnimationKeyframe) -> Array[Double] {
self.in_tangent.copy()
}
///|
pub fn AnimationKeyframe::out_tangent(
self : AnimationKeyframe,
) -> Array[Double] {
self.out_tangent.copy()
}
///|
pub(all) struct AnimationChannel {
node : NodeId
path : AnimationPath
interpolation : AnimationInterpolation
keyframes : Array[AnimationKeyframe]
} derive(Debug)
///|
pub extend AnimationChannel with Debug::{to_repr}
///|
pub fn AnimationChannel::new(
node~ : NodeId,
path~ : AnimationPath,
interpolation~ : AnimationInterpolation,
keyframes~ : Array[AnimationKeyframe],
) -> AnimationChannel {
{ node, path, interpolation, keyframes: keyframes.copy(), }
}
///|
pub fn AnimationChannel::node(self : AnimationChannel) -> NodeId {
self.node
}
///|
pub fn AnimationChannel::path(self : AnimationChannel) -> AnimationPath {
self.path
}
///|
pub fn AnimationChannel::interpolation(
self : AnimationChannel,
) -> AnimationInterpolation {
self.interpolation
}
///|
pub fn AnimationChannel::keyframes(
self : AnimationChannel,
) -> Array[AnimationKeyframe] {
self.keyframes.copy()
}
///|
/// A named glTF animation clip. Channels are evaluated independently and can
/// be blended or selected by the application without coupling the scene to a
/// playback clock.
pub(all) struct AnimationClip {
name : String
channels : Array[AnimationChannel]
duration : Double
} derive(Debug)
///|
pub extend AnimationClip with Debug::{to_repr}
///|
pub fn AnimationClip::new(
name? : String = "",
channels~ : Array[AnimationChannel],
) -> AnimationClip {
let mut duration = 0.0
for channel in channels {
for keyframe in channel.keyframes() {
duration = duration.max(keyframe.time())
}
}
{ name, channels: channels.copy(), duration, }
}
///|
pub fn AnimationClip::name(self : AnimationClip) -> String {
self.name
}
///|
pub fn AnimationClip::channels(self : AnimationClip) -> Array[AnimationChannel] {
self.channels.copy()
}
///|
pub fn AnimationClip::duration(self : AnimationClip) -> Double {
self.duration
}
///|
pub(all) struct AnimationSample {
node : NodeId
path : AnimationPath
value : Array[Double]
} derive(Debug)
///|
pub extend AnimationSample with Debug::{to_repr}
///|
pub fn AnimationSample::new(
node~ : NodeId,
path~ : AnimationPath,
value~ : Array[Double],
) -> AnimationSample {
{ node, path, value: value.copy(), }
}
///|
pub fn AnimationSample::node(self : AnimationSample) -> NodeId {
self.node
}
///|
pub fn AnimationSample::path(self : AnimationSample) -> AnimationPath {
self.path
}
///|
pub fn AnimationSample::value(self : AnimationSample) -> Array[Double] {
self.value.copy()
}
///|
/// CPU playback state for a selected clip. Applying samples to node TRS is
/// intentionally left to the scene owner so renderers never own animation
/// clocks or mutable scene state.
pub struct AnimationPlayer {
mut clip : AnimationClip?
mut time : Double
mut playing : Bool
}
///|
pub fn AnimationPlayer::new() -> AnimationPlayer {
{ clip: None, time: 0.0, playing: false, }
}
///|
pub fn AnimationPlayer::set_clip(
self : AnimationPlayer,
clip : AnimationClip,
) -> Unit {
self.clip = Some(clip)
self.time = 0.0
}
///|
pub fn AnimationPlayer::clear_clip(self : AnimationPlayer) -> Unit {
self.clip = None
self.time = 0.0
self.playing = false
}
///|
pub fn AnimationPlayer::play(self : AnimationPlayer) -> Unit {
self.playing = true
}
///|
pub fn AnimationPlayer::pause(self : AnimationPlayer) -> Unit {
self.playing = false
}
///|
pub fn AnimationPlayer::seek(self : AnimationPlayer, time : Double) -> Unit {
self.time = self.clamp_time(time)
}
///|
pub fn AnimationPlayer::advance(
self : AnimationPlayer,
delta_seconds : Double,
) -> Unit {
if self.playing && delta_seconds > 0.0 {
self.time = self.clamp_time(self.time + delta_seconds)
}
}
///|
pub fn AnimationPlayer::time(self : AnimationPlayer) -> Double {
self.time
}
///|
pub fn AnimationPlayer::playing(self : AnimationPlayer) -> Bool {
self.playing
}
///|
pub fn AnimationPlayer::sample(
self : AnimationPlayer,
) -> Array[AnimationSample] {
match self.clip {
None => []
Some(clip) =>
clip
.channels()
.map(channel => {
AnimationSample::new(
node=channel.node(),
path=channel.path(),
value=sample_animation_channel(channel, self.time),
)
})
}
}
///|
fn AnimationPlayer::clamp_time(
self : AnimationPlayer,
value : Double,
) -> Double {
match self.clip {
None => 0.0
Some(clip) => {
let duration = clip.duration()
if duration <= 0.0 {
0.0
} else if value < 0.0 {
0.0
} else if value > duration {
duration
} else {
value
}
}
}
}
///|
pub fn sample_animation_channel(
channel : AnimationChannel,
time : Double,
) -> Array[Double] {
let keys = channel.keyframes()
if keys.length() == 0 {
return []
}
if keys.length() == 1 || time <= keys[0].time() {
return keys[0].value()
}
let last = keys[keys.length() - 1]
if time >= last.time() {
return last.value()
}
let mut index = 0
while index + 1 < keys.length() && time > keys[index + 1].time() {
index = index + 1
}
let first = keys[index]
let second = keys[index + 1]
let span = second.time() - first.time()
let t = if span <= 1.0e-12 { 0.0 } else { (time - first.time()) / span }
let a = first.value()
let b = second.value()
let count = a.length().min(b.length())
let result : Array[Double] = []
for component in 0.. a[component]
Linear => a[component] + (b[component] - a[component]) * t
CubicSpline => {
let out = first.out_tangent()
let incoming = second.in_tangent()
let m0 = if component < out.length() {
out[component] * span
} else {
0.0
}
let m1 = if component < incoming.length() {
incoming[component] * span
} else {
0.0
}
let t2 = t * t
let t3 = t2 * t
(2.0 * t3 - 3.0 * t2 + 1.0) * a[component] +
(t3 - 2.0 * t2 + t) * m0 +
(-2.0 * t3 + 3.0 * t2) * b[component] +
(t3 - t2) * m1
}
}
result.push(value)
}
result
}