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 1 commit
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::*;
127 changes: 79 additions & 48 deletions godot-core/src/builtin/vector2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,80 +4,111 @@
* 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_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`.
ttencate marked this conversation as resolved.
Show resolved Hide resolved
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);
ttencate marked this conversation as resolved.
Show resolved Hide resolved

#[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),
}
/// Formats this vector in the same way the Godot engine would.
ttencate marked this conversation as resolved.
Show resolved Hide resolved
impl fmt::Display for Vector2 {
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; .. }
}
105 changes: 105 additions & 0 deletions godot-core/src/builtin/vector2i.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* 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_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 }
}
ttencate marked this conversation as resolved.
Show resolved Hide resolved

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

/// Formats this vector in the same way the Godot engine would.
impl fmt::Display for Vector2i {
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; .. }
}
Loading