Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

236
vendor/bevy_picking/src/backend.rs vendored Normal file
View File

@@ -0,0 +1,236 @@
//! This module provides a simple interface for implementing a picking backend.
//!
//! Don't be dissuaded by terminology like "backend"; the idea is dead simple. `bevy_picking`
//! will tell you where pointers are, all you have to do is send an event if the pointers are
//! hitting something. That's it. The rest of this documentation explains the requirements in more
//! detail.
//!
//! Because `bevy_picking` is very loosely coupled with its backends, you can mix and match as
//! many backends as you want. For example, you could use the `rapier` backend to raycast against
//! physics objects, a picking shader backend to pick non-physics meshes, and the `bevy_ui` backend
//! for your UI. The [`PointerHits`] instances produced by these various backends will be combined,
//! sorted, and used as a homogeneous input for the picking systems that consume these events.
//!
//! ## Implementation
//!
//! - A picking backend only has one job: read [`PointerLocation`](crate::pointer::PointerLocation)
//! components and produce [`PointerHits`] events. In plain English, a backend is provided the
//! location of pointers, and is asked to provide a list of entities under those pointers.
//!
//! - The [`PointerHits`] events produced by a backend do **not** need to be sorted or filtered, all
//! that is needed is an unordered list of entities and their [`HitData`].
//!
//! - Backends do not need to consider the [`Pickable`](crate::Pickable) component, though they may
//! use it for optimization purposes. For example, a backend that traverses a spatial hierarchy
//! may want to exit early if it intersects an entity that blocks lower entities from being
//! picked.
//!
//! ### Raycasting Backends
//!
//! Backends that require a ray to cast into the scene should use [`ray::RayMap`]. This
//! automatically constructs rays in world space for all cameras and pointers, handling details like
//! viewports and DPI for you.
use bevy_ecs::prelude::*;
use bevy_math::Vec3;
use bevy_reflect::Reflect;
/// The picking backend prelude.
///
/// This includes the most common types in this module, re-exported for your convenience.
pub mod prelude {
pub use super::{ray::RayMap, HitData, PointerHits};
pub use crate::{
pointer::{PointerId, PointerLocation},
PickSet, Pickable,
};
}
/// An event produced by a picking backend after it has run its hit tests, describing the entities
/// under a pointer.
///
/// Some backends may only support providing the topmost entity; this is a valid limitation. For
/// example, a picking shader might only have data on the topmost rendered output from its buffer.
///
/// Note that systems reading these events in [`PreUpdate`](bevy_app::PreUpdate) will not report ordering
/// ambiguities with picking backends. Take care to ensure such systems are explicitly ordered
/// against [`PickSet::Backend`](crate::PickSet::Backend), or better, avoid reading `PointerHits` in `PreUpdate`.
#[derive(Event, Debug, Clone, Reflect)]
#[reflect(Debug, Clone)]
pub struct PointerHits {
/// The pointer associated with this hit test.
pub pointer: prelude::PointerId,
/// An unordered collection of entities and their distance (depth) from the cursor.
pub picks: Vec<(Entity, HitData)>,
/// Set the order of this group of picks. Normally, this is the
/// [`bevy_render::camera::Camera::order`].
///
/// Used to allow multiple `PointerHits` submitted for the same pointer to be ordered.
/// `PointerHits` with a higher `order` will be checked before those with a lower `order`,
/// regardless of the depth of each entity pick.
///
/// In other words, when pick data is coalesced across all backends, the data is grouped by
/// pointer, then sorted by order, and checked sequentially, sorting each `PointerHits` by
/// entity depth. Events with a higher `order` are effectively on top of events with a lower
/// order.
///
/// ### Why is this an `f32`???
///
/// Bevy UI is special in that it can share a camera with other things being rendered. in order
/// to properly sort them, we need a way to make `bevy_ui`'s order a tiny bit higher, like adding
/// 0.5 to the order. We can't use integers, and we want users to be using camera.order by
/// default, so this is the best solution at the moment.
pub order: f32,
}
impl PointerHits {
#[expect(missing_docs, reason = "Not all docs are written yet, see #3492.")]
pub fn new(pointer: prelude::PointerId, picks: Vec<(Entity, HitData)>, order: f32) -> Self {
Self {
pointer,
picks,
order,
}
}
}
/// Holds data from a successful pointer hit test. See [`HitData::depth`] for important details.
#[derive(Clone, Debug, PartialEq, Reflect)]
#[reflect(Clone, PartialEq)]
pub struct HitData {
/// The camera entity used to detect this hit. Useful when you need to find the ray that was
/// casted for this hit when using a raycasting backend.
pub camera: Entity,
/// `depth` only needs to be self-consistent with other [`PointerHits`]s using the same
/// [`RenderTarget`](bevy_render::camera::RenderTarget). However, it is recommended to use the
/// distance from the pointer to the hit, measured from the near plane of the camera, to the
/// point, in world space.
pub depth: f32,
/// The position reported by the backend, if the data is available. Position data may be in any
/// space (e.g. World space, Screen space, Local space), specified by the backend providing it.
pub position: Option<Vec3>,
/// The normal vector of the hit test, if the data is available from the backend.
pub normal: Option<Vec3>,
}
impl HitData {
#[expect(missing_docs, reason = "Not all docs are written yet, see #3492.")]
pub fn new(camera: Entity, depth: f32, position: Option<Vec3>, normal: Option<Vec3>) -> Self {
Self {
camera,
depth,
position,
normal,
}
}
}
pub mod ray {
//! Types and systems for constructing rays from cameras and pointers.
use crate::backend::prelude::{PointerId, PointerLocation};
use bevy_ecs::prelude::*;
use bevy_math::Ray3d;
use bevy_platform::collections::{hash_map::Iter, HashMap};
use bevy_reflect::Reflect;
use bevy_render::camera::Camera;
use bevy_transform::prelude::GlobalTransform;
use bevy_window::PrimaryWindow;
/// Identifies a ray constructed from some (pointer, camera) combination. A pointer can be over
/// multiple cameras, which is why a single pointer may have multiple rays.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Reflect)]
#[reflect(Clone, PartialEq, Hash)]
pub struct RayId {
/// The camera whose projection was used to calculate the ray.
pub camera: Entity,
/// The pointer whose pixel coordinates were used to calculate the ray.
pub pointer: PointerId,
}
impl RayId {
/// Construct a [`RayId`].
pub fn new(camera: Entity, pointer: PointerId) -> Self {
Self { camera, pointer }
}
}
/// A map from [`RayId`] to [`Ray3d`].
///
/// This map is cleared and re-populated every frame before any backends run. Ray-based picking
/// backends should use this when possible, as it automatically handles viewports, DPI, and
/// other details of building rays from pointer locations.
///
/// ## Usage
///
/// Iterate over each [`Ray3d`] and its [`RayId`] with [`RayMap::iter`].
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_picking::backend::ray::RayMap;
/// # use bevy_picking::backend::PointerHits;
/// // My raycasting backend
/// pub fn update_hits(ray_map: Res<RayMap>, mut output_events: EventWriter<PointerHits>,) {
/// for (&ray_id, &ray) in ray_map.iter() {
/// // Run a raycast with each ray, returning any `PointerHits` found.
/// }
/// }
/// ```
#[derive(Clone, Debug, Default, Resource)]
pub struct RayMap {
/// Cartesian product of all pointers and all cameras
/// Add your rays here to support picking through indirections,
/// e.g. rendered-to-texture cameras
pub map: HashMap<RayId, Ray3d>,
}
impl RayMap {
/// Iterates over all world space rays for every picking pointer.
pub fn iter(&self) -> Iter<'_, RayId, Ray3d> {
self.map.iter()
}
/// Clears the [`RayMap`] and re-populates it with one ray for each
/// combination of pointer entity and camera entity where the pointer
/// intersects the camera's viewport.
pub fn repopulate(
mut ray_map: ResMut<Self>,
primary_window_entity: Query<Entity, With<PrimaryWindow>>,
cameras: Query<(Entity, &Camera, &GlobalTransform)>,
pointers: Query<(&PointerId, &PointerLocation)>,
) {
ray_map.map.clear();
for (camera_entity, camera, camera_tfm) in &cameras {
if !camera.is_active {
continue;
}
for (&pointer_id, pointer_loc) in &pointers {
if let Some(ray) =
make_ray(&primary_window_entity, camera, camera_tfm, pointer_loc)
{
ray_map
.map
.insert(RayId::new(camera_entity, pointer_id), ray);
}
}
}
}
}
fn make_ray(
primary_window_entity: &Query<Entity, With<PrimaryWindow>>,
camera: &Camera,
camera_tfm: &GlobalTransform,
pointer_loc: &PointerLocation,
) -> Option<Ray3d> {
let pointer_loc = pointer_loc.location()?;
if !pointer_loc.is_in_viewport(camera, primary_window_entity) {
return None;
}
camera
.viewport_to_world(camera_tfm, pointer_loc.position)
.ok()
}
}

824
vendor/bevy_picking/src/events.rs vendored Normal file
View File

