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