From d4ff57e4721c355644239540fbbe05ded3b3ff5b Mon Sep 17 00:00:00 2001 From: Thierry Berger Date: Tue, 25 Jun 2024 10:15:33 +0200 Subject: [PATCH 01/31] working rapierContext as component, but not ideal API. --- CHANGELOG.md | 12 ++++ bevy_rapier2d/examples/testbed2.rs | 3 +- bevy_rapier3d/examples/ray_casting3.rs | 3 +- src/plugin/configuration.rs | 42 +++++++------ src/plugin/context.rs | 5 +- src/plugin/plugin.rs | 70 +++++++++++++++------- src/plugin/systems/character_controller.rs | 8 ++- src/plugin/systems/collider.rs | 18 ++++-- src/plugin/systems/joint.rs | 7 ++- src/plugin/systems/mod.rs | 25 ++++---- src/plugin/systems/remove.rs | 4 +- src/plugin/systems/rigid_body.rs | 30 ++++++---- src/plugin/systems/writeback.rs | 7 ++- src/render/mod.rs | 3 +- 14 files changed, 154 insertions(+), 83 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e18bd28..6e4850b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## Unreleased + +### Changed + +- `RapierContext` is now a `Component` + - Rapier now supports multiple worlds. + - Migration guide: + - `ResMut` -> `Query<&mut RapierContext>` + - `Res` -> `Query<&RapierContext>` + - `Query<&mut RapierConfiguration>` -> `Query<&mut RapierConfiguration>` + - `Query<&RapierConfiguration>` -> `Query<&RapierConfiguration>` + ## v0.27.0-rc.1 (18 June 2024) **This is an update to Rapier 0.20 which includes several stability improvements diff --git a/bevy_rapier2d/examples/testbed2.rs b/bevy_rapier2d/examples/testbed2.rs index e7a52a97..8999324e 100644 --- a/bevy_rapier2d/examples/testbed2.rs +++ b/bevy_rapier2d/examples/testbed2.rs @@ -147,7 +147,8 @@ fn main() { OnExit(Examples::PlayerMovement2), ( cleanup, - |mut rapier_config: ResMut, ctxt: Res| { + |mut rapier_config: ResMut, + ctxt: Query<&mut RapierContext>| { rapier_config.gravity = RapierConfiguration::new(ctxt.integration_parameters.length_unit).gravity; }, diff --git a/bevy_rapier3d/examples/ray_casting3.rs b/bevy_rapier3d/examples/ray_casting3.rs index 0840fe7d..0a49bebd 100644 --- a/bevy_rapier3d/examples/ray_casting3.rs +++ b/bevy_rapier3d/examples/ray_casting3.rs @@ -76,10 +76,11 @@ pub fn setup_physics(mut commands: Commands) { pub fn cast_ray( mut commands: Commands, windows: Query<&Window, With>, - rapier_context: Res, + mut rapier_context: Query<&mut RapierContext>, cameras: Query<(&Camera, &GlobalTransform)>, ) { let window = windows.single(); + let rapier_context = &mut *rapier_context.single_mut(); let Some(cursor_position) = window.cursor_position() else { return; diff --git a/src/plugin/configuration.rs b/src/plugin/configuration.rs index 8d4b7d3b..84a747fa 100644 --- a/src/plugin/configuration.rs +++ b/src/plugin/configuration.rs @@ -1,17 +1,10 @@ -use bevy::prelude::{FromWorld, Resource, World}; +use bevy::prelude::{default, Component, FromWorld, Resource, World}; use crate::math::{Real, Vect}; use crate::plugin::RapierContext; -/// Difference between simulation and rendering time -#[derive(Resource, Default)] -pub struct SimulationToRenderTime { - /// Difference between simulation and rendering time - pub diff: f32, -} - -/// The different ways of adjusting the timestep length. -#[derive(Copy, Clone, Debug, PartialEq)] +/// The different ways of adjusting the timestep length each frame. +#[derive(Copy, Clone, Debug, PartialEq, Resource)] pub enum TimestepMode { /// Use a fixed timestep: the physics simulation will be advanced by the fixed value /// `dt` seconds at each Bevy tick by performing `substeps` of length `dt / substeps`. @@ -50,7 +43,24 @@ pub enum TimestepMode { }, } -#[derive(Resource, Copy, Clone, Debug)] +/// Difference between simulation and rendering time +#[derive(Component, Default)] +pub struct SimulationToRenderTime { + /// Difference between simulation and rendering time + pub diff: f32, +} + +impl Default for TimestepMode { + fn default() -> Self { + TimestepMode::Variable { + max_dt: 1.0 / 60.0, + time_scale: 1.0, + substeps: 1, + } + } +} + +#[derive(Component, Copy, Clone, Debug)] /// A resource for specifying configuration information for the physics simulation pub struct RapierConfiguration { /// Specifying the gravity of the physics simulation. @@ -59,8 +69,6 @@ pub struct RapierConfiguration { pub physics_pipeline_active: bool, /// Specifies if the query pipeline is active and update the query pipeline. pub query_pipeline_active: bool, - /// Specifies the way the timestep length should be adjusted at each frame. - pub timestep_mode: TimestepMode, /// Specifies the number of subdivisions along each axes a shape should be subdivided /// if its scaled representation cannot be represented with the same shape type. /// @@ -73,6 +81,7 @@ pub struct RapierConfiguration { pub force_update_from_transform_changes: bool, } +/* impl FromWorld for RapierConfiguration { fn from_world(world: &mut World) -> Self { let length_unit = world @@ -81,7 +90,7 @@ impl FromWorld for RapierConfiguration { .unwrap_or(1.0); Self::new(length_unit) } -} +}*/ impl RapierConfiguration { /// Configures rapier with the specified length unit. @@ -95,11 +104,6 @@ impl RapierConfiguration { gravity: Vect::Y * -9.81 * length_unit, physics_pipeline_active: true, query_pipeline_active: true, - timestep_mode: TimestepMode::Variable { - max_dt: 1.0 / 60.0, - time_scale: 1.0, - substeps: 1, - }, scaled_shape_subdivision: 10, force_update_from_transform_changes: false, } diff --git a/src/plugin/context.rs b/src/plugin/context.rs index 4d39149b..294c9cd3 100644 --- a/src/plugin/context.rs +++ b/src/plugin/context.rs @@ -22,9 +22,12 @@ use crate::prelude::{CollisionGroups, RapierRigidBodyHandle}; use rapier::control::CharacterAutostep; use rapier::geometry::DefaultBroadPhase; +#[derive(Component, Reflect)] +pub struct DefaultContext; + /// The Rapier context, containing all the state of the physics engine. #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] -#[derive(Resource)] +#[derive(Component)] pub struct RapierContext { /// The island manager, which detects what object is sleeping /// (not moving much) to reduce computations. diff --git a/src/plugin/plugin.rs b/src/plugin/plugin.rs index 7864f558..8ffe7ac0 100644 --- a/src/plugin/plugin.rs +++ b/src/plugin/plugin.rs @@ -20,8 +20,9 @@ pub type NoUserData = (); /// Rapier physics engine. pub struct RapierPhysicsPlugin { schedule: Interned, - length_unit: f32, default_system_setup: bool, + /// Read in PreStartup, this will create a default world. + pub default_world_setup: Option, _phantom: PhantomData, } @@ -37,7 +38,9 @@ where /// likely always be 1.0 in 3D. In 2D, this is useful to specify a "pixels-per-meter" /// conversion ratio. pub fn with_length_unit(mut self, length_unit: f32) -> Self { - self.length_unit = length_unit; + self.default_world_setup = Some(RapierContextInitialization { + length_unit: length_unit, + }); self } @@ -56,8 +59,10 @@ where #[cfg(feature = "dim2")] pub fn pixels_per_meter(pixels_per_meter: f32) -> Self { Self { - length_unit: pixels_per_meter, default_system_setup: true, + default_world_setup: Some(RapierContextInitialization { + length_unit: pixels_per_meter, + }), ..default() } } @@ -127,8 +132,8 @@ impl Default for RapierPhysicsPlugin Self { Self { schedule: PostUpdate.intern(), - length_unit: 1.0, default_system_setup: true, + default_world_setup: Some(RapierContextInitialization { length_unit: 1f32 }), _phantom: PhantomData, } } @@ -183,24 +188,19 @@ where .register_type::() .register_type::(); - app.insert_resource(SimulationToRenderTime::default()) - .insert_resource(RapierContext { - integration_parameters: IntegrationParameters { - length_unit: self.length_unit, - ..Default::default() - }, - ..Default::default() - }) - .insert_resource(Events::::default()) + app.insert_resource(Events::::default()) .insert_resource(Events::::default()) .insert_resource(Events::::default()); - // Insert all of our required resources. Don’t overwrite - // the `RapierConfiguration` if it already exists. - // - // NOTE: be sure to call this after the `.insert_resource(RapierContext)` so we can - // access the length_unit when initializing the RapierConfiguration. - app.init_resource::(); + app.add_systems( + self.schedule, + ( + setup_rapier_configuration, + setup_rapier_simulation_to_render_time, + ) + .before(PhysicsSet::SyncBackend), + ); + app.add_systems(PreStartup, insert_default_world); // Add each set as necessary if self.default_system_setup { @@ -227,11 +227,12 @@ where Self::get_systems(PhysicsSet::Writeback).in_set(PhysicsSet::Writeback), ), ); + app.init_resource::(); // Warn user if the timestep mode isn't in Fixed if self.schedule.as_dyn_eq().dyn_eq(FixedUpdate.as_dyn_eq()) { - let config = app.world_mut().resource::(); - match config.timestep_mode { + let config = app.world_mut().resource::(); + match config { TimestepMode::Fixed { .. } => {} mode => { warn!("TimestepMode is set to `{:?}`, it is recommended to use `TimestepMode::Fixed` if you have the physics in `FixedUpdate`", mode); @@ -241,3 +242,30 @@ where } } } + +pub struct RapierContextInitialization { + pub length_unit: f32, +} + +pub fn insert_default_world(mut commands: Commands) { + commands.spawn(RapierContext::default()); +} + +pub fn setup_rapier_configuration( + mut commands: Commands, + rapier_context: Query<(Entity, &RapierContext), Without>, +) { + for (e, rapier_context) in rapier_context.iter() { + commands.entity(e).insert(RapierConfiguration::new( + rapier_context.integration_parameters.length_unit, + )); + } +} +pub fn setup_rapier_simulation_to_render_time( + mut commands: Commands, + rapier_context: Query, Without)>, +) { + for e in rapier_context.iter() { + commands.entity(e).insert(SimulationToRenderTime::default()); + } +} diff --git a/src/plugin/systems/character_controller.rs b/src/plugin/systems/character_controller.rs index 57e05da0..b1763c74 100644 --- a/src/plugin/systems/character_controller.rs +++ b/src/plugin/systems/character_controller.rs @@ -15,8 +15,8 @@ use rapier::pipeline::QueryFilter; /// collider. pub fn update_character_controls( mut commands: Commands, - config: Res, - mut context: ResMut, + config: Query<&RapierConfiguration>, + mut context: Query<&mut RapierContext>, mut character_controllers: Query<( Entity, &mut KinematicCharacterController, @@ -27,7 +27,9 @@ pub fn update_character_controls( )>, mut transforms: Query<&mut Transform>, ) { - let context = &mut *context; + let context = &mut *context.single_mut(); + let config = &*config.single(); + for (entity, mut controller, output, collider_handle, body_handle, glob_transform) in character_controllers.iter_mut() { diff --git a/src/plugin/systems/collider.rs b/src/plugin/systems/collider.rs index f58a78b0..a1908fdb 100644 --- a/src/plugin/systems/collider.rs +++ b/src/plugin/systems/collider.rs @@ -41,7 +41,7 @@ pub type ColliderComponents<'a> = ( /// System responsible for applying [`GlobalTransform::scale`] and/or [`ColliderScale`] to /// colliders. pub fn apply_scale( - config: Res, + config: Query<&RapierConfiguration>, mut changed_collider_scales: Query< (&mut Collider, &GlobalTransform, Option<&ColliderScale>), Or<( @@ -51,6 +51,8 @@ pub fn apply_scale( )>, >, ) { + let config = &*config.single(); + for (mut shape, transform, custom_scale) in changed_collider_scales.iter_mut() { #[cfg(feature = "dim2")] let effective_scale = match custom_scale { @@ -75,8 +77,8 @@ pub fn apply_scale( /// System responsible for applying changes the user made to a collider-related component. pub fn apply_collider_user_changes( - mut context: ResMut, - config: Res, + mut context: Query<&mut RapierContext>, + config: Query<&RapierConfiguration>, (changed_collider_transforms, parent_query, transform_query): ( Query< (Entity, &RapierColliderHandle, &GlobalTransform), @@ -116,6 +118,9 @@ pub fn apply_collider_user_changes( mut mass_modified: EventWriter, ) { + let context = &mut *context.single_mut(); + let config = &*config.single(); + for (entity, handle, transform) in changed_collider_transforms.iter() { if context.collider_parent(entity).is_some() { let (_, collider_position) = @@ -269,14 +274,15 @@ pub(crate) fn collider_offset( /// System responsible for creating new Rapier colliders from the related `bevy_rapier` components. pub fn init_colliders( mut commands: Commands, - config: Res, - mut context: ResMut, + config: Query<&RapierConfiguration>, + mut context: Query<&mut RapierContext>, colliders: Query<(ColliderComponents, Option<&GlobalTransform>), Without>, mut rigid_body_mprops: Query<&mut ReadMassProperties>, parent_query: Query<&Parent>, transform_query: Query<&Transform>, ) { - let context = &mut *context; + let context = &mut *context.single_mut(); + let config = &*config.single(); for ( ( diff --git a/src/plugin/systems/joint.rs b/src/plugin/systems/joint.rs index ff79dcd1..8bfe750d 100644 --- a/src/plugin/systems/joint.rs +++ b/src/plugin/systems/joint.rs @@ -8,12 +8,12 @@ use bevy::prelude::*; /// System responsible for creating new Rapier joints from the related `bevy_rapier` components. pub fn init_joints( mut commands: Commands, - mut context: ResMut, + mut context: Query<&mut RapierContext>, impulse_joints: Query<(Entity, &ImpulseJoint), Without>, multibody_joints: Query<(Entity, &MultibodyJoint), Without>, parent_query: Query<&Parent>, ) { - let context = &mut *context; + let context = &mut *context.single_mut(); for (entity, joint) in impulse_joints.iter() { let mut target = None; @@ -61,7 +61,7 @@ pub fn init_joints( /// System responsible for applying changes the user made to a joint component. pub fn apply_joint_user_changes( - mut context: ResMut, + mut context: Query<&mut RapierContext>, changed_impulse_joints: Query< (&RapierImpulseJointHandle, &ImpulseJoint), Changed, @@ -71,6 +71,7 @@ pub fn apply_joint_user_changes( Changed, >, ) { + let mut context = context.single_mut(); // TODO: right now, we only support propagating changes made to the joint data. // Re-parenting the joint isn’t supported yet. for (handle, changed_joint) in changed_impulse_joints.iter() { diff --git a/src/plugin/systems/mod.rs b/src/plugin/systems/mod.rs index c513856c..f362539f 100644 --- a/src/plugin/systems/mod.rs +++ b/src/plugin/systems/mod.rs @@ -17,7 +17,7 @@ pub use writeback::*; use crate::dynamics::{RapierRigidBodyHandle, TransformInterpolation}; use crate::pipeline::{CollisionEvent, ContactForceEvent}; use crate::plugin::configuration::SimulationToRenderTime; -use crate::plugin::{RapierConfiguration, RapierContext}; +use crate::plugin::{RapierConfiguration, RapierContext, TimestepMode}; use crate::prelude::{BevyPhysicsHooks, BevyPhysicsHooksAdapter}; use bevy::ecs::system::{StaticSystemParam, SystemParamItem}; use bevy::prelude::*; @@ -25,11 +25,12 @@ use bevy::prelude::*; /// System responsible for advancing the physics simulation, and updating the internal state /// for scene queries. pub fn step_simulation( - mut context: ResMut, - config: Res, + mut context: Query<&mut RapierContext>, + timestep_mode: Res, + config: Query<&RapierConfiguration>, hooks: StaticSystemParam, time: Res