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, 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) -> 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 { // Header let mut bytes: Vec = 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, &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) -> &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) { for (blpos, block) in self.all_blocks() { renderer.write(&(offset + blpos), block); } } }