start to move ezgui to glium. just copying extra code in, not deleting

existing stuff yet
This commit is contained in:
Dustin Carlino 2019-01-22 14:34:11 -08:00
parent 6064aa10af
commit 974b04e74d
8 changed files with 582 additions and 2 deletions

View File

@ -67,3 +67,5 @@
- switch ezgui (could make it generic and have piston or glium support, but maybe not worth it)
- render text
- change ezgui API to allow uploading geometry once
- undo the y inversion hacks at last!

View File

@ -8,6 +8,8 @@ edition = "2018"
abstutil = { path = "../abstutil" }
dimensioned = { git = "https://github.com/paholg/dimensioned", rev = "0e1076ebfa5128d1ee544bdc9754c948987b6fe3", features = ["serde"] }
geom = { path = "../geom" }
glium = "0.23.0"
glutin = "0.19.0"
log = "0.4.5"
ordered-float = "1.0.1"
palette = "0.4"

171
ezgui/src/camera.rs Normal file
View File

@ -0,0 +1,171 @@
use glutin;
use std::f32;
pub struct CameraState {
aspect_ratio: f32,
position: (f32, f32, f32),
direction: (f32, f32, f32),
moving_up: bool,
moving_left: bool,
moving_down: bool,
moving_right: bool,
moving_forward: bool,
moving_backward: bool,
}
impl CameraState {
pub fn new() -> CameraState {
CameraState {
aspect_ratio: 1024.0 / 768.0,
position: (0.1, 0.1, 1.0),
direction: (0.0, 0.0, -1.0),
moving_up: false,
moving_left: false,
moving_down: false,
moving_right: false,
moving_forward: false,
moving_backward: false,
}
}
pub fn get_perspective(&self) -> [[f32; 4]; 4] {
let fov = f32::consts::PI / 2.0;
let zfar = 1024.0;
let znear = 0.1;
let f = 1.0 / (fov / 2.0).tan();
// note: remember that this is column-major, so the lines of code are actually columns
[
[f / self.aspect_ratio, 0.0, 0.0, 0.0],
[0.0, f, 0.0, 0.0],
[0.0, 0.0, (zfar + znear) / (zfar - znear), 1.0],
[0.0, 0.0, -(2.0 * zfar * znear) / (zfar - znear), 0.0],
]
}
pub fn get_view(&self) -> [[f32; 4]; 4] {
let f = {
let f = self.direction;
let len = f.0 * f.0 + f.1 * f.1 + f.2 * f.2;
let len = len.sqrt();
(f.0 / len, f.1 / len, f.2 / len)
};
let up = (0.0, 1.0, 0.0);
let s = (
f.1 * up.2 - f.2 * up.1,
f.2 * up.0 - f.0 * up.2,
f.0 * up.1 - f.1 * up.0,
);
let s_norm = {
let len = s.0 * s.0 + s.1 * s.1 + s.2 * s.2;
let len = len.sqrt();
(s.0 / len, s.1 / len, s.2 / len)
};
let u = (
s_norm.1 * f.2 - s_norm.2 * f.1,
s_norm.2 * f.0 - s_norm.0 * f.2,
s_norm.0 * f.1 - s_norm.1 * f.0,
);
let p = (
-self.position.0 * s.0 - self.position.1 * s.1 - self.position.2 * s.2,
-self.position.0 * u.0 - self.position.1 * u.1 - self.position.2 * u.2,
-self.position.0 * f.0 - self.position.1 * f.1 - self.position.2 * f.2,
);
// note: remember that this is column-major, so the lines of code are actually columns
[
[s_norm.0, u.0, f.0, 0.0],
[s_norm.1, u.1, f.1, 0.0],
[s_norm.2, u.2, f.2, 0.0],
[p.0, p.1, p.2, 1.0],
]
}
fn update(&mut self) {
let f = {
let f = self.direction;
let len = f.0 * f.0 + f.1 * f.1 + f.2 * f.2;
let len = len.sqrt();
(f.0 / len, f.1 / len, f.2 / len)
};
let up = (0.0, 1.0, 0.0);
let s = (
f.1 * up.2 - f.2 * up.1,
f.2 * up.0 - f.0 * up.2,
f.0 * up.1 - f.1 * up.0,
);
let s = {
let len = s.0 * s.0 + s.1 * s.1 + s.2 * s.2;
let len = len.sqrt();
(s.0 / len, s.1 / len, s.2 / len)
};
let u = (
s.1 * f.2 - s.2 * f.1,
s.2 * f.0 - s.0 * f.2,
s.0 * f.1 - s.1 * f.0,
);
let speed = 0.1;
if self.moving_up {
self.position.0 += u.0 * speed;
self.position.1 += u.1 * speed;
self.position.2 += u.2 * speed;
}
if self.moving_left {
self.position.0 -= s.0 * speed;
self.position.1 -= s.1 * speed;
self.position.2 -= s.2 * speed;
}
if self.moving_down {
self.position.0 -= u.0 * speed;
self.position.1 -= u.1 * speed;
self.position.2 -= u.2 * speed;
}
if self.moving_right {
self.position.0 += s.0 * speed;
self.position.1 += s.1 * speed;
self.position.2 += s.2 * speed;
}
if self.moving_forward {
self.position.0 += f.0 * speed;
self.position.1 += f.1 * speed;
self.position.2 += f.2 * speed;
}
if self.moving_backward {
self.position.0 -= f.0 * speed;
self.position.1 -= f.1 * speed;
self.position.2 -= f.2 * speed;
}
}
pub fn process_input(&mut self, input: glutin::KeyboardInput) {
let pressed = input.state == glutin::ElementState::Pressed;
match input.virtual_keycode {
Some(glutin::VirtualKeyCode::Up) => self.moving_up = pressed,
Some(glutin::VirtualKeyCode::Down) => self.moving_down = pressed,
Some(glutin::VirtualKeyCode::Left) => self.moving_left = pressed,
Some(glutin::VirtualKeyCode::Right) => self.moving_right = pressed,
Some(glutin::VirtualKeyCode::Q) => self.moving_forward = pressed,
Some(glutin::VirtualKeyCode::A) => self.moving_backward = pressed,
_ => {}
};
self.update();
}
}

