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