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}