Expose CachedSpatialGrid resource that is updated by the physics system, move BuildAreas into its own module, removed unused ExecMode

This commit is contained in:
Imbris 2021-03-28 23:54:49 -04:00
parent 26222a0a2d
commit a76fdbc325
8 changed files with 137 additions and 79 deletions

View File

@ -0,0 +1,21 @@
use crate::util::SpatialGrid;
/// Cached [`SpatialGrid`] for reuse within different ecs systems during a tick.
/// This is used to accelerate queries on entities within a specific area.
/// Updated within the physics system [`crate::sys::phys::Sys`] after new entity
/// positions are calculated for the tick. So any position modifications outside
/// the physics system will not be reflected here until the next tick when the
/// physics system runs.
pub struct CachedSpatialGrid(pub SpatialGrid);
impl Default for CachedSpatialGrid {
fn default() -> Self {
let lg2_cell_size = 5; // 32
let lg2_large_cell_size = 6; // 64
let radius_cutoff = 8;
let spatial_grid = SpatialGrid::new(lg2_cell_size, lg2_large_cell_size, radius_cutoff);
Self(spatial_grid)
}
}

View File

@ -26,6 +26,8 @@ pub use uuid;
pub mod assets;
#[cfg(not(target_arch = "wasm32"))] pub mod astar;
#[cfg(not(target_arch = "wasm32"))]
mod cached_spatial_grid;
#[cfg(not(target_arch = "wasm32"))]
pub mod character;
#[cfg(not(target_arch = "wasm32"))] pub mod clock;
#[cfg(not(target_arch = "wasm32"))] pub mod cmd;
@ -78,6 +80,8 @@ pub mod uid;
#[cfg(not(target_arch = "wasm32"))]
pub mod volumes;
#[cfg(not(target_arch = "wasm32"))]
pub use cached_spatial_grid::CachedSpatialGrid;
pub use combat::DamageSource;
#[cfg(not(target_arch = "wasm32"))]
pub use combat::{Damage, GroupTarget, Knockback, KnockbackDir};

View File

@ -4,6 +4,9 @@ pub mod find_dist;
mod option;
pub mod plane;
pub mod projection;
/// Contains [`SpatialGrid`] which is useful for accelerating queries of nearby
/// entities
mod spatial_grid;
pub const GIT_VERSION: &str = include_str!(concat!(env!("OUT_DIR"), "/githash"));
pub const GIT_TAG: &str = include_str!(concat!(env!("OUT_DIR"), "/gittag"));
@ -28,6 +31,7 @@ lazy_static::lazy_static! {
pub use color::*;
pub use dir::*;
pub use option::*;
pub use plane::*;
pub use projection::*;
pub use option::either_with;
pub use plane::Plane;
pub use projection::Projection;
pub use spatial_grid::SpatialGrid;

View File

@ -1,5 +1,6 @@
use vek::*;
#[derive(Debug)]
pub struct SpatialGrid {
// Uses two scales of grids so that we can have a hard limit on how far to search in the
// smaller grid
@ -74,4 +75,10 @@ impl SpatialGrid {
self.lg2_large_cell_size,
))
}
pub fn clear(&mut self) {
self.grid.clear();
self.large_grid.clear();
self.largest_large_radius = self.radius_cutoff;
}
}

View File

@ -0,0 +1,54 @@
use common::depot::{Depot, Id};
use hashbrown::{hash_map, HashMap};
use vek::*;
/// NOTE: Please don't add `Deserialize` without checking to make sure we
/// can guarantee the invariant that every entry in `area_names` points to a
/// valid id in `areas`.
#[derive(Default)]
pub struct BuildAreas {
areas: Depot<Aabb<i32>>,
area_names: HashMap<String, Id<Aabb<i32>>>,
}
pub enum BuildAreaError {
/// This build area name is reserved by the system.
Reserved,
/// The build area name was not found.
NotFound,
}
/// Build area names that can only be inserted, not removed.
const RESERVED_BUILD_AREA_NAMES: &[&str] = &["world"];
impl BuildAreas {
pub fn areas(&self) -> &Depot<geom::Aabb<i32>> { &self.areas }
pub fn area_names(&self) -> &HashMap<String, Id<Aabb<i32>>> { &self.area_names }
/// If the area_name is already in the map, returns Err(area_name).
pub fn insert(&mut self, area_name: String, area: Aabb<i32>) -> Result<Id<Aabb<i32>>, String> {
let area_name_entry = match self.area_names.entry(area_name) {
hash_map::Entry::Occupied(o) => return Err(o.replace_key()),
hash_map::Entry::Vacant(v) => v,
};
let bb_id = self.areas.insert(area.made_valid());
area_name_entry.insert(bb_id);
Ok(bb_id)
}
pub fn remove(&mut self, area_name: &str) -> Result<Aabb<i32>, BuildAreaError> {
if RESERVED_BUILD_AREA_NAMES.contains(&area_name) {
return Err(BuildAreaError::Reserved);
}
let bb_id = self
.area_names
.remove(area_name)
.ok_or(BuildAreaError::NotFound)?;
let area = self.areas.remove(bb_id).expect(
"Entries in `areas` are added before entries in `area_names` in `insert`, and that is \
the only exposed way to add elements to `area_names`.",
);
Ok(area)
}
}

