From b499cf2c5864141a73a8929cebd83a30fcb2b0dc Mon Sep 17 00:00:00 2001 From: Ben Wallis Date: Sun, 4 Jul 2021 09:47:18 +0000 Subject: [PATCH] Added egui debug UI - a 100% rust UI framework (similar to imgui) allowing for rapid development of debug interfaces to aid development. This is feature-gated behind the `egui-ui` feature which is enabled by default but removed for airshipper builds. Included in the initial implementation is an entity browser which lists all entities in the client ECS, an entity component viewer which shows select components belonging to the selected entity including character state information, and a simple frame time graph. This MR also includes an extraction of the animation hot reloading code which has been reused for egui to allow for hot-reloading of the egui interface to allow rapid development of the UI with realtime feedback upon save as is the case with aninmations. This is feature-gated behind the `hot-egui` feature which is not enabled by default due to the extra startup time that it adds. --- .cargo/config | 4 +- Cargo.lock | 101 ++- Cargo.toml | 3 + client/src/lib.rs | 2 +- common/frontend/src/lib.rs | 1 + common/src/clock.rs | 2 +- common/src/comp/body.rs | 3 +- common/src/comp/character_state.rs | 3 +- common/src/states/utils.rs | 3 +- voxygen/Cargo.toml | 13 +- voxygen/anim/Cargo.toml | 13 +- voxygen/anim/dyn/src/lib.rs | 2 +- voxygen/anim/src/lib.rs | 34 +- voxygen/dynlib/Cargo.toml | 11 + .../src/dyn_lib.rs => dynlib/src/lib.rs} | 134 ++-- voxygen/egui/Cargo.toml | 20 + voxygen/egui/dyn/Cargo.toml | 14 + voxygen/egui/dyn/src/lib.rs | 14 + voxygen/egui/src/character_states.rs | 87 +++ voxygen/egui/src/lib.rs | 697 ++++++++++++++++++ voxygen/src/lib.rs | 15 +- voxygen/src/main.rs | 20 +- voxygen/src/menu/char_selection/mod.rs | 24 +- voxygen/src/menu/main/mod.rs | 23 +- voxygen/src/render/renderer.rs | 12 + voxygen/src/render/renderer/drawer.rs | 52 ++ voxygen/src/run.rs | 34 +- voxygen/src/scene/debug.rs | 2 +- voxygen/src/session/mod.rs | 31 +- voxygen/src/ui/egui/mod.rs | 68 ++ voxygen/src/ui/mod.rs | 1 + 31 files changed, 1298 insertions(+), 145 deletions(-) create mode 100644 voxygen/dynlib/Cargo.toml rename voxygen/{anim/src/dyn_lib.rs => dynlib/src/lib.rs} (67%) create mode 100644 voxygen/egui/Cargo.toml create mode 100644 voxygen/egui/dyn/Cargo.toml create mode 100644 voxygen/egui/dyn/src/lib.rs create mode 100644 voxygen/egui/src/character_states.rs create mode 100644 voxygen/egui/src/lib.rs create mode 100644 voxygen/src/ui/egui/mod.rs diff --git a/.cargo/config b/.cargo/config index 7ce962b17f..d514696c7e 100644 --- a/.cargo/config +++ b/.cargo/config @@ -9,8 +9,8 @@ csv-import = "run --manifest-path common/Cargo.toml --features=bin_csv --bin csv test-server = "run --bin veloren-server-cli --no-default-features -- -b" tracy-server = "-Zunstable-options run --bin veloren-server-cli --no-default-features --features tracy,simd --profile no_overflow" tracy-world-server = "-Zunstable-options run --bin veloren-server-cli --features tracy,simd --profile no_overflow -- -b" -test-voxygen = "run --bin veloren-voxygen --no-default-features --features simd" -tracy-voxygen = "-Zunstable-options run --bin veloren-voxygen --no-default-features --features tracy,simd --profile no_overflow" +test-voxygen = "run --bin veloren-voxygen --no-default-features --features simd,egui-ui" +tracy-voxygen = "-Zunstable-options run --bin veloren-voxygen --no-default-features --features tracy,simd,egui-ui --profile no_overflow" server = "run --bin veloren-server-cli" dbg-voxygen = "run --bin veloren-voxygen -Zunstable-options --profile debuginfo" diff --git a/Cargo.lock b/Cargo.lock index b496d58d02..f0ffddf320 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -257,6 +257,12 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9ff149ed9780025acfdb36862d35b28856bb693ceb451259a7164442f22fdc3" +[[package]] +name = "atomic_refcell" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "681b971236e0f76b20fcafca0236b8718c9186ee778d67cd78bd5f28fd85427f" + [[package]] name = "atomicwrites" version = "0.3.0" @@ -1437,12 +1443,47 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "egui" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "788148861d80b87d28d64440a3d31cae190e50ccc3ea585597466d38428365d7" +dependencies = [ + "epaint", +] + +[[package]] +name = "egui_wgpu_backend" +version = "0.8.0" +source = "git+https://github.com/hasenbanck/egui_wgpu_backend.git?rev=63a002c6a9b6c016e45806dd065864431caab621#63a002c6a9b6c016e45806dd065864431caab621" +dependencies = [ + "bytemuck", + "epi", + "wgpu", +] + +[[package]] +name = "egui_winit_platform" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd4cf17c0cd4dbcf2f8fef55a3592b9b7cfd970576c7302d8ba5c521b8560371" +dependencies = [ + "egui", + "winit", +] + [[package]] name = "either" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "emath" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e73d6c8c70eadb71756fbbc3c303ab25e163b46b656886dd250de5636efea12" + [[package]] name = "encoding_rs" version = "0.8.28" @@ -1499,6 +1540,28 @@ dependencies = [ "syn 1.0.72", ] +[[package]] +name = "epaint" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e2db640801230bdda80629bc3a063927a462f5eaf38a98da676954e78ccb99" +dependencies = [ + "ahash 0.7.4", + "atomic_refcell", + "emath", + "ordered-float 2.5.1", + "rusttype 0.9.2", +] + +[[package]] +name = "epi" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59c4f6cbede1fc8f836384f85295a59199a4825940abcc3a8a29cfe2e3c37583" +dependencies = [ + "egui", +] + [[package]] name = "error-code" version = "2.3.0" @@ -6095,6 +6158,9 @@ dependencies = [ "directories-next", "dispatch 0.1.4", "dot_vox", + "egui", + "egui_wgpu_backend", + "egui_winit_platform", "enum-iterator", "euc", "futures-executor", @@ -6138,6 +6204,7 @@ dependencies = [ "veloren-i18n", "veloren-server", "veloren-voxygen-anim", + "veloren-voxygen-egui", "veloren-world", "wgpu", "wgpu-profiler", @@ -6151,13 +6218,10 @@ name = "veloren-voxygen-anim" version = "0.10.0" dependencies = [ "bytemuck", - "find_folder", "lazy_static", - "libloading 0.7.0", - "notify 5.0.0-pre.9", - "tracing", "vek", "veloren-common", + "veloren-voxygen-dynlib", ] [[package]] @@ -6167,6 +6231,35 @@ dependencies = [ "veloren-voxygen-anim", ] +[[package]] +name = "veloren-voxygen-dynlib" +version = "0.1.0" +dependencies = [ + "find_folder", + "libloading 0.7.0", + "notify 5.0.0-pre.9", + "tracing", +] + +[[package]] +name = "veloren-voxygen-egui" +version = "0.9.0" +dependencies = [ + "egui", + "egui_winit_platform", + "lazy_static", + "veloren-client", + "veloren-common", + "veloren-voxygen-dynlib", +] + +[[package]] +name = "veloren-voxygen-egui-dyn" +version = "0.9.0" +dependencies = [ + "veloren-voxygen-egui", +] + [[package]] name = "veloren-world" version = "0.10.0" diff --git a/Cargo.toml b/Cargo.toml index 23e860d861..45716c89b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,9 @@ members = [ "voxygen/anim", "voxygen/anim/dyn", "voxygen/i18n", + "voxygen/dynlib", + "voxygen/egui", + "voxygen/egui/dyn", "world", "network", "network/protocol", diff --git a/client/src/lib.rs b/client/src/lib.rs index 28efbf8e1e..675f2cd70c 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -13,7 +13,7 @@ pub use common_net::msg::ServerInfo; pub use specs::{ join::Join, saveload::{Marker, MarkerAllocator}, - Builder, DispatcherBuilder, Entity as EcsEntity, ReadStorage, WorldExt, + Builder, DispatcherBuilder, Entity as EcsEntity, ReadStorage, World, WorldExt, }; use crate::addr::ConnectionArgs; diff --git a/common/frontend/src/lib.rs b/common/frontend/src/lib.rs index 358797b823..cf3a72766b 100644 --- a/common/frontend/src/lib.rs +++ b/common/frontend/src/lib.rs @@ -61,6 +61,7 @@ where .add_directive("gfx_backend_vulkan=info".parse().unwrap()) .add_directive("wgpu_core=info".parse().unwrap()) .add_directive("wgpu_core::device=warn".parse().unwrap()) + .add_directive("wgpu_core::swap_chain=info".parse().unwrap()) .add_directive("veloren_network_protocol=info".parse().unwrap()) .add_directive("quinn_proto::connection=info".parse().unwrap()) .add_directive( diff --git a/common/src/clock.rs b/common/src/clock.rs index 0eed375d95..4654fa6065 100644 --- a/common/src/clock.rs +++ b/common/src/clock.rs @@ -85,7 +85,7 @@ impl Clock { .map_or(self.last_dt.as_secs_f32(), |t| t.into_inner()), ); if self.last_dts.len() >= NUMBER_OF_DELTAS_COMPARED && self.last_dt > 2 * stable_dt { - tracing::debug!(?self.last_dt, ?self.total_tick_time, "lag spike detected, unusually slow tick"); + tracing::trace!(?self.last_dt, ?self.total_tick_time, "lag spike detected, unusually slow tick"); stable_dt } else { self.last_dt diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 9925e692c9..94f08e2041 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -23,13 +23,14 @@ use crate::{ use serde::{Deserialize, Serialize}; use specs::{Component, DerefFlaggedStorage}; use specs_idvs::IdvStorage; +use strum_macros::Display; use vek::*; use super::{BuffKind, Density, Mass}; make_case_elim!( body, - #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] + #[derive(Copy, Clone, Debug, Display, PartialEq, Eq, Hash, Serialize, Deserialize)] #[repr(u32)] pub enum Body { Humanoid(body: humanoid::Body) = 0, diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 1b917e7178..4bd835593f 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -8,6 +8,7 @@ use serde::{Deserialize, Serialize}; use specs::{Component, DerefFlaggedStorage, VecStorage}; use specs_idvs::IdvStorage; use std::collections::{BTreeMap, VecDeque}; +use strum_macros::Display; use vek::*; /// Data returned from character behavior fn's to Character Behavior System. @@ -44,7 +45,7 @@ impl From<&JoinData<'_>> for StateUpdate { } } } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, Display, PartialEq, Serialize, Deserialize)] pub enum CharacterState { Idle, Climb(climb::Data), diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index c9550723a2..095e1e92c0 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -19,6 +19,7 @@ use std::{ ops::{Add, Div}, time::Duration, }; +use strum_macros::Display; use vek::*; pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0; @@ -869,7 +870,7 @@ pub fn tick_attack_or_default( /// Determines what portion a state is in. Used in all attacks (eventually). Is /// used to control aspects of animation code, as well as logic within the /// character states. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, Display, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum StageSection { Buildup, Recover, diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 6de4bde135..bd569f7c4d 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -23,17 +23,16 @@ buildInputs = ["xorg.libxcb"] [features] hot-anim = ["anim/use-dyn-lib"] +hot-egui = ["voxygen-egui/use-dyn-lib", "egui"] singleplayer = ["server"] simd = ["vek/platform_intrinsics"] tracy = ["profiling", "profiling/profile-with-tracy", "common-frontend/tracy", "client/tracy"] plugins = ["client/plugins"] +egui-ui = ["voxygen-egui", "egui", "egui_wgpu_backend", "egui_winit_platform"] # We don't ship egui with published release builds so a separate feature is required that excludes it. -# This feature has been added ahead of the egui merge to allow time for Flatpak, AUR and Nix to be updated -# to ensure there isn't any period of time where we're shipping the egui-ui feature enabled by default. -# This comment will be updated after MR 2253 merges. default-publish = ["singleplayer", "native-dialog", "plugins", "simd"] -default = ["default-publish"] +default = ["default-publish", "egui-ui"] [dependencies] client = {package = "veloren-client", path = "../client"} @@ -47,6 +46,7 @@ common-state = {package = "veloren-common-state", path = "../common/state"} anim = {package = "veloren-voxygen-anim", path = "anim"} i18n = {package = "veloren-i18n", path = "i18n"} +voxygen-egui = {package = "veloren-voxygen-egui", path = "egui", optional = true } # Graphics winit = {version = "0.25.0", features = ["serde"]} @@ -65,6 +65,11 @@ window_clipboard = "0.2" glyph_brush = "0.7.0" keyboard-keynames = { git = "https://gitlab.com/Frinksy/keyboard-keynames.git", rev = "9ae8f89014d0b0c5b61d0e821c5aeb6140c5c0dc" } +# EGUI +egui = {version = "0.12", optional = true } +egui_wgpu_backend = {git = "https://github.com/hasenbanck/egui_wgpu_backend.git", rev = "63a002c6a9b6c016e45806dd065864431caab621", optional = true } +egui_winit_platform = {version = "0.8", optional = true } + # ECS specs = {git = "https://github.com/amethyst/specs.git", rev = "f985bec5d456f7b0dd8aae99848f9473c2cd9d46"} specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", rev = "8be2abcddf8f524cb5876e8dd20a7e47cfaf7573" } diff --git a/voxygen/anim/Cargo.toml b/voxygen/anim/Cargo.toml index 787defdbd6..5081c532b8 100644 --- a/voxygen/anim/Cargo.toml +++ b/voxygen/anim/Cargo.toml @@ -5,7 +5,7 @@ name = "veloren-voxygen-anim" version = "0.10.0" [features] -use-dyn-lib = ["libloading", "notify", "lazy_static", "tracing", "find_folder"] +use-dyn-lib = ["lazy_static", "voxygen-dynlib"] be-dyn-lib = [] simd = ["vek/platform_intrinsics"] @@ -13,11 +13,10 @@ default = ["simd"] [dependencies] common = {package = "veloren-common", path = "../../common"} -find_folder = {version = "0.3.0", optional = true} # inline_tweak = "1.0.2" -lazy_static = {version = "1.4.0", optional = true} -libloading = {version = "0.7", optional = true} -notify = {version = "5.0.0-pre.2", optional = true} -tracing = {version = "0.1", optional = true} +bytemuck = { version = "1.4", features=["derive"] } vek = {version = "=0.14.1", features = ["serde"]} -bytemuck = { version="1.4", features=["derive"] } +voxygen-dynlib = {package = "veloren-voxygen-dynlib", path = "../dynlib", optional = true} + +# Hot Reloading +lazy_static = {version = "1.4.0", optional = true} diff --git a/voxygen/anim/dyn/src/lib.rs b/voxygen/anim/dyn/src/lib.rs index 9887b30eb7..ea74d42e96 100644 --- a/voxygen/anim/dyn/src/lib.rs +++ b/voxygen/anim/dyn/src/lib.rs @@ -8,7 +8,7 @@ //! start earlier since a cdylib doesn't pipeline with it's dependencies. //! //! NOTE: the `be-dyn-lib` feature must be used for this crate to be useful, it -//! is not on by default becaue this causes cargo to switch the feature on in +//! is not on by default because this causes cargo to switch the feature on in //! the anim crate when compiling the static lib into voxygen. #[cfg(feature = "be-dyn-lib")] pub use veloren_voxygen_anim::*; diff --git a/voxygen/anim/src/lib.rs b/voxygen/anim/src/lib.rs index b11e1410ad..ab9d933338 100644 --- a/voxygen/anim/src/lib.rs +++ b/voxygen/anim/src/lib.rs @@ -54,7 +54,6 @@ pub mod bird_large; pub mod bird_medium; pub mod character; pub mod dragon; -#[cfg(feature = "use-dyn-lib")] pub mod dyn_lib; pub mod fish_medium; pub mod fish_small; pub mod fixture; @@ -67,14 +66,13 @@ pub mod ship; pub mod theropod; pub mod vek; -#[cfg(feature = "use-dyn-lib")] -pub use dyn_lib::init; - -#[cfg(feature = "use-dyn-lib")] -use std::ffi::CStr; - use self::vek::*; use bytemuck::{Pod, Zeroable}; +#[cfg(feature = "use-dyn-lib")] +use { + lazy_static::lazy_static, std::ffi::CStr, std::sync::Arc, std::sync::Mutex, + voxygen_dynlib::LoadedLib, +}; type MatRaw = [[f32; 4]; 4]; @@ -91,6 +89,15 @@ fn make_bone(mat: Mat4) -> FigureBoneData { pub type Bone = Transform; +#[cfg(feature = "use-dyn-lib")] +lazy_static! { + static ref LIB: Arc>> = + voxygen_dynlib::init("veloren-voxygen-anim", "veloren-voxygen-anim-dyn", "anim"); +} + +#[cfg(feature = "use-dyn-lib")] +pub fn init() { lazy_static::initialize(&LIB); } + pub trait Skeleton: Send + Sync + 'static { type Attr; type Body; @@ -118,10 +125,11 @@ pub fn compute_matrices( } #[cfg(feature = "use-dyn-lib")] { - let lock = dyn_lib::LIB.lock().unwrap(); + let lock = LIB.lock().unwrap(); let lib = &lock.as_ref().unwrap().lib; - let compute_fn: libloading::Symbol< + #[allow(clippy::type_complexity)] + let compute_fn: voxygen_dynlib::Symbol< fn(&S, Mat4, &mut [FigureBoneData; MAX_BONE_COUNT]) -> Vec3, > = unsafe { lib.get(S::COMPUTE_FN) }.unwrap_or_else(|e| { panic!( @@ -169,10 +177,11 @@ pub trait Animation { } #[cfg(feature = "use-dyn-lib")] { - let lock = dyn_lib::LIB.lock().unwrap(); + let lock = LIB.lock().unwrap(); let lib = &lock.as_ref().unwrap().lib; - let update_fn: libloading::Symbol< + #[allow(clippy::type_complexity)] + let update_fn: voxygen_dynlib::Symbol< fn( &Self::Skeleton, Self::Dependency<'a>, @@ -183,9 +192,8 @@ pub trait Animation { > = unsafe { //let start = std::time::Instant::now(); // Overhead of 0.5-5 us (could use hashmap to mitigate if this is an issue) - let f = lib.get(Self::UPDATE_FN); + lib.get(Self::UPDATE_FN) //println!("{}", start.elapsed().as_nanos()); - f } .unwrap_or_else(|e| { panic!( diff --git a/voxygen/dynlib/Cargo.toml b/voxygen/dynlib/Cargo.toml new file mode 100644 index 0000000000..7208c27386 --- /dev/null +++ b/voxygen/dynlib/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "veloren-voxygen-dynlib" +version = "0.1.0" +authors = ["Ben Wallis "] +edition = "2018" + +[dependencies] +find_folder = {version = "0.3.0"} +libloading = {version = "0.7"} +notify = {version = "5.0.0-pre.2"} +tracing = "0.1" diff --git a/voxygen/anim/src/dyn_lib.rs b/voxygen/dynlib/src/lib.rs similarity index 67% rename from voxygen/anim/src/dyn_lib.rs rename to voxygen/dynlib/src/lib.rs index 3e8366ed32..336908f5eb 100644 --- a/voxygen/anim/src/dyn_lib.rs +++ b/voxygen/dynlib/src/lib.rs @@ -1,4 +1,3 @@ -use lazy_static::lazy_static; use libloading::Library; use notify::{immediate_watcher, EventKind, RecursiveMode, Watcher}; use std::{ @@ -8,23 +7,16 @@ use std::{ }; use find_folder::Search; -use std::{env, path::PathBuf}; +use std::{ + env, + env::consts::{DLL_PREFIX, DLL_SUFFIX}, + path::{Path, PathBuf}, + sync::Arc, +}; use tracing::{debug, error, info}; -#[cfg(target_os = "windows")] -const COMPILED_FILE: &str = "veloren_voxygen_anim_dyn.dll"; -#[cfg(target_os = "windows")] -const ACTIVE_FILE: &str = "veloren_voxygen_anim_dyn_active.dll"; - -#[cfg(not(target_os = "windows"))] -const COMPILED_FILE: &str = "libveloren_voxygen_anim_dyn.so"; -#[cfg(not(target_os = "windows"))] -const ACTIVE_FILE: &str = "libveloren_voxygen_anim_dyn_active.so"; - -// This option is required as `hotreload()` moves the `LoadedLib`. -lazy_static! { - pub static ref LIB: Mutex> = Mutex::new(Some(LoadedLib::compile_load())); -} +// Re-exports +pub use libloading::Symbol; /// LoadedLib holds a loaded dynamic library and the location of library file /// with the appropriate OS specific name and extension i.e. @@ -45,20 +37,20 @@ impl LoadedLib { /// /// This is necessary because the very first time you use hot reloading you /// wont have the library, so you can't load it until you have compiled it! - fn compile_load() -> Self { + fn compile_load(dyn_package: &str) -> Self { #[cfg(target_os = "macos")] error!("The hot reloading feature does not work on macos."); // Compile - if !compile() { - panic!("Animation compile failed."); + if !compile(dyn_package) { + panic!("{} compile failed.", dyn_package); } else { - info!("Animation compile succeeded."); + info!("{} compile succeeded.", dyn_package); } - copy(&LoadedLib::determine_path()); + copy(&LoadedLib::determine_path(dyn_package), dyn_package); - Self::load() + Self::load(dyn_package) } /// Load a library from disk. @@ -66,8 +58,8 @@ impl LoadedLib { /// Currently this is pretty fragile, it gets the path of where it thinks /// the dynamic library should be and tries to load it. It will panic if it /// is missing. - fn load() -> Self { - let lib_path = LoadedLib::determine_path(); + fn load(dyn_package: &str) -> Self { + let lib_path = LoadedLib::determine_path(dyn_package); // Try to load the library. let lib = match unsafe { Library::new(lib_path.clone()) } { @@ -84,7 +76,7 @@ impl LoadedLib { /// Determine the path to the dynamic library based on the path of the /// current executable. - fn determine_path() -> PathBuf { + fn determine_path(dyn_package: &str) -> PathBuf { let current_exe = env::current_exe(); // If we got the current_exe, we need to go up a level and then down @@ -109,7 +101,7 @@ impl LoadedLib { Err(e) => { panic!( "Could not determine the path of the current executable, this is needed to \ - hotreload the dynamic library. {:?}", + hot-reload the dynamic library. {:?}", e ); }, @@ -117,7 +109,7 @@ impl LoadedLib { // Determine the platform specific path and push it onto our already // established target/debug dir. - lib_path.push(ACTIVE_FILE); + lib_path.push(active_file(dyn_package)); lib_path } @@ -125,13 +117,14 @@ impl LoadedLib { /// Initialise a watcher. /// -/// The assumption is that this is run from the voxygen crate's root directory -/// as it will watch the relative path `anim` for any changes to `.rs` -/// files. Upon noticing changes it will wait a moment and then recompile. -pub fn init() { - // Make sure first compile is done by accessing the lazy_static and then - // immediately dropping (because we don't actually need it). - drop(LIB.lock()); +/// This will search for the directory named `package_source_dir` and watch the +/// files within it for any changes. +pub fn init( + package: &'static str, + dyn_package: &'static str, + package_source_dir: &'static str, +) -> Arc>> { + let lib_storage = Arc::new(Mutex::new(Some(LoadedLib::compile_load(dyn_package)))); // TODO: use crossbeam let (reload_send, reload_recv) = mpsc::channel(); @@ -139,18 +132,24 @@ pub fn init() { // Start watcher let mut watcher = immediate_watcher(move |res| event_fn(res, &reload_send)).unwrap(); - // Search for the anim directory. - let anim_dir = Search::Kids(1) - .for_folder("anim") - .expect("Could not find the anim crate directory relative to the current directory"); + // Search for the source directory of the package being hot-reloaded. + let watch_dir = Search::Kids(1) + .for_folder(package_source_dir) + .unwrap_or_else(|_| { + panic!( + "Could not find the {} crate directory relative to the current directory", + package_source_dir + ) + }); - watcher.watch(anim_dir, RecursiveMode::Recursive).unwrap(); + watcher.watch(watch_dir, RecursiveMode::Recursive).unwrap(); // Start reloader that watcher signals // "Debounces" events since I can't find the option to do this in the latest // `notify` + let lib_storage_clone = Arc::clone(&lib_storage); std::thread::Builder::new() - .name("voxygen_anim_watcher".into()) + .name(format!("{}_hotreload_watcher", package)) .spawn(move || { let mut modified_paths = std::collections::HashSet::new(); while let Ok(path) = reload_recv.recv() { @@ -162,16 +161,32 @@ pub fn init() { info!( ?modified_paths, - "Hot reloading animations because files in `anim` modified." + "Hot reloading {} because files in `{}` modified.", package, package_source_dir ); - hotreload(); + hotreload(dyn_package, &lib_storage_clone); } }) .unwrap(); // Let the watcher live forever std::mem::forget(watcher); + + lib_storage +} + +fn compiled_file(dyn_package: &str) -> String { dyn_lib_file(dyn_package, false) } + +fn active_file(dyn_package: &str) -> String { dyn_lib_file(dyn_package, true) } + +fn dyn_lib_file(dyn_package: &str, active: bool) -> String { + format!( + "{}{}{}{}", + DLL_PREFIX, + dyn_package.replace("-", "_"), + if active { "_active" } else { "" }, + DLL_SUFFIX + ) } /// Event function to hotreload the dynamic library @@ -180,8 +195,8 @@ pub fn init() { /// before sending them back. fn event_fn(res: notify::Result, sender: &mpsc::Sender) { match res { - Ok(event) => match event.kind { - EventKind::Modify(_) => { + Ok(event) => { + if let EventKind::Modify(_) = event.kind { event .paths .iter() @@ -189,10 +204,9 @@ fn event_fn(res: notify::Result, sender: &mpsc::Sender) { .map(|p| p.to_string_lossy().into_owned()) // Signal reloader .for_each(|p| { let _ = sender.send(p); }); - }, - _ => {}, + } }, - Err(e) => error!(?e, "Animation hotreload watcher error."), + Err(e) => error!(?e, "hotreload watcher error."), } } @@ -200,35 +214,35 @@ fn event_fn(res: notify::Result, sender: &mpsc::Sender) { /// /// This will reload the dynamic library by first internally calling compile /// and then reloading the library. -fn hotreload() { +fn hotreload(dyn_package: &str, loaded_lib: &Mutex>) { // Do nothing if recompile failed. - if compile() { - let mut lock = LIB.lock().unwrap(); + if compile(dyn_package) { + let mut lock = loaded_lib.lock().unwrap(); // Close lib. let loaded_lib = lock.take().unwrap(); loaded_lib.lib.close().unwrap(); - copy(&loaded_lib.lib_path); + copy(&loaded_lib.lib_path, dyn_package); // Open new lib. - *lock = Some(LoadedLib::load()); + *lock = Some(LoadedLib::load(dyn_package)); - info!("Updated animations."); + info!("Updated {}.", dyn_package); } } -/// Recompile the anim package +/// Recompile the dyn package /// /// Returns `false` if the compile failed. -fn compile() -> bool { +fn compile(dyn_package: &str) -> bool { let output = Command::new("cargo") .stderr(Stdio::inherit()) .stdout(Stdio::inherit()) .arg("build") .arg("--package") - .arg("veloren-voxygen-anim-dyn") + .arg(dyn_package) .arg("--features") - .arg("veloren-voxygen-anim-dyn/be-dyn-lib") + .arg(format!("{}/be-dyn-lib", dyn_package)) .output() .unwrap(); @@ -239,10 +253,10 @@ fn compile() -> bool { /// /// We do this for all OS's although it is only strictly necessary for windows. /// The reason we do this is to make the code easier to understand and debug. -fn copy(lib_path: &PathBuf) { +fn copy(lib_path: &Path, dyn_package: &str) { // Use the platform specific names. - let lib_compiled_path = lib_path.with_file_name(COMPILED_FILE); - let lib_output_path = lib_path.with_file_name(ACTIVE_FILE); + let lib_compiled_path = lib_path.with_file_name(compiled_file(dyn_package)); + let lib_output_path = lib_path.with_file_name(active_file(dyn_package)); // Get the path to where the lib was compiled to. debug!(?lib_compiled_path, ?lib_output_path, "Moving."); diff --git a/voxygen/egui/Cargo.toml b/voxygen/egui/Cargo.toml new file mode 100644 index 0000000000..6c59194d8c --- /dev/null +++ b/voxygen/egui/Cargo.toml @@ -0,0 +1,20 @@ +[package] +authors = ["Ben Wallis "] +name = "veloren-voxygen-egui" +edition = "2018" +version = "0.9.0" + +[features] +use-dyn-lib = ["lazy_static", "voxygen-dynlib"] +be-dyn-lib = [] + +[dependencies] +client = {package = "veloren-client", path = "../../client"} +common = {package = "veloren-common", path = "../../common"} +egui = "0.12" +egui_winit_platform = "0.8" +voxygen-dynlib = {package = "veloren-voxygen-dynlib", path = "../dynlib", optional = true} + +# Hot Reloading +lazy_static = {version = "1.4.0", optional = true} + diff --git a/voxygen/egui/dyn/Cargo.toml b/voxygen/egui/dyn/Cargo.toml new file mode 100644 index 0000000000..d4865fcacb --- /dev/null +++ b/voxygen/egui/dyn/Cargo.toml @@ -0,0 +1,14 @@ +[package] +authors = ["Imbris "] +edition = "2018" +name = "veloren-voxygen-egui-dyn" +version = "0.9.0" + +[lib] +crate-type = ["dylib"] + +[features] +be-dyn-lib = ["veloren-voxygen-egui/be-dyn-lib"] + +[dependencies] +veloren-voxygen-egui = { path = "../" } diff --git a/voxygen/egui/dyn/src/lib.rs b/voxygen/egui/dyn/src/lib.rs new file mode 100644 index 0000000000..68d5e32166 --- /dev/null +++ b/voxygen/egui/dyn/src/lib.rs @@ -0,0 +1,14 @@ +//! This crate hacks around the inability to dynamically specify the +//! `crate-type` for cargo to build. +//! +//! For more details on the issue this is a decent starting point: https://github.com/rust-lang/cargo/pull/8789 +//! +//! This crate avoids use building the dynamic lib when it isn't needed and the +//! same with the non dynamic build. Additionally, this allows compilation to +//! start earlier since a cdylib doesn't pipeline with it's dependencies. +//! +//! NOTE: the `be-dyn-lib` feature must be used for this crate to be useful, it +//! is not on by default because this causes cargo to switch the feature on in +//! the anim crate when compiling the static lib into voxygen. +#[cfg(feature = "be-dyn-lib")] +pub use veloren_voxygen_egui::*; diff --git a/voxygen/egui/src/character_states.rs b/voxygen/egui/src/character_states.rs new file mode 100644 index 0000000000..52b1c9df13 --- /dev/null +++ b/voxygen/egui/src/character_states.rs @@ -0,0 +1,87 @@ +use crate::{two_col_row, SelectedEntityInfo}; +use common::{ + comp::CharacterState, + states::{charged_melee, combo_melee, dash_melee, leap_melee}, +}; +use egui::{Grid, Ui}; + +pub fn draw_char_state_group( + ui: &mut Ui, + _selected_entity_info: &SelectedEntityInfo, + character_state: &CharacterState, +) { + ui.horizontal(|ui| { + ui.label("Current State: "); + ui.label(character_state.to_string()); + }); + match character_state { + CharacterState::ComboMelee(data) => { + combo_melee_grid(ui, data); + }, + CharacterState::DashMelee(data) => dash_melee_grid(ui, data), + CharacterState::ChargedMelee(data) => charged_melee_grid(ui, data), + // Character states with no associated data to display + CharacterState::Dance + | CharacterState::Idle + | CharacterState::Sit + | CharacterState::GlideWield + | CharacterState::Sneak + | CharacterState::Talk + | CharacterState::Wielding => {}, + CharacterState::LeapMelee(data) => leap_melee_grid(ui, data), + _ => { + ui.label(""); + }, + }; +} + +fn charged_melee_grid(ui: &mut Ui, data: &charged_melee::Data) { + Grid::new("selected_entity_charged_melee_grid") + .spacing([40.0, 4.0]) + .max_col_width(100.0) + .striped(true) + .show(ui, |ui| #[rustfmt::skip] { + two_col_row(ui, "Stage Section", data.stage_section.to_string()); + two_col_row(ui, "Timer", format!("{}ms", data.timer.as_millis())); + two_col_row(ui, "Charge Amount", format!("{:.1}", data.charge_amount)); + two_col_row(ui, "Exhausted", if data.exhausted { "True" } else { "False" }); + }); +} + +fn combo_melee_grid(ui: &mut Ui, data: &combo_melee::Data) { + Grid::new("selected_entity_combo_melee_grid") + .spacing([40.0, 4.0]) + .max_col_width(100.0) + .striped(true) + .show(ui, |ui| #[rustfmt::skip] { + two_col_row(ui, "Stage", data.stage.to_string()); + two_col_row(ui, "Timer", format!("{}ms", data.timer.as_millis())); + two_col_row(ui, "num_stages", data.static_data.num_stages.to_string()); + }); +} + +fn dash_melee_grid(ui: &mut Ui, data: &dash_melee::Data) { + Grid::new("selected_entity_dash_melee_grid") + .spacing([40.0, 4.0]) + .max_col_width(100.0) + .striped(true) + .show(ui, |ui| #[rustfmt::skip] { + two_col_row(ui, "Auto Charge", if data.auto_charge { "True" } else { "False " }); + two_col_row(ui, "Timer", format!("{}ms", data.timer.as_millis())); + two_col_row(ui, "Stage Section", data.stage_section.to_string()); + two_col_row(ui, "Exhausted", if data.exhausted { "True" } else { "False " }); + two_col_row(ui, "Charge End Timer", format!("{}ms", data.charge_end_timer.as_millis())); + }); +} + +fn leap_melee_grid(ui: &mut Ui, data: &leap_melee::Data) { + Grid::new("selected_entity_leap_melee_grid") + .spacing([40.0, 4.0]) + .max_col_width(100.0) + .striped(true) + .show(ui, |ui| #[rustfmt::skip] { + two_col_row(ui, "Stage Section", data.stage_section.to_string()); + two_col_row(ui, "Timer", format!("{}ms", data.timer.as_millis())); + two_col_row(ui, "Exhausted", if data.exhausted { "True" } else { "False " }); + }); +} diff --git a/voxygen/egui/src/lib.rs b/voxygen/egui/src/lib.rs new file mode 100644 index 0000000000..03b905695c --- /dev/null +++ b/voxygen/egui/src/lib.rs @@ -0,0 +1,697 @@ +#![feature(stmt_expr_attributes)] + +#[cfg(all(feature = "be-dyn-lib", feature = "use-dyn-lib"))] +compile_error!("Can't use both \"be-dyn-lib\" and \"use-dyn-lib\" features at once"); + +mod character_states; + +use client::{Client, Join, World, WorldExt}; +use common::{ + comp, + comp::{Poise, PoiseState}, +}; +use core::mem; +use egui::{ + plot::{Plot, Value}, + widgets::plot::Curve, + CollapsingHeader, Color32, Grid, Label, ScrollArea, Slider, Ui, Window, +}; + +fn two_col_row(ui: &mut Ui, label: impl Into