bevy_picking/mesh_picking/ray_cast/
mod.rs

1//! Ray casting for meshes.
2//!
3//! See the [`MeshRayCast`] system parameter for more information.
4
5mod intersections;
6
7use bevy_derive::{Deref, DerefMut};
8
9use bevy_camera::{
10    primitives::Aabb,
11    visibility::{InheritedVisibility, ViewVisibility},
12};
13use bevy_math::{bounding::Aabb3d, Ray3d};
14use bevy_mesh::{Mesh, Mesh2d, Mesh3d};
15use bevy_reflect::{std_traits::ReflectDefault, Reflect};
16
17use intersections::*;
18pub use intersections::{ray_aabb_intersection_3d, ray_mesh_intersection, RayMeshHit};
19
20use bevy_asset::{Assets, Handle};
21use bevy_ecs::{prelude::*, system::lifetimeless::Read, system::SystemParam};
22use bevy_math::FloatOrd;
23use bevy_transform::components::GlobalTransform;
24use tracing::*;
25
26/// How a ray cast should handle [`Visibility`](bevy_camera::visibility::Visibility).
27#[derive(Clone, Copy, Reflect)]
28#[reflect(Clone)]
29pub enum RayCastVisibility {
30    /// Completely ignore visibility checks. Hidden items can still be ray cast against.
31    Any,
32    /// Only cast rays against entities that are visible in the hierarchy. See [`Visibility`](bevy_camera::visibility::Visibility).
33    Visible,
34    /// Only cast rays against entities that are visible in the hierarchy and visible to a camera or
35    /// light. See [`Visibility`](bevy_camera::visibility::Visibility).
36    VisibleInView,
37}
38
39/// Settings for a ray cast.
40#[derive(Clone)]
41pub struct MeshRayCastSettings<'a> {
42    /// Determines how ray casting should consider [`Visibility`](bevy_camera::visibility::Visibility).
43    pub visibility: RayCastVisibility,
44    /// A predicate that is applied for every entity that ray casts are performed against.
45    /// Only entities that return `true` will be considered.
46    pub filter: &'a dyn Fn(Entity) -> bool,
47    /// A function that is run every time a hit is found. Ray casting will continue to check for hits
48    /// along the ray as long as this returns `false`.
49    pub early_exit_test: &'a dyn Fn(Entity) -> bool,
50}
51
52impl<'a> MeshRayCastSettings<'a> {
53    /// Set the filter to apply to the ray cast.
54    pub fn with_filter(mut self, filter: &'a impl Fn(Entity) -> bool) -> Self {
55        self.filter = filter;
56        self
57    }
58
59    /// Set the early exit test to apply to the ray cast.
60    pub fn with_early_exit_test(mut self, early_exit_test: &'a impl Fn(Entity) -> bool) -> Self {
61        self.early_exit_test = early_exit_test;
62        self
63    }
64
65    /// Set the [`RayCastVisibility`] setting to apply to the ray cast.
66    pub fn with_visibility(mut self, visibility: RayCastVisibility) -> Self {
67        self.visibility = visibility;
68        self
69    }
70
71    /// This ray cast should exit as soon as the nearest hit is found.
72    pub fn always_early_exit(self) -> Self {
73        self.with_early_exit_test(&|_| true)
74    }
75
76    /// This ray cast should check all entities whose AABB intersects the ray and return all hits.
77    pub fn never_early_exit(self) -> Self {
78        self.with_early_exit_test(&|_| false)
79    }
80}
81
82impl<'a> Default for MeshRayCastSettings<'a> {
83    fn default() -> Self {
84        Self {
85            visibility: RayCastVisibility::VisibleInView,
86            filter: &|_| true,
87            early_exit_test: &|_| true,
88        }
89    }
90}
91
92/// Determines whether backfaces should be culled or included in ray intersection tests.
93///
94/// By default, backfaces are culled.
95#[derive(Copy, Clone, Default, Reflect)]
96#[reflect(Default, Clone)]
97pub enum Backfaces {
98    /// Cull backfaces.
99    #[default]
100    Cull,
101    /// Include backfaces.
102    Include,
103}
104
105/// Disables backface culling for [ray casts](MeshRayCast) on this entity.
106#[derive(Component, Copy, Clone, Default, Reflect)]
107#[reflect(Component, Default, Clone)]
108pub struct RayCastBackfaces;
109
110/// A simplified mesh component that can be used for [ray casting](super::MeshRayCast).
111///
112/// Consider using this component for complex meshes that don't need perfectly accurate ray casting.
113#[derive(Component, Clone, Debug, Deref, DerefMut, Reflect)]
114#[reflect(Component, Debug, Clone)]
115pub struct SimplifiedMesh(pub Handle<Mesh>);
116
117type MeshFilter = Or<(With<Mesh3d>, With<Mesh2d>, With<SimplifiedMesh>)>;
118
119/// Add this ray casting [`SystemParam`] to your system to cast rays into the world with an
120/// immediate-mode API. Call `cast_ray` to immediately perform a ray cast and get a result.
121///
122/// Under the hood, this is a collection of regular bevy queries, resources, and local parameters
123/// that are added to your system.
124///
125/// ## Usage
126///
127/// The following system casts a ray into the world with the ray positioned at the origin, pointing in
128/// the X-direction, and returns a list of intersections:
129///
130/// ```
131/// # use bevy_math::prelude::*;
132/// # use bevy_picking::prelude::*;
133/// fn ray_cast_system(mut ray_cast: MeshRayCast) {
134///     let ray = Ray3d::new(Vec3::ZERO, Dir3::X);
135///     let hits = ray_cast.cast_ray(ray, &MeshRayCastSettings::default());
136/// }
137/// ```
138///
139/// ## Configuration
140///
141/// You can specify the behavior of the ray cast using [`MeshRayCastSettings`]. This allows you to filter out
142/// entities, configure early-out behavior, and set whether the [`Visibility`](bevy_camera::visibility::Visibility)
143/// of an entity should be considered.
144///
145/// ```
146/// # use bevy_ecs::prelude::*;
147/// # use bevy_math::prelude::*;
148/// # use bevy_picking::prelude::*;
149/// # #[derive(Component)]
150/// # struct Foo;
151/// fn ray_cast_system(mut ray_cast: MeshRayCast, foo_query: Query<(), With<Foo>>) {
152///     let ray = Ray3d::new(Vec3::ZERO, Dir3::X);
153///
154///     // Only ray cast against entities with the `Foo` component.
155///     let filter = |entity| foo_query.contains(entity);
156///
157///     // Never early-exit. Note that you can change behavior per-entity.
158///     let early_exit_test = |_entity| false;
159///
160///     // Ignore the visibility of entities. This allows ray casting hidden entities.
161///     let visibility = RayCastVisibility::Any;
162///
163///     let settings = MeshRayCastSettings::default()
164///         .with_filter(&filter)
165///         .with_early_exit_test(&early_exit_test)
166///         .with_visibility(visibility);
167///
168///     // Cast the ray with the settings, returning a list of intersections.
169///     let hits = ray_cast.cast_ray(ray, &settings);
170/// }
171/// ```
172#[derive(SystemParam)]
173pub struct MeshRayCast<'w, 's> {
174    #[doc(hidden)]
175    pub meshes: Res<'w, Assets<Mesh>>,
176    #[doc(hidden)]
177    pub hits: Local<'s, Vec<(FloatOrd, (Entity, RayMeshHit))>>,
178    #[doc(hidden)]
179    pub output: Local<'s, Vec<(Entity, RayMeshHit)>>,
180    #[doc(hidden)]
181    pub culled_list: Local<'s, Vec<(FloatOrd, Entity)>>,
182    #[doc(hidden)]
183    pub culling_query: Query<
184        'w,
185        's,
186        (
187            Read<InheritedVisibility>,
188            Read<ViewVisibility>,
189            Read<Aabb>,
190            Read<GlobalTransform>,
191            Entity,
192        ),
193        MeshFilter,
194    >,
195    #[doc(hidden)]
196    pub mesh_query: Query<
197        'w,
198        's,
199        (
200            Option<Read<Mesh2d>>,
201            Option<Read<Mesh3d>>,
202            Option<Read<SimplifiedMesh>>,
203            Has<RayCastBackfaces>,
204            Read<GlobalTransform>,
205        ),
206        MeshFilter,
207    >,
208}
209
210impl<'w, 's> MeshRayCast<'w, 's> {
211    /// Casts the `ray` into the world and returns a sorted list of intersections, nearest first.
212    pub fn cast_ray(
213        &mut self,
214        ray: Ray3d,
215        settings: &MeshRayCastSettings,
216    ) -> &[(Entity, RayMeshHit)] {
217        let ray_cull = info_span!("ray culling");
218        let ray_cull_guard = ray_cull.enter();
219
220        self.hits.clear();
221        self.culled_list.clear();
222        self.output.clear();
223
224        // Check all entities to see if the ray intersects the AABB. Use this to build a short list
225        // of entities that are in the path of the ray.
226        let (aabb_hits_tx, aabb_hits_rx) = crossbeam_channel::unbounded::<(FloatOrd, Entity)>();
227        let visibility_setting = settings.visibility;
228        self.culling_query.par_iter().for_each(
229            |(inherited_visibility, view_visibility, aabb, transform, entity)| {
230                let should_ray_cast = match visibility_setting {
231                    RayCastVisibility::Any => true,
232                    RayCastVisibility::Visible => inherited_visibility.get(),
233                    RayCastVisibility::VisibleInView => view_visibility.get(),
234                };
235                if should_ray_cast
236                    && let Some(distance) = ray_aabb_intersection_3d(
237                        ray,
238                        &Aabb3d::new(aabb.center, aabb.half_extents),
239                        &transform.affine(),
240                    )
241                {
242                    aabb_hits_tx.send((FloatOrd(distance), entity)).ok();
243                }
244            },
245        );
246        *self.culled_list = aabb_hits_rx.try_iter().collect();
247
248        // Sort by the distance along the ray.
249        self.culled_list.sort_by_key(|(aabb_near, _)| *aabb_near);
250
251        drop(ray_cull_guard);
252
253        // Perform ray casts against the culled entities.
254        let mut nearest_blocking_hit = FloatOrd(f32::INFINITY);
255        let ray_cast_guard = debug_span!("ray_cast");
256        self.culled_list
257            .iter()
258            .filter(|(_, entity)| (settings.filter)(*entity))
259            .for_each(|(aabb_near, entity)| {
260                // Get the mesh components and transform.
261                let Ok((mesh2d, mesh3d, simplified_mesh, has_backfaces, transform)) =
262                    self.mesh_query.get(*entity)
263                else {
264                    return;
265                };
266
267                // Get the underlying mesh handle. One of these will always be `Some` because of the query filters.
268                let Some(mesh_handle) = simplified_mesh
269                    .map(|m| &m.0)
270                    .or(mesh3d.map(|m| &m.0).or(mesh2d.map(|m| &m.0)))
271                else {
272                    return;
273                };
274
275                // Is it even possible the mesh could be closer than the current best?
276                if *aabb_near > nearest_blocking_hit {
277                    return;
278                }
279
280                // Does the mesh handle resolve?
281                let Some(mesh) = self.meshes.get(mesh_handle) else {
282                    return;
283                };
284
285                // Backfaces of 2d meshes are never culled, unlike 3d meshes.
286                let backfaces = match (has_backfaces, mesh2d.is_some()) {
287                    (false, false) => Backfaces::Cull,
288                    _ => Backfaces::Include,
289                };
290
291                // Perform the actual ray cast.
292                let _ray_cast_guard = ray_cast_guard.enter();
293                let transform = transform.affine();
294                let intersection = ray_intersection_over_mesh(mesh, &transform, ray, backfaces);
295
296                if let Some(intersection) = intersection {
297                    let distance = FloatOrd(intersection.distance);
298                    if (settings.early_exit_test)(*entity) && distance < nearest_blocking_hit {
299                        // The reason we don't just return here is because right now we are
300                        // going through the AABBs in order, but that doesn't mean that an
301                        // AABB that starts further away can't end up with a closer hit than
302                        // an AABB that starts closer. We need to keep checking AABBs that
303                        // could possibly contain a nearer hit.
304                        nearest_blocking_hit = distance.min(nearest_blocking_hit);
305                    }
306                    self.hits.push((distance, (*entity, intersection)));
307                };
308            });
309
310        self.hits.retain(|(dist, _)| *dist <= nearest_blocking_hit);
311        self.hits.sort_by_key(|(k, _)| *k);
312        let hits = self.hits.iter().map(|(_, (e, i))| (*e, i.to_owned()));
313        self.output.extend(hits);
314        self.output.as_ref()
315    }
316}