- Add `Neighbor` enum - Add `USize3` struct - Remove `BloxelView` trait - Remove `generic_math` module - Add `ChunkedBloxelView` helper struct which contains neighboring chunk data to pass to mesh generator - Add and use a whole bunch of helper functions, like `BloxelStoreMut::update` and `BloxelArray::from_fn` - Use `ndarray` and `cart_prod` crates to simplify codemain
							parent
							
								
									877f72a1e0
								
							
						
					
					
						commit
						8ce5513e77
					
				
				 24 changed files with 561 additions and 631 deletions
			
			
		@ -1,5 +0,0 @@ | 
				
			||||
mod pos; | 
				
			||||
mod region; | 
				
			||||
 | 
				
			||||
pub use pos::Pos; | 
				
			||||
pub use region::Region; | 
				
			||||
@ -1,213 +0,0 @@ | 
				
			||||
use std::{ | 
				
			||||
    marker::PhantomData, | 
				
			||||
    ops::{Add, AddAssign, Index, IndexMut, Sub, SubAssign}, | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
use bevy::{ | 
				
			||||
    ecs::component::{Component, Immutable}, | 
				
			||||
    math::{Dir3, IVec3, InvalidDirectionError}, | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
use super::Region; | 
				
			||||
 | 
				
			||||
pub struct Pos<T> { | 
				
			||||
    pub x: i32, | 
				
			||||
    pub y: i32, | 
				
			||||
    pub z: i32, | 
				
			||||
    _marker: PhantomData<T>, | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> Pos<T> { | 
				
			||||
    pub const ORIGIN: Self = Self::new(0, 0, 0); | 
				
			||||
 | 
				
			||||
    pub const fn new(x: i32, y: i32, z: i32) -> Self { | 
				
			||||
        Self { | 
				
			||||
            x, | 
				
			||||
            y, | 
				
			||||
            z, | 
				
			||||
            _marker: PhantomData, | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn region(self) -> Region<T> { | 
				
			||||
        Region::new_unchecked(self.into(), self.into()) | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    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 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)) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> std::fmt::Display for Pos<T> { | 
				
			||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | 
				
			||||
        write!(f, "[{}, {}, {}]", self.x, self.y, self.z) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> std::fmt::Debug for Pos<T> { | 
				
			||||
    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | 
				
			||||
        fmt.debug_tuple(stringify!(Pos<T>)) | 
				
			||||
            .field(&self.x) | 
				
			||||
            .field(&self.y) | 
				
			||||
            .field(&self.z) | 
				
			||||
            .finish() | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Trait implementations that SHOULD be handled by #[derive]
 | 
				
			||||
 | 
				
			||||
use bevy::ecs::component::StorageType; | 
				
			||||
impl<T: Send + Sync + 'static> Component for Pos<T> { | 
				
			||||
    const STORAGE_TYPE: StorageType = StorageType::Table; | 
				
			||||
    type Mutability = Immutable; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> Default for Pos<T> { | 
				
			||||
    fn default() -> Self { | 
				
			||||
        Self { | 
				
			||||
            x: 0, | 
				
			||||
            y: 0, | 
				
			||||
            z: 0, | 
				
			||||
            _marker: PhantomData, | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> Clone for Pos<T> { | 
				
			||||
    fn clone(&self) -> Self { | 
				
			||||
        *self | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> Copy for Pos<T> {} | 
				
			||||
 | 
				
			||||
impl<T> PartialEq for Pos<T> { | 
				
			||||
    fn eq(&self, other: &Self) -> bool { | 
				
			||||
        (self.x, self.y, self.z).eq(&(other.x, other.y, other.z)) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> Eq for Pos<T> {} | 
				
			||||
 | 
				
			||||
impl<T> std::hash::Hash for Pos<T> { | 
				
			||||
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) { | 
				
			||||
        (self.x, self.y, self.z).hash(state) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Conversion
 | 
				
			||||
 | 
				
			||||
impl<T> From<(i32, i32, i32)> for Pos<T> { | 
				
			||||
    fn from((x, y, z): (i32, i32, i32)) -> Self { | 
				
			||||
        Self::new(x, y, z) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
impl<T> From<Pos<T>> for (i32, i32, i32) { | 
				
			||||
    fn from(Pos { x, y, z, .. }: Pos<T>) -> Self { | 
				
			||||
        (x, y, z) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> From<[i32; 3]> for Pos<T> { | 
				
			||||
    fn from([x, y, z]: [i32; 3]) -> Self { | 
				
			||||
        Self::new(x, y, z) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
impl<T> From<Pos<T>> for [i32; 3] { | 
				
			||||
    fn from(Pos { x, y, z, .. }: Pos<T>) -> Self { | 
				
			||||
        [x, y, z] | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> From<IVec3> for Pos<T> { | 
				
			||||
    fn from(IVec3 { x, y, z }: IVec3) -> Self { | 
				
			||||
        Self::new(x, y, z) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
impl<T> From<Pos<T>> for IVec3 { | 
				
			||||
    fn from(Pos { x, y, z, .. }: Pos<T>) -> Self { | 
				
			||||
        Self::new(x, y, z) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Offsetting
 | 
				
			||||
 | 
				
			||||
impl<T> Add<IVec3> for Pos<T> { | 
				
			||||
    type Output = Pos<T>; | 
				
			||||
    fn add(self, rhs: IVec3) -> Self::Output { | 
				
			||||
        Self::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> Sub<IVec3> for Pos<T> { | 
				
			||||
    type Output = Pos<T>; | 
				
			||||
    fn sub(self, rhs: IVec3) -> Self::Output { | 
				
			||||
        Self::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> AddAssign<IVec3> for Pos<T> { | 
				
			||||
    fn add_assign(&mut self, rhs: IVec3) { | 
				
			||||
        self.x += rhs.x; | 
				
			||||
        self.y += rhs.y; | 
				
			||||
        self.z += rhs.z; | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> SubAssign<IVec3> for Pos<T> { | 
				
			||||
    fn sub_assign(&mut self, rhs: IVec3) { | 
				
			||||
        self.x -= rhs.x; | 
				
			||||
        self.y -= rhs.y; | 
				
			||||
        self.z -= rhs.z; | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Difference
 | 
				
			||||
 | 
				
			||||
impl<T> Sub<Pos<T>> for Pos<T> { | 
				
			||||
    type Output = IVec3; | 
				
			||||
    fn sub(self, rhs: Self) -> IVec3 { | 
				
			||||
        IVec3::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Indexing
 | 
				
			||||
 | 
				
			||||
impl<T> Index<usize> for Pos<T> { | 
				
			||||
    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<T> IndexMut<usize> for Pos<T> { | 
				
			||||
    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"), | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -1,184 +0,0 @@ | 
				
			||||
use std::{ | 
				
			||||
    marker::PhantomData, | 
				
			||||
    ops::{Add, AddAssign, Sub, SubAssign}, | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
use bevy::{ | 
				
			||||
    ecs::component::{Component, Immutable}, | 
				
			||||
    math::{IVec3, UVec3}, | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
use super::Pos; | 
				
			||||
 | 
				
			||||
pub struct Region<T> { | 
				
			||||
    min: IVec3, | 
				
			||||
    max: IVec3, | 
				
			||||
    _marker: PhantomData<T>, | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> Region<T> { | 
				
			||||
    pub fn new_unchecked(min: IVec3, max: IVec3) -> Self { | 
				
			||||
        Self { | 
				
			||||
            min, | 
				
			||||
            max, | 
				
			||||
            _marker: PhantomData, | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn new_checked(min: Pos<T>, max: Pos<T>) -> Result<Self, ()> { | 
				
			||||
        let min: IVec3 = min.into(); | 
				
			||||
        let max: IVec3 = max.into(); | 
				
			||||
        if min.x <= max.x && min.y <= max.y && min.z <= max.z { | 
				
			||||
            Ok(Self::new_unchecked(min, max)) | 
				
			||||
        } else { | 
				
			||||
            Err(()) | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn new(a: Pos<T>, b: Pos<T>) -> Self { | 
				
			||||
        let a: IVec3 = a.into(); | 
				
			||||
        let b: IVec3 = b.into(); | 
				
			||||
        let min = IVec3::new(a.x.min(b.x), a.y.min(b.y), a.z.min(b.z)); | 
				
			||||
        let max = IVec3::new(a.x.max(b.x), a.y.max(b.y), a.z.max(b.z)); | 
				
			||||
        Self::new_unchecked(min, max) | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn size(&self) -> UVec3 { | 
				
			||||
        (self.max - self.min + IVec3::ONE).as_uvec3() | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn contains(&self, pos: Pos<T>) -> bool { | 
				
			||||
        let pos: IVec3 = pos.into(); | 
				
			||||
        (pos.x >= self.min.x && pos.x <= self.max.x) | 
				
			||||
            && (pos.y >= self.min.y && pos.y <= self.max.y) | 
				
			||||
            && (pos.z >= self.min.z && pos.z <= self.max.z) | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn intersects(&self, other: Self) -> bool { | 
				
			||||
        (other.max.x > self.min.x && other.min.x < self.max.x) | 
				
			||||
            && (other.max.y > self.min.y && other.min.y < self.max.y) | 
				
			||||
            && (other.max.z > self.min.z && other.min.z < self.max.z) | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn distance_to(&self, pos: Pos<T>) -> f32 { | 
				
			||||
        (self.distance_to_squared(pos) as f32).sqrt() | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn distance_to_squared(&self, pos: Pos<T>) -> i32 { | 
				
			||||
        let pos: IVec3 = pos.into(); | 
				
			||||
        let clamped = pos.max(self.min).min(self.max); | 
				
			||||
        pos.distance_squared(clamped) | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn expand(&self, amount: i32) -> Result<Self, ()> { | 
				
			||||
        Self::new_checked( | 
				
			||||
            (self.min - IVec3::splat(amount)).into(), | 
				
			||||
            (self.max + IVec3::splat(amount)).into(), | 
				
			||||
        ) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> std::fmt::Display for Region<T> { | 
				
			||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | 
				
			||||
        write!(f, "{} to {}", self.min, self.max) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> std::fmt::Debug for Region<T> { | 
				
			||||
    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | 
				
			||||
        fmt.debug_struct(stringify!(Region<T>)) | 
				
			||||
            .field("min", &self.min) | 
				
			||||
            .field("max", &self.max) | 
				
			||||
            .finish() | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Trait implementations that SHOULD be handled by #[derive]
 | 
				
			||||
 | 
				
			||||
use bevy::ecs::component::StorageType; | 
				
			||||
impl<T: Send + Sync + 'static> Component for Region<T> { | 
				
			||||
    const STORAGE_TYPE: StorageType = StorageType::Table; | 
				
			||||
    type Mutability = Immutable; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> Default for Region<T> { | 
				
			||||
    fn default() -> Self { | 
				
			||||
        Self { | 
				
			||||
            min: IVec3::ZERO, | 
				
			||||
            max: IVec3::ZERO, | 
				
			||||
            _marker: PhantomData, | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> Clone for Region<T> { | 
				
			||||
    fn clone(&self) -> Self { | 
				
			||||
        *self | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> Copy for Region<T> {} | 
				
			||||
 | 
				
			||||
impl<T> PartialEq for Region<T> { | 
				
			||||
    fn eq(&self, other: &Self) -> bool { | 
				
			||||
        (self.min, self.max).eq(&(other.min, other.max)) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> Eq for Region<T> {} | 
				
			||||
 | 
				
			||||
impl<T> std::hash::Hash for Region<T> { | 
				
			||||
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) { | 
				
			||||
        (self.min, self.max).hash(state) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Conversion
 | 
				
			||||
 | 
				
			||||
impl<T> From<Region<T>> for (Pos<T>, Pos<T>) { | 
				
			||||
    fn from(Region { min, max, .. }: Region<T>) -> Self { | 
				
			||||
        (min.into(), max.into()) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Offsetting
 | 
				
			||||
 | 
				
			||||
impl<T> Add<IVec3> for Region<T> { | 
				
			||||
    type Output = Region<T>; | 
				
			||||
    fn add(self, rhs: IVec3) -> Self::Output { | 
				
			||||
        Self::new_unchecked(self.min + rhs, self.max + rhs) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
impl<T> Add<IVec3> for &Region<T> { | 
				
			||||
    type Output = Region<T>; | 
				
			||||
    fn add(self, rhs: IVec3) -> Self::Output { | 
				
			||||
        Region::new_unchecked(self.min + rhs, self.max + rhs) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> Sub<IVec3> for Region<T> { | 
				
			||||
    type Output = Region<T>; | 
				
			||||
    fn sub(self, rhs: IVec3) -> Self::Output { | 
				
			||||
        Self::new_unchecked(self.min - rhs, self.max - rhs) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
impl<T> Sub<IVec3> for &Region<T> { | 
				
			||||
    type Output = Region<T>; | 
				
			||||
    fn sub(self, rhs: IVec3) -> Self::Output { | 
				
			||||
        Region::new_unchecked(self.min - rhs, self.max - rhs) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> AddAssign<IVec3> for Region<T> { | 
				
			||||
    fn add_assign(&mut self, rhs: IVec3) { | 
				
			||||
        self.min += rhs; | 
				
			||||
        self.max += rhs; | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T> SubAssign<IVec3> for Region<T> { | 
				
			||||
    fn sub_assign(&mut self, rhs: IVec3) { | 
				
			||||
        self.min -= rhs; | 
				
			||||
        self.max -= rhs; | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -0,0 +1,195 @@ | 
				
			||||
use std::ops::{self, Neg}; | 
				
			||||
 | 
				
			||||
use bevy::math::{Dir3, IVec3}; | 
				
			||||
use overload::overload; | 
				
			||||
 | 
				
			||||
use super::{BlockPos, ChunkPos}; | 
				
			||||
 | 
				
			||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] | 
				
			||||
pub enum Neighbor { | 
				
			||||
    Right,  // +X
 | 
				
			||||
    Left,   // -X
 | 
				
			||||
    Top,    // +Y
 | 
				
			||||
    Bottom, // -Y
 | 
				
			||||
    Front,  // +Z
 | 
				
			||||
    Back,   // +Z
 | 
				
			||||
 | 
				
			||||
    RightTop, | 
				
			||||
    LeftTop, | 
				
			||||
    RightBottom, | 
				
			||||
    LeftBottom, | 
				
			||||
    RightFront, | 
				
			||||
    LeftFront, | 
				
			||||
    RightBack, | 
				
			||||
    LeftBack, | 
				
			||||
    TopFront, | 
				
			||||
    BottomFront, | 
				
			||||
    TopBack, | 
				
			||||
    BottomBack, | 
				
			||||
 | 
				
			||||
    RightTopFront, | 
				
			||||
    LeftTopFront, | 
				
			||||
    RightBottomFront, | 
				
			||||
    LeftBottomFront, | 
				
			||||
    RightTopBack, | 
				
			||||
    LeftTopBack, | 
				
			||||
    RightBottomBack, | 
				
			||||
    LeftBottomBack, | 
				
			||||
} | 
				
			||||
 | 
				
			||||
#[rustfmt::skip] | 
				
			||||
impl Neighbor { | 
				
			||||
    /// All neighbors that share a face. Also known as "orthogonal" or "direct" neighbors.
 | 
				
			||||
    pub const FACE: [Self; 6] = [ Self::Right, Self::Left, Self::Top, Self::Bottom, Self::Front, Self::Back ]; | 
				
			||||
 | 
				
			||||
    /// All neighbors that share an edge and only an edge.
 | 
				
			||||
    pub const EDGE: [Self; 12] = [ Self::RightTop, Self::LeftTop, Self::RightBottom, Self::LeftBottom, Self::RightFront, Self::LeftFront, | 
				
			||||
                                   Self::RightBack, Self::LeftBack, Self::TopFront, Self::BottomFront, Self::TopBack, Self::BottomBack ]; | 
				
			||||
 | 
				
			||||
    /// All neighbors that share a corner and only a corner.
 | 
				
			||||
    pub const CORNER: [Self; 8] = [ Self::RightTopFront, Self::LeftTopFront, Self::RightBottomFront, Self::LeftBottomFront, | 
				
			||||
                                    Self::RightTopBack, Self::LeftTopBack, Self::RightBottomBack, Self::LeftBottomBack ]; | 
				
			||||
 | 
				
			||||
    pub const ALL: [Self; 26] = [ | 
				
			||||
        Self::Right, Self::Left, Self::Top, Self::Bottom, Self::Front, Self::Back, | 
				
			||||
        Self::RightTop, Self::LeftTop, Self::RightBottom, Self::LeftBottom, Self::RightFront, Self::LeftFront, | 
				
			||||
        Self::RightBack, Self::LeftBack, Self::TopFront, Self::BottomFront, Self::TopBack, Self::BottomBack, | 
				
			||||
        Self::RightTopFront, Self::LeftTopFront, Self::RightBottomFront, Self::LeftBottomFront, | 
				
			||||
        Self::RightTopBack, Self::LeftTopBack, Self::RightBottomBack, Self::LeftBottomBack, | 
				
			||||
    ]; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl From<Neighbor> for IVec3 { | 
				
			||||
    #[rustfmt::skip] | 
				
			||||
    fn from(n: Neighbor) -> Self { | 
				
			||||
        match n { | 
				
			||||
            Neighbor::Right  => Self::new( 1, 0, 0), | 
				
			||||
            Neighbor::Left   => Self::new(-1, 0, 0), | 
				
			||||
            Neighbor::Top    => Self::new(0,  1, 0), | 
				
			||||
            Neighbor::Bottom => Self::new(0, -1, 0), | 
				
			||||
            Neighbor::Front  => Self::new(0, 0,  1), | 
				
			||||
            Neighbor::Back   => Self::new(0, 0, -1), | 
				
			||||
 | 
				
			||||
            Neighbor::RightTop    => Self::new( 1,  1,  0), | 
				
			||||
            Neighbor::LeftTop     => Self::new(-1,  1,  0), | 
				
			||||
            Neighbor::RightBottom => Self::new( 1, -1,  0), | 
				
			||||
            Neighbor::LeftBottom  => Self::new(-1, -1,  0), | 
				
			||||
            Neighbor::RightFront  => Self::new( 1,  0,  1), | 
				
			||||
            Neighbor::LeftFront   => Self::new(-1,  0,  1), | 
				
			||||
            Neighbor::RightBack   => Self::new( 1,  0, -1), | 
				
			||||
            Neighbor::LeftBack    => Self::new(-1,  0, -1), | 
				
			||||
            Neighbor::TopFront    => Self::new( 0,  1,  1), | 
				
			||||
            Neighbor::BottomFront => Self::new( 0, -1,  1), | 
				
			||||
            Neighbor::TopBack     => Self::new( 0,  1, -1), | 
				
			||||
            Neighbor::BottomBack  => Self::new( 0, -1, -1), | 
				
			||||
 | 
				
			||||
            Neighbor::RightTopFront    => Self::new( 1,  1,  1), | 
				
			||||
            Neighbor::LeftTopFront     => Self::new(-1,  1,  1), | 
				
			||||
            Neighbor::RightBottomFront => Self::new( 1, -1,  1), | 
				
			||||
            Neighbor::LeftBottomFront  => Self::new(-1, -1,  1), | 
				
			||||
            Neighbor::RightTopBack     => Self::new( 1,  1, -1), | 
				
			||||
            Neighbor::LeftTopBack      => Self::new(-1,  1, -1), | 
				
			||||
            Neighbor::RightBottomBack  => Self::new( 1, -1, -1), | 
				
			||||
            Neighbor::LeftBottomBack   => Self::new(-1, -1, -1), | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl TryFrom<IVec3> for Neighbor { | 
				
			||||
    type Error = (); | 
				
			||||
    #[rustfmt::skip] | 
				
			||||
    fn try_from(v: IVec3) -> Result<Neighbor, Self::Error> { | 
				
			||||
        match v { | 
				
			||||
            IVec3 { x:  1, y: 0, z: 0 } => Ok(Self::Right), | 
				
			||||
            IVec3 { x: -1, y: 0, z: 0 } => Ok(Self::Left), | 
				
			||||
            IVec3 { x: 0, y:  1, z: 0 } => Ok(Self::Top), | 
				
			||||
            IVec3 { x: 0, y: -1, z: 0 } => Ok(Self::Bottom), | 
				
			||||
            IVec3 { x: 0, y: 0, z:  1 } => Ok(Self::Front), | 
				
			||||
            IVec3 { x: 0, y: 0, z: -1 } => Ok(Self::Back), | 
				
			||||
 | 
				
			||||
            IVec3 { x:  1, y:  1, z:  0 } => Ok(Self::RightTop), | 
				
			||||
            IVec3 { x: -1, y:  1, z:  0 } => Ok(Self::LeftTop), | 
				
			||||
            IVec3 { x:  1, y: -1, z:  0 } => Ok(Self::RightBottom), | 
				
			||||
            IVec3 { x: -1, y: -1, z:  0 } => Ok(Self::LeftBottom), | 
				
			||||
            IVec3 { x:  1, y:  0, z:  1 } => Ok(Self::RightFront), | 
				
			||||
            IVec3 { x: -1, y:  0, z:  1 } => Ok(Self::LeftFront), | 
				
			||||
            IVec3 { x:  1, y:  0, z: -1 } => Ok(Self::RightBack), | 
				
			||||
            IVec3 { x: -1, y:  0, z: -1 } => Ok(Self::LeftBack), | 
				
			||||
            IVec3 { x:  0, y:  1, z:  1 } => Ok(Self::TopFront), | 
				
			||||
            IVec3 { x:  0, y: -1, z:  1 } => Ok(Self::BottomFront), | 
				
			||||
            IVec3 { x:  0, y:  1, z: -1 } => Ok(Self::TopBack), | 
				
			||||
            IVec3 { x:  0, y: -1, z: -1 } => Ok(Self::BottomBack), | 
				
			||||
 | 
				
			||||
            IVec3 { x:  1, y:  1, z:  1 } => Ok(Self::RightTopFront), | 
				
			||||
            IVec3 { x: -1, y:  1, z:  1 } => Ok(Self::LeftTopFront), | 
				
			||||
            IVec3 { x:  1, y: -1, z:  1 } => Ok(Self::RightBottomFront), | 
				
			||||
            IVec3 { x: -1, y: -1, z:  1 } => Ok(Self::LeftBottomFront), | 
				
			||||
            IVec3 { x:  1, y:  1, z: -1 } => Ok(Self::RightTopBack), | 
				
			||||
            IVec3 { x: -1, y:  1, z: -1 } => Ok(Self::LeftTopBack), | 
				
			||||
            IVec3 { x:  1, y: -1, z: -1 } => Ok(Self::RightBottomBack), | 
				
			||||
            IVec3 { x: -1, y: -1, z: -1 } => Ok(Self::LeftBottomBack), | 
				
			||||
 | 
				
			||||
            _ => Err(()), | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl From<Neighbor> for Dir3 { | 
				
			||||
    fn from(n: Neighbor) -> Dir3 { | 
				
			||||
        Self::new(IVec3::from(n).as_vec3()).unwrap() | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl Neg for Neighbor { | 
				
			||||
    type Output = Neighbor; | 
				
			||||
    #[rustfmt::skip] | 
				
			||||
    fn neg(self) -> Self::Output { | 
				
			||||
        match self { | 
				
			||||
            Self::Right  => Self::Left, | 
				
			||||
            Self::Left   => Self::Right, | 
				
			||||
            Self::Top    => Self::Bottom, | 
				
			||||
            Self::Bottom => Self::Top, | 
				
			||||
            Self::Front  => Self::Back, | 
				
			||||
            Self::Back   => Self::Front, | 
				
			||||
 | 
				
			||||
            Self::RightTop    => Self::LeftBottom, | 
				
			||||
            Self::LeftTop     => Self::RightBottom, | 
				
			||||
            Self::RightBottom => Self::LeftTop, | 
				
			||||
            Self::LeftBottom  => Self::RightTop, | 
				
			||||
            Self::RightFront  => Self::LeftBack, | 
				
			||||
            Self::LeftFront   => Self::RightBack, | 
				
			||||
            Self::RightBack   => Self::LeftFront, | 
				
			||||
            Self::LeftBack    => Self::RightFront, | 
				
			||||
            Self::TopFront    => Self::BottomBack, | 
				
			||||
            Self::BottomFront => Self::TopBack, | 
				
			||||
            Self::TopBack     => Self::BottomFront, | 
				
			||||
            Self::BottomBack  => Self::TopFront, | 
				
			||||
 | 
				
			||||
            Self::RightTopFront    => Self::LeftBottomBack, | 
				
			||||
            Self::LeftTopFront     => Self::RightBottomBack, | 
				
			||||
            Self::RightBottomFront => Self::LeftTopBack, | 
				
			||||
            Self::LeftBottomFront  => Self::RightTopBack, | 
				
			||||
            Self::RightTopBack     => Self::LeftBottomFront, | 
				
			||||
            Self::LeftTopBack      => Self::RightBottomFront, | 
				
			||||
            Self::RightBottomBack  => Self::LeftTopFront, | 
				
			||||
            Self::LeftBottomBack   => Self::RightTopFront, | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Offsetting
 | 
				
			||||
 | 
				
			||||
overload!((l: ?IVec3) + (r: Neighbor) -> IVec3 { l + IVec3::from(r) }); | 
				
			||||
overload!((l: ?IVec3) - (r: Neighbor) -> IVec3 { l - IVec3::from(r) }); | 
				
			||||
overload!((s: &mut IVec3) += (v: Neighbor) { s.add_assign(IVec3::from(v)) }); | 
				
			||||
overload!((s: &mut IVec3) -= (v: Neighbor) { s.sub_assign(IVec3::from(v)) }); | 
				
			||||
 | 
				
			||||
overload!((l: ?BlockPos) + (r: Neighbor) -> BlockPos { l + IVec3::from(r) }); | 
				
			||||
overload!((l: ?BlockPos) - (r: Neighbor) -> BlockPos { l - IVec3::from(r) }); | 
				
			||||
overload!((s: &mut BlockPos) += (v: Neighbor) { s.add_assign(IVec3::from(v)) }); | 
				
			||||
overload!((s: &mut BlockPos) -= (v: Neighbor) { s.sub_assign(IVec3::from(v)) }); | 
				
			||||
 | 
				
			||||
overload!((l: ?ChunkPos) + (r: Neighbor) -> ChunkPos { l + IVec3::from(r) }); | 
				
			||||
overload!((l: ?ChunkPos) - (r: Neighbor) -> ChunkPos { l - IVec3::from(r) }); | 
				
			||||
overload!((s: &mut ChunkPos) += (v: Neighbor) { s.add_assign(IVec3::from(v)) }); | 
				
			||||
overload!((s: &mut ChunkPos) -= (v: Neighbor) { s.sub_assign(IVec3::from(v)) }); | 
				
			||||
@ -0,0 +1,80 @@ | 
				
			||||
use std::num::{NonZeroU32, TryFromIntError}; | 
				
			||||
 | 
				
			||||
use bevy::math::{IVec3, UVec3}; | 
				
			||||
 | 
				
			||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] | 
				
			||||
pub struct USize3 { | 
				
			||||
    pub width: NonZeroU32, | 
				
			||||
    pub height: NonZeroU32, | 
				
			||||
    pub depth: NonZeroU32, | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl USize3 { | 
				
			||||
    pub fn new(width: NonZeroU32, height: NonZeroU32, depth: NonZeroU32) -> Self { | 
				
			||||
        Self { | 
				
			||||
            width, | 
				
			||||
            height, | 
				
			||||
            depth, | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn splat(length: NonZeroU32) -> Self { | 
				
			||||
        Self::new(length, length, length) | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn contains(&self, pos: IVec3) -> bool { | 
				
			||||
        (0..self.width.get() as i32).contains(&pos.x) | 
				
			||||
            && (0..self.height.get() as i32).contains(&pos.y) | 
				
			||||
            && (0..self.depth.get() as i32).contains(&pos.z) | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn ivec3_to_index(&self, pos: IVec3) -> Option<usize> { | 
				
			||||
        self.contains(pos).then(|| { | 
				
			||||
            let w = self.width.get() as i32; | 
				
			||||
            let h = self.height.get() as i32; | 
				
			||||
            (pos.x + (pos.y * w) + (pos.z * w * h)) as usize | 
				
			||||
        }) | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn index_to_ivec3(&self, index: usize) -> IVec3 { | 
				
			||||
        let i = index as i32; | 
				
			||||
        let w = self.width.get() as i32; | 
				
			||||
        let h = self.height.get() as i32; | 
				
			||||
        IVec3::new(i % w, i / w % h, i / w / h) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl From<USize3> for (NonZeroU32, NonZeroU32, NonZeroU32) { | 
				
			||||
    fn from(size: USize3) -> Self { | 
				
			||||
        (size.width, size.depth, size.height) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl From<USize3> for (u32, u32, u32) { | 
				
			||||
    fn from(size: USize3) -> Self { | 
				
			||||
        (size.width.get(), size.depth.get(), size.height.get()) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl TryFrom<UVec3> for USize3 { | 
				
			||||
    type Error = TryFromIntError; | 
				
			||||
    fn try_from(UVec3 { x, y, z }: UVec3) -> Result<Self, Self::Error> { | 
				
			||||
        Ok(Self::new(x.try_into()?, y.try_into()?, z.try_into()?)) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl From<USize3> for UVec3 { | 
				
			||||
    fn from(size: USize3) -> Self { | 
				
			||||
        Self::new(size.width.get(), size.height.get(), size.depth.get()) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl ndarray::IntoDimension for USize3 { | 
				
			||||
    type Dim = ndarray::Ix3; | 
				
			||||
    fn into_dimension(self) -> Self::Dim { | 
				
			||||
        let w = self.width.get() as usize; | 
				
			||||
        let h = self.height.get() as usize; | 
				
			||||
        let d = self.depth.get() as usize; | 
				
			||||
        ndarray::Ix3(w, h, d) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -1,74 +1,56 @@ | 
				
			||||
use std::ops::{Index, IndexMut}; | 
				
			||||
use bevy::math::IVec3; | 
				
			||||
use ndarray::Array3; | 
				
			||||
 | 
				
			||||
use bevy::math::{IVec3, UVec3}; | 
				
			||||
 | 
				
			||||
use super::bloxel_view::{ivec3_to_index, BloxelView, BloxelViewMut}; | 
				
			||||
use super::{BloxelStore, BloxelStoreMut}; | 
				
			||||
use crate::bloxel::math::USize3; | 
				
			||||
 | 
				
			||||
pub struct BloxelArray<T: Copy> { | 
				
			||||
    size: UVec3, | 
				
			||||
    data: Vec<T>, | 
				
			||||
    size: USize3, | 
				
			||||
    data: Array3<T>, | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T: Copy> BloxelArray<T> { | 
				
			||||
    pub fn new(size: UVec3, fill: T) -> Self { | 
				
			||||
        assert!(size.x > 0 && size.y > 0 && size.z > 0); | 
				
			||||
        let len = (size.x * size.y * size.z) as usize; | 
				
			||||
        let data = vec![fill; len]; | 
				
			||||
    pub fn new(size: USize3, fill: T) -> Self { | 
				
			||||
        let data = Array3::from_elem(size, fill); | 
				
			||||
        Self { size, data } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn new_with_default(size: UVec3) -> Self | 
				
			||||
    pub fn default(size: USize3) -> Self | 
				
			||||
    where | 
				
			||||
        T: Default, | 
				
			||||
    { | 
				
			||||
        Self::new(size, Default::default()) | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    fn get_index(&self, pos: impl Into<IVec3>) -> usize { | 
				
			||||
        let pos = pos.into(); | 
				
			||||
        assert!(self.contains(pos)); | 
				
			||||
        ivec3_to_index(pos, self.size) | 
				
			||||
        let data = Array3::default(size); | 
				
			||||
        Self { size, data } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T: Copy> Index<IVec3> for BloxelArray<T> { | 
				
			||||
    type Output = T; | 
				
			||||
 | 
				
			||||
    fn index(&self, pos: IVec3) -> &Self::Output { | 
				
			||||
        let index = self.get_index(pos); | 
				
			||||
        &self.data[index] | 
				
			||||
    pub fn from_fn(size: USize3, f: impl Fn(IVec3) -> T) -> Self { | 
				
			||||
        let f = |(x, y, z)| f(IVec3::new(x as i32, y as i32, z as i32)); | 
				
			||||
        let data = Array3::from_shape_fn(size, f); | 
				
			||||
        Self { size, data } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T: Copy> IndexMut<IVec3> for BloxelArray<T> { | 
				
			||||
    fn index_mut(&mut self, pos: IVec3) -> &mut Self::Output { | 
				
			||||
        let index = self.get_index(pos); | 
				
			||||
        &mut self.data[index] | 
				
			||||
    /// Helper function to construct a `[usize; 3]` to index into `data`, if valid, or `None` otherwise.
 | 
				
			||||
    fn to_index(&self, pos: IVec3) -> Option<[usize; 3]> { | 
				
			||||
        self.size | 
				
			||||
            .contains(pos) | 
				
			||||
            .then(|| [pos.x as usize, pos.y as usize, pos.z as usize]) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T: Copy> BloxelView<T> for BloxelArray<T> { | 
				
			||||
    fn size(&self) -> UVec3 { | 
				
			||||
impl<T: Copy> BloxelStore<T> for BloxelArray<T> { | 
				
			||||
    fn size(&self) -> USize3 { | 
				
			||||
        self.size | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    fn contains(&self, pos: impl Into<IVec3>) -> bool { | 
				
			||||
        let pos = pos.into(); | 
				
			||||
        let size = self.size.as_ivec3(); | 
				
			||||
        (pos.x >= 0 && pos.x < size.x) | 
				
			||||
            && (pos.y >= 0 && pos.y < size.y) | 
				
			||||
            && (pos.z >= 0 && pos.z < size.z) | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    fn get(&self, pos: impl Into<IVec3>) -> T { | 
				
			||||
        let index = self.get_index(pos); | 
				
			||||
        self.data[index] | 
				
			||||
    fn get(&self, pos: IVec3) -> Option<T> { | 
				
			||||
        self.to_index(pos).map(|index| self.data[index]) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl<T: Copy> BloxelViewMut<T> for BloxelArray<T> { | 
				
			||||
    fn set(&mut self, pos: impl Into<IVec3>, value: T) { | 
				
			||||
        let index = self.get_index(pos); | 
				
			||||
        self.data[index] = value; | 
				
			||||
impl<T: Copy> BloxelStoreMut<T> for BloxelArray<T> { | 
				
			||||
    fn set(&mut self, pos: IVec3, value: T) -> Result<T, ()> { | 
				
			||||
        self.to_index(pos) | 
				
			||||
            .map(|index| std::mem::replace(&mut self.data[index], value)) | 
				
			||||
            .ok_or(()) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
@ -0,0 +1,24 @@ | 
				
			||||
use bevy::math::{IVec3, UVec3}; | 
				
			||||
use cart_prod::specs::Hom3FCartProd; | 
				
			||||
 | 
				
			||||
use crate::bloxel::math::USize3; | 
				
			||||
 | 
				
			||||
pub trait BloxelStore<T: Copy> { | 
				
			||||
    fn size(&self) -> USize3; | 
				
			||||
 | 
				
			||||
    fn get(&self, pos: IVec3) -> Option<T>; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
pub trait BloxelStoreMut<T: Copy>: BloxelStore<T> { | 
				
			||||
    fn set(&mut self, pos: IVec3, value: T) -> Result<T, ()>; | 
				
			||||
 | 
				
			||||
    fn update(&mut self, f: impl Fn(IVec3, T) -> T) { | 
				
			||||
        let (w, h, d) = self.size().into(); | 
				
			||||
        for prod in Hom3FCartProd::new(0..w, 0..h, 0..d) { | 
				
			||||
            let pos = UVec3::from(prod).as_ivec3(); | 
				
			||||
            let old = self.get(pos).unwrap(); | 
				
			||||
            let new = f(pos, old); | 
				
			||||
            self.set(pos, new).unwrap(); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -1,28 +0,0 @@ | 
				
			||||
use bevy::math::{IVec3, UVec3}; | 
				
			||||
 | 
				
			||||
pub trait BloxelView<T> { | 
				
			||||
    fn size(&self) -> UVec3; | 
				
			||||
 | 
				
			||||
    fn contains(&self, pos: impl Into<IVec3>) -> bool; | 
				
			||||
 | 
				
			||||
    fn get(&self, pos: impl Into<IVec3>) -> T; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
pub trait BloxelViewMut<T>: BloxelView<T> { | 
				
			||||
    fn set(&mut self, pos: impl Into<IVec3>, value: T); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
pub fn ivec3_to_index(pos: impl Into<IVec3>, size: UVec3) -> usize { | 
				
			||||
    let pos = pos.into(); | 
				
			||||
    let size = size.as_ivec3(); | 
				
			||||
    (pos.x + (pos.y * size.x) + (pos.z * size.x * size.y)) as usize | 
				
			||||
} | 
				
			||||
 | 
				
			||||
pub fn index_to_ivec3(index: usize, size: UVec3) -> IVec3 { | 
				
			||||
    let size = size.as_ivec3(); | 
				
			||||
    let i = index as i32; | 
				
			||||
    let x = i % size.x; | 
				
			||||
    let y = i / size.x % size.y; | 
				
			||||
    let z = i / size.x / size.y; | 
				
			||||
    IVec3::new(x, y, z) | 
				
			||||
} | 
				
			||||
@ -1,10 +1,10 @@ | 
				
			||||
mod bloxel_array; | 
				
			||||
mod bloxel_view; | 
				
			||||
mod bloxel_store; | 
				
			||||
mod chunked_octree; | 
				
			||||
mod palette_bloxel_storage; | 
				
			||||
mod palette_storage; | 
				
			||||
 | 
				
			||||
pub use bloxel_array::*; | 
				
			||||
pub use bloxel_view::*; | 
				
			||||
pub use bloxel_store::*; | 
				
			||||
pub use chunked_octree::*; | 
				
			||||
pub use palette_bloxel_storage::*; | 
				
			||||
 | 
				
			||||
					Loading…
					
					
				
		Reference in new issue