View File

@ -1,4 +1,5 @@
use crate::ScreenPt;
use glium::glutin;
use piston::input as pi;
#[derive(Clone, Copy, PartialEq)]
@ -24,6 +25,50 @@ pub enum Event {
}
impl Event {
pub fn from_glutin_event(ev: glutin::WindowEvent) -> Option<Event> {
match ev {
glutin::WindowEvent::MouseInput { state, button, .. } => match (button, state) {
(glutin::MouseButton::Left, glutin::ElementState::Pressed) => {
Some(Event::LeftMouseButtonDown)
}
(glutin::MouseButton::Left, glutin::ElementState::Released) => {
Some(Event::LeftMouseButtonUp)
}
(glutin::MouseButton::Right, glutin::ElementState::Pressed) => {
Some(Event::RightMouseButtonDown)
}
(glutin::MouseButton::Right, glutin::ElementState::Released) => {
Some(Event::RightMouseButtonUp)
}
_ => None,
},
glutin::WindowEvent::KeyboardInput { input, .. } => {
if let Some(key) = Key::from_glutin_key(input) {
if input.state == glutin::ElementState::Pressed {
Some(Event::KeyPress(key))
} else {
Some(Event::KeyRelease(key))
}
} else {
None
}
}
glutin::WindowEvent::CursorMoved { position, .. } => {
Some(Event::MouseMovedTo(ScreenPt::new(position.x, position.y)))
}
//glutin::WindowEvent::MouseWheel { delta, .. } => Event::MouseWheelScroll(),
glutin::WindowEvent::Resized(size) => {
Some(Event::WindowResized(size.width, size.height))
}
glutin::WindowEvent::Focused(gained) => Some(if gained {
Event::WindowGainedCursor
} else {
Event::WindowLostCursor
}),
_ => None,
}
}
pub fn from_piston_event(ev: pi::Event) -> Event {
use piston::input::{
ButtonEvent, CursorEvent, MouseCursorEvent, MouseScrollEvent, PressEvent, ReleaseEvent,
@ -348,4 +393,79 @@ impl Key {
}
})
}
fn from_glutin_key(input: glutin::KeyboardInput) -> Option<Key> {
let key = input.virtual_keycode?;
Some(match key {
glutin::VirtualKeyCode::A => Key::A,
glutin::VirtualKeyCode::B => Key::B,
glutin::VirtualKeyCode::C => Key::C,
glutin::VirtualKeyCode::D => Key::D,
glutin::VirtualKeyCode::E => Key::E,
glutin::VirtualKeyCode::F => Key::F,
glutin::VirtualKeyCode::G => Key::G,
glutin::VirtualKeyCode::H => Key::H,
glutin::VirtualKeyCode::I => Key::I,
glutin::VirtualKeyCode::J => Key::J,
glutin::VirtualKeyCode::K => Key::K,
glutin::VirtualKeyCode::L => Key::L,
glutin::VirtualKeyCode::M => Key::M,
glutin::VirtualKeyCode::N => Key::N,
glutin::VirtualKeyCode::O => Key::O,
glutin::VirtualKeyCode::P => Key::P,
glutin::VirtualKeyCode::Q => Key::Q,
glutin::VirtualKeyCode::R => Key::R,
glutin::VirtualKeyCode::S => Key::S,
glutin::VirtualKeyCode::T => Key::T,
glutin::VirtualKeyCode::U => Key::U,
glutin::VirtualKeyCode::V => Key::V,
glutin::VirtualKeyCode::W => Key::W,
glutin::VirtualKeyCode::X => Key::X,
glutin::VirtualKeyCode::Y => Key::Y,
glutin::VirtualKeyCode::Z => Key::Z,
glutin::VirtualKeyCode::Key1 => Key::Num1,
glutin::VirtualKeyCode::Key2 => Key::Num2,
glutin::VirtualKeyCode::Key3 => Key::Num3,
glutin::VirtualKeyCode::Key4 => Key::Num4,
glutin::VirtualKeyCode::Key5 => Key::Num5,
glutin::VirtualKeyCode::Key6 => Key::Num6,
glutin::VirtualKeyCode::Key7 => Key::Num7,
glutin::VirtualKeyCode::Key8 => Key::Num8,
glutin::VirtualKeyCode::Key9 => Key::Num9,
glutin::VirtualKeyCode::Key0 => Key::Num0,
glutin::VirtualKeyCode::LBracket => Key::LeftBracket,
glutin::VirtualKeyCode::RBracket => Key::RightBracket,
glutin::VirtualKeyCode::Space => Key::Space,
glutin::VirtualKeyCode::Slash => Key::Slash,
glutin::VirtualKeyCode::Period => Key::Dot,
glutin::VirtualKeyCode::Comma => Key::Comma,
glutin::VirtualKeyCode::Escape => Key::Escape,
glutin::VirtualKeyCode::Return => Key::Enter,
glutin::VirtualKeyCode::Tab => Key::Tab,
glutin::VirtualKeyCode::Back => Key::Backspace,
glutin::VirtualKeyCode::LShift => Key::LeftShift,
glutin::VirtualKeyCode::LControl => Key::LeftControl,
glutin::VirtualKeyCode::LAlt => Key::LeftAlt,
glutin::VirtualKeyCode::Left => Key::LeftArrow,
glutin::VirtualKeyCode::Right => Key::RightArrow,
glutin::VirtualKeyCode::Up => Key::UpArrow,
glutin::VirtualKeyCode::Down => Key::DownArrow,
glutin::VirtualKeyCode::F1 => Key::F1,
glutin::VirtualKeyCode::F2 => Key::F2,
glutin::VirtualKeyCode::F3 => Key::F3,
glutin::VirtualKeyCode::F4 => Key::F4,
glutin::VirtualKeyCode::F5 => Key::F5,
glutin::VirtualKeyCode::F6 => Key::F6,
glutin::VirtualKeyCode::F7 => Key::F7,
glutin::VirtualKeyCode::F8 => Key::F8,
glutin::VirtualKeyCode::F9 => Key::F9,
glutin::VirtualKeyCode::F10 => Key::F10,
glutin::VirtualKeyCode::F11 => Key::F11,
glutin::VirtualKeyCode::F12 => Key::F12,
_ => {
println!("Unknown glutin key {:?}", key);
return None;
}
})
}
}

