Switch from bevy_ui to bevy_egui
Generic widgets done with inspiration from: https://github.com/bevyengine/bevy/discussions/5522
This commit is contained in:
parent
2a1a8855d8
commit
91018d1d73
12 changed files with 508 additions and 584 deletions
75
Cargo.lock
generated
75
Cargo.lock
generated
|
@ -29,6 +29,18 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57e6e951cfbb2db8de1828d49073a113a29fd7117b1596caa781a258c7e38d72"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.19"
|
||||
|
@ -126,6 +138,12 @@ version = "4.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524"
|
||||
|
||||
[[package]]
|
||||
name = "atomic_refcell"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73b5e5f48b927f04e952dedc932f31995a65a0bf65ec971c74436e51bf6e970d"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
|
@ -277,6 +295,16 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bevy_egui"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d365761fd6a5c227b1f88f38b560287334accb69cfe938443e27615464edc897"
|
||||
dependencies = [
|
||||
"bevy",
|
||||
"egui",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bevy_encase_derive"
|
||||
version = "0.8.1"
|
||||
|
@ -678,7 +706,7 @@ version = "0.8.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6e9aa1866c1cf7ee000f281ce9e90d02d701f5c7380a107252017e58e2f5246"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"ahash 0.7.6",
|
||||
"getrandom",
|
||||
"hashbrown",
|
||||
"instant",
|
||||
|
@ -1072,6 +1100,26 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
||||
|
||||
[[package]]
|
||||
name = "egui"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc9fcd393c3daaaf5909008a1d948319d538b79c51871e4df0993260260a94e4"
|
||||
dependencies = [
|
||||
"ahash 0.8.0",
|
||||
"epaint",
|
||||
"nohash-hasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "emath"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9542a40106fdba943a055f418d1746a050e1a903a049b030c2b097d4686a33cf"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encase"
|
||||
version = "0.3.0"
|
||||
|
@ -1114,6 +1162,21 @@ dependencies = [
|
|||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "epaint"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ba04741be7f6602b1a1b28f1082cce45948a7032961c52814f8946b28493300"
|
||||
dependencies = [
|
||||
"ab_glyph",
|
||||
"ahash 0.8.0",
|
||||
"atomic_refcell",
|
||||
"bytemuck",
|
||||
"emath",
|
||||
"nohash-hasher",
|
||||
"parking_lot 0.12.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "erased-serde"
|
||||
version = "0.3.23"
|
||||
|
@ -1366,7 +1429,7 @@ version = "0.12.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"ahash 0.7.6",
|
||||
"serde",
|
||||
]
|
||||
|
||||
|
@ -1702,6 +1765,12 @@ dependencies = [
|
|||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nohash-hasher"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.1"
|
||||
|
@ -2768,6 +2837,8 @@ name = "worlds-history-sim-rs"
|
|||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"bevy",
|
||||
"bevy_egui",
|
||||
"fxhash",
|
||||
"planet",
|
||||
]
|
||||
|
||||
|
|
16
Cargo.toml
16
Cargo.toml
|
@ -10,13 +10,23 @@ release = { strip = "symbols", lto = "thin", opt-level = "z" }
|
|||
[features]
|
||||
logging = ["planet/logging"]
|
||||
globe_view = ["planet/globe_view", "render"]
|
||||
render = ["bevy/bevy_asset", "bevy/bevy_winit", "bevy/x11", "bevy/wayland", "bevy/render", "planet/render"]
|
||||
default = ["render", "logging"]
|
||||
render = ["bevy/bevy_asset", "bevy/bevy_winit", "bevy/x11", "bevy/wayland", "bevy/render", "planet/render", "dep:fxhash", "dep:bevy_egui"]
|
||||
default = ["render", "logging", "globe_view"]
|
||||
|
||||
[dependencies.planet]
|
||||
path = "planet"
|
||||
default-features = false
|
||||
|
||||
[dependencies.bevy]
|
||||
version = "0.8"
|
||||
version = "0.8.1"
|
||||
default-features = false
|
||||
|
||||
[dependencies.fxhash]
|
||||
version = "0.2.1"
|
||||
optional = true
|
||||
|
||||
[dependencies.bevy_egui]
|
||||
version = "0.16.1"
|
||||
optional = true
|
||||
default-features = false
|
||||
# features = ["manage_clipboard"] # In the future, when I add text input.
|
|
@ -1,65 +0,0 @@
|
|||
#[cfg(feature = "render")]
|
||||
use {crate::macros::iterable_enum, bevy::ecs::component::Component};
|
||||
|
||||
#[cfg(all(feature = "render", not(feature = "globe_view")))]
|
||||
iterable_enum!(ToolbarButton {
|
||||
GenerateWorld,
|
||||
SaveWorld,
|
||||
LoadWorld,
|
||||
Rainfall,
|
||||
Temperature,
|
||||
PlanetView,
|
||||
Contours,
|
||||
});
|
||||
#[cfg(all(feature = "render", feature = "globe_view"))]
|
||||
iterable_enum!(ToolbarButton {
|
||||
GenerateWorld,
|
||||
SaveWorld,
|
||||
LoadWorld,
|
||||
Rainfall,
|
||||
Temperature,
|
||||
PlanetView,
|
||||
Contours,
|
||||
GlobeView,
|
||||
});
|
||||
|
||||
#[cfg(feature = "render")]
|
||||
impl From<ToolbarButton> for &'static str {
|
||||
fn from(button: ToolbarButton) -> Self {
|
||||
match button {
|
||||
ToolbarButton::Rainfall => "Toggle rainfall",
|
||||
ToolbarButton::Temperature => "Toggle temperature",
|
||||
ToolbarButton::Contours => "Toggle contours",
|
||||
ToolbarButton::PlanetView => "Cycle view",
|
||||
ToolbarButton::GenerateWorld => "Generate new world",
|
||||
ToolbarButton::SaveWorld => "Save",
|
||||
ToolbarButton::LoadWorld => "Load",
|
||||
#[cfg(feature = "globe_view")]
|
||||
ToolbarButton::GlobeView => "Toggle globe",
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "render")]
|
||||
impl From<&ToolbarButton> for &'static str {
|
||||
fn from(button: &ToolbarButton) -> Self {
|
||||
(*button).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "render")]
|
||||
impl From<ToolbarButton> for String {
|
||||
fn from(button: ToolbarButton) -> Self {
|
||||
<&'static str>::from(button).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "render")]
|
||||
impl From<&ToolbarButton> for String {
|
||||
fn from(button: &ToolbarButton) -> Self {
|
||||
<&'static str>::from(button).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "render")]
|
||||
#[derive(Component)]
|
||||
pub(crate) struct InfoPanel;
|
|
@ -1,3 +1,2 @@
|
|||
pub(crate) mod markers;
|
||||
#[cfg(feature = "render")]
|
||||
pub(crate) mod panning;
|
||||
|
|
3
src/gui/mod.rs
Normal file
3
src/gui/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub(crate) mod widget;
|
||||
pub(crate) mod widgets;
|
||||
pub(crate) use widget::*;
|
73
src/gui/widget.rs
Normal file
73
src/gui/widget.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
use {
|
||||
bevy::{
|
||||
ecs::{
|
||||
change_detection::Mut,
|
||||
system::{SystemParam, SystemState},
|
||||
world::World,
|
||||
},
|
||||
log::debug,
|
||||
utils::HashMap,
|
||||
},
|
||||
bevy_egui::egui::Ui,
|
||||
fxhash::FxHasher32,
|
||||
std::hash::Hasher,
|
||||
};
|
||||
|
||||
pub(crate) trait WidgetSystem: SystemParam {
|
||||
fn system(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) {
|
||||
// We need to cache `SystemState` to allow for a system's locally tracked state
|
||||
if !world.contains_resource::<StateInstances<S>>() {
|
||||
// Note, this message should only appear once! If you see it twice in the logs,
|
||||
// the function may have been called recursively, and will panic.
|
||||
debug!("Init system state {}", std::any::type_name::<S>());
|
||||
world.insert_resource(StateInstances::<S> {
|
||||
instances: HashMap::new(),
|
||||
});
|
||||
}
|
||||
world.resource_scope(|world, mut states: Mut<'_, StateInstances<S>>| {
|
||||
if !states.instances.contains_key(&id) {
|
||||
debug!(
|
||||
"Registering system state for widget {id:?} of type {}",
|
||||
std::any::type_name::<S>()
|
||||
);
|
||||
_ = states.instances.insert(id, SystemState::new(world));
|
||||
}
|
||||
let cached_state = states.instances.get_mut(&id).unwrap();
|
||||
S::system(world, cached_state, ui, id);
|
||||
cached_state.apply(world);
|
||||
});
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// store instance states.
|
||||
#[derive(Default)]
|
||||
struct StateInstances<T: WidgetSystem> {
|
||||
instances: HashMap<WidgetId, SystemState<T>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct WidgetId(pub(crate) u64);
|
||||
impl WidgetId {
|
||||
#[must_use]
|
||||
pub(crate) fn new(name: &str) -> Self {
|
||||
let bytes = name.as_bytes();
|
||||
let mut hasher = FxHasher32::default();
|
||||
hasher.write(bytes);
|
||||
WidgetId(hasher.finish())
|
||||
}
|
||||
|
||||
// #[must_use]
|
||||
// pub(crate) fn with(&self, name: &str) -> Self {
|
||||
// Self::new(&format!("{}{name}", self.0))
|
||||
// }
|
||||
}
|
||||
impl From<&str> for WidgetId {
|
||||
#[must_use]
|
||||
fn from(str: &str) -> Self {
|
||||
Self::new(str)
|
||||
}
|
||||
}
|
50
src/gui/widgets/info_panel.rs
Normal file
50
src/gui/widgets/info_panel.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
#[cfg(feature = "logging")]
|
||||
use bevy::diagnostic::{Diagnostics, FrameTimeDiagnosticsPlugin};
|
||||
use {
|
||||
crate::{
|
||||
gui::{WidgetId, WidgetSystem},
|
||||
resources::CursorMapPosition,
|
||||
},
|
||||
bevy::ecs::{
|
||||
system::{SystemParam, SystemState},
|
||||
world::World,
|
||||
},
|
||||
bevy_egui::egui::{Grid, Ui},
|
||||
std::marker::PhantomData,
|
||||
};
|
||||
|
||||
#[derive(SystemParam)]
|
||||
pub(crate) struct InfoPanel<'w, 's> {
|
||||
#[system_param(ignore)]
|
||||
_phantom: PhantomData<(&'w (), &'s ())>,
|
||||
}
|
||||
impl WidgetSystem for InfoPanel<'_, '_> {
|
||||
fn system(world: &mut World, _state: &mut SystemState<Self>, ui: &mut Ui, _id: WidgetId) {
|
||||
// This will get everything our system/widget requested
|
||||
// let mut params = state.get_mut(world);
|
||||
|
||||
_ = Grid::new("info_panel")
|
||||
.num_columns(2)
|
||||
.striped(true)
|
||||
.show(ui, |ui| {
|
||||
#[cfg(feature = "logging")]
|
||||
{
|
||||
let diagnostics = world.resource::<Diagnostics>();
|
||||
|
||||
_ = ui.label("Framerate");
|
||||
_ = ui.label(
|
||||
match diagnostics.get_measurement(FrameTimeDiagnosticsPlugin::FPS) {
|
||||
None => f64::NAN,
|
||||
Some(fps) => fps.value.round(),
|
||||
}
|
||||
.to_string(),
|
||||
);
|
||||
ui.end_row();
|
||||
}
|
||||
|
||||
_ = ui.label("Cursor position");
|
||||
_ = ui.label(world.resource::<CursorMapPosition>().to_string());
|
||||
ui.end_row()
|
||||
});
|
||||
}
|
||||
}
|
4
src/gui/widgets/mod.rs
Normal file
4
src/gui/widgets/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
pub(crate) mod info_panel;
|
||||
pub(crate) use info_panel::InfoPanel;
|
||||
pub(crate) mod toolbar;
|
||||
pub(crate) use toolbar::ToolbarWidget;
|
181
src/gui/widgets/toolbar.rs
Normal file
181
src/gui/widgets/toolbar.rs
Normal file
|
@ -0,0 +1,181 @@
|
|||
use {
|
||||
crate::{
|
||||
components::panning::Pan2d,
|
||||
gui::{WidgetId, WidgetSystem},
|
||||
macros::iterable_enum,
|
||||
},
|
||||
bevy::{
|
||||
ecs::{
|
||||
component::Component,
|
||||
system::{SystemParam, SystemState},
|
||||
world::World,
|
||||
},
|
||||
log::debug,
|
||||
prelude::{Assets, Camera, Camera2d, Camera3d, Image, Mut, With, Without},
|
||||
render::render_resource::Extent3d,
|
||||
},
|
||||
bevy_egui::egui::{Layout, Ui},
|
||||
planet::WorldManager,
|
||||
std::marker::PhantomData,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "globe_view"))]
|
||||
iterable_enum!(ToolbarButton {
|
||||
GenerateWorld,
|
||||
SaveWorld,
|
||||
LoadWorld,
|
||||
Rainfall,
|
||||
Temperature,
|
||||
PlanetView,
|
||||
Contours,
|
||||
});
|
||||
#[cfg(feature = "globe_view")]
|
||||
iterable_enum!(ToolbarButton {
|
||||
GenerateWorld,
|
||||
SaveWorld,
|
||||
LoadWorld,
|
||||
Rainfall,
|
||||
Temperature,
|
||||
PlanetView,
|
||||
Contours,
|
||||
GlobeView,
|
||||
});
|
||||
fn update_textures(world: &mut World) {
|
||||
debug!("refreshing world texture");
|
||||
world.resource_scope(|world, world_manager: Mut<WorldManager>| {
|
||||
let mut images = world.resource_mut::<Assets<Image>>();
|
||||
|
||||
let map_image_handle = images.get_handle(
|
||||
world_manager
|
||||
.map_image_handle_id
|
||||
.expect("No map image handle"),
|
||||
);
|
||||
let map_image = images
|
||||
.get_mut(&map_image_handle)
|
||||
.expect("Map image handle pointing to non-existing image");
|
||||
map_image.resize(Extent3d {
|
||||
width: world_manager.world().width,
|
||||
height: world_manager.world().height,
|
||||
depth_or_array_layers: 1,
|
||||
});
|
||||
map_image.data = world_manager.map_color_bytes();
|
||||
});
|
||||
}
|
||||
impl ToolbarButton {
|
||||
fn clicked(self, world: &mut World) {
|
||||
match self {
|
||||
ToolbarButton::GenerateWorld => {
|
||||
world.resource_scope(|world, mut world_manager: Mut<'_, WorldManager>| {
|
||||
match world_manager.new_world() {
|
||||
Err(err) => {
|
||||
eprintln!("Failed to generate world: {}", err);
|
||||
},
|
||||
Ok(_) => {
|
||||
update_textures(world);
|
||||
},
|
||||
}
|
||||
})
|
||||
},
|
||||
ToolbarButton::SaveWorld => {
|
||||
if let Err(err) = world.resource::<WorldManager>().save_world("planet.ron") {
|
||||
eprintln!("Failed to save planet.ron: {}", err);
|
||||
}
|
||||
},
|
||||
ToolbarButton::LoadWorld => {
|
||||
world.resource_scope(|world, mut images: Mut<'_, Assets<Image>>| {
|
||||
if let Err(err) = world
|
||||
.resource_mut::<WorldManager>()
|
||||
.load_world("planet.ron", &mut images)
|
||||
{
|
||||
eprintln!("Failed to save planet.ron: {}", err);
|
||||
} else {
|
||||
update_textures(world);
|
||||
}
|
||||
});
|
||||
},
|
||||
ToolbarButton::Rainfall => {
|
||||
world.resource_mut::<WorldManager>().toggle_rainfall();
|
||||
update_textures(world);
|
||||
},
|
||||
ToolbarButton::Temperature => {
|
||||
world.resource_mut::<WorldManager>().toggle_temperature();
|
||||
update_textures(world);
|
||||
},
|
||||
ToolbarButton::PlanetView => {
|
||||
world.resource_mut::<WorldManager>().cycle_view();
|
||||
update_textures(world);
|
||||
},
|
||||
ToolbarButton::Contours => {
|
||||
world.resource_mut::<WorldManager>().toggle_contours();
|
||||
update_textures(world);
|
||||
},
|
||||
#[cfg(feature = "globe_view")]
|
||||
ToolbarButton::GlobeView => {
|
||||
let mut camera_3d = world
|
||||
.query_filtered::<&mut Camera, (With<Camera3d>, Without<Camera2d>)>()
|
||||
.single_mut(world);
|
||||
camera_3d.is_active = !camera_3d.is_active;
|
||||
let (mut camera_2d, mut pancam) = world
|
||||
.query_filtered::<(&mut Camera, &mut Pan2d), (With<Camera2d>, Without<Camera3d>)>()
|
||||
.single_mut(world);
|
||||
camera_2d.is_active = !camera_2d.is_active;
|
||||
pancam.enabled = camera_2d.is_active;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ToolbarButton> for &'static str {
|
||||
fn from(button: ToolbarButton) -> Self {
|
||||
match button {
|
||||
ToolbarButton::Rainfall => "Toggle rainfall",
|
||||
ToolbarButton::Temperature => "Toggle temperature",
|
||||
ToolbarButton::Contours => "Toggle contours",
|
||||
ToolbarButton::PlanetView => "Cycle view",
|
||||
ToolbarButton::GenerateWorld => "Generate new world",
|
||||
ToolbarButton::SaveWorld => "Save",
|
||||
ToolbarButton::LoadWorld => "Load",
|
||||
#[cfg(feature = "globe_view")]
|
||||
ToolbarButton::GlobeView => "Toggle globe",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ToolbarButton> for &'static str {
|
||||
fn from(button: &ToolbarButton) -> Self {
|
||||
(*button).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ToolbarButton> for String {
|
||||
fn from(button: ToolbarButton) -> Self {
|
||||
<&'static str>::from(button).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ToolbarButton> for String {
|
||||
fn from(button: &ToolbarButton) -> Self {
|
||||
<&'static str>::from(button).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(SystemParam)]
|
||||
pub(crate) struct ToolbarWidget<'w, 's> {
|
||||
#[system_param(ignore)]
|
||||
_phantom: PhantomData<(&'w (), &'s ())>,
|
||||
}
|
||||
impl WidgetSystem for ToolbarWidget<'_, '_> {
|
||||
fn system(world: &mut World, _state: &mut SystemState<Self>, ui: &mut Ui, _id: WidgetId) {
|
||||
ui.with_layout(
|
||||
Layout::left_to_right(bevy_egui::egui::Align::Center),
|
||||
|ui| {
|
||||
for button in ToolbarButton::ITEMS {
|
||||
if ui.button(<&'static str>::from(button)).clicked() {
|
||||
debug!("Pressed button: {:#?}", button);
|
||||
button.clicked(world);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
501
src/main.rs
501
src/main.rs
|
@ -1,64 +1,36 @@
|
|||
#![warn(absolute_paths_not_starting_with_crate)]
|
||||
// #![warn(box_pointers)]
|
||||
#![warn(elided_lifetimes_in_paths)]
|
||||
#![warn(explicit_outlives_requirements)]
|
||||
#![warn(keyword_idents)]
|
||||
#![warn(macro_use_extern_crate)]
|
||||
#![warn(meta_variable_misuse)]
|
||||
#![warn(missing_abi)]
|
||||
// #![warn(missing_copy_implementations)]
|
||||
#![warn(missing_debug_implementations)]
|
||||
// #![warn(missing_docs)]
|
||||
#![warn(non_ascii_idents)]
|
||||
#![warn(noop_method_call)]
|
||||
#![warn(pointer_structural_match)]
|
||||
#![warn(rust_2021_incompatible_closure_captures)]
|
||||
#![warn(rust_2021_incompatible_or_patterns)]
|
||||
#![warn(rust_2021_prefixes_incompatible_syntax)]
|
||||
#![warn(rust_2021_prelude_collisions)]
|
||||
#![warn(single_use_lifetimes)]
|
||||
#![warn(trivial_casts)]
|
||||
#![warn(trivial_numeric_casts)]
|
||||
#![warn(unreachable_pub)]
|
||||
#![warn(unsafe_code)]
|
||||
#![warn(unsafe_op_in_unsafe_fn)]
|
||||
#![warn(unstable_features)]
|
||||
#![warn(unused_crate_dependencies)]
|
||||
#![warn(unused_extern_crates)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![warn(unused_lifetimes)]
|
||||
#![warn(unused_macro_rules)]
|
||||
#![warn(unused_qualifications)]
|
||||
#![warn(unused_results)]
|
||||
#![warn(variant_size_differences)]
|
||||
use gui::widgets::{InfoPanel, ToolbarWidget};
|
||||
|
||||
pub(crate) mod components;
|
||||
#[cfg(feature = "render")]
|
||||
pub(crate) mod gui;
|
||||
pub(crate) mod macros;
|
||||
pub(crate) mod plugins;
|
||||
pub(crate) mod resources;
|
||||
pub(crate) mod ui_helpers;
|
||||
|
||||
#[cfg(all(feature = "logging", feature = "render"))]
|
||||
use bevy::{
|
||||
diagnostic::{Diagnostics, FrameTimeDiagnosticsPlugin},
|
||||
log::debug,
|
||||
use {
|
||||
bevy::{
|
||||
app::App,
|
||||
log::LogSettings,
|
||||
prelude::{IntoExclusiveSystem, World},
|
||||
utils::{default, tracing::Level},
|
||||
},
|
||||
bevy_egui::egui::{FontData, FontDefinitions, FontFamily},
|
||||
planet::WorldManager,
|
||||
plugins::WorldPlugins,
|
||||
};
|
||||
#[cfg(feature = "render")]
|
||||
use {
|
||||
bevy::text::Font,
|
||||
bevy::{
|
||||
asset::Assets,
|
||||
core_pipeline::core_2d::{Camera2d, Camera2dBundle},
|
||||
ecs::{
|
||||
change_detection::ResMut,
|
||||
query::{Changed, With},
|
||||
change_detection::{Mut, ResMut},
|
||||
query::With,
|
||||
system::{Commands, Query, Res},
|
||||
},
|
||||
hierarchy::BuildChildren,
|
||||
prelude::Vec2,
|
||||
render::{
|
||||
camera::{Camera, RenderTarget},
|
||||
color::Color,
|
||||
render_resource::{
|
||||
Extent3d,
|
||||
TextureDescriptor,
|
||||
|
@ -69,47 +41,20 @@ use {
|
|||
texture::{Image, ImageSettings},
|
||||
},
|
||||
sprite::{Sprite, SpriteBundle},
|
||||
text::Text,
|
||||
transform::components::GlobalTransform,
|
||||
ui::{
|
||||
entity::{NodeBundle, TextBundle},
|
||||
AlignSelf,
|
||||
FocusPolicy,
|
||||
Interaction,
|
||||
JustifyContent,
|
||||
PositionType,
|
||||
Size,
|
||||
Style,
|
||||
UiColor,
|
||||
UiRect,
|
||||
Val,
|
||||
},
|
||||
window::{CursorIcon, WindowDescriptor, Windows},
|
||||
window::{WindowDescriptor, Windows},
|
||||
winit::WinitSettings,
|
||||
},
|
||||
components::{
|
||||
markers::{InfoPanel, ToolbarButton},
|
||||
panning::Pan2d,
|
||||
},
|
||||
planet::BiomeStats,
|
||||
bevy_egui::EguiContext,
|
||||
components::panning::Pan2d,
|
||||
gui::widget,
|
||||
resources::CursorMapPosition,
|
||||
ui_helpers::{toolbar_button, toolbar_button_text},
|
||||
};
|
||||
use {
|
||||
bevy::{
|
||||
app::App,
|
||||
log::LogSettings,
|
||||
utils::{default, tracing::Level},
|
||||
},
|
||||
planet::WorldManager,
|
||||
plugins::WorldPlugins,
|
||||
};
|
||||
#[cfg(all(feature = "render", feature = "globe_view"))]
|
||||
use {
|
||||
bevy::{
|
||||
asset::Handle,
|
||||
core_pipeline::core_3d::{Camera3d, Camera3dBundle},
|
||||
ecs::query::Without,
|
||||
core_pipeline::core_3d::Camera3dBundle,
|
||||
pbr::{PbrBundle, PointLight, PointLightBundle, StandardMaterial},
|
||||
prelude::{Quat, Vec3},
|
||||
render::camera::OrthographicProjection,
|
||||
|
@ -119,198 +64,6 @@ use {
|
|||
std::f32::consts::FRAC_PI_2,
|
||||
};
|
||||
|
||||
#[cfg(feature = "render")]
|
||||
fn refresh_map_texture(
|
||||
images: &mut Assets<Image>,
|
||||
#[cfg(feature = "globe_view")] materials: &mut Assets<StandardMaterial>,
|
||||
world_manager: &WorldManager,
|
||||
) {
|
||||
let world = world_manager.world();
|
||||
#[cfg(feature = "logging")]
|
||||
debug!("refreshing world texture");
|
||||
let map_image_handle = images.get_handle(
|
||||
world_manager
|
||||
.map_image_handle_id
|
||||
.expect("No map image handle"),
|
||||
);
|
||||
let map_image = images
|
||||
.get_mut(&map_image_handle)
|
||||
.expect("Map image handle pointing to non-existing image");
|
||||
map_image.resize(Extent3d {
|
||||
width: world.width,
|
||||
height: world.height,
|
||||
depth_or_array_layers: 1,
|
||||
});
|
||||
map_image.data = world_manager.map_color_bytes();
|
||||
|
||||
#[cfg(feature = "globe_view")]
|
||||
{
|
||||
let planet_image_handle = images.get_handle(
|
||||
world_manager
|
||||
.globe_image_handle_id
|
||||
.expect("No planet image handle"),
|
||||
);
|
||||
let planet_image = images
|
||||
.get_mut(&planet_image_handle)
|
||||
.expect("Planet image handle pointing to non-existing image");
|
||||
planet_image.resize(Extent3d {
|
||||
width: world.width,
|
||||
height: world.height,
|
||||
depth_or_array_layers: 1,
|
||||
});
|
||||
planet_image.data = world_manager.globe_color_bytes();
|
||||
|
||||
let planet_material_handle = materials.get_handle(
|
||||
world_manager
|
||||
.globe_material_handle_id
|
||||
.expect("No planet material handle"),
|
||||
);
|
||||
let planet_material = materials
|
||||
.get_mut(&planet_material_handle)
|
||||
.expect("Planet material handle pointing to non-existing material");
|
||||
planet_material.base_color_texture = Some(planet_image_handle);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "render")]
|
||||
const NORMAL_BUTTON: Color = Color::rgb(0.15, 0.15, 0.15);
|
||||
#[cfg(feature = "render")]
|
||||
const HOVERED_BUTTON: Color = Color::rgb(0.25, 0.25, 0.25);
|
||||
#[cfg(feature = "render")]
|
||||
const PRESSED_BUTTON: Color = Color::rgb(0.35, 0.60, 0.35);
|
||||
#[cfg(feature = "render")]
|
||||
fn handle_toolbar_button(
|
||||
mut interaction_query: Query<
|
||||
'_,
|
||||
'_,
|
||||
(&Interaction, &mut UiColor, &ToolbarButton),
|
||||
Changed<Interaction>,
|
||||
>,
|
||||
mut windows: ResMut<'_, Windows>,
|
||||
mut images: ResMut<'_, Assets<Image>>,
|
||||
mut world_manager: ResMut<'_, WorldManager>,
|
||||
#[cfg(feature = "globe_view")] mut camera_3d_query: Query<
|
||||
'_,
|
||||
'_,
|
||||
&mut Camera,
|
||||
(With<Camera3d>, Without<Camera2d>),
|
||||
>,
|
||||
#[cfg(feature = "globe_view")] mut camera_2d_query: Query<
|
||||
'_,
|
||||
'_,
|
||||
(&mut Camera, &mut Pan2d),
|
||||
(With<Camera2d>, Without<Camera3d>),
|
||||
>,
|
||||
#[cfg(feature = "globe_view")] mut materials: ResMut<'_, Assets<StandardMaterial>>,
|
||||
) {
|
||||
for (interaction, mut color, toolbar_button) in &mut interaction_query {
|
||||
match *interaction {
|
||||
Interaction::Clicked => {
|
||||
windows.primary_mut().set_cursor_icon(CursorIcon::Default);
|
||||
*color = PRESSED_BUTTON.into();
|
||||
match toolbar_button {
|
||||
ToolbarButton::Rainfall => {
|
||||
#[cfg(feature = "logging")]
|
||||
debug!("Toggling rainfall");
|
||||
world_manager.toggle_rainfall();
|
||||
refresh_map_texture(
|
||||
&mut images,
|
||||
#[cfg(feature = "globe_view")]
|
||||
&mut materials,
|
||||
&world_manager,
|
||||
);
|
||||
},
|
||||
ToolbarButton::Temperature => {
|
||||
#[cfg(feature = "logging")]
|
||||
debug!("Toggling temperature");
|
||||
world_manager.toggle_temperature();
|
||||
refresh_map_texture(
|
||||
&mut images,
|
||||
#[cfg(feature = "globe_view")]
|
||||
&mut materials,
|
||||
&world_manager,
|
||||
);
|
||||
},
|
||||
ToolbarButton::PlanetView => {
|
||||
#[cfg(feature = "logging")]
|
||||
debug!("Cycling planet view");
|
||||
world_manager.cycle_view();
|
||||
refresh_map_texture(
|
||||
&mut images,
|
||||
#[cfg(feature = "globe_view")]
|
||||
&mut materials,
|
||||
&world_manager,
|
||||
);
|
||||
},
|
||||
ToolbarButton::Contours => {
|
||||
#[cfg(feature = "logging")]
|
||||
debug!("Toggling contours");
|
||||
world_manager.toggle_contours();
|
||||
refresh_map_texture(
|
||||
&mut images,
|
||||
#[cfg(feature = "globe_view")]
|
||||
&mut materials,
|
||||
&world_manager,
|
||||
);
|
||||
},
|
||||
ToolbarButton::GenerateWorld => {
|
||||
#[cfg(feature = "logging")]
|
||||
debug!("Generating new world");
|
||||
_ = world_manager
|
||||
.new_world()
|
||||
.expect("Failed to generate new world");
|
||||
refresh_map_texture(
|
||||
&mut images,
|
||||
#[cfg(feature = "globe_view")]
|
||||
&mut materials,
|
||||
&world_manager,
|
||||
);
|
||||
},
|
||||
ToolbarButton::SaveWorld => {
|
||||
#[cfg(feature = "logging")]
|
||||
debug!("Saving world");
|
||||
if let Err(err) = world_manager.save_world("planet.ron") {
|
||||
eprintln!("Failed to save planet.ron: {}", err);
|
||||
}
|
||||
},
|
||||
ToolbarButton::LoadWorld => {
|
||||
#[cfg(feature = "logging")]
|
||||
debug!("Loading world");
|
||||
if let Err(err) = world_manager.load_world("planet.ron", &mut images) {
|
||||
eprintln!("Failed to load planet.ron: {}", err);
|
||||
} else {
|
||||
refresh_map_texture(
|
||||
&mut images,
|
||||
#[cfg(feature = "globe_view")]
|
||||
&mut materials,
|
||||
&world_manager,
|
||||
);
|
||||
}
|
||||
},
|
||||
#[cfg(feature = "globe_view")]
|
||||
ToolbarButton::GlobeView => {
|
||||
#[cfg(feature = "logging")]
|
||||
debug!("Toggling globe view");
|
||||
let mut camera_3d = camera_3d_query.single_mut();
|
||||
camera_3d.is_active = !camera_3d.is_active;
|
||||
let (mut camera_2d, mut pancam) = camera_2d_query.single_mut();
|
||||
camera_2d.is_active = !camera_2d.is_active;
|
||||
pancam.enabled = camera_2d.is_active;
|
||||
},
|
||||
}
|
||||
},
|
||||
Interaction::Hovered => {
|
||||
windows.primary_mut().set_cursor_icon(CursorIcon::Hand);
|
||||
*color = HOVERED_BUTTON.into();
|
||||
},
|
||||
Interaction::None => {
|
||||
windows.primary_mut().set_cursor_icon(CursorIcon::Default);
|
||||
*color = NORMAL_BUTTON.into();
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "render")]
|
||||
fn update_cursor_map_position(
|
||||
mut cursor_map_position: ResMut<'_, CursorMapPosition>,
|
||||
|
@ -350,107 +103,44 @@ fn rotate_globe(mut globe_transform: Query<'_, '_, &mut Transform, With<Handle<M
|
|||
globe_transform.single_mut().rotate_y(ROTATION_SPEED);
|
||||
}
|
||||
|
||||
#[cfg(feature = "render")]
|
||||
fn update_info_panel(
|
||||
#[cfg(feature = "logging")] diagnostics: Res<'_, Diagnostics>,
|
||||
cursor_position: Res<'_, CursorMapPosition>,
|
||||
world_manager: Res<'_, WorldManager>,
|
||||
mut text: Query<'_, '_, &mut Text, With<InfoPanel>>,
|
||||
) {
|
||||
let world = world_manager.world();
|
||||
text.single_mut().sections[0].value = if cursor_position.x >= 0
|
||||
&& cursor_position.x < world.width as i32
|
||||
&& cursor_position.y >= 0
|
||||
&& cursor_position.y < world.height as i32
|
||||
{
|
||||
let cell = &world.terrain[cursor_position.y as usize][cursor_position.x as usize];
|
||||
|
||||
#[cfg(feature = "logging")]
|
||||
{
|
||||
format!(
|
||||
"FPS: ~{}\nMouse position: {}\nAltitude: {}\nRainfall: {}\nTemperature: {}\n\n{}",
|
||||
match diagnostics.get_measurement(FrameTimeDiagnosticsPlugin::FPS) {
|
||||
None => f64::NAN,
|
||||
Some(fps) => fps.value.round(),
|
||||
},
|
||||
*cursor_position,
|
||||
cell.altitude,
|
||||
cell.rainfall,
|
||||
cell.temperature,
|
||||
cell.biome_presences
|
||||
.iter()
|
||||
.map(|(biome_type, presence)| {
|
||||
format!(
|
||||
"Biome: {} ({:.2}%)",
|
||||
(<BiomeStats>::from(biome_type).name),
|
||||
presence * 100.0
|
||||
)
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "logging"))]
|
||||
{
|
||||
format!(
|
||||
"Mouse position: {}\nAltitude: {}\nRainfall: {}\nTemperature: {}\n{}",
|
||||
*cursor_position,
|
||||
cell.altitude,
|
||||
cell.rainfall,
|
||||
cell.temperature,
|
||||
cell.biome_presences
|
||||
.iter()
|
||||
.map(|(biome_type, presence)| {
|
||||
format!(
|
||||
"Biome: {} ({:.2}%)",
|
||||
(<BiomeStats>::from(biome_type).name),
|
||||
presence * 100.0
|
||||
)
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
)
|
||||
}
|
||||
} else {
|
||||
#[cfg(feature = "logging")]
|
||||
{
|
||||
format!(
|
||||
"FPS: ~{}\nMouse position: {}\nOut of bounds",
|
||||
match diagnostics.get_measurement(FrameTimeDiagnosticsPlugin::FPS) {
|
||||
None => f64::NAN,
|
||||
Some(fps) => fps.value.round(),
|
||||
},
|
||||
*cursor_position
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "logging"))]
|
||||
{
|
||||
format!("Mouse position: {}\nOut of bounds", *cursor_position)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "render")]
|
||||
fn generate_graphics(
|
||||
mut commands: Commands<'_, '_>,
|
||||
mut world_manager: ResMut<'_, WorldManager>,
|
||||
mut images: ResMut<'_, Assets<Image>>,
|
||||
mut fonts: ResMut<'_, Assets<Font>>,
|
||||
mut egui_context: ResMut<'_, EguiContext>,
|
||||
#[cfg(feature = "globe_view")] mut materials: ResMut<'_, Assets<StandardMaterial>>,
|
||||
#[cfg(feature = "globe_view")] mut meshes: ResMut<'_, Assets<Mesh>>,
|
||||
) {
|
||||
let julia_mono_handle = fonts.add(
|
||||
Font::try_from_bytes(include_bytes!("../assets/JuliaMono.ttf").to_vec())
|
||||
.expect("Failed to create JuliaMono font!"),
|
||||
// Add Julia-Mono font to egui
|
||||
{
|
||||
let ctx = egui_context.ctx_mut();
|
||||
let mut fonts = FontDefinitions::default();
|
||||
const FONT_NAME: &str = "Julia-Mono";
|
||||
_ = fonts.font_data.insert(
|
||||
FONT_NAME.to_owned(),
|
||||
FontData::from_static(include_bytes!("../assets/JuliaMono.ttf")),
|
||||
);
|
||||
fonts
|
||||
.families
|
||||
.get_mut(&FontFamily::Monospace)
|
||||
.expect("Failed to get 'Monospace' FontFamily")
|
||||
.insert(0, FONT_NAME.to_owned());
|
||||
fonts
|
||||
.families
|
||||
.get_mut(&FontFamily::Proportional)
|
||||
.expect("Failed to get 'Proportional' FontFamily")
|
||||
.push(FONT_NAME.to_owned());
|
||||
ctx.set_fonts(fonts);
|
||||
}
|
||||
|
||||
let world = world_manager.world();
|
||||
let custom_sprite_size = Vec2 {
|
||||
x: (WORLD_SCALE * world.width as i32) as f32,
|
||||
y: (WORLD_SCALE * world.height as i32) as f32,
|
||||
};
|
||||
|
||||
// Set up 2D map mode
|
||||
{
|
||||
let map_image_handle = images.add(Image {
|
||||
data: world_manager.map_color_bytes(),
|
||||
texture_descriptor: TextureDescriptor {
|
||||
|
@ -469,6 +159,20 @@ fn generate_graphics(
|
|||
..default()
|
||||
});
|
||||
world_manager.map_image_handle_id = Some(map_image_handle.id);
|
||||
_ = commands
|
||||
.spawn_bundle(Camera2dBundle::default())
|
||||
.insert(Pan2d::new());
|
||||
|
||||
// TODO: Switch to egui
|
||||
_ = commands.spawn_bundle(SpriteBundle {
|
||||
texture: images.get_handle(world_manager.map_image_handle_id.unwrap()),
|
||||
sprite: Sprite {
|
||||
custom_size: Some(custom_sprite_size),
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "globe_view")]
|
||||
{
|
||||
|
@ -533,80 +237,22 @@ fn generate_graphics(
|
|||
..default()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_ = commands
|
||||
.spawn_bundle(Camera2dBundle::default())
|
||||
.insert(Pan2d::new());
|
||||
_ = commands.spawn_bundle(SpriteBundle {
|
||||
texture: images.get_handle(world_manager.map_image_handle_id.unwrap()),
|
||||
sprite: Sprite {
|
||||
custom_size: Some(custom_sprite_size),
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
fn update_gui(world: &mut World) {
|
||||
world.resource_scope(|world, mut ctx: Mut<'_, EguiContext>| {
|
||||
let ctx = ctx.ctx_mut();
|
||||
_ = bevy_egui::egui::Window::new("Info panel")
|
||||
.resizable(false)
|
||||
.show(ctx, |ui| {
|
||||
widget::<InfoPanel<'_, '_>>(world, ui, "Map Info Panel".into());
|
||||
});
|
||||
|
||||
_ = commands
|
||||
.spawn_bundle(NodeBundle {
|
||||
style: Style {
|
||||
size: Size::new(Val::Percent(100.0), Val::Percent(100.0)),
|
||||
..default()
|
||||
},
|
||||
color: Color::NONE.into(),
|
||||
..default()
|
||||
})
|
||||
.with_children(|root_node| {
|
||||
_ = root_node
|
||||
.spawn_bundle(NodeBundle {
|
||||
style: Style {
|
||||
align_self: AlignSelf::FlexEnd,
|
||||
padding: UiRect::all(Val::Px(2.0)),
|
||||
..default()
|
||||
},
|
||||
color: Color::rgba(1.0, 1.0, 1.0, 0.05).into(),
|
||||
focus_policy: FocusPolicy::Pass,
|
||||
..default()
|
||||
})
|
||||
.with_children(|info_panel| {
|
||||
_ = info_panel
|
||||
.spawn_bundle(TextBundle {
|
||||
text: Text::from_section(
|
||||
"Info Panel",
|
||||
bevy::text::TextStyle {
|
||||
font: julia_mono_handle.clone(),
|
||||
font_size: 15.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
),
|
||||
..default()
|
||||
})
|
||||
.insert(InfoPanel);
|
||||
});
|
||||
_ = root_node
|
||||
.spawn_bundle(NodeBundle {
|
||||
style: Style {
|
||||
size: Size::new(Val::Percent(100.0), Val::Undefined),
|
||||
padding: UiRect::all(Val::Px(3.0)),
|
||||
justify_content: JustifyContent::SpaceAround,
|
||||
position_type: PositionType::Absolute,
|
||||
..default()
|
||||
},
|
||||
color: Color::NONE.into(),
|
||||
focus_policy: FocusPolicy::Pass,
|
||||
..default()
|
||||
})
|
||||
.with_children(|button_box| {
|
||||
ToolbarButton::iterator().for_each(|&button_type| {
|
||||
_ = button_box
|
||||
.spawn_bundle(toolbar_button())
|
||||
.with_children(|button| {
|
||||
_ = button.spawn_bundle(toolbar_button_text(
|
||||
julia_mono_handle.clone(),
|
||||
button_type,
|
||||
));
|
||||
})
|
||||
.insert(button_type)
|
||||
});
|
||||
_ = bevy_egui::egui::TopBottomPanel::bottom("Toolbar")
|
||||
.resizable(false)
|
||||
.default_height(30.0)
|
||||
.show(ctx, |ui| {
|
||||
widget::<ToolbarWidget<'_, '_>>(world, ui, "Toolbar".into());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -632,9 +278,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
})
|
||||
.insert_resource(CursorMapPosition::default())
|
||||
.add_startup_system(generate_graphics)
|
||||
.add_system(handle_toolbar_button)
|
||||
.add_system(update_cursor_map_position)
|
||||
.add_system(update_info_panel);
|
||||
.add_system(update_gui.exclusive_system())
|
||||
.add_system(update_cursor_map_position);
|
||||
#[cfg(all(feature = "render", feature = "globe_view"))]
|
||||
{
|
||||
_ = app.add_system(rotate_globe);
|
||||
|
|
|
@ -10,10 +10,7 @@ use bevy::{
|
|||
|
||||
impl PluginGroup for WorldPlugins {
|
||||
fn build(&mut self, group: &mut PluginGroupBuilder) {
|
||||
_ = group
|
||||
.add(LogPlugin::default())
|
||||
.add(CorePlugin::default())
|
||||
.add(TimePlugin::default());
|
||||
_ = group.add(LogPlugin).add(CorePlugin).add(TimePlugin);
|
||||
|
||||
#[cfg(feature = "render")]
|
||||
{
|
||||
|
@ -32,39 +29,41 @@ impl PluginGroup for WorldPlugins {
|
|||
window::WindowPlugin,
|
||||
winit::WinitPlugin,
|
||||
},
|
||||
bevy_egui::EguiPlugin,
|
||||
};
|
||||
|
||||
_ = group
|
||||
.add(TransformPlugin::default())
|
||||
.add(TransformPlugin)
|
||||
// hierarchy
|
||||
.add(InputPlugin::default())
|
||||
.add(WindowPlugin::default())
|
||||
.add(AssetPlugin::default())
|
||||
.add(HierarchyPlugin::default())
|
||||
.add(WinitPlugin::default())
|
||||
.add(RenderPlugin::default())
|
||||
.add(CorePipelinePlugin::default())
|
||||
.add(SpritePlugin::default())
|
||||
.add(TextPlugin::default())
|
||||
.add(UiPlugin::default())
|
||||
.add(PanningPlugin::default());
|
||||
.add(InputPlugin)
|
||||
.add(WindowPlugin)
|
||||
.add(AssetPlugin)
|
||||
.add(HierarchyPlugin)
|
||||
.add(WinitPlugin)
|
||||
.add(RenderPlugin)
|
||||
.add(CorePipelinePlugin)
|
||||
.add(SpritePlugin)
|
||||
.add(TextPlugin)
|
||||
.add(UiPlugin)
|
||||
.add(PanningPlugin)
|
||||
.add(EguiPlugin);
|
||||
#[cfg(feature = "globe_view")]
|
||||
{
|
||||
use bevy::pbr::PbrPlugin;
|
||||
_ = group.add(PbrPlugin::default())
|
||||
_ = group.add(PbrPlugin)
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "render"))]
|
||||
{
|
||||
use bevy::app::ScheduleRunnerPlugin;
|
||||
_ = group.add(ScheduleRunnerPlugin::default());
|
||||
_ = group.add(ScheduleRunnerPlugin);
|
||||
}
|
||||
|
||||
_ = group.add(DiagnosticsPlugin::default());
|
||||
_ = group.add(DiagnosticsPlugin);
|
||||
#[cfg(all(feature = "logging"))]
|
||||
{
|
||||
use bevy::diagnostic::FrameTimeDiagnosticsPlugin;
|
||||
_ = group.add(FrameTimeDiagnosticsPlugin::default());
|
||||
_ = group.add(FrameTimeDiagnosticsPlugin);
|
||||
}
|
||||
_ = group.add(LogDiagnosticsPlugin::default());
|
||||
}
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
#[cfg(feature = "render")]
|
||||
use {
|
||||
crate::{components::markers::ToolbarButton, NORMAL_BUTTON},
|
||||
bevy::{
|
||||
asset::Handle,
|
||||
render::color::Color,
|
||||
text::{Font, Text, TextStyle},
|
||||
ui::{
|
||||
entity::{ButtonBundle, TextBundle},
|
||||
widget::Button,
|
||||
AlignItems,
|
||||
JustifyContent,
|
||||
Style,
|
||||
},
|
||||
utils::default,
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(feature = "render")]
|
||||
pub(crate) fn toolbar_button() -> ButtonBundle {
|
||||
ButtonBundle {
|
||||
button: Button,
|
||||
style: Style {
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::Center,
|
||||
..default()
|
||||
},
|
||||
color: NORMAL_BUTTON.into(),
|
||||
..default()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "render")]
|
||||
pub(crate) fn toolbar_button_text(font: Handle<Font>, which: ToolbarButton) -> TextBundle {
|
||||
TextBundle {
|
||||
text: Text::from_section(
|
||||
which,
|
||||
TextStyle {
|
||||
font,
|
||||
font_size: 20.0,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
),
|
||||
..default()
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue