use std::{marker::PhantomData, sync::PoisonError};
use bevy_app::prelude::*;
use bevy_ecs::{
prelude::*,
query::{QueryFilter, QueryItem, ReadOnlyQueryData},
system::{ReadOnlySystemParam, StaticSystemParam, SystemParamItem},
};
use bevy_reflect::prelude::*;
use bevy_render::{
prelude::*,
sync_component::SyncComponentPlugin,
sync_world::RenderEntity,
view::{ExtractedView, RenderVisibleEntities},
Extract, Render, RenderApp,
};
use fixedbitset::FixedBitSet;
use crate::{
vertex::{Vertex, VertexDrawers, VertexQueues},
HephaeSystems,
};
pub struct DrawerPlugin<D: Drawer>(PhantomData<fn() -> D>);
impl<T: Drawer> Default for DrawerPlugin<T> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<D: Drawer> DrawerPlugin<D> {
pub const fn new() -> Self {
Self(PhantomData)
}
}
impl<D: Drawer> Plugin for DrawerPlugin<D> {
fn build(&self, app: &mut App) {
app.add_plugins(SyncComponentPlugin::<HasDrawer<D>>::default());
app.world_mut()
.resource_scope::<VertexDrawers<D::Vertex>, ()>(|world, mut drawers| drawers.add::<D>(world));
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
.add_systems(ExtractSchedule, extract_drawers::<D>)
.add_systems(Render, queue_drawers::<D>.in_set(HephaeSystems::QueueDrawers));
}
}
}
pub trait Drawer: Component + Sized {
type Vertex: Vertex;
type ExtractParam: ReadOnlySystemParam;
type ExtractData: ReadOnlyQueryData;
type ExtractFilter: QueryFilter;
type DrawParam: ReadOnlySystemParam;
fn extract(param: &SystemParamItem<Self::ExtractParam>, query: QueryItem<Self::ExtractData>) -> Option<Self>;
fn enqueue(
&self,
param: &SystemParamItem<Self::DrawParam>,
queuer: &mut impl Extend<(f32, <Self::Vertex as Vertex>::PipelineKey, <Self::Vertex as Vertex>::Command)>,
);
}
#[derive(Reflect, Component, Copy, Clone)]
#[reflect(Component)]
#[require(Visibility)]
pub struct HasDrawer<T: Drawer>(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 fn extract_drawers<T: Drawer>(
mut commands: Commands,
param: Extract<T::ExtractParam>,
query: Extract<Query<(RenderEntity, &ViewVisibility, T::ExtractData), (T::ExtractFilter, With<HasDrawer<T>>)>>,
) {
for (e, &view, data) in &query {
if view.get() {
if let Some(out) = T::extract(¶m, data) {
commands.entity(e).insert(out);
} else {
commands.entity(e).remove::<T>();
}
}
}
}
pub 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);
}