avian3d/collision/collider/parry/
mod.rs

1#![allow(clippy::unnecessary_cast)]
2
3pub mod contact_query;
4
5#[cfg(feature = "2d")]
6mod primitives2d;
7#[cfg(feature = "3d")]
8mod primitives3d;
9
10#[cfg(feature = "2d")]
11pub use primitives2d::{EllipseColliderShape, RegularPolygonColliderShape};
12
13use crate::{make_isometry, prelude::*};
14#[cfg(feature = "collider-from-mesh")]
15use bevy::render::mesh::{Indices, VertexAttributeValues};
16use bevy::{log, prelude::*};
17use contact_query::UnsupportedShape;
18use itertools::Either;
19use parry::shape::{RoundShape, SharedShape, TypedShape};
20
21impl<T: IntoCollider<Collider>> From<T> for Collider {
22    fn from(value: T) -> Self {
23        value.collider()
24    }
25}
26
27/// Parameters controlling the VHACD convex decomposition.
28///
29/// See <https://github.com/Unity-Technologies/VHACD#parameters> for details.
30#[derive(Clone, PartialEq, Debug, Reflect)]
31#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
32#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
33#[reflect(PartialEq, Debug)]
34pub struct VhacdParameters {
35    /// Maximum concavity.
36    ///
37    /// Default: 0.1 (in 2D), 0.01 (in 3D).
38    /// Valid range `[0.0, 1.0]`.
39    pub concavity: Scalar,
40    /// Controls the bias toward clipping along symmetry planes.
41    ///
42    /// Default: 0.05.
43    /// Valid Range: `[0.0, 1.0]`.
44    pub alpha: Scalar,
45    /// Controls the bias toward clipping along revolution planes.
46    ///
47    /// Default: 0.05.
48    /// Valid Range: `[0.0, 1.0]`.
49    pub beta: Scalar,
50    /// Resolution used during the voxelization stage.
51    ///
52    /// Default: 256 (in 2D), 64 (in 3D).
53    pub resolution: u32,
54    /// Controls the granularity of the search for the best
55    /// clipping plane during the decomposition.
56    ///
57    /// Default: 4
58    pub plane_downsampling: u32,
59    /// Controls the precision of the convex-hull generation
60    /// process during the clipping plane selection stage.
61    ///
62    /// Default: 4
63    pub convex_hull_downsampling: u32,
64    /// Controls the way the input mesh or polyline is being
65    /// voxelized.
66    ///
67    /// Default: `FillMode::FloodFill { detect_cavities: false, detect_self_intersections: false }`
68    pub fill_mode: FillMode,
69    /// Controls whether the convex-hull should be approximated during the decomposition stage.
70    /// Setting this to `true` increases performances with a slight degradation of the decomposition
71    /// quality.
72    ///
73    /// Default: true
74    pub convex_hull_approximation: bool,
75    /// Controls the max number of convex-hull generated by the convex decomposition.
76    ///
77    /// Default: 1024
78    pub max_convex_hulls: u32,
79}
80
81impl Default for VhacdParameters {
82    fn default() -> Self {
83        Self {
84            #[cfg(feature = "3d")]
85            resolution: 64,
86            #[cfg(feature = "3d")]
87            concavity: 0.01,
88            #[cfg(feature = "2d")]
89            resolution: 256,
90            #[cfg(feature = "2d")]
91            concavity: 0.1,
92            plane_downsampling: 4,
93            convex_hull_downsampling: 4,
94            alpha: 0.05,
95            beta: 0.05,
96            convex_hull_approximation: true,
97            max_convex_hulls: 1024,
98            fill_mode: FillMode::FloodFill {
99                detect_cavities: false,
100                #[cfg(feature = "2d")]
101                detect_self_intersections: false,
102            },
103        }
104    }
105}
106
107impl From<VhacdParameters> for parry::transformation::vhacd::VHACDParameters {
108    fn from(value: VhacdParameters) -> Self {
109        Self {
110            concavity: value.concavity,
111            alpha: value.alpha,
112            beta: value.beta,
113            resolution: value.resolution,
114            plane_downsampling: value.plane_downsampling,
115            convex_hull_downsampling: value.convex_hull_downsampling,
116            fill_mode: value.fill_mode.into(),
117            convex_hull_approximation: value.convex_hull_approximation,
118            max_convex_hulls: value.max_convex_hulls,
119        }
120    }
121}
122
123/// Controls how the voxelization determines which voxel needs
124/// to be considered empty, and which ones will be considered full.
125#[derive(Hash, Clone, Copy, PartialEq, Eq, Debug, Reflect)]
126#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
127#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
128#[reflect(Hash, PartialEq, Debug)]
129pub enum FillMode {
130    /// Only consider full the voxels intersecting the surface of the
131    /// shape being voxelized.
132    SurfaceOnly,
133    /// Use a flood-fill technique to consider fill the voxels intersecting
134    /// the surface of the shape being voxelized, as well as all the voxels
135    /// bounded of them.
136    FloodFill {
137        /// Detects holes inside of a solid contour.
138        detect_cavities: bool,
139        /// Attempts to properly handle self-intersections.
140        #[cfg(feature = "2d")]
141        detect_self_intersections: bool,
142    },
143}
144
145impl From<FillMode> for parry::transformation::voxelization::FillMode {
146    fn from(value: FillMode) -> Self {
147        match value {
148            FillMode::SurfaceOnly => Self::SurfaceOnly,
149            FillMode::FloodFill {
150                detect_cavities,
151                #[cfg(feature = "2d")]
152                detect_self_intersections,
153            } => Self::FloodFill {
154                detect_cavities,
155                #[cfg(feature = "2d")]
156                detect_self_intersections,
157            },
158        }
159    }
160}
161
162/// Flags used for the preprocessing of a triangle mesh collider.
163#[repr(transparent)]
164#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
165#[derive(Hash, Clone, Copy, PartialEq, Eq, Debug, Reflect)]
166#[reflect(opaque, Hash, PartialEq, Debug)]
167pub struct TrimeshFlags(u8);
168
169bitflags::bitflags! {
170    impl TrimeshFlags: u8 {
171        /// If set, the half-edge topology of the trimesh will be computed if possible.
172        const HALF_EDGE_TOPOLOGY = 0b0000_0001;
173        /// If set, the half-edge topology and connected components of the trimesh will be computed if possible.
174        ///
175        /// Because of the way it is currently implemented, connected components can only be computed on
176        /// a mesh where the half-edge topology computation succeeds. It will no longer be the case in the
177        /// future once we decouple the computations.
178        const CONNECTED_COMPONENTS = 0b0000_0010;
179        /// If set, any triangle that results in a failing half-hedge topology computation will be deleted.
180        const DELETE_BAD_TOPOLOGY_TRIANGLES = 0b0000_0100;
181        /// If set, the trimesh will be assumed to be oriented (with outward normals).
182        ///
183        /// The pseudo-normals of its vertices and edges will be computed.
184        const ORIENTED = 0b0000_1000;
185        /// If set, the duplicate vertices of the trimesh will be merged.
186        ///
187        /// Two vertices with the exact same coordinates will share the same entry on the
188        /// vertex buffer and the index buffer is adjusted accordingly.
189        const MERGE_DUPLICATE_VERTICES = 0b0001_0000;
190        /// If set, the triangles sharing two vertices with identical index values will be removed.
191        ///
192        /// Because of the way it is currently implemented, this methods implies that duplicate
193        /// vertices will be merged. It will no longer be the case in the future once we decouple
194        /// the computations.
195        const DELETE_DEGENERATE_TRIANGLES = 0b0010_0000;
196        /// If set, two triangles sharing three vertices with identical index values (in any order) will be removed.
197        ///
198        /// Because of the way it is currently implemented, this methods implies that duplicate
199        /// vertices will be merged. It will no longer be the case in the future once we decouple
200        /// the computations.
201        const DELETE_DUPLICATE_TRIANGLES = 0b0100_0000;
202        /// If set, a special treatment will be applied to contact manifold calculation to eliminate
203        /// or fix contacts normals that could lead to incorrect bumps in physics simulation
204        /// (especially on flat surfaces).
205        ///
206        /// This is achieved by taking into account adjacent triangle normals when computing contact
207        /// points for a given triangle.
208        const FIX_INTERNAL_EDGES = 0b1000_0000 | Self::ORIENTED.bits() | Self::MERGE_DUPLICATE_VERTICES.bits();
209    }
210}
211
212impl From<TrimeshFlags> for parry::shape::TriMeshFlags {
213    fn from(value: TrimeshFlags) -> Self {
214        Self::from_bits(value.bits().into()).unwrap()
215    }
216}
217
218/// A collider used for detecting collisions and generating contacts.
219///
220/// # Creation
221///
222/// `Collider` has tons of methods for creating colliders of various shapes:
223///
224/// ```
225#[cfg_attr(feature = "2d", doc = "# use avian2d::prelude::*;")]
226#[cfg_attr(feature = "3d", doc = "# use avian3d::prelude::*;")]
227/// # use bevy::prelude::*;
228/// #
229/// # fn setup(mut commands: Commands) {
230/// // Create a ball collider with a given radius
231#[cfg_attr(feature = "2d", doc = "commands.spawn(Collider::circle(0.5));")]
232#[cfg_attr(feature = "3d", doc = "commands.spawn(Collider::sphere(0.5));")]
233/// // Create a capsule collider with a given radius and height
234/// commands.spawn(Collider::capsule(0.5, 2.0));
235/// # }
236/// ```
237///
238/// Colliders on their own only detect contacts and generate
239/// [collision events](crate::collision#collision-events).
240/// To make colliders apply contact forces, they have to be attached
241/// to [rigid bodies](RigidBody):
242///
243/// ```
244#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
245#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
246/// use bevy::prelude::*;
247///
248/// // Spawn a dynamic body that falls onto a static platform
249/// fn setup(mut commands: Commands) {
250///     commands.spawn((
251///         RigidBody::Dynamic,
252#[cfg_attr(feature = "2d", doc = "        Collider::circle(0.5),")]
253#[cfg_attr(feature = "3d", doc = "        Collider::sphere(0.5),")]
254///         Transform::from_xyz(0.0, 2.0, 0.0),
255///     ));
256#[cfg_attr(
257    feature = "2d",
258    doc = "    commands.spawn((RigidBody::Static, Collider::rectangle(5.0, 0.5)));"
259)]
260#[cfg_attr(
261    feature = "3d",
262    doc = "    commands.spawn((RigidBody::Static, Collider::cuboid(5.0, 0.5, 5.0)));"
263)]
264/// }
265/// ```
266///
267/// Colliders can be further configured using various components like [`Friction`], [`Restitution`],
268/// [`Sensor`], [`CollisionLayers`], [`CollisionMargin`], and [`ColliderDensity`].
269///
270/// If you need to specify the shape of the collider statically, use [`ColliderConstructor`] and build your collider
271/// with the [`Collider::try_from_constructor`] method.
272/// This can also be done automatically by simply placing the [`ColliderConstructor`] on an entity.
273///
274#[cfg_attr(
275    feature = "3d",
276    doc = "Colliders can also be generated automatically for meshes and scenes. See [`ColliderConstructor`] and [`ColliderConstructorHierarchy`]."
277)]
278///
279/// ## Multiple Colliders
280///
281/// It can often be useful to attach multiple colliders to the same rigid body.
282///
283/// This can be done in two ways. Either use [`Collider::compound`] to have one collider that consists of many
284/// shapes, or for more control, spawn several collider entities as the children of a rigid body:
285///
286/// ```
287#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
288#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
289/// use bevy::prelude::*;
290///
291/// fn setup(mut commands: Commands) {
292///     // Spawn a rigid body with one collider on the same entity and two as children
293///     commands
294#[cfg_attr(
295    feature = "2d",
296    doc = "        .spawn((RigidBody::Dynamic, Collider::circle(0.5)))"
297)]
298#[cfg_attr(
299    feature = "3d",
300    doc = "        .spawn((RigidBody::Dynamic, Collider::sphere(0.5)))"
301)]
302///         .with_children(|children| {
303///             // Spawn the child colliders positioned relative to the rigid body
304#[cfg_attr(
305    feature = "2d",
306    doc = "            children.spawn((Collider::circle(0.5), Transform::from_xyz(2.0, 0.0, 0.0)));
307            children.spawn((Collider::circle(0.5), Transform::from_xyz(-2.0, 0.0, 0.0)));"
308)]
309#[cfg_attr(
310    feature = "3d",
311    doc = "            children.spawn((Collider::sphere(0.5), Transform::from_xyz(2.0, 0.0, 0.0)));
312            children.spawn((Collider::sphere(0.5), Transform::from_xyz(-2.0, 0.0, 0.0)));"
313)]
314///         });
315/// }
316/// ```
317///
318/// Colliders can be arbitrarily nested and transformed relative to the parent.
319/// The rigid body that a collider is attached to can be accessed using the [`ColliderOf`] component.
320///
321/// The benefit of using separate entities for the colliders is that each collider can have its own
322/// [friction](Friction), [restitution](Restitution), [collision layers](CollisionLayers),
323/// and other configuration options, and they send separate [collision events](crate::collision#collision-events).
324///
325/// # See More
326///
327/// - [Rigid bodies](RigidBody)
328/// - [Density](ColliderDensity)
329/// - [Friction] and [restitution](Restitution) (bounciness)
330/// - [Collision layers](CollisionLayers)
331/// - [Sensors](Sensor)
332/// - [Collision margins for adding extra thickness to colliders](CollisionMargin)
333#[cfg_attr(
334    feature = "3d",
335    doc = "- Generating colliders for meshes and scenes with [`ColliderConstructor`] and [`ColliderConstructorHierarchy`]"
336)]
337/// - [Get colliding entities](CollidingEntities)
338/// - [Collision events](crate::collision#collision-events)
339/// - [Accessing collision data](Collisions)
340/// - [Filtering and modifying contacts with hooks](CollisionHooks)
341/// - [Manual contact queries](contact_query)
342///
343/// # Advanced Usage
344///
345/// Internally, `Collider` uses the shapes provided by `parry`. If you want to create a collider
346/// using these shapes, you can simply use `Collider::from(SharedShape::some_method())`.
347///
348/// To get a reference to the internal [`SharedShape`], you can use the [`Collider::shape()`]
349/// or [`Collider::shape_scaled()`] methods.
350///
351/// `Collider` is currently not `Reflect`. If you need to reflect it, you can use [`ColliderConstructor`] as a workaround.
352#[derive(Clone, Component, Debug)]
353#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
354#[require(
355    ColliderMarker,
356    ColliderAabb,
357    CollisionLayers,
358    ColliderDensity,
359    ColliderMassProperties
360)]
361pub struct Collider {
362    /// The raw unscaled collider shape.
363    shape: SharedShape,
364    /// The scaled version of the collider shape.
365    ///
366    /// If the scale is `Vector::ONE`, this will be `None` and `unscaled_shape`
367    /// will be used instead.
368    scaled_shape: SharedShape,
369    /// The global scale used for the collider shape.
370    scale: Vector,
371}
372
373impl From<SharedShape> for Collider {
374    fn from(value: SharedShape) -> Self {
375        Self {
376            shape: value.clone(),
377            scaled_shape: value,
378            scale: Vector::ONE,
379        }
380    }
381}
382
383impl Default for Collider {
384    fn default() -> Self {
385        #[cfg(feature = "2d")]
386        {
387            Self::rectangle(0.5, 0.5)
388        }
389        #[cfg(feature = "3d")]
390        {
391            Self::cuboid(0.5, 0.5, 0.5)
392        }
393    }
394}
395
396impl AnyCollider for Collider {
397    type Context = ();
398
399    fn aabb_with_context(
400        &self,
401        position: Vector,
402        rotation: impl Into<Rotation>,
403        _: AabbContext<Self::Context>,
404    ) -> ColliderAabb {
405        let aabb = self
406            .shape_scaled()
407            .compute_aabb(&make_isometry(position, rotation));
408        ColliderAabb {
409            min: aabb.mins.into(),
410            max: aabb.maxs.into(),
411        }
412    }
413
414    fn contact_manifolds_with_context(
415        &self,
416        other: &Self,
417        position1: Vector,
418        rotation1: impl Into<Rotation>,
419        position2: Vector,
420        rotation2: impl Into<Rotation>,
421        prediction_distance: Scalar,
422        manifolds: &mut Vec<ContactManifold>,
423        _: ContactManifoldContext<Self::Context>,
424    ) {
425        contact_query::contact_manifolds(
426            self,
427            position1,
428            rotation1,
429            other,
430            position2,
431            rotation2,
432            prediction_distance,
433            manifolds,
434        )
435    }
436}
437
438// TODO: `bevy_heavy` supports computing the individual mass properties efficiently for Bevy's primitive shapes,
439//       but Parry doesn't support it for its own shapes, so we have to compute all mass properties in each method :(
440#[cfg(feature = "2d")]
441impl ComputeMassProperties for Collider {
442    fn mass(&self, density: f32) -> f32 {
443        let props = self.shape_scaled().mass_properties(density as Scalar);
444        props.mass() as f32
445    }
446
447    fn unit_angular_inertia(&self) -> f32 {
448        self.angular_inertia(1.0)
449    }
450
451    fn angular_inertia(&self, mass: f32) -> f32 {
452        let props = self.shape_scaled().mass_properties(mass as Scalar);
453        props.principal_inertia() as f32
454    }
455
456    fn center_of_mass(&self) -> Vec2 {
457        let props = self.shape_scaled().mass_properties(1.0);
458        Vector::from(props.local_com).f32()
459    }
460
461    fn mass_properties(&self, density: f32) -> MassProperties {
462        let props = self.shape_scaled().mass_properties(density as Scalar);
463
464        MassProperties {
465            mass: props.mass() as f32,
466            #[cfg(feature = "2d")]
467            angular_inertia: props.principal_inertia() as f32,
468            #[cfg(feature = "3d")]
469            principal_angular_inertia: Vector::from(props.principal_inertia()).f32(),
470            #[cfg(feature = "3d")]
471            local_inertial_frame: Quaternion::from(props.principal_inertia_local_frame).f32(),
472            center_of_mass: Vector::from(props.local_com).f32(),
473        }
474    }
475}
476
477#[cfg(feature = "3d")]
478impl ComputeMassProperties for Collider {
479    fn mass(&self, density: f32) -> f32 {
480        let props = self.shape_scaled().mass_properties(density as Scalar);
481        props.mass() as f32
482    }
483
484    fn unit_principal_angular_inertia(&self) -> Vec3 {
485        self.principal_angular_inertia(1.0)
486    }
487
488    fn principal_angular_inertia(&self, mass: f32) -> Vec3 {
489        let props = self.shape_scaled().mass_properties(mass as Scalar);
490        Vector::from(props.principal_inertia()).f32()
491    }
492
493    fn local_inertial_frame(&self) -> Quat {
494        let props = self.shape_scaled().mass_properties(1.0);
495        Quaternion::from(props.principal_inertia_local_frame).f32()
496    }
497
498    fn center_of_mass(&self) -> Vec3 {
499        let props = self.shape_scaled().mass_properties(1.0);
500        Vector::from(props.local_com).f32()
501    }
502
503    fn mass_properties(&self, density: f32) -> MassProperties {
504        let props = self.shape_scaled().mass_properties(density as Scalar);
505
506        MassProperties {
507            mass: props.mass() as f32,
508            #[cfg(feature = "2d")]
509            angular_inertia: props.principal_inertia() as f32,
510            #[cfg(feature = "3d")]
511            principal_angular_inertia: Vector::from(props.principal_inertia()).f32(),
512            #[cfg(feature = "3d")]
513            local_inertial_frame: Quaternion::from(props.principal_inertia_local_frame).f32(),
514            center_of_mass: Vector::from(props.local_com).f32(),
515        }
516    }
517}
518
519impl ScalableCollider for Collider {
520    fn scale(&self) -> Vector {
521        self.scale()
522    }
523
524    fn set_scale(&mut self, scale: Vector, detail: u32) {
525        self.set_scale(scale, detail)
526    }
527}
528
529impl Collider {
530    /// Returns the raw unscaled shape of the collider.
531    pub fn shape(&self) -> &SharedShape {
532        &self.shape
533    }
534
535    /// Returns the shape of the collider with the scale from its `GlobalTransform` applied.
536    pub fn shape_scaled(&self) -> &SharedShape {
537        &self.scaled_shape
538    }
539
540    /// Sets the unscaled shape of the collider. The collider's scale will be applied to this shape.
541    pub fn set_shape(&mut self, shape: SharedShape) {
542        self.shape = shape;
543
544        // TODO: The number of subdivisions probably shouldn't be hard-coded
545        if let Ok(scaled) = scale_shape(&self.shape, self.scale, 10) {
546            self.scaled_shape = scaled;
547        } else {
548            log::error!("Failed to create convex hull for scaled collider.");
549        }
550    }
551
552    /// Returns the global scale of the collider.
553    pub fn scale(&self) -> Vector {
554        self.scale
555    }
556
557    /// Set the global scaling factor of this shape.
558    ///
559    /// If the scaling factor is not uniform, and the scaled shape can’t be
560    /// represented as a supported shape, the shape is approximated as
561    /// a convex polygon or polyhedron using `num_subdivisions`.
562    ///
563    /// For example, if a ball was scaled to an ellipse, the new shape would be approximated.
564    pub fn set_scale(&mut self, scale: Vector, num_subdivisions: u32) {
565        if scale == self.scale {
566            return;
567        }
568
569        if scale == Vector::ONE {
570            // Trivial case.
571            self.scaled_shape = self.shape.clone();
572            self.scale = Vector::ONE;
573            return;
574        }
575
576        if let Ok(scaled) = scale_shape(&self.shape, scale, num_subdivisions) {
577            self.scaled_shape = scaled;
578            self.scale = scale;
579        } else {
580            log::error!("Failed to create convex hull for scaled collider.");
581        }
582    }
583
584    /// Projects the given `point` onto `self` transformed by `translation` and `rotation`.
585    /// The returned tuple contains the projected point and whether it is inside the collider.
586    ///
587    /// If `solid` is true and the given `point` is inside of the collider, the projection will be at the point.
588    /// Otherwise, the collider will be treated as hollow, and the projection will be at the collider's boundary.
589    pub fn project_point(
590        &self,
591        translation: impl Into<Position>,
592        rotation: impl Into<Rotation>,
593        point: Vector,
594        solid: bool,
595    ) -> (Vector, bool) {
596        let projection = self.shape_scaled().project_point(
597            &make_isometry(translation, rotation),
598            &point.into(),
599            solid,
600        );
601        (projection.point.into(), projection.is_inside)
602    }
603
604    /// Computes the minimum distance between the given `point` and `self` transformed by `translation` and `rotation`.
605    ///
606    /// If `solid` is true and the given `point` is inside of the collider, the returned distance will be `0.0`.
607    /// Otherwise, the collider will be treated as hollow, and the distance will be the distance
608    /// to the collider's boundary.
609    pub fn distance_to_point(
610        &self,
611        translation: impl Into<Position>,
612        rotation: impl Into<Rotation>,
613        point: Vector,
614        solid: bool,
615    ) -> Scalar {
616        self.shape_scaled().distance_to_point(
617            &make_isometry(translation, rotation),
618            &point.into(),
619            solid,
620        )
621    }
622
623    /// Tests whether the given `point` is inside of `self` transformed by `translation` and `rotation`.
624    pub fn contains_point(
625        &self,
626        translation: impl Into<Position>,
627        rotation: impl Into<Rotation>,
628        point: Vector,
629    ) -> bool {
630        self.shape_scaled()
631            .contains_point(&make_isometry(translation, rotation), &point.into())
632    }
633
634    /// Computes the distance and normal between the given ray and `self`
635    /// transformed by `translation` and `rotation`.
636    ///
637    /// The returned tuple is in the format `(distance, normal)`.
638    ///
639    /// # Arguments
640    ///
641    /// - `ray_origin`: Where the ray is cast from.
642    /// - `ray_direction`: What direction the ray is cast in.
643    /// - `max_distance`: The maximum distance the ray can travel.
644    /// - `solid`: If true and the ray origin is inside of a collider, the hit point will be the ray origin itself.
645    ///   Otherwise, the collider will be treated as hollow, and the hit point will be at the collider's boundary.
646    pub fn cast_ray(
647        &self,
648        translation: impl Into<Position>,
649        rotation: impl Into<Rotation>,
650        ray_origin: Vector,
651        ray_direction: Vector,
652        max_distance: Scalar,
653        solid: bool,
654    ) -> Option<(Scalar, Vector)> {
655        let hit = self.shape_scaled().cast_ray_and_get_normal(
656            &make_isometry(translation, rotation),
657            &parry::query::Ray::new(ray_origin.into(), ray_direction.into()),
658            max_distance,
659            solid,
660        );
661        hit.map(|hit| (hit.time_of_impact, hit.normal.into()))
662    }
663
664    /// Tests whether the given ray intersects `self` transformed by `translation` and `rotation`.
665    ///
666    /// # Arguments
667    ///
668    /// - `ray_origin`: Where the ray is cast from.
669    /// - `ray_direction`: What direction the ray is cast in.
670    /// - `max_distance`: The maximum distance the ray can travel.
671    pub fn intersects_ray(
672        &self,
673        translation: impl Into<Position>,
674        rotation: impl Into<Rotation>,
675        ray_origin: Vector,
676        ray_direction: Vector,
677        max_distance: Scalar,
678    ) -> bool {
679        self.shape_scaled().intersects_ray(
680            &make_isometry(translation, rotation),
681            &parry::query::Ray::new(ray_origin.into(), ray_direction.into()),
682            max_distance,
683        )
684    }
685
686    /// Creates a collider with a compound shape defined by a given vector of colliders with a position and a rotation.
687    ///
688    /// Especially for dynamic rigid bodies, compound shape colliders should be preferred over triangle meshes and polylines,
689    /// because convex shapes typically provide more reliable results.
690    ///
691    /// If you want to create a compound shape from a 3D triangle mesh or 2D polyline, consider using the
692    /// [`Collider::convex_decomposition`] method.
693    pub fn compound(
694        shapes: Vec<(
695            impl Into<Position>,
696            impl Into<Rotation>,
697            impl Into<Collider>,
698        )>,
699    ) -> Self {
700        let shapes = shapes
701            .into_iter()
702            .map(|(p, r, c)| {
703                (
704                    make_isometry(*p.into(), r.into()),
705                    c.into().shape_scaled().clone(),
706                )
707            })
708            .collect::<Vec<_>>();
709        SharedShape::compound(shapes).into()
710    }
711
712    /// Creates a collider with a circle shape defined by its radius.
713    #[cfg(feature = "2d")]
714    pub fn circle(radius: Scalar) -> Self {
715        SharedShape::ball(radius).into()
716    }
717
718    /// Creates a collider with a sphere shape defined by its radius.
719    #[cfg(feature = "3d")]
720    pub fn sphere(radius: Scalar) -> Self {
721        SharedShape::ball(radius).into()
722    }
723
724    /// Creates a collider with an ellipse shape defined by a half-width and half-height.
725    #[cfg(feature = "2d")]
726    pub fn ellipse(half_width: Scalar, half_height: Scalar) -> Self {
727        SharedShape::new(EllipseColliderShape(Ellipse::new(
728            half_width as f32,
729            half_height as f32,
730        )))
731        .into()
732    }
733
734    /// Creates a collider with a rectangle shape defined by its extents.
735    #[cfg(feature = "2d")]
736    pub fn rectangle(x_length: Scalar, y_length: Scalar) -> Self {
737        SharedShape::cuboid(x_length * 0.5, y_length * 0.5).into()
738    }
739
740    /// Creates a collider with a cuboid shape defined by its extents.
741    #[cfg(feature = "3d")]
742    pub fn cuboid(x_length: Scalar, y_length: Scalar, z_length: Scalar) -> Self {
743        SharedShape::cuboid(x_length * 0.5, y_length * 0.5, z_length * 0.5).into()
744    }
745
746    /// Creates a collider with a rectangle shape defined by its extents and rounded corners.
747    #[cfg(feature = "2d")]
748    pub fn round_rectangle(x_length: Scalar, y_length: Scalar, border_radius: Scalar) -> Self {
749        SharedShape::round_cuboid(x_length * 0.5, y_length * 0.5, border_radius).into()
750    }
751
752    /// Creates a collider with a cuboid shape defined by its extents and rounded corners.
753    #[cfg(feature = "3d")]
754    pub fn round_cuboid(
755        x_length: Scalar,
756        y_length: Scalar,
757        z_length: Scalar,
758        border_radius: Scalar,
759    ) -> Self {
760        SharedShape::round_cuboid(
761            x_length * 0.5,
762            y_length * 0.5,
763            z_length * 0.5,
764            border_radius,
765        )
766        .into()
767    }
768
769    /// Creates a collider with a cylinder shape defined by its radius
770    /// on the `XZ` plane and its height along the `Y` axis.
771    #[cfg(feature = "3d")]
772    pub fn cylinder(radius: Scalar, height: Scalar) -> Self {
773        SharedShape::cylinder(height * 0.5, radius).into()
774    }
775
776    /// Creates a collider with a cone shape defined by the radius of its base
777    /// on the `XZ` plane and its height along the `Y` axis.
778    #[cfg(feature = "3d")]
779    pub fn cone(radius: Scalar, height: Scalar) -> Self {
780        SharedShape::cone(height * 0.5, radius).into()
781    }
782
783    /// Creates a collider with a capsule shape defined by its radius
784    /// and its height along the `Y` axis, excluding the hemispheres.
785    pub fn capsule(radius: Scalar, length: Scalar) -> Self {
786        SharedShape::capsule(
787            (Vector::Y * length * 0.5).into(),
788            (Vector::NEG_Y * length * 0.5).into(),
789            radius,
790        )
791        .into()
792    }
793
794    /// Creates a collider with a capsule shape defined by its radius and endpoints `a` and `b`.
795    pub fn capsule_endpoints(radius: Scalar, a: Vector, b: Vector) -> Self {
796        SharedShape::capsule(a.into(), b.into(), radius).into()
797    }
798
799    /// Creates a collider with a [half-space](https://en.wikipedia.org/wiki/Half-space_(geometry)) shape
800    /// defined by the outward normal of its planar boundary.
801    pub fn half_space(outward_normal: Vector) -> Self {
802        SharedShape::halfspace(nalgebra::Unit::new_normalize(outward_normal.into())).into()
803    }
804
805    /// Creates a collider with a segment shape defined by its endpoints `a` and `b`.
806    pub fn segment(a: Vector, b: Vector) -> Self {
807        SharedShape::segment(a.into(), b.into()).into()
808    }
809
810    /// Creates a collider with a triangle shape defined by its points `a`, `b`, and `c`.
811    ///
812    /// If the triangle is oriented clockwise, it will be reversed to be counterclockwise
813    /// by swapping `b` and `c`. This is needed for collision detection.
814    ///
815    /// If you know that the given points produce a counterclockwise triangle,
816    /// consider using [`Collider::triangle_unchecked`] instead.
817    #[cfg(feature = "2d")]
818    pub fn triangle(a: Vector, b: Vector, c: Vector) -> Self {
819        let mut triangle = parry::shape::Triangle::new(a.into(), b.into(), c.into());
820
821        // Make sure the triangle is counterclockwise. This is needed for collision detection.
822        if triangle.orientation(1e-8) == parry::shape::TriangleOrientation::Clockwise {
823            triangle.reverse();
824        }
825
826        SharedShape::new(triangle).into()
827    }
828
829    /// Creates a collider with a triangle shape defined by its points `a`, `b`, and `c`.
830    ///
831    /// The orientation of the triangle is assumed to be counterclockwise.
832    /// This is needed for collision detection.
833    ///
834    /// If you are unsure about the orientation of the triangle, consider using [`Collider::triangle`] instead.
835    #[cfg(feature = "2d")]
836    pub fn triangle_unchecked(a: Vector, b: Vector, c: Vector) -> Self {
837        SharedShape::triangle(a.into(), b.into(), c.into()).into()
838    }
839
840    /// Creates a collider with a triangle shape defined by its points `a`, `b`, and `c`.
841    #[cfg(feature = "3d")]
842    pub fn triangle(a: Vector, b: Vector, c: Vector) -> Self {
843        SharedShape::triangle(a.into(), b.into(), c.into()).into()
844    }
845
846    /// Creates a collider with a regular polygon shape defined by the circumradius and the number of sides.
847    #[cfg(feature = "2d")]
848    pub fn regular_polygon(circumradius: f32, sides: u32) -> Self {
849        RegularPolygon::new(circumradius, sides).collider()
850    }
851
852    /// Creates a collider with a polyline shape defined by its vertices and optionally an index buffer.
853    pub fn polyline(vertices: Vec<Vector>, indices: Option<Vec<[u32; 2]>>) -> Self {
854        let vertices = vertices.into_iter().map(|v| v.into()).collect();
855        SharedShape::polyline(vertices, indices).into()
856    }
857
858    /// Creates a collider with a triangle mesh shape defined by its vertex and index buffers.
859    ///
860    /// Note that the resulting collider will be hollow and have no interior. This makes it more prone to tunneling and other collision issues.
861    ///
862    /// The [`CollisionMargin`] component can be used to add thickness to the shape if needed.
863    /// For thin shapes like triangle meshes, it can help improve collision stability and performance.
864    pub fn trimesh(vertices: Vec<Vector>, indices: Vec<[u32; 3]>) -> Self {
865        let vertices = vertices.into_iter().map(|v| v.into()).collect();
866        SharedShape::trimesh(vertices, indices).into()
867    }
868
869    /// Creates a collider with a triangle mesh shape defined by its vertex and index buffers
870    /// and flags controlling the preprocessing.
871    ///
872    /// Note that the resulting collider will be hollow and have no interior. This makes it more prone to tunneling and other collision issues.
873    ///
874    /// The [`CollisionMargin`] component can be used to add thickness to the shape if needed.
875    /// For thin shapes like triangle meshes, it can help improve collision stability and performance.
876    pub fn trimesh_with_config(
877        vertices: Vec<Vector>,
878        indices: Vec<[u32; 3]>,
879        flags: TrimeshFlags,
880    ) -> Self {
881        let vertices = vertices.into_iter().map(|v| v.into()).collect();
882        SharedShape::trimesh_with_flags(vertices, indices, flags.into()).into()
883    }
884
885    /// Creates a collider shape with a compound shape obtained from the decomposition of a given polyline
886    /// defined by its vertex and index buffers.
887    #[cfg(feature = "2d")]
888    pub fn convex_decomposition(vertices: Vec<Vector>, indices: Vec<[u32; 2]>) -> Self {
889        let vertices = vertices.iter().map(|v| (*v).into()).collect::<Vec<_>>();
890        SharedShape::convex_decomposition(&vertices, &indices).into()
891    }
892
893    /// Creates a collider shape with a compound shape obtained from the decomposition of a given trimesh
894    /// defined by its vertex and index buffers.
895    #[cfg(feature = "3d")]
896    pub fn convex_decomposition(vertices: Vec<Vector>, indices: Vec<[u32; 3]>) -> Self {
897        let vertices = vertices.iter().map(|v| (*v).into()).collect::<Vec<_>>();
898        SharedShape::convex_decomposition(&vertices, &indices).into()
899    }
900
901    /// Creates a collider shape with a compound shape obtained from the decomposition of a given polyline
902    /// defined by its vertex and index buffers. The given [`VhacdParameters`] are used for configuring
903    /// the decomposition process.
904    #[cfg(feature = "2d")]
905    pub fn convex_decomposition_with_config(
906        vertices: Vec<Vector>,
907        indices: Vec<[u32; 2]>,
908        params: &VhacdParameters,
909    ) -> Self {
910        let vertices = vertices.iter().map(|v| (*v).into()).collect::<Vec<_>>();
911        SharedShape::convex_decomposition_with_params(&vertices, &indices, &params.clone().into())
912            .into()
913    }
914
915    /// Creates a collider shape with a compound shape obtained from the decomposition of a given trimesh
916    /// defined by its vertex and index buffers. The given [`VhacdParameters`] are used for configuring
917    /// the decomposition process.
918    #[cfg(feature = "3d")]
919    pub fn convex_decomposition_with_config(
920        vertices: Vec<Vector>,
921        indices: Vec<[u32; 3]>,
922        params: VhacdParameters,
923    ) -> Self {
924        let vertices = vertices.iter().map(|v| (*v).into()).collect::<Vec<_>>();
925        SharedShape::convex_decomposition_with_params(&vertices, &indices, &params.clone().into())
926            .into()
927    }
928
929    /// Creates a collider with a [convex polygon](https://en.wikipedia.org/wiki/Convex_polygon) shape obtained after computing
930    /// the [convex hull](https://en.wikipedia.org/wiki/Convex_hull) of the given points.
931    #[cfg(feature = "2d")]
932    pub fn convex_hull(points: Vec<Vector>) -> Option<Self> {
933        let points = points.iter().map(|v| (*v).into()).collect::<Vec<_>>();
934        SharedShape::convex_hull(&points).map(Into::into)
935    }
936
937    /// Creates a collider with a [convex polyhedron](https://en.wikipedia.org/wiki/Convex_polytope) shape obtained after computing
938    /// the [convex hull](https://en.wikipedia.org/wiki/Convex_hull) of the given points.
939    #[cfg(feature = "3d")]
940    pub fn convex_hull(points: Vec<Vector>) -> Option<Self> {
941        let points = points.iter().map(|v| (*v).into()).collect::<Vec<_>>();
942        SharedShape::convex_hull(&points).map(Into::into)
943    }
944
945    /// Creates a collider with a heightfield shape.
946    ///
947    /// A 2D heightfield is a segment along the `X` axis, subdivided at regular intervals.
948    ///
949    /// `heights` is a list indicating the altitude of each subdivision point, and `scale` controls
950    /// the scaling factor along each axis.
951    #[cfg(feature = "2d")]
952    pub fn heightfield(heights: Vec<Scalar>, scale: Vector) -> Self {
953        SharedShape::heightfield(heights.into(), scale.into()).into()
954    }
955
956    /// Creates a collider with a heightfield shape.
957    ///
958    /// A 3D heightfield is a rectangle on the `XZ` plane, subdivided in a grid pattern at regular intervals.
959    ///
960    /// `heights` is a matrix indicating the altitude of each subdivision point. The number of rows indicates
961    /// the number of subdivisions along the `X` axis, while the number of columns indicates the number of
962    /// subdivisions along the `Z` axis.
963    ///
964    /// `scale` controls the scaling factor along each axis.
965    #[cfg(feature = "3d")]
966    pub fn heightfield(heights: Vec<Vec<Scalar>>, scale: Vector) -> Self {
967        let row_count = heights.len();
968        let column_count = heights[0].len();
969        let data: Vec<Scalar> = heights.into_iter().flatten().collect();
970
971        assert_eq!(
972            data.len(),
973            row_count * column_count,
974            "Each row in `heights` must have the same amount of points"
975        );
976
977        let heights = nalgebra::DMatrix::from_vec(row_count, column_count, data);
978        SharedShape::heightfield(heights, scale.into()).into()
979    }
980
981    /// Creates a collider with a triangle mesh shape from a `Mesh`.
982    ///
983    /// Note that the resulting collider will be hollow and have no interior. This makes it more prone to tunneling and other collision issues.
984    ///
985    /// The [`CollisionMargin`] component can be used to add thickness to the shape if needed.
986    /// For thin shapes like triangle meshes, it can help improve collision stability and performance.
987    ///
988    /// # Example
989    ///
990    /// ```
991    /// use avian3d::prelude::*;
992    /// use bevy::prelude::*;
993    ///
994    /// fn setup(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>) {
995    ///     let mesh = Mesh::from(Cuboid::default());
996    ///     commands.spawn((
997    ///         Collider::trimesh_from_mesh(&mesh).unwrap(),
998    ///         Mesh3d(meshes.add(mesh)),
999    ///     ));
1000    /// }
1001    /// ```
1002    #[cfg(feature = "collider-from-mesh")]
1003    pub fn trimesh_from_mesh(mesh: &Mesh) -> Option<Self> {
1004        extract_mesh_vertices_indices(mesh).map(|(vertices, indices)| {
1005            SharedShape::trimesh_with_flags(
1006                vertices,
1007                indices,
1008                TrimeshFlags::MERGE_DUPLICATE_VERTICES.into(),
1009            )
1010            .into()
1011        })
1012    }
1013
1014    /// Creates a collider with a triangle mesh shape from a `Mesh` using the given [`TrimeshFlags`]
1015    /// for controlling the preprocessing.
1016    ///
1017    /// Note that the resulting collider will be hollow and have no interior. This makes it more prone to tunneling and other collision issues.
1018    ///
1019    /// The [`CollisionMargin`] component can be used to add thickness to the shape if needed.
1020    /// For thin shapes like triangle meshes, it can help improve collision stability and performance.
1021    ///
1022    /// # Example
1023    ///
1024    /// ```
1025    /// use avian3d::prelude::*;
1026    /// use bevy::prelude::*;
1027    ///
1028    /// fn setup(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>) {
1029    ///     let mesh = Mesh::from(Cuboid::default());
1030    ///     commands.spawn((
1031    ///         Collider::trimesh_from_mesh_with_config(&mesh, TrimeshFlags::all()).unwrap(),
1032    ///         Mesh3d(meshes.add(mesh)),
1033    ///     ));
1034    /// }
1035    /// ```
1036    #[cfg(feature = "collider-from-mesh")]
1037    pub fn trimesh_from_mesh_with_config(mesh: &Mesh, flags: TrimeshFlags) -> Option<Self> {
1038        extract_mesh_vertices_indices(mesh).map(|(vertices, indices)| {
1039            SharedShape::trimesh_with_flags(vertices, indices, flags.into()).into()
1040        })
1041    }
1042
1043    /// Creates a collider with a convex polygon shape obtained from the convex hull of a `Mesh`.
1044    ///
1045    /// # Example
1046    ///
1047    /// ```
1048    /// use avian3d::prelude::*;
1049    /// use bevy::prelude::*;
1050    ///
1051    /// fn setup(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>) {
1052    ///     let mesh = Mesh::from(Cuboid::default());
1053    ///     commands.spawn((
1054    ///         Collider::convex_hull_from_mesh(&mesh).unwrap(),
1055    ///         Mesh3d(meshes.add(mesh)),
1056    ///     ));
1057    /// }
1058    /// ```
1059    #[cfg(feature = "collider-from-mesh")]
1060    pub fn convex_hull_from_mesh(mesh: &Mesh) -> Option<Self> {
1061        extract_mesh_vertices_indices(mesh)
1062            .and_then(|(vertices, _)| SharedShape::convex_hull(&vertices).map(|shape| shape.into()))
1063    }
1064
1065    /// Creates a compound shape obtained from the decomposition of a `Mesh`.
1066    ///
1067    /// # Example
1068    ///
1069    /// ```
1070    /// use avian3d::prelude::*;
1071    /// use bevy::prelude::*;
1072    ///
1073    /// fn setup(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>) {
1074    ///     let mesh = Mesh::from(Cuboid::default());
1075    ///     commands.spawn((
1076    ///         Collider::convex_decomposition_from_mesh(&mesh).unwrap(),
1077    ///         Mesh3d(meshes.add(mesh)),
1078    ///     ));
1079    /// }
1080    /// ```
1081    #[cfg(feature = "collider-from-mesh")]
1082    pub fn convex_decomposition_from_mesh(mesh: &Mesh) -> Option<Self> {
1083        extract_mesh_vertices_indices(mesh).map(|(vertices, indices)| {
1084            SharedShape::convex_decomposition(&vertices, &indices).into()
1085        })
1086    }
1087
1088    /// Creates a compound shape obtained from the decomposition of a `Mesh`
1089    /// with the given [`VhacdParameters`] passed to the decomposition algorithm.
1090    ///
1091    /// # Example
1092    ///
1093    /// ```
1094    /// use avian3d::prelude::*;
1095    /// use bevy::prelude::*;
1096    ///
1097    /// fn setup(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>) {
1098    ///     let mesh = Mesh::from(Cuboid::default());
1099    ///     let config = VhacdParameters {
1100    ///         convex_hull_approximation: false,
1101    ///         ..default()
1102    ///     };
1103    ///     commands.spawn((
1104    ///         Collider::convex_decomposition_from_mesh_with_config(&mesh, &config).unwrap(),
1105    ///         Mesh3d(meshes.add(mesh)),
1106    ///     ));
1107    /// }
1108    /// ```
1109    #[cfg(feature = "collider-from-mesh")]
1110    pub fn convex_decomposition_from_mesh_with_config(
1111        mesh: &Mesh,
1112        parameters: &VhacdParameters,
1113    ) -> Option<Self> {
1114        extract_mesh_vertices_indices(mesh).map(|(vertices, indices)| {
1115            SharedShape::convex_decomposition_with_params(
1116                &vertices,
1117                &indices,
1118                &parameters.clone().into(),
1119            )
1120            .into()
1121        })
1122    }
1123
1124    /// Attempts to create a collider with the given [`ColliderConstructor`].
1125    /// By using this, you can serialize and deserialize the collider's creation method
1126    /// separately from the collider itself via the [`ColliderConstructor`] enum.
1127    ///
1128    #[cfg_attr(
1129        feature = "collider-from-mesh",
1130        doc = "Returns `None` in the following cases:
1131- The given [`ColliderConstructor`] requires a mesh, but none was provided.
1132- Creating the collider from the given [`ColliderConstructor`] failed."
1133    )]
1134    #[cfg_attr(
1135        not(feature = "collider-from-mesh"),
1136        doc = "Returns `None` if creating the collider from the given [`ColliderConstructor`] failed."
1137    )]
1138    pub fn try_from_constructor(
1139        collider_constructor: ColliderConstructor,
1140        #[cfg(feature = "collider-from-mesh")] mesh: Option<&Mesh>,
1141    ) -> Option<Self> {
1142        match collider_constructor {
1143            #[cfg(feature = "2d")]
1144            ColliderConstructor::Circle { radius } => Some(Self::circle(radius)),
1145            #[cfg(feature = "3d")]
1146            ColliderConstructor::Sphere { radius } => Some(Self::sphere(radius)),
1147            #[cfg(feature = "2d")]
1148            ColliderConstructor::Ellipse {
1149                half_width,
1150                half_height,
1151            } => Some(Self::ellipse(half_width, half_height)),
1152            #[cfg(feature = "2d")]
1153            ColliderConstructor::Rectangle { x_length, y_length } => {
1154                Some(Self::rectangle(x_length, y_length))
1155            }
1156            #[cfg(feature = "3d")]
1157            ColliderConstructor::Cuboid {
1158                x_length,
1159                y_length,
1160                z_length,
1161            } => Some(Self::cuboid(x_length, y_length, z_length)),
1162            #[cfg(feature = "2d")]
1163            ColliderConstructor::RoundRectangle {
1164                x_length,
1165                y_length,
1166                border_radius,
1167            } => Some(Self::round_rectangle(x_length, y_length, border_radius)),
1168            #[cfg(feature = "3d")]
1169            ColliderConstructor::RoundCuboid {
1170                x_length,
1171                y_length,
1172                z_length,
1173                border_radius,
1174            } => Some(Self::round_cuboid(
1175                x_length,
1176                y_length,
1177                z_length,
1178                border_radius,
1179            )),
1180            #[cfg(feature = "3d")]
1181            ColliderConstructor::Cylinder { radius, height } => {
1182                Some(Self::cylinder(radius, height))
1183            }
1184            #[cfg(feature = "3d")]
1185            ColliderConstructor::Cone { radius, height } => Some(Self::cone(radius, height)),
1186            ColliderConstructor::Capsule { radius, height } => Some(Self::capsule(radius, height)),
1187            ColliderConstructor::CapsuleEndpoints { radius, a, b } => {
1188                Some(Self::capsule_endpoints(radius, a, b))
1189            }
1190            ColliderConstructor::HalfSpace { outward_normal } => {
1191                Some(Self::half_space(outward_normal))
1192            }
1193            ColliderConstructor::Segment { a, b } => Some(Self::segment(a, b)),
1194            ColliderConstructor::Triangle { a, b, c } => Some(Self::triangle(a, b, c)),
1195            #[cfg(feature = "2d")]
1196            ColliderConstructor::RegularPolygon {
1197                circumradius,
1198                sides,
1199            } => Some(Self::regular_polygon(circumradius, sides)),
1200            ColliderConstructor::Polyline { vertices, indices } => {
1201                Some(Self::polyline(vertices, indices))
1202            }
1203            ColliderConstructor::Trimesh { vertices, indices } => {
1204                Some(Self::trimesh(vertices, indices))
1205            }
1206            ColliderConstructor::TrimeshWithConfig {
1207                vertices,
1208                indices,
1209                flags,
1210            } => Some(Self::trimesh_with_config(vertices, indices, flags)),
1211            #[cfg(feature = "2d")]
1212            ColliderConstructor::ConvexDecomposition { vertices, indices } => {
1213                Some(Self::convex_decomposition(vertices, indices))
1214            }
1215            #[cfg(feature = "3d")]
1216            ColliderConstructor::ConvexDecomposition { vertices, indices } => {
1217                Some(Self::convex_decomposition(vertices, indices))
1218            }
1219            #[cfg(feature = "2d")]
1220            ColliderConstructor::ConvexDecompositionWithConfig {
1221                vertices,
1222                indices,
1223                params,
1224            } => Some(Self::convex_decomposition_with_config(
1225                vertices, indices, &params,
1226            )),
1227            #[cfg(feature = "3d")]
1228            ColliderConstructor::ConvexDecompositionWithConfig {
1229                vertices,
1230                indices,
1231                params,
1232            } => Some(Self::convex_decomposition_with_config(
1233                vertices, indices, params,
1234            )),
1235            #[cfg(feature = "2d")]
1236            ColliderConstructor::ConvexHull { points } => Self::convex_hull(points),
1237            #[cfg(feature = "3d")]
1238            ColliderConstructor::ConvexHull { points } => Self::convex_hull(points),
1239            #[cfg(feature = "2d")]
1240            ColliderConstructor::Heightfield { heights, scale } => {
1241                Some(Self::heightfield(heights, scale))
1242            }
1243            #[cfg(feature = "3d")]
1244            ColliderConstructor::Heightfield { heights, scale } => {
1245                Some(Self::heightfield(heights, scale))
1246            }
1247            #[cfg(feature = "collider-from-mesh")]
1248            ColliderConstructor::TrimeshFromMesh => Self::trimesh_from_mesh(mesh?),
1249            #[cfg(all(feature = "collider-from-mesh", feature = "default-collider"))]
1250            ColliderConstructor::TrimeshFromMeshWithConfig(flags) => {
1251                Self::trimesh_from_mesh_with_config(mesh?, flags)
1252            }
1253            #[cfg(feature = "collider-from-mesh")]
1254            ColliderConstructor::ConvexDecompositionFromMesh => {
1255                Self::convex_decomposition_from_mesh(mesh?)
1256            }
1257            #[cfg(all(feature = "collider-from-mesh", feature = "default-collider"))]
1258            ColliderConstructor::ConvexDecompositionFromMeshWithConfig(params) => {
1259                Self::convex_decomposition_from_mesh_with_config(mesh?, &params)
1260            }
1261            #[cfg(feature = "collider-from-mesh")]
1262            ColliderConstructor::ConvexHullFromMesh => Self::convex_hull_from_mesh(mesh?),
1263        }
1264    }
1265}
1266
1267#[cfg(feature = "collider-from-mesh")]
1268type VerticesIndices = (Vec<nalgebra::Point3<Scalar>>, Vec<[u32; 3]>);
1269
1270#[cfg(feature = "collider-from-mesh")]
1271fn extract_mesh_vertices_indices(mesh: &Mesh) -> Option<VerticesIndices> {
1272    let vertices = mesh.attribute(Mesh::ATTRIBUTE_POSITION)?;
1273    let indices = mesh.indices()?;
1274
1275    let vtx: Vec<_> = match vertices {
1276        VertexAttributeValues::Float32(vtx) => Some(
1277            vtx.chunks(3)
1278                .map(|v| [v[0] as Scalar, v[1] as Scalar, v[2] as Scalar].into())
1279                .collect(),
1280        ),
1281        VertexAttributeValues::Float32x3(vtx) => Some(
1282            vtx.iter()
1283                .map(|v| [v[0] as Scalar, v[1] as Scalar, v[2] as Scalar].into())
1284                .collect(),
1285        ),
1286        _ => None,
1287    }?;
1288
1289    let idx = match indices {
1290        Indices::U16(idx) => idx
1291            .chunks_exact(3)
1292            .map(|i| [i[0] as u32, i[1] as u32, i[2] as u32])
1293            .collect(),
1294        Indices::U32(idx) => idx.chunks_exact(3).map(|i| [i[0], i[1], i[2]]).collect(),
1295    };
1296
1297    Some((vtx, idx))
1298}
1299
1300fn scale_shape(
1301    shape: &SharedShape,
1302    scale: Vector,
1303    num_subdivisions: u32,
1304) -> Result<SharedShape, UnsupportedShape> {
1305    let scale = scale.abs();
1306    match shape.as_typed_shape() {
1307        TypedShape::Cuboid(s) => Ok(SharedShape::new(s.scaled(&scale.abs().into()))),
1308        TypedShape::RoundCuboid(s) => Ok(SharedShape::new(RoundShape {
1309            border_radius: s.border_radius,
1310            inner_shape: s.inner_shape.scaled(&scale.abs().into()),
1311        })),
1312        TypedShape::Capsule(c) => match c.scaled(&scale.abs().into(), num_subdivisions) {
1313            None => {
1314                log::error!("Failed to apply scale {} to Capsule shape.", scale);
1315                Ok(SharedShape::ball(0.0))
1316            }
1317            Some(Either::Left(b)) => Ok(SharedShape::new(b)),
1318            Some(Either::Right(b)) => Ok(SharedShape::new(b)),
1319        },
1320        TypedShape::Ball(b) => {
1321            #[cfg(feature = "2d")]
1322            {
1323                if scale.x == scale.y {
1324                    Ok(SharedShape::ball(b.radius * scale.x.abs()))
1325                } else {
1326                    // A 2D circle becomes an ellipse when scaled non-uniformly.
1327                    Ok(SharedShape::new(EllipseColliderShape(Ellipse {
1328                        half_size: Vec2::splat(b.radius as f32) * scale.f32().abs(),
1329                    })))
1330                }
1331            }
1332            #[cfg(feature = "3d")]
1333            match b.scaled(&scale.abs().into(), num_subdivisions) {
1334                None => {
1335                    log::error!("Failed to apply scale {} to Ball shape.", scale);
1336                    Ok(SharedShape::ball(0.0))
1337                }
1338                Some(Either::Left(b)) => Ok(SharedShape::new(b)),
1339                Some(Either::Right(b)) => Ok(SharedShape::new(b)),
1340            }
1341        }
1342        TypedShape::Segment(s) => Ok(SharedShape::new(s.scaled(&scale.into()))),
1343        TypedShape::Triangle(t) => Ok(SharedShape::new(t.scaled(&scale.into()))),
1344        TypedShape::RoundTriangle(t) => Ok(SharedShape::new(RoundShape {
1345            border_radius: t.border_radius,
1346            inner_shape: t.inner_shape.scaled(&scale.into()),
1347        })),
1348        TypedShape::TriMesh(t) => Ok(SharedShape::new(t.clone().scaled(&scale.into()))),
1349        TypedShape::Polyline(p) => Ok(SharedShape::new(p.clone().scaled(&scale.into()))),
1350        TypedShape::HalfSpace(h) => match h.scaled(&scale.into()) {
1351            None => {
1352                log::error!("Failed to apply scale {} to HalfSpace shape.", scale);
1353                Ok(SharedShape::ball(0.0))
1354            }
1355            Some(scaled) => Ok(SharedShape::new(scaled)),
1356        },
1357        TypedShape::HeightField(h) => Ok(SharedShape::new(h.clone().scaled(&scale.into()))),
1358        #[cfg(feature = "2d")]
1359        TypedShape::ConvexPolygon(cp) => match cp.clone().scaled(&scale.into()) {
1360            None => {
1361                log::error!("Failed to apply scale {} to ConvexPolygon shape.", scale);
1362                Ok(SharedShape::ball(0.0))
1363            }
1364            Some(scaled) => Ok(SharedShape::new(scaled)),
1365        },
1366        #[cfg(feature = "2d")]
1367        TypedShape::RoundConvexPolygon(cp) => match cp.inner_shape.clone().scaled(&scale.into()) {
1368            None => {
1369                log::error!(
1370                    "Failed to apply scale {} to RoundConvexPolygon shape.",
1371                    scale
1372                );
1373                Ok(SharedShape::ball(0.0))
1374            }
1375            Some(scaled) => Ok(SharedShape::new(RoundShape {
1376                border_radius: cp.border_radius,
1377                inner_shape: scaled,
1378            })),
1379        },
1380        #[cfg(feature = "3d")]
1381        TypedShape::ConvexPolyhedron(cp) => match cp.clone().scaled(&scale.into()) {
1382            None => {
1383                log::error!("Failed to apply scale {} to ConvexPolyhedron shape.", scale);
1384                Ok(SharedShape::ball(0.0))
1385            }
1386            Some(scaled) => Ok(SharedShape::new(scaled)),
1387        },
1388        #[cfg(feature = "3d")]
1389        TypedShape::RoundConvexPolyhedron(cp) => {
1390            match cp.clone().inner_shape.scaled(&scale.into()) {
1391                None => {
1392                    log::error!(
1393                        "Failed to apply scale {} to RoundConvexPolyhedron shape.",
1394                        scale
1395                    );
1396                    Ok(SharedShape::ball(0.0))
1397                }
1398                Some(scaled) => Ok(SharedShape::new(RoundShape {
1399                    border_radius: cp.border_radius,
1400                    inner_shape: scaled,
1401                })),
1402            }
1403        }
1404        #[cfg(feature = "3d")]
1405        TypedShape::Cylinder(c) => match c.scaled(&scale.abs().into(), num_subdivisions) {
1406            None => {
1407                log::error!("Failed to apply scale {} to Cylinder shape.", scale);
1408                Ok(SharedShape::ball(0.0))
1409            }
1410            Some(Either::Left(b)) => Ok(SharedShape::new(b)),
1411            Some(Either::Right(b)) => Ok(SharedShape::new(b)),
1412        },
1413        #[cfg(feature = "3d")]
1414        TypedShape::RoundCylinder(c) => {
1415            match c.inner_shape.scaled(&scale.abs().into(), num_subdivisions) {
1416                None => {
1417                    log::error!("Failed to apply scale {} to RoundCylinder shape.", scale);
1418                    Ok(SharedShape::ball(0.0))
1419                }
1420                Some(Either::Left(scaled)) => Ok(SharedShape::new(RoundShape {
1421                    border_radius: c.border_radius,
1422                    inner_shape: scaled,
1423                })),
1424                Some(Either::Right(scaled)) => Ok(SharedShape::new(RoundShape {
1425                    border_radius: c.border_radius,
1426                    inner_shape: scaled,
1427                })),
1428            }
1429        }
1430        #[cfg(feature = "3d")]
1431        TypedShape::Cone(c) => match c.scaled(&scale.into(), num_subdivisions) {
1432            None => {
1433                log::error!("Failed to apply scale {} to Cone shape.", scale);
1434                Ok(SharedShape::ball(0.0))
1435            }
1436            Some(Either::Left(b)) => Ok(SharedShape::new(b)),
1437            Some(Either::Right(b)) => Ok(SharedShape::new(b)),
1438        },
1439        #[cfg(feature = "3d")]
1440        TypedShape::RoundCone(c) => match c.inner_shape.scaled(&scale.into(), num_subdivisions) {
1441            None => {
1442                log::error!("Failed to apply scale {} to RoundCone shape.", scale);
1443                Ok(SharedShape::ball(0.0))
1444            }
1445            Some(Either::Left(scaled)) => Ok(SharedShape::new(RoundShape {
1446                border_radius: c.border_radius,
1447                inner_shape: scaled,
1448            })),
1449            Some(Either::Right(scaled)) => Ok(SharedShape::new(RoundShape {
1450                border_radius: c.border_radius,
1451                inner_shape: scaled,
1452            })),
1453        },
1454        TypedShape::Compound(c) => {
1455            let mut scaled = Vec::with_capacity(c.shapes().len());
1456
1457            for (iso, shape) in c.shapes() {
1458                scaled.push((
1459                    #[cfg(feature = "2d")]
1460                    make_isometry(
1461                        Vector::from(iso.translation) * scale,
1462                        Rotation::radians(iso.rotation.angle()),
1463                    ),
1464                    #[cfg(feature = "3d")]
1465                    make_isometry(
1466                        Vector::from(iso.translation) * scale,
1467                        Quaternion::from(iso.rotation),
1468                    ),
1469                    scale_shape(shape, scale, num_subdivisions)?,
1470                ));
1471            }
1472            Ok(SharedShape::compound(scaled))
1473        }
1474        TypedShape::Custom(_shape) => {
1475            #[cfg(feature = "2d")]
1476            {
1477                if let Some(ellipse) = _shape.as_shape::<EllipseColliderShape>() {
1478                    return Ok(SharedShape::new(EllipseColliderShape(Ellipse {
1479                        half_size: ellipse.half_size * scale.f32().abs(),
1480                    })));
1481                }
1482                if let Some(polygon) = _shape.as_shape::<RegularPolygonColliderShape>() {
1483                    if scale.x == scale.y {
1484                        return Ok(SharedShape::new(RegularPolygonColliderShape(
1485                            RegularPolygon::new(
1486                                polygon.circumradius() * scale.x.abs() as f32,
1487                                polygon.sides,
1488                            ),
1489                        )));
1490                    } else {
1491                        let vertices = polygon
1492                            .vertices(0.0)
1493                            .into_iter()
1494                            .map(|v| v.adjust_precision().into())
1495                            .collect::<Vec<_>>();
1496
1497                        return scale_shape(
1498                            &SharedShape::convex_hull(&vertices).unwrap(),
1499                            scale,
1500                            num_subdivisions,
1501                        );
1502                    }
1503                }
1504            }
1505            Err(parry::query::Unsupported)
1506        }
1507    }
1508}