use super::{
VoxelIndex, EMPTY_FACE_MASK, FACES_TO_FEATURE_MASKS, FACES_TO_OCTANT_MASKS,
FACES_TO_VOXEL_TYPES, INTERIOR_FACE_MASK,
};
use crate::bounding_volume::{Aabb, BoundingVolume};
#[cfg(not(feature = "std"))]
use crate::math::ComplexField;
use crate::math::{ivect_to_vect, vect_to_ivect, IVector, Int, Vector};
use crate::partitioning::{Bvh, BvhBuildStrategy, BvhNode};
use crate::shape::voxels::voxels_chunk::{VoxelsChunk, VoxelsChunkHeader};
use crate::shape::VoxelsChunkRef;
use crate::utils::hashmap::HashMap;
use alloc::{vec, vec::Vec};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum VoxelType {
Empty,
Vertex,
#[cfg(feature = "dim3")]
Edge,
Face,
Interior,
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct AxisMask(u8);
bitflags::bitflags! {
impl AxisMask: u8 {
const X_POS = 1 << 0;
const X_NEG = 1 << 1;
const Y_POS = 1 << 2;
const Y_NEG = 1 << 3;
#[cfg(feature= "dim3")]
const Z_POS = 1 << 4;
#[cfg(feature= "dim3")]
const Z_NEG = 1 << 5;
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct OctantPattern;
#[cfg(feature = "dim3")]
impl OctantPattern {
pub const INTERIOR: u32 = 0;
pub const VERTEX: u32 = 1;
pub const EDGE_X: u32 = 2;
pub const EDGE_Y: u32 = 3;
pub const EDGE_Z: u32 = 4;
pub const FACE_X: u32 = 5;
pub const FACE_Y: u32 = 6;
pub const FACE_Z: u32 = 7;
}
#[cfg(feature = "dim2")]
impl OctantPattern {
pub const INTERIOR: u32 = 0;
pub const VERTEX: u32 = 1;
pub const FACE_X: u32 = 2;
pub const FACE_Y: u32 = 3;
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub struct VoxelState(pub(super) u8);
impl VoxelState {
pub const EMPTY: VoxelState = VoxelState(EMPTY_FACE_MASK);
pub const INTERIOR: VoxelState = VoxelState(INTERIOR_FACE_MASK);
pub(crate) const fn new(state: u8) -> Self {
Self(state)
}
pub const fn is_empty(self) -> bool {
self.0 == EMPTY_FACE_MASK
}
pub const fn free_faces(self) -> AxisMask {
if self.0 == INTERIOR_FACE_MASK || self.0 == EMPTY_FACE_MASK {
AxisMask::empty()
} else {
AxisMask::from_bits_truncate((!self.0) & INTERIOR_FACE_MASK)
}
}
pub const fn voxel_type(self) -> VoxelType {
FACES_TO_VOXEL_TYPES[self.0 as usize]
}
pub(crate) const fn feature_mask(self) -> u16 {
FACES_TO_FEATURE_MASKS[self.0 as usize]
}
pub(crate) const fn octant_mask(self) -> u32 {
FACES_TO_OCTANT_MASKS[self.0 as usize]
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct VoxelData {
pub linear_id: VoxelIndex,
pub grid_coords: IVector,
pub center: Vector,
pub state: VoxelState,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub struct Voxels {
pub(super) chunk_bvh: Bvh,
pub(super) chunk_headers: HashMap<IVector, VoxelsChunkHeader>,
pub(super) chunk_keys: Vec<IVector>,
pub(super) chunks: Vec<VoxelsChunk>,
pub(super) free_chunks: Vec<usize>,
pub(super) voxel_size: Vector,
}
impl Voxels {
pub fn new(voxel_size: Vector, grid_coordinates: &[IVector]) -> Self {
let mut result = Self {
chunk_bvh: Bvh::new(),
chunk_headers: HashMap::default(),
chunk_keys: vec![],
chunks: vec![],
free_chunks: vec![],
voxel_size,
};
for vox in grid_coordinates {
let (chunk_key, id_in_chunk) = Self::chunk_key_and_id_in_chunk(*vox);
let chunk_header = result.chunk_headers.entry(chunk_key).or_insert_with(|| {
let id = result.chunks.len();
result.chunks.push(VoxelsChunk::default());
result.chunk_keys.push(chunk_key);
VoxelsChunkHeader { id, len: 0 }
});
chunk_header.len += 1;
result.chunks[chunk_header.id].states[id_in_chunk] = VoxelState::INTERIOR;
}
result.chunk_bvh = Bvh::from_iter(
BvhBuildStrategy::Ploc,
result.chunk_headers.iter().map(|(chunk_key, chunk_id)| {
(chunk_id.id, VoxelsChunk::aabb(chunk_key, result.voxel_size))
}),
);
result.recompute_all_voxels_states();
result
}
pub fn from_points(voxel_size: Vector, points: &[Vector]) -> Self {
let voxels: Vec<_> = points
.iter()
.map(|pt| vect_to_ivect((*pt / voxel_size).floor()))
.collect();
Self::new(voxel_size, &voxels)
}
pub(crate) fn chunk_bvh(&self) -> &Bvh {
&self.chunk_bvh
}
pub fn domain(&self) -> [IVector; 2] {
let aabb = self.chunk_bvh.root_aabb();
let half_sz = self.voxel_size() / 2.0;
let mins = self.voxel_at_point(aabb.mins + half_sz);
let maxs = self.voxel_at_point(aabb.maxs - half_sz);
let one = IVector::splat(1);
[mins, maxs + one]
}
pub fn voxel_size(&self) -> Vector {
self.voxel_size
}
pub fn scaled(mut self, scale: Vector) -> Self {
self.voxel_size *= scale;
self
}
pub fn chunk_ref(&self, chunk_id: u32) -> VoxelsChunkRef<'_> {
VoxelsChunkRef {
my_id: chunk_id as usize,
parent: self,
states: &self.chunks[chunk_id as usize].states,
key: &self.chunk_keys[chunk_id as usize],
}
}
pub fn voxel_aabb(&self, key: IVector) -> Aabb {
let center = self.voxel_center(key);
let hext = self.voxel_size / 2.0;
Aabb::from_half_extents(center, hext)
}
pub fn voxel_state(&self, key: IVector) -> Option<VoxelState> {
let vid = self.linear_index(key)?;
Some(self.chunks[vid.chunk_id].states[vid.id_in_chunk])
}
pub fn voxel_at_point(&self, point: Vector) -> IVector {
vect_to_ivect((point / self.voxel_size).floor())
}
pub fn voxel_at_flat_id(&self, id: u32) -> Option<IVector> {
let vid = VoxelIndex::from_flat_id(id as usize);
let chunk_key = self.chunk_keys.get(vid.chunk_id)?;
if *chunk_key == VoxelsChunk::INVALID_CHUNK_KEY {
return None;
}
Some(VoxelsChunk::voxel_key_at_id(
*chunk_key,
vid.id_in_chunk as u32,
))
}
pub fn voxel_range_intersecting_local_aabb(&self, aabb: &Aabb) -> [IVector; 2] {
let mins = vect_to_ivect((aabb.mins / self.voxel_size).floor());
let maxs = vect_to_ivect((aabb.maxs / self.voxel_size).ceil());
[mins, maxs]
}
pub fn voxel_range_aabb(&self, mins: IVector, maxs: IVector) -> Aabb {
Aabb {
mins: ivect_to_vect(mins) * self.voxel_size,
maxs: ivect_to_vect(maxs) * self.voxel_size,
}
}
pub fn align_aabb_to_grid(&self, aabb: &Aabb) -> Aabb {
let mins = (aabb.mins / self.voxel_size).floor() * self.voxel_size;
let maxs = (aabb.maxs / self.voxel_size).ceil() * self.voxel_size;
Aabb { mins, maxs }
}
pub fn voxels_intersecting_local_aabb(
&self,
aabb: &Aabb,
) -> impl Iterator<Item = VoxelData> + '_ {
let [mins, maxs] = self.voxel_range_intersecting_local_aabb(aabb);
self.voxels_in_range(mins, maxs)
}
pub fn voxels(&self) -> impl Iterator<Item = VoxelData> + '_ {
let aabb = self.chunk_bvh.root_aabb();
self.voxels_in_range(
self.voxel_at_point(aabb.mins),
self.voxel_at_point(aabb.maxs),
)
}
pub fn voxels_in_range(
&self,
mins: IVector,
maxs: IVector,
) -> impl Iterator<Item = VoxelData> + '_ {
let range_aabb = Aabb::new(self.voxel_center(mins), self.voxel_center(maxs));
self.chunk_bvh
.leaves(move |node: &BvhNode| node.aabb().intersects(&range_aabb))
.flat_map(move |chunk_id| {
let chunk = self.chunk_ref(chunk_id);
chunk.voxels_in_range(mins, maxs)
})
}
fn voxel_to_chunk_key(voxel_key: IVector) -> IVector {
fn div_floor(a: Int, b: usize) -> Int {
let sign = (a < 0) as Int;
(a + sign) / b as Int - sign
}
#[cfg(feature = "dim2")]
{
IVector::new(
div_floor(voxel_key.x, VoxelsChunk::VOXELS_PER_CHUNK_DIM),
div_floor(voxel_key.y, VoxelsChunk::VOXELS_PER_CHUNK_DIM),
)
}
#[cfg(feature = "dim3")]
{
IVector::new(
div_floor(voxel_key.x, VoxelsChunk::VOXELS_PER_CHUNK_DIM),
div_floor(voxel_key.y, VoxelsChunk::VOXELS_PER_CHUNK_DIM),
div_floor(voxel_key.z, VoxelsChunk::VOXELS_PER_CHUNK_DIM),
)
}
}
#[cfg(feature = "dim2")]
pub(super) fn chunk_key_and_id_in_chunk(voxel_key: IVector) -> (IVector, usize) {
let chunk_key = Self::voxel_to_chunk_key(voxel_key);
let voxel_key_in_chunk = voxel_key - chunk_key * VoxelsChunk::VOXELS_PER_CHUNK_DIM as Int;
let id_in_chunk = (voxel_key_in_chunk.x
+ voxel_key_in_chunk.y * VoxelsChunk::VOXELS_PER_CHUNK_DIM as Int)
as usize;
(chunk_key, id_in_chunk)
}
#[cfg(feature = "dim3")]
pub(super) fn chunk_key_and_id_in_chunk(voxel_key: IVector) -> (IVector, usize) {
let chunk_key = Self::voxel_to_chunk_key(voxel_key);
let voxel_key_in_chunk = voxel_key - chunk_key * VoxelsChunk::VOXELS_PER_CHUNK_DIM as Int;
let id_in_chunk = (voxel_key_in_chunk.x
+ voxel_key_in_chunk.y * VoxelsChunk::VOXELS_PER_CHUNK_DIM as Int
+ voxel_key_in_chunk.z
* VoxelsChunk::VOXELS_PER_CHUNK_DIM as Int
* VoxelsChunk::VOXELS_PER_CHUNK_DIM as Int) as usize;
(chunk_key, id_in_chunk)
}
pub fn linear_index(&self, voxel_key: IVector) -> Option<VoxelIndex> {
let (chunk_key, id_in_chunk) = Self::chunk_key_and_id_in_chunk(voxel_key);
let chunk_id = self.chunk_headers.get(&chunk_key)?.id;
Some(VoxelIndex {
chunk_id,
id_in_chunk,
})
}
pub fn voxel_center(&self, key: IVector) -> Vector {
(ivect_to_vect(key) + Vector::splat(0.5)) * self.voxel_size
}
}