Add biomes!
06f11e44aaeb99bc73522e86a08abd61a9da2e58 0e4387490e18cef0bccda5e7233d6949845e9620 759b4c6c744d4a7f44efb656ab8c5e66d178283c af8b9e06e7935d17153c8603d2dcaf281de753c3
This commit is contained in:
parent
6a17e6df7b
commit
fb78c618e4
7 changed files with 635245 additions and 386343 deletions
1024415
planet.ron
1024415
planet.ron
File diff suppressed because it is too large
Load diff
127
planet/src/biome.rs
Normal file
127
planet/src/biome.rs
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[cfg(feature = "render")]
|
||||||
|
use bevy::render::color::Color;
|
||||||
|
|
||||||
|
use crate::World;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct Biome {
|
||||||
|
pub name: String,
|
||||||
|
#[cfg(feature = "render")]
|
||||||
|
pub color: Color,
|
||||||
|
pub min_altitude: f32,
|
||||||
|
pub max_altitude: f32,
|
||||||
|
pub min_rainfall: f32,
|
||||||
|
pub max_rainfall: f32,
|
||||||
|
pub min_temperature: f32,
|
||||||
|
pub max_temperature: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! biome_enum {
|
||||||
|
($($Variant:ident),*$(,)?) =>
|
||||||
|
{
|
||||||
|
#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
|
||||||
|
pub enum BiomeType {
|
||||||
|
$($Variant),*,
|
||||||
|
}
|
||||||
|
impl BiomeType {
|
||||||
|
pub const BIOMES: &'static [BiomeType] = &[$(BiomeType::$Variant),*];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
biome_enum!(IceCap, Ocean, Grassland, Forest, Taiga, Tundra, Desert, Rainforest);
|
||||||
|
|
||||||
|
impl From<BiomeType> for Biome {
|
||||||
|
fn from(biome_type: BiomeType) -> Biome {
|
||||||
|
match biome_type {
|
||||||
|
BiomeType::IceCap => Biome {
|
||||||
|
name: "Ice Cap".into(),
|
||||||
|
color: Color::rgb_u8(253, 244, 235),
|
||||||
|
min_altitude: World::MIN_ALTITUDE,
|
||||||
|
max_altitude: World::MAX_ALTITUDE,
|
||||||
|
min_rainfall: World::MIN_RAINFALL,
|
||||||
|
max_rainfall: World::MAX_RAINFALL,
|
||||||
|
min_temperature: World::MIN_TEMPERATURE,
|
||||||
|
max_temperature: -15.0,
|
||||||
|
},
|
||||||
|
BiomeType::Ocean => Biome {
|
||||||
|
name: "Ocean".into(),
|
||||||
|
color: Color::rgb_u8(28, 66, 84),
|
||||||
|
min_altitude: World::MIN_ALTITUDE,
|
||||||
|
max_altitude: 0.0,
|
||||||
|
min_rainfall: World::MIN_RAINFALL,
|
||||||
|
max_rainfall: World::MAX_RAINFALL,
|
||||||
|
min_temperature: -15.0,
|
||||||
|
max_temperature: World::MAX_TEMPERATURE,
|
||||||
|
},
|
||||||
|
BiomeType::Grassland => Biome {
|
||||||
|
name: "Grassland".into(),
|
||||||
|
color: Color::rgb_u8(167, 177, 84),
|
||||||
|
min_altitude: 0.0,
|
||||||
|
max_altitude: World::MAX_ALTITUDE,
|
||||||
|
min_rainfall: 25.0,
|
||||||
|
max_rainfall: 1475.0,
|
||||||
|
min_temperature: -5.0,
|
||||||
|
max_temperature: World::MAX_TEMPERATURE,
|
||||||
|
},
|
||||||
|
BiomeType::Forest => Biome {
|
||||||
|
name: "Forest".into(),
|
||||||
|
color: Color::rgb_u8(76, 132, 55),
|
||||||
|
min_altitude: 0.0,
|
||||||
|
max_altitude: World::MAX_ALTITUDE,
|
||||||
|
min_rainfall: 975.0,
|
||||||
|
max_rainfall: 2475.0,
|
||||||
|
min_temperature: -5.0,
|
||||||
|
max_temperature: World::MAX_TEMPERATURE,
|
||||||
|
},
|
||||||
|
BiomeType::Taiga => Biome {
|
||||||
|
name: "Taiga".into(),
|
||||||
|
color: Color::rgb_u8(43, 63, 40),
|
||||||
|
min_altitude: 0.0,
|
||||||
|
max_altitude: World::MAX_ALTITUDE,
|
||||||
|
min_rainfall: 475.0,
|
||||||
|
max_rainfall: World::MAX_RAINFALL,
|
||||||
|
min_temperature: -15.0,
|
||||||
|
max_temperature: -0.0,
|
||||||
|
},
|
||||||
|
BiomeType::Tundra => Biome {
|
||||||
|
name: "Tundra ".into(),
|
||||||
|
color: Color::rgb_u8(139, 139, 128),
|
||||||
|
min_altitude: 0.0,
|
||||||
|
max_altitude: World::MAX_ALTITUDE,
|
||||||
|
min_rainfall: World::MIN_RAINFALL,
|
||||||
|
max_rainfall: 725.0,
|
||||||
|
min_temperature: -20.0,
|
||||||
|
max_temperature: -0.0,
|
||||||
|
},
|
||||||
|
BiomeType::Desert => Biome {
|
||||||
|
name: "Desert ".into(),
|
||||||
|
color: Color::rgb_u8(253, 225, 171),
|
||||||
|
min_altitude: 0.0,
|
||||||
|
max_altitude: World::MAX_ALTITUDE,
|
||||||
|
min_rainfall: World::MIN_RAINFALL,
|
||||||
|
max_rainfall: 125.0,
|
||||||
|
min_temperature: -5.0,
|
||||||
|
max_temperature: World::MAX_TEMPERATURE,
|
||||||
|
},
|
||||||
|
BiomeType::Rainforest => Biome {
|
||||||
|
name: "Rainforest".into(),
|
||||||
|
color: Color::rgb_u8(59, 103, 43),
|
||||||
|
min_altitude: 0.0,
|
||||||
|
max_altitude: World::MAX_ALTITUDE,
|
||||||
|
min_rainfall: 1975.0,
|
||||||
|
max_rainfall: World::MAX_RAINFALL,
|
||||||
|
min_temperature: -5.0,
|
||||||
|
max_temperature: World::MAX_TEMPERATURE,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&BiomeType> for Biome {
|
||||||
|
fn from(biome_type: &BiomeType) -> Biome {
|
||||||
|
(*biome_type).into()
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,6 +34,8 @@
|
||||||
|
|
||||||
pub mod world;
|
pub mod world;
|
||||||
pub use world::*;
|
pub use world::*;
|
||||||
|
pub mod biome;
|
||||||
|
pub use biome::Biome;
|
||||||
pub mod world_manager;
|
pub mod world_manager;
|
||||||
pub use world_manager::*;
|
pub use world_manager::*;
|
||||||
pub mod math_util;
|
pub mod math_util;
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::{
|
||||||
|
|
||||||
// TODO: Logging doesn't seem to work here? Figure out why and fix
|
// TODO: Logging doesn't seem to work here? Figure out why and fix
|
||||||
|
|
||||||
use crate::perlin;
|
use crate::{biome::BiomeType, perlin, Biome};
|
||||||
use bevy::{log::info, math::Vec3A, prelude::Vec2, utils::default};
|
use bevy::{log::info, math::Vec3A, prelude::Vec2, utils::default};
|
||||||
use rand::{rngs::StdRng, Rng, SeedableRng};
|
use rand::{rngs::StdRng, Rng, SeedableRng};
|
||||||
|
|
||||||
|
@ -65,23 +65,13 @@ pub struct World {
|
||||||
pub rng: StdRng,
|
pub rng: StdRng,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
|
||||||
pub struct Biome {
|
|
||||||
pub altitude: f32,
|
|
||||||
pub rainfall: f32,
|
|
||||||
pub temperature: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||||
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 biome_presences: Vec<(BiomeType, f32)>,
|
||||||
pub rain_accumulated: f32,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub previous_rain_accumulated: f32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl World {
|
impl World {
|
||||||
|
@ -106,33 +96,32 @@ impl World {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const NUM_CONTINENTS: u8 = 7;
|
const NUM_CONTINENTS: u8 = 7;
|
||||||
pub const CONTINENT_FACTOR: f32 = 0.75;
|
const CONTINENT_MIN_WIDTH_FACTOR: f32 = 3.0;
|
||||||
pub const CONTINENT_MIN_WIDTH_FACTOR: f32 = 3.0;
|
const CONTINENT_MAX_WIDTH_FACTOR: f32 = 7.0;
|
||||||
pub const CONTINENT_MAX_WIDTH_FACTOR: f32 = 7.0;
|
|
||||||
|
|
||||||
pub const MIN_ALTITUDE: f32 = -10000.0;
|
pub(crate) const MIN_ALTITUDE: f32 = -15000.0;
|
||||||
pub const MAX_ALTITUDE: f32 = 10000.0;
|
pub(crate) const MAX_ALTITUDE: f32 = 15000.0;
|
||||||
pub const ALTITUDE_SPAN: f32 = World::MAX_ALTITUDE - World::MIN_ALTITUDE;
|
const ALTITUDE_SPAN: f32 = World::MAX_ALTITUDE - World::MIN_ALTITUDE;
|
||||||
|
|
||||||
pub const MOUNTAIN_RANGE_MIX_FACTOR: f32 = 0.075;
|
const MOUNTAIN_RANGE_MIX_FACTOR: f32 = 0.075;
|
||||||
pub const MOUNTAIN_RANGE_WIDTH_FACTOR: f32 = 25.0;
|
const MOUNTAIN_RANGE_WIDTH_FACTOR: f32 = 25.0;
|
||||||
|
|
||||||
pub const TERRAIN_NOISE_FACTOR_1: f32 = 0.2;
|
const TERRAIN_NOISE_FACTOR_1: f32 = 0.15;
|
||||||
pub const TERRAIN_NOISE_FACTOR_2: f32 = 0.15;
|
const TERRAIN_NOISE_FACTOR_2: f32 = 0.15;
|
||||||
pub const TERRAIN_NOISE_FACTOR_3: f32 = 0.1;
|
const TERRAIN_NOISE_FACTOR_3: f32 = 0.1;
|
||||||
|
const TERRAIN_NOISE_FACTOR_4: f32 = 2.5;
|
||||||
|
|
||||||
pub const MIN_RAINFALL: f32 = 0.0;
|
pub(crate) const MIN_RAINFALL: f32 = 0.0;
|
||||||
pub const MAX_RAINFALL: f32 = 5000.0;
|
pub(crate) const MAX_RAINFALL: f32 = 7500.0;
|
||||||
pub const RAINFALL_SPAN: f32 = World::MAX_RAINFALL - World::MIN_RAINFALL;
|
const RAINFALL_SPAN: f32 = World::MAX_RAINFALL - World::MIN_RAINFALL;
|
||||||
pub const RAINFALL_ALTITUDE_FACTOR: f32 = 1.0;
|
const RAINFALL_DRYNESS_FACTOR: f32 = 0.005;
|
||||||
pub const RAINFALL_DRYNESS_FACTOR: f32 = 0.001;
|
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 MIN_TEMPERATURE: f32 = -50.0;
|
pub(crate) const MIN_TEMPERATURE: f32 = -60.0;
|
||||||
pub const MAX_TEMPERATURE: f32 = 30.0;
|
pub(crate) const MAX_TEMPERATURE: f32 = 30.0;
|
||||||
pub const TEMPERATURE_SPAN: f32 = World::MAX_TEMPERATURE - World::MIN_RAINFALL;
|
const TEMPERATURE_SPAN: f32 = World::MAX_TEMPERATURE - World::MIN_TEMPERATURE;
|
||||||
pub const TEMPERATURE_ALTITUDE_FACTOR: f32 = 1.0;
|
const TEMPERATURE_ALTITUDE_FACTOR: f32 = 1.0;
|
||||||
|
|
||||||
pub fn generate(&mut self) -> Result<(), WorldGenError> {
|
pub fn generate(&mut self) -> Result<(), WorldGenError> {
|
||||||
if let Err(err) = self.generate_altitude() {
|
if let Err(err) = self.generate_altitude() {
|
||||||
|
@ -145,6 +134,8 @@ impl World {
|
||||||
return Err(WorldGenError::CartesianError(err));
|
return Err(WorldGenError::CartesianError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.generate_biomes();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,9 +245,16 @@ impl World {
|
||||||
World::MOUNTAIN_RANGE_MIX_FACTOR,
|
World::MOUNTAIN_RANGE_MIX_FACTOR,
|
||||||
)),
|
)),
|
||||||
value_3,
|
value_3,
|
||||||
World::TERRAIN_NOISE_FACTOR_1 * 1.5,
|
World::TERRAIN_NOISE_FACTOR_1 * World::TERRAIN_NOISE_FACTOR_4,
|
||||||
) * mix_values(1.0, value_4, World::TERRAIN_NOISE_FACTOR_2 * 1.5)
|
) * mix_values(
|
||||||
* mix_values(1.0, value_5, World::TERRAIN_NOISE_FACTOR_3 * 1.5);
|
1.0,
|
||||||
|
value_4,
|
||||||
|
World::TERRAIN_NOISE_FACTOR_2 * World::TERRAIN_NOISE_FACTOR_4,
|
||||||
|
) * mix_values(
|
||||||
|
1.0,
|
||||||
|
value_5,
|
||||||
|
World::TERRAIN_NOISE_FACTOR_3 * World::TERRAIN_NOISE_FACTOR_4,
|
||||||
|
);
|
||||||
|
|
||||||
let mut value_d = mix_values(value_a, value_c, 0.25);
|
let mut value_d = mix_values(value_a, value_c, 0.25);
|
||||||
value_d = mix_values(value_d, value_c, 0.1);
|
value_d = mix_values(value_d, value_c, 0.1);
|
||||||
|
@ -364,10 +362,10 @@ impl World {
|
||||||
(altitude - (offset_altitude * 1.5) - (offset_altitude_2 * 1.5))
|
(altitude - (offset_altitude * 1.5) - (offset_altitude_2 * 1.5))
|
||||||
/ World::MAX_ALTITUDE;
|
/ World::MAX_ALTITUDE;
|
||||||
|
|
||||||
let normalized_rainfall = mix_values(latitude_modifier_1, altitude_modifier, 0.6);
|
let rainfall_value = mix_values(latitude_modifier_1, altitude_modifier, 0.63);
|
||||||
let rainfall = f32::min(
|
let rainfall = f32::min(
|
||||||
World::MAX_RAINFALL,
|
World::MAX_RAINFALL,
|
||||||
World::calculate_rainfall(normalized_rainfall),
|
World::calculate_rainfall(rainfall_value),
|
||||||
);
|
);
|
||||||
|
|
||||||
cell.rainfall = rainfall;
|
cell.rainfall = rainfall;
|
||||||
|
@ -408,17 +406,14 @@ impl World {
|
||||||
|
|
||||||
let cell = &mut self.terrain[y][x];
|
let cell = &mut self.terrain[y][x];
|
||||||
|
|
||||||
|
let latitude_modifer = mix_values(alpha, random_noise * PI, 0.1);
|
||||||
let altitude_factor = f32::max(
|
let altitude_factor = f32::max(
|
||||||
0.0,
|
0.0,
|
||||||
(cell.altitude / World::MAX_ALTITUDE)
|
(cell.altitude / World::MAX_ALTITUDE) * World::TEMPERATURE_ALTITUDE_FACTOR,
|
||||||
* 2.5
|
|
||||||
* World::TEMPERATURE_ALTITUDE_FACTOR,
|
|
||||||
);
|
);
|
||||||
|
let temperature =
|
||||||
|
World::calculate_temperature(f32::sin(latitude_modifer) - altitude_factor);
|
||||||
|
|
||||||
let latitude_modifer = (alpha * 0.8) + (random_noise * 0.2 * PI);
|
|
||||||
let base_temperature = World::calculate_temperature(f32::sin(latitude_modifer));
|
|
||||||
|
|
||||||
let temperature = base_temperature * (1.0 - altitude_factor);
|
|
||||||
cell.temperature = temperature;
|
cell.temperature = temperature;
|
||||||
|
|
||||||
if temperature > self.max_temperature {
|
if temperature > self.max_temperature {
|
||||||
|
@ -440,4 +435,83 @@ impl World {
|
||||||
World::MAX_TEMPERATURE,
|
World::MAX_TEMPERATURE,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_biomes(&mut self) {
|
||||||
|
for y in 0..self.terrain.len() {
|
||||||
|
for x in 0..self.terrain[y].len() {
|
||||||
|
let cell = &self.terrain[y][x];
|
||||||
|
|
||||||
|
let mut total_presence = 0.0;
|
||||||
|
|
||||||
|
let mut biome_presences = vec![];
|
||||||
|
for biome_type in BiomeType::BIOMES {
|
||||||
|
let presence = self.biome_presence(cell, &biome_type.into());
|
||||||
|
|
||||||
|
if presence <= 0.0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
biome_presences.push((*biome_type, presence));
|
||||||
|
total_presence += presence;
|
||||||
|
}
|
||||||
|
self.terrain[y][x].biome_presences = biome_presences
|
||||||
|
.iter()
|
||||||
|
.map(|(biome_type, presence)| (*biome_type, presence / total_presence))
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn biome_presence(&self, cell: &TerrainCell, biome: &Biome) -> f32 {
|
||||||
|
let mut presence = 0.0;
|
||||||
|
let altitude_diff = cell.altitude - biome.min_altitude;
|
||||||
|
if altitude_diff < 0.0 {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let altitude_factor = altitude_diff / (biome.max_altitude - biome.min_altitude);
|
||||||
|
if altitude_factor > 1.0 {
|
||||||
|
return 0.0;
|
||||||
|
};
|
||||||
|
|
||||||
|
presence += if altitude_factor > 0.5 {
|
||||||
|
1.0 - altitude_factor
|
||||||
|
} else {
|
||||||
|
altitude_factor
|
||||||
|
};
|
||||||
|
|
||||||
|
let rainfall_diff = cell.rainfall - biome.min_rainfall;
|
||||||
|
if rainfall_diff < 0.0 {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rainfall_factor = rainfall_diff / (biome.max_rainfall - biome.min_rainfall);
|
||||||
|
if rainfall_factor > 1.0 {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
presence += if rainfall_factor > 0.5 {
|
||||||
|
1.0 - rainfall_factor
|
||||||
|
} else {
|
||||||
|
rainfall_factor
|
||||||
|
};
|
||||||
|
|
||||||
|
let temperature_diff = cell.temperature - biome.min_temperature;
|
||||||
|
if temperature_diff < 0.0 {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let temperature_factor = temperature_diff / (biome.max_temperature - biome.min_temperature);
|
||||||
|
if temperature_factor > 1.0 {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
presence += if temperature_factor > 0.5 {
|
||||||
|
1.0 - temperature_factor
|
||||||
|
} else {
|
||||||
|
temperature_factor
|
||||||
|
};
|
||||||
|
|
||||||
|
presence
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
use crate::TerrainCell;
|
use crate::TerrainCell;
|
||||||
use crate::{World, WorldGenError};
|
use crate::{Biome, World, WorldGenError};
|
||||||
#[cfg(all(feature = "debug", feature = "render"))]
|
#[cfg(all(feature = "debug", feature = "render"))]
|
||||||
use bevy::log::debug;
|
use bevy::log::debug;
|
||||||
use bevy::log::warn;
|
use bevy::log::warn;
|
||||||
|
@ -94,6 +94,8 @@ pub struct WorldManager {
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
temperature_visible: bool,
|
temperature_visible: bool,
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
|
biomes_visible: bool,
|
||||||
|
#[cfg(feature = "render")]
|
||||||
contours: bool,
|
contours: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,6 +110,8 @@ impl WorldManager {
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
temperature_visible: false,
|
temperature_visible: false,
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
|
biomes_visible: false,
|
||||||
|
#[cfg(feature = "render")]
|
||||||
contours: true,
|
contours: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,6 +212,17 @@ impl WorldManager {
|
||||||
self.temperature_visible = !self.temperature_visible;
|
self.temperature_visible = !self.temperature_visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "render")]
|
||||||
|
pub fn toggle_biomes(&mut self) {
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
|
if self.temperature_visible {
|
||||||
|
debug!("Turning biomes off");
|
||||||
|
} else {
|
||||||
|
debug!("Turning biomes on");
|
||||||
|
}
|
||||||
|
self.biomes_visible = !self.biomes_visible;
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
pub fn toggle_contours(&mut self) {
|
pub fn toggle_contours(&mut self) {
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
|
@ -237,6 +252,10 @@ impl WorldManager {
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
fn generate_color(&self, cell: &TerrainCell) -> Color {
|
fn generate_color(&self, cell: &TerrainCell) -> Color {
|
||||||
|
if self.biomes_visible {
|
||||||
|
return WorldManager::biome_color(cell);
|
||||||
|
}
|
||||||
|
|
||||||
let altitude_color = if self.contours {
|
let altitude_color = if self.contours {
|
||||||
WorldManager::altitude_contour_color(cell.altitude)
|
WorldManager::altitude_contour_color(cell.altitude)
|
||||||
} else {
|
} else {
|
||||||
|
@ -245,9 +264,9 @@ impl WorldManager {
|
||||||
|
|
||||||
let mut layer_count = 1.0;
|
let mut layer_count = 1.0;
|
||||||
|
|
||||||
let mut r = altitude_color.r();
|
let mut red = altitude_color.r();
|
||||||
let mut g = altitude_color.g();
|
let mut green = altitude_color.g();
|
||||||
let mut b = altitude_color.b();
|
let mut blue = altitude_color.b();
|
||||||
|
|
||||||
if self.rainfall_visible {
|
if self.rainfall_visible {
|
||||||
layer_count += 1.0;
|
layer_count += 1.0;
|
||||||
|
@ -258,9 +277,9 @@ impl WorldManager {
|
||||||
// WorldManager::rainfall_color(cell.rainfall)
|
// WorldManager::rainfall_color(cell.rainfall)
|
||||||
// };
|
// };
|
||||||
|
|
||||||
r += rainfall_color.r();
|
red += rainfall_color.r();
|
||||||
g += rainfall_color.g();
|
green += rainfall_color.g();
|
||||||
b += rainfall_color.b();
|
blue += rainfall_color.b();
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.temperature_visible {
|
if self.temperature_visible {
|
||||||
|
@ -272,12 +291,12 @@ impl WorldManager {
|
||||||
// WorldManager::temperature_color(cell.temperature)
|
// WorldManager::temperature_color(cell.temperature)
|
||||||
// };
|
// };
|
||||||
|
|
||||||
r += temperature_color.r();
|
red += temperature_color.r();
|
||||||
g += temperature_color.g();
|
green += temperature_color.g();
|
||||||
b += temperature_color.b();
|
blue += temperature_color.b();
|
||||||
}
|
}
|
||||||
|
|
||||||
Color::rgb(r / layer_count, g / layer_count, b / layer_count)
|
Color::rgb(red / layer_count, green / layer_count, blue / layer_count)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
|
@ -318,15 +337,6 @@ impl WorldManager {
|
||||||
Color::rgb(0.0, shade_value, 0.0)
|
Color::rgb(0.0, shade_value, 0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
|
||||||
fn rainfall_color(rainfall: f32) -> Color {
|
|
||||||
if rainfall <= 0.0 {
|
|
||||||
Color::BLACK
|
|
||||||
} else {
|
|
||||||
Color::rgb(0.0, rainfall / World::MAX_RAINFALL, 0.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
fn temperature_contour_color(&self, temperature: f32) -> Color {
|
fn temperature_contour_color(&self, temperature: f32) -> Color {
|
||||||
let mut shade_value = 1.0;
|
let mut shade_value = 1.0;
|
||||||
|
@ -341,14 +351,19 @@ impl WorldManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
fn temperature_color(temperature: f32) -> Color {
|
fn biome_color(cell: &TerrainCell) -> Color {
|
||||||
let normalized_temperature = WorldManager::normalize_temperature(temperature);
|
cell.biome_presences
|
||||||
Color::rgb(normalized_temperature, 1.0 - normalized_temperature, 0.0)
|
.iter()
|
||||||
}
|
.fold(Color::BLACK, |color, (biome_type, presence)| {
|
||||||
|
let biome: Biome = (*biome_type).into();
|
||||||
|
let biome_color = biome.color;
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
Color::rgb(
|
||||||
fn normalize_temperature(temperature: f32) -> f32 {
|
color.r() + (biome_color.r() * presence),
|
||||||
(temperature - World::MIN_TEMPERATURE) / World::TEMPERATURE_SPAN
|
color.g() + (biome_color.g() * presence),
|
||||||
|
color.b() + (biome_color.b() * presence),
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
|
|
|
@ -2,28 +2,29 @@
|
||||||
use bevy::ecs::component::Component;
|
use bevy::ecs::component::Component;
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
macro_rules! define_enum {
|
macro_rules! toolbar_enum {
|
||||||
($Name:ident { $($Variant:ident),* $(,)* }) =>
|
($($Variant:ident),*$(,)?) =>
|
||||||
{
|
{
|
||||||
#[derive(Debug, Component, Copy, Clone)]
|
#[derive(Debug, Component, Copy, Clone)]
|
||||||
pub enum $Name {
|
pub enum ToolbarButton {
|
||||||
$($Variant),*,
|
$($Variant),*,
|
||||||
}
|
}
|
||||||
impl $Name {
|
impl ToolbarButton {
|
||||||
pub const ITEMS: &'static [$Name] = &[$($Name::$Variant),*];
|
pub const BUTTONS: &'static [ToolbarButton] = &[$(ToolbarButton::$Variant),*];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
define_enum!(ToolbarButton {
|
toolbar_enum!(
|
||||||
GenerateWorld,
|
GenerateWorld,
|
||||||
SaveWorld,
|
SaveWorld,
|
||||||
LoadWorld,
|
LoadWorld,
|
||||||
Rainfall,
|
Rainfall,
|
||||||
Temperature,
|
Temperature,
|
||||||
|
Biomes,
|
||||||
Contours,
|
Contours,
|
||||||
});
|
);
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
impl From<ToolbarButton> for &'static str {
|
impl From<ToolbarButton> for &'static str {
|
||||||
|
@ -32,6 +33,7 @@ impl From<ToolbarButton> for &'static str {
|
||||||
ToolbarButton::Rainfall => "Toggle rainfall",
|
ToolbarButton::Rainfall => "Toggle rainfall",
|
||||||
ToolbarButton::Temperature => "Toggle temperature",
|
ToolbarButton::Temperature => "Toggle temperature",
|
||||||
ToolbarButton::Contours => "Toggle contours",
|
ToolbarButton::Contours => "Toggle contours",
|
||||||
|
ToolbarButton::Biomes => "Toggle biomes",
|
||||||
ToolbarButton::GenerateWorld => "Generate new world",
|
ToolbarButton::GenerateWorld => "Generate new world",
|
||||||
ToolbarButton::SaveWorld => "Save",
|
ToolbarButton::SaveWorld => "Save",
|
||||||
ToolbarButton::LoadWorld => "Load",
|
ToolbarButton::LoadWorld => "Load",
|
||||||
|
@ -41,14 +43,7 @@ impl From<ToolbarButton> for &'static str {
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
impl From<&ToolbarButton> for &'static str {
|
impl From<&ToolbarButton> for &'static str {
|
||||||
fn from(button: &ToolbarButton) -> Self {
|
fn from(button: &ToolbarButton) -> Self {
|
||||||
match button {
|
(*button).into()
|
||||||
ToolbarButton::Rainfall => "Toggle rainfall",
|
|
||||||
ToolbarButton::Temperature => "Toggle temperature",
|
|
||||||
ToolbarButton::Contours => "Toggle contours",
|
|
||||||
ToolbarButton::GenerateWorld => "Generate new world",
|
|
||||||
ToolbarButton::SaveWorld => "Save",
|
|
||||||
ToolbarButton::LoadWorld => "Load",
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
42
src/main.rs
42
src/main.rs
|
@ -82,6 +82,7 @@ use bevy::{
|
||||||
window::{CursorIcon, WindowDescriptor, Windows},
|
window::{CursorIcon, WindowDescriptor, Windows},
|
||||||
winit::WinitSettings,
|
winit::WinitSettings,
|
||||||
};
|
};
|
||||||
|
use planet::Biome;
|
||||||
|
|
||||||
#[cfg(all(feature = "debug", feature = "render"))]
|
#[cfg(all(feature = "debug", feature = "render"))]
|
||||||
use bevy::{
|
use bevy::{
|
||||||
|
@ -154,6 +155,12 @@ fn handle_toolbar_button(
|
||||||
world_manager.toggle_temperature();
|
world_manager.toggle_temperature();
|
||||||
refresh_world_texture(&mut images, &world_manager);
|
refresh_world_texture(&mut images, &world_manager);
|
||||||
}
|
}
|
||||||
|
ToolbarButton::Biomes => {
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
|
debug!("Toggling biomes");
|
||||||
|
world_manager.toggle_biomes();
|
||||||
|
refresh_world_texture(&mut images, &world_manager);
|
||||||
|
}
|
||||||
ToolbarButton::Contours => {
|
ToolbarButton::Contours => {
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
debug!("Toggling contours");
|
debug!("Toggling contours");
|
||||||
|
@ -250,7 +257,7 @@ fn update_info_panel(
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
{
|
{
|
||||||
format!(
|
format!(
|
||||||
"FPS: ~{}\nMouse position: {}\nAltitude: {}\nRainfall: {}\nTemperature: {}",
|
"FPS: ~{}\nMouse position: {}\nAltitude: {}\nRainfall: {}\nTemperature: {}\n\n{}",
|
||||||
match diagnostics.get_measurement(FrameTimeDiagnosticsPlugin::FPS) {
|
match diagnostics.get_measurement(FrameTimeDiagnosticsPlugin::FPS) {
|
||||||
None => f64::NAN,
|
None => f64::NAN,
|
||||||
Some(fps) => fps.value.round(),
|
Some(fps) => fps.value.round(),
|
||||||
|
@ -258,15 +265,40 @@ fn update_info_panel(
|
||||||
*cursor_position,
|
*cursor_position,
|
||||||
cell.altitude,
|
cell.altitude,
|
||||||
cell.rainfall,
|
cell.rainfall,
|
||||||
cell.temperature
|
cell.temperature,
|
||||||
|
cell.biome_presences
|
||||||
|
.iter()
|
||||||
|
.map(|(biome_type, presence)| {
|
||||||
|
format!(
|
||||||
|
"Biome: {} ({:.2}%)",
|
||||||
|
(<Biome>::from(biome_type).name),
|
||||||
|
presence * 100.0
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("\n")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "debug"))]
|
#[cfg(not(feature = "debug"))]
|
||||||
{
|
{
|
||||||
format!(
|
format!(
|
||||||
"Mouse position: {}\nAltitude: {}\nRainfall: {}\nTemperature: {}",
|
"Mouse position: {}\nAltitude: {}\nRainfall: {}\nTemperature: {}\n{}",
|
||||||
*cursor_position, cell.altitude, cell.rainfall, cell.temperature
|
*cursor_position,
|
||||||
|
cell.altitude,
|
||||||
|
cell.rainfall,
|
||||||
|
cell.temperature,
|
||||||
|
cell.biome_presences
|
||||||
|
.iter()
|
||||||
|
.map(|(biome_type, presence)| {
|
||||||
|
format!(
|
||||||
|
"Biome: {} ({:.2}%)",
|
||||||
|
(<Biome>::from(biome_type).name),
|
||||||
|
presence * 100.0
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("\n")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -422,7 +454,7 @@ fn generate_graphics(
|
||||||
..default()
|
..default()
|
||||||
})
|
})
|
||||||
.with_children(|button_box| {
|
.with_children(|button_box| {
|
||||||
ToolbarButton::ITEMS.iter().for_each(|&button_type| {
|
ToolbarButton::BUTTONS.iter().for_each(|&button_type| {
|
||||||
_ = button_box
|
_ = button_box
|
||||||
.spawn_bundle(toolbar_button())
|
.spawn_bundle(toolbar_button())
|
||||||
.with_children(|button| {
|
.with_children(|button| {
|
||||||
|
|
Loading…
Reference in a new issue