Implement based occlusion culling
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
03781e3dc8
commit
59e4172083
2 changed files with 81 additions and 8 deletions
|
@ -30,6 +30,7 @@ pub const CHUNK_ISIZE: isize = CHUNK_SIZE as isize;
|
|||
pub struct Chunk {
|
||||
pub blocks: [[[Option<Block>; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE],
|
||||
pub buffers: Option<GeometryBuffers<u16>>,
|
||||
pub full: bool,
|
||||
}
|
||||
|
||||
impl Default for Chunk {
|
||||
|
@ -37,6 +38,7 @@ impl Default for Chunk {
|
|||
Self {
|
||||
blocks: [[[None; CHUNK_SIZE]; CHUNK_SIZE]; CHUNK_SIZE],
|
||||
buffers: None,
|
||||
full: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -113,6 +115,21 @@ impl Chunk {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update_fullness(&mut self) {
|
||||
for y in 0..CHUNK_SIZE {
|
||||
for z in 0..CHUNK_SIZE {
|
||||
for x in 0..CHUNK_SIZE {
|
||||
if self.blocks[y][z][x].is_none() {
|
||||
self.full = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.full = true;
|
||||
}
|
||||
|
||||
pub fn generate(&mut self, chunk_x: isize, chunk_y: isize, chunk_z: isize) {
|
||||
let fbm = noise::Fbm::new();
|
||||
|
||||
|
@ -403,6 +420,8 @@ impl Chunk {
|
|||
&Self::quads_to_geometry(quads),
|
||||
BufferUsage::empty(),
|
||||
));
|
||||
|
||||
self.update_fullness();
|
||||
}
|
||||
|
||||
pub fn save(&self, position: Point3<isize>, store: &sled::Db) -> anyhow::Result<()> {
|
||||
|
|
|
@ -18,7 +18,7 @@ use crate::{
|
|||
view::View,
|
||||
world::{
|
||||
block::{Block, BlockType},
|
||||
chunk::{Chunk, CHUNK_ISIZE},
|
||||
chunk::{Chunk, CHUNK_ISIZE, CHUNK_SIZE},
|
||||
npc::Npc,
|
||||
},
|
||||
};
|
||||
|
@ -44,6 +44,8 @@ pub struct World {
|
|||
pub chunk_save_queue: VecDeque<(Point3<isize>, bool)>,
|
||||
pub chunk_load_queue: VecDeque<Point3<isize>>,
|
||||
pub chunk_generate_queue: VecDeque<Point3<isize>>,
|
||||
pub chunk_occlusion_position: Option<Point3<isize>>,
|
||||
pub chunks_visible: Option<Vec<Point3<isize>>>,
|
||||
|
||||
pub highlighted: Option<(Point3<isize>, Vector3<i32>)>,
|
||||
|
||||
|
@ -119,7 +121,7 @@ impl World {
|
|||
|
||||
let start = Instant::now() - render_time;
|
||||
let mut chunk_updates = 0;
|
||||
while chunk_updates == 0 || start.elapsed() < Duration::from_millis(16) {
|
||||
while chunk_updates == 0 || start.elapsed() < Duration::from_millis(15) {
|
||||
if let Some(position) = self.chunk_load_queue.pop_front() {
|
||||
let chunk = self.chunks.entry(position).or_default();
|
||||
match chunk.load(position, &self.chunk_database) {
|
||||
|
@ -166,15 +168,22 @@ impl World {
|
|||
|
||||
chunk_updates += 1;
|
||||
}
|
||||
|
||||
if chunk_updates > 0 {
|
||||
self.chunk_occlusion_position = None;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render<'a>(
|
||||
&'a self,
|
||||
&'a mut self,
|
||||
render_context: &RenderContext,
|
||||
render_encoder: &mut CommandEncoder,
|
||||
frame: &SwapChainTexture,
|
||||
view: &View,
|
||||
) -> usize {
|
||||
// TODO Move this to update
|
||||
self.update_occlusion(view);
|
||||
|
||||
let mut render_pass = render_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("render_pass"),
|
||||
color_attachments: &[wgpu::RenderPassColorAttachment {
|
||||
|
@ -206,14 +215,13 @@ impl World {
|
|||
render_pass.set_bind_group(1, &view.bind_group, &[]);
|
||||
render_pass.set_bind_group(2, &self.time_bind_group, &[]);
|
||||
|
||||
let visible = self.chunks_visible.as_ref().unwrap();
|
||||
let mut triangle_count = 0;
|
||||
|
||||
for (position, chunk) in &self.chunks {
|
||||
triangle_count += chunk.render(&mut render_pass, position, view);
|
||||
for position in visible {
|
||||
let chunk = self.chunks.get(position).unwrap();
|
||||
triangle_count += chunk.render(&mut render_pass, &position, view);
|
||||
}
|
||||
|
||||
triangle_count += self.npc.render(&mut render_pass);
|
||||
|
||||
triangle_count
|
||||
}
|
||||
|
||||
|
@ -345,6 +353,8 @@ impl World {
|
|||
chunk_load_queue: VecDeque::new(),
|
||||
chunk_save_queue: VecDeque::new(),
|
||||
chunk_generate_queue: VecDeque::new(),
|
||||
chunk_occlusion_position: None,
|
||||
chunks_visible: None,
|
||||
|
||||
highlighted: None,
|
||||
|
||||
|
@ -352,6 +362,50 @@ impl World {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update_occlusion(&mut self, view: &View) {
|
||||
let initial_position = view
|
||||
.camera
|
||||
.position
|
||||
.map(|x| (x.floor() as isize).div_euclid(CHUNK_ISIZE));
|
||||
|
||||
if self.chunk_occlusion_position == Some(initial_position) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.chunk_occlusion_position = Some(initial_position);
|
||||
let mut queue = VecDeque::from(vec![initial_position]);
|
||||
|
||||
assert_eq!(CHUNK_SIZE, 32);
|
||||
let mut visited = [0u32; CHUNK_SIZE * CHUNK_SIZE];
|
||||
let mut render_queue = Vec::new();
|
||||
|
||||
while !queue.is_empty() {
|
||||
let position = queue.pop_front().unwrap();
|
||||
|
||||
let b = position.map(|x| x.rem_euclid(CHUNK_ISIZE) as usize);
|
||||
if (visited[b.x * CHUNK_SIZE + b.y] >> b.z) & 1 == 1 {
|
||||
continue;
|
||||
}
|
||||
visited[b.x * CHUNK_SIZE + b.y] |= 1 << b.z;
|
||||
|
||||
if let Some(chunk) = self.chunks.get(&position) {
|
||||
render_queue.push(position);
|
||||
if !chunk.full {
|
||||
queue.extend(&[
|
||||
position + Vector3::unit_x(),
|
||||
position - Vector3::unit_x(),
|
||||
position + Vector3::unit_y(),
|
||||
position - Vector3::unit_y(),
|
||||
position + Vector3::unit_z(),
|
||||
position - Vector3::unit_z(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.chunks_visible = Some(render_queue);
|
||||
}
|
||||
|
||||
pub fn enqueue_chunk_save(&mut self, position: Point3<isize>, unload: bool) {
|
||||
if let Some((_, unload_)) = self
|
||||
.chunk_save_queue
|
||||
|
|
Loading…
Reference in a new issue