Skip to main content

rapier3d/geometry/
mod.rs

1//! Structures related to geometry: colliders, shapes, etc.
2
3#[cfg(feature = "alloc")]
4pub(crate) use self::broad_phase_bvh::DeferredBvhOptimize;
5#[cfg(feature = "alloc")]
6pub use self::broad_phase_bvh::{BroadPhaseBvh, BvhOptimizationStrategy};
7pub use self::broad_phase_pair_event::{BroadPhasePairEvent, ColliderPair};
8#[cfg(feature = "alloc")]
9pub use self::collider::{Collider, ColliderBuilder};
10#[cfg(feature = "alloc")]
11pub use self::collider_components::*;
12pub use self::collider_handle::ColliderHandle;
13#[cfg(feature = "alloc")]
14pub use self::collider_set::{ColliderSet, ModifiedColliders};
15#[cfg(feature = "alloc")]
16pub(crate) use self::contact_pair::ContactRecycleState;
17#[cfg(feature = "alloc")]
18pub(crate) use self::contact_pair::PairEventStatus;
19#[cfg(feature = "alloc")]
20pub(crate) use self::contact_pair::SOLVER_DYNAMIC_COLOR_COUNT;
21#[cfg(feature = "alloc")]
22pub(crate) use self::contact_pair::relative_pose_drift;
23#[cfg(feature = "alloc")]
24pub use self::contact_pair::{
25    ContactData, ContactId, ContactManifoldData, ContactPair, IntersectionPair, NEW_CONTACT_BIT,
26    PairContacts, RigidPairContacts, SimdSolverContact, SolverContact, SolverContactGeneric,
27    SolverContacts, SolverFlags, is_bouncy, is_bouncy_simd,
28};
29#[cfg(feature = "alloc")]
30pub use self::interaction_graph::{
31    ColliderGraphIndex, InteractionGraph, RigidBodyGraphIndex, TemporaryInteractionIndex,
32};
33pub use self::interaction_groups::{Group, InteractionGroups, InteractionTestMode};
34#[cfg(feature = "alloc")]
35pub use self::mesh_converter::{MeshConverter, MeshConverterError};
36#[cfg(feature = "alloc")]
37pub use self::narrow_phase::NarrowPhase;
38#[cfg(feature = "alloc")]
39pub(crate) use self::narrow_phase::soft_contacts;
40#[cfg(feature = "alloc")]
41pub use self::narrow_phase::soft_contacts::{
42    SoftContactImpulse, SoftEdgeCandidate, SoftEdgePass, SoftPairContacts, SoftVertexCandidate,
43    SoftVertexHits, SoftVertexPass, SoftVolumePatch, VolumeBin,
44};
45#[cfg(feature = "alloc")]
46pub use parry::utils::Array2;
47
48pub use parry::bounding_volume::BoundingVolume;
49#[cfg(feature = "alloc")]
50pub use parry::partitioning::{Bvh, BvhBuildStrategy};
51#[cfg(feature = "alloc")]
52pub use parry::query::{PointQuery, PointQueryWithLocation, RayCast, TrackedContact};
53#[cfg(feature = "alloc")]
54pub use parry::shape::{SharedShape, VoxelState, VoxelType, Voxels};
55
56#[cfg(feature = "alloc")]
57use crate::math::{Real, Vector};
58
59/// A contact between two colliders.
60#[cfg(feature = "alloc")]
61pub type Contact = parry::query::TrackedContact<ContactData>;
62/// A contact manifold between two colliders.
63#[cfg(feature = "alloc")]
64pub type ContactManifold = parry::query::ContactManifold<ContactManifoldData, ContactData>;
65/// A segment shape.
66pub type Segment = parry::shape::Segment;
67/// A cuboid shape.
68pub type Cuboid = parry::shape::Cuboid;
69/// A triangle shape.
70pub type Triangle = parry::shape::Triangle;
71/// A ball shape.
72pub type Ball = parry::shape::Ball;
73/// A capsule shape.
74pub type Capsule = parry::shape::Capsule;
75/// A heightfield shape.
76#[cfg(feature = "alloc")]
77pub type HeightField = parry::shape::HeightField;
78/// A cylindrical shape.
79#[cfg(feature = "dim3")]
80pub type Cylinder = parry::shape::Cylinder;
81/// A cone shape.
82#[cfg(feature = "dim3")]
83pub type Cone = parry::shape::Cone;
84/// An axis-aligned bounding box.
85pub type Aabb = parry::bounding_volume::Aabb;
86/// A ray that can be cast against colliders.
87pub type Ray = parry::query::Ray;
88/// The intersection between a ray and a  collider.
89pub type RayIntersection = parry::query::RayIntersection;
90/// The projection of a point on a collider.
91pub type PointProjection = parry::query::PointProjection;
92/// The result of a shape-cast between two shapes.
93pub type ShapeCastHit = parry::query::ShapeCastHit;
94/// The default broad-phase implementation recommended for general-purpose usage.
95#[cfg(feature = "alloc")]
96pub type DefaultBroadPhase = BroadPhaseBvh;
97
98bitflags::bitflags! {
99    /// Flags providing more information regarding a collision event.
100    #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
101    #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
102    pub struct CollisionEventFlags: u32 {
103        /// Flag set if at least one of the colliders involved in the
104        /// collision was a sensor when the event was fired.
105        const SENSOR = 0b0001;
106        /// Flag set if a `CollisionEvent::Stopped` was fired because
107        /// at least one of the colliders was removed.
108        const REMOVED = 0b0010;
109    }
110}
111
112#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
113#[derive(Copy, Clone, Hash, Debug)]
114/// Events triggered when two colliders start or stop touching.
115///
116/// Receive these through an [`EventHandler`](crate::pipeline::EventHandler) implementation.
117/// At least one collider must have [`ActiveEvents::COLLISION_EVENTS`](crate::pipeline::ActiveEvents::COLLISION_EVENTS) enabled.
118///
119/// Use for:
120/// - Trigger zones (player entered/exited area)
121/// - Collectible items (player touched coin)
122/// - Sound effects (objects started colliding)
123/// - Game logic based on contact state
124///
125/// # Example
126/// ```
127/// # use rapier3d::prelude::*;
128/// # let h1 = ColliderHandle::from_raw_parts(0, 0);
129/// # let h2 = ColliderHandle::from_raw_parts(1, 0);
130/// # let event = CollisionEvent::Started(h1, h2, CollisionEventFlags::empty());
131/// match event {
132///     CollisionEvent::Started(h1, h2, flags) => {
133///         println!("Colliders {:?} and {:?} started touching", h1, h2);
134///         if flags.contains(CollisionEventFlags::SENSOR) {
135///             println!("At least one is a sensor!");
136///         }
137///     }
138///     CollisionEvent::Stopped(h1, h2, _) => {
139///         println!("Colliders {:?} and {:?} stopped touching", h1, h2);
140///     }
141/// }
142/// ```
143pub enum CollisionEvent {
144    /// Two colliders just started touching this frame.
145    Started(ColliderHandle, ColliderHandle, CollisionEventFlags),
146    /// Two colliders just stopped touching this frame.
147    Stopped(ColliderHandle, ColliderHandle, CollisionEventFlags),
148}
149
150impl CollisionEvent {
151    /// Returns `true` if this is a Started event (colliders began touching).
152    pub fn started(self) -> bool {
153        matches!(self, CollisionEvent::Started(..))
154    }
155
156    /// Returns `true` if this is a Stopped event (colliders stopped touching).
157    pub fn stopped(self) -> bool {
158        matches!(self, CollisionEvent::Stopped(..))
159    }
160
161    /// Returns the handle of the first collider in this collision.
162    pub fn collider1(self) -> ColliderHandle {
163        match self {
164            Self::Started(h, _, _) | Self::Stopped(h, _, _) => h,
165        }
166    }
167
168    /// Returns the handle of the second collider in this collision.
169    pub fn collider2(self) -> ColliderHandle {
170        match self {
171            Self::Started(_, h, _) | Self::Stopped(_, h, _) => h,
172        }
173    }
174
175    /// Was at least one of the colliders involved in the collision a sensor?
176    pub fn sensor(self) -> bool {
177        match self {
178            Self::Started(_, _, f) | Self::Stopped(_, _, f) => {
179                f.contains(CollisionEventFlags::SENSOR)
180            }
181        }
182    }
183
184    /// Was at least one of the colliders involved in the collision removed?
185    pub fn removed(self) -> bool {
186        match self {
187            Self::Started(_, _, f) | Self::Stopped(_, _, f) => {
188                f.contains(CollisionEventFlags::REMOVED)
189            }
190        }
191    }
192}
193
194#[cfg(feature = "alloc")]
195#[derive(Copy, Clone, PartialEq, Debug, Default)]
196/// Event occurring when the sum of the magnitudes of the contact forces
197/// between two colliders exceed a threshold.
198pub struct ContactForceEvent {
199    /// The first collider involved in the contact.
200    pub collider1: ColliderHandle,
201    /// The second collider involved in the contact.
202    pub collider2: ColliderHandle,
203    /// The sum of all the forces between the two colliders.
204    pub total_force: Vector,
205    /// The sum of the magnitudes of each force between the two colliders.
206    ///
207    /// Note that this is **not** the same as the magnitude of `self.total_force`.
208    /// Here we are summing the magnitude of all the forces, instead of taking
209    /// the magnitude of their sum.
210    pub total_force_magnitude: Real,
211    /// The world-space (unit) direction of the force with strongest magnitude.
212    pub max_force_direction: Vector,
213    /// The magnitude of the largest force at a contact point of this contact pair.
214    pub max_force_magnitude: Real,
215    /// Is this the first step the pair's total force exceeded its threshold?
216    ///
217    /// `true` on the step the force crosses the pair's
218    /// [`Collider::contact_force_event_threshold`] coming from below (or from not
219    /// touching), `false` while it stays above on consecutive steps. The status resets
220    /// when the force drops back below the threshold or the colliders separate, so the
221    /// next crossing reports `true` again. Note that this is about the *force*
222    /// threshold, not contact newness: a pair can touch gently for many steps (emitting
223    /// no force event) before its first `started` event.
224    pub started: bool,
225}
226
227#[cfg(feature = "alloc")]
228impl ContactForceEvent {
229    /// Init a contact force event from a contact pair.
230    pub fn from_contact_pair(dt: Real, pair: &ContactPair, total_force_magnitude: Real) -> Self {
231        let mut result = ContactForceEvent {
232            collider1: pair.collider1,
233            collider2: pair.collider2,
234            total_force_magnitude,
235            // The pair's status is updated only after the event handlers ran, so at
236            // this point it still holds the previous step's value.
237            started: !pair
238                .event_status
239                .contains(PairEventStatus::INITIAL_FORCE_THRESHOLD_EVENT_EMITTED),
240            ..ContactForceEvent::default()
241        };
242
243        for m in pair.solver_manifolds() {
244            let mut total_manifold_impulse = 0.0;
245            for pt in m.contacts() {
246                total_manifold_impulse += pt.data.impulse;
247
248                if pt.data.impulse > result.max_force_magnitude {
249                    result.max_force_magnitude = pt.data.impulse;
250                    result.max_force_direction = m.data.normal;
251                }
252            }
253
254            result.total_force += m.data.normal * total_manifold_impulse;
255        }
256        // Two soft surfaces: their contacts are candidates, each reporting its impulse.
257        if let Some(soft) = pair.soft() {
258            for i in soft.impulses(pair.collider1) {
259                if i.impulse > result.max_force_magnitude {
260                    result.max_force_magnitude = i.impulse;
261                    result.max_force_direction = i.normal;
262                }
263                result.total_force += i.normal * i.impulse;
264            }
265        }
266
267        let inv_dt = crate::utils::inv(dt);
268        // NOTE: convert impulses to forces. Note that we
269        //       don’t need to convert the `total_force_magnitude`
270        //       because it’s an input of this function already
271        //       assumed to be a force instead of an impulse.
272        result.total_force *= inv_dt;
273        result.max_force_magnitude *= inv_dt;
274        result
275    }
276}
277
278#[cfg(feature = "alloc")]
279pub(crate) use self::narrow_phase::ContactManifoldIndex;
280#[cfg(feature = "alloc")]
281pub use parry::shape::*;
282
283#[cfg(all(feature = "serde-serialize", feature = "alloc"))]
284pub(crate) fn default_persistent_query_dispatcher()
285-> alloc::sync::Arc<dyn parry::query::PersistentQueryDispatcher<ContactManifoldData, ContactData>> {
286    alloc::sync::Arc::new(parry::query::DefaultQueryDispatcher)
287}
288
289#[cfg(feature = "alloc")]
290mod collider_components;
291mod collider_handle;
292#[cfg(feature = "alloc")]
293pub(crate) mod contact_pair;
294#[cfg(feature = "alloc")]
295mod interaction_graph;
296mod interaction_groups;
297#[cfg(feature = "alloc")]
298mod narrow_phase;
299
300#[cfg(feature = "alloc")]
301mod broad_phase_bvh;
302mod broad_phase_pair_event;
303#[cfg(feature = "alloc")]
304mod collider;
305#[cfg(feature = "alloc")]
306mod collider_set;
307#[cfg(feature = "alloc")]
308mod mesh_converter;
309
310#[cfg(all(feature = "dim3", feature = "alloc"))]
311mod manifold_reduction;
312
313#[cfg(all(feature = "dim3", feature = "alloc"))]
314mod contact_clustering;