From c02f2a7f9e647e721d16d94f71e78b4f8f827625 Mon Sep 17 00:00:00 2001 From: Joshua Yanovski Date: Mon, 26 Aug 2019 11:49:14 +0200 Subject: [PATCH] Fixes to worldgen and adding a debug command. Humidity and temperature are now indexed to uniform altitude *over land chunks* (and water chunks adjacent to land) rather than over the whole range of altitude. This is necessary in order to satisfy the uniformity conditions of the formula for weighted sum CDF. Additionally, fixes the computation of whether a tree should be generated or not. Previously, it was using a source of randomness scaled to use much less than the full 0-1 range; this has been resolved. This makes for much nicer and more gradual transitions between densities and reduces the amount of completely barren landscapes, while also making forests larger. Finally, this commit adds a server command, debug_column, which returns some useful debug information about a column given an x and y coordinate. This is useful for debugging worldgen. --- client/src/lib.rs | 3 +- server/src/cmd.rs | 58 +++++++++++++++++++++++++++++++++++++- world/src/block/natural.rs | 4 ++- world/src/column/mod.rs | 2 +- world/src/sim/mod.rs | 20 ++++++++++--- 5 files changed, 79 insertions(+), 8 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index 00f64c5025..bb1bbf2873 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -172,7 +172,8 @@ impl Client { pub fn set_view_distance(&mut self, view_distance: u32) { self.view_distance = Some(view_distance.max(1).min(25)); self.postbox - .send_message(ClientMsg::SetViewDistance(self.view_distance.unwrap())); // Can't fail + .send_message(ClientMsg::SetViewDistance(self.view_distance.unwrap())); + // Can't fail } pub fn swap_inventory_slots(&mut self, a: usize, b: usize) { diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 5305b8f223..cbd5b2b99f 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -10,6 +10,8 @@ use common::{ msg::ServerMsg, npc::{get_npc_name, NpcKind}, state::TimeOfDay, + terrain::TerrainChunkSize, + vol::VolSize, }; use rand::Rng; use specs::{Builder, Entity as EcsEntity, Join}; @@ -198,7 +200,14 @@ lazy_static! { "/adminify : Temporarily gives a player admin permissions or removes them", true, handle_adminify, - ) + ), + ChatCommand::new( + "debug_column", + "{} {}", + "/debug_column : Prints some debug information about a column", + false, + handle_debug_column, + ), ]; } @@ -828,3 +837,50 @@ fn handle_tell(server: &mut Server, entity: EcsEntity, args: String, action: &Ch .notify(entity, ServerMsg::private(String::from(action.help_string))); } } + +fn handle_debug_column(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { + let sim = server.world.sim(); + if let Ok((x, y)) = scan_fmt!(&args, action.arg_fmt, i32, i32) { + let wpos = Vec2::new(x, y); + /* let chunk_pos = wpos.map2(Vec2::from(TerrainChunkSize::SIZE), |e, sz: u32| { + e / sz as i32 + }); */ + + let foo = || { + // let sim_chunk = sim.get(chunk_pos)?; + let alt_base = sim.get_interpolated(wpos, |chunk| chunk.alt_base)?; + let alt = sim.get_interpolated(wpos, |chunk| chunk.alt)?; + let chaos = sim.get_interpolated(wpos, |chunk| chunk.chaos)?; + let temp = sim.get_interpolated(wpos, |chunk| chunk.temp)?; + let humidity = sim.get_interpolated(wpos, |chunk| chunk.humidity)?; + let rockiness = sim.get_interpolated(wpos, |chunk| chunk.rockiness)?; + let tree_density = sim.get_interpolated(wpos, |chunk| chunk.tree_density)?; + let spawn_rate = sim.get_interpolated(wpos, |chunk| chunk.spawn_rate)?; + + Some(format!( + r#"wpos: {:?} +alt_base {:?} +alt {:?} +chaos {:?} +temp {:?} +humidity {:?} +rockiness {:?} +tree_density {:?} +spawn_rate {:?} "#, + wpos, alt_base, alt, chaos, temp, humidity, rockiness, tree_density, spawn_rate + )) + }; + if let Some(s) = foo() { + server.clients.notify(entity, ServerMsg::private(s)); + } else { + server.clients.notify( + entity, + ServerMsg::private(String::from("Not a pregenerated chunk.")), + ); + } + } else { + server + .clients + .notify(entity, ServerMsg::private(String::from(action.help_string))); + } +} diff --git a/world/src/block/natural.rs b/world/src/block/natural.rs index 1d12a72cc5..79fde45685 100644 --- a/world/src/block/natural.rs +++ b/world/src/block/natural.rs @@ -8,6 +8,7 @@ use crate::{ use common::{assets, terrain::Structure}; use lazy_static::lazy_static; use std::sync::Arc; +use std::u32; use vek::*; static VOLUME_RAND: RandomPerm = RandomPerm::new(0xDB21C052); @@ -25,7 +26,8 @@ pub fn structure_gen<'a>( let st_sample = &structure_samples[idx].as_ref()?; // Assuming it's a tree... figure out when it SHOULDN'T spawn - if st_sample.tree_density < 0.5 + (st_seed as f32 / 1000.0).fract() * 0.5 + let random_seed = (st_seed as f64) / (u32::MAX as f64); + if (st_sample.tree_density as f64) < random_seed || st_sample.alt < st_sample.water_level || st_sample.spawn_rate < 0.5 { diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index e8d58b7c47..c89575e1e4 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -78,8 +78,8 @@ impl<'a> ColumnGen<'a> { if seed % 5 == 2 && chunk.temp > CONFIG.desert_temp - && chunk.humidity < CONFIG.desert_hum && chunk.alt > CONFIG.sea_level + 5.0 + && chunk.chaos <= 0.35 { Some(StructureData { pos, diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index a259a98309..5be3ae1e69 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -55,6 +55,7 @@ struct GenCdf { alt_base: InverseCdf, chaos: InverseCdf, alt: InverseCdf, + alt_no_seawater: InverseCdf, } pub(crate) struct GenCtx { @@ -223,8 +224,8 @@ impl WorldSim { // are included). let pure_water = |posi| { let pos = uniform_idx_as_vec2(posi); - for x in (pos.x - 1..=pos.x + 1) { - for y in (pos.y - 1..=pos.y + 1) { + for x in pos.x - 1..=pos.x + 1 { + for y in pos.y - 1..=pos.y + 1 { if x >= 0 && y >= 0 && x < WORLD_SIZE.x as i32 && y < WORLD_SIZE.y as i32 { let posi = vec2_as_uniform_idx(Vec2::new(x, y)); if alt[posi].1.mul(CONFIG.mountain_scale) > 0.0 { @@ -236,12 +237,21 @@ impl WorldSim { true }; + // A version of alt that is uniform over *non-seawater* (or land-adjacent seawater) chunks. + let alt_no_seawater = uniform_noise(|posi, wposf| { + if pure_water(posi) { + None + } else { + Some(alt[posi].1) + } + }); + // -1 to 1. let temp_base = uniform_noise(|posi, wposf| { if pure_water(posi) { None } else { - Some((gen_ctx.temp_nz.get((wposf.div(12000.0)).into_array()) as f32)) + Some(gen_ctx.temp_nz.get((wposf.div(12000.0)).into_array()) as f32) } }); @@ -265,6 +275,7 @@ impl WorldSim { alt_base, chaos, alt, + alt_no_seawater, }; let mut chunks = Vec::new(); @@ -530,7 +541,8 @@ impl SimChunk { let map_edge_factor = map_edge_factor(posi); let (_, chaos) = gen_cdf.chaos[posi]; let (humid_uniform, _) = gen_cdf.humid_base[posi]; - let (alt_uniform, alt_pre) = gen_cdf.alt[posi]; + let (_, alt_pre) = gen_cdf.alt[posi]; + let (alt_uniform, _) = gen_cdf.alt_no_seawater[posi]; let (temp_uniform, _) = gen_cdf.temp_base[posi]; // Take the weighted average of our randomly generated base humidity, the scaled