Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
729daa1503 | ||
|
|
dac7c52912 | ||
|
|
7daec14ba7 | ||
|
|
a5d109fb0a | ||
|
|
e9632a20d2 | ||
|
|
c4daecae6f | ||
|
|
15e40a941c | ||
|
|
3718c2145e | ||
|
|
10182730a5 | ||
|
|
9fb88c634b |
+84
-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;
|
||||
|
||||
@@ -36,48 +37,25 @@ impl Game {
|
||||
};
|
||||
|
||||
// Preload chunks around player
|
||||
let timer = Instant::now();
|
||||
let mut count = 0;
|
||||
for x in -RENDER_DIST..RENDER_DIST+1 {
|
||||
for z in -RENDER_DIST..RENDER_DIST+1 {
|
||||
let pos = Vector3{x, y: 0, z};
|
||||
instance.world.load_chunk(pos);
|
||||
count += 1;
|
||||
for x in -RENDER_DIST..RENDER_DIST {
|
||||
for z in -RENDER_DIST..RENDER_DIST {
|
||||
instance.world.load(Vector3{x, y: 0, z});
|
||||
}
|
||||
}
|
||||
println!("Preloaded {} chunks in {} ms (about {} voxels)", count, timer.elapsed().as_millis(), count * world::block::SIZE_QB);
|
||||
|
||||
// Return generated instance
|
||||
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 !self.world.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+1 {
|
||||
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) {
|
||||
@@ -87,43 +65,87 @@ impl Game {
|
||||
|
||||
// Get positions
|
||||
let cam_pos = camera.get_position();
|
||||
let cam_pos = Vector3{x: cam_pos.x as i32, y: 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;
|
||||
// why was this there? lol
|
||||
//if cam_pos.x < 0 { chunk_pos.x -= 1; }
|
||||
//if cam_pos.y < 0 { chunk_pos.y -= 1; }
|
||||
//if cam_pos.z < 0 { chunk_pos.z -= 1; }
|
||||
|
||||
// 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));
|
||||
self.load_chunks(renderer, chunk_pos);
|
||||
}
|
||||
// Debug
|
||||
self.debug_info(renderer);
|
||||
|
||||
// Receives chunks from workers
|
||||
self.world.update();
|
||||
|
||||
// Send dirty chunks to renderer
|
||||
if chunk_pos == self.prev_chunk_pos.unwrap_or(chunk_pos) {
|
||||
for (pos, chunk) in self.world.all_chunks() {
|
||||
if chunk.dirty {
|
||||
renderer.write_chunk(pos, chunk);
|
||||
chunk.dirty = false;
|
||||
// Load, unload or write chunks to gpu
|
||||
if !renderer.is_busy() {
|
||||
|
||||
// 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);
|
||||
|
||||
// Save previous
|
||||
self.prev_chunk_pos = Some(prev_chunk_pos+dir);
|
||||
}
|
||||
|
||||
// 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, oy, oz) = (pos.x - chunk_pos.x, pos.y - chunk_pos.y, pos.z - chunk_pos.z);
|
||||
let within_distance = ox > -RENDER_DIST && ox < RENDER_DIST
|
||||
&& oy > -RENDER_DIST && oy < RENDER_DIST
|
||||
&& oz > -RENDER_DIST && oz < RENDER_DIST;
|
||||
|
||||
// Within render distance and waiting
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Outside render distance and 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-5
@@ -13,19 +13,25 @@ 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
|
||||
|
||||
#[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 // true by default, set to false when uploaded to renderer. should be set to true when unloaded (TODO)
|
||||
pub state: ChunkState,
|
||||
}
|
||||
|
||||
impl WorldChunk {
|
||||
@@ -33,7 +39,7 @@ impl WorldChunk {
|
||||
Self {
|
||||
nodes: vec![0_u32; SIZE_QB].into_boxed_slice(),
|
||||
blocks: Vec::new(),
|
||||
dirty: true
|
||||
state: ChunkState::Partial,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +49,7 @@ impl WorldChunk {
|
||||
// Header
|
||||
let (header_bytes, bytes) = bytes.split_at(128);
|
||||
let file_ver = header_bytes[0];
|
||||
let mut num_blocks = LittleEndian::read_u32(&header_bytes[1..5]) as usize;
|
||||
let num_blocks = LittleEndian::read_u32(&header_bytes[1..5]) as usize;
|
||||
assert_eq!(file_ver, 1);
|
||||
|
||||
// Nodes
|
||||
@@ -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");
|
||||
|
||||
+16
-32
@@ -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,8 +18,7 @@ use loader::ChunkLoader;
|
||||
pub struct World {
|
||||
data: WorldData,
|
||||
loader: ChunkLoader,
|
||||
chunks: HashMap<Vector3<i32>, WorldChunk>,
|
||||
busy: u32
|
||||
chunks: HashMap<Vector3<i32>, Option<WorldChunk>>,
|
||||
}
|
||||
|
||||
impl World {
|
||||
@@ -32,15 +28,17 @@ impl World {
|
||||
data: WorldData { name: name.clone(), ..Default::default() },
|
||||
loader: ChunkLoader::new(name.clone()),
|
||||
chunks: HashMap::new(),
|
||||
busy: 0,
|
||||
};
|
||||
instance.load_metadata();
|
||||
instance.save_all();
|
||||
instance
|
||||
}
|
||||
|
||||
pub fn is_busy(&self) -> bool {
|
||||
self.busy > 0
|
||||
/*
|
||||
* 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()
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -48,21 +46,19 @@ impl World {
|
||||
*/
|
||||
pub fn update(&mut self) {
|
||||
for (pos, chunk) in self.loader.receive() {
|
||||
self.chunks.insert(pos, chunk);
|
||||
self.busy -= 1;
|
||||
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) {
|
||||
chunk.dirty = true;
|
||||
} else {
|
||||
self.loader.load(pos);
|
||||
self.busy += 1;
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -91,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();
|
||||
@@ -102,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()
|
||||
}
|
||||
}
|
||||
+6
-3
@@ -1,3 +1,4 @@
|
||||
use std::time::Instant;
|
||||
use cgmath::Vector3;
|
||||
use crate::renderer::renderer_view::RendererView;
|
||||
use winit::{
|
||||
@@ -89,14 +90,16 @@ 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() {
|
||||
//if let GameState::Running = game.get_state() {
|
||||
window.request_redraw();
|
||||
}
|
||||
//}
|
||||
}
|
||||
// Draw
|
||||
Event::RedrawRequested(_) => {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::time::Instant;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use std::convert::TryInto;
|
||||
use cgmath::Vector3;
|
||||
use crate::game::world::block::WorldBlock;
|
||||
@@ -82,11 +80,22 @@ impl Content {
|
||||
// Done
|
||||
let nodes = Nodes::new();
|
||||
let block_freeidx = 0;
|
||||
let node_dirty = false;
|
||||
let block_freed = Vec::new();
|
||||
Self { nodes, block_freed, node_texture, block_freeidx, block_texture, bind_layout, bind_group }
|
||||
}
|
||||
|
||||
pub fn is_busy(&self) -> bool {
|
||||
self.nodes.is_busy()
|
||||
}
|
||||
|
||||
pub fn get_world_offset(&self) -> Vector3<i32> {
|
||||
self.nodes.get_world_offset()
|
||||
}
|
||||
|
||||
pub fn update(&mut self, queue: &wgpu::Queue) {
|
||||
self.nodes.update(queue, &self.node_texture);
|
||||
}
|
||||
|
||||
pub fn stats(&self) -> ContentStats {
|
||||
const TO_MB: f64 = 1.0 / (1024.0 * 1024.0);
|
||||
|
||||
@@ -101,22 +110,6 @@ impl Content {
|
||||
ContentStats { node_tex_size, block_tex_size, block_tex_used, block_count, block_freed, block_used }
|
||||
}
|
||||
|
||||
pub fn free(&mut self, block_pos: Vector3<i32>) {
|
||||
|
||||
// 0,0 at node buffer center
|
||||
let half = NODE_TEX_SIZE as i32 / 2;
|
||||
let pos = block_pos + &Vector3{x: half, y: half, z: half};
|
||||
|
||||
// Get node
|
||||
let index = (pos.x + NODE_TEX_SIZE as i32 * (pos.y + NODE_TEX_SIZE as i32 * pos.z)) as usize;
|
||||
let value = self.nodes.get_node(index) as usize;
|
||||
|
||||
// Free it if used
|
||||
if value != 0 {
|
||||
self.block_freed.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(&mut self, queue: &wgpu::Queue, block_pos: &Vector3<i32>, block: &WorldBlock) {
|
||||
|
||||
// 0,0 at node buffer center
|
||||
@@ -172,11 +165,27 @@ impl Content {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn shift(&mut self, offset: &Vector3<i32>) -> bool {
|
||||
self.nodes.shift(offset)
|
||||
}
|
||||
pub fn shift(&mut self, offset: &Vector3<i32>) {
|
||||
|
||||
pub fn update(&mut self, queue: &wgpu::Queue) {
|
||||
self.nodes.update(queue, &self.node_texture);
|
||||
// Work in progress.
|
||||
// Note: [z is swapped with x ], [ node buffer is not equal to render distance?]
|
||||
|
||||
// if offset.x > 0 {
|
||||
// let size = NODE_TEX_SIZE as i32;
|
||||
|
||||
// for x in 0..128 {
|
||||
// for y in 0..128 {
|
||||
// for z in 0..32 {
|
||||
// let index = (x + size * (y + size * z)) as usize;
|
||||
// let value = self.nodes.get_node(index);
|
||||
// if value != 0 { self.block_freed.push(value as usize); }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// println!("Saved: {}", self.block_freed.len());
|
||||
// }
|
||||
|
||||
self.nodes.shift(offset);
|
||||
}
|
||||
}
|
||||
@@ -1,83 +1,129 @@
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::cmp::{min, max};
|
||||
use std::time::Instant;
|
||||
use cgmath::Vector3;
|
||||
use std::thread;
|
||||
use std::sync::{RwLock, mpsc, mpsc::Receiver, mpsc::Sender};
|
||||
use byteorder::{LittleEndian, ByteOrder};
|
||||
|
||||
pub const NODE_TEX_SIZE: usize = 128; // 32x32x32 nodes in texture
|
||||
pub const NODE_TEX_SIZE_SQ: usize = NODE_TEX_SIZE*NODE_TEX_SIZE;
|
||||
pub const NODE_TEX_SIZE_QB: usize = NODE_TEX_SIZE*NODE_TEX_SIZE*NODE_TEX_SIZE;
|
||||
|
||||
type NodeBuffer = (Vector3<i32>, Box<[u32]>);
|
||||
|
||||
pub struct Nodes {
|
||||
node_buffer: [Box<[u32]>; 2],
|
||||
active_buffer: usize,
|
||||
modified: bool,
|
||||
buffer: Arc<RwLock<Box<[u32]>>>,
|
||||
offset: Vector3<i32>,
|
||||
dirty: bool,
|
||||
shift: Option<Vector3<i32>>,
|
||||
working: bool,
|
||||
channel: (Sender<NodeBuffer>, Receiver<NodeBuffer>)
|
||||
}
|
||||
|
||||
impl Nodes {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
node_buffer: [vec![0_u32; NODE_TEX_SIZE_QB].into_boxed_slice(), vec![0_u32; NODE_TEX_SIZE_QB].into_boxed_slice()],
|
||||
active_buffer: 0,
|
||||
modified: false,
|
||||
// Present
|
||||
buffer: Arc::new(RwLock::new(vec![0_u32; NODE_TEX_SIZE_QB].into_boxed_slice())),
|
||||
offset: Vector3{x:0,y:0,z:0},
|
||||
dirty: false,
|
||||
|
||||
shift: None,
|
||||
working: false,
|
||||
channel: mpsc::channel()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_world_offset(&self) -> Vector3<i32> {
|
||||
self.offset
|
||||
}
|
||||
|
||||
pub fn get_node(&self, index: usize) -> u32 {
|
||||
self.node_buffer[self.active_buffer][index]
|
||||
self.buffer.read().unwrap()[index]
|
||||
}
|
||||
|
||||
pub fn is_busy(&self) -> bool {
|
||||
self.shift != None
|
||||
}
|
||||
|
||||
pub fn set_node(&mut self, index: usize, value: u32) {
|
||||
self.node_buffer[self.active_buffer][index] = value;
|
||||
self.modified = true;
|
||||
self.buffer.write().unwrap()[index] = value;
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
pub fn shift(&mut self, offset: &Vector3<i32>) -> bool {
|
||||
pub fn shift(&mut self, offset: &Vector3<i32>) {
|
||||
if self.shift == None {
|
||||
self.shift = Some(*offset);
|
||||
} else {
|
||||
println!("Tried to shift while another shifing was in progress. Check RendererView.is_busy() before shifting.");
|
||||
}
|
||||
}
|
||||
|
||||
let a = if self.active_buffer == 0 { 0 } else { 1 };
|
||||
let b = if self.active_buffer == 0 { 1 } else { 0 };
|
||||
pub fn update(&mut self, queue: &wgpu::Queue, texture: &wgpu::Texture) {
|
||||
|
||||
// Check if there is some more work
|
||||
if !self.working {
|
||||
if let Some(offset) = self.shift {
|
||||
let source = self.buffer.clone();
|
||||
let tx = self.channel.0.clone();
|
||||
self.working = true;
|
||||
|
||||
// Spawn worker
|
||||
thread::spawn(move || {
|
||||
let source = source.read().unwrap();
|
||||
|
||||
let (ox, oy, oz) = (offset.x, offset.y, offset.z);
|
||||
let size = NODE_TEX_SIZE as i32;
|
||||
let mut target = vec![0_u32; NODE_TEX_SIZE_QB].into_boxed_slice();
|
||||
let (ox, oy, oz) = (offset.x, offset.y, offset.z);
|
||||
|
||||
for i in self.node_buffer[b].iter_mut() {
|
||||
*i = 0;
|
||||
}
|
||||
|
||||
// Copy slice of source buffer into target buffer
|
||||
for x in max(0, -ox)..min(size-ox, size) {
|
||||
for y in max(0, -oy)..min(size-oy, size) {
|
||||
for z in max(0, -oz)..min(size-oz, size) {
|
||||
let idx0 = x + size * (y + size * z);
|
||||
let idx1 = (x+ox) + size * ((y+oy) + size * (z+oz));
|
||||
self.node_buffer[b][idx1 as usize] = self.node_buffer[a][idx0 as usize];
|
||||
target[idx1 as usize] = source[idx0 as usize];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.active_buffer = b;
|
||||
self.modified = true;
|
||||
true
|
||||
// Send result
|
||||
tx.send((offset, target)).unwrap();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, queue: &wgpu::Queue, texture: &wgpu::Texture) {
|
||||
if self.modified {
|
||||
self.modified = false;
|
||||
// Receive work
|
||||
while let Ok(result) = self.channel.1.try_recv() {
|
||||
let moved_by = result.0;
|
||||
|
||||
// Write all nodes to gpu
|
||||
for z in 0..NODE_TEX_SIZE {
|
||||
self.offset += moved_by;
|
||||
self.buffer = Arc::new(RwLock::new(result.1));
|
||||
self.shift = None;
|
||||
self.dirty = true;
|
||||
self.working = false;
|
||||
|
||||
}
|
||||
|
||||
if self.dirty {
|
||||
self.dirty = false;
|
||||
|
||||
// Note: this takes usually 2ms, up to 9ms
|
||||
// TODO: Maybe instead of converting u32 to u8, just store u8 and convert them when writing/reading
|
||||
|
||||
// Convert u32 node buffer to u8
|
||||
let node_origin = wgpu::Origin3d{ x:0, y: 0, z: z as u32 };
|
||||
let node_size = wgpu::Extent3d { width: NODE_TEX_SIZE as u32, height: NODE_TEX_SIZE as u32, depth: 1 };
|
||||
let mut node_bytes = [0_u8; NODE_TEX_SIZE_SQ*4];
|
||||
let offset = NODE_TEX_SIZE_SQ*z;
|
||||
LittleEndian::write_u32_into(&self.node_buffer[self.active_buffer][offset..offset+NODE_TEX_SIZE_SQ], &mut node_bytes);
|
||||
let node_origin = wgpu::Origin3d{ x:0, y: 0, z: 0 };
|
||||
let node_size = wgpu::Extent3d { width: NODE_TEX_SIZE as u32, height: NODE_TEX_SIZE as u32, depth: NODE_TEX_SIZE as u32 };
|
||||
let mut node_bytes = vec![0_u8; NODE_TEX_SIZE_QB*4].into_boxed_slice();
|
||||
LittleEndian::write_u32_into(&self.buffer.read().unwrap()[..], &mut node_bytes[..]);
|
||||
|
||||
// Write nodes
|
||||
queue.write_texture(
|
||||
wgpu::TextureCopyView { texture: texture, mip_level: 0, origin: node_origin }, &node_bytes,
|
||||
wgpu::TextureCopyView { texture: texture, mip_level: 0, origin: node_origin }, &node_bytes[..],
|
||||
wgpu::TextureDataLayout { offset: 0, bytes_per_row: 4*node_size.width, rows_per_image: node_size.height }, node_size
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
-15
@@ -26,8 +26,7 @@ pub struct Renderer {
|
||||
buffers: Buffers,
|
||||
// Other
|
||||
pub camera: Camera,
|
||||
start: std::time::Instant,
|
||||
world_offset: Vector3<i32>
|
||||
start: std::time::Instant
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
@@ -67,7 +66,7 @@ impl Renderer {
|
||||
|
||||
// Middleman texture
|
||||
let texture_descriptor = wgpu::TextureDescriptor {
|
||||
label: Some("glow_post_process_texture1"),
|
||||
label: Some("Middle texture"),
|
||||
size: wgpu::Extent3d { width: swapchain_desc.width,
|
||||
height: swapchain_desc.height,
|
||||
depth: 1,
|
||||
@@ -88,8 +87,7 @@ impl Renderer {
|
||||
|
||||
// Other
|
||||
let start = std::time::Instant::now();
|
||||
let world_offset = Vector3{x:0, y:0, z:0};
|
||||
Self { surface, device, queue, swapchain_desc, swapchain, texture, raytrace_pass, postprocess_pass, ui, buffers, camera, start, world_offset }
|
||||
Self { surface, device, queue, swapchain_desc, swapchain, texture, raytrace_pass, postprocess_pass, ui, buffers, camera, start }
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -105,8 +103,26 @@ impl Renderer {
|
||||
// Update camera
|
||||
self.camera.set_aspect(size.width as f32 / size.height as f32);
|
||||
}
|
||||
|
||||
// Recreate swapchain
|
||||
self.swapchain = self.device.create_swap_chain(&self.surface, &self.swapchain_desc);
|
||||
|
||||
// Recreate texture
|
||||
let texture_descriptor = wgpu::TextureDescriptor {
|
||||
label: Some("Middle texture"),
|
||||
size: wgpu::Extent3d {
|
||||
width: self.swapchain_desc.width,
|
||||
height: self.swapchain_desc.height,
|
||||
depth: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: self.swapchain_desc.format,
|
||||
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::RENDER_ATTACHMENT,
|
||||
};
|
||||
self.texture = self.device.create_texture(&texture_descriptor).create_view(&wgpu::TextureViewDescriptor::default());
|
||||
self.postprocess_pass = PostprocessPass::new(&self.device, &self.swapchain_desc, &self.texture);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -114,21 +130,25 @@ impl Renderer {
|
||||
*/
|
||||
pub fn render(&mut self) -> Result<(), wgpu::SwapChainError> {
|
||||
|
||||
// Update uniform buffer
|
||||
let half = (buffers::nodes::NODE_TEX_SIZE / 2) as i32;
|
||||
let cam_offset = Vector3{x: (self.world_offset.x + half) as f32, y: (self.world_offset.y + half) as f32, z: (self.world_offset.z + half) as 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]));
|
||||
|
||||
// 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();
|
||||
let off = self.buffers.content.get_world_offset();
|
||||
let half = (buffers::nodes::NODE_TEX_SIZE / 2) as i32;
|
||||
let cam_offset = Vector3{x: (off.x + half) as f32, y: (off.y + half) as f32, z: (off.z + half) as 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.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", 2, 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();
|
||||
@@ -136,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", 3, 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", 4, 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();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::renderer::UserInterface;
|
||||
use crate::renderer::camera::CameraTransform;
|
||||
use crate::Renderer;
|
||||
use crate::{game, game::world::{chunk, chunk::WorldChunk, block::WorldBlock}};
|
||||
use crate::{game, game::world::{chunk, chunk::WorldChunk}};
|
||||
use cgmath::Vector3;
|
||||
|
||||
pub trait RendererView {
|
||||
@@ -9,22 +9,21 @@ pub trait RendererView {
|
||||
fn shift(&mut self, offset: Vector3<i32>);
|
||||
fn get_camera_transform(&mut self) -> &mut dyn CameraTransform;
|
||||
fn get_ui(&mut self) -> &mut UserInterface;
|
||||
fn get_world_offset(&self) -> Vector3<i32>;
|
||||
fn free_chunk(&mut self, chunk_pos: Vector3<i32>);
|
||||
fn is_busy(&self) -> bool;
|
||||
}
|
||||
|
||||
impl RendererView for Renderer {
|
||||
|
||||
fn write_chunk(&mut self, chunk_pos: &Vector3<i32>, chunk: &WorldChunk) -> bool {
|
||||
// Calculate chunk position in renderer space
|
||||
let chunk_off = self.world_offset / chunk::SIZE as i32;
|
||||
let chunk_off = self.buffers.content.get_world_offset() / chunk::SIZE as i32;
|
||||
let pos = chunk_pos + chunk_off;
|
||||
// Make sure we are withing current world bounds
|
||||
if pos.x.abs() > game::RENDER_DIST { println!("out of bounds! x: {} off: {:?} pos: {:?}", pos.x, chunk_off, chunk_pos); return false; }
|
||||
if pos.y.abs() > game::RENDER_DIST { println!("out of bounds! y: {} off: {:?} pos: {:?}", pos.y, chunk_off, chunk_pos); return false; }
|
||||
if pos.z.abs() > game::RENDER_DIST { println!("out of bounds! z: {} off: {:?} pos: {:?}", pos.z, chunk_off, chunk_pos); return false; }
|
||||
// Write chunk to renderer
|
||||
println!("write chunk {:?}", pos);
|
||||
//println!("write chunk {:?}", pos);
|
||||
let pos = pos * chunk::SIZE as i32;
|
||||
for (block_pos, block) in chunk.all_blocks() {
|
||||
self.buffers.content.write(&self.queue, &(block_pos + pos), block);
|
||||
@@ -32,26 +31,12 @@ impl RendererView for Renderer {
|
||||
true
|
||||
}
|
||||
|
||||
fn free_chunk(&mut self, chunk_pos: Vector3<i32>) {
|
||||
// Free all blocks within chunk
|
||||
let size = chunk::SIZE as i32;
|
||||
let pos = chunk_pos * size;
|
||||
for x in 0..size {
|
||||
for y in 0..size {
|
||||
for z in 0..size {
|
||||
self.buffers.content.free(pos + Vector3{x, y, z});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* offset: By how much to shift the world, in blocks
|
||||
*/
|
||||
fn shift(&mut self, offset: Vector3<i32>) {
|
||||
self.buffers.content.shift(&-offset);
|
||||
self.world_offset -= offset;
|
||||
println!("World shifted by {:?}. World offset is now {:?}", offset, self.world_offset);
|
||||
//println!("World shifted by {:?}", offset);
|
||||
}
|
||||
|
||||
fn get_camera_transform(&mut self) -> &mut dyn CameraTransform {
|
||||
@@ -62,7 +47,7 @@ impl RendererView for Renderer {
|
||||
&mut self.ui
|
||||
}
|
||||
|
||||
fn get_world_offset(&self) -> Vector3<i32> {
|
||||
self.world_offset
|
||||
fn is_busy(&self) -> bool {
|
||||
self.buffers.content.is_busy()
|
||||
}
|
||||
}
|
||||
@@ -98,6 +98,7 @@ HitResult castNodes(Ray ray, uint maxSteps)
|
||||
incAxis = step(dist.xyz, dist.yzx) * step(dist.xyz, dist.zxy);
|
||||
dist += incAxis * raySign * rayInv;
|
||||
pos += incAxis * raySign;
|
||||
t = dot(dist, incAxis);
|
||||
|
||||
// Outside bounds
|
||||
// if(pos.x < 0 || pos.y < 0 || pos.z < 0) { break; }
|
||||
|
||||
Reference in New Issue
Block a user