Skip to content

Commit

Permalink
Merge pull request #323 from bravely-beep/master
Browse files Browse the repository at this point in the history
Use SystemParam for physics hooks
  • Loading branch information
sebcrozet authored Feb 4, 2023
2 parents 0df92f3 + b89d5fe commit be96f14
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 121 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
26 changes: 11 additions & 15 deletions bevy_rapier2d/examples/contact_filter2.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use bevy::prelude::*;
use bevy::{ecs::system::SystemParam, prelude::*};
use bevy_rapier2d::prelude::*;

#[derive(PartialEq, Eq, Clone, Copy, Component)]
Expand All @@ -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<SolverFlags> {
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<SolverFlags> {
if self.tags.get(context.collider1()).ok().copied()
== self.tags.get(context.collider2()).ok().copied()
{
Some(SolverFlags::COMPUTE_IMPULSES)
} else {
Expand All @@ -36,7 +36,7 @@ fn main() {
0xFF as f32 / 255.0,
)))
.add_plugins(DefaultPlugins)
.add_plugin(RapierPhysicsPlugin::<&CustomFilterTag>::pixels_per_meter(
.add_plugin(RapierPhysicsPlugin::<SameUserDataFilter>::pixels_per_meter(
100.0,
))
.add_plugin(RapierDebugRenderPlugin::default())
Expand All @@ -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((
Expand Down
26 changes: 11 additions & 15 deletions bevy_rapier3d/examples/contact_filter3.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use bevy::prelude::*;
use bevy::{ecs::system::SystemParam, prelude::*};
use bevy_rapier3d::prelude::*;

#[derive(PartialEq, Eq, Clone, Copy, Component)]
Expand All @@ -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<SolverFlags> {
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<SolverFlags> {
if self.tags.get(context.collider1()).ok().copied()
== self.tags.get(context.collider2()).ok().copied()
{
Some(SolverFlags::COMPUTE_IMPULSES)
} else {
Expand All @@ -36,7 +36,7 @@ fn main() {
0xFF as f32 / 255.0,
)))
.add_plugins(DefaultPlugins)
.add_plugin(RapierPhysicsPlugin::<&CustomFilterTag>::default())
.add_plugin(RapierPhysicsPlugin::<SameUserDataFilter>::default())
.add_plugin(RapierDebugRenderPlugin::default())
.add_startup_system(setup_graphics)
.add_startup_system(setup_physics)
Expand All @@ -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((
Expand Down
5 changes: 2 additions & 3 deletions src/pipeline/mod.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down
93 changes: 42 additions & 51 deletions src/pipeline/physics_hooks.rs
Original file line number Diff line number Diff line change
@@ -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> {
Expand Down Expand Up @@ -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<UserData: WorldQuery>: 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
Expand All @@ -105,11 +109,7 @@ pub trait PhysicsHooksWithQuery<UserData: WorldQuery>: 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<UserData>,
) -> Option<SolverFlags> {
fn filter_contact_pair(&self, _context: PairFilterContextView) -> Option<SolverFlags> {
None
}

Expand All @@ -133,11 +133,7 @@ pub trait PhysicsHooksWithQuery<UserData: WorldQuery>: 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<UserData>,
) -> bool {
fn filter_intersection_pair(&self, _context: PairFilterContextView) -> bool {
false
}

Expand Down Expand Up @@ -166,68 +162,63 @@ pub trait PhysicsHooksWithQuery<UserData: WorldQuery>: 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<UserData>,
) {
}
fn modify_solver_contacts(&self, _context: ContactModificationContextView) {}
}

impl<T, UserData> PhysicsHooksWithQuery<UserData> for T
impl<T> 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<UserData>,
) -> Option<SolverFlags> {
fn filter_contact_pair(&self, context: PairFilterContextView) -> Option<SolverFlags> {
PhysicsHooks::filter_contact_pair(self, context.raw)
}

fn filter_intersection_pair(
&self,
context: PairFilterContextView,
_: &Query<UserData>,
) -> bool {
fn filter_intersection_pair(&self, context: PairFilterContextView) -> bool {
PhysicsHooks::filter_intersection_pair(self, context.raw)
}

fn modify_solver_contacts(&self, context: ContactModificationContextView, _: &Query<UserData>) {
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<UserData: WorldQuery>(
pub Box<dyn PhysicsHooksWithQuery<UserData>>,
);
/// 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<UserData>,
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<UserData: WorldQuery> 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<SolverFlags> {
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)
}
}
39 changes: 18 additions & 21 deletions src/plugin/plugin.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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<PhysicsHooksData = ()> {
pub struct RapierPhysicsPlugin<PhysicsHooks = ()> {
physics_scale: f32,
default_system_setup: bool,
_phantom: PhantomData<PhysicsHooksData>,
_phantom: PhantomData<PhysicsHooks>,
}

impl<PhysicsHooksData: 'static + WorldQuery + Send + Sync> RapierPhysicsPlugin<PhysicsHooksData> {
impl<PhysicsHooks> RapierPhysicsPlugin<PhysicsHooks>
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
Expand Down Expand Up @@ -112,14 +117,14 @@ impl<PhysicsHooksData: 'static + WorldQuery + Send + Sync> RapierPhysicsPlugin<P
}
}
PhysicsStages::StepSimulation => SystemSet::new()
.with_system(systems::step_simulation::<PhysicsHooksData>)
.with_system(systems::step_simulation::<PhysicsHooks>)
.with_system(
Events::<CollisionEvent>::update_system
.before(systems::step_simulation::<PhysicsHooksData>),
.before(systems::step_simulation::<PhysicsHooks>),
)
.with_system(
Events::<ContactForceEvent>::update_system
.before(systems::step_simulation::<PhysicsHooksData>),
.before(systems::step_simulation::<PhysicsHooks>),
),
PhysicsStages::Writeback => SystemSet::new()
.with_system(systems::update_colliding_entities)
Expand All @@ -129,7 +134,7 @@ impl<PhysicsHooksData: 'static + WorldQuery + Send + Sync> RapierPhysicsPlugin<P
}
}

impl<PhysicsHooksData> Default for RapierPhysicsPlugin<PhysicsHooksData> {
impl<PhysicsHooksSystemParam> Default for RapierPhysicsPlugin<PhysicsHooksSystemParam> {
fn default() -> Self {
Self {
physics_scale: 1.0,
Expand Down Expand Up @@ -163,8 +168,10 @@ pub enum PhysicsStages {
DetectDespawn,
}

impl<PhysicsHooksData: 'static + WorldQuery + Send + Sync> Plugin
for RapierPhysicsPlugin<PhysicsHooksData>
impl<PhysicsHooks> Plugin for RapierPhysicsPlugin<PhysicsHooks>
where
PhysicsHooks: 'static + BevyPhysicsHooks,
for<'w, 's> SystemParamItem<'w, 's, PhysicsHooks>: BevyPhysicsHooks,
{
fn build(&self, app: &mut App) {
// Register components as reflectable.
Expand Down Expand Up @@ -232,15 +239,5 @@ impl<PhysicsHooksData: 'static + WorldQuery + Send + Sync> Plugin
.with_system_set(Self::get_systems(PhysicsStages::DetectDespawn)),
);
}

if app
.world
.get_resource::<PhysicsHooksWithQueryResource<PhysicsHooksData>>()
.is_none()
{
app.insert_resource(PhysicsHooksWithQueryResource::<PhysicsHooksData>(Box::new(
(),
)));
}
}
}
Loading

0 comments on commit be96f14

Please sign in to comment.