WIP: clip_rect
This commit is contained in:
parent
ffc1768e40
commit
2f02446f6f
14 changed files with 307 additions and 198 deletions
Binary file not shown.
|
@ -82,7 +82,7 @@ impl Context {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drain_paint_lists(&self) -> Vec<PaintCmd> {
|
pub fn drain_paint_lists(&self) -> Vec<(Rect, PaintCmd)> {
|
||||||
let memory = self.memory.lock();
|
let memory = self.memory.lock();
|
||||||
self.graphics.lock().drain(&memory.window_order).collect()
|
self.graphics.lock().drain(&memory.window_order).collect()
|
||||||
}
|
}
|
||||||
|
@ -224,7 +224,10 @@ impl Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_paint_cmd(&self, layer: Layer, paint_cmd: PaintCmd) {
|
pub fn add_paint_cmd(&self, layer: Layer, paint_cmd: PaintCmd) {
|
||||||
self.graphics.lock().layer(layer).push(paint_cmd)
|
self.graphics
|
||||||
|
.lock()
|
||||||
|
.layer(layer)
|
||||||
|
.push((Rect::everything(), paint_cmd))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{layout, mesher::Mesher, widgets::*, *};
|
use crate::{layout, mesher::*, widgets::*, *};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Default)]
|
||||||
struct Stats {
|
struct Stats {
|
||||||
|
num_batches: usize,
|
||||||
num_vertices: usize,
|
num_vertices: usize,
|
||||||
num_triangles: usize,
|
num_triangles: usize,
|
||||||
}
|
}
|
||||||
|
@ -55,16 +56,20 @@ impl Emigui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn paint(&mut self) -> Mesh {
|
pub fn paint(&mut self) -> PaintBatches {
|
||||||
|
let mesher_options = MesherOptions {
|
||||||
|
anti_alias: self.anti_alias,
|
||||||
|
aa_size: 1.0 / self.last_input.pixels_per_point,
|
||||||
|
};
|
||||||
let paint_commands = self.ctx.drain_paint_lists();
|
let paint_commands = self.ctx.drain_paint_lists();
|
||||||
let mut mesher = Mesher::new(self.last_input.pixels_per_point);
|
let batches = mesh_paint_commands(&mesher_options, &self.ctx.fonts, paint_commands);
|
||||||
mesher.options.anti_alias = self.anti_alias;
|
self.stats = Default::default();
|
||||||
|
self.stats.num_batches = batches.len();
|
||||||
mesher.paint(&self.ctx.fonts, &paint_commands);
|
for (_, mesh) in &batches {
|
||||||
let mesh = mesher.mesh;
|
self.stats.num_vertices += mesh.vertices.len();
|
||||||
self.stats.num_vertices = mesh.vertices.len();
|
self.stats.num_triangles += mesh.indices.len() / 3;
|
||||||
self.stats.num_triangles = mesh.indices.len() / 3;
|
}
|
||||||
mesh
|
batches
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ui(&mut self, region: &mut Region) {
|
pub fn ui(&mut self, region: &mut Region) {
|
||||||
|
@ -104,6 +109,7 @@ impl Emigui {
|
||||||
region.cursor().x,
|
region.cursor().x,
|
||||||
region.cursor().y,
|
region.cursor().y,
|
||||||
));
|
));
|
||||||
|
region.add(label!("num_batches: {}", self.stats.num_batches));
|
||||||
region.add(label!("num_vertices: {}", self.stats.num_vertices));
|
region.add(label!("num_vertices: {}", self.stats.num_vertices));
|
||||||
region.add(label!("num_triangles: {}", self.stats.num_triangles));
|
region.add(label!("num_triangles: {}", self.stats.num_triangles));
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{Id, PaintCmd};
|
use crate::{math::Rect, Id, PaintCmd};
|
||||||
|
|
||||||
// TODO: support multiple windows
|
// TODO: support multiple windows
|
||||||
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
|
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
|
||||||
|
@ -11,7 +11,8 @@ pub enum Layer {
|
||||||
Popup,
|
Popup,
|
||||||
}
|
}
|
||||||
|
|
||||||
type PaintList = Vec<PaintCmd>;
|
/// Each PaintCmd is paired with a clip rectangle.
|
||||||
|
type PaintList = Vec<(Rect, PaintCmd)>;
|
||||||
|
|
||||||
/// TODO: improve this
|
/// TODO: improve this
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
|
@ -30,7 +31,10 @@ impl GraphicLayers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drain(&mut self, window_oreder: &[Id]) -> impl ExactSizeIterator<Item = PaintCmd> {
|
pub fn drain(
|
||||||
|
&mut self,
|
||||||
|
window_oreder: &[Id],
|
||||||
|
) -> impl ExactSizeIterator<Item = (Rect, PaintCmd)> {
|
||||||
let mut all_commands: Vec<_> = self.bg.drain(..).collect();
|
let mut all_commands: Vec<_> = self.bg.drain(..).collect();
|
||||||
|
|
||||||
for id in window_oreder {
|
for id in window_oreder {
|
||||||
|
|
|
@ -70,6 +70,7 @@ pub enum Align {
|
||||||
/// Right/Bottom
|
/// Right/Bottom
|
||||||
/// Note: requires a bounded/known available_width.
|
/// Note: requires a bounded/known available_width.
|
||||||
Max,
|
Max,
|
||||||
|
// TODO: Justified
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Align {
|
impl Default for Align {
|
||||||
|
@ -123,11 +124,14 @@ where
|
||||||
let mut graphics = ctx.graphics.lock();
|
let mut graphics = ctx.graphics.lock();
|
||||||
graphics.layer(layer).insert(
|
graphics.layer(layer).insert(
|
||||||
where_to_put_background,
|
where_to_put_background,
|
||||||
|
(
|
||||||
|
Rect::everything(),
|
||||||
PaintCmd::Rect {
|
PaintCmd::Rect {
|
||||||
corner_radius: 5.0,
|
corner_radius: 5.0,
|
||||||
fill_color: Some(style.background_fill_color()),
|
fill_color: Some(style.background_fill_color()),
|
||||||
outline: Some(Outline::new(1.0, color::WHITE)),
|
outline: Some(Outline::new(1.0, color::WHITE)),
|
||||||
rect,
|
rect,
|
||||||
},
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ pub use {
|
||||||
layout::Align,
|
layout::Align,
|
||||||
math::*,
|
math::*,
|
||||||
memory::Memory,
|
memory::Memory,
|
||||||
mesher::{Mesh, Vertex},
|
mesher::{Mesh, PaintBatches, Vertex},
|
||||||
region::Region,
|
region::Region,
|
||||||
style::Style,
|
style::Style,
|
||||||
texture_atlas::Texture,
|
texture_atlas::Texture,
|
||||||
|
|
|
@ -4,6 +4,13 @@ pub struct Vec2 {
|
||||||
pub y: f32,
|
pub y: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Vec2 {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.x == other.x && self.y == other.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Eq for Vec2 {}
|
||||||
|
|
||||||
impl Vec2 {
|
impl Vec2 {
|
||||||
pub fn splat(v: impl Into<f32>) -> Vec2 {
|
pub fn splat(v: impl Into<f32>) -> Vec2 {
|
||||||
let v: f32 = v.into();
|
let v: f32 = v.into();
|
||||||
|
@ -153,6 +160,13 @@ pub struct Pos2 {
|
||||||
// implicit w = 1
|
// implicit w = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Pos2 {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.x == other.x && self.y == other.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Eq for Pos2 {}
|
||||||
|
|
||||||
impl Pos2 {
|
impl Pos2 {
|
||||||
pub fn dist(self: Pos2, other: Pos2) -> f32 {
|
pub fn dist(self: Pos2, other: Pos2) -> f32 {
|
||||||
(self - other).length()
|
(self - other).length()
|
||||||
|
@ -238,13 +252,30 @@ pub fn pos2(x: f32, y: f32) -> Pos2 {
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize)]
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct Rect {
|
pub struct Rect {
|
||||||
min: Pos2,
|
min: Pos2,
|
||||||
max: Pos2,
|
max: Pos2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rect {
|
impl Rect {
|
||||||
|
/// Infinite rectangle that contains everything
|
||||||
|
pub fn everything() -> Self {
|
||||||
|
let inf = std::f32::INFINITY;
|
||||||
|
Self {
|
||||||
|
min: pos2(-inf, -inf),
|
||||||
|
max: pos2(inf, inf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nothing() -> Self {
|
||||||
|
let inf = std::f32::INFINITY;
|
||||||
|
Self {
|
||||||
|
min: pos2(inf, inf),
|
||||||
|
max: pos2(-inf, -inf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn from_min_max(min: Pos2, max: Pos2) -> Self {
|
pub fn from_min_max(min: Pos2, max: Pos2) -> Self {
|
||||||
Rect { min, max: max }
|
Rect { min, max: max }
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,6 @@ pub struct Vertex {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Serialize)]
|
#[derive(Clone, Debug, Default, Serialize)]
|
||||||
pub struct Mesh {
|
pub struct Mesh {
|
||||||
/// Draw as triangles (i.e. the length is a multiple of three)
|
/// Draw as triangles (i.e. the length is a multiple of three)
|
||||||
|
@ -24,6 +22,11 @@ pub struct Mesh {
|
||||||
pub vertices: Vec<Vertex>,
|
pub vertices: Vec<Vertex>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Grouped by clip rectangles, in pixel coordinates
|
||||||
|
pub type PaintBatches = Vec<(Rect, Mesh)>;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
impl Mesh {
|
impl Mesh {
|
||||||
pub fn append(&mut self, mesh: &Mesh) {
|
pub fn append(&mut self, mesh: &Mesh) {
|
||||||
let index_offset = self.vertices.len() as u32;
|
let index_offset = self.vertices.len() as u32;
|
||||||
|
@ -373,44 +376,28 @@ pub fn paint_path(
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
pub struct Mesher {
|
pub fn mesh_command(
|
||||||
pub options: MesherOptions,
|
options: &MesherOptions,
|
||||||
|
fonts: &Fonts,
|
||||||
/// Where the output goes
|
command: PaintCmd,
|
||||||
pub mesh: Mesh,
|
out_mesh: &mut Mesh,
|
||||||
}
|
) {
|
||||||
|
match command {
|
||||||
impl Mesher {
|
|
||||||
pub fn new(pixels_per_point: f32) -> Mesher {
|
|
||||||
Mesher {
|
|
||||||
options: MesherOptions {
|
|
||||||
anti_alias: true,
|
|
||||||
aa_size: 1.0 / pixels_per_point,
|
|
||||||
},
|
|
||||||
mesh: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn paint(&mut self, fonts: &Fonts, commands: &[PaintCmd]) {
|
|
||||||
let mut path = Path::default();
|
|
||||||
|
|
||||||
for cmd in commands {
|
|
||||||
match cmd {
|
|
||||||
PaintCmd::Circle {
|
PaintCmd::Circle {
|
||||||
center,
|
center,
|
||||||
fill_color,
|
fill_color,
|
||||||
outline,
|
outline,
|
||||||
radius,
|
radius,
|
||||||
} => {
|
} => {
|
||||||
path.clear();
|
let mut path = Path::default();
|
||||||
path.add_circle(*center, *radius);
|
path.add_circle(center, radius);
|
||||||
if let Some(color) = fill_color {
|
if let Some(color) = fill_color {
|
||||||
fill_closed_path(&mut self.mesh, &self.options, &path.0, *color);
|
fill_closed_path(out_mesh, options, &path.0, color);
|
||||||
}
|
}
|
||||||
if let Some(outline) = outline {
|
if let Some(outline) = outline {
|
||||||
paint_path(
|
paint_path(
|
||||||
&mut self.mesh,
|
out_mesh,
|
||||||
&self.options,
|
options,
|
||||||
Closed,
|
Closed,
|
||||||
&path.0,
|
&path.0,
|
||||||
outline.color,
|
outline.color,
|
||||||
|
@ -418,8 +405,8 @@ impl Mesher {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PaintCmd::Mesh(cmd_frame) => {
|
PaintCmd::Mesh(mesh) => {
|
||||||
self.mesh.append(cmd_frame);
|
out_mesh.append(&mesh);
|
||||||
}
|
}
|
||||||
PaintCmd::Line {
|
PaintCmd::Line {
|
||||||
points,
|
points,
|
||||||
|
@ -428,9 +415,9 @@ impl Mesher {
|
||||||
} => {
|
} => {
|
||||||
let n = points.len();
|
let n = points.len();
|
||||||
if n >= 2 {
|
if n >= 2 {
|
||||||
path.clear();
|
let mut path = Path::default();
|
||||||
path.add_line(points);
|
path.add_line(&points);
|
||||||
paint_path(&mut self.mesh, &self.options, Open, &path.0, *color, *width);
|
paint_path(out_mesh, options, Open, &path.0, color, width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PaintCmd::Path {
|
PaintCmd::Path {
|
||||||
|
@ -441,15 +428,15 @@ impl Mesher {
|
||||||
} => {
|
} => {
|
||||||
if let Some(fill_color) = fill_color {
|
if let Some(fill_color) = fill_color {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
*closed,
|
closed,
|
||||||
"You asked to fill a path that is not closed. That makes no sense."
|
"You asked to fill a path that is not closed. That makes no sense."
|
||||||
);
|
);
|
||||||
fill_closed_path(&mut self.mesh, &self.options, &path.0, *fill_color);
|
fill_closed_path(out_mesh, options, &path.0, fill_color);
|
||||||
}
|
}
|
||||||
if let Some(outline) = outline {
|
if let Some(outline) = outline {
|
||||||
paint_path(
|
paint_path(
|
||||||
&mut self.mesh,
|
out_mesh,
|
||||||
&self.options,
|
options,
|
||||||
Closed,
|
Closed,
|
||||||
&path.0,
|
&path.0,
|
||||||
outline.color,
|
outline.color,
|
||||||
|
@ -463,15 +450,15 @@ impl Mesher {
|
||||||
outline,
|
outline,
|
||||||
rect,
|
rect,
|
||||||
} => {
|
} => {
|
||||||
path.clear();
|
let mut path = Path::default();
|
||||||
path.add_rounded_rectangle(rect, *corner_radius);
|
path.add_rounded_rectangle(&rect, corner_radius);
|
||||||
if let Some(fill_color) = fill_color {
|
if let Some(fill_color) = fill_color {
|
||||||
fill_closed_path(&mut self.mesh, &self.options, &path.0, *fill_color);
|
fill_closed_path(out_mesh, options, &path.0, fill_color);
|
||||||
}
|
}
|
||||||
if let Some(outline) = outline {
|
if let Some(outline) = outline {
|
||||||
paint_path(
|
paint_path(
|
||||||
&mut self.mesh,
|
out_mesh,
|
||||||
&self.options,
|
options,
|
||||||
Closed,
|
Closed,
|
||||||
&path.0,
|
&path.0,
|
||||||
outline.color,
|
outline.color,
|
||||||
|
@ -486,26 +473,44 @@ impl Mesher {
|
||||||
text_style,
|
text_style,
|
||||||
x_offsets,
|
x_offsets,
|
||||||
} => {
|
} => {
|
||||||
let font = &fonts[*text_style];
|
let font = &fonts[text_style];
|
||||||
for (c, x_offset) in text.chars().zip(x_offsets.iter()) {
|
for (c, x_offset) in text.chars().zip(x_offsets.iter()) {
|
||||||
if let Some(glyph) = font.uv_rect(c) {
|
if let Some(glyph) = font.uv_rect(c) {
|
||||||
let mut top_left = Vertex {
|
let mut top_left = Vertex {
|
||||||
pos: *pos + glyph.offset + vec2(*x_offset, 0.0),
|
pos: pos + glyph.offset + vec2(*x_offset, 0.0),
|
||||||
uv: glyph.min,
|
uv: glyph.min,
|
||||||
color: *color,
|
color: color,
|
||||||
};
|
};
|
||||||
top_left.pos.x = font.round_to_pixel(top_left.pos.x); // Pixel-perfection.
|
top_left.pos.x = font.round_to_pixel(top_left.pos.x); // Pixel-perfection.
|
||||||
top_left.pos.y = font.round_to_pixel(top_left.pos.y); // Pixel-perfection.
|
top_left.pos.y = font.round_to_pixel(top_left.pos.y); // Pixel-perfection.
|
||||||
let bottom_right = Vertex {
|
let bottom_right = Vertex {
|
||||||
pos: top_left.pos + glyph.size,
|
pos: top_left.pos + glyph.size,
|
||||||
uv: glyph.max,
|
uv: glyph.max,
|
||||||
color: *color,
|
color: color,
|
||||||
};
|
};
|
||||||
self.mesh.add_rect(top_left, bottom_right);
|
out_mesh.add_rect(top_left, bottom_right);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn mesh_paint_commands(
|
||||||
|
options: &MesherOptions,
|
||||||
|
fonts: &Fonts,
|
||||||
|
commands: Vec<(Rect, PaintCmd)>,
|
||||||
|
) -> Vec<(Rect, Mesh)> {
|
||||||
|
let mut batches = PaintBatches::default();
|
||||||
|
for (clip_rect, cmd) in commands {
|
||||||
|
// TODO: cull(clip_rect, cmd)
|
||||||
|
|
||||||
|
if batches.is_empty() || batches.last().unwrap().0 != clip_rect {
|
||||||
|
batches.push((clip_rect, Mesh::default()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let out_mesh = &mut batches.last_mut().unwrap().1;
|
||||||
|
mesh_command(options, fonts, cmd, out_mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
batches
|
||||||
|
}
|
||||||
|
|
|
@ -61,11 +61,20 @@ impl Region {
|
||||||
/// Can be used for free painting.
|
/// Can be used for free painting.
|
||||||
/// NOTE: all coordinates are screen coordinates!
|
/// NOTE: all coordinates are screen coordinates!
|
||||||
pub fn add_paint_cmd(&mut self, paint_cmd: PaintCmd) {
|
pub fn add_paint_cmd(&mut self, paint_cmd: PaintCmd) {
|
||||||
self.ctx.graphics.lock().layer(self.layer).push(paint_cmd)
|
self.ctx
|
||||||
|
.graphics
|
||||||
|
.lock()
|
||||||
|
.layer(self.layer)
|
||||||
|
.push((self.clip_rect(), paint_cmd))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_paint_cmds(&mut self, mut cmds: Vec<PaintCmd>) {
|
pub fn add_paint_cmds(&mut self, mut cmds: Vec<PaintCmd>) {
|
||||||
self.ctx.graphics.lock().layer(self.layer).append(&mut cmds)
|
let clip_rect = self.clip_rect();
|
||||||
|
self.ctx
|
||||||
|
.graphics
|
||||||
|
.lock()
|
||||||
|
.layer(self.layer)
|
||||||
|
.extend(cmds.drain(..).map(|cmd| (clip_rect, cmd)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Options for this region, and any child regions we may spawn.
|
/// Options for this region, and any child regions we may spawn.
|
||||||
|
@ -85,6 +94,13 @@ impl Region {
|
||||||
&*self.ctx.fonts
|
&*self.ctx.fonts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Screen-space rectangle for clipping what we paint in this region.
|
||||||
|
/// This is used, for instance, to avoid painting outside a window that is smaller
|
||||||
|
/// than its contents.
|
||||||
|
pub fn clip_rect(&self) -> Rect {
|
||||||
|
self.rect
|
||||||
|
}
|
||||||
|
|
||||||
pub fn available_width(&self) -> f32 {
|
pub fn available_width(&self) -> f32 {
|
||||||
self.rect.max().x - self.cursor.x
|
self.rect.max().x - self.cursor.x
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,12 +123,15 @@ impl Window {
|
||||||
let corner_radius = style.window.corner_radius;
|
let corner_radius = style.window.corner_radius;
|
||||||
graphics.layer(layer).insert(
|
graphics.layer(layer).insert(
|
||||||
where_to_put_background,
|
where_to_put_background,
|
||||||
|
(
|
||||||
|
Rect::everything(),
|
||||||
PaintCmd::Rect {
|
PaintCmd::Rect {
|
||||||
corner_radius,
|
corner_radius,
|
||||||
fill_color: Some(style.background_fill_color()),
|
fill_color: Some(style.background_fill_color()),
|
||||||
outline: Some(Outline::new(1.0, color::WHITE)),
|
outline: Some(Outline::new(1.0, color::WHITE)),
|
||||||
rect: state.rect,
|
rect: state.rect,
|
||||||
},
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
let corner_interact = if self.resizeable {
|
let corner_interact = if self.resizeable {
|
||||||
|
@ -148,12 +151,15 @@ impl Window {
|
||||||
path.add_point(corner_center, vec2(-1.0, 0.0));
|
path.add_point(corner_center, vec2(-1.0, 0.0));
|
||||||
graphics.layer(layer).insert(
|
graphics.layer(layer).insert(
|
||||||
where_to_put_background + 1,
|
where_to_put_background + 1,
|
||||||
|
(
|
||||||
|
Rect::everything(),
|
||||||
PaintCmd::Path {
|
PaintCmd::Path {
|
||||||
path,
|
path,
|
||||||
closed: true,
|
closed: true,
|
||||||
fill_color: style.interact_fill_color(&corner_interact),
|
fill_color: style.interact_fill_color(&corner_interact),
|
||||||
outline: style.interact_outline(&corner_interact),
|
outline: style.interact_outline(&corner_interact),
|
||||||
},
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
corner_interact
|
corner_interact
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#![allow(deprecated)] // legacy implement_vertex macro
|
#![allow(deprecated)] // legacy implement_vertex macro
|
||||||
|
|
||||||
use {
|
use {
|
||||||
emigui::Mesh,
|
emigui::{Mesh, PaintBatches, Rect},
|
||||||
glium::{implement_vertex, index::PrimitiveType, program, texture, uniform, Surface},
|
glium::{implement_vertex, index::PrimitiveType, program, texture, uniform, Frame, Surface},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Painter {
|
pub struct Painter {
|
||||||
|
@ -17,29 +17,36 @@ impl Painter {
|
||||||
140 => {
|
140 => {
|
||||||
vertex: "
|
vertex: "
|
||||||
#version 140
|
#version 140
|
||||||
|
uniform vec4 u_clip_rect; // min_x, min_y, max_x, max_y
|
||||||
uniform vec2 u_screen_size;
|
uniform vec2 u_screen_size;
|
||||||
uniform vec2 u_tex_size;
|
uniform vec2 u_tex_size;
|
||||||
in vec2 a_pos;
|
in vec2 a_pos;
|
||||||
in vec4 a_color;
|
in vec4 a_color;
|
||||||
in vec2 a_tc;
|
in vec2 a_tc;
|
||||||
|
out vec2 v_pos;
|
||||||
out vec4 v_color;
|
out vec4 v_color;
|
||||||
out vec2 v_tc;
|
out vec2 v_tc;
|
||||||
|
out vec4 v_clip_rect;
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = vec4(
|
gl_Position = vec4(
|
||||||
2.0 * a_pos.x / u_screen_size.x - 1.0,
|
2.0 * a_pos.x / u_screen_size.x - 1.0,
|
||||||
1.0 - 2.0 * a_pos.y / u_screen_size.y,
|
1.0 - 2.0 * a_pos.y / u_screen_size.y,
|
||||||
0.0,
|
0.0,
|
||||||
1.0);
|
1.0);
|
||||||
|
v_pos = a_pos;
|
||||||
v_color = a_color / 255.0;
|
v_color = a_color / 255.0;
|
||||||
v_tc = a_tc / u_tex_size;
|
v_tc = a_tc / u_tex_size;
|
||||||
|
v_clip_rect = u_clip_rect;
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
|
|
||||||
fragment: "
|
fragment: "
|
||||||
#version 140
|
#version 140
|
||||||
uniform sampler2D u_sampler;
|
uniform sampler2D u_sampler;
|
||||||
|
in vec2 v_pos;
|
||||||
in vec4 v_color;
|
in vec4 v_color;
|
||||||
in vec2 v_tc;
|
in vec2 v_tc;
|
||||||
|
in vec4 v_clip_rect;
|
||||||
out vec4 f_color;
|
out vec4 f_color;
|
||||||
|
|
||||||
// glium expects linear output.
|
// glium expects linear output.
|
||||||
|
@ -51,6 +58,10 @@ impl Painter {
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
if (v_pos.x < v_clip_rect.x) { discard; }
|
||||||
|
if (v_pos.y < v_clip_rect.y) { discard; }
|
||||||
|
// if (v_pos.x < v_clip_rect.z) { discard; } // TODO
|
||||||
|
// if (v_pos.y > v_clip_rect.w) { discard; } // TODO
|
||||||
f_color = v_color;
|
f_color = v_color;
|
||||||
f_color.rgb = linear_from_srgb(f_color.rgb);
|
f_color.rgb = linear_from_srgb(f_color.rgb);
|
||||||
f_color.a *= texture(u_sampler, v_tc).r;
|
f_color.a *= texture(u_sampler, v_tc).r;
|
||||||
|
@ -61,6 +72,7 @@ impl Painter {
|
||||||
110 => {
|
110 => {
|
||||||
vertex: "
|
vertex: "
|
||||||
#version 110
|
#version 110
|
||||||
|
uniform vec4 u_clip_rect; // min_x, min_y, max_x, max_y
|
||||||
uniform vec2 u_screen_size;
|
uniform vec2 u_screen_size;
|
||||||
uniform vec2 u_tex_size;
|
uniform vec2 u_tex_size;
|
||||||
attribute vec2 a_pos;
|
attribute vec2 a_pos;
|
||||||
|
@ -104,6 +116,7 @@ impl Painter {
|
||||||
100 => {
|
100 => {
|
||||||
vertex: "
|
vertex: "
|
||||||
#version 100
|
#version 100
|
||||||
|
uniform mediump vec4 u_clip_rect; // min_x, min_y, max_x, max_y
|
||||||
uniform mediump vec2 u_screen_size;
|
uniform mediump vec2 u_screen_size;
|
||||||
uniform mediump vec2 u_tex_size;
|
uniform mediump vec2 u_tex_size;
|
||||||
attribute mediump vec2 a_pos;
|
attribute mediump vec2 a_pos;
|
||||||
|
@ -177,9 +190,30 @@ impl Painter {
|
||||||
self.current_texture_id = Some(texture.id);
|
self.current_texture_id = Some(texture.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn paint(&mut self, display: &glium::Display, mesh: Mesh, texture: &emigui::Texture) {
|
pub fn paint_batches(
|
||||||
|
&mut self,
|
||||||
|
display: &glium::Display,
|
||||||
|
batches: PaintBatches,
|
||||||
|
texture: &emigui::Texture,
|
||||||
|
) {
|
||||||
self.upload_texture(display, texture);
|
self.upload_texture(display, texture);
|
||||||
|
|
||||||
|
let mut target = display.draw();
|
||||||
|
target.clear_color(0.0, 0.0, 0.0, 0.0);
|
||||||
|
for (clip_rect, mesh) in batches {
|
||||||
|
self.paint_batch(&mut target, display, &clip_rect, &mesh, texture)
|
||||||
|
}
|
||||||
|
target.finish().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint_batch(
|
||||||
|
&mut self,
|
||||||
|
target: &mut Frame,
|
||||||
|
display: &glium::Display,
|
||||||
|
clip_rect: &Rect,
|
||||||
|
mesh: &Mesh,
|
||||||
|
texture: &emigui::Texture,
|
||||||
|
) {
|
||||||
let vertex_buffer = {
|
let vertex_buffer = {
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
|
@ -213,6 +247,8 @@ impl Painter {
|
||||||
let height_points = height_pixels as f32 / pixels_per_point;
|
let height_points = height_pixels as f32 / pixels_per_point;
|
||||||
|
|
||||||
let uniforms = uniform! {
|
let uniforms = uniform! {
|
||||||
|
u_clip_rect_min: [clip_rect.min().x, clip_rect.min().y],
|
||||||
|
u_clip_rect_max: [clip_rect.max().x, clip_rect.max().y],
|
||||||
u_screen_size: [width_points, height_points],
|
u_screen_size: [width_points, height_points],
|
||||||
u_tex_size: [texture.width as f32, texture.height as f32],
|
u_tex_size: [texture.width as f32, texture.height as f32],
|
||||||
u_sampler: &self.texture,
|
u_sampler: &self.texture,
|
||||||
|
@ -223,8 +259,6 @@ impl Painter {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut target = display.draw();
|
|
||||||
target.clear_color(0.0, 0.0, 0.0, 0.0);
|
|
||||||
target
|
target
|
||||||
.draw(
|
.draw(
|
||||||
&vertex_buffer,
|
&vertex_buffer,
|
||||||
|
@ -234,6 +268,5 @@ impl Painter {
|
||||||
¶ms,
|
¶ms,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
target.finish().unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use {
|
||||||
web_sys::{WebGlBuffer, WebGlProgram, WebGlRenderingContext, WebGlShader, WebGlTexture},
|
web_sys::{WebGlBuffer, WebGlProgram, WebGlRenderingContext, WebGlShader, WebGlTexture},
|
||||||
};
|
};
|
||||||
|
|
||||||
use emigui::{Color, Mesh, Texture};
|
use emigui::{Color, Mesh, PaintBatches, Rect, Texture};
|
||||||
|
|
||||||
type Gl = WebGlRenderingContext;
|
type Gl = WebGlRenderingContext;
|
||||||
|
|
||||||
|
@ -143,10 +143,10 @@ impl Painter {
|
||||||
self.current_texture_id = Some(texture.id);
|
self.current_texture_id = Some(texture.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn paint(
|
pub fn paint_batches(
|
||||||
&mut self,
|
&mut self,
|
||||||
bg_color: Color,
|
bg_color: Color,
|
||||||
mesh: Mesh,
|
batches: PaintBatches,
|
||||||
texture: &Texture,
|
texture: &Texture,
|
||||||
pixels_per_point: f32,
|
pixels_per_point: f32,
|
||||||
) -> Result<(), JsValue> {
|
) -> Result<(), JsValue> {
|
||||||
|
@ -195,13 +195,15 @@ impl Painter {
|
||||||
);
|
);
|
||||||
gl.clear(Gl::COLOR_BUFFER_BIT);
|
gl.clear(Gl::COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
for (clip_rect, mesh) in batches {
|
||||||
for mesh in mesh.split_to_u16() {
|
for mesh in mesh.split_to_u16() {
|
||||||
self.paint_mesh(&mesh)?;
|
self.paint_mesh(&clip_rect, &mesh)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint_mesh(&mut self, mesh: &Mesh) -> Result<(), JsValue> {
|
fn paint_mesh(&mut self, _clip_rec: &Rect, mesh: &Mesh) -> Result<(), JsValue> {
|
||||||
let indices: Vec<u16> = mesh.indices.iter().map(|idx| *idx as u16).collect();
|
let indices: Vec<u16> = mesh.indices.iter().map(|idx| *idx as u16).collect();
|
||||||
|
|
||||||
let mut positions: Vec<f32> = Vec::with_capacity(2 * mesh.vertices.len());
|
let mut positions: Vec<f32> = Vec::with_capacity(2 * mesh.vertices.len());
|
||||||
|
|
|
@ -102,10 +102,11 @@ fn main() {
|
||||||
.show(region.ctx(), |region| {
|
.show(region.ctx(), |region| {
|
||||||
region
|
region
|
||||||
.add_label("This window may shrink so small that its contents no longer fit.");
|
.add_label("This window may shrink so small that its contents no longer fit.");
|
||||||
|
region.add_label("Maybe you can no longer read this, for instance");
|
||||||
|
region.add_label("And this line may be way too far down.");
|
||||||
});
|
});
|
||||||
|
|
||||||
let mesh = emigui.paint();
|
painter.paint_batches(&display, emigui.paint(), emigui.texture());
|
||||||
painter.paint(&display, mesh, emigui.texture());
|
|
||||||
|
|
||||||
let cursor = *emigui.ctx.cursor_icon.lock();
|
let cursor = *emigui.ctx.cursor_icon.lock();
|
||||||
let cursor = match cursor {
|
let cursor = match cursor {
|
||||||
|
|
|
@ -46,39 +46,37 @@ impl State {
|
||||||
let mut region = region.centered_column(region.available_width().min(480.0));
|
let mut region = region.centered_column(region.available_width().min(480.0));
|
||||||
region.set_align(Align::Min);
|
region.set_align(Align::Min);
|
||||||
region.add(label!("Emigui!").text_style(TextStyle::Heading));
|
region.add(label!("Emigui!").text_style(TextStyle::Heading));
|
||||||
region.add(label!("Emigui is an immediate mode GUI written in Rust, compiled to WebAssembly, rendered with WebGL."));
|
region.add_label("Emigui is an immediate mode GUI written in Rust, compiled to WebAssembly, rendered with WebGL.");
|
||||||
region.add(label!(
|
region.add_label(
|
||||||
"Everything you see is rendered as textured triangles. There is no DOM. There are no HTML elements."
|
"Everything you see is rendered as textured triangles. There is no DOM. There are no HTML elements."
|
||||||
));
|
);
|
||||||
region.add(label!("This not JavaScript. This is Rust code, running at 60 Hz. This is the web page, reinvented with game tech."));
|
region.add_label("This not JavaScript. This is Rust code, running at 60 Hz. This is the web page, reinvented with game tech.");
|
||||||
region.add(label!(
|
region.add_label("This is also work in progress, and not ready for production... yet :)");
|
||||||
"This is also work in progress, and not ready for production... yet :)"
|
|
||||||
));
|
|
||||||
region.add(Separator::new());
|
region.add(Separator::new());
|
||||||
self.example_app.ui(&mut region);
|
self.example_app.ui(&mut region);
|
||||||
self.emigui.ui(&mut region);
|
self.emigui.ui(&mut region);
|
||||||
|
|
||||||
region.set_align(Align::Min);
|
region.set_align(Align::Min);
|
||||||
region.add(label!("WebGl painter info:"));
|
region.add_label("WebGl painter info:");
|
||||||
region.indent(|region| {
|
region.indent(|region| {
|
||||||
region.add(label!(self.webgl_painter.debug_info()));
|
region.add_label(self.webgl_painter.debug_info());
|
||||||
});
|
});
|
||||||
region.add(
|
region.add(
|
||||||
label!("Everything: {:.1} ms", self.everything_ms).text_style(TextStyle::Monospace),
|
label!("Everything: {:.1} ms", self.everything_ms).text_style(TextStyle::Monospace),
|
||||||
);
|
);
|
||||||
|
|
||||||
Window::new("Test window").show(region.ctx(), |region| {
|
// TODO: Make it even simpler to show a window
|
||||||
region.add(label!("Grab the window and move it around!"));
|
|
||||||
|
|
||||||
region.add(label!(
|
Window::new("Test window").show(region.ctx(), |region| {
|
||||||
"This window can be reisized, but not smaller than the contents."
|
region.add_label("Grab the window and move it around!");
|
||||||
));
|
|
||||||
|
region.add_label("This window can be reisized, but not smaller than the contents.");
|
||||||
});
|
});
|
||||||
Window::new("Another test window")
|
Window::new("Another test window")
|
||||||
.default_pos(pos2(400.0, 100.0))
|
.default_pos(pos2(400.0, 100.0))
|
||||||
.show(region.ctx(), |region| {
|
.show(region.ctx(), |region| {
|
||||||
region.add(label!("This might be on top of the other window?"));
|
region.add_label("This might be on top of the other window?");
|
||||||
region.add(label!("Second line of text"));
|
region.add_label("Second line of text");
|
||||||
});
|
});
|
||||||
|
|
||||||
let bg_color = srgba(16, 16, 16, 255);
|
let bg_color = srgba(16, 16, 16, 255);
|
||||||
|
|
Loading…
Reference in a new issue