Skip to main content

rapier2d/geometry/
collider_set.rs

1use crate::alloc_prelude::*;
2use crate::data::arena::Arena;
3use crate::data::{HasModifiedFlag, ModifiedObjects};
4use crate::dynamics::{
5    IslandManager, RigidBodyHandle, RigidBodySet, SoftBindingError, SoftBodySet, SoftMeshBinding,
6};
7use crate::geometry::{Collider, ColliderChanges, ColliderHandle, ColliderParent};
8use crate::math::{DIM, Pose, Vector};
9use core::ops::{Index, IndexMut};
10
11/// A set of modified colliders
12pub type ModifiedColliders = ModifiedObjects<ColliderHandle, Collider>;
13
14impl HasModifiedFlag for Collider {
15    #[inline]
16    fn has_modified_flag(&self) -> bool {
17        self.changes.contains(ColliderChanges::IN_MODIFIED_SET)
18    }
19
20    #[inline]
21    fn set_modified_flag(&mut self) {
22        self.changes |= ColliderChanges::IN_MODIFIED_SET;
23    }
24}
25
26#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
27#[derive(Clone, Default, Debug)]
28/// The collection that stores all colliders (collision shapes) in your physics world.
29///
30/// Similar to [`RigidBodySet`](crate::dynamics::RigidBodySet), this is the "database" where
31/// all your collision shapes live. Each collider can be attached to a rigid body or exist
32/// independently.
33///
34/// # Example
35/// ```
36/// # use rapier3d::prelude::*;
37/// let mut colliders = ColliderSet::new();
38/// # let mut bodies = RigidBodySet::new();
39/// # let body_handle = bodies.insert(RigidBodyBuilder::dynamic());
40///
41/// // Add a standalone collider (no parent body)
42/// let handle = colliders.insert(ColliderBuilder::ball(0.5));
43///
44/// // Or attach it to a body
45/// let handle = colliders.insert_with_parent(
46///     ColliderBuilder::cuboid(1.0, 1.0, 1.0),
47///     body_handle,
48///     &mut bodies
49/// );
50/// ```
51pub struct ColliderSet {
52    pub(crate) colliders: Arena<Collider>,
53    pub(crate) modified_colliders: ModifiedColliders,
54    pub(crate) removed_colliders: Vec<ColliderHandle>,
55}
56
57impl ColliderSet {
58    /// Creates a new empty collection of colliders.
59    pub fn new() -> Self {
60        ColliderSet {
61            colliders: Arena::new(),
62            modified_colliders: Default::default(),
63            removed_colliders: Vec::new(),
64        }
65    }
66
67    /// Creates a new collection with pre-allocated space for the given number of colliders.
68    ///
69    /// Use this if you know approximately how many colliders you'll need.
70    pub fn with_capacity(capacity: usize) -> Self {
71        ColliderSet {
72            colliders: Arena::with_capacity(capacity),
73            modified_colliders: ModifiedColliders::with_capacity(capacity),
74            removed_colliders: Vec::new(),
75        }
76    }
77
78    /// Fetch the set of colliders modified since the last call to
79    /// `take_modified`
80    ///
81    /// Provides a value that can be passed to the `modified_colliders` argument
82    /// of [`BroadPhaseBvh::update`](crate::geometry::BroadPhaseBvh::update).
83    ///
84    /// Should not be used if this [`ColliderSet`] will be used with a
85    /// [`PhysicsPipeline`](crate::pipeline::PhysicsPipeline), which handles
86    /// broadphase updates automatically.
87    pub fn take_modified(&mut self) -> ModifiedColliders {
88        core::mem::take(&mut self.modified_colliders)
89    }
90
91    pub(crate) fn set_modified(&mut self, modified: ModifiedColliders) {
92        self.modified_colliders = modified;
93    }
94
95    /// Fetch the set of colliders removed since the last call to `take_removed`
96    ///
97    /// Provides a value that can be passed to the `removed_colliders` argument
98    /// of [`BroadPhaseBvh::update`](crate::geometry::BroadPhaseBvh::update).
99    ///
100    /// Should not be used if this [`ColliderSet`] will be used with a
101    /// [`PhysicsPipeline`](crate::pipeline::PhysicsPipeline), which handles
102    /// broadphase updates automatically.
103    pub fn take_removed(&mut self) -> Vec<ColliderHandle> {
104        core::mem::take(&mut self.removed_colliders)
105    }
106
107    /// Returns a handle that's guaranteed to be invalid.
108    ///
109    /// Useful as a sentinel/placeholder value.
110    pub fn invalid_handle() -> ColliderHandle {
111        ColliderHandle::from_raw_parts(crate::INVALID_U32, crate::INVALID_U32)
112    }
113
114    /// Iterates over all colliders in this collection.
115    ///
116    /// Yields `(handle, &Collider)` pairs for each collider (including disabled ones).
117    pub fn iter(&self) -> impl ExactSizeIterator<Item = (ColliderHandle, &Collider)> {
118        self.colliders.iter().map(|(h, c)| (ColliderHandle(h), c))
119    }
120
121    /// Iterates over only the enabled colliders.
122    ///
123    /// Disabled colliders are excluded from physics simulation and queries.
124    pub fn iter_enabled(&self) -> impl Iterator<Item = (ColliderHandle, &Collider)> {
125        self.colliders
126            .iter()
127            .map(|(h, c)| (ColliderHandle(h), c))
128            .filter(|(_, c)| c.is_enabled())
129    }
130
131    /// Iterates over all colliders with mutable access.
132    #[cfg(not(feature = "dev-remove-slow-accessors"))]
133    pub fn iter_mut(&mut self) -> impl Iterator<Item = (ColliderHandle, &mut Collider)> {
134        self.modified_colliders.clear();
135        let modified_colliders = &mut self.modified_colliders;
136        self.colliders.iter_mut().map(move |(h, co)| {
137            // NOTE: we push unchecked here since we are just re-populating the
138            //       `modified_colliders` set that we just cleared before iteration.
139            modified_colliders.push_unchecked(ColliderHandle(h), co);
140            (ColliderHandle(h), co)
141        })
142    }
143
144    /// Iterates over only the enabled colliders with mutable access.
145    #[cfg(not(feature = "dev-remove-slow-accessors"))]
146    pub fn iter_enabled_mut(&mut self) -> impl Iterator<Item = (ColliderHandle, &mut Collider)> {
147        self.iter_mut().filter(|(_, c)| c.is_enabled())
148    }
149
150    /// Returns how many colliders are currently in this collection.
151    pub fn len(&self) -> usize {
152        self.colliders.len()
153    }
154
155    /// Returns `true` if there are no colliders in this collection.
156    pub fn is_empty(&self) -> bool {
157        self.colliders.is_empty()
158    }
159
160    /// Checks if the given handle points to a valid collider that still exists.
161    pub fn contains(&self, handle: ColliderHandle) -> bool {
162        self.colliders.contains(handle.0)
163    }
164
165    /// Adds a standalone collider (not attached to any body) and returns its handle.
166    ///
167    /// Most colliders should be attached to rigid bodies using [`insert_with_parent()`](Self::insert_with_parent) instead.
168    /// Standalone colliders are useful for sensors or static collision geometry that doesn't need a body.
169    pub fn insert(&mut self, coll: impl Into<Collider>) -> ColliderHandle {
170        let mut coll = coll.into();
171        // Make sure the internal links are reset, they may not be
172        // if this rigid-body was obtained by cloning another one.
173        coll.reset_internal_references();
174        coll.parent = None;
175        let handle = ColliderHandle(self.colliders.insert(coll));
176        // NOTE: we push unchecked because this is a brand-new collider
177        //       so it was initialized with the changed flag but isn’t in
178        //       the set yet.
179        self.modified_colliders
180            .push_unchecked(handle, &mut self.colliders[handle.0]);
181        handle
182    }
183
184    /// Adds a collider attached to a rigid body and returns its handle.
185    ///
186    /// This is the most common way to add colliders. The collider's position is relative
187    /// to its parent body, so when the body moves, the collider moves with it.
188    ///
189    /// # Example
190    /// ```
191    /// # use rapier3d::prelude::*;
192    /// # let mut colliders = ColliderSet::new();
193    /// # let mut bodies = RigidBodySet::new();
194    /// # let body_handle = bodies.insert(RigidBodyBuilder::dynamic());
195    /// // Create a ball collider attached to a dynamic body
196    /// let collider_handle = colliders.insert_with_parent(
197    ///     ColliderBuilder::ball(0.5),
198    ///     body_handle,
199    ///     &mut bodies
200    /// );
201    /// ```
202    pub fn insert_with_parent(
203        &mut self,
204        coll: impl Into<Collider>,
205        parent_handle: RigidBodyHandle,
206        bodies: &mut RigidBodySet,
207    ) -> ColliderHandle {
208        let mut coll = coll.into();
209        // Make sure the internal links are reset, they may not be
210        // if this collider was obtained by cloning another one.
211        coll.reset_internal_references();
212
213        if let Some(prev_parent) = &mut coll.parent {
214            prev_parent.handle = parent_handle;
215        } else {
216            coll.parent = Some(ColliderParent {
217                handle: parent_handle,
218                pos_wrt_parent: coll.pos.0,
219            });
220        }
221
222        // NOTE: we use `get_mut` instead of `get_mut_internal` so that the
223        // modification flag is updated properly.
224        let parent = bodies
225            .get_mut_internal_with_modification_tracking(parent_handle)
226            .expect("Parent rigid body not found.");
227        let handle = ColliderHandle(self.colliders.insert(coll));
228        let coll = self.colliders.get_mut(handle.0).unwrap();
229        // NOTE: we push unchecked because this is a brand-new collider
230        //       so it was initialized with the changed flag but isn’t in
231        //       the set yet.
232        self.modified_colliders.push_unchecked(handle, coll);
233
234        parent.add_collider_internal(
235            handle,
236            coll.parent.as_mut().unwrap(),
237            &mut coll.pos,
238            &coll.shape,
239            &coll.mprops,
240        );
241        handle
242    }
243
244    /// Inserts a collider holding a soft body's deformable collision mesh: a polyline (2D) or
245    /// triangle mesh (3D) whose vertices follow the cluster proxy `parent_handle` via `binding`
246    /// ([`crate::dynamics::SoftBodyCluster::proxy`]); a failed call leaves both sets untouched.
247    pub fn insert_deformable(
248        &mut self,
249        coll: impl Into<Collider>,
250        binding: SoftMeshBinding,
251        parent_handle: RigidBodyHandle,
252        bodies: &mut RigidBodySet,
253        soft_bodies: &mut SoftBodySet,
254    ) -> Result<ColliderHandle, SoftBindingError> {
255        let mut coll = coll.into();
256        // The parent must be a live cluster proxy: it says which body and cluster the mesh
257        // joins, and its pose places the collider like any attached one.
258        let (body_handle, cluster_id, frame) = {
259            let rb = bodies
260                .get(parent_handle)
261                .ok_or(SoftBindingError::NotAClusterProxy)?;
262            let body = rb.soft_body().ok_or(SoftBindingError::NotAClusterProxy)?;
263            (body, rb.soft_cluster, rb.pos.position)
264        };
265        let sb = soft_bodies
266            .get_mut(body_handle)
267            .ok_or(SoftBindingError::NotAClusterProxy)?;
268        if sb
269            .cluster(cluster_id)
270            .is_none_or(|cluster| cluster.proxy() != parent_handle)
271        {
272            return Err(SoftBindingError::NotAClusterProxy);
273        }
274
275        let (vertices, indices) = mesh_geometry(coll.shape())?;
276        // The shape is kept as given, in the collider's own frame: the binding reads its
277        // vertices where the collider's pose relative to the proxy puts them.
278        let pos_wrt_parent = coll
279            .parent
280            .as_ref()
281            .map_or(coll.pos.0, |parent| parent.pos_wrt_parent);
282        let pose = frame * pos_wrt_parent;
283        let vertices: Vec<Vector> = vertices.iter().map(|v| pose * *v).collect();
284        let mut mesh = sb.bind_mesh(cluster_id, &binding, vertices, indices)?;
285        // The shape's `ORIENTED` flag says whether the closed mesh encloses solid matter.
286        #[cfg(feature = "dim2")]
287        let oriented = coll.shape().as_polyline().is_some_and(|polyline| {
288            polyline
289                .flags()
290                .contains(parry::shape::PolylineFlags::ORIENTED)
291        });
292        #[cfg(feature = "dim3")]
293        let oriented = coll.shape().as_trimesh().is_some_and(|trimesh| {
294            trimesh
295                .flags()
296                .contains(parry::shape::TriMeshFlags::ORIENTED)
297        });
298        mesh.oriented = oriented;
299        coll.set_density(0.0);
300        // The contact skin is the thickness of the mesh's vertices in the soft contact
301        // passes: a collider left without one gets the body's particle radius.
302        if coll.contact_skin() <= 0.0 {
303            coll.set_contact_skin(sb.particle_radius());
304        }
305
306        let id = sb.push_mesh(cluster_id, mesh);
307        let handle = self.insert_with_parent(coll, parent_handle, bodies);
308        let co = self.index_mut_internal(handle);
309        co.deformable_mesh_ref = Some(crate::dynamics::SoftMeshRef {
310            body: body_handle,
311            id,
312        });
313        soft_bodies[body_handle].set_mesh_collider(id, handle);
314        Ok(handle)
315    }
316
317    /// Changes which rigid body a collider is attached to, or detaches it completely.
318    ///
319    /// Use this to move a collider from one body to another, or to make it standalone.
320    ///
321    /// # Parameters
322    /// * `new_parent_handle` - `Some(handle)` to attach to a body, `None` to make standalone
323    ///
324    /// # Example
325    /// ```
326    /// # use rapier3d::prelude::*;
327    /// # let mut colliders = ColliderSet::new();
328    /// # let mut bodies = RigidBodySet::new();
329    /// # let body_handle = bodies.insert(RigidBodyBuilder::dynamic());
330    /// # let other_body = bodies.insert(RigidBodyBuilder::dynamic());
331    /// # let collider_handle = colliders.insert_with_parent(ColliderBuilder::ball(0.5).build(), body_handle, &mut bodies);
332    /// // Detach collider from its current body
333    /// colliders.set_parent(collider_handle, None, &mut bodies);
334    ///
335    /// // Attach it to a different body
336    /// colliders.set_parent(collider_handle, Some(other_body), &mut bodies);
337    /// ```
338    pub fn set_parent(
339        &mut self,
340        handle: ColliderHandle,
341        new_parent_handle: Option<RigidBodyHandle>,
342        bodies: &mut RigidBodySet,
343    ) {
344        if let Some(collider) = self.get_mut(handle) {
345            let curr_parent = collider.parent.map(|p| p.handle);
346            if new_parent_handle == curr_parent {
347                return; // Nothing to do, this is the same parent.
348            }
349
350            collider.changes |= ColliderChanges::PARENT;
351
352            if let Some(parent_handle) = curr_parent {
353                if let Some(rb) = bodies.get_mut(parent_handle) {
354                    rb.remove_collider_internal(handle);
355                }
356            }
357
358            match new_parent_handle {
359                Some(new_parent_handle) => {
360                    if let Some(parent) = &mut collider.parent {
361                        parent.handle = new_parent_handle;
362                    } else {
363                        collider.parent = Some(ColliderParent {
364                            handle: new_parent_handle,
365                            pos_wrt_parent: Pose::IDENTITY,
366                        })
367                    };
368
369                    if let Some(rb) = bodies.get_mut(new_parent_handle) {
370                        rb.add_collider_internal(
371                            handle,
372                            collider.parent.as_ref().unwrap(),
373                            &mut collider.pos,
374                            &collider.shape,
375                            &collider.mprops,
376                        );
377                    }
378                }
379                None => collider.parent = None,
380            }
381        }
382    }
383
384    /// Removes a collider from the world.
385    ///
386    /// The collider is detached from its parent body (if any) and removed from all
387    /// collision detection structures. Returns the removed collider if it existed.
388    ///
389    /// # Parameters
390    /// * `wake_up` - If `true`, wakes up the parent body (useful when collider removal
391    ///   changes the body's mass or collision behavior significantly)
392    ///
393    /// # Example
394    /// ```
395    /// # use rapier3d::prelude::*;
396    /// # let mut colliders = ColliderSet::new();
397    /// # let mut bodies = RigidBodySet::new();
398    /// # let mut islands = IslandManager::new();
399    /// # let body_handle = bodies.insert(RigidBodyBuilder::dynamic().build());
400    /// # let mut soft_bodies = SoftBodySet::new();
401    /// # let handle = colliders.insert_with_parent(ColliderBuilder::ball(0.5).build(), body_handle, &mut bodies);
402    /// if let Some(collider) = colliders.remove(
403    ///     handle,
404    ///     &mut islands,
405    ///     &mut bodies,
406    ///     &mut soft_bodies,
407    ///     true  // Wake up the parent body
408    /// ) {
409    ///     println!("Removed collider with shape: {:?}", collider.shared_shape());
410    /// }
411    /// ```
412    pub fn remove(
413        &mut self,
414        handle: ColliderHandle,
415        islands: &mut IslandManager,
416        bodies: &mut RigidBodySet,
417        soft_bodies: &mut SoftBodySet,
418        wake_up: bool,
419    ) -> Option<Collider> {
420        let collider = self.remove_internal(handle, islands, bodies, wake_up)?;
421        // A removed deformable collider takes its mesh with it: the soft body keeps simulating,
422        // without that mesh's collisions.
423        if let Some(mesh) = collider.deformable_mesh_ref() {
424            if let Some(sb) = soft_bodies.get_mut(mesh.body) {
425                sb.remove_mesh(mesh.id);
426            }
427        }
428        Some(collider)
429    }
430
431    /// Like [`Self::remove`], for internal callers that maintain the soft-body back-references
432    /// themselves.
433    pub(crate) fn remove_internal(
434        &mut self,
435        handle: ColliderHandle,
436        islands: &mut IslandManager,
437        bodies: &mut RigidBodySet,
438        wake_up: bool,
439    ) -> Option<Collider> {
440        let collider = self.colliders.remove(handle.0)?;
441
442        /*
443         * Delete the collider from its parent body.
444         */
445        // NOTE: we use `get_mut_internal_with_modification_tracking` instead of `get_mut_internal` so that the
446        // modification flag is updated properly.
447        if let Some(parent) = &collider.parent {
448            if let Some(parent_rb) =
449                bodies.get_mut_internal_with_modification_tracking(parent.handle)
450            {
451                parent_rb.remove_collider_internal(handle);
452
453                if wake_up {
454                    islands.wake_up(bodies, parent.handle, true);
455                }
456            }
457        }
458
459        /*
460         * Publish removal.
461         */
462        self.removed_colliders.push(handle);
463
464        Some(collider)
465    }
466
467    /// Gets a collider by its index without knowing the generation number.
468    ///
469    /// ⚠️ **Advanced/unsafe usage** - prefer [`get()`](Self::get) instead! See [`RigidBodySet::get_unknown_gen`] for details.
470    pub fn get_unknown_gen(&self, i: u32) -> Option<(&Collider, ColliderHandle)> {
471        self.colliders
472            .get_unknown_gen(i)
473            .map(|(c, h)| (c, ColliderHandle(h)))
474    }
475
476    /// Gets a mutable reference to a collider by its index without knowing the generation.
477    ///
478    /// ⚠️ **Advanced/unsafe usage** - prefer [`get_mut()`](Self::get_mut) instead!
479    /// suffer form the ABA problem.
480    #[cfg(not(feature = "dev-remove-slow-accessors"))]
481    pub fn get_unknown_gen_mut(&mut self, i: u32) -> Option<(&mut Collider, ColliderHandle)> {
482        let (collider, handle) = self.colliders.get_unknown_gen_mut(i)?;
483        let handle = ColliderHandle(handle);
484        self.modified_colliders.push_once(handle, collider);
485        Some((collider, handle))
486    }
487
488    /// Gets a read-only reference to the collider with the given handle.
489    ///
490    /// Returns `None` if the handle is invalid or the collider was removed.
491    pub fn get(&self, handle: ColliderHandle) -> Option<&Collider> {
492        self.colliders.get(handle.0)
493    }
494
495    /// Gets a mutable reference to the collider with the given handle.
496    ///
497    /// Returns `None` if the handle is invalid or the collider was removed.
498    /// Use this to modify collider properties like friction, restitution, sensor status, etc.
499    #[cfg(not(feature = "dev-remove-slow-accessors"))]
500    pub fn get_mut(&mut self, handle: ColliderHandle) -> Option<&mut Collider> {
501        let result = self.colliders.get_mut(handle.0)?;
502        self.modified_colliders.push_once(handle, result);
503        Some(result)
504    }
505
506    /// Gets mutable references to two different colliders at once.
507    ///
508    /// Useful when you need to modify two colliders simultaneously. If both handles
509    /// are the same, only the first value will be `Some`.
510    #[cfg(not(feature = "dev-remove-slow-accessors"))]
511    pub fn get_pair_mut(
512        &mut self,
513        handle1: ColliderHandle,
514        handle2: ColliderHandle,
515    ) -> (Option<&mut Collider>, Option<&mut Collider>) {
516        if handle1 == handle2 {
517            (self.get_mut(handle1), None)
518        } else {
519            let (mut co1, mut co2) = self.colliders.get2_mut(handle1.0, handle2.0);
520            if let Some(co1) = co1.as_deref_mut() {
521                self.modified_colliders.push_once(handle1, co1);
522            }
523            if let Some(co2) = co2.as_deref_mut() {
524                self.modified_colliders.push_once(handle2, co2);
525            }
526            (co1, co2)
527        }
528    }
529
530    pub(crate) fn index_mut_internal(&mut self, handle: ColliderHandle) -> &mut Collider {
531        &mut self.colliders[handle.0]
532    }
533
534    pub(crate) fn get_mut_internal(&mut self, handle: ColliderHandle) -> Option<&mut Collider> {
535        self.colliders.get_mut(handle.0)
536    }
537
538    // Just a very long name instead of `.get_mut` to make sure
539    // this is really the method we wanted to use instead of `get_mut_internal`.
540    #[allow(dead_code)]
541    pub(crate) fn get_mut_internal_with_modification_tracking(
542        &mut self,
543        handle: ColliderHandle,
544    ) -> Option<&mut Collider> {
545        let result = self.colliders.get_mut(handle.0)?;
546        self.modified_colliders.push_once(handle, result);
547        Some(result)
548    }
549}
550
551impl Index<crate::data::Index> for ColliderSet {
552    type Output = Collider;
553
554    fn index(&self, index: crate::data::Index) -> &Collider {
555        &self.colliders[index]
556    }
557}
558
559impl Index<ColliderHandle> for ColliderSet {
560    type Output = Collider;
561
562    fn index(&self, index: ColliderHandle) -> &Collider {
563        &self.colliders[index.0]
564    }
565}
566
567#[cfg(not(feature = "dev-remove-slow-accessors"))]
568impl IndexMut<ColliderHandle> for ColliderSet {
569    fn index_mut(&mut self, handle: ColliderHandle) -> &mut Collider {
570        let collider = &mut self.colliders[handle.0];
571        self.modified_colliders.push_once(handle, collider);
572        collider
573    }
574}
575
576/// The vertices (world space) and elements of a deformable collider's shape.
577fn mesh_geometry(
578    shape: &dyn crate::geometry::Shape,
579) -> Result<(Vec<Vector>, Vec<[u32; DIM]>), SoftBindingError> {
580    #[cfg(feature = "dim2")]
581    let (deformable, geometry) = {
582        use parry::shape::PolylineFlags;
583        let polyline = shape
584            .as_polyline()
585            .ok_or(SoftBindingError::UnsupportedShape)?;
586        (
587            polyline.flags().contains(PolylineFlags::DEFORMABLE),
588            (polyline.vertices().to_vec(), polyline.indices().to_vec()),
589        )
590    };
591    #[cfg(feature = "dim3")]
592    let (deformable, geometry) = {
593        use parry::shape::TriMeshFlags;
594        let trimesh = shape
595            .as_trimesh()
596            .ok_or(SoftBindingError::UnsupportedShape)?;
597        (
598            trimesh.flags().contains(TriMeshFlags::DEFORMABLE),
599            (trimesh.vertices().to_vec(), trimesh.indices().to_vec()),
600        )
601    };
602    if !deformable {
603        return Err(SoftBindingError::NotDeformable);
604    }
605    let (vertices, indices) = geometry;
606    if vertices.is_empty() || indices.is_empty() {
607        return Err(SoftBindingError::DegenerateMesh);
608    }
609    Ok((vertices, indices))
610}