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,
};
use bevy::{core::FixedTimestep, prelude::*};
use bevy::prelude::*;
use bevy_rapier2d::{
na::{Isometry2, Vector2},
parry::bounding_volume::BoundingVolume,
};
use coord_2d::{Coord, Size};
use shadowcast::{vision_distance, Context, InputGrid};
use crate::{
bevy_rapier2d::prelude::*,
commands::RunIfExistsExt,
core::{GlobalTransformExt, Player, PointLike},
log::Log,
map::{Map, MapConfig, MapObstruction},
@ -21,10 +24,6 @@ use crate::{
#[reflect(Component)]
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)]
#[reflect(Component)]
pub struct RevealedTiles(pub Vec<bool>);
@ -53,63 +52,111 @@ impl Viewshed {
&mut self,
viewer_entity: &Entity,
visible_entities: &mut VisibleEntities,
start: &dyn PointLike,
start: &GlobalTransform,
rapier_context: &RapierContext,
map: &Map<D>,
visible_query: &Query<&Visible>,
visible_query: &Query<(&Visible, &Collider, &GlobalTransform)>,
obstructions_query: &Query<&MapObstruction>,
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 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 size = (map.width as u32, map.height as u32);
let visibility_grid = VisibilityGrid(
size,
RefCell::new(Box::new(|coord: Coord| {
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. {
// println!("Out of range");
return u8::MAX;
}
if let Some((opacity, entities)) = cache.get(&coord) {
for e in entities {
let result = if let Some((opacity, coord_entities)) = cache.get(&(coord.x, coord.y))
{
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);
}
if entities.contains(viewer_entity) {
return 0;
if coord_entities.contains(viewer_entity) {
// println!("Self hit, 0");
0
} 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 {
opacity
0
}
})),
);
@ -129,12 +176,19 @@ impl Viewshed {
}
if new_visible_entities != **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 {
viewer: *viewer_entity,
viewed: *lost,
});
}
for gained in new_visible_entities.difference(visible_entities) {
// println!("transition: {:?} gains {:?}", viewer_entity, gained);
events.send(VisibilityChanged::Gained {
viewer: *viewer_entity,
viewed: *gained,
@ -248,15 +302,15 @@ where
}
fn update_viewshed<D: 'static + Clone + Default + Send + Sync>(
mut commands: Commands,
config: Res<RapierConfiguration>,
positions: Query<
(Entity, &Transform, &Collider, Option<&LastCoordinates>),
Or<(Changed<Transform>, Changed<Visible>)>,
>,
visible: Query<&Visible>,
visible: Query<(&Visible, &Collider, &GlobalTransform)>,
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>>,
rapier_context: Res<RapierContext>,
mut changed: EventWriter<VisibilityChanged>,
@ -264,95 +318,61 @@ fn update_viewshed<D: 'static + Clone + Default + Send + Sync>(
if !config.query_pipeline_active {
return;
}
let mut to_update = HashSet::new();
for (entity, transform, collider, last_coordinates) in positions.iter() {
if to_update.contains(&entity) {
continue;
}
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() {
let mut cache = HashMap::new();
for (viewer_entity, mut viewshed, mut visible_entities, viewer_transform) in
viewers.iter_mut()
{
if let Ok(map) = map.get_single() {
viewshed.update(
&viewer_entity,
&mut visible_entities,
viewer_coordinates,
&*rapier_context,
map,
&visible,
&obstructions,
&mut changed,
&mut cache,
);
}
viewshed.update(
&viewer_entity,
&mut visible_entities,
viewer_transform,
&rapier_context,
map,
&visible,
&obstructions,
&mut changed,
&mut cache,
);
}
}
}
fn remove_visible<D: 'static + Clone + Default + Send + Sync>(
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>>,
rapier_context: Res<RapierContext>,
visible: Query<&Visible>,
visible: Query<(&Visible, &Collider, &GlobalTransform)>,
obstructions: Query<&MapObstruction>,
mut changed: EventWriter<VisibilityChanged>,
) {
for removed in removed.iter() {
if removed.iter().len() != 0 {
let mut cache = HashMap::new();
for (viewer_entity, mut viewshed, mut visible_entities, start) in viewers.iter_mut() {
if !visible_entities.contains(&removed) {
continue;
}
visible_entities.remove(&removed);
if let Ok(map) = map.get_single() {
viewshed.update(
&viewer_entity,
&mut visible_entities,
start,
&*rapier_context,
map,
&visible,
&obstructions,
&mut changed,
&mut cache,
);
for removed in removed.iter() {
for (viewer_entity, mut viewshed, mut visible_entities, start) in viewers.iter_mut() {
if !visible_entities.contains(&removed) {
continue;
}
visible_entities.remove(&removed);
if let Ok(map) = map.get_single() {
viewshed.update(
&viewer_entity,
&mut visible_entities,
start,
&*rapier_context,
map,
&visible,
&obstructions,
&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> {
fn build(&self, app: &mut App) {
const UPDATE_VIEWSHED_STAGE: &str = "UPDATE_VIEWSHED_STAGE";
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_set_to_stage(
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::PreUpdate, update_viewshed::<D>)
.add_system_to_stage(CoreStage::PostUpdate, viewshed_removed)
.add_system_to_stage(CoreStage::PostUpdate, remove_visible::<D>)
.add_system_to_stage(CoreStage::PreUpdate, update_revealed_tiles::<D>)