wde_renderer/sync/
extract_component_plugin.rs

1use std::marker::PhantomData;
2
3use bevy::{
4    ecs::{
5        bundle::NoBundleEffect,
6        query::{QueryFilter, ReadOnlyQueryData},
7    },
8    prelude::*,
9};
10
11use crate::{
12    core::{Extract, ExtractWorld, RenderApp},
13    sync::{RenderEntity, SyncComponent, SyncComponentPlugin},
14};
15
16pub use bevy::ecs::query::QueryItem;
17
18/// Describes how a component gets extracted for rendering.
19///
20/// Therefore the component is transferred from the "app world" into the "render world" in the [`Extract`] step. This functionality is enabled by adding [`ExtractComponentPlugin`] with the component type.
21///
22/// The Out type is defined in [`SyncComponent`].
23/// See [sync](crate::sync) for more details.
24pub trait ExtractComponent<F = ()>: SyncComponent<F> {
25    /// ECS [`ReadOnlyQueryData`] to fetch the components to extract.
26    type QueryData: ReadOnlyQueryData;
27    /// Filters the entities with additional constraints.
28    type QueryFilter: QueryFilter;
29    /// The output from extraction, i.e. [`ExtractComponent::extract_component`].
30    type Out: Bundle<Effect: NoBundleEffect>;
31
32    /// Defines how the component is transferred into the "render world".
33    /// Returning `None` based on the queried item will remove the [`SyncComponent::Target`] from the entity in the render world.
34    fn extract_component(item: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out>;
35}
36
37/// This plugin extracts the components into the render world for synced entities.
38/// To do so, it sets up the [`Extract`] step for the specified [`ExtractComponent`].
39///
40/// It also registers [`SyncComponentPlugin`] to ensure the extracted components are deleted if the main world components are removed.
41/// See [sync](crate::sync) for more details.
42pub struct ExtractComponentPlugin<C, F = ()>(PhantomData<fn() -> (C, F)>);
43impl<C, F> Default for ExtractComponentPlugin<C, F> {
44    fn default() -> Self {
45        Self(PhantomData)
46    }
47}
48impl<C: ExtractComponent<F>, F: 'static + Send + Sync> Plugin for ExtractComponentPlugin<C, F> {
49    fn build(&self, app: &mut App) {
50        app.add_plugins(SyncComponentPlugin::<C, F>::default());
51        app.get_sub_app_mut(RenderApp)
52            .unwrap()
53            .add_systems(Extract, extract_components::<C, F>);
54    }
55}
56
57/// This system extracts all components of the corresponding [`ExtractComponent`], for entities that are synced via [`crate::sync_world::SyncToRenderWorld`].
58#[allow(clippy::type_complexity)]
59fn extract_components<C: ExtractComponent<F>, F>(
60    mut commands: Commands,
61    mut previous_len: Local<usize>,
62    query: ExtractWorld<Query<(RenderEntity, C::QueryData), C::QueryFilter>>,
63) {
64    let mut values = Vec::with_capacity(*previous_len);
65    for (entity, query_item) in &query {
66        if let Some(component) = C::extract_component(query_item) {
67            values.push((entity, component));
68        } else {
69            commands.entity(entity).remove::<C::Target>();
70        }
71    }
72    *previous_len = values.len();
73    commands.try_insert_batch(values);
74}