1pub mod systemparams;
4
5use bevy::prelude::*;
6use rapier::geometry::{ContactData, ContactManifoldData};
7use rapier::parry::query::{PersistentQueryDispatcher, QueryDispatcher};
8use std::collections::HashMap;
9use std::sync::RwLock;
10
11use rapier::prelude::{
12 Aabb, CCDSolver, ColliderHandle, ColliderSet, CollisionPipeline, EventHandler, FeatureId,
13 ImpulseJointHandle, ImpulseJointSet, IntegrationParameters, IslandManager,
14 MultibodyJointHandle, MultibodyJointSet, NarrowPhase, PhysicsHooks, PhysicsPipeline,
15 QueryFilter as RapierQueryFilter, QueryPipeline, QueryPipelineMut, Ray, Real, RigidBodyHandle,
16 RigidBodySet, RigidBodyType, SoftBodyHandle, SoftBodySet,
17};
18
19use crate::geometry::{
20 AsShape, NonlinearMotion, PointProjection, RayIntersection, ShapeCastHit, ShapeClosestPoints,
21 ShapeContact,
22};
23use crate::math::{Rot, Vect};
24use crate::pipeline::{CollisionEvent, ContactForceEvent, EventHandlerFanOut, EventQueue};
25use bevy::prelude::{Entity, GlobalTransform, Query};
26use rapier::geometry::Collider as RapierCollider;
27use rapier::parry::partitioning::Bvh;
28use rapier::parry::query::ClosestPoints;
29use rapier::parry::shape::CompositeShapeRef;
30
31use crate::control::{CharacterCollision, MoveShapeOptions, MoveShapeOutput};
32use crate::dynamics::{
33 ImpulseJointImpulses, InverseKinematicsOption, MultibodyJointState, RapierSoftBody,
34 RapierSoftBodyTearEvent, TransformInterpolation,
35};
36use crate::parry::query::details::ShapeCastOptions;
37use crate::pipeline::{PhysicsQuarantineEvent, SoftBodyTearResult};
38use crate::plugin::configuration::{BroadPhaseOptimizationStrategy, SimulationMode, TimestepMode};
39use crate::prelude::{CollisionGroups, QueryFilter, RapierRigidBodyHandle};
40use rapier::control::CharacterAutostep;
41use rapier::dynamics::ImpulseJoint as RapierImpulseJoint;
42use rapier::dynamics::{Multibody, MultibodyJoint as RapierMultibodyJoint};
43use rapier::geometry::DefaultBroadPhase;
44use rapier::math::{DVector, Jacobian};
45use std::ops::Range;
46
47#[cfg(doc)]
48use crate::prelude::{
49 systemparams::{RapierContext, ReadRapierContext},
50 ImpulseJoint, KinematicMultibodyJoint, MultibodyJoint, RevoluteJoint, TypedJoint,
51};
52
53#[derive(Component, Default, Reflect, Clone)]
55pub struct SimulationToRenderTime {
56 pub diff: f32,
58}
59
60#[derive(Component, Reflect, Debug, Clone, Copy)]
70pub struct DefaultRapierContext;
71
72#[derive(Component, Reflect, Debug, Clone, Copy, PartialEq, Eq, Hash)]
77pub struct RapierContextEntityLink(pub Entity);
78
79#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
86#[cfg_attr(
87 feature = "serde-serialize",
88 serde(from = "serde_shadows::RapierContextCollidersData")
89)]
90#[derive(Component, Default, Debug, Clone)]
91pub struct RapierContextColliders {
92 pub colliders: ColliderSet,
94 #[cfg_attr(feature = "serde-serialize", serde(skip))]
95 pub(crate) entity2collider: HashMap<Entity, ColliderHandle>,
96}
97
98impl RapierContextColliders {
99 pub fn rebuild_entity_maps(&mut self) {
105 self.entity2collider = self
106 .colliders
107 .iter()
108 .filter_map(|(handle, co)| Some((Entity::try_from_bits(co.user_data as u64)?, handle)))
109 .collect();
110 }
111
112 pub fn collider_parent(
115 &self,
116 rigidbody_set: &RapierRigidBodySet,
117 entity: Entity,
118 ) -> Option<Entity> {
119 self.entity2collider
120 .get(&entity)
121 .and_then(|h| self.colliders.get(*h))
122 .and_then(|co| co.parent())
123 .and_then(|h| rigidbody_set.rigid_body_entity(h))
124 }
125
126 pub fn rigid_body_colliders<'a, 'b: 'a>(
129 &'a self,
130 entity: Entity,
131 rigidbody_set: &'b RapierRigidBodySet,
132 ) -> impl Iterator<Item = Entity> + 'a {
133 rigidbody_set
134 .entity2body()
135 .get(&entity)
136 .and_then(|handle| rigidbody_set.bodies.get(*handle))
137 .map(|body| {
138 body.colliders()
139 .iter()
140 .filter_map(|handle| self.collider_entity(*handle))
141 })
142 .into_iter()
143 .flatten()
144 }
145
146 pub fn collider_entity(&self, handle: ColliderHandle) -> Option<Entity> {
148 RapierContextColliders::collider_entity_with_set(&self.colliders, handle)
149 }
150
151 pub(crate) fn collider_entity_with_set(
153 colliders: &ColliderSet,
154 handle: ColliderHandle,
155 ) -> Option<Entity> {
156 colliders.get(handle).map(Self::entity_from_collider)
157 }
158
159 pub fn entity_from_collider(collider: &rapier::prelude::Collider) -> Entity {
161 Entity::from_bits(collider.user_data as u64)
162 }
163
164 pub fn entity2collider(&self) -> &HashMap<Entity, ColliderHandle> {
166 &self.entity2collider
167 }
168
169 fn collider(&self, entity: Entity) -> Option<&rapier::geometry::Collider> {
171 self.colliders.get(*self.entity2collider.get(&entity)?)
172 }
173
174 #[cfg(feature = "dim2")]
178 pub fn compute_aabb(&self, entity: Entity) -> Option<bevy::shape::Aabb2d> {
179 Some(aabb_to_bevy(self.collider(entity)?.compute_aabb()))
180 }
181
182 #[cfg(feature = "dim3")]
186 pub fn compute_aabb(&self, entity: Entity) -> Option<bevy::shape::Aabb3d> {
187 Some(aabb_to_bevy(self.collider(entity)?.compute_aabb()))
188 }
189
190 #[cfg(feature = "dim2")]
195 pub fn compute_swept_aabb(
196 &self,
197 entity: Entity,
198 next_position: Transform,
199 ) -> Option<bevy::shape::Aabb2d> {
200 let next_position = crate::utils::transform_to_iso(&next_position);
201 Some(aabb_to_bevy(
202 self.collider(entity)?.compute_swept_aabb(&next_position),
203 ))
204 }
205
206 #[cfg(feature = "dim3")]
211 pub fn compute_swept_aabb(
212 &self,
213 entity: Entity,
214 next_position: Transform,
215 ) -> Option<bevy::shape::Aabb3d> {
216 let next_position = crate::utils::transform_to_iso(&next_position);
217 Some(aabb_to_bevy(
218 self.collider(entity)?.compute_swept_aabb(&next_position),
219 ))
220 }
221
222 pub fn volume(&self, entity: Entity) -> Option<Real> {
226 Some(self.collider(entity)?.volume())
227 }
228
229 pub fn mass(&self, entity: Entity) -> Option<Real> {
233 Some(self.collider(entity)?.mass())
234 }
235
236 pub fn density(&self, entity: Entity) -> Option<Real> {
241 Some(self.collider(entity)?.density())
242 }
243}
244
245#[cfg(feature = "dim2")]
247fn aabb_to_bevy(aabb: Aabb) -> bevy::shape::Aabb2d {
248 bevy::shape::Aabb2d {
249 min: aabb.mins,
250 max: aabb.maxs,
251 }
252}
253
254#[cfg(feature = "dim3")]
256fn aabb_to_bevy(aabb: Aabb) -> bevy::shape::Aabb3d {
257 bevy::shape::Aabb3d {
258 min: aabb.mins.into(),
259 max: aabb.maxs.into(),
260 }
261}
262
263#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
270#[cfg_attr(
271 feature = "serde-serialize",
272 serde(from = "serde_shadows::RapierContextJointsData")
273)]
274#[derive(Component, Default, Debug, Clone)]
275pub struct RapierContextJoints {
276 pub impulse_joints: ImpulseJointSet,
278 pub multibody_joints: MultibodyJointSet,
280
281 #[cfg_attr(feature = "serde-serialize", serde(skip))]
282 pub(crate) entity2impulse_joint: HashMap<Entity, ImpulseJointHandle>,
283 #[cfg_attr(feature = "serde-serialize", serde(skip))]
284 pub(crate) entity2multibody_joint: HashMap<Entity, MultibodyJointHandle>,
285}
286
287impl RapierContextJoints {
288 pub fn rebuild_entity_maps(&mut self) {
294 self.entity2impulse_joint = self
295 .impulse_joints
296 .iter()
297 .filter_map(|(handle, joint)| {
298 Some((Entity::try_from_bits(joint.data.user_data as u64)?, handle))
299 })
300 .collect();
301 self.entity2multibody_joint = self
302 .multibody_joints
303 .iter()
304 .filter_map(|(handle, _, _, link)| {
305 Some((
306 Entity::try_from_bits(link.joint().data.user_data as u64)?,
307 handle,
308 ))
309 })
310 .collect();
311 }
312
313 pub fn entity2impulse_joint(&self) -> &HashMap<Entity, ImpulseJointHandle> {
315 &self.entity2impulse_joint
316 }
317
318 pub fn entity2multibody_joint(&self) -> &HashMap<Entity, MultibodyJointHandle> {
320 &self.entity2multibody_joint
321 }
322
323 pub fn entity_from_impulse_joint(joint: &RapierImpulseJoint) -> Entity {
326 Entity::from_bits(joint.data.user_data as u64)
327 }
328
329 pub fn impulse_joint_entity(&self, handle: ImpulseJointHandle) -> Option<Entity> {
332 self.impulse_joints
333 .get(handle)
334 .map(Self::entity_from_impulse_joint)
335 }
336
337 pub fn impulse_joint_impulses(&self, entity: Entity) -> Option<ImpulseJointImpulses> {
342 let joint = self
343 .impulse_joints
344 .get(*self.entity2impulse_joint.get(&entity)?)?;
345 Some(ImpulseJointImpulses::from_rapier(joint))
346 }
347
348 pub fn impulse_joints_between<'a>(
351 &'a self,
352 rigidbody_set: &RapierRigidBodySet,
353 body1: Entity,
354 body2: Entity,
355 ) -> impl Iterator<Item = Entity> + 'a {
356 let handles = rigidbody_set
357 .entity2body
358 .get(&body1)
359 .copied()
360 .zip(rigidbody_set.entity2body.get(&body2).copied());
361 handles.into_iter().flat_map(move |(handle1, handle2)| {
362 self.impulse_joints
363 .joints_between(handle1, handle2)
364 .map(|(_, joint)| Self::entity_from_impulse_joint(joint))
365 })
366 }
367
368 pub fn attached_impulse_joints<'a>(
371 &'a self,
372 rigidbody_set: &RapierRigidBodySet,
373 body: Entity,
374 ) -> impl Iterator<Item = Entity> + 'a {
375 let handle = rigidbody_set.entity2body.get(&body).copied();
376 handle.into_iter().flat_map(move |handle| {
377 self.impulse_joints
378 .attached_joints(handle)
379 .map(|(_, _, _, joint)| Self::entity_from_impulse_joint(joint))
380 })
381 }
382
383 pub fn attached_enabled_impulse_joints<'a>(
386 &'a self,
387 rigidbody_set: &RapierRigidBodySet,
388 body: Entity,
389 ) -> impl Iterator<Item = Entity> + 'a {
390 let handle = rigidbody_set.entity2body.get(&body).copied();
391 handle.into_iter().flat_map(move |handle| {
392 self.impulse_joints
393 .attached_enabled_joints(handle)
394 .map(|(_, _, _, joint)| Self::entity_from_impulse_joint(joint))
395 })
396 }
397
398 pub fn entity_from_multibody_joint(joint: &RapierMultibodyJoint) -> Entity {
403 Entity::from_bits(joint.data.user_data as u64)
404 }
405
406 pub fn multibody(
412 &self,
413 rigidbody_set: &RapierRigidBodySet,
414 entity: Entity,
415 ) -> Option<(&Multibody, usize)> {
416 let body = rigidbody_set.entity2body.get(&entity)?;
417 let link = self.multibody_joints.rigid_body_link(*body)?;
418 Some((
419 self.multibody_joints.get_multibody(link.multibody)?,
420 link.id,
421 ))
422 }
423
424 pub fn multibody_mut(
429 &mut self,
430 rigidbody_set: &RapierRigidBodySet,
431 entity: Entity,
432 ) -> Option<(&mut Multibody, usize)> {
433 let body = rigidbody_set.entity2body.get(&entity)?;
434 let link = *self.multibody_joints.rigid_body_link(*body)?;
435 Some((
436 self.multibody_joints.get_multibody_mut(link.multibody)?,
437 link.id,
438 ))
439 }
440
441 pub fn multibody_root(
443 &self,
444 rigidbody_set: &RapierRigidBodySet,
445 entity: Entity,
446 ) -> Option<Entity> {
447 let (mb, _) = self.multibody(rigidbody_set, entity)?;
448 rigidbody_set.rigid_body_entity(mb.root().rigid_body_handle())
449 }
450
451 pub fn multibody_links<'a>(
460 &'a self,
461 rigidbody_set: &'a RapierRigidBodySet,
462 entity: Entity,
463 ) -> impl Iterator<Item = Entity> + 'a {
464 self.multibody(rigidbody_set, entity)
465 .into_iter()
466 .flat_map(move |(mb, _)| {
467 mb.links()
468 .filter_map(|link| rigidbody_set.rigid_body_entity(link.rigid_body_handle()))
469 })
470 }
471
472 pub fn multibody_parent_link(
477 &self,
478 rigidbody_set: &RapierRigidBodySet,
479 entity: Entity,
480 ) -> Option<Entity> {
481 let (mb, link_id) = self.multibody(rigidbody_set, entity)?;
482 let parent = mb.link(mb.link(link_id)?.parent_id()?)?;
483 rigidbody_set.rigid_body_entity(parent.rigid_body_handle())
484 }
485
486 pub fn multibody_joint_between(
489 &self,
490 rigidbody_set: &RapierRigidBodySet,
491 body1: Entity,
492 body2: Entity,
493 ) -> Option<Entity> {
494 let handle1 = rigidbody_set.entity2body.get(&body1)?;
495 let handle2 = rigidbody_set.entity2body.get(&body2)?;
496 let (_, _, link) = self.multibody_joints.joint_between(*handle1, *handle2)?;
497 rigidbody_set.rigid_body_entity(link.rigid_body_handle())
498 }
499
500 pub fn attached_multibody_joints<'a>(
504 &'a self,
505 rigidbody_set: &'a RapierRigidBodySet,
506 body: Entity,
507 ) -> impl Iterator<Item = Entity> + 'a {
508 let handle = rigidbody_set.entity2body.get(&body).copied();
509 handle.into_iter().flat_map(move |handle| {
510 self.multibody_joints
511 .attached_joints(handle)
512 .filter_map(|(_, body2, _)| rigidbody_set.rigid_body_entity(body2))
513 })
514 }
515
516 pub fn multibody_link_dofs(
523 &self,
524 rigidbody_set: &RapierRigidBodySet,
525 entity: Entity,
526 ) -> Option<Range<usize>> {
527 let (mb, link_id) = self.multibody(rigidbody_set, entity)?;
528 let link = mb.link(link_id)?;
529 let start = link.assembly_id();
530 Some(start..start + link.joint().ndofs())
531 }
532
533 pub fn multibody_generalized_velocity(
537 &self,
538 rigidbody_set: &RapierRigidBodySet,
539 entity: Entity,
540 ) -> Option<&[Real]> {
541 let (mb, _) = self.multibody(rigidbody_set, entity)?;
542 Some(mb.generalized_velocity().data.into_slice())
543 }
544
545 pub fn multibody_generalized_velocity_mut(
550 &mut self,
551 rigidbody_set: &mut RapierRigidBodySet,
552 entity: Entity,
553 ) -> Option<&mut [Real]> {
554 let (mb, _) = self.multibody_mut(rigidbody_set, entity)?;
555 crate::plugin::systems::wake_up_multibody(mb, &mut rigidbody_set.bodies);
556 Some(mb.generalized_velocity_mut().data.into_slice_mut())
557 }
558
559 pub fn multibody_joint_velocity_mut(
567 &mut self,
568 rigidbody_set: &mut RapierRigidBodySet,
569 entity: Entity,
570 ) -> Option<&mut [Real]> {
571 let handle = *self.entity2multibody_joint.get(&entity)?;
572 let (mb, link_id) = self.multibody_joints.get_mut(handle)?;
573 crate::plugin::systems::wake_up_multibody(mb, &mut rigidbody_set.bodies);
574 let link = mb.link(link_id)?;
575 let range = link.assembly_id()..link.assembly_id() + link.joint().ndofs();
576 Some(&mut mb.generalized_velocity_mut().data.into_slice_mut()[range])
577 }
578
579 pub fn multibody_joint_state(&self, entity: Entity) -> Option<MultibodyJointState> {
584 self.multibody_joint_state_from_handle(*self.entity2multibody_joint.get(&entity)?)
585 }
586
587 pub(crate) fn multibody_joint_state_from_handle(
589 &self,
590 handle: MultibodyJointHandle,
591 ) -> Option<MultibodyJointState> {
592 let (mb, link_id) = self.multibody_joints.get(handle)?;
593 let link = mb.link(link_id)?;
594 Some(MultibodyJointState::from_rapier(
595 link.joint(),
596 mb.joint_velocity(link).as_slice(),
597 ))
598 }
599
600 pub fn multibody_body_jacobian(
606 &self,
607 rigidbody_set: &RapierRigidBodySet,
608 entity: Entity,
609 ) -> Option<&Jacobian<Real>> {
610 let (mb, link_id) = self.multibody(rigidbody_set, entity)?;
611 Some(mb.body_jacobian(link_id))
612 }
613
614 pub fn multibody_apply_displacements(
622 &mut self,
623 rigidbody_set: &RapierRigidBodySet,
624 entity: Entity,
625 displacements: &[Real],
626 ) -> bool {
627 let Some((mb, _)) = self.multibody_mut(rigidbody_set, entity) else {
628 return false;
629 };
630 if displacements.len() != mb.ndofs() {
631 return false;
632 }
633 mb.apply_displacements(displacements);
634 true
635 }
636
637 pub fn multibody_forward_kinematics(
644 &mut self,
645 rigidbody_set: &mut RapierRigidBodySet,
646 entity: Entity,
647 read_root_pose_from_rigid_body: bool,
648 ) -> bool {
649 let Some((mb, _)) = self.multibody_mut(rigidbody_set, entity) else {
650 return false;
651 };
652 mb.forward_kinematics(&rigidbody_set.bodies, read_root_pose_from_rigid_body);
653 mb.update_rigid_bodies(&mut rigidbody_set.bodies, true);
654 true
655 }
656
657 pub fn multibody_inverse_kinematics(
669 &self,
670 rigidbody_set: &RapierRigidBodySet,
671 link: Entity,
672 target: Transform,
673 options: &InverseKinematicsOption,
674 joint_can_move: impl Fn(Entity) -> bool,
675 displacements: &mut Vec<Real>,
676 ) -> bool {
677 let Some((mb, link_id)) = self.multibody(rigidbody_set, link) else {
678 return false;
679 };
680 displacements.clear();
681 displacements.resize(mb.ndofs(), 0.0);
682 let mut result = DVector::from_vec(std::mem::take(displacements));
683 mb.inverse_kinematics(
684 &rigidbody_set.bodies,
685 link_id,
686 options,
687 &crate::utils::transform_to_iso(&target),
688 |link| {
689 rigidbody_set
690 .rigid_body_entity(link.rigid_body_handle())
691 .is_some_and(&joint_can_move)
692 },
693 &mut result,
694 );
695 *displacements = result.data.into();
696 true
697 }
698}
699
700#[derive(Copy, Clone)]
706pub struct RapierQueryPipeline<'a> {
707 pub query_pipeline: QueryPipeline<'a>,
709}
710
711pub struct RapierQueryPipelineMut<'a> {
717 pub query_pipeline: QueryPipelineMut<'a>,
719}
720impl<'a> RapierQueryPipelineMut<'a> {
721 pub fn new_scoped<T>(
723 broad_phase: &DefaultBroadPhase,
724 colliders: &mut RapierContextColliders,
725 rigid_bodies: &mut RapierRigidBodySet,
726 filter: &QueryFilter<'_>,
727 dispatcher: &dyn QueryDispatcher,
728 scoped_fn: impl FnOnce(RapierQueryPipelineMut<'_>) -> T,
729 ) -> T {
730 let predicate = to_rapier_query_filter_combined_predicate(filter);
731 let rapier_filter =
732 to_rapier_query_filter(filter, colliders, rigid_bodies, predicate.as_ref());
733 let query_pipeline = broad_phase.as_query_pipeline_mut(
734 dispatcher,
735 &mut rigid_bodies.bodies,
736 &mut colliders.colliders,
737 rapier_filter,
738 );
739 scoped_fn(RapierQueryPipelineMut { query_pipeline })
740 }
741
742 pub fn as_ref(&self) -> RapierQueryPipeline<'_> {
744 RapierQueryPipeline {
745 query_pipeline: self.query_pipeline.as_ref(),
746 }
747 }
748}
749
750pub fn to_rapier_query_filter_predicate(
752 predicate: &dyn Fn(Entity, &RapierCollider) -> bool,
753) -> impl Fn(ColliderHandle, &RapierCollider) -> bool + use<'_> {
754 |_: ColliderHandle, collider: &RapierCollider| -> bool {
755 let entity = RapierContextColliders::entity_from_collider(collider);
756 (predicate)(entity, collider)
757 }
758}
759
760fn to_rapier_query_filter_combined_predicate<'b>(
762 filter: &QueryFilter<'b>,
763) -> Option<impl Fn(ColliderHandle, &RapierCollider) -> bool + 'b> {
764 filter.predicate.map(to_rapier_query_filter_predicate)
765}
766
767fn to_rapier_query_filter<'b>(
769 filter: &QueryFilter<'_>,
770 colliders: &RapierContextColliders,
771 rigid_bodies: &RapierRigidBodySet,
772 predicate: Option<&'b (impl Fn(ColliderHandle, &RapierCollider) -> bool + 'b)>,
773) -> RapierQueryFilter<'b> {
774 RapierQueryFilter {
775 flags: filter.flags,
776 groups: filter.groups.map(CollisionGroups::into),
777 exclude_collider: filter
778 .exclude_collider
779 .and_then(|c| colliders.entity2collider.get(&c).copied()),
780 exclude_rigid_body: filter
781 .exclude_rigid_body
782 .and_then(|b| rigid_bodies.entity2body.get(&b).copied()),
783 predicate: predicate.map(|p| p as &dyn Fn(ColliderHandle, &RapierCollider) -> bool),
784 }
785}
786
787impl<'a> RapierQueryPipeline<'a> {
788 pub fn new_scoped<T>(
790 broad_phase: &DefaultBroadPhase,
791 colliders: &RapierContextColliders,
792 rigid_bodies: &RapierRigidBodySet,
793 filter: &QueryFilter<'_>,
794 dispatcher: &dyn QueryDispatcher,
795 scoped_fn: impl FnOnce(RapierQueryPipeline<'_>) -> T,
796 ) -> T {
797 let predicate = to_rapier_query_filter_combined_predicate(filter);
798 let rapier_filter =
799 to_rapier_query_filter(filter, colliders, rigid_bodies, predicate.as_ref());
800 let query_pipeline = broad_phase.as_query_pipeline(
801 dispatcher,
802 &rigid_bodies.bodies,
803 &colliders.colliders,
804 rapier_filter,
805 );
806 scoped_fn(RapierQueryPipeline { query_pipeline })
807 }
808
809 pub fn collider_entity(&self, collider_handle: ColliderHandle) -> Entity {
811 RapierContextColliders::collider_entity_with_set(
812 self.query_pipeline.colliders,
813 collider_handle,
814 )
815 .unwrap()
816 }
817
818 pub fn cast_ray(
829 &self,
830 ray_origin: Vect,
831 ray_dir: Vect,
832 max_toi: Real,
833 solid: bool,
834 ) -> Option<(Entity, Real)> {
835 let ray = Ray::new(ray_origin, ray_dir);
836
837 let (h, toi) = self.query_pipeline.cast_ray(&ray, max_toi, solid)?;
838
839 Some((self.collider_entity(h), toi))
840 }
841
842 pub fn cast_ray_and_get_normal(
853 &self,
854 ray_origin: Vect,
855 ray_dir: Vect,
856 max_toi: Real,
857 solid: bool,
858 ) -> Option<(Entity, RayIntersection)> {
859 let ray = Ray::new(ray_origin, ray_dir);
860
861 let (h, result) = self
862 .query_pipeline
863 .cast_ray_and_get_normal(&ray, max_toi, solid)?;
864
865 Some((
866 self.collider_entity(h),
867 RayIntersection::from_rapier(result, ray_origin, ray_dir),
868 ))
869 }
870
871 pub fn intersect_ray(
882 &'a self,
883 ray_origin: Vect,
884 ray_dir: Vect,
885 max_toi: Real,
886 solid: bool,
887 ) -> impl Iterator<Item = (Entity, &'a RapierCollider, RayIntersection)> + 'a {
888 let ray = Ray::new(ray_origin, ray_dir);
889
890 self.query_pipeline.intersect_ray(ray, max_toi, solid).map(
891 move |(_, collider, intersection)| {
892 (
893 RapierContextColliders::entity_from_collider(collider),
894 collider,
895 RayIntersection::from_rapier(intersection, ray_origin, ray_dir),
896 )
897 },
898 )
899 }
900
901 pub fn intersect_shape(
908 &'a self,
909 shape_pos: Vect,
910 shape_rot: Rot,
911 shape: &'a (impl AsShape + ?Sized + 'a),
912 ) -> impl Iterator<Item = (Entity, &'a RapierCollider)> + 'a {
913 let shape = shape.as_shape();
914 let scaled_transform = crate::utils::pose_from(shape_pos, shape_rot);
915
916 self.query_pipeline
917 .intersect_shape(scaled_transform, shape)
918 .map(|(_, collider)| {
919 (
920 RapierContextColliders::entity_from_collider(collider),
921 collider,
922 )
923 })
924 }
925
926 pub fn project_point(
938 &self,
939 point: Vect,
940 max_dist: Real,
941 solid: bool,
942 ) -> Option<(Entity, PointProjection)> {
943 let (h, result) = self.query_pipeline.project_point(point, max_dist, solid)?;
944
945 Some((
946 self.collider_entity(h),
947 PointProjection::from_rapier(result),
948 ))
949 }
950
951 pub fn intersect_point(
956 &'a self,
957 point: Vect,
958 ) -> impl Iterator<Item = (Entity, &'a RapierCollider)> + 'a {
959 self.query_pipeline
960 .intersect_point(point)
961 .map(|(_, collider)| {
962 (
963 RapierContextColliders::entity_from_collider(collider),
964 collider,
965 )
966 })
967 }
968
969 pub fn project_point_and_get_feature(
978 &self,
979 point: Vect,
980 max_dist: Real,
981 ) -> Option<(Entity, PointProjection, FeatureId)> {
982 let (h, proj, fid) = self
983 .query_pipeline
984 .project_point_and_get_feature(point, max_dist)?;
985
986 Some((
987 self.collider_entity(h),
988 PointProjection::from_rapier(proj),
989 fid,
990 ))
991 }
992
993 pub fn intersect_aabb_conservative(
999 &'a self,
1000 #[cfg(feature = "dim2")] aabb: bevy::shape::Aabb2d,
1001 #[cfg(feature = "dim3")] aabb: bevy::shape::Aabb3d,
1002 ) -> impl Iterator<Item = (Entity, &'a RapierCollider)> + 'a {
1003 #[cfg(feature = "dim2")]
1004 let scaled_aabb = Aabb {
1005 mins: aabb.min,
1006 maxs: aabb.max,
1007 };
1008 #[cfg(feature = "dim3")]
1009 let scaled_aabb = Aabb {
1010 mins: aabb.min.into(),
1011 maxs: aabb.max.into(),
1012 };
1013 self.query_pipeline
1014 .intersect_aabb_conservative(scaled_aabb)
1015 .map(|(_, collider)| {
1016 (
1017 RapierContextColliders::entity_from_collider(collider),
1018 collider,
1019 )
1020 })
1021 }
1022
1023 #[allow(clippy::too_many_arguments)]
1045 pub fn cast_shape(
1046 &'a self,
1047 shape_pos: Vect,
1048 shape_rot: Rot,
1049 shape_vel: Vect,
1050 shape: &(impl AsShape + ?Sized),
1051 options: ShapeCastOptions,
1052 ) -> Option<(Entity, ShapeCastHit)> {
1053 let shape = shape.as_shape();
1054 let scaled_transform = crate::utils::pose_from(shape_pos, shape_rot);
1055
1056 let (h, result) =
1057 self.query_pipeline
1058 .cast_shape(&scaled_transform, shape_vel, shape, options)?;
1059
1060 Some((
1061 self.collider_entity(h),
1062 ShapeCastHit::from_rapier(result, options.compute_impact_geometry_on_penetration),
1063 ))
1064 }
1065
1066 pub fn cast_shape_nonlinear(
1084 &self,
1085 shape_motion: &NonlinearMotion,
1086 shape: &(impl AsShape + ?Sized),
1087 start_time: Real,
1088 end_time: Real,
1089 stop_at_penetration: bool,
1090 ) -> Option<(Entity, ShapeCastHit)> {
1091 let shape = shape.as_shape();
1092 let (h, result) = self.query_pipeline.cast_shape_nonlinear(
1093 &shape_motion.into_rapier(),
1094 shape,
1095 start_time,
1096 end_time,
1097 stop_at_penetration,
1098 )?;
1099
1100 Some((
1101 self.collider_entity(h),
1102 ShapeCastHit::from_rapier(result, false),
1103 ))
1104 }
1105
1106 pub fn distance_to_shape(
1116 &self,
1117 shape_pos: Vect,
1118 shape_rot: Rot,
1119 shape: &(impl AsShape + ?Sized),
1120 ) -> Option<(Entity, Real)> {
1121 let shape = shape.as_shape();
1122 let pose = crate::utils::pose_from(shape_pos, shape_rot);
1123 let (id, dist) = CompositeShapeRef(&self.query_pipeline).distance_to_shape(
1124 self.query_pipeline.dispatcher,
1125 &pose,
1126 shape,
1127 )?;
1128 Some((self.collider_entity_from_id(id)?, dist.distance))
1129 }
1130
1131 pub fn closest_points_to_shape(
1144 &self,
1145 shape_pos: Vect,
1146 shape_rot: Rot,
1147 shape: &(impl AsShape + ?Sized),
1148 max_dist: Real,
1149 ) -> Option<(Entity, ShapeClosestPoints)> {
1150 let shape = shape.as_shape();
1151 let pose = crate::utils::pose_from(shape_pos, shape_rot);
1152 let (id, mut pts) = CompositeShapeRef(&self.query_pipeline).closest_points_to_shape(
1153 self.query_pipeline.dispatcher,
1154 &pose,
1155 shape,
1156 max_dist,
1157 )?;
1158 match &mut pts {
1159 ClosestPoints::Disjoint => return None,
1160 ClosestPoints::Intersecting => {}
1161 ClosestPoints::WithinMargin(_, p2) => *p2 = pose * *p2,
1163 }
1164 Some((
1165 self.collider_entity_from_id(id)?,
1166 ShapeClosestPoints::from_rapier(pts),
1167 ))
1168 }
1169
1170 pub fn contact_with_shape(
1183 &self,
1184 shape_pos: Vect,
1185 shape_rot: Rot,
1186 shape: &(impl AsShape + ?Sized),
1187 prediction: Real,
1188 ) -> Option<(Entity, ShapeContact)> {
1189 let shape = shape.as_shape();
1190 let pose = crate::utils::pose_from(shape_pos, shape_rot);
1191 let (id, mut contact) = CompositeShapeRef(&self.query_pipeline).contact_with_shape(
1192 self.query_pipeline.dispatcher,
1193 &pose,
1194 shape,
1195 prediction,
1196 )?;
1197 contact.point2 = pose * contact.point2;
1199 contact.normal2 = pose.rotation * contact.normal2;
1200 Some((
1201 self.collider_entity_from_id(id)?,
1202 ShapeContact::from_rapier(contact),
1203 ))
1204 }
1205
1206 pub fn bvh(&self) -> &'a Bvh {
1212 self.query_pipeline.bvh
1213 }
1214
1215 pub fn bvh_leaf_collider(&self, leaf: u32) -> Option<(Entity, &'a RapierCollider)> {
1219 let (collider, handle) = self.query_pipeline.colliders.get_unknown_gen(leaf)?;
1220 self.query_pipeline
1221 .filter
1222 .test(self.query_pipeline.bodies, handle, collider)
1223 .then(|| {
1224 (
1225 RapierContextColliders::entity_from_collider(collider),
1226 collider,
1227 )
1228 })
1229 }
1230
1231 fn collider_entity_from_id(&self, id: u32) -> Option<Entity> {
1233 self.query_pipeline
1234 .colliders
1235 .get_unknown_gen(id)
1236 .map(|(collider, _)| RapierContextColliders::entity_from_collider(collider))
1237 }
1238}
1239
1240#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1247#[cfg_attr(
1248 feature = "serde-serialize",
1249 serde(from = "serde_shadows::RapierRigidBodySetData")
1250)]
1251#[derive(Component, Default, Clone)]
1252pub struct RapierRigidBodySet {
1253 pub bodies: RigidBodySet,
1255 pub soft_bodies: SoftBodySet,
1257 #[cfg_attr(feature = "serde-serialize", serde(skip))]
1259 pub(crate) entity2body: HashMap<Entity, RigidBodyHandle>,
1260 #[cfg_attr(feature = "serde-serialize", serde(skip))]
1262 pub(crate) entity2soft_body: HashMap<Entity, SoftBodyHandle>,
1263
1264 #[cfg_attr(feature = "serde-serialize", serde(skip))]
1266 pub(crate) last_body_transform_set: HashMap<RigidBodyHandle, GlobalTransform>,
1267}
1268
1269impl RapierRigidBodySet {
1270 pub fn rebuild_entity_maps(&mut self) {
1283 let soft_bodies = &self.soft_bodies;
1284 self.entity2body = self
1285 .bodies
1286 .iter()
1287 .filter(|(_, rb)| {
1288 rb.soft_body()
1290 .and_then(|h| soft_bodies.get(h))
1291 .is_none_or(|sb| sb.user_data != rb.user_data)
1292 })
1293 .filter_map(|(handle, rb)| Some((Entity::try_from_bits(rb.user_data as u64)?, handle)))
1294 .collect();
1295 self.entity2soft_body = self
1296 .soft_bodies
1297 .iter()
1298 .filter_map(|(handle, sb)| Some((Entity::try_from_bits(sb.user_data as u64)?, handle)))
1299 .collect();
1300 let soft_bodies: Vec<_> = self
1301 .entity2soft_body
1302 .iter()
1303 .map(|(entity, handle)| (*entity, *handle))
1304 .collect();
1305 for (entity, handle) in soft_bodies {
1306 self.map_soft_body_whole_proxy(entity, handle);
1307 }
1308 self.last_body_transform_set.clear();
1309 }
1310
1311 pub(crate) fn map_soft_body_whole_proxy(&mut self, entity: Entity, handle: SoftBodyHandle) {
1314 match self.whole_proxy(entity, handle) {
1315 Some(proxy) => {
1316 self.entity2body.insert(entity, proxy);
1317 }
1318 None => {
1319 self.entity2body.remove(&entity);
1320 }
1321 }
1322 }
1323
1324 fn whole_proxy(&self, entity: Entity, handle: SoftBodyHandle) -> Option<RigidBodyHandle> {
1327 let sb = self.soft_bodies.get(handle)?;
1328 let bits = entity.to_bits() as u128;
1329 let stands_for_entity = |proxy: RigidBodyHandle| {
1330 self.bodies
1331 .get(proxy)
1332 .is_some_and(|rb| rb.user_data == bits)
1333 };
1334 let root = sb.root_body();
1335 if stands_for_entity(root) {
1336 return Some(root);
1337 }
1338 sb.live_clusters()
1339 .map(|(_, cluster)| cluster)
1340 .find(|cluster| {
1341 cluster.particles().len() == sb.num_particles()
1342 && stands_for_entity(cluster.proxy())
1343 })
1344 .map(|cluster| cluster.proxy())
1345 }
1346
1347 pub(crate) fn map_soft_body_piece(
1351 &mut self,
1352 colliders: &mut RapierContextColliders,
1353 handle: SoftBodyHandle,
1354 entity: Entity,
1355 ) {
1356 let new_bits = entity.to_bits() as u128;
1357 let Some(sb) = self.soft_bodies.get_mut(handle) else {
1358 return;
1359 };
1360 let old_bits = sb.user_data;
1361 sb.user_data = new_bits;
1362 for (_, cluster) in sb.live_clusters() {
1363 let Some(rb) = self.bodies.get_mut(cluster.proxy()) else {
1364 continue;
1365 };
1366 if rb.user_data == old_bits {
1367 rb.user_data = new_bits;
1368 }
1369 for co_handle in rb.colliders() {
1370 if let Some(co) = colliders.colliders.get_mut(*co_handle) {
1371 if co.user_data == old_bits {
1372 co.user_data = new_bits;
1373 }
1374 }
1375 }
1376 }
1377 self.entity2soft_body.insert(entity, handle);
1378 }
1379
1380 pub(crate) fn map_soft_body_collision_mesh(
1382 &self,
1383 colliders: &mut RapierContextColliders,
1384 handle: SoftBodyHandle,
1385 entity: Entity,
1386 ) {
1387 match self
1388 .soft_bodies
1389 .get(handle)
1390 .and_then(|sb| sb.collision_mesh())
1391 {
1392 Some(mesh) => {
1393 colliders.entity2collider.insert(entity, mesh.collider());
1394 }
1395 None => {
1396 colliders.entity2collider.remove(&entity);
1397 }
1398 }
1399 }
1400
1401 pub fn soft_body_colliders(
1410 &self,
1411 colliders: &ColliderSet,
1412 entity: Entity,
1413 ) -> Option<Vec<ColliderHandle>> {
1414 let sb = self.soft_body(entity)?;
1415 let bits = entity.to_bits() as u128;
1416 let mut result = vec![];
1417 for (_, cluster) in sb.live_clusters() {
1418 let Some(rb) = self.bodies.get(cluster.proxy()) else {
1419 continue;
1420 };
1421 result.extend(
1422 rb.colliders()
1423 .iter()
1424 .copied()
1425 .filter(|h| colliders.get(*h).is_some_and(|co| co.user_data == bits)),
1426 );
1427 }
1428 Some(result)
1429 }
1430
1431 pub fn entity2soft_body(&self) -> &HashMap<Entity, SoftBodyHandle> {
1433 &self.entity2soft_body
1434 }
1435
1436 pub fn soft_body_entity(&self, handle: SoftBodyHandle) -> Option<Entity> {
1438 let entity = Entity::try_from_bits(self.soft_bodies.get(handle)?.user_data as u64)?;
1439 (self.entity2soft_body.get(&entity) == Some(&handle)).then_some(entity)
1440 }
1441
1442 pub fn soft_body(&self, entity: Entity) -> Option<&RapierSoftBody> {
1444 self.soft_bodies.get(*self.entity2soft_body.get(&entity)?)
1445 }
1446
1447 pub fn soft_body_mut(&mut self, entity: Entity) -> Option<&mut RapierSoftBody> {
1454 self.soft_bodies
1455 .get_mut(*self.entity2soft_body.get(&entity)?)
1456 }
1457
1458 pub fn soft_body_particle_positions(
1462 &self,
1463 entity: Entity,
1464 ) -> Option<impl ExactSizeIterator<Item = Vect> + '_> {
1465 Some(self.soft_body(entity)?.particle_positions())
1466 }
1467
1468 pub fn soft_body_particle_velocities(
1472 &self,
1473 entity: Entity,
1474 ) -> Option<impl ExactSizeIterator<Item = Vect> + '_> {
1475 Some(self.soft_body(entity)?.particle_velocities())
1476 }
1477
1478 pub fn soft_body_volume(&self, entity: Entity) -> Option<Real> {
1483 Some(self.soft_body(entity)?.volume())
1484 }
1485
1486 pub fn soft_body_mass(&self, entity: Entity) -> Option<Real> {
1490 Some(self.soft_body(entity)?.mass())
1491 }
1492
1493 pub fn soft_body_center_of_mass(&self, entity: Entity) -> Option<Vect> {
1497 Some(self.soft_body(entity)?.center_of_mass())
1498 }
1499
1500 pub fn is_soft_body_sleeping(&self, entity: Entity) -> Option<bool> {
1504 Some(self.soft_body(entity)?.is_sleeping())
1505 }
1506
1507 pub fn wake_up_soft_body(&mut self, entity: Entity, strong: bool) -> bool {
1512 let Some(handle) = self.entity2soft_body.get(&entity).copied() else {
1513 return false;
1514 };
1515 self.soft_bodies.wake_up(handle, &mut self.bodies, strong);
1516 true
1517 }
1518
1519 pub fn soft_body_cluster_proxy(&self, entity: Entity, cluster: u32) -> Option<RigidBodyHandle> {
1524 self.soft_body(entity)?.cluster_proxy(cluster)
1525 }
1526
1527 pub fn soft_body_whole_proxy(&self, entity: Entity) -> Option<RigidBodyHandle> {
1534 self.whole_proxy(entity, *self.entity2soft_body.get(&entity)?)
1535 }
1536
1537 pub fn soft_body_cluster_index(&self, entity: Entity) -> Option<(SoftBodyHandle, u32)> {
1545 let proxy = *self.entity2body.get(&entity)?;
1546 let handle = self.bodies.get(proxy)?.soft_body()?;
1547 let sb = self.soft_bodies.get(handle)?;
1548 let index = self
1549 .bodies
1550 .get(proxy)?
1551 .soft_cluster()
1552 .filter(|i| sb.cluster_proxy(*i) == Some(proxy))
1553 .or_else(|| {
1554 sb.live_clusters()
1555 .find(|(_, cluster)| cluster.proxy() == proxy)
1556 .map(|(i, _)| i)
1557 })?;
1558 Some((handle, index))
1559 }
1560
1561 pub fn entity2body(&self) -> &HashMap<Entity, RigidBodyHandle> {
1563 &self.entity2body
1564 }
1565
1566 pub fn rigid_body_entity(&self, handle: RigidBodyHandle) -> Option<Entity> {
1568 self.bodies
1569 .get(handle)
1570 .map(|c| Entity::from_bits(c.user_data as u64))
1571 }
1572
1573 pub fn propagate_modified_body_positions_to_colliders(
1576 &self,
1577 colliders: &mut RapierContextColliders,
1578 ) {
1579 self.bodies
1580 .propagate_modified_body_positions_to_colliders(&mut colliders.colliders);
1581 }
1582
1583 pub fn impulse_revolute_joint_angle(
1589 &self,
1590 joints: &RapierContextJoints,
1591 entity: Entity,
1592 ) -> Option<f32> {
1593 let joint_handle = joints.entity2impulse_joint().get(&entity)?;
1594 let impulse_joint = joints.impulse_joints.get(*joint_handle)?;
1595 let revolute_joint = impulse_joint.data.as_revolute()?;
1596
1597 let rb1 = &self.bodies[impulse_joint.body1()];
1598 let rb2 = &self.bodies[impulse_joint.body2()];
1599 Some(revolute_joint.angle(rb1.rotation(), rb2.rotation()))
1600 }
1601
1602 fn body(&self, entity: Entity) -> Option<&rapier::dynamics::RigidBody> {
1604 self.bodies.get(*self.entity2body.get(&entity)?)
1605 }
1606
1607 pub fn velocity_at_point(&self, entity: Entity, point: Vect) -> Option<Vect> {
1613 Some(self.body(entity)?.velocity_at_point(point))
1614 }
1615
1616 pub fn is_moving(&self, entity: Entity) -> Option<bool> {
1620 Some(self.body(entity)?.is_moving())
1621 }
1622
1623 pub fn is_ccd_active(&self, entity: Entity) -> Option<bool> {
1628 Some(self.body(entity)?.is_ccd_active())
1629 }
1630
1631 pub fn next_position(&self, entity: Entity) -> Option<Transform> {
1637 Some(crate::utils::iso_to_transform(
1638 self.body(entity)?.next_position(),
1639 ))
1640 }
1641
1642 pub fn predict_position_using_velocity_and_forces(
1647 &self,
1648 entity: Entity,
1649 dt: Real,
1650 ) -> Option<Transform> {
1651 Some(crate::utils::iso_to_transform(
1652 &self
1653 .body(entity)?
1654 .predict_position_using_velocity_and_forces(dt),
1655 ))
1656 }
1657
1658 pub fn predict_position_using_velocity(&self, entity: Entity, dt: Real) -> Option<Transform> {
1663 Some(crate::utils::iso_to_transform(
1664 &self.body(entity)?.predict_position_using_velocity(dt),
1665 ))
1666 }
1667
1668 pub fn kinetic_energy(&self, entity: Entity) -> Option<Real> {
1672 Some(self.body(entity)?.kinetic_energy())
1673 }
1674
1675 pub fn gravitational_potential_energy(
1680 &self,
1681 entity: Entity,
1682 dt: Real,
1683 gravity: Vect,
1684 ) -> Option<Real> {
1685 Some(
1686 self.body(entity)?
1687 .gravitational_potential_energy(dt, gravity),
1688 )
1689 }
1690
1691 pub fn user_force(&self, entity: Entity) -> Option<Vect> {
1696 Some(self.body(entity)?.user_force())
1697 }
1698
1699 #[cfg(feature = "dim2")]
1704 pub fn user_torque(&self, entity: Entity) -> Option<Real> {
1705 Some(self.body(entity)?.user_torque())
1706 }
1707
1708 #[cfg(feature = "dim3")]
1713 pub fn user_torque(&self, entity: Entity) -> Option<Vect> {
1714 Some(self.body(entity)?.user_torque())
1715 }
1716
1717 pub fn effective_dominance_group(&self, entity: Entity) -> Option<i16> {
1722 Some(self.body(entity)?.effective_dominance_group())
1723 }
1724
1725 pub fn center_of_mass(&self, entity: Entity) -> Option<Vect> {
1729 Some(self.body(entity)?.center_of_mass())
1730 }
1731
1732 pub fn mass(&self, entity: Entity) -> Option<Real> {
1736 Some(self.body(entity)?.mass())
1737 }
1738}
1739
1740fn apply_kinematic_targets(bodies: &mut RigidBodySet) {
1744 let targets: Vec<_> = bodies
1745 .iter()
1746 .filter(|(_, rb)| {
1747 rb.body_type() == RigidBodyType::KinematicPositionBased
1748 && rb.position() != rb.next_position()
1749 })
1750 .map(|(handle, rb)| (handle, *rb.next_position()))
1751 .collect();
1752 for (handle, pose) in targets {
1753 if let Some(rb) = bodies.get_mut(handle) {
1754 rb.set_position(pose, false);
1755 }
1756 }
1757}
1758
1759pub(crate) struct PendingSoftBodyTear {
1761 pub raw: RapierSoftBodyTearEvent,
1763 pub piece_entities: Vec<Entity>,
1766}
1767
1768#[derive(Copy, Clone, Debug, Default, PartialEq)]
1778pub struct SimulationStepStats {
1779 pub num_steps: usize,
1781 pub step_time_ms: f64,
1783 pub collision_detection_time_ms: f64,
1785 pub broad_phase_time_ms: f64,
1787 pub narrow_phase_time_ms: f64,
1789 pub island_construction_time_ms: f64,
1791 pub solver_time_ms: f64,
1793 pub ccd_time_ms: f64,
1795 pub update_time_ms: f64,
1797 pub ccd_substeps: usize,
1799 pub num_contact_pairs: usize,
1802 pub num_solver_constraints: usize,
1805 pub num_solver_contacts: usize,
1808}
1809
1810impl SimulationStepStats {
1811 fn accumulate_counters(&mut self, pipeline: &PhysicsPipeline) {
1813 let counters = &pipeline.counters;
1814 self.collision_detection_time_ms += counters.collision_detection_time_ms();
1815 self.broad_phase_time_ms += counters.broad_phase_time_ms();
1816 self.narrow_phase_time_ms += counters.narrow_phase_time_ms();
1817 self.island_construction_time_ms += counters.island_construction_time_ms();
1818 self.solver_time_ms += counters.solver_time_ms();
1819 self.ccd_time_ms += counters.ccd_time_ms();
1820 self.update_time_ms += counters.update_time_ms();
1821 self.ccd_substeps += counters.ccd.num_substeps;
1822 self.num_contact_pairs = counters.cd.ncontact_pairs;
1823 self.num_solver_constraints += counters.solver.nconstraints;
1824 self.num_solver_contacts += counters.solver.ncontacts;
1825 }
1826}
1827
1828#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1834#[derive(Component)]
1835#[require(
1836 RapierContextColliders,
1837 RapierRigidBodySet,
1838 RapierContextJoints,
1839 SimulationToRenderTime
1840)]
1841pub struct RapierContextSimulation {
1842 pub islands: IslandManager,
1845 pub broad_phase: DefaultBroadPhase,
1847 pub narrow_phase: NarrowPhase,
1850 pub ccd_solver: CCDSolver,
1852 #[cfg_attr(feature = "serde-serialize", serde(skip))]
1854 pub pipeline: PhysicsPipeline,
1855 #[cfg_attr(feature = "serde-serialize", serde(skip))]
1858 pub collision_pipeline: CollisionPipeline,
1859 pub integration_parameters: IntegrationParameters,
1861 #[cfg_attr(feature = "serde-serialize", serde(skip))]
1863 pub(crate) event_handler: Option<Box<dyn EventHandler + Send + Sync>>,
1864 #[cfg_attr(feature = "serde-serialize", serde(skip))]
1868 pub(crate) deleted_colliders: HashMap<ColliderHandle, Entity>,
1869
1870 #[cfg_attr(feature = "serde-serialize", serde(skip))]
1871 pub(crate) collision_events_to_send: Vec<CollisionEvent>,
1872 #[cfg_attr(feature = "serde-serialize", serde(skip))]
1873 pub(crate) contact_force_events_to_send: Vec<ContactForceEvent>,
1874 #[cfg_attr(feature = "serde-serialize", serde(skip))]
1875 pub(crate) character_collisions_collector: Vec<rapier::control::CharacterCollision>,
1876 #[cfg_attr(feature = "serde-serialize", serde(skip))]
1877 pub(crate) step_stats: SimulationStepStats,
1878 #[cfg_attr(feature = "serde-serialize", serde(skip))]
1880 pub(crate) quarantined_bodies: Vec<Entity>,
1881 #[cfg_attr(feature = "serde-serialize", serde(skip))]
1883 pub(crate) quarantined_colliders: Vec<Entity>,
1884 #[cfg_attr(feature = "serde-serialize", serde(skip))]
1886 pub(crate) quarantined_soft_bodies: Vec<Entity>,
1887 #[cfg_attr(feature = "serde-serialize", serde(skip))]
1889 pub(crate) requested_num_threads: Option<usize>,
1890 #[cfg_attr(feature = "serde-serialize", serde(skip))]
1895 pub(crate) pending_soft_body_tears: Vec<PendingSoftBodyTear>,
1896}
1897
1898impl Default for RapierContextSimulation {
1899 fn default() -> Self {
1900 Self {
1901 islands: IslandManager::new(),
1902 broad_phase: DefaultBroadPhase::new(),
1903 narrow_phase: NarrowPhase::new(),
1904 ccd_solver: CCDSolver::new(),
1905 pipeline: PhysicsPipeline::new(),
1906 collision_pipeline: CollisionPipeline::new(),
1907 integration_parameters: IntegrationParameters::default(),
1908 event_handler: None,
1909 deleted_colliders: HashMap::default(),
1910 collision_events_to_send: Vec::new(),
1911 contact_force_events_to_send: Vec::new(),
1912 character_collisions_collector: Vec::new(),
1913 step_stats: SimulationStepStats::default(),
1914 quarantined_bodies: Vec::new(),
1915 quarantined_colliders: Vec::new(),
1916 quarantined_soft_bodies: Vec::new(),
1917 requested_num_threads: None,
1918 pending_soft_body_tears: Vec::new(),
1919 }
1920 }
1921}
1922
1923impl RapierContextSimulation {
1924 pub fn with_broad_phase_optimization_strategy(
1931 strategy: BroadPhaseOptimizationStrategy,
1932 ) -> Self {
1933 Self {
1934 broad_phase: DefaultBroadPhase::with_optimization_strategy(strategy.into()),
1935 ..Self::default()
1936 }
1937 }
1938
1939 pub fn step_stats(&self) -> &SimulationStepStats {
1941 &self.step_stats
1942 }
1943
1944 pub fn set_num_threads(&mut self, num_threads: Option<usize>) {
1954 if self.requested_num_threads == num_threads {
1955 return;
1956 }
1957 self.requested_num_threads = num_threads;
1958
1959 #[cfg(all(feature = "parallel", not(feature = "unsync-callbacks")))]
1960 match num_threads {
1961 Some(n) => {
1962 if let Err(err) = self.pipeline.configure_thread_pool(n) {
1963 log::error!("Failed to create a Rapier thread pool with {n} threads: {err}");
1964 }
1965 }
1966 None => self.pipeline.clear_thread_pool(),
1967 }
1968 }
1969
1970 pub fn with_query_dispatcher<D>(dispatcher: D) -> Self
1978 where
1979 D: 'static + PersistentQueryDispatcher<ContactManifoldData, ContactData>,
1980 {
1981 Self {
1982 narrow_phase: NarrowPhase::with_query_dispatcher(dispatcher),
1983 ..Self::default()
1984 }
1985 }
1986
1987 pub fn set_query_dispatcher<D>(&mut self, dispatcher: D)
1995 where
1996 D: 'static + PersistentQueryDispatcher<ContactManifoldData, ContactData>,
1997 {
1998 self.narrow_phase = NarrowPhase::with_query_dispatcher(dispatcher);
1999 }
2000
2001 pub fn query_dispatcher(
2003 &self,
2004 ) -> &dyn PersistentQueryDispatcher<ContactManifoldData, ContactData> {
2005 self.narrow_phase.query_dispatcher()
2006 }
2007
2008 pub fn set_event_handler(&mut self, handler: impl EventHandler + Send + Sync + 'static) {
2018 self.event_handler = Some(Box::new(handler));
2019 }
2020
2021 pub fn remove_event_handler(&mut self) -> Option<Box<dyn EventHandler + Send + Sync>> {
2023 self.event_handler.take()
2024 }
2025
2026 pub fn event_handler(&self) -> Option<&(dyn EventHandler + Send + Sync)> {
2028 self.event_handler.as_deref()
2029 }
2030
2031 pub(crate) fn remove_soft_body(
2034 &mut self,
2035 colliders: &mut RapierContextColliders,
2036 joints: &mut RapierContextJoints,
2037 rigidbody_set: &mut RapierRigidBodySet,
2038 handle: SoftBodyHandle,
2039 ) -> Option<RapierSoftBody> {
2040 let sb = rigidbody_set.soft_bodies.get(handle)?;
2041 for (_, cluster) in sb.live_clusters() {
2042 let proxy = cluster.proxy();
2043 let Some(rb) = rigidbody_set.bodies.get(proxy) else {
2044 continue;
2045 };
2046 if let Some(entity) = Entity::try_from_bits(rb.user_data as u64) {
2047 if rigidbody_set.entity2body.get(&entity) == Some(&proxy) {
2048 rigidbody_set.entity2body.remove(&entity);
2049 rigidbody_set.last_body_transform_set.remove(&proxy);
2050 }
2051 }
2052 for co_handle in rb.colliders() {
2053 let Some(co) = colliders.colliders.get(*co_handle) else {
2054 continue;
2055 };
2056 let Some(entity) = Entity::try_from_bits(co.user_data as u64) else {
2057 continue;
2058 };
2059 self.deleted_colliders.insert(*co_handle, entity);
2061 if colliders.entity2collider.get(&entity) == Some(co_handle) {
2062 colliders.entity2collider.remove(&entity);
2063 }
2064 }
2065 for (_, _, joint_handle, joint) in joints.impulse_joints.attached_joints(proxy) {
2066 let entity = RapierContextJoints::entity_from_impulse_joint(joint);
2067 if joints.entity2impulse_joint.get(&entity) == Some(&joint_handle) {
2068 joints.entity2impulse_joint.remove(&entity);
2069 }
2070 }
2071 }
2072 rigidbody_set.entity2soft_body.retain(|_, h| *h != handle);
2073 rigidbody_set.soft_bodies.remove(
2074 handle,
2075 &mut self.islands,
2076 &mut rigidbody_set.bodies,
2077 &mut colliders.colliders,
2078 &mut joints.impulse_joints,
2079 &mut joints.multibody_joints,
2080 )
2081 }
2082
2083 #[allow(clippy::too_many_arguments)]
2092 pub fn tear_soft_body(
2093 &mut self,
2094 colliders: &mut RapierContextColliders,
2095 joints: &mut RapierContextJoints,
2096 rigidbody_set: &mut RapierRigidBodySet,
2097 commands: &mut Commands,
2098 entity: Entity,
2099 edges: &[u32],
2100 cells: &[u32],
2101 ) -> Option<SoftBodyTearResult> {
2102 let handle = *rigidbody_set.entity2soft_body.get(&entity)?;
2103 let event = rigidbody_set.soft_bodies.tear(
2104 handle,
2105 edges,
2106 cells,
2107 &mut self.islands,
2108 &mut rigidbody_set.bodies,
2109 &mut colliders.colliders,
2110 &mut joints.impulse_joints,
2111 &mut joints.multibody_joints,
2112 )?;
2113 Some(self.map_manual_tear(colliders, rigidbody_set, commands, entity, event))
2114 }
2115
2116 pub fn cut_soft_body(
2125 &mut self,
2126 colliders: &mut RapierContextColliders,
2127 joints: &mut RapierContextJoints,
2128 rigidbody_set: &mut RapierRigidBodySet,
2129 commands: &mut Commands,
2130 entity: Entity,
2131 blade: &[Vect; rapier::math::DIM],
2132 ) -> Option<SoftBodyTearResult> {
2133 let handle = *rigidbody_set.entity2soft_body.get(&entity)?;
2134 let event = rigidbody_set.soft_bodies.cut(
2135 handle,
2136 blade,
2137 &mut self.islands,
2138 &mut rigidbody_set.bodies,
2139 &mut colliders.colliders,
2140 &mut joints.impulse_joints,
2141 &mut joints.multibody_joints,
2142 )?;
2143 Some(self.map_manual_tear(colliders, rigidbody_set, commands, entity, event))
2144 }
2145
2146 fn map_manual_tear(
2149 &mut self,
2150 colliders: &mut RapierContextColliders,
2151 rigidbody_set: &mut RapierRigidBodySet,
2152 commands: &mut Commands,
2153 entity: Entity,
2154 event: RapierSoftBodyTearEvent,
2155 ) -> SoftBodyTearResult {
2156 let mut pieces = Vec::with_capacity(event.pieces.len());
2157 for (k, piece) in event.pieces.iter().enumerate() {
2158 let piece_entity = if k == 0 {
2159 entity
2160 } else {
2161 let piece_entity = commands.spawn_empty().id();
2162 rigidbody_set.map_soft_body_piece(colliders, piece.soft_body, piece_entity);
2163 piece_entity
2164 };
2165 rigidbody_set.map_soft_body_collision_mesh(colliders, piece.soft_body, piece_entity);
2166 rigidbody_set.map_soft_body_whole_proxy(piece_entity, piece.soft_body);
2167 pieces.push(piece_entity);
2168 }
2169 if event.pieces.is_empty() {
2170 rigidbody_set.map_soft_body_collision_mesh(colliders, event.soft_body, entity);
2171 rigidbody_set.map_soft_body_whole_proxy(entity, event.soft_body);
2172 }
2173 self.pending_soft_body_tears.push(PendingSoftBodyTear {
2174 raw: event.clone(),
2175 piece_entities: pieces.get(1..).unwrap_or_default().to_vec(),
2176 });
2177 SoftBodyTearResult { pieces, raw: event }
2178 }
2179
2180 #[allow(clippy::too_many_arguments)]
2188 pub fn step_simulation(
2189 &mut self,
2190 colliders: &mut RapierContextColliders,
2191 joints: &mut RapierContextJoints,
2192 rigidbody_set: &mut RapierRigidBodySet,
2193 gravity: Vect,
2194 simulation_mode: SimulationMode,
2195 timestep_mode: TimestepMode,
2196 events: Option<(
2197 &MessageWriter<CollisionEvent>,
2198 &MessageWriter<ContactForceEvent>,
2199 )>,
2200 hooks: &dyn PhysicsHooks,
2201 time: &Time,
2202 sim_to_render_time: &mut SimulationToRenderTime,
2203 mut interpolation_query: Option<
2204 &mut Query<(&RapierRigidBodyHandle, &mut TransformInterpolation)>,
2205 >,
2206 ) {
2207 self.step_stats = SimulationStepStats::default();
2208
2209 let user_event_handler = self.event_handler.take();
2212 let deleted_colliders = std::mem::take(&mut self.deleted_colliders);
2213
2214 let event_queue = if events.is_some() {
2215 Some(EventQueue {
2216 deleted_colliders: &deleted_colliders,
2217 collision_events: RwLock::new(Vec::new()),
2218 contact_force_events: RwLock::new(Vec::new()),
2219 })
2220 } else {
2221 None
2222 };
2223
2224 let soft_body_tears = RwLock::new(Vec::new());
2225 let fan_out = EventHandlerFanOut {
2226 queue: event_queue.as_ref(),
2227 user: user_event_handler.as_deref(),
2228 soft_body_tears: &soft_body_tears,
2229 };
2230 let event_handler = &fan_out as &dyn EventHandler;
2231
2232 let mut executed_steps = 0;
2233 match timestep_mode {
2234 TimestepMode::Interpolated {
2235 dt,
2236 time_scale,
2237 substeps,
2238 } => {
2239 self.integration_parameters.dt = dt;
2240
2241 sim_to_render_time.diff += time.delta_secs();
2242
2243 while sim_to_render_time.diff > 0.0 {
2244 if sim_to_render_time.diff - dt <= 0.0 {
2248 if let Some(interpolation_query) = interpolation_query.as_mut() {
2249 for (handle, mut interpolation) in interpolation_query.iter_mut() {
2252 if let Some(body) = rigidbody_set.bodies.get(handle.0) {
2253 interpolation.start = Some(*body.position());
2254 interpolation.end = None;
2255 }
2256 }
2257 }
2258 }
2259
2260 let mut substep_integration_parameters = self.integration_parameters;
2261 substep_integration_parameters.dt = dt / (substeps as Real) * time_scale;
2262
2263 let num_substeps = if substep_integration_parameters.dt > 0.0 {
2265 substeps
2266 } else {
2267 0
2268 };
2269
2270 for _ in 0..num_substeps {
2271 self.step_once(
2272 simulation_mode,
2273 gravity,
2274 &substep_integration_parameters,
2275 colliders,
2276 joints,
2277 rigidbody_set,
2278 hooks,
2279 event_handler,
2280 );
2281 executed_steps += 1;
2282 }
2283
2284 sim_to_render_time.diff -= dt;
2285 }
2286 }
2287 TimestepMode::Variable {
2288 max_dt,
2289 time_scale,
2290 substeps,
2291 } => {
2292 self.integration_parameters.dt = (time.delta_secs() * time_scale).min(max_dt);
2293
2294 let mut substep_integration_parameters = self.integration_parameters;
2295 substep_integration_parameters.dt /= substeps as Real;
2296
2297 let num_substeps = if substep_integration_parameters.dt > 0.0 {
2300 substeps
2301 } else {
2302 0
2303 };
2304
2305 for _ in 0..num_substeps {
2306 self.step_once(
2307 simulation_mode,
2308 gravity,
2309 &substep_integration_parameters,
2310 colliders,
2311 joints,
2312 rigidbody_set,
2313 hooks,
2314 event_handler,
2315 );
2316 executed_steps += 1;
2317 }
2318 }
2319 TimestepMode::Fixed { dt, substeps } => {
2320 self.integration_parameters.dt = dt;
2321
2322 let mut substep_integration_parameters = self.integration_parameters;
2323 substep_integration_parameters.dt = dt / (substeps as Real);
2324
2325 let num_substeps = if substep_integration_parameters.dt > 0.0 {
2326 substeps
2327 } else {
2328 0
2329 };
2330
2331 for _ in 0..num_substeps {
2332 self.step_once(
2333 simulation_mode,
2334 gravity,
2335 &substep_integration_parameters,
2336 colliders,
2337 joints,
2338 rigidbody_set,
2339 hooks,
2340 event_handler,
2341 );
2342 executed_steps += 1;
2343 }
2344 }
2345 }
2346 if let Some(mut event_queue) = event_queue {
2347 self.collision_events_to_send =
2350 std::mem::take(event_queue.collision_events.get_mut().unwrap());
2351 self.contact_force_events_to_send =
2352 std::mem::take(event_queue.contact_force_events.get_mut().unwrap());
2353 }
2354
2355 self.pending_soft_body_tears.extend(
2357 soft_body_tears
2358 .into_inner()
2359 .unwrap_or_default()
2360 .into_iter()
2361 .map(|raw| PendingSoftBodyTear {
2362 raw,
2363 piece_entities: vec![],
2364 }),
2365 );
2366
2367 self.event_handler = user_event_handler;
2368 if executed_steps == 0 {
2369 self.deleted_colliders = deleted_colliders;
2370 rigidbody_set.propagate_modified_body_positions_to_colliders(colliders);
2373 }
2374 }
2375
2376 #[allow(clippy::too_many_arguments)]
2379 fn step_once(
2380 &mut self,
2381 simulation_mode: SimulationMode,
2382 gravity: Vect,
2383 integration_parameters: &IntegrationParameters,
2384 colliders: &mut RapierContextColliders,
2385 joints: &mut RapierContextJoints,
2386 rigidbody_set: &mut RapierRigidBodySet,
2387 hooks: &dyn PhysicsHooks,
2388 event_handler: &dyn EventHandler,
2389 ) {
2390 let start = bevy::platform::time::Instant::now();
2391
2392 match simulation_mode {
2393 SimulationMode::Full => {
2394 self.pipeline.step(
2395 gravity,
2396 integration_parameters,
2397 &mut self.islands,
2398 &mut self.broad_phase,
2399 &mut self.narrow_phase,
2400 &mut rigidbody_set.bodies,
2401 &mut colliders.colliders,
2402 &mut joints.impulse_joints,
2403 &mut joints.multibody_joints,
2404 &mut rigidbody_set.soft_bodies,
2405 &mut self.ccd_solver,
2406 hooks,
2407 event_handler,
2408 );
2409 }
2410 SimulationMode::CollisionOnly => {
2411 apply_kinematic_targets(&mut rigidbody_set.bodies);
2412 self.collision_pipeline.step(
2413 integration_parameters.prediction_distance(),
2414 &mut self.islands,
2415 &mut self.broad_phase,
2416 &mut self.narrow_phase,
2417 &mut rigidbody_set.bodies,
2418 &mut colliders.colliders,
2419 hooks,
2420 event_handler,
2421 );
2422 }
2423 }
2424
2425 self.step_stats.num_steps += 1;
2426 self.step_stats.step_time_ms += start.elapsed().as_secs_f64() * 1000.0;
2427
2428 if self.pipeline.counters.enabled() {
2430 match simulation_mode {
2431 SimulationMode::Full => self.step_stats.accumulate_counters(&self.pipeline),
2432 SimulationMode::CollisionOnly => {
2434 self.step_stats.num_contact_pairs = self.narrow_phase.contact_pairs().count();
2435 }
2436 }
2437 }
2438
2439 if simulation_mode == SimulationMode::Full {
2440 let quarantine = self.pipeline.quarantine();
2441 if !quarantine.is_empty() {
2442 self.quarantined_bodies.extend(
2443 quarantine
2444 .bodies()
2445 .iter()
2446 .filter_map(|h| rigidbody_set.rigid_body_entity(*h)),
2447 );
2448 self.quarantined_colliders.extend(
2449 quarantine
2450 .colliders()
2451 .iter()
2452 .filter_map(|h| colliders.collider_entity(*h)),
2453 );
2454 self.quarantined_soft_bodies.extend(
2455 quarantine
2456 .soft_bodies()
2457 .iter()
2458 .filter_map(|h| rigidbody_set.soft_body_entity(*h)),
2459 );
2460 }
2461 }
2462 }
2463
2464 pub fn send_quarantine_events(
2470 &mut self,
2471 context: Entity,
2472 quarantine_event_writer: &mut MessageWriter<PhysicsQuarantineEvent>,
2473 ) {
2474 if self.quarantined_bodies.is_empty()
2475 && self.quarantined_colliders.is_empty()
2476 && self.quarantined_soft_bodies.is_empty()
2477 {
2478 return;
2479 }
2480
2481 let event = PhysicsQuarantineEvent {
2482 context,
2483 bodies: std::mem::take(&mut self.quarantined_bodies),
2484 colliders: std::mem::take(&mut self.quarantined_colliders),
2485 soft_bodies: std::mem::take(&mut self.quarantined_soft_bodies),
2486 };
2487 log::warn!(
2488 "Rapier detected non-finite (NaN or infinite) state in context {context}; the \
2489 following rigid-bodies were rolled back and disabled: {:?}; the following colliders \
2490 were disabled: {:?}; the following soft bodies were disabled: {:?}.",
2491 event.bodies,
2492 event.colliders,
2493 event.soft_bodies
2494 );
2495 quarantine_event_writer.write(event);
2496 }
2497
2498 pub fn send_bevy_events(
2501 &mut self,
2502 collision_event_writer: &mut MessageWriter<CollisionEvent>,
2503 contact_force_event_writer: &mut MessageWriter<ContactForceEvent>,
2504 ) {
2505 for collision_event in self.collision_events_to_send.drain(..) {
2506 collision_event_writer.write(collision_event);
2507 }
2508 for contact_force_event in self.contact_force_events_to_send.drain(..) {
2509 contact_force_event_writer.write(contact_force_event);
2510 }
2511 }
2512
2513 #[allow(clippy::too_many_arguments)]
2532 pub fn move_shape(
2533 &mut self,
2534 colliders: &mut RapierContextColliders,
2535 rigidbody_set: &mut RapierRigidBodySet,
2536 movement: Vect,
2537 shape: &(impl AsShape + ?Sized),
2538 shape_translation: Vect,
2539 shape_rotation: Rot,
2540 shape_mass: Real,
2541 options: &MoveShapeOptions,
2542 filter: QueryFilter,
2543 mut events: impl FnMut(CharacterCollision),
2544 ) -> MoveShapeOutput {
2545 let shape = shape.as_shape();
2546 assert!(
2547 options.up.length_squared() > 0.0,
2548 "The up vector must be non-zero."
2549 );
2550 let up = options.up.normalize();
2551 let autostep = options.autostep.map(|autostep| CharacterAutostep {
2552 max_height: autostep.max_height,
2553 min_width: autostep.min_width,
2554 include_dynamic_bodies: autostep.include_dynamic_bodies,
2555 });
2556 let controller = rapier::control::KinematicCharacterController {
2557 up,
2558 offset: options.offset,
2559 slide: options.slide,
2560 autostep,
2561 max_slope_climb_angle: options.max_slope_climb_angle,
2562 min_slope_slide_angle: options.min_slope_slide_angle,
2563 snap_to_ground: options.snap_to_ground,
2564 normal_nudge_factor: options.normal_nudge_factor,
2565 };
2566
2567 let dt = self.integration_parameters.dt;
2568 let collisions = &mut self.character_collisions_collector;
2569 collisions.clear();
2570
2571 let result = RapierQueryPipelineMut::new_scoped(
2572 &self.broad_phase,
2573 colliders,
2574 rigidbody_set,
2575 &filter,
2576 self.narrow_phase.query_dispatcher(),
2577 |mut query_pipeline| {
2578 let result = controller.move_shape(
2579 dt,
2580 &query_pipeline.query_pipeline.as_ref(),
2581 shape,
2582 &crate::utils::pose_from(shape_translation, shape_rotation),
2583 movement,
2584 |c| collisions.push(c),
2585 );
2586
2587 if options.apply_impulse_to_dynamic_bodies {
2588 controller.solve_character_collision_impulses(
2589 dt,
2590 &mut query_pipeline.query_pipeline,
2591 shape,
2592 shape_mass,
2593 collisions.iter(),
2594 );
2595 }
2596
2597 result
2598 },
2599 );
2600
2601 for collision in collisions.iter() {
2602 if let Some(collision) =
2603 CharacterCollision::from_raw_with_set(&colliders.colliders, collision, true)
2604 {
2605 events(collision);
2606 }
2607 }
2608
2609 MoveShapeOutput {
2610 effective_translation: result.translation,
2611 grounded: result.grounded,
2612 is_sliding_down_slope: result.is_sliding_down_slope,
2613 }
2614 }
2615}
2616
2617#[cfg(feature = "serde-serialize")]
2619mod serde_shadows {
2620 use super::*;
2621 use rapier::prelude::{ImpulseJointSet, MultibodyJointSet};
2622
2623 #[derive(Deserialize)]
2624 pub(super) struct RapierContextCollidersData {
2625 colliders: ColliderSet,
2626 }
2627
2628 impl From<RapierContextCollidersData> for RapierContextColliders {
2629 fn from(data: RapierContextCollidersData) -> Self {
2630 let mut result = Self {
2631 colliders: data.colliders,
2632 entity2collider: HashMap::default(),
2633 };
2634 result.rebuild_entity_maps();
2635 result
2636 }
2637 }
2638
2639 #[derive(Deserialize)]
2640 pub(super) struct RapierContextJointsData {
2641 impulse_joints: ImpulseJointSet,
2642 multibody_joints: MultibodyJointSet,
2643 }
2644
2645 impl From<RapierContextJointsData> for RapierContextJoints {
2646 fn from(data: RapierContextJointsData) -> Self {
2647 let mut result = Self {
2648 impulse_joints: data.impulse_joints,
2649 multibody_joints: data.multibody_joints,
2650 entity2impulse_joint: HashMap::default(),
2651 entity2multibody_joint: HashMap::default(),
2652 };
2653 result.rebuild_entity_maps();
2654 result
2655 }
2656 }
2657
2658 #[derive(Deserialize)]
2659 pub(super) struct RapierRigidBodySetData {
2660 bodies: RigidBodySet,
2661 soft_bodies: SoftBodySet,
2662 }
2663
2664 impl From<RapierRigidBodySetData> for RapierRigidBodySet {
2665 fn from(data: RapierRigidBodySetData) -> Self {
2666 let mut result = Self {
2667 bodies: data.bodies,
2668 soft_bodies: data.soft_bodies,
2669 entity2body: HashMap::default(),
2670 entity2soft_body: HashMap::default(),
2671 last_body_transform_set: HashMap::default(),
2672 };
2673 result.rebuild_entity_maps();
2674 result
2675 }
2676 }
2677}