ingame_ui

Former-commit-id: 5e836043d58f9217d711462d9f700e3eb096a076
This commit is contained in:
Imbris 2019-05-14 02:43:07 -04:00
parent f839b623f4
commit 7fd3854093
16 changed files with 312 additions and 61 deletions

View File

@ -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;

View File

@ -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,15 @@ 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;
}

View File

@ -23,17 +23,20 @@ use skillbar::Skillbar;
use small_window::{SmallWindow, SmallWindowType};
use crate::{
render::Renderer,
render::{Consts, Globals, Renderer},
settings::{ControlSettings, Settings},
ui::{ScaleMode, Ui},
ui::{Ingame, 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;
const XP_COLOR: Color = Color::Rgba(0.59, 0.41, 0.67, 1.0);
@ -43,7 +46,11 @@ const MANA_COLOR: Color = Color::Rgba(0.42, 0.41, 0.66, 1.0);
widget_ids! {
struct Ids {
// Character Names
name_tags[],
// Test
temp,
bag_space_add,
// Debug
debug_bg,
@ -253,7 +260,7 @@ impl Hud {
}
}
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 +270,34 @@ impl Hud {
return events;
}
// Nametags
let ecs = client.state().ecs();
/* {
let actor_read_storage = ecs.read_storage::<comp::Actor>();
let pos_read_storage = ecs.read_storage::<comp::phys::Pos>();
let num = (&actor_read_storage, &pos_read_storage).join().count();
self.ids
.name_tags
.resize(num, &mut ui_widgets.widget_id_generator());
for (i, (name, pos)) in (&actor_read_storage, &pos_read_storage)
.join()
.map(|(actor, pos)| match actor {
comp::Actor::Character { name, .. } => (name, pos.0),
})
.enumerate()
{
Ingame::from_primitive(pos, Text::new(&name))
.set(self.ids.name_tags[i], ui_widgets);
}
}*/
// test
Ingame::from_primitive(
[0.0, 25.0, 25.0].into(),
Rectangle::fill_with([1.0, 1.0], Color::Rgba(0.2, 0.0, 0.4, 1.0)),
)
.x_y(0.0, 0.0)
.set(self.ids.temp, ui_widgets);
// Display debug window.
if self.show.debug {
// Alpha Version
@ -474,7 +509,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
@ -580,19 +617,20 @@ impl Hud {
pub fn maintain(
&mut self,
client: &Client,
global_state: &mut GlobalState,
debug_info: DebugInfo,
) -> 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);
let events = self.update_layout(client, global_state, debug_info);
self.ui.maintain(&mut global_state.window.renderer_mut());
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));
}
}

View File

@ -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

View File

@ -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());

View File

@ -1,5 +1,5 @@
use crate::{
render::Renderer,
render::{Consts, Globals, Renderer},
ui::{
self,
img_ids::{ImageGraphic, VoxelGraphic},
@ -1085,7 +1085,7 @@ impl CharSelectionUi {
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));
}
}

View File

@ -513,6 +513,6 @@ impl MainMenuUi {
}
pub fn render(&self, renderer: &mut Renderer) {
self.ui.render(renderer);
self.ui.render(renderer, None);
}
}

View File

@ -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,
},

View File

@ -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_WRITE,
}
}
@ -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.

View File

@ -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(),
},

View File

@ -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

View File

