bevy_rapier3d/geometry/
collider_impl.rs

1#[cfg(feature = "dim2")]
2use na::DVector;
3#[cfg(all(feature = "dim3", feature = "async-collider"))]
4use {
5    bevy::mesh::{Indices, VertexAttributeValues},
6    bevy::prelude::*,
7};
8
9use rapier::{
10    parry::transformation::voxelization::FillMode,
11    prelude::{FeatureId, Point, Ray, SharedShape, Vector, Voxels, DIM},
12};
13
14use super::{get_snapped_scale, shape_views::*};
15#[cfg(all(feature = "dim3", feature = "async-collider"))]
16use crate::geometry::ComputedColliderShape;
17use crate::math::{Real, Rot, Vect};
18use crate::{
19    geometry::{Collider, PointProjection, RayIntersection, TriMeshFlags, VHACDParameters},
20    math::IVect,
21};
22
23impl Collider {
24    /// The scaling factor that was applied to this collider.
25    pub fn scale(&self) -> Vect {
26        self.scale
27    }
28
29    /// This replaces the unscaled version of this collider by its scaled version,
30    /// and resets `self.scale()` to `1.0`.
31    pub fn promote_scaled_shape(&mut self) {
32        self.unscaled = self.raw.clone();
33        self.scale = Vect::ONE;
34    }
35
36    /// Initialize a new collider with a compound shape.
37    pub fn compound(shapes: Vec<(Vect, Rot, Collider)>) -> Self {
38        let shapes = shapes
39            .into_iter()
40            .map(|(t, r, s)| ((t, r).into(), s.raw))
41            .collect();
42        SharedShape::compound(shapes).into()
43    }
44
45    /// Initialize a new collider with a ball shape defined by its radius.
46    pub fn ball(radius: Real) -> Self {
47        SharedShape::ball(radius).into()
48    }
49
50    /// Initialize a new collider build with a half-space shape defined by the outward normal
51    /// of its planar boundary.
52    pub fn halfspace(outward_normal: Vect) -> Option<Self> {
53        use rapier::na::Unit;
54        let normal = Vector::from(outward_normal);
55        Unit::try_new(normal, 1.0e-6).map(|n| SharedShape::halfspace(n).into())
56    }
57
58    /// Initialize a new collider with a cylindrical shape defined by its half-height
59    /// (along the y axis) and its radius.
60    #[cfg(feature = "dim3")]
61    pub fn cylinder(half_height: Real, radius: Real) -> Self {
62        SharedShape::cylinder(half_height, radius).into()
63    }
64
65    /// Initialize a new collider with a rounded cylindrical shape defined by its half-height
66    /// (along the y axis), its radius, and its roundedness (the
67    /// radius of the sphere used for dilating the cylinder).
68    #[cfg(feature = "dim3")]
69    pub fn round_cylinder(half_height: Real, radius: Real, border_radius: Real) -> Self {
70        SharedShape::round_cylinder(half_height, radius, border_radius).into()
71    }
72
73    /// Initialize a new collider with a cone shape defined by its half-height
74    /// (along the y axis) and its basis radius.
75    #[cfg(feature = "dim3")]
76    pub fn cone(half_height: Real, radius: Real) -> Self {
77        SharedShape::cone(half_height, radius).into()
78    }
79
80    /// Initialize a new collider with a rounded cone shape defined by its half-height
81    /// (along the y axis), its radius, and its roundedness (the
82    /// radius of the sphere used for dilating the cylinder).
83    #[cfg(feature = "dim3")]
84    pub fn round_cone(half_height: Real, radius: Real, border_radius: Real) -> Self {
85        SharedShape::round_cone(half_height, radius, border_radius).into()
86    }
87
88    /// Initialize a new collider with a cuboid shape defined by its half-extents.
89    #[cfg(feature = "dim2")]
90    pub fn cuboid(half_x: Real, half_y: Real) -> Self {
91        SharedShape::cuboid(half_x, half_y).into()
92    }
93
94    /// Initialize a new collider with a round cuboid shape defined by its half-extents
95    /// and border radius.
96    #[cfg(feature = "dim2")]
97    pub fn round_cuboid(half_x: Real, half_y: Real, border_radius: Real) -> Self {
98        SharedShape::round_cuboid(half_x, half_y, border_radius).into()
99    }
100
101    /// Initialize a new collider with a capsule shape.
102    pub fn capsule(start: Vect, end: Vect, radius: Real) -> Self {
103        SharedShape::capsule(start.into(), end.into(), radius).into()
104    }
105
106    /// Initialize a new collider with a capsule shape aligned with the `x` axis.
107    pub fn capsule_x(half_height: Real, radius: Real) -> Self {
108        let p = Point::from(Vector::x() * half_height);
109        SharedShape::capsule(-p, p, radius).into()
110    }
111
112    /// Initialize a new collider with a capsule shape aligned with the `y` axis.
113    pub fn capsule_y(half_height: Real, radius: Real) -> Self {
114        let p = Point::from(Vector::y() * half_height);
115        SharedShape::capsule(-p, p, radius).into()
116    }
117
118    /// Initialize a new collider with a capsule shape aligned with the `z` axis.
119    #[cfg(feature = "dim3")]
120    pub fn capsule_z(half_height: Real, radius: Real) -> Self {
121        let p = Point::from(Vector::z() * half_height);
122        SharedShape::capsule(-p, p, radius).into()
123    }
124
125    /// Initialize a new collider with a cuboid shape defined by its half-extents.
126    #[cfg(feature = "dim3")]
127    pub fn cuboid(hx: Real, hy: Real, hz: Real) -> Self {
128        SharedShape::cuboid(hx, hy, hz).into()
129    }
130
131    /// Initialize a new collider with a round cuboid shape defined by its half-extents
132    /// and border radius.
133    #[cfg(feature = "dim3")]
134    pub fn round_cuboid(half_x: Real, half_y: Real, half_z: Real, border_radius: Real) -> Self {
135        SharedShape::round_cuboid(half_x, half_y, half_z, border_radius).into()
136    }
137
138    /// Initializes a collider with a segment shape.
139    pub fn segment(a: Vect, b: Vect) -> Self {
140        SharedShape::segment(a.into(), b.into()).into()
141    }
142
143    /// Initializes a collider with a triangle shape.
144    pub fn triangle(a: Vect, b: Vect, c: Vect) -> Self {
145        SharedShape::triangle(a.into(), b.into(), c.into()).into()
146    }
147
148    /// Initializes a collider with a triangle shape with round corners.
149    pub fn round_triangle(a: Vect, b: Vect, c: Vect, border_radius: Real) -> Self {
150        SharedShape::round_triangle(a.into(), b.into(), c.into(), border_radius).into()
151    }
152
153    fn ivec_array_from_point_int_array(points: &[IVect]) -> Vec<Point<i32>> {
154        points
155            .iter()
156            .map(|p| {
157                #[cfg(feature = "dim3")]
158                return Point::new(p.x, p.y, p.z);
159                #[cfg(feature = "dim2")]
160                return Point::new(p.x, p.y);
161            })
162            .collect::<Vec<_>>()
163    }
164
165    fn vec_array_from_point_float_array(points: &[Vect]) -> Vec<Point<Real>> {
166        points
167            .iter()
168            .map(|p| {
169                #[cfg(feature = "dim3")]
170                return Point::new(p.x, p.y, p.z);
171                #[cfg(feature = "dim2")]
172                return Point::new(p.x, p.y);
173            })
174            .collect::<Vec<_>>()
175    }
176
177    /// Initializes a shape made of voxels.
178    ///
179    /// Each voxel has the size `voxel_size` and grid coordinate given by `grid_coords`.
180    /// The `primitive_geometry` controls the behavior of collision detection at voxels boundaries.
181    ///
182    /// For initializing a voxels shape from points in space, see [`Self::voxels_from_points`].
183    /// For initializing a voxels shape from a mesh to voxelize, see [`Self::voxelized_mesh`].
184    /// For initializing multiple voxels shape from the convex decomposition of a mesh, see
185    /// [`Self::voxelized_convex_decomposition`].
186    pub fn voxels(voxel_size: Vect, grid_coordinates: &[IVect]) -> Self {
187        let shape = Voxels::new(
188            voxel_size.into(),
189            &Self::ivec_array_from_point_int_array(grid_coordinates),
190        );
191        SharedShape::new(shape).into()
192    }
193
194    /// Initializes a shape made of voxels.
195    ///
196    /// Each voxel has the size `voxel_size` and contains at least one point from `centers`.
197    /// The `primitive_geometry` controls the behavior of collision detection at voxels boundaries.
198    pub fn voxels_from_points(voxel_size: Vect, points: &[Vect]) -> Self {
199        SharedShape::voxels_from_points(
200            voxel_size.into(),
201            &Self::vec_array_from_point_float_array(points),
202        )
203        .into()
204    }
205
206    /// Initializes a voxels shape obtained from the decomposition of the given trimesh (in 3D)
207    /// or polyline (in 2D) into voxelized convex parts.
208    pub fn voxelized_mesh(
209        vertices: &[Vect],
210        indices: &[[u32; DIM]],
211        voxel_size: Real,
212        fill_mode: FillMode,
213    ) -> Self {
214        let vertices = Self::vec_array_from_point_float_array(vertices);
215        SharedShape::voxelized_mesh(&vertices, indices, voxel_size, fill_mode).into()
216    }
217
218    /// Initializes a compound shape obtained from the decomposition of the given trimesh (in 3D)
219    /// or polyline (in 2D) into voxelized convex parts.
220    pub fn voxelized_convex_decomposition(vertices: &[Vect], indices: &[[u32; DIM]]) -> Vec<Self> {
221        Self::voxelized_convex_decomposition_with_params(
222            vertices,
223            indices,
224            &VHACDParameters::default(),
225        )
226    }
227
228    /// Initializes a compound shape obtained from the decomposition of the given trimesh (in 3D)
229    /// or polyline (in 2D) into voxelized convex parts.
230    pub fn voxelized_convex_decomposition_with_params(
231        vertices: &[Vect],
232        indices: &[[u32; DIM]],
233        params: &VHACDParameters,
234    ) -> Vec<Self> {
235        SharedShape::voxelized_convex_decomposition_with_params(
236            &Self::vec_array_from_point_float_array(vertices),
237            indices,
238            params,
239        )
240        .into_iter()
241        .map(|c| c.into())
242        .collect()
243    }
244
245    /// Initializes a collider with a polyline shape defined by its vertex and index buffers.
246    pub fn polyline(vertices: Vec<Vect>, indices: Option<Vec<[u32; 2]>>) -> Self {
247        let vertices = vertices.into_iter().map(|v| v.into()).collect();
248        SharedShape::polyline(vertices, indices).into()
249    }
250
251    /// Initializes a collider with a triangle mesh shape defined by its vertex and index buffers.
252    pub fn trimesh(
253        vertices: Vec<Vect>,
254        indices: Vec<[u32; 3]>,
255    ) -> Result<Self, crate::rapier::prelude::TriMeshBuilderError> {
256        let vertices = vertices.into_iter().map(|v| v.into()).collect();
257        Ok(SharedShape::trimesh(vertices, indices)?.into())
258    }
259
260    /// Initializes a collider with a triangle mesh shape defined by its vertex and index buffers, and flags
261    /// controlling its pre-processing.
262    pub fn trimesh_with_flags(
263        vertices: Vec<Vect>,
264        indices: Vec<[u32; 3]>,
265        flags: TriMeshFlags,
266    ) -> Result<Self, crate::rapier::prelude::TriMeshBuilderError> {
267        let vertices = vertices.into_iter().map(|v| v.into()).collect();
268        Ok(SharedShape::trimesh_with_flags(vertices, indices, flags)?.into())
269    }
270
271    /// Initializes a collider with a Bevy Mesh.
272    ///
273    /// Returns `None` if the index buffer or vertex buffer of the mesh are in an incompatible format.
274    #[cfg(all(feature = "dim3", feature = "async-collider"))]
275    pub fn from_bevy_mesh(mesh: &Mesh, collider_shape: &ComputedColliderShape) -> Option<Self> {
276        let (vtx, idx) = extract_mesh_vertices_indices(mesh)?;
277
278        match collider_shape {
279            ComputedColliderShape::TriMesh(flags) => Some(
280                SharedShape::trimesh_with_flags(vtx, idx, *flags)
281                    .ok()?
282                    .into(),
283            ),
284            ComputedColliderShape::ConvexHull => {
285                SharedShape::convex_hull(&vtx).map(|shape| shape.into())
286            }
287            ComputedColliderShape::ConvexDecomposition(params) => {
288                Some(SharedShape::convex_decomposition_with_params(&vtx, &idx, params).into())
289            }
290        }
291    }
292
293    /// Initializes a collider with a compound shape obtained from the decomposition of
294    /// the given trimesh (in 3D) or polyline (in 2D) into convex parts.
295    pub fn convex_decomposition(vertices: &[Vect], indices: &[[u32; DIM]]) -> Self {
296        let vertices: Vec<_> = vertices.iter().map(|v| (*v).into()).collect();
297        SharedShape::convex_decomposition(&vertices, indices).into()
298    }
299
300    /// Initializes a collider with a compound shape obtained from the decomposition of
301    /// the given trimesh (in 3D) or polyline (in 2D) into convex parts dilated with round corners.
302    pub fn round_convex_decomposition(
303        vertices: &[Vect],
304        indices: &[[u32; DIM]],
305        border_radius: Real,
306    ) -> Self {
307        let vertices: Vec<_> = vertices.iter().map(|v| (*v).into()).collect();
308        SharedShape::round_convex_decomposition(&vertices, indices, border_radius).into()
309    }
310
311    /// Initializes a collider with a compound shape obtained from the decomposition of
312    /// the given trimesh (in 3D) or polyline (in 2D) into convex parts.
313    pub fn convex_decomposition_with_params(
314        vertices: &[Vect],
315        indices: &[[u32; DIM]],
316        params: &VHACDParameters,
317    ) -> Self {
318        let vertices: Vec<_> = vertices.iter().map(|v| (*v).into()).collect();
319        SharedShape::convex_decomposition_with_params(&vertices, indices, params).into()
320    }
321
322    /// Initializes a collider with a compound shape obtained from the decomposition of
323    /// the given trimesh (in 3D) or polyline (in 2D) into convex parts dilated with round corners.
324    pub fn round_convex_decomposition_with_params(
325        vertices: &[Vect],
326        indices: &[[u32; DIM]],
327        params: &VHACDParameters,
328        border_radius: Real,
329    ) -> Self {
330        let vertices: Vec<_> = vertices.iter().map(|v| (*v).into()).collect();
331        SharedShape::round_convex_decomposition_with_params(
332            &vertices,
333            indices,
334            params,
335            border_radius,
336        )
337        .into()
338    }
339
340    /// Initializes a new collider with a 2D convex polygon or 3D convex polyhedron
341    /// obtained after computing the convex-hull of the given points.
342    pub fn convex_hull(points: &[Vect]) -> Option<Self> {
343        let points: Vec<_> = points.iter().map(|v| (*v).into()).collect();
344        SharedShape::convex_hull(&points).map(Into::into)
345    }
346
347    /// Initializes a new collider with a round 2D convex polygon or 3D convex polyhedron
348    /// obtained after computing the convex-hull of the given points. The shape is dilated
349    /// by a sphere of radius `border_radius`.
350    pub fn round_convex_hull(points: &[Vect], border_radius: Real) -> Option<Self> {
351        let points: Vec<_> = points.iter().map(|v| (*v).into()).collect();
352        SharedShape::round_convex_hull(&points, border_radius).map(Into::into)
353    }
354
355    /// Creates a new collider that is a convex polygon formed by the
356    /// given polyline assumed to be convex (no convex-hull will be automatically
357    /// computed).
358    #[cfg(feature = "dim2")]
359    pub fn convex_polyline(points: Vec<Vect>) -> Option<Self> {
360        let points = points.into_iter().map(|v| v.into()).collect();
361        SharedShape::convex_polyline(points).map(Into::into)
362    }
363
364    /// Creates a new collider that is a round convex polygon formed by the
365    /// given polyline assumed to be convex (no convex-hull will be automatically
366    /// computed). The polygon shape is dilated by a sphere of radius `border_radius`.
367    #[cfg(feature = "dim2")]
368    pub fn round_convex_polyline(points: Vec<Vect>, border_radius: Real) -> Option<Self> {
369        let points = points.into_iter().map(|v| v.into()).collect();
370        SharedShape::round_convex_polyline(points, border_radius).map(Into::into)
371    }
372
373    /// Creates a new collider that is a convex polyhedron formed by the
374    /// given triangle-mesh assumed to be convex (no convex-hull will be automatically
375    /// computed).
376    #[cfg(feature = "dim3")]
377    pub fn convex_mesh(points: Vec<Vect>, indices: &[[u32; 3]]) -> Option<Self> {
378        let points = points.into_iter().map(|v| v.into()).collect();
379        SharedShape::convex_mesh(points, indices).map(Into::into)
380    }
381
382    /// Creates a new collider that is a round convex polyhedron formed by the
383    /// given triangle-mesh assumed to be convex (no convex-hull will be automatically
384    /// computed). The triangle mesh shape is dilated by a sphere of radius `border_radius`.
385    #[cfg(feature = "dim3")]
386    pub fn round_convex_mesh(
387        points: Vec<Vect>,
388        indices: &[[u32; 3]],
389        border_radius: Real,
390    ) -> Option<Self> {
391        let points = points.into_iter().map(|v| v.into()).collect();
392        SharedShape::round_convex_mesh(points, indices, border_radius).map(Into::into)
393    }
394
395    /// Initializes a collider with a heightfield shape defined by its set of height and a scale
396    /// factor along each coordinate axis.
397    #[cfg(feature = "dim2")]
398    pub fn heightfield(heights: Vec<Real>, scale: Vect) -> Self {
399        SharedShape::heightfield(DVector::from_vec(heights), scale.into()).into()
400    }
401
402    /// Initializes a collider with a heightfield shape defined by its set of height (in
403    /// column-major format) and a scale factor along each coordinate axis.
404    #[cfg(feature = "dim3")]
405    pub fn heightfield(heights: Vec<Real>, num_rows: usize, num_cols: usize, scale: Vect) -> Self {
406        assert_eq!(
407            heights.len(),
408            num_rows * num_cols,
409            "Invalid number of heights provided."
410        );
411        let heights = rapier::na::DMatrix::from_vec(num_rows, num_cols, heights);
412        SharedShape::heightfield(heights, scale.into()).into()
413    }
414
415    /// Takes a strongly typed reference of this collider.
416    pub fn as_typed_shape(&self) -> ColliderView<'_> {
417        self.raw.as_typed_shape().into()
418    }
419
420    /// Takes a strongly typed reference of the unscaled version of this collider.
421    pub fn as_unscaled_typed_shape(&self) -> ColliderView<'_> {
422        self.unscaled.as_typed_shape().into()
423    }
424
425    /// Downcast this collider to a ball, if it is one.
426    pub fn as_ball(&self) -> Option<BallView<'_>> {
427        self.raw.as_ball().map(|s| BallView { raw: s })
428    }
429
430    /// Downcast this collider to a cuboid, if it is one.
431    pub fn as_cuboid(&self) -> Option<CuboidView<'_>> {
432        self.raw.as_cuboid().map(|s| CuboidView { raw: s })
433    }
434
435    /// Downcast this collider to a capsule, if it is one.
436    pub fn as_capsule(&self) -> Option<CapsuleView<'_>> {
437        self.raw.as_capsule().map(|s| CapsuleView { raw: s })
438    }
439
440    /// Downcast this collider to a segment, if it is one.
441    pub fn as_segment(&self) -> Option<SegmentView<'_>> {
442        self.raw.as_segment().map(|s| SegmentView { raw: s })
443    }
444
445    /// Downcast this collider to a triangle, if it is one.
446    pub fn as_triangle(&self) -> Option<TriangleView<'_>> {
447        self.raw.as_triangle().map(|s| TriangleView { raw: s })
448    }
449
450    /// Downcast this collider to a voxels, if it is one.
451    pub fn as_voxels(&self) -> Option<VoxelsView<'_>> {
452        self.raw.as_voxels().map(|s| VoxelsView { raw: s })
453    }
454
455    /// Downcast this collider to a triangle mesh, if it is one.
456    pub fn as_trimesh(&self) -> Option<TriMeshView<'_>> {
457        self.raw.as_trimesh().map(|s| TriMeshView { raw: s })
458    }
459
460    /// Downcast this collider to a polyline, if it is one.
461    pub fn as_polyline(&self) -> Option<PolylineView<'_>> {
462        self.raw.as_polyline().map(|s| PolylineView { raw: s })
463    }
464
465    /// Downcast this collider to a half-space, if it is one.
466    pub fn as_halfspace(&self) -> Option<HalfSpaceView<'_>> {
467        self.raw.as_halfspace().map(|s| HalfSpaceView { raw: s })
468    }
469
470    /// Downcast this collider to a heightfield, if it is one.
471    pub fn as_heightfield(&self) -> Option<HeightFieldView<'_>> {
472        self.raw
473            .as_heightfield()
474            .map(|s| HeightFieldView { raw: s })
475    }
476
477    /// Downcast this collider to a compound shape, if it is one.
478    pub fn as_compound(&self) -> Option<CompoundView<'_>> {
479        self.raw.as_compound().map(|s| CompoundView { raw: s })
480    }
481
482    /// Downcast this collider to a convex polygon, if it is one.
483    #[cfg(feature = "dim2")]
484    pub fn as_convex_polygon(&self) -> Option<ConvexPolygonView<'_>> {
485        self.raw
486            .as_convex_polygon()
487            .map(|s| ConvexPolygonView { raw: s })
488    }
489
490    /// Downcast this collider to a convex polyhedron, if it is one.
491    #[cfg(feature = "dim3")]
492    pub fn as_convex_polyhedron(&self) -> Option<ConvexPolyhedronView<'_>> {
493        self.raw
494            .as_convex_polyhedron()
495            .map(|s| ConvexPolyhedronView { raw: s })
496    }
497
498    /// Downcast this collider to a cylinder, if it is one.
499    #[cfg(feature = "dim3")]
500    pub fn as_cylinder(&self) -> Option<CylinderView<'_>> {
501        self.raw.as_cylinder().map(|s| CylinderView { raw: s })
502    }
503
504    /// Downcast this collider to a cone, if it is one.
505    #[cfg(feature = "dim3")]
506    pub fn as_cone(&self) -> Option<ConeView<'_>> {
507        self.raw.as_cone().map(|s| ConeView { raw: s })
508    }
509
510    /// Downcast this collider to a mutable ball, if it is one.
511    pub fn as_ball_mut(&mut self) -> Option<BallViewMut<'_>> {
512        self.raw
513            .make_mut()
514            .as_ball_mut()
515            .map(|s| BallViewMut { raw: s })
516    }
517
518    /// Downcast this collider to a mutable cuboid, if it is one.
519    pub fn as_cuboid_mut(&mut self) -> Option<CuboidViewMut<'_>> {
520        self.raw
521            .make_mut()
522            .as_cuboid_mut()
523            .map(|s| CuboidViewMut { raw: s })
524    }
525
526    /// Downcast this collider to a mutable capsule, if it is one.
527    pub fn as_capsule_mut(&mut self) -> Option<CapsuleViewMut<'_>> {
528        self.raw
529            .make_mut()
530            .as_capsule_mut()
531            .map(|s| CapsuleViewMut { raw: s })
532    }
533
534    /// Downcast this collider to a mutable segment, if it is one.
535    pub fn as_segment_mut(&mut self) -> Option<SegmentViewMut<'_>> {
536        self.raw
537            .make_mut()
538            .as_segment_mut()
539            .map(|s| SegmentViewMut { raw: s })
540    }
541
542    /// Downcast this collider to a mutable triangle, if it is one.
543    pub fn as_triangle_mut(&mut self) -> Option<TriangleViewMut<'_>> {
544        self.raw
545            .make_mut()
546            .as_triangle_mut()
547            .map(|s| TriangleViewMut { raw: s })
548    }
549
550    /// Downcast this collider to a mutable voxels, if it is one.
551    pub fn as_voxels_mut(&mut self) -> Option<VoxelsViewMut<'_>> {
552        self.raw
553            .make_mut()
554            .as_voxels_mut()
555            .map(|s| VoxelsViewMut { raw: s })
556    }
557
558    /// Downcast this collider to a mutable triangle mesh, if it is one.
559    pub fn as_trimesh_mut(&mut self) -> Option<TriMeshViewMut<'_>> {
560        self.raw
561            .make_mut()
562            .as_trimesh_mut()
563            .map(|s| TriMeshViewMut { raw: s })
564    }
565
566    /// Downcast this collider to a mutable polyline, if it is one.
567    pub fn as_polyline_mut(&mut self) -> Option<PolylineViewMut<'_>> {
568        self.raw
569            .make_mut()
570            .as_polyline_mut()
571            .map(|s| PolylineViewMut { raw: s })
572    }
573
574    /// Downcast this collider to a mutable half-space, if it is one.
575    pub fn as_halfspace_mut(&mut self) -> Option<HalfSpaceViewMut<'_>> {
576        self.raw
577            .make_mut()
578            .as_halfspace_mut()
579            .map(|s| HalfSpaceViewMut { raw: s })
580    }
581
582    /// Downcast this collider to a mutable heightfield, if it is one.
583    pub fn as_heightfield_mut(&mut self) -> Option<HeightFieldViewMut<'_>> {
584        self.raw
585            .make_mut()
586            .as_heightfield_mut()
587            .map(|s| HeightFieldViewMut { raw: s })
588    }
589
590    // /// Downcast this collider to a mutable compound shape, if it is one.
591    // pub fn as_compound_mut(&mut self) -> Option<CompoundViewMut> {
592    //     self.raw.make_mut()
593    //         .as_compound_mut()
594    //         .map(|s| CompoundViewMut { raw: s })
595    // }
596
597    // /// Downcast this collider to a mutable convex polygon, if it is one.
598    // #[cfg(feature = "dim2")]
599    // pub fn as_convex_polygon_mut(&mut self) -> Option<ConvexPolygonViewMut> {
600    //     self.raw.make_mut()
601    //         .as_convex_polygon_mut()
602    //         .map(|s| ConvexPolygonViewMut { raw: s })
603    // }
604
605    // /// Downcast this collider to a mutable convex polyhedron, if it is one.
606    // #[cfg(feature = "dim3")]
607    // pub fn as_convex_polyhedron_mut(&mut self) -> Option<ConvexPolyhedronViewMut> {
608    //     self.raw.make_mut()
609    //         .as_convex_polyhedron_mut()
610    //         .map(|s| ConvexPolyhedronViewMut { raw: s })
611    // }
612
613    /// Downcast this collider to a mutable cylinder, if it is one.
614    #[cfg(feature = "dim3")]
615    pub fn as_cylinder_mut(&mut self) -> Option<CylinderViewMut<'_>> {
616        self.raw
617            .make_mut()
618            .as_cylinder_mut()
619            .map(|s| CylinderViewMut { raw: s })
620    }
621
622    /// Downcast this collider to a mutable cone, if it is one.
623    #[cfg(feature = "dim3")]
624    pub fn as_cone_mut(&mut self) -> Option<ConeViewMut<'_>> {
625        self.raw
626            .make_mut()
627            .as_cone_mut()
628            .map(|s| ConeViewMut { raw: s })
629    }
630
631    /// Set the scaling factor of this shape.
632    ///
633    /// If the scaling factor is non-uniform, and the scaled shape can’t be
634    /// represented as a supported smooth shape (for example scalling a Ball
635    /// with a non-uniform scale results in an ellipse which isn’t supported),
636    /// the shape is approximated by a convex polygon/convex polyhedron using
637    /// `num_subdivisions` subdivisions.
638    pub fn set_scale(&mut self, scale: Vect, num_subdivisions: u32) {
639        let scale = get_snapped_scale(scale);
640
641        if scale == self.scale {
642            // Nothing to do.
643            return;
644        }
645
646        if scale == Vect::ONE {
647            // Trivial case.
648            self.raw = self.unscaled.clone();
649            self.scale = Vect::ONE;
650            return;
651        }
652
653        if let Some(scaled) = self
654            .as_unscaled_typed_shape()
655            .raw_scale_by(scale, num_subdivisions)
656        {
657            self.raw = scaled;
658            self.scale = scale;
659        } else {
660            log::error!("Failed to create the scaled convex hull geometry.");
661        }
662    }
663
664    /// Projects a point on `self`, unless the projection lies further than the given max distance.
665    ///
666    /// The point is assumed to be expressed in the local-space of `self`.
667    pub fn project_local_point_with_max_dist(
668        &self,
669        point: Vect,
670        solid: bool,
671        max_dist: Real,
672    ) -> Option<PointProjection> {
673        self.raw
674            .project_local_point_with_max_dist(&point.into(), solid, max_dist)
675            .map(Into::into)
676    }
677
678    /// Projects a point on `self` transformed by `m`, unless the projection lies further than the given max distance.
679    pub fn project_point_with_max_dist(
680        &self,
681        translation: Vect,
682        rotation: Rot,
683        point: Vect,
684        solid: bool,
685        max_dist: Real,
686    ) -> Option<PointProjection> {
687        let pos = (translation, rotation).into();
688        self.raw
689            .project_point_with_max_dist(&pos, &point.into(), solid, max_dist)
690            .map(Into::into)
691    }
692
693    /// Projects a point on `self`.
694    ///
695    /// The point is assumed to be expressed in the local-space of `self`.
696    pub fn project_local_point(&self, point: Vect, solid: bool) -> PointProjection {
697        self.raw.project_local_point(&point.into(), solid).into()
698    }
699
700    /// Projects a point on the boundary of `self` and returns the id of the
701    /// feature the point was projected on.
702    pub fn project_local_point_and_get_feature(&self, point: Vect) -> (PointProjection, FeatureId) {
703        let (proj, feat) = self.raw.project_local_point_and_get_feature(&point.into());
704        (proj.into(), feat)
705    }
706
707    /// Computes the minimal distance between a point and `self`.
708    pub fn distance_to_local_point(&self, point: Vect, solid: bool) -> Real {
709        self.raw.distance_to_local_point(&point.into(), solid)
710    }
711
712    /// Tests if the given point is inside of `self`.
713    pub fn contains_local_point(&self, point: Vect) -> bool {
714        self.raw.contains_local_point(&point.into())
715    }
716
717    /// Projects a point on `self` transformed by `m`.
718    pub fn project_point(
719        &self,
720        translation: Vect,
721        rotation: Rot,
722        point: Vect,
723        solid: bool,
724    ) -> PointProjection {
725        let pos = (translation, rotation).into();
726        self.raw.project_point(&pos, &point.into(), solid).into()
727    }
728
729    /// Computes the minimal distance between a point and `self` transformed by `m`.
730    #[inline]
731    pub fn distance_to_point(
732        &self,
733        translation: Vect,
734        rotation: Rot,
735        point: Vect,
736        solid: bool,
737    ) -> Real {
738        let pos = (translation, rotation).into();
739        self.raw.distance_to_point(&pos, &point.into(), solid)
740    }
741
742    /// Projects a point on the boundary of `self` transformed by `m` and returns the id of the
743    /// feature the point was projected on.
744    pub fn project_point_and_get_feature(
745        &self,
746        translation: Vect,
747        rotation: Rot,
748        point: Vect,
749    ) -> (PointProjection, FeatureId) {
750        let pos = (translation, rotation).into();
751        let (proj, feat) = self.raw.project_point_and_get_feature(&pos, &point.into());
752        (proj.into(), feat)
753    }
754
755    /// Tests if the given point is inside of `self` transformed by `m`.
756    pub fn contains_point(&self, translation: Vect, rotation: Rot, point: Vect) -> bool {
757        let pos = (translation, rotation).into();
758        self.raw.contains_point(&pos, &point.into())
759    }
760
761    /// Computes the time of impact between this transform shape and a ray.
762    pub fn cast_local_ray(
763        &self,
764        ray_origin: Vect,
765        ray_dir: Vect,
766        max_time_of_impact: Real,
767        solid: bool,
768    ) -> Option<Real> {
769        let ray = Ray::new(ray_origin.into(), ray_dir.into());
770        self.raw.cast_local_ray(&ray, max_time_of_impact, solid)
771    }
772
773    /// Computes the time of impact, and normal between this transformed shape and a ray.
774    pub fn cast_local_ray_and_get_normal(
775        &self,
776        ray_origin: Vect,
777        ray_dir: Vect,
778        max_time_of_impact: Real,
779        solid: bool,
780    ) -> Option<RayIntersection> {
781        let ray = Ray::new(ray_origin.into(), ray_dir.into());
782        self.raw
783            .cast_local_ray_and_get_normal(&ray, max_time_of_impact, solid)
784            .map(|inter| RayIntersection::from_rapier(inter, ray_origin, ray_dir))
785    }
786
787    /// Tests whether a ray intersects this transformed shape.
788    pub fn intersects_local_ray(
789        &self,
790        ray_origin: Vect,
791        ray_dir: Vect,
792        max_time_of_impact: Real,
793    ) -> bool {
794        let ray = Ray::new(ray_origin.into(), ray_dir.into());
795        self.raw.intersects_local_ray(&ray, max_time_of_impact)
796    }
797
798    /// Computes the time of impact between this transform shape and a ray.
799    pub fn cast_ray(
800        &self,
801        translation: Vect,
802        rotation: Rot,
803        ray_origin: Vect,
804        ray_dir: Vect,
805        max_time_of_impact: Real,
806        solid: bool,
807    ) -> Option<Real> {
808        let pos = (translation, rotation).into();
809        let ray = Ray::new(ray_origin.into(), ray_dir.into());
810        self.raw.cast_ray(&pos, &ray, max_time_of_impact, solid)
811    }
812
813    /// Computes the time of impact, and normal between this transformed shape and a ray.
814    pub fn cast_ray_and_get_normal(
815        &self,
816        translation: Vect,
817        rotation: Rot,
818        ray_origin: Vect,
819        ray_dir: Vect,
820        max_time_of_impact: Real,
821        solid: bool,
822    ) -> Option<RayIntersection> {
823        let pos = (translation, rotation).into();
824        let ray = Ray::new(ray_origin.into(), ray_dir.into());
825        self.raw
826            .cast_ray_and_get_normal(&pos, &ray, max_time_of_impact, solid)
827            .map(|inter| RayIntersection::from_rapier(inter, ray_origin, ray_dir))
828    }
829
830    /// Tests whether a ray intersects this transformed shape.
831    pub fn intersects_ray(
832        &self,
833        translation: Vect,
834        rotation: Rot,
835        ray_origin: Vect,
836        ray_dir: Vect,
837        max_time_of_impact: Real,
838    ) -> bool {
839        let pos = (translation, rotation).into();
840        let ray = Ray::new(ray_origin.into(), ray_dir.into());
841        self.raw.intersects_ray(&pos, &ray, max_time_of_impact)
842    }
843}
844
845impl Default for Collider {
846    fn default() -> Self {
847        Self::ball(0.5)
848    }
849}
850
851#[cfg(all(feature = "dim3", feature = "async-collider"))]
852#[allow(clippy::type_complexity)]
853fn extract_mesh_vertices_indices(mesh: &Mesh) -> Option<(Vec<na::Point3<Real>>, Vec<[u32; 3]>)> {
854    use rapier::na::point;
855
856    let vertices = mesh.attribute(Mesh::ATTRIBUTE_POSITION)?;
857    let indices = mesh.indices()?;
858
859    let vtx: Vec<_> = match vertices {
860        VertexAttributeValues::Float32(vtx) => Some(
861            vtx.chunks(3)
862                .map(|v| point![v[0] as Real, v[1] as Real, v[2] as Real])
863                .collect(),
864        ),
865        VertexAttributeValues::Float32x3(vtx) => Some(
866            vtx.iter()
867                .map(|v| point![v[0] as Real, v[1] as Real, v[2] as Real])
868                .collect(),
869        ),
870        _ => None,
871    }?;
872
873    let idx = match indices {
874        Indices::U16(idx) => idx
875            .chunks_exact(3)
876            .map(|i| [i[0] as u32, i[1] as u32, i[2] as u32])
877            .collect(),
878        Indices::U32(idx) => idx.chunks_exact(3).map(|i| [i[0], i[1], i[2]]).collect(),
879    };
880
881    Some((vtx, idx))
882}