Path: blob/main/crates/bevy_picking/src/mesh_picking/mod.rs
6598 views
//! A [mesh ray casting](ray_cast) backend for [`bevy_picking`](crate).1//!2//! By default, all meshes that have [`bevy_asset::RenderAssetUsages::MAIN_WORLD`] are pickable.3//! Picking can be disabled for individual entities by adding [`Pickable::IGNORE`].4//!5//! To make mesh picking entirely opt-in, set [`MeshPickingSettings::require_markers`]6//! to `true` and add [`MeshPickingCamera`] and [`Pickable`] components to the desired camera and7//! target entities.8//!9//! To manually perform mesh ray casts independent of picking, use the [`MeshRayCast`] system parameter.10//!11//! ## Implementation Notes12//!13//! - The `position` reported in `HitData` is in world space. The `normal` is a vector pointing14//! away from the face, it is not guaranteed to be normalized for scaled meshes.1516pub mod ray_cast;1718use crate::{19backend::{ray::RayMap, HitData, PointerHits},20prelude::*,21PickingSystems,22};23use bevy_app::prelude::*;24use bevy_camera::{visibility::RenderLayers, Camera};25use bevy_ecs::prelude::*;26use bevy_reflect::prelude::*;27use ray_cast::{MeshRayCast, MeshRayCastSettings, RayCastVisibility};2829/// An optional component that marks cameras that should be used in the [`MeshPickingPlugin`].30///31/// Only needed if [`MeshPickingSettings::require_markers`] is set to `true`, and ignored otherwise.32#[derive(Debug, Clone, Default, Component, Reflect)]33#[reflect(Debug, Default, Component)]34pub struct MeshPickingCamera;3536/// Runtime settings for the [`MeshPickingPlugin`].37#[derive(Resource, Reflect)]38#[reflect(Resource, Default)]39pub struct MeshPickingSettings {40/// When set to `true` ray casting will only consider cameras marked with41/// [`MeshPickingCamera`] and entities marked with [`Pickable`]. `false` by default.42///43/// This setting is provided to give you fine-grained control over which cameras and entities44/// should be used by the mesh picking backend at runtime.45pub require_markers: bool,4647/// Determines how mesh picking should consider [`Visibility`](bevy_camera::visibility::Visibility).48/// When set to [`RayCastVisibility::Any`], ray casts can be performed against both visible and hidden entities.49///50/// Defaults to [`RayCastVisibility::VisibleInView`], only performing picking against visible entities51/// that are in the view of a camera.52pub ray_cast_visibility: RayCastVisibility,53}5455impl Default for MeshPickingSettings {56fn default() -> Self {57Self {58require_markers: false,59ray_cast_visibility: RayCastVisibility::VisibleInView,60}61}62}6364/// Adds the mesh picking backend to your app.65#[derive(Clone, Default)]66pub struct MeshPickingPlugin;6768impl Plugin for MeshPickingPlugin {69fn build(&self, app: &mut App) {70app.init_resource::<MeshPickingSettings>()71.add_systems(PreUpdate, update_hits.in_set(PickingSystems::Backend));72}73}7475/// Casts rays into the scene using [`MeshPickingSettings`] and sends [`PointerHits`] events.76pub fn update_hits(77backend_settings: Res<MeshPickingSettings>,78ray_map: Res<RayMap>,79picking_cameras: Query<(&Camera, Has<MeshPickingCamera>, Option<&RenderLayers>)>,80pickables: Query<&Pickable>,81marked_targets: Query<&Pickable>,82layers: Query<&RenderLayers>,83mut ray_cast: MeshRayCast,84mut output: EventWriter<PointerHits>,85) {86for (&ray_id, &ray) in ray_map.iter() {87let Ok((camera, cam_can_pick, cam_layers)) = picking_cameras.get(ray_id.camera) else {88continue;89};90if backend_settings.require_markers && !cam_can_pick {91continue;92}9394let cam_layers = cam_layers.to_owned().unwrap_or_default();9596let settings = MeshRayCastSettings {97visibility: backend_settings.ray_cast_visibility,98filter: &|entity| {99let marker_requirement =100!backend_settings.require_markers || marked_targets.get(entity).is_ok();101102// Other entities missing render layers are on the default layer 0103let entity_layers = layers.get(entity).cloned().unwrap_or_default();104let render_layers_match = cam_layers.intersects(&entity_layers);105106let is_pickable = pickables.get(entity).ok().is_none_or(|p| p.is_hoverable);107108marker_requirement && render_layers_match && is_pickable109},110early_exit_test: &|entity_hit| {111pickables112.get(entity_hit)113.is_ok_and(|pickable| pickable.should_block_lower)114},115};116let picks = ray_cast117.cast_ray(ray, &settings)118.iter()119.map(|(entity, hit)| {120let hit_data = HitData::new(121ray_id.camera,122hit.distance,123Some(hit.point),124Some(hit.normal),125);126(*entity, hit_data)127})128.collect::<Vec<_>>();129let order = camera.order as f32;130if !picks.is_empty() {131output.write(PointerHits::new(ray_id.pointer, picks, order));132}133}134}135136137