#[cfg(feature = "dim2")]
use na::DVector;
#[cfg(all(feature = "dim3", feature = "async-collider"))]
use {
bevy::prelude::*,
bevy::render::mesh::{Indices, VertexAttributeValues},
};
use rapier::prelude::{FeatureId, Point, Ray, SharedShape, Vector, DIM};
use super::{get_snapped_scale, shape_views::*};
#[cfg(all(feature = "dim3", feature = "async-collider"))]
use crate::geometry::ComputedColliderShape;
use crate::geometry::{Collider, PointProjection, RayIntersection, TriMeshFlags, VHACDParameters};
use crate::math::{Real, Rot, Vect};
impl Collider {
pub fn scale(&self) -> Vect {
self.scale
}
pub fn promote_scaled_shape(&mut self) {
self.unscaled = self.raw.clone();
self.scale = Vect::ONE;
}
pub fn compound(shapes: Vec<(Vect, Rot, Collider)>) -> Self {
let shapes = shapes
.into_iter()
.map(|(t, r, s)| ((t, r).into(), s.raw))
.collect();
SharedShape::compound(shapes).into()
}
pub fn ball(radius: Real) -> Self {
SharedShape::ball(radius).into()
}
pub fn halfspace(outward_normal: Vect) -> Option<Self> {
use rapier::na::Unit;
let normal = Vector::from(outward_normal);
Unit::try_new(normal, 1.0e-6).map(|n| SharedShape::halfspace(n).into())
}
#[cfg(feature = "dim3")]
pub fn cylinder(half_height: Real, radius: Real) -> Self {
SharedShape::cylinder(half_height, radius).into()
}
#[cfg(feature = "dim3")]
pub fn round_cylinder(half_height: Real, radius: Real, border_radius: Real) -> Self {
SharedShape::round_cylinder(half_height, radius, border_radius).into()
}
#[cfg(feature = "dim3")]
pub fn cone(half_height: Real, radius: Real) -> Self {
SharedShape::cone(half_height, radius).into()
}
#[cfg(feature = "dim3")]
pub fn round_cone(half_height: Real, radius: Real, border_radius: Real) -> Self {
SharedShape::round_cone(half_height, radius, border_radius).into()
}
#[cfg(feature = "dim2")]
pub fn cuboid(hx: Real, hy: Real) -> Self {
SharedShape::cuboid(hx, hy).into()
}
#[cfg(feature = "dim2")]
pub fn round_cuboid(hx: Real, hy: Real, border_radius: Real) -> Self {
SharedShape::round_cuboid(hx, hy, border_radius).into()
}
pub fn capsule(a: Vect, b: Vect, radius: Real) -> Self {
SharedShape::capsule(a.into(), b.into(), radius).into()
}
pub fn capsule_x(half_height: Real, radius: Real) -> Self {
let p = Point::from(Vector::x() * half_height);
SharedShape::capsule(-p, p, radius).into()
}
pub fn capsule_y(half_height: Real, radius: Real) -> Self {
let p = Point::from(Vector::y() * half_height);
SharedShape::capsule(-p, p, radius).into()
}
#[cfg(feature = "dim3")]
pub fn capsule_z(half_height: Real, radius: Real) -> Self {
let p = Point::from(Vector::z() * half_height);
SharedShape::capsule(-p, p, radius).into()
}
#[cfg(feature = "dim3")]
pub fn cuboid(hx: Real, hy: Real, hz: Real) -> Self {
SharedShape::cuboid(hx, hy, hz).into()
}
#[cfg(feature = "dim3")]
pub fn round_cuboid(hx: Real, hy: Real, hz: Real, border_radius: Real) -> Self {
SharedShape::round_cuboid(hx, hy, hz, border_radius).into()
}
pub fn segment(a: Vect, b: Vect) -> Self {
SharedShape::segment(a.into(), b.into()).into()
}
pub fn triangle(a: Vect, b: Vect, c: Vect) -> Self {
SharedShape::triangle(a.into(), b.into(), c.into()).into()
}
pub fn round_triangle(a: Vect, b: Vect, c: Vect, border_radius: Real) -> Self {
SharedShape::round_triangle(a.into(), b.into(), c.into(), border_radius).into()
}
pub fn polyline(vertices: Vec<Vect>, indices: Option<Vec<[u32; 2]>>) -> Self {
let vertices = vertices.into_iter().map(|v| v.into()).collect();
SharedShape::polyline(vertices, indices).into()
}
pub fn trimesh(vertices: Vec<Vect>, indices: Vec<[u32; 3]>) -> Self {
let vertices = vertices.into_iter().map(|v| v.into()).collect();
SharedShape::trimesh(vertices, indices).into()
}
pub fn trimesh_with_flags(
vertices: Vec<Vect>,
indices: Vec<[u32; 3]>,
flags: TriMeshFlags,
) -> Self {
let vertices = vertices.into_iter().map(|v| v.into()).collect();
SharedShape::trimesh_with_flags(vertices, indices, flags).into()
}
#[cfg(all(feature = "dim3", feature = "async-collider"))]
pub fn from_bevy_mesh(mesh: &Mesh, collider_shape: &ComputedColliderShape) -> Option<Self> {
let vertices_indices = extract_mesh_vertices_indices(mesh);
match collider_shape {
ComputedColliderShape::TriMesh => vertices_indices.map(|(vtx, idx)| {
SharedShape::trimesh_with_flags(vtx, idx, TriMeshFlags::MERGE_DUPLICATE_VERTICES)
.into()
}),
ComputedColliderShape::ConvexDecomposition(params) => {
vertices_indices.map(|(vtx, idx)| {
SharedShape::convex_decomposition_with_params(&vtx, &idx, params).into()
})
}
}
}
pub fn convex_decomposition(vertices: &[Vect], indices: &[[u32; DIM]]) -> Self {
let vertices: Vec<_> = vertices.iter().map(|v| (*v).into()).collect();
SharedShape::convex_decomposition(&vertices, indices).into()
}
pub fn round_convex_decomposition(
vertices: &[Vect],
indices: &[[u32; DIM]],
border_radius: Real,
) -> Self {
let vertices: Vec<_> = vertices.iter().map(|v| (*v).into()).collect();
SharedShape::round_convex_decomposition(&vertices, indices, border_radius).into()
}
pub fn convex_decomposition_with_params(
vertices: &[Vect],
indices: &[[u32; DIM]],
params: &VHACDParameters,
) -> Self {
let vertices: Vec<_> = vertices.iter().map(|v| (*v).into()).collect();
SharedShape::convex_decomposition_with_params(&vertices, indices, params).into()
}
pub fn round_convex_decomposition_with_params(
vertices: &[Vect],
indices: &[[u32; DIM]],
params: &VHACDParameters,
border_radius: Real,
) -> Self {
let vertices: Vec<_> = vertices.iter().map(|v| (*v).into()).collect();
SharedShape::round_convex_decomposition_with_params(
&vertices,
indices,
params,
border_radius,
)
.into()
}
pub fn convex_hull(points: &[Vect]) -> Option<Self> {
let points: Vec<_> = points.iter().map(|v| (*v).into()).collect();
SharedShape::convex_hull(&points).map(Into::into)
}
pub fn round_convex_hull(points: &[Vect], border_radius: Real) -> Option<Self> {
let points: Vec<_> = points.iter().map(|v| (*v).into()).collect();
SharedShape::round_convex_hull(&points, border_radius).map(Into::into)
}
#[cfg(feature = "dim2")]
pub fn convex_polyline(points: Vec<Vect>) -> Option<Self> {
let points = points.into_iter().map(|v| v.into()).collect();
SharedShape::convex_polyline(points).map(Into::into)
}
#[cfg(feature = "dim2")]
pub fn round_convex_polyline(points: Vec<Vect>, border_radius: Real) -> Option<Self> {
let points = points.into_iter().map(|v| v.into()).collect();
SharedShape::round_convex_polyline(points, border_radius).map(Into::into)
}
#[cfg(feature = "dim3")]
pub fn convex_mesh(points: Vec<Vect>, indices: &[[u32; 3]]) -> Option<Self> {
let points = points.into_iter().map(|v| v.into()).collect();
SharedShape::convex_mesh(points, indices).map(Into::into)
}
#[cfg(feature = "dim3")]
pub fn round_convex_mesh(
points: Vec<Vect>,
indices: &[[u32; 3]],
border_radius: Real,
) -> Option<Self> {
let points = points.into_iter().map(|v| v.into()).collect();
SharedShape::round_convex_mesh(points, indices, border_radius).map(Into::into)
}
#[cfg(feature = "dim2")]
pub fn heightfield(heights: Vec<Real>, scale: Vect) -> Self {
SharedShape::heightfield(DVector::from_vec(heights), scale.into()).into()
}
#[cfg(feature = "dim3")]
pub fn heightfield(heights: Vec<Real>, num_rows: usize, num_cols: usize, scale: Vect) -> Self {
assert_eq!(
heights.len(),
num_rows * num_cols,
"Invalid number of heights provided."
);
let heights = rapier::na::DMatrix::from_vec(num_rows, num_cols, heights);
SharedShape::heightfield(heights, scale.into()).into()
}
pub fn as_typed_shape(&self) -> ColliderView {
self.raw.as_typed_shape().into()
}
pub fn as_unscaled_typed_shape(&self) -> ColliderView {
self.unscaled.as_typed_shape().into()
}
pub fn as_ball(&self) -> Option<BallView> {
self.raw.as_ball().map(|s| BallView { raw: s })
}
pub fn as_cuboid(&self) -> Option<CuboidView> {
self.raw.as_cuboid().map(|s| CuboidView { raw: s })
}
pub fn as_capsule(&self) -> Option<CapsuleView> {
self.raw.as_capsule().map(|s| CapsuleView { raw: s })
}
pub fn as_segment(&self) -> Option<SegmentView> {
self.raw.as_segment().map(|s| SegmentView { raw: s })
}
pub fn as_triangle(&self) -> Option<TriangleView> {
self.raw.as_triangle().map(|s| TriangleView { raw: s })
}
pub fn as_trimesh(&self) -> Option<TriMeshView> {
self.raw.as_trimesh().map(|s| TriMeshView { raw: s })
}
pub fn as_polyline(&self) -> Option<PolylineView> {
self.raw.as_polyline().map(|s| PolylineView { raw: s })
}
pub fn as_halfspace(&self) -> Option<HalfSpaceView> {
self.raw.as_halfspace().map(|s| HalfSpaceView { raw: s })
}
pub fn as_heightfield(&self) -> Option<HeightFieldView> {
self.raw
.as_heightfield()
.map(|s| HeightFieldView { raw: s })
}
pub fn as_compound(&self) -> Option<CompoundView> {
self.raw.as_compound().map(|s| CompoundView { raw: s })
}
#[cfg(feature = "dim2")]
pub fn as_convex_polygon(&self) -> Option<ConvexPolygonView> {
self.raw
.as_convex_polygon()
.map(|s| ConvexPolygonView { raw: s })
}
#[cfg(feature = "dim3")]
pub fn as_convex_polyhedron(&self) -> Option<ConvexPolyhedronView> {
self.raw
.as_convex_polyhedron()
.map(|s| ConvexPolyhedronView { raw: s })
}
#[cfg(feature = "dim3")]
pub fn as_cylinder(&self) -> Option<CylinderView> {
self.raw.as_cylinder().map(|s| CylinderView { raw: s })
}
#[cfg(feature = "dim3")]
pub fn as_cone(&self) -> Option<ConeView> {
self.raw.as_cone().map(|s| ConeView { raw: s })
}
pub fn as_ball_mut(&mut self) -> Option<BallViewMut> {
self.raw
.make_mut()
.as_ball_mut()
.map(|s| BallViewMut { raw: s })
}
pub fn as_cuboid_mut(&mut self) -> Option<CuboidViewMut> {
self.raw
.make_mut()
.as_cuboid_mut()
.map(|s| CuboidViewMut { raw: s })
}
pub fn as_capsule_mut(&mut self) -> Option<CapsuleViewMut> {
self.raw
.make_mut()
.as_capsule_mut()
.map(|s| CapsuleViewMut { raw: s })
}
pub fn as_segment_mut(&mut self) -> Option<SegmentViewMut> {
self.raw
.make_mut()
.as_segment_mut()
.map(|s| SegmentViewMut { raw: s })
}
pub fn as_triangle_mut(&mut self) -> Option<TriangleViewMut> {
self.raw
.make_mut()
.as_triangle_mut()
.map(|s| TriangleViewMut { raw: s })
}
pub fn as_trimesh_mut(&mut self) -> Option<TriMeshViewMut> {
self.raw
.make_mut()
.as_trimesh_mut()
.map(|s| TriMeshViewMut { raw: s })
}
pub fn as_polyline_mut(&mut self) -> Option<PolylineViewMut> {
self.raw
.make_mut()
.as_polyline_mut()
.map(|s| PolylineViewMut { raw: s })
}
pub fn as_halfspace_mut(&mut self) -> Option<HalfSpaceViewMut> {
self.raw
.make_mut()
.as_halfspace_mut()
.map(|s| HalfSpaceViewMut { raw: s })
}
pub fn as_heightfield_mut(&mut self) -> Option<HeightFieldViewMut> {
self.raw
.make_mut()
.as_heightfield_mut()
.map(|s| HeightFieldViewMut { raw: s })
}
#[cfg(feature = "dim3")]
pub fn as_cylinder_mut(&mut self) -> Option<CylinderViewMut> {
self.raw
.make_mut()
.as_cylinder_mut()
.map(|s| CylinderViewMut { raw: s })
}
#[cfg(feature = "dim3")]
pub fn as_cone_mut(&mut self) -> Option<ConeViewMut> {
self.raw
.make_mut()
.as_cone_mut()
.map(|s| ConeViewMut { raw: s })
}
pub fn set_scale(&mut self, scale: Vect, num_subdivisions: u32) {
let scale = get_snapped_scale(scale);
if scale == self.scale {
return;
}
if scale == Vect::ONE {
self.raw = self.unscaled.clone();
self.scale = Vect::ONE;
return;
}
if let Some(scaled) = self
.as_unscaled_typed_shape()
.raw_scale_by(scale, num_subdivisions)
{
self.raw = scaled;
self.scale = scale;
} else {
log::error!("Failed to create the scaled convex hull geometry.");
}
}
pub fn project_local_point_with_max_dist(
&self,
pt: Vect,
solid: bool,
max_dist: Real,
) -> Option<PointProjection> {
self.raw
.project_local_point_with_max_dist(&pt.into(), solid, max_dist)
.map(Into::into)
}
pub fn project_point_with_max_dist(
&self,
translation: Vect,
rotation: Rot,
pt: Vect,
solid: bool,
max_dist: Real,
) -> Option<PointProjection> {
let pos = (translation, rotation).into();
self.raw
.project_point_with_max_dist(&pos, &pt.into(), solid, max_dist)
.map(Into::into)
}
pub fn project_local_point(&self, pt: Vect, solid: bool) -> PointProjection {
self.raw.project_local_point(&pt.into(), solid).into()
}
pub fn project_local_point_and_get_feature(&self, pt: Vect) -> (PointProjection, FeatureId) {
let (proj, feat) = self.raw.project_local_point_and_get_feature(&pt.into());
(proj.into(), feat)
}
pub fn distance_to_local_point(&self, pt: Vect, solid: bool) -> Real {
self.raw.distance_to_local_point(&pt.into(), solid)
}
pub fn contains_local_point(&self, pt: Vect) -> bool {
self.raw.contains_local_point(&pt.into())
}
pub fn project_point(
&self,
translation: Vect,
rotation: Rot,
pt: Vect,
solid: bool,
) -> PointProjection {
let pos = (translation, rotation).into();
self.raw.project_point(&pos, &pt.into(), solid).into()
}
#[inline]
pub fn distance_to_point(
&self,
translation: Vect,
rotation: Rot,
pt: Vect,
solid: bool,
) -> Real {
let pos = (translation, rotation).into();
self.raw.distance_to_point(&pos, &pt.into(), solid)
}
pub fn project_point_and_get_feature(
&self,
translation: Vect,
rotation: Rot,
pt: Vect,
) -> (PointProjection, FeatureId) {
let pos = (translation, rotation).into();
let (proj, feat) = self.raw.project_point_and_get_feature(&pos, &pt.into());
(proj.into(), feat)
}
pub fn contains_point(&self, translation: Vect, rotation: Rot, pt: Vect) -> bool {
let pos = (translation, rotation).into();
self.raw.contains_point(&pos, &pt.into())
}
pub fn cast_local_ray(
&self,
ray_origin: Vect,
ray_dir: Vect,
max_toi: Real,
solid: bool,
) -> Option<Real> {
let ray = Ray::new(ray_origin.into(), ray_dir.into());
self.raw.cast_local_ray(&ray, max_toi, solid)
}
pub fn cast_local_ray_and_get_normal(
&self,
ray_origin: Vect,
ray_dir: Vect,
max_toi: Real,
solid: bool,
) -> Option<RayIntersection> {
let ray = Ray::new(ray_origin.into(), ray_dir.into());
self.raw
.cast_local_ray_and_get_normal(&ray, max_toi, solid)
.map(|inter| RayIntersection::from_rapier(inter, ray_origin, ray_dir))
}
pub fn intersects_local_ray(&self, ray_origin: Vect, ray_dir: Vect, max_toi: Real) -> bool {
let ray = Ray::new(ray_origin.into(), ray_dir.into());
self.raw.intersects_local_ray(&ray, max_toi)
}
pub fn cast_ray(
&self,
translation: Vect,
rotation: Rot,
ray_origin: Vect,
ray_dir: Vect,
max_toi: Real,
solid: bool,
) -> Option<Real> {
let pos = (translation, rotation).into();
let ray = Ray::new(ray_origin.into(), ray_dir.into());
self.raw.cast_ray(&pos, &ray, max_toi, solid)
}
pub fn cast_ray_and_get_normal(
&self,
translation: Vect,
rotation: Rot,
ray_origin: Vect,
ray_dir: Vect,
max_toi: Real,
solid: bool,
) -> Option<RayIntersection> {
let pos = (translation, rotation).into();
let ray = Ray::new(ray_origin.into(), ray_dir.into());
self.raw
.cast_ray_and_get_normal(&pos, &ray, max_toi, solid)
.map(|inter| RayIntersection::from_rapier(inter, ray_origin, ray_dir))
}
pub fn intersects_ray(
&self,
translation: Vect,
rotation: Rot,
ray_origin: Vect,
ray_dir: Vect,
max_toi: Real,
) -> bool {
let pos = (translation, rotation).into();
let ray = Ray::new(ray_origin.into(), ray_dir.into());
self.raw.intersects_ray(&pos, &ray, max_toi)
}
}
impl Default for Collider {
fn default() -> Self {
Self::ball(0.5)
}
}
#[cfg(all(feature = "dim3", feature = "async-collider"))]
#[allow(clippy::type_complexity)]
fn extract_mesh_vertices_indices(mesh: &Mesh) -> Option<(Vec<na::Point3<Real>>, Vec<[u32; 3]>)> {
use rapier::na::point;
let vertices = mesh.attribute(Mesh::ATTRIBUTE_POSITION)?;
let indices = mesh.indices()?;
let vtx: Vec<_> = match vertices {
VertexAttributeValues::Float32(vtx) => Some(
vtx.chunks(3)
.map(|v| point![v[0] as Real, v[1] as Real, v[2] as Real])
.collect(),
),
VertexAttributeValues::Float32x3(vtx) => Some(
vtx.iter()
.map(|v| point![v[0] as Real, v[1] as Real, v[2] as Real])
.collect(),
),
_ => None,
}?;
let idx = match indices {
Indices::U16(idx) => idx
.chunks_exact(3)
.map(|i| [i[0] as u32, i[1] as u32, i[2] as u32])
.collect(),
Indices::U32(idx) => idx.chunks_exact(3).map(|i| [i[0], i[1], i[2]]).collect(),
};
Some((vtx, idx))
}