use bevy_derive::{Deref, DerefMut};
use bevy_ecs::prelude::*;
use bevy_render::view::RenderLayers;
use bitfield::bitfield;
use rmf_site_camera::{VISUAL_CUE_RENDER_LAYER, XRAY_RENDER_LAYER};
use smallvec::SmallVec;
bitfield! {
#[derive(Copy, Clone)]
pub struct VisibilityDependents(u8);
impl Debug;
#[inline]
pub always, set_always: 0;
#[inline]
pub selected, set_selected: 1;
#[inline]
pub hovered, set_hovered: 2;
#[inline]
pub support_always, set_support_always: 3;
#[inline]
pub support_selected, set_support_selected: 4;
#[inline]
pub support_hovered, set_support_hovered: 5;
#[inline]
pub proximity, set_proximity: 6;
#[inline]
pub unassigned, set_unassigned: 7;
}
impl VisibilityDependents {
pub fn new_always() -> VisibilityDependents {
VisibilityDependents(1)
}
pub fn new_none() -> VisibilityDependents {
VisibilityDependents(0)
}
pub fn none(&self) -> bool {
self.0 == 0
}
pub fn any(&self) -> bool {
!self.none()
}
pub fn union(mut self, other: Self) -> Self {
self.0 = self.0 | other.0;
self
}
}
#[derive(Component, Debug, Clone, Copy)]
pub struct VisualCue {
pub allow_outline: bool,
pub regular: VisibilityDependents,
pub xray: VisibilityDependents,
}
#[derive(Component, Debug, Clone, Copy, Deref, DerefMut)]
pub struct ComputedVisualCue(pub VisualCue);
#[derive(Component, Debug, Clone, Copy)]
pub struct DisableXray;
impl VisualCue {
pub fn outline() -> VisualCue {
VisualCue {
allow_outline: true,
regular: VisibilityDependents::new_always(),
xray: VisibilityDependents::new_none(),
}
}
pub fn no_outline() -> VisualCue {
VisualCue {
allow_outline: false,
regular: VisibilityDependents::new_always(),
xray: VisibilityDependents::new_none(),
}
}
pub fn irregular(mut self) -> VisualCue {
self.regular.set_always(false);
self
}
pub fn always_xray(mut self) -> VisualCue {
self.xray.set_always(true);
self
}
pub fn layers(&self) -> RenderLayers {
let mut layers = RenderLayers::none();
if self.regular.any() {
layers = layers.with(VISUAL_CUE_RENDER_LAYER);
}
if self.xray.any() {
layers = layers.with(XRAY_RENDER_LAYER);
}
layers
}
pub fn with_constraints(mut self, constraints: VisualCueConstraints) -> Self {
if constraints.disable_xray {
self.regular = self.regular.union(self.xray);
self.xray = VisibilityDependents::new_none();
}
self
}
}
pub struct VisualCueConstraints {
pub disable_xray: bool,
}
impl VisualCueConstraints {
pub fn new(disable_xray: bool) -> Self {
Self { disable_xray }
}
}
pub fn propagate_visual_cues(
mut commands: Commands,
changed_cues: Query<(Entity, &VisualCue), Or<(Changed<VisualCue>, Changed<Children>)>>,
children: Query<&Children>,
existing_cues: Query<(), With<VisualCue>>,
disabled_xray: Query<(), With<DisableXray>>,
) {
for (e, root_cue) in &changed_cues {
let mut queue = SmallVec::<[(Entity, VisualCue); 5]>::new();
queue.push((e, root_cue.clone()));
while let Some((top, top_cue)) = queue.pop() {
commands
.entity(top)
.insert(top_cue.layers())
.insert(ComputedVisualCue(top_cue));
if let Ok(children) = children.get(top) {
for child in children {
if existing_cues.contains(*child) {
continue;
}
let disable_xray = disabled_xray.get(*child).is_ok();
let constraints = VisualCueConstraints::new(disable_xray);
queue.push((*child, top_cue.clone().with_constraints(constraints)));
}
}
}
}
}