Skip to main content

bevy_render/
extract_component.rs

1use crate::{
2    sync_component::{SyncComponent, SyncComponentPlugin},
3    sync_world::RenderEntity,
4    Extract, ExtractSchedule, RenderApp,
5};
6use bevy_app::{App, Plugin};
7use bevy_camera::visibility::ViewVisibility;
8use bevy_ecs::{
9    bundle::NoBundleEffect,
10    prelude::*,
11    query::{QueryFilter, QueryItem, ReadOnlyQueryData},
12};
13use core::marker::PhantomData;
14
15pub use crate::uniform::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin};
16
17pub use bevy_render_macros::ExtractComponent;
18
19/// Describes how a component gets extracted for rendering.
20///
21/// Therefore the component is transferred from the "app world" into the "render
22/// world" in the [`ExtractSchedule`] step. This functionality is enabled by
23/// adding [`ExtractComponentPlugin`] with the component type.
24///
25/// The Out type is defined in [`SyncComponent`].
26///
27/// The marker type `F` is only used as a way to bypass the orphan rules. To
28/// implement the trait for a foreign type you can use a local type as the
29/// marker, e.g. the type of the plugin that calls [`ExtractComponentPlugin`].
30pub trait ExtractComponent<F = ()>: SyncComponent<F> {
31    /// ECS [`ReadOnlyQueryData`] to fetch the components to extract.
32    type QueryData: ReadOnlyQueryData;
33    /// Filters the entities with additional constraints.
34    type QueryFilter: QueryFilter;
35    /// The output from extraction, i.e. [`ExtractComponent::extract_component`].
36    ///
37    /// The output components won't be removed automatically from the render world if the implementing component is removed,
38    /// unless you set them in the [`SyncComponent::Target`].
39    type Out: Bundle<Effect: NoBundleEffect>;
40    // TODO: https://github.com/rust-lang/rust/issues/29661
41    // type Out: Bundle<Effect: NoBundleEffect> = Self;
42
43    /// Defines how the component is transferred into the "render world".
44    ///
45    /// Returning `None` based on the queried item will remove the [`SyncComponent::Target`] from the entity in
46    /// the render world.
47    fn extract_component(item: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out>;
48}
49
50/// This plugin extracts the components into the render world for synced
51/// entities. To do so, it sets up the [`ExtractSchedule`] step for the
52/// specified [`ExtractComponent`].
53///
54/// It also registers [`SyncComponentPlugin`] to ensure the extracted components
55/// are deleted if the main world components are removed.
56///
57/// The marker type `F` is only used as a way to bypass the orphan rules. To
58/// implement the trait for a foreign type you can use a local type as the
59/// marker, e.g. the type of the plugin that calls [`ExtractComponentPlugin`].
60pub struct ExtractComponentPlugin<C, F = ()> {
61    only_extract_visible: bool,
62    marker: PhantomData<fn() -> (C, F)>,
63}
64
65impl<C, F> Default for ExtractComponentPlugin<C, F> {
66    fn default() -> Self {
67        Self {
68            only_extract_visible: false,
69            marker: PhantomData,
70        }
71    }
72}
73
74impl<C, F> ExtractComponentPlugin<C, F> {
75    pub fn extract_visible() -> Self {
76        Self {
77            only_extract_visible: true,
78            marker: PhantomData,
79        }
80    }
81}
82
83impl<C: ExtractComponent<F>, F: 'static + Send + Sync> Plugin for ExtractComponentPlugin<C, F> {
84    fn build(&self, app: &mut App) {
85        app.add_plugins(SyncComponentPlugin::<C, F>::default());
86
87        if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
88            if self.only_extract_visible {
89                render_app.add_systems(ExtractSchedule, extract_visible_components::<C, F>);
90            } else {
91                render_app.add_systems(ExtractSchedule, extract_components::<C, F>);
92            }
93        }
94    }
95}
96
97/// This system extracts all components of the corresponding [`ExtractComponent`], for entities that are synced via [`crate::sync_world::SyncToRenderWorld`].
98fn extract_components<C: ExtractComponent<F>, F>(
99    mut commands: Commands,
100    mut previous_len: Local<usize>,
101    query: Extract<Query<(RenderEntity, C::QueryData), C::QueryFilter>>,
102) {
103    let mut values = Vec::with_capacity(*previous_len);
104    for (entity, query_item) in &query {
105        if let Some(component) = C::extract_component(query_item) {
106            values.push((entity, component));
107        } else {
108            commands.entity(entity).remove::<C::Target>();
109        }
110    }
111    *previous_len = values.len();
112    commands.try_insert_batch(values);
113}
114
115/// This system extracts all components of the corresponding [`ExtractComponent`], for entities that are visible and synced via [`crate::sync_world::SyncToRenderWorld`].
116fn extract_visible_components<C: ExtractComponent<F>, F>(
117    mut commands: Commands,
118    mut previous_len: Local<usize>,
119    query: Extract<Query<(RenderEntity, &ViewVisibility, C::QueryData), C::QueryFilter>>,
120) {
121    let mut values = Vec::with_capacity(*previous_len);
122    for (entity, view_visibility, query_item) in &query {
123        if view_visibility.get() {
124            if let Some(component) = C::extract_component(query_item) {
125                values.push((entity, component));
126            } else {
127                commands.entity(entity).remove::<C::Target>();
128            }
129        }
130    }
131    *previous_len = values.len();
132    commands.try_insert_batch(values);
133}