139 lines
3.6 KiB
Rust

use std::cmp::{max, min};
const SECTIONS_PER_CHUNK: usize = 16;
const SLICE_SIZE: usize = 16 * 16;
#[derive(Debug)]
pub struct ChunkPos {
pub x: isize,
pub z: isize,
}
impl From<&BlockPos> for ChunkPos {
fn from(value: &BlockPos) -> Self {
ChunkPos {
x: value.x / 16,
z: value.z / 16,
}
}
}
impl ChunkPos {
pub fn same_location(&self, other: &ChunkPos) -> bool {
self.x == other.x && self.z == other.z
}
}
pub struct ChunkData {
pub pos: ChunkPos,
pub sections: [ChunkSection; SECTIONS_PER_CHUNK],
}
// https://wiki.vg/Chunk_Format
pub struct ChunkSection {
/// The number of non-empty blocks in the section. If completely full, the
/// section contains a 16 x 16 x 16 cube of blocks = 4096 blocks
/// If the section is empty, this is skipped
block_count: u16,
/// The data for all the blocks in the chunk
/// The representation for this may be different based on the number of
/// non-empty blocks
block_states: [BlockID; 16 * 16 * 16],
}
impl ChunkSection {
pub fn index_of_block(&self, pos: &BlockPos) -> usize {
let base_x = pos.x.rem_euclid(16) as usize;
let base_y = pos.y.rem_euclid(16) as usize;
let base_z = pos.z.rem_euclid(16) as usize;
(base_y * SLICE_SIZE) + (base_z * 16) + base_x
}
pub fn update_block_at_index(&mut self, id: &BlockID, index: usize) {
let existing_block = &self.block_states[index];
match existing_block {
BlockID::Empty => match id {
BlockID::Generic => {
// If the existing block is empty, and the block that we
// are inserting is non-empty, increment the number of blocks
self.block_count += 1;
}
_ => {}
},
_ => match id {
BlockID::Empty => {
// If the existing block is non-empty, and the block that
// we are inserting is empty, then decrement the number of
// blocks
self.block_count -= 1;
}
_ => {}
},
}
self.block_states[index] = id.clone();
}
}
/// `BlockPos` represents the location of a block in world space
#[derive(Debug, Clone, PartialEq)]
pub struct BlockPos {
pub x: isize,
pub y: usize,
pub z: isize,
}
impl BlockPos {
pub fn new(x: isize, y: usize, z: isize) -> Self {
BlockPos { x, y, z }
}
}
/// BlockRange represents a range of blocks that have been updated
pub struct BlockRange {
pub start: BlockPos,
pub end: BlockPos,
}
impl BlockRange {
pub fn new(start: &BlockPos, end: &BlockPos) -> Self {
BlockRange {
start: start.clone(),
end: end.clone(),
}
}
pub fn within_range(&self, pos: &BlockPos) -> bool {
let minx = min(self.start.x, self.end.x);
let maxx = max(self.start.x, self.end.x);
if pos.x < minx || pos.x > maxx {
return false;
}
let miny = min(self.start.y, self.end.y);
let maxy = max(self.start.y, self.end.y);
if pos.y < miny || pos.y > maxy {
return false;
}
let minz = min(self.start.z, self.end.z);
let maxz = max(self.start.z, self.end.z);
if pos.z < minz || pos.z > maxz {
return false;
}
true
}
}
/// BlockID represents the type of block stored
#[repr(u8)]
#[derive(Debug, Clone, PartialEq)]
pub enum BlockID {
Empty,
Generic,
}