Added frustum culling for chunks

This commit is contained in:
Joshua Barretto 2019-06-06 10:04:37 +01:00
parent 66cd8de0e1
commit 8da46b48ac
4 changed files with 81 additions and 29 deletions

7
Cargo.lock generated
View File

@ -782,6 +782,11 @@ name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "frustum_query"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
@ -2635,6 +2640,7 @@ dependencies = [
"euc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"frustum_query 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"gfx 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gfx_device_gl 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)",
"gfx_window_glutin 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2952,6 +2958,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
"checksum frustum_query 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e1771c26abed26b2527d888742fffd27dab86d205bf4846748abf29c06ef5a05"
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"

View File

@ -55,3 +55,4 @@ portpicker = "0.1"
num = "0.2"
backtrace = "0.3"
rand = "0.5"
frustum_query = "0.1.2"

View File

@ -151,7 +151,14 @@ impl Scene {
.expect("Failed to update global constants");
// Maintain the terrain.
self.terrain.maintain(renderer, client);
self.terrain.maintain(
renderer,
client,
self.camera.get_focus_pos(),
self.loaded_distance,
view_mat,
proj_mat,
);
// Maintain the figures.
self.figure_mgr.maintain(renderer, client);
@ -166,12 +173,7 @@ impl Scene {
renderer.render_skybox(&self.skybox.model, &self.globals, &self.skybox.locals);
// Render terrain and figures.
self.terrain.render(
renderer,
&self.globals,
self.camera.get_focus_pos(),
self.loaded_distance,
);
self.terrain.render(renderer, &self.globals);
self.figure_mgr.render(renderer, client, &self.globals);
renderer.render_post_process(

View File

@ -8,13 +8,16 @@ use common::{
vol::{SampleVol, VolSize},
volumes::vol_map_2d::VolMap2dErr,
};
use std::{collections::HashMap, i32, sync::mpsc, time::Duration};
use frustum_query::frustum::Frustum;
use std::{collections::HashMap, i32, ops::Mul, sync::mpsc, time::Duration};
use vek::*;
struct TerrainChunk {
// GPU data
model: Model<TerrainPipeline>,
locals: Consts<TerrainLocals>,
visible: bool,
z_bounds: (f32, f32),
}
struct ChunkMeshState {
@ -26,6 +29,7 @@ struct ChunkMeshState {
/// A type produced by mesh worker threads corresponding to the position and mesh of a chunk.
struct MeshWorkerResponse {
pos: Vec2<i32>,
z_bounds: (f32, f32),
mesh: Mesh<TerrainPipeline>,
started_tick: u64,
}
@ -33,12 +37,14 @@ struct MeshWorkerResponse {
/// Function executed by worker threads dedicated to chunk meshing.
fn mesh_worker(
pos: Vec2<i32>,
z_bounds: (f32, f32),
started_tick: u64,
volume: <TerrainMap as SampleVol<Aabr<i32>>>::Sample,
range: Aabb<i32>,
) -> MeshWorkerResponse {
MeshWorkerResponse {
pos,
z_bounds,
mesh: volume.generate_mesh(range),
started_tick,
}
@ -70,7 +76,15 @@ impl Terrain {
}
/// Maintain terrain data. To be called once per tick.
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) {
pub fn maintain(
&mut self,
renderer: &mut Renderer,
client: &Client,
focus_pos: Vec3<f32>,
loaded_distance: f32,
view_mat: Mat4<f32>,
proj_mat: Mat4<f32>,
) {
let current_tick = client.get_tick();
// Add any recently created or changed chunks to the list of chunks to be meshed.
@ -165,7 +179,13 @@ impl Terrain {
// Queue the worker thread.
client.thread_pool().execute(move || {
let _ = send.send(mesh_worker(pos, current_tick, volume, aabb));
let _ = send.send(mesh_worker(
pos,
(min_z as f32, max_z as f32),
current_tick,
volume,
aabb,
));
});
todo.active_worker = true;
}
@ -194,6 +214,8 @@ impl Terrain {
.into_array(),
}])
.expect("Failed to upload chunk locals to the GPU!"),
visible: false,
z_bounds: response.z_bounds,
},
);
}
@ -202,28 +224,48 @@ impl Terrain {
_ => {}
}
}
}
pub fn render(
&self,
renderer: &mut Renderer,
globals: &Consts<Globals>,
focus_pos: Vec3<f32>,
loaded_distance: f32,
) {
for (pos, chunk) in &self.chunks {
// Limit focus_pos to chunk bounds
let chunk_pos = pos.map2(TerrainChunkSize::SIZE.into(), |e, sz: u32| {
e as f32 * sz as f32
});
let nearest_in_chunk = Vec2::from(focus_pos).clamped(
chunk_pos,
chunk_pos + Vec2::from(TerrainChunkSize::SIZE).map(|e: u32| e as f32),
// Construct view frustum
let frustum = Frustum::from_modelview_and_projection(
&view_mat.into_col_array(),
&proj_mat.into_col_array(),
);
// Update chunk visibility
let chunk_sz = TerrainChunkSize::SIZE.x as f32;
for (pos, chunk) in &mut self.chunks {
let chunk_pos = pos.map(|e| e as f32 * chunk_sz);
// Limit focus_pos to chunk bounds and ensure the chunk is within the fog boundary
let nearest_in_chunk = Vec2::from(focus_pos).clamped(chunk_pos, chunk_pos + chunk_sz);
let in_range = Vec2::<f32>::from(focus_pos).distance_squared(nearest_in_chunk)
< loaded_distance.powf(2.0);
// Ensure the chunk is within the view frustrum
let chunk_mid = Vec3::new(
chunk_pos.x + chunk_sz / 2.0,
chunk_pos.y + chunk_sz / 2.0,
(chunk.z_bounds.0 + chunk.z_bounds.1) * 0.5,
);
let chunk_radius = (chunk.z_bounds.1 - chunk.z_bounds.0)
.max(chunk_sz / 2.0)
.powf(2.0)
.mul(2.0)
.sqrt();
let in_frustum = frustum.sphere_intersecting(
&chunk_mid.x,
&chunk_mid.y,
&chunk_mid.z,
&chunk_radius,
);
if Vec2::<f32>::from(focus_pos).distance_squared(nearest_in_chunk)
< loaded_distance.powf(2.0)
{
chunk.visible = in_range && in_frustum;
}
}
pub fn render(&self, renderer: &mut Renderer, globals: &Consts<Globals>) {
for (pos, chunk) in &self.chunks {
if chunk.visible {
renderer.render_terrain_chunk(&chunk.model, globals, &chunk.locals);
}
}