diff options
| author | mat <git@matdoes.dev> | 2023-10-02 16:41:40 -0500 |
|---|---|---|
| committer | mat <git@matdoes.dev> | 2023-10-02 16:41:40 -0500 |
| commit | c3d27487cae6af5d593193b922d1e81e93a1c45d (patch) | |
| tree | 022b13cf7e2d32ec9a132206001602236905513c | |
| parent | 994bac2c13f11aa212798b44816310c661af4a0d (diff) | |
| download | azalea-drasl-c3d27487cae6af5d593193b922d1e81e93a1c45d.tar.xz | |
start optimizing pathfinder
| -rw-r--r-- | azalea-physics/src/collision/blocks.rs | 2 | ||||
| -rwxr-xr-x | azalea-physics/src/collision/shape.rs | 49 | ||||
| -rw-r--r-- | azalea-physics/src/collision/world_collisions.rs | 6 | ||||
| -rw-r--r-- | azalea-physics/src/lib.rs | 1 | ||||
| -rw-r--r-- | azalea/src/pathfinder/moves/mod.rs | 131 | ||||
| -rwxr-xr-x | codegen/lib/code/shapes.py | 2 |
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:]: |
