rapier3d/geometry/
collider.rs

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