Add progress bar for world generation.
Also remove a bunch of spammy logging
This commit is contained in:
parent
1a3b4bb72c
commit
9581a5d206
21 changed files with 221 additions and 89 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -54,4 +54,9 @@ 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"]
|
|
@ -26,4 +26,9 @@ features = ["derive"]
|
||||||
[dependencies.postcard]
|
[dependencies.postcard]
|
||||||
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"]
|
|
@ -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;
|
||||||
|
|
|
@ -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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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) => {
|
||||||
|
@ -124,7 +124,7 @@ impl WorldManager {
|
||||||
if let Err(err) = file.read_to_end(&mut buf) {
|
if let Err(err) = file.read_to_end(&mut buf) {
|
||||||
return Err(LoadError::MissingSave(err));
|
return Err(LoadError::MissingSave(err));
|
||||||
};
|
};
|
||||||
|
|
||||||
match postcard::from_bytes(buf.as_slice()) {
|
match postcard::from_bytes(buf.as_slice()) {
|
||||||
Ok(world) => {
|
Ok(world) => {
|
||||||
self.world = Some(world);
|
self.world = Some(world);
|
||||||
|
@ -139,11 +139,15 @@ impl WorldManager {
|
||||||
self.world.as_ref()
|
self.world.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_world(&mut self, world: World) {
|
pub fn set_world(&mut self, world: World) {
|
||||||
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),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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))
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
mod toolbar;
|
mod toolbar;
|
||||||
pub(crate) use toolbar::ToolbarWidget;
|
pub use toolbar::ToolbarWidget;
|
||||||
|
|
|
@ -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 ())>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 ())>,
|
||||||
|
|
|
@ -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 ())>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ())>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ())>,
|
||||||
}
|
}
|
||||||
|
|
41
src/main.rs
41
src/main.rs
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
pub(crate) mod world_plugins;
|
pub mod world_plugins;
|
||||||
pub(crate) use world_plugins::WorldPlugins;
|
pub use world_plugins::WorldPlugins;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>>>);
|
||||||
|
|
Loading…
Reference in a new issue