Tweak some more, idk what else to say, not like I understand this.

Population is next

7d5d16bde5fd3e50bb6e5faf934db32415ef6626
149fbe66d2903c9ad4005977973ee53583b97430
a979a07249a0b0eed797ab365bc88cd7c74672e1
8a81ec053123cc2e6fd69b4b7bae6d7b39c75e63
This commit is contained in:
Tobias Berger 2022-11-20 19:47:23 +01:00
parent cb935618b0
commit af05c1b761
Signed by: toby
GPG key ID: 2D05EFAB764D6A88
9 changed files with 236 additions and 74 deletions

View file

@ -0,0 +1,7 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct HumanGroup {
pub id: u32,
pub population: u32,
}

View file

@ -1,3 +1,4 @@
pub mod human_group;
pub mod world;
pub use world::{TerrainCell, World, WorldGenError};
pub mod biome;

View file

@ -1,4 +1,17 @@
const PERMUTATION: [u8; 256] = [
const PERMUTATION: [u8; 512] = [
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69,
142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219,
203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175,
74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230,
220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76,
132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173,
186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206,
59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163,
70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232,
178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162,
241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204,
176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141,
128, 195, 78, 66, 215, 61, 156, 180, // Duplicated
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69,
142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219,
203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175,
@ -15,9 +28,28 @@ const PERMUTATION: [u8; 256] = [
];
#[must_use]
pub fn perlin_value(x: f32, y: f32, z: f32) -> f32 {
let p = [PERMUTATION, PERMUTATION].concat();
pub fn permutation_value(x: f32, y: f32, z: f32) -> u32 {
let x = (f32::floor(x) as i32 & 255) as usize;
let y = (f32::floor(y) as i32 & 255) as usize;
let z = (f32::floor(z) as i32 & 255) as usize;
let a = PERMUTATION[x] as usize + y;
let aa = PERMUTATION[a] as usize + z;
let ab = PERMUTATION[aa] as u32;
let b = PERMUTATION[x + 1] as usize + y;
let ba = PERMUTATION[b] as usize + z;
let bb = PERMUTATION[ba] as u32;
let c = PERMUTATION[x + 2] as usize + y;
let ca = PERMUTATION[c] as usize + z;
let cb = PERMUTATION[ca] as u32;
ab + (bb * 256) + (cb * 256 * 256)
}
#[must_use]
pub fn perlin_value(x: f32, y: f32, z: f32) -> f32 {
let fx: i32 = f32::floor(x) as i32;
let fy: i32 = f32::floor(y) as i32;
let fz: i32 = f32::floor(z) as i32;
@ -34,36 +66,40 @@ pub fn perlin_value(x: f32, y: f32, z: f32) -> f32 {
let v = fade(y);
let w = fade(z);
let a = p[xb] as usize + yb;
let aa = p[a] as usize + zb;
let ab = p[a + 1] as usize + zb;
let a = PERMUTATION[xb] as usize + yb;
let aa = PERMUTATION[a] as usize + zb;
let ab = PERMUTATION[a + 1] as usize + zb;
let b = p[xb + 1] as usize + yb;
let ba = p[b] as usize + zb;
let bb = p[b + 1] as usize + zb;
let b = PERMUTATION[xb + 1] as usize + yb;
let ba = PERMUTATION[b] as usize + zb;
let bb = PERMUTATION[b + 1] as usize + zb;
scale(lerp(
w,
lerp(
v,
lerp(u, grad(p[aa], x, y, z), grad(p[ba], x - 1.0, y, z)),
lerp(
u,
grad(p[ab], x, y - 1.0, z),
grad(p[bb], x - 1.0, y - 1.0, z),
grad(PERMUTATION[aa], x, y, z),
grad(PERMUTATION[ba], x - 1.0, y, z),
),
lerp(
u,
grad(PERMUTATION[ab], x, y - 1.0, z),
grad(PERMUTATION[bb], x - 1.0, y - 1.0, z),
),
),
lerp(
v,
lerp(
u,
grad(p[aa + 1], x, y, z - 1.0),
grad(p[ba + 1], x - 1.0, y, z - 1.0),
grad(PERMUTATION[aa + 1], x, y, z - 1.0),
grad(PERMUTATION[ba + 1], x - 1.0, y, z - 1.0),
),
lerp(
u,
grad(p[ab + 1], x, y - 1.0, z - 1.0),
grad(p[bb + 1], x - 1.0, y - 1.0, z - 1.0),
grad(PERMUTATION[ab + 1], x, y - 1.0, z - 1.0),
grad(PERMUTATION[bb + 1], x - 1.0, y - 1.0, z - 1.0),
),
),
))

2
planet/src/saving/mod.rs Normal file
View file

@ -0,0 +1,2 @@
// pub mod terrain_cell;
pub mod world;

View file

@ -1,5 +1,6 @@
use {
crate::{TerrainCell, World},
bevy::prelude::debug,
rand::{rngs::StdRng, SeedableRng},
serde::{
de::{Error, MapAccess, SeqAccess, Visitor},
@ -43,6 +44,7 @@ impl<'de> Deserialize<'de> for World {
Terrain,
ContinentOffsets,
ContinentSizes,
Iteration,
}
struct WorldVisitor;
@ -64,23 +66,29 @@ impl<'de> Deserialize<'de> for World {
let height = seq
.next_element()?
.ok_or_else(|| Error::invalid_length(0, &self))?;
.ok_or_else(|| Error::invalid_length(1, &self))?;
let seed = seq
.next_element()?
.ok_or_else(|| Error::invalid_length(0, &self))?;
.ok_or_else(|| Error::invalid_length(2, &self))?;
let terrain: Vec<Vec<TerrainCell>> = seq
.next_element()?
.ok_or_else(|| Error::invalid_length(0, &self))?;
.ok_or_else(|| Error::invalid_length(3, &self))?;
let continent_offsets = seq
.next_element()?
.ok_or_else(|| Error::invalid_length(0, &self))?;
.ok_or_else(|| Error::invalid_length(4, &self))?;
let continent_widths = seq
let continent_sizes = seq
.next_element()?
.ok_or_else(|| Error::invalid_length(0, &self))?;
.ok_or_else(|| Error::invalid_length(5, &self))?;
debug!("Iteration aaaaa");
let iteration = seq
.next_element()?
.ok_or_else(|| Error::invalid_length(6, &self))?;
debug!("Iteration bbbbb");
let world_attributes = &mut WorldTerrainAttributes::default();
let world_attributes =
@ -111,13 +119,14 @@ impl<'de> Deserialize<'de> for World {
attributes
});
Ok(World {
debug!("Constructing world");
let mut world = World {
width,
height,
seed,
terrain,
continent_offsets,
continent_sizes: continent_widths,
continent_sizes,
max_altitude: world_attributes.max_altitude,
min_altitude: world_attributes.min_altitude,
@ -127,7 +136,22 @@ impl<'de> Deserialize<'de> for World {
min_temperature: world_attributes.min_temperature,
rng: StdRng::seed_from_u64(seed as u64),
})
iteration,
};
{
let mut y = 0;
debug!("Completing terrain");
for terrain_row in world.terrain.iter_mut() {
let mut x = 0;
for terrain_cell in terrain_row.iter_mut() {
terrain_cell.x = x;
terrain_cell.y = y;
x += 1;
}
y += 1;
}
}
Ok(world)
}
fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
@ -140,6 +164,7 @@ impl<'de> Deserialize<'de> for World {
let mut terrain = None;
let mut continent_offsets = None;
let mut continent_widths = None;
let mut iteration = None;
while let Some(key) = map.next_key()? {
match key {
@ -179,6 +204,12 @@ impl<'de> Deserialize<'de> for World {
}
continent_widths = Some(map.next_value()?);
},
Field::Iteration => {
if iteration.is_some() {
return Err(Error::duplicate_field("iteration"));
}
iteration = Some(map.next_value()?);
},
}
}
@ -229,7 +260,9 @@ impl<'de> Deserialize<'de> for World {
attributes
});
Ok(World {
let iteration = iteration.ok_or_else(|| Error::missing_field("iteration"))?;
let mut world = World {
width,
height,
seed,
@ -245,7 +278,21 @@ impl<'de> Deserialize<'de> for World {
min_temperature: world_attributes.min_temperature,
rng: StdRng::seed_from_u64(seed as u64),
})
iteration,
};
{
let mut y = 0;
for terrain_row in world.terrain.iter_mut() {
let mut x = 0;
for terrain_cell in terrain_row.iter_mut() {
terrain_cell.x = x;
terrain_cell.y = y;
x += 1;
}
y += 1;
}
}
Ok(world)
}
}

View file

@ -89,6 +89,7 @@ pub struct World {
pub min_temperature: f32,
#[serde(skip)]
pub rng: StdRng,
pub iteration: usize,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
@ -98,24 +99,40 @@ pub struct TerrainCell {
pub temperature: f32,
#[serde(skip)]
pub x: usize,
pub x: usize,
#[serde(skip)]
pub y: usize,
pub y: usize,
pub local_iteration: usize,
pub biome_presences: Vec<(BiomeType, f32)>,
}
impl TerrainCell {
pub fn get_next_local_random_int(&mut self, world: &World) -> f32 {
let seed = world.seed;
let x = seed as f32 + self.x as f32;
let y = seed as f32 + self.y as f32;
let z = seed as f32 + world.iteration as f32 + (self.local_iteration - 1) as f32;
drop(world);
self.local_iteration += 1;
perlin::perlin_value(x, y, z)
}
}
impl World {
pub const ALTITUDE_SPAN: f32 = World::MAX_ALTITUDE - World::MIN_ALTITUDE;
pub const CONTINENT_MAX_SIZE_FACTOR: f32 = 8.0;
pub const CONTINENT_MIN_SIZE_FACTOR: f32 = 6.0;
pub const CONTINENT_MAX_SIZE_FACTOR: f32 = 8.7;
pub const CONTINENT_MIN_SIZE_FACTOR: f32 = 5.7;
pub const MAX_ALTITUDE: f32 = 15000.0;
pub const MAX_RAINFALL: f32 = 13000.0;
pub const MAX_TEMPERATURE: f32 = 30.0;
pub const MIN_ALTITUDE: f32 = -15000.0;
pub const MIN_RAINFALL: f32 = 0.0;
pub const MIN_TEMPERATURE: f32 = -35.0;
pub const NUM_CONTINENTS: u8 = 6;
pub const NUM_CONTINENTS: u8 = 12;
pub const RAINFALL_DRYNESS_FACTOR: f32 = 0.005;
pub const RAINFALL_DRYNESS_OFFSET: f32 = World::RAINFALL_DRYNESS_FACTOR * World::MAX_RAINFALL;
pub const RAINFALL_SPAN: f32 = World::MAX_RAINFALL - World::MIN_RAINFALL;
@ -127,10 +144,7 @@ impl World {
width,
height,
seed,
terrain: vec![
vec![TerrainCell::default(); width.try_into().unwrap()];
height.try_into().unwrap()
],
terrain: vec![vec![default(); width.try_into().unwrap()]; height.try_into().unwrap()],
continent_offsets: [default(); World::NUM_CONTINENTS as usize],
continent_sizes: [default(); World::NUM_CONTINENTS as usize],
max_altitude: World::MIN_ALTITUDE,
@ -140,6 +154,7 @@ impl World {
max_temperature: World::MIN_TEMPERATURE,
min_temperature: World::MAX_TEMPERATURE,
rng: StdRng::seed_from_u64(seed as u64),
iteration: 0,
}
}
@ -148,10 +163,7 @@ impl World {
width,
height,
seed,
terrain: vec![
vec![TerrainCell::default(); width.try_into().unwrap()];
height.try_into().unwrap()
],
terrain: vec![vec![default(); width.try_into().unwrap()]; height.try_into().unwrap()],
continent_offsets: [default(); World::NUM_CONTINENTS as usize],
continent_sizes: [default(); World::NUM_CONTINENTS as usize],
max_altitude: World::MIN_ALTITUDE,
@ -161,6 +173,7 @@ impl World {
max_temperature: World::MIN_TEMPERATURE,
min_temperature: World::MAX_TEMPERATURE,
rng: StdRng::seed_from_u64(seed as u64),
iteration: 0,
}
}
@ -187,35 +200,76 @@ impl World {
Ok(())
}
fn generate_continents(&mut self) {
#[cfg(feature = "logging")]
fn generate_continents(&mut self, progress_sender: &Sender<(f32, String)>) {
info!("Generating continents");
send_progress(progress_sender, 0.0, format!("Generating continents"));
let width = self.width as f32;
let height = self.height as f32;
const LONGTITUDE_FACTOR: f32 = 15.0;
const LATITUDE_FACTOR: f32 = 6.0;
for i in 0..World::NUM_CONTINENTS {
// #[cfg(feature = "logging")]
// info!("Continents: {}/{}", i, World::NUM_CONTINENTS);
self.continent_offsets[i as usize].x = self
let mut previous_position = Vec2 {
x: self
.rng
.gen_range(width * i as f32 * 2.0 / 5.0..(width * (i as f32 + 2.0) * 2.0 / 5.0))
.repeat(width);
self.continent_offsets[i as usize].y = self.rng.gen_range(
height * 1.0 / LATITUDE_FACTOR..height * (LATITUDE_FACTOR - 1.0) / LATITUDE_FACTOR,
.gen_range(0.0..width * (LONGTITUDE_FACTOR - 1.0) / LONGTITUDE_FACTOR),
y: self.rng.gen_range(
height / LATITUDE_FACTOR..height * (LATITUDE_FACTOR - 1.0) / LATITUDE_FACTOR,
),
};
for i in 0..World::NUM_CONTINENTS {
send_progress(
progress_sender,
i as f32 / World::NUM_CONTINENTS as f32,
format!("Generating continents: {i}/{{World::NUM_CONTINENTS}}"),
);
let idx = i as usize;
let width_offset = self.rng.gen_range(0.0..6.0);
self.continent_offsets[idx] = previous_position;
self.continent_sizes[idx] = Vec2 {
x: self.rng.gen_range(
World::CONTINENT_MIN_SIZE_FACTOR + width_offset
..World::CONTINENT_MAX_SIZE_FACTOR + width_offset,
),
y: self.rng.gen_range(
World::CONTINENT_MIN_SIZE_FACTOR + width_offset
..World::CONTINENT_MAX_SIZE_FACTOR + width_offset,
),
};
let y_position = self.rng.gen_range(
height / LATITUDE_FACTOR..height * (LATITUDE_FACTOR - 1.0) / LATITUDE_FACTOR,
);
self.continent_sizes[i as usize] = Vec2 {
x: self
.rng
.gen_range(World::CONTINENT_MIN_SIZE_FACTOR..World::CONTINENT_MAX_SIZE_FACTOR),
y: self
.rng
.gen_range(World::CONTINENT_MIN_SIZE_FACTOR..World::CONTINENT_MAX_SIZE_FACTOR)
/ 2.0,
let new_vector = if i % 3 == 2 {
Vec2 {
x: f32::repeat(
previous_position.x
+ self.rng.gen_range(
width * 4.0 / LONGTITUDE_FACTOR..width * 6.0 / LONGTITUDE_FACTOR,
),
width,
),
y: y_position,
}
} else {
Vec2 {
x: f32::repeat(
previous_position.x
+ self.rng.gen_range(
width / LONGTITUDE_FACTOR..width * 2.0 / LONGTITUDE_FACTOR,
),
width,
),
y: y_position,
}
};
previous_position = new_vector;
}
info!("Done generating continents");
}
@ -278,16 +332,16 @@ impl World {
progress_sender: &Sender<(f32, String)>,
) -> Result<(), CartesianError> {
info!("Generating altitude");
self.generate_continents();
self.generate_continents(progress_sender);
const RADIUS_1: f32 = 0.5;
const RADIUS_1: f32 = 0.75;
const RADIUS_2: f32 = 8.0;
const RADIUS_3: f32 = 4.0;
const RADIUS_4: f32 = 8.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_8: f32 = 1.5;
const RADIUS_9: f32 = 1.0;
let offset_1 = World::random_offset_vector(&mut self.rng);
@ -433,8 +487,10 @@ impl World {
info!("Generating rainfall");
const RADIUS_1: f32 = 2.0;
const RADIUS_2: f32 = 1.0;
const RADIUS_3: f32 = 16.0;
let offset_1 = World::random_offset_vector(&mut self.rng);
let offset_2 = World::random_offset_vector(&mut self.rng);
let offset_3 = World::random_offset_vector(&mut self.rng);
let height = self.terrain.len();
for y in 0..height {
@ -457,8 +513,12 @@ impl World {
.random_noise_from_polar_coordinates(alpha, beta, RADIUS_2, offset_2)?
* 1.5
+ 0.25;
let random_noise_3 =
self.random_noise_from_polar_coordinates(alpha, beta, RADIUS_3, offset_3)?;
let latitude_factor = alpha + (random_noise_1 * 2.0 - 1.0) * PI * 0.2;
let value_a = mix_values(random_noise_1, random_noise_3, 0.15);
let latitude_factor = alpha + (value_a * 2.0 - 1.0) * PI * 0.2;
let latitude_modifier_1 = (1.5 * f32::sin(latitude_factor)) - 0.5;
let latitude_modifier_2 = f32::cos(latitude_factor);
@ -545,8 +605,10 @@ impl World {
progress_sender: &Sender<(f32, String)>,
) -> Result<(), CartesianError> {
info!("Generating temperature");
let offset = World::random_offset_vector(&mut self.rng);
const RADIUS: f32 = 2.0;
let offset_1 = World::random_offset_vector(&mut self.rng);
let offset_2 = World::random_offset_vector(&mut self.rng);
const RADIUS_1: f32 = 2.0;
const RADIUS_2: f32 = 16.0;
let height = self.terrain.len();
for y in 0..height {
@ -564,12 +626,14 @@ impl World {
let beta = (x as f32 / self.width as f32) * TAU;
let random_noise =
self.random_noise_from_polar_coordinates(alpha, beta, RADIUS, offset)?;
let random_noise_1 =
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)?;
let cell = &mut self.terrain[y][x];
let latitude_modifer = mix_values(alpha, random_noise * PI, 0.1);
let latitude_modifer = alpha * 0.9 + (random_noise_1 + random_noise_2) * 0.05 * PI;
let altitude_factor = f32::max(
0.0,
(cell.altitude / World::MAX_ALTITUDE) * World::TEMPERATURE_ALTITUDE_FACTOR,

View file

@ -42,7 +42,10 @@ impl Display for LoadError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
LoadError::MissingSave(_) => f.write_str("No save found at given path"),
LoadError::InvalidSave(_) => f.write_str("Loaded file is not a valid save"),
LoadError::InvalidSave(err) => f.write_fmt(format_args!(
"Loaded file is not a valid save - {}",
err.to_string()
)),
}
}
}

View file

@ -31,12 +31,12 @@ impl WindowSystem for SaveLoad<'_, '_> {
if let Some(path) = tinyfiledialogs::save_file_dialog_with_filter(
"Save world",
state.file_name.as_str(),
&["*.rsplnt", "*.ron"],
&["*.rsplnt", "*.rsplnt"],
"World file",
) {
if let Err(err) = world_manager.save_world(&path) {
// TODO: Error popup
error!("Failed to save: {err:#?}");
error!("Failed to save: {err}");
}
*state.file_name = path;
}
@ -45,14 +45,15 @@ impl WindowSystem for SaveLoad<'_, '_> {
if let Some(path) = tinyfiledialogs::open_file_dialog(
"World file",
state.file_name.as_str(),
Some((&["*.ron", "*.rsplnt"], "*.ron,*.rsplnt")),
Some((&["*.rsplnt"], "*.rspnt")),
) {
if let Err(err) = world_manager.load_world(&path) {
// TODO: Error popup
error!("Failed to load: {err:#?}");
error!("Failed to load: {err}");
} else {
should_redraw.0 = true;
}
*state.file_name = path;
should_redraw.0 = true;
}
}
});

View file

@ -41,6 +41,7 @@ impl WindowSystem for TileInfo<'_, '_> {
biome_presences,
x,
y,
..
} = &world.terrain[cursor_y as usize][cursor_x as usize];
_ = ui.label("Coordinates");