aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/dynamics/rigid_body.rs18
-rw-r--r--src/geometry/collider.rs49
-rw-r--r--src/geometry/mod.rs6
-rw-r--r--src/geometry/waabb.rs93
-rw-r--r--src/lib.rs9
-rw-r--r--src/pipeline/mod.rs2
-rw-r--r--src/pipeline/query_pipeline.rs306
7 files changed, 473 insertions, 10 deletions
diff --git a/src/dynamics/rigid_body.rs b/src/dynamics/rigid_body.rs
index a2fcacc..d32ea46 100644
--- a/src/dynamics/rigid_body.rs
+++ b/src/dynamics/rigid_body.rs
@@ -137,6 +137,15 @@ impl RigidBody {
crate::utils::inv(self.mass_properties.inv_mass)
}
+ /// The predicted position of this rigid-body.
+ ///
+ /// If this rigid-body is kinematic this value is set by the `set_next_kinematic_position`
+ /// method and is used for estimating the kinematic body velocity at the next timestep.
+ /// For non-kinematic bodies, this value is currently unspecified.
+ pub fn predicted_position(&self) -> &Isometry<f32> {
+ &self.predicted_position
+ }
+
/// Adds a collider to this rigid-body.
pub(crate) fn add_collider_internal(&mut self, handle: ColliderHandle, coll: &Collider) {
let mass_properties = coll
@@ -201,12 +210,17 @@ impl RigidBody {
self.position = self.integrate_velocity(dt) * self.position;
}
- /// Sets the position of this rigid body.
+ /// Sets the position and `next_kinematic_position` of this rigid body.
+ ///
+ /// This will teleport the rigid-body to the specified position/orientation,
+ /// completely ignoring any physics rule. If this body is kinematic, this will
+ /// also set the next kinematic position to the same value, effectively
+ /// resetting to zero the next interpolated velocity of the kinematic body.
pub fn set_position(&mut self, pos: Isometry<f32>) {
self.position = pos;
// TODO: update the predicted position for dynamic bodies too?
- if self.is_static() {
+ if self.is_static() || self.is_kinematic() {
self.predicted_position = pos;
}
}
diff --git a/src/geometry/collider.rs b/src/geometry/collider.rs
index 2d55857..183446b 100644
--- a/src/geometry/collider.rs
+++ b/src/geometry/collider.rs
@@ -1,11 +1,12 @@
use crate::dynamics::{MassProperties, RigidBodyHandle, RigidBodySet};
use crate::geometry::{
Ball, Capsule, ColliderGraphIndex, Contact, Cuboid, HeightField, InteractionGraph, Polygon,
- Proximity, Triangle, Trimesh,
+ Proximity, Ray, RayIntersection, Triangle, Trimesh,
};
use crate::math::{AngVector, Isometry, Point, Rotation, Vector};
use na::Point3;
use ncollide::bounding_volume::{HasBoundingVolume, AABB};
+use ncollide::query::RayCast;
use num::Zero;
#[derive(Clone)]
@@ -97,6 +98,46 @@ impl Shape {
Shape::HeightField(heightfield) => heightfield.bounding_volume(position),
}
}
+
+ /// Computes the first intersection point between a ray in this collider.
+ ///
+ /// Some shapes are not supported yet and will always return `None`.
+ ///
+ /// # Parameters
+ /// - `position`: the position of this shape.
+ /// - `ray`: the ray to cast.
+ /// - `max_toi`: the maximum time-of-impact that can be reported by this cast. This effectively
+ /// limits the length of the ray to `ray.dir.norm() * max_toi`. Use `f32::MAX` for an unbounded ray.
+ pub fn cast_ray(
+ &self,
+ position: &Isometry<f32>,
+ ray: &Ray,
+ max_toi: f32,
+ ) -> Option<RayIntersection> {
+ match self {
+ Shape::Ball(ball) => ball.toi_and_normal_with_ray(position, ray, max_toi, true),
+ Shape::Polygon(_poly) => None,
+ Shape::Capsule(caps) => {
+ let pos = position * caps.transform_wrt_y();
+ let caps = ncollide::shape::Capsule::new(caps.half_height(), caps.radius);
+ caps.toi_and_normal_with_ray(&pos, ray, max_toi, true)
+ }
+ Shape::Cuboid(cuboid) => cuboid.toi_and_normal_with_ray(position, ray, max_toi, true),
+ #[cfg(feature = "dim2")]
+ Shape::Triangle(triangle) => {
+ // This is not implemented yet in 2D.
+ None
+ }
+ #[cfg(feature = "dim3")]
+ Shape::Triangle(triangle) => {
+ triangle.toi_and_normal_with_ray(position, ray, max_toi, true)
+ }
+ Shape::Trimesh(_trimesh) => None,
+ Shape::HeightField(heightfield) => {
+ heightfield.toi_and_normal_with_ray(position, ray, max_toi, true)
+ }
+ }
+ }
}
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
@@ -353,6 +394,12 @@ impl ColliderBuilder {
self
}
+ /// Sets the restitution coefficient of the collider this builder will build.
+ pub fn restitution(mut self, restitution: f32) -> Self {
+ self.restitution = restitution;
+ self
+ }
+
/// Sets the density of the collider this builder will build.
pub fn density(mut self, density: f32) -> Self {
self.density = Some(density);
diff --git a/src/geometry/mod.rs b/src/geometry/mod.rs
index 4f72778..5fcdf71 100644
--- a/src/geometry/mod.rs
+++ b/src/geometry/mod.rs
@@ -36,6 +36,10 @@ pub type AABB = ncollide::bounding_volume::AABB<f32>;
pub type ContactEvent = ncollide::pipeline::ContactEvent<ColliderHandle>;
/// Event triggered when a sensor collider starts or stop being in proximity with another collider (sensor or not).
pub type ProximityEvent = ncollide::pipeline::ProximityEvent<ColliderHandle>;
+/// A ray that can be cast against colliders.
+pub type Ray = ncollide::query::Ray<f32>;
+/// The intersection between a ray and a collider.
+pub type RayIntersection = ncollide::query::RayIntersection<f32>;
#[cfg(feature = "simd-is-enabled")]
pub(crate) use self::ball::WBall;
@@ -48,7 +52,6 @@ pub(crate) use self::contact_generator::{clip_segments, clip_segments_with_norma
pub(crate) use self::narrow_phase::ContactManifoldIndex;
#[cfg(feature = "dim3")]
pub(crate) use self::polyhedron_feature3d::PolyhedronFace;
-#[cfg(feature = "simd-is-enabled")]
pub(crate) use self::waabb::WAABB;
//pub(crate) use self::z_order::z_cmp_floats;
@@ -75,6 +78,5 @@ mod proximity_detector;
pub(crate) mod sat;
pub(crate) mod triangle;
mod trimesh;
-#[cfg(feature = "simd-is-enabled")]
mod waabb;
//mod z_order;
diff --git a/src/geometry/waabb.rs b/src/geometry/waabb.rs
index c3853bc..702b5aa 100644
--- a/src/geometry/waabb.rs
+++ b/src/geometry/waabb.rs
@@ -1,15 +1,27 @@
#[cfg(feature = "serde-serialize")]
use crate::math::DIM;
-use crate::math::{Point, SimdBool, SimdFloat, SIMD_WIDTH};
+use crate::math::{Point, SIMD_WIDTH};
use ncollide::bounding_volume::AABB;
-use simba::simd::{SimdPartialOrd, SimdValue};
+#[cfg(feature = "simd-is-enabled")]
+use {
+ crate::math::{SimdBool, SimdFloat},
+ simba::simd::{SimdPartialOrd, SimdValue},
+};
#[derive(Debug, Copy, Clone)]
+#[cfg(feature = "simd-is-enabled")]
pub(crate) struct WAABB {
pub mins: Point<SimdFloat>,
pub maxs: Point<SimdFloat>,
}
+#[derive(Debug, Copy, Clone)]
+#[cfg(not(feature = "simd-is-enabled"))]
+pub(crate) struct WAABB {
+ pub mins: [Point<f32>; SIMD_WIDTH],
+ pub maxs: [Point<f32>; SIMD_WIDTH],
+}
+
#[cfg(feature = "serde-serialize")]
impl serde::Serialize for WAABB {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@@ -18,16 +30,24 @@ impl serde::Serialize for WAABB {
{
use serde::ser::SerializeStruct;
+ #[cfg(feature = "simd-is-enabled")]
let mins: Point<[f32; SIMD_WIDTH]> = Point::from(
self.mins
.coords
.map(|e| array![|ii| e.extract(ii); SIMD_WIDTH]),
);
+ #[cfg(feature = "simd-is-enabled")]
let maxs: Point<[f32; SIMD_WIDTH]> = Point::from(
self.maxs
.coords
.map(|e| array![|ii| e.extract(ii); SIMD_WIDTH]),
);
+
+ #[cfg(not(feature = "simd-is-enabled"))]
+ let mins = self.mins;
+ #[cfg(not(feature = "simd-is-enabled"))]
+ let maxs = self.maxs;
+
let mut waabb = serializer.serialize_struct("WAABB", 2)?;
waabb.serialize_field("mins", &mins)?;
waabb.serialize_field("maxs", &maxs)?;
@@ -52,6 +72,7 @@ impl<'de> serde::Deserialize<'de> for WAABB {
)
}
+ #[cfg(feature = "simd-is-enabled")]
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
@@ -66,17 +87,36 @@ impl<'de> serde::Deserialize<'de> for WAABB {
let maxs = Point::from(maxs.coords.map(|e| SimdFloat::from(e)));
Ok(WAABB { mins, maxs })
}
+
+ #[cfg(not(feature = "simd-is-enabled"))]
+ fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
+ where
+ A: serde::de::SeqAccess<'de>,
+ {
+ let mins = seq
+ .next_element()?
+ .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
+ let maxs = seq
+ .next_element()?
+ .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
+ Ok(WAABB { mins, maxs })
+ }
}
deserializer.deserialize_struct("WAABB", &["mins", "maxs"], Visitor {})
}
}
+#[cfg(feature = "simd-is-enabled")]
impl WAABB {
pub fn new(mins: Point<SimdFloat>, maxs: Point<SimdFloat>) -> Self {
Self { mins, maxs }
}
+ pub fn new_invalid() -> Self {
+ Self::splat(AABB::new_invalid())
+ }
+
pub fn splat(aabb: AABB<f32>) -> Self {
Self {
mins: Point::splat(aabb.mins),
@@ -103,6 +143,7 @@ impl WAABB {
}
}
+#[cfg(feature = "simd-is-enabled")]
impl From<[AABB<f32>; SIMD_WIDTH]> for WAABB {
fn from(aabbs: [AABB<f32>; SIMD_WIDTH]) -> Self {
let mins = array![|ii| aabbs[ii].mins; SIMD_WIDTH];
@@ -114,3 +155,51 @@ impl From<[AABB<f32>; SIMD_WIDTH]> for WAABB {
}
}
}
+
+#[cfg(not(feature = "simd-is-enabled"))]
+impl WAABB {
+ pub fn new_invalid() -> Self {
+ Self::splat(AABB::new_invalid())
+ }
+
+ pub fn splat(aabb: AABB<f32>) -> Self {
+ Self {
+ mins: [aabb.mins; SIMD_WIDTH],
+ maxs: [aabb.maxs; SIMD_WIDTH],
+ }
+ }
+
+ #[cfg(feature = "dim2")]
+ pub fn intersects_lanewise(&self, other: &WAABB) -> [bool; SIMD_WIDTH] {
+ array![|ii|
+ self.mins[ii].x <= other.maxs[ii].x
+ && other.mins[ii].x <= self.maxs[ii].x
+ && self.mins[ii].y <= other.maxs[ii].y
+ && other.mins[ii].y <= self.maxs[ii].y
+ ; SIMD_WIDTH
+ ]
+ }
+
+ #[cfg(feature = "dim3")]
+ pub fn intersects_lanewise(&self, other: &WAABB) -> [bool; SIMD_WIDTH] {
+ array![|ii|
+ self.mins[ii].x <= other.maxs[ii].x
+ && other.mins[ii].x <= self.maxs[ii].x
+ && self.mins[ii].y <= other.maxs[ii].y
+ && other.mins[ii].y <= self.maxs[ii].y
+ && self.mins[ii].z <= other.maxs[ii].z
+ && other.mins[ii].z <= self.maxs[ii].z
+ ; SIMD_WIDTH
+ ]
+ }
+}
+
+#[cfg(not(feature = "simd-is-enabled"))]
+impl From<[AABB<f32>; SIMD_WIDTH]> for WAABB {
+ fn from(aabbs: [AABB<f32>; SIMD_WIDTH]) -> Self {
+ let mins = array![|ii| aabbs[ii].mins; SIMD_WIDTH];
+ let maxs = array![|ii| aabbs[ii].maxs; SIMD_WIDTH];
+
+ WAABB { mins, maxs }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 118ac23..8cbf673 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -44,7 +44,6 @@ macro_rules! enable_flush_to_zero(
}
);
-#[cfg(feature = "simd-is-enabled")]
macro_rules! array(
($callback: expr; SIMD_WIDTH) => {
{
@@ -139,7 +138,6 @@ pub mod utils;
#[cfg(feature = "dim2")]
/// Math primitives used throughout Rapier.
pub mod math {
- #[cfg(feature = "simd-is-enabled")]
pub use super::simd::*;
use na::{Isometry2, Matrix2, Point2, Translation2, UnitComplex, Vector2, Vector3, U1, U2};
@@ -182,7 +180,6 @@ pub mod math {
#[cfg(feature = "dim3")]
/// Math primitives used throughout Rapier.
pub mod math {
- #[cfg(feature = "simd-is-enabled")]
pub use super::simd::*;
use na::{Isometry3, Matrix3, Point3, Translation3, UnitQuaternion, Vector3, Vector6, U3};
@@ -220,6 +217,12 @@ pub mod math {
pub type SdpMatrix<N> = crate::utils::SdpMatrix3<N>;
}
+#[cfg(not(feature = "simd-is-enabled"))]
+mod simd {
+ /// The number of lanes of a SIMD number.
+ pub const SIMD_WIDTH: usize = 4;
+}
+
#[cfg(feature = "simd-is-enabled")]
mod simd {
#[allow(unused_imports)]
diff --git a/src/pipeline/mod.rs b/src/pipeline/mod.rs
index 6298d18..287de9d 100644
--- a/src/pipeline/mod.rs
+++ b/src/pipeline/mod.rs
@@ -3,7 +3,9 @@
pub use collision_pipeline::CollisionPipeline;
pub use event_handler::{ChannelEventCollector, EventHandler};
pub use physics_pipeline::PhysicsPipeline;
+pub use query_pipeline::QueryPipeline;
mod collision_pipeline;
mod event_handler;
mod physics_pipeline;
+mod query_pipeline;
diff --git a/src/pipeline/query_pipeline.rs b/src/pipeline/query_pipeline.rs
new file mode 100644
index 0000000..15caaef
--- /dev/null
+++ b/src/pipeline/query_pipeline.rs
@@ -0,0 +1,306 @@
+use crate::dynamics::RigidBodySet;
+use crate::geometry::{Collider, ColliderHandle, ColliderSet, Ray, RayIntersection, AABB, WAABB};
+use crate::math::{Point, Vector};
+use ncollide::bounding_volume::BoundingVolume;
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+struct NodeIndex {
+ index: u32, // Index of the addressed node in the `children` array.
+ lane: u8, // SIMD lane of the addressed node.
+}
+
+impl NodeIndex {
+ fn new(index: u32, lane: u8) -> Self {
+ Self { index, lane }
+ }
+
+ fn invalid() -> Self {
+ Self {
+ index: u32::MAX,
+ lane: 0,
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug)]
+struct WAABBHierarchyNodeChildren {
+ waabb: WAABB,
+ // Index of the children of the 4 nodes represented by self.
+ // If this is a leaf, it contains the proxy ids instead.
+ grand_children: [u32; 4],
+ parent: NodeIndex,
+ leaf: bool, // TODO: pack this with the NodexIndex.lane?
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+struct ColliderNodeIndex {
+ node: NodeIndex,
+ handle: ColliderHandle, // The collider handle. TODO: only set the collider generation here?
+}
+
+impl ColliderNodeIndex {
+ fn invalid() -> Self {
+ Self {
+ node: NodeIndex::invalid(),
+ handle: ColliderSet::invalid_handle(),
+ }
+ }
+}
+
+struct WAABBHierarchy {
+ children: Vec<WAABBHierarchyNodeChildren>,
+ dirty: Vec<bool>, // TODO: use a bitvec/Vob and check it does not break cross-platform determinism.
+ proxies: Vec<ColliderNodeIndex>,
+}
+
+impl WAABBHierarchy {
+ pub fn new() -> Self {
+ WAABBHierarchy {
+ children: Vec::new(),
+ dirty: Vec::new(),
+ proxies: Vec::new(),
+ }
+ }
+
+ pub fn clear_and_rebuild(&mut self, colliders: &ColliderSet) {
+ self.children.clear();
+ self.dirty.clear();
+ self.proxies.clear();
+
+ // Create proxies.
+ let mut indices = Vec::with_capacity(colliders.len());
+ let mut proxies = vec![ColliderNodeIndex::invalid(); colliders.len()];
+ for (handle, collider) in colliders.iter() {
+ let index = handle.into_raw_parts().0;
+ if proxies.len() < handle.into_raw_parts().0 {
+ proxies.resize(index + 1, ColliderNodeIndex::invalid());
+ }
+
+ proxies[index].handle = handle;
+ indices.push(index);
+ }
+
+ // Compute AABBs.
+ let mut aabbs = vec![AABB::new_invalid(); proxies.len()];
+ for (handle, collider) in colliders.iter() {
+ let index = handle.into_raw_parts().0;
+ let aabb = collider.compute_aabb();
+ aabbs[index] = aabb;
+ }
+
+ // Build the tree recursively.
+ let root_node = WAABBHierarchyNodeChildren {
+ waabb: WAABB::new_invalid(),
+ grand_children: [1; 4],
+ parent: NodeIndex::invalid(),
+ leaf: false,
+ };
+
+ self.children.push(root_node);
+ let root_id = NodeIndex::new(0, 0);
+ let (_, aabb) = self.do_recurse_build(&mut indices, &aabbs, root_id);
+ self.children[0].waabb = WAABB::splat(aabb);
+ }
+
+ fn do_recurse_build(
+ &mut self,
+ indices: &mut [usize],
+ aabbs: &[AABB],
+ parent: NodeIndex,
+ ) -> (u32, AABB) {
+ // Leaf case.
+ if indices.len() <= 4 {
+ let my_id = self.children.len();
+ let mut my_aabb = AABB::new_invalid();
+ let mut leaf_aabbs = [AABB::new_invalid(); 4];
+ let mut proxy_ids = [u32::MAX; 4];
+
+ for (k, id) in indices.iter().enumerate() {
+ my_aabb.merge(&aabbs[*id]);
+ leaf_aabbs[k] = aabbs[*id];
+ proxy_ids[k] = *id as u32;
+ }
+
+ let node = WAABBHierarchyNodeChildren {
+ waabb: WAABB::from(leaf_aabbs),
+ grand_children: proxy_ids,
+ parent,
+ leaf: true,
+ };
+
+ self.children.push(node);
+ return (my_id as u32, my_aabb);
+ }
+
+ // Compute the center and variance along each dimension.
+ // In 3D we compute the variance to not-subdivide the dimension with lowest variance.
+ // Therefore variance computation is not needed in 2D because we only have 2 dimension
+ // to split in the first place.
+ let mut center = Point::origin();
+ #[cfg(feature = "dim3")]
+ let mut variance = Vector::zeros();
+
+ let denom = 1.0 / (indices.len() as f32);
+
+ for i in &*indices {
+ let coords = aabbs[*i].center().coords;
+ center += coords * denom;
+ #[cfg(feature = "dim3")]
+ {
+ variance += coords.component_mul(&coords) * denom;
+ }
+ }
+
+ #[cfg(feature = "dim3")]
+ {
+ variance = variance - center.coords.component_mul(&center.coords);
+ }
+
+ // Find the axis with minimum variance. This is the axis along
+ // which we are **not** subdividing our set.
+ let mut subdiv_dims = [0, 1];
+ #[cfg(feature = "dim3")]
+ {
+ let min = variance.imin();
+ subdiv_dims[0] = (min + 1) % 3;
+ subdiv_dims[1] = (min + 2) % 3;
+ }
+
+ // Split the set along the two subdiv_dims dimensions.
+ // TODO: should we split wrt. the median instead of the average?
+ // TODO: we should ensure each subslice contains at least 4 elements each (or less if
+ // indices has less than 16 elements in the first place.
+ let (left, right) = split_indices_wrt_dim(indices, &aabbs, subdiv_dims[0]);
+ let (left_bottom, left_top) = split_indices_wrt_dim(left, &aabbs, subdiv_dims[1]);
+ let (right_bottom, right_top) = split_indices_wrt_dim(right, &aabbs, subdiv_dims[1]);
+
+ let node = WAABBHierarchyNodeChildren {
+ waabb: WAABB::new_invalid(),
+ grand_children: [0; 4], // Will be set after the recursive call
+ parent,
+ leaf: false,
+ };
+
+ let id = self.children.len() as u32;
+ self.children.push(node);
+
+ // Recurse!
+ let a = self.do_recurse_build(left_bottom, aabbs, NodeIndex::new(id, 0));
+ let b = self.do_recurse_build(left_top, aabbs, NodeIndex::new(id, 1));
+ let c = self.do_recurse_build(right_bottom, aabbs, NodeIndex::new(id, 2));
+ let d = self.do_recurse_build(right_top, aabbs, NodeIndex::new(id, 3));
+
+ // Now we know the indices of the grand-children.
+ self.children[id as usize].grand_children = [a.0, b.0, c.0, d.0];
+ self.children[id as usize].waabb = WAABB::from([a.1, b.1, c.1, d.1]);
+
+ // TODO: will this chain of .merged be properly optimized?
+ let my_aabb = a.1.merged(&b.1).merged(&c.1).merged(&c.1);
+ (id, my_aabb)
+ }
+}
+
+fn split_indices_wrt_dim<'a>(
+ indices: &'a mut [usize],
+ aabbs: &[AABB],
+ dim: usize,
+) -> (&'a mut [usize], &'a mut [usize]) {
+ let mut icurr = 0;
+ let mut ilast = indices.len() - 1;
+
+ // The loop condition we can just do 0..indices.len()
+ // instead of the test icurr < ilast because we know
+ // we will iterate exactly once per index.
+ for _ in 0..indices.len() {
+ let i = indices[icurr];
+ let center = aabbs[i].center();
+
+ if center[dim] > center[dim] {
+ indices.swap(icurr, ilast);
+ ilast -= 1;
+ } else {
+ icurr += 1;
+ }
+ }
+
+ indices.split_at_mut(icurr)
+}
+
+/// A pipeline for performing queries on all the colliders of a scene.
+pub struct QueryPipeline {
+ // hierarchy: WAABBHierarchy,
+}
+
+impl Default for QueryPipeline {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl QueryPipeline {
+ /// Initializes an empty query pipeline.
+ pub fn new() -> Self {
+ Self {
+ // hierarchy: WAABBHierarchy::new(),
+ }
+ }
+
+ /// Update the acceleration structure on the query pipeline.
+ pub fn update(&mut self, _bodies: &mut RigidBodySet, _colliders: &mut ColliderSet) {}
+
+ /// Find the closest intersection between a ray and a set of collider.
+ ///
+ /// # Parameters
+ /// - `position`: the position of this shape.
+ /// - `ray`: the ray to cast.
+ /// - `max_toi`: the maximum time-of-impact that can be reported by this cast. This effectively
+ /// limits the length of the ray to `ray.dir.norm() * max_toi`. Use `f32::MAX` for an unbounded ray.
+ pub fn cast_ray<'a>(
+ &self,
+ colliders: &'a ColliderSet,
+ ray: &Ray,
+ max_toi: f32,
+ ) -> Option<(ColliderHandle, &'a Collider, RayIntersection)> {
+ let mut best = f32::MAX;
+ let mut result = None;
+
+ // FIXME: this is a brute-force approach.
+ for (handle, collider) in colliders.iter() {
+ if let Some(inter) = collider.shape().cast_ray(collider.position(), ray, max_toi) {
+ if inter.toi < best {
+ best = inter.toi;
+ result = Some((handle, collider, inter));
+ }
+ }
+ }
+
+ result
+ }
+
+ /// Find the all intersections between a ray and a set of collider and passes them to a callback.
+ ///
+ /// # Parameters
+ /// - `position`: the position of this shape.
+ /// - `ray`: the ray to cast.
+ /// - `max_toi`: the maximum time-of-impact that can be reported by this cast. This effectively
+ /// limits the length of the ray to `ray.dir.norm() * max_toi`. Use `f32::MAX` for an unbounded ray.
+ /// - `callback`: function executed on each collider for which a ray intersection has been found.
+ /// There is no guarantees on the order the results will be yielded. If this callback returns `false`,
+ /// this method will exit early, ignory any further raycast.
+ pub fn interferences_with_ray<'a>(
+ &self,
+ colliders: &'a ColliderSet,
+ ray: &Ray,
+ max_toi: f32,
+ mut callback: impl FnMut(ColliderHandle, &'a Collider, RayIntersection) -> bool,
+ ) {
+ // FIXME: this is a brute-force approach.
+ for (handle, collider) in colliders.iter() {
+ if let Some(inter) = collider.shape().cast_ray(collider.position(), ray, max_toi) {
+ if !callback(handle, collider, inter) {
+ return;
+ }
+ }
+ }
+ }
+}