bevy_rapier3d 0.20.0

3-dimensional physics engine in Rust, official Bevy plugin.
Documentation
use bevy::prelude::*;
use std::collections::HashMap;
use std::sync::RwLock;

use rapier::prelude::{
    Aabb as RapierAabb, BroadPhase, CCDSolver, ColliderHandle, ColliderSet, EventHandler,
    FeatureId, ImpulseJointHandle, ImpulseJointSet, IntegrationParameters, IslandManager,
    MultibodyJointHandle, MultibodyJointSet, NarrowPhase, PhysicsHooks, PhysicsPipeline,
    QueryFilter as RapierQueryFilter, QueryPipeline, Ray, Real, RigidBodyHandle, RigidBodySet,
};

use crate::geometry::{Collider, PointProjection, RayIntersection, Toi};
use crate::math::{Rot, Vect};
use crate::pipeline::{CollisionEvent, ContactForceEvent, EventQueue, QueryFilter};
use bevy::prelude::{Entity, EventWriter, GlobalTransform, Query};
use bevy::render::primitives::Aabb;

use crate::control::{CharacterCollision, MoveShapeOptions, MoveShapeOutput};
use crate::dynamics::TransformInterpolation;
use crate::plugin::configuration::{SimulationToRenderTime, TimestepMode};
use crate::prelude::{CollisionGroups, RapierRigidBodyHandle};
#[cfg(feature = "dim2")]
use bevy::math::Vec3Swizzles;
use rapier::control::CharacterAutostep;

/// The Rapier context, containing all the state of the physics engine.
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[derive(Resource)]
pub struct RapierContext {
    /// The island manager, which detects what object is sleeping
    /// (not moving much) to reduce computations.
    pub islands: IslandManager,
    /// The broad-phase, which detects potential contact pairs.
    pub broad_phase: BroadPhase,
    /// The narrow-phase, which computes contact points, tests intersections,
    /// and maintain the contact and intersection graphs.
    pub narrow_phase: NarrowPhase,
    /// The set of rigid-bodies part of the simulation.
    pub bodies: RigidBodySet,
    /// The set of colliders part of the simulation.
    pub colliders: ColliderSet,
    /// The set of impulse joints part of the simulation.
    pub impulse_joints: ImpulseJointSet,
    /// The set of multibody joints part of the simulation.
    pub multibody_joints: MultibodyJointSet,
    /// The solver, which handles Continuous Collision Detection (CCD).
    pub ccd_solver: CCDSolver,
    /// The physics pipeline, which advance the simulation step by step.
    #[cfg_attr(feature = "serde-serialize", serde(skip))]
    pub pipeline: PhysicsPipeline,
    /// The query pipeline, which performs scene queries (ray-casting, point projection, etc.)
    pub query_pipeline: QueryPipeline,
    /// The integration parameters, controlling various low-level coefficient of the simulation.
    pub integration_parameters: IntegrationParameters,
    pub(crate) physics_scale: Real,
    #[cfg_attr(feature = "serde-serialize", serde(skip))]
    pub(crate) event_handler: Option<Box<dyn EventHandler>>,
    // For transform change detection.
    #[cfg_attr(feature = "serde-serialize", serde(skip))]
    pub(crate) last_body_transform_set: HashMap<RigidBodyHandle, GlobalTransform>,
    // NOTE: these maps are needed to handle despawning.
    #[cfg_attr(feature = "serde-serialize", serde(skip))]
    pub(crate) entity2body: HashMap<Entity, RigidBodyHandle>,
    #[cfg_attr(feature = "serde-serialize", serde(skip))]
    pub(crate) entity2collider: HashMap<Entity, ColliderHandle>,
    #[cfg_attr(feature = "serde-serialize", serde(skip))]
    pub(crate) entity2impulse_joint: HashMap<Entity, ImpulseJointHandle>,
    #[cfg_attr(feature = "serde-serialize", serde(skip))]
    pub(crate) entity2multibody_joint: HashMap<Entity, MultibodyJointHandle>,
    // This maps the handles of colliders that have been deleted since the last
    // physics update, to the entity they was attached to.
    #[cfg_attr(feature = "serde-serialize", serde(skip))]
    pub(crate) deleted_colliders: HashMap<ColliderHandle, Entity>,
    #[cfg_attr(feature = "serde-serialize", serde(skip))]
    pub(crate) character_collisions_collector: Vec<rapier::control::CharacterCollision>,
}