@@ -0,0 +1,824 @@
//! This module defines a stateful set of interaction events driven by the `PointerInput` stream
//! and the hover state of each Pointer.
//!
//! # Usage
//!
//! To receive events from this module, you must use an [`Observer`] or [`EventReader`] with [`Pointer<E>`] events.
//! The simplest example, registering a callback when an entity is hovered over by a pointer, looks like this:
//!
//! ```rust
//! # use bevy_ecs::prelude::*;
//! # use bevy_picking::prelude::*;
//! # let mut world = World::default();
//! world.spawn_empty()
//! .observe(|trigger: Trigger<Pointer<Over>>| {
//! println!("I am being hovered over");
//! });
//! ```
//!
//! Observers give us three important properties:
//! 1. They allow for attaching event handlers to specific entities,
//! 2. they allow events to bubble up the entity hierarchy,
//! 3. and they allow events of different types to be called in a specific order.
//!
//! The order in which interaction events are received is extremely important, and you can read more
//! about it on the docs for the dispatcher system: [`pointer_events`]. This system runs in
//! [`PreUpdate`](bevy_app::PreUpdate) in [`PickSet::Hover`](crate::PickSet::Hover). All pointer-event
//! observers resolve during the sync point between [`pointer_events`] and
//! [`update_interactions`](crate::hover::update_interactions).
//!
//! # Events Types
//!
//! The events this module defines fall into a few broad categories:
//! + Hovering and movement: [`Over`], [`Move`], and [`Out`].
//! + Clicking and pressing: [`Pressed`], [`Released`], and [`Click`].
//! + Dragging and dropping: [`DragStart`], [`Drag`], [`DragEnd`], [`DragEnter`], [`DragOver`], [`DragDrop`], [`DragLeave`].
//!
//! When received by an observer, these events will always be wrapped by the [`Pointer`] type, which contains
//! general metadata about the pointer event.
use core::{fmt::Debug, time::Duration};
use bevy_ecs::{prelude::*, query::QueryData, system::SystemParam, traversal::Traversal};
use bevy_input::mouse::MouseScrollUnit;
use bevy_math::Vec2;
use bevy_platform::collections::HashMap;
use bevy_platform::time::Instant;
use bevy_reflect::prelude::*;
use bevy_render::camera::NormalizedRenderTarget;
use bevy_window::Window;
use tracing::debug;
use crate::{
backend::{prelude::PointerLocation, HitData},
hover::{HoverMap, PreviousHoverMap},
pointer::{Location, PointerAction, PointerButton, PointerId, PointerInput, PointerMap},
};
/// Stores the common data needed for all pointer events.
///
/// The documentation for the [`pointer_events`] explains the events this module exposes and
/// the order in which they fire.
#[derive(Clone, PartialEq, Debug, Reflect, Component)]
#[reflect(Component, Debug, Clone)]
pub struct Pointer<E: Debug + Clone + Reflect> {
/// The original target of this picking event, before bubbling
pub target: Entity,
/// The pointer that triggered this event
pub pointer_id: PointerId,
/// The location of the pointer during this event
pub pointer_location: Location,
/// Additional event-specific data. [`DragDrop`] for example, has an additional field to describe
/// the `Entity` that is being dropped on the target.
pub event: E,
}
/// A traversal query (i.e. it implements [`Traversal`]) intended for use with [`Pointer`] events.
///
/// This will always traverse to the parent, if the entity being visited has one. Otherwise, it
/// propagates to the pointer's window and stops there.
#[derive(QueryData)]
pub struct PointerTraversal {
child_of: Option<&'static ChildOf>,
window: Option<&'static Window>,
}
impl<E> Traversal<Pointer<E>> for PointerTraversal
where
E: Debug + Clone + Reflect,
{
fn traverse(item: Self::Item<'_>, pointer: &Pointer<E>) -> Option<Entity> {
let PointerTraversalItem { child_of, window } = item;
// Send event to parent, if it has one.
if let Some(child_of) = child_of {
return Some(child_of.parent());
};
// Otherwise, send it to the window entity (unless this is a window entity).
if window.is_none() {
if let NormalizedRenderTarget::Window(window_ref) = pointer.pointer_location.target {
return Some(window_ref.entity());
}
}
None
}
}
impl<E> Event for Pointer<E>
where
E: Debug + Clone + Reflect,
{
type Traversal = PointerTraversal;
const AUTO_PROPAGATE: bool = true;
}
impl<E: Debug + Clone + Reflect> core::fmt::Display for Pointer<E> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_fmt(format_args!(
"{:?}, {:.1?}, {:.1?}",
self.pointer_id, self.pointer_location.position, self.event
))
}
}
impl<E: Debug + Clone + Reflect> core::ops::Deref for Pointer<E> {
type Target = E;
fn deref(&self) -> &Self::Target {
&self.event
}
}
impl<E: Debug + Clone + Reflect> Pointer<E> {
/// Construct a new `Pointer<E>` event.
pub fn new(id: PointerId, location: Location, target: Entity, event: E) -> Self {
Self {
target,
pointer_id: id,
pointer_location: location,
event,
}
}
}
/// Fires when a pointer is canceled, and its current interaction state is dropped.
#[derive(Clone, PartialEq, Debug, Reflect)]
#[reflect(Clone, PartialEq)]
pub struct Cancel {
/// Information about the picking intersection.
pub hit: HitData,
}
/// Fires when a the pointer crosses into the bounds of the `target` entity.
#[derive(Clone, PartialEq, Debug, Reflect)]
#[reflect(Clone, PartialEq)]
pub struct Over {
/// Information about the picking intersection.
pub hit: HitData,
}
/// Fires when a the pointer crosses out of the bounds of the `target` entity.
#[derive(Clone, PartialEq, Debug, Reflect)]
#[reflect(Clone, PartialEq)]
pub struct Out {
/// Information about the latest prior picking intersection.
pub hit: HitData,
}
/// Fires when a pointer button is pressed over the `target` entity.
#[derive(Clone, PartialEq, Debug, Reflect)]
#[reflect(Clone, PartialEq)]
pub struct Pressed {
/// Pointer button pressed to trigger this event.
pub button: PointerButton,
/// Information about the picking intersection.
pub hit: HitData,
}
/// Fires when a pointer button is released over the `target` entity.
#[derive(Clone, PartialEq, Debug, Reflect)]
#[reflect(Clone, PartialEq)]
pub struct Released {
/// Pointer button lifted to trigger this event.
pub button: PointerButton,
/// Information about the picking intersection.
pub hit: HitData,
}
/// Fires when a pointer sends a pointer pressed event followed by a pointer released event, with the same
/// `target` entity for both events.
#[derive(Clone, PartialEq, Debug, Reflect)]
#[reflect(Clone, PartialEq)]
pub struct Click {
/// Pointer button pressed and lifted to trigger this event.
pub button: PointerButton,
/// Information about the picking intersection.
pub hit: HitData,
/// Duration between the pointer pressed and lifted for this click
pub duration: Duration,
}
/// Fires while a pointer is moving over the `target` entity.
#[derive(Clone, PartialEq, Debug, Reflect)]
#[reflect(Clone, PartialEq)]
pub struct Move {
/// Information about the picking intersection.
pub hit: HitData,
/// The change in position since the last move event.
pub delta: Vec2,
}
/// Fires when the `target` entity receives a pointer pressed event followed by a pointer move event.
#[derive(Clone, PartialEq, Debug, Reflect)]
#[reflect(Clone, PartialEq)]
pub struct DragStart {
/// Pointer button pressed and moved to trigger this event.
pub button: PointerButton,
/// Information about the picking intersection.
pub hit: HitData,
}
/// Fires while the `target` entity is being dragged.
#[derive(Clone, PartialEq, Debug, Reflect)]
#[reflect(Clone, PartialEq)]
pub struct Drag {
/// Pointer button pressed and moved to trigger this event.
pub button: PointerButton,
/// The total distance vector of a drag, measured from drag start to the current position.
pub distance: Vec2,
/// The change in position since the last drag event.
pub delta: Vec2,
}
/// Fires when a pointer is dragging the `target` entity and a pointer released event is received.
#[derive(Clone, PartialEq, Debug, Reflect)]
#[reflect(Clone, PartialEq)]
pub struct DragEnd {
/// Pointer button pressed, moved, and released to trigger this event.
pub button: PointerButton,
/// The vector of drag movement measured from start to final pointer position.
pub distance: Vec2,
}
/// Fires when a pointer dragging the `dragged` entity enters the `target` entity.
#[derive(Clone, PartialEq, Debug, Reflect)]
#[reflect(Clone, PartialEq)]
pub struct DragEnter {
/// Pointer button pressed to enter drag.
pub button: PointerButton,
/// The entity that was being dragged when the pointer entered the `target` entity.
pub dragged: Entity,
/// Information about the picking intersection.
pub hit: HitData,
}
/// Fires while the `dragged` entity is being dragged over the `target` entity.
#[derive(Clone, PartialEq, Debug, Reflect)]
#[reflect(Clone, PartialEq)]
pub struct DragOver {
/// Pointer button pressed while dragging over.
pub button: PointerButton,
/// The entity that was being dragged when the pointer was over the `target` entity.
pub dragged: Entity,
/// Information about the picking intersection.
pub hit: HitData,
}
/// Fires when a pointer dragging the `dragged` entity leaves the `target` entity.
#[derive(Clone, PartialEq, Debug, Reflect)]
#[reflect(Clone, PartialEq)]
pub struct DragLeave {
/// Pointer button pressed while leaving drag.
pub button: PointerButton,
/// The entity that was being dragged when the pointer left the `target` entity.
pub dragged: Entity,
/// Information about the latest prior picking intersection.
pub hit: HitData,
}
/// Fires when a pointer drops the `dropped` entity onto the `target` entity.
#[derive(Clone, PartialEq, Debug, Reflect)]
#[reflect(Clone, PartialEq)]
pub struct DragDrop {
/// Pointer button released to drop.
pub button: PointerButton,
/// The entity that was dropped onto the `target` entity.
pub dropped: Entity,
/// Information about the picking intersection.
pub hit: HitData,
}
/// Dragging state.
#[derive(Clone, PartialEq, Debug, Reflect)]
#[reflect(Clone, PartialEq)]
pub struct DragEntry {
/// The position of the pointer at drag start.
pub start_pos: Vec2,
/// The latest position of the pointer during this drag, used to compute deltas.
pub latest_pos: Vec2,
}
/// Fires while a pointer is scrolling over the `target` entity.
#[derive(Clone, PartialEq, Debug, Reflect)]
#[reflect(Clone, PartialEq)]
pub struct Scroll {
/// The mouse scroll unit.
pub unit: MouseScrollUnit,
/// The horizontal scroll value.
pub x: f32,
/// The vertical scroll value.
pub y: f32,
/// Information about the picking intersection.
pub hit: HitData,
}
/// An entry in the cache that drives the `pointer_events` system, storing additional data
/// about pointer button presses.
#[derive(Debug, Clone, Default)]
pub struct PointerButtonState {
/// Stores the press location and start time for each button currently being pressed by the pointer.
pub pressing: HashMap<Entity, (Location, Instant, HitData)>,
/// Stores the starting and current locations for each entity currently being dragged by the pointer.
pub dragging: HashMap<Entity, DragEntry>,
/// Stores the hit data for each entity currently being dragged over by the pointer.
pub dragging_over: HashMap<Entity, HitData>,
}
/// State for all pointers.
#[derive(Debug, Clone, Default, Resource)]
pub struct PointerState {
/// Pressing and dragging state, organized by pointer and button.
pub pointer_buttons: HashMap<(PointerId, PointerButton), PointerButtonState>,
}
impl PointerState {
/// Retrieves the current state for a specific pointer and button, if it has been created.
pub fn get(&self, pointer_id: PointerId, button: PointerButton) -> Option<&PointerButtonState> {
self.pointer_buttons.get(&(pointer_id, button))
}
/// Provides write access to the state of a pointer and button, creating it if it does not yet exist.
pub fn get_mut(
&mut self,
pointer_id: PointerId,
button: PointerButton,
) -> &mut PointerButtonState {
self.pointer_buttons
.entry((pointer_id, button))
.or_default()
}
/// Clears all the data associated with all of the buttons on a pointer. Does not free the underlying memory.
pub fn clear(&mut self, pointer_id: PointerId) {
for button in PointerButton::iter() {
if let Some(state) = self.pointer_buttons.get_mut(&(pointer_id, button)) {
state.pressing.clear();
state.dragging.clear();
state.dragging_over.clear();
}
}
}
}
/// A helper system param for accessing the picking event writers.
#[derive(SystemParam)]
pub struct PickingEventWriters<'w> {
cancel_events: EventWriter<'w, Pointer<Cancel>>,
click_events: EventWriter<'w, Pointer<Click>>,
pressed_events: EventWriter<'w, Pointer<Pressed>>,
drag_drop_events: EventWriter<'w, Pointer<DragDrop>>,
drag_end_events: EventWriter<'w, Pointer<DragEnd>>,
drag_enter_events: EventWriter<'w, Pointer<DragEnter>>,
drag_events: EventWriter<'w, Pointer<Drag>>,
drag_leave_events: EventWriter<'w, Pointer<DragLeave>>,
drag_over_events: EventWriter<'w, Pointer<DragOver>>,
drag_start_events: EventWriter<'w, Pointer<DragStart>>,
scroll_events: EventWriter<'w, Pointer<Scroll>>,
move_events: EventWriter<'w, Pointer<Move>>,
out_events: EventWriter<'w, Pointer<Out>>,
over_events: EventWriter<'w, Pointer<Over>>,
released_events: EventWriter<'w, Pointer<Released>>,
}
/// Dispatches interaction events to the target entities.
///
/// Within a single frame, events are dispatched in the following order:
/// + [`Out`] → [`DragLeave`].
/// + [`DragEnter`] → [`Over`].
/// + Any number of any of the following:
/// + For each movement: [`DragStart`] → [`Drag`] → [`DragOver`] → [`Move`].
/// + For each button press: [`Pressed`] or [`Click`] → [`Released`] → [`DragDrop`] → [`DragEnd`] → [`DragLeave`].
/// + For each pointer cancellation: [`Cancel`].
///
/// Additionally, across multiple frames, the following are also strictly
/// ordered by the interaction state machine:
/// + When a pointer moves over the target:
/// [`Over`], [`Move`], [`Out`].
/// + When a pointer presses buttons on the target:
/// [`Pressed`], [`Click`], [`Released`].
/// + When a pointer drags the target:
/// [`DragStart`], [`Drag`], [`DragEnd`].
/// + When a pointer drags something over the target:
/// [`DragEnter`], [`DragOver`], [`DragDrop`], [`DragLeave`].
/// + When a pointer is canceled:
/// No other events will follow the [`Cancel`] event for that pointer.
///
/// Two events -- [`Over`] and [`Out`] -- are driven only by the [`HoverMap`].
/// The rest rely on additional data from the [`PointerInput`] event stream. To
/// receive these events for a custom pointer, you must add [`PointerInput`]
/// events.
///
/// When the pointer goes from hovering entity A to entity B, entity A will
/// receive [`Out`] and then entity B will receive [`Over`]. No entity will ever
/// receive both an [`Over`] and and a [`Out`] event during the same frame.
///
/// When we account for event bubbling, this is no longer true. When the hovering focus shifts
/// between children, parent entities may receive redundant [`Out`] → [`Over`] pairs.
/// In the context of UI, this is especially problematic. Additional hierarchy-aware
/// events will be added in a future release.
///
/// Both [`Click`] and [`Released`] target the entity hovered in the *previous frame*,
/// rather than the current frame. This is because touch pointers hover nothing
/// on the frame they are released. The end effect is that these two events can
/// be received sequentially after an [`Out`] event (but always on the same frame
/// as the [`Out`] event).
///
/// Note: Though it is common for the [`PointerInput`] stream may contain
/// multiple pointer movements and presses each frame, the hover state is
/// determined only by the pointer's *final position*. Since the hover state
/// ultimately determines which entities receive events, this may mean that an
/// entity can receive events from before or after it was actually hovered.
pub fn pointer_events(
// Input
mut input_events: EventReader<PointerInput>,
// ECS State
pointers: Query<&PointerLocation>,
pointer_map: Res<PointerMap>,
hover_map: Res<HoverMap>,
previous_hover_map: Res<PreviousHoverMap>,
mut pointer_state: ResMut<PointerState>,
// Output
mut commands: Commands,
mut event_writers: PickingEventWriters,
) {
// Setup utilities
let now = Instant::now();
let pointer_location = |pointer_id: PointerId| {
pointer_map
.get_entity(pointer_id)
.and_then(|entity| pointers.get(entity).ok())
.and_then(|pointer| pointer.location.clone())
};
// If the entity was hovered by a specific pointer last frame...
for (pointer_id, hovered_entity, hit) in previous_hover_map
.iter()
.flat_map(|(id, hashmap)| hashmap.iter().map(|data| (*id, *data.0, data.1.clone())))
{
// ...but is now not being hovered by that same pointer...
if !hover_map
.get(&pointer_id)
.iter()
.any(|e| e.contains_key(&hovered_entity))
{
let Some(location) = pointer_location(pointer_id) else {
debug!(
"Unable to get location for pointer {:?} during pointer out",
pointer_id
);
continue;
};
// Always send Out events
let out_event = Pointer::new(
pointer_id,
location.clone(),
hovered_entity,
Out { hit: hit.clone() },
);
commands.trigger_targets(out_event.clone(), hovered_entity);
event_writers.out_events.write(out_event);
// Possibly send DragLeave events
for button in PointerButton::iter() {
let state = pointer_state.get_mut(pointer_id, button);
state.dragging_over.remove(&hovered_entity);
for drag_target in state.dragging.keys() {
let drag_leave_event = Pointer::new(
pointer_id,
location.clone(),
hovered_entity,
DragLeave {
button,
dragged: *drag_target,
hit: hit.clone(),
},
);
commands.trigger_targets(drag_leave_event.clone(), hovered_entity);
event_writers.drag_leave_events.write(drag_leave_event);
}
}
}
}
// If the entity is hovered...
for (pointer_id, hovered_entity, hit) in hover_map
.iter()
.flat_map(|(id, hashmap)| hashmap.iter().map(|data| (*id, *data.0, data.1.clone())))
{
// ...but was not hovered last frame...
if !previous_hover_map
.get(&pointer_id)
.iter()
.any(|e| e.contains_key(&hovered_entity))
{
let Some(location) = pointer_location(pointer_id) else {
debug!(
"Unable to get location for pointer {:?} during pointer over",
pointer_id
);
continue;
};
// Possibly send DragEnter events
for button in PointerButton::iter() {
let state = pointer_state.get_mut(pointer_id, button);
for drag_target in state
.dragging
.keys()
.filter(|&&drag_target| hovered_entity != drag_target)
{
state.dragging_over.insert(hovered_entity, hit.clone());
let drag_enter_event = Pointer::new(
pointer_id,
location.clone(),
hovered_entity,
DragEnter {
button,
dragged: *drag_target,
hit: hit.clone(),
},
);
commands.trigger_targets(drag_enter_event.clone(), hovered_entity);
event_writers.drag_enter_events.write(drag_enter_event);
}
}
// Always send Over events
let over_event = Pointer::new(
pointer_id,
location.clone(),
hovered_entity,
Over { hit: hit.clone() },
);
commands.trigger_targets(over_event.clone(), hovered_entity);
event_writers.over_events.write(over_event);
}
}
// Dispatch input events...
for PointerInput {
pointer_id,
location,
action,
} in input_events.read().cloned()
{
match action {
PointerAction::Press(button) => {
let state = pointer_state.get_mut(pointer_id, button);
// If it's a press, emit a Pressed event and mark the hovered entities as pressed
for (hovered_entity, hit) in hover_map
.get(&pointer_id)
.iter()
.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))
{
let pressed_event = Pointer::new(
pointer_id,
location.clone(),
hovered_entity,
Pressed {
button,
hit: hit.clone(),
},
);
commands.trigger_targets(pressed_event.clone(), hovered_entity);
event_writers.pressed_events.write(pressed_event);
// Also insert the press into the state
state
.pressing
.insert(hovered_entity, (location.clone(), now, hit));
}
}
PointerAction::Release(button) => {
let state = pointer_state.get_mut(pointer_id, button);
// Emit Click and Up events on all the previously hovered entities.
for (hovered_entity, hit) in previous_hover_map
.get(&pointer_id)
.iter()
.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))
{
// If this pointer previously pressed the hovered entity, emit a Click event
if let Some((_, press_instant, _)) = state.pressing.get(&hovered_entity) {
let click_event = Pointer::new(
pointer_id,
location.clone(),
hovered_entity,
Click {
button,
hit: hit.clone(),
duration: now - *press_instant,
},
);
commands.trigger_targets(click_event.clone(), hovered_entity);
event_writers.click_events.write(click_event);
}
// Always send the Released event
let released_event = Pointer::new(
pointer_id,
location.clone(),
hovered_entity,
Released {
button,
hit: hit.clone(),
},
);
commands.trigger_targets(released_event.clone(), hovered_entity);
event_writers.released_events.write(released_event);
}
// Then emit the drop events.
for (drag_target, drag) in state.dragging.drain() {
// Emit DragDrop
for (dragged_over, hit) in state.dragging_over.iter() {
let drag_drop_event = Pointer::new(
pointer_id,
location.clone(),
*dragged_over,
DragDrop {
button,
dropped: drag_target,
hit: hit.clone(),
},
);
commands.trigger_targets(drag_drop_event.clone(), *dragged_over);
event_writers.drag_drop_events.write(drag_drop_event);
}
// Emit DragEnd
let drag_end_event = Pointer::new(
pointer_id,
location.clone(),
drag_target,
DragEnd {
button,
distance: drag.latest_pos - drag.start_pos,
},
);
commands.trigger_targets(drag_end_event.clone(), drag_target);
event_writers.drag_end_events.write(drag_end_event);
// Emit DragLeave
for (dragged_over, hit) in state.dragging_over.iter() {
let drag_leave_event = Pointer::new(
pointer_id,
location.clone(),
*dragged_over,
DragLeave {
button,
dragged: drag_target,
hit: hit.clone(),
},
);
commands.trigger_targets(drag_leave_event.clone(), *dragged_over);
event_writers.drag_leave_events.write(drag_leave_event);
}
}
// Finally, we can clear the state of everything relating to presses or drags.
state.pressing.clear();
state.dragging.clear();
state.dragging_over.clear();
}
// Moved
PointerAction::Move { delta } => {
if delta == Vec2::ZERO {
continue; // If delta is zero, the following events will not be triggered.
}
// Triggers during movement even if not over an entity
for button in PointerButton::iter() {
let state = pointer_state.get_mut(pointer_id, button);
// Emit DragEntry and DragStart the first time we move while pressing an entity
for (press_target, (location, _, hit)) in state.pressing.iter() {
if state.dragging.contains_key(press_target) {
continue; // This entity is already logged as being dragged
}
state.dragging.insert(
*press_target,
DragEntry {
start_pos: location.position,
latest_pos: location.position,
},
);
let drag_start_event = Pointer::new(
pointer_id,
location.clone(),
*press_target,
DragStart {
button,
hit: hit.clone(),
},
);
commands.trigger_targets(drag_start_event.clone(), *press_target);
event_writers.drag_start_events.write(drag_start_event);
}
// Emit Drag events to the entities we are dragging
for (drag_target, drag) in state.dragging.iter_mut() {
let delta = location.position - drag.latest_pos;
if delta == Vec2::ZERO {
continue; // No need to emit a Drag event if there is no movement
}
let drag_event = Pointer::new(
pointer_id,
location.clone(),
*drag_target,
Drag {
button,
distance: location.position - drag.start_pos,
delta,
},
);
commands.trigger_targets(drag_event.clone(), *drag_target);
event_writers.drag_events.write(drag_event);
// Update drag position
drag.latest_pos = location.position;
// Emit corresponding DragOver to the hovered entities
for (hovered_entity, hit) in hover_map
.get(&pointer_id)
.iter()
.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
.filter(|(hovered_entity, _)| *hovered_entity != *drag_target)
{
let drag_over_event = Pointer::new(
pointer_id,
location.clone(),
hovered_entity,
DragOver {
button,
dragged: *drag_target,
hit: hit.clone(),
},
);
commands.trigger_targets(drag_over_event.clone(), hovered_entity);
event_writers.drag_over_events.write(drag_over_event);
}
}
}
for (hovered_entity, hit) in hover_map
.get(&pointer_id)
.iter()
.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
{
// Emit Move events to the entities we are hovering
let move_event = Pointer::new(
pointer_id,
location.clone(),
hovered_entity,
Move {
hit: hit.clone(),
delta,
},
);
commands.trigger_targets(move_event.clone(), hovered_entity);
event_writers.move_events.write(move_event);
}
}
PointerAction::Scroll { x, y, unit } => {
for (hovered_entity, hit) in hover_map
.get(&pointer_id)
.iter()
.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.clone())))
{
// Emit Scroll events to the entities we are hovering
let scroll_event = Pointer::new(
pointer_id,
location.clone(),
hovered_entity,
Scroll {
unit,
x,
y,
hit: hit.clone(),
},
);
commands.trigger_targets(scroll_event.clone(), hovered_entity);
event_writers.scroll_events.write(scroll_event);
}
}
// Canceled
PointerAction::Cancel => {
// Emit a Cancel to the hovered entity.
for (hovered_entity, hit) in hover_map
.get(&pointer_id)
.iter()
.flat_map(|h| h.iter().map(|(entity, data)| (*entity, data.to_owned())))
{
let cancel_event =
Pointer::new(pointer_id, location.clone(), hovered_entity, Cancel { hit });
commands.trigger_targets(cancel_event.clone(), hovered_entity);
event_writers.cancel_events.write(cancel_event);
}
// Clear the state for the canceled pointer
pointer_state.clear(pointer_id);
}
}
}
}

