Files
first-voxels/src/game/world/chunk.rs
T
2021-05-16 17:19:23 +02:00

133 lines
4.2 KiB
Rust

use std::sync::Arc;
use std::time::Instant;
use std::thread;
use std::sync::mpsc;
use crate::game::RendererView;
use byteorder::LittleEndian;
use byteorder::ByteOrder;
use cgmath::Vector3;
use lzzzz::lz4;
use crate::game::world::{WorldBlock, block};
pub const SIZE: usize = 16;
const SIZE_SQ: usize = SIZE*SIZE;
const SIZE_QB: usize = SIZE*SIZE*SIZE;
const NUM_WORKERS: usize = 8;
// 1 chunk times on i7-5930K
// alg | size | compr | decom
// raw 32.89kB 0.06s 1.19s
// deflate fast 1.23kB 1.22s 5.46s
// lz4 1.6kB 0.07s 1.23s
pub struct WorldChunk {
nodes: Box<[u32]>,
blocks: Vec<WorldBlock>,
pub dirty: bool // true by default, set to false when uploaded to renderer. should be set to true when unloaded (TODO)
}
impl WorldChunk {
pub fn new() -> Self {
Self {
nodes: vec![0_u32; SIZE_QB].into_boxed_slice(),
blocks: Vec::new(),
dirty: true
}
}
pub fn from_bytes(bytes: Vec<u8>) -> Self {
let mut instance = Self::new();
// Header
let (header_bytes, bytes) = bytes.split_at(128);
let file_ver = header_bytes[0];
let num_blocks = LittleEndian::read_u32(&header_bytes[1..5]) as usize;
assert_eq!(file_ver, 1);
// Nodes
let (node_bytes, block_bytes_compressed) = bytes.split_at((SIZE_QB*4) as usize);
for i in 0..SIZE_QB {
let idx = (i*4) as usize;
let val = u32::from_le_bytes([node_bytes[idx], node_bytes[idx+1], node_bytes[idx+2], node_bytes[idx+3]]);
instance.nodes[i as usize] = val;
}
// Blocks
let mut block_bytes = vec![0_u8; num_blocks*block::SIZE_QB];
if lz4::decompress(&block_bytes_compressed, &mut block_bytes[0..num_blocks*block::SIZE_QB]).is_err() {
}
for i in 0..num_blocks {
let mut block = WorldBlock::new();
let idx = i * block::SIZE_QB;
block.materials.clone_from_slice(&block_bytes[idx..idx+block::SIZE_QB]);
instance.blocks.push(block);
}
instance
}
pub fn to_bytes(&self) -> Vec<u8> {
// Header
let mut bytes: Vec<u8> = vec![0_u8; 5];
bytes[0] = 1_u8; // 1b -> File version
LittleEndian::write_u32(&mut bytes[1..5], self.blocks.len() as u32); // 4b -> Number of blocks in this file
bytes.extend((0..128-5).map(|_| 0)); // Reserved 128b - 5b used
// Nodes
let mut node_bytes = [0_u8; (SIZE_QB*4) as usize];
LittleEndian::write_u32_into(&self.nodes, &mut node_bytes);
bytes.extend(node_bytes.iter());
// Blocks
let mut buf = Vec::new();
for block in self.blocks.iter() {
buf.extend(&block.materials[..]);
}
lz4::compress_to_vec(&buf, &mut bytes, lz4::ACC_LEVEL_DEFAULT).unwrap();
bytes
}
pub fn all_blocks(&self) -> Vec<(Vector3<i32>, &WorldBlock)> {
self.nodes.iter().enumerate()
.filter(|(_, n)| **n != 0)
.map(|(index, node)| {
let mut idx = index;
let z = idx / SIZE_SQ;
idx -= z * SIZE_SQ;
let y = idx / SIZE;
let x = idx % SIZE;
let pos = Vector3{x: x as i32, y: y as i32, z: z as i32};
let block = &self.blocks[(*node-1) as usize];
(pos, block)
})
.collect()
}
pub fn get_block_mut(&mut self, block_pos: &Vector3<i32>) -> &mut WorldBlock {
// Get block index
let index = (block_pos.x + SIZE as i32 * (block_pos.y + SIZE as i32 * block_pos.z)) as usize;
let mut value = self.nodes[index];
// Allocate new block if needed
if value == 0 {
value = (self.blocks.len() + 1) as u32;
self.nodes[index] = value;
self.blocks.push(WorldBlock::new());
}
// Return block reference
&mut self.blocks[(value-1) as usize]
}
pub fn write_to(&self, renderer: &mut dyn RendererView, offset: Vector3<i32>) {
for (blpos, block) in self.all_blocks() {
renderer.write(&(offset + blpos), block);
}
}
}