mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'imbris/opt-phys-2.0' into 'master'
Make terrain physics faster with internal iteration See merge request veloren/veloren!2915
This commit is contained in:
commit
4b2842da2f
@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Sprite spawn rates
|
||||
- The Interact button can be used on campfires to sit
|
||||
- Made map icons fade out when near the edge of the map display
|
||||
- Roughly doubled the speed of entity vs terrain physics checks
|
||||
|
||||
### Removed
|
||||
|
||||
|
@ -19,6 +19,7 @@ impl LiquidKind {
|
||||
/// If an entity is in multiple overlapping liquid blocks, which one takes
|
||||
/// precedence? (should be a rare edge case, since checkerboard patterns of
|
||||
/// water and lava shouldn't show up in worldgen)
|
||||
#[inline]
|
||||
pub fn merge(self, other: LiquidKind) -> LiquidKind {
|
||||
use LiquidKind::{Lava, Water};
|
||||
match (self, other) {
|
||||
|
@ -57,8 +57,10 @@ impl<V, S: RectVolSize, M: Clone> Chonk<V, S, M> {
|
||||
|
||||
pub fn meta(&self) -> &M { &self.meta }
|
||||
|
||||
#[inline]
|
||||
pub fn get_min_z(&self) -> i32 { self.z_offset }
|
||||
|
||||
#[inline]
|
||||
pub fn get_max_z(&self) -> i32 {
|
||||
self.z_offset + (self.sub_chunks.len() as u32 * SubChunkSize::<S>::SIZE.z) as i32
|
||||
}
|
||||
@ -71,6 +73,7 @@ impl<V, S: RectVolSize, M: Clone> Chonk<V, S, M> {
|
||||
|
||||
// Returns the index (in self.sub_chunks) of the SubChunk that contains
|
||||
// layer z; note that this index changes when more SubChunks are prepended
|
||||
#[inline]
|
||||
fn sub_chunk_idx(&self, z: i32) -> i32 {
|
||||
let diff = z - self.z_offset;
|
||||
diff >> (SubChunkSize::<S>::SIZE.z - 1).count_ones()
|
||||
|
@ -181,6 +181,7 @@ make_case_elim!(
|
||||
);
|
||||
|
||||
impl SpriteKind {
|
||||
#[inline]
|
||||
pub fn solid_height(&self) -> Option<f32> {
|
||||
// Beware: the height *must* be <= `MAX_HEIGHT` or the collision system will not
|
||||
// properly detect it!
|
||||
@ -268,6 +269,7 @@ impl SpriteKind {
|
||||
}
|
||||
|
||||
/// What loot table does collecting this sprite draw from?
|
||||
#[inline]
|
||||
pub fn collectible_id(&self) -> Option<LootSpec<&'static str>> {
|
||||
let item = |id: &'static str| LootSpec::Item(id);
|
||||
let table = |id: &'static str| LootSpec::LootTable(id);
|
||||
@ -336,16 +338,19 @@ impl SpriteKind {
|
||||
}
|
||||
|
||||
/// Can this sprite be picked up to yield an item without a tool?
|
||||
#[inline]
|
||||
pub fn is_collectible(&self) -> bool {
|
||||
self.collectible_id().is_some() && self.mine_tool().is_none()
|
||||
}
|
||||
|
||||
/// Is the sprite a container that will emit a mystery item?
|
||||
#[inline]
|
||||
pub fn is_container(&self) -> bool {
|
||||
matches!(self.collectible_id(), Some(LootSpec::LootTable(_)))
|
||||
}
|
||||
|
||||
/// Which tool (if any) is needed to collect this sprite?
|
||||
#[inline]
|
||||
pub fn mine_tool(&self) -> Option<ToolKind> {
|
||||
match self {
|
||||
SpriteKind::Velorite
|
||||
@ -375,6 +380,7 @@ impl SpriteKind {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_ori(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
@ -446,5 +452,6 @@ lazy_static! {
|
||||
impl<'a> TryFrom<&'a str> for SpriteKind {
|
||||
type Error = ();
|
||||
|
||||
#[inline]
|
||||
fn try_from(s: &'a str) -> Result<Self, Self::Error> { SPRITE_KINDS.get(s).copied().ok_or(()) }
|
||||
}
|
||||
|
@ -111,6 +111,23 @@ pub trait ReadVol: BaseVol {
|
||||
{
|
||||
Ray::new(self, from, to, |_| true)
|
||||
}
|
||||
|
||||
/// Call provided closure with each block in the supplied Aabb
|
||||
/// Portions of the Aabb outside the volume are ignored
|
||||
fn for_each_in(&self, aabb: Aabb<i32>, mut f: impl FnMut(Vec3<i32>, Self::Vox))
|
||||
where
|
||||
Self::Vox: Copy,
|
||||
{
|
||||
for z in aabb.min.z..aabb.max.z + 1 {
|
||||
for y in aabb.min.y..aabb.max.y + 1 {
|
||||
for x in aabb.min.x..aabb.max.x + 1 {
|
||||
if let Ok(block) = self.get(Vec3::new(x, y, z)) {
|
||||
f(Vec3::new(x, y, z), *block);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A volume that provides the ability to sample (i.e., clone a section of) its
|
||||
|
@ -53,6 +53,40 @@ impl<V: RectRasterableVol + ReadVol + Debug> ReadVol for VolGrid2d<V> {
|
||||
chunk.get(co).map_err(VolGrid2dError::ChunkError)
|
||||
})
|
||||
}
|
||||
|
||||
/// Call provided closure with each block in the supplied Aabb
|
||||
/// Areas outside loaded chunks are ignored
|
||||
fn for_each_in(&self, aabb: Aabb<i32>, mut f: impl FnMut(Vec3<i32>, Self::Vox))
|
||||
where
|
||||
Self::Vox: Copy,
|
||||
{
|
||||
let min_chunk_key = self.pos_key(aabb.min);
|
||||
let max_chunk_key = self.pos_key(aabb.max);
|
||||
for key_x in min_chunk_key.x..max_chunk_key.x + 1 {
|
||||
for key_y in min_chunk_key.y..max_chunk_key.y + 1 {
|
||||
let key = Vec2::new(key_x, key_y);
|
||||
let pos = self.key_pos(key);
|
||||
// Calculate intersection of Aabb and this chunk
|
||||
// TODO: should we do this more implicitly as part of the loop
|
||||
// TODO: this probably has to be computed in the chunk.for_each_in() as well
|
||||
// maybe remove here?
|
||||
let intersection = aabb.intersection(Aabb {
|
||||
min: pos.with_z(i32::MIN),
|
||||
// -1 here since the Aabb is inclusive and chunk_offs below will wrap it if
|
||||
// it's outside the range of the chunk
|
||||
max: (pos + Self::chunk_size().map(|e| e as i32) - 1).with_z(i32::MAX),
|
||||
});
|
||||
// Map intersection into chunk coordinates
|
||||
let intersection = Aabb {
|
||||
min: Self::chunk_offs(intersection.min),
|
||||
max: Self::chunk_offs(intersection.max),
|
||||
};
|
||||
if let Some(chonk) = self.get_key(key) {
|
||||
chonk.for_each_in(intersection, |pos_offset, block| f(pos_offset + pos, block));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This actually breaks the API: samples are supposed to have an offset of
|
||||
@ -117,12 +151,14 @@ impl<V: RectRasterableVol> VolGrid2d<V> {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn chunk_size() -> Vec2<u32> { V::RECT_SIZE }
|
||||
|
||||
pub fn insert(&mut self, key: Vec2<i32>, chunk: Arc<V>) -> Option<Arc<V>> {
|
||||
self.chunks.insert(key, chunk)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_key(&self, key: Vec2<i32>) -> Option<&V> {
|
||||
self.chunks.get(&key).map(|arc_chunk| arc_chunk.as_ref())
|
||||
}
|
||||
@ -135,8 +171,10 @@ impl<V: RectRasterableVol> VolGrid2d<V> {
|
||||
|
||||
pub fn remove(&mut self, key: Vec2<i32>) -> Option<Arc<V>> { self.chunks.remove(&key) }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn key_pos(&self, key: Vec2<i32>) -> Vec2<i32> { key * V::RECT_SIZE.map(|e| e as i32) }
|
||||
|
||||
#[inline(always)]
|
||||
pub fn pos_key(&self, pos: Vec3<i32>) -> Vec2<i32> { Self::chunk_key(pos) }
|
||||
|
||||
pub fn iter(&self) -> ChunkIter<V> {
|
||||
|
@ -1342,73 +1342,54 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_sign_loss
|
||||
)]
|
||||
//prof_span!("box_voxel_collision");
|
||||
|
||||
// Function for iterating over the blocks the player at a specific position
|
||||
// collides with
|
||||
fn collision_iter<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
pos: Vec3<f32>,
|
||||
terrain: &'a T,
|
||||
hit: &'a impl Fn(&Block) -> bool,
|
||||
height: &'a impl Fn(&Block) -> f32,
|
||||
near_iter: impl Iterator<Item = (i32, i32, i32)> + 'a,
|
||||
radius: f32,
|
||||
z_range: Range<f32>,
|
||||
) -> impl Iterator<Item = Aabb<f32>> + 'a {
|
||||
near_iter.filter_map(move |(i, j, k)| {
|
||||
let block_pos = pos.map(|e| e.floor() as i32) + Vec3::new(i, j, k);
|
||||
// Convience function to compute the player aabb
|
||||
fn player_aabb(pos: Vec3<f32>, radius: f32, z_range: Range<f32>) -> Aabb<f32> {
|
||||
Aabb {
|
||||
min: pos + Vec3::new(-radius, -radius, z_range.start),
|
||||
max: pos + Vec3::new(radius, radius, z_range.end),
|
||||
}
|
||||
}
|
||||
|
||||
// `near_iter` could be a few blocks too large due to being integer
|
||||
// aligned and rounding up, so skip points outside of the tighter
|
||||
// bounds before looking them up in the terrain
|
||||
// (which incurs a hashmap cost for volgrids)
|
||||
let player_aabb = Aabb {
|
||||
min: pos + Vec3::new(-radius, -radius, z_range.start),
|
||||
max: pos + Vec3::new(radius, radius, z_range.end),
|
||||
};
|
||||
let block_approx = Aabb {
|
||||
min: block_pos.as_(),
|
||||
max: block_pos.as_() + Vec3::new(1.0, 1.0, Block::MAX_HEIGHT),
|
||||
};
|
||||
if !player_aabb.collides_with_aabb(block_approx) {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(block) = terrain.get(block_pos).ok().copied().filter(hit) {
|
||||
let block_aabb = Aabb {
|
||||
min: block_pos.map(|e| e as f32),
|
||||
max: block_pos.map(|e| e as f32) + Vec3::new(1.0, 1.0, height(&block)),
|
||||
};
|
||||
|
||||
if player_aabb.collides_with_aabb(block_aabb) {
|
||||
return Some(block_aabb);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
})
|
||||
// Convience function to translate the near_aabb into the world space
|
||||
fn move_aabb(aabb: Aabb<i32>, pos: Vec3<f32>) -> Aabb<i32> {
|
||||
Aabb {
|
||||
min: aabb.min + pos.map(|e| e.floor() as i32),
|
||||
max: aabb.max + pos.map(|e| e.floor() as i32),
|
||||
}
|
||||
}
|
||||
|
||||
// Function for determining whether the player at a specific position collides
|
||||
// with blocks with the given criteria
|
||||
fn collision_with<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
fn collision_with<T: BaseVol<Vox = Block> + ReadVol>(
|
||||
pos: Vec3<f32>,
|
||||
terrain: &'a T,
|
||||
terrain: &T,
|
||||
hit: impl Fn(&Block) -> bool,
|
||||
near_iter: impl Iterator<Item = (i32, i32, i32)> + 'a,
|
||||
near_aabb: Aabb<i32>,
|
||||
radius: f32,
|
||||
z_range: Range<f32>,
|
||||
) -> bool {
|
||||
collision_iter(
|
||||
pos,
|
||||
terrain,
|
||||
&|block| block.is_solid() && hit(block),
|
||||
&Block::solid_height,
|
||||
near_iter,
|
||||
radius,
|
||||
z_range,
|
||||
)
|
||||
.next()
|
||||
.is_some()
|
||||
let player_aabb = player_aabb(pos, radius, z_range);
|
||||
|
||||
// Calculate the world space near aabb
|
||||
let near_aabb = move_aabb(near_aabb, pos);
|
||||
|
||||
let mut collision = false;
|
||||
// TODO: could short-circuit here
|
||||
terrain.for_each_in(near_aabb, |block_pos, block| {
|
||||
if block.is_solid() && hit(&block) {
|
||||
let block_aabb = Aabb {
|
||||
min: block_pos.map(|e| e as f32),
|
||||
max: block_pos.map(|e| e as f32) + Vec3::new(1.0, 1.0, block.solid_height()),
|
||||
};
|
||||
if player_aabb.collides_with_aabb(block_aabb) {
|
||||
collision = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
collision
|
||||
}
|
||||
|
||||
// Should be easy to just make clippy happy if we want?
|
||||
@ -1420,26 +1401,23 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
// Probe distances
|
||||
let hdist = radius.ceil() as i32;
|
||||
|
||||
// Neighbouring blocks iterator
|
||||
let near_iter = (-hdist..=hdist)
|
||||
.flat_map(move |i| {
|
||||
(-hdist..=hdist).map(move |j| {
|
||||
let max_block_height = Block::MAX_HEIGHT.ceil() as i32;
|
||||
let box_floor = z_min.floor() as i32;
|
||||
let floor = 1 - max_block_height + box_floor;
|
||||
let ceil = z_max.ceil() as i32;
|
||||
|
||||
(floor..=ceil).map(move |k| (i, j, k))
|
||||
})
|
||||
})
|
||||
.flatten();
|
||||
// Neighbouring blocks Aabb
|
||||
let near_aabb = Aabb {
|
||||
min: Vec3::new(
|
||||
-hdist,
|
||||
-hdist,
|
||||
1 - Block::MAX_HEIGHT.ceil() as i32 + z_min.floor() as i32,
|
||||
),
|
||||
max: Vec3::new(hdist, hdist, z_max.ceil() as i32),
|
||||
};
|
||||
|
||||
let z_range = z_min..z_max;
|
||||
|
||||
// Setup values for the loop below
|
||||
physics_state.on_ground = None;
|
||||
physics_state.on_ceiling = false;
|
||||
|
||||
let mut on_ground = None;
|
||||
let mut on_ground = None::<Block>;
|
||||
let mut on_ceiling = false;
|
||||
// Don't loop infinitely here
|
||||
let mut attempts = 0;
|
||||
@ -1452,54 +1430,60 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
.clamped(1, MAX_INCREMENTS);
|
||||
let old_pos = pos.0;
|
||||
for _ in 0..increments {
|
||||
//prof_span!("increment");
|
||||
const MAX_ATTEMPTS: usize = 16;
|
||||
pos.0 += pos_delta / increments as f32;
|
||||
|
||||
let try_colliding_block = |pos: &Pos| {
|
||||
//prof_span!("most colliding check");
|
||||
// Calculate the player's AABB
|
||||
let player_aabb = Aabb {
|
||||
min: pos.0 + Vec3::new(-radius, -radius, z_min),
|
||||
max: pos.0 + Vec3::new(radius, radius, z_max),
|
||||
};
|
||||
let player_aabb = player_aabb(pos.0, radius, z_range.clone());
|
||||
|
||||
// Determine the block that we are colliding with most
|
||||
// (based on minimum collision axis)
|
||||
// (if we are colliding with one)
|
||||
//
|
||||
// 1) Calculate the block's positions in world space
|
||||
// 2) Make sure the block is actually solid
|
||||
// 3) Calculate block AABB
|
||||
// 4) Find the maximum of the minimum collision axes
|
||||
// (this bit is weird, trust me that it works)
|
||||
near_iter
|
||||
.clone()
|
||||
.map(|(i, j, k)| pos.0.map(|e| e.floor() as i32) + Vec3::new(i, j, k))
|
||||
.filter_map(|block_pos| {
|
||||
terrain
|
||||
.get(block_pos)
|
||||
.ok()
|
||||
.filter(|block| block.is_solid())
|
||||
.map(|block| (block_pos, block))
|
||||
})
|
||||
.map(|(block_pos, block)| {
|
||||
(
|
||||
block_pos,
|
||||
Aabb {
|
||||
min: block_pos.map(|e| e as f32),
|
||||
max: block_pos.map(|e| e as f32)
|
||||
+ Vec3::new(1.0, 1.0, block.solid_height()),
|
||||
},
|
||||
block,
|
||||
)
|
||||
})
|
||||
.filter(|(_, block_aabb, _)| block_aabb.collides_with_aabb(player_aabb))
|
||||
.min_by_key(|(_, block_aabb, _)| {
|
||||
ordered_float::OrderedFloat(
|
||||
(block_aabb.center() - player_aabb.center() - Vec3::unit_z() * 0.5)
|
||||
.map(f32::abs)
|
||||
.sum(),
|
||||
)
|
||||
})
|
||||
let mut most_colliding = None;
|
||||
// Calculate the world space near aabb
|
||||
let near_aabb = move_aabb(near_aabb, pos.0);
|
||||
let player_overlap = |block_aabb: Aabb<f32>| {
|
||||
ordered_float::OrderedFloat(
|
||||
(block_aabb.center() - player_aabb.center() - Vec3::unit_z() * 0.5)
|
||||
.map(f32::abs)
|
||||
.sum(),
|
||||
)
|
||||
};
|
||||
|
||||
terrain.for_each_in(near_aabb, |block_pos, block| {
|
||||
// Make sure the block is actually solid
|
||||
if block.is_solid() {
|
||||
// Calculate block AABB
|
||||
let block_aabb = Aabb {
|
||||
min: block_pos.map(|e| e as f32),
|
||||
max: block_pos.map(|e| e as f32)
|
||||
+ Vec3::new(1.0, 1.0, block.solid_height()),
|
||||
};
|
||||
|
||||
// Determine whether the block's AABB collides with the player's AABB
|
||||
if block_aabb.collides_with_aabb(player_aabb) {
|
||||
most_colliding = match most_colliding {
|
||||
// Select the minimum of the value from `player_overlap`
|
||||
other @ Some((_, other_block_aabb, _))
|
||||
if {
|
||||
// TODO: comment below is outdated (as of ~1 year ago)
|
||||
// Find the maximum of the minimum collision axes (this bit
|
||||
// is weird, trust me that it works)
|
||||
player_overlap(block_aabb) >= player_overlap(other_block_aabb)
|
||||
} =>
|
||||
{
|
||||
other
|
||||
},
|
||||
_ => Some((block_pos, block_aabb, block)),
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
most_colliding
|
||||
};
|
||||
|
||||
// While the player is colliding with the terrain...
|
||||
@ -1508,10 +1492,7 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
.flatten()
|
||||
{
|
||||
// Calculate the player's AABB
|
||||
let player_aabb = Aabb {
|
||||
min: pos.0 + Vec3::new(-radius, -radius, z_min),
|
||||
max: pos.0 + Vec3::new(radius, radius, z_max),
|
||||
};
|
||||
let player_aabb = player_aabb(pos.0, radius, z_range.clone());
|
||||
|
||||
// Find the intrusion vector of the collision
|
||||
let dir = player_aabb.collision_vector_with_aabb(block_aabb);
|
||||
@ -1531,7 +1512,7 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
// ground
|
||||
/* if resolve_dir.z > 0.0 && vel.0.z <= 0.0 { */
|
||||
if resolve_dir.z > 0.0 {
|
||||
on_ground = Some(*block);
|
||||
on_ground = Some(block);
|
||||
|
||||
if !was_on_ground {
|
||||
land_on_ground(entity, *vel);
|
||||
@ -1544,33 +1525,33 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
// with a wall
|
||||
//
|
||||
// If we're being pushed out horizontally...
|
||||
let pushed_horizontaly = resolve_dir.z == 0.0;
|
||||
if resolve_dir.z == 0.0
|
||||
// ...and the vertical resolution direction is sufficiently great...
|
||||
let vertical_resolution = dir.z < -0.1;
|
||||
&& dir.z < -0.1
|
||||
// ...and the space above is free...
|
||||
let space_above_is_free = !collision_with(
|
||||
Vec3::new(pos.0.x, pos.0.y, (pos.0.z + 0.1).ceil()),
|
||||
&terrain,
|
||||
always_hits,
|
||||
near_iter.clone(),
|
||||
radius,
|
||||
z_range.clone(),
|
||||
);
|
||||
&& {
|
||||
//prof_span!("space above free");
|
||||
!collision_with(
|
||||
Vec3::new(pos.0.x, pos.0.y, (pos.0.z + 0.1).ceil()),
|
||||
&terrain,
|
||||
always_hits,
|
||||
near_aabb,
|
||||
radius,
|
||||
z_range.clone(),
|
||||
)
|
||||
}
|
||||
// ...and there is a collision with a block beneath our current hitbox...
|
||||
let block_beneath_collides = collision_with(
|
||||
pos.0 + resolve_dir - Vec3::unit_z() * 1.25,
|
||||
&terrain,
|
||||
always_hits,
|
||||
near_iter.clone(),
|
||||
radius,
|
||||
z_range.clone(),
|
||||
);
|
||||
|
||||
if pushed_horizontaly
|
||||
&& vertical_resolution
|
||||
&& space_above_is_free
|
||||
&& block_beneath_collides
|
||||
{
|
||||
&& {
|
||||
//prof_span!("collision beneath");
|
||||
collision_with(
|
||||
pos.0 + resolve_dir - Vec3::unit_z() * 1.25,
|
||||
&terrain,
|
||||
always_hits,
|
||||
near_aabb,
|
||||
radius,
|
||||
z_range.clone(),
|
||||
)
|
||||
} {
|
||||
// ...block-hop!
|
||||
pos.0.z = pos.0.z.max(block_aabb.max.z);
|
||||
vel.0.z = vel.0.z.max(0.0);
|
||||
@ -1579,7 +1560,7 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
if (vel.0 * resolve_dir).xy().magnitude_squared() < 1.0_f32.powi(2) {
|
||||
pos.0 -= resolve_dir.normalized() * 0.05;
|
||||
}
|
||||
on_ground = Some(*block);
|
||||
on_ground = Some(block);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1606,6 +1587,7 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
}
|
||||
}
|
||||
|
||||
// Report on_ceiling state
|
||||
if on_ceiling {
|
||||
physics_state.on_ceiling = true;
|
||||
}
|
||||
@ -1613,17 +1595,18 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
if on_ground.is_some() {
|
||||
physics_state.on_ground = on_ground;
|
||||
// If the space below us is free, then "snap" to the ground
|
||||
} else if collision_with(
|
||||
pos.0 - Vec3::unit_z() * 1.1,
|
||||
&terrain,
|
||||
always_hits,
|
||||
near_iter.clone(),
|
||||
radius,
|
||||
z_range.clone(),
|
||||
) && vel.0.z <= 0.0
|
||||
&& was_on_ground
|
||||
&& block_snap
|
||||
{
|
||||
} else if vel.0.z <= 0.0 && was_on_ground && block_snap && {
|
||||
//prof_span!("snap check");
|
||||
collision_with(
|
||||
pos.0 - Vec3::unit_z() * 1.1,
|
||||
&terrain,
|
||||
always_hits,
|
||||
near_aabb,
|
||||
radius,
|
||||
z_range.clone(),
|
||||
)
|
||||
} {
|
||||
//prof_span!("snap!!");
|
||||
let snap_height = terrain
|
||||
.get(Vec3::new(pos.0.x, pos.0.y, pos.0.z - 0.1).map(|e| e.floor() as i32))
|
||||
.ok()
|
||||
@ -1637,11 +1620,10 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
.copied();
|
||||
}
|
||||
|
||||
let player_aabb = Aabb {
|
||||
min: pos.0 + Vec3::new(-radius, -radius, z_range.start),
|
||||
max: pos.0 + Vec3::new(radius, radius, z_range.end),
|
||||
};
|
||||
let player_voxel_pos = pos.0.map(|e| e.floor() as i32);
|
||||
// Find liquid immersion and wall collision all in one round of iteration
|
||||
let player_aabb = player_aabb(pos.0, radius, z_range.clone());
|
||||
// Calculate the world space near_aabb
|
||||
let near_aabb = move_aabb(near_aabb, pos.0);
|
||||
|
||||
let dirs = [
|
||||
Vec3::unit_x(),
|
||||
@ -1649,6 +1631,8 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
-Vec3::unit_x(),
|
||||
-Vec3::unit_y(),
|
||||
];
|
||||
|
||||
// Compute a list of aabbs to check for collision with nearby walls
|
||||
let player_wall_aabbs = dirs.map(|dir| {
|
||||
let pos = pos.0 + dir * 0.01;
|
||||
Aabb {
|
||||
@ -1657,45 +1641,46 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||
}
|
||||
});
|
||||
|
||||
// Find liquid immersion and wall collision all in one round of iteration
|
||||
let mut liquid = None::<(LiquidKind, f32)>;
|
||||
let mut wall_dir_collisions = [false; 4];
|
||||
near_iter.for_each(|(i, j, k)| {
|
||||
let block_pos = player_voxel_pos + Vec3::new(i, j, k);
|
||||
|
||||
if let Some(block) = terrain.get(block_pos).ok().copied() {
|
||||
// Check for liquid blocks
|
||||
if let Some(block_liquid) = block.liquid_kind() {
|
||||
let liquid_aabb = Aabb {
|
||||
min: block_pos.map(|e| e as f32),
|
||||
// The liquid part of a liquid block always extends 1 block high.
|
||||
max: block_pos.map(|e| e as f32) + Vec3::one(),
|
||||
//prof_span!(guard, "liquid/walls");
|
||||
terrain.for_each_in(near_aabb, |block_pos, block| {
|
||||
// Check for liquid blocks
|
||||
if let Some(block_liquid) = block.liquid_kind() {
|
||||
let liquid_aabb = Aabb {
|
||||
min: block_pos.map(|e| e as f32),
|
||||
// The liquid part of a liquid block always extends 1 block high.
|
||||
max: block_pos.map(|e| e as f32) + Vec3::one(),
|
||||
};
|
||||
if player_aabb.collides_with_aabb(liquid_aabb) {
|
||||
liquid = match liquid {
|
||||
Some((kind, max_liquid_z)) => Some((
|
||||
// TODO: merging of liquid kinds and max_liquid_z are done
|
||||
// independently which allows mix and
|
||||
// matching them
|
||||
kind.merge(block_liquid),
|
||||
max_liquid_z.max(liquid_aabb.max.z),
|
||||
)),
|
||||
None => Some((block_liquid, liquid_aabb.max.z)),
|
||||
};
|
||||
if player_aabb.collides_with_aabb(liquid_aabb) {
|
||||
liquid = match liquid {
|
||||
Some((kind, max_liquid_z)) => Some((
|
||||
kind.merge(block_liquid),
|
||||
max_liquid_z.max(liquid_aabb.max.z),
|
||||
)),
|
||||
None => Some((block_liquid, liquid_aabb.max.z)),
|
||||
};
|
||||
}
|
||||
}
|
||||
// Check for walls
|
||||
if block.is_solid() {
|
||||
let block_aabb = Aabb {
|
||||
min: block_pos.map(|e| e as f32),
|
||||
max: block_pos.map(|e| e as f32) + Vec3::new(1.0, 1.0, block.solid_height()),
|
||||
};
|
||||
}
|
||||
|
||||
for dir in 0..4 {
|
||||
if player_wall_aabbs[dir].collides_with_aabb(block_aabb) {
|
||||
wall_dir_collisions[dir] = true;
|
||||
}
|
||||
// Check for walls
|
||||
if block.is_solid() {
|
||||
let block_aabb = Aabb {
|
||||
min: block_pos.map(|e| e as f32),
|
||||
max: block_pos.map(|e| e as f32) + Vec3::new(1.0, 1.0, block.solid_height()),
|
||||
};
|
||||
|
||||
for dir in 0..4 {
|
||||
if player_wall_aabbs[dir].collides_with_aabb(block_aabb) {
|
||||
wall_dir_collisions[dir] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
//drop(guard);
|
||||
|
||||
// Use wall collision results to determine if we are against a wall
|
||||
let mut on_wall = None;
|
||||
|
Loading…
Reference in New Issue
Block a user