//! This module provides a simple interface for implementing a picking backend.1//!2//! Don't be dissuaded by terminology like "backend"; the idea is dead simple. `bevy_picking`3//! will tell you where pointers are, all you have to do is send an event if the pointers are4//! hitting something. That's it. The rest of this documentation explains the requirements in more5//! detail.6//!7//! Because `bevy_picking` is very loosely coupled with its backends, you can mix and match as8//! many backends as you want. For example, you could use the `rapier` backend to raycast against9//! physics objects, a picking shader backend to pick non-physics meshes, and the `bevy_ui` backend10//! for your UI. The [`PointerHits`] instances produced by these various backends will be combined,11//! sorted, and used as a homogeneous input for the picking systems that consume these events.12//!13//! ## Implementation14//!15//! - A picking backend only has one job: read [`PointerLocation`](crate::pointer::PointerLocation)16//! components and produce [`PointerHits`] events. In plain English, a backend is provided the17//! location of pointers, and is asked to provide a list of entities under those pointers.18//!19//! - The [`PointerHits`] events produced by a backend do **not** need to be sorted or filtered, all20//! that is needed is an unordered list of entities and their [`HitData`].21//!22//! - Backends do not need to consider the [`Pickable`](crate::Pickable) component, though they may23//! use it for optimization purposes. For example, a backend that traverses a spatial hierarchy24//! may want to exit early if it intersects an entity that blocks lower entities from being25//! picked.26//!27//! ### Raycasting Backends28//!29//! Backends that require a ray to cast into the scene should use [`ray::RayMap`]. This30//! automatically constructs rays in world space for all cameras and pointers, handling details like31//! viewports and DPI for you.3233use bevy_ecs::prelude::*;34use bevy_math::Vec3;35use bevy_reflect::Reflect;3637/// The picking backend prelude.38///39/// This includes the most common types in this module, re-exported for your convenience.40pub mod prelude {41pub use super::{ray::RayMap, HitData, PointerHits};42pub use crate::{43pointer::{PointerId, PointerLocation},44Pickable, PickingSystems,45};46}4748/// An event produced by a picking backend after it has run its hit tests, describing the entities49/// under a pointer.50///51/// Some backends may only support providing the topmost entity; this is a valid limitation. For52/// example, a picking shader might only have data on the topmost rendered output from its buffer.53///54/// Note that systems reading these events in [`PreUpdate`](bevy_app::PreUpdate) will not report ordering55/// ambiguities with picking backends. Take care to ensure such systems are explicitly ordered56/// against [`PickingSystems::Backend`](crate::PickingSystems::Backend), or better, avoid reading `PointerHits` in `PreUpdate`.57#[derive(BufferedEvent, Debug, Clone, Reflect)]58#[reflect(Debug, Clone)]59pub struct PointerHits {60/// The pointer associated with this hit test.61pub pointer: prelude::PointerId,62/// An unordered collection of entities and their distance (depth) from the cursor.63pub picks: Vec<(Entity, HitData)>,64/// Set the order of this group of picks. Normally, this is the65/// [`bevy_camera::Camera::order`].66///67/// Used to allow multiple `PointerHits` submitted for the same pointer to be ordered.68/// `PointerHits` with a higher `order` will be checked before those with a lower `order`,69/// regardless of the depth of each entity pick.70///71/// In other words, when pick data is coalesced across all backends, the data is grouped by72/// pointer, then sorted by order, and checked sequentially, sorting each `PointerHits` by73/// entity depth. Events with a higher `order` are effectively on top of events with a lower74/// order.75///76/// ### Why is this an `f32`???77///78/// Bevy UI is special in that it can share a camera with other things being rendered. in order79/// to properly sort them, we need a way to make `bevy_ui`'s order a tiny bit higher, like adding80/// 0.5 to the order. We can't use integers, and we want users to be using camera.order by81/// default, so this is the best solution at the moment.82pub order: f32,83}8485impl PointerHits {86/// Construct [`PointerHits`].87pub fn new(pointer: prelude::PointerId, picks: Vec<(Entity, HitData)>, order: f32) -> Self {88Self {89pointer,90picks,91order,92}93}94}9596/// Holds data from a successful pointer hit test. See [`HitData::depth`] for important details.97#[derive(Clone, Debug, PartialEq, Reflect)]98#[reflect(Clone, PartialEq)]99pub struct HitData {100/// The camera entity used to detect this hit. Useful when you need to find the ray that was101/// cast for this hit when using a raycasting backend.102pub camera: Entity,103/// `depth` only needs to be self-consistent with other [`PointerHits`]s using the same104/// [`RenderTarget`](bevy_camera::RenderTarget). However, it is recommended to use the105/// distance from the pointer to the hit, measured from the near plane of the camera, to the106/// point, in world space.107pub depth: f32,108/// The position reported by the backend, if the data is available. Position data may be in any109/// space (e.g. World space, Screen space, Local space), specified by the backend providing it.110pub position: Option<Vec3>,111/// The normal vector of the hit test, if the data is available from the backend.112pub normal: Option<Vec3>,113}114115impl HitData {116/// Construct a [`HitData`].117pub fn new(camera: Entity, depth: f32, position: Option<Vec3>, normal: Option<Vec3>) -> Self {118Self {119camera,120depth,121position,122normal,123}124}125}126127pub mod ray {128//! Types and systems for constructing rays from cameras and pointers.129130use crate::backend::prelude::{PointerId, PointerLocation};131use bevy_camera::Camera;132use bevy_ecs::prelude::*;133use bevy_math::Ray3d;134use bevy_platform::collections::{hash_map::Iter, HashMap};135use bevy_reflect::Reflect;136use bevy_transform::prelude::GlobalTransform;137use bevy_window::PrimaryWindow;138139/// Identifies a ray constructed from some (pointer, camera) combination. A pointer can be over140/// multiple cameras, which is why a single pointer may have multiple rays.141#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Reflect)]142#[reflect(Clone, PartialEq, Hash)]143pub struct RayId {144/// The camera whose projection was used to calculate the ray.145pub camera: Entity,146/// The pointer whose pixel coordinates were used to calculate the ray.147pub pointer: PointerId,148}149150impl RayId {151/// Construct a [`RayId`].152pub fn new(camera: Entity, pointer: PointerId) -> Self {153Self { camera, pointer }154}155}156157/// A map from [`RayId`] to [`Ray3d`].158///159/// This map is cleared and re-populated every frame before any backends run. Ray-based picking160/// backends should use this when possible, as it automatically handles viewports, DPI, and161/// other details of building rays from pointer locations.162///163/// ## Usage164///165/// Iterate over each [`Ray3d`] and its [`RayId`] with [`RayMap::iter`].166///167/// ```168/// # use bevy_ecs::prelude::*;169/// # use bevy_picking::backend::ray::RayMap;170/// # use bevy_picking::backend::PointerHits;171/// // My raycasting backend172/// pub fn update_hits(ray_map: Res<RayMap>, mut output_events: EventWriter<PointerHits>,) {173/// for (&ray_id, &ray) in ray_map.iter() {174/// // Run a raycast with each ray, returning any `PointerHits` found.175/// }176/// }177/// ```178#[derive(Clone, Debug, Default, Resource)]179pub struct RayMap {180/// Cartesian product of all pointers and all cameras181/// Add your rays here to support picking through indirections,182/// e.g. rendered-to-texture cameras183pub map: HashMap<RayId, Ray3d>,184}185186impl RayMap {187/// Iterates over all world space rays for every picking pointer.188pub fn iter(&self) -> Iter<'_, RayId, Ray3d> {189self.map.iter()190}191192/// Clears the [`RayMap`] and re-populates it with one ray for each193/// combination of pointer entity and camera entity where the pointer194/// intersects the camera's viewport.195pub fn repopulate(196mut ray_map: ResMut<Self>,197primary_window_entity: Query<Entity, With<PrimaryWindow>>,198cameras: Query<(Entity, &Camera, &GlobalTransform)>,199pointers: Query<(&PointerId, &PointerLocation)>,200) {201ray_map.map.clear();202203for (camera_entity, camera, camera_tfm) in &cameras {204if !camera.is_active {205continue;206}207208for (&pointer_id, pointer_loc) in &pointers {209if let Some(ray) =210make_ray(&primary_window_entity, camera, camera_tfm, pointer_loc)211{212ray_map213.map214.insert(RayId::new(camera_entity, pointer_id), ray);215}216}217}218}219}220221fn make_ray(222primary_window_entity: &Query<Entity, With<PrimaryWindow>>,223camera: &Camera,224camera_tfm: &GlobalTransform,225pointer_loc: &PointerLocation,226) -> Option<Ray3d> {227let pointer_loc = pointer_loc.location()?;228if !pointer_loc.is_in_viewport(camera, primary_window_entity) {229return None;230}231camera232.viewport_to_world(camera_tfm, pointer_loc.position)233.ok()234}235}236237238