Skip to content

Commit 4c28670

Browse files
committed
Implement the basics of built-in vector types
For Vector{2,3,4}{,i} this implements: - public fields - constructors - constants - operators - indexing by axis - (private) conversions to/from glam types - Display - a couple of functions like `abs()` and `length()` for demonstration See also #6.
1 parent edf2fe5 commit 4c28670

File tree

9 files changed

+824
-125
lines changed

9 files changed

+824
-125
lines changed

examples/dodge-the-creeps/rust/src/player.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ impl GodotExt for Player {
6262
.base
6363
.get_node_as::<AnimatedSprite2D>("AnimatedSprite2D");
6464

65-
let mut velocity = Vector2::new(0.0, 0.0).inner();
65+
let mut velocity = Vector2::new(0.0, 0.0);
6666

6767
// Note: exact=false by default, in Rust we have to provide it explicitly
6868
let input = Input::singleton();
@@ -80,7 +80,7 @@ impl GodotExt for Player {
8080
}
8181

8282
if velocity.length() > 0.0 {
83-
velocity = velocity.normalize() * self.speed;
83+
velocity = velocity.normalized() * self.speed;
8484

8585
let animation;
8686

@@ -101,10 +101,10 @@ impl GodotExt for Player {
101101
}
102102

103103
let change = velocity * delta as f32;
104-
let position = self.base.get_global_position().inner() + change;
104+
let position = self.base.get_global_position() + change;
105105
let position = Vector2::new(
106-
position.x.max(0.0).min(self.screen_size.inner().x),
107-
position.y.max(0.0).min(self.screen_size.inner().y),
106+
position.x.max(0.0).min(self.screen_size.x),
107+
position.y.max(0.0).min(self.screen_size.y),
108108
);
109109
self.base.set_global_position(position);
110110
}

godot-core/src/builtin/mod.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,36 @@
44
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
55
*/
66

7-
//! Built-in types like `Vector2`, `GodotString` or `Variant`.
7+
//! Built-in types like `Vector2`, `GodotString` and `Variant`.
8+
//!
9+
//! # Implementation note on vector algebra types
10+
//!
11+
//! The basic vector algebra types like `Vector2`, `Matrix4` and `Quaternion` are re-implemented
12+
//! here, with an API similar to that in the Godot engine itself. There are other approaches, but
13+
//! they all have their disadvantages:
14+
//!
15+
//! - We could invoke API methods from the engine. The implementations could be generated, but it
16+
//! is slower and prevents inlining.
17+
//!
18+
//! - We could re-export types from an existing vector algebra crate, like `glam`. This removes the
19+
//! duplication, but it would create a strong dependency on a volatile API outside our control.
20+
//! The `gdnative` crate started out this way, using types from `euler`, but [found it
21+
//! impractical](https://github.com/godot-rust/gdnative/issues/594#issue-705061720). Moreover,
22+
//! the API would not match Godot's own, which would make porting from GDScript (slightly)
23+
//! harder.
24+
//!
25+
//! - We could opaquely wrap types from an existing vector algebra crate. This protects users of
26+
//! `gdextension` from changes in the wrapped crate. However, direct field access using `.x`,
27+
//! `.y`, `.z` is no longer possible. Instead of `v.y += a;` you would have to write
28+
//! `v.set_y(v.get_y() + a);`. (A `union` could be used to add these fields in the public API,
29+
//! but would make every field access unsafe, which is also not great.)
30+
//!
31+
//! - We could re-export types from the [`mint`](https://crates.io/crates/mint) crate, which was
32+
//! explicitly designed to solve this problem. However, it falls short because [operator
33+
//! overloading would become impossible](https://github.com/kvark/mint/issues/75).
834
935
mod macros;
36+
mod vector_macros;
1037

1138
mod arrays;
1239
mod color;
@@ -16,8 +43,11 @@ mod string;
1643
mod string_name;
1744
mod variant;
1845
mod vector2;
46+
mod vector2i;
1947
mod vector3;
48+
mod vector3i;
2049
mod vector4;
50+
mod vector4i;
2151

2252
pub mod meta;
2353

@@ -29,5 +59,8 @@ pub use string::*;
2959
pub use string_name::*;
3060
pub use variant::*;
3161
pub use vector2::*;
62+
pub use vector2i::*;
3263
pub use vector3::*;
64+
pub use vector3i::*;
3365
pub use vector4::*;
66+
pub use vector4i::*;

godot-core/src/builtin/vector2.rs

Lines changed: 81 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,80 +4,113 @@
44
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
55
*/
66

7+
use std::fmt;
8+
79
use godot_ffi as sys;
810
use sys::{ffi_methods, GodotFfi};
911

10-
type Inner = glam::f32::Vec2;
11-
//type Inner = glam::f64::DVec2;
12+
use crate::builtin::Vector2i;
1213

13-
#[derive(Default, Copy, Clone, Debug, PartialEq)]
14+
/// Vector used for 2D math using floating point coordinates.
15+
///
16+
/// # Description
17+
///
18+
/// 2-element structure that can be used to represent positions in 2D space or any other pair of
19+
/// numeric values.
20+
///
21+
/// It uses floating-point coordinates of 32-bit precision, unlike the engine's `float` type which
22+
/// is always 64-bit. The engine can be compiled with the option `precision=double` to use 64-bit
23+
/// vectors, but this is not yet supported in the `gdextension` crate.
24+
///
25+
/// See [`Vector2i`] for its integer counterpart.
26+
#[derive(Debug, Default, Clone, Copy, PartialEq)]
1427
#[repr(C)]
1528
pub struct Vector2 {
16-
inner: Inner,
29+
/// The vector's X component.
30+
pub x: f32,
31+
/// The vector's Y component.
32+
pub y: f32,
1733
}
1834

35+
impl_vector_operators!(Vector2, f32, (x, y));
36+
impl_vector_index!(Vector2, f32, (x, y), Vector2Axis, (X, Y));
37+
impl_common_vector_fns!(Vector2, f32);
38+
impl_float_vector_fns!(Vector2, f32);
39+
1940
impl Vector2 {
20-
pub fn new(x: f32, y: f32) -> Self {
21-
Self {
22-
inner: Inner::new(x, y),
23-
}
41+
/// Constructs a new `Vector2` from the given `x` and `y`.
42+
pub const fn new(x: f32, y: f32) -> Self {
43+
Self { x, y }
2444
}
2545

26-
pub fn from_inner(inner: Inner) -> Self {
27-
Self { inner }
46+
/// Constructs a new `Vector2` with all components set to `v`.
47+
pub const fn splat(v: f32) -> Self {
48+
Self { x: v, y: v }
2849
}
2950

30-
/// only for testing
31-
pub fn inner(self) -> Inner {
32-
self.inner
51+
/// Constructs a new `Vector2` from a [`Vector2i`].
52+
pub const fn from_vector2i(v: Vector2i) -> Self {
53+
Self { x: v.x as f32, y: v.y as f32 }
3354
}
3455

35-
// Hacks for example
36-
// pub fn length(self) -> f32 {
37-
// self.inner.length()
38-
// }
39-
// pub fn normalized(self) -> Vector2 {
40-
// Self::from_inner(self.inner.normalize())
41-
// }
42-
pub fn rotated(self, angle: f32) -> Self {
43-
Self::from_inner(glam::Affine2::from_angle(angle).transform_vector2(self.inner))
44-
}
45-
}
56+
/// Zero vector, a vector with all components set to `0.0`.
57+
pub const ZERO: Self = Self::splat(0.0);
4658

47-
impl GodotFfi for Vector2 {
48-
ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
49-
}
59+
/// One vector, a vector with all components set to `1.0`.
60+
pub const ONE: Self = Self::splat(1.0);
5061

51-
impl std::fmt::Display for Vector2 {
52-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53-
self.inner.fmt(f)
54-
}
55-
}
62+
/// Infinity vector, a vector with all components set to `INFIINTY`.
63+
pub const INF: Self = Self::splat(f32::INFINITY);
5664

57-
// ----------------------------------------------------------------------------------------------------------------------------------------------
65+
/// Left unit vector. Represents the direction of left.
66+
pub const LEFT: Self = Self::new(-1.0, 0.0);
5867

59-
type IInner = glam::IVec2;
68+
/// Right unit vector. Represents the direction of right.
69+
pub const RIGHT: Self = Self::new(1.0, 0.0);
6070

61-
#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)]
62-
#[repr(C)]
63-
pub struct Vector2i {
64-
inner: IInner,
71+
/// Up unit vector. Y is down in 2D, so this vector points -Y.
72+
pub const UP: Self = Self::new(0.0, -1.0);
73+
74+
/// Down unit vector. Y is down in 2D, so this vector points +Y.
75+
pub const DOWN: Self = Self::new(0.0, 1.0);
76+
77+
/// Returns the result of rotating this vector by `angle` (in radians).
78+
pub fn rotated(self, angle: f32) -> Self {
79+
Self::from_glam(glam::Affine2::from_angle(angle).transform_vector2(self.as_glam()))
80+
}
81+
82+
/// Converts the corresponding `glam` type to `Self`.
83+
fn from_glam(v: glam::Vec2) -> Self {
84+
Self::new(v.x, v.y)
85+
}
86+
87+
/// Converts `self` to the corresponding `glam` type.
88+
fn as_glam(self) -> glam::Vec2 {
89+
glam::Vec2::new(self.x, self.y)
90+
}
6591
}
6692

67-
impl Vector2i {
68-
pub fn new(x: i32, y: i32) -> Self {
69-
Self {
70-
inner: IInner::new(x, y),
71-
}
93+
impl fmt::Display for Vector2 {
94+
/// Formats this vector in the same way the Godot engine would.
95+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
96+
write!(f, "({}, {})", self.x, self.y)
7297
}
7398
}
7499

75-
impl GodotFfi for Vector2i {
100+
impl GodotFfi for Vector2 {
76101
ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
77102
}
78103

79-
impl std::fmt::Display for Vector2i {
80-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81-
self.inner.fmt(f)
82-
}
104+
/// Enumerates the axes in a [`Vector2`].
105+
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
106+
#[repr(i32)]
107+
pub enum Vector2Axis {
108+
/// The X axis.
109+
X,
110+
/// The Y axis.
111+
Y,
112+
}
113+
114+
impl GodotFfi for Vector2Axis {
115+
ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
83116
}

godot-core/src/builtin/vector2i.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
5+
*/
6+
7+
use std::fmt;
8+
9+
use godot_ffi as sys;
10+
use sys::{ffi_methods, GodotFfi};
11+
12+
use crate::builtin::Vector2;
13+
14+
/// Vector used for 2D math using integer coordinates.
15+
///
16+
/// # Description
17+
///
18+
/// 2-element structure that can be used to represent positions in 2D space or any other pair of
19+
/// numeric values.
20+
///
21+
/// It uses integer coordinates and is therefore preferable to [`Vector2`] when exact precision is
22+
/// required. Note that the values are limited to 32 bits, and unlike [`Vector2`] this cannot be
23+
/// configured with an engine build option. Use `i64` or [`PackedInt64Array`] if 64-bit values are
24+
/// needed.
25+
#[derive(Debug, Default, Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
26+
#[repr(C)]
27+
pub struct Vector2i {
28+
/// The vector's X component.
29+
pub x: i32,
30+
/// The vector's Y component.
31+
pub y: i32,
32+
}
33+
34+
impl_vector_operators!(Vector2i, i32, (x, y));
35+
impl_vector_index!(Vector2i, i32, (x, y), Vector2iAxis, (X, Y));
36+
impl_common_vector_fns!(Vector2i, i32);
37+
38+
impl Vector2i {
39+
/// Constructs a new `Vector2i` from the given `x` and `y`.
40+
pub const fn new(x: i32, y: i32) -> Self {
41+
Self { x, y }
42+
}
43+
44+
/// Constructs a new `Vector2i` with all components set to `v`.
45+
pub const fn splat(v: i32) -> Self {
46+
Self { x: v, y: v }
47+
}
48+
49+
/// Constructs a new `Vector2i` from a [`Vector2`]. The floating point coordinates will be
50+
/// truncated.
51+
pub const fn from_vector2(v: Vector2) -> Self {
52+
Self { x: v.x as i32, y: v.y as i32 }
53+
}
54+
55+
/// Zero vector, a vector with all components set to `0`.
56+
pub const ZERO: Self = Self::splat(0);
57+
58+
/// One vector, a vector with all components set to `1`.
59+
pub const ONE: Self = Self::splat(1);
60+
61+
/// Left unit vector. Represents the direction of left.
62+
pub const LEFT: Self = Self::new(-1, 0);
63+
64+
/// Right unit vector. Represents the direction of right.
65+
pub const RIGHT: Self = Self::new(1, 0);
66+
67+
/// Up unit vector. Y is down in 2D, so this vector points -Y.
68+
pub const UP: Self = Self::new(0, -1);
69+
70+
/// Down unit vector. Y is down in 2D, so this vector points +Y.
71+
pub const DOWN: Self = Self::new(0, 1);
72+
73+
/// Converts the corresponding `glam` type to `Self`.
74+
fn from_glam(v: glam::IVec2) -> Self {
75+
Self::new(v.x, v.y)
76+
}
77+
78+
/// Converts `self` to the corresponding `glam` type.
79+
fn as_glam(self) -> glam::IVec2 {
80+
glam::IVec2::new(self.x, self.y)
81+
}
82+
}
83+
84+
impl fmt::Display for Vector2i {
85+
/// Formats this vector in the same way the Godot engine would.
86+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
87+
write!(f, "({}, {})", self.x, self.y)
88+
}
89+
}
90+
91+
impl GodotFfi for Vector2i {
92+
ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
93+
}
94+
95+
/// Enumerates the axes in a [`Vector2i`].
96+
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
97+
#[repr(i32)]
98+
pub enum Vector2iAxis {
99+
/// The X axis.
100+
X,
101+
/// The Y axis.
102+
Y,
103+
}
104+
105+
impl GodotFfi for Vector2iAxis {
106+
ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
107+
}

0 commit comments

Comments
 (0)