Skip to content

Commit ef28f9f

Browse files
committed
Add Basis
1 parent c043188 commit ef28f9f

File tree

8 files changed

+1048
-10
lines changed

8 files changed

+1048
-10
lines changed

godot-core/src/builtin/basis.rs

Lines changed: 840 additions & 0 deletions
Large diffs are not rendered by default.

godot-core/src/builtin/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
pub use crate::{array, dict, varray};
3737

3838
pub use array_inner::{Array, TypedArray};
39+
pub use basis::*;
3940
pub use color::*;
4041
pub use dictionary_inner::Dictionary;
4142
pub use math::*;
@@ -79,6 +80,7 @@ mod array_inner;
7980
#[path = "dictionary.rs"]
8081
mod dictionary_inner;
8182

83+
mod basis;
8284
mod color;
8385
mod glam_helpers;
8486
mod math;

godot-core/src/builtin/others.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ impl_builtin_stub!(Rect2, OpaqueRect2);
1717
impl_builtin_stub!(Rect2i, OpaqueRect2i);
1818
impl_builtin_stub!(Plane, OpaquePlane);
1919
impl_builtin_stub!(Aabb, OpaqueAabb);
20-
impl_builtin_stub!(Basis, OpaqueBasis);
2120
impl_builtin_stub!(Transform2D, OpaqueTransform2D);
2221
impl_builtin_stub!(Transform3D, OpaqueTransform3D);
2322
impl_builtin_stub!(Projection, OpaqueProjection);

godot-core/src/builtin/quaternion.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ use sys::{ffi_methods, GodotFfi};
1111
use crate::builtin::glam_helpers::{GlamConv, GlamType};
1212
use crate::builtin::{inner, math::*, vector3::*};
1313

14-
#[derive(Copy, Clone, Debug, PartialEq)]
14+
use super::{Basis, EulerOrder};
15+
16+
#[derive(Copy, Clone, PartialEq, Debug)]
1517
#[repr(C)]
1618
pub struct Quaternion {
1719
pub x: f32,
@@ -96,12 +98,8 @@ impl Quaternion {
9698
}
9799
}
98100

