feat: documentation improvements (#884)
This commit is contained in:
@@ -4,7 +4,16 @@ use crate::math::Real;
|
||||
use parry::partitioning::{Bvh, BvhWorkspace};
|
||||
use parry::utils::hashmap::{Entry, HashMap};
|
||||
|
||||
/// A broad-phase based on parry’s [`Bvh`] data structure.
|
||||
/// The broad-phase collision detector that quickly filters out distant object pairs.
|
||||
///
|
||||
/// The broad-phase is the "first pass" of collision detection. It uses a hierarchical
|
||||
/// bounding volume tree (BVH) to quickly identify which collider pairs are close enough
|
||||
/// to potentially collide, avoiding expensive narrow-phase checks for distant objects.
|
||||
///
|
||||
/// Think of it as a "spatial index" that answers: "Which objects are near each other?"
|
||||
///
|
||||
/// You typically don't interact with this directly - it's managed by [`PhysicsPipeline`](crate::pipeline::PhysicsPipeline).
|
||||
/// However, you can use it to create a [`QueryPipeline`](crate::pipeline::QueryPipeline) for spatial queries.
|
||||
#[derive(Default, Clone)]
|
||||
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
|
||||
pub struct BroadPhaseBvh {
|
||||
|
||||
@@ -17,9 +17,34 @@ use parry::transformation::voxelization::FillMode;
|
||||
|
||||
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
|
||||
#[derive(Clone, Debug)]
|
||||
/// A geometric entity that can be attached to a body so it can be affected by contacts and proximity queries.
|
||||
/// The collision shape attached to a rigid body that defines what it can collide with.
|
||||
///
|
||||
/// To build a new collider, use the [`ColliderBuilder`] structure.
|
||||
/// Think of a collider as the "hitbox" or "collision shape" for your physics object. While a
|
||||
/// [`RigidBody`](crate::dynamics::RigidBody) handles the physics (mass, velocity, forces),
|
||||
/// the collider defines what shape the object has for collision detection.
|
||||
///
|
||||
/// ## Key concepts
|
||||
///
|
||||
/// - **Shape**: The geometric form (box, sphere, capsule, mesh, etc.)
|
||||
/// - **Material**: Physical properties like friction (slipperiness) and restitution (bounciness)
|
||||
/// - **Sensor vs. Solid**: Sensors detect overlaps but don't create physical collisions
|
||||
/// - **Mass properties**: Automatically computed from the shape's volume and density
|
||||
///
|
||||
/// ## Creating colliders
|
||||
///
|
||||
/// Always use [`ColliderBuilder`] to create colliders:
|
||||
///
|
||||
/// ```ignore
|
||||
/// let collider = ColliderBuilder::cuboid(1.0, 0.5, 1.0) // 2x1x2 box
|
||||
/// .friction(0.7)
|
||||
/// .restitution(0.3);
|
||||
/// colliders.insert_with_parent(collider, body_handle, &mut bodies);
|
||||
/// ```
|
||||
///
|
||||
/// ## Attaching to bodies
|
||||
///
|
||||
/// Colliders are usually attached to rigid bodies. One body can have multiple colliders
|
||||
/// to create compound shapes (like a character with separate colliders for head, torso, limbs).
|
||||
pub struct Collider {
|
||||
pub(crate) coll_type: ColliderType,
|
||||
pub(crate) shape: ColliderShape,
|
||||
@@ -52,12 +77,21 @@ impl Collider {
|
||||
}
|
||||
}
|
||||
|
||||
/// The rigid body this collider is attached to.
|
||||
/// The rigid body this collider is attached to, if any.
|
||||
///
|
||||
/// Returns `None` for standalone colliders (not attached to any body).
|
||||
pub fn parent(&self) -> Option<RigidBodyHandle> {
|
||||
self.parent.map(|parent| parent.handle)
|
||||
}
|
||||
|
||||
/// Is this collider a sensor?
|
||||
/// Checks if this collider is a sensor (detects overlaps without physical collision).
|
||||
///
|
||||
/// Sensors are like "trigger zones" - they detect when other colliders enter/exit them
|
||||
/// but don't create physical contact forces. Use for:
|
||||
/// - Trigger zones (checkpoint areas, damage regions)
|
||||
/// - Proximity detection
|
||||
/// - Collectible items
|
||||
/// - Area-of-effect detection
|
||||
pub fn is_sensor(&self) -> bool {
|
||||
self.coll_type.is_sensor()
|
||||
}
|
||||
@@ -110,22 +144,31 @@ impl Collider {
|
||||
self.contact_skin = *contact_skin;
|
||||
}
|
||||
|
||||
/// The physics hooks enabled for this collider.
|
||||
/// Which physics hooks are enabled for this collider.
|
||||
///
|
||||
/// Hooks allow custom filtering and modification of collisions. See [`PhysicsHooks`](crate::pipeline::PhysicsHooks).
|
||||
pub fn active_hooks(&self) -> ActiveHooks {
|
||||
self.flags.active_hooks
|
||||
}
|
||||
|
||||
/// Sets the physics hooks enabled for this collider.
|
||||
/// Enables/disables physics hooks for this collider.
|
||||
///
|
||||
/// Use to opt colliders into custom collision filtering logic.
|
||||
pub fn set_active_hooks(&mut self, active_hooks: ActiveHooks) {
|
||||
self.flags.active_hooks = active_hooks;
|
||||
}
|
||||
|
||||
/// The events enabled for this collider.
|
||||
/// Which events are enabled for this collider.
|
||||
///
|
||||
/// Controls whether you receive collision/contact force events. See [`ActiveEvents`](crate::pipeline::ActiveEvents).
|
||||
pub fn active_events(&self) -> ActiveEvents {
|
||||
self.flags.active_events
|
||||
}
|
||||
|
||||
/// Sets the events enabled for this collider.
|
||||
/// Enables/disables event generation for this collider.
|
||||
///
|
||||
/// Set to `ActiveEvents::COLLISION_EVENTS` to receive started/stopped collision notifications.
|
||||
/// Set to `ActiveEvents::CONTACT_FORCE_EVENTS` to receive force threshold events.
|
||||
pub fn set_active_events(&mut self, active_events: ActiveEvents) {
|
||||
self.flags.active_events = active_events;
|
||||
}
|
||||
@@ -154,12 +197,19 @@ impl Collider {
|
||||
self.contact_skin = skin_thickness;
|
||||
}
|
||||
|
||||
/// The friction coefficient of this collider.
|
||||
/// The friction coefficient of this collider (how "slippery" it is).
|
||||
///
|
||||
/// - `0.0` = perfectly slippery (ice)
|
||||
/// - `1.0` = high friction (rubber on concrete)
|
||||
/// - Typical values: 0.3-0.8
|
||||
pub fn friction(&self) -> Real {
|
||||
self.material.friction
|
||||
}
|
||||
|
||||
/// Sets the friction coefficient of this collider.
|
||||
/// Sets the friction coefficient (slipperiness).
|
||||
///
|
||||
/// Controls how much this surface resists sliding. Higher values = more grip.
|
||||
/// Works with other collider's friction via the combine rule.
|
||||
pub fn set_friction(&mut self, coefficient: Real) {
|
||||
self.material.friction = coefficient
|
||||
}
|
||||
@@ -178,12 +228,20 @@ impl Collider {
|
||||
self.material.friction_combine_rule = rule;
|
||||
}
|
||||
|
||||
/// The restitution coefficient of this collider.
|
||||
/// The restitution coefficient of this collider (how "bouncy" it is).
|
||||
///
|
||||
/// - `0.0` = no bounce (clay, soft material)
|
||||
/// - `1.0` = perfect bounce (ideal elastic collision)
|
||||
/// - `>1.0` = super bouncy (gains energy, unrealistic but fun!)
|
||||
/// - Typical values: 0.0-0.8
|
||||
pub fn restitution(&self) -> Real {
|
||||
self.material.restitution
|
||||
}
|
||||
|
||||
/// Sets the restitution coefficient of this collider.
|
||||
/// Sets the restitution coefficient (bounciness).
|
||||
///
|
||||
/// Controls how much velocity is preserved after impact. Higher values = more bounce.
|
||||
/// Works with other collider's restitution via the combine rule.
|
||||
pub fn set_restitution(&mut self, coefficient: Real) {
|
||||
self.material.restitution = coefficient
|
||||
}
|
||||
@@ -207,7 +265,10 @@ impl Collider {
|
||||
self.contact_force_event_threshold = threshold;
|
||||
}
|
||||
|
||||
/// Sets whether or not this is a sensor collider.
|
||||
/// Converts this collider to/from a sensor.
|
||||
///
|
||||
/// Sensors detect overlaps but don't create physical contact forces.
|
||||
/// Use `true` for trigger zones, `false` for solid collision shapes.
|
||||
pub fn set_sensor(&mut self, is_sensor: bool) {
|
||||
if is_sensor != self.is_sensor() {
|
||||
self.changes.insert(ColliderChanges::TYPE);
|
||||
@@ -219,12 +280,17 @@ impl Collider {
|
||||
}
|
||||
}
|
||||
|
||||
/// Is this collider enabled?
|
||||
/// Returns `true` if this collider is active in the simulation.
|
||||
///
|
||||
/// Disabled colliders are excluded from collision detection and physics.
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
matches!(self.flags.enabled, ColliderEnabled::Enabled)
|
||||
}
|
||||
|
||||
/// Sets whether or not this collider is enabled.
|
||||
/// Enables or disables this collider.
|
||||
///
|
||||
/// When disabled, the collider is excluded from all collision detection and physics.
|
||||
/// Useful for temporarily "turning off" colliders without removing them.
|
||||
pub fn set_enabled(&mut self, enabled: bool) {
|
||||
match self.flags.enabled {
|
||||
ColliderEnabled::Enabled | ColliderEnabled::DisabledByParent => {
|
||||
@@ -242,45 +308,60 @@ impl Collider {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the translational part of this collider's position.
|
||||
/// Sets the collider's position (for standalone colliders).
|
||||
///
|
||||
/// For attached colliders, modify the parent body's position instead.
|
||||
/// This directly sets world-space position.
|
||||
pub fn set_translation(&mut self, translation: Vector<Real>) {
|
||||
self.changes.insert(ColliderChanges::POSITION);
|
||||
self.pos.0.translation.vector = translation;
|
||||
}
|
||||
|
||||
/// Sets the rotational part of this collider's position.
|
||||
/// Sets the collider's rotation (for standalone colliders).
|
||||
///
|
||||
/// For attached colliders, modify the parent body's rotation instead.
|
||||
pub fn set_rotation(&mut self, rotation: Rotation<Real>) {
|
||||
self.changes.insert(ColliderChanges::POSITION);
|
||||
self.pos.0.rotation = rotation;
|
||||
}
|
||||
|
||||
/// Sets the position of this collider.
|
||||
/// Sets the collider's full pose (for standalone colliders).
|
||||
///
|
||||
/// For attached colliders, modify the parent body instead.
|
||||
pub fn set_position(&mut self, position: Isometry<Real>) {
|
||||
self.changes.insert(ColliderChanges::POSITION);
|
||||
self.pos.0 = position;
|
||||
}
|
||||
|
||||
/// The world-space position of this collider.
|
||||
/// The current world-space position of this collider.
|
||||
///
|
||||
/// For attached colliders, this is automatically updated when the parent body moves.
|
||||
/// For standalone colliders, this is the position you set directly.
|
||||
pub fn position(&self) -> &Isometry<Real> {
|
||||
&self.pos
|
||||
}
|
||||
|
||||
/// The translational part of this collider's position.
|
||||
/// The current position vector of this collider (world coordinates).
|
||||
pub fn translation(&self) -> &Vector<Real> {
|
||||
&self.pos.0.translation.vector
|
||||
}
|
||||
|
||||
/// The rotational part of this collider's position.
|
||||
/// The current rotation/orientation of this collider.
|
||||
pub fn rotation(&self) -> &Rotation<Real> {
|
||||
&self.pos.0.rotation
|
||||
}
|
||||
|
||||
/// The position of this collider with respect to the body it is attached to.
|
||||
/// The collider's position relative to its parent body (local coordinates).
|
||||
///
|
||||
/// Returns `None` for standalone colliders. This is the offset from the parent body's origin.
|
||||
pub fn position_wrt_parent(&self) -> Option<&Isometry<Real>> {
|
||||
self.parent.as_ref().map(|p| &p.pos_wrt_parent)
|
||||
}
|
||||
|
||||
/// Sets the translational part of this collider's translation relative to its parent rigid-body.
|
||||
/// Changes this collider's position offset from its parent body.
|
||||
///
|
||||
/// Useful for adjusting where a collider sits on a body without moving the whole body.
|
||||
/// Does nothing if the collider has no parent.
|
||||
pub fn set_translation_wrt_parent(&mut self, translation: Vector<Real>) {
|
||||
if let Some(parent) = self.parent.as_mut() {
|
||||
self.changes.insert(ColliderChanges::PARENT);
|
||||
@@ -288,7 +369,9 @@ impl Collider {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the rotational part of this collider's rotation relative to its parent rigid-body.
|
||||
/// Changes this collider's rotation offset from its parent body.
|
||||
///
|
||||
/// Rotates the collider relative to its parent. Does nothing if no parent.
|
||||
pub fn set_rotation_wrt_parent(&mut self, rotation: AngVector<Real>) {
|
||||
if let Some(parent) = self.parent.as_mut() {
|
||||
self.changes.insert(ColliderChanges::PARENT);
|
||||
@@ -296,7 +379,7 @@ impl Collider {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the position of this collider with respect to its parent rigid-body.
|
||||
/// Changes this collider's full pose (position + rotation) relative to its parent.
|
||||
///
|
||||
/// Does nothing if the collider is not attached to a rigid-body.
|
||||
pub fn set_position_wrt_parent(&mut self, pos_wrt_parent: Isometry<Real>) {
|
||||
@@ -306,12 +389,16 @@ impl Collider {
|
||||
}
|
||||
}
|
||||
|
||||
/// The collision groups used by this collider.
|
||||
/// The collision groups controlling what this collider can interact with.
|
||||
///
|
||||
/// See [`InteractionGroups`] for details on collision filtering.
|
||||
pub fn collision_groups(&self) -> InteractionGroups {
|
||||
self.flags.collision_groups
|
||||
}
|
||||
|
||||
/// Sets the collision groups of this collider.
|
||||
/// Changes which collision groups this collider belongs to and can interact with.
|
||||
///
|
||||
/// Use to control collision filtering (like changing layers).
|
||||
pub fn set_collision_groups(&mut self, groups: InteractionGroups) {
|
||||
if self.flags.collision_groups != groups {
|
||||
self.changes.insert(ColliderChanges::GROUPS);
|
||||
@@ -319,12 +406,14 @@ impl Collider {
|
||||
}
|
||||
}
|
||||
|
||||
/// The solver groups used by this collider.
|
||||
/// The solver groups for this collider (advanced collision filtering).
|
||||
///
|
||||
/// Most users should use `collision_groups()` instead.
|
||||
pub fn solver_groups(&self) -> InteractionGroups {
|
||||
self.flags.solver_groups
|
||||
}
|
||||
|
||||
/// Sets the solver groups of this collider.
|
||||
/// Changes the solver groups (advanced contact resolution filtering).
|
||||
pub fn set_solver_groups(&mut self, groups: InteractionGroups) {
|
||||
if self.flags.solver_groups != groups {
|
||||
self.changes.insert(ColliderChanges::GROUPS);
|
||||
@@ -332,17 +421,22 @@ impl Collider {
|
||||
}
|
||||
}
|
||||
|
||||
/// The material (friction and restitution properties) of this collider.
|
||||
/// Returns the material properties (friction and restitution) of this collider.
|
||||
pub fn material(&self) -> &ColliderMaterial {
|
||||
&self.material
|
||||
}
|
||||
|
||||
/// The volume (or surface in 2D) of this collider.
|
||||
/// Returns the volume (3D) or area (2D) of this collider's shape.
|
||||
///
|
||||
/// Used internally for mass calculations when density is set.
|
||||
pub fn volume(&self) -> Real {
|
||||
self.shape.mass_properties(1.0).mass()
|
||||
}
|
||||
|
||||
/// The density of this collider.
|
||||
/// The density of this collider (mass per unit volume).
|
||||
///
|
||||
/// Used to automatically compute mass from the collider's volume.
|
||||
/// Returns an approximate density if mass was set directly instead.
|
||||
pub fn density(&self) -> Real {
|
||||
match &self.mprops {
|
||||
ColliderMassProps::Density(density) => *density,
|
||||
@@ -357,7 +451,9 @@ impl Collider {
|
||||
}
|
||||
}
|
||||
|
||||
/// The mass of this collider.
|
||||
/// The mass contributed by this collider to its parent body.
|
||||
///
|
||||
/// Either set directly or computed from density × volume.
|
||||
pub fn mass(&self) -> Real {
|
||||
match &self.mprops {
|
||||
ColliderMassProps::Density(density) => self.shape.mass_properties(*density).mass(),
|
||||
@@ -409,7 +505,10 @@ impl Collider {
|
||||
}
|
||||
}
|
||||
|
||||
/// The geometric shape of this collider.
|
||||
/// The geometric shape of this collider (ball, cuboid, mesh, etc.).
|
||||
///
|
||||
/// Returns a reference to the underlying shape object for reading properties
|
||||
/// or performing geometric queries.
|
||||
pub fn shape(&self) -> &dyn Shape {
|
||||
self.shape.as_ref()
|
||||
}
|
||||
@@ -430,29 +529,34 @@ impl Collider {
|
||||
self.shape = shape;
|
||||
}
|
||||
|
||||
/// Retrieve the SharedShape. Also see the `shape()` function
|
||||
/// Returns the shape as a `SharedShape` (reference-counted shape).
|
||||
///
|
||||
/// Use `shape()` for the trait object, this for the concrete type.
|
||||
pub fn shared_shape(&self) -> &SharedShape {
|
||||
&self.shape
|
||||
}
|
||||
|
||||
/// Compute the axis-aligned bounding box of this collider.
|
||||
/// Computes the axis-aligned bounding box (AABB) of this collider.
|
||||
///
|
||||
/// This AABB doesn’t take into account the collider’s contact skin.
|
||||
/// [`Collider::contact_skin`].
|
||||
/// The AABB is the smallest box (aligned with world axes) that contains the shape.
|
||||
/// Doesn't include contact skin.
|
||||
pub fn compute_aabb(&self) -> Aabb {
|
||||
self.shape.compute_aabb(&self.pos)
|
||||
}
|
||||
|
||||
/// Compute the axis-aligned bounding box of this collider, taking into account the
|
||||
/// [`Collider::contact_skin`] and prediction distance.
|
||||
/// Computes the AABB including contact skin and prediction distance.
|
||||
///
|
||||
/// This is the AABB used for collision detection (slightly larger than the visual shape).
|
||||
pub fn compute_collision_aabb(&self, prediction: Real) -> Aabb {
|
||||
self.shape
|
||||
.compute_aabb(&self.pos)
|
||||
.loosened(self.contact_skin + prediction)
|
||||
}
|
||||
|
||||
/// Compute the axis-aligned bounding box of this collider moving from its current position
|
||||
/// to the given `next_position`
|
||||
/// Computes the AABB swept from current position to `next_position`.
|
||||
///
|
||||
/// Returns a box that contains the shape at both positions plus everything in between.
|
||||
/// Used for continuous collision detection.
|
||||
pub fn compute_swept_aabb(&self, next_position: &Isometry<Real>) -> Aabb {
|
||||
self.shape.compute_swept_aabb(&self.pos, next_position)
|
||||
}
|
||||
@@ -491,18 +595,45 @@ impl Collider {
|
||||
aabb
|
||||
}
|
||||
|
||||
/// Compute the local-space mass properties of this collider.
|
||||
/// Computes the full mass properties (mass, center of mass, angular inertia).
|
||||
///
|
||||
/// Returns properties in the collider's local coordinate system.
|
||||
pub fn mass_properties(&self) -> MassProperties {
|
||||
self.mprops.mass_properties(&*self.shape)
|
||||
}
|
||||
|
||||
/// The total force magnitude beyond which a contact force event can be emitted.
|
||||
/// Returns the force threshold for contact force events.
|
||||
///
|
||||
/// When contact forces exceed this value, a `ContactForceEvent` is generated.
|
||||
/// See `set_contact_force_event_threshold()` for details.
|
||||
pub fn contact_force_event_threshold(&self) -> Real {
|
||||
self.contact_force_event_threshold
|
||||
}
|
||||
}
|
||||
|
||||
/// A structure responsible for building a new collider.
|
||||
/// A builder for creating colliders with custom shapes and properties.
|
||||
///
|
||||
/// This builder lets you create collision shapes and configure their physical properties
|
||||
/// (friction, bounciness, density, etc.) before adding them to your world.
|
||||
///
|
||||
/// # Common shapes
|
||||
///
|
||||
/// - [`ball(radius)`](Self::ball) - Sphere (3D) or circle (2D)
|
||||
/// - [`cuboid(hx, hy, hz)`](Self::cuboid) - Box with half-extents
|
||||
/// - [`capsule_y(half_height, radius)`](Self::capsule_y) - Pill shape (great for characters)
|
||||
/// - [`trimesh(vertices, indices)`](Self::trimesh) - Triangle mesh for complex geometry
|
||||
/// - [`heightfield(...)`](Self::heightfield) - Terrain from height data
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// // Create a bouncy ball
|
||||
/// let collider = ColliderBuilder::ball(0.5)
|
||||
/// .restitution(0.9) // Very bouncy
|
||||
/// .friction(0.1) // Low friction (slippery)
|
||||
/// .density(2.0); // Heavy material
|
||||
/// colliders.insert_with_parent(collider, body_handle, &mut bodies);
|
||||
/// ```
|
||||
#[derive(Clone, Debug)]
|
||||
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
|
||||
#[must_use = "Builder functions return the updated builder"]
|
||||
@@ -578,7 +709,16 @@ impl ColliderBuilder {
|
||||
Self::new(SharedShape::compound(shapes))
|
||||
}
|
||||
|
||||
/// Initialize a new collider builder with a ball shape defined by its radius.
|
||||
/// Creates a sphere (3D) or circle (2D) collider.
|
||||
///
|
||||
/// The simplest and fastest collision shape. Use for:
|
||||
/// - Balls and spheres
|
||||
/// - Approximate round objects
|
||||
/// - Projectiles
|
||||
/// - Particles
|
||||
///
|
||||
/// # Parameters
|
||||
/// * `radius` - The sphere's radius
|
||||
pub fn ball(radius: Real) -> Self {
|
||||
Self::new(SharedShape::ball(radius))
|
||||
}
|
||||
@@ -683,7 +823,18 @@ impl ColliderBuilder {
|
||||
Self::new(SharedShape::capsule_x(half_height, radius))
|
||||
}
|
||||
|
||||
/// Initialize a new collider builder with a capsule shape aligned with the `y` axis.
|
||||
/// Creates a capsule (pill-shaped) collider aligned with the Y axis.
|
||||
///
|
||||
/// Capsules are cylinders with hemispherical caps. Excellent for characters because:
|
||||
/// - Smooth collision (no getting stuck on edges)
|
||||
/// - Good for upright objects (characters, trees)
|
||||
/// - Fast collision detection
|
||||
///
|
||||
/// # Parameters
|
||||
/// * `half_height` - Half the height of the cylindrical part (not including caps)
|
||||
/// * `radius` - Radius of the cylinder and caps
|
||||
///
|
||||
/// **Example**: `capsule_y(1.0, 0.5)` creates a 3.0 tall capsule (1.0×2 cylinder + 0.5×2 caps)
|
||||
pub fn capsule_y(half_height: Real, radius: Real) -> Self {
|
||||
Self::new(SharedShape::capsule_y(half_height, radius))
|
||||
}
|
||||
@@ -694,7 +845,17 @@ impl ColliderBuilder {
|
||||
Self::new(SharedShape::capsule_z(half_height, radius))
|
||||
}
|
||||
|
||||
/// Initialize a new collider builder with a cuboid shape defined by its half-extents.
|
||||
/// Creates a box collider defined by its half-extents (half-widths).
|
||||
///
|
||||
/// Very fast collision detection. Use for:
|
||||
/// - Boxes and crates
|
||||
/// - Buildings and rooms
|
||||
/// - Most rectangular objects
|
||||
///
|
||||
/// # Parameters (3D)
|
||||
/// * `hx`, `hy`, `hz` - Half-extents (half the width) along each axis
|
||||
///
|
||||
/// **Example**: `cuboid(1.0, 0.5, 2.0)` creates a box with full size 2×1×4
|
||||
#[cfg(feature = "dim3")]
|
||||
pub fn cuboid(hx: Real, hy: Real, hz: Real) -> Self {
|
||||
Self::new(SharedShape::cuboid(hx, hy, hz))
|
||||
@@ -707,12 +868,17 @@ impl ColliderBuilder {
|
||||
Self::new(SharedShape::round_cuboid(hx, hy, hz, border_radius))
|
||||
}
|
||||
|
||||
/// Initializes a collider builder with a segment shape.
|
||||
/// Creates a line segment collider between two points.
|
||||
///
|
||||
/// Useful for thin barriers, edges, or 2D line-based collision.
|
||||
/// Has no thickness - purely a mathematical line.
|
||||
pub fn segment(a: Point<Real>, b: Point<Real>) -> Self {
|
||||
Self::new(SharedShape::segment(a, b))
|
||||
}
|
||||
|
||||
/// Initializes a collider builder with a triangle shape.
|
||||
/// Creates a single triangle collider.
|
||||
///
|
||||
/// Use for simple 3-sided shapes or as building blocks for more complex geometry.
|
||||
pub fn triangle(a: Point<Real>, b: Point<Real>, c: Point<Real>) -> Self {
|
||||
Self::new(SharedShape::triangle(a, b, c))
|
||||
}
|
||||
@@ -732,7 +898,34 @@ impl ColliderBuilder {
|
||||
Self::new(SharedShape::polyline(vertices, indices))
|
||||
}
|
||||
|
||||
/// Initializes a collider builder with a triangle mesh shape defined by its vertex and index buffers.
|
||||
/// Creates a triangle mesh collider from vertices and triangle indices.
|
||||
///
|
||||
/// Use for complex, arbitrary shapes like:
|
||||
/// - Level geometry and terrain
|
||||
/// - Imported 3D models
|
||||
/// - Custom irregular shapes
|
||||
///
|
||||
/// **Performance note**: Triangle meshes are slower than primitive shapes (balls, boxes, capsules).
|
||||
/// Consider using compound shapes or simpler approximations when possible.
|
||||
///
|
||||
/// # Parameters
|
||||
/// * `vertices` - Array of 3D points
|
||||
/// * `indices` - Array of triangles, each is 3 indices into the vertex array
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// use rapier3d::prelude::*;
|
||||
/// use nalgebra::Point3;
|
||||
///
|
||||
/// let vertices = vec![
|
||||
/// Point3::new(0.0, 0.0, 0.0),
|
||||
/// Point3::new(1.0, 0.0, 0.0),
|
||||
/// Point3::new(0.0, 1.0, 0.0),
|
||||
/// ];
|
||||
/// let triangle: [u32; 3] = [0, 1, 2];
|
||||
/// let indices = vec![triangle]; // One triangle
|
||||
/// let collider = ColliderBuilder::trimesh(vertices, indices)?;
|
||||
/// ```
|
||||
pub fn trimesh(
|
||||
vertices: Vec<Point<Real>>,
|
||||
indices: Vec<[u32; 3]>,
|
||||
@@ -767,8 +960,12 @@ impl ColliderBuilder {
|
||||
Ok(Self::new(shape).position(pose))
|
||||
}
|
||||
|
||||
/// Initializes a collider builder with a compound shape obtained from the decomposition of
|
||||
/// the given trimesh (in 3D) or polyline (in 2D) into convex parts.
|
||||
/// Creates a compound collider by decomposing a mesh/polyline into convex pieces.
|
||||
///
|
||||
/// Concave shapes (like an 'L' or 'C') are automatically broken into multiple convex
|
||||
/// parts for efficient collision detection. This is often faster than using a trimesh.
|
||||
///
|
||||
/// Uses the V-HACD algorithm. Good for imported models that aren't already convex.
|
||||
pub fn convex_decomposition(vertices: &[Point<Real>], indices: &[[u32; DIM]]) -> Self {
|
||||
Self::new(SharedShape::convex_decomposition(vertices, indices))
|
||||
}
|
||||
@@ -815,8 +1012,15 @@ impl ColliderBuilder {
|
||||
))
|
||||
}
|
||||
|
||||
/// Initializes a new collider builder with a 2D convex polygon or 3D convex polyhedron
|
||||
/// obtained after computing the convex-hull of the given points.
|
||||
/// Creates the smallest convex shape that contains all the given points.
|
||||
///
|
||||
/// Computes the "shrink-wrap" around a point cloud. Useful for:
|
||||
/// - Creating collision shapes from vertex data
|
||||
/// - Approximating complex shapes with a simpler convex one
|
||||
///
|
||||
/// Returns `None` if the points don't form a valid convex shape.
|
||||
///
|
||||
/// **Performance**: Convex shapes are much faster than triangle meshes!
|
||||
pub fn convex_hull(points: &[Point<Real>]) -> Option<Self> {
|
||||
SharedShape::convex_hull(points).map(Self::new)
|
||||
}
|
||||
@@ -871,8 +1075,21 @@ impl ColliderBuilder {
|
||||
Self::new(SharedShape::heightfield(heights, scale))
|
||||
}
|
||||
|
||||
/// Initializes a collider builder with a heightfield shape defined by its set of height and a scale
|
||||
/// factor along each coordinate axis.
|
||||
/// Creates a terrain/landscape collider from a 2D grid of height values.
|
||||
///
|
||||
/// Perfect for outdoor terrain in 3D games. The heightfield is a grid where each cell
|
||||
/// stores a height value, creating a landscape surface.
|
||||
///
|
||||
/// Use for:
|
||||
/// - Terrain and landscapes
|
||||
/// - Hills and valleys
|
||||
/// - Ground surfaces in open worlds
|
||||
///
|
||||
/// # Parameters
|
||||
/// * `heights` - 2D matrix of height values (Y coordinates)
|
||||
/// * `scale` - Size of each grid cell in X and Z directions
|
||||
///
|
||||
/// **Performance**: Much faster than triangle meshes for terrain!
|
||||
#[cfg(feature = "dim3")]
|
||||
pub fn heightfield(heights: na::DMatrix<Real>, scale: Vector<Real>) -> Self {
|
||||
Self::new(SharedShape::heightfield(heights, scale))
|
||||
@@ -889,77 +1106,142 @@ impl ColliderBuilder {
|
||||
Self::new(SharedShape::heightfield_with_flags(heights, scale, flags))
|
||||
}
|
||||
|
||||
/// The default friction coefficient used by the collider builder.
|
||||
/// Returns the default friction value used when not specified (0.5).
|
||||
pub fn default_friction() -> Real {
|
||||
0.5
|
||||
}
|
||||
|
||||
/// The default density used by the collider builder.
|
||||
/// Returns the default density value used when not specified (1.0).
|
||||
pub fn default_density() -> Real {
|
||||
100.0
|
||||
1.0
|
||||
}
|
||||
|
||||
/// Sets an arbitrary user-defined 128-bit integer associated to the colliders built by this builder.
|
||||
/// Stores custom user data with this collider (128-bit integer).
|
||||
///
|
||||
/// Use to associate game data (entity ID, type, etc.) with physics objects.
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// let collider = ColliderBuilder::ball(0.5)
|
||||
/// .user_data(entity_id as u128)
|
||||
/// .build();
|
||||
/// ```
|
||||
pub fn user_data(mut self, data: u128) -> Self {
|
||||
self.user_data = data;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the collision groups used by this collider.
|
||||
/// Sets which collision groups this collider belongs to and can interact with.
|
||||
///
|
||||
/// Two colliders will interact iff. their collision groups are compatible.
|
||||
/// See [InteractionGroups::test] for details.
|
||||
/// Use this to control what can collide with what (like collision layers).
|
||||
/// See [`InteractionGroups`] for examples.
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// // Player bullet: in group 1, only hits group 2 (enemies)
|
||||
/// let groups = InteractionGroups::new(Group::GROUP_1, Group::GROUP_2);
|
||||
/// let bullet = ColliderBuilder::ball(0.1)
|
||||
/// .collision_groups(groups)
|
||||
/// .build();
|
||||
/// ```
|
||||
pub fn collision_groups(mut self, groups: InteractionGroups) -> Self {
|
||||
self.collision_groups = groups;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the solver groups used by this collider.
|
||||
/// Sets solver groups (advanced collision filtering for contact resolution).
|
||||
///
|
||||
/// Forces between two colliders in contact will be computed iff their solver groups are
|
||||
/// compatible. See [InteractionGroups::test] for details.
|
||||
/// Similar to collision_groups but specifically for the contact solver.
|
||||
/// Most users should use `collision_groups()` instead - this is for advanced scenarios
|
||||
/// where you want collisions detected but not resolved (e.g., one-way platforms).
|
||||
pub fn solver_groups(mut self, groups: InteractionGroups) -> Self {
|
||||
self.solver_groups = groups;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether or not the collider built by this builder is a sensor.
|
||||
/// Makes this collider a sensor (trigger zone) instead of a solid collision shape.
|
||||
///
|
||||
/// Sensors detect overlaps but don't create physical collisions. Use for:
|
||||
/// - Trigger zones (checkpoints, danger areas)
|
||||
/// - Collectible item detection
|
||||
/// - Proximity sensors
|
||||
/// - Win/lose conditions
|
||||
///
|
||||
/// You'll receive collision events when objects enter/exit the sensor.
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// let trigger = ColliderBuilder::cuboid(5.0, 5.0, 5.0)
|
||||
/// .sensor(true)
|
||||
/// .build();
|
||||
/// ```
|
||||
pub fn sensor(mut self, is_sensor: bool) -> Self {
|
||||
self.is_sensor = is_sensor;
|
||||
self
|
||||
}
|
||||
|
||||
/// The set of physics hooks enabled for this collider.
|
||||
/// Enables custom physics hooks for this collider (advanced).
|
||||
///
|
||||
/// See [`ActiveHooks`](crate::pipeline::ActiveHooks) for details on custom collision filtering.
|
||||
pub fn active_hooks(mut self, active_hooks: ActiveHooks) -> Self {
|
||||
self.active_hooks = active_hooks;
|
||||
self
|
||||
}
|
||||
|
||||
/// The set of events enabled for this collider.
|
||||
/// Enables event generation for this collider.
|
||||
///
|
||||
/// Set to `ActiveEvents::COLLISION_EVENTS` for start/stop notifications.
|
||||
/// Set to `ActiveEvents::CONTACT_FORCE_EVENTS` for force threshold events.
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// let sensor = ColliderBuilder::ball(1.0)
|
||||
/// .sensor(true)
|
||||
/// .active_events(ActiveEvents::COLLISION_EVENTS)
|
||||
/// .build();
|
||||
/// ```
|
||||
pub fn active_events(mut self, active_events: ActiveEvents) -> Self {
|
||||
self.active_events = active_events;
|
||||
self
|
||||
}
|
||||
|
||||
/// The set of active collision types for this collider.
|
||||
/// Sets which body type combinations can collide with this collider.
|
||||
///
|
||||
/// See [`ActiveCollisionTypes`] for details. Most users don't need to change this.
|
||||
pub fn active_collision_types(mut self, active_collision_types: ActiveCollisionTypes) -> Self {
|
||||
self.active_collision_types = active_collision_types;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the friction coefficient of the collider this builder will build.
|
||||
/// Sets the friction coefficient (slipperiness) for this collider.
|
||||
///
|
||||
/// - `0.0` = ice (very slippery)
|
||||
/// - `0.5` = wood on wood
|
||||
/// - `1.0` = rubber (high grip)
|
||||
///
|
||||
/// Default is `0.5`.
|
||||
pub fn friction(mut self, friction: Real) -> Self {
|
||||
self.friction = friction;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the rule to be used to combine two friction coefficients in a contact.
|
||||
/// Sets how friction coefficients are combined when two colliders touch.
|
||||
///
|
||||
/// Options: Average, Min, Max, Multiply. Default is Average.
|
||||
/// Most games can ignore this and use the default.
|
||||
pub fn friction_combine_rule(mut self, rule: CoefficientCombineRule) -> Self {
|
||||
self.friction_combine_rule = rule;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the restitution coefficient of the collider this builder will build.
|
||||
/// Sets the restitution coefficient (bounciness) for this collider.
|
||||
///
|
||||
/// - `0.0` = no bounce (clay, soft)
|
||||
/// - `0.5` = moderate bounce
|
||||
/// - `1.0` = perfect elastic bounce
|
||||
/// - `>1.0` = super bouncy (gains energy!)
|
||||
///
|
||||
/// Default is `0.0`.
|
||||
pub fn restitution(mut self, restitution: Real) -> Self {
|
||||
self.restitution = restitution;
|
||||
self
|
||||
@@ -971,25 +1253,35 @@ impl ColliderBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the uniform density of the collider this builder will build.
|
||||
/// Sets the density (mass per unit volume) of this collider.
|
||||
///
|
||||
/// This will be overridden by a call to [`Self::mass`] or [`Self::mass_properties`] so it only
|
||||
/// makes sense to call either [`Self::density`] or [`Self::mass`] or [`Self::mass_properties`].
|
||||
/// Mass will be computed as: `density × volume`. Common densities:
|
||||
/// - `1000.0` = water
|
||||
/// - `2700.0` = aluminum
|
||||
/// - `7850.0` = steel
|
||||
///
|
||||
/// The mass and angular inertia of this collider will be computed automatically based on its
|
||||
/// shape.
|
||||
/// ⚠️ Use either `density()` OR `mass()`, not both (last call wins).
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// let steel_ball = ColliderBuilder::ball(0.5).density(7850.0).build();
|
||||
/// ```
|
||||
pub fn density(mut self, density: Real) -> Self {
|
||||
self.mass_properties = ColliderMassProps::Density(density);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the mass of the collider this builder will build.
|
||||
/// Sets the total mass of this collider directly.
|
||||
///
|
||||
/// This will be overridden by a call to [`Self::density`] or [`Self::mass_properties`] so it only
|
||||
/// makes sense to call either [`Self::density`] or [`Self::mass`] or [`Self::mass_properties`].
|
||||
/// Angular inertia is computed automatically from the shape and mass.
|
||||
///
|
||||
/// The angular inertia of this collider will be computed automatically based on its shape
|
||||
/// and this mass value.
|
||||
/// ⚠️ Use either `mass()` OR `density()`, not both (last call wins).
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// // 10kg ball regardless of its radius
|
||||
/// let collider = ColliderBuilder::ball(0.5).mass(10.0).build();
|
||||
/// ```
|
||||
pub fn mass(mut self, mass: Real) -> Self {
|
||||
self.mass_properties = ColliderMassProps::Mass(mass);
|
||||
self
|
||||
@@ -1004,34 +1296,55 @@ impl ColliderBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the total force magnitude beyond which a contact force event can be emitted.
|
||||
/// Sets the force threshold for triggering contact force events.
|
||||
///
|
||||
/// When total contact force exceeds this value, a `ContactForceEvent` is generated
|
||||
/// (if `ActiveEvents::CONTACT_FORCE_EVENTS` is enabled).
|
||||
///
|
||||
/// Use for detecting hard impacts, breaking objects, or damage systems.
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// let glass = ColliderBuilder::cuboid(1.0, 1.0, 0.1)
|
||||
/// .active_events(ActiveEvents::CONTACT_FORCE_EVENTS)
|
||||
/// .contact_force_event_threshold(1000.0) // Break at 1000N
|
||||
/// .build();
|
||||
/// ```
|
||||
pub fn contact_force_event_threshold(mut self, threshold: Real) -> Self {
|
||||
self.contact_force_event_threshold = threshold;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the initial translation of the collider to be created.
|
||||
/// Sets where the collider sits relative to its parent body.
|
||||
///
|
||||
/// If the collider will be attached to a rigid-body, this sets the translation relative to the
|
||||
/// rigid-body it will be attached to.
|
||||
/// For attached colliders, this is the offset from the body's origin.
|
||||
/// For standalone colliders, this is the world position.
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// // Collider offset 2 units to the right of the body
|
||||
/// let collider = ColliderBuilder::ball(0.5)
|
||||
/// .translation(vector![2.0, 0.0, 0.0])
|
||||
/// .build();
|
||||
/// ```
|
||||
pub fn translation(mut self, translation: Vector<Real>) -> Self {
|
||||
self.position.translation.vector = translation;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the initial orientation of the collider to be created.
|
||||
/// Sets the collider's rotation relative to its parent body.
|
||||
///
|
||||
/// If the collider will be attached to a rigid-body, this sets the orientation relative to the
|
||||
/// rigid-body it will be attached to.
|
||||
/// For attached colliders, this rotates the collider relative to the body.
|
||||
/// For standalone colliders, this is the world rotation.
|
||||
pub fn rotation(mut self, angle: AngVector<Real>) -> Self {
|
||||
self.position.rotation = Rotation::new(angle);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the initial position (translation and orientation) of the collider to be created.
|
||||
/// Sets the collider's full pose (position + rotation) relative to its parent.
|
||||
///
|
||||
/// If the collider will be attached to a rigid-body, this sets the position relative
|
||||
/// to the rigid-body it will be attached to.
|
||||
/// For attached colliders, this is relative to the parent body.
|
||||
/// For standalone colliders, this is the world pose.
|
||||
pub fn position(mut self, pos: Isometry<Real>) -> Self {
|
||||
self.position = pos;
|
||||
self
|
||||
@@ -1066,13 +1379,23 @@ impl ColliderBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable or disable the collider after its creation.
|
||||
/// Sets whether this collider starts enabled or disabled.
|
||||
///
|
||||
/// Default is `true` (enabled). Set to `false` to create a disabled collider.
|
||||
pub fn enabled(mut self, enabled: bool) -> Self {
|
||||
self.enabled = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds a new collider attached to the given rigid-body.
|
||||
/// Finalizes the collider and returns it, ready to be added to the world.
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// let collider = ColliderBuilder::ball(0.5)
|
||||
/// .friction(0.7)
|
||||
/// .build();
|
||||
/// colliders.insert_with_parent(collider, body_handle, &mut bodies);
|
||||
/// ```
|
||||
pub fn build(&self) -> Collider {
|
||||
let shape = self.shape.clone();
|
||||
let material = ColliderMaterial {
|
||||
|
||||
@@ -277,29 +277,45 @@ impl Default for ColliderMaterial {
|
||||
bitflags::bitflags! {
|
||||
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
/// Flags affecting whether or not collision-detection happens between two colliders
|
||||
/// depending on the type of rigid-bodies they are attached to.
|
||||
/// Controls which combinations of body types can collide with each other.
|
||||
///
|
||||
/// By default, Rapier only detects collisions between pairs that make physical sense
|
||||
/// (e.g., dynamic-dynamic, dynamic-fixed). Use this to customize that behavior.
|
||||
///
|
||||
/// **Most users don't need to change this** - the defaults are correct for normal physics.
|
||||
///
|
||||
/// ## Default behavior
|
||||
/// - ✅ Dynamic ↔ Dynamic (moving objects collide)
|
||||
/// - ✅ Dynamic ↔ Fixed (moving objects hit walls)
|
||||
/// - ✅ Dynamic ↔ Kinematic (moving objects hit platforms)
|
||||
/// - ❌ Fixed ↔ Fixed (walls don't collide with each other - waste of CPU)
|
||||
/// - ❌ Kinematic ↔ Kinematic (platforms don't collide - they're user-controlled)
|
||||
/// - ❌ Kinematic ↔ Fixed (platforms don't collide with walls)
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use rapier3d::prelude::*;
|
||||
/// # let mut colliders = ColliderSet::new();
|
||||
/// # let mut bodies = RigidBodySet::new();
|
||||
/// # let body_handle = bodies.insert(RigidBodyBuilder::dynamic());
|
||||
/// # let collider_handle = colliders.insert_with_parent(ColliderBuilder::ball(0.5), body_handle, &mut bodies);
|
||||
/// # let collider = colliders.get_mut(collider_handle).unwrap();
|
||||
/// // Enable kinematic-kinematic collisions (unusual)
|
||||
/// let types = ActiveCollisionTypes::default() | ActiveCollisionTypes::KINEMATIC_KINEMATIC;
|
||||
/// collider.set_active_collision_types(types);
|
||||
/// ```
|
||||
pub struct ActiveCollisionTypes: u16 {
|
||||
/// Enable collision-detection between a collider attached to a dynamic body
|
||||
/// and another collider attached to a dynamic body.
|
||||
/// Enables dynamic ↔ dynamic collision detection.
|
||||
const DYNAMIC_DYNAMIC = 0b0000_0000_0000_0001;
|
||||
/// Enable collision-detection between a collider attached to a dynamic body
|
||||
/// and another collider attached to a kinematic body.
|
||||
/// Enables dynamic ↔ kinematic collision detection.
|
||||
const DYNAMIC_KINEMATIC = 0b0000_0000_0000_1100;
|
||||
/// Enable collision-detection between a collider attached to a dynamic body
|
||||
/// and another collider attached to a fixed body (or not attached to any body).
|
||||
/// Enables dynamic ↔ fixed collision detection.
|
||||
const DYNAMIC_FIXED = 0b0000_0000_0000_0010;
|
||||
/// Enable collision-detection between a collider attached to a kinematic body
|
||||
/// and another collider attached to a kinematic body.
|
||||
/// Enables kinematic ↔ kinematic collision detection (rarely needed).
|
||||
const KINEMATIC_KINEMATIC = 0b1100_1100_0000_0000;
|
||||
|
||||
/// Enable collision-detection between a collider attached to a kinematic body
|
||||
/// and another collider attached to a fixed body (or not attached to any body).
|
||||
/// Enables kinematic ↔ fixed collision detection (rarely needed).
|
||||
const KINEMATIC_FIXED = 0b0010_0010_0000_0000;
|
||||
|
||||
/// Enable collision-detection between a collider attached to a fixed body (or
|
||||
/// not attached to any body) and another collider attached to a fixed body (or
|
||||
/// not attached to any body).
|
||||
/// Enables fixed ↔ fixed collision detection (rarely needed).
|
||||
const FIXED_FIXED = 0b0000_0000_0010_0000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,29 @@ impl HasModifiedFlag for Collider {
|
||||
|
||||
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
|
||||
#[derive(Clone, Default, Debug)]
|
||||
/// A set of colliders that can be handled by a physics `World`.
|
||||
/// The collection that stores all colliders (collision shapes) in your physics world.
|
||||
///
|
||||
/// Similar to [`RigidBodySet`](crate::dynamics::RigidBodySet), this is the "database" where
|
||||
/// all your collision shapes live. Each collider can be attached to a rigid body or exist
|
||||
/// independently.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use rapier3d::prelude::*;
|
||||
/// let mut colliders = ColliderSet::new();
|
||||
/// # let mut bodies = RigidBodySet::new();
|
||||
/// # let body_handle = bodies.insert(RigidBodyBuilder::dynamic());
|
||||
///
|
||||
/// // Add a standalone collider (no parent body)
|
||||
/// let handle = colliders.insert(ColliderBuilder::ball(0.5));
|
||||
///
|
||||
/// // Or attach it to a body
|
||||
/// let handle = colliders.insert_with_parent(
|
||||
/// ColliderBuilder::cuboid(1.0, 1.0, 1.0),
|
||||
/// body_handle,
|
||||
/// &mut bodies
|
||||
/// );
|
||||
/// ```
|
||||
pub struct ColliderSet {
|
||||
pub(crate) colliders: Arena<Collider>,
|
||||
pub(crate) modified_colliders: ModifiedColliders,
|
||||
@@ -29,7 +51,7 @@ pub struct ColliderSet {
|
||||
}
|
||||
|
||||
impl ColliderSet {
|
||||
/// Create a new empty set of colliders.
|
||||
/// Creates a new empty collection of colliders.
|
||||
pub fn new() -> Self {
|
||||
ColliderSet {
|
||||
colliders: Arena::new(),
|
||||
@@ -38,9 +60,9 @@ impl ColliderSet {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new set of colliders, with an initial capacity
|
||||
/// for the set of colliders as well as the tracking of
|
||||
/// modified colliders.
|
||||
/// Creates a new collection with pre-allocated space for the given number of colliders.
|
||||
///
|
||||
/// Use this if you know approximately how many colliders you'll need.
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
ColliderSet {
|
||||
colliders: Arena::with_capacity(capacity),
|
||||
@@ -61,17 +83,23 @@ impl ColliderSet {
|
||||
std::mem::take(&mut self.removed_colliders)
|
||||
}
|
||||
|
||||
/// An always-invalid collider handle.
|
||||
/// Returns a handle that's guaranteed to be invalid.
|
||||
///
|
||||
/// Useful as a sentinel/placeholder value.
|
||||
pub fn invalid_handle() -> ColliderHandle {
|
||||
ColliderHandle::from_raw_parts(crate::INVALID_U32, crate::INVALID_U32)
|
||||
}
|
||||
|
||||
/// Iterate through all the colliders on this set.
|
||||
/// Iterates over all colliders in this collection.
|
||||
///
|
||||
/// Yields `(handle, &Collider)` pairs for each collider (including disabled ones).
|
||||
pub fn iter(&self) -> impl ExactSizeIterator<Item = (ColliderHandle, &Collider)> {
|
||||
self.colliders.iter().map(|(h, c)| (ColliderHandle(h), c))
|
||||
}
|
||||
|
||||
/// Iterate through all the enabled colliders on this set.
|
||||
/// Iterates over only the enabled colliders.
|
||||
///
|
||||
/// Disabled colliders are excluded from physics simulation and queries.
|
||||
pub fn iter_enabled(&self) -> impl Iterator<Item = (ColliderHandle, &Collider)> {
|
||||
self.colliders
|
||||
.iter()
|
||||
@@ -79,7 +107,7 @@ impl ColliderSet {
|
||||
.filter(|(_, c)| c.is_enabled())
|
||||
}
|
||||
|
||||
/// Iterates mutably through all the colliders on this set.
|
||||
/// Iterates over all colliders with mutable access.
|
||||
#[cfg(not(feature = "dev-remove-slow-accessors"))]
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = (ColliderHandle, &mut Collider)> {
|
||||
self.modified_colliders.clear();
|
||||
@@ -92,28 +120,31 @@ impl ColliderSet {
|
||||
})
|
||||
}
|
||||
|
||||
/// Iterates mutably through all the enabled colliders on this set.
|
||||
/// Iterates over only the enabled colliders with mutable access.
|
||||
#[cfg(not(feature = "dev-remove-slow-accessors"))]
|
||||
pub fn iter_enabled_mut(&mut self) -> impl Iterator<Item = (ColliderHandle, &mut Collider)> {
|
||||
self.iter_mut().filter(|(_, c)| c.is_enabled())
|
||||
}
|
||||
|
||||
/// The number of colliders on this set.
|
||||
/// Returns how many colliders are currently in this collection.
|
||||
pub fn len(&self) -> usize {
|
||||
self.colliders.len()
|
||||
}
|
||||
|
||||
/// `true` if there are no colliders in this set.
|
||||
/// Returns `true` if there are no colliders in this collection.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.colliders.is_empty()
|
||||
}
|
||||
|
||||
/// Is this collider handle valid?
|
||||
/// Checks if the given handle points to a valid collider that still exists.
|
||||
pub fn contains(&self, handle: ColliderHandle) -> bool {
|
||||
self.colliders.contains(handle.0)
|
||||
}
|
||||
|
||||
/// Inserts a new collider to this set and retrieve its handle.
|
||||
/// Adds a standalone collider (not attached to any body) and returns its handle.
|
||||
///
|
||||
/// Most colliders should be attached to rigid bodies using [`insert_with_parent()`](Self::insert_with_parent) instead.
|
||||
/// Standalone colliders are useful for sensors or static collision geometry that doesn't need a body.
|
||||
pub fn insert(&mut self, coll: impl Into<Collider>) -> ColliderHandle {
|
||||
let mut coll = coll.into();
|
||||
// Make sure the internal links are reset, they may not be
|
||||
@@ -129,7 +160,24 @@ impl ColliderSet {
|
||||
handle
|
||||
}
|
||||
|
||||
/// Inserts a new collider to this set, attach it to the given rigid-body, and retrieve its handle.
|
||||
/// Adds a collider attached to a rigid body and returns its handle.
|
||||
///
|
||||
/// This is the most common way to add colliders. The collider's position is relative
|
||||
/// to its parent body, so when the body moves, the collider moves with it.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use rapier3d::prelude::*;
|
||||
/// # let mut colliders = ColliderSet::new();
|
||||
/// # let mut bodies = RigidBodySet::new();
|
||||
/// # let body_handle = bodies.insert(RigidBodyBuilder::dynamic());
|
||||
/// // Create a ball collider attached to a dynamic body
|
||||
/// let collider_handle = colliders.insert_with_parent(
|
||||
/// ColliderBuilder::ball(0.5),
|
||||
/// body_handle,
|
||||
/// &mut bodies
|
||||
/// );
|
||||
/// ```
|
||||
pub fn insert_with_parent(
|
||||
&mut self,
|
||||
coll: impl Into<Collider>,
|
||||
@@ -172,8 +220,27 @@ impl ColliderSet {
|
||||
handle
|
||||
}
|
||||
|
||||
/// Sets the parent of the given collider.
|
||||
// TODO: find a way to define this as a method of Collider.
|
||||
/// Changes which rigid body a collider is attached to, or detaches it completely.
|
||||
///
|
||||
/// Use this to move a collider from one body to another, or to make it standalone.
|
||||
///
|
||||
/// # Parameters
|
||||
/// * `new_parent_handle` - `Some(handle)` to attach to a body, `None` to make standalone
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use rapier3d::prelude::*;
|
||||
/// # let mut colliders = ColliderSet::new();
|
||||
/// # let mut bodies = RigidBodySet::new();
|
||||
/// # let body_handle = bodies.insert(RigidBodyBuilder::dynamic());
|
||||
/// # let other_body = bodies.insert(RigidBodyBuilder::dynamic());
|
||||
/// # let collider_handle = colliders.insert_with_parent(ColliderBuilder::ball(0.5).build(), body_handle, &mut bodies);
|
||||
/// // Detach collider from its current body
|
||||
/// colliders.set_parent(collider_handle, None, &mut bodies);
|
||||
///
|
||||
/// // Attach it to a different body
|
||||
/// colliders.set_parent(collider_handle, Some(other_body), &mut bodies);
|
||||
/// ```
|
||||
pub fn set_parent(
|
||||
&mut self,
|
||||
handle: ColliderHandle,
|
||||
@@ -220,10 +287,32 @@ impl ColliderSet {
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove a collider from this set and update its parent accordingly.
|
||||
/// Removes a collider from the world.
|
||||
///
|
||||
/// If `wake_up` is `true`, the rigid-body the removed collider is attached to
|
||||
/// will be woken up.
|
||||
/// The collider is detached from its parent body (if any) and removed from all
|
||||
/// collision detection structures. Returns the removed collider if it existed.
|
||||
///
|
||||
/// # Parameters
|
||||
/// * `wake_up` - If `true`, wakes up the parent body (useful when collider removal
|
||||
/// changes the body's mass or collision behavior significantly)
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use rapier3d::prelude::*;
|
||||
/// # let mut colliders = ColliderSet::new();
|
||||
/// # let mut bodies = RigidBodySet::new();
|
||||
/// # let mut islands = IslandManager::new();
|
||||
/// # let body_handle = bodies.insert(RigidBodyBuilder::dynamic().build());
|
||||
/// # let handle = colliders.insert_with_parent(ColliderBuilder::ball(0.5).build(), body_handle, &mut bodies);
|
||||
/// if let Some(collider) = colliders.remove(
|
||||
/// handle,
|
||||
/// &mut islands,
|
||||
/// &mut bodies,
|
||||
/// true // Wake up the parent body
|
||||
/// ) {
|
||||
/// println!("Removed collider with shape: {:?}", collider.shared_shape());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn remove(
|
||||
&mut self,
|
||||
handle: ColliderHandle,
|
||||
@@ -258,29 +347,18 @@ impl ColliderSet {
|
||||
Some(collider)
|
||||
}
|
||||
|
||||
/// Gets the collider with the given handle without a known generation.
|
||||
/// Gets a collider by its index without knowing the generation number.
|
||||
///
|
||||
/// This is useful when you know you want the collider at position `i` but
|
||||
/// don't know what is its current generation number. Generation numbers are
|
||||
/// used to protect from the ABA problem because the collider position `i`
|
||||
/// are recycled between two insertion and a removal.
|
||||
///
|
||||
/// Using this is discouraged in favor of `self.get(handle)` which does not
|
||||
/// suffer form the ABA problem.
|
||||
/// ⚠️ **Advanced/unsafe usage** - prefer [`get()`](Self::get) instead! See [`RigidBodySet::get_unknown_gen`] for details.
|
||||
pub fn get_unknown_gen(&self, i: u32) -> Option<(&Collider, ColliderHandle)> {
|
||||
self.colliders
|
||||
.get_unknown_gen(i)
|
||||
.map(|(c, h)| (c, ColliderHandle(h)))
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the collider with the given handle without a known generation.
|
||||
/// Gets a mutable reference to a collider by its index without knowing the generation.
|
||||
///
|
||||
/// This is useful when you know you want the collider at position `i` but
|
||||
/// don't know what is its current generation number. Generation numbers are
|
||||
/// used to protect from the ABA problem because the collider position `i`
|
||||
/// are recycled between two insertion and a removal.
|
||||
///
|
||||
/// Using this is discouraged in favor of `self.get_mut(handle)` which does not
|
||||
/// ⚠️ **Advanced/unsafe usage** - prefer [`get_mut()`](Self::get_mut) instead!
|
||||
/// suffer form the ABA problem.
|
||||
#[cfg(not(feature = "dev-remove-slow-accessors"))]
|
||||
pub fn get_unknown_gen_mut(&mut self, i: u32) -> Option<(&mut Collider, ColliderHandle)> {
|
||||
@@ -290,12 +368,17 @@ impl ColliderSet {
|
||||
Some((collider, handle))
|
||||
}
|
||||
|
||||
/// Get the collider with the given handle.
|
||||
/// Gets a read-only reference to the collider with the given handle.
|
||||
///
|
||||
/// Returns `None` if the handle is invalid or the collider was removed.
|
||||
pub fn get(&self, handle: ColliderHandle) -> Option<&Collider> {
|
||||
self.colliders.get(handle.0)
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the collider with the given handle.
|
||||
///
|
||||
/// Returns `None` if the handle is invalid or the collider was removed.
|
||||
/// Use this to modify collider properties like friction, restitution, sensor status, etc.
|
||||
#[cfg(not(feature = "dev-remove-slow-accessors"))]
|
||||
pub fn get_mut(&mut self, handle: ColliderHandle) -> Option<&mut Collider> {
|
||||
let result = self.colliders.get_mut(handle.0)?;
|
||||
@@ -303,9 +386,10 @@ impl ColliderSet {
|
||||
Some(result)
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the two colliders with the given handles.
|
||||
/// Gets mutable references to two different colliders at once.
|
||||
///
|
||||
/// If `handle1 == handle2`, only the first returned value will be `Some`.
|
||||
/// Useful when you need to modify two colliders simultaneously. If both handles
|
||||
/// are the same, only the first value will be `Some`.
|
||||
#[cfg(not(feature = "dev-remove-slow-accessors"))]
|
||||
pub fn get_pair_mut(
|
||||
&mut self,
|
||||
|
||||
@@ -115,7 +115,34 @@ impl IntersectionPair {
|
||||
|
||||
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
|
||||
#[derive(Clone)]
|
||||
/// The description of all the contacts between a pair of colliders.
|
||||
/// All contact information between two colliding colliders.
|
||||
///
|
||||
/// When two colliders are touching, a ContactPair stores all the contact points, normals,
|
||||
/// and forces between them. You can access this through the narrow phase or in event handlers.
|
||||
///
|
||||
/// ## Contact manifolds
|
||||
///
|
||||
/// The contacts are organized into "manifolds" - groups of contact points that share similar
|
||||
/// properties (like being on the same face). Most collider pairs have 1 manifold, but complex
|
||||
/// shapes may have multiple.
|
||||
///
|
||||
/// ## Use cases
|
||||
///
|
||||
/// - Reading contact normals for custom physics
|
||||
/// - Checking penetration depth
|
||||
/// - Analyzing impact forces
|
||||
/// - Implementing custom contact responses
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use rapier3d::prelude::*;
|
||||
/// # use rapier3d::geometry::ContactPair;
|
||||
/// # let contact_pair = ContactPair::default();
|
||||
/// if let Some((manifold, contact)) = contact_pair.find_deepest_contact() {
|
||||
/// println!("Deepest penetration: {}", -contact.dist);
|
||||
/// println!("Contact normal: {:?}", manifold.data.normal);
|
||||
/// }
|
||||
/// ```
|
||||
pub struct ContactPair {
|
||||
/// The first collider involved in the contact pair.
|
||||
pub collider1: ColliderHandle,
|
||||
@@ -135,6 +162,12 @@ pub struct ContactPair {
|
||||
pub(crate) workspace: Option<ContactManifoldsWorkspace>,
|
||||
}
|
||||
|
||||
impl Default for ContactPair {
|
||||
fn default() -> Self {
|
||||
Self::new(ColliderHandle::invalid(), ColliderHandle::invalid())
|
||||
}
|
||||
}
|
||||
|
||||
impl ContactPair {
|
||||
pub(crate) fn new(collider1: ColliderHandle, collider2: ColliderHandle) -> Self {
|
||||
Self {
|
||||
@@ -154,7 +187,10 @@ impl ContactPair {
|
||||
self.workspace = None;
|
||||
}
|
||||
|
||||
/// The sum of all the impulses applied by contacts on this contact pair.
|
||||
/// The total impulse (force × time) applied by all contacts.
|
||||
///
|
||||
/// This is the accumulated force that pushed the colliders apart.
|
||||
/// Useful for determining impact strength.
|
||||
pub fn total_impulse(&self) -> Vector<Real> {
|
||||
self.manifolds
|
||||
.iter()
|
||||
@@ -162,14 +198,18 @@ impl ContactPair {
|
||||
.sum()
|
||||
}
|
||||
|
||||
/// The sum of the magnitudes of the contacts on this contact pair.
|
||||
/// The total magnitude of all contact impulses (sum of lengths, not length of sum).
|
||||
///
|
||||
/// This is what's compared against `contact_force_event_threshold`.
|
||||
pub fn total_impulse_magnitude(&self) -> Real {
|
||||
self.manifolds
|
||||
.iter()
|
||||
.fold(0.0, |a, m| a + m.total_impulse())
|
||||
}
|
||||
|
||||
/// The magnitude and (unit) direction of the maximum impulse on this contact pair.
|
||||
/// Finds the strongest contact impulse and its direction.
|
||||
///
|
||||
/// Returns `(magnitude, normal_direction)` of the strongest individual contact.
|
||||
pub fn max_impulse(&self) -> (Real, Vector<Real>) {
|
||||
let mut result = (0.0, Vector::zeros());
|
||||
|
||||
@@ -184,13 +224,26 @@ impl ContactPair {
|
||||
result
|
||||
}
|
||||
|
||||
/// Finds the contact with the smallest signed distance.
|
||||
/// Finds the contact point with the deepest penetration.
|
||||
///
|
||||
/// If the colliders involved in this contact pair are penetrating, then
|
||||
/// this returns the contact with the largest penetration depth.
|
||||
/// When objects overlap, this returns the contact point that's penetrating the most.
|
||||
/// Useful for:
|
||||
/// - Finding the "worst" overlap
|
||||
/// - Determining primary contact direction
|
||||
/// - Custom penetration resolution
|
||||
///
|
||||
/// Returns a reference to the contact, as well as the contact manifold
|
||||
/// it is part of.
|
||||
/// Returns both the contact point and its parent manifold.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use rapier3d::prelude::*;
|
||||
/// # use rapier3d::geometry::ContactPair;
|
||||
/// # let pair = ContactPair::default();
|
||||
/// if let Some((manifold, contact)) = pair.find_deepest_contact() {
|
||||
/// let penetration_depth = -contact.dist; // Negative dist = penetration
|
||||
/// println!("Deepest penetration: {} units", penetration_depth);
|
||||
/// }
|
||||
/// ```
|
||||
#[profiling::function]
|
||||
pub fn find_deepest_contact(&self) -> Option<(&ContactManifold, &Contact)> {
|
||||
let mut deepest = None;
|
||||
|
||||
@@ -64,15 +64,7 @@ impl<N: Copy, E> InteractionGraph<N, E> {
|
||||
/// When a node is removed, another node of the graph takes it place. This means that the `ColliderGraphIndex`
|
||||
/// of the collision object returned by this method will be equal to `id`. Thus if you maintain
|
||||
/// a map between `CollisionObjectSlabHandle` and `ColliderGraphIndex`, then you should update this
|
||||
/// map to associate `id` to the handle returned by this method. For example:
|
||||
///
|
||||
/// ```ignore
|
||||
/// // Let `id` be the graph index of the collision object we want to remove.
|
||||
/// if let Some(other_handle) = graph.remove_node(id) {
|
||||
/// // The graph index of `other_handle` changed to `id` due to the removal.
|
||||
/// map.insert(other_handle, id) ;
|
||||
/// }
|
||||
/// ```
|
||||
/// map to associate `id` to the handle returned by this method.
|
||||
#[must_use = "The graph index of the collision object returned by this method has been changed to `id`."]
|
||||
pub(crate) fn remove_node(&mut self, id: ColliderGraphIndex) -> Option<N> {
|
||||
let _ = self.graph.remove_node(id);
|
||||
|
||||
@@ -1,19 +1,41 @@
|
||||
#![allow(clippy::bad_bit_mask)] // Clippy will complain about the bitmasks due to Group::NONE being 0.
|
||||
|
||||
/// Pairwise filtering using bit masks.
|
||||
/// Collision filtering system that controls which colliders can interact with each other.
|
||||
///
|
||||
/// This filtering method is based on two 32-bit values:
|
||||
/// - The interaction groups memberships.
|
||||
/// - The interaction groups filter.
|
||||
/// Think of this as "collision layers" in game engines. Each collider has:
|
||||
/// - **Memberships**: What groups does this collider belong to? (up to 32 groups)
|
||||
/// - **Filter**: What groups can this collider interact with?
|
||||
///
|
||||
/// An interaction is allowed between two filters `a` and `b` when two conditions
|
||||
/// are met simultaneously:
|
||||
/// - The groups membership of `a` has at least one bit set to `1` in common with the groups filter of `b`.
|
||||
/// - The groups membership of `b` has at least one bit set to `1` in common with the groups filter of `a`.
|
||||
/// Two colliders interact only if:
|
||||
/// 1. Collider A's memberships overlap with Collider B's filter, AND
|
||||
/// 2. Collider B's memberships overlap with Collider A's filter
|
||||
///
|
||||
/// In other words, interactions are allowed between two filter iff. the following condition is met:
|
||||
/// ```ignore
|
||||
/// (self.memberships & rhs.filter) != 0 && (rhs.memberships & self.filter) != 0
|
||||
/// # Common use cases
|
||||
///
|
||||
/// - **Player vs. Enemy bullets**: Players in group 1, enemies in group 2. Player bullets
|
||||
/// only hit group 2, enemy bullets only hit group 1.
|
||||
/// - **Trigger zones**: Sensors that only detect specific object types.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use rapier3d::geometry::{InteractionGroups, Group};
|
||||
/// // Player collider: in group 1, collides with groups 2 and 3
|
||||
/// let player_groups = InteractionGroups::new(
|
||||
/// Group::GROUP_1, // I am in group 1
|
||||
/// Group::GROUP_2 | Group::GROUP_3 // I collide with groups 2 and 3
|
||||
/// );
|
||||
///
|
||||
/// // Enemy collider: in group 2, collides with group 1
|
||||
/// let enemy_groups = InteractionGroups::new(
|
||||
/// Group::GROUP_2, // I am in group 2
|
||||
/// Group::GROUP_1 // I collide with group 1
|
||||
/// );
|
||||
///
|
||||
/// // These will collide because:
|
||||
/// // - Player's membership (GROUP_1) is in enemy's filter (GROUP_1) ✓
|
||||
/// // - Enemy's membership (GROUP_2) is in player's filter (GROUP_2) ✓
|
||||
/// assert!(player_groups.test(enemy_groups));
|
||||
/// ```
|
||||
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
@@ -34,12 +56,16 @@ impl InteractionGroups {
|
||||
}
|
||||
}
|
||||
|
||||
/// Allow interaction with everything.
|
||||
/// Creates a filter that allows interactions with everything (default behavior).
|
||||
///
|
||||
/// The collider is in all groups and collides with all groups.
|
||||
pub const fn all() -> Self {
|
||||
Self::new(Group::ALL, Group::ALL)
|
||||
}
|
||||
|
||||
/// Prevent all interactions.
|
||||
/// Creates a filter that prevents all interactions.
|
||||
///
|
||||
/// The collider won't collide with anything. Useful for temporarily disabled colliders.
|
||||
pub const fn none() -> Self {
|
||||
Self::new(Group::NONE, Group::NONE)
|
||||
}
|
||||
|
||||
@@ -74,33 +74,61 @@ bitflags::bitflags! {
|
||||
|
||||
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
|
||||
#[derive(Copy, Clone, Hash, Debug)]
|
||||
/// Events occurring when two colliders start or stop colliding
|
||||
/// Events triggered when two colliders start or stop touching.
|
||||
///
|
||||
/// Receive these through an [`EventHandler`](crate::pipeline::EventHandler) implementation.
|
||||
/// At least one collider must have [`ActiveEvents::COLLISION_EVENTS`](crate::pipeline::ActiveEvents::COLLISION_EVENTS) enabled.
|
||||
///
|
||||
/// Use for:
|
||||
/// - Trigger zones (player entered/exited area)
|
||||
/// - Collectible items (player touched coin)
|
||||
/// - Sound effects (objects started colliding)
|
||||
/// - Game logic based on contact state
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use rapier3d::prelude::*;
|
||||
/// # let h1 = ColliderHandle::from_raw_parts(0, 0);
|
||||
/// # let h2 = ColliderHandle::from_raw_parts(1, 0);
|
||||
/// # let event = CollisionEvent::Started(h1, h2, CollisionEventFlags::empty());
|
||||
/// match event {
|
||||
/// CollisionEvent::Started(h1, h2, flags) => {
|
||||
/// println!("Colliders {:?} and {:?} started touching", h1, h2);
|
||||
/// if flags.contains(CollisionEventFlags::SENSOR) {
|
||||
/// println!("At least one is a sensor!");
|
||||
/// }
|
||||
/// }
|
||||
/// CollisionEvent::Stopped(h1, h2, _) => {
|
||||
/// println!("Colliders {:?} and {:?} stopped touching", h1, h2);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub enum CollisionEvent {
|
||||
/// Event occurring when two colliders start colliding
|
||||
/// Two colliders just started touching this frame.
|
||||
Started(ColliderHandle, ColliderHandle, CollisionEventFlags),
|
||||
/// Event occurring when two colliders stop colliding.
|
||||
/// Two colliders just stopped touching this frame.
|
||||
Stopped(ColliderHandle, ColliderHandle, CollisionEventFlags),
|
||||
}
|
||||
|
||||
impl CollisionEvent {
|
||||
/// Is this a `Started` collision event?
|
||||
/// Returns `true` if this is a Started event (colliders began touching).
|
||||
pub fn started(self) -> bool {
|
||||
matches!(self, CollisionEvent::Started(..))
|
||||
}
|
||||
|
||||
/// Is this a `Stopped` collision event?
|
||||
/// Returns `true` if this is a Stopped event (colliders stopped touching).
|
||||
pub fn stopped(self) -> bool {
|
||||
matches!(self, CollisionEvent::Stopped(..))
|
||||
}
|
||||
|
||||
/// The handle of the first collider involved in this collision event.
|
||||
/// Returns the handle of the first collider in this collision.
|
||||
pub fn collider1(self) -> ColliderHandle {
|
||||
match self {
|
||||
Self::Started(h, _, _) | Self::Stopped(h, _, _) => h,
|
||||
}
|
||||
}
|
||||
|
||||
/// The handle of the second collider involved in this collision event.
|
||||
/// Returns the handle of the second collider in this collision.
|
||||
pub fn collider2(self) -> ColliderHandle {
|
||||
match self {
|
||||
Self::Started(_, h, _) | Self::Stopped(_, h, _) => h,
|
||||
|
||||
@@ -47,7 +47,18 @@ enum PairRemovalMode {
|
||||
Auto,
|
||||
}
|
||||
|
||||
/// The narrow-phase responsible for computing precise contact information between colliders.
|
||||
/// The narrow-phase collision detector that computes precise contact points between colliders.
|
||||
///
|
||||
/// After the broad-phase quickly filters out distant object pairs, the narrow-phase performs
|
||||
/// detailed geometric computations to find exact:
|
||||
/// - Contact points (where surfaces touch)
|
||||
/// - Contact normals (which direction surfaces face)
|
||||
/// - Penetration depths (how much objects overlap)
|
||||
///
|
||||
/// You typically don't interact with this directly - it's managed by [`PhysicsPipeline::step`](crate::pipeline::PhysicsPipeline::step).
|
||||
/// However, you can access it to query contact information or intersection state between specific colliders.
|
||||
///
|
||||
/// **For spatial queries** (raycasts, shape casts), use [`QueryPipeline`](crate::pipeline::QueryPipeline) instead.
|
||||
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
|
||||
#[derive(Clone)]
|
||||
pub struct NarrowPhase {
|
||||
|
||||
Reference in New Issue
Block a user