Move planet rendering from WorldManager to a trait
This commit is contained in:
parent
cc2bf6f535
commit
616a7d0697
15 changed files with 293 additions and 294 deletions
|
@ -27,7 +27,7 @@ codegen-units = 1
|
||||||
# bevy/trace_chrome for tracing by function
|
# bevy/trace_chrome for tracing by function
|
||||||
# https://github.com/bevyengine/bevy/blob/main/docs/profiling.md
|
# https://github.com/bevyengine/bevy/blob/main/docs/profiling.md
|
||||||
logging = ["planet/logging"]
|
logging = ["planet/logging"]
|
||||||
render = ["bevy/bevy_asset", "bevy/bevy_winit", "bevy/x11", "bevy/wayland", "bevy/render", "planet/render", "dep:fxhash", "dep:bevy_egui", "dep:tinyfiledialogs"]
|
render = ["bevy/bevy_asset", "bevy/bevy_winit", "bevy/x11", "bevy/wayland", "bevy/render", "dep:fxhash", "dep:bevy_egui", "dep:tinyfiledialogs"]
|
||||||
default = ["render", "logging"]
|
default = ["render", "logging"]
|
||||||
|
|
||||||
[dependencies.planet]
|
[dependencies.planet]
|
||||||
|
|
|
@ -8,8 +8,7 @@ release = { strip = "symbols", lto = "thin", opt-level = "z" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
logging = []
|
logging = []
|
||||||
render = ["bevy/render"]
|
default = ["logging"]
|
||||||
default = ["render", "logging"]
|
|
||||||
|
|
||||||
[dependencies.rand]
|
[dependencies.rand]
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::{macros::iterable_enum, World};
|
use {
|
||||||
#[cfg(feature = "render")]
|
crate::{macros::iterable_enum, World},
|
||||||
use bevy::render::color::Color;
|
bevy::render::color::Color,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct BiomeStats {
|
pub struct BiomeStats {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
#[cfg(feature = "render")]
|
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
pub min_altitude: f32,
|
pub min_altitude: f32,
|
||||||
pub max_altitude: f32,
|
pub max_altitude: f32,
|
||||||
|
@ -30,90 +30,82 @@ impl From<BiomeType> for BiomeStats {
|
||||||
fn from(biome_type: BiomeType) -> BiomeStats {
|
fn from(biome_type: BiomeType) -> BiomeStats {
|
||||||
match biome_type {
|
match biome_type {
|
||||||
BiomeType::IceCap => BiomeStats {
|
BiomeType::IceCap => BiomeStats {
|
||||||
name: "Ice Cap".into(),
|
name: "Ice Cap".into(),
|
||||||
#[cfg(feature = "render")]
|
color: Color::rgb_u8(253, 244, 235),
|
||||||
color: Color::rgb_u8(253, 244, 235),
|
min_altitude: World::MIN_ALTITUDE,
|
||||||
min_altitude: World::MIN_ALTITUDE,
|
max_altitude: World::MAX_ALTITUDE,
|
||||||
max_altitude: World::MAX_ALTITUDE,
|
min_rainfall: World::MIN_RAINFALL,
|
||||||
min_rainfall: World::MIN_RAINFALL,
|
max_rainfall: World::MAX_RAINFALL,
|
||||||
max_rainfall: World::MAX_RAINFALL,
|
|
||||||
min_temperature: World::MIN_TEMPERATURE,
|
min_temperature: World::MIN_TEMPERATURE,
|
||||||
max_temperature: -15.0,
|
max_temperature: -15.0,
|
||||||
},
|
},
|
||||||
BiomeType::Ocean => BiomeStats {
|
BiomeType::Ocean => BiomeStats {
|
||||||
name: "Ocean".into(),
|
name: "Ocean".into(),
|
||||||
#[cfg(feature = "render")]
|
color: Color::rgb_u8(28, 66, 84),
|
||||||
color: Color::rgb_u8(28, 66, 84),
|
min_altitude: World::MIN_ALTITUDE,
|
||||||
min_altitude: World::MIN_ALTITUDE,
|
max_altitude: 0.0,
|
||||||
max_altitude: 0.0,
|
min_rainfall: World::MIN_RAINFALL,
|
||||||
min_rainfall: World::MIN_RAINFALL,
|
max_rainfall: World::MAX_RAINFALL,
|
||||||
max_rainfall: World::MAX_RAINFALL,
|
|
||||||
min_temperature: -15.0,
|
min_temperature: -15.0,
|
||||||
max_temperature: World::MAX_TEMPERATURE,
|
max_temperature: World::MAX_TEMPERATURE,
|
||||||
},
|
},
|
||||||
BiomeType::Grassland => BiomeStats {
|
BiomeType::Grassland => BiomeStats {
|
||||||
name: "Grassland".into(),
|
name: "Grassland".into(),
|
||||||
#[cfg(feature = "render")]
|
color: Color::rgb_u8(167, 177, 84),
|
||||||
color: Color::rgb_u8(167, 177, 84),
|
min_altitude: 0.0,
|
||||||
min_altitude: 0.0,
|
max_altitude: World::MAX_ALTITUDE,
|
||||||
max_altitude: World::MAX_ALTITUDE,
|
min_rainfall: 25.0,
|
||||||
min_rainfall: 25.0,
|
max_rainfall: 1475.0,
|
||||||
max_rainfall: 1475.0,
|
|
||||||
min_temperature: -5.0,
|
min_temperature: -5.0,
|
||||||
max_temperature: World::MAX_TEMPERATURE,
|
max_temperature: World::MAX_TEMPERATURE,
|
||||||
},
|
},
|
||||||
BiomeType::Forest => BiomeStats {
|
BiomeType::Forest => BiomeStats {
|
||||||
name: "Forest".into(),
|
name: "Forest".into(),
|
||||||
#[cfg(feature = "render")]
|
color: Color::rgb_u8(76, 132, 55),
|
||||||
color: Color::rgb_u8(76, 132, 55),
|
min_altitude: 0.0,
|
||||||
min_altitude: 0.0,
|
max_altitude: World::MAX_ALTITUDE,
|
||||||
max_altitude: World::MAX_ALTITUDE,
|
min_rainfall: 975.0,
|
||||||
min_rainfall: 975.0,
|
max_rainfall: 2475.0,
|
||||||
max_rainfall: 2475.0,
|
|
||||||
min_temperature: -5.0,
|
min_temperature: -5.0,
|
||||||
max_temperature: World::MAX_TEMPERATURE,
|
max_temperature: World::MAX_TEMPERATURE,
|
||||||
},
|
},
|
||||||
BiomeType::Taiga => BiomeStats {
|
BiomeType::Taiga => BiomeStats {
|
||||||
name: "Taiga".into(),
|
name: "Taiga".into(),
|
||||||
#[cfg(feature = "render")]
|
color: Color::rgb_u8(43, 63, 40),
|
||||||
color: Color::rgb_u8(43, 63, 40),
|
min_altitude: 0.0,
|
||||||
min_altitude: 0.0,
|
max_altitude: World::MAX_ALTITUDE,
|
||||||
max_altitude: World::MAX_ALTITUDE,
|
min_rainfall: 475.0,
|
||||||
min_rainfall: 475.0,
|
max_rainfall: World::MAX_RAINFALL,
|
||||||
max_rainfall: World::MAX_RAINFALL,
|
|
||||||
min_temperature: -15.0,
|
min_temperature: -15.0,
|
||||||
max_temperature: -0.0,
|
max_temperature: -0.0,
|
||||||
},
|
},
|
||||||
BiomeType::Tundra => BiomeStats {
|
BiomeType::Tundra => BiomeStats {
|
||||||
name: "Tundra ".into(),
|
name: "Tundra ".into(),
|
||||||
#[cfg(feature = "render")]
|
color: Color::rgb_u8(139, 139, 128),
|
||||||
color: Color::rgb_u8(139, 139, 128),
|
min_altitude: 0.0,
|
||||||
min_altitude: 0.0,
|
max_altitude: World::MAX_ALTITUDE,
|
||||||
max_altitude: World::MAX_ALTITUDE,
|
min_rainfall: World::MIN_RAINFALL,
|
||||||
min_rainfall: World::MIN_RAINFALL,
|
max_rainfall: 725.0,
|
||||||
max_rainfall: 725.0,
|
|
||||||
min_temperature: -20.0,
|
min_temperature: -20.0,
|
||||||
max_temperature: -0.0,
|
max_temperature: -0.0,
|
||||||
},
|
},
|
||||||
BiomeType::Desert => BiomeStats {
|
BiomeType::Desert => BiomeStats {
|
||||||
name: "Desert ".into(),
|
name: "Desert ".into(),
|
||||||
#[cfg(feature = "render")]
|
color: Color::rgb_u8(253, 225, 171),
|
||||||
color: Color::rgb_u8(253, 225, 171),
|
min_altitude: 0.0,
|
||||||
min_altitude: 0.0,
|
max_altitude: World::MAX_ALTITUDE,
|
||||||
max_altitude: World::MAX_ALTITUDE,
|
min_rainfall: World::MIN_RAINFALL,
|
||||||
min_rainfall: World::MIN_RAINFALL,
|
max_rainfall: 125.0,
|
||||||
max_rainfall: 125.0,
|
|
||||||
min_temperature: -5.0,
|
min_temperature: -5.0,
|
||||||
max_temperature: World::MAX_TEMPERATURE,
|
max_temperature: World::MAX_TEMPERATURE,
|
||||||
},
|
},
|
||||||
BiomeType::Rainforest => BiomeStats {
|
BiomeType::Rainforest => BiomeStats {
|
||||||
name: "Rainforest".into(),
|
name: "Rainforest".into(),
|
||||||
#[cfg(feature = "render")]
|
color: Color::rgb_u8(59, 103, 43),
|
||||||
color: Color::rgb_u8(59, 103, 43),
|
min_altitude: 0.0,
|
||||||
min_altitude: 0.0,
|
max_altitude: World::MAX_ALTITUDE,
|
||||||
max_altitude: World::MAX_ALTITUDE,
|
min_rainfall: 1975.0,
|
||||||
min_rainfall: 1975.0,
|
max_rainfall: World::MAX_RAINFALL,
|
||||||
max_rainfall: World::MAX_RAINFALL,
|
|
||||||
min_temperature: -5.0,
|
min_temperature: -5.0,
|
||||||
max_temperature: World::MAX_TEMPERATURE,
|
max_temperature: World::MAX_TEMPERATURE,
|
||||||
},
|
},
|
||||||
|
|
|
@ -41,8 +41,4 @@ pub use world_manager::WorldManager;
|
||||||
pub(crate) mod macros;
|
pub(crate) mod macros;
|
||||||
pub mod math_util;
|
pub mod math_util;
|
||||||
pub mod perlin;
|
pub mod perlin;
|
||||||
#[cfg(feature = "render")]
|
|
||||||
pub mod rendering;
|
|
||||||
#[cfg(feature = "render")]
|
|
||||||
pub use rendering::*;
|
|
||||||
pub mod saving;
|
pub mod saving;
|
||||||
|
|
|
@ -14,7 +14,8 @@ const PERMUTATION: [u8; 256] = [
|
||||||
128, 195, 78, 66, 215, 61, 156, 180,
|
128, 195, 78, 66, 215, 61, 156, 180,
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn get_value(x: f32, y: f32, z: f32) -> f32 {
|
#[must_use]
|
||||||
|
pub fn perlin_value(x: f32, y: f32, z: f32) -> f32 {
|
||||||
let p = [PERMUTATION, PERMUTATION].concat();
|
let p = [PERMUTATION, PERMUTATION].concat();
|
||||||
|
|
||||||
let fx: i32 = f32::floor(x) as i32;
|
let fx: i32 = f32::floor(x) as i32;
|
||||||
|
@ -68,10 +69,12 @@ pub fn get_value(x: f32, y: f32, z: f32) -> f32 {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
fn fade(t: f32) -> f32 {
|
fn fade(t: f32) -> f32 {
|
||||||
t * t * t * (t * (t * 6.0 - 15.0) + 10.0)
|
t * t * t * (t * (t * 6.0 - 15.0) + 10.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
fn grad(hash: u8, x: f32, y: f32, z: f32) -> f32 {
|
fn grad(hash: u8, x: f32, y: f32, z: f32) -> f32 {
|
||||||
let h = hash & 15;
|
let h = hash & 15;
|
||||||
let u = if h < 8 { x } else { y };
|
let u = if h < 8 { x } else { y };
|
||||||
|
@ -86,10 +89,12 @@ fn grad(hash: u8, x: f32, y: f32, z: f32) -> f32 {
|
||||||
(if h & 1 == 0 { u } else { -u }) + (if h & 2 == 0 { v } else { -v })
|
(if h & 1 == 0 { u } else { -u }) + (if h & 2 == 0 { v } else { -v })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
fn lerp(t: f32, a: f32, b: f32) -> f32 {
|
fn lerp(t: f32, a: f32, b: f32) -> f32 {
|
||||||
a + t * (b - a)
|
a + t * (b - a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
fn scale(n: f32) -> f32 {
|
fn scale(n: f32) -> f32 {
|
||||||
(1.0 + n) / 2.0
|
(1.0 + n) / 2.0
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
#[cfg(feature = "render")]
|
|
||||||
use bevy::asset::HandleId;
|
|
||||||
use {crate::macros::iterable_enum, bevy::utils::HashSet};
|
|
||||||
|
|
||||||
iterable_enum!(WorldView { Biomes, Topography });
|
|
||||||
iterable_enum!(WorldOverlay {
|
|
||||||
Temperature,
|
|
||||||
Rainfall
|
|
||||||
});
|
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct WorldRenderSettings {
|
|
||||||
pub map_image_handle_id: Option<HandleId>,
|
|
||||||
|
|
||||||
visible_overlays: HashSet<WorldOverlay>,
|
|
||||||
pub view: WorldView,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
|
||||||
impl WorldRenderSettings {
|
|
||||||
pub fn overlay_visible(&self, overlay: &WorldOverlay) -> bool {
|
|
||||||
self.visible_overlays.contains(overlay)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn toggle_overlay(&mut self, overlay: &WorldOverlay) {
|
|
||||||
if self.visible_overlays.contains(overlay) {
|
|
||||||
assert!(
|
|
||||||
self.visible_overlays.remove(overlay),
|
|
||||||
"Failed to remove overlay [{overlay:#?}], that shouldn't happen."
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
assert!(
|
|
||||||
self.visible_overlays.insert(*overlay),
|
|
||||||
"Failed to insert overlay [{overlay:#?}], that shouldn't happen."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for WorldView {
|
|
||||||
fn default() -> Self {
|
|
||||||
WorldView::Biomes
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -106,27 +106,27 @@ pub struct TerrainCell {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl World {
|
impl World {
|
||||||
pub(crate) const ALTITUDE_SPAN: f32 = World::MAX_ALTITUDE - World::MIN_ALTITUDE;
|
pub const ALTITUDE_SPAN: f32 = World::MAX_ALTITUDE - World::MIN_ALTITUDE;
|
||||||
const CONTINENT_MAX_SIZE_FACTOR: f32 = 6.0;
|
pub const CONTINENT_MAX_SIZE_FACTOR: f32 = 6.0;
|
||||||
const CONTINENT_MIN_SIZE_FACTOR: f32 = 2.5;
|
pub const CONTINENT_MIN_SIZE_FACTOR: f32 = 2.5;
|
||||||
pub(crate) const MAX_ALTITUDE: f32 = 15000.0;
|
pub const MAX_ALTITUDE: f32 = 15000.0;
|
||||||
pub(crate) const MAX_RAINFALL: f32 = 7500.0;
|
pub const MAX_RAINFALL: f32 = 7500.0;
|
||||||
pub(crate) const MAX_TEMPERATURE: f32 = 30.0;
|
pub const MAX_TEMPERATURE: f32 = 30.0;
|
||||||
pub(crate) const MIN_ALTITUDE: f32 = -15000.0;
|
pub const MIN_ALTITUDE: f32 = -15000.0;
|
||||||
pub(crate) const MIN_RAINFALL: f32 = 0.0;
|
pub const MIN_RAINFALL: f32 = 0.0;
|
||||||
pub(crate) const MIN_TEMPERATURE: f32 = -35.0;
|
pub const MIN_TEMPERATURE: f32 = -35.0;
|
||||||
const MOUNTAIN_RANGE_MIX_FACTOR: f32 = 0.075;
|
pub const MOUNTAIN_RANGE_MIX_FACTOR: f32 = 0.075;
|
||||||
const MOUNTAIN_RANGE_WIDTH_FACTOR: f32 = 25.0;
|
pub const MOUNTAIN_RANGE_WIDTH_FACTOR: f32 = 25.0;
|
||||||
const NUM_CONTINENTS: u8 = 7;
|
pub const NUM_CONTINENTS: u8 = 7;
|
||||||
const RAINFALL_DRYNESS_FACTOR: f32 = 0.005;
|
pub const RAINFALL_DRYNESS_FACTOR: f32 = 0.005;
|
||||||
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;
|
||||||
const RAINFALL_SPAN: f32 = World::MAX_RAINFALL - World::MIN_RAINFALL;
|
pub const RAINFALL_SPAN: f32 = World::MAX_RAINFALL - World::MIN_RAINFALL;
|
||||||
const TEMPERATURE_ALTITUDE_FACTOR: f32 = 1.0;
|
pub const TEMPERATURE_ALTITUDE_FACTOR: f32 = 1.0;
|
||||||
const TEMPERATURE_SPAN: f32 = World::MAX_TEMPERATURE - World::MIN_TEMPERATURE;
|
pub const TEMPERATURE_SPAN: f32 = World::MAX_TEMPERATURE - World::MIN_TEMPERATURE;
|
||||||
const TERRAIN_NOISE_FACTOR_1: f32 = 0.15;
|
pub const TERRAIN_NOISE_FACTOR_1: f32 = 0.15;
|
||||||
const TERRAIN_NOISE_FACTOR_2: f32 = 0.15;
|
pub const TERRAIN_NOISE_FACTOR_2: f32 = 0.15;
|
||||||
const TERRAIN_NOISE_FACTOR_3: f32 = 0.1;
|
pub const TERRAIN_NOISE_FACTOR_3: f32 = 0.1;
|
||||||
const TERRAIN_NOISE_FACTOR_4: f32 = 2.5;
|
pub const TERRAIN_NOISE_FACTOR_4: f32 = 2.5;
|
||||||
|
|
||||||
pub fn new(width: u32, height: u32, seed: u32) -> World {
|
pub fn new(width: u32, height: u32, seed: u32) -> World {
|
||||||
World {
|
World {
|
||||||
|
@ -326,7 +326,7 @@ impl World {
|
||||||
offset: Vec3A,
|
offset: Vec3A,
|
||||||
) -> Result<f32, CartesianError> {
|
) -> Result<f32, CartesianError> {
|
||||||
let cartesian = cartesian_coordinates(alpha, beta, radius)?;
|
let cartesian = cartesian_coordinates(alpha, beta, radius)?;
|
||||||
Ok(perlin::get_value(
|
Ok(perlin::perlin_value(
|
||||||
cartesian.x + offset.x,
|
cartesian.x + offset.x,
|
||||||
cartesian.y + offset.y,
|
cartesian.y + offset.y,
|
||||||
cartesian.z + offset.z,
|
cartesian.z + offset.z,
|
||||||
|
|
|
@ -159,6 +159,7 @@ impl WorldManager {
|
||||||
self.world.as_ref()
|
self.world.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn world(&self) -> &World {
|
pub fn world(&self) -> &World {
|
||||||
assert!(self.world.is_some(), "No world.");
|
assert!(self.world.is_some(), "No world.");
|
||||||
self.get_world().unwrap()
|
self.get_world().unwrap()
|
||||||
|
@ -171,149 +172,4 @@ impl WorldManager {
|
||||||
self.world = Some(new_world);
|
self.world = Some(new_world);
|
||||||
Ok(self.get_world().unwrap())
|
Ok(self.get_world().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
|
||||||
#[must_use]
|
|
||||||
fn generate_color(&self, cell: &TerrainCell, render_settings: &WorldRenderSettings) -> Color {
|
|
||||||
let base_color = match render_settings.view {
|
|
||||||
WorldView::Biomes => self.biome_color(cell),
|
|
||||||
WorldView::Topography => WorldManager::altitude_contour_color(cell.altitude),
|
|
||||||
};
|
|
||||||
let mut normalizer = 1.0;
|
|
||||||
|
|
||||||
let mut red = base_color.r();
|
|
||||||
let mut green = base_color.g();
|
|
||||||
let mut blue = base_color.b();
|
|
||||||
|
|
||||||
if render_settings.overlay_visible(&WorldOverlay::Rainfall) {
|
|
||||||
normalizer += 1.0;
|
|
||||||
let rainfall_color = self.rainfall_contour_color(cell.rainfall);
|
|
||||||
|
|
||||||
red += rainfall_color.r();
|
|
||||||
green += rainfall_color.g();
|
|
||||||
blue += rainfall_color.b();
|
|
||||||
}
|
|
||||||
|
|
||||||
if render_settings.overlay_visible(&WorldOverlay::Temperature) {
|
|
||||||
normalizer += 1.0;
|
|
||||||
let temperature_color = self.temperature_contour_color(cell.temperature);
|
|
||||||
|
|
||||||
red += temperature_color.r();
|
|
||||||
green += temperature_color.g();
|
|
||||||
blue += temperature_color.b();
|
|
||||||
}
|
|
||||||
|
|
||||||
Color::rgb(red / normalizer, green / normalizer, blue / normalizer)
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[cfg(feature = "render")]
|
|
||||||
// #[must_use]
|
|
||||||
// fn altitude_color(altitude: f32) -> Color {
|
|
||||||
// if altitude < 0.0 {
|
|
||||||
// Color::rgb(0.0, 0.0, (2.0 - altitude / World::MIN_ALTITUDE) / 2.0)
|
|
||||||
// } else {
|
|
||||||
// let mult = (1.0 + altitude / World::MAX_ALTITUDE) / 2.0;
|
|
||||||
|
|
||||||
// Color::rgb(0.58 * mult, 0.29 * mult, 0.0)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
|
||||||
#[must_use]
|
|
||||||
fn altitude_contour_color(altitude: f32) -> Color {
|
|
||||||
if altitude < 0.0 {
|
|
||||||
Color::rgb(0.0, 0.0, (2.0 - altitude / World::MIN_ALTITUDE) / 2.0)
|
|
||||||
} else {
|
|
||||||
let mut shade_value = 1.0;
|
|
||||||
|
|
||||||
while shade_value > altitude / World::MAX_ALTITUDE {
|
|
||||||
shade_value -= 0.05;
|
|
||||||
}
|
|
||||||
|
|
||||||
Color::rgb(shade_value, shade_value, shade_value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
|
||||||
#[must_use]
|
|
||||||
fn rainfall_contour_color(&self, rainfall: f32) -> Color {
|
|
||||||
let mut shade_value = 1.0;
|
|
||||||
let value = rainfall / self.world().max_rainfall;
|
|
||||||
|
|
||||||
while shade_value > value {
|
|
||||||
shade_value -= 0.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Color::rgb(0.0, shade_value, 0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
|
||||||
#[must_use]
|
|
||||||
fn temperature_contour_color(&self, temperature: f32) -> Color {
|
|
||||||
let mut shade_value = 1.0;
|
|
||||||
let value = (temperature - self.world().min_temperature)
|
|
||||||
/ (self.world().max_temperature - self.world().min_temperature);
|
|
||||||
|
|
||||||
while shade_value > value {
|
|
||||||
shade_value -= 0.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Color::rgb(shade_value, 0.0, 1.0 - shade_value)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
|
||||||
#[must_use]
|
|
||||||
fn biome_color(&self, cell: &TerrainCell) -> Color {
|
|
||||||
let slant = self.world().get_slant(cell);
|
|
||||||
|
|
||||||
let slant_factor = f32::min(1.0, (4.0 + (10.0 * slant / World::ALTITUDE_SPAN)) / 5.0);
|
|
||||||
let altitude_factor = f32::min(
|
|
||||||
1.0,
|
|
||||||
(0.5 + (cell.altitude - World::MIN_ALTITUDE) / World::ALTITUDE_SPAN) / 1.5,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut red = 0.0;
|
|
||||||
let mut green = 0.0;
|
|
||||||
let mut blue = 0.0;
|
|
||||||
|
|
||||||
for (biome, presence) in cell.biome_presences.iter() {
|
|
||||||
red += BiomeStats::from(biome).color.r() * presence;
|
|
||||||
green += BiomeStats::from(biome).color.g() * presence;
|
|
||||||
blue += BiomeStats::from(biome).color.b() * presence;
|
|
||||||
}
|
|
||||||
red *= slant_factor * altitude_factor;
|
|
||||||
green *= slant_factor * altitude_factor;
|
|
||||||
blue *= slant_factor * altitude_factor;
|
|
||||||
Color::rgb(red, green, blue)
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[cfg(feature = "render")]
|
|
||||||
// #[must_use]
|
|
||||||
// fn map_colors(&self) -> Vec<Color> {
|
|
||||||
// self.world()
|
|
||||||
// .terrain
|
|
||||||
// .iter()
|
|
||||||
// .rev()
|
|
||||||
// .flatten()
|
|
||||||
// .map(|cell| self.generate_color(cell))
|
|
||||||
// .collect()
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
|
||||||
#[must_use]
|
|
||||||
pub fn map_color_bytes(&self, render_settings: &WorldRenderSettings) -> Vec<u8> {
|
|
||||||
self.world()
|
|
||||||
.terrain
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.flatten()
|
|
||||||
.flat_map(|cell| {
|
|
||||||
self.generate_color(cell, render_settings)
|
|
||||||
.as_rgba_f32()
|
|
||||||
.iter()
|
|
||||||
.flat_map(|num| num.to_le_bytes())
|
|
||||||
.collect::<Vec<u8>>()
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ use {
|
||||||
bevy::{
|
bevy::{
|
||||||
ecs::{
|
ecs::{
|
||||||
change_detection::Mut,
|
change_detection::Mut,
|
||||||
component::Component,
|
|
||||||
system::{SystemParam, SystemState},
|
system::{SystemParam, SystemState},
|
||||||
world::World,
|
world::World,
|
||||||
},
|
},
|
||||||
|
@ -59,6 +58,12 @@ impl ToolbarButton {
|
||||||
|
|
||||||
impl From<ToolbarButton> for &'static str {
|
impl From<ToolbarButton> for &'static str {
|
||||||
fn from(button: ToolbarButton) -> Self {
|
fn from(button: ToolbarButton) -> Self {
|
||||||
|
(&button).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&ToolbarButton> for &'static str {
|
||||||
|
fn from(button: &ToolbarButton) -> Self {
|
||||||
match button {
|
match button {
|
||||||
ToolbarButton::Views => "Change view",
|
ToolbarButton::Views => "Change view",
|
||||||
ToolbarButton::Overlays => "Overlays",
|
ToolbarButton::Overlays => "Overlays",
|
||||||
|
@ -68,12 +73,6 @@ impl From<ToolbarButton> for &'static str {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&ToolbarButton> for &'static str {
|
|
||||||
fn from(button: &ToolbarButton) -> Self {
|
|
||||||
(*button).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ToolbarButton> for String {
|
impl From<ToolbarButton> for String {
|
||||||
fn from(button: ToolbarButton) -> Self {
|
fn from(button: ToolbarButton) -> Self {
|
||||||
<&'static str>::from(button).into()
|
<&'static str>::from(button).into()
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
use {
|
use {
|
||||||
crate::{gui::WindowSystem, resources::ShouldRedraw},
|
crate::{
|
||||||
|
gui::WindowSystem,
|
||||||
|
planet_renderer::{WorldOverlay, WorldRenderSettings},
|
||||||
|
resources::ShouldRedraw,
|
||||||
|
},
|
||||||
bevy::ecs::{
|
bevy::ecs::{
|
||||||
change_detection::Mut,
|
change_detection::Mut,
|
||||||
system::{SystemParam, SystemState},
|
system::{SystemParam, SystemState},
|
||||||
world::World,
|
world::World,
|
||||||
},
|
},
|
||||||
bevy_egui::egui::Ui,
|
bevy_egui::egui::Ui,
|
||||||
planet::{WorldOverlay, WorldRenderSettings},
|
|
||||||
std::marker::PhantomData,
|
std::marker::PhantomData,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
use {
|
use {
|
||||||
crate::{gui::WindowSystem, resources::ShouldRedraw},
|
crate::{
|
||||||
|
gui::WindowSystem,
|
||||||
|
planet_renderer::{WorldRenderSettings, WorldView},
|
||||||
|
resources::ShouldRedraw,
|
||||||
|
},
|
||||||
bevy::ecs::{
|
bevy::ecs::{
|
||||||
change_detection::Mut,
|
change_detection::Mut,
|
||||||
system::{SystemParam, SystemState},
|
system::{SystemParam, SystemState},
|
||||||
world::World,
|
world::World,
|
||||||
},
|
},
|
||||||
bevy_egui::egui::Ui,
|
bevy_egui::egui::Ui,
|
||||||
planet::{WorldRenderSettings, WorldView},
|
|
||||||
std::marker::PhantomData,
|
std::marker::PhantomData,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
macro_rules! iterable_enum {
|
macro_rules! iterable_enum {
|
||||||
($Name:ident { $($Variant:ident),*$(,)? }) =>
|
($Name:ident { $($Variant:ident),*$(,)? }) =>
|
||||||
{
|
{
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Component)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, bevy::ecs::component::Component)]
|
||||||
pub enum $Name {
|
pub enum $Name {
|
||||||
$($Variant),*,
|
$($Variant),*,
|
||||||
}
|
}
|
||||||
impl $Name {
|
impl $Name {
|
||||||
const ITEMS: &'static [$Name] = &[$($Name::$Variant),*];
|
pub const ITEMS: &'static [$Name] = &[$($Name::$Variant),*];
|
||||||
|
pub const ITEM_COUNT: usize = $Name::ITEMS.len();
|
||||||
|
|
||||||
pub fn iterator() -> core::slice::Iter<'static, $Name> {
|
pub fn iterator() -> core::slice::Iter<'static, $Name> {
|
||||||
$Name::ITEMS.iter()
|
$Name::ITEMS.iter()
|
||||||
|
@ -15,5 +16,26 @@ macro_rules! iterable_enum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
macro_rules! iterable_enum_stringify {
|
||||||
|
($Name:ident { $($Variant:ident),*$(,)? }) =>
|
||||||
|
{
|
||||||
|
crate::macros::iterable_enum!($Name { $($Variant,)* });
|
||||||
|
impl From<$Name> for &'static str {
|
||||||
|
fn from(value: $Name) -> &'static str {
|
||||||
|
match value {
|
||||||
|
$($Name::$Variant => stringify!($Variant),)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&$Name> for &'static str {
|
||||||
|
fn from(value: &$Name) -> &'static str {
|
||||||
|
match value {
|
||||||
|
$($Name::$Variant => stringify!($Variant),)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
pub(crate) use iterable_enum;
|
pub(crate) use iterable_enum;
|
||||||
|
pub(crate) use iterable_enum_stringify;
|
||||||
|
|
|
@ -4,6 +4,8 @@ pub(crate) mod components;
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
pub(crate) mod gui;
|
pub(crate) mod gui;
|
||||||
pub(crate) mod macros;
|
pub(crate) mod macros;
|
||||||
|
#[cfg(feature = "render")]
|
||||||
|
pub(crate) mod planet_renderer;
|
||||||
pub(crate) mod plugins;
|
pub(crate) mod plugins;
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
pub(crate) mod resources;
|
pub(crate) mod resources;
|
||||||
|
@ -52,7 +54,7 @@ use {
|
||||||
},
|
},
|
||||||
components::panning::Pan2d,
|
components::panning::Pan2d,
|
||||||
gui::{render_windows, widget, widgets::ToolbarWidget, window::open_window, windows::TileInfo},
|
gui::{render_windows, widget, widgets::ToolbarWidget, window::open_window, windows::TileInfo},
|
||||||
planet::WorldRenderSettings,
|
planet_renderer::{WorldRenderSettings, WorldRenderer},
|
||||||
resources::{CursorMapPosition, OpenedWindows, ShouldRedraw},
|
resources::{CursorMapPosition, OpenedWindows, ShouldRedraw},
|
||||||
};
|
};
|
||||||
#[cfg(all(feature = "render", feature = "logging"))]
|
#[cfg(all(feature = "render", feature = "logging"))]
|
||||||
|
|
167
src/planet_renderer.rs
Normal file
167
src/planet_renderer.rs
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
use {
|
||||||
|
crate::macros::iterable_enum_stringify,
|
||||||
|
bevy::{asset::HandleId, prelude::Color, utils::HashSet},
|
||||||
|
planet::{BiomeStats, TerrainCell, World, WorldManager},
|
||||||
|
};
|
||||||
|
|
||||||
|
iterable_enum_stringify!(WorldView { Biomes, Topography });
|
||||||
|
iterable_enum_stringify!(WorldOverlay {
|
||||||
|
Temperature,
|
||||||
|
Rainfall
|
||||||
|
});
|
||||||
|
|
||||||
|
#[cfg(feature = "render")]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct WorldRenderSettings {
|
||||||
|
pub map_image_handle_id: Option<HandleId>,
|
||||||
|
|
||||||
|
visible_overlays: HashSet<WorldOverlay>,
|
||||||
|
pub view: WorldView,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "render")]
|
||||||
|
impl WorldRenderSettings {
|
||||||
|
pub fn overlay_visible(&self, overlay: &WorldOverlay) -> bool {
|
||||||
|
self.visible_overlays.contains(overlay)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toggle_overlay(&mut self, overlay: &WorldOverlay) {
|
||||||
|
if self.visible_overlays.contains(overlay) {
|
||||||
|
assert!(
|
||||||
|
self.visible_overlays.remove(overlay),
|
||||||
|
"Failed to remove overlay [{overlay:#?}], that shouldn't happen."
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
assert!(
|
||||||
|
self.visible_overlays.insert(*overlay),
|
||||||
|
"Failed to insert overlay [{overlay:#?}], that shouldn't happen."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
fn altitude_contour_color(altitude: f32) -> Color {
|
||||||
|
if altitude < 0.0 {
|
||||||
|
Color::rgb(0.0, 0.0, (2.0 - altitude / World::MIN_ALTITUDE) / 2.0)
|
||||||
|
} else {
|
||||||
|
let mut shade_value = 1.0;
|
||||||
|
|
||||||
|
while shade_value > altitude / World::MAX_ALTITUDE {
|
||||||
|
shade_value -= 0.05;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color::rgb(shade_value, shade_value, shade_value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
fn rainfall_contour_color(world: &World, rainfall: f32) -> Color {
|
||||||
|
let mut shade_value = 1.0;
|
||||||
|
let value = rainfall / world.max_rainfall;
|
||||||
|
|
||||||
|
while shade_value > value {
|
||||||
|
shade_value -= 0.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color::rgb(0.0, shade_value, 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
fn temperature_contour_color(world: &World, temperature: f32) -> Color {
|
||||||
|
let mut shade_value = 1.0;
|
||||||
|
let value =
|
||||||
|
(temperature - world.min_temperature) / (world.max_temperature - world.min_temperature);
|
||||||
|
|
||||||
|
while shade_value > value {
|
||||||
|
shade_value -= 0.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color::rgb(shade_value, 0.0, 1.0 - shade_value)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
fn biome_color(world: &World, cell: &TerrainCell) -> Color {
|
||||||
|
let slant = world.get_slant(cell);
|
||||||
|
|
||||||
|
let slant_factor = f32::min(1.0, (4.0 + (10.0 * slant / World::ALTITUDE_SPAN)) / 5.0);
|
||||||
|
let altitude_factor = f32::min(
|
||||||
|
1.0,
|
||||||
|
(0.5 + (cell.altitude - World::MIN_ALTITUDE) / World::ALTITUDE_SPAN) / 1.5,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut red = 0.0;
|
||||||
|
let mut green = 0.0;
|
||||||
|
let mut blue = 0.0;
|
||||||
|
|
||||||
|
for (biome, presence) in cell.biome_presences.iter() {
|
||||||
|
red += BiomeStats::from(biome).color.r() * presence;
|
||||||
|
green += BiomeStats::from(biome).color.g() * presence;
|
||||||
|
blue += BiomeStats::from(biome).color.b() * presence;
|
||||||
|
}
|
||||||
|
red *= slant_factor * altitude_factor;
|
||||||
|
green *= slant_factor * altitude_factor;
|
||||||
|
blue *= slant_factor * altitude_factor;
|
||||||
|
Color::rgb(red, green, blue)
|
||||||
|
}
|
||||||
|
pub(crate) trait WorldRenderer {
|
||||||
|
fn map_color_bytes(&self, render_settings: &WorldRenderSettings) -> Vec<u8>;
|
||||||
|
fn generate_color(&self, cell: &TerrainCell, render_settings: &WorldRenderSettings) -> Color;
|
||||||
|
}
|
||||||
|
impl WorldRenderer for WorldManager {
|
||||||
|
#[must_use]
|
||||||
|
fn map_color_bytes(&self, render_settings: &WorldRenderSettings) -> Vec<u8> {
|
||||||
|
self.world()
|
||||||
|
.terrain
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.flatten()
|
||||||
|
.flat_map(|cell| {
|
||||||
|
self.generate_color(cell, render_settings)
|
||||||
|
.as_rgba_f32()
|
||||||
|
.iter()
|
||||||
|
.flat_map(|num| num.to_le_bytes())
|
||||||
|
.collect::<Vec<u8>>()
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
fn generate_color(&self, cell: &TerrainCell, render_settings: &WorldRenderSettings) -> Color {
|
||||||
|
let base_color = match render_settings.view {
|
||||||
|
WorldView::Biomes => biome_color(&self.world(), cell),
|
||||||
|
WorldView::Topography => altitude_contour_color(cell.altitude),
|
||||||
|
};
|
||||||
|
let mut normalizer = 1.0;
|
||||||
|
|
||||||
|
let mut red = base_color.r();
|
||||||
|
let mut green = base_color.g();
|
||||||
|
let mut blue = base_color.b();
|
||||||
|
|
||||||
|
if render_settings.overlay_visible(&WorldOverlay::Rainfall) {
|
||||||
|
normalizer += 1.0;
|
||||||
|
let rainfall_color = rainfall_contour_color(self.world(), cell.rainfall);
|
||||||
|
|
||||||
|
red += rainfall_color.r();
|
||||||
|
green += rainfall_color.g();
|
||||||
|
blue += rainfall_color.b();
|
||||||
|
}
|
||||||
|
|
||||||
|
if render_settings.overlay_visible(&WorldOverlay::Temperature) {
|
||||||
|
normalizer += 1.0;
|
||||||
|
let temperature_color = temperature_contour_color(self.world(), cell.temperature);
|
||||||
|
|
||||||
|
red += temperature_color.r();
|
||||||
|
green += temperature_color.g();
|
||||||
|
blue += temperature_color.b();
|
||||||
|
}
|
||||||
|
|
||||||
|
Color::rgb(red / normalizer, green / normalizer, blue / normalizer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for WorldView {
|
||||||
|
fn default() -> Self {
|
||||||
|
WorldView::Biomes
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue