Skip to content

Commit 2c04101

Browse files
committed
Refactor 3D vectors with new macros
1 parent f428763 commit 2c04101

File tree

4 files changed

+106
-248
lines changed

4 files changed

+106
-248
lines changed

godot-core/src/builtin/aabb.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ impl Aabb {
202202
/// Returns the index of the longest axis of the AABB (according to Vector3's AXIS_* constants).
203203
#[inline]
204204
pub fn longest_axis_index(&self) -> Vector3Axis {
205-
self.size.max_axis_index()
205+
self.size.max_axis().unwrap_or(Vector3Axis::X)
206206
}
207207

208208
/// Returns the scalar length of the longest axis of the AABB.
@@ -224,7 +224,7 @@ impl Aabb {
224224
/// Returns the index of the shortest axis of the AABB (according to Vector3::AXIS* enum).
225225
#[inline]
226226
pub fn shortest_axis_index(&self) -> Vector3Axis {
227-
self.size.min_axis_index()
227+
self.size.min_axis().unwrap_or(Vector3Axis::Z)
228228
}
229229

230230
/// Returns the scalar length of the shortest axis of the AABB.

godot-core/src/builtin/vectors/vector3.rs

Lines changed: 87 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
66
*/
77

8+
use core::cmp::Ordering;
89
use godot_ffi as sys;
910
use sys::{ffi_methods, GodotFfi};
1011

1112
use crate::builtin::math::{FloatExt, GlamConv, GlamType};
1213
use crate::builtin::meta::impl_godot_as_self;
1314
use crate::builtin::vectors::Vector3Axis;
14-
use crate::builtin::{real, Basis, RVec3, Vector3i};
15+
use crate::builtin::{inner, real, Basis, RVec3, Vector2, Vector3i};
1516

1617
use std::fmt;
1718

@@ -39,42 +40,39 @@ pub struct Vector3 {
3940
pub z: real,
4041
}
4142

42-
impl Vector3 {
43-
/// Vector with all components set to `0.0`.
44-
pub const ZERO: Self = Self::splat(0.0);
43+
impl_vector_operators!(Vector3, real, (x, y, z));
4544

46-
/// Vector with all components set to `1.0`.
47-
pub const ONE: Self = Self::splat(1.0);
45+
impl_vector_consts!(Vector3, real);
46+
impl_float_vector_consts!(Vector3);
47+
impl_vector3x_consts!(Vector3, real);
4848

49-
/// Unit vector in -X direction. Can be interpreted as left in an untransformed 3D world.
50-
pub const LEFT: Self = Self::new(-1.0, 0.0, 0.0);
49+
impl_vector_fns!(Vector3, RVec3, real, (x, y, z));
50+
impl_float_vector_fns!(Vector3, (x, y, z));
51+
impl_vector3x_fns!(Vector3, real);
52+
impl_vector2_vector3_fns!(Vector3, (x, y, z));
53+
impl_vector3_vector4_fns!(Vector3, (x, y, z));
5154

52-
/// Unit vector in +X direction. Can be interpreted as right in an untransformed 3D world.
53-
pub const RIGHT: Self = Self::new(1.0, 0.0, 0.0);
55+
impl Vector3 {
56+
/// Unit vector pointing towards the left side of imported 3D assets.
57+
pub const MODEL_LEFT: Self = Self::new(1.0, 0.0, 0.0);
5458

55-
/// Unit vector in +Y direction. Typically interpreted as up in a 3D world.
56-
pub const UP: Self = Self::new(0.0, 1.0, 0.0);
59+
/// Unit vector pointing towards the right side of imported 3D assets.
60+
pub const MODEL_RIGHT: Self = Self::new(-1.0, 0.0, 0.0);
5761

58-
/// Unit vector in -Y direction. Typically interpreted as down in a 3D world.
59-
pub const DOWN: Self = Self::new(0.0, -1.0, 0.0);
62+
/// Unit vector pointing towards the top side (up) of imported 3D assets.
63+
pub const MODEL_UP: Self = Self::new(0.0, 1.0, 0.0);
6064

61-
/// Unit vector in -Z direction. Can be interpreted as "into the screen" in an untransformed 3D world.
62-
pub const FORWARD: Self = Self::new(0.0, 0.0, -1.0);
65+
/// Unit vector pointing towards the bottom side (down) of imported 3D assets.
66+
pub const MODEL_BOTTOM: Self = Self::new(0.0, -1.0, 0.0);
6367

64-
/// Unit vector in +Z direction. Can be interpreted as "out of the screen" in an untransformed 3D world.
65-
pub const BACK: Self = Self::new(0.0, 0.0, 1.0);
68+
/// Unit vector pointing towards the front side (facing forward) of imported 3D assets.
69+
pub const MODEL_FRONT: Self = Self::new(0.0, 0.0, 1.0);
6670

67-
/// Returns a `Vector3` with the given components.
68-
pub const fn new(x: real, y: real, z: real) -> Self {
69-
Self { x, y, z }
70-
}
71-
72-
/// Returns a new `Vector3` with all components set to `v`.
73-
pub const fn splat(v: real) -> Self {
74-
Self::new(v, v, v)
75-
}
71+
/// Unit vector pointing towards the rear side (back) of imported 3D assets.
72+
pub const MODEL_REAR: Self = Self::new(0.0, 0.0, -1.0);
7673

7774
/// Constructs a new `Vector3` from a [`Vector3i`].
75+
#[inline]
7876
pub const fn from_vector3i(v: Vector3i) -> Self {
7977
Self {
8078
x: v.x as real,
@@ -83,126 +81,89 @@ impl Vector3 {
8381
}
8482
}
8583

86-
/// Converts the corresponding `glam` type to `Self`.
87-
fn from_glam(v: RVec3) -> Self {
88-
Self::new(v.x, v.y, v.z)
89-
}
90-
91-
/// Converts `self` to the corresponding `glam` type.
92-
fn to_glam(self) -> RVec3 {
93-
RVec3::new(self.x, self.y, self.z)
94-
}
95-
96-
pub fn angle_to(self, to: Self) -> real {
97-
self.to_glam().angle_between(to.to_glam())
98-
}
99-
100-
pub fn bounce(self, normal: Self) -> Self {
101-
-self.reflect(normal)
102-
}
103-
104-
pub fn ceil(self) -> Self {
105-
Self::from_glam(self.to_glam().ceil())
106-
}
107-
108-
pub fn clamp(self, min: Self, max: Self) -> Self {
109-
Self::from_glam(self.to_glam().clamp(min.to_glam(), max.to_glam()))
84+
#[doc(hidden)]
85+
#[inline]
86+
pub fn as_inner(&self) -> inner::InnerVector3 {
87+
inner::InnerVector3::from_outer(self)
11088
}
11189

90+
/// Returns the cross product of this vector and `with`.
91+
///
92+
/// This returns a vector perpendicular to both this and `with`, which would be the normal vector of the plane
93+
/// defined by the two vectors. As there are two such vectors, in opposite directions,
94+
/// this method returns the vector defined by a right-handed coordinate system.
95+
/// If the two vectors are parallel this returns an empty vector, making it useful for testing if two vectors are parallel.
96+
#[inline]
11297
pub fn cross(self, with: Self) -> Self {
11398
Self::from_glam(self.to_glam().cross(with.to_glam()))
11499
}
115100

116-
pub fn direction_to(self, to: Self) -> Self {
117-
(to - self).normalized()
118-
}
119-
120-
pub fn distance_squared_to(self, to: Self) -> real {
121-
(to - self).length_squared()
122-
}
123-
124-
pub fn distance_to(self, to: Self) -> real {
125-
(to - self).length()
126-
}
127-
128-
pub fn dot(self, with: Self) -> real {
129-
self.to_glam().dot(with.to_glam())
130-
}
131-
132-
pub fn floor(self) -> Self {
133-
Self::from_glam(self.to_glam().floor())
134-
}
101+
/// Returns the Vector3 from an octahedral-compressed form created using [`Vector3::octahedron_encode`] (stored as a [`Vector2`]).
102+
#[inline]
103+
pub fn octahedron_decode(uv: Vector2) -> Self {
104+
let f = Vector2::new(uv.x * 2.0 - 1.0, uv.y * 2.0 - 1.0);
105+
let mut n = Vector3::new(f.x, f.y, 1.0 - f.x.abs() - f.y.abs());
135106

136-
pub fn inverse(self) -> Self {
137-
Self::new(1.0 / self.x, 1.0 / self.y, 1.0 / self.z)
138-
}
107+
let t = (-n.z).clamp(0.0, 1.0);
108+
n.x += if n.x >= 0.0 { -t } else { t };
109+
n.y += if n.y >= 0.0 { -t } else { t };
139110

140-
pub fn is_finite(self) -> bool {
141-
self.to_glam().is_finite()
111+
n.normalized()
142112
}
143113

144-
pub fn is_normalized(self) -> bool {
145-
self.to_glam().is_normalized()
146-
}
114+
/// Returns the octahedral-encoded (oct32) form of this Vector3 as a [`Vector2`]. Since a [`Vector2`] occupies 1/3 less memory compared to Vector3,
115+
/// this form of compression can be used to pass greater amounts of [`Vector3::normalized`] Vector3s without increasing storage or memory requirements.
116+
/// See also [`Vector3::octahedron_decode`].
117+
///
118+
/// Note: Octahedral compression is lossy, although visual differences are rarely perceptible in real world scenarios.
119+
///
120+
/// # Panics
121+
/// If vector is not normalized.
122+
#[inline]
123+
pub fn octahedron_encode(self) -> Vector2 {
124+
assert!(self.is_normalized());
147125

148-
pub fn length_squared(self) -> real {
149-
self.to_glam().length_squared()
150-
}
126+
let mut n = self;
127+
n /= n.x.abs() + n.y.abs() + n.z.abs();
151128

152-
pub fn limit_length(self, length: Option<real>) -> Self {
153-
Self::from_glam(self.to_glam().clamp_length_max(length.unwrap_or(1.0)))
154-
}
155-
156-
pub fn max_axis_index(self) -> Vector3Axis {
157-
if self.x < self.y {
158-
if self.y < self.z {
159-
Vector3Axis::Z
160-
} else {
161-
Vector3Axis::Y
162-
}
163-
} else if self.x < self.z {
164-
Vector3Axis::Z
129+
let mut o = if n.z >= 0.0 {
130+
Vector2::new(n.x, n.y)
165131
} else {
166-
Vector3Axis::X
167-
}
168-
}
132+
let x = (1.0 - n.y.abs()) * (if n.x >= 0.0 { 1.0 } else { -1.0 });
133+
let y = (1.0 - n.x.abs()) * (if n.y >= 0.0 { 1.0 } else { -1.0 });
169134

170-
pub fn min_axis_index(self) -> Vector3Axis {
171-
if self.x < self.y {
172-
if self.x < self.z {
173-
Vector3Axis::X
174-
} else {
175-
Vector3Axis::Z
176-
}
177-
} else if self.y < self.z {
178-
Vector3Axis::Y
179-
} else {
180-
Vector3Axis::Z
181-
}
182-
}
135+
Vector2::new(x, y)
136+
};
183137

184-
pub fn move_toward(self, to: Self, delta: real) -> Self {
185-
let vd = to - self;
186-
let len = vd.length();
187-
if len <= delta || len < real::CMP_EPSILON {
188-
to
189-
} else {
190-
self + vd / len * delta
191-
}
192-
}
138+
o.x = o.x * 0.5 + 0.5;
139+
o.y = o.y * 0.5 + 0.5;
193140

194-
pub fn project(self, b: Self) -> Self {
195-
Self::from_glam(self.to_glam().project_onto(b.to_glam()))
141+
o
196142
}
197143

198-
pub fn reflect(self, normal: Self) -> Self {
199-
2.0 * normal * self.dot(normal) - self
144+
/// Returns the outer product with `with`.
145+
#[inline]
146+
pub fn outer(self, with: Self) -> Basis {
147+
let x = Vector3::new(self.x * with.x, self.x * with.y, self.x * with.z);
148+
let y = Vector3::new(self.y * with.x, self.y * with.y, self.y * with.z);
149+
let z = Vector3::new(self.z * with.x, self.z * with.y, self.z * with.z);
150+
151+
Basis::from_rows(x, y, z)
200152
}
201153

202-
pub fn round(self) -> Self {
203-
Self::from_glam(self.to_glam().round())
154+
/// Returns this vector rotated around `axis` by `angle` radians. `axis` must be normalized.
155+
///
156+
/// # Panics
157+
/// If `axis` is not normalized.
158+
#[inline]
159+
pub fn rotated(self, axis: Self, angle: real) -> Self {
160+
assert!(axis.is_normalized());
161+
Basis::from_axis_angle(axis, angle) * self
204162
}
205163

164+
/// Returns the signed angle to the given vector, in radians. The sign of the angle is positive in a counter-clockwise direction and
165+
/// negative in a clockwise direction when viewed from the side specified by the `axis`.
166+
#[inline]
206167
pub fn signed_angle_to(self, to: Self, axis: Self) -> real {
207168
let cross_to = self.cross(to);
208169
let unsigned_angle = cross_to.length().atan2(self.dot(to));
@@ -222,6 +183,7 @@ impl Vector3 {
222183
/// Length is also interpolated in the case that the input vectors have different lengths. If both
223184
/// input vectors have zero length or are collinear to each other, the method instead behaves like
224185
/// [`Vector3::lerp`].
186+
#[inline]
225187
pub fn slerp(self, to: Self, weight: real) -> Self {
226188
let start_length_sq: real = self.length_squared();
227189
let end_length_sq = to.length_squared();
@@ -246,23 +208,6 @@ impl Vector3 {
246208
let angle = self.angle_to(to);
247209
self.rotated(unit_axis, angle * weight) * (result_length / start_length)
248210
}
249-
250-
pub fn slide(self, normal: Self) -> Self {
251-
self - normal * self.dot(normal)
252-
}
253-
254-
/// Returns this vector rotated around `axis` by `angle` radians. `axis` must be normalized.
255-
///
256-
/// # Panics
257-
/// If `axis` is not normalized.
258-
pub fn rotated(self, axis: Self, angle: real) -> Self {
259-
assert!(axis.is_normalized());
260-
Basis::from_axis_angle(axis, angle) * self
261-
}
262-
263-
pub fn coords(&self) -> (real, real, real) {
264-
(self.x, self.y, self.z)
265-
}
266211
}
267212

268213
/// Formats the vector like Godot: `(x, y, z)`.
@@ -272,12 +217,6 @@ impl fmt::Display for Vector3 {
272217
}
273218
}
274219

275-
impl_common_vector_fns!(Vector3, real);
276-
impl_float_vector_glam_fns!(Vector3, real);
277-
impl_float_vector_component_fns!(Vector3, real, (x, y, z));
278-
impl_vector_operators!(Vector3, real, (x, y, z));
279-
impl_swizzle_trait_for_vector3x!(Vector3, real);
280-
281220
// SAFETY:
282221
// This type is represented as `Self` in Godot, so `*mut Self` is sound.
283222
unsafe impl GodotFfi for Vector3 {

0 commit comments

Comments
 (0)