Tweak world generation ; Fix discrepancies
3fbe1db79f1256c75234e61cb163be63a783beb2
This commit is contained in:
parent
208a7f6a63
commit
0c16637901
2 changed files with 124 additions and 115 deletions
|
@ -80,13 +80,6 @@ pub trait RepeatNum {
|
||||||
}
|
}
|
||||||
impl RepeatNum for f32 {
|
impl RepeatNum for f32 {
|
||||||
fn repeat(self, length: f32) -> f32 {
|
fn repeat(self, length: f32) -> f32 {
|
||||||
let mut val = self;
|
f32::clamp(self - (self / length).floor() * length, 0.0, length)
|
||||||
while val < 0.0 {
|
|
||||||
val += length;
|
|
||||||
}
|
|
||||||
while val >= length {
|
|
||||||
val -= length;
|
|
||||||
}
|
|
||||||
val
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ impl World {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const NUM_CONTINENTS: u8 = 3;
|
pub const NUM_CONTINENTS: u8 = 7;
|
||||||
pub const CONTINENT_FACTOR: f32 = 0.7;
|
pub const CONTINENT_FACTOR: f32 = 0.7;
|
||||||
pub const CONTINENT_WIDTH_FACTOR: f32 = 5.0;
|
pub const CONTINENT_WIDTH_FACTOR: f32 = 5.0;
|
||||||
|
|
||||||
|
@ -77,7 +77,8 @@ impl World {
|
||||||
pub const MAX_ALTITUDE: f32 = 10000.0;
|
pub const MAX_ALTITUDE: f32 = 10000.0;
|
||||||
pub const ALTITUDE_SPAN: f32 = Self::MAX_ALTITUDE - Self::MIN_ALTITUDE;
|
pub const ALTITUDE_SPAN: f32 = Self::MAX_ALTITUDE - Self::MIN_ALTITUDE;
|
||||||
|
|
||||||
pub const MOUNTAIN_RANGE_WIDTH_FACTOR: f32 = 15.0;
|
pub const MOUNTAIN_RANGE_MIX_FACTOR: f32 = 0.075;
|
||||||
|
pub const MOUNTAIN_RANGE_WIDTH_FACTOR: f32 = 25.0;
|
||||||
|
|
||||||
pub const TERRAIN_NOISE_FACTOR_1: f32 = 0.2;
|
pub const TERRAIN_NOISE_FACTOR_1: f32 = 0.2;
|
||||||
pub const TERRAIN_NOISE_FACTOR_2: f32 = 0.15;
|
pub const TERRAIN_NOISE_FACTOR_2: f32 = 0.15;
|
||||||
|
@ -92,28 +93,71 @@ impl World {
|
||||||
if let Err(err) = self.generate_altitude() {
|
if let Err(err) = self.generate_altitude() {
|
||||||
return Err(WorldGenError::CartesianError(err));
|
return Err(WorldGenError::CartesianError(err));
|
||||||
}
|
}
|
||||||
if let Err(err) = self.generate_rainfall() {
|
// if let Err(err) = self.generate_rainfall() {
|
||||||
return Err(WorldGenError::CartesianError(err));
|
// return Err(WorldGenError::CartesianError(err));
|
||||||
}
|
// }
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_continents(&mut self) {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let width = self.width as f32;
|
||||||
|
let height = self.height as f32;
|
||||||
|
for (idx, continent_offset) in self.contintent_offsets.iter_mut().enumerate() {
|
||||||
|
continent_offset.x = rng
|
||||||
|
.gen_range(width * idx as f32 * 2.0 / 5.0..width * (idx as f32 + 2.0) * 2.0 / 5.0)
|
||||||
|
.repeat(width);
|
||||||
|
continent_offset.y = rng.gen_range(height * 1.0 / 6.0..height * 5.0 / 6.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn continent_modifier(&self, x: usize, y: usize) -> f32 {
|
||||||
|
let x = x as f32;
|
||||||
|
let y = y as f32;
|
||||||
|
let width = self.width as f32;
|
||||||
|
let height = self.height as f32;
|
||||||
|
|
||||||
|
let mut max_value = 0.0;
|
||||||
|
let beta_factor = f32::sin(PI * y / height);
|
||||||
|
|
||||||
|
for Vec2 {
|
||||||
|
x: continent_x,
|
||||||
|
y: contintent_y,
|
||||||
|
} in self.contintent_offsets
|
||||||
|
{
|
||||||
|
let distance_x = f32::min(
|
||||||
|
f32::min((continent_x - x).abs(), (width + continent_x - x).abs()),
|
||||||
|
(continent_x - x - width).abs(),
|
||||||
|
) * beta_factor;
|
||||||
|
|
||||||
|
let distance_y = f32::abs(contintent_y - y);
|
||||||
|
|
||||||
|
let distance = (distance_x * distance_x + distance_y * distance_y).sqrt();
|
||||||
|
|
||||||
|
let value = f32::max(0.0, 1.0 - Self::CONTINENT_WIDTH_FACTOR * distance / width);
|
||||||
|
|
||||||
|
max_value = f32::max(max_value, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
max_value
|
||||||
|
}
|
||||||
|
|
||||||
fn generate_altitude(&mut self) -> Result<(), CartesianError> {
|
fn generate_altitude(&mut self) -> Result<(), CartesianError> {
|
||||||
self.generate_continents();
|
self.generate_continents();
|
||||||
|
|
||||||
let offset_1 = Self::random_offset_vector();
|
|
||||||
let offset_2 = Self::random_offset_vector();
|
|
||||||
let offset_3 = Self::random_offset_vector();
|
|
||||||
let offset_4 = Self::random_offset_vector();
|
|
||||||
let offset_5 = Self::random_offset_vector();
|
|
||||||
|
|
||||||
const RADIUS_1: f32 = 0.5;
|
const RADIUS_1: f32 = 0.5;
|
||||||
const RADIUS_2: f32 = 4.0;
|
const RADIUS_2: f32 = 4.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;
|
||||||
|
|
||||||
|
let offset_1 = Self::random_offset_vector();
|
||||||
|
let offset_2 = Self::random_offset_vector();
|
||||||
|
let offset_3 = Self::random_offset_vector();
|
||||||
|
let offset_4 = Self::random_offset_vector();
|
||||||
|
let offset_5 = Self::random_offset_vector();
|
||||||
|
|
||||||
for y in 0..self.terrain.len() {
|
for y in 0..self.terrain.len() {
|
||||||
let alpha = (y as f32 / self.height as f32) * PI;
|
let alpha = (y as f32 / self.height as f32) * PI;
|
||||||
|
|
||||||
|
@ -133,113 +177,27 @@ impl World {
|
||||||
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 mut raw_altitude = self
|
let value = self.mountain_range_noise_from_random_noise(mix_values(
|
||||||
.random_mountain_noise_from_random_noise(mix_values(value_1, value_2, 0.1))
|
value_1,
|
||||||
* mix_values(1.0, continent_value, 0.3);
|
value_2,
|
||||||
|
Self::MOUNTAIN_RANGE_MIX_FACTOR,
|
||||||
|
)) * mix_values(1.0, continent_value, 0.3);
|
||||||
|
|
||||||
raw_altitude = mix_values(raw_altitude, continent_value, Self::CONTINENT_FACTOR);
|
let value = mix_values(value, continent_value, Self::CONTINENT_FACTOR);
|
||||||
raw_altitude = mix_values(raw_altitude, value_3, Self::TERRAIN_NOISE_FACTOR_1);
|
let value = mix_values(value, value_3, Self::TERRAIN_NOISE_FACTOR_1);
|
||||||
raw_altitude *= mix_values(1.0, value_4, Self::TERRAIN_NOISE_FACTOR_2);
|
let value = value * mix_values(1.0, value_4, Self::TERRAIN_NOISE_FACTOR_2);
|
||||||
raw_altitude *= mix_values(1.0, value_5, Self::TERRAIN_NOISE_FACTOR_3);
|
let value = value * mix_values(1.0, value_5, Self::TERRAIN_NOISE_FACTOR_3);
|
||||||
|
|
||||||
self.terrain[y][x].altitude = Self::calculate_altitude(raw_altitude);
|
self.terrain[y][x].altitude = Self::calculate_altitude(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_altitude(raw_altitude: f32) -> f32 {
|
|
||||||
Self::MIN_ALTITUDE + (raw_altitude * Self::ALTITUDE_SPAN)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_rainfall(&mut self) -> Result<(), CartesianError> {
|
|
||||||
let offset = Self::random_offset_vector();
|
|
||||||
const RADIUS: f32 = 2.0;
|
|
||||||
|
|
||||||
for (y, row) in self.terrain.iter_mut().enumerate() {
|
|
||||||
let alpha = (y as f32 / self.height as f32) * PI;
|
|
||||||
for (x, cell) in row.iter_mut().enumerate() {
|
|
||||||
let beta = (x as f32 / self.width as f32) * TAU;
|
|
||||||
|
|
||||||
let pos = cartesian_coordinates(alpha, beta, RADIUS)? + offset;
|
|
||||||
|
|
||||||
let value = self.perlin.get([pos.x.into(), pos.y.into(), pos.z.into()]) as f32;
|
|
||||||
|
|
||||||
let base_rainfall = (value * Self::RAINFALL_SPAN + Self::MIN_RAINFALL)
|
|
||||||
.clamp(0.0, World::MAX_RAINFALL);
|
|
||||||
let altitude_factor = ((cell.altitude / Self::MAX_ALTITUDE)
|
|
||||||
* World::RAINFALL_ALTITUDE_FACTOR)
|
|
||||||
.clamp(0.0, 1.0);
|
|
||||||
let rainfall = base_rainfall * (1.0 - altitude_factor);
|
|
||||||
|
|
||||||
cell.rainfall = rainfall;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_continents(&mut self) {
|
|
||||||
let mut rng = rand::thread_rng();
|
|
||||||
for (idx, continent_offset) in self.contintent_offsets.iter_mut().enumerate() {
|
|
||||||
continent_offset.x = rng
|
|
||||||
.gen_range(
|
|
||||||
self.width as f32 * idx as f32 * 2.0 / 5.0
|
|
||||||
..self.width as f32 * (idx as f32 + 2.0) * 2.0 / 5.0,
|
|
||||||
)
|
|
||||||
.repeat(self.width as f32);
|
|
||||||
continent_offset.y =
|
|
||||||
rng.gen_range(self.height as f32 * 2.0 / 7.0..self.height as f32 * 5.0 / 7.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn continent_modifier(&self, x: usize, y: usize) -> f32 {
|
|
||||||
let x = x as f32;
|
|
||||||
let y = y as f32;
|
|
||||||
let width = self.width as f32;
|
|
||||||
let height = self.height as f32;
|
|
||||||
|
|
||||||
let mut max_value = 0.0;
|
|
||||||
let beta_factor =
|
|
||||||
Self::CONTINENT_WIDTH_FACTOR * width / 1.5 * (1.0 - f32::sin(PI * y / height));
|
|
||||||
|
|
||||||
for Vec2 {
|
|
||||||
x: continent_x,
|
|
||||||
y: contintent_y,
|
|
||||||
} in self.contintent_offsets
|
|
||||||
{
|
|
||||||
let distance_x = f32::min(
|
|
||||||
f32::min(f32::abs(continent_x - x), f32::abs(width + continent_x - x)),
|
|
||||||
continent_x - x - width,
|
|
||||||
);
|
|
||||||
let distance_y = 2.0 * f32::abs(contintent_y - y);
|
|
||||||
|
|
||||||
let distance = (distance_x * distance_x + distance_y * distance_y).sqrt();
|
|
||||||
|
|
||||||
max_value = f32::max(
|
|
||||||
max_value,
|
|
||||||
f32::max(
|
|
||||||
0.0,
|
|
||||||
1.0 - Self::CONTINENT_WIDTH_FACTOR * distance / (width + beta_factor),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
max_value
|
|
||||||
}
|
|
||||||
|
|
||||||
fn random_offset_vector() -> Vec3A {
|
fn random_offset_vector() -> Vec3A {
|
||||||
random_point_in_sphere(1000.0)
|
random_point_in_sphere(1000.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_mountain_noise_from_random_noise(&self, noise: f32) -> f32 {
|
|
||||||
let noise = noise * 2.0 - 1.0;
|
|
||||||
|
|
||||||
let value_1 = -(-(noise * Self::MOUNTAIN_RANGE_WIDTH_FACTOR + 1.0).powf(2.0)).exp();
|
|
||||||
let value_2 = (-(noise * Self::MOUNTAIN_RANGE_WIDTH_FACTOR - 1.0).powf(2.0)).exp();
|
|
||||||
|
|
||||||
(value_1 + value_2 + 1.0) / 2.0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn random_noise_from_polar_coordinates(
|
fn random_noise_from_polar_coordinates(
|
||||||
&self,
|
&self,
|
||||||
alpha: f32,
|
alpha: f32,
|
||||||
|
@ -252,4 +210,62 @@ impl World {
|
||||||
.perlin
|
.perlin
|
||||||
.get([offset.x as f64, offset.y as f64, offset.z as f64]) as f32)
|
.get([offset.x as f64, offset.y as f64, offset.z as f64]) as f32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mountain_range_noise_from_random_noise(&self, noise: f32) -> f32 {
|
||||||
|
let noise = noise * 2.0 - 1.0;
|
||||||
|
|
||||||
|
let value_1 = -f32::exp(-f32::powi(
|
||||||
|
noise * Self::MOUNTAIN_RANGE_WIDTH_FACTOR + 1.0,
|
||||||
|
2,
|
||||||
|
));
|
||||||
|
let value_2 = f32::exp(-f32::powi(
|
||||||
|
noise * Self::MOUNTAIN_RANGE_WIDTH_FACTOR - 1.0,
|
||||||
|
2,
|
||||||
|
));
|
||||||
|
let value_3 = -f32::exp(-f32::powi(
|
||||||
|
noise * Self::MOUNTAIN_RANGE_WIDTH_FACTOR + Self::MOUNTAIN_RANGE_WIDTH_FACTOR / 2.0,
|
||||||
|
2,
|
||||||
|
));
|
||||||
|
let value_4 = f32::exp(-f32::powi(
|
||||||
|
noise * Self::MOUNTAIN_RANGE_WIDTH_FACTOR - Self::MOUNTAIN_RANGE_WIDTH_FACTOR / 2.0,
|
||||||
|
2,
|
||||||
|
));
|
||||||
|
|
||||||
|
(value_1 + value_2 + value_3 + value_4 + 1.0) / 2.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_altitude(raw_altitude: f32) -> f32 {
|
||||||
|
Self::MIN_ALTITUDE + (raw_altitude * Self::ALTITUDE_SPAN)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_rainfall(&mut self) -> Result<(), CartesianError> {
|
||||||
|
let offset = Self::random_offset_vector();
|
||||||
|
const RADIUS: f32 = 2.0;
|
||||||
|
|
||||||
|
for y in 0..self.terrain.len() {
|
||||||
|
let alpha = (y as f32 / self.height as f32) * PI;
|
||||||
|
for x in 0..self.terrain[y].len() {
|
||||||
|
let mut cell = self.terrain[y][x];
|
||||||
|
let beta = (x as f32 / self.width as f32) * TAU;
|
||||||
|
|
||||||
|
let base_rainfall = Self::calculate_rainfall(
|
||||||
|
self.random_noise_from_polar_coordinates(alpha, beta, RADIUS, offset)?,
|
||||||
|
);
|
||||||
|
let altitude_factor = f32::clamp(
|
||||||
|
cell.altitude / Self::MAX_ALTITUDE * Self::RAINFALL_ALTITUDE_FACTOR,
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
);
|
||||||
|
let rainfall = base_rainfall * (1.0 - altitude_factor);
|
||||||
|
|
||||||
|
cell.rainfall = rainfall;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_rainfall(raw_rainfall: f32) -> f32 {
|
||||||
|
((raw_rainfall * Self::RAINFALL_ALTITUDE_FACTOR) + Self::MIN_RAINFALL)
|
||||||
|
.clamp(0.0, Self::MAX_RAINFALL)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue