You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

161 lines
4.2 KiB

use std::{
num::NonZeroU32,
ops::{self, Index, IndexMut},
};
use bevy::{
ecs::component::Component,
math::{Dir3, IVec3, InvalidDirectionError},
transform::components::Transform,
};
use overload::overload;
use super::{BlockPos, BlockRegion, ChunkRegion};
use crate::bloxel::math::USize3;
pub const CHUNK_SHIFT: i32 = 4;
pub const CHUNK_MASK: i32 = !(!0 << CHUNK_SHIFT); // = 0b1111
pub const CHUNK_LENGTH: usize = 1 << CHUNK_SHIFT; // = 16
pub const CHUNK_SIZE: USize3 =
USize3::splat(unsafe { NonZeroU32::new_unchecked(CHUNK_LENGTH as u32) });
pub const CHUNK_MAX: IVec3 = IVec3::splat((CHUNK_LENGTH - 1) as i32);
#[derive(Component, Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct ChunkPos {
pub x: i32,
pub y: i32,
pub z: i32,
}
impl ChunkPos {
pub const ORIGIN: ChunkPos = Self::new(0, 0, 0);
pub const fn new(x: i32, y: i32, z: i32) -> Self {
Self { x, y, z }
}
pub fn region(self) -> ChunkRegion {
ChunkRegion::new_unchecked(self, self)
}
pub fn distance_to(self, other: Self) -> f32 {
(self.distance_to_squared(other) as f32).sqrt()
}
pub fn distance_to_squared(self, other: Self) -> i32 {
IVec3::distance_squared(self.into(), other.into())
}
pub fn direction_to(self, other: Self) -> Result<Dir3, InvalidDirectionError> {
Dir3::new((other - self).as_vec3())
}
pub fn transform(self) -> Transform {
let pos: IVec3 = self.into();
Transform::from_translation((pos << CHUNK_SHIFT).as_vec3())
}
pub fn min(self, rhs: Self) -> Self {
Self::new(self.x.min(rhs.x), self.y.min(rhs.y), self.z.min(rhs.z))
}
pub fn max(self, rhs: Self) -> Self {
Self::new(self.x.max(rhs.x), self.y.max(rhs.y), self.z.max(rhs.z))
}
pub fn from_block_pos(pos: BlockPos) -> (Self, IVec3) {
let pos: IVec3 = pos.into();
((pos >> CHUNK_SHIFT).into(), pos & CHUNK_MASK)
}
pub fn to_block_pos(self, relative: IVec3) -> BlockPos {
let pos: IVec3 = self.into();
((pos << CHUNK_SHIFT) + relative).into()
}
pub fn to_block_region(self) -> BlockRegion {
BlockRegion::new_unchecked(
self.to_block_pos(IVec3::ZERO),
self.to_block_pos(IVec3::splat(CHUNK_LENGTH as i32 - 1)),
)
}
}
impl std::fmt::Display for ChunkPos {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[{}, {}, {}]", self.x, self.y, self.z)
}
}
// Offsetting
overload!((l: ?ChunkPos) + (r: ?IVec3) -> ChunkPos { ChunkPos::new(l.x + r.x, l.y + r.y, l.z + r.z) });
overload!((l: ?ChunkPos) - (r: ?IVec3) -> ChunkPos { ChunkPos::new(l.x - r.x, l.y - r.y, l.z - r.z) });
overload!((s: &mut ChunkPos) += (v: ?IVec3) { s.x += v.x; s.y += v.y; s.z += v.z; });
overload!((s: &mut ChunkPos) -= (v: ?IVec3) { s.x -= v.x; s.y -= v.y; s.z -= v.z; });
// Difference
overload!((l: ?ChunkPos) - (r: ?ChunkPos) -> IVec3 { IVec3::new(l.x - r.x, l.y - r.y, l.z - r.z) });
// Indexing
impl Index<usize> for ChunkPos {
type Output = i32;
fn index(&self, index: usize) -> &Self::Output {
match index {
0 => &self.x,
1 => &self.y,
2 => &self.z,
_ => panic!("index out of bounds"),
}
}
}
impl IndexMut<usize> for ChunkPos {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
match index {
0 => &mut self.x,
1 => &mut self.y,
2 => &mut self.z,
_ => panic!("index out of bounds"),
}
}
}
// Conversion
impl From<(i32, i32, i32)> for ChunkPos {
fn from((x, y, z): (i32, i32, i32)) -> Self {
Self::new(x, y, z)
}
}
impl From<ChunkPos> for (i32, i32, i32) {
fn from(ChunkPos { x, y, z }: ChunkPos) -> Self {
(x, y, z)
}
}
impl From<[i32; 3]> for ChunkPos {
fn from([x, y, z]: [i32; 3]) -> Self {
Self::new(x, y, z)
}
}
impl From<ChunkPos> for [i32; 3] {
fn from(ChunkPos { x, y, z }: ChunkPos) -> Self {
[x, y, z]
}
}
impl From<IVec3> for ChunkPos {
fn from(IVec3 { x, y, z }: IVec3) -> Self {
Self::new(x, y, z)
}
}
impl From<ChunkPos> for IVec3 {
fn from(ChunkPos { x, y, z }: ChunkPos) -> Self {
Self::new(x, y, z)
}
}