mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'ingame_ui_attempt' into 'master'
Add Health Bars and Name Tags See merge request veloren/veloren!155 Former-commit-id: 8ebb8dd060cc66349829b9e4b335b1f42ae949e5
This commit is contained in:
commit
959945e97f
BIN
assets/voxygen/element/skill_bar/xp_bar2.png
(Stored with Git LFS)
BIN
assets/voxygen/element/skill_bar/xp_bar2.png
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/skill_bar/xp_bar_l.png
(Stored with Git LFS)
BIN
assets/voxygen/element/skill_bar/xp_bar_l.png
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/skill_bar/xp_bar_l_filled.png
(Stored with Git LFS)
BIN
assets/voxygen/element/skill_bar/xp_bar_l_filled.png
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/skill_bar/xp_bar_r.png
(Stored with Git LFS)
BIN
assets/voxygen/element/skill_bar/xp_bar_r.png
(Stored with Git LFS)
Binary file not shown.
@ -1,9 +1,16 @@
|
||||
#version 330 core
|
||||
|
||||
#include <globals.glsl>
|
||||
|
||||
in vec2 f_uv;
|
||||
in vec4 f_color;
|
||||
flat in uint f_mode;
|
||||
|
||||
layout (std140)
|
||||
uniform u_locals {
|
||||
vec4 w_pos;
|
||||
};
|
||||
|
||||
uniform sampler2D u_tex;
|
||||
|
||||
out vec4 tgt_color;
|
||||
|
@ -1,10 +1,17 @@
|
||||
#version 330 core
|
||||
|
||||
#include <globals.glsl>
|
||||
|
||||
in vec2 v_pos;
|
||||
in vec2 v_uv;
|
||||
in vec4 v_color;
|
||||
in uint v_mode;
|
||||
|
||||
layout (std140)
|
||||
uniform u_locals {
|
||||
vec4 w_pos;
|
||||
};
|
||||
|
||||
uniform sampler2D u_tex;
|
||||
|
||||
out vec2 f_uv;
|
||||
@ -14,6 +21,13 @@ out vec4 f_color;
|
||||
void main() {
|
||||
f_uv = v_uv;
|
||||
f_color = v_color;
|
||||
gl_Position = vec4(v_pos, 0.0, 1.0);
|
||||
|
||||
if (w_pos.w == 1.0) {
|
||||
// In-game element
|
||||
gl_Position = proj_mat * (view_mat * w_pos + vec4(v_pos, 0.0, 0.0));
|
||||
} else {
|
||||
// Interface element
|
||||
gl_Position = vec4(v_pos, 0.0, 1.0);
|
||||
}
|
||||
f_mode = v_mode;
|
||||
}
|
||||
|
@ -23,18 +23,23 @@ use skillbar::Skillbar;
|
||||
use small_window::{SmallWindow, SmallWindowType};
|
||||
|
||||
use crate::{
|
||||
render::Renderer,
|
||||
render::{Consts, Globals, Renderer},
|
||||
scene::camera::Camera,
|
||||
settings::{ControlSettings, Settings},
|
||||
ui::{ScaleMode, Ui},
|
||||
ui::{Ingame, Ingameable, ScaleMode, Ui},
|
||||
window::{Event as WinEvent, Key, Window},
|
||||
GlobalState,
|
||||
};
|
||||
use client::Client;
|
||||
use common::comp;
|
||||
use conrod_core::{
|
||||
color, graph,
|
||||
widget::{self, Button, Image, Rectangle, Text},
|
||||
widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget,
|
||||
};
|
||||
use specs::Join;
|
||||
use std::collections::VecDeque;
|
||||
use vek::*;
|
||||
|
||||
const XP_COLOR: Color = Color::Rgba(0.59, 0.41, 0.67, 1.0);
|
||||
const TEXT_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0);
|
||||
@ -43,6 +48,12 @@ const MANA_COLOR: Color = Color::Rgba(0.42, 0.41, 0.66, 1.0);
|
||||
|
||||
widget_ids! {
|
||||
struct Ids {
|
||||
// Character Names
|
||||
name_tags[],
|
||||
// Health Bars
|
||||
health_bars[],
|
||||
health_bar_backs[],
|
||||
|
||||
// Test
|
||||
bag_space_add,
|
||||
// Debug
|
||||
@ -124,6 +135,7 @@ pub struct Show {
|
||||
map: bool,
|
||||
inventory_test_button: bool,
|
||||
mini_map: bool,
|
||||
ingame: bool,
|
||||
|
||||
want_grab: bool,
|
||||
}
|
||||
@ -247,13 +259,19 @@ impl Hud {
|
||||
inventory_test_button: false,
|
||||
mini_map: false,
|
||||
want_grab: true,
|
||||
ingame: true,
|
||||
},
|
||||
to_focus: None,
|
||||
force_ungrab: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn update_layout(&mut self, global_state: &GlobalState, debug_info: DebugInfo) -> Vec<Event> {
|
||||
fn update_layout(
|
||||
&mut self,
|
||||
client: &Client,
|
||||
global_state: &GlobalState,
|
||||
debug_info: DebugInfo,
|
||||
) -> Vec<Event> {
|
||||
let mut events = Vec::new();
|
||||
let ref mut ui_widgets = self.ui.set_widgets();
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
@ -263,6 +281,76 @@ impl Hud {
|
||||
return events;
|
||||
}
|
||||
|
||||
// Nametags and healthbars
|
||||
if self.show.ingame {
|
||||
let ecs = client.state().ecs();
|
||||
let actor = ecs.read_storage::<comp::Actor>();
|
||||
let pos = ecs.read_storage::<comp::phys::Pos>();
|
||||
let stats = ecs.read_storage::<comp::Stats>();
|
||||
let entities = ecs.entities();
|
||||
let player = client.entity();
|
||||
let mut name_id_walker = self.ids.name_tags.walk();
|
||||
let mut health_id_walker = self.ids.health_bars.walk();
|
||||
let mut health_back_id_walker = self.ids.health_bar_backs.walk();
|
||||
for (pos, name) in
|
||||
(&entities, &pos, &actor)
|
||||
.join()
|
||||
.filter_map(|(entity, pos, actor)| match actor {
|
||||
comp::Actor::Character { name, .. } if entity != player => {
|
||||
Some((pos.0, name))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
let id = name_id_walker.next(
|
||||
&mut self.ids.name_tags,
|
||||
&mut ui_widgets.widget_id_generator(),
|
||||
);
|
||||
Text::new(&name)
|
||||
.font_size(20)
|
||||
.color(Color::Rgba(1.0, 1.0, 1.0, 1.0))
|
||||
.x_y(0.0, 0.0)
|
||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 3.0))
|
||||
.resolution(100.0)
|
||||
.set(id, ui_widgets);
|
||||
}
|
||||
for (pos, hp) in (&entities, &pos, &stats)
|
||||
.join()
|
||||
.filter_map(|(entity, pos, stats)| {
|
||||
if entity != player {
|
||||
Some((pos.0, stats.hp))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
{
|
||||
let back_id = health_back_id_walker.next(
|
||||
&mut self.ids.health_bar_backs,
|
||||
&mut ui_widgets.widget_id_generator(),
|
||||
);
|
||||
let bar_id = health_id_walker.next(
|
||||
&mut self.ids.health_bars,
|
||||
&mut ui_widgets.widget_id_generator(),
|
||||
);
|
||||
// Healh Bar
|
||||
Rectangle::fill_with([120.0, 8.0], Color::Rgba(0.3, 0.3, 0.3, 0.5))
|
||||
.x_y(0.0, -25.0)
|
||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 3.0))
|
||||
.resolution(100.0)
|
||||
.set(back_id, ui_widgets);
|
||||
|
||||
// Filling
|
||||
Rectangle::fill_with(
|
||||
[120.0 * (hp.current as f64 / hp.maximum as f64), 8.0],
|
||||
HP_COLOR,
|
||||
)
|
||||
.x_y(0.0, -25.0)
|
||||
.position_ingame(pos + Vec3::new(0.0, 0.0, 3.0))
|
||||
.resolution(100.0)
|
||||
.set(bar_id, ui_widgets);
|
||||
}
|
||||
}
|
||||
|
||||
// Display debug window.
|
||||
if self.show.debug {
|
||||
// Alpha Version
|
||||
@ -362,7 +450,15 @@ impl Hud {
|
||||
}
|
||||
|
||||
// Skillbar
|
||||
Skillbar::new(&self.imgs, &self.fonts).set(self.ids.skillbar, ui_widgets);
|
||||
// Get player stats
|
||||
let stats = client
|
||||
.state()
|
||||
.ecs()
|
||||
.read_storage::<comp::Stats>()
|
||||
.get(client.entity())
|
||||
.map(|&s| s)
|
||||
.unwrap_or_default();
|
||||
Skillbar::new(&self.imgs, &self.fonts, stats).set(self.ids.skillbar, ui_widgets);
|
||||
|
||||
// Chat box
|
||||
match Chat::new(&mut self.new_messages, &self.imgs, &self.fonts)
|
||||
@ -474,7 +570,9 @@ impl Hud {
|
||||
self.ui
|
||||
.widget_graph()
|
||||
.widget(id)
|
||||
.and_then(graph::Container::unique_widget_state::<widget::TextEdit>)
|
||||
.filter(|c| {
|
||||
c.type_id == std::any::TypeId::of::<<widget::TextEdit as Widget>::State>()
|
||||
})
|
||||
.is_some()
|
||||
} else {
|
||||
false
|
||||
@ -559,6 +657,10 @@ impl Hud {
|
||||
self.show.debug = !self.show.debug;
|
||||
true
|
||||
}
|
||||
Key::ToggleIngameUi => {
|
||||
self.show.ingame = !self.show.ingame;
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
WinEvent::KeyDown(key) | WinEvent::KeyUp(key) => match key {
|
||||
@ -580,19 +682,26 @@ impl Hud {
|
||||
|
||||
pub fn maintain(
|
||||
&mut self,
|
||||
client: &Client,
|
||||
global_state: &mut GlobalState,
|
||||
debug_info: DebugInfo,
|
||||
camera: &Camera,
|
||||
) -> Vec<Event> {
|
||||
if let Some(maybe_id) = self.to_focus.take() {
|
||||
self.ui.focus_widget(maybe_id);
|
||||
}
|
||||
let events = self.update_layout(&global_state, debug_info);
|
||||
self.ui.maintain(&mut global_state.window.renderer_mut());
|
||||
let events = self.update_layout(client, global_state, debug_info);
|
||||
let (view_mat, _, _) = camera.compute_dependents(client);
|
||||
let fov = camera.get_fov();
|
||||
self.ui.maintain(
|
||||
&mut global_state.window.renderer_mut(),
|
||||
Some((view_mat, fov)),
|
||||
);
|
||||
events
|
||||
}
|
||||
|
||||
pub fn render(&self, renderer: &mut Renderer) {
|
||||
self.ui.render(renderer);
|
||||
pub fn render(&self, renderer: &mut Renderer, globals: &Consts<Globals>) {
|
||||
self.ui.render(renderer, Some(globals));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
use super::{img_ids::Imgs, Fonts, HP_COLOR, MANA_COLOR, TEXT_COLOR, XP_COLOR};
|
||||
use common::comp::Stats;
|
||||
use conrod_core::{
|
||||
widget::{self, Image, Rectangle, Text},
|
||||
widget_ids, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
@ -30,15 +31,18 @@ pub struct Skillbar<'a> {
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a Fonts,
|
||||
|
||||
stats: Stats,
|
||||
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
}
|
||||
|
||||
impl<'a> Skillbar<'a> {
|
||||
pub fn new(imgs: &'a Imgs, fonts: &'a Fonts) -> Self {
|
||||
pub fn new(imgs: &'a Imgs, fonts: &'a Fonts, stats: Stats) -> Self {
|
||||
Self {
|
||||
imgs,
|
||||
fonts,
|
||||
stats,
|
||||
common: widget::CommonBuilder::default(),
|
||||
}
|
||||
}
|
||||
@ -68,9 +72,13 @@ impl<'a> Widget for Skillbar<'a> {
|
||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||
let widget::UpdateArgs { state, ui, .. } = args;
|
||||
|
||||
// TODO: Read from parameter/character struct
|
||||
let xp_percentage = 0.4;
|
||||
let hp_percentage = 1.0;
|
||||
// TODO: remove this
|
||||
let level = (self.stats.xp as f64).log(4.0).trunc() as u32 + 1;
|
||||
let start_level_xp = ((level - 1) as f64).powi(4);
|
||||
let next_level_xp = (level as f64).powi(4) - start_level_xp;
|
||||
// TODO: We need a max xp value
|
||||
let xp_percentage = (self.stats.xp as f64 - start_level_xp) / next_level_xp;
|
||||
let hp_percentage = self.stats.hp.current as f64 / self.stats.hp.maximum as f64;
|
||||
let mana_percentage = 1.0;
|
||||
|
||||
// TODO: Only show while aiming with a bow or when casting a spell.
|
||||
@ -82,7 +90,7 @@ impl<'a> Widget for Skillbar<'a> {
|
||||
|
||||
// Experience-Bar
|
||||
Image::new(self.imgs.xp_bar)
|
||||
.w_h(2688.0 / 6.0, 116.0 / 6.0)
|
||||
.w_h(672.0 / 1.5, 29.0 / 1.5)
|
||||
.mid_bottom_of(ui.window)
|
||||
.set(state.ids.xp_bar, ui);
|
||||
|
||||
@ -92,37 +100,37 @@ impl<'a> Widget for Skillbar<'a> {
|
||||
|
||||
// Left Grid
|
||||
Image::new(self.imgs.sb_grid)
|
||||
.w_h(2240.0 / 12.0, 448.0 / 12.0)
|
||||
.w_h(280.0 / 1.5, 56.0 / 1.5)
|
||||
.up_from(state.ids.xp_bar, 0.0)
|
||||
.align_left_of(state.ids.xp_bar)
|
||||
.set(state.ids.sb_grid_l, ui);
|
||||
|
||||
Image::new(self.imgs.sb_grid_bg)
|
||||
.w_h(2240.0 / 12.0, 448.0 / 12.0)
|
||||
.w_h(280.0 / 1.5, 56.0 / 1.5)
|
||||
.middle_of(state.ids.sb_grid_l)
|
||||
.set(state.ids.sb_grid_bg_l, ui);
|
||||
|
||||
// Right Grid
|
||||
Image::new(self.imgs.sb_grid)
|
||||
.w_h(2240.0 / 12.0, 448.0 / 12.0)
|
||||
.w_h(280.0 / 1.5, 56.0 / 1.5)
|
||||
.up_from(state.ids.xp_bar, 0.0)
|
||||
.align_right_of(state.ids.xp_bar)
|
||||
.set(state.ids.sb_grid_r, ui);
|
||||
|
||||
Image::new(self.imgs.sb_grid_bg)
|
||||
.w_h(2240.0 / 12.0, 448.0 / 12.0)
|
||||
.w_h(280.0 / 1.5, 56.0 / 1.5)
|
||||
.middle_of(state.ids.sb_grid_r)
|
||||
.set(state.ids.sb_grid_bg_r, ui);
|
||||
|
||||
// Right and Left Click
|
||||
Image::new(self.imgs.l_click)
|
||||
.w_h(224.0 / 6.0, 320.0 / 6.0)
|
||||
.w_h(56.0 / 1.5, 80.0 / 1.5)
|
||||
.right_from(state.ids.sb_grid_bg_l, 0.0)
|
||||
.align_bottom_of(state.ids.sb_grid_bg_l)
|
||||
.set(state.ids.l_click, ui);
|
||||
|
||||
Image::new(self.imgs.r_click)
|
||||
.w_h(224.0 / 6.0, 320.0 / 6.0)
|
||||
.w_h(56.0 / 1.5, 80.0 / 1.5)
|
||||
.left_from(state.ids.sb_grid_bg_r, 0.0)
|
||||
.align_bottom_of(state.ids.sb_grid_bg_r)
|
||||
.set(state.ids.r_click, ui);
|
||||
@ -135,7 +143,7 @@ impl<'a> Widget for Skillbar<'a> {
|
||||
.set(state.ids.health_bar, ui);
|
||||
|
||||
// Filling
|
||||
Rectangle::fill_with([182.0 * (hp_percentage), 6.0], HP_COLOR) // "W=182.0 * [Health. %]"
|
||||
Rectangle::fill_with([182.0 * hp_percentage, 6.0], HP_COLOR) // "W=182.0 * [Health. %]"
|
||||
.top_right_with_margins_on(state.ids.health_bar, 5.0, 0.0)
|
||||
.set(state.ids.health_bar_color, ui);
|
||||
|
||||
@ -147,7 +155,7 @@ impl<'a> Widget for Skillbar<'a> {
|
||||
.set(state.ids.mana_bar, ui);
|
||||
|
||||
// Filling
|
||||
Rectangle::fill_with([182.0 * (mana_percentage), 6.0], MANA_COLOR) // "W=182.0 * [Mana. %]"
|
||||
Rectangle::fill_with([182.0 * mana_percentage, 6.0], MANA_COLOR) // "W=182.0 * [Mana. %]"
|
||||
.top_left_with_margins_on(state.ids.mana_bar, 5.0, 0.0)
|
||||
.set(state.ids.mana_bar_color, ui);
|
||||
|
||||
@ -159,15 +167,16 @@ impl<'a> Widget for Skillbar<'a> {
|
||||
|
||||
// Level Display
|
||||
|
||||
// TODO: don't construct a new string here
|
||||
// TODO: Insert actual Level here.
|
||||
Text::new("1")
|
||||
Text::new(&level.to_string())
|
||||
.left_from(state.ids.xp_bar, -15.0)
|
||||
.font_size(10)
|
||||
.color(TEXT_COLOR)
|
||||
.set(state.ids.level_text, ui);
|
||||
|
||||
// TODO: Insert next Level here.
|
||||
Text::new("2")
|
||||
Text::new(&(level + 1).to_string())
|
||||
.right_from(state.ids.xp_bar, -15.0)
|
||||
.font_size(10)
|
||||
.color(TEXT_COLOR)
|
||||
|
@ -106,7 +106,7 @@ impl PlayState for CharSelectionState {
|
||||
|
||||
// Draw the UI to the screen.
|
||||
self.char_selection_ui
|
||||
.render(global_state.window.renderer_mut());
|
||||
.render(global_state.window.renderer_mut(), self.scene.globals());
|
||||
|
||||
// Tick the client (currently only to keep the connection alive).
|
||||
self.client
|
||||
|
@ -72,6 +72,10 @@ impl Scene {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn globals(&self) -> &Consts<Globals> {
|
||||
&self.globals
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) {
|
||||
self.camera.set_focus_pos(Vec3::unit_z() * 2.0);
|
||||
self.camera.update(client.state().get_time());
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
render::Renderer,
|
||||
render::{Consts, Globals, Renderer},
|
||||
ui::{
|
||||
self,
|
||||
img_ids::{ImageGraphic, VoxelGraphic},
|
||||
@ -1081,11 +1081,11 @@ impl CharSelectionUi {
|
||||
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer) -> Vec<Event> {
|
||||
let events = self.update_layout();
|
||||
self.ui.maintain(renderer);
|
||||
self.ui.maintain(renderer, None);
|
||||
events
|
||||
}
|
||||
|
||||
pub fn render(&self, renderer: &mut Renderer) {
|
||||
self.ui.render(renderer);
|
||||
pub fn render(&self, renderer: &mut Renderer, globals: &Consts<Globals>) {
|
||||
self.ui.render(renderer, Some(globals));
|
||||
}
|
||||
}
|
||||
|
@ -508,11 +508,11 @@ impl MainMenuUi {
|
||||
|
||||
pub fn maintain(&mut self, global_state: &mut GlobalState) -> Vec<Event> {
|
||||
let events = self.update_layout(global_state);
|
||||
self.ui.maintain(global_state.window.renderer_mut());
|
||||
self.ui.maintain(global_state.window.renderer_mut(), None);
|
||||
events
|
||||
}
|
||||
|
||||
pub fn render(&self, renderer: &mut Renderer) {
|
||||
self.ui.render(renderer);
|
||||
self.ui.render(renderer, None);
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,8 @@ pub use self::{
|
||||
skybox::{create_mesh as create_skybox_mesh, Locals as SkyboxLocals, SkyboxPipeline},
|
||||
terrain::{Locals as TerrainLocals, TerrainPipeline},
|
||||
ui::{
|
||||
create_quad as create_ui_quad, create_tri as create_ui_tri, Mode as UiMode, UiPipeline,
|
||||
create_quad as create_ui_quad, create_tri as create_ui_tri, Locals as UiLocals,
|
||||
Mode as UiMode, UiPipeline,
|
||||
},
|
||||
Globals,
|
||||
},
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::super::{Pipeline, Quad, Tri, WinColorFmt, WinDepthFmt};
|
||||
use super::super::{Globals, Pipeline, Quad, Tri, WinColorFmt, WinDepthFmt};
|
||||
use gfx::{
|
||||
self,
|
||||
gfx_constant_struct_meta,
|
||||
// Macros
|
||||
gfx_defines,
|
||||
gfx_impl_struct_meta,
|
||||
@ -18,15 +19,21 @@ gfx_defines! {
|
||||
mode: u32 = "v_mode",
|
||||
}
|
||||
|
||||
constant Locals {
|
||||
pos: [f32; 4] = "w_pos",
|
||||
}
|
||||
|
||||
pipeline pipe {
|
||||
vbuf: gfx::VertexBuffer<Vertex> = (),
|
||||
|
||||
locals: gfx::ConstantBuffer<Locals> = "u_locals",
|
||||
globals: gfx::ConstantBuffer<Globals> = "u_globals",
|
||||
tex: gfx::TextureSampler<[f32; 4]> = "u_tex",
|
||||
|
||||
scissor: gfx::Scissor = (),
|
||||
|
||||
tgt_color: gfx::BlendTarget<WinColorFmt> = ("tgt_color", gfx::state::ColorMask::all(), gfx::preset::blend::ALPHA),
|
||||
tgt_depth: gfx::DepthTarget<WinDepthFmt> = gfx::preset::depth::PASS_TEST,
|
||||
tgt_depth: gfx::DepthTarget<WinDepthFmt> = gfx::preset::depth::LESS_EQUAL_TEST,
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,6 +43,20 @@ impl Pipeline for UiPipeline {
|
||||
type Vertex = Vertex;
|
||||
}
|
||||
|
||||
impl From<Vec3<f32>> for Locals {
|
||||
fn from(pos: Vec3<f32>) -> Self {
|
||||
Self {
|
||||
pos: [pos[0], pos[1], pos[2], 1.0],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Locals {
|
||||
fn default() -> Self {
|
||||
Self { pos: [0.0; 4] }
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw text from the text cache texture `tex` in the fragment shader.
|
||||
pub const MODE_TEXT: u32 = 0;
|
||||
/// Draw an image from the texture at `tex` in the fragment shader.
|
||||
|
@ -314,7 +314,7 @@ impl Renderer {
|
||||
memory::Typed,
|
||||
};
|
||||
type WinSurfaceData = <<WinColorFmt as Formatted>::Surface as SurfaceTyped>::DataType;
|
||||
let mut download = self
|
||||
let download = self
|
||||
.factory
|
||||
.create_download_buffer::<WinSurfaceData>(width as usize * height as usize)
|
||||
.map_err(|err| RenderError::BufferCreationError(err))?;
|
||||
@ -423,6 +423,8 @@ impl Renderer {
|
||||
model: &Model<ui::UiPipeline>,
|
||||
tex: &Texture<ui::UiPipeline>,
|
||||
scissor: Aabr<u16>,
|
||||
globals: &Consts<Globals>,
|
||||
locals: &Consts<ui::Locals>,
|
||||
) {
|
||||
let Aabr { min, max } = scissor;
|
||||
self.encoder.draw(
|
||||
@ -437,6 +439,8 @@ impl Renderer {
|
||||
h: max.y - min.y,
|
||||
},
|
||||
tex: (tex.srv.clone(), tex.sampler.clone()),
|
||||
locals: locals.buf.clone(),
|
||||
globals: globals.buf.clone(),
|
||||
tgt_color: self.win_color_view.clone(),
|
||||
tgt_depth: self.win_depth_view.clone(),
|
||||
},
|
||||
|
@ -150,4 +150,9 @@ impl Camera {
|
||||
pub fn get_orientation(&self) -> Vec3<f32> {
|
||||
self.ori
|
||||
}
|
||||
|
||||
/// Get the field of view of the camera in radians.
|
||||
pub fn get_fov(&self) -> f32 {
|
||||
self.fov
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +68,11 @@ impl Scene {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a reference to the scene's globals
|
||||
pub fn globals(&self) -> &Consts<Globals> {
|
||||
&self.globals
|
||||
}
|
||||
|
||||
/// Get a reference to the scene's camera.
|
||||
pub fn camera(&self) -> &Camera {
|
||||
&self.camera
|
||||
|
@ -100,7 +100,7 @@ impl SessionState {
|
||||
// Render the screen using the global renderer
|
||||
self.scene.render(renderer, &mut self.client.borrow_mut());
|
||||
// Draw the UI to the screen
|
||||
self.hud.render(renderer);
|
||||
self.hud.render(renderer, self.scene.globals());
|
||||
|
||||
// Finish the frame
|
||||
renderer.flush();
|
||||
@ -171,18 +171,18 @@ impl PlayState for SessionState {
|
||||
global_state.maintain();
|
||||
|
||||
// Maintain the scene.
|
||||
self.scene.maintain(
|
||||
global_state.window.renderer_mut(),
|
||||
&self.client.borrow_mut(),
|
||||
);
|
||||
self.scene
|
||||
.maintain(global_state.window.renderer_mut(), &self.client.borrow());
|
||||
|
||||
// extract HUD events ensuring the client borrow gets dropped
|
||||
let hud_events = self.hud.maintain(
|
||||
&self.client.borrow(),
|
||||
global_state,
|
||||
DebugInfo {
|
||||
tps: clock.get_tps(),
|
||||
ping_ms: self.client.borrow().get_ping_ms(),
|
||||
},
|
||||
&self.scene.camera(),
|
||||
);
|
||||
// Maintain the UI.
|
||||
for event in hud_events {
|
||||
@ -215,6 +215,7 @@ impl PlayState for SessionState {
|
||||
}
|
||||
}
|
||||
}
|
||||
{}
|
||||
|
||||
// Render the session.
|
||||
self.render(global_state.window.renderer_mut());
|
||||
|
@ -40,6 +40,7 @@ pub struct ControlSettings {
|
||||
pub toggle_debug: VirtualKeyCode,
|
||||
pub fullscreen: VirtualKeyCode,
|
||||
pub screenshot: VirtualKeyCode,
|
||||
pub toggle_ingame_ui: VirtualKeyCode,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
@ -95,6 +96,7 @@ impl Default for Settings {
|
||||
toggle_debug: VirtualKeyCode::F3,
|
||||
fullscreen: VirtualKeyCode::F11,
|
||||
screenshot: VirtualKeyCode::F4,
|
||||
toggle_ingame_ui: VirtualKeyCode::F6,
|
||||
},
|
||||
networking: NetworkingSettings {
|
||||
username: "Username".to_string(),
|
||||
|
@ -6,7 +6,6 @@ pub struct Event(pub Input);
|
||||
impl Event {
|
||||
pub fn try_from(event: glutin::Event, window: &glutin::GlWindow) -> Option<Self> {
|
||||
use conrod_winit::*;
|
||||
use winit;
|
||||
// A wrapper around the winit window that allows us to implement the trait necessary for
|
||||
// enabling the winit <-> conrod conversion functions.
|
||||
struct WindowRef<'a>(&'a winit::Window);
|
||||
|
@ -12,13 +12,18 @@ mod font_ids;
|
||||
pub use event::Event;
|
||||
pub use graphic::Graphic;
|
||||
pub use scale::ScaleMode;
|
||||
pub use widgets::{image_slider::ImageSlider, toggle_button::ToggleButton};
|
||||
pub use widgets::{
|
||||
image_slider::ImageSlider,
|
||||
ingame::{Ingame, IngameAnchor, Ingameable},
|
||||
toggle_button::ToggleButton,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
render::{
|
||||
create_ui_quad, create_ui_tri, DynamicModel, Mesh, RenderError, Renderer, UiMode,
|
||||
UiPipeline,
|
||||
create_ui_quad, create_ui_tri, Consts, DynamicModel, Globals, Mesh, RenderError, Renderer,
|
||||
UiLocals, UiMode, UiPipeline,
|
||||
},
|
||||
scene::camera::Camera,
|
||||
window::Window,
|
||||
Error,
|
||||
};
|
||||
@ -27,12 +32,12 @@ use common::assets;
|
||||
use conrod_core::{
|
||||
event::Input,
|
||||
graph::Graph,
|
||||
image::{Id as ImgId, Map},
|
||||
image::{self, Map},
|
||||
input::{touch::Touch, Motion, Widget},
|
||||
render::Primitive,
|
||||
render::{Primitive, PrimitiveKind},
|
||||
text::{self, font},
|
||||
widget::{id::Generator, Id as WidgId},
|
||||
Ui as CrUi, UiBuilder, UiCell,
|
||||
widget::{self, id::Generator},
|
||||
Rect, UiBuilder, UiCell,
|
||||
};
|
||||
use graphic::Id as GraphicId;
|
||||
use scale::Scale;
|
||||
@ -55,6 +60,7 @@ enum DrawKind {
|
||||
enum DrawCommand {
|
||||
Draw { kind: DrawKind, verts: Range<usize> },
|
||||
Scissor(Aabr<u16>),
|
||||
WorldPos(Option<Consts<UiLocals>>),
|
||||
}
|
||||
impl DrawCommand {
|
||||
fn image(verts: Range<usize>) -> DrawCommand {
|
||||
@ -81,13 +87,16 @@ impl assets::Asset for Font {
|
||||
}
|
||||
|
||||
pub struct Ui {
|
||||
ui: CrUi,
|
||||
ui: conrod_core::Ui,
|
||||
image_map: Map<GraphicId>,
|
||||
cache: Cache,
|
||||
// Draw commands for the next render
|
||||
draw_commands: Vec<DrawCommand>,
|
||||
// Model for drawing the ui
|
||||
model: DynamicModel<UiPipeline>,
|
||||
// Consts for default ui drawing position (ie the interface)
|
||||
interface_locals: Consts<UiLocals>,
|
||||
default_globals: Consts<Globals>,
|
||||
// Window size for updating scaling
|
||||
window_resized: Option<Vec2<f64>>,
|
||||
// Scaling of the ui
|
||||
@ -99,12 +108,16 @@ impl Ui {
|
||||
let scale = Scale::new(window, ScaleMode::Absolute(1.0));
|
||||
let win_dims = scale.scaled_window_size().into_array();
|
||||
|
||||
let mut renderer = window.renderer_mut();
|
||||
|
||||
Ok(Self {
|
||||
ui: UiBuilder::new(win_dims).build(),
|
||||
image_map: Map::new(),
|
||||
cache: Cache::new(window.renderer_mut())?,
|
||||
cache: Cache::new(renderer)?,
|
||||
draw_commands: vec![],
|
||||
model: window.renderer_mut().create_dynamic_model(100)?,
|
||||
model: renderer.create_dynamic_model(100)?,
|
||||
interface_locals: renderer.create_consts(&[UiLocals::default()])?,
|
||||
default_globals: renderer.create_consts(&[Globals::default()])?,
|
||||
window_resized: None,
|
||||
scale,
|
||||
})
|
||||
@ -118,7 +131,7 @@ impl Ui {
|
||||
self.ui.handle_event(Input::Resize(w, h));
|
||||
}
|
||||
|
||||
pub fn add_graphic(&mut self, graphic: Graphic) -> ImgId {
|
||||
pub fn add_graphic(&mut self, graphic: Graphic) -> image::Id {
|
||||
self.image_map.insert(self.cache.add_graphic(graphic))
|
||||
}
|
||||
|
||||
@ -135,7 +148,7 @@ impl Ui {
|
||||
}
|
||||
|
||||
// Accepts Option so widget can be unfocused.
|
||||
pub fn focus_widget(&mut self, id: Option<WidgId>) {
|
||||
pub fn focus_widget(&mut self, id: Option<widget::Id>) {
|
||||
self.ui.keyboard_capture(match id {
|
||||
Some(id) => id,
|
||||
None => self.ui.window,
|
||||
@ -143,7 +156,7 @@ impl Ui {
|
||||
}
|
||||
|
||||
// Get id of current widget capturing keyboard.
|
||||
pub fn widget_capturing_keyboard(&self) -> Option<WidgId> {
|
||||
pub fn widget_capturing_keyboard(&self) -> Option<widget::Id> {
|
||||
self.ui.global_input().current.widget_capturing_keyboard
|
||||
}
|
||||
|
||||
@ -189,11 +202,11 @@ impl Ui {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn widget_input(&self, id: WidgId) -> Widget {
|
||||
pub fn widget_input(&self, id: widget::Id) -> Widget {
|
||||
self.ui.widget_input(id)
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer) {
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer, cam_params: Option<(Mat4<f32>, f32)>) {
|
||||
// Regenerate draw commands and associated models only if the ui changed
|
||||
let mut primitives = match self.ui.draw_if_changed() {
|
||||
Some(primitives) => primitives,
|
||||
@ -216,6 +229,16 @@ impl Ui {
|
||||
let window_scissor = default_scissor(renderer);
|
||||
let mut current_scissor = window_scissor;
|
||||
|
||||
enum Placement {
|
||||
Interface,
|
||||
// Number and resolution
|
||||
InWorld(usize, Option<f32>),
|
||||
};
|
||||
|
||||
let mut placement = Placement::Interface;
|
||||
// TODO: maybe mutate an ingame scale factor instead of this, depends on if we want them to scale with other ui scaling or not
|
||||
let mut p_scale_factor = self.scale.scale_factor_physical();
|
||||
|
||||
// Switches to the `Plain` state and completes the previous `Command` if not already in the
|
||||
// `Plain` state.
|
||||
macro_rules! switch_to_plain_state {
|
||||
@ -223,14 +246,12 @@ impl Ui {
|
||||
if let State::Image = current_state {
|
||||
self.draw_commands
|
||||
.push(DrawCommand::image(start..mesh.vertices().len()));
|
||||
current_state = State::Plain;
|
||||
start = mesh.vertices().len();
|
||||
current_state = State::Plain;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let p_scale_factor = self.scale.scale_factor_physical();
|
||||
|
||||
while let Some(prim) = primitives.next() {
|
||||
let Primitive {
|
||||
kind,
|
||||
@ -242,6 +263,7 @@ impl Ui {
|
||||
// Check for a change in the scissor.
|
||||
let new_scissor = {
|
||||
let (l, b, w, h) = scizzor.l_b_w_h();
|
||||
let scale_factor = self.scale.scale_factor_physical();
|
||||
// Calculate minimum x and y coordinates while
|
||||
// flipping y axis (from +up to +down) and
|
||||
// moving origin to top-left corner (from middle).
|
||||
@ -249,12 +271,12 @@ impl Ui {
|
||||
let min_y = self.ui.win_h / 2.0 - b - h;
|
||||
Aabr {
|
||||
min: Vec2 {
|
||||
x: (min_x * p_scale_factor) as u16,
|
||||
y: (min_y * p_scale_factor) as u16,
|
||||
x: (min_x * scale_factor) as u16,
|
||||
y: (min_y * scale_factor) as u16,
|
||||
},
|
||||
max: Vec2 {
|
||||
x: ((min_x + w) * p_scale_factor) as u16,
|
||||
y: ((min_y + h) * p_scale_factor) as u16,
|
||||
x: ((min_x + w) * scale_factor) as u16,
|
||||
y: ((min_y + h) * scale_factor) as u16,
|
||||
},
|
||||
}
|
||||
.intersection(window_scissor)
|
||||
@ -272,12 +294,41 @@ impl Ui {
|
||||
self.draw_commands.push(DrawCommand::Scissor(new_scissor));
|
||||
}
|
||||
|
||||
match placement {
|
||||
Placement::InWorld(0, _) => {
|
||||
placement = Placement::Interface;
|
||||
p_scale_factor = self.scale.scale_factor_physical();
|
||||
// Finish current state
|
||||
self.draw_commands.push(match current_state {
|
||||
State::Plain => DrawCommand::plain(start..mesh.vertices().len()),
|
||||
State::Image => DrawCommand::image(start..mesh.vertices().len()),
|
||||
});
|
||||
start = mesh.vertices().len();
|
||||
// Push new position command
|
||||
self.draw_commands.push(DrawCommand::WorldPos(None));
|
||||
}
|
||||
Placement::InWorld(n, res) => match kind {
|
||||
// Other types don't need to be drawn in the game
|
||||
PrimitiveKind::Other(_) => {}
|
||||
_ => {
|
||||
placement = Placement::InWorld(n - 1, res);
|
||||
if res.is_none() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
},
|
||||
Placement::Interface => {}
|
||||
}
|
||||
|
||||
// Functions for converting for conrod scalar coords to GL vertex coords (-1.0 to 1.0).
|
||||
let ui_win_w = self.ui.win_w;
|
||||
let ui_win_h = self.ui.win_h;
|
||||
let (ui_win_w, ui_win_h) = if let Placement::InWorld(_, Some(res)) = placement {
|
||||
(res as f64, res as f64)
|
||||
} else {
|
||||
(self.ui.win_w, self.ui.win_h)
|
||||
};
|
||||
let vx = |x: f64| (x / ui_win_w * 2.0) as f32;
|
||||
let vy = |y: f64| (y / ui_win_h * 2.0) as f32;
|
||||
let gl_aabr = |rect: conrod_core::Rect| {
|
||||
let gl_aabr = |rect: Rect| {
|
||||
let (l, r, b, t) = rect.l_r_b_t();
|
||||
Aabr {
|
||||
min: Vec2::new(vx(l), vy(b)),
|
||||
@ -285,7 +336,6 @@ impl Ui {
|
||||
}
|
||||
};
|
||||
|
||||
use conrod_core::render::PrimitiveKind;
|
||||
match kind {
|
||||
PrimitiveKind::Image {
|
||||
image_id,
|
||||
@ -321,16 +371,17 @@ impl Ui {
|
||||
// Transform the source rectangle into uv coordinate.
|
||||
// TODO: Make sure this is right.
|
||||
let source_aabr = {
|
||||
let (uv_l, uv_r, uv_b, uv_t) = (0.0, 1.0, 0.0, 1.0); /*match source_rect {
|
||||
Some(src_rect) => {
|
||||
let (l, r, b, t) = src_rect.l_r_b_t();
|
||||
((l / image_w) as f32,
|
||||
(r / image_w) as f32,
|
||||
(b / image_h) as f32,
|
||||
(t / image_h) as f32)
|
||||
}
|
||||
None => (0.0, 1.0, 0.0, 1.0),
|
||||
};*/
|
||||
let (uv_l, uv_r, uv_b, uv_t) = (0.0, 1.0, 0.0, 1.0);
|
||||
/*match source_rect {
|
||||
Some(src_rect) => {
|
||||
let (l, r, b, t) = src_rect.l_r_b_t();
|
||||
((l / image_w) as f32,
|
||||
(r / image_w) as f32,
|
||||
(b / image_h) as f32,
|
||||
(t / image_h) as f32)
|
||||
}
|
||||
None => (0.0, 1.0, 0.0, 1.0),
|
||||
};*/
|
||||
Aabr {
|
||||
min: Vec2::new(uv_l, uv_b),
|
||||
max: Vec2::new(uv_r, uv_t),
|
||||
@ -371,13 +422,8 @@ impl Ui {
|
||||
font_id,
|
||||
} => {
|
||||
switch_to_plain_state!();
|
||||
// Get screen width and height.
|
||||
let (screen_w, screen_h) =
|
||||
renderer.get_resolution().map(|e| e as f32).into_tuple();
|
||||
// Calculate dpi factor.
|
||||
let dpi_factor = screen_w / ui_win_w as f32;
|
||||
|
||||
let positioned_glyphs = text.positioned_glyphs(dpi_factor);
|
||||
let positioned_glyphs = text.positioned_glyphs(p_scale_factor as f32);
|
||||
let (glyph_cache, cache_tex) = self.cache.glyph_cache_mut_and_tex();
|
||||
// Queue the glyphs to be cached.
|
||||
for glyph in positioned_glyphs {
|
||||
@ -410,12 +456,16 @@ impl Ui {
|
||||
};
|
||||
let rect = Aabr {
|
||||
min: Vec2::new(
|
||||
(screen_rect.min.x as f32 / screen_w - 0.5) * 2.0,
|
||||
(screen_rect.max.y as f32 / screen_h - 0.5) * -2.0,
|
||||
vx(screen_rect.min.x as f64 / p_scale_factor
|
||||
- self.ui.win_w / 2.0),
|
||||
vy(self.ui.win_h / 2.0
|
||||
- screen_rect.max.y as f64 / p_scale_factor),
|
||||
),
|
||||
max: Vec2::new(
|
||||
(screen_rect.max.x as f32 / screen_w - 0.5) * 2.0,
|
||||
(screen_rect.min.y as f32 / screen_h - 0.5) * -2.0,
|
||||
vx(screen_rect.max.x as f64 / p_scale_factor
|
||||
- self.ui.win_w / 2.0),
|
||||
vy(self.ui.win_h / 2.0
|
||||
- screen_rect.min.y as f64 / p_scale_factor),
|
||||
),
|
||||
};
|
||||
mesh.push_quad(create_ui_quad(rect, uv, color, UiMode::Text));
|
||||
@ -469,10 +519,48 @@ impl Ui {
|
||||
));
|
||||
}
|
||||
}
|
||||
PrimitiveKind::Other(container) => {
|
||||
if container.type_id == std::any::TypeId::of::<widgets::ingame::State>() {
|
||||
// Calculate the scale factor to pixels at this 3d point using the camera.
|
||||
if let Some((view_mat, fov)) = cam_params {
|
||||
// Retrieve world position
|
||||
let parameters = container
|
||||
.state_and_style::<widgets::ingame::State, widgets::ingame::Style>()
|
||||
.unwrap()
|
||||
.state
|
||||
.parameters;
|
||||
// Finish current state
|
||||
self.draw_commands.push(match current_state {
|
||||
State::Plain => DrawCommand::plain(start..mesh.vertices().len()),
|
||||
State::Image => DrawCommand::image(start..mesh.vertices().len()),
|
||||
});
|
||||
start = mesh.vertices().len();
|
||||
// Push new position command
|
||||
self.draw_commands.push(DrawCommand::WorldPos(Some(
|
||||
renderer.create_consts(&[parameters.pos.into()]).unwrap(),
|
||||
)));
|
||||
|
||||
let pos_in_view = view_mat * Vec4::from_point(parameters.pos);
|
||||
let scale_factor = self.ui.win_w as f64
|
||||
/ (-2.0
|
||||
* pos_in_view.z as f64
|
||||
* (0.5 * fov as f64).tan()
|
||||
* parameters.res as f64);
|
||||
// Don't process ingame elements behind the camera or very far away
|
||||
placement = if scale_factor > 0.1 {
|
||||
p_scale_factor = ((scale_factor * 10.0).log2().round().powi(2)
|
||||
/ 10.0)
|
||||
.min(1.6)
|
||||
.max(0.2);
|
||||
Placement::InWorld(parameters.num, Some(parameters.res))
|
||||
} else {
|
||||
Placement::InWorld(parameters.num, None)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {} // TODO: Add this.
|
||||
//PrimitiveKind::TrianglesMultiColor {..} => {println!("primitive kind multicolor with id {:?}", id);}
|
||||
// Other unneeded for now.
|
||||
//PrimitiveKind::Other {..} => {println!("primitive kind other with id {:?}", id);}
|
||||
}
|
||||
}
|
||||
// Enter the final command.
|
||||
@ -481,6 +569,25 @@ impl Ui {
|
||||
State::Image => DrawCommand::image(start..mesh.vertices().len()),
|
||||
});
|
||||
|
||||
// Draw glyph cache (use for debugging).
|
||||
/*self.draw_commands
|
||||
.push(DrawCommand::Scissor(default_scissor(renderer)));
|
||||
start = mesh.vertices().len();
|
||||
mesh.push_quad(create_ui_quad(
|
||||
Aabr {
|
||||
min: (-1.0, -1.0).into(),
|
||||
max: (1.0, 1.0).into(),
|
||||
},
|
||||
Aabr {
|
||||
min: (0.0, 1.0).into(),
|
||||
max: (1.0, 0.0).into(),
|
||||
},
|
||||
Rgba::new(1.0, 1.0, 1.0, 0.8),
|
||||
UiMode::Text,
|
||||
));
|
||||
self.draw_commands
|
||||
.push(DrawCommand::plain(start..mesh.vertices().len()));*/
|
||||
|
||||
// Create a larger dynamic model if the mesh is larger than the current model size.
|
||||
if self.model.vbuf.len() < mesh.vertices().len() {
|
||||
self.model = renderer
|
||||
@ -506,12 +613,17 @@ impl Ui {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(&self, renderer: &mut Renderer) {
|
||||
let mut scissor_to_render = default_scissor(renderer);
|
||||
pub fn render(&self, renderer: &mut Renderer, maybe_globals: Option<&Consts<Globals>>) {
|
||||
let mut scissor = default_scissor(renderer);
|
||||
let globals = maybe_globals.unwrap_or(&self.default_globals);
|
||||
let mut locals = &self.interface_locals;
|
||||
for draw_command in self.draw_commands.iter() {
|
||||
match draw_command {
|
||||
DrawCommand::Scissor(scissor) => {
|
||||
scissor_to_render = *scissor;
|
||||
DrawCommand::Scissor(new_scissor) => {
|
||||
scissor = *new_scissor;
|
||||
}
|
||||
DrawCommand::WorldPos(ref pos) => {
|
||||
locals = pos.as_ref().unwrap_or(&self.interface_locals);
|
||||
}
|
||||
DrawCommand::Draw { kind, verts } => {
|
||||
let tex = match kind {
|
||||
@ -519,14 +631,14 @@ impl Ui {
|
||||
DrawKind::Plain => self.cache.glyph_cache_tex(),
|
||||
};
|
||||
let model = self.model.submodel(verts.clone());
|
||||
renderer.render_ui_element(&model, &tex, scissor_to_render);
|
||||
renderer.render_ui_element(&model, &tex, scissor, globals, locals);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn default_scissor(renderer: &mut Renderer) -> Aabr<u16> {
|
||||
fn default_scissor(renderer: &Renderer) -> Aabr<u16> {
|
||||
let (screen_w, screen_h) = renderer.get_resolution().map(|e| e as u16).into_tuple();
|
||||
Aabr {
|
||||
min: Vec2 { x: 0, y: 0 },
|
||||
|
@ -48,6 +48,10 @@ impl Scale {
|
||||
pub fn scale_factor_physical(&self) -> f64 {
|
||||
self.scale_factor_logical() * self.dpi_factor
|
||||
}
|
||||
// Get the dpi factor (ratio between physical and logical coordinates)
|
||||
pub fn dpi_factor(&self) -> f64 {
|
||||
self.dpi_factor
|
||||
}
|
||||
// Updates internal window size (and/or dpi_factor).
|
||||
pub fn window_resized(&mut self, new_dims: Vec2<f64>, renderer: &Renderer) {
|
||||
self.dpi_factor = renderer.get_resolution().x as f64 / new_dims.x;
|
||||
|
199
voxygen/src/ui/widgets/ingame.rs
Normal file
199
voxygen/src/ui/widgets/ingame.rs
Normal file
@ -0,0 +1,199 @@
|
||||
use conrod_core::{
|
||||
builder_methods, image,
|
||||
position::Dimension,
|
||||
widget::{self, button, Id},
|
||||
widget_ids, Color, Position, Positionable, Rect, Sizeable, Ui, UiCell, Widget, WidgetCommon,
|
||||
};
|
||||
use std::slice;
|
||||
use vek::*;
|
||||
|
||||
#[derive(Clone, WidgetCommon)]
|
||||
pub struct Ingame<W> {
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
widget: W,
|
||||
parameters: IngameParameters,
|
||||
}
|
||||
|
||||
pub trait Ingameable: Widget + Sized {
|
||||
fn prim_count(&self) -> usize;
|
||||
// Note this is not responsible for the 3d positioning
|
||||
// Only call this directly if using IngameAnchor
|
||||
fn set_ingame(self, id: widget::Id, parent_id: Id, ui: &mut UiCell) -> Self::Event {
|
||||
self
|
||||
// should pass focus to the window if these are clicked
|
||||
// (they are not displayed where conrod thinks they are)
|
||||
.graphics_for(ui.window)
|
||||
//.parent(parent_id) // is this needed
|
||||
.set(id, ui)
|
||||
}
|
||||
fn position_ingame(self, pos: Vec3<f32>) -> Ingame<Self> {
|
||||
Ingame::new(pos, self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PrimitiveMarker {}
|
||||
impl PrimitiveMarker for widget::Line {}
|
||||
impl PrimitiveMarker for widget::Image {}
|
||||
impl<I> PrimitiveMarker for widget::PointPath<I> {}
|
||||
impl PrimitiveMarker for widget::Circle {}
|
||||
impl<S> PrimitiveMarker for widget::Oval<S> {}
|
||||
impl<I> PrimitiveMarker for widget::Polygon<I> {}
|
||||
impl PrimitiveMarker for widget::Rectangle {}
|
||||
impl<S, I> PrimitiveMarker for widget::Triangles<S, I> {}
|
||||
impl<'a> PrimitiveMarker for widget::Text<'a> {}
|
||||
|
||||
impl<P> Ingameable for P
|
||||
where
|
||||
P: Widget + PrimitiveMarker,
|
||||
{
|
||||
fn prim_count(&self) -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub struct IngameParameters {
|
||||
// Number of primitive widgets to position in the game at the specified position
|
||||
// Note this could be more than the number of widgets in the widgets field since widgets can contain widgets
|
||||
pub num: usize,
|
||||
pub pos: Vec3<f32>,
|
||||
// Number of pixels per 1 unit in world coordinates (ie a voxel)
|
||||
// Used for widgets that are rasterized before being sent to the gpu (text & images)
|
||||
// Potentially make this autmatic based on distance to camera?
|
||||
pub res: f32,
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
id: Option<widget::Id>,
|
||||
pub parameters: IngameParameters,
|
||||
}
|
||||
|
||||
pub type Style = ();
|
||||
|
||||
impl<W: Ingameable> Ingame<W> {
|
||||
pub fn new(pos: Vec3<f32>, widget: W) -> Self {
|
||||
Self {
|
||||
common: widget::CommonBuilder::default(),
|
||||
parameters: IngameParameters {
|
||||
num: widget.prim_count(),
|
||||
pos,
|
||||
res: 1.0,
|
||||
},
|
||||
widget,
|
||||
}
|
||||
}
|
||||
builder_methods! {
|
||||
pub resolution { parameters.res = f32 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Ingameable> Widget for Ingame<W> {
|
||||
type State = State;
|
||||
type Style = Style;
|
||||
type Event = W::Event;
|
||||
|
||||
fn init_state(&self, mut id_gen: widget::id::Generator) -> Self::State {
|
||||
State {
|
||||
id: Some(id_gen.next()),
|
||||
parameters: self.parameters,
|
||||
}
|
||||
}
|
||||
|
||||
fn style(&self) -> Self::Style {
|
||||
()
|
||||
}
|
||||
|
||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||
let widget::UpdateArgs { id, state, ui, .. } = args;
|
||||
let Ingame {
|
||||
widget, parameters, ..
|
||||
} = self;
|
||||
|
||||
// Update pos if it has changed
|
||||
if state.parameters != parameters {
|
||||
state.update(|s| {
|
||||
s.parameters = parameters;
|
||||
});
|
||||
}
|
||||
|
||||
widget.set_ingame(state.id.unwrap(), id, ui)
|
||||
}
|
||||
|
||||
fn default_x_position(&self, ui: &Ui) -> Position {
|
||||
Position::Absolute(0.0)
|
||||
}
|
||||
fn default_y_position(&self, ui: &Ui) -> Position {
|
||||
Position::Absolute(0.0)
|
||||
}
|
||||
fn default_x_dimension(&self, ui: &Ui) -> Dimension {
|
||||
Dimension::Absolute(1.0)
|
||||
}
|
||||
fn default_y_dimension(&self, ui: &Ui) -> Dimension {
|
||||
Dimension::Absolute(1.0)
|
||||
}
|
||||
}
|
||||
|
||||
// Use this if you have multiple widgets that you want to place at the same spot in-game
|
||||
// but don't want to create a new custom widget to contain them both
|
||||
// Note: widgets must be set immediately after settings this
|
||||
// Note: remove this if it ends up unused
|
||||
#[derive(Clone, WidgetCommon)]
|
||||
pub struct IngameAnchor {
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
parameters: IngameParameters,
|
||||
}
|
||||
impl IngameAnchor {
|
||||
pub fn new(pos: Vec3<f32>) -> Self {
|
||||
IngameAnchor {
|
||||
common: widget::CommonBuilder::default(),
|
||||
parameters: IngameParameters {
|
||||
num: 0,
|
||||
pos,
|
||||
res: 1.0,
|
||||
},
|
||||
}
|
||||
}
|
||||
pub fn for_widget(mut self, widget: impl Ingameable) -> Self {
|
||||
self.parameters.num += widget.prim_count();
|
||||
self
|
||||
}
|
||||
pub fn for_widgets(mut self, widget: impl Ingameable, n: usize) -> Self {
|
||||
self.parameters.num += n * widget.prim_count();
|
||||
self
|
||||
}
|
||||
pub fn for_prims(mut self, num: usize) -> Self {
|
||||
self.parameters.num += num;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for IngameAnchor {
|
||||
type State = State;
|
||||
type Style = Style;
|
||||
type Event = ();
|
||||
|
||||
fn init_state(&self, _: widget::id::Generator) -> Self::State {
|
||||
State {
|
||||
id: None,
|
||||
parameters: self.parameters,
|
||||
}
|
||||
}
|
||||
|
||||
fn style(&self) -> Self::Style {
|
||||
()
|
||||
}
|
||||
|
||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||
let widget::UpdateArgs { id, state, ui, .. } = args;
|
||||
let IngameAnchor { parameters, .. } = self;
|
||||
|
||||
// Update pos if it has changed
|
||||
if state.parameters != parameters {
|
||||
state.update(|s| {
|
||||
s.parameters = parameters;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,2 +1,3 @@
|
||||
pub mod image_slider;
|
||||
pub mod ingame;
|
||||
pub mod toggle_button;
|
||||
|
@ -60,6 +60,7 @@ impl Window {
|
||||
key_map.insert(settings.controls.toggle_debug, Key::ToggleDebug);
|
||||
key_map.insert(settings.controls.fullscreen, Key::Fullscreen);
|
||||
key_map.insert(settings.controls.screenshot, Key::Screenshot);
|
||||
key_map.insert(settings.controls.toggle_ingame_ui, Key::ToggleIngameUi);
|
||||
|
||||
let tmp = Ok(Self {
|
||||
events_loop,
|
||||
@ -219,7 +220,7 @@ impl Window {
|
||||
std::thread::spawn(move || {
|
||||
use std::{path::PathBuf, time::SystemTime};
|
||||
// Check if folder exists and create it if it does not
|
||||
let mut path = std::path::PathBuf::from("./screenshots");
|
||||
let mut path = PathBuf::from("./screenshots");
|
||||
if !path.exists() {
|
||||
if let Err(err) = std::fs::create_dir(&path) {
|
||||
log::error!("Coudn't create folder for screenshot: {:?}", err);
|
||||
@ -266,6 +267,7 @@ pub enum Key {
|
||||
ToggleDebug,
|
||||
Fullscreen,
|
||||
Screenshot,
|
||||
ToggleIngameUi,
|
||||
}
|
||||
|
||||
/// Represents an incoming event from the window.
|
||||
|
Loading…
Reference in New Issue
Block a user