mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Floodfill shadows, smooth shadow lighting
This commit is contained in:
parent
a432cef263
commit
d745acc948
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user