use crate::{ render_resource::DynamicUniformVec, renderer::{RenderDevice, RenderQueue}, RenderApp, RenderStage, }; use bevy_app::{App, Plugin}; use bevy_asset::{Asset, Handle}; use bevy_ecs::{ component::Component, prelude::*, query::{FilterFetch, QueryItem, WorldQuery}, system::{ lifetimeless::{Read, SCommands, SQuery}, RunSystem, SystemParamItem, }, }; use crevice::std140::AsStd140; use std::{marker::PhantomData, ops::Deref}; /// Stores the index of a uniform inside of [`ComponentUniforms`]. #[derive(Component)] pub struct DynamicUniformIndex { index: u32, marker: PhantomData, } impl DynamicUniformIndex { #[inline] pub fn index(&self) -> u32 { self.index } } /// Describes how a component gets extracted for rendering. /// /// Therefore the component is transferred from the "app world" into the "render world" /// in the [`RenderStage::Extract`](crate::RenderStage::Extract) step. pub trait ExtractComponent: Component { /// ECS [`WorldQuery`] to fetch the components to extract. type Query: WorldQuery; /// Filters the entities with additional constraints. type Filter: WorldQuery; /// Defines how the component is transferred into the "render world". fn extract_component(item: QueryItem) -> Self; } /// This plugin prepares the components of the corresponding type for the GPU /// by transforming them into uniforms. /// /// They can then be accessed from the [`ComponentUniforms`] resource. /// For referencing the newly created uniforms a [`DynamicUniformIndex`] is inserted /// for every processed entity. /// /// Therefore it sets up the [`RenderStage::Prepare`](crate::RenderStage::Prepare) step /// for the specified [`ExtractComponent`]. pub struct UniformComponentPlugin(PhantomData C>); impl Default for UniformComponentPlugin { fn default() -> Self { Self(PhantomData) } } impl Plugin for UniformComponentPlugin { fn build(&self, app: &mut App) { app.sub_app(RenderApp) .insert_resource(ComponentUniforms::::default()) .add_system_to_stage( RenderStage::Prepare, prepare_uniform_components::.system(), ); } } /// Stores all uniforms of the component type. pub struct ComponentUniforms { uniforms: DynamicUniformVec, } impl Deref for ComponentUniforms { type Target = DynamicUniformVec; #[inline] fn deref(&self) -> &Self::Target { &self.uniforms } } impl ComponentUniforms { #[inline] pub fn uniforms(&self) -> &DynamicUniformVec { &self.uniforms } } impl Default for ComponentUniforms { fn default() -> Self { Self { uniforms: Default::default(), } } } /// This system prepares all components of the corresponding component type. /// They are transformed into uniforms and stored in the [`ComponentUniforms`] resource. fn prepare_uniform_components( mut commands: Commands, render_device: Res, render_queue: Res, mut component_uniforms: ResMut>, components: Query<(Entity, &C)>, ) where C: AsStd140 + Clone, { component_uniforms.uniforms.clear(); for (entity, component) in components.iter() { commands .get_or_spawn(entity) .insert(DynamicUniformIndex:: { index: component_uniforms.uniforms.push(component.clone()), marker: PhantomData, }); } component_uniforms .uniforms .write_buffer(&render_device, &render_queue); } /// This plugin extracts the components into the "render world". /// /// Therefore it sets up the [`RenderStage::Extract`](crate::RenderStage::Extract) step /// for the specified [`ExtractComponent`]. pub struct ExtractComponentPlugin(PhantomData (C, F)>); impl Default for ExtractComponentPlugin { fn default() -> Self { Self(PhantomData) } } impl Plugin for ExtractComponentPlugin where ::Fetch: FilterFetch, { fn build(&self, app: &mut App) { let system = ExtractComponentSystem::::system(&mut app.world); let render_app = app.sub_app(RenderApp); render_app.add_system_to_stage(RenderStage::Extract, system); } } impl ExtractComponent for Handle { type Query = Read>; type Filter = (); #[inline] fn extract_component(handle: QueryItem) -> Self { handle.clone_weak() } } /// This system extracts all components of the corresponding [`ExtractComponent`] type. pub struct ExtractComponentSystem(PhantomData); impl RunSystem for ExtractComponentSystem where ::Fetch: FilterFetch, { type Param = ( SCommands, // the previous amount of extracted components Local<'static, usize>, SQuery<(Entity, C::Query), C::Filter>, ); fn run((mut commands, mut previous_len, mut query): SystemParamItem) { let mut values = Vec::with_capacity(*previous_len); for (entity, query_item) in query.iter_mut() { values.push((entity, (C::extract_component(query_item),))); } *previous_len = values.len(); commands.insert_or_spawn_batch(values); } }