Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rapier context as a Component #545

Merged
merged 36 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d4ff57e
working rapierContext as component, but not ideal API.
Vrixyz Jun 25, 2024
3cb7676
systemparam exploration
Vrixyz Jun 25, 2024
a01395e
RapierContextEntityLink to know which RapierContext is referred to
Vrixyz Jun 25, 2024
4e7c36c
more rapierContext conf + broken events -> added a test which should …
Vrixyz Jun 25, 2024
4952ea5
fix events test
Vrixyz Jun 26, 2024
7a4ba5e
better support for multi world
Vrixyz Jun 26, 2024
24fd036
multiple worlds working + example
Vrixyz Jun 26, 2024
f900dac
progress multi world with change
Vrixyz Jun 27, 2024
d171fe4
cargo clippy + pr feedbacks
Vrixyz Jul 1, 2024
8dd49ee
less unwraps
Vrixyz Jul 2, 2024
2b262ea
ci without debug symbols
Vrixyz Jul 2, 2024
f849cd2
more tests + logs + better changelog
Vrixyz Jul 2, 2024
df3578e
more error logs in case of no rapier context
Vrixyz Jul 2, 2024
ac16f38
Merge remote-tracking branch 'upstream' into RapierContext_Component
Vrixyz Jul 4, 2024
941b52e
fix conflict
Vrixyz Jul 4, 2024
5669a8c
fix unwrap
Vrixyz Jul 4, 2024
0b9ca86
fix warning
Vrixyz Jul 4, 2024
f4643ef
address pr feedback
Vrixyz Jul 5, 2024
f8b296a
address pr feedback
Vrixyz Jul 8, 2024
0e46610
Merge branch 'master' into RapierContext_Component
Vrixyz Jul 8, 2024
75c2195
fixed compilation
Vrixyz Jul 8, 2024
ccd2be7
fix doc comment
Vrixyz Jul 8, 2024
9142888
fixed joints3 example
Vrixyz Jul 8, 2024
ba0cd76
experiment on stepping to test system ordering and consistent ecs state
Vrixyz Jul 9, 2024
6d10fe6
fix compilation + warning
Vrixyz Jul 10, 2024
d0b07a0
better error handling when rapier context is missing
Vrixyz Jul 10, 2024
2676c35
add a name for rapier context entity
Vrixyz Jul 10, 2024
40be1f1
Merge branch 'master' into RapierContext_Component
Vrixyz Jul 23, 2024
deb0a06
rename an init function
Vrixyz Jul 23, 2024
cf165bd
apply pr suggestion
Vrixyz Jul 23, 2024
aeab8c9
apply PR suggestions
Vrixyz Aug 12, 2024
07eefbb
Merge branch 'master' into RapierContext_Component
Vrixyz Aug 12, 2024
4cccde1
rename rapier context system params
Vrixyz Aug 12, 2024
cbfe138
fix clippy (+ random typo)
Vrixyz Aug 12, 2024
5dc5b2d
cargo fmt
Vrixyz Aug 12, 2024
d0961d0
Merge remote-tracking branch 'upstream' into RapierContext_Component
Vrixyz Sep 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ which was its hardcoded behaviour.
`RapierDebugColliderPlugin` and `DebugRenderContext`, as well as individual collider setup via
a `ColliderDebug` component.

### Modified

- `RapierContext`, `RapierConfiguration` and `RenderToSimulationTime` are now a `Component` instead of resources.
- Rapier now supports multiple independent physics worlds, see example `multi_world3` for usage details.
- Migration guide:
- `ResMut<mut RapierContext>` -> `WriteDefaultRapierContext`
- `Res<RapierContext>` -> `ReadDefaultRapierContext`
- Access to `RapierConfiguration` and `RenderToSimulationTime` should query for it
on the responsible entity owning the `RenderContext`.
- If you are building a library on top of `bevy_rapier` and would want to support multiple independent physics worlds too,
you can check out the details of [#545](https://github.com/dimforge/bevy_rapier/pull/545)
to get more context and information.

## v0.27.0 (07 July 2024)

**This is an update from rapier 0.19 to Rapier 0.21 which includes several stability improvements
Expand Down
1 change: 1 addition & 0 deletions bevy_rapier2d/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ serde = { version = "1", features = ["derive"], optional = true }
bevy = { version = "0.14", default-features = false, features = [
"x11",
"bevy_state",
"bevy_debug_stepping",
] }
oorandom = "11"
approx = "0.5.1"
Expand Down
3 changes: 2 additions & 1 deletion bevy_rapier2d/examples/player_movement2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ fn main() {
#[derive(Component)]
pub struct Player(f32);

pub fn spawn_player(mut commands: Commands, mut rapier_config: ResMut<RapierConfiguration>) {
pub fn spawn_player(mut commands: Commands, mut rapier_config: Query<&mut RapierConfiguration>) {
let mut rapier_config = rapier_config.single_mut();
// Set gravity to 0.0 and spawn camera.
rapier_config.gravity = Vec2::ZERO;
commands.spawn(Camera2dBundle::default());
Expand Down
4 changes: 3 additions & 1 deletion bevy_rapier2d/examples/testbed2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,9 @@ fn main() {
OnExit(Examples::PlayerMovement2),
(
cleanup,
|mut rapier_config: ResMut<RapierConfiguration>, ctxt: Res<RapierContext>| {
|mut rapier_config: Query<&mut RapierConfiguration>,
ctxt: ReadDefaultRapierContext| {
let mut rapier_config = rapier_config.single_mut();
rapier_config.gravity =
RapierConfiguration::new(ctxt.integration_parameters.length_unit).gravity;
},
Expand Down
1 change: 1 addition & 0 deletions bevy_rapier3d/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ bevy = { version = "0.14", default-features = false, features = [
"x11",
"tonemapping_luts",
"bevy_state",
"bevy_debug_stepping",
] }
approx = "0.5.1"
glam = { version = "0.27", features = ["approx"] }
Expand Down
2 changes: 1 addition & 1 deletion bevy_rapier3d/examples/joints3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ pub fn setup_physics(mut commands: Commands) {
}

pub fn print_impulse_revolute_joints(
context: Res<RapierContext>,
context: ReadDefaultRapierContext,
joints: Query<(Entity, &ImpulseJoint)>,
) {
for (entity, impulse_joint) in joints.iter() {
Expand Down
120 changes: 120 additions & 0 deletions bevy_rapier3d/examples/multi_world3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
use bevy::{input::common_conditions::input_just_pressed, prelude::*};
use bevy_rapier3d::prelude::*;

const N_WORLDS: usize = 2;

fn main() {
App::new()
.insert_resource(ClearColor(Color::srgb(
0xF9 as f32 / 255.0,
0xF9 as f32 / 255.0,
0xFF as f32 / 255.0,
)))
.add_plugins((
DefaultPlugins,
RapierPhysicsPlugin::<NoUserData>::default()
.with_custom_initialization(RapierContextInitialization::NoAutomaticRapierContext),
RapierDebugRenderPlugin::default(),
))
.add_systems(
Startup,
((create_worlds, setup_physics).chain(), setup_graphics),
)
.add_systems(Update, move_platforms)
.add_systems(
Update,
change_world.run_if(input_just_pressed(KeyCode::KeyC)),
)
.run();
}

fn create_worlds(mut commands: Commands) {
for i in 0..N_WORLDS {
let mut world = commands.spawn((RapierContext::default(), WorldId(i)));
Vrixyz marked this conversation as resolved.
Show resolved Hide resolved
if i == 0 {
world.insert(DefaultRapierContext);
}
}
}

fn setup_graphics(mut commands: Commands) {
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(0.0, 3.0, -10.0)
.looking_at(Vec3::new(0.0, 0.0, 0.0), Vec3::Y),
..Default::default()
});
}

#[derive(Component)]
pub struct WorldId(pub usize);

#[derive(Component)]
struct Platform {
starting_y: f32,
}

fn move_platforms(time: Res<Time>, mut query: Query<(&mut Transform, &Platform)>) {
for (mut transform, platform) in query.iter_mut() {
transform.translation.y = platform.starting_y + -time.elapsed_seconds().sin();
}
}

/// Demonstrates how easy it is to move one entity to another world.
fn change_world(
query_context: Query<Entity, With<DefaultRapierContext>>,
mut query_links: Query<(Entity, &mut RapierContextEntityLink)>,
) {
let default_context = query_context.single();
for (e, mut link) in query_links.iter_mut() {
if link.0 == default_context {
continue;
}
link.0 = default_context;
println!("changing world of {} for world {}", e, link.0);
}
Vrixyz marked this conversation as resolved.
Show resolved Hide resolved
}

pub fn setup_physics(
context: Query<(Entity, &WorldId), With<RapierContext>>,
mut commands: Commands,
) {
for (context_entity, id) in context.iter() {
let id = id.0;

let color = [
Hsla::hsl(220.0, 1.0, 0.3),
Hsla::hsl(180.0, 1.0, 0.3),
Hsla::hsl(260.0, 1.0, 0.7),
][id % 3];

/*
* Ground
*/
let ground_size = 5.1;
let ground_height = 0.1;

let starting_y = (id as f32) * -0.5 - ground_height;

let mut platforms = commands.spawn((
TransformBundle::from(Transform::from_xyz(0.0, starting_y, 0.0)),
Collider::cuboid(ground_size, ground_height, ground_size),
ColliderDebugColor(color),
RapierContextEntityLink(context_entity),
));
if id == 1 {
platforms.insert(Platform { starting_y });
}

/*
* Create the cube
*/

commands.spawn((
TransformBundle::from(Transform::from_xyz(0.0, 1.0 + id as f32 * 5.0, 0.0)),
RigidBody::Dynamic,
Collider::cuboid(0.5, 0.5, 0.5),
ColliderDebugColor(color),
RapierContextEntityLink(context_entity),
));
}
}
2 changes: 1 addition & 1 deletion bevy_rapier3d/examples/ray_casting3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub fn setup_physics(mut commands: Commands) {
pub fn cast_ray(
mut commands: Commands,
windows: Query<&Window, With<PrimaryWindow>>,
rapier_context: Res<RapierContext>,
rapier_context: ReadDefaultRapierContext,
cameras: Query<(&Camera, &GlobalTransform)>,
) {
let window = windows.single();
Expand Down
5 changes: 4 additions & 1 deletion bevy_rapier_benches3d/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ pub fn custom_bencher(steps: usize, setup: impl Fn(&mut App)) {
app.update();
timer_full_update.pause();
let elapsed_time = timer_full_update.time() as f32;
let rc = app.world().resource::<RapierContext>();
let rc = app
.world_mut()
.query::<&RapierContext>()
.single(app.world());
rapier_step_times.push(rc.pipeline.counters.step_time.time() as f32);
total_update_times.push(elapsed_time);
}
Expand Down
32 changes: 13 additions & 19 deletions src/pipeline/events.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::math::{Real, Vect};
use bevy::prelude::{Entity, Event, EventWriter};
use bevy::prelude::{Entity, Event};
use rapier::dynamics::RigidBodySet;
use rapier::geometry::{
ColliderHandle, ColliderSet, CollisionEvent as RapierCollisionEvent, CollisionEventFlags,
Expand Down Expand Up @@ -58,8 +58,8 @@ pub(crate) struct EventQueue<'a> {
// Used to retrieve the entity of colliders that have been removed from the simulation
// since the last physics step.
pub deleted_colliders: &'a HashMap<ColliderHandle, Entity>,
pub collision_events: RwLock<EventWriter<'a, CollisionEvent>>,
pub contact_force_events: RwLock<EventWriter<'a, ContactForceEvent>>,
pub collision_events: RwLock<Vec<CollisionEvent>>,
pub contact_force_events: RwLock<Vec<ContactForceEvent>>,
}

impl<'a> EventQueue<'a> {
Expand Down Expand Up @@ -94,7 +94,7 @@ impl<'a> EventHandler for EventQueue<'a> {
};

if let Ok(mut events) = self.collision_events.write() {
events.send(event);
events.push(event);
}
}

Expand All @@ -118,7 +118,7 @@ impl<'a> EventHandler for EventQueue<'a> {
};

if let Ok(mut events) = self.contact_force_events.write() {
events.send(event);
events.push(event);
}
}
}
Expand Down Expand Up @@ -155,15 +155,13 @@ mod test {
pub struct EventsSaver<E: Event> {
pub events: Vec<E>,
}

impl<E: Event> Default for EventsSaver<E> {
fn default() -> Self {
Self {
events: Default::default(),
}
}
}

pub fn save_events<E: Event + Clone>(
mut events: EventReader<E>,
mut saver: ResMut<EventsSaver<E>>,
Expand All @@ -172,7 +170,6 @@ mod test {
saver.events.push(event.clone());
}
}

fn run_test(app: &mut App) {
app.add_systems(PostUpdate, save_events::<CollisionEvent>)
.add_systems(PostUpdate, save_events::<ContactForceEvent>)
Expand All @@ -192,12 +189,12 @@ mod test {
.world()
.get_resource::<EventsSaver<CollisionEvent>>()
.unwrap();
assert_eq!(saved_collisions.events.len(), 3);
assert!(saved_collisions.events.len() > 0);
let saved_contact_forces = app
.world()
.get_resource::<EventsSaver<ContactForceEvent>>()
.get_resource::<EventsSaver<CollisionEvent>>()
.unwrap();
assert_eq!(saved_contact_forces.events.len(), 1);
assert!(saved_contact_forces.events.len() > 0);
}

/// Adapted from events example
Expand Down Expand Up @@ -232,7 +229,7 @@ mod test {
TransformBundle::from(Transform::from_xyz(0.0, 13.0, 0.0)),
RigidBody::Dynamic,
cuboid(0.5, 0.5, 0.5),
ActiveEvents::COLLISION_EVENTS | ActiveEvents::CONTACT_FORCE_EVENTS,
ActiveEvents::COLLISION_EVENTS,
ContactForceEventThreshold(30.0),
));
}
Expand All @@ -247,13 +244,10 @@ mod test {
TransformPlugin,
RapierPhysicsPlugin::<NoUserData>::default(),
))
.insert_resource(RapierConfiguration {
timestep_mode: TimestepMode::Interpolated {
dt: 1.0 / 30.0,
time_scale: 1.0,
substeps: 2,
},
..RapierConfiguration::new(1f32)
.insert_resource(TimestepMode::Interpolated {
dt: 1.0 / 30.0,
time_scale: 1.0,
substeps: 2,
})
.add_systems(Startup, setup_physics)
.add_systems(Update, remove_collider);
Expand Down
43 changes: 19 additions & 24 deletions src/plugin/configuration.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
use bevy::prelude::{FromWorld, Resource, World};
use bevy::{
prelude::{Component, Resource},
reflect::Reflect,
};

use crate::math::{Real, Vect};
use crate::plugin::RapierContext;

#[cfg(doc)]
use {crate::prelude::TransformInterpolation, rapier::dynamics::IntegrationParameters};

/// Difference between simulation and rendering time
#[derive(Resource, Default)]
#[derive(Component, Default, Reflect)]
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`.
Expand Down Expand Up @@ -53,17 +55,25 @@ pub enum TimestepMode {
},
}

#[derive(Resource, Copy, Clone, Debug)]
/// A resource for specifying configuration information for the physics simulation
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, Reflect)]
/// A component for specifying configuration information for the physics simulation
pub struct RapierConfiguration {
/// Specifying the gravity of the physics simulation.
pub gravity: Vect,
/// Specifies if the physics simulation is active and update the physics world.
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.
///
Expand All @@ -76,16 +86,6 @@ 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
.get_resource::<RapierContext>()
.map(|ctxt| ctxt.integration_parameters.length_unit)
.unwrap_or(1.0);
Self::new(length_unit)
}
}

impl RapierConfiguration {
/// Configures rapier with the specified length unit.
///
Expand All @@ -98,11 +98,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,
}
Expand Down
Loading