2019-01-25 03:09:29 +03:00
|
|
|
use crate::{Canvas, Color, ScreenPt};
|
|
|
|
use geom::{Circle, Line, Polygon, Pt2D};
|
|
|
|
use glium::{implement_vertex, uniform, Surface};
|
|
|
|
|
2019-01-25 03:25:06 +03:00
|
|
|
const TRIANGLES_PER_CIRCLE: usize = 60;
|
|
|
|
|
2019-01-25 03:09:29 +03:00
|
|
|
#[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; 2],
|
|
|
|
glium::uniforms::UniformsStorage<'a, [f32; 3], glium::uniforms::EmptyUniforms>,
|
|
|
|
>;
|
|
|
|
|
|
|
|
pub struct GfxCtx<'a> {
|
|
|
|
display: &'a glium::Display,
|
|
|
|
target: &'a mut glium::Frame,
|
|
|
|
program: &'a glium::Program,
|
|
|
|
uniforms: Uniforms<'a>,
|
|
|
|
params: glium::DrawParameters<'a>,
|
2019-01-25 03:19:02 +03:00
|
|
|
|
|
|
|
pub num_new_uploads: usize,
|
|
|
|
pub num_draw_calls: usize,
|
2019-01-25 03:09:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> GfxCtx<'a> {
|
|
|
|
pub fn new(
|
|
|
|
canvas: &Canvas,
|
|
|
|
display: &'a glium::Display,
|
|
|
|
target: &'a mut glium::Frame,
|
|
|
|
program: &'a glium::Program,
|
|
|
|
) -> GfxCtx<'a> {
|
|
|
|
let params = glium::DrawParameters {
|
|
|
|
blend: glium::Blend::alpha_blending(),
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
|
|
|
|
let uniforms = uniform! {
|
|
|
|
transform: [canvas.cam_x as f32, canvas.cam_y as f32, canvas.cam_zoom as f32],
|
|
|
|
window: [canvas.window_width as f32, canvas.window_height as f32],
|
|
|
|
};
|
|
|
|
|
|
|
|
GfxCtx {
|
|
|
|
display,
|
|
|
|
target,
|
|
|
|
program,
|
|
|
|
uniforms,
|
|
|
|
params,
|
2019-01-25 03:19:02 +03:00
|
|
|
num_new_uploads: 0,
|
|
|
|
num_draw_calls: 0,
|
2019-01-25 03:09:29 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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_map: Pt2D,
|
|
|
|
top_left_screen: ScreenPt,
|
|
|
|
zoom: f64,
|
|
|
|
canvas: &Canvas,
|
|
|
|
) {
|
|
|
|
// map_to_screen of top_left_map should be top_left_screen
|
|
|
|
let cam_x = (top_left_map.x() * zoom) - top_left_screen.x;
|
|
|
|
let cam_y = (top_left_map.y() * zoom) - top_left_screen.y;
|
|
|
|
|
|
|
|
self.uniforms = uniform! {
|
|
|
|
transform: [cam_x as f32, cam_y as f32, zoom as f32],
|
|
|
|
window: [canvas.window_width as f32, canvas.window_height as f32],
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn fork_screenspace(&mut self, canvas: &Canvas) {
|
|
|
|
self.uniforms = uniform! {
|
|
|
|
transform: [0.0, 0.0, 1.0],
|
|
|
|
window: [canvas.window_width as f32, canvas.window_height as f32],
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn unfork(&mut self, canvas: &Canvas) {
|
|
|
|
self.uniforms = uniform! {
|
|
|
|
transform: [canvas.cam_x as f32, canvas.cam_y as f32, canvas.cam_zoom as f32],
|
|
|
|
window: [canvas.window_width as f32, canvas.window_height as f32],
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn clear(&mut self, color: Color) {
|
|
|
|
// Without this, SRGB gets enabled and post-processes the color from the fragment shader.
|
|
|
|
self.target
|
|
|
|
.clear_color_srgb(color.0[0], color.0[1], color.0[2], color.0[3]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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: &Line) {
|
2019-01-25 19:01:08 +03:00
|
|
|
self.draw_polygon(color, &line.make_polygons(thickness));
|
2019-01-25 03:09:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn draw_rounded_line(&mut self, color: Color, thickness: f64, line: &Line) {
|
2019-01-25 19:14:35 +03:00
|
|
|
self.draw_polygon_batch(vec![
|
2019-01-25 19:01:08 +03:00
|
|
|
(color, &line.make_polygons(thickness)),
|
2019-01-25 03:25:06 +03:00
|
|
|
(
|
|
|
|
color,
|
|
|
|
&Circle::new(line.pt1(), thickness / 2.0).to_polygon(TRIANGLES_PER_CIRCLE),
|
|
|
|
),
|
|
|
|
(
|
|
|
|
color,
|
|
|
|
&Circle::new(line.pt2(), thickness / 2.0).to_polygon(TRIANGLES_PER_CIRCLE),
|
|
|
|
),
|
2019-01-25 19:14:35 +03:00
|
|
|
]);
|
2019-01-25 03:09:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn draw_arrow(&mut self, color: Color, thickness: f64, line: &Line) {
|
2019-01-25 21:59:39 +03:00
|
|
|
let polygons = line.make_arrow(thickness);
|
|
|
|
self.draw_polygon_batch(polygons.iter().map(|poly| (color, poly)).collect());
|
2019-01-25 03:09:29 +03:00
|
|
|
}
|
|
|
|
|
2019-01-25 03:25:06 +03:00
|
|
|
pub fn draw_circle(&mut self, color: Color, circle: &Circle) {
|
|
|
|
self.draw_polygon(color, &circle.to_polygon(TRIANGLES_PER_CIRCLE));
|
|
|
|
}
|
|
|
|
|
2019-01-25 03:09:29 +03:00
|
|
|
pub fn draw_polygon(&mut self, color: Color, poly: &Polygon) {
|
2019-01-25 21:59:39 +03:00
|
|
|
let obj = Prerender {
|
|
|
|
display: self.display,
|
|
|
|
}
|
|
|
|
.upload_borrowed(vec![(color, poly)]);
|
|
|
|
self.num_new_uploads += 1;
|
|
|
|
self.redraw(&obj);
|
2019-01-25 03:25:06 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn draw_polygon_batch(&mut self, list: Vec<(Color, &Polygon)>) {
|
2019-01-25 20:27:53 +03:00
|
|
|
let obj = Prerender {
|
|
|
|
display: self.display,
|
|
|
|
}
|
2019-01-25 21:59:39 +03:00
|
|
|
.upload_borrowed(list);
|
2019-01-25 20:27:53 +03:00
|
|
|
self.num_new_uploads += 1;
|
2019-01-25 21:24:29 +03:00
|
|
|
self.redraw(&obj);
|
2019-01-25 20:09:55 +03:00
|
|
|
}
|
|
|
|
|
2019-01-25 21:24:29 +03:00
|
|
|
pub fn redraw(&mut self, obj: &Drawable) {
|
2019-01-25 20:27:53 +03:00
|
|
|
self.target
|
|
|
|
.draw(
|
|
|
|
&obj.vertex_buffer,
|
|
|
|
&obj.index_buffer,
|
|
|
|
&self.program,
|
|
|
|
&self.uniforms,
|
|
|
|
&self.params,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
self.num_draw_calls += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Prerender<'a> {
|
2019-01-25 20:48:33 +03:00
|
|
|
pub(crate) display: &'a glium::Display,
|
2019-01-25 20:27:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Something that's been sent to the GPU already.
|
|
|
|
pub struct Drawable {
|
|
|
|
vertex_buffer: glium::VertexBuffer<Vertex>,
|
|
|
|
index_buffer: glium::IndexBuffer<u32>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Prerender<'a> {
|
2019-01-25 21:59:39 +03:00
|
|
|
pub fn upload_borrowed(&self, list: Vec<(Color, &Polygon)>) -> Drawable {
|
2019-01-25 03:25:06 +03:00
|
|
|
let mut vertices: Vec<Vertex> = Vec::new();
|
|
|
|
let mut indices: Vec<u32> = Vec::new();
|
|
|
|
|
|
|
|
for (color, poly) in list {
|
2019-01-25 19:01:08 +03:00
|
|
|
let idx_offset = vertices.len();
|
2019-01-25 03:25:06 +03:00
|
|
|
let (pts, raw_indices) = poly.raw_for_rendering();
|
|
|
|
for pt in pts {
|
|
|
|
vertices.push(Vertex {
|
|
|
|
position: [pt.x() as f32, pt.y() as f32],
|
|
|
|
color: color.0,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
for idx in raw_indices {
|
2019-01-25 19:01:08 +03:00
|
|
|
indices.push((idx_offset + *idx) as u32);
|
2019-01-25 03:25:06 +03:00
|
|
|
}
|
|
|
|
}
|
2019-01-25 03:09:29 +03:00
|
|
|
|
|
|
|
let vertex_buffer = glium::VertexBuffer::new(self.display, &vertices).unwrap();
|
|
|
|
let index_buffer = glium::IndexBuffer::new(
|
|
|
|
self.display,
|
|
|
|
glium::index::PrimitiveType::TrianglesList,
|
|
|
|
&indices,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2019-01-25 20:09:55 +03:00
|
|
|
Drawable {
|
2019-01-25 20:27:53 +03:00
|
|
|
vertex_buffer,
|
|
|
|
index_buffer,
|
|
|
|
}
|
2019-01-25 03:09:29 +03:00
|
|
|
}
|
2019-01-25 21:59:39 +03:00
|
|
|
|
|
|
|
pub fn upload(&self, list: Vec<(Color, Polygon)>) -> Drawable {
|
|
|
|
let borrows = list.iter().map(|(c, p)| (*c, p)).collect();
|
|
|
|
self.upload_borrowed(borrows)
|
|
|
|
}
|
2019-01-25 03:09:29 +03:00
|
|
|
}
|