1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 06:16:04 +00:00

start optimizing pathfinder

This commit is contained in:
mat 2023-10-02 16:41:40 -05:00
parent 994bac2c13
commit c3d27487ca
6 changed files with 89 additions and 102 deletions

View file

@ -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));

View file

@ -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);

View file

@ -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,

View file

@ -1,5 +1,6 @@
#![doc = include_str!("../README.md")]
#![feature(trait_alias)]
#![feature(lazy_cell)]
pub mod clip;
pub mod collision;

View file

@ -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));

View file

@ -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:]: