Skip to content

Commit

Permalink
Frustum culling for chunk generation and rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
Ralith committed May 26, 2020
1 parent 9b04419 commit 925486d
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 3 deletions.
2 changes: 1 addition & 1 deletion client/src/graphics/draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ impl Draw {
);

if let Some(ref mut voxels) = self.voxels {
voxels.prepare(device, state.voxels.as_mut().unwrap(), sim, cmd);
voxels.prepare(device, state.voxels.as_mut().unwrap(), sim, cmd, frustum);
}

// Ensure reads of just-transferred memory wait until it's ready
Expand Down
81 changes: 81 additions & 0 deletions client/src/graphics/frustum.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use common::Plane;

#[derive(Debug, Copy, Clone)]
pub struct Frustum {
pub left: f32,
Expand Down Expand Up @@ -44,4 +46,83 @@ impl Frustum {
0.0, 0.0, za, zb,
0.0, 0.0, -1.0, 0.0))
}

pub fn planes(&self) -> FrustumPlanes {
FrustumPlanes {
left: Plane::from(
na::UnitQuaternion::from_axis_angle(&na::Vector3::y_axis(), self.left)
* -na::Vector3::x_axis(),
),
right: Plane::from(
na::UnitQuaternion::from_axis_angle(&na::Vector3::y_axis(), self.right)
* na::Vector3::x_axis(),
),
down: Plane::from(
na::UnitQuaternion::from_axis_angle(&na::Vector3::x_axis(), self.down)
* na::Vector3::y_axis(),
),
up: Plane::from(
na::UnitQuaternion::from_axis_angle(&na::Vector3::x_axis(), self.up)
* -na::Vector3::y_axis(),
),
}
}
}

#[derive(Debug, Copy, Clone)]
pub struct FrustumPlanes {
left: Plane<f32>,
right: Plane<f32>,
down: Plane<f32>,
up: Plane<f32>,
}

impl FrustumPlanes {
pub fn contain(&self, point: &na::Vector4<f32>, radius: f32) -> bool {
for &plane in &[&self.left, &self.right, &self.down, &self.up] {
if plane.distance_to(point) < -radius {
return false;
}
}
true
}
}

#[cfg(test)]
mod tests {
use super::*;
use common::math::{origin, translate_along};
use std::f32;

#[test]
fn planes_sanity() {
// 90 degree square
let planes = Frustum::from_vfov(f32::consts::FRAC_PI_4, 1.0).planes();
assert!(planes.contain(&origin(), 0.1));
assert!(planes.contain(
&(translate_along(&na::Vector3::z_axis(), -1.0) * origin()),
0.0
));
assert!(!planes.contain(
&(translate_along(&na::Vector3::z_axis(), 1.0) * origin()),
0.0
));

assert!(!planes.contain(
&(translate_along(&na::Vector3::x_axis(), 1.0) * origin()),
0.0
));
assert!(!planes.contain(
&(translate_along(&na::Vector3::y_axis(), 1.0) * origin()),
0.0
));
assert!(!planes.contain(
&(translate_along(&na::Vector3::x_axis(), -1.0) * origin()),
0.0
));
assert!(!planes.contain(
&(translate_along(&na::Vector3::y_axis(), -1.0) * origin()),
0.0
));
}
}
15 changes: 13 additions & 2 deletions client/src/graphics/voxels/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ use metrics::timing;
use tracing::warn;

use crate::{
graphics::Base,
graphics::{Base, Frustum},
loader::{Cleanup, LoadCtx, LoadFuture, Loadable, WorkQueue},
sim::VoxelData,
worldgen, Config, Loader, Sim,
};
use common::{dodeca::Vertex, graph::NodeId, lru_slab::SlotId, math, LruSlab};
use common::{dodeca, dodeca::Vertex, graph::NodeId, lru_slab::SlotId, math, LruSlab};

use surface::Surface;
use surface_extraction::{DrawBuffer, ScratchBuffer, SurfaceExtraction};
Expand Down Expand Up @@ -79,6 +79,7 @@ impl Voxels {
frame: &mut Frame,
sim: &mut Sim,
cmd: vk::CommandBuffer,
frustum: &Frustum,
) {
// Clean up after previous frame
for i in frame.extracted.drain(..) {
Expand Down Expand Up @@ -119,7 +120,17 @@ impl Voxels {
.unwrap_or(std::cmp::Ordering::Less)
});
let node_scan_started = Instant::now();
let frustum_planes = frustum.planes();
let local_to_view = view.local.try_inverse().unwrap();
for &(node, ref node_transform) in &nodes {
let node_to_view = local_to_view * node_transform;
let origin = node_to_view * math::origin();
if !frustum_planes.contain(&origin, dodeca::BOUNDING_SPHERE_RADIUS as f32) {
// Don't bother generating or drawing chunks from nodes that are wholly outside the
// frustum.
continue;
}

use Chunk::*;
for chunk in Vertex::iter() {
// Fetch existing chunk, or extract surface of new chunk
Expand Down
19 changes: 19 additions & 0 deletions common/src/dodeca.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ impl Vertex {

pub const VERTEX_COUNT: usize = 20;
pub const SIDE_COUNT: usize = 12;
#[allow(clippy::unreadable_literal)]
pub const BOUNDING_SPHERE_RADIUS: f64 = 1.2264568712514068;

lazy_static! {
/// Whether two sides share an edge
Expand Down Expand Up @@ -251,6 +253,7 @@ lazy_static! {
#[cfg(test)]
mod tests {
use super::*;
use approx::*;

#[test]
fn vertex_sides() {
Expand Down Expand Up @@ -287,4 +290,20 @@ mod tests {
assert!(side.faces(&(side.reflection() * math::origin())));
}
}

#[test]
fn radius() {
let corner = Vertex::A.chunk_to_node() * na::Vector4::repeat(1.0);
assert_abs_diff_eq!(
BOUNDING_SPHERE_RADIUS,
math::distance(&corner, &math::origin()),
epsilon = 1e-10
);
let phi = (1.0 + 5.0f64.sqrt()) / 2.0; // Golden ratio
assert_abs_diff_eq!(
BOUNDING_SPHERE_RADIUS,
(1.5 * phi).sqrt().asinh(),
epsilon = 1e-10
);
}
}

0 comments on commit 925486d

Please sign in to comment.