use std::collections::HashSet;
use nalgebra_glm::{IVec3, Vec3};
pub const BRICK_SIZE: usize = 8;
pub const BRICK_CORNER_COUNT: usize = 9;
pub const GRID_SIZE: usize = 128;
#[cfg(target_arch = "wasm32")]
pub const ATLAS_SIZE: usize = 360;
#[cfg(not(target_arch = "wasm32"))]
pub const ATLAS_SIZE: usize = 450;
pub const BRICKS_PER_ATLAS_DIM: usize = ATLAS_SIZE / BRICK_CORNER_COUNT;
pub const MAX_BRICKS: usize = BRICKS_PER_ATLAS_DIM * BRICKS_PER_ATLAS_DIM * BRICKS_PER_ATLAS_DIM;
pub const EMPTY_BRICK: i32 = -1;
#[derive(Clone)]
pub struct BrickData {
pub distances: [f32; BRICK_CORNER_COUNT * BRICK_CORNER_COUNT * BRICK_CORNER_COUNT],
pub material_ids: [u32; BRICK_SIZE * BRICK_SIZE * BRICK_SIZE],
}
impl Default for BrickData {
fn default() -> Self {
Self {
distances: [f32::MAX; BRICK_CORNER_COUNT * BRICK_CORNER_COUNT * BRICK_CORNER_COUNT],
material_ids: [0; BRICK_SIZE * BRICK_SIZE * BRICK_SIZE],
}
}
}
impl BrickData {
pub fn get_corner_distance(&self, x: usize, y: usize, z: usize) -> f32 {
self.distances[x + y * BRICK_CORNER_COUNT + z * BRICK_CORNER_COUNT * BRICK_CORNER_COUNT]
}
pub fn set_corner_distance(&mut self, x: usize, y: usize, z: usize, distance: f32) {
self.distances[x + y * BRICK_CORNER_COUNT + z * BRICK_CORNER_COUNT * BRICK_CORNER_COUNT] =
distance;
}
pub fn has_surface(&self) -> bool {
let mut has_positive = false;
let mut has_negative = false;
for &distance in &self.distances {
if distance > 0.0 {
has_positive = true;
} else if distance < 0.0 {
has_negative = true;
}
if has_positive && has_negative {
return true;
}
}
false
}
}
#[derive(Clone)]
pub struct BrickPointerGrid {
pub pointers: Vec<i32>,
pub origin_brick: IVec3,
pub voxel_size: f32,
pub dirty_bricks: HashSet<(i32, i32, i32)>,
pub allocated_coords: HashSet<(i32, i32, i32)>,
pub pointer_dirty: bool,
}
impl BrickPointerGrid {
pub fn new(voxel_size: f32) -> Self {
Self {
pointers: vec![EMPTY_BRICK; GRID_SIZE * GRID_SIZE * GRID_SIZE],
origin_brick: IVec3::zeros(),
voxel_size,
dirty_bricks: HashSet::new(),
allocated_coords: HashSet::new(),
pointer_dirty: true,
}
}
pub fn world_to_brick_coord(&self, world_pos: Vec3) -> IVec3 {
let brick_world_size = self.voxel_size * BRICK_SIZE as f32;
IVec3::new(
(world_pos.x / brick_world_size).floor() as i32,
(world_pos.y / brick_world_size).floor() as i32,
(world_pos.z / brick_world_size).floor() as i32,
)
}
pub fn brick_to_world_origin(&self, brick_coord: IVec3) -> Vec3 {
let brick_world_size = self.voxel_size * BRICK_SIZE as f32;
Vec3::new(
brick_coord.x as f32 * brick_world_size,
brick_coord.y as f32 * brick_world_size,
brick_coord.z as f32 * brick_world_size,
)
}
fn wrap_coord(&self, coord: IVec3) -> IVec3 {
let grid_size = GRID_SIZE as i32;
IVec3::new(
((coord.x % grid_size) + grid_size) % grid_size,
((coord.y % grid_size) + grid_size) % grid_size,
((coord.z % grid_size) + grid_size) % grid_size,
)
}
fn coord_to_index(&self, wrapped_coord: IVec3) -> usize {
wrapped_coord.x as usize
+ wrapped_coord.y as usize * GRID_SIZE
+ wrapped_coord.z as usize * GRID_SIZE * GRID_SIZE
}
pub fn get_pointer(&self, brick_coord: IVec3) -> i32 {
let relative = brick_coord - self.origin_brick;
let grid_size = GRID_SIZE as i32;
if relative.x < 0
|| relative.x >= grid_size
|| relative.y < 0
|| relative.y >= grid_size
|| relative.z < 0
|| relative.z >= grid_size
{
return EMPTY_BRICK;
}
let wrapped = self.wrap_coord(brick_coord);
self.pointers[self.coord_to_index(wrapped)]
}
pub fn set_pointer(&mut self, brick_coord: IVec3, pointer: i32) {
let wrapped = self.wrap_coord(brick_coord);
let index = self.coord_to_index(wrapped);
let old_pointer = self.pointers[index];
if old_pointer != pointer {
self.pointers[index] = pointer;
self.pointer_dirty = true;
}
let coord_tuple = (brick_coord.x, brick_coord.y, brick_coord.z);
if pointer != EMPTY_BRICK && old_pointer == EMPTY_BRICK {
self.allocated_coords.insert(coord_tuple);
} else if pointer == EMPTY_BRICK && old_pointer != EMPTY_BRICK {
self.allocated_coords.remove(&coord_tuple);
}
}
pub fn mark_dirty(&mut self, brick_coord: IVec3) {
self.dirty_bricks
.insert((brick_coord.x, brick_coord.y, brick_coord.z));
}
pub fn take_dirty_bricks(&mut self) -> Vec<IVec3> {
let result: Vec<IVec3> = self
.dirty_bricks
.iter()
.map(|&(x, y, z)| IVec3::new(x, y, z))
.collect();
self.dirty_bricks.clear();
result
}
pub fn world_extent(&self) -> f32 {
self.voxel_size * BRICK_SIZE as f32 * GRID_SIZE as f32
}
pub fn is_in_bounds(&self, brick_coord: IVec3) -> bool {
let relative = brick_coord - self.origin_brick;
let grid_size = GRID_SIZE as i32;
relative.x >= 0
&& relative.x < grid_size
&& relative.y >= 0
&& relative.y < grid_size
&& relative.z >= 0
&& relative.z < grid_size
}
pub fn get_allocated_brick_coords(&self) -> impl Iterator<Item = IVec3> + '_ {
let grid_size = GRID_SIZE as i32;
(0..grid_size).flat_map(move |z| {
(0..grid_size).flat_map(move |y| {
(0..grid_size).filter_map(move |x| {
let coord = IVec3::new(
self.origin_brick.x + x,
self.origin_brick.y + y,
self.origin_brick.z + z,
);
if self.get_pointer(coord) != EMPTY_BRICK {
Some(coord)
} else {
None
}
})
})
})
}
}
pub struct BrickAllocator {
free_list: Vec<u32>,
next_slot: u32,
max_bricks: u32,
}
impl BrickAllocator {
pub fn new(max_bricks: u32) -> Self {
Self {
free_list: Vec::new(),
next_slot: 0,
max_bricks,
}
}
pub fn allocate(&mut self) -> Option<u32> {
if let Some(slot) = self.free_list.pop() {
return Some(slot);
}
if self.next_slot < self.max_bricks {
let slot = self.next_slot;
self.next_slot += 1;
return Some(slot);
}
None
}
pub fn deallocate(&mut self, slot: u32) {
self.free_list.push(slot);
}
pub fn allocated_count(&self) -> u32 {
self.next_slot - self.free_list.len() as u32
}
pub fn capacity(&self) -> u32 {
self.max_bricks
}
}
pub fn brick_index_to_atlas_coord(brick_index: u32) -> IVec3 {
let bricks_per_row = BRICKS_PER_ATLAS_DIM as u32;
let bricks_per_slice = bricks_per_row * bricks_per_row;
let z = brick_index / bricks_per_slice;
let remainder = brick_index % bricks_per_slice;
let y = remainder / bricks_per_row;
let x = remainder % bricks_per_row;
IVec3::new(
(x * BRICK_CORNER_COUNT as u32) as i32,
(y * BRICK_CORNER_COUNT as u32) as i32,
(z * BRICK_CORNER_COUNT as u32) as i32,
)
}