Floodfill shadows, smooth shadow lighting

This commit is contained in:
Joshua Barretto 2019-09-24 01:15:38 +01:00
parent a432cef263
commit d745acc948
3 changed files with 175 additions and 58 deletions

View File

@ -14,39 +14,44 @@ pub enum DynaError {
// V = Voxel
// S = Size (replace when const generics are a thing)
// M = Metadata
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Dyna<V: Vox, M> {
#[derive(Debug, Serialize, Deserialize)]
pub struct Dyna<V: Vox, M, A: Access = ColumnAccess> {
vox: Vec<V>,
meta: M,
sz: Vec3<u32>,
_phantom: std::marker::PhantomData<A>,
}
impl<V: Vox, M> Dyna<V, M> {
impl<V: Vox, M: Clone, A: Access> Clone for Dyna<V, M, A> {
fn clone(&self) -> Self {
Self {
vox: self.vox.clone(),
meta: self.meta.clone(),
sz: self.sz,
_phantom: std::marker::PhantomData,
}
}
}
impl<V: Vox, M, A: Access> Dyna<V, M, A> {
/// Used to transform a voxel position in the volume into its corresponding index
/// in the voxel array.
#[inline(always)]
fn idx_for(sz: Vec3<u32>, pos: Vec3<i32>) -> Option<usize> {
if pos.map(|e| e >= 0).reduce_and() && pos.map2(sz, |e, lim| e < lim as i32).reduce_and() {
Some(Self::idx_for_unchecked(sz, pos))
Some(A::idx(pos, sz))
} else {
None
}
}
/// Used to transform a voxel position in the volume into its corresponding index
/// in the voxel array.
#[inline(always)]
fn idx_for_unchecked(sz: Vec3<u32>, pos: Vec3<i32>) -> usize {
(pos.x * sz.y as i32 * sz.z as i32 + pos.y * sz.z as i32 + pos.z) as usize
}
}
impl<V: Vox, M> BaseVol for Dyna<V, M> {
impl<V: Vox, M, A: Access> BaseVol for Dyna<V, M, A> {
type Vox = V;
type Error = DynaError;
}
impl<V: Vox, M> SizedVol for Dyna<V, M> {
impl<V: Vox, M, A: Access> SizedVol for Dyna<V, M, A> {
#[inline(always)]
fn lower_bound(&self) -> Vec3<i32> {
Vec3::zero()
@ -58,7 +63,7 @@ impl<V: Vox, M> SizedVol for Dyna<V, M> {
}
}
impl<V: Vox, M> ReadVol for Dyna<V, M> {
impl<V: Vox, M, A: Access> ReadVol for Dyna<V, M, A> {
#[inline(always)]
fn get(&self, pos: Vec3<i32>) -> Result<&V, DynaError> {
Self::idx_for(self.sz, pos)
@ -67,7 +72,7 @@ impl<V: Vox, M> ReadVol for Dyna<V, M> {
}
}
impl<V: Vox, M> WriteVol for Dyna<V, M> {
impl<V: Vox, M, A: Access> WriteVol for Dyna<V, M, A> {
#[inline(always)]
fn set(&mut self, pos: Vec3<i32>, vox: Self::Vox) -> Result<(), DynaError> {
Self::idx_for(self.sz, pos)
@ -77,7 +82,7 @@ impl<V: Vox, M> WriteVol for Dyna<V, M> {
}
}
impl<'a, V: Vox, M> IntoPosIterator for &'a Dyna<V, M> {
impl<'a, V: Vox, M, A: Access> IntoPosIterator for &'a Dyna<V, M, A> {
type IntoIter = DefaultPosIterator;
fn pos_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> Self::IntoIter {
@ -85,15 +90,15 @@ impl<'a, V: Vox, M> IntoPosIterator for &'a Dyna<V, M> {
}
}
impl<'a, V: Vox, M> IntoVolIterator<'a> for &'a Dyna<V, M> {
type IntoIter = DefaultVolIterator<'a, Dyna<V, M>>;
impl<'a, V: Vox, M, A: Access> IntoVolIterator<'a> for &'a Dyna<V, M, A> {
type IntoIter = DefaultVolIterator<'a, Dyna<V, M, A>>;
fn vol_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> Self::IntoIter {
Self::IntoIter::new(self, lower_bound, upper_bound)
}
}
impl<V: Vox + Clone, M> Dyna<V, M> {
impl<V: Vox + Clone, M, A: Access> Dyna<V, M, A> {
/// Create a new `Dyna` with the provided dimensions and all voxels filled with duplicates of
/// the provided voxel.
pub fn filled(sz: Vec3<u32>, vox: V, meta: M) -> Self {
@ -101,6 +106,7 @@ impl<V: Vox + Clone, M> Dyna<V, M> {
vox: vec![vox; sz.product() as usize],
meta,
sz,
_phantom: std::marker::PhantomData,
}
}
@ -114,3 +120,15 @@ impl<V: Vox + Clone, M> Dyna<V, M> {
&mut self.meta
}
}
pub trait Access {
fn idx(pos: Vec3<i32>, sz: Vec3<u32>) -> usize;
}
pub struct ColumnAccess;
impl Access for ColumnAccess {
fn idx(pos: Vec3<i32>, sz: Vec3<u32>) -> usize {
(pos.x * sz.y as i32 * sz.z as i32 + pos.y * sz.z as i32 + pos.z) as usize
}
}

View File

@ -4,15 +4,114 @@ use crate::{
};
use common::{
terrain::{Block, BlockKind},
vol::{ReadVol, RectRasterableVol},
volumes::vol_grid_2d::VolGrid2d,
vol::{ReadVol, RectRasterableVol, Vox},
volumes::{dyna::Dyna, vol_grid_2d::VolGrid2d},
};
use hashbrown::{HashMap, HashSet};
use std::fmt::Debug;
use vek::*;
type TerrainVertex = <TerrainPipeline as render::Pipeline>::Vertex;
type FluidVertex = <FluidPipeline as render::Pipeline>::Vertex;
const DIRS: [Vec2<i32>; 4] = [
Vec2 { x: 1, y: 0 },
Vec2 { x: 0, y: 1 },
Vec2 { x: -1, y: 0 },
Vec2 { x: 0, y: -1 },
];
const DIRS_3D: [Vec3<i32>; 6] = [
Vec3 { x: 1, y: 0, z: 0 },
Vec3 { x: 0, y: 1, z: 0 },
Vec3 { x: 0, y: 0, z: 1 },
Vec3 { x: -1, y: 0, z: 0 },
Vec3 { x: 0, y: -1, z: 0 },
Vec3 { x: 0, y: 0, z: -1 },
];
fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
bounds: Aabb<i32>,
vol: &VolGrid2d<V>,
) -> impl Fn(Vec3<i32>) -> f32 {
let sunlight = 24;
let outer = Aabb {
min: bounds.min - sunlight,
max: bounds.max + sunlight,
};
let mut voids = HashMap::new();
let mut rays = vec![outer.size().d; outer.size().product() as usize];
for x in 0..outer.size().w {
for y in 0..outer.size().h {
let mut outside = true;
for z in (0..outer.size().d).rev() {
if vol
.get(outer.min + Vec3::new(x, y, z))
.map(|vox| vox.is_air())
.unwrap_or(true)
{
if !outside {
voids.insert(Vec3::new(x, y, z), None);
}
} else if outside {
rays[(outer.size().w * y + x) as usize] = z;
outside = false;
}
}
}
}
let mut opens = HashSet::new();
for (pos, l) in &mut voids {
for dir in &DIRS {
let col = Vec2::<i32>::from(*pos) + dir;
if pos.z
> *rays
.get(((outer.size().w * col.y) + col.x) as usize)
.unwrap_or(&0)
{
*l = Some(sunlight - 1);
opens.insert(*pos);
}
}
}
for _ in 0..20 {
let mut new_opens = HashSet::new();
for open in &opens {
let parent_l = voids[open].unwrap_or(0);
for dir in &DIRS_3D {
let other = *open + *dir;
if !opens.contains(&other) {
if let Some(l) = voids.get_mut(&other) {
if l.unwrap_or(0) < parent_l - 1 {
new_opens.insert(other);
}
*l = Some(parent_l - 1);
}
}
}
}
opens = new_opens;
}
move |wpos| {
let pos = wpos - outer.min;
rays.get(((outer.size().w * pos.y) + pos.x) as usize)
.and_then(|ray| if pos.z > *ray { Some(1.0) } else { None })
.or_else(|| {
if let Some(Some(l)) = voids.get(&pos) {
Some(*l as f32 / sunlight as f32)
} else {
None
}
})
.unwrap_or(0.0)
}
}
fn block_shadow_density(kind: BlockKind) -> (f32, f32) {
// (density, cap)
match kind {
@ -38,10 +137,10 @@ impl<V: RectRasterableVol<Vox = Block> + ReadVol + Debug> Meshable<TerrainPipeli
let mut opaque_mesh = Mesh::new();
let mut fluid_mesh = Mesh::new();
let mut light = calc_light(range, self);
for x in range.min.x + 1..range.max.x - 1 {
for y in range.min.y + 1..range.max.y - 1 {
let mut neighbour_light = [[[1.0f32; 3]; 3]; 3];
for z in (range.min.z..range.max.z).rev() {
let pos = Vec3::new(x, y, z);
let offs = (pos - (range.min + 1) * Vec3::new(1, 1, 0)).map(|e| e as f32);
@ -65,7 +164,19 @@ impl<V: RectRasterableVol<Vox = Block> + ReadVol + Debug> Meshable<TerrainPipeli
TerrainVertex::new(pos, norm, col, light * ao)
},
false,
&neighbour_light,
&{
let mut ls = [[[0.0; 3]; 3]; 3];
for x in 0..3 {
for y in 0..3 {
for z in 0..3 {
ls[x][y][z] = light(
pos + Vec3::new(x as i32, y as i32, z as i32) - 1,
);
}
}
}
ls
},
|vox| !vox.is_opaque(),
|vox| vox.is_opaque(),
);
@ -85,38 +196,23 @@ impl<V: RectRasterableVol<Vox = Block> + ReadVol + Debug> Meshable<TerrainPipeli
FluidVertex::new(pos, norm, col, light * ao, 0.3)
},
false,
&neighbour_light,
&{
let mut ls = [[[0.0; 3]; 3]; 3];
for x in 0..3 {
for y in 0..3 {
for z in 0..3 {
ls[x][y][z] = light(
pos + Vec3::new(x as i32, y as i32, z as i32) - 1,
);
}
}
}
ls
},
|vox| vox.is_air(),
|vox| vox.is_opaque(),
);
}
// Shift lighting
neighbour_light[2] = neighbour_light[1];
neighbour_light[1] = neighbour_light[0];
// Accumulate shade under opaque blocks
for i in 0..3 {
for j in 0..3 {
let (density, cap) = self
.get(pos + Vec3::new(i as i32 - 1, j as i32 - 1, -1))
.ok()
.map(|vox| block_shadow_density(vox.kind()))
.unwrap_or((0.0, 0.0));
neighbour_light[0][i][j] = (neighbour_light[0][i][j] * (1.0 - density))
.max(cap.min(neighbour_light[1][i][j]));
}
}
// Spread light
neighbour_light[0] = [[neighbour_light[0]
.iter()
.map(|col| col.iter())
.flatten()
.copied()
.fold(0.0, |a, x| a + x)
/ 9.0; 3]; 3];
}
}
}

View File

@ -29,12 +29,15 @@ fn get_ao_quad<V: ReadVol>(
.unwrap_or(false),
);
let darkness = darknesses
.iter()
.map(|x| x.iter().map(|y| y.iter()))
.flatten()
.flatten()
.fold(0.0, |a: f32, x| a.max(*x));
let mut darkness = 0.0;
for x in 0..2 {
for y in 0..2 {
let dark_pos = shift + offs[0] * x + offs[1] * y + 1;
darkness += darknesses[dark_pos.x as usize][dark_pos.y as usize]
[dark_pos.z as usize]
/ 4.0;
}
}
(
darkness,