8
ezgui/src/fragment.glsl Normal file
View File

@ -0,0 +1,8 @@
#version 140
in vec4 pass_color;
out vec4 f_color;
void main() {
f_color = pass_color;
}

View File

@ -1,3 +1,4 @@
mod camera;
mod canvas;
mod color;
mod event;
@ -12,6 +13,7 @@ mod text_box;
mod top_menu;
mod wizard;
use crate::camera::CameraState;
pub use crate::canvas::{Canvas, HorizontalAlignment, VerticalAlignment, BOTTOM_LEFT, CENTERED};
pub use crate::color::Color;
pub use crate::event::{Event, Key};
@ -25,6 +27,7 @@ pub use crate::text_box::TextBox;
pub use crate::top_menu::{Folder, TopMenu};
pub use crate::wizard::{Wizard, WrappedWizard};
use geom::Pt2D;
use glium::{implement_vertex, uniform, Surface};
use graphics::Transformed;
use opengl_graphics::GlGraphics;
use std::mem;
@ -212,3 +215,189 @@ pub enum InputResult<T: Clone> {
StillActive,
Done(String, T),
}
#[derive(Copy, Clone)]
struct Vertex {
position: [f32; 2],
// TODO Maybe pass color as a uniform instead
color: [f32; 4],
}
implement_vertex!(Vertex, position, color);
type Uniforms<'a> = glium::uniforms::UniformsStorage<
'a,
[[f32; 4]; 4],
glium::uniforms::UniformsStorage<'a, [[f32; 4]; 4], glium::uniforms::EmptyUniforms>,
>;
pub struct NewGfxCtx<'a> {
display: &'a glium::Display,
target: &'a mut glium::Frame,
program: &'a glium::Program,
uniforms: Uniforms<'a>,
params: glium::DrawParameters<'a>,
}
impl<'a> NewGfxCtx<'a> {
pub fn new(
display: &'a glium::Display,
target: &'a mut glium::Frame,
program: &'a glium::Program,
) -> NewGfxCtx<'a> {
let params = glium::DrawParameters {
depth: glium::Depth {
test: glium::DepthTest::IfLess,
write: true,
..Default::default()
},
..Default::default()
};
let mut camera = CameraState::new();
// TODO setup camera based on canvas
let uniforms = uniform! {
persp_matrix: camera.get_perspective(),
view_matrix: camera.get_view(),
};
NewGfxCtx {
display,
target,
program,
uniforms,
params,
}
}
// Up to the caller to call unfork()!
// TODO Canvas doesn't understand this change, so things like text drawing that use
// map_to_screen will just be confusing.
pub fn fork(&mut self, top_left: Pt2D, zoom: f64) -> Uniforms {
let mut camera = CameraState::new();
// TODO setup camera based on values above
let mut uniforms = uniform! {
persp_matrix: camera.get_perspective(),
view_matrix: camera.get_view(),
};
mem::swap(&mut self.uniforms, &mut uniforms);
uniforms
}
pub fn fork_screenspace(&mut self) -> Uniforms {
self.fork(Pt2D::new(0.0, 0.0), 1.0)
}
pub fn unfork(&mut self, old_uniforms: Uniforms<'a>) {
// TODO What do we need to do to re-upload?
self.uniforms = old_uniforms;
}
pub fn clear(&mut self, color: Color) {
self.target
.clear_color_and_depth((color.0[0], color.0[1], color.0[2], color.0[3]), 1.0);
}
// Use graphics::Line internally for now, but make it easy to switch to something else by
// picking this API now.
pub fn draw_line(&mut self, color: Color, thickness: f64, line: &geom::Line) {
self.draw_polygon(color, &line.to_polyline().make_polygons(thickness));
}
pub fn draw_rounded_line(&mut self, color: Color, thickness: f64, line: &geom::Line) {
self.draw_line(color, thickness, line);
self.draw_circle(color, &geom::Circle::new(line.pt1(), thickness / 2.0));
self.draw_circle(color, &geom::Circle::new(line.pt2(), thickness / 2.0));
}
pub fn draw_arrow(&mut self, color: Color, thickness: f64, line: &geom::Line) {
// TODO Raw method doesn't work yet in all cases...
/*graphics::Line::new_round(color.0, thickness).draw_arrow(
[
line.pt1().x(),
line.pt1().y(),
line.pt2().x(),
line.pt2().y(),
],
2.0 * thickness,
&self.ctx.draw_state,
self.ctx.transform,
self.gfx,
);*/
/*use dimensioned::si;
let head_size = 2.0 * thickness;
let angle = line.angle();
let triangle_height = (head_size / 2.0).sqrt() * si::M;
self.draw_polygon(
color,
&geom::Polygon::new(&vec![
//line.pt2(),
//line.pt2().project_away(head_size, angle.rotate_degs(-135.0)),
line.reverse()
.dist_along(triangle_height)
.project_away(thickness / 2.0, angle.rotate_degs(90.0)),
line.pt1()
.project_away(thickness / 2.0, angle.rotate_degs(90.0)),
line.pt1()
.project_away(thickness / 2.0, angle.rotate_degs(-90.0)),
line.reverse()
.dist_along(triangle_height)
.project_away(thickness / 2.0, angle.rotate_degs(-90.0)),
//line.pt2().project_away(head_size, angle.rotate_degs(135.0)),
]),
);
self.draw_polygon(
color,
&geom::Polygon::new(&vec![
line.pt2(),
line.pt2()
.project_away(head_size, angle.rotate_degs(-135.0)),
line.pt2().project_away(head_size, angle.rotate_degs(135.0)),
]),
);*/
}
pub fn draw_polygon(&mut self, color: Color, poly: &geom::Polygon) {
for tri in &poly.triangles {
let vb = glium::VertexBuffer::new(
self.display,
&[
Vertex {
position: [tri.pt1.x() as f32, tri.pt1.y() as f32],
color: color.0,
},
Vertex {
position: [tri.pt2.x() as f32, tri.pt2.y() as f32],
color: color.0,
},
Vertex {
position: [tri.pt3.x() as f32, tri.pt3.y() as f32],
color: color.0,
},
],
)
.unwrap();
let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);
self.target
.draw(&vb, &indices, &self.program, &self.uniforms, &self.params)
.unwrap();
}
}
pub fn draw_circle(&mut self, color: Color, circle: &geom::Circle) {
/*graphics::Ellipse::new(color.0).draw(
[
circle.center.x() - circle.radius,
circle.center.y() - circle.radius,
2.0 * circle.radius,
2.0 * circle.radius,
],
&self.ctx.draw_state,
self.ctx.transform,
self.gfx,
);*/
}
}

