implemented more imgui stuff

This commit is contained in:
Ben Wallis 2020-07-26 20:17:56 +01:00
parent 2cd6ee9ca8
commit 1078873ae4
8 changed files with 215 additions and 51 deletions

View File

@ -86,7 +86,7 @@ pub struct Client {
last_server_ping: f64,
last_server_pong: f64,
last_ping_delta: f64,
ping_deltas: VecDeque<f64>,
pub ping_deltas: VecDeque<f64>,
tick: u64,
state: State,

View File

@ -150,7 +150,7 @@ lazy_static! {
.collect();
/// List of item specifiers. Useful for tab completing
static ref ITEM_SPECS: Vec<String> = {
pub static ref ITEM_SPECS: Vec<String> = {
let path = assets::ASSETS_PATH.join("common").join("items");
let mut items = vec![];
fn list_items (path: &Path, base: &Path, mut items: &mut Vec<String>) -> std::io::Result<()>{

View File

@ -232,7 +232,7 @@ pub struct DebugInfo {
pub velocity: Option<comp::Vel>,
pub ori: Option<comp::Ori>,
pub num_chunks: u32,
pub num_visible_chunks: u32,
pub num_chunks_visible: u32,
pub num_figures: u32,
pub num_figures_visible: u32,
}
@ -633,7 +633,7 @@ impl Hud {
&mut self,
client: &Client,
global_state: &GlobalState,
debug_info: DebugInfo,
debug_info: &DebugInfo,
dt: Duration,
info: HudInfo,
camera: &Camera,
@ -1279,7 +1279,8 @@ impl Hud {
}
// Display debug window.
if global_state.settings.gameplay.toggle_debug {
// Temporarily disabled while testing ImgUi
if false { //global_state.settings.gameplay.toggle_debug {
// Alpha Version
Text::new(&version)
.top_left_with_margins_on(ui_widgets.window, 5.0, 5.0)
@ -1386,7 +1387,7 @@ impl Hud {
// Number of chunks
Text::new(&format!(
"Chunks: {} ({} visible)",
debug_info.num_chunks, debug_info.num_visible_chunks,
debug_info.num_chunks, debug_info.num_chunks_visible,
))
.color(TEXT_COLOR)
.down_from(self.ids.entity_count, 5.0)
@ -2293,7 +2294,7 @@ impl Hud {
&mut self,
client: &Client,
global_state: &mut GlobalState,
debug_info: DebugInfo,
debug_info: &DebugInfo,
camera: &Camera,
dt: Duration,
info: HudInfo,

View File

@ -51,6 +51,7 @@ pub struct GlobalState {
pub singleplayer: Option<Singleplayer>,
// TODO: redo this so that the watcher doesn't have to exist for reloading to occur
pub localization_watcher: watch::ReloadIndicator,
pub imgui_render_required: bool
}
impl GlobalState {

View File

@ -165,6 +165,7 @@ fn main() {
#[cfg(feature = "singleplayer")]
singleplayer: None,
localization_watcher,
imgui_render_required: false
};
run::run(global_state, event_loop);

View File

@ -79,7 +79,6 @@ fn handle_main_events_cleared(
let mut exit = true;
while let Some(state_result) = states.last_mut().map(|last| {
let events = global_state.window.fetch_events();
global_state.window.imgui_begin_frame();
last.tick(global_state, events)
}) {
// Implement state transfer logic.
@ -144,8 +143,9 @@ fn handle_main_events_cleared(
last.render(global_state.window.renderer_mut(), &global_state.settings);
if global_state.settings.gameplay.toggle_debug {
if global_state.imgui_render_required && global_state.settings.gameplay.toggle_debug {
global_state.window.imgui_render();
global_state.imgui_render_required = false;
}
global_state.window.renderer_mut().flush();

View File

@ -11,6 +11,7 @@ use crate::{
window::{AnalogGameInput, Event, GameInput},
Direction, Error, GlobalState, PlayState, PlayStateResult,
};
use chrono::NaiveTime;
use client::{self, Client};
use common::{
assets::{load_expect, load_watched},
@ -21,15 +22,15 @@ use common::{
},
event::EventBus,
msg::ClientState,
terrain::{Block, BlockKind},
terrain::{Block, BlockKind, TerrainChunk},
util::Dir,
vol::ReadVol,
vol::{ReadVol, RectRasterableVol}
};
use specs::{Join, WorldExt};
use std::{cell::RefCell, rc::Rc, time::Duration};
use tracing::{error, info};
use vek::*;
use imgui::{Condition, im_str, Window};
use imgui::{Condition, im_str, Window, ImString, ImStr};
/// The action to perform after a tick
enum TickAction {
@ -53,6 +54,24 @@ pub struct SessionState {
free_look: bool,
auto_walk: bool,
is_aiming: bool,
imgui_state: ImgUiState
}
pub struct ImgUiState {
give_item_quantity: Rc<RefCell<i32>>,
items_list_selected_item: Rc<RefCell<i32>>,
/// Stores pending hud events from the last frame
pending_hud_events: Rc<RefCell<Vec<HudEvent>>>
}
impl Default for ImgUiState {
fn default() -> Self {
ImgUiState {
give_item_quantity: Rc::new(RefCell::new(1)),
items_list_selected_item: Rc::new(RefCell::new(0)),
pending_hud_events: Rc::new(RefCell::new(Vec::<HudEvent>::new()))
}
}
}
/// Represents an active game session (i.e., the one being played).
@ -86,6 +105,7 @@ impl SessionState {
free_look: false,
auto_walk: false,
is_aiming: false,
imgui_state: ImgUiState::default()
}
}
@ -657,42 +677,44 @@ impl PlayState for SessionState {
.camera_mut()
.compute_dependents(&*self.client.borrow().state().terrain());
let debug_info = DebugInfo {
tps: global_state.clock.get_tps(),
ping_ms: self.client.borrow().get_ping_ms_rolling_avg(),
coordinates: self
.client
.borrow()
.state()
.ecs()
.read_storage::<Pos>()
.get(self.client.borrow().entity())
.cloned(),
velocity: self
.client
.borrow()
.state()
.ecs()
.read_storage::<Vel>()
.get(self.client.borrow().entity())
.cloned(),
ori: self
.client
.borrow()
.state()
.ecs()
.read_storage::<comp::Ori>()
.get(self.client.borrow().entity())
.cloned(),
num_chunks: self.scene.terrain().chunk_count() as u32,
num_chunks_visible: self.scene.terrain().visible_chunk_count() as u32,
num_figures: self.scene.figure_mgr().figure_count() as u32,
num_figures_visible: self.scene.figure_mgr().figure_count_visible() as u32,
};
// Extract HUD events ensuring the client borrow gets dropped.
let mut hud_events = self.hud.maintain(
&self.client.borrow(),
global_state,
DebugInfo {
tps: global_state.clock.get_tps(),
ping_ms: self.client.borrow().get_ping_ms_rolling_avg(),
coordinates: self
.client
.borrow()
.state()
.ecs()
.read_storage::<Pos>()
.get(self.client.borrow().entity())
.cloned(),
velocity: self
.client
.borrow()
.state()
.ecs()
.read_storage::<Vel>()
.get(self.client.borrow().entity())
.cloned(),
ori: self
.client
.borrow()
.state()
.ecs()
.read_storage::<comp::Ori>()
.get(self.client.borrow().entity())
.cloned(),
num_chunks: self.scene.terrain().chunk_count() as u32,
num_visible_chunks: self.scene.terrain().visible_chunk_count() as u32,
num_figures: self.scene.figure_mgr().figure_count() as u32,
num_figures_visible: self.scene.figure_mgr().figure_count_visible() as u32,
},
&debug_info,
&self.scene.camera(),
global_state.clock.get_last_delta(),
HudInfo {
@ -710,20 +732,157 @@ impl PlayState for SessionState {
}
if global_state.settings.gameplay.toggle_debug {
global_state.window.imgui_run_ui = Box::new(|ui| {
Window::new(im_str!("Veloren ImgUi Test"))
.size([300.0, 100.0], Condition::FirstUseEver)
global_state.window.imgui_begin_frame();
global_state.imgui_render_required = true;
// These variables are set outside of the creation of the imgui_run_ui closure below
// to avoid lifetime issues. They are all moved into the closure due to the use of `move` in
// the closure definition.
let loaded_distance = self.client.borrow().loaded_distance();
let is_admin = self.client.borrow().is_admin();
//let ping_deltas = self.client.borrow().ping_deltas.iter().map(|x| *x as f32).collect::<Vec<f32>>();
let version = format!(
"{}-{}",
env!("CARGO_PKG_VERSION"),
common::util::GIT_VERSION.to_string()
);
let time_in_seconds = self.client.borrow().state().get_time_of_day();
let current_time = NaiveTime::from_num_seconds_from_midnight(
// Wraps around back to 0s if it exceeds 24 hours (24 hours = 86400s)
(time_in_seconds as u64 % 86400) as u32,
0,
);
let entity_count = self.client.borrow().state().ecs().entities().join().count();
// Push the hud events generated by the last imgui frame (button clicks etc)
for event in self.imgui_state.pending_hud_events.borrow_mut().drain(..) {
hud_events.push(event);
}
// TODO: Only do this once
let items: Vec<ImString> = common::cmd::ITEM_SPECS.iter().map(|x| ImString::new(x)).collect();
// ImgUi state is handled with Rc<RefCell<T>> because the ImgUi window requires mutable
// access to the values that represent the state of UI controls.
let hud_events = Rc::clone(&self.imgui_state.pending_hud_events);
let items_list_selected_item = Rc::clone(&self.imgui_state.items_list_selected_item);
let give_item_quantity = Rc::clone(&self.imgui_state.give_item_quantity);
global_state.window.imgui_run_ui = Box::new(move |ui| {
ui.show_demo_window(&mut true);
Window::new(im_str!("Debug Info"))
.size([356.0, 322.0], Condition::FirstUseEver)
.position([10.0, 90.0], Condition::FirstUseEver)
.build(ui, || {
ui.text(im_str!("Hello world!"));
ui.text(im_str!("こんにちは世界!"));
ui.text(im_str!("This...is...imgui-rs!"));
ui.label_text(im_str!("Version"), &im_str!("{}", version));
ui.separator();
ui.label_text(im_str!("FPS"), &im_str!("{:.0}", debug_info.tps));
ui.label_text(im_str!("Ping"), &im_str!("{:.0}", debug_info.ping_ms));
// TODO: Ping graph works but looks crap with the current 10 historical values stored
// ui.plot_lines(im_str!("Ping"), &ping_deltas.clone())
// .graph_size([300.0, 75.0])
// .build();
ui.separator();
// Time
ui.label_text(im_str!("Time"), &im_str!("{}", current_time.format("%H:%M").to_string()));
// Player Coords
let coords_text = match debug_info.coordinates {
Some(coordinates) => format!(
"({:.0}, {:.0}, {:.0})",
coordinates.0.x, coordinates.0.y, coordinates.0.z,
),
None => "Player has no Pos component".to_owned(),
};
ui.label_text(im_str!("Coords"), &im_str!("{}", coords_text));
// Player Velocity
let velocity_text = match debug_info.velocity {
Some(velocity) => format!(
"({:.1}, {:.1}, {:.1}) [{:.1} u/s]",
velocity.0.x,
velocity.0.y,
velocity.0.z,
velocity.0.magnitude()
),
None => "Player has no Vel component".to_owned(),
};
ui.label_text(im_str!("Velocity"), &im_str!("{}", velocity_text));
// Player Orientation
let orientation_text = match debug_info.ori {
Some(ori) => format!(
"({:.1}, {:.1}, {:.1})",
ori.0.x, ori.0.y, ori.0.z,
),
None => "Player has no Ori component".to_owned(),
};
ui.label_text(im_str!("Orientation"), &im_str!("{}", orientation_text));
ui.separator();
// View Distance
ui.label_text(im_str!("View Distance"), &im_str!("{:.2} blocks ({:.2} chunks)",
loaded_distance,
loaded_distance / TerrainChunk::RECT_SIZE.x as f32));
// Entities
ui.label_text(im_str!("Entities"), &im_str!("{}", entity_count));
// Chunks
ui.label_text(im_str!("Chunks"), &im_str!("{} ({} visible)", debug_info.num_chunks, debug_info.num_chunks_visible));
// Figures
ui.label_text(im_str!("Figures"), &im_str!("{} ({} visible)", debug_info.num_figures, debug_info.num_figures_visible));
ui.separator();
// Mouse Position
let mouse_pos = ui.io().mouse_pos;
ui.text(format!(
"Mouse Position: ({:.1},{:.1})",
mouse_pos[0], mouse_pos[1]
));
});
if is_admin {
Window::new(im_str!("Admin Commands"))
.collapsed(true, Condition::FirstUseEver)
.size([600.0, 400.0], Condition::FirstUseEver)
.position([400.0, 20.0], Condition::FirstUseEver)
.build(ui, || {
ui.list_box::<ImStr>(&im_str!("##"), &mut items_list_selected_item.borrow_mut(), &items.iter().map(|x| x.as_ref()).collect::<Vec<_>>(), 10);
ui.set_next_item_width(75.0);
ui.input_int(im_str!("Quantity"), &mut give_item_quantity.borrow_mut()).build();
ui.same_line(155.0);
if ui.button(im_str!("Give item"), [70.0, 20.0]) {
if let Some(selected_item) = items.iter().nth(*items_list_selected_item.borrow() as usize) {
let item_name = selected_item.to_str();
hud_events.borrow_mut().push(HudEvent::SendMessage(format!("/give_item {} {}", item_name, give_item_quantity.borrow())));
}
}
if ui.button(im_str!("Explosion!"), [100.0, 20.0]) {
hud_events.borrow_mut().push(HudEvent::SendMessage(format!("/explosion")));
}
if ui.button(im_str!("Set Waypoint"), [100.0, 20.0]) {
hud_events.borrow_mut().push(HudEvent::SendMessage(format!("/waypoint")));
}
if ui.button(im_str!("Spawn Dummy"), [100.0, 20.0]) {
hud_events.borrow_mut().push(HudEvent::SendMessage(format!("/dummy")));
}
});
}
});
}

View File

@ -678,8 +678,10 @@ impl Window {
pub fn fetch_events(&mut self) -> Vec<Event> {
// Refresh ui size (used when changing playstates)
if self.needs_refresh_resize {
let logical_size = self.logical_size();
self.events
.push(Event::Ui(ui::Event::new_resize(self.logical_size())));
.push(Event::Ui(ui::Event::new_resize(logical_size)));
self.imgui.io_mut().display_size = [logical_size.x as f32, logical_size.y as f32];
self.needs_refresh_resize = false;
}