Skip to content

Commit

Permalink
better support for multi world
Browse files Browse the repository at this point in the history
  • Loading branch information
Vrixyz committed Jun 26, 2024
1 parent 4952ea5 commit 7a4ba5e
Show file tree
Hide file tree
Showing 11 changed files with 160 additions and 138 deletions.
2 changes: 1 addition & 1 deletion 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
3 changes: 1 addition & 2 deletions src/plugin/configuration.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use bevy::prelude::{default, Component, FromWorld, Resource, World};
use bevy::prelude::{Component, Resource};

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

/// The different ways of adjusting the timestep length each frame.
#[derive(Copy, Clone, Debug, PartialEq, Resource)]
Expand Down
49 changes: 32 additions & 17 deletions src/plugin/context/systemparams.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use bevy::ecs::system::SystemParam;
use bevy::prelude::*;
use rapier::dynamics::RigidBodyHandle;
use std::collections::HashMap;
use std::ops::{Deref, DerefMut};

use super::{DefaultRapierContext, RapierContext, RapierContextEntityLink};
Expand All @@ -14,7 +12,7 @@ pub struct DefaultRapierContextAccess<'w, 's> {

impl<'w, 's> DefaultRapierContextAccess<'w, 's> {
/// Use this method if you only have one world.
pub fn single<'a>(&'_ self) -> &RapierContext {
pub fn single(&'_ self) -> &RapierContext {
self.rapier_context.single()
}
}
Expand Down Expand Up @@ -51,13 +49,25 @@ impl<'w, 's> DerefMut for DefaultRapierContextAccessMut<'w, 's> {
/// Utility [`SystemParam`] to easily access any [`RapierContext`] immutably
#[derive(SystemParam)]
pub struct RapierContextAccess<'w, 's> {
rapier_context: Query<'w, 's, &'static RapierContext, With<DefaultRapierContext>>,
rapier_context: Query<'w, 's, &'static RapierContext>,
rapier_context_link: Query<'w, 's, &'static RapierContextEntityLink>,
}

impl<'w, 's> RapierContextAccess<'w, 's> {
/// Use this method if you only have one world.
pub fn single<'a>(&'_ self) -> &RapierContext {
self.rapier_context.single()
pub fn link(&self, entity: Entity) -> &RapierContextEntityLink {
self.rapier_context_link
.get(entity)
.expect("RapierContextAccess.link called on an entity without RapierContextEntityLink.")
}
pub fn follow_link(&self, link: RapierContextEntityLink) -> &'_ RapierContext {
self.rapier_context
.get(link.0)
.expect("RapierContextEntityLink.0 refers to an entity without RapierContext.")
}

pub fn context(&self, entity: Entity) -> &'_ RapierContext {
let context_link = self.link(entity);
self.follow_link(*context_link)
}
}

Expand All @@ -72,21 +82,27 @@ impl<'w, 's> Deref for RapierContextAccess<'w, 's> {
/// Utility [`SystemParam`] to easily access any [`RapierContext`] mutably
#[derive(SystemParam)]
pub struct RapierContextAccessMut<'w, 's> {
rapier_context: Query<'w, 's, &'static mut RapierContext>,
rapier_context_link: Query<'w, 's, &'static mut RapierContextEntityLink>,
pub rapier_context: Query<'w, 's, &'static mut RapierContext>,
pub rapier_context_link: Query<'w, 's, &'static RapierContextEntityLink>,
}

impl<'w, 's> RapierContextAccessMut<'w, 's> {
pub fn context(&mut self, entity: Entity) -> &'_ mut RapierContext {
let context_link = self
.rapier_context_link
.get(entity)
.expect("entity2body called on an entity without RapierContextEntityLink.");
pub fn link(&self, entity: Entity) -> &RapierContextEntityLink {
self.rapier_context_link.get(entity).expect(
"RapierContextAccessMut.link called on an entity without RapierContextEntityLink.",
)
}
pub fn follow_link(&mut self, link: RapierContextEntityLink) -> &'_ mut RapierContext {
self.rapier_context
.get_mut(context_link.0)
.expect("RapierContextEntityLink refers to an entity without RapierContext.")
.get_mut(link.0)
.expect("RapierContextEntityLink.0 refers to an entity without RapierContext.")
.into_inner()
}

pub fn context(&mut self, entity: Entity) -> &'_ mut RapierContext {
let context_link = self.link(entity);
self.follow_link(*context_link)
}
}

