From 3b42af12e1120012bd7d74448235c8920b2f758f Mon Sep 17 00:00:00 2001 From: Nolan Darilek Date: Tue, 12 Oct 2021 16:59:59 -0500 Subject: [PATCH] Route all visibility updates through a single system. --- src/visibility.rs | 279 ++++++++++++++++++++-------------------------- 1 file changed, 119 insertions(+), 160 deletions(-) diff --git a/src/visibility.rs b/src/visibility.rs index 214d984..ced037e 100644 --- a/src/visibility.rs +++ b/src/visibility.rs @@ -44,6 +44,99 @@ impl Viewshed { pub fn is_point_visible(&self, point: &dyn PointLike) -> bool { self.visible_points.contains(&point.into()) } + fn update( + &mut self, + viewer_entity: &Entity, + visible_entities: &mut VisibleEntities, + start: &dyn PointLike, + query_pipeline: &QueryPipeline, + collider_query: &QueryPipelineColliderComponentsQuery, + map: &Map, + visible_query: &Query<&Visible>, + cache: &mut HashMap<(i32, i32), (HashSet, u8)>, + events: &mut EventWriter, + ) { + let mut context: Context = Context::default(); + let vision_distance = vision_distance::Circle::new(self.range); + let shape = ColliderShape::cuboid(0.49, 0.49); + let origin = point!(start.x(), start.y()); + let coord = Coord::new(start.x_i32(), start.y_i32()); + let collider_set = QueryPipelineColliderComponentsSet(&collider_query); + let mut new_visible_entities = HashSet::::new(); + let visibility_grid = VisibilityGrid( + map, + RefCell::new(Box::new(|coord: Coord| { + if let Some((entities, opacity)) = cache.get(&(coord.x, coord.y)) { + for e in entities { + new_visible_entities.insert(*e); + } + return *opacity; + } + let tile = map.at(coord.x as usize, coord.y as usize); + if tile.blocks_visibility() { + return u8::MAX; + } + let mut entities = HashSet::new(); + let shape_pos = (Vec2::new(coord.x as f32 + 0.5, coord.y as f32 + 0.5), 0.); + let dest = point!(coord.x as f32 + 0.5, coord.y as f32 + 0.5); + if na::distance(&origin, &dest) >= self.range as f32 { + return u8::MAX; + } + let mut opacity = 0; + query_pipeline.intersections_with_shape( + &collider_set, + &shape_pos.into(), + &*shape, + InteractionGroups::all(), + Some(&|v| { + v.entity() != *viewer_entity && visible_query.get(v.entity()).is_ok() + }), + |handle| { + let entity = handle.entity(); + entities.insert(entity); + if let Ok(visible) = visible_query.get(entity) { + opacity = **visible; + } + true + }, + ); + for e in &entities { + new_visible_entities.insert(*e); + } + cache.insert((coord.x, coord.y), (entities, opacity)); + opacity + })), + ); + let mut new_visible = HashSet::new(); + context.for_each_visible( + coord, + &visibility_grid, + &visibility_grid, + vision_distance, + 255, + |coord, _directions, _visibility| { + new_visible.insert((coord.x, coord.y)); + }, + ); + if self.visible_points != new_visible { + self.visible_points = new_visible; + } + if new_visible_entities != **visible_entities { + for lost in visible_entities.difference(&new_visible_entities) { + events.send(VisibilityChanged::Lost { + viewer: *viewer_entity, + viewed: *lost, + }); + } + for gained in new_visible_entities.difference(visible_entities) { + events.send(VisibilityChanged::Gained { + viewer: *viewer_entity, + viewed: *gained, + }); + } + **visible_entities = new_visible_entities; + } + } } #[derive(Clone, Copy, Debug, Deref, DerefMut, Reflect)] @@ -65,6 +158,7 @@ impl Visible { Self(u8::MAX) } } + #[derive(Clone, Debug, Default, Deref, DerefMut)] pub struct VisibleEntities(HashSet); @@ -151,179 +245,48 @@ where } fn update_viewshed( - viewer_entity: &Entity, - viewshed: &mut Viewshed, - visible_entities: &mut VisibleEntities, - start: &dyn PointLike, - query_pipeline: &QueryPipeline, - collider_query: &QueryPipelineColliderComponentsQuery, - map: &Map, - visible_query: &Query<&Visible>, - cache: &mut HashMap<(i32, i32), (HashSet, u8)>, - events: &mut EventWriter, -) { - let mut context: Context = Context::default(); - let vision_distance = vision_distance::Circle::new(viewshed.range); - let shape = ColliderShape::cuboid(0.49, 0.49); - let origin = point!(start.x(), start.y()); - let coord = Coord::new(start.x_i32(), start.y_i32()); - let collider_set = QueryPipelineColliderComponentsSet(&collider_query); - let mut new_visible_entities = HashSet::::new(); - let visibility_grid = VisibilityGrid( - map, - RefCell::new(Box::new(|coord: Coord| { - if let Some((entities, opacity)) = cache.get(&(coord.x, coord.y)) { - for e in entities { - new_visible_entities.insert(*e); - } - return *opacity; - } - let tile = map.at(coord.x as usize, coord.y as usize); - if tile.blocks_visibility() { - return u8::MAX; - } - let mut entities = HashSet::new(); - let shape_pos = (Vec2::new(coord.x as f32 + 0.5, coord.y as f32 + 0.5), 0.); - let dest = point!(coord.x as f32 + 0.5, coord.y as f32 + 0.5); - if na::distance(&origin, &dest) >= viewshed.range as f32 { - return u8::MAX; - } - let mut opacity = 0; - query_pipeline.intersections_with_shape( - &collider_set, - &shape_pos.into(), - &*shape, - InteractionGroups::all(), - Some(&|v| v.entity() != *viewer_entity && visible_query.get(v.entity()).is_ok()), - |handle| { - let entity = handle.entity(); - entities.insert(entity); - if let Ok(visible) = visible_query.get(entity) { - opacity = **visible; - } - true - }, - ); - for e in &entities { - new_visible_entities.insert(*e); - } - cache.insert((coord.x, coord.y), (entities, opacity)); - opacity - })), - ); - let mut new_visible = HashSet::new(); - context.for_each_visible( - coord, - &visibility_grid, - &visibility_grid, - vision_distance, - 255, - |coord, _directions, _visibility| { - new_visible.insert((coord.x, coord.y)); - }, - ); - if viewshed.visible_points != new_visible { - viewshed.visible_points = new_visible; - } - if new_visible_entities != **visible_entities { - for lost in visible_entities.difference(&new_visible_entities) { - events.send(VisibilityChanged::Lost { - viewer: *viewer_entity, - viewed: *lost, - }); - } - for gained in new_visible_entities.difference(visible_entities) { - events.send(VisibilityChanged::Gained { - viewer: *viewer_entity, - viewed: *gained, - }); - } - **visible_entities = new_visible_entities; - } -} - -fn update_viewshed_for_coordinates( config: Res, - visible: Query<(Entity, &Coordinates), (Changed, With)>, + coordinates: Query<(Entity, &Coordinates), Changed>, + visible: Query<&Visible>, mut viewers: Query<(Entity, &mut Viewshed, &mut VisibleEntities, &Coordinates)>, map: Query<&Map>, query_pipeline: Res, collider_query: QueryPipelineColliderComponentsQuery, - visible_query: Query<&Visible>, mut changed: EventWriter, ) { if !config.query_pipeline_active { return; } - for (visible_entity, visible_coordinates) in visible.iter() { - for (viewer_entity, mut viewshed, mut visible_entities, start) in viewers.iter_mut() { - if viewer_entity == visible_entity - || start.distance(&visible_coordinates) > viewshed.range as f32 + let mut to_update = HashSet::new(); + for (entity, coordinates) in coordinates.iter() { + if to_update.contains(&entity) { + continue; + } + if viewers.get_mut(entity).is_ok() { + to_update.insert(entity); + } + if visible.get(entity).is_err() { + continue; + } + for (viewer_entity, viewshed, _, viewer_coordinates) in viewers.iter_mut() { + if viewer_entity != entity + && viewer_coordinates.distance(&coordinates) <= viewshed.range as f32 { + to_update.insert(viewer_entity); continue; } - if let Ok(map) = map.single() { - let mut cache = HashMap::new(); - update_viewshed( - &viewer_entity, - &mut viewshed, - &mut visible_entities, - start, - &*query_pipeline, - &collider_query, - map, - &visible_query, - &mut cache, - &mut changed, - ); - } } } -} - -#[derive(Clone, Debug, Deref, DerefMut)] -struct LastStart((i32, i32)); - -fn update_viewshed_for_start( - mut commands: Commands, - config: Res, - mut viewers: Query< - ( - Entity, - &mut Viewshed, - &mut VisibleEntities, - &Coordinates, - Option<&LastStart>, - ), - Changed, - >, - map: Query<&Map>, - query_pipeline: Res, - collider_query: QueryPipelineColliderComponentsQuery, - visible: Query<&Visible>, - mut changed: EventWriter, -) { - if !config.query_pipeline_active { - return; - } - for (viewer_entity, mut viewshed, mut visible_entities, start, last_start) in viewers.iter_mut() - { - let should_update = if let Some(last) = last_start { - start.i32() != **last - } else { - true - }; - commands - .entity(viewer_entity) - .insert(LastStart(start.i32())); - if should_update { + for entity in to_update.iter() { + if let Ok((viewer_entity, mut viewshed, mut visible_entities, viewer_coordinates)) = + viewers.get_mut(*entity) + { if let Ok(map) = map.single() { let mut cache = HashMap::new(); - update_viewshed( + viewshed.update( &viewer_entity, - &mut viewshed, &mut visible_entities, - start, + viewer_coordinates, &*query_pipeline, &collider_query, map, @@ -353,9 +316,8 @@ fn remove_visible( visible_entities.remove(&removed); if let Ok(map) = map.single() { let mut cache = HashMap::new(); - update_viewshed( + viewshed.update( &viewer_entity, - &mut viewshed, &mut visible_entities, start, &*query_pipeline, @@ -447,11 +409,8 @@ impl Plugin for VisibilityPlugin { .add_system_set( SystemSet::new() .with_run_criteria(FixedTimestep::step(0.2)) - .with_system(update_viewshed_for_coordinates.system()) - .with_system(update_viewshed_for_start.system()), + .with_system(update_viewshed.system()), ) - //.add_system(update_viewshed_for_coordinates.system()) - //.add_system(update_viewshed_for_start.system()) .add_system_to_stage(CoreStage::PostUpdate, remove_visible.system()) .add_system_to_stage(CoreStage::PostUpdate, update_revealed_tiles.system()) .add_system_to_stage(CoreStage::PostUpdate, log_visible.system());