Skip to main content

rapier2d/geometry/
mod.rs

1//! Structures related to geometry: colliders, shapes, etc.
2
3pub use self::broad_phase_bvh::{BroadPhaseBvh, BvhOptimizationStrategy};
4pub use self::broad_phase_pair_event::{BroadPhasePairEvent, ColliderPair};
5pub use self::collider::{Collider, ColliderBuilder};
6pub use self::collider_components::*;
7pub use self::collider_set::{ColliderSet, ModifiedColliders};
8pub use self::contact_pair::{
9    ContactData, ContactManifoldData, ContactPair, IntersectionPair, SimdSolverContact,
10    SolverContact, SolverFlags,
11};
12pub use self::interaction_graph::{
13    ColliderGraphIndex, InteractionGraph, RigidBodyGraphIndex, TemporaryInteractionIndex,
14};
15pub use self::interaction_groups::{Group, InteractionGroups, InteractionTestMode};
16pub use self::mesh_converter::{MeshConverter, MeshConverterError};
17pub use self::narrow_phase::NarrowPhase;
18pub use parry::utils::Array2;
19
20pub use parry::bounding_volume::BoundingVolume;
21pub use parry::partitioning::{Bvh, BvhBuildStrategy};
22pub use parry::query::{PointQuery, PointQueryWithLocation, RayCast, TrackedContact};
23pub use parry::shape::{SharedShape, VoxelState, VoxelType, Voxels};
24
25use crate::math::{Real, Vector};
26
27/// A contact between two colliders.
28pub type Contact = parry::query::TrackedContact<ContactData>;
29/// A contact manifold between two colliders.
30pub type ContactManifold = parry::query::ContactManifold<ContactManifoldData, ContactData>;
31/// A segment shape.
32pub type Segment = parry::shape::Segment;
33/// A cuboid shape.
34pub type Cuboid = parry::shape::Cuboid;
35/// A triangle shape.
36pub type Triangle = parry::shape::Triangle;
37/// A ball shape.
38pub type Ball = parry::shape::Ball;
39/// A capsule shape.
40pub type Capsule = parry::shape::Capsule;
41/// A heightfield shape.
42pub type HeightField = parry::shape::HeightField;
43/// A cylindrical shape.
44#[cfg(feature = "dim3")]
45pub type Cylinder = parry::shape::Cylinder;
46/// A cone shape.
47#[cfg(feature = "dim3")]
48pub type Cone = parry::shape::Cone;
49/// An axis-aligned bounding box.
50pub type Aabb = parry::bounding_volume::Aabb;
51/// A ray that can be cast against colliders.
52pub type Ray = parry::query::Ray;
53/// The intersection between a ray and a  collider.
54pub type RayIntersection = parry::query::RayIntersection;
55/// The projection of a point on a collider.
56pub type PointProjection = parry::query::PointProjection;
57/// The result of a shape-cast between two shapes.
58pub type ShapeCastHit = parry::query::ShapeCastHit;
59/// The default broad-phase implementation recommended for general-purpose usage.
60pub type DefaultBroadPhase = BroadPhaseBvh;
61
62bitflags::bitflags! {
63    /// Flags providing more information regarding a collision event.
64    #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
65    #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
66    pub struct CollisionEventFlags: u32 {
67        /// Flag set if at least one of the colliders involved in the
68        /// collision was a sensor when the event was fired.
69        const SENSOR = 0b0001;
70        /// Flag set if a `CollisionEvent::Stopped` was fired because
71        /// at least one of the colliders was removed.
72        const REMOVED = 0b0010;
73    }
74}
75
76#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
77#[derive(Copy, Clone, Hash, Debug)]
78/// Events triggered when two colliders start or stop touching.
79///
80/// Receive these through an [`EventHandler`](crate::pipeline::EventHandler) implementation.
81/// At least one collider must have [`ActiveEvents::COLLISION_EVENTS`](crate::pipeline::ActiveEvents::COLLISION_EVENTS) enabled.
82///
83/// Use for:
84/// - Trigger zones (player entered/exited area)
85/// - Collectible items (player touched coin)
86/// - Sound effects (objects started colliding)
87/// - Game logic based on contact state
88///
89/// # Example
90/// ```
91/// # use rapier3d::prelude::*;
92/// # let h1 = ColliderHandle::from_raw_parts(0, 0);
93/// # let h2 = ColliderHandle::from_raw_parts(1, 0);
94/// # let event = CollisionEvent::Started(h1, h2, CollisionEventFlags::empty());
95/// match event {
96///     CollisionEvent::Started(h1, h2, flags) => {
97///         println!("Colliders {:?} and {:?} started touching", h1, h2);
98///         if flags.contains(CollisionEventFlags::SENSOR) {
99///             println!("At least one is a sensor!");
100///         }
101///     }
102///     CollisionEvent::Stopped(h1, h2, _) => {
103///         println!("Colliders {:?} and {:?} stopped touching", h1, h2);
104///     }
105/// }
106/// ```
107pub enum CollisionEvent {
108    /// Two colliders just started touching this frame.
109    Started(ColliderHandle, ColliderHandle, CollisionEventFlags),
110    /// Two colliders just stopped touching this frame.
111    Stopped(ColliderHandle, ColliderHandle, CollisionEventFlags),
112}
113
114impl CollisionEvent {
115    /// Returns `true` if this is a Started event (colliders began touching).
116    pub fn started(self) -> bool {
117        matches!(self, CollisionEvent::Started(..))
118    }
119
120    /// Returns `true` if this is a Stopped event (colliders stopped touching).
121    pub fn stopped(self) -> bool {
122        matches!(self, CollisionEvent::Stopped(..))
123    }
124
125    /// Returns the handle of the first collider in this collision.
126    pub fn collider1(self) -> ColliderHandle {
127        match self {
128            Self::Started(h, _, _) | Self::Stopped(h, _, _) => h,
129        }
130    }
131
132    /// Returns the handle of the second collider in this collision.
133    pub fn collider2(self) -> ColliderHandle {
134        match self {
135            Self::Started(_, h, _) | Self::Stopped(_, h, _) => h,
136        }
137    }
138
139    /// Was at least one of the colliders involved in the collision a sensor?
140    pub fn sensor(self) -> bool {
141        match self {
142            Self::Started(_, _, f) | Self::Stopped(_, _, f) => {
143                f.contains(CollisionEventFlags::SENSOR)
144            }
145        }
146    }
147
148    /// Was at least one of the colliders involved in the collision removed?
149    pub fn removed(self) -> bool {
150        match self {
151            Self::Started(_, _, f) | Self::Stopped(_, _, f) => {
152                f.contains(CollisionEventFlags::REMOVED)
153            }
154        }
155    }
156}
157
158#[derive(Copy, Clone, PartialEq, Debug, Default)]
159/// Event occurring when the sum of the magnitudes of the contact forces
160/// between two colliders exceed a threshold.
161pub struct ContactForceEvent {
162    /// The first collider involved in the contact.
163    pub collider1: ColliderHandle,
164    /// The second collider involved in the contact.
165    pub collider2: ColliderHandle,
166    /// The sum of all the forces between the two colliders.
167    pub total_force: Vector,
168    /// The sum of the magnitudes of each force between the two colliders.
169    ///
170    /// Note that this is **not** the same as the magnitude of `self.total_force`.
171    /// Here we are summing the magnitude of all the forces, instead of taking
172    /// the magnitude of their sum.
173    pub total_force_magnitude: Real,
174    /// The world-space (unit) direction of the force with strongest magnitude.
175    pub max_force_direction: Vector,
176    /// The magnitude of the largest force at a contact point of this contact pair.
177    pub max_force_magnitude: Real,
178}
179
180impl ContactForceEvent {
181    /// Init a contact force event from a contact pair.
182    pub fn from_contact_pair(dt: Real, pair: &ContactPair, total_force_magnitude: Real) -> Self {
183        let mut result = ContactForceEvent {
184            collider1: pair.collider1,
185            collider2: pair.collider2,
186            total_force_magnitude,
187            ..ContactForceEvent::default()
188        };
189
190        for m in &pair.manifolds {
191            let mut total_manifold_impulse = 0.0;
192            for pt in m.contacts() {
193                total_manifold_impulse += pt.data.impulse;
194
195                if pt.data.impulse > result.max_force_magnitude {
196                    result.max_force_magnitude = pt.data.impulse;
197                    result.max_force_direction = m.data.normal;
198                }
199            }
200
201            result.total_force += m.data.normal * total_manifold_impulse;
202        }
203
204        let inv_dt = crate::utils::inv(dt);
205        // NOTE: convert impulses to forces. Note that we
206        //       don’t need to convert the `total_force_magnitude`
207        //       because it’s an input of this function already
208        //       assumed to be a force instead of an impulse.
209        result.total_force *= inv_dt;
210        result.max_force_magnitude *= inv_dt;
211        result
212    }
213}
214
215pub(crate) use self::narrow_phase::ContactManifoldIndex;
216pub use parry::shape::*;
217
218#[cfg(feature = "serde-serialize")]
219pub(crate) fn default_persistent_query_dispatcher()
220-> std::sync::Arc<dyn parry::query::PersistentQueryDispatcher<ContactManifoldData, ContactData>> {
221    std::sync::Arc::new(parry::query::DefaultQueryDispatcher)
222}
223
224mod collider_components;
225mod contact_pair;
226mod interaction_graph;
227mod interaction_groups;
228mod narrow_phase;
229
230mod broad_phase_bvh;
231mod broad_phase_pair_event;
232mod collider;
233mod collider_set;
234mod mesh_converter;
235
236#[cfg(feature = "dim3")]
237mod manifold_reduction;