//! Convenience logic for turning components from the main world into extracted //! instances in the render world. //! //! This is essentially the same as the `extract_component` module, but //! higher-performance because it avoids the ECS overhead. use std::marker::PhantomData; use bevy_app::{App, Plugin}; use bevy_asset::{Asset, AssetId, Handle}; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ prelude::Entity, query::{QueryFilter, QueryItem, ReadOnlyQueryData}, system::{lifetimeless::Read, Query, ResMut, Resource}, }; use bevy_utils::EntityHashMap; use crate::{prelude::ViewVisibility, Extract, ExtractSchedule, RenderApp}; /// Describes how to extract data needed for rendering from a component or /// components. /// /// Before rendering, any applicable components will be transferred from the /// main world to the render world in the [`ExtractSchedule`] step. /// /// This is essentially the same as /// [`ExtractComponent`](crate::extract_component::ExtractComponent), but /// higher-performance because it avoids the ECS overhead. pub trait ExtractInstance: Send + Sync + Sized + 'static { /// ECS [`ReadOnlyQueryData`] to fetch the components to extract. type QueryData: ReadOnlyQueryData; /// Filters the entities with additional constraints. type QueryFilter: QueryFilter; /// Defines how the component is transferred into the "render world". fn extract(item: QueryItem<'_, Self::QueryData>) -> Option; } /// This plugin extracts one or more components into the "render world" as /// extracted instances. /// /// Therefore it sets up the [`ExtractSchedule`] step for the specified /// [`ExtractedInstances`]. #[derive(Default)] pub struct ExtractInstancesPlugin where EI: ExtractInstance, { only_extract_visible: bool, marker: PhantomData EI>, } /// Stores all extract instances of a type in the render world. #[derive(Resource, Deref, DerefMut)] pub struct ExtractedInstances(EntityHashMap) where EI: ExtractInstance; impl Default for ExtractedInstances where EI: ExtractInstance, { fn default() -> Self { Self(Default::default()) } } impl ExtractInstancesPlugin where EI: ExtractInstance, { /// Creates a new [`ExtractInstancesPlugin`] that unconditionally extracts to /// the render world, whether the entity is visible or not. pub fn new() -> Self { Self { only_extract_visible: false, marker: PhantomData, } } /// Creates a new [`ExtractInstancesPlugin`] that extracts to the render world /// if and only if the entity it's attached to is visible. pub fn extract_visible() -> Self { Self { only_extract_visible: true, marker: PhantomData, } } } impl Plugin for ExtractInstancesPlugin where EI: ExtractInstance, { fn build(&self, app: &mut App) { if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { render_app.init_resource::>(); if self.only_extract_visible { render_app.add_systems(ExtractSchedule, extract_visible::); } else { render_app.add_systems(ExtractSchedule, extract_all::); } } } } fn extract_all( mut extracted_instances: ResMut>, query: Extract>, ) where EI: ExtractInstance, { extracted_instances.clear(); for (entity, other) in &query { if let Some(extract_instance) = EI::extract(other) { extracted_instances.insert(entity, extract_instance); } } } fn extract_visible( mut extracted_instances: ResMut>, query: Extract>, ) where EI: ExtractInstance, { extracted_instances.clear(); for (entity, view_visibility, other) in &query { if view_visibility.get() { if let Some(extract_instance) = EI::extract(other) { extracted_instances.insert(entity, extract_instance); } } } } impl ExtractInstance for AssetId where A: Asset, { type QueryData = Read>; type QueryFilter = (); fn extract(item: QueryItem<'_, Self::QueryData>) -> Option { Some(item.id()) } }