Massive visibility cleanup/optimization.

This commit is contained in:
Nolan Darilek 2022-07-20 08:24:38 -05:00
parent bd899b9c8e
commit a0ca6a9f55

View File

@ -4,14 +4,14 @@ use std::{
marker::PhantomData,
};
use bevy::{core::FixedTimestep, prelude::*};
use bevy::prelude::*;
use bevy_rapier2d::na::{Isometry2, Point2, Vector2};
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},
@ -53,64 +53,94 @@ 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!("Pushing {:?}", 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 {
new_visible_entities.insert(*e);
}
if entities.contains(viewer_entity) {
return 0;
} else {
return *opacity;
let point = Point2::new(shape_pos.x, shape_pos.y);
let mut hit = false;
for b in &boxes {
if b.contains_local_point(&point) {
// println!("Hit at {:?}", b);
hit = true;
break;
}
}
if hit {
// println!("Hit test");
let mut opacity = 0;
let mut entities = HashSet::new();
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 {
entities.clear();
// println!("Obstruction");
coord_entities.clear();
}
entities.insert(entity);
if let Ok(visible) = visible_query.get(entity) {
coord_entities.insert(entity);
if let Ok((visible, _, _)) = visible_query.get(entity) {
opacity = opacity.max(**visible);
}
!obstruction
},
);
for e in &entities {
for e in &coord_entities {
new_visible_entities.insert(*e);
}
cache.insert(coord, (opacity, entities.clone()));
if entities.contains(viewer_entity) {
if coord_entities.contains(viewer_entity) {
// println!("Self hit, 0");
0
} else {
// println!("{}", opacity);
opacity
}
} else {
// println!("No hits, 0");
0
}
})),
);
let mut new_visible = HashSet::new();
@ -248,15 +278,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,56 +294,16 @@ 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()
{
viewshed.update(
&viewer_entity,
&mut visible_entities,
viewer_coordinates,
&*rapier_context,
viewer_transform,
&rapier_context,
map,
&visible,
&obstructions,
@ -323,19 +313,24 @@ fn update_viewshed<D: 'static + Clone + Default + Send + Sync>(
}
}
}
}
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 removed in removed.iter() {
for (viewer_entity, mut viewshed, mut visible_entities, start) in viewers.iter_mut() {
if !visible_entities.contains(&removed) {
continue;
@ -357,6 +352,7 @@ fn remove_visible<D: 'static + Clone + Default + Send + Sync>(
}
}
}
}
fn update_revealed_tiles<D: 'static + Clone + Default + Send + Sync>(
mut map: Query<(&Map<D>, &mut RevealedTiles)>,
@ -431,25 +427,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>)