impl Default for RapierContext {
    fn default() -> Self {
        Self {
            islands: IslandManager::new(),
            broad_phase: BroadPhase::new(),
            narrow_phase: NarrowPhase::new(),
            bodies: RigidBodySet::new(),
            colliders: ColliderSet::new(),
            impulse_joints: ImpulseJointSet::new(),
            multibody_joints: MultibodyJointSet::new(),
            ccd_solver: CCDSolver::new(),
            pipeline: PhysicsPipeline::new(),
            query_pipeline: QueryPipeline::new(),
            integration_parameters: IntegrationParameters::default(),
            physics_scale: 1.0,
            event_handler: None,
            last_body_transform_set: HashMap::new(),
            entity2body: HashMap::new(),
            entity2collider: HashMap::new(),
            entity2impulse_joint: HashMap::new(),
            entity2multibody_joint: HashMap::new(),
            deleted_colliders: HashMap::new(),
            character_collisions_collector: vec![],
        }
    }
}

impl RapierContext {
    /// Get the physics scale that was set for this Rapier context.
    ///
    /// See [`RapierPhysicsPlugin::with_physics_scale()`][crate::plugin::RapierPhysicsPlugin::with_physics_scale()].
    pub fn physics_scale(&self) -> Real {
        self.physics_scale
    }

    /// If the collider attached to `entity` is attached to a rigid-body, this
    /// returns the `Entity` containing that rigid-body.
    pub fn collider_parent(&self, entity: Entity) -> Option<Entity> {
        self.entity2collider
            .get(&entity)
            .and_then(|h| self.colliders.get(*h))
            .and_then(|co| co.parent())
            .and_then(|h| self.rigid_body_entity(h))
    }

    /// Retrieve the Bevy entity the given Rapier collider (identified by its handle) is attached.
    pub fn collider_entity(&self, handle: ColliderHandle) -> Option<Entity> {
        Self::collider_entity_with_set(&self.colliders, handle)
    }

    // Mostly used to avoid borrowing self completely.
    pub(crate) fn collider_entity_with_set(
        colliders: &ColliderSet,
        handle: ColliderHandle,
    ) -> Option<Entity> {
        colliders
            .get(handle)
            .map(|c| Entity::from_bits(c.user_data as u64))
    }

    /// Retrieve the Bevy entity the given Rapier rigid-body (identified by its handle) is attached.
    pub fn rigid_body_entity(&self, handle: RigidBodyHandle) -> Option<Entity> {
        self.bodies
            .get(handle)
            .map(|c| Entity::from_bits(c.user_data as u64))
    }

    fn with_query_filter<T>(
        &self,
        filter: QueryFilter,
        f: impl FnOnce(RapierQueryFilter) -> T,
    ) -> T {
        Self::with_query_filter_elts(
            &self.entity2collider,
            &self.entity2body,
            &self.colliders,
            filter,
            f,
        )
    }

    fn with_query_filter_elts<T>(
        entity2collider: &HashMap<Entity, ColliderHandle>,
        entity2body: &HashMap<Entity, RigidBodyHandle>,
        colliders: &ColliderSet,
        filter: QueryFilter,
        f: impl FnOnce(RapierQueryFilter) -> T,
    ) -> T {
        let mut rapier_filter = RapierQueryFilter {
            flags: filter.flags,
            groups: filter.groups.map(CollisionGroups::into),
            exclude_collider: filter
                .exclude_collider
                .and_then(|c| entity2collider.get(&c).copied()),
            exclude_rigid_body: filter
                .exclude_rigid_body
                .and_then(|b| entity2body.get(&b).copied()),
            predicate: None,
        };

        if let Some(predicate) = filter.predicate {
            let wrapped_predicate = |h: ColliderHandle, _: &rapier::geometry::Collider| {
                Self::collider_entity_with_set(colliders, h)
                    .map(predicate)
                    .unwrap_or(false)
            };
            rapier_filter.predicate = Some(&wrapped_predicate);
            f(rapier_filter)
        } else {
            f(rapier_filter)
        }
    }

