- 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