99-
// TODO: Figure out how godot actually treats "order", then make a match/if chain
100-
pub fn get_euler(self, order: Option<i32>) -> Vector3 {
101-
let _o = order.unwrap_or(2);
102-
let vt = self.glam(|quat| quat.to_euler(glam::EulerRot::XYZ));
103-
104-
Vector3::new(vt.0, vt.1, vt.2)
101+
pub fn to_euler(self, order: EulerOrder) -> Vector3 {
102+
Basis::from_quat(self).to_euler(order)
105103
}
106104

107105
pub fn inverse(self) -> Self {

godot-core/src/builtin/variant/impls.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ mod impls {
143143
use super::*;
144144

145145
impl_variant_traits!(bool, bool_to_variant, bool_from_variant, Bool);
146+
impl_variant_traits!(Basis, basis_to_variant, basis_from_variant, Basis);
146147
impl_variant_traits!(Vector2, vector2_to_variant, vector2_from_variant, Vector2);
147148
impl_variant_traits!(Vector3, vector3_to_variant, vector3_from_variant, Vector3);
148149
impl_variant_traits!(Vector4, vector4_to_variant, vector4_from_variant, Vector4);
@@ -158,7 +159,6 @@ mod impls {
158159
impl_variant_metadata!(Plane, /* plane_to_variant, plane_from_variant, */ Plane);
159160
impl_variant_metadata!(Quaternion, /* quaternion_to_variant, quaternion_from_variant, */ Quaternion);
160161
impl_variant_metadata!(Aabb, /* aabb_to_variant, aabb_from_variant, */ Aabb);
161-
impl_variant_metadata!(Basis, /* basis_to_variant, basis_from_variant, */ Basis);
162162
impl_variant_metadata!(Transform2D, /* transform_2d_to_variant, transform_2d_from_variant, */ Transform2D);
163163
impl_variant_metadata!(Transform3D, /* transform_3d_to_variant, transform_3d_from_variant, */ Transform3D);
164164
impl_variant_metadata!(Projection, /* projection_to_variant, projection_from_variant, */ Projection);

itest/rust/src/basis_test.rs

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
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+
7+
use crate::itest;
8+
use godot::prelude::{inner::InnerBasis, *};
9+
use godot::private::class_macros::assert_eq_approx;
10+
11+
const TEST_BASIS: Basis = Basis::from_rows(
12+
Vector3::new(0.942155, -0.270682, 0.197677),
13+
Vector3::new(0.294044, 0.950564, -0.099833),
14+
Vector3::new(-0.160881, 0.152184, 0.97517),
15+
);
16+
17+
pub(crate) fn run() -> bool {
18+
let mut ok = true;
19+
ok &= basis_multiply_same();
20+
ok &= basis_euler_angles_same();
21+
22+
ok
23+
}
24+
25+
#[itest]
26+
fn basis_multiply_same() {
27+
let rust_res = TEST_BASIS * Basis::IDENTITY;
28+
let godot_res = TEST_BASIS
29+
.to_variant()
30+
.evaluate(&Basis::IDENTITY.to_variant(), VariantOperator::Multiply)
31+
.unwrap()
32+
.to::<Basis>();
33+
assert_eq_approx!(rust_res, godot_res, |a, b| Basis::is_equal_approx(&a, &b));
34+
35+
let rhs = Basis::from_axis_angle(Vector3::new(1.0, 2.0, 3.0).normalized(), 0.5);
36+
let rust_res = TEST_BASIS * rhs;
37+
let godot_res = TEST_BASIS
38+
.to_variant()
39+
.evaluate(&rhs.to_variant(), VariantOperator::Multiply)
40+
.unwrap()
41+
.to::<Basis>();
42+
assert_eq_approx!(rust_res, godot_res, |a, b| Basis::is_equal_approx(&a, &b));
43+
}
44+
45+
#[itest]
46+
fn basis_euler_angles_same() {
47+
let euler_order_to_test: Vec<EulerOrder> = vec![
48+
EulerOrder::XYZ,
49+
EulerOrder::XZY,
50+
EulerOrder::YZX,
51+
EulerOrder::YXZ,
52+
EulerOrder::ZXY,
53+
EulerOrder::ZYX,
54+
];
55+
56+
let vectors_to_test: Vec<Vector3> = vec![
57+
Vector3::new(0.0, 0.0, 0.0),
58+
Vector3::new(0.5, 0.5, 0.5),
59+
Vector3::new(-0.5, -0.5, -0.5),
60+
Vector3::new(40.0, 40.0, 40.0),
61+
Vector3::new(-40.0, -40.0, -40.0),
62+
Vector3::new(0.0, 0.0, -90.0),
63+
Vector3::new(0.0, -90.0, 0.0),
64+
Vector3::new(-90.0, 0.0, 0.0),
65+
Vector3::new(0.0, 0.0, 90.0),
66+
Vector3::new(0.0, 90.0, 0.0),
67+
Vector3::new(90.0, 0.0, 0.0),
68+
Vector3::new(0.0, 0.0, -30.0),
69+
Vector3::new(0.0, -30.0, 0.0),
70+
Vector3::new(-30.0, 0.0, 0.0),
71+
Vector3::new(0.0, 0.0, 30.0),
72+
Vector3::new(0.0, 30.0, 0.0),
73+
Vector3::new(30.0, 0.0, 0.0),
74+
Vector3::new(0.5, 50.0, 20.0),
75+
Vector3::new(-0.5, -50.0, -20.0),
76+
Vector3::new(0.5, 0.0, 90.0),
77+
Vector3::new(0.5, 0.0, -90.0),
78+
Vector3::new(360.0, 360.0, 360.0),
79+
Vector3::new(-360.0, -360.0, -360.0),
80+
Vector3::new(-90.0, 60.0, -90.0),
81+
Vector3::new(90.0, 60.0, -90.0),
82+
Vector3::new(90.0, -60.0, -90.0),
83+
Vector3::new(-90.0, -60.0, -90.0),
84+
Vector3::new(-90.0, 60.0, 90.0),
85+
Vector3::new(90.0, 60.0, 90.0),
86+
Vector3::new(90.0, -60.0, 90.0),
87+
Vector3::new(-90.0, -60.0, 90.0),
88+
Vector3::new(60.0, 90.0, -40.0),
89+
Vector3::new(60.0, -90.0, -40.0),
90+
Vector3::new(-60.0, -90.0, -40.0),
91+
Vector3::new(-60.0, 90.0, 40.0),
92+
Vector3::new(60.0, 90.0, 40.0),
93+
Vector3::new(60.0, -90.0, 40.0),
94+
Vector3::new(-60.0, -90.0, 40.0),
95+
Vector3::new(-90.0, 90.0, -90.0),
96+
Vector3::new(90.0, 90.0, -90.0),
97+
Vector3::new(90.0, -90.0, -90.0),
98+
Vector3::new(-90.0, -90.0, -90.0),
99+
Vector3::new(-90.0, 90.0, 90.0),
100+
Vector3::new(90.0, 90.0, 90.0),
101+
Vector3::new(90.0, -90.0, 90.0),
102+
Vector3::new(20.0, 150.0, 30.0),
103+
Vector3::new(20.0, -150.0, 30.0),
104+
Vector3::new(-120.0, -150.0, 30.0),
105+
Vector3::new(-120.0, -150.0, -130.0),
106+
Vector3::new(120.0, -150.0, -130.0),
107+
Vector3::new(120.0, 150.0, -130.0),
108+
Vector3::new(120.0, 150.0, 130.0),
109+
];
110+
111+
for order in euler_order_to_test.into_iter() {
112+
for vector in vectors_to_test.iter() {
113+
let vector = deg_to_rad(*vector);
114+
let rust_basis = Basis::from_euler(order, vector);
115+
let godot_basis = InnerBasis::from_euler(vector, order as i64);
116+
assert!(
117+
(rust_basis).is_equal_approx(&godot_basis),
118+
"got = {rust_basis}, expected = {godot_basis}"
119+
);
120+
}
121+
}
122+
}
123+
124+
#[itest]
125+
fn basis_equiv() {
126+
let inner = InnerBasis::from_outer(&TEST_BASIS);
127+
let outer = TEST_BASIS;
128+
let vec = Vector3::new(1.0, 2.0, 3.0);
129+
130+
#[rustfmt::skip]
131+
let mappings_basis = [
132+
("inverse", inner.inverse(), outer.inverse() ),
133+
("transposed", inner.transposed(), outer.transposed() ),
134+
("orthonormalized", inner.orthonormalized(), outer.orthonormalized() ),
135+
("rotated", inner.rotated(vec.normalized(), 0.1), outer.rotated(vec.normalized(), 0.1)),
136+
("scaled", inner.scaled(vec), outer.scaled(vec) ),
137+
("slerp", inner.slerp(Basis::IDENTITY, 0.5), outer.slerp(Basis::IDENTITY, 0.5) ),
138+
];
139+
for (name, inner, outer) in mappings_basis {
140+
assert_eq_approx!(&inner, &outer, Basis::is_equal_approx, "function: {name}\n");
141+
}
142+
143+
#[rustfmt::skip]
144+
let mappings_float = [
145+
("determinant", inner.determinant(), outer.determinant()),
146+
("tdotx", inner.tdotx(vec), outer.tdotx(vec) ),
147+
("tdoty", inner.tdoty(vec), outer.tdoty(vec) ),
148+
("tdotz", inner.tdotz(vec), outer.tdotz(vec) ),
149+
];
150+
for (name, inner, outer) in mappings_float {
151+
assert_eq_approx!(
152+
inner,
153+
outer,
154+
|a, b| is_equal_approx(a as f32, b),
155+
"function: {name}\n"
156+
);
157+
}
158+
159+
assert_eq_approx!(
160+
inner.get_scale(),
161+
outer.scale(),
162+
Vector3::is_equal_approx,
163+
"function: get_scale\n"
164+
);
165+
assert_eq_approx!(
166+
inner.get_euler(EulerOrder::XYZ as i64),
167+
outer.to_euler(EulerOrder::XYZ),
168+
Vector3::is_equal_approx,
169+
"function: get_euler\n"
170+
);
171+
assert_eq_approx!(
172+
inner.get_rotation_quaternion(),
173+
outer.to_quat(),
174+
Quaternion::is_equal_approx,
175+
"function: get_rotation_quaternion\n"
176+
)
177+
}
178+
179+
fn deg_to_rad(rotation: Vector3) -> Vector3 {
180+
Vector3::new(
181+
rotation.x.to_radians(),
182+
rotation.y.to_radians(),
183+
rotation.z.to_radians(),
184+
)
185+
}

itest/rust/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use std::panic::UnwindSafe;
1111

1212
mod array_test;
1313
mod base_test;
14+
mod basis_test;
1415
mod builtin_test;
1516
mod codegen_test;
1617
mod color_test;
@@ -32,6 +33,7 @@ fn run_tests() -> bool {
3233
let mut ok = true;
3334
ok &= array_test::run();
3435
ok &= base_test::run();
36+
ok &= basis_test::run();
3537
ok &= builtin_test::run();
3638
ok &= codegen_test::run();
3739
ok &= color_test::run();

itest/rust/src/variant_test.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,17 @@ use godot::builtin::{
1010
};
1111
use godot::engine::Node3D;
1212
use godot::obj::InstanceId;
13-
use godot::prelude::{Array, Dictionary, VariantConversionError};
13+
use godot::prelude::{Array, Basis, Dictionary, VariantConversionError};
1414
use godot::sys::{GodotFfi, VariantOperator, VariantType};
1515
use std::cmp::Ordering;
1616
use std::fmt::{Debug, Display};
1717

18+
const TEST_BASIS: Basis = Basis::from_rows(
19+
Vector3::new(1.0, 2.0, 3.0),
20+
Vector3::new(4.0, 5.0, 6.0),
21+
Vector3::new(7.0, 8.0, 9.0),
22+
);
23+
1824
pub fn run() -> bool {
1925
let mut ok = true;
2026
ok &= variant_nil();
@@ -73,6 +79,9 @@ fn variant_conversions() {
7379
let str_val = "abcdefghijklmnop";
7480
let back = String::from_variant(&str_val.to_variant());
7581
assert_eq!(str_val, back.as_str());
82+
83+
// basis
84+
roundtrip(TEST_BASIS);
7685
}
7786

7887
#[itest]
@@ -93,6 +102,9 @@ fn variant_get_type() {
93102

94103
let variant = gstr("hello").to_variant();
95104
assert_eq!(variant.get_type(), VariantType::String);
105+
106+
let variant = TEST_BASIS.to_variant();
107+
assert_eq!(variant.get_type(), VariantType::Basis)
96108
}
97109

98110
#[itest]

0 commit comments

Comments
 (0)