Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_picking/src/backend.rs
6596 views
1
//! This module provides a simple interface for implementing a picking backend.
2
//!
3
//! Don't be dissuaded by terminology like "backend"; the idea is dead simple. `bevy_picking`
4
//! will tell you where pointers are, all you have to do is send an event if the pointers are
5
//! hitting something. That's it. The rest of this documentation explains the requirements in more
6
//! detail.
7
//!
8
//! Because `bevy_picking` is very loosely coupled with its backends, you can mix and match as
9
//! many backends as you want. For example, you could use the `rapier` backend to raycast against
10
//! physics objects, a picking shader backend to pick non-physics meshes, and the `bevy_ui` backend
11
//! for your UI. The [`PointerHits`] instances produced by these various backends will be combined,
12
//! sorted, and used as a homogeneous input for the picking systems that consume these events.
13
//!
14
//! ## Implementation
15
//!
16
//! - A picking backend only has one job: read [`PointerLocation`](crate::pointer::PointerLocation)
17
//! components and produce [`PointerHits`] events. In plain English, a backend is provided the
18
//! location of pointers, and is asked to provide a list of entities under those pointers.
19
//!
20
//! - The [`PointerHits`] events produced by a backend do **not** need to be sorted or filtered, all
21
//! that is needed is an unordered list of entities and their [`HitData`].
22
//!
23
//! - Backends do not need to consider the [`Pickable`](crate::Pickable) component, though they may
24
//! use it for optimization purposes. For example, a backend that traverses a spatial hierarchy
25
//! may want to exit early if it intersects an entity that blocks lower entities from being
26
//! picked.
27
//!
28
//! ### Raycasting Backends
29
//!
30
//! Backends that require a ray to cast into the scene should use [`ray::RayMap`]. This
31
//! automatically constructs rays in world space for all cameras and pointers, handling details like
32
//! viewports and DPI for you.
33
34
use bevy_ecs::prelude::*;
35
use bevy_math::Vec3;
36
use bevy_reflect::Reflect;
37
38
/// The picking backend prelude.
39
///
40
/// This includes the most common types in this module, re-exported for your convenience.
41
pub mod prelude {
42
pub use super::{ray::RayMap, HitData, PointerHits};
43
pub use crate::{
44
pointer::{PointerId, PointerLocation},
45
Pickable, PickingSystems,
46
};
47
}
48
49
/// An event produced by a picking backend after it has run its hit tests, describing the entities
50
/// under a pointer.
51
///
52
/// Some backends may only support providing the topmost entity; this is a valid limitation. For
53
/// example, a picking shader might only have data on the topmost rendered output from its buffer.
54
///
55
/// Note that systems reading these events in [`PreUpdate`](bevy_app::PreUpdate) will not report ordering
56
/// ambiguities with picking backends. Take care to ensure such systems are explicitly ordered
57
/// against [`PickingSystems::Backend`](crate::PickingSystems::Backend), or better, avoid reading `PointerHits` in `PreUpdate`.
58
#[derive(BufferedEvent, Debug, Clone, Reflect)]
59
#[reflect(Debug, Clone)]
60
pub struct PointerHits {
61
/// The pointer associated with this hit test.
62
pub pointer: prelude::PointerId,
63
/// An unordered collection of entities and their distance (depth) from the cursor.
64
pub picks: Vec<(Entity, HitData)>,
65
/// Set the order of this group of picks. Normally, this is the
66
/// [`bevy_camera::Camera::order`].
67
///
68
/// Used to allow multiple `PointerHits` submitted for the same pointer to be ordered.
69
/// `PointerHits` with a higher `order` will be checked before those with a lower `order`,
70
/// regardless of the depth of each entity pick.
71
///
72
/// In other words, when pick data is coalesced across all backends, the data is grouped by
73
/// pointer, then sorted by order, and checked sequentially, sorting each `PointerHits` by
74
/// entity depth. Events with a higher `order` are effectively on top of events with a lower
75
/// order.
76
///
77
/// ### Why is this an `f32`???
78
///
79
/// Bevy UI is special in that it can share a camera with other things being rendered. in order
80
/// to properly sort them, we need a way to make `bevy_ui`'s order a tiny bit higher, like adding
81
/// 0.5 to the order. We can't use integers, and we want users to be using camera.order by
82
/// default, so this is the best solution at the moment.
83
pub order: f32,
84
}
85
86
impl PointerHits {
87
/// Construct [`PointerHits`].
88
pub fn new(pointer: prelude::PointerId, picks: Vec<(Entity, HitData)>, order: f32) -> Self {
89
Self {
90
pointer,
91
picks,
92
order,
93
}
94
}
95
}
96
97
/// Holds data from a successful pointer hit test. See [`HitData::depth`] for important details.
98
#[derive(Clone, Debug, PartialEq, Reflect)]
99
#[reflect(Clone, PartialEq)]
100
pub struct HitData {
101
/// The camera entity used to detect this hit. Useful when you need to find the ray that was
102
/// cast for this hit when using a raycasting backend.
103
pub camera: Entity,
104
/// `depth` only needs to be self-consistent with other [`PointerHits`]s using the same
105
/// [`RenderTarget`](bevy_camera::RenderTarget). However, it is recommended to use the
106
/// distance from the pointer to the hit, measured from the near plane of the camera, to the
107
/// point, in world space.
108
pub depth: f32,
109
/// The position reported by the backend, if the data is available. Position data may be in any
110
/// space (e.g. World space, Screen space, Local space), specified by the backend providing it.
111
pub position: Option<Vec3>,
112
/// The normal vector of the hit test, if the data is available from the backend.
113
pub normal: Option<Vec3>,
114
}
115
116
impl HitData {
117
/// Construct a [`HitData`].
118
pub fn new(camera: Entity, depth: f32, position: Option<Vec3>, normal: Option<Vec3>) -> Self {
119
Self {
120
camera,
121
depth,
122
position,
123
normal,
124
}
125
}
126
}
127
128
pub mod ray {
129
//! Types and systems for constructing rays from cameras and pointers.
130
131
use crate::backend::prelude::{PointerId, PointerLocation};
132
use bevy_camera::Camera;
133
use bevy_ecs::prelude::*;
134
use bevy_math::Ray3d;
135
use bevy_platform::collections::{hash_map::Iter, HashMap};
136
use bevy_reflect::Reflect;
137
use bevy_transform::prelude::GlobalTransform;
138
use bevy_window::PrimaryWindow;
139
140
/// Identifies a ray constructed from some (pointer, camera) combination. A pointer can be over
141
/// multiple cameras, which is why a single pointer may have multiple rays.
142
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Reflect)]
143
#[reflect(Clone, PartialEq, Hash)]
144
pub struct RayId {
145
/// The camera whose projection was used to calculate the ray.
146
pub camera: Entity,
147
/// The pointer whose pixel coordinates were used to calculate the ray.
148
pub pointer: PointerId,
149
}
150
151
impl RayId {
152
/// Construct a [`RayId`].
153
pub fn new(camera: Entity, pointer: PointerId) -> Self {
154
Self { camera, pointer }
155
}
156
}
157
158
/// A map from [`RayId`] to [`Ray3d`].
159
///
160
/// This map is cleared and re-populated every frame before any backends run. Ray-based picking
161
/// backends should use this when possible, as it automatically handles viewports, DPI, and
162
/// other details of building rays from pointer locations.
163
///
164
/// ## Usage
165
///
166
/// Iterate over each [`Ray3d`] and its [`RayId`] with [`RayMap::iter`].
167
///
168
/// ```
169
/// # use bevy_ecs::prelude::*;
170
/// # use bevy_picking::backend::ray::RayMap;
171
/// # use bevy_picking::backend::PointerHits;
172
/// // My raycasting backend
173
/// pub fn update_hits(ray_map: Res<RayMap>, mut output_events: EventWriter<PointerHits>,) {
174
/// for (&ray_id, &ray) in ray_map.iter() {
175
/// // Run a raycast with each ray, returning any `PointerHits` found.
176
/// }
177
/// }
178
/// ```
179
#[derive(Clone, Debug, Default, Resource)]
180
pub struct RayMap {
181
/// Cartesian product of all pointers and all cameras
182
/// Add your rays here to support picking through indirections,
183
/// e.g. rendered-to-texture cameras
184
pub map: HashMap<RayId, Ray3d>,
185
}
186
187
impl RayMap {
188
/// Iterates over all world space rays for every picking pointer.
189
pub fn iter(&self) -> Iter<'_, RayId, Ray3d> {
190
self.map.iter()
191
}
192
193
/// Clears the [`RayMap`] and re-populates it with one ray for each
194
/// combination of pointer entity and camera entity where the pointer
195
/// intersects the camera's viewport.
196
pub fn repopulate(
197
mut ray_map: ResMut<Self>,
198
primary_window_entity: Query<Entity, With<PrimaryWindow>>,
199
cameras: Query<(Entity, &Camera, &GlobalTransform)>,
200
pointers: Query<(&PointerId, &PointerLocation)>,
201
) {
202
ray_map.map.clear();
203
204
for (camera_entity, camera, camera_tfm) in &cameras {
205
if !camera.is_active {
206
continue;
207
}
208
209
for (&pointer_id, pointer_loc) in &pointers {
210
if let Some(ray) =
211
make_ray(&primary_window_entity, camera, camera_tfm, pointer_loc)
212
{
213
ray_map
214
.map
215
.insert(RayId::new(camera_entity, pointer_id), ray);
216
}
217
}
218
}
219
}
220
}
221
222
fn make_ray(
223
primary_window_entity: &Query<Entity, With<PrimaryWindow>>,
224
camera: &Camera,
225
camera_tfm: &GlobalTransform,
226
pointer_loc: &PointerLocation,
227
) -> Option<Ray3d> {
228
let pointer_loc = pointer_loc.location()?;
229
if !pointer_loc.is_in_viewport(camera, primary_window_entity) {
230
return None;
231
}
232
camera
233
.viewport_to_world(camera_tfm, pointer_loc.position)
234
.ok()
235
}
236
}
237
238