#![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_rapier3d::prelude::*;
pub use bevy_rapier3d;
pub mod prelude {
pub use crate::{RapierBackend, RapierBackendSettings, RapierPickable};
}
#[derive(Clone)]
pub struct RapierBackend;
impl Plugin for RapierBackend {
fn build(&self, app: &mut App) {
app.init_resource::<RapierBackendSettings>()
.add_systems(PreUpdate, update_hits.in_set(PickSet::Backend))
.register_type::<RapierBackendSettings>()
.register_type::<RapierPickable>();
}
}
#[derive(Resource, Default, Reflect)]
#[reflect(Resource, Default)]
pub struct RapierBackendSettings {
pub require_markers: bool,
}
#[derive(Debug, Clone, Default, Component, Reflect)]
#[reflect(Component, Default)]
pub struct RapierPickable;
pub fn update_hits(
backend_settings: Res<RapierBackendSettings>,
ray_map: Res<RayMap>,
picking_cameras: Query<(&Camera, Option<&RapierPickable>, Option<&RenderLayers>)>,
pickables: Query<&Pickable>,
marked_targets: Query<&RapierPickable>,
layers: Query<&RenderLayers>,
rapier_context: Option<Res<RapierContext>>,
mut output_events: EventWriter<PointerHits>,
) {
let Some(rapier_context) = rapier_context 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() {
continue;
}
let cam_layers = cam_layers.to_owned().unwrap_or_default();
let predicate = |entity| {
let marker_requirement =
!backend_settings.require_markers || marked_targets.get(entity).is_ok();
let entity_layers = layers.get(entity).to_owned().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
};
if let Some((entity, hit_data)) = rapier_context
.cast_ray_and_get_normal(
ray.origin,
*ray.direction,
f32::MAX,
true,
QueryFilter::new().predicate(&predicate),
)
.map(|(entity, hit)| {
let hit_data = HitData::new(
ray_id.camera,
hit.time_of_impact,
Some(hit.point),
Some(hit.normal),
);
(entity, hit_data)
})
{
output_events.send(PointerHits::new(
ray_id.pointer,
vec![(entity, hit_data)],
camera.order as f32,
));
}
}
}