diff --git a/CHANGELOG.md b/CHANGELOG.md index 61567fa5..fbb0cc16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## Unreleased +### Modified +- The `PhysicsHooksWithQuery` trait has been renamed to by the `BevyPhysicsHooks`. +- Bevy resources and queries accessed by the physics hook are now specified by the implementor of `BevyPhysicsHooks` + which must derive Bevy’s `SystemParam` trait. This implies that the physics hook’s `filter_contact_pair` (and + all its other methods) no longer take the Bevy `Query` as argument. Queries and resources are accessed through + `self`. + ## 0.20.0 (15 Jan. 2023) ### Added - Add the `RigidBodyDisabled` and `ColliderDisabled` component that can be inserted to disable a rigid-body diff --git a/bevy_rapier2d/examples/contact_filter2.rs b/bevy_rapier2d/examples/contact_filter2.rs index a8096a5d..37dc0d08 100644 --- a/bevy_rapier2d/examples/contact_filter2.rs +++ b/bevy_rapier2d/examples/contact_filter2.rs @@ -1,4 +1,4 @@ -use bevy::prelude::*; +use bevy::{ecs::system::SystemParam, prelude::*}; use bevy_rapier2d::prelude::*; #[derive(PartialEq, Eq, Clone, Copy, Component)] @@ -11,15 +11,15 @@ enum CustomFilterTag { // same user_data value. // Note that using collision groups would be a more efficient way of doing // this, but we use custom filters instead for demonstration purpose. -struct SameUserDataFilter; -impl<'a> PhysicsHooksWithQuery<&'a CustomFilterTag> for SameUserDataFilter { - fn filter_contact_pair( - &self, - context: PairFilterContextView, - tags: &Query<&'a CustomFilterTag>, - ) -> Option { - if tags.get(context.collider1()).ok().copied() - == tags.get(context.collider2()).ok().copied() +#[derive(SystemParam)] +struct SameUserDataFilter<'w, 's> { + tags: Query<'w, 's, &'static CustomFilterTag>, +} + +impl BevyPhysicsHooks for SameUserDataFilter<'_, '_> { + fn filter_contact_pair(&self, context: PairFilterContextView) -> Option { + if self.tags.get(context.collider1()).ok().copied() + == self.tags.get(context.collider2()).ok().copied() { Some(SolverFlags::COMPUTE_IMPULSES) } else { @@ -36,7 +36,7 @@ fn main() { 0xFF as f32 / 255.0, ))) .add_plugins(DefaultPlugins) - .add_plugin(RapierPhysicsPlugin::<&CustomFilterTag>::pixels_per_meter( + .add_plugin(RapierPhysicsPlugin::::pixels_per_meter( 100.0, )) .add_plugin(RapierDebugRenderPlugin::default()) @@ -56,10 +56,6 @@ pub fn setup_physics(mut commands: Commands) { /* * Ground */ - commands.insert_resource(PhysicsHooksWithQueryResource(Box::new( - SameUserDataFilter {}, - ))); - let ground_size = 100.0; commands.spawn(( diff --git a/bevy_rapier3d/examples/contact_filter3.rs b/bevy_rapier3d/examples/contact_filter3.rs index 172aef5a..7a5ebfaa 100644 --- a/bevy_rapier3d/examples/contact_filter3.rs +++ b/bevy_rapier3d/examples/contact_filter3.rs @@ -1,4 +1,4 @@ -use bevy::prelude::*; +use bevy::{ecs::system::SystemParam, prelude::*}; use bevy_rapier3d::prelude::*; #[derive(PartialEq, Eq, Clone, Copy, Component)] @@ -11,15 +11,15 @@ enum CustomFilterTag { // same user_data value. // Note that using collision groups would be a more efficient way of doing // this, but we use custom filters instead for demonstration purpose. -struct SameUserDataFilter; -impl<'a> PhysicsHooksWithQuery<&'a CustomFilterTag> for SameUserDataFilter { - fn filter_contact_pair( - &self, - context: PairFilterContextView, - tags: &Query<&'a CustomFilterTag>, - ) -> Option { - if tags.get(context.collider1()).ok().copied() - == tags.get(context.collider2()).ok().copied() +#[derive(SystemParam)] +struct SameUserDataFilter<'w, 's> { + tags: Query<'w, 's, &'static CustomFilterTag>, +} + +impl BevyPhysicsHooks for SameUserDataFilter<'_, '_> { + fn filter_contact_pair(&self, context: PairFilterContextView) -> Option { + if self.tags.get(context.collider1()).ok().copied() + == self.tags.get(context.collider2()).ok().copied() { Some(SolverFlags::COMPUTE_IMPULSES) } else { @@ -36,7 +36,7 @@ fn main() { 0xFF as f32 / 255.0, ))) .add_plugins(DefaultPlugins) - .add_plugin(RapierPhysicsPlugin::<&CustomFilterTag>::default()) + .add_plugin(RapierPhysicsPlugin::::default()) .add_plugin(RapierDebugRenderPlugin::default()) .add_startup_system(setup_graphics) .add_startup_system(setup_physics) @@ -55,10 +55,6 @@ pub fn setup_physics(mut commands: Commands) { /* * Ground */ - commands.insert_resource(PhysicsHooksWithQueryResource(Box::new( - SameUserDataFilter {}, - ))); - let ground_size = 10.0; commands.spawn(( diff --git a/src/pipeline/mod.rs b/src/pipeline/mod.rs index 76b64740..f857e1f9 100644 --- a/src/pipeline/mod.rs +++ b/src/pipeline/mod.rs @@ -1,9 +1,8 @@ pub(crate) use self::events::EventQueue; pub use self::events::{CollisionEvent, ContactForceEvent}; -pub(crate) use self::physics_hooks::PhysicsHooksWithQueryInstance; +pub(crate) use self::physics_hooks::BevyPhysicsHooksAdapter; pub use self::physics_hooks::{ - ContactModificationContextView, PairFilterContextView, PhysicsHooksWithQuery, - PhysicsHooksWithQueryResource, + BevyPhysicsHooks, ContactModificationContextView, PairFilterContextView, }; pub use query_filter::{QueryFilter, QueryFilterFlags}; diff --git a/src/pipeline/physics_hooks.rs b/src/pipeline/physics_hooks.rs index 7768b62a..2ce9e30b 100644 --- a/src/pipeline/physics_hooks.rs +++ b/src/pipeline/physics_hooks.rs @@ -1,7 +1,11 @@ -use bevy::ecs::query::WorldQuery; -use bevy::prelude::*; -use rapier::geometry::SolverFlags; -use rapier::pipeline::{ContactModificationContext, PairFilterContext, PhysicsHooks}; +use bevy::{ + ecs::system::{SystemParam, SystemParamFetch, SystemParamItem}, + prelude::*, +}; +use rapier::{ + pipeline::{ContactModificationContext, PairFilterContext}, + prelude::{PhysicsHooks, SolverFlags}, +}; /// Read-only access to the properties of a collision pair filter context. pub struct PairFilterContextView<'a> { @@ -80,7 +84,7 @@ impl<'a, 'b> ContactModificationContextView<'a, 'b> { } /// User-defined functions called by the physics engines during one timestep in order to customize its behavior. -pub trait PhysicsHooksWithQuery: Send + Sync { +pub trait BevyPhysicsHooks: SystemParam + Send + Sync { /// Applies the contact pair filter. /// /// Note that this method will only be called if at least one of the colliders @@ -105,11 +109,7 @@ pub trait PhysicsHooksWithQuery: Send + Sync { /// will be taken into account by the constraints solver. If this returns /// `Some(SolverFlags::empty())` then the constraints solver will ignore these /// contacts. - fn filter_contact_pair( - &self, - _context: PairFilterContextView, - _user_data: &Query, - ) -> Option { + fn filter_contact_pair(&self, _context: PairFilterContextView) -> Option { None } @@ -133,11 +133,7 @@ pub trait PhysicsHooksWithQuery: Send + Sync { /// not compute any intersection information for it. /// If this return `true` then the narrow-phase will compute intersection /// information for this pair. - fn filter_intersection_pair( - &self, - _context: PairFilterContextView, - _user_data: &Query, - ) -> bool { + fn filter_intersection_pair(&self, _context: PairFilterContextView) -> bool { false } @@ -166,68 +162,63 @@ pub trait PhysicsHooksWithQuery: Send + Sync { /// as 0 and can be modified in `context.user_data`. /// /// The world-space contact normal can be modified in `context.normal`. - fn modify_solver_contacts( - &self, - _context: ContactModificationContextView, - _user_data: &Query, - ) { - } + fn modify_solver_contacts(&self, _context: ContactModificationContextView) {} } -impl PhysicsHooksWithQuery for T +impl BevyPhysicsHooks for T where - T: PhysicsHooks + Send + Sync, - UserData: WorldQuery, + T: 'static + PhysicsHooks + SystemParam + Send + Sync, + for<'w, 's> T::Fetch: SystemParamFetch<'w, 's, Item = T>, { - fn filter_contact_pair( - &self, - context: PairFilterContextView, - _: &Query, - ) -> Option { + fn filter_contact_pair(&self, context: PairFilterContextView) -> Option { PhysicsHooks::filter_contact_pair(self, context.raw) } - fn filter_intersection_pair( - &self, - context: PairFilterContextView, - _: &Query, - ) -> bool { + fn filter_intersection_pair(&self, context: PairFilterContextView) -> bool { PhysicsHooks::filter_intersection_pair(self, context.raw) } - fn modify_solver_contacts(&self, context: ContactModificationContextView, _: &Query) { + fn modify_solver_contacts(&self, context: ContactModificationContextView) { PhysicsHooks::modify_solver_contacts(self, context.raw) } } -/// Resource containing the user-defined physics hooks. -#[derive(Resource)] -pub struct PhysicsHooksWithQueryResource( - pub Box>, -); +/// Adapts a type implementing `BevyPhysicsHooks` so that it implements `PhysicsHooks`. +pub(crate) struct BevyPhysicsHooksAdapter<'w, 's, Hooks> +where + Hooks: 'static + BevyPhysicsHooks, + for<'w1, 's1> SystemParamItem<'w1, 's1, Hooks>: BevyPhysicsHooks, +{ + hooks: SystemParamItem<'w, 's, Hooks>, +} -pub(crate) struct PhysicsHooksWithQueryInstance<'world, 'state, 'b, UserData: WorldQuery> { - // pub commands: Commands<'world, 'state>, - pub user_data: Query<'world, 'state, UserData>, - pub hooks: &'b dyn PhysicsHooksWithQuery, +impl<'w, 's, Hooks> BevyPhysicsHooksAdapter<'w, 's, Hooks> +where + Hooks: 'static + BevyPhysicsHooks, + for<'w1, 's1> SystemParamItem<'w1, 's1, Hooks>: BevyPhysicsHooks, +{ + pub(crate) fn new(hooks: SystemParamItem<'w, 's, Hooks>) -> Self { + Self { hooks } + } } -impl PhysicsHooks for PhysicsHooksWithQueryInstance<'_, '_, '_, UserData> { +impl<'w, 's, Hooks> PhysicsHooks for BevyPhysicsHooksAdapter<'w, 's, Hooks> +where + Hooks: 'static + BevyPhysicsHooks, + for<'w1, 's1> SystemParamItem<'w1, 's1, Hooks>: BevyPhysicsHooks, +{ fn filter_contact_pair(&self, context: &PairFilterContext) -> Option { let context_view = PairFilterContextView { raw: context }; - self.hooks - .filter_contact_pair(context_view, &self.user_data) + self.hooks.filter_contact_pair(context_view) } fn filter_intersection_pair(&self, context: &PairFilterContext) -> bool { let context_view = PairFilterContextView { raw: context }; - self.hooks - .filter_intersection_pair(context_view, &self.user_data) + self.hooks.filter_intersection_pair(context_view) } fn modify_solver_contacts(&self, context: &mut ContactModificationContext) { let context_view = ContactModificationContextView { raw: context }; - self.hooks - .modify_solver_contacts(context_view, &self.user_data) + self.hooks.modify_solver_contacts(context_view) } } diff --git a/src/plugin/plugin.rs b/src/plugin/plugin.rs index ca7a6450..ffd761dd 100644 --- a/src/plugin/plugin.rs +++ b/src/plugin/plugin.rs @@ -1,8 +1,9 @@ -use crate::pipeline::{CollisionEvent, ContactForceEvent, PhysicsHooksWithQueryResource}; +use crate::pipeline::{CollisionEvent, ContactForceEvent}; use crate::plugin::configuration::SimulationToRenderTime; use crate::plugin::{systems, RapierConfiguration, RapierContext}; use crate::prelude::*; -use bevy::ecs::{event::Events, query::WorldQuery}; +use bevy::ecs::event::Events; +use bevy::ecs::system::SystemParamItem; use bevy::prelude::*; use std::marker::PhantomData; @@ -13,13 +14,17 @@ pub type NoUserData = (); // // This will automatically setup all the resources needed to run a physics simulation with the // Rapier physics engine. -pub struct RapierPhysicsPlugin { +pub struct RapierPhysicsPlugin { physics_scale: f32, default_system_setup: bool, - _phantom: PhantomData, + _phantom: PhantomData, } -impl RapierPhysicsPlugin { +impl RapierPhysicsPlugin +where + PhysicsHooks: 'static + BevyPhysicsHooks, + for<'w, 's> SystemParamItem<'w, 's, PhysicsHooks>: BevyPhysicsHooks, +{ /// Specifies a scale ratio between the physics world and the bevy transforms. /// /// This affects the size of every elements in the physics engine, by multiplying @@ -112,14 +117,14 @@ impl RapierPhysicsPlugin

