Tweak world generation

3396b4a9e53be34ec2fdcc3bd24034e39937f5e0
2e566dc9ad45b80e4c94031b5be78ca6703cba45
66ce82b31d0a61a31a96ba909332069606b700ad
4f133213e787fd7d725ed8147376814b05e9694a
a282ae965bf83736f48f47cddc2fc8a799455fda
e28eb81541730277e437003005707728669ea859
4947b7be051971432f5569bfbf4e5414f7cd3532
This commit is contained in:
Tobias Berger 2022-11-19 09:46:08 +01:00
parent 7d3e97da5a
commit cb935618b0
Signed by: toby
GPG key ID: 2D05EFAB764D6A88
3 changed files with 163 additions and 109 deletions

View file

@ -57,8 +57,8 @@ impl From<BiomeType> for BiomeStats {
color: Color::rgb_u8(167, 177, 84), color: Color::rgb_u8(167, 177, 84),
min_altitude: 0.0, min_altitude: 0.0,
max_altitude: World::MAX_ALTITUDE, max_altitude: World::MAX_ALTITUDE,
min_rainfall: 25.0, min_rainfall: 15.0,
max_rainfall: 1475.0, max_rainfall: 1575.0,
min_temperature: -5.0, min_temperature: -5.0,
max_temperature: World::MAX_TEMPERATURE, max_temperature: World::MAX_TEMPERATURE,
}, },
@ -68,8 +68,8 @@ impl From<BiomeType> for BiomeStats {
color: Color::rgb_u8(76, 132, 55), color: Color::rgb_u8(76, 132, 55),
min_altitude: 0.0, min_altitude: 0.0,
max_altitude: World::MAX_ALTITUDE, max_altitude: World::MAX_ALTITUDE,
min_rainfall: 975.0, min_rainfall: 1375.0,
max_rainfall: 2475.0, max_rainfall: 2975.0,
min_temperature: -5.0, min_temperature: -5.0,
max_temperature: World::MAX_TEMPERATURE, max_temperature: World::MAX_TEMPERATURE,
}, },
@ -102,7 +102,7 @@ impl From<BiomeType> for BiomeStats {
min_altitude: 0.0, min_altitude: 0.0,
max_altitude: World::MAX_ALTITUDE, max_altitude: World::MAX_ALTITUDE,
min_rainfall: World::MIN_RAINFALL, min_rainfall: World::MIN_RAINFALL,
max_rainfall: 125.0, max_rainfall: 275.0,
min_temperature: -5.0, min_temperature: -5.0,
max_temperature: World::MAX_TEMPERATURE, max_temperature: World::MAX_TEMPERATURE,
}, },
@ -112,7 +112,7 @@ impl From<BiomeType> for BiomeStats {
color: Color::rgb_u8(59, 103, 43), color: Color::rgb_u8(59, 103, 43),
min_altitude: 0.0, min_altitude: 0.0,
max_altitude: World::MAX_ALTITUDE, max_altitude: World::MAX_ALTITUDE,
min_rainfall: 1975.0, min_rainfall: 1775.0,
max_rainfall: World::MAX_RAINFALL, max_rainfall: World::MAX_RAINFALL,
min_temperature: -5.0, min_temperature: -5.0,
max_temperature: World::MAX_TEMPERATURE, max_temperature: World::MAX_TEMPERATURE,

View file

@ -107,26 +107,20 @@ pub struct TerrainCell {
impl World { impl World {
pub const ALTITUDE_SPAN: f32 = World::MAX_ALTITUDE - World::MIN_ALTITUDE; pub const ALTITUDE_SPAN: f32 = World::MAX_ALTITUDE - World::MIN_ALTITUDE;
pub const CONTINENT_MAX_SIZE_FACTOR: f32 = 6.0; pub const CONTINENT_MAX_SIZE_FACTOR: f32 = 8.0;
pub const CONTINENT_MIN_SIZE_FACTOR: f32 = 2.5; pub const CONTINENT_MIN_SIZE_FACTOR: f32 = 6.0;
pub const MAX_ALTITUDE: f32 = 15000.0; pub const MAX_ALTITUDE: f32 = 15000.0;
pub const MAX_RAINFALL: f32 = 7500.0; pub const MAX_RAINFALL: f32 = 13000.0;
pub const MAX_TEMPERATURE: f32 = 30.0; pub const MAX_TEMPERATURE: f32 = 30.0;
pub const MIN_ALTITUDE: f32 = -15000.0; pub const MIN_ALTITUDE: f32 = -15000.0;
pub const MIN_RAINFALL: f32 = 0.0; pub const MIN_RAINFALL: f32 = 0.0;
pub const MIN_TEMPERATURE: f32 = -35.0; pub const MIN_TEMPERATURE: f32 = -35.0;
pub const MOUNTAIN_RANGE_MIX_FACTOR: f32 = 0.075; pub const NUM_CONTINENTS: u8 = 6;
pub const MOUNTAIN_RANGE_WIDTH_FACTOR: f32 = 25.0;
pub const NUM_CONTINENTS: u8 = 7;
pub const RAINFALL_DRYNESS_FACTOR: f32 = 0.005; pub const RAINFALL_DRYNESS_FACTOR: f32 = 0.005;
pub const RAINFALL_DRYNESS_OFFSET: f32 = World::RAINFALL_DRYNESS_FACTOR * World::MAX_RAINFALL; pub const RAINFALL_DRYNESS_OFFSET: f32 = World::RAINFALL_DRYNESS_FACTOR * World::MAX_RAINFALL;
pub const RAINFALL_SPAN: f32 = World::MAX_RAINFALL - World::MIN_RAINFALL; pub const RAINFALL_SPAN: f32 = World::MAX_RAINFALL - World::MIN_RAINFALL;
pub const TEMPERATURE_ALTITUDE_FACTOR: f32 = 1.0; pub const TEMPERATURE_ALTITUDE_FACTOR: f32 = 2.05;
pub const TEMPERATURE_SPAN: f32 = World::MAX_TEMPERATURE - World::MIN_TEMPERATURE; pub const TEMPERATURE_SPAN: f32 = World::MAX_TEMPERATURE - World::MIN_TEMPERATURE;
pub const TERRAIN_NOISE_FACTOR_1: f32 = 0.15;
pub const TERRAIN_NOISE_FACTOR_2: f32 = 0.15;
pub const TERRAIN_NOISE_FACTOR_3: f32 = 0.1;
pub const TERRAIN_NOISE_FACTOR_4: f32 = 2.5;
pub fn new(width: u32, height: u32, seed: u32) -> World { pub fn new(width: u32, height: u32, seed: u32) -> World {
World { World {
@ -199,6 +193,8 @@ impl World {
let width = self.width as f32; let width = self.width as f32;
let height = self.height as f32; let height = self.height as f32;
const LATITUDE_FACTOR: f32 = 6.0;
for i in 0..World::NUM_CONTINENTS { for i in 0..World::NUM_CONTINENTS {
// #[cfg(feature = "logging")] // #[cfg(feature = "logging")]
// info!("Continents: {}/{}", i, World::NUM_CONTINENTS); // info!("Continents: {}/{}", i, World::NUM_CONTINENTS);
@ -207,8 +203,9 @@ impl World {
.rng .rng
.gen_range(width * i as f32 * 2.0 / 5.0..(width * (i as f32 + 2.0) * 2.0 / 5.0)) .gen_range(width * i as f32 * 2.0 / 5.0..(width * (i as f32 + 2.0) * 2.0 / 5.0))
.repeat(width); .repeat(width);
self.continent_offsets[i as usize].y = self.continent_offsets[i as usize].y = self.rng.gen_range(
self.rng.gen_range(height * 1.0 / 6.0..height * 5.0 / 6.0); height * 1.0 / LATITUDE_FACTOR..height * (LATITUDE_FACTOR - 1.0) / LATITUDE_FACTOR,
);
self.continent_sizes[i as usize] = Vec2 { self.continent_sizes[i as usize] = Vec2 {
x: self x: self
@ -216,47 +213,61 @@ impl World {
.gen_range(World::CONTINENT_MIN_SIZE_FACTOR..World::CONTINENT_MAX_SIZE_FACTOR), .gen_range(World::CONTINENT_MIN_SIZE_FACTOR..World::CONTINENT_MAX_SIZE_FACTOR),
y: self y: self
.rng .rng
.gen_range(World::CONTINENT_MIN_SIZE_FACTOR..World::CONTINENT_MAX_SIZE_FACTOR), .gen_range(World::CONTINENT_MIN_SIZE_FACTOR..World::CONTINENT_MAX_SIZE_FACTOR)
/ 2.0,
}; };
} }
info!("Done generating continents"); info!("Done generating continents");
} }
fn continent_modifier(&self, x: usize, y: usize) -> f32 { fn continent_distance(&self, continent_num: u8, x: usize, y: usize) -> f32 {
let x = x as f32; let beta_factor = f32::sin(std::f32::consts::PI * y as f32 / self.height as f32);
let y = y as f32;
let width = self.width as f32;
let height = self.height as f32;
let Vec2 {
x: continent_x,
y: continent_y,
} = self.continent_offsets[usize::from(continent_num)];
let distance_x = f32::min(
f32::abs(continent_x - x as f32),
f32::abs(self.width as f32 + continent_x - x as f32),
);
let distance_x = f32::min(
distance_x,
f32::abs(continent_x - x as f32 - self.width as f32),
);
let distance_x = distance_x * beta_factor;
let distance_y = f32::abs(continent_y - y as f32);
let Vec2 {
x: continent_width,
y: continent_height,
} = self.continent_sizes[usize::from(continent_num)];
f32::sqrt(
f32::powi(distance_x * continent_width, 2)
+ f32::powi(distance_y * continent_height, 2),
)
}
fn continent_modifier(&self, x: usize, y: usize) -> f32 {
let mut max_value = 0.0; let mut max_value = 0.0;
let beta_factor = f32::sin(PI * y / height);
for i in 0..World::NUM_CONTINENTS { for i in 0..World::NUM_CONTINENTS {
let idx = i as usize; let distance = self.continent_distance(i, x, y);
let Vec2 { let value = f32::clamp(1.0 - distance / self.width as f32, 0.0, 1.0);
x: offset_x,
y: offset_y,
} = self.continent_offsets[idx];
let Vec2 {
x: continent_width,
y: continent_height,
} = self.continent_sizes[idx];
let distance_x = f32::min( let mut other_value = value;
f32::min(f32::abs(offset_x - x), f32::abs(width + offset_x - x)),
f32::abs(offset_x - x - width),
) * beta_factor
* continent_width;
let distance_y = f32::abs(offset_y - y) * continent_height;
let distance = f32::sqrt((distance_x * distance_x) + (distance_y * distance_y));
let value = f32::max(0.0, 1.0 - distance / width);
if value > max_value { if value > max_value {
other_value = max_value;
max_value = value; max_value = value;
} }
let value_mod = f32::min(other_value * 2.0, 1.0);
max_value = mix_values(max_value, other_value, value_mod);
} }
max_value max_value
@ -270,16 +281,26 @@ impl World {
self.generate_continents(); self.generate_continents();
const RADIUS_1: f32 = 0.5; const RADIUS_1: f32 = 0.5;
const RADIUS_2: f32 = 4.0; const RADIUS_2: f32 = 8.0;
const RADIUS_3: f32 = 4.0; const RADIUS_3: f32 = 4.0;
const RADIUS_4: f32 = 8.0; const RADIUS_4: f32 = 8.0;
const RADIUS_5: f32 = 16.0; const RADIUS_5: f32 = 16.0;
const RADIUS_6: f32 = 64.0;
const RADIUS_7: f32 = 128.0;
const RADIUS_8: f32 = 1.0;
const RADIUS_9: f32 = 1.0;
let offset_1 = World::random_offset_vector(&mut self.rng); let offset_1 = World::random_offset_vector(&mut self.rng);
let offset_1b = World::random_offset_vector(&mut self.rng);
let offset_2 = World::random_offset_vector(&mut self.rng); let offset_2 = World::random_offset_vector(&mut self.rng);
let offset_2b = World::random_offset_vector(&mut self.rng);
let offset_3 = World::random_offset_vector(&mut self.rng); let offset_3 = World::random_offset_vector(&mut self.rng);
let offset_4 = World::random_offset_vector(&mut self.rng); let offset_4 = World::random_offset_vector(&mut self.rng);
let offset_5 = World::random_offset_vector(&mut self.rng); let offset_5 = World::random_offset_vector(&mut self.rng);
let offset_6 = World::random_offset_vector(&mut self.rng);
let offset_7 = World::random_offset_vector(&mut self.rng);
let offset_8 = World::random_offset_vector(&mut self.rng);
let offset_9 = World::random_offset_vector(&mut self.rng);
let height = self.terrain.len(); let height = self.terrain.len();
for y in 0..height { for y in 0..height {
@ -299,46 +320,62 @@ impl World {
let value_1 = let value_1 =
self.random_noise_from_polar_coordinates(alpha, beta, RADIUS_1, offset_1)?; self.random_noise_from_polar_coordinates(alpha, beta, RADIUS_1, offset_1)?;
let value_1b =
self.random_noise_from_polar_coordinates(alpha, beta, RADIUS_1, offset_1b)?;
let value_2 = let value_2 =
self.random_noise_from_polar_coordinates(alpha, beta, RADIUS_2, offset_2)?; self.random_noise_from_polar_coordinates(alpha, beta, RADIUS_2, offset_2)?;
let value_2b =
self.random_noise_from_polar_coordinates(alpha, beta, RADIUS_2, offset_2b)?;
let value_3 = let value_3 =
self.random_noise_from_polar_coordinates(alpha, beta, RADIUS_3, offset_3)?; self.random_noise_from_polar_coordinates(alpha, beta, RADIUS_3, offset_3)?;
let value_4 = let value_4 =
self.random_noise_from_polar_coordinates(alpha, beta, RADIUS_4, offset_4)?; self.random_noise_from_polar_coordinates(alpha, beta, RADIUS_4, offset_4)?;
let value_5 = let value_5 =
self.random_noise_from_polar_coordinates(alpha, beta, RADIUS_5, offset_5)?; self.random_noise_from_polar_coordinates(alpha, beta, RADIUS_5, offset_5)?;
let value_6 =
self.random_noise_from_polar_coordinates(alpha, beta, RADIUS_6, offset_6)?;
let value_7 =
self.random_noise_from_polar_coordinates(alpha, beta, RADIUS_7, offset_7)?;
let value_8 = self
.random_noise_from_polar_coordinates(alpha, beta, RADIUS_8, offset_8)?
* 1.5
+ 0.25;
let value_9 =
self.random_noise_from_polar_coordinates(alpha, beta, RADIUS_9, offset_9)?;
let value_a = mix_values( let mut value_a = self.continent_modifier(x, y);
self.continent_modifier(x, y), value_a = mix_values(value_a, value_3, 0.22 * value_8);
value_3, value_a = mix_values(value_a, value_4, 0.15 * value_8);
World::TERRAIN_NOISE_FACTOR_1, value_a = mix_values(value_a, value_5, 0.1 * value_8);
) * mix_values(1.0, value_4, World::TERRAIN_NOISE_FACTOR_2) value_a = mix_values(value_a, value_6, 0.03 * value_8);
* mix_values(1.0, value_5, World::TERRAIN_NOISE_FACTOR_3); value_a = mix_values(value_a, value_7, 0.005 * value_8);
let value_b = value_a * 0.04 + 0.48; let mut value_c = mix_values(value_1, value_9, 0.5 * value_8);
value_c = mix_values(value_c, value_2, 0.04 * value_8);
value_c = self.mountain_range_noise_from_random_noise(value_c, 25.0);
let value_c = mix_values( let mut value_cb = mix_values(value_1b, value_9, 0.5 * value_8);
self.mountain_range_noise_from_random_noise(mix_values( value_cb = mix_values(value_cb, value_2b, 0.04 * value_8);
value_1, value_cb = self.mountain_range_noise_from_random_noise(value_cb, 25.0);
value_2,
World::MOUNTAIN_RANGE_MIX_FACTOR, value_c = mix_values(value_c, value_cb, 0.5 * value_8);
)),
value_3, value_c = mix_values(value_c, value_3, 0.35 * value_8);
World::TERRAIN_NOISE_FACTOR_1 * World::TERRAIN_NOISE_FACTOR_4, value_c = mix_values(value_c, value_4, 0.075);
) * mix_values( value_c = mix_values(value_c, value_5, 0.05);
1.0, value_c = mix_values(value_c, value_6, 0.02);
value_4, value_c = mix_values(value_c, value_7, 0.01);
World::TERRAIN_NOISE_FACTOR_2 * World::TERRAIN_NOISE_FACTOR_4,
) * mix_values( let value_b = mix_values(
1.0, value_a,
value_5, value_a * 0.02 + 0.49,
World::TERRAIN_NOISE_FACTOR_3 * World::TERRAIN_NOISE_FACTOR_4, value_a - f32::max(0.0, 2.0 * value_c - 1.0),
); );
let mut value_d = mix_values(value_a, value_c, 0.25); let value_d = mix_values(value_b, value_c, 0.225 * value_8);
value_d = mix_values(value_d, value_c, 0.1);
value_d = mix_values(value_d, value_b, 0.1);
// todo!("Calculate and set altitude");
// This is done in a separate method in the original. But why?
let altitude = World::calculate_altitude(value_d); let altitude = World::calculate_altitude(value_d);
self.terrain[y][x].altitude = altitude; self.terrain[y][x].altitude = altitude;
@ -376,27 +413,13 @@ impl World {
)) ))
} }
fn mountain_range_noise_from_random_noise(&self, noise: f32) -> f32 { fn mountain_range_noise_from_random_noise(&self, noise: f32, width_factor: f32) -> f32 {
let noise = noise * 2.0 - 1.0; let noise = noise * 2.0 - 1.0;
let value_1 = -f32::exp(-f32::powi( let value_1 = -f32::exp(-f32::powi(noise * width_factor + 1.0, 2));
noise * World::MOUNTAIN_RANGE_WIDTH_FACTOR + 1.0, let value_2 = f32::exp(-f32::powi(noise * width_factor - 1.0, 2));
2,
));
let value_2 = f32::exp(-f32::powi(
noise * World::MOUNTAIN_RANGE_WIDTH_FACTOR - 1.0,
2,
));
let value_3 = -f32::exp(-f32::powi(
noise * World::MOUNTAIN_RANGE_WIDTH_FACTOR + World::MOUNTAIN_RANGE_WIDTH_FACTOR / 2.0,
2,
));
let value_4 = f32::exp(-f32::powi(
noise * World::MOUNTAIN_RANGE_WIDTH_FACTOR - World::MOUNTAIN_RANGE_WIDTH_FACTOR / 2.0,
2,
));
(value_1 + value_2 + value_3 + value_4 + 1.0) / 2.0 (value_1 + value_2 + 1.0) / 2.0
} }
fn calculate_altitude(raw_altitude: f32) -> f32 { fn calculate_altitude(raw_altitude: f32) -> f32 {
@ -408,8 +431,10 @@ impl World {
progress_sender: &Sender<(f32, String)>, progress_sender: &Sender<(f32, String)>,
) -> Result<(), CartesianError> { ) -> Result<(), CartesianError> {
info!("Generating rainfall"); info!("Generating rainfall");
const RADIUS: f32 = 2.0; const RADIUS_1: f32 = 2.0;
let offset = World::random_offset_vector(&mut self.rng); const RADIUS_2: f32 = 1.0;
let offset_1 = World::random_offset_vector(&mut self.rng);
let offset_2 = World::random_offset_vector(&mut self.rng);
let height = self.terrain.len(); let height = self.terrain.len();
for y in 0..height { for y in 0..height {
@ -426,35 +451,66 @@ impl World {
); );
let beta = (x as f32 / self.width as f32) * TAU; let beta = (x as f32 / self.width as f32) * TAU;
let random_noise = let random_noise_1 =
self.random_noise_from_polar_coordinates(alpha, beta, RADIUS, offset)?; self.random_noise_from_polar_coordinates(alpha, beta, RADIUS_1, offset_1)?;
let random_noise_2 = self
.random_noise_from_polar_coordinates(alpha, beta, RADIUS_2, offset_2)?
* 1.5
+ 0.25;
let latitude_factor = alpha + (random_noise * 2.0 - 1.0) * PI * 0.1; let latitude_factor = alpha + (random_noise_1 * 2.0 - 1.0) * PI * 0.2;
let latitude_modifier_1 = (1.5 * f32::sin(latitude_factor)) - 0.5; let latitude_modifier_1 = (1.5 * f32::sin(latitude_factor)) - 0.5;
let latitude_modifier_2 = f32::cos(latitude_factor); let latitude_modifier_2 = f32::cos(latitude_factor);
let offset_cell_x = let offset_cell_x_1 =
(width + x + f32::floor(latitude_modifier_2 * width as f32 / 20.0) as usize) (width + x + f32::floor(latitude_modifier_2 * width as f32 / 20.0) as usize)
% width; % width;
let offset_cell_2_x = let offset_cell_x_2 =
(width + x + f32::floor(latitude_modifier_2 * width as f32 / 15.0) as usize)
% width;
let offset_cell_x_3 =
(width + x + f32::floor(latitude_modifier_2 * width as f32 / 10.0) as usize) (width + x + f32::floor(latitude_modifier_2 * width as f32 / 10.0) as usize)
% width; % width;
let offset_cell_x_4 =
(width + x + f32::floor(latitude_modifier_2 * width as f32 / 5.0) as usize)
% width;
let offset_cell_y =
y + f32::floor(latitude_modifier_2 * height as f32 / 10.0) as usize;
let offset_cell = &self.terrain[y][offset_cell_x]; let offset_cell = &self.terrain[y][offset_cell_x_1];
let offset_altitude = f32::max(0.0, offset_cell.altitude); let offset_altitude = f32::max(0.0, offset_cell.altitude);
let offset_cell_2 = &self.terrain[y][offset_cell_2_x]; let offset_cell_2 = &self.terrain[y][offset_cell_x_2];
let offset_altitude_2 = f32::max(0.0, offset_cell_2.altitude); let offset_altitude_2 = f32::max(0.0, offset_cell_2.altitude);
let offset_cell_3 = &self.terrain[y][offset_cell_x_3];
let offset_altitude_3 = f32::max(0.0, offset_cell_3.altitude);
let offset_cell_4 = &self.terrain[y][offset_cell_x_4];
let offset_altitude_4 = f32::max(0.0, offset_cell_4.altitude);
let offset_cell_5 = &self.terrain[offset_cell_y][x];
let offset_altitude_5 = f32::max(0.0, offset_cell_5.altitude);
let cell = &mut self.terrain[y][x]; let cell = &mut self.terrain[y][x];
let altitude_value = f32::max(0.0, cell.altitude); let altitude_value = f32::max(0.0, cell.altitude);
let altitude_modifier = let altitude_modifier = (altitude_value
(altitude_value - (offset_altitude * 1.5) - (offset_altitude_2)) - (offset_altitude * 0.7)
/ World::MAX_ALTITUDE; - (offset_altitude_2 * 0.6)
- (offset_altitude_3 * 0.5)
- (offset_altitude_4 * 0.4)
- (offset_altitude_5 * 0.5)
+ (World::MAX_ALTITUDE * 0.18 * random_noise_2)
- (altitude_value * 0.25))
/ World::MAX_ALTITUDE;
let rainfall_value = mix_values(latitude_modifier_1, altitude_modifier, 0.63); let mut rainfall_value = mix_values(latitude_modifier_1, altitude_modifier, 0.85);
let rainfall_value = mix_values(rainfall_value.powi(2), rainfall_value, 0.85); rainfall_value = mix_values(
rainfall_value.powi(2).copysign(rainfall_value),
rainfall_value,
0.75,
);
let rainfall = f32::min( let rainfall = f32::min(
World::MAX_RAINFALL, World::MAX_RAINFALL,
World::calculate_rainfall(rainfall_value), World::calculate_rainfall(rainfall_value),

View file

@ -155,14 +155,12 @@ impl WorldManager {
WorldManager::NEW_WORLD_HEIGHT, WorldManager::NEW_WORLD_HEIGHT,
seed, seed,
); );
if let Err(_) = if let Err(_) = progress_sender.try_send((0.0, String::from("Generating new world...")))
progress_sender.try_send((0.0, String::from("Generating new world...")))
{ {
// Quietly ignore. It's not critical and logging is slow. // Quietly ignore. It's not critical and logging is slow.
} }
let result = new_world.generate(&progress_sender); let result = new_world.generate(&progress_sender);
if let Err(_) = if let Err(_) = progress_sender.try_send((1.0, String::from("Done generating world!")))
progress_sender.try_send((1.0, String::from("Done generating world!")))
{ {
// Quietly ignore. See above // Quietly ignore. See above
} }