diff --git a/world/src/block/natural.rs b/world/src/block/natural.rs index 845a033982..e09ca2158a 100644 --- a/world/src/block/natural.rs +++ b/world/src/block/natural.rs @@ -28,7 +28,7 @@ pub fn structure_gen<'a>( || st_sample.alt < st_sample.water_level || st_sample.spawn_rate < 0.5 || st_sample.water_dist.map(|d| d < 8.0).unwrap_or(false) - || st_sample.path.map(|(d, _)| d < 12.0).unwrap_or(false) + || st_sample.path.map(|(d, _, _, _)| d < 12.0).unwrap_or(false) { return None; } diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index bbcc762428..e0df5e832b 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -1,7 +1,7 @@ use crate::{ all::ForestKind, block::StructureMeta, - sim::{local_cells, uniform_idx_as_vec2, vec2_as_uniform_idx, RiverKind, SimChunk, WorldSim}, + sim::{local_cells, uniform_idx_as_vec2, vec2_as_uniform_idx, RiverKind, SimChunk, WorldSim, Path}, util::Sampler, Index, CONFIG, }; @@ -1157,7 +1157,7 @@ pub struct ColumnSample<'a> { pub spawn_rate: f32, pub stone_col: Rgb, pub water_dist: Option, - pub path: Option<(f32, Vec2)>, + pub path: Option<(f32, Vec2, Path, Vec2)>, pub chunk: &'a SimChunk, } diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 6b3ebfbb50..d8f6b9228d 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -37,7 +37,7 @@ pub fn apply_paths_to<'a>( }) }; - if let Some((path_dist, path_nearest)) = col_sample.path.filter(|(dist, _)| *dist < 5.0) + if let Some((path_dist, path_nearest, path, _)) = col_sample.path.filter(|(dist, _, path, _)| *dist < path.width) { let inset = 0; @@ -82,7 +82,7 @@ pub fn apply_paths_to<'a>( }, ); } - let head_space = (8 - (path_dist * 0.25).powf(6.0).round() as i32).max(1); + let head_space = path.head_space(path_dist); for z in inset..inset + head_space { let pos = Vec3::new(offs.x, offs.y, surface_z + z); if vol.get(pos).unwrap().kind() != BlockKind::Water { diff --git a/world/src/lib.rs b/world/src/lib.rs index 67769a1d67..9aabf4dff8 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -175,14 +175,14 @@ impl World { let mut rng = rand::thread_rng(); + // Apply paths + layer::apply_paths_to(chunk_wpos2d, sample_get, &mut chunk); + // Apply site generation sim_chunk.sites.iter().for_each(|site| { self.index.sites[*site].apply_to(chunk_wpos2d, sample_get, &mut chunk) }); - // Apply paths - layer::apply_paths_to(chunk_wpos2d, sample_get, &mut chunk); - let gen_entity_pos = || { let lpos2d = TerrainChunkSize::RECT_SIZE .map(|sz| rand::thread_rng().gen::().rem_euclid(sz) as i32); diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index fc5ca00208..00c95058e3 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -15,7 +15,7 @@ pub use self::{ }, location::Location, map::{MapConfig, MapDebug}, - path::PathData, + path::{Path, PathData}, util::{ cdf_irwin_hall, downhill, get_oceans, local_cells, map_edge_factor, neighbors, uniform_idx_as_vec2, uniform_noise, uphill, vec2_as_uniform_idx, InverseCdf, ScaleBias, @@ -1699,7 +1699,9 @@ impl WorldSim { Some(z0 + z1 + z2 + z3) } - pub fn get_nearest_path(&self, wpos: Vec2) -> Option<(f32, Vec2)> { + /// Return the distance to the nearest path in blocks, along with the closest point on the path + /// and the tangent vector of that path. + pub fn get_nearest_path(&self, wpos: Vec2) -> Option<(f32, Vec2, Path, Vec2)> { let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| { e.div_euclid(sz as i32) }); @@ -1759,13 +1761,13 @@ impl WorldSim { .clamped(0.0, 1.0); let pos = bez.evaluate(nearest_interval); let dist_sqrd = pos.distance_squared(wpos.map(|e| e as f32)); - Some((dist_sqrd, pos)) + Some((dist_sqrd, pos, chunk.path.path, move || bez.evaluate_derivative(nearest_interval).normalized())) }), ) }) .flatten() - .min_by_key(|(dist_sqrd, _)| (dist_sqrd * 1024.0) as i32) - .map(|(dist, pos)| (dist.sqrt(), pos)) + .min_by_key(|(dist_sqrd, _, _, _)| (dist_sqrd * 1024.0) as i32) + .map(|(dist, pos, path, calc_tangent)| (dist.sqrt(), pos, path, calc_tangent())) } } diff --git a/world/src/sim/path.rs b/world/src/sim/path.rs index 525069a1d8..c8ac115b38 100644 --- a/world/src/sim/path.rs +++ b/world/src/sim/path.rs @@ -1,9 +1,15 @@ use vek::*; +#[derive(Copy, Clone, Debug)] +pub struct Path { + pub width: f32, +} + #[derive(Debug)] pub struct PathData { pub offset: Vec2, /* Offset from centre of chunk: must not be more than half chunk * width in any direction */ + pub path: Path, pub neighbors: u8, // One bit for each neighbor } @@ -15,7 +21,17 @@ impl Default for PathData { fn default() -> Self { Self { offset: Vec2::zero(), + path: Path { + width: 5.0, + }, neighbors: 0, } } } + +impl Path { + /// Return the number of blocks of headspace required at the given path distance + pub fn head_space(&self, dist: f32) -> i32 { + (8 - (dist * 0.25).powf(6.0).round() as i32).max(1) + } +} diff --git a/world/src/site/castle/mod.rs b/world/src/site/castle/mod.rs index 319ffc6873..12952c6d52 100644 --- a/world/src/site/castle/mod.rs +++ b/world/src/site/castle/mod.rs @@ -88,7 +88,7 @@ impl Castle { if ctx .sim .and_then(|sim| sim.get_nearest_path(wpos + offset)) - .map(|(dist, _)| dist > 24.0) + .map(|(dist, _, _, _)| dist > 24.0) .unwrap_or(true) { break; @@ -202,6 +202,16 @@ impl Castle { }) .min_by_key(|x| x.0) .unwrap(); + let border_pos = (wall_pos - rpos).map(|e| e.abs()); + let wall_normal = (rpos - wall_pos).map(|e| e as f32); + let wall_rpos = if wall_ori == Ori::East { + rpos + } else { + rpos.yx() + }; + let head_space = col_sample.path + .map(|(dist, _, path, _)| path.head_space(dist)) + .unwrap_or(0); // Apply the dungeon entrance let wall_sample = if let Some(col) = get_column(offs + wall_pos - rpos) { @@ -218,27 +228,26 @@ impl Castle { }; // Boundary - let border_pos = (wall_pos - rpos).map(|e| e.abs()); - let wall_rpos = if wall_ori == Ori::East { - rpos + let wall_z = wpos.z - wall_alt; + let mut mask = if z < head_space { + BlockMask::nothing() } else { - rpos.yx() + keep.draw( + Vec3::from(wall_rpos) + Vec3::unit_z() * wpos.z - wall_alt, + wall_dist, + Vec2::new(border_pos.reduce_max(), border_pos.reduce_min()), + rpos - wall_pos, + wall_z, + wall_ori, + 4, + 0, + &Attr { + height: 16, + is_tower: false, + rounded: true, + }, + ) }; - let mut mask = keep.draw( - Vec3::from(wall_rpos) + Vec3::unit_z() * wpos.z - wall_alt, - wall_dist, - Vec2::new(border_pos.reduce_max(), border_pos.reduce_min()), - rpos - wall_pos, - wpos.z - wall_alt, - wall_ori, - 4, - 0, - &Attr { - height: 16, - is_tower: false, - rounded: true, - }, - ); for tower in &self.towers { let tower_wpos = Vec3::new( self.origin.x + tower.offset.x, diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs index 0c563e5ad8..4bba7a17f7 100644 --- a/world/src/site/settlement/mod.rs +++ b/world/src/site/settlement/mod.rs @@ -380,7 +380,7 @@ impl Settlement { || ctx .sim .and_then(|sim| sim.get_nearest_path(self.origin + house_pos)) - .map(|(dist, _)| dist < 28.0) + .map(|(dist, _, _, _)| dist < 28.0) .unwrap_or(false) { continue; @@ -590,45 +590,46 @@ impl Settlement { } // Paths - if let Some((WayKind::Path, dist, nearest)) = sample.way { - let inset = -1; + // if let Some((WayKind::Path, dist, nearest)) = sample.way { + // let inset = -1; - // Try to use the column at the centre of the path for sampling to make them - // flatter - let col = get_column(offs + (nearest.floor().map(|e| e as i32) - rpos)) - .unwrap_or(col_sample); - let (bridge_offset, depth) = if let Some(water_dist) = col.water_dist { - ( - ((water_dist.max(0.0) * 0.2).min(f32::consts::PI).cos() + 1.0) * 5.0, - ((1.0 - ((water_dist + 2.0) * 0.3).min(0.0).cos().abs()) - * (col.riverless_alt + 5.0 - col.alt).max(0.0) - * 1.75 - + 3.0) as i32, - ) - } else { - (0.0, 3) - }; - let surface_z = (col.riverless_alt + bridge_offset).floor() as i32; + // // Try to use the column at the centre of the path for sampling to make them + // // flatter + // let col = get_column(offs + (nearest.floor().map(|e| e as i32) - rpos)) + // .unwrap_or(col_sample); + // let (bridge_offset, depth) = if let Some(water_dist) = col.water_dist { + // ( + // ((water_dist.max(0.0) * 0.2).min(f32::consts::PI).cos() + 1.0) * 5.0, + // ((1.0 - ((water_dist + 2.0) * 0.3).min(0.0).cos().abs()) + // * (col.riverless_alt + 5.0 - col.alt).max(0.0) + // * 1.75 + // + 3.0) as i32, + // ) + // } else { + // (0.0, 3) + // }; + // let surface_z = (col.riverless_alt + bridge_offset).floor() as i32; - for z in inset - depth..inset { - let _ = vol.set( - Vec3::new(offs.x, offs.y, surface_z + z), - if bridge_offset >= 2.0 && dist >= 3.0 || z < inset - 1 { - Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 80, 100), 8)) - } else { - Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 50, 30), 8)) - }, - ); - } - let head_space = (8 - (dist * 0.25).powf(6.0).round() as i32).max(1); - for z in inset..inset + head_space { - let pos = Vec3::new(offs.x, offs.y, surface_z + z); - if vol.get(pos).unwrap().kind() != BlockKind::Water { - let _ = vol.set(pos, Block::empty()); - } - } - // Ground colour - } else { + // for z in inset - depth..inset { + // let _ = vol.set( + // Vec3::new(offs.x, offs.y, surface_z + z), + // if bridge_offset >= 2.0 && dist >= 3.0 || z < inset - 1 { + // Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 80, 100), 8)) + // } else { + // Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 50, 30), 8)) + // }, + // ); + // } + // let head_space = (8 - (dist * 0.25).powf(6.0).round() as i32).max(1); + // for z in inset..inset + head_space { + // let pos = Vec3::new(offs.x, offs.y, surface_z + z); + // if vol.get(pos).unwrap().kind() != BlockKind::Water { + // let _ = vol.set(pos, Block::empty()); + // } + // } + // // Ground colour + // } else + { let mut surface_block = None; let roll = @@ -639,7 +640,7 @@ impl Settlement { Some(Plot::Grass) => Some(Rgb::new(100, 200, 0)), Some(Plot::Water) => Some(Rgb::new(100, 150, 250)), Some(Plot::Town { district }) => { - if let Some((_, path_nearest)) = col_sample.path { + if let Some((_, path_nearest, _, _)) = col_sample.path { let path_dir = (path_nearest - wpos2d.map(|e| e as f32)) .rotated_z(f32::consts::PI / 2.0) .normalized(); @@ -651,8 +652,8 @@ impl Settlement { / 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 && col_sample.path.map(|(dist, _)| dist > 20.0).unwrap_or(true)) + 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 && col_sample.path.map(|(dist, _, _, _)| dist > 20.0).unwrap_or(true)) { surface_block = Some(Block::new(BlockKind::StreetLamp, Rgb::white()));