Make world generation asynchronous; Update dependencies (Bevy 0.9, ayyy)
This commit is contained in:
parent
030f590d79
commit
f65f29e6b0
17 changed files with 535 additions and 503 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
/target
|
/target
|
||||||
|
.fleet/run.json
|
614
Cargo.lock
generated
614
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
12
Cargo.toml
12
Cargo.toml
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "worlds-history-sim-rs"
|
name = "worlds-history-sim-rs"
|
||||||
version = "0.2.1"
|
version = "0.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ path = "planet"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
[dependencies.bevy]
|
[dependencies.bevy]
|
||||||
version = "0.8.1"
|
version = "0.9.0"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
[dependencies.fxhash]
|
[dependencies.fxhash]
|
||||||
|
@ -43,11 +43,15 @@ version = "0.2.1"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.bevy_egui]
|
[dependencies.bevy_egui]
|
||||||
version = "0.16.1"
|
version = "0.17"
|
||||||
optional = true
|
optional = true
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["manage_clipboard"]
|
features = ["manage_clipboard"]
|
||||||
|
|
||||||
[dependencies.tinyfiledialogs]
|
[dependencies.tinyfiledialogs]
|
||||||
version = "3.9.1"
|
version = "3.9.1"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.futures-lite]
|
||||||
|
version = "1.12.0"
|
||||||
|
default-features = false
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "planet"
|
name = "planet"
|
||||||
version = "0.2.1"
|
version = "0.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[profile]
|
[profile]
|
||||||
|
@ -15,7 +15,7 @@ default = ["logging", "render"]
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
|
|
||||||
[dependencies.bevy]
|
[dependencies.bevy]
|
||||||
version = "0.8"
|
version = "0.9.0"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
[dependencies.serde]
|
[dependencies.serde]
|
||||||
|
@ -24,4 +24,4 @@ default-features = false
|
||||||
features = ["derive"]
|
features = ["derive"]
|
||||||
|
|
||||||
[dependencies.ron]
|
[dependencies.ron]
|
||||||
version = "0.7.1"
|
version = "0.8.0"
|
|
@ -1,4 +1,3 @@
|
||||||
// TODO: Logging doesn't seem to work here? Figure out why and fix
|
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
math_util::{
|
math_util::{
|
||||||
|
@ -149,6 +148,27 @@ impl World {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn async_new(width: u32, height: u32, seed: u32) -> World {
|
||||||
|
World {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
seed,
|
||||||
|
terrain: vec![
|
||||||
|
vec![TerrainCell::default(); width.try_into().unwrap()];
|
||||||
|
height.try_into().unwrap()
|
||||||
|
],
|
||||||
|
continent_offsets: [default(); World::NUM_CONTINENTS as usize],
|
||||||
|
continent_sizes: [default(); World::NUM_CONTINENTS as usize],
|
||||||
|
max_altitude: World::MIN_ALTITUDE,
|
||||||
|
min_altitude: World::MAX_ALTITUDE,
|
||||||
|
max_rainfall: World::MIN_RAINFALL,
|
||||||
|
min_rainfall: World::MAX_RAINFALL,
|
||||||
|
max_temperature: World::MIN_TEMPERATURE,
|
||||||
|
min_temperature: World::MAX_TEMPERATURE,
|
||||||
|
rng: StdRng::seed_from_u64(seed as u64),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn generate(&mut self) -> Result<(), WorldGenError> {
|
pub fn generate(&mut self) -> Result<(), WorldGenError> {
|
||||||
if let Err(err) = self.generate_altitude() {
|
if let Err(err) = self.generate_altitude() {
|
||||||
return Err(WorldGenError::CartesianError(err));
|
return Err(WorldGenError::CartesianError(err));
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
use {
|
use {
|
||||||
crate::{World, WorldGenError},
|
crate::{World, WorldGenError},
|
||||||
bevy::{log::warn, utils::default},
|
bevy::{
|
||||||
|
log::warn,
|
||||||
|
prelude::Resource,
|
||||||
|
tasks::{AsyncComputeTaskPool, Task},
|
||||||
|
utils::default,
|
||||||
|
},
|
||||||
rand::random,
|
rand::random,
|
||||||
std::{
|
std::{
|
||||||
error::Error,
|
error::Error,
|
||||||
|
@ -14,7 +19,7 @@ use {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum LoadError {
|
pub enum LoadError {
|
||||||
MissingSave(io::Error),
|
MissingSave(io::Error),
|
||||||
InvalidSave(ron::Error),
|
InvalidSave(ron::error::SpannedError),
|
||||||
}
|
}
|
||||||
impl Error for LoadError {
|
impl Error for LoadError {
|
||||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
|
@ -74,12 +79,15 @@ impl Display for SaveError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default, Resource)]
|
||||||
pub struct WorldManager {
|
pub struct WorldManager {
|
||||||
world: Option<World>,
|
world: Option<World>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorldManager {
|
impl WorldManager {
|
||||||
|
const NEW_WORLD_HEIGHT: u32 = 200;
|
||||||
|
const NEW_WORLD_WIDTH: u32 = 400;
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> WorldManager {
|
pub fn new() -> WorldManager {
|
||||||
default()
|
default()
|
||||||
|
@ -160,11 +168,34 @@ impl WorldManager {
|
||||||
self.get_world().unwrap()
|
self.get_world().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_world(&mut self, world: World) {
|
||||||
|
self.world = Some(world);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_world(&mut self, seed: Option<u32>) -> Result<&World, WorldGenError> {
|
pub fn new_world(&mut self, seed: Option<u32>) -> Result<&World, WorldGenError> {
|
||||||
let seed = seed.unwrap_or_else(random);
|
let seed = seed.unwrap_or_else(random);
|
||||||
let mut new_world = World::new(400, 200, seed);
|
let mut new_world = World::new(
|
||||||
|
WorldManager::NEW_WORLD_WIDTH,
|
||||||
|
WorldManager::NEW_WORLD_HEIGHT,
|
||||||
|
seed,
|
||||||
|
);
|
||||||
new_world.generate()?;
|
new_world.generate()?;
|
||||||
self.world = Some(new_world);
|
self.world = Some(new_world);
|
||||||
Ok(self.get_world().unwrap())
|
Ok(self.get_world().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_world_async(&mut self, seed: Option<u32>) -> Task<Result<World, WorldGenError>> {
|
||||||
|
AsyncComputeTaskPool::get().spawn(async move {
|
||||||
|
let seed = seed.unwrap_or_else(random);
|
||||||
|
let mut new_world = World::async_new(
|
||||||
|
WorldManager::NEW_WORLD_WIDTH,
|
||||||
|
WorldManager::NEW_WORLD_HEIGHT,
|
||||||
|
seed,
|
||||||
|
);
|
||||||
|
match new_world.generate() {
|
||||||
|
Ok(()) => Ok(new_world),
|
||||||
|
Err(err) => Err(err),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
#[cfg(feature = "render")]
|
|
||||||
pub(crate) mod panning;
|
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
use bevy::ecs::component::Component;
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
pub(crate) struct Pan2d {
|
|
||||||
pub(crate) enabled: bool,
|
|
||||||
}
|
|
||||||
impl Pan2d {
|
|
||||||
#[must_use]
|
|
||||||
pub(crate) const fn new() -> Pan2d {
|
|
||||||
Pan2d { enabled: true }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,6 +6,7 @@ use {
|
||||||
world::World,
|
world::World,
|
||||||
},
|
},
|
||||||
log::debug,
|
log::debug,
|
||||||
|
prelude::*,
|
||||||
utils::HashMap,
|
utils::HashMap,
|
||||||
},
|
},
|
||||||
bevy_egui::egui::Ui,
|
bevy_egui::egui::Ui,
|
||||||
|
@ -44,8 +45,8 @@ pub(crate) fn widget<S: 'static + WidgetSystem>(world: &mut World, ui: &mut Ui,
|
||||||
/// A UI widget may have multiple instances. We need to ensure the local state
|
/// A UI widget may have multiple instances. We need to ensure the local state
|
||||||
/// of these instances is not shared. This hashmap allows us to dynamically
|
/// of these instances is not shared. This hashmap allows us to dynamically
|
||||||
/// store instance states.
|
/// store instance states.
|
||||||
#[derive(Default)]
|
#[derive(Default, Resource)]
|
||||||
struct StateInstances<T: WidgetSystem> {
|
struct StateInstances<T: WidgetSystem + 'static> {
|
||||||
instances: HashMap<WidgetId, SystemState<T>>,
|
instances: HashMap<WidgetId, SystemState<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ use {
|
||||||
WidgetSystem,
|
WidgetSystem,
|
||||||
},
|
},
|
||||||
macros::iterable_enum,
|
macros::iterable_enum,
|
||||||
resources::{OpenedWindows, ShouldRedraw},
|
resources::{GenerateWorldTask, OpenedWindows},
|
||||||
},
|
},
|
||||||
bevy::{
|
bevy::{
|
||||||
ecs::{
|
ecs::{
|
||||||
|
@ -34,10 +34,11 @@ 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 => {
|
||||||
if let Err(err) = world_manager.new_world(None) {
|
let generate_world_task = &mut world.resource_mut::<GenerateWorldTask>();
|
||||||
eprintln!("Failed to generate world: {}", err);
|
if generate_world_task.0.is_some() {
|
||||||
|
debug!("Already generating new world")
|
||||||
} else {
|
} else {
|
||||||
world.resource_mut::<ShouldRedraw>().0 = true;
|
generate_world_task.0 = Some(world_manager.new_world_async(None))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ToolbarButton::SaveLoad => {
|
ToolbarButton::SaveLoad => {
|
||||||
|
|
|
@ -8,6 +8,7 @@ use {
|
||||||
world::World,
|
world::World,
|
||||||
},
|
},
|
||||||
log::debug,
|
log::debug,
|
||||||
|
prelude::Resource,
|
||||||
utils::HashMap,
|
utils::HashMap,
|
||||||
},
|
},
|
||||||
bevy_egui::egui::{Context, Ui, Window},
|
bevy_egui::egui::{Context, Ui, Window},
|
||||||
|
@ -80,8 +81,8 @@ fn window<S: 'static + WindowSystem>(world: &mut World, ctx: &Context) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default, Resource)]
|
||||||
struct StateInstances<T: WindowSystem> {
|
struct StateInstances<T: WindowSystem + 'static> {
|
||||||
instances: HashMap<WindowId, SystemState<T>>,
|
instances: HashMap<WindowId, SystemState<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
152
src/main.rs
152
src/main.rs
|
@ -1,5 +1,10 @@
|
||||||
#![cfg_attr(not(feature = "logging"), windows_subsystem = "windows")]
|
#![cfg_attr(not(feature = "logging"), windows_subsystem = "windows")]
|
||||||
|
|
||||||
|
use {
|
||||||
|
crate::resources::GenerateWorldTask,
|
||||||
|
futures_lite::future::{block_on, poll_once},
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) mod components;
|
pub(crate) mod components;
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
pub(crate) mod gui;
|
pub(crate) mod gui;
|
||||||
|
@ -7,52 +12,16 @@ pub(crate) mod macros;
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
pub(crate) mod planet_renderer;
|
pub(crate) mod planet_renderer;
|
||||||
pub(crate) mod plugins;
|
pub(crate) mod plugins;
|
||||||
#[cfg(feature = "render")]
|
|
||||||
pub(crate) mod resources;
|
pub(crate) mod resources;
|
||||||
|
|
||||||
use {
|
use {bevy::prelude::*, planet::WorldManager, plugins::WorldPlugins};
|
||||||
bevy::{
|
|
||||||
app::App,
|
|
||||||
log::LogSettings,
|
|
||||||
utils::{default, tracing::Level},
|
|
||||||
},
|
|
||||||
planet::WorldManager,
|
|
||||||
plugins::WorldPlugins,
|
|
||||||
};
|
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
use {
|
use {
|
||||||
bevy::{
|
bevy::render::camera::RenderTarget,
|
||||||
asset::Assets,
|
|
||||||
core_pipeline::core_2d::{Camera2d, Camera2dBundle},
|
|
||||||
ecs::{
|
|
||||||
change_detection::{Mut, ResMut},
|
|
||||||
query::With,
|
|
||||||
system::{Commands, IntoExclusiveSystem, Query, Res},
|
|
||||||
world::World,
|
|
||||||
},
|
|
||||||
input::{keyboard::KeyCode, Input},
|
|
||||||
prelude::Vec2,
|
|
||||||
render::{
|
|
||||||
camera::{Camera, RenderTarget},
|
|
||||||
render_resource::{
|
|
||||||
Extent3d,
|
|
||||||
TextureDescriptor,
|
|
||||||
TextureDimension,
|
|
||||||
TextureFormat,
|
|
||||||
TextureUsages,
|
|
||||||
},
|
|
||||||
texture::{Image, ImageSettings},
|
|
||||||
},
|
|
||||||
sprite::{Sprite, SpriteBundle},
|
|
||||||
transform::components::GlobalTransform,
|
|
||||||
window::{WindowDescriptor, Windows},
|
|
||||||
winit::WinitSettings,
|
|
||||||
},
|
|
||||||
bevy_egui::{
|
bevy_egui::{
|
||||||
egui::{FontData, FontDefinitions, FontFamily},
|
egui::{FontData, FontDefinitions, FontFamily},
|
||||||
EguiContext,
|
EguiContext,
|
||||||
},
|
},
|
||||||
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_renderer::{WorldRenderSettings, WorldRenderer},
|
planet_renderer::{WorldRenderSettings, WorldRenderer},
|
||||||
resources::{CursorMapPosition, OpenedWindows, ShouldRedraw},
|
resources::{CursorMapPosition, OpenedWindows, ShouldRedraw},
|
||||||
|
@ -98,16 +67,45 @@ fn update_cursor_map_position(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_generate_world_task(
|
||||||
|
mut generate_world_task: ResMut<'_, GenerateWorldTask>,
|
||||||
|
mut world_manager: ResMut<'_, WorldManager>,
|
||||||
|
#[cfg(feature = "render")] mut should_redraw: ResMut<'_, ShouldRedraw>,
|
||||||
|
) {
|
||||||
|
if let Some(task) = &mut generate_world_task.0 {
|
||||||
|
if task.is_finished() {
|
||||||
|
debug!("Done");
|
||||||
|
if let Some(result) = block_on(poll_once(task)) {
|
||||||
|
match result {
|
||||||
|
Ok(world) => {
|
||||||
|
world_manager.set_world(world);
|
||||||
|
#[cfg(feature = "render")]
|
||||||
|
{
|
||||||
|
should_redraw.0 = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => error!("{err:#?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
generate_world_task.0 = None;
|
||||||
|
} else {
|
||||||
|
debug!("Working")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
fn generate_graphics(
|
fn generate_graphics(
|
||||||
mut commands: Commands<'_, '_>,
|
mut commands: Commands<'_, '_>,
|
||||||
world_manager: ResMut<'_, WorldManager>,
|
world_manager: ResMut<'_, WorldManager>,
|
||||||
mut images: ResMut<'_, Assets<Image>>,
|
images: ResMut<'_, Assets<Image>>,
|
||||||
mut egui_context: ResMut<'_, EguiContext>,
|
egui_context: ResMut<'_, EguiContext>,
|
||||||
mut render_settings: ResMut<'_, WorldRenderSettings>,
|
render_settings: ResMut<'_, WorldRenderSettings>,
|
||||||
) {
|
) {
|
||||||
// Add Julia-Mono font to egui
|
// Add Julia-Mono font to egui
|
||||||
|
|
||||||
{
|
{
|
||||||
|
let egui_context = egui_context.into_inner();
|
||||||
let ctx = egui_context.ctx_mut();
|
let ctx = egui_context.ctx_mut();
|
||||||
let mut fonts = FontDefinitions::default();
|
let mut fonts = FontDefinitions::default();
|
||||||
const FONT_NAME: &str = "Julia-Mono";
|
const FONT_NAME: &str = "Julia-Mono";
|
||||||
|
@ -143,6 +141,14 @@ fn generate_graphics(
|
||||||
};
|
};
|
||||||
// Set up 2D map mode
|
// Set up 2D map mode
|
||||||
{
|
{
|
||||||
|
use bevy::render::render_resource::{
|
||||||
|
TextureDescriptor,
|
||||||
|
TextureDimension,
|
||||||
|
TextureFormat,
|
||||||
|
TextureUsages,
|
||||||
|
};
|
||||||
|
let images = images.into_inner();
|
||||||
|
let mut render_settings = render_settings.into_inner();
|
||||||
let map_image_handle = images.add(Image {
|
let map_image_handle = images.add(Image {
|
||||||
data: vec![],
|
data: vec![],
|
||||||
texture_descriptor: TextureDescriptor {
|
texture_descriptor: TextureDescriptor {
|
||||||
|
@ -156,14 +162,13 @@ fn generate_graphics(
|
||||||
},
|
},
|
||||||
..default()
|
..default()
|
||||||
});
|
});
|
||||||
render_settings.map_image_handle_id = Some(map_image_handle.id);
|
render_settings.map_image_handle_id = Some(map_image_handle.id());
|
||||||
_ = commands
|
_ = commands.spawn(Camera2dBundle::default());
|
||||||
.spawn_bundle(Camera2dBundle::default())
|
|
||||||
.insert(Pan2d::new());
|
|
||||||
|
|
||||||
// TODO: Switch to egui
|
// TODO: Switch to egui
|
||||||
_ = commands.spawn_bundle(SpriteBundle {
|
_ = commands.spawn(SpriteBundle {
|
||||||
texture: images.get_handle(map_image_handle.id),
|
texture: images
|
||||||
|
.get_handle(unsafe { render_settings.map_image_handle_id.unwrap_unchecked() }),
|
||||||
sprite: Sprite {
|
sprite: Sprite {
|
||||||
custom_size: Some(custom_sprite_size),
|
custom_size: Some(custom_sprite_size),
|
||||||
..default()
|
..default()
|
||||||
|
@ -234,7 +239,7 @@ fn redraw_map(
|
||||||
let map_image = images
|
let map_image = images
|
||||||
.get_mut(&map_image_handle)
|
.get_mut(&map_image_handle)
|
||||||
.expect("Map image handle pointing to non-existing image");
|
.expect("Map image handle pointing to non-existing image");
|
||||||
map_image.resize(Extent3d {
|
map_image.resize(bevy::render::render_resource::Extent3d {
|
||||||
width: world_manager.world().width,
|
width: world_manager.world().width,
|
||||||
height: world_manager.world().height,
|
height: world_manager.world().height,
|
||||||
depth_or_array_layers: 1,
|
depth_or_array_layers: 1,
|
||||||
|
@ -246,48 +251,49 @@ fn redraw_map(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
const WORLD_SCALE: i32 = 4;
|
const WORLD_SCALE: i32 = 2;
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut app = App::new();
|
let mut app = App::new();
|
||||||
let mut manager = WorldManager::new();
|
let mut manager = WorldManager::new();
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
{
|
{
|
||||||
let world = manager.new_world(None)?;
|
use bevy::winit::WinitSettings;
|
||||||
|
|
||||||
|
let world = manager.new_world(Some(0))?;
|
||||||
_ = app
|
_ = app
|
||||||
.insert_resource(WinitSettings::game())
|
.insert_resource(WinitSettings::game())
|
||||||
// Use nearest-neighbor rendering for cripsier pixels
|
.insert_resource(CursorMapPosition::default())
|
||||||
.insert_resource(ImageSettings::default_nearest())
|
.insert_resource(OpenedWindows::default())
|
||||||
.insert_resource(WindowDescriptor {
|
.insert_resource(WorldRenderSettings::default())
|
||||||
|
.insert_resource(ShouldRedraw::default())
|
||||||
|
.insert_resource(GenerateWorldTask::default())
|
||||||
|
.add_startup_system(generate_graphics)
|
||||||
|
.add_system(update_gui)
|
||||||
|
.add_system(update_cursor_map_position)
|
||||||
|
.add_system(open_tile_info)
|
||||||
|
.add_system(redraw_map);
|
||||||
|
|
||||||
|
app.add_plugins(WorldPlugins.set(WindowPlugin {
|
||||||
|
window: WindowDescriptor {
|
||||||
width: (WORLD_SCALE * world.width as i32) as f32,
|
width: (WORLD_SCALE * world.width as i32) as f32,
|
||||||
height: (WORLD_SCALE * world.height as i32) as f32,
|
height: (WORLD_SCALE * world.height as i32) as f32,
|
||||||
title: String::from("World-RS"),
|
title: String::from("World-RS"),
|
||||||
resizable: true,
|
resizable: true,
|
||||||
..default()
|
..default()
|
||||||
})
|
},
|
||||||
.insert_resource(CursorMapPosition::default())
|
..default()
|
||||||
.insert_resource(OpenedWindows::default())
|
}));
|
||||||
.insert_resource(WorldRenderSettings::default())
|
|
||||||
.insert_resource(ShouldRedraw::default())
|
|
||||||
.add_startup_system(generate_graphics)
|
|
||||||
.add_system(update_gui.exclusive_system())
|
|
||||||
.add_system(update_cursor_map_position)
|
|
||||||
.add_system(open_tile_info)
|
|
||||||
.add_system(redraw_map);
|
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "render"))]
|
#[cfg(not(feature = "render"))]
|
||||||
{
|
{
|
||||||
_ = manager.new_world()?
|
_ = manager.new_world(Some(0))?;
|
||||||
|
|
||||||
|
app.add_plugins(WorldPlugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = app.insert_resource(LogSettings {
|
app.add_system(handle_generate_world_task)
|
||||||
#[cfg(feature = "logging")]
|
.insert_resource(manager)
|
||||||
level: Level::DEBUG,
|
.run();
|
||||||
#[cfg(not(feature = "logging"))]
|
|
||||||
level: Level::WARN,
|
|
||||||
..default()
|
|
||||||
});
|
|
||||||
|
|
||||||
app.add_plugins(WorldPlugins).insert_resource(manager).run();
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
use {
|
use {
|
||||||
crate::macros::iterable_enum_stringify,
|
crate::macros::iterable_enum_stringify,
|
||||||
bevy::{asset::HandleId, prelude::Color, utils::HashSet},
|
bevy::{
|
||||||
|
asset::HandleId,
|
||||||
|
prelude::{Color, Resource},
|
||||||
|
utils::HashSet,
|
||||||
|
},
|
||||||
planet::{BiomeStats, TerrainCell, World, WorldManager},
|
planet::{BiomeStats, TerrainCell, World, WorldManager},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -15,7 +19,7 @@ iterable_enum_stringify!(WorldOverlay {
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default, Resource)]
|
||||||
pub struct WorldRenderSettings {
|
pub struct WorldRenderSettings {
|
||||||
pub map_image_handle_id: Option<HandleId>,
|
pub map_image_handle_id: Option<HandleId>,
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,2 @@
|
||||||
#[cfg(feature = "render")]
|
|
||||||
pub(crate) mod panning_plugin;
|
|
||||||
pub(crate) mod world_plugins;
|
pub(crate) mod world_plugins;
|
||||||
pub(crate) use world_plugins::WorldPlugins;
|
pub(crate) use world_plugins::WorldPlugins;
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
use {
|
|
||||||
crate::components::panning::Pan2d,
|
|
||||||
bevy::{
|
|
||||||
app::{App, Plugin},
|
|
||||||
ecs::{
|
|
||||||
event::EventReader,
|
|
||||||
query::With,
|
|
||||||
system::{Query, Res},
|
|
||||||
},
|
|
||||||
input::{
|
|
||||||
mouse::{MouseButton, MouseMotion},
|
|
||||||
Input,
|
|
||||||
},
|
|
||||||
sprite::Sprite,
|
|
||||||
transform::components::Transform,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub(crate) struct PanningPlugin;
|
|
||||||
|
|
||||||
impl Plugin for PanningPlugin {
|
|
||||||
fn build(&self, app: &mut App) {
|
|
||||||
_ = app.add_system(panning_system_2d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn panning_system_2d(
|
|
||||||
mut query: Query<'_, '_, (&mut Transform, &Pan2d), With<Sprite>>,
|
|
||||||
mut mouse_motion_events: EventReader<'_, '_, MouseMotion>,
|
|
||||||
input_mouse: Res<'_, Input<MouseButton>>,
|
|
||||||
) {
|
|
||||||
if !input_mouse.pressed(MouseButton::Left) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut horizontal = 0.0;
|
|
||||||
for movement in mouse_motion_events.iter() {
|
|
||||||
horizontal += movement.delta.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
query.for_each_mut(|(mut transform, pan)| {
|
|
||||||
if pan.enabled {
|
|
||||||
transform.translation.x += horizontal;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,65 +1,75 @@
|
||||||
pub(crate) struct WorldPlugins;
|
pub(crate) struct WorldPlugins;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "render"))]
|
||||||
|
use bevy::app::ScheduleRunnerPlugin;
|
||||||
|
#[cfg(all(feature = "logging"))]
|
||||||
|
use bevy::diagnostic::{DiagnosticsPlugin, FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin};
|
||||||
use bevy::{
|
use bevy::{
|
||||||
app::{PluginGroup, PluginGroupBuilder},
|
app::{PluginGroup, PluginGroupBuilder},
|
||||||
core::CorePlugin,
|
core::CorePlugin,
|
||||||
diagnostic::{DiagnosticsPlugin, LogDiagnosticsPlugin},
|
log::{Level, LogPlugin},
|
||||||
log::LogPlugin,
|
prelude::*,
|
||||||
time::TimePlugin,
|
time::TimePlugin,
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "render")]
|
||||||
|
use {
|
||||||
|
bevy::{
|
||||||
|
asset::AssetPlugin,
|
||||||
|
core_pipeline::CorePipelinePlugin,
|
||||||
|
input::InputPlugin,
|
||||||
|
render::RenderPlugin,
|
||||||
|
sprite::SpritePlugin,
|
||||||
|
text::TextPlugin,
|
||||||
|
transform::TransformPlugin,
|
||||||
|
ui::UiPlugin,
|
||||||
|
window::WindowPlugin,
|
||||||
|
winit::WinitPlugin,
|
||||||
|
},
|
||||||
|
bevy_egui::EguiPlugin,
|
||||||
|
};
|
||||||
|
|
||||||
impl PluginGroup for WorldPlugins {
|
impl PluginGroup for WorldPlugins {
|
||||||
fn build(&mut self, group: &mut PluginGroupBuilder) {
|
fn build(self) -> PluginGroupBuilder {
|
||||||
_ = group.add(LogPlugin).add(CorePlugin).add(TimePlugin);
|
let mut group_builder = PluginGroupBuilder::start::<Self>()
|
||||||
|
.add(LogPlugin {
|
||||||
|
#[cfg(feature = "logging")]
|
||||||
|
level: Level::DEBUG,
|
||||||
|
#[cfg(not(feature = "logging"))]
|
||||||
|
level: Level::WARN,
|
||||||
|
..default()
|
||||||
|
})
|
||||||
|
.add(CorePlugin::default()) // sets compute pool config
|
||||||
|
.add(TimePlugin);
|
||||||
|
|
||||||
#[cfg(feature = "render")]
|
#[cfg(feature = "render")]
|
||||||
{
|
{
|
||||||
use {
|
group_builder = group_builder
|
||||||
crate::plugins::panning_plugin::PanningPlugin,
|
|
||||||
bevy::{
|
|
||||||
asset::AssetPlugin,
|
|
||||||
core_pipeline::CorePipelinePlugin,
|
|
||||||
hierarchy::HierarchyPlugin,
|
|
||||||
input::InputPlugin,
|
|
||||||
render::RenderPlugin,
|
|
||||||
sprite::SpritePlugin,
|
|
||||||
text::TextPlugin,
|
|
||||||
transform::TransformPlugin,
|
|
||||||
ui::UiPlugin,
|
|
||||||
window::WindowPlugin,
|
|
||||||
winit::WinitPlugin,
|
|
||||||
},
|
|
||||||
bevy_egui::EguiPlugin,
|
|
||||||
};
|
|
||||||
|
|
||||||
_ = group
|
|
||||||
.add(TransformPlugin)
|
.add(TransformPlugin)
|
||||||
// hierarchy
|
|
||||||
.add(InputPlugin)
|
.add(InputPlugin)
|
||||||
.add(WindowPlugin)
|
.add(WindowPlugin::default())
|
||||||
.add(AssetPlugin)
|
.add(AssetPlugin::default())
|
||||||
.add(HierarchyPlugin)
|
|
||||||
.add(WinitPlugin)
|
|
||||||
.add(RenderPlugin)
|
.add(RenderPlugin)
|
||||||
|
.add(ImagePlugin::default_nearest())
|
||||||
|
.add(WinitPlugin)
|
||||||
.add(CorePipelinePlugin)
|
.add(CorePipelinePlugin)
|
||||||
.add(SpritePlugin)
|
.add(SpritePlugin)
|
||||||
.add(TextPlugin)
|
.add(TextPlugin)
|
||||||
.add(UiPlugin)
|
.add(UiPlugin)
|
||||||
.add(PanningPlugin)
|
|
||||||
.add(EguiPlugin);
|
.add(EguiPlugin);
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "render"))]
|
#[cfg(not(feature = "render"))]
|
||||||
{
|
{
|
||||||
use bevy::app::ScheduleRunnerPlugin;
|
group_builder = group_builder.add(ScheduleRunnerPlugin);
|
||||||
_ = group.add(ScheduleRunnerPlugin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = group.add(DiagnosticsPlugin);
|
#[cfg(feature = "logging")]
|
||||||
#[cfg(all(feature = "logging"))]
|
|
||||||
{
|
{
|
||||||
use bevy::diagnostic::FrameTimeDiagnosticsPlugin;
|
group_builder = group_builder
|
||||||
_ = group.add(FrameTimeDiagnosticsPlugin);
|
.add(DiagnosticsPlugin)
|
||||||
|
.add(FrameTimeDiagnosticsPlugin)
|
||||||
|
.add(LogDiagnosticsPlugin::default());
|
||||||
}
|
}
|
||||||
_ = group.add(LogDiagnosticsPlugin::default());
|
|
||||||
|
group_builder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,38 @@
|
||||||
|
#[cfg(feature = "render")]
|
||||||
use {crate::gui::WindowId, bevy::utils::HashSet, std::fmt::Display};
|
use {crate::gui::WindowId, bevy::utils::HashSet, std::fmt::Display};
|
||||||
|
use {
|
||||||
|
bevy::{prelude::Resource, tasks::Task},
|
||||||
|
planet::{World, WorldGenError},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[cfg(feature = "render")]
|
||||||
|
#[derive(Default, Debug, Resource)]
|
||||||
pub(crate) struct CursorMapPosition {
|
pub(crate) struct CursorMapPosition {
|
||||||
pub(crate) x: i32,
|
pub(crate) x: i32,
|
||||||
pub(crate) y: i32,
|
pub(crate) y: i32,
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "render")]
|
||||||
impl Display for CursorMapPosition {
|
impl Display for CursorMapPosition {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.write_fmt(format_args!("x: {}, y: {}", self.x, self.y))
|
f.write_fmt(format_args!("x: {}, y: {}", self.x, self.y))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "render")]
|
||||||
|
#[derive(Resource)]
|
||||||
pub(crate) struct ShouldRedraw(pub(crate) bool);
|
pub(crate) struct ShouldRedraw(pub(crate) bool);
|
||||||
|
#[cfg(feature = "render")]
|
||||||
impl Default for ShouldRedraw {
|
impl Default for ShouldRedraw {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self(true)
|
Self(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[cfg(feature = "render")]
|
||||||
|
#[derive(Default, Resource)]
|
||||||
pub(crate) struct OpenedWindows(HashSet<WindowId>);
|
pub(crate) struct OpenedWindows(HashSet<WindowId>);
|
||||||
|
|
||||||
|
#[cfg(feature = "render")]
|
||||||
impl OpenedWindows {
|
impl OpenedWindows {
|
||||||
pub(crate) fn open(&mut self, id: WindowId) {
|
pub(crate) fn open(&mut self, id: WindowId) {
|
||||||
// Ignore opening already opened windows
|
// Ignore opening already opened windows
|
||||||
|
@ -36,3 +48,6 @@ impl OpenedWindows {
|
||||||
self.0.contains(id)
|
self.0.contains(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Resource)]
|
||||||
|
pub struct GenerateWorldTask(pub Option<Task<Result<World, WorldGenError>>>);
|
||||||
|
|
Loading…
Reference in a new issue