198 lines
8.1 KiB
Rust
198 lines
8.1 KiB
Rust
use std::time::Instant;
|
|
use byteorder::{ByteOrder, LittleEndian};
|
|
use std::convert::TryInto;
|
|
use cgmath::Vector3;
|
|
use crate::game::world::block::WorldBlock;
|
|
|
|
//note: remember to update in main.frag
|
|
pub const BLOCK_SIZE : usize = 32; // 32x32x32 block size
|
|
pub const BLOCK_TEX_SIZE : usize = 40; // number of blocks in texture (32^3 = 1GB, 40^3 = 2GB)
|
|
pub const NODE_TEX_SIZE: usize = 128; // 32x32x32 nodes in texture
|
|
const BLOCK_SIZE_QB: usize = BLOCK_SIZE*BLOCK_SIZE*BLOCK_SIZE;
|
|
|
|
const BLOCK_TEX_SIZE_SQ: usize = BLOCK_TEX_SIZE*BLOCK_TEX_SIZE;
|
|
const BLOCK_TEX_SIZE_QB: usize = BLOCK_TEX_SIZE*BLOCK_TEX_SIZE*BLOCK_TEX_SIZE;
|
|
const NODE_TEX_SIZE_SQ: usize = NODE_TEX_SIZE*NODE_TEX_SIZE;
|
|
const NODE_TEX_SIZE_QB: usize = NODE_TEX_SIZE*NODE_TEX_SIZE*NODE_TEX_SIZE;
|
|
|
|
pub struct ContentStats {
|
|
pub node_tex_size: f64, // MB
|
|
pub block_tex_size: f64, // MB
|
|
pub block_tex_used: f64, // MB
|
|
pub block_count: u64, // amount
|
|
pub block_used: u64, // amount
|
|
}
|
|
|
|
pub struct Content {
|
|
node_buffer: Box<[u32]>,
|
|
node_texture: wgpu::Texture,
|
|
node_dirty: bool,
|
|
block_freeidx: usize,
|
|
block_texture: wgpu::Texture,
|
|
pub bind_layout: wgpu::BindGroupLayout,
|
|
pub bind_group: wgpu::BindGroup
|
|
}
|
|
|
|
impl Content {
|
|
|
|
pub fn new(device: &wgpu::Device) -> Self {
|
|
// Create node buffer
|
|
let node_buffer = vec![0_u32; (NODE_TEX_SIZE*NODE_TEX_SIZE*NODE_TEX_SIZE) as usize].into_boxed_slice();
|
|
|
|
// Create textures
|
|
let node_texture = device.create_texture(
|
|
&wgpu::TextureDescriptor {
|
|
mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D3,
|
|
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, label: Some("NodeTexture"),
|
|
size: wgpu::Extent3d{ width: NODE_TEX_SIZE as u32, height: NODE_TEX_SIZE as u32, depth: NODE_TEX_SIZE as u32 },
|
|
format: wgpu::TextureFormat::R32Uint,
|
|
}
|
|
);
|
|
let block_texture = device.create_texture(
|
|
&wgpu::TextureDescriptor {
|
|
mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D3,
|
|
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, label: Some("BlockTexture"),
|
|
size: wgpu::Extent3d{ width: (BLOCK_TEX_SIZE * BLOCK_SIZE) as u32, height: (BLOCK_TEX_SIZE * BLOCK_SIZE) as u32, depth: (BLOCK_TEX_SIZE * BLOCK_SIZE) as u32 },
|
|
format: wgpu::TextureFormat::R8Uint,
|
|
}
|
|
);
|
|
|
|
|
|
// Bind layout
|
|
let texture_entry = wgpu::BindGroupLayoutEntry {
|
|
binding: 0, visibility: wgpu::ShaderStage::FRAGMENT, count: None,
|
|
ty: wgpu::BindingType::Texture { multisampled: false, view_dimension: wgpu::TextureViewDimension::D3, sample_type: wgpu::TextureSampleType::Uint },
|
|
};
|
|
let bind_layout = device.create_bind_group_layout(
|
|
&wgpu::BindGroupLayoutDescriptor {
|
|
label: None, entries: &[
|
|
wgpu::BindGroupLayoutEntry { binding: 0, ..texture_entry },
|
|
wgpu::BindGroupLayoutEntry { binding: 1, ..texture_entry },
|
|
],
|
|
}
|
|
);
|
|
|
|
// Bind group
|
|
let bind_group = device.create_bind_group(
|
|
&wgpu::BindGroupDescriptor {
|
|
label: None, layout: &bind_layout,
|
|
entries: &[
|
|
wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView(&node_texture.create_view(&wgpu::TextureViewDescriptor::default())) },
|
|
wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::TextureView(&block_texture.create_view(&wgpu::TextureViewDescriptor::default())) },
|
|
],
|
|
}
|
|
);
|
|
|
|
// Done
|
|
let block_freeidx = 0;
|
|
Self { node_buffer, node_dirty: false, node_texture, block_freeidx, block_texture, bind_layout, bind_group }
|
|
}
|
|
|
|
pub fn stats(&self) -> ContentStats {
|
|
const TO_MB: f64 = 1.0 / (1024.0 * 1024.0);
|
|
|
|
let block_used = self.block_freeidx as u64;
|
|
let block_count = BLOCK_TEX_SIZE_QB as u64;
|
|
|
|
let node_tex_size = (NODE_TEX_SIZE_QB*4) as f64 * TO_MB;
|
|
let block_tex_used = (block_used * BLOCK_SIZE_QB as u64) as f64 * TO_MB;
|
|
let block_tex_size = (BLOCK_TEX_SIZE_QB*BLOCK_SIZE_QB) as f64 * TO_MB;
|
|
|
|
ContentStats { node_tex_size, block_tex_size, block_tex_used, block_count, block_used }
|
|
}
|
|
|
|
pub fn write(&mut self, queue: &wgpu::Queue, block_pos: &Vector3<i32>, block: &WorldBlock) {
|
|
|
|
// Get node
|
|
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;
|
|
|
|
// Allocate new block
|
|
if value == 0 {
|
|
value = self.block_freeidx + 1;
|
|
self.node_buffer[index] = value as u32;
|
|
self.block_freeidx = value;
|
|
//println!("Alloc: {}", value);
|
|
|
|
// Mark node buffer as dirty
|
|
self.node_dirty = true;
|
|
}
|
|
|
|
// Calculate position in block texture
|
|
let mut idx = value-1;
|
|
let z = idx / BLOCK_TEX_SIZE_SQ;
|
|
idx -= z * BLOCK_TEX_SIZE_SQ;
|
|
let y = idx / BLOCK_TEX_SIZE;
|
|
let x = idx % BLOCK_TEX_SIZE;
|
|
|
|
let block_origin = wgpu::Origin3d{ x: (x*BLOCK_SIZE) as u32, y: (y*BLOCK_SIZE) as u32, z: (z*BLOCK_SIZE) as u32 };
|
|
let block_size = wgpu::Extent3d{ width: BLOCK_SIZE as u32, height: BLOCK_SIZE as u32, depth: BLOCK_SIZE as u32 };
|
|
let block_bytes : [u8;BLOCK_SIZE*BLOCK_SIZE*BLOCK_SIZE] = block.materials.try_into().unwrap();
|
|
|
|
//println!("Y: {} Z: {}", y, z);
|
|
|
|
// Write block
|
|
queue.write_texture(
|
|
wgpu::TextureCopyView { texture: &self.block_texture, mip_level: 0, origin: block_origin }, &block_bytes,
|
|
wgpu::TextureDataLayout { offset: 0, bytes_per_row: block_size.width, rows_per_image: block_size.height }, block_size
|
|
);
|
|
}
|
|
|
|
pub fn shift(&mut self, offset: &Vector3<i32>) {
|
|
let timer = Instant::now();
|
|
let mut result = vec![0_u32; self.node_buffer.len()].into_boxed_slice();
|
|
let ox = offset.x as usize;
|
|
let oy = offset.y as usize;
|
|
let oz = offset.z as usize;
|
|
|
|
for x0 in 0..NODE_TEX_SIZE {
|
|
let x1 = x0+ox;
|
|
if x1 >= NODE_TEX_SIZE { continue; }
|
|
|
|
for y0 in 0..NODE_TEX_SIZE {
|
|
let y1 = y0+oy;
|
|
if y1 >= NODE_TEX_SIZE { continue; }
|
|
|
|
for z0 in 0..NODE_TEX_SIZE {
|
|
let z1 = z0+oz;
|
|
if z1 >= NODE_TEX_SIZE { continue; }
|
|
|
|
let idx0 = x0 + NODE_TEX_SIZE * (y0 + NODE_TEX_SIZE * z0);
|
|
let idx1 = x1 + NODE_TEX_SIZE * (y1 + NODE_TEX_SIZE * z1);
|
|
result[idx1] = self.node_buffer[idx0];
|
|
}
|
|
}
|
|
}
|
|
|
|
self.node_buffer = result;
|
|
self.node_dirty = true;
|
|
println!("Shifted in {} ms", timer.elapsed().as_millis());
|
|
}
|
|
|
|
fn write_nodes(&mut self, queue: &wgpu::Queue, z: usize) {
|
|
// 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[offset..offset+NODE_TEX_SIZE_SQ], &mut node_bytes);
|
|
|
|
// Write nodes
|
|
queue.write_texture(
|
|
wgpu::TextureCopyView { texture: &self.node_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
|
|
);
|
|
}
|
|
|
|
pub fn update(&mut self, queue: &wgpu::Queue) {
|
|
if self.node_dirty {
|
|
self.node_dirty = false;
|
|
|
|
let timer = Instant::now();
|
|
for z in 0..NODE_TEX_SIZE {
|
|
self.write_nodes(queue, z);
|
|
}
|
|
println!("Write nodes: {}", timer.elapsed().as_secs_f32());
|
|
}
|
|
}
|
|
} |