WIP
This commit is contained in:
parent
6a7ecc11b6
commit
836be517a2
13 changed files with 303 additions and 128 deletions
26
Cargo.lock
generated
26
Cargo.lock
generated
|
@ -804,6 +804,15 @@ dependencies = [
|
||||||
"winit",
|
"winit",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bincode"
|
||||||
|
version = "1.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bit-set"
|
name = "bit-set"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
|
@ -871,9 +880,9 @@ checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "calloop"
|
name = "calloop"
|
||||||
version = "0.10.3"
|
version = "0.10.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5bcf530afb40e45e14440701e5e996d7fd139e84a912a4d83a8d6a0fb3e58663"
|
checksum = "19457a0da465234abd76134a5c2a910c14bd3c5558463e4396ab9a37a328e465"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"nix 0.25.1",
|
"nix 0.25.1",
|
||||||
|
@ -2094,6 +2103,7 @@ name = "planet"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy",
|
"bevy",
|
||||||
|
"bincode",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -2305,18 +2315,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.148"
|
version = "1.0.149"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc"
|
checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.148"
|
version = "1.0.149"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c"
|
checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -2596,9 +2606,9 @@ checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.15.0"
|
version = "1.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
#!/bin/env /bin/sh
|
#!/bin/env /bin/sh
|
||||||
echo "Debug-build with features: minimal"
|
echo "Debug-build with features: minimal"
|
||||||
cargo build --no-default-features --features= &&
|
cargo build -j 6 --no-default-features --features= &&
|
||||||
echo "Debug-build with features: logging" &&
|
echo "Debug-build with features: logging" &&
|
||||||
cargo build --no-default-features --features=logging &&
|
cargo build -j 6 --no-default-features --features=logging &&
|
||||||
echo "Debug-build with features: render" &&
|
echo "Debug-build with features: render" &&
|
||||||
cargo build --no-default-features --features=render &&
|
cargo build -j 6 --no-default-features --features=render &&
|
||||||
echo "Debug-build with features: logging render" &&
|
echo "Debug-build with features: logging render" &&
|
||||||
cargo build --no-default-features --features="logging,render" &&
|
cargo build -j 6 --no-default-features --features="logging,render" &&
|
||||||
echo "Release-build with features: minimal"
|
echo "Release-build with features: minimal"
|
||||||
cargo build --release --no-default-features --features= &&
|
cargo build -j 6 --release --no-default-features --features= &&
|
||||||
echo "Release-build with features: logging" &&
|
echo "Release-build with features: logging" &&
|
||||||
cargo build --release --no-default-features --features=logging &&
|
cargo build -j 6 --release --no-default-features --features=logging &&
|
||||||
echo "Release-build with features: render" &&
|
echo "Release-build with features: render" &&
|
||||||
cargo build --release --no-default-features --features=render &&
|
cargo build -j 6 --release --no-default-features --features=render &&
|
||||||
echo "Release-build with features: logging render" &&
|
echo "Release-build with features: logging render" &&
|
||||||
cargo build --release --no-default-features --features="logging,render" &&
|
cargo build -j 6 --release --no-default-features --features="logging,render" &&
|
||||||
echo "Done!"
|
echo "Done!"
|
|
@ -23,6 +23,9 @@ version = "1.0"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["derive"]
|
features = ["derive"]
|
||||||
|
|
||||||
|
[dependencies.bincode]
|
||||||
|
version = "1.3.3"
|
||||||
|
|
||||||
[dependencies.crossbeam-channel]
|
[dependencies.crossbeam-channel]
|
||||||
version = "0.5.6"
|
version = "0.5.6"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
|
@ -1,7 +1,17 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use {
|
||||||
|
crate::World,
|
||||||
|
core::hash::Hash,
|
||||||
|
serde::{Deserialize, Serialize},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, Eq, PartialEq, Hash)]
|
||||||
pub struct HumanGroup {
|
pub struct HumanGroup {
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
pub population: u32,
|
pub population: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HumanGroup {
|
||||||
|
pub fn update(&self, _world: &World) {
|
||||||
|
// TODO: Anything
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
// pub mod terrain_cell;
|
pub mod terrain_cell;
|
||||||
pub mod world;
|
pub mod world;
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
use {
|
use {
|
||||||
crate::TerrainCell,
|
crate::{HumanGroup, TerrainCell},
|
||||||
bevy::prelude::default,
|
bevy::prelude::{debug, default},
|
||||||
serde::{
|
serde::{
|
||||||
de::{Error, MapAccess, SeqAccess, Visitor},
|
de::{Error, MapAccess, SeqAccess, Visitor},
|
||||||
|
ser::SerializeStruct,
|
||||||
Deserialize,
|
Deserialize,
|
||||||
|
Serialize,
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
fmt::{self, Formatter},
|
fmt::{self, Formatter},
|
||||||
sync::Weak,
|
sync::Arc,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,6 +26,9 @@ impl<'de> Deserialize<'de> for TerrainCell {
|
||||||
Temperature,
|
Temperature,
|
||||||
LocalIteration,
|
LocalIteration,
|
||||||
BiomePresences,
|
BiomePresences,
|
||||||
|
Height,
|
||||||
|
Width,
|
||||||
|
HumanGroups,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TerrainCellVisitor;
|
struct TerrainCellVisitor;
|
||||||
|
@ -39,25 +44,48 @@ impl<'de> Deserialize<'de> for TerrainCell {
|
||||||
where
|
where
|
||||||
V: SeqAccess<'de>,
|
V: SeqAccess<'de>,
|
||||||
{
|
{
|
||||||
|
std::mem::transmute::<u128>(seq);
|
||||||
|
|
||||||
|
let mut length = 0;
|
||||||
let altitude = seq
|
let altitude = seq
|
||||||
.next_element()?
|
.next_element()?
|
||||||
.ok_or_else(|| Error::invalid_length(0, &self))?;
|
.ok_or_else(|| panic!("Invalid length {length}, expected 8"))?;
|
||||||
|
length += 1;
|
||||||
|
|
||||||
let rainfall = seq
|
let rainfall = seq
|
||||||
.next_element()?
|
.next_element()?
|
||||||
.ok_or_else(|| Error::invalid_length(1, &self))?;
|
.ok_or_else(|| panic!("Invalid length {length}, expected 8"))?;
|
||||||
|
length += 1;
|
||||||
|
|
||||||
let temperature = seq
|
let temperature = seq
|
||||||
.next_element()?
|
.next_element()?
|
||||||
.ok_or_else(|| Error::invalid_length(2, &self))?;
|
.ok_or_else(|| panic!("Invalid length {length}, expected 8"))?;
|
||||||
|
length += 1;
|
||||||
|
|
||||||
let local_iteration = seq
|
let local_iteration = seq
|
||||||
.next_element()?
|
.next_element()?
|
||||||
.ok_or_else(|| Error::invalid_length(3, &self))?;
|
.ok_or_else(|| panic!("Invalid length {length}, expected 8"))?;
|
||||||
|
length += 1;
|
||||||
|
|
||||||
let biome_presences = seq
|
let biome_presences = seq
|
||||||
.next_element()?
|
.next_element()?
|
||||||
.ok_or_else(|| Error::invalid_length(4, &self))?;
|
.ok_or_else(|| panic!("Invalid length {length}, expected 8"))?;
|
||||||
|
length += 1;
|
||||||
|
|
||||||
|
let height = seq
|
||||||
|
.next_element()?
|
||||||
|
.ok_or_else(|| panic!("Invalid length {length}, expected 8"))?;
|
||||||
|
length += 1;
|
||||||
|
|
||||||
|
let width = seq
|
||||||
|
.next_element()?
|
||||||
|
.ok_or_else(|| panic!("Invalid length {length}, expected 8"))?;
|
||||||
|
length += 1;
|
||||||
|
|
||||||
|
let human_groups = seq
|
||||||
|
.next_element::<Vec<HumanGroup>>()?
|
||||||
|
.ok_or_else(|| panic!("Invalid length {length}, expected 8"))?;
|
||||||
|
// length += 1;
|
||||||
|
|
||||||
Ok(TerrainCell {
|
Ok(TerrainCell {
|
||||||
altitude,
|
altitude,
|
||||||
|
@ -67,6 +95,9 @@ impl<'de> Deserialize<'de> for TerrainCell {
|
||||||
biome_presences,
|
biome_presences,
|
||||||
x: default(),
|
x: default(),
|
||||||
y: default(),
|
y: default(),
|
||||||
|
human_groups: human_groups.iter().map(|group| Arc::new(*group)).collect(),
|
||||||
|
height,
|
||||||
|
width,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,6 +110,9 @@ impl<'de> Deserialize<'de> for TerrainCell {
|
||||||
let mut temperature = None;
|
let mut temperature = None;
|
||||||
let mut local_iteration = None;
|
let mut local_iteration = None;
|
||||||
let mut biome_presences = None;
|
let mut biome_presences = None;
|
||||||
|
let mut height = None;
|
||||||
|
let mut width = None;
|
||||||
|
let mut human_groups: Option<Vec<HumanGroup>> = None;
|
||||||
|
|
||||||
while let Some(key) = map.next_key()? {
|
while let Some(key) = map.next_key()? {
|
||||||
match key {
|
match key {
|
||||||
|
@ -112,6 +146,24 @@ impl<'de> Deserialize<'de> for TerrainCell {
|
||||||
}
|
}
|
||||||
biome_presences = Some(map.next_value()?);
|
biome_presences = Some(map.next_value()?);
|
||||||
},
|
},
|
||||||
|
Field::Height => {
|
||||||
|
if height.is_some() {
|
||||||
|
return Err(Error::duplicate_field("height"));
|
||||||
|
}
|
||||||
|
height = Some(map.next_value()?);
|
||||||
|
},
|
||||||
|
Field::Width => {
|
||||||
|
if width.is_some() {
|
||||||
|
return Err(Error::duplicate_field("width"));
|
||||||
|
}
|
||||||
|
width = Some(map.next_value()?);
|
||||||
|
},
|
||||||
|
Field::HumanGroups => {
|
||||||
|
if human_groups.is_some() {
|
||||||
|
return Err(Error::duplicate_field("human_groups"));
|
||||||
|
}
|
||||||
|
human_groups = Some(map.next_value()?);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,6 +174,10 @@ impl<'de> Deserialize<'de> for TerrainCell {
|
||||||
local_iteration.ok_or_else(|| Error::missing_field("local_iteration"))?;
|
local_iteration.ok_or_else(|| Error::missing_field("local_iteration"))?;
|
||||||
let biome_presences =
|
let biome_presences =
|
||||||
biome_presences.ok_or_else(|| Error::missing_field("biome_presences"))?;
|
biome_presences.ok_or_else(|| Error::missing_field("biome_presences"))?;
|
||||||
|
let height = height.ok_or_else(|| Error::missing_field("height"))?;
|
||||||
|
let width = width.ok_or_else(|| Error::missing_field("width"))?;
|
||||||
|
let human_groups =
|
||||||
|
human_groups.ok_or_else(|| Error::missing_field("human_groups"))?;
|
||||||
|
|
||||||
Ok(TerrainCell {
|
Ok(TerrainCell {
|
||||||
altitude,
|
altitude,
|
||||||
|
@ -131,6 +187,9 @@ impl<'de> Deserialize<'de> for TerrainCell {
|
||||||
biome_presences,
|
biome_presences,
|
||||||
x: default(),
|
x: default(),
|
||||||
y: default(),
|
y: default(),
|
||||||
|
human_groups: human_groups.iter().map(|group| Arc::new(*group)).collect(),
|
||||||
|
height,
|
||||||
|
width,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,3 +205,43 @@ impl<'de> Deserialize<'de> for TerrainCell {
|
||||||
deserializer.deserialize_struct("TerrainCell", FIELDS, TerrainCellVisitor)
|
deserializer.deserialize_struct("TerrainCell", FIELDS, TerrainCellVisitor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Serialize for TerrainCell {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
let TerrainCell {
|
||||||
|
altitude,
|
||||||
|
rainfall,
|
||||||
|
temperature,
|
||||||
|
x: _x,
|
||||||
|
y: _y,
|
||||||
|
local_iteration,
|
||||||
|
biome_presences,
|
||||||
|
human_groups,
|
||||||
|
height,
|
||||||
|
width,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
let human_groups = &human_groups
|
||||||
|
.iter()
|
||||||
|
.map(|group_arc| **group_arc)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let mut serialized_struct = serializer.serialize_struct(stringify!(TerrainCell), 10)?;
|
||||||
|
serialized_struct.serialize_field(stringify!(altitude), altitude)?;
|
||||||
|
serialized_struct.serialize_field(stringify!(rainfall), rainfall)?;
|
||||||
|
serialized_struct.serialize_field(stringify!(temperature), temperature)?;
|
||||||
|
// #[skip]
|
||||||
|
// serialized_struct.serialize_field(stringify!(x), x)?;
|
||||||
|
// #[skip]
|
||||||
|
// serialized_struct.serialize_field(stringify!(y), y)?;
|
||||||
|
serialized_struct.serialize_field(stringify!(local_iteration), local_iteration)?;
|
||||||
|
serialized_struct.serialize_field(stringify!(biome_presences), biome_presences)?;
|
||||||
|
serialized_struct.serialize_field(stringify!(human_groups), human_groups)?;
|
||||||
|
serialized_struct.serialize_field(stringify!(height), height)?;
|
||||||
|
serialized_struct.serialize_field(stringify!(width), width)?;
|
||||||
|
serialized_struct.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::{TerrainCell, World},
|
crate::{TerrainCell, World},
|
||||||
bevy::prelude::debug,
|
bevy::{prelude::debug, utils::default},
|
||||||
rand::{rngs::StdRng, SeedableRng},
|
rand::{rngs::StdRng, SeedableRng},
|
||||||
serde::{
|
serde::{
|
||||||
de::{Error, MapAccess, SeqAccess, Visitor},
|
de::{Error, MapAccess, SeqAccess, Visitor},
|
||||||
|
@ -137,6 +137,7 @@ impl<'de> Deserialize<'de> for World {
|
||||||
|
|
||||||
rng: StdRng::seed_from_u64(seed as u64),
|
rng: StdRng::seed_from_u64(seed as u64),
|
||||||
iteration,
|
iteration,
|
||||||
|
human_groups_to_update: default(),
|
||||||
};
|
};
|
||||||
{
|
{
|
||||||
let mut y = 0;
|
let mut y = 0;
|
||||||
|
@ -219,10 +220,10 @@ impl<'de> Deserialize<'de> for World {
|
||||||
let mut terrain: Vec<Vec<TerrainCell>> =
|
let mut terrain: Vec<Vec<TerrainCell>> =
|
||||||
terrain.ok_or_else(|| Error::missing_field("terrain"))?;
|
terrain.ok_or_else(|| Error::missing_field("terrain"))?;
|
||||||
|
|
||||||
for x in 0..width as usize {
|
for x in 0..width as u32 {
|
||||||
for y in 0..height as usize {
|
for y in 0..height as u32 {
|
||||||
terrain[y][x].x = x;
|
terrain[y as usize][x as usize].x = x;
|
||||||
terrain[y][x].y = y;
|
terrain[y as usize][x as usize].y = y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,6 +280,7 @@ impl<'de> Deserialize<'de> for World {
|
||||||
|
|
||||||
rng: StdRng::seed_from_u64(seed as u64),
|
rng: StdRng::seed_from_u64(seed as u64),
|
||||||
iteration,
|
iteration,
|
||||||
|
human_groups_to_update: default(),
|
||||||
};
|
};
|
||||||
{
|
{
|
||||||
let mut y = 0;
|
let mut y = 0;
|
||||||
|
|
|
@ -1,22 +1,23 @@
|
||||||
use {
|
use {
|
||||||
crate::{perlin, BiomeType, HumanGroup, World},
|
crate::{perlin, BiomeType, HumanGroup, World},
|
||||||
serde::{Deserialize, Serialize},
|
std::sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct TerrainCell {
|
pub struct TerrainCell {
|
||||||
pub altitude: f32,
|
pub altitude: f32,
|
||||||
pub rainfall: f32,
|
pub rainfall: f32,
|
||||||
pub temperature: f32,
|
pub temperature: f32,
|
||||||
|
|
||||||
#[serde(skip)]
|
pub x: u32,
|
||||||
pub x: usize,
|
pub y: u32,
|
||||||
#[serde(skip)]
|
pub local_iteration: u64,
|
||||||
pub y: usize,
|
|
||||||
pub local_iteration: usize,
|
|
||||||
|
|
||||||
pub biome_presences: Vec<(BiomeType, f32)>,
|
pub biome_presences: Vec<(BiomeType, f32)>,
|
||||||
pub human_groups: Vec<HumanGroup>,
|
pub human_groups: Vec<Arc<HumanGroup>>,
|
||||||
|
|
||||||
|
pub height: f32,
|
||||||
|
pub width: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TerrainCell {
|
impl TerrainCell {
|
||||||
|
@ -36,15 +37,15 @@ impl TerrainCell {
|
||||||
self.get_next_local_random_int(world) as f32 / perlin::MAX_PERMUTATION_VALUE as f32
|
self.get_next_local_random_int(world) as f32 / perlin::MAX_PERMUTATION_VALUE as f32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn biome_presence(&self, biome: BiomeType) -> f32 {
|
pub fn biome_presence(&self, biome: BiomeType) -> Option<f32> {
|
||||||
if let Some(presence) = self
|
if let Some(presence) = self
|
||||||
.biome_presences
|
.biome_presences
|
||||||
.iter()
|
.iter()
|
||||||
.find(|biome_presence| biome_presence.0 == biome)
|
.find(|biome_presence| biome_presence.0 == biome)
|
||||||
{
|
{
|
||||||
presence.1
|
Some(presence.1)
|
||||||
} else {
|
} else {
|
||||||
0.0
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ use {
|
||||||
perlin,
|
perlin,
|
||||||
BiomeStats,
|
BiomeStats,
|
||||||
BiomeType,
|
BiomeType,
|
||||||
|
HumanGroup,
|
||||||
TerrainCell,
|
TerrainCell,
|
||||||
},
|
},
|
||||||
bevy::{
|
bevy::{
|
||||||
|
@ -20,11 +21,12 @@ use {
|
||||||
},
|
},
|
||||||
crossbeam_channel::Sender,
|
crossbeam_channel::Sender,
|
||||||
rand::{rngs::StdRng, Rng, SeedableRng},
|
rand::{rngs::StdRng, Rng, SeedableRng},
|
||||||
serde::{Deserialize, Serialize},
|
serde::Serialize,
|
||||||
std::{
|
std::{
|
||||||
error::Error,
|
error::Error,
|
||||||
f32::consts::{PI, TAU},
|
f32::consts::{PI, TAU},
|
||||||
fmt::{Debug, Display},
|
fmt::{Debug, Display},
|
||||||
|
sync::{Arc, Weak},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -91,6 +93,9 @@ pub struct World {
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub rng: StdRng,
|
pub rng: StdRng,
|
||||||
pub iteration: usize,
|
pub iteration: usize,
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
pub human_groups_to_update: Vec<Weak<HumanGroup>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl World {
|
impl World {
|
||||||
|
@ -114,7 +119,7 @@ impl World {
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn cell_max_widht(&self) -> f32 {
|
pub fn cell_max_width(&self) -> f32 {
|
||||||
self.width as f32 / World::CIRCUMFERENCE as f32
|
self.width as f32 / World::CIRCUMFERENCE as f32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,25 +139,7 @@ impl World {
|
||||||
min_temperature: World::MAX_TEMPERATURE,
|
min_temperature: World::MAX_TEMPERATURE,
|
||||||
rng: StdRng::seed_from_u64(seed as u64),
|
rng: StdRng::seed_from_u64(seed as u64),
|
||||||
iteration: 0,
|
iteration: 0,
|
||||||
}
|
human_groups_to_update: default(),
|
||||||
}
|
|
||||||
|
|
||||||
pub fn async_new(width: u32, height: u32, seed: u32) -> World {
|
|
||||||
World {
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
seed,
|
|
||||||
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,
|
|
||||||
min_altitude: World::MAX_ALTITUDE,
|
|
||||||
max_rainfall: World::MIN_RAINFALL,
|
|
||||||
min_rainfall: World::MAX_RAINFALL,
|
|
||||||
max_temperature: World::MIN_TEMPERATURE,
|
|
||||||
min_temperature: World::MAX_TEMPERATURE,
|
|
||||||
rng: StdRng::seed_from_u64(seed as u64),
|
|
||||||
iteration: 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,6 +163,9 @@ impl World {
|
||||||
send_progress(progress_sender, 0.0, "Generating biomes");
|
send_progress(progress_sender, 0.0, "Generating biomes");
|
||||||
self.generate_biomes(progress_sender);
|
self.generate_biomes(progress_sender);
|
||||||
|
|
||||||
|
send_progress(progress_sender, 0.0, "Populating world");
|
||||||
|
self.set_initial_human_groups(progress_sender);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,8 +409,10 @@ impl World {
|
||||||
self.min_altitude = altitude;
|
self.min_altitude = altitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.terrain[y][x].x = x;
|
self.terrain[y][x].x = x as u32;
|
||||||
self.terrain[y][x].y = y;
|
self.terrain[y][x].y = y as u32;
|
||||||
|
self.terrain[y][x].height = f32::sin(alpha) * self.cell_max_width();
|
||||||
|
self.terrain[y][x].width = self.cell_max_width();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info!("Done generating altitude");
|
info!("Done generating altitude");
|
||||||
|
@ -502,10 +494,10 @@ impl World {
|
||||||
let latitude_modifier_2 = f32::cos(latitude_factor);
|
let latitude_modifier_2 = f32::cos(latitude_factor);
|
||||||
|
|
||||||
let offset_cell_x_1 =
|
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 / 40.0) as usize)
|
||||||
% width;
|
% width;
|
||||||
let offset_cell_x_2 =
|
let offset_cell_x_2 =
|
||||||
(width + x + f32::floor(latitude_modifier_2 * width as f32 / 15.0) as usize)
|
(width + x + f32::floor(latitude_modifier_2 * width as f32 / 20.0) as usize)
|
||||||
% width;
|
% width;
|
||||||
let offset_cell_x_3 =
|
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)
|
||||||
|
@ -540,7 +532,7 @@ impl World {
|
||||||
- (offset_altitude_3 * 0.5)
|
- (offset_altitude_3 * 0.5)
|
||||||
- (offset_altitude_4 * 0.4)
|
- (offset_altitude_4 * 0.4)
|
||||||
- (offset_altitude_5 * 0.5)
|
- (offset_altitude_5 * 0.5)
|
||||||
+ (World::MAX_ALTITUDE * 0.18 * random_noise_2)
|
+ (World::MAX_ALTITUDE * 0.17 * random_noise_2)
|
||||||
- (altitude_value * 0.25))
|
- (altitude_value * 0.25))
|
||||||
/ World::MAX_ALTITUDE;
|
/ World::MAX_ALTITUDE;
|
||||||
|
|
||||||
|
@ -734,9 +726,12 @@ impl World {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn cell_neighbors(&self, x: usize, y: usize) -> HashMap<CompassDirection, &TerrainCell> {
|
pub fn cell_neighbors(&self, x: u32, y: u32) -> HashMap<CompassDirection, &TerrainCell> {
|
||||||
let mut neighbors = HashMap::new();
|
let mut neighbors = HashMap::new();
|
||||||
|
|
||||||
|
let x = x as usize;
|
||||||
|
let y = y as usize;
|
||||||
|
|
||||||
let height = self.height as usize;
|
let height = self.height as usize;
|
||||||
let width = self.width as usize;
|
let width = self.width as usize;
|
||||||
|
|
||||||
|
@ -910,6 +905,63 @@ impl World {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_initial_human_groups(&mut self, progress_sender: &Sender<(f32, String)>) -> () {
|
||||||
|
const MAX_GROUPS: u8 = 5;
|
||||||
|
const MINIMUM_PRESENCE: f32 = 0.1;
|
||||||
|
|
||||||
|
let mut placed_groups = 0;
|
||||||
|
let mut tries = 0;
|
||||||
|
while placed_groups < MAX_GROUPS {
|
||||||
|
tries += 1;
|
||||||
|
send_progress(
|
||||||
|
progress_sender,
|
||||||
|
placed_groups as f32 / MAX_GROUPS as f32,
|
||||||
|
format!("Placing initial population: {placed_groups}/{MAX_GROUPS} (try #{tries})"),
|
||||||
|
);
|
||||||
|
|
||||||
|
let x = self.rng.gen_range(0..self.width as usize);
|
||||||
|
let y = self.rng.gen_range(0..self.height as usize);
|
||||||
|
|
||||||
|
let grassland_presence = self.terrain[y][x].biome_presence(BiomeType::Grassland);
|
||||||
|
|
||||||
|
match grassland_presence {
|
||||||
|
Some(presence) => {
|
||||||
|
if presence < MINIMUM_PRESENCE {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => continue,
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_human_group = Arc::new(HumanGroup {
|
||||||
|
id: placed_groups as u32,
|
||||||
|
population: 1000,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.human_groups_to_update
|
||||||
|
.push(Arc::<HumanGroup>::downgrade(&new_human_group));
|
||||||
|
self.terrain[y][x].human_groups.push(new_human_group);
|
||||||
|
|
||||||
|
placed_groups += 1;
|
||||||
|
tries = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iterate(&mut self) {
|
||||||
|
for group in self.human_groups_to_update.iter() {
|
||||||
|
if let Some(group) = group.upgrade() {
|
||||||
|
group.update(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.human_groups_to_update.clear();
|
||||||
|
self.iteration += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn add_human_group_to_update(&mut self, group: Weak<HumanGroup>) {
|
||||||
|
self.human_groups_to_update.push(group);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_progress<T: Into<String>>(
|
fn send_progress<T: Into<String>>(
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::io::{Write, BufReader};
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::{World, WorldGenError},
|
crate::{World, WorldGenError},
|
||||||
bevy::{
|
bevy::{
|
||||||
|
@ -12,7 +14,7 @@ use {
|
||||||
error::Error,
|
error::Error,
|
||||||
fmt::Display,
|
fmt::Display,
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{self, Read, Write},
|
io::{self, BufWriter},
|
||||||
path::Path,
|
path::Path,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -20,7 +22,7 @@ use {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum LoadError {
|
pub enum LoadError {
|
||||||
MissingSave(io::Error),
|
MissingSave(io::Error),
|
||||||
InvalidSave(postcard::Error),
|
InvalidSave(bincode::Error),
|
||||||
}
|
}
|
||||||
impl Error for LoadError {
|
impl Error for LoadError {
|
||||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
|
@ -29,15 +31,8 @@ impl Error for LoadError {
|
||||||
LoadError::InvalidSave(error) => Some(error),
|
LoadError::InvalidSave(error) => Some(error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn description(&self) -> &str {
|
|
||||||
"description() is deprecated; use Display"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cause(&self) -> Option<&dyn Error> {
|
|
||||||
self.source()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for LoadError {
|
impl Display for LoadError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
@ -53,7 +48,7 @@ impl Display for LoadError {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SaveError {
|
pub enum SaveError {
|
||||||
MissingWorld,
|
MissingWorld,
|
||||||
SerializationError(postcard::Error),
|
SerializationError(bincode::Error),
|
||||||
FailedToWrite(io::Error),
|
FailedToWrite(io::Error),
|
||||||
}
|
}
|
||||||
impl Error for SaveError {
|
impl Error for SaveError {
|
||||||
|
@ -103,32 +98,31 @@ impl WorldManager {
|
||||||
return Err(SaveError::MissingWorld);
|
return Err(SaveError::MissingWorld);
|
||||||
};
|
};
|
||||||
|
|
||||||
let serialized = match postcard::to_stdvec(world) {
|
let save_file = match File::create(path) {
|
||||||
Ok(serialized) => serialized,
|
Ok(save_file) => save_file,
|
||||||
Err(err) => {
|
Err(err) => return Err(SaveError::FailedToWrite(err)),
|
||||||
return Err(SaveError::SerializationError(err));
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match File::create(path).unwrap().write_all(serialized.as_slice()) {
|
let serialized = match bincode::serialize(world) {
|
||||||
|
Ok(serialized) => serialized,
|
||||||
|
Err(err) => return Err(SaveError::SerializationError(err)),
|
||||||
|
};
|
||||||
|
|
||||||
|
match BufWriter::new(save_file).write(serialized.as_slice()) {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(err) => Err(SaveError::FailedToWrite(err)),
|
Err(err) => Err(SaveError::FailedToWrite(err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_world<P: AsRef<Path>>(&mut self, path: P) -> Result<(), LoadError> {
|
pub fn load_world<P: AsRef<Path>>(&mut self, path: P) -> Result<(), LoadError> {
|
||||||
let mut file = match File::open(path) {
|
let file = match File::open(path) {
|
||||||
Ok(file) => file,
|
Ok(file) => file,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return Err(LoadError::MissingSave(err));
|
return Err(LoadError::MissingSave(err));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let mut buf = vec![];
|
|
||||||
if let Err(err) = file.read_to_end(&mut buf) {
|
match bincode::deserialize_from(BufReader::new(file)) {
|
||||||
return Err(LoadError::MissingSave(err));
|
|
||||||
};
|
|
||||||
|
|
||||||
match postcard::from_bytes(buf.as_slice()) {
|
|
||||||
Ok(world) => {
|
Ok(world) => {
|
||||||
self.world = Some(world);
|
self.world = Some(world);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -142,6 +136,11 @@ impl WorldManager {
|
||||||
self.world.as_ref()
|
self.world.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_world_mut(&mut self) -> Option<&mut World> {
|
||||||
|
self.world.as_mut()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_world(&mut self, world: World) {
|
pub fn set_world(&mut self, world: World) {
|
||||||
self.world = Some(world);
|
self.world = Some(world);
|
||||||
}
|
}
|
||||||
|
@ -153,7 +152,7 @@ impl WorldManager {
|
||||||
) -> Task<Result<World, WorldGenError>> {
|
) -> Task<Result<World, WorldGenError>> {
|
||||||
AsyncComputeTaskPool::get().spawn(async move {
|
AsyncComputeTaskPool::get().spawn(async move {
|
||||||
let seed = seed.unwrap_or_else(random);
|
let seed = seed.unwrap_or_else(random);
|
||||||
let mut new_world = World::async_new(
|
let mut new_world = World::new(
|
||||||
WorldManager::NEW_WORLD_WIDTH,
|
WorldManager::NEW_WORLD_WIDTH,
|
||||||
WorldManager::NEW_WORLD_HEIGHT,
|
WorldManager::NEW_WORLD_HEIGHT,
|
||||||
seed,
|
seed,
|
||||||
|
|
|
@ -44,6 +44,9 @@ impl WindowSystem for TileInfo<'_, '_> {
|
||||||
..
|
..
|
||||||
} = &world.terrain[cursor_y as usize][cursor_x as usize];
|
} = &world.terrain[cursor_y as usize][cursor_x as usize];
|
||||||
|
|
||||||
|
_ = ui.label("Iteration");
|
||||||
|
_ = ui.label(format!("{}", world.iteration));
|
||||||
|
ui.end_row();
|
||||||
_ = ui.label("Coordinates");
|
_ = ui.label("Coordinates");
|
||||||
_ = ui.label(format!("{x}:{y}"));
|
_ = ui.label(format!("{x}:{y}"));
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
|
|
13
src/main.rs
13
src/main.rs
|
@ -232,11 +232,11 @@ fn update_gui(world: &mut World) {
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
fn redraw_map(
|
fn redraw_map(
|
||||||
mut should_redraw: ResMut<ShouldRedraw>,
|
mut should_redraw: ResMut<ShouldRedraw>,
|
||||||
world_manager: Res<WorldManager>,
|
|
||||||
mut world_renderer: ResMut<WorldRenderer>,
|
mut world_renderer: ResMut<WorldRenderer>,
|
||||||
render_settings: Res<WorldRenderSettings>,
|
|
||||||
mut images: ResMut<Assets<Image>>,
|
mut images: ResMut<Assets<Image>>,
|
||||||
mut map_sprite: Query<&mut Sprite>,
|
mut map_sprite: Query<&mut Sprite>,
|
||||||
|
render_settings: Res<WorldRenderSettings>,
|
||||||
|
world_manager: Res<WorldManager>,
|
||||||
) {
|
) {
|
||||||
let Some(world) = world_manager.get_world() else {
|
let Some(world) = world_manager.get_world() else {
|
||||||
#[cfg(feature = "logging")]
|
#[cfg(feature = "logging")]
|
||||||
|
@ -278,6 +278,12 @@ fn redraw_map(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn iterate_world(mut world_manager: ResMut<WorldManager>) {
|
||||||
|
if let Some(world) = world_manager.get_world_mut() {
|
||||||
|
world.iterate();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
const WORLD_SCALE: i32 = 4;
|
const WORLD_SCALE: i32 = 4;
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
@ -303,7 +309,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
.add_system(update_gui)
|
.add_system(update_gui)
|
||||||
.add_system(update_cursor_map_position)
|
.add_system(update_cursor_map_position)
|
||||||
.add_system(open_tile_info)
|
.add_system(open_tile_info)
|
||||||
.add_system(redraw_map);
|
.add_system(redraw_map)
|
||||||
|
.add_system(iterate_world);
|
||||||
|
|
||||||
app.add_plugins(WorldPlugins);
|
app.add_plugins(WorldPlugins);
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,13 +39,13 @@ impl WorldRenderSettings {
|
||||||
pub fn toggle_overlay(&mut self, overlay: &WorldOverlay) {
|
pub fn toggle_overlay(&mut self, overlay: &WorldOverlay) {
|
||||||
if self.visible_overlays.contains(overlay) {
|
if self.visible_overlays.contains(overlay) {
|
||||||
assert!(
|
assert!(
|
||||||
self.visible_overlays.remove(overlay),
|
self.visible_overlays.remove(overlay),
|
||||||
"Failed to remove overlay [{overlay:#?}], that shouldn't happen."
|
"Failed to remove overlay [{overlay:#?}], that shouldn't happen."
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
assert!(
|
assert!(
|
||||||
self.visible_overlays.insert(*overlay),
|
self.visible_overlays.insert(*overlay),
|
||||||
"Failed to insert overlay [{overlay:#?}], that shouldn't happen."
|
"Failed to insert overlay [{overlay:#?}], that shouldn't happen."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,38 +53,25 @@ impl WorldRenderSettings {
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn population_color(world: &World, cell: &TerrainCell) -> Color {
|
fn population_color(world: &World, cell: &TerrainCell) -> Color {
|
||||||
let slant = world.get_slant(cell);
|
let cell_population = cell
|
||||||
let altitude_difference = world.max_altitude - world.min_altitude;
|
.human_groups
|
||||||
|
.iter()
|
||||||
|
.map(|group| group.population)
|
||||||
|
.sum::<u32>();
|
||||||
|
|
||||||
let slant_factor = f32::min(1.0, (4.0 + (10.0 * slant / altitude_difference)) / 5.0);
|
if cell_population > 0 {
|
||||||
|
debug!(cell.x, cell.y, "Population present");
|
||||||
let altitude_factor = f32::min(1.0, (0.5 + ((cell.altitude - altitude_difference) / altitude_difference)) / 1.5);
|
|
||||||
|
|
||||||
let mut total_population = 0;
|
|
||||||
|
|
||||||
for human_group in cell.human_groups {
|
|
||||||
total_population += human_group.population;
|
|
||||||
}
|
|
||||||
|
|
||||||
let color = if total_population > 0 {
|
|
||||||
Color::GREEN
|
Color::GREEN
|
||||||
} else {
|
} else {
|
||||||
let color = biome_color(world, cell);
|
let color = biome_color(world, cell);
|
||||||
|
|
||||||
let greyscale = (color.r() + color.g() + color.b()) / 4.5 + 0.25;
|
let grey = color.r() + color.g() + color.b();
|
||||||
|
let r = (color.r() + grey) / 6.0;
|
||||||
|
let g = (color.g() + grey) / 6.0;
|
||||||
|
let b = (color.b() + grey) / 6.0;
|
||||||
|
|
||||||
color.set_r(greyscale);
|
Color::rgb(r, g, b)
|
||||||
color.set_g(greyscale);
|
}
|
||||||
color.set_b(greyscale);
|
|
||||||
|
|
||||||
color
|
|
||||||
};
|
|
||||||
|
|
||||||
let r = color.r() * slant_factor * altitude_factor;
|
|
||||||
let g = color.g() * slant_factor * altitude_factor;
|
|
||||||
let b = color.b() * slant_factor * altitude_factor;
|
|
||||||
|
|
||||||
Color::rgb(r, g, b)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
@ -221,6 +208,7 @@ impl WorldRenderer {
|
||||||
WorldView::Biomes => 0,
|
WorldView::Biomes => 0,
|
||||||
WorldView::Topography => 1,
|
WorldView::Topography => 1,
|
||||||
WorldView::Coastlines => 2,
|
WorldView::Coastlines => 2,
|
||||||
|
WorldView::Population => 3,
|
||||||
};
|
};
|
||||||
let mut overlay_num = 0;
|
let mut overlay_num = 0;
|
||||||
for overlay in render_settings.visible_overlays.iter() {
|
for overlay in render_settings.visible_overlays.iter() {
|
||||||
|
@ -292,6 +280,7 @@ impl WorldRenderer {
|
||||||
WorldView::Biomes => biome_color(world, cell),
|
WorldView::Biomes => biome_color(world, cell),
|
||||||
WorldView::Topography => altitude_contour_color(world, cell.altitude),
|
WorldView::Topography => altitude_contour_color(world, cell.altitude),
|
||||||
WorldView::Coastlines => coastline_color(world, cell),
|
WorldView::Coastlines => coastline_color(world, cell),
|
||||||
|
WorldView::Population => population_color(world, cell),
|
||||||
};
|
};
|
||||||
let mut normalizer = 1.0;
|
let mut normalizer = 1.0;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue