fyrox_impl/scene/dim2/
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::{Isometry2, Translation2, UnitComplex, Vector2},
28        log::Log,
29        math::aabb::AxisAlignedBoundingBox,
30        pool::Handle,
31        reflect::prelude::*,
32        type_traits::prelude::*,
33        uuid::{uuid, Uuid},
34        uuid_provider,
35        variable::InheritableVariable,
36        visitor::prelude::*,
37        ImmutableString, TypeUuidProvider,
38    },
39    graph::{BaseSceneGraph, SceneGraphNode},
40    scene::{
41        base::{Base, BaseBuilder},
42        collider::InteractionGroups,
43        dim2::{
44            physics::{ContactPair, IntersectionPair, PhysicsWorld},
45            rigidbody::RigidBody,
46        },
47        graph::{physics::CoefficientCombineRule, Graph},
48        node::{Node, NodeTrait, SyncContext},
49        Scene,
50    },
51};
52
53use fyrox_graph::constructor::ConstructorProvider;
54use rapier2d::geometry::ColliderHandle;
55use std::{
56    cell::Cell,
57    ops::{Deref, DerefMut},
58};
59use strum_macros::{AsRefStr, EnumString, VariantNames};
60
61/// Ball is an idea sphere shape defined by a single parameters - its radius.
62#[derive(Clone, Debug, Visit, PartialEq, Reflect)]
63pub struct BallShape {
64    /// Radius of the sphere.
65    #[reflect(min_value = 0.001, step = 0.05)]
66    pub radius: f32,
67}
68
69impl Default for BallShape {
70    fn default() -> Self {
71        Self { radius: 0.5 }
72    }
73}
74
75/// Cuboid shape (rectangle).
76#[derive(Clone, Debug, Visit, PartialEq, Reflect)]
77pub struct CuboidShape {
78    /// Half extents of the box. X - half width, Y - half height.
79    /// Actual _size_ will be 2 times bigger.
80    #[reflect(min_value = 0.001, step = 0.05)]
81    pub half_extents: Vector2<f32>,
82}
83
84impl Default for CuboidShape {
85    fn default() -> Self {
86        Self {
87            half_extents: Vector2::new(0.5, 0.5),
88        }
89    }
90}
91
92/// Arbitrary capsule shape defined by 2 points (which forms axis) and a radius.
93#[derive(Clone, Debug, Visit, PartialEq, Reflect)]
94pub struct CapsuleShape {
95    /// Begin point of the capsule.
96    pub begin: Vector2<f32>,
97    /// End point of the capsule.
98    pub end: Vector2<f32>,
99    /// Radius of the capsule.
100    #[reflect(min_value = 0.001, step = 0.05)]
101    pub radius: f32,
102}
103
104impl Default for CapsuleShape {
105    // Y-capsule
106    fn default() -> Self {
107        Self {
108            begin: Default::default(),
109            end: Vector2::new(0.0, 1.0),
110            radius: 0.5,
111        }
112    }
113}
114
115/// Arbitrary segment shape defined by two points.
116#[derive(Clone, Debug, Visit, PartialEq, Reflect)]
117pub struct SegmentShape {
118    /// Begin point of the capsule.
119    pub begin: Vector2<f32>,
120    /// End point of the capsule.
121    pub end: Vector2<f32>,
122}
123
124impl Default for SegmentShape {
125    fn default() -> Self {
126        Self {
127            begin: Default::default(),
128            end: Vector2::new(0.0, 1.0),
129        }
130    }
131}
132
133/// Arbitrary triangle shape.
134#[derive(Clone, Debug, Visit, PartialEq, Reflect)]
135pub struct TriangleShape {
136    /// First point of the triangle shape.
137    pub a: Vector2<f32>,
138    /// Second point of the triangle shape.
139    pub b: Vector2<f32>,
140    /// Third point of the triangle shape.
141    pub c: Vector2<f32>,
142}
143
144impl Default for TriangleShape {
145    fn default() -> Self {
146        Self {
147            a: Default::default(),
148            b: Vector2::new(1.0, 0.0),
149            c: Vector2::new(0.0, 1.0),
150        }
151    }
152}
153
154/// Geometry source for colliders with complex geometry.
155///
156/// # Notes
157///
158/// Currently there is only one way to set geometry - using a scene node as a source of data.
159#[derive(Default, Clone, Copy, PartialEq, Hash, Debug, Visit, Reflect, Eq, TypeUuidProvider)]
160#[type_uuid(id = "1d451699-d76e-4774-87ea-dd3e2751cb39")]
161pub struct GeometrySource(pub Handle<Node>);
162
163/// Arbitrary triangle mesh shape.
164#[derive(Default, Clone, Debug, PartialEq, Visit, Reflect, Eq)]
165pub struct TrimeshShape {
166    /// Geometry sources for the shape.
167    pub sources: Vec<GeometrySource>,
168}
169
170/// Arbitrary height field shape.
171#[derive(Default, Clone, Debug, PartialEq, Visit, Reflect, Eq)]
172pub struct HeightfieldShape {
173    /// A handle to terrain scene node.
174    pub geometry_source: GeometrySource,
175}
176
177/// Arbitrary tile map shape.
178#[derive(Default, Clone, Debug, PartialEq, Visit, Reflect, Eq)]
179pub struct TileMapShape {
180    /// A handle to tile map scene node.
181    pub tile_map: GeometrySource,
182    /// Name of a collider layer in the tile map's tile set.
183    pub layer_name: ImmutableString,
184}
185
186/// Possible collider shapes.
187#[derive(Clone, Debug, Visit, Reflect, AsRefStr, PartialEq, EnumString, VariantNames)]
188pub enum ColliderShape {
189    /// See [`BallShape`] docs.
190    Ball(BallShape),
191    /// See [`CuboidShape`] docs.
192    Cuboid(CuboidShape),
193    /// See [`CapsuleShape`] docs.
194    Capsule(CapsuleShape),
195    /// See [`SegmentShape`] docs.
196    Segment(SegmentShape),
197    /// See [`TriangleShape`] docs.
198    Triangle(TriangleShape),
199    /// See [`TrimeshShape`] docs.
200    Trimesh(TrimeshShape),
201    /// See [`HeightfieldShape`] docs.
202    Heightfield(HeightfieldShape),
203    /// See [`TileMapShape`] docs.
204    TileMap(TileMapShape),
205}
206
207uuid_provider!(ColliderShape = "4615485f-f8db-4405-b4a5-437e74b3f5b8");
208
209impl Default for ColliderShape {
210    fn default() -> Self {
211        Self::Ball(Default::default())
212    }
213}
214
215impl ColliderShape {
216    /// Initializes a ball shape defined by its radius.
217    pub fn ball(radius: f32) -> Self {
218        Self::Ball(BallShape { radius })
219    }
220
221    /// Initializes a cuboid shape defined by its half-extents.
222    pub fn cuboid(hx: f32, hy: f32) -> Self {
223        Self::Cuboid(CuboidShape {
224            half_extents: Vector2::new(hx, hy),
225        })
226    }
227
228    /// Initializes a capsule shape from its endpoints and radius.
229    pub fn capsule(begin: Vector2<f32>, end: Vector2<f32>, radius: f32) -> Self {
230        Self::Capsule(CapsuleShape { begin, end, radius })
231    }
232
233    /// Initializes a new collider builder with a capsule shape aligned with the `x` axis.
234    pub fn capsule_x(half_height: f32, radius: f32) -> Self {
235        let p = Vector2::x() * half_height;
236        Self::capsule(-p, p, radius)
237    }
238
239    /// Initializes a new collider builder with a capsule shape aligned with the `y` axis.
240    pub fn capsule_y(half_height: f32, radius: f32) -> Self {
241        let p = Vector2::y() * half_height;
242        Self::capsule(-p, p, radius)
243    }
244
245    /// Initializes a segment shape from its endpoints.
246    pub fn segment(begin: Vector2<f32>, end: Vector2<f32>) -> Self {
247        Self::Segment(SegmentShape { begin, end })
248    }
249
250    /// Initializes a triangle shape.
251    pub fn triangle(a: Vector2<f32>, b: Vector2<f32>, c: Vector2<f32>) -> Self {
252        Self::Triangle(TriangleShape { a, b, c })
253    }
254
255    /// Initializes a triangle mesh shape defined by a set of handles to mesh nodes that will be
256    /// used to create physical shape.
257    pub fn trimesh(geometry_sources: Vec<GeometrySource>) -> Self {
258        Self::Trimesh(TrimeshShape {
259            sources: geometry_sources,
260        })
261    }
262
263    /// Initializes a heightfield shape defined by a handle to terrain node.
264    pub fn heightfield(geometry_source: GeometrySource) -> Self {
265        Self::Heightfield(HeightfieldShape { geometry_source })
266    }
267}
268
269/// Collider is a geometric entity that can be attached to a rigid body to allow participate it
270/// participate in contact generation, collision response and proximity queries.
271#[derive(Reflect, Visit, Debug, ComponentProvider)]
272#[reflect(derived_type = "Node")]
273pub struct Collider {
274    base: Base,
275
276    #[reflect(setter = "set_shape")]
277    pub(crate) shape: InheritableVariable<ColliderShape>,
278
279    #[reflect(min_value = 0.0, step = 0.05, setter = "set_friction")]
280    pub(crate) friction: InheritableVariable<f32>,
281
282    #[reflect(setter = "set_density")]
283    pub(crate) density: InheritableVariable<Option<f32>>,
284
285    #[reflect(min_value = 0.0, step = 0.05, setter = "set_restitution")]
286    pub(crate) restitution: InheritableVariable<f32>,
287
288    #[reflect(setter = "set_is_sensor")]
289    pub(crate) is_sensor: InheritableVariable<bool>,
290
291    #[reflect(setter = "set_collision_groups")]
292    pub(crate) collision_groups: InheritableVariable<InteractionGroups>,
293
294    #[reflect(setter = "set_solver_groups")]
295    pub(crate) solver_groups: InheritableVariable<InteractionGroups>,
296
297    #[reflect(setter = "set_friction_combine_rule")]
298    pub(crate) friction_combine_rule: InheritableVariable<CoefficientCombineRule>,
299
300    #[reflect(setter = "set_restitution_combine_rule")]
301    pub(crate) restitution_combine_rule: InheritableVariable<CoefficientCombineRule>,
302
303    #[visit(skip)]
304    #[reflect(hidden)]
305    pub(crate) native: Cell<ColliderHandle>,
306}
307
308impl Default for Collider {
309    fn default() -> Self {
310        Self {
311            base: Default::default(),
312            shape: Default::default(),
313            friction: Default::default(),
314            density: Default::default(),
315            restitution: Default::default(),
316            is_sensor: Default::default(),
317            collision_groups: Default::default(),
318            solver_groups: Default::default(),
319            friction_combine_rule: Default::default(),
320            restitution_combine_rule: Default::default(),
321            native: Cell::new(ColliderHandle::invalid()),
322        }
323    }
324}
325
326impl Deref for Collider {
327    type Target = Base;
328
329    fn deref(&self) -> &Self::Target {
330        &self.base
331    }
332}
333
334impl DerefMut for Collider {
335    fn deref_mut(&mut self) -> &mut Self::Target {
336        &mut self.base
337    }
338}
339
340impl Clone for Collider {
341    fn clone(&self) -> Self {
342        Self {
343            base: self.base.clone(),
344            shape: self.shape.clone(),
345            friction: self.friction.clone(),
346            density: self.density.clone(),
347            restitution: self.restitution.clone(),
348            is_sensor: self.is_sensor.clone(),
349            collision_groups: self.collision_groups.clone(),
350            solver_groups: self.solver_groups.clone(),
351            friction_combine_rule: self.friction_combine_rule.clone(),
352            restitution_combine_rule: self.restitution_combine_rule.clone(),
353            // Do not copy. The copy will have its own native representation.
354            native: Cell::new(ColliderHandle::invalid()),
355        }
356    }
357}
358
359impl TypeUuidProvider for Collider {
360    fn type_uuid() -> Uuid {
361        uuid!("2b1659ea-a116-4224-bcd4-7931e3ae3b40")
362    }
363}
364
365impl Collider {
366    /// Sets the new shape to the collider.
367    ///
368    /// # Performance
369    ///
370    /// This is relatively expensive operation - it forces the physics engine to recalculate contacts,
371    /// perform collision response, etc. Try avoid calling this method each frame for better
372    /// performance.
373    pub fn set_shape(&mut self, shape: ColliderShape) -> ColliderShape {
374        self.shape.set_value_and_mark_modified(shape)
375    }
376
377    /// Returns shared reference to the collider shape.
378    pub fn shape(&self) -> &ColliderShape {
379        &self.shape
380    }
381
382    /// Returns a copy of the collider shape.
383    pub fn shape_value(&self) -> ColliderShape {
384        (*self.shape).clone()
385    }
386
387    /// Returns mutable reference to the current collider shape.
388    ///
389    /// # Performance
390    ///
391    /// This is relatively expensive operation - it forces the physics engine to recalculate contacts,
392    /// perform collision response, etc. Try avoid calling this method each frame for better
393    /// performance.
394    pub fn shape_mut(&mut self) -> &mut ColliderShape {
395        self.shape.get_value_mut_and_mark_modified()
396    }
397
398    /// Sets the new restitution value. The exact meaning of possible values is somewhat complex,
399    /// check [Wikipedia page](https://en.wikipedia.org/wiki/Coefficient_of_restitution) for more
400    /// info.
401    ///
402    /// # Performance
403    ///
404    /// This is relatively expensive operation - it forces the physics engine to recalculate contacts,
405    /// perform collision response, etc. Try avoid calling this method each frame for better
406    /// performance.
407    pub fn set_restitution(&mut self, restitution: f32) -> f32 {
408        self.restitution.set_value_and_mark_modified(restitution)
409    }
410
411    /// Returns current restitution value of the collider.
412    pub fn restitution(&self) -> f32 {
413        *self.restitution
414    }
415
416    /// Sets the new density value of the collider. Density defines actual mass of the rigid body to
417    /// which the collider is attached. Final mass will be a sum of `ColliderVolume * ColliderDensity`
418    /// of each collider. In case if density is undefined, the mass of the collider will be zero,
419    /// which will lead to two possible effects:
420    ///
421    /// 1) If a rigid body to which collider is attached have no additional mass, then the rigid body
422    ///    won't rotate, only move.
423    /// 2) If the rigid body have some additional mass, then the rigid body will have normal behaviour.
424    ///
425    /// # Performance
426    ///
427    /// This is relatively expensive operation - it forces the physics engine to recalculate contacts,
428    /// perform collision response, etc. Try avoid calling this method each frame for better
429    /// performance.
430    pub fn set_density(&mut self, density: Option<f32>) -> Option<f32> {
431        self.density.set_value_and_mark_modified(density)
432    }
433
434    /// Returns current density of the collider.
435    pub fn density(&self) -> Option<f32> {
436        *self.density
437    }
438
439    /// Sets friction coefficient for the collider. The greater value is the more kinematic energy
440    /// will be converted to heat (in other words - lost), the parent rigid body will slowdown much
441    /// faster and so on.
442    ///
443    /// # Performance
444    ///
445    /// This is relatively expensive operation - it forces the physics engine to recalculate contacts,
446    /// perform collision response, etc. Try avoid calling this method each frame for better
447    /// performance.
448    pub fn set_friction(&mut self, friction: f32) -> f32 {
449        self.friction.set_value_and_mark_modified(friction)
450    }
451
452    /// Return current friction of the collider.
453    pub fn friction(&self) -> f32 {
454        *self.friction
455    }
456
457    /// Sets the new collision filtering options. See [`InteractionGroups`] docs for more info.
458    ///
459    /// # Performance
460    ///
461    /// This is relatively expensive operation - it forces the physics engine to recalculate contacts,
462    /// perform collision response, etc. Try avoid calling this method each frame for better
463    /// performance.
464    pub fn set_collision_groups(&mut self, groups: InteractionGroups) -> InteractionGroups {
465        self.collision_groups.set_value_and_mark_modified(groups)
466    }
467
468    /// Returns current collision filtering options.
469    pub fn collision_groups(&self) -> InteractionGroups {
470        *self.collision_groups
471    }
472
473    /// Sets the new joint solver filtering options. See [`InteractionGroups`] docs for more info.
474    ///
475    /// # Performance
476    ///
477    /// This is relatively expensive operation - it forces the physics engine to recalculate contacts,
478    /// perform collision response, etc. Try avoid calling this method each frame for better
479    /// performance.
480    pub fn set_solver_groups(&mut self, groups: InteractionGroups) -> InteractionGroups {
481        self.solver_groups.set_value_and_mark_modified(groups)
482    }
483
484    /// Returns current solver groups.
485    pub fn solver_groups(&self) -> InteractionGroups {
486        *self.solver_groups
487    }
488
489    /// If true is passed, the method makes collider a sensor. Sensors will not participate in
490    /// collision response, but it is still possible to query contact information from them.
491    ///
492    /// # Performance
493    ///
494    /// This is relatively expensive operation - it forces the physics engine to recalculate contacts,
495    /// perform collision response, etc. Try avoid calling this method each frame for better
496    /// performance.
497    pub fn set_is_sensor(&mut self, is_sensor: bool) -> bool {
498        self.is_sensor.set_value_and_mark_modified(is_sensor)
499    }
500
501    /// Returns true if the collider is sensor, false - otherwise.
502    pub fn is_sensor(&self) -> bool {
503        *self.is_sensor
504    }
505
506    /// Sets the new friction combine rule. See [`CoefficientCombineRule`] docs for more info.
507    ///
508    /// # Performance
509    ///
510    /// This is relatively expensive operation - it forces the physics engine to recalculate contacts,
511    /// perform collision response, etc. Try avoid calling this method each frame for better
512    /// performance.
513    pub fn set_friction_combine_rule(
514        &mut self,
515        rule: CoefficientCombineRule,
516    ) -> CoefficientCombineRule {
517        self.friction_combine_rule.set_value_and_mark_modified(rule)
518    }
519
520    /// Returns current friction combine rule of the collider.
521    pub fn friction_combine_rule(&self) -> CoefficientCombineRule {
522        *self.friction_combine_rule
523    }
524
525    /// Sets the new restitution combine rule. See [`CoefficientCombineRule`] docs for more info.
526    ///
527    /// # Performance
528    ///
529    /// This is relatively expensive operation - it forces the physics engine to recalculate contacts,
530    /// perform collision response, etc. Try avoid calling this method each frame for better
531    /// performance.
532    pub fn set_restitution_combine_rule(
533        &mut self,
534        rule: CoefficientCombineRule,
535    ) -> CoefficientCombineRule {
536        self.restitution_combine_rule
537            .set_value_and_mark_modified(rule)
538    }
539
540    /// Returns current restitution combine rule of the collider.
541    pub fn restitution_combine_rule(&self) -> CoefficientCombineRule {
542        *self.restitution_combine_rule
543    }
544
545    /// Returns an iterator that yields contact information for the collider.
546    /// Contacts checks between two non-sensor colliders.
547    /// This includes only cases where two colliders are pressing against each other,
548    /// and only if [`ContactPair::has_any_active_contact`] is true.
549    /// When `has_any_active_contact` is false, the colliders may merely have overlapping
550    /// bounding boxes. See [`Collider::active_contacts`] for an interator that yields
551    /// only pairs where `has_any_active_contact` is true.
552    ///
553    /// When a collider is passing through a sensor collider, that goes into the
554    /// [`Collider::intersects`] list. Those intersections will not be produced by
555    /// this iterator.
556    ///
557    /// Each pair produced by this iterator includes the handles of two colliders,
558    /// this collider and some other collider. There is no guarantee about whether
559    /// this collider will be the first member of the pair or the second,
560    /// but [`ContactPair::other`] can be used to get the handle of the other collider
561    /// given the handle of this collider.
562    pub fn contacts<'a>(
563        &self,
564        physics: &'a PhysicsWorld,
565    ) -> impl Iterator<Item = ContactPair> + 'a {
566        physics.contacts_with(self.native.get())
567    }
568
569    /// Returns an iterator that yields contact information for the collider.
570    /// Contacts checks between two non-sensor colliders that are actually touching.
571    /// This includes only cases where two colliders are pressing against each other.
572    /// When a collider is passing through a sensor collider, that goes into the
573    /// [`Collider::intersects`] list.
574    ///
575    /// [`ContactPair::has_any_active_contact`] is guaranteed to be true for every pair
576    /// produced by this iterator.
577    ///
578    /// Each pair produced by this iterator includes the handles of two colliders,
579    /// this collider and some other collider. There is no guarantee about whether
580    /// this collider will be the first member of the pair or the second,
581    /// but [`ContactPair::other`] can be used to get the handle of the other collider
582    /// given the handle of this collider.
583    pub fn active_contacts<'a>(
584        &self,
585        physics: &'a PhysicsWorld,
586    ) -> impl Iterator<Item = ContactPair> + 'a {
587        self.contacts(physics)
588            .filter(|pair| pair.has_any_active_contact)
589    }
590
591    /// Returns an iterator that yields intersection information for the collider.
592    /// Intersections checks for colliders passing through each other due to at least
593    /// one of the colliders being a sensor.
594    /// If [`IntersectionPair::has_any_active_contact`] is true, that means the colliders are actually touching.
595    /// When `has_any_active_contact` is false, the colliders may merely have overlapping
596    /// bounding boxes. See [`Collider::active_intersects`] for an interator that yields
597    /// only colliders that actually overlap this collider.
598    ///
599    /// Each pair produced by this iterator includes the handles of two colliders,
600    /// this collider and some other collider. There is no guarantee about whether
601    /// this collider will be the first member of the pair or the second,
602    /// but [`IntersectionPair::other`] can be used to get the handle of the other collider
603    /// given the handle of this collider.
604    pub fn intersects<'a>(
605        &self,
606        physics: &'a PhysicsWorld,
607    ) -> impl Iterator<Item = IntersectionPair> + 'a {
608        physics.intersections_with(self.native.get())
609    }
610
611    /// Returns an iterator that yields intersection information for the collider.
612    /// Intersections checks for colliders passing through each other due to at least
613    /// one of the colliders being a sensor.
614    pub fn active_intersects<'a>(
615        &self,
616        physics: &'a PhysicsWorld,
617    ) -> impl Iterator<Item = Handle<Node>> + 'a {
618        let self_handle = self.handle();
619        self.intersects(physics)
620            .filter(|pair| pair.has_any_active_contact)
621            .map(move |pair| pair.other(self_handle))
622    }
623
624    pub(crate) fn needs_sync_model(&self) -> bool {
625        self.shape.need_sync()
626            || self.friction.need_sync()
627            || self.density.need_sync()
628            || self.restitution.need_sync()
629            || self.is_sensor.need_sync()
630            || self.collision_groups.need_sync()
631            || self.solver_groups.need_sync()
632            || self.friction_combine_rule.need_sync()
633            || self.restitution_combine_rule.need_sync()
634    }
635}
636
637impl ConstructorProvider<Node, Graph> for Collider {
638    fn constructor() -> NodeConstructor {
639        NodeConstructor::new::<Self>()
640            .with_variant("Collider", |_| {
641                ColliderBuilder::new(BaseBuilder::new().with_name("Collider 2D"))
642                    .with_shape(ColliderShape::Cuboid(Default::default()))
643                    .build_node()
644                    .into()
645            })
646            .with_group("Physics 2D")
647    }
648}
649
650impl NodeTrait for Collider {
651    fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
652        self.base.local_bounding_box()
653    }
654
655    fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
656        self.base.world_bounding_box()
657    }
658
659    fn id(&self) -> Uuid {
660        Self::type_uuid()
661    }
662
663    fn on_removed_from_graph(&mut self, graph: &mut Graph) {
664        graph.physics2d.remove_collider(self.native.get());
665        self.native.set(ColliderHandle::invalid());
666
667        Log::info(format!(
668            "Native collider 2D was removed for node: {}",
669            self.name()
670        ));
671    }
672
673    fn on_unlink(&mut self, graph: &mut Graph) {
674        if graph.physics2d.remove_collider(self.native.get()) {
675            // Remove native collider when detaching a collider node from rigid body node.
676            self.native.set(ColliderHandle::invalid());
677        }
678    }
679
680    fn on_local_transform_changed(&self, context: &mut SyncContext) {
681        if self.native.get() != ColliderHandle::invalid() {
682            if let Some(native) = context.physics2d.colliders.get_mut(self.native.get()) {
683                native.set_position_wrt_parent(Isometry2 {
684                    rotation: UnitComplex::from_angle(
685                        self.local_transform().rotation().euler_angles().2,
686                    ),
687                    translation: Translation2 {
688                        vector: self.local_transform().position().xy(),
689                    },
690                });
691            }
692        }
693    }
694
695    fn sync_native(&self, self_handle: Handle<Node>, context: &mut SyncContext) {
696        context
697            .physics2d
698            .sync_to_collider_node(context.nodes, self_handle, self);
699    }
700
701    fn validate(&self, scene: &Scene) -> Result<(), String> {
702        let mut message = String::new();
703
704        if scene
705            .graph
706            .try_get_node(self.parent())
707            .and_then(|p| p.component_ref::<RigidBody>())
708            .is_none()
709        {
710            message += "2D Collider must be a direct child of a 3D Rigid Body node, \
711            otherwise it will not have any effect!";
712        }
713
714        match &*self.shape {
715            ColliderShape::Trimesh(trimesh) => {
716                for source in trimesh.sources.iter() {
717                    if !scene.graph.is_valid_handle(source.0) {
718                        message += &format!("Trimesh shape data handle {} is invalid!", source.0);
719                    }
720                }
721            }
722            ColliderShape::Heightfield(heightfield) => {
723                if !scene.graph.is_valid_handle(heightfield.geometry_source.0) {
724                    message += &format!(
725                        "Heightfield shape data handle {} is invalid!",
726                        heightfield.geometry_source.0
727                    );
728                }
729            }
730            ColliderShape::TileMap(tile_map) => {
731                if !scene.graph.is_valid_handle(tile_map.tile_map.0) {
732                    message += &format!(
733                        "Tile map shape data handle {} is invalid!",
734                        tile_map.tile_map.0
735                    );
736                }
737            }
738            _ => (),
739        }
740
741        if message.is_empty() {
742            Ok(())
743        } else {
744            Err(message)
745        }
746    }
747}
748
749/// Collider builder allows you to build a collider node in declarative manner.
750pub struct ColliderBuilder {
751    base_builder: BaseBuilder,
752    shape: ColliderShape,
753    friction: f32,
754    density: Option<f32>,
755    restitution: f32,
756    is_sensor: bool,
757    collision_groups: InteractionGroups,
758    solver_groups: InteractionGroups,
759    friction_combine_rule: CoefficientCombineRule,
760    restitution_combine_rule: CoefficientCombineRule,
761}
762
763impl ColliderBuilder {
764    /// Creates new collider builder.
765    pub fn new(base_builder: BaseBuilder) -> Self {
766        Self {
767            base_builder,
768            shape: Default::default(),
769            friction: 0.0,
770            density: None,
771            restitution: 0.0,
772            is_sensor: false,
773            collision_groups: Default::default(),
774            solver_groups: Default::default(),
775            friction_combine_rule: Default::default(),
776            restitution_combine_rule: Default::default(),
777        }
778    }
779
780    /// Sets desired shape of the collider.
781    pub fn with_shape(mut self, shape: ColliderShape) -> Self {
782        self.shape = shape;
783        self
784    }
785
786    /// Sets desired density value.
787    pub fn with_density(mut self, density: Option<f32>) -> Self {
788        self.density = density;
789        self
790    }
791
792    /// Sets desired restitution value.
793    pub fn with_restitution(mut self, restitution: f32) -> Self {
794        self.restitution = restitution;
795        self
796    }
797
798    /// Sets desired friction value.
799    pub fn with_friction(mut self, friction: f32) -> Self {
800        self.friction = friction;
801        self
802    }
803
804    /// Sets whether this collider will be used a sensor or not.
805    pub fn with_sensor(mut self, sensor: bool) -> Self {
806        self.is_sensor = sensor;
807        self
808    }
809
810    /// Sets desired solver groups.
811    pub fn with_solver_groups(mut self, solver_groups: InteractionGroups) -> Self {
812        self.solver_groups = solver_groups;
813        self
814    }
815
816    /// Sets desired collision groups.
817    pub fn with_collision_groups(mut self, collision_groups: InteractionGroups) -> Self {
818        self.collision_groups = collision_groups;
819        self
820    }
821
822    /// Sets desired friction combine rule.
823    pub fn with_friction_combine_rule(mut self, rule: CoefficientCombineRule) -> Self {
824        self.friction_combine_rule = rule;
825        self
826    }
827
828    /// Sets desired restitution combine rule.
829    pub fn with_restitution_combine_rule(mut self, rule: CoefficientCombineRule) -> Self {
830        self.restitution_combine_rule = rule;
831        self
832    }
833
834    /// Creates collider node, but does not add it to a graph.
835    pub fn build_collider(self) -> Collider {
836        Collider {
837            base: self.base_builder.build_base(),
838            shape: self.shape.into(),
839            friction: self.friction.into(),
840            density: self.density.into(),
841            restitution: self.restitution.into(),
842            is_sensor: self.is_sensor.into(),
843            collision_groups: self.collision_groups.into(),
844            solver_groups: self.solver_groups.into(),
845            friction_combine_rule: self.friction_combine_rule.into(),
846            restitution_combine_rule: self.restitution_combine_rule.into(),
847            native: Cell::new(ColliderHandle::invalid()),
848        }
849    }
850
851    /// Creates collider node, but does not add it to a graph.
852    pub fn build_node(self) -> Node {
853        Node::new(self.build_collider())
854    }
855
856    /// Creates collider node and adds it to the graph.
857    pub fn build(self, graph: &mut Graph) -> Handle<Node> {
858        graph.add_node(self.build_node())
859    }
860}
861
862#[cfg(test)]
863mod test {
864
865    use crate::core::algebra::Vector2;
866    use crate::scene::{
867        base::BaseBuilder,
868        dim2::{
869            collider::{ColliderBuilder, ColliderShape},
870            rigidbody::RigidBodyBuilder,
871        },
872        graph::Graph,
873        rigidbody::RigidBodyType,
874    };
875
876    #[test]
877    fn test_collider_2d_intersect() {
878        let mut graph = Graph::new();
879
880        let mut create_rigid_body = |is_sensor| {
881            let cube_half_size = 0.5;
882            let collider_sensor = ColliderBuilder::new(BaseBuilder::new())
883                .with_shape(ColliderShape::cuboid(cube_half_size, cube_half_size))
884                .with_sensor(is_sensor)
885                .build(&mut graph);
886
887            RigidBodyBuilder::new(BaseBuilder::new().with_children(&[collider_sensor]))
888                .with_body_type(RigidBodyType::Static)
889                .build(&mut graph);
890
891            collider_sensor
892        };
893
894        let collider_sensor = create_rigid_body(true);
895        let collider_non_sensor = create_rigid_body(false);
896
897        // need to call two times for the physics engine to execute
898        graph.update(Vector2::new(800.0, 600.0), 1.0, Default::default());
899        graph.update(Vector2::new(800.0, 600.0), 1.0, Default::default());
900
901        // we don't expect contact between regular body and sensor
902        assert_eq!(
903            0,
904            graph[collider_sensor]
905                .as_collider2d()
906                .contacts(&graph.physics2d)
907                .count()
908        );
909        assert_eq!(
910            0,
911            graph[collider_non_sensor]
912                .as_collider2d()
913                .contacts(&graph.physics2d)
914                .count()
915        );
916
917        // we expect intersection between regular body and sensor
918        assert_eq!(
919            1,
920            graph[collider_sensor]
921                .as_collider2d()
922                .intersects(&graph.physics2d)
923                .count()
924        );
925        assert_eq!(
926            1,
927            graph[collider_non_sensor]
928                .as_collider2d()
929                .intersects(&graph.physics2d)
930                .count()
931        );
932    }
933}