Skip to content

Commit 7bffee3

Browse files
RealAstolfoBromeon
RealAstolfo
authored andcommitted
Quaternion implementation, stub removal
1 parent cc82849 commit 7bffee3

File tree

3 files changed

+331
-1
lines changed

3 files changed

+331
-1
lines changed

godot-core/src/builtin/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ pub use math::*;
4242
pub use node_path::*;
4343
pub use others::*;
4444
pub use packed_array::*;
45+
pub use quaternion::*;
4546
pub use string::*;
4647
pub use string_name::*;
4748
pub use variant::*;
@@ -83,6 +84,7 @@ mod math;
8384
mod node_path;
8485
mod others;
8586
mod packed_array;
87+
mod quaternion;
8688
mod string;
8789
mod string_name;
8890
mod variant;

godot-core/src/builtin/others.rs

-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ use sys::{ffi_methods, GodotFfi};
1616
impl_builtin_stub!(Rect2, OpaqueRect2);
1717
impl_builtin_stub!(Rect2i, OpaqueRect2i);
1818
impl_builtin_stub!(Plane, OpaquePlane);
19-
impl_builtin_stub!(Quaternion, OpaqueQuaternion);
2019
impl_builtin_stub!(Aabb, OpaqueAabb);
2120
impl_builtin_stub!(Basis, OpaqueBasis);
2221
impl_builtin_stub!(Transform2D, OpaqueTransform2D);

godot-core/src/builtin/quaternion.rs

