1use super::InterpolatableComponent;
5use common::comp::{Ori, Pos, Vel};
6use specs::Component;
7use tracing::warn;
8use vek::ops::{Lerp, Slerp};
9
10#[derive(Debug)]
11pub struct InterpBuffer<T> {
12 pub buf: [(f64, T); 4],
13 pub i: usize,
14}
15
16impl<T: Clone> InterpBuffer<T> {
17 pub fn new(x: T) -> Self {
18 Self {
19 buf: [
20 (0.0, x.clone()),
21 (0.0, x.clone()),
22 (0.0, x.clone()),
23 (0.0, x),
24 ],
25 i: 0,
26 }
27 }
28
29 fn push(&mut self, time: f64, x: T) {
30 let InterpBuffer {
31 ref mut buf,
32 ref mut i,
33 } = self;
34 *i += 1;
35 *i %= buf.len();
36 buf[*i] = (time, x);
37 }
38
39 fn force_update(&mut self, time: f64, x: T) {
40 for i in 0..self.buf.len() {
41 self.buf[i] = (time, x.clone());
42 }
43 }
44
45 fn update(&mut self, time: f64, x: T, force_update: bool) {
46 if force_update {
47 self.force_update(time, x);
48 } else {
49 self.push(time, x);
50 }
51 }
52}
53
54impl<T: 'static + Send + Sync> Component for InterpBuffer<T> {
55 type Storage = specs::VecStorage<Self>;
56}
57
58const PHYSICS_VS_EXTRAPOLATION_FACTOR: f32 = 0.1;
60const POSITION_INTERP_SANITY: Option<f32> = None;
61const VELOCITY_INTERP_SANITY: Option<f32> = None;
62const ENABLE_POSITION_HERMITE: bool = false;
63
64impl InterpolatableComponent for Pos {
65 type InterpData = InterpBuffer<Pos>;
66 type ReadData = InterpBuffer<Vel>;
67
68 fn new_data(x: Self) -> Self::InterpData { InterpBuffer::new(x) }
69
70 fn update_component(&self, interp_data: &mut Self::InterpData, time: f64, force_update: bool) {
71 interp_data.update(time, *self, force_update);
72 }
73
74 fn interpolate(self, interp_data: &Self::InterpData, t2: f64, vel: &InterpBuffer<Vel>) -> Self {
75 let InterpBuffer { ref buf, ref i } = interp_data;
77 let (t0, p0) = buf[(i + buf.len() - 1) % buf.len()];
78 let (t1, p1) = buf[i % buf.len()];
79 if (t1 - t0).abs() < f64::EPSILON {
80 return self;
81 }
82 if POSITION_INTERP_SANITY.is_some_and(|limit| p0.0.distance_squared(p1.0) > limit.powf(2.0))
83 {
84 warn!("position delta exceeded sanity check, clamping");
85 return p1;
86 }
87 let (t0prime, m0) = vel.buf[(i + vel.buf.len() - 1) % vel.buf.len()];
88 let (t1prime, m1) = vel.buf[i % vel.buf.len()];
89 let t = (t2 - t0) / (t1 - t0);
90 let mut out = if ENABLE_POSITION_HERMITE
91 && ((t0 - t0prime).abs() < f64::EPSILON && (t1 - t1prime).abs() < f64::EPSILON)
92 {
93 let h00 = |t: f64| (2.0 * t.powf(3.0) - 3.0 * t.powf(2.0) + 1.0) as f32;
94 let h10 = |t: f64| (t.powf(3.0) - 2.0 * t.powf(2.0) + t) as f32;
95 let h01 = |t: f64| (-2.0 * t.powf(3.0) + 3.0 * t.powf(2.0)) as f32;
96 let h11 = |t: f64| (t.powf(3.0) - t.powf(2.0)) as f32;
97 let dt = (t1 - t0) as f32;
98 h00(t) * p0.0 + h10(t) * dt * m0.0 + h01(t) * p1.0 + h11(t) * dt * m1.0
99 } else {
100 if ENABLE_POSITION_HERMITE {
101 warn!(
102 "timestamps for pos and vel don't match ({:?}, {:?}), falling back to lerp",
103 interp_data, vel
104 );
105 }
106 Lerp::lerp_unclamped(p0.0, p1.0, t as f32)
107 };
108
109 if out.map(|x| x.is_nan()).reduce_or() {
110 warn!("interpolation output is nan: {}, {}, {:?}", t2, t, buf);
111 out = p1.0;
112 }
113
114 Pos(Lerp::lerp(self.0, out, PHYSICS_VS_EXTRAPOLATION_FACTOR))
115 }
116}
117
118impl InterpolatableComponent for Vel {
119 type InterpData = InterpBuffer<Vel>;
120 type ReadData = ();
121
122 fn new_data(x: Self) -> Self::InterpData { InterpBuffer::new(x) }
123
124 fn update_component(&self, interp_data: &mut Self::InterpData, time: f64, force_update: bool) {
125 interp_data.update(time, *self, force_update);
126 }
127
128 fn interpolate(self, interp_data: &Self::InterpData, t2: f64, _: &()) -> Self {
129 let InterpBuffer { ref buf, ref i } = interp_data;
130 let (t0, p0) = buf[(i + buf.len() - 1) % buf.len()];
131 let (t1, p1) = buf[i % buf.len()];
132 if (t1 - t0).abs() < f64::EPSILON {
133 return self;
134 }
135 if VELOCITY_INTERP_SANITY.is_some_and(|limit| p0.0.distance_squared(p1.0) > limit.powf(2.0))
136 {
137 warn!("velocity delta exceeded sanity check, clamping");
138 return p1;
139 }
140 let lerp_factor = 1.0 + ((t2 - t1) / (t1 - t0)) as f32;
141 let mut out = Lerp::lerp_unclamped(p0.0, p1.0, lerp_factor);
142 if out.map(|x| x.is_nan()).reduce_or() {
143 warn!(
144 "interpolation output is nan: {}, {}, {:?}",
145 t2, lerp_factor, buf
146 );
147 out = p1.0;
148 }
149
150 Vel(Lerp::lerp(self.0, out, PHYSICS_VS_EXTRAPOLATION_FACTOR))
151 }
152}
153
154impl InterpolatableComponent for Ori {
155 type InterpData = InterpBuffer<Ori>;
156 type ReadData = ();
157
158 fn new_data(x: Self) -> Self::InterpData { InterpBuffer::new(x) }
159
160 fn update_component(&self, interp_data: &mut Self::InterpData, time: f64, force_update: bool) {
161 interp_data.update(time, *self, force_update);
162 }
163
164 fn interpolate(self, interp_data: &Self::InterpData, t2: f64, _: &()) -> Self {
165 let InterpBuffer { ref buf, ref i } = interp_data;
166 let (t0, p0) = buf[(i + buf.len() - 1) % buf.len()];
167 let (t1, p1) = buf[i % buf.len()];
168 if (t1 - t0).abs() < f64::EPSILON {
169 return self;
170 }
171 let lerp_factor = 1.0 + ((t2 - t1) / (t1 - t0)) as f32;
172 let mut out = Slerp::slerp_unclamped(p0.to_quat(), p1.to_quat(), lerp_factor);
173 if out.into_vec4().map(|x| x.is_nan()).reduce_or() {
174 warn!(
175 "interpolation output is nan: {}, {}, {:?}",
176 t2, lerp_factor, buf
177 );
178 out = p1.to_quat();
179 }
180
181 Ori::new(Slerp::slerp(self.to_quat(), out, PHYSICS_VS_EXTRAPOLATION_FACTOR).normalized())
182 }
183}