pub fn try_retrieve_context<'a>(
Expand All @@ -96,7 +112,6 @@ pub fn try_retrieve_context<'a>(
link.map_or_else(
|| {
let context_entity = context.iter().next().unwrap().0;
RapierContextEntityLink(context_entity);
Err(context_entity)
},
|link| Ok(link.0),
Expand Down
10 changes: 5 additions & 5 deletions src/plugin/systems/character_controller.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::control::CharacterCollision;
use crate::dynamics::RapierRigidBodyHandle;
use crate::geometry::RapierColliderHandle;
use crate::plugin::DefaultRapierContextAccessMut;
use crate::plugin::RapierConfiguration;
use crate::plugin::RapierContextAccessMut;
use crate::prelude::KinematicCharacterController;
use crate::prelude::KinematicCharacterControllerOutput;
use crate::utils;
Expand All @@ -16,7 +16,7 @@ use rapier::pipeline::QueryFilter;
pub fn update_character_controls(
mut commands: Commands,
config: Query<&RapierConfiguration>,
mut context: DefaultRapierContextAccessMut,
mut context_access: RapierContextAccessMut,
mut character_controllers: Query<(
Entity,
&mut KinematicCharacterController,
Expand All @@ -27,12 +27,12 @@ pub fn update_character_controls(
)>,
mut transforms: Query<&mut Transform>,
) {
let context = &mut *context;
let config = &*config.single();

for (entity, mut controller, output, collider_handle, body_handle, glob_transform) in
character_controllers.iter_mut()
{
let link = *context_access.link(entity);
let context = context_access.follow_link(link);
let config = config.get(link.0).unwrap();
if let (Some(raw_controller), Some(translation)) =
(controller.to_raw(), controller.translation)
{
Expand Down
23 changes: 10 additions & 13 deletions src/plugin/systems/collider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ use crate::dynamics::ReadMassProperties;
use crate::geometry::Collider;
use crate::plugin::context::systemparams::try_retrieve_context;
use crate::plugin::context::RapierContextEntityLink;
use crate::plugin::{
DefaultRapierContextAccessMut, RapierConfiguration, RapierContext, RapierContextAccessMut,
};
use crate::plugin::{RapierConfiguration, RapierContext, RapierContextAccessMut};
use crate::prelude::{
ActiveCollisionTypes, ActiveEvents, ActiveHooks, ColliderDisabled, ColliderMassProperties,
ColliderScale, CollidingEntities, CollisionEvent, CollisionGroups, ContactForceEventThreshold,
Expand Down Expand Up @@ -310,8 +308,6 @@ pub fn init_colliders(
parent_query: Query<&Parent>,
transform_query: Query<&Transform>,
) {
let config = &*config.single();

for (
(
(entity, context_link),
Expand All @@ -332,6 +328,15 @@ pub fn init_colliders(
global_transform,
) in colliders.iter()
{
let context_entity =
try_retrieve_context(context_link, &context).unwrap_or_else(|context_entity| {
commands
.entity(entity)
.insert(RapierContextEntityLink(context_entity));
context_entity
});
let context = &mut *context.get_mut(context_entity).unwrap().1;
let config = config.get(context_entity).unwrap();
let mut scaled_shape = shape.clone();
scaled_shape.set_scale(shape.scale, config.scaled_shape_subdivision);
let mut builder = ColliderBuilder::new(scaled_shape.raw.clone());
Expand Down Expand Up @@ -388,14 +393,6 @@ pub fn init_colliders(
if let Some(threshold) = contact_force_event_threshold {
builder = builder.contact_force_event_threshold(threshold.0);
}
let context_entity =
try_retrieve_context(context_link, &context).unwrap_or_else(|context_entity| {
commands
.entity(entity)
.insert(RapierContextEntityLink(context_entity));
context_entity
});
let context = &mut *context.get_mut(context_entity).unwrap().1;
let body_entity = entity;
let (body_handle, child_transform) =
collider_offset(entity, context, &parent_query, &transform_query);
Expand Down
121 changes: 63 additions & 58 deletions src/plugin/systems/joint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,94 +2,99 @@ use crate::dynamics::ImpulseJoint;
use crate::dynamics::MultibodyJoint;
use crate::dynamics::RapierImpulseJointHandle;
use crate::dynamics::RapierMultibodyJointHandle;
use crate::plugin::RapierContext;
use crate::plugin::context::RapierContextEntityLink;
use crate::plugin::RapierContextAccessMut;
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: Query<&mut RapierContext>,
mut context: RapierContextAccessMut,
impulse_joints: Query<(Entity, &ImpulseJoint), Without<RapierImpulseJointHandle>>,
multibody_joints: Query<(Entity, &MultibodyJoint), Without<RapierMultibodyJointHandle>>,
parent_query: Query<&Parent>,
) {
for mut context in context.iter_mut() {
let context = &mut *context;

for (entity, joint) in impulse_joints.iter() {
let mut target = None;
let mut body_entity = entity;
while target.is_none() {
target = context.entity2body.get(&body_entity).copied();
if let Ok(parent_entity) = parent_query.get(body_entity) {
body_entity = parent_entity.get();
} else {
break;
}
for (entity, joint) in impulse_joints.iter() {
let context = context.context(entity);
let mut target = None;
let mut body_entity = entity;
while target.is_none() {
target = context.entity2body.get(&body_entity).copied();
if let Ok(parent_entity) = parent_query.get(body_entity) {
body_entity = parent_entity.get();
} else {
break;
}
}

if let (Some(target), Some(source)) = (target, context.entity2body.get(&joint.parent)) {
let handle =
context
.impulse_joints
.insert(*source, target, joint.data.into_rapier(), true);
commands
.entity(entity)
.insert(RapierImpulseJointHandle(handle));
context.entity2impulse_joint.insert(entity, handle);
}
if let (Some(target), Some(source)) = (target, context.entity2body.get(&joint.parent)) {
let handle =
context
.impulse_joints
.insert(*source, target, joint.data.into_rapier(), true);
commands
.entity(entity)
.insert(RapierImpulseJointHandle(handle));
context.entity2impulse_joint.insert(entity, handle);
}
}

for (entity, joint) in multibody_joints.iter() {
let target = context.entity2body.get(&entity);
for (entity, joint) in multibody_joints.iter() {
let context = context.context(entity);
let target = context.entity2body.get(&entity);

if let (Some(target), Some(source)) = (target, context.entity2body.get(&joint.parent)) {
if let Some(handle) = context.multibody_joints.insert(
*source,
*target,
joint.data.into_rapier(),
true,
) {
commands
.entity(entity)
.insert(RapierMultibodyJointHandle(handle));
context.entity2multibody_joint.insert(entity, handle);
} else {
error!("Failed to create multibody joint: loop detected.")
}
if let (Some(target), Some(source)) = (target, context.entity2body.get(&joint.parent)) {
if let Some(handle) =
context
.multibody_joints
.insert(*source, *target, joint.data.into_rapier(), true)
{
commands
.entity(entity)
.insert(RapierMultibodyJointHandle(handle));
context.entity2multibody_joint.insert(entity, handle);
} else {
error!("Failed to create multibody joint: loop detected.")
}
}
}
}

/// System responsible for applying changes the user made to a joint component.
pub fn apply_joint_user_changes(
mut context: Query<&mut RapierContext>,
mut context: RapierContextAccessMut,
changed_impulse_joints: Query<
(&RapierImpulseJointHandle, &ImpulseJoint),
(
&RapierContextEntityLink,
&RapierImpulseJointHandle,
&ImpulseJoint,
),
Changed<ImpulseJoint>,
>,
changed_multibody_joints: Query<
(&RapierMultibodyJointHandle, &MultibodyJoint),
(
&RapierContextEntityLink,
&RapierMultibodyJointHandle,
&MultibodyJoint,
),
Changed<MultibodyJoint>,
>,
) {
for mut context in context.iter_mut() {
let context = &mut *context;
// 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() {
if let Some(joint) = context.impulse_joints.get_mut(handle.0) {
joint.data = changed_joint.data.into_rapier();
}
// TODO: right now, we only support propagating changes made to the joint data.
// Re-parenting the joint isn’t supported yet.
for (link, handle, changed_joint) in changed_impulse_joints.iter() {
let context = context.follow_link(*link);
if let Some(joint) = context.impulse_joints.get_mut(handle.0) {
joint.data = changed_joint.data.into_rapier();
}
}

for (handle, changed_joint) in changed_multibody_joints.iter() {
// TODO: not sure this will always work properly, e.g., if the number of Dofs is changed.
if let Some((mb, link_id)) = context.multibody_joints.get_mut(handle.0) {
if let Some(link) = mb.link_mut(link_id) {
link.joint.data = changed_joint.data.into_rapier();
}
for (link, handle, changed_joint) in changed_multibody_joints.iter() {
let context = context.follow_link(*link);
// TODO: not sure this will always work properly, e.g., if the number of Dofs is changed.
if let Some((mb, link_id)) = context.multibody_joints.get_mut(handle.0) {
if let Some(link) = mb.link_mut(link_id) {
link.joint.data = changed_joint.data.into_rapier();
}
}
}
Expand Down
15 changes: 7 additions & 8 deletions src/plugin/systems/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ use bevy::prelude::*;
/// System responsible for advancing the physics simulation, and updating the internal state
/// for scene queries.
pub fn step_simulation<Hooks>(
mut context: Query<&mut RapierContext>,
mut context: Query<(
&mut RapierContext,
&RapierConfiguration,
&mut SimulationToRenderTime,
)>,
timestep_mode: Res<TimestepMode>,
config: Query<&RapierConfiguration>,
hooks: StaticSystemParam<Hooks>,
time: Res<Time>,
mut sim_to_render_time: Query<&mut SimulationToRenderTime>,
mut collision_events: EventWriter<CollisionEvent>,
mut contact_force_events: EventWriter<ContactForceEvent>,
mut interpolation_query: Query<(&RapierRigidBodyHandle, &mut TransformInterpolation)>,
Expand All @@ -40,20 +42,17 @@ pub fn step_simulation<Hooks>(
{
let hooks_adapter = BevyPhysicsHooksAdapter::new(hooks.into_inner());

for mut context in context.iter_mut() {
for (mut context, config, mut sim_to_render_time) in context.iter_mut() {
let context = &mut *context;

let config = &*config.single();
let sim_to_render_time = &mut *sim_to_render_time.single_mut();

if config.physics_pipeline_active {
context.step_simulation(
config.gravity,
*timestep_mode,
Some((&collision_events, &contact_force_events)),
&hooks_adapter,
&time,
sim_to_render_time,
&mut sim_to_render_time,
Some(&mut interpolation_query),
);
context.deleted_colliders.clear();
Expand Down
Loading

0 comments on commit 7a4ba5e

Please sign in to comment.