use bevy::{
prelude::*,
render::{mesh::MeshAabb, primitives::Aabb, view::VisibilitySystems},
};
use smallvec::SmallVec;
use std::collections::HashMap;
#[derive(Debug, Default, Clone, Resource)]
pub struct EntityMeshMap {
entities_with_mesh: HashMap<AssetId<Mesh>, SmallVec<[Entity; 1]>>,
mesh_for_entity: HashMap<Entity, AssetId<Mesh>>,
}
impl EntityMeshMap {
fn register(&mut self, entity: Entity, mesh_handle: &Handle<Mesh>) {
self.entities_with_mesh
.entry(mesh_handle.into())
.or_default()
.push(entity);
self.mesh_for_entity.insert(entity, mesh_handle.into());
}
fn deregister(&mut self, entity: Entity) {
let mut inner = || {
let mesh = self.mesh_for_entity.remove(&entity)?;
let entities = self.entities_with_mesh.get_mut(&mesh)?;
for i in (0..entities.len()).rev() {
if entities[i] == entity {
entities.swap_remove(i);
}
}
Some(())
};
inner();
}
}
pub fn register_bounds(
new_aabb: Query<(Entity, &Mesh3d), Added<Aabb>>,
mut entity_mesh_map: ResMut<EntityMeshMap>,
) {
for (e, mesh) in &new_aabb {
entity_mesh_map.register(e, mesh);
}
}
pub fn update_bounds(
mut commands: Commands,
meshes: Res<Assets<Mesh>>,
mut mesh_reassigned: Query<(Entity, &Mesh3d, &mut Aabb), Changed<Mesh3d>>,
mut entity_mesh_map: ResMut<EntityMeshMap>,
mut mesh_events: EventReader<AssetEvent<Mesh>>,
mut entities_lost_mesh: RemovedComponents<Mesh3d>,
) {
for entity in entities_lost_mesh.read() {
entity_mesh_map.deregister(entity);
}
for (entity, mesh_handle, mut aabb) in mesh_reassigned.iter_mut() {
entity_mesh_map.deregister(entity);
if let Some(mesh) = meshes.get(&mesh_handle.0) {
if let Some(new_aabb) = mesh.compute_aabb() {
entity_mesh_map.register(entity, mesh_handle);
*aabb = new_aabb;
}
}
}
let to_update = |event: &AssetEvent<Mesh>| {
let id = match event {
AssetEvent::Modified { id } => id,
_ => return None,
};
let mesh = meshes.get(*id)?;
let entities_with_handle = entity_mesh_map.entities_with_mesh.get(id)?;
let aabb = mesh.compute_aabb()?;
Some((aabb, entities_with_handle))
};
for (aabb, entities_with_handle) in mesh_events.read().filter_map(to_update) {
for entity in entities_with_handle {
commands.entity(*entity).insert(aabb.clone());
}
}
}
pub struct AabbUpdatePlugin;
impl Plugin for AabbUpdatePlugin {
fn build(&self, app: &mut App) {
app.init_resource::<EntityMeshMap>()
.add_systems(
PostUpdate,
register_bounds.after(VisibilitySystems::CalculateBounds),
)
.add_systems(PostUpdate, update_bounds);
}
}