View File

@ -1,8 +1,9 @@
//! This crate contains the [`State`] and shared between
//! server (`veloren-server`) and the client (`veloren-client`)
mod build_areas;
#[cfg(feature = "plugins")] pub mod plugin;
mod state;
// TODO: breakup state module and remove globs
pub use state::*;
// TODO: breakup state module and remove glob
pub use build_areas::{BuildAreaError, BuildAreas};
pub use state::{BlockChange, State, TerrainChanges};

View File

@ -6,7 +6,6 @@ use crate::plugin::PluginMgr;
use common::uid::UidAllocator;
use common::{
comp,
depot::{Depot, Id},
event::{EventBus, LocalEvent, ServerEvent},
outcome::Outcome,
region::RegionMap,
@ -21,7 +20,7 @@ use common_base::span;
use common_ecs::{PhysicsMetrics, SysMetrics};
use common_net::sync::{interpolation as sync_interp, WorldSyncExt};
use core::{convert::identity, time::Duration};
use hashbrown::{hash_map, HashMap, HashSet};
use hashbrown::{HashMap, HashSet};
use rayon::{ThreadPool, ThreadPoolBuilder};
use specs::{
prelude::Resource,
@ -43,57 +42,6 @@ const DAY_CYCLE_FACTOR: f64 = 24.0 * 2.0;
/// avoid such a situation.
const MAX_DELTA_TIME: f32 = 1.0;
/// NOTE: Please don't add `Deserialize` without checking to make sure we
/// can guarantee the invariant that every entry in `area_names` points to a
/// valid id in `areas`.
#[derive(Default)]
pub struct BuildAreas {
areas: Depot<Aabb<i32>>,
area_names: HashMap<String, Id<Aabb<i32>>>,
}
pub enum BuildAreaError {
/// This build area name is reserved by the system.
Reserved,
/// The build area name was not found.
NotFound,
}
/// Build area names that can only be inserted, not removed.
const RESERVED_BUILD_AREA_NAMES: &[&str] = &["world"];
impl BuildAreas {
pub fn areas(&self) -> &Depot<geom::Aabb<i32>> { &self.areas }
pub fn area_names(&self) -> &HashMap<String, Id<Aabb<i32>>> { &self.area_names }
/// If the area_name is already in the map, returns Err(area_name).
pub fn insert(&mut self, area_name: String, area: Aabb<i32>) -> Result<Id<Aabb<i32>>, String> {
let area_name_entry = match self.area_names.entry(area_name) {
hash_map::Entry::Occupied(o) => return Err(o.replace_key()),
hash_map::Entry::Vacant(v) => v,
};
let bb_id = self.areas.insert(area.made_valid());
area_name_entry.insert(bb_id);
Ok(bb_id)
}
pub fn remove(&mut self, area_name: &str) -> Result<Aabb<i32>, BuildAreaError> {
if RESERVED_BUILD_AREA_NAMES.contains(&area_name) {
return Err(BuildAreaError::Reserved);
}
let bb_id = self
.area_names
.remove(area_name)
.ok_or(BuildAreaError::NotFound)?;
let area = self.areas.remove(bb_id).expect(
"Entries in `areas` are added before entries in `area_names` in `insert`, and that is \
the only exposed way to add elements to `area_names`.",
);
Ok(area)
}
}
#[derive(Default)]
pub struct BlockChange {
blocks: HashMap<Vec3<i32>, Block>,
@ -130,13 +78,6 @@ impl TerrainChanges {
}
}
#[derive(Copy, Clone)]
pub enum ExecMode {
Server,
Client,
Singleplayer,
}
/// A type used to represent game state stored on both the client and the
/// server. This includes things like entity components, terrain data, and
/// global states like weather, time of day, etc.
@ -260,7 +201,7 @@ impl State {
ecs.insert(PlayerEntity(None));
ecs.insert(TerrainGrid::new().unwrap());
ecs.insert(BlockChange::default());
ecs.insert(BuildAreas::default());
ecs.insert(crate::build_areas::BuildAreas::default());
ecs.insert(TerrainChanges::default());
ecs.insert(EventBus::<LocalEvent>::default());
ecs.insert(game_mode);

View File

@ -1,7 +1,3 @@
mod spatial_grid;
use spatial_grid::SpatialGrid;
use common::{
comp::{
body::ship::figuredata::{VoxelCollider, VOXEL_COLLIDER_MANIFEST},
@ -15,7 +11,7 @@ use common::{
resources::DeltaTime,
terrain::{Block, TerrainGrid},
uid::Uid,
util::Projection,
util::{Projection, SpatialGrid},
vol::{BaseVol, ReadVol},
};
use common_base::{prof_span, span};
@ -148,6 +144,7 @@ pub struct PhysicsRead<'a> {
#[derive(SystemData)]
pub struct PhysicsWrite<'a> {
physics_metrics: WriteExpect<'a, PhysicsMetrics>,
cached_spatial_grid: WriteExpect<'a, common::CachedSpatialGrid>,
physics_states: WriteStorage<'a, PhysicsState>,
positions: WriteStorage<'a, Pos>,
velocities: WriteStorage<'a, Vel>,
@ -1119,6 +1116,32 @@ impl<'a> PhysicsData<'a> {
event_emitter.emit(ServerEvent::LandOnGround { entity, vel: vel.0 });
});
}
fn update_cached_spatial_grid(&mut self) {
span!(_guard, "Update cached spatial grid");
let PhysicsData {
ref read,
ref mut write,
} = self;
let spatial_grid = &mut write.cached_spatial_grid.0;
spatial_grid.clear();
(
&read.entities,
&write.positions,
read.scales.maybe(),
read.colliders.maybe(),
)
.join()
.for_each(|(entity, pos, scale, collider)| {
let scale = scale.map(|s| s.0).unwrap_or(1.0);
let radius_2d =
(collider.map(|c| c.get_radius()).unwrap_or(0.5) * scale).ceil() as u32;
let pos_2d = pos.0.xy().map(|e| e as i32);
const POS_TRUNCATION_ERROR: u32 = 1;
spatial_grid.insert(pos_2d, radius_2d + POS_TRUNCATION_ERROR, entity);
});
}
}
impl<'a> System<'a> for Sys {
@ -1128,8 +1151,8 @@ impl<'a> System<'a> for Sys {
const ORIGIN: Origin = Origin::Common;
const PHASE: Phase = Phase::Create;
fn run(job: &mut Job<Self>, mut psd: Self::SystemData) {
psd.reset();
fn run(job: &mut Job<Self>, mut physics_data: Self::SystemData) {
physics_data.reset();
// Apply pushback
//
@ -1144,13 +1167,16 @@ impl<'a> System<'a> for Sys {
// terrain collision code below, although that's not trivial to do since
// it means the step needs to take into account the speeds of both
// entities.
psd.maintain_pushback_cache();
physics_data.maintain_pushback_cache();
let spatial_grid = psd.construct_spatial_grid();
psd.apply_pushback(job, &spatial_grid);
let spatial_grid = physics_data.construct_spatial_grid();
physics_data.apply_pushback(job, &spatial_grid);
let voxel_collider_spatial_grid = psd.construct_voxel_collider_spatial_grid();
psd.handle_movement_and_terrain(job, &voxel_collider_spatial_grid);
let voxel_collider_spatial_grid = physics_data.construct_voxel_collider_spatial_grid();
physics_data.handle_movement_and_terrain(job, &voxel_collider_spatial_grid);
// Spatial grid used by other systems
physics_data.update_cached_spatial_grid();
}
}