From 8af2b11970547345ae4022418e06d7add0563501 Mon Sep 17 00:00:00 2001 From: Caelum van Ispelen Date: Wed, 28 Aug 2019 18:10:49 -0600 Subject: [PATCH 1/7] Begin fixing entity jittering --- core/src/world/mod.rs | 11 +++ server/src/physics/entity.rs | 30 +++---- server/src/physics/math.rs | 151 ++++++++++++++++------------------- 3 files changed, 93 insertions(+), 99 deletions(-) diff --git a/core/src/world/mod.rs b/core/src/world/mod.rs index 8fb73a76f..e6c78df99 100644 --- a/core/src/world/mod.rs +++ b/core/src/world/mod.rs @@ -97,6 +97,17 @@ impl Add for Position { } } +impl Add for Position { + type Output = Position; + + fn add(mut self, rhs: DVec3) -> Self::Output { + self.x += rhs.x; + self.y += rhs.y; + self.z += rhs.z; + self + } +} + impl Add for Position { type Output = Position; diff --git a/server/src/physics/entity.rs b/server/src/physics/entity.rs index 9966cb498..ee63d5500 100644 --- a/server/src/physics/entity.rs +++ b/server/src/physics/entity.rs @@ -7,7 +7,7 @@ use feather_core::world::ChunkMap; use crate::entity::{EntityType, PositionComponent, VelocityComponent}; use crate::physics::{ - bbox_front, block_impacted_by_ray, blocks_intersecting_bbox, BlockFace, BoundingBoxComponent, + bbox_front, block_impacted_by_ray, blocks_intersecting_bbox, Side, BoundingBoxComponent, }; /// System for updating all entities' positions and velocities @@ -47,22 +47,14 @@ impl<'a> System<'a> for EntityPhysicsSystem { { let mut velocity = restrict_velocity.get_unchecked().clone(); - // Check for blocks around the bbox. - let blocks_around_bbox = - blocks_intersecting_bbox(&chunk_map, position.current, bounding_box); - // Set velocity to 0 where there are blocks - velocity.0.x *= blocks_around_bbox.x; - velocity.0.y *= blocks_around_bbox.y; - velocity.0.z *= blocks_around_bbox.z; - - if blocks_around_bbox.y == 0.0 { - position.current.on_ground = true; - } else { - position.current.on_ground = false; - } - let mut pending_position = position.current + velocity.0; + // Check for blocks around the bbox and apply offset + // to position to stop the bbox from intersecting blocks. + let intersect = + blocks_intersecting_bbox(&chunk_map, position.current, bounding_box); + intersect.apply_to(&mut pending_position); + // Check for blocks along path between old position and pending position. // This prevents entities from flying through blocks when their // velocity is sufficiently high. @@ -81,19 +73,19 @@ impl<'a> System<'a> for EntityPhysicsSystem { let face = impacted.face; let impact = impacted.pos; - if face.contains(BlockFace::EAST) || face.contains(BlockFace::WEST) { + if face.contains(Side::EAST) || face.contains(Side::WEST) { velocity.x = 0.0; pending_position.x = impact.x + bounding_box.size().x * face.as_vector().x; } - if face.contains(BlockFace::NORTH) || face.contains(BlockFace::SOUTH) { + if face.contains(Side::NORTH) || face.contains(Side::SOUTH) { velocity.z = 0.0; pending_position.z = impact.z + bounding_box.size().z * face.as_vector().z; } - if face.contains(BlockFace::TOP) || face.contains(BlockFace::BOTTOM) { + if face.contains(Side::TOP) || face.contains(Side::BOTTOM) { velocity.y = 0.0; pending_position.y = impact.y + bounding_box.size().y * face.as_vector().y; } - if face.contains(BlockFace::TOP) { + if face.contains(Side::TOP) { pending_position.on_ground = true; } } diff --git a/server/src/physics/math.rs b/server/src/physics/math.rs index 2c9f30198..e36bc7024 100644 --- a/server/src/physics/math.rs +++ b/server/src/physics/math.rs @@ -6,7 +6,7 @@ use crate::physics::BoundingBoxComponent; use feather_core::world::{BlockPosition, ChunkMap, Position}; use feather_core::{BlockExt, ChunkPosition}; use glm::{vec3, DVec3, Vec3}; -use nalgebra::{Isometry3, Point3}; +use nalgebra::{Isometry3, Point3, Isometry2, RealField}; use ncollide3d::bounding_volume::AABB; use ncollide3d::query::{Ray, RayCast}; use ncollide3d::shape::Cuboid; @@ -18,7 +18,7 @@ use std::f32::INFINITY; // TODO is a bitflag really the most // idiomatic way to do this? bitflags! { - /// A face of a block. + /// A side. /// /// * East is on the positive X side. /// * West is on the negative X side. @@ -26,7 +26,7 @@ bitflags! { /// * South is on the positive Z side. /// * Top is on the positive Y side. /// * Bottom is on the negative Y side. - pub struct BlockFace: u8 { + pub struct Side: u8 { const EAST = 0x01; const WEST = 0x02; const NORTH = 0x04; @@ -37,7 +37,7 @@ bitflags! { } } -impl BlockFace { +impl Side { /// Returns a vector with coordinates set to 1.0 /// where the face is toward the positive axis /// and to -1.0 where the face is toward the negative @@ -45,21 +45,21 @@ impl BlockFace { pub fn as_vector(self) -> DVec3 { let mut vector = glm::vec3(0.0, 0.0, 0.0); - if self.contains(BlockFace::EAST) { + if self.contains(Side::EAST) { vector.x = 1.0; - } else if self.contains(BlockFace::WEST) { + } else if self.contains(Side::WEST) { vector.x = -1.0; } - if self.contains(BlockFace::NORTH) { + if self.contains(Side::NORTH) { vector.z = 1.0; - } else if self.contains(BlockFace::SOUTH) { + } else if self.contains(Side::SOUTH) { vector.z = -1.0; } - if self.contains(BlockFace::TOP) { + if self.contains(Side::TOP) { vector.y = 1.0; - } else if self.contains(BlockFace::BOTTOM) { + } else if self.contains(Side::BOTTOM) { vector.y = -1.0; } @@ -76,7 +76,7 @@ pub struct RayImpact { /// which the ray met the block. pub pos: Position, /// The face(s) of the block where the ray impacted. - pub face: BlockFace, + pub face: Side, } /// Finds the first block impacted by the given ray. @@ -115,7 +115,7 @@ pub fn block_impacted_by_ray( // handle when a ray hits multiple faces. // In practice, this should not be an issue, // but it may causes subtle issues in the future. - let mut face = BlockFace::NONE; + let mut face = Side::NONE; if direction.x > 0.0 { step.x = 1; @@ -180,18 +180,18 @@ pub fn block_impacted_by_ray( current_pos.x += step.x; dist_traveled.x += 1.0; face = if step.x == 1 { - BlockFace::WEST + Side::WEST } else { - BlockFace::EAST + Side::EAST } } else { next.z += delta.z; current_pos.z += step.z; dist_traveled.z += 1.0; face = if step.z == 1 { - BlockFace::SOUTH + Side::SOUTH } else { - BlockFace::NORTH + Side::NORTH } } } else if next.y < next.z { @@ -199,18 +199,18 @@ pub fn block_impacted_by_ray( current_pos.y += step.y; dist_traveled.y += 1.0; face = if step.y == 1 { - BlockFace::BOTTOM + Side::BOTTOM } else { - BlockFace::TOP + Side::TOP } } else { next.z += delta.z; current_pos.z += step.z; dist_traveled.z += 1.0; face = if step.z == 1 { - BlockFace::SOUTH + Side::SOUTH } else { - BlockFace::NORTH + Side::NORTH } } } @@ -260,47 +260,67 @@ where result } -/// Returns a vector containing `1.0` for each axis where -/// there are no blocks intersecting the bounding box and `0.0` for -/// where there are. -/// -/// NOTE: This implementation only covers the most basic cases. -/// It does not correctly work when the bounding box is larger -/// than one block in any length. +/// The offsets which need to be applied to a position +/// to prevent it from intersecting with a block. +#[derive(Debug, Clone)] +pub struct BlockIntersect { + offset: DVec3, +} + +impl BlockIntersect { + /// Applies this offset to the given position. + pub fn apply_to(&self, pos: &mut Position) { + pos.x += self.offset.x; + pos.y += self.offset.y; + pos.z += self.offset.z; + } +} + +/// Returns a struct containing position offsets which +/// must be applied to prevent blocks from intersecting +/// the bounding box. Call `BlockIntersect::apply` to +/// apply the offsets to a position. pub fn blocks_intersecting_bbox( chunk_map: &ChunkMap, pos: Position, bbox: &BoundingBoxComponent, -) -> Vec3 { +) -> BlockIntersect { let bbox_size = bbox.size(); - let mut result = vec3(1.0, 1.0, 1.0); + let mut result = BlockIntersect { + offset: vec3(0.0, 0.0, 0.0), + }; let offsets = [ - vec3(bbox_size.x as f32, 0.0, 0.0), - vec3(-bbox_size.x as f32, 0.0, 0.0), - vec3(0.0, bbox_size.y as f32, 0.0), - vec3(0.0, -bbox_size.y as f32, 0.0), - vec3(0.0, 0.0, bbox_size.z as f32), - vec3(0.0, 0.0, -bbox_size.z as f32), - ]; - let masks = [ - vec3(0.0f32, 1.0, 1.0), - vec3(0.0, 1.0, 1.0), - vec3(1.0, 0.0, 1.0), - vec3(1.0, 0.0, 1.0), - vec3(1.0, 1.0, 0.0), - vec3(1.0, 1.0, 0.0), + vec3(bbox_size.x, 0.0, 0.0), + vec3(-bbox_size.x, 0.0, 0.0), + vec3(0.0, bbox_size.y, 0.0), + vec3(0.0, -bbox_size.y, 0.0), + vec3(0.0, 0.0, bbox_size.z), + vec3(0.0, 0.0, -bbox_size.z), ]; - for (offset, mask) in offsets.iter().zip(masks.iter()) { + + for offset in &offsets { let block_pos = (pos + *offset).block_pos(); if let Some(block) = chunk_map.block_at(block_pos) { if block.is_solid() { - result.x *= mask.x; - result.y *= mask.y; - result.z *= mask.z; + // Calculate the offset which needs to be applied to the position + // along this axis. + // This is done by checking for contact points and then retrieving + // the penetration depth. + let block = block_shape(); + let contact = ncollide3d::query::contact::( + &Isometry3::new(block_pos.world_pos().into(), vec3(0.0, 0.0, 0.0)), + &block, + &pos.into(), + &bbox, + 1.0, + ).unwrap(); // Okay because we already know the block collides with the bbox + + let offset = contact.normal.into_inner() * contact.depth; + result.offset += offset; } } } @@ -309,8 +329,8 @@ pub fn blocks_intersecting_bbox( } /// Returns an `ncollide` `Cuboid` corresponding to a block. -pub fn block_shape() -> Cuboid { - Cuboid::new(vec3(0.5, 0.5, 0.5)) +pub fn block_shape() -> Cuboid { + Cuboid::new(vec3(N::one(), 0.5, 0.5)) } /// Returns an `Isometry` representing a block's translation. @@ -426,7 +446,7 @@ mod tests { Some(RayImpact { block: BlockPosition::new(0, 64, 0), pos: position!(0.0, 65.0, 0.0), - face: BlockFace::TOP, + face: Side::TOP, }) ); @@ -448,7 +468,7 @@ mod tests { Some(RayImpact { block: BlockPosition::new(1, 65, 1), pos: position!(1.0, 65.0, 1.0), - face: BlockFace::WEST, // This should be three faces—see the TODO above + face: Side::WEST, // This should be three faces—see the TODO above }) ); } @@ -569,35 +589,6 @@ mod tests { chunks_within_distance(pos, distance); } - #[test] - fn test_blocks_intersecting_bbox() { - let chunk_map = chunk_map(); - - assert_eq!( - blocks_intersecting_bbox( - &chunk_map, - position!(0.0, 32.0, 0.0), - &BoundingBoxComponent(AABB::new( - Point3::from(vec3(0.0, 0.0, 0.0)), - Point3::from(vec3(0.5, 0.5, 0.5)) - )), - ), - vec3(0.0, 0.0, 0.0) - ); - - assert_eq!( - blocks_intersecting_bbox( - &chunk_map, - position!(0.0, 65.0, 0.0), - &BoundingBoxComponent(AABB::new( - Point3::from(vec3(0.0, 0.0, 0.0)), - Point3::from(vec3(0.5, 1.5, 0.5)) - )), - ), - vec3(1.0, 0.0, 1.0) - ); - } - #[test] fn test_bbox_front() { let bbox = AABB::new(Point3::from([0.0, 0.0, 0.0]), Point3::from([1.0, 2.0, 3.0])); From cc762ac1f4619a2215028fe76f3f3fc10ea8c4c0 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 30 Aug 2019 17:21:48 -0600 Subject: [PATCH 2/7] Make progress on fixing physics --- Cargo.lock | 82 ++++++++++++++++++++ core/Cargo.toml | 4 +- core/src/lib.rs | 2 + core/src/world/mod.rs | 19 ++++- server/Cargo.toml | 1 + server/src/entity/item.rs | 2 +- server/src/physics/entity.rs | 25 +++++- server/src/physics/math.rs | 142 +++++++++++++++++++++++++++-------- 8 files changed, 237 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f81b07448..4e797af7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,6 +90,15 @@ dependencies = [ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "as-slice" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "atom" version = "0.3.5" @@ -616,6 +625,8 @@ dependencies = [ "feather_codegen 0.3.0", "feather_items 0.1.0", "flate2 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "hash32 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hash32-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "hashbrown 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "hematite-nbt 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -681,6 +692,7 @@ dependencies = [ "feather_item_block 0.1.0", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "hashbrown 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "heapless 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "hematite-nbt 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -794,6 +806,14 @@ dependencies = [ "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "generic-array" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "getrandom" version = "0.1.8" @@ -820,6 +840,24 @@ dependencies = [ "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hash32" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hash32-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "hashbrown" version = "0.5.0" @@ -836,6 +874,16 @@ dependencies = [ "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "heapless" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "as-slice 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hash32 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "heck" version = "0.3.1" @@ -1448,6 +1496,14 @@ dependencies = [ "syn 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "proc-macro2" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "proc-macro2" version = "0.4.30" @@ -1476,6 +1532,14 @@ dependencies = [ "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "quote" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "quote" version = "0.6.13" @@ -2026,6 +2090,16 @@ name = "subtle" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "syn" +version = "0.13.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "0.15.43" @@ -2456,6 +2530,7 @@ dependencies = [ "checksum approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08abcc3b4e9339e33a3d0a5ed15d84a687350c05689d825e0f6655eef9e76a94" "checksum approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" "checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" +"checksum as-slice 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "293dac66b274fab06f95e7efb05ec439a6b70136081ea522d270bc351ae5bb27" "checksum atom 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3c86699c3f02778ec07158376991c8f783dd1f2f95c579ffaf0738dc984b2fe2" "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" "checksum autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "22130e92352b948e7e82a49cdb0aa94f2211761117f29e052dd397c1ac33542b" @@ -2520,10 +2595,14 @@ dependencies = [ "checksum futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "45dc39533a6cae6da2b56da48edae506bb767ec07370f86f70fc062e9d435869" "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +"checksum generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd" "checksum getrandom 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "34f33de6f0ae7c9cb5e574502a562e2b512799e32abb801cd1e79ad952b62b49" "checksum h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" +"checksum hash32 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "12d790435639c06a7b798af9e1e331ae245b7ef915b92f70a39b4cf8c00686af" +"checksum hash32-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebc0efbd154a17cddc3616d83faef479c0076d871a2143c157b310cc7ca799a2" "checksum hashbrown 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1de41fb8dba9714efd92241565cdff73f78508c95697dd56787d3cba27e2353" "checksum hashbrown 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2bcea5b597dd98e6d1f1ec171744cc5dee1a30d1c23c5b98e3cf9d4fbdf8a526" +"checksum heapless 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c6c7ce2e47016f34d17acbf2fe5f9e0337ea59d2ab8ceecd9405b2336ffaca9b" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum hematite-nbt 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "98b407a33bb1715a4cf0276edfe8df52352c55b2a3703c5079adedf398b92932" "checksum hibitset 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "47e7292fd9f7fe89fa35c98048f2d0a69b79ed243604234d18f6f8a1aa6f408d" @@ -2590,9 +2669,11 @@ dependencies = [ "checksum pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c1d2cfa5a714db3b5f24f0915e74fcdf91d09d496ba61329705dda7774d2af" "checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" "checksum proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e688f31d92ffd7c1ddc57a1b4e6d773c0f2a14ee437a4b0a4f5a69c80eb221c8" +"checksum proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1b06e2f335f48d24442b35a19df506a835fb3547bc3c06ef27340da9acf5cae7" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c5c2380ae88876faae57698be9e9775e3544decad214599c3a6266cca6ac802" "checksum publicsuffix 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5afecba86dcf1e4fd610246f89899d1924fe12e1e89f555eb7c7f710f3c5ad1d" +"checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" @@ -2655,6 +2736,7 @@ dependencies = [ "checksum strum 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e5d1c33039533f051704951680f1adfd468fd37ac46816ded0d9ee068e60f05f" "checksum strum_macros 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "47cd23f5c7dee395a00fa20135e2ec0fffcdfa151c56182966d7a3261343432e" "checksum subtle 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "01f40907d9ffc762709e4ff3eb4a6f6b41b650375a3f09ac92b641942b7fb082" +"checksum syn 0.13.11 (registry+https://github.com/rust-lang/crates.io-index)" = "14f9bf6292f3a61d2c716723fdb789a41bbe104168e6f496dc6497e531ea1b9b" "checksum syn 0.15.43 (registry+https://github.com/rust-lang/crates.io-index)" = "ee06ea4b620ab59a2267c6b48be16244a3389f8bfa0986bdd15c35b890b00af3" "checksum syn 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "158521e6f544e7e3dcfc370ac180794aa38cb34a1b1e07609376d4adcf429b93" "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" diff --git a/core/Cargo.toml b/core/Cargo.toml index d2af71da5..8956619a7 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -25,4 +25,6 @@ hematite-nbt = "0.4.1" byteorder = "1.3.2" nalgebra-glm = "0.4.0" derive_more = "0.15.0" -smallvec = "0.6.10" \ No newline at end of file +smallvec = "0.6.10" +hash32 = "0.1.0" +hash32-derive = "0.1.0" \ No newline at end of file diff --git a/core/src/lib.rs b/core/src/lib.rs index bc3024641..5ce13707e 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -12,6 +12,8 @@ extern crate feather_codegen; extern crate num_derive; #[macro_use] extern crate smallvec; +#[macro_use] +extern crate hash32_derive; extern crate nalgebra_glm as glm; diff --git a/core/src/world/mod.rs b/core/src/world/mod.rs index e6c78df99..e1ff2a9c4 100644 --- a/core/src/world/mod.rs +++ b/core/src/world/mod.rs @@ -84,6 +84,10 @@ impl Position { glm::vec3(x, y, z) } + + pub fn as_vec(&self) -> DVec3 { + (*self).into() + } } impl Add for Position { @@ -132,6 +136,17 @@ impl Sub for Position { } } +impl Sub for Position { + type Output = Position; + + fn sub(mut self, vec: DVec3) -> Self::Output { + self.x -= vec.x; + self.y -= vec.y; + self.z -= vec.z; + self + } +} + impl Sub for Position { type Output = Position; @@ -181,13 +196,13 @@ fn square(x: f64) -> f64 { x * x } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, new)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Hash32, Default, new)] pub struct ChunkPosition { pub x: i32, pub z: i32, } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, new)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Hash32, Default, new)] pub struct BlockPosition { pub x: i32, pub y: i32, diff --git a/server/Cargo.toml b/server/Cargo.toml index 5921aa393..fed7cf558 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -52,6 +52,7 @@ base64 = "0.10.1" bumpalo = "2.6.0" thread_local = "0.3.6" parking_lot = "0.9.0" +heapless = "0.5.0" [features] nightly = ["specs/nightly", "parking_lot/nightly"] \ No newline at end of file diff --git a/server/src/entity/item.rs b/server/src/entity/item.rs index efb50b853..71e50cf35 100644 --- a/server/src/entity/item.rs +++ b/server/src/entity/item.rs @@ -59,7 +59,7 @@ impl<'a> System<'a> for ItemSpawnSystem { let mut pos = { let player_pos = positions.get(event.player).unwrap().current + glm::vec3(0.0, PLAYER_EYE_HEIGHT, 0.0); - player_pos - glm::vec3(0.0, 0.3, 0.0) + player_pos - glm::vec3(0.0f64, 0.3, 0.0) }; pos.on_ground = false; diff --git a/server/src/physics/entity.rs b/server/src/physics/entity.rs index ee63d5500..32db44db2 100644 --- a/server/src/physics/entity.rs +++ b/server/src/physics/entity.rs @@ -7,7 +7,7 @@ use feather_core::world::ChunkMap; use crate::entity::{EntityType, PositionComponent, VelocityComponent}; use crate::physics::{ - bbox_front, block_impacted_by_ray, blocks_intersecting_bbox, Side, BoundingBoxComponent, + bbox_front, block_impacted_by_ray, blocks_intersecting_bbox, BoundingBoxComponent, Side, }; /// System for updating all entities' positions and velocities @@ -51,10 +51,29 @@ impl<'a> System<'a> for EntityPhysicsSystem { // Check for blocks around the bbox and apply offset // to position to stop the bbox from intersecting blocks. - let intersect = - blocks_intersecting_bbox(&chunk_map, position.current, bounding_box); + let intersect = blocks_intersecting_bbox( + &chunk_map, + position.current, + pending_position, + bounding_box, + ); intersect.apply_to(&mut pending_position); + if intersect.x_affected() { + velocity.x = 0.0; + } + + if intersect.y_affected() { + velocity.y = 0.0; + pending_position.on_ground = true; + } else { + pending_position.on_ground = false; + } + + if intersect.z_affected() { + velocity.z = 0.0; + } + // Check for blocks along path between old position and pending position. // This prevents entities from flying through blocks when their // velocity is sufficiently high. diff --git a/server/src/physics/math.rs b/server/src/physics/math.rs index e36bc7024..dd2692855 100644 --- a/server/src/physics/math.rs +++ b/server/src/physics/math.rs @@ -6,8 +6,10 @@ use crate::physics::BoundingBoxComponent; use feather_core::world::{BlockPosition, ChunkMap, Position}; use feather_core::{BlockExt, ChunkPosition}; use glm::{vec3, DVec3, Vec3}; -use nalgebra::{Isometry3, Point3, Isometry2, RealField}; +use heapless::consts::U8; +use nalgebra::{Isometry3, Point3}; use ncollide3d::bounding_volume::AABB; +use ncollide3d::query; use ncollide3d::query::{Ray, RayCast}; use ncollide3d::shape::Cuboid; use smallvec::SmallVec; @@ -179,11 +181,7 @@ pub fn block_impacted_by_ray( next.x += delta.x; current_pos.x += step.x; dist_traveled.x += 1.0; - face = if step.x == 1 { - Side::WEST - } else { - Side::EAST - } + face = if step.x == 1 { Side::WEST } else { Side::EAST } } else { next.z += delta.z; current_pos.z += step.z; @@ -198,11 +196,7 @@ pub fn block_impacted_by_ray( next.y += delta.y; current_pos.y += step.y; dist_traveled.y += 1.0; - face = if step.y == 1 { - Side::BOTTOM - } else { - Side::TOP - } + face = if step.y == 1 { Side::BOTTOM } else { Side::TOP } } else { next.z += delta.z; current_pos.z += step.z; @@ -265,6 +259,9 @@ where #[derive(Debug, Clone)] pub struct BlockIntersect { offset: DVec3, + x: bool, + y: bool, + z: bool, } impl BlockIntersect { @@ -274,24 +271,56 @@ impl BlockIntersect { pos.y += self.offset.y; pos.z += self.offset.z; } + + /// Returns whether the X axis is affected. + pub fn x_affected(&self) -> bool { + self.x + } + + /// Returns whether the Y axis is affected. + pub fn y_affected(&self) -> bool { + self.y + } + + /// Returns whether the Z axis is affected. + pub fn z_affected(&self) -> bool { + self.z + } } /// Returns a struct containing position offsets which /// must be applied to prevent blocks from intersecting /// the bounding box. Call `BlockIntersect::apply` to /// apply the offsets to a position. +/// +/// `prev` should be the entity's position on the previous +/// tick. This is used to calculate impact points. +/// +/// # Restrictions +/// Currently, bounding boxes with side lengths greater +/// than 1 are not supported. If the bounding box's size +/// is more than 1, this function will panic. pub fn blocks_intersecting_bbox( chunk_map: &ChunkMap, - pos: Position, + from: Position, + mut dest: Position, bbox: &BoundingBoxComponent, ) -> BlockIntersect { let bbox_size = bbox.size(); + assert!(bbox_size.x <= 1.0); + assert!(bbox_size.y <= 1.0); + assert!(bbox_size.z <= 1.0); + let mut result = BlockIntersect { offset: vec3(0.0, 0.0, 0.0), + x: false, + y: false, + z: false, }; let offsets = [ + vec3(0.0, 0.0, 0.0), vec3(bbox_size.x, 0.0, 0.0), vec3(-bbox_size.x, 0.0, 0.0), vec3(0.0, bbox_size.y, 0.0), @@ -300,37 +329,73 @@ pub fn blocks_intersecting_bbox( vec3(0.0, 0.0, -bbox_size.z), ]; + // Compute a vector of isometries representing adjacent block locations. + let mut blocks: SmallVec<[Isometry3; 16]> = smallvec![]; + + // Prevent same block being checked twice. + let mut checked = heapless::FnvIndexSet::::new(); for offset in &offsets { - let block_pos = (pos + *offset).block_pos(); + let block_pos = (dest + *offset).block_pos(); - if let Some(block) = chunk_map.block_at(block_pos) { - if block.is_solid() { - // Calculate the offset which needs to be applied to the position - // along this axis. - // This is done by checking for contact points and then retrieving - // the penetration depth. - let block = block_shape(); - let contact = ncollide3d::query::contact::( - &Isometry3::new(block_pos.world_pos().into(), vec3(0.0, 0.0, 0.0)), - &block, - &pos.into(), - &bbox, - 1.0, - ).unwrap(); // Okay because we already know the block collides with the bbox - - let offset = contact.normal.into_inner() * contact.depth; - result.offset += offset; - } + if checked.contains(&block_pos) { + continue; // Already added this block + } + + checked.insert(block_pos).unwrap(); // Unwrap is safe because set has capacity 8 and there are at most 7 blocks + + let block = match chunk_map.block_at(block_pos) { + Some(block) => block, + None => continue, // Unloaded chunk + }; + if !block.is_solid() { + continue; // Not a solid b lock } + + let isometry = block_isometry_64(block_pos); + blocks.push(isometry); + } + + // Go through blocks and check for time of impact from original + // position to the block. If the time of impact is <= 1, the entity + // has collided with the block; update the position accordingly. + let block_shape = block_shape_64(); + let velocity = (dest - from).as_vec(); + let bbox_shape = bbox_to_cuboid(&bbox.0); + + for block_isometry in blocks { + let toi = match query::time_of_impact( + &block_isometry, + &vec3(0.0, 0.0, 0.0), + &block_shape, + &Isometry3::new(from.as_vec(), vec3(0.0, 0.0, 0.0)), + &velocity, + &bbox_shape, + 1.0, + 0.0, + ) { + Some(toi) => toi, + None => continue, // No impact + }; + + let world_pos = from + velocity * toi.toi; + let absolute_offset = world_pos - dest; + + result.offset += absolute_offset.as_vec(); + + dest = dest + absolute_offset; } result } /// Returns an `ncollide` `Cuboid` corresponding to a block. -pub fn block_shape() -> Cuboid { - Cuboid::new(vec3(N::one(), 0.5, 0.5)) +pub fn block_shape() -> Cuboid { + Cuboid::new(vec3(0.5, 0.5, 0.5)) +} + +pub fn block_shape_64() -> Cuboid { + Cuboid::new(vec3(0.5, 0.5, 0.5)) } /// Returns an `Isometry` representing a block's translation. @@ -341,6 +406,17 @@ pub fn block_isometry(pos: BlockPosition) -> Isometry3 { ) } +pub fn block_isometry_64(pos: BlockPosition) -> Isometry3 { + Isometry3::new( + vec3( + f64::from(pos.x) + 0.5, + f64::from(pos.y) + 0.5, + f64::from(pos.z) + 0.5, + ), + vec3(0.0, 0.0, 0.0), + ) +} + /// Finds all chunks within a given distance (in blocks) /// of a position. /// From 26197e75843c652cf0db818f963c20794fd08f81 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 30 Aug 2019 19:59:41 -0600 Subject: [PATCH 3/7] Additional physics improvements --- server/src/physics/entity.rs | 81 +++++++++++++++++++++--------------- server/src/physics/math.rs | 51 +++++++++++++++++------ 2 files changed, 85 insertions(+), 47 deletions(-) diff --git a/server/src/physics/entity.rs b/server/src/physics/entity.rs index 32db44db2..28a0577e7 100644 --- a/server/src/physics/entity.rs +++ b/server/src/physics/entity.rs @@ -3,12 +3,11 @@ use specs::{Join, Read, ReadStorage, System, WriteStorage}; -use feather_core::world::ChunkMap; - use crate::entity::{EntityType, PositionComponent, VelocityComponent}; -use crate::physics::{ - bbox_front, block_impacted_by_ray, blocks_intersecting_bbox, BoundingBoxComponent, Side, -}; +use crate::physics::{block_impacted_by_ray, blocks_intersecting_bbox, BoundingBoxComponent, Side}; +use feather_core::world::ChunkMap; +use feather_core::BlockExt; +use feather_core::Position; /// System for updating all entities' positions and velocities /// each tick. @@ -49,38 +48,10 @@ impl<'a> System<'a> for EntityPhysicsSystem { let mut pending_position = position.current + velocity.0; - // Check for blocks around the bbox and apply offset - // to position to stop the bbox from intersecting blocks. - let intersect = blocks_intersecting_bbox( - &chunk_map, - position.current, - pending_position, - bounding_box, - ); - intersect.apply_to(&mut pending_position); - - if intersect.x_affected() { - velocity.x = 0.0; - } - - if intersect.y_affected() { - velocity.y = 0.0; - pending_position.on_ground = true; - } else { - pending_position.on_ground = false; - } - - if intersect.z_affected() { - velocity.z = 0.0; - } - // Check for blocks along path between old position and pending position. // This prevents entities from flying through blocks when their // velocity is sufficiently high. - - // The origin is the "leading point" of the bounding box: - // the point at the front. - let origin = (position.current + bbox_front(&bounding_box.0, velocity.0)).into(); + let origin = pending_position.into(); let direction = (pending_position - position.previous).into(); let distance_squared = pending_position.distance_squared(position.previous); @@ -109,6 +80,44 @@ impl<'a> System<'a> for EntityPhysicsSystem { } } + // Check for blocks around the bbox and apply offset + // to position to stop the bbox from intersecting blocks. + let intersect = blocks_intersecting_bbox( + &chunk_map, + position.current, + pending_position, + bounding_box, + ); + intersect.apply_to(&mut pending_position); + + if intersect.x_affected() { + velocity.x = 0.0; + } + + if intersect.y_affected() { + velocity.y = 0.0; + } + + if intersect.z_affected() { + velocity.z = 0.0; + } + + // Set on ground status + pending_position.on_ground = match chunk_map.block_at( + position!( + pending_position.x, + pending_position.y - bounding_box.size().y / 2.0, + pending_position.z + ) + .block_pos(), + ) { + Some(block) => { + debug!("{:?}", block); + block.is_solid() + } + None => false, + }; + // Apply drag and gravity. // TODO account for liquid let gravity = gravitational_acceleration(*ty); @@ -128,10 +137,14 @@ impl<'a> System<'a> for EntityPhysicsSystem { // A move event is triggered through FlaggedStorage. position.current = pending_position; + debug!("velocity {:?}", velocity); + // Update velocity, if it changed. if velocity != *restrict_velocity.get_unchecked() { *restrict_velocity.get_mut_unchecked() = velocity; } + + info!("ITERATION"); } } } diff --git a/server/src/physics/math.rs b/server/src/physics/math.rs index dd2692855..7de810a46 100644 --- a/server/src/physics/math.rs +++ b/server/src/physics/math.rs @@ -303,10 +303,10 @@ impl BlockIntersect { pub fn blocks_intersecting_bbox( chunk_map: &ChunkMap, from: Position, - mut dest: Position, + dest: Position, bbox: &BoundingBoxComponent, ) -> BlockIntersect { - let bbox_size = bbox.size(); + let bbox_size = bbox.size() / 2.0; assert!(bbox_size.x <= 1.0); assert!(bbox_size.y <= 1.0); @@ -320,22 +320,31 @@ pub fn blocks_intersecting_bbox( }; let offsets = [ - vec3(0.0, 0.0, 0.0), - vec3(bbox_size.x, 0.0, 0.0), - vec3(-bbox_size.x, 0.0, 0.0), vec3(0.0, bbox_size.y, 0.0), vec3(0.0, -bbox_size.y, 0.0), + vec3(bbox_size.x, 0.0, 0.0), + vec3(-bbox_size.x, 0.0, 0.0), vec3(0.0, 0.0, bbox_size.z), vec3(0.0, 0.0, -bbox_size.z), + vec3(0.0, 0.0, 0.0), + ]; + let normals = [ + vec3(0.0, 1.0, 0.0), + vec3(0.0, 1.0, 0.0), + vec3(1.0, 0.0, 0.0), + vec3(1.0, 0.0, 0.0), + vec3(0.0, 0.0, 1.0), + vec3(0.0, 0.0, 1.0), + vec3(0.0, 0.0, 0.0), ]; - // Compute a vector of isometries representing adjacent block locations. - let mut blocks: SmallVec<[Isometry3; 16]> = smallvec![]; + // Compute a vector of isometries and axis normals representing adjacent block locations. + let mut blocks: SmallVec<[(Isometry3, DVec3); 16]> = smallvec![]; // Prevent same block being checked twice. let mut checked = heapless::FnvIndexSet::::new(); - for offset in &offsets { + for (offset, normal) in offsets.iter().zip(normals.iter()) { let block_pos = (dest + *offset).block_pos(); if checked.contains(&block_pos) { @@ -349,13 +358,15 @@ pub fn blocks_intersecting_bbox( None => continue, // Unloaded chunk }; if !block.is_solid() { - continue; // Not a solid b lock + continue; // Not a solid block } let isometry = block_isometry_64(block_pos); - blocks.push(isometry); + blocks.push((isometry, *normal)); } + debug!("{:?}", blocks); + // Go through blocks and check for time of impact from original // position to the block. If the time of impact is <= 1, the entity // has collided with the block; update the position accordingly. @@ -363,7 +374,7 @@ pub fn blocks_intersecting_bbox( let velocity = (dest - from).as_vec(); let bbox_shape = bbox_to_cuboid(&bbox.0); - for block_isometry in blocks { + for (block_isometry, normal) in blocks { let toi = match query::time_of_impact( &block_isometry, &vec3(0.0, 0.0, 0.0), @@ -381,11 +392,25 @@ pub fn blocks_intersecting_bbox( let world_pos = from + velocity * toi.toi; let absolute_offset = world_pos - dest; - result.offset += absolute_offset.as_vec(); + result.offset += absolute_offset.as_vec().component_mul(&normal); - dest = dest + absolute_offset; + if normal.x != 0.0 { + result.x = true; + } + if normal.y != 0.0 { + result.y = true; + } + if normal.z != 0.0 { + result.z = true; + } + + debug!("normal {:?}", normal); + + break; // Only check along one axis } + debug!("{:?}", result); + result } From 56f27a4f952a18d7d06a3a5dfe42d3cd2def0b33 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 30 Aug 2019 21:41:31 -0600 Subject: [PATCH 4/7] additional fixes --- server/src/physics/entity.rs | 10 +++++----- server/src/physics/math.rs | 2 -- server/src/player/digging.rs | 1 + 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/server/src/physics/entity.rs b/server/src/physics/entity.rs index 28a0577e7..66798402c 100644 --- a/server/src/physics/entity.rs +++ b/server/src/physics/entity.rs @@ -51,9 +51,9 @@ impl<'a> System<'a> for EntityPhysicsSystem { // Check for blocks along path between old position and pending position. // This prevents entities from flying through blocks when their // velocity is sufficiently high. - let origin = pending_position.into(); - let direction = (pending_position - position.previous).into(); - let distance_squared = pending_position.distance_squared(position.previous); + let origin = position.current.into(); + let direction = (pending_position - position.current).into(); + let distance_squared = pending_position.distance_squared(position.current); if let Some(impacted) = block_impacted_by_ray(&chunk_map, origin, direction, distance_squared as f32) @@ -106,7 +106,7 @@ impl<'a> System<'a> for EntityPhysicsSystem { pending_position.on_ground = match chunk_map.block_at( position!( pending_position.x, - pending_position.y - bounding_box.size().y / 2.0, + pending_position.y - bounding_box.size().y / 2.0 - 0.01, pending_position.z ) .block_pos(), @@ -137,7 +137,7 @@ impl<'a> System<'a> for EntityPhysicsSystem { // A move event is triggered through FlaggedStorage. position.current = pending_position; - debug!("velocity {:?}", velocity); + debug!("velocity {:?}, position {:?}", velocity, pending_position); // Update velocity, if it changed. if velocity != *restrict_velocity.get_unchecked() { diff --git a/server/src/physics/math.rs b/server/src/physics/math.rs index 7de810a46..f44bf3bc0 100644 --- a/server/src/physics/math.rs +++ b/server/src/physics/math.rs @@ -365,8 +365,6 @@ pub fn blocks_intersecting_bbox( blocks.push((isometry, *normal)); } - debug!("{:?}", blocks); - // Go through blocks and check for time of impact from original // position to the block. If the time of impact is <= 1, the entity // has collided with the block; update the position accordingly. diff --git a/server/src/player/digging.rs b/server/src/player/digging.rs index b4fd5d510..44f45e13e 100644 --- a/server/src/player/digging.rs +++ b/server/src/player/digging.rs @@ -258,6 +258,7 @@ impl<'a> System<'a> for BlockUpdateBroadcastSystem { // Process events for event in events.read(&mut self.reader.as_mut().unwrap()) { + warn!("block break"); // Send Block Change packet to every player, // except for the one that performed the update // (if any) From 064dd9bbc7d1c9275e26d656dcdefbe51e34e33f Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 30 Aug 2019 21:52:24 -0600 Subject: [PATCH 5/7] Y position of entity is at bottom of bounding box, not center --- server/src/physics/math.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/server/src/physics/math.rs b/server/src/physics/math.rs index f44bf3bc0..ab87f454d 100644 --- a/server/src/physics/math.rs +++ b/server/src/physics/math.rs @@ -302,12 +302,17 @@ impl BlockIntersect { /// is more than 1, this function will panic. pub fn blocks_intersecting_bbox( chunk_map: &ChunkMap, - from: Position, - dest: Position, + mut from: Position, + mut dest: Position, bbox: &BoundingBoxComponent, ) -> BlockIntersect { let bbox_size = bbox.size() / 2.0; + // Center along Y axis of bounding box is at bottom, not center. + // This is a quick fix to get around this. + from.y += bbox_size.y; + dest.y += bbox_size.y; + assert!(bbox_size.x <= 1.0); assert!(bbox_size.y <= 1.0); assert!(bbox_size.z <= 1.0); From b117d77728dd97bcd5cb7a824371034a1dc2bfd1 Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 30 Aug 2019 22:30:08 -0600 Subject: [PATCH 6/7] Add test for `blocks_intersecting_bbox` --- server/src/physics/component.rs | 2 +- server/src/physics/entity.rs | 16 ++++++------- server/src/physics/math.rs | 41 ++++++++++++++++++++++++++++----- server/src/player/digging.rs | 1 - server/src/util/macros.rs | 10 ++++++++ 5 files changed, 54 insertions(+), 16 deletions(-) diff --git a/server/src/physics/component.rs b/server/src/physics/component.rs index 310a3c0b1..9aee153c9 100644 --- a/server/src/physics/component.rs +++ b/server/src/physics/component.rs @@ -62,7 +62,7 @@ fn bbox_for_type(ty: EntityType) -> Option> { } /// Returns a bounding box with the given width and height. -fn bbox(size_xz: f64, size_y: f64) -> AABB { +pub fn bbox(size_xz: f64, size_y: f64) -> AABB { AABB::new( glm::vec3(0.0, 0.0, 0.0).into(), glm::vec3(size_xz, size_y, size_xz).into(), diff --git a/server/src/physics/entity.rs b/server/src/physics/entity.rs index 66798402c..6b000a0ce 100644 --- a/server/src/physics/entity.rs +++ b/server/src/physics/entity.rs @@ -111,13 +111,17 @@ impl<'a> System<'a> for EntityPhysicsSystem { ) .block_pos(), ) { - Some(block) => { - debug!("{:?}", block); - block.is_solid() - } + Some(block) => block.is_solid(), None => false, }; + // If entity is inside block, push it up. + if let Some(block) = chunk_map.block_at(pending_position.block_pos()) { + if block.is_solid() { + pending_position.y += 0.2; + } + } + // Apply drag and gravity. // TODO account for liquid let gravity = gravitational_acceleration(*ty); @@ -137,14 +141,10 @@ impl<'a> System<'a> for EntityPhysicsSystem { // A move event is triggered through FlaggedStorage. position.current = pending_position; - debug!("velocity {:?}, position {:?}", velocity, pending_position); - // Update velocity, if it changed. if velocity != *restrict_velocity.get_unchecked() { *restrict_velocity.get_mut_unchecked() = velocity; } - - info!("ITERATION"); } } } diff --git a/server/src/physics/math.rs b/server/src/physics/math.rs index ab87f454d..5462f8fe7 100644 --- a/server/src/physics/math.rs +++ b/server/src/physics/math.rs @@ -406,14 +406,8 @@ pub fn blocks_intersecting_bbox( if normal.z != 0.0 { result.z = true; } - - debug!("normal {:?}", normal); - - break; // Only check along one axis } - debug!("{:?}", result); - result } @@ -712,4 +706,39 @@ mod tests { assert_float_eq!(half_extents.y, 1.0); assert_float_eq!(half_extents.z, 1.5); } + + #[test] + fn test_blocks_intersecting_bbox() { + let chunk_map = chunk_map(); + + let froms = [ + position!(0.0, 66.0, 0.0), + position!(100.0, 65.0, 0.0), + position!(0.0, 100.0, 0.0), + ]; + + let dests = [ + position!(0.0, 65.0, 0.0), + position!(100.0, 65.0, 0.0), + position!(0.0, 90.0, 0.0), + ]; + + let results = [ + position!(0.0, 65.0, 0.0), + position!(100.0, 65.0, 0.0), + position!(0.0, 90.0, 0.0), + ]; + + let bbox = BoundingBoxComponent(crate::physics::component::bbox(0.25, 0.25)); + + for ((from, dest), result) in froms.iter().zip(&dests).zip(&results) { + let intersect = blocks_intersecting_bbox(&chunk_map, *from, *dest, &bbox); + let mut pos = *dest; + intersect.apply_to(&mut pos); + + println!("{:?}", pos); + + assert_pos_eq!(pos, result); + } + } } diff --git a/server/src/player/digging.rs b/server/src/player/digging.rs index 44f45e13e..b4fd5d510 100644 --- a/server/src/player/digging.rs +++ b/server/src/player/digging.rs @@ -258,7 +258,6 @@ impl<'a> System<'a> for BlockUpdateBroadcastSystem { // Process events for event in events.read(&mut self.reader.as_mut().unwrap()) { - warn!("block break"); // Send Block Change packet to every player, // except for the one that performed the update // (if any) diff --git a/server/src/util/macros.rs b/server/src/util/macros.rs index 7b0c5ece9..66e6b841a 100644 --- a/server/src/util/macros.rs +++ b/server/src/util/macros.rs @@ -11,6 +11,16 @@ macro_rules! assert_float_eq { }; } +/// Checks that two positions are approximately equivalent. +#[cfg(test)] +macro_rules! assert_pos_eq { + ($left:expr, $right:expr) => { + assert_float_eq!($left.x, $right.x, 0.01); + assert_float_eq!($left.y, $right.y, 0.01); + assert_float_eq!($left.z, $right.z, 0.011); + }; +} + /// Generates a setup() implementation for a system /// which initializes an internal event reader. macro_rules! setup_impl { From a155a0af7a039dff4ca4dfaf5cc12e0e89e993fc Mon Sep 17 00:00:00 2001 From: caelunshun Date: Fri, 30 Aug 2019 23:19:17 -0600 Subject: [PATCH 7/7] Add basic physics system test --- server/src/physics/entity.rs | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/server/src/physics/entity.rs b/server/src/physics/entity.rs index 6b000a0ce..34b475b91 100644 --- a/server/src/physics/entity.rs +++ b/server/src/physics/entity.rs @@ -186,3 +186,41 @@ fn drag_force(ty: EntityType) -> f32 { 0.0 } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::testframework as t; + use specs::WorldExt; + + #[test] + fn test_physics_basic() { + let (mut w, mut d) = t::builder().with(EntityPhysicsSystem, "").build(); + + t::populate_with_air(&mut w); + + let item = t::add_entity_with_pos_and_vel( + &mut w, + EntityType::Item, + position!(0.0, 0.0, 0.0), + glm::vec3(0.0, 1.0, 0.0), + false, + ); + + let bbox = crate::physics::component::bbox(0.25, 0.25); + w.write_component::() + .insert(item, BoundingBoxComponent(bbox)) + .unwrap(); + + d.dispatch(&w); + w.maintain(); + + let pos = t::entity_pos(&w, item); + let vel = t::entity_vel(&w, item).unwrap(); + + assert_pos_eq!(pos, position!(0.0, 1.0, 0.0)); + assert_float_eq!(vel.x, 0.0); + assert_float_eq!(vel.y, 0.94); + assert_float_eq!(vel.z, 0.0); + } +}