Replace nalgebra-glm with vek

Vek has been pleasant to use in practice. nalgebra is slow to compile
and its documentation is difficult to traverse. Vek also comes with its
own AABB impl, so we use that instead.
This commit is contained in:
Ryan 2022-05-16 04:09:51 -07:00
parent adc8a4faae
commit 620c0bf287
10 changed files with 136 additions and 264 deletions

View file

@ -24,7 +24,6 @@ flume = "0.10"
futures = "0.3"
hematite-nbt = "0.5"
log = "0.4"
nalgebra-glm = "0.17"
num = "0.4"
parking_lot = "0.12"
paste = "1"
@ -39,6 +38,7 @@ sha2 = "0.10"
thiserror = "1"
tokio = { version = "1", features = ["full"] }
uuid = "1"
vek = "0.15"
[dependencies.reqwest]
version = "0.11"

View file

@ -7,8 +7,8 @@ use valence::client::GameMode;
use valence::config::{Config, ServerListPing};
use valence::text::Color;
use valence::{
async_trait, ChunkPos, ClientMut, DimensionId, Server, ShutdownResult, Text,
TextFormat, WorldId, WorldsMut,
async_trait, ChunkPos, ClientMut, DimensionId, Server, ShutdownResult, Text, TextFormat,
WorldId, WorldsMut,
};
pub fn main() -> ShutdownResult {

View file

@ -1,176 +0,0 @@
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use num::cast::AsPrimitive;
use crate::glm::{self, Number, RealNumber, TVec, TVec3};
/// An Axis-aligned bounding box in an arbitrary dimension, defined by its
/// minimum and maximum corners.
///
/// This type maintains the invariant that `min <= max` componentwise.
///
/// The generic type `T` can be an integer, which is useful for AABBs on grids.
#[derive(Clone, Copy, Debug)]
pub struct Aabb<T, const D: usize> {
min: TVec<T, D>,
max: TVec<T, D>,
}
impl<T: Number, const D: usize> Aabb<T, D> {
pub fn new(p0: impl Into<TVec<T, D>>, p1: impl Into<TVec<T, D>>) -> Self {
let p0 = p0.into();
let p1 = p1.into();
Self {
min: glm::min2(&p0, &p1),
max: glm::max2(&p0, &p1),
}
}
pub fn point(pos: impl Into<TVec<T, D>>) -> Self {
let pos = pos.into();
Self { min: pos, max: pos }
}
pub fn min(&self) -> TVec<T, D> {
self.min
}
pub fn max(&self) -> TVec<T, D> {
self.max
}
pub fn dimensions(&self) -> TVec<T, D> {
self.max - self.min
}
/// Moves this AABB by some vector.
pub fn translate(&self, v: impl Into<TVec<T, D>>) -> Self {
let v = v.into();
Self {
min: self.min + v,
max: self.max + v,
}
}
/// Calculates the AABB union, which is the smallest AABB completely
/// encompassing both AABBs.
pub fn union(&self, other: Self) -> Self {
Self {
min: glm::min2(&self.min, &other.min),
max: glm::max2(&self.max, &other.max),
}
}
pub fn collides_with_aabb(&self, other: &Self) -> bool {
let l = glm::less_than_equal(&self.min, &other.max);
let r = glm::greater_than_equal(&self.max, &other.min);
glm::all(&l.zip_map(&r, |l, r| l && r))
}
}
impl<T: Number, const D: usize> Aabb<T, D>
where
i32: AsPrimitive<T>,
{
/// Returns the center (centroid) of this AABB.
pub fn center(&self) -> TVec<T, D> {
(self.min + self.max).map(|c| c / 2.as_())
}
}
impl<T: RealNumber, const D: usize> Aabb<T, D> {
/// Construct an AABB from a center (centroid) and the dimensions of the box
/// along each axis.
pub fn from_center_and_dimensions(
center: impl Into<TVec<T, D>>,
dims: impl Into<TVec<T, D>>,
) -> Self {
let half = dims.into() * T::from_subset(&0.5);
let center = center.into();
Self {
min: center - half,
max: center + half,
}
}
pub fn collides_with_sphere(
&self,
center: impl Into<TVec<T, D>>,
radius: impl Into<T>,
) -> bool {
self.distance_to_point(center.into()) <= radius.into()
}
pub fn distance_to_point(&self, p: impl Into<TVec<T, D>>) -> T {
let p = p.into();
glm::distance(&p, &glm::clamp_vec(&p, &self.min, &self.max))
}
}
impl<T: Number> Aabb<T, 3>
where
i32: AsPrimitive<T>,
{
pub fn surface_area(&self) -> T {
let d = self.dimensions();
(d.x * d.y + d.y * d.z + d.z * d.x) * 2.as_()
}
}
impl<T: RealNumber> Aabb<T, 3> {
/// Constructs an AABB from a position and the dimensions of the box along
/// each axis. The position is the center of the bottom face of the AABB.
pub fn from_bottom_and_dimensions(
bottom: impl Into<TVec3<T>>,
dims: impl Into<TVec3<T>>,
) -> Self {
let dims = dims.into();
Self::from_center_and_dimensions(bottom, dims).translate([
T::from_subset(&0.0),
dims.y * T::from_subset(&0.5),
T::from_subset(&0.0),
])
}
}
impl<T: Number + Default, const D: usize> Default for Aabb<T, D> {
fn default() -> Self {
let d = T::default();
Self::new([d; D], [d; D])
}
}
impl<T: Number, const D: usize> PartialEq for Aabb<T, D> {
fn eq(&self, other: &Self) -> bool {
self.min == other.min && self.max == other.max
}
}
impl<T: Number + Eq, const D: usize> Eq for Aabb<T, D> {}
impl<T: Number, const D: usize> PartialOrd for Aabb<T, D> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match self.min.partial_cmp(&other.min) {
Some(Ordering::Equal) => self.max.partial_cmp(&other.max),
ord => return ord,
}
}
}
impl<T: Number + Hash, const D: usize> Hash for Aabb<T, D> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.min.hash(state);
self.max.hash(state);
}
}
// TODO: impl Ord for Aabb
//impl<T: Number + Ord, const D: usize> Ord for Aabb<T, D> {
// fn cmp(&self, other: &Self) -> std::cmp::Ordering {
// match self.min.cmp(&other.min) {
// Ordering::Equal => self.max.cmp(&other.max),
// ord => ord,
// }
// }
//}

