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