277
vendor/bevy_picking/src/hover.rs vendored Normal file
View File

@@ -0,0 +1,277 @@
//! Determines which entities are being hovered by which pointers.
//!
//! The most important type in this module is the [`HoverMap`], which maps pointers to the entities
//! they are hovering over.
use alloc::collections::BTreeMap;
use core::fmt::Debug;
use std::collections::HashSet;
use crate::{
backend::{self, HitData},
pointer::{PointerAction, PointerId, PointerInput, PointerInteraction, PointerPress},
Pickable,
};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::prelude::*;
use bevy_math::FloatOrd;
use bevy_platform::collections::HashMap;
use bevy_reflect::prelude::*;
type DepthSortedHits = Vec<(Entity, HitData)>;
/// Events returned from backends can be grouped with an order field. This allows picking to work
/// with multiple layers of rendered output to the same render target.
type PickLayer = FloatOrd;
/// Maps [`PickLayer`]s to the map of entities within that pick layer, sorted by depth.
type LayerMap = BTreeMap<PickLayer, DepthSortedHits>;
/// Maps Pointers to a [`LayerMap`]. Note this is much more complex than the [`HoverMap`] because
/// this data structure is used to sort entities by layer then depth for every pointer.
type OverMap = HashMap<PointerId, LayerMap>;
/// The source of truth for all hover state. This is used to determine what events to send, and what
/// state components should be in.
///
/// Maps pointers to the entities they are hovering over.
///
/// "Hovering" refers to the *hover* state, which is not the same as whether or not a picking
/// backend is reporting hits between a pointer and an entity. A pointer is "hovering" an entity
/// only if the pointer is hitting the entity (as reported by a picking backend) *and* no entities
/// between it and the pointer block interactions.
///
/// For example, if a pointer is hitting a UI button and a 3d mesh, but the button is in front of
/// the mesh, the UI button will be hovered, but the mesh will not. Unless, the [`Pickable`]
/// component is present with [`should_block_lower`](Pickable::should_block_lower) set to `false`.
///
/// # Advanced Users
///
/// If you want to completely replace the provided picking events or state produced by this plugin,
/// you can use this resource to do that. All of the event systems for picking are built *on top of*
/// this authoritative hover state, and you can do the same. You can also use the
/// [`PreviousHoverMap`] as a robust way of determining changes in hover state from the previous
/// update.
#[derive(Debug, Deref, DerefMut, Default, Resource)]
pub struct HoverMap(pub HashMap<PointerId, HashMap<Entity, HitData>>);
/// The previous state of the hover map, used to track changes to hover state.
#[derive(Debug, Deref, DerefMut, Default, Resource)]
pub struct PreviousHoverMap(pub HashMap<PointerId, HashMap<Entity, HitData>>);
/// Coalesces all data from inputs and backends to generate a map of the currently hovered entities.
/// This is the final focusing step to determine which entity the pointer is hovering over.
pub fn generate_hovermap(
// Inputs
pickable: Query<&Pickable>,
pointers: Query<&PointerId>,
mut under_pointer: EventReader<backend::PointerHits>,
mut pointer_input: EventReader<PointerInput>,
// Local
mut over_map: Local<OverMap>,
// Output
mut hover_map: ResMut<HoverMap>,
mut previous_hover_map: ResMut<PreviousHoverMap>,
) {
reset_maps(
&mut hover_map,
&mut previous_hover_map,
&mut over_map,
&pointers,
);
build_over_map(&mut under_pointer, &mut over_map, &mut pointer_input);
build_hover_map(&pointers, pickable, &over_map, &mut hover_map);
}
/// Clear non-empty local maps, reusing allocated memory.
fn reset_maps(
hover_map: &mut HoverMap,
previous_hover_map: &mut PreviousHoverMap,
over_map: &mut OverMap,
pointers: &Query<&PointerId>,
) {
// Swap the previous and current hover maps. This results in the previous values being stored in
// `PreviousHoverMap`. Swapping is okay because we clear the `HoverMap` which now holds stale
// data. This process is done without any allocations.
core::mem::swap(&mut previous_hover_map.0, &mut hover_map.0);
for entity_set in hover_map.values_mut() {
entity_set.clear();
}
for layer_map in over_map.values_mut() {
layer_map.clear();
}
// Clear pointers from the maps if they have been removed.
let active_pointers: Vec<PointerId> = pointers.iter().copied().collect();
hover_map.retain(|pointer, _| active_pointers.contains(pointer));
over_map.retain(|pointer, _| active_pointers.contains(pointer));
}
/// Build an ordered map of entities that are under each pointer
fn build_over_map(
backend_events: &mut EventReader<backend::PointerHits>,
pointer_over_map: &mut Local<OverMap>,
pointer_input: &mut EventReader<PointerInput>,
) {
let cancelled_pointers: HashSet<PointerId> = pointer_input
.read()
.filter_map(|p| {
if let PointerAction::Cancel = p.action {
Some(p.pointer_id)
} else {
None
}
})
.collect();
for entities_under_pointer in backend_events
.read()
.filter(|e| !cancelled_pointers.contains(&e.pointer))
{
let pointer = entities_under_pointer.pointer;
let layer_map = pointer_over_map.entry(pointer).or_default();
for (entity, pick_data) in entities_under_pointer.picks.iter() {
let layer = entities_under_pointer.order;
let hits = layer_map.entry(FloatOrd(layer)).or_default();
hits.push((*entity, pick_data.clone()));
}
}
for layers in pointer_over_map.values_mut() {
for hits in layers.values_mut() {
hits.sort_by_key(|(_, hit)| FloatOrd(hit.depth));
}
}
}
/// Build an unsorted set of hovered entities, accounting for depth, layer, and [`Pickable`]. Note
/// that unlike the pointer map, this uses [`Pickable`] to determine if lower entities receive hover
/// focus. Often, only a single entity per pointer will be hovered.
fn build_hover_map(
pointers: &Query<&PointerId>,
pickable: Query<&Pickable>,
over_map: &Local<OverMap>,
// Output
hover_map: &mut HoverMap,
) {
for pointer_id in pointers.iter() {
let pointer_entity_set = hover_map.entry(*pointer_id).or_default();
if let Some(layer_map) = over_map.get(pointer_id) {
// Note we reverse here to start from the highest layer first.
for (entity, pick_data) in layer_map.values().rev().flatten() {
if let Ok(pickable) = pickable.get(*entity) {
if pickable.is_hoverable {
pointer_entity_set.insert(*entity, pick_data.clone());
}
if pickable.should_block_lower {
break;
}
} else {
pointer_entity_set.insert(*entity, pick_data.clone()); // Emit events by default
break; // Entities block by default so we break out of the loop
}
}
}
}
}
/// A component that aggregates picking interaction state of this entity across all pointers.
///
/// Unlike bevy's `Interaction` component, this is an aggregate of the state of all pointers
/// interacting with this entity. Aggregation is done by taking the interaction with the highest
/// precedence.
///
/// For example, if we have an entity that is being hovered by one pointer, and pressed by another,
/// the entity will be considered pressed. If that entity is instead being hovered by both pointers,
/// it will be considered hovered.
#[derive(Component, Copy, Clone, Default, Eq, PartialEq, Debug, Reflect)]
#[reflect(Component, Default, PartialEq, Debug, Clone)]
pub enum PickingInteraction {
/// The entity is being pressed down by a pointer.
Pressed = 2,
/// The entity is being hovered by a pointer.
Hovered = 1,
/// No pointers are interacting with this entity.
#[default]
None = 0,
}
/// Uses [`HoverMap`] changes to update [`PointerInteraction`] and [`PickingInteraction`] components.
pub fn update_interactions(
// Input
hover_map: Res<HoverMap>,
previous_hover_map: Res<PreviousHoverMap>,
// Outputs
mut commands: Commands,
mut pointers: Query<(&PointerId, &PointerPress, &mut PointerInteraction)>,
mut interact: Query<&mut PickingInteraction>,
) {
// Clear all previous hover data from pointers and entities
for (pointer, _, mut pointer_interaction) in &mut pointers {
pointer_interaction.sorted_entities.clear();
if let Some(previously_hovered_entities) = previous_hover_map.get(pointer) {
for entity in previously_hovered_entities.keys() {
if let Ok(mut interaction) = interact.get_mut(*entity) {
*interaction = PickingInteraction::None;
}
}
}
}
// Create a map to hold the aggregated interaction for each entity. This is needed because we
// need to be able to insert the interaction component on entities if they do not exist. To do
// so we need to know the final aggregated interaction state to avoid the scenario where we set
// an entity to `Pressed`, then overwrite that with a lower precedent like `Hovered`.
let mut new_interaction_state = HashMap::<Entity, PickingInteraction>::default();
for (pointer, pointer_press, mut pointer_interaction) in &mut pointers {
if let Some(pointers_hovered_entities) = hover_map.get(pointer) {
// Insert a sorted list of hit entities into the pointer's interaction component.
let mut sorted_entities: Vec<_> = pointers_hovered_entities.clone().drain().collect();
sorted_entities.sort_by_key(|(_, hit)| FloatOrd(hit.depth));
pointer_interaction.sorted_entities = sorted_entities;
for hovered_entity in pointers_hovered_entities.iter().map(|(entity, _)| entity) {
merge_interaction_states(pointer_press, hovered_entity, &mut new_interaction_state);
}
}
}
// Take the aggregated entity states and update or insert the component if missing.
for (hovered_entity, new_interaction) in new_interaction_state.drain() {
if let Ok(mut interaction) = interact.get_mut(hovered_entity) {
*interaction = new_interaction;
} else if let Ok(mut entity_commands) = commands.get_entity(hovered_entity) {
entity_commands.try_insert(new_interaction);
}
}
}
/// Merge the interaction state of this entity into the aggregated map.
fn merge_interaction_states(
pointer_press: &PointerPress,
hovered_entity: &Entity,
new_interaction_state: &mut HashMap<Entity, PickingInteraction>,
) {
let new_interaction = match pointer_press.is_any_pressed() {
true => PickingInteraction::Pressed,
false => PickingInteraction::Hovered,
};
if let Some(old_interaction) = new_interaction_state.get_mut(hovered_entity) {
// Only update if the new value has a higher precedence than the old value.
if *old_interaction != new_interaction
&& matches!(
(*old_interaction, new_interaction),
(PickingInteraction::Hovered, PickingInteraction::Pressed)
| (PickingInteraction::None, PickingInteraction::Pressed)
| (PickingInteraction::None, PickingInteraction::Hovered)
)
{
*old_interaction = new_interaction;
}
} else {
new_interaction_state.insert(*hovered_entity, new_interaction);
}
}

