diff options
| author | mat <27899617+mat-1@users.noreply.github.com> | 2025-01-10 16:45:27 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-01-10 16:45:27 -0600 |
| commit | 0d16f01571ec8315f3979eae46981e559ade1cf9 (patch) | |
| tree | ea43c32a57b0e6a67579d75a134dfbc009d09781 /azalea-core/src | |
| parent | 615d8f9d2ac56b3244d328587243301da253eafd (diff) | |
| download | azalea-drasl-0d16f01571ec8315f3979eae46981e559ade1cf9.tar.xz | |
Fluid physics (#199)
* start implementing fluid physics
* Initial implementation of fluid pushing
* different travel function in water
* bubble columns
* jumping in water
* cleanup
* change ultrawarm to be required
* fix for clippy
Diffstat (limited to 'azalea-core/src')
| -rwxr-xr-x | azalea-core/src/aabb.rs | 376 | ||||
| -rwxr-xr-x | azalea-core/src/direction.rs | 43 | ||||
| -rw-r--r-- | azalea-core/src/math.rs | 19 | ||||
| -rwxr-xr-x | azalea-core/src/position.rs | 49 | ||||
| -rw-r--r-- | azalea-core/src/registry_holder.rs | 35 |
5 files changed, 324 insertions, 198 deletions
diff --git a/azalea-core/src/aabb.rs b/azalea-core/src/aabb.rs index 70829aa2..fe45c35e 100755 --- a/azalea-core/src/aabb.rs +++ b/azalea-core/src/aabb.rs @@ -8,13 +8,8 @@ use crate::{ /// A rectangular prism with a starting and ending point. #[derive(Copy, Clone, Debug, PartialEq, Default)] pub struct AABB { - pub min_x: f64, - pub min_y: f64, - pub min_z: f64, - - pub max_x: f64, - pub max_y: f64, - pub max_z: f64, + pub min: Vec3, + pub max: Vec3, } pub struct ClipPointOpts<'a> { @@ -23,8 +18,8 @@ pub struct ClipPointOpts<'a> { pub delta: &'a Vec3, pub begin: f64, pub min_x: f64, - pub max_x: f64, pub min_z: f64, + pub max_x: f64, pub max_z: f64, pub result_dir: Direction, pub start: &'a Vec3, @@ -32,51 +27,38 @@ pub struct ClipPointOpts<'a> { impl AABB { pub fn contract(&self, x: f64, y: f64, z: f64) -> AABB { - let mut min_x = self.min_x; - let mut min_y = self.min_y; - let mut min_z = self.min_z; - - let mut max_x = self.max_x; - let mut max_y = self.max_y; - let mut max_z = self.max_z; + let mut min = self.min; + let mut max = self.max; if x < 0.0 { - min_x -= x; + min.x -= x; } else if x > 0.0 { - max_x -= x; + max.x -= x; } if y < 0.0 { - min_y -= y; + min.y -= y; } else if y > 0.0 { - max_y -= y; + max.y -= y; } if z < 0.0 { - min_z -= z; + min.z -= z; } else if z > 0.0 { - max_z -= z; + max.z -= z; } - AABB { - min_x, - min_y, - min_z, - - max_x, - max_y, - max_z, - } + AABB { min, max } } pub fn expand_towards(&self, other: &Vec3) -> AABB { - let mut min_x = self.min_x; - let mut min_y = self.min_y; - let mut min_z = self.min_z; + let mut min_x = self.min.x; + let mut min_y = self.min.y; + let mut min_z = self.min.z; - let mut max_x = self.max_x; - let mut max_y = self.max_y; - let mut max_z = self.max_z; + let mut max_x = self.max.x; + let mut max_y = self.max.y; + let mut max_z = self.max.z; if other.x < 0.0 { min_x += other.x; @@ -97,115 +79,93 @@ impl AABB { } AABB { - min_x, - min_y, - min_z, - - max_x, - max_y, - max_z, + min: Vec3::new(min_x, min_y, min_z), + max: Vec3::new(max_x, max_y, max_z), } } pub fn inflate(&self, x: f64, y: f64, z: f64) -> AABB { - let min_x = self.min_x - x; - let min_y = self.min_y - y; - let min_z = self.min_z - z; + let min_x = self.min.x - x; + let min_y = self.min.y - y; + let min_z = self.min.z - z; - let max_x = self.max_x + x; - let max_y = self.max_y + y; - let max_z = self.max_z + z; + let max_x = self.max.x + x; + let max_y = self.max.y + y; + let max_z = self.max.z + z; AABB { - min_x, - min_y, - min_z, - - max_x, - max_y, - max_z, + min: Vec3::new(min_x, min_y, min_z), + max: Vec3::new(max_x, max_y, max_z), } } pub fn intersect(&self, other: &AABB) -> AABB { - let min_x = self.min_x.max(other.min_x); - let min_y = self.min_y.max(other.min_y); - let min_z = self.min_z.max(other.min_z); + let min_x = self.min.x.max(other.min.x); + let min_y = self.min.y.max(other.min.y); + let min_z = self.min.z.max(other.min.z); - let max_x = self.max_x.min(other.max_x); - let max_y = self.max_y.min(other.max_y); - let max_z = self.max_z.min(other.max_z); + let max_x = self.max.x.min(other.max.x); + let max_y = self.max.y.min(other.max.y); + let max_z = self.max.z.min(other.max.z); AABB { - min_x, - min_y, - min_z, - - max_x, - max_y, - max_z, + min: Vec3::new(min_x, min_y, min_z), + max: Vec3::new(max_x, max_y, max_z), } } pub fn minmax(&self, other: &AABB) -> AABB { - let min_x = self.min_x.min(other.min_x); - let min_y = self.min_y.min(other.min_y); - let min_z = self.min_z.min(other.min_z); + let min_x = self.min.x.min(other.min.x); + let min_y = self.min.y.min(other.min.y); + let min_z = self.min.z.min(other.min.z); - let max_x = self.max_x.max(other.max_x); - let max_y = self.max_y.max(other.max_y); - let max_z = self.max_z.max(other.max_z); + let max_x = self.max.x.max(other.max.x); + let max_y = self.max.y.max(other.max.y); + let max_z = self.max.z.max(other.max.z); AABB { - min_x, - min_y, - min_z, - - max_x, - max_y, - max_z, + min: Vec3::new(min_x, min_y, min_z), + max: Vec3::new(max_x, max_y, max_z), } } - pub fn move_relative(&self, delta: &Vec3) -> AABB { + pub fn move_relative(&self, delta: Vec3) -> AABB { AABB { - min_x: self.min_x + delta.x, - min_y: self.min_y + delta.y, - min_z: self.min_z + delta.z, - - max_x: self.max_x + delta.x, - max_y: self.max_y + delta.y, - max_z: self.max_z + delta.z, + min: self.min + delta, + max: self.max + delta, } } pub fn intersects_aabb(&self, other: &AABB) -> bool { - self.min_x < other.max_x - && self.max_x > other.min_x - && self.min_y < other.max_y - && self.max_y > other.min_y - && self.min_z < other.max_z - && self.max_z > other.min_z + self.min.x < other.max.x + && self.max.x > other.min.x + && self.min.y < other.max.y + && self.max.y > other.min.y + && self.min.z < other.max.z + && self.max.z > other.min.z } pub fn intersects_vec3(&self, other: &Vec3, other2: &Vec3) -> bool { self.intersects_aabb(&AABB { - min_x: other.x.min(other2.x), - min_y: other.y.min(other2.y), - min_z: other.z.min(other2.z), - - max_x: other.x.max(other2.x), - max_y: other.y.max(other2.y), - max_z: other.z.max(other2.z), + min: Vec3::new( + other.x.min(other2.x), + other.y.min(other2.y), + other.z.min(other2.z), + ), + max: Vec3::new( + other.x.max(other2.x), + other.y.max(other2.y), + other.z.max(other2.z), + ), }) } - pub fn contains(&self, x: f64, y: f64, z: f64) -> bool { - x >= self.min_x - && x < self.max_x - && y >= self.min_y - && y < self.max_y - && z >= self.min_z - && z < self.max_z + pub fn contains(&self, point: &Vec3) -> bool { + point.x >= self.min.x + && point.x < self.max.x + && point.y >= self.min.y + && point.y < self.max.y + && point.z >= self.min.z + && point.z < self.max.z } pub fn size(&self) -> f64 { @@ -217,9 +177,9 @@ impl AABB { pub fn get_size(&self, axis: Axis) -> f64 { axis.choose( - self.max_x - self.min_x, - self.max_y - self.min_y, - self.max_z - self.min_z, + self.max.x - self.min.x, + self.max.y - self.min.y, + self.max.z - self.min.z, ) } @@ -227,13 +187,24 @@ impl AABB { self.inflate(-x, -y, -z) } + pub fn deflate_all(&mut self, amount: f64) -> AABB { + self.deflate(amount, amount, amount) + } + pub fn clip(&self, min: &Vec3, max: &Vec3) -> Option<Vec3> { let mut t = 1.0; let delta = max - min; - let _dir = Self::get_direction(self, min, &mut t, None, &delta)?; + let _dir = Self::get_direction_aabb(self, min, &mut t, None, &delta)?; Some(min + &(delta * t)) } + pub fn clip_with_from_and_to(min: &Vec3, max: &Vec3, from: &Vec3, to: &Vec3) -> Option<Vec3> { + let mut t = 1.0; + let delta = to - from; + let _dir = Self::get_direction(min, max, from, &mut t, None, &delta)?; + Some(from + &(delta * t)) + } + pub fn clip_iterable( boxes: &Vec<AABB>, from: &Vec3, @@ -245,8 +216,8 @@ impl AABB { let delta = to - from; for aabb in boxes { - dir = Self::get_direction( - &aabb.move_relative(&pos.to_vec3_floored()), + dir = Self::get_direction_aabb( + &aabb.move_relative(pos.to_vec3_floored()), from, &mut t, dir, @@ -264,8 +235,19 @@ impl AABB { }) } + fn get_direction_aabb( + &self, + from: &Vec3, + t: &mut f64, + dir: Option<Direction>, + delta: &Vec3, + ) -> Option<Direction> { + AABB::get_direction(&self.min, &self.max, from, t, dir, delta) + } + fn get_direction( - aabb: &AABB, + min: &Vec3, + max: &Vec3, from: &Vec3, t: &mut f64, mut dir: Option<Direction>, @@ -276,11 +258,11 @@ impl AABB { t, approach_dir: dir, delta, - begin: aabb.min_x, - min_x: aabb.min_y, - max_x: aabb.max_y, - min_z: aabb.min_z, - max_z: aabb.max_z, + begin: min.x, + min_x: min.y, + max_x: max.y, + min_z: min.z, + max_z: max.z, result_dir: Direction::West, start: from, }); @@ -289,11 +271,11 @@ impl AABB { t, approach_dir: dir, delta, - begin: aabb.max_x, - min_x: aabb.min_y, - max_x: aabb.max_y, - min_z: aabb.min_z, - max_z: aabb.max_z, + begin: max.x, + min_x: min.y, + max_x: max.y, + min_z: min.z, + max_z: max.z, result_dir: Direction::East, start: from, }); @@ -308,11 +290,11 @@ impl AABB { y: delta.z, z: delta.x, }, - begin: aabb.min_y, - min_x: aabb.min_z, - max_x: aabb.max_z, - min_z: aabb.min_x, - max_z: aabb.max_x, + begin: min.y, + min_x: min.z, + max_x: max.z, + min_z: min.x, + max_z: max.x, result_dir: Direction::Down, start: &Vec3 { x: from.y, @@ -329,11 +311,11 @@ impl AABB { y: delta.z, z: delta.x, }, - begin: aabb.max_y, - min_x: aabb.min_z, - max_x: aabb.max_z, - min_z: aabb.min_x, - max_z: aabb.max_x, + begin: max.y, + min_x: min.z, + max_x: max.z, + min_z: min.x, + max_z: max.x, result_dir: Direction::Up, start: &Vec3 { x: from.y, @@ -352,11 +334,11 @@ impl AABB { y: delta.x, z: delta.y, }, - begin: aabb.min_z, - min_x: aabb.min_x, - max_x: aabb.max_x, - min_z: aabb.min_y, - max_z: aabb.max_y, + begin: min.z, + min_x: min.x, + max_x: max.x, + min_z: min.y, + max_z: max.y, result_dir: Direction::North, start: &Vec3 { x: from.z, @@ -373,11 +355,11 @@ impl AABB { y: delta.x, z: delta.y, }, - begin: aabb.max_z, - min_x: aabb.min_x, - max_x: aabb.max_x, - min_z: aabb.min_y, - max_z: aabb.max_y, + begin: max.z, + min_x: min.x, + max_x: max.x, + min_z: min.y, + max_z: max.y, result_dir: Direction::South, start: &Vec3 { x: from.z, @@ -409,38 +391,96 @@ impl AABB { } pub fn has_nan(&self) -> bool { - self.min_x.is_nan() - || self.min_y.is_nan() - || self.min_z.is_nan() - || self.max_x.is_nan() - || self.max_y.is_nan() - || self.max_z.is_nan() + self.min.x.is_nan() + || self.min.y.is_nan() + || self.min.z.is_nan() + || self.max.x.is_nan() + || self.max.y.is_nan() + || self.max.z.is_nan() } pub fn get_center(&self) -> Vec3 { Vec3::new( - (self.min_x + self.max_x) / 2.0, - (self.min_y + self.max_y) / 2.0, - (self.min_z + self.max_z) / 2.0, + (self.min.x + self.max.x) / 2.0, + (self.min.y + self.max.y) / 2.0, + (self.min.z + self.max.z) / 2.0, ) } pub fn of_size(center: Vec3, dx: f64, dy: f64, dz: f64) -> AABB { AABB { - min_x: center.x - dx / 2.0, - min_y: center.y - dy / 2.0, - min_z: center.z - dz / 2.0, - max_x: center.x + dx / 2.0, - max_y: center.y + dy / 2.0, - max_z: center.z + dz / 2.0, + min: Vec3::new( + center.x - dx / 2.0, + center.y - dy / 2.0, + center.z - dz / 2.0, + ), + max: Vec3::new( + center.x + dx / 2.0, + center.y + dy / 2.0, + center.z + dz / 2.0, + ), } } pub fn max(&self, axis: &Axis) -> f64 { - axis.choose(self.max_x, self.max_y, self.max_z) + axis.choose(self.max.x, self.max.y, self.max.z) } pub fn min(&self, axis: &Axis) -> f64 { - axis.choose(self.min_x, self.min_y, self.min_z) + axis.choose(self.min.x, self.min.y, self.min.z) + } + + pub fn collided_along_vector(&self, vector: Vec3, boxes: &Vec<AABB>) -> bool { + let center = self.get_center(); + let new_center = center + vector; + + for aabb in boxes { + let inflated = aabb.inflate( + self.get_size(Axis::X) * 0.5, + self.get_size(Axis::Y) * 0.5, + self.get_size(Axis::Z) * 0.5, + ); + if inflated.contains(&new_center) || inflated.contains(¢er) { + return true; + } + + if inflated.clip(¢er, &new_center).is_some() { + return true; + } + } + + false + } +} + +impl BlockPos { + pub fn between_closed_aabb(aabb: &AABB) -> Vec<BlockPos> { + BlockPos::between_closed(BlockPos::from(aabb.min), BlockPos::from(aabb.max)) + } + + pub fn between_closed(min: BlockPos, max: BlockPos) -> Vec<BlockPos> { + assert!(min.x <= max.x); + assert!(min.y <= max.y); + assert!(min.z <= max.z); + + let length_x = max.x - min.x + 1; + let length_y = max.y - min.y + 1; + let length_z = max.z - min.z + 1; + let volume = length_x * length_y * length_z; + + let mut result = Vec::with_capacity(volume as usize); + for index in 0..volume { + let index_x = index % length_x; + let remaining_after_x = index / length_x; + let index_y = remaining_after_x % length_y; + let index_z = remaining_after_x / length_y; + result.push(BlockPos::new( + min.x + index_x, + min.y + index_y, + min.z + index_z, + )); + } + + result } } @@ -453,12 +493,8 @@ mod tests { assert_ne!( AABB::clip_iterable( &vec![AABB { - min_x: 0., - min_y: 0., - min_z: 0., - max_x: 1., - max_y: 1., - max_z: 1., + min: Vec3::new(0., 0., 0.), + max: Vec3::new(1., 1., 1.), }], &Vec3::new(-1., -1., -1.), &Vec3::new(1., 1., 1.), diff --git a/azalea-core/src/direction.rs b/azalea-core/src/direction.rs index 6ff55615..d458f487 100755 --- a/azalea-core/src/direction.rs +++ b/azalea-core/src/direction.rs @@ -1,6 +1,6 @@ use azalea_buf::AzBuf; -use crate::position::Vec3; +use crate::position::{BlockPos, Vec3}; #[derive(Clone, Copy, Debug, AzBuf, Default, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] @@ -15,6 +15,14 @@ pub enum Direction { } impl Direction { + pub const HORIZONTAL: [Direction; 4] = [ + Direction::North, + Direction::South, + Direction::West, + Direction::East, + ]; + pub const VERTICAL: [Direction; 2] = [Direction::Down, Direction::Up]; + pub fn nearest(vec: Vec3) -> Direction { let mut best_direction = Direction::North; let mut best_direction_amount = 0.0; @@ -29,7 +37,7 @@ impl Direction { ] .iter() { - let amount = dir.normal().dot(vec); + let amount = dir.normal_vec3().dot(vec); if amount > best_direction_amount { best_direction = *dir; best_direction_amount = amount; @@ -39,17 +47,23 @@ impl Direction { best_direction } - pub fn normal(self) -> Vec3 { + #[inline] + pub fn normal(self) -> BlockPos { match self { - Direction::Down => Vec3::new(0.0, -1.0, 0.0), - Direction::Up => Vec3::new(0.0, 1.0, 0.0), - Direction::North => Vec3::new(0.0, 0.0, -1.0), - Direction::South => Vec3::new(0.0, 0.0, 1.0), - Direction::West => Vec3::new(-1.0, 0.0, 0.0), - Direction::East => Vec3::new(1.0, 0.0, 0.0), + Direction::Down => BlockPos::new(0, -1, 0), + Direction::Up => BlockPos::new(0, 1, 0), + Direction::North => BlockPos::new(0, 0, -1), + Direction::South => BlockPos::new(0, 0, 1), + Direction::West => BlockPos::new(-1, 0, 0), + Direction::East => BlockPos::new(1, 0, 0), } } + #[inline] + pub fn normal_vec3(self) -> Vec3 { + self.normal().to_vec3_floored() + } + pub fn opposite(self) -> Direction { match self { Direction::Down => Direction::Up, @@ -60,6 +74,16 @@ impl Direction { Direction::East => Direction::West, } } + + pub fn x(self) -> i32 { + self.normal().x + } + pub fn y(self) -> i32 { + self.normal().y + } + pub fn z(self) -> i32 { + self.normal().z + } } /// The four cardinal directions. @@ -75,6 +99,7 @@ pub enum CardinalDirection { East, } +/// A 3D axis like x, y, z. #[derive(Clone, Copy, Debug)] pub enum Axis { X = 0, diff --git a/azalea-core/src/math.rs b/azalea-core/src/math.rs index 67ece5cc..a763fc49 100644 --- a/azalea-core/src/math.rs +++ b/azalea-core/src/math.rs @@ -86,6 +86,25 @@ pub fn to_degrees(radians: f64) -> f64 { radians * 57.29577951308232 } +/// Returns either -1, 0, or 1, depending on whether the number is negative, +/// zero, or positive. +/// +/// This function exists because f64::signum doesn't check for 0. +pub fn sign(num: f64) -> f64 { + if num == 0. { + 0. + } else { + num.signum() + } +} +pub fn sign_as_int(num: f64) -> i32 { + if num == 0. { + 0 + } else { + num.signum() as i32 + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/azalea-core/src/position.rs b/azalea-core/src/position.rs index 819d72cf..cba58415 100755 --- a/azalea-core/src/position.rs +++ b/azalea-core/src/position.rs @@ -8,11 +8,12 @@ use std::{ fmt, hash::Hash, io::{Cursor, Write}, - ops::{Add, AddAssign, Mul, Rem, Sub}, + ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub}, }; use azalea_buf::{AzBuf, AzaleaRead, AzaleaWrite, BufReadError}; +use crate::direction::Direction; use crate::math; use crate::resource_location::ResourceLocation; @@ -138,7 +139,6 @@ macro_rules! vec3_impl { } } } - impl Add for $name { type Output = $name; @@ -147,6 +147,18 @@ macro_rules! vec3_impl { (&self).add(&rhs) } } + impl Add<$type> for $name { + type Output = Self; + + #[inline] + fn add(self, rhs: $type) -> Self::Output { + Self { + x: self.x + rhs, + y: self.y + rhs, + z: self.z + rhs, + } + } + } impl AddAssign for $name { #[inline] @@ -203,6 +215,35 @@ macro_rules! vec3_impl { } } } + impl MulAssign<$type> for $name { + #[inline] + fn mul_assign(&mut self, multiplier: $type) { + self.x *= multiplier; + self.y *= multiplier; + self.z *= multiplier; + } + } + + impl Div<$type> for $name { + type Output = Self; + + #[inline] + fn div(self, divisor: $type) -> Self::Output { + Self { + x: self.x / divisor, + y: self.y / divisor, + z: self.z / divisor, + } + } + } + impl DivAssign<$type> for $name { + #[inline] + fn div_assign(&mut self, divisor: $type) { + self.x /= divisor; + self.y /= divisor; + self.z /= divisor; + } + } impl From<($type, $type, $type)> for $name { #[inline] @@ -345,6 +386,10 @@ impl BlockPos { z: self.z.max(other.z), } } + + pub fn offset_with_direction(self, direction: Direction) -> Self { + self + direction.normal() + } } /// Chunk coordinates are used to represent where a chunk is in the world. You diff --git a/azalea-core/src/registry_holder.rs b/azalea-core/src/registry_holder.rs index 8b3dd4e6..0d2588cf 100644 --- a/azalea-core/src/registry_holder.rs +++ b/azalea-core/src/registry_holder.rs @@ -39,6 +39,23 @@ impl RegistryHolder { } } + /// Get the dimension type registry, or `None` if it doesn't exist. You + /// should do some type of error handling if this returns `None`. + pub fn dimension_type(&self) -> Option<RegistryType<DimensionTypeElement>> { + let name = ResourceLocation::new("minecraft:dimension_type"); + match self.get(&name) { + Some(Ok(registry)) => Some(registry), + Some(Err(err)) => { + error!( + "Error deserializing dimension type registry: {err:?}\n{:?}", + self.map.get(&name) + ); + None + } + None => None, + } + } + fn get<T: Deserialize>( &self, name: &ResourceLocation, @@ -66,23 +83,6 @@ impl RegistryHolder { Some(Ok(RegistryType { map })) } - - /// Get the dimension type registry, or `None` if it doesn't exist. You - /// should do some type of error handling if this returns `None`. - pub fn dimension_type(&self) -> Option<RegistryType<DimensionTypeElement>> { - let name = ResourceLocation::new("minecraft:dimension_type"); - match self.get(&name) { - Some(Ok(registry)) => Some(registry), - Some(Err(err)) => { - error!( - "Error deserializing dimension type registry: {err:?}\n{:?}", - self.map.get(&name) - ); - None - } - None => None, - } - } } /// A collection of values for a certain type of registry data. @@ -161,6 +161,7 @@ pub struct DimensionTypeElement { pub struct DimensionTypeElement { pub height: u32, pub min_y: i32, + pub ultrawarm: bool, #[simdnbt(flatten)] pub _extra: HashMap<String, NbtTag>, } |
