Skip to content

Commit

Permalink
more tests + logs + better changelog
Browse files Browse the repository at this point in the history
  • Loading branch information
Vrixyz committed Jul 2, 2024
1 parent 2b262ea commit f849cd2
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 35 deletions.
13 changes: 8 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@

### Changed

- `RapierContext` is now a `Component`
- Rapier now supports multiple worlds.
- Migration guide:
- `RapierContext`, `RapierConfiguration` and `RenderToSimulationTime` are now a `Component`
- Rapier now supports multiple worlds, see example `multi_world3` for usage details.
- Migration guide:
- `ResMut<mut RapierContext>` -> `DefaultRapierContextAccessMut`
- `Res<RapierContext>` -> `DefaultRapierContextAccess`
- `ResMut<RapierConfiguration>` -> `DefaultRapierConfigurationMut`
- `Res<RapierConfiguration>` -> `DefaultRapierConfiguration`
- 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 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-rc.1 (18 June 2024)

Expand Down
11 changes: 0 additions & 11 deletions src/plugin/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,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 Down
2 changes: 2 additions & 0 deletions src/plugin/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,8 @@ impl RapierContext {
}
}
if let Some(mut event_queue) = event_queue {
// SAFETY: event_queue and its inner locksare only accessed from
// within `self.pipeline.step` called above, so we can unwrap here safely.
self.collision_events_to_send =
std::mem::take(event_queue.collision_events.get_mut().unwrap());
self.contact_force_events_to_send =
Expand Down
17 changes: 17 additions & 0 deletions src/plugin/context/systemparams/rapier_context_access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,20 @@ impl<'w, 's> RapierContextAccessMut<'w, 's> {
.into_inner()
}
}

pub fn try_get_default_context(
default_context_access: &Query<Entity, With<DefaultRapierContext>>,
) -> Option<Entity> {
let context_entity = match default_context_access.iter().next() {
Some(it) => it,
None => {
log::error!(
"No entity with `DefaultRapierContext` found.\
Please add a default `RapierContext` or a `RapierContextEntityLink`\
on the new rapier-managed entity."
);
return None;
}
};
Some(context_entity)
}
9 changes: 6 additions & 3 deletions src/plugin/systems/collider.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use crate::dynamics::ReadMassProperties;
use crate::geometry::Collider;
use crate::plugin::context::systemparams::RapierEntity;
use crate::plugin::context::systemparams::{try_get_default_context, RapierEntity};
use crate::plugin::context::RapierContextEntityLink;
use crate::plugin::{RapierConfiguration, RapierContext, RapierContextAccessMut};
use crate::plugin::{
DefaultRapierContext, RapierConfiguration, RapierContext, RapierContextAccessMut,
};
use crate::prelude::{
ActiveCollisionTypes, ActiveEvents, ActiveHooks, ColliderDisabled, ColliderMassProperties,
ColliderScale, CollidingEntities, CollisionEvent, CollisionGroups, ContactForceEventThreshold,
Expand Down Expand Up @@ -314,6 +316,7 @@ pub fn init_colliders(
mut commands: Commands,
config: Query<&RapierConfiguration>,
mut context: Query<(Entity, &mut RapierContext)>,
default_context: Query<Entity, With<DefaultRapierContext>>,
colliders: Query<(ColliderComponents, Option<&GlobalTransform>), Without<RapierColliderHandle>>,
mut rigid_body_mprops: Query<&mut ReadMassProperties>,
parent_query: Query<&Parent>,
Expand Down Expand Up @@ -342,7 +345,7 @@ pub fn init_colliders(
let context_entity = context_link.map_or_else(
|| {
dbg!("unknown rapierContext for object, setting to default");
let context_entity = context.iter().next().unwrap().0;
let context_entity = try_get_default_context(&default_context).unwrap();
commands
.entity(entity)
.insert(RapierContextEntityLink(context_entity));
Expand Down
5 changes: 4 additions & 1 deletion src/plugin/systems/joint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use crate::dynamics::ImpulseJoint;
use crate::dynamics::MultibodyJoint;
use crate::dynamics::RapierImpulseJointHandle;
use crate::dynamics::RapierMultibodyJointHandle;
use crate::plugin::context::systemparams::try_get_default_context;
use crate::plugin::context::RapierContextEntityLink;
use crate::plugin::DefaultRapierContext;
use crate::plugin::RapierContext;
use crate::plugin::RapierContextAccessMut;
use bevy::prelude::*;
Expand All @@ -11,6 +13,7 @@ use bevy::prelude::*;
pub fn init_joints(
mut commands: Commands,
mut context: Query<(Entity, &mut RapierContext)>,
default_context: Query<Entity, With<DefaultRapierContext>>,
impulse_joints: Query<
(Entity, Option<&RapierContextEntityLink>, &ImpulseJoint),
Without<RapierImpulseJointHandle>,
Expand All @@ -24,7 +27,7 @@ pub fn init_joints(
for (entity, entity_context_link, joint) in impulse_joints.iter() {
let context_entity = entity_context_link.map_or_else(
|| {
let context_entity = context.iter().next().unwrap().0;
let context_entity = try_get_default_context(&default_context).unwrap();
commands
.entity(entity)
.insert(RapierContextEntityLink(context_entity));
Expand Down
102 changes: 102 additions & 0 deletions src/plugin/systems/multiple_rapier_contexts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,105 @@ fn bubble_down_world_change(
);
});
}

#[cfg(test)]
mod test {
use crate::plugin::systems::tests::HeadlessRenderPlugin;
use crate::plugin::{
NoUserData, PhysicsSet, RapierContext, RapierContextEntityLink, RapierPhysicsPlugin,
};
use crate::prelude::{ActiveEvents, Collider, ContactForceEventThreshold, RigidBody, Sensor};
use bevy::prelude::*;
use bevy::time::{TimePlugin, TimeUpdateStrategy};
use rapier::math::Real;

#[test]
pub fn multi_world_hierarchy_update() {
let mut app = App::new();
app.add_plugins((
HeadlessRenderPlugin,
TransformPlugin,
TimePlugin,
RapierPhysicsPlugin::<NoUserData>::default(),
))
.add_systems(
PostUpdate,
setup_physics
.run_if(run_once())
.before(PhysicsSet::SyncBackend),
);
// Simulates 60 updates per seconds
app.insert_resource(TimeUpdateStrategy::ManualDuration(
std::time::Duration::from_secs_f32(1f32 / 60f32),
));
app.update();
// Verify all rapier entities have a `RapierContextEntityLink`.
let mut world = app.world_mut();
let mut query = world.query_filtered::<Entity, With<Marker<'R'>>>();
for entity in query.iter(&world) {
world
.get::<RapierContextEntityLink>(entity)
.expect(&format!("no link to rapier context entity from {entity}."));
}
// Verify link is correctly updated for children.
let new_rapier_context = world.spawn(RapierContext::default()).id();
// FIXME: We need to wait 1 frame when creating a world.
// Ideally we should be able to order the systems so that we don't have to wait.
app.update();
let mut world = app.world_mut();
let mut query = world.query_filtered::<&mut RapierContextEntityLink, With<Marker<'P'>>>();
let mut link_parent = query.get_single_mut(&mut world).unwrap();
link_parent.0 = new_rapier_context;
app.update();
let mut world = app.world_mut();
let mut query = world.query_filtered::<&RapierContextEntityLink, With<Marker<'C'>>>();
let link_child = query.get_single_mut(&mut world).unwrap();
assert_eq!(link_child.0, new_rapier_context);
return;

#[derive(Component)]
pub struct Marker<const MARKER: char>;

#[cfg(feature = "dim3")]
fn cuboid(hx: Real, hy: Real, hz: Real) -> Collider {
Collider::cuboid(hx, hy, hz)
}
#[cfg(feature = "dim2")]
fn cuboid(hx: Real, hy: Real, _hz: Real) -> Collider {
Collider::cuboid(hx, hy)
}
pub fn setup_physics(mut commands: Commands) {
commands.spawn((
TransformBundle::from(Transform::from_xyz(0.0, -1.2, 0.0)),
cuboid(4.0, 1.0, 1.0),
Marker::<'R'>,
));

commands.spawn((
TransformBundle::from(Transform::from_xyz(0.0, 5.0, 0.0)),
cuboid(4.0, 1.5, 1.0),
Sensor,
Marker::<'R'>,
));

commands
.spawn((
TransformBundle::from(Transform::from_xyz(0.0, 13.0, 0.0)),
RigidBody::Dynamic,
cuboid(0.5, 0.5, 0.5),
ActiveEvents::COLLISION_EVENTS,
ContactForceEventThreshold(30.0),
Marker::<'P'>,
Marker::<'R'>,
))
.with_children(|child_builder| {
child_builder.spawn((
TransformBundle::from(Transform::from_xyz(0.0, -1.2, 0.0)),
cuboid(4.0, 1.0, 1.0),
Marker::<'C'>,
Marker::<'R'>,
));
});
}
}
}
14 changes: 3 additions & 11 deletions src/plugin/systems/rigid_body.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::dynamics::RapierRigidBodyHandle;
use crate::plugin::context::systemparams::try_get_default_context;
use crate::plugin::context::RapierContextEntityLink;
use crate::plugin::{configuration::TimestepMode, RapierConfiguration, RapierContext};
use crate::{dynamics::RigidBody, plugin::configuration::SimulationToRenderTime};
Expand Down Expand Up @@ -603,17 +604,7 @@ pub fn init_rigid_bodies(
// Get rapier context from RapierContextEntityLink or insert its default value.
let context_entity = entity_context_link.map_or_else(
|| {
let context_entity = match default_context_access.iter().next() {
Some(it) => it,
None => {
log::error!(
"No entity with `DefaultRapierContext` found.\
Please add a default `RapierContext` or a `RapierContextEntityLink`\
on the new rapier-managed entity."
);
return None;
}
};
let context_entity = try_get_default_context(&default_context_access)?;
commands
.entity(entity)
.insert(RapierContextEntityLink(context_entity));
Expand All @@ -640,6 +631,7 @@ pub fn init_rigid_bodies(
}
}
}

/// This applies the initial impulse given to a rigid-body when it is created.
///
/// This cannot be done inside `init_rigid_bodies` because impulses require the rigid-body
Expand Down
4 changes: 0 additions & 4 deletions src/plugin/systems/writeback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@ pub fn writeback_mass_properties(
for entity in mass_modified.read() {
let link = link.get(entity.0).unwrap();
let config = config.get(link.0).unwrap();
// FIXME: I think this should still run even if the physics pipeline is not enabled:
// - if we re-enable the pipeline, we'll have missed that event
// - it's not a heavy computation
// - it shouldn't happen too often anyway (only at initialization or when a user add/removes child colliders / changes their mass)
if config.physics_pipeline_active {
let context = context.get(link.0).unwrap();

Expand Down

0 comments on commit f849cd2

Please sign in to comment.