diff options
| author | mat <27899617+mat-1@users.noreply.github.com> | 2023-05-03 20:57:27 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-05-03 20:57:27 -0500 |
| commit | 634cb8d72c6608512aedba19e5cd669104bc35ea (patch) | |
| tree | f8e76ce9eb43403d29cc0cbcf9a4f51522419dc2 /azalea-physics | |
| parent | 1fb4418f2c9cbd004c64c2f23d2d0352ee12c0e5 (diff) | |
| download | azalea-drasl-634cb8d72c6608512aedba19e5cd669104bc35ea.tar.xz | |
Inventory (#48)
* start adding azalea-inventory
* design more of how inventories are defined
* start working on az-inv-macros
* inventory macro works
* start adding inventory codegen
* update some deps
* add inventory codegen
* manually write inventory menus
* put the inventories in Client
* start on containersetcontent
* inventory menu should hopefully work
* checks in containersetcontent
* format a comment
* move some variant matches
* inventory.rs
* inventory stuff
* more inventory stuff
* inventory/container tracking works
* start adding interact function
* sequence number
* start adding HitResultComponent
* implement traverse_blocks
* start adding clip
* add clip function
* update_hit_result_component
* start trying to fix
* fix
* make some stuff simpler
* clippy
* lever
* chest
* container handle
* fix ambiguity
* fix some doc tests
* move some container stuff from az-client to azalea
* clicking container
* start implementing simulate_click
* keep working on simulate click
* implement more of simulate_click
this is really boring
* inventory fixes
* start implementing shift clicking
* fix panic in azalea-chat i hope
* shift clicking implemented
* more inventory stuff
* fix items not showing in containers sometimes
* fix test
* fix all warnings
* remove a println
---------
Co-authored-by: mat <git@matdoes.dev>
Diffstat (limited to 'azalea-physics')
| -rw-r--r-- | azalea-physics/Cargo.toml | 1 | ||||
| -rw-r--r-- | azalea-physics/src/clip.rs | 232 | ||||
| -rwxr-xr-x | azalea-physics/src/collision/discrete_voxel_shape.rs | 1 | ||||
| -rw-r--r-- | azalea-physics/src/collision/mod.rs | 23 | ||||
| -rwxr-xr-x | azalea-physics/src/collision/shape.rs | 62 | ||||
| -rw-r--r-- | azalea-physics/src/lib.rs | 49 |
6 files changed, 340 insertions, 28 deletions
diff --git a/azalea-physics/Cargo.toml b/azalea-physics/Cargo.toml index dd579471..ca7390eb 100644 --- a/azalea-physics/Cargo.toml +++ b/azalea-physics/Cargo.toml @@ -11,6 +11,7 @@ version = "0.6.0" [dependencies] azalea-block = { path = "../azalea-block", version = "^0.6.0" } azalea-core = { path = "../azalea-core", version = "^0.6.0" } +azalea-inventory = { version = "0.1.0", path = "../azalea-inventory" } azalea-registry = { path = "../azalea-registry", version = "^0.6.0" } azalea-world = { path = "../azalea-world", version = "^0.6.0" } bevy_app = "0.10.0" diff --git a/azalea-physics/src/clip.rs b/azalea-physics/src/clip.rs new file mode 100644 index 00000000..ca85c32a --- /dev/null +++ b/azalea-physics/src/clip.rs @@ -0,0 +1,232 @@ +use azalea_block::BlockState; +use azalea_core::{lerp, BlockHitResult, BlockPos, Direction, Vec3, EPSILON}; +use azalea_inventory::ItemSlot; +use azalea_world::ChunkStorage; +use bevy_ecs::entity::Entity; + +use crate::collision::{BlockWithShape, VoxelShape}; + +#[derive(Debug, Clone)] +pub struct ClipContext { + pub from: Vec3, + pub to: Vec3, + pub block_shape_type: BlockShapeType, + pub fluid_pick_type: FluidPickType, + // pub collision_context: EntityCollisionContext, +} +impl ClipContext { + // minecraft passes in the world and blockpos here... but it doesn't actually + // seem necessary? + pub fn block_shape(&self, block_state: BlockState) -> &VoxelShape { + // TODO: implement the other shape getters + // (see the ClipContext.Block class in the vanilla source) + match self.block_shape_type { + BlockShapeType::Collider => block_state.shape(), + BlockShapeType::Outline => block_state.shape(), + BlockShapeType::Visual => block_state.shape(), + BlockShapeType::FallDamageResetting => block_state.shape(), + } + } +} + +#[derive(Debug, Copy, Clone)] +pub enum BlockShapeType { + Collider, + Outline, + Visual, + FallDamageResetting, +} +#[derive(Debug, Copy, Clone)] +pub enum FluidPickType { + None, + SourceOnly, + Any, + Water, +} +#[derive(Debug, Clone)] +pub struct EntityCollisionContext { + pub descending: bool, + pub entity_bottom: f64, + pub held_item: ItemSlot, + // pub can_stand_on_fluid: Box<dyn Fn(&FluidState) -> bool>, + pub entity: Entity, +} + +pub fn clip(chunk_storage: &ChunkStorage, context: ClipContext) -> BlockHitResult { + traverse_blocks( + context.from, + context.to, + context, + |context, block_pos| { + let block_state = chunk_storage.get_block_state(block_pos).unwrap_or_default(); + // TODO: add fluid stuff to this (see getFluidState in vanilla source) + let block_shape = context.block_shape(block_state); + clip_with_interaction_override( + &context.from, + &context.to, + block_pos, + block_shape, + &block_state, + ) + // let block_distance = if let Some(block_hit_result) = + // block_hit_result { context.from.distance_to_sqr(& + // block_hit_result.location) } else { + // f64::MAX + // }; + }, + |context| { + let vec = context.from - context.to; + BlockHitResult::miss( + context.to, + Direction::nearest(vec), + BlockPos::from(context.to), + ) + }, + ) +} + +// default BlockHitResult clipWithInteractionOverride(Vec3 world, Vec3 from, +// BlockPos to, VoxelShape shape, BlockState block) { +// BlockHitResult blockHitResult = shape.clip(world, from, to); +// if (blockHitResult != null) { +// BlockHitResult var7 = block.getInteractionShape(this, to).clip(world, +// from, to); if (var7 != null +// && var7.getLocation().subtract(world).lengthSqr() < +// blockHitResult.getLocation().subtract(world).lengthSqr()) { return +// blockHitResult.withDirection(var7.getDirection()); } +// } + +// return blockHitResult; +// } +fn clip_with_interaction_override( + from: &Vec3, + to: &Vec3, + block_pos: &BlockPos, + block_shape: &VoxelShape, + 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 + // are there even any blocks that have a physics shape different from the + // interaction shape??? + // (if not then you can delete this comment) + // (if there are then you have to implement BlockState::interaction_shape, lol + // have fun) + let interaction_shape = block_state.shape(); + let interaction_hit_result = interaction_shape.clip(from, to, block_pos); + if let Some(interaction_hit_result) = interaction_hit_result { + if interaction_hit_result.location.distance_to_sqr(from) + < block_hit_result.location.distance_to_sqr(from) + { + return Some(block_hit_result.with_direction(interaction_hit_result.direction)); + } + } + Some(block_hit_result) + } else { + block_hit_result + } +} + +pub fn traverse_blocks<C, T>( + from: Vec3, + to: Vec3, + context: C, + get_hit_result: impl Fn(&C, &BlockPos) -> Option<T>, + get_miss_result: impl Fn(&C) -> T, +) -> T { + if from == to { + return get_miss_result(&context); + } + + let right_after_end = Vec3 { + x: lerp(-EPSILON, to.x, from.x), + y: lerp(-EPSILON, to.y, from.y), + z: lerp(-EPSILON, to.z, from.z), + }; + + let right_before_start = Vec3 { + x: lerp(-EPSILON, from.x, to.x), + y: lerp(-EPSILON, from.y, to.y), + z: lerp(-EPSILON, from.z, to.z), + }; + + let mut current_block = BlockPos::from(right_before_start); + if let Some(data) = get_hit_result(&context, ¤t_block) { + return data; + } + + let vec = right_after_end - right_before_start; + + /// 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. + fn get_number_sign(num: f64) -> f64 { + if num == 0. { + 0. + } else { + num.signum() + } + } + + let vec_sign = Vec3 { + x: get_number_sign(vec.x), + y: get_number_sign(vec.y), + z: get_number_sign(vec.z), + }; + + #[rustfmt::skip] + let percentage_step = Vec3 { + x: if vec_sign.x == 0. { f64::MAX } else { vec_sign.x / vec.x }, + y: if vec_sign.y == 0. { f64::MAX } else { vec_sign.y / vec.y }, + z: if vec_sign.z == 0. { f64::MAX } else { vec_sign.z / vec.z }, + }; + + let mut percentage = Vec3 { + x: percentage_step.x + * if vec_sign.x > 0. { + 1. - right_before_start.x.fract() + } else { + right_before_start.x.fract().abs() + }, + y: percentage_step.y + * if vec_sign.y > 0. { + 1. - right_before_start.y.fract() + } else { + right_before_start.y.fract().abs() + }, + z: percentage_step.z + * if vec_sign.z > 0. { + 1. - right_before_start.z.fract() + } else { + right_before_start.z.fract().abs() + }, + }; + + loop { + if percentage.x > 1. && percentage.y > 1. && percentage.z > 1. { + return get_miss_result(&context); + } + + if percentage.x < percentage.y { + if percentage.x < percentage.z { + current_block.x += vec_sign.x as i32; + percentage.x += percentage_step.x; + } else { + current_block.z += vec_sign.z as i32; + percentage.z += percentage_step.z; + } + } else if percentage.y < percentage.z { + current_block.y += vec_sign.y as i32; + percentage.y += percentage_step.y; + } else { + current_block.z += vec_sign.z as i32; + percentage.z += percentage_step.z; + } + + if let Some(data) = get_hit_result(&context, ¤t_block) { + return data; + } + } +} diff --git a/azalea-physics/src/collision/discrete_voxel_shape.rs b/azalea-physics/src/collision/discrete_voxel_shape.rs index 4a329398..2bcd1f61 100755 --- a/azalea-physics/src/collision/discrete_voxel_shape.rs +++ b/azalea-physics/src/collision/discrete_voxel_shape.rs @@ -45,6 +45,7 @@ impl DiscreteVoxelShape { return false; } let (x, y, z) = (x as u32, y as u32, z as u32); + (x < self.size(Axis::X) && y < self.size(Axis::Y) && z < self.size(Axis::Z)) && (self.is_full(x, y, z)) } diff --git a/azalea-physics/src/collision/mod.rs b/azalea-physics/src/collision/mod.rs index 53efd2fe..a99b5710 100644 --- a/azalea-physics/src/collision/mod.rs +++ b/azalea-physics/src/collision/mod.rs @@ -5,10 +5,7 @@ mod shape; mod world_collisions; use azalea_core::{Axis, Vec3, AABB, EPSILON}; -use azalea_world::{ - entity::{self}, - Instance, MoveEntityError, -}; +use azalea_world::{entity, Instance, MoveEntityError}; pub use blocks::BlockWithShape; pub use discrete_voxel_shape::*; pub use shape::*; @@ -219,7 +216,11 @@ fn collide_with_shapes( if y_movement != 0. { y_movement = Shapes::collide(&Axis::Y, &entity_box, collision_boxes, y_movement); if y_movement != 0. { - entity_box = entity_box.move_relative(0., y_movement, 0.); + entity_box = entity_box.move_relative(&Vec3 { + x: 0., + y: y_movement, + z: 0., + }); } } @@ -230,14 +231,22 @@ fn collide_with_shapes( if more_z_movement && z_movement != 0. { z_movement = Shapes::collide(&Axis::Z, &entity_box, collision_boxes, z_movement); if z_movement != 0. { - entity_box = entity_box.move_relative(0., 0., z_movement); + entity_box = entity_box.move_relative(&Vec3 { + x: 0., + y: 0., + z: z_movement, + }); } } if x_movement != 0. { x_movement = Shapes::collide(&Axis::X, &entity_box, collision_boxes, x_movement); if x_movement != 0. { - entity_box = entity_box.move_relative(x_movement, 0., 0.); + entity_box = entity_box.move_relative(&Vec3 { + x: x_movement, + y: 0., + z: 0., + }); } } diff --git a/azalea-physics/src/collision/shape.rs b/azalea-physics/src/collision/shape.rs index cc184591..29c1b440 100755 --- a/azalea-physics/src/collision/shape.rs +++ b/azalea-physics/src/collision/shape.rs @@ -1,9 +1,11 @@ use super::mergers::IndexMerger; use crate::collision::{BitSetDiscreteVoxelShape, DiscreteVoxelShape, AABB}; -use azalea_core::{binary_search, Axis, AxisCycle, EPSILON}; +use azalea_core::{ + binary_search, Axis, AxisCycle, BlockHitResult, BlockPos, Direction, Vec3, EPSILON, +}; use std::{cmp, num::NonZeroU32}; -pub struct Shapes {} +pub struct Shapes; pub fn block_shape() -> VoxelShape { let mut shape = BitSetDiscreteVoxelShape::new(1, 1, 1); @@ -390,6 +392,33 @@ impl VoxelShape { } } + pub fn clip(&self, from: &Vec3, to: &Vec3, block_pos: &BlockPos) -> Option<BlockHitResult> { + if self.is_empty() { + return None; + } + let vector = to - from; + if vector.length_sqr() < EPSILON { + return None; + } + let right_after_start = from + &(vector * 0.0001); + + if self.shape().is_full_wide( + self.find_index(Axis::X, right_after_start.x - block_pos.x as f64), + self.find_index(Axis::Y, right_after_start.y - block_pos.y as f64), + self.find_index(Axis::Z, right_after_start.z - block_pos.z as f64), + ) { + Some(BlockHitResult { + block_pos: *block_pos, + direction: Direction::nearest(vector).opposite(), + location: right_after_start, + inside: true, + miss: false, + }) + } else { + AABB::clip_iterable(&self.to_aabbs(), from, to, block_pos) + } + } + pub fn collide(&self, axis: &Axis, entity_box: &AABB, movement: f64) -> f64 { self.collide_x(AxisCycle::between(*axis, Axis::X), entity_box, movement) } @@ -531,19 +560,34 @@ impl VoxelShape { let y_coords = self.get_coords(Axis::Y); let z_coords = self.get_coords(Axis::Z); self.shape().for_all_boxes( - |var4x, var5, var6, var7, var8, var9| { + |min_x, min_y, min_z, max_x, max_y, max_z| { consumer( - x_coords[var4x as usize], - y_coords[var5 as usize], - z_coords[var6 as usize], - x_coords[var7 as usize], - y_coords[var8 as usize], - z_coords[var9 as usize], + x_coords[min_x as usize], + y_coords[min_y as usize], + z_coords[min_z as usize], + x_coords[max_x as usize], + y_coords[max_y as usize], + z_coords[max_z as usize], ); }, true, ); } + + pub fn to_aabbs(&self) -> Vec<AABB> { + let mut aabbs = Vec::new(); + self.for_all_boxes(|min_x, min_y, min_z, max_x, max_y, max_z| { + aabbs.push(AABB { + min_x, + min_y, + min_z, + max_x, + max_y, + max_z, + }); + }); + aabbs + } } impl From<AABB> for VoxelShape { diff --git a/azalea-physics/src/lib.rs b/azalea-physics/src/lib.rs index 049091f7..57c2100e 100644 --- a/azalea-physics/src/lib.rs +++ b/azalea-physics/src/lib.rs @@ -1,14 +1,15 @@ #![doc = include_str!("../README.md")] #![feature(trait_alias)] +pub mod clip; pub mod collision; use azalea_block::{Block, BlockState}; use azalea_core::{BlockPos, Vec3}; use azalea_world::{ entity::{ - metadata::Sprinting, move_relative, Attributes, Jumping, Local, Physics, Position, - WorldName, + clamp_look_direction, metadata::Sprinting, move_relative, Attributes, Jumping, Local, + LookDirection, Physics, Position, WorldName, }, Instance, InstanceContainer, }; @@ -30,7 +31,11 @@ pub struct PhysicsPlugin; impl Plugin for PhysicsPlugin { fn build(&self, app: &mut App) { app.add_event::<ForceJumpEvent>() - .add_system(force_jump_listener.before(azalea_world::entity::update_bounding_box)) + .add_system( + force_jump_listener + .before(azalea_world::entity::update_bounding_box) + .after(clamp_look_direction), + ) .add_systems( (ai_step, travel) .chain() @@ -43,11 +48,20 @@ impl Plugin for PhysicsPlugin { /// Move the entity with the given acceleration while handling friction, /// gravity, collisions, and some other stuff. fn travel( - mut query: Query<(&mut Physics, &mut Position, &Attributes, &WorldName), With<Local>>, - world_container: Res<InstanceContainer>, + mut query: Query< + ( + &mut Physics, + &mut LookDirection, + &mut Position, + &Attributes, + &WorldName, + ), + With<Local>, + >, + instance_container: Res<InstanceContainer>, ) { - for (mut physics, mut position, attributes, world_name) in &mut query { - let world_lock = world_container + for (mut physics, direction, mut position, attributes, world_name) in &mut query { + let world_lock = instance_container .get(world_name) .expect("All entities should be in a valid world"); let world = world_lock.read(); @@ -85,6 +99,7 @@ fn travel( block_friction, &world, &mut physics, + &direction, &mut position, attributes, ); @@ -158,13 +173,21 @@ pub fn ai_step( pub struct ForceJumpEvent(pub Entity); pub fn force_jump_listener( - mut query: Query<(&mut Physics, &Position, &Sprinting, &WorldName)>, - world_container: Res<InstanceContainer>, + mut query: Query<( + &mut Physics, + &Position, + &LookDirection, + &Sprinting, + &WorldName, + )>, + instance_container: Res<InstanceContainer>, mut events: EventReader<ForceJumpEvent>, ) { for event in events.iter() { - if let Ok((mut physics, position, sprinting, world_name)) = query.get_mut(event.0) { - let world_lock = world_container + if let Ok((mut physics, position, direction, sprinting, world_name)) = + query.get_mut(event.0) + { + let world_lock = instance_container .get(world_name) .expect("All entities should be in a valid world"); let world = world_lock.read(); @@ -178,7 +201,7 @@ pub fn force_jump_listener( }; if **sprinting { // sprint jumping gives some extra velocity - let y_rot = physics.y_rot * 0.017453292; + let y_rot = direction.y_rot * 0.017453292; physics.delta += Vec3 { x: (-f32::sin(y_rot) * 0.2) as f64, y: 0., @@ -204,11 +227,13 @@ fn handle_relative_friction_and_calculate_movement( block_friction: f32, world: &Instance, physics: &mut Physics, + direction: &LookDirection, position: &mut Position, attributes: &Attributes, ) -> Vec3 { move_relative( physics, + direction, get_friction_influenced_speed(physics, attributes, block_friction), &Vec3 { x: physics.xxa as f64, |
