fyrox_impl/scene/
collider.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//! Collider is a geometric entity that can be attached to a rigid body to allow participate it
22//! participate in contact generation, collision response and proximity queries.
23
24use crate::scene::node::constructor::NodeConstructor;
25use crate::{
26    core::{
27        algebra::Vector3,
28        log::Log,
29        math::aabb::AxisAlignedBoundingBox,
30        num_traits::{NumCast, One, ToPrimitive, Zero},
31        pool::Handle,
32        reflect::prelude::*,
33        type_traits::prelude::*,
34        uuid::{uuid, Uuid},
35        variable::InheritableVariable,
36        visitor::prelude::*,
37    },
38    scene::{
39        base::{Base, BaseBuilder},
40        graph::{
41            physics::{CoefficientCombineRule, ContactPair, IntersectionPair, PhysicsWorld},
42            Graph,
43        },
44        node::{Node, NodeTrait, SyncContext},
45        rigidbody::RigidBody,
46        Scene,
47    },
48};
49use fyrox_core::algebra::{Isometry3, Translation3};
50use fyrox_core::uuid_provider;
51use fyrox_graph::constructor::ConstructorProvider;
52use fyrox_graph::{BaseSceneGraph, SceneGraphNode};
53use rapier3d::geometry::{self, ColliderHandle};
54use std::{
55    cell::Cell,
56    ops::{Add, BitAnd, BitOr, Deref, DerefMut, Mul, Not, Shl},
57};
58use strum_macros::{AsRefStr, EnumString, VariantNames};
59
60/// Ball is an idea sphere shape defined by a single parameters - its radius.
61#[derive(Clone, Debug, PartialEq, Visit, Reflect)]
62pub struct BallShape {
63    /// Radius of the sphere.
64    #[reflect(min_value = 0.001, step = 0.05)]
65    pub radius: f32,
66}
67
68impl Default for BallShape {
69    fn default() -> Self {
70        Self { radius: 0.5 }
71    }
72}
73
74/// Cylinder shape aligned in Y axis.
75#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
76pub struct CylinderShape {
77    /// Half height of the cylinder, actual height will be 2 times bigger.
78    #[reflect(min_value = 0.001, step = 0.05)]
79    pub half_height: f32,
80    /// Radius of the cylinder.
81    #[reflect(min_value = 0.001, step = 0.05)]
82    pub radius: f32,
83}
84
85impl Default for CylinderShape {
86    fn default() -> Self {
87        Self {
88            half_height: 0.5,
89            radius: 0.5,
90        }
91    }
92}
93
94/// Cone shape aligned in Y axis.
95#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
96pub struct ConeShape {
97    /// Half height of the cone, actual height will be 2 times bigger.
98    #[reflect(min_value = 0.001, step = 0.05)]
99    pub half_height: f32,
100    /// Radius of the cone base.
101    #[reflect(min_value = 0.001, step = 0.05)]
102    pub radius: f32,
103}
104
105impl Default for ConeShape {
106    fn default() -> Self {
107        Self {
108            half_height: 0.5,
109            radius: 0.5,
110        }
111    }
112}
113
114/// Cuboid shape (box).
115#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
116pub struct CuboidShape {
117    /// Half extents of the box. X - half width, Y - half height, Z - half depth.
118    /// Actual _size_ will be 2 times bigger.
119    #[reflect(min_value = 0.001, step = 0.05)]
120    pub half_extents: Vector3<f32>,
121}
122
123impl Default for CuboidShape {
124    fn default() -> Self {
125        Self {
126            half_extents: Vector3::new(0.5, 0.5, 0.5),
127        }
128    }
129}
130
131/// Arbitrary capsule shape defined by 2 points (which forms axis) and a radius.
132#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
133pub struct CapsuleShape {
134    /// Begin point of the capsule.
135    pub begin: Vector3<f32>,
136    /// End point of the capsule.
137    pub end: Vector3<f32>,
138    /// Radius of the capsule.
139    #[reflect(min_value = 0.001, step = 0.05)]
140    pub radius: f32,
141}
142
143impl Default for CapsuleShape {
144    // Y-capsule
145    fn default() -> Self {
146        Self {
147            begin: Default::default(),
148            end: Vector3::new(0.0, 1.0, 0.0),
149            radius: 0.5,
150        }
151    }
152}
153
154/// Arbitrary segment shape defined by two points.
155#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
156pub struct SegmentShape {
157    /// Begin point of the capsule.
158    pub begin: Vector3<f32>,
159    /// End point of the capsule.
160    pub end: Vector3<f32>,
161}
162
163impl Default for SegmentShape {
164    fn default() -> Self {
165        Self {
166            begin: Default::default(),
167            end: Vector3::new(0.0, 1.0, 0.0),
168        }
169    }
170}
171
172/// Arbitrary triangle shape.
173#[derive(Clone, Debug, Visit, Reflect, PartialEq)]
174pub struct TriangleShape {
175    /// First point of the triangle shape.
176    pub a: Vector3<f32>,
177    /// Second point of the triangle shape.
178    pub b: Vector3<f32>,
179    /// Third point of the triangle shape.
180    pub c: Vector3<f32>,
181}
182
183impl Default for TriangleShape {
184    fn default() -> Self {
185        Self {
186            a: Default::default(),
187            b: Vector3::new(1.0, 0.0, 0.0),
188            c: Vector3::new(0.0, 0.0, 1.0),
189        }
190    }
191}
192
193/// Geometry source for colliders with complex geometry.
194///
195/// # Notes
196///
197/// Currently there is only one way to set geometry - using a scene node as a source of data.
198#[derive(Default, Clone, Copy, PartialEq, Hash, Debug, Visit, Reflect, Eq)]
199pub struct GeometrySource(pub Handle<Node>);
200
201uuid_provider!(GeometrySource = "6fea7c72-c488-48a1-935f-2752a8a10e9a");
202
203/// Arbitrary triangle mesh shape.
204#[derive(Default, Clone, Debug, Visit, Reflect, PartialEq, Eq)]
205pub struct TrimeshShape {
206    /// Geometry sources for the shape.
207    pub sources: Vec<GeometrySource>,
208}
209
210/// Arbitrary height field shape.
211#[derive(Default, Clone, Debug, Visit, Reflect, PartialEq, Eq)]
212pub struct HeightfieldShape {
213    /// A handle to terrain scene node.
214    pub geometry_source: GeometrySource,
215}
216
217/// Arbitrary convex polyhedron shape.
218#[derive(Default, Clone, Debug, Visit, Reflect, PartialEq, Eq)]
219pub struct ConvexPolyhedronShape {
220    /// A handle to a mesh node.
221    pub geometry_source: GeometrySource,
222}
223
224/// A set of bits used for pairwise collision filtering.
225#[derive(Clone, Copy, Default, PartialEq, Debug, Reflect, Eq)]
226pub struct BitMask(pub u32);
227
228uuid_provider!(BitMask = "f2db0c2a-921b-4728-9ce4-2506d95c60fa");
229
230impl Visit for BitMask {
231    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
232        self.0.visit(name, visitor)
233    }
234}
235
236impl BitOr for BitMask {
237    type Output = Self;
238
239    fn bitor(self, rhs: Self) -> Self::Output {
240        Self(self.0 | rhs.0)
241    }
242}
243
244impl BitAnd for BitMask {
245    type Output = Self;
246
247    fn bitand(self, rhs: Self) -> Self::Output {
248        Self(self.0 & rhs.0)
249    }
250}
251
252impl Mul for BitMask {
253    type Output = Self;
254
255    fn mul(self, rhs: Self) -> Self::Output {
256        Self(self.0 * rhs.0)
257    }
258}
259
260impl One for BitMask {
261    fn one() -> Self {
262        Self(1)
263    }
264}
265
266impl Add for BitMask {
267    type Output = Self;
268
269    fn add(self, rhs: Self) -> Self::Output {
270        Self(self.0 + rhs.0)
271    }
272}
273
274impl Zero for BitMask {
275    fn zero() -> Self {
276        Self(0)
277    }
278
279    fn is_zero(&self) -> bool {
280        self.0 == 0
281    }
282}
283
284impl Shl for BitMask {
285    type Output = Self;
286
287    fn shl(self, rhs: Self) -> Self::Output {
288        Self(self.0 << rhs.0)
289    }
290}
291
292impl Not for BitMask {
293    type Output = Self;
294
295    fn not(self) -> Self::Output {
296        Self(!self.0)
297    }
298}
299
300impl ToPrimitive for BitMask {
301    fn to_i64(&self) -> Option<i64> {
302        Some(self.0 as i64)
303    }
304
305    fn to_u64(&self) -> Option<u64> {
306        Some(self.0 as u64)
307    }
308}
309
310impl NumCast for BitMask {
311    fn from<T: ToPrimitive>(n: T) -> Option<Self> {
312        n.to_u32().map(Self)
313    }
314}
315
316/// Pairwise filtering using bit masks.
317///
318/// This filtering method is based on two 32-bit values:
319/// - The interaction groups memberships.
320/// - The interaction groups filter.
321///
322/// An interaction is allowed between two filters `a` and `b` when two conditions
323/// are met simultaneously:
324/// - The groups membership of `a` has at least one bit set to `1` in common with the groups filter of `b`.
325/// - The groups membership of `b` has at least one bit set to `1` in common with the groups filter of `a`.
326///
327/// In other words, interactions are allowed between two filter iff. the following condition is met:
328/// ```ignore
329/// (self.memberships & rhs.filter) != 0 && (rhs.memberships & self.filter) != 0
330/// ```
331#[derive(Visit, Debug, Clone, Copy, PartialEq, Reflect, Eq)]
332pub struct InteractionGroups {
333    /// Groups memberships.
334    pub memberships: BitMask,
335    /// Groups filter.
336    pub filter: BitMask,
337}
338
339impl InteractionGroups {
340    /// Creates new interaction group using given values.
341    pub fn new(memberships: BitMask, filter: BitMask) -> Self {
342        Self {
343            memberships,
344            filter,
345        }
346    }
347}
348
349impl Default for InteractionGroups {
350    fn default() -> Self {
351        Self {
352            memberships: BitMask(u32::MAX),
353            filter: BitMask(u32::MAX),
354        }
355    }
356}
357
358impl From<geometry::InteractionGroups> for InteractionGroups {
359    fn from(g: geometry::InteractionGroups) -> Self {
360        Self {
361            memberships: BitMask(g.memberships.bits()),
362            filter: BitMask(g.filter.bits()),
363        }
364    }
365}
366
367bitflags::bitflags! {
368    #[derive(Default, Copy, Clone)]
369    /// Flags for excluding whole sets of colliders from a scene query.
370    pub struct QueryFilterFlags: u32 {
371        /// Exclude from the query any collider attached to a fixed rigid-body and colliders with no rigid-body attached.
372        const EXCLUDE_FIXED = 1 << 1;
373        /// Exclude from the query any collider attached to a kinematic rigid-body.
374        const EXCLUDE_KINEMATIC = 1 << 2;
375        /// Exclude from the query any collider attached to a dynamic rigid-body.
376        const EXCLUDE_DYNAMIC = 1 << 3;
377        /// Exclude from the query any collider that is a sensor.
378        const EXCLUDE_SENSORS = 1 << 4;
379        /// Exclude from the query any collider that is not a sensor.
380        const EXCLUDE_SOLIDS = 1 << 5;
381        /// Excludes all colliders not attached to a dynamic rigid-body.
382        const ONLY_DYNAMIC = Self::EXCLUDE_FIXED.bits() | Self::EXCLUDE_KINEMATIC.bits();
383        /// Excludes all colliders not attached to a kinematic rigid-body.
384        const ONLY_KINEMATIC = Self::EXCLUDE_DYNAMIC.bits() | Self::EXCLUDE_FIXED.bits();
385        /// Exclude all colliders attached to a non-fixed rigid-body
386        /// (this will not exclude colliders not attached to any rigid-body).
387        const ONLY_FIXED = Self::EXCLUDE_DYNAMIC.bits() | Self::EXCLUDE_KINEMATIC.bits();
388    }
389}
390
391/// The status of the time-of-impact computation algorithm.
392#[derive(Copy, Clone, Debug, PartialEq, Eq)]
393pub enum TOIStatus {
394    /// The TOI algorithm ran out of iterations before achieving convergence.
395    ///
396    /// The content of the `TOI` will still be a conservative approximation of the actual result so
397    /// it is often fine to interpret this case as a success.
398    OutOfIterations,
399    /// The TOI algorithm converged successfully.
400    Converged,
401    /// Something went wrong during the TOI computation, likely due to numerical instabilities.
402    ///
403    /// The content of the `TOI` will still be a conservative approximation of the actual result so
404    /// it is often fine to interpret this case as a success.
405    Failed,
406    /// The two shape already overlap at the time 0.
407    ///
408    /// The witness points and normals provided by the `TOI` will have undefined values.
409    Penetrating,
410}
411
412impl From<rapier3d::parry::query::ShapeCastStatus> for TOIStatus {
413    fn from(value: rapier3d::parry::query::ShapeCastStatus) -> Self {
414        match value {
415            rapier3d::parry::query::ShapeCastStatus::OutOfIterations => Self::OutOfIterations,
416            rapier3d::parry::query::ShapeCastStatus::Converged => Self::Converged,
417            rapier3d::parry::query::ShapeCastStatus::Failed => Self::Failed,
418            rapier3d::parry::query::ShapeCastStatus::PenetratingOrWithinTargetDist => {
419                Self::Penetrating
420            }
421        }
422    }
423}
424
425impl From<rapier2d::parry::query::ShapeCastStatus> for TOIStatus {
426    fn from(value: rapier2d::parry::query::ShapeCastStatus) -> Self {
427        match value {
428            rapier2d::parry::query::ShapeCastStatus::OutOfIterations => Self::OutOfIterations,
429            rapier2d::parry::query::ShapeCastStatus::Converged => Self::Converged,
430            rapier2d::parry::query::ShapeCastStatus::Failed => Self::Failed,
431            rapier2d::parry::query::ShapeCastStatus::PenetratingOrWithinTargetDist => {
432                Self::Penetrating
433            }
434        }
435    }
436}
437
438/// Possible collider shapes.
439#[derive(Clone, Debug, PartialEq, Visit, Reflect, AsRefStr, EnumString, VariantNames)]
440pub enum ColliderShape {
441    /// See [`BallShape`] docs.
442    Ball(BallShape),
443    /// See [`CylinderShape`] docs.
444    Cylinder(CylinderShape),
445    /// See [`ConeShape`] docs.
446    Cone(ConeShape),
447    /// See [`CuboidShape`] docs.
448    Cuboid(CuboidShape),
449    /// See [`CapsuleShape`] docs.
450    Capsule(CapsuleShape),
451    /// See [`SegmentShape`] docs.
452    Segment(SegmentShape),
453    /// See [`TriangleShape`] docs.
454    Triangle(TriangleShape),
455    /// See [`TrimeshShape`] docs.
456    Trimesh(TrimeshShape),
457    /// See [`HeightfieldShape`] docs.
458    Heightfield(HeightfieldShape),
459    /// See [`ConvexPolyhedronShape`] docs.
460    Polyhedron(ConvexPolyhedronShape),
461}
462
463uuid_provider!(ColliderShape = "2e627337-71ea-4b33-a5f1-be697f705a86");
464
465impl Default for ColliderShape {
466    fn default() -> Self {
467        Self::Ball(Default::default())
468    }
469}
470
471impl ColliderShape {
472    /// Initializes a ball shape defined by its radius.
473    pub fn ball(radius: f32) -> Self {
474        Self::Ball(BallShape { radius })
475    }
476
477    /// Initializes a cylindrical shape defined by its half-height (along along the y axis) and its
478    /// radius.
479    pub fn cylinder(half_height: f32, radius: f32) -> Self {
480        Self::Cylinder(CylinderShape {
481            half_height,
482            radius,
483        })
484    }
485
486    /// Initializes a cone shape defined by its half-height (along along the y axis) and its basis
487    /// radius.
488    pub fn cone(half_height: f32, radius: f32) -> Self {
489        Self::Cone(ConeShape {
490            half_height,
491            radius,
492        })
493    }
494
495    /// Initializes a cuboid shape defined by its half-extents.
496    pub fn cuboid(hx: f32, hy: f32, hz: f32) -> Self {
497        Self::Cuboid(CuboidShape {
498            half_extents: Vector3::new(hx, hy, hz),
499        })
500    }
501
502    /// Initializes a capsule shape from its endpoints and radius.
503    pub fn capsule(begin: Vector3<f32>, end: Vector3<f32>, radius: f32) -> Self {
504        Self::Capsule(CapsuleShape { begin, end, radius })
505    }
506
507    /// Initializes a new collider builder with a capsule shape aligned with the `x` axis.
508    pub fn capsule_x(half_height: f32, radius: f32) -> Self {
509        let p = Vector3::x() * half_height;
510        Self::capsule(-p, p, radius)
511    }
512
513    /// Initializes a new collider builder with a capsule shape aligned with the `y` axis.
514    pub fn capsule_y(half_height: f32, radius: f32) -> Self {
515        let p = Vector3::y() * half_height;
516        Self::capsule(-p, p, radius)
517    }
518
519    /// Initializes a new collider builder with a capsule shape aligned with the `z` axis.
520    pub fn capsule_z(half_height: f32, radius: f32) -> Self {
521        let p = Vector3::z() * half_height;
522        Self::capsule(-p, p, radius)
523    }
524
525    /// Initializes a segment shape from its endpoints.
526    pub fn segment(begin: Vector3<f32>, end: Vector3<f32>) -> Self {
527        Self::Segment(SegmentShape { begin, end })
528    }
529
530    /// Initializes a triangle shape.
531    pub fn triangle(a: Vector3<f32>, b: Vector3<f32>, c: Vector3<f32>) -> Self {
532        Self::Triangle(TriangleShape { a, b, c })
533    }
534
535    /// Initializes a triangle mesh shape defined by a set of handles to mesh nodes that will be
536    /// used to create physical shape.
537    pub fn trimesh(geometry_sources: Vec<GeometrySource>) -> Self {
538        Self::Trimesh(TrimeshShape {
539            sources: geometry_sources,
540        })
541    }
542
543    /// Initializes a heightfield shape defined by a handle to terrain node.
544    pub fn heightfield(geometry_source: GeometrySource) -> Self {
545        Self::Heightfield(HeightfieldShape { geometry_source })
546    }
547}
548
549/// Collider is a geometric entity that can be attached to a rigid body to allow participate it
550/// participate in contact generation, collision response and proximity queries.
551#[derive(Reflect, Visit, Debug, ComponentProvider)]
552pub struct Collider {
553    base: Base,
554
555    #[reflect(setter = "set_shape")]
556    pub(crate) shape: InheritableVariable<ColliderShape>,
557
558    #[reflect(min_value = 0.0, step = 0.05, setter = "set_friction")]
559    pub(crate) friction: InheritableVariable<f32>,
560
561    #[reflect(setter = "set_density")]
562    pub(crate) density: InheritableVariable<Option<f32>>,
563
564    #[reflect(min_value = 0.0, step = 0.05, setter = "set_restitution")]
565    pub(crate) restitution: InheritableVariable<f32>,
566
567    #[reflect(setter = "set_is_sensor")]
568    pub(crate) is_sensor: InheritableVariable<bool>,
569
570    #[reflect(setter = "set_collision_groups")]
571    pub(crate) collision_groups: InheritableVariable<InteractionGroups>,
572
573    #[reflect(setter = "set_solver_groups")]
574    pub(crate) solver_groups: InheritableVariable<InteractionGroups>,
575
576    #[reflect(setter = "set_friction_combine_rule")]
577    pub(crate) friction_combine_rule: InheritableVariable<CoefficientCombineRule>,
578
579    #[reflect(setter = "set_restitution_combine_rule")]
580    pub(crate) restitution_combine_rule: InheritableVariable<CoefficientCombineRule>,
581
582    #[visit(skip)]
583    #[reflect(hidden)]
584    pub(crate) native: Cell<ColliderHandle>,
585}
586
587impl Default for Collider {
588    fn default() -> Self {
589        Self {
590            base: Default::default(),
591            shape: Default::default(),
592            friction: InheritableVariable::new_modified(0.0),
593            density: InheritableVariable::new_modified(None),
594            restitution: InheritableVariable::new_modified(0.0),
595            is_sensor: InheritableVariable::new_modified(false),
596            collision_groups: Default::default(),
597            solver_groups: Default::default(),
598            friction_combine_rule: Default::default(),
599            restitution_combine_rule: Default::default(),
600            native: Cell::new(ColliderHandle::invalid()),
601        }
602    }
603}
604
605impl Deref for Collider {
606    type Target = Base;
607
608    fn deref(&self) -> &Self::Target {
609        &self.base
610    }
611}
612
613impl DerefMut for Collider {
614    fn deref_mut(&mut self) -> &mut Self::Target {
615        &mut self.base
616    }
617}
618
619impl Clone for Collider {
620    fn clone(&self) -> Self {
621        Self {
622            base: self.base.clone(),
623            shape: self.shape.clone(),
624            friction: self.friction.clone(),
625            density: self.density.clone(),
626            restitution: self.restitution.clone(),
627            is_sensor: self.is_sensor.clone(),
628            collision_groups: self.collision_groups.clone(),
629            solver_groups: self.solver_groups.clone(),
630            friction_combine_rule: self.friction_combine_rule.clone(),
631            restitution_combine_rule: self.restitution_combine_rule.clone(),
632            // Do not copy. The copy will have its own native representation (for example - Rapier's collider)
633            native: Cell::new(ColliderHandle::invalid()),
634        }
635    }
636}
637
638impl TypeUuidProvider for Collider {
639    fn type_uuid() -> Uuid {
640        uuid!("bfaa2e82-9c19-4b99-983b-3bc115744a1d")
641    }
642}
643
644impl Collider {
645    /// Sets the new shape to the collider.
646    ///
647    /// # Performance
648    ///
649    /// This is relatively expensive operation - it forces the physics engine to recalculate contacts,
650    /// perform collision response, etc. Try avoid calling this method each frame for better
651    /// performance.
652    pub fn set_shape(&mut self, shape: ColliderShape) -> ColliderShape {
653        self.shape.set_value_and_mark_modified(shape)
654    }
655
656    /// Returns shared reference to the collider shape.
657    pub fn shape(&self) -> &ColliderShape {
658        &self.shape
659    }
660
661    /// Returns a copy of the collider shape.
662    pub fn shape_value(&self) -> ColliderShape {
663        (*self.shape).clone()
664    }
665
666    /// Returns mutable reference to the current collider shape.
667    ///
668    /// # Performance
669    ///
670    /// This is relatively expensive operation - it forces the physics engine to recalculate contacts,
671    /// perform collision response, etc. Try avoid calling this method each frame for better
672    /// performance.
673    pub fn shape_mut(&mut self) -> &mut ColliderShape {
674        self.shape.get_value_mut_and_mark_modified()
675    }
676
677    /// Sets the new restitution value. The exact meaning of possible values is somewhat complex,
678    /// check [Wikipedia page](https://en.wikipedia.org/wiki/Coefficient_of_restitution) for more
679    /// info.
680    ///
681    /// # Performance
682    ///
683    /// This is relatively expensive operation - it forces the physics engine to recalculate contacts,
684    /// perform collision response, etc. Try avoid calling this method each frame for better
685    /// performance.
686    pub fn set_restitution(&mut self, restitution: f32) -> f32 {
687        self.restitution.set_value_and_mark_modified(restitution)
688    }
689
690    /// Returns current restitution value of the collider.
691    pub fn restitution(&self) -> f32 {
692        *self.restitution
693    }
694
695    /// Sets the new density value of the collider. Density defines actual mass of the rigid body to
696    /// which the collider is attached. Final mass will be a sum of `ColliderVolume * ColliderDensity`
697    /// of each collider. In case if density is undefined, the mass of the collider will be zero,
698    /// which will lead to two possible effects:
699    ///
700    /// 1) If a rigid body to which collider is attached have no additional mass, then the rigid body
701    ///    won't rotate, only move.
702    /// 2) If the rigid body have some additional mass, then the rigid body will have normal behaviour.
703    ///
704    /// # Performance
705    ///
706    /// This is relatively expensive operation - it forces the physics engine to recalculate contacts,
707    /// perform collision response, etc. Try avoid calling this method each frame for better
708    /// performance.
709    pub fn set_density(&mut self, density: Option<f32>) -> Option<f32> {
710        self.density.set_value_and_mark_modified(density)
711    }
712
713    /// Returns current density of the collider.
714    pub fn density(&self) -> Option<f32> {
715        *self.density
716    }
717
718    /// Sets friction coefficient for the collider. The greater value is the more kinematic energy
719    /// will be converted to heat (in other words - lost), the parent rigid body will slowdown much
720    /// faster and so on.
721    ///
722    /// # Performance
723    ///
724    /// This is relatively expensive operation - it forces the physics engine to recalculate contacts,
725    /// perform collision response, etc. Try avoid calling this method each frame for better
726    /// performance.
727    pub fn set_friction(&mut self, friction: f32) -> f32 {
728        self.friction.set_value_and_mark_modified(friction)
729    }
730
731    /// Return current friction of the collider.
732    pub fn friction(&self) -> f32 {
733        *self.friction
734    }
735
736    /// Sets the new collision filtering options. See [`InteractionGroups`] docs for more info.
737    ///
738    /// # Performance
739    ///
740    /// This is relatively expensive operation - it forces the physics engine to recalculate contacts,
741    /// perform collision response, etc. Try avoid calling this method each frame for better
742    /// performance.
743    pub fn set_collision_groups(&mut self, groups: InteractionGroups) -> InteractionGroups {
744        self.collision_groups.set_value_and_mark_modified(groups)
745    }
746
747    /// Returns current collision filtering options.
748    pub fn collision_groups(&self) -> InteractionGroups {
749        *self.collision_groups
750    }
751
752    /// Sets the new joint solver filtering options. See [`InteractionGroups`] docs for more info.
753    ///
754    /// # Performance
755    ///
756    /// This is relatively expensive operation - it forces the physics engine to recalculate contacts,
757    /// perform collision response, etc. Try avoid calling this method each frame for better
758    /// performance.
759    pub fn set_solver_groups(&mut self, groups: InteractionGroups) -> InteractionGroups {
760        self.solver_groups.set_value_and_mark_modified(groups)
761    }
762
763    /// Returns current solver groups.
764    pub fn solver_groups(&self) -> InteractionGroups {
765        *self.solver_groups
766    }
767
768    /// If true is passed, the method makes collider a sensor. Sensors will not participate in
769    /// collision response, but it is still possible to query contact information from them.
770    ///
771    /// # Performance
772    ///
773    /// This is relatively expensive operation - it forces the physics engine to recalculate contacts,
774    /// perform collision response, etc. Try avoid calling this method each frame for better
775    /// performance.
776    pub fn set_is_sensor(&mut self, is_sensor: bool) -> bool {
777        self.is_sensor.set_value_and_mark_modified(is_sensor)
778    }
779
780    /// Returns true if the collider is sensor, false - otherwise.
781    pub fn is_sensor(&self) -> bool {
782        *self.is_sensor
783    }
784
785    /// Sets the new friction combine rule. See [`CoefficientCombineRule`] docs for more info.
786    ///
787    /// # Performance
788    ///
789    /// This is relatively expensive operation - it forces the physics engine to recalculate contacts,
790    /// perform collision response, etc. Try avoid calling this method each frame for better
791    /// performance.
792    pub fn set_friction_combine_rule(
793        &mut self,
794        rule: CoefficientCombineRule,
795    ) -> CoefficientCombineRule {
796        self.friction_combine_rule.set_value_and_mark_modified(rule)
797    }
798
799    /// Returns current friction combine rule of the collider.
800    pub fn friction_combine_rule(&self) -> CoefficientCombineRule {
801        *self.friction_combine_rule
802    }
803
804    /// Sets the new restitution combine rule. See [`CoefficientCombineRule`] docs for more info.
805    ///
806    /// # Performance
807    ///
808    /// This is relatively expensive operation - it forces the physics engine to recalculate contacts,
809    /// perform collision response, etc. Try avoid calling this method each frame for better
810    /// performance.
811    pub fn set_restitution_combine_rule(
812        &mut self,
813        rule: CoefficientCombineRule,
814    ) -> CoefficientCombineRule {
815        self.restitution_combine_rule
816            .set_value_and_mark_modified(rule)
817    }
818
819    /// Returns current restitution combine rule of the collider.
820    pub fn restitution_combine_rule(&self) -> CoefficientCombineRule {
821        *self.restitution_combine_rule
822    }
823
824    /// Returns an iterator that yields contact information for the collider.
825    /// Contacts checks between two regular colliders
826    pub fn contacts<'a>(
827        &self,
828        physics: &'a PhysicsWorld,
829    ) -> impl Iterator<Item = ContactPair> + 'a {
830        physics.contacts_with(self.native.get())
831    }
832
833    /// Returns an iterator that yields intersection information for the collider.
834    /// Intersections checks between regular colliders and sensor colliders
835    pub fn intersects<'a>(
836        &self,
837        physics: &'a PhysicsWorld,
838    ) -> impl Iterator<Item = IntersectionPair> + 'a {
839        physics.intersections_with(self.native.get())
840    }
841
842    pub(crate) fn needs_sync_model(&self) -> bool {
843        self.shape.need_sync()
844            || self.friction.need_sync()
845            || self.density.need_sync()
846            || self.restitution.need_sync()
847            || self.is_sensor.need_sync()
848            || self.collision_groups.need_sync()
849            || self.solver_groups.need_sync()
850            || self.friction_combine_rule.need_sync()
851            || self.restitution_combine_rule.need_sync()
852    }
853}
854
855impl ConstructorProvider<Node, Graph> for Collider {
856    fn constructor() -> NodeConstructor {
857        NodeConstructor::new::<Self>()
858            .with_variant("Collider", |_| {
859                ColliderBuilder::new(BaseBuilder::new().with_name("Collider"))
860                    .with_shape(ColliderShape::Cuboid(Default::default()))
861                    .build_node()
862                    .into()
863            })
864            .with_group("Physics")
865    }
866}
867
868impl NodeTrait for Collider {
869    fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
870        self.base.local_bounding_box()
871    }
872
873    fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
874        self.base.world_bounding_box()
875    }
876
877    fn id(&self) -> Uuid {
878        Self::type_uuid()
879    }
880
881    fn on_removed_from_graph(&mut self, graph: &mut Graph) {
882        graph.physics.remove_collider(self.native.get());
883        self.native.set(ColliderHandle::invalid());
884
885        Log::info(format!(
886            "Native collider was removed for node: {}",
887            self.name()
888        ));
889    }
890
891    fn on_unlink(&mut self, graph: &mut Graph) {
892        if graph.physics.remove_collider(self.native.get()) {
893            // Remove native collider when detaching a collider node from rigid body node.
894            self.native.set(ColliderHandle::invalid());
895        }
896    }
897
898    fn on_local_transform_changed(&self, context: &mut SyncContext) {
899        if self.native.get() != ColliderHandle::invalid() {
900            if let Some(native) = context.physics.colliders.get_mut(self.native.get()) {
901                native.set_position_wrt_parent(Isometry3 {
902                    rotation: **self.local_transform().rotation(),
903                    translation: Translation3 {
904                        vector: **self.local_transform().position(),
905                    },
906                });
907            }
908        }
909    }
910
911    fn sync_native(&self, self_handle: Handle<Node>, context: &mut SyncContext) {
912        context
913            .physics
914            .sync_to_collider_node(context.nodes, self_handle, self);
915    }
916
917    fn validate(&self, scene: &Scene) -> Result<(), String> {
918        let mut message = String::new();
919
920        if scene
921            .graph
922            .try_get(self.parent())
923            .and_then(|p| p.component_ref::<RigidBody>())
924            .is_none()
925        {
926            message += "3D Collider must be a direct child of a 3D Rigid Body node, \
927            otherwise it will not have any effect!";
928        }
929
930        match &*self.shape {
931            ColliderShape::Trimesh(trimesh) => {
932                for source in trimesh.sources.iter() {
933                    if !scene.graph.is_valid_handle(source.0) {
934                        message += &format!("Trimesh data source {} handle is invalid!", source.0);
935                    }
936                }
937            }
938            ColliderShape::Heightfield(heightfield) => {
939                if !scene.graph.is_valid_handle(heightfield.geometry_source.0) {
940                    message += &format!(
941                        "Heightfield data source {} handle is invalid!",
942                        heightfield.geometry_source.0
943                    );
944                }
945            }
946            ColliderShape::Polyhedron(polyhedron) => {
947                if !scene.graph.is_valid_handle(polyhedron.geometry_source.0) {
948                    message += &format!(
949                        "Polyhedron data source {} handle is invalid!",
950                        polyhedron.geometry_source.0
951                    );
952                }
953            }
954            _ => (),
955        }
956
957        if message.is_empty() {
958            Ok(())
959        } else {
960            Err(message)
961        }
962    }
963}
964
965/// Collider builder allows you to build a collider node in declarative mannner.
966pub struct ColliderBuilder {
967    base_builder: BaseBuilder,
968    shape: ColliderShape,
969    friction: f32,
970    density: Option<f32>,
971    restitution: f32,
972    is_sensor: bool,
973    collision_groups: InteractionGroups,
974    solver_groups: InteractionGroups,
975    friction_combine_rule: CoefficientCombineRule,
976    restitution_combine_rule: CoefficientCombineRule,
977}
978
979impl ColliderBuilder {
980    /// Creates new collider builder.
981    pub fn new(base_builder: BaseBuilder) -> Self {
982        Self {
983            base_builder,
984            shape: Default::default(),
985            friction: 0.0,
986            density: None,
987            restitution: 0.0,
988            is_sensor: false,
989            collision_groups: Default::default(),
990            solver_groups: Default::default(),
991            friction_combine_rule: Default::default(),
992            restitution_combine_rule: Default::default(),
993        }
994    }
995
996    /// Sets desired shape of the collider.
997    pub fn with_shape(mut self, shape: ColliderShape) -> Self {
998        self.shape = shape;
999        self
1000    }
1001
1002    /// Sets desired density value.
1003    pub fn with_density(mut self, density: Option<f32>) -> Self {
1004        self.density = density;
1005        self
1006    }
1007
1008    /// Sets desired restitution value.
1009    pub fn with_restitution(mut self, restitution: f32) -> Self {
1010        self.restitution = restitution;
1011        self
1012    }
1013
1014    /// Sets desired friction value.    
1015    pub fn with_friction(mut self, friction: f32) -> Self {
1016        self.friction = friction;
1017        self
1018    }
1019
1020    /// Sets whether this collider will be used a sensor or not.
1021    pub fn with_sensor(mut self, sensor: bool) -> Self {
1022        self.is_sensor = sensor;
1023        self
1024    }
1025
1026    /// Sets desired solver groups.    
1027    pub fn with_solver_groups(mut self, solver_groups: InteractionGroups) -> Self {
1028        self.solver_groups = solver_groups;
1029        self
1030    }
1031
1032    /// Sets desired collision groups.
1033    pub fn with_collision_groups(mut self, collision_groups: InteractionGroups) -> Self {
1034        self.collision_groups = collision_groups;
1035        self
1036    }
1037
1038    /// Sets desired friction combine rule.
1039    pub fn with_friction_combine_rule(mut self, rule: CoefficientCombineRule) -> Self {
1040        self.friction_combine_rule = rule;
1041        self
1042    }
1043
1044    /// Sets desired restitution combine rule.
1045    pub fn with_restitution_combine_rule(mut self, rule: CoefficientCombineRule) -> Self {
1046        self.restitution_combine_rule = rule;
1047        self
1048    }
1049
1050    /// Creates collider node, but does not add it to a graph.
1051    pub fn build_collider(self) -> Collider {
1052        Collider {
1053            base: self.base_builder.build_base(),
1054            shape: self.shape.into(),
1055            friction: self.friction.into(),
1056            density: self.density.into(),
1057            restitution: self.restitution.into(),
1058            is_sensor: self.is_sensor.into(),
1059            collision_groups: self.collision_groups.into(),
1060            solver_groups: self.solver_groups.into(),
1061            friction_combine_rule: self.friction_combine_rule.into(),
1062            restitution_combine_rule: self.restitution_combine_rule.into(),
1063            native: Cell::new(ColliderHandle::invalid()),
1064        }
1065    }
1066
1067    /// Creates collider node, but does not add it to a graph.
1068    pub fn build_node(self) -> Node {
1069        Node::new(self.build_collider())
1070    }
1071
1072    /// Creates collider node and adds it to the graph.
1073    pub fn build(self, graph: &mut Graph) -> Handle<Node> {
1074        graph.add_node(self.build_node())
1075    }
1076}
1077
1078#[cfg(test)]
1079mod test {
1080    use crate::core::algebra::Vector2;
1081    use crate::scene::{
1082        base::BaseBuilder,
1083        collider::{ColliderBuilder, ColliderShape},
1084        graph::Graph,
1085        rigidbody::{RigidBodyBuilder, RigidBodyType},
1086    };
1087
1088    #[test]
1089    fn test_collider_intersect() {
1090        let mut graph = Graph::new();
1091
1092        let mut create_rigid_body = |is_sensor| {
1093            let cube_half_size = 0.5;
1094            let collider_sensor = ColliderBuilder::new(BaseBuilder::new())
1095                .with_shape(ColliderShape::cuboid(
1096                    cube_half_size,
1097                    cube_half_size,
1098                    cube_half_size,
1099                ))
1100                .with_sensor(is_sensor)
1101                .build(&mut graph);
1102
1103            RigidBodyBuilder::new(BaseBuilder::new().with_children(&[collider_sensor]))
1104                .with_body_type(RigidBodyType::Static)
1105                .build(&mut graph);
1106
1107            collider_sensor
1108        };
1109
1110        let collider_sensor = create_rigid_body(true);
1111        let collider_non_sensor = create_rigid_body(false);
1112
1113        // need to call two times for the physics engine to execute
1114        graph.update(Vector2::new(800.0, 600.0), 1.0, Default::default());
1115        graph.update(Vector2::new(800.0, 600.0), 1.0, Default::default());
1116
1117        // we don't expect contact between regular body and sensor
1118        assert_eq!(
1119            0,
1120            graph[collider_sensor]
1121                .as_collider()
1122                .contacts(&graph.physics)
1123                .count()
1124        );
1125        assert_eq!(
1126            0,
1127            graph[collider_non_sensor]
1128                .as_collider()
1129                .contacts(&graph.physics)
1130                .count()
1131        );
1132
1133        // we expect intersection between regular body and sensor
1134        assert_eq!(
1135            1,
1136            graph[collider_sensor]
1137                .as_collider()
1138                .intersects(&graph.physics)
1139                .count()
1140        );
1141        assert_eq!(
1142            1,
1143            graph[collider_non_sensor]
1144                .as_collider()
1145                .intersects(&graph.physics)
1146                .count()
1147        );
1148    }
1149}