Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement the basics of built-in vector types #71

Merged
merged 2 commits into from
Jan 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions examples/dodge-the-creeps/rust/src/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ impl GodotExt for Player {
.base
.get_node_as::<AnimatedSprite2D>("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();
Expand All @@ -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;

Expand All @@ -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);
}
Expand Down
35 changes: 34 additions & 1 deletion godot-core/src/builtin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
//!
//! # Background on the design of 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 `euclid`, 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;
Expand All @@ -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;

Expand All @@ -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::*;
128 changes: 81 additions & 47 deletions godot-core/src/builtin/vector2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,80 +4,114 @@
* 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.
///
/// 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 Vector2 {
pub fn new(x: f32, y: f32) -> Self {
Self {
inner: Inner::new(x, y),
}
/// Vector with all components set to `0.0`.
pub const ZERO: Self = Self::splat(0.0);

/// Vector with all components set to `1.0`.
pub const ONE: Self = Self::splat(1.0);

/// Vector with all components set to `f32::INFINITY`.
pub const INF: Self = Self::splat(f32::INFINITY);

/// Unit vector in -X direction (right in 2D coordinate system).
pub const LEFT: Self = Self::new(-1.0, 0.0);

/// Unit vector in +X direction (right in 2D coordinate system).
pub const RIGHT: Self = Self::new(1.0, 0.0);

/// Unit vector in -Y direction (up in 2D coordinate system).
pub const UP: Self = Self::new(0.0, -1.0);

/// Unit vector in +Y direction (down in 2D coordinate system).
pub const DOWN: Self = Self::new(0.0, 1.0);

/// 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 both components set to `v`.
pub const fn splat(v: f32) -> Self {
Self::new(v, 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())
// }
/// Returns the result of rotating this vector by `angle` (in radians).
pub fn rotated(self, angle: f32) -> Self {
Self::from_inner(glam::Affine2::from_angle(angle).transform_vector2(self.inner))
Self::from_glam(glam::Affine2::from_angle(angle).transform_vector2(self.to_glam()))
}
}

impl GodotFfi for Vector2 {
ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
}
/// Converts the corresponding `glam` type to `Self`.
fn from_glam(v: glam::Vec2) -> Self {
Self::new(v.x, v.y)
}

impl std::fmt::Display for Vector2 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
/// Converts `self` to the corresponding `glam` type.
fn to_glam(self) -> glam::Vec2 {
glam::Vec2::new(self.x, self.y)
}
}

// ----------------------------------------------------------------------------------------------------------------------------------------------
/// Formats the vector like Godot: `(x, y)`.
impl fmt::Display for Vector2 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}

type IInner = glam::IVec2;
impl_common_vector_fns!(Vector2, f32);
impl_float_vector_fns!(Vector2, f32);
impl_vector_operators!(Vector2, f32, (x, y));
impl_vector_index!(Vector2, f32, (x, y), Vector2Axis, (X, Y));

#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)]
#[repr(C)]
pub struct Vector2i {
inner: IInner,
impl GodotFfi for Vector2 {
ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
}

impl Vector2i {
pub fn new(x: i32, y: i32) -> Self {
Self {
inner: IInner::new(x, y),
}
}
/// 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 Vector2i {
impl GodotFfi for Vector2Axis {
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)
}
}
107 changes: 107 additions & 0 deletions godot-core/src/builtin/vector2i.rs
Original file line number Diff line number Diff line change
@@ -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.
///
/// 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 Vector2i {
/// Vector with all components set to `0`.
pub const ZERO: Self = Self::splat(0);

/// Vector with all components set to `1`.
pub const ONE: Self = Self::splat(1);

/// Unit vector in -X direction (right in 2D coordinate system).
pub const LEFT: Self = Self::new(-1, 0);

/// Unit vector in +X direction (right in 2D coordinate system).
pub const RIGHT: Self = Self::new(1, 0);

/// Unit vector in -Y direction (up in 2D coordinate system).
pub const UP: Self = Self::new(0, -1);

/// Unit vector in +Y direction (down in 2D coordinate system).
pub const DOWN: Self = Self::new(0, 1);

/// 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 both components set to `v`.
pub const fn splat(v: i32) -> Self {
Self::new(v, 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,
}
}

/// 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)
}
}

/// Formats the vector like Godot: `(x, y)`.
impl fmt::Display for Vector2i {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}

impl_common_vector_fns!(Vector2i, i32);
impl_vector_operators!(Vector2i, i32, (x, y));
impl_vector_index!(Vector2i, i32, (x, y), Vector2iAxis, (X, 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; .. }
}
Loading