Skip to content

Commit 4bd3114

Browse files
committed
Copy Godot's Quat::slerp
1 parent 7ad7dba commit 4bd3114

File tree

1 file changed

+46
-3
lines changed
  • gdnative-core/src/core_types

1 file changed

+46
-3
lines changed

gdnative-core/src/core_types/quat.rs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use super::{Basis, IsEqualApprox, Vector3};
1+
use super::{Basis, IsEqualApprox, Vector3, CMP_EPSILON};
22
use glam::EulerRot;
3-
use std::ops::Mul;
3+
use std::ops::{Mul, Neg};
44

55
#[derive(Copy, Clone, Debug, PartialEq)]
66
#[repr(C)]
@@ -119,7 +119,38 @@ impl Quat {
119119
pub fn slerp(self, b: Self, t: f32) -> Self {
120120
debug_assert!(self.is_normalized(), "Quaternion `self` is not normalized");
121121
debug_assert!(b.is_normalized(), "Quaternion `b` is not normalized");
122-
Self::gd(self.glam().lerp(b.glam(), t))
122+
123+
// Copied from Godot's Quat::slerp as glam::lerp version diverges too much
124+
125+
// calc cosine
126+
let cosom = self.dot(b);
127+
128+
// adjust signs (if necessary)
129+
let (cosom, b) = if cosom < 0.0 {
130+
(-cosom, -b)
131+
} else {
132+
(cosom, b)
133+
};
134+
135+
// calculate coefficients
136+
let scale = if (1.0 - cosom) > CMP_EPSILON as f32 {
137+
// standard case (slerp)
138+
let omega = cosom.acos();
139+
let sinom = omega.sin();
140+
(((1.0 - t) * omega).sin() / sinom, (t * omega).sin() / sinom)
141+
} else {
142+
// "from" and "to" quaternions are very close
143+
// ... so we can do a linear interpolation
144+
(1.0 - t, t)
145+
};
146+
147+
// calculate final values
148+
Self::new(
149+
scale.0 * self.x + scale.1 * b.x,
150+
scale.0 * self.y + scale.1 * b.y,
151+
scale.0 * self.z + scale.1 * b.z,
152+
scale.0 * self.w + scale.1 * b.w,
153+
)
123154
}
124155

125156
/// Returns the result of the spherical linear interpolation between this quaternion and `t` by
@@ -128,6 +159,9 @@ impl Quat {
128159
pub fn slerpni(self, b: Self, t: f32) -> Self {
129160
debug_assert!(self.is_normalized(), "Quaternion `self` is not normalized");
130161
debug_assert!(b.is_normalized(), "Quaternion `b` is not normalized");
162+
163+
// Copied from Godot's Quat::slerpni as glam::slerp version diverges too much
164+
131165
let dot = self.dot(b);
132166
if dot.abs() > 0.9999 {
133167
self
@@ -172,6 +206,15 @@ impl Mul<Vector3> for Quat {
172206
}
173207
}
174208

209+
impl Neg for Quat {
210+
type Output = Quat;
211+
212+
#[inline]
213+
fn neg(self) -> Self {
214+
Self::gd(-self.glam())
215+
}
216+
}
217+
175218
#[cfg(test)]
176219
mod test {
177220
use super::*;

0 commit comments

Comments
 (0)