More voxel collision speedups by reducing the common cases from 5 to 3 voxel iterations

This commit is contained in:
Imbris 2021-03-18 21:01:32 -04:00
parent 87883d9b11
commit 9fb44e5674
4 changed files with 54 additions and 54 deletions

View File

@ -77,6 +77,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Overhauled the sceptre - Overhauled the sceptre
- Make the /time command relative to the current day - Make the /time command relative to the current day
- Spatial partitioning via a grid for entity versus entity collisions was added which can more than halve the total tick time at higher entity counts (> ~1000) - Spatial partitioning via a grid for entity versus entity collisions was added which can more than halve the total tick time at higher entity counts (> ~1000)
- Improved efficency of entity versus terrain collisions (they now take less than half the time)
- The loading screen will now display random animations - The loading screen will now display random animations
### Removed ### Removed

1
Cargo.lock generated
View File

@ -5634,6 +5634,7 @@ dependencies = [
"bincode", "bincode",
"hashbrown", "hashbrown",
"indexmap", "indexmap",
"ordered-float 2.1.1",
"rand 0.8.3", "rand 0.8.3",
"rayon", "rayon",
"scopeguard", "scopeguard",

View File

@ -21,6 +21,7 @@ rand = "0.8"
rayon = "1.5" rayon = "1.5"
tracing = { version = "0.1", default-features = false } tracing = { version = "0.1", default-features = false }
vek = { version = "=0.14.1", features = ["serde"] } vek = { version = "=0.14.1", features = ["serde"] }
ordered-float = { version = "2.0.1", default-features = false }
# Data structures # Data structures
hashbrown = { version = "0.9", features = ["rayon", "serde", "nightly"] } hashbrown = { version = "0.9", features = ["rayon", "serde", "nightly"] }

View File

@ -1162,57 +1162,54 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
const MAX_ATTEMPTS: usize = 16; const MAX_ATTEMPTS: usize = 16;
// While the player is colliding with the terrain... // While the player is colliding with the terrain...
while collision_with( while let Some((_block_pos, block_aabb, block_height)) =
pos.0, (attempts < MAX_ATTEMPTS).then(|| {
&terrain, // Calculate the player's AABB
block_true, let player_aabb = Aabb {
near_iter.clone(), min: pos.0 + Vec3::new(-radius, -radius, z_min),
radius, max: pos.0 + Vec3::new(radius, radius, z_max),
z_range.clone(), };
) && attempts < MAX_ATTEMPTS
{
// 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),
};
// Determine the block that we are colliding with most (based on minimum // Determine the block that we are colliding with most (based on minimum
// collision axis) // collision axis) (if we are colliding with one)
let (_block_pos, block_aabb, block_height) = near_iter near_iter
.clone() .clone()
// Calculate the block's position in world space // Calculate the block's position in world space
.map(|(i, j, k)| pos.0.map(|e| e.floor() as i32) + Vec3::new(i, j, k)) .map(|(i, j, k)| pos.0.map(|e| e.floor() as i32) + Vec3::new(i, j, k))
// Make sure the block is actually solid // Make sure the block is actually solid
.filter_map(|block_pos| { .filter_map(|block_pos| {
if let Some(block) = terrain terrain
.get(block_pos) .get(block_pos)
.ok() .ok()
.filter(|block| block.is_solid()) .filter(|block| block.is_solid())
{ .map(|block| (block_pos, block))
// Calculate block AABB })
Some(( // Calculate block AABB
.map(|(block_pos, block)| {
(
block_pos, block_pos,
Aabb { Aabb {
min: block_pos.map(|e| e as f32), 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()), max: block_pos.map(|e| e as f32) + Vec3::new(1.0, 1.0, block.solid_height()),
}, },
block.solid_height(), block.solid_height(),
)) )
} else { })
None // Determine whether the block's AABB collides with the player's AABB
} .filter(|(_, block_aabb, _)| block_aabb.collides_with_aabb(player_aabb))
}) // Find the maximum of the minimum collision axes (this bit is weird, trust me that it works)
// Determine whether the block's AABB collides with the player's AABB .min_by_key(|(_, block_aabb, _)| {
.filter(|(_, block_aabb, _)| block_aabb.collides_with_aabb(player_aabb)) ordered_float::OrderedFloat((block_aabb.center() - player_aabb.center() - Vec3::unit_z() * 0.5)
// Find the maximum of the minimum collision axes (this bit is weird, trust me that it works) .map(f32::abs)
.min_by_key(|(_, block_aabb, _)| { .sum())
((block_aabb.center() - player_aabb.center() - Vec3::unit_z() * 0.5) })
.map(|e| e.abs()) }).flatten()
.sum() {
* 1_000_000.0) as i32 // Calculate the player's AABB
}) let player_aabb = Aabb {
.expect("Collision detected, but no colliding blocks found!"); min: pos.0 + Vec3::new(-radius, -radius, z_min),
max: pos.0 + Vec3::new(radius, radius, z_max),
};
// Find the intrusion vector of the collision // Find the intrusion vector of the collision
let dir = player_aabb.collision_vector_with_aabb(block_aabb); let dir = player_aabb.collision_vector_with_aabb(block_aabb);
@ -1243,12 +1240,12 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
} }
// When the resolution direction is non-vertical, we must be colliding // When the resolution direction is non-vertical, we must be colliding
// with a wall If the space above is free... // with a wall If we're being pushed out horizontally...
if !collision_with(Vec3::new(pos.0.x, pos.0.y, (pos.0.z + 0.1).ceil()), &terrain, block_true, near_iter.clone(), radius, z_range.clone()) if resolve_dir.z == 0.0
// ...and we're being pushed out horizontally...
&& resolve_dir.z == 0.0
// ...and the vertical resolution direction is sufficiently great... // ...and the vertical resolution direction is sufficiently great...
&& dir.z < -0.1 && dir.z < -0.1
// ...and the space above is free...
&& !collision_with(Vec3::new(pos.0.x, pos.0.y, (pos.0.z + 0.1).ceil()), &terrain, block_true, near_iter.clone(), radius, z_range.clone())
// ...and we're falling/standing OR there is a block *directly* beneath our current origin (note: not hitbox)... // ...and we're falling/standing OR there is a block *directly* beneath our current origin (note: not hitbox)...
// && terrain // && terrain
// .get((pos.0 - Vec3::unit_z() * 0.1).map(|e| e.floor() as i32)) // .get((pos.0 - Vec3::unit_z() * 0.1).map(|e| e.floor() as i32))
@ -1323,18 +1320,18 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
physics_state.on_ground = true; physics_state.on_ground = true;
} }
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);
let dirs = [ let dirs = [
Vec3::unit_x(), Vec3::unit_x(),
Vec3::unit_y(), Vec3::unit_y(),
-Vec3::unit_x(), -Vec3::unit_x(),
-Vec3::unit_y(), -Vec3::unit_y(),
]; ];
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);
let player_wall_aabbs = dirs.map(|dir| { let player_wall_aabbs = dirs.map(|dir| {
let pos = pos.0 + dir * 0.01; let pos = pos.0 + dir * 0.01;
Aabb { Aabb {