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.
This commit is contained in:
Joshua Yanovski 2019-08-26 11:49:14 +02:00
parent 61546239a8
commit c02f2a7f9e
5 changed files with 79 additions and 8 deletions

View File

@ -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) {

View File

@ -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 <playername> : Temporarily gives a player admin permissions or removes them",
true,
handle_adminify,
)
),
ChatCommand::new(
"debug_column",
"{} {}",
"/debug_column <x> <y> : 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)));
}
}

View File

@ -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
{

View File

@ -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,

View File

@ -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