More informative chunk states, much cleaner
This commit is contained in:
+66
-62
@@ -7,6 +7,7 @@ use cgmath::Vector3;
|
||||
use winit::event::Event;
|
||||
use player::Player;
|
||||
use world::World;
|
||||
use world::chunk::{WorldChunk, ChunkState};
|
||||
|
||||
pub const RENDER_DIST: i32 = 3;
|
||||
|
||||
@@ -38,7 +39,7 @@ impl Game {
|
||||
// Preload chunks around player
|
||||
for x 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
|
||||
}
|
||||
|
||||
fn load_chunks(&mut self, renderer: &mut impl RendererView, chunk_pos: Vector3<i32>) {
|
||||
// Wait for previous chunks to be loaded before loading new ones
|
||||
if !renderer.is_busy() {
|
||||
if self.prev_chunk_pos == None { self.prev_chunk_pos = Some(chunk_pos); }
|
||||
let prev_chunk_pos = self.prev_chunk_pos.unwrap();
|
||||
let mut dir = chunk_pos - prev_chunk_pos;
|
||||
|
||||
// Shift max by 1, in one axis
|
||||
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.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);
|
||||
}
|
||||
fn debug_info(&mut self, renderer: &mut impl RendererView) {
|
||||
// Update pointing direction
|
||||
let yaw = self.player.camera_controller.get_yaw();
|
||||
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));
|
||||
}
|
||||
|
||||
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 = 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;
|
||||
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
|
||||
// let yaw = self.player.camera_controller.get_yaw();
|
||||
// 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));
|
||||
// }
|
||||
// Debug
|
||||
self.debug_info(renderer);
|
||||
|
||||
// Receives chunks from workers
|
||||
self.world.update();
|
||||
@@ -113,36 +83,70 @@ impl Game {
|
||||
|
||||
// If chunk position changed
|
||||
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
|
||||
if self.prev_chunk_pos == None { self.prev_chunk_pos = Some(chunk_pos); }
|
||||
let prev_chunk_pos = self.prev_chunk_pos.unwrap();
|
||||
let mut dir = chunk_pos - prev_chunk_pos;
|
||||
|
||||
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.z != 0 { dir = Vector3{x:0, y:0, z:dir.z.clamp(-1, 1) } }
|
||||
else { return; }
|
||||
renderer.shift(dir * world::chunk::SIZE as i32);
|
||||
|
||||
// Load chunks in radius
|
||||
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});
|
||||
}
|
||||
}
|
||||
renderer.shift(dir * world::chunk::SIZE as i32);
|
||||
|
||||
// Save previous
|
||||
self.prev_chunk_pos = Some(prev_chunk_pos+dir);
|
||||
}
|
||||
|
||||
// Send dirty to gpu
|
||||
for (pos, chunk) in self.world.all_chunks() {
|
||||
if chunk.dirty {
|
||||
chunk.dirty = false;
|
||||
chunk.loaded = renderer.write_chunk(pos, chunk);
|
||||
if !chunk.loaded { println!("Failed to load {:?}", pos); }
|
||||
// Load chunks in radius
|
||||
//TODO: y direction
|
||||
for ox in -RENDER_DIST..RENDER_DIST+1 {
|
||||
for oz in -RENDER_DIST..RENDER_DIST+1 {
|
||||
// Load chunk
|
||||
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
@@ -19,12 +19,19 @@ const SIZE_QB: usize = SIZE*SIZE*SIZE;
|
||||
// deflate fast 1.23kB 1.22s 5.46s
|
||||
// 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 {
|
||||
nodes: Box<[u32]>,
|
||||
blocks: Vec<WorldBlock>,
|
||||
pub dirty: bool, // Request write to GPU
|
||||
pub loaded: bool // Written to GPU
|
||||
pub state: ChunkState,
|
||||
}
|
||||
|
||||
impl WorldChunk {
|
||||
@@ -32,8 +39,7 @@ impl WorldChunk {
|
||||
Self {
|
||||
nodes: vec![0_u32; SIZE_QB].into_boxed_slice(),
|
||||
blocks: Vec::new(),
|
||||
dirty: true,
|
||||
loaded: false
|
||||
state: ChunkState::Partial,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +74,7 @@ impl WorldChunk {
|
||||
instance.blocks.push(block);
|
||||
}
|
||||
|
||||
instance.state = ChunkState::Ready;
|
||||
Some(instance)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::io::{Read, Write};
|
||||
use std::thread;
|
||||
use std::sync::{mpsc, mpsc::Receiver, mpsc::Sender};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use super::{chunk, chunk::WorldChunk};
|
||||
use super::{chunk, chunk::{WorldChunk, ChunkState}};
|
||||
use crate::game::world::WorldGen;
|
||||
|
||||
type ChunkPos = Vector3<i32>;
|
||||
@@ -112,6 +112,7 @@ impl ChunkLoader {
|
||||
WorldGen::generate(&(pos*chunk::SIZE as i32 + bpos), block);
|
||||
}
|
||||
}
|
||||
chunk.state = ChunkState::Ready;
|
||||
// save
|
||||
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");
|
||||
|
||||
+18
-40
@@ -3,9 +3,6 @@ mod data;
|
||||
mod loader;
|
||||
pub mod chunk;
|
||||
pub mod block;
|
||||
use std::thread;
|
||||
use std::sync::{mpsc, mpsc::Sender, mpsc::Receiver};
|
||||
use std::time::Instant;
|
||||
use std::path::PathBuf;
|
||||
use std::io::Write;
|
||||
use std::io::Read;
|
||||
@@ -21,7 +18,7 @@ use loader::ChunkLoader;
|
||||
pub struct World {
|
||||
data: WorldData,
|
||||
loader: ChunkLoader,
|
||||
chunks: HashMap<Vector3<i32>, WorldChunk>,
|
||||
chunks: HashMap<Vector3<i32>, Option<WorldChunk>>,
|
||||
}
|
||||
|
||||
impl World {
|
||||
@@ -37,38 +34,31 @@ impl World {
|
||||
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
|
||||
*/
|
||||
pub fn update(&mut self) {
|
||||
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>) {
|
||||
if let Some(chunk) = self.chunks.get_mut(&pos) {
|
||||
if !chunk.loaded {
|
||||
println!("Chunk {:?} exists but not loaded! Loading now", pos);
|
||||
chunk.dirty = true;
|
||||
}
|
||||
} 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
|
||||
}
|
||||
pub fn load(&mut self, pos: Vector3<i32>) -> Option<&mut WorldChunk> {
|
||||
let mut exists = false;
|
||||
let result : Option<&mut WorldChunk> = self.chunks.entry(pos).and_modify(|_| { exists = true; }).or_insert(None).as_mut();
|
||||
if !exists { self.loader.load(pos); }
|
||||
result
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -97,10 +87,12 @@ impl World {
|
||||
|
||||
// Save all chunks
|
||||
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 mut file = fs::File::create(filepath).expect("Failed to create chunk file");
|
||||
file.write(&chunk.to_bytes()).expect("Failed to write chunk file");
|
||||
}
|
||||
}
|
||||
|
||||
// Save metadata
|
||||
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");
|
||||
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
@@ -1,3 +1,4 @@
|
||||
use std::time::Instant;
|
||||
use cgmath::Vector3;
|
||||
use crate::renderer::renderer_view::RendererView;
|
||||
use winit::{
|
||||
@@ -89,9 +90,11 @@ fn main() {
|
||||
// Loop
|
||||
Event::MainEventsCleared => {
|
||||
// Update
|
||||
let timer = Instant::now();
|
||||
let delta = delta_timer.elapsed().as_secs_f32();
|
||||
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
|
||||
//if let GameState::Running = game.get_state() {
|
||||
|
||||
+5
-5
@@ -133,7 +133,7 @@ impl Renderer {
|
||||
// Update content
|
||||
let timer = Instant::now();
|
||||
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
|
||||
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.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
|
||||
let timer = Instant::now();
|
||||
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
|
||||
let timer = Instant::now();
|
||||
@@ -156,13 +156,13 @@ impl Renderer {
|
||||
self.raytrace_pass.render(&mut encoder, &mut self.buffers, &self.texture);//&frame.view);
|
||||
self.postprocess_pass.render(&mut encoder, &frame.view);
|
||||
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)
|
||||
let timer = Instant::now();
|
||||
self.queue.submit(std::iter::once(encoder.finish()));
|
||||
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
|
||||
let content_stats = self.buffers.content.stats();
|
||||
|
||||
Reference in New Issue
Block a user