nphysics2d/object/
collider.rs

1use std::f64;
2use std::sync::Arc;
3
4use na::RealField;
5use ncollide::pipeline::{
6    BroadPhaseProxyHandle, CollisionGroups, CollisionObject, CollisionObjectGraphIndex,
7    CollisionObjectRef, CollisionObjectUpdateFlags, GeometricQueryType,
8};
9use ncollide::shape::{Shape, ShapeHandle};
10use std::any::Any;
11
12use crate::material::{BasicMaterial, Material, MaterialHandle};
13use crate::math::{Isometry, Rotation, Vector};
14use crate::object::{BodyHandle, BodyPartHandle};
15
16use crate::utils::{UserData, UserDataBox};
17
18/// Description of the way a collider is attached to a body.
19#[derive(Clone)]
20pub enum ColliderAnchor<N: RealField + Copy, Handle: BodyHandle> {
21    /// Attach of a collider with a body part.
22    OnBodyPart {
23        /// The attached body part handle.
24        body_part: BodyPartHandle<Handle>,
25        /// Relative position of the collider wrt. the body part.
26        position_wrt_body_part: Isometry<N>,
27    },
28    /// Attach of a collider with a deformable body.
29    OnDeformableBody {
30        /// The attached body handle.
31        body: Handle,
32        /// A map between the colliders parts and body part indices.
33        ///
34        /// The `i`-th part of the collider corresponds to the `body_parts[i]`-th body part.
35        /// If set to `None`, the mapping is trivial, i.e., `i`-th part of the collider corresponds to the `i`-th body part.
36        body_parts: Option<Arc<Vec<usize>>>,
37    },
38}
39
40impl<N: RealField + Copy, Handle: BodyHandle> ColliderAnchor<N, Handle> {
41    /// The body this anchor is attached to.
42    #[inline]
43    pub fn body(&self) -> Handle {
44        match self {
45            ColliderAnchor::OnBodyPart { body_part, .. } => body_part.0,
46            ColliderAnchor::OnDeformableBody { body, .. } => *body,
47        }
48    }
49}
50
51/// The data a collider set must return after a collider has been removed.
52pub struct ColliderRemovalData<N: RealField + Copy, Handle: BodyHandle> {
53    pub(crate) anchor: ColliderAnchor<N, Handle>,
54    pub(crate) shape: ShapeHandle<N>,
55    pub(crate) density: N,
56    pub(crate) proxy_handle: BroadPhaseProxyHandle,
57    pub(crate) graph_index: CollisionObjectGraphIndex,
58}
59
60/// Data stored in each collider.
61///
62/// This is needed by nphysics.
63pub struct ColliderData<N: RealField + Copy, Handle: BodyHandle> {
64    margin: N,
65    density: N,
66    anchor: ColliderAnchor<N, Handle>,
67    // NOTE: needed for the collision filter.
68    body_status_dependent_ndofs: usize,
69    material: MaterialHandle<N>,
70    ccd_enabled: bool,
71    user_data: Option<Box<dyn Any + Send + Sync>>,
72}
73
74impl<N: RealField + Copy, Handle: BodyHandle> ColliderData<N, Handle> {
75    /// Initializes data for a collider.
76    pub fn new(
77        margin: N,
78        density: N,
79        anchor: ColliderAnchor<N, Handle>,
80        body_status_dependent_ndofs: usize,
81        material: MaterialHandle<N>,
82    ) -> Self {
83        ColliderData {
84            margin,
85            density,
86            anchor,
87            body_status_dependent_ndofs,
88            material,
89            ccd_enabled: false,
90            user_data: None,
91        }
92    }
93
94    user_data_accessors!();
95
96    /// The collision margin surrounding this collider.
97    #[inline]
98    pub fn margin(&self) -> N {
99        self.margin
100    }
101
102    /// Handle to the body this collider is attached to.
103    pub fn body(&self) -> Handle {
104        self.anchor.body()
105    }
106
107    /// The anchor attaching this collider with a body part or deformable body.
108    pub fn anchor(&self) -> &ColliderAnchor<N, Handle> {
109        &self.anchor
110    }
111
112    /// The density of this collider.
113    pub fn density(&self) -> N {
114        self.density
115    }
116
117    /// The position of this collider geometry wrt. the body it is attached to.
118    pub fn position_wrt_body(&self) -> Isometry<N> {
119        if let ColliderAnchor::OnBodyPart {
120            position_wrt_body_part,
121            ..
122        } = self.anchor
123        {
124            position_wrt_body_part
125        } else {
126            Isometry::identity()
127        }
128    }
129
130    /// Handle to the body part containing the given subshape of this collider's shape.
131    pub fn body_part(&self, subshape_id: usize) -> BodyPartHandle<Handle> {
132        match &self.anchor {
133            ColliderAnchor::OnBodyPart { body_part, .. } => *body_part,
134            ColliderAnchor::OnDeformableBody {
135                body, body_parts, ..
136            } => {
137                if let Some(body_parts) = body_parts {
138                    BodyPartHandle(*body, body_parts[subshape_id])
139                } else {
140                    BodyPartHandle(*body, subshape_id)
141                }
142            }
143        }
144    }
145
146    /// The material of this collider.
147    #[inline]
148    pub fn material(&self) -> &dyn Material<N> {
149        &*self.material
150    }
151
152    /// A mutable reference to this collider's material.
153    ///
154    /// If the material is shared, then an internal clone is performed
155    /// before returning the mutable reference (this effectively calls
156    /// the `Arc::make_mut` method to get a copy-on-write behavior).
157    #[inline]
158    pub fn material_mut(&mut self) -> &mut dyn Material<N> {
159        self.material.make_mut()
160    }
161}
162
163/// A geometric entity that can be attached to a body so it can be affected by contacts and proximity queries.
164#[repr(transparent)]
165pub struct Collider<N: RealField + Copy, Handle: BodyHandle>(
166    pub(crate) CollisionObject<N, ColliderData<N, Handle>>,
167); // FIXME: keep this pub(crate) or private?
168
169impl<N: RealField + Copy, Handle: BodyHandle> Collider<N, Handle> {
170    /// Computes the data that needs to be returned once this collider has been removed from a collider set.
171    pub fn removal_data(&self) -> Option<ColliderRemovalData<N, Handle>> {
172        Some(ColliderRemovalData {
173            anchor: self.0.data().anchor.clone(),
174            shape: self.shape_handle().clone(),
175            density: self.0.data().density,
176            proxy_handle: self.0.proxy_handle()?,
177            graph_index: self.0.graph_index()?,
178        })
179    }
180
181    /*
182     * Methods of ColliderData.
183     */
184    /// The user-data attached to this collider.
185    #[inline]
186    pub fn user_data(&self) -> Option<&(dyn Any + Send + Sync)> {
187        self.0.data().user_data.as_deref()
188    }
189
190    /// Mutable reference to the user-data attached to this collider.
191    #[inline]
192    pub fn user_data_mut(&mut self) -> Option<&mut (dyn Any + Send + Sync)> {
193        self.0.data_mut().user_data.as_deref_mut()
194    }
195
196    /// Sets the user-data attached to this collider.
197    #[inline]
198    pub fn set_user_data(
199        &mut self,
200        data: Option<Box<dyn Any + Send + Sync>>,
201    ) -> Option<Box<dyn Any + Send + Sync>> {
202        std::mem::replace(&mut self.0.data_mut().user_data, data)
203    }
204
205    /// Replaces the user-data of this collider by `None` and returns the old value.
206    #[inline]
207    pub fn take_user_data(&mut self) -> Option<Box<dyn Any + Send + Sync>> {
208        self.0.data_mut().user_data.take()
209    }
210
211    /// The collision margin surrounding this collider.
212    #[inline]
213    pub fn margin(&self) -> N {
214        self.0.data().margin()
215    }
216
217    /// Sets the marging on this collider's shapes.
218    #[inline]
219    pub fn set_margin(&mut self, margin: N) {
220        *self.0.update_flags_mut() |= CollisionObjectUpdateFlags::SHAPE_CHANGED;
221        self.0.data_mut().margin = margin;
222    }
223
224    /// Clears all the internal flags tracking changes made to this collider.
225    #[inline]
226    pub fn clear_update_flags(&mut self) {
227        self.0.clear_update_flags()
228    }
229
230    /// The density of this collider.
231    #[inline]
232    pub fn density(&self) -> N {
233        self.0.data().density
234    }
235
236    /// Handle to the body this collider is attached to.
237    #[inline]
238    pub fn body(&self) -> Handle {
239        self.0.data().body()
240    }
241
242    /// The anchor attaching this collider with a body part or deformable body.
243    #[inline]
244    pub fn anchor(&self) -> &ColliderAnchor<N, Handle> {
245        self.0.data().anchor()
246    }
247
248    /// The position of this collider geometry wrt. the body it is attached to.
249    #[inline]
250    pub fn position_wrt_body(&self) -> Isometry<N> {
251        self.0.data().position_wrt_body()
252    }
253
254    /// Handle to the body part containing the given subshape of this collider's shape.
255    #[inline]
256    pub fn body_part(&self, subshape_id: usize) -> BodyPartHandle<Handle> {
257        self.0.data().body_part(subshape_id)
258    }
259
260    /// The material of this collider.
261    #[inline]
262    pub fn material(&self) -> &dyn Material<N> {
263        self.0.data().material()
264    }
265
266    /// A mutable reference to this collider's material.
267    ///
268    /// If the material is shared, then an internal clone is performed
269    /// before returning the mutable reference (this effectively calls
270    /// the `Arc::make_mut` method to get a copy-on-write behavior).
271    #[inline]
272    pub fn material_mut(&mut self) -> &mut dyn Material<N> {
273        self.0.data_mut().material_mut()
274    }
275
276    /// Returns `true` if this collider is a sensor.
277    #[inline]
278    pub fn is_sensor(&self) -> bool {
279        self.query_type().is_proximity_query()
280    }
281
282    /// Returns `true` if this collider is subjected to Continuous Collision Detection (CCD).
283    #[inline]
284    pub fn is_ccd_enabled(&self) -> bool {
285        self.0.data().ccd_enabled
286    }
287
288    /// Enables or disables Continuous Collision Detection (CCD) for this collider.
289    #[inline]
290    pub fn enable_ccd(&mut self, enabled: bool) {
291        self.0.data_mut().ccd_enabled = enabled
292    }
293
294    #[inline]
295    pub(crate) fn body_status_dependent_ndofs(&self) -> usize {
296        self.0.data().body_status_dependent_ndofs
297    }
298
299    #[inline]
300    pub(crate) fn set_body_status_dependent_ndofs(&mut self, ndofs: usize) {
301        self.0.data_mut().body_status_dependent_ndofs = ndofs
302    }
303
304    /*
305     * Original methods from the CollisionObject.
306     */
307
308    /// This collider's non-stable graph index.
309    ///
310    /// This index may change whenever a collider is removed from the world.
311    #[inline]
312    pub fn graph_index(&self) -> Option<CollisionObjectGraphIndex> {
313        self.0.graph_index()
314    }
315
316    /// Sets the collider unique but non-stable graph index.
317    #[inline]
318    pub fn set_graph_index(&mut self, index: Option<CollisionObjectGraphIndex>) {
319        self.0.set_graph_index(index)
320    }
321
322    /// The collider's broad phase proxy unique identifier.
323    #[inline]
324    pub fn proxy_handle(&self) -> Option<BroadPhaseProxyHandle> {
325        self.0.proxy_handle()
326    }
327
328    /// Sets the collider's broad phase proxy unique identifier.
329    #[inline]
330    pub fn set_proxy_handle(&mut self, handle: Option<BroadPhaseProxyHandle>) {
331        self.0.set_proxy_handle(handle)
332    }
333
334    /// The collider position.
335    #[inline]
336    pub fn position(&self) -> &Isometry<N> {
337        self.0.position()
338    }
339
340    /// Sets the position of the collider.
341    #[inline]
342    pub fn set_position(&mut self, pos: Isometry<N>) {
343        self.0.set_position(pos)
344    }
345
346    /// Sets the position of the collider.
347    #[inline]
348    pub fn set_position_with_prediction(&mut self, position: Isometry<N>, prediction: Isometry<N>) {
349        self.0.set_position_with_prediction(position, prediction)
350    }
351
352    /// Deforms the underlying shape if possible.
353    ///
354    /// Panics if the shape is not deformable.
355    #[inline]
356    pub fn set_deformations(&mut self, coords: &[N]) {
357        self.0.set_deformations(coords)
358    }
359
360    /// This collider's shape.
361    #[inline]
362    pub fn shape(&self) -> &dyn Shape<N> {
363        &**self.0.shape()
364    }
365
366    /// This collider's shape.
367    #[inline]
368    pub fn shape_handle(&self) -> &ShapeHandle<N> {
369        self.0.shape()
370    }
371
372    /// Sets this collider's shape.
373    #[inline]
374    pub fn set_shape(&mut self, shape: ShapeHandle<N>) {
375        self.0.set_shape(shape)
376    }
377
378    /// The collision groups of the collider.
379    #[inline]
380    pub fn collision_groups(&self) -> &CollisionGroups {
381        self.0.collision_groups()
382    }
383
384    /// Sets the collision groups of this collider.
385    #[inline]
386    pub fn set_collision_groups(&mut self, groups: CollisionGroups) {
387        self.0.set_collision_groups(groups)
388    }
389
390    /// Returns the kind of queries this collider is expected to emit.
391    #[inline]
392    pub fn query_type(&self) -> GeometricQueryType<N> {
393        self.0.query_type()
394    }
395
396    /// Sets the `GeometricQueryType` of the collider.
397    /// Use `CollisionWorld::set_query_type` to use this method.
398    #[inline]
399    pub fn set_query_type(&mut self, query_type: GeometricQueryType<N>) {
400        self.0.set_query_type(query_type);
401    }
402}
403
404impl<N: RealField + Copy, Handle: BodyHandle> CollisionObjectRef<N> for Collider<N, Handle> {
405    fn graph_index(&self) -> Option<CollisionObjectGraphIndex> {
406        self.0.graph_index()
407    }
408
409    fn proxy_handle(&self) -> Option<BroadPhaseProxyHandle> {
410        self.0.proxy_handle()
411    }
412
413    fn position(&self) -> &Isometry<N> {
414        self.0.position()
415    }
416
417    fn predicted_position(&self) -> Option<&Isometry<N>> {
418        self.0.predicted_position()
419    }
420
421    fn shape(&self) -> &dyn Shape<N> {
422        self.0.shape().as_ref()
423    }
424
425    fn collision_groups(&self) -> &CollisionGroups {
426        self.0.collision_groups()
427    }
428
429    fn query_type(&self) -> GeometricQueryType<N> {
430        self.0.query_type()
431    }
432
433    fn update_flags(&self) -> CollisionObjectUpdateFlags {
434        self.0.update_flags()
435    }
436}
437
438/// A non-deformable collider builder.
439///
440/// See https://www.nphysics.org/rigid_body_simulations_with_contacts/#colliders for details.
441pub struct ColliderDesc<N: RealField + Copy> {
442    user_data: Option<UserDataBox>,
443    margin: N,
444    collision_groups: CollisionGroups,
445    shape: ShapeHandle<N>,
446    position: Isometry<N>,
447    material: Option<MaterialHandle<N>>,
448    density: N,
449    linear_prediction: N,
450    angular_prediction: N,
451    is_sensor: bool,
452    ccd_enabled: bool,
453}
454
455impl<N: RealField + Copy> ColliderDesc<N> {
456    /// Creates a new collider builder with the given shape.
457    pub fn new(shape: ShapeHandle<N>) -> Self {
458        let linear_prediction = na::convert(0.001);
459        let angular_prediction = na::convert(f64::consts::PI / 180.0 * 5.0);
460
461        ColliderDesc {
462            user_data: None,
463            shape,
464            margin: Self::default_margin(),
465            collision_groups: CollisionGroups::default(),
466            position: Isometry::identity(),
467            material: None,
468            density: N::zero(),
469            linear_prediction,
470            angular_prediction,
471            is_sensor: false,
472            ccd_enabled: false,
473        }
474    }
475
476    /// The default margin surrounding a collider: 0.01
477    pub fn default_margin() -> N {
478        na::convert(0.01)
479    }
480
481    user_data_desc_accessors!();
482
483    #[cfg(feature = "dim3")]
484    desc_custom_setters!(
485        self.rotation,
486        set_rotation,
487        axisangle: Vector<N> | { self.position.rotation = Rotation::new(axisangle) }
488    );
489
490    #[cfg(feature = "dim2")]
491    desc_custom_setters!(
492        self.rotation,
493        set_rotation,
494        angle: N | { self.position.rotation = Rotation::new(angle) }
495    );
496
497    desc_custom_setters!(
498        self.translation, set_translation, vector: Vector<N> | { self.position.translation.vector = vector }
499        self.material, set_material, material: MaterialHandle<N> | { self.material = Some(material) }
500    );
501
502    desc_setters!(
503        shape, set_shape, shape: ShapeHandle<N>
504        margin, set_margin, margin: N
505        density, set_density, density: N
506        collision_groups, set_collision_groups, collision_groups: CollisionGroups
507        linear_prediction, set_linear_prediction, linear_prediction: N
508        angular_prediction, set_angular_prediction, angular_prediction: N
509        sensor, set_is_sensor, is_sensor: bool
510        position, set_position, position: Isometry<N>
511        ccd_enabled, set_ccd_enabled, ccd_enabled: bool
512    );
513
514    #[cfg(feature = "dim3")]
515    desc_custom_getters!(self.get_rotation: Vector<N> | { self.position.rotation.scaled_axis() });
516
517    #[cfg(feature = "dim2")]
518    desc_custom_getters!(self.get_rotation: N | { self.position.rotation.angle() });
519
520    desc_custom_getters!(
521        self.get_shape: &dyn Shape<N> | { &*self.shape }
522        self.get_translation: &Vector<N> | { &self.position.translation.vector }
523        self.get_material: Option<&dyn Material<N>> | { self.material.as_deref() }
524    );
525
526    desc_getters!(
527        [val] get_margin -> margin: N
528        [val] get_density -> density: N
529        [val] get_collision_groups -> collision_groups: CollisionGroups
530        [val] get_linear_prediction -> linear_prediction: N
531        [val] get_angular_prediction -> angular_prediction: N
532        [val] is_sensor -> is_sensor: bool
533        [val] get_ccd_enabled -> ccd_enabled: bool
534        [ref] get_position -> position: Isometry<N>
535    );
536
537    /// Build a collider and configure it to be attached to the given parent body part.
538    pub fn build<Handle: BodyHandle>(
539        &self,
540        parent_handle: BodyPartHandle<Handle>,
541    ) -> Collider<N, Handle> {
542        let query = if self.is_sensor {
543            GeometricQueryType::Proximity(self.linear_prediction)
544        } else {
545            GeometricQueryType::Contacts(
546                self.margin + self.linear_prediction,
547                self.angular_prediction,
548            )
549        };
550
551        let anchor = ColliderAnchor::OnBodyPart {
552            body_part: parent_handle,
553            position_wrt_body_part: self.position,
554        };
555        let material = self
556            .material
557            .clone()
558            .unwrap_or_else(|| MaterialHandle::new(BasicMaterial::default()));
559        let mut data = ColliderData::new(self.margin, self.density, anchor, 0, material);
560        data.ccd_enabled = self.ccd_enabled;
561        data.user_data = self.user_data.as_ref().map(|data| data.0.to_any());
562        let co = CollisionObject::new(
563            None,
564            None,
565            self.position,
566            self.shape.clone(),
567            self.collision_groups,
568            query,
569            data,
570        );
571        Collider(co)
572    }
573}
574
575/// A deformable collider builder.
576pub struct DeformableColliderDesc<N: RealField + Copy> {
577    user_data: Option<UserDataBox>,
578    margin: N,
579    collision_groups: CollisionGroups,
580    shape: ShapeHandle<N>,
581    material: Option<MaterialHandle<N>>,
582    linear_prediction: N,
583    angular_prediction: N,
584    is_sensor: bool,
585    ccd_enabled: bool,
586    body_parts_mapping: Option<Arc<Vec<usize>>>,
587}
588
589impl<N: RealField + Copy> DeformableColliderDesc<N> {
590    /// Creates a deformable collider from the given shape.
591    ///
592    /// Panics if the shape is not deformable.
593    pub fn new(shape: ShapeHandle<N>) -> Self {
594        assert!(
595            shape.is_deformable_shape(),
596            "The the shape of a deformable collider must be deformable."
597        );
598        let linear_prediction = na::convert(0.002);
599        let angular_prediction = na::convert(f64::consts::PI / 180.0 * 5.0);
600
601        DeformableColliderDesc {
602            user_data: None,
603            shape,
604            margin: na::convert(0.01),
605            collision_groups: CollisionGroups::default(),
606            material: None,
607            linear_prediction,
608            angular_prediction,
609            is_sensor: false,
610            ccd_enabled: false,
611            body_parts_mapping: None,
612        }
613    }
614}
615
616impl<N: RealField + Copy> DeformableColliderDesc<N> {
617    user_data_desc_accessors!();
618
619    /// Sets the shape of this collider builder.
620    ///
621    /// Panics if the shape is not deformable.
622    pub fn shape(mut self, shape: ShapeHandle<N>) -> Self {
623        assert!(
624            shape.is_deformable_shape(),
625            "The the shape of a deformable collider must be deformable."
626        );
627        self.shape = shape;
628        self
629    }
630
631    /// Sets the shape of this collider builder.
632    ///
633    /// Panics if the shape is not deformable.
634    pub fn set_shape(&mut self, shape: ShapeHandle<N>) -> &mut Self {
635        assert!(
636            shape.is_deformable_shape(),
637            "The the shape of a deformable collider must be deformable."
638        );
639        self.shape = shape;
640        self
641    }
642
643    desc_custom_setters!(
644        self.material,
645        set_material,
646        material: MaterialHandle<N> | { self.material = Some(material) }
647    );
648
649    desc_setters!(
650        margin, set_margin, margin: N
651        collision_groups, set_collision_groups, collision_groups: CollisionGroups
652        linear_prediction, set_linear_prediction, linear_prediction: N
653        angular_prediction, set_angular_prediction, angular_prediction: N
654        as_sensor, set_as_sensor, is_sensor: bool
655        ccd_enabled, set_ccd_enabled, ccd_enabled: bool
656        body_parts_mapping, set_body_parts_mapping, body_parts_mapping: Option<Arc<Vec<usize>>>
657    );
658
659    desc_custom_getters!(
660        self.get_shape: &dyn Shape<N> | { &*self.shape }
661        self.get_material: Option<&dyn Material<N>> | { self.material.as_deref() }
662
663    );
664
665    desc_getters!(
666        [val] get_margin -> margin: N
667        [val] get_collision_groups -> collision_groups: CollisionGroups
668        [val] get_linear_prediction -> linear_prediction: N
669        [val] get_angular_prediction -> angular_prediction: N
670        [val] get_is_sensor -> is_sensor: bool
671        [val] get_ccd_enabled -> ccd_enabled: bool
672    );
673
674    /// Builds a deformable collider and configures it to be attached to the given parent body.
675    pub fn build<Handle: BodyHandle>(&self, parent_handle: Handle) -> Collider<N, Handle> {
676        let query = if self.is_sensor {
677            GeometricQueryType::Proximity(self.linear_prediction)
678        } else {
679            GeometricQueryType::Contacts(
680                self.margin + self.linear_prediction,
681                self.angular_prediction,
682            )
683        };
684
685        let body_parts = self.body_parts_mapping.clone();
686        let anchor = ColliderAnchor::OnDeformableBody {
687            body: parent_handle,
688            body_parts,
689        };
690        let material = self
691            .material
692            .clone()
693            .unwrap_or_else(|| MaterialHandle::new(BasicMaterial::default()));
694        let mut data = ColliderData::new(self.margin, N::zero(), anchor, 0, material);
695        data.ccd_enabled = data.ccd_enabled;
696        data.user_data = self.user_data.as_ref().map(|data| data.0.to_any());
697
698        let co = CollisionObject::new(
699            None,
700            None,
701            Isometry::identity(),
702            self.shape.clone(),
703            self.collision_groups,
704            query,
705            data,
706        );
707        Collider(co)
708    }
709}