Skip to main content

bevy_rapier2d/plugin/context/
mod.rs

1//! These are components used and modified during a simulation frame.
2
3pub mod systemparams;
4
5use bevy::prelude::*;
6use rapier::parry::query::QueryDispatcher;
7use std::collections::HashMap;
8use std::sync::RwLock;
9
10use rapier::prelude::{
11    Aabb, CCDSolver, ColliderHandle, ColliderSet, EventHandler, FeatureId, ImpulseJointHandle,
12    ImpulseJointSet, IntegrationParameters, IslandManager, MultibodyJointHandle, MultibodyJointSet,
13    NarrowPhase, PhysicsHooks, PhysicsPipeline, QueryFilter as RapierQueryFilter, QueryPipeline,
14    QueryPipelineMut, Ray, Real, RigidBodyHandle, RigidBodySet, Shape,
15};
16
17use crate::geometry::{PointProjection, RayIntersection, ShapeCastHit};
18use crate::math::{Rot, Vect};
19use crate::pipeline::{CollisionEvent, ContactForceEvent, EventQueue};
20use bevy::prelude::{Entity, GlobalTransform, Query};
21
22use crate::control::{CharacterCollision, MoveShapeOptions, MoveShapeOutput};
23use crate::dynamics::TransformInterpolation;
24use crate::parry::query::details::ShapeCastOptions;
25use crate::plugin::configuration::TimestepMode;
26use crate::prelude::{CollisionGroups, QueryFilter, RapierRigidBodyHandle};
27use rapier::control::CharacterAutostep;
28use rapier::geometry::DefaultBroadPhase;
29
30#[cfg(doc)]
31use crate::prelude::{
32    systemparams::{RapierContext, ReadRapierContext},
33    ImpulseJoint, MultibodyJoint, RevoluteJoint, TypedJoint,
34};
35
36/// Difference between simulation and rendering time
37#[derive(Component, Default, Reflect, Clone)]
38pub struct SimulationToRenderTime {
39    /// Difference between simulation and rendering time
40    pub diff: f32,
41}
42
43/// Marker component for to access the default [`ReadRapierContext`].
44///
45/// This is used as the default marker filter for [`systemparams::ReadRapierContext`] and [`systemparams::WriteRapierContext`]
46/// to help with getting a reference to the correct RapierContext.
47///
48/// If you're making a library, you might be interested in [`RapierContextEntityLink`]
49/// and leverage a [`Query`] to have precise access to relevant components (for example [`RapierContextSimulation`]).
50///
51/// See the list of full components in [`RapierContext`]
52#[derive(Component, Reflect, Debug, Clone, Copy)]
53pub struct DefaultRapierContext;
54
55/// This is a component applied to any entity containing a rapier handle component.
56/// The inner Entity referred to has the component [`RapierContextSimulation`]
57/// and others from [`crate::plugin::context`], responsible for handling
58/// its rapier data.
59#[derive(Component, Reflect, Debug, Clone, Copy, PartialEq, Eq, Hash)]
60pub struct RapierContextEntityLink(pub Entity);
61
62/// The set of colliders part of the simulation.
63///
64/// This should be attached on an entity with a [`RapierContextSimulation`]
65#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
66#[derive(Component, Default, Debug, Clone)]
67pub struct RapierContextColliders {
68    /// The set of colliders part of the simulation.
69    pub colliders: ColliderSet,
70    #[cfg_attr(feature = "serde-serialize", serde(skip))]
71    pub(crate) entity2collider: HashMap<Entity, ColliderHandle>,
72}
73
74impl RapierContextColliders {
75    /// If the collider attached to `entity` is attached to a rigid-body, this
76    /// returns the `Entity` containing that rigid-body.
77    pub fn collider_parent(
78        &self,
79        rigidbody_set: &RapierRigidBodySet,
80        entity: Entity,
81    ) -> Option<Entity> {
82        self.entity2collider
83            .get(&entity)
84            .and_then(|h| self.colliders.get(*h))
85            .and_then(|co| co.parent())
86            .and_then(|h| rigidbody_set.rigid_body_entity(h))
87    }
88
89    /// If entity is a rigid-body, this returns the collider `Entity`s attached
90    /// to that rigid-body.
91    pub fn rigid_body_colliders<'a, 'b: 'a>(
92        &'a self,
93        entity: Entity,
94        rigidbody_set: &'b RapierRigidBodySet,
95    ) -> impl Iterator<Item = Entity> + 'a {
96        rigidbody_set
97            .entity2body()
98            .get(&entity)
99            .and_then(|handle| rigidbody_set.bodies.get(*handle))
100            .map(|body| {
101                body.colliders()
102                    .iter()
103                    .filter_map(|handle| self.collider_entity(*handle))
104            })
105            .into_iter()
106            .flatten()
107    }
108
109    /// Retrieve the Bevy entity the given Rapier collider (identified by its handle) is attached to.
110    pub fn collider_entity(&self, handle: ColliderHandle) -> Option<Entity> {
111        RapierContextColliders::collider_entity_with_set(&self.colliders, handle)
112    }
113
114    // Mostly used to avoid borrowing self completely.
115    pub(crate) fn collider_entity_with_set(
116        colliders: &ColliderSet,
117        handle: ColliderHandle,
118    ) -> Option<Entity> {
119        colliders.get(handle).map(Self::entity_from_collider)
120    }
121
122    /// Retrieve the Bevy entity the given Rapier collider is attached to.
123    pub fn entity_from_collider(collider: &rapier::prelude::Collider) -> Entity {
124        Entity::from_bits(collider.user_data as u64)
125    }
126
127    /// The map from entities to collider handles.
128    pub fn entity2collider(&self) -> &HashMap<Entity, ColliderHandle> {
129        &self.entity2collider
130    }
131}
132
133/// The sets of joints part of the simulation.
134///
135/// This should be attached on an entity with a [`RapierContextSimulation`]
136#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
137#[derive(Component, Default, Debug, Clone)]
138pub struct RapierContextJoints {
139    /// The set of impulse joints part of the simulation.
140    pub impulse_joints: ImpulseJointSet,
141    /// The set of multibody joints part of the simulation.
142    pub multibody_joints: MultibodyJointSet,
143
144    #[cfg_attr(feature = "serde-serialize", serde(skip))]
145    pub(crate) entity2impulse_joint: HashMap<Entity, ImpulseJointHandle>,
146    #[cfg_attr(feature = "serde-serialize", serde(skip))]
147    pub(crate) entity2multibody_joint: HashMap<Entity, MultibodyJointHandle>,
148}
149
150impl RapierContextJoints {
151    /// The map from entities to impulse joint handles.
152    pub fn entity2impulse_joint(&self) -> &HashMap<Entity, ImpulseJointHandle> {
153        &self.entity2impulse_joint
154    }
155
156    /// The map from entities to multibody joint handles.
157    pub fn entity2multibody_joint(&self) -> &HashMap<Entity, MultibodyJointHandle> {
158        &self.entity2multibody_joint
159    }
160}
161
162/// Wrapper around [QueryPipeline] to provide bevy friendly methods.
163///
164/// This wrapper is designed to be short lived, made whenever necessary.
165///
166/// See [RapierQueryPipeline::new_scoped] to create one.
167#[derive(Copy, Clone)]
168pub struct RapierQueryPipeline<'a> {
169    /// The query pipeline, which performs scene queries (ray-casting, point projection, etc.)
170    pub query_pipeline: QueryPipeline<'a>,
171}
172
173/// Wrapper around [QueryPipelineMut] to provide bevy friendly methods.
174///
175/// This wrapper is designed to be short lived, made whenever necessary.
176///
177/// See [RapierQueryPipelineMut::new_scoped] to create one.
178pub struct RapierQueryPipelineMut<'a> {
179    /// The query pipeline, which performs scene queries (ray-casting, point projection, etc.)
180    pub query_pipeline: QueryPipelineMut<'a>,
181}
182impl<'a> RapierQueryPipelineMut<'a> {
183    /// Creates a temporary [RapierQueryPipelineMut] and passes it as a parameter to `scoped_fn`.
184    pub fn new_scoped<T>(
185        broad_phase: &DefaultBroadPhase,
186        colliders: &mut RapierContextColliders,
187        rigid_bodies: &mut RapierRigidBodySet,
188        filter: &QueryFilter<'_>,
189        dispatcher: &dyn QueryDispatcher,
190        scoped_fn: impl FnOnce(RapierQueryPipelineMut<'_>) -> T,
191    ) -> T {
192        let mut rapier_filter = RapierQueryFilter {
193            flags: filter.flags,
194            groups: filter.groups.map(CollisionGroups::into),
195            exclude_collider: filter
196                .exclude_collider
197                .and_then(|c| colliders.entity2collider.get(&c).copied()),
198            exclude_rigid_body: filter
199                .exclude_rigid_body
200                .and_then(|b| rigid_bodies.entity2body.get(&b).copied()),
201            predicate: None,
202        };
203
204        if let Some(predicate) = filter.predicate {
205            let wrapped_predicate = to_rapier_query_filter_predicate(predicate);
206            rapier_filter.predicate = Some(&wrapped_predicate);
207            let query_pipeline = broad_phase.as_query_pipeline_mut(
208                dispatcher,
209                &mut rigid_bodies.bodies,
210                &mut colliders.colliders,
211                rapier_filter,
212            );
213            scoped_fn(RapierQueryPipelineMut { query_pipeline })
214        } else {
215            let query_pipeline = broad_phase.as_query_pipeline_mut(
216                dispatcher,
217                &mut rigid_bodies.bodies,
218                &mut colliders.colliders,
219                rapier_filter,
220            );
221            scoped_fn(RapierQueryPipelineMut { query_pipeline })
222        }
223    }
224
225    /// Downgrades the mutable reference to an immutable reference.
226    pub fn as_ref(&self) -> RapierQueryPipeline<'_> {
227        RapierQueryPipeline {
228            query_pipeline: self.query_pipeline.as_ref(),
229        }
230    }
231}
232
233/// Wraps a [bevy query filter predicate](QueryFilter::predicate) taking an Entity into a [rapier query filter predicate](RapierQueryFilter::predicate)
234pub fn to_rapier_query_filter_predicate(
235    predicate: &dyn Fn(Entity) -> bool,
236) -> impl Fn(ColliderHandle, &rapier::prelude::Collider) -> bool + use<'_> {
237    |_: ColliderHandle, collider: &'_ rapier::geometry::Collider| -> bool {
238        let entity = crate::prelude::RapierContextColliders::entity_from_collider(collider);
239        (predicate)(entity)
240    }
241}
242
243impl<'a> RapierQueryPipeline<'a> {
244    /// Creates a temporary [RapierQueryPipeline] and passes it as a parameter to `scoped_fn`.
245    pub fn new_scoped<T>(
246        broad_phase: &DefaultBroadPhase,
247        colliders: &RapierContextColliders,
248        rigid_bodies: &RapierRigidBodySet,
249        filter: &QueryFilter<'_>,
250        dispatcher: &dyn QueryDispatcher,
251        scoped_fn: impl FnOnce(RapierQueryPipeline<'_>) -> T,
252    ) -> T {
253        let mut rapier_filter = RapierQueryFilter {
254            flags: filter.flags,
255            groups: filter.groups.map(CollisionGroups::into),
256            exclude_collider: filter
257                .exclude_collider
258                .and_then(|c| colliders.entity2collider.get(&c).copied()),
259            exclude_rigid_body: filter
260                .exclude_rigid_body
261                .and_then(|b| rigid_bodies.entity2body.get(&b).copied()),
262            predicate: None,
263        };
264
265        if let Some(predicate) = filter.predicate {
266            let wrapped_predicate = to_rapier_query_filter_predicate(predicate);
267            rapier_filter.predicate = Some(&wrapped_predicate);
268            let query_pipeline = broad_phase.as_query_pipeline(
269                dispatcher,
270                &rigid_bodies.bodies,
271                &colliders.colliders,
272                rapier_filter,
273            );
274            scoped_fn(RapierQueryPipeline { query_pipeline })
275        } else {
276            let query_pipeline = broad_phase.as_query_pipeline(
277                dispatcher,
278                &rigid_bodies.bodies,
279                &colliders.colliders,
280                rapier_filter,
281            );
282            scoped_fn(RapierQueryPipeline { query_pipeline })
283        }
284    }
285
286    /// Retrieves the Entity for a given collider handle.
287    pub fn collider_entity(&self, collider_handle: ColliderHandle) -> Entity {
288        RapierContextColliders::collider_entity_with_set(
289            self.query_pipeline.colliders,
290            collider_handle,
291        )
292        .unwrap()
293    }
294
295    /// Find the closest intersection between a ray and a set of collider.
296    ///
297    /// # Parameters
298    /// * `ray_origin`: the starting point of the ray to cast.
299    /// * `ray_dir`: the direction of the ray to cast.
300    /// * `max_toi`: the maximum time-of-impact that can be reported by this cast. This effectively
301    ///   limits the length of the ray to `ray.dir.norm() * max_toi`. Use `Real::MAX` for an unbounded ray.
302    /// * `solid`: if this is `true` an impact at time 0.0 (i.e. at the ray origin) is returned if
303    ///   it starts inside of a shape. If this `false` then the ray will hit the shape's boundary
304    ///   even if its starts inside of it.
305    pub fn cast_ray(
306        &self,
307        ray_origin: Vect,
308        ray_dir: Vect,
309        max_toi: Real,
310        solid: bool,
311    ) -> Option<(Entity, Real)> {
312        let ray = Ray::new(ray_origin, ray_dir);
313
314        let (h, toi) = self.query_pipeline.cast_ray(&ray, max_toi, solid)?;
315
316        Some((self.collider_entity(h), toi))
317    }
318
319    /// Find the closest intersection between a ray and a set of collider.
320    ///
321    /// # Parameters
322    /// * `ray_origin`: the starting point of the ray to cast.
323    /// * `ray_dir`: the direction of the ray to cast.
324    /// * `max_toi`: the maximum time-of-impact that can be reported by this cast. This effectively
325    ///   limits the length of the ray to `ray.dir.norm() * max_toi`. Use `Real::MAX` for an unbounded ray.
326    /// * `solid`: if this is `true` an impact at time 0.0 (i.e. at the ray origin) is returned if
327    ///   it starts inside of a shape. If this `false` then the ray will hit the shape's boundary
328    ///   even if its starts inside of it.
329    pub fn cast_ray_and_get_normal(
330        &self,
331        ray_origin: Vect,
332        ray_dir: Vect,
333        max_toi: Real,
334        solid: bool,
335    ) -> Option<(Entity, RayIntersection)> {
336        let ray = Ray::new(ray_origin, ray_dir);
337
338        let (h, result) = self
339            .query_pipeline
340            .cast_ray_and_get_normal(&ray, max_toi, solid)?;
341
342        Some((
343            self.collider_entity(h),
344            RayIntersection::from_rapier(result, ray_origin, ray_dir),
345        ))
346    }
347
348    /// Iterates through all the colliders intersecting a given ray.
349    ///
350    /// # Parameters
351    /// * `ray_origin`: the starting point of the ray to cast.
352    /// * `ray_dir`: the direction of the ray to cast.
353    /// * `max_toi`: the maximum time-of-impact that can be reported by this cast. This effectively
354    ///   limits the length of the ray to `ray.dir.norm() * max_toi`. Use `Real::MAX` for an unbounded ray.
355    /// * `solid`: if this is `true` an impact at time 0.0 (i.e. at the ray origin) is returned if
356    ///   it starts inside of a shape. If this `false` then the ray will hit the shape's boundary
357    ///   even if its starts inside of it.
358    #[allow(clippy::too_many_arguments)]
359    pub fn intersect_ray(
360        &'a self,
361        ray_origin: Vect,
362        ray_dir: Vect,
363        max_toi: Real,
364        solid: bool,
365    ) -> impl Iterator<Item = (Entity, RayIntersection)> + 'a {
366        let ray = Ray::new(ray_origin, ray_dir);
367
368        self.query_pipeline.intersect_ray(ray, max_toi, solid).map(
369            move |(collider_handle, _, intersection)| {
370                (
371                    self.collider_entity(collider_handle),
372                    RayIntersection::from_rapier(intersection, ray_origin, ray_dir),
373                )
374            },
375        )
376    }
377
378    /// Retrieve all the colliders intersecting the given shape.
379    ///
380    /// # Parameters
381    /// * `shape_pos` - The position of the shape used for the intersection test.
382    /// * `shape` - The shape used for the intersection test.
383    pub fn intersect_shape(
384        &'a self,
385        shape_pos: Vect,
386        shape_rot: Rot,
387        shape: &'a dyn Shape,
388    ) -> impl Iterator<Item = Entity> + 'a {
389        let scaled_transform = crate::utils::pose_from(shape_pos, shape_rot);
390
391        self.query_pipeline
392            .intersect_shape(scaled_transform, shape)
393            .map(move |(collider_handle, _)| self.collider_entity(collider_handle))
394    }
395
396    /// Find the projection of a point on the closest collider.
397    ///
398    /// # Parameters
399    /// * `point` - The point to project.
400    /// * `solid` - If this is set to `true` then the collider shapes are considered to
401    ///   be plain (if the point is located inside of a plain shape, its projection is the point
402    ///   itself). If it is set to `false` the collider shapes are considered to be hollow
403    ///   (if the point is located inside of an hollow shape, it is projected on the shape's
404    ///   boundary).
405    pub fn project_point(
406        &self,
407        point: Vect,
408        max_dist: Real,
409        solid: bool,
410    ) -> Option<(Entity, PointProjection)> {
411        let (h, result) = self.query_pipeline.project_point(point, max_dist, solid)?;
412
413        Some((
414            self.collider_entity(h),
415            PointProjection::from_rapier(result),
416        ))
417    }
418
419    /// Find all the colliders containing the given point.
420    ///
421    /// # Parameters
422    /// * `point` - The point used for the containment test.
423    pub fn intersect_point(&'a self, point: Vect) -> impl Iterator<Item = Entity> + 'a {
424        self.query_pipeline
425            .intersect_point(point)
426            .map(move |(collider_handle, _)| self.collider_entity(collider_handle))
427    }
428
429    /// Find the projection of a point on the closest collider.
430    ///
431    /// The results include the ID of the feature hit by the point.
432    ///
433    /// # Parameters
434    /// * `point` - The point to project.
435    /// * `solid` - If this is set to `true` then the collider shapes are considered to
436    ///   be plain (if the point is located inside of a plain shape, its projection is the point
437    ///   itself). If it is set to `false` the collider shapes are considered to be hollow
438    ///   (if the point is located inside of an hollow shape, it is projected on the shape's
439    ///   boundary).
440    pub fn project_point_and_get_feature(
441        &self,
442        point: Vect,
443    ) -> Option<(Entity, PointProjection, FeatureId)> {
444        let (h, proj, fid) = self.query_pipeline.project_point_and_get_feature(point)?;
445
446        Some((
447            self.collider_entity(h),
448            PointProjection::from_rapier(proj),
449            fid,
450        ))
451    }
452
453    /// Finds all handles of all the colliders with an [`Aabb`] intersecting the given [`Aabb`].
454    ///
455    /// Note that the collider AABB taken into account is the one currently stored in the query
456    /// pipeline’s BVH. It doesn’t recompute the latest collider AABB.
457    pub fn intersect_aabb_conservative(
458        &'a self,
459        #[cfg(feature = "dim2")] aabb: bevy::math::bounding::Aabb2d,
460        #[cfg(feature = "dim3")] aabb: bevy::math::bounding::Aabb3d,
461    ) -> impl Iterator<Item = Entity> + 'a {
462        #[cfg(feature = "dim2")]
463        let scaled_aabb = Aabb {
464            mins: aabb.min,
465            maxs: aabb.max,
466        };
467        #[cfg(feature = "dim3")]
468        let scaled_aabb = Aabb {
469            mins: aabb.min.into(),
470            maxs: aabb.max.into(),
471        };
472        self.query_pipeline
473            .intersect_aabb_conservative(scaled_aabb)
474            .map(move |(collider_handle, _)| self.collider_entity(collider_handle))
475    }
476
477    /// Casts a shape at a constant linear velocity and retrieve the first collider it hits.
478    ///
479    /// This is similar to ray-casting except that we are casting a whole shape instead of just a
480    /// point (the ray origin). In the resulting `ShapeCastHit`, witness and normal 1 refer to the world
481    /// collider, and are in world space.
482    ///
483    /// # Parameters
484    /// * `shape_pos` - The initial translation of the shape to cast.
485    /// * `shape_rot` - The rotation of the shape to cast.
486    /// * `shape_vel` - The constant velocity of the shape to cast (i.e. the cast direction).
487    /// * `shape` - The shape to cast.
488    /// * `max_toi` - The maximum time-of-impact that can be reported by this cast. This effectively
489    ///   limits the distance traveled by the shape to `shape_vel.norm() * maxToi`.
490    /// * `stop_at_penetration` - If the casted shape starts in a penetration state with any
491    ///   collider, two results are possible. If `stop_at_penetration` is `true` then, the
492    ///   result will have a `toi` equal to `start_time`. If `stop_at_penetration` is `false`
493    ///   then the nonlinear shape-casting will see if further motion wrt. the penetration normal
494    ///   would result in tunnelling. If it does not (i.e. we have a separating velocity along
495    ///   that normal) then the nonlinear shape-casting will attempt to find another impact,
496    ///   at a time `> start_time` that could result in tunnelling.
497    #[allow(clippy::too_many_arguments)]
498    pub fn cast_shape(
499        &'a self,
500        shape_pos: Vect,
501        shape_rot: Rot,
502        shape_vel: Vect,
503        shape: &dyn Shape,
504        options: ShapeCastOptions,
505    ) -> Option<(Entity, ShapeCastHit)> {
506        let scaled_transform = crate::utils::pose_from(shape_pos, shape_rot);
507
508        let (h, result) =
509            self.query_pipeline
510                .cast_shape(&scaled_transform, shape_vel, shape, options)?;
511
512        Some((
513            self.collider_entity(h),
514            ShapeCastHit::from_rapier(result, options.compute_impact_geometry_on_penetration),
515        ))
516    }
517
518    /* TODO: we need to wrap the NonlinearRigidMotion somehow.
519     *
520    /// Casts a shape with an arbitrary continuous motion and retrieve the first collider it hits.
521    ///
522    /// In the resulting `ShapeCastHit`, witness and normal 1 refer to the world collider, and are
523    /// in world space.
524    ///
525    /// # Parameters
526    /// * `shape_motion` - The motion of the shape.
527    /// * `shape` - The shape to cast.
528    /// * `start_time` - The starting time of the interval where the motion takes place.
529    /// * `end_time` - The end time of the interval where the motion takes place.
530    /// * `stop_at_penetration` - If the casted shape starts in a penetration state with any
531    ///    collider, two results are possible. If `stop_at_penetration` is `true` then, the
532    ///    result will have a `toi` equal to `start_time`. If `stop_at_penetration` is `false`
533    ///    then the nonlinear shape-casting will see if further motion wrt. the penetration normal
534    ///    would result in tunnelling. If it does not (i.e. we have a separating velocity along
535    ///    that normal) then the nonlinear shape-casting will attempt to find another impact,
536    ///    at a time `> start_time` that could result in tunnelling.
537    /// * `filter`: set of rules used to determine which collider is taken into account by this scene query.
538    pub fn nonlinear_cast_shape(
539        &self,
540        shape_motion: &NonlinearRigidMotion,
541        shape: &Collider,
542        start_time: Real,
543        end_time: Real,
544        stop_at_penetration: bool,
545        filter: QueryFilter,
546    ) -> Option<(Entity, Toi)> {
547        let scaled_transform = (shape_pos, shape_rot).into();
548        let mut scaled_shape = shape.clone();
549        // TODO: how to set a good number of subdivisions, we don’t have access to the
550        //       RapierConfiguration::scaled_shape_subdivision here.
551        scaled_shape.set_scale(shape.scale, 20);
552
553        let (h, result) = rigidbody_set.with_query_filter(filter, move |filter| {
554            self.query_pipeline.nonlinear_cast_shape(
555                &rigidbody_set.bodies,
556                &self.colliders,
557                shape_motion,
558                &*scaled_shape.raw,
559                start_time,
560                end_time,
561                stop_at_penetration,
562                filter,
563            )
564        })?;
565
566        self.collider_entity(h).map(|e| (e, result))
567    }
568     */
569}
570
571/// The set of rigid-bodies part of the simulation.
572///
573/// This should be attached on an entity with a [`RapierContextSimulation`]
574#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
575#[derive(Component, Default, Clone)]
576pub struct RapierRigidBodySet {
577    /// The set of rigid-bodies part of the simulation.
578    pub bodies: RigidBodySet,
579    /// NOTE: this map is needed to handle despawning.
580    #[cfg_attr(feature = "serde-serialize", serde(skip))]
581    pub(crate) entity2body: HashMap<Entity, RigidBodyHandle>,
582
583    /// For transform change detection.
584    #[cfg_attr(feature = "serde-serialize", serde(skip))]
585    pub(crate) last_body_transform_set: HashMap<RigidBodyHandle, GlobalTransform>,
586}
587
588impl RapierRigidBodySet {
589    /// The map from entities to rigid-body handles.
590    pub fn entity2body(&self) -> &HashMap<Entity, RigidBodyHandle> {
591        &self.entity2body
592    }
593
594    /// Retrieve the Bevy entity the given Rapier rigid-body (identified by its handle) is attached.
595    pub fn rigid_body_entity(&self, handle: RigidBodyHandle) -> Option<Entity> {
596        self.bodies
597            .get(handle)
598            .map(|c| Entity::from_bits(c.user_data as u64))
599    }
600
601    /// This method makes sure that the rigid-body positions have been propagated to
602    /// their attached colliders, without having to perform a simulation step.
603    pub fn propagate_modified_body_positions_to_colliders(
604        &self,
605        colliders: &mut RapierContextColliders,
606    ) {
607        self.bodies
608            .propagate_modified_body_positions_to_colliders(&mut colliders.colliders);
609    }
610
611    /// Computes the angle between the two bodies attached by the [`RevoluteJoint`] component (if any) referenced by the given `entity`.
612    ///
613    /// The angle is computed along the revolute joint’s principal axis.
614    ///
615    /// Parameter `entity` should have a [`ImpulseJoint`] component with a [`TypedJoint::RevoluteJoint`] variant as `data`.
616    pub fn impulse_revolute_joint_angle(
617        &self,
618        joints: &RapierContextJoints,
619        entity: Entity,
620    ) -> Option<f32> {
621        let joint_handle = joints.entity2impulse_joint().get(&entity)?;
622        let impulse_joint = joints.impulse_joints.get(*joint_handle)?;
623        let revolute_joint = impulse_joint.data.as_revolute()?;
624
625        let rb1 = &self.bodies[impulse_joint.body1];
626        let rb2 = &self.bodies[impulse_joint.body2];
627        Some(revolute_joint.angle(rb1.rotation(), rb2.rotation()))
628    }
629}
630
631/// The Rapier context, containing parts of the state of the physics engine, specific to the simulation step.
632///
633/// This is the main driver for a rapier context, which will create other required components if needed.
634///
635/// Additionally to its required components, this component is also always paired with a [`RapierConfiguration`][crate::prelude::RapierConfiguration] component.
636#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
637#[derive(Component)]
638#[require(
639    RapierContextColliders,
640    RapierRigidBodySet,
641    RapierContextJoints,
642    SimulationToRenderTime
643)]
644pub struct RapierContextSimulation {
645    /// The island manager, which detects what object is sleeping
646    /// (not moving much) to reduce computations.
647    pub islands: IslandManager,
648    /// The broad-phase, which detects potential contact pairs.
649    pub broad_phase: DefaultBroadPhase,
650    /// The narrow-phase, which computes contact points, tests intersections,
651    /// and maintain the contact and intersection graphs.
652    pub narrow_phase: NarrowPhase,
653    /// The solver, which handles Continuous Collision Detection (CCD).
654    pub ccd_solver: CCDSolver,
655    /// The physics pipeline, which advance the simulation step by step.
656    #[cfg_attr(feature = "serde-serialize", serde(skip))]
657    pub pipeline: PhysicsPipeline,
658    /// The integration parameters, controlling various low-level coefficient of the simulation.
659    pub integration_parameters: IntegrationParameters,
660    #[cfg_attr(feature = "serde-serialize", serde(skip))]
661    pub(crate) event_handler: Option<Box<dyn EventHandler>>,
662    // This maps the handles of colliders that have been deleted since the last
663    // physics update, to the entity they was attached to.
664    /// NOTE: this map is needed to handle despawning.
665    #[cfg_attr(feature = "serde-serialize", serde(skip))]
666    pub(crate) deleted_colliders: HashMap<ColliderHandle, Entity>,
667
668    #[cfg_attr(feature = "serde-serialize", serde(skip))]
669    pub(crate) collision_events_to_send: Vec<CollisionEvent>,
670    #[cfg_attr(feature = "serde-serialize", serde(skip))]
671    pub(crate) contact_force_events_to_send: Vec<ContactForceEvent>,
672    #[cfg_attr(feature = "serde-serialize", serde(skip))]
673    pub(crate) character_collisions_collector: Vec<rapier::control::CharacterCollision>,
674}
675
676impl Default for RapierContextSimulation {
677    fn default() -> Self {
678        Self {
679            islands: IslandManager::new(),
680            broad_phase: DefaultBroadPhase::new(),
681            narrow_phase: NarrowPhase::new(),
682            ccd_solver: CCDSolver::new(),
683            pipeline: PhysicsPipeline::new(),
684            integration_parameters: IntegrationParameters::default(),
685            event_handler: None,
686            deleted_colliders: HashMap::default(),
687            collision_events_to_send: Vec::new(),
688            contact_force_events_to_send: Vec::new(),
689            character_collisions_collector: Vec::new(),
690        }
691    }
692}
693
694impl RapierContextSimulation {
695    /// Advance the simulation, based on the given timestep mode.
696    #[allow(clippy::too_many_arguments)]
697    pub fn step_simulation(
698        &mut self,
699        colliders: &mut RapierContextColliders,
700        joints: &mut RapierContextJoints,
701        rigidbody_set: &mut RapierRigidBodySet,
702        gravity: Vect,
703        timestep_mode: TimestepMode,
704        events: Option<(
705            &MessageWriter<CollisionEvent>,
706            &MessageWriter<ContactForceEvent>,
707        )>,
708        hooks: &dyn PhysicsHooks,
709        time: &Time,
710        sim_to_render_time: &mut SimulationToRenderTime,
711        mut interpolation_query: Option<
712            &mut Query<(&RapierRigidBodyHandle, &mut TransformInterpolation)>,
713        >,
714    ) {
715        let event_queue = if events.is_some() {
716            Some(EventQueue {
717                deleted_colliders: &self.deleted_colliders,
718                collision_events: RwLock::new(Vec::new()),
719                contact_force_events: RwLock::new(Vec::new()),
720            })
721        } else {
722            None
723        };
724
725        let event_handler = self
726            .event_handler
727            .as_deref()
728            .or_else(|| event_queue.as_ref().map(|q| q as &dyn EventHandler))
729            .unwrap_or(&() as &dyn EventHandler);
730
731        let mut executed_steps = 0;
732        match timestep_mode {
733            TimestepMode::Interpolated {
734                dt,
735                time_scale,
736                substeps,
737            } => {
738                self.integration_parameters.dt = dt;
739
740                sim_to_render_time.diff += time.delta_secs();
741
742                while sim_to_render_time.diff > 0.0 {
743                    // NOTE: in this comparison we do the same computations we
744                    // will do for the next `while` iteration test, to make sure we
745                    // don't get bit by potential float inaccuracy.
746                    if sim_to_render_time.diff - dt <= 0.0 {
747                        if let Some(interpolation_query) = interpolation_query.as_mut() {
748                            // This is the last simulation step to be executed in the loop
749                            // Update the previous state transforms
750                            for (handle, mut interpolation) in interpolation_query.iter_mut() {
751                                if let Some(body) = rigidbody_set.bodies.get(handle.0) {
752                                    interpolation.start = Some(*body.position());
753                                    interpolation.end = None;
754                                }
755                            }
756                        }
757                    }
758
759                    let mut substep_integration_parameters = self.integration_parameters;
760                    substep_integration_parameters.dt = dt / (substeps as Real) * time_scale;
761
762                    for _ in 0..substeps {
763                        self.pipeline.step(
764                            gravity,
765                            &substep_integration_parameters,
766                            &mut self.islands,
767                            &mut self.broad_phase,
768                            &mut self.narrow_phase,
769                            &mut rigidbody_set.bodies,
770                            &mut colliders.colliders,
771                            &mut joints.impulse_joints,
772                            &mut joints.multibody_joints,
773                            &mut self.ccd_solver,
774                            hooks,
775                            event_handler,
776                        );
777                        executed_steps += 1;
778                    }
779
780                    sim_to_render_time.diff -= dt;
781                }
782            }
783            TimestepMode::Variable {
784                max_dt,
785                time_scale,
786                substeps,
787            } => {
788                self.integration_parameters.dt = (time.delta_secs() * time_scale).min(max_dt);
789
790                let mut substep_integration_parameters = self.integration_parameters;
791                substep_integration_parameters.dt /= substeps as Real;
792
793                for _ in 0..substeps {
794                    self.pipeline.step(
795                        gravity,
796                        &substep_integration_parameters,
797                        &mut self.islands,
798                        &mut self.broad_phase,
799                        &mut self.narrow_phase,
800                        &mut rigidbody_set.bodies,
801                        &mut colliders.colliders,
802                        &mut joints.impulse_joints,
803                        &mut joints.multibody_joints,
804                        &mut self.ccd_solver,
805                        hooks,
806                        event_handler,
807                    );
808                    executed_steps += 1;
809                }
810            }
811            TimestepMode::Fixed { dt, substeps } => {
812                self.integration_parameters.dt = dt;
813
814                let mut substep_integration_parameters = self.integration_parameters;
815                substep_integration_parameters.dt = dt / (substeps as Real);
816
817                for _ in 0..substeps {
818                    self.pipeline.step(
819                        gravity,
820                        &substep_integration_parameters,
821                        &mut self.islands,
822                        &mut self.broad_phase,
823                        &mut self.narrow_phase,
824                        &mut rigidbody_set.bodies,
825                        &mut colliders.colliders,
826                        &mut joints.impulse_joints,
827                        &mut joints.multibody_joints,
828                        &mut self.ccd_solver,
829                        hooks,
830                        event_handler,
831                    );
832                    executed_steps += 1;
833                }
834            }
835        }
836        if let Some(mut event_queue) = event_queue {
837            // NOTE: event_queue and its inner locks are only accessed from
838            // within `self.pipeline.step` called above, so we can unwrap here safely.
839            self.collision_events_to_send =
840                std::mem::take(event_queue.collision_events.get_mut().unwrap());
841            self.contact_force_events_to_send =
842                std::mem::take(event_queue.contact_force_events.get_mut().unwrap());
843        }
844
845        if executed_steps > 0 {
846            self.deleted_colliders.clear();
847        }
848    }
849    /// Generates bevy events for any physics interactions that have happened
850    /// that are stored in the events list
851    pub fn send_bevy_events(
852        &mut self,
853        collision_event_writer: &mut MessageWriter<CollisionEvent>,
854        contact_force_event_writer: &mut MessageWriter<ContactForceEvent>,
855    ) {
856        for collision_event in self.collision_events_to_send.drain(..) {
857            collision_event_writer.write(collision_event);
858        }
859        for contact_force_event in self.contact_force_events_to_send.drain(..) {
860            contact_force_event_writer.write(contact_force_event);
861        }
862    }
863
864    /// Attempts to move shape, optionally sliding or climbing obstacles.
865    ///
866    /// # Parameters
867    /// * `movement`: the translational movement to apply.
868    /// * `shape`: the shape to move.
869    /// * `shape_translation`: the initial position of the shape.
870    /// * `shape_rotation`: the rotation of the shape.
871    /// * `shape_mass`: the mass of the shape to be considered by the impulse calculation if
872    ///   `MoveShapeOptions::apply_impulse_to_dynamic_bodies` is set to true.
873    /// * `options`: configures the behavior of the automatic sliding and climbing.
874    /// * `events`: callback run on each obstacle hit by the shape on its path.
875    #[allow(clippy::too_many_arguments)]
876    pub fn move_shape(
877        &mut self,
878        rapier_colliders: &RapierContextColliders,
879        rapier_query_pipeline: &mut RapierQueryPipelineMut<'_>,
880        movement: Vect,
881        shape: &dyn Shape,
882        shape_translation: Vect,
883        shape_rotation: Rot,
884        shape_mass: Real,
885        options: &MoveShapeOptions,
886        mut events: impl FnMut(CharacterCollision),
887    ) -> MoveShapeOutput {
888        assert!(
889            options.up.length_squared() > 0.0,
890            "The up vector must be non-zero."
891        );
892        let up = options.up.normalize();
893        let autostep = options.autostep.map(|autostep| CharacterAutostep {
894            max_height: autostep.max_height,
895            min_width: autostep.min_width,
896            include_dynamic_bodies: autostep.include_dynamic_bodies,
897        });
898        let controller = rapier::control::KinematicCharacterController {
899            up,
900            offset: options.offset,
901            slide: options.slide,
902            autostep,
903            max_slope_climb_angle: options.max_slope_climb_angle,
904            min_slope_slide_angle: options.min_slope_slide_angle,
905            snap_to_ground: options.snap_to_ground,
906            normal_nudge_factor: options.normal_nudge_factor,
907        };
908
909        self.character_collisions_collector.clear();
910
911        // TODO: having to grab all the references to avoid having self in
912        //       the closure is ugly.
913        let dt = self.integration_parameters.dt;
914        let colliders = &rapier_colliders.colliders;
915        let collisions = &mut self.character_collisions_collector;
916        collisions.clear();
917
918        let result = controller.move_shape(
919            dt,
920            &rapier_query_pipeline.query_pipeline.as_ref(),
921            shape,
922            &crate::utils::pose_from(shape_translation, shape_rotation),
923            movement,
924            |c| {
925                if let Some(collision) = CharacterCollision::from_raw_with_set(colliders, &c, true)
926                {
927                    events(collision);
928                }
929                collisions.push(c);
930            },
931        );
932
933        if options.apply_impulse_to_dynamic_bodies {
934            controller.solve_character_collision_impulses(
935                dt,
936                &mut rapier_query_pipeline.query_pipeline,
937                shape,
938                shape_mass,
939                collisions.iter(),
940            )
941        };
942
943        MoveShapeOutput {
944            effective_translation: result.translation,
945            grounded: result.grounded,
946            is_sliding_down_slope: result.is_sliding_down_slope,
947        }
948    }
949}