Merge branch 'visibility'

This commit is contained in:
Nolan Darilek 2022-07-20 10:17:45 -05:00
commit 3e6b864e3e

View File

@ -4,14 +4,17 @@ use std::{
marker::PhantomData, marker::PhantomData,
}; };
use bevy::{core::FixedTimestep, prelude::*}; use bevy::prelude::*;
use bevy_rapier2d::{
na::{Isometry2, Vector2},
parry::bounding_volume::BoundingVolume,
};
use coord_2d::{Coord, Size}; use coord_2d::{Coord, Size};
use shadowcast::{vision_distance, Context, InputGrid}; use shadowcast::{vision_distance, Context, InputGrid};
use crate::{ use crate::{
bevy_rapier2d::prelude::*, bevy_rapier2d::prelude::*,
commands::RunIfExistsExt,
core::{GlobalTransformExt, Player, PointLike}, core::{GlobalTransformExt, Player, PointLike},
log::Log, log::Log,
map::{Map, MapConfig, MapObstruction}, map::{Map, MapConfig, MapObstruction},
@ -21,10 +24,6 @@ use crate::{
#[reflect(Component)] #[reflect(Component)]
pub struct DontLogWhenVisible; pub struct DontLogWhenVisible;
#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect)]
#[reflect(Component)]
struct LastCoordinates((i32, i32));
#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect)] #[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct RevealedTiles(pub Vec<bool>); pub struct RevealedTiles(pub Vec<bool>);
@ -53,63 +52,111 @@ impl Viewshed {
&mut self, &mut self,
viewer_entity: &Entity, viewer_entity: &Entity,
visible_entities: &mut VisibleEntities, visible_entities: &mut VisibleEntities,
start: &dyn PointLike, start: &GlobalTransform,
rapier_context: &RapierContext, rapier_context: &RapierContext,
map: &Map<D>, map: &Map<D>,
visible_query: &Query<&Visible>, visible_query: &Query<(&Visible, &Collider, &GlobalTransform)>,
obstructions_query: &Query<&MapObstruction>, obstructions_query: &Query<&MapObstruction>,
events: &mut EventWriter<VisibilityChanged>, events: &mut EventWriter<VisibilityChanged>,
cache: &mut HashMap<Coord, (u8, HashSet<Entity>)>, cache: &mut HashMap<(i32, i32), (u8, HashSet<Entity>)>,
) { ) {
// println!("Start");
let mut boxes = vec![];
let shape = Collider::cuboid(self.range as f32, self.range as f32);
rapier_context.intersections_with_shape(
start.translation.truncate(),
0.,
&shape,
QueryFilter::new().predicate(&|e| visible_query.get(e).is_ok()),
|entity| {
if let Ok((_, collider, transform)) = visible_query.get(entity) {
let position = Isometry2::new(
Vector2::new(transform.translation.x, transform.translation.y),
0.,
);
// println!(
// "Hit {:?}, pushing {:?}",
// entity,
// collider.raw.compute_aabb(&position)
// );
boxes.push(collider.raw.compute_aabb(&position));
}
true
},
);
let mut context: Context<u8> = Context::default(); let mut context: Context<u8> = Context::default();
let vision_distance = vision_distance::Circle::new(self.range); let vision_distance = vision_distance::Circle::new(self.range);
let shape = Collider::cuboid(0.5 - f32::EPSILON, 0.5 - f32::EPSILON); let shape = Collider::cuboid(0.5, 0.5);
let mut new_visible_entities = HashSet::new(); let mut new_visible_entities = HashSet::new();
let size = (map.width as u32, map.height as u32); let size = (map.width as u32, map.height as u32);
let visibility_grid = VisibilityGrid( let visibility_grid = VisibilityGrid(
size, size,
RefCell::new(Box::new(|coord: Coord| { RefCell::new(Box::new(|coord: Coord| {
let shape_pos = Vec2::new(coord.x as f32 + 0.5, coord.y as f32 + 0.5); let shape_pos = Vec2::new(coord.x as f32 + 0.5, coord.y as f32 + 0.5);
// println!("Checking {:?}", shape_pos);
if start.distance(&shape_pos) > self.range as f32 + 1. { if start.distance(&shape_pos) > self.range as f32 + 1. {
// println!("Out of range");
return u8::MAX; return u8::MAX;
} }
if let Some((opacity, entities)) = cache.get(&coord) { let result = if let Some((opacity, coord_entities)) = cache.get(&(coord.x, coord.y))
for e in entities { {
Some((*opacity, coord_entities.clone()))
} else {
let position = Isometry2::new(Vector2::new(shape_pos.x, shape_pos.y), 0.);
let aabb = shape.raw.compute_aabb(&position);
let mut hit = false;
for b in &boxes {
if b.intersects(&aabb) {
// println!("Hit at {:?}", b);
hit = true;
break;
}
}
if hit {
// println!("Hit test");
let mut opacity = 0;
let mut coord_entities = HashSet::new();
rapier_context.intersections_with_shape(
shape_pos,
0.,
&shape,
QueryFilter::new().predicate(&|v| visible_query.get(v).is_ok()),
|entity| {
// println!("{:?}", entity);
let obstruction = obstructions_query.get(entity).is_ok();
if obstruction {
// println!("Obstruction");
coord_entities.clear();
}
coord_entities.insert(entity);
if let Ok((visible, _, _)) = visible_query.get(entity) {
opacity = opacity.max(**visible);
}
!obstruction
},
);
cache.insert((coord.x, coord.y), (opacity, coord_entities.clone()));
Some((opacity, coord_entities))
} else {
// println!("No hits, 0");
let coord_entities = HashSet::new();
cache.insert((coord.x, coord.y), (0, coord_entities.clone()));
Some((0, coord_entities))
}
};
if let Some((opacity, coord_entities)) = result {
for e in &coord_entities {
new_visible_entities.insert(*e); new_visible_entities.insert(*e);
} }
if entities.contains(viewer_entity) { if coord_entities.contains(viewer_entity) {
return 0; // println!("Self hit, 0");
0
} else { } else {
return *opacity; // println!("{}", opacity);
opacity
} }
}
let mut opacity = 0;
let mut entities = HashSet::new();
rapier_context.intersections_with_shape(
shape_pos,
0.,
&shape,
QueryFilter::new().predicate(&|v| visible_query.get(v).is_ok()),
|entity| {
let obstruction = obstructions_query.get(entity).is_ok();
if obstruction {
entities.clear();
}
entities.insert(entity);
if let Ok(visible) = visible_query.get(entity) {
opacity = opacity.max(**visible);
}
!obstruction
},
);
for e in &entities {
new_visible_entities.insert(*e);
}
cache.insert(coord, (opacity, entities.clone()));
if entities.contains(viewer_entity) {
0
} else { } else {
opacity 0
} }
})), })),
); );
@ -129,12 +176,19 @@ impl Viewshed {
} }
if new_visible_entities != **visible_entities { if new_visible_entities != **visible_entities {
for lost in visible_entities.difference(&new_visible_entities) { for lost in visible_entities.difference(&new_visible_entities) {
if let Ok((_, _, transform)) = visible_query.get(*lost) {
// println!(
// "transition: {:?} loses {:?} at {:?}",
// viewer_entity, lost, transform.translation
// );
}
events.send(VisibilityChanged::Lost { events.send(VisibilityChanged::Lost {
viewer: *viewer_entity, viewer: *viewer_entity,
viewed: *lost, viewed: *lost,
}); });
} }
for gained in new_visible_entities.difference(visible_entities) { for gained in new_visible_entities.difference(visible_entities) {
// println!("transition: {:?} gains {:?}", viewer_entity, gained);
events.send(VisibilityChanged::Gained { events.send(VisibilityChanged::Gained {
viewer: *viewer_entity, viewer: *viewer_entity,
viewed: *gained, viewed: *gained,
@ -248,15 +302,15 @@ where
} }
fn update_viewshed<D: 'static + Clone + Default + Send + Sync>( fn update_viewshed<D: 'static + Clone + Default + Send + Sync>(
mut commands: Commands,
config: Res<RapierConfiguration>, config: Res<RapierConfiguration>,
positions: Query< visible: Query<(&Visible, &Collider, &GlobalTransform)>,
(Entity, &Transform, &Collider, Option<&LastCoordinates>),
Or<(Changed<Transform>, Changed<Visible>)>,
>,
visible: Query<&Visible>,
obstructions: Query<&MapObstruction>, obstructions: Query<&MapObstruction>,
mut viewers: Query<(Entity, &mut Viewshed, &mut VisibleEntities, &Transform)>, mut viewers: Query<(
Entity,
&mut Viewshed,
&mut VisibleEntities,
&GlobalTransform,
)>,
map: Query<&Map<D>>, map: Query<&Map<D>>,
rapier_context: Res<RapierContext>, rapier_context: Res<RapierContext>,
mut changed: EventWriter<VisibilityChanged>, mut changed: EventWriter<VisibilityChanged>,
@ -264,95 +318,61 @@ fn update_viewshed<D: 'static + Clone + Default + Send + Sync>(
if !config.query_pipeline_active { if !config.query_pipeline_active {
return; return;
} }
let mut to_update = HashSet::new(); if let Ok(map) = map.get_single() {
for (entity, transform, collider, last_coordinates) in positions.iter() { let mut cache = HashMap::new();
if to_update.contains(&entity) { for (viewer_entity, mut viewshed, mut visible_entities, viewer_transform) in
continue; viewers.iter_mut()
}
let coordinates = (transform.translation.x, transform.translation.y);
let coordinates_i32 = coordinates.i32();
if viewers.get_mut(entity).is_ok() {
commands.run_if_exists(entity, move |mut entity| {
entity.insert(LastCoordinates(coordinates_i32));
});
if let Some(last_coordinates) = last_coordinates {
if **last_coordinates != coordinates.i32() {
to_update.insert(entity);
}
} else {
to_update.insert(entity);
}
}
if visible.get(entity).is_err() {
continue;
}
for (viewer_entity, viewshed, visible_entities, viewer_coordinates) in viewers.iter_mut() {
if viewer_entity != entity {
let forward = transform.local_x();
let rotation = forward.y.atan2(forward.x);
let distance = collider.distance_to_point(
transform.translation.truncate(),
rotation,
viewer_coordinates.translation.truncate(),
true,
);
if distance <= viewshed.range as f32 || visible_entities.contains(&entity) {
to_update.insert(viewer_entity);
to_update.insert(entity);
}
}
}
}
let mut cache = HashMap::new();
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.get_single() { viewshed.update(
viewshed.update( &viewer_entity,
&viewer_entity, &mut visible_entities,
&mut visible_entities, viewer_transform,
viewer_coordinates, &rapier_context,
&*rapier_context, map,
map, &visible,
&visible, &obstructions,
&obstructions, &mut changed,
&mut changed, &mut cache,
&mut cache, );
);
}
} }
} }
} }
fn remove_visible<D: 'static + Clone + Default + Send + Sync>( fn remove_visible<D: 'static + Clone + Default + Send + Sync>(
removed: RemovedComponents<Visible>, removed: RemovedComponents<Visible>,
mut viewers: Query<(Entity, &mut Viewshed, &mut VisibleEntities, &Transform)>, mut viewers: Query<(
Entity,
&mut Viewshed,
&mut VisibleEntities,
&GlobalTransform,
)>,
map: Query<&Map<D>>, map: Query<&Map<D>>,
rapier_context: Res<RapierContext>, rapier_context: Res<RapierContext>,
visible: Query<&Visible>, visible: Query<(&Visible, &Collider, &GlobalTransform)>,
obstructions: Query<&MapObstruction>, obstructions: Query<&MapObstruction>,
mut changed: EventWriter<VisibilityChanged>, mut changed: EventWriter<VisibilityChanged>,
) { ) {
for removed in removed.iter() { if removed.iter().len() != 0 {
let mut cache = HashMap::new(); let mut cache = HashMap::new();
for (viewer_entity, mut viewshed, mut visible_entities, start) in viewers.iter_mut() { for removed in removed.iter() {
if !visible_entities.contains(&removed) { for (viewer_entity, mut viewshed, mut visible_entities, start) in viewers.iter_mut() {
continue; if !visible_entities.contains(&removed) {
} continue;
visible_entities.remove(&removed); }
if let Ok(map) = map.get_single() { visible_entities.remove(&removed);
viewshed.update( if let Ok(map) = map.get_single() {
&viewer_entity, viewshed.update(
&mut visible_entities, &viewer_entity,
start, &mut visible_entities,
&*rapier_context, start,
map, &*rapier_context,
&visible, map,
&obstructions, &visible,
&mut changed, &obstructions,
&mut cache, &mut changed,
); &mut cache,
);
}
} }
} }
} }
@ -431,25 +451,11 @@ impl<D: 'static + Clone + Default + Send + Sync> Default for VisibilityPlugin<D>
} }
} }
pub const UPDATE_VIEWSHED_LABEL: &str = "UPDATE_VIEWSHED";
impl<D: 'static + Clone + Default + Send + Sync> Plugin for VisibilityPlugin<D> { impl<D: 'static + Clone + Default + Send + Sync> Plugin for VisibilityPlugin<D> {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
const UPDATE_VIEWSHED_STAGE: &str = "UPDATE_VIEWSHED_STAGE";
app.add_event::<VisibilityChanged>() app.add_event::<VisibilityChanged>()
.add_stage_after(
CoreStage::PreUpdate,
UPDATE_VIEWSHED_STAGE,
SystemStage::parallel(),
)
.add_system_to_stage(CoreStage::PreUpdate, add_visibility_indices::<D>) .add_system_to_stage(CoreStage::PreUpdate, add_visibility_indices::<D>)
.add_system_set_to_stage( .add_system_to_stage(CoreStage::PreUpdate, update_viewshed::<D>)
UPDATE_VIEWSHED_STAGE,
SystemSet::new()
.with_run_criteria(FixedTimestep::step(0.05))
.with_system(update_viewshed::<D>)
.label(UPDATE_VIEWSHED_LABEL),
)
.add_system_to_stage(CoreStage::PostUpdate, viewshed_removed) .add_system_to_stage(CoreStage::PostUpdate, viewshed_removed)
.add_system_to_stage(CoreStage::PostUpdate, remove_visible::<D>) .add_system_to_stage(CoreStage::PostUpdate, remove_visible::<D>)
.add_system_to_stage(CoreStage::PreUpdate, update_revealed_tiles::<D>) .add_system_to_stage(CoreStage::PreUpdate, update_revealed_tiles::<D>)