View File

@ -1,12 +1,14 @@
use crate::input::{ContextMenu, ModalMenuState};
use crate::{Canvas, Event, GfxCtx, ModalMenu, TopMenu, UserInput};
use crate::{Canvas, Event, GfxCtx, ModalMenu, NewGfxCtx, TopMenu, UserInput};
use abstutil::Timer;
use glium::glutin;
use glutin_window::GlutinWindow;
use opengl_graphics::{GlGraphics, OpenGL};
use piston::event_loop::{EventLoop, EventSettings, Events};
use piston::window::WindowSettings;
use std::io::Write;
use std::{env, fs, panic, process};
use std::time::{Duration, Instant};
use std::{env, fs, panic, process, thread};
pub trait GUI<T> {
// Called once
@ -172,6 +174,13 @@ impl<T, G: GUI<T>> State<T, G> {
self
}
fn new_draw(&mut self, display: &glium::Display, program: &glium::Program) {
let mut target = display.draw();
// TODO call draw
NewGfxCtx::new(&display, &mut target, program);
target.finish().unwrap();
}
fn draw(&mut self, g: &mut GfxCtx) {
// If the very first event is render, then just wait.
if let Some(ref data) = self.last_data {
@ -309,3 +318,67 @@ impl ScreenCaptureState {
writeln!(file, "rm -f combine.sh").unwrap();
}
}
pub fn new_run<T, G: GUI<T>>(mut gui: G, window_title: &str) {
// DPI is broken on my system; force the old behavior.
env::set_var("WINIT_HIDPI_FACTOR", "1.0");
let mut events_loop = glutin::EventsLoop::new();
let window = glutin::WindowBuilder::new()
.with_title(window_title)
.with_dimensions(glutin::dpi::LogicalSize::new(
gui.get_mut_canvas().window_width,
gui.get_mut_canvas().window_height,
));
let context = glutin::ContextBuilder::new().with_depth_buffer(24);
let display = glium::Display::new(window, context, &events_loop).unwrap();
let program = glium::Program::from_source(
&display,
include_str!("vertex.glsl"),
include_str!("fragment.glsl"),
None,
)
.unwrap();
let mut state = State {
last_event_mode: EventLoopMode::InputOnly,
context_menu: ContextMenu::Inactive,
top_menu: gui.top_menu(),
modal_state: ModalMenuState::new(G::modal_menus()),
last_data: None,
screen_cap: None,
gui,
};
let mut accumulator = Duration::new(0, 0);
let mut previous_clock = Instant::now();
loop {
state.new_draw(&display, &program);
state.after_render();
events_loop.poll_events(|event| {
if let glutin::Event::WindowEvent { event, .. } = event {
if event == glutin::WindowEvent::CloseRequested {
state.gui.before_quit();
process::exit(0);
}
if state.screen_cap.is_none() {
// TODO manage laziness differently
//state = state.event(event, &mut events);
}
}
});
let now = Instant::now();
accumulator += now - previous_clock;
previous_clock = now;
let fixed_time_stamp = Duration::new(0, 16_666_667);
while accumulator >= fixed_time_stamp {
accumulator -= fixed_time_stamp;
// TODO send off an update event
}
thread::sleep(fixed_time_stamp - accumulator);
}
}

15
ezgui/src/vertex.glsl Normal file
View File

@ -0,0 +1,15 @@
#version 140
uniform mat4 persp_matrix;
uniform mat4 view_matrix;
in vec2 position;
in vec4 color;
out vec4 pass_color;
void main() {
pass_color = color;
gl_Position = vec4(position, 0.0, 1.0);
gl_Position = persp_matrix * view_matrix * vec4(position, 0.0, 1.0);
}