feat(client): add a stackable stats overlay toggle
This commit is contained in:
parent
17e7b465ce
commit
d445a29e5e
|
|
@ -6,6 +6,7 @@
|
||||||
//!
|
//!
|
||||||
//! - **F1 + V**: filled terrain with vertex points overlaid, showing where the mesher placed geometry without losing the surface.
|
//! - **F1 + V**: filled terrain with vertex points overlaid, showing where the mesher placed geometry without losing the surface.
|
||||||
//! - **F1 + B**: filled terrain with the triangle edges overlaid, showing the size and shape of the emitted quads.
|
//! - **F1 + B**: filled terrain with the triangle edges overlaid, showing the size and shape of the emitted quads.
|
||||||
|
//! - **F1 + I**: the debug statistics panel.
|
||||||
//!
|
//!
|
||||||
//! Holding a [`SOLO_MODIFIER`] (either Shift) as well drops the filled pass, leaving the debug geometry alone against the clear colour: **F1 + Shift + V** for points only, **F1 + Shift + B** for wireframe only.
|
//! Holding a [`SOLO_MODIFIER`] (either Shift) as well drops the filled pass, leaving the debug geometry alone against the clear colour: **F1 + Shift + V** for points only, **F1 + Shift + B** for wireframe only.
|
||||||
//!
|
//!
|
||||||
|
|
@ -20,6 +21,13 @@ const DEBUG_MODIFIER: KeyCode = KeyCode::F1;
|
||||||
/// The keys that, held alongside [`DEBUG_MODIFIER`], select the solo form of a debug view. Both shifts are accepted so the chord is reachable with either hand.
|
/// The keys that, held alongside [`DEBUG_MODIFIER`], select the solo form of a debug view. Both shifts are accepted so the chord is reachable with either hand.
|
||||||
const SOLO_MODIFIER: [KeyCode; 2] = [KeyCode::ShiftLeft, KeyCode::ShiftRight];
|
const SOLO_MODIFIER: [KeyCode; 2] = [KeyCode::ShiftLeft, KeyCode::ShiftRight];
|
||||||
|
|
||||||
|
const VERTEX_POINTS_OVERLAY_KEY: KeyCode = KeyCode::KeyV;
|
||||||
|
|
||||||
|
const WIREFRAME_OVERLAY_KEY: KeyCode = KeyCode::KeyB;
|
||||||
|
|
||||||
|
/// The key that, held alongside [`DEBUG_MODIFIER`], toggles the statistics panel.
|
||||||
|
const STATS_KEY: KeyCode = KeyCode::KeyI;
|
||||||
|
|
||||||
/// A debug operation requested by the input layer, applied by the caller.
|
/// A debug operation requested by the input layer, applied by the caller.
|
||||||
///
|
///
|
||||||
/// The layer deliberately returns an intent rather than acting directly, so it owns no renderer or window handles and stays a pure function of key events.
|
/// The layer deliberately returns an intent rather than acting directly, so it owns no renderer or window handles and stays a pure function of key events.
|
||||||
|
|
@ -27,6 +35,8 @@ const SOLO_MODIFIER: [KeyCode; 2] = [KeyCode::ShiftLeft, KeyCode::ShiftRight];
|
||||||
pub(crate) enum DebugAction {
|
pub(crate) enum DebugAction {
|
||||||
/// Applies the given rasterisation mode to the renderer.
|
/// Applies the given rasterisation mode to the renderer.
|
||||||
SetRenderMode(RenderMode),
|
SetRenderMode(RenderMode),
|
||||||
|
/// Enables or disables emission of the statistics panel.
|
||||||
|
SetStatsOverlay(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Owns debug-only input state and translates key events into [`DebugAction`]s.
|
/// Owns debug-only input state and translates key events into [`DebugAction`]s.
|
||||||
|
|
@ -38,6 +48,8 @@ pub(crate) struct DebugControls {
|
||||||
solo_held: bool,
|
solo_held: bool,
|
||||||
/// The rasterisation mode most recently requested, used to make each chord a toggle back to [`RenderMode::Filled`].
|
/// The rasterisation mode most recently requested, used to make each chord a toggle back to [`RenderMode::Filled`].
|
||||||
render_mode: RenderMode,
|
render_mode: RenderMode,
|
||||||
|
/// Whether the statistics panel is being emitted.
|
||||||
|
stats_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DebugControls {
|
impl DebugControls {
|
||||||
|
|
@ -60,6 +72,12 @@ impl DebugControls {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The statistics chord is resolved before the raster table so the two axes never contend for a key. The solo modifier selects between overlaid and standalone geometry and has no meaning for a panel that draws none, so it is ignored here.
|
||||||
|
if code == STATS_KEY {
|
||||||
|
self.stats_enabled = !self.stats_enabled;
|
||||||
|
return Some(DebugAction::SetStatsOverlay(self.stats_enabled));
|
||||||
|
}
|
||||||
|
|
||||||
let requested = render_mode_for_key(code, self.solo_held)?;
|
let requested = render_mode_for_key(code, self.solo_held)?;
|
||||||
|
|
||||||
// Re-pressing the chord for the active mode returns to the normal path, so a single chord both enables and disables its mode.
|
// Re-pressing the chord for the active mode returns to the normal path, so a single chord both enables and disables its mode.
|
||||||
|
|
@ -78,10 +96,10 @@ impl DebugControls {
|
||||||
/// This is the single table a new rasterisation debug mode is added to: one key, one overlaid form, one solo form.
|
/// This is the single table a new rasterisation debug mode is added to: one key, one overlaid form, one solo form.
|
||||||
const fn render_mode_for_key(code: KeyCode, solo: bool) -> Option<RenderMode> {
|
const fn render_mode_for_key(code: KeyCode, solo: bool) -> Option<RenderMode> {
|
||||||
match (code, solo) {
|
match (code, solo) {
|
||||||
(KeyCode::KeyV, false) => Some(RenderMode::FilledPoints),
|
(VERTEX_POINTS_OVERLAY_KEY, false) => Some(RenderMode::FilledPoints),
|
||||||
(KeyCode::KeyV, true) => Some(RenderMode::Points),
|
(VERTEX_POINTS_OVERLAY_KEY, true) => Some(RenderMode::Points),
|
||||||
(KeyCode::KeyB, false) => Some(RenderMode::FilledWireframe),
|
(WIREFRAME_OVERLAY_KEY, false) => Some(RenderMode::FilledWireframe),
|
||||||
(KeyCode::KeyB, true) => Some(RenderMode::Wireframe),
|
(WIREFRAME_OVERLAY_KEY, true) => Some(RenderMode::Wireframe),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,8 @@ struct App {
|
||||||
last_center: Option<shared::world::ChunkPos>,
|
last_center: Option<shared::world::ChunkPos>,
|
||||||
/// Streams chunk meshes in and out around the camera. `None` until the renderer is initialised on resume.
|
/// Streams chunk meshes in and out around the camera. `None` until the renderer is initialised on resume.
|
||||||
chunks: Option<chunks::ChunkManager>,
|
chunks: Option<chunks::ChunkManager>,
|
||||||
|
/// Whether the debug statistics panel is being emitted. Collection is unconditional; only emission is gated on this.
|
||||||
|
stats_overlay: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for App {
|
impl Default for App {
|
||||||
|
|
@ -92,6 +94,7 @@ impl Default for App {
|
||||||
connected: false,
|
connected: false,
|
||||||
last_center: None,
|
last_center: None,
|
||||||
chunks: None,
|
chunks: None,
|
||||||
|
stats_overlay: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -108,6 +111,10 @@ impl App {
|
||||||
info!(?mode, "render mode toggled");
|
info!(?mode, "render mode toggled");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
debug::DebugAction::SetStatsOverlay(enabled) => {
|
||||||
|
self.stats_overlay = enabled;
|
||||||
|
info!(enabled, "statistics overlay toggled");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -141,3 +141,62 @@ fn solo_modifier_alone_produces_no_action() {
|
||||||
assert_eq!(controls.handle_key(KeyCode::ShiftLeft, true), None);
|
assert_eq!(controls.handle_key(KeyCode::ShiftLeft, true), None);
|
||||||
assert_eq!(tap(&mut controls, KeyCode::KeyV), None);
|
assert_eq!(tap(&mut controls, KeyCode::KeyV), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn the_stats_chord_toggles_the_overlay_on_and_off() {
|
||||||
|
let mut controls = DebugControls::default();
|
||||||
|
controls.handle_key(DEBUG_MODIFIER, true);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
tap(&mut controls, STATS_KEY),
|
||||||
|
Some(DebugAction::SetStatsOverlay(true))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
tap(&mut controls, STATS_KEY),
|
||||||
|
Some(DebugAction::SetStatsOverlay(false))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn the_stats_chord_leaves_the_render_mode_untouched() {
|
||||||
|
let mut controls = DebugControls::default();
|
||||||
|
controls.handle_key(DEBUG_MODIFIER, true);
|
||||||
|
tap(&mut controls, KeyCode::KeyV);
|
||||||
|
|
||||||
|
tap(&mut controls, STATS_KEY);
|
||||||
|
|
||||||
|
// The raster axis must survive a toggle on the statistics axis; a single-enum design would have reset it.
|
||||||
|
assert_eq!(controls.render_mode, RenderMode::FilledPoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_raster_chord_leaves_the_stats_overlay_untouched() {
|
||||||
|
let mut controls = DebugControls::default();
|
||||||
|
controls.handle_key(DEBUG_MODIFIER, true);
|
||||||
|
tap(&mut controls, STATS_KEY);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
tap(&mut controls, KeyCode::KeyB),
|
||||||
|
Some(DebugAction::SetRenderMode(RenderMode::FilledWireframe))
|
||||||
|
);
|
||||||
|
assert!(controls.stats_enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn the_solo_modifier_does_not_change_the_stats_chord() {
|
||||||
|
let mut controls = DebugControls::default();
|
||||||
|
controls.handle_key(DEBUG_MODIFIER, true);
|
||||||
|
controls.handle_key(KeyCode::ShiftLeft, true);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
tap(&mut controls, STATS_KEY),
|
||||||
|
Some(DebugAction::SetStatsOverlay(true))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn the_stats_chord_requires_the_debug_modifier() {
|
||||||
|
let mut controls = DebugControls::default();
|
||||||
|
assert_eq!(tap(&mut controls, STATS_KEY), None);
|
||||||
|
assert!(!controls.stats_enabled);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue