diff --git a/Cargo.toml b/Cargo.toml index f7a09c4..d495a24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ codegen-units = 1 # bevy/trace_chrome for tracing by function # https://github.com/bevyengine/bevy/blob/main/docs/profiling.md logging = ["planet/logging"] -render = ["bevy/bevy_asset", "bevy/bevy_winit", "bevy/x11", "bevy/wayland", "bevy/render", "dep:fxhash", "dep:bevy_egui", "dep:tinyfiledialogs"] +render = ["bevy/bevy_asset", "bevy/bevy_winit", "bevy/x11", "bevy/wayland", "bevy/render", "planet/render", "dep:fxhash", "dep:bevy_egui", "dep:tinyfiledialogs"] default = ["render", "logging"] [dependencies.planet] diff --git a/planet/Cargo.toml b/planet/Cargo.toml index 16bb3ff..d718729 100644 --- a/planet/Cargo.toml +++ b/planet/Cargo.toml @@ -8,7 +8,8 @@ release = { strip = "symbols", lto = "thin", opt-level = "z" } [features] logging = [] -default = ["logging"] +render = ["bevy/render"] +default = ["logging", "render"] [dependencies.rand] version = "0.8.5" diff --git a/planet/src/biome.rs b/planet/src/biome.rs index 932cc68..1596c0b 100644 --- a/planet/src/biome.rs +++ b/planet/src/biome.rs @@ -1,11 +1,11 @@ -use { - crate::{macros::iterable_enum, World}, - bevy::render::color::Color, -}; +use crate::{macros::iterable_enum, World}; +#[cfg(feature = "render")] +use bevy::prelude::Color; #[derive(Debug, Clone, Default)] pub struct BiomeStats { pub name: String, + #[cfg(feature = "render")] pub color: Color, pub min_altitude: f32, pub max_altitude: f32, @@ -30,82 +30,90 @@ impl From for BiomeStats { fn from(biome_type: BiomeType) -> BiomeStats { match biome_type { BiomeType::IceCap => BiomeStats { - 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, + name: "Ice Cap".into(), + #[cfg(feature = "render")] + 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 => BiomeStats { - 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, + name: "Ocean".into(), + #[cfg(feature = "render")] + 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 => BiomeStats { - 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, + name: "Grassland".into(), + #[cfg(feature = "render")] + 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 => BiomeStats { - 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, + name: "Forest".into(), + #[cfg(feature = "render")] + 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 => BiomeStats { - 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, + name: "Taiga".into(), + #[cfg(feature = "render")] + 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 => BiomeStats { - 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, + name: "Tundra ".into(), + #[cfg(feature = "render")] + 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 => BiomeStats { - 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, + name: "Desert ".into(), + #[cfg(feature = "render")] + 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 => BiomeStats { - 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, + name: "Rainforest".into(), + #[cfg(feature = "render")] + 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, }, diff --git a/planet/src/world.rs b/planet/src/world.rs index 2202f1f..d1f8712 100644 --- a/planet/src/world.rs +++ b/planet/src/world.rs @@ -631,6 +631,56 @@ impl World { west_altitude - east_altitude } + pub fn is_cell_near_coastline(&self, cell: &TerrainCell) -> bool { + if cell.altitude >= 0.0 { + return false; + } + + let neighbors = self.cell_neighbors(cell.x, cell.y); + + if let Some(neighbor) = neighbors.get(&CompassDirection::West) { + if neighbor.altitude >= 0.0 { + return true; + } + } + if let Some(neighbor) = neighbors.get(&CompassDirection::NorthWest) { + if neighbor.altitude >= 0.0 { + return true; + } + } + if let Some(neighbor) = neighbors.get(&CompassDirection::North) { + if neighbor.altitude >= 0.0 { + return true; + } + } + if let Some(neighbor) = neighbors.get(&CompassDirection::NorthEast) { + if neighbor.altitude >= 0.0 { + return true; + } + } + if let Some(neighbor) = neighbors.get(&CompassDirection::East) { + if neighbor.altitude >= 0.0 { + return true; + } + } + if let Some(neighbor) = neighbors.get(&CompassDirection::SouthEast) { + if neighbor.altitude >= 0.0 { + return true; + } + } + if let Some(neighbor) = neighbors.get(&CompassDirection::South) { + if neighbor.altitude >= 0.0 { + return true; + } + } + if let Some(neighbor) = neighbors.get(&CompassDirection::SouthWest) { + if neighbor.altitude >= 0.0 { + return true; + } + } + return false; + } + #[must_use] pub fn is_cell_coastline(&self, cell: &TerrainCell) -> bool { if cell.altitude <= 0.0 { diff --git a/planet/src/world_manager.rs b/planet/src/world_manager.rs index b7362f7..f9ee94b 100644 --- a/planet/src/world_manager.rs +++ b/planet/src/world_manager.rs @@ -1,8 +1,3 @@ -#[cfg(feature = "render")] -use { - crate::{BiomeStats, TerrainCell, WorldOverlay, WorldRenderSettings, WorldView}, - bevy::render::color::Color, -}; use { crate::{World, WorldGenError}, bevy::{log::warn, utils::default}, diff --git a/src/macros.rs b/src/macros.rs index 700de28..baace8b 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -16,6 +16,7 @@ macro_rules! iterable_enum { } } } +#[cfg(feature = "render")] macro_rules! iterable_enum_stringify { ($Name:ident { $($Variant:ident),*$(,)? }) => { @@ -38,4 +39,5 @@ macro_rules! iterable_enum_stringify { } #[cfg(feature = "render")] pub(crate) use iterable_enum; +#[cfg(feature = "render")] pub(crate) use iterable_enum_stringify; diff --git a/src/planet_renderer.rs b/src/planet_renderer.rs index f54abdf..6a89a52 100644 --- a/src/planet_renderer.rs +++ b/src/planet_renderer.rs @@ -45,24 +45,34 @@ impl WorldRenderSettings { } #[must_use] -fn altitude_contour_color(altitude: f32) -> Color { +fn altitude_contour_color(world: &World, altitude: f32) -> Color { + let mut color = Color::rgb(1.0, 0.6, 0.0); + + let mut shade_value = 1.0; + let mut value = f32::max(0.0, altitude / world.max_altitude); + 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) + value = f32::max(0.0, 1.0 - (altitude / world.min_altitude)); + color = Color::BLUE; } + + while shade_value > value { + shade_value -= 0.15; + } + shade_value = 0.5 * shade_value + 0.5; + + Color::rgb( + color.r() * shade_value, + color.g() * shade_value, + color.b() * shade_value, + ) } +#[cfg(feature = "placeholder")] #[must_use] fn rainfall_contour_color(world: &World, rainfall: f32) -> Color { let mut shade_value = 1.0; - let value = rainfall / world.max_rainfall; + let value = f32::max(0.0, rainfall / world.max_rainfall); while shade_value > value { shade_value -= 0.1; @@ -71,6 +81,16 @@ fn rainfall_contour_color(world: &World, rainfall: f32) -> Color { Color::rgb(0.0, shade_value, 0.0) } +#[must_use] +fn rainfall_color(rainfall: f32) -> Color { + if rainfall <= 0.0 { + Color::BLACK + } else { + Color::rgb(rainfall / World::MAX_RAINFALL, 0.0, 0.0) + } +} + +#[cfg(feature = "placeholder")] #[must_use] fn temperature_contour_color(world: &World, temperature: f32) -> Color { let mut shade_value = 1.0; @@ -84,6 +104,12 @@ fn temperature_contour_color(world: &World, temperature: f32) -> Color { Color::rgb(shade_value, 0.0, 1.0 - shade_value) } +#[must_use] +fn temperature_color(temperature: f32) -> Color { + let value = (temperature - World::MIN_TEMPERATURE) / World::TEMPERATURE_SPAN; + Color::rgb(value, 0.0, 1.0 - value) +} + #[must_use] fn biome_color(world: &World, cell: &TerrainCell) -> Color { let slant = world.get_slant(cell); @@ -109,14 +135,31 @@ fn biome_color(world: &World, cell: &TerrainCell) -> Color { Color::rgb(red, green, blue) } +const COASTLINE_PALETTE: [Color; 5] = [ + Color::rgb(251.0 / 255.0, 230.0 / 255.0, 197.0 / 255.0), + Color::rgb(233.0 / 255.0, 192.0 / 255.0, 136.0 / 255.0), + Color::rgb(74.0 / 255.0, 39.0 / 255.0, 13.0 / 255.0), + Color::rgb(155.0 / 255.0, 105.0 / 255.0, 72.0 / 255.0), + Color::rgb(188.0 / 255.0, 136.0 / 255.0, 84.0 / 255.0), +]; #[must_use] fn coastline_color(world: &World, cell: &TerrainCell) -> Color { if world.is_cell_coastline(cell) { - Color::BLACK + COASTLINE_PALETTE[2] + } else if world.is_cell_near_coastline(cell) { + COASTLINE_PALETTE[3] } else if cell.altitude > 0.0 { - Color::rgb(0.75, 0.75, 0.75) + let slant = world.get_slant(cell); + let altitude_span = world.max_altitude - world.min_altitude; + let slant_factor = f32::min(1.0, -(500.0 * (slant / altitude_span))); + + if slant_factor > 0.9 { + COASTLINE_PALETTE[4] * slant_factor + COASTLINE_PALETTE[1] * (1.0 - slant_factor) + } else { + COASTLINE_PALETTE[1] + } } else { - Color::ANTIQUE_WHITE + COASTLINE_PALETTE[0] } } pub(crate) trait WorldRenderer { @@ -144,9 +187,9 @@ impl WorldRenderer for WorldManager { #[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), - WorldView::Coastlines => coastline_color(&self.world(), cell), + WorldView::Biomes => biome_color(self.world(), cell), + WorldView::Topography => altitude_contour_color(self.world(), cell.altitude), + WorldView::Coastlines => coastline_color(self.world(), cell), }; let mut normalizer = 1.0; @@ -154,9 +197,18 @@ impl WorldRenderer for WorldManager { let mut green = base_color.g(); let mut blue = base_color.b(); + if render_settings.overlay_visible(&WorldOverlay::Rainfall) + || render_settings.overlay_visible(&WorldOverlay::Temperature) + { + let grey = (red + green + blue) / 3.0; + red = grey; + green = grey; + blue = grey; + } + if render_settings.overlay_visible(&WorldOverlay::Rainfall) { normalizer += 1.0; - let rainfall_color = rainfall_contour_color(self.world(), cell.rainfall); + let rainfall_color = rainfall_color(cell.rainfall); red += rainfall_color.r(); green += rainfall_color.g(); @@ -165,7 +217,7 @@ impl WorldRenderer for WorldManager { if render_settings.overlay_visible(&WorldOverlay::Temperature) { normalizer += 1.0; - let temperature_color = temperature_contour_color(self.world(), cell.temperature); + let temperature_color = temperature_color(cell.temperature); red += temperature_color.r(); green += temperature_color.g();