1pub 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#[derive(Component, Default, Reflect, Clone)]
38pub struct SimulationToRenderTime {
39 pub diff: f32,
41}
42
43#[derive(Component, Reflect, Debug, Clone, Copy)]
53pub struct DefaultRapierContext;
54
55#[derive(Component, Reflect, Debug, Clone, Copy, PartialEq, Eq, Hash)]
60pub struct RapierContextEntityLink(pub Entity);
61
62#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
66#[derive(Component, Default, Debug, Clone)]
67pub struct RapierContextColliders {
68 pub colliders: ColliderSet,
70 #[cfg_attr(feature = "serde-serialize", serde(skip))]
71 pub(crate) entity2collider: HashMap<Entity, ColliderHandle>,
72}
73
74impl RapierContextColliders {
75 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 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 pub fn collider_entity(&self, handle: ColliderHandle) -> Option<Entity> {
111 RapierContextColliders::collider_entity_with_set(&self.colliders, handle)
112 }
113
114 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 pub fn entity_from_collider(collider: &rapier::prelude::Collider) -> Entity {
124 Entity::from_bits(collider.user_data as u64)
125 }
126
127 pub fn entity2collider(&self) -> &HashMap<Entity, ColliderHandle> {
129 &self.entity2collider
130 }
131}
132
133#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
137#[derive(Component, Default, Debug, Clone)]
138pub struct RapierContextJoints {
139 pub impulse_joints: ImpulseJointSet,
141 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 pub fn entity2impulse_joint(&self) -> &HashMap<Entity, ImpulseJointHandle> {
153 &self.entity2impulse_joint
154 }
155
156 pub fn entity2multibody_joint(&self) -> &HashMap<Entity, MultibodyJointHandle> {
158 &self.entity2multibody_joint
159 }
160}
161
162#[derive(Copy, Clone)]
168pub struct RapierQueryPipeline<'a> {
169 pub query_pipeline: QueryPipeline<'a>,
171}
172
173pub struct RapierQueryPipelineMut<'a> {
179 pub query_pipeline: QueryPipelineMut<'a>,
181}
182impl<'a> RapierQueryPipelineMut<'a> {
183 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 pub fn as_ref(&self) -> RapierQueryPipeline<'_> {
227 RapierQueryPipeline {
228 query_pipeline: self.query_pipeline.as_ref(),
229 }
230 }
231}
232
233pub 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 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 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 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 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 #[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 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 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 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 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 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 #[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 }
568
569#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
573#[derive(Component, Default, Clone)]
574pub struct RapierRigidBodySet {
575 pub bodies: RigidBodySet,
577 #[cfg_attr(feature = "serde-serialize", serde(skip))]
579 pub(crate) entity2body: HashMap<Entity, RigidBodyHandle>,
580
581 #[cfg_attr(feature = "serde-serialize", serde(skip))]
583 pub(crate) last_body_transform_set: HashMap<RigidBodyHandle, GlobalTransform>,
584}
585
586impl RapierRigidBodySet {
587 pub fn entity2body(&self) -> &HashMap<Entity, RigidBodyHandle> {
589 &self.entity2body
590 }
591
592 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 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 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#[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 pub islands: IslandManager,
646 pub broad_phase: DefaultBroadPhase,
648 pub narrow_phase: NarrowPhase,
651 pub ccd_solver: CCDSolver,
653 #[cfg_attr(feature = "serde-serialize", serde(skip))]
655 pub pipeline: PhysicsPipeline,
656 pub integration_parameters: IntegrationParameters,
658 #[cfg_attr(feature = "serde-serialize", serde(skip))]
659 pub(crate) event_handler: Option<Box<dyn EventHandler>>,
660 #[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 #[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 if sim_to_render_time.diff - dt <= 0.0 {
745 if let Some(interpolation_query) = interpolation_query.as_mut() {
746 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 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 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 #[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 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}