Dynamic loading, saving, generating chunks works
This commit is contained in:
+7
-7
@@ -2,7 +2,7 @@ mod camera_controller;
|
|||||||
mod player;
|
mod player;
|
||||||
pub mod world;
|
pub mod world;
|
||||||
use crate::renderer::renderer_view::RendererView;
|
use crate::renderer::renderer_view::RendererView;
|
||||||
use cgmath::Point3;
|
use cgmath::Vector3;
|
||||||
use winit::event::Event;
|
use winit::event::Event;
|
||||||
use player::Player;
|
use player::Player;
|
||||||
use world::World;
|
use world::World;
|
||||||
@@ -19,7 +19,7 @@ pub struct Game {
|
|||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
world: World,
|
world: World,
|
||||||
|
|
||||||
prev_pos: Option<Point3<i32>>
|
prev_pos: Option<Vector3<i32>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
@@ -64,16 +64,16 @@ impl Game {
|
|||||||
|
|
||||||
// Update chunks
|
// Update chunks
|
||||||
let pos = camera.get_position();
|
let pos = camera.get_position();
|
||||||
let pos = Point3{x: pos.x as i32, y: pos.y as i32, z: pos.z as i32};
|
let pos = Vector3{x: pos.x as i32, y: pos.y as i32, z: pos.z as i32};
|
||||||
if self.prev_pos == None || self.prev_pos != Some(pos) {
|
if self.prev_pos == None || self.prev_pos != Some(pos) {
|
||||||
self.prev_pos = Some(pos);
|
self.prev_pos = Some(pos);
|
||||||
renderer.get_ui().set_text("World", 0, format!("Position: {}, {}, {}", pos.x, pos.y, pos.z));
|
renderer.get_ui().set_text("World", 0, format!("Position: {}, {}, {}", pos.x, pos.y, pos.z));
|
||||||
|
|
||||||
// Load sphere of chunks around player (TODO)
|
// Load sphere of chunks around player
|
||||||
let chunk_pos = Point3{x: pos.x / 32, y: pos.y / 32, z: pos.z / 32};
|
let chunk_pos = pos / 32;//Vector3{x: pos.x / 32, y: pos.y / 32, z: pos.z / 32};
|
||||||
if self.world.load_chunk(chunk_pos) {
|
if self.world.load_chunk(chunk_pos) {
|
||||||
|
let chunk = self.world.get_chunk(&chunk_pos).unwrap();
|
||||||
renderer.write(, block: &WorldBlock)
|
chunk.write_to(renderer, chunk_pos * 32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use cgmath::Point3;
|
use cgmath::Vector3;
|
||||||
|
|
||||||
pub const SIZE: usize = 32;
|
pub const SIZE: usize = 32;
|
||||||
pub const SIZE_QB: usize = SIZE*SIZE*SIZE;
|
pub const SIZE_QB: usize = SIZE*SIZE*SIZE;
|
||||||
@@ -16,7 +16,7 @@ impl WorldBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&mut self, voxel_pos: Point3<usize>, value: u8) {
|
pub fn set(&mut self, voxel_pos: Vector3<usize>, value: u8) {
|
||||||
self.materials[voxel_pos.x + SIZE * (voxel_pos.y + SIZE * voxel_pos.z)] = value;
|
self.materials[voxel_pos.x + SIZE * (voxel_pos.y + SIZE * voxel_pos.z)] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+15
-9
@@ -1,6 +1,7 @@
|
|||||||
|
use crate::game::RendererView;
|
||||||
use byteorder::LittleEndian;
|
use byteorder::LittleEndian;
|
||||||
use byteorder::ByteOrder;
|
use byteorder::ByteOrder;
|
||||||
use cgmath::Point3;
|
use cgmath::Vector3;
|
||||||
use lzzzz::lz4;
|
use lzzzz::lz4;
|
||||||
use crate::game::world::{WorldBlock, block};
|
use crate::game::world::{WorldBlock, block};
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ pub struct WorldChunk {
|
|||||||
impl WorldChunk {
|
impl WorldChunk {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
nodes: vec![0_u32; SIZE_QB as usize].into_boxed_slice(),
|
nodes: vec![0_u32; SIZE_QB].into_boxed_slice(),
|
||||||
blocks: Vec::new()
|
blocks: Vec::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,7 +86,7 @@ impl WorldChunk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub fn all_blocks(&self) -> Vec<(Point3<u32>, &WorldBlock)> {
|
pub fn all_blocks(&self) -> Vec<(Vector3<i32>, &WorldBlock)> {
|
||||||
self.nodes.iter().enumerate()
|
self.nodes.iter().enumerate()
|
||||||
.filter(|(_, n)| **n != 0)
|
.filter(|(_, n)| **n != 0)
|
||||||
.map(|(index, node)| {
|
.map(|(index, node)| {
|
||||||
@@ -96,17 +97,16 @@ impl WorldChunk {
|
|||||||
let y = idx / SIZE;
|
let y = idx / SIZE;
|
||||||
let x = idx % SIZE;
|
let x = idx % SIZE;
|
||||||
|
|
||||||
let pos = Point3{x: x as u32, y: y as u32, z: z as u32};
|
let pos = Vector3{x: x as i32, y: y as i32, z: z as i32};
|
||||||
|
|
||||||
let block = &self.blocks[(*node-1) as usize];
|
let block = &self.blocks[(*node-1) as usize];
|
||||||
(pos, block)
|
(pos, block)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_block_mut(&mut self, block_pos: &Point3<u32>) -> &mut WorldBlock {
|
pub fn get_block_mut(&mut self, block_pos: &Vector3<i32>) -> &mut WorldBlock {
|
||||||
// Get block index
|
// Get block index
|
||||||
let index = (block_pos.x + SIZE as u32 * (block_pos.y + SIZE as u32 * block_pos.z)) as usize;
|
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];
|
let mut value = self.nodes[index];
|
||||||
// Allocate new block
|
// Allocate new block
|
||||||
if value == 0 {
|
if value == 0 {
|
||||||
@@ -118,11 +118,17 @@ impl WorldChunk {
|
|||||||
&mut self.blocks[(value-1) as usize]
|
&mut self.blocks[(value-1) as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_block(&self, block_pos: &Point3<u32>) -> &WorldBlock {
|
pub fn get_block(&self, block_pos: &Vector3<i32>) -> &WorldBlock {
|
||||||
// Get block index
|
// Get block index
|
||||||
let index = (block_pos.x + SIZE as u32 * (block_pos.y + SIZE as u32 * block_pos.z)) as usize;
|
let index = (block_pos.x + SIZE as i32 * (block_pos.y + SIZE as i32 * block_pos.z)) as usize;
|
||||||
let value = self.nodes[index];
|
let value = self.nodes[index];
|
||||||
// Return block reference
|
// Return block reference
|
||||||
&self.blocks[(value-1) as usize]
|
&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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use cgmath::Point3;
|
use cgmath::Vector3;
|
||||||
use noise::{Perlin, NoiseFn};
|
use noise::{Perlin, NoiseFn};
|
||||||
use rand::{Rng, prelude::ThreadRng};
|
use rand::{Rng, prelude::ThreadRng};
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ impl WorldGen {
|
|||||||
/*
|
/*
|
||||||
* Generate block
|
* Generate block
|
||||||
*/
|
*/
|
||||||
pub fn generate(block_pos: &Point3<i32>, block: &mut WorldBlock) {
|
pub fn generate(block_pos: &Vector3<i32>, block: &mut WorldBlock) {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let noise = Perlin::new();
|
let noise = Perlin::new();
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ impl WorldGen {
|
|||||||
let h = map[x][z];
|
let h = map[x][z];
|
||||||
//let h = 16; // testing
|
//let h = 16; // testing
|
||||||
for y in 0..h {
|
for y in 0..h {
|
||||||
block.set(Point3{x, y, z}, 2);
|
block.set(Vector3{x, y, z}, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-28
@@ -1,11 +1,12 @@
|
|||||||
mod generator;
|
mod generator;
|
||||||
pub mod chunk;
|
pub mod chunk;
|
||||||
pub mod block;
|
pub mod block;
|
||||||
|
use std::time::Instant;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use cgmath::Point3;
|
use cgmath::{Point3, Vector3};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use generator::WorldGen;
|
use generator::WorldGen;
|
||||||
use chunk::WorldChunk;
|
use chunk::WorldChunk;
|
||||||
@@ -19,11 +20,7 @@ struct WorldData {
|
|||||||
|
|
||||||
pub struct World {
|
pub struct World {
|
||||||
data: WorldData,
|
data: WorldData,
|
||||||
chunks: HashMap<Point3<i32>, WorldChunk>,
|
chunks: HashMap<Vector3<i32>, WorldChunk>,
|
||||||
}
|
|
||||||
|
|
||||||
fn point_abs(p: &Point3<i32>) -> Point3<u32> {
|
|
||||||
Point3 {x: p.x.abs() as u32, y: p.y.abs() as u32, z: p.z.abs() as u32 }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl World {
|
impl World {
|
||||||
@@ -35,12 +32,12 @@ impl World {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_blocks(&self) -> Vec<(Point3<i32>, &WorldBlock)> {
|
pub fn all_blocks(&self) -> Vec<(Vector3<i32>, &WorldBlock)> {
|
||||||
self.chunks.iter().map(|(chunk_pos, chunk)| {
|
self.chunks.iter().map(|(chunk_pos, chunk)| {
|
||||||
let off = chunk_pos * chunk::SIZE as i32;
|
let off = chunk_pos * chunk::SIZE as i32;
|
||||||
let result: Vec<(Point3<i32>, &WorldBlock)> = chunk.all_blocks().iter().map(|(pos, block)| {
|
let result: Vec<(Vector3<i32>, &WorldBlock)> = chunk.all_blocks().iter().map(|(pos, block)| {
|
||||||
(
|
(
|
||||||
Point3{x: off.x+pos.x as i32, y: off.y+pos.y as i32, z: off.z+pos.z as i32},
|
Vector3{x: off.x+pos.x as i32, y: off.y+pos.y as i32, z: off.z+pos.z as i32},
|
||||||
*block
|
*block
|
||||||
)
|
)
|
||||||
}).collect();
|
}).collect();
|
||||||
@@ -50,18 +47,14 @@ impl World {
|
|||||||
|
|
||||||
pub fn save(&self) {
|
pub fn save(&self) {
|
||||||
// Get directory
|
// Get directory
|
||||||
let mut dir = dirs::data_local_dir().unwrap();
|
let dir = dirs::data_local_dir().unwrap().join("Voxelgame");
|
||||||
dir.push("Voxelgame");
|
|
||||||
fs::create_dir_all(&dir).expect("Failed to create directory");
|
fs::create_dir_all(&dir).expect("Failed to create directory");
|
||||||
|
|
||||||
// Save all chunks
|
// Save all chunks
|
||||||
for (pos, chunk) in self.chunks.iter() {
|
for (pos, chunk) in self.chunks.iter() {
|
||||||
let bytes = chunk.to_bytes();
|
let path = dir.with_file_name(format!("chunk_{}_{}_{}.dat", pos.x, pos.y, pos.z));
|
||||||
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");
|
let mut file = fs::File::create(path).expect("Failed to create file");
|
||||||
file.write(&bytes).unwrap();
|
file.write(&chunk.to_bytes()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save metadata
|
// Save metadata
|
||||||
@@ -72,41 +65,56 @@ impl World {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Loads chunk and displays it in the world.
|
* Loads chunk into memory
|
||||||
* It will try to load the chunk from file, and if that fails it will generate the chunk and save it.
|
* It will try to load the chunk from file, and if that fails it will generate the chunk and save it.
|
||||||
*/
|
*/
|
||||||
pub fn load_chunk(&mut self, pos: Point3<i32>) -> Option<&WorldChunk> {
|
pub fn load_chunk(&mut self, pos: Vector3<i32>) ->bool {
|
||||||
// Abort if chunk is already loaded
|
// Abort if chunk is already loaded
|
||||||
if let Some(_) = self.chunks.get(&pos) {
|
if let Some(_) = self.chunks.get(&pos) {
|
||||||
return None;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try loading chunk from file
|
// Try loading chunk from file
|
||||||
let filename = format!("chunk_{}_{}_{}.dat", pos.x, pos.y, pos.z);
|
let filename = format!("chunk_{}_{}_{}.dat", pos.x, pos.y, pos.z);
|
||||||
let filepath = dirs::data_local_dir().unwrap().join("Voxelgame").with_file_name(filename);
|
let filepath = dirs::data_local_dir().unwrap().join("Voxelgame").with_file_name(filename);
|
||||||
if filepath.is_file() {
|
if filepath.is_file() {
|
||||||
|
let timer = Instant::now();
|
||||||
let mut buff = Vec::new();
|
let mut buff = Vec::new();
|
||||||
let mut file = fs::File::open(filepath).expect("Failed to open file");
|
let mut file = fs::File::open(filepath).expect("Failed to open file");
|
||||||
file.read_to_end(&mut buff).expect("Failed to read file");
|
file.read_to_end(&mut buff).expect("Failed to read file");
|
||||||
let chunk = WorldChunk::from_bytes(buff);
|
let chunk = WorldChunk::from_bytes(buff);
|
||||||
self.chunks.insert(pos, chunk);
|
self.chunks.insert(pos, chunk);
|
||||||
println!("Chunk at {:?} loaded", pos);
|
println!("Chunk at {:?} loaded ({} ms)", pos, timer.elapsed().as_millis());
|
||||||
return Some(&chunk);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try generating chunk
|
// Try generating chunk
|
||||||
|
let timer0 = Instant::now();
|
||||||
let mut chunk = WorldChunk::new();
|
let mut chunk = WorldChunk::new();
|
||||||
for x in 0..32 {
|
for x in 0..32 {
|
||||||
for z in 0..32 {
|
for z in 0..32 {
|
||||||
let bpos = &Point3::<u32>{x,y:0,z};
|
let bpos = Vector3::<i32>{x,y:0,z};
|
||||||
let block = chunk.get_block_mut(&bpos);
|
let block = chunk.get_block_mut(&bpos);
|
||||||
let wpos = &Point3::<i32>{ x: pos.x+bpos.x as i32, y: pos.y+bpos.y as i32, z: pos.z+bpos.z as i32};
|
WorldGen::generate(&(pos*32 + bpos), block);
|
||||||
WorldGen::generate(wpos, block);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save
|
||||||
|
let timer1 = Instant::now();
|
||||||
|
let dir = dirs::data_local_dir().unwrap().join("Voxelgame");
|
||||||
|
fs::create_dir_all(&dir).expect("Failed to create directory");
|
||||||
|
let path = dir.with_file_name(format!("chunk_{}_{}_{}.dat", pos.x, pos.y, pos.z));
|
||||||
|
let mut file = fs::File::create(path).expect("Failed to create file");
|
||||||
|
file.write(&chunk.to_bytes()).unwrap();
|
||||||
|
|
||||||
|
// Add to list
|
||||||
self.chunks.insert(pos, chunk);
|
self.chunks.insert(pos, chunk);
|
||||||
println!("Chunk at {:?} generated", pos);
|
|
||||||
Some(&chunk)
|
// Stats
|
||||||
|
let t1 = timer1.elapsed().as_millis();
|
||||||
|
let t0 = timer0.elapsed().as_millis() - t1;
|
||||||
|
println!("Chunk at {:?} generated ({} ms) and saved ({} ms)", pos, t0, t1);
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load(&mut self) -> bool {
|
pub fn load(&mut self) -> bool {
|
||||||
@@ -124,14 +132,18 @@ impl World {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub fn get_block(&mut self, block_wpos: &Point3<i32>) -> Option<&WorldBlock> {
|
pub fn get_block(&mut self, block_wpos: &Vector3<i32>) -> Option<&WorldBlock> {
|
||||||
// Split world space to chunk and block space
|
// Split world space to chunk and block space
|
||||||
let ch_pos = block_wpos / chunk::SIZE as i32;
|
let ch_pos = block_wpos / chunk::SIZE as i32;
|
||||||
let bl_pos = point_abs(block_wpos) % chunk::SIZE as u32;
|
let bl_pos = block_wpos % chunk::SIZE as i32;
|
||||||
// Return block if exists
|
// Return block if exists
|
||||||
if let Some(chunk) = self.chunks.get_mut(&ch_pos) {
|
if let Some(chunk) = self.chunks.get_mut(&ch_pos) {
|
||||||
return Some(chunk.get_block(&bl_pos));
|
return Some(chunk.get_block(&bl_pos));
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_chunk(&mut self, chunk_wpos: &Vector3<i32>) -> Option<&mut WorldChunk> {
|
||||||
|
self.chunks.get_mut(chunk_wpos)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use byteorder::{ByteOrder, LittleEndian};
|
use byteorder::{ByteOrder, LittleEndian};
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use cgmath::Point3;
|
use cgmath::Vector3;
|
||||||
use crate::game::world::block::WorldBlock;
|
use crate::game::world::block::WorldBlock;
|
||||||
|
|
||||||
//note: remember to update in main.frag
|
//note: remember to update in main.frag
|
||||||
@@ -102,10 +102,10 @@ impl Content {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub fn write(&mut self, queue: &wgpu::Queue, block_pos: &Point3<u32>, block: &WorldBlock) {
|
pub fn write(&mut self, queue: &wgpu::Queue, block_pos: &Vector3<i32>, block: &WorldBlock) {
|
||||||
|
|
||||||
// Get node
|
// Get node
|
||||||
let index = (block_pos.x + NODE_TEX_SIZE as u32 * (block_pos.y + NODE_TEX_SIZE as u32 * block_pos.z)) as usize;
|
let index = (block_pos.x + NODE_TEX_SIZE as i32 * (block_pos.y + NODE_TEX_SIZE as i32 * block_pos.z)) as usize;
|
||||||
let mut value = self.node_buffer[index] as usize;
|
let mut value = self.node_buffer[index] as usize;
|
||||||
|
|
||||||
// Allocate new block
|
// Allocate new block
|
||||||
|
|||||||
@@ -2,17 +2,17 @@ use crate::renderer::UserInterface;
|
|||||||
use crate::renderer::camera::CameraTransform;
|
use crate::renderer::camera::CameraTransform;
|
||||||
use crate::Renderer;
|
use crate::Renderer;
|
||||||
use crate::game::world::block::WorldBlock;
|
use crate::game::world::block::WorldBlock;
|
||||||
use cgmath::Point3;
|
use cgmath::Vector3;
|
||||||
|
|
||||||
pub trait RendererView {
|
pub trait RendererView {
|
||||||
fn write(&mut self, block_pos: &Point3<u32>, block: &WorldBlock);
|
fn write(&mut self, block_pos: &Vector3<i32>, block: &WorldBlock);
|
||||||
fn get_camera_transform(&mut self) -> &mut dyn CameraTransform;
|
fn get_camera_transform(&mut self) -> &mut dyn CameraTransform;
|
||||||
fn get_ui(&mut self) -> &mut UserInterface;
|
fn get_ui(&mut self) -> &mut UserInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RendererView for Renderer {
|
impl RendererView for Renderer {
|
||||||
|
|
||||||
fn write(&mut self, block_pos: &Point3<u32>, block: &WorldBlock) {
|
fn write(&mut self, block_pos: &Vector3<i32>, block: &WorldBlock) {
|
||||||
self.buffers.content.write(&self.queue, block_pos, block)
|
self.buffers.content.write(&self.queue, block_pos, block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user