    /// Advance the simulation, based on the given timestep mode.
    #[allow(clippy::too_many_arguments)]
    pub fn step_simulation(
        &mut self,
        gravity: Vect,
        timestep_mode: TimestepMode,
        events: Option<(EventWriter<CollisionEvent>, EventWriter<ContactForceEvent>)>,
        hooks: &dyn PhysicsHooks,
        time: &Time,
        sim_to_render_time: &mut SimulationToRenderTime,
        mut interpolation_query: Option<
            Query<(&RapierRigidBodyHandle, &mut TransformInterpolation)>,
        >,
    ) {
        let event_queue = events.map(|(ce, fe)| EventQueue {
            deleted_colliders: &self.deleted_colliders,
            collision_events: RwLock::new(ce),
            contact_force_events: RwLock::new(fe),
        });

        let events = self
            .event_handler
            .as_deref()
            .or_else(|| event_queue.as_ref().map(|q| q as &dyn EventHandler))
            .unwrap_or(&() as &dyn EventHandler);

        match timestep_mode {
            TimestepMode::Interpolated {
                dt,
                time_scale,
                substeps,
            } => {
                sim_to_render_time.diff += time.delta_seconds();

                while sim_to_render_time.diff > 0.0 {
                    // NOTE: in this comparison we do the same computations we
                    // will do for the next `while` iteration test, to make sure we
                    // don't get bit by potential float inaccuracy.
                    if sim_to_render_time.diff - dt <= 0.0 {
                        if let Some(interpolation_query) = interpolation_query.as_mut() {
                            // This is the last simulation step to be executed in the loop
                            // Update the previous state transforms
                            for (handle, mut interpolation) in interpolation_query.iter_mut() {
                                if let Some(body) = self.bodies.get(handle.0) {
                                    interpolation.start = Some(*body.position());
                                    interpolation.end = None;
                                }
                            }
                        }
                    }

                    let mut substep_integration_parameters = self.integration_parameters;
                    substep_integration_parameters.dt = dt / (substeps as Real) * time_scale;

                    for _ in 0..substeps {
                        self.pipeline.step(
                            &(gravity / self.physics_scale).into(),
                            &substep_integration_parameters,
                            &mut self.islands,
                            &mut self.broad_phase,
                            &mut self.narrow_phase,
                            &mut self.bodies,
                            &mut self.colliders,
                            &mut self.impulse_joints,
                            &mut self.multibody_joints,
                            &mut self.ccd_solver,
                            None,
                            hooks,
                            events,
                        );
                    }

                    sim_to_render_time.diff -= dt;
                }
            }
            TimestepMode::Variable {
                max_dt,
                time_scale,
                substeps,
            } => {
                let mut substep_integration_parameters = self.integration_parameters;
                substep_integration_parameters.dt =
                    (time.delta_seconds() * time_scale).min(max_dt) / (substeps as Real);

                for _ in 0..substeps {
                    self.pipeline.step(
                        &(gravity / self.physics_scale).into(),
                        &substep_integration_parameters,
                        &mut self.islands,
                        &mut self.broad_phase,
                        &mut self.narrow_phase,
                        &mut self.bodies,
                        &mut self.colliders,
                        &mut self.impulse_joints,
                        &mut self.multibody_joints,
                        &mut self.ccd_solver,
                        None,
                        hooks,
                        events,
                    );
                }
            }
            TimestepMode::Fixed { dt, substeps } => {
                let mut substep_integration_parameters = self.integration_parameters;
                substep_integration_parameters.dt = dt / (substeps as Real);

                for _ in 0..substeps {
                    self.pipeline.step(
                        &(gravity / self.physics_scale).into(),
                        &substep_integration_parameters,
                        &mut self.islands,
                        &mut self.broad_phase,
                        &mut self.narrow_phase,
                        &mut self.bodies,
                        &mut self.colliders,
                        &mut self.impulse_joints,
                        &mut self.multibody_joints,
                        &mut self.ccd_solver,
                        None,
                        hooks,
                        events,
                    );
                }
            }
        }
    }

