Skip to main content

rapier2d/geometry/
collider.rs

1use crate::alloc_prelude::*;
2use crate::dynamics::{CoefficientCombineRule, MassProperties, RigidBodyHandle, RigidBodySet};
3#[cfg(feature = "dim3")]
4use crate::geometry::HeightFieldFlags;
5use crate::geometry::{
6    ActiveCollisionTypes, ColliderChanges, ColliderFlags, ColliderMassProps, ColliderMaterial,
7    ColliderParent, ColliderPosition, ColliderShape, ColliderType, InteractionGroups,
8    MeshConverter, MeshConverterError, SharedShape,
9};
10use crate::math::{AngVector, DIM, IVector, Pose, Real, Rotation, Vector, rotation_from_angle};
11use crate::parry::transformation::vhacd::VHACDParameters;
12use crate::pipeline::{ActiveEvents, ActiveHooks};
13use crate::prelude::{ColliderEnabled, IntegrationParameters};
14use na::Unit;
15use parry::bounding_volume::{Aabb, BoundingVolume};
16use parry::shape::{Shape, TriMeshBuilderError, TriMeshFlags};
17use parry::transformation::voxelization::FillMode;
18#[cfg(feature = "dim3")]
19use parry::utils::Array2;
20
21#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
22#[derive(Clone, Debug)]
23/// The collision shape attached to a rigid body that defines what it can collide with.
24///
25/// Think of a collider as the "hitbox" or "collision shape" for your physics object. While a
26/// [`RigidBody`](crate::dynamics::RigidBody) handles the physics (mass, velocity, forces),
27/// the collider defines what shape the object has for collision detection.
28///
29/// ## Key concepts
30///
31/// - **Shape**: The geometric form (box, sphere, capsule, mesh, etc.)
32/// - **Material**: Physical properties like friction (slipperiness) and restitution (bounciness)
33/// - **Sensor vs. Solid**: Sensors detect overlaps but don't create physical collisions
34/// - **Mass properties**: Automatically computed from the shape's volume and density
35///
36/// ## Creating colliders
37///
38/// Always use [`ColliderBuilder`] to create colliders:
39///
40/// ```ignore
41/// let collider = ColliderBuilder::cuboid(1.0, 0.5, 1.0)  // 2x1x2 box
42///     .friction(0.7)
43///     .restitution(0.3);
44/// colliders.insert_with_parent(collider, body_handle, &mut bodies);
45/// ```
46///
47/// ## Attaching to bodies
48///
49/// Colliders are usually attached to rigid bodies. One body can have multiple colliders
50/// to create compound shapes (like a character with separate colliders for head, torso, limbs).
51pub struct Collider {
52    pub(crate) coll_type: ColliderType,
53    pub(crate) shape: ColliderShape,
54    pub(crate) mprops: ColliderMassProps,
55    pub(crate) changes: ColliderChanges,
56    pub(crate) parent: Option<ColliderParent>,
57    pub(crate) pos: ColliderPosition,
58    pub(crate) material: ColliderMaterial,
59    pub(crate) flags: ColliderFlags,
60    contact_skin: Real,
61    contact_force_event_threshold: Real,
62    /// Reference to the soft body associated to this collider's shape if it is deformable; the soft
63    /// body keeps its mesh (automatic skinning, tearing) and syncs it into the shape each frame.
64    pub(crate) deformable_mesh_ref: Option<crate::dynamics::SoftMeshRef>,
65    /// User-defined data associated to this collider.
66    pub user_data: u128,
67}
68
69impl Collider {
70    pub(crate) fn reset_internal_references(&mut self) {
71        self.changes = ColliderChanges::all();
72        self.deformable_mesh_ref = None;
73    }
74
75    /// The soft-body collision mesh this collider is associated to, if any.
76    pub fn deformable_mesh_ref(&self) -> Option<crate::dynamics::SoftMeshRef> {
77        self.deformable_mesh_ref
78    }
79
80    /// Whether this collider is associated to a soft body's deformable collision mesh.
81    pub(crate) fn is_deformable_collider(&self) -> bool {
82        self.deformable_mesh_ref.is_some()
83    }
84
85    /// The soft-body motion margin padding this collider's broad-phase AABB and contact
86    /// prediction: its parent proxy's for a deformable collider, zero for a rigid one.
87    pub(crate) fn soft_motion_margin(&self, parent: &crate::dynamics::RigidBody) -> Real {
88        if self.is_deformable_collider() {
89            parent.soft_motion_margin
90        } else {
91            0.0
92        }
93    }
94
95    /// Deforms the shape of a soft-body surface collider in place through `f` without
96    /// invalidating the narrow-phase pair workspaces (its topology and identity are unchanged).
97    pub(crate) fn deform_shape(&mut self, f: impl FnOnce(&mut dyn Shape)) {
98        f(self.shape.make_mut());
99        self.changes.insert(ColliderChanges::DEFORMED);
100    }
101
102    /// Replaces the shape of a soft-body surface collider whose topology changed (a tear), as a
103    /// deformation: no narrow-phase pair workspace invalidation, no wake-up.
104    pub(crate) fn replace_deformed_shape(&mut self, shape: SharedShape) {
105        self.shape = shape;
106        self.changes.insert(ColliderChanges::DEFORMED);
107    }
108
109    /// Moves a soft-body surface collider along with its cluster proxy (`frame` is the proxy's
110    /// pose, the collider keeps its pose relative to it), as a deformation: no wake-up, no
111    /// workspace invalidation. The shape's vertices are expressed in the resulting frame.
112    pub(crate) fn deform_pose(&mut self, frame: Pose) {
113        let pos_wrt_parent = self
114            .parent
115            .as_ref()
116            .map_or(Pose::IDENTITY, |parent| parent.pos_wrt_parent);
117        self.pos = ColliderPosition(frame * pos_wrt_parent);
118        self.changes.insert(ColliderChanges::DEFORMED);
119    }
120
121    pub(crate) fn effective_contact_force_event_threshold(&self) -> Real {
122        if self
123            .flags
124            .active_events
125            .contains(ActiveEvents::CONTACT_FORCE_EVENTS)
126        {
127            self.contact_force_event_threshold
128        } else {
129            Real::MAX
130        }
131    }
132
133    /// The rigid body this collider is attached to, if any.
134    ///
135    /// Returns `None` for standalone colliders (not attached to any body).
136    pub fn parent(&self) -> Option<RigidBodyHandle> {
137        self.parent.map(|parent| parent.handle)
138    }
139
140    /// Checks if this collider is a sensor (detects overlaps without physical collision).
141    ///
142    /// Sensors are like "trigger zones" - they detect when other colliders enter/exit them
143    /// but don't create physical contact forces. Use for:
144    /// - Trigger zones (checkpoint areas, damage regions)
145    /// - Proximity detection
146    /// - Collectible items
147    /// - Area-of-effect detection
148    pub fn is_sensor(&self) -> bool {
149        self.coll_type.is_sensor()
150    }
151
152    /// Copy all the characteristics from `other` to `self`.
153    ///
154    /// If you have a mutable reference to a collider `collider: &mut Collider`, attempting to
155    /// assign it a whole new collider instance, e.g., `*collider = ColliderBuilder::ball(0.5).build()`,
156    /// will crash due to some internal indices being overwritten. Instead, use
157    /// `collider.copy_from(&ColliderBuilder::ball(0.5).build())`.
158    ///
159    /// This method will allow you to set most characteristics of this collider from another
160    /// collider instance without causing any breakage.
161    ///
162    /// This method **cannot** be used for reparenting a collider. Therefore, the parent of the
163    /// `other` (if any), as well as its relative position to that parent will not be copied into
164    /// `self`.
165    ///
166    /// The pose of `other` will only copied into `self` if `self` doesn’t have a parent (if it has
167    /// a parent, its position is directly controlled by the parent rigid-body).
168    pub fn copy_from(&mut self, other: &Collider) {
169        // NOTE: we deconstruct the collider struct to be sure we don’t forget to
170        //       add some copies here if we add more field to Collider in the future.
171        let Collider {
172            coll_type,
173            shape,
174            mprops,
175            changes: _changes, // Will be set to ALL.
176            parent: _parent,   // This function cannot be used to reparent the collider.
177            pos,
178            material,
179            flags,
180            contact_force_event_threshold,
181            user_data,
182            contact_skin,
183            deformable_mesh_ref: _soft_mesh, // The soft-body markers belong to the collider's identity.
184        } = other;
185
186        if self.parent.is_none() {
187            self.pos = *pos;
188        }
189
190        self.coll_type = *coll_type;
191        self.shape = shape.clone();
192        self.mprops = mprops.clone();
193        self.material = *material;
194        self.contact_force_event_threshold = *contact_force_event_threshold;
195        self.user_data = *user_data;
196        self.flags = *flags;
197        self.changes = ColliderChanges::all();
198        self.contact_skin = *contact_skin;
199    }
200
201    /// Which physics hooks are enabled for this collider.
202    ///
203    /// Hooks allow custom filtering and modification of collisions. See [`PhysicsHooks`](crate::pipeline::PhysicsHooks).
204    pub fn active_hooks(&self) -> ActiveHooks {
205        self.flags.active_hooks
206    }
207
208    /// Enables/disables physics hooks for this collider.
209    ///
210    /// Use to opt colliders into custom collision filtering logic.
211    pub fn set_active_hooks(&mut self, active_hooks: ActiveHooks) {
212        self.flags.active_hooks = active_hooks;
213    }
214
215    /// Which events are enabled for this collider.
216    ///
217    /// Controls whether you receive collision/contact force events. See [`ActiveEvents`](crate::pipeline::ActiveEvents).
218    pub fn active_events(&self) -> ActiveEvents {
219        self.flags.active_events
220    }
221
222    /// Enables/disables event generation for this collider.
223    ///
224    /// Set to `ActiveEvents::COLLISION_EVENTS` to receive started/stopped collision notifications.
225    /// Set to `ActiveEvents::CONTACT_FORCE_EVENTS` to receive force threshold events.
226    pub fn set_active_events(&mut self, active_events: ActiveEvents) {
227        self.flags.active_events = active_events;
228    }
229
230    /// The collision types enabled for this collider.
231    pub fn active_collision_types(&self) -> ActiveCollisionTypes {
232        self.flags.active_collision_types
233    }
234
235    /// Sets the collision types enabled for this collider.
236    pub fn set_active_collision_types(&mut self, active_collision_types: ActiveCollisionTypes) {
237        self.flags.active_collision_types = active_collision_types;
238    }
239
240    /// The contact skin of this collider.
241    ///
242    /// See the documentation of [`ColliderBuilder::contact_skin`] for details.
243    pub fn contact_skin(&self) -> Real {
244        self.contact_skin
245    }
246
247    /// Sets the contact skin of this collider.
248    ///
249    /// See the documentation of [`ColliderBuilder::contact_skin`] for details.
250    pub fn set_contact_skin(&mut self, skin_thickness: Real) {
251        self.contact_skin = skin_thickness;
252    }
253
254    /// The friction coefficient of this collider (how "slippery" it is).
255    ///
256    /// - `0.0` = perfectly slippery (ice)
257    /// - `1.0` = high friction (rubber on concrete)
258    /// - Typical values: 0.3-0.8
259    pub fn friction(&self) -> Real {
260        self.material.friction
261    }
262
263    /// Sets the friction coefficient (slipperiness).
264    ///
265    /// Controls how much this surface resists sliding. Higher values = more grip.
266    /// Works with other collider's friction via the combine rule.
267    pub fn set_friction(&mut self, coefficient: Real) {
268        self.material.friction = coefficient
269    }
270
271    /// The combine rule used by this collider to combine its friction
272    /// coefficient with the friction coefficient of the other collider it
273    /// is in contact with.
274    pub fn friction_combine_rule(&self) -> CoefficientCombineRule {
275        self.material.friction_combine_rule
276    }
277
278    /// Sets the combine rule used by this collider to combine its friction
279    /// coefficient with the friction coefficient of the other collider it
280    /// is in contact with.
281    pub fn set_friction_combine_rule(&mut self, rule: CoefficientCombineRule) {
282        self.material.friction_combine_rule = rule;
283    }
284
285    /// The restitution coefficient of this collider (how "bouncy" it is).
286    ///
287    /// - `0.0` = no bounce (clay, soft material)
288    /// - `1.0` = perfect bounce (ideal elastic collision)
289    /// - `>1.0` = super bouncy (gains energy, unrealistic but fun!)
290    /// - Typical values: 0.0-0.8
291    pub fn restitution(&self) -> Real {
292        self.material.restitution
293    }
294
295    /// Sets the restitution coefficient (bounciness).
296    ///
297    /// Controls how much velocity is preserved after impact. Higher values = more bounce.
298    /// Works with other collider's restitution via the combine rule.
299    pub fn set_restitution(&mut self, coefficient: Real) {
300        self.material.restitution = coefficient
301    }
302
303    /// The combine rule used by this collider to combine its restitution
304    /// coefficient with the restitution coefficient of the other collider it
305    /// is in contact with.
306    pub fn restitution_combine_rule(&self) -> CoefficientCombineRule {
307        self.material.restitution_combine_rule
308    }
309
310    /// Sets the combine rule used by this collider to combine its restitution
311    /// coefficient with the restitution coefficient of the other collider it
312    /// is in contact with.
313    pub fn set_restitution_combine_rule(&mut self, rule: CoefficientCombineRule) {
314        self.material.restitution_combine_rule = rule;
315    }
316
317    /// Sets the total force magnitude beyond which a contact force event can be emitted.
318    pub fn set_contact_force_event_threshold(&mut self, threshold: Real) {
319        self.contact_force_event_threshold = threshold;
320    }
321
322    /// Converts this collider to/from a sensor.
323    ///
324    /// Sensors detect overlaps but don't create physical contact forces.
325    /// Use `true` for trigger zones, `false` for solid collision shapes.
326    pub fn set_sensor(&mut self, is_sensor: bool) {
327        if is_sensor != self.is_sensor() {
328            self.changes.insert(ColliderChanges::TYPE);
329            self.coll_type = if is_sensor {
330                ColliderType::Sensor
331            } else {
332                ColliderType::Solid
333            };
334        }
335    }
336
337    /// Returns `true` if this collider is active in the simulation.
338    ///
339    /// Disabled colliders are excluded from collision detection and physics.
340    pub fn is_enabled(&self) -> bool {
341        matches!(self.flags.enabled, ColliderEnabled::Enabled)
342    }
343
344    /// Enables or disables this collider.
345    ///
346    /// When disabled, the collider is excluded from all collision detection and physics.
347    /// Useful for temporarily "turning off" colliders without removing them.
348    pub fn set_enabled(&mut self, enabled: bool) {
349        match self.flags.enabled {
350            ColliderEnabled::Enabled | ColliderEnabled::DisabledByParent => {
351                if !enabled {
352                    self.changes.insert(ColliderChanges::ENABLED_OR_DISABLED);
353                    self.flags.enabled = ColliderEnabled::Disabled;
354                }
355            }
356            ColliderEnabled::Disabled => {
357                if enabled {
358                    self.changes.insert(ColliderChanges::ENABLED_OR_DISABLED);
359                    self.flags.enabled = ColliderEnabled::Enabled;
360                }
361            }
362        }
363    }
364
365    /// Sets the collider's position (for standalone colliders).
366    ///
367    /// For attached colliders, modify the parent body's position instead.
368    /// This directly sets world-space position.
369    pub fn set_translation(&mut self, translation: Vector) {
370        self.changes.insert(ColliderChanges::POSITION);
371        self.pos.0.translation = translation;
372    }
373
374    /// Sets the collider's rotation (for standalone colliders).
375    ///
376    /// For attached colliders, modify the parent body's rotation instead.
377    pub fn set_rotation(&mut self, rotation: Rotation) {
378        self.changes.insert(ColliderChanges::POSITION);
379        self.pos.0.rotation = rotation;
380    }
381
382    /// Sets the collider's full pose (for standalone colliders).
383    ///
384    /// For attached colliders, modify the parent body instead.
385    pub fn set_position(&mut self, position: Pose) {
386        self.changes.insert(ColliderChanges::POSITION);
387        self.pos.0 = position;
388    }
389
390    /// The current world-space position of this collider.
391    ///
392    /// For attached colliders, this is automatically updated when the parent body moves.
393    /// For standalone colliders, this is the position you set directly.
394    pub fn position(&self) -> &Pose {
395        &self.pos
396    }
397
398    /// The current position vector of this collider (world coordinates).
399    pub fn translation(&self) -> Vector {
400        self.pos.0.translation
401    }
402
403    /// The current rotation/orientation of this collider.
404    pub fn rotation(&self) -> Rotation {
405        self.pos.0.rotation
406    }
407
408    /// The collider's position relative to its parent body (local coordinates).
409    ///
410    /// Returns `None` for standalone colliders. This is the offset from the parent body's origin.
411    pub fn position_wrt_parent(&self) -> Option<&Pose> {
412        self.parent.as_ref().map(|p| &p.pos_wrt_parent)
413    }
414
415    /// Changes this collider's position offset from its parent body.
416    ///
417    /// Useful for adjusting where a collider sits on a body without moving the whole body.
418    /// Does nothing if the collider has no parent.
419    pub fn set_translation_wrt_parent(&mut self, translation: Vector) {
420        if let Some(parent) = self.parent.as_mut() {
421            self.changes.insert(ColliderChanges::PARENT);
422            parent.pos_wrt_parent.translation = translation;
423        }
424    }
425
426    /// Changes this collider's rotation offset from its parent body.
427    ///
428    /// Rotates the collider relative to its parent. Does nothing if no parent.
429    pub fn set_rotation_wrt_parent(&mut self, rotation: AngVector) {
430        if let Some(parent) = self.parent.as_mut() {
431            self.changes.insert(ColliderChanges::PARENT);
432            parent.pos_wrt_parent.rotation = rotation_from_angle(rotation);
433        }
434    }
435
436    /// Changes this collider's full pose (position + rotation) relative to its parent.
437    ///
438    /// Does nothing if the collider is not attached to a rigid-body.
439    pub fn set_position_wrt_parent(&mut self, pos_wrt_parent: Pose) {
440        if let Some(parent) = self.parent.as_mut() {
441            self.changes.insert(ColliderChanges::PARENT);
442            parent.pos_wrt_parent = pos_wrt_parent;
443        }
444    }
445
446    /// The collision groups controlling what this collider can interact with.
447    ///
448    /// See [`InteractionGroups`] for details on collision filtering.
449    pub fn collision_groups(&self) -> InteractionGroups {
450        self.flags.collision_groups
451    }
452
453    /// Changes which collision groups this collider belongs to and can interact with.
454    ///
455    /// Use to control collision filtering (like changing layers).
456    pub fn set_collision_groups(&mut self, groups: InteractionGroups) {
457        if self.flags.collision_groups != groups {
458            self.changes.insert(ColliderChanges::GROUPS);
459            self.flags.collision_groups = groups;
460        }
461    }
462
463    /// The solver groups for this collider (advanced collision filtering).
464    ///
465    /// Most users should use `collision_groups()` instead.
466    pub fn solver_groups(&self) -> InteractionGroups {
467        self.flags.solver_groups
468    }
469
470    /// Changes the solver groups (advanced contact resolution filtering).
471    pub fn set_solver_groups(&mut self, groups: InteractionGroups) {
472        if self.flags.solver_groups != groups {
473            self.changes.insert(ColliderChanges::GROUPS);
474            self.flags.solver_groups = groups;
475        }
476    }
477
478    /// Returns the material properties (friction and restitution) of this collider.
479    pub fn material(&self) -> &ColliderMaterial {
480        &self.material
481    }
482
483    /// Returns the volume (3D) or area (2D) of this collider's shape.
484    ///
485    /// Used internally for mass calculations when density is set.
486    pub fn volume(&self) -> Real {
487        self.shape.mass_properties(1.0).mass()
488    }
489
490    /// The density of this collider (mass per unit volume).
491    ///
492    /// Used to automatically compute mass from the collider's volume.
493    /// Returns an approximate density if mass was set directly instead.
494    pub fn density(&self) -> Real {
495        match &self.mprops {
496            ColliderMassProps::Density(density) => *density,
497            ColliderMassProps::Mass(mass) => {
498                let inv_volume = self.shape.mass_properties(1.0).inv_mass;
499                mass * inv_volume
500            }
501            ColliderMassProps::MassProperties(mprops) => {
502                let inv_volume = self.shape.mass_properties(1.0).inv_mass;
503                mprops.mass() * inv_volume
504            }
505        }
506    }
507
508    /// The mass contributed by this collider to its parent body.
509    ///
510    /// Either set directly or computed from density × volume.
511    pub fn mass(&self) -> Real {
512        match &self.mprops {
513            ColliderMassProps::Density(density) => self.shape.mass_properties(*density).mass(),
514            ColliderMassProps::Mass(mass) => *mass,
515            ColliderMassProps::MassProperties(mprops) => mprops.mass(),
516        }
517    }
518
519    /// Sets the uniform density of this collider.
520    ///
521    /// This will override any previous mass-properties set by [`Self::set_density`],
522    /// [`Self::set_mass`], [`Self::set_mass_properties`], [`ColliderBuilder::density`],
523    /// [`ColliderBuilder::mass`], or [`ColliderBuilder::mass_properties`]
524    /// for this collider.
525    ///
526    /// The mass and angular inertia of this collider will be computed automatically based on its
527    /// shape.
528    pub fn set_density(&mut self, density: Real) {
529        self.do_set_mass_properties(ColliderMassProps::Density(density));
530    }
531
532    /// Sets the mass of this collider.
533    ///
534    /// This will override any previous mass-properties set by [`Self::set_density`],
535    /// [`Self::set_mass`], [`Self::set_mass_properties`], [`ColliderBuilder::density`],
536    /// [`ColliderBuilder::mass`], or [`ColliderBuilder::mass_properties`]
537    /// for this collider.
538    ///
539    /// The angular inertia of this collider will be computed automatically based on its shape
540    /// and this mass value.
541    pub fn set_mass(&mut self, mass: Real) {
542        self.do_set_mass_properties(ColliderMassProps::Mass(mass));
543    }
544
545    /// Sets the mass properties of this collider.
546    ///
547    /// This will override any previous mass-properties set by [`Self::set_density`],
548    /// [`Self::set_mass`], [`Self::set_mass_properties`], [`ColliderBuilder::density`],
549    /// [`ColliderBuilder::mass`], or [`ColliderBuilder::mass_properties`]
550    /// for this collider.
551    pub fn set_mass_properties(&mut self, mass_properties: MassProperties) {
552        self.do_set_mass_properties(ColliderMassProps::MassProperties(Box::new(mass_properties)))
553    }
554
555    fn do_set_mass_properties(&mut self, mprops: ColliderMassProps) {
556        if mprops != self.mprops {
557            self.changes |= ColliderChanges::LOCAL_MASS_PROPERTIES;
558            self.mprops = mprops;
559        }
560    }
561
562    /// The geometric shape of this collider (ball, cuboid, mesh, etc.).
563    ///
564    /// Returns a reference to the underlying shape object for reading properties
565    /// or performing geometric queries.
566    pub fn shape(&self) -> &dyn Shape {
567        self.shape.as_ref()
568    }
569
570    /// A mutable reference to the geometric shape of this collider.
571    ///
572    /// If that shape is shared by multiple colliders, it will be
573    /// cloned first so that `self` contains a unique copy of that
574    /// shape that you can modify.
575    pub fn shape_mut(&mut self) -> &mut dyn Shape {
576        self.changes.insert(ColliderChanges::SHAPE);
577        self.shape.make_mut()
578    }
579
580    /// Sets the shape of this collider.
581    pub fn set_shape(&mut self, shape: SharedShape) {
582        self.changes.insert(ColliderChanges::SHAPE);
583        self.shape = shape;
584    }
585
586    /// Returns the shape as a `SharedShape` (reference-counted shape).
587    ///
588    /// Use `shape()` for the trait object, this for the concrete type.
589    pub fn shared_shape(&self) -> &SharedShape {
590        &self.shape
591    }
592
593    /// Computes the axis-aligned bounding box (AABB) of this collider.
594    ///
595    /// The AABB is the smallest box (aligned with world axes) that contains the shape.
596    /// Doesn't include contact skin.
597    pub fn compute_aabb(&self) -> Aabb {
598        self.shape.compute_aabb(&self.pos)
599    }
600
601    /// Computes the AABB including contact skin and prediction distance.
602    ///
603    /// This is the AABB used for collision detection (slightly larger than the visual shape).
604    pub fn compute_collision_aabb(&self, prediction: Real) -> Aabb {
605        self.shape
606            .compute_aabb(&self.pos)
607            .loosened(self.contact_skin + prediction)
608    }
609
610    /// Computes the AABB swept from current position to `next_position`.
611    ///
612    /// Returns a box that contains the shape at both positions plus everything in between.
613    /// Used for continuous collision detection.
614    pub fn compute_swept_aabb(&self, next_position: &Pose) -> Aabb {
615        self.shape.compute_swept_aabb(&self.pos, next_position)
616    }
617
618    // TODO: we have a lot of different AABB computation functions
619    //       We should group them somehow.
620    /// Computes the collider’s AABB for usage in a broad-phase.
621    ///
622    /// It takes into account soft-ccd, the contact skin, and the contact prediction.
623    pub fn compute_broad_phase_aabb(
624        &self,
625        params: &IntegrationParameters,
626        bodies: &RigidBodySet,
627    ) -> Aabb {
628        let parent = self.parent.and_then(|p| Some((p, bodies.get(p.handle)?)));
629        // Take soft-ccd into account by growing the aabb.
630        let next_pose = parent.and_then(|(p, parent)| {
631            (parent.soft_ccd_prediction() > 0.0).then(|| {
632                parent.predict_position_using_velocity_and_forces_with_max_dist(
633                    params.dt,
634                    parent.soft_ccd_prediction(),
635                ) * p.pos_wrt_parent
636            })
637        });
638        let soft_motion_margin = parent.map_or(0.0, |(_, parent)| self.soft_motion_margin(parent));
639
640        let prediction_distance = params.prediction_distance();
641        let mut aabb = self.compute_collision_aabb(prediction_distance / 2.0 + soft_motion_margin);
642        if let Some(next_pose) = next_pose {
643            let next_aabb = self
644                .shape
645                .compute_aabb(&next_pose)
646                .loosened(self.contact_skin() + prediction_distance / 2.0);
647            aabb.merge(&next_aabb);
648        }
649
650        aabb
651    }
652
653    /// Computes the full mass properties (mass, center of mass, angular inertia).
654    ///
655    /// Returns properties in the collider's local coordinate system.
656    pub fn mass_properties(&self) -> MassProperties {
657        self.mprops.mass_properties(&*self.shape)
658    }
659
660    /// Returns the force threshold for contact force events.
661    ///
662    /// When contact forces exceed this value, a `ContactForceEvent` is generated.
663    /// See `set_contact_force_event_threshold()` for details.
664    pub fn contact_force_event_threshold(&self) -> Real {
665        self.contact_force_event_threshold
666    }
667}
668
669/// A builder for creating colliders with custom shapes and properties.
670///
671/// This builder lets you create collision shapes and configure their physical properties
672/// (friction, bounciness, density, etc.) before adding them to your world.
673///
674/// # Common shapes
675///
676/// - [`ball(radius)`](Self::ball) - Sphere (3D) or circle (2D)
677/// - [`cuboid(hx, hy, hz)`](Self::cuboid) - Box with half-extents
678/// - [`capsule_y(half_height, radius)`](Self::capsule_y) - Pill shape (great for characters)
679/// - [`trimesh(vertices, indices)`](Self::trimesh) - Triangle mesh for complex geometry
680/// - [`heightfield(...)`](Self::heightfield) - Terrain from height data
681///
682/// # Example
683///
684/// ```ignore
685/// // Create a bouncy ball
686/// let collider = ColliderBuilder::ball(0.5)
687///     .restitution(0.9)       // Very bouncy
688///     .friction(0.1)          // Low friction (slippery)
689///     .density(2.0);           // Heavy material
690/// colliders.insert_with_parent(collider, body_handle, &mut bodies);
691/// ```
692#[derive(Clone, Debug)]
693#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
694#[must_use = "Builder functions return the updated builder"]
695pub struct ColliderBuilder {
696    /// The shape of the collider to be built.
697    pub shape: SharedShape,
698    /// Controls the way the collider’s mass-properties are computed.
699    pub mass_properties: ColliderMassProps,
700    /// The friction coefficient of the collider to be built.
701    pub friction: Real,
702    /// The rule used to combine two friction coefficients.
703    pub friction_combine_rule: CoefficientCombineRule,
704    /// The restitution coefficient of the collider to be built.
705    pub restitution: Real,
706    /// The rule used to combine two restitution coefficients.
707    pub restitution_combine_rule: CoefficientCombineRule,
708    /// The position of this collider.
709    pub position: Pose,
710    /// Is this collider a sensor?
711    pub is_sensor: bool,
712    /// Contact pairs enabled for this collider.
713    pub active_collision_types: ActiveCollisionTypes,
714    /// Physics hooks enabled for this collider.
715    pub active_hooks: ActiveHooks,
716    /// Events enabled for this collider.
717    pub active_events: ActiveEvents,
718    /// The user-data of the collider being built.
719    pub user_data: u128,
720    /// The collision groups for the collider being built.
721    pub collision_groups: InteractionGroups,
722    /// The solver groups for the collider being built.
723    pub solver_groups: InteractionGroups,
724    /// Will the collider being built be enabled?
725    pub enabled: bool,
726    /// The total force magnitude beyond which a contact force event can be emitted.
727    pub contact_force_event_threshold: Real,
728    /// An extra thickness around the collider shape to keep them further apart when colliding.
729    pub contact_skin: Real,
730}
731
732impl Default for ColliderBuilder {
733    fn default() -> Self {
734        Self::ball(0.5)
735    }
736}
737
738impl ColliderBuilder {
739    /// Initialize a new collider builder with the given shape.
740    pub fn new(shape: SharedShape) -> Self {
741        Self {
742            shape,
743            mass_properties: ColliderMassProps::default(),
744            friction: Self::default_friction(),
745            restitution: 0.0,
746            position: Pose::IDENTITY,
747            is_sensor: false,
748            user_data: 0,
749            collision_groups: InteractionGroups::all(),
750            solver_groups: InteractionGroups::all(),
751            friction_combine_rule: CoefficientCombineRule::Average,
752            restitution_combine_rule: CoefficientCombineRule::Average,
753            active_collision_types: ActiveCollisionTypes::default(),
754            active_hooks: ActiveHooks::empty(),
755            active_events: ActiveEvents::empty(),
756            enabled: true,
757            contact_force_event_threshold: 0.0,
758            contact_skin: 0.0,
759        }
760    }
761
762    /// Initialize a new collider builder with a compound shape.
763    pub fn compound(shapes: Vec<(Pose, SharedShape)>) -> Self {
764        Self::new(SharedShape::compound(shapes))
765    }
766
767    /// Creates a sphere (3D) or circle (2D) collider.
768    ///
769    /// The simplest and fastest collision shape. Use for:
770    /// - Balls and spheres
771    /// - Approximate round objects
772    /// - Projectiles
773    /// - Particles
774    ///
775    /// # Parameters
776    /// * `radius` - The sphere's radius
777    pub fn ball(radius: Real) -> Self {
778        Self::new(SharedShape::ball(radius))
779    }
780
781    /// Initialize a new collider build with a half-space shape defined by the outward normal
782    /// of its planar boundary.
783    pub fn halfspace(outward_normal: Unit<Vector>) -> Self {
784        Self::new(SharedShape::halfspace(outward_normal.into_inner()))
785    }
786
787    /// Initializes a shape made of voxels.
788    ///
789    /// Each voxel has the size `voxel_size` and grid coordinate given by `voxels`.
790    /// The `primitive_geometry` controls the behavior of collision detection at voxels boundaries.
791    ///
792    /// For initializing a voxels shape from points in space, see [`Self::voxels_from_points`].
793    /// For initializing a voxels shape from a mesh to voxelize, see [`Self::voxelized_mesh`].
794    pub fn voxels(voxel_size: Vector, voxels: &[IVector]) -> Self {
795        Self::new(SharedShape::voxels(voxel_size, voxels))
796    }
797
798    /// Initializes a collider made of voxels.
799    ///
800    /// Each voxel has the size `voxel_size` and contains at least one point from `centers`.
801    /// The `primitive_geometry` controls the behavior of collision detection at voxels boundaries.
802    pub fn voxels_from_points(voxel_size: Vector, points: &[Vector]) -> Self {
803        Self::new(SharedShape::voxels_from_points(voxel_size, points))
804    }
805
806    /// Initializes a voxels obtained from the decomposition of the given trimesh (in 3D)
807    /// or polyline (in 2D) into voxelized convex parts.
808    pub fn voxelized_mesh(
809        vertices: &[Vector],
810        indices: &[[u32; DIM]],
811        voxel_size: Real,
812        fill_mode: FillMode,
813    ) -> Self {
814        Self::new(SharedShape::voxelized_mesh(
815            vertices, indices, voxel_size, fill_mode,
816        ))
817    }
818
819    /// Initialize a new collider builder with a cylindrical shape defined by its half-height
820    /// (along the Y axis) and its radius.
821    #[cfg(feature = "dim3")]
822    pub fn cylinder(half_height: Real, radius: Real) -> Self {
823        Self::new(SharedShape::cylinder(half_height, radius))
824    }
825
826    /// Initialize a new collider builder with a rounded cylindrical shape defined by its half-height
827    /// (along the Y axis), its radius, and its roundedness (the radius of the sphere used for
828    /// dilating the cylinder).
829    #[cfg(feature = "dim3")]
830    pub fn round_cylinder(half_height: Real, radius: Real, border_radius: Real) -> Self {
831        Self::new(SharedShape::round_cylinder(
832            half_height,
833            radius,
834            border_radius,
835        ))
836    }
837
838    /// Initialize a new collider builder with a cone shape defined by its half-height
839    /// (along the Y axis) and its basis radius.
840    #[cfg(feature = "dim3")]
841    pub fn cone(half_height: Real, radius: Real) -> Self {
842        Self::new(SharedShape::cone(half_height, radius))
843    }
844
845    /// Initialize a new collider builder with a rounded cone shape defined by its half-height
846    /// (along the Y axis), its radius, and its roundedness (the radius of the sphere used for
847    /// dilating the cylinder).
848    #[cfg(feature = "dim3")]
849    pub fn round_cone(half_height: Real, radius: Real, border_radius: Real) -> Self {
850        Self::new(SharedShape::round_cone(half_height, radius, border_radius))
851    }
852
853    /// Initialize a new collider builder with a cuboid shape defined by its half-extents.
854    #[cfg(feature = "dim2")]
855    pub fn cuboid(hx: Real, hy: Real) -> Self {
856        Self::new(SharedShape::cuboid(hx, hy))
857    }
858
859    /// Initialize a new collider builder with a round cuboid shape defined by its half-extents
860    /// and border radius.
861    #[cfg(feature = "dim2")]
862    pub fn round_cuboid(hx: Real, hy: Real, border_radius: Real) -> Self {
863        Self::new(SharedShape::round_cuboid(hx, hy, border_radius))
864    }
865
866    /// Initialize a new collider builder with a capsule defined from its endpoints.
867    ///
868    /// See also [`ColliderBuilder::capsule_x`], [`ColliderBuilder::capsule_y`],
869    /// (and `ColliderBuilder::capsule_z` in 3D only)
870    /// for a simpler way to build capsules with common
871    /// orientations.
872    pub fn capsule_from_endpoints(a: Vector, b: Vector, radius: Real) -> Self {
873        Self::new(SharedShape::capsule(a, b, radius))
874    }
875
876    /// Initialize a new collider builder with a capsule shape aligned with the `x` axis.
877    pub fn capsule_x(half_height: Real, radius: Real) -> Self {
878        Self::new(SharedShape::capsule_x(half_height, radius))
879    }
880
881    /// Creates a capsule (pill-shaped) collider aligned with the Y axis.
882    ///
883    /// Capsules are cylinders with hemispherical caps. Excellent for characters because:
884    /// - Smooth collision (no getting stuck on edges)
885    /// - Good for upright objects (characters, trees)
886    /// - Fast collision detection
887    ///
888    /// # Parameters
889    /// * `half_height` - Half the height of the cylindrical part (not including caps)
890    /// * `radius` - Radius of the cylinder and caps
891    ///
892    /// **Example**: `capsule_y(1.0, 0.5)` creates a 3.0 tall capsule (1.0×2 cylinder + 0.5×2 caps)
893    pub fn capsule_y(half_height: Real, radius: Real) -> Self {
894        Self::new(SharedShape::capsule_y(half_height, radius))
895    }
896
897    /// Initialize a new collider builder with a capsule shape aligned with the `z` axis.
898    #[cfg(feature = "dim3")]
899    pub fn capsule_z(half_height: Real, radius: Real) -> Self {
900        Self::new(SharedShape::capsule_z(half_height, radius))
901    }
902
903    /// Creates a box collider defined by its half-extents (half-widths).
904    ///
905    /// Very fast collision detection. Use for:
906    /// - Boxes and crates
907    /// - Buildings and rooms
908    /// - Most rectangular objects
909    ///
910    /// # Parameters (3D)
911    /// * `hx`, `hy`, `hz` - Half-extents (half the width) along each axis
912    ///
913    /// **Example**: `cuboid(1.0, 0.5, 2.0)` creates a box with full size 2×1×4
914    #[cfg(feature = "dim3")]
915    pub fn cuboid(hx: Real, hy: Real, hz: Real) -> Self {
916        Self::new(SharedShape::cuboid(hx, hy, hz))
917    }
918
919    /// Initialize a new collider builder with a round cuboid shape defined by its half-extents
920    /// and border radius.
921    #[cfg(feature = "dim3")]
922    pub fn round_cuboid(hx: Real, hy: Real, hz: Real, border_radius: Real) -> Self {
923        Self::new(SharedShape::round_cuboid(hx, hy, hz, border_radius))
924    }
925
926    /// Creates a line segment collider between two points.
927    ///
928    /// Useful for thin barriers, edges, or 2D line-based collision.
929    /// Has no thickness - purely a mathematical line.
930    pub fn segment(a: Vector, b: Vector) -> Self {
931        Self::new(SharedShape::segment(a, b))
932    }
933
934    /// Creates a single triangle collider.
935    ///
936    /// Use for simple 3-sided shapes or as building blocks for more complex geometry.
937    pub fn triangle(a: Vector, b: Vector, c: Vector) -> Self {
938        Self::new(SharedShape::triangle(a, b, c))
939    }
940
941    /// Initializes a collider builder with a triangle shape with round corners.
942    pub fn round_triangle(a: Vector, b: Vector, c: Vector, border_radius: Real) -> Self {
943        Self::new(SharedShape::round_triangle(a, b, c, border_radius))
944    }
945
946    /// Initializes a collider builder with a polyline shape defined by its vertex and index buffers.
947    pub fn polyline(vertices: Vec<Vector>, indices: Option<Vec<[u32; 2]>>) -> Self {
948        Self::new(SharedShape::polyline(vertices, indices))
949    }
950
951    /// Initializes a collider builder with a polyline shape defined by its vertex and index
952    /// buffers and the flags controlling its optional data (orientation, deformability).
953    pub fn polyline_with_flags(
954        vertices: Vec<Vector>,
955        indices: Option<Vec<[u32; 2]>>,
956        flags: parry::shape::PolylineFlags,
957    ) -> Self {
958        Self::new(SharedShape::new(parry::shape::Polyline::with_flags(
959            vertices, indices, flags,
960        )))
961    }
962
963    /// Initializes a collider builder with an **oriented** (one-sided) polyline shape.
964    ///
965    /// Unlike [`Self::polyline`], the segments only collide from their outward side, determined by
966    /// the winding of the vertices (counter-clockwise ⇒ the enclosed interior is solid; clockwise ⇒
967    /// the exterior is solid). This is the right choice for
968    /// container walls: bodies pushed against the wall are only resolved on the intended side, which
969    /// avoids the two-sided normal-flip that lets crushed/piled bodies squeeze through a thin wall.
970    #[cfg(feature = "dim2")]
971    pub fn oriented_polyline(vertices: Vec<Vector>, indices: Option<Vec<[u32; 2]>>) -> Self {
972        use parry::shape::{Polyline, PolylineFlags};
973        Self::new(SharedShape::new(Polyline::with_flags(
974            vertices,
975            indices,
976            PolylineFlags::ORIENTED,
977        )))
978    }
979
980    /// Creates a triangle mesh collider from vertices and triangle indices.
981    ///
982    /// Use for complex, arbitrary shapes like:
983    /// - Level geometry and terrain
984    /// - Imported 3D models
985    /// - Custom irregular shapes
986    ///
987    /// **Performance note**: Triangle meshes are slower than primitive shapes (balls, boxes, capsules).
988    /// Consider using compound shapes or simpler approximations when possible.
989    ///
990    /// # Parameters
991    /// * `vertices` - Array of 3D points
992    /// * `indices` - Array of triangles, each is 3 indices into the vertex array
993    ///
994    /// # Example
995    /// ```ignore
996    /// use rapier3d::prelude::*;
997    /// use nalgebra::Point3;
998    ///
999    /// let vertices = vec![
1000    ///     Point3::new(0.0, 0.0, 0.0),
1001    ///     Point3::new(1.0, 0.0, 0.0),
1002    ///     Point3::new(0.0, 1.0, 0.0),
1003    /// ];
1004    /// let triangle: [u32; 3] = [0, 1, 2];
1005    /// let indices = vec![triangle];  // One triangle
1006    /// let collider = ColliderBuilder::trimesh(vertices, indices)?;
1007    /// ```
1008    pub fn trimesh(
1009        vertices: Vec<Vector>,
1010        indices: Vec<[u32; 3]>,
1011    ) -> Result<Self, TriMeshBuilderError> {
1012        Ok(Self::new(SharedShape::trimesh(vertices, indices)?))
1013    }
1014
1015    /// Initializes a collider builder with a triangle mesh shape defined by its vertex and index buffers and
1016    /// flags controlling its pre-processing.
1017    pub fn trimesh_with_flags(
1018        vertices: Vec<Vector>,
1019        indices: Vec<[u32; 3]>,
1020        flags: TriMeshFlags,
1021    ) -> Result<Self, TriMeshBuilderError> {
1022        Ok(Self::new(SharedShape::trimesh_with_flags(
1023            vertices, indices, flags,
1024        )?))
1025    }
1026
1027    /// Initializes a collider builder with a shape converted from the given triangle mesh index
1028    /// and vertex buffer.
1029    ///
1030    /// All the conversion variants could be achieved with other constructors of [`ColliderBuilder`]
1031    /// but having this specified by an enum can occasionally be easier or more flexible (determined
1032    /// at runtime).
1033    pub fn converted_trimesh(
1034        vertices: Vec<Vector>,
1035        indices: Vec<[u32; 3]>,
1036        converter: MeshConverter,
1037    ) -> Result<Self, MeshConverterError> {
1038        let (shape, pose) = converter.convert(vertices, indices)?;
1039        Ok(Self::new(shape).position(pose))
1040    }
1041
1042    /// Creates a compound collider by decomposing a mesh/polyline into convex pieces.
1043    ///
1044    /// Concave shapes (like an 'L' or 'C') are automatically broken into multiple convex
1045    /// parts for efficient collision detection. This is often faster than using a trimesh.
1046    ///
1047    /// Uses the V-HACD algorithm. Good for imported models that aren't already convex.
1048    pub fn convex_decomposition(vertices: &[Vector], indices: &[[u32; DIM]]) -> Self {
1049        Self::new(SharedShape::convex_decomposition(vertices, indices))
1050    }
1051
1052    /// Initializes a collider builder with a compound shape obtained from the decomposition of
1053    /// the given trimesh (in 3D) or polyline (in 2D) into convex parts dilated with round corners.
1054    pub fn round_convex_decomposition(
1055        vertices: &[Vector],
1056        indices: &[[u32; DIM]],
1057        border_radius: Real,
1058    ) -> Self {
1059        Self::new(SharedShape::round_convex_decomposition(
1060            vertices,
1061            indices,
1062            border_radius,
1063        ))
1064    }
1065
1066    /// Initializes a collider builder with a compound shape obtained from the decomposition of
1067    /// the given trimesh (in 3D) or polyline (in 2D) into convex parts.
1068    pub fn convex_decomposition_with_params(
1069        vertices: &[Vector],
1070        indices: &[[u32; DIM]],
1071        params: &VHACDParameters,
1072    ) -> Self {
1073        Self::new(SharedShape::convex_decomposition_with_params(
1074            vertices, indices, params,
1075        ))
1076    }
1077
1078    /// Initializes a collider builder with a compound shape obtained from the decomposition of
1079    /// the given trimesh (in 3D) or polyline (in 2D) into convex parts dilated with round corners.
1080    pub fn round_convex_decomposition_with_params(
1081        vertices: &[Vector],
1082        indices: &[[u32; DIM]],
1083        params: &VHACDParameters,
1084        border_radius: Real,
1085    ) -> Self {
1086        Self::new(SharedShape::round_convex_decomposition_with_params(
1087            vertices,
1088            indices,
1089            params,
1090            border_radius,
1091        ))
1092    }
1093
1094    /// Creates the smallest convex shape that contains all the given points.
1095    ///
1096    /// Computes the "shrink-wrap" around a point cloud. Useful for:
1097    /// - Creating collision shapes from vertex data
1098    /// - Approximating complex shapes with a simpler convex one
1099    ///
1100    /// Returns `None` if the points don't form a valid convex shape.
1101    ///
1102    /// **Performance**: Convex shapes are much faster than triangle meshes!
1103    pub fn convex_hull(points: &[Vector]) -> Option<Self> {
1104        SharedShape::convex_hull(points).map(Self::new)
1105    }
1106
1107    /// Initializes a new collider builder with a round 2D convex polygon or 3D convex polyhedron
1108    /// obtained after computing the convex-hull of the given points. The shape is dilated
1109    /// by a sphere of radius `border_radius`.
1110    pub fn round_convex_hull(points: &[Vector], border_radius: Real) -> Option<Self> {
1111        SharedShape::round_convex_hull(points, border_radius).map(Self::new)
1112    }
1113
1114    /// Creates a new collider builder that is a convex polygon formed by the
1115    /// given polyline assumed to be convex (no convex-hull will be automatically
1116    /// computed).
1117    #[cfg(feature = "dim2")]
1118    pub fn convex_polyline(points: Vec<Vector>) -> Option<Self> {
1119        SharedShape::convex_polyline(points).map(Self::new)
1120    }
1121
1122    /// Creates a new collider builder that is a round convex polygon formed by the
1123    /// given polyline assumed to be convex (no convex-hull will be automatically
1124    /// computed). The polygon shape is dilated by a sphere of radius `border_radius`.
1125    #[cfg(feature = "dim2")]
1126    pub fn round_convex_polyline(points: Vec<Vector>, border_radius: Real) -> Option<Self> {
1127        SharedShape::round_convex_polyline(points, border_radius).map(Self::new)
1128    }
1129
1130    /// Creates a new collider builder that is a convex polyhedron formed by the
1131    /// given triangle-mesh assumed to be convex (no convex-hull will be automatically
1132    /// computed).
1133    #[cfg(feature = "dim3")]
1134    pub fn convex_mesh(points: Vec<Vector>, indices: &[[u32; 3]]) -> Option<Self> {
1135        SharedShape::convex_mesh(points, indices).map(Self::new)
1136    }
1137
1138    /// Creates a new collider builder that is a round convex polyhedron formed by the
1139    /// given triangle-mesh assumed to be convex (no convex-hull will be automatically
1140    /// computed). The triangle mesh shape is dilated by a sphere of radius `border_radius`.
1141    #[cfg(feature = "dim3")]
1142    pub fn round_convex_mesh(
1143        points: Vec<Vector>,
1144        indices: &[[u32; 3]],
1145        border_radius: Real,
1146    ) -> Option<Self> {
1147        SharedShape::round_convex_mesh(points, indices, border_radius).map(Self::new)
1148    }
1149
1150    /// Initializes a collider builder with a heightfield shape defined by its set of height and a scale
1151    /// factor along each coordinate axis.
1152    #[cfg(feature = "dim2")]
1153    pub fn heightfield(heights: Vec<Real>, scale: Vector) -> Self {
1154        Self::new(SharedShape::heightfield(heights, scale))
1155    }
1156
1157    /// Creates a terrain/landscape collider from a 2D grid of height values.
1158    ///
1159    /// Perfect for outdoor terrain in 3D games. The heightfield is a grid where each cell
1160    /// stores a height value, creating a landscape surface.
1161    ///
1162    /// Use for:
1163    /// - Terrain and landscapes
1164    /// - Hills and valleys
1165    /// - Ground surfaces in open worlds
1166    ///
1167    /// # Parameters
1168    /// * `heights` - 2D matrix of height values (Y coordinates)
1169    /// * `scale` - Size of each grid cell in X and Z directions
1170    ///
1171    /// **Performance**: Much faster than triangle meshes for terrain!
1172    #[cfg(feature = "dim3")]
1173    pub fn heightfield(heights: Array2<Real>, scale: Vector) -> Self {
1174        Self::new(SharedShape::heightfield(heights, scale))
1175    }
1176
1177    /// Initializes a collider builder with a heightfield shape defined by its set of height and a scale
1178    /// factor along each coordinate axis.
1179    #[cfg(feature = "dim3")]
1180    pub fn heightfield_with_flags(
1181        heights: Array2<Real>,
1182        scale: Vector,
1183        flags: HeightFieldFlags,
1184    ) -> Self {
1185        Self::new(SharedShape::heightfield_with_flags(heights, scale, flags))
1186    }
1187
1188    /// Returns the default friction value used when not specified (0.5).
1189    pub fn default_friction() -> Real {
1190        0.5
1191    }
1192
1193    /// Returns the default density value used when not specified (1.0).
1194    pub fn default_density() -> Real {
1195        1.0
1196    }
1197
1198    /// Stores custom user data with this collider (128-bit integer).
1199    ///
1200    /// Use to associate game data (entity ID, type, etc.) with physics objects.
1201    ///
1202    /// # Example
1203    /// ```ignore
1204    /// let collider = ColliderBuilder::ball(0.5)
1205    ///     .user_data(entity_id as u128)
1206    ///     .build();
1207    /// ```
1208    pub fn user_data(mut self, data: u128) -> Self {
1209        self.user_data = data;
1210        self
1211    }
1212
1213    /// Sets which collision groups this collider belongs to and can interact with.
1214    ///
1215    /// Use this to control what can collide with what (like collision layers).
1216    /// See [`InteractionGroups`] for examples.
1217    ///
1218    /// # Example
1219    /// ```ignore
1220    /// // Player bullet: in group 1, only hits group 2 (enemies)
1221    /// let groups = InteractionGroups::new(Group::GROUP_1, Group::GROUP_2);
1222    /// let bullet = ColliderBuilder::ball(0.1)
1223    ///     .collision_groups(groups)
1224    ///     .build();
1225    /// ```
1226    pub fn collision_groups(mut self, groups: InteractionGroups) -> Self {
1227        self.collision_groups = groups;
1228        self
1229    }
1230
1231    /// Sets solver groups (advanced collision filtering for contact resolution).
1232    ///
1233    /// Similar to collision_groups but specifically for the contact solver.
1234    /// Most users should use `collision_groups()` instead - this is for advanced scenarios
1235    /// where you want collisions detected but not resolved (e.g., one-way platforms).
1236    pub fn solver_groups(mut self, groups: InteractionGroups) -> Self {
1237        self.solver_groups = groups;
1238        self
1239    }
1240
1241    /// Makes this collider a sensor (trigger zone) instead of a solid collision shape.
1242    ///
1243    /// Sensors detect overlaps but don't create physical collisions. Use for:
1244    /// - Trigger zones (checkpoints, danger areas)
1245    /// - Collectible item detection
1246    /// - Proximity sensors
1247    /// - Win/lose conditions
1248    ///
1249    /// You'll receive collision events when objects enter/exit the sensor.
1250    ///
1251    /// # Example
1252    /// ```ignore
1253    /// let trigger = ColliderBuilder::cuboid(5.0, 5.0, 5.0)
1254    ///     .sensor(true)
1255    ///     .build();
1256    /// ```
1257    pub fn sensor(mut self, is_sensor: bool) -> Self {
1258        self.is_sensor = is_sensor;
1259        self
1260    }
1261
1262    /// Enables custom physics hooks for this collider (advanced).
1263    ///
1264    /// See [`ActiveHooks`](crate::pipeline::ActiveHooks) for details on custom collision filtering.
1265    pub fn active_hooks(mut self, active_hooks: ActiveHooks) -> Self {
1266        self.active_hooks = active_hooks;
1267        self
1268    }
1269
1270    /// Enables event generation for this collider.
1271    ///
1272    /// Set to `ActiveEvents::COLLISION_EVENTS` for start/stop notifications.
1273    /// Set to `ActiveEvents::CONTACT_FORCE_EVENTS` for force threshold events.
1274    ///
1275    /// # Example
1276    /// ```ignore
1277    /// let sensor = ColliderBuilder::ball(1.0)
1278    ///     .sensor(true)
1279    ///     .active_events(ActiveEvents::COLLISION_EVENTS)
1280    ///     .build();
1281    /// ```
1282    pub fn active_events(mut self, active_events: ActiveEvents) -> Self {
1283        self.active_events = active_events;
1284        self
1285    }
1286
1287    /// Sets which body type combinations can collide with this collider.
1288    ///
1289    /// See [`ActiveCollisionTypes`] for details. Most users don't need to change this.
1290    pub fn active_collision_types(mut self, active_collision_types: ActiveCollisionTypes) -> Self {
1291        self.active_collision_types = active_collision_types;
1292        self
1293    }
1294
1295    /// Sets the friction coefficient (slipperiness) for this collider.
1296    ///
1297    /// - `0.0` = ice (very slippery)
1298    /// - `0.5` = wood on wood
1299    /// - `1.0` = rubber (high grip)
1300    ///
1301    /// Default is `0.5`.
1302    pub fn friction(mut self, friction: Real) -> Self {
1303        self.friction = friction;
1304        self
1305    }
1306
1307    /// Sets how friction coefficients are combined when two colliders touch.
1308    ///
1309    /// Options: Average, Min, Max, Multiply. Default is Average.
1310    /// Most games can ignore this and use the default.
1311    pub fn friction_combine_rule(mut self, rule: CoefficientCombineRule) -> Self {
1312        self.friction_combine_rule = rule;
1313        self
1314    }
1315
1316    /// Sets the restitution coefficient (bounciness) for this collider.
1317    ///
1318    /// - `0.0` = no bounce (clay, soft)
1319    /// - `0.5` = moderate bounce
1320    /// - `1.0` = perfect elastic bounce
1321    /// - `>1.0` = super bouncy (gains energy!)
1322    ///
1323    /// Default is `0.0`.
1324    pub fn restitution(mut self, restitution: Real) -> Self {
1325        self.restitution = restitution;
1326        self
1327    }
1328
1329    /// Sets the rule to be used to combine two restitution coefficients in a contact.
1330    pub fn restitution_combine_rule(mut self, rule: CoefficientCombineRule) -> Self {
1331        self.restitution_combine_rule = rule;
1332        self
1333    }
1334
1335    /// Sets the density (mass per unit volume) of this collider.
1336    ///
1337    /// Mass will be computed as: `density × volume`. Common densities:
1338    /// - `1000.0` = water
1339    /// - `2700.0` = aluminum
1340    /// - `7850.0` = steel
1341    ///
1342    /// ⚠️ Use either `density()` OR `mass()`, not both (last call wins).
1343    ///
1344    /// # Example
1345    /// ```ignore
1346    /// let steel_ball = ColliderBuilder::ball(0.5).density(7850.0).build();
1347    /// ```
1348    pub fn density(mut self, density: Real) -> Self {
1349        self.mass_properties = ColliderMassProps::Density(density);
1350        self
1351    }
1352
1353    /// Sets the total mass of this collider directly.
1354    ///
1355    /// Angular inertia is computed automatically from the shape and mass.
1356    ///
1357    /// ⚠️ Use either `mass()` OR `density()`, not both (last call wins).
1358    ///
1359    /// # Example
1360    /// ```ignore
1361    /// // 10kg ball regardless of its radius
1362    /// let collider = ColliderBuilder::ball(0.5).mass(10.0).build();
1363    /// ```
1364    pub fn mass(mut self, mass: Real) -> Self {
1365        self.mass_properties = ColliderMassProps::Mass(mass);
1366        self
1367    }
1368
1369    /// Sets the mass properties of the collider this builder will build.
1370    ///
1371    /// This will be overridden by a call to [`Self::density`] or [`Self::mass`] so it only
1372    /// makes sense to call either [`Self::density`] or [`Self::mass`] or [`Self::mass_properties`].
1373    pub fn mass_properties(mut self, mass_properties: MassProperties) -> Self {
1374        self.mass_properties = ColliderMassProps::MassProperties(Box::new(mass_properties));
1375        self
1376    }
1377
1378    /// Sets the force threshold for triggering contact force events.
1379    ///
1380    /// When total contact force exceeds this value, a `ContactForceEvent` is generated
1381    /// (if `ActiveEvents::CONTACT_FORCE_EVENTS` is enabled).
1382    ///
1383    /// Use for detecting hard impacts, breaking objects, or damage systems.
1384    ///
1385    /// # Example
1386    /// ```ignore
1387    /// let glass = ColliderBuilder::cuboid(1.0, 1.0, 0.1)
1388    ///     .active_events(ActiveEvents::CONTACT_FORCE_EVENTS)
1389    ///     .contact_force_event_threshold(1000.0)  // Break at 1000N
1390    ///     .build();
1391    /// ```
1392    pub fn contact_force_event_threshold(mut self, threshold: Real) -> Self {
1393        self.contact_force_event_threshold = threshold;
1394        self
1395    }
1396
1397    /// Sets where the collider sits relative to its parent body.
1398    ///
1399    /// For attached colliders, this is the offset from the body's origin.
1400    /// For standalone colliders, this is the world position.
1401    ///
1402    /// # Example
1403    /// ```ignore
1404    /// // Collider offset 2 units to the right of the body
1405    /// let collider = ColliderBuilder::ball(0.5)
1406    ///     .translation(vector![2.0, 0.0, 0.0])
1407    ///     .build();
1408    /// ```
1409    pub fn translation(mut self, translation: Vector) -> Self {
1410        self.position.translation = translation;
1411        self
1412    }
1413
1414    /// Sets the collider's rotation relative to its parent body.
1415    ///
1416    /// For attached colliders, this rotates the collider relative to the body.
1417    /// For standalone colliders, this is the world rotation.
1418    pub fn rotation(mut self, angle: AngVector) -> Self {
1419        self.position.rotation = rotation_from_angle(angle);
1420        self
1421    }
1422
1423    /// Sets the collider's full pose (position + rotation) relative to its parent.
1424    ///
1425    /// For attached colliders, this is relative to the parent body.
1426    /// For standalone colliders, this is the world pose.
1427    pub fn position(mut self, pos: Pose) -> Self {
1428        self.position = pos;
1429        self
1430    }
1431
1432    /// Sets the initial position (translation and orientation) of the collider to be created,
1433    /// relative to the rigid-body it is attached to.
1434    #[deprecated(note = "Use `.position` instead.")]
1435    pub fn position_wrt_parent(mut self, pos: Pose) -> Self {
1436        self.position = pos;
1437        self
1438    }
1439
1440    /// Set the position of this collider in the local-space of the rigid-body it is attached to.
1441    #[deprecated(note = "Use `.position` instead.")]
1442    pub fn delta(mut self, delta: Pose) -> Self {
1443        self.position = delta;
1444        self
1445    }
1446
1447    /// Sets the contact skin of the collider.
1448    ///
1449    /// The contact skin acts as if the collider was enlarged with a skin of width `skin_thickness`
1450    /// around it, keeping objects further apart when colliding.
1451    ///
1452    /// A non-zero contact skin can increase performance, and in some cases, stability. However
1453    /// it creates a small gap between colliding object (equal to the sum of their skin). If the
1454    /// skin is sufficiently small, this might not be visually significant or can be hidden by the
1455    /// rendering assets.
1456    pub fn contact_skin(mut self, skin_thickness: Real) -> Self {
1457        self.contact_skin = skin_thickness;
1458        self
1459    }
1460
1461    /// Sets whether this collider starts enabled or disabled.
1462    ///
1463    /// Default is `true` (enabled). Set to `false` to create a disabled collider.
1464    pub fn enabled(mut self, enabled: bool) -> Self {
1465        self.enabled = enabled;
1466        self
1467    }
1468
1469    /// Finalizes the collider and returns it, ready to be added to the world.
1470    ///
1471    /// # Example
1472    /// ```ignore
1473    /// let collider = ColliderBuilder::ball(0.5)
1474    ///     .friction(0.7)
1475    ///     .build();
1476    /// colliders.insert_with_parent(collider, body_handle, &mut bodies);
1477    /// ```
1478    pub fn build(&self) -> Collider {
1479        let shape = self.shape.clone();
1480        let material = ColliderMaterial {
1481            friction: self.friction,
1482            restitution: self.restitution,
1483            friction_combine_rule: self.friction_combine_rule,
1484            restitution_combine_rule: self.restitution_combine_rule,
1485        };
1486        let flags = ColliderFlags {
1487            collision_groups: self.collision_groups,
1488            solver_groups: self.solver_groups,
1489            active_collision_types: self.active_collision_types,
1490            active_hooks: self.active_hooks,
1491            active_events: self.active_events,
1492            enabled: if self.enabled {
1493                ColliderEnabled::Enabled
1494            } else {
1495                ColliderEnabled::Disabled
1496            },
1497        };
1498        let changes = ColliderChanges::all();
1499        let pos = ColliderPosition(self.position);
1500        let coll_type = if self.is_sensor {
1501            ColliderType::Sensor
1502        } else {
1503            ColliderType::Solid
1504        };
1505
1506        Collider {
1507            shape,
1508            mprops: self.mass_properties.clone(),
1509            material,
1510            parent: None,
1511            changes,
1512            pos,
1513            flags,
1514            coll_type,
1515            contact_force_event_threshold: self.contact_force_event_threshold,
1516            contact_skin: self.contact_skin,
1517            deformable_mesh_ref: None,
1518            user_data: self.user_data,
1519        }
1520    }
1521}
1522
1523impl From<ColliderBuilder> for Collider {
1524    fn from(val: ColliderBuilder) -> Collider {
1525        val.build()
1526    }
1527}