mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'ui_fixes' into 'master'
Ui Fixes and Improvements (MS and Transparency) See merge request veloren/veloren!121 Former-commit-id: 3dab489d2b185bdcfee05f87cee59cca54ead605
This commit is contained in:
commit
fbbdb8996b
@ -9,14 +9,14 @@ uniform sampler2D u_tex;
|
||||
out vec4 tgt_color;
|
||||
|
||||
void main() {
|
||||
// Text
|
||||
if (f_mode == uint(0)) {
|
||||
tgt_color = f_color * vec4(1.0, 1.0, 1.0, texture(u_tex, f_uv).a);
|
||||
// Image
|
||||
} else if (f_mode == uint(1)) {
|
||||
tgt_color = texture(u_tex, f_uv);
|
||||
// 2D Geometry
|
||||
} else if (f_mode == uint(2)) {
|
||||
tgt_color = f_color;
|
||||
}
|
||||
// Text
|
||||
if (f_mode == uint(0)) {
|
||||
tgt_color = f_color * vec4(1.0, 1.0, 1.0, texture(u_tex, f_uv).a);
|
||||
// Image
|
||||
} else if (f_mode == uint(1)) {
|
||||
tgt_color = f_color * texture(u_tex, f_uv);
|
||||
// 2D Geometry
|
||||
} else if (f_mode == uint(2)) {
|
||||
tgt_color = f_color;
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ flat out uint f_mode;
|
||||
out vec4 f_color;
|
||||
|
||||
void main() {
|
||||
f_uv = v_uv;
|
||||
f_color = v_color;
|
||||
gl_Position = vec4(v_pos, 0.0, 1.0);
|
||||
f_mode = v_mode;
|
||||
f_uv = v_uv;
|
||||
f_color = v_color;
|
||||
gl_Position = vec4(v_pos, 0.0, 1.0);
|
||||
f_mode = v_mode;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::ui::{BlankGraphic, ImageGraphic, VoxelGraphic};
|
||||
use crate::ui::img_ids::{BlankGraphic, ImageGraphic, VoxelGraphic, VoxelMs9Graphic};
|
||||
|
||||
image_ids! {
|
||||
pub struct Imgs {
|
||||
@ -8,6 +8,8 @@ image_ids! {
|
||||
inv_grid: "/voxygen/element/frames/inv_grid.vox",
|
||||
inv_slot: "/voxygen/element/buttons/inv_slot.vox",
|
||||
|
||||
<VoxelMs9Graphic>
|
||||
|
||||
// Buttons
|
||||
mmap_closed: "/voxygen/element/buttons/button_mmap_closed.vox",
|
||||
mmap_closed_hover: "/voxygen/element/buttons/button_mmap_closed_hover.vox",
|
||||
@ -52,6 +54,8 @@ image_ids! {
|
||||
button_hover: "/voxygen/element/buttons/button_hover.vox",
|
||||
button_press: "/voxygen/element/buttons/button_press.vox",
|
||||
|
||||
<VoxelGraphic>
|
||||
|
||||
// MiniMap
|
||||
mmap_frame: "/voxygen/element/frames/mmap.vox",
|
||||
mmap_frame_closed: "/voxygen/element/frames/mmap_closed.vox",
|
||||
|
@ -475,6 +475,13 @@ impl Hud {
|
||||
self.show.toggle_ui();
|
||||
true
|
||||
}
|
||||
WinEvent::KeyDown(Key::ToggleCursor) => {
|
||||
self.force_ungrab = !self.force_ungrab;
|
||||
if self.force_ungrab {
|
||||
global_state.window.grab_cursor(false);
|
||||
}
|
||||
true
|
||||
}
|
||||
_ if !self.show.ui => false,
|
||||
WinEvent::Zoom(_) => !cursor_grabbed && !self.ui.no_widget_capturing_mouse(),
|
||||
WinEvent::KeyDown(Key::Enter) => {
|
||||
@ -527,13 +534,6 @@ impl Hud {
|
||||
self.show.toggle_help();
|
||||
true
|
||||
}
|
||||
Key::ToggleCursor => {
|
||||
self.force_ungrab = !self.force_ungrab;
|
||||
if self.force_ungrab {
|
||||
global_state.window.grab_cursor(false);
|
||||
}
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
WinEvent::KeyDown(key) | WinEvent::KeyUp(key) => match key {
|
||||
|
@ -1,17 +1,18 @@
|
||||
use crate::{
|
||||
render::Renderer,
|
||||
ui::{self, BlankGraphic, Graphic, ImageGraphic, ScaleMode, Ui, VoxelGraphic},
|
||||
ui::{
|
||||
self,
|
||||
img_ids::{ImageGraphic, VoxelGraphic},
|
||||
ScaleMode, Ui,
|
||||
},
|
||||
window::Window,
|
||||
};
|
||||
use common::{
|
||||
assets,
|
||||
comp::character::{Belt, Character, Chest, Foot, Gender, Hand, Head, Pants, Race, Weapon},
|
||||
use common::comp::character::{
|
||||
Belt, Character, Chest, Foot, Gender, Hand, Head, Pants, Race, Weapon,
|
||||
};
|
||||
use conrod_core::{
|
||||
color,
|
||||
color::TRANSPARENT,
|
||||
image::Id as ImgId,
|
||||
text::font::Id as FontId,
|
||||
widget::{text_box::Event as TextBoxEvent, Button, Image, Rectangle, Text, TextBox},
|
||||
widget_ids, Borderable, Color, Colorable, Labelable, Positionable, Sizeable, Widget,
|
||||
};
|
||||
|
@ -1,15 +1,16 @@
|
||||
use crate::{
|
||||
render::Renderer,
|
||||
ui::{self, BlankGraphic, Graphic, ImageGraphic, ScaleMode, Ui, VoxelGraphic},
|
||||
ui::{
|
||||
self,
|
||||
img_ids::{ImageGraphic, VoxelGraphic},
|
||||
ScaleMode, Ui,
|
||||
},
|
||||
GlobalState, DEFAULT_PUBLIC_SERVER,
|
||||
};
|
||||
use common::assets;
|
||||
use conrod_core::{
|
||||
color,
|
||||
color::TRANSPARENT,
|
||||
image::Id as ImgId,
|
||||
position::Relative,
|
||||
text::font::Id as FontId,
|
||||
widget::{text_box::Event as TextBoxEvent, Button, Image, List, Rectangle, Text, TextBox},
|
||||
widget_ids, Borderable, Color, Colorable, Labelable, Positionable, Sizeable, Widget,
|
||||
};
|
||||
|
@ -10,7 +10,7 @@ mod util;
|
||||
pub use self::{
|
||||
consts::Consts,
|
||||
mesh::{Mesh, Quad, Tri},
|
||||
model::Model,
|
||||
model::{DynamicModel, Model},
|
||||
pipelines::{
|
||||
figure::{BoneData as FigureBoneData, FigurePipeline, Locals as FigureLocals},
|
||||
postprocess::{
|
||||
@ -40,6 +40,7 @@ pub enum RenderError {
|
||||
UpdateError(gfx::UpdateError<usize>),
|
||||
TexUpdateError(gfx::UpdateError<[u16; 3]>),
|
||||
CombinedError(gfx::CombinedError),
|
||||
BufferCreationError(gfx::buffer::CreationError),
|
||||
}
|
||||
|
||||
/// Used to represent a specific rendering configuration.
|
||||
|
@ -1,8 +1,11 @@
|
||||
// Library
|
||||
use gfx::{self, traits::FactoryExt};
|
||||
|
||||
// Local
|
||||
use super::{gfx_backend, mesh::Mesh, Pipeline};
|
||||
use super::{gfx_backend, mesh::Mesh, Pipeline, RenderError};
|
||||
use gfx::{
|
||||
buffer::Role,
|
||||
memory::{Bind, Usage},
|
||||
traits::FactoryExt,
|
||||
Factory,
|
||||
};
|
||||
use std::ops::Range;
|
||||
|
||||
/// Represents a mesh that has been sent to the GPU.
|
||||
pub struct Model<P: Pipeline> {
|
||||
@ -24,3 +27,43 @@ impl<P: Pipeline> Model<P> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a mesh on the GPU which can be updated dynamically
|
||||
pub struct DynamicModel<P: Pipeline> {
|
||||
pub vbuf: gfx::handle::Buffer<gfx_backend::Resources, P::Vertex>,
|
||||
}
|
||||
|
||||
impl<P: Pipeline> DynamicModel<P> {
|
||||
pub fn new(factory: &mut gfx_backend::Factory, size: usize) -> Result<Self, RenderError> {
|
||||
Ok(Self {
|
||||
vbuf: factory
|
||||
.create_buffer(size, Role::Vertex, Usage::Dynamic, Bind::empty())
|
||||
.map_err(|err| RenderError::BufferCreationError(err))?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a model with a slice of a portion of this model to send to the renderer
|
||||
pub fn submodel(&self, range: Range<usize>) -> Model<P> {
|
||||
Model {
|
||||
vbuf: self.vbuf.clone(),
|
||||
slice: gfx::Slice {
|
||||
start: range.start as u32,
|
||||
end: range.end as u32,
|
||||
base_vertex: 0,
|
||||
instances: None,
|
||||
buffer: gfx::IndexBuffer::Auto,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(
|
||||
&self,
|
||||
encoder: &mut gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
|
||||
mesh: &Mesh<P>,
|
||||
offset: usize,
|
||||
) -> Result<(), RenderError> {
|
||||
encoder
|
||||
.update_buffer(&self.vbuf, mesh.vertices(), offset)
|
||||
.map_err(|err| RenderError::UpdateError(err))
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use super::{
|
||||
consts::Consts,
|
||||
gfx_backend,
|
||||
mesh::Mesh,
|
||||
model::Model,
|
||||
model::{DynamicModel, Model},
|
||||
pipelines::{figure, postprocess, skybox, terrain, ui, Globals},
|
||||
texture::Texture,
|
||||
Pipeline, RenderError,
|
||||
@ -12,7 +12,6 @@ use gfx::{
|
||||
handle::Sampler,
|
||||
traits::{Device, Factory, FactoryExt},
|
||||
};
|
||||
use image;
|
||||
use vek::*;
|
||||
|
||||
/// Represents the format of the pre-processed color target.
|
||||
@ -246,6 +245,24 @@ impl Renderer {
|
||||
Ok(Model::new(&mut self.factory, mesh))
|
||||
}
|
||||
|
||||
/// Create a new dynamic model with the specified size
|
||||
pub fn create_dynamic_model<P: Pipeline>(
|
||||
&mut self,
|
||||
size: usize,
|
||||
) -> Result<DynamicModel<P>, RenderError> {
|
||||
DynamicModel::new(&mut self.factory, size)
|
||||
}
|
||||
|
||||
/// Update a dynamic model with a mesh and a offset
|
||||
pub fn update_model<P: Pipeline>(
|
||||
&mut self,
|
||||
model: &DynamicModel<P>,
|
||||
mesh: &Mesh<P>,
|
||||
offset: usize,
|
||||
) -> Result<(), RenderError> {
|
||||
model.update(&mut self.encoder, mesh, offset)
|
||||
}
|
||||
|
||||
/// Create a new texture from the provided image.
|
||||
pub fn create_texture<P: Pipeline>(
|
||||
&mut self,
|
||||
|
54
voxygen/src/ui/cache.rs
Normal file
54
voxygen/src/ui/cache.rs
Normal file
@ -0,0 +1,54 @@
|
||||
use super::graphic::{Graphic, GraphicCache, Id as GraphicId};
|
||||
use crate::{
|
||||
render::{Renderer, Texture, UiPipeline},
|
||||
Error,
|
||||
};
|
||||
use conrod_core::text::GlyphCache;
|
||||
use vek::*;
|
||||
|
||||
pub struct Cache {
|
||||
glyph_cache: GlyphCache<'static>,
|
||||
glyph_cache_tex: Texture<UiPipeline>,
|
||||
graphic_cache: GraphicCache,
|
||||
graphic_cache_tex: Texture<UiPipeline>,
|
||||
}
|
||||
|
||||
// TODO: Should functions be returning UiError instead of Error?
|
||||
impl Cache {
|
||||
pub fn new(renderer: &mut Renderer) -> Result<Self, Error> {
|
||||
let (w, h) = renderer.get_resolution().into_tuple();
|
||||
const SCALE_TOLERANCE: f32 = 0.1;
|
||||
const POSITION_TOLERANCE: f32 = 0.1;
|
||||
|
||||
let graphic_cache_dims = Vec2::new(w * 4, h * 4);
|
||||
Ok(Self {
|
||||
glyph_cache: GlyphCache::builder()
|
||||
.dimensions(w as u32, h as u32)
|
||||
.scale_tolerance(SCALE_TOLERANCE)
|
||||
.position_tolerance(POSITION_TOLERANCE)
|
||||
.build(),
|
||||
glyph_cache_tex: renderer.create_dynamic_texture((w, h).into())?,
|
||||
graphic_cache: GraphicCache::new(graphic_cache_dims),
|
||||
graphic_cache_tex: renderer.create_dynamic_texture(graphic_cache_dims)?,
|
||||
})
|
||||
}
|
||||
pub fn glyph_cache_tex(&self) -> &Texture<UiPipeline> {
|
||||
&self.glyph_cache_tex
|
||||
}
|
||||
pub fn glyph_cache_mut_and_tex(&mut self) -> (&mut GlyphCache<'static>, &Texture<UiPipeline>) {
|
||||
(&mut self.glyph_cache, &self.glyph_cache_tex)
|
||||
}
|
||||
pub fn graphic_cache_tex(&self) -> &Texture<UiPipeline> {
|
||||
&self.graphic_cache_tex
|
||||
}
|
||||
pub fn graphic_cache_mut_and_tex(&mut self) -> (&mut GraphicCache, &Texture<UiPipeline>) {
|
||||
(&mut self.graphic_cache, &self.graphic_cache_tex)
|
||||
}
|
||||
pub fn add_graphic(&mut self, graphic: Graphic) -> GraphicId {
|
||||
self.graphic_cache.add_graphic(graphic)
|
||||
}
|
||||
pub fn clear_graphic_cache(&mut self, renderer: &mut Renderer, new_size: Vec2<u16>) {
|
||||
self.graphic_cache.clear_cache(new_size);
|
||||
self.graphic_cache_tex = renderer.create_dynamic_texture(new_size).unwrap();
|
||||
}
|
||||
}
|
47
voxygen/src/ui/event.rs
Normal file
47
voxygen/src/ui/event.rs
Normal file
@ -0,0 +1,47 @@
|
||||
use conrod_core::{event::Input, input::Button};
|
||||
use vek::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
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);
|
||||
|
||||
// Implement the `WinitWindow` trait for `WindowRef` to allow for generating compatible conversion
|
||||
// functions.
|
||||
impl<'a> conrod_winit::WinitWindow for WindowRef<'a> {
|
||||
fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
winit::Window::get_inner_size(&self.0).map(Into::into)
|
||||
}
|
||||
fn hidpi_factor(&self) -> f32 {
|
||||
winit::Window::get_hidpi_factor(&self.0) as _
|
||||
}
|
||||
}
|
||||
convert_event!(event, &WindowRef(window.window())).map(|input| Self(input))
|
||||
}
|
||||
pub fn is_keyboard_or_mouse(&self) -> bool {
|
||||
match self.0 {
|
||||
Input::Press(_)
|
||||
| Input::Release(_)
|
||||
| Input::Motion(_)
|
||||
| Input::Touch(_)
|
||||
| Input::Text(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn is_keyboard(&self) -> bool {
|
||||
match self.0 {
|
||||
Input::Press(Button::Keyboard(_))
|
||||
| Input::Release(Button::Keyboard(_))
|
||||
| Input::Text(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn new_resize(dims: Vec2<f64>) -> Self {
|
||||
Self(Input::Resize(dims.x, dims.y))
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ use vek::*;
|
||||
|
||||
pub enum Graphic {
|
||||
Image(Arc<DynamicImage>),
|
||||
Voxel(Arc<DotVoxData>),
|
||||
Voxel(Arc<DotVoxData>, Option<u8>),
|
||||
Blank,
|
||||
}
|
||||
|
||||
@ -94,9 +94,8 @@ impl GraphicCache {
|
||||
.pixels()
|
||||
.map(|p| p.data)
|
||||
.collect::<Vec<[u8; 4]>>(),
|
||||
Graphic::Voxel(ref vox) => {
|
||||
super::renderer::draw_vox(&vox.as_ref().into(), aabr.size().into())
|
||||
}
|
||||
Graphic::Voxel(ref vox, min_samples) =>
|
||||
super::renderer::draw_vox(&vox.as_ref().into(), aabr.size().into(), *min_samples),
|
||||
Graphic::Blank => return None,
|
||||
};
|
||||
|
||||
|
@ -4,6 +4,7 @@ use common::{
|
||||
vol::{ReadVol, SizedVol, Vox},
|
||||
};
|
||||
use euc::{buffer::Buffer2d, rasterizer, Pipeline};
|
||||
use image::{DynamicImage, RgbaImage};
|
||||
use vek::*;
|
||||
|
||||
struct Voxel {
|
||||
@ -56,8 +57,13 @@ impl<'a> Pipeline for Voxel {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_vox(segment: &Segment, output_size: Vec2<u16>) -> Vec<[u8; 4]> {
|
||||
let dims = output_size.map(|e| e as usize).into_array();
|
||||
pub fn draw_vox(
|
||||
segment: &Segment,
|
||||
output_size: Vec2<u16>,
|
||||
min_samples: Option<u8>,
|
||||
) -> Vec<[u8; 4]> {
|
||||
let scale = min_samples.map_or(1.0, |s| s as f32).sqrt().ceil() as usize;
|
||||
let dims = output_size.map(|e| e as usize * scale).into_array();
|
||||
let mut color = Buffer2d::new(dims, [0; 4]);
|
||||
let mut depth = Buffer2d::new(dims, 1.0);
|
||||
|
||||
@ -79,8 +85,33 @@ pub fn draw_vox(segment: &Segment, output_size: Vec2<u16>) -> Vec<[u8; 4]> {
|
||||
&mut depth,
|
||||
);
|
||||
|
||||
// TODO: remove this clone
|
||||
color.as_ref().to_vec()
|
||||
if scale > 1 {
|
||||
DynamicImage::ImageRgba8(
|
||||
RgbaImage::from_vec(
|
||||
dims[0] as u32,
|
||||
dims[1] as u32,
|
||||
color
|
||||
.as_ref()
|
||||
.iter()
|
||||
.flatten()
|
||||
.cloned()
|
||||
.collect::<Vec<u8>>(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.resize_exact(
|
||||
output_size.x as u32,
|
||||
output_size.y as u32,
|
||||
image::FilterType::Triangle,
|
||||
)
|
||||
.to_rgba()
|
||||
.pixels()
|
||||
.map(|p| p.data)
|
||||
.collect::<Vec<[u8; 4]>>()
|
||||
} else {
|
||||
// TODO: remove clone
|
||||
color.as_ref().to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
fn ao_level(side1: bool, corner: bool, side2: bool) -> u8 {
|
||||
|
@ -3,9 +3,8 @@ use common::assets::{load, Error};
|
||||
use dot_vox::DotVoxData;
|
||||
use image::DynamicImage;
|
||||
|
||||
pub struct BlankGraphic;
|
||||
pub struct ImageGraphic;
|
||||
pub struct VoxelGraphic;
|
||||
pub enum BlankGraphic {}
|
||||
pub enum ImageGraphic {}
|
||||
|
||||
pub trait GraphicCreator<'a> {
|
||||
type Specifier;
|
||||
@ -23,10 +22,37 @@ impl<'a> GraphicCreator<'a> for ImageGraphic {
|
||||
Ok(Graphic::Image(load::<DynamicImage>(specifier)?))
|
||||
}
|
||||
}
|
||||
|
||||
pub enum VoxelGraphic {}
|
||||
pub enum VoxelMsGraphic {}
|
||||
pub enum VoxelMs4Graphic {}
|
||||
pub enum VoxelMs9Graphic {}
|
||||
|
||||
impl<'a> GraphicCreator<'a> for VoxelGraphic {
|
||||
type Specifier = &'a str;
|
||||
fn new_graphic(specifier: Self::Specifier) -> Result<Graphic, Error> {
|
||||
Ok(Graphic::Voxel(load::<DotVoxData>(specifier)?))
|
||||
Ok(Graphic::Voxel(load::<DotVoxData>(specifier)?, None))
|
||||
}
|
||||
}
|
||||
impl<'a> GraphicCreator<'a> for VoxelMsGraphic {
|
||||
type Specifier = (&'a str, u8);
|
||||
fn new_graphic(specifier: Self::Specifier) -> Result<Graphic, Error> {
|
||||
Ok(Graphic::Voxel(
|
||||
load::<DotVoxData>(specifier.0)?,
|
||||
Some(specifier.1),
|
||||
))
|
||||
}
|
||||
}
|
||||
impl<'a> GraphicCreator<'a> for VoxelMs4Graphic {
|
||||
type Specifier = &'a str;
|
||||
fn new_graphic(specifier: Self::Specifier) -> Result<Graphic, Error> {
|
||||
Ok(Graphic::Voxel(load::<DotVoxData>(specifier)?, Some(4)))
|
||||
}
|
||||
}
|
||||
impl<'a> GraphicCreator<'a> for VoxelMs9Graphic {
|
||||
type Specifier = &'a str;
|
||||
fn new_graphic(specifier: Self::Specifier) -> Result<Graphic, Error> {
|
||||
Ok(Graphic::Voxel(load::<DotVoxData>(specifier)?, Some(9)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,9 +85,9 @@ macro_rules! image_ids {
|
||||
|
||||
impl $Ids {
|
||||
pub fn load(ui: &mut crate::ui::Ui) -> Result<Self, common::assets::Error> {
|
||||
use crate::ui::GraphicCreator;
|
||||
use crate::ui::img_ids::GraphicCreator;
|
||||
Ok(Self {
|
||||
$($( $name: ui.add_graphic(<$T>::new_graphic($specifier)?), )*)*
|
||||
$($( $name: ui.add_graphic(<$T as GraphicCreator>::new_graphic($specifier)?), )*)*
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,134 +1,50 @@
|
||||
mod cache;
|
||||
mod event;
|
||||
mod graphic;
|
||||
mod scale;
|
||||
mod util;
|
||||
mod widgets;
|
||||
#[macro_use]
|
||||
mod img_ids;
|
||||
pub mod img_ids;
|
||||
#[macro_use]
|
||||
mod font_ids;
|
||||
|
||||
pub use event::Event;
|
||||
pub use graphic::Graphic;
|
||||
pub use img_ids::{BlankGraphic, GraphicCreator, ImageGraphic, VoxelGraphic};
|
||||
pub(self) use util::{linear_to_srgb, srgb_to_linear};
|
||||
pub use scale::ScaleMode;
|
||||
pub use widgets::toggle_button::ToggleButton;
|
||||
|
||||
use crate::{
|
||||
render::{
|
||||
create_ui_quad, create_ui_tri, Mesh, Model, RenderError, Renderer, Texture, UiMode,
|
||||
create_ui_quad, create_ui_tri, DynamicModel, Mesh, RenderError, Renderer, UiMode,
|
||||
UiPipeline,
|
||||
},
|
||||
window::Window,
|
||||
Error,
|
||||
};
|
||||
use cache::Cache;
|
||||
use common::assets;
|
||||
use conrod_core::{
|
||||
event::Input,
|
||||
graph::Graph,
|
||||
image::{Id as ImgId, Map},
|
||||
input::{touch::Touch, Button, Motion, Widget},
|
||||
input::{touch::Touch, Motion, Widget},
|
||||
render::Primitive,
|
||||
text::{self, GlyphCache},
|
||||
text::{self, font},
|
||||
widget::{id::Generator, Id as WidgId},
|
||||
Ui as CrUi, UiBuilder, UiCell,
|
||||
};
|
||||
use graphic::{GraphicCache, Id as GraphicId};
|
||||
use graphic::Id as GraphicId;
|
||||
use scale::Scale;
|
||||
use std::ops::Range;
|
||||
use std::sync::Arc;
|
||||
use util::{linear_to_srgb, srgb_to_linear};
|
||||
use vek::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum UiError {
|
||||
RenderError(RenderError),
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct Event(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);
|
||||
|
||||
// Implement the `WinitWindow` trait for `WindowRef` to allow for generating compatible conversion
|
||||
// functions.
|
||||
impl<'a> conrod_winit::WinitWindow for WindowRef<'a> {
|
||||
fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
winit::Window::get_inner_size(&self.0).map(Into::into)
|
||||
}
|
||||
fn hidpi_factor(&self) -> f32 {
|
||||
winit::Window::get_hidpi_factor(&self.0) as _
|
||||
}
|
||||
}
|
||||
convert_event!(event, &WindowRef(window.window())).map(|input| Self(input))
|
||||
}
|
||||
pub fn is_keyboard_or_mouse(&self) -> bool {
|
||||
match self.0 {
|
||||
Input::Press(_)
|
||||
| Input::Release(_)
|
||||
| Input::Motion(_)
|
||||
| Input::Touch(_)
|
||||
| Input::Text(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn is_keyboard(&self) -> bool {
|
||||
match self.0 {
|
||||
Input::Press(Button::Keyboard(_))
|
||||
| Input::Release(Button::Keyboard(_))
|
||||
| Input::Text(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn new_resize(dims: Vec2<f64>) -> Self {
|
||||
Self(Input::Resize(dims.x, dims.y))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Cache {
|
||||
glyph_cache: GlyphCache<'static>,
|
||||
glyph_cache_tex: Texture<UiPipeline>,
|
||||
graphic_cache: graphic::GraphicCache,
|
||||
graphic_cache_tex: Texture<UiPipeline>,
|
||||
}
|
||||
|
||||
// TODO: Should functions be returning UiError instead of Error?
|
||||
impl Cache {
|
||||
pub fn new(renderer: &mut Renderer) -> Result<Self, Error> {
|
||||
let (w, h) = renderer.get_resolution().into_tuple();
|
||||
const SCALE_TOLERANCE: f32 = 0.1;
|
||||
const POSITION_TOLERANCE: f32 = 0.1;
|
||||
|
||||
let graphic_cache_dims = Vec2::new(w * 4, h * 4);
|
||||
Ok(Self {
|
||||
glyph_cache: GlyphCache::builder()
|
||||
.dimensions(w as u32, h as u32)
|
||||
.scale_tolerance(SCALE_TOLERANCE)
|
||||
.position_tolerance(POSITION_TOLERANCE)
|
||||
.build(),
|
||||
glyph_cache_tex: renderer.create_dynamic_texture((w, h).into())?,
|
||||
graphic_cache: GraphicCache::new(graphic_cache_dims),
|
||||
graphic_cache_tex: renderer.create_dynamic_texture(graphic_cache_dims)?,
|
||||
})
|
||||
}
|
||||
pub fn glyph_cache_tex(&self) -> &Texture<UiPipeline> {
|
||||
&self.glyph_cache_tex
|
||||
}
|
||||
pub fn glyph_cache_mut_and_tex(&mut self) -> (&mut GlyphCache<'static>, &Texture<UiPipeline>) {
|
||||
(&mut self.glyph_cache, &self.glyph_cache_tex)
|
||||
}
|
||||
pub fn graphic_cache_tex(&self) -> &Texture<UiPipeline> {
|
||||
&self.graphic_cache_tex
|
||||
}
|
||||
pub fn graphic_cache_mut_and_tex(&mut self) -> (&mut GraphicCache, &Texture<UiPipeline>) {
|
||||
(&mut self.graphic_cache, &self.graphic_cache_tex)
|
||||
}
|
||||
pub fn add_graphic(&mut self, graphic: Graphic) -> GraphicId {
|
||||
self.graphic_cache.add_graphic(graphic)
|
||||
}
|
||||
pub fn clear_graphic_cache(&mut self, renderer: &mut Renderer, new_size: Vec2<u16>) {
|
||||
self.graphic_cache.clear_cache(new_size);
|
||||
self.graphic_cache_tex = renderer.create_dynamic_texture(new_size).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
enum DrawKind {
|
||||
Image,
|
||||
@ -136,90 +52,24 @@ enum DrawKind {
|
||||
Plain,
|
||||
}
|
||||
enum DrawCommand {
|
||||
Draw {
|
||||
kind: DrawKind,
|
||||
model: Model<UiPipeline>,
|
||||
},
|
||||
Draw { kind: DrawKind, verts: Range<usize> },
|
||||
Scissor(Aabr<u16>),
|
||||
}
|
||||
impl DrawCommand {
|
||||
fn image(model: Model<UiPipeline>) -> DrawCommand {
|
||||
fn image(verts: Range<usize>) -> DrawCommand {
|
||||
DrawCommand::Draw {
|
||||
kind: DrawKind::Image,
|
||||
model,
|
||||
verts,
|
||||
}
|
||||
}
|
||||
fn plain(model: Model<UiPipeline>) -> DrawCommand {
|
||||
fn plain(verts: Range<usize>) -> DrawCommand {
|
||||
DrawCommand::Draw {
|
||||
kind: DrawKind::Plain,
|
||||
model,
|
||||
verts,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// How to scale the ui
|
||||
pub enum ScaleMode {
|
||||
// Scale against physical size
|
||||
Absolute(f64),
|
||||
// Use the dpi factor provided by the windowing system (i.e. use logical size)
|
||||
DpiFactor,
|
||||
// Scale based on the window's physical size, but maintain aspect ratio of widgets
|
||||
// Contains width and height of the "default" window size (ie where there should be no scaling)
|
||||
RelativeToWindow(Vec2<f64>),
|
||||
}
|
||||
|
||||
struct Scale {
|
||||
// Type of scaling to use
|
||||
mode: ScaleMode,
|
||||
// Current dpi factor
|
||||
dpi_factor: f64,
|
||||
// Current logical window size
|
||||
window_dims: Vec2<f64>,
|
||||
}
|
||||
|
||||
impl Scale {
|
||||
fn new(window: &Window, mode: ScaleMode) -> Self {
|
||||
let window_dims = window.logical_size();
|
||||
let dpi_factor = window.renderer().get_resolution().x as f64 / window_dims.x;
|
||||
Scale {
|
||||
mode,
|
||||
dpi_factor,
|
||||
window_dims,
|
||||
}
|
||||
}
|
||||
// Change the scaling mode
|
||||
pub fn scaling_mode(&mut self, mode: ScaleMode) {
|
||||
self.mode = mode;
|
||||
}
|
||||
// Calculate factor to transform between logical coordinates and our scaled coordinates
|
||||
fn scale_factor_logical(&self) -> f64 {
|
||||
match self.mode {
|
||||
ScaleMode::Absolute(scale) => scale / self.dpi_factor,
|
||||
ScaleMode::DpiFactor => 1.0,
|
||||
ScaleMode::RelativeToWindow(dims) => {
|
||||
(self.window_dims.x / dims.x).min(self.window_dims.y / dims.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Calculate factor to transform between physical coordinates and our scaled coordinates
|
||||
fn scale_factor_physical(&self) -> f64 {
|
||||
self.scale_factor_logical() * self.dpi_factor
|
||||
}
|
||||
// Updates internal window size (and/or dpi_factor)
|
||||
fn window_resized(&mut self, new_dims: Vec2<f64>, renderer: &Renderer) {
|
||||
self.dpi_factor = renderer.get_resolution().x as f64 / new_dims.x;
|
||||
self.window_dims = new_dims;
|
||||
}
|
||||
// Get scaled window size
|
||||
fn scaled_window_size(&self) -> Vec2<f64> {
|
||||
self.window_dims / self.scale_factor_logical()
|
||||
}
|
||||
// Transform point from logical to scaled coordinates
|
||||
fn scale_point(&self, point: Vec2<f64>) -> Vec2<f64> {
|
||||
point / self.scale_factor_logical()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Font(text::Font);
|
||||
impl assets::Asset for Font {
|
||||
fn load(specifier: &str) -> Result<Self, assets::Error> {
|
||||
@ -235,6 +85,8 @@ pub struct Ui {
|
||||
cache: Cache,
|
||||
// Draw commands for the next render
|
||||
draw_commands: Vec<DrawCommand>,
|
||||
// Model for drawing the ui
|
||||
model: DynamicModel<UiPipeline>,
|
||||
// Stores new window size for updating scaling
|
||||
window_resized: Option<Vec2<f64>>,
|
||||
// Scaling of the ui
|
||||
@ -250,8 +102,9 @@ impl Ui {
|
||||
ui: UiBuilder::new(win_dims).build(),
|
||||
image_map: Map::new(),
|
||||
cache: Cache::new(window.renderer_mut())?,
|
||||
window_resized: None,
|
||||
draw_commands: vec![],
|
||||
model: window.renderer_mut().create_dynamic_model(100)?,
|
||||
window_resized: None,
|
||||
scale,
|
||||
})
|
||||
}
|
||||
@ -268,7 +121,7 @@ impl Ui {
|
||||
self.image_map.insert(self.cache.add_graphic(graphic))
|
||||
}
|
||||
|
||||
pub fn new_font(&mut self, mut font: Arc<Font>) -> text::font::Id {
|
||||
pub fn new_font(&mut self, mut font: Arc<Font>) -> font::Id {
|
||||
self.ui.fonts.insert(font.as_ref().0.clone())
|
||||
}
|
||||
|
||||
@ -309,7 +162,9 @@ impl Ui {
|
||||
}
|
||||
pub fn handle_event(&mut self, event: Event) {
|
||||
match event.0 {
|
||||
Input::Resize(w, h) => self.window_resized = Some(Vec2::new(w, h)),
|
||||
Input::Resize(w, h) if w > 1.0 && h > 1.0 => {
|
||||
self.window_resized = Some(Vec2::new(w, h))
|
||||
}
|
||||
Input::Touch(touch) => self.ui.handle_event(Input::Touch(Touch {
|
||||
xy: self.scale.scale_point(touch.xy.into()).into_array(),
|
||||
..touch
|
||||
@ -338,308 +193,315 @@ impl Ui {
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer) {
|
||||
let ref mut ui = self.ui;
|
||||
// Regenerate draw commands and associated models only if the ui changed
|
||||
if let Some(mut primitives) = ui.draw_if_changed() {
|
||||
self.draw_commands.clear();
|
||||
let mut mesh = Mesh::new();
|
||||
let mut primitives = match self.ui.draw_if_changed() {
|
||||
Some(primitives) => primitives,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// TODO: this could be removed entirely if the draw call just used both textures
|
||||
// however this allows for flexibility if we want to interleave other draw calls later
|
||||
enum State {
|
||||
Image,
|
||||
Plain,
|
||||
self.draw_commands.clear();
|
||||
let mut mesh = Mesh::new();
|
||||
|
||||
// TODO: this could be removed entirely if the draw call just used both textures
|
||||
// however this allows for flexibility if we want to interleave other draw calls later
|
||||
enum State {
|
||||
Image,
|
||||
Plain,
|
||||
};
|
||||
|
||||
let mut current_state = State::Plain;
|
||||
let mut start = 0;
|
||||
|
||||
let window_scizzor = default_scissor(renderer);
|
||||
let mut current_scizzor = window_scizzor;
|
||||
|
||||
// Switches to the `Plain` state and completes the previous `Command` if not already in the
|
||||
// `Plain` state.
|
||||
macro_rules! switch_to_plain_state {
|
||||
() => {
|
||||
if let State::Image = current_state {
|
||||
self.draw_commands
|
||||
.push(DrawCommand::image(start..mesh.vertices().len()));
|
||||
current_state = State::Plain;
|
||||
start = mesh.vertices().len();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let mut current_state = State::Plain;
|
||||
let p_scale_factor = self.scale.scale_factor_physical();
|
||||
|
||||
let window_scizzor = default_scissor(renderer);
|
||||
let mut current_scizzor = window_scizzor;
|
||||
while let Some(prim) = primitives.next() {
|
||||
let Primitive {
|
||||
kind,
|
||||
scizzor,
|
||||
rect,
|
||||
..
|
||||
} = prim;
|
||||
|
||||
// Switches to the `Plain` state and completes the previous `Command` if not already in the
|
||||
// `Plain` state.
|
||||
macro_rules! switch_to_plain_state {
|
||||
() => {
|
||||
if let State::Image = current_state {
|
||||
self.draw_commands
|
||||
.push(DrawCommand::image(renderer.create_model(&mesh).unwrap()));
|
||||
mesh.clear();
|
||||
current_state = State::Plain;
|
||||
}
|
||||
};
|
||||
// Check for a change in the scizzor
|
||||
let new_scizzor = {
|
||||
let (l, b, w, h) = scizzor.l_b_w_h();
|
||||
// Calculate minimum x and y coordinates while
|
||||
// - flipping y axis (from +up to +down)
|
||||
// - moving origin to top-left corner (from middle)
|
||||
let min_x = self.ui.win_w / 2.0 + l;
|
||||
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,
|
||||
},
|
||||
max: Vec2 {
|
||||
x: ((min_x + w) * p_scale_factor) as u16,
|
||||
y: ((min_y + h) * p_scale_factor) as u16,
|
||||
},
|
||||
}
|
||||
.intersection(window_scizzor)
|
||||
};
|
||||
if new_scizzor != current_scizzor {
|
||||
// Finish the current command
|
||||
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();
|
||||
|
||||
// Update the scizzor and produce a command.
|
||||
current_scizzor = new_scizzor;
|
||||
self.draw_commands.push(DrawCommand::Scissor(new_scizzor));
|
||||
}
|
||||
|
||||
let p_scale_factor = self.scale.scale_factor_physical();
|
||||
|
||||
while let Some(prim) = primitives.next() {
|
||||
let Primitive {
|
||||
kind,
|
||||
scizzor,
|
||||
id: _id,
|
||||
rect,
|
||||
} = prim;
|
||||
|
||||
// Check for a change in the scizzor
|
||||
let new_scizzor = {
|
||||
let (l, b, w, h) = scizzor.l_b_w_h();
|
||||
// Calculate minimum x and y coordinates while
|
||||
// - flipping y axis (from +up to +down)
|
||||
// - moving origin to top-left corner (from middle)
|
||||
let min_x = ui.win_w / 2.0 + l;
|
||||
let min_y = 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,
|
||||
},
|
||||
max: Vec2 {
|
||||
x: ((min_x + w) * p_scale_factor) as u16,
|
||||
y: ((min_y + h) * p_scale_factor) as u16,
|
||||
},
|
||||
}
|
||||
.intersection(window_scizzor)
|
||||
};
|
||||
if new_scizzor != current_scizzor {
|
||||
// Finish the current command
|
||||
self.draw_commands.push(match current_state {
|
||||
State::Plain => DrawCommand::plain(renderer.create_model(&mesh).unwrap()),
|
||||
State::Image => DrawCommand::image(renderer.create_model(&mesh).unwrap()),
|
||||
});
|
||||
mesh.clear();
|
||||
|
||||
// Update the scizzor and produce a command.
|
||||
current_scizzor = new_scizzor;
|
||||
self.draw_commands.push(DrawCommand::Scissor(new_scizzor));
|
||||
// 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 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 (l, r, b, t) = rect.l_r_b_t();
|
||||
Aabr {
|
||||
min: Vec2::new(vx(l), vy(b)),
|
||||
max: Vec2::new(vx(r), vy(t)),
|
||||
}
|
||||
};
|
||||
|
||||
// Functions for converting for conrod scalar coords to GL vertex coords (-1.0 to 1.0)
|
||||
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 (l, r, b, t) = rect.l_r_b_t();
|
||||
Aabr {
|
||||
min: Vec2::new(vx(l), vy(b)),
|
||||
max: Vec2::new(vx(r), vy(t)),
|
||||
use conrod_core::render::PrimitiveKind;
|
||||
match kind {
|
||||
PrimitiveKind::Image {
|
||||
image_id,
|
||||
color,
|
||||
source_rect,
|
||||
} => {
|
||||
let graphic_id = self
|
||||
.image_map
|
||||
.get(&image_id)
|
||||
.expect("Image does not exist in image map");
|
||||
let (graphic_cache, cache_tex) = self.cache.graphic_cache_mut_and_tex();
|
||||
|
||||
match graphic_cache.get_graphic(*graphic_id) {
|
||||
Some(Graphic::Blank) | None => continue,
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
|
||||
use conrod_core::render::PrimitiveKind;
|
||||
match kind {
|
||||
PrimitiveKind::Image {
|
||||
image_id,
|
||||
color,
|
||||
source_rect,
|
||||
} => {
|
||||
let graphic_id = self
|
||||
.image_map
|
||||
.get(&image_id)
|
||||
.expect("Image does not exist in image map");
|
||||
let (graphic_cache, cache_tex) = self.cache.graphic_cache_mut_and_tex();
|
||||
// Switch to the image state if we are not in it already
|
||||
if let State::Plain = current_state {
|
||||
self.draw_commands
|
||||
.push(DrawCommand::plain(start..mesh.vertices().len()));
|
||||
start = mesh.vertices().len();
|
||||
current_state = State::Image;
|
||||
}
|
||||
|
||||
match graphic_cache.get_graphic(*graphic_id) {
|
||||
Some(Graphic::Blank) | None => continue,
|
||||
_ => {}
|
||||
let color =
|
||||
srgb_to_linear(color.unwrap_or(conrod_core::color::WHITE).to_fsa().into());
|
||||
|
||||
let resolution = Vec2::new(
|
||||
(rect.w() * p_scale_factor).round() as u16,
|
||||
(rect.h() * p_scale_factor).round() as u16,
|
||||
);
|
||||
// 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),
|
||||
};*/
|
||||
Aabr {
|
||||
min: Vec2::new(uv_l, uv_b),
|
||||
max: Vec2::new(uv_r, uv_t),
|
||||
}
|
||||
};
|
||||
let (cache_w, cache_h) =
|
||||
cache_tex.get_dimensions().map(|e| e as f32).into_tuple();
|
||||
|
||||
// Switch to the `Image` state for this image if we're not in it already.
|
||||
if let State::Plain = current_state {
|
||||
self.draw_commands
|
||||
.push(DrawCommand::plain(renderer.create_model(&mesh).unwrap()));
|
||||
mesh.clear();
|
||||
current_state = State::Image;
|
||||
}
|
||||
// Cache graphic at particular resolution
|
||||
let uv_aabr = match graphic_cache.cache_res(
|
||||
*graphic_id,
|
||||
resolution,
|
||||
source_aabr,
|
||||
|aabr, data| {
|
||||
let offset = aabr.min.into_array();
|
||||
let size = aabr.size().into_array();
|
||||
renderer.update_texture(cache_tex, offset, size, &data);
|
||||
},
|
||||
) {
|
||||
Some(aabr) => Aabr {
|
||||
min: Vec2::new(
|
||||
aabr.min.x as f32 / cache_w,
|
||||
aabr.max.y as f32 / cache_h,
|
||||
),
|
||||
max: Vec2::new(
|
||||
aabr.max.x as f32 / cache_w,
|
||||
aabr.min.y as f32 / cache_h,
|
||||
),
|
||||
},
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let color = srgb_to_linear(
|
||||
color.unwrap_or(conrod_core::color::WHITE).to_fsa().into(),
|
||||
);
|
||||
mesh.push_quad(create_ui_quad(gl_aabr(rect), uv_aabr, color, UiMode::Image));
|
||||
}
|
||||
PrimitiveKind::Text {
|
||||
color,
|
||||
text,
|
||||
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 resolution = Vec2::new(
|
||||
(rect.w() * p_scale_factor) as u16,
|
||||
(rect.h() * p_scale_factor) as u16,
|
||||
);
|
||||
// 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),
|
||||
};*/
|
||||
Aabr {
|
||||
min: Vec2::new(uv_l, uv_b),
|
||||
max: Vec2::new(uv_r, uv_t),
|
||||
}
|
||||
};
|
||||
let (cache_w, cache_h) =
|
||||
cache_tex.get_dimensions().map(|e| e as f32).into_tuple();
|
||||
let positioned_glyphs = text.positioned_glyphs(dpi_factor);
|
||||
let (glyph_cache, cache_tex) = self.cache.glyph_cache_mut_and_tex();
|
||||
// Queue the glyphs to be cached
|
||||
for glyph in positioned_glyphs {
|
||||
glyph_cache.queue_glyph(font_id.index(), glyph.clone());
|
||||
}
|
||||
|
||||
// Cache graphic at particular resolution
|
||||
let uv_aabr = match graphic_cache.cache_res(
|
||||
*graphic_id,
|
||||
resolution,
|
||||
source_aabr,
|
||||
|aabr, data| {
|
||||
let offset = aabr.min.into_array();
|
||||
let size = aabr.size().into_array();
|
||||
renderer.update_texture(cache_tex, offset, size, &data);
|
||||
},
|
||||
) {
|
||||
Some(aabr) => Aabr {
|
||||
glyph_cache
|
||||
.cache_queued(|rect, data| {
|
||||
let offset = [rect.min.x as u16, rect.min.y as u16];
|
||||
let size = [rect.width() as u16, rect.height() as u16];
|
||||
|
||||
let new_data = data
|
||||
.iter()
|
||||
.map(|x| [255, 255, 255, *x])
|
||||
.collect::<Vec<[u8; 4]>>();
|
||||
|
||||
renderer.update_texture(cache_tex, offset, size, &new_data);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let color = srgb_to_linear(color.to_fsa().into());
|
||||
|
||||
for g in positioned_glyphs {
|
||||
if let Ok(Some((uv_rect, screen_rect))) =
|
||||
glyph_cache.rect_for(font_id.index(), g)
|
||||
{
|
||||
let uv = Aabr {
|
||||
min: Vec2::new(uv_rect.min.x, uv_rect.max.y),
|
||||
max: Vec2::new(uv_rect.max.x, uv_rect.min.y),
|
||||
};
|
||||
let rect = Aabr {
|
||||
min: Vec2::new(
|
||||
aabr.min.x as f32 / cache_w,
|
||||
aabr.max.y as f32 / cache_h,
|
||||
(screen_rect.min.x as f32 / screen_w - 0.5) * 2.0,
|
||||
(screen_rect.max.y as f32 / screen_h - 0.5) * -2.0,
|
||||
),
|
||||
max: Vec2::new(
|
||||
aabr.max.x as f32 / cache_w,
|
||||
aabr.min.y as f32 / cache_h,
|
||||
(screen_rect.max.x as f32 / screen_w - 0.5) * 2.0,
|
||||
(screen_rect.min.y as f32 / screen_h - 0.5) * -2.0,
|
||||
),
|
||||
},
|
||||
None => continue,
|
||||
};
|
||||
|
||||
mesh.push_quad(create_ui_quad(
|
||||
gl_aabr(rect),
|
||||
uv_aabr,
|
||||
color,
|
||||
UiMode::Image,
|
||||
));
|
||||
};
|
||||
mesh.push_quad(create_ui_quad(rect, uv, color, UiMode::Text));
|
||||
}
|
||||
}
|
||||
PrimitiveKind::Text {
|
||||
}
|
||||
PrimitiveKind::Rectangle { color } => {
|
||||
let color = srgb_to_linear(color.to_fsa().into());
|
||||
// Don't draw a transparent rectangle
|
||||
if color[3] == 0.0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch_to_plain_state!();
|
||||
|
||||
mesh.push_quad(create_ui_quad(
|
||||
gl_aabr(rect),
|
||||
Aabr {
|
||||
min: Vec2::new(0.0, 0.0),
|
||||
max: Vec2::new(0.0, 0.0),
|
||||
},
|
||||
color,
|
||||
text,
|
||||
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 (glyph_cache, cache_tex) = self.cache.glyph_cache_mut_and_tex();
|
||||
// Queue the glyphs to be cached
|
||||
for glyph in positioned_glyphs {
|
||||
glyph_cache.queue_glyph(font_id.index(), glyph.clone());
|
||||
}
|
||||
|
||||
glyph_cache
|
||||
.cache_queued(|rect, data| {
|
||||
let offset = [rect.min.x as u16, rect.min.y as u16];
|
||||
let size = [rect.width() as u16, rect.height() as u16];
|
||||
|
||||
let new_data = data
|
||||
.iter()
|
||||
.map(|x| [255, 255, 255, *x])
|
||||
.collect::<Vec<[u8; 4]>>();
|
||||
|
||||
renderer.update_texture(cache_tex, offset, size, &new_data);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let color = srgb_to_linear(color.to_fsa().into());
|
||||
|
||||
for g in positioned_glyphs {
|
||||
if let Ok(Some((uv_rect, screen_rect))) =
|
||||
glyph_cache.rect_for(font_id.index(), g)
|
||||
{
|
||||
let uv = Aabr {
|
||||
min: Vec2::new(uv_rect.min.x, uv_rect.max.y),
|
||||
max: Vec2::new(uv_rect.max.x, uv_rect.min.y),
|
||||
};
|
||||
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,
|
||||
),
|
||||
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,
|
||||
),
|
||||
};
|
||||
mesh.push_quad(create_ui_quad(rect, uv, color, UiMode::Text));
|
||||
}
|
||||
}
|
||||
UiMode::Geometry,
|
||||
));
|
||||
}
|
||||
PrimitiveKind::TrianglesSingleColor { color, triangles } => {
|
||||
// Don't draw transparent triangle or switch state if there are actually no triangles
|
||||
let color = srgb_to_linear(Rgba::from(Into::<[f32; 4]>::into(color)));
|
||||
if triangles.is_empty() || color[3] == 0.0 {
|
||||
continue;
|
||||
}
|
||||
PrimitiveKind::Rectangle { color } => {
|
||||
let color = srgb_to_linear(color.to_fsa().into());
|
||||
// Don't draw a transparent rectangle
|
||||
if color[3] == 0.0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch_to_plain_state!();
|
||||
switch_to_plain_state!();
|
||||
|
||||
mesh.push_quad(create_ui_quad(
|
||||
gl_aabr(rect),
|
||||
Aabr {
|
||||
min: Vec2::new(0.0, 0.0),
|
||||
max: Vec2::new(0.0, 0.0),
|
||||
},
|
||||
for tri in triangles {
|
||||
let p1 = Vec2::new(vx(tri[0][0]), vy(tri[0][1]));
|
||||
let p2 = Vec2::new(vx(tri[1][0]), vy(tri[1][1]));
|
||||
let p3 = Vec2::new(vx(tri[2][0]), vy(tri[2][1]));
|
||||
// If triangle is clockwise reverse it
|
||||
let (v1, v2): (Vec3<f32>, Vec3<f32>) = ((p2 - p1).into(), (p3 - p1).into());
|
||||
let triangle = if v1.cross(v2).z > 0.0 {
|
||||
[p1.into_array(), p2.into_array(), p3.into_array()]
|
||||
} else {
|
||||
[p2.into_array(), p1.into_array(), p3.into_array()]
|
||||
};
|
||||
mesh.push_tri(create_ui_tri(
|
||||
triangle,
|
||||
[[0.0; 2]; 3],
|
||||
color,
|
||||
UiMode::Geometry,
|
||||
));
|
||||
}
|
||||
PrimitiveKind::TrianglesSingleColor { color, triangles } => {
|
||||
// Don't draw transparent triangle or switch state if there are actually no triangles
|
||||
let color = srgb_to_linear(Rgba::from(Into::<[f32; 4]>::into(color)));
|
||||
if triangles.is_empty() || color[3] == 0.0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch_to_plain_state!();
|
||||
|
||||
for tri in triangles {
|
||||
let p1 = Vec2::new(vx(tri[0][0]), vy(tri[0][1]));
|
||||
let p2 = Vec2::new(vx(tri[1][0]), vy(tri[1][1]));
|
||||
let p3 = Vec2::new(vx(tri[2][0]), vy(tri[2][1]));
|
||||
// If triangle is clockwise reverse it
|
||||
let (v1, v2): (Vec3<f32>, Vec3<f32>) =
|
||||
((p2 - p1).into(), (p3 - p1).into());
|
||||
let triangle = if v1.cross(v2).z > 0.0 {
|
||||
[p1.into_array(), p2.into_array(), p3.into_array()]
|
||||
} else {
|
||||
[p2.into_array(), p1.into_array(), p3.into_array()]
|
||||
};
|
||||
mesh.push_tri(create_ui_tri(
|
||||
triangle,
|
||||
[[0.0; 2]; 3],
|
||||
color,
|
||||
UiMode::Geometry,
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => {} // TODO: Add this
|
||||
//PrimitiveKind::TrianglesMultiColor {..} => {println!("primitive kind multicolor with id {:?}", id);}
|
||||
// Other uneeded for now
|
||||
//PrimitiveKind::Other {..} => {println!("primitive kind other with id {:?}", id);}
|
||||
}
|
||||
_ => {} // TODO: Add this
|
||||
//PrimitiveKind::TrianglesMultiColor {..} => {println!("primitive kind multicolor with id {:?}", id);}
|
||||
// Other uneeded for now
|
||||
//PrimitiveKind::Other {..} => {println!("primitive kind other with id {:?}", id);}
|
||||
}
|
||||
// Enter the final command
|
||||
self.draw_commands.push(match current_state {
|
||||
State::Plain => DrawCommand::plain(renderer.create_model(&mesh).unwrap()),
|
||||
State::Image => DrawCommand::image(renderer.create_model(&mesh).unwrap()),
|
||||
});
|
||||
}
|
||||
// Enter the final command
|
||||
self.draw_commands.push(match current_state {
|
||||
State::Plain => DrawCommand::plain(start..mesh.vertices().len()),
|
||||
State::Image => DrawCommand::image(start..mesh.vertices().len()),
|
||||
});
|
||||
|
||||
// Handle window resizing
|
||||
if let Some(new_dims) = self.window_resized.take() {
|
||||
self.scale.window_resized(new_dims, renderer);
|
||||
let (w, h) = self.scale.scaled_window_size().into_tuple();
|
||||
self.ui.handle_event(Input::Resize(w, h));
|
||||
// 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
|
||||
.create_dynamic_model(mesh.vertices().len() * 4 / 3)
|
||||
.unwrap();
|
||||
}
|
||||
renderer.update_model(&self.model, &mesh, 0).unwrap();
|
||||
// Update model with new mesh
|
||||
|
||||
let res = renderer.get_resolution();
|
||||
// Avoid panic in graphic cache when minimizing
|
||||
if res.x > 0 && res.y > 0 {
|
||||
self.cache
|
||||
.clear_graphic_cache(renderer, renderer.get_resolution().map(|e| e * 4));
|
||||
}
|
||||
// TODO: probably need to resize glyph cache, see conrod's gfx backend for reference
|
||||
// Handle window resizing
|
||||
if let Some(new_dims) = self.window_resized.take() {
|
||||
self.scale.window_resized(new_dims, renderer);
|
||||
let (w, h) = self.scale.scaled_window_size().into_tuple();
|
||||
self.ui.handle_event(Input::Resize(w, h));
|
||||
|
||||
let res = renderer.get_resolution();
|
||||
// Avoid panic in graphic cache when minimizing
|
||||
if res.x > 0 && res.y > 0 {
|
||||
self.cache
|
||||
.clear_graphic_cache(renderer, renderer.get_resolution().map(|e| e * 4));
|
||||
}
|
||||
// TODO: probably need to resize glyph cache, see conrod's gfx backend for reference
|
||||
}
|
||||
}
|
||||
|
||||
@ -650,11 +512,12 @@ impl Ui {
|
||||
DrawCommand::Scissor(scizzor) => {
|
||||
scissor = *scizzor;
|
||||
}
|
||||
DrawCommand::Draw { kind, model } => {
|
||||
DrawCommand::Draw { kind, verts } => {
|
||||
let tex = match kind {
|
||||
DrawKind::Image => self.cache.graphic_cache_tex(),
|
||||
DrawKind::Plain => self.cache.glyph_cache_tex(),
|
||||
};
|
||||
let model = self.model.submodel(verts.clone());
|
||||
renderer.render_ui_element(&model, &tex, scissor);
|
||||
}
|
||||
}
|
||||
|
65
voxygen/src/ui/scale.rs
Normal file
65
voxygen/src/ui/scale.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use crate::{render::Renderer, window::Window};
|
||||
use vek::*;
|
||||
|
||||
// How the ui is scaled
|
||||
pub enum ScaleMode {
|
||||
// Scale against physical size
|
||||
Absolute(f64),
|
||||
// Use the dpi factor provided by the windowing system (i.e. use logical size)
|
||||
DpiFactor,
|
||||
// Scale based on the window's physical size, but maintain aspect ratio of widgets
|
||||
// Contains width and height of the "default" window size (ie where there should be no scaling)
|
||||
RelativeToWindow(Vec2<f64>),
|
||||
}
|
||||
|
||||
pub struct Scale {
|
||||
// Type of scaling to use
|
||||
mode: ScaleMode,
|
||||
// Current dpi factor
|
||||
dpi_factor: f64,
|
||||
// Current logical window size
|
||||
window_dims: Vec2<f64>,
|
||||
}
|
||||
|
||||
impl Scale {
|
||||
pub fn new(window: &Window, mode: ScaleMode) -> Self {
|
||||
let window_dims = window.logical_size();
|
||||
let dpi_factor = window.renderer().get_resolution().x as f64 / window_dims.x;
|
||||
Scale {
|
||||
mode,
|
||||
dpi_factor,
|
||||
window_dims,
|
||||
}
|
||||
}
|
||||
// Change the scaling mode
|
||||
pub fn scaling_mode(&mut self, mode: ScaleMode) {
|
||||
self.mode = mode;
|
||||
}
|
||||
// Calculate factor to transform between logical coordinates and our scaled coordinates
|
||||
pub fn scale_factor_logical(&self) -> f64 {
|
||||
match self.mode {
|
||||
ScaleMode::Absolute(scale) => scale / self.dpi_factor,
|
||||
ScaleMode::DpiFactor => 1.0,
|
||||
ScaleMode::RelativeToWindow(dims) => {
|
||||
(self.window_dims.x / dims.x).min(self.window_dims.y / dims.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Calculate factor to transform between physical coordinates and our scaled coordinates
|
||||
pub fn scale_factor_physical(&self) -> f64 {
|
||||
self.scale_factor_logical() * 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;
|
||||
self.window_dims = new_dims;
|
||||
}
|
||||
// Get scaled window size
|
||||
pub fn scaled_window_size(&self) -> Vec2<f64> {
|
||||
self.window_dims / self.scale_factor_logical()
|
||||
}
|
||||
// Transform point from logical to scaled coordinates
|
||||
pub fn scale_point(&self, point: Vec2<f64>) -> Vec2<f64> {
|
||||
point / self.scale_factor_logical()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user