mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
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.
This commit is contained in:
parent
ca2dcf7712
commit
b499cf2c58
@ -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"
|
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-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"
|
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"
|
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 --profile no_overflow"
|
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"
|
server = "run --bin veloren-server-cli"
|
||||||
dbg-voxygen = "run --bin veloren-voxygen -Zunstable-options --profile debuginfo"
|
dbg-voxygen = "run --bin veloren-voxygen -Zunstable-options --profile debuginfo"
|
||||||
|
|
||||||
|
101
Cargo.lock
generated
101
Cargo.lock
generated
@ -257,6 +257,12 @@ version = "0.3.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c9ff149ed9780025acfdb36862d35b28856bb693ceb451259a7164442f22fdc3"
|
checksum = "c9ff149ed9780025acfdb36862d35b28856bb693ceb451259a7164442f22fdc3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic_refcell"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "681b971236e0f76b20fcafca0236b8718c9186ee778d67cd78bd5f28fd85427f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atomicwrites"
|
name = "atomicwrites"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
@ -1437,12 +1443,47 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
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]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.6.1"
|
version = "1.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "emath"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3e73d6c8c70eadb71756fbbc3c303ab25e163b46b656886dd250de5636efea12"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.28"
|
version = "0.8.28"
|
||||||
@ -1499,6 +1540,28 @@ dependencies = [
|
|||||||
"syn 1.0.72",
|
"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]]
|
[[package]]
|
||||||
name = "error-code"
|
name = "error-code"
|
||||||
version = "2.3.0"
|
version = "2.3.0"
|
||||||
@ -6095,6 +6158,9 @@ dependencies = [
|
|||||||
"directories-next",
|
"directories-next",
|
||||||
"dispatch 0.1.4",
|
"dispatch 0.1.4",
|
||||||
"dot_vox",
|
"dot_vox",
|
||||||
|
"egui",
|
||||||
|
"egui_wgpu_backend",
|
||||||
|
"egui_winit_platform",
|
||||||
"enum-iterator",
|
"enum-iterator",
|
||||||
"euc",
|
"euc",
|
||||||
"futures-executor",
|
"futures-executor",
|
||||||
@ -6138,6 +6204,7 @@ dependencies = [
|
|||||||
"veloren-i18n",
|
"veloren-i18n",
|
||||||
"veloren-server",
|
"veloren-server",
|
||||||
"veloren-voxygen-anim",
|
"veloren-voxygen-anim",
|
||||||
|
"veloren-voxygen-egui",
|
||||||
"veloren-world",
|
"veloren-world",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
"wgpu-profiler",
|
"wgpu-profiler",
|
||||||
@ -6151,13 +6218,10 @@ name = "veloren-voxygen-anim"
|
|||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"find_folder",
|
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libloading 0.7.0",
|
|
||||||
"notify 5.0.0-pre.9",
|
|
||||||
"tracing",
|
|
||||||
"vek",
|
"vek",
|
||||||
"veloren-common",
|
"veloren-common",
|
||||||
|
"veloren-voxygen-dynlib",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -6167,6 +6231,35 @@ dependencies = [
|
|||||||
"veloren-voxygen-anim",
|
"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]]
|
[[package]]
|
||||||
name = "veloren-world"
|
name = "veloren-world"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
@ -20,6 +20,9 @@ members = [
|
|||||||
"voxygen/anim",
|
"voxygen/anim",
|
||||||
"voxygen/anim/dyn",
|
"voxygen/anim/dyn",
|
||||||
"voxygen/i18n",
|
"voxygen/i18n",
|
||||||
|
"voxygen/dynlib",
|
||||||
|
"voxygen/egui",
|
||||||
|
"voxygen/egui/dyn",
|
||||||
"world",
|
"world",
|
||||||
"network",
|
"network",
|
||||||
"network/protocol",
|
"network/protocol",
|
||||||
|
@ -13,7 +13,7 @@ pub use common_net::msg::ServerInfo;
|
|||||||
pub use specs::{
|
pub use specs::{
|
||||||
join::Join,
|
join::Join,
|
||||||
saveload::{Marker, MarkerAllocator},
|
saveload::{Marker, MarkerAllocator},
|
||||||
Builder, DispatcherBuilder, Entity as EcsEntity, ReadStorage, WorldExt,
|
Builder, DispatcherBuilder, Entity as EcsEntity, ReadStorage, World, WorldExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::addr::ConnectionArgs;
|
use crate::addr::ConnectionArgs;
|
||||||
|
@ -61,6 +61,7 @@ where
|
|||||||
.add_directive("gfx_backend_vulkan=info".parse().unwrap())
|
.add_directive("gfx_backend_vulkan=info".parse().unwrap())
|
||||||
.add_directive("wgpu_core=info".parse().unwrap())
|
.add_directive("wgpu_core=info".parse().unwrap())
|
||||||
.add_directive("wgpu_core::device=warn".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("veloren_network_protocol=info".parse().unwrap())
|
||||||
.add_directive("quinn_proto::connection=info".parse().unwrap())
|
.add_directive("quinn_proto::connection=info".parse().unwrap())
|
||||||
.add_directive(
|
.add_directive(
|
||||||
|
@ -85,7 +85,7 @@ impl Clock {
|
|||||||
.map_or(self.last_dt.as_secs_f32(), |t| t.into_inner()),
|
.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 {
|
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
|
stable_dt
|
||||||
} else {
|
} else {
|
||||||
self.last_dt
|
self.last_dt
|
||||||
|
@ -23,13 +23,14 @@ use crate::{
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specs::{Component, DerefFlaggedStorage};
|
use specs::{Component, DerefFlaggedStorage};
|
||||||
use specs_idvs::IdvStorage;
|
use specs_idvs::IdvStorage;
|
||||||
|
use strum_macros::Display;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
use super::{BuffKind, Density, Mass};
|
use super::{BuffKind, Density, Mass};
|
||||||
|
|
||||||
make_case_elim!(
|
make_case_elim!(
|
||||||
body,
|
body,
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Display, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
pub enum Body {
|
pub enum Body {
|
||||||
Humanoid(body: humanoid::Body) = 0,
|
Humanoid(body: humanoid::Body) = 0,
|
||||||
|
@ -8,6 +8,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use specs::{Component, DerefFlaggedStorage, VecStorage};
|
use specs::{Component, DerefFlaggedStorage, VecStorage};
|
||||||
use specs_idvs::IdvStorage;
|
use specs_idvs::IdvStorage;
|
||||||
use std::collections::{BTreeMap, VecDeque};
|
use std::collections::{BTreeMap, VecDeque};
|
||||||
|
use strum_macros::Display;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
/// Data returned from character behavior fn's to Character Behavior System.
|
/// 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 {
|
pub enum CharacterState {
|
||||||
Idle,
|
Idle,
|
||||||
Climb(climb::Data),
|
Climb(climb::Data),
|
||||||
|
@ -19,6 +19,7 @@ use std::{
|
|||||||
ops::{Add, Div},
|
ops::{Add, Div},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
use strum_macros::Display;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0;
|
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
|
/// 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
|
/// used to control aspects of animation code, as well as logic within the
|
||||||
/// character states.
|
/// character states.
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, Display, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum StageSection {
|
pub enum StageSection {
|
||||||
Buildup,
|
Buildup,
|
||||||
Recover,
|
Recover,
|
||||||
|
@ -23,17 +23,16 @@ buildInputs = ["xorg.libxcb"]
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
hot-anim = ["anim/use-dyn-lib"]
|
hot-anim = ["anim/use-dyn-lib"]
|
||||||
|
hot-egui = ["voxygen-egui/use-dyn-lib", "egui"]
|
||||||
singleplayer = ["server"]
|
singleplayer = ["server"]
|
||||||
simd = ["vek/platform_intrinsics"]
|
simd = ["vek/platform_intrinsics"]
|
||||||
tracy = ["profiling", "profiling/profile-with-tracy", "common-frontend/tracy", "client/tracy"]
|
tracy = ["profiling", "profiling/profile-with-tracy", "common-frontend/tracy", "client/tracy"]
|
||||||
plugins = ["client/plugins"]
|
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.
|
# 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-publish = ["singleplayer", "native-dialog", "plugins", "simd"]
|
||||||
default = ["default-publish"]
|
default = ["default-publish", "egui-ui"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
client = {package = "veloren-client", path = "../client"}
|
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"}
|
anim = {package = "veloren-voxygen-anim", path = "anim"}
|
||||||
i18n = {package = "veloren-i18n", path = "i18n"}
|
i18n = {package = "veloren-i18n", path = "i18n"}
|
||||||
|
voxygen-egui = {package = "veloren-voxygen-egui", path = "egui", optional = true }
|
||||||
|
|
||||||
# Graphics
|
# Graphics
|
||||||
winit = {version = "0.25.0", features = ["serde"]}
|
winit = {version = "0.25.0", features = ["serde"]}
|
||||||
@ -65,6 +65,11 @@ window_clipboard = "0.2"
|
|||||||
glyph_brush = "0.7.0"
|
glyph_brush = "0.7.0"
|
||||||
keyboard-keynames = { git = "https://gitlab.com/Frinksy/keyboard-keynames.git", rev = "9ae8f89014d0b0c5b61d0e821c5aeb6140c5c0dc" }
|
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
|
# ECS
|
||||||
specs = {git = "https://github.com/amethyst/specs.git", rev = "f985bec5d456f7b0dd8aae99848f9473c2cd9d46"}
|
specs = {git = "https://github.com/amethyst/specs.git", rev = "f985bec5d456f7b0dd8aae99848f9473c2cd9d46"}
|
||||||
specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", rev = "8be2abcddf8f524cb5876e8dd20a7e47cfaf7573" }
|
specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", rev = "8be2abcddf8f524cb5876e8dd20a7e47cfaf7573" }
|
||||||
|
@ -5,7 +5,7 @@ name = "veloren-voxygen-anim"
|
|||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
use-dyn-lib = ["libloading", "notify", "lazy_static", "tracing", "find_folder"]
|
use-dyn-lib = ["lazy_static", "voxygen-dynlib"]
|
||||||
be-dyn-lib = []
|
be-dyn-lib = []
|
||||||
simd = ["vek/platform_intrinsics"]
|
simd = ["vek/platform_intrinsics"]
|
||||||
|
|
||||||
@ -13,11 +13,10 @@ default = ["simd"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
common = {package = "veloren-common", path = "../../common"}
|
common = {package = "veloren-common", path = "../../common"}
|
||||||
find_folder = {version = "0.3.0", optional = true}
|
|
||||||
# inline_tweak = "1.0.2"
|
# inline_tweak = "1.0.2"
|
||||||
lazy_static = {version = "1.4.0", optional = true}
|
bytemuck = { version = "1.4", features=["derive"] }
|
||||||
libloading = {version = "0.7", optional = true}
|
|
||||||
notify = {version = "5.0.0-pre.2", optional = true}
|
|
||||||
tracing = {version = "0.1", optional = true}
|
|
||||||
vek = {version = "=0.14.1", features = ["serde"]}
|
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}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
//! start earlier since a cdylib doesn't pipeline with it's dependencies.
|
//! 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
|
//! 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.
|
//! the anim crate when compiling the static lib into voxygen.
|
||||||
#[cfg(feature = "be-dyn-lib")]
|
#[cfg(feature = "be-dyn-lib")]
|
||||||
pub use veloren_voxygen_anim::*;
|
pub use veloren_voxygen_anim::*;
|
||||||
|
@ -54,7 +54,6 @@ pub mod bird_large;
|
|||||||
pub mod bird_medium;
|
pub mod bird_medium;
|
||||||
pub mod character;
|
pub mod character;
|
||||||
pub mod dragon;
|
pub mod dragon;
|
||||||
#[cfg(feature = "use-dyn-lib")] pub mod dyn_lib;
|
|
||||||
pub mod fish_medium;
|
pub mod fish_medium;
|
||||||
pub mod fish_small;
|
pub mod fish_small;
|
||||||
pub mod fixture;
|
pub mod fixture;
|
||||||
@ -67,14 +66,13 @@ pub mod ship;
|
|||||||
pub mod theropod;
|
pub mod theropod;
|
||||||
pub mod vek;
|
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 self::vek::*;
|
||||||
use bytemuck::{Pod, Zeroable};
|
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];
|
type MatRaw = [[f32; 4]; 4];
|
||||||
|
|
||||||
@ -91,6 +89,15 @@ fn make_bone(mat: Mat4<f32>) -> FigureBoneData {
|
|||||||
|
|
||||||
pub type Bone = Transform<f32, f32, f32>;
|
pub type Bone = Transform<f32, f32, f32>;
|
||||||
|
|
||||||
|
#[cfg(feature = "use-dyn-lib")]
|
||||||
|
lazy_static! {
|
||||||
|
static ref LIB: Arc<Mutex<Option<LoadedLib>>> =
|
||||||
|
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 {
|
pub trait Skeleton: Send + Sync + 'static {
|
||||||
type Attr;
|
type Attr;
|
||||||
type Body;
|
type Body;
|
||||||
@ -118,10 +125,11 @@ pub fn compute_matrices<S: Skeleton>(
|
|||||||
}
|
}
|
||||||
#[cfg(feature = "use-dyn-lib")]
|
#[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 lib = &lock.as_ref().unwrap().lib;
|
||||||
|
|
||||||
let compute_fn: libloading::Symbol<
|
#[allow(clippy::type_complexity)]
|
||||||
|
let compute_fn: voxygen_dynlib::Symbol<
|
||||||
fn(&S, Mat4<f32>, &mut [FigureBoneData; MAX_BONE_COUNT]) -> Vec3<f32>,
|
fn(&S, Mat4<f32>, &mut [FigureBoneData; MAX_BONE_COUNT]) -> Vec3<f32>,
|
||||||
> = unsafe { lib.get(S::COMPUTE_FN) }.unwrap_or_else(|e| {
|
> = unsafe { lib.get(S::COMPUTE_FN) }.unwrap_or_else(|e| {
|
||||||
panic!(
|
panic!(
|
||||||
@ -169,10 +177,11 @@ pub trait Animation {
|
|||||||
}
|
}
|
||||||
#[cfg(feature = "use-dyn-lib")]
|
#[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 lib = &lock.as_ref().unwrap().lib;
|
||||||
|
|
||||||
let update_fn: libloading::Symbol<
|
#[allow(clippy::type_complexity)]
|
||||||
|
let update_fn: voxygen_dynlib::Symbol<
|
||||||
fn(
|
fn(
|
||||||
&Self::Skeleton,
|
&Self::Skeleton,
|
||||||
Self::Dependency<'a>,
|
Self::Dependency<'a>,
|
||||||
@ -183,9 +192,8 @@ pub trait Animation {
|
|||||||
> = unsafe {
|
> = unsafe {
|
||||||
//let start = std::time::Instant::now();
|
//let start = std::time::Instant::now();
|
||||||
// Overhead of 0.5-5 us (could use hashmap to mitigate if this is an issue)
|
// 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());
|
//println!("{}", start.elapsed().as_nanos());
|
||||||
f
|
|
||||||
}
|
}
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
panic!(
|
panic!(
|
||||||
|
11
voxygen/dynlib/Cargo.toml
Normal file
11
voxygen/dynlib/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "veloren-voxygen-dynlib"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Ben Wallis <atomyc@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
find_folder = {version = "0.3.0"}
|
||||||
|
libloading = {version = "0.7"}
|
||||||
|
notify = {version = "5.0.0-pre.2"}
|
||||||
|
tracing = "0.1"
|
@ -1,4 +1,3 @@
|
|||||||
use lazy_static::lazy_static;
|
|
||||||
use libloading::Library;
|
use libloading::Library;
|
||||||
use notify::{immediate_watcher, EventKind, RecursiveMode, Watcher};
|
use notify::{immediate_watcher, EventKind, RecursiveMode, Watcher};
|
||||||
use std::{
|
use std::{
|
||||||
@ -8,23 +7,16 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use find_folder::Search;
|
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};
|
use tracing::{debug, error, info};
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
// Re-exports
|
||||||
const COMPILED_FILE: &str = "veloren_voxygen_anim_dyn.dll";
|
pub use libloading::Symbol;
|
||||||
#[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<Option<LoadedLib>> = Mutex::new(Some(LoadedLib::compile_load()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// LoadedLib holds a loaded dynamic library and the location of library file
|
/// LoadedLib holds a loaded dynamic library and the location of library file
|
||||||
/// with the appropriate OS specific name and extension i.e.
|
/// 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
|
/// 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!
|
/// 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")]
|
#[cfg(target_os = "macos")]
|
||||||
error!("The hot reloading feature does not work on macos.");
|
error!("The hot reloading feature does not work on macos.");
|
||||||
|
|
||||||
// Compile
|
// Compile
|
||||||
if !compile() {
|
if !compile(dyn_package) {
|
||||||
panic!("Animation compile failed.");
|
panic!("{} compile failed.", dyn_package);
|
||||||
} else {
|
} 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.
|
/// Load a library from disk.
|
||||||
@ -66,8 +58,8 @@ impl LoadedLib {
|
|||||||
/// Currently this is pretty fragile, it gets the path of where it thinks
|
/// 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
|
/// the dynamic library should be and tries to load it. It will panic if it
|
||||||
/// is missing.
|
/// is missing.
|
||||||
fn load() -> Self {
|
fn load(dyn_package: &str) -> Self {
|
||||||
let lib_path = LoadedLib::determine_path();
|
let lib_path = LoadedLib::determine_path(dyn_package);
|
||||||
|
|
||||||
// Try to load the library.
|
// Try to load the library.
|
||||||
let lib = match unsafe { Library::new(lib_path.clone()) } {
|
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
|
/// Determine the path to the dynamic library based on the path of the
|
||||||
/// current executable.
|
/// current executable.
|
||||||
fn determine_path() -> PathBuf {
|
fn determine_path(dyn_package: &str) -> PathBuf {
|
||||||
let current_exe = env::current_exe();
|
let current_exe = env::current_exe();
|
||||||
|
|
||||||
// If we got the current_exe, we need to go up a level and then down
|
// If we got the current_exe, we need to go up a level and then down
|
||||||
@ -109,7 +101,7 @@ impl LoadedLib {
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
panic!(
|
panic!(
|
||||||
"Could not determine the path of the current executable, this is needed to \
|
"Could not determine the path of the current executable, this is needed to \
|
||||||
hotreload the dynamic library. {:?}",
|
hot-reload the dynamic library. {:?}",
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -117,7 +109,7 @@ impl LoadedLib {
|
|||||||
|
|
||||||
// Determine the platform specific path and push it onto our already
|
// Determine the platform specific path and push it onto our already
|
||||||
// established target/debug dir.
|
// established target/debug dir.
|
||||||
lib_path.push(ACTIVE_FILE);
|
lib_path.push(active_file(dyn_package));
|
||||||
|
|
||||||
lib_path
|
lib_path
|
||||||
}
|
}
|
||||||
@ -125,13 +117,14 @@ impl LoadedLib {
|
|||||||
|
|
||||||
/// Initialise a watcher.
|
/// Initialise a watcher.
|
||||||
///
|
///
|
||||||
/// The assumption is that this is run from the voxygen crate's root directory
|
/// This will search for the directory named `package_source_dir` and watch the
|
||||||
/// as it will watch the relative path `anim` for any changes to `.rs`
|
/// files within it for any changes.
|
||||||
/// files. Upon noticing changes it will wait a moment and then recompile.
|
pub fn init(
|
||||||
pub fn init() {
|
package: &'static str,
|
||||||
// Make sure first compile is done by accessing the lazy_static and then
|
dyn_package: &'static str,
|
||||||
// immediately dropping (because we don't actually need it).
|
package_source_dir: &'static str,
|
||||||
drop(LIB.lock());
|
) -> Arc<Mutex<Option<LoadedLib>>> {
|
||||||
|
let lib_storage = Arc::new(Mutex::new(Some(LoadedLib::compile_load(dyn_package))));
|
||||||
|
|
||||||
// TODO: use crossbeam
|
// TODO: use crossbeam
|
||||||
let (reload_send, reload_recv) = mpsc::channel();
|
let (reload_send, reload_recv) = mpsc::channel();
|
||||||
@ -139,18 +132,24 @@ pub fn init() {
|
|||||||
// Start watcher
|
// Start watcher
|
||||||
let mut watcher = immediate_watcher(move |res| event_fn(res, &reload_send)).unwrap();
|
let mut watcher = immediate_watcher(move |res| event_fn(res, &reload_send)).unwrap();
|
||||||
|
|
||||||
// Search for the anim directory.
|
// Search for the source directory of the package being hot-reloaded.
|
||||||
let anim_dir = Search::Kids(1)
|
let watch_dir = Search::Kids(1)
|
||||||
.for_folder("anim")
|
.for_folder(package_source_dir)
|
||||||
.expect("Could not find the anim crate directory relative to the current directory");
|
.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
|
// Start reloader that watcher signals
|
||||||
// "Debounces" events since I can't find the option to do this in the latest
|
// "Debounces" events since I can't find the option to do this in the latest
|
||||||
// `notify`
|
// `notify`
|
||||||
|
let lib_storage_clone = Arc::clone(&lib_storage);
|
||||||
std::thread::Builder::new()
|
std::thread::Builder::new()
|
||||||
.name("voxygen_anim_watcher".into())
|
.name(format!("{}_hotreload_watcher", package))
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
let mut modified_paths = std::collections::HashSet::new();
|
let mut modified_paths = std::collections::HashSet::new();
|
||||||
while let Ok(path) = reload_recv.recv() {
|
while let Ok(path) = reload_recv.recv() {
|
||||||
@ -162,16 +161,32 @@ pub fn init() {
|
|||||||
|
|
||||||
info!(
|
info!(
|
||||||
?modified_paths,
|
?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();
|
.unwrap();
|
||||||
|
|
||||||
// Let the watcher live forever
|
// Let the watcher live forever
|
||||||
std::mem::forget(watcher);
|
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
|
/// Event function to hotreload the dynamic library
|
||||||
@ -180,8 +195,8 @@ pub fn init() {
|
|||||||
/// before sending them back.
|
/// before sending them back.
|
||||||
fn event_fn(res: notify::Result<notify::Event>, sender: &mpsc::Sender<String>) {
|
fn event_fn(res: notify::Result<notify::Event>, sender: &mpsc::Sender<String>) {
|
||||||
match res {
|
match res {
|
||||||
Ok(event) => match event.kind {
|
Ok(event) => {
|
||||||
EventKind::Modify(_) => {
|
if let EventKind::Modify(_) = event.kind {
|
||||||
event
|
event
|
||||||
.paths
|
.paths
|
||||||
.iter()
|
.iter()
|
||||||
@ -189,10 +204,9 @@ fn event_fn(res: notify::Result<notify::Event>, sender: &mpsc::Sender<String>) {
|
|||||||
.map(|p| p.to_string_lossy().into_owned())
|
.map(|p| p.to_string_lossy().into_owned())
|
||||||
// Signal reloader
|
// Signal reloader
|
||||||
.for_each(|p| { let _ = sender.send(p); });
|
.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<notify::Event>, sender: &mpsc::Sender<String>) {
|
|||||||
///
|
///
|
||||||
/// This will reload the dynamic library by first internally calling compile
|
/// This will reload the dynamic library by first internally calling compile
|
||||||
/// and then reloading the library.
|
/// and then reloading the library.
|
||||||
fn hotreload() {
|
fn hotreload(dyn_package: &str, loaded_lib: &Mutex<Option<LoadedLib>>) {
|
||||||
// Do nothing if recompile failed.
|
// Do nothing if recompile failed.
|
||||||
if compile() {
|
if compile(dyn_package) {
|
||||||
let mut lock = LIB.lock().unwrap();
|
let mut lock = loaded_lib.lock().unwrap();
|
||||||
|
|
||||||
// Close lib.
|
// Close lib.
|
||||||
let loaded_lib = lock.take().unwrap();
|
let loaded_lib = lock.take().unwrap();
|
||||||
loaded_lib.lib.close().unwrap();
|
loaded_lib.lib.close().unwrap();
|
||||||
copy(&loaded_lib.lib_path);
|
copy(&loaded_lib.lib_path, dyn_package);
|
||||||
|
|
||||||
// Open new lib.
|
// 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.
|
/// Returns `false` if the compile failed.
|
||||||
fn compile() -> bool {
|
fn compile(dyn_package: &str) -> bool {
|
||||||
let output = Command::new("cargo")
|
let output = Command::new("cargo")
|
||||||
.stderr(Stdio::inherit())
|
.stderr(Stdio::inherit())
|
||||||
.stdout(Stdio::inherit())
|
.stdout(Stdio::inherit())
|
||||||
.arg("build")
|
.arg("build")
|
||||||
.arg("--package")
|
.arg("--package")
|
||||||
.arg("veloren-voxygen-anim-dyn")
|
.arg(dyn_package)
|
||||||
.arg("--features")
|
.arg("--features")
|
||||||
.arg("veloren-voxygen-anim-dyn/be-dyn-lib")
|
.arg(format!("{}/be-dyn-lib", dyn_package))
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -239,10 +253,10 @@ fn compile() -> bool {
|
|||||||
///
|
///
|
||||||
/// We do this for all OS's although it is only strictly necessary for windows.
|
/// 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.
|
/// 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.
|
// Use the platform specific names.
|
||||||
let lib_compiled_path = lib_path.with_file_name(COMPILED_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);
|
let lib_output_path = lib_path.with_file_name(active_file(dyn_package));
|
||||||
|
|
||||||
// Get the path to where the lib was compiled to.
|
// Get the path to where the lib was compiled to.
|
||||||
debug!(?lib_compiled_path, ?lib_output_path, "Moving.");
|
debug!(?lib_compiled_path, ?lib_output_path, "Moving.");
|
20
voxygen/egui/Cargo.toml
Normal file
20
voxygen/egui/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
authors = ["Ben Wallis <atomyc@gmail.com>"]
|
||||||
|
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}
|
||||||
|
|
14
voxygen/egui/dyn/Cargo.toml
Normal file
14
voxygen/egui/dyn/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
authors = ["Imbris <imbrisf@gmail.com>"]
|
||||||
|
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 = "../" }
|
14
voxygen/egui/dyn/src/lib.rs
Normal file
14
voxygen/egui/dyn/src/lib.rs
Normal file
@ -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::*;
|
87
voxygen/egui/src/character_states.rs
Normal file
87
voxygen/egui/src/character_states.rs
Normal file
@ -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("<Rendering not yet implemented for this state>");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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 " });
|
||||||
|
});
|
||||||
|
}
|
697
voxygen/egui/src/lib.rs
Normal file
697
voxygen/egui/src/lib.rs
Normal file
@ -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<Label>, content: impl Into<Label>) {
|
||||||
|
ui.label(label);
|
||||||
|
ui.label(content);
|
||||||
|
ui.end_row();
|
||||||
|
}
|
||||||
|
|
||||||
|
use crate::character_states::draw_char_state_group;
|
||||||
|
use common::comp::{aura::AuraKind::Buff, Body, Fluid};
|
||||||
|
use egui_winit_platform::Platform;
|
||||||
|
use std::time::Duration;
|
||||||
|
#[cfg(feature = "use-dyn-lib")]
|
||||||
|
use {
|
||||||
|
lazy_static::lazy_static, std::ffi::CStr, std::sync::Arc, std::sync::Mutex,
|
||||||
|
voxygen_dynlib::LoadedLib,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "use-dyn-lib")]
|
||||||
|
lazy_static! {
|
||||||
|
static ref LIB: Arc<Mutex<Option<LoadedLib>>> =
|
||||||
|
voxygen_dynlib::init("veloren-voxygen-egui", "veloren-voxygen-egui-dyn", "egui");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "use-dyn-lib")]
|
||||||
|
const MAINTAIN_EGUI_FN: &[u8] = b"maintain_egui_inner\0";
|
||||||
|
|
||||||
|
pub struct SelectedEntityInfo {
|
||||||
|
entity_id: u32,
|
||||||
|
debug_shape_id: Option<u64>,
|
||||||
|
character_state_history: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SelectedEntityInfo {
|
||||||
|
fn new(entity_id: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
entity_id,
|
||||||
|
debug_shape_id: None,
|
||||||
|
character_state_history: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EguiDebugInfo {
|
||||||
|
pub frame_time: Duration,
|
||||||
|
pub ping_ms: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EguiInnerState {
|
||||||
|
selected_entity_info: Option<SelectedEntityInfo>,
|
||||||
|
max_entity_distance: f32,
|
||||||
|
selected_entity_cylinder_height: f32,
|
||||||
|
frame_times: Vec<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub struct EguiWindows {
|
||||||
|
egui_inspection: bool,
|
||||||
|
egui_settings: bool,
|
||||||
|
egui_memory: bool,
|
||||||
|
frame_time: bool,
|
||||||
|
ecs_entities: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for EguiInnerState {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
selected_entity_info: None,
|
||||||
|
max_entity_distance: 100000.0,
|
||||||
|
selected_entity_cylinder_height: 10.0,
|
||||||
|
frame_times: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum DebugShapeAction {
|
||||||
|
AddCylinder {
|
||||||
|
radius: f32,
|
||||||
|
height: f32,
|
||||||
|
},
|
||||||
|
RemoveShape(u64),
|
||||||
|
SetPosAndColor {
|
||||||
|
id: u64,
|
||||||
|
pos: [f32; 4],
|
||||||
|
color: [f32; 4],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct EguiActions {
|
||||||
|
pub actions: Vec<DebugShapeAction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "use-dyn-lib")]
|
||||||
|
pub fn init() { lazy_static::initialize(&LIB); }
|
||||||
|
|
||||||
|
pub fn maintain(
|
||||||
|
platform: &mut Platform,
|
||||||
|
egui_state: &mut EguiInnerState,
|
||||||
|
egui_windows: &mut EguiWindows,
|
||||||
|
client: &Client,
|
||||||
|
debug_info: Option<EguiDebugInfo>,
|
||||||
|
added_cylinder_shape_id: Option<u64>,
|
||||||
|
) -> EguiActions {
|
||||||
|
#[cfg(not(feature = "use-dyn-lib"))]
|
||||||
|
{
|
||||||
|
maintain_egui_inner(
|
||||||
|
platform,
|
||||||
|
egui_state,
|
||||||
|
egui_windows,
|
||||||
|
client,
|
||||||
|
debug_info,
|
||||||
|
added_cylinder_shape_id,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "use-dyn-lib")]
|
||||||
|
{
|
||||||
|
let lock = LIB.lock().unwrap();
|
||||||
|
let lib = &lock.as_ref().unwrap().lib;
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
let maintain_fn: voxygen_dynlib::Symbol<
|
||||||
|
fn(
|
||||||
|
&mut Platform,
|
||||||
|
&mut EguiInnerState,
|
||||||
|
&mut EguiWindows,
|
||||||
|
&Client,
|
||||||
|
Option<EguiDebugInfo>,
|
||||||
|
Option<u64>,
|
||||||
|
) -> EguiActions,
|
||||||
|
> = unsafe { lib.get(MAINTAIN_EGUI_FN) }.unwrap_or_else(|e| {
|
||||||
|
panic!(
|
||||||
|
"Trying to use: {} but had error: {:?}",
|
||||||
|
CStr::from_bytes_with_nul(MAINTAIN_EGUI_FN)
|
||||||
|
.map(CStr::to_str)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap(),
|
||||||
|
e
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
maintain_fn(
|
||||||
|
platform,
|
||||||
|
egui_state,
|
||||||
|
egui_windows,
|
||||||
|
client,
|
||||||
|
debug_info,
|
||||||
|
added_cylinder_shape_id,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "be-dyn-lib", export_name = "maintain_egui_inner")]
|
||||||
|
pub fn maintain_egui_inner(
|
||||||
|
platform: &mut Platform,
|
||||||
|
egui_state: &mut EguiInnerState,
|
||||||
|
egui_windows: &mut EguiWindows,
|
||||||
|
client: &Client,
|
||||||
|
debug_info: Option<EguiDebugInfo>,
|
||||||
|
added_cylinder_shape_id: Option<u64>,
|
||||||
|
) -> EguiActions {
|
||||||
|
platform.begin_frame();
|
||||||
|
let ctx = &platform.context();
|
||||||
|
|
||||||
|
let mut egui_actions = EguiActions::default();
|
||||||
|
let mut previous_selected_entity: Option<SelectedEntityInfo> = None;
|
||||||
|
let mut max_entity_distance = egui_state.max_entity_distance;
|
||||||
|
let mut selected_entity_cylinder_height = egui_state.selected_entity_cylinder_height;
|
||||||
|
|
||||||
|
// If a debug cylinder was added in the last frame, store it against the
|
||||||
|
// selected entity
|
||||||
|
if let Some(shape_id) = added_cylinder_shape_id {
|
||||||
|
if let Some(selected_entity) = &mut egui_state.selected_entity_info {
|
||||||
|
selected_entity.debug_shape_id = Some(shape_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(debug_info) = debug_info.as_ref() {
|
||||||
|
egui_state
|
||||||
|
.frame_times
|
||||||
|
.push(debug_info.frame_time.as_nanos() as f32);
|
||||||
|
if egui_state.frame_times.len() > 250 {
|
||||||
|
egui_state.frame_times.remove(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
egui::Window::new("Debug Control")
|
||||||
|
.default_width(200.0)
|
||||||
|
.default_height(200.0)
|
||||||
|
.show(&platform.context(), |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label(format!(
|
||||||
|
"Ping: {:.1}ms",
|
||||||
|
debug_info.as_ref().map_or(0.0, |x| x.ping_ms)
|
||||||
|
));
|
||||||
|
});
|
||||||
|
ui.group(|ui| {
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
ui.checkbox(&mut egui_windows.ecs_entities, "ECS Entities");
|
||||||
|
ui.checkbox(&mut egui_windows.frame_time, "Frame Time");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.group(|ui| {
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
ui.label("Show EGUI Windows");
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.checkbox(&mut egui_windows.egui_inspection, "🔍 Inspection");
|
||||||
|
ui.checkbox(&mut egui_windows.egui_settings, "🔧 Settings");
|
||||||
|
ui.checkbox(&mut egui_windows.egui_memory, "📝 Memory");
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Window::new("🔧 Settings")
|
||||||
|
.open(&mut egui_windows.egui_settings)
|
||||||
|
.scroll(true)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ctx.settings_ui(ui);
|
||||||
|
});
|
||||||
|
Window::new("🔍 Inspection")
|
||||||
|
.open(&mut egui_windows.egui_inspection)
|
||||||
|
.scroll(true)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ctx.inspection_ui(ui);
|
||||||
|
});
|
||||||
|
|
||||||
|
Window::new("📝 Memory")
|
||||||
|
.open(&mut egui_windows.egui_memory)
|
||||||
|
.resizable(false)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ctx.memory_ui(ui);
|
||||||
|
});
|
||||||
|
|
||||||
|
Window::new("Frame Time")
|
||||||
|
.open(&mut egui_windows.frame_time)
|
||||||
|
.default_width(200.0)
|
||||||
|
.default_height(200.0)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
let plot = Plot::new("Frame Time").curve(Curve::from_values_iter(
|
||||||
|
egui_state
|
||||||
|
.frame_times
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, x)| Value::new(i as f64, *x)),
|
||||||
|
));
|
||||||
|
ui.add(plot);
|
||||||
|
});
|
||||||
|
|
||||||
|
if egui_windows.ecs_entities {
|
||||||
|
let ecs = client.state().ecs();
|
||||||
|
|
||||||
|
let positions = client.state().ecs().read_storage::<comp::Pos>();
|
||||||
|
let client_pos = positions.get(client.entity());
|
||||||
|
|
||||||
|
egui::Window::new("ECS Entities")
|
||||||
|
.open(&mut egui_windows.ecs_entities)
|
||||||
|
.default_width(500.0)
|
||||||
|
.default_height(500.0)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ui.label(format!("Entity count: {}", &ecs.entities().join().count()));
|
||||||
|
ui.add(
|
||||||
|
Slider::new(&mut max_entity_distance, 1.0..=100000.0)
|
||||||
|
.logarithmic(true)
|
||||||
|
.clamp_to_range(true)
|
||||||
|
.text("Max entity distance"),
|
||||||
|
);
|
||||||
|
|
||||||
|
ui.add(
|
||||||
|
Slider::new(&mut selected_entity_cylinder_height, 0.1..=100.0)
|
||||||
|
.logarithmic(true)
|
||||||
|
.clamp_to_range(true)
|
||||||
|
.text("Cylinder height"),
|
||||||
|
);
|
||||||
|
|
||||||
|
let scroll_area = ScrollArea::from_max_height(800.0);
|
||||||
|
let (_current_scroll, _max_scroll) = scroll_area.show(ui, |ui| {
|
||||||
|
Grid::new("entities_grid")
|
||||||
|
.spacing([40.0, 4.0])
|
||||||
|
.max_col_width(300.0)
|
||||||
|
.striped(true)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.label("-");
|
||||||
|
ui.label("ID");
|
||||||
|
ui.label("Pos");
|
||||||
|
ui.label("Vel");
|
||||||
|
ui.label("Name");
|
||||||
|
ui.label("Body");
|
||||||
|
ui.label("Poise");
|
||||||
|
ui.label("Character State");
|
||||||
|
ui.end_row();
|
||||||
|
for (entity, body, stats, pos, _ori, vel, poise, character_state) in (
|
||||||
|
&ecs.entities(),
|
||||||
|
ecs.read_storage::<comp::Body>().maybe(),
|
||||||
|
ecs.read_storage::<comp::Stats>().maybe(),
|
||||||
|
ecs.read_storage::<comp::Pos>().maybe(),
|
||||||
|
ecs.read_storage::<comp::Ori>().maybe(),
|
||||||
|
ecs.read_storage::<comp::Vel>().maybe(),
|
||||||
|
ecs.read_storage::<comp::Poise>().maybe(),
|
||||||
|
ecs.read_storage::<comp::CharacterState>().maybe(),
|
||||||
|
)
|
||||||
|
.join()
|
||||||
|
.filter(|(_, _, _, pos, _, _, _, _)| {
|
||||||
|
client_pos.map_or(true, |client_pos| {
|
||||||
|
pos.map_or(0.0, |pos| pos.0.distance_squared(client_pos.0))
|
||||||
|
< max_entity_distance
|
||||||
|
})
|
||||||
|
})
|
||||||
|
{
|
||||||
|
if ui.button("View").clicked() {
|
||||||
|
previous_selected_entity =
|
||||||
|
mem::take(&mut egui_state.selected_entity_info);
|
||||||
|
|
||||||
|
if pos.is_some() {
|
||||||
|
egui_actions.actions.push(DebugShapeAction::AddCylinder {
|
||||||
|
radius: 1.0,
|
||||||
|
height: egui_state.selected_entity_cylinder_height,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
egui_state.selected_entity_info =
|
||||||
|
Some(SelectedEntityInfo::new(entity.id()));
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.label(format!("{}", entity.id()));
|
||||||
|
|
||||||
|
if let Some(pos) = pos {
|
||||||
|
ui.label(format!(
|
||||||
|
"{:.0},{:.0},{:.0}",
|
||||||
|
pos.0.x, pos.0.y, pos.0.z
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
ui.label("-");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(vel) = vel {
|
||||||
|
ui.label(format!("{:.1}u/s", vel.0.magnitude()));
|
||||||
|
} else {
|
||||||
|
ui.label("-");
|
||||||
|
}
|
||||||
|
if let Some(stats) = stats {
|
||||||
|
ui.label(&stats.name);
|
||||||
|
} else {
|
||||||
|
ui.label("-");
|
||||||
|
}
|
||||||
|
if let Some(body) = body {
|
||||||
|
ui.label(body_species(&body));
|
||||||
|
} else {
|
||||||
|
ui.label("-");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(poise) = poise {
|
||||||
|
poise_state_label(ui, poise);
|
||||||
|
} else {
|
||||||
|
ui.label("-");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(character_state) = character_state {
|
||||||
|
ui.label(character_state.to_string());
|
||||||
|
} else {
|
||||||
|
ui.label("-");
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.end_row();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let margin = ui.visuals().clip_rect_margin;
|
||||||
|
|
||||||
|
let current_scroll = ui.clip_rect().top() - ui.min_rect().top() + margin;
|
||||||
|
let max_scroll =
|
||||||
|
ui.min_rect().height() - ui.clip_rect().height() + 2.0 * margin;
|
||||||
|
(current_scroll, max_scroll)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if let Some(selected_entity_info) = &mut egui_state.selected_entity_info {
|
||||||
|
let selected_entity = ecs.entities().entity(selected_entity_info.entity_id);
|
||||||
|
if !selected_entity.gen().is_alive() {
|
||||||
|
previous_selected_entity = mem::take(&mut egui_state.selected_entity_info);
|
||||||
|
} else {
|
||||||
|
selected_entity_window(platform, ecs, selected_entity_info, &mut egui_actions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(previous) = previous_selected_entity {
|
||||||
|
if let Some(debug_shape_id) = previous.debug_shape_id {
|
||||||
|
egui_actions
|
||||||
|
.actions
|
||||||
|
.push(DebugShapeAction::RemoveShape(debug_shape_id));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(selected_entity) = &egui_state.selected_entity_info {
|
||||||
|
if let Some(debug_shape_id) = selected_entity.debug_shape_id {
|
||||||
|
if (egui_state.selected_entity_cylinder_height - selected_entity_cylinder_height).abs()
|
||||||
|
> f32::EPSILON
|
||||||
|
{
|
||||||
|
egui_actions
|
||||||
|
.actions
|
||||||
|
.push(DebugShapeAction::RemoveShape(debug_shape_id));
|
||||||
|
egui_actions.actions.push(DebugShapeAction::AddCylinder {
|
||||||
|
radius: 1.0,
|
||||||
|
height: selected_entity_cylinder_height,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
egui_state.max_entity_distance = max_entity_distance;
|
||||||
|
egui_state.selected_entity_cylinder_height = selected_entity_cylinder_height;
|
||||||
|
egui_actions
|
||||||
|
}
|
||||||
|
|
||||||
|
fn selected_entity_window(
|
||||||
|
platform: &mut Platform,
|
||||||
|
ecs: &World,
|
||||||
|
selected_entity_info: &mut SelectedEntityInfo,
|
||||||
|
egui_actions: &mut EguiActions,
|
||||||
|
) {
|
||||||
|
let entity_id = selected_entity_info.entity_id;
|
||||||
|
for (
|
||||||
|
_entity,
|
||||||
|
body,
|
||||||
|
stats,
|
||||||
|
pos,
|
||||||
|
_ori,
|
||||||
|
vel,
|
||||||
|
poise,
|
||||||
|
buffs,
|
||||||
|
auras,
|
||||||
|
character_state,
|
||||||
|
physics_state,
|
||||||
|
alignment,
|
||||||
|
scale,
|
||||||
|
mass,
|
||||||
|
(density, health, energy),
|
||||||
|
) in (
|
||||||
|
&ecs.entities(),
|
||||||
|
ecs.read_storage::<comp::Body>().maybe(),
|
||||||
|
ecs.read_storage::<comp::Stats>().maybe(),
|
||||||
|
ecs.read_storage::<comp::Pos>().maybe(),
|
||||||
|
ecs.read_storage::<comp::Ori>().maybe(),
|
||||||
|
ecs.read_storage::<comp::Vel>().maybe(),
|
||||||
|
ecs.read_storage::<comp::Poise>().maybe(),
|
||||||
|
ecs.read_storage::<comp::Buffs>().maybe(),
|
||||||
|
ecs.read_storage::<comp::Auras>().maybe(),
|
||||||
|
ecs.read_storage::<comp::CharacterState>().maybe(),
|
||||||
|
ecs.read_storage::<comp::PhysicsState>().maybe(),
|
||||||
|
ecs.read_storage::<comp::Alignment>().maybe(),
|
||||||
|
ecs.read_storage::<comp::Scale>().maybe(),
|
||||||
|
ecs.read_storage::<comp::Mass>().maybe(),
|
||||||
|
(
|
||||||
|
ecs.read_storage::<comp::Density>().maybe(),
|
||||||
|
ecs.read_storage::<comp::Health>().maybe(),
|
||||||
|
ecs.read_storage::<comp::Energy>().maybe(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.join()
|
||||||
|
.filter(|(e, _, _, _, _, _, _, _, _, _, _, _, _, _, (_, _, _))| e.id() == entity_id)
|
||||||
|
{
|
||||||
|
if let Some(pos) = pos {
|
||||||
|
if let Some(shape_id) = selected_entity_info.debug_shape_id {
|
||||||
|
egui_actions.actions.push(DebugShapeAction::SetPosAndColor {
|
||||||
|
id: shape_id,
|
||||||
|
color: [1.0, 1.0, 0.0, 0.5],
|
||||||
|
pos: [pos.0.x, pos.0.y, pos.0.z + 2.0, 0.0],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
egui::Window::new("Selected Entity")
|
||||||
|
.default_width(300.0)
|
||||||
|
.default_height(200.0)
|
||||||
|
.show(&platform.context(), |ui| {
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
CollapsingHeader::new("General").default_open(true).show(ui, |ui| {
|
||||||
|
Grid::new("selected_entity_general_grid")
|
||||||
|
.spacing([40.0, 4.0])
|
||||||
|
.striped(true)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
two_col_row(ui, "Health", health.map_or("-".to_owned(), |x| format!("{:.1}/{:.1}", x.current(), x.maximum())));
|
||||||
|
two_col_row(ui, "Energy", energy.map_or("-".to_owned(), |x| format!("{:.1}/{:.1}", x.current(), x.maximum())));
|
||||||
|
two_col_row(ui, "Position", pos.map_or("-".to_owned(), |x| format!("({:.1},{:.1},{:.1})", x.0.x, x.0.y, x.0.z)));
|
||||||
|
two_col_row(ui, "Velocity", vel.map_or("-".to_owned(), |x| format!("({:.1},{:.1},{:.1}) ({:.1} u/s)", x.0.x, x.0.y, x.0.z, x.0.magnitude())));
|
||||||
|
two_col_row(ui, "Alignment", alignment.map_or("-".to_owned(), |x| format!("{:?}", x)));
|
||||||
|
two_col_row(ui, "Scale", scale.map_or("-".to_owned(), |x| format!("{:?}", x)));
|
||||||
|
two_col_row(ui, "Mass", mass.map_or("-".to_owned(), |x| format!("{:.1}", x.0)));
|
||||||
|
two_col_row(ui, "Density", density.map_or("-".to_owned(), |x| format!("{:.1}", x.0)));
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
if let Some(stats) = stats {
|
||||||
|
CollapsingHeader::new("Stats").default_open(true).show(ui, |ui| {
|
||||||
|
Grid::new("selected_entity_stats_grid")
|
||||||
|
.spacing([40.0, 4.0])
|
||||||
|
.striped(true)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
two_col_row(ui, "Name", stats.name.to_string());
|
||||||
|
two_col_row(ui, "Damage Reduction", format!("{:.1}", stats.damage_reduction));
|
||||||
|
two_col_row(ui, "Max Health Modifier", format!("{:.1}", stats.max_health_modifier));
|
||||||
|
two_col_row(ui, "Move Speed Modifier", format!("{:.1}", stats.move_speed_modifier));
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(body) = body {
|
||||||
|
CollapsingHeader::new("Body").default_open(false).show(ui, |ui| {
|
||||||
|
Grid::new("selected_entity_body_grid")
|
||||||
|
.spacing([40.0, 4.0])
|
||||||
|
.striped(true)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
two_col_row(ui, "Type", body.to_string());
|
||||||
|
two_col_row(ui, "Species", body_species(body));
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(pos) = pos {
|
||||||
|
CollapsingHeader::new("Pos").default_open(false).show(ui, |ui| {
|
||||||
|
Grid::new("selected_entity_pos_grid")
|
||||||
|
.spacing([40.0, 4.0])
|
||||||
|
.max_col_width(100.0)
|
||||||
|
.striped(true)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
two_col_row(ui, "x", format!("{:.1}", pos.0.x));
|
||||||
|
two_col_row(ui, "y", format!("{:.1}", pos.0.y));
|
||||||
|
two_col_row(ui, "z", format!("{:.1}", pos.0.z));
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(poise) = poise {
|
||||||
|
CollapsingHeader::new("Poise").default_open(false).show(ui, |ui| {
|
||||||
|
Grid::new("selected_entity_poise_grid")
|
||||||
|
.spacing([40.0, 4.0])
|
||||||
|
.max_col_width(100.0)
|
||||||
|
.striped(true)
|
||||||
|
.show(ui, |ui| #[rustfmt::skip] {
|
||||||
|
ui.label("State");
|
||||||
|
poise_state_label(ui, poise);
|
||||||
|
ui.end_row();
|
||||||
|
two_col_row(ui, "Current", format!("{}/{}", poise.current(), poise.maximum()));
|
||||||
|
two_col_row(ui, "Base Max", poise.base_max().to_string());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(buffs) = buffs {
|
||||||
|
CollapsingHeader::new("Buffs").default_open(false).show(ui, |ui| {
|
||||||
|
Grid::new("selected_entity_buffs_grid")
|
||||||
|
.spacing([40.0, 4.0])
|
||||||
|
.max_col_width(100.0)
|
||||||
|
.striped(true)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.label("Kind");
|
||||||
|
ui.label("Time");
|
||||||
|
ui.label("Source");
|
||||||
|
ui.end_row();
|
||||||
|
buffs.buffs.iter().for_each(|(_, v)| {
|
||||||
|
ui.label(format!("{:?}", v.kind));
|
||||||
|
ui.label(
|
||||||
|
v.time.map_or("-".to_string(), |time| {
|
||||||
|
format!("{:?}", time)
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
ui.label(format!("{:?}", v.source));
|
||||||
|
ui.end_row();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(auras) = auras {
|
||||||
|
CollapsingHeader::new("Auras").default_open(false).show(ui, |ui| {
|
||||||
|
Grid::new("selected_entity_auras_grid")
|
||||||
|
.spacing([40.0, 4.0])
|
||||||
|
.striped(true)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.label("Kind");
|
||||||
|
ui.label("Radius");
|
||||||
|
ui.label("Duration");
|
||||||
|
ui.label("Target");
|
||||||
|
ui.end_row();
|
||||||
|
auras.auras.iter().for_each(|(_, v)| {
|
||||||
|
ui.label(match v.aura_kind {
|
||||||
|
Buff { kind, .. } => format!("Buff - {:?}", kind)
|
||||||
|
});
|
||||||
|
ui.label(format!("{:1}", v.radius));
|
||||||
|
ui.label(v.duration.map_or("-".to_owned(), |x| format!("{:1}s", x.as_secs())));
|
||||||
|
ui.label(format!("{:?}", v.target));
|
||||||
|
ui.end_row();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(character_state) = character_state {
|
||||||
|
if selected_entity_info
|
||||||
|
.character_state_history
|
||||||
|
.first()
|
||||||
|
.unwrap_or(&"-".to_owned())
|
||||||
|
!= &character_state.to_string()
|
||||||
|
{
|
||||||
|
selected_entity_info
|
||||||
|
.character_state_history
|
||||||
|
.insert(0, character_state.to_string());
|
||||||
|
if selected_entity_info.character_state_history.len() > 50 {
|
||||||
|
selected_entity_info.character_state_history.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CollapsingHeader::new("Character State").default_open(false).show(ui, |ui| {
|
||||||
|
draw_char_state_group(ui, selected_entity_info, character_state);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(physics_state) = physics_state {
|
||||||
|
CollapsingHeader::new("Physics State").default_open(false).show(ui, |ui| {
|
||||||
|
Grid::new("selected_entity_physics_state_grid")
|
||||||
|
.spacing([40.0, 4.0])
|
||||||
|
.striped(true)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
two_col_row(ui, "On Ground", physics_state.on_ground.map_or("None".to_owned(), |x| format!("{:?}", x)));
|
||||||
|
two_col_row(ui, "On Ceiling", (if physics_state.on_ceiling { "True" } else { "False " }).to_string());
|
||||||
|
two_col_row(ui, "On Wall", physics_state.on_wall.map_or("-".to_owned(), |x| format!("{:.1},{:.1},{:.1}", x.x, x.y, x.z )));
|
||||||
|
two_col_row(ui, "Touching Entities", physics_state.touch_entities.len().to_string());
|
||||||
|
two_col_row(ui, "In Fluid", match physics_state.in_fluid {
|
||||||
|
|
||||||
|
Some(Fluid::Air { elevation, .. }) => format!("Air (Elevation: {:.1})", elevation),
|
||||||
|
Some(Fluid::Liquid { depth, kind, .. }) => format!("{:?} (Depth: {:.1})", kind, depth),
|
||||||
|
_ => "None".to_owned() });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn body_species(body: &Body) -> String {
|
||||||
|
match body {
|
||||||
|
Body::Humanoid(body) => format!("{:?}", body.species),
|
||||||
|
Body::QuadrupedSmall(body) => format!("{:?}", body.species),
|
||||||
|
Body::QuadrupedMedium(body) => format!("{:?}", body.species),
|
||||||
|
Body::BirdMedium(body) => format!("{:?}", body.species),
|
||||||
|
Body::FishMedium(body) => format!("{:?}", body.species),
|
||||||
|
Body::Dragon(body) => format!("{:?}", body.species),
|
||||||
|
Body::BirdLarge(body) => format!("{:?}", body.species),
|
||||||
|
Body::FishSmall(body) => format!("{:?}", body.species),
|
||||||
|
Body::BipedLarge(body) => format!("{:?}", body.species),
|
||||||
|
Body::BipedSmall(body) => format!("{:?}", body.species),
|
||||||
|
Body::Object(body) => format!("{:?}", body),
|
||||||
|
Body::Golem(body) => format!("{:?}", body.species),
|
||||||
|
Body::Theropod(body) => format!("{:?}", body.species),
|
||||||
|
Body::QuadrupedLow(body) => format!("{:?}", body.species),
|
||||||
|
Body::Ship(body) => format!("{:?}", body),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poise_state_label(ui: &mut Ui, poise: &Poise) {
|
||||||
|
match poise.poise_state() {
|
||||||
|
PoiseState::Normal => {
|
||||||
|
ui.label("Normal");
|
||||||
|
},
|
||||||
|
PoiseState::Interrupted => {
|
||||||
|
ui.colored_label(Color32::YELLOW, "Interrupted");
|
||||||
|
},
|
||||||
|
PoiseState::Stunned => {
|
||||||
|
ui.colored_label(Color32::RED, "Stunned");
|
||||||
|
},
|
||||||
|
PoiseState::Dazed => {
|
||||||
|
ui.colored_label(Color32::RED, "Dazed");
|
||||||
|
},
|
||||||
|
PoiseState::KnockedDown => {
|
||||||
|
ui.colored_label(Color32::BLUE, "Knocked Down");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
@ -38,10 +38,12 @@ pub use i18n;
|
|||||||
|
|
||||||
#[cfg(feature = "singleplayer")]
|
#[cfg(feature = "singleplayer")]
|
||||||
use crate::singleplayer::Singleplayer;
|
use crate::singleplayer::Singleplayer;
|
||||||
|
#[cfg(feature = "egui-ui")]
|
||||||
|
use crate::ui::egui::EguiState;
|
||||||
use crate::{
|
use crate::{
|
||||||
audio::AudioFrontend,
|
audio::AudioFrontend,
|
||||||
profile::Profile,
|
profile::Profile,
|
||||||
render::Renderer,
|
render::{Drawer, GlobalsBindGroup},
|
||||||
settings::Settings,
|
settings::Settings,
|
||||||
window::{Event, Window},
|
window::{Event, Window},
|
||||||
};
|
};
|
||||||
@ -54,6 +56,8 @@ pub struct GlobalState {
|
|||||||
pub settings: Settings,
|
pub settings: Settings,
|
||||||
pub profile: Profile,
|
pub profile: Profile,
|
||||||
pub window: Window,
|
pub window: Window,
|
||||||
|
#[cfg(feature = "egui-ui")]
|
||||||
|
pub egui_state: EguiState,
|
||||||
pub lazy_init: scene::terrain::SpriteRenderContextLazy,
|
pub lazy_init: scene::terrain::SpriteRenderContextLazy,
|
||||||
pub audio: AudioFrontend,
|
pub audio: AudioFrontend,
|
||||||
pub info_message: Option<String>,
|
pub info_message: Option<String>,
|
||||||
@ -67,6 +71,8 @@ pub struct GlobalState {
|
|||||||
// enter the game before confirmation of successful character load
|
// enter the game before confirmation of successful character load
|
||||||
/// An error returned by Client that needs to be displayed by the UI
|
/// An error returned by Client that needs to be displayed by the UI
|
||||||
pub client_error: Option<String>,
|
pub client_error: Option<String>,
|
||||||
|
// Used to clear the shadow textures when entering a PlayState that doesn't utilise shadows
|
||||||
|
pub clear_shadows_next_frame: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlobalState {
|
impl GlobalState {
|
||||||
@ -136,6 +142,11 @@ pub trait PlayState {
|
|||||||
/// Determines whether the play state should have an enforced FPS cap
|
/// Determines whether the play state should have an enforced FPS cap
|
||||||
fn capped_fps(&self) -> bool;
|
fn capped_fps(&self) -> bool;
|
||||||
|
|
||||||
|
fn globals_bind_group(&self) -> &GlobalsBindGroup;
|
||||||
|
|
||||||
/// Draw the play state.
|
/// Draw the play state.
|
||||||
fn render(&mut self, renderer: &mut Renderer, settings: &Settings);
|
fn render<'a>(&'a self, drawer: &mut Drawer<'a>, settings: &Settings);
|
||||||
|
|
||||||
|
/// Determines whether egui will be rendered for this play state
|
||||||
|
fn egui_enabled(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,8 @@ use common::{
|
|||||||
};
|
};
|
||||||
use std::panic;
|
use std::panic;
|
||||||
use tracing::{error, info, warn};
|
use tracing::{error, info, warn};
|
||||||
|
#[cfg(feature = "egui-ui")]
|
||||||
|
use veloren_voxygen::ui::egui::EguiState;
|
||||||
|
|
||||||
#[allow(clippy::manual_unwrap_or)]
|
#[allow(clippy::manual_unwrap_or)]
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -145,9 +147,17 @@ fn main() {
|
|||||||
|
|
||||||
assets::start_hot_reloading();
|
assets::start_hot_reloading();
|
||||||
|
|
||||||
// Initialise watcher for animation hotreloading
|
// Initialise watcher for animation hot-reloading
|
||||||
#[cfg(feature = "hot-anim")]
|
#[cfg(feature = "hot-anim")]
|
||||||
anim::init();
|
{
|
||||||
|
anim::init();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialise watcher for egui hot-reloading
|
||||||
|
#[cfg(feature = "hot-egui")]
|
||||||
|
{
|
||||||
|
voxygen_egui::init();
|
||||||
|
}
|
||||||
|
|
||||||
// Setup audio
|
// Setup audio
|
||||||
let mut audio = match settings.audio.output {
|
let mut audio = match settings.audio.output {
|
||||||
@ -184,10 +194,15 @@ fn main() {
|
|||||||
|
|
||||||
let lazy_init = SpriteRenderContext::new(window.renderer_mut());
|
let lazy_init = SpriteRenderContext::new(window.renderer_mut());
|
||||||
|
|
||||||
|
#[cfg(feature = "egui-ui")]
|
||||||
|
let egui_state = EguiState::new(&window);
|
||||||
|
|
||||||
let global_state = GlobalState {
|
let global_state = GlobalState {
|
||||||
audio,
|
audio,
|
||||||
profile,
|
profile,
|
||||||
window,
|
window,
|
||||||
|
#[cfg(feature = "egui-ui")]
|
||||||
|
egui_state,
|
||||||
lazy_init,
|
lazy_init,
|
||||||
clock: Clock::new(std::time::Duration::from_secs_f64(
|
clock: Clock::new(std::time::Duration::from_secs_f64(
|
||||||
1.0 / get_fps(settings.graphics.max_fps) as f64,
|
1.0 / get_fps(settings.graphics.max_fps) as f64,
|
||||||
@ -199,6 +214,7 @@ fn main() {
|
|||||||
i18n,
|
i18n,
|
||||||
clipboard,
|
clipboard,
|
||||||
client_error: None,
|
client_error: None,
|
||||||
|
clear_shadows_next_frame: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
run::run(global_state, event_loop);
|
run::run(global_state, event_loop);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
mod ui;
|
mod ui;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
render::Renderer,
|
render::{Drawer, GlobalsBindGroup},
|
||||||
scene::simple::{self as scene, Scene},
|
scene::simple::{self as scene, Scene},
|
||||||
session::SessionState,
|
session::SessionState,
|
||||||
settings::Settings,
|
settings::Settings,
|
||||||
@ -20,7 +20,6 @@ pub struct CharSelectionState {
|
|||||||
char_selection_ui: CharSelectionUi,
|
char_selection_ui: CharSelectionUi,
|
||||||
client: Rc<RefCell<Client>>,
|
client: Rc<RefCell<Client>>,
|
||||||
scene: Scene,
|
scene: Scene,
|
||||||
need_shadow_clear: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CharSelectionState {
|
impl CharSelectionState {
|
||||||
@ -37,7 +36,6 @@ impl CharSelectionState {
|
|||||||
char_selection_ui,
|
char_selection_ui,
|
||||||
client,
|
client,
|
||||||
scene,
|
scene,
|
||||||
need_shadow_clear: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +73,7 @@ impl PlayState for CharSelectionState {
|
|||||||
.set_scale_mode(global_state.settings.interface.ui_scale);
|
.set_scale_mode(global_state.settings.interface.ui_scale);
|
||||||
|
|
||||||
// Clear shadow textures since we don't render to them here
|
// Clear shadow textures since we don't render to them here
|
||||||
self.need_shadow_clear = true;
|
global_state.clear_shadows_next_frame = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<WinEvent>) -> PlayStateResult {
|
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<WinEvent>) -> PlayStateResult {
|
||||||
@ -234,21 +232,9 @@ impl PlayState for CharSelectionState {
|
|||||||
|
|
||||||
fn capped_fps(&self) -> bool { true }
|
fn capped_fps(&self) -> bool { true }
|
||||||
|
|
||||||
fn render(&mut self, renderer: &mut Renderer, _: &Settings) {
|
fn globals_bind_group(&self) -> &GlobalsBindGroup { self.scene.global_bind_group() }
|
||||||
let mut drawer = match renderer
|
|
||||||
.start_recording_frame(self.scene.global_bind_group())
|
|
||||||
.expect("Unrecoverable render error when starting a new frame!")
|
|
||||||
{
|
|
||||||
Some(d) => d,
|
|
||||||
// Couldn't get swap chain texture this fime
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
if self.need_shadow_clear {
|
|
||||||
drawer.clear_shadows();
|
|
||||||
self.need_shadow_clear = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
fn render<'a>(&'a self, drawer: &mut Drawer<'a>, _: &Settings) {
|
||||||
let client = self.client.borrow();
|
let client = self.client.borrow();
|
||||||
let (humanoid_body, loadout) =
|
let (humanoid_body, loadout) =
|
||||||
Self::get_humanoid_body_inventory(&self.char_selection_ui, &client);
|
Self::get_humanoid_body_inventory(&self.char_selection_ui, &client);
|
||||||
@ -270,4 +256,6 @@ impl PlayState for CharSelectionState {
|
|||||||
self.char_selection_ui.render(&mut ui_drawer);
|
self.char_selection_ui.render(&mut ui_drawer);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn egui_enabled(&self) -> bool { false }
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,11 @@ use super::char_selection::CharSelectionState;
|
|||||||
#[cfg(feature = "singleplayer")]
|
#[cfg(feature = "singleplayer")]
|
||||||
use crate::singleplayer::Singleplayer;
|
use crate::singleplayer::Singleplayer;
|
||||||
use crate::{
|
use crate::{
|
||||||
i18n::LocalizationHandle, render::Renderer, settings::Settings, window::Event, Direction,
|
i18n::LocalizationHandle,
|
||||||
GlobalState, PlayState, PlayStateResult,
|
render::{Drawer, GlobalsBindGroup},
|
||||||
|
settings::Settings,
|
||||||
|
window::Event,
|
||||||
|
Direction, GlobalState, PlayState, PlayStateResult,
|
||||||
};
|
};
|
||||||
use client::{
|
use client::{
|
||||||
addr::ConnectionArgs,
|
addr::ConnectionArgs,
|
||||||
@ -319,21 +322,17 @@ impl PlayState for MainMenuState {
|
|||||||
|
|
||||||
fn capped_fps(&self) -> bool { true }
|
fn capped_fps(&self) -> bool { true }
|
||||||
|
|
||||||
fn render(&mut self, renderer: &mut Renderer, _: &Settings) {
|
fn globals_bind_group(&self) -> &GlobalsBindGroup { self.scene.global_bind_group() }
|
||||||
let mut drawer = match renderer
|
|
||||||
.start_recording_frame(self.scene.global_bind_group())
|
|
||||||
.expect("Unrecoverable render error when starting a new frame!")
|
|
||||||
{
|
|
||||||
Some(d) => d,
|
|
||||||
// Couldn't get swap chain texture this frame
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
fn render<'a>(&'a self, drawer: &mut Drawer<'a>, _: &Settings) {
|
||||||
// Draw the UI to the screen.
|
// Draw the UI to the screen.
|
||||||
if let Some(mut ui_drawer) = drawer.third_pass().draw_ui() {
|
let mut third_pass = drawer.third_pass();
|
||||||
|
if let Some(mut ui_drawer) = third_pass.draw_ui() {
|
||||||
self.main_menu_ui.render(&mut ui_drawer);
|
self.main_menu_ui.render(&mut ui_drawer);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn egui_enabled(&self) -> bool { false }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_client_msg_error(
|
fn get_client_msg_error(
|
||||||
|
@ -30,6 +30,8 @@ use super::{
|
|||||||
use common::assets::{self, AssetExt, AssetHandle};
|
use common::assets::{self, AssetExt, AssetHandle};
|
||||||
use common_base::span;
|
use common_base::span;
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
|
#[cfg(feature = "egui-ui")]
|
||||||
|
use egui_wgpu_backend::wgpu::TextureFormat;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::{error, info, warn};
|
use tracing::{error, info, warn};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
@ -136,6 +138,9 @@ pub struct Renderer {
|
|||||||
profile_times: Vec<wgpu_profiler::GpuTimerScopeResult>,
|
profile_times: Vec<wgpu_profiler::GpuTimerScopeResult>,
|
||||||
profiler_features_enabled: bool,
|
profiler_features_enabled: bool,
|
||||||
|
|
||||||
|
#[cfg(feature = "egui-ui")]
|
||||||
|
egui_renderpass: egui_wgpu_backend::RenderPass,
|
||||||
|
|
||||||
// This checks is added because windows resizes the window to 0,0 when
|
// This checks is added because windows resizes the window to 0,0 when
|
||||||
// minimizing and this causes a bunch of validation errors
|
// minimizing and this causes a bunch of validation errors
|
||||||
is_minimized: bool,
|
is_minimized: bool,
|
||||||
@ -398,6 +403,10 @@ impl Renderer {
|
|||||||
profiler.enable_timer = mode.profiler_enabled;
|
profiler.enable_timer = mode.profiler_enabled;
|
||||||
profiler.enable_debug_marker = mode.profiler_enabled;
|
profiler.enable_debug_marker = mode.profiler_enabled;
|
||||||
|
|
||||||
|
#[cfg(feature = "egui-ui")]
|
||||||
|
let egui_renderpass =
|
||||||
|
egui_wgpu_backend::RenderPass::new(&*device, TextureFormat::Bgra8UnormSrgb, 1);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
device,
|
device,
|
||||||
queue,
|
queue,
|
||||||
@ -430,6 +439,9 @@ impl Renderer {
|
|||||||
profile_times: Vec::new(),
|
profile_times: Vec::new(),
|
||||||
profiler_features_enabled,
|
profiler_features_enabled,
|
||||||
|
|
||||||
|
#[cfg(feature = "egui-ui")]
|
||||||
|
egui_renderpass,
|
||||||
|
|
||||||
is_minimized: false,
|
is_minimized: false,
|
||||||
|
|
||||||
graphics_backend,
|
graphics_backend,
|
||||||
|
@ -14,6 +14,8 @@ use core::{num::NonZeroU32, ops::Range};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use vek::Aabr;
|
use vek::Aabr;
|
||||||
use wgpu_profiler::scope::{ManualOwningScope, OwningScope, Scope};
|
use wgpu_profiler::scope::{ManualOwningScope, OwningScope, Scope};
|
||||||
|
#[cfg(feature = "egui-ui")]
|
||||||
|
use {common_base::span, egui_wgpu_backend::ScreenDescriptor, egui_winit_platform::Platform};
|
||||||
|
|
||||||
// Currently available pipelines
|
// Currently available pipelines
|
||||||
enum Pipelines<'frame> {
|
enum Pipelines<'frame> {
|
||||||
@ -53,6 +55,8 @@ impl<'frame> Pipelines<'frame> {
|
|||||||
struct RendererBorrow<'frame> {
|
struct RendererBorrow<'frame> {
|
||||||
queue: &'frame wgpu::Queue,
|
queue: &'frame wgpu::Queue,
|
||||||
device: &'frame wgpu::Device,
|
device: &'frame wgpu::Device,
|
||||||
|
#[cfg(feature = "egui-ui")]
|
||||||
|
sc_desc: &'frame wgpu::SwapChainDescriptor,
|
||||||
shadow: Option<&'frame super::Shadow>,
|
shadow: Option<&'frame super::Shadow>,
|
||||||
pipelines: Pipelines<'frame>,
|
pipelines: Pipelines<'frame>,
|
||||||
locals: &'frame super::locals::Locals,
|
locals: &'frame super::locals::Locals,
|
||||||
@ -60,6 +64,8 @@ struct RendererBorrow<'frame> {
|
|||||||
mode: &'frame super::super::RenderMode,
|
mode: &'frame super::super::RenderMode,
|
||||||
quad_index_buffer_u16: &'frame Buffer<u16>,
|
quad_index_buffer_u16: &'frame Buffer<u16>,
|
||||||
quad_index_buffer_u32: &'frame Buffer<u32>,
|
quad_index_buffer_u32: &'frame Buffer<u32>,
|
||||||
|
#[cfg(feature = "egui-ui")]
|
||||||
|
egui_render_pass: &'frame mut egui_wgpu_backend::RenderPass,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Drawer<'frame> {
|
pub struct Drawer<'frame> {
|
||||||
@ -100,6 +106,8 @@ impl<'frame> Drawer<'frame> {
|
|||||||
let borrow = RendererBorrow {
|
let borrow = RendererBorrow {
|
||||||
queue: &renderer.queue,
|
queue: &renderer.queue,
|
||||||
device: &renderer.device,
|
device: &renderer.device,
|
||||||
|
#[cfg(feature = "egui-ui")]
|
||||||
|
sc_desc: &renderer.sc_desc,
|
||||||
shadow,
|
shadow,
|
||||||
pipelines,
|
pipelines,
|
||||||
locals: &renderer.locals,
|
locals: &renderer.locals,
|
||||||
@ -107,6 +115,8 @@ impl<'frame> Drawer<'frame> {
|
|||||||
mode: &renderer.mode,
|
mode: &renderer.mode,
|
||||||
quad_index_buffer_u16: &renderer.quad_index_buffer_u16,
|
quad_index_buffer_u16: &renderer.quad_index_buffer_u16,
|
||||||
quad_index_buffer_u32: &renderer.quad_index_buffer_u32,
|
quad_index_buffer_u32: &renderer.quad_index_buffer_u32,
|
||||||
|
#[cfg(feature = "egui-ui")]
|
||||||
|
egui_render_pass: &mut renderer.egui_renderpass,
|
||||||
};
|
};
|
||||||
|
|
||||||
let encoder =
|
let encoder =
|
||||||
@ -261,6 +271,48 @@ impl<'frame> Drawer<'frame> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "egui-ui")]
|
||||||
|
pub fn draw_egui(&mut self, platform: &mut Platform, scale_factor: f32) {
|
||||||
|
span!(guard, "Draw egui");
|
||||||
|
|
||||||
|
let (_output, paint_commands) = platform.end_frame();
|
||||||
|
|
||||||
|
let paint_jobs = platform.context().tessellate(paint_commands);
|
||||||
|
|
||||||
|
let screen_descriptor = ScreenDescriptor {
|
||||||
|
physical_width: self.borrow.sc_desc.width,
|
||||||
|
physical_height: self.borrow.sc_desc.height,
|
||||||
|
scale_factor: scale_factor as f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.borrow.egui_render_pass.update_texture(
|
||||||
|
self.borrow.device,
|
||||||
|
self.borrow.queue,
|
||||||
|
&platform.context().texture(),
|
||||||
|
);
|
||||||
|
self.borrow
|
||||||
|
.egui_render_pass
|
||||||
|
.update_user_textures(self.borrow.device, self.borrow.queue);
|
||||||
|
self.borrow.egui_render_pass.update_buffers(
|
||||||
|
self.borrow.device,
|
||||||
|
self.borrow.queue,
|
||||||
|
&paint_jobs,
|
||||||
|
&screen_descriptor,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.borrow.egui_render_pass.execute(
|
||||||
|
&mut self.encoder.as_mut().unwrap(),
|
||||||
|
self.taking_screenshot
|
||||||
|
.as_ref()
|
||||||
|
.map_or(&self.swap_tex.view, |s| s.texture_view()),
|
||||||
|
&paint_jobs,
|
||||||
|
&screen_descriptor,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
drop(guard);
|
||||||
|
}
|
||||||
|
|
||||||
/// Does nothing if the shadow pipelines are not available or shadow map
|
/// Does nothing if the shadow pipelines are not available or shadow map
|
||||||
/// rendering is disabled
|
/// rendering is disabled
|
||||||
pub fn draw_point_shadows<'data: 'frame>(
|
pub fn draw_point_shadows<'data: 'frame>(
|
||||||
|
@ -31,6 +31,8 @@ pub fn run(mut global_state: GlobalState, event_loop: EventLoop) {
|
|||||||
// Continuously run loop since we handle sleeping
|
// Continuously run loop since we handle sleeping
|
||||||
*control_flow = winit::event_loop::ControlFlow::Poll;
|
*control_flow = winit::event_loop::ControlFlow::Poll;
|
||||||
|
|
||||||
|
#[cfg(feature = "egui-ui")]
|
||||||
|
global_state.egui_state.platform.handle_event(&event);
|
||||||
// Get events for the ui.
|
// Get events for the ui.
|
||||||
if let Some(event) = ui::Event::try_from(&event, global_state.window.window()) {
|
if let Some(event) = ui::Event::try_from(&event, global_state.window.window()) {
|
||||||
global_state.window.send_event(Event::Ui(event));
|
global_state.window.send_event(Event::Ui(event));
|
||||||
@ -167,13 +169,37 @@ fn handle_main_events_cleared(
|
|||||||
let mut capped_fps = false;
|
let mut capped_fps = false;
|
||||||
|
|
||||||
drop(guard);
|
drop(guard);
|
||||||
|
|
||||||
|
#[cfg(feature = "egui-ui")]
|
||||||
|
let scale_factor = global_state.window.window().scale_factor() as f32;
|
||||||
|
|
||||||
if let Some(last) = states.last_mut() {
|
if let Some(last) = states.last_mut() {
|
||||||
span!(guard, "Render");
|
|
||||||
let renderer = global_state.window.renderer_mut();
|
|
||||||
// Render the screen using the global renderer
|
|
||||||
last.render(renderer, &global_state.settings);
|
|
||||||
capped_fps = last.capped_fps();
|
capped_fps = last.capped_fps();
|
||||||
|
|
||||||
|
span!(guard, "Render");
|
||||||
|
|
||||||
|
// Render the screen using the global renderer
|
||||||
|
if let Some(mut drawer) = global_state
|
||||||
|
.window
|
||||||
|
.renderer_mut()
|
||||||
|
.start_recording_frame(last.globals_bind_group())
|
||||||
|
.expect("Unrecoverable render error when starting a new frame!")
|
||||||
|
{
|
||||||
|
if global_state.clear_shadows_next_frame {
|
||||||
|
drawer.clear_shadows();
|
||||||
|
}
|
||||||
|
|
||||||
|
last.render(&mut drawer, &global_state.settings);
|
||||||
|
|
||||||
|
#[cfg(feature = "egui-ui")]
|
||||||
|
if last.egui_enabled() && global_state.settings.interface.toggle_debug {
|
||||||
|
drawer.draw_egui(&mut global_state.egui_state.platform, scale_factor);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if global_state.clear_shadows_next_frame {
|
||||||
|
global_state.clear_shadows_next_frame = false;
|
||||||
|
}
|
||||||
|
|
||||||
drop(guard);
|
drop(guard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ impl DebugShape {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub struct DebugShapeId(u64);
|
pub struct DebugShapeId(pub u64);
|
||||||
|
|
||||||
pub struct Debug {
|
pub struct Debug {
|
||||||
next_shape_id: DebugShapeId,
|
next_shape_id: DebugShapeId,
|
||||||
|
@ -38,7 +38,7 @@ use crate::{
|
|||||||
hud::{DebugInfo, Event as HudEvent, Hud, HudInfo, LootMessage, PromptDialogSettings},
|
hud::{DebugInfo, Event as HudEvent, Hud, HudInfo, LootMessage, PromptDialogSettings},
|
||||||
key_state::KeyState,
|
key_state::KeyState,
|
||||||
menu::char_selection::CharSelectionState,
|
menu::char_selection::CharSelectionState,
|
||||||
render::Renderer,
|
render::{Drawer, GlobalsBindGroup},
|
||||||
scene::{camera, terrain::Interaction, CameraMode, DebugShapeId, Scene, SceneData},
|
scene::{camera, terrain::Interaction, CameraMode, DebugShapeId, Scene, SceneData},
|
||||||
settings::Settings,
|
settings::Settings,
|
||||||
window::{AnalogGameInput, Event, GameInput},
|
window::{AnalogGameInput, Event, GameInput},
|
||||||
@ -46,6 +46,8 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use settings_change::Language::ChangeLanguage;
|
use settings_change::Language::ChangeLanguage;
|
||||||
|
#[cfg(feature = "egui-ui")]
|
||||||
|
use voxygen_egui::EguiDebugInfo;
|
||||||
|
|
||||||
/// The action to perform after a tick
|
/// The action to perform after a tick
|
||||||
enum TickAction {
|
enum TickAction {
|
||||||
@ -1022,6 +1024,19 @@ impl PlayState for SessionState {
|
|||||||
self.interactable,
|
self.interactable,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Maintain egui (debug interface)
|
||||||
|
#[cfg(feature = "egui-ui")]
|
||||||
|
if global_state.settings.interface.toggle_debug {
|
||||||
|
global_state.egui_state.maintain(
|
||||||
|
&self.client.borrow(),
|
||||||
|
&mut self.scene,
|
||||||
|
debug_info.map(|debug_info| EguiDebugInfo {
|
||||||
|
frame_time: debug_info.frame_time,
|
||||||
|
ping_ms: debug_info.ping_ms,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Look for changes in the localization files
|
// Look for changes in the localization files
|
||||||
if global_state.i18n.reloaded() {
|
if global_state.i18n.reloaded() {
|
||||||
hud_events.push(HudEvent::SettingsChange(
|
hud_events.push(HudEvent::SettingsChange(
|
||||||
@ -1401,19 +1416,13 @@ impl PlayState for SessionState {
|
|||||||
|
|
||||||
fn capped_fps(&self) -> bool { false }
|
fn capped_fps(&self) -> bool { false }
|
||||||
|
|
||||||
|
fn globals_bind_group(&self) -> &GlobalsBindGroup { self.scene.global_bind_group() }
|
||||||
|
|
||||||
/// Render the session to the screen.
|
/// Render the session to the screen.
|
||||||
///
|
///
|
||||||
/// This method should be called once per frame.
|
/// This method should be called once per frame.
|
||||||
fn render(&mut self, renderer: &mut Renderer, settings: &Settings) {
|
fn render<'a>(&'a self, mut drawer: &mut Drawer<'a>, settings: &Settings) {
|
||||||
span!(_guard, "render", "<Session as PlayState>::render");
|
span!(_guard, "render", "<Session as PlayState>::render");
|
||||||
let mut drawer = match renderer
|
|
||||||
.start_recording_frame(self.scene.global_bind_group())
|
|
||||||
.expect("Unrecoverable render error when starting a new frame!")
|
|
||||||
{
|
|
||||||
Some(d) => d,
|
|
||||||
// Couldn't get swap chain texture this frame
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Render world
|
// Render world
|
||||||
{
|
{
|
||||||
@ -1465,6 +1474,8 @@ impl PlayState for SessionState {
|
|||||||
}; // Note: this semicolon is needed for the third_pass borrow to be dropped before it's lifetime ends
|
}; // Note: this semicolon is needed for the third_pass borrow to be dropped before it's lifetime ends
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn egui_enabled(&self) -> bool { true }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Max distance an entity can be "targeted"
|
/// Max distance an entity can be "targeted"
|
||||||
|
68
voxygen/src/ui/egui/mod.rs
Normal file
68
voxygen/src/ui/egui/mod.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
use crate::{
|
||||||
|
scene::{DebugShape, DebugShapeId, Scene},
|
||||||
|
window::Window,
|
||||||
|
};
|
||||||
|
use client::Client;
|
||||||
|
use egui::FontDefinitions;
|
||||||
|
use egui_winit_platform::{Platform, PlatformDescriptor};
|
||||||
|
use voxygen_egui::{DebugShapeAction, EguiDebugInfo, EguiInnerState, EguiWindows};
|
||||||
|
|
||||||
|
pub struct EguiState {
|
||||||
|
pub platform: Platform,
|
||||||
|
egui_inner_state: EguiInnerState,
|
||||||
|
egui_windows: EguiWindows,
|
||||||
|
new_debug_shape_id: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EguiState {
|
||||||
|
pub fn new(window: &Window) -> Self {
|
||||||
|
let platform = Platform::new(PlatformDescriptor {
|
||||||
|
physical_width: window.window().inner_size().width as u32,
|
||||||
|
physical_height: window.window().inner_size().height as u32,
|
||||||
|
scale_factor: window.scale_factor(),
|
||||||
|
font_definitions: FontDefinitions::default(),
|
||||||
|
style: Default::default(),
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
platform,
|
||||||
|
egui_inner_state: EguiInnerState::default(),
|
||||||
|
egui_windows: EguiWindows::default(),
|
||||||
|
new_debug_shape_id: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn maintain(
|
||||||
|
&mut self,
|
||||||
|
client: &Client,
|
||||||
|
scene: &mut Scene,
|
||||||
|
debug_info: Option<EguiDebugInfo>,
|
||||||
|
) {
|
||||||
|
let egui_actions = voxygen_egui::maintain(
|
||||||
|
&mut self.platform,
|
||||||
|
&mut self.egui_inner_state,
|
||||||
|
&mut self.egui_windows,
|
||||||
|
client,
|
||||||
|
debug_info,
|
||||||
|
self.new_debug_shape_id.take(),
|
||||||
|
);
|
||||||
|
|
||||||
|
egui_actions.actions.iter().for_each(|action| match action {
|
||||||
|
DebugShapeAction::AddCylinder { height, radius } => {
|
||||||
|
let shape_id = scene.debug.add_shape(DebugShape::Cylinder {
|
||||||
|
height: *height,
|
||||||
|
radius: *radius,
|
||||||
|
});
|
||||||
|
self.new_debug_shape_id = Some(shape_id.0);
|
||||||
|
},
|
||||||
|
DebugShapeAction::RemoveShape(debug_shape_id) => {
|
||||||
|
scene.debug.remove_shape(DebugShapeId(*debug_shape_id));
|
||||||
|
},
|
||||||
|
DebugShapeAction::SetPosAndColor { id, pos, color } => {
|
||||||
|
scene
|
||||||
|
.debug
|
||||||
|
.set_pos_and_color(DebugShapeId(*id), *pos, *color);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@ mod widgets;
|
|||||||
pub mod img_ids;
|
pub mod img_ids;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod fonts;
|
pub mod fonts;
|
||||||
|
#[cfg(feature = "egui-ui")] pub mod egui;
|
||||||
pub mod ice;
|
pub mod ice;
|
||||||
pub mod keyed_jobs;
|
pub mod keyed_jobs;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user