#![allow(clippy::type_complexity)]
#![allow(clippy::too_many_arguments)]
#![deny(missing_docs)]
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::{prelude::*, view::RenderLayers};
use bevy_picking_core::backend::prelude::*;
use bevy_xpbd_3d::prelude::*;
pub use bevy_xpbd_3d;
pub mod prelude {
pub use crate::{XpbdBackend, XpbdBackendSettings, XpbdPickable};
}
#[derive(Clone)]
pub struct XpbdBackend;
impl Plugin for XpbdBackend {
fn build(&self, app: &mut App) {
app.init_resource::<XpbdBackendSettings>()
.add_systems(PreUpdate, update_hits.in_set(PickSet::Backend))
.register_type::<XpbdBackendSettings>()
.register_type::<XpbdPickable>();
}
}
#[derive(Resource, Default, Reflect)]
#[reflect(Resource, Default)]
pub struct XpbdBackendSettings {
pub require_markers: bool,
}
#[derive(Debug, Clone, Default, Component, Reflect)]
#[reflect(Component, Default)]
pub struct XpbdPickable;
pub fn update_hits(
picking_cameras: Query<(&Camera, Option<&XpbdPickable>, Option<&RenderLayers>)>,
ray_map: Res<RayMap>,
pickables: Query<&Pickable>,
marked_targets: Query<&XpbdPickable>,
layers: Query<&RenderLayers>,
backend_settings: Res<XpbdBackendSettings>,
spatial_query: Option<Res<SpatialQueryPipeline>>,
mut output_events: EventWriter<PointerHits>,
) {
let Some(spatial_query) = spatial_query else {
return;
};
for (&ray_id, &ray) in ray_map.map().iter() {
let Ok((camera, cam_pickable, cam_layers)) = picking_cameras.get(ray_id.camera) else {
continue;
};
if backend_settings.require_markers && cam_pickable.is_none() || !camera.is_active {
continue;
}
let cam_layers = cam_layers.unwrap_or_default();
if let Some((entity, hit_data)) = spatial_query
.cast_ray_predicate(
ray.origin,
ray.direction,
f32::MAX,
true,
SpatialQueryFilter::default(),
&|entity| {
let marker_requirement =
!backend_settings.require_markers || marked_targets.get(entity).is_ok();
let entity_layers = layers.get(entity).unwrap_or_default();
let render_layers_match = cam_layers.intersects(entity_layers);
let is_pickable = pickables
.get(entity)
.map(|p| *p != Pickable::IGNORE)
.unwrap_or(true);
marker_requirement && render_layers_match && is_pickable
},
)
.map(|ray_hit_data| {
let hit_data = HitData::new(
ray_id.camera,
ray_hit_data.time_of_impact,
Some(ray.origin + (ray.direction * ray_hit_data.time_of_impact)),
Some(ray_hit_data.normal),
);
(ray_hit_data.entity, hit_data)
})
{
output_events.send(PointerHits::new(
ray_id.pointer,
vec![(entity, hit_data)],
camera.order as f32,
));
}
}
}