aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormat <git@matdoes.dev>2023-10-02 16:41:40 -0500
committermat <git@matdoes.dev>2023-10-02 16:41:40 -0500
commitc3d27487cae6af5d593193b922d1e81e93a1c45d (patch)
tree022b13cf7e2d32ec9a132206001602236905513c
parent994bac2c13f11aa212798b44816310c661af4a0d (diff)
downloadazalea-drasl-c3d27487cae6af5d593193b922d1e81e93a1c45d.tar.xz
start optimizing pathfinder
-rw-r--r--azalea-physics/src/collision/blocks.rs2
-rwxr-xr-xazalea-physics/src/collision/shape.rs49
-rw-r--r--azalea-physics/src/collision/world_collisions.rs6
-rw-r--r--azalea-physics/src/lib.rs1
-rw-r--r--azalea/src/pathfinder/moves/mod.rs131
-rwxr-xr-xcodegen/lib/code/shapes.py2
6 files changed, 89 insertions, 102 deletions
diff --git a/azalea-physics/src/collision/blocks.rs b/azalea-physics/src/collision/blocks.rs
index a361c208..dfe322b7 100644
--- a/azalea-physics/src/collision/blocks.rs
+++ b/azalea-physics/src/collision/blocks.rs
@@ -15,7 +15,7 @@ pub trait BlockWithShape {
fn shape(&self) -> &'static VoxelShape;
}
-static SHAPE0: Lazy<VoxelShape> = Lazy::new(|| collision::empty_shape());
+static SHAPE0: Lazy<VoxelShape> = Lazy::new(|| collision::EMPTY_SHAPE.clone());
static SHAPE1: Lazy<VoxelShape> = Lazy::new(|| collision::box_shape(0., 0., 0., 1., 1., 1.));
static SHAPE2: Lazy<VoxelShape> =
Lazy::new(|| collision::box_shape(0.4375, 0., 0.4375, 0.5625, 1., 0.5625));
diff --git a/azalea-physics/src/collision/shape.rs b/azalea-physics/src/collision/shape.rs
index 156f5869..6329184f 100755
--- a/azalea-physics/src/collision/shape.rs
+++ b/azalea-physics/src/collision/shape.rs
@@ -6,15 +6,15 @@ use azalea_core::{
math::{binary_search, EPSILON},
position::{BlockPos, Vec3},
};
-use std::{cmp, num::NonZeroU32};
+use std::{cmp, num::NonZeroU32, sync::LazyLock};
pub struct Shapes;
-pub fn block_shape() -> VoxelShape {
+pub static BLOCK_SHAPE: LazyLock<VoxelShape> = LazyLock::new(|| {
let mut shape = BitSetDiscreteVoxelShape::new(1, 1, 1);
shape.fill(0, 0, 0);
VoxelShape::Cube(CubeVoxelShape::new(DiscreteVoxelShape::BitSet(shape)))
-}
+});
pub fn box_shape(
min_x: f64,
@@ -43,7 +43,7 @@ pub fn box_shape_unchecked(
max_z: f64,
) -> VoxelShape {
if max_x - min_x < EPSILON && max_y - min_y < EPSILON && max_z - min_z < EPSILON {
- return empty_shape();
+ return EMPTY_SHAPE.clone();
}
let x_bits = find_bits(min_x, max_x);
@@ -52,14 +52,14 @@ pub fn box_shape_unchecked(
if x_bits < 0 || y_bits < 0 || z_bits < 0 {
return VoxelShape::Array(ArrayVoxelShape::new(
- block_shape().shape(),
+ BLOCK_SHAPE.shape(),
vec![min_x, max_x],
vec![min_y, max_y],
vec![min_z, max_z],
));
}
if x_bits == 0 && y_bits == 0 && z_bits == 0 {
- return block_shape();
+ return BLOCK_SHAPE.clone();
}
let x_bits = 1 << x_bits;
@@ -79,14 +79,14 @@ pub fn box_shape_unchecked(
VoxelShape::Cube(CubeVoxelShape::new(DiscreteVoxelShape::BitSet(shape)))
}
-pub fn empty_shape() -> VoxelShape {
+pub static EMPTY_SHAPE: LazyLock<VoxelShape> = LazyLock::new(|| {
VoxelShape::Array(ArrayVoxelShape::new(
DiscreteVoxelShape::BitSet(BitSetDiscreteVoxelShape::new(0, 0, 0)),
vec![0.],
vec![0.],
vec![0.],
))
-}
+});
fn find_bits(min: f64, max: f64) -> i32 {
if min < -EPSILON || max > 1. + EPSILON {
@@ -143,10 +143,18 @@ impl Shapes {
let op_true_false = op(true, false);
let op_false_true = op(false, true);
if a.is_empty() {
- return if op_false_true { b } else { empty_shape() };
+ return if op_false_true {
+ b
+ } else {
+ EMPTY_SHAPE.clone()
+ };
}
if b.is_empty() {
- return if op_true_false { a } else { empty_shape() };
+ return if op_true_false {
+ a
+ } else {
+ EMPTY_SHAPE.clone()
+ };
}
// IndexMerger var5 = createIndexMerger(1, a.getCoords(Direction.Axis.X),
// b.getCoords(Direction.Axis.X), var3, var4); IndexMerger var6 =
@@ -360,7 +368,7 @@ impl VoxelShape {
#[must_use]
pub fn move_relative(&self, x: f64, y: f64, z: f64) -> VoxelShape {
if self.shape().is_empty() {
- return empty_shape();
+ return EMPTY_SHAPE.clone();
}
VoxelShape::Array(ArrayVoxelShape::new(
@@ -510,24 +518,15 @@ impl VoxelShape {
// return var1[0];
// }
fn optimize(&self) -> VoxelShape {
- // let mut var1 = empty_shape();
- // self.for_all_boxes(|var1x, var3, var5, var7, var9, var11| {
- // var1 = Shapes::join_unoptimized(
- // var1,
- // box_shape(var1x, var3, var5, var7, var9, var11),
- // |a, b| a || b,
- // );
- // });
- // var1
- let mut var1 = empty_shape();
+ let mut shape = EMPTY_SHAPE.clone();
self.for_all_boxes(|var1x, var3, var5, var7, var9, var11| {
- var1 = Shapes::join_unoptimized(
- var1.clone(),
+ shape = Shapes::join_unoptimized(
+ shape.clone(),
box_shape(var1x, var3, var5, var7, var9, var11),
|a, b| a || b,
);
});
- var1
+ shape
}
// public void forAllBoxes(Shapes.DoubleLineConsumer var1) {
@@ -703,7 +702,7 @@ mod tests {
#[test]
fn test_block_shape() {
- let shape = block_shape();
+ let shape = &*BLOCK_SHAPE;
assert_eq!(shape.shape().size(Axis::X), 1);
assert_eq!(shape.shape().size(Axis::Y), 1);
assert_eq!(shape.shape().size(Axis::Z), 1);
diff --git a/azalea-physics/src/collision/world_collisions.rs b/azalea-physics/src/collision/world_collisions.rs
index f5d1b650..c4c6a846 100644
--- a/azalea-physics/src/collision/world_collisions.rs
+++ b/azalea-physics/src/collision/world_collisions.rs
@@ -1,4 +1,4 @@
-use super::Shapes;
+use super::{Shapes, BLOCK_SHAPE};
use crate::collision::{BlockWithShape, VoxelShape, AABB};
use azalea_block::BlockState;
use azalea_core::{
@@ -8,7 +8,7 @@ use azalea_core::{
};
use azalea_world::{Chunk, Instance};
use parking_lot::RwLock;
-use std::sync::Arc;
+use std::{ops::Deref, sync::Arc};
pub fn get_block_collisions(world: &Instance, aabb: AABB) -> BlockCollisions<'_> {
BlockCollisions::new(world, aabb)
@@ -91,7 +91,7 @@ impl<'a> Iterator for BlockCollisions<'a> {
let block_shape = block_state.shape();
// if it's a full block do a faster collision check
- if block_shape == &crate::collision::block_shape() {
+ if block_shape == BLOCK_SHAPE.deref() {
if !self.aabb.intersects_aabb(&AABB {
min_x: item.pos.x as f64,
min_y: item.pos.y as f64,
diff --git a/azalea-physics/src/lib.rs b/azalea-physics/src/lib.rs
index 53818ed3..8c88b97a 100644
--- a/azalea-physics/src/lib.rs
+++ b/azalea-physics/src/lib.rs
@@ -1,5 +1,6 @@
#![doc = include_str!("../README.md")]
#![feature(trait_alias)]
+#![feature(lazy_cell)]
pub mod clip;
pub mod collision;
diff --git a/azalea/src/pathfinder/moves/mod.rs b/azalea/src/pathfinder/moves/mod.rs
index 9bd074c0..197229fd 100644
--- a/azalea/src/pathfinder/moves/mod.rs
+++ b/azalea/src/pathfinder/moves/mod.rs
@@ -35,35 +35,41 @@ impl Debug for MoveData {
/// whether this block is passable
fn is_block_passable(pos: &BlockPos, world: &ChunkStorage) -> bool {
- if let Some(block) = world.get_block_state(pos) {
- if block.shape() != &collision::empty_shape() {
- return false;
- }
- if block == azalea_registry::Block::Water.into() {
- return false;
- }
- if block.waterlogged() {
- return false;
- }
- // block.waterlogged currently doesn't account for seagrass and some other water
- // blocks
- if block == azalea_registry::Block::Seagrass.into() {
- return false;
- }
-
- block.shape() == &collision::empty_shape()
- } else {
- false
+ let Some(block) = world.get_block_state(pos) else {
+ return false;
+ };
+ if block.is_air() {
+ // fast path
+ return true;
+ }
+ if block.shape() != &*collision::EMPTY_SHAPE {
+ return false;
}
+ if block == azalea_registry::Block::Water.into() {
+ return false;
+ }
+ if block.waterlogged() {
+ return false;
+ }
+ // block.waterlogged currently doesn't account for seagrass and some other water
+ // blocks
+ if block == azalea_registry::Block::Seagrass.into() {
+ return false;
+ }
+
+ true
}
/// whether this block has a solid hitbox (i.e. we can stand on it)
fn is_block_solid(pos: &BlockPos, world: &ChunkStorage) -> bool {
- if let Some(block) = world.get_block_state(pos) {
- block.shape() == &collision::block_shape()
- } else {
- false
+ let Some(block) = world.get_block_state(pos) else {
+ return false;
+ };
+ if block.is_air() {
+ // fast path
+ return false;
}
+ block.shape() == &*collision::BLOCK_SHAPE
}
/// Whether this block and the block above are passable
@@ -143,25 +149,20 @@ mod tests {
#[test]
fn test_is_passable() {
let mut partial_world = PartialInstance::default();
- let mut chunk_storage = ChunkStorage::default();
+ let mut world = ChunkStorage::default();
- partial_world.chunks.set(
- &ChunkPos { x: 0, z: 0 },
- Some(Chunk::default()),
- &mut chunk_storage,
- );
+ partial_world
+ .chunks
+ .set(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()), &mut world);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 0, 0),
azalea_registry::Block::Stone.into(),
- &chunk_storage,
- );
- partial_world.chunks.set_block_state(
- &BlockPos::new(0, 1, 0),
- BlockState::AIR,
- &chunk_storage,
+ &world,
);
+ partial_world
+ .chunks
+ .set_block_state(&BlockPos::new(0, 1, 0), BlockState::AIR, &world);
- let world = chunk_storage.into();
assert!(!is_block_passable(&BlockPos::new(0, 0, 0), &world));
assert!(is_block_passable(&BlockPos::new(0, 1, 0), &world));
}
@@ -169,24 +170,19 @@ mod tests {
#[test]
fn test_is_solid() {
let mut partial_world = PartialInstance::default();
- let mut chunk_storage = ChunkStorage::default();
- partial_world.chunks.set(
- &ChunkPos { x: 0, z: 0 },
- Some(Chunk::default()),
- &mut chunk_storage,
- );
+ let mut world = ChunkStorage::default();
+ partial_world
+ .chunks
+ .set(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()), &mut world);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 0, 0),
azalea_registry::Block::Stone.into(),
- &chunk_storage,
- );
- partial_world.chunks.set_block_state(
- &BlockPos::new(0, 1, 0),
- BlockState::AIR,
- &chunk_storage,
+ &world,
);
+ partial_world
+ .chunks
+ .set_block_state(&BlockPos::new(0, 1, 0), BlockState::AIR, &world);
- let world = chunk_storage.into();
assert!(is_block_solid(&BlockPos::new(0, 0, 0), &world));
assert!(!is_block_solid(&BlockPos::new(0, 1, 0), &world));
}
@@ -194,34 +190,25 @@ mod tests {
#[test]
fn test_is_standable() {
let mut partial_world = PartialInstance::default();
- let mut chunk_storage = ChunkStorage::default();
- partial_world.chunks.set(
- &ChunkPos { x: 0, z: 0 },
- Some(Chunk::default()),
- &mut chunk_storage,
- );
+ let mut world = ChunkStorage::default();
+ partial_world
+ .chunks
+ .set(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()), &mut world);
partial_world.chunks.set_block_state(
&BlockPos::new(0, 0, 0),
azalea_registry::Block::Stone.into(),
- &chunk_storage,
- );
- partial_world.chunks.set_block_state(
- &BlockPos::new(0, 1, 0),
- BlockState::AIR,
- &chunk_storage,
- );
- partial_world.chunks.set_block_state(
- &BlockPos::new(0, 2, 0),
- BlockState::AIR,
- &chunk_storage,
- );
- partial_world.chunks.set_block_state(
- &BlockPos::new(0, 3, 0),
- BlockState::AIR,
- &chunk_storage,
+ &world,
);
+ partial_world
+ .chunks
+ .set_block_state(&BlockPos::new(0, 1, 0), BlockState::AIR, &world);
+ partial_world
+ .chunks
+ .set_block_state(&BlockPos::new(0, 2, 0), BlockState::AIR, &world);
+ partial_world
+ .chunks
+ .set_block_state(&BlockPos::new(0, 3, 0), BlockState::AIR, &world);
- let world = chunk_storage.into();
assert!(is_standable(&BlockPos::new(0, 1, 0), &world));
assert!(!is_standable(&BlockPos::new(0, 0, 0), &world));
assert!(!is_standable(&BlockPos::new(0, 2, 0), &world));
diff --git a/codegen/lib/code/shapes.py b/codegen/lib/code/shapes.py
index 18f2ccbd..f270fd7f 100755
--- a/codegen/lib/code/shapes.py
+++ b/codegen/lib/code/shapes.py
@@ -148,7 +148,7 @@ def generate_code_for_shape(shape_id: str, parts: list[list[float]]):
code += f'static SHAPE{shape_id}: Lazy<VoxelShape> = Lazy::new(|| {{'
steps = []
if parts == ():
- steps.append('collision::empty_shape()')
+ steps.append('collision::EMPTY_SHAPE.clone()')
else:
steps.append(f'collision::box_shape({make_arguments(parts[0])})')
for part in parts[1:]: