From 341bc96de681ccb29cfdcd38f6c0acc4b67c1529 Mon Sep 17 00:00:00 2001 From: Lili Zoey Date: Sun, 5 Mar 2023 22:00:45 +0100 Subject: [PATCH] Add support for precision=double add transform2d/3d-itests back Add --double to check.sh Make check.sh run in the stable toolchain, since that's what the CI does --- .github/workflows/full-ci.yml | 69 ++++++- .github/workflows/minimal-ci.yml | 20 +- Contributing.md | 11 ++ check.sh | 50 +++-- .../dodge-the-creeps/rust/src/main_scene.rs | 2 +- examples/dodge-the-creeps/rust/src/mob.rs | 4 +- examples/dodge-the-creeps/rust/src/player.rs | 4 +- godot-codegen/Cargo.toml | 1 + godot-codegen/src/api_parser.rs | 3 + godot-core/Cargo.toml | 1 + godot-core/src/builtin/basis.rs | 84 ++++---- godot-core/src/builtin/glam_helpers.rs | 6 +- godot-core/src/builtin/math.rs | 79 +++++--- godot-core/src/builtin/mod.rs | 187 ++++++++++++++++++ godot-core/src/builtin/packed_array.rs | 4 +- godot-core/src/builtin/projection.rs | 186 ++++++++++------- godot-core/src/builtin/quaternion.rs | 75 +++---- godot-core/src/builtin/transform2d.rs | 84 ++++---- godot-core/src/builtin/transform3d.rs | 24 +-- godot-core/src/builtin/vector2.rs | 88 ++++----- godot-core/src/builtin/vector3.rs | 78 ++++---- godot-core/src/builtin/vector4.rs | 46 ++--- godot-core/src/builtin/vector_macros.rs | 8 +- godot/Cargo.toml | 1 + itest/rust/Cargo.toml | 1 + itest/rust/src/basis_test.rs | 2 +- itest/rust/src/lib.rs | 2 + itest/rust/src/transform2d_test.rs | 6 +- 28 files changed, 740 insertions(+), 386 deletions(-) diff --git a/.github/workflows/full-ci.yml b/.github/workflows/full-ci.yml index 82a383b11..5b387dacd 100644 --- a/.github/workflows/full-ci.yml +++ b/.github/workflows/full-ci.yml @@ -44,7 +44,20 @@ jobs: clippy: + name: clippy (${{ matrix.name }}) runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + include: + - name: linux + rust-toolchain: stable + godot-binary: godot.linuxbsd.editor.dev.x86_64 + + - name: linux-double + rust-toolchain: stable + godot-binary: godot.linuxbsd.editor.dev.double.x86_64 + rust-extra-args: --features double-precision steps: - uses: actions/checkout@v3 @@ -56,13 +69,14 @@ jobs: - name: "Install Godot" uses: ./.github/composite/godot-install with: - artifact-name: godot-linux - godot-binary: godot.linuxbsd.editor.dev.x86_64 + artifact-name: godot-${{ matrix.name }} + godot-binary: ${{ matrix.godot-binary }} - name: "Check clippy" run: | - cargo clippy --all-targets $GDEXT_FEATURES -- -D clippy::suspicious -D clippy::style -D clippy::complexity -D clippy::perf \ - -D clippy::dbg_macro -D clippy::todo -D clippy::unimplemented -D warnings + cargo clippy --all-targets $GDEXT_FEATURES ${{ matrix.rust-extra-args }} -- \ + -D clippy::suspicious -D clippy::style -D clippy::complexity -D clippy::perf \ + -D clippy::dbg_macro -D clippy::todo -D clippy::unimplemented -D warnings unit-test: @@ -80,12 +94,24 @@ jobs: os: macos-11 rust-toolchain: stable godot-binary: godot.macos.editor.dev.x86_64 + with-llvm: true + + - name: macos-double + os: macos-11 + rust-toolchain: stable + godot-binary: godot.macos.editor.dev.double.x86_64 + with-llvm: true - name: windows os: windows-latest rust-toolchain: stable-x86_64-pc-windows-msvc godot-binary: godot.windows.editor.dev.x86_64.exe + - name: windows-double + os: windows-latest + rust-toolchain: stable-x86_64-pc-windows-msvc + godot-binary: godot.windows.editor.dev.double.x86_64.exe + # Don't use latest Ubuntu (22.04) as it breaks lots of ecosystem compatibility. # If ever moving to ubuntu-latest, need to manually install libtinfo5 for LLVM. - name: linux @@ -98,6 +124,12 @@ jobs: rust-toolchain: stable rust-special: -minimal-deps godot-binary: godot.linuxbsd.editor.dev.x86_64 + + - name: linux-double + os: ubuntu-20.04 + rust-toolchain: stable + godot-binary: godot.linuxbsd.editor.dev.double.x86_64 + rust-extra-args: --features double-precision steps: - uses: actions/checkout@v3 @@ -107,7 +139,7 @@ jobs: with: rust: stable cache-key: ${{ matrix.rust-special }} # 'minimal-deps' or empty/not defined - with-llvm: ${{ matrix.name == 'macos' }} + with-llvm: ${{ matrix.with-llvm }} - name: "Install Rust nightly (minimal deps)" uses: ./.github/composite/rust @@ -129,10 +161,10 @@ jobs: godot-binary: ${{ matrix.godot-binary }} - name: "Compile tests" - run: cargo test $GDEXT_FEATURES --no-run + run: cargo test $GDEXT_FEATURES --no-run ${{ matrix.rust-extra-args }} - name: "Test" - run: cargo test $GDEXT_FEATURES + run: cargo test $GDEXT_FEATURES ${{ matrix.rust-extra-args }} godot-itest: @@ -151,12 +183,26 @@ jobs: os: macos-12 rust-toolchain: stable godot-binary: godot.macos.editor.dev.x86_64 + with-llvm: true + + - name: macos-double + os: macos-12 + rust-toolchain: stable + godot-binary: godot.macos.editor.dev.double.x86_64 + rust-extra-args: --features double-precision + with-llvm: true - name: windows os: windows-latest rust-toolchain: stable-x86_64-pc-windows-msvc godot-binary: godot.windows.editor.dev.x86_64.exe + - name: windows-double + os: windows-latest + rust-toolchain: stable-x86_64-pc-windows-msvc + godot-binary: godot.windows.editor.dev.double.x86_64.exe + rust-extra-args: --features double-precision + # Don't use latest Ubuntu (22.04) as it breaks lots of ecosystem compatibility. # If ever moving to ubuntu-latest, need to manually install libtinfo5 for LLVM. - name: linux @@ -182,6 +228,12 @@ jobs: godot-binary: godot.linuxbsd.editor.dev.x86_64.llvm.san godot-args: -- --disallow-focus + - name: linux-double + os: ubuntu-20.04 + rust-toolchain: stable + godot-binary: godot.linuxbsd.editor.dev.double.x86_64 + rust-extra-args: --features double-precision + steps: - uses: actions/checkout@v3 @@ -191,7 +243,8 @@ jobs: artifact-name: godot-${{ matrix.name }} godot-binary: ${{ matrix.godot-binary }} godot-args: ${{ matrix.godot-args }} - with-llvm: ${{ matrix.name == 'macos' }} + with-llvm: ${{ matrix.with-llvm }} + rust-extra-args: ${{ matrix.rust-extra-args }} license-guard: diff --git a/.github/workflows/minimal-ci.yml b/.github/workflows/minimal-ci.yml index 662e044ff..7741f295b 100644 --- a/.github/workflows/minimal-ci.yml +++ b/.github/workflows/minimal-ci.yml @@ -44,7 +44,20 @@ jobs: clippy: + name: clippy (${{ matrix.name }}) runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + include: + - name: linux + rust-toolchain: stable + godot-binary: godot.linuxbsd.editor.dev.x86_64 + + - name: linux-double + rust-toolchain: stable + godot-binary: godot.linuxbsd.editor.dev.double.x86_64 + rust-extra-args: --features double-precision steps: - uses: actions/checkout@v3 @@ -56,12 +69,13 @@ jobs: - name: "Install Godot" uses: ./.github/composite/godot-install with: - artifact-name: godot-linux - godot-binary: godot.linuxbsd.editor.dev.x86_64 + artifact-name: godot-${{ matrix.name }} + godot-binary: ${{ matrix.godot-binary }} - name: "Check clippy" run: | - cargo clippy --all-targets $GDEXT_FEATURES -- -D clippy::suspicious -D clippy::style -D clippy::complexity -D clippy::perf \ + cargo clippy --all-targets $GDEXT_FEATURES ${{ matrix.rust-extra-args }} -- \ + -D clippy::suspicious -D clippy::style -D clippy::complexity -D clippy::perf \ -D clippy::dbg_macro -D clippy::todo -D clippy::unimplemented -D warnings diff --git a/Contributing.md b/Contributing.md index b61e0c7fa..5226c05e6 100644 --- a/Contributing.md +++ b/Contributing.md @@ -60,5 +60,16 @@ $ cargo fmt $ check.sh clippy ``` +## Real + +Certain types in Godot use either a single or double-precision float internally, such as `Vector2`. When using these types we +use the `real` type instead of choosing either `f32` or `f64`. Thus our code is portable between Godot binaries compiled with +`precision=single` or `precision=double`. + +To run the testing suite with `double-precision` enabled you may add `--double` to a `check.sh` invocation: +``` +$ check.sh --double +``` + [GitHub issue]: https://github.com/godot-rust/gdextension/issues [Discord]: https://discord.gg/aKUCJ8rJsc diff --git a/check.sh b/check.sh index fbf48981a..ad41aabae 100755 --- a/check.sh +++ b/check.sh @@ -9,17 +9,10 @@ # Note: at the moment, there is a lot of useless recompilation. # This should be better once unit tests and #[cfg] are sorted out. -# No args specified: do everything -if [ "$#" -eq 0 ]; then - args=("fmt" "clippy" "test" "itest") -else - args=("$@") -fi - # --help menu -for arg in "${args[@]}"; do +for arg in $@; do if [ "$arg" == "--help" ]; then - echo "Usage: check.sh []" + echo "Usage: check.sh [--double] []" echo "" echo "Each specified command will be run (until one fails)." echo "If no commands are specified, all checks are run (no doc; may take several minutes)." @@ -32,13 +25,37 @@ for arg in "${args[@]}"; do echo " doc generate docs for 'godot' crate" echo " dok generate docs and open in browser" echo "" + echo "Options:" + echo " --double run check with double-precision" + echo "" echo "Examples:" echo " check.sh fmt clippy" echo " check.sh" + echo " check.sh --double clippy" exit 0 fi done +firstArg=1 +toolchain="" +extraArgs=() + +if [[ "$1" == "--double" ]]; then + firstArg=2 + extraArgs+=("--features double-precision") +fi + +args=() + +for arg in "${@:$firstArg}"; do + args+=("$arg") +done + +# No args specified: do everything +if [ ${#args[@]} -eq 0 ]; then + args=("fmt" "clippy" "test" "itest") +fi + # For integration tests function findGodot() { # User-defined GODOT4_BIN @@ -67,32 +84,31 @@ function findGodot() { fi } -#features="--features crate/feature" -features="" cmds=() +extraArgs="${extraArgs[@]}" for arg in "${args[@]}"; do case "$arg" in fmt) - cmds+=("cargo fmt --all -- --check") + cmds+=("cargo $toolchain fmt --all -- --check") ;; clippy) - cmds+=("cargo clippy $features -- -D clippy::suspicious -D clippy::style -D clippy::complexity -D clippy::perf -D clippy::dbg_macro -D clippy::todo -D clippy::unimplemented -D warnings") + cmds+=("cargo $toolchain clippy $extraArgs -- -D clippy::suspicious -D clippy::style -D clippy::complexity -D clippy::perf -D clippy::dbg_macro -D clippy::todo -D clippy::unimplemented -D warnings") ;; test) - cmds+=("cargo test $features") + cmds+=("cargo $toolchain test $extraArgs") ;; itest) findGodot - cmds+=("cargo build -p itest") + cmds+=("cargo $toolchain build -p itest $extraArgs") cmds+=("$godotBin --path itest/godot --headless") ;; doc) - cmds+=("cargo doc --lib -p godot --no-deps $features") + cmds+=("cargo $toolchain doc --lib -p godot --no-deps $extraArgs") ;; dok) - cmds+=("cargo doc --lib -p godot --no-deps $features --open") + cmds+=("cargo $toolchain doc --lib -p godot --no-deps $extraArgs --open") ;; *) echo "Unrecognized command '$arg'" diff --git a/examples/dodge-the-creeps/rust/src/main_scene.rs b/examples/dodge-the-creeps/rust/src/main_scene.rs index 157767eed..c528e7496 100644 --- a/examples/dodge-the-creeps/rust/src/main_scene.rs +++ b/examples/dodge-the-creeps/rust/src/main_scene.rs @@ -104,7 +104,7 @@ impl Main { let range = rng.gen_range(mob.min_speed..mob.max_speed); mob.set_linear_velocity(Vector2::new(range, 0.0)); - let lin_vel = mob.get_linear_velocity().rotated(direction as f32); + let lin_vel = mob.get_linear_velocity().rotated(real::from_f64(direction)); mob.set_linear_velocity(lin_vel); } diff --git a/examples/dodge-the-creeps/rust/src/mob.rs b/examples/dodge-the-creeps/rust/src/mob.rs index 45374dffc..6498f0486 100644 --- a/examples/dodge-the-creeps/rust/src/mob.rs +++ b/examples/dodge-the-creeps/rust/src/mob.rs @@ -26,8 +26,8 @@ const MOB_TYPES: [MobType; 3] = [MobType::Walk, MobType::Swim, MobType::Fly]; #[derive(GodotClass)] #[class(base=RigidBody2D)] pub struct Mob { - pub min_speed: f32, - pub max_speed: f32, + pub min_speed: real, + pub max_speed: real, #[base] base: Base, diff --git a/examples/dodge-the-creeps/rust/src/player.rs b/examples/dodge-the-creeps/rust/src/player.rs index c933ed41e..15bf862e9 100644 --- a/examples/dodge-the-creeps/rust/src/player.rs +++ b/examples/dodge-the-creeps/rust/src/player.rs @@ -4,7 +4,7 @@ use godot::prelude::*; #[derive(GodotClass)] #[class(base=Area2D)] pub struct Player { - speed: f32, + speed: real, screen_size: Vector2, #[base] @@ -100,7 +100,7 @@ impl GodotExt for Player { animated_sprite.stop(); } - let change = velocity * delta as f32; + let change = velocity * real::from_f64(delta); let position = self.base.get_global_position() + change; let position = Vector2::new( position.x.clamp(0.0, self.screen_size.x), diff --git a/godot-codegen/Cargo.toml b/godot-codegen/Cargo.toml index 7f1bdeb3f..1c4179efb 100644 --- a/godot-codegen/Cargo.toml +++ b/godot-codegen/Cargo.toml @@ -10,6 +10,7 @@ categories = ["game-engines", "graphics"] [features] codegen-fmt = [] codegen-full = [] +double-precision = [] [dependencies] quote = "1" diff --git a/godot-codegen/src/api_parser.rs b/godot-codegen/src/api_parser.rs index da5d8643b..e0a5ece86 100644 --- a/godot-codegen/src/api_parser.rs +++ b/godot-codegen/src/api_parser.rs @@ -222,6 +222,9 @@ pub fn load_extension_api(watch: &mut StopWatch) -> (ExtensionApi, &'static str) // For float/double inference, see: // * https://github.com/godotengine/godot-proposals/issues/892 // * https://github.com/godotengine/godot-cpp/pull/728 + #[cfg(feature = "double-precision")] + let build_config = "double_64"; // TODO infer this + #[cfg(not(feature = "double-precision"))] let build_config = "float_64"; // TODO infer this let json: String = godot_exe::load_extension_api_json(watch); diff --git a/godot-core/Cargo.toml b/godot-core/Cargo.toml index 0642f69da..2898c12ca 100644 --- a/godot-core/Cargo.toml +++ b/godot-core/Cargo.toml @@ -12,6 +12,7 @@ default = [] trace = [] codegen-fmt = ["godot-ffi/codegen-fmt"] codegen-full = ["godot-codegen/codegen-full"] +double-precision = ["godot-codegen/double-precision"] [dependencies] godot-ffi = { path = "../godot-ffi" } diff --git a/godot-core/src/builtin/basis.rs b/godot-core/src/builtin/basis.rs index 7bf9b5873..bf54e5736 100644 --- a/godot-core/src/builtin/basis.rs +++ b/godot-core/src/builtin/basis.rs @@ -4,15 +4,15 @@ use std::cmp::Ordering; * 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::{f32::consts::FRAC_PI_2, fmt::Display, ops::*}; +use std::{fmt::Display, ops::*}; use godot_ffi as sys; use sys::{ffi_methods, GodotFfi}; use super::glam_helpers::{GlamConv, GlamType}; +use super::real_consts::FRAC_PI_2; use super::{math::*, Quaternion, Vector3}; - -use glam; +use super::{real, RMat3, RQuat, RVec2, RVec3}; /// A 3x3 matrix, typically used as an orthogonal basis for [`Transform3D`](crate::builtin::Transform3D). /// @@ -67,12 +67,12 @@ impl Basis { /// Create a `Basis` from an axis and angle. /// /// _Godot equivalent: `Basis(Vector3 axis, float angle)`_ - pub fn from_axis_angle(axis: Vector3, angle: f32) -> Self { - glam::Mat3::from_axis_angle(axis.to_glam(), angle).to_front() + pub fn from_axis_angle(axis: Vector3, angle: real) -> Self { + RMat3::from_axis_angle(axis.to_glam(), angle).to_front() } /// Create a diagonal matrix from the given values. - pub const fn from_diagonal(x: f32, y: f32, z: f32) -> Self { + pub const fn from_diagonal(x: real, y: real, z: real) -> Self { Self { rows: [ Vector3::new(x, 0.0, 0.0), @@ -89,7 +89,7 @@ impl Basis { Self::from_diagonal(scale.x, scale.y, scale.z) } - const fn from_rows_array(rows: &[f32; 9]) -> Self { + const fn from_rows_array(rows: &[real; 9]) -> Self { let [ax, bx, cx, ay, by, cy, az, bz, cz] = rows; Self::from_rows( Vector3::new(*ax, *bx, *cx), @@ -102,7 +102,7 @@ impl Basis { /// /// _Godot equivalent: `Basis(Quaternion from)`_ pub fn from_quat(quat: Quaternion) -> Self { - glam::Mat3::from_quat(quat.to_glam()).to_front() + RMat3::from_quat(quat.to_glam()).to_front() } /// Create a `Basis` from three angles `a`, `b`, and `c` interpreted @@ -156,10 +156,10 @@ impl Basis { /// /// _Godot equivalent: `Basis()`_ pub fn to_quat(self) -> Quaternion { - glam::Quat::from_mat3(&self.orthonormalized().to_glam()).to_front() + RQuat::from_mat3(&self.orthonormalized().to_glam()).to_front() } - const fn to_rows_array(self) -> [f32; 9] { + const fn to_rows_array(self) -> [real; 9] { let [Vector3 { x: ax, y: bx, @@ -220,9 +220,9 @@ impl Basis { if let Some(pure_rotation) = match order { EulerOrder::XYZ => self .to_euler_pure_rotation(major, 1, row_a.zx()) - .map(glam::Vec3::yxz), + .map(RVec3::yxz), EulerOrder::YXZ => { - self.to_euler_pure_rotation(major, 0, glam::Vec2::new(-major, self.rows[1].y)) + self.to_euler_pure_rotation(major, 0, RVec2::new(-major, self.rows[1].y)) } _ => None, } { @@ -256,7 +256,7 @@ impl Basis { .to_front() } - fn is_between_neg1_1(f: f32) -> Ordering { + fn is_between_neg1_1(f: real) -> Ordering { if f >= (1.0 - CMP_EPSILON) { Ordering::Greater } else if f <= -(1.0 - CMP_EPSILON) { @@ -285,10 +285,10 @@ impl Basis { #[allow(clippy::wrong_self_convention)] fn to_euler_pure_rotation( &self, - major: f32, + major: real, index: usize, - rotation_vec: glam::Vec2, - ) -> Option { + rotation_vec: RVec2, + ) -> Option { if Self::is_between_neg1_1(major).is_ne() { return None; } @@ -297,37 +297,32 @@ impl Basis { return None; } - Some(glam::Vec3::new( - f32::atan2(rotation_vec.x, rotation_vec.y), + Some(RVec3::new( + real::atan2(rotation_vec.x, rotation_vec.y), 0.0, 0.0, )) } - fn to_euler_inner( - major: f32, - pair0: glam::Vec2, - pair1: glam::Vec2, - pair2: glam::Vec2, - ) -> glam::Vec3 { + fn to_euler_inner(major: real, pair0: RVec2, pair1: RVec2, pair2: RVec2) -> RVec3 { match Self::is_between_neg1_1(major) { // It's -1 - Ordering::Less => glam::Vec3::new(FRAC_PI_2, -f32::atan2(pair2.x, pair2.y), 0.0), + Ordering::Less => RVec3::new(FRAC_PI_2, -real::atan2(pair2.x, pair2.y), 0.0), // Is it a pure rotation? - Ordering::Equal => glam::Vec3::new( - f32::asin(-major), - f32::atan2(pair0.x, pair0.y), - f32::atan2(pair1.x, pair1.y), + Ordering::Equal => RVec3::new( + real::asin(-major), + real::atan2(pair0.x, pair0.y), + real::atan2(pair1.x, pair1.y), ), // It's 1 - Ordering::Greater => glam::Vec3::new(-FRAC_PI_2, -f32::atan2(pair2.x, pair2.y), 0.0), + Ordering::Greater => RVec3::new(-FRAC_PI_2, -real::atan2(pair2.x, pair2.y), 0.0), } } /// Returns the determinant of the matrix. /// /// _Godot equivalent: `Basis.determinant()`_ - pub fn determinant(&self) -> f32 { + pub fn determinant(&self) -> real { self.to_glam().determinant() } @@ -386,7 +381,7 @@ impl Basis { /// /// _Godot equivalent: `Basis.rotated()`_ #[must_use] - pub fn rotated(self, axis: Vector3, angle: f32) -> Self { + pub fn rotated(self, axis: Vector3, angle: real) -> Self { Self::from_axis_angle(axis, angle) * self } @@ -395,7 +390,7 @@ impl Basis { /// /// _Godot equivalent: `Basis.slerp()`_ #[must_use] - pub fn slerp(self, other: Self, weight: f32) -> Self { + pub fn slerp(self, other: Self, weight: real) -> Self { let from = self.to_quat(); let to = other.to_quat(); @@ -412,7 +407,7 @@ impl Basis { /// /// _Godot equivalent: `Basis.tdotx()`_ #[must_use] - pub fn tdotx(&self, with: Vector3) -> f32 { + pub fn tdotx(&self, with: Vector3) -> real { self.col_a().dot(with) } @@ -420,7 +415,7 @@ impl Basis { /// /// _Godot equivalent: `Basis.tdoty()`_ #[must_use] - pub fn tdoty(&self, with: Vector3) -> f32 { + pub fn tdoty(&self, with: Vector3) -> real { self.col_b().dot(with) } @@ -428,7 +423,7 @@ impl Basis { /// /// _Godot equivalent: `Basis.tdotz()`_ #[must_use] - pub fn tdotz(&self, with: Vector3) -> f32 { + pub fn tdotz(&self, with: Vector3) -> real { self.col_c().dot(with) } @@ -508,10 +503,10 @@ impl Display for Basis { } impl GlamConv for Basis { - type Glam = glam::Mat3; + type Glam = RMat3; } -impl GlamType for glam::Mat3 { +impl GlamType for RMat3 { type Mapped = Basis; fn to_front(&self) -> Self::Mapped { @@ -523,6 +518,7 @@ impl GlamType for glam::Mat3 { } } +#[cfg(not(feature = "double-precision"))] impl GlamType for glam::Mat3A { type Mapped = Basis; @@ -549,17 +545,17 @@ impl Mul for Basis { } } -impl Mul for Basis { +impl Mul for Basis { type Output = Self; - fn mul(mut self, rhs: f32) -> Self::Output { + fn mul(mut self, rhs: real) -> Self::Output { self *= rhs; self } } -impl MulAssign for Basis { - fn mul_assign(&mut self, rhs: f32) { +impl MulAssign for Basis { + fn mul_assign(&mut self, rhs: real) { self.rows[0] *= rhs; self.rows[1] *= rhs; self.rows[2] *= rhs; @@ -593,7 +589,7 @@ pub enum EulerOrder { #[cfg(test)] mod test { - use std::f32::consts::{FRAC_PI_2, PI}; + use crate::builtin::real_consts::{FRAC_PI_2, PI}; use crate::assert_eq_approx; @@ -799,7 +795,7 @@ mod test { #[test] fn basis_finite_number_test() { let x: Vector3 = Vector3::new(0.0, 1.0, 2.0); - let infinite: Vector3 = Vector3::new(f32::NAN, f32::NAN, f32::NAN); + let infinite: Vector3 = Vector3::new(real::NAN, real::NAN, real::NAN); assert!( Basis::from_cols(x, x, x).is_finite(), diff --git a/godot-core/src/builtin/glam_helpers.rs b/godot-core/src/builtin/glam_helpers.rs index bd43db123..1e0f2b852 100644 --- a/godot-core/src/builtin/glam_helpers.rs +++ b/godot-core/src/builtin/glam_helpers.rs @@ -15,6 +15,8 @@ // self.glam().dot(b.glam()) // GlamType::dot(self.glam(), b.glam()) +use super::real; + pub(crate) trait GlamConv { type Glam: GlamType; @@ -70,6 +72,6 @@ macro_rules! impl_glam_map_self { }; } -impl_glam_map_self!(f32); +impl_glam_map_self!(real); impl_glam_map_self!(bool); -impl_glam_map_self!((f32, f32, f32)); +impl_glam_map_self!((real, real, real)); diff --git a/godot-core/src/builtin/math.rs b/godot-core/src/builtin/math.rs index 8e52185ab..37942abaf 100644 --- a/godot-core/src/builtin/math.rs +++ b/godot-core/src/builtin/math.rs @@ -4,17 +4,18 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use std::f32::consts::TAU; +use super::real_consts::TAU; +use super::real; use super::Vector2; -pub const CMP_EPSILON: f32 = 0.00001; +pub const CMP_EPSILON: real = 0.00001; -pub fn lerp(a: f32, b: f32, t: f32) -> f32 { +pub fn lerp(a: real, b: real, t: real) -> real { a + ((b - a) * t) } -pub fn is_equal_approx(a: f32, b: f32) -> bool { +pub fn is_equal_approx(a: real, b: real) -> bool { if a == b { return true; } @@ -27,21 +28,23 @@ pub fn is_equal_approx(a: f32, b: f32) -> bool { /// Check if two angles are approximately equal, by comparing the distance /// between the points on the unit circle with 0 using [`is_equal_approx`]. -pub fn is_angle_equal_approx(a: f32, b: f32) -> bool { +pub fn is_angle_equal_approx(a: real, b: real) -> bool { let (x1, y1) = a.sin_cos(); let (x2, y2) = b.sin_cos(); + println!("({x1}, {y1}) ({x2}, {y2})"); + is_equal_approx( Vector2::distance_to(Vector2::new(x1, y1), Vector2::new(x2, y2)), 0.0, ) } -pub fn is_zero_approx(s: f32) -> bool { +pub fn is_zero_approx(s: real) -> bool { s.abs() < CMP_EPSILON } -pub fn fposmod(x: f32, y: f32) -> f32 { +pub fn fposmod(x: real, y: real) -> real { let mut value = x % y; if ((value < 0.0) && (y > 0.0)) || ((value > 0.0) && (y < 0.0)) { value += y; @@ -50,14 +53,14 @@ pub fn fposmod(x: f32, y: f32) -> f32 { value } -pub fn snapped(mut value: f32, step: f32) -> f32 { +pub fn snapped(mut value: real, step: real) -> real { if step != 0.0 { value = ((value / step + 0.5) * step).floor() } value } -pub fn sign(value: f32) -> f32 { +pub fn sign(value: real) -> real { if value == 0.0 { 0.0 } else if value < 0.0 { @@ -67,7 +70,13 @@ pub fn sign(value: f32) -> f32 { } } -pub fn bezier_derivative(start: f32, control_1: f32, control_2: f32, end: f32, t: f32) -> f32 { +pub fn bezier_derivative( + start: real, + control_1: real, + control_2: real, + end: real, + t: real, +) -> real { let omt = 1.0 - t; let omt2 = omt * omt; let t2 = t * t; @@ -76,7 +85,13 @@ pub fn bezier_derivative(start: f32, control_1: f32, control_2: f32, end: f32, t + (end - control_2) * 3.0 * t2 } -pub fn bezier_interpolate(start: f32, control_1: f32, control_2: f32, end: f32, t: f32) -> f32 { +pub fn bezier_interpolate( + start: real, + control_1: real, + control_2: real, + end: real, + t: real, +) -> real { let omt = 1.0 - t; let omt2 = omt * omt; let omt3 = omt2 * omt; @@ -85,7 +100,7 @@ pub fn bezier_interpolate(start: f32, control_1: f32, control_2: f32, end: f32, start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3 } -pub fn cubic_interpolate(from: f32, to: f32, pre: f32, post: f32, weight: f32) -> f32 { +pub fn cubic_interpolate(from: real, to: real, pre: real, post: real, weight: real) -> real { 0.5 * ((from * 2.0) + (-pre + to) * weight + (2.0 * pre - 5.0 * from + 4.0 * to - post) * (weight * weight) @@ -94,15 +109,15 @@ pub fn cubic_interpolate(from: f32, to: f32, pre: f32, post: f32, weight: f32) - #[allow(clippy::too_many_arguments)] pub fn cubic_interpolate_in_time( - from: f32, - to: f32, - pre: f32, - post: f32, - weight: f32, - to_t: f32, - pre_t: f32, - post_t: f32, -) -> f32 { + from: real, + to: real, + pre: real, + post: real, + weight: real, + to_t: real, + pre_t: real, + post_t: real, +) -> real { let t = lerp(0.0, to_t, weight); let a1 = lerp( pre, @@ -147,12 +162,12 @@ pub fn cubic_interpolate_in_time( /// Note: This function lerps through the shortest path between `from` and /// `to`. However, when these two angles are approximately `PI + k * TAU` apart /// for any integer `k`, it's not obvious which way they lerp due to -/// floating-point precision errors. For example, `lerp_angle(0.0, PI, weight)` -/// lerps clockwise, while `lerp_angle(0.0, PI + 3.0 * TAU, weight)` lerps -/// counter-clockwise. +/// floating-point precision errors. For example, with single-precision floats +/// `lerp_angle(0.0, PI, weight)` lerps clockwise, while `lerp_angle(0.0, PI + 3.0 * TAU, weight)` +/// lerps counter-clockwise. /// /// _Godot equivalent: @GlobalScope.lerp_angle()_ -pub fn lerp_angle(from: f32, to: f32, weight: f32) -> f32 { +pub fn lerp_angle(from: real, to: real, weight: real) -> real { let difference = (to - from) % TAU; let distance = (2.0 * difference) % TAU - difference; from + distance * weight @@ -192,7 +207,7 @@ macro_rules! assert_ne_approx { #[cfg(test)] mod test { - use std::f32::consts::{FRAC_PI_2, PI}; + use crate::builtin::real_consts::{FRAC_PI_2, PI}; use super::*; @@ -222,11 +237,23 @@ mod test { #[test] fn lerp_angle_test() { assert_eq_approx!(lerp_angle(0.0, PI, 0.5), -FRAC_PI_2, is_angle_equal_approx); + // As mentioned in the docs for `lerp_angle`, direction can be unpredictable + // when lerping towards PI radians, this also means it's different for single vs + // double precision floats. + + // TODO: look into if it's possible to make a more robust impl. + #[cfg(not(feature = "double-precision"))] assert_eq_approx!( lerp_angle(0.0, PI + 3.0 * TAU, 0.5), FRAC_PI_2, is_angle_equal_approx ); + #[cfg(feature = "double-precision")] + assert_eq_approx!( + lerp_angle(0.0, PI + 3.0 * TAU, 0.5), + -FRAC_PI_2, + is_angle_equal_approx + ); let angle = PI * 2.0 / 3.0; assert_eq_approx!( lerp_angle(-5.0 * TAU, angle + 3.0 * TAU, 0.5), diff --git a/godot-core/src/builtin/mod.rs b/godot-core/src/builtin/mod.rs index c407881d7..a3581bbff 100644 --- a/godot-core/src/builtin/mod.rs +++ b/godot-core/src/builtin/mod.rs @@ -128,3 +128,190 @@ pub(crate) fn u8_to_bool(u: u8) -> bool { _ => panic!("Invalid boolean value {u}"), } } + +/// Clippy often complains if you do `f as f64` when `f` is already an `f64`. This trait exists to make it easy to +/// convert between the different reals and floats without a lot of allowing clippy lints for your code. +pub trait RealConv { + /// Cast this [`real`] to an [`f32`] using `as`. + // Clippy complains that this is an `as_*` function but it takes a `self` + // however, since this uses `as` internally it makes much more sense for + // it to be named `as_f32` rather than `to_f32`. + #[allow(clippy::wrong_self_convention)] + fn as_f32(self) -> f32; + + /// Cast this [`real`] to an [`f64`] using `as`. + // Clippy complains that this is an `as_*` function but it takes a `self` + // however, since this uses `as` internally it makes much more sense for + // it to be named `as_f64` rather than `to_f64`. + #[allow(clippy::wrong_self_convention)] + fn as_f64(self) -> f64; + + /// Cast an [`f32`] to a [`real`] using `as`. + fn from_f32(f: f32) -> Self; + + /// Cast an [`f64`] to a [`real`] using `as`. + fn from_f64(f: f64) -> Self; +} + +#[cfg(not(feature = "double-precision"))] +mod real_mod { + //! Definitions for single-precision `real`. + + /// Floating point type used for many structs and functions in Godot. + /// + /// This is not the `float` type in GDScript; that type is always 64-bits. Rather, many structs in Godot may use + /// either 32-bit or 64-bit floats such as [`Vector2`](super::Vector2). To convert between [`real`] and [`f32`] or + /// [`f64`] see [`RealConv`](super::RealConv). + /// + /// See also the [Godot docs on float](https://docs.godotengine.org/en/stable/classes/class_float.html). + /// + /// _Godot equivalent: `real_t`_ + // As this is a scalar value, we will use a non-standard type name. + #[allow(non_camel_case_types)] + pub type real = f32; + + impl super::RealConv for real { + #[inline] + fn as_f32(self) -> f32 { + self + } + + #[inline] + fn as_f64(self) -> f64 { + self as f64 + } + + #[inline] + fn from_f32(f: f32) -> Self { + f + } + + #[inline] + fn from_f64(f: f64) -> Self { + f as f32 + } + } + + pub use std::f32::consts; + + /// A 2-dimensional vector from [`glam`]. Using a floating-point format compatible with [`real`]. + pub type RVec2 = glam::Vec2; + /// A 3-dimensional vector from [`glam`]. Using a floating-point format compatible with [`real`]. + pub type RVec3 = glam::Vec3; + /// A 4-dimensional vector from [`glam`]. Using a floating-point format compatible with [`real`]. + pub type RVec4 = glam::Vec4; + + /// A 2x2 column-major matrix from [`glam`]. Using a floating-point format compatible with [`real`]. + pub type RMat2 = glam::Mat2; + /// A 3x3 column-major matrix from [`glam`]. Using a floating-point format compatible with [`real`]. + pub type RMat3 = glam::Mat3; + /// A 4x4 column-major matrix from [`glam`]. Using a floating-point format compatible with [`real`]. + pub type RMat4 = glam::Mat4; + + /// A matrix from [`glam`] quaternion representing an orientation. Using a floating-point format compatible + /// with [`real`]. + pub type RQuat = glam::Quat; + + /// A 2D affine transform from [`glam`], which can represent translation, rotation, scaling and + /// shear. Using a floating-point format compatible with [`real`]. + pub type RAffine2 = glam::Affine2; + /// A 3D affine transform from [`glam`], which can represent translation, rotation, scaling and + /// shear. Using a floating-point format compatible with [`real`]. + pub type RAffine3 = glam::Affine3A; +} + +#[cfg(feature = "double-precision")] +mod real_mod { + //! Definitions for double-precision `real`. + + /// Floating point type used for many structs and functions in Godot. + /// + /// This is not the `float` type in GDScript; that type is always 64-bits. Rather, many structs in Godot may use + /// either 32-bit or 64-bit floats such as [`Vector2`](super::Vector2). To convert between [`real`] and [`f32`] or + /// [`f64`] see [`RealConv`](super::RealConv). + /// + /// See also the [Godot docs on float](https://docs.godotengine.org/en/stable/classes/class_float.html). + /// + /// _Godot equivalent: `real_t`_ + // As this is a scalar value, we will use a non-standard type name. + #[allow(non_camel_case_types)] + pub type real = f64; + + impl super::RealConv for real { + #[inline] + fn as_f32(self) -> f32 { + self as f32 + } + + #[inline] + fn as_f64(self) -> f64 { + self + } + + #[inline] + fn from_f32(f: f32) -> Self { + f as f64 + } + + #[inline] + fn from_f64(f: f64) -> Self { + f + } + } + + pub use std::f64::consts; + + /// A 2-dimensional vector from [`glam`]. Using a floating-point format compatible with [`real`]. + pub type RVec2 = glam::DVec2; + /// A 3-dimensional vector from [`glam`]. Using a floating-point format compatible with [`real`]. + pub type RVec3 = glam::DVec3; + /// A 4-dimensional vector from [`glam`]. Using a floating-point format compatible with [`real`]. + pub type RVec4 = glam::DVec4; + + /// A 2x2 column-major matrix from [`glam`]. Using a floating-point format compatible with [`real`]. + pub type RMat2 = glam::DMat2; + /// A 3x3 column-major matrix from [`glam`]. Using a floating-point format compatible with [`real`]. + pub type RMat3 = glam::DMat3; + /// A 4x4 column-major matrix from [`glam`]. Using a floating-point format compatible with [`real`]. + pub type RMat4 = glam::DMat4; + + /// A matrix from [`glam`] quaternion representing an orientation. Using a floating-point format + /// compatible with [`real`]. + pub type RQuat = glam::DQuat; + + /// A 2D affine transform from [`glam`], which can represent translation, rotation, scaling and + /// shear. Using a floating-point format compatible with [`real`]. + pub type RAffine2 = glam::DAffine2; + /// A 3D affine transform from [`glam`], which can represent translation, rotation, scaling and + /// shear. Using a floating-point format compatible with [`real`]. + pub type RAffine3 = glam::DAffine3; +} + +pub use crate::real; +pub(crate) use real_mod::*; +pub use real_mod::{consts as real_consts, real}; + +/// A macro to coerce float-literals into the real type. Mainly used where +/// you'd normally use a suffix to specity the type, such as `115.0f32`. +/// +/// ### Examples +/// Rust will not know how to infer the type of this call to `to_radians`: +/// ```compile_fail +/// use godot_core::builtin::real; +/// +/// let radians: real = 115.0.to_radians(); +/// ``` +/// But we can't add a suffix to the literal, since it may be either `f32` or +/// `f64` depending on the context. So instead we use our macro: +/// ``` +/// use godot_core::builtin::real; +/// +/// let radians: real = godot_core::real!(115.0).to_radians(); +/// ``` +#[macro_export] +macro_rules! real { + ($f:literal) => {{ + let f: $crate::builtin::real = $f; + f + }}; +} diff --git a/godot-core/src/builtin/packed_array.rs b/godot-core/src/builtin/packed_array.rs index 52d39750a..c731cd254 100644 --- a/godot-core/src/builtin/packed_array.rs +++ b/godot-core/src/builtin/packed_array.rs @@ -305,8 +305,8 @@ macro_rules! impl_packed_array { } /// Converts an `$Element` into a value that can be passed into API functions. For most - /// types, this is a no-op. But `u8` and `i32` are widened to `i64`, and `f32` is - /// widened to `f64`. + /// types, this is a no-op. But `u8` and `i32` are widened to `i64`, and `real` is + /// widened to `f64` if it is an `f32`. #[inline] fn into_arg(e: $Element) -> $Arg { e.into() diff --git a/godot-core/src/builtin/projection.rs b/godot-core/src/builtin/projection.rs index 92ee2d5f4..149ac76a9 100644 --- a/godot-core/src/builtin/projection.rs +++ b/godot-core/src/builtin/projection.rs @@ -10,8 +10,8 @@ use sys::{ffi_methods, GodotFfi}; use super::glam_helpers::{GlamConv, GlamType}; use super::{inner::InnerProjection, Plane, Transform3D, Vector2, Vector4}; +use super::{real, RMat4, RealConv}; -use glam; /// A 4x4 matrix used for 3D projective transformations. It can represent /// transformations such as translation, rotation, scaling, shearing, and /// perspective division. It consists of four Vector4 columns. @@ -50,7 +50,7 @@ impl Projection { } /// Create a diagonal matrix from the given values. - pub const fn from_diagonal(x: f32, y: f32, z: f32, w: f32) -> Self { + pub const fn from_diagonal(x: real, y: real, z: real, w: real) -> Self { Self::from_cols( Vector4::new(x, 0.0, 0.0, 0.0), Vector4::new(0.0, y, 0.0, 0.0), @@ -83,23 +83,23 @@ impl Projection { #[allow(clippy::too_many_arguments)] pub fn create_for_hmd( eye: ProjectionEye, - aspect: f64, - intraocular_dist: f64, - display_width: f64, - display_to_lens: f64, - oversample: f64, - near: f64, - far: f64, + aspect: real, + intraocular_dist: real, + display_width: real, + display_to_lens: real, + oversample: real, + near: real, + far: real, ) -> Self { InnerProjection::create_for_hmd( eye as i64, - aspect, - intraocular_dist, - display_width, - display_to_lens, - oversample, - near, - far, + aspect.as_f64(), + intraocular_dist.as_f64(), + display_width.as_f64(), + display_to_lens.as_f64(), + oversample.as_f64(), + near.as_f64(), + far.as_f64(), ) } @@ -108,14 +108,21 @@ impl Projection { /// /// _Godot equivalent: Projection.create_frustum()_ pub fn create_frustum( - left: f64, - right: f64, - bottom: f64, - top: f64, - near: f64, - far: f64, + left: real, + right: real, + bottom: real, + top: real, + near: real, + far: real, ) -> Self { - InnerProjection::create_frustum(left, right, bottom, top, near, far) + InnerProjection::create_frustum( + left.as_f64(), + right.as_f64(), + bottom.as_f64(), + top.as_f64(), + near.as_f64(), + far.as_f64(), + ) } /// Creates a new Projection that projects positions in a frustum with the @@ -126,14 +133,21 @@ impl Projection { /// /// _Godot equivalent: Projection.create_frustum_aspect()_ pub fn create_frustum_aspect( - size: f64, - aspect: f64, + size: real, + aspect: real, offset: Vector2, - near: f64, - far: f64, + near: real, + far: real, flip_fov: bool, ) -> Self { - InnerProjection::create_frustum_aspect(size, aspect, offset, near, far, flip_fov) + InnerProjection::create_frustum_aspect( + size.as_f64(), + aspect.as_f64(), + offset, + near.as_f64(), + far.as_f64(), + flip_fov, + ) } /// Creates a new Projection that projects positions using an orthogonal @@ -141,14 +155,21 @@ impl Projection { /// /// _Godot equivalent: Projection.create_orthogonal()_ pub fn create_orthogonal( - left: f64, - right: f64, - bottom: f64, - top: f64, - near: f64, - far: f64, + left: real, + right: real, + bottom: real, + top: real, + near: real, + far: real, ) -> Self { - InnerProjection::create_orthogonal(left, right, bottom, top, near, far) + InnerProjection::create_orthogonal( + left.as_f64(), + right.as_f64(), + bottom.as_f64(), + top.as_f64(), + near.as_f64(), + far.as_f64(), + ) } /// Creates a new Projection that projects positions using an orthogonal @@ -159,13 +180,19 @@ impl Projection { /// /// _Godot equivalent: Projection.create_orthogonal_aspect()_ pub fn create_orthogonal_aspect( - size: f64, - aspect: f64, - near: f64, - far: f64, + size: real, + aspect: real, + near: real, + far: real, flip_fov: bool, ) -> Self { - InnerProjection::create_orthogonal_aspect(size, aspect, near, far, flip_fov) + InnerProjection::create_orthogonal_aspect( + size.as_f64(), + aspect.as_f64(), + near.as_f64(), + far.as_f64(), + flip_fov, + ) } /// Creates a new Projection that projects positions using a perspective @@ -177,13 +204,19 @@ impl Projection { /// /// _Godot equivalent: Projection.create_perspective()_ pub fn create_perspective( - fov_y: f64, - aspect: f64, - near: f64, - far: f64, + fov_y: real, + aspect: real, + near: real, + far: real, flip_fov: bool, ) -> Self { - InnerProjection::create_perspective(fov_y, aspect, near, far, flip_fov) + InnerProjection::create_perspective( + fov_y.as_f64(), + aspect.as_f64(), + near.as_f64(), + far.as_f64(), + flip_fov, + ) } /// Creates a new Projection that projects positions using a perspective @@ -198,32 +231,32 @@ impl Projection { /// _Godot equivalent: Projection.create_perspective_hmd()_ #[allow(clippy::too_many_arguments)] pub fn create_perspective_hmd( - fov_y: f64, - aspect: f64, - near: f64, - far: f64, + fov_y: real, + aspect: real, + near: real, + far: real, flip_fov: bool, eye: ProjectionEye, - intraocular_dist: f64, - convergence_dist: f64, + intraocular_dist: real, + convergence_dist: real, ) -> Self { InnerProjection::create_perspective_hmd( - fov_y, - aspect, - near, - far, + fov_y.as_f64(), + aspect.as_f64(), + near.as_f64(), + far.as_f64(), flip_fov, eye as i64, - intraocular_dist, - convergence_dist, + intraocular_dist.as_f64(), + convergence_dist.as_f64(), ) } /// Return the determinant of the matrix. /// /// _Godot equivalent: Projection.determinant()_ - pub fn determinant(&self) -> f64 { - self.glam(|mat| mat.determinant()) as f64 + pub fn determinant(&self) -> real { + self.glam(|mat| mat.determinant()) } /// Returns a copy of this Projection with the signs of the values of the Y @@ -238,8 +271,8 @@ impl Projection { /// Returns the X:Y aspect ratio of this Projection's viewport. /// /// _Godot equivalent: Projection.get_aspect()_ - pub fn aspect(&self) -> f64 { - self.as_inner().get_aspect() + pub fn aspect(&self) -> real { + real::from_f64(self.as_inner().get_aspect()) } /// Returns the dimensions of the far clipping plane of the projection, @@ -253,24 +286,24 @@ impl Projection { /// Returns the horizontal field of view of the projection (in degrees). /// /// _Godot equivalent: Projection.get_fov()_ - pub fn fov(&self) -> f64 { - self.as_inner().get_fov() + pub fn fov(&self) -> real { + real::from_f64(self.as_inner().get_fov()) } /// Returns the vertical field of view of a projection (in degrees) which /// has the given horizontal field of view (in degrees) and aspect ratio. /// /// _Godot equivalent: Projection.get_fovy()_ - pub fn fovy_of(fov_x: f64, aspect: f64) -> f64 { - InnerProjection::get_fovy(fov_x, aspect) + pub fn fovy_of(fov_x: real, aspect: real) -> real { + real::from_f64(InnerProjection::get_fovy(fov_x.as_f64(), aspect.as_f64())) } /// Returns the factor by which the visible level of detail is scaled by /// this Projection. /// /// _Godot equivalent: Projection.get_lod_multiplier()_ - pub fn lod_multiplier(&self) -> f64 { - self.as_inner().get_lod_multiplier() + pub fn lod_multiplier(&self) -> real { + real::from_f64(self.as_inner().get_lod_multiplier()) } /// Returns the number of pixels with the given pixel width displayed per @@ -301,16 +334,16 @@ impl Projection { /// clipped. /// /// _Godot equivalent: Projection.get_z_far()_ - pub fn z_far(&self) -> f64 { - self.as_inner().get_z_far() + pub fn z_far(&self) -> real { + real::from_f64(self.as_inner().get_z_far()) } /// Returns the distance for this Projection before which positions are /// clipped. /// /// _Godot equivalent: Projection.get_z_near()_ - pub fn z_near(&self) -> f64 { - self.as_inner().get_z_near() + pub fn z_near(&self) -> real { + real::from_f64(self.as_inner().get_z_near()) } /// Returns a Projection that performs the inverse of this Projection's @@ -343,8 +376,9 @@ impl Projection { /// Note: The original Projection must be a perspective projection. /// /// _Godot equivalent: Projection.perspective_znear_adjusted()_ - pub fn perspective_znear_adjusted(&self, new_znear: f64) -> Self { - self.as_inner().perspective_znear_adjusted(new_znear) + pub fn perspective_znear_adjusted(&self, new_znear: real) -> Self { + self.as_inner() + .perspective_znear_adjusted(new_znear.as_f64()) } #[doc(hidden)] @@ -355,7 +389,7 @@ impl Projection { impl From for Projection { fn from(trans: Transform3D) -> Self { - trans.glam(glam::Mat4::from) + trans.glam(RMat4::from) } } @@ -381,7 +415,7 @@ impl Mul for Projection { } } -impl GlamType for glam::Mat4 { +impl GlamType for RMat4 { type Mapped = Projection; fn to_front(&self) -> Self::Mapped { @@ -399,7 +433,7 @@ impl GlamType for glam::Mat4 { } impl GlamConv for Projection { - type Glam = glam::Mat4; + type Glam = RMat4; } impl GodotFfi for Projection { diff --git a/godot-core/src/builtin/quaternion.rs b/godot-core/src/builtin/quaternion.rs index 598a875e2..0de222d0c 100644 --- a/godot-core/src/builtin/quaternion.rs +++ b/godot-core/src/builtin/quaternion.rs @@ -11,23 +11,24 @@ use sys::{ffi_methods, GodotFfi}; use crate::builtin::glam_helpers::{GlamConv, GlamType}; use crate::builtin::{inner, math::*, vector3::*}; +use super::{real, RQuat}; use super::{Basis, EulerOrder}; #[derive(Copy, Clone, PartialEq, Debug)] #[repr(C)] pub struct Quaternion { - pub x: f32, - pub y: f32, - pub z: f32, - pub w: f32, + pub x: real, + pub y: real, + pub z: real, + pub w: real, } impl Quaternion { - pub fn new(x: f32, y: f32, z: f32, w: f32) -> Self { + pub fn new(x: real, y: real, z: real, w: real) -> Self { Self { x, y, z, w } } - pub fn from_angle_axis(axis: Vector3, angle: f32) -> Self { + pub fn from_angle_axis(axis: Vector3, angle: real) -> Self { let d = axis.length(); if d == 0.0 { Self::new(0.0, 0.0, 0.0, 0.0) @@ -43,12 +44,12 @@ impl Quaternion { } } - pub fn angle_to(self, to: Self) -> f32 { - self.glam2(&to, glam::f32::Quat::angle_between) + pub fn angle_to(self, to: Self) -> real { + self.glam2(&to, RQuat::angle_between) } - pub fn dot(self, with: Self) -> f32 { - self.glam2(&with, glam::f32::Quat::dot) + pub fn dot(self, with: Self) -> real { + self.glam2(&with, RQuat::dot) } pub fn to_exp(self) -> Self { @@ -82,7 +83,7 @@ impl Quaternion { ) } - pub fn get_angle(self) -> f32 { + pub fn get_angle(self) -> real { 2.0 * self.w.acos() } @@ -121,11 +122,11 @@ impl Quaternion { is_equal_approx(self.length_squared(), 1.0) } - pub fn length(self) -> f32 { + pub fn length(self) -> real { self.length_squared().sqrt() } - pub fn length_squared(self) -> f32 { + pub fn length_squared(self) -> real { self.dot(self) } @@ -138,13 +139,13 @@ impl Quaternion { self / self.length() } - pub fn slerp(self, to: Self, weight: f32) -> Self { + pub fn slerp(self, to: Self, weight: real) -> Self { let mut cosom = self.dot(to); let to1: Self; - let omega: f32; - let sinom: f32; - let scale0: f32; - let scale1: f32; + let omega: real; + let sinom: real; + let scale0: real; + let scale1: real; if cosom < 0.0 { cosom = -cosom; to1 = -to; @@ -165,7 +166,7 @@ impl Quaternion { scale0 * self + scale1 * to1 } - pub fn slerpni(self, to: Self, weight: f32) -> Self { + pub fn slerpni(self, to: Self, weight: real) -> Self { let dot = self.dot(to); if dot.abs() > 0.9999 { return self; @@ -178,7 +179,7 @@ impl Quaternion { inv_factor * self + new_factor * to } - // pub fn spherical_cubic_interpolate(self, b: Self, pre_a: Self, post_b: Self, weight: f32) -> Self {} + // pub fn spherical_cubic_interpolate(self, b: Self, pre_a: Self, post_b: Self, weight: real) -> Self {} // TODO: Implement godot's function in rust /* pub fn spherical_cubic_interpolate_in_time( @@ -186,10 +187,10 @@ impl Quaternion { b: Self, pre_a: Self, post_b: Self, - weight: f32, - b_t: f32, - pre_a_t: f32, - post_b_t: f32, + weight: real, + b_t: real, + pre_a_t: real, + post_b_t: real, ) -> Self { } */ @@ -242,7 +243,7 @@ impl Mul for Quaternion { type Output = Self; fn mul(self, other: Quaternion) -> Self { - // TODO use glam? + // TODO use super::glam? let x = self.w * other.x + self.x * other.w + self.y * other.z - self.z * other.y; let y = self.w * other.y + self.y * other.w + self.z * other.x - self.x * other.z; @@ -270,10 +271,10 @@ impl Default for Quaternion { } impl GlamConv for Quaternion { - type Glam = glam::f32::Quat; + type Glam = RQuat; } -impl GlamType for glam::f32::Quat { +impl GlamType for RQuat { type Mapped = Quaternion; fn to_front(&self) -> Self::Mapped { @@ -281,7 +282,7 @@ impl GlamType for glam::f32::Quat { } fn from_front(mapped: &Self::Mapped) -> Self { - glam::f32::Quat::from_xyzw(mapped.x, mapped.y, mapped.z, mapped.w) + RQuat::from_xyzw(mapped.x, mapped.y, mapped.z, mapped.w) } } @@ -291,10 +292,10 @@ impl MulAssign for Quaternion { } } -impl Mul for Quaternion { +impl Mul for Quaternion { type Output = Self; - fn mul(self, other: f32) -> Self { + fn mul(self, other: real) -> Self { Quaternion::new( self.x * other, self.y * other, @@ -304,7 +305,7 @@ impl Mul for Quaternion { } } -impl Mul for f32 { +impl Mul for real { type Output = Quaternion; fn mul(self, other: Quaternion) -> Quaternion { @@ -312,16 +313,16 @@ impl Mul for f32 { } } -impl MulAssign for Quaternion { - fn mul_assign(&mut self, other: f32) { +impl MulAssign for Quaternion { + fn mul_assign(&mut self, other: real) { *self = *self * other } } -impl Div for Quaternion { +impl Div for Quaternion { type Output = Self; - fn div(self, other: f32) -> Self { + fn div(self, other: real) -> Self { Self::new( self.x / other, self.y / other, @@ -331,8 +332,8 @@ impl Div for Quaternion { } } -impl DivAssign for Quaternion { - fn div_assign(&mut self, other: f32) { +impl DivAssign for Quaternion { + fn div_assign(&mut self, other: real) { *self = *self / other } } diff --git a/godot-core/src/builtin/transform2d.rs b/godot-core/src/builtin/transform2d.rs index 9161ffea4..d37589d84 100644 --- a/godot-core/src/builtin/transform2d.rs +++ b/godot-core/src/builtin/transform2d.rs @@ -3,7 +3,7 @@ * 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::{f32::consts::PI, fmt::Display, ops::*}; +use std::{fmt::Display, ops::*}; use godot_ffi as sys; use sys::{ffi_methods, GodotFfi}; @@ -11,7 +11,8 @@ use sys::{ffi_methods, GodotFfi}; use super::glam_helpers::{GlamConv, GlamType}; use super::{math::*, Vector2}; -use glam; +use super::real_consts::PI; +use super::{real, RAffine2, RMat2}; /// Affine 2D transform (2x3 matrix). /// @@ -76,7 +77,7 @@ impl Transform2D { } /// Create a new `Transform2D` which will rotate by the given angle. - pub fn from_angle(angle: f32) -> Self { + pub fn from_angle(angle: real) -> Self { Self::from_angle_origin(angle, Vector2::ZERO) } @@ -84,7 +85,7 @@ impl Transform2D { /// by `origin`. /// /// _Godot equivalent: `Transform2D(float rotation, Vector2 position)`_ - pub fn from_angle_origin(angle: f32, origin: Vector2) -> Self { + pub fn from_angle_origin(angle: real, origin: Vector2) -> Self { Self::from_basis_origin(Basis2D::from_angle(angle), origin) } @@ -93,9 +94,9 @@ impl Transform2D { /// /// _Godot equivalent: `Transform2D(float rotation, Vector2 scale, float skew, Vector2 position)`_ pub fn from_angle_scale_skew_origin( - angle: f32, + angle: real, scale: Vector2, - skew: f32, + skew: real, origin: Vector2, ) -> Self { // Translated from Godot's implementation @@ -136,7 +137,7 @@ impl Transform2D { /// Returns the transform's rotation (in radians). /// /// _Godot equivalent: `Transform2D.get_rotation()`_ - pub fn rotation(&self) -> f32 { + pub fn rotation(&self) -> real { self.basis().rotation() } @@ -152,7 +153,7 @@ impl Transform2D { /// /// _Godot equivalent: `Transform2D.get_skew()`_ #[must_use] - pub fn skew(&self) -> f32 { + pub fn skew(&self) -> real { self.basis().skew() } @@ -161,7 +162,7 @@ impl Transform2D { /// /// _Godot equivalent: `Transform2D.interpolate_with()`_ #[must_use] - pub fn interpolate_with(self, other: Self, weight: f32) -> Self { + pub fn interpolate_with(self, other: Self, weight: real) -> Self { Self::from_angle_scale_skew_origin( lerp_angle(self.rotation(), other.rotation(), weight), self.scale().lerp(other.scale(), weight), @@ -204,7 +205,7 @@ impl Transform2D { /// /// _Godot equivalent: `Transform2D.rotated()`_ #[must_use] - pub fn rotated(self, angle: f32) -> Self { + pub fn rotated(self, angle: real) -> Self { Self::from_angle(angle) * self } @@ -215,7 +216,7 @@ impl Transform2D { /// /// _Godot equivalent: `Transform2D.rotated_local()`_ #[must_use] - pub fn rotated_local(self, angle: f32) -> Self { + pub fn rotated_local(self, angle: real) -> Self { self * Self::from_angle(angle) } @@ -311,15 +312,15 @@ impl Mul for Transform2D { } } -impl Mul for Transform2D { +impl Mul for Transform2D { type Output = Self; - fn mul(self, rhs: f32) -> Self::Output { + fn mul(self, rhs: real) -> Self::Output { Self::from_cols(self.a * rhs, self.b * rhs, self.origin * rhs) } } -impl GlamType for glam::Affine2 { +impl GlamType for RAffine2 { type Mapped = Transform2D; fn to_front(&self) -> Self::Mapped { @@ -335,7 +336,7 @@ impl GlamType for glam::Affine2 { } impl GlamConv for Transform2D { - type Glam = glam::Affine2; + type Glam = RAffine2; } impl GodotFfi for Transform2D { @@ -369,7 +370,7 @@ impl Basis2D { pub(crate) const FLIP_Y: Self = Self::from_diagonal(1.0, -1.0); /// Create a diagonal matrix from the given values. - pub(crate) const fn from_diagonal(x: f32, y: f32) -> Self { + pub(crate) const fn from_diagonal(x: real, y: real) -> Self { Self::from_cols(Vector2::new(x, 0.0), Vector2::new(0.0, y)) } @@ -379,8 +380,8 @@ impl Basis2D { } /// Create a `Basis2D` from an angle. - pub(crate) fn from_angle(angle: f32) -> Self { - glam::Mat2::from_angle(angle).to_front() + pub(crate) fn from_angle(angle: real) -> Self { + RMat2::from_angle(angle).to_front() } /// Returns the scale of the matrix. @@ -399,7 +400,7 @@ impl Basis2D { } /// Returns the determinant of the matrix. - pub(crate) fn determinant(&self) -> f32 { + pub(crate) fn determinant(&self) -> real { self.glam(|mat| mat.determinant()) } @@ -429,14 +430,14 @@ impl Basis2D { } /// Returns the rotation of the matrix - pub(crate) fn rotation(&self) -> f32 { + pub(crate) fn rotation(&self) -> real { // Translated from Godot - f32::atan2(self.cols[0].y, self.cols[0].x) + real::atan2(self.cols[0].y, self.cols[0].x) } /// Returns the skew of the matrix #[must_use] - pub(crate) fn skew(&self) -> f32 { + pub(crate) fn skew(&self) -> real { // Translated from Godot let det_sign = self.determinant().signum(); self.cols[0] @@ -487,16 +488,16 @@ impl Mul for Basis2D { } } -impl Mul for Basis2D { +impl Mul for Basis2D { type Output = Self; - fn mul(self, rhs: f32) -> Self::Output { + fn mul(self, rhs: real) -> Self::Output { (self.to_glam() * rhs).to_front() } } -impl MulAssign for Basis2D { - fn mul_assign(&mut self, rhs: f32) { +impl MulAssign for Basis2D { + fn mul_assign(&mut self, rhs: real) { self.cols[0] *= rhs; self.cols[1] *= rhs; } @@ -510,7 +511,7 @@ impl Mul for Basis2D { } } -impl GlamType for glam::Mat2 { +impl GlamType for RMat2 { type Mapped = Basis2D; fn to_front(&self) -> Self::Mapped { @@ -525,7 +526,7 @@ impl GlamType for glam::Mat2 { } impl GlamConv for Basis2D { - type Glam = glam::Mat2; + type Glam = RMat2; } #[cfg(test)] @@ -536,11 +537,12 @@ mod test { #[test] fn transform2d_constructors_correct() { - let trans = Transform2D::from_angle(115.0f32.to_radians()); - assert_eq_approx!(trans.rotation(), 115.0f32.to_radians(), is_equal_approx); + let trans = Transform2D::from_angle(real!(115.0).to_radians()); + assert_eq_approx!(trans.rotation(), real!(115.0).to_radians(), is_equal_approx); - let trans = Transform2D::from_angle_origin((-80.0f32).to_radians(), Vector2::new(1.4, 9.8)); - assert_eq_approx!(trans.rotation(), (-80.0f32).to_radians(), is_equal_approx); + let trans = + Transform2D::from_angle_origin(real!(-80.0).to_radians(), Vector2::new(1.4, 9.8)); + assert_eq_approx!(trans.rotation(), real!(-80.0).to_radians(), is_equal_approx); assert_eq_approx!( trans.origin, Vector2::new(1.4, 9.8), @@ -548,18 +550,18 @@ mod test { ); let trans = Transform2D::from_angle_scale_skew_origin( - 170.0f32.to_radians(), + real!(170.0).to_radians(), Vector2::new(3.6, 8.0), - 20.0f32.to_radians(), + real!(20.0).to_radians(), Vector2::new(2.4, 6.8), ); - assert_eq_approx!(trans.rotation(), 170.0f32.to_radians(), is_equal_approx); + assert_eq_approx!(trans.rotation(), real!(170.0).to_radians(), is_equal_approx); assert_eq_approx!( trans.scale(), Vector2::new(3.6, 8.0), Vector2::is_equal_approx ); - assert_eq_approx!(trans.skew(), 20.0f32.to_radians(), is_equal_approx); + assert_eq_approx!(trans.skew(), real!(20.0).to_radians(), is_equal_approx); assert_eq_approx!( trans.origin, Vector2::new(2.4, 6.8), @@ -628,16 +630,16 @@ mod test { #[test] fn interpolation() { let rotate_scale_skew_pos: Transform2D = Transform2D::from_angle_scale_skew_origin( - 170.0f32.to_radians(), + real!(170.0).to_radians(), Vector2::new(3.6, 8.0), - 20.0f32.to_radians(), + real!(20.0).to_radians(), Vector2::new(2.4, 6.8), ); let rotate_scale_skew_pos_halfway: Transform2D = Transform2D::from_angle_scale_skew_origin( - 85.0f32.to_radians(), + real!(85.0).to_radians(), Vector2::new(2.3, 4.5), - 10.0f32.to_radians(), + real!(10.0).to_radians(), Vector2::new(1.2, 3.4), ); let interpolated: Transform2D = @@ -678,7 +680,7 @@ mod test { #[test] fn finite_number_checks() { let x: Vector2 = Vector2::new(0.0, 1.0); - let infinite: Vector2 = Vector2::new(f32::NAN, f32::NAN); + let infinite: Vector2 = Vector2::new(real::NAN, real::NAN); assert!( Transform2D::from_basis_origin(Basis2D::from_cols(x, x), x).is_finite(), diff --git a/godot-core/src/builtin/transform3d.rs b/godot-core/src/builtin/transform3d.rs index 660813e31..476d3ef2e 100644 --- a/godot-core/src/builtin/transform3d.rs +++ b/godot-core/src/builtin/transform3d.rs @@ -9,10 +9,9 @@ use godot_ffi as sys; use sys::{ffi_methods, GodotFfi}; use super::glam_helpers::{GlamConv, GlamType}; +use super::{real, RAffine3}; use super::{Basis, Projection, Vector3}; -use glam; - /// Affine 3D transform (3x4 matrix). /// /// Used for 3D linear transformations. Uses a basis + origin representation. @@ -105,7 +104,7 @@ impl Transform3D { /// /// _Godot equivalent: Transform3D.interpolate_with()_ #[must_use] - pub fn interpolate_with(self, other: Self, weight: f32) -> Self { + pub fn interpolate_with(self, other: Self, weight: real) -> Self { let src_scale = self.basis.scale(); let src_rot = self.basis.to_quat().normalized(); let src_loc = self.origin; @@ -172,7 +171,7 @@ impl Transform3D { /// /// _Godot equivalent: `Transform2D.rotated()`_ #[must_use] - pub fn rotated(self, axis: Vector3, angle: f32) -> Self { + pub fn rotated(self, axis: Vector3, angle: real) -> Self { let rotation = Basis::from_axis_angle(axis, angle); Self { basis: rotation * self.basis, @@ -186,7 +185,7 @@ impl Transform3D { /// /// _Godot equivalent: `Transform2D.rotated_local()`_ #[must_use] - pub fn rotated_local(self, axis: Vector3, angle: f32) -> Self { + pub fn rotated_local(self, axis: Vector3, angle: real) -> Self { Self { basis: self.basis * Basis::from_axis_angle(axis, angle), origin: self.origin, @@ -285,10 +284,10 @@ impl Mul for Transform3D { } } -impl Mul for Transform3D { +impl Mul for Transform3D { type Output = Self; - fn mul(self, rhs: f32) -> Self::Output { + fn mul(self, rhs: real) -> Self::Output { Self { basis: self.basis * rhs, origin: self.origin * rhs, @@ -296,13 +295,16 @@ impl Mul for Transform3D { } } -impl GlamType for glam::Affine3A { +impl GlamType for RAffine3 { type Mapped = Transform3D; fn to_front(&self) -> Self::Mapped { Transform3D::new(self.matrix3.to_front(), self.translation.to_front()) } + // When `double-precision` is enabled this will complain. But it is + // needed for when it is not enabled. + #[allow(clippy::useless_conversion)] fn from_front(mapped: &Self::Mapped) -> Self { Self { matrix3: mapped.basis.to_glam().into(), @@ -312,7 +314,7 @@ impl GlamType for glam::Affine3A { } impl GlamConv for Transform3D { - type Glam = glam::Affine3A; + type Glam = RAffine3; } impl GodotFfi for Transform3D { @@ -372,7 +374,7 @@ mod test { #[test] fn rotation() { let axis = Vector3::new(1.0, 2.0, 3.0).normalized(); - let phi: f32 = 1.0; + let phi: real = 1.0; // Both versions should give the same result applied to identity. assert_eq!( @@ -392,7 +394,7 @@ mod test { #[test] fn finite_number_checks() { let y = Vector3::new(0.0, 1.0, 2.0); - let infinite_vec = Vector3::new(f32::NAN, f32::NAN, f32::NAN); + let infinite_vec = Vector3::new(real::NAN, real::NAN, real::NAN); let x = Basis::from_rows(y, y, y); let infinite_basis = Basis::from_rows(infinite_vec, infinite_vec, infinite_vec); diff --git a/godot-core/src/builtin/vector2.rs b/godot-core/src/builtin/vector2.rs index 1f9433ea5..8cb3b7583 100644 --- a/godot-core/src/builtin/vector2.rs +++ b/godot-core/src/builtin/vector2.rs @@ -6,7 +6,6 @@ use std::fmt; use std::ops::*; -use glam::Vec2; use godot_ffi as sys; use sys::{ffi_methods, GodotFfi}; @@ -15,6 +14,7 @@ use crate::builtin::{inner, Vector2i}; use super::glam_helpers::GlamConv; use super::glam_helpers::GlamType; +use super::{real, RAffine2, RVec2}; /// Vector used for 2D math using floating point coordinates. /// @@ -30,9 +30,9 @@ use super::glam_helpers::GlamType; #[repr(C)] pub struct Vector2 { /// The vector's X component. - pub x: f32, + pub x: real, /// The vector's Y component. - pub y: f32, + pub y: real, } impl Vector2 { @@ -42,8 +42,8 @@ impl Vector2 { /// 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); + /// Vector with all components set to `real::INFINITY`. + pub const INF: Self = Self::splat(real::INFINITY); /// Unit vector in -X direction (right in 2D coordinate system). pub const LEFT: Self = Self::new(-1.0, 0.0); @@ -58,61 +58,61 @@ impl Vector2 { 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 { + pub const fn new(x: real, y: real) -> Self { Self { x, y } } /// Constructs a new `Vector2` with both components set to `v`. - pub const fn splat(v: f32) -> Self { + pub const fn splat(v: real) -> Self { Self::new(v, v) } /// 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, + x: v.x as real, + y: v.y as real, } } /// Converts the corresponding `glam` type to `Self`. - fn from_glam(v: glam::Vec2) -> Self { + fn from_glam(v: RVec2) -> 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) + fn to_glam(self) -> RVec2 { + RVec2::new(self.x, self.y) } - pub fn angle(self) -> f32 { + pub fn angle(self) -> real { self.y.atan2(self.x) } - pub fn angle_to(self, to: Self) -> f32 { + pub fn angle_to(self, to: Self) -> real { self.to_glam().angle_between(to.to_glam()) } - pub fn angle_to_point(self, to: Self) -> f32 { + pub fn angle_to_point(self, to: Self) -> real { (to - self).angle() } - pub fn aspect(self) -> f32 { + pub fn aspect(self) -> real { self.x / self.y } - pub fn lerp(self, other: Self, weight: f32) -> Self { + pub fn lerp(self, other: Self, weight: real) -> Self { Self::new(lerp(self.x, other.x, weight), lerp(self.y, other.y, weight)) } - pub fn bezier_derivative(self, control_1: Self, control_2: Self, end: Self, t: f32) -> Self { + pub fn bezier_derivative(self, control_1: Self, control_2: Self, end: Self, t: real) -> Self { let x = bezier_derivative(self.x, control_1.x, control_2.x, end.x, t); let y = bezier_derivative(self.y, control_1.y, control_2.y, end.y, t); Self::new(x, y) } - pub fn bezier_interpolate(self, control_1: Self, control_2: Self, end: Self, t: f32) -> Self { + pub fn bezier_interpolate(self, control_1: Self, control_2: Self, end: Self, t: real) -> Self { let x = bezier_interpolate(self.x, control_1.x, control_2.x, end.x, t); let y = bezier_interpolate(self.y, control_1.y, control_2.y, end.y, t); @@ -131,11 +131,11 @@ impl Vector2 { Self::from_glam(self.to_glam().clamp(min.to_glam(), max.to_glam())) } - pub fn cross(self, with: Self) -> f32 { + pub fn cross(self, with: Self) -> real { self.to_glam().perp_dot(with.to_glam()) } - pub fn cubic_interpolate(self, b: Self, pre_a: Self, post_b: Self, weight: f32) -> Self { + pub fn cubic_interpolate(self, b: Self, pre_a: Self, post_b: Self, weight: real) -> Self { let x = cubic_interpolate(self.x, b.x, pre_a.x, post_b.x, weight); let y = cubic_interpolate(self.y, b.y, pre_a.y, post_b.y, weight); @@ -148,10 +148,10 @@ impl Vector2 { b: Self, pre_a: Self, post_b: Self, - weight: f32, - b_t: f32, - pre_a_t: f32, - post_b_t: f32, + weight: real, + b_t: real, + pre_a_t: real, + post_b_t: real, ) -> Self { let x = cubic_interpolate_in_time( self.x, b.x, pre_a.x, post_b.x, weight, b_t, pre_a_t, post_b_t, @@ -167,15 +167,15 @@ impl Vector2 { (to - self).normalized() } - pub fn distance_squared_to(self, to: Self) -> f32 { + pub fn distance_squared_to(self, to: Self) -> real { (to - self).length_squared() } - pub fn distance_to(self, to: Self) -> f32 { + pub fn distance_to(self, to: Self) -> real { (to - self).length() } - pub fn dot(self, other: Self) -> f32 { + pub fn dot(self, other: Self) -> real { self.to_glam().dot(other.to_glam()) } @@ -183,8 +183,8 @@ impl Vector2 { Self::from_glam(self.to_glam().floor()) } - pub fn from_angle(angle: f32) -> Self { - Self::from_glam(glam::Vec2::from_angle(angle)) + pub fn from_angle(angle: real) -> Self { + Self::from_glam(RVec2::from_angle(angle)) } pub fn is_equal_approx(self, to: Self) -> bool { @@ -203,11 +203,11 @@ impl Vector2 { is_zero_approx(self.x) && is_zero_approx(self.y) } - pub fn length_squared(self) -> f32 { + pub fn length_squared(self) -> real { self.to_glam().length_squared() } - pub fn limit_length(self, length: Option) -> Self { + pub fn limit_length(self, length: Option) -> Self { Self::from_glam(self.to_glam().clamp_length_max(length.unwrap_or(1.0))) } @@ -227,7 +227,7 @@ impl Vector2 { } } - pub fn move_toward(self, to: Self, delta: f32) -> Self { + pub fn move_toward(self, to: Self, delta: real) -> Self { let vd = to - self; let len = vd.length(); if len <= delta || len < CMP_EPSILON { @@ -241,7 +241,7 @@ impl Vector2 { Self::new(self.y, -self.x) } - pub fn posmod(self, pmod: f32) -> Self { + pub fn posmod(self, pmod: real) -> Self { Self::new(fposmod(self.x, pmod), fposmod(self.y, pmod)) } @@ -267,7 +267,7 @@ impl Vector2 { // TODO compare with gdnative implementation: // https://github.com/godot-rust/gdnative/blob/master/gdnative-core/src/core_types/vector3.rs#L335-L343 - pub fn slerp(self, to: Self, weight: f32) -> Self { + pub fn slerp(self, to: Self, weight: real) -> Self { let start_length_sq = self.length_squared(); let end_length_sq = to.length_squared(); if start_length_sq == 0.0 || end_length_sq == 0.0 { @@ -288,8 +288,8 @@ impl Vector2 { } /// 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())) + pub fn rotated(self, angle: real) -> Self { + Self::from_glam(RAffine2::from_angle(angle).transform_vector2(self.to_glam())) } #[doc(hidden)] @@ -305,10 +305,10 @@ impl fmt::Display for Vector2 { } } -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)); +impl_common_vector_fns!(Vector2, real); +impl_float_vector_fns!(Vector2, real); +impl_vector_operators!(Vector2, real, (x, y)); +impl_vector_index!(Vector2, real, (x, y), Vector2Axis, (X, Y)); impl GodotFfi for Vector2 { ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } @@ -328,7 +328,7 @@ impl GodotFfi for Vector2Axis { ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } -impl GlamType for Vec2 { +impl GlamType for RVec2 { type Mapped = Vector2; fn to_front(&self) -> Self::Mapped { @@ -336,10 +336,10 @@ impl GlamType for Vec2 { } fn from_front(mapped: &Self::Mapped) -> Self { - Vec2::new(mapped.x, mapped.y) + RVec2::new(mapped.x, mapped.y) } } impl GlamConv for Vector2 { - type Glam = Vec2; + type Glam = RVec2; } diff --git a/godot-core/src/builtin/vector3.rs b/godot-core/src/builtin/vector3.rs index 5cb9a9138..0c2ea462f 100644 --- a/godot-core/src/builtin/vector3.rs +++ b/godot-core/src/builtin/vector3.rs @@ -7,8 +7,6 @@ use std::ops::*; use std::fmt; -use glam::f32::Vec3; -use glam::Vec3A; use godot_ffi as sys; use sys::{ffi_methods, GodotFfi}; @@ -17,6 +15,7 @@ use crate::builtin::Vector3i; use super::glam_helpers::GlamConv; use super::glam_helpers::GlamType; +use super::{real, RVec3}; /// Vector used for 3D math using floating point coordinates. /// @@ -32,11 +31,11 @@ use super::glam_helpers::GlamType; #[repr(C)] pub struct Vector3 { /// The vector's X component. - pub x: f32, + pub x: real, /// The vector's Y component. - pub y: f32, + pub y: real, /// The vector's Z component. - pub z: f32, + pub z: real, } impl Vector3 { @@ -65,39 +64,39 @@ impl Vector3 { pub const BACK: Self = Self::new(0.0, 0.0, 1.0); /// Returns a `Vector3` with the given components. - pub const fn new(x: f32, y: f32, z: f32) -> Self { + pub const fn new(x: real, y: real, z: real) -> Self { Self { x, y, z } } /// Returns a new `Vector3` with all components set to `v`. - pub const fn splat(v: f32) -> Self { + pub const fn splat(v: real) -> Self { Self::new(v, v, v) } /// 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, + x: v.x as real, + y: v.y as real, + z: v.z as real, } } /// Converts the corresponding `glam` type to `Self`. - fn from_glam(v: glam::Vec3) -> Self { + fn from_glam(v: RVec3) -> Self { Self::new(v.x, v.y, v.z) } /// Converts `self` to the corresponding `glam` type. - fn to_glam(self) -> glam::Vec3 { - glam::Vec3::new(self.x, self.y, self.z) + fn to_glam(self) -> RVec3 { + RVec3::new(self.x, self.y, self.z) } - pub fn angle_to(self, to: Self) -> f32 { + pub fn angle_to(self, to: Self) -> real { self.to_glam().angle_between(to.to_glam()) } - pub fn bezier_derivative(self, control_1: Self, control_2: Self, end: Self, t: f32) -> Self { + pub fn bezier_derivative(self, control_1: Self, control_2: Self, end: Self, t: real) -> Self { let x = bezier_derivative(self.x, control_1.x, control_2.x, end.x, t); let y = bezier_derivative(self.y, control_1.y, control_2.y, end.y, t); let z = bezier_derivative(self.z, control_1.z, control_2.z, end.z, t); @@ -105,7 +104,7 @@ impl Vector3 { Self::new(x, y, z) } - pub fn bezier_interpolate(self, control_1: Self, control_2: Self, end: Self, t: f32) -> Self { + pub fn bezier_interpolate(self, control_1: Self, control_2: Self, end: Self, t: real) -> Self { let x = bezier_interpolate(self.x, control_1.x, control_2.x, end.x, t); let y = bezier_interpolate(self.y, control_1.y, control_2.y, end.y, t); let z = bezier_interpolate(self.z, control_1.z, control_2.z, end.z, t); @@ -129,7 +128,7 @@ impl Vector3 { Self::from_glam(self.to_glam().cross(with.to_glam())) } - pub fn cubic_interpolate(self, b: Self, pre_a: Self, post_b: Self, weight: f32) -> Self { + pub fn cubic_interpolate(self, b: Self, pre_a: Self, post_b: Self, weight: real) -> Self { let x = cubic_interpolate(self.x, b.x, pre_a.x, post_b.x, weight); let y = cubic_interpolate(self.y, b.y, pre_a.y, post_b.y, weight); let z = cubic_interpolate(self.z, b.z, pre_a.z, post_b.z, weight); @@ -143,10 +142,10 @@ impl Vector3 { b: Self, pre_a: Self, post_b: Self, - weight: f32, - b_t: f32, - pre_a_t: f32, - post_b_t: f32, + weight: real, + b_t: real, + pre_a_t: real, + post_b_t: real, ) -> Self { let x = cubic_interpolate_in_time( self.x, b.x, pre_a.x, post_b.x, weight, b_t, pre_a_t, post_b_t, @@ -165,15 +164,15 @@ impl Vector3 { (to - self).normalized() } - pub fn distance_squared_to(self, to: Self) -> f32 { + pub fn distance_squared_to(self, to: Self) -> real { (to - self).length_squared() } - pub fn distance_to(self, to: Self) -> f32 { + pub fn distance_to(self, to: Self) -> real { (to - self).length() } - pub fn dot(self, with: Self) -> f32 { + pub fn dot(self, with: Self) -> real { self.to_glam().dot(with.to_glam()) } @@ -203,15 +202,15 @@ impl Vector3 { is_zero_approx(self.x) && is_zero_approx(self.y) && is_zero_approx(self.z) } - pub fn length_squared(self) -> f32 { + pub fn length_squared(self) -> real { self.to_glam().length_squared() } - pub fn lerp(self, to: Self, weight: f32) -> Self { + pub fn lerp(self, to: Self, weight: real) -> Self { Self::from_glam(self.to_glam().lerp(to.to_glam(), weight)) } - pub fn limit_length(self, length: Option) -> Self { + pub fn limit_length(self, length: Option) -> Self { Self::from_glam(self.to_glam().clamp_length_max(length.unwrap_or(1.0))) } @@ -243,7 +242,7 @@ impl Vector3 { } } - pub fn move_toward(self, to: Self, delta: f32) -> Self { + pub fn move_toward(self, to: Self, delta: real) -> Self { let vd = to - self; let len = vd.length(); if len <= delta || len < CMP_EPSILON { @@ -253,7 +252,7 @@ impl Vector3 { } } - pub fn posmod(self, pmod: f32) -> Self { + pub fn posmod(self, pmod: real) -> Self { Self::new( fposmod(self.x, pmod), fposmod(self.y, pmod), @@ -285,7 +284,7 @@ impl Vector3 { Self::new(sign(self.x), sign(self.y), sign(self.z)) } - pub fn signed_angle_to(self, to: Self, axis: Self) -> f32 { + pub fn signed_angle_to(self, to: Self, axis: Self) -> real { let cross_to = self.cross(to); let unsigned_angle = self.dot(to).atan2(cross_to.length()); let sign = cross_to.dot(axis); @@ -316,10 +315,10 @@ impl fmt::Display for Vector3 { } } -impl_common_vector_fns!(Vector3, f32); -impl_float_vector_fns!(Vector3, 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, real); +impl_float_vector_fns!(Vector3, real); +impl_vector_operators!(Vector3, real, (x, y, z)); +impl_vector_index!(Vector3, real, (x, y, z), Vector3Axis, (X, Y, Z)); impl GodotFfi for Vector3 { ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } @@ -342,7 +341,7 @@ impl GodotFfi for Vector3Axis { ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } -impl GlamType for Vec3 { +impl GlamType for RVec3 { type Mapped = Vector3; fn to_front(&self) -> Self::Mapped { @@ -350,11 +349,12 @@ impl GlamType for Vec3 { } fn from_front(mapped: &Self::Mapped) -> Self { - Vec3::new(mapped.x, mapped.y, mapped.z) + RVec3::new(mapped.x, mapped.y, mapped.z) } } -impl GlamType for Vec3A { +#[cfg(not(feature = "double-precision"))] +impl GlamType for glam::Vec3A { type Mapped = Vector3; fn to_front(&self) -> Self::Mapped { @@ -362,10 +362,10 @@ impl GlamType for Vec3A { } fn from_front(mapped: &Self::Mapped) -> Self { - Vec3A::new(mapped.x, mapped.y, mapped.z) + glam::Vec3A::new(mapped.x, mapped.y, mapped.z) } } impl GlamConv for Vector3 { - type Glam = Vec3; + type Glam = RVec3; } diff --git a/godot-core/src/builtin/vector4.rs b/godot-core/src/builtin/vector4.rs index 6999d56fc..0197b540a 100644 --- a/godot-core/src/builtin/vector4.rs +++ b/godot-core/src/builtin/vector4.rs @@ -6,13 +6,13 @@ use std::fmt; -use glam::Vec4; use godot_ffi as sys; use sys::{ffi_methods, GodotFfi}; use crate::builtin::Vector4i; use super::glam_helpers::{GlamConv, GlamType}; +use super::{real, RVec4}; /// Vector used for 4D math using floating point coordinates. /// @@ -27,38 +27,38 @@ use super::glam_helpers::{GlamConv, GlamType}; #[repr(C)] pub struct Vector4 { /// The vector's X component. - pub x: f32, + pub x: real, /// The vector's Y component. - pub y: f32, + pub y: real, /// The vector's Z component. - pub z: f32, + pub z: real, /// The vector's W component. - pub w: f32, + pub w: real, } -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_vector_operators!(Vector4, real, (x, y, z, w)); +impl_vector_index!(Vector4, real, (x, y, z, w), Vector4Axis, (X, Y, Z, W)); +impl_common_vector_fns!(Vector4, real); +impl_float_vector_fns!(Vector4, real); impl Vector4 { /// Returns a `Vector4` with the given components. - pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self { + pub const fn new(x: real, y: real, z: real, w: real) -> Self { Self { x, y, z, w } } /// Returns a new `Vector4` with all components set to `v`. - pub const fn splat(v: f32) -> Self { + pub const fn splat(v: real) -> Self { Self::new(v, v, v, v) } /// 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, + x: v.x as real, + y: v.y as real, + z: v.z as real, + w: v.w as real, } } @@ -68,17 +68,17 @@ impl Vector4 { /// 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 `f32::INFINITY`. - pub const INF: Self = Self::splat(f32::INFINITY); + /// Infinity vector, a vector with all components set to `real::INFINITY`. + pub const INF: Self = Self::splat(real::INFINITY); /// Converts the corresponding `glam` type to `Self`. - fn from_glam(v: glam::Vec4) -> Self { + fn from_glam(v: RVec4) -> 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) + fn to_glam(self) -> RVec4 { + RVec4::new(self.x, self.y, self.z, self.w) } } @@ -111,7 +111,7 @@ impl GodotFfi for Vector4Axis { ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } -impl GlamType for Vec4 { +impl GlamType for RVec4 { type Mapped = Vector4; fn to_front(&self) -> Self::Mapped { @@ -119,10 +119,10 @@ impl GlamType for Vec4 { } fn from_front(mapped: &Self::Mapped) -> Self { - Vec4::new(mapped.x, mapped.y, mapped.z, mapped.w) + RVec4::new(mapped.x, mapped.y, mapped.z, mapped.w) } } impl GlamConv for Vector4 { - type Glam = Vec4; + type Glam = RVec4; } diff --git a/godot-core/src/builtin/vector_macros.rs b/godot-core/src/builtin/vector_macros.rs index 1585363cf..eed3d9c5c 100644 --- a/godot-core/src/builtin/vector_macros.rs +++ b/godot-core/src/builtin/vector_macros.rs @@ -167,7 +167,7 @@ macro_rules! impl_vector_operators { ( // Name of the vector type to be implemented, for example `Vector2`. $Vector:ty, - // Type of each individual component, for example `f32`. + // Type of each individual component, for example `real`. $Scalar:ty, // Names of the components, with parentheses, for example `(x, y)`. ($($components:ident),*) @@ -194,7 +194,7 @@ macro_rules! impl_vector_index { ( // Name of the vector type to be implemented, for example `Vector2`. $Vector:ty, - // Type of each individual component, for example `f32`. + // Type of each individual component, for example `real`. $Scalar:ty, // Names of the components, with parentheses, for example `(x, y)`. ($($components:ident),*), @@ -228,7 +228,7 @@ macro_rules! impl_common_vector_fns { ( // Name of the vector type. $Vector:ty, - // Type of target component, for example `f32`. + // Type of target component, for example `real`. $Scalar:ty ) => { impl $Vector { @@ -248,7 +248,7 @@ macro_rules! impl_float_vector_fns { ( // Name of the vector type. $Vector:ty, - // Type of target component, for example `f32`. + // Type of target component, for example `real`. $Scalar:ty ) => { impl $Vector { diff --git a/godot/Cargo.toml b/godot/Cargo.toml index bf85f1147..b05d29ee4 100644 --- a/godot/Cargo.toml +++ b/godot/Cargo.toml @@ -11,6 +11,7 @@ categories = ["game-engines", "graphics"] default = ["codegen-full"] formatted = ["godot-core/codegen-fmt"] trace = ["godot-core/trace"] +double-precision = ["godot-core/double-precision"] # Private features, they are under no stability guarantee codegen-full = ["godot-core/codegen-full"] diff --git a/itest/rust/Cargo.toml b/itest/rust/Cargo.toml index 0e610b266..8930d8ddb 100644 --- a/itest/rust/Cargo.toml +++ b/itest/rust/Cargo.toml @@ -11,6 +11,7 @@ crate-type = ["cdylib"] [features] default = [] trace = ["godot/trace"] +double-precision = ["godot/double-precision"] [dependencies] godot = { path = "../../godot", default-features = false, features = ["formatted"] } diff --git a/itest/rust/src/basis_test.rs b/itest/rust/src/basis_test.rs index 3a01c34d2..2bc700243 100644 --- a/itest/rust/src/basis_test.rs +++ b/itest/rust/src/basis_test.rs @@ -143,7 +143,7 @@ fn basis_equiv() { assert_eq_approx!( inner, outer, - |a, b| is_equal_approx(a as f32, b), + |a, b| is_equal_approx(real::from_f64(a), b), "function: {name}\n" ); } diff --git a/itest/rust/src/lib.rs b/itest/rust/src/lib.rs index 9b5d8074a..9fc5fc5dd 100644 --- a/itest/rust/src/lib.rs +++ b/itest/rust/src/lib.rs @@ -23,6 +23,8 @@ mod packed_array_test; mod quaternion_test; mod singleton_test; mod string_test; +mod transform2d_test; +mod transform3d_test; mod utilities_test; mod variant_test; mod virtual_methods_test; diff --git a/itest/rust/src/transform2d_test.rs b/itest/rust/src/transform2d_test.rs index 7191dd6c0..0c59cb445 100644 --- a/itest/rust/src/transform2d_test.rs +++ b/itest/rust/src/transform2d_test.rs @@ -44,19 +44,19 @@ fn transform2d_equiv() { assert_eq_approx!( inner.get_rotation(), outer.rotation(), - |a, b| is_equal_approx(a as f32, b), + |a, b| is_equal_approx(real::from_f64(a), b), "function: get_rotation\n" ); assert_eq_approx!( inner.get_rotation(), outer.rotation(), - |a, b| is_equal_approx(a as f32, b), + |a, b| is_equal_approx(real::from_f64(a), b), "function: get_rotation\n" ); assert_eq_approx!( inner.get_skew(), outer.skew(), - |a, b| is_equal_approx(a as f32, b), + |a, b| is_equal_approx(real::from_f64(a), b), "function: get_scale\n" ); }