From 33993638fe276995046d032781fd0c050d44f278 Mon Sep 17 00:00:00 2001 From: Imbris Date: Mon, 6 Jan 2020 21:00:03 -0500 Subject: [PATCH 01/13] Experiment with replacing hashmap in light calculations --- voxygen/src/mesh/terrain.rs | 65 ++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index 75439e1f0a..87a688882e 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -7,7 +7,6 @@ use common::{ vol::{ReadVol, RectRasterableVol, Vox}, volumes::vol_grid_2d::VolGrid2d, }; -use hashbrown::{HashMap, HashSet}; use std::fmt::Debug; use vek::*; @@ -34,17 +33,29 @@ fn calc_light + ReadVol + Debug>( bounds: Aabb, vol: &VolGrid2d, ) -> impl Fn(Vec3) -> f32 { - let sunlight = 24; + const NOT_VOID: u8 = 255; + const SUNLIGHT: u8 = 24; let outer = Aabb { - min: bounds.min - sunlight, - max: bounds.max + sunlight, + min: bounds.min - SUNLIGHT as i32, + max: bounds.max + SUNLIGHT as i32, }; let mut vol_cached = vol.cached(); - let mut voids = HashMap::new(); - let mut rays = vec![(outer.size().d, 0); outer.size().product() as usize]; + // 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 + }; + // 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]; for x in 0..outer.size().w { for y in 0..outer.size().h { let mut outside = true; @@ -64,51 +75,54 @@ fn calc_light + ReadVol + Debug>( } if (block.is_air() || block.is_fluid()) && !outside { - voids.insert(Vec3::new(x, y, z), None); + voids_list.push(Vec3::new(x, y, z)); + voids[void_idx(x, y, z)] = 0; } } } } - let mut opens = HashSet::new(); - 'voids: for (pos, l) in &mut voids { + // 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::::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) { - *l = Some(sunlight - 1); - opens.insert(*pos); + 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) { - *l = Some(sunlight - 1); - opens.insert(*pos); + voids[void_idx] = SUNLIGHT - 1; + opens.push(*pos); } } while opens.len() > 0 { - let mut new_opens = HashSet::new(); + let mut new_opens = Vec::new(); for open in &opens { - let parent_l = voids[open].unwrap_or(0); + let parent_l = voids[void_idx(open.x, open.y, open.z)]; 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); + 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; } } } @@ -129,11 +143,10 @@ fn calc_light + ReadVol + Debug>( } }) .or_else(|| { - if let Some(Some(l)) = voids.get(&pos) { - Some(*l as f32 / sunlight as f32) - } else { - None - } + voids + .get(void_idx(pos.x, pos.y, pos.z)) + .filter(|l| **l != NOT_VOID) + .map(|l| *l as f32 / SUNLIGHT as f32) }) .unwrap_or(0.0) } From 211c076b8d5da5003b5f70e5ea811b277164cf9b Mon Sep 17 00:00:00 2001 From: Imbris Date: Wed, 8 Jan 2020 00:43:13 -0500 Subject: [PATCH 02/13] Remove unnecessary sampling in meshing light calculations --- voxygen/src/mesh/terrain.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index 87a688882e..f279cff85c 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -37,8 +37,8 @@ fn calc_light + ReadVol + Debug>( const SUNLIGHT: u8 = 24; let outer = Aabb { - min: bounds.min - SUNLIGHT as i32, - max: bounds.max + SUNLIGHT as i32, + min: bounds.min - Vec3::new(SUNLIGHT as i32, SUNLIGHT as i32, 1), + max: bounds.max + Vec3::new(SUNLIGHT as i32, SUNLIGHT as i32, 1), }; let mut vol_cached = vol.cached(); From 3347438d5127a2aa8a3b5babac34bd0c4fb03bcb Mon Sep 17 00:00:00 2001 From: Imbris Date: Sun, 12 Jan 2020 00:50:58 -0500 Subject: [PATCH 03/13] Propagate light via queue to avoid block lookups --- voxygen/src/mesh/terrain.rs | 303 ++++++++++++++++++++++++------------ 1 file changed, 203 insertions(+), 100 deletions(-) diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index f279cff85c..c88541f5d6 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -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 = ::Vertex; @@ -33,10 +33,12 @@ fn calc_light + ReadVol + Debug>( bounds: Aabb, vol: &VolGrid2d, ) -> impl Fn(Vec3) -> 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 + 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::::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, + prop_que: &mut VecDeque<_>, + vol: &mut CachedVolGrid2d| { + 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 + ReadVol + Debug> Meshable 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 + ReadVol + Debug> Meshable + ReadVol + Debug> Meshable + ReadVol + Debug> Meshable + ReadVol + Debug> Meshable for VolGrid3d { } } */ + +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) -> i64 { + interleave_i32_with_zeros(pos.x) | (interleave_i32_with_zeros(pos.y) << 1) +} From 09239caf88e2d4df820ecc227c2a807a544e6f30 Mon Sep 17 00:00:00 2001 From: Imbris Date: Sun, 12 Jan 2020 00:54:41 -0500 Subject: [PATCH 04/13] While copying terrain sample to flat array, keep track of highest and lowest positions with exposed faces in order to constrain further iteration --- voxygen/src/mesh/terrain.rs | 70 ++++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 20 deletions(-) diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index c88541f5d6..08c4cf8c1e 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -229,6 +229,12 @@ impl + ReadVol + Debug> Meshable + ReadVol + Debug> Meshable + ReadVol + Debug> Meshable lowest_opaque && lowest_air <= lowest_fluid) + || (lowest_air > lowest_fluid && lowest_air <= lowest_opaque) + { + lowest_air - 2 + } else if lowest_fluid > lowest_opaque && lowest_fluid <= lowest_air { + lowest_fluid - 2 + } else if lowest_fluid > lowest_air && lowest_fluid <= lowest_opaque { + lowest_fluid - 1 + } else { + lowest_opaque - 1 + } + .max(0); + let z_end = if (highest_air < highest_opaque && highest_air >= highest_fluid) + || (highest_air < highest_fluid && highest_air >= highest_opaque) + { + highest_air + 1 + } else if highest_fluid < highest_opaque && highest_fluid >= highest_air { + highest_fluid + 1 + } else if highest_fluid < highest_air && highest_fluid >= highest_opaque { + highest_fluid + } else { + highest_opaque + } + .min(range.size().d - 1); for x in 1..range.size().w - 1 { for y in 1..range.size().w - 1 { let mut lights = [[[0.0; 3]; 3]; 3]; @@ -266,7 +309,7 @@ impl + ReadVol + Debug> Meshable + ReadVol + Debug> Meshable + ReadVol + Debug> Meshable + ReadVol + Debug> Meshable + ReadVol + Debug> Meshable for VolGrid3d { } } */ - -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) -> i64 { - interleave_i32_with_zeros(pos.x) | (interleave_i32_with_zeros(pos.y) << 1) -} From aa48729376a41e53117bd4786654ee368ca1a7f9 Mon Sep 17 00:00:00 2001 From: Imbris Date: Sat, 11 Jan 2020 23:51:19 -0500 Subject: [PATCH 05/13] Compress position used in propagation queue, decrease outer bounds to the area sunlight can reach the inner area from --- voxygen/src/mesh/terrain.rs | 48 ++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index 08c4cf8c1e..80966e9976 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -38,9 +38,8 @@ fn calc_light + ReadVol + Debug>( 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), + min: bounds.min - Vec3::new(SUNLIGHT as i32 - 1, SUNLIGHT as i32 - 1, 1), + max: bounds.max + Vec3::new(SUNLIGHT as i32 - 1, SUNLIGHT as i32 - 1, 1), }; let mut vol_cached = vol.cached(); @@ -48,7 +47,6 @@ fn calc_light + ReadVol + Debug>( // Voids are voxels that that contain air or liquid that are protected from direct rays by blocks // above them 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 @@ -56,7 +54,6 @@ fn calc_light + ReadVol + Debug>( // 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 z = outer.size().d - 1; @@ -72,8 +69,11 @@ fn calc_light + ReadVol + Debug>( .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)); + prop_que.push_back( + ((x as u32 & 0xff) << 24) + | ((y as u32 & 0xff) << 16) + | ((z - 1) as u32 & 0xffff), + ); } SUNLIGHT } else { @@ -98,7 +98,11 @@ fn calc_light + ReadVol + Debug>( *dest = src - 1; // Can't propagate further if *dest > 1 { - prop_que.push_back(pos); + prop_que.push_back( + ((pos.x as u32 & 0xff) << 24) + | ((pos.y as u32 & 0xff) << 16) + | (pos.z as u32 & 0xffff), + ); } } else { *dest = OPAQUE; @@ -107,7 +111,11 @@ fn calc_light + ReadVol + Debug>( *dest = src - 1; // Can't propagate further if *dest > 1 { - prop_que.push_back(pos); + prop_que.push_back( + ((pos.x as u32 & 0xff) << 24) + | ((pos.y as u32 & 0xff) << 16) + | (pos.z as u32 & 0xffff), + ); } } } @@ -115,9 +123,11 @@ fn calc_light + ReadVol + Debug>( // 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 pos = Vec3::new( + ((pos >> 24) & 0xFF) as i32, + ((pos >> 16) & 0xFF) as i32, + (pos & 0xFFFF) as i32, + ); let light = light_map[lm_idx(pos.x, pos.y, pos.z)]; // If ray propagate downwards at full strength @@ -130,10 +140,18 @@ fn calc_light + ReadVol + Debug>( .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); + prop_que.push_back( + ((pos.x as u32 & 0xff) << 24) + | ((pos.y as u32 & 0xff) << 16) + | (pos.z as u32 & 0xffff), + ); SUNLIGHT } else if is_fluid { - prop_que.push_back(pos); + prop_que.push_back( + ((pos.x as u32 & 0xff) << 24) + | ((pos.y as u32 & 0xff) << 16) + | (pos.z as u32 & 0xffff), + ); SUNLIGHT - 1 } else { OPAQUE @@ -227,8 +245,6 @@ impl + ReadVol + Debug> Meshable Date: Sun, 12 Jan 2020 01:23:21 -0500 Subject: [PATCH 06/13] Cleanup --- voxygen/src/mesh/terrain.rs | 88 +++++++++++++------------------------ 1 file changed, 31 insertions(+), 57 deletions(-) diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index 80966e9976..095cdb19f5 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -13,22 +13,6 @@ use vek::*; type TerrainVertex = ::Vertex; type FluidVertex = ::Vertex; -const DIRS: [Vec2; 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; 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 + ReadVol + Debug>( bounds: Aabb, vol: &VolGrid2d, @@ -44,8 +28,6 @@ fn calc_light + ReadVol + Debug>( let mut vol_cached = vol.cached(); - // Voids are voxels that that contain air or liquid that are protected from direct rays by blocks - // above them let mut light_map = vec![UNKOWN; outer.size().product() as usize]; let lm_idx = { let (w, h, _) = outer.clone().size().into_tuple(); @@ -53,7 +35,7 @@ fn calc_light + ReadVol + Debug>( }; // Light propagation queue let mut prop_que = VecDeque::new(); - // Start rays + // Start sun rays for x in 0..outer.size().w { for y in 0..outer.size().h { let z = outer.size().d - 1; @@ -159,7 +141,6 @@ fn calc_light + ReadVol + Debug>( } 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, @@ -255,33 +236,32 @@ impl + ReadVol + Debug> Meshable + ReadVol + Debug> Meshable lowest_opaque && lowest_air <= lowest_fluid) || (lowest_air > lowest_fluid && lowest_air <= lowest_opaque) { @@ -346,14 +327,10 @@ impl + ReadVol + Debug> Meshable + ReadVol + Debug> Meshable Date: Thu, 9 Jan 2020 01:05:20 -0500 Subject: [PATCH 07/13] Add terrain meshing benchmark --- Cargo.lock | 2 + voxygen/Cargo.toml | 11 +++ voxygen/benches/meshing_benchmark.rs | 126 +++++++++++++++++++++++++++ voxygen/src/lib.rs | 3 + world/src/lib.rs | 1 + 5 files changed, 143 insertions(+) create mode 100644 voxygen/benches/meshing_benchmark.rs create mode 100644 voxygen/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index c8f438c727..56f141905c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3248,6 +3248,7 @@ dependencies = [ "conrod_core 0.63.0 (git+https://gitlab.com/veloren/conrod.git)", "conrod_winit 0.63.0 (git+https://gitlab.com/veloren/conrod.git)", "cpal 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "directories 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "dispatch 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3279,6 +3280,7 @@ dependencies = [ "veloren-client 0.4.0", "veloren-common 0.4.0", "veloren-server 0.4.0", + "veloren-world 0.4.0", "winit 0.19.5 (registry+https://github.com/rust-lang/crates.io-index)", "winres 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 3e497aef74..1760929b2f 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -4,6 +4,9 @@ version = "0.4.0" authors = ["Joshua Barretto ", "Imbris "] edition = "2018" default-run = "veloren-voxygen" +# Cargo thinks it should build the voxygen binary even when a specific bench is specified for building +# Uncomment below and comment out default-run if you want to avoid this +# autobins = false [features] gl = ["gfx_device_gl"] @@ -65,3 +68,11 @@ dispatch = "0.1.4" [target.'cfg(windows)'.build-dependencies] winres = "0.1" + +[dev-dependencies] +criterion = "0.3" +world = { package = "veloren-world", path = "../world" } + +[[bench]] +name = "meshing_benchmark" +harness = false diff --git a/voxygen/benches/meshing_benchmark.rs b/voxygen/benches/meshing_benchmark.rs new file mode 100644 index 0000000000..4025e9f031 --- /dev/null +++ b/voxygen/benches/meshing_benchmark.rs @@ -0,0 +1,126 @@ +use common::terrain::Block; +use common::terrain::TerrainGrid; +use common::vol::SampleVol; +use common::vol::Vox; +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use std::sync::Arc; +use vek::*; +use veloren_voxygen::mesh::Meshable; +use world::World; + +const CENTER: Vec2 = Vec2 { x: 512, y: 512 }; +const GEN_SIZE: i32 = 4; + +pub fn criterion_benchmark(c: &mut Criterion) { + // Generate chunks here to test + let mut terrain = TerrainGrid::new().unwrap(); + let world = World::generate(42); + (0..GEN_SIZE) + .flat_map(|x| (0..GEN_SIZE).map(move |y| Vec2::new(x, y))) + .map(|offset| offset + CENTER) + .map(|pos| (pos, world.generate_chunk(pos, || false).unwrap())) + .for_each(|(key, chunk)| { + terrain.insert(key, Arc::new(chunk.0)); + }); + + let sample = |chunk_pos: Vec2| { + let chunk_pos = chunk_pos + CENTER; + // Find the area of the terrain we want. Because meshing needs to compute things like + // ambient occlusion and edge elision, we also need the borders of the chunk's + // neighbours too (hence the `- 1` and `+ 1`). + let aabr = Aabr { + min: chunk_pos.map2(TerrainGrid::chunk_size(), |e, sz| e * sz as i32 - 1), + max: chunk_pos.map2(TerrainGrid::chunk_size(), |e, sz| (e + 1) * sz as i32 + 1), + }; + + // Copy out the chunk data we need to perform the meshing. We do this by taking a + // sample of the terrain that includes both the chunk we want and its neighbours. + let volume = terrain.sample(aabr).unwrap(); + + // The region to actually mesh + let min_z = volume + .iter() + .fold(std::i32::MAX, |min, (_, chunk)| chunk.get_min_z().min(min)); + let max_z = volume + .iter() + .fold(std::i32::MIN, |max, (_, chunk)| chunk.get_max_z().max(max)); + + let aabb = Aabb { + min: Vec3::from(aabr.min) + Vec3::unit_z() * (min_z - 1), + max: Vec3::from(aabr.max) + Vec3::unit_z() * (max_z + 1), + }; + + (volume, aabb) + }; + + // Test speed of cloning voxel sample into a flat array + let (volume, range) = sample(Vec2::new(1, 1)); + c.bench_function("copying 1,1 into flat array", |b| { + b.iter(|| { + let mut flat = vec![Block::empty(); range.size().product() as usize]; + let mut i = 0; + let mut volume = volume.cached(); + for x in 0..range.size().w { + for y in 0..range.size().h { + for z in 0..range.size().d { + flat[i] = *volume.get(range.min + Vec3::new(x, y, z)).unwrap(); + i += 1; + } + } + } + /*let (w, h, d) = range.size().into_tuple(); + for (chunk_key, chunk) in volume.iter() { + let chunk_pos = volume.key_pos(chunk_key); + let min = chunk_pos.map2( + Vec2::new(range.min.x, range.min.y), + |cmin: i32, rmin: i32| (rmin - cmin).max(0), + ); + // Chunk not in area of interest + if min + .map2(TerrainGrid::chunk_size(), |m, size| m >= size as i32) + .reduce_and() + { + // TODO: comment after ensuing no panics + panic!("Shouldn't happen in this case"); + continue; + } + let min = min.map(|m| m.min(31)); + // TODO: Don't hardcode 31 + let max = chunk_pos.map2(Vec2::new(range.max.x, range.max.y), |cmin, rmax| { + (rmax - cmin).min(31) + }); + if max.map(|m| m < 0).reduce_and() { + panic!("Shouldn't happen in this case: {:?}", max); + continue; + } + let max = max.map(|m| m.max(0)); + // Add z dims + let min = Vec3::new(min.x, min.y, range.min.z); + let max = Vec3::new(max.x, max.y, range.max.z); + // Offset of chunk in sample being cloned + let offset = Vec3::new( + chunk_pos.x - range.min.x, + chunk_pos.y - range.min.y, + -range.min.z, + ); + for (pos, &block) in chunk.vol_iter(min, max) { + let pos = pos + offset; + flat[(w * h * pos.z + w * pos.y + pos.x) as usize] = block; + } + }*/ + black_box(flat); + }); + }); + + for x in 1..GEN_SIZE - 1 { + for y in 1..GEN_SIZE - 1 { + let (volume, range) = sample(Vec2::new(x, y)); + c.bench_function(&format!("Terrain mesh {}, {}", x, y), |b| { + b.iter(|| volume.generate_mesh(black_box(range))) + }); + } + } +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/voxygen/src/lib.rs b/voxygen/src/lib.rs new file mode 100644 index 0000000000..f3fad20a8b --- /dev/null +++ b/voxygen/src/lib.rs @@ -0,0 +1,3 @@ +/// Used by benchmarks +pub mod mesh; +pub mod render; diff --git a/world/src/lib.rs b/world/src/lib.rs index 745515cd54..cf68981989 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -63,6 +63,7 @@ impl World { pub fn generate_chunk( &self, chunk_pos: Vec2, + // TODO: misleading name mut should_continue: impl FnMut() -> bool, ) -> Result<(TerrainChunk, ChunkSupplement), ()> { let air = Block::empty(); From b3cdde3ce99c61942535696db045c102c2861171 Mon Sep 17 00:00:00 2001 From: Imbris Date: Fri, 17 Jan 2020 23:05:26 -0500 Subject: [PATCH 08/13] fix: panic in terrain meshing --- CHANGELOG.md | 1 + voxygen/src/mesh/terrain.rs | 5 ++- voxygen/src/scene/terrain.rs | 82 ++++++++++++++++++------------------ 3 files changed, 46 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ed94cfcce..576ed42aa2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Made shadows and lights use interpolated positions - Changed "Create Character" button position - Made clouds bigger, more performant and prettier +- Terrain meshing optimized further ### Removed diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index 095cdb19f5..7b6e8fb069 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -243,7 +243,10 @@ impl + ReadVol + Debug> Meshable Terrain { .map(|(p, _)| *p) { let chunk_pos = client.state().terrain().pos_key(pos); - let new_mesh_state = ChunkMeshState { - pos: chunk_pos, - started_tick: current_tick, - active_worker: None, - }; // Only mesh if this chunk has all its neighbors - // If it does have all its neighbors either it should have already been meshed or is in - // mesh_todo - match self.mesh_todo.entry(chunk_pos) { - Entry::Occupied(mut entry) => { - entry.insert(new_mesh_state); - } - Entry::Vacant(entry) => { - if self.chunks.contains_key(&chunk_pos) { - entry.insert(new_mesh_state); - } + let mut neighbours = true; + for i in -1..2 { + for j in -1..2 { + neighbours &= client + .state() + .terrain() + .get_key(chunk_pos + Vec2::new(i, j)) + .is_some(); } } + if neighbours { + self.mesh_todo.insert( + chunk_pos, + ChunkMeshState { + pos: chunk_pos, + started_tick: current_tick, + active_worker: None, + }, + ); + } // Handle block changes on chunk borders + // Remesh all neighbours because we have complex lighting now + // TODO: if lighting is on the server this can be updated to only remesh when lighting + // changes in that neighbouring chunk or if the block change was on the border for x in -1..2 { for y in -1..2 { let neighbour_pos = pos + Vec3::new(x, y, 0); let neighbour_chunk_pos = client.state().terrain().pos_key(neighbour_pos); if neighbour_chunk_pos != chunk_pos { - let new_mesh_state = ChunkMeshState { - pos: neighbour_chunk_pos, - started_tick: current_tick, - active_worker: None, - }; - // Only mesh if this chunk has all its neighbors - match self.mesh_todo.entry(neighbour_chunk_pos) { - Entry::Occupied(mut entry) => { - entry.insert(new_mesh_state); - } - Entry::Vacant(entry) => { - if self.chunks.contains_key(&neighbour_chunk_pos) { - entry.insert(new_mesh_state); - } + // Only remesh if this chunk has all its neighbors + let mut neighbours = true; + for i in -1..2 { + for j in -1..2 { + neighbours &= client + .state() + .terrain() + .get_key(neighbour_chunk_pos + Vec2::new(i, j)) + .is_some(); } } + if neighbours { + self.mesh_todo.insert( + neighbour_chunk_pos, + ChunkMeshState { + pos: neighbour_chunk_pos, + started_tick: current_tick, + active_worker: None, + }, + ); + } } - - // TODO: Remesh all neighbours because we have complex lighting now - /*self.mesh_todo.insert( - neighbour_chunk_pos, - ChunkMeshState { - pos: chunk_pos + Vec2::new(x, y), - started_tick: current_tick, - active_worker: None, - }, - ); - */ } } } From ae1fa5e4c446dce18fc01f3cc04fd0d6bb520b0b Mon Sep 17 00:00:00 2001 From: timokoesters Date: Sun, 12 Jan 2020 12:09:37 +0100 Subject: [PATCH 09/13] improvement: fog doesn't go back and forth anymore --- client/src/lib.rs | 33 +++++++++++++++++++++------------ voxygen/src/hud/mod.rs | 5 +++-- voxygen/src/scene/mod.rs | 2 +- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index 1f20cc1227..1a360460c4 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -69,7 +69,7 @@ pub struct Client { entity: EcsEntity, view_distance: Option, - loaded_distance: Option, + loaded_distance: f32, pending_chunks: HashMap, Instant>, } @@ -153,7 +153,7 @@ impl Client { state, entity, view_distance, - loaded_distance: None, + loaded_distance: 0.0, pending_chunks: HashMap::new(), }) @@ -260,7 +260,7 @@ impl Client { self.view_distance } - pub fn loaded_distance(&self) -> Option { + pub fn loaded_distance(&self) -> f32 { self.loaded_distance } @@ -410,8 +410,9 @@ impl Client { } // Request chunks from the server. - let mut all_loaded = true; - 'outer: for dist in 0..(view_distance as i32) + 1 { + self.loaded_distance = ((view_distance * TerrainChunkSize::RECT_SIZE.x) as f32).powi(2); + // +1 so we can find a chunk that's outside the vd for better fog + for dist in 0..view_distance as i32 + 1 { // Only iterate through chunks that need to be loaded for circular vd // The (dist - 2) explained: // -0.5 because a chunk is visible if its corner is within the view distance @@ -428,6 +429,7 @@ impl Client { dist }; + let mut skip_mode = false; for i in -top..top + 1 { let keys = [ chunk_pos + Vec2::new(dist, i), @@ -438,25 +440,32 @@ impl Client { for key in keys.iter() { if self.state.terrain().get_key(*key).is_none() { - if !self.pending_chunks.contains_key(key) { + if !skip_mode && !self.pending_chunks.contains_key(key) { if self.pending_chunks.len() < 4 { self.postbox .send_message(ClientMsg::TerrainChunkRequest { key: *key }); self.pending_chunks.insert(*key, Instant::now()); } else { - break 'outer; + skip_mode = true; } } - all_loaded = false; + let dist_to_player = + (self.state.terrain().key_pos(*key).map(|x| x as f32) + + TerrainChunkSize::RECT_SIZE.map(|x| x as f32) / 2.0) + .distance_squared(pos.0.into()); + + if dist_to_player < self.loaded_distance { + self.loaded_distance = dist_to_player; + } } } } - - if all_loaded { - self.loaded_distance = Some((dist - 1).max(0) as u32); - } } + self.loaded_distance = self.loaded_distance.sqrt() + - ((TerrainChunkSize::RECT_SIZE.x as f32 / 2.0).powi(2) + + (TerrainChunkSize::RECT_SIZE.y as f32 / 2.0).powi(2)) + .sqrt(); // If chunks are taking too long, assume they're no longer pending. let now = Instant::now(); diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 42bf449c1f..dfaa32e2ed 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -1433,8 +1433,9 @@ impl Hud { .set(self.ids.velocity, ui_widgets); // Loaded distance Text::new(&format!( - "View distance: {} chunks", - client.loaded_distance().unwrap_or(0) + "View distance: {:.2} blocks ({:.2} chunks)", + client.loaded_distance(), + client.loaded_distance() / TerrainChunk::RECT_SIZE.x as f32, )) .color(TEXT_COLOR) .down_from(self.ids.velocity, 5.0) diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 9be5180727..e571e175c7 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -208,7 +208,7 @@ impl Scene { let (view_mat, proj_mat, cam_pos) = self.camera.compute_dependents(client); // Update chunk loaded distance smoothly for nice shader fog - let loaded_distance = client.loaded_distance().unwrap_or(0) as f32 * 32.0; // TODO: No magic! + let loaded_distance = client.loaded_distance(); self.loaded_distance = (0.98 * self.loaded_distance + 0.02 * loaded_distance).max(0.01); // Update light constants From 4b01c1b0820d26738e057b3388b725a494d7d85d Mon Sep 17 00:00:00 2001 From: Imbris Date: Sat, 18 Jan 2020 00:24:48 -0500 Subject: [PATCH 10/13] add: capability to disable blending for particular BlockKinds --- common/src/terrain/block.rs | 1 + voxygen/src/mesh/terrain.rs | 46 ++++++++++++++++++++++++++++--------- world/src/block/mod.rs | 6 ++--- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 3c58bd2a12..b5ae25e919 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -32,6 +32,7 @@ pub enum BlockKind { Velorite, VeloriteFrag, Chest, + Leaves, } impl BlockKind { diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index 7b6e8fb069..1235ea81a4 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -3,7 +3,7 @@ use crate::{ render::{self, FluidPipeline, Mesh, TerrainPipeline}, }; use common::{ - terrain::Block, + terrain::{Block, BlockKind}, vol::{ReadVol, RectRasterableVol, Vox}, volumes::vol_grid_2d::{CachedVolGrid2d, VolGrid2d}, }; @@ -13,6 +13,19 @@ use vek::*; type TerrainVertex = ::Vertex; type FluidVertex = ::Vertex; +trait Blendable { + fn is_blended(&self) -> bool; +} + +impl Blendable for BlockKind { + fn is_blended(&self) -> bool { + match self { + BlockKind::Leaves => false, + _ => true, + } + } +} + fn calc_light + ReadVol + Debug>( bounds: Aabb, vol: &VolGrid2d, @@ -317,16 +330,15 @@ impl + ReadVol + Debug> Meshable| { + let get_color = |maybe_block: Option<&Block>, neighbour: bool| { maybe_block - .filter(|vox| vox.is_opaque()) + .filter(|vox| vox.is_opaque() && (!neighbour || vox.is_blended())) .and_then(|vox| vox.get_color()) .map(|col| Rgba::from_opaque(col)) .unwrap_or(Rgba::zero()) }; let mut blocks = [[[None; 3]; 3]; 3]; - let mut colors = [[[Rgba::zero(); 3]; 3]; 3]; for i in 0..3 { for j in 0..3 { for k in 0..3 { @@ -334,7 +346,6 @@ impl + ReadVol + Debug> Meshable + ReadVol + Debug> Meshable + ReadVol + Debug> Meshable + ReadVol + Debug> Meshable None, StructureBlock::TemperateLeaves => Some(Block::new( - BlockKind::Normal, + BlockKind::Leaves, Lerp::lerp( Rgb::new(0.0, 132.0, 94.0), Rgb::new(142.0, 181.0, 0.0), @@ -584,12 +584,12 @@ pub fn block_from_structure( .map(|e| e as u8), )), StructureBlock::PineLeaves => Some(Block::new( - BlockKind::Normal, + BlockKind::Leaves, Lerp::lerp(Rgb::new(0.0, 60.0, 50.0), Rgb::new(30.0, 100.0, 10.0), lerp) .map(|e| e as u8), )), StructureBlock::PalmLeaves => Some(Block::new( - BlockKind::Normal, + BlockKind::Leaves, Lerp::lerp( Rgb::new(0.0, 108.0, 113.0), Rgb::new(30.0, 156.0, 10.0), From 3553c31101b414f3fa1755af801a9c4437f5d3d1 Mon Sep 17 00:00:00 2001 From: Imbris Date: Sat, 18 Jan 2020 16:58:15 -0500 Subject: [PATCH 11/13] Add release debuginfo profile --- CHANGELOG.md | 1 + Cargo.toml | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 576ed42aa2..3b2a4eaecb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Changed "Create Character" button position - Made clouds bigger, more performant and prettier - Terrain meshing optimized further +- Tree leaves no longer color blended ### Removed diff --git a/Cargo.toml b/Cargo.toml index 122302dbfc..5eb5e07257 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,3 +53,8 @@ overflow-checks = false debug-assertions = false lto = true debug = false + +# this profile is used by developers for release profiling +[profile.releasedebuginfo] +inherits = 'release' +debug = 1 From 3c8d0a3bb9c7992f566d8804a1318ed3a5e8790e Mon Sep 17 00:00:00 2001 From: Imbris Date: Sun, 19 Jan 2020 15:48:57 -0500 Subject: [PATCH 12/13] fix typo, replace u32 with Vec3 in light propagation queue --- client/src/lib.rs | 1 + voxygen/src/mesh/terrain.rs | 44 +++++++++---------------------------- 2 files changed, 11 insertions(+), 34 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index 1a360460c4..94dda21e5a 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -69,6 +69,7 @@ pub struct Client { entity: EcsEntity, view_distance: Option, + // TODO: move into voxygen loaded_distance: f32, pending_chunks: HashMap, Instant>, diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index 1235ea81a4..d98c27dd2c 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -30,7 +30,7 @@ fn calc_light + ReadVol + Debug>( bounds: Aabb, vol: &VolGrid2d, ) -> impl Fn(Vec3) -> f32 { - const UNKOWN: u8 = 255; + const UNKNOWN: u8 = 255; const OPAQUE: u8 = 254; const SUNLIGHT: u8 = 24; @@ -41,7 +41,7 @@ fn calc_light + ReadVol + Debug>( let mut vol_cached = vol.cached(); - let mut light_map = vec![UNKOWN; outer.size().product() as usize]; + let mut light_map = vec![UNKNOWN; outer.size().product() as usize]; let lm_idx = { let (w, h, _) = outer.clone().size().into_tuple(); move |x, y, z| (z * h * w + x * h + y) as usize @@ -64,11 +64,7 @@ fn calc_light + ReadVol + Debug>( .map_or(false, |b| b.is_air()) { light_map[lm_idx(x, y, z - 1)] = SUNLIGHT; - prop_que.push_back( - ((x as u32 & 0xff) << 24) - | ((y as u32 & 0xff) << 16) - | ((z - 1) as u32 & 0xffff), - ); + prop_que.push_back(Vec3::new(x as u8, y as u8, z as u8)); } SUNLIGHT } else { @@ -84,7 +80,7 @@ fn calc_light + ReadVol + Debug>( prop_que: &mut VecDeque<_>, vol: &mut CachedVolGrid2d| { if *dest != OPAQUE { - if *dest == UNKOWN { + if *dest == UNKNOWN { if vol .get(outer.min + pos) .ok() @@ -93,11 +89,7 @@ fn calc_light + ReadVol + Debug>( *dest = src - 1; // Can't propagate further if *dest > 1 { - prop_que.push_back( - ((pos.x as u32 & 0xff) << 24) - | ((pos.y as u32 & 0xff) << 16) - | (pos.z as u32 & 0xffff), - ); + prop_que.push_back(Vec3::new(pos.x as u8, pos.y as u8, pos.z as u8)); } } else { *dest = OPAQUE; @@ -106,11 +98,7 @@ fn calc_light + ReadVol + Debug>( *dest = src - 1; // Can't propagate further if *dest > 1 { - prop_que.push_back( - ((pos.x as u32 & 0xff) << 24) - | ((pos.y as u32 & 0xff) << 16) - | (pos.z as u32 & 0xffff), - ); + prop_que.push_back(Vec3::new(pos.x as u8, pos.y as u8, pos.z as u8)); } } } @@ -118,11 +106,7 @@ fn calc_light + ReadVol + Debug>( // Propage light while let Some(pos) = prop_que.pop_front() { - let pos = Vec3::new( - ((pos >> 24) & 0xFF) as i32, - ((pos >> 16) & 0xFF) as i32, - (pos & 0xFFFF) as i32, - ); + let pos = pos.map(|e| e as i32); let light = light_map[lm_idx(pos.x, pos.y, pos.z)]; // If ray propagate downwards at full strength @@ -135,18 +119,10 @@ fn calc_light + ReadVol + Debug>( .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.x as u32 & 0xff) << 24) - | ((pos.y as u32 & 0xff) << 16) - | (pos.z as u32 & 0xffff), - ); + prop_que.push_back(Vec3::new(pos.x as u8, pos.y as u8, pos.z as u8)); SUNLIGHT } else if is_fluid { - prop_que.push_back( - ((pos.x as u32 & 0xff) << 24) - | ((pos.y as u32 & 0xff) << 16) - | (pos.z as u32 & 0xffff), - ); + prop_que.push_back(Vec3::new(pos.x as u8, pos.y as u8, pos.z as u8)); SUNLIGHT - 1 } else { OPAQUE @@ -217,7 +193,7 @@ fn calc_light + ReadVol + Debug>( let pos = wpos - outer.min; light_map .get(lm_idx(pos.x, pos.y, pos.z)) - .filter(|l| **l != OPAQUE && **l != UNKOWN) + .filter(|l| **l != OPAQUE && **l != UNKNOWN) .map(|l| *l as f32 / SUNLIGHT as f32) .unwrap_or(0.0) } From 94210070f7b2663c37ffd2e8b001e75334c971ea Mon Sep 17 00:00:00 2001 From: Imbris Date: Sun, 19 Jan 2020 16:00:33 -0500 Subject: [PATCH 13/13] fmt fix --- voxygen/src/hud/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index dfaa32e2ed..e79965efd7 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -1208,8 +1208,7 @@ impl Hud { } // Introduction Text - let intro_text: &'static str = - "Welcome to the Veloren Alpha!\n\ + let intro_text: &'static str = "Welcome to the Veloren Alpha!\n\ \n\ \n\ Some tips before you start:\n\