///|
/// Animation support for camera paths and object transforms.
/// Keyframe interpolation using Catmull-Rom and linear splines.
pub(all) struct Keyframe {
time : Double
position : Vec3
lookat : Vec3
} derive(Debug)
pub fn Keyframe::new(time~ : Double, position~ : Vec3, lookat~ : Vec3) -> Keyframe {
{ time, position, lookat }
}
pub fn interpolate_keyframe(k1 : Keyframe, k2 : Keyframe, t : Double) -> Keyframe {
let total = k2.time - k1.time
let factor = if total > 0.0 { (t - k1.time) / total } else { 0.0 }
let cf = factor.clamp(min=0.0, max=1.0)
{
time: t,
position: k1.position.lerp(k2.position, cf),
lookat: k1.lookat.lerp(k2.lookat, cf),
}
}
pub fn camera_along_path(keyframes : Array[Keyframe], t : Double) -> (Vec3, Vec3) {
if keyframes.length() == 0 {
return ({ x: 0.0, y: 0.0, z: 0.0 }, { x: 0.0, y: 0.0, z: -1.0 })
}
if keyframes.length() == 1 {
return (keyframes[0].position, keyframes[0].lookat)
}
let mut i = 0
for j in 1.. Vec3 {
let t2 = t * t
let t3 = t2 * t
let a = v0.mul_scalar(-0.5 * t3 + t2 - 0.5 * t)
let b = v1.mul_scalar(1.5 * t3 - 2.5 * t2 + 1.0)
let c = v2.mul_scalar(-1.5 * t3 + 2.0 * t2 + 0.5 * t)
let d = v3.mul_scalar(0.5 * t3 - 0.5 * t2)
a + b + c + d
}
pub fn motion_blur_sample(time0 : Double, time1 : Double) -> (Double, Double) {
let rng = default_rng()
let (t, _) = rng.random_double_range(min=time0, max=time1)
(t, time1 - time0)
}
pub fn smoothstep_edge0(edge0 : Double, edge1 : Double, x : Double) -> Double {
let t = ((x - edge0) / (edge1 - edge0)).clamp(min=0.0, max=1.0)
t * t * (3.0 - 2.0 * t)
}
pub fn ease_in_out_quad(t : Double) -> Double {
if t < 0.5 {
2.0 * t * t
} else {
1.0 - @math.pow(-2.0 * t + 2.0, 2.0) / 2.0
}
}
pub fn ease_in_out_cubic(t : Double) -> Double {
if t < 0.5 {
4.0 * t * t * t
} else {
1.0 - @math.pow(-2.0 * t + 2.0, 3.0) / 2.0
}
}
pub fn create_orbiting_camera_path(
center~ : Vec3,
radius~ : Double,
height~ : Double,
num_frames~ : Int,
duration~ : Double
) -> Array[Keyframe] {
let result = Array::new(capacity=num_frames)
for i in 0..