Skip to content

Commit

Permalink
Add basic impls of Rect2, Rect2i, Aabb, Plane
Browse files Browse the repository at this point in the history
Add `Mul<X>` impls for `Transform2/3D` for the new types
Add `min/max` functions for `Vector2/3`
  • Loading branch information
lilizoey committed Mar 15, 2023
1 parent f05915f commit c82e566
Show file tree
Hide file tree
Showing 13 changed files with 608 additions and 28 deletions.
2 changes: 1 addition & 1 deletion examples/dodge-the-creeps/rust/src/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ impl GodotExt for Player {

fn ready(&mut self) {
let viewport = self.base.get_viewport_rect();
self.screen_size = viewport.size();
self.screen_size = viewport.size;
self.base.hide();
}

Expand Down
77 changes: 77 additions & 0 deletions godot-core/src/builtin/aabb.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* 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 godot_ffi as sys;
use sys::{ffi_methods, GodotFfi};

use super::Vector3;

/// Axis-aligned bounding box in 3D space.
///
/// `Aabb` consists of a position, a size, and several utility functions. It is typically used for
/// fast overlap tests.
///
/// Currently most methods are only available through [`InnerAabb`](super::inner::InnerAabb).
///
/// The 2D counterpart to `Aabb` is [`Rect2`](super::Rect2).
#[derive(Default, Copy, Clone, PartialEq, Debug)]
#[repr(C)]
pub struct Aabb {
pub position: Vector3,
pub size: Vector3,
}

impl Aabb {
/// Create a new `Aabb` from a position and a size.
///
/// _Godot equivalent: `Aabb(Vector3 position, Vector3 size)`_
#[inline]
pub const fn new(position: Vector3, size: Vector3) -> Self {
Self { position, size }
}

/// The end of the `Aabb` calculated as `position + size`.
///
/// _Godot equivalent: `Aabb.size`_
#[inline]
pub fn end(&self) -> Vector3 {
self.position + self.size
}

/// Set size based on desired end-point.
///
/// _Godot equivalent: `Aabb.size`_
#[inline]
pub fn set_end(&mut self, end: Vector3) {
self.size = end - self.position
}

/// Returns `true` if the two `Aabb`s are approximately equal, by calling `is_equal_approx` on
/// `position` and `size`.
///
/// _Godot equivalent: `Aabb.is_equal_approx()`_
#[inline]
pub fn is_equal_approx(&self, other: &Self) -> bool {
self.position.is_equal_approx(other.position) && self.size.is_equal_approx(other.size)
}

/* Add in when `Aabb::abs()` is implemented.
/// Assert that the size of the `Aabb` is not negative.
///
/// Certain functions will fail to give a correct result if the size is negative.
#[inline]
fn assert_nonnegative(&self) {
assert!(
self.size.x >= 0.0 && self.size.y >= 0.0 && self.size.z >= 0.0,
"Aabb size is negative, this is not supported. Use Aabb.abs() to get an Aabb with a positive size."
);
}
*/
}

impl GodotFfi for Aabb {
ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
}
19 changes: 19 additions & 0 deletions godot-core/src/builtin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
// Re-export macros.
pub use crate::{array, dict, varray};

pub use aabb::*;
pub use array_inner::{Array, VariantArray};
pub use basis::*;
pub use color::*;
Expand All @@ -43,8 +44,11 @@ pub use math::*;
pub use node_path::*;
pub use others::*;
pub use packed_array::*;
pub use plane::*;
pub use projection::*;
pub use quaternion::*;
pub use rect2::*;
pub use rect2i::*;
pub use rid::*;
pub use string::*;
pub use string_name::*;
Expand Down Expand Up @@ -84,15 +88,19 @@ mod array_inner;
#[path = "dictionary.rs"]
mod dictionary_inner;

mod aabb;
mod basis;
mod color;
mod glam_helpers;
mod math;
mod node_path;
mod others;
mod packed_array;
mod plane;
mod projection;
mod quaternion;
mod rect2;
mod rect2i;
mod rid;
mod string;
mod string_chars;
Expand Down Expand Up @@ -318,3 +326,14 @@ macro_rules! real {
f
}};
}

/// The side of a [`Rect2`] or [`Rect2i`].
///
/// _Godot equivalent: `@GlobalScope.Side`_
#[repr(C)]
pub enum RectSide {
Left = 0,
Top = 1,
Right = 2,
Bottom = 3,
}
22 changes: 1 addition & 21 deletions godot-core/src/builtin/others.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,16 @@

// Stub for various other built-in classes, which are currently incomplete, but whose types
// are required for codegen
use crate::builtin::{inner, StringName, Vector2};
use crate::builtin::{inner, StringName};
use crate::obj::{Gd, GodotClass};
use godot_ffi as sys;
use sys::{ffi_methods, GodotFfi};

// TODO: Swap more inner math types with glam types
// Note: ordered by enum ord in extension JSON
impl_builtin_stub!(Rect2, OpaqueRect2);
impl_builtin_stub!(Rect2i, OpaqueRect2i);
impl_builtin_stub!(Plane, OpaquePlane);
impl_builtin_stub!(Aabb, OpaqueAabb);
impl_builtin_stub!(Callable, OpaqueCallable);
impl_builtin_stub!(Signal, OpaqueSignal);

#[repr(C)]
struct InnerRect {
position: Vector2,
size: Vector2,
}