SystemSet::new() - .with_system(systems::step_simulation::) + .with_system(systems::step_simulation::) .with_system( Events::::update_system - .before(systems::step_simulation::), + .before(systems::step_simulation::), ) .with_system( Events::::update_system - .before(systems::step_simulation::), + .before(systems::step_simulation::), ), PhysicsStages::Writeback => SystemSet::new() .with_system(systems::update_colliding_entities) @@ -129,7 +134,7 @@ impl RapierPhysicsPlugin

Default for RapierPhysicsPlugin { +impl Default for RapierPhysicsPlugin { fn default() -> Self { Self { physics_scale: 1.0, @@ -163,8 +168,10 @@ pub enum PhysicsStages { DetectDespawn, } -impl Plugin - for RapierPhysicsPlugin +impl Plugin for RapierPhysicsPlugin +where + PhysicsHooks: 'static + BevyPhysicsHooks, + for<'w, 's> SystemParamItem<'w, 's, PhysicsHooks>: BevyPhysicsHooks, { fn build(&self, app: &mut App) { // Register components as reflectable. @@ -232,15 +239,5 @@ impl Plugin .with_system_set(Self::get_systems(PhysicsStages::DetectDespawn)), ); } - - if app - .world - .get_resource::>() - .is_none() - { - app.insert_resource(PhysicsHooksWithQueryResource::(Box::new( - (), - ))); - } } } diff --git a/src/plugin/systems.rs b/src/plugin/systems.rs index 46e91f3f..c11ba7e7 100644 --- a/src/plugin/systems.rs +++ b/src/plugin/systems.rs @@ -11,17 +11,15 @@ use crate::geometry::{ ColliderMassProperties, ColliderScale, CollisionGroups, ContactForceEventThreshold, Friction, RapierColliderHandle, Restitution, Sensor, SolverGroups, }; -use crate::pipeline::{ - CollisionEvent, ContactForceEvent, PhysicsHooksWithQueryInstance, PhysicsHooksWithQueryResource, -}; +use crate::pipeline::{CollisionEvent, ContactForceEvent}; use crate::plugin::configuration::{SimulationToRenderTime, TimestepMode}; use crate::plugin::{RapierConfiguration, RapierContext}; use crate::prelude::{ - CollidingEntities, KinematicCharacterController, KinematicCharacterControllerOutput, - RigidBodyDisabled, + BevyPhysicsHooks, BevyPhysicsHooksAdapter, CollidingEntities, KinematicCharacterController, + KinematicCharacterControllerOutput, RigidBodyDisabled, }; use crate::utils; -use bevy::ecs::query::WorldQuery; +use bevy::ecs::system::SystemParamItem; use bevy::prelude::*; use rapier::prelude::*; use std::collections::HashMap; @@ -630,29 +628,27 @@ pub fn writeback_rigid_bodies( /// System responsible for advancing the physics simulation, and updating the internal state /// for scene queries. -pub fn step_simulation( +pub fn step_simulation( mut context: ResMut, config: Res, - hooks: Res>, + hooks: SystemParamItem, (time, mut sim_to_render_time): (Res