use std::{marker::PhantomData, sync::PoisonError};
use bevy_ecs::{
prelude::*,
query::{QueryFilter, QueryItem, ReadOnlyQueryData},
system::{ReadOnlySystemParam, StaticSystemParam, SystemParamItem},
};
use bevy_reflect::prelude::*;
use bevy_render::{
prelude::*,
sync_world::RenderEntity,
view::{ExtractedView, RenderVisibleEntities},
Extract,
};
use fixedbitset::FixedBitSet;
use crate::vertex::{Vertex, VertexQueues};
pub trait Drawer: TypePath + Component + Sized {
type Vertex: Vertex;
type ExtractParam: ReadOnlySystemParam;
type ExtractData: ReadOnlyQueryData;
type ExtractFilter: QueryFilter;
type DrawParam: ReadOnlySystemParam;
fn extract(
drawer: DrawerExtract<Self>,
param: &SystemParamItem<Self::ExtractParam>,
query: QueryItem<Self::ExtractData>,
);
fn enqueue(
&self,
param: &SystemParamItem<Self::DrawParam>,
queuer: &mut impl Extend<(f32, <Self::Vertex as Vertex>::PipelineKey, <Self::Vertex as Vertex>::Command)>,
);
}
pub enum DrawerExtract<'a, T: Drawer> {
Borrowed(&'a mut T),
Spawn(&'a mut Option<T>),
}
impl<T: Drawer> DrawerExtract<'_, T> {
#[inline]
pub fn get_mut(&mut self, new: impl FnOnce() -> T) -> &mut T {
match self {
Self::Borrowed(value) => value,
Self::Spawn(opt) => opt.insert(new()),
}
}
#[inline]
pub fn get_or_default(&mut self) -> &mut T
where
T: Default,
{
self.get_mut(Default::default)
}
}
#[derive(Reflect, Component, Copy, Clone)]
#[reflect(Component, Default)]
#[require(Visibility)]
pub struct HasDrawer<T: Drawer>(#[reflect(ignore)] pub PhantomData<fn() -> T>);
impl<T: Drawer> Default for HasDrawer<T> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<T: Drawer> HasDrawer<T> {
#[inline]
pub const fn new() -> Self {
Self(PhantomData)
}
}
pub(crate) fn extract_drawers<T: Drawer>(
mut commands: Commands,
param: Extract<T::ExtractParam>,
query: Extract<Query<(RenderEntity, &ViewVisibility, T::ExtractData), (T::ExtractFilter, With<HasDrawer<T>>)>>,
mut target_query: Query<&mut T>,
) {
for (e, &view, data) in &query {
if view.get() {
if let Ok(mut dst) = target_query.get_mut(e) {
T::extract(DrawerExtract::Borrowed(&mut dst), ¶m, data)
} else {
let mut extract = None;
T::extract(DrawerExtract::Spawn(&mut extract), ¶m, data);
if let Some(extract) = extract {
commands.entity(e).insert(extract);
}
}
}
}
}
pub(crate) fn queue_drawers<T: Drawer>(
param: StaticSystemParam<T::DrawParam>,
query: Query<&T>,
views: Query<(Entity, &RenderVisibleEntities), With<ExtractedView>>,
queues: Res<VertexQueues<T::Vertex>>,
mut iterated: Local<FixedBitSet>,
) {
iterated.clear();
for (view_entity, visible_entities) in &views {
for &(e, main_e) in visible_entities.iter::<With<HasDrawer<T>>>() {
let index = e.index() as usize;
if iterated[index] {
continue;
}
let Ok(drawer) = query.get(e) else { continue };
iterated.grow_and_insert(index);
queues.entities.entry(view_entity).or_default().insert((e, main_e));
drawer.enqueue(¶m, &mut *queues.commands.entry(e).or_default());
}
}
queues
.entity_bits
.write()
.unwrap_or_else(PoisonError::into_inner)
.union_with(&iterated);
}