generalize ezgui Color to also cleanly represent textured things

This commit is contained in:
Dustin Carlino 2019-09-10 16:41:35 -07:00
parent 5ef4ed16b8
commit 9867619c9c
8 changed files with 84 additions and 59 deletions

View File

@ -3,15 +3,18 @@ use std::fmt;
// Copy could be reconsidered, but eh
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Color(pub(crate) [f32; 4]);
pub enum Color {
RGBA(f32, f32, f32, f32),
// The texture ID to pass to the shader
Texture(f32),
}
impl fmt::Display for Color {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Color(r={}, g={}, b={}, a={})",
self.0[0], self.0[1], self.0[2], self.0[3]
)
match self {
Color::RGBA(r, g, b, a) => write!(f, "Color(r={}, g={}, b={}, a={})", r, g, b, a),
Color::Texture(id) => write!(f, "Color::Texture({})", id),
}
}
}
@ -36,28 +39,31 @@ impl Color {
}
pub const fn rgb_f(r: f32, g: f32, b: f32) -> Color {
Color([r, g, b, 1.0])
Color::RGBA(r, g, b, 1.0)
}
pub fn rgba(r: usize, g: usize, b: usize, a: f32) -> Color {
Color([
Color::RGBA(
(r as f32) / 255.0,
(g as f32) / 255.0,
(b as f32) / 255.0,
a,
])
)
}
pub const fn rgba_f(r: f32, g: f32, b: f32, a: f32) -> Color {
Color([r, g, b, a])
Color::RGBA(r, g, b, a)
}
pub const fn grey(f: f32) -> Color {
Color([f, f, f, 1.0])
Color::RGBA(f, f, f, 1.0)
}
pub const fn alpha(&self, a: f32) -> Color {
Color([self.0[0], self.0[1], self.0[2], a])
pub fn alpha(&self, a: f32) -> Color {
match self {
Color::RGBA(r, g, b, _) => Color::RGBA(*r, *g, *b, a),
_ => unreachable!(),
}
}
pub fn from_hex(raw: &str) -> Color {

View File

@ -136,9 +136,14 @@ impl<'a> GfxCtx<'a> {
}
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_and_depth((color.0[0], color.0[1], color.0[2], color.0[3]), 1.0);
match color {
Color::RGBA(r, g, b, a) => {
// Without this, SRGB gets enabled and post-processes the color from the fragment
// shader.
self.target.clear_color_srgb_and_depth((r, g, b, a), 1.0);
}
_ => unreachable!(),
}
}
pub fn draw_line(&mut self, color: Color, thickness: Distance, line: &Line) {

View File

@ -67,27 +67,24 @@ impl<'a> Prerender<'a> {
for (color, poly) in list {
let idx_offset = vertices.len();
let (pts, raw_indices) = poly.raw_for_rendering();
let bounds = poly.get_bounds();
let mut maybe_bounds = None;
for pt in pts {
// TODO Encode this more directly in Color!
let style = if color == Color::rgb(170, 211, 223) {
[
0.0,
((pt.x() - bounds.min_x) / (bounds.max_x - bounds.min_x)) as f32,
// TODO Maybe need to do y inversion here
((pt.y() - bounds.min_y) / (bounds.max_y - bounds.min_y)) as f32,
0.0,
]
} else if color == Color::rgb(200, 250, 204) {
[
1.0,
((pt.x() - bounds.min_x) / (bounds.max_x - bounds.min_x)) as f32,
// TODO Maybe need to do y inversion here
((pt.y() - bounds.min_y) / (bounds.max_y - bounds.min_y)) as f32,
0.0,
]
} else {
[color.0[0], color.0[1], color.0[2], color.0[3]]
let style = match color {
Color::RGBA(r, g, b, a) => [r, g, b, a],
Color::Texture(id) => {
if maybe_bounds.is_none() {
maybe_bounds = Some(poly.get_bounds());
}
let b = maybe_bounds.as_ref().unwrap();
[
id,
((pt.x() - b.min_x) / (b.max_x - b.min_x)) as f32,
// TODO Maybe need to do y inversion here
((pt.y() - b.min_y) / (b.max_y - b.min_y)) as f32,
0.0,
]
}
};
vertices.push(Vertex {
position: [pt.x() as f32, pt.y() as f32],
@ -167,6 +164,15 @@ impl<'a> EventCtx<'a> {
self.input.window_lost_cursor()
|| (!self.canvas.is_dragging() && self.input.get_moved_mouse().is_some())
}
pub fn texture(&self, filename: &str) -> Color {
for (idx, (name, _)) in self.canvas.textures.iter().enumerate() {
if filename == name {
return Color::Texture(idx as f32);
}
}
panic!("Don't know texture {}", filename);
}
}
pub struct LoadingScreen<'a> {

View File

@ -205,7 +205,10 @@ pub fn draw_text_bubble(
max_size = max_size.max(span.size);
SectionText {
text: &span.text,
color: span.fg_color.0,
color: match span.fg_color {
Color::RGBA(r, g, b, a) => [r, g, b, a],
_ => unreachable!(),
},
scale: Scale::uniform(span.size as f32),
..SectionText::default()
}
@ -273,7 +276,10 @@ pub fn draw_text_bubble_mapspace(
max_size = max_size.max(span.size);
SectionText {
text: &span.text,
color: span.fg_color.0,
color: match span.fg_color {
Color::RGBA(r, g, b, a) => [r, g, b, a],
_ => unreachable!(),
},
scale: Scale::uniform(span.size as f32),
..SectionText::default()
}

View File

@ -204,7 +204,7 @@ fn launch_savestate(test: &ABTest, ss_path: String, ui: &mut UI, ctx: &mut Event
&ui.primary.map,
&ui.primary.current_flags,
&ui.cs,
ctx.prerender,
ctx,
&mut timer,
);
timer.stop("setup primary");
@ -215,7 +215,7 @@ fn launch_savestate(test: &ABTest, ss_path: String, ui: &mut UI, ctx: &mut Event
&ss.secondary_map,
&ui.primary.current_flags,
&ui.cs,
ctx.prerender,
ctx,
&mut timer,
),
map: ss.secondary_map,

View File

@ -1,6 +1,6 @@
use crate::helpers::{ColorScheme, ID};
use crate::helpers::ID;
use crate::render::{DrawCtx, DrawOptions, Renderable};
use ezgui::{Color, GeomBatch, GfxCtx};
use ezgui::{EventCtx, GeomBatch, GfxCtx};
use geom::Polygon;
use map_model::{Area, AreaID, AreaType, Map};
@ -9,11 +9,13 @@ pub struct DrawArea {
}
impl DrawArea {
pub fn new(area: &Area, cs: &ColorScheme, batch: &mut GeomBatch) -> DrawArea {
pub fn new(area: &Area, ctx: &EventCtx, batch: &mut GeomBatch) -> DrawArea {
batch.push(
match area.area_type {
AreaType::Park => cs.get_def("park area", Color::rgb(200, 250, 204)),
AreaType::Water => cs.get_def("water area", Color::rgb(170, 211, 223)),
//AreaType::Park => cs.get_def("park area", Color::rgb(200, 250, 204)),
//AreaType::Water => cs.get_def("water area", Color::rgb(170, 211, 223)),
AreaType::Park => ctx.texture("assets/grass_texture.png"),
AreaType::Water => ctx.texture("assets/water_texture.png"),
},
area.polygon.clone(),
);

View File

@ -12,7 +12,7 @@ use crate::render::Renderable;
use crate::ui::{Flags, PerMapUI};
use aabb_quadtree::QuadTree;
use abstutil::{Cloneable, Timer};
use ezgui::{Color, Drawable, GeomBatch, GfxCtx, Prerender};
use ezgui::{Color, Drawable, EventCtx, GeomBatch, GfxCtx};
use geom::{Bounds, Circle, Distance, Duration, FindClosest};
use map_model::{
AreaID, BuildingID, BusStopID, DirectedRoadID, IntersectionID, Lane, LaneID, Map, RoadID,
@ -52,7 +52,7 @@ impl DrawMap {
map: &Map,
flags: &Flags,
cs: &ColorScheme,
prerender: &Prerender,
ctx: &EventCtx,
timer: &mut Timer,
) -> DrawMap {
let mut roads: Vec<DrawRoad> = Vec::new();
@ -60,7 +60,7 @@ impl DrawMap {
timer.start_iter("make DrawRoads", map.all_roads().len());
for r in map.all_roads() {
timer.next();
let draw_r = DrawRoad::new(r, cs, prerender);
let draw_r = DrawRoad::new(r, cs, ctx.prerender);
all_roads.push(
osm_rank_to_color(cs, r.get_rank()),
r.get_thick_polygon().get(timer),
@ -72,7 +72,7 @@ impl DrawMap {
roads.push(draw_r);
}
timer.start("upload thick roads");
let draw_all_thick_roads = prerender.upload(all_roads);
let draw_all_thick_roads = ctx.prerender.upload(all_roads);
timer.stop("upload thick roads");
let almost_lanes =
@ -91,7 +91,7 @@ impl DrawMap {
let mut lanes: Vec<DrawLane> = Vec::new();
for almost in almost_lanes {
timer.next();
lanes.push(almost.finish(prerender));
lanes.push(almost.finish(ctx.prerender));
}
timer.start_iter("compute_turn_to_lane_offset", map.all_lanes().len());
@ -117,7 +117,7 @@ impl DrawMap {
timer.start_iter("make DrawIntersections", map.all_intersections().len());
for i in map.all_intersections() {
timer.next();
let draw_i = DrawIntersection::new(i, map, cs, prerender, timer);
let draw_i = DrawIntersection::new(i, map, cs, ctx.prerender, timer);
if i.is_stop_sign() {
all_intersections.push(osm_rank_to_color(cs, i.get_rank(map)), i.polygon.clone());
all_intersections.push(cs.get("unzoomed outline"), draw_i.get_outline(map));
@ -130,7 +130,7 @@ impl DrawMap {
intersections.push(draw_i);
}
timer.start("upload all intersections");
let draw_all_unzoomed_intersections = prerender.upload(all_intersections);
let draw_all_unzoomed_intersections = ctx.prerender.upload(all_intersections);
timer.stop("upload all intersections");
let mut buildings: Vec<DrawBuilding> = Vec::new();
@ -141,7 +141,7 @@ impl DrawMap {
buildings.push(DrawBuilding::new(b, cs, &mut all_buildings));
}
timer.start("upload all buildings");
let draw_all_buildings = prerender.upload(all_buildings);
let draw_all_buildings = ctx.prerender.upload(all_buildings);
timer.stop("upload all buildings");
let mut extra_shapes: Vec<DrawExtraShape> = Vec::new();
@ -182,7 +182,7 @@ impl DrawMap {
let mut bus_stops: HashMap<BusStopID, DrawBusStop> = HashMap::new();
for s in map.all_bus_stops().values() {
timer.next();
bus_stops.insert(s.id, DrawBusStop::new(s, map, cs, prerender));
bus_stops.insert(s.id, DrawBusStop::new(s, map, cs, ctx.prerender));
}
let mut areas: Vec<DrawArea> = Vec::new();
@ -190,13 +190,13 @@ impl DrawMap {
timer.start_iter("make DrawAreas", map.all_areas().len());
for a in map.all_areas() {
timer.next();
areas.push(DrawArea::new(a, cs, &mut all_areas));
areas.push(DrawArea::new(a, ctx, &mut all_areas));
}
timer.start("upload all areas");
let draw_all_areas = prerender.upload(all_areas);
let draw_all_areas = ctx.prerender.upload(all_areas);
timer.stop("upload all areas");
let boundary_polygon = prerender.upload_borrowed(vec![(
let boundary_polygon = ctx.prerender.upload_borrowed(vec![(
cs.get_def("map background", Color::rgb(242, 239, 233)),
map.get_boundary_polygon(),
)]);
@ -227,7 +227,7 @@ impl DrawMap {
timer.note(format!(
"static DrawMap consumes {} MB on the GPU",
abstutil::prettyprint_usize(prerender.get_total_bytes_uploaded() / 1024 / 1024)
abstutil::prettyprint_usize(ctx.prerender.get_total_bytes_uploaded() / 1024 / 1024)
));
DrawMap {

View File

@ -469,7 +469,7 @@ impl PerMapUI {
mem.reset("Map and Sim", timer);
timer.start("draw_map");
let draw_map = DrawMap::new(&map, &flags, cs, ctx.prerender, timer);
let draw_map = DrawMap::new(&map, &flags, cs, ctx, timer);
timer.stop("draw_map");
mem.reset("DrawMap", timer);