109 lines
3.4 KiB
Rust
109 lines
3.4 KiB
Rust
use godot::prelude::*;
|
|
|
|
pub const CHUNK_SIZE: i32 = 32;
|
|
pub const ARR_SIZE: usize = CHUNK_SIZE.pow(3) as usize;
|
|
// 32x32x32 = 32768 bits, stored in u32s (32 bits each) = 1024 u32s
|
|
pub const BITPACKED_SIZE: usize = ARR_SIZE / 32;
|
|
|
|
#[derive(Debug)]
|
|
pub struct Chunk {
|
|
// Bitpacked voxel data: 0 = air, 1 = solid
|
|
pub voxels: Vec<u32>,
|
|
// WORLD COORDINATES
|
|
pub world_position: (f64, f64, f64),
|
|
modified: bool,
|
|
}
|
|
|
|
impl Chunk {
|
|
/// pos_z and pos_x are `world` coordinates of the chunk, inside the world
|
|
pub fn new(world_pos_x: f64, world_pos_y: f64, world_pos_z: f64, size: Vector3i) -> Self {
|
|
Chunk {
|
|
voxels: vec![0u32; BITPACKED_SIZE],
|
|
world_position: (world_pos_x, world_pos_y, world_pos_z),
|
|
modified: false,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub fn get_voxel_index(&self, local_pos: Vector3i, chunk_size: Vector3i) -> usize {
|
|
((local_pos.x * chunk_size.y * chunk_size.z) + (local_pos.y * chunk_size.x) + local_pos.z)
|
|
as usize
|
|
}
|
|
|
|
#[inline]
|
|
pub fn is_voxel_within_bounds(&self, local_pos: Vector3i, chunk_size: Vector3i) -> bool {
|
|
local_pos.x >= 0
|
|
&& local_pos.x < chunk_size.x
|
|
&& local_pos.y >= 0
|
|
&& local_pos.y < chunk_size.y
|
|
&& local_pos.z >= 0
|
|
&& local_pos.z < chunk_size.z
|
|
}
|
|
|
|
#[inline]
|
|
pub fn get_voxel(&self, local_pos: Vector3i, chunk_size: Vector3i) -> bool {
|
|
debug_assert!(
|
|
self.is_voxel_within_bounds(local_pos, chunk_size),
|
|
"Voxel position {:?} out of chunk bounds",
|
|
local_pos
|
|
);
|
|
|
|
let index = self.get_voxel_index(local_pos, chunk_size);
|
|
debug_assert!(
|
|
index < ARR_SIZE,
|
|
"Voxel index {:?} out of chunk bounds",
|
|
index
|
|
);
|
|
|
|
// Get the u32 containing this bit and the bit position within it
|
|
let word_index = index / chunk_size.x as usize;
|
|
let bit_index = index % chunk_size.x as usize;
|
|
|
|
(self.voxels[word_index] & (1 << bit_index)) != 0
|
|
}
|
|
|
|
pub fn set_voxel(&mut self, voxel_pos: Vector3i, is_solid: bool, chunk_size: Vector3i) {
|
|
debug_assert!(
|
|
self.is_voxel_within_bounds(voxel_pos, chunk_size),
|
|
"Voxel position {:?} out of chunk bounds",
|
|
voxel_pos
|
|
);
|
|
let index = self.get_voxel_index(voxel_pos, chunk_size);
|
|
|
|
let word_index = index / chunk_size.x as usize;
|
|
let bit_index = index % chunk_size.x as usize;
|
|
|
|
if is_solid {
|
|
self.voxels[word_index] |= 1 << bit_index;
|
|
} else {
|
|
self.voxels[word_index] &= !(1 << bit_index);
|
|
}
|
|
self.modified = true;
|
|
}
|
|
|
|
pub fn fill(&mut self, is_solid: bool) {
|
|
let fill_value = if is_solid { u32::MAX } else { 0 };
|
|
self.voxels.fill(fill_value);
|
|
self.modified = true;
|
|
}
|
|
|
|
pub fn get_position(&self) -> (f64, f64, f64) {
|
|
self.world_position
|
|
}
|
|
|
|
pub fn get_world_bounds(&self, chunk_size: Vector3i) -> (Vector3, Vector3) {
|
|
let (x, y, z) = self.world_position;
|
|
let min = Vector3::new(
|
|
(x * chunk_size.x as f64) as f32,
|
|
(y * chunk_size.y as f64) as f32,
|
|
(z * chunk_size.z as f64) as f32,
|
|
);
|
|
let max = Vector3::new(
|
|
((x + 1.0) * chunk_size.x as f64) as f32,
|
|
((y + 1.0) * chunk_size.y as f64) as f32,
|
|
((z + 1.0) * chunk_size.z as f64) as f32,
|
|
);
|
|
(min, max)
|
|
}
|
|
}
|