Add progress bar for world generation.

Also remove a bunch of spammy logging
This commit is contained in:
Tobias Berger 2022-11-15 19:04:02 +01:00
parent 1a3b4bb72c
commit 9581a5d206
Signed by: toby
GPG key ID: 2D05EFAB764D6A88
21 changed files with 221 additions and 89 deletions

2
Cargo.lock generated
View file

@ -2087,6 +2087,7 @@ name = "planet"
version = "0.3.0" version = "0.3.0"
dependencies = [ dependencies = [
"bevy", "bevy",
"crossbeam-channel",
"postcard", "postcard",
"rand", "rand",
"serde", "serde",
@ -3087,6 +3088,7 @@ version = "0.3.0"
dependencies = [ dependencies = [
"bevy", "bevy",
"bevy_egui", "bevy_egui",
"crossbeam-channel",
"futures-lite", "futures-lite",
"fxhash", "fxhash",
"planet", "planet",

View file

@ -55,3 +55,8 @@ optional = true
[dependencies.futures-lite] [dependencies.futures-lite]
version = "1.12.0" version = "1.12.0"
default-features = false default-features = false
[dependencies.crossbeam-channel]
version = "0.5.6"
default-features = false
features = ["std"]

View file

@ -27,3 +27,8 @@ features = ["derive"]
version = "1.0.2" version = "1.0.2"
default-features = false default-features = false
features = ["use-std"] features = ["use-std"]
[dependencies.crossbeam-channel]
version = "0.5.6"
default-features = false
features = ["std"]

View file

@ -4,7 +4,7 @@ pub mod biome;
pub use biome::{BiomeStats, BiomeType}; pub use biome::{BiomeStats, BiomeType};
pub mod world_manager; pub mod world_manager;
pub use world_manager::WorldManager; pub use world_manager::WorldManager;
pub(crate) mod macros; pub mod macros;
pub mod math_util; pub mod math_util;
pub mod perlin; pub mod perlin;
pub mod saving; pub mod saving;

View file

@ -17,6 +17,7 @@ use {
prelude::Vec2, prelude::Vec2,
utils::{default, HashMap}, utils::{default, HashMap},
}, },
crossbeam_channel::Sender,
rand::{rngs::StdRng, Rng, SeedableRng}, rand::{rngs::StdRng, Rng, SeedableRng},
serde::{Deserialize, Serialize}, serde::{Deserialize, Serialize},
std::{ std::{
@ -169,18 +170,25 @@ impl World {
} }
} }
pub fn generate(&mut self) -> Result<(), WorldGenError> { pub fn generate(
if let Err(err) = self.generate_altitude() { &mut self,
progress_sender: &Sender<(f32, String)>,
) -> Result<(), WorldGenError> {
send_progress(progress_sender, 0.0, "Generating altitude");
if let Err(err) = self.generate_altitude(progress_sender) {
return Err(WorldGenError::CartesianError(err)); return Err(WorldGenError::CartesianError(err));
} }
if let Err(err) = self.generate_rainfall() { send_progress(progress_sender, 0.0, "Generating rainfall");
if let Err(err) = self.generate_rainfall(progress_sender) {
return Err(WorldGenError::CartesianError(err)); return Err(WorldGenError::CartesianError(err));
} }
if let Err(err) = self.generate_temperature() { send_progress(progress_sender, 0.0, "Generating temperature");
if let Err(err) = self.generate_temperature(progress_sender) {
return Err(WorldGenError::CartesianError(err)); return Err(WorldGenError::CartesianError(err));
} }
self.generate_biomes(); send_progress(progress_sender, 0.0, "Generating biomes");
self.generate_biomes(progress_sender);
Ok(()) Ok(())
} }
@ -192,8 +200,8 @@ impl World {
let height = self.height as f32; let height = self.height as f32;
for i in 0..World::NUM_CONTINENTS { for i in 0..World::NUM_CONTINENTS {
#[cfg(feature = "logging")] // #[cfg(feature = "logging")]
info!("Continents {}/{}", i, World::NUM_CONTINENTS); // info!("Continents: {}/{}", i, World::NUM_CONTINENTS);
self.continent_offsets[i as usize].x = self self.continent_offsets[i as usize].x = self
.rng .rng
@ -254,7 +262,10 @@ impl World {
max_value max_value
} }
fn generate_altitude(&mut self) -> Result<(), CartesianError> { fn generate_altitude(
&mut self,
progress_sender: &Sender<(f32, String)>,
) -> Result<(), CartesianError> {
info!("Generating altitude"); info!("Generating altitude");
self.generate_continents(); self.generate_continents();
@ -270,13 +281,20 @@ impl World {
let offset_4 = World::random_offset_vector(&mut self.rng); let offset_4 = World::random_offset_vector(&mut self.rng);
let offset_5 = World::random_offset_vector(&mut self.rng); let offset_5 = World::random_offset_vector(&mut self.rng);
for y in 0..self.terrain.len() { let height = self.terrain.len();
#[cfg(feature = "logging")] for y in 0..height {
info!("Altitude: {}/{}", y, self.terrain.len());
let alpha = (y as f32 / self.height as f32) * PI; let alpha = (y as f32 / self.height as f32) * PI;
for x in 0..self.terrain[y].len() { let width = self.terrain[y].len();
let size = height * width;
for x in 0..width {
let index = y * width + x;
send_progress(
progress_sender,
index as f32 / size as f32,
format!("Generating topography: {index}/{size}"),
);
let beta = (x as f32 / self.width as f32) * TAU; let beta = (x as f32 / self.width as f32) * TAU;
let value_1 = let value_1 =
@ -385,20 +403,27 @@ impl World {
World::MIN_ALTITUDE + (raw_altitude * World::ALTITUDE_SPAN) World::MIN_ALTITUDE + (raw_altitude * World::ALTITUDE_SPAN)
} }
fn generate_rainfall(&mut self) -> Result<(), CartesianError> { fn generate_rainfall(
#[cfg(feature = "logging")] &mut self,
progress_sender: &Sender<(f32, String)>,
) -> Result<(), CartesianError> {
info!("Generating rainfall"); info!("Generating rainfall");
const RADIUS: f32 = 2.0; const RADIUS: f32 = 2.0;
let offset = World::random_offset_vector(&mut self.rng); let offset = World::random_offset_vector(&mut self.rng);
let height = self.terrain.len(); let height = self.terrain.len();
for y in 0..height { for y in 0..height {
#[cfg(feature = "logging")]
info!("Rainfall: {}/{}", y, height);
let alpha = (y as f32 / self.height as f32) * PI; let alpha = (y as f32 / self.height as f32) * PI;
let width = self.terrain[y].len(); let width = self.terrain[y].len();
let size = width * height;
for x in 0..width { for x in 0..width {
let index = y * width + x;
send_progress(
progress_sender,
index as f32 / size as f32,
format!("Generating rainfall: {index}/{size}"),
);
let beta = (x as f32 / self.width as f32) * TAU; let beta = (x as f32 / self.width as f32) * TAU;
let random_noise = let random_noise =
@ -459,17 +484,28 @@ impl World {
) )
} }
fn generate_temperature(&mut self) -> Result<(), CartesianError> { fn generate_temperature(
#[cfg(feature = "logging")] &mut self,
progress_sender: &Sender<(f32, String)>,
) -> Result<(), CartesianError> {
info!("Generating temperature"); info!("Generating temperature");
let offset = World::random_offset_vector(&mut self.rng); let offset = World::random_offset_vector(&mut self.rng);
const RADIUS: f32 = 2.0; const RADIUS: f32 = 2.0;
for y in 0..self.terrain.len() { let height = self.terrain.len();
#[cfg(feature = "logging")] for y in 0..height {
info!("Temperature: {}/{}", y, self.terrain.len());
let alpha = (y as f32 / self.height as f32) * PI; let alpha = (y as f32 / self.height as f32) * PI;
for x in 0..self.terrain[y].len() {
let width = self.terrain[y].len();
let size = width * height;
for x in 0..width {
let index = y * width + x;
send_progress(
progress_sender,
index as f32 / size as f32,
format!("Generating temperature: {index}/{size}"),
);
let beta = (x as f32 / self.width as f32) * TAU; let beta = (x as f32 / self.width as f32) * TAU;
let random_noise = let random_noise =
@ -496,6 +532,7 @@ impl World {
} }
} }
info!("Done generating temperature");
Ok(()) Ok(())
} }
@ -507,12 +544,19 @@ impl World {
) )
} }
fn generate_biomes(&mut self) { fn generate_biomes(&mut self, progress_sender: &Sender<(f32, String)>) {
info!("Generating biomes"); info!("Generating biomes");
for y in 0..self.terrain.len() { let height = self.terrain.len();
#[cfg(feature = "logging")] for y in 0..height {
info!("Biomes: {}/{}", y, self.terrain.len()); let width = self.terrain[y].len();
for x in 0..self.terrain[y].len() { let size = height * width;
for x in 0..width {
let index = y * width + x;
send_progress(
progress_sender,
index as f32 / size as f32,
format!("Generating biomes: {index}/{size}"),
);
let cell = &self.terrain[y][x]; let cell = &self.terrain[y][x];
let mut total_presence = 0.0; let mut total_presence = 0.0;
@ -534,6 +578,7 @@ impl World {
.collect(); .collect();
} }
} }
info!("Done generating biomes");
} }
fn biome_presence(&self, cell: &TerrainCell, biome: &BiomeStats) -> f32 { fn biome_presence(&self, cell: &TerrainCell, biome: &BiomeStats) -> f32 {
@ -767,3 +812,15 @@ impl World {
return false; return false;
} }
} }
fn send_progress<T: Into<String>>(
progress_sender: &Sender<(f32, String)>,
progress: f32,
progress_text: T,
) {
if let Err(_) = progress_sender.try_send((progress, progress_text.into())) {
// Quietly ignore the error, it's not critical, and logging is slow.
// debug!("Failed to send world generation progress. {err}");
}
}

View file

@ -6,6 +6,7 @@ use {
tasks::{AsyncComputeTaskPool, Task}, tasks::{AsyncComputeTaskPool, Task},
utils::default, utils::default,
}, },
crossbeam_channel::Sender,
rand::random, rand::random,
std::{ std::{
error::Error, error::Error,
@ -99,7 +100,6 @@ impl WorldManager {
return Err(SaveError::MissingWorld); return Err(SaveError::MissingWorld);
}; };
let serialized = match postcard::to_stdvec(world) { let serialized = match postcard::to_stdvec(world) {
Ok(serialized) => serialized, Ok(serialized) => serialized,
Err(err) => { Err(err) => {
@ -143,7 +143,11 @@ impl WorldManager {
self.world = Some(world); self.world = Some(world);
} }
pub fn new_world_async(&mut self, seed: Option<u32>) -> Task<Result<World, WorldGenError>> { pub fn new_world_async(
&mut self,
seed: Option<u32>,
progress_sender: Sender<(f32, String)>,
) -> 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::async_new(
@ -151,7 +155,18 @@ impl WorldManager {
WorldManager::NEW_WORLD_HEIGHT, WorldManager::NEW_WORLD_HEIGHT,
seed, seed,
); );
match new_world.generate() { if let Err(_) =
progress_sender.try_send((0.0, String::from("Generating new world...")))
{
// Quietly ignore. It's not critical and logging is slow.
}
let result = new_world.generate(&progress_sender);
if let Err(_) =
progress_sender.try_send((1.0, String::from("Done generating world!")))
{
// Quietly ignore. See above
}
match result {
Ok(()) => Ok(new_world), Ok(()) => Ok(new_world),
Err(err) => Err(err), Err(err) => Err(err),
} }

View file

@ -1,9 +1,9 @@
pub(crate) mod widget; pub mod widget;
pub(crate) use widget::*; pub use widget::*;
pub(crate) mod window; pub mod window;
pub(crate) use window::*; pub use window::*;
pub(crate) mod widgets; pub mod widgets;
pub(crate) mod windows; pub mod windows;
use crate::gui::{open_window, WidgetId, WidgetSystem}; use crate::gui::{open_window, WidgetId, WidgetSystem};

View file

@ -14,11 +14,11 @@ use {
std::hash::Hasher, std::hash::Hasher,
}; };
pub(crate) trait WidgetSystem: SystemParam { pub trait WidgetSystem: SystemParam {
fn render(world: &mut World, state: &mut SystemState<Self>, ui: &mut Ui, id: WidgetId); fn render(world: &mut World, state: &mut SystemState<Self>, ui: &mut Ui, id: WidgetId);
} }
pub(crate) fn widget<S: 'static + WidgetSystem>(world: &mut World, ui: &mut Ui, id: WidgetId) { pub fn widget<S: 'static + WidgetSystem>(world: &mut World, ui: &mut Ui, id: WidgetId) {
// We need to cache `SystemState` to allow for a system's locally tracked state // We need to cache `SystemState` to allow for a system's locally tracked state
if !world.contains_resource::<StateInstances<S>>() { if !world.contains_resource::<StateInstances<S>>() {
// Note, this message should only appear once! If you see it twice in the logs, // Note, this message should only appear once! If you see it twice in the logs,
@ -51,10 +51,10 @@ struct StateInstances<T: WidgetSystem + 'static> {
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct WidgetId(pub(crate) u64); pub struct WidgetId(pub u64);
impl WidgetId { impl WidgetId {
#[must_use] #[must_use]
pub(crate) fn new(name: &str) -> Self { pub fn new(name: &str) -> Self {
let bytes = name.as_bytes(); let bytes = name.as_bytes();
let mut hasher = FxHasher32::default(); let mut hasher = FxHasher32::default();
hasher.write(bytes); hasher.write(bytes);
@ -62,7 +62,7 @@ impl WidgetId {
} }
// #[must_use] // #[must_use]
// pub(crate) fn with(&self, name: &str) -> Self { // pub fn with(&self, name: &str) -> Self {
// Self::new(&format!("{}{name}", self.0)) // Self::new(&format!("{}{name}", self.0))
// } // }
} }

View file

@ -1,2 +1,2 @@
mod toolbar; mod toolbar;
pub(crate) use toolbar::ToolbarWidget; pub use toolbar::ToolbarWidget;

View file

@ -7,7 +7,7 @@ use {
WidgetSystem, WidgetSystem,
}, },
macros::iterable_enum, macros::iterable_enum,
resources::{GenerateWorldTask, OpenedWindows}, resources::{GenerateWorldProgressChannel, GenerateWorldTask, OpenedWindows},
}, },
bevy::{ bevy::{
ecs::{ ecs::{
@ -34,11 +34,13 @@ impl ToolbarButton {
world.resource_scope(|world, mut world_manager: Mut<WorldManager>| { world.resource_scope(|world, mut world_manager: Mut<WorldManager>| {
match self { match self {
ToolbarButton::GenerateWorld => { ToolbarButton::GenerateWorld => {
let progress_sender = world.resource::<GenerateWorldProgressChannel>().sender();
let generate_world_task = &mut world.resource_mut::<GenerateWorldTask>(); let generate_world_task = &mut world.resource_mut::<GenerateWorldTask>();
if generate_world_task.0.is_some() { if generate_world_task.0.is_some() {
debug!("Already generating new world") debug!("Already generating new world")
} else { } else {
generate_world_task.0 = Some(world_manager.new_world_async(None)) generate_world_task.0 =
Some(world_manager.new_world_async(None, progress_sender))
} }
}, },
ToolbarButton::SaveLoad => { ToolbarButton::SaveLoad => {
@ -87,7 +89,7 @@ impl From<&ToolbarButton> for String {
} }
#[derive(SystemParam)] #[derive(SystemParam)]
pub(crate) struct ToolbarWidget<'w, 's> { pub struct ToolbarWidget<'w, 's> {
#[system_param(ignore)] #[system_param(ignore)]
_phantom: PhantomData<(&'w (), &'s ())>, _phantom: PhantomData<(&'w (), &'s ())>,
} }

View file

@ -16,13 +16,13 @@ use {
std::hash::Hasher, std::hash::Hasher,
}; };
pub(crate) trait WindowSystem: SystemParam { pub trait WindowSystem: SystemParam {
fn draw_contents(world: &mut World, state: &mut SystemState<Self>, ui: &mut Ui); fn draw_contents(world: &mut World, state: &mut SystemState<Self>, ui: &mut Ui);
fn name() -> &'static str; fn name() -> &'static str;
fn resizable() -> bool; fn resizable() -> bool;
} }
pub(crate) fn render_windows(world: &mut World, ctx: &Context) { pub fn render_windows(world: &mut World, ctx: &Context) {
// TODO: Windows are hard-coded here instead of being iterable, and allows // TODO: Windows are hard-coded here instead of being iterable, and allows
// creating new windows that are never rendered. // creating new windows that are never rendered.
// Is that good enough? // Is that good enough?
@ -32,10 +32,10 @@ pub(crate) fn render_windows(world: &mut World, ctx: &Context) {
window::<windows::SaveLoad>(world, ctx); window::<windows::SaveLoad>(world, ctx);
} }
pub(crate) fn open_window<S: 'static + WindowSystem>(windows: &mut OpenedWindows) { pub fn open_window<S: 'static + WindowSystem>(windows: &mut OpenedWindows) {
windows.open(S::name().into()); windows.open(S::name().into());
} }
pub(crate) fn close_window<S: 'static + WindowSystem>(windows: &mut OpenedWindows) { pub fn close_window<S: 'static + WindowSystem>(windows: &mut OpenedWindows) {
windows.close(&S::name().into()); windows.close(&S::name().into());
} }
@ -87,10 +87,10 @@ struct StateInstances<T: WindowSystem + 'static> {
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct WindowId(pub(crate) u64); pub struct WindowId(pub u64);
impl WindowId { impl WindowId {
#[must_use] #[must_use]
pub(crate) fn new(name: &str) -> Self { pub fn new(name: &str) -> Self {
let bytes = name.as_bytes(); let bytes = name.as_bytes();
let mut hasher = FxHasher32::default(); let mut hasher = FxHasher32::default();
hasher.write(bytes); hasher.write(bytes);
@ -98,7 +98,7 @@ impl WindowId {
} }
// #[must_use] // #[must_use]
// pub(crate) fn with(&self, name: &str) -> Self { // pub fn with(&self, name: &str) -> Self {
// Self::new(&format!("{}{name}", self.0)) // Self::new(&format!("{}{name}", self.0))
// } // }
} }

View file

@ -1,8 +1,8 @@
mod tile_info; mod tile_info;
pub(crate) use tile_info::TileInfo; pub use tile_info::TileInfo;
mod world_view_selection; mod world_view_selection;
pub(crate) use world_view_selection::WorldViewSelection; pub use world_view_selection::WorldViewSelection;
mod world_overlay_selection; mod world_overlay_selection;
pub(crate) use world_overlay_selection::WorldOverlaySelection; pub use world_overlay_selection::WorldOverlaySelection;
mod save_load; mod save_load;
pub(crate) use save_load::SaveLoad; pub use save_load::SaveLoad;

View file

@ -14,7 +14,7 @@ use {
}; };
#[derive(SystemParam)] #[derive(SystemParam)]
pub(crate) struct SaveLoad<'w, 's> { pub struct SaveLoad<'w, 's> {
pub file_name: Local<'s, String>, pub file_name: Local<'s, String>,
#[system_param(ignore)] #[system_param(ignore)]
_phantom: PhantomData<(&'w (), &'s ())>, _phantom: PhantomData<(&'w (), &'s ())>,

View file

@ -10,7 +10,7 @@ use {
}; };
#[derive(SystemParam)] #[derive(SystemParam)]
pub(crate) struct TileInfo<'w, 's> { pub struct TileInfo<'w, 's> {
#[system_param(ignore)] #[system_param(ignore)]
_phantom: PhantomData<(&'w (), &'s ())>, _phantom: PhantomData<(&'w (), &'s ())>,
} }

View file

@ -14,7 +14,7 @@ use {
}; };
#[derive(SystemParam)] #[derive(SystemParam)]
pub(crate) struct WorldOverlaySelection<'w, 's> { pub struct WorldOverlaySelection<'w, 's> {
#[system_param(ignore)] #[system_param(ignore)]
_phantom: PhantomData<(&'w (), &'s ())>, _phantom: PhantomData<(&'w (), &'s ())>,
} }

View file

@ -14,7 +14,7 @@ use {
}; };
#[derive(SystemParam)] #[derive(SystemParam)]
pub(crate) struct WorldViewSelection<'w, 's> { pub struct WorldViewSelection<'w, 's> {
#[system_param(ignore)] #[system_param(ignore)]
_phantom: PhantomData<(&'w (), &'s ())>, _phantom: PhantomData<(&'w (), &'s ())>,
} }

View file

@ -3,23 +3,24 @@
use { use {
crate::resources::GenerateWorldTask, crate::resources::GenerateWorldTask,
futures_lite::future::{block_on, poll_once}, futures_lite::future::{block_on, poll_once},
resources::GenerateWorldProgressChannel,
}; };
pub(crate) mod components; pub mod components;
#[cfg(feature = "render")] #[cfg(feature = "render")]
pub(crate) mod gui; pub mod gui;
pub(crate) mod macros; pub mod macros;
#[cfg(feature = "render")] #[cfg(feature = "render")]
pub(crate) mod planet_renderer; pub mod planet_renderer;
pub(crate) mod plugins; pub mod plugins;
pub(crate) mod resources; pub mod resources;
use {bevy::prelude::*, planet::WorldManager, plugins::WorldPlugins}; use {bevy::prelude::*, planet::WorldManager, plugins::WorldPlugins};
#[cfg(feature = "render")] #[cfg(feature = "render")]
use { use {
bevy::render::camera::RenderTarget, bevy::render::camera::RenderTarget,
bevy_egui::{ bevy_egui::{
egui::{FontData, FontDefinitions, FontFamily}, egui::{FontData, FontDefinitions, FontFamily, ProgressBar},
EguiContext, EguiContext,
}, },
gui::{render_windows, widget, widgets::ToolbarWidget, window::open_window, windows::TileInfo}, gui::{render_windows, widget, widgets::ToolbarWidget, window::open_window, windows::TileInfo},
@ -73,6 +74,9 @@ fn handle_generate_world_task(
mut generate_world_task: ResMut<GenerateWorldTask>, mut generate_world_task: ResMut<GenerateWorldTask>,
mut world_manager: ResMut<WorldManager>, mut world_manager: ResMut<WorldManager>,
#[cfg(feature = "render")] mut should_redraw: ResMut<ShouldRedraw>, #[cfg(feature = "render")] mut should_redraw: ResMut<ShouldRedraw>,
#[cfg(feature = "render")] mut egui_ctx: ResMut<'_, EguiContext>,
#[cfg(feature = "render")] progress_channel: Res<'_, GenerateWorldProgressChannel>,
#[cfg(feature = "render")] mut progress: Local<(f32, String)>,
) { ) {
if let Some(task) = &mut generate_world_task.0 { if let Some(task) = &mut generate_world_task.0 {
if task.is_finished() { if task.is_finished() {
@ -92,8 +96,24 @@ fn handle_generate_world_task(
} }
} }
generate_world_task.0 = None; generate_world_task.0 = None;
#[cfg(feature = "render")]
{
*progress = (0.0, String::from("Generating world..."));
}
} else { } else {
debug!("Still generating world") debug!("Still generating world");
#[cfg(feature = "render")]
{
if let Ok(new_progress) = progress_channel.receiver().try_recv() {
*progress = new_progress;
}
_ = bevy_egui::egui::TopBottomPanel::bottom("Generating World ProgressBar")
.default_height(8.0)
.show(egui_ctx.ctx_mut(), |ui| {
ui.add(ProgressBar::new(progress.0).text(progress.1.as_str()));
});
}
} }
} }
} }
@ -293,9 +313,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
app.insert_resource(WorldManager::new()) app.insert_resource(WorldManager::new())
.insert_resource(GenerateWorldTask( .insert_resource(GenerateWorldProgressChannel::new())
/* Some(manager.new_world_async(Some(0))) */ None, .insert_resource(GenerateWorldTask(None))
))
.add_system(handle_generate_world_task) .add_system(handle_generate_world_task)
.run(); .run();

View file

@ -166,7 +166,7 @@ fn coastline_color(world: &World, cell: &TerrainCell) -> Color {
COASTLINE_PALETTE[0] COASTLINE_PALETTE[0]
} }
} }
pub(crate) trait WorldRenderer { pub trait WorldRenderer {
fn map_color_bytes(&self, render_settings: &WorldRenderSettings) -> Vec<u8>; fn map_color_bytes(&self, render_settings: &WorldRenderSettings) -> Vec<u8>;
fn generate_color(&self, cell: &TerrainCell, render_settings: &WorldRenderSettings) -> Color; fn generate_color(&self, cell: &TerrainCell, render_settings: &WorldRenderSettings) -> Color;
} }

View file

@ -1,2 +1,2 @@
pub(crate) mod world_plugins; pub mod world_plugins;
pub(crate) use world_plugins::WorldPlugins; pub use world_plugins::WorldPlugins;

View file

@ -1,4 +1,4 @@
pub(crate) struct WorldPlugins; pub struct WorldPlugins;
#[cfg(not(feature = "render"))] #[cfg(not(feature = "render"))]
use bevy::app::ScheduleRunnerPlugin; use bevy::app::ScheduleRunnerPlugin;

View file

@ -2,14 +2,15 @@
use {crate::gui::WindowId, bevy::utils::HashSet, std::fmt::Display}; use {crate::gui::WindowId, bevy::utils::HashSet, std::fmt::Display};
use { use {
bevy::{prelude::Resource, tasks::Task}, bevy::{prelude::Resource, tasks::Task},
crossbeam_channel::{bounded, Receiver, Sender},
planet::{World, WorldGenError}, planet::{World, WorldGenError},
}; };
#[cfg(feature = "render")] #[cfg(feature = "render")]
#[derive(Default, Debug, Resource)] #[derive(Default, Debug, Resource)]
pub(crate) struct CursorMapPosition { pub struct CursorMapPosition {
pub(crate) x: i32, pub x: i32,
pub(crate) y: i32, pub y: i32,
} }
#[cfg(feature = "render")] #[cfg(feature = "render")]
impl Display for CursorMapPosition { impl Display for CursorMapPosition {
@ -20,27 +21,53 @@ impl Display for CursorMapPosition {
#[cfg(feature = "render")] #[cfg(feature = "render")]
#[derive(Resource, Default)] #[derive(Resource, Default)]
pub(crate) struct ShouldRedraw(pub(crate) bool); pub struct ShouldRedraw(pub bool);
#[cfg(feature = "render")] #[cfg(feature = "render")]
#[derive(Default, Resource)] #[derive(Default, Resource)]
pub(crate) struct OpenedWindows(HashSet<WindowId>); pub struct OpenedWindows(HashSet<WindowId>);
#[cfg(feature = "render")] #[cfg(feature = "render")]
impl OpenedWindows { impl OpenedWindows {
pub(crate) fn open(&mut self, id: WindowId) { pub fn open(&mut self, id: WindowId) {
// Ignore opening already opened windows // Ignore opening already opened windows
_ = self.0.insert(id); _ = self.0.insert(id);
} }
pub(crate) fn close(&mut self, id: &WindowId) { pub fn close(&mut self, id: &WindowId) {
// Ignore closing already closed windows // Ignore closing already closed windows
_ = self.0.remove(id); _ = self.0.remove(id);
} }
pub(crate) fn is_open(&self, id: &WindowId) -> bool { pub fn is_open(&self, id: &WindowId) -> bool {
self.0.contains(id) self.0.contains(id)
} }
} }
#[derive(Resource)]
pub struct GenerateWorldProgressChannel(Sender<(f32, String)>, Receiver<(f32, String)>);
impl GenerateWorldProgressChannel {
pub fn new() -> Self {
bounded(1).into()
}
pub fn sender(&self) -> Sender<(f32, String)> {
self.0.clone()
}
pub fn receiver(&self) -> &Receiver<(f32, String)> {
&self.1
}
}
impl Default for GenerateWorldProgressChannel {
fn default() -> Self {
Self::new()
}
}
impl From<(Sender<(f32, String)>, Receiver<(f32, String)>)> for GenerateWorldProgressChannel {
fn from(value: (Sender<(f32, String)>, Receiver<(f32, String)>)) -> Self {
Self(value.0, value.1)
}
}
#[derive(Default, Resource)] #[derive(Default, Resource)]
pub struct GenerateWorldTask(pub Option<Task<Result<World, WorldGenError>>>); pub struct GenerateWorldTask(pub Option<Task<Result<World, WorldGenError>>>);