More informative chunk states, much cleaner

This commit is contained in:
Piotrek
2021-05-20 14:05:43 +02:00
parent a5d109fb0a
commit 7daec14ba7
6 changed files with 109 additions and 116 deletions
+66 -62
View File
@@ -7,6 +7,7 @@ use cgmath::Vector3;
use winit::event::Event; use winit::event::Event;
use player::Player; use player::Player;
use world::World; use world::World;
use world::chunk::{WorldChunk, ChunkState};
pub const RENDER_DIST: i32 = 3; pub const RENDER_DIST: i32 = 3;
@@ -38,7 +39,7 @@ impl Game {
// Preload chunks around player // Preload chunks around player
for x in -RENDER_DIST..RENDER_DIST { for x in -RENDER_DIST..RENDER_DIST {
for z in -RENDER_DIST..RENDER_DIST { for z in -RENDER_DIST..RENDER_DIST {
instance.world.load_chunk(Vector3{x, y: 0, z}); instance.world.load(Vector3{x, y: 0, z});
} }
} }
@@ -46,33 +47,15 @@ impl Game {
instance instance
} }
fn load_chunks(&mut self, renderer: &mut impl RendererView, chunk_pos: Vector3<i32>) { fn debug_info(&mut self, renderer: &mut impl RendererView) {
// Wait for previous chunks to be loaded before loading new ones // Update pointing direction
if !renderer.is_busy() { let yaw = self.player.camera_controller.get_yaw();
if self.prev_chunk_pos == None { self.prev_chunk_pos = Some(chunk_pos); } let mut yaw_str = "";
let prev_chunk_pos = self.prev_chunk_pos.unwrap(); if yaw >= 315.0 || yaw <= 45.0 { yaw_str = "+z"; }
let mut dir = chunk_pos - prev_chunk_pos; else if yaw >= 45.0 && yaw < 135.0 { yaw_str = "-x"; }
else if yaw >= 135.0 && yaw < 225.0 { yaw_str = "-z"; }
// Shift max by 1, in one axis else if yaw >= 225.0 && yaw < 315.0 { yaw_str = "+x"; }
if dir.x != 0 { dir = Vector3{x: dir.x.clamp(-1, 1), y:0, z:0 } } renderer.get_ui().set_text("World", 0, format!("Direction: {}", yaw_str));
else if dir.y != 0 { dir = Vector3{x:0, y:dir.y.clamp(-1, 1), z:0 } }
else if dir.z != 0 { dir = Vector3{x:0, y:0, z:dir.z.clamp(-1, 1) } }
else { return; }
// Shift world
println!("Shiftin by {:?}", dir);
renderer.shift(dir * world::chunk::SIZE as i32);
// Load chunks at the edge
let center = chunk_pos.clone() + RENDER_DIST*dir;
let mask = Vector3{x: dir.x.abs(), y: dir.y.abs(), z: dir.z.abs()};
for a in -RENDER_DIST..RENDER_DIST {
let pos = center + Vector3{x: mask.z*a, y: 0, z: mask.x*a};
self.world.load_chunk(pos);
}
self.prev_chunk_pos = Some(prev_chunk_pos+dir);
}
} }
pub fn update(&mut self, delta: f32, renderer: &mut impl RendererView) { pub fn update(&mut self, delta: f32, renderer: &mut impl RendererView) {
@@ -84,26 +67,13 @@ impl Game {
let cam_pos = camera.get_position(); let cam_pos = camera.get_position();
let cam_pos = Vector3{x: cam_pos.x as i32, y: 0/*cam_pos.y as i32*/, z: cam_pos.z as i32}; let cam_pos = Vector3{x: cam_pos.x as i32, y: 0/*cam_pos.y as i32*/, z: cam_pos.z as i32};
let chunk_pos = cam_pos / world::chunk::SIZE as i32; let chunk_pos = cam_pos / world::chunk::SIZE as i32;
if self.prev_pos == None || self.prev_pos != Some(cam_pos) {
renderer.get_ui().set_text("World", 1, format!("Position: {}, {}, {}", cam_pos.x, cam_pos.y, cam_pos.z));
self.prev_pos = Some(cam_pos);
}
// Update pointing direction // Debug
// let yaw = self.player.camera_controller.get_yaw(); self.debug_info(renderer);
// let mut yaw_str = "";
// if yaw >= 315.0 || yaw <= 45.0 { yaw_str = "+z"; }
// else if yaw >= 45.0 && yaw < 135.0 { yaw_str = "-x"; }
// else if yaw >= 135.0 && yaw < 225.0 { yaw_str = "-z"; }
// else if yaw >= 225.0 && yaw < 315.0 { yaw_str = "+x"; }
// renderer.get_ui().set_text("World", 0, format!("Direction: {}", yaw_str));
// Block position
// if self.prev_pos == None || self.prev_pos != Some(cam_pos) {
// renderer.get_ui().set_text("World", 1, format!("Position: {}, {}, {}", cam_pos.x, cam_pos.y, cam_pos.z));
// self.prev_pos = Some(cam_pos);
// }
// Chunk position
// if self.prev_chunk_pos == None || self.prev_chunk_pos != Some(chunk_pos) {
// renderer.get_ui().set_text("World", 2, format!("Chunk: {}, {}, {}", chunk_pos.x, chunk_pos.y, chunk_pos.z));
// }
// Receives chunks from workers // Receives chunks from workers
self.world.update(); self.world.update();
@@ -113,36 +83,70 @@ impl Game {
// If chunk position changed // If chunk position changed
if self.prev_chunk_pos == None || self.prev_chunk_pos != Some(chunk_pos) { if self.prev_chunk_pos == None || self.prev_chunk_pos != Some(chunk_pos) {
renderer.get_ui().set_text("World", 2, format!("Chunk: {}, {}, {}", chunk_pos.x, chunk_pos.y, chunk_pos.z));
// Shift world // Shift world
if self.prev_chunk_pos == None { self.prev_chunk_pos = Some(chunk_pos); } if self.prev_chunk_pos == None { self.prev_chunk_pos = Some(chunk_pos); }
let prev_chunk_pos = self.prev_chunk_pos.unwrap(); let prev_chunk_pos = self.prev_chunk_pos.unwrap();
let mut dir = chunk_pos - prev_chunk_pos; let mut dir = chunk_pos - prev_chunk_pos;
if dir.x != 0 { dir = Vector3{x: dir.x.clamp(-1, 1), y:0, z:0 } } if dir.x != 0 { dir = Vector3{x: dir.x.clamp(-1, 1), y:0, z:0 } }
else if dir.y != 0 { dir = Vector3{x:0, y:dir.y.clamp(-1, 1), z:0 } } else if dir.y != 0 { dir = Vector3{x:0, y:dir.y.clamp(-1, 1), z:0 } }
else if dir.z != 0 { dir = Vector3{x:0, y:0, z:dir.z.clamp(-1, 1) } } else if dir.z != 0 { dir = Vector3{x:0, y:0, z:dir.z.clamp(-1, 1) } }
else { return; } else { return; }
renderer.shift(dir * world::chunk::SIZE as i32);
// Load chunks in radius renderer.shift(dir * world::chunk::SIZE as i32);
for ox in -RENDER_DIST..RENDER_DIST {
for oz in -RENDER_DIST..RENDER_DIST {
let x = chunk_pos.x+ox;
let z = chunk_pos.z+oz;
self.world.load_chunk(Vector3{x, y: 0, z});
}
}
// Save previous // Save previous
self.prev_chunk_pos = Some(prev_chunk_pos+dir); self.prev_chunk_pos = Some(prev_chunk_pos+dir);
} }
// Send dirty to gpu // Load chunks in radius
for (pos, chunk) in self.world.all_chunks() { //TODO: y direction
if chunk.dirty { for ox in -RENDER_DIST..RENDER_DIST+1 {
chunk.dirty = false; for oz in -RENDER_DIST..RENDER_DIST+1 {
chunk.loaded = renderer.write_chunk(pos, chunk); // Load chunk
if !chunk.loaded { println!("Failed to load {:?}", pos); } let pos = Vector3{x: chunk_pos.x+ox, y: 0, z: chunk_pos.z+oz};
self.world.load(pos).and_then(|ch| -> Option<()> {
// Chunk is ready, schedule it for writing
if ch.state == ChunkState::Ready || ch.state == ChunkState::Stale {
ch.state = ChunkState::Waiting;
}
None
});
}
}
// Send waiting chunks to gpu
for (pos, chunk) in self.world.all() {
if let Some(chunk) = chunk {
// Skip stale chunks
if chunk.state == ChunkState::Stale {
continue;
}
// Calculate if chunk is within render distance
let ox = pos.x - chunk_pos.x;
let oy = pos.y - chunk_pos.y;
let oz = pos.z - chunk_pos.z;
let dist = RENDER_DIST;
let within_distance = ox > -dist && ox < dist && oy > -dist && oy < dist && oz > -dist && oz < dist;
// Check if chunk is within render distance
if within_distance && chunk.state == ChunkState::Waiting {
// Write chunk. If succeded change its state to Visible, otherwise make it Ready again.
if renderer.write_chunk(pos, chunk) {
chunk.state = ChunkState::Visible;
} else {
chunk.state = ChunkState::Ready;
}
}
// If not within render distance and set as visible
if !within_distance && chunk.state == ChunkState::Visible {
// Set chunk to stale, a.k.a. was visible but is not anymore
chunk.state = ChunkState::Stale;
}
} }
} }
} }
+11 -4
View File
@@ -19,12 +19,19 @@ const SIZE_QB: usize = SIZE*SIZE*SIZE;
// deflate fast 1.23kB 1.22s 5.46s // deflate fast 1.23kB 1.22s 5.46s
// lz4 1.6kB 0.07s 1.23s // lz4 1.6kB 0.07s 1.23s
#[derive(PartialEq)]
pub enum ChunkState {
Partial, // Not yet loaded
Ready, // Loaded
Waiting, // Scheduled to be uploaded
Visible, // Visible
Stale, // Scheduled to be unloaded
}
pub struct WorldChunk { pub struct WorldChunk {
nodes: Box<[u32]>, nodes: Box<[u32]>,
blocks: Vec<WorldBlock>, blocks: Vec<WorldBlock>,
pub dirty: bool, // Request write to GPU pub state: ChunkState,
pub loaded: bool // Written to GPU
} }
impl WorldChunk { impl WorldChunk {
@@ -32,8 +39,7 @@ impl WorldChunk {
Self { Self {
nodes: vec![0_u32; SIZE_QB].into_boxed_slice(), nodes: vec![0_u32; SIZE_QB].into_boxed_slice(),
blocks: Vec::new(), blocks: Vec::new(),
dirty: true, state: ChunkState::Partial,
loaded: false
} }
} }
@@ -68,6 +74,7 @@ impl WorldChunk {
instance.blocks.push(block); instance.blocks.push(block);
} }
instance.state = ChunkState::Ready;
Some(instance) Some(instance)
} }
+2 -1
View File
@@ -5,7 +5,7 @@ use std::io::{Read, Write};
use std::thread; use std::thread;
use std::sync::{mpsc, mpsc::Receiver, mpsc::Sender}; use std::sync::{mpsc, mpsc::Receiver, mpsc::Sender};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use super::{chunk, chunk::WorldChunk}; use super::{chunk, chunk::{WorldChunk, ChunkState}};
use crate::game::world::WorldGen; use crate::game::world::WorldGen;
type ChunkPos = Vector3<i32>; type ChunkPos = Vector3<i32>;
@@ -112,6 +112,7 @@ impl ChunkLoader {
WorldGen::generate(&(pos*chunk::SIZE as i32 + bpos), block); WorldGen::generate(&(pos*chunk::SIZE as i32 + bpos), block);
} }
} }
chunk.state = ChunkState::Ready;
// save // save
fs::create_dir_all(&filepath.parent().unwrap()).expect("Failed to create world directory"); fs::create_dir_all(&filepath.parent().unwrap()).expect("Failed to create world directory");
let mut file = fs::File::create(filepath).expect("Failed to create chunk file"); let mut file = fs::File::create(filepath).expect("Failed to create chunk file");
+18 -40
View File
@@ -3,9 +3,6 @@ mod data;
mod loader; mod loader;
pub mod chunk; pub mod chunk;
pub mod block; pub mod block;
use std::thread;
use std::sync::{mpsc, mpsc::Sender, mpsc::Receiver};
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;
@@ -21,7 +18,7 @@ use loader::ChunkLoader;
pub struct World { pub struct World {
data: WorldData, data: WorldData,
loader: ChunkLoader, loader: ChunkLoader,
chunks: HashMap<Vector3<i32>, WorldChunk>, chunks: HashMap<Vector3<i32>, Option<WorldChunk>>,
} }
impl World { impl World {
@@ -37,38 +34,31 @@ impl World {
instance instance
} }
/*
* Returns vec of references to all currently loaded chunks
*/
pub fn all(&mut self) -> Vec<(&Vector3<i32>, &mut Option<WorldChunk>)> {
self.chunks.iter_mut().collect()
}
/* /*
* Receives loaded chunks from ChunkLoader * Receives loaded chunks from ChunkLoader
*/ */
pub fn update(&mut self) { pub fn update(&mut self) {
for (pos, chunk) in self.loader.receive() { for (pos, chunk) in self.loader.receive() {
self.chunks.insert(pos, chunk); self.chunks.insert(pos, Some(chunk));
} }
} }
/* /*
* Sends chunk position to the ChunkLoader * Sends chunk position to the ChunkLoader if chunk is not loaded
* Returns loaded chunk otherwise
*/ */
pub fn load_chunk(&mut self, pos: Vector3<i32>) { pub fn load(&mut self, pos: Vector3<i32>) -> Option<&mut WorldChunk> {
if let Some(chunk) = self.chunks.get_mut(&pos) { let mut exists = false;
if !chunk.loaded { let result : Option<&mut WorldChunk> = self.chunks.entry(pos).and_modify(|_| { exists = true; }).or_insert(None).as_mut();
println!("Chunk {:?} exists but not loaded! Loading now", pos); if !exists { self.loader.load(pos); }
chunk.dirty = true; result
}
} else {
self.loader.load(pos);
}
}
/*
*
*/
pub fn unload_chunk(&mut self, pos: Vector3<i32>) {
if let Some(chunk) = self.chunks.get_mut(&pos) {
chunk.loaded = false;
chunk.dirty = false;
// TODO: Mark chunk as "to be unloaded" and send it to renderer for freeing resources
}
} }
/* /*
@@ -97,10 +87,12 @@ impl World {
// Save all chunks // Save all chunks
for (pos, chunk) in self.chunks.iter() { for (pos, chunk) in self.chunks.iter() {
if let Some(chunk) = chunk {
let filepath = dirpath.join(format!("chunk_{}_{}_{}.dat", pos.x, pos.y, pos.z)); let filepath = dirpath.join(format!("chunk_{}_{}_{}.dat", pos.x, pos.y, pos.z));
let mut file = fs::File::create(filepath).expect("Failed to create chunk file"); let mut file = fs::File::create(filepath).expect("Failed to create chunk file");
file.write(&chunk.to_bytes()).expect("Failed to write chunk file"); file.write(&chunk.to_bytes()).expect("Failed to write chunk file");
} }
}
// Save metadata // Save metadata
let datastr = toml::to_string(&self.data).unwrap(); let datastr = toml::to_string(&self.data).unwrap();
@@ -108,18 +100,4 @@ impl World {
let mut datafile = fs::File::create(datapath).expect("Failed to create world metadata file"); let mut datafile = fs::File::create(datapath).expect("Failed to create world metadata file");
datafile.write_all(datastr.as_bytes()).expect("Failed to write world metadata"); datafile.write_all(datastr.as_bytes()).expect("Failed to write world metadata");
} }
/*
* Returns chunk at given world chunk position
*/
pub fn get_chunk(&mut self, chunk_wpos: &Vector3<i32>) -> Option<&mut WorldChunk> {
self.chunks.get_mut(chunk_wpos)
}
/*
* Returns vec of references to all currently loaded chunks
*/
pub fn all_chunks(&mut self) -> Vec<(&Vector3<i32>, &mut WorldChunk)> {
self.chunks.iter_mut().collect()
}
} }
+4 -1
View File
@@ -1,3 +1,4 @@
use std::time::Instant;
use cgmath::Vector3; use cgmath::Vector3;
use crate::renderer::renderer_view::RendererView; use crate::renderer::renderer_view::RendererView;
use winit::{ use winit::{
@@ -89,9 +90,11 @@ fn main() {
// Loop // Loop
Event::MainEventsCleared => { Event::MainEventsCleared => {
// Update // Update
let timer = Instant::now();
let delta = delta_timer.elapsed().as_secs_f32(); let delta = delta_timer.elapsed().as_secs_f32();
game.update(delta, &mut renderer); game.update(delta, &mut renderer);
delta_timer = std::time::Instant::now(); delta_timer = Instant::now();
renderer.ui.set_text("Performance", 1, format!("Update: {} ms", timer.elapsed().as_millis()));
// Redraw if running // Redraw if running
//if let GameState::Running = game.get_state() { //if let GameState::Running = game.get_state() {
+5 -5
View File
@@ -133,7 +133,7 @@ impl Renderer {
// Update content // Update content
let timer = Instant::now(); let timer = Instant::now();
self.buffers.content.update(&self.queue); self.buffers.content.update(&self.queue);
self.ui.set_text("Performance", 1, format!("Update content: {}ms", timer.elapsed().as_micros() as f64 / 1000.0)); self.ui.set_text("Performance", 3, format!("Update content: {}ms", timer.elapsed().as_micros() as f64 / 1000.0));
// Update uniform buffer // Update uniform buffer
let timer = Instant::now(); let timer = Instant::now();
@@ -143,12 +143,12 @@ impl Renderer {
self.buffers.uniforms.values.update(&self.camera, cam_offset, self.start.elapsed().as_secs_f32()); self.buffers.uniforms.values.update(&self.camera, cam_offset, self.start.elapsed().as_secs_f32());
self.queue.write_buffer(&self.buffers.uniforms.buffer, 0, bytemuck::cast_slice(&[self.buffers.uniforms.values])); self.queue.write_buffer(&self.buffers.uniforms.buffer, 0, bytemuck::cast_slice(&[self.buffers.uniforms.values]));
self.ui.set_text("Performance", 2, format!("Update uniform: {}ms", timer.elapsed().as_micros() as f64 / 1000.0)); self.ui.set_text("Performance", 4, format!("Update uniform: {}ms", timer.elapsed().as_micros() as f64 / 1000.0));
// Get next frame to render to // Get next frame to render to
let timer = Instant::now(); let timer = Instant::now();
let frame = self.swapchain.get_current_frame()?.output; let frame = self.swapchain.get_current_frame()?.output;
self.ui.set_text("Performance", 3, format!("Swapchain get frame: {}ms", timer.elapsed().as_micros() as f64 / 1000.0)); self.ui.set_text("Performance", 5, format!("Swapchain get frame: {}ms", timer.elapsed().as_micros() as f64 / 1000.0));
// Create encoder that will build command buffer for us // Create encoder that will build command buffer for us
let timer = Instant::now(); let timer = Instant::now();
@@ -156,13 +156,13 @@ impl Renderer {
self.raytrace_pass.render(&mut encoder, &mut self.buffers, &self.texture);//&frame.view); self.raytrace_pass.render(&mut encoder, &mut self.buffers, &self.texture);//&frame.view);
self.postprocess_pass.render(&mut encoder, &frame.view); self.postprocess_pass.render(&mut encoder, &frame.view);
self.ui.render(&self.device, &mut encoder, &frame.view, 1280, 720); self.ui.render(&self.device, &mut encoder, &frame.view, 1280, 720);
self.ui.set_text("Performance", 4, format!("Build renderpass: {}ms", timer.elapsed().as_micros() as f64 / 1000.0)); self.ui.set_text("Performance", 6, format!("Build renderpass: {}ms", timer.elapsed().as_micros() as f64 / 1000.0));
// Submit encoder (command buffer) // Submit encoder (command buffer)
let timer = Instant::now(); let timer = Instant::now();
self.queue.submit(std::iter::once(encoder.finish())); self.queue.submit(std::iter::once(encoder.finish()));
self.ui.recall(); self.ui.recall();
self.ui.set_text("Performance", 5, format!("Submit queue: {}ms", timer.elapsed().as_micros() as f64 / 1000.0)); self.ui.set_text("Performance", 7, format!("Submit queue: {}ms", timer.elapsed().as_micros() as f64 / 1000.0));
// Stats // Stats
let content_stats = self.buffers.content.stats(); let content_stats = self.buffers.content.stats();