Skip to content

Commit f777cfa

Browse files
committed
Refactor 3D vectors with new macros
1 parent ccf7745 commit f777cfa

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
@@ -200,7 +200,7 @@ impl Aabb {
200200
/// Returns the index of the longest axis of the AABB (according to Vector3's AXIS_* constants).
201201
#[inline]
202202
pub fn longest_axis_index(&self) -> Vector3Axis {
203-
self.size.max_axis_index()
203+
self.size.max_axis().unwrap_or(Vector3Axis::X)
204204
}
205205

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

228228
/// 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,12 +5,13 @@
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::vectors::Vector3Axis;
13-
use crate::builtin::{real, Basis, RVec3, Vector3i};
14+
use crate::builtin::{inner, real, Basis, RVec3, Vector2, Vector3i};
1415

1516
use std::fmt;
1617

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

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

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

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

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

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

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

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

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

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

7673
/// Constructs a new `Vector3` from a [`Vector3i`].
74+
#[inline]
7775
pub const fn from_vector3i(v: Vector3i) -> Self {
7876
Self {
7977
x: v.x as real,
@@ -82,126 +80,89 @@ impl Vector3 {
8280
}
8381
}
8482

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

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

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

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

139-
pub fn is_finite(self) -> bool {
140-
self.to_glam().is_finite()
110+
n.normalized()
141111
}
142112

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

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

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

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

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

193-
pub fn project(self, b: Self) -> Self {
194-
Self::from_glam(self.to_glam().project_onto(b.to_glam()))
140+
o
195141
}
196142

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

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

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

267212
/// Formats the vector like Godot: `(x, y, z)`.
@@ -271,12 +216,6 @@ impl fmt::Display for Vector3 {
271216
}
272217
}
273218

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

0 commit comments

Comments
 (0)