279
vendor/bevy_picking/src/input.rs vendored Normal file
View File

@@ -0,0 +1,279 @@
//! This module provides unsurprising default inputs to `bevy_picking` through [`PointerInput`].
//! The included systems are responsible for sending mouse and touch inputs to their
//! respective `Pointer`s.
//!
//! Because this has it's own plugin, it's easy to omit it, and provide your own inputs as
//! needed. Because `Pointer`s aren't coupled to the underlying input hardware, you can easily mock
//! inputs, and allow users full accessibility to map whatever inputs they need to pointer input.
//!
//! If, for example, you wanted to add support for VR input, all you need to do is spawn a pointer
//! entity with a custom [`PointerId`], and write a system
//! that updates its position. If you want this to work properly with the existing interaction events,
//! you need to be sure that you also write a [`PointerInput`] event stream.
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bevy_input::{
mouse::MouseWheel,
prelude::*,
touch::{TouchInput, TouchPhase},
ButtonState,
};
use bevy_math::Vec2;
use bevy_platform::collections::{HashMap, HashSet};
use bevy_reflect::prelude::*;
use bevy_render::camera::RenderTarget;
use bevy_window::{PrimaryWindow, WindowEvent, WindowRef};
use tracing::debug;
use crate::pointer::{
Location, PointerAction, PointerButton, PointerId, PointerInput, PointerLocation,
};
use crate::PickSet;
/// The picking input prelude.
///
/// This includes the most common types in this module, re-exported for your convenience.
pub mod prelude {
pub use crate::input::PointerInputPlugin;
}
/// Adds mouse and touch inputs for picking pointers to your app. This is a default input plugin,
/// that you can replace with your own plugin as needed.
///
/// [`crate::PickingPlugin::is_input_enabled`] can be used to toggle whether
/// the core picking plugin processes the inputs sent by this, or other input plugins, in one place.
///
/// This plugin contains several settings, and is added to the world as a resource after initialization.
/// You can configure pointer input settings at runtime by accessing the resource.
#[derive(Copy, Clone, Resource, Debug, Reflect)]
#[reflect(Resource, Default, Clone)]
pub struct PointerInputPlugin {
/// Should touch inputs be updated?
pub is_touch_enabled: bool,
/// Should mouse inputs be updated?
pub is_mouse_enabled: bool,
}
impl PointerInputPlugin {
fn is_mouse_enabled(state: Res<Self>) -> bool {
state.is_mouse_enabled
}
fn is_touch_enabled(state: Res<Self>) -> bool {
state.is_touch_enabled
}
}
impl Default for PointerInputPlugin {
fn default() -> Self {
Self {
is_touch_enabled: true,
is_mouse_enabled: true,
}
}
}
impl Plugin for PointerInputPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(*self)
.add_systems(Startup, spawn_mouse_pointer)
.add_systems(
First,
(
mouse_pick_events.run_if(PointerInputPlugin::is_mouse_enabled),
touch_pick_events.run_if(PointerInputPlugin::is_touch_enabled),
)
.chain()
.in_set(PickSet::Input),
)
.add_systems(
Last,
deactivate_touch_pointers.run_if(PointerInputPlugin::is_touch_enabled),
)
.register_type::<Self>()
.register_type::<PointerInputPlugin>();
}
}
/// Spawns the default mouse pointer.
pub fn spawn_mouse_pointer(mut commands: Commands) {
commands.spawn(PointerId::Mouse);
}
/// Sends mouse pointer events to be processed by the core plugin
pub fn mouse_pick_events(
// Input
mut window_events: EventReader<WindowEvent>,
primary_window: Query<Entity, With<PrimaryWindow>>,
// Locals
mut cursor_last: Local<Vec2>,
// Output
mut pointer_events: EventWriter<PointerInput>,
) {
for window_event in window_events.read() {
match window_event {
// Handle cursor movement events
WindowEvent::CursorMoved(event) => {
let location = Location {
target: match RenderTarget::Window(WindowRef::Entity(event.window))
.normalize(primary_window.single().ok())
{
Some(target) => target,
None => continue,
},
position: event.position,
};
pointer_events.write(PointerInput::new(
PointerId::Mouse,
location,
PointerAction::Move {
delta: event.position - *cursor_last,
},
));
*cursor_last = event.position;
}
// Handle mouse button press events
WindowEvent::MouseButtonInput(input) => {
let location = Location {
target: match RenderTarget::Window(WindowRef::Entity(input.window))
.normalize(primary_window.single().ok())
{
Some(target) => target,
None => continue,
},
position: *cursor_last,
};
let button = match input.button {
MouseButton::Left => PointerButton::Primary,
MouseButton::Right => PointerButton::Secondary,
MouseButton::Middle => PointerButton::Middle,
MouseButton::Other(_) | MouseButton::Back | MouseButton::Forward => continue,
};
let action = match input.state {
ButtonState::Pressed => PointerAction::Press(button),
ButtonState::Released => PointerAction::Release(button),
};
pointer_events.write(PointerInput::new(PointerId::Mouse, location, action));
}
WindowEvent::MouseWheel(event) => {
let MouseWheel { unit, x, y, window } = *event;
let location = Location {
target: match RenderTarget::Window(WindowRef::Entity(window))
.normalize(primary_window.single().ok())
{
Some(target) => target,
None => continue,
},
position: *cursor_last,
};
let action = PointerAction::Scroll { x, y, unit };
pointer_events.write(PointerInput::new(PointerId::Mouse, location, action));
}
_ => {}
}
}
}
/// Sends touch pointer events to be consumed by the core plugin
pub fn touch_pick_events(
// Input
mut window_events: EventReader<WindowEvent>,
primary_window: Query<Entity, With<PrimaryWindow>>,
// Locals
mut touch_cache: Local<HashMap<u64, TouchInput>>,
// Output
mut commands: Commands,
mut pointer_events: EventWriter<PointerInput>,
) {
for window_event in window_events.read() {
if let WindowEvent::TouchInput(touch) = window_event {
let pointer = PointerId::Touch(touch.id);
let location = Location {
target: match RenderTarget::Window(WindowRef::Entity(touch.window))
.normalize(primary_window.single().ok())
{
Some(target) => target,
None => continue,
},
position: touch.position,
};
match touch.phase {
TouchPhase::Started => {
debug!("Spawning pointer {:?}", pointer);
commands.spawn((pointer, PointerLocation::new(location.clone())));
pointer_events.write(PointerInput::new(
pointer,
location,
PointerAction::Press(PointerButton::Primary),
));
touch_cache.insert(touch.id, *touch);
}
TouchPhase::Moved => {
// Send a move event only if it isn't the same as the last one
if let Some(last_touch) = touch_cache.get(&touch.id) {
if last_touch == touch {
continue;
}
pointer_events.write(PointerInput::new(
pointer,
location,
PointerAction::Move {
delta: touch.position - last_touch.position,
},
));
}
touch_cache.insert(touch.id, *touch);
}
TouchPhase::Ended => {
pointer_events.write(PointerInput::new(
pointer,
location,
PointerAction::Release(PointerButton::Primary),
));
touch_cache.remove(&touch.id);
}
TouchPhase::Canceled => {
pointer_events.write(PointerInput::new(
pointer,
location,
PointerAction::Cancel,
));
touch_cache.remove(&touch.id);
}
}
}
}
}
/// Deactivates unused touch pointers.
///
/// Because each new touch gets assigned a new ID, we need to remove the pointers associated with
/// touches that are no longer active.
pub fn deactivate_touch_pointers(
mut commands: Commands,
mut despawn_list: Local<HashSet<(Entity, PointerId)>>,
pointers: Query<(Entity, &PointerId)>,
mut touches: EventReader<TouchInput>,
) {
for touch in touches.read() {
if let TouchPhase::Ended | TouchPhase::Canceled = touch.phase {
for (entity, pointer) in &pointers {
if pointer.get_touch_id() == Some(touch.id) {
despawn_list.insert((entity, *pointer));
}
}
}
}
// A hash set is used to prevent despawning the same entity twice.
for (entity, pointer) in despawn_list.drain() {
debug!("Despawning pointer {:?}", pointer);
commands.entity(entity).despawn();
}
}