    /// This method makes sure tha the rigid-body positions have been propagated to
    /// their attached colliders, without having to perform a srimulation step.
    pub fn propagate_modified_body_positions_to_colliders(&mut self) {
        self.bodies
            .propagate_modified_body_positions_to_colliders(&mut self.colliders);
    }

    /// Updates the state of the query pipeline, based on the collider positions known
    /// from the last timestep or the last call to `self.propagate_modified_body_positions_to_colliders()`.
    pub fn update_query_pipeline(&mut self) {
        self.query_pipeline.update(&self.bodies, &self.colliders);
    }

    /// The map from entities to rigid-body handles.
    pub fn entity2body(&self) -> &HashMap<Entity, RigidBodyHandle> {
        &self.entity2body
    }

    /// The map from entities to collider handles.
    pub fn entity2collider(&self) -> &HashMap<Entity, ColliderHandle> {
        &self.entity2collider
    }

    /// The map from entities to impulse joint handles.
    pub fn entity2impulse_joint(&self) -> &HashMap<Entity, ImpulseJointHandle> {
        &self.entity2impulse_joint
    }

    /// The map from entities to multibody joint handles.
    pub fn entity2multibody_joint(&self) -> &HashMap<Entity, MultibodyJointHandle> {
        &self.entity2multibody_joint
    }

    /// Attempts to move shape, optionally sliding or climbing obstacles.
    ///
    /// # Parameters
    /// * `movement`: the translational movement to apply.
    /// * `shape`: the shape to move.
    /// * `shape_translation`: the initial position of the shape.
    /// * `shape_rotation`: the rotation of the shape.
    /// * `shape_mass`: the mass of the shape to be considered by the impulse calculation if
    ///                 `MoveShapeOptions::apply_impulse_to_dynamic_bodies` is set to true.
    /// * `options`: configures the behavior of the automatic sliding and climbing.
    /// * `filter`: indicates what collider or rigid-body needs to be ignored by the obstacle detection.
    /// * `events`: callback run on each obstacle hit by the shape on its path.
    #[allow(clippy::too_many_arguments)]
    pub fn move_shape(
        &mut self,
        movement: Vect,
        shape: &Collider,
        shape_translation: Vect,
        shape_rotation: Rot,
        shape_mass: Real,
        options: &MoveShapeOptions,
        filter: QueryFilter,
        mut events: impl FnMut(CharacterCollision),
    ) -> MoveShapeOutput {
        let physics_scale = self.physics_scale;
        let mut scaled_shape = shape.clone();
        // TODO: how to set a good number of subdivisions, we don’t have access to the
        //       RapierConfiguration::scaled_shape_subdivision here.
        scaled_shape.set_scale(shape.scale / physics_scale, 20);

        let up = options
            .up
            .try_into()
            .expect("The up vector must be non-zero.");
        let autostep = options.autostep.map(|autostep| CharacterAutostep {
            max_height: autostep.max_height.map_absolute(|x| x / physics_scale),
            min_width: autostep.min_width.map_absolute(|x| x / physics_scale),
            include_dynamic_bodies: autostep.include_dynamic_bodies,
        });
        let controller = rapier::control::KinematicCharacterController {
            up,
            offset: options.offset.map_absolute(|x| x / physics_scale),
            slide: options.slide,
            autostep,
            max_slope_climb_angle: options.max_slope_climb_angle,
            min_slope_slide_angle: options.min_slope_slide_angle,
            snap_to_ground: options
                .snap_to_ground
                .map(|x| x.map_absolute(|x| x / physics_scale)),
        };

        self.character_collisions_collector.clear();

        // TODO: having to grab all the references to avoid having self in
        //       the closure is ugly.
        let dt = self.integration_parameters.dt;
        let colliders = &self.colliders;
        let bodies = &mut self.bodies;
        let query_pipeline = &self.query_pipeline;
        let collisions = &mut self.character_collisions_collector;
        collisions.clear();

        let result = Self::with_query_filter_elts(
            &self.entity2collider,
            &self.entity2body,
            &self.colliders,
            filter,
            move |filter| {
                let result = controller.move_shape(
                    dt,
                    bodies,
                    colliders,
                    query_pipeline,
                    (&scaled_shape).into(),
                    &(shape_translation / physics_scale, shape_rotation).into(),
                    (movement / physics_scale).into(),
                    filter,
                    |c| {
                        if let Some(collision) =
                            CharacterCollision::from_raw_with_set(physics_scale, colliders, &c)
                        {
                            events(collision);
                        }
                        collisions.push(c);
                    },
                );

                if options.apply_impulse_to_dynamic_bodies {
                    for collision in &*collisions {
                        controller.solve_character_collision_impulses(
                            dt,
                            bodies,
                            colliders,
                            query_pipeline,
                            (&scaled_shape).into(),
                            shape_mass,
                            collision,
                            filter,
                        )
                    }
                }

                result
            },
        );

        MoveShapeOutput {
            effective_translation: (result.translation * physics_scale).into(),
            grounded: result.grounded,
        }
    }

