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, 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 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 #[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 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 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 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 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 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 #[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 }
570
571#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
575#[derive(Component, Default, Clone)]
576pub struct RapierRigidBodySet {
577 pub bodies: RigidBodySet,
579 #[cfg_attr(feature = "serde-serialize", serde(skip))]
581 pub(crate) entity2body: HashMap<Entity, RigidBodyHandle>,
582
583 #[cfg_attr(feature = "serde-serialize", serde(skip))]
585 pub(crate) last_body_transform_set: HashMap<RigidBodyHandle, GlobalTransform>,
586}
587
588impl RapierRigidBodySet {
589 pub fn entity2body(&self) -> &HashMap<Entity, RigidBodyHandle> {
591 &self.entity2body
592 }
593
594 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 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 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#[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 pub islands: IslandManager,
648 pub broad_phase: DefaultBroadPhase,
650 pub narrow_phase: NarrowPhase,
653 pub ccd_solver: CCDSolver,
655 #[cfg_attr(feature = "serde-serialize", serde(skip))]
657 pub pipeline: PhysicsPipeline,
658 pub integration_parameters: IntegrationParameters,
660 #[cfg_attr(feature = "serde-serialize", serde(skip))]
661 pub(crate) event_handler: Option<Box<dyn EventHandler>>,
662 #[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 #[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 if sim_to_render_time.diff - dt <= 0.0 {
747 if let Some(interpolation_query) = interpolation_query.as_mut() {
748 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 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 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 #[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 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}