mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Propagate light via queue to avoid block lookups
This commit is contained in:
parent
211c076b8d
commit
3347438d51
@ -5,9 +5,9 @@ use crate::{
|
||||
use common::{
|
||||
terrain::Block,
|
||||
vol::{ReadVol, RectRasterableVol, Vox},
|
||||
volumes::vol_grid_2d::VolGrid2d,
|
||||
volumes::vol_grid_2d::{CachedVolGrid2d, VolGrid2d},
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
use std::{collections::VecDeque, fmt::Debug};
|
||||
use vek::*;
|
||||
|
||||
type TerrainVertex = <TerrainPipeline as render::Pipeline>::Vertex;
|
||||
@ -33,10 +33,12 @@ fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
|
||||
bounds: Aabb<i32>,
|
||||
vol: &VolGrid2d<V>,
|
||||
) -> impl Fn(Vec3<i32>) -> f32 {
|
||||
const NOT_VOID: u8 = 255;
|
||||
const UNKOWN: u8 = 255;
|
||||
const OPAQUE: u8 = 254;
|
||||
const SUNLIGHT: u8 = 24;
|
||||
|
||||
let outer = Aabb {
|
||||
// TODO: subtract 1 from sunlight here
|
||||
min: bounds.min - Vec3::new(SUNLIGHT as i32, SUNLIGHT as i32, 1),
|
||||
max: bounds.max + Vec3::new(SUNLIGHT as i32, SUNLIGHT as i32, 1),
|
||||
};
|
||||
@ -45,109 +47,166 @@ fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
|
||||
|
||||
// Voids are voxels that that contain air or liquid that are protected from direct rays by blocks
|
||||
// above them
|
||||
//
|
||||
let mut voids = vec![NOT_VOID; outer.size().product() as usize];
|
||||
let void_idx = {
|
||||
let (_, h, d) = outer.clone().size().into_tuple();
|
||||
move |x, y, z| (x * h * d + y * d + z) as usize
|
||||
let mut light_map = vec![UNKOWN; outer.size().product() as usize];
|
||||
// TODO: would a morton curve be more efficient?
|
||||
let lm_idx = {
|
||||
let (w, h, _) = outer.clone().size().into_tuple();
|
||||
move |x, y, z| (z * h * w + x * h + y) as usize
|
||||
};
|
||||
// List of voids for efficient iteration
|
||||
let mut voids_list = vec![];
|
||||
// Rays are cast down
|
||||
// Vec<(highest non air block, lowest non air block)>
|
||||
let mut rays = vec![(outer.size().d, 0); (outer.size().w * outer.size().h) as usize];
|
||||
// Light propagation queue
|
||||
let mut prop_que = VecDeque::new();
|
||||
// Start rays
|
||||
// TODO: how much would it cost to clone the whole sample into a flat array?
|
||||
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() {
|
||||
let block = vol_cached
|
||||
.get(outer.min + Vec3::new(x, y, z))
|
||||
let z = outer.size().d - 1;
|
||||
let is_air = vol_cached
|
||||
.get(outer.min + Vec3::new(x, y, z))
|
||||
.ok()
|
||||
.map_or(false, |b| b.is_air());
|
||||
|
||||
light_map[lm_idx(x, y, z)] = if is_air {
|
||||
if vol_cached
|
||||
.get(outer.min + Vec3::new(x, y, z - 1))
|
||||
.ok()
|
||||
.copied()
|
||||
.unwrap_or(Block::empty());
|
||||
|
||||
if !block.is_air() {
|
||||
if outside {
|
||||
rays[(outer.size().w * y + x) as usize].0 = z;
|
||||
outside = false;
|
||||
}
|
||||
rays[(outer.size().w * y + x) as usize].1 = z;
|
||||
.map_or(false, |b| b.is_air())
|
||||
{
|
||||
light_map[lm_idx(x, y, z - 1)] = SUNLIGHT;
|
||||
// TODO: access efficiency of using less space to store pos
|
||||
prop_que.push_back(Vec3::new(x, y, z - 1));
|
||||
}
|
||||
|
||||
if (block.is_air() || block.is_fluid()) && !outside {
|
||||
voids_list.push(Vec3::new(x, y, z));
|
||||
voids[void_idx(x, y, z)] = 0;
|
||||
}
|
||||
}
|
||||
SUNLIGHT
|
||||
} else {
|
||||
OPAQUE
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Propagate light into voids adjacent to rays
|
||||
let mut opens = Vec::new();
|
||||
'voids: for pos in &mut voids_list {
|
||||
let void_idx = void_idx(pos.x, pos.y, pos.z);
|
||||
for dir in &DIRS {
|
||||
let col = Vec2::<i32>::from(*pos) + dir;
|
||||
// If above highest non air block (ray passes by)
|
||||
if pos.z
|
||||
> *rays
|
||||
.get(((outer.size().w * col.y) + col.x) as usize)
|
||||
.map(|(ray, _)| ray)
|
||||
.unwrap_or(&0)
|
||||
{
|
||||
voids[void_idx] = SUNLIGHT - 1;
|
||||
opens.push(*pos);
|
||||
continue 'voids;
|
||||
}
|
||||
}
|
||||
|
||||
// Ray hits directly (occurs for liquids)
|
||||
if pos.z
|
||||
>= *rays
|
||||
.get(((outer.size().w * pos.y) + pos.x) as usize)
|
||||
.map(|(ray, _)| ray)
|
||||
.unwrap_or(&0)
|
||||
{
|
||||
voids[void_idx] = SUNLIGHT - 1;
|
||||
opens.push(*pos);
|
||||
}
|
||||
}
|
||||
|
||||
while opens.len() > 0 {
|
||||
let mut new_opens = Vec::new();
|
||||
for open in &opens {
|
||||
let parent_l = voids[void_idx(open.x, open.y, open.z)];
|
||||
for dir in &DIRS_3D {
|
||||
let other = *open + *dir;
|
||||
if let Some(l) = voids.get_mut(void_idx(other.x, other.y, other.z)) {
|
||||
if *l < parent_l - 1 {
|
||||
new_opens.push(other);
|
||||
*l = parent_l - 1;
|
||||
// Determines light propagation
|
||||
let propagate = |src: u8,
|
||||
dest: &mut u8,
|
||||
pos: Vec3<i32>,
|
||||
prop_que: &mut VecDeque<_>,
|
||||
vol: &mut CachedVolGrid2d<V>| {
|
||||
if *dest != OPAQUE {
|
||||
if *dest == UNKOWN {
|
||||
if vol
|
||||
.get(outer.min + pos)
|
||||
.ok()
|
||||
.map_or(false, |b| b.is_air() || b.is_fluid())
|
||||
{
|
||||
*dest = src - 1;
|
||||
// Can't propagate further
|
||||
if *dest > 1 {
|
||||
prop_que.push_back(pos);
|
||||
}
|
||||
} else {
|
||||
*dest = OPAQUE;
|
||||
}
|
||||
} else if *dest < src - 1 {
|
||||
*dest = src - 1;
|
||||
// Can't propagate further
|
||||
if *dest > 1 {
|
||||
prop_que.push_back(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
opens = new_opens;
|
||||
};
|
||||
|
||||
// Propage light
|
||||
while let Some(pos) = prop_que.pop_front() {
|
||||
// TODO: access efficiency of storing current light level in queue
|
||||
// TODO: access efficiency of storing originating direction index in queue so that dir
|
||||
// doesn't need to be checked
|
||||
let light = light_map[lm_idx(pos.x, pos.y, pos.z)];
|
||||
|
||||
// If ray propagate downwards at full strength
|
||||
if light == SUNLIGHT {
|
||||
// Down is special cased and we know up is a ray
|
||||
// Special cased ray propagation
|
||||
let pos = Vec3::new(pos.x, pos.y, pos.z - 1);
|
||||
let (is_air, is_fluid) = vol_cached
|
||||
.get(outer.min + pos)
|
||||
.ok()
|
||||
.map_or((false, false), |b| (b.is_air(), b.is_fluid()));
|
||||
light_map[lm_idx(pos.x, pos.y, pos.z)] = if is_air {
|
||||
prop_que.push_back(pos);
|
||||
SUNLIGHT
|
||||
} else if is_fluid {
|
||||
prop_que.push_back(pos);
|
||||
SUNLIGHT - 1
|
||||
} else {
|
||||
OPAQUE
|
||||
}
|
||||
} else {
|
||||
// Up
|
||||
// Bounds checking
|
||||
// TODO: check if propagated light level can ever reach area of interest
|
||||
if pos.z + 1 < outer.size().d {
|
||||
propagate(
|
||||
light,
|
||||
light_map.get_mut(lm_idx(pos.x, pos.y, pos.z + 1)).unwrap(),
|
||||
Vec3::new(pos.x, pos.y, pos.z + 1),
|
||||
&mut prop_que,
|
||||
&mut vol_cached,
|
||||
)
|
||||
}
|
||||
// Down
|
||||
if pos.z > 0 {
|
||||
propagate(
|
||||
light,
|
||||
light_map.get_mut(lm_idx(pos.x, pos.y, pos.z - 1)).unwrap(),
|
||||
Vec3::new(pos.x, pos.y, pos.z - 1),
|
||||
&mut prop_que,
|
||||
&mut vol_cached,
|
||||
)
|
||||
}
|
||||
}
|
||||
// The XY directions
|
||||
if pos.y + 1 < outer.size().h {
|
||||
propagate(
|
||||
light,
|
||||
light_map.get_mut(lm_idx(pos.x, pos.y + 1, pos.z)).unwrap(),
|
||||
Vec3::new(pos.x, pos.y + 1, pos.z),
|
||||
&mut prop_que,
|
||||
&mut vol_cached,
|
||||
)
|
||||
}
|
||||
if pos.y > 0 {
|
||||
propagate(
|
||||
light,
|
||||
light_map.get_mut(lm_idx(pos.x, pos.y - 1, pos.z)).unwrap(),
|
||||
Vec3::new(pos.x, pos.y - 1, pos.z),
|
||||
&mut prop_que,
|
||||
&mut vol_cached,
|
||||
)
|
||||
}
|
||||
if pos.x + 1 < outer.size().w {
|
||||
propagate(
|
||||
light,
|
||||
light_map.get_mut(lm_idx(pos.x + 1, pos.y, pos.z)).unwrap(),
|
||||
Vec3::new(pos.x + 1, pos.y, pos.z),
|
||||
&mut prop_que,
|
||||
&mut vol_cached,
|
||||
)
|
||||
}
|
||||
if pos.x > 0 {
|
||||
propagate(
|
||||
light,
|
||||
light_map.get_mut(lm_idx(pos.x - 1, pos.y, pos.z)).unwrap(),
|
||||
Vec3::new(pos.x - 1, pos.y, pos.z),
|
||||
&mut prop_que,
|
||||
&mut vol_cached,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
move |wpos| {
|
||||
let pos = wpos - outer.min;
|
||||
rays.get(((outer.size().w * pos.y) + pos.x) as usize)
|
||||
.and_then(|(ray, deep)| {
|
||||
if pos.z > *ray {
|
||||
Some(1.0)
|
||||
} else if pos.z < *deep {
|
||||
Some(0.0)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.or_else(|| {
|
||||
voids
|
||||
.get(void_idx(pos.x, pos.y, pos.z))
|
||||
.filter(|l| **l != NOT_VOID)
|
||||
.map(|l| *l as f32 / SUNLIGHT as f32)
|
||||
})
|
||||
light_map
|
||||
.get(lm_idx(pos.x, pos.y, pos.z))
|
||||
.filter(|l| **l != OPAQUE && **l != UNKOWN)
|
||||
.map(|l| *l as f32 / SUNLIGHT as f32)
|
||||
.unwrap_or(0.0)
|
||||
}
|
||||
}
|
||||
@ -168,16 +227,46 @@ impl<V: RectRasterableVol<Vox = Block> + ReadVol + Debug> Meshable<TerrainPipeli
|
||||
|
||||
let light = calc_light(range, self);
|
||||
|
||||
let mut vol_cached = self.cached();
|
||||
//let mut vol_cached = self.cached();
|
||||
|
||||
for x in range.min.x + 1..range.max.x - 1 {
|
||||
for y in range.min.y + 1..range.max.y - 1 {
|
||||
let flat_get = {
|
||||
let (w, h, d) = range.size().into_tuple();
|
||||
// z can range from -1..range.size().d + 1
|
||||
let d = d + 2;
|
||||
let mut flat = vec![Block::empty(); (w * h * d) as usize];
|
||||
|
||||
let mut i = 0;
|
||||
let mut volume = self.cached();
|
||||
for x in 0..range.size().w {
|
||||
for y in 0..range.size().h {
|
||||
for z in -1..range.size().d + 1 {
|
||||
flat[i] = *volume.get(range.min + Vec3::new(x, y, z)).unwrap();
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Cleanup
|
||||
drop(i);
|
||||
let flat = flat;
|
||||
|
||||
move |Vec3 { x, y, z }| {
|
||||
// z can range from -1..range.size().d + 1
|
||||
let z = z + 1;
|
||||
match flat.get((x * h * d + y * d + z) as usize).copied() {
|
||||
Some(b) => b,
|
||||
None => panic!("x {} y {} z {} d {} h {}"),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for x in 1..range.size().w - 1 {
|
||||
for y in 1..range.size().w - 1 {
|
||||
let mut lights = [[[0.0; 3]; 3]; 3];
|
||||
for i in 0..3 {
|
||||
for j in 0..3 {
|
||||
for k in 0..3 {
|
||||
lights[k][j][i] = light(
|
||||
Vec3::new(x, y, range.min.z)
|
||||
Vec3::new(x + range.min.x, y + range.min.y, range.min.z)
|
||||
+ Vec3::new(i as i32, j as i32, k as i32)
|
||||
- 1,
|
||||
);
|
||||
@ -198,23 +287,23 @@ impl<V: RectRasterableVol<Vox = Block> + ReadVol + Debug> Meshable<TerrainPipeli
|
||||
for i in 0..3 {
|
||||
for j in 0..3 {
|
||||
for k in 0..3 {
|
||||
let block = vol_cached
|
||||
let block = /*vol_cached
|
||||
.get(
|
||||
Vec3::new(x, y, range.min.z)
|
||||
+ Vec3::new(i as i32, j as i32, k as i32)
|
||||
- 1,
|
||||
)
|
||||
.ok()
|
||||
.copied();
|
||||
.copied()*/ Some(flat_get(Vec3::new(x, y, 0) + Vec3::new(i as i32, j as i32, k as i32) - 1));
|
||||
colors[k][j][i] = get_color(block.as_ref());
|
||||
blocks[k][j][i] = block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for z in range.min.z..range.max.z {
|
||||
for z in 0..range.size().d {
|
||||
let pos = Vec3::new(x, y, z);
|
||||
let offs = (pos - (range.min + 1) * Vec3::new(1, 1, 0)).map(|e| e as f32);
|
||||
let offs = (pos - Vec3::new(1, 1, 0)).map(|e| e as f32);
|
||||
|
||||
lights[0] = lights[1];
|
||||
lights[1] = lights[2];
|
||||
@ -230,10 +319,10 @@ impl<V: RectRasterableVol<Vox = Block> + ReadVol + Debug> Meshable<TerrainPipeli
|
||||
}
|
||||
for i in 0..3 {
|
||||
for j in 0..3 {
|
||||
let block = vol_cached
|
||||
let block = /*vol_cached
|
||||
.get(pos + Vec3::new(i as i32, j as i32, 2) - 1)
|
||||
.ok()
|
||||
.copied();
|
||||
.copied()*/ Some(flat_get(pos + Vec3::new(i as i32, j as i32, 2) - 1));
|
||||
colors[2][j][i] = get_color(block.as_ref());
|
||||
blocks[2][j][i] = block;
|
||||
}
|
||||
@ -242,6 +331,7 @@ impl<V: RectRasterableVol<Vox = Block> + ReadVol + Debug> Meshable<TerrainPipeli
|
||||
let block = blocks[1][1][1];
|
||||
|
||||
// Create mesh polygons
|
||||
let pos = pos + range.min;
|
||||
if block.map(|vox| vox.is_opaque()).unwrap_or(false) {
|
||||
vol::push_vox_verts(
|
||||
&mut opaque_mesh,
|
||||
@ -380,3 +470,16 @@ impl<V: BaseVol<Vox = Block> + ReadVol + Debug> Meshable for VolGrid3d<V> {
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
fn interleave_i32_with_zeros(mut x: i32) -> i64 {
|
||||
x = (x ^ (x << 16)) & 0x0000ffff0000ffff;
|
||||
x = (x ^ (x << 8)) & 0x00ff00ff00ff00ff;
|
||||
x = (x ^ (x << 4)) & 0x0f0f0f0f0f0f0f0f;
|
||||
x = (x ^ (x << 2)) & 0x3333333333333333;
|
||||
x = (x ^ (x << 1)) & 0x5555555555555555;
|
||||
x
|
||||
}
|
||||
|
||||
fn morton_code(pos: Vec2<i32>) -> i64 {
|
||||
interleave_i32_with_zeros(pos.x) | (interleave_i32_with_zeros(pos.y) << 1)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user