433
vendor/bevy_picking/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,433 @@
//! This crate provides 'picking' capabilities for the Bevy game engine, allowing pointers to
//! interact with entities using hover, click, and drag events.
//!
//! ## Overview
//!
//! In the simplest case, this plugin allows you to click on things in the scene. However, it also
//! allows you to express more complex interactions, like detecting when a touch input drags a UI
//! element and drops it on a 3d mesh rendered to a different camera.
//!
//! Pointer events bubble up the entity hierarchy and can be used with observers, allowing you to
//! succinctly express rich interaction behaviors by attaching pointer callbacks to entities:
//!
//! ```rust
//! # use bevy_ecs::prelude::*;
//! # use bevy_picking::prelude::*;
//! # #[derive(Component)]
//! # struct MyComponent;
//! # let mut world = World::new();
//! world.spawn(MyComponent)
//! .observe(|mut trigger: Trigger<Pointer<Click>>| {
//! println!("I was just clicked!");
//! // Get the underlying pointer event data
//! let click_event: &Pointer<Click> = trigger.event();
//! // Stop the event from bubbling up the entity hierarchy
//! trigger.propagate(false);
//! });
//! ```
//!
//! At its core, this crate provides a robust abstraction for computing picking state regardless of
//! pointing devices, or what you are hit testing against. It is designed to work with any input,
//! including mouse, touch, pens, or virtual pointers controlled by gamepads.
//!
//! ## Expressive Events
//!
//! Although the events in this module (see [`events`]) can be listened to with normal
//! `EventReader`s, using observers is often more expressive, with less boilerplate. This is because
//! observers allow you to attach event handling logic to specific entities, as well as make use of
//! event bubbling.
//!
//! When events are generated, they bubble up the entity hierarchy starting from their target, until
//! they reach the root or bubbling is halted with a call to
//! [`Trigger::propagate`](bevy_ecs::observer::Trigger::propagate). See [`Observer`] for details.
//!
//! This allows you to run callbacks when any children of an entity are interacted with, and leads
//! to succinct, expressive code:
//!
//! ```
//! # use bevy_ecs::prelude::*;
//! # use bevy_transform::prelude::*;
//! # use bevy_picking::prelude::*;
//! # #[derive(Event)]
//! # struct Greeting;
//! fn setup(mut commands: Commands) {
//! commands.spawn(Transform::default())
//! // Spawn your entity here, e.g. a Mesh.
//! // When dragged, mutate the `Transform` component on the dragged target entity:
//! .observe(|trigger: Trigger<Pointer<Drag>>, mut transforms: Query<&mut Transform>| {
//! let mut transform = transforms.get_mut(trigger.target()).unwrap();
//! let drag = trigger.event();
//! transform.rotate_local_y(drag.delta.x / 50.0);
//! })
//! .observe(|trigger: Trigger<Pointer<Click>>, mut commands: Commands| {
//! println!("Entity {} goes BOOM!", trigger.target());
//! commands.entity(trigger.target()).despawn();
//! })
//! .observe(|trigger: Trigger<Pointer<Over>>, mut events: EventWriter<Greeting>| {
//! events.write(Greeting);
//! });
//! }
//! ```
//!
//! ## Modularity
//!
//! #### Mix and Match Hit Testing Backends
//!
//! The plugin attempts to handle all the hard parts for you, all you need to do is tell it when a
//! pointer is hitting any entities. Multiple backends can be used at the same time! [Use this
//! simple API to write your own backend](crate::backend) in about 100 lines of code.
//!
//! #### Input Agnostic
//!
//! Picking provides a generic Pointer abstraction, which is useful for reacting to many different
//! types of input devices. Pointers can be controlled with anything, whether it's the included
//! mouse or touch inputs, or a custom gamepad input system you write yourself to control a virtual
//! pointer.
//!
//! ## Robustness
//!
//! In addition to these features, this plugin also correctly handles multitouch, multiple windows,
//! multiple cameras, viewports, and render layers. Using this as a library allows you to write a
//! picking backend that can interoperate with any other picking backend.
//!
//! # Getting Started
//!
//! TODO: This section will need to be re-written once more backends are introduced.
//!
//! #### Next Steps
//!
//! To learn more, take a look at the examples in the
//! [examples](https://github.com/bevyengine/bevy/tree/main/examples/picking). You can read the next
//! section to understand how the plugin works.
//!
//! # The Picking Pipeline
//!
//! This plugin is designed to be extremely modular. To do so, it works in well-defined stages that
//! form a pipeline, where events are used to pass data between each stage.
//!
//! #### Pointers ([`pointer`](mod@pointer))
//!
//! The first stage of the pipeline is to gather inputs and update pointers. This stage is
//! ultimately responsible for generating [`PointerInput`](pointer::PointerInput) events. The
//! provided crate does this automatically for mouse, touch, and pen inputs. If you wanted to
//! implement your own pointer, controlled by some other input, you can do that here. The ordering
//! of events within the [`PointerInput`](pointer::PointerInput) stream is meaningful for events
//! with the same [`PointerId`](pointer::PointerId), but not between different pointers.
//!
//! Because pointer positions and presses are driven by these events, you can use them to mock
//! inputs for testing.
//!
//! After inputs are generated, they are then collected to update the current
//! [`PointerLocation`](pointer::PointerLocation) for each pointer.
//!
//! #### Backend ([`backend`])
//!
//! A picking backend only has one job: reading [`PointerLocation`](pointer::PointerLocation)
//! components, and producing [`PointerHits`](backend::PointerHits). You can find all documentation
//! and types needed to implement a backend at [`backend`].
//!
//! You will eventually need to choose which picking backend(s) you want to use. This crate does not
//! supply any backends, and expects you to select some from the other bevy crates or the
//! third-party ecosystem.
//!
//! It's important to understand that you can mix and match backends! For example, you might have a
//! backend for your UI, and one for the 3d scene, with each being specialized for their purpose.
//! Bevy provides some backends out of the box, but you can even write your own. It's been made as
//! easy as possible intentionally; the `bevy_mod_raycast` backend is 50 lines of code.
//!
//! #### Hover ([`hover`])
//!
//! The next step is to use the data from the backends, combine and sort the results, and determine
//! what each cursor is hovering over, producing a [`HoverMap`](`crate::hover::HoverMap`). Note that
//! just because a pointer is over an entity, it is not necessarily *hovering* that entity. Although
//! multiple backends may be reporting that a pointer is hitting an entity, the hover system needs
//! to determine which entities are actually being hovered by this pointer based on the pick depth,
//! order of the backend, and the optional [`Pickable`] component of the entity. In other
//! words, if one entity is in front of another, usually only the topmost one will be hovered.
//!
//! #### Events ([`events`])
//!
//! In the final step, the high-level pointer events are generated, such as events that trigger when
//! a pointer hovers or clicks an entity. These simple events are then used to generate more complex
//! events for dragging and dropping.
//!
//! Because it is completely agnostic to the earlier stages of the pipeline, you can easily extend
//! the plugin with arbitrary backends and input methods, yet still use all the high level features.
#![deny(missing_docs)]
extern crate alloc;
pub mod backend;
pub mod events;
pub mod hover;
pub mod input;
#[cfg(feature = "bevy_mesh_picking_backend")]
pub mod mesh_picking;
pub mod pointer;
pub mod window;
use bevy_app::{prelude::*, PluginGroupBuilder};
use bevy_ecs::prelude::*;
use bevy_reflect::prelude::*;
/// The picking prelude.
///
/// This includes the most common types in this crate, re-exported for your convenience.
pub mod prelude {
#[cfg(feature = "bevy_mesh_picking_backend")]
#[doc(hidden)]
pub use crate::mesh_picking::{
ray_cast::{MeshRayCast, MeshRayCastSettings, RayCastBackfaces, RayCastVisibility},
MeshPickingCamera, MeshPickingPlugin, MeshPickingSettings,
};
#[doc(hidden)]
pub use crate::{
events::*, input::PointerInputPlugin, pointer::PointerButton, DefaultPickingPlugins,
InteractionPlugin, Pickable, PickingPlugin,
};
}
/// An optional component that marks an entity as usable by a backend, and overrides default
/// picking behavior for an entity.
///
/// This allows you to make an entity non-hoverable, or allow items below it to be hovered.
///
/// See the documentation on the fields for more details.
#[derive(Component, Debug, Clone, Reflect, PartialEq, Eq)]
#[reflect(Component, Default, Debug, PartialEq, Clone)]
pub struct Pickable {
/// Should this entity block entities below it from being picked?
///
/// This is useful if you want picking to continue hitting entities below this one. Normally,
/// only the topmost entity under a pointer can be hovered, but this setting allows the pointer
/// to hover multiple entities, from nearest to farthest, stopping as soon as it hits an entity
/// that blocks lower entities.
///
/// Note that the word "lower" here refers to entities that have been reported as hit by any
/// picking backend, but are at a lower depth than the current one. This is different from the
/// concept of event bubbling, as it works irrespective of the entity hierarchy.
///
/// For example, if a pointer is over a UI element, as well as a 3d mesh, backends will report
/// hits for both of these entities. Additionally, the hits will be sorted by the camera order,
/// so if the UI is drawing on top of the 3d mesh, the UI will be "above" the mesh. When hovering
/// is computed, the UI element will be checked first to see if it this field is set to block
/// lower entities. If it does (default), the hovering system will stop there, and only the UI
/// element will be marked as hovered. However, if this field is set to `false`, both the UI
/// element *and* the mesh will be marked as hovered.
///
/// Entities without the [`Pickable`] component will block by default.
pub should_block_lower: bool,
/// If this is set to `false` and `should_block_lower` is set to true, this entity will block
/// lower entities from being interacted and at the same time will itself not emit any events.
///
/// Note that the word "lower" here refers to entities that have been reported as hit by any
/// picking backend, but are at a lower depth than the current one. This is different from the
/// concept of event bubbling, as it works irrespective of the entity hierarchy.
///
/// For example, if a pointer is over a UI element, and this field is set to `false`, it will
/// not be marked as hovered, and consequently will not emit events nor will any picking
/// components mark it as hovered. This can be combined with the other field
/// [`Self::should_block_lower`], which is orthogonal to this one.
///
/// Entities without the [`Pickable`] component are hoverable by default.
pub is_hoverable: bool,
}
impl Pickable {
/// This entity will not block entities beneath it, nor will it emit events.
///
/// If a backend reports this entity as being hit, the picking plugin will completely ignore it.
pub const IGNORE: Self = Self {
should_block_lower: false,
is_hoverable: false,
};
}
impl Default for Pickable {
fn default() -> Self {
Self {
should_block_lower: true,
is_hoverable: true,
}
}
}
/// Groups the stages of the picking process under shared labels.
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
pub enum PickSet {
/// Produces pointer input events. In the [`First`] schedule.
Input,
/// Runs after input events are generated but before commands are flushed. In the [`First`]
/// schedule.
PostInput,
/// Receives and processes pointer input events. In the [`PreUpdate`] schedule.
ProcessInput,
/// Reads inputs and produces [`backend::PointerHits`]s. In the [`PreUpdate`] schedule.
Backend,
/// Reads [`backend::PointerHits`]s, and updates the hovermap, selection, and highlighting states. In
/// the [`PreUpdate`] schedule.
Hover,
/// Runs after all the [`PickSet::Hover`] systems are done, before event listeners are triggered. In the
/// [`PreUpdate`] schedule.
PostHover,
/// Runs after all other picking sets. In the [`PreUpdate`] schedule.
Last,
}
/// One plugin that contains the [`PointerInputPlugin`](input::PointerInputPlugin), [`PickingPlugin`]
/// and the [`InteractionPlugin`], this is probably the plugin that will be most used.
///
/// Note: for any of these plugins to work, they require a picking backend to be active,
/// The picking backend is responsible to turn an input, into a [`crate::backend::PointerHits`]
/// that [`PickingPlugin`] and [`InteractionPlugin`] will refine into [`bevy_ecs::observer::Trigger`]s.
#[derive(Default)]
pub struct DefaultPickingPlugins;
impl PluginGroup for DefaultPickingPlugins {
fn build(self) -> PluginGroupBuilder {
PluginGroupBuilder::start::<Self>()
.add(input::PointerInputPlugin::default())
.add(PickingPlugin::default())
.add(InteractionPlugin)
}
}
/// This plugin sets up the core picking infrastructure. It receives input events, and provides the shared
/// types used by other picking plugins.
///
/// This plugin contains several settings, and is added to the world as a resource after initialization. You
/// can configure picking settings at runtime through the resource.
#[derive(Copy, Clone, Debug, Resource, Reflect)]
#[reflect(Resource, Default, Debug, Clone)]
pub struct PickingPlugin {
/// Enables and disables all picking features.
pub is_enabled: bool,
/// Enables and disables input collection.
pub is_input_enabled: bool,
/// Enables and disables updating interaction states of entities.
pub is_hover_enabled: bool,
/// Enables or disables picking for window entities.
pub is_window_picking_enabled: bool,
}
impl PickingPlugin {
/// Whether or not input collection systems should be running.
pub fn input_should_run(state: Res<Self>) -> bool {
state.is_input_enabled && state.is_enabled
}
/// Whether or not systems updating entities' [`PickingInteraction`](hover::PickingInteraction)
/// component should be running.
pub fn hover_should_run(state: Res<Self>) -> bool {
state.is_hover_enabled && state.is_enabled
}
/// Whether or not window entities should receive pick events.
pub fn window_picking_should_run(state: Res<Self>) -> bool {
state.is_window_picking_enabled && state.is_enabled
}
}
impl Default for PickingPlugin {
fn default() -> Self {
Self {
is_enabled: true,
is_input_enabled: true,
is_hover_enabled: true,
is_window_picking_enabled: true,
}
}
}
impl Plugin for PickingPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(*self)
.init_resource::<pointer::PointerMap>()
.init_resource::<backend::ray::RayMap>()
.add_event::<pointer::PointerInput>()
.add_event::<backend::PointerHits>()
// Rather than try to mark all current and future backends as ambiguous with each other,
// we allow them to send their hits in any order. These are later sorted, so submission
// order doesn't matter. See `PointerHits` docs for caveats.
.allow_ambiguous_resource::<Events<backend::PointerHits>>()
.add_systems(
PreUpdate,
(
pointer::update_pointer_map,
pointer::PointerInput::receive,
backend::ray::RayMap::repopulate.after(pointer::PointerInput::receive),
)
.in_set(PickSet::ProcessInput),
)
.add_systems(
PreUpdate,
window::update_window_hits
.run_if(Self::window_picking_should_run)
.in_set(PickSet::Backend),
)
.configure_sets(
First,
(PickSet::Input, PickSet::PostInput)
.after(bevy_time::TimeSystem)
.after(bevy_ecs::event::EventUpdates)
.chain(),
)
.configure_sets(
PreUpdate,
(
PickSet::ProcessInput.run_if(Self::input_should_run),
PickSet::Backend,
PickSet::Hover.run_if(Self::hover_should_run),
PickSet::PostHover,
PickSet::Last,
)
.chain(),
)
.register_type::<Self>()
.register_type::<Pickable>()
.register_type::<hover::PickingInteraction>()
.register_type::<pointer::PointerId>()
.register_type::<pointer::PointerLocation>()
.register_type::<pointer::PointerPress>()
.register_type::<pointer::PointerInteraction>()
.register_type::<backend::ray::RayId>();
}
}
/// Generates [`Pointer`](events::Pointer) events and handles event bubbling.
#[derive(Default)]
pub struct InteractionPlugin;
impl Plugin for InteractionPlugin {
fn build(&self, app: &mut App) {
use events::*;
use hover::{generate_hovermap, update_interactions};
app.init_resource::<hover::HoverMap>()
.init_resource::<hover::PreviousHoverMap>()
.init_resource::<PointerState>()
.add_event::<Pointer<Cancel>>()
.add_event::<Pointer<Click>>()
.add_event::<Pointer<Pressed>>()
.add_event::<Pointer<DragDrop>>()
.add_event::<Pointer<DragEnd>>()
.add_event::<Pointer<DragEnter>>()
.add_event::<Pointer<Drag>>()
.add_event::<Pointer<DragLeave>>()
.add_event::<Pointer<DragOver>>()
.add_event::<Pointer<DragStart>>()
.add_event::<Pointer<Move>>()
.add_event::<Pointer<Out>>()
.add_event::<Pointer<Over>>()
.add_event::<Pointer<Released>>()
.add_event::<Pointer<Scroll>>()
.add_systems(
PreUpdate,
(generate_hovermap, update_interactions, pointer_events)
.chain()
.in_set(PickSet::Hover),
);
}
}

View File

