1use std::{any::TypeId, hash::Hash, marker::PhantomData, ops::Range, sync::Mutex};
6
7use bevy_app::prelude::*;
8use bevy_ecs::{
9 component::ComponentId,
10 entity::EntityHashMap,
11 prelude::*,
12 storage::SparseSet,
13 system::{ReadOnlySystemParam, SystemParam, SystemParamItem, SystemState, lifetimeless::Read},
14 world::FilteredEntityRef,
15};
16use bevy_render::{
17 prelude::*,
18 primitives::{Aabb, Frustum, Sphere},
19 render_phase::{CachedRenderPipelinePhaseItem, DrawFunctionId, RenderCommand, SortedPhaseItem},
20 render_resource::{CachedRenderPipelineId, RenderPipelineDescriptor, TextureFormat},
21 sync_world::MainEntity,
22 view::{NoCpuCulling, NoFrustumCulling, RenderLayers, VisibilityRange, VisibleEntities, VisibleEntityRanges},
23};
24use bevy_transform::prelude::*;
25use bevy_utils::{Parallel, TypeIdMap};
26use smallvec::SmallVec;
27
28use crate::{
29 attribute::VertexLayout,
30 drawer::{Drawer, HasDrawer},
31};
32
33pub trait Vertex: Send + Sync + VertexLayout {
36 type PipelineParam: SystemParam;
40 type PipelineProp: Send + Sync;
45 type PipelineKey: Send + Sync + Clone + Eq + PartialEq + Hash;
49 const DEPTH_FORMAT: Option<TextureFormat> = Some(TextureFormat::Depth32Float);
53
54 type BatchParam: SystemParam;
56 type BatchProp: Send + Sync;
62
63 type Item: CachedRenderPipelinePhaseItem + SortedPhaseItem;
65 type RenderCommand: RenderCommand<Self::Item, Param: ReadOnlySystemParam> + Send + Sync;
69
70 const SHADER: &'static str;
73
74 #[allow(unused)]
78 fn setup(app: &mut App) {}
79
80 fn init_pipeline(param: SystemParamItem<Self::PipelineParam>) -> Self::PipelineProp;
83
84 fn specialize_pipeline(key: Self::PipelineKey, prop: &Self::PipelineProp, desc: &mut RenderPipelineDescriptor);
87
88 fn create_item(
91 layer: f32,
92 entity: (Entity, MainEntity),
93 pipeline: CachedRenderPipelineId,
94 draw_function: DrawFunctionId,
95 command: usize,
96 ) -> Self::Item;
97
98 fn create_batch(param: &mut SystemParamItem<Self::BatchParam>, key: Self::PipelineKey) -> Self::BatchProp;
100}
101
102#[derive(Resource)]
105pub struct VertexDrawers<T: Vertex>(pub(crate) SparseSet<ComponentId, TypeId>, PhantomData<fn() -> T>);
106impl<T: Vertex> Default for VertexDrawers<T> {
107 #[inline]
108 fn default() -> Self {
109 Self(SparseSet::new(), PhantomData)
110 }
111}
112
113impl<T: Vertex> VertexDrawers<T> {
114 #[inline]
116 pub fn add<D: Drawer<Vertex = T>>(&mut self, world: &mut World) {
117 self.0
118 .insert(world.register_component::<HasDrawer<D>>(), TypeId::of::<With<HasDrawer<D>>>());
119 }
120}
121
122#[derive(Component)]
123pub(crate) struct DrawItems<T: Vertex>(pub Mutex<SmallVec<[(Range<usize>, f32, T::PipelineKey); 8]>>);
124impl<T: Vertex> Default for DrawItems<T> {
125 #[inline]
126 fn default() -> Self {
127 Self(Mutex::new(SmallVec::new()))
128 }
129}
130
131pub fn check_visibilities<T: Vertex>(
138 world: &mut World,
139 visibility: &mut QueryState<FilteredEntityRef>,
140 views: &mut SystemState<(
141 Query<(
142 Entity,
143 Read<Frustum>,
144 Option<Read<RenderLayers>>,
145 Read<Camera>,
146 Has<NoCpuCulling>,
147 )>,
148 Query<(
149 Entity,
150 &InheritedVisibility,
151 &mut ViewVisibility,
152 Option<&RenderLayers>,
153 Option<&Aabb>,
154 &GlobalTransform,
155 Has<NoFrustumCulling>,
156 Has<VisibilityRange>,
157 )>,
158 Option<Res<VisibleEntityRanges>>,
159 )>,
160 visible_entities: &mut SystemState<Query<(Entity, &mut VisibleEntities)>>,
161 mut thread_queues: Local<Parallel<Vec<Entity>>>,
162 mut view_queues: Local<EntityHashMap<Vec<Entity>>>,
163 mut view_maps: Local<EntityHashMap<TypeIdMap<Vec<Entity>>>>,
164) {
165 world.resource_scope(|world, drawers: Mut<VertexDrawers<T>>| {
166 if drawers.is_changed() {
167 let mut builder = QueryBuilder::<FilteredEntityRef>::new(world);
168 builder.or(|query| {
169 for id in drawers.0.indices() {
170 query.with_id(id);
171 }
172 });
173
174 *visibility = builder.build();
175 }
176 });
177
178 let (view_query, mut visible_aabb_query, visible_entity_ranges) = views.get_mut(world);
179 let visible_entity_ranges = visible_entity_ranges.as_deref();
180 for (view, &frustum, maybe_view_mask, camera, no_cpu_culling) in &view_query {
181 if !camera.is_active {
182 continue;
183 }
184
185 let view_mask = maybe_view_mask.unwrap_or_default();
186 visible_aabb_query.par_iter_mut().for_each_init(
187 || thread_queues.borrow_local_mut(),
188 |queue,
189 (
190 entity,
191 inherited_visibility,
192 mut view_visibility,
193 maybe_entity_mask,
194 maybe_model_aabb,
195 transform,
196 no_frustum_culling,
197 has_visibility_range,
198 )| {
199 if !inherited_visibility.get() {
200 return;
201 }
202
203 let entity_mask = maybe_entity_mask.unwrap_or_default();
204 if !view_mask.intersects(entity_mask) {
205 return;
206 }
207
208 if has_visibility_range &&
210 visible_entity_ranges.is_some_and(|visible_entity_ranges| {
211 !visible_entity_ranges.entity_is_in_range_of_view(entity, view)
212 })
213 {
214 return;
215 }
216
217 if !no_frustum_culling && !no_cpu_culling {
219 if let Some(model_aabb) = maybe_model_aabb {
220 let world_from_local = transform.affine();
221 let model_sphere = Sphere {
222 center: world_from_local.transform_point3a(model_aabb.center),
223 radius: transform.radius_vec3a(model_aabb.half_extents),
224 };
225
226 if !frustum.intersects_sphere(&model_sphere, false) {
228 return;
229 }
230
231 if !frustum.intersects_obb(model_aabb, &world_from_local, true, false) {
233 return;
234 }
235 }
236 }
237
238 view_visibility.set();
239 queue.push(entity);
240 },
241 );
242
243 thread_queues.drain_into(view_queues.entry(view).or_default());
244 }
245
246 visibility.update_archetypes(world);
247
248 let drawers = world.resource::<VertexDrawers<T>>();
249 for (&view, queues) in &mut view_queues {
250 let map = view_maps.entry(view).or_default();
251 for e in queues.drain(..) {
252 let Ok(visible) = visibility.get_manual(world, e) else {
253 continue;
254 };
255
256 for (&id, &key) in drawers.0.iter() {
257 if visible.contains_id(id) {
258 map.entry(key).or_default().push(e);
259 }
260 }
261 }
262 }
263
264 let mut visible_entities = visible_entities.get_mut(world);
265 for (view, mut visible_entities) in &mut visible_entities {
266 let Some(map) = view_maps.get_mut(&view) else {
267 continue;
268 };
269 for (&id, entities) in map {
270 let dst = visible_entities.entities.entry(id).or_default();
271 dst.clear();
272 dst.append(entities);
273 }
274 }
275}