fyrox_impl/scene/dim2/
physics.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Scene physics module.
22
23use crate::{
24    core::{
25        algebra::{
26            Isometry2, Isometry3, Matrix4, Point2, Rotation3, Translation2, Translation3,
27            UnitComplex, UnitQuaternion, UnitVector2, Vector2, Vector3,
28        },
29        arrayvec::ArrayVec,
30        instant,
31        log::{Log, MessageKind},
32        math::Matrix4Ext,
33        parking_lot::Mutex,
34        pool::Handle,
35        reflect::prelude::*,
36        variable::{InheritableVariable, VariableFlags},
37        visitor::prelude::*,
38        BiDirHashMap, ImmutableString,
39    },
40    graph::{BaseSceneGraph, SceneGraphNode},
41    scene::{
42        self,
43        collider::{self},
44        debug::SceneDrawingContext,
45        dim2::{
46            self, collider::ColliderShape, collider::TileMapShape, joint::JointLocalFrames,
47            joint::JointParams, rigidbody::ApplyAction,
48        },
49        graph::{
50            isometric_global_transform,
51            physics::{FeatureId, IntegrationParameters, PhysicsPerformanceStatistics},
52            Graph, NodePool,
53        },
54        node::{Node, NodeTrait},
55        tilemap::TileMap,
56    },
57};
58pub use rapier2d::geometry::shape::*;
59use rapier2d::{
60    dynamics::{
61        CCDSolver, GenericJoint, GenericJointBuilder, ImpulseJointHandle, ImpulseJointSet,
62        IslandManager, JointAxesMask, JointAxis, MultibodyJointHandle, MultibodyJointSet,
63        RigidBody, RigidBodyActivation, RigidBodyBuilder, RigidBodyHandle, RigidBodySet,
64        RigidBodyType,
65    },
66    geometry::{
67        Collider, ColliderBuilder, ColliderHandle, ColliderSet, Cuboid, DefaultBroadPhase,
68        InteractionGroups, NarrowPhase, Ray, SharedShape,
69    },
70    parry::query::ShapeCastOptions,
71    pipeline::{DebugRenderPipeline, EventHandler, PhysicsPipeline, QueryPipeline},
72};
73use std::{
74    cell::RefCell,
75    cmp::Ordering,
76    fmt::{Debug, Formatter},
77    hash::Hash,
78    num::NonZeroUsize,
79    sync::Arc,
80};
81
82use super::collider::GeometrySource;
83
84/// A trait for ray cast results storage. It has two implementations: Vec and ArrayVec.
85/// Latter is needed for the cases where you need to avoid runtime memory allocations
86/// and do everything on stack.
87pub trait QueryResultsStorage {
88    /// Pushes new intersection in the storage. Returns true if intersection was
89    /// successfully inserted, false otherwise.
90    fn push(&mut self, intersection: Intersection) -> bool;
91
92    /// Clears the storage.
93    fn clear(&mut self);
94
95    /// Sorts intersections by given compare function.
96    fn sort_intersections_by<C: FnMut(&Intersection, &Intersection) -> Ordering>(&mut self, cmp: C);
97}
98
99impl QueryResultsStorage for Vec<Intersection> {
100    fn push(&mut self, intersection: Intersection) -> bool {
101        self.push(intersection);
102        true
103    }
104
105    fn clear(&mut self) {
106        self.clear()
107    }
108
109    fn sort_intersections_by<C>(&mut self, cmp: C)
110    where
111        C: FnMut(&Intersection, &Intersection) -> Ordering,
112    {
113        self.sort_by(cmp);
114    }
115}
116
117impl<const CAP: usize> QueryResultsStorage for ArrayVec<Intersection, CAP> {
118    fn push(&mut self, intersection: Intersection) -> bool {
119        self.try_push(intersection).is_ok()
120    }
121
122    fn clear(&mut self) {
123        self.clear()
124    }
125
126    fn sort_intersections_by<C>(&mut self, cmp: C)
127    where
128        C: FnMut(&Intersection, &Intersection) -> Ordering,
129    {
130        self.sort_by(cmp);
131    }
132}
133
134/// A ray intersection result.
135#[derive(Debug, Clone, PartialEq)]
136pub struct Intersection {
137    /// A handle of the collider with which intersection was detected.
138    pub collider: Handle<Node>,
139
140    /// A normal at the intersection position.
141    pub normal: Vector2<f32>,
142
143    /// A position of the intersection in world coordinates.
144    pub position: Point2<f32>,
145
146    /// Additional data that contains a kind of the feature with which
147    /// intersection was detected as well as its index.
148    ///
149    /// # Important notes.
150    ///
151    /// FeatureId::Face might have index that is greater than amount of triangles in
152    /// a triangle mesh, this means that intersection was detected from "back" side of
153    /// a face. To "fix" that index, simply subtract amount of triangles of a triangle
154    /// mesh from the value.
155    pub feature: FeatureId,
156
157    /// Distance from the ray origin.
158    pub toi: f32,
159}
160
161/// A set of options for the ray cast.
162pub struct RayCastOptions {
163    /// A ray origin.
164    pub ray_origin: Point2<f32>,
165
166    /// A ray direction. Can be non-normalized.
167    pub ray_direction: Vector2<f32>,
168
169    /// Maximum distance of cast.
170    pub max_len: f32,
171
172    /// Groups to check.
173    pub groups: collider::InteractionGroups,
174
175    /// Whether to sort intersections from closest to farthest.
176    pub sort_results: bool,
177}
178
179/// Data of the contact.
180#[derive(Debug, Clone, PartialEq)]
181pub struct ContactData {
182    /// The contact point in the local-space of the first shape.
183    pub local_p1: Vector2<f32>,
184    /// The contact point in the local-space of the second shape.
185    pub local_p2: Vector2<f32>,
186    /// The distance between the two contact points.
187    pub dist: f32,
188    /// The impulse, along the contact normal, applied by this contact to the first collider's rigid-body.
189    /// The impulse applied to the second collider's rigid-body is given by `-impulse`.
190    pub impulse: f32,
191    /// The friction impulses along the basis orthonormal to the contact normal, applied to the first
192    /// collider's rigid-body.
193    pub tangent_impulse: f32,
194}
195
196/// A contact manifold between two colliders.
197#[derive(Debug, Clone, PartialEq)]
198pub struct ContactManifold {
199    /// The contacts points.
200    pub points: Vec<ContactData>,
201    /// The contact normal of all the contacts of this manifold, expressed in the local space of the first shape.
202    pub local_n1: Vector2<f32>,
203    /// The contact normal of all the contacts of this manifold, expressed in the local space of the second shape.
204    pub local_n2: Vector2<f32>,
205    /// The first rigid-body involved in this contact manifold.
206    pub rigid_body1: Handle<Node>,
207    /// The second rigid-body involved in this contact manifold.
208    pub rigid_body2: Handle<Node>,
209    /// The world-space contact normal shared by all the contact in this contact manifold.
210    pub normal: Vector2<f32>,
211}
212
213/// Contact info for pair of colliders.
214#[derive(Debug, Clone, PartialEq)]
215pub struct ContactPair {
216    /// The first collider involved in the contact pair.
217    pub collider1: Handle<Node>,
218    /// The second collider involved in the contact pair.
219    pub collider2: Handle<Node>,
220    /// The set of contact manifolds between the two colliders.
221    /// All contact manifold contain themselves contact points between the colliders.
222    pub manifolds: Vec<ContactManifold>,
223    /// Is there any active contact in this contact pair?
224    pub has_any_active_contact: bool,
225}
226
227impl ContactPair {
228    fn from_native(c: &rapier2d::geometry::ContactPair, physics: &PhysicsWorld) -> Option<Self> {
229        Some(ContactPair {
230            collider1: Handle::decode_from_u128(physics.colliders.get(c.collider1)?.user_data),
231            collider2: Handle::decode_from_u128(physics.colliders.get(c.collider2)?.user_data),
232            manifolds: c
233                .manifolds
234                .iter()
235                .filter_map(|m| {
236                    Some(ContactManifold {
237                        points: m
238                            .points
239                            .iter()
240                            .map(|p| ContactData {
241                                local_p1: p.local_p1.coords,
242                                local_p2: p.local_p2.coords,
243                                dist: p.dist,
244                                impulse: p.data.impulse,
245                                tangent_impulse: p.data.tangent_impulse.x,
246                            })
247                            .collect(),
248                        local_n1: m.local_n1,
249                        local_n2: m.local_n2,
250                        rigid_body1: m.data.rigid_body1.and_then(|h| {
251                            physics
252                                .bodies
253                                .get(h)
254                                .map(|b| Handle::decode_from_u128(b.user_data))
255                        })?,
256                        rigid_body2: m.data.rigid_body2.and_then(|h| {
257                            physics
258                                .bodies
259                                .get(h)
260                                .map(|b| Handle::decode_from_u128(b.user_data))
261                        })?,
262                        normal: m.data.normal,
263                    })
264                })
265                .collect(),
266            has_any_active_contact: c.has_any_active_contact,
267        })
268    }
269}
270
271/// Intersection info for pair of colliders.
272#[derive(Debug, Clone, PartialEq)]
273pub struct IntersectionPair {
274    /// The first collider involved in the contact pair.
275    pub collider1: Handle<Node>,
276    /// The second collider involved in the contact pair.
277    pub collider2: Handle<Node>,
278    /// Is there any active contact in this contact pair?
279    pub has_any_active_contact: bool,
280}
281
282pub(super) struct Container<S, A>
283where
284    A: Hash + Eq + Clone,
285{
286    set: S,
287    map: BiDirHashMap<A, Handle<Node>>,
288}
289
290fn convert_joint_params(
291    params: scene::dim2::joint::JointParams,
292    local_frame1: Isometry2<f32>,
293    local_frame2: Isometry2<f32>,
294) -> GenericJoint {
295    let locked_axis = match params {
296        JointParams::BallJoint(_) => JointAxesMask::LOCKED_REVOLUTE_AXES,
297        JointParams::FixedJoint(_) => JointAxesMask::LOCKED_FIXED_AXES,
298        JointParams::PrismaticJoint(_) => JointAxesMask::LOCKED_PRISMATIC_AXES,
299    };
300
301    let mut joint = GenericJointBuilder::new(locked_axis)
302        .local_frame1(local_frame1)
303        .local_frame2(local_frame2)
304        .build();
305
306    match params {
307        scene::dim2::joint::JointParams::BallJoint(v) => {
308            if v.limits_enabled {
309                joint.set_limits(
310                    JointAxis::AngX,
311                    [v.limits_angles.start, v.limits_angles.end],
312                );
313            }
314        }
315        scene::dim2::joint::JointParams::FixedJoint(_) => {}
316        scene::dim2::joint::JointParams::PrismaticJoint(v) => {
317            if v.limits_enabled {
318                joint.set_limits(JointAxis::LinX, [v.limits.start, v.limits.end]);
319            }
320        }
321    }
322
323    joint
324}
325
326fn tile_map_to_collider_shape(
327    tile_map: &GeometrySource,
328    owner_inv_transform: Matrix4<f32>,
329    nodes: &NodePool,
330    collider_name: &ImmutableString,
331) -> Option<SharedShape> {
332    let tile_map = nodes.try_borrow(tile_map.0)?.component_ref::<TileMap>()?;
333
334    let tile_set_resource = tile_map.tile_set()?.data_ref();
335    let tile_set = tile_set_resource.as_loaded_ref()?;
336
337    let tile_scale = tile_map.tile_scale();
338    let global_transform = owner_inv_transform
339        * tile_map.global_transform()
340        * Matrix4::new_nonuniform_scaling(&Vector3::new(-tile_scale.x, tile_scale.y, 1.0));
341
342    let mut vertices = Vec::new();
343    let mut triangles = Vec::new();
344
345    let collider_uuid = tile_set.collider_name_to_uuid(collider_name)?;
346    let tile_data = tile_map.tiles()?.data_ref();
347    let tile_data = tile_data.as_loaded_ref()?;
348    for (position, handle) in tile_data.iter() {
349        let Some(tile_definition) = tile_set.get_tile_data(handle.into()) else {
350            continue;
351        };
352
353        if let Some(collider) = tile_definition.colliders.get(&collider_uuid) {
354            let position = position.cast::<f32>().to_homogeneous();
355            collider.build_collider_shape(
356                &global_transform,
357                position,
358                &mut vertices,
359                &mut triangles,
360            );
361        }
362    }
363
364    SharedShape::trimesh(vertices, triangles).ok()
365}
366
367// Converts descriptor in a shared shape.
368fn collider_shape_into_native_shape(
369    shape: &ColliderShape,
370    owner_inv_transform: Matrix4<f32>,
371    nodes: &NodePool,
372) -> Option<SharedShape> {
373    match shape {
374        ColliderShape::Ball(ball) => Some(SharedShape::ball(ball.radius)),
375        ColliderShape::Cuboid(cuboid) => {
376            Some(SharedShape(Arc::new(Cuboid::new(cuboid.half_extents))))
377        }
378        ColliderShape::Capsule(capsule) => Some(SharedShape::capsule(
379            Point2::from(capsule.begin),
380            Point2::from(capsule.end),
381            capsule.radius,
382        )),
383        ColliderShape::Segment(segment) => Some(SharedShape::segment(
384            Point2::from(segment.begin),
385            Point2::from(segment.end),
386        )),
387        ColliderShape::Triangle(triangle) => Some(SharedShape::triangle(
388            Point2::from(triangle.a),
389            Point2::from(triangle.b),
390            Point2::from(triangle.c),
391        )),
392        ColliderShape::Trimesh(_) => {
393            None // TODO
394        }
395        ColliderShape::Heightfield(_) => {
396            None // TODO
397        }
398        ColliderShape::TileMap(TileMapShape {
399            tile_map,
400            layer_name: collider_layer_name,
401        }) => tile_map_to_collider_shape(tile_map, owner_inv_transform, nodes, collider_layer_name),
402    }
403}
404
405fn isometry2_to_mat4(isometry: &Isometry2<f32>) -> Matrix4<f32> {
406    Isometry3 {
407        rotation: UnitQuaternion::from_euler_angles(0.0, 0.0, isometry.rotation.angle()),
408        translation: Translation3 {
409            vector: Vector3::new(isometry.translation.x, isometry.translation.y, 0.0),
410        },
411    }
412    .to_homogeneous()
413}
414
415/// Physics world is responsible for physics simulation in the engine. There is a very few public
416/// methods, mostly for ray casting. You should add physical entities using scene graph nodes, such
417/// as RigidBody, Collider, Joint.
418#[derive(Visit, Reflect)]
419pub struct PhysicsWorld {
420    /// A flag that defines whether physics simulation is enabled or not.
421    pub enabled: InheritableVariable<bool>,
422
423    /// A set of parameters that define behavior of every rigid body.
424    pub integration_parameters: InheritableVariable<IntegrationParameters>,
425
426    /// Current gravity vector. Default is (0.0, -9.81)
427    pub gravity: InheritableVariable<Vector2<f32>>,
428
429    /// Performance statistics of a single simulation step.
430    #[visit(skip)]
431    #[reflect(hidden)]
432    pub performance_statistics: PhysicsPerformanceStatistics,
433
434    // Current physics pipeline.
435    #[visit(skip)]
436    #[reflect(hidden)]
437    pipeline: PhysicsPipeline,
438    // Broad phase performs rough intersection checks.
439    #[visit(skip)]
440    #[reflect(hidden)]
441    broad_phase: DefaultBroadPhase,
442    // Narrow phase is responsible for precise contact generation.
443    #[visit(skip)]
444    #[reflect(hidden)]
445    narrow_phase: NarrowPhase,
446    // A continuous collision detection solver.
447    #[visit(skip)]
448    #[reflect(hidden)]
449    ccd_solver: CCDSolver,
450    // Structure responsible for maintaining the set of active rigid-bodies, and putting non-moving
451    // rigid-bodies to sleep to save computation times.
452    #[visit(skip)]
453    #[reflect(hidden)]
454    islands: IslandManager,
455    // A container of rigid bodies.
456    #[visit(skip)]
457    #[reflect(hidden)]
458    bodies: RigidBodySet,
459    // A container of colliders.
460    #[visit(skip)]
461    #[reflect(hidden)]
462    pub(crate) colliders: ColliderSet,
463    // A container of impulse joints.
464    #[visit(skip)]
465    #[reflect(hidden)]
466    joints: Container<ImpulseJointSet, ImpulseJointHandle>,
467    // A container of multibody joints.
468    #[visit(skip)]
469    #[reflect(hidden)]
470    multibody_joints: Container<MultibodyJointSet, MultibodyJointHandle>,
471    // Event handler collects info about contacts and proximity events.
472    #[visit(skip)]
473    #[reflect(hidden)]
474    event_handler: Box<dyn EventHandler>,
475    #[visit(skip)]
476    #[reflect(hidden)]
477    query: RefCell<QueryPipeline>,
478    #[visit(skip)]
479    #[reflect(hidden)]
480    debug_render_pipeline: Mutex<DebugRenderPipeline>,
481}
482
483impl Clone for PhysicsWorld {
484    fn clone(&self) -> Self {
485        PhysicsWorld {
486            enabled: self.enabled.clone(),
487            integration_parameters: self.integration_parameters.clone(),
488            gravity: self.gravity.clone(),
489            ..Default::default()
490        }
491    }
492}
493
494fn isometry_from_global_transform(transform: &Matrix4<f32>) -> Isometry2<f32> {
495    Isometry2 {
496        translation: Translation2::new(transform[12], transform[13]),
497        rotation: UnitComplex::from_angle(
498            Rotation3::from_matrix_eps(&transform.basis(), f32::EPSILON, 16, Rotation3::identity())
499                .euler_angles()
500                .2,
501        ),
502    }
503}
504
505fn calculate_local_frames(
506    joint: &dyn NodeTrait,
507    body1: &dyn NodeTrait,
508    body2: &dyn NodeTrait,
509) -> (Isometry2<f32>, Isometry2<f32>) {
510    let joint_isometry = isometry_from_global_transform(&joint.global_transform());
511
512    (
513        joint_isometry * isometry_from_global_transform(&body1.global_transform()).inverse(),
514        joint_isometry * isometry_from_global_transform(&body2.global_transform()).inverse(),
515    )
516}
517
518fn u32_to_group(v: u32) -> rapier2d::geometry::Group {
519    rapier2d::geometry::Group::from_bits(v).unwrap_or_else(rapier2d::geometry::Group::all)
520}
521
522/// A filter tha describes what collider should be included or excluded from a scene query.
523#[derive(Copy, Clone, Default)]
524#[allow(clippy::type_complexity)]
525pub struct QueryFilter<'a> {
526    /// Flags indicating what particular type of colliders should be excluded from the scene query.
527    pub flags: collider::QueryFilterFlags,
528    /// If set, only colliders with collision groups compatible with this one will
529    /// be included in the scene query.
530    pub groups: Option<collider::InteractionGroups>,
531    /// If set, this collider will be excluded from the scene query.
532    pub exclude_collider: Option<Handle<Node>>,
533    /// If set, any collider attached to this rigid-body will be excluded from the scene query.
534    pub exclude_rigid_body: Option<Handle<Node>>,
535    /// If set, any collider for which this closure returns false will be excluded from the scene query.
536    pub predicate: Option<&'a dyn Fn(Handle<Node>, &collider::Collider) -> bool>,
537}
538
539/// The result of a time-of-impact (TOI) computation.
540#[derive(Copy, Clone, Debug)]
541pub struct TOI {
542    /// The time at which the objects touch.
543    pub toi: f32,
544    /// The local-space closest point on the first shape at the time of impact.
545    ///
546    /// Undefined if `status` is `Penetrating`.
547    pub witness1: Point2<f32>,
548    /// The local-space closest point on the second shape at the time of impact.
549    ///
550    /// Undefined if `status` is `Penetrating`.
551    pub witness2: Point2<f32>,
552    /// The local-space outward normal on the first shape at the time of impact.
553    ///
554    /// Undefined if `status` is `Penetrating`.
555    pub normal1: UnitVector2<f32>,
556    /// The local-space outward normal on the second shape at the time of impact.
557    ///
558    /// Undefined if `status` is `Penetrating`.
559    pub normal2: UnitVector2<f32>,
560    /// The way the time-of-impact computation algorithm terminated.
561    pub status: collider::TOIStatus,
562}
563
564impl PhysicsWorld {
565    /// Creates a new instance of the physics world.
566    pub(crate) fn new() -> Self {
567        Self {
568            enabled: true.into(),
569            pipeline: PhysicsPipeline::new(),
570            gravity: Vector2::new(0.0, -9.81).into(),
571            integration_parameters: IntegrationParameters::default().into(),
572            broad_phase: DefaultBroadPhase::new(),
573            narrow_phase: NarrowPhase::new(),
574            ccd_solver: CCDSolver::new(),
575            islands: IslandManager::new(),
576            bodies: RigidBodySet::new(),
577            colliders: ColliderSet::new(),
578            joints: Container {
579                set: ImpulseJointSet::new(),
580                map: Default::default(),
581            },
582            multibody_joints: Container {
583                set: MultibodyJointSet::new(),
584                map: Default::default(),
585            },
586            event_handler: Box::new(()),
587            query: RefCell::new(Default::default()),
588            performance_statistics: Default::default(),
589            debug_render_pipeline: Default::default(),
590        }
591    }
592
593    pub(crate) fn update(&mut self, dt: f32) {
594        let time = instant::Instant::now();
595
596        if *self.enabled {
597            let integration_parameters = rapier2d::dynamics::IntegrationParameters {
598                dt: self.integration_parameters.dt.unwrap_or(dt),
599                min_ccd_dt: self.integration_parameters.min_ccd_dt,
600                contact_damping_ratio: self.integration_parameters.contact_damping_ratio,
601                contact_natural_frequency: self.integration_parameters.contact_natural_frequency,
602                joint_natural_frequency: self.integration_parameters.joint_natural_frequency,
603                joint_damping_ratio: self.integration_parameters.joint_damping_ratio,
604                warmstart_coefficient: self.integration_parameters.warmstart_coefficient,
605                length_unit: self.integration_parameters.length_unit,
606                normalized_allowed_linear_error: self.integration_parameters.allowed_linear_error,
607                normalized_max_corrective_velocity: self
608                    .integration_parameters
609                    .normalized_max_corrective_velocity,
610                normalized_prediction_distance: self.integration_parameters.prediction_distance,
611                num_solver_iterations: NonZeroUsize::new(
612                    self.integration_parameters.num_solver_iterations,
613                )
614                .unwrap(),
615                num_additional_friction_iterations: self
616                    .integration_parameters
617                    .num_additional_friction_iterations,
618                num_internal_pgs_iterations: self
619                    .integration_parameters
620                    .num_internal_pgs_iterations,
621                num_internal_stabilization_iterations: self
622                    .integration_parameters
623                    .num_internal_stabilization_iterations,
624                min_island_size: self.integration_parameters.min_island_size as usize,
625                max_ccd_substeps: self.integration_parameters.max_ccd_substeps as usize,
626            };
627
628            self.pipeline.step(
629                &self.gravity,
630                &integration_parameters,
631                &mut self.islands,
632                &mut self.broad_phase,
633                &mut self.narrow_phase,
634                &mut self.bodies,
635                &mut self.colliders,
636                &mut self.joints.set,
637                &mut self.multibody_joints.set,
638                &mut self.ccd_solver,
639                // In Rapier 0.17 passing query pipeline here sometimes causing panic in numeric overflow,
640                // so we keep updating it manually.
641                None,
642                &(),
643                &*self.event_handler,
644            );
645        }
646
647        self.performance_statistics.step_time += instant::Instant::now() - time;
648    }
649
650    pub(crate) fn add_body(&mut self, owner: Handle<Node>, mut body: RigidBody) -> RigidBodyHandle {
651        body.user_data = owner.encode_to_u128();
652        self.bodies.insert(body)
653    }
654
655    pub(crate) fn remove_body(&mut self, handle: RigidBodyHandle) {
656        self.bodies.remove(
657            handle,
658            &mut self.islands,
659            &mut self.colliders,
660            &mut self.joints.set,
661            &mut self.multibody_joints.set,
662            true,
663        );
664    }
665
666    pub(crate) fn add_collider(
667        &mut self,
668        owner: Handle<Node>,
669        parent_body: RigidBodyHandle,
670        mut collider: Collider,
671    ) -> ColliderHandle {
672        collider.user_data = owner.encode_to_u128();
673        self.colliders
674            .insert_with_parent(collider, parent_body, &mut self.bodies)
675    }
676
677    pub(crate) fn remove_collider(&mut self, handle: ColliderHandle) -> bool {
678        self.colliders
679            .remove(handle, &mut self.islands, &mut self.bodies, false)
680            .is_some()
681    }
682
683    pub(crate) fn add_joint(
684        &mut self,
685        owner: Handle<Node>,
686        body1: RigidBodyHandle,
687        body2: RigidBodyHandle,
688        params: GenericJoint,
689    ) -> ImpulseJointHandle {
690        let handle = self.joints.set.insert(body1, body2, params, false);
691        self.joints.map.insert(handle, owner);
692        handle
693    }
694
695    pub(crate) fn remove_joint(&mut self, handle: ImpulseJointHandle) {
696        if self.joints.set.remove(handle, false).is_some() {
697            assert!(self.joints.map.remove_by_key(&handle).is_some());
698        }
699    }
700
701    /// Draws physics world. Very useful for debugging, it allows you to see where are
702    /// rigid bodies, which colliders they have and so on.
703    pub fn draw(&self, context: &mut SceneDrawingContext) {
704        self.debug_render_pipeline.lock().render(
705            context,
706            &self.bodies,
707            &self.colliders,
708            &self.joints.set,
709            &self.multibody_joints.set,
710            &self.narrow_phase,
711        );
712    }
713
714    /// Casts a ray with given options.
715    pub fn cast_ray<S: QueryResultsStorage>(&self, opts: RayCastOptions, query_buffer: &mut S) {
716        let time = instant::Instant::now();
717
718        let mut query = self.query.borrow_mut();
719
720        // TODO: Ideally this must be called once per frame, but it seems to be impossible because
721        // a body can be deleted during the consecutive calls of this method which will most
722        // likely end up in panic because of invalid handle stored in internal acceleration
723        // structure. This could be fixed by delaying deleting of bodies/collider to the end
724        // of the frame.
725        query.update(&self.colliders);
726
727        query_buffer.clear();
728        let ray = Ray::new(
729            opts.ray_origin,
730            opts.ray_direction
731                .try_normalize(f32::EPSILON)
732                .unwrap_or_default(),
733        );
734        query.intersections_with_ray(
735            &self.bodies,
736            &self.colliders,
737            &ray,
738            opts.max_len,
739            true,
740            rapier2d::pipeline::QueryFilter::new().groups(InteractionGroups::new(
741                u32_to_group(opts.groups.memberships.0),
742                u32_to_group(opts.groups.filter.0),
743            )),
744            |handle, intersection| {
745                query_buffer.push(Intersection {
746                    collider: Handle::decode_from_u128(
747                        self.colliders.get(handle).unwrap().user_data,
748                    ),
749                    normal: intersection.normal,
750                    position: ray.point_at(intersection.time_of_impact),
751                    feature: intersection.feature.into(),
752                    toi: intersection.time_of_impact,
753                })
754            },
755        );
756        if opts.sort_results {
757            query_buffer.sort_intersections_by(|a, b| {
758                if a.toi > b.toi {
759                    Ordering::Greater
760                } else if a.toi < b.toi {
761                    Ordering::Less
762                } else {
763                    Ordering::Equal
764                }
765            })
766        }
767
768        self.performance_statistics.total_ray_cast_time.set(
769            self.performance_statistics.total_ray_cast_time.get()
770                + (instant::Instant::now() - time),
771        );
772    }
773
774    /// Casts a shape at a constant linear velocity and retrieve the first collider it hits.
775    ///
776    /// This is similar to ray-casting except that we are casting a whole shape instead of just a
777    /// point (the ray origin). In the resulting `TOI`, witness and normal 1 refer to the world
778    /// collider, and are in world space.
779    ///
780    /// # Parameters
781    ///
782    /// * `graph` - a reference to the scene graph.
783    /// * `shape` - The shape to cast.
784    /// * `shape_pos` - The initial position of the shape to cast.
785    /// * `shape_vel` - The constant velocity of the shape to cast (i.e. the cast direction).
786    /// * `max_toi` - The maximum time-of-impact that can be reported by this cast. This effectively
787    ///   limits the distance traveled by the shape to `shapeVel.norm() * maxToi`.
788    /// * `stop_at_penetration` - If set to `false`, the linear shape-cast won’t immediately stop if
789    ///   the shape is penetrating another shape at its starting point **and** its trajectory is such
790    ///   that it’s on a path to exist that penetration state.
791    /// * `filter`: set of rules used to determine which collider is taken into account by this scene
792    ///   query.
793    pub fn cast_shape(
794        &self,
795        graph: &Graph,
796        shape: &dyn Shape,
797        shape_pos: &Isometry2<f32>,
798        shape_vel: &Vector2<f32>,
799        max_toi: f32,
800        stop_at_penetration: bool,
801        filter: QueryFilter,
802    ) -> Option<(Handle<Node>, TOI)> {
803        let predicate = |handle: ColliderHandle, _: &Collider| -> bool {
804            if let Some(pred) = filter.predicate {
805                let h = Handle::decode_from_u128(self.colliders.get(handle).unwrap().user_data);
806                pred(
807                    h,
808                    graph.node(h).component_ref::<collider::Collider>().unwrap(),
809                )
810            } else {
811                true
812            }
813        };
814
815        let filter = rapier2d::pipeline::QueryFilter {
816            flags: rapier2d::pipeline::QueryFilterFlags::from_bits(filter.flags.bits()).unwrap(),
817            groups: filter.groups.map(|g| {
818                InteractionGroups::new(u32_to_group(g.memberships.0), u32_to_group(g.filter.0))
819            }),
820            exclude_collider: filter
821                .exclude_collider
822                .and_then(|h| graph.try_get(h))
823                .and_then(|n| n.component_ref::<dim2::collider::Collider>())
824                .map(|c| c.native.get()),
825            exclude_rigid_body: filter
826                .exclude_collider
827                .and_then(|h| graph.try_get(h))
828                .and_then(|n| n.component_ref::<dim2::rigidbody::RigidBody>())
829                .map(|c| c.native.get()),
830            predicate: Some(&predicate),
831        };
832
833        let query = self.query.borrow_mut();
834
835        let opts = ShapeCastOptions {
836            max_time_of_impact: max_toi,
837            target_distance: 0.0,
838            stop_at_penetration,
839            compute_impact_geometry_on_penetration: true,
840        };
841
842        query
843            .cast_shape(
844                &self.bodies,
845                &self.colliders,
846                shape_pos,
847                shape_vel,
848                shape,
849                opts,
850                filter,
851            )
852            .map(|(handle, toi)| {
853                (
854                    Handle::decode_from_u128(self.colliders.get(handle).unwrap().user_data),
855                    TOI {
856                        toi: toi.time_of_impact,
857                        witness1: toi.witness1,
858                        witness2: toi.witness2,
859                        normal1: toi.normal1,
860                        normal2: toi.normal2,
861                        status: toi.status.into(),
862                    },
863                )
864            })
865    }
866
867    pub(crate) fn set_rigid_body_position(
868        &mut self,
869        rigid_body: &scene::dim2::rigidbody::RigidBody,
870        new_global_transform: &Matrix4<f32>,
871    ) {
872        if let Some(native) = self.bodies.get_mut(rigid_body.native.get()) {
873            native.set_position(
874                isometry_from_global_transform(new_global_transform),
875                // Do not wake up body, it is too expensive and must be done **only** by explicit
876                // `wake_up` call!
877                false,
878            );
879        }
880    }
881
882    pub(crate) fn sync_rigid_body_node(
883        &mut self,
884        rigid_body: &mut scene::dim2::rigidbody::RigidBody,
885        parent_transform: Matrix4<f32>,
886    ) {
887        if *self.enabled {
888            if let Some(native) = self.bodies.get(rigid_body.native.get()) {
889                if native.body_type() == RigidBodyType::Dynamic {
890                    let local_transform: Matrix4<f32> = parent_transform
891                        .try_inverse()
892                        .unwrap_or_else(Matrix4::identity)
893                        * isometry2_to_mat4(native.position());
894
895                    let new_local_rotation = UnitQuaternion::from_matrix_eps(
896                        &local_transform.basis(),
897                        f32::EPSILON,
898                        16,
899                        UnitQuaternion::identity(),
900                    );
901                    let new_local_position =
902                        Vector3::new(local_transform[12], local_transform[13], 0.0);
903
904                    // Do not touch local transform if position/rotation is not changing. This will
905                    // prevent redundant update of its global transform, which in its turn save some
906                    // CPU cycles.
907                    let local_transform = rigid_body.local_transform();
908                    if **local_transform.position() != new_local_position
909                        || **local_transform.rotation() != new_local_rotation
910                    {
911                        rigid_body
912                            .local_transform_mut()
913                            .set_position(new_local_position)
914                            .set_rotation(new_local_rotation);
915                    }
916
917                    rigid_body
918                        .lin_vel
919                        .set_value_with_flags(*native.linvel(), VariableFlags::MODIFIED);
920                    rigid_body
921                        .ang_vel
922                        .set_value_with_flags(native.angvel(), VariableFlags::MODIFIED);
923                    rigid_body.sleeping = native.is_sleeping();
924                }
925            }
926        }
927    }
928
929    pub(crate) fn sync_to_rigid_body_node(
930        &mut self,
931        handle: Handle<Node>,
932        rigid_body_node: &scene::dim2::rigidbody::RigidBody,
933    ) {
934        if !rigid_body_node.is_globally_enabled() {
935            self.remove_body(rigid_body_node.native.get());
936            rigid_body_node.native.set(Default::default());
937            return;
938        }
939
940        // Important notes!
941        // 1) `get_mut` is **very** expensive because it forces physics engine to recalculate contacts
942        //    and a lot of other stuff, this is why we need `anything_changed` flag.
943        if rigid_body_node.native.get() != RigidBodyHandle::invalid() {
944            let mut actions = rigid_body_node.actions.lock();
945            if rigid_body_node.need_sync_model() || !actions.is_empty() {
946                if let Some(native) = self.bodies.get_mut(rigid_body_node.native.get()) {
947                    // Sync native rigid body's properties with scene node's in case if they
948                    // were changed by user.
949                    rigid_body_node
950                        .body_type
951                        .try_sync_model(|v| native.set_body_type(v.into(), false));
952                    rigid_body_node
953                        .lin_vel
954                        .try_sync_model(|v| native.set_linvel(v, false));
955                    rigid_body_node
956                        .ang_vel
957                        .try_sync_model(|v| native.set_angvel(v, false));
958                    rigid_body_node.mass.try_sync_model(|v| {
959                        native.set_additional_mass(v, true);
960                    });
961                    rigid_body_node
962                        .lin_damping
963                        .try_sync_model(|v| native.set_linear_damping(v));
964                    rigid_body_node
965                        .ang_damping
966                        .try_sync_model(|v| native.set_angular_damping(v));
967                    rigid_body_node
968                        .ccd_enabled
969                        .try_sync_model(|v| native.enable_ccd(v));
970                    rigid_body_node.can_sleep.try_sync_model(|v| {
971                        let activation = native.activation_mut();
972                        if v {
973                            activation.normalized_linear_threshold =
974                                RigidBodyActivation::default_normalized_linear_threshold();
975                            activation.angular_threshold =
976                                RigidBodyActivation::default_angular_threshold();
977                        } else {
978                            activation.sleeping = false;
979                            activation.normalized_linear_threshold = -1.0;
980                            activation.angular_threshold = -1.0;
981                        };
982                    });
983                    rigid_body_node
984                        .translation_locked
985                        .try_sync_model(|v| native.lock_translations(v, false));
986                    rigid_body_node.rotation_locked.try_sync_model(|v| {
987                        native.set_enabled_rotations(!v, !v, !v, false);
988                    });
989                    rigid_body_node
990                        .dominance
991                        .try_sync_model(|v| native.set_dominance_group(v));
992                    rigid_body_node
993                        .gravity_scale
994                        .try_sync_model(|v| native.set_gravity_scale(v, false));
995
996                    // We must reset any forces applied at previous update step, otherwise physics engine
997                    // will keep pushing the rigid body infinitely.
998                    if rigid_body_node.reset_forces.replace(false) {
999                        native.reset_forces(false);
1000                        native.reset_torques(false);
1001                    }
1002
1003                    while let Some(action) = actions.pop_front() {
1004                        match action {
1005                            ApplyAction::Force(force) => {
1006                                native.add_force(force, false);
1007                                rigid_body_node.reset_forces.set(true);
1008                            }
1009                            ApplyAction::Torque(torque) => {
1010                                native.add_torque(torque, false);
1011                                rigid_body_node.reset_forces.set(true);
1012                            }
1013                            ApplyAction::ForceAtPoint { force, point } => {
1014                                native.add_force_at_point(force, Point2::from(point), false);
1015                                rigid_body_node.reset_forces.set(true);
1016                            }
1017                            ApplyAction::Impulse(impulse) => native.apply_impulse(impulse, false),
1018                            ApplyAction::TorqueImpulse(impulse) => {
1019                                native.apply_torque_impulse(impulse, false)
1020                            }
1021                            ApplyAction::ImpulseAtPoint { impulse, point } => {
1022                                native.apply_impulse_at_point(impulse, Point2::from(point), false)
1023                            }
1024                            ApplyAction::WakeUp => native.wake_up(true),
1025                        }
1026                    }
1027                }
1028            }
1029        } else {
1030            let mut builder = RigidBodyBuilder::new(rigid_body_node.body_type().into())
1031                .position(isometry_from_global_transform(
1032                    &rigid_body_node.global_transform(),
1033                ))
1034                .ccd_enabled(rigid_body_node.is_ccd_enabled())
1035                .additional_mass(rigid_body_node.mass())
1036                .angvel(*rigid_body_node.ang_vel)
1037                .linvel(*rigid_body_node.lin_vel)
1038                .linear_damping(*rigid_body_node.lin_damping)
1039                .angular_damping(*rigid_body_node.ang_damping)
1040                .can_sleep(rigid_body_node.is_can_sleep())
1041                .sleeping(rigid_body_node.is_sleeping())
1042                .dominance_group(rigid_body_node.dominance())
1043                .gravity_scale(rigid_body_node.gravity_scale());
1044
1045            if rigid_body_node.is_translation_locked() {
1046                builder = builder.lock_translations();
1047            }
1048
1049            let mut body = builder.build();
1050
1051            body.set_enabled_rotations(
1052                !rigid_body_node.is_rotation_locked(),
1053                !rigid_body_node.is_rotation_locked(),
1054                !rigid_body_node.is_rotation_locked(),
1055                false,
1056            );
1057
1058            rigid_body_node.native.set(self.add_body(handle, body));
1059
1060            Log::writeln(
1061                MessageKind::Information,
1062                format!(
1063                    "Native rigid body was created for node {}",
1064                    rigid_body_node.name()
1065                ),
1066            );
1067        }
1068    }
1069
1070    pub(crate) fn sync_to_collider_node(
1071        &mut self,
1072        nodes: &NodePool,
1073        handle: Handle<Node>,
1074        collider_node: &scene::dim2::collider::Collider,
1075    ) {
1076        if !collider_node.is_globally_enabled() {
1077            self.remove_collider(collider_node.native.get());
1078            collider_node.native.set(Default::default());
1079            return;
1080        }
1081
1082        let anything_changed = collider_node.needs_sync_model();
1083
1084        // Important notes!
1085        // 1) The collider node may lack backing native physics collider in case if it
1086        //    is not attached to a rigid body.
1087        // 2) `get_mut` is **very** expensive because it forces physics engine to recalculate contacts
1088        //    and a lot of other stuff, this is why we need `anything_changed` flag.
1089        if collider_node.native.get() != ColliderHandle::invalid() {
1090            if anything_changed {
1091                if let Some(native) = self.colliders.get_mut(collider_node.native.get()) {
1092                    collider_node
1093                        .restitution
1094                        .try_sync_model(|v| native.set_restitution(v));
1095                    collider_node.collision_groups.try_sync_model(|v| {
1096                        native.set_collision_groups(InteractionGroups::new(
1097                            u32_to_group(v.memberships.0),
1098                            u32_to_group(v.filter.0),
1099                        ))
1100                    });
1101                    collider_node.solver_groups.try_sync_model(|v| {
1102                        native.set_solver_groups(InteractionGroups::new(
1103                            u32_to_group(v.memberships.0),
1104                            u32_to_group(v.filter.0),
1105                        ))
1106                    });
1107                    collider_node
1108                        .friction
1109                        .try_sync_model(|v| native.set_friction(v));
1110                    collider_node
1111                        .is_sensor
1112                        .try_sync_model(|v| native.set_sensor(v));
1113                    collider_node
1114                        .friction_combine_rule
1115                        .try_sync_model(|v| native.set_friction_combine_rule(v.into()));
1116                    collider_node
1117                        .restitution_combine_rule
1118                        .try_sync_model(|v| native.set_restitution_combine_rule(v.into()));
1119                    let mut remove_collider = false;
1120                    collider_node.shape.try_sync_model(|v| {
1121                        let inv_global_transform = isometric_global_transform(nodes, handle)
1122                            .try_inverse()
1123                            .unwrap_or_default();
1124
1125                        if let Some(shape) =
1126                            collider_shape_into_native_shape(&v, inv_global_transform, nodes)
1127                        {
1128                            native.set_shape(shape);
1129                        } else {
1130                            remove_collider = true;
1131                        }
1132                    });
1133                    if remove_collider {
1134                        self.remove_collider(collider_node.native.get());
1135                        collider_node.native.set(ColliderHandle::invalid());
1136                    }
1137                }
1138            }
1139        } else if let Some(parent_body) = nodes
1140            .try_borrow(collider_node.parent())
1141            .and_then(|n| n.cast::<dim2::rigidbody::RigidBody>())
1142        {
1143            if parent_body.native.get() != RigidBodyHandle::invalid() {
1144                let rigid_body_native = parent_body.native.get();
1145                let inv_global_transform = isometric_global_transform(nodes, handle)
1146                    .try_inverse()
1147                    .unwrap();
1148                if let Some(shape) = collider_shape_into_native_shape(
1149                    collider_node.shape(),
1150                    inv_global_transform,
1151                    nodes,
1152                ) {
1153                    let mut builder = ColliderBuilder::new(shape)
1154                        .position(Isometry2 {
1155                            rotation: UnitComplex::from_angle(
1156                                collider_node.local_transform().rotation().euler_angles().2,
1157                            ),
1158                            translation: Translation2 {
1159                                vector: collider_node.local_transform().position().xy(),
1160                            },
1161                        })
1162                        .friction(collider_node.friction())
1163                        .restitution(collider_node.restitution())
1164                        .collision_groups(InteractionGroups::new(
1165                            u32_to_group(collider_node.collision_groups().memberships.0),
1166                            u32_to_group(collider_node.collision_groups().filter.0),
1167                        ))
1168                        .friction_combine_rule(collider_node.friction_combine_rule().into())
1169                        .restitution_combine_rule(collider_node.restitution_combine_rule().into())
1170                        .solver_groups(InteractionGroups::new(
1171                            u32_to_group(collider_node.solver_groups().memberships.0),
1172                            u32_to_group(collider_node.solver_groups().filter.0),
1173                        ))
1174                        .sensor(collider_node.is_sensor());
1175
1176                    if let Some(density) = collider_node.density() {
1177                        builder = builder.density(density);
1178                    }
1179
1180                    let native_handle =
1181                        self.add_collider(handle, rigid_body_native, builder.build());
1182
1183                    collider_node.native.set(native_handle);
1184
1185                    Log::writeln(
1186                        MessageKind::Information,
1187                        format!(
1188                            "Native collider was created for node {}",
1189                            collider_node.name()
1190                        ),
1191                    );
1192                }
1193            }
1194        }
1195    }
1196
1197    pub(crate) fn sync_to_joint_node(
1198        &mut self,
1199        nodes: &NodePool,
1200        handle: Handle<Node>,
1201        joint: &scene::dim2::joint::Joint,
1202    ) {
1203        if !joint.is_globally_enabled() {
1204            self.remove_joint(joint.native.get());
1205            joint.native.set(ImpulseJointHandle(Default::default()));
1206            return;
1207        }
1208
1209        if let Some(native) = self.joints.set.get_mut(joint.native.get(), false) {
1210            joint.body1.try_sync_model(|v| {
1211                if let Some(rigid_body_node) = nodes
1212                    .try_borrow(v)
1213                    .and_then(|n| n.cast::<dim2::rigidbody::RigidBody>())
1214                {
1215                    native.body1 = rigid_body_node.native.get();
1216                }
1217            });
1218            joint.body2.try_sync_model(|v| {
1219                if let Some(rigid_body_node) = nodes
1220                    .try_borrow(v)
1221                    .and_then(|n| n.cast::<dim2::rigidbody::RigidBody>())
1222                {
1223                    native.body2 = rigid_body_node.native.get();
1224                }
1225            });
1226            joint.params.try_sync_model(|v| {
1227                native.data =
1228                    // Preserve local frames.
1229                    convert_joint_params(v, native.data.local_frame1, native.data.local_frame2)
1230            });
1231            joint.contacts_enabled.try_sync_model(|v| {
1232                native.data.set_contacts_enabled(v);
1233            });
1234            let mut local_frames = joint.local_frames.borrow_mut();
1235            if local_frames.is_none() {
1236                if let (Some(body1), Some(body2)) = (
1237                    nodes
1238                        .try_borrow(joint.body1())
1239                        .and_then(|n| n.cast::<scene::rigidbody::RigidBody>()),
1240                    nodes
1241                        .try_borrow(joint.body2())
1242                        .and_then(|n| n.cast::<scene::rigidbody::RigidBody>()),
1243                ) {
1244                    let (local_frame1, local_frame2) = calculate_local_frames(joint, body1, body2);
1245                    native.data =
1246                        convert_joint_params((*joint.params).clone(), local_frame1, local_frame2);
1247                    *local_frames = Some(JointLocalFrames::new(&local_frame1, &local_frame2));
1248                }
1249            }
1250        } else {
1251            let body1_handle = joint.body1();
1252            let body2_handle = joint.body2();
1253            let params = joint.params().clone();
1254
1255            // A native joint can be created iff both rigid bodies are correctly assigned.
1256            if let (Some(body1), Some(body2)) = (
1257                nodes
1258                    .try_borrow(body1_handle)
1259                    .and_then(|n| n.cast::<dim2::rigidbody::RigidBody>()),
1260                nodes
1261                    .try_borrow(body2_handle)
1262                    .and_then(|n| n.cast::<dim2::rigidbody::RigidBody>()),
1263            ) {
1264                // Calculate local frames first (if needed).
1265                let mut local_frames = joint.local_frames.borrow_mut();
1266                let (local_frame1, local_frame2) = local_frames
1267                    .clone()
1268                    .map(|frames| {
1269                        (
1270                            Isometry2 {
1271                                rotation: frames.body1.rotation,
1272                                translation: Translation2 {
1273                                    vector: frames.body1.position,
1274                                },
1275                            },
1276                            Isometry2 {
1277                                rotation: frames.body2.rotation,
1278                                translation: Translation2 {
1279                                    vector: frames.body2.position,
1280                                },
1281                            },
1282                        )
1283                    })
1284                    .unwrap_or_else(|| calculate_local_frames(joint, body1, body2));
1285
1286                let native_body1 = body1.native.get();
1287                let native_body2 = body2.native.get();
1288
1289                assert!(self.bodies.get(native_body1).is_some());
1290                assert!(self.bodies.get(native_body2).is_some());
1291
1292                let mut native_joint = convert_joint_params(params, local_frame1, local_frame2);
1293                native_joint.contacts_enabled = joint.is_contacts_enabled();
1294                let native_handle =
1295                    self.add_joint(handle, native_body1, native_body2, native_joint);
1296
1297                joint.native.set(native_handle);
1298                *local_frames = Some(JointLocalFrames::new(&local_frame1, &local_frame2));
1299
1300                Log::writeln(
1301                    MessageKind::Information,
1302                    format!("Native joint was created for node {}", joint.name()),
1303                );
1304            }
1305        }
1306    }
1307
1308    /// Intersections checks between regular colliders and sensor colliders
1309    pub(crate) fn intersections_with(
1310        &self,
1311        collider: ColliderHandle,
1312    ) -> impl Iterator<Item = IntersectionPair> + '_ {
1313        self.narrow_phase
1314            .intersection_pairs_with(collider)
1315            .filter_map(|(collider1, collider2, intersecting)| {
1316                Some(IntersectionPair {
1317                    collider1: Handle::decode_from_u128(self.colliders.get(collider1)?.user_data),
1318                    collider2: Handle::decode_from_u128(self.colliders.get(collider2)?.user_data),
1319                    has_any_active_contact: intersecting,
1320                })
1321            })
1322    }
1323
1324    /// Contacts checks between two regular colliders
1325    pub(crate) fn contacts_with(
1326        &self,
1327        collider: ColliderHandle,
1328    ) -> impl Iterator<Item = ContactPair> + '_ {
1329        self.narrow_phase
1330            .contact_pairs_with(collider)
1331            .filter_map(|c| ContactPair::from_native(c, self))
1332    }
1333
1334    /// Returns an iterator over all contact pairs generated in this frame.
1335    pub fn contacts(&self) -> impl Iterator<Item = ContactPair> + '_ {
1336        self.narrow_phase
1337            .contact_pairs()
1338            .filter_map(|c| ContactPair::from_native(c, self))
1339    }
1340}
1341
1342impl Default for PhysicsWorld {
1343    fn default() -> Self {
1344        Self::new()
1345    }
1346}
1347
1348impl Debug for PhysicsWorld {
1349    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1350        write!(f, "PhysicsWorld")
1351    }
1352}