    /// Find the closest intersection between a ray and a set of collider.
    ///
    /// # Parameters
    /// * `ray_origin`: the starting point of the ray to cast.
    /// * `ray_dir`: the direction of the ray to cast.
    /// * `max_toi`: the maximum time-of-impact that can be reported by this cast. This effectively
    ///   limits the length of the ray to `ray.dir.norm() * max_toi`. Use `Real::MAX` for an unbounded ray.
    /// * `solid`: if this is `true` an impact at time 0.0 (i.e. at the ray origin) is returned if
    ///            it starts inside of a shape. If this `false` then the ray will hit the shape's boundary
    ///            even if its starts inside of it.
    /// * `filter`: set of rules used to determine which collider is taken into account by this scene query.
    pub fn cast_ray(
        &self,
        ray_origin: Vect,
        ray_dir: Vect,
        max_toi: Real,
        solid: bool,
        filter: QueryFilter,
    ) -> Option<(Entity, Real)> {
        let ray = Ray::new(
            (ray_origin / self.physics_scale).into(),
            (ray_dir / self.physics_scale).into(),
        );

        let (h, toi) = self.with_query_filter(filter, move |filter| {
            self.query_pipeline.cast_ray(
                &self.bodies,
                &self.colliders,
                &ray,
                max_toi,
                solid,
                filter,
            )
        })?;

        self.collider_entity(h).map(|e| (e, toi))
    }

    /// Find the closest intersection between a ray and a set of collider.
    ///
    /// # Parameters
    /// * `ray_origin`: the starting point of the ray to cast.
    /// * `ray_dir`: the direction of the ray to cast.
    /// * `max_toi`: the maximum time-of-impact that can be reported by this cast. This effectively
    ///   limits the length of the ray to `ray.dir.norm() * max_toi`. Use `Real::MAX` for an unbounded ray.
    /// * `solid`: if this is `true` an impact at time 0.0 (i.e. at the ray origin) is returned if
    ///            it starts inside of a shape. If this `false` then the ray will hit the shape's boundary
    ///            even if its starts inside of it.
    /// * `filter`: set of rules used to determine which collider is taken into account by this scene query.
    pub fn cast_ray_and_get_normal(
        &self,
        ray_origin: Vect,
        ray_dir: Vect,
        max_toi: Real,
        solid: bool,
        filter: QueryFilter,
    ) -> Option<(Entity, RayIntersection)> {
        let ray = Ray::new(
            (ray_origin / self.physics_scale).into(),
            (ray_dir / self.physics_scale).into(),
        );

        let (h, result) = self.with_query_filter(filter, move |filter| {
            self.query_pipeline.cast_ray_and_get_normal(
                &self.bodies,
                &self.colliders,
                &ray,
                max_toi,
                solid,
                filter,
            )
        })?;

        self.collider_entity(h)
            .map(|e| (e, RayIntersection::from_rapier(result, ray_origin, ray_dir)))
    }

