diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f00b84cf..2391219f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -32,16 +32,28 @@ jobs: - run: sudo apt update && sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev - name: Clippy for bevy_rapier2d run: cargo clippy --verbose -p bevy_rapier2d + - name: Clippy for bevy_rapier2d_f64 + run: cargo clippy --verbose -p bevy_rapier2d_f64 - name: Clippy for bevy_rapier3d run: cargo clippy --verbose -p bevy_rapier3d + - name: Clippy for bevy_rapier3d_f64 + run: cargo clippy --verbose -p bevy_rapier3d_f64 - name: Clippy for bevy_rapier2d (debug-render, simd, serde) run: cargo clippy --verbose -p bevy_rapier2d --features debug-render-2d,simd-stable,serde-serialize + - name: Clippy for bevy_rapier2d_f64 (debug-render, simd, serde) + run: cargo clippy --verbose -p bevy_rapier2d_f64 --features debug-render-2d,simd-stable,serde-serialize - name: Clippy for bevy_rapier3d (debug-render, simd, serde) run: cargo clippy --verbose -p bevy_rapier3d --features debug-render-3d,simd-stable,serde-serialize + - name: Clippy for bevy_rapier3d_f64 (debug-render, simd, serde) + run: cargo clippy --verbose -p bevy_rapier3d_f64 --features debug-render-3d,simd-stable,serde-serialize - name: Test for bevy_rapier2d run: cargo test --verbose -p bevy_rapier2d + - name: Test for bevy_rapier2d_f64 + run: cargo test --verbose -p bevy_rapier2d_f64 - name: Test for bevy_rapier3d run: cargo test --verbose -p bevy_rapier3d + - name: Test for bevy_rapier3d_f64 + run: cargo test --verbose -p bevy_rapier3d_f64 test-wasm: runs-on: ubuntu-latest env: diff --git a/Cargo.toml b/Cargo.toml index a0431007..f1fddad4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["bevy_rapier2d", "bevy_rapier3d"] +members = ["bevy_rapier2d", "bevy_rapier2d_f64", "bevy_rapier3d", "bevy_rapier3d_f64"] resolver = "2" [profile.dev] diff --git a/bevy_rapier2d/Cargo.toml b/bevy_rapier2d/Cargo.toml index a691f05a..6b02bbb4 100644 --- a/bevy_rapier2d/Cargo.toml +++ b/bevy_rapier2d/Cargo.toml @@ -15,11 +15,12 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] path = "../src/lib.rs" -required-features = [ "dim2" ] +required-features = [ "dim2", "f32" ] [features] -default = [ "dim2", "async-collider", "debug-render-2d" ] +default = [ "dim2", "async-collider", "debug-render-2d", "f32" ] dim2 = [] +f32 = [] debug-render-2d = [ "bevy/bevy_core_pipeline", "bevy/bevy_sprite", "bevy/bevy_gizmos", "rapier2d/debug-render", "bevy/bevy_asset" ] debug-render-3d = [ "bevy/bevy_core_pipeline", "bevy/bevy_pbr", "bevy/bevy_gizmos", "rapier2d/debug-render", "bevy/bevy_asset" ] parallel = [ "rapier2d/parallel" ] @@ -27,7 +28,7 @@ simd-stable = [ "rapier2d/simd-stable" ] simd-nightly = [ "rapier2d/simd-nightly" ] wasm-bindgen = [ "rapier2d/wasm-bindgen" ] serde-serialize = [ "rapier2d/serde-serialize", "bevy/serialize", "serde" ] -enhanced-determinism = [ "rapier2d/enhanced-determinism" ] +enhanced-determinism = [ "rapier2d/enhanced-determinism", ] headless = [] async-collider = [ "bevy/bevy_asset", "bevy/bevy_scene" ] @@ -36,7 +37,7 @@ bevy = { version = "0.11", default-features = false } nalgebra = { version = "0.32.3", features = [ "convert-glam024" ] } # Don't enable the default features because we don't need the ColliderSet/RigidBodySet rapier2d = "0.17.0" -bitflags = "1" +bitflags = "2.4" #bevy_prototype_debug_lines = { version = "0.6", optional = true } log = "0.4" serde = { version = "1", features = [ "derive" ], optional = true} diff --git a/bevy_rapier2d_f64/Cargo.toml b/bevy_rapier2d_f64/Cargo.toml new file mode 100644 index 00000000..cf9147d5 --- /dev/null +++ b/bevy_rapier2d_f64/Cargo.toml @@ -0,0 +1,53 @@ +[package] +name = "bevy_rapier2d_f64" +version = "0.22.0" +authors = ["Sébastien Crozet "] +description = "2-dimensional physics engine in Rust, official Bevy plugin." +documentation = "http://docs.rs/bevy_rapier3d" +homepage = "http://rapier.rs" +repository = "https://github.com/dimforge/bevy_rapier" +readme = "../README.md" +keywords = [ "physics", "dynamics", "rigid", "real-time", "joints" ] +license = "Apache-2.0" +edition = "2021" + + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +path = "../src/lib.rs" +required-features = [ "dim2", "f64" ] + +[features] +default = [ "dim2", "async-collider", "debug-render-2d", "f64" ] +dim2 = [] +f64 = [] +debug-render = [ "debug-render-3d" ] +debug-render-2d = [ "bevy/bevy_core_pipeline", "bevy/bevy_sprite", "bevy/bevy_gizmos", "rapier2d-f64/debug-render", "bevy/bevy_asset" ] +debug-render-3d = [ "bevy/bevy_core_pipeline", "bevy/bevy_pbr", "bevy/bevy_gizmos", "rapier2d-f64/debug-render", "bevy/bevy_asset" ] +parallel = [ "rapier2d-f64/parallel" ] +simd-stable = [ "rapier2d-f64/simd-stable" ] +simd-nightly = [ "rapier2d-f64/simd-nightly" ] +wasm-bindgen = [ "rapier2d-f64/wasm-bindgen" ] +serde-serialize = [ "rapier2d-f64/serde-serialize", "bevy/serialize", "serde" ] +enhanced-determinism = [ "rapier2d-f64/enhanced-determinism" ] +headless = [ ] +async-collider = [ "bevy/bevy_asset", "bevy/bevy_scene" ] + +[dependencies] +bevy = { version = "0.11", default-features = false } +nalgebra = { version = "0.32.3", features = [ "convert-glam024" ] } +# Don't enable the default features because we don't need the ColliderSet/RigidBodySet +rapier2d-f64 = "0.17" +bitflags = "2.4" +#bevy_prototype_debug_lines = { version = "0.6", features = ["3d"], optional = true } +log = "0.4" +serde = { version = "1", features = [ "derive" ], optional = true} + +[dev-dependencies] +bevy = { version = "0.11", default-features = false, features = ["x11"]} +approx = "0.5.1" +glam = { version = "0.24", features = [ "approx" ] } + +[package.metadata.docs.rs] +# Enable all the features when building the docs on docs.rs +features = [ "debug-render-2d", "serde-serialize" ] diff --git a/bevy_rapier3d/Cargo.toml b/bevy_rapier3d/Cargo.toml index 1082f501..8876bfc5 100644 --- a/bevy_rapier3d/Cargo.toml +++ b/bevy_rapier3d/Cargo.toml @@ -15,11 +15,12 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] path = "../src/lib.rs" -required-features = [ "dim3" ] +required-features = [ "dim3", "f32" ] [features] -default = [ "dim3", "async-collider", "debug-render-3d" ] +default = [ "dim3", "async-collider", "debug-render-3d", "f32" ] dim3 = [] +f32 = [] debug-render = [ "debug-render-3d" ] debug-render-2d = [ "bevy/bevy_core_pipeline", "bevy/bevy_sprite", "bevy/bevy_gizmos", "rapier3d/debug-render", "bevy/bevy_asset" ] debug-render-3d = [ "bevy/bevy_core_pipeline", "bevy/bevy_pbr", "bevy/bevy_gizmos", "rapier3d/debug-render", "bevy/bevy_asset" ] @@ -36,8 +37,8 @@ async-collider = [ "bevy/bevy_asset", "bevy/bevy_scene" ] bevy = { version = "0.11", default-features = false } nalgebra = { version = "0.32.3", features = [ "convert-glam024" ] } # Don't enable the default features because we don't need the ColliderSet/RigidBodySet -rapier3d = "0.17.0" -bitflags = "1" +rapier3d = "0.17" +bitflags = "2.4" #bevy_prototype_debug_lines = { version = "0.6", features = ["3d"], optional = true } log = "0.4" serde = { version = "1", features = [ "derive" ], optional = true} diff --git a/bevy_rapier3d/examples/joints3.rs b/bevy_rapier3d/examples/joints3.rs index a7cb84f9..96ad7d9b 100644 --- a/bevy_rapier3d/examples/joints3.rs +++ b/bevy_rapier3d/examples/joints3.rs @@ -25,7 +25,7 @@ fn setup_graphics(mut commands: Commands) { }); } -fn create_prismatic_joints(commands: &mut Commands, origin: Vect, num: usize) { +fn create_prismatic_joints(commands: &mut Commands, origin: Vec3, num: usize) { let rad = 0.4; let shift = 1.0; @@ -62,7 +62,7 @@ fn create_prismatic_joints(commands: &mut Commands, origin: Vect, num: usize) { } } -fn create_rope_joints(commands: &mut Commands, origin: Vect, num: usize) { +fn create_rope_joints(commands: &mut Commands, origin: Vec3, num: usize) { let rad = 0.4; let shift = 1.0; diff --git a/bevy_rapier3d/examples/ray_casting3.rs b/bevy_rapier3d/examples/ray_casting3.rs index 50a27a4c..c6068117 100644 --- a/bevy_rapier3d/examples/ray_casting3.rs +++ b/bevy_rapier3d/examples/ray_casting3.rs @@ -80,12 +80,16 @@ fn cast_ray( ) { let window = windows.single(); - let Some(cursor_position) = window.cursor_position() else { return; }; + let Some(cursor_position) = window.cursor_position() else { + return; + }; // We will color in read the colliders hovered by the mouse. for (camera, camera_transform) in &cameras { // First, compute a ray from the mouse position. - let Some(ray) = camera.viewport_to_world(camera_transform, cursor_position) else { return; }; + let Some(ray) = camera.viewport_to_world(camera_transform, cursor_position) else { + return; + }; // Then cast the ray. let hit = rapier_context.cast_ray( diff --git a/bevy_rapier3d/examples/static_trimesh3.rs b/bevy_rapier3d/examples/static_trimesh3.rs index 09c6e4bb..7fcd1a11 100644 --- a/bevy_rapier3d/examples/static_trimesh3.rs +++ b/bevy_rapier3d/examples/static_trimesh3.rs @@ -30,7 +30,7 @@ fn setup_graphics(mut commands: Commands) { } fn ramp_size() -> Vec3 { - Vec3::new(10.0, 1.0, 1.0) + Vec3::new(10.0, 1.5, 1.0) } pub fn setup_physics(mut commands: Commands) { @@ -62,7 +62,7 @@ pub fn setup_physics(mut commands: Commands) { let mut indices: Vec<[u32; 3]> = Vec::new(); let segments = 32; - let bowl_size = Vec3::new(10.0, 3.0, 10.0); + let bowl_size = Vec3::new(10.0, 6.0, 10.0); for ix in 0..=segments { for iz in 0..=segments { @@ -111,9 +111,9 @@ impl Default for BallState { fn default() -> Self { Self { seconds_until_next_spawn: 0.5, - seconds_between_spawns: 2.0, + seconds_between_spawns: 2.5, balls_spawned: 0, - max_balls: 10, + max_balls: 200, } } } @@ -129,7 +129,7 @@ fn ball_spawner( // NOTE: The timing here only works properly with `time_dependent_number_of_timesteps` // disabled, as it is for examples. - ball_state.seconds_until_next_spawn -= rapier_context.integration_parameters.dt; + ball_state.seconds_until_next_spawn -= rapier_context.integration_parameters.dt as f32; if ball_state.seconds_until_next_spawn > 0.0 { return; } @@ -142,7 +142,7 @@ fn ball_spawner( commands.spawn(( TransformBundle::from(Transform::from_xyz( ramp_size.x * 0.9, - ramp_size.y / 2.0 + rad * 3.0, + ramp_size.y + rad * 3.0, 0.0, )), RigidBody::Dynamic, diff --git a/bevy_rapier3d_f64/Cargo.toml b/bevy_rapier3d_f64/Cargo.toml new file mode 100644 index 00000000..756f0c98 --- /dev/null +++ b/bevy_rapier3d_f64/Cargo.toml @@ -0,0 +1,53 @@ +[package] +name = "bevy_rapier3d_f64" +version = "0.22.0" +authors = ["Sébastien Crozet "] +description = "3-dimensional physics engine in Rust, official Bevy plugin." +documentation = "http://docs.rs/bevy_rapier3d" +homepage = "http://rapier.rs" +repository = "https://github.com/dimforge/bevy_rapier" +readme = "../README.md" +keywords = [ "physics", "dynamics", "rigid", "real-time", "joints" ] +license = "Apache-2.0" +edition = "2021" + + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +path = "../src/lib.rs" +required-features = [ "dim3", "f64" ] + +[features] +default = [ "dim3", "async-collider", "debug-render-3d", "f64" ] +dim3 = [] +f64 = [] +debug-render = [ "debug-render-3d" ] +debug-render-2d = [ "bevy/bevy_core_pipeline", "bevy/bevy_sprite", "bevy/bevy_gizmos", "rapier3d-f64/debug-render", "bevy/bevy_asset" ] +debug-render-3d = [ "bevy/bevy_core_pipeline", "bevy/bevy_pbr", "bevy/bevy_gizmos", "rapier3d-f64/debug-render", "bevy/bevy_asset" ] +parallel = [ "rapier3d-f64/parallel" ] +simd-stable = [ "rapier3d-f64/simd-stable" ] +simd-nightly = [ "rapier3d-f64/simd-nightly" ] +wasm-bindgen = [ "rapier3d-f64/wasm-bindgen" ] +serde-serialize = [ "rapier3d-f64/serde-serialize", "bevy/serialize", "serde" ] +enhanced-determinism = [ "rapier3d-f64/enhanced-determinism" ] +headless = [ ] +async-collider = [ "bevy/bevy_asset", "bevy/bevy_scene" ] + +[dependencies] +bevy = { version = "0.11", default-features = false } +nalgebra = { version = "0.32.3", features = [ "convert-glam024" ] } +# Don't enable the default features because we don't need the ColliderSet/RigidBodySet +rapier3d-f64 = "0.17" +bitflags = "2.4" +#bevy_prototype_debug_lines = { version = "0.6", features = ["3d"], optional = true } +log = "0.4" +serde = { version = "1", features = [ "derive" ], optional = true} + +[dev-dependencies] +bevy = { version = "0.11", default-features = false, features = ["x11"]} +approx = "0.5.1" +glam = { version = "0.24", features = [ "approx" ] } + +[package.metadata.docs.rs] +# Enable all the features when building the docs on docs.rs +features = [ "debug-render-3d", "serde-serialize" ] diff --git a/src/dynamics/fixed_joint.rs b/src/dynamics/fixed_joint.rs index 3bfe9346..54d48038 100644 --- a/src/dynamics/fixed_joint.rs +++ b/src/dynamics/fixed_joint.rs @@ -1,5 +1,5 @@ use crate::dynamics::{GenericJoint, GenericJointBuilder}; -use crate::math::{Rot, Vect}; +use crate::math::{AsPrecise, Rot, Vect}; use rapier::dynamics::JointAxesMask; #[derive(Copy, Clone, Debug, PartialEq)] @@ -41,8 +41,8 @@ impl FixedJoint { } /// Sets the joint’s basis, expressed in the first rigid-body’s local-space. - pub fn set_local_basis1(&mut self, local_basis: Rot) -> &mut Self { - self.data.set_local_basis1(local_basis); + pub fn set_local_basis1(&mut self, local_basis: impl AsPrecise) -> &mut Self { + self.data.set_local_basis1(local_basis.as_precise()); self } @@ -53,8 +53,8 @@ impl FixedJoint { } /// Sets joint’s basis, expressed in the second rigid-body’s local-space. - pub fn set_local_basis2(&mut self, local_basis: Rot) -> &mut Self { - self.data.set_local_basis2(local_basis); + pub fn set_local_basis2(&mut self, local_basis: impl AsPrecise) -> &mut Self { + self.data.set_local_basis2(local_basis.as_precise()); self } @@ -65,8 +65,8 @@ impl FixedJoint { } /// Sets the joint’s anchor, expressed in the local-space of the first rigid-body. - pub fn set_local_anchor1(&mut self, anchor1: Vect) -> &mut Self { - self.data.set_local_anchor1(anchor1); + pub fn set_local_anchor1(&mut self, anchor1: impl AsPrecise) -> &mut Self { + self.data.set_local_anchor1(anchor1.as_precise()); self } @@ -77,8 +77,8 @@ impl FixedJoint { } /// Sets the joint’s anchor, expressed in the local-space of the second rigid-body. - pub fn set_local_anchor2(&mut self, anchor2: Vect) -> &mut Self { - self.data.set_local_anchor2(anchor2); + pub fn set_local_anchor2(&mut self, anchor2: impl AsPrecise) -> &mut Self { + self.data.set_local_anchor2(anchor2.as_precise()); self } } @@ -101,29 +101,29 @@ impl FixedJointBuilder { /// Sets the joint’s basis, expressed in the first rigid-body’s local-space. #[must_use] - pub fn local_basis1(mut self, local_basis: Rot) -> Self { - self.0.set_local_basis1(local_basis); + pub fn local_basis1(mut self, local_basis: impl AsPrecise) -> Self { + self.0.set_local_basis1(local_basis.as_precise()); self } /// Sets joint’s basis, expressed in the second rigid-body’s local-space. #[must_use] - pub fn local_basis2(mut self, local_basis: Rot) -> Self { - self.0.set_local_basis2(local_basis); + pub fn local_basis2(mut self, local_basis: impl AsPrecise) -> Self { + self.0.set_local_basis2(local_basis.as_precise()); self } /// Sets the joint’s anchor, expressed in the local-space of the first rigid-body. #[must_use] - pub fn local_anchor1(mut self, anchor1: Vect) -> Self { - self.0.set_local_anchor1(anchor1); + pub fn local_anchor1(mut self, anchor1: impl AsPrecise) -> Self { + self.0.set_local_anchor1(anchor1.as_precise()); self } /// Sets the joint’s anchor, expressed in the local-space of the second rigid-body. #[must_use] - pub fn local_anchor2(mut self, anchor2: Vect) -> Self { - self.0.set_local_anchor2(anchor2); + pub fn local_anchor2(mut self, anchor2: impl AsPrecise) -> Self { + self.0.set_local_anchor2(anchor2.as_precise()); self } diff --git a/src/dynamics/generic_joint.rs b/src/dynamics/generic_joint.rs index 62c4da1f..f7a08108 100644 --- a/src/dynamics/generic_joint.rs +++ b/src/dynamics/generic_joint.rs @@ -1,5 +1,5 @@ use crate::dynamics::{FixedJoint, PrismaticJoint, RevoluteJoint, RopeJoint}; -use crate::math::{Real, Rot, Vect}; +use crate::math::{AsPrecise, Real, Rot, Vect}; use rapier::dynamics::{ GenericJoint as RapierGenericJoint, JointAxesMask, JointAxis, JointLimits, JointMotor, MotorModel, @@ -75,7 +75,8 @@ impl GenericJoint { } /// Sets the joint’s frame, expressed in the first rigid-body’s local-space. - pub fn set_local_basis1(&mut self, local_basis: Rot) -> &mut Self { + pub fn set_local_basis1(&mut self, local_basis: impl AsPrecise) -> &mut Self { + let local_basis = local_basis.as_precise(); #[cfg(feature = "dim2")] { self.raw.local_frame1.rotation = na::UnitComplex::new(local_basis); @@ -97,7 +98,8 @@ impl GenericJoint { } /// Sets the joint’s frame, expressed in the second rigid-body’s local-space. - pub fn set_local_basis2(&mut self, local_basis: Rot) -> &mut Self { + pub fn set_local_basis2(&mut self, local_basis: impl AsPrecise) -> &mut Self { + let local_basis = local_basis.as_precise(); #[cfg(feature = "dim2")] { self.raw.local_frame2.rotation = na::UnitComplex::new(local_basis); @@ -116,8 +118,9 @@ impl GenericJoint { } /// Sets the principal (local X) axis of this joint, expressed in the first rigid-body’s local-space. - pub fn set_local_axis1(&mut self, local_axis: Vect) -> &mut Self { - self.raw.set_local_axis1(local_axis.try_into().unwrap()); + pub fn set_local_axis1(&mut self, local_axis: impl AsPrecise) -> &mut Self { + self.raw + .set_local_axis1(local_axis.as_precise().try_into().unwrap()); self } @@ -128,8 +131,9 @@ impl GenericJoint { } /// Sets the principal (local X) axis of this joint, expressed in the second rigid-body’s local-space. - pub fn set_local_axis2(&mut self, local_axis: Vect) -> &mut Self { - self.raw.set_local_axis2(local_axis.try_into().unwrap()); + pub fn set_local_axis2(&mut self, local_axis: impl AsPrecise) -> &mut Self { + self.raw + .set_local_axis2(local_axis.as_precise().try_into().unwrap()); self } @@ -140,8 +144,8 @@ impl GenericJoint { } /// Sets anchor of this joint, expressed in the first rigid-body’s local-space. - pub fn set_local_anchor1(&mut self, anchor1: Vect) -> &mut Self { - self.raw.set_local_anchor1(anchor1.into()); + pub fn set_local_anchor1(&mut self, anchor1: impl AsPrecise) -> &mut Self { + self.raw.set_local_anchor1(anchor1.as_precise().into()); self } @@ -152,8 +156,8 @@ impl GenericJoint { } /// Sets anchor of this joint, expressed in the second rigid-body’s local-space. - pub fn set_local_anchor2(&mut self, anchor2: Vect) -> &mut Self { - self.raw.set_local_anchor2(anchor2.into()); + pub fn set_local_anchor2(&mut self, anchor2: impl AsPrecise) -> &mut Self { + self.raw.set_local_anchor2(anchor2.as_precise().into()); self } @@ -333,43 +337,43 @@ impl GenericJointBuilder { /// Sets the joint’s frame, expressed in the first rigid-body’s local-space. #[must_use] - pub fn local_basis1(mut self, local_basis: Rot) -> Self { - self.0.set_local_basis1(local_basis); + pub fn local_basis1(mut self, local_basis: impl AsPrecise) -> Self { + self.0.set_local_basis1(local_basis.as_precise()); self } /// Sets the joint’s frame, expressed in the second rigid-body’s local-space. #[must_use] - pub fn local_basis2(mut self, local_basis: Rot) -> Self { - self.0.set_local_basis2(local_basis); + pub fn local_basis2(mut self, local_basis: impl AsPrecise) -> Self { + self.0.set_local_basis2(local_basis.as_precise()); self } /// Sets the principal (local X) axis of this joint, expressed in the first rigid-body’s local-space. #[must_use] - pub fn local_axis1(mut self, local_axis: Vect) -> Self { - self.0.set_local_axis1(local_axis); + pub fn local_axis1(mut self, local_axis: impl AsPrecise) -> Self { + self.0.set_local_axis1(local_axis.as_precise()); self } /// Sets the principal (local X) axis of this joint, expressed in the second rigid-body’s local-space. #[must_use] - pub fn local_axis2(mut self, local_axis: Vect) -> Self { - self.0.set_local_axis2(local_axis); + pub fn local_axis2(mut self, local_axis: impl AsPrecise) -> Self { + self.0.set_local_axis2(local_axis.as_precise()); self } /// Sets the anchor of this joint, expressed in the first rigid-body’s local-space. #[must_use] - pub fn local_anchor1(mut self, anchor1: Vect) -> Self { - self.0.set_local_anchor1(anchor1); + pub fn local_anchor1(mut self, anchor1: impl AsPrecise) -> Self { + self.0.set_local_anchor1(anchor1.as_precise()); self } /// Sets the anchor of this joint, expressed in the second rigid-body’s local-space. #[must_use] - pub fn local_anchor2(mut self, anchor2: Vect) -> Self { - self.0.set_local_anchor2(anchor2); + pub fn local_anchor2(mut self, anchor2: impl AsPrecise) -> Self { + self.0.set_local_anchor2(anchor2.as_precise()); self } diff --git a/src/dynamics/prismatic_joint.rs b/src/dynamics/prismatic_joint.rs index 5d3f35e4..87fb69ca 100644 --- a/src/dynamics/prismatic_joint.rs +++ b/src/dynamics/prismatic_joint.rs @@ -1,5 +1,5 @@ use crate::dynamics::{GenericJoint, GenericJointBuilder}; -use crate::math::{Real, Vect}; +use crate::math::{AsPrecise, Real, Vect}; use rapier::dynamics::{JointAxesMask, JointAxis, JointLimits, JointMotor, MotorModel}; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] @@ -14,10 +14,10 @@ impl PrismaticJoint { /// Creates a new prismatic joint allowing only relative translations along the specified axis. /// /// This axis is expressed in the local-space of both rigid-bodies. - pub fn new(axis: Vect) -> Self { + pub fn new(axis: impl AsPrecise) -> Self { let data = GenericJointBuilder::new(JointAxesMask::LOCKED_PRISMATIC_AXES) - .local_axis1(axis) - .local_axis2(axis) + .local_axis1(axis.as_precise()) + .local_axis2(axis.as_precise()) .build(); Self { data } } @@ -45,8 +45,8 @@ impl PrismaticJoint { } /// Sets the joint’s anchor, expressed in the local-space of the first rigid-body. - pub fn set_local_anchor1(&mut self, anchor1: Vect) -> &mut Self { - self.data.set_local_anchor1(anchor1); + pub fn set_local_anchor1(&mut self, anchor1: impl AsPrecise) -> &mut Self { + self.data.set_local_anchor1(anchor1.as_precise()); self } @@ -57,8 +57,8 @@ impl PrismaticJoint { } /// Sets the joint’s anchor, expressed in the local-space of the second rigid-body. - pub fn set_local_anchor2(&mut self, anchor2: Vect) -> &mut Self { - self.data.set_local_anchor2(anchor2); + pub fn set_local_anchor2(&mut self, anchor2: impl AsPrecise) -> &mut Self { + self.data.set_local_anchor2(anchor2.as_precise()); self } @@ -69,8 +69,8 @@ impl PrismaticJoint { } /// Sets the principal axis of the joint, expressed in the local-space of the first rigid-body. - pub fn set_local_axis1(&mut self, axis1: Vect) -> &mut Self { - self.data.set_local_axis1(axis1); + pub fn set_local_axis1(&mut self, axis1: impl AsPrecise) -> &mut Self { + self.data.set_local_axis1(axis1.as_precise()); self } @@ -81,8 +81,8 @@ impl PrismaticJoint { } /// Sets the principal axis of the joint, expressed in the local-space of the second rigid-body. - pub fn set_local_axis2(&mut self, axis2: Vect) -> &mut Self { - self.data.set_local_axis2(axis2); + pub fn set_local_axis2(&mut self, axis2: impl AsPrecise) -> &mut Self { + self.data.set_local_axis2(axis2.as_precise()); self } @@ -166,35 +166,35 @@ impl PrismaticJointBuilder { /// Creates a new builder for prismatic joints. /// /// This axis is expressed in the local-space of both rigid-bodies. - pub fn new(axis: Vect) -> Self { - Self(PrismaticJoint::new(axis)) + pub fn new(axis: impl AsPrecise) -> Self { + Self(PrismaticJoint::new(axis.as_precise())) } /// Sets the joint’s anchor, expressed in the local-space of the first rigid-body. #[must_use] - pub fn local_anchor1(mut self, anchor1: Vect) -> Self { - self.0.set_local_anchor1(anchor1); + pub fn local_anchor1(mut self, anchor1: impl AsPrecise) -> Self { + self.0.set_local_anchor1(anchor1.as_precise()); self } /// Sets the joint’s anchor, expressed in the local-space of the second rigid-body. #[must_use] - pub fn local_anchor2(mut self, anchor2: Vect) -> Self { - self.0.set_local_anchor2(anchor2); + pub fn local_anchor2(mut self, anchor2: impl AsPrecise) -> Self { + self.0.set_local_anchor2(anchor2.as_precise()); self } /// Sets the principal axis of the joint, expressed in the local-space of the first rigid-body. #[must_use] - pub fn local_axis1(mut self, axis1: Vect) -> Self { - self.0.set_local_axis1(axis1); + pub fn local_axis1(mut self, axis1: impl AsPrecise) -> Self { + self.0.set_local_axis1(axis1.as_precise()); self } /// Sets the principal axis of the joint, expressed in the local-space of the second rigid-body. #[must_use] - pub fn local_axis2(mut self, axis2: Vect) -> Self { - self.0.set_local_axis2(axis2); + pub fn local_axis2(mut self, axis2: impl AsPrecise) -> Self { + self.0.set_local_axis2(axis2.as_precise()); self } diff --git a/src/dynamics/revolute_joint.rs b/src/dynamics/revolute_joint.rs index 590c033c..038c20e0 100644 --- a/src/dynamics/revolute_joint.rs +++ b/src/dynamics/revolute_joint.rs @@ -1,5 +1,5 @@ use crate::dynamics::{GenericJoint, GenericJointBuilder}; -use crate::math::{Real, Vect}; +use crate::math::{AsPrecise, Real, Vect}; use rapier::dynamics::{JointAxesMask, JointAxis, JointLimits, JointMotor, MotorModel}; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] @@ -29,7 +29,8 @@ impl RevoluteJoint { /// /// This axis is expressed in the local-space of both rigid-bodies. #[cfg(feature = "dim3")] - pub fn new(axis: Vect) -> Self { + pub fn new(axis: impl AsPrecise) -> Self { + let axis = axis.as_precise(); let data = GenericJointBuilder::new(JointAxesMask::LOCKED_REVOLUTE_AXES) .local_axis1(axis) .local_axis2(axis) @@ -60,8 +61,8 @@ impl RevoluteJoint { } /// Sets the joint’s anchor, expressed in the local-space of the first rigid-body. - pub fn set_local_anchor1(&mut self, anchor1: Vect) -> &mut Self { - self.data.set_local_anchor1(anchor1); + pub fn set_local_anchor1(&mut self, anchor1: impl AsPrecise) -> &mut Self { + self.data.set_local_anchor1(anchor1.as_precise()); self } @@ -72,8 +73,8 @@ impl RevoluteJoint { } /// Sets the joint’s anchor, expressed in the local-space of the second rigid-body. - pub fn set_local_anchor2(&mut self, anchor2: Vect) -> &mut Self { - self.data.set_local_anchor2(anchor2); + pub fn set_local_anchor2(&mut self, anchor2: impl AsPrecise) -> &mut Self { + self.data.set_local_anchor2(anchor2.as_precise()); self } @@ -171,21 +172,21 @@ impl RevoluteJointBuilder { /// /// This axis is expressed in the local-space of both rigid-bodies. #[cfg(feature = "dim3")] - pub fn new(axis: Vect) -> Self { - Self(RevoluteJoint::new(axis)) + pub fn new(axis: impl AsPrecise) -> Self { + Self(RevoluteJoint::new(axis.as_precise())) } /// Sets the joint’s anchor, expressed in the local-space of the first rigid-body. #[must_use] - pub fn local_anchor1(mut self, anchor1: Vect) -> Self { - self.0.set_local_anchor1(anchor1); + pub fn local_anchor1(mut self, anchor1: impl AsPrecise) -> Self { + self.0.set_local_anchor1(anchor1.as_precise()); self } /// Sets the joint’s anchor, expressed in the local-space of the second rigid-body. #[must_use] - pub fn local_anchor2(mut self, anchor2: Vect) -> Self { - self.0.set_local_anchor2(anchor2); + pub fn local_anchor2(mut self, anchor2: impl AsPrecise) -> Self { + self.0.set_local_anchor2(anchor2.as_precise()); self } diff --git a/src/dynamics/rigid_body.rs b/src/dynamics/rigid_body.rs index f2be60ec..9ae993e2 100644 --- a/src/dynamics/rigid_body.rs +++ b/src/dynamics/rigid_body.rs @@ -1,4 +1,5 @@ use crate::math::Vect; +use crate::prelude::Real; use bevy::prelude::*; use rapier::prelude::{ Isometry, LockedAxes as RapierLockedAxes, RigidBodyActivation, RigidBodyHandle, RigidBodyType, @@ -70,7 +71,7 @@ pub struct Velocity { pub linvel: Vect, /// The angular velocity of the rigid-body. #[cfg(feature = "dim2")] - pub angvel: f32, + pub angvel: Real, /// The angular velocity of the rigid-body. #[cfg(feature = "dim3")] pub angvel: Vect, @@ -101,7 +102,7 @@ impl Velocity { /// Initialize a velocity with the given angular velocity, and a linear velocity of zero. #[cfg(feature = "dim2")] - pub const fn angular(angvel: f32) -> Self { + pub const fn angular(angvel: Real) -> Self { Self { linvel: Vect::ZERO, angvel, @@ -138,7 +139,7 @@ pub enum AdditionalMassProperties { /// This mass will be added to the rigid-body. The rigid-body’s total /// angular inertia tensor (obtained from its attached colliders) will /// be scaled accordingly. - Mass(f32), + Mass(Real), /// These mass properties will be added to the rigid-body. MassProperties(MassProperties), } @@ -190,10 +191,10 @@ pub struct MassProperties { /// The center of mass of a rigid-body expressed in its local-space. pub local_center_of_mass: Vect, /// The mass of a rigid-body. - pub mass: f32, + pub mass: Real, /// The principal angular inertia of the rigid-body. #[cfg(feature = "dim2")] - pub principal_inertia: f32, + pub principal_inertia: Real, /// The principal vectors of the local angular inertia tensor of the rigid-body. #[cfg(feature = "dim3")] pub principal_inertia_local_frame: crate::math::Rot, @@ -205,7 +206,7 @@ pub struct MassProperties { impl MassProperties { /// Converts these mass-properties to Rapier’s `MassProperties` structure. #[cfg(feature = "dim2")] - pub fn into_rapier(self, physics_scale: f32) -> rapier::dynamics::MassProperties { + pub fn into_rapier(self, physics_scale: Real) -> rapier::dynamics::MassProperties { rapier::dynamics::MassProperties::new( (self.local_center_of_mass / physics_scale).into(), self.mass, @@ -216,7 +217,7 @@ impl MassProperties { /// Converts these mass-properties to Rapier’s `MassProperties` structure. #[cfg(feature = "dim3")] - pub fn into_rapier(self, physics_scale: f32) -> rapier::dynamics::MassProperties { + pub fn into_rapier(self, physics_scale: Real) -> rapier::dynamics::MassProperties { rapier::dynamics::MassProperties::with_principal_inertia_frame( (self.local_center_of_mass / physics_scale).into(), self.mass, @@ -226,7 +227,7 @@ impl MassProperties { } /// Converts Rapier’s `MassProperties` structure to `Self`. - pub fn from_rapier(mprops: rapier::dynamics::MassProperties, physics_scale: f32) -> Self { + pub fn from_rapier(mprops: rapier::dynamics::MassProperties, physics_scale: Real) -> Self { #[allow(clippy::useless_conversion)] // Need to convert if dim3 enabled Self { mass: mprops.mass(), @@ -239,11 +240,13 @@ impl MassProperties { } } +#[derive(Default, Component, Reflect, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] +#[reflect(Component, PartialEq)] +/// Flags affecting the behavior of the constraints solver for a given contact manifold. +pub struct LockedAxes(u8); + bitflags::bitflags! { - #[derive(Default, Component, Reflect)] - #[reflect(Component, PartialEq)] - /// Flags affecting the behavior of the constraints solver for a given contact manifold. - pub struct LockedAxes: u8 { + impl LockedAxes: u8 { /// Flag indicating that the rigid-body cannot translate along the `X` axis. const TRANSLATION_LOCKED_X = 1 << 0; /// Flag indicating that the rigid-body cannot translate along the `Y` axis. @@ -251,7 +254,7 @@ bitflags::bitflags! { /// Flag indicating that the rigid-body cannot translate along the `Z` axis. const TRANSLATION_LOCKED_Z = 1 << 2; /// Flag indicating that the rigid-body cannot translate along any direction. - const TRANSLATION_LOCKED = Self::TRANSLATION_LOCKED_X.bits | Self::TRANSLATION_LOCKED_Y.bits | Self::TRANSLATION_LOCKED_Z.bits; + const TRANSLATION_LOCKED = Self::TRANSLATION_LOCKED_X.bits() | Self::TRANSLATION_LOCKED_Y.bits() | Self::TRANSLATION_LOCKED_Z.bits(); /// Flag indicating that the rigid-body cannot rotate along the `X` axis. const ROTATION_LOCKED_X = 1 << 3; /// Flag indicating that the rigid-body cannot rotate along the `Y` axis. @@ -259,7 +262,7 @@ bitflags::bitflags! { /// Flag indicating that the rigid-body cannot rotate along the `Z` axis. const ROTATION_LOCKED_Z = 1 << 5; /// Combination of flags indicating that the rigid-body cannot rotate along any axis. - const ROTATION_LOCKED = Self::ROTATION_LOCKED_X.bits | Self::ROTATION_LOCKED_Y.bits | Self::ROTATION_LOCKED_Z.bits; + const ROTATION_LOCKED = Self::ROTATION_LOCKED_X.bits() | Self::ROTATION_LOCKED_Y.bits() | Self::ROTATION_LOCKED_Z.bits(); } } @@ -279,7 +282,7 @@ pub struct ExternalForce { pub force: Vect, /// The angular torque applied to the rigid-body. #[cfg(feature = "dim2")] - pub torque: f32, + pub torque: Real, /// The angular torque applied to the rigid-body. #[cfg(feature = "dim3")] pub torque: Vect, @@ -351,7 +354,7 @@ pub struct ExternalImpulse { pub impulse: Vect, /// The angular impulse applied to the rigid-body. #[cfg(feature = "dim2")] - pub torque_impulse: f32, + pub torque_impulse: Real, /// The angular impulse applied to the rigid-body. #[cfg(feature = "dim3")] pub torque_impulse: Vect, @@ -421,7 +424,7 @@ impl SubAssign for ExternalImpulse { /// applied to this rigid-body. #[derive(Copy, Clone, Debug, PartialEq, Component, Reflect)] #[reflect(Component, PartialEq)] -pub struct GravityScale(pub f32); +pub struct GravityScale(pub Real); impl Default for GravityScale { fn default() -> Self { @@ -476,9 +479,9 @@ impl Dominance { #[reflect(Component, PartialEq)] pub struct Sleeping { /// The linear velocity below which the body can fall asleep. - pub linear_threshold: f32, + pub linear_threshold: Real, /// The angular velocity below which the body can fall asleep. - pub angular_threshold: f32, + pub angular_threshold: Real, /// Is this body sleeping? pub sleeping: bool, } @@ -510,9 +513,9 @@ impl Default for Sleeping { pub struct Damping { // TODO: rename these to "linear" and "angular"? /// Damping factor for gradually slowing down the translational motion of the rigid-body. - pub linear_damping: f32, + pub linear_damping: Real, /// Damping factor for gradually slowing down the angular motion of the rigid-body. - pub angular_damping: f32, + pub angular_damping: Real, } impl Default for Damping { @@ -530,14 +533,14 @@ impl Default for Damping { #[derive(Copy, Clone, Debug, Default, PartialEq, Component)] pub struct TransformInterpolation { /// The starting point of the interpolation. - pub start: Option>, + pub start: Option>, /// The end point of the interpolation. - pub end: Option>, + pub end: Option>, } impl TransformInterpolation { /// Interpolates between the start and end positions with `t` in the range `[0..1]`. - pub fn lerp_slerp(&self, t: f32) -> Option> { + pub fn lerp_slerp(&self, t: Real) -> Option> { if let (Some(start), Some(end)) = (self.start, self.end) { Some(start.lerp_slerp(&end, t)) } else { diff --git a/src/dynamics/rope_joint.rs b/src/dynamics/rope_joint.rs index cac1cb65..6eac01cd 100644 --- a/src/dynamics/rope_joint.rs +++ b/src/dynamics/rope_joint.rs @@ -1,5 +1,5 @@ use crate::dynamics::{GenericJoint, GenericJointBuilder}; -use crate::math::{Real, Vect}; +use crate::math::{AsPrecise, Real, Vect}; use rapier::dynamics::{JointAxesMask, JointAxis, JointLimits, JointMotor, MotorModel}; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] @@ -42,8 +42,8 @@ impl RopeJoint { } /// Sets the joint’s anchor, expressed in the local-space of the first rigid-body. - pub fn set_local_anchor1(&mut self, anchor1: Vect) -> &mut Self { - self.data.set_local_anchor1(anchor1); + pub fn set_local_anchor1(&mut self, anchor1: impl AsPrecise) -> &mut Self { + self.data.set_local_anchor1(anchor1.as_precise()); self } @@ -54,8 +54,8 @@ impl RopeJoint { } /// Sets the joint’s anchor, expressed in the local-space of the second rigid-body. - pub fn set_local_anchor2(&mut self, anchor2: Vect) -> &mut Self { - self.data.set_local_anchor2(anchor2); + pub fn set_local_anchor2(&mut self, anchor2: impl AsPrecise) -> &mut Self { + self.data.set_local_anchor2(anchor2.as_precise()); self } @@ -66,8 +66,8 @@ impl RopeJoint { } /// Sets the principal axis of the joint, expressed in the local-space of the first rigid-body. - pub fn set_local_axis1(&mut self, axis1: Vect) -> &mut Self { - self.data.set_local_axis1(axis1); + pub fn set_local_axis1(&mut self, axis1: impl AsPrecise) -> &mut Self { + self.data.set_local_axis1(axis1.as_precise()); self } @@ -78,8 +78,8 @@ impl RopeJoint { } /// Sets the principal axis of the joint, expressed in the local-space of the second rigid-body. - pub fn set_local_axis2(&mut self, axis2: Vect) -> &mut Self { - self.data.set_local_axis2(axis2); + pub fn set_local_axis2(&mut self, axis2: impl AsPrecise) -> &mut Self { + self.data.set_local_axis2(axis2.as_precise()); self } @@ -199,15 +199,15 @@ impl RopeJointBuilder { /// Sets the joint’s anchor, expressed in the local-space of the first rigid-body. #[must_use] - pub fn local_anchor1(mut self, anchor1: Vect) -> Self { - self.0.set_local_anchor1(anchor1); + pub fn local_anchor1(mut self, anchor1: impl AsPrecise) -> Self { + self.0.set_local_anchor1(anchor1.as_precise()); self } /// Sets the joint’s anchor, expressed in the local-space of the second rigid-body. #[must_use] - pub fn local_anchor2(mut self, anchor2: Vect) -> Self { - self.0.set_local_anchor2(anchor2); + pub fn local_anchor2(mut self, anchor2: impl AsPrecise) -> Self { + self.0.set_local_anchor2(anchor2.as_precise()); self } diff --git a/src/dynamics/spherical_joint.rs b/src/dynamics/spherical_joint.rs index a8619d53..de11eb19 100644 --- a/src/dynamics/spherical_joint.rs +++ b/src/dynamics/spherical_joint.rs @@ -1,5 +1,5 @@ use crate::dynamics::{GenericJoint, GenericJointBuilder}; -use crate::math::{Real, Vect}; +use crate::math::{AsPrecise, Real, Vect}; use rapier::dynamics::{JointAxesMask, JointAxis, JointLimits, JointMotor, MotorModel}; #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] @@ -46,8 +46,8 @@ impl SphericalJoint { } /// Sets the joint’s anchor, expressed in the local-space of the first rigid-body. - pub fn set_local_anchor1(&mut self, anchor1: Vect) -> &mut Self { - self.data.set_local_anchor1(anchor1); + pub fn set_local_anchor1(&mut self, anchor1: impl AsPrecise) -> &mut Self { + self.data.set_local_anchor1(anchor1.as_precise()); self } @@ -58,8 +58,8 @@ impl SphericalJoint { } /// Sets the joint’s anchor, expressed in the local-space of the second rigid-body. - pub fn set_local_anchor2(&mut self, anchor2: Vect) -> &mut Self { - self.data.set_local_anchor2(anchor2); + pub fn set_local_anchor2(&mut self, anchor2: impl AsPrecise) -> &mut Self { + self.data.set_local_anchor2(anchor2.as_precise()); self } @@ -157,15 +157,15 @@ impl SphericalJointBuilder { /// Sets the joint’s anchor, expressed in the local-space of the first rigid-body. #[must_use] - pub fn local_anchor1(mut self, anchor1: Vect) -> Self { - self.0.set_local_anchor1(anchor1); + pub fn local_anchor1(mut self, anchor1: impl AsPrecise) -> Self { + self.0.set_local_anchor1(anchor1.as_precise()); self } /// Sets the joint’s anchor, expressed in the local-space of the second rigid-body. #[must_use] - pub fn local_anchor2(mut self, anchor2: Vect) -> Self { - self.0.set_local_anchor2(anchor2); + pub fn local_anchor2(mut self, anchor2: impl AsPrecise) -> Self { + self.0.set_local_anchor2(anchor2.as_precise()); self } diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs index a7dd2384..d47cc377 100644 --- a/src/geometry/collider.rs +++ b/src/geometry/collider.rs @@ -10,7 +10,7 @@ use rapier::geometry::Shape; use rapier::prelude::{ColliderHandle, InteractionGroups, SharedShape}; use crate::dynamics::{CoefficientCombineRule, MassProperties}; -use crate::math::Vect; +use crate::math::{Real, Vect}; /// The Rapier handle of a collider that was inserted to the physics scene. #[derive(Copy, Clone, Debug, Component)] @@ -109,9 +109,9 @@ pub struct Sensor; #[reflect(Component, PartialEq)] pub enum ColliderMassProperties { /// The mass-properties are computed automatically from the collider’s shape and this density. - Density(f32), + Density(Real), /// The mass-properties are computed automatically from the collider’s shape and this mass. - Mass(f32), + Mass(Real), /// The mass-properties of the collider are replaced by the ones specified here. MassProperties(MassProperties), } @@ -130,7 +130,7 @@ pub struct Friction { /// /// The greater the value, the stronger the friction forces will be. /// Should be `>= 0`. - pub coefficient: f32, + pub coefficient: Real, /// The rule applied to combine the friction coefficients of two colliders in contact. pub combine_rule: CoefficientCombineRule, } @@ -147,16 +147,13 @@ impl Default for Friction { impl Friction { /// Creates a `Friction` component from the given friction coefficient, and using the default /// `CoefficientCombineRule::Average` coefficient combine rule. - pub const fn new(coefficient: f32) -> Self { - Self { - coefficient, - combine_rule: CoefficientCombineRule::Average, - } + pub const fn new(coefficient: Real) -> Self { + Self::coefficient(coefficient) } /// Creates a `Friction` component from the given friction coefficient, and using the default /// `CoefficientCombineRule::Average` coefficient combine rule. - pub const fn coefficient(coefficient: f32) -> Self { + pub const fn coefficient(coefficient: Real) -> Self { Self { coefficient, combine_rule: CoefficientCombineRule::Average, @@ -172,7 +169,7 @@ pub struct Restitution { /// /// The greater the value, the stronger the restitution forces will be. /// Should be `>= 0`. - pub coefficient: f32, + pub coefficient: Real, /// The rule applied to combine the friction coefficients of two colliders in contact. pub combine_rule: CoefficientCombineRule, } @@ -180,16 +177,13 @@ pub struct Restitution { impl Restitution { /// Creates a `Restitution` component from the given restitution coefficient, and using the default /// `CoefficientCombineRule::Average` coefficient combine rule. - pub const fn new(coefficient: f32) -> Self { - Self { - coefficient, - combine_rule: CoefficientCombineRule::Average, - } + pub const fn new(coefficient: Real) -> Self { + Self::coefficient(coefficient) } /// Creates a `Restitution` component from the given restitution coefficient, and using the default /// `CoefficientCombineRule::Average` coefficient combine rule. - pub const fn coefficient(coefficient: f32) -> Self { + pub const fn coefficient(coefficient: Real) -> Self { Self { coefficient, combine_rule: CoefficientCombineRule::Average, @@ -206,13 +200,15 @@ impl Default for Restitution { } } +#[derive(Component, Reflect, Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] +#[reflect(Component, Hash, PartialEq)] +#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +/// Flags affecting whether or not collision-detection happens between two colliders +/// depending on the type of rigid-bodies they are attached to. +pub struct ActiveCollisionTypes(u16); + bitflags::bitflags! { - #[derive(Component, Reflect)] - #[reflect(Component, Hash, PartialEq)] - #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] - /// Flags affecting whether or not collision-detection happens between two colliders - /// depending on the type of rigid-bodies they are attached to. - pub struct ActiveCollisionTypes: u16 { + impl ActiveCollisionTypes: u16 { /// Enable collision-detection between a collider attached to a dynamic body /// and another collider attached to a dynamic body. const DYNAMIC_DYNAMIC = 0b0000_0000_0000_0001; @@ -245,17 +241,19 @@ impl Default for ActiveCollisionTypes { impl From for rapier::geometry::ActiveCollisionTypes { fn from(collision_types: ActiveCollisionTypes) -> rapier::geometry::ActiveCollisionTypes { - rapier::geometry::ActiveCollisionTypes::from_bits(collision_types.bits) + rapier::geometry::ActiveCollisionTypes::from_bits(collision_types.bits()) .expect("Internal error: invalid active events conversion.") } } +/// A bit mask identifying groups for interaction. +#[derive(Component, Reflect, Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[reflect(Component, Hash, PartialEq)] +#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +pub struct Group(u32); + bitflags::bitflags! { - /// A bit mask identifying groups for interaction. - #[derive(Component, Reflect)] - #[reflect(Component, Hash, PartialEq)] - #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] - pub struct Group: u32 { + impl Group: u32 { /// The group n°1. const GROUP_1 = 1 << 0; /// The group n°2. @@ -410,12 +408,14 @@ impl From for InteractionGroups { } } +#[derive(Default, Component, Reflect, Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] +#[reflect(Component)] +#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +/// Flags affecting the behavior of the constraints solver for a given contact manifold. +pub struct ActiveHooks(u32); + bitflags::bitflags! { - #[derive(Default, Component, Reflect)] - #[reflect(Component)] - #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] - /// Flags affecting the behavior of the constraints solver for a given contact manifold. - pub struct ActiveHooks: u32 { + impl ActiveHooks: u32 { /// If set, Rapier will call `PhysicsHooks::filter_contact_pair` whenever relevant. const FILTER_CONTACT_PAIRS = 0b0001; /// If set, Rapier will call `PhysicsHooks::filter_intersection_pair` whenever relevant. @@ -427,17 +427,19 @@ bitflags::bitflags! { impl From for rapier::pipeline::ActiveHooks { fn from(active_hooks: ActiveHooks) -> rapier::pipeline::ActiveHooks { - rapier::pipeline::ActiveHooks::from_bits(active_hooks.bits) + rapier::pipeline::ActiveHooks::from_bits(active_hooks.bits()) .expect("Internal error: invalid active events conversion.") } } +#[derive(Default, Component, Reflect, Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] +#[reflect(Component)] +#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +/// Flags affecting the events generated for this collider. +pub struct ActiveEvents(u32); + bitflags::bitflags! { - #[derive(Default, Component, Reflect)] - #[reflect(Component)] - #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] - /// Flags affecting the events generated for this collider. - pub struct ActiveEvents: u32 { + impl ActiveEvents: u32 { /// If set, Rapier will call `EventHandler::handle_collision_event` /// whenever relevant for this collider. const COLLISION_EVENTS = 0b0001; @@ -449,7 +451,7 @@ bitflags::bitflags! { impl From for rapier::pipeline::ActiveEvents { fn from(active_events: ActiveEvents) -> rapier::pipeline::ActiveEvents { - rapier::pipeline::ActiveEvents::from_bits(active_events.bits) + rapier::pipeline::ActiveEvents::from_bits(active_events.bits()) .expect("Internal error: invalid active events conversion.") } } @@ -457,11 +459,11 @@ impl From for rapier::pipeline::ActiveEvents { /// The total force magnitude beyond which a contact force event can be emitted. #[derive(Copy, Clone, PartialEq, Component, Reflect)] #[reflect(Component)] -pub struct ContactForceEventThreshold(pub f32); +pub struct ContactForceEventThreshold(pub Real); impl Default for ContactForceEventThreshold { fn default() -> Self { - Self(f32::MAX) + Self(Real::MAX) } } @@ -506,8 +508,8 @@ pub struct ColliderDisabled; /// We restrict the scaling increment to 1.0e-4, to avoid numerical jitter /// due to the extraction of scaling factor from the GlobalTransform matrix. pub fn get_snapped_scale(scale: Vect) -> Vect { - fn snap_value(new: f32) -> f32 { - const PRECISION: f32 = 1.0e4; + fn snap_value(new: Real) -> Real { + const PRECISION: Real = 1.0e4; (new * PRECISION).round() / PRECISION } diff --git a/src/geometry/collider_impl.rs b/src/geometry/collider_impl.rs index f526ecd8..c7b5f187 100644 --- a/src/geometry/collider_impl.rs +++ b/src/geometry/collider_impl.rs @@ -13,6 +13,7 @@ use super::{get_snapped_scale, shape_views::*}; use crate::geometry::ComputedColliderShape; use crate::geometry::{Collider, PointProjection, RayIntersection, TriMeshFlags, VHACDParameters}; use crate::math::{Real, Rot, Vect}; +use crate::utils::as_precise::AsPrecise; impl Collider { /// The scaling factor that was applied to this collider. @@ -37,111 +38,197 @@ impl Collider { } /// Initialize a new collider with a ball shape defined by its radius. - pub fn ball(radius: Real) -> Self { - SharedShape::ball(radius).into() + pub fn ball(radius: impl AsPrecise) -> Self { + SharedShape::ball(radius.as_precise()).into() } /// Initialize a new collider build with a half-space shape defined by the outward normal /// of its planar boundary. - pub fn halfspace(outward_normal: Vect) -> Option { + pub fn halfspace(outward_normal: impl AsPrecise) -> Option { use rapier::na::Unit; - let normal = Vector::from(outward_normal); + let normal = Vector::from(outward_normal.as_precise()); Unit::try_new(normal, 1.0e-6).map(|n| SharedShape::halfspace(n).into()) } /// Initialize a new collider with a cylindrical shape defined by its half-height /// (along along the y axis) and its radius. #[cfg(feature = "dim3")] - pub fn cylinder(half_height: Real, radius: Real) -> Self { - SharedShape::cylinder(half_height, radius).into() + pub fn cylinder( + half_height: impl AsPrecise, + radius: impl AsPrecise, + ) -> Self { + SharedShape::cylinder(half_height.as_precise(), radius.as_precise()).into() } /// Initialize a new collider with a rounded cylindrical shape defined by its half-height /// (along along the y axis), its radius, and its roundedness (the /// radius of the sphere used for dilating the cylinder). #[cfg(feature = "dim3")] - pub fn round_cylinder(half_height: Real, radius: Real, border_radius: Real) -> Self { - SharedShape::round_cylinder(half_height, radius, border_radius).into() + pub fn round_cylinder( + half_height: impl AsPrecise, + radius: impl AsPrecise, + border_radius: impl AsPrecise, + ) -> Self { + SharedShape::round_cylinder( + half_height.as_precise(), + radius.as_precise(), + border_radius.as_precise(), + ) + .into() } /// Initialize a new collider with a cone shape defined by its half-height /// (along along the y axis) and its basis radius. #[cfg(feature = "dim3")] - pub fn cone(half_height: Real, radius: Real) -> Self { - SharedShape::cone(half_height, radius).into() + pub fn cone( + half_height: impl AsPrecise, + radius: impl AsPrecise, + ) -> Self { + SharedShape::cone(half_height.as_precise(), radius.as_precise()).into() } /// Initialize a new collider with a rounded cone shape defined by its half-height /// (along along the y axis), its radius, and its roundedness (the /// radius of the sphere used for dilating the cylinder). #[cfg(feature = "dim3")] - pub fn round_cone(half_height: Real, radius: Real, border_radius: Real) -> Self { - SharedShape::round_cone(half_height, radius, border_radius).into() + pub fn round_cone( + half_height: impl AsPrecise, + radius: impl AsPrecise, + border_radius: impl AsPrecise, + ) -> Self { + SharedShape::round_cone( + half_height.as_precise(), + radius.as_precise(), + border_radius.as_precise(), + ) + .into() } /// Initialize a new collider with a cuboid shape defined by its half-extents. #[cfg(feature = "dim2")] - pub fn cuboid(half_x: Real, half_y: Real) -> Self { - SharedShape::cuboid(half_x, half_y).into() + pub fn cuboid(half_x: impl AsPrecise, half_y: impl AsPrecise) -> Self { + SharedShape::cuboid(half_x.as_precise(), half_y.as_precise()).into() } /// Initialize a new collider with a round cuboid shape defined by its half-extents /// and border radius. #[cfg(feature = "dim2")] - pub fn round_cuboid(half_x: Real, half_y: Real, border_radius: Real) -> Self { - SharedShape::round_cuboid(half_x, half_y, border_radius).into() + pub fn round_cuboid( + half_x: impl AsPrecise, + half_y: impl AsPrecise, + border_radius: impl AsPrecise, + ) -> Self { + SharedShape::round_cuboid( + half_x.as_precise(), + half_y.as_precise(), + border_radius.as_precise(), + ) + .into() } /// Initialize a new collider with a capsule shape. - pub fn capsule(start: Vect, end: Vect, radius: Real) -> Self { - SharedShape::capsule(start.into(), end.into(), radius).into() + pub fn capsule( + start: impl AsPrecise, + end: impl AsPrecise, + radius: impl AsPrecise, + ) -> Self { + SharedShape::capsule( + start.as_precise().into(), + end.as_precise().into(), + radius.as_precise(), + ) + .into() } /// Initialize a new collider with a capsule shape aligned with the `x` axis. - pub fn capsule_x(half_height: Real, radius: Real) -> Self { - let p = Point::from(Vector::x() * half_height); - SharedShape::capsule(-p, p, radius).into() + pub fn capsule_x( + half_height: impl AsPrecise, + radius: impl AsPrecise, + ) -> Self { + let p = Point::from(Vector::x() * half_height.as_precise()); + SharedShape::capsule(-p, p, radius.as_precise()).into() } /// Initialize a new collider with a capsule shape aligned with the `y` axis. - pub fn capsule_y(half_height: Real, radius: Real) -> Self { - let p = Point::from(Vector::y() * half_height); - SharedShape::capsule(-p, p, radius).into() + pub fn capsule_y( + half_height: impl AsPrecise, + radius: impl AsPrecise, + ) -> Self { + let p = Point::from(Vector::y() * half_height.as_precise()); + SharedShape::capsule(-p, p, radius.as_precise()).into() } /// Initialize a new collider with a capsule shape aligned with the `z` axis. #[cfg(feature = "dim3")] - pub fn capsule_z(half_height: Real, radius: Real) -> Self { - let p = Point::from(Vector::z() * half_height); - SharedShape::capsule(-p, p, radius).into() + pub fn capsule_z( + half_height: impl AsPrecise, + radius: impl AsPrecise, + ) -> Self { + let p = Point::from(Vector::z() * half_height.as_precise()); + SharedShape::capsule(-p, p, radius.as_precise()).into() } /// Initialize a new collider with a cuboid shape defined by its half-extents. #[cfg(feature = "dim3")] - pub fn cuboid(hx: Real, hy: Real, hz: Real) -> Self { - SharedShape::cuboid(hx, hy, hz).into() + pub fn cuboid( + hx: impl AsPrecise, + hy: impl AsPrecise, + hz: impl AsPrecise, + ) -> Self { + SharedShape::cuboid(hx.as_precise(), hy.as_precise(), hz.as_precise()).into() } /// Initialize a new collider with a round cuboid shape defined by its half-extents /// and border radius. #[cfg(feature = "dim3")] - pub fn round_cuboid(half_x: Real, half_y: Real, half_z: Real, border_radius: Real) -> Self { - SharedShape::round_cuboid(half_x, half_y, half_z, border_radius).into() + pub fn round_cuboid( + half_x: impl AsPrecise, + half_y: impl AsPrecise, + half_z: impl AsPrecise, + border_radius: impl AsPrecise, + ) -> Self { + SharedShape::round_cuboid( + half_x.as_precise(), + half_y.as_precise(), + half_z.as_precise(), + border_radius.as_precise(), + ) + .into() } /// Initializes a collider with a segment shape. - pub fn segment(a: Vect, b: Vect) -> Self { - SharedShape::segment(a.into(), b.into()).into() + pub fn segment(a: impl AsPrecise, b: impl AsPrecise) -> Self { + SharedShape::segment(a.as_precise().into(), b.as_precise().into()).into() } /// Initializes a collider with a triangle shape. - pub fn triangle(a: Vect, b: Vect, c: Vect) -> Self { - SharedShape::triangle(a.into(), b.into(), c.into()).into() + pub fn triangle( + a: impl AsPrecise, + b: impl AsPrecise, + c: impl AsPrecise, + ) -> Self { + SharedShape::triangle( + a.as_precise().into(), + b.as_precise().into(), + c.as_precise().into(), + ) + .into() } /// Initializes a collider with a triangle shape with round corners. - pub fn round_triangle(a: Vect, b: Vect, c: Vect, border_radius: Real) -> Self { - SharedShape::round_triangle(a.into(), b.into(), c.into(), border_radius).into() + pub fn round_triangle( + a: impl AsPrecise, + b: impl AsPrecise, + c: impl AsPrecise, + border_radius: impl AsPrecise, + ) -> Self { + SharedShape::round_triangle( + a.as_precise().into(), + b.as_precise().into(), + c.as_precise().into(), + border_radius.as_precise(), + ) + .into() } /// Initializes a collider with a polyline shape defined by its vertex and index buffers. @@ -151,19 +238,25 @@ impl Collider { } /// Initializes a collider with a triangle mesh shape defined by its vertex and index buffers. - pub fn trimesh(vertices: Vec, indices: Vec<[u32; 3]>) -> Self { - let vertices = vertices.into_iter().map(|v| v.into()).collect(); + pub fn trimesh>(vertices: Vec, indices: Vec<[u32; 3]>) -> Self { + let vertices = vertices + .into_iter() + .map(|v| v.as_precise().into()) + .collect(); SharedShape::trimesh(vertices, indices).into() } /// Initializes a collider with a triangle mesh shape defined by its vertex and index buffers, and flags /// controlling its pre-processing. - pub fn trimesh_with_flags( - vertices: Vec, + pub fn trimesh_with_flags>( + vertices: Vec, indices: Vec<[u32; 3]>, flags: TriMeshFlags, ) -> Self { - let vertices = vertices.into_iter().map(|v| v.into()).collect(); + let vertices = vertices + .into_iter() + .map(|v| v.as_precise().into()) + .collect(); SharedShape::trimesh_with_flags(vertices, indices, flags).into() } @@ -172,7 +265,9 @@ impl Collider { /// Returns `None` if the index buffer or vertex buffer of the mesh are in an incompatible format. #[cfg(all(feature = "dim3", feature = "async-collider"))] pub fn from_bevy_mesh(mesh: &Mesh, collider_shape: &ComputedColliderShape) -> Option { - let Some((vtx, idx)) = extract_mesh_vertices_indices(mesh) else { return None; }; + let Some((vtx, idx)) = extract_mesh_vertices_indices(mesh) else { + return None; + }; match collider_shape { ComputedColliderShape::TriMesh => Some( SharedShape::trimesh_with_flags(vtx, idx, TriMeshFlags::MERGE_DUPLICATE_VERTICES) @@ -189,42 +284,45 @@ impl Collider { /// Initializes a collider with a compound shape obtained from the decomposition of /// the given trimesh (in 3D) or polyline (in 2D) into convex parts. - pub fn convex_decomposition(vertices: &[Vect], indices: &[[u32; DIM]]) -> Self { - let vertices: Vec<_> = vertices.iter().map(|v| (*v).into()).collect(); + pub fn convex_decomposition>( + vertices: &[V], + indices: &[[u32; DIM]], + ) -> Self { + let vertices: Vec<_> = vertices.iter().map(|v| (*v).as_precise().into()).collect(); SharedShape::convex_decomposition(&vertices, indices).into() } /// Initializes a collider with a compound shape obtained from the decomposition of /// the given trimesh (in 3D) or polyline (in 2D) into convex parts dilated with round corners. - pub fn round_convex_decomposition( - vertices: &[Vect], + pub fn round_convex_decomposition>( + vertices: &[V], indices: &[[u32; DIM]], border_radius: Real, ) -> Self { - let vertices: Vec<_> = vertices.iter().map(|v| (*v).into()).collect(); + let vertices: Vec<_> = vertices.iter().map(|v| (*v).as_precise().into()).collect(); SharedShape::round_convex_decomposition(&vertices, indices, border_radius).into() } /// Initializes a collider with a compound shape obtained from the decomposition of /// the given trimesh (in 3D) or polyline (in 2D) into convex parts. - pub fn convex_decomposition_with_params( - vertices: &[Vect], + pub fn convex_decomposition_with_params>( + vertices: &[V], indices: &[[u32; DIM]], params: &VHACDParameters, ) -> Self { - let vertices: Vec<_> = vertices.iter().map(|v| (*v).into()).collect(); + let vertices: Vec<_> = vertices.iter().map(|v| (*v).as_precise().into()).collect(); SharedShape::convex_decomposition_with_params(&vertices, indices, params).into() } /// Initializes a collider with a compound shape obtained from the decomposition of /// the given trimesh (in 3D) or polyline (in 2D) into convex parts dilated with round corners. - pub fn round_convex_decomposition_with_params( - vertices: &[Vect], + pub fn round_convex_decomposition_with_params>( + vertices: &[V], indices: &[[u32; DIM]], params: &VHACDParameters, border_radius: Real, ) -> Self { - let vertices: Vec<_> = vertices.iter().map(|v| (*v).into()).collect(); + let vertices: Vec<_> = vertices.iter().map(|v| (*v).as_precise().into()).collect(); SharedShape::round_convex_decomposition_with_params( &vertices, indices, @@ -236,25 +334,28 @@ impl Collider { /// Initializes a new collider with a 2D convex polygon or 3D convex polyhedron /// obtained after computing the convex-hull of the given points. - pub fn convex_hull(points: &[Vect]) -> Option { - let points: Vec<_> = points.iter().map(|v| (*v).into()).collect(); + pub fn convex_hull>(points: &[V]) -> Option { + let points: Vec<_> = points.iter().map(|v| (*v).as_precise().into()).collect(); SharedShape::convex_hull(&points).map(Into::into) } /// Initializes a new collider with a round 2D convex polygon or 3D convex polyhedron /// obtained after computing the convex-hull of the given points. The shape is dilated /// by a sphere of radius `border_radius`. - pub fn round_convex_hull(points: &[Vect], border_radius: Real) -> Option { - let points: Vec<_> = points.iter().map(|v| (*v).into()).collect(); - SharedShape::round_convex_hull(&points, border_radius).map(Into::into) + pub fn round_convex_hull>( + points: &[V], + border_radius: impl AsPrecise, + ) -> Option { + let points: Vec<_> = points.iter().map(|v| (*v).as_precise().into()).collect(); + SharedShape::round_convex_hull(&points, border_radius.as_precise()).map(Into::into) } /// Creates a new collider that is a convex polygon formed by the /// given polyline assumed to be convex (no convex-hull will be automatically /// computed). #[cfg(feature = "dim2")] - pub fn convex_polyline(points: Vec) -> Option { - let points = points.into_iter().map(|v| v.into()).collect(); + pub fn convex_polyline>(points: Vec) -> Option { + let points = points.into_iter().map(|v| v.as_precise().into()).collect(); SharedShape::convex_polyline(points).map(Into::into) } @@ -262,8 +363,11 @@ impl Collider { /// given polyline assumed to be convex (no convex-hull will be automatically /// computed). The polygon shape is dilated by a sphere of radius `border_radius`. #[cfg(feature = "dim2")] - pub fn round_convex_polyline(points: Vec, border_radius: Real) -> Option { - let points = points.into_iter().map(|v| v.into()).collect(); + pub fn round_convex_polyline>( + points: Vec, + border_radius: Real, + ) -> Option { + let points = points.into_iter().map(|v| v.as_precise().into()).collect(); SharedShape::round_convex_polyline(points, border_radius).map(Into::into) } @@ -271,8 +375,11 @@ impl Collider { /// given triangle-mesh assumed to be convex (no convex-hull will be automatically /// computed). #[cfg(feature = "dim3")] - pub fn convex_mesh(points: Vec, indices: &[[u32; 3]]) -> Option { - let points = points.into_iter().map(|v| v.into()).collect(); + pub fn convex_mesh>( + points: Vec, + indices: &[[u32; 3]], + ) -> Option { + let points = points.into_iter().map(|v| v.as_precise().into()).collect(); SharedShape::convex_mesh(points, indices).map(Into::into) } @@ -280,31 +387,41 @@ impl Collider { /// given triangle-mesh assumed to be convex (no convex-hull will be automatically /// computed). The triangle mesh shape is dilated by a sphere of radius `border_radius`. #[cfg(feature = "dim3")] - pub fn round_convex_mesh( - points: Vec, + pub fn round_convex_mesh>( + points: Vec, indices: &[[u32; 3]], - border_radius: Real, + border_radius: impl AsPrecise, ) -> Option { - let points = points.into_iter().map(|v| v.into()).collect(); - SharedShape::round_convex_mesh(points, indices, border_radius).map(Into::into) + let points = points.into_iter().map(|v| v.as_precise().into()).collect(); + SharedShape::round_convex_mesh(points, indices, border_radius.as_precise()).map(Into::into) } /// Initializes a collider with a heightfield shape defined by its set of height and a scale /// factor along each coordinate axis. #[cfg(feature = "dim2")] - pub fn heightfield(heights: Vec, scale: Vect) -> Self { - SharedShape::heightfield(DVector::from_vec(heights), scale.into()).into() + pub fn heightfield>( + heights: Vec, + scale: impl AsPrecise, + ) -> Self { + let heights = heights.into_iter().map(|v| v.as_precise()).collect(); + SharedShape::heightfield(DVector::from_vec(heights), scale.as_precise().into()).into() } /// Initializes a collider with a heightfield shape defined by its set of height (in /// column-major format) and a scale factor along each coordinate axis. #[cfg(feature = "dim3")] - pub fn heightfield(heights: Vec, num_rows: usize, num_cols: usize, scale: Vect) -> Self { + pub fn heightfield>( + heights: Vec, + num_rows: usize, + num_cols: usize, + scale: Vect, + ) -> Self { assert_eq!( heights.len(), num_rows * num_cols, "Invalid number of heights provided." ); + let heights = heights.into_iter().map(|v| v.as_precise()).collect(); let heights = rapier::na::DMatrix::from_vec(num_rows, num_cols, heights); SharedShape::heightfield(heights, scale.into()).into() } diff --git a/src/geometry/shape_views/round_shape.rs b/src/geometry/shape_views/round_shape.rs index 936a1170..254e813c 100644 --- a/src/geometry/shape_views/round_shape.rs +++ b/src/geometry/shape_views/round_shape.rs @@ -1,4 +1,5 @@ use crate::geometry::shape_views::{CuboidView, CuboidViewMut, TriangleView, TriangleViewMut}; +use crate::math::Real; use rapier::geometry::{RoundCuboid, RoundTriangle}; #[cfg(feature = "dim2")] @@ -27,7 +28,7 @@ macro_rules! round_shape_view( impl<'a> $RoundShapeView<'a> { /// The radius of the round border of this shape. - pub fn border_radius(&self) -> f32 { + pub fn border_radius(&self) -> Real { self.raw.border_radius } @@ -47,12 +48,12 @@ macro_rules! round_shape_view( impl<'a> $RoundShapeViewMut<'a> { /// The radius of the round border of this shape. - pub fn border_radius(&self) -> f32 { + pub fn border_radius(&self) -> Real { self.raw.border_radius } /// Set the radius of the round border of this shape. - pub fn set_border_radius(&mut self, new_border_radius: f32) { + pub fn set_border_radius(&mut self, new_border_radius: Real) { self.raw.border_radius = new_border_radius; } diff --git a/src/lib.rs b/src/lib.rs index 557cd268..217700b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,36 +17,54 @@ extern crate serde; pub extern crate nalgebra as na; -#[cfg(feature = "dim2")] +#[cfg(all(feature = "dim2", feature = "f32"))] pub extern crate rapier2d as rapier; -#[cfg(feature = "dim3")] +#[cfg(all(feature = "dim2", feature = "f64"))] +pub extern crate rapier2d_f64 as rapier; +#[cfg(all(feature = "dim3", feature = "f32"))] pub extern crate rapier3d as rapier; +#[cfg(all(feature = "dim3", feature = "f64"))] +pub extern crate rapier3d_f64 as rapier; pub use rapier::parry; /// Type aliases to select the right vector/rotation types based -/// on the dimension used by the engine. +/// on the dimension and precision used by the engine. #[cfg(feature = "dim2")] pub mod math { - use bevy::math::Vec2; + pub use crate::utils::as_precise::*; + /// The real type (f32 or f64). pub type Real = rapier::math::Real; /// The vector type. - pub type Vect = Vec2; + #[cfg(feature = "f32")] + pub type Vect = bevy::math::Vec2; + /// The vector type. + #[cfg(feature = "f64")] + pub type Vect = bevy::math::DVec2; /// The rotation type (in 2D this is an angle in radians). pub type Rot = Real; } /// Type aliases to select the right vector/rotation types based -/// on the dimension used by the engine. +/// on the dimension and precision used by the engine. #[cfg(feature = "dim3")] pub mod math { - use bevy::math::{Quat, Vec3}; + pub use crate::utils::as_precise::*; + /// The real type (f32 or f64). pub type Real = rapier::math::Real; /// The vector type. - pub type Vect = Vec3; + #[cfg(feature = "f32")] + pub type Vect = bevy::math::Vec3; + /// The vector type. + #[cfg(feature = "f64")] + pub type Vect = bevy::math::DVec3; + /// The rotation type. + #[cfg(feature = "f32")] + pub type Rot = bevy::math::Quat; /// The rotation type. - pub type Rot = Quat; + #[cfg(feature = "f64")] + pub type Rot = bevy::math::DQuat; } /// Components related to physics dynamics (rigid-bodies, velocities, etc.) diff --git a/src/plugin/configuration.rs b/src/plugin/configuration.rs index dac478b0..37ad3cb0 100644 --- a/src/plugin/configuration.rs +++ b/src/plugin/configuration.rs @@ -1,12 +1,12 @@ use bevy::prelude::Resource; -use crate::math::Vect; +use crate::math::{Real, Vect}; /// Difference between simulation and rendering time #[derive(Resource, Default)] pub struct SimulationToRenderTime { /// Difference between simulation and rendering time - pub diff: f32, + pub diff: Real, } /// The different ways of adjusting the timestep length. @@ -16,7 +16,7 @@ pub enum TimestepMode { /// `dt` seconds at each Bevy tick by performing `substeps` of length `dt / substeps`. Fixed { /// The physics simulation will be advanced by this total amount at each Bevy tick. - dt: f32, + dt: Real, /// This number of substeps of length `dt / substeps` will be performed at each Bevy tick. substeps: usize, }, @@ -26,10 +26,10 @@ pub enum TimestepMode { /// `time_scale < 1.0` makes the simulation run in slow-motion. Variable { /// Maximum amount of time the physics simulation may be advanced at each Bevy tick. - max_dt: f32, + max_dt: Real, /// Multiplier controlling if the physics simulation should advance faster (> 1.0), /// at the same speed (= 1.0) or slower (< 1.0) than the real time. - time_scale: f32, + time_scale: Real, /// The number of substeps that will be performed at each tick. substeps: usize, }, @@ -40,10 +40,10 @@ pub enum TimestepMode { Interpolated { /// The physics simulation will be advanced by this total amount at each Bevy tick, unless /// the physics simulation time is ahead of a the real time. - dt: f32, + dt: Real, /// Multiplier controlling if the physics simulation should advance faster (> 1.0), /// at the same speed (= 1.0) or slower (< 1.0) than the real time. - time_scale: f32, + time_scale: Real, /// The number of substeps that will be performed whenever the physics simulation is advanced. substeps: usize, }, diff --git a/src/plugin/context.rs b/src/plugin/context.rs index 0dadbc47..bb184432 100644 --- a/src/plugin/context.rs +++ b/src/plugin/context.rs @@ -18,6 +18,7 @@ use crate::control::{CharacterCollision, MoveShapeOptions, MoveShapeOutput}; use crate::dynamics::TransformInterpolation; use crate::plugin::configuration::{SimulationToRenderTime, TimestepMode}; use crate::prelude::{CollisionGroups, RapierRigidBodyHandle}; +use crate::utils::as_precise::*; use rapier::control::CharacterAutostep; /// The Rapier context, containing all the state of the physics engine. @@ -232,7 +233,7 @@ impl RapierContext { time_scale, substeps, } => { - sim_to_render_time.diff += time.delta_seconds(); + sim_to_render_time.diff += time.delta_seconds_f64().as_precise(); while sim_to_render_time.diff > 0.0 { // NOTE: in this comparison we do the same computations we @@ -282,7 +283,8 @@ impl RapierContext { } => { let mut substep_integration_parameters = self.integration_parameters; substep_integration_parameters.dt = - (time.delta_seconds() * time_scale).min(max_dt) / (substeps as Real); + (time.delta_seconds_f64().as_precise() * time_scale).min(max_dt) + / (substeps as Real); for _ in 0..substeps { self.pipeline.step( @@ -485,15 +487,15 @@ impl RapierContext { /// * `filter`: set of rules used to determine which collider is taken into account by this scene query. pub fn cast_ray( &self, - ray_origin: Vect, - ray_dir: Vect, - max_toi: Real, + ray_origin: impl AsPrecise, + ray_dir: impl AsPrecise, + max_toi: impl AsPrecise, solid: bool, filter: QueryFilter, ) -> Option<(Entity, Real)> { let ray = Ray::new( - (ray_origin / self.physics_scale).into(), - (ray_dir / self.physics_scale).into(), + (ray_origin.as_precise() / self.physics_scale).into(), + (ray_dir.as_precise() / self.physics_scale).into(), ); let (h, toi) = self.with_query_filter(filter, move |filter| { @@ -501,7 +503,7 @@ impl RapierContext { &self.bodies, &self.colliders, &ray, - max_toi, + max_toi.as_precise(), solid, filter, ) @@ -729,17 +731,18 @@ impl RapierContext { aabb: bevy::render::primitives::Aabb, mut callback: impl FnMut(Entity) -> bool, ) { + let scale = self.physics_scale; #[cfg(feature = "dim2")] use bevy::math::Vec3Swizzles; #[cfg(feature = "dim2")] let scaled_aabb = rapier::prelude::Aabb { - mins: (aabb.min().xy() / self.physics_scale).into(), - maxs: (aabb.max().xy() / self.physics_scale).into(), + mins: (aabb.min().xy().as_precise() / scale).into(), + maxs: (aabb.max().xy().as_precise() / scale).into(), }; #[cfg(feature = "dim3")] let scaled_aabb = rapier::prelude::Aabb { - mins: (aabb.min() / self.physics_scale).into(), - maxs: (aabb.max() / self.physics_scale).into(), + mins: (aabb.min().as_precise() / scale).into(), + maxs: (aabb.max().as_precise() / scale).into(), }; #[allow(clippy::redundant_closure)] // False-positive, we can't move callback, closure becomes `FnOnce` @@ -768,14 +771,18 @@ impl RapierContext { #[allow(clippy::too_many_arguments)] pub fn cast_shape( &self, - shape_pos: Vect, - shape_rot: Rot, - shape_vel: Vect, + shape_pos: impl AsPrecise, + shape_rot: impl AsPrecise, + shape_vel: impl AsPrecise, shape: &Collider, - max_toi: Real, + max_toi: impl AsPrecise, filter: QueryFilter, ) -> Option<(Entity, Toi)> { - let scaled_transform = (shape_pos / self.physics_scale, shape_rot).into(); + let scaled_transform = ( + shape_pos.as_precise() / self.physics_scale, + shape_rot.as_precise(), + ) + .into(); let mut scaled_shape = shape.clone(); // TODO: how to set a good number of subdivisions, we don’t have access to the // RapierConfiguration::scaled_shape_subdivision here. @@ -786,9 +793,9 @@ impl RapierContext { &self.bodies, &self.colliders, &scaled_transform, - &(shape_vel / self.physics_scale).into(), + &(shape_vel.as_precise() / self.physics_scale).into(), &*scaled_shape.raw, - max_toi, + max_toi.as_precise(), true, filter, ) @@ -860,13 +867,17 @@ impl RapierContext { /// * `callback` - A function called with the entities of each collider intersecting the `shape`. pub fn intersections_with_shape( &self, - shape_pos: Vect, - shape_rot: Rot, + shape_pos: impl AsPrecise, + shape_rot: impl AsPrecise, shape: &Collider, filter: QueryFilter, mut callback: impl FnMut(Entity) -> bool, ) { - let scaled_transform = (shape_pos / self.physics_scale, shape_rot).into(); + let scaled_transform = ( + shape_pos.as_precise() / self.physics_scale, + shape_rot.as_precise(), + ) + .into(); let mut scaled_shape = shape.clone(); // TODO: how to set a good number of subdivisions, we don’t have access to the // RapierConfiguration::scaled_shape_subdivision here. diff --git a/src/plugin/plugin.rs b/src/plugin/plugin.rs index fc5aa295..afbf7e73 100644 --- a/src/plugin/plugin.rs +++ b/src/plugin/plugin.rs @@ -1,3 +1,4 @@ +use crate::math::Real; use crate::pipeline::{CollisionEvent, ContactForceEvent}; use crate::plugin::configuration::SimulationToRenderTime; use crate::plugin::{systems, RapierConfiguration, RapierContext}; @@ -19,7 +20,7 @@ pub type NoUserData = (); /// Rapier physics engine. pub struct RapierPhysicsPlugin { schedule: Box, - physics_scale: f32, + physics_scale: Real, default_system_setup: bool, _phantom: PhantomData, } @@ -35,7 +36,7 @@ where /// all the length-related quantities by the `physics_scale` factor. This should /// likely always be 1.0 in 3D. In 2D, this is useful to specify a "pixels-per-meter" /// conversion ratio. - pub fn with_physics_scale(mut self, physics_scale: f32) -> Self { + pub fn with_physics_scale(mut self, physics_scale: Real) -> Self { self.physics_scale = physics_scale; self } @@ -53,7 +54,7 @@ where /// /// This conversion unit assumes that the 2D camera uses an unscaled projection. #[cfg(feature = "dim2")] - pub fn pixels_per_meter(pixels_per_meter: f32) -> Self { + pub fn pixels_per_meter(pixels_per_meter: Real) -> Self { Self { physics_scale: pixels_per_meter, default_system_setup: true, diff --git a/src/plugin/systems.rs b/src/plugin/systems.rs index aadbf662..d40f6434 100644 --- a/src/plugin/systems.rs +++ b/src/plugin/systems.rs @@ -18,7 +18,7 @@ use crate::prelude::{ BevyPhysicsHooks, BevyPhysicsHooksAdapter, CollidingEntities, KinematicCharacterController, KinematicCharacterControllerOutput, MassModifiedEvent, RigidBodyDisabled, }; -use crate::utils; +use crate::utils::{self, as_precise::*}; use bevy::ecs::system::{StaticSystemParam, SystemParamItem}; use bevy::prelude::*; use rapier::prelude::*; @@ -100,15 +100,17 @@ pub fn apply_scale( let effective_scale = match custom_scale { Some(ColliderScale::Absolute(scale)) => *scale, Some(ColliderScale::Relative(scale)) => { - *scale * transform.compute_transform().scale.xy() + *scale * transform.compute_transform().scale.xy().as_precise() } - None => transform.compute_transform().scale.xy(), + None => transform.compute_transform().scale.xy().as_precise(), }; #[cfg(feature = "dim3")] let effective_scale = match custom_scale { Some(ColliderScale::Absolute(scale)) => *scale, - Some(ColliderScale::Relative(scale)) => *scale * transform.compute_transform().scale, - None => transform.compute_transform().scale, + Some(ColliderScale::Relative(scale)) => { + *scale * transform.compute_transform().scale.as_precise() + } + None => transform.compute_transform().scale.as_precise(), }; if shape.scale != crate::geometry::get_snapped_scale(effective_scale) { @@ -1465,11 +1467,11 @@ pub fn update_character_controls( if let Ok(mut transform) = transforms.get_mut(entity_to_move) { // TODO: take the parent’s GlobalTransform rotation into account? - transform.translation.x += movement.translation.x * physics_scale; - transform.translation.y += movement.translation.y * physics_scale; + transform.translation.x += (movement.translation.x * physics_scale).as_single(); + transform.translation.y += (movement.translation.y * physics_scale).as_single(); #[cfg(feature = "dim3")] { - transform.translation.z += movement.translation.z * physics_scale; + transform.translation.z += (movement.translation.z * physics_scale).as_single(); } } diff --git a/src/render/mod.rs b/src/render/mod.rs index 7bc01880..2e2cc4d2 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -1,4 +1,5 @@ use crate::plugin::RapierContext; +use crate::prelude::*; use bevy::prelude::*; use bevy::transform::TransformSystem; use rapier::math::{Point, Real}; @@ -95,7 +96,7 @@ impl Plugin for RapierDebugRenderPlugin { } struct BevyLinesRenderBackend<'world, 'state, 'a, 'b> { - physics_scale: f32, + physics_scale: Real, custom_colors: Query<'world, 'state, &'a ColliderDebugColor>, context: &'b RapierContext, gizmos: Gizmos<'state>, @@ -126,11 +127,12 @@ impl<'world, 'state, 'a, 'b> DebugRenderBackend for BevyLinesRenderBackend<'worl b: Point, color: [f32; 4], ) { - let scale = self.physics_scale; + let a = (Vect::from(a) * self.physics_scale).as_single(); + let b = (Vect::from(b) * self.physics_scale).as_single(); let color = self.object_color(object, color); self.gizmos.line( - [a.x * scale, a.y * scale, 0.0].into(), - [b.x * scale, b.y * scale, 0.0].into(), + a.extend(0.0), + b.extend(0.0), Color::hsla(color[0], color[1], color[2], color[3]), ) } @@ -143,13 +145,11 @@ impl<'world, 'state, 'a, 'b> DebugRenderBackend for BevyLinesRenderBackend<'worl b: Point, color: [f32; 4], ) { - let scale = self.physics_scale; + let a = (Vect::from(a) * self.physics_scale).as_single(); + let b = (Vect::from(b) * self.physics_scale).as_single(); let color = self.object_color(object, color); - self.gizmos.line( - [a.x * scale, a.y * scale, a.z * scale].into(), - [b.x * scale, b.y * scale, b.z * scale].into(), - Color::hsla(color[0], color[1], color[2], color[3]), - ) + self.gizmos + .line(a, b, Color::hsla(color[0], color[1], color[2], color[3])) } } diff --git a/src/utils/as_precise.rs b/src/utils/as_precise.rs new file mode 100644 index 00000000..bcd9d4ca --- /dev/null +++ b/src/utils/as_precise.rs @@ -0,0 +1,185 @@ +use bevy::math::*; + +/// Convenience method for converting math types into single or double +/// floating point precision depending on feature flags. +pub trait AsPrecise: Clone + Copy { + /// Single or double precision version of this type. + type Out; + /// Convert into single or double precision floating point + /// depending on feature flags. + fn as_precise(self) -> Self::Out; +} + +/// Convenience method for converting math types into single precision floating point. +pub trait AsSingle: Clone + Copy { + /// Single precision version of this type. + type Out; + /// Convert value into single precision floating point. + fn as_single(self) -> Self::Out; +} + +macro_rules! as_precise_self { + ($ty:ty) => { + impl AsPrecise for $ty { + type Out = $ty; + fn as_precise(self) -> Self::Out { + self + } + } + }; +} + +macro_rules! as_single_self { + ($ty:ty) => { + impl AsSingle for $ty { + type Out = $ty; + fn as_single(self) -> Self::Out { + self + } + } + }; +} + +as_single_self!(f32); +as_single_self!(Vec2); +as_single_self!(Vec3); +as_single_self!(Vec3A); +as_single_self!(Vec4); +as_single_self!(Quat); + +impl AsSingle for f64 { + type Out = f32; + fn as_single(self) -> Self::Out { + self as f32 + } +} + +impl AsSingle for DVec2 { + type Out = Vec2; + fn as_single(self) -> Self::Out { + self.as_vec2() + } +} + +impl AsSingle for DVec3 { + type Out = Vec3; + fn as_single(self) -> Self::Out { + self.as_vec3() + } +} + +impl AsSingle for DVec4 { + type Out = Vec4; + fn as_single(self) -> Self::Out { + self.as_vec4() + } +} + +impl AsSingle for DQuat { + type Out = Quat; + fn as_single(self) -> Self::Out { + self.as_f32() + } +} + +#[cfg(feature = "f32")] +mod real { + use super::{AsPrecise, AsSingle}; + use bevy::math::*; + + as_precise_self!(f32); + as_precise_self!(Vec2); + as_precise_self!(Vec3); + as_precise_self!(Vec3A); + as_precise_self!(Vec4); + as_precise_self!(Quat); + + impl AsPrecise for f64 { + type Out = f32; + fn as_precise(self) -> Self::Out { + self.as_single() + } + } + + impl AsPrecise for DVec2 { + type Out = Vec2; + fn as_precise(self) -> Self::Out { + self.as_single() + } + } + + impl AsPrecise for DVec3 { + type Out = Vec3; + fn as_precise(self) -> Self::Out { + self.as_single() + } + } + + impl AsPrecise for DVec4 { + type Out = Vec4; + fn as_precise(self) -> Self::Out { + self.as_single() + } + } + + impl AsPrecise for DQuat { + type Out = Quat; + fn as_precise(self) -> Self::Out { + self.as_single() + } + } +} + +#[cfg(feature = "f64")] +mod real { + use super::AsPrecise; + use bevy::math::*; + + as_precise_self!(f64); + as_precise_self!(DVec2); + as_precise_self!(DVec3); + as_precise_self!(DVec4); + as_precise_self!(DQuat); + + impl AsPrecise for f32 { + type Out = f64; + fn as_precise(self) -> Self::Out { + self as f64 + } + } + + impl AsPrecise for Vec2 { + type Out = DVec2; + fn as_precise(self) -> Self::Out { + self.as_dvec2() + } + } + + impl AsPrecise for Vec3 { + type Out = DVec3; + fn as_precise(self) -> Self::Out { + self.as_dvec3() + } + } + + impl AsPrecise for Vec3A { + type Out = DVec3; + fn as_precise(self) -> Self::Out { + self.as_dvec3() + } + } + + impl AsPrecise for Vec4 { + type Out = DVec4; + fn as_precise(self) -> Self::Out { + self.as_dvec4() + } + } + + impl AsPrecise for Quat { + type Out = DQuat; + fn as_precise(self) -> Self::Out { + self.as_f64() + } + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 5523dac0..dfd0bc6d 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,14 +1,21 @@ +use crate::math::*; use bevy::prelude::Transform; -use rapier::math::{Isometry, Real}; +use rapier::math::Isometry; + +/// Conversions to various precisions for interop reasons. +pub mod as_precise; /// Converts a Rapier isometry to a Bevy transform. /// /// The translation is multiplied by the `physics_scale`. #[cfg(feature = "dim2")] pub fn iso_to_transform(iso: &Isometry, physics_scale: Real) -> Transform { + use bevy::math::Quat; + let translation = Vect::from(iso.translation.vector) * physics_scale; + let rotation = Quat::from_rotation_z(iso.rotation.angle().as_single()); Transform { - translation: (iso.translation.vector.push(0.0) * physics_scale).into(), - rotation: bevy::prelude::Quat::from_rotation_z(iso.rotation.angle()), + translation: translation.as_single().extend(0.0), + rotation, ..Default::default() } } @@ -18,9 +25,11 @@ pub fn iso_to_transform(iso: &Isometry, physics_scale: Real) -> Transform /// The translation is multiplied by the `physics_scale`. #[cfg(feature = "dim3")] pub fn iso_to_transform(iso: &Isometry, physics_scale: Real) -> Transform { + let translation = (Vect::from(iso.translation.vector) * physics_scale).as_single(); + let rotation = Rot::from(iso.rotation).as_single(); Transform { - translation: (iso.translation.vector * physics_scale).into(), - rotation: iso.rotation.into(), + translation, + rotation, ..Default::default() } } @@ -30,11 +39,10 @@ pub fn iso_to_transform(iso: &Isometry, physics_scale: Real) -> Transform /// The translation is divided by the `physics_scale`. #[cfg(feature = "dim2")] pub(crate) fn transform_to_iso(transform: &Transform, physics_scale: Real) -> Isometry { - use bevy::math::Vec3Swizzles; - Isometry::new( - (transform.translation / physics_scale).xy().into(), - transform.rotation.to_scaled_axis().z, - ) + use bevy::math::{EulerRot, Vec3Swizzles}; + let translation = transform.translation.as_precise() / physics_scale; + let rotation = transform.rotation.to_euler(EulerRot::ZYX).0.as_precise(); + Isometry::new(translation.xy().into(), rotation) } /// Converts a Bevy transform to a Rapier isometry. @@ -42,10 +50,9 @@ pub(crate) fn transform_to_iso(transform: &Transform, physics_scale: Real) -> Is /// The translation is divided by the `physics_scale`. #[cfg(feature = "dim3")] pub(crate) fn transform_to_iso(transform: &Transform, physics_scale: Real) -> Isometry { - Isometry::from_parts( - (transform.translation / physics_scale).into(), - transform.rotation.into(), - ) + let translation = transform.translation.as_precise() / physics_scale; + let rotation = transform.rotation.as_precise(); + Isometry::from_parts(translation.into(), rotation.into()) } #[cfg(test)]