#![allow(clippy::type_complexity)]
use bevy_app::{Plugin, PreUpdate};
use bevy_ecs::{
component::Component,
entity::Entity,
event::EventWriter,
query::With,
system::{Query, Res},
};
use bevy_math::{primitives::InfinitePlane3d, Vec2, Vec3Swizzles};
use bevy_mod_picking::backend::{ray::RayMap, HitData, PointerHits};
use bevy_rectray::{RotatedRect, Transform2D};
use bevy_render::{camera::Camera, view::RenderLayers};
use bevy_transform::components::GlobalTransform;
#[derive(Debug, Component, Default, Clone, Copy, PartialEq, Eq)]
pub struct RectrayPickable;
pub fn rectray_picking_backend(
map: Res<RayMap>,
layers: Query<(Option<&RenderLayers>, &Camera)>,
query: Query<
(
Entity,
&RotatedRect,
&GlobalTransform,
&Transform2D,
Option<&RenderLayers>,
),
With<RectrayPickable>,
>,
mut writer: EventWriter<PointerHits>,
) {
for (ray_id, ray) in map.iter() {
let Ok((layer, cam)) = layers.get(ray_id.camera) else {
continue;
};
let cam_layer = if let Some(layer) = layer {
layer
} else {
&RenderLayers::default()
};
let mut event = PointerHits {
pointer: ray_id.pointer,
picks: Vec::new(),
order: cam.order as f32,
};
for (entity, rect, transform, transform_2d, layers) in query.iter() {
let layer = if let Some(layer) = layers {
layer
} else {
&RenderLayers::default()
};
if !cam_layer.intersects(layer) {
continue;
}
let plane = InfinitePlane3d::new(transform.forward());
let Some(depth) = ray.intersect_plane(transform.translation(), plane) else {
continue;
};
let position = ray.get_point(depth);
let local = transform.affine().inverse().transform_point3(position);
let local = local.xy() - rect.dimension * transform_2d.center;
let half_size = rect.dimension * rect.scale / 2.0;
let inside = Vec2::from_angle(-rect.rotation)
.rotate(local)
.abs()
.cmple(half_size)
.all();
if inside {
event.picks.push((
entity,
HitData {
camera: ray_id.camera,
depth,
position: Some(position),
normal: Some(transform.forward().into()),
},
))
}
}
if !event.picks.is_empty() {
writer.send(event);
}
}
}
pub struct RectrayPickingBackendPlugin;
impl Plugin for RectrayPickingBackendPlugin {
fn build(&self, app: &mut bevy_app::App) {
app.add_systems(PreUpdate, rectray_picking_backend);
}
}