    /// Find the all intersections between a ray and a set of collider and passes them to a callback.
    ///
    /// # Parameters
    /// * `ray_origin`: the starting point of the ray to cast.
    /// * `ray_dir`: the direction of the ray to cast.
    /// * `max_toi`: the maximum time-of-impact that can be reported by this cast. This effectively
    ///   limits the length of the ray to `ray.dir.norm() * max_toi`. Use `Real::MAX` for an unbounded ray.
    /// * `solid`: if this is `true` an impact at time 0.0 (i.e. at the ray origin) is returned if
    ///            it starts inside of a shape. If this `false` then the ray will hit the shape's boundary
    ///            even if its starts inside of it.
    /// * `filter`: set of rules used to determine which collider is taken into account by this scene query.
    /// * `callback`: function executed on each collider for which a ray intersection has been found.
    ///               There is no guarantees on the order the results will be yielded. If this callback returns `false`,
    ///               this method will exit early, ignore any further raycast.
    #[allow(clippy::too_many_arguments)]
    pub fn intersections_with_ray(
        &self,
        ray_origin: Vect,
        ray_dir: Vect,
        max_toi: Real,
        solid: bool,
        filter: QueryFilter,
        mut callback: impl FnMut(Entity, RayIntersection) -> bool,
    ) {
        let ray = Ray::new(
            (ray_origin / self.physics_scale).into(),
            (ray_dir / self.physics_scale).into(),
        );
        let callback = |h, inter: rapier::prelude::RayIntersection| {
            self.collider_entity(h)
                .map(|e| callback(e, RayIntersection::from_rapier(inter, ray_origin, ray_dir)))
                .unwrap_or(true)
        };

        self.with_query_filter(filter, move |filter| {
            self.query_pipeline.intersections_with_ray(
                &self.bodies,
                &self.colliders,
                &ray,
                max_toi,
                solid,
                filter,
                callback,
            )
        });
    }

    /// Gets the handle of up to one collider intersecting the given shape.
    ///
    /// # Parameters
    /// * `shape_pos` - The position of the shape used for the intersection test.
    /// * `shape` - The shape used for the intersection test.
    /// * `filter`: set of rules used to determine which collider is taken into account by this scene query.
    pub fn intersection_with_shape(
        &self,
        shape_pos: Vect,
        shape_rot: Rot,
        shape: &Collider,
        filter: QueryFilter,
    ) -> Option<Entity> {
        let scaled_transform = (shape_pos / self.physics_scale, shape_rot).into();
        let mut scaled_shape = shape.clone();
        // TODO: how to set a good number of subdivisions, we don’t have access to the
        //       RapierConfiguration::scaled_shape_subdivision here.
        scaled_shape.set_scale(shape.scale / self.physics_scale, 20);

        let h = self.with_query_filter(filter, move |filter| {
            self.query_pipeline.intersection_with_shape(
                &self.bodies,
                &self.colliders,
                &scaled_transform,
                &*scaled_shape.raw,
                filter,
            )
        })?;

        self.collider_entity(h)
    }

    /// Find the projection of a point on the closest collider.
    ///
    /// # Parameters
    /// * `point` - The point to project.
    /// * `solid` - If this is set to `true` then the collider shapes are considered to
    ///   be plain (if the point is located inside of a plain shape, its projection is the point
    ///   itself). If it is set to `false` the collider shapes are considered to be hollow
    ///   (if the point is located inside of an hollow shape, it is projected on the shape's
    ///   boundary).
    /// * `filter`: set of rules used to determine which collider is taken into account by this scene query.
    pub fn project_point(
        &self,
        point: Vect,
        solid: bool,
        filter: QueryFilter,
    ) -> Option<(Entity, PointProjection)> {
        let (h, result) = self.with_query_filter(filter, move |filter| {
            self.query_pipeline.project_point(
                &self.bodies,
                &self.colliders,
                &(point / self.physics_scale).into(),
                solid,
                filter,
            )
        })?;

        self.collider_entity(h)
            .map(|e| (e, PointProjection::from_rapier(self.physics_scale, result)))
    }

