|
| 1 | +//! Convenience logic for turning components from the main world into render |
| 2 | +//! instances in the render world. |
| 3 | +//! |
| 4 | +//! This is essentially the same as the `extract_component` module, but |
| 5 | +//! higher-performance because it avoids the ECS overhead. |
| 6 | +
|
| 7 | +use std::marker::PhantomData; |
| 8 | + |
| 9 | +use bevy_app::{App, Plugin}; |
| 10 | +use bevy_asset::{Asset, AssetId, Handle}; |
| 11 | +use bevy_derive::{Deref, DerefMut}; |
| 12 | +use bevy_ecs::{ |
| 13 | + component::Component, |
| 14 | + prelude::Entity, |
| 15 | + query::{QueryItem, ReadOnlyWorldQuery, WorldQuery}, |
| 16 | + system::{lifetimeless::Read, Query, ResMut, Resource}, |
| 17 | +}; |
| 18 | +use bevy_utils::EntityHashMap; |
| 19 | + |
| 20 | +use crate::{prelude::ViewVisibility, Extract, ExtractSchedule, RenderApp}; |
| 21 | + |
| 22 | +/// Describes how a component gets turned into a render instance for rendering. |
| 23 | +/// |
| 24 | +/// Before rendering, a component will be transferred from the main world to the |
| 25 | +/// render world in the [`ExtractSchedule`](crate::ExtractSchedule) step. |
| 26 | +/// |
| 27 | +/// This is essentially the same as |
| 28 | +/// [`ExtractComponent`](crate::extract_component::ExtractComponent), but |
| 29 | +/// higher-performance because it avoids the ECS overhead. |
| 30 | +pub trait ExtractToRenderInstance: Component { |
| 31 | + /// ECS [`WorldQuery`] to fetch the components to extract. |
| 32 | + type Query: WorldQuery + ReadOnlyWorldQuery; |
| 33 | + /// Filters the entities with additional constraints. |
| 34 | + type Filter: WorldQuery + ReadOnlyWorldQuery; |
| 35 | + |
| 36 | + type Instance: Send + Sync; |
| 37 | + |
| 38 | + /// Defines how the component is transferred into the "render world". |
| 39 | + fn extract_to_render_instance(item: QueryItem<'_, Self::Query>) -> Option<Self::Instance>; |
| 40 | +} |
| 41 | + |
| 42 | +/// This plugin extracts the components into the "render world" as render |
| 43 | +/// instances. |
| 44 | +/// |
| 45 | +/// Therefore it sets up the [`ExtractSchedule`](crate::ExtractSchedule) step |
| 46 | +/// for the specified [`RenderInstances`]. |
| 47 | +#[derive(Default)] |
| 48 | +pub struct ExtractToRenderInstancePlugin<C> |
| 49 | +where |
| 50 | + C: ExtractToRenderInstance, |
| 51 | +{ |
| 52 | + only_extract_visible: bool, |
| 53 | + marker: PhantomData<fn() -> C>, |
| 54 | +} |
| 55 | + |
| 56 | +/// Stores all render instances corresponding to the given component in the render world. |
| 57 | +#[derive(Resource, Deref, DerefMut)] |
| 58 | +pub struct RenderInstances<C>(EntityHashMap<Entity, C::Instance>) |
| 59 | +where |
| 60 | + C: ExtractToRenderInstance; |
| 61 | + |
| 62 | +impl<C> Default for RenderInstances<C> |
| 63 | +where |
| 64 | + C: ExtractToRenderInstance, |
| 65 | +{ |
| 66 | + fn default() -> Self { |
| 67 | + Self(Default::default()) |
| 68 | + } |
| 69 | +} |
| 70 | + |
| 71 | +impl<C> ExtractToRenderInstancePlugin<C> |
| 72 | +where |
| 73 | + C: ExtractToRenderInstance, |
| 74 | +{ |
| 75 | + /// Creates a new [`ExtractToRenderInstancePlugin`] that unconditionally |
| 76 | + /// extracts the component to the render world, whether visible or not. |
| 77 | + pub fn new() -> Self { |
| 78 | + Self { |
| 79 | + only_extract_visible: false, |
| 80 | + marker: PhantomData, |
| 81 | + } |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +impl<C> ExtractToRenderInstancePlugin<C> |
| 86 | +where |
| 87 | + C: ExtractToRenderInstance, |
| 88 | +{ |
| 89 | + /// Creates a new [`ExtractToRenderInstancePlugin`] that extracts the |
| 90 | + /// component to the render world if and only if the entity it's attached to |
| 91 | + /// is visible. |
| 92 | + pub fn extract_visible() -> Self { |
| 93 | + Self { |
| 94 | + only_extract_visible: true, |
| 95 | + marker: PhantomData, |
| 96 | + } |
| 97 | + } |
| 98 | +} |
| 99 | + |
| 100 | +impl<C> Plugin for ExtractToRenderInstancePlugin<C> |
| 101 | +where |
| 102 | + C: ExtractToRenderInstance, |
| 103 | +{ |
| 104 | + fn build(&self, app: &mut App) { |
| 105 | + if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { |
| 106 | + render_app.init_resource::<RenderInstances<C>>(); |
| 107 | + if self.only_extract_visible { |
| 108 | + render_app.add_systems(ExtractSchedule, extract_visible_to_render_instances::<C>); |
| 109 | + } else { |
| 110 | + render_app.add_systems(ExtractSchedule, extract_to_render_instances::<C>); |
| 111 | + } |
| 112 | + } |
| 113 | + } |
| 114 | +} |
| 115 | + |
| 116 | +fn extract_to_render_instances<C>( |
| 117 | + mut instances: ResMut<RenderInstances<C>>, |
| 118 | + query: Extract<Query<(Entity, C::Query), C::Filter>>, |
| 119 | +) where |
| 120 | + C: ExtractToRenderInstance, |
| 121 | +{ |
| 122 | + instances.clear(); |
| 123 | + for (entity, other) in &query { |
| 124 | + if let Some(render_instance) = C::extract_to_render_instance(other) { |
| 125 | + instances.insert(entity, render_instance); |
| 126 | + } |
| 127 | + } |
| 128 | +} |
| 129 | + |
| 130 | +fn extract_visible_to_render_instances<C>( |
| 131 | + mut instances: ResMut<RenderInstances<C>>, |
| 132 | + query: Extract<Query<(Entity, &ViewVisibility, C::Query), C::Filter>>, |
| 133 | +) where |
| 134 | + C: ExtractToRenderInstance, |
| 135 | +{ |
| 136 | + instances.clear(); |
| 137 | + for (entity, view_visibility, other) in &query { |
| 138 | + if view_visibility.get() { |
| 139 | + if let Some(render_instance) = C::extract_to_render_instance(other) { |
| 140 | + instances.insert(entity, render_instance); |
| 141 | + } |
| 142 | + } |
| 143 | + } |
| 144 | +} |
| 145 | + |
| 146 | +impl<A> ExtractToRenderInstance for Handle<A> |
| 147 | +where |
| 148 | + A: Asset, |
| 149 | +{ |
| 150 | + type Query = Read<Handle<A>>; |
| 151 | + type Filter = (); |
| 152 | + type Instance = AssetId<A>; |
| 153 | + |
| 154 | + fn extract_to_render_instance(item: QueryItem<'_, Self::Query>) -> Option<Self::Instance> { |
| 155 | + Some(item.id()) |
| 156 | + } |
| 157 | +} |
0 commit comments