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