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.into(), ray_dir.into());
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.into(), ray_dir.into());
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.into(), ray_dir.into());
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 = (shape_pos, shape_rot).into();
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
412            .query_pipeline
413            .project_point(&point.into(), max_dist, solid)?;
414
415        Some((
416            self.collider_entity(h),
417            PointProjection::from_rapier(result),
418        ))
419    }
420
421    /// Find all the colliders containing the given point.
422    ///
423    /// # Parameters
424    /// * `point` - The point used for the containment test.
425    pub fn intersect_point(&'a self, point: Vect) -> impl Iterator<Item = Entity> + 'a {
426        self.query_pipeline
427            .intersect_point(point.into())
428            .map(move |(collider_handle, _)| self.collider_entity(collider_handle))
429    }
430
431    /// Find the projection of a point on the closest collider.
432    ///
433    /// The results include the ID of the feature hit by the point.
434    ///
435    /// # Parameters
436    /// * `point` - The point to project.
437    /// * `solid` - If this is set to `true` then the collider shapes are considered to
438    ///   be plain (if the point is located inside of a plain shape, its projection is the point
439    ///   itself). If it is set to `false` the collider shapes are considered to be hollow
440    ///   (if the point is located inside of an hollow shape, it is projected on the shape's
441    ///   boundary).
442    pub fn project_point_and_get_feature(
443        &self,
444        point: Vect,
445    ) -> Option<(Entity, PointProjection, FeatureId)> {
446        let (h, proj, fid) = self
447            .query_pipeline
448            .project_point_and_get_feature(&point.into())?;
449
450        Some((
451            self.collider_entity(h),
452            PointProjection::from_rapier(proj),
453            fid,
454        ))
455    }
456
457    /// Finds all handles of all the colliders with an [`Aabb`] intersecting the given [`Aabb`].
458    ///
459    /// Note that the collider AABB taken into account is the one currently stored in the query
460    /// pipeline’s BVH. It doesn’t recompute the latest collider AABB.
461    pub fn intersect_aabb_conservative(
462        &'a self,
463        #[cfg(feature = "dim2")] aabb: bevy::math::bounding::Aabb2d,
464        #[cfg(feature = "dim3")] aabb: bevy::math::bounding::Aabb3d,
465    ) -> impl Iterator<Item = Entity> + 'a {
466        let scaled_aabb = Aabb {
467            mins: aabb.min.into(),
468            maxs: aabb.max.into(),
469        };
470        self.query_pipeline
471            .intersect_aabb_conservative(scaled_aabb)
472            .map(move |(collider_handle, _)| self.collider_entity(collider_handle))
473    }
474
475    /// Casts a shape at a constant linear velocity and retrieve the first collider it hits.
476    ///
477    /// This is similar to ray-casting except that we are casting a whole shape instead of just a
478    /// point (the ray origin). In the resulting `ShapeCastHit`, witness and normal 1 refer to the world
479    /// collider, and are in world space.
480    ///
481    /// # Parameters
482    /// * `shape_pos` - The initial translation of the shape to cast.
483    /// * `shape_rot` - The rotation of the shape to cast.
484    /// * `shape_vel` - The constant velocity of the shape to cast (i.e. the cast direction).
485    /// * `shape` - The shape to cast.
486    /// * `max_toi` - The maximum time-of-impact that can be reported by this cast. This effectively
487    ///   limits the distance traveled by the shape to `shape_vel.norm() * maxToi`.
488    /// * `stop_at_penetration` - If the casted shape starts in a penetration state with any
489    ///   collider, two results are possible. If `stop_at_penetration` is `true` then, the
490    ///   result will have a `toi` equal to `start_time`. If `stop_at_penetration` is `false`
491    ///   then the nonlinear shape-casting will see if further motion wrt. the penetration normal
492    ///   would result in tunnelling. If it does not (i.e. we have a separating velocity along
493    ///   that normal) then the nonlinear shape-casting will attempt to find another impact,
494    ///   at a time `> start_time` that could result in tunnelling.
495    #[allow(clippy::too_many_arguments)]
496    pub fn cast_shape(
497        &'a self,
498        shape_pos: Vect,
499        shape_rot: Rot,
500        shape_vel: Vect,
501        shape: &dyn Shape,
502        options: ShapeCastOptions,
503    ) -> Option<(Entity, ShapeCastHit)> {
504        let scaled_transform = (shape_pos, shape_rot).into();
505
506        let (h, result) =
507            self.query_pipeline
508                .cast_shape(&scaled_transform, &shape_vel.into(), shape, options)?;
509
510        Some((
511            self.collider_entity(h),
512            ShapeCastHit::from_rapier(result, options.compute_impact_geometry_on_penetration),
513        ))
514    }
515
516    /* TODO: we need to wrap the NonlinearRigidMotion somehow.
517     *
518    /// Casts a shape with an arbitrary continuous motion and retrieve the first collider it hits.
519    ///
520    /// In the resulting `ShapeCastHit`, witness and normal 1 refer to the world collider, and are
521    /// in world space.
522    ///
523    /// # Parameters
524    /// * `shape_motion` - The motion of the shape.
525    /// * `shape` - The shape to cast.
526    /// * `start_time` - The starting time of the interval where the motion takes place.
527    /// * `end_time` - The end time of the interval where the motion takes place.
528    /// * `stop_at_penetration` - If the casted shape starts in a penetration state with any
529    ///    collider, two results are possible. If `stop_at_penetration` is `true` then, the
530    ///    result will have a `toi` equal to `start_time`. If `stop_at_penetration` is `false`
531    ///    then the nonlinear shape-casting will see if further motion wrt. the penetration normal
532    ///    would result in tunnelling. If it does not (i.e. we have a separating velocity along
533    ///    that normal) then the nonlinear shape-casting will attempt to find another impact,
534    ///    at a time `> start_time` that could result in tunnelling.
535    /// * `filter`: set of rules used to determine which collider is taken into account by this scene query.
536    pub fn nonlinear_cast_shape(
537        &self,
538        shape_motion: &NonlinearRigidMotion,
539        shape: &Collider,
540        start_time: Real,
541        end_time: Real,
542        stop_at_penetration: bool,
543        filter: QueryFilter,
544    ) -> Option<(Entity, Toi)> {
545        let scaled_transform = (shape_pos, shape_rot).into();
546        let mut scaled_shape = shape.clone();
547        // TODO: how to set a good number of subdivisions, we don’t have access to the
548        //       RapierConfiguration::scaled_shape_subdivision here.
549        scaled_shape.set_scale(shape.scale, 20);
550
551        let (h, result) = rigidbody_set.with_query_filter(filter, move |filter| {
552            self.query_pipeline.nonlinear_cast_shape(
553                &rigidbody_set.bodies,
554                &self.colliders,
555                shape_motion,
556                &*scaled_shape.raw,
557                start_time,
558                end_time,
559                stop_at_penetration,
560                filter,
561            )
562        })?;
563
564        self.collider_entity(h).map(|e| (e, result))
565    }
566     */
567}
568
569/// The set of rigid-bodies part of the simulation.
570///
571/// This should be attached on an entity with a [`RapierContextSimulation`]
572#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
573#[derive(Component, Default, Clone)]
574pub struct RapierRigidBodySet {
575    /// The set of rigid-bodies part of the simulation.
576    pub bodies: RigidBodySet,
577    /// NOTE: this map is needed to handle despawning.
578    #[cfg_attr(feature = "serde-serialize", serde(skip))]
579    pub(crate) entity2body: HashMap<Entity, RigidBodyHandle>,
580
581    /// For transform change detection.
582    #[cfg_attr(feature = "serde-serialize", serde(skip))]
583    pub(crate) last_body_transform_set: HashMap<RigidBodyHandle, GlobalTransform>,
584}
585
586impl RapierRigidBodySet {
587    /// The map from entities to rigid-body handles.
588    pub fn entity2body(&self) -> &HashMap<Entity, RigidBodyHandle> {
589        &self.entity2body
590    }
591
592    /// Retrieve the Bevy entity the given Rapier rigid-body (identified by its handle) is attached.
593    pub fn rigid_body_entity(&self, handle: RigidBodyHandle) -> Option<Entity> {
594        self.bodies
595            .get(handle)
596            .map(|c| Entity::from_bits(c.user_data as u64))
597    }
598
599    /// This method makes sure that the rigid-body positions have been propagated to
600    /// their attached colliders, without having to perform a simulation step.
601    pub fn propagate_modified_body_positions_to_colliders(
602        &self,
603        colliders: &mut RapierContextColliders,
604    ) {
605        self.bodies
606            .propagate_modified_body_positions_to_colliders(&mut colliders.colliders);
607    }
608
609    /// Computes the angle between the two bodies attached by the [`RevoluteJoint`] component (if any) referenced by the given `entity`.
610    ///
611    /// The angle is computed along the revolute joint’s principal axis.
612    ///
613    /// Parameter `entity` should have a [`ImpulseJoint`] component with a [`TypedJoint::RevoluteJoint`] variant as `data`.
614    pub fn impulse_revolute_joint_angle(
615        &self,
616        joints: &RapierContextJoints,
617        entity: Entity,
618    ) -> Option<f32> {
619        let joint_handle = joints.entity2impulse_joint().get(&entity)?;
620        let impulse_joint = joints.impulse_joints.get(*joint_handle)?;
621        let revolute_joint = impulse_joint.data.as_revolute()?;
622
623        let rb1 = &self.bodies[impulse_joint.body1];
624        let rb2 = &self.bodies[impulse_joint.body2];
625        Some(revolute_joint.angle(rb1.rotation(), rb2.rotation()))
626    }
627}
628
629/// The Rapier context, containing parts of the state of the physics engine, specific to the simulation step.
630///
631/// This is the main driver for a rapier context, which will create other required components if needed.
632///
633/// Additionally to its required components, this component is also always paired with a [`RapierConfiguration`][crate::prelude::RapierConfiguration] component.
634#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
635#[derive(Component)]
636#[require(
637    RapierContextColliders,
638    RapierRigidBodySet,
639    RapierContextJoints,
640    SimulationToRenderTime
641)]
642pub struct RapierContextSimulation {
643    /// The island manager, which detects what object is sleeping
644    /// (not moving much) to reduce computations.
645    pub islands: IslandManager,
646    /// The broad-phase, which detects potential contact pairs.
647    pub broad_phase: DefaultBroadPhase,
648    /// The narrow-phase, which computes contact points, tests intersections,
649    /// and maintain the contact and intersection graphs.
650    pub narrow_phase: NarrowPhase,
651    /// The solver, which handles Continuous Collision Detection (CCD).
652    pub ccd_solver: CCDSolver,
653    /// The physics pipeline, which advance the simulation step by step.
654    #[cfg_attr(feature = "serde-serialize", serde(skip))]
655    pub pipeline: PhysicsPipeline,
656    /// The integration parameters, controlling various low-level coefficient of the simulation.
657    pub integration_parameters: IntegrationParameters,
658    #[cfg_attr(feature = "serde-serialize", serde(skip))]
659    pub(crate) event_handler: Option<Box<dyn EventHandler>>,
660    // This maps the handles of colliders that have been deleted since the last
661    // physics update, to the entity they was attached to.
662    /// NOTE: this map is needed to handle despawning.
663    #[cfg_attr(feature = "serde-serialize", serde(skip))]
664    pub(crate) deleted_colliders: HashMap<ColliderHandle, Entity>,
665
666    #[cfg_attr(feature = "serde-serialize", serde(skip))]
667    pub(crate) collision_events_to_send: Vec<CollisionEvent>,
668    #[cfg_attr(feature = "serde-serialize", serde(skip))]
669    pub(crate) contact_force_events_to_send: Vec<ContactForceEvent>,
670    #[cfg_attr(feature = "serde-serialize", serde(skip))]
671    pub(crate) character_collisions_collector: Vec<rapier::control::CharacterCollision>,
672}
673
674impl Default for RapierContextSimulation {
675    fn default() -> Self {
676        Self {
677            islands: IslandManager::new(),
678            broad_phase: DefaultBroadPhase::new(),
679            narrow_phase: NarrowPhase::new(),
680            ccd_solver: CCDSolver::new(),
681            pipeline: PhysicsPipeline::new(),
682            integration_parameters: IntegrationParameters::default(),
683            event_handler: None,
684            deleted_colliders: HashMap::default(),
685            collision_events_to_send: Vec::new(),
686            contact_force_events_to_send: Vec::new(),
687            character_collisions_collector: Vec::new(),
688        }
689    }
690}
691
692impl RapierContextSimulation {
693    /// Advance the simulation, based on the given timestep mode.
694    #[allow(clippy::too_many_arguments)]
695    pub fn step_simulation(
696        &mut self,
697        colliders: &mut RapierContextColliders,
698        joints: &mut RapierContextJoints,
699        rigidbody_set: &mut RapierRigidBodySet,
700        gravity: Vect,
701        timestep_mode: TimestepMode,
702        events: Option<(
703            &MessageWriter<CollisionEvent>,
704            &MessageWriter<ContactForceEvent>,
705        )>,
706        hooks: &dyn PhysicsHooks,
707        time: &Time,
708        sim_to_render_time: &mut SimulationToRenderTime,
709        mut interpolation_query: Option<
710            &mut Query<(&RapierRigidBodyHandle, &mut TransformInterpolation)>,
711        >,
712    ) {
713        let event_queue = if events.is_some() {
714            Some(EventQueue {
715                deleted_colliders: &self.deleted_colliders,
716                collision_events: RwLock::new(Vec::new()),
717                contact_force_events: RwLock::new(Vec::new()),
718            })
719        } else {
720            None
721        };
722
723        let event_handler = self
724            .event_handler
725            .as_deref()
726            .or_else(|| event_queue.as_ref().map(|q| q as &dyn EventHandler))
727            .unwrap_or(&() as &dyn EventHandler);
728
729        let mut executed_steps = 0;
730        match timestep_mode {
731            TimestepMode::Interpolated {
732                dt,
733                time_scale,
734                substeps,
735            } => {
736                self.integration_parameters.dt = dt;
737
738                sim_to_render_time.diff += time.delta_secs();
739
740                while sim_to_render_time.diff > 0.0 {
741                    // NOTE: in this comparison we do the same computations we
742                    // will do for the next `while` iteration test, to make sure we
743                    // don't get bit by potential float inaccuracy.
744                    if sim_to_render_time.diff - dt <= 0.0 {
745                        if let Some(interpolation_query) = interpolation_query.as_mut() {
746                            // This is the last simulation step to be executed in the loop
747                            // Update the previous state transforms
748                            for (handle, mut interpolation) in interpolation_query.iter_mut() {
749                                if let Some(body) = rigidbody_set.bodies.get(handle.0) {
750                                    interpolation.start = Some(*body.position());
751                                    interpolation.end = None;
752                                }
753                            }
754                        }
755                    }
756
757                    let mut substep_integration_parameters = self.integration_parameters;
758                    substep_integration_parameters.dt = dt / (substeps as Real) * time_scale;
759
760                    for _ in 0..substeps {
761                        self.pipeline.step(
762                            &gravity.into(),
763                            &substep_integration_parameters,
764                            &mut self.islands,
765                            &mut self.broad_phase,
766                            &mut self.narrow_phase,
767                            &mut rigidbody_set.bodies,
768                            &mut colliders.colliders,
769                            &mut joints.impulse_joints,
770                            &mut joints.multibody_joints,
771                            &mut self.ccd_solver,
772                            hooks,
773                            event_handler,
774                        );
775                        executed_steps += 1;
776                    }
777
778                    sim_to_render_time.diff -= dt;
779                }
780            }
781            TimestepMode::Variable {
782                max_dt,
783                time_scale,
784                substeps,
785            } => {
786                self.integration_parameters.dt = (time.delta_secs() * time_scale).min(max_dt);
787
788                let mut substep_integration_parameters = self.integration_parameters;
789                substep_integration_parameters.dt /= substeps as Real;
790
791                for _ in 0..substeps {
792                    self.pipeline.step(
793                        &gravity.into(),
794                        &substep_integration_parameters,
795                        &mut self.islands,
796                        &mut self.broad_phase,
797                        &mut self.narrow_phase,
798                        &mut rigidbody_set.bodies,
799                        &mut colliders.colliders,
800                        &mut joints.impulse_joints,
801                        &mut joints.multibody_joints,
802                        &mut self.ccd_solver,
803                        hooks,
804                        event_handler,
805                    );
806                    executed_steps += 1;
807                }
808            }
809            TimestepMode::Fixed { dt, substeps } => {
810                self.integration_parameters.dt = dt;
811
812                let mut substep_integration_parameters = self.integration_parameters;
813                substep_integration_parameters.dt = dt / (substeps as Real);
814
815                for _ in 0..substeps {
816                    self.pipeline.step(
817                        &gravity.into(),
818                        &substep_integration_parameters,
819                        &mut self.islands,
820                        &mut self.broad_phase,
821                        &mut self.narrow_phase,
822                        &mut rigidbody_set.bodies,
823                        &mut colliders.colliders,
824                        &mut joints.impulse_joints,
825                        &mut joints.multibody_joints,
826                        &mut self.ccd_solver,
827                        hooks,
828                        event_handler,
829                    );
830                    executed_steps += 1;
831                }
832            }
833        }
834        if let Some(mut event_queue) = event_queue {
835            // NOTE: event_queue and its inner locks are only accessed from
836            // within `self.pipeline.step` called above, so we can unwrap here safely.
837            self.collision_events_to_send =
838                std::mem::take(event_queue.collision_events.get_mut().unwrap());
839            self.contact_force_events_to_send =
840                std::mem::take(event_queue.contact_force_events.get_mut().unwrap());
841        }
842
843        if executed_steps > 0 {
844            self.deleted_colliders.clear();
845        }
846    }
847    /// Generates bevy events for any physics interactions that have happened
848    /// that are stored in the events list
849    pub fn send_bevy_events(
850        &mut self,
851        collision_event_writer: &mut MessageWriter<CollisionEvent>,
852        contact_force_event_writer: &mut MessageWriter<ContactForceEvent>,
853    ) {
854        for collision_event in self.collision_events_to_send.drain(..) {
855            collision_event_writer.write(collision_event);
856        }
857        for contact_force_event in self.contact_force_events_to_send.drain(..) {
858            contact_force_event_writer.write(contact_force_event);
859        }
860    }
861
862    /// Attempts to move shape, optionally sliding or climbing obstacles.
863    ///
864    /// # Parameters
865    /// * `movement`: the translational movement to apply.
866    /// * `shape`: the shape to move.
867    /// * `shape_translation`: the initial position of the shape.
868    /// * `shape_rotation`: the rotation of the shape.
869    /// * `shape_mass`: the mass of the shape to be considered by the impulse calculation if
870    ///   `MoveShapeOptions::apply_impulse_to_dynamic_bodies` is set to true.
871    /// * `options`: configures the behavior of the automatic sliding and climbing.
872    /// * `events`: callback run on each obstacle hit by the shape on its path.
873    #[allow(clippy::too_many_arguments)]
874    pub fn move_shape(
875        &mut self,
876        rapier_colliders: &RapierContextColliders,
877        rapier_query_pipeline: &mut RapierQueryPipelineMut<'_>,
878        movement: Vect,
879        shape: &dyn Shape,
880        shape_translation: Vect,
881        shape_rotation: Rot,
882        shape_mass: Real,
883        options: &MoveShapeOptions,
884        mut events: impl FnMut(CharacterCollision),
885    ) -> MoveShapeOutput {
886        let up = options
887            .up
888            .try_into()
889            .expect("The up vector must be non-zero.");
890        let autostep = options.autostep.map(|autostep| CharacterAutostep {
891            max_height: autostep.max_height,
892            min_width: autostep.min_width,
893            include_dynamic_bodies: autostep.include_dynamic_bodies,
894        });
895        let controller = rapier::control::KinematicCharacterController {
896            up,
897            offset: options.offset,
898            slide: options.slide,
899            autostep,
900            max_slope_climb_angle: options.max_slope_climb_angle,
901            min_slope_slide_angle: options.min_slope_slide_angle,
902            snap_to_ground: options.snap_to_ground,
903            normal_nudge_factor: options.normal_nudge_factor,
904        };
905
906        self.character_collisions_collector.clear();
907
908        // TODO: having to grab all the references to avoid having self in
909        //       the closure is ugly.
910        let dt = self.integration_parameters.dt;
911        let colliders = &rapier_colliders.colliders;
912        let collisions = &mut self.character_collisions_collector;
913        collisions.clear();
914
915        let result = controller.move_shape(
916            dt,
917            &rapier_query_pipeline.query_pipeline.as_ref(),
918            shape,
919            &(shape_translation, shape_rotation).into(),
920            movement.into(),
921            |c| {
922                if let Some(collision) = CharacterCollision::from_raw_with_set(colliders, &c, true)
923                {
924                    events(collision);
925                }
926                collisions.push(c);
927            },
928        );
929
930        if options.apply_impulse_to_dynamic_bodies {
931            controller.solve_character_collision_impulses(
932                dt,
933                &mut rapier_query_pipeline.query_pipeline,
934                shape,
935                shape_mass,
936                collisions.iter(),
937            )
938        };
939
940        MoveShapeOutput {
941            effective_translation: result.translation.into(),
942            grounded: result.grounded,
943            is_sliding_down_slope: result.is_sliding_down_slope,
944        }
945    }
946}