diff --git a/examples/dodge-the-creeps/rust/src/player.rs b/examples/dodge-the-creeps/rust/src/player.rs index 0d8a1c563..f24265129 100644 --- a/examples/dodge-the-creeps/rust/src/player.rs +++ b/examples/dodge-the-creeps/rust/src/player.rs @@ -62,7 +62,7 @@ impl GodotExt for Player { .base .get_node_as::("AnimatedSprite2D"); - let mut velocity = Vector2::new(0.0, 0.0).inner(); + let mut velocity = Vector2::new(0.0, 0.0); // Note: exact=false by default, in Rust we have to provide it explicitly let input = Input::singleton(); @@ -80,7 +80,7 @@ impl GodotExt for Player { } if velocity.length() > 0.0 { - velocity = velocity.normalize() * self.speed; + velocity = velocity.normalized() * self.speed; let animation; @@ -101,10 +101,10 @@ impl GodotExt for Player { } let change = velocity * delta as f32; - let position = self.base.get_global_position().inner() + change; + let position = self.base.get_global_position() + change; let position = Vector2::new( - position.x.max(0.0).min(self.screen_size.inner().x), - position.y.max(0.0).min(self.screen_size.inner().y), + position.x.max(0.0).min(self.screen_size.x), + position.y.max(0.0).min(self.screen_size.y), ); self.base.set_global_position(position); } diff --git a/godot-core/src/builtin/mod.rs b/godot-core/src/builtin/mod.rs index 9f8c83c4a..fc97996ac 100644 --- a/godot-core/src/builtin/mod.rs +++ b/godot-core/src/builtin/mod.rs @@ -4,9 +4,36 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -//! Built-in types like `Vector2`, `GodotString` or `Variant`. +//! Built-in types like `Vector2`, `GodotString` and `Variant`. +//! +//! # Implementation note on vector algebra types +//! +//! The basic vector algebra types like `Vector2`, `Matrix4` and `Quaternion` are re-implemented +//! here, with an API similar to that in the Godot engine itself. There are other approaches, but +//! they all have their disadvantages: +//! +//! - We could invoke API methods from the engine. The implementations could be generated, but it +//! is slower and prevents inlining. +//! +//! - We could re-export types from an existing vector algebra crate, like `glam`. This removes the +//! duplication, but it would create a strong dependency on a volatile API outside our control. +//! The `gdnative` crate started out this way, using types from `euler`, but [found it +//! impractical](https://github.com/godot-rust/gdnative/issues/594#issue-705061720). Moreover, +//! the API would not match Godot's own, which would make porting from GDScript (slightly) +//! harder. +//! +//! - We could opaquely wrap types from an existing vector algebra crate. This protects users of +//! `gdextension` from changes in the wrapped crate. However, direct field access using `.x`, +//! `.y`, `.z` is no longer possible. Instead of `v.y += a;` you would have to write +//! `v.set_y(v.get_y() + a);`. (A `union` could be used to add these fields in the public API, +//! but would make every field access unsafe, which is also not great.) +//! +//! - We could re-export types from the [`mint`](https://crates.io/crates/mint) crate, which was +//! explicitly designed to solve this problem. However, it falls short because [operator +//! overloading would become impossible](https://github.com/kvark/mint/issues/75). mod macros; +mod vector_macros; mod arrays; mod color; @@ -16,8 +43,11 @@ mod string; mod string_name; mod variant; mod vector2; +mod vector2i; mod vector3; +mod vector3i; mod vector4; +mod vector4i; pub mod meta; @@ -29,5 +59,8 @@ pub use string::*; pub use string_name::*; pub use variant::*; pub use vector2::*; +pub use vector2i::*; pub use vector3::*; +pub use vector3i::*; pub use vector4::*; +pub use vector4i::*; diff --git a/godot-core/src/builtin/vector2.rs b/godot-core/src/builtin/vector2.rs index 2e3eb6dc9..77b7b922f 100644 --- a/godot-core/src/builtin/vector2.rs +++ b/godot-core/src/builtin/vector2.rs @@ -4,80 +4,113 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use std::fmt; + use godot_ffi as sys; use sys::{ffi_methods, GodotFfi}; -type Inner = glam::f32::Vec2; -//type Inner = glam::f64::DVec2; +use crate::builtin::Vector2i; -#[derive(Default, Copy, Clone, Debug, PartialEq)] +/// Vector used for 2D math using floating point coordinates. +/// +/// # Description +/// +/// 2-element structure that can be used to represent positions in 2D space or any other pair of +/// numeric values. +/// +/// It uses floating-point coordinates of 32-bit precision, unlike the engine's `float` type which +/// is always 64-bit. The engine can be compiled with the option `precision=double` to use 64-bit +/// vectors, but this is not yet supported in the `gdextension` crate. +/// +/// See [`Vector2i`] for its integer counterpart. +#[derive(Debug, Default, Clone, Copy, PartialEq)] #[repr(C)] pub struct Vector2 { - inner: Inner, + /// The vector's X component. + pub x: f32, + /// The vector's Y component. + pub y: f32, } +impl_vector_operators!(Vector2, f32, (x, y)); +impl_vector_index!(Vector2, f32, (x, y), Vector2Axis, (X, Y)); +impl_common_vector_fns!(Vector2, f32); +impl_float_vector_fns!(Vector2, f32); + impl Vector2 { - pub fn new(x: f32, y: f32) -> Self { - Self { - inner: Inner::new(x, y), - } + /// Constructs a new `Vector2` from the given `x` and `y`. + pub const fn new(x: f32, y: f32) -> Self { + Self { x, y } } - pub fn from_inner(inner: Inner) -> Self { - Self { inner } + /// Constructs a new `Vector2` with all components set to `v`. + pub const fn splat(v: f32) -> Self { + Self { x: v, y: v } } - /// only for testing - pub fn inner(self) -> Inner { - self.inner + /// Constructs a new `Vector2` from a [`Vector2i`]. + pub const fn from_vector2i(v: Vector2i) -> Self { + Self { x: v.x as f32, y: v.y as f32 } } - // Hacks for example - // pub fn length(self) -> f32 { - // self.inner.length() - // } - // pub fn normalized(self) -> Vector2 { - // Self::from_inner(self.inner.normalize()) - // } - pub fn rotated(self, angle: f32) -> Self { - Self::from_inner(glam::Affine2::from_angle(angle).transform_vector2(self.inner)) - } -} + /// Zero vector, a vector with all components set to `0.0`. + pub const ZERO: Self = Self::splat(0.0); -impl GodotFfi for Vector2 { - ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } -} + /// One vector, a vector with all components set to `1.0`. + pub const ONE: Self = Self::splat(1.0); -impl std::fmt::Display for Vector2 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.inner.fmt(f) - } -} + /// Infinity vector, a vector with all components set to `INFIINTY`. + pub const INF: Self = Self::splat(f32::INFINITY); -// ---------------------------------------------------------------------------------------------------------------------------------------------- + /// Left unit vector. Represents the direction of left. + pub const LEFT: Self = Self::new(-1.0, 0.0); -type IInner = glam::IVec2; + /// Right unit vector. Represents the direction of right. + pub const RIGHT: Self = Self::new(1.0, 0.0); -#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)] -#[repr(C)] -pub struct Vector2i { - inner: IInner, + /// Up unit vector. Y is down in 2D, so this vector points -Y. + pub const UP: Self = Self::new(0.0, -1.0); + + /// Down unit vector. Y is down in 2D, so this vector points +Y. + pub const DOWN: Self = Self::new(0.0, 1.0); + + /// Returns the result of rotating this vector by `angle` (in radians). + pub fn rotated(self, angle: f32) -> Self { + Self::from_glam(glam::Affine2::from_angle(angle).transform_vector2(self.to_glam())) + } + + /// Converts the corresponding `glam` type to `Self`. + fn from_glam(v: glam::Vec2) -> Self { + Self::new(v.x, v.y) + } + + /// Converts `self` to the corresponding `glam` type. + fn to_glam(self) -> glam::Vec2 { + glam::Vec2::new(self.x, self.y) + } } -impl Vector2i { - pub fn new(x: i32, y: i32) -> Self { - Self { - inner: IInner::new(x, y), - } +impl fmt::Display for Vector2 { + /// Formats this vector in the same way the Godot engine would. + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({}, {})", self.x, self.y) } } -impl GodotFfi for Vector2i { +impl GodotFfi for Vector2 { ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } -impl std::fmt::Display for Vector2i { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.inner.fmt(f) - } +/// Enumerates the axes in a [`Vector2`]. +#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[repr(i32)] +pub enum Vector2Axis { + /// The X axis. + X, + /// The Y axis. + Y, +} + +impl GodotFfi for Vector2Axis { + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } diff --git a/godot-core/src/builtin/vector2i.rs b/godot-core/src/builtin/vector2i.rs new file mode 100644 index 000000000..cc719e8ff --- /dev/null +++ b/godot-core/src/builtin/vector2i.rs @@ -0,0 +1,107 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +use std::fmt; + +use godot_ffi as sys; +use sys::{ffi_methods, GodotFfi}; + +use crate::builtin::Vector2; + +/// Vector used for 2D math using integer coordinates. +/// +/// # Description +/// +/// 2-element structure that can be used to represent positions in 2D space or any other pair of +/// numeric values. +/// +/// It uses integer coordinates and is therefore preferable to [`Vector2`] when exact precision is +/// required. Note that the values are limited to 32 bits, and unlike [`Vector2`] this cannot be +/// configured with an engine build option. Use `i64` or [`PackedInt64Array`] if 64-bit values are +/// needed. +#[derive(Debug, Default, Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] +#[repr(C)] +pub struct Vector2i { + /// The vector's X component. + pub x: i32, + /// The vector's Y component. + pub y: i32, +} + +impl_vector_operators!(Vector2i, i32, (x, y)); +impl_vector_index!(Vector2i, i32, (x, y), Vector2iAxis, (X, Y)); +impl_common_vector_fns!(Vector2i, i32); + +impl Vector2i { + /// Constructs a new `Vector2i` from the given `x` and `y`. + pub const fn new(x: i32, y: i32) -> Self { + Self { x, y } + } + + /// Constructs a new `Vector2i` with all components set to `v`. + pub const fn splat(v: i32) -> Self { + Self { x: v, y: v } + } + + /// Constructs a new `Vector2i` from a [`Vector2`]. The floating point coordinates will be + /// truncated. + pub const fn from_vector2(v: Vector2) -> Self { + Self { x: v.x as i32, y: v.y as i32 } + } + + /// Zero vector, a vector with all components set to `0`. + pub const ZERO: Self = Self::splat(0); + + /// One vector, a vector with all components set to `1`. + pub const ONE: Self = Self::splat(1); + + /// Left unit vector. Represents the direction of left. + pub const LEFT: Self = Self::new(-1, 0); + + /// Right unit vector. Represents the direction of right. + pub const RIGHT: Self = Self::new(1, 0); + + /// Up unit vector. Y is down in 2D, so this vector points -Y. + pub const UP: Self = Self::new(0, -1); + + /// Down unit vector. Y is down in 2D, so this vector points +Y. + pub const DOWN: Self = Self::new(0, 1); + + /// Converts the corresponding `glam` type to `Self`. + fn from_glam(v: glam::IVec2) -> Self { + Self::new(v.x, v.y) + } + + /// Converts `self` to the corresponding `glam` type. + fn to_glam(self) -> glam::IVec2 { + glam::IVec2::new(self.x, self.y) + } +} + +impl fmt::Display for Vector2i { + /// Formats this vector in the same way the Godot engine would. + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({}, {})", self.x, self.y) + } +} + +impl GodotFfi for Vector2i { + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } +} + +/// Enumerates the axes in a [`Vector2i`]. +#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[repr(i32)] +pub enum Vector2iAxis { + /// The X axis. + X, + /// The Y axis. + Y, +} + +impl GodotFfi for Vector2iAxis { + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } +} diff --git a/godot-core/src/builtin/vector3.rs b/godot-core/src/builtin/vector3.rs index 0f2b94954..5b7fa76ee 100644 --- a/godot-core/src/builtin/vector3.rs +++ b/godot-core/src/builtin/vector3.rs @@ -4,75 +4,116 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use std::fmt; + use godot_ffi as sys; use sys::{ffi_methods, GodotFfi}; -type Inner = glam::f32::Vec3; -// type Inner = glam::f64::DVec3; - -#[derive(Default, Copy, Clone, Debug, PartialEq)] +use crate::builtin::Vector3i; + +/// Vector used for 3D math using floating point coordinates. +/// +/// # Description +/// +/// 2-element structure that can be used to represent positions in 2D space or any other pair of +/// numeric values. +/// +/// It uses floating-point coordinates of 32-bit precision, unlike the engine's `float` type which +/// is always 64-bit. The engine can be compiled with the option `precision=double` to use 64-bit +/// vectors, but this is not yet supported in the `gdextension` crate. +/// +/// See [`Vector3i`] for its integer counterpart. +#[derive(Debug, Default, Clone, Copy, PartialEq)] #[repr(C)] pub struct Vector3 { - inner: Inner, + /// The vector's X component. + pub x: f32, + /// The vector's Y component. + pub y: f32, + /// The vector's Z component. + pub z: f32, } +impl_vector_operators!(Vector3, f32, (x, y, z)); +impl_vector_index!(Vector3, f32, (x, y, z), Vector3Axis, (X, Y, Z)); +impl_common_vector_fns!(Vector3, f32); +impl_float_vector_fns!(Vector3, f32); + impl Vector3 { - pub fn new(x: f32, y: f32, z: f32) -> Self { - Self { - inner: Inner::new(x, y, z), - } + /// Returns a `Vector3` with the given components. + pub const fn new(x: f32, y: f32, z: f32) -> Self { + Self { x, y, z } } -} -impl GodotFfi for Vector3 { - ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } -} + /// Returns a new `Vector3` with all components set to `v`. + pub const fn splat(v: f32) -> Self { + Self { x: v, y: v, z: v } + } -impl std::fmt::Display for Vector3 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - //let Inner {x, y, z} = self.inner; - //write!(f, "({x}, {y}, {z})") - self.inner.fmt(f) + /// Constructs a new `Vector3` from a [`Vector3i`]. + pub const fn from_vector3i(v: Vector3i) -> Self { + Self { x: v.x as f32, y: v.y as f32, z: v.z as f32 } } -} -// ---------------------------------------------------------------------------------------------------------------------------------------------- + /// Zero vector, a vector with all components set to `0.0`. + pub const ZERO: Self = Self::splat(0.0); -type IInner = glam::IVec3; + /// One vector, a vector with all components set to `1.0`. + pub const ONE: Self = Self::splat(1.0); -#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)] -#[repr(C)] -pub struct Vector3i { - inner: IInner, -} + /// Infinity vector, a vector with all components set to `INFIINTY`. + pub const INF: Self = Self::splat(f32::INFINITY); + + /// Left unit vector. Represents the local direction of left, and the global direction of west. + pub const LEFT: Self = Self::new(-1.0, 0.0, 0.0); -impl Vector3i { - pub fn new(x: i32, y: i32, z: i32) -> Self { - Self { - inner: IInner::new(x, y, z), - } + /// Right unit vector. Represents the local direction of right, and the global direction of east. + pub const RIGHT: Self = Self::new(1.0, 0.0, 0.0); + + /// Up unit vector. + pub const UP: Self = Self::new(0.0, -1.0, 0.0); + + /// Down unit vector. + pub const DOWN: Self = Self::new(0.0, 1.0, 0.0); + + /// Forward unit vector. Represents the local direction of forward, and the global direction of north. + pub const FORWARD: Self = Self::new(0.0, 0.0, -1.0); + + /// Back unit vector. Represents the local direction of back, and the global direction of south. + pub const BACK: Self = Self::new(0.0, 0.0, 1.0); + + /// Converts the corresponding `glam` type to `Self`. + fn from_glam(v: glam::Vec3) -> Self { + Self::new(v.x, v.y, v.z) } -} -impl GodotFfi for Vector3i { - ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } + /// Converts `self` to the corresponding `glam` type. + fn to_glam(self) -> glam::Vec3 { + glam::Vec3::new(self.x, self.y, self.z) + } } -impl std::fmt::Display for Vector3i { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.inner.fmt(f) +impl fmt::Display for Vector3 { + /// Formats this vector in the same way the Godot engine would. + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({}, {}, {})", self.x, self.y, self.z) } } -// ---------------------------------------------------------------------------------------------------------------------------------------------- +impl GodotFfi for Vector3 { + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } +} +/// Enumerates the axes in a [`Vector3`]. // TODO auto-generate this, alongside all the other builtin type's enums - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] #[repr(i32)] pub enum Vector3Axis { + /// The X axis. X, + /// The Y axis. Y, + /// The Z axis. Z, } diff --git a/godot-core/src/builtin/vector3i.rs b/godot-core/src/builtin/vector3i.rs new file mode 100644 index 000000000..34c7a65e7 --- /dev/null +++ b/godot-core/src/builtin/vector3i.rs @@ -0,0 +1,117 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +use std::fmt; + +use godot_ffi as sys; +use sys::{ffi_methods, GodotFfi}; + +use crate::builtin::Vector3; + +/// Vector used for 3D math using integer coordinates. +/// +/// # Description +/// +/// 3-element structure that can be used to represent positions in 3D space or any other pair of +/// numeric values. +/// +/// It uses integer coordinates and is therefore preferable to [`Vector3`] when exact precision is +/// required. Note that the values are limited to 32 bits, and unlike [`Vector3`] this cannot be +/// configured with an engine build option. Use `i64` or [`PackedInt64Array`] if 64-bit values are +/// needed. +#[derive(Debug, Default, Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] +#[repr(C)] +pub struct Vector3i { + /// The vector's X component. + pub x: i32, + /// The vector's Y component. + pub y: i32, + /// The vector's Z component. + pub z: i32, +} + +impl_vector_operators!(Vector3i, i32, (x, y, z)); +impl_vector_index!(Vector3i, i32, (x, y, z), Vector3iAxis, (X, Y, Z)); +impl_common_vector_fns!(Vector3i, i32); + +impl Vector3i { + /// Returns a `Vector3i` with the given components. + pub const fn new(x: i32, y: i32, z: i32) -> Self { + Self { x, y, z } + } + + /// Constructs a new `Vector3i` with all components set to `v`. + pub const fn splat(v: i32) -> Self { + Self { x: v, y: v, z: v } + } + + /// Constructs a new `Vector3i` from a [`Vector3`]. The floating point coordinates will be + /// truncated. + pub const fn from_vector3(v: Vector3) -> Self { + Self { x: v.x as i32, y: v.y as i32, z: v.z as i32 } + } + + /// Zero vector, a vector with all components set to `0`. + pub const ZERO: Self = Self::splat(0); + + /// One vector, a vector with all components set to `1`. + pub const ONE: Self = Self::splat(1); + + /// Left unit vector. Represents the local direction of left, and the global direction of west. + pub const LEFT: Self = Self::new(-1, 0, 0); + + /// Right unit vector. Represents the local direction of right, and the global direction of east. + pub const RIGHT: Self = Self::new(1, 0, 0); + + /// Up unit vector. + pub const UP: Self = Self::new(0, -1, 0); + + /// Down unit vector. + pub const DOWN: Self = Self::new(0, 1, 0); + + /// Forward unit vector. Represents the local direction of forward, and the global direction of north. + pub const FORWARD: Self = Self::new(0, 0, -1); + + /// Back unit vector. Represents the local direction of back, and the global direction of south. + pub const BACK: Self = Self::new(0, 0, 1); + + /// Converts the corresponding `glam` type to `Self`. + fn from_glam(v: glam::IVec3) -> Self { + Self::new(v.x, v.y, v.z) + } + + /// Converts `self` to the corresponding `glam` type. + fn to_glam(self) -> glam::IVec3 { + glam::IVec3::new(self.x, self.y, self.z) + } +} + +impl fmt::Display for Vector3i { + /// Formats this vector in the same way the Godot engine would. + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({}, {}, {})", self.x, self.y, self.z) + } +} + +impl GodotFfi for Vector3i { + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } +} + +/// Enumerates the axes in a [`Vector3i`]. +#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[repr(i32)] +pub enum Vector3iAxis { + /// The X axis. + X, + /// The Y axis. + Y, + /// The Z axis. + Z, +} + +impl GodotFfi for Vector3iAxis { + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } +} diff --git a/godot-core/src/builtin/vector4.rs b/godot-core/src/builtin/vector4.rs index 22e856ff8..024ad52ce 100644 --- a/godot-core/src/builtin/vector4.rs +++ b/godot-core/src/builtin/vector4.rs @@ -4,58 +4,103 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use std::fmt; + use godot_ffi as sys; use sys::{ffi_methods, GodotFfi}; -type Inner = glam::f32::Vec4; -//type Inner = glam::f64::DVec4; +use crate::builtin::Vector4i; -#[derive(Default, Copy, Clone, Debug, PartialEq)] +/// Vector used for 4D math using floating point coordinates. +/// +/// # Description +/// +/// 4-element structure that can be used to represent any quadruplet of numeric values. +/// +/// It uses floating-point coordinates of 32-bit precision, unlike the engine's `float` type which +/// is always 64-bit. The engine can be compiled with the option `precision=double` to use 64-bit +/// vectors, but this is not yet supported in the `gdextension` crate. +/// +/// See [`Vector4i`] for its integer counterpart. +#[derive(Debug, Default, Clone, Copy, PartialEq)] #[repr(C)] pub struct Vector4 { - inner: Inner, + /// The vector's X component. + pub x: f32, + /// The vector's Y component. + pub y: f32, + /// The vector's Z component. + pub z: f32, + /// The vector's W component. + pub w: f32, } +impl_vector_operators!(Vector4, f32, (x, y, z, w)); +impl_vector_index!(Vector4, f32, (x, y, z, w), Vector4Axis, (X, Y, Z, W)); +impl_common_vector_fns!(Vector4, f32); +impl_float_vector_fns!(Vector4, f32); + impl Vector4 { - pub fn new(x: f32, y: f32, z: f32, w: f32) -> Self { - Self { - inner: Inner::new(x, y, z, w), - } + /// Returns a `Vector4` with the given components. + pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self { + Self { x, y, z, w } } -} -impl GodotFfi for Vector4 { - ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } -} + /// Returns a new `Vector4` with all components set to `v`. + pub const fn splat(v: f32) -> Self { + Self { x: v, y: v, z: v, w: v } + } -impl std::fmt::Display for Vector4 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.inner.fmt(f) + /// Constructs a new `Vector3` from a [`Vector3i`]. + pub const fn from_vector4i(v: Vector4i) -> Self { + Self { x: v.x as f32, y: v.y as f32, z: v.z as f32, w: v.w as f32 } } -} -type IInner = glam::IVec4; + /// Zero vector, a vector with all components set to `0.0`. + pub const ZERO: Self = Self::splat(0.0); -#[derive(Default, Copy, Clone, Debug)] -#[repr(C)] -pub struct Vector4i { - inner: IInner, + /// One vector, a vector with all components set to `1.0`. + pub const ONE: Self = Self::splat(1.0); + + /// Infinity vector, a vector with all components set to `INFIINTY`. + pub const INF: Self = Self::splat(f32::INFINITY); + + /// Converts the corresponding `glam` type to `Self`. + fn from_glam(v: glam::Vec4) -> Self { + Self::new(v.x, v.y, v.z, v.w) + } + + /// Converts `self` to the corresponding `glam` type. + fn to_glam(self) -> glam::Vec4 { + glam::Vec4::new(self.x, self.y, self.z, self.w) + } } -impl Vector4i { - pub fn new(x: i32, y: i32, z: i32, w: i32) -> Self { - Self { - inner: IInner::new(x, y, z, w), - } +impl fmt::Display for Vector4 { + /// Formats this vector in the same way the Godot engine would. + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({}, {}, {}, {})", self.x, self.y, self.z, self.w) } } -impl GodotFfi for Vector4i { +impl GodotFfi for Vector4 { ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } -impl std::fmt::Display for Vector4i { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.inner.fmt(f) - } +/// Enumerates the axes in a [`Vector4`]. +#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[repr(i32)] +pub enum Vector4Axis { + /// The X axis. + X, + /// The Y axis. + Y, + /// The Z axis. + Z, + /// The W axis. + W, +} + +impl GodotFfi for Vector4Axis { + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } diff --git a/godot-core/src/builtin/vector4i.rs b/godot-core/src/builtin/vector4i.rs new file mode 100644 index 000000000..f1b8d703f --- /dev/null +++ b/godot-core/src/builtin/vector4i.rs @@ -0,0 +1,102 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +use std::fmt; + +use godot_ffi as sys; +use sys::{ffi_methods, GodotFfi}; + +use crate::builtin::Vector4; + +/// Vector used for 4D math using integer coordinates. +/// +/// # Description +/// +/// 4-element structure that can be used to represent 4D grid coordinates or sets of integers. +/// +/// It uses integer coordinates and is therefore preferable to [`Vector4`] when exact precision is +/// required. Note that the values are limited to 32 bits, and unlike [`Vector4`] this cannot be +/// configured with an engine build option. Use `i64` or [`PackedInt64Array`] if 64-bit values are +/// needed. +#[derive(Debug, Default, Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] +#[repr(C)] +pub struct Vector4i { + /// The vector's X component. + pub x: i32, + /// The vector's Y component. + pub y: i32, + /// The vector's Z component. + pub z: i32, + /// The vector's W component. + pub w: i32, +} + +impl_vector_operators!(Vector4i, i32, (x, y, z, w)); +impl_vector_index!(Vector4i, i32, (x, y, z, w), Vector4iAxis, (X, Y, Z, W)); +impl_common_vector_fns!(Vector4i, i32); + +impl Vector4i { + /// Returns a `Vector4i` with the given components. + pub const fn new(x: i32, y: i32, z: i32, w: i32) -> Self { + Self { x, y, z, w } + } + + /// Constructs a new `Vector4i` with all components set to `v`. + pub const fn splat(v: i32) -> Self { + Self { x: v, y: v, z: v, w: v } + } + + /// Constructs a new `Vector4i` from a [`Vector4`]. The floating point coordinates will be + /// truncated. + pub const fn from_vector3(v: Vector4) -> Self { + Self { x: v.x as i32, y: v.y as i32, z: v.z as i32, w: v.w as i32 } + } + + /// Zero vector, a vector with all components set to `0`. + pub const ZERO: Self = Self::splat(0); + + /// One vector, a vector with all components set to `1`. + pub const ONE: Self = Self::splat(1); + + /// Converts the corresponding `glam` type to `Self`. + fn from_glam(v: glam::IVec4) -> Self { + Self::new(v.x, v.y, v.z, v.w) + } + + /// Converts `self` to the corresponding `glam` type. + fn to_glam(self) -> glam::IVec4 { + glam::IVec4::new(self.x, self.y, self.z, self.w) + } +} + +impl fmt::Display for Vector4i { + /// Formats this vector in the same way the Godot engine would. + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({}, {}, {}, {})", self.x, self.y, self.z, self.w) + } +} + +impl GodotFfi for Vector4i { + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } +} + +/// Enumerates the axes in a [`Vector4i`]. +#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[repr(i32)] +pub enum Vector4iAxis { + /// The X axis. + X, + /// The Y axis. + Y, + /// The Z axis. + Z, + /// The W axis. + W, +} + +impl GodotFfi for Vector4iAxis { + ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } +} diff --git a/godot-core/src/builtin/vector_macros.rs b/godot-core/src/builtin/vector_macros.rs new file mode 100644 index 000000000..e34b44967 --- /dev/null +++ b/godot-core/src/builtin/vector_macros.rs @@ -0,0 +1,221 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#![macro_use] + +/// Implements a single unary operator for a vector type. Only used for `Neg` at the moment. +macro_rules! impl_vector_unary_operator { + ( + // Name of the vector type. + $vector:ty, + // Type of each individual component, for example `i32`. + $scalar:ty, + // Names of the components, with parentheses, for example `(x, y)`. + ($($components:ident),*), + // Name of the operator trait, for example `Neg`. + $operator:ident, + // Name of the function on the operator trait, for example `neg`. + $func:ident$(,)? + ) => { + // -vector + impl std::ops::$operator for $vector { + type Output = Self; + fn $func(mut self) -> Self::Output { + $( + self.$components = self.$components.$func(); + )* + self + } + } + } +} + +/// Implements a single infix binary operator for a vector type, between two vectors and between a +/// vector and a scalar in both directions. +macro_rules! impl_vector_binary_operator { + ( + // Name of the vector type. + $vector:ty, + // Type of each individual component, for example `i32`. + $scalar:ty, + // Names of the components, with parentheses, for example `(x, y)`. + ($($components:ident),*), + // Name of the operator trait, for example `Add`. + $operator:ident, + // Name of the function on the operator trait, for example `add`. + $func:ident$(,)? + ) => { + // vector + vector + impl std::ops::$operator for $vector { + type Output = Self; + fn $func(mut self, rhs: $vector) -> Self::Output { + $( + self.$components = self.$components.$func(rhs.$components); + )* + self + } + } + + // vector + scalar + impl std::ops::$operator<$scalar> for $vector { + type Output = Self; + fn $func(mut self, rhs: $scalar) -> Self::Output { + $( + self.$components = self.$components.$func(rhs); + )* + self + } + } + + // scalar + vector + impl std::ops::$operator<$vector> for $scalar { + type Output = $vector; + fn $func(self, mut rhs: $vector) -> Self::Output { + $( + rhs.$components = rhs.$components.$func(self); + )* + rhs + } + } + } +} + +/// Implements a single arithmetic assignment operator for a vector type. +macro_rules! impl_vector_assign_operator { + ( + // Name of the vector type. + $vector:ty, + // Type of each individual component, for example `i32`. + $scalar:ty, + // Names of the components, with parentheses, for example `(x, y)`. + ($($components:ident),*), + // Name of the operator trait, for example `AddAssign`. + $operator:ident, + // Name of the function on the operator trait, for example `add_assign`. + $func:ident$(,)? + ) => { + // vector += vector + impl std::ops::$operator for $vector { + fn $func(&mut self, rhs: $vector) { + $( + self.$components.$func(rhs.$components); + )* + } + } + + // vector += scalar + impl std::ops::$operator<$scalar> for $vector { + fn $func(&mut self, rhs: $scalar) { + $( + self.$components.$func(rhs); + )* + } + } + } +} + +/// Implements all common arithmetic operators on a built-in vector type. +macro_rules! impl_vector_operators { + ( + // Name of the vector type to be implemented, for example `Vector2`. + $vector:ident, + // Type of each individual component, for example `f32`. + $scalar:ty, + // Names of the components, with parentheses, for example `(x, y)`. + ($($components:ident),*)$(,)? + ) => { + impl_vector_unary_operator!($vector, $scalar, ($($components),*), Neg, neg); + impl_vector_binary_operator!($vector, $scalar, ($($components),*), Add, add); + impl_vector_binary_operator!($vector, $scalar, ($($components),*), Sub, sub); + impl_vector_binary_operator!($vector, $scalar, ($($components),*), Mul, mul); + impl_vector_binary_operator!($vector, $scalar, ($($components),*), Div, div); + impl_vector_binary_operator!($vector, $scalar, ($($components),*), Rem, rem); + impl_vector_assign_operator!($vector, $scalar, ($($components),*), AddAssign, add_assign); + impl_vector_assign_operator!($vector, $scalar, ($($components),*), SubAssign, sub_assign); + impl_vector_assign_operator!($vector, $scalar, ($($components),*), MulAssign, mul_assign); + impl_vector_assign_operator!($vector, $scalar, ($($components),*), DivAssign, div_assign); + impl_vector_assign_operator!($vector, $scalar, ($($components),*), RemAssign, rem_assign); + } +} + +/// Implements `Index` and `IndexMut` for a vector type, using an enum to indicate the desired axis. +macro_rules! impl_vector_index { + ( + // Name of the vector type to be implemented, for example `Vector2`. + $vector:ident, + // Type of each individual component, for example `f32`. + $scalar:ty, + // Names of the components, with parentheses, for example `(x, y)`. + ($($components:ident),*), + // Name of the enum type for the axes, for example `Vector2Axis`. + $axis_enum:ident, + // Names of the enum variants, with parenthes, for example `(X, Y)`. + ($($axis_variants:ident),*)$(,)? + ) => { + impl std::ops::Index<$axis_enum> for $vector { + type Output = $scalar; + fn index(&self, axis: $axis_enum) -> &$scalar { + match axis { + $($axis_enum::$axis_variants => &self.$components),* + } + } + } + + impl std::ops::IndexMut<$axis_enum> for $vector { + fn index_mut(&mut self, axis: $axis_enum) -> &mut $scalar { + match axis { + $($axis_enum::$axis_variants => &mut self.$components),* + } + } + } + } +} + +/// Implements functions on vector types which make sense for both floating-point and integer +/// vectors. +macro_rules! impl_common_vector_fns { + ( + // Name of the vector type. + $vector:ty, + // Type of target component, for example `f32`. + $scalar:ty + ) => { + impl $vector { + /// Returns a new vector with all components in absolute values (i.e. positive or + /// zero). + #[inline] + pub fn abs(self) -> Self { + Self::from_glam(self.to_glam().abs()) + } + } + } +} + +/// Implements common constants and methods for floating-point type vectors. Works for any vector +/// type that has `to_glam` and `from_glam` functions. +macro_rules! impl_float_vector_fns { + ( + // Name of the vector type. + $vector:ty, + // Type of target component, for example `f32`. + $scalar:ty + ) => { + impl $vector { + /// Returns the length (magnitude) of this vector. + #[inline] + pub fn length(self) -> $scalar { + self.to_glam().length() + } + + /// Returns the vector scaled to unit length. Equivalent to `self / self.length()`. See + /// also `is_normalized()`. + #[inline] + pub fn normalized(self) -> Self { + Self::from_glam(self.to_glam().normalize()) + } + } + } +}