View file

@ -5,13 +5,12 @@ use std::io::Write;
use std::iter::FusedIterator;
use std::ops::Deref;
use bitvec::bitvec;
use bitvec::vec::BitVec;
use num::Integer;
use rayon::iter::{IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator};
use vek::Vec2;
use crate::block::BlockState;
use crate::glm::DVec2;
use crate::packets::play::{
BlockChange, ChunkDataAndUpdateLight, ChunkDataHeightmaps, ClientPlayPacket, MultiBlockChange,
};
@ -292,15 +291,6 @@ impl ChunkPos {
pub const fn new(x: i32, z: i32) -> Self {
Self { x, z }
}
/// Returns the chunk position of the chunk that the given coordinates are
/// contained within.
pub fn from_xz(xz: DVec2) -> Self {
Self {
x: (xz.x / 16.0) as i32,
z: (xz.y / 16.0) as i32,
}
}
}
impl From<(i32, i32)> for ChunkPos {

View file

@ -3,9 +3,9 @@ use std::iter::FusedIterator;
use std::ops::Deref;
use flume::{Receiver, Sender, TrySendError};
use glm::DVec3;
use rayon::iter::ParallelIterator;
use uuid::Uuid;
use vek::Vec3;
use crate::block_pos::BlockPos;
use crate::config::{
@ -26,9 +26,7 @@ use crate::server::ServerPacketChannels;
use crate::slotmap::{Key, SlotMap};
use crate::util::{chunks_in_view_distance, is_chunk_in_view_distance};
use crate::var_int::VarInt;
use crate::{
glm, ident, ChunkPos, Chunks, Entities, EntityId, Server, Text, Ticks, LIBRARY_NAMESPACE,
};
use crate::{ident, ChunkPos, Chunks, Entities, EntityId, Server, Text, Ticks, LIBRARY_NAMESPACE};
pub struct Clients {
sm: SlotMap<Client>,
@ -117,8 +115,8 @@ pub struct Client {
username: String,
uuid: Uuid,
on_ground: bool,
new_position: DVec3,
old_position: DVec3,
new_position: Vec3<f64>,
old_position: Vec3<f64>,
/// Measured in degrees
yaw: f32,
/// Measured in degrees
@ -181,8 +179,8 @@ impl Client {
username,
uuid,
on_ground: false,
new_position: DVec3::default(),
old_position: DVec3::default(),
new_position: Vec3::default(),
old_position: Vec3::default(),
yaw: 0.0,
pitch: 0.0,
teleported_this_tick: false,
@ -216,7 +214,7 @@ impl Client {
self.uuid
}
pub fn position(&self) -> DVec3 {
pub fn position(&self) -> Vec3<f64> {
self.new_position
}
@ -262,7 +260,7 @@ impl<'a> ClientMut<'a> {
ClientMut(self.0)
}
pub fn teleport(&mut self, pos: impl Into<DVec3>, yaw: f32, pitch: f32) {
pub fn teleport(&mut self, pos: impl Into<Vec3<f64>>, yaw: f32, pitch: f32) {
self.0.new_position = pos.into();
self.0.yaw = yaw;
self.0.pitch = pitch;
@ -425,7 +423,10 @@ impl<'a> ClientMut<'a> {
.map_or(2, |s| s.view_distance)
.min(self.new_max_view_distance);
let center = ChunkPos::from_xz(self.0.new_position.xz());
let center = ChunkPos::new(
(self.new_position.x / 16.0) as i32,
(self.new_position.z / 16.0) as i32,
);
// Send the update view position packet if the client changes the chunk section
// they're in.
@ -484,19 +485,18 @@ impl<'a> ClientMut<'a> {
// This is done after the chunks are loaded so that the "downloading terrain"
// screen is closed at the appropriate time.
// TODO: temporarily broken
// if self.0.teleported_this_tick {
// self.0.teleported_this_tick = false;
if self.0.teleported_this_tick {
self.0.teleported_this_tick = false;
// self.send_packet(dbg!(PlayerPositionAndLook {
// position: self.new_position,
// yaw: self.yaw,
// pitch: self.pitch,
// flags: PlayerPositionAndLookFlags::new(false, false, false, false, false),
// teleport_id: VarInt((self.teleport_id_counter - 1) as i32),
// dismount_vehicle: false,
// }));
// }
self.send_packet(PlayerPositionAndLook {
position: self.new_position,
yaw: self.yaw,
pitch: self.pitch,
flags: PlayerPositionAndLookFlags::new(false, false, false, false, false),
teleport_id: VarInt((self.teleport_id_counter - 1) as i32),
dismount_vehicle: false,
});
}
let mut entities_to_unload = Vec::new();
@ -504,9 +504,7 @@ impl<'a> ClientMut<'a> {
// longer visible.
self.0.loaded_entities.retain(|&id| {
if let Some(entity) = entities.get(id) {
if glm::distance(&self.0.new_position, &entity.position())
<= view_dist as f64 * 16.0
{
if self.0.new_position.distance(entity.position()) <= view_dist as f64 * 16.0 {
todo!("update entity");
return true;
}
@ -524,7 +522,7 @@ impl<'a> ClientMut<'a> {
// Spawn new entities within the view distance.
for (id, entity) in entities.iter() {
if glm::distance(&self.position(), &entity.position()) <= view_dist as f64 * 16.0
if self.position().distance(entity.position()) <= view_dist as f64 * 16.0
&& entity.typ() != EntityType::Marker
&& self.0.loaded_entities.insert(id)
{
@ -548,7 +546,7 @@ impl<'a> ClientMut<'a> {
fn handle_movement_packet(
client: &mut Client,
new_position: DVec3,
new_position: Vec3<f64>,
new_yaw: f32,
new_pitch: f32,
new_on_ground: bool,
@ -556,8 +554,8 @@ impl<'a> ClientMut<'a> {
if client.pending_teleports == 0 {
let event = Event::Movement {
position: client.new_position,
yaw_degrees: client.yaw,
pitch_degrees: client.pitch,
yaw: client.yaw,
pitch: client.pitch,
on_ground: client.on_ground,
};
@ -693,9 +691,9 @@ pub enum Event {
/// The client has moved. The values in this variant are the previous
/// position and look.
Movement {
position: DVec3,
yaw_degrees: f32,
pitch_degrees: f32,
position: Vec3<f64>,
yaw: f32,
pitch: f32,
on_ground: bool,
},
}

View file

@ -9,18 +9,17 @@ use std::ops::Deref;
use bitfield_struct::bitfield;
use rayon::iter::ParallelIterator;
use uuid::Uuid;
use vek::{Aabb, Vec3};
use crate::byte_angle::ByteAngle;
use crate::chunk::ChunkPos;
use crate::glm::{DVec3, I16Vec3, Vec3};
use crate::packets::play::{
ClientPlayPacket, EntityMetadata, SpawnEntity, SpawnExperienceOrb, SpawnLivingEntity,
SpawnPainting, SpawnPlayer,
};
use crate::protocol::ReadToEnd;
use crate::slotmap::{Key, SlotMap};
use crate::util::aabb_from_bottom_and_size;
use crate::var_int::VarInt;
use crate::{Aabb, WorldId};
pub struct Entities {
sm: SlotMap<Entity>,
@ -103,8 +102,8 @@ impl<'a> EntitiesMut<'a> {
let entity = EntityId(self.0.sm.insert(Entity {
flags: EntityFlags(0),
meta: EntityMeta::new(EntityType::Marker),
new_position: DVec3::default(),
old_position: DVec3::default(),
new_position: Vec3::default(),
old_position: Vec3::default(),
yaw: 0.0,
pitch: 0.0,
head_yaw: 0.0,
@ -181,13 +180,13 @@ impl EntityId {
pub struct Entity {
flags: EntityFlags,
meta: EntityMeta,
new_position: DVec3,
old_position: DVec3,
new_position: Vec3<f64>,
old_position: Vec3<f64>,
yaw: f32,
pitch: f32,
head_yaw: f32,
head_pitch: f32,
velocity: Vec3,
velocity: Vec3<f32>,
uuid: Uuid,
}
@ -230,13 +229,13 @@ impl Entity {
}
/// Returns the position of this entity in the world it inhabits.
pub fn position(&self) -> DVec3 {
pub fn position(&self) -> Vec3<f64> {
self.new_position
}
/// Returns the position of this entity as it existed at the end of the
/// previous tick.
pub fn old_position(&self) -> DVec3 {
pub fn old_position(&self) -> Vec3<f64> {
self.old_position
}
@ -261,7 +260,7 @@ impl Entity {
}
/// Gets the velocity of this entity in meters per second.
pub fn velocity(&self) -> Vec3 {
pub fn velocity(&self) -> Vec3<f32> {
self.velocity
}
@ -369,7 +368,7 @@ impl Entity {
}
}
pub fn hitbox(&self) -> Aabb<f64, 3> {
pub fn hitbox(&self) -> Aabb<f64> {
let dims = match &self.meta {
EntityMeta::AreaEffectCloud(e) => [
e.get_radius() as f64 * 2.0,
@ -504,13 +503,13 @@ impl Entity {
EntityMeta::FishingBobber(_) => [0.25, 0.25, 0.25],
};
Aabb::from_bottom_and_dimensions(self.new_position, dims)
aabb_from_bottom_and_size(self.new_position, dims.into())
}
}
fn velocity_to_packet_units(vel: Vec3) -> I16Vec3 {
fn velocity_to_packet_units(vel: Vec3<f32>) -> Vec3<i16> {
// The saturating cast to i16 is desirable.
vel.map(|v| (v * 400.0) as i16)
(vel * 400.0).as_()
}
impl<'a> EntityMut<'a> {
@ -534,7 +533,7 @@ impl<'a> EntityMut<'a> {
}
/// Sets the position of this entity in the world it inhabits.
pub fn set_position(&mut self, pos: impl Into<DVec3>) {
pub fn set_position(&mut self, pos: impl Into<Vec3<f64>>) {
self.0.new_position = pos.into();
}
@ -570,9 +569,10 @@ impl<'a> EntityMut<'a> {
}
}
pub fn set_velocity(&mut self, velocity: Vec3) {
self.0.velocity = velocity;
if velocity_to_packet_units(self.velocity) != velocity_to_packet_units(velocity) {
pub fn set_velocity(&mut self, velocity: impl Into<Vec3<f32>>) {
let new_vel = velocity.into();
self.0.velocity = new_vel;
if velocity_to_packet_units(self.velocity) != velocity_to_packet_units(new_vel) {
self.0.flags.set_velocity_modified(true);
}
}

View file

@ -7,7 +7,6 @@
// missing_docs
)]
mod aabb;
pub mod block;
mod block_pos;
mod byte_angle;
@ -27,7 +26,6 @@ mod var_int;
mod var_long;
pub mod world;
pub use aabb::Aabb;
pub use async_trait::async_trait;
pub use block_pos::BlockPos;
pub use chunk::{Chunk, ChunkPos, Chunks, ChunksMut};
@ -39,7 +37,7 @@ pub use server::{start_server, NewClientData, Server, ShutdownResult};
pub use text::{Text, TextFormat};
pub use uuid::Uuid;
pub use world::{WorldId, WorldMut, WorldRef, Worlds, WorldsMut};
pub use {nalgebra_glm as glm, nbt, uuid};
pub use {nbt, uuid, vek};
/// The Minecraft protocol version that this library targets.
pub const PROTOCOL_VERSION: i32 = 758;

View file

@ -13,10 +13,10 @@ use num::{One, Zero};
use paste::paste;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use vek::{Vec2, Vec3};
use crate::block_pos::BlockPos;
use crate::byte_angle::ByteAngle;
use crate::glm::{DVec3, I16Vec3, Vec3};
use crate::identifier::Identifier;
use crate::protocol::{BoundedArray, BoundedInt, BoundedString, Decode, Encode, Nbt, ReadToEnd};
use crate::var_int::VarInt;
@ -445,18 +445,18 @@ pub mod play {
entity_id: VarInt,
object_uuid: Uuid,
typ: VarInt,
position: DVec3,
position: Vec3<f64>,
pitch: ByteAngle,
yaw: ByteAngle,
data: i32,
velocity: I16Vec3,
velocity: Vec3<i16>,
}
}
def_struct! {
SpawnExperienceOrb 0x01 {
entity_id: VarInt,
position: DVec3,
position: Vec3<f64>,
count: i16,
}
}
@ -466,11 +466,11 @@ pub mod play {
entity_id: VarInt,
entity_uuid: Uuid,
typ: VarInt,
position: DVec3,
position: Vec3<f64>,
yaw: ByteAngle,
pitch: ByteAngle,
head_pitch: ByteAngle,
velocity: I16Vec3,
velocity: Vec3<i16>,
}
}
@ -497,7 +497,7 @@ pub mod play {
SpawnPlayer 0x04 {
entity_id: VarInt,
player_uuid: Uuid,
position: DVec3,
position: Vec3<f64>,
yaw: ByteAngle,
pitch: ByteAngle,
}
@ -966,7 +966,7 @@ pub mod play {
def_struct! {
PlayerPositionAndLook 0x38 {
position: DVec3,
position: Vec3<f64>,
yaw: f32,
pitch: f32,
flags: PlayerPositionAndLookFlags,
@ -1290,7 +1290,7 @@ pub mod play {
def_struct! {
InteractAtData {
target: Vec3,
target: Vec3<f64>,
hand: Hand,
}
}
@ -1317,7 +1317,7 @@ pub mod play {
def_struct! {
PlayerPosition 0x11 {
position: DVec3,
position: Vec3<f64>,
on_ground: bool,
}
}
@ -1325,7 +1325,7 @@ pub mod play {
def_struct! {
PlayerPositionAndRotation 0x12 {
// Absolute position
position: DVec3,
position: Vec3<f64>,
/// Absolute rotation on X axis in degrees.
yaw: f32,
/// Absolute rotation on Y axis in degrees.
@ -1353,7 +1353,7 @@ pub mod play {
def_struct! {
VehicleMoveServerbound 0x15 {
/// Absolute position
position: DVec3,
position: Vec3<f64>,
/// Degrees
yaw: f32,
/// Degrees
@ -1669,7 +1669,7 @@ pub mod play {
hand: Hand,
location: BlockPos,
face: BlockFace,
cursor_pos: Vec3,
cursor_pos: Vec3<f64>,
head_inside_block: bool,
}
}

View file

@ -5,9 +5,9 @@ use anyhow::{anyhow, ensure, Context};
use arrayvec::ArrayVec;
use bitvec::prelude::*;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use nalgebra_glm::{Number, TVec};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use vek::{Vec2, Vec3, Vec4};
use crate::var_int::VarInt;
use crate::EntityId;
@ -372,15 +372,50 @@ impl<T: Decode, const N: usize> Decode for [T; N] {
}
}
impl<T: Encode, const N: usize> Encode for TVec<T, N> {
impl<T: Encode> Encode for Vec2<T> {
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
encode_array_bounded(self.as_slice(), N, N, w)
self.x.encode(w)?;
self.y.encode(w)
}
}
impl<T: Decode + Number, const N: usize> Decode for TVec<T, N> {
impl<T: Encode> Encode for Vec3<T> {
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
self.x.encode(w)?;
self.y.encode(w)?;
self.z.encode(w)
}
}
impl<T: Encode> Encode for Vec4<T> {
fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> {
self.x.encode(w)?;
self.y.encode(w)?;
self.z.encode(w)?;
self.w.encode(w)
}
}
impl<T: Decode> Decode for Vec2<T> {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
Ok(<[T; N]>::decode(r)?.into())
Ok(Vec2::new(T::decode(r)?, T::decode(r)?))
}
}
impl<T: Decode> Decode for Vec3<T> {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
Ok(Vec3::new(T::decode(r)?, T::decode(r)?, T::decode(r)?))
}
}
impl<T: Decode> Decode for Vec4<T> {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
Ok(Vec4::new(
T::decode(r)?,
T::decode(r)?,
T::decode(r)?,
T::decode(r)?,
))
}
}

View file

@ -1,5 +1,9 @@
use std::iter::FusedIterator;
use num::cast::AsPrimitive;
use num::Float;
use vek::{Aabb, Extent3, Vec3};
use crate::ChunkPos;
/// Returns true if the given string meets the criteria for a valid Minecraft
@ -26,3 +30,26 @@ pub fn is_chunk_in_view_distance(center: ChunkPos, other: ChunkPos, distance: u8
(center.x as f64 - other.x as f64).powi(2) + (center.z as f64 - other.z as f64).powi(2)
<= (distance as f64 + EXTRA_RADIUS as f64).powi(2)
}
pub(crate) fn aabb_from_bottom_and_size<T>(bottom: Vec3<T>, size: Vec3<T>) -> Aabb<T>
where
T: Float + 'static,
f64: AsPrimitive<T>,
{
let aabb = Aabb {
min: Vec3::new(
bottom.x - size.x / 2.0.as_(),
bottom.y,
bottom.z - size.z / 2.0.as_(),
),
max: Vec3::new(
bottom.x + size.x / 2.0.as_(),
bottom.y,
bottom.z + size.z / 2.0.as_(),
),
};
debug_assert!(aabb.is_valid());
aabb
}