Changed project name (placeholder), some renames
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
use cgmath::Point3;
|
||||
use std::iter::FromIterator;
|
||||
use std::io::Write;
|
||||
use flate2::{write::DeflateEncoder, Compression};
|
||||
|
||||
pub const SIZE: usize = 32;
|
||||
pub const SIZE_QB: usize = SIZE*SIZE*SIZE;
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WorldBlock {
|
||||
pub materials: [u8;SIZE_QB]
|
||||
}
|
||||
|
||||
impl WorldBlock {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
materials: [0_u8; SIZE_QB]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&mut self, voxel_pos: Point3<usize>, value: u8) {
|
||||
self.materials[voxel_pos.x + SIZE * (voxel_pos.y + SIZE * voxel_pos.z)] = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
use std::time::Instant;
|
||||
use flate2::Compression;
|
||||
use flate2::write::DeflateEncoder;
|
||||
use flate2::read::DeflateDecoder;
|
||||
use byteorder::LittleEndian;
|
||||
use byteorder::ByteOrder;
|
||||
use cgmath::Point3;
|
||||
use std::io::Write;
|
||||
use std::convert::TryInto;
|
||||
use std::io::Read;
|
||||
use crate::game::world::{WorldBlock, block};
|
||||
|
||||
pub const SIZE: usize = 32;
|
||||
const SIZE_SQ: usize = SIZE*SIZE;
|
||||
const SIZE_QB: usize = SIZE*SIZE*SIZE;
|
||||
|
||||
pub struct WorldChunk {
|
||||
nodes: Box<[u32]>,
|
||||
blocks: Vec<WorldBlock>
|
||||
}
|
||||
|
||||
impl WorldChunk {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
nodes: vec![0_u32; SIZE_QB as usize].into_boxed_slice(),
|
||||
blocks: Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: Vec<u8>) -> Self {
|
||||
let mut instance = Self::new();
|
||||
let (node_bytes, block_bytes) = bytes.split_at((SIZE_QB*4) as usize);
|
||||
|
||||
// Nodes
|
||||
let mut num_blocks = 0;
|
||||
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;
|
||||
if val > 0 { num_blocks+=1; }
|
||||
}
|
||||
|
||||
// Blocks
|
||||
let mut decoder = DeflateDecoder::new(block_bytes);
|
||||
let mut block_bytes = Vec::with_capacity(SIZE_QB);
|
||||
decoder.read_to_end(&mut block_bytes).expect("Failed to decode");
|
||||
|
||||
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 all_blocks(&self) -> Vec<(Point3<u32>, &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 = Point3{x: x as u32, y: y as u32, z: z as u32};
|
||||
|
||||
let block = &self.blocks[(*node-1) as usize];
|
||||
(pos, block)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_block(&mut self, block_pos: &Point3<u32>) -> &mut WorldBlock {
|
||||
// Get block index
|
||||
let index = (block_pos.x + SIZE as u32 * (block_pos.y + SIZE as u32 * block_pos.z)) as usize;
|
||||
let mut value = self.nodes[index];
|
||||
// Allocate new block
|
||||
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 to_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::with_capacity(self.nodes.len()*4 + self.blocks.len()* 32*32*32);
|
||||
|
||||
// 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 encoder = DeflateEncoder::new(Vec::new(), Compression::fast());
|
||||
for block in self.blocks.iter() {
|
||||
encoder.write_all(&block.materials).unwrap();
|
||||
}
|
||||
bytes.extend(encoder.finish().unwrap());
|
||||
|
||||
bytes
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
use cgmath::Point3;
|
||||
use noise::{Perlin, NoiseFn};
|
||||
use rand::{Rng, prelude::ThreadRng};
|
||||
|
||||
use crate::game::world::{block, block::WorldBlock};
|
||||
|
||||
const SCALE: f64 = 150.0;
|
||||
|
||||
|
||||
pub struct WorldGen {
|
||||
|
||||
}
|
||||
|
||||
impl WorldGen {
|
||||
|
||||
/*
|
||||
* Generate height map for given block position
|
||||
*/
|
||||
fn gen_height_map(x: usize, z: usize, rand: &mut ThreadRng, noise: &Perlin) -> ([[usize;block::SIZE]; block::SIZE], usize) {
|
||||
let x = x * block::SIZE;
|
||||
let z = z * block::SIZE;
|
||||
|
||||
// Generate map
|
||||
let mut max: usize = 0;
|
||||
let mut map = [[0_usize; block::SIZE]; block::SIZE];
|
||||
for lx in 0..block::SIZE {
|
||||
for lz in 0..block::SIZE {
|
||||
let value = (noise.get([(x + lx) as f64 / SCALE, (z + lz) as f64 /SCALE]) + 1.0) / 2.0;
|
||||
let mut h = (value * 30.0) as usize;
|
||||
h += (rand.gen::<f32>() * 2.0) as usize;
|
||||
if h == 0 { h = 1; }
|
||||
if h > max { max = h; }
|
||||
map[lx][lz] = h;
|
||||
}
|
||||
}
|
||||
(map, max)
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate block
|
||||
*/
|
||||
pub fn generate(block_pos: &Point3<u32>, block: &mut WorldBlock) {
|
||||
let mut rng = rand::thread_rng();
|
||||
let noise = Perlin::new();
|
||||
|
||||
// Generate height map
|
||||
let (map, max) = WorldGen::gen_height_map(block_pos.x as usize, block_pos.z as usize, &mut rng, &noise);
|
||||
|
||||
// Fill
|
||||
for x in 0..block::SIZE {
|
||||
for z in 0..block::SIZE {
|
||||
let h = map[x][z];
|
||||
//let h = 16; // testing
|
||||
for y in 0..h {
|
||||
block.set(Point3{x, y, z}, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
mod generator;
|
||||
pub mod chunk;
|
||||
pub mod block;
|
||||
use std::time::Instant;
|
||||
use linked_hash_map::LinkedHashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::path::Path;
|
||||
use std::io::Write;
|
||||
use std::io::Read;
|
||||
use std::fs;
|
||||
use cgmath::{Point3, InnerSpace};
|
||||
use std::collections::HashMap;
|
||||
use generator::WorldGen;
|
||||
use chunk::WorldChunk;
|
||||
use block::WorldBlock;
|
||||
use yaml_rust::{Yaml, YamlLoader, YamlEmitter};
|
||||
|
||||
|
||||
pub struct World {
|
||||
name: String,
|
||||
chunks: HashMap<Point3<u32>, WorldChunk>,
|
||||
}
|
||||
|
||||
impl World {
|
||||
|
||||
pub fn new(name: String) -> Self {
|
||||
Self {
|
||||
name,
|
||||
chunks: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all_blocks(&self) -> Vec<(Point3<u32>, &WorldBlock)> {
|
||||
self.chunks.iter().map(|(chunk_pos, chunk)| {
|
||||
let off = chunk_pos * chunk::SIZE as u32;
|
||||
let result: Vec<(Point3<u32>, &WorldBlock)> = chunk.all_blocks().iter().map(|(pos, block)| {
|
||||
(
|
||||
Point3{x: off.x+pos.x, y: off.y+pos.y, z: off.z+pos.z},
|
||||
*block
|
||||
)
|
||||
}).collect();
|
||||
result
|
||||
}).flatten().collect()
|
||||
}
|
||||
|
||||
pub fn save(&self) {
|
||||
// Get directory
|
||||
let mut dir = dirs::data_local_dir().unwrap();
|
||||
dir.push("Voxelgame");
|
||||
fs::create_dir_all(&dir).expect("Failed to create directory");
|
||||
|
||||
// Save all chunks
|
||||
for (pos, chunk) in self.chunks.iter() {
|
||||
let bytes = chunk.to_bytes();
|
||||
let filename = format!("chunk_{}_{}_{}.dat", pos.x, pos.y, pos.z);
|
||||
let path: PathBuf = [dir.to_str().unwrap(), &filename].iter().collect();
|
||||
|
||||
let mut file = fs::File::create(path).expect("Failed to create file");
|
||||
file.write(&bytes).unwrap();
|
||||
}
|
||||
|
||||
// Save metadata
|
||||
let mut data: LinkedHashMap<Yaml, Yaml> = LinkedHashMap::new();
|
||||
data.insert(Yaml::String("name".to_string()), Yaml::String(self.name.clone()));
|
||||
|
||||
let mut data_str = String::new();
|
||||
let mut emit = YamlEmitter::new(&mut data_str);
|
||||
emit.dump(&Yaml::Hash(data)).expect("Failed to encode yaml");
|
||||
|
||||
let data_path: PathBuf = [dir.to_str().unwrap(), "world.yml"].iter().collect();
|
||||
let mut data_file = fs::File::create(data_path).unwrap();
|
||||
data_file.write_all(data_str.as_bytes()).expect("Failed to write");
|
||||
}
|
||||
|
||||
pub fn load(&mut self) {
|
||||
// Get directory
|
||||
let mut dir = dirs::data_local_dir().unwrap();
|
||||
dir.push("Voxelgame");
|
||||
if !dir.is_dir() {
|
||||
println!("Not exists: {:?}", dir);
|
||||
return;
|
||||
}
|
||||
|
||||
// Load all chunks
|
||||
for entry in fs::read_dir(dir).unwrap() {
|
||||
let path: PathBuf = entry.unwrap().path();
|
||||
if path.extension().unwrap().to_str() != Some("dat") {
|
||||
continue;
|
||||
}
|
||||
let stem = path.file_stem().unwrap().to_str().unwrap();
|
||||
if !stem.starts_with("chunk") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let s: Vec<&str> = stem.split("_").collect();
|
||||
let pos = Point3{x: s[1].parse::<u32>().unwrap(), y: s[2].parse::<u32>().unwrap(), z: s[3].parse::<u32>().unwrap()};
|
||||
|
||||
let mut bytes = Vec::new();
|
||||
let mut file = fs::File::open(path).expect("Failed to open file");
|
||||
file.read_to_end(&mut bytes).expect("Failed to read file");
|
||||
let chunk = WorldChunk::from_bytes(bytes);
|
||||
|
||||
println!("loaded {:?}", pos);
|
||||
self.chunks.insert(pos, chunk);
|
||||
}
|
||||
|
||||
// Load metadata
|
||||
|
||||
}
|
||||
|
||||
pub fn gen_block(&mut self, block_wpos: &Point3<u32>) -> &WorldBlock {
|
||||
// Split world space to chunk and block space
|
||||
let ch_pos = block_wpos / chunk::SIZE as u32;
|
||||
let bl_pos = block_wpos % chunk::SIZE as u32;
|
||||
// Create chunk if does not exist
|
||||
if let None = self.chunks.get(&ch_pos) {
|
||||
self.chunks.insert(ch_pos.clone(), WorldChunk::new());
|
||||
println!("Alloc chunk [{}, {}, {}]", ch_pos.x, ch_pos.y, ch_pos.z);
|
||||
}
|
||||
// Generate block for chunk
|
||||
let chunk = self.chunks.get_mut(&ch_pos).unwrap();
|
||||
let block = chunk.get_block(&bl_pos);
|
||||
WorldGen::generate(&block_wpos, block);
|
||||
block
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn get_block(&mut self, block_wpos: &Point3<u32>) -> Option<&WorldBlock> {
|
||||
// Split world space to chunk and block space
|
||||
let ch_pos = block_wpos / chunk::SIZE as u32;
|
||||
let bl_pos = block_wpos % chunk::SIZE as u32;
|
||||
// Return block if exists
|
||||
if let Some(chunk) = self.chunks.get_mut(&ch_pos) {
|
||||
return Some(chunk.get_block(&bl_pos));
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user