    /// Find all the colliders containing the given point.
    ///
    /// # Parameters
    /// * `point` - The point used for the containment test.
    /// * `filter`: set of rules used to determine which collider is taken into account by this scene query.
    /// * `callback` - A function called with each collider with a shape containing the `point`.
    ///                If this callback returns `false`, this method will exit early, ignore any
    ///                further point projection.
    pub fn intersections_with_point(
        &self,
        point: Vect,
        filter: QueryFilter,
        mut callback: impl FnMut(Entity) -> bool,
    ) {
        #[allow(clippy::redundant_closure)]
        // False-positive, we can't move callback, closure becomes `FnOnce`
        let callback = |h| self.collider_entity(h).map(|e| callback(e)).unwrap_or(true);

        self.with_query_filter(filter, move |filter| {
            self.query_pipeline.intersections_with_point(
                &self.bodies,
                &self.colliders,
                &(point / self.physics_scale).into(),
                filter,
                callback,
            )
        });
    }

    /// Find the projection of a point on the closest collider.
    ///
    /// The results include the ID of the feature hit by the point.
    ///
    /// # Parameters
    /// * `point` - The point to project.
    /// * `solid` - If this is set to `true` then the collider shapes are considered to
    ///   be plain (if the point is located inside of a plain shape, its projection is the point
    ///   itself). If it is set to `false` the collider shapes are considered to be hollow
    ///   (if the point is located inside of an hollow shape, it is projected on the shape's
    ///   boundary).
    /// * `filter`: set of rules used to determine which collider is taken into account by this scene query.
    pub fn project_point_and_get_feature(
        &self,
        point: Vect,
        filter: QueryFilter,
    ) -> Option<(Entity, PointProjection, FeatureId)> {
        let (h, proj, fid) = self.with_query_filter(filter, move |filter| {
            self.query_pipeline.project_point_and_get_feature(
                &self.bodies,
                &self.colliders,
                &(point / self.physics_scale).into(),
                filter,
            )
        })?;

        self.collider_entity(h).map(|e| {
            (
                e,
                PointProjection::from_rapier(self.physics_scale, proj),
                fid,
            )
        })
    }

    /// Finds all entities of all the colliders with an Aabb intersecting the given Aabb.
    pub fn colliders_with_aabb_intersecting_aabb(
        &self,
        aabb: Aabb,
        mut callback: impl FnMut(Entity) -> bool,
    ) {
        #[cfg(feature = "dim2")]
        let scaled_aabb = RapierAabb {
            mins: (aabb.min().xy() / self.physics_scale).into(),
            maxs: (aabb.max().xy() / self.physics_scale).into(),
        };
        #[cfg(feature = "dim3")]
        let scaled_aabb = RapierAabb {
            mins: (aabb.min() / self.physics_scale).into(),
            maxs: (aabb.max() / self.physics_scale).into(),
        };
        #[allow(clippy::redundant_closure)]
        // False-positive, we can't move callback, closure becomes `FnOnce`
        let callback = |h: &ColliderHandle| {
            self.collider_entity(*h)
                .map(|e| callback(e))
                .unwrap_or(true)
        };
        self.query_pipeline
            .colliders_with_aabb_intersecting_aabb(&scaled_aabb, callback);
    }

    /// Casts a shape at a constant linear velocity and retrieve the first collider it hits.
    ///
    /// This is similar to ray-casting except that we are casting a whole shape instead of just a
    /// point (the ray origin). In the resulting `TOI`, witness and normal 1 refer to the world
    /// collider, and are in world space.
    ///
    /// # Parameters
    /// * `shape_pos` - The initial position of the shape to cast.
    /// * `shape_vel` - The constant velocity of the shape to cast (i.e. the cast direction).
    /// * `shape` - The shape to cast.
    /// * `max_toi` - The maximum time-of-impact that can be reported by this cast. This effectively
    ///   limits the distance traveled by the shape to `shapeVel.norm() * maxToi`.
    /// * `filter`: set of rules used to determine which collider is taken into account by this scene query.
    #[allow(clippy::too_many_arguments)]
    pub fn cast_shape(
        &self,
        shape_pos: Vect,
        shape_rot: Rot,
        shape_vel: Vect,
        shape: &Collider,
        max_toi: Real,
        filter: QueryFilter,
    ) -> Option<(Entity, Toi)> {
        let scaled_transform = (shape_pos / self.physics_scale, shape_rot).into();
        let mut scaled_shape = shape.clone();
        // TODO: how to set a good number of subdivisions, we don’t have access to the
        //       RapierConfiguration::scaled_shape_subdivision here.
        scaled_shape.set_scale(shape.scale / self.physics_scale, 20);

        let (h, result) = self.with_query_filter(filter, move |filter| {
            self.query_pipeline.cast_shape(
                &self.bodies,
                &self.colliders,
                &scaled_transform,
                &(shape_vel / self.physics_scale).into(),
                &*scaled_shape.raw,
                max_toi,
                true,
                filter,
            )
        })?;

        self.collider_entity(h)
            .map(|e| (e, Toi::from_rapier(self.physics_scale, result)))
    }

