2019-11-29 09:41:08 +03:00
|
|
|
use crate::assets::Assets;
|
2020-02-11 07:23:12 +03:00
|
|
|
use crate::backend::{GfxCtxInnards, PrerenderInnards};
|
2019-10-09 21:21:04 +03:00
|
|
|
use crate::{
|
2020-04-05 21:57:27 +03:00
|
|
|
Canvas, Color, Drawable, FancyColor, GeomBatch, ScreenDims, ScreenPt, ScreenRectangle, Style,
|
|
|
|
Text,
|
2019-10-09 21:21:04 +03:00
|
|
|
};
|
2020-05-14 01:31:29 +03:00
|
|
|
use geom::{ArrowCap, Bounds, Circle, Distance, Line, Polygon, Pt2D};
|
2019-09-11 19:03:59 +03:00
|
|
|
use std::cell::Cell;
|
|
|
|
|
2020-02-07 22:20:11 +03:00
|
|
|
// Lower is more on top
|
|
|
|
const MAPSPACE_Z: f32 = 1.0;
|
|
|
|
const SCREENSPACE_Z: f32 = 0.5;
|
|
|
|
const TOOLTIP_Z: f32 = 0.0;
|
2019-01-25 03:09:29 +03:00
|
|
|
|
2020-02-11 03:18:31 +03:00
|
|
|
pub struct Uniforms {
|
2019-09-11 00:08:05 +03:00
|
|
|
// (cam_x, cam_y, cam_zoom)
|
2020-02-11 03:18:31 +03:00
|
|
|
pub transform: [f32; 3],
|
2020-03-09 19:40:59 +03:00
|
|
|
// (window_width, window_height, Z values)
|
2020-02-11 03:18:31 +03:00
|
|
|
pub window: [f32; 3],
|
2019-09-11 00:08:05 +03:00
|
|
|
}
|
|
|
|
|
2020-02-11 03:18:31 +03:00
|
|
|
impl Uniforms {
|
|
|
|
pub fn new(canvas: &Canvas) -> Uniforms {
|
2019-09-11 00:08:05 +03:00
|
|
|
Uniforms {
|
|
|
|
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,
|
2020-02-07 22:20:11 +03:00
|
|
|
MAPSPACE_Z,
|
2019-09-11 00:08:05 +03:00
|
|
|
],
|
2019-09-11 01:44:07 +03:00
|
|
|
}
|
2019-09-11 00:08:05 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-25 03:09:29 +03:00
|
|
|
pub struct GfxCtx<'a> {
|
2020-02-11 03:18:31 +03:00
|
|
|
pub(crate) inner: GfxCtxInnards<'a>,
|
|
|
|
uniforms: Uniforms,
|
2019-01-25 03:19:02 +03:00
|
|
|
|
2019-04-24 01:33:52 +03:00
|
|
|
screencap_mode: bool,
|
|
|
|
pub(crate) naming_hint: Option<String>,
|
|
|
|
|
2019-02-01 03:43:09 +03:00
|
|
|
// TODO Don't be pub. Delegate everything.
|
|
|
|
pub canvas: &'a Canvas,
|
2020-02-10 21:01:49 +03:00
|
|
|
pub prerender: &'a Prerender,
|
2020-04-05 21:57:27 +03:00
|
|
|
style: &'a Style,
|
2019-02-01 03:43:09 +03:00
|
|
|
|
2019-01-25 03:19:02 +03:00
|
|
|
pub num_draw_calls: usize,
|
2020-02-09 00:52:29 +03:00
|
|
|
pub num_forks: usize,
|
2019-01-25 03:09:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> GfxCtx<'a> {
|
2019-02-06 01:43:46 +03:00
|
|
|
pub(crate) fn new(
|
2020-02-10 21:01:49 +03:00
|
|
|
prerender: &'a Prerender,
|
2020-02-11 03:18:31 +03:00
|
|
|
canvas: &'a Canvas,
|
2020-04-05 21:57:27 +03:00
|
|
|
style: &'a Style,
|
2019-04-24 01:33:52 +03:00
|
|
|
screencap_mode: bool,
|
2019-01-25 03:09:29 +03:00
|
|
|
) -> GfxCtx<'a> {
|
2020-02-11 03:18:31 +03:00
|
|
|
let uniforms = Uniforms::new(canvas);
|
2019-01-25 03:09:29 +03:00
|
|
|
GfxCtx {
|
2020-02-11 03:18:31 +03:00
|
|
|
inner: prerender.inner.draw_new_frame(),
|
|
|
|
uniforms,
|
2019-02-01 03:43:09 +03:00
|
|
|
canvas,
|
2020-04-05 21:57:27 +03:00
|
|
|
style,
|
2019-02-06 01:43:46 +03:00
|
|
|
prerender,
|
2019-01-25 03:19:02 +03:00
|
|
|
num_draw_calls: 0,
|
2020-02-09 00:52:29 +03:00
|
|
|
num_forks: 0,
|
2019-04-24 01:33:52 +03:00
|
|
|
screencap_mode,
|
|
|
|
naming_hint: None,
|
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.
|
2020-03-09 19:40:59 +03:00
|
|
|
pub fn fork(
|
|
|
|
&mut self,
|
|
|
|
top_left_map: Pt2D,
|
|
|
|
top_left_screen: ScreenPt,
|
|
|
|
zoom: f64,
|
|
|
|
z: Option<f32>,
|
|
|
|
) {
|
2019-01-25 03:09:29 +03:00
|
|
|
// 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;
|
|
|
|
|
2019-09-11 00:08:05 +03:00
|
|
|
self.uniforms.transform = [cam_x as f32, cam_y as f32, zoom as f32];
|
|
|
|
self.uniforms.window = [
|
|
|
|
self.canvas.window_width as f32,
|
|
|
|
self.canvas.window_height as f32,
|
2020-03-09 19:40:59 +03:00
|
|
|
z.unwrap_or(SCREENSPACE_Z),
|
2019-09-11 00:08:05 +03:00
|
|
|
];
|
2020-02-09 00:52:29 +03:00
|
|
|
self.num_forks += 1;
|
2019-01-25 03:09:29 +03:00
|
|
|
}
|
|
|
|
|
2019-02-01 03:43:09 +03:00
|
|
|
pub fn fork_screenspace(&mut self) {
|
2019-09-11 00:08:05 +03:00
|
|
|
self.uniforms.transform = [0.0, 0.0, 1.0];
|
|
|
|
self.uniforms.window = [
|
|
|
|
self.canvas.window_width as f32,
|
|
|
|
self.canvas.window_height as f32,
|
2020-02-07 22:20:11 +03:00
|
|
|
SCREENSPACE_Z,
|
2019-09-11 00:08:05 +03:00
|
|
|
];
|
2020-02-09 00:52:29 +03:00
|
|
|
self.num_forks += 1;
|
2019-01-25 03:09:29 +03:00
|
|
|
}
|
|
|
|
|
2019-02-01 03:43:09 +03:00
|
|
|
pub fn unfork(&mut self) {
|
2019-09-11 00:08:05 +03:00
|
|
|
self.uniforms = Uniforms::new(&self.canvas);
|
2020-02-09 00:52:29 +03:00
|
|
|
self.num_forks += 1;
|
2019-01-25 03:09:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn clear(&mut self, color: Color) {
|
2020-02-11 03:18:31 +03:00
|
|
|
self.inner.clear(color);
|
2019-01-25 03:09:29 +03:00
|
|
|
}
|
|
|
|
|
2019-01-31 02:10:05 +03:00
|
|
|
pub fn draw_line(&mut self, color: Color, thickness: Distance, 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
|
|
|
}
|
|
|
|
|
2019-01-31 02:10:05 +03:00
|
|
|
pub fn draw_rounded_line(&mut self, color: Color, thickness: Distance, line: &Line) {
|
2019-04-12 03:04:59 +03:00
|
|
|
self.draw_polygons(
|
|
|
|
color,
|
|
|
|
&vec![
|
|
|
|
line.make_polygons(thickness),
|
|
|
|
Circle::new(line.pt1(), thickness / 2.0).to_polygon(),
|
|
|
|
Circle::new(line.pt2(), thickness / 2.0).to_polygon(),
|
|
|
|
],
|
|
|
|
);
|
2019-01-25 03:09:29 +03:00
|
|
|
}
|
|
|
|
|
2019-01-31 02:10:05 +03:00
|
|
|
pub fn draw_arrow(&mut self, color: Color, thickness: Distance, line: &Line) {
|
2020-05-14 01:31:29 +03:00
|
|
|
self.draw_polygon(
|
|
|
|
color,
|
2020-07-12 01:17:16 +03:00
|
|
|
&line.to_polyline().make_arrow(thickness, ArrowCap::Triangle),
|
2020-05-14 01:31:29 +03:00
|
|
|
);
|
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) {
|
2019-02-02 23:54:16 +03:00
|
|
|
self.draw_polygon(color, &circle.to_polygon());
|
2019-01-25 03:25:06 +03:00
|
|
|
}
|
|
|
|
|
2019-01-25 03:09:29 +03:00
|
|
|
pub fn draw_polygon(&mut self, color: Color, poly: &Polygon) {
|
2020-03-31 01:48:23 +03:00
|
|
|
let obj = self
|
|
|
|
.prerender
|
2020-03-31 02:15:32 +03:00
|
|
|
.upload_temporary(vec![(FancyColor::RGBA(color), poly)]);
|
2019-01-25 21:59:39 +03:00
|
|
|
self.redraw(&obj);
|
2019-01-25 03:25:06 +03:00
|
|
|
}
|
|
|
|
|
2019-04-12 03:04:59 +03:00
|
|
|
pub fn draw_polygons(&mut self, color: Color, polygons: &Vec<Polygon>) {
|
2020-03-31 01:48:23 +03:00
|
|
|
let obj = self.prerender.upload_temporary(
|
|
|
|
polygons
|
|
|
|
.iter()
|
2020-03-31 02:15:32 +03:00
|
|
|
.map(|p| (FancyColor::RGBA(color), p))
|
2020-03-31 01:48:23 +03:00
|
|
|
.collect(),
|
|
|
|
);
|
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) {
|
2020-02-11 03:18:31 +03:00
|
|
|
self.inner
|
|
|
|
.redraw(obj, &self.uniforms, &self.prerender.inner);
|
2019-01-25 20:27:53 +03:00
|
|
|
self.num_draw_calls += 1;
|
2019-05-17 03:10:22 +03:00
|
|
|
|
|
|
|
// println!("{:?}", backtrace::Backtrace::new());
|
2019-01-25 20:27:53 +03:00
|
|
|
}
|
|
|
|
|
2020-02-07 10:08:17 +03:00
|
|
|
pub fn redraw_at(&mut self, top_left: ScreenPt, obj: &Drawable) {
|
2020-03-09 19:40:59 +03:00
|
|
|
self.fork(Pt2D::new(0.0, 0.0), top_left, 1.0, None);
|
2020-02-07 10:08:17 +03:00
|
|
|
self.redraw(obj);
|
|
|
|
self.unfork();
|
|
|
|
}
|
|
|
|
|
2020-01-14 03:57:09 +03:00
|
|
|
// TODO Stateful API :(
|
|
|
|
pub fn enable_clipping(&mut self, rect: ScreenRectangle) {
|
2020-02-11 03:18:31 +03:00
|
|
|
self.inner.enable_clipping(rect, self.canvas);
|
2020-01-14 03:57:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn disable_clipping(&mut self) {
|
2020-02-11 07:10:58 +03:00
|
|
|
self.inner.disable_clipping(self.canvas);
|
2019-11-24 18:21:21 +03:00
|
|
|
}
|
|
|
|
|
2019-02-01 10:32:38 +03:00
|
|
|
// Canvas stuff.
|
|
|
|
|
2020-02-07 08:39:23 +03:00
|
|
|
pub fn draw_mouse_tooltip(&mut self, txt: Text) {
|
2020-02-27 22:28:57 +03:00
|
|
|
// Add some padding
|
|
|
|
let pad = 5.0;
|
|
|
|
|
2020-02-09 01:08:26 +03:00
|
|
|
let txt_batch = txt.render_g(self);
|
2020-02-27 22:28:57 +03:00
|
|
|
let raw_dims = txt_batch.get_dims();
|
|
|
|
let dims = ScreenDims::new(raw_dims.width + 2.0 * pad, raw_dims.height + 2.0 * pad);
|
|
|
|
|
2019-12-12 03:08:58 +03:00
|
|
|
// TODO Maybe also consider the cursor as a valid center
|
2019-11-24 18:21:30 +03:00
|
|
|
let pt = dims.top_left_for_corner(
|
2020-02-20 22:24:50 +03:00
|
|
|
ScreenPt::new(self.canvas.cursor_x, self.canvas.cursor_y + 20.0),
|
2019-10-09 21:21:04 +03:00
|
|
|
&self.canvas,
|
|
|
|
);
|
2020-02-07 04:13:42 +03:00
|
|
|
let mut batch = GeomBatch::new();
|
2020-02-27 22:28:57 +03:00
|
|
|
// TODO Outline?
|
|
|
|
batch.push(
|
|
|
|
Color::BLACK,
|
|
|
|
Polygon::rectangle(dims.width, dims.height).translate(pt.x, pt.y),
|
|
|
|
);
|
2020-06-05 21:14:17 +03:00
|
|
|
batch.append(txt_batch.translate(pt.x + pad, pt.y + pad));
|
2020-02-07 22:20:11 +03:00
|
|
|
|
|
|
|
// fork_screenspace, but with an even more prominent Z
|
|
|
|
self.uniforms.transform = [0.0, 0.0, 1.0];
|
|
|
|
self.uniforms.window = [
|
|
|
|
self.canvas.window_width as f32,
|
|
|
|
self.canvas.window_height as f32,
|
|
|
|
TOOLTIP_Z,
|
|
|
|
];
|
2020-02-09 00:52:29 +03:00
|
|
|
self.num_forks += 1;
|
2020-02-07 22:20:11 +03:00
|
|
|
// Temporarily disable clipping if needed.
|
2020-02-11 03:18:31 +03:00
|
|
|
let clip = self.inner.take_clip();
|
2020-02-07 04:13:42 +03:00
|
|
|
batch.draw(self);
|
|
|
|
self.unfork();
|
2020-02-11 03:18:31 +03:00
|
|
|
self.inner.restore_clip(clip);
|
2019-02-01 03:43:09 +03:00
|
|
|
}
|
2019-02-01 10:32:38 +03:00
|
|
|
|
2020-02-09 00:52:29 +03:00
|
|
|
pub fn get_screen_bounds(&self) -> Bounds {
|
|
|
|
self.canvas.get_screen_bounds()
|
|
|
|
}
|
|
|
|
|
2019-02-01 03:43:09 +03:00
|
|
|
pub fn screen_to_map(&self, pt: ScreenPt) -> Pt2D {
|
|
|
|
self.canvas.screen_to_map(pt)
|
|
|
|
}
|
2019-02-01 10:32:38 +03:00
|
|
|
|
2019-02-01 03:43:09 +03:00
|
|
|
pub fn get_cursor_in_map_space(&self) -> Option<Pt2D> {
|
|
|
|
self.canvas.get_cursor_in_map_space()
|
2019-01-25 21:59:39 +03:00
|
|
|
}
|
2019-02-06 01:43:46 +03:00
|
|
|
|
|
|
|
pub fn get_num_uploads(&self) -> usize {
|
|
|
|
self.prerender.num_uploads.get()
|
|
|
|
}
|
2019-04-24 01:33:52 +03:00
|
|
|
|
|
|
|
pub fn is_screencap(&self) -> bool {
|
|
|
|
self.screencap_mode
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_screencap_naming_hint(&mut self, hint: String) {
|
|
|
|
assert!(self.screencap_mode);
|
|
|
|
assert!(self.naming_hint.is_none());
|
|
|
|
self.naming_hint = Some(hint);
|
|
|
|
}
|
2019-05-03 22:49:27 +03:00
|
|
|
|
2019-08-15 00:32:02 +03:00
|
|
|
pub fn upload(&mut self, batch: GeomBatch) -> Drawable {
|
|
|
|
self.prerender.upload(batch)
|
|
|
|
}
|
2019-11-01 21:45:02 +03:00
|
|
|
|
2019-11-29 09:41:08 +03:00
|
|
|
// Delegation to assets
|
|
|
|
pub fn default_line_height(&self) -> f64 {
|
2020-03-02 23:02:25 +03:00
|
|
|
*self.prerender.assets.default_line_height.borrow()
|
2019-11-29 09:41:08 +03:00
|
|
|
}
|
2020-04-05 21:57:27 +03:00
|
|
|
|
|
|
|
pub fn style(&self) -> &Style {
|
|
|
|
&self.style
|
|
|
|
}
|
2019-01-25 03:09:29 +03:00
|
|
|
}
|
2019-05-17 03:10:22 +03:00
|
|
|
|
2019-09-11 19:03:59 +03:00
|
|
|
// TODO Don't expose this directly
|
2020-02-11 03:18:31 +03:00
|
|
|
// TODO Rename or something maybe. This actually owns all the permanent state of everything.
|
2020-02-10 21:01:49 +03:00
|
|
|
pub struct Prerender {
|
2020-02-11 03:18:31 +03:00
|
|
|
pub(crate) inner: PrerenderInnards,
|
2020-02-07 08:39:23 +03:00
|
|
|
pub(crate) assets: Assets,
|
2019-09-11 19:03:59 +03:00
|
|
|
pub(crate) num_uploads: Cell<usize>,
|
|
|
|
}
|
|
|
|
|
2020-02-10 21:01:49 +03:00
|
|
|
impl Prerender {
|
2019-09-11 19:03:59 +03:00
|
|
|
pub fn upload(&self, batch: GeomBatch) -> Drawable {
|
2020-03-31 01:48:23 +03:00
|
|
|
let borrows = batch.list.iter().map(|(c, p)| (c.clone(), p)).collect();
|
2019-09-11 19:03:59 +03:00
|
|
|
self.actually_upload(true, borrows)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_total_bytes_uploaded(&self) -> usize {
|
2020-02-11 03:18:31 +03:00
|
|
|
self.inner.total_bytes_uploaded.get()
|
2019-09-11 19:03:59 +03:00
|
|
|
}
|
|
|
|
|
2020-03-31 01:48:23 +03:00
|
|
|
pub(crate) fn upload_temporary(&self, list: Vec<(FancyColor, &Polygon)>) -> Drawable {
|
2019-09-11 19:03:59 +03:00
|
|
|
self.actually_upload(false, list)
|
|
|
|
}
|
|
|
|
|
2020-03-31 01:48:23 +03:00
|
|
|
fn actually_upload(&self, permanent: bool, list: Vec<(FancyColor, &Polygon)>) -> Drawable {
|
2019-12-22 21:52:41 +03:00
|
|
|
// println!("{:?}", backtrace::Backtrace::new());
|
2019-09-11 19:03:59 +03:00
|
|
|
self.num_uploads.set(self.num_uploads.get() + 1);
|
2020-02-11 03:18:31 +03:00
|
|
|
self.inner.actually_upload(permanent, list)
|
|
|
|
}
|
2019-09-11 19:03:59 +03:00
|
|
|
|
2020-02-11 03:18:31 +03:00
|
|
|
pub fn request_redraw(&self) {
|
|
|
|
self.inner.request_redraw()
|
|
|
|
}
|
2019-09-11 19:03:59 +03:00
|
|
|
}
|