@ -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();
@ -178,6 +178,7 @@ impl PlayState for SessionState {
// 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(),

View File

@ -12,12 +12,12 @@ 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, 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,
},
window::Window,
Error,
@ -27,11 +27,11 @@ 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},
widget::{self, id::Generator},
Ui as CrUi, UiBuilder, UiCell,
};
use graphic::Id as GraphicId;
@ -55,6 +55,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 {
@ -88,6 +89,9 @@ pub struct Ui {
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 +103,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 +126,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 +143,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 +151,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,7 +197,7 @@ impl Ui {
}
}
pub fn widget_input(&self, id: WidgId) -> Widget {
pub fn widget_input(&self, id: widget::Id) -> Widget {
self.ui.widget_input(id)
}
@ -216,6 +224,9 @@ impl Ui {
let window_scissor = default_scissor(renderer);
let mut current_scissor = window_scissor;
let mut in_world = None;
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 +234,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,
@ -272,9 +281,29 @@ impl Ui {
self.draw_commands.push(DrawCommand::Scissor(new_scissor));
}
match in_world {
Some(0) => {
in_world = None;
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));
}
Some(n) => in_world = Some(n - 1),
None => (),
}
// 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 in_world.is_some() {
(2.0, 2.0)
} 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| {
@ -285,7 +314,6 @@ impl Ui {
}
};
use conrod_core::render::PrimitiveKind;
match kind {
PrimitiveKind::Image {
image_id,
@ -321,16 +349,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 +400,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 +434,12 @@ 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) - 1.0,
1.0 - vy(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) - 1.0,
1.0 - vy(screen_rect.min.y as f64 / p_scale_factor),
),
};
mesh.push_quad(create_ui_quad(rect, uv, color, UiMode::Text));
@ -469,10 +493,32 @@ impl Ui {
));
}
}
PrimitiveKind::Other(container) => {
if container.type_id == std::any::TypeId::of::<widgets::ingame::State>() {
// Retrieve world position
let pos = container
.state_and_style::<widgets::ingame::State, widgets::ingame::Style>()
.unwrap()
.state
.pos;
// 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(&[pos.into()]).unwrap(),
)));
in_world = Some(1);
p_scale_factor = self.scale.dpi_factor();
}
}
_ => {} // 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.
@ -506,12 +552,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,7 +570,7 @@ 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);
}
}
}

View File

@ -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;

View File

@ -0,0 +1,98 @@
use conrod_core::{
image,
position::Dimension,
widget::{self, button},
widget_ids, Color, Position, Positionable, Rect, Sizeable, Ui, Widget, WidgetCommon,
};
use vek::*;
#[derive(Clone, WidgetCommon)]
pub struct Ingame<W>
where
W: Widget,
{
#[conrod(common_builder)]
common: widget::CommonBuilder,
widget: W,
pos: Vec3<f32>,
}
// TODO: add convenience function to this trait
pub trait Primitive {}
impl Primitive for widget::Line {}
impl Primitive for widget::Image {}
impl<I> Primitive for widget::PointPath<I> {}
impl Primitive for widget::Circle {}
impl<S> Primitive for widget::Oval<S> {}
impl<I> Primitive for widget::Polygon<I> {}
impl Primitive for widget::Rectangle {}
impl<S, I> Primitive for widget::Triangles<S, I> {}
impl<'a> Primitive for widget::Text<'a> {}
widget_ids! {
struct Ids {
prim,
}
}
pub struct State {
ids: Ids,
pub pos: Vec3<f32>,
}
pub type Style = ();
impl<W: Widget + Primitive> Ingame<W> {
pub fn from_primitive(pos: Vec3<f32>, widget: W) -> Self {
Self {
common: widget::CommonBuilder::default(),
pos,
widget,
}
}
}
impl<W: Widget> Widget for Ingame<W> {
type State = State;
type Style = Style;
type Event = ();
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
State {
ids: Ids::new(id_gen),
pos: Vec3::default(),
}
}
fn style(&self) -> Self::Style {
()
}
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
let widget::UpdateArgs { id, state, ui, .. } = args;
let Ingame { widget, pos, .. } = self;
// Update pos if it has changed
if state.pos != pos {
state.update(|s| s.pos = pos);
}
widget
.graphics_for(ui.window)
.parent(id) // is this needed
.set(state.ids.prim, 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)
}
}

View File

@ -1,2 +1,3 @@
pub mod image_slider;
pub mod ingame;
pub mod toggle_button;