mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Parallelize spatial grid construction.
This solution may or may not actually work well. An incremental solution may be better.
This commit is contained in:
parent
46cd112928
commit
cc6d904c75
@ -71,6 +71,7 @@ petgraph = { version = "0.6", optional = true }
|
|||||||
kiddo = { version = "0.1", optional = true }
|
kiddo = { version = "0.1", optional = true }
|
||||||
|
|
||||||
# Data structures
|
# Data structures
|
||||||
|
dashmap = { version = "5.4.0", features = ["rayon"] }
|
||||||
hashbrown = { version = "0.12", features = ["rayon", "serde", "nightly"] }
|
hashbrown = { version = "0.12", features = ["rayon", "serde", "nightly"] }
|
||||||
slotmap = { version = "1.0", features = ["serde"] }
|
slotmap = { version = "1.0", features = ["serde"] }
|
||||||
indexmap = { version = "1.3.0", features = ["rayon"] }
|
indexmap = { version = "1.3.0", features = ["rayon"] }
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::util::SpatialGrid;
|
use crate::util::{SpatialGrid, SpatialGridRef};
|
||||||
|
|
||||||
/// Cached [`SpatialGrid`] for reuse within different ecs systems during a tick.
|
/// Cached [`SpatialGrid`] for reuse within different ecs systems during a tick.
|
||||||
/// This is used to accelerate queries on entities within a specific area.
|
/// This is used to accelerate queries on entities within a specific area.
|
||||||
@ -6,7 +6,7 @@ use crate::util::SpatialGrid;
|
|||||||
/// positions are calculated for the tick. So any position modifications outside
|
/// 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
|
/// the physics system will not be reflected here until the next tick when the
|
||||||
/// physics system runs.
|
/// physics system runs.
|
||||||
pub struct CachedSpatialGrid(pub SpatialGrid);
|
pub struct CachedSpatialGrid(pub SpatialGridRef);
|
||||||
|
|
||||||
impl Default for CachedSpatialGrid {
|
impl Default for CachedSpatialGrid {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@ -14,7 +14,7 @@ impl Default for CachedSpatialGrid {
|
|||||||
let lg2_large_cell_size = 6; // 64
|
let lg2_large_cell_size = 6; // 64
|
||||||
let radius_cutoff = 8;
|
let radius_cutoff = 8;
|
||||||
|
|
||||||
let spatial_grid = SpatialGrid::new(lg2_cell_size, lg2_large_cell_size, radius_cutoff);
|
let spatial_grid = SpatialGrid::new(lg2_cell_size, lg2_large_cell_size, radius_cutoff).into_read_only();
|
||||||
|
|
||||||
Self(spatial_grid)
|
Self(spatial_grid)
|
||||||
}
|
}
|
||||||
|
@ -38,4 +38,4 @@ pub use dir::*;
|
|||||||
pub use option::either_with;
|
pub use option::either_with;
|
||||||
pub use plane::Plane;
|
pub use plane::Plane;
|
||||||
pub use projection::Projection;
|
pub use projection::Projection;
|
||||||
pub use spatial_grid::SpatialGrid;
|
pub use spatial_grid::{SpatialGrid, SpatialGridRef};
|
||||||
|
@ -1,29 +1,36 @@
|
|||||||
|
use core::sync::atomic::{AtomicU32, Ordering};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
|
pub type MapMut = dashmap::DashMap<Vec2<i32>, Vec<specs::Entity>>;
|
||||||
|
pub type MapRef = dashmap::ReadOnlyView<Vec2<i32>, Vec<specs::Entity>>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SpatialGrid {
|
pub struct SpatialGridInner<Map, Radius> {
|
||||||
// Uses two scales of grids so that we can have a hard limit on how far to search in the
|
/// Uses two scales of grids so that we can have a hard limit on how far to search in the
|
||||||
// smaller grid
|
/// smaller grid
|
||||||
grid: hashbrown::HashMap<Vec2<i32>, Vec<specs::Entity>>,
|
grid: Map,
|
||||||
large_grid: hashbrown::HashMap<Vec2<i32>, Vec<specs::Entity>>,
|
large_grid: Map,
|
||||||
// Log base 2 of the cell size of the spatial grid
|
/// Log base 2 of the cell size of the spatial grid
|
||||||
lg2_cell_size: usize,
|
lg2_cell_size: usize,
|
||||||
// Log base 2 of the cell size of the large spatial grid
|
/// Log base 2 of the cell size of the large spatial grid
|
||||||
lg2_large_cell_size: usize,
|
lg2_large_cell_size: usize,
|
||||||
// Entities with a radius over this value are store in the coarser large_grid
|
/// Entities with a radius over this value are store in the coarser large_grid
|
||||||
// This is the amount of buffer space we need to add when finding the intersections with cells
|
/// This is the amount of buffer space we need to add when finding the intersections with cells
|
||||||
// in the regular grid
|
/// in the regular grid
|
||||||
radius_cutoff: u32,
|
radius_cutoff: u32,
|
||||||
// Stores the largest radius of the entities in the large_grid
|
/// Stores the largest radius of the entities in the large_grid
|
||||||
// This is the amount of buffer space we need to add when finding the intersections with cells
|
/// This is the amount of buffer space we need to add when finding the intersections with cells
|
||||||
// in the larger grid
|
/// in the larger grid
|
||||||
// note: could explore some distance field type thing for querying whether there are large
|
/// note: could explore some distance field type thing for querying whether there are large
|
||||||
// entities nearby that necessitate expanding the cells searched for collision (and querying
|
/// entities nearby that necessitate expanding the cells searched for collision (and querying
|
||||||
// how much it needs to be expanded)
|
/// how much it needs to be expanded)
|
||||||
// TODO: log this to metrics?
|
/// TODO: log this to metrics?
|
||||||
largest_large_radius: u32,
|
largest_large_radius: Radius,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type SpatialGrid = SpatialGridInner<MapMut, AtomicU32>;
|
||||||
|
pub type SpatialGridRef = SpatialGridInner<MapRef, u32>;
|
||||||
|
|
||||||
impl SpatialGrid {
|
impl SpatialGrid {
|
||||||
pub fn new(lg2_cell_size: usize, lg2_large_cell_size: usize, radius_cutoff: u32) -> Self {
|
pub fn new(lg2_cell_size: usize, lg2_large_cell_size: usize, radius_cutoff: u32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -32,28 +39,55 @@ impl SpatialGrid {
|
|||||||
lg2_cell_size,
|
lg2_cell_size,
|
||||||
lg2_large_cell_size,
|
lg2_large_cell_size,
|
||||||
radius_cutoff,
|
radius_cutoff,
|
||||||
largest_large_radius: radius_cutoff,
|
largest_large_radius: radius_cutoff.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add an entity at the provided 2d pos into the spatial grid
|
/// Add an entity at the provided 2d pos into the spatial grid
|
||||||
pub fn insert(&mut self, pos: Vec2<i32>, radius: u32, entity: specs::Entity) {
|
pub fn insert(&self, pos: Vec2<i32>, radius: u32, entity: specs::Entity) {
|
||||||
if radius <= self.radius_cutoff {
|
if radius <= self.radius_cutoff {
|
||||||
let cell = pos.map(|e| e >> self.lg2_cell_size);
|
let cell = pos.map(|e| e >> self.lg2_cell_size);
|
||||||
self.grid.entry(cell).or_default().push(entity);
|
self.grid.entry(cell).or_default().push(entity);
|
||||||
} else {
|
} else {
|
||||||
let cell = pos.map(|e| e >> self.lg2_large_cell_size);
|
let cell = pos.map(|e| e >> self.lg2_large_cell_size);
|
||||||
self.large_grid.entry(cell).or_default().push(entity);
|
self.large_grid.entry(cell).or_default().push(entity);
|
||||||
self.largest_large_radius = self.largest_large_radius.max(radius);
|
// NOTE: Relaxed ordering is sufficient, because max is a monotonic function (for the
|
||||||
|
// duration of shared write access to the map; clear takes &mut so it's okay that it's
|
||||||
|
// not monotonic). We don't need to order this operation with anything else, either,
|
||||||
|
// because all interesting map operations require unique access to Self to perform,
|
||||||
|
// which axiomatically synchronizes with the end of all prior shared borrows.
|
||||||
|
//
|
||||||
|
// TODO: Verify that intrinsics lower intelligently to a priority update on CPUs (since
|
||||||
|
// the intrinsic seems targeted at GPUs).
|
||||||
|
self.largest_large_radius.fetch_max(radius, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_read_only(self) -> SpatialGridRef {
|
||||||
|
SpatialGridInner {
|
||||||
|
grid: self.grid.into_read_only(),
|
||||||
|
large_grid: self.large_grid.into_read_only(),
|
||||||
|
lg2_cell_size: self.lg2_cell_size,
|
||||||
|
lg2_large_cell_size: self.lg2_large_cell_size,
|
||||||
|
radius_cutoff: self.radius_cutoff,
|
||||||
|
largest_large_radius: self.largest_large_radius.into_inner(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.grid.clear();
|
||||||
|
self.large_grid.clear();
|
||||||
|
self.largest_large_radius = self.radius_cutoff.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpatialGridRef {
|
||||||
/// Get an iterator over the entities overlapping the provided axis aligned
|
/// Get an iterator over the entities overlapping the provided axis aligned
|
||||||
/// bounding region.
|
/// bounding region.
|
||||||
/// NOTE: for best optimization of the iterator use
|
/// NOTE: for best optimization of the iterator use
|
||||||
/// `for_each` rather than a for loop.
|
/// `for_each` rather than a for loop.
|
||||||
pub fn in_aabr<'a>(&'a self, aabr: Aabr<i32>) -> impl Iterator<Item = specs::Entity> + 'a {
|
pub fn in_aabr<'a>(&'a self, aabr: Aabr<i32>) -> impl Iterator<Item = specs::Entity> + 'a {
|
||||||
let iter = |max_entity_radius, grid: &'a hashbrown::HashMap<_, _>, lg2_cell_size| {
|
let iter = |max_entity_radius, grid: &'a MapRef, lg2_cell_size| {
|
||||||
// Add buffer for other entity radius
|
// Add buffer for other entity radius
|
||||||
let min = aabr.min - max_entity_radius as i32;
|
let min = aabr.min - max_entity_radius as i32;
|
||||||
let max = aabr.max + max_entity_radius as i32;
|
let max = aabr.max + max_entity_radius as i32;
|
||||||
@ -100,9 +134,14 @@ impl SpatialGrid {
|
|||||||
self.in_aabr(aabr)
|
self.in_aabr(aabr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
pub fn into_inner(self) -> SpatialGrid {
|
||||||
self.grid.clear();
|
SpatialGridInner {
|
||||||
self.large_grid.clear();
|
grid: self.grid.into_inner(),
|
||||||
self.largest_large_radius = self.radius_cutoff;
|
large_grid: self.large_grid.into_inner(),
|
||||||
|
lg2_cell_size: self.lg2_cell_size,
|
||||||
|
lg2_large_cell_size: self.lg2_large_cell_size,
|
||||||
|
radius_cutoff: self.radius_cutoff,
|
||||||
|
largest_large_radius: self.largest_large_radius.into(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ use common::{
|
|||||||
states,
|
states,
|
||||||
terrain::{Block, BlockKind, TerrainGrid},
|
terrain::{Block, BlockKind, TerrainGrid},
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
util::{Projection, SpatialGrid},
|
util::{Projection, SpatialGrid, SpatialGridRef},
|
||||||
vol::{BaseVol, ReadVol},
|
vol::{BaseVol, ReadVol},
|
||||||
};
|
};
|
||||||
use common_base::{prof_span, span};
|
use common_base::{prof_span, span};
|
||||||
@ -146,56 +146,57 @@ impl<'a> PhysicsData<'a> {
|
|||||||
/// Add/reset physics state components
|
/// Add/reset physics state components
|
||||||
fn reset(&mut self) {
|
fn reset(&mut self) {
|
||||||
span!(_guard, "Add/reset physics state components");
|
span!(_guard, "Add/reset physics state components");
|
||||||
for (entity, _, _, _, _) in (
|
(
|
||||||
&self.read.entities,
|
&self.read.entities,
|
||||||
&self.read.colliders,
|
self.read.colliders.mask(),
|
||||||
&self.write.positions,
|
self.write.positions.mask(),
|
||||||
&self.write.velocities,
|
self.write.velocities.mask(),
|
||||||
&self.write.orientations,
|
self.write.orientations.mask(),
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
{
|
.for_each(|(entity, _, _, _, _)| {
|
||||||
let _ = self
|
let _ = self
|
||||||
.write
|
.write
|
||||||
.physics_states
|
.physics_states
|
||||||
.entry(entity)
|
.entry(entity)
|
||||||
.map(|e| e.or_insert_with(Default::default));
|
.map(|e| e.or_insert_with(Default::default));
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maintain_pushback_cache(&mut self) {
|
fn maintain_pushback_cache(&mut self) {
|
||||||
span!(_guard, "Maintain pushback cache");
|
span!(_guard, "Maintain pushback cache");
|
||||||
// Add PreviousPhysCache for all relevant entities
|
// Add PreviousPhysCache for all relevant entities
|
||||||
for entity in (
|
(
|
||||||
&self.read.entities,
|
&self.read.entities,
|
||||||
&self.read.colliders,
|
self.read.colliders.mask(),
|
||||||
&self.write.velocities,
|
self.write.velocities.mask(),
|
||||||
&self.write.positions,
|
self.write.positions.mask(),
|
||||||
!&self.write.previous_phys_cache,
|
!self.write.previous_phys_cache.mask(),
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
.map(|(e, _, _, _, _)| e)
|
.map(|(e, _, _, _, _)| e)
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
{
|
.into_iter()
|
||||||
let _ = self
|
.for_each(|entity| {
|
||||||
.write
|
let _ = self
|
||||||
.previous_phys_cache
|
.write
|
||||||
.insert(entity, PreviousPhysCache {
|
.previous_phys_cache
|
||||||
velocity_dt: Vec3::zero(),
|
.insert(entity, PreviousPhysCache {
|
||||||
center: Vec3::zero(),
|
velocity_dt: Vec3::zero(),
|
||||||
collision_boundary: 0.0,
|
center: Vec3::zero(),
|
||||||
scale: 0.0,
|
collision_boundary: 0.0,
|
||||||
scaled_radius: 0.0,
|
scale: 0.0,
|
||||||
neighborhood_radius: 0.0,
|
scaled_radius: 0.0,
|
||||||
origins: None,
|
neighborhood_radius: 0.0,
|
||||||
pos: None,
|
origins: None,
|
||||||
ori: Quaternion::identity(),
|
pos: None,
|
||||||
});
|
ori: Quaternion::identity(),
|
||||||
}
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Update PreviousPhysCache
|
// Update PreviousPhysCache
|
||||||
for (_, vel, position, ori, mut phys_cache, collider, scale, cs) in (
|
(
|
||||||
&self.read.entities,
|
/* &self.read.entities, */
|
||||||
&self.write.velocities,
|
&self.write.velocities,
|
||||||
&self.write.positions,
|
&self.write.positions,
|
||||||
&self.write.orientations,
|
&self.write.orientations,
|
||||||
@ -204,73 +205,78 @@ impl<'a> PhysicsData<'a> {
|
|||||||
self.read.scales.maybe(),
|
self.read.scales.maybe(),
|
||||||
self.read.char_states.maybe(),
|
self.read.char_states.maybe(),
|
||||||
)
|
)
|
||||||
.join()
|
.par_join()
|
||||||
{
|
.for_each(
|
||||||
let scale = scale.map(|s| s.0).unwrap_or(1.0);
|
|(/* _, */ vel, position, ori, mut phys_cache, collider, scale, cs)| {
|
||||||
let z_limits = calc_z_limit(cs, collider);
|
let scale = scale.map(|s| s.0).unwrap_or(1.0);
|
||||||
let (z_min, z_max) = z_limits;
|
let z_limits = calc_z_limit(cs, collider);
|
||||||
let (z_min, z_max) = (z_min * scale, z_max * scale);
|
let (z_min, z_max) = z_limits;
|
||||||
let half_height = (z_max - z_min) / 2.0;
|
let (z_min, z_max) = (z_min * scale, z_max * scale);
|
||||||
|
let half_height = (z_max - z_min) / 2.0;
|
||||||
|
|
||||||
phys_cache.velocity_dt = vel.0 * self.read.dt.0;
|
phys_cache.velocity_dt = vel.0 * self.read.dt.0;
|
||||||
let entity_center = position.0 + Vec3::new(0.0, 0.0, z_min + half_height);
|
let entity_center = position.0 + Vec3::new(0.0, 0.0, z_min + half_height);
|
||||||
let flat_radius = collider.bounding_radius() * scale;
|
let flat_radius = collider.bounding_radius() * scale;
|
||||||
let radius = (flat_radius.powi(2) + half_height.powi(2)).sqrt();
|
let radius = (flat_radius.powi(2) + half_height.powi(2)).sqrt();
|
||||||
|
|
||||||
// Move center to the middle between OLD and OLD+VEL_DT
|
// Move center to the middle between OLD and OLD+VEL_DT
|
||||||
// so that we can reduce the collision_boundary.
|
// so that we can reduce the collision_boundary.
|
||||||
phys_cache.center = entity_center + phys_cache.velocity_dt / 2.0;
|
phys_cache.center = entity_center + phys_cache.velocity_dt / 2.0;
|
||||||
phys_cache.collision_boundary = radius + (phys_cache.velocity_dt / 2.0).magnitude();
|
phys_cache.collision_boundary =
|
||||||
phys_cache.scale = scale;
|
radius + (phys_cache.velocity_dt / 2.0).magnitude();
|
||||||
phys_cache.scaled_radius = flat_radius;
|
phys_cache.scale = scale;
|
||||||
|
phys_cache.scaled_radius = flat_radius;
|
||||||
|
|
||||||
let neighborhood_radius = match collider {
|
let neighborhood_radius = match collider {
|
||||||
Collider::CapsulePrism { radius, .. } => radius * scale,
|
Collider::CapsulePrism { radius, .. } => radius * scale,
|
||||||
Collider::Voxel { .. } | Collider::Volume(_) | Collider::Point => flat_radius,
|
Collider::Voxel { .. } | Collider::Volume(_) | Collider::Point => {
|
||||||
};
|
flat_radius
|
||||||
phys_cache.neighborhood_radius = neighborhood_radius;
|
},
|
||||||
|
};
|
||||||
|
phys_cache.neighborhood_radius = neighborhood_radius;
|
||||||
|
|
||||||
let ori = ori.to_quat();
|
let ori = ori.to_quat();
|
||||||
let origins = match collider {
|
let origins = match collider {
|
||||||
Collider::CapsulePrism { p0, p1, .. } => {
|
Collider::CapsulePrism { p0, p1, .. } => {
|
||||||
let a = p1 - p0;
|
let a = p1 - p0;
|
||||||
let len = a.magnitude();
|
let len = a.magnitude();
|
||||||
// If origins are close enough, our capsule prism is cylinder
|
// If origins are close enough, our capsule prism is cylinder
|
||||||
// with one origin which we don't even need to rotate.
|
// with one origin which we don't even need to rotate.
|
||||||
//
|
//
|
||||||
// Other advantage of early-return is that we don't
|
// Other advantage of early-return is that we don't
|
||||||
// later divide by zero and return NaN
|
// later divide by zero and return NaN
|
||||||
if len < f32::EPSILON * 10.0 {
|
if len < f32::EPSILON * 10.0 {
|
||||||
Some((*p0, *p0))
|
Some((*p0, *p0))
|
||||||
} else {
|
} else {
|
||||||
// Apply orientation to origins of prism.
|
// Apply orientation to origins of prism.
|
||||||
//
|
//
|
||||||
// We do this by building line between them,
|
// We do this by building line between them,
|
||||||
// rotate it and then split back to origins.
|
// rotate it and then split back to origins.
|
||||||
// (Otherwise we will need to do the same with each
|
// (Otherwise we will need to do the same with each
|
||||||
// origin).
|
// origin).
|
||||||
//
|
//
|
||||||
// Cast it to 3d and then convert it back to 2d
|
// Cast it to 3d and then convert it back to 2d
|
||||||
// to apply quaternion.
|
// to apply quaternion.
|
||||||
let a = a.with_z(0.0);
|
let a = a.with_z(0.0);
|
||||||
let a = ori * a;
|
let a = ori * a;
|
||||||
let a = a.xy();
|
let a = a.xy();
|
||||||
// Previous operation could shrink x and y coordinates
|
// Previous operation could shrink x and y coordinates
|
||||||
// if orientation had Z parameter.
|
// if orientation had Z parameter.
|
||||||
// Make sure we have the same length as before
|
// Make sure we have the same length as before
|
||||||
// (and scale it, while we on it).
|
// (and scale it, while we on it).
|
||||||
let a = a.normalized() * scale * len;
|
let a = a.normalized() * scale * len;
|
||||||
let p0 = -a / 2.0;
|
let p0 = -a / 2.0;
|
||||||
let p1 = a / 2.0;
|
let p1 = a / 2.0;
|
||||||
|
|
||||||
Some((p0, p1))
|
Some((p0, p1))
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
Collider::Voxel { .. } | Collider::Volume(_) | Collider::Point => None,
|
||||||
|
};
|
||||||
|
phys_cache.origins = origins;
|
||||||
|
phys_cache.ori = ori;
|
||||||
},
|
},
|
||||||
Collider::Voxel { .. } | Collider::Volume(_) | Collider::Point => None,
|
);
|
||||||
};
|
|
||||||
phys_cache.origins = origins;
|
|
||||||
phys_cache.ori = ori;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn construct_spatial_grid(&mut self) -> SpatialGrid {
|
fn construct_spatial_grid(&mut self) -> SpatialGrid {
|
||||||
@ -292,27 +298,28 @@ impl<'a> PhysicsData<'a> {
|
|||||||
let lg2_cell_size = 5;
|
let lg2_cell_size = 5;
|
||||||
let lg2_large_cell_size = 6;
|
let lg2_large_cell_size = 6;
|
||||||
let radius_cutoff = 8;
|
let radius_cutoff = 8;
|
||||||
let mut spatial_grid = SpatialGrid::new(lg2_cell_size, lg2_large_cell_size, radius_cutoff);
|
let spatial_grid = SpatialGrid::new(lg2_cell_size, lg2_large_cell_size, radius_cutoff);
|
||||||
for (entity, pos, phys_cache, _, _) in (
|
(
|
||||||
&read.entities,
|
&read.entities,
|
||||||
&write.positions,
|
&write.positions,
|
||||||
&write.previous_phys_cache,
|
&write.previous_phys_cache,
|
||||||
write.velocities.mask(),
|
write.velocities.mask(),
|
||||||
!&read.projectiles, // Not needed because they are skipped in the inner loop below
|
!read.projectiles.mask(), /* Not needed because they are skipped in the inner loop
|
||||||
|
* below */
|
||||||
)
|
)
|
||||||
.join()
|
.par_join()
|
||||||
{
|
.for_each(|(entity, pos, phys_cache, _, _)| {
|
||||||
// Note: to not get too fine grained we use a 2D grid for now
|
// Note: to not get too fine grained we use a 2D grid for now
|
||||||
let radius_2d = phys_cache.scaled_radius.ceil() as u32;
|
let radius_2d = phys_cache.scaled_radius.ceil() as u32;
|
||||||
let pos_2d = pos.0.xy().map(|e| e as i32);
|
let pos_2d = pos.0.xy().map(|e| e as i32);
|
||||||
const POS_TRUNCATION_ERROR: u32 = 1;
|
const POS_TRUNCATION_ERROR: u32 = 1;
|
||||||
spatial_grid.insert(pos_2d, radius_2d + POS_TRUNCATION_ERROR, entity);
|
spatial_grid.insert(pos_2d, radius_2d + POS_TRUNCATION_ERROR, entity);
|
||||||
}
|
});
|
||||||
|
|
||||||
spatial_grid
|
spatial_grid
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_pushback(&mut self, job: &mut Job<Sys>, spatial_grid: &SpatialGrid) {
|
fn apply_pushback(&mut self, job: &mut Job<Sys>, spatial_grid: &SpatialGridRef) {
|
||||||
span!(_guard, "Apply pushback");
|
span!(_guard, "Apply pushback");
|
||||||
job.cpu_stats.measure(ParMode::Rayon);
|
job.cpu_stats.measure(ParMode::Rayon);
|
||||||
let PhysicsData {
|
let PhysicsData {
|
||||||
@ -529,30 +536,30 @@ impl<'a> PhysicsData<'a> {
|
|||||||
let lg2_cell_size = 7; // 128
|
let lg2_cell_size = 7; // 128
|
||||||
let lg2_large_cell_size = 8; // 256
|
let lg2_large_cell_size = 8; // 256
|
||||||
let radius_cutoff = 64;
|
let radius_cutoff = 64;
|
||||||
let mut spatial_grid = SpatialGrid::new(lg2_cell_size, lg2_large_cell_size, radius_cutoff);
|
let spatial_grid = SpatialGrid::new(lg2_cell_size, lg2_large_cell_size, radius_cutoff);
|
||||||
// TODO: give voxel colliders their own component type
|
// TODO: give voxel colliders their own component type
|
||||||
for (entity, pos, collider, ori) in (
|
(
|
||||||
&read.entities,
|
&read.entities,
|
||||||
&write.positions,
|
&write.positions,
|
||||||
&read.colliders,
|
&read.colliders,
|
||||||
&write.orientations,
|
&write.orientations,
|
||||||
)
|
)
|
||||||
.join()
|
.par_join()
|
||||||
{
|
.for_each(|(entity, pos, collider, ori)| {
|
||||||
let vol = match collider {
|
let vol = match collider {
|
||||||
Collider::Voxel { id } => voxel_colliders_manifest.colliders.get(id),
|
Collider::Voxel { id } => voxel_colliders_manifest.colliders.get(id),
|
||||||
Collider::Volume(vol) => Some(&**vol),
|
Collider::Volume(vol) => Some(&**vol),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(vol) = vol {
|
if let Some(vol) = vol {
|
||||||
let sphere = voxel_collider_bounding_sphere(vol, pos, ori);
|
let sphere = voxel_collider_bounding_sphere(vol, pos, ori);
|
||||||
let radius = sphere.radius.ceil() as u32;
|
let radius = sphere.radius.ceil() as u32;
|
||||||
let pos_2d = sphere.center.xy().map(|e| e as i32);
|
let pos_2d = sphere.center.xy().map(|e| e as i32);
|
||||||
const POS_TRUNCATION_ERROR: u32 = 1;
|
const POS_TRUNCATION_ERROR: u32 = 1;
|
||||||
spatial_grid.insert(pos_2d, radius + POS_TRUNCATION_ERROR, entity);
|
spatial_grid.insert(pos_2d, radius + POS_TRUNCATION_ERROR, entity);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
spatial_grid
|
spatial_grid
|
||||||
}
|
}
|
||||||
@ -560,7 +567,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
fn handle_movement_and_terrain(
|
fn handle_movement_and_terrain(
|
||||||
&mut self,
|
&mut self,
|
||||||
job: &mut Job<Sys>,
|
job: &mut Job<Sys>,
|
||||||
voxel_collider_spatial_grid: &SpatialGrid,
|
voxel_collider_spatial_grid: &SpatialGridRef,
|
||||||
) {
|
) {
|
||||||
let PhysicsData {
|
let PhysicsData {
|
||||||
ref read,
|
ref read,
|
||||||
@ -610,7 +617,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
&write.physics_states,
|
&write.physics_states,
|
||||||
&read.masses,
|
&read.masses,
|
||||||
&read.densities,
|
&read.densities,
|
||||||
!&read.is_ridings,
|
!read.is_ridings.mask(),
|
||||||
)
|
)
|
||||||
.par_join()
|
.par_join()
|
||||||
.for_each_init(
|
.for_each_init(
|
||||||
@ -694,19 +701,19 @@ impl<'a> PhysicsData<'a> {
|
|||||||
// Update cached 'old' physics values to the current values ready for the next
|
// Update cached 'old' physics values to the current values ready for the next
|
||||||
// tick
|
// tick
|
||||||
prof_span!(guard, "record ori into phys_cache");
|
prof_span!(guard, "record ori into phys_cache");
|
||||||
for (pos, ori, previous_phys_cache, _) in (
|
(
|
||||||
&write.positions,
|
&write.positions,
|
||||||
&write.orientations,
|
&write.orientations,
|
||||||
&mut write.previous_phys_cache,
|
&mut write.previous_phys_cache,
|
||||||
&read.colliders,
|
read.colliders.mask(),
|
||||||
)
|
)
|
||||||
.join()
|
.par_join()
|
||||||
{
|
.for_each(|(pos, ori, previous_phys_cache, _)| {
|
||||||
// Note: updating ori with the rest of the cache values above was attempted but
|
// Note: updating ori with the rest of the cache values above was attempted but
|
||||||
// it did not work (investigate root cause?)
|
// it did not work (investigate root cause?)
|
||||||
previous_phys_cache.pos = Some(*pos);
|
previous_phys_cache.pos = Some(*pos);
|
||||||
previous_phys_cache.ori = ori.to_quat();
|
previous_phys_cache.ori = ori.to_quat();
|
||||||
}
|
});
|
||||||
drop(guard);
|
drop(guard);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -714,7 +721,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
job: &mut Job<Sys>,
|
job: &mut Job<Sys>,
|
||||||
read: &PhysicsRead,
|
read: &PhysicsRead,
|
||||||
write: &mut PhysicsWrite,
|
write: &mut PhysicsWrite,
|
||||||
voxel_collider_spatial_grid: &SpatialGrid,
|
voxel_collider_spatial_grid: &SpatialGridRef,
|
||||||
terrain_like_entities: bool,
|
terrain_like_entities: bool,
|
||||||
) {
|
) {
|
||||||
let (positions, velocities, previous_phys_cache, orientations) = (
|
let (positions, velocities, previous_phys_cache, orientations) = (
|
||||||
@ -1240,27 +1247,27 @@ impl<'a> PhysicsData<'a> {
|
|||||||
write.outcomes.emitter().emit_many(outcomes);
|
write.outcomes.emitter().emit_many(outcomes);
|
||||||
|
|
||||||
prof_span!(guard, "write deferred pos and vel");
|
prof_span!(guard, "write deferred pos and vel");
|
||||||
for (_, pos, vel, ori, pos_vel_ori_defer, _) in (
|
(
|
||||||
&read.entities,
|
/* &read.entities, */
|
||||||
&mut write.positions,
|
&mut write.positions,
|
||||||
&mut write.velocities,
|
&mut write.velocities,
|
||||||
&mut write.orientations,
|
&mut write.orientations,
|
||||||
&mut write.pos_vel_ori_defers,
|
&mut write.pos_vel_ori_defers,
|
||||||
&read.colliders,
|
&read.colliders,
|
||||||
)
|
)
|
||||||
.join()
|
.par_join()
|
||||||
.filter(|tuple| tuple.5.is_voxel() == terrain_like_entities)
|
.filter(|tuple| tuple./*5*/4.is_voxel() == terrain_like_entities)
|
||||||
{
|
.for_each(|(/* _, */ pos, vel, ori, pos_vel_ori_defer, _)| {
|
||||||
if let Some(new_pos) = pos_vel_ori_defer.pos.take() {
|
if let Some(new_pos) = pos_vel_ori_defer.pos.take() {
|
||||||
*pos = new_pos;
|
*pos = new_pos;
|
||||||
}
|
}
|
||||||
if let Some(new_vel) = pos_vel_ori_defer.vel.take() {
|
if let Some(new_vel) = pos_vel_ori_defer.vel.take() {
|
||||||
*vel = new_vel;
|
*vel = new_vel;
|
||||||
}
|
}
|
||||||
if let Some(new_ori) = pos_vel_ori_defer.ori.take() {
|
if let Some(new_ori) = pos_vel_ori_defer.ori.take() {
|
||||||
*ori = new_ori;
|
*ori = new_ori;
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
drop(guard);
|
drop(guard);
|
||||||
|
|
||||||
let mut event_emitter = read.event_bus.emitter();
|
let mut event_emitter = read.event_bus.emitter();
|
||||||
@ -1276,7 +1283,11 @@ impl<'a> PhysicsData<'a> {
|
|||||||
ref mut write,
|
ref mut write,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let spatial_grid = &mut write.cached_spatial_grid.0;
|
// Borrow checker dance, since transferring away from a read only view requires
|
||||||
|
// &mut self.
|
||||||
|
let mut spatial_grid = core::mem::take(&mut *write.cached_spatial_grid)
|
||||||
|
.0
|
||||||
|
.into_inner();
|
||||||
spatial_grid.clear();
|
spatial_grid.clear();
|
||||||
(
|
(
|
||||||
&read.entities,
|
&read.entities,
|
||||||
@ -1284,7 +1295,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
read.scales.maybe(),
|
read.scales.maybe(),
|
||||||
read.colliders.maybe(),
|
read.colliders.maybe(),
|
||||||
)
|
)
|
||||||
.join()
|
.par_join()
|
||||||
.for_each(|(entity, pos, scale, collider)| {
|
.for_each(|(entity, pos, scale, collider)| {
|
||||||
let scale = scale.map(|s| s.0).unwrap_or(1.0);
|
let scale = scale.map(|s| s.0).unwrap_or(1.0);
|
||||||
let radius_2d =
|
let radius_2d =
|
||||||
@ -1293,6 +1304,8 @@ impl<'a> PhysicsData<'a> {
|
|||||||
const POS_TRUNCATION_ERROR: u32 = 1;
|
const POS_TRUNCATION_ERROR: u32 = 1;
|
||||||
spatial_grid.insert(pos_2d, radius_2d + POS_TRUNCATION_ERROR, entity);
|
spatial_grid.insert(pos_2d, radius_2d + POS_TRUNCATION_ERROR, entity);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
write.cached_spatial_grid.0 = spatial_grid.into_read_only();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1321,10 +1334,12 @@ impl<'a> System<'a> for Sys {
|
|||||||
// entities.
|
// entities.
|
||||||
physics_data.maintain_pushback_cache();
|
physics_data.maintain_pushback_cache();
|
||||||
|
|
||||||
let spatial_grid = physics_data.construct_spatial_grid();
|
let spatial_grid = physics_data.construct_spatial_grid().into_read_only();
|
||||||
physics_data.apply_pushback(job, &spatial_grid);
|
physics_data.apply_pushback(job, &spatial_grid);
|
||||||
|
|
||||||
let voxel_collider_spatial_grid = physics_data.construct_voxel_collider_spatial_grid();
|
let voxel_collider_spatial_grid = physics_data
|
||||||
|
.construct_voxel_collider_spatial_grid()
|
||||||
|
.into_read_only();
|
||||||
physics_data.handle_movement_and_terrain(job, &voxel_collider_spatial_grid);
|
physics_data.handle_movement_and_terrain(job, &voxel_collider_spatial_grid);
|
||||||
|
|
||||||
// Spatial grid used by other systems
|
// Spatial grid used by other systems
|
||||||
|
Loading…
Reference in New Issue
Block a user