use crate::bounding_volume::Aabb;
use crate::math::{ivect_to_vect, IVector, Int, Real, Vector};
use crate::shape::{VoxelData, VoxelState, Voxels};
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub(super) struct VoxelsChunkHeader {
pub(super) id: usize,
pub(super) len: usize,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[repr(C)]
#[repr(align(64))]
pub(super) struct VoxelsChunk {
#[cfg_attr(feature = "serde-serialize", serde(with = "serde_arrays"))]
pub(super) states: [VoxelState; VoxelsChunk::VOXELS_PER_CHUNK],
}
impl Default for VoxelsChunk {
fn default() -> Self {
Self {
states: [VoxelState::EMPTY; VoxelsChunk::VOXELS_PER_CHUNK],
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct VoxelIndex {
pub(super) chunk_id: usize,
pub(super) id_in_chunk: usize,
}
impl VoxelIndex {
pub fn flat_id(&self) -> usize {
self.chunk_id * VoxelsChunk::VOXELS_PER_CHUNK + self.id_in_chunk
}
pub fn from_flat_id(id: usize) -> Self {
Self {
chunk_id: id / VoxelsChunk::VOXELS_PER_CHUNK,
id_in_chunk: id % VoxelsChunk::VOXELS_PER_CHUNK,
}
}
}
impl VoxelsChunk {
#[cfg(feature = "dim2")]
pub(super) const VOXELS_PER_CHUNK_DIM: usize = 16;
#[cfg(feature = "dim3")]
pub(super) const VOXELS_PER_CHUNK_DIM: usize = 8;
#[cfg(feature = "dim2")]
pub(super) const VOXELS_PER_CHUNK: usize =
Self::VOXELS_PER_CHUNK_DIM * Self::VOXELS_PER_CHUNK_DIM;
#[cfg(feature = "dim3")]
pub(super) const VOXELS_PER_CHUNK: usize =
Self::VOXELS_PER_CHUNK_DIM * Self::VOXELS_PER_CHUNK_DIM * Self::VOXELS_PER_CHUNK_DIM;
#[cfg(feature = "dim2")]
pub(super) const INVALID_CHUNK_KEY: IVector = IVector::new(Int::MAX, Int::MAX);
#[cfg(feature = "dim3")]
pub(super) const INVALID_CHUNK_KEY: IVector = IVector::new(Int::MAX, Int::MAX, Int::MAX);
#[cfg(feature = "dim2")]
pub(super) fn voxel_key_at_id(chunk_key: IVector, id_in_chunk: u32) -> IVector {
let y = id_in_chunk as Int / Self::VOXELS_PER_CHUNK_DIM as Int;
let x = id_in_chunk as Int % Self::VOXELS_PER_CHUNK_DIM as Int;
chunk_key * (Self::VOXELS_PER_CHUNK_DIM as Int) + IVector::new(x, y)
}
#[cfg(feature = "dim3")]
pub(super) fn voxel_key_at_id(chunk_key: IVector, id_in_chunk: u32) -> IVector {
let d0d1 = (Self::VOXELS_PER_CHUNK_DIM * Self::VOXELS_PER_CHUNK_DIM) as u32;
let z = id_in_chunk / d0d1;
let y = (id_in_chunk - z * d0d1) / Self::VOXELS_PER_CHUNK_DIM as u32;
let x = id_in_chunk % Self::VOXELS_PER_CHUNK_DIM as u32;
chunk_key * (Self::VOXELS_PER_CHUNK_DIM as Int) + IVector::new(x as Int, y as Int, z as Int)
}
pub(super) fn keys_bounds(chunk_key: &IVector) -> [IVector; 2] {
let imins = chunk_key * Self::VOXELS_PER_CHUNK_DIM as Int;
let imaxs = imins + IVector::splat(Self::VOXELS_PER_CHUNK_DIM as Int);
[imins, imaxs]
}
pub(super) fn aabb(chunk_key: &IVector, voxel_size: Vector) -> Aabb {
let [imins, imaxs] = Self::keys_bounds(chunk_key);
let aabb = Aabb::new(ivect_to_vect(imins), ivect_to_vect(imaxs));
Aabb::new(aabb.mins * voxel_size, aabb.maxs * voxel_size)
}
}
#[derive(Copy, Clone)]
pub struct VoxelsChunkRef<'a> {
pub my_id: usize,
pub parent: &'a Voxels,
pub states: &'a [VoxelState; VoxelsChunk::VOXELS_PER_CHUNK],
pub key: &'a IVector,
}
impl<'a> VoxelsChunkRef<'a> {
pub fn local_aabb(&self) -> Aabb {
VoxelsChunk::aabb(self.key, self.parent.voxel_size)
}
pub fn domain(&self) -> [IVector; 2] {
VoxelsChunk::keys_bounds(self.key)
}
pub fn voxel_at_point_unchecked(&self, pt: Vector) -> IVector {
self.parent.voxel_at_point(pt)
}
pub fn voxel_state(&self, voxel_key: IVector) -> Option<VoxelState> {
let (chunk_key, id_in_chunk) = Voxels::chunk_key_and_id_in_chunk(voxel_key);
if &chunk_key != self.key {
return None;
}
Some(self.states[id_in_chunk])
}
pub fn clamp_voxel(&self, voxel_key: IVector) -> IVector {
let [mins, maxs] = self.domain();
voxel_key.clamp(mins, maxs)
}
pub fn voxel_aabb_unchecked(&self, voxel_key: IVector) -> Aabb {
self.parent.voxel_aabb(voxel_key)
}
pub fn flat_id(&self, voxel_key: IVector) -> Option<u32> {
let (chunk_key, id_in_chunk) = Voxels::chunk_key_and_id_in_chunk(voxel_key);
if &chunk_key != self.key {
return None;
}
Some(
VoxelIndex {
chunk_id: self.my_id,
id_in_chunk,
}
.flat_id() as u32,
)
}
pub fn voxels(&self) -> impl Iterator<Item = VoxelData> + '_ {
let range = self.domain();
self.voxels_in_range(range[0], range[1])
}
#[cfg(feature = "dim2")]
pub fn voxels_in_range(
self,
mins: IVector,
maxs: IVector,
) -> impl Iterator<Item = VoxelData> + use<'a> {
let [chunk_mins, chunk_maxs] = VoxelsChunk::keys_bounds(self.key);
let mins = mins.max(chunk_mins);
let maxs = maxs.min(chunk_maxs);
(mins[0]..maxs[0]).flat_map(move |ix| {
(mins[1]..maxs[1]).flat_map(move |iy| {
let id_in_chunk = (ix - chunk_mins[0]) as usize
+ (iy - chunk_mins[1]) as usize * VoxelsChunk::VOXELS_PER_CHUNK_DIM;
let state = self.states[id_in_chunk];
if state.is_empty() {
return None;
}
let grid_coords = IVector::new(ix, iy);
let center =
Vector::new(ix as Real + 0.5, iy as Real + 0.5) * (self.parent.voxel_size);
Some(VoxelData {
linear_id: VoxelIndex {
chunk_id: self.my_id,
id_in_chunk,
},
grid_coords,
center,
state,
})
})
})
}
#[cfg(feature = "dim3")]
pub fn voxels_in_range(
self,
mins: IVector,
maxs: IVector,
) -> impl Iterator<Item = VoxelData> + use<'a> {
let [chunk_mins, chunk_maxs] = VoxelsChunk::keys_bounds(self.key);
let mins = mins.max(chunk_mins);
let maxs = maxs.min(chunk_maxs);
(mins[0]..maxs[0]).flat_map(move |ix| {
(mins[1]..maxs[1]).flat_map(move |iy| {
(mins[2]..maxs[2]).filter_map(move |iz| {
let id_in_chunk = (ix - chunk_mins[0]) as usize
+ (iy - chunk_mins[1]) as usize * VoxelsChunk::VOXELS_PER_CHUNK_DIM
+ (iz - chunk_mins[2]) as usize
* VoxelsChunk::VOXELS_PER_CHUNK_DIM
* VoxelsChunk::VOXELS_PER_CHUNK_DIM;
let state = self.states[id_in_chunk];
if state.is_empty() {
return None;
}
let grid_coords = IVector::new(ix, iy, iz);
let center = Vector::new(ix as Real + 0.5, iy as Real + 0.5, iz as Real + 0.5)
* (self.parent.voxel_size);
Some(VoxelData {
linear_id: VoxelIndex {
chunk_id: self.my_id,
id_in_chunk,
},
grid_coords,
center,
state,
})
})
})
})
}
}