gdnative_core/core_types/
vector3.rs

1use super::geom::Basis;
2use super::IsEqualApprox;
3use glam::Vec3A;
4use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
5
6/// 3D vector class.
7///
8/// See also [Vector3](https://docs.godotengine.org/en/stable/classes/class_vector3.html) in the Godot API doc.
9#[derive(Copy, Clone, Debug, PartialEq, Default)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11#[repr(C)]
12pub struct Vector3 {
13    pub x: f32,
14    pub y: f32,
15    pub z: f32,
16}
17
18#[allow(clippy::unnecessary_cast)] // False positives: casts necessary for cross-platform
19#[derive(Copy, Clone, Debug, PartialEq, Eq)]
20#[repr(u32)]
21pub enum Axis {
22    X = sys::godot_vector3_axis_GODOT_VECTOR3_AXIS_X as u32,
23    Y = sys::godot_vector3_axis_GODOT_VECTOR3_AXIS_Y as u32,
24    Z = sys::godot_vector3_axis_GODOT_VECTOR3_AXIS_Z as u32,
25}
26
27impl Axis {
28    /// Returns this axis as a vector of length 1, with only one component set.
29    #[inline]
30    pub fn to_unit_vector(self) -> Vector3 {
31        match self {
32            Axis::X => Vector3::RIGHT,
33            Axis::Y => Vector3::UP,
34            Axis::Z => Vector3::BACK,
35        }
36    }
37}
38
39/// Helper methods for `Vector3`.
40///
41/// See the official [`Godot documentation`](https://docs.godotengine.org/en/3.1/classes/class_vector3.html).
42impl Vector3 {
43    /// The zero vector.
44    pub const ZERO: Self = Self::new(0.0, 0.0, 0.0);
45
46    /// A vector with all components set to 1. Typically used for scaling.
47    pub const ONE: Self = Self::new(1.0, 1.0, 1.0);
48
49    /// A vector with all components set to +infinity.
50    pub const INF: Self = Self::new(f32::INFINITY, f32::INFINITY, f32::INFINITY);
51
52    /// Unit vector in -X direction.
53    pub const LEFT: Self = Self::new(-1.0, 0.0, 0.0);
54
55    /// Unit vector in +X direction.
56    pub const RIGHT: Self = Self::new(1.0, 0.0, 0.0);
57
58    /// Unit vector in +Y direction.
59    pub const UP: Self = Self::new(0.0, 1.0, 0.0);
60
61    /// Unit vector in -Y direction.
62    pub const DOWN: Self = Self::new(0.0, -1.0, 0.0);
63
64    /// Unit vector in -Z direction.
65    pub const FORWARD: Self = Self::new(0.0, 0.0, -1.0);
66
67    /// Unit vector in +Z direction.
68    pub const BACK: Self = Self::new(0.0, 0.0, 1.0);
69
70    /// Returns a Vector3 with the given components.
71    #[inline]
72    pub const fn new(x: f32, y: f32, z: f32) -> Self {
73        Self { x, y, z }
74    }
75
76    /// Returns a new vector with all components in absolute values (i.e. positive).
77    #[inline]
78    pub fn abs(self) -> Self {
79        Self::gd(self.glam().abs())
80    }
81
82    /// Returns the minimum angle to the given vector, in radians.
83    #[inline]
84    pub fn angle_to(self, to: Self) -> f32 {
85        self.glam().angle_between(to.glam())
86    }
87
88    /// Returns the vector "bounced off" from a plane defined by the given normal.
89    #[inline]
90    pub fn bounce(self, n: Self) -> Self {
91        -self.reflect(n)
92    }
93
94    /// Returns a new vector with all components rounded up (towards positive infinity).
95    #[inline]
96    pub fn ceil(self) -> Self {
97        Self::gd(self.glam().ceil())
98    }
99
100    /// Returns the cross product of this vector and b.
101    #[inline]
102    pub fn cross(self, b: Self) -> Self {
103        Self::gd(self.glam().cross(b.glam()))
104    }
105
106    /// Performs a cubic interpolation between vectors pre_a, a, b, post_b (a is current), by the
107    /// given amount t. t is on the range of 0.0 to 1.0, representing the amount of interpolation.
108    #[inline]
109    pub fn cubic_interpolate(self, b: Self, pre_a: Self, post_b: Self, t: f32) -> Self {
110        let mut p = (pre_a, self, b, post_b);
111
112        {
113            let ab = p.0.distance_to(p.1);
114            let bc = p.1.distance_to(p.2);
115            let cd = p.2.distance_to(p.3);
116
117            if ab > 0.0 {
118                p.0 = p.1 + (p.0 - p.1) * (bc / ab);
119            }
120            if cd > 0.0 {
121                p.3 = p.2 + (p.3 - p.2) * (bc / cd);
122            }
123        }
124
125        let t = (t, t * t, t * t * t);
126
127        0.5 * ((p.1 * 2.0)
128            + (-p.0 + p.2) * t.0
129            + (2.0 * p.0 - 5.0 * p.1 + 4.0 * p.2 - p.3) * t.1
130            + (-p.0 + 3.0 * p.1 - 3.0 * p.2 + p.3) * t.2)
131    }
132
133    /// Returns the normalized vector pointing from this vector to `other`.
134    #[inline]
135    pub fn direction_to(self, other: Vector3) -> Vector3 {
136        Self::gd((other.glam() - self.glam()).normalize())
137    }
138
139    /// Returns the squared distance to `other`.
140    ///
141    /// This method runs faster than distance_to, so prefer it if you need to compare vectors or
142    /// need the squared distance for some formula.
143    #[inline]
144    pub fn distance_squared_to(self, other: Vector3) -> f32 {
145        other.glam().distance_squared(self.glam())
146    }
147
148    /// Returns the distance to `other`.
149    #[inline]
150    pub fn distance_to(self, other: Vector3) -> f32 {
151        other.glam().distance(self.glam())
152    }
153
154    /// Returns the dot product of this vector and b. This can be used to compare the angle between
155    /// two vectors. For example, this can be used to determine whether an enemy is facing the player.
156    ///
157    /// The dot product will be 0 for a straight angle (90 degrees), greater than 0 for angles
158    /// narrower than 90 degrees and lower than 0 for angles wider than 90 degrees.
159    ///
160    /// When using unit (normalized) vectors, the result will always be between -1.0 (180 degree
161    /// angle) when the vectors are facing opposite directions, and 1.0 (0 degree angle) when the
162    /// vectors are aligned.
163    ///
164    /// Note: a.dot(b) is equivalent to b.dot(a).
165    #[inline]
166    pub fn dot(self, other: Self) -> f32 {
167        self.glam().dot(other.glam())
168    }
169
170    /// Returns a new vector with all components rounded down (towards negative infinity).
171    #[inline]
172    pub fn floor(self) -> Self {
173        Self::gd(self.glam().floor())
174    }
175
176    /// Returns the inverse of the vector. This is the same as
177    /// `Vector3::new(1.0 / self.x, 1.0 / self.y, 1.0 / self.z)`.
178    #[inline]
179    pub fn inverse(self) -> Self {
180        Self::new(1.0 / self.x, 1.0 / self.y, 1.0 / self.z)
181    }
182
183    /// Returns `true` if this vector and v are approximately equal, by running `relative_eq` on
184    /// each component.
185    #[inline]
186    pub fn is_equal_approx(self, v: Self) -> bool {
187        self.x.is_equal_approx(v.x) && self.y.is_equal_approx(v.y) && self.z.is_equal_approx(v.z)
188    }
189
190    /// Returns `true` if the vector is normalized, and `false` otherwise.
191    #[inline]
192    pub fn is_normalized(self) -> bool {
193        self.glam().is_normalized()
194    }
195
196    /// Returns the length (magnitude) of this vector.
197    #[inline]
198    pub fn length(self) -> f32 {
199        self.glam().length()
200    }
201
202    /// Returns the squared length (squared magnitude) of this vector.
203    ///
204    /// This method runs faster than length, so prefer it if you need to compare vectors or need
205    /// the squared distance for some formula.
206    #[inline]
207    pub fn length_squared(self) -> f32 {
208        self.glam().length_squared()
209    }
210
211    /// Returns the result of the linear interpolation between this vector and b by amount t. t is
212    /// on the range of 0.0 to 1.0, representing the amount of interpolation.
213    #[inline]
214    pub fn linear_interpolate(self, b: Self, t: f32) -> Self {
215        Self::gd(self.glam().lerp(b.glam(), t))
216    }
217
218    /// Returns the axis of the vector's largest value. See [`Axis`] enum.
219    ///
220    /// If multiple components are equal, this method returns in preferred order `Axis::X`, `Axis::Y`, `Axis::Z`.
221    #[inline]
222    #[allow(clippy::collapsible_else_if)]
223    pub fn max_axis(self) -> Axis {
224        if self.z > self.y {
225            if self.z > self.x {
226                Axis::Z
227            } else {
228                Axis::X
229            }
230        } else {
231            if self.y > self.x {
232                Axis::Y
233            } else {
234                Axis::X
235            }
236        }
237    }
238
239    /// Returns the axis of the vector's smallest value. See `Axis` enum.
240    ///
241    /// If multiple components are equal, this method returns in preferred order `Axis::X`, `Axis::Y`, `Axis::Z`.
242    #[inline]
243    #[allow(clippy::collapsible_else_if)]
244    pub fn min_axis(self) -> Axis {
245        if self.x < self.y {
246            if self.x < self.z {
247                Axis::X
248            } else {
249                Axis::Z
250            }
251        } else {
252            if self.y < self.z {
253                Axis::Y
254            } else {
255                Axis::Z
256            }
257        }
258    }
259
260    /// Moves this vector toward `to` by the fixed `delta` amount.
261    #[inline]
262    pub fn move_toward(self, to: Self, delta: f32) -> Self {
263        let vd = to - self;
264        let len = vd.length();
265        if len <= delta || approx::abs_diff_eq!(0.0, len) {
266            to
267        } else {
268            self.linear_interpolate(to, delta / len)
269        }
270    }
271
272    /// Returns the vector scaled to unit length. Equivalent to `v / v.length()`.
273    #[inline]
274    pub fn normalized(self) -> Self {
275        Self::gd(self.glam().normalize())
276    }
277
278    /// Returns the outer product with `b`.
279    #[inline]
280    pub fn outer(self, b: Self) -> Basis {
281        Basis::from_rows(b * self.x, b * self.y, b * self.z)
282    }
283
284    /// Returns a vector composed of the `rem_euclid` of this vector's components and `mod`.
285    #[inline]
286    pub fn posmod(self, rem: f32) -> Self {
287        self.posmodv(Self::new(rem, rem, rem))
288    }
289
290    /// Returns a vector composed of the `rem_euclid` of this vector's components and `remv`
291    /// components.
292    #[inline]
293    pub fn posmodv(self, remv: Self) -> Self {
294        Self::new(
295            self.x.rem_euclid(remv.x),
296            self.y.rem_euclid(remv.y),
297            self.z.rem_euclid(remv.z),
298        )
299    }
300
301    /// Returns this vector projected onto another vector `b`.
302    #[inline]
303    pub fn project(self, b: Self) -> Self {
304        b * (self.dot(b) / b.length_squared())
305    }
306
307    /// Returns this vector reflected from a plane defined by the given normal.
308    #[inline]
309    pub fn reflect(self, n: Self) -> Self {
310        n * self.dot(n) * 2.0 - self
311    }
312
313    /// Rotates this vector around a given axis by `phi` radians. The axis must be a normalized
314    /// vector.
315    #[inline]
316    pub fn rotated(self, axis: Self, phi: f32) -> Self {
317        Basis::from_axis_angle(axis, phi) * self
318    }
319
320    /// Returns this vector with all components rounded to the nearest integer, with halfway cases
321    /// rounded away from zero.
322    #[inline]
323    pub fn round(self) -> Self {
324        Self::gd(self.glam().round())
325    }
326
327    /// Returns a vector with each component set to one or negative one, depending on the signs of
328    /// this vector's components, or zero if the component is zero, by calling `signum` on each
329    /// component.
330    #[inline]
331    pub fn sign(self) -> Self {
332        Self::gd(self.glam().signum())
333    }
334
335    /// Returns the result of spherical linear interpolation between this vector and b, by amount t.
336    /// t is on the range of 0.0 to 1.0, representing the amount of interpolation.
337    ///
338    /// **Note**: Both vectors must be normalized.
339    #[inline]
340    pub fn slerp(self, b: Self, t: f32) -> Self {
341        let theta = self.angle_to(b);
342        self.rotated(self.cross(b).normalized(), theta * t)
343    }
344
345    /// Returns this vector slid along a plane defined by the given normal.
346    #[inline]
347    pub fn slide(self, n: Self) -> Self {
348        self - n * self.dot(n)
349    }
350
351    /// Returns this vector with each component snapped to the nearest multiple of step.
352    /// This can also be used to round to an arbitrary number of decimals.
353    #[inline]
354    pub fn snapped(self, by: Self) -> Self {
355        let stepify = |v: f32, s: f32| {
356            if by.x != 0.0 {
357                (v / s + 0.5).floor() * s
358            } else {
359                v
360            }
361        };
362        Self::new(
363            stepify(self.x, by.x),
364            stepify(self.y, by.y),
365            stepify(self.z, by.z),
366        )
367    }
368
369    /// Returns a diagonal matrix with the vector as main diagonal.
370    ///
371    /// This is equivalent to a Basis with no rotation or shearing and this vector's components set
372    /// as the scale.
373    #[inline]
374    pub fn to_diagonal_matrix(self) -> Basis {
375        Basis::from_diagonal(self)
376    }
377
378    #[doc(hidden)]
379    #[allow(clippy::wrong_self_convention)]
380    #[inline]
381    pub fn to_sys(self) -> sys::godot_vector3 {
382        unsafe { std::mem::transmute(self) }
383    }
384
385    /// Internal API for converting to `sys` representation. Makes it possible to remove
386    /// `transmute`s elsewhere.
387    #[doc(hidden)]
388    #[inline]
389    pub fn sys(&self) -> *const sys::godot_vector3 {
390        self as *const _ as *const _
391    }
392
393    /// Internal API for converting from `sys` representation. Makes it possible to remove
394    /// `transmute`s elsewhere.
395    #[doc(hidden)]
396    #[inline]
397    pub fn from_sys(v: sys::godot_vector3) -> Self {
398        unsafe { std::mem::transmute(v) }
399    }
400
401    #[inline]
402    pub(super) fn glam(self) -> Vec3A {
403        Vec3A::new(self.x, self.y, self.z)
404    }
405
406    #[inline]
407    pub(super) fn gd(from: Vec3A) -> Self {
408        Self::new(from.x, from.y, from.z)
409    }
410}
411
412impl AsRef<[f32; 3]> for Vector3 {
413    #[inline]
414    fn as_ref(&self) -> &[f32; 3] {
415        // SAFETY: Vector3 is repr(C)
416        unsafe { &*(self as *const Vector3 as *const [f32; 3]) }
417    }
418}
419
420macro_rules! derive_op_impl {
421    ($trait:ident, $func:ident) => {
422        impl $trait for Vector3 {
423            type Output = Self;
424
425            #[inline]
426            fn $func(self, with: Self) -> Self {
427                Self::gd(self.glam().$func(with.glam()))
428            }
429        }
430    };
431    ($trait:ident, $func:ident, $in_type:ty) => {
432        impl $trait<$in_type> for Vector3 {
433            type Output = Self;
434
435            #[inline]
436            fn $func(self, with: $in_type) -> Self {
437                Self::gd(self.glam().$func(with))
438            }
439        }
440    };
441}
442
443macro_rules! derive_op_impl_rev {
444    ($trait:ident, $func:ident, $in_type:ty) => {
445        impl $trait<Vector3> for $in_type {
446            type Output = Vector3;
447
448            #[inline]
449            fn $func(self, with: Self::Output) -> Self::Output {
450                $trait::$func(with, self)
451            }
452        }
453    };
454}
455
456macro_rules! derive_assign_op_impl {
457    ($trait:ident, $func:ident, $op_func:ident) => {
458        impl $trait for Vector3 {
459            #[inline]
460            fn $func(&mut self, with: Self) {
461                *self = self.$op_func(with);
462            }
463        }
464    };
465    ($trait:ident, $func:ident, $op_func:ident, $in_type:ty) => {
466        impl $trait<$in_type> for Vector3 {
467            #[inline]
468            fn $func(&mut self, with: $in_type) {
469                *self = self.$op_func(with);
470            }
471        }
472    };
473}
474
475derive_op_impl!(Add, add);
476derive_op_impl!(Sub, sub);
477derive_op_impl!(Mul, mul);
478derive_op_impl!(Div, div);
479derive_op_impl!(Mul, mul, f32);
480derive_op_impl!(Div, div, f32);
481derive_op_impl_rev!(Mul, mul, f32);
482derive_assign_op_impl!(AddAssign, add_assign, add);
483derive_assign_op_impl!(SubAssign, sub_assign, sub);
484derive_assign_op_impl!(MulAssign, mul_assign, mul);
485derive_assign_op_impl!(DivAssign, div_assign, div);
486derive_assign_op_impl!(MulAssign, mul_assign, mul, f32);
487derive_assign_op_impl!(DivAssign, div_assign, div, f32);
488
489impl Neg for Vector3 {
490    type Output = Self;
491
492    #[inline]
493    fn neg(self) -> Self {
494        Self::gd(-self.glam())
495    }
496}
497
498godot_test!(
499    test_vector3_variants {
500        use crate::core_types::{FromVariant, ToVariant, Vector3};
501
502        fn test(vector: Vector3, set_to: Vector3) {
503            let api = crate::private::get_api();
504
505            let copied = vector;
506            unsafe {
507                assert_relative_eq!(vector.x, (api.godot_vector3_get_axis)(
508                    &copied as *const _ as *const sys::godot_vector3,
509                    Axis::X as u32 as sys::godot_vector3_axis
510                ));
511                assert_relative_eq!(vector.y, (api.godot_vector3_get_axis)(
512                    &copied as *const _ as *const sys::godot_vector3,
513                    Axis::Y as u32 as sys::godot_vector3_axis
514                ));
515                assert_relative_eq!(vector.z, (api.godot_vector3_get_axis)(
516                    &copied as *const _ as *const sys::godot_vector3,
517                    Axis::Z as u32 as sys::godot_vector3_axis
518                ));
519            }
520            assert_eq!(vector, copied);
521
522            let mut copied = vector;
523            unsafe {
524                (api.godot_vector3_set_axis)(
525                    &mut copied as *mut _ as *mut sys::godot_vector3,
526                    Axis::X as u32 as sys::godot_vector3_axis,
527                    set_to.x
528                );
529                (api.godot_vector3_set_axis)(
530                    &mut copied as *mut _ as *mut sys::godot_vector3,
531                    Axis::Y as u32 as sys::godot_vector3_axis,
532                    set_to.y
533                );
534                (api.godot_vector3_set_axis)(
535                    &mut copied as *mut _ as *mut sys::godot_vector3,
536                    Axis::Z as u32 as sys::godot_vector3_axis,
537                    set_to.z
538                );
539            }
540            assert_eq!(set_to, copied);
541
542            let variant = vector.to_variant();
543            let vector_from_variant = Vector3::from_variant(&variant).unwrap();
544            assert_eq!(vector, vector_from_variant);
545        }
546
547        test(Vector3::new(1.0, 2.0, 3.0), Vector3::new(4.0, 5.0, 6.0));
548        test(Vector3::new(4.0, 5.0, 6.0), Vector3::new(7.0, 8.0, 9.0));
549    }
550);
551
552#[cfg(test)]
553mod tests {
554    use crate::core_types::Vector3;
555
556    /*
557     * Test introduced due to bug in Basis * Vector3 operator
558     *
559     * matching result in GDScript:
560     * var v1 = Vector3(37.51756, 20.39467, 49.96816)
561     * var phi = -0.4927880786382844
562     * var v2 = v1.rotated(Vector3.UP, r)
563     * print(c)
564     */
565    #[test]
566    fn rotated() {
567        let v = Vector3::new(37.51756, 20.39467, 49.96816);
568        let phi = -0.4927880786382844;
569        let r = v.rotated(Vector3::UP, phi);
570        assert!(r.is_equal_approx(Vector3::new(9.414476, 20.39467, 61.77177)));
571    }
572
573    #[test]
574    fn it_is_copy() {
575        fn copy<T: Copy>() {}
576        copy::<Vector3>();
577    }
578
579    #[test]
580    fn it_has_the_same_size() {
581        use std::mem::size_of;
582        assert_eq!(size_of::<sys::godot_vector3>(), size_of::<Vector3>());
583    }
584
585    #[test]
586    fn it_supports_equality() {
587        assert_eq!(Vector3::new(1.0, 2.0, 3.0), Vector3::new(1.0, 2.0, 3.0));
588    }
589
590    #[test]
591    fn it_supports_inequality() {
592        assert_ne!(Vector3::new(1.0, 10.0, 100.0), Vector3::new(1.0, 2.0, 3.0));
593    }
594}