egui/epaint/src/mesh.rs

296 lines
9 KiB
Rust
Raw Normal View History

2021-01-10 13:39:03 +00:00
use crate::*;
use emath::*;
2021-01-10 13:39:03 +00:00
2021-01-25 20:23:24 +00:00
/// The 2D vertex type.
2021-01-10 13:39:03 +00:00
///
/// Should be friendly to send to GPU as is.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
2021-01-10 13:39:03 +00:00
pub struct Vertex {
/// Logical pixel coordinates (points).
/// (0,0) is the top left corner of the screen.
pub pos: Pos2, // 64 bit
/// Normalized texture coordinates.
/// (0, 0) is the top left corner of the texture.
/// (1, 1) is the bottom right corner of the texture.
pub uv: Pos2, // 64 bit
/// sRGBA with premultiplied alpha
pub color: Color32, // 32 bit
}
2021-01-25 20:23:24 +00:00
/// Textured triangles in two dimensions.
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
2021-01-25 20:23:24 +00:00
pub struct Mesh {
2021-01-10 13:39:03 +00:00
/// Draw as triangles (i.e. the length is always multiple of three).
///
/// If you only support 16-bit indices you can use [`Mesh::split_to_u16`].
///
/// egui is NOT consistent with what winding order it uses, so turn off backface culling.
2021-01-10 13:39:03 +00:00
pub indices: Vec<u32>,
/// The vertex data indexed by `indices`.
pub vertices: Vec<Vertex>,
/// The texture to use when drawing these triangles.
2021-01-10 13:39:03 +00:00
pub texture_id: TextureId,
// TODO(emilk): bounding rectangle
2021-01-10 13:39:03 +00:00
}
2021-01-25 20:23:24 +00:00
impl Mesh {
2021-01-10 13:39:03 +00:00
pub fn with_texture(texture_id: TextureId) -> Self {
Self {
texture_id,
..Default::default()
}
}
/// Restore to default state, but without freeing memory.
pub fn clear(&mut self) {
self.indices.clear();
self.vertices.clear();
self.vertices = Default::default();
}
2021-01-10 13:39:03 +00:00
pub fn bytes_used(&self) -> usize {
std::mem::size_of::<Self>()
+ self.vertices.len() * std::mem::size_of::<Vertex>()
+ self.indices.len() * std::mem::size_of::<u32>()
}
/// Are all indices within the bounds of the contained vertices?
pub fn is_valid(&self) -> bool {
2021-05-09 12:13:09 +00:00
if let Ok(n) = u32::try_from(self.vertices.len()) {
self.indices.iter().all(|&i| i < n)
} else {
false
}
2021-01-10 13:39:03 +00:00
}
pub fn is_empty(&self) -> bool {
self.indices.is_empty() && self.vertices.is_empty()
}
New text layout (#682) This PR introduces a completely rewritten text layout engine which is simpler and more powerful. It allows mixing different text styles (heading, body, etc) and formats (color, underlining, strikethrough, …) in the same layout pass, and baked into the same `Galley`. This opens up the door to having a syntax-highlighed code editor, or a WYSIWYG markdown editor. One major change is the color is now baked in at layout time. However, many widgets changes text color on hovered. But we need to do the text layout before we know if it is hovered. Therefor the painter has an option to override the text color of a galley. ## Performance Text layout alone is about 20% slower, but a lot of that is because more tessellation is done upfront. Text tessellation is now a lot faster, but text layout + tessellation still lands at a net loss of 5-10% in performance. There are however a few tricks to speed it up (like using `smallvec`) which I am saving for later. Text layout is also cached, meaning that in most cases (when all text isn't changing each frame) text tessellation is actually more important (and that's more than 2x faster!). Sadly, the actual text cache lookup is significantly slower (300ns -> 600ns). That's because the `TextLayoutJob` is a lot bigger (it has more options, like underlining, fonts etc), so it is slower to hash and compare. I have an idea how to speed this up, but I need to do some other work before I can implement that. All in all, the performance impact on `demo_with_tesselate__realistic` is about 5-6% in the red. Not great; not terrible. The benefits are worth it, but I also think with some work I can get that down significantly, hopefully down to the old levels.
2021-09-03 16:18:00 +00:00
/// Calculate a bounding rectangle.
pub fn calc_bounds(&self) -> Rect {
let mut bounds = Rect::NOTHING;
for v in &self.vertices {
bounds.extend_with(v.pos);
}
bounds
}
2021-01-10 13:39:03 +00:00
/// Append all the indices and vertices of `other` to `self`.
2021-01-25 20:23:24 +00:00
pub fn append(&mut self, other: Mesh) {
crate::epaint_assert!(other.is_valid());
2021-01-10 13:39:03 +00:00
if self.is_empty() {
*self = other;
} else {
self.append_ref(&other);
}
}
/// Append all the indices and vertices of `other` to `self` without
/// taking ownership.
pub fn append_ref(&mut self, other: &Mesh) {
crate::epaint_assert!(other.is_valid());
if !self.is_empty() {
2021-01-10 13:39:03 +00:00
assert_eq!(
self.texture_id, other.texture_id,
2021-01-25 20:23:24 +00:00
"Can't merge Mesh using different textures"
2021-01-10 13:39:03 +00:00
);
} else {
self.texture_id = other.texture_id;
2021-01-10 13:39:03 +00:00
}
let index_offset = self.vertices.len() as u32;
self.indices
.extend(other.indices.iter().map(|index| index + index_offset));
self.vertices.extend(other.vertices.iter());
2021-01-10 13:39:03 +00:00
}
#[inline(always)]
2021-01-10 13:39:03 +00:00
pub fn colored_vertex(&mut self, pos: Pos2, color: Color32) {
crate::epaint_assert!(self.texture_id == TextureId::default());
2021-01-10 13:39:03 +00:00
self.vertices.push(Vertex {
pos,
uv: WHITE_UV,
color,
});
}
/// Add a triangle.
#[inline(always)]
2021-01-10 13:39:03 +00:00
pub fn add_triangle(&mut self, a: u32, b: u32, c: u32) {
self.indices.push(a);
self.indices.push(b);
self.indices.push(c);
}
/// Make room for this many additional triangles (will reserve 3x as many indices).
/// See also `reserve_vertices`.
#[inline(always)]
2021-01-10 13:39:03 +00:00
pub fn reserve_triangles(&mut self, additional_triangles: usize) {
self.indices.reserve(3 * additional_triangles);
}
/// Make room for this many additional vertices.
/// See also `reserve_triangles`.
#[inline(always)]
2021-01-10 13:39:03 +00:00
pub fn reserve_vertices(&mut self, additional: usize) {
self.vertices.reserve(additional);
}
/// Rectangle with a texture and color.
2021-01-30 13:48:36 +00:00
pub fn add_rect_with_uv(&mut self, rect: Rect, uv: Rect, color: Color32) {
2021-01-10 13:39:03 +00:00
#![allow(clippy::identity_op)]
let idx = self.vertices.len() as u32;
self.add_triangle(idx + 0, idx + 1, idx + 2);
self.add_triangle(idx + 2, idx + 1, idx + 3);
2021-01-30 13:48:36 +00:00
self.vertices.push(Vertex {
pos: rect.left_top(),
2021-01-10 13:39:03 +00:00
uv: uv.left_top(),
color,
2021-01-30 13:48:36 +00:00
});
self.vertices.push(Vertex {
pos: rect.right_top(),
uv: uv.right_top(),
color,
});
self.vertices.push(Vertex {
pos: rect.left_bottom(),
2021-01-10 13:39:03 +00:00
uv: uv.left_bottom(),
color,
2021-01-30 13:48:36 +00:00
});
self.vertices.push(Vertex {
pos: rect.right_bottom(),
2021-01-10 13:39:03 +00:00
uv: uv.right_bottom(),
color,
2021-01-30 13:48:36 +00:00
});
2021-01-10 13:39:03 +00:00
}
/// Uniformly colored rectangle.
#[inline(always)]
2021-01-10 13:39:03 +00:00
pub fn add_colored_rect(&mut self, rect: Rect, color: Color32) {
crate::epaint_assert!(self.texture_id == TextureId::default());
self.add_rect_with_uv(rect, [WHITE_UV, WHITE_UV].into(), color);
2021-01-10 13:39:03 +00:00
}
/// This is for platforms that only support 16-bit index buffers.
///
/// Splits this mesh into many smaller meshes (if needed)
/// where the smaller meshes have 16-bit indices.
pub fn split_to_u16(self) -> Vec<Mesh16> {
crate::epaint_assert!(self.is_valid());
2021-01-10 13:39:03 +00:00
const MAX_SIZE: u32 = 1 << 16;
if self.vertices.len() < MAX_SIZE as usize {
// Common-case optimization:
return vec![Mesh16 {
indices: self.indices.iter().map(|&i| i as u16).collect(),
vertices: self.vertices,
texture_id: self.texture_id,
}];
2021-01-10 13:39:03 +00:00
}
let mut output = vec![];
let mut index_cursor = 0;
while index_cursor < self.indices.len() {
let span_start = index_cursor;
let mut min_vindex = self.indices[index_cursor];
let mut max_vindex = self.indices[index_cursor];
while index_cursor < self.indices.len() {
let (mut new_min, mut new_max) = (min_vindex, max_vindex);
for i in 0..3 {
let idx = self.indices[index_cursor + i];
new_min = new_min.min(idx);
new_max = new_max.max(idx);
}
if new_max - new_min < MAX_SIZE {
// Triangle fits
min_vindex = new_min;
max_vindex = new_max;
index_cursor += 3;
} else {
break;
}
}
assert!(
index_cursor > span_start,
"One triangle spanned more than {} vertices",
MAX_SIZE
);
let mesh = Mesh16 {
2021-01-10 13:39:03 +00:00
indices: self.indices[span_start..index_cursor]
.iter()
.map(|vi| u16::try_from(vi - min_vindex).unwrap())
2021-01-10 13:39:03 +00:00
.collect(),
vertices: self.vertices[(min_vindex as usize)..=(max_vindex as usize)].to_vec(),
texture_id: self.texture_id,
};
crate::epaint_assert!(mesh.is_valid());
output.push(mesh);
2021-01-10 13:39:03 +00:00
}
output
}
/// Translate location by this much, in-place
pub fn translate(&mut self, delta: Vec2) {
for v in &mut self.vertices {
v.pos += delta;
}
}
/// Rotate by some angle about an origin, in-place.
///
/// Origin is a position in screen space.
pub fn rotate(&mut self, rot: Rot2, origin: Pos2) {
for v in &mut self.vertices {
v.pos = origin + rot * (v.pos - origin);
}
}
2021-01-10 13:39:03 +00:00
}
// ----------------------------------------------------------------------------
/// A version of [`Mesh`] that uses 16-bit indices.
///
/// This is produced by [`Mesh::split_to_u16`] and is meant to be used for legacy render backends.
pub struct Mesh16 {
/// Draw as triangles (i.e. the length is always multiple of three).
///
/// egui is NOT consistent with what winding order it uses, so turn off backface culling.
pub indices: Vec<u16>,
/// The vertex data indexed by `indices`.
pub vertices: Vec<Vertex>,
/// The texture to use when drawing these triangles.
pub texture_id: TextureId,
}
impl Mesh16 {
/// Are all indices within the bounds of the contained vertices?
pub fn is_valid(&self) -> bool {
2021-05-09 12:13:09 +00:00
if let Ok(n) = u16::try_from(self.vertices.len()) {
self.indices.iter().all(|&i| i < n)
} else {
false
}
}
}