impl Rect2 {
pub fn size(self) -> Vector2 {
self.inner().size
}

fn inner(self) -> InnerRect {
unsafe { std::mem::transmute(self) }
}
}

impl Callable {
pub fn from_object_method<T, S>(object: Gd<T>, method: S) -> Self
where
Expand Down
168 changes: 168 additions & 0 deletions godot-core/src/builtin/plane.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
* 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::ops::Neg;

use godot_ffi as sys;
use sys::{ffi_methods, GodotFfi};

use super::{is_equal_approx, real, Vector3};

/// 3D plane in Hessian form: `a*b + b*y + c*z + d = 0`
///
/// Currently most methods are only available through [`InnerPlane`](super::inner::InnerPlane).
///
/// Note: almost all methods on `Plane` require that the `normal` vector have
/// unit length and will panic if this invariant is violated. This is not separately
/// annotated for each method.
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(C)]
pub struct Plane {
pub normal: Vector3,
pub d: real,
}

impl Plane {
/// Creates a new `Plane` from the `normal` and the distance from the origin `d`.
///
/// # Panics
/// In contrast to construction via `Plane { normal, d }`, this verifies that `normal` has unit length, and will
/// panic if this is not the case.
///
/// _Godot equivalent: `Plane(Vector3 normal, float d)`_
#[inline]
pub fn new(normal: Vector3, d: real) -> Self {
let plane = Self { normal, d };
plane.assert_normalized();
plane
}

/// Create a new `Plane` through the origin from a normal.
///
/// # Panics
/// See [`Self::new()`].
///
/// _Godot equivalent: `Plane(Vector3 normal)`_
#[inline]
pub fn from_normal_at_origin(normal: Vector3) -> Self {
Self::new(normal, 0.0)
}

/// Create a new `Plane` from a normal and a point in the plane.
///
/// # Panics
/// See [`Self::new()`].
///
/// _Godot equivalent: `Plane(Vector3 normal, Vector3 point)`_
#[inline]
pub fn from_point_normal(point: Vector3, normal: Vector3) -> Self {
Self::new(normal, normal.dot(point))
}

/// Creates a new `Plane` from normal and origin distance.
///
/// `a`, `b`, `c` are used for the `normal` vector.
/// `d` is the distance from the origin.
///
/// # Panics
/// See [`Self::new()`].
///
/// _Godot equivalent: `Plane(float a, float b, float c, float d)`_
#[inline]
pub fn from_coordinates(a: real, b: real, c: real, d: real) -> Self {
Self::new(Vector3::new(a, b, c), d)
}

/// Creates a new `Plane` from three points, given in clockwise order.
///
/// # Panics
/// Will panic if all three points are colinear.
///
/// _Godot equivalent: `Plane(Vector3 point1, Vector3 point2, Vector3 point3)`_
#[inline]
pub fn from_points(a: Vector3, b: Vector3, c: Vector3) -> Self {
let normal = (a - c).cross(a - b);
assert!(
normal != Vector3::ZERO,
"The points {a}, {b}, {c} are all colinear"
);
let normal = normal.normalized();
Self {
normal,
d: normal.dot(a),
}
}

/// Returns `true` if the two `Plane`s are approximately equal, by calling `is_equal_approx` on
/// `normal` and `d` or on `-normal` and `-d`.
///
/// _Godot equivalent: `Plane.is_equal_approx()`_
#[inline]
pub fn is_equal_approx(&self, other: &Self) -> bool {
(self.normal.is_equal_approx(other.normal) && is_equal_approx(self.d, other.d))
|| (self.normal.is_equal_approx(-other.normal) && is_equal_approx(self.d, -other.d))
}

#[inline]
fn assert_normalized(self) {
assert!(
self.normal.is_normalized(),
"Plane {:?} -- normal does not have unit length",
self.normal
);
}
}

impl Neg for Plane {
type Output = Plane;

/// Returns the negative value of the plane by flipping both the normal and the distance value. Meaning
/// it creates a plane that is in the same place, but facing the opposite direction.
fn neg(self) -> Self::Output {
Self::new(-self.normal, -self.d)
}
}

impl GodotFfi for Plane {
ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
}

#[cfg(test)]
mod test {
use super::*;

/// Tests that none of the constructors panic for some simple planes.
#[test]
fn construction_succeeds() {
let vec = Vector3::new(1.0, 2.0, 3.0).normalized();
let Vector3 { x, y, z } = vec;
let _ = Plane::new(vec, 5.0);
let _ = Plane::from_normal_at_origin(vec);
let _ = Plane::from_point_normal(Vector3::new(10.0, 20.0, 30.0), vec);
let _ = Plane::from_coordinates(x, y, z, 5.0);
let _ = Plane::from_points(
Vector3::new(1.0, 2.0, 3.0),
Vector3::new(2.0, 3.0, 1.0),
Vector3::new(3.0, 2.0, 1.0),
);
}

#[test]
#[should_panic]
fn new_unnormalized_panics() {
let _ = Plane::new(Vector3::new(1.0, 2.0, 3.0), 5.0);
}

#[test]
#[should_panic]
fn from_points_colinear_panics() {
let _ = Plane::from_points(
Vector3::new(0.0, 0.0, 0.0),
Vector3::new(0.0, 0.0, 1.0),
Vector3::new(0.0, 0.0, 2.0),
);
}
}
Loading

0 comments on commit c82e566

Please sign in to comment.