diff --git a/common/src/cmd.rs b/common/src/cmd.rs index 4c800c3f93..7e3194c4a6 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -298,11 +298,6 @@ impl ChatCommand { "View the server description", NoAdmin, ), - ChatCommand::MakeBlock => cmd( - vec![Enum("block", BLOCK_KINDS.clone(), Required)], - "Make a block", - Admin, - ), ChatCommand::Object => cmd( vec![Enum("object", OBJECTS.clone(), Required)], "Spawn an object", diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 3cdd6c986e..3a87f8f767 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -202,6 +202,14 @@ impl BlockKind { } } + pub fn get_glow(&self) -> Option { + match self { + BlockKind::StreetLamp | BlockKind::StreetLampTall => Some(20), + BlockKind::Velorite | BlockKind::VeloriteFrag => Some(10), + _ => None, + } + } + pub fn is_opaque(&self) -> bool { match self { BlockKind::Air => false, diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index a25d174313..b465565b71 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -4,7 +4,7 @@ use crate::{ }; use common::{ terrain::{Block, BlockKind}, - vol::{ReadVol, RectRasterableVol, Vox}, + vol::{DefaultVolIterator, ReadVol, RectRasterableVol, Vox}, volumes::vol_grid_2d::{CachedVolGrid2d, VolGrid2d}, }; use std::{collections::VecDeque, fmt::Debug}; @@ -26,13 +26,16 @@ impl Blendable for BlockKind { } } +const SUNLIGHT: u8 = 24; +const MAX_LIGHT_DIST: i32 = SUNLIGHT as i32; + fn calc_light + ReadVol + Debug>( bounds: Aabb, vol: &VolGrid2d, + lit_blocks: impl Iterator, u8)>, ) -> impl FnMut(Vec3) -> f32 + '_ { const UNKNOWN: u8 = 255; const OPAQUE: u8 = 254; - const SUNLIGHT: u8 = 24; let outer = Aabb { min: bounds.min - Vec3::new(SUNLIGHT as i32 - 1, SUNLIGHT as i32 - 1, 1), @@ -47,7 +50,13 @@ fn calc_light + ReadVol + Debug>( move |x, y, z| (z * h * w + x * h + y) as usize }; // Light propagation queue - let mut prop_que = VecDeque::new(); + let mut prop_que = lit_blocks + .map(|(pos, light)| { + let rpos = pos - outer.min; + light_map[lm_idx(rpos.x, rpos.y, rpos.z)] = light; + (rpos.x as u8, rpos.y as u8, rpos.z as u16) + }) + .collect::>(); // Start sun rays for x in 0..outer.size().w { for y in 0..outer.size().h { @@ -216,7 +225,13 @@ impl<'a, V: RectRasterableVol + ReadVol + Debug> &'a self, range: Self::Supplement, ) -> (Mesh, Mesh) { - let mut light = calc_light(range, self); + // Find blocks that should glow + let lit_blocks = + DefaultVolIterator::new(self, range.min - MAX_LIGHT_DIST, range.max + MAX_LIGHT_DIST) + .filter_map(|(pos, block)| block.get_glow().map(|glow| (pos, glow))); + + // Calculate chunk lighting + let mut light = calc_light(range, self, lit_blocks); let mut lowest_opaque = range.size().d; let mut highest_opaque = 0; diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index 0573816606..6cffd77fd8 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -2820,26 +2820,6 @@ impl Terrain { .iter() .map(|(p, _)| *p) { - let chunk_pos = scene_data.state.terrain().pos_key(pos); - // Only mesh if this chunk has all its neighbors - let mut neighbours = true; - for i in -1..2 { - for j in -1..2 { - neighbours &= scene_data - .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 @@ -2850,26 +2830,24 @@ impl Terrain { let neighbour_pos = pos + Vec3::new(x, y, 0); let neighbour_chunk_pos = scene_data.state.terrain().pos_key(neighbour_pos); - if neighbour_chunk_pos != chunk_pos { - // Only remesh if this chunk has all its neighbors - let mut neighbours = true; - for i in -1..2 { - for j in -1..2 { - neighbours &= scene_data - .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, - }); + // Only remesh if this chunk has all its neighbors + let mut neighbours = true; + for i in -1..2 { + for j in -1..2 { + neighbours &= scene_data + .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, + }); + } } } } diff --git a/world/src/site/castle/mod.rs b/world/src/site/castle/mod.rs index 33d83d24c2..319ffc6873 100644 --- a/world/src/site/castle/mod.rs +++ b/world/src/site/castle/mod.rs @@ -48,6 +48,7 @@ pub struct Castle { radius: i32, towers: Vec, segments: Vec, + rounded_towers: bool, } pub struct GenCtx<'a, R: Rng> { @@ -106,6 +107,7 @@ impl Castle { } }) .collect(), + rounded_towers: ctx.rng.gen(), segments: (0..0) //rng.gen_range(18, 24)) .map(|_| { @@ -159,7 +161,7 @@ impl Castle { continue; }; - let (wall_dist, wall_pos, wall_alt, wall_ori) = (0..self.towers.len()) + let (wall_dist, wall_pos, wall_alt, wall_ori, towers) = (0..self.towers.len()) .map(|i| { let tower0 = &self.towers[i]; let tower1 = &self.towers[(i + 1) % self.towers.len()]; @@ -171,7 +173,7 @@ impl Castle { let projected = wall .projected_point(rpos.map(|e| e as f32)) - .map(|e| e as i32); + .map(|e| e.floor() as i32); let tower0_dist = tower0 .offset @@ -195,11 +197,19 @@ impl Castle { projected, Lerp::lerp(tower0.alt as f32, tower1.alt as f32, tower_lerp) as i32, wall_ori, + [tower0, tower1], ) }) .min_by_key(|x| x.0) .unwrap(); + // Apply the dungeon entrance + let wall_sample = if let Some(col) = get_column(offs + wall_pos - rpos) { + col + } else { + col_sample + }; + for z in -10..64 { let wpos = Vec3::new(wpos2d.x, wpos2d.y, col_sample.alt as i32 + z); @@ -226,6 +236,7 @@ impl Castle { &Attr { height: 16, is_tower: false, + rounded: true, }, ); for tower in &self.towers { @@ -257,6 +268,7 @@ impl Castle { &Attr { height: 28, is_tower: true, + rounded: self.rounded_towers, }, )); } diff --git a/world/src/site/settlement/building/archetype/house.rs b/world/src/site/settlement/building/archetype/house.rs index ef2fd417c9..4c9a2492ef 100644 --- a/world/src/site/settlement/building/archetype/house.rs +++ b/world/src/site/settlement/building/archetype/house.rs @@ -85,6 +85,8 @@ pub struct Attr { pub roof_style: RoofStyle, pub mansard: i32, pub pillar: Pillar, + pub levels: i32, + pub window: BlockKind, } impl Attr { @@ -103,9 +105,16 @@ impl Attr { }, mansard: rng.gen_range(-7, 4).max(0), pillar: match rng.gen_range(0, 4) { - 0 => Pillar::Chimney(9 + locus + rng.gen_range(0, 4)), + 0 => Pillar::Chimney(rng.gen_range(1, 5)), _ => Pillar::None, }, + levels: rng.gen_range(1, 3), + window: match rng.gen_range(0, 4) { + 0 => BlockKind::Window1, + 1 => BlockKind::Window2, + 2 => BlockKind::Window3, + _ => BlockKind::Window4, + }, } } } @@ -126,10 +135,11 @@ impl Archetype for House { storey_fill: StoreyFill::All, mansard: 0, pillar: match rng.gen_range(0, 3) { - 0 => Pillar::Chimney(10 + locus + rng.gen_range(0, 4)), - 1 => Pillar::Tower(15 + locus + rng.gen_range(0, 4)), + 0 => Pillar::Chimney(rng.gen_range(1, 5)), + 1 => Pillar::Tower(5 + rng.gen_range(1, 5)), _ => Pillar::None, }, + levels: rng.gen_range(1, 4), ..Attr::generate(rng, locus) }, locus, @@ -224,12 +234,15 @@ impl Archetype for House { let empty = BlockMask::nothing(); let internal = BlockMask::new(Block::empty(), internal_layer); let end_window = BlockMask::new( - Block::new(BlockKind::Window1, make_meta(ori.flip())), + Block::new(attr.window, make_meta(ori.flip())), structural_layer, ); let fire = BlockMask::new(Block::new(BlockKind::Ember, Rgb::white()), foundation_layer); - let ceil_height = 6; + let storey_height = 6; + let storey = ((z - 1) / storey_height).min(attr.levels - 1); + let floor_height = storey_height * storey; + let ceil_height = storey_height * (storey + 1); let lower_width = locus - 1; let upper_width = locus; let width = if profile.y >= ceil_height { @@ -238,9 +251,10 @@ impl Archetype for House { lower_width }; let foundation_height = 0 - (dist - width - 1).max(0); - let roof_top = 8 + width; + let roof_top = storey_height * attr.levels + 2 + width; - if let Pillar::Chimney(chimney_top) = attr.pillar { + if let Pillar::Chimney(chimney_height) = attr.pillar { + let chimney_top = roof_top + chimney_height; // Chimney shaft if center_offset.map(|e| e.abs()).reduce_max() == 0 && profile.y >= foundation_height + 1 @@ -331,12 +345,14 @@ impl Archetype for House { && bound_offset.x < width && profile.y < ceil_height && attr.storey_fill.has_lower() + && storey == 0 { return Some( if (bound_offset.x == (width - 1) / 2 || bound_offset.x == (width - 1) / 2 + 1) && profile.y <= foundation_height + 3 { + // Doors on first floor only if profile.y == foundation_height + 1 { BlockMask::new( Block::new( @@ -377,7 +393,7 @@ impl Archetype for House { } else { ( Aabr { - min: Vec2::new(2, foundation_height + 2), + min: Vec2::new(2, floor_height + 2), max: Vec2::new(width - 2, ceil_height - 2), }, Vec2::new(1, 0), @@ -393,7 +409,7 @@ impl Archetype for House { // Window if (frame_bounds.size() + 1).reduce_min() > 2 { // Window frame is large enough for a window - let surface_pos = Vec2::new(bound_offset.x, profile.y); + let surface_pos = Vec2::new(bound_offset.x, profile.y - floor_height); if window_bounds.contains_point(surface_pos) { return Some(end_window); } else if frame_bounds.contains_point(surface_pos) { @@ -441,7 +457,8 @@ impl Archetype for House { cblock = cblock.resolve_with(block); } - if let Pillar::Tower(tower_top) = attr.pillar { + if let Pillar::Tower(tower_height) = attr.pillar { + let tower_top = roof_top + tower_height; let profile = Vec2::new(center_offset.x.abs(), profile.y); let dist = center_offset.map(|e| e.abs()).reduce_max(); diff --git a/world/src/site/settlement/building/archetype/keep.rs b/world/src/site/settlement/building/archetype/keep.rs index 333c208b88..51a5c47c15 100644 --- a/world/src/site/settlement/building/archetype/keep.rs +++ b/world/src/site/settlement/building/archetype/keep.rs @@ -14,6 +14,7 @@ pub struct Keep { pub struct Attr { pub height: i32, pub is_tower: bool, + pub rounded: bool, } impl Archetype for Keep { @@ -29,6 +30,7 @@ impl Archetype for Keep { attr: Attr { height: rng.gen_range(12, 16), is_tower: false, + rounded: true, }, locus: 10 + rng.gen_range(0, 5), border: 3, @@ -41,6 +43,7 @@ impl Archetype for Keep { attr: Attr { height: rng.gen_range(20, 28), is_tower: true, + rounded: true, }, locus: 4 + rng.gen_range(0, 5), border: 3, @@ -89,7 +92,12 @@ impl Archetype for Keep { let foundation = make_block(100, 100, 100); let wall = make_block(100, 100, 110); - let floor = make_block(120, 80, 50).with_priority(important_layer); + let floor = make_block( + 80 + (pos.y.abs() % 2) as u8 * 15, + 60 + (pos.y.abs() % 2) as u8 * 15, + 10 + (pos.y.abs() % 2) as u8 * 15, + ) + .with_priority(important_layer); let pole = make_block(90, 70, 50).with_priority(important_layer); let flag = make_block(self.flag_color.r, self.flag_color.g, self.flag_color.b) .with_priority(important_layer); @@ -106,12 +114,11 @@ impl Archetype for Keep { pos.x }; let rampart_height = ceil_height + if edge_pos % 2 == 0 { 3 } else { 4 }; - let inner = Clamp::clamp( - center_offset, - Vec2::new(-5, -len / 2 - 5), - Vec2::new(5, len / 2 + 5), - ); - let min_dist = bound_offset.map(|e| e.pow(2) as f32).sum().powf(0.5) as i32; //(bound_offset.distance_squared(inner) as f32).sqrt() as i32 + 5;//bound_offset.reduce_max(); + let min_dist = if attr.rounded { + bound_offset.map(|e| e.pow(2) as f32).sum().powf(0.5) as i32 + } else { + bound_offset.map(|e| e.abs()).reduce_max() + }; if profile.y <= 0 - (min_dist - width - 1).max(0) && min_dist < width + 3 { // Foundations diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs index 07a46f7c44..0c563e5ad8 100644 --- a/world/src/site/settlement/mod.rs +++ b/world/src/site/settlement/mod.rs @@ -390,6 +390,7 @@ impl Settlement { district .and_then(|d| self.town.as_ref().map(|t| t.districts().get(d))) .map(|d| d.alt) + .filter(|_| false) // Temporary .unwrap_or_else(|| { ctx.sim .and_then(|sim| sim.get_alt_approx(self.origin + house_pos)) @@ -563,8 +564,10 @@ impl Settlement { // District alt if let Some(Plot::Town { district }) = sample.plot { - if let Some(d) = - district.and_then(|d| self.town.as_ref().map(|t| t.districts().get(d))) + if let Some(d) = district + .and_then(|d| self.town.as_ref().map(|t| t.districts().get(d))) + .filter(|_| false) + // Temporary { let other = self .land @@ -576,6 +579,7 @@ impl Settlement { .and_then(|d| { self.town.as_ref().map(|t| t.districts().get(d).alt as f32) }) + .filter(|_| false) .unwrap_or(surface_z as f32); surface_z = Lerp::lerp( (other + d.alt as f32) / 2.0, @@ -640,14 +644,15 @@ impl Settlement { .rotated_z(f32::consts::PI / 2.0) .normalized(); let is_lamp = if path_dir.x.abs() > path_dir.y.abs() { - wpos2d.x as f32 % 20.0 / path_dir.dot(Vec2::unit_y()).abs() + wpos2d.x as f32 % 30.0 / path_dir.dot(Vec2::unit_y()).abs() <= 1.0 } else { - wpos2d.y as f32 % 20.0 / path_dir.dot(Vec2::unit_x()).abs() + (wpos2d.y as f32 + 10.0) % 30.0 + / path_dir.dot(Vec2::unit_x()).abs() <= 1.0 }; if (col_sample.path.map(|(dist, _)| dist > 6.0 && dist < 7.0).unwrap_or(false) && is_lamp) //roll(0, 50) == 0) - || roll(0, 2000) == 0 + || (roll(0, 2000) == 0 && col_sample.path.map(|(dist, _)| dist > 20.0).unwrap_or(true)) { surface_block = Some(Block::new(BlockKind::StreetLamp, Rgb::white()));