+329
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
5+
*/
6+
use std::ops::*;
7+
8+
use godot_ffi as sys;
9+
use sys::{ffi_methods, GodotFfi};
10+
11+
use crate::builtin::{math::*, vector3::*};
12+
13+
type Inner = glam::f32::Quat;
14+
15+
#[derive(Default, Copy, Clone, Debug, PartialEq)]
16+
#[repr(C)]
17+
pub struct Quaternion {
18+
inner: Inner,
19+
}
20+
21+
impl Quaternion {
22+
pub fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
23+
Self {
24+
inner: Inner::from_xyzw(x, y, z, w),
25+
}
26+
}
27+
28+
pub fn from_angle_axis(axis: Vector3, angle: f32) -> Self {
29+
let d = axis.length();
30+
if d == 0.0 {
31+
Self {
32+
inner: Inner::from_xyzw(0.0, 0.0, 0.0, 0.0),
33+
}
34+
} else {
35+
let sin_angle = (angle * 0.5).sin();
36+
let cos_angle = (angle * 0.5).cos();
37+
let s = sin_angle / d;
38+
let x = axis.x() * s;
39+
let y = axis.y() * s;
40+
let z = axis.z() * s;
41+
let w = cos_angle;
42+
Self {
43+
inner: Inner::from_xyzw(x, y, z, w),
44+
}
45+
}
46+
}
47+
48+
pub fn angle_to(self, to: Self) -> f32 {
49+
self.inner.angle_between(to.inner)
50+
}
51+
52+
pub fn dot(self, with: Self) -> f32 {
53+
self.inner.dot(with.inner)
54+
}
55+
56+
pub fn to_exp(self) -> Self {
57+
let mut v = Vector3::new(self.inner.x, self.inner.y, self.inner.z);
58+
let theta = v.length();
59+
v = v.normalized();
60+
if theta < CMP_EPSILON || !v.is_normalized() {
61+
return Quaternion::new(0.0, 0.0, 0.0, 1.0);
62+
}
63+
Quaternion::from_angle_axis(v, theta)
64+
}
65+
66+
pub fn from_euler(self, euler: Vector3) -> Self {
67+
let half_a1 = euler.y() * 0.5;
68+
let half_a2 = euler.x() * 0.5;
69+
let half_a3 = euler.z() * 0.5;
70+
let cos_a1 = half_a1.cos();
71+
let sin_a1 = half_a1.sin();
72+
let cos_a2 = half_a2.cos();
73+
let sin_a2 = half_a2.sin();
74+
let cos_a3 = half_a3.cos();
75+
let sin_a3 = half_a3.sin();
76+
Quaternion::new(
77+
sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3,
78+
sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3,
79+
-sin_a1 * sin_a2 * cos_a3 + cos_a1 * cos_a2 * sin_a3,
80+
sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3,
81+
)
82+
}
83+
84+
pub fn get_angle(self) -> f32 {
85+
2.0 * self.inner.w.acos()
86+
}
87+
88+
pub fn get_axis(self) -> Vector3 {
89+
if self.inner.w.abs() > 1.0 - CMP_EPSILON {
90+
return Vector3::new(self.inner.x, self.inner.y, self.inner.z);
91+
}
92+
let r = 1.0 / (1.0 - self.inner.w * self.inner.w).sqrt();
93+
Vector3::new(self.inner.x * r, self.inner.y * r, self.inner.z * r)
94+
}
95+
96+
/// TODO: Figure out how godot actually treats "order", then make a match/if chain
97+
pub fn get_euler(self, order: Option<i32>) -> Vector3 {
98+
let _o = order.unwrap_or(2);
99+
let vt = self.inner.to_euler(glam::EulerRot::XYZ);
100+
Vector3::new(vt.0, vt.1, vt.2)
101+
}
102+
103+
pub fn inverse(self) -> Self {
104+
Quaternion::new(-self.inner.x, -self.inner.y, -self.inner.z, self.inner.w)
105+
}
106+
107+
pub fn is_equal_approx(self, to: Self) -> bool {
108+
is_equal_approx(self.inner.x, to.inner.x)
109+
&& is_equal_approx(self.inner.y, to.inner.y)
110+
&& is_equal_approx(self.inner.z, to.inner.z)
111+
&& is_equal_approx(self.inner.w, to.inner.w)
112+
}
113+
114+
pub fn is_finite(self) -> bool {
115+
self.inner.x.is_finite()
116+
&& self.inner.y.is_finite()
117+
&& self.inner.z.is_finite()
118+
&& self.inner.w.is_finite()
119+
}
120+
121+
pub fn is_normalized(self) -> bool {
122+
is_equal_approx(self.length_squared(), 1.0) /*,UNIT_EPSILON)*/
123+
}
124+
125+
pub fn length(self) -> f32 {
126+
self.length_squared().sqrt()
127+
}
128+
129+
pub fn length_squared(self) -> f32 {
130+
self.dot(self)
131+
}
132+
133+
pub fn log(self) -> Self {
134+
let v = self.get_axis() * self.get_angle();
135+
Quaternion::new(v.x(), v.y(), v.z(), 0.0)
136+
}
137+
138+
pub fn normalized(self) -> Self {
139+
self / self.length()
140+
}
141+
142+
pub fn slerp(self, to: Self, weight: f32) -> Self {
143+
let mut cosom = self.dot(to);
144+
let to1: Self;
145+
let omega: f32;
146+
let sinom: f32;
147+
let scale0: f32;
148+
let scale1: f32;
149+
if cosom < 0.0 {
150+
cosom = -cosom;
151+
to1 = -to;
152+
} else {
153+
to1 = to;
154+
}
155+
156+
if 1.0 - cosom > CMP_EPSILON {
157+
omega = cosom.acos();
158+
sinom = omega.sin();
159+
scale0 = ((1.0 - weight) * omega).sin() / sinom;
160+
scale1 = (weight * omega).sin() / sinom;
161+
} else {
162+
scale0 = 1.0 - weight;
163+
scale1 = weight;
164+
}
165+
Quaternion::new(
166+
scale0 * self.inner.x + scale1 * to1.inner.x,
167+
scale0 * self.inner.y + scale1 * to1.inner.y,
168+
scale0 * self.inner.z + scale1 * to1.inner.z,
169+
scale0 * self.inner.w + scale1 * to1.inner.w,
170+
)
171+
}
172+
173+
pub fn slerpni(self, to: Self, weight: f32) -> Self {
174+
let dot = self.dot(to);
175+
if dot.abs() > 0.9999 {
176+
return self;
177+
}
178+
let theta = dot.acos();
179+
let sin_t = 1.0 / theta.sin();
180+
let new_factor = (weight * theta).sin() * sin_t;
181+
let inv_factor = ((1.0 - weight) * theta).sin() * sin_t;
182+
Quaternion::new(
183+
inv_factor * self.inner.x + new_factor * to.inner.x,
184+
inv_factor * self.inner.y + new_factor * to.inner.y,
185+
inv_factor * self.inner.z + new_factor * to.inner.z,
186+
inv_factor * self.inner.w + new_factor * to.inner.w,
187+
)
188+
}
189+
190+
// pub fn spherical_cubic_interpolate(self, b: Self, pre_a: Self, post_b: Self, weight: f32) -> Self {}
191+
// TODO: Implement godot's function in rust
192+
/*
193+
pub fn spherical_cubic_interpolate_in_time(
194+
self,
195+
b: Self,
196+
pre_a: Self,
197+
post_b: Self,
198+
weight: f32,
199+
b_t: f32,
200+
pre_a_t: f32,
201+
post_b_t: f32,
202+
) -> Self {
203+
}
204+
*/
205+
}
206+
207+
impl Add for Quaternion {
208+
type Output = Self;
209+
210+
fn add(self, other: Self) -> Self {
211+
Self::new(
212+
self.inner.x + other.inner.x,
213+
self.inner.y + other.inner.y,
214+
self.inner.z + other.inner.z,
215+
self.inner.w + other.inner.w,
216+
)
217+
}
218+
}
219+
220+
impl AddAssign for Quaternion {
221+
fn add_assign(&mut self, other: Self) {
222+
*self = *self + other
223+
}
224+
}
225+
226+
impl Sub for Quaternion {
227+
type Output = Self;
228+
229+
fn sub(self, other: Self) -> Self {
230+
Self::new(
231+
self.inner.x - other.inner.x,
232+
self.inner.y - other.inner.y,
233+
self.inner.z - other.inner.z,
234+
self.inner.w - other.inner.w,
235+
)
236+
}
237+
}
238+
239+
impl SubAssign for Quaternion {
240+
fn sub_assign(&mut self, other: Self) {
241+
*self = *self - other
242+
}
243+
}
244+
245+
impl Mul<Quaternion> for Quaternion {
246+
type Output = Self;
247+
248+
fn mul(self, other: Quaternion) -> Self {
249+
let x = self.inner.w * other.inner.x
250+
+ self.inner.x * other.inner.w
251+
+ self.inner.y * other.inner.z
252+
- self.inner.z * other.inner.y;
253+
let y = self.inner.w * other.inner.y
254+
+ self.inner.y * other.inner.w
255+
+ self.inner.z * other.inner.x
256+
- self.inner.x * other.inner.z;
257+
let z = self.inner.w * other.inner.z
258+
+ self.inner.z * other.inner.w
259+
+ self.inner.x * other.inner.y
260+
- self.inner.y * other.inner.x;
261+
let w = self.inner.w * other.inner.w
262+
- self.inner.x * other.inner.x
263+
- self.inner.y * other.inner.y
264+
- self.inner.z * other.inner.z;
265+
Self::new(x, y, z, w)
266+
}
267+
}
268+
269+
impl MulAssign<Quaternion> for Quaternion {
270+
fn mul_assign(&mut self, other: Quaternion) {
271+
*self = *self * other
272+
}
273+
}
274+
275+
impl Mul<f32> for Quaternion {
276+
type Output = Self;
277+
278+
fn mul(self, other: f32) -> Self {
279+
Quaternion::new(
280+
self.inner.x * other,
281+
self.inner.y * other,
282+
self.inner.z * other,
283+
self.inner.w * other,
284+
)
285+
}
286+
}
287+
288+
impl MulAssign<f32> for Quaternion {
289+
fn mul_assign(&mut self, other: f32) {
290+
*self = *self * other
291+
}
292+
}
293+
294+
impl Div<f32> for Quaternion {
295+
type Output = Self;
296+
297+
fn div(self, other: f32) -> Self {
298+
Self::new(
299+
self.inner.x / other,
300+
self.inner.y / other,
301+
self.inner.z / other,
302+
self.inner.w / other,
303+
)
304+
}
305+
}
306+
307+
impl DivAssign<f32> for Quaternion {
308+
fn div_assign(&mut self, other: f32) {
309+
*self = *self / other
310+
}
311+
}
312+
313+
impl Neg for Quaternion {
314+
type Output = Self;
315+
316+
fn neg(self) -> Self {
317+
Self::new(-self.inner.x, -self.inner.y, -self.inner.z, -self.inner.w)
318+
}
319+
}
320+
321+
impl GodotFfi for Quaternion {
322+
ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
323+
}
324+
325+
impl std::fmt::Display for Quaternion {
326+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
327+
self.inner.fmt(f)
328+
}
329+
}

0 commit comments

Comments
 (0)