Skip to main content

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