Compare commits

..
10 Commits
10 changed files with 294 additions and 216 deletions
+84 -62
View File
@@ -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
View File
@@ -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)
}
+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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(_) => {
+33 -24
View File
@@ -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);
}
}
+79 -33
View File
@@ -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
View File
@@ -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();
+7 -22
View File
@@ -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()
}
}
+1
View File
@@ -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; }