11mod render_layers;
22
3+ use smallvec:: SmallVec ;
4+
35pub use render_layers:: * ;
46
57use bevy_app:: { CoreStage , Plugin } ;
6- use bevy_asset:: { Assets , Handle } ;
8+ use bevy_asset:: { AssetEvent , Assets , Handle } ;
79use bevy_ecs:: prelude:: * ;
810use bevy_hierarchy:: { Children , Parent } ;
911use bevy_reflect:: std_traits:: ReflectDefault ;
1012use bevy_reflect:: Reflect ;
1113use bevy_transform:: components:: GlobalTransform ;
1214use bevy_transform:: TransformSystem ;
15+ use bevy_utils:: HashMap ;
1316use std:: cell:: Cell ;
1417use thread_local:: ThreadLocal ;
1518
@@ -106,6 +109,11 @@ pub struct VisibilityBundle {
106109#[ derive( Component ) ]
107110pub struct NoFrustumCulling ;
108111
112+ /// Use this component to opt-out of built-in [`Aabb`] updating for Mesh entities (i.e. to opt out
113+ /// of [`update_bounds`]).
114+ #[ derive( Component ) ]
115+ pub struct NoAabbUpdate ;
116+
109117/// Collection of entities visible from the current view.
110118///
111119/// This component contains all entities which are visible from the currently
@@ -139,9 +147,59 @@ impl VisibleEntities {
139147 }
140148}
141149
150+ /// Tracks which [`Entities`](Entity) have which meshes for entities whose [`Aabb`]s are managed by
151+ /// the [`calculate_bounds`] and [`update_bounds`] systems. This is needed because `update_bounds`
152+ /// recomputes `Aabb`s for entities whose mesh has been mutated. These mutations are visible via
153+ /// [`AssetEvent<Mesh>`](AssetEvent) which tells us which mesh was changed but not which entities
154+ /// have that mesh.
155+ #[ derive( Debug , Default , Clone ) ]
156+ pub struct EntityMeshMap {
157+ entities_with_mesh : HashMap < Handle < Mesh > , SmallVec < [ Entity ; 1 ] > > ,
158+ mesh_for_entity : HashMap < Entity , Handle < Mesh > > ,
159+ }
160+
161+ impl EntityMeshMap {
162+ /// Register the passed `entity` as having the passed `mesh_handle`.
163+ fn register ( & mut self , entity : Entity , mesh_handle : & Handle < Mesh > ) {
164+ // Note that this list can have duplicates if an entity is registered for a mesh multiple
165+ // times. This should be rare and only cause an additional `Aabb.clone()` in
166+ // `update_bounds` so it is preferable to a `HashSet` for now.
167+ self . entities_with_mesh
168+ . entry ( mesh_handle. clone_weak ( ) )
169+ . or_default ( )
170+ . push ( entity) ;
171+ self . mesh_for_entity
172+ . insert ( entity, mesh_handle. clone_weak ( ) ) ;
173+ }
174+
175+ /// Deregisters the mapping between an `Entity` and `Mesh`. Used so [`update_bounds`] can
176+ /// track which mappings are still active so `Aabb`s are updated correctly.
177+ fn deregister ( & mut self , entity : Entity ) {
178+ let mut inner = || {
179+ let mesh = self . mesh_for_entity . remove ( & entity) ?;
180+
181+ // This lookup failing is _probably_ an error.
182+ let entities = self . entities_with_mesh . get_mut ( & mesh) ?;
183+
184+ // There could be duplicate entries in here if an entity was registered with a mesh
185+ // multiple times. It's important to remove all references so that if an entity gets a
186+ // new mesh and its old mesh is mutated, the entity doesn't get its old mesh's new
187+ // `Aabb`. Note that there _should_ only be one entity.
188+ for i in ( 0 ..entities. len ( ) ) . rev ( ) {
189+ if entities[ i] == entity {
190+ entities. swap_remove ( i) ;
191+ }
192+ }
193+ Some ( ( ) )
194+ } ;
195+ inner ( ) ;
196+ }
197+ }
198+
142199#[ derive( Debug , Hash , PartialEq , Eq , Clone , SystemLabel ) ]
143200pub enum VisibilitySystems {
144201 CalculateBounds ,
202+ UpdateBounds ,
145203 UpdateOrthographicFrusta ,
146204 UpdatePerspectiveFrusta ,
147205 UpdateProjectionFrusta ,
@@ -157,60 +215,121 @@ impl Plugin for VisibilityPlugin {
157215 fn build ( & self , app : & mut bevy_app:: App ) {
158216 use VisibilitySystems :: * ;
159217
160- app. add_system_to_stage (
161- CoreStage :: PostUpdate ,
162- calculate_bounds. label ( CalculateBounds ) ,
163- )
164- . add_system_to_stage (
165- CoreStage :: PostUpdate ,
166- update_frusta :: < OrthographicProjection >
167- . label ( UpdateOrthographicFrusta )
168- . after ( TransformSystem :: TransformPropagate ) ,
169- )
170- . add_system_to_stage (
171- CoreStage :: PostUpdate ,
172- update_frusta :: < PerspectiveProjection >
173- . label ( UpdatePerspectiveFrusta )
174- . after ( TransformSystem :: TransformPropagate ) ,
175- )
176- . add_system_to_stage (
177- CoreStage :: PostUpdate ,
178- update_frusta :: < Projection >
179- . label ( UpdateProjectionFrusta )
180- . after ( TransformSystem :: TransformPropagate ) ,
181- )
182- . add_system_to_stage (
183- CoreStage :: PostUpdate ,
184- visibility_propagate_system. label ( VisibilityPropagate ) ,
185- )
186- . add_system_to_stage (
187- CoreStage :: PostUpdate ,
188- check_visibility
189- . label ( CheckVisibility )
190- . after ( CalculateBounds )
191- . after ( UpdateOrthographicFrusta )
192- . after ( UpdatePerspectiveFrusta )
193- . after ( UpdateProjectionFrusta )
194- . after ( VisibilityPropagate )
195- . after ( TransformSystem :: TransformPropagate ) ,
196- ) ;
218+ app. init_resource :: < EntityMeshMap > ( )
219+ . add_system_to_stage (
220+ CoreStage :: PostUpdate ,
221+ calculate_bounds. label ( CalculateBounds ) ,
222+ )
223+ . add_system_to_stage ( CoreStage :: PostUpdate , update_bounds. label ( UpdateBounds ) )
224+ . add_system_to_stage (
225+ CoreStage :: PostUpdate ,
226+ update_frusta :: < OrthographicProjection >
227+ . label ( UpdateOrthographicFrusta )
228+ . after ( TransformSystem :: TransformPropagate ) ,
229+ )
230+ . add_system_to_stage (
231+ CoreStage :: PostUpdate ,
232+ update_frusta :: < PerspectiveProjection >
233+ . label ( UpdatePerspectiveFrusta )
234+ . after ( TransformSystem :: TransformPropagate ) ,
235+ )
236+ . add_system_to_stage (
237+ CoreStage :: PostUpdate ,
238+ update_frusta :: < Projection >
239+ . label ( UpdateProjectionFrusta )
240+ . after ( TransformSystem :: TransformPropagate ) ,
241+ )
242+ . add_system_to_stage (
243+ CoreStage :: PostUpdate ,
244+ visibility_propagate_system. label ( VisibilityPropagate ) ,
245+ )
246+ . add_system_to_stage (
247+ CoreStage :: PostUpdate ,
248+ check_visibility
249+ . label ( CheckVisibility )
250+ . after ( CalculateBounds )
251+ . after ( UpdateBounds )
252+ . after ( UpdateOrthographicFrusta )
253+ . after ( UpdatePerspectiveFrusta )
254+ . after ( UpdateProjectionFrusta )
255+ . after ( VisibilityPropagate )
256+ . after ( TransformSystem :: TransformPropagate ) ,
257+ ) ;
197258 }
198259}
199260
261+ /// Calculates [`Aabb`]s for [`Entities`](Entity) with [`Mesh`]es.
200262pub fn calculate_bounds (
201263 mut commands : Commands ,
202264 meshes : Res < Assets < Mesh > > ,
203265 without_aabb : Query < ( Entity , & Handle < Mesh > ) , ( Without < Aabb > , Without < NoFrustumCulling > ) > ,
266+ mut entity_mesh_map : ResMut < EntityMeshMap > ,
204267) {
205268 for ( entity, mesh_handle) in & without_aabb {
206269 if let Some ( mesh) = meshes. get ( mesh_handle) {
207270 if let Some ( aabb) = mesh. compute_aabb ( ) {
271+ entity_mesh_map. register ( entity, mesh_handle) ;
208272 commands. entity ( entity) . insert ( aabb) ;
209273 }
210274 }
211275 }
212276}
213277
278+ /// Updates [`Aabb`]s for [`Entities`](Entity) with [`Mesh`]es. This includes `Entities` that have
279+ /// been assigned new `Mesh`es as well as `Entities` whose `Mesh` has been directly mutated.
280+ ///
281+ /// To opt out of bound calculation for an `Entity`, give it the [`NoAabbUpdate`] component.
282+ ///
283+ /// NOTE: This system needs to remove entities from their collection in
284+ /// [`EntityMeshMap`] whenever a mesh handle is reassigned or an entity's mesh handle is
285+ /// removed. This may impact performance if meshes with many entities are frequently
286+ /// reassigned/removed.
287+ pub fn update_bounds (
288+ mut commands : Commands ,
289+ meshes : Res < Assets < Mesh > > ,
290+ mut mesh_reassigned : Query <
291+ ( Entity , & Handle < Mesh > , & mut Aabb ) ,
292+ (
293+ Changed < Handle < Mesh > > ,
294+ Without < NoFrustumCulling > ,
295+ Without < NoAabbUpdate > ,
296+ ) ,
297+ > ,
298+ mut entity_mesh_map : ResMut < EntityMeshMap > ,
299+ mut mesh_events : EventReader < AssetEvent < Mesh > > ,
300+ entities_lost_mesh : RemovedComponents < Handle < Mesh > > ,
301+ ) {
302+ for entity in entities_lost_mesh. iter ( ) {
303+ entity_mesh_map. deregister ( entity) ;
304+ }
305+
306+ for ( entity, mesh_handle, mut aabb) in mesh_reassigned. iter_mut ( ) {
307+ entity_mesh_map. deregister ( entity) ;
308+ if let Some ( mesh) = meshes. get ( mesh_handle) {
309+ if let Some ( new_aabb) = mesh. compute_aabb ( ) {
310+ entity_mesh_map. register ( entity, mesh_handle) ;
311+ * aabb = new_aabb;
312+ }
313+ }
314+ }
315+
316+ let to_update = |event : & AssetEvent < Mesh > | {
317+ let handle = match event {
318+ AssetEvent :: Modified { handle } => handle,
319+ _ => return None ,
320+ } ;
321+ let mesh = meshes. get ( handle) ?;
322+ let entities_with_handle = entity_mesh_map. entities_with_mesh . get ( handle) ?;
323+ let aabb = mesh. compute_aabb ( ) ?;
324+ Some ( ( aabb, entities_with_handle) )
325+ } ;
326+ for ( aabb, entities_with_handle) in mesh_events. iter ( ) . filter_map ( to_update) {
327+ for entity in entities_with_handle {
328+ commands. entity ( * entity) . insert ( aabb. clone ( ) ) ;
329+ }
330+ }
331+ }
332+
214333pub fn update_frusta < T : Component + CameraProjection + Send + Sync + ' static > (
215334 mut views : Query < ( & GlobalTransform , & T , & mut Frustum ) > ,
216335) {
0 commit comments