use bevy::prelude::*;
use bevy_mod_picking::{backend::PointerHits, prelude::*};
use lunex_engine::YInvert;
use std::cmp::Ordering;
use bevy::window::PrimaryWindow;
use bevy_mod_picking::backend::prelude::*;
use crate::{Dimension, Element};
#[derive(Clone)]
pub struct LunexBackend;
impl Plugin for LunexBackend {
fn build(&self, app: &mut App) {
app
.add_plugins(DefaultPickingPlugins.build().disable::<InputPlugin>())
.add_systems(PreUpdate, lunex_picking.in_set(PickSet::Backend))
.add_systems(Update, rendered_texture_picking);
}
}
pub fn lunex_picking(
pointers: Query<(&PointerId, &PointerLocation)>,
cameras: Query<(Entity, &Camera, &GlobalTransform, &OrthographicProjection)>,
primary_window: Query<Entity, With<PrimaryWindow>>,
node_query: Query<
(
Entity,
&Dimension,
Option<&Element>,
&GlobalTransform,
Option<&Pickable>,
&ViewVisibility,
)
>,
mut output: EventWriter<PointerHits>,
) {
let mut sorted_nodes: Vec<_> = node_query.iter().collect();
sorted_nodes.sort_by(|a, b| { (b.3.translation().z).partial_cmp(&a.3.translation().z).unwrap_or(Ordering::Equal) });
for (pointer, location) in pointers.iter().filter_map(|(pointer, pointer_location)| { pointer_location.location().map(|loc| (pointer, loc)) }) {
let mut blocked = false;
let Some((cam_entity, camera, cam_transform, cam_ortho)) = cameras.iter().filter(|(_, camera, _, _)| camera.is_active)
.find(|(_, camera, _, _)| {
camera
.target
.normalize(Some(match primary_window.get_single() {
Ok(w) => w,
Err(_) => return false,
}))
.unwrap()
== location.target
})
else { continue; };
let Some(cursor_pos_world) = camera.viewport_to_world_2d(cam_transform, location.position) else { continue; };
let picks: Vec<(Entity, HitData)> = sorted_nodes
.iter()
.copied()
.filter(|(.., visibility)| visibility.get())
.filter_map(
|(entity, dimension, element, node_transform, pickable, ..)| {
if blocked {
return None;
}
let pos = if !element.is_some() { dimension.size.invert_y() / 2.0 } else { Vec2::ZERO };
let rect = Rect::from_center_size(pos, dimension.size);
let cursor_pos_sprite = node_transform
.affine()
.inverse()
.transform_point3((cursor_pos_world, 0.0).into());
let is_cursor_in_sprite = rect.contains(cursor_pos_sprite.truncate());
blocked = is_cursor_in_sprite && pickable.map(|p| p.should_block_lower) != Some(false);
let depth = -cam_ortho.near - node_transform.translation().z;
is_cursor_in_sprite.then_some((entity, HitData::new(cam_entity, depth, None, None)))
},
)
.collect();
let order = camera.order as f32;
output.send(PointerHits::new(*pointer, picks, order));
}
}
#[derive(Component)]
pub struct PickingPortal;
pub fn rendered_texture_picking(
mut events: EventReader<Pointer<Move>>,
texture_viewports: Query<&Handle<Image>, With<PickingPortal>>,
mut pointer_move: EventWriter<pointer::InputMove>,
) {
for event in events.read() {
if let Ok(texture_handle) = texture_viewports.get(event.target) {
let position = event.pointer_location.position;
pointer_move.send(pointer::InputMove {
pointer_id: event.pointer_id,
location: pointer::Location {
target: bevy::render::camera::NormalizedRenderTarget::Image(
texture_handle.clone_weak(),
),
position,
},
delta: event.delta,
});
}
}
}