@@ -0,0 +1,137 @@
//! A [mesh ray casting](ray_cast) backend for [`bevy_picking`](crate).
//!
//! By default, all meshes are pickable. Picking can be disabled for individual entities
//! by adding [`Pickable::IGNORE`].
//!
//! To make mesh picking entirely opt-in, set [`MeshPickingSettings::require_markers`]
//! to `true` and add [`MeshPickingCamera`] and [`Pickable`] components to the desired camera and
//! target entities.
//!
//! To manually perform mesh ray casts independent of picking, use the [`MeshRayCast`] system parameter.
//!
//! ## Implementation Notes
//!
//! - The `position` reported in `HitData` is in world space. The `normal` is a vector pointing
//! away from the face, it is not guaranteed to be normalized for scaled meshes.
pub mod ray_cast;
use crate::{
backend::{ray::RayMap, HitData, PointerHits},
prelude::*,
PickSet,
};
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bevy_reflect::prelude::*;
use bevy_render::{prelude::*, view::RenderLayers};
use ray_cast::{MeshRayCast, MeshRayCastSettings, RayCastVisibility, SimplifiedMesh};
/// An optional component that marks cameras that should be used in the [`MeshPickingPlugin`].
///
/// Only needed if [`MeshPickingSettings::require_markers`] is set to `true`, and ignored otherwise.
#[derive(Debug, Clone, Default, Component, Reflect)]
#[reflect(Debug, Default, Component)]
pub struct MeshPickingCamera;
/// Runtime settings for the [`MeshPickingPlugin`].
#[derive(Resource, Reflect)]
#[reflect(Resource, Default)]
pub struct MeshPickingSettings {
/// When set to `true` ray casting will only consider cameras marked with
/// [`MeshPickingCamera`] and entities marked with [`Pickable`]. `false` by default.
///
/// This setting is provided to give you fine-grained control over which cameras and entities
/// should be used by the mesh picking backend at runtime.
pub require_markers: bool,
/// Determines how mesh picking should consider [`Visibility`]. When set to [`RayCastVisibility::Any`],
/// ray casts can be performed against both visible and hidden entities.
///
/// Defaults to [`RayCastVisibility::VisibleInView`], only performing picking against visible entities
/// that are in the view of a camera.
pub ray_cast_visibility: RayCastVisibility,
}
impl Default for MeshPickingSettings {
fn default() -> Self {
Self {
require_markers: false,
ray_cast_visibility: RayCastVisibility::VisibleInView,
}
}
}
/// Adds the mesh picking backend to your app.
#[derive(Clone, Default)]
pub struct MeshPickingPlugin;
impl Plugin for MeshPickingPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<MeshPickingSettings>()
.register_type::<MeshPickingSettings>()
.register_type::<SimplifiedMesh>()
.add_systems(PreUpdate, update_hits.in_set(PickSet::Backend));
}
}
/// Casts rays into the scene using [`MeshPickingSettings`] and sends [`PointerHits`] events.
pub fn update_hits(
backend_settings: Res<MeshPickingSettings>,
ray_map: Res<RayMap>,
picking_cameras: Query<(&Camera, Has<MeshPickingCamera>, Option<&RenderLayers>)>,
pickables: Query<&Pickable>,
marked_targets: Query<&Pickable>,
layers: Query<&RenderLayers>,
mut ray_cast: MeshRayCast,
mut output: EventWriter<PointerHits>,
) {
for (&ray_id, &ray) in ray_map.iter() {
let Ok((camera, cam_can_pick, cam_layers)) = picking_cameras.get(ray_id.camera) else {
continue;
};
if backend_settings.require_markers && !cam_can_pick {
continue;
}
let cam_layers = cam_layers.to_owned().unwrap_or_default();
let settings = MeshRayCastSettings {
visibility: backend_settings.ray_cast_visibility,
filter: &|entity| {
let marker_requirement =
!backend_settings.require_markers || marked_targets.get(entity).is_ok();
// Other entities missing render layers are on the default layer 0
let entity_layers = layers.get(entity).cloned().unwrap_or_default();
let render_layers_match = cam_layers.intersects(&entity_layers);
let is_pickable = pickables.get(entity).ok().is_none_or(|p| p.is_hoverable);
marker_requirement && render_layers_match && is_pickable
},
early_exit_test: &|entity_hit| {
pickables
.get(entity_hit)
.is_ok_and(|pickable| pickable.should_block_lower)
},
};
let picks = ray_cast
.cast_ray(ray, &settings)
.iter()
.map(|(entity, hit)| {
let hit_data = HitData::new(
ray_id.camera,
hit.distance,
Some(hit.point),
Some(hit.normal),
);
(*entity, hit_data)
})
.collect::<Vec<_>>();
let order = camera.order as f32;
if !picks.is_empty() {
output.write(PointerHits::new(ray_id.pointer, picks, order));
}
}
}

View File

@@ -0,0 +1,486 @@
use bevy_math::{bounding::Aabb3d, Dir3, Mat4, Ray3d, Vec3, Vec3A};
use bevy_mesh::{Indices, Mesh, PrimitiveTopology};
use bevy_reflect::Reflect;
use super::Backfaces;
/// Hit data for an intersection between a ray and a mesh.
#[derive(Debug, Clone, Reflect)]
#[reflect(Clone)]
pub struct RayMeshHit {
/// The point of intersection in world space.
pub point: Vec3,
/// The normal vector of the triangle at the point of intersection. Not guaranteed to be normalized for scaled meshes.
pub normal: Vec3,
/// The barycentric coordinates of the intersection.
pub barycentric_coords: Vec3,
/// The distance from the ray origin to the intersection point.
pub distance: f32,
/// The vertices of the triangle that was hit.
pub triangle: Option<[Vec3; 3]>,
/// The index of the triangle that was hit.
pub triangle_index: Option<usize>,
}
/// Hit data for an intersection between a ray and a triangle.
#[derive(Default, Debug)]
pub struct RayTriangleHit {
pub distance: f32,
pub barycentric_coords: (f32, f32),
}
/// Casts a ray on a mesh, and returns the intersection.
pub(super) fn ray_intersection_over_mesh(
mesh: &Mesh,
transform: &Mat4,
ray: Ray3d,
culling: Backfaces,
) -> Option<RayMeshHit> {
if mesh.primitive_topology() != PrimitiveTopology::TriangleList {
return None; // ray_mesh_intersection assumes vertices are laid out in a triangle list
}
// Vertex positions are required
let positions = mesh.attribute(Mesh::ATTRIBUTE_POSITION)?.as_float3()?;
// Normals are optional
let normals = mesh
.attribute(Mesh::ATTRIBUTE_NORMAL)
.and_then(|normal_values| normal_values.as_float3());
match mesh.indices() {
Some(Indices::U16(indices)) => {
ray_mesh_intersection(ray, transform, positions, normals, Some(indices), culling)
}
Some(Indices::U32(indices)) => {
ray_mesh_intersection(ray, transform, positions, normals, Some(indices), culling)
}
None => ray_mesh_intersection::<usize>(ray, transform, positions, normals, None, culling),
}
}
/// Checks if a ray intersects a mesh, and returns the nearest intersection if one exists.
pub fn ray_mesh_intersection<I: TryInto<usize> + Clone + Copy>(
ray: Ray3d,
mesh_transform: &Mat4,
positions: &[[f32; 3]],
vertex_normals: Option<&[[f32; 3]]>,
indices: Option<&[I]>,
backface_culling: Backfaces,
) -> Option<RayMeshHit> {
let world_to_mesh = mesh_transform.inverse();
let ray = Ray3d::new(
world_to_mesh.transform_point3(ray.origin),
Dir3::new(world_to_mesh.transform_vector3(*ray.direction)).ok()?,
);
let closest_hit = if let Some(indices) = indices {
// The index list must be a multiple of three. If not, the mesh is malformed and the raycast
// result might be nonsensical.
if indices.len() % 3 != 0 {
return None;
}
indices
.chunks_exact(3)
.enumerate()
.fold(
(f32::MAX, None),
|(closest_distance, closest_hit), (tri_idx, triangle)| {
let [Ok(a), Ok(b), Ok(c)] = [
triangle[0].try_into(),
triangle[1].try_into(),
triangle[2].try_into(),
] else {
return (closest_distance, closest_hit);
};
let tri_vertices = match [positions.get(a), positions.get(b), positions.get(c)]
{
[Some(a), Some(b), Some(c)] => {
[Vec3::from(*a), Vec3::from(*b), Vec3::from(*c)]
}
_ => return (closest_distance, closest_hit),
};
match ray_triangle_intersection(&ray, &tri_vertices, backface_culling) {
Some(hit) if hit.distance >= 0. && hit.distance < closest_distance => {
(hit.distance, Some((tri_idx, hit)))
}
_ => (closest_distance, closest_hit),
}
},
)
.1
} else {
positions
.chunks_exact(3)
.enumerate()
.fold(
(f32::MAX, None),
|(closest_distance, closest_hit), (tri_idx, triangle)| {
let tri_vertices = [
Vec3::from(triangle[0]),
Vec3::from(triangle[1]),
Vec3::from(triangle[2]),
];
match ray_triangle_intersection(&ray, &tri_vertices, backface_culling) {
Some(hit) if hit.distance >= 0. && hit.distance < closest_distance => {
(hit.distance, Some((tri_idx, hit)))
}
_ => (closest_distance, closest_hit),
}
},
)
.1
};
closest_hit.and_then(|(tri_idx, hit)| {
let [a, b, c] = match indices {
Some(indices) => {
let triangle = indices.get((tri_idx * 3)..(tri_idx * 3 + 3))?;
let [Ok(a), Ok(b), Ok(c)] = [
triangle[0].try_into(),
triangle[1].try_into(),
triangle[2].try_into(),
] else {
return None;
};
[a, b, c]
}
None => [tri_idx * 3, tri_idx * 3 + 1, tri_idx * 3 + 2],
};
let tri_vertices = match [positions.get(a), positions.get(b), positions.get(c)] {
[Some(a), Some(b), Some(c)] => [Vec3::from(*a), Vec3::from(*b), Vec3::from(*c)],
_ => return None,
};
let tri_normals = vertex_normals.and_then(|normals| {
let [Some(a), Some(b), Some(c)] = [normals.get(a), normals.get(b), normals.get(c)]
else {
return None;
};
Some([Vec3::from(*a), Vec3::from(*b), Vec3::from(*c)])
});
let point = ray.get_point(hit.distance);
let u = hit.barycentric_coords.0;
let v = hit.barycentric_coords.1;
let w = 1.0 - u - v;
let barycentric = Vec3::new(u, v, w);
let normal = if let Some(normals) = tri_normals {
normals[1] * u + normals[2] * v + normals[0] * w
} else {
(tri_vertices[1] - tri_vertices[0])
.cross(tri_vertices[2] - tri_vertices[0])
.normalize()
};
Some(RayMeshHit {
point: mesh_transform.transform_point3(point),
normal: mesh_transform.transform_vector3(normal),
barycentric_coords: barycentric,
distance: mesh_transform
.transform_vector3(ray.direction * hit.distance)
.length(),
triangle: Some(tri_vertices.map(|v| mesh_transform.transform_point3(v))),
triangle_index: Some(tri_idx),
})
})
}
/// Takes a ray and triangle and computes the intersection.
#[inline]
fn ray_triangle_intersection(
ray: &Ray3d,
triangle: &[Vec3; 3],
backface_culling: Backfaces,
) -> Option<RayTriangleHit> {
// Source: https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/moller-trumbore-ray-triangle-intersection
let vector_v0_to_v1: Vec3 = triangle[1] - triangle[0];
let vector_v0_to_v2: Vec3 = triangle[2] - triangle[0];
let p_vec: Vec3 = ray.direction.cross(vector_v0_to_v2);
let determinant: f32 = vector_v0_to_v1.dot(p_vec);
match backface_culling {
Backfaces::Cull => {
// if the determinant is negative the triangle is back facing
// if the determinant is close to 0, the ray misses the triangle
// This test checks both cases
if determinant < f32::EPSILON {
return None;
}
}
Backfaces::Include => {
// ray and triangle are parallel if det is close to 0
if determinant.abs() < f32::EPSILON {
return None;
}
}
}
let determinant_inverse = 1.0 / determinant;
let t_vec = ray.origin - triangle[0];
let u = t_vec.dot(p_vec) * determinant_inverse;
if !(0.0..=1.0).contains(&u) {
return None;
}
let q_vec = t_vec.cross(vector_v0_to_v1);
let v = (*ray.direction).dot(q_vec) * determinant_inverse;
if v < 0.0 || u + v > 1.0 {
return None;
}
// The distance between ray origin and intersection is t.
let t: f32 = vector_v0_to_v2.dot(q_vec) * determinant_inverse;
Some(RayTriangleHit {
distance: t,
barycentric_coords: (u, v),
})
}
// TODO: It'd be nice to reuse `RayCast3d::aabb_intersection_at`, but it assumes a normalized ray.
// In our case, the ray is transformed to model space, which could involve scaling.
/// Checks if the ray intersects with the AABB of a mesh, returning the distance to the point of intersection.
/// The distance is zero if the ray starts inside the AABB.
pub fn ray_aabb_intersection_3d(ray: Ray3d, aabb: &Aabb3d, model_to_world: &Mat4) -> Option<f32> {
// Transform the ray to model space
let world_to_model = model_to_world.inverse();
let ray_direction: Vec3A = world_to_model.transform_vector3a((*ray.direction).into());
let ray_direction_recip = ray_direction.recip();
let ray_origin: Vec3A = world_to_model.transform_point3a(ray.origin.into());
// Check if the ray intersects the mesh's AABB. It's useful to work in model space
// because we can do an AABB intersection test, instead of an OBB intersection test.
// NOTE: This is largely copied from `RayCast3d::aabb_intersection_at`.
let positive = ray_direction.signum().cmpgt(Vec3A::ZERO);
let min = Vec3A::select(positive, aabb.min, aabb.max);
let max = Vec3A::select(positive, aabb.max, aabb.min);
// Calculate the minimum/maximum time for each axis based on how much the direction goes that
// way. These values can get arbitrarily large, or even become NaN, which is handled by the
// min/max operations below
let tmin = (min - ray_origin) * ray_direction_recip;
let tmax = (max - ray_origin) * ray_direction_recip;
// An axis that is not relevant to the ray direction will be NaN. When one of the arguments
// to min/max is NaN, the other argument is used.
// An axis for which the direction is the wrong way will return an arbitrarily large
// negative value.
let tmin = tmin.max_element().max(0.0);
let tmax = tmax.min_element();
if tmin <= tmax {
Some(tmin)
} else {
None
}
}
#[cfg(test)]
mod tests {
use bevy_math::Vec3;
use bevy_transform::components::GlobalTransform;
use super::*;
// Triangle vertices to be used in a left-hand coordinate system
const V0: [f32; 3] = [1.0, -1.0, 2.0];
const V1: [f32; 3] = [1.0, 2.0, -1.0];
const V2: [f32; 3] = [1.0, -1.0, -1.0];
#[test]
fn ray_cast_triangle_mt() {
let triangle = [V0.into(), V1.into(), V2.into()];
let ray = Ray3d::new(Vec3::ZERO, Dir3::X);
let result = ray_triangle_intersection(&ray, &triangle, Backfaces::Include);
assert!(result.unwrap().distance - 1.0 <= f32::EPSILON);
}
#[test]
fn ray_cast_triangle_mt_culling() {
let triangle = [V2.into(), V1.into(), V0.into()];
let ray = Ray3d::new(Vec3::ZERO, Dir3::X);
let result = ray_triangle_intersection(&ray, &triangle, Backfaces::Cull);
assert!(result.is_none());
}
#[test]
fn ray_mesh_intersection_simple() {
let ray = Ray3d::new(Vec3::ZERO, Dir3::X);
let mesh_transform = GlobalTransform::IDENTITY.compute_matrix();
let positions = &[V0, V1, V2];
let vertex_normals = None;
let indices: Option<&[u16]> = None;
let backface_culling = Backfaces::Cull;
let result = ray_mesh_intersection(
ray,
&mesh_transform,
positions,
vertex_normals,
indices,
backface_culling,
);
assert!(result.is_some());
}
#[test]
fn ray_mesh_intersection_indices() {
let ray = Ray3d::new(Vec3::ZERO, Dir3::X);
let mesh_transform = GlobalTransform::IDENTITY.compute_matrix();
let positions = &[V0, V1, V2];
let vertex_normals = None;
let indices: Option<&[u16]> = Some(&[0, 1, 2]);
let backface_culling = Backfaces::Cull;
let result = ray_mesh_intersection(
ray,
&mesh_transform,
positions,
vertex_normals,
indices,
backface_culling,
);
assert!(result.is_some());
}
#[test]
fn ray_mesh_intersection_indices_vertex_normals() {
let ray = Ray3d::new(Vec3::ZERO, Dir3::X);
let mesh_transform = GlobalTransform::IDENTITY.compute_matrix();
let positions = &[V0, V1, V2];
let vertex_normals: Option<&[[f32; 3]]> =
Some(&[[-1., 0., 0.], [-1., 0., 0.], [-1., 0., 0.]]);
let indices: Option<&[u16]> = Some(&[0, 1, 2]);
let backface_culling = Backfaces::Cull;
let result = ray_mesh_intersection(
ray,
&mesh_transform,
positions,
vertex_normals,
indices,
backface_culling,
);
assert!(result.is_some());
}
#[test]
fn ray_mesh_intersection_vertex_normals() {
let ray = Ray3d::new(Vec3::ZERO, Dir3::X);
let mesh_transform = GlobalTransform::IDENTITY.compute_matrix();
let positions = &[V0, V1, V2];
let vertex_normals: Option<&[[f32; 3]]> =
Some(&[[-1., 0., 0.], [-1., 0., 0.], [-1., 0., 0.]]);
let indices: Option<&[u16]> = None;
let backface_culling = Backfaces::Cull;
let result = ray_mesh_intersection(
ray,
&mesh_transform,
positions,
vertex_normals,
indices,
backface_culling,
);
assert!(result.is_some());
}
#[test]
fn ray_mesh_intersection_missing_vertex_normals() {
let ray = Ray3d::new(Vec3::ZERO, Dir3::X);
let mesh_transform = GlobalTransform::IDENTITY.compute_matrix();
let positions = &[V0, V1, V2];
let vertex_normals: Option<&[[f32; 3]]> = Some(&[]);
let indices: Option<&[u16]> = None;
let backface_culling = Backfaces::Cull;
let result = ray_mesh_intersection(
ray,
&mesh_transform,
positions,
vertex_normals,
indices,
backface_culling,
);
assert!(result.is_some());
}
#[test]
fn ray_mesh_intersection_indices_missing_vertex_normals() {
let ray = Ray3d::new(Vec3::ZERO, Dir3::X);
let mesh_transform = GlobalTransform::IDENTITY.compute_matrix();
let positions = &[V0, V1, V2];
let vertex_normals: Option<&[[f32; 3]]> = Some(&[]);
let indices: Option<&[u16]> = Some(&[0, 1, 2]);
let backface_culling = Backfaces::Cull;
let result = ray_mesh_intersection(
ray,
&mesh_transform,
positions,
vertex_normals,
indices,
backface_culling,
);
assert!(result.is_some());
}
#[test]
fn ray_mesh_intersection_not_enough_indices() {
let ray = Ray3d::new(Vec3::ZERO, Dir3::X);
let mesh_transform = GlobalTransform::IDENTITY.compute_matrix();
let positions = &[V0, V1, V2];
let vertex_normals = None;
let indices: Option<&[u16]> = Some(&[0]);
let backface_culling = Backfaces::Cull;
let result = ray_mesh_intersection(
ray,
&mesh_transform,
positions,
vertex_normals,
indices,
backface_culling,
);
assert!(result.is_none());
}
#[test]
fn ray_mesh_intersection_bad_indices() {
let ray = Ray3d::new(Vec3::ZERO, Dir3::X);
let mesh_transform = GlobalTransform::IDENTITY.compute_matrix();
let positions = &[V0, V1, V2];
let vertex_normals = None;
let indices: Option<&[u16]> = Some(&[0, 1, 3]);
let backface_culling = Backfaces::Cull;
let result = ray_mesh_intersection(
ray,
&mesh_transform,
positions,
vertex_normals,
indices,
backface_culling,
);
assert!(result.is_none());
}
}

View File

@@ -0,0 +1,313 @@
//! Ray casting for meshes.
//!
//! See the [`MeshRayCast`] system parameter for more information.
mod intersections;
use bevy_derive::{Deref, DerefMut};
use bevy_math::{bounding::Aabb3d, Ray3d};
use bevy_mesh::Mesh;
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use intersections::*;
pub use intersections::{ray_aabb_intersection_3d, ray_mesh_intersection, RayMeshHit};
use bevy_asset::{Assets, Handle};
use bevy_ecs::{prelude::*, system::lifetimeless::Read, system::SystemParam};
use bevy_math::FloatOrd;
use bevy_render::{prelude::*, primitives::Aabb};
use bevy_transform::components::GlobalTransform;
use tracing::*;
/// How a ray cast should handle [`Visibility`].
#[derive(Clone, Copy, Reflect)]
#[reflect(Clone)]
pub enum RayCastVisibility {
/// Completely ignore visibility checks. Hidden items can still be ray casted against.
Any,
/// Only cast rays against entities that are visible in the hierarchy. See [`Visibility`].
Visible,
/// Only cast rays against entities that are visible in the hierarchy and visible to a camera or
/// light. See [`Visibility`].
VisibleInView,
}
/// Settings for a ray cast.
#[derive(Clone)]
pub struct MeshRayCastSettings<'a> {
/// Determines how ray casting should consider [`Visibility`].
pub visibility: RayCastVisibility,
/// A predicate that is applied for every entity that ray casts are performed against.
/// Only entities that return `true` will be considered.
pub filter: &'a dyn Fn(Entity) -> bool,
/// A function that is run every time a hit is found. Ray casting will continue to check for hits
/// along the ray as long as this returns `false`.
pub early_exit_test: &'a dyn Fn(Entity) -> bool,
}
impl<'a> MeshRayCastSettings<'a> {
/// Set the filter to apply to the ray cast.
pub fn with_filter(mut self, filter: &'a impl Fn(Entity) -> bool) -> Self {
self.filter = filter;
self
}
/// Set the early exit test to apply to the ray cast.
pub fn with_early_exit_test(mut self, early_exit_test: &'a impl Fn(Entity) -> bool) -> Self {
self.early_exit_test = early_exit_test;
self
}
/// Set the [`RayCastVisibility`] setting to apply to the ray cast.
pub fn with_visibility(mut self, visibility: RayCastVisibility) -> Self {
self.visibility = visibility;
self
}
/// This ray cast should exit as soon as the nearest hit is found.
pub fn always_early_exit(self) -> Self {
self.with_early_exit_test(&|_| true)
}
/// This ray cast should check all entities whose AABB intersects the ray and return all hits.
pub fn never_early_exit(self) -> Self {
self.with_early_exit_test(&|_| false)
}
}
impl<'a> Default for MeshRayCastSettings<'a> {
fn default() -> Self {
Self {
visibility: RayCastVisibility::VisibleInView,
filter: &|_| true,
early_exit_test: &|_| true,
}
}
}
/// Determines whether backfaces should be culled or included in ray intersection tests.
///
/// By default, backfaces are culled.
#[derive(Copy, Clone, Default, Reflect)]
#[reflect(Default, Clone)]
pub enum Backfaces {
/// Cull backfaces.
#[default]
Cull,
/// Include backfaces.
Include,
}
/// Disables backface culling for [ray casts](MeshRayCast) on this entity.
#[derive(Component, Copy, Clone, Default, Reflect)]
#[reflect(Component, Default, Clone)]
pub struct RayCastBackfaces;
/// A simplified mesh component that can be used for [ray casting](super::MeshRayCast).
///
/// Consider using this component for complex meshes that don't need perfectly accurate ray casting.
#[derive(Component, Clone, Debug, Deref, DerefMut, Reflect)]
#[reflect(Component, Debug, Clone)]
pub struct SimplifiedMesh(pub Handle<Mesh>);
type MeshFilter = Or<(With<Mesh3d>, With<Mesh2d>, With<SimplifiedMesh>)>;
/// Add this ray casting [`SystemParam`] to your system to cast rays into the world with an
/// immediate-mode API. Call `cast_ray` to immediately perform a ray cast and get a result.
///
/// Under the hood, this is a collection of regular bevy queries, resources, and local parameters
/// that are added to your system.
///
/// ## Usage
///
/// The following system casts a ray into the world with the ray positioned at the origin, pointing in
/// the X-direction, and returns a list of intersections:
///
/// ```
/// # use bevy_math::prelude::*;
/// # use bevy_picking::prelude::*;
/// fn ray_cast_system(mut ray_cast: MeshRayCast) {
/// let ray = Ray3d::new(Vec3::ZERO, Dir3::X);
/// let hits = ray_cast.cast_ray(ray, &MeshRayCastSettings::default());
/// }
/// ```
///
/// ## Configuration
///
/// You can specify the behavior of the ray cast using [`MeshRayCastSettings`]. This allows you to filter out
/// entities, configure early-out behavior, and set whether the [`Visibility`] of an entity should be
/// considered.
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_math::prelude::*;
/// # use bevy_picking::prelude::*;
/// # #[derive(Component)]
/// # struct Foo;
/// fn ray_cast_system(mut ray_cast: MeshRayCast, foo_query: Query<(), With<Foo>>) {
/// let ray = Ray3d::new(Vec3::ZERO, Dir3::X);
///
/// // Only ray cast against entities with the `Foo` component.
/// let filter = |entity| foo_query.contains(entity);
///
/// // Never early-exit. Note that you can change behavior per-entity.
/// let early_exit_test = |_entity| false;
///
/// // Ignore the visibility of entities. This allows ray casting hidden entities.
/// let visibility = RayCastVisibility::Any;
///
/// let settings = MeshRayCastSettings::default()
/// .with_filter(&filter)
/// .with_early_exit_test(&early_exit_test)
/// .with_visibility(visibility);
///
/// // Cast the ray with the settings, returning a list of intersections.
/// let hits = ray_cast.cast_ray(ray, &settings);
/// }
/// ```
#[derive(SystemParam)]
pub struct MeshRayCast<'w, 's> {
#[doc(hidden)]
pub meshes: Res<'w, Assets<Mesh>>,
#[doc(hidden)]
pub hits: Local<'s, Vec<(FloatOrd, (Entity, RayMeshHit))>>,
#[doc(hidden)]
pub output: Local<'s, Vec<(Entity, RayMeshHit)>>,
#[doc(hidden)]
pub culled_list: Local<'s, Vec<(FloatOrd, Entity)>>,
#[doc(hidden)]
pub culling_query: Query<
'w,
's,
(
Read<InheritedVisibility>,
Read<ViewVisibility>,
Read<Aabb>,
Read<GlobalTransform>,
Entity,
),
MeshFilter,
>,
#[doc(hidden)]
pub mesh_query: Query<
'w,
's,
(
Option<Read<Mesh2d>>,
Option<Read<Mesh3d>>,
Option<Read<SimplifiedMesh>>,
Has<RayCastBackfaces>,
Read<GlobalTransform>,
),
MeshFilter,
>,
}
impl<'w, 's> MeshRayCast<'w, 's> {
/// Casts the `ray` into the world and returns a sorted list of intersections, nearest first.
pub fn cast_ray(
&mut self,
ray: Ray3d,
settings: &MeshRayCastSettings,
) -> &[(Entity, RayMeshHit)] {
let ray_cull = info_span!("ray culling");
let ray_cull_guard = ray_cull.enter();
self.hits.clear();
self.culled_list.clear();
self.output.clear();
// Check all entities to see if the ray intersects the AABB. Use this to build a short list
// of entities that are in the path of the ray.
let (aabb_hits_tx, aabb_hits_rx) = crossbeam_channel::unbounded::<(FloatOrd, Entity)>();
let visibility_setting = settings.visibility;
self.culling_query.par_iter().for_each(
|(inherited_visibility, view_visibility, aabb, transform, entity)| {
let should_ray_cast = match visibility_setting {
RayCastVisibility::Any => true,
RayCastVisibility::Visible => inherited_visibility.get(),
RayCastVisibility::VisibleInView => view_visibility.get(),
};
if should_ray_cast {
if let Some(distance) = ray_aabb_intersection_3d(
ray,
&Aabb3d::new(aabb.center, aabb.half_extents),
&transform.compute_matrix(),
) {
aabb_hits_tx.send((FloatOrd(distance), entity)).ok();
}
}
},
);
*self.culled_list = aabb_hits_rx.try_iter().collect();
// Sort by the distance along the ray.
self.culled_list.sort_by_key(|(aabb_near, _)| *aabb_near);
drop(ray_cull_guard);
// Perform ray casts against the culled entities.
let mut nearest_blocking_hit = FloatOrd(f32::INFINITY);
let ray_cast_guard = debug_span!("ray_cast");
self.culled_list
.iter()
.filter(|(_, entity)| (settings.filter)(*entity))
.for_each(|(aabb_near, entity)| {
// Get the mesh components and transform.
let Ok((mesh2d, mesh3d, simplified_mesh, has_backfaces, transform)) =
self.mesh_query.get(*entity)
else {
return;
};
// Get the underlying mesh handle. One of these will always be `Some` because of the query filters.
let Some(mesh_handle) = simplified_mesh
.map(|m| &m.0)
.or(mesh3d.map(|m| &m.0).or(mesh2d.map(|m| &m.0)))
else {
return;
};
// Is it even possible the mesh could be closer than the current best?
if *aabb_near > nearest_blocking_hit {
return;
}
// Does the mesh handle resolve?
let Some(mesh) = self.meshes.get(mesh_handle) else {
return;
};
// Backfaces of 2d meshes are never culled, unlike 3d meshes.
let backfaces = match (has_backfaces, mesh2d.is_some()) {
(false, false) => Backfaces::Cull,
_ => Backfaces::Include,
};
// Perform the actual ray cast.
let _ray_cast_guard = ray_cast_guard.enter();
let transform = transform.compute_matrix();
let intersection = ray_intersection_over_mesh(mesh, &transform, ray, backfaces);
if let Some(intersection) = intersection {
let distance = FloatOrd(intersection.distance);
if (settings.early_exit_test)(*entity) && distance < nearest_blocking_hit {
// The reason we don't just return here is because right now we are
// going through the AABBs in order, but that doesn't mean that an
// AABB that starts further away can't end up with a closer hit than
// an AABB that starts closer. We need to keep checking AABBs that
// could possibly contain a nearer hit.
nearest_blocking_hit = distance.min(nearest_blocking_hit);
}
self.hits.push((distance, (*entity, intersection)));
};
});
self.hits.retain(|(dist, _)| *dist <= nearest_blocking_hit);
self.hits.sort_by_key(|(k, _)| *k);
let hits = self.hits.iter().map(|(_, (e, i))| (*e, i.to_owned()));
self.output.extend(hits);
self.output.as_ref()
}
}

359
vendor/bevy_picking/src/pointer.rs vendored Normal file
View File

@@ -0,0 +1,359 @@
//! Types and systems for pointer inputs, such as position and buttons.
//!
//! The picking system is built around the concept of a 'Pointer', which is an
//! abstract representation of a user input with a specific screen location. The cursor
//! and touch input is provided under [`crate::input`], but you can also implement
//! your own custom pointers by supplying a unique ID.
//!
//! The purpose of this module is primarily to provide a common interface that can be
//! driven by lower-level input devices and consumed by higher-level interaction systems.
use bevy_ecs::prelude::*;
use bevy_input::mouse::MouseScrollUnit;
use bevy_math::Vec2;
use bevy_platform::collections::HashMap;
use bevy_reflect::prelude::*;
use bevy_render::camera::{Camera, NormalizedRenderTarget};
use bevy_window::PrimaryWindow;
use uuid::Uuid;
use core::{fmt::Debug, ops::Deref};
use crate::backend::HitData;
/// Identifies a unique pointer entity. `Mouse` and `Touch` pointers are automatically spawned.
///
/// This component is needed because pointers can be spawned and despawned, but they need to have a
/// stable ID that persists regardless of the Entity they are associated with.
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, Component, Reflect)]
#[require(PointerLocation, PointerPress, PointerInteraction)]
#[reflect(Component, Default, Debug, Hash, PartialEq, Clone)]
pub enum PointerId {
/// The mouse pointer.
#[default]
Mouse,
/// A touch input, usually numbered by window touch events from `winit`.
Touch(u64),
/// A custom, uniquely identified pointer. Useful for mocking inputs or implementing a software
/// controlled cursor.
#[reflect(ignore, clone)]
Custom(Uuid),
}
impl PointerId {
/// Returns true if the pointer is a touch input.
pub fn is_touch(&self) -> bool {
matches!(self, PointerId::Touch(_))
}
/// Returns true if the pointer is the mouse.
pub fn is_mouse(&self) -> bool {
matches!(self, PointerId::Mouse)
}
/// Returns true if the pointer is a custom input.
pub fn is_custom(&self) -> bool {
matches!(self, PointerId::Custom(_))
}
/// Returns the touch id if the pointer is a touch input.
pub fn get_touch_id(&self) -> Option<u64> {
if let PointerId::Touch(id) = self {
Some(*id)
} else {
None
}
}
}
/// Holds a list of entities this pointer is currently interacting with, sorted from nearest to
/// farthest.
#[derive(Debug, Default, Clone, Component, Reflect)]
#[reflect(Component, Default, Debug, Clone)]
pub struct PointerInteraction {
pub(crate) sorted_entities: Vec<(Entity, HitData)>,
}
impl PointerInteraction {
/// Returns the nearest hit entity and data about that intersection.
pub fn get_nearest_hit(&self) -> Option<&(Entity, HitData)> {
self.sorted_entities.first()
}
}
impl Deref for PointerInteraction {
type Target = Vec<(Entity, HitData)>;
fn deref(&self) -> &Self::Target {
&self.sorted_entities
}
}
/// A resource that maps each [`PointerId`] to their [`Entity`] for easy lookups.
#[derive(Debug, Clone, Default, Resource)]
pub struct PointerMap {
inner: HashMap<PointerId, Entity>,
}
impl PointerMap {
/// Get the [`Entity`] of the supplied [`PointerId`].
pub fn get_entity(&self, pointer_id: PointerId) -> Option<Entity> {
self.inner.get(&pointer_id).copied()
}
}
/// Update the [`PointerMap`] resource with the current frame's data.
pub fn update_pointer_map(pointers: Query<(Entity, &PointerId)>, mut map: ResMut<PointerMap>) {
map.inner.clear();
for (entity, id) in &pointers {
map.inner.insert(*id, entity);
}
}
/// Tracks the state of the pointer's buttons in response to [`PointerInput`] events.
#[derive(Debug, Default, Clone, Component, Reflect, PartialEq, Eq)]
#[reflect(Component, Default, Debug, PartialEq, Clone)]
pub struct PointerPress {
primary: bool,
secondary: bool,
middle: bool,
}
impl PointerPress {
/// Returns true if the primary pointer button is pressed.
#[inline]
pub fn is_primary_pressed(&self) -> bool {
self.primary
}
/// Returns true if the secondary pointer button is pressed.
#[inline]
pub fn is_secondary_pressed(&self) -> bool {
self.secondary
}
/// Returns true if the middle (tertiary) pointer button is pressed.
#[inline]
pub fn is_middle_pressed(&self) -> bool {
self.middle
}
/// Returns true if any pointer button is pressed.
#[inline]
pub fn is_any_pressed(&self) -> bool {
self.primary || self.middle || self.secondary
}
}
/// The stage of the pointer button press event
#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect)]
#[reflect(Clone, PartialEq)]
pub enum PressDirection {
/// The pointer button was just pressed
Pressed,
/// The pointer button was just released
Released,
}
/// The button that was just pressed or released
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect)]
#[reflect(Clone, PartialEq)]
pub enum PointerButton {
/// The primary pointer button
Primary,
/// The secondary pointer button
Secondary,
/// The tertiary pointer button
Middle,
}
impl PointerButton {
/// Iterator over all buttons that a pointer can have.
pub fn iter() -> impl Iterator<Item = PointerButton> {
[Self::Primary, Self::Secondary, Self::Middle].into_iter()
}
}
/// Component that tracks a pointer's current [`Location`].
#[derive(Debug, Default, Clone, Component, Reflect, PartialEq)]
#[reflect(Component, Default, Debug, PartialEq, Clone)]
pub struct PointerLocation {
/// The [`Location`] of the pointer. Note that a location is both the target, and the position
/// on the target.
#[reflect(ignore, clone)]
pub location: Option<Location>,
}
impl PointerLocation {
///Returns a [`PointerLocation`] associated with the given location
pub fn new(location: Location) -> Self {
Self {
location: Some(location),
}
}
/// Returns `Some(&`[`Location`]`)` if the pointer is active, or `None` if the pointer is
/// inactive.
pub fn location(&self) -> Option<&Location> {
self.location.as_ref()
}
}
/// The location of a pointer, including the current [`NormalizedRenderTarget`], and the x/y
/// position of the pointer on this render target.
///
/// Note that:
/// - a pointer can move freely between render targets
/// - a pointer is not associated with a [`Camera`] because multiple cameras can target the same
/// render target. It is up to picking backends to associate a Pointer's `Location` with a
/// specific `Camera`, if any.
#[derive(Debug, Clone, Component, Reflect, PartialEq)]
#[reflect(Component, Debug, PartialEq, Clone)]
pub struct Location {
/// The [`NormalizedRenderTarget`] associated with the pointer, usually a window.
pub target: NormalizedRenderTarget,
/// The position of the pointer in the `target`.
pub position: Vec2,
}
impl Location {
/// Returns `true` if this pointer's [`Location`] is within the [`Camera`]'s viewport.
///
/// Note this returns `false` if the location and camera have different render targets.
#[inline]
pub fn is_in_viewport(
&self,
camera: &Camera,
primary_window: &Query<Entity, With<PrimaryWindow>>,
) -> bool {
if camera
.target
.normalize(Some(match primary_window.single() {
Ok(w) => w,
Err(_) => return false,
}))
.as_ref()
!= Some(&self.target)
{
return false;
}
camera
.logical_viewport_rect()
.is_some_and(|rect| rect.contains(self.position))
}
}
/// Event sent to drive a pointer.
#[derive(Debug, Clone, Copy, Reflect)]
#[reflect(Clone)]
pub enum PointerAction {
/// Causes the pointer to press a button.
Press(PointerButton),
/// Causes the pointer to release a button.
Release(PointerButton),
/// Move the pointer.
Move {
/// How much the pointer moved from the previous position.
delta: Vec2,
},
/// Scroll the pointer
Scroll {
/// The mouse scroll unit.
unit: MouseScrollUnit,
/// The horizontal scroll value.
x: f32,
/// The vertical scroll value.
y: f32,
},
/// Cancel the pointer. Often used for touch events.
Cancel,
}
/// An input event effecting a pointer.
#[derive(Event, Debug, Clone, Reflect)]
#[reflect(Clone)]
pub struct PointerInput {
/// The id of the pointer.
pub pointer_id: PointerId,
/// The location of the pointer. For [`PointerAction::Move`], this is the location after the movement.
pub location: Location,
/// The action that the event describes.
pub action: PointerAction,
}
impl PointerInput {
/// Creates a new pointer input event.
///
/// Note that `location` refers to the position of the pointer *after* the event occurred.
pub fn new(pointer_id: PointerId, location: Location, action: PointerAction) -> PointerInput {
PointerInput {
pointer_id,
location,
action,
}
}
/// Returns true if the `target_button` of this pointer was just pressed.
#[inline]
pub fn button_just_pressed(&self, target_button: PointerButton) -> bool {
if let PointerAction::Press(button) = self.action {
button == target_button
} else {
false
}
}
/// Returns true if the `target_button` of this pointer was just released.
#[inline]
pub fn button_just_released(&self, target_button: PointerButton) -> bool {
if let PointerAction::Release(button) = self.action {
button == target_button
} else {
false
}
}
/// Updates pointer entities according to the input events.
pub fn receive(
mut events: EventReader<PointerInput>,
mut pointers: Query<(&PointerId, &mut PointerLocation, &mut PointerPress)>,
) {
for event in events.read() {
match event.action {
PointerAction::Press(button) => {
pointers
.iter_mut()
.for_each(|(pointer_id, _, mut pointer)| {
if *pointer_id == event.pointer_id {
match button {
PointerButton::Primary => pointer.primary = true,
PointerButton::Secondary => pointer.secondary = true,
PointerButton::Middle => pointer.middle = true,
}
}
});
}
PointerAction::Release(button) => {
pointers
.iter_mut()
.for_each(|(pointer_id, _, mut pointer)| {
if *pointer_id == event.pointer_id {
match button {
PointerButton::Primary => pointer.primary = false,
PointerButton::Secondary => pointer.secondary = false,
PointerButton::Middle => pointer.middle = false,
}
}
});
}
PointerAction::Move { .. } => {
pointers.iter_mut().for_each(|(id, mut pointer, _)| {
if *id == event.pointer_id {
pointer.location = Some(event.location.to_owned());
}
});
}
_ => {}
}
}
}
}

49
vendor/bevy_picking/src/window.rs vendored Normal file
View File

@@ -0,0 +1,49 @@
//! This module contains a basic backend that implements picking for window
//! entities.
//!
//! Pointers can exist on windows, images, and gpu texture views. With
//! [`update_window_hits`] enabled, when a pointer hovers over a window that
//! window will be inserted as a pointer hit, listed behind all other pointer
//! hits. This means that when the pointer isn't hovering any other entities,
//! the picking events will be routed to the window.
//!
//! ## Implementation Notes
//!
//! - This backend does not provide `position` or `normal` in `HitData`.
use core::f32;
use bevy_ecs::prelude::*;
use bevy_render::camera::NormalizedRenderTarget;
use crate::{
backend::{HitData, PointerHits},
pointer::{Location, PointerId, PointerLocation},
};
/// Generates pointer hit events for window entities.
///
/// A pointer is treated as hitting a window when it is located on that window. The order
/// of the hit event is negative infinity, meaning it should appear behind all other entities.
///
/// The depth of the hit will be listed as zero.
pub fn update_window_hits(
pointers: Query<(&PointerId, &PointerLocation)>,
mut output_events: EventWriter<PointerHits>,
) {
for (pointer_id, pointer_location) in pointers.iter() {
if let Some(Location {
target: NormalizedRenderTarget::Window(window_ref),
..
}) = pointer_location.location
{
let entity = window_ref.entity();
let hit_data = HitData::new(entity, 0.0, None, None);
output_events.write(PointerHits::new(
*pointer_id,
vec![(entity, hit_data)],
f32::NEG_INFINITY,
));
}
}
}