Skip to main content

bevy_rapier2d/geometry/
collider_impl.rs

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