1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
use bevy::{
ecs::{query::QueryFilter, system::SystemParam},
prelude::*,
};
/// Top-level non-transparent or otherwise interaction blocking Node. All such
/// nodes are marked with this component and no descendants have it attached.
///
/// These nodes block mouse based interaction with the 3D world behind them.
/// These nodes do not block UI interaction: if desired, this must be done via
/// native bevi_ui mechanisms.
#[derive(Component)]
pub(crate) struct InteractionBlocker;
#[derive(SystemParam)]
pub(crate) struct HudNodes<'w, 's, F = With<InteractionBlocker>>
where
F: QueryFilter + Sync + Send + 'static,
{
hud: Query<
'w,
's,
(
&'static GlobalTransform,
&'static ViewVisibility,
&'static Node,
),
F,
>,
}
impl<'w, 's, F> HudNodes<'w, 's, F>
where
F: QueryFilter + Sync + Send + 'static,
{
/// See [`Self::relative_position`].
pub(crate) fn contains_point(&self, point: Vec2) -> bool {
self.relative_position(point).is_some()
}
/// Returns relative position of `point` to the fist Node which contains
/// the point.
///
/// The returned point is between (0, 0) (top-left corner) and (1, 1)
/// (bottom-right corner).
///
/// The method relies on [`ViewVisibility`], therefore the results are
/// accurate with respect to the last rendered frame only iff called before
/// [`bevy::render::view::VisibilitySystems::VisibilityPropagate`] (during
/// `PostUpdate` schedule).
pub(crate) fn relative_position(&self, point: Vec2) -> Option<Vec2> {
self.hud
.iter()
.filter_map(|(box_transform, visibility, node)| {
if !visibility.get() {
return None;
}
let box_size = node.size();
let box_transform: Vec3 = box_transform.translation();
// GlobalTransform is centered, width/2 to left and to right,
// same on vertical.
let box_position = box_transform.xy() - box_size / 2.;
let relative = (point - box_position) / box_size;
if relative.cmpge(Vec2::ZERO).all() && relative.cmple(Vec2::ONE).all() {
Some(relative)
} else {
None
}
})
.next()
}
}