    /* TODO: we need to wrap the NonlinearRigidMotion somehow.
     *
    /// Casts a shape with an arbitrary continuous motion and retrieve the first collider it hits.
    ///
    /// In the resulting `TOI`, witness and normal 1 refer to the world collider, and are in world
    /// space.
    ///
    /// # Parameters
    /// * `shape_motion` - The motion of the shape.
    /// * `shape` - The shape to cast.
    /// * `start_time` - The starting time of the interval where the motion takes place.
    /// * `end_time` - The end time of the interval where the motion takes place.
    /// * `stop_at_penetration` - If the casted shape starts in a penetration state with any
    ///    collider, two results are possible. If `stop_at_penetration` is `true` then, the
    ///    result will have a `toi` equal to `start_time`. If `stop_at_penetration` is `false`
    ///    then the nonlinear shape-casting will see if further motion wrt. the penetration normal
    ///    would result in tunnelling. If it does not (i.e. we have a separating velocity along
    ///    that normal) then the nonlinear shape-casting will attempt to find another impact,
    ///    at a time `> start_time` that could result in tunnelling.
    /// * `filter`: set of rules used to determine which collider is taken into account by this scene query.
    pub fn nonlinear_cast_shape(
        &self,
        shape_motion: &NonlinearRigidMotion,
        shape: &Collider,
        start_time: Real,
        end_time: Real,
        stop_at_penetration: bool,
        filter: QueryFilter,
    ) -> Option<(Entity, Toi)> {
        let scaled_transform = (shape_pos * self.physics_scale, shape_rot).into();
        let mut scaled_shape = shape.clone();
        // TODO: how to set a good number of subdivisions, we don’t have access to the
        //       RapierConfiguration::scaled_shape_subdivision here.
        scaled_shape.set_scale(shape.scale * self.physics_scale, 20);

        let (h, result) = self.with_query_filter(filter, move |filter| {
            self.query_pipeline.nonlinear_cast_shape(
                &self.bodies,
                &self.colliders,
                shape_motion,
                &*scaled_shape.raw,
                start_time,
                end_time,
                stop_at_penetration,
                filter,
            )
        })?;

        self.collider_entity(h).map(|e| (e, result))
    }
     */

    /// Retrieve all the colliders intersecting the given shape.
    ///
    /// # Parameters
    /// * `shapePos` - The position of the shape to test.
    /// * `shapeRot` - The orientation of the shape to test.
    /// * `shape` - The shape to test.
    /// * `filter`: set of rules used to determine which collider is taken into account by this scene query.
    /// * `callback` - A function called with the entities of each collider intersecting the `shape`.
    pub fn intersections_with_shape(
        &self,
        shape_pos: Vect,
        shape_rot: Rot,
        shape: &Collider,
        filter: QueryFilter,
        mut callback: impl FnMut(Entity) -> bool,
    ) {
        let scaled_transform = (shape_pos / self.physics_scale, shape_rot).into();
        let mut scaled_shape = shape.clone();
        // TODO: how to set a good number of subdivisions, we don’t have access to the
        //       RapierConfiguration::scaled_shape_subdivision here.
        scaled_shape.set_scale(shape.scale / self.physics_scale, 20);

        #[allow(clippy::redundant_closure)]
        // False-positive, we can't move callback, closure becomes `FnOnce`
        let callback = |h| self.collider_entity(h).map(|e| callback(e)).unwrap_or(true);

        self.with_query_filter(filter, move |filter| {
            self.query_pipeline.intersections_with_shape(
                &self.bodies,
                &self.colliders,
                &scaled_transform,
                &*scaled_shape.raw,
                filter,
                callback,
            )
        });
    }
}