Merge branch 'visibility'
This commit is contained in:
commit
3e6b864e3e
|
@ -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,64 +52,112 @@ 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 {
|
{
|
||||||
new_visible_entities.insert(*e);
|
Some((*opacity, coord_entities.clone()))
|
||||||
}
|
|
||||||
if entities.contains(viewer_entity) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
} else {
|
||||||
return *opacity;
|
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 opacity = 0;
|
||||||
let mut entities = HashSet::new();
|
let mut coord_entities = HashSet::new();
|
||||||
rapier_context.intersections_with_shape(
|
rapier_context.intersections_with_shape(
|
||||||
shape_pos,
|
shape_pos,
|
||||||
0.,
|
0.,
|
||||||
&shape,
|
&shape,
|
||||||
QueryFilter::new().predicate(&|v| visible_query.get(v).is_ok()),
|
QueryFilter::new().predicate(&|v| visible_query.get(v).is_ok()),
|
||||||
|entity| {
|
|entity| {
|
||||||
|
// println!("{:?}", entity);
|
||||||
let obstruction = obstructions_query.get(entity).is_ok();
|
let obstruction = obstructions_query.get(entity).is_ok();
|
||||||
if obstruction {
|
if obstruction {
|
||||||
entities.clear();
|
// println!("Obstruction");
|
||||||
|
coord_entities.clear();
|
||||||
}
|
}
|
||||||
entities.insert(entity);
|
coord_entities.insert(entity);
|
||||||
if let Ok(visible) = visible_query.get(entity) {
|
if let Ok((visible, _, _)) = visible_query.get(entity) {
|
||||||
opacity = opacity.max(**visible);
|
opacity = opacity.max(**visible);
|
||||||
}
|
}
|
||||||
!obstruction
|
!obstruction
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
for e in &entities {
|
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);
|
||||||
}
|
}
|
||||||
cache.insert(coord, (opacity, entities.clone()));
|
if coord_entities.contains(viewer_entity) {
|
||||||
if entities.contains(viewer_entity) {
|
// println!("Self hit, 0");
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
|
// println!("{}", opacity);
|
||||||
opacity
|
opacity
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
let mut new_visible = HashSet::new();
|
let mut new_visible = HashSet::new();
|
||||||
|
@ -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,56 +318,16 @@ 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();
|
|
||||||
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() {
|
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(
|
viewshed.update(
|
||||||
&viewer_entity,
|
&viewer_entity,
|
||||||
&mut visible_entities,
|
&mut visible_entities,
|
||||||
viewer_coordinates,
|
viewer_transform,
|
||||||
&*rapier_context,
|
&rapier_context,
|
||||||
map,
|
map,
|
||||||
&visible,
|
&visible,
|
||||||
&obstructions,
|
&obstructions,
|
||||||
|
@ -322,20 +336,25 @@ fn update_viewshed<D: 'static + Clone + Default + Send + Sync>(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 removed in removed.iter() {
|
||||||
for (viewer_entity, mut viewshed, mut visible_entities, start) in viewers.iter_mut() {
|
for (viewer_entity, mut viewshed, mut visible_entities, start) in viewers.iter_mut() {
|
||||||
if !visible_entities.contains(&removed) {
|
if !visible_entities.contains(&removed) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -356,6 +375,7 @@ fn remove_visible<D: 'static + Clone + Default + Send + Sync>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_revealed_tiles<D: 'static + Clone + Default + Send + Sync>(
|
fn update_revealed_tiles<D: 'static + Clone + Default + Send + Sync>(
|
||||||
|
@ -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>)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user