Skip to content

Commit

Permalink
implement fluid_shape and outline_shape
Browse files Browse the repository at this point in the history
  • Loading branch information
mat-1 committed Dec 27, 2024
1 parent 03b0b24 commit d055da9
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 33 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 32 additions & 12 deletions azalea-block/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ mod range;
use core::fmt::Debug;
use std::{
any::Any,
io::{Cursor, Write},
fmt,
io::{self, Cursor, Write},
};

use azalea_buf::{AzaleaRead, AzaleaReadVar, AzaleaWrite, AzaleaWriteVar, BufReadError};
Expand Down Expand Up @@ -115,13 +116,13 @@ impl AzaleaRead for BlockState {
}
}
impl AzaleaWrite for BlockState {
fn azalea_write(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
fn azalea_write(&self, buf: &mut impl Write) -> Result<(), io::Error> {
u32::azalea_write_var(&(self.id as u32), buf)
}
}

impl std::fmt::Debug for BlockState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl Debug for BlockState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"BlockState(id: {}, {:?})",
Expand All @@ -134,65 +135,84 @@ impl std::fmt::Debug for BlockState {
#[derive(Clone, Debug)]
pub struct FluidState {
pub fluid: azalea_registry::Fluid,
pub height: u8,
/// 0 = empty, 8 = full, 9 = max.
///
/// 9 is meant to be used when there's another fluid block of the same type
/// above it, but it's usually unused by this struct.
pub amount: u8,
}
impl FluidState {
/// A floating point number in between 0 and 1 representing the height (as a
/// percentage of a full block) of the fluid.
pub fn height(&self) -> f32 {
self.amount as f32 / 9.
}
}

impl Default for FluidState {
fn default() -> Self {
Self {
fluid: azalea_registry::Fluid::Empty,
height: 0,
amount: 0,
}
}
}

impl From<BlockState> for FluidState {
fn from(state: BlockState) -> Self {
// note that 8 here might be treated as 9 in some cases if there's another fluid
// block of the same type above it

if state
.property::<crate::properties::Waterlogged>()
.unwrap_or_default()
{
Self {
fluid: azalea_registry::Fluid::Water,
height: 15,
amount: 8,
}
} else {
let block = Box::<dyn Block>::from(state);
if let Some(water) = block.downcast_ref::<crate::blocks::Water>() {
Self {
fluid: azalea_registry::Fluid::Water,
height: water.level as u8,
amount: to_or_from_legacy_fluid_level(water.level as u8),
}
} else if let Some(lava) = block.downcast_ref::<crate::blocks::Lava>() {
Self {
fluid: azalea_registry::Fluid::Lava,
height: lava.level as u8,
amount: to_or_from_legacy_fluid_level(lava.level as u8),
}
} else {
Self {
fluid: azalea_registry::Fluid::Empty,
height: 0,
amount: 0,
}
}
}
}
}

// see FlowingFluid.getLegacyLevel
fn to_or_from_legacy_fluid_level(level: u8) -> u8 {
8_u8.saturating_sub(level)
}

impl From<FluidState> for BlockState {
fn from(state: FluidState) -> Self {
match state.fluid {
azalea_registry::Fluid::Empty => BlockState::AIR,
azalea_registry::Fluid::Water | azalea_registry::Fluid::FlowingWater => {
BlockState::from(crate::blocks::Water {
level: crate::properties::WaterLevel::from(
state.height as BlockStateIntegerRepr,
state.amount as BlockStateIntegerRepr,
),
})
}
azalea_registry::Fluid::Lava | azalea_registry::Fluid::FlowingLava => {
BlockState::from(crate::blocks::Lava {
level: crate::properties::LavaLevel::from(
state.height as BlockStateIntegerRepr,
state.amount as BlockStateIntegerRepr,
),
})
}
Expand Down
2 changes: 1 addition & 1 deletion azalea-entity/src/plugin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ pub fn update_fluid_on_eyes(
.read()
.get_fluid_state(&eye_block_pos)
.unwrap_or_default();
let fluid_cutoff_y = eye_block_pos.y as f64 + (fluid_at_eye.height as f64 / 16f64);
let fluid_cutoff_y = eye_block_pos.y as f64 + (fluid_at_eye.amount as f64 / 16f64);
if fluid_cutoff_y > adjusted_eye_y {
**fluid_on_eyes = fluid_at_eye.fluid;
} else {
Expand Down
1 change: 1 addition & 0 deletions azalea-physics/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ azalea-registry = { path = "../azalea-registry", version = "0.11.0" }
azalea-world = { path = "../azalea-world", version = "0.11.0" }
bevy_app = { workspace = true }
bevy_ecs = { workspace = true }
tracing = { workspace = true }
parking_lot = { workspace = true }
63 changes: 51 additions & 12 deletions azalea-physics/src/clip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@ pub struct ClipContext {
// pub collision_context: EntityCollisionContext,
}
impl ClipContext {
// minecraft passes in the world and blockpos here... but it doesn't actually
// seem necessary?

/// Get the shape of given block, using the type of shape set in
/// [`Self::block_shape_type`].
pub fn block_shape(&self, block_state: BlockState) -> &VoxelShape {
// minecraft passes in the world and blockpos to this function but it's not
// actually necessary. it is for fluid_shape though
match self.block_shape_type {
BlockShapeType::Collider => block_state.collision_shape(),
BlockShapeType::Outline => block_state.outline_shape(),
Expand All @@ -41,6 +40,19 @@ impl ClipContext {
}
}
}

pub fn fluid_shape(
&self,
fluid_state: FluidState,
world: &ChunkStorage,
pos: &BlockPos,
) -> &VoxelShape {
if self.fluid_pick_type.can_pick(&fluid_state) {
crate::collision::fluid_shape(&fluid_state, world, pos)
} else {
&EMPTY_SHAPE
}
}
}

#[derive(Debug, Copy, Clone)]
Expand All @@ -63,6 +75,17 @@ pub enum FluidPickType {
Any,
Water,
}
impl FluidPickType {
pub fn can_pick(&self, fluid_state: &FluidState) -> bool {
match self {
Self::None => false,
Self::SourceOnly => fluid_state.amount == 8,
Self::Any => fluid_state.fluid != azalea_registry::Fluid::Empty,
Self::Water => fluid_state.fluid == azalea_registry::Fluid::Water,
}
}
}

#[derive(Debug, Clone)]
pub struct EntityCollisionContext {
pub descending: bool,
Expand All @@ -81,15 +104,29 @@ pub fn clip(chunk_storage: &ChunkStorage, context: ClipContext) -> BlockHitResul
let block_state = chunk_storage.get_block_state(block_pos).unwrap_or_default();
let fluid_state = FluidState::from(block_state);

// TODO: add fluid stuff to this (see getFluidState in vanilla source)
let block_shape = ctx.block_shape(block_state);
let interaction_clip = clip_with_interaction_override(
&ctx.from,
&ctx.to,
block_pos,
block_shape,
&block_state,
);
let fluid_shape = ctx.fluid_shape(fluid_state, chunk_storage, block_pos);
let fluid_clip = fluid_shape.clip(&ctx.from, &ctx.to, block_pos);

clip_with_interaction_override(&ctx.from, &ctx.to, block_pos, block_shape, &block_state)
// let block_distance = if let Some(block_hit_result) =
// block_hit_result { context.from.distance_squared_to(&
// block_hit_result.location) } else {
// f64::INFINITY
// };
let distance_to_interaction = interaction_clip
.map(|hit| ctx.from.distance_squared_to(&hit.location))
.unwrap_or(f64::MAX);
let distance_to_fluid = fluid_clip
.map(|hit| ctx.from.distance_squared_to(&hit.location))
.unwrap_or(f64::MAX);

if distance_to_interaction <= distance_to_fluid {
interaction_clip
} else {
fluid_clip
}
},
|context| {
let vec = context.from - context.to;
Expand All @@ -107,9 +144,10 @@ fn clip_with_interaction_override(
to: &Vec3,
block_pos: &BlockPos,
block_shape: &VoxelShape,
block_state: &BlockState,
_block_state: &BlockState,
) -> Option<BlockHitResult> {
let block_hit_result = block_shape.clip(from, to, block_pos);

if let Some(block_hit_result) = block_hit_result {
// TODO: minecraft calls .getInteractionShape here
// getInteractionShape is empty for almost every shape except cauldons,
Expand All @@ -123,9 +161,10 @@ fn clip_with_interaction_override(
return Some(block_hit_result.with_direction(interaction_hit_result.direction));
}
}

Some(block_hit_result)
} else {
block_hit_result
None
}
}

Expand Down
60 changes: 56 additions & 4 deletions azalea-physics/src/collision/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,21 @@ mod mergers;
mod shape;
mod world_collisions;

use std::ops::Add;

use azalea_core::{aabb::AABB, direction::Axis, math::EPSILON, position::Vec3};
use azalea_world::{Instance, MoveEntityError};
use std::{ops::Add, sync::LazyLock};

use azalea_block::FluidState;
use azalea_core::{
aabb::AABB,
direction::Axis,
math::EPSILON,
position::{BlockPos, Vec3},
};
use azalea_world::{ChunkStorage, Instance, MoveEntityError};
use bevy_ecs::world::Mut;
pub use blocks::BlockWithShape;
pub use discrete_voxel_shape::*;
pub use shape::*;
use tracing::warn;

use self::world_collisions::get_block_collisions;

Expand Down Expand Up @@ -333,3 +340,48 @@ fn collide_with_shapes(
z: z_movement,
}
}

/// Get the [`VoxelShape`] for the given fluid state.
///
/// The instance and position are required so it can check if the block above is
/// also the same fluid type.
pub fn fluid_shape(
fluid: &FluidState,
world: &ChunkStorage,
pos: &BlockPos,
) -> &'static VoxelShape {
if fluid.amount == 9 {
let fluid_state_above = world.get_fluid_state(&pos.up(1)).unwrap_or_default();
if fluid_state_above.fluid == fluid.fluid {
return &BLOCK_SHAPE;
}
}

// pre-calculate these in a LazyLock so this function can return a
// reference instead

static FLUID_SHAPES: LazyLock<[VoxelShape; 10]> = LazyLock::new(|| {
[
calculate_shape_for_fluid(0),
calculate_shape_for_fluid(1),
calculate_shape_for_fluid(2),
calculate_shape_for_fluid(3),
calculate_shape_for_fluid(4),
calculate_shape_for_fluid(5),
calculate_shape_for_fluid(6),
calculate_shape_for_fluid(7),
calculate_shape_for_fluid(8),
calculate_shape_for_fluid(9),
]
});

if fluid.amount > 9 {
warn!("Tried to calculate shape for fluid with height > 9: {fluid:?} at {pos}");
return &EMPTY_SHAPE;
}

&FLUID_SHAPES[fluid.amount as usize]
}
fn calculate_shape_for_fluid(amount: u8) -> VoxelShape {
box_shape(0.0, 0.0, 0.0, 1.0, (f32::from(amount) / 9.0) as f64, 1.0)
}
2 changes: 1 addition & 1 deletion azalea-physics/src/collision/shape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ impl VoxelShape {
VoxelShape::Cube(s) => s.find_index(axis, coord),
_ => {
let upper_limit = (self.shape().size(axis) + 1) as i32;
binary_search(0, upper_limit, &|t| coord < self.get(axis, t as usize)) - 1
binary_search(0, upper_limit, |t| coord < self.get(axis, t as usize)) - 1
}
}
}
Expand Down
5 changes: 2 additions & 3 deletions azalea/src/pathfinder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,8 +340,6 @@ pub fn calculate_path(opts: CalculatePathOpts) -> Option<PathFoundEvent> {
call_successors_fn(&cached_world, &opts.mining_cache, opts.successors_fn, pos)
};

let path;

let start_time = Instant::now();

let astar::Path {
Expand Down Expand Up @@ -375,7 +373,7 @@ pub fn calculate_path(opts: CalculatePathOpts) -> Option<PathFoundEvent> {
debug!(" {}", movement.target.apply(origin));
}

path = movements.into_iter().collect::<VecDeque<_>>();
let path = movements.into_iter().collect::<VecDeque<_>>();

let goto_id_now = opts.goto_id_atomic.load(atomic::Ordering::SeqCst);
if goto_id != goto_id_now {
Expand Down Expand Up @@ -520,6 +518,7 @@ pub fn path_found_listener(
}
}

#[allow(clippy::type_complexity)]
pub fn timeout_movement(
mut query: Query<(
Entity,
Expand Down

0 comments on commit d055da9

Please sign in to comment.