Skip to main content

rapier2d/pipeline/
physics_world.rs

1use crate::alloc_prelude::*;
2use crate::dynamics::{
3    CCDSolver, GenericJoint, ImpulseJoint, ImpulseJointHandle, ImpulseJointSet,
4    IntegrationParameters, IslandManager, Multibody, MultibodyJointHandle, MultibodyJointSet,
5    MultibodyLink, MultibodyLinkId, RigidBody, RigidBodyHandle, RigidBodySet,
6};
7use crate::geometry::{
8    BroadPhaseBvh, Collider, ColliderHandle, ColliderSet, ContactPair, DefaultBroadPhase,
9    NarrowPhase,
10};
11use crate::math::{Real, Vector};
12use crate::pipeline::{
13    EventHandler, PhysicsHooks, PhysicsPipeline, Quarantine, QueryFilter, QueryPipeline,
14};
15use parry::bounding_volume::{Aabb, BoundingVolume};
16use parry::partitioning::BvhNode;
17use parry::query::details::ShapeCastOptions;
18use parry::query::{NonlinearRigidMotion, RayCast, ShapeCastHit};
19use parry::shape::{FeatureId, Shape};
20
21use crate::geometry::{PointProjection, Ray, RayIntersection};
22use crate::math::Pose;
23
24#[cfg(feature = "debug-render")]
25use crate::pipeline::{DebugRenderBackend, DebugRenderPipeline};
26
27/// A convenience wrapper that bundles all the Rapier physics state into a single struct.
28///
29/// Rapier intentionally splits its state across many structs (`RigidBodySet`, `ColliderSet`,
30/// `NarrowPhase`, etc.) to give you fine-grained control over borrowing. This is important
31/// for advanced use cases, but it makes simple setups verbose.
32///
33/// `PhysicsWorld` gives you a single struct for the common case. All fields are `pub`, so
34/// you can always reach in and borrow individual fields when the borrow checker requires it.
35///
36/// # Example
37/// ```
38/// # use rapier3d::prelude::*;
39/// let mut world = PhysicsWorld::default();
40///
41/// // Create a ground plane
42/// let (_ground, _) = world.insert(
43///     RigidBodyBuilder::fixed(),
44///     ColliderBuilder::cuboid(10.0, 0.1, 10.0),
45/// );
46///
47/// // Create a falling ball
48/// let (ball, _) = world.insert(
49///     RigidBodyBuilder::dynamic().translation(Vector::new(0.0, 5.0, 0.0)),
50///     ColliderBuilder::ball(0.5),
51/// );
52///
53/// // Simulate 100 steps
54/// for _ in 0..100 {
55///     world.step();
56/// }
57///
58/// println!("Ball position: {:?}", world.bodies[ball].translation());
59/// ```
60#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
61pub struct PhysicsWorld {
62    /// Gravity applied to all dynamic bodies each step.
63    pub gravity: Vector,
64    /// Parameters controlling the simulation (timestep, solver iterations, etc.).
65    pub integration_parameters: IntegrationParameters,
66    /// The main simulation pipeline that orchestrates each physics step.
67    #[cfg_attr(feature = "serde-serialize", serde(skip))]
68    pub physics_pipeline: PhysicsPipeline,
69    /// Manages active/sleeping body groups (islands) for efficient simulation.
70    pub islands: IslandManager,
71    /// The broad-phase acceleration structure for fast spatial queries.
72    pub broad_phase: BroadPhaseBvh,
73    /// Precise contact and intersection detection between collider pairs.
74    pub narrow_phase: NarrowPhase,
75    /// All rigid bodies in this world.
76    pub bodies: RigidBodySet,
77    /// All colliders (collision shapes) in this world.
78    pub colliders: ColliderSet,
79    /// All impulse-based joints (hinges, springs, ropes, etc.).
80    pub impulse_joints: ImpulseJointSet,
81    /// All multibody joints (kinematic chains, articulations).
82    pub multibody_joints: MultibodyJointSet,
83    /// The continuous collision detection solver.
84    ///
85    /// Workspace only: not part of a snapshot (see the type docs).
86    #[cfg_attr(feature = "serde-serialize", serde(skip))]
87    pub ccd_solver: CCDSolver,
88}
89
90impl Default for PhysicsWorld {
91    fn default() -> Self {
92        Self {
93            gravity: Vector::Y * -9.81,
94            integration_parameters: IntegrationParameters::default(),
95            physics_pipeline: PhysicsPipeline::new(),
96            islands: IslandManager::new(),
97            broad_phase: DefaultBroadPhase::default(),
98            narrow_phase: NarrowPhase::new(),
99            bodies: RigidBodySet::new(),
100            colliders: ColliderSet::new(),
101            impulse_joints: ImpulseJointSet::new(),
102            multibody_joints: MultibodyJointSet::new(),
103            ccd_solver: CCDSolver::new(),
104        }
105    }
106}
107
108impl PhysicsWorld {
109    /// Creates a new physics world with default parameters and gravity `(0, -9.81, 0)`.
110    pub fn new() -> Self {
111        Self::default()
112    }
113
114    // ── Simulation ──────────────────────────────────────────────────────
115
116    /// Advance the simulation by one timestep, using no hooks and no event handler.
117    ///
118    /// This is the simplest way to step. If you need collision events or physics hooks,
119    /// use [`step_with_events`](Self::step_with_events).
120    pub fn step(&mut self) {
121        self.step_with_events(&(), &());
122    }
123
124    /// Advance the simulation by one timestep with custom physics hooks and event handling.
125    ///
126    /// # Example
127    /// ```
128    /// # use rapier3d::prelude::*;
129    /// # let mut world = PhysicsWorld::default();
130    /// # use std::sync::mpsc::channel;
131    /// let (collision_send, collision_recv) = channel();
132    /// let (contact_force_send, contact_force_recv) = channel();
133    /// let event_handler = ChannelEventCollector::new(collision_send, contact_force_send);
134    ///
135    /// world.step_with_events(&(), &event_handler);
136    ///
137    /// while let Ok(event) = collision_recv.try_recv() {
138    ///     println!("Collision event: {:?}", event);
139    /// }
140    /// ```
141    pub fn step_with_events(&mut self, hooks: &dyn PhysicsHooks, events: &dyn EventHandler) {
142        self.physics_pipeline.step(
143            self.gravity,
144            &self.integration_parameters,
145            &mut self.islands,
146            &mut self.broad_phase,
147            &mut self.narrow_phase,
148            &mut self.bodies,
149            &mut self.colliders,
150            &mut self.impulse_joints,
151            &mut self.multibody_joints,
152            &mut self.ccd_solver,
153            hooks,
154            events,
155        );
156    }
157
158    /// The bodies and colliders automatically disabled during the last step because their
159    /// state became non-finite; see [`Quarantine`].
160    pub fn quarantine(&self) -> &Quarantine {
161        self.physics_pipeline.quarantine()
162    }
163
164    // ── Rigid bodies ────────────────────────────────────────────────────
165
166    /// Insert a rigid body with an attached collider, and return both handles.
167    ///
168    /// This bundles the two most common setup steps — creating a body and attaching
169    /// its collider — into a single call. For the rare case of a rigid body without
170    /// any collider (typically an anchor body for joints), use
171    /// [`insert_body`](Self::insert_body) instead. For compound bodies with multiple
172    /// colliders, pass the first collider here and use
173    /// [`insert_collider`](Self::insert_collider) with `Some(body_handle)` for the rest.
174    ///
175    /// # Example
176    /// ```
177    /// # use rapier3d::prelude::*;
178    /// # let mut world = PhysicsWorld::default();
179    /// let (body, collider) = world.insert(
180    ///     RigidBodyBuilder::dynamic().translation(Vector::new(0.0, 5.0, 0.0)),
181    ///     ColliderBuilder::ball(0.5),
182    /// );
183    /// ```
184    pub fn insert(
185        &mut self,
186        body: impl Into<RigidBody>,
187        collider: impl Into<Collider>,
188    ) -> (RigidBodyHandle, ColliderHandle) {
189        let body_handle = self.bodies.insert(body);
190        let collider_handle =
191            self.colliders
192                .insert_with_parent(collider, body_handle, &mut self.bodies);
193        (body_handle, collider_handle)
194    }
195
196    /// Insert a rigid body without any collider, and return its handle.
197    ///
198    /// Most bodies should be inserted together with a collider — prefer
199    /// [`insert`](Self::insert) when possible. Use this method for bodies that
200    /// truly don't need a collider, such as anchor bodies used purely as joint
201    /// attachment points.
202    ///
203    /// # Example
204    /// ```
205    /// # use rapier3d::prelude::*;
206    /// # let mut world = PhysicsWorld::default();
207    /// let anchor = world.insert_body(RigidBodyBuilder::fixed());
208    /// ```
209    pub fn insert_body(&mut self, body: impl Into<RigidBody>) -> RigidBodyHandle {
210        self.bodies.insert(body)
211    }
212
213    /// Remove a rigid body and all its attached colliders and joints.
214    ///
215    /// Returns the removed body, or `None` if the handle was invalid.
216    pub fn remove_body(&mut self, handle: RigidBodyHandle) -> Option<RigidBody> {
217        self.bodies.remove(
218            handle,
219            &mut self.islands,
220            &mut self.colliders,
221            &mut self.impulse_joints,
222            &mut self.multibody_joints,
223            true,
224        )
225    }
226
227    /// Wake a sleeping body, forcing it back into the active simulation.
228    ///
229    /// Useful after manually moving a body, applying forces, or otherwise wanting to make
230    /// sure it gets simulated on the next step. No-op for already-awake bodies, and for
231    /// fixed bodies (which don't sleep).
232    ///
233    /// # Parameters
234    /// * `strong` — if `true`, the body is guaranteed to stay awake for several frames.
235    ///   If `false`, it may sleep again immediately if sleep conditions are met.
236    pub fn wake_up(&mut self, handle: RigidBodyHandle, strong: bool) {
237        self.islands.wake_up(&mut self.bodies, handle, strong);
238    }
239
240    /// Wake every sleeping body in the world.
241    pub fn wake_up_all(&mut self, strong: bool) {
242        let handles: Vec<_> = self.bodies.iter().map(|(h, _)| h).collect();
243        for handle in handles {
244            self.islands.wake_up(&mut self.bodies, handle, strong);
245        }
246    }
247
248    // ── Colliders ───────────────────────────────────────────────────────
249
250    /// Insert a collider, optionally attached to a rigid body, and return its handle.
251    ///
252    /// Pass `Some(parent)` to attach the collider to a rigid body — its position will
253    /// then be interpreted relative to its parent. Pass `None` for a standalone collider,
254    /// useful for static collision geometry or sensors that don't need a rigid body.
255    ///
256    /// # Example
257    /// ```
258    /// # use rapier3d::prelude::*;
259    /// # let mut world = PhysicsWorld::default();
260    /// let body = world.insert_body(RigidBodyBuilder::dynamic());
261    ///
262    /// // Attached collider.
263    /// let attached = world.insert_collider(ColliderBuilder::ball(0.5), Some(body));
264    ///
265    /// // Standalone collider (e.g. static geometry).
266    /// let standalone = world.insert_collider(ColliderBuilder::cuboid(10.0, 0.1, 10.0), None);
267    /// ```
268    pub fn insert_collider(
269        &mut self,
270        collider: impl Into<Collider>,
271        parent: Option<RigidBodyHandle>,
272    ) -> ColliderHandle {
273        match parent {
274            Some(parent) => self
275                .colliders
276                .insert_with_parent(collider, parent, &mut self.bodies),
277            None => self.colliders.insert(collider),
278        }
279    }
280
281    /// Remove a collider from the world.
282    ///
283    /// Returns the removed collider, or `None` if the handle was invalid.
284    pub fn remove_collider(&mut self, handle: ColliderHandle) -> Option<Collider> {
285        self.colliders
286            .remove(handle, &mut self.islands, &mut self.bodies, true)
287    }
288
289    // ── Impulse joints ──────────────────────────────────────────────────
290
291    /// Insert an impulse joint between two bodies and return its handle.
292    ///
293    /// # Example
294    /// ```
295    /// # use rapier3d::prelude::*;
296    /// # let mut world = PhysicsWorld::default();
297    /// let body1 = world.insert_body(RigidBodyBuilder::dynamic());
298    /// let body2 = world.insert_body(RigidBodyBuilder::dynamic());
299    /// let joint = world.insert_impulse_joint(body1, body2, RevoluteJointBuilder::new(Vector::Z));
300    /// ```
301    pub fn insert_impulse_joint(
302        &mut self,
303        body1: RigidBodyHandle,
304        body2: RigidBodyHandle,
305        joint: impl Into<GenericJoint>,
306    ) -> ImpulseJointHandle {
307        self.impulse_joints.insert(body1, body2, joint, true)
308    }
309
310    /// Remove an impulse joint.
311    ///
312    /// Returns the removed joint data, or `None` if the handle was invalid.
313    pub fn remove_impulse_joint(&mut self, handle: ImpulseJointHandle) -> Option<GenericJoint> {
314        self.impulse_joints.remove(handle, true).map(|j| j.data)
315    }
316
317    /// Iterate over every impulse joint in the world as `(handle, &joint)` pairs.
318    pub fn impulse_joints(&self) -> impl Iterator<Item = (ImpulseJointHandle, &ImpulseJoint)> {
319        self.impulse_joints.iter()
320    }
321
322    /// Iterate over every impulse joint attached to the given rigid body.
323    ///
324    /// Each item is `(body1, body2, joint_handle, &joint)`. `body1` and `body2` are the
325    /// joint's endpoints — one of them is always `body`, the other is the neighbor.
326    pub fn impulse_joints_with(
327        &self,
328        body: RigidBodyHandle,
329    ) -> impl Iterator<
330        Item = (
331            RigidBodyHandle,
332            RigidBodyHandle,
333            ImpulseJointHandle,
334            &ImpulseJoint,
335        ),
336    > {
337        self.impulse_joints.attached_joints(body)
338    }
339
340    // ── Multibody joints ────────────────────────────────────────────────
341
342    /// Insert a multibody joint between two bodies and return its handle.
343    ///
344    /// Returns `None` if the joint would create an invalid kinematic chain (e.g. a cycle).
345    pub fn insert_multibody_joint(
346        &mut self,
347        body1: RigidBodyHandle,
348        body2: RigidBodyHandle,
349        joint: impl Into<GenericJoint>,
350    ) -> Option<MultibodyJointHandle> {
351        self.multibody_joints.insert(body1, body2, joint, true)
352    }
353
354    /// Remove a multibody joint.
355    pub fn remove_multibody_joint(&mut self, handle: MultibodyJointHandle) {
356        self.multibody_joints.remove(handle, true);
357    }
358
359    /// Iterate over every multibody joint in the world.
360    ///
361    /// Each item is `(joint_handle, &link_id, &multibody, &link)`.
362    pub fn multibody_joints(
363        &self,
364    ) -> impl Iterator<
365        Item = (
366            MultibodyJointHandle,
367            &MultibodyLinkId,
368            &Multibody,
369            &MultibodyLink,
370        ),
371    > {
372        self.multibody_joints.iter()
373    }
374
375    /// Iterate over every multibody joint attached to the given rigid body.
376    ///
377    /// Each item is `(body1, body2, joint_handle)`. `body1` and `body2` are the joint's
378    /// endpoints — one of them is always `body`, the other is the neighbor.
379    pub fn multibody_joints_with(
380        &self,
381        body: RigidBodyHandle,
382    ) -> impl Iterator<Item = (RigidBodyHandle, RigidBodyHandle, MultibodyJointHandle)> + '_ {
383        self.multibody_joints.attached_joints(body)
384    }
385
386    // ── Scene queries ───────────────────────────────────────────────────
387
388    /// Get a [`QueryPipeline`] for performing spatial queries (raycasts, shape casts, etc.).
389    ///
390    /// # Example
391    /// ```
392    /// # use rapier3d::prelude::*;
393    /// # let mut world = PhysicsWorld::default();
394    /// # let (ground, _) = world.insert(
395    /// #     RigidBodyBuilder::fixed(),
396    /// #     ColliderBuilder::cuboid(10.0, 0.1, 10.0),
397    /// # );
398    /// # world.step();
399    /// let query_pipeline = world.query_pipeline();
400    /// let ray = Ray::new(Vector::new(0.0, 10.0, 0.0), Vector::new(0.0, -1.0, 0.0));
401    /// if let Some((handle, toi)) = query_pipeline.cast_ray(&ray, Real::MAX, true) {
402    ///     println!("Hit {:?} at distance {}", handle, toi);
403    /// }
404    /// ```
405    pub fn query_pipeline(&self) -> QueryPipeline<'_> {
406        self.query_pipeline_with_filter(QueryFilter::default())
407    }
408
409    /// Get a [`QueryPipeline`] with a custom [`QueryFilter`].
410    pub fn query_pipeline_with_filter<'a>(&'a self, filter: QueryFilter<'a>) -> QueryPipeline<'a> {
411        self.broad_phase.as_query_pipeline(
412            self.narrow_phase.query_dispatcher(),
413            &self.bodies,
414            &self.colliders,
415            filter,
416        )
417    }
418
419    /// Cast a ray and return the first collider hit.
420    ///
421    /// Shorthand for `world.query_pipeline_with_filter(filter).cast_ray(...)`.
422    ///
423    /// Returns `Some((collider_handle, distance))`, or `None` if nothing was hit.
424    /// Pass [`QueryFilter::default()`] to consider every collider.
425    pub fn cast_ray<'a>(
426        &'a self,
427        ray: &Ray,
428        max_toi: Real,
429        solid: bool,
430        filter: QueryFilter<'a>,
431    ) -> Option<(ColliderHandle, Real)> {
432        self.query_pipeline_with_filter(filter)
433            .cast_ray(ray, max_toi, solid)
434    }
435
436    /// Cast a ray and return the first hit with surface normal information.
437    ///
438    /// Shorthand for `world.query_pipeline_with_filter(filter).cast_ray_and_get_normal(...)`.
439    pub fn cast_ray_and_get_normal<'a>(
440        &'a self,
441        ray: &Ray,
442        max_toi: Real,
443        solid: bool,
444        filter: QueryFilter<'a>,
445    ) -> Option<(ColliderHandle, RayIntersection)> {
446        self.query_pipeline_with_filter(filter)
447            .cast_ray_and_get_normal(ray, max_toi, solid)
448    }
449
450    /// Cast (sweep) a shape through the world and return the first collider hit.
451    ///
452    /// Shorthand for `world.query_pipeline_with_filter(filter).cast_shape(...)`.
453    pub fn cast_shape<'a>(
454        &'a self,
455        shape_pos: &Pose,
456        shape_vel: Vector,
457        shape: &dyn Shape,
458        options: ShapeCastOptions,
459        filter: QueryFilter<'a>,
460    ) -> Option<(ColliderHandle, ShapeCastHit)> {
461        self.query_pipeline_with_filter(filter)
462            .cast_shape(shape_pos, shape_vel, shape, options)
463    }
464
465    /// Cast a shape with a nonlinear motion and return the first collider hit.
466    ///
467    /// Shorthand for `world.query_pipeline_with_filter(filter).cast_shape_nonlinear(...)`.
468    pub fn cast_shape_nonlinear<'a>(
469        &'a self,
470        shape_motion: &NonlinearRigidMotion,
471        shape: &dyn Shape,
472        start_time: Real,
473        end_time: Real,
474        stop_at_penetration: bool,
475        filter: QueryFilter<'a>,
476    ) -> Option<(ColliderHandle, ShapeCastHit)> {
477        self.query_pipeline_with_filter(filter)
478            .cast_shape_nonlinear(
479                shape_motion,
480                shape,
481                start_time,
482                end_time,
483                stop_at_penetration,
484            )
485    }
486
487    /// Find the closest point on any collider to the given point.
488    ///
489    /// Shorthand for `world.query_pipeline_with_filter(filter).project_point(...)`.
490    pub fn project_point<'a>(
491        &'a self,
492        point: Vector,
493        max_dist: Real,
494        solid: bool,
495        filter: QueryFilter<'a>,
496    ) -> Option<(ColliderHandle, PointProjection)> {
497        self.query_pipeline_with_filter(filter)
498            .project_point(point, max_dist, solid)
499    }
500
501    /// Project a point onto the closest collider and also return the geometric feature
502    /// (vertex, edge, or face) that contains the projection.
503    ///
504    /// Shorthand for
505    /// `world.query_pipeline_with_filter(filter).project_point_and_get_feature(...)`.
506    pub fn project_point_and_get_feature<'a>(
507        &'a self,
508        point: Vector,
509        filter: QueryFilter<'a>,
510        max_dist: Real,
511    ) -> Option<(ColliderHandle, PointProjection, FeatureId)> {
512        self.query_pipeline_with_filter(filter)
513            .project_point_and_get_feature(point, max_dist)
514    }
515
516    /// Iterate over every collider that the given ray passes through.
517    ///
518    /// Unlike [`cast_ray`](Self::cast_ray) which stops at the first hit, this yields
519    /// every collider along the ray's path. Each item is
520    /// `(handle, &collider, intersection)`.
521    pub fn intersect_ray<'a>(
522        &'a self,
523        ray: Ray,
524        max_toi: Real,
525        solid: bool,
526        filter: QueryFilter<'a>,
527    ) -> impl Iterator<Item = (ColliderHandle, &'a Collider, RayIntersection)> + 'a {
528        let bvh = &self.broad_phase.tree;
529        let bodies = &self.bodies;
530        let colliders = &self.colliders;
531        bvh.leaves(move |node: &BvhNode| node.aabb().intersects_local_ray(&ray, max_toi))
532            .filter_map(move |leaf| {
533                let (co, co_handle) = colliders.get_unknown_gen(leaf)?;
534                if filter.test(bodies, co_handle, co) {
535                    let intersection =
536                        co.shape
537                            .cast_ray_and_get_normal(co.position(), &ray, max_toi, solid)?;
538                    Some((co_handle, co, intersection))
539                } else {
540                    None
541                }
542            })
543    }
544
545    /// Iterate over every collider that contains the given point.
546    ///
547    /// Each item is `(handle, &collider)`.
548    pub fn intersect_point<'a>(
549        &'a self,
550        point: Vector,
551        filter: QueryFilter<'a>,
552    ) -> impl Iterator<Item = (ColliderHandle, &'a Collider)> + 'a {
553        let bvh = &self.broad_phase.tree;
554        let bodies = &self.bodies;
555        let colliders = &self.colliders;
556        bvh.leaves(move |node: &BvhNode| node.aabb().contains_local_point(point))
557            .filter_map(move |leaf| {
558                let (co, co_handle) = colliders.get_unknown_gen(leaf)?;
559                if filter.test(bodies, co_handle, co)
560                    && co.shape.contains_point(co.position(), point)
561                {
562                    Some((co_handle, co))
563                } else {
564                    None
565                }
566            })
567    }
568
569    /// Iterate over every collider whose shape intersects the given shape positioned
570    /// at `shape_pos`.
571    ///
572    /// Each item is `(handle, &collider)`.
573    pub fn intersect_shape<'a>(
574        &'a self,
575        shape_pos: Pose,
576        shape: &'a dyn Shape,
577        filter: QueryFilter<'a>,
578    ) -> impl Iterator<Item = (ColliderHandle, &'a Collider)> + 'a {
579        let bvh = &self.broad_phase.tree;
580        let bodies = &self.bodies;
581        let colliders = &self.colliders;
582        let dispatcher = self.narrow_phase.query_dispatcher();
583        let shape_aabb = shape.compute_aabb(&shape_pos);
584        bvh.leaves(move |node: &BvhNode| node.aabb().intersects(&shape_aabb))
585            .filter_map(move |leaf| {
586                let (co, co_handle) = colliders.get_unknown_gen(leaf)?;
587                if filter.test(bodies, co_handle, co) {
588                    let pos12 = shape_pos.inv_mul(co.position());
589                    if dispatcher.intersection_test(&pos12, shape, co.shape()) == Ok(true) {
590                        return Some((co_handle, co));
591                    }
592                }
593                None
594            })
595    }
596
597    /// Iterate over every collider whose stored AABB intersects the given AABB.
598    ///
599    /// This is *conservative*: the AABBs used are the ones in the broad-phase BVH,
600    /// not freshly recomputed collider AABBs. Useful for cheap, broad-strokes
601    /// proximity queries.
602    ///
603    /// Each item is `(handle, &collider)`.
604    pub fn intersect_aabb_conservative<'a>(
605        &'a self,
606        aabb: Aabb,
607        filter: QueryFilter<'a>,
608    ) -> impl Iterator<Item = (ColliderHandle, &'a Collider)> + 'a {
609        let bvh = &self.broad_phase.tree;
610        let bodies = &self.bodies;
611        let colliders = &self.colliders;
612        bvh.leaves(move |node: &BvhNode| node.aabb().intersects(&aabb))
613            .filter_map(move |leaf| {
614                let (co, co_handle) = colliders.get_unknown_gen(leaf)?;
615                if filter.test(bodies, co_handle, co) {
616                    Some((co_handle, co))
617                } else {
618                    None
619                }
620            })
621    }
622
623    // ── Contact / intersection queries (from NarrowPhase) ───────────────
624
625    /// Get the contact pair between two specific colliders, if it exists.
626    ///
627    /// Returns `None` if the two colliders are not in contact or not neighbors
628    /// in the broad-phase.
629    pub fn contact_pair(
630        &self,
631        collider1: ColliderHandle,
632        collider2: ColliderHandle,
633    ) -> Option<&ContactPair> {
634        self.narrow_phase.contact_pair(collider1, collider2)
635    }
636
637    /// Iterate over all contact pairs involving the given collider.
638    pub fn contact_pairs_with(
639        &self,
640        collider: ColliderHandle,
641    ) -> impl Iterator<Item = &ContactPair> {
642        self.narrow_phase.contact_pairs_with(collider)
643    }
644
645    /// Iterate over all contact pairs in the world.
646    pub fn contact_pairs(&self) -> impl Iterator<Item = &ContactPair> {
647        self.narrow_phase.contact_pairs()
648    }
649
650    /// Check if two specific colliders are intersecting (for sensor colliders).
651    ///
652    /// Returns `None` if the pair doesn't exist, `Some(true)` if intersecting.
653    pub fn intersection_pair(
654        &self,
655        collider1: ColliderHandle,
656        collider2: ColliderHandle,
657    ) -> Option<bool> {
658        self.narrow_phase.intersection_pair(collider1, collider2)
659    }
660
661    /// Iterate over all intersection pairs involving the given collider.
662    ///
663    /// Each item is `(handle_a, &collider_a, handle_b, &collider_b, intersecting)`.
664    ///
665    /// # Mutable access
666    ///
667    /// There is no `_mut` variant of this method: the same collider may appear in
668    /// several intersection pairs, so a safe `Iterator` yielding `&mut Collider` from
669    /// a pair iterator isn't possible in stable Rust. To mutate colliders based on
670    /// intersection pairs, iterate here to collect the handles you care about, then
671    /// call [`ColliderSet::get_pair_mut`](crate::geometry::ColliderSet::get_pair_mut)
672    /// on [`self.colliders`](Self#structfield.colliders):
673    ///
674    /// ```
675    /// # use rapier3d::prelude::*;
676    /// # let mut world = PhysicsWorld::default();
677    /// # let collider = ColliderHandle::invalid();
678    /// let pairs: Vec<_> = world
679    ///     .intersection_pairs_with(collider)
680    ///     .map(|(h1, _, h2, _, _)| (h1, h2))
681    ///     .collect();
682    /// for (h1, h2) in pairs {
683    ///     if let (Some(c1), Some(c2)) = world.colliders.get_pair_mut(h1, h2) {
684    ///         // mutate c1 and c2…
685    ///         # let _ = (c1, c2);
686    ///     }
687    /// }
688    /// ```
689    pub fn intersection_pairs_with(
690        &self,
691        collider: ColliderHandle,
692    ) -> impl Iterator<Item = (ColliderHandle, &Collider, ColliderHandle, &Collider, bool)> + '_
693    {
694        self.narrow_phase
695            .intersection_pairs_with(collider)
696            .filter_map(move |(h1, h2, intersecting)| {
697                let c1 = self.colliders.get(h1)?;
698                let c2 = self.colliders.get(h2)?;
699                Some((h1, c1, h2, c2, intersecting))
700            })
701    }
702
703    /// Iterate over all intersection pairs in the world.
704    ///
705    /// Each item is `(handle_a, &collider_a, handle_b, &collider_b, intersecting)`.
706    ///
707    /// # Mutable access
708    ///
709    /// There is no `_mut` variant of this method: the same collider may appear in
710    /// several intersection pairs, so a safe `Iterator` yielding `&mut Collider` from
711    /// a pair iterator isn't possible in stable Rust. To mutate colliders based on
712    /// intersection pairs, iterate here to collect the handles you care about, then
713    /// call [`ColliderSet::get_pair_mut`](crate::geometry::ColliderSet::get_pair_mut)
714    /// on [`self.colliders`](Self#structfield.colliders):
715    ///
716    /// ```
717    /// # use rapier3d::prelude::*;
718    /// # let mut world = PhysicsWorld::default();
719    /// let pairs: Vec<_> = world
720    ///     .intersection_pairs()
721    ///     .map(|(h1, _, h2, _, _)| (h1, h2))
722    ///     .collect();
723    /// for (h1, h2) in pairs {
724    ///     if let (Some(c1), Some(c2)) = world.colliders.get_pair_mut(h1, h2) {
725    ///         // mutate c1 and c2…
726    ///         # let _ = (c1, c2);
727    ///     }
728    /// }
729    /// ```
730    pub fn intersection_pairs(
731        &self,
732    ) -> impl Iterator<Item = (ColliderHandle, &Collider, ColliderHandle, &Collider, bool)> + '_
733    {
734        self.narrow_phase
735            .intersection_pairs()
736            .filter_map(move |(h1, h2, intersecting)| {
737                let c1 = self.colliders.get(h1)?;
738                let c2 = self.colliders.get(h2)?;
739                Some((h1, c1, h2, c2, intersecting))
740            })
741    }
742
743    // ── Iterators over all bodies and colliders ─────────────────────────
744
745    /// Iterate over every rigid body in the world as `(handle, &body)` pairs.
746    pub fn rigid_bodies(&self) -> impl Iterator<Item = (RigidBodyHandle, &RigidBody)> {
747        self.bodies.iter()
748    }
749
750    /// Iterate over every rigid body in the world as `(handle, &mut body)` pairs.
751    pub fn rigid_bodies_mut(&mut self) -> impl Iterator<Item = (RigidBodyHandle, &mut RigidBody)> {
752        self.bodies.iter_mut()
753    }
754
755    /// Iterate over only the currently active (awake) rigid bodies.
756    ///
757    /// Sleeping bodies are skipped, as are bodies that never sleep but aren't part of any
758    /// active island (e.g. unattached fixed bodies). This is the iterator to use when
759    /// rendering or syncing transforms, since transforms of sleeping bodies haven't moved
760    /// since the last step.
761    pub fn active_bodies(&self) -> impl Iterator<Item = (RigidBodyHandle, &RigidBody)> + '_ {
762        self.islands
763            .active_bodies()
764            .filter_map(move |h| Some((h, self.bodies.get(h)?)))
765    }
766
767    /// Iterate over every collider in the world as `(handle, &collider)` pairs.
768    pub fn all_colliders(&self) -> impl Iterator<Item = (ColliderHandle, &Collider)> {
769        self.colliders.iter()
770    }
771
772    /// Iterate over every collider in the world as `(handle, &mut collider)` pairs.
773    pub fn all_colliders_mut(&mut self) -> impl Iterator<Item = (ColliderHandle, &mut Collider)> {
774        self.colliders.iter_mut()
775    }
776
777    // ── Debug rendering ─────────────────────────────────────────────────
778
779    /// Render debug information using the given debug-render pipeline and backend.
780    ///
781    /// This is a convenience method that passes all the required state to the
782    /// debug-render pipeline.
783    #[cfg(feature = "debug-render")]
784    pub fn debug_render(
785        &self,
786        pipeline: &mut DebugRenderPipeline,
787        backend: &mut impl DebugRenderBackend,
788    ) {
789        pipeline.render(
790            backend,
791            &self.bodies,
792            &self.colliders,
793            &self.impulse_joints,
794            &self.multibody_joints,
795            &self.narrow_phase,
796        );
797    }
798}
799
800#[cfg(all(feature = "parallel", not(feature = "unsync-callbacks")))]
801impl PhysicsWorld {
802    /// Configures a dedicated thread pool for this physics world’s parallel work.
803    ///
804    /// If `num_threads` is `0`, then the new threadpool will use rayon’s default
805    /// number of threads.
806    ///
807    /// If no threadpool is configured, the global rayon thread pool, or the threadpool
808    /// setup with `ThreadPool::install`, is used.
809    pub fn configure_thread_pool(
810        &mut self,
811        num_threads: usize,
812    ) -> Result<(), rayon::ThreadPoolBuildError> {
813        self.physics_pipeline.configure_thread_pool(num_threads)
814    }
815
816    /// The thread-pool used by this physics world, if it was configured.
817    pub fn thread_pool(&self) -> Option<std::sync::Arc<rayon::ThreadPool>> {
818        self.physics_pipeline.thread_pool()
819    }
820
821    /// Sets (or clears) the thread pool running this physics world’s parallel work.
822    ///
823    /// Unlike [`Self::configure_thread_pool`], this takes an existing pool.
824    pub fn set_thread_pool(&mut self, pool: Option<std::sync::Arc<rayon::ThreadPool>>) {
825        self.physics_pipeline.set_thread_pool(pool)
826    }
827
828    /// Removes the dedicated thread pool: the parallel parts of the step run on whichever
829    /// pool the calling thread is in again.
830    pub fn clear_thread_pool(&mut self) {
831        self.physics_pipeline.clear_thread_pool()
832    }
833
834    /// The number of workers this physics world’s parallel work runs on: the size of its
835    /// dedicated thread pool if one was configured.
836    pub fn num_threads(&self) -> Option<usize> {
837        self.physics_pipeline.num_threads()
838    }
839}