epaint/mesh.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
use crate::{emath, Color32, TextureId, WHITE_UV};
use emath::{Pos2, Rect, Rot2, TSTransform, Vec2};
/// The 2D vertex type.
///
/// Should be friendly to send to GPU as is.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[cfg(not(feature = "unity"))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
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
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[cfg(feature = "unity")]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
pub struct Vertex {
/// Logical pixel coordinates (points).
/// (0,0) is the top left corner of the screen.
pub pos: Pos2, // 64 bit
/// sRGBA with premultiplied alpha
pub color: Color32, // 32 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
}
/// Textured triangles in two dimensions.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Mesh {
/// 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.
pub indices: Vec<u32>,
/// The vertex data indexed by `indices`.
pub vertices: Vec<Vertex>,
/// The texture to use when drawing these triangles.
pub texture_id: TextureId,
// TODO(emilk): bounding rectangle
}
impl Mesh {
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();
}
/// Returns the amount of memory used by the vertices and indices.
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 {
crate::profile_function!();
if let Ok(n) = u32::try_from(self.vertices.len()) {
self.indices.iter().all(|&i| i < n)
} else {
false
}
}
pub fn is_empty(&self) -> bool {
self.indices.is_empty() && self.vertices.is_empty()
}
/// 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
}
/// Append all the indices and vertices of `other` to `self`.
///
/// Panics when `other` mesh has a different texture.
pub fn append(&mut self, other: Self) {
crate::profile_function!();
debug_assert!(other.is_valid());
if self.is_empty() {
*self = other;
} else {
self.append_ref(&other);
}
}
/// Append all the indices and vertices of `other` to `self` without
/// taking ownership.
///
/// Panics when `other` mesh has a different texture.
pub fn append_ref(&mut self, other: &Self) {
debug_assert!(other.is_valid());
if self.is_empty() {
self.texture_id = other.texture_id;
} else {
assert_eq!(
self.texture_id, other.texture_id,
"Can't merge Mesh using different textures"
);
}
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());
}
/// Add a colored vertex.
///
/// Panics when the mesh has assigned a texture.
#[inline(always)]
pub fn colored_vertex(&mut self, pos: Pos2, color: Color32) {
debug_assert!(self.texture_id == TextureId::default());
self.vertices.push(Vertex {
pos,
uv: WHITE_UV,
color,
});
}
/// Add a triangle.
#[inline(always)]
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)]
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)]
pub fn reserve_vertices(&mut self, additional: usize) {
self.vertices.reserve(additional);
}
/// Rectangle with a texture and color.
pub fn add_rect_with_uv(&mut self, rect: Rect, uv: Rect, color: Color32) {
#![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);
self.vertices.push(Vertex {
pos: rect.left_top(),
uv: uv.left_top(),
color,
});
self.vertices.push(Vertex {
pos: rect.right_top(),
uv: uv.right_top(),
color,
});
self.vertices.push(Vertex {
pos: rect.left_bottom(),
uv: uv.left_bottom(),
color,
});
self.vertices.push(Vertex {
pos: rect.right_bottom(),
uv: uv.right_bottom(),
color,
});
}
/// Uniformly colored rectangle.
#[inline(always)]
pub fn add_colored_rect(&mut self, rect: Rect, color: Color32) {
debug_assert!(self.texture_id == TextureId::default());
self.add_rect_with_uv(rect, [WHITE_UV, WHITE_UV].into(), color);
}
/// 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> {
debug_assert!(self.is_valid());
const MAX_SIZE: u32 = u16::MAX as u32;
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,
}];
}
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);
}
let new_span_size = new_max - new_min + 1; // plus one, because it is an inclusive range
if new_span_size <= 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 {MAX_SIZE} vertices"
);
let mesh = Mesh16 {
indices: self.indices[span_start..index_cursor]
.iter()
.map(|vi| u16::try_from(vi - min_vindex).unwrap())
.collect(),
vertices: self.vertices[(min_vindex as usize)..=(max_vindex as usize)].to_vec(),
texture_id: self.texture_id,
};
debug_assert!(mesh.is_valid());
output.push(mesh);
}
output
}
/// Translate location by this much, in-place
pub fn translate(&mut self, delta: Vec2) {
for v in &mut self.vertices {
v.pos += delta;
}
}
/// Transform the mesh in-place with the given transform.
pub fn transform(&mut self, transform: TSTransform) {
for v in &mut self.vertices {
v.pos = transform * v.pos;
}
}
/// 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);
}
}
}
// ----------------------------------------------------------------------------
/// 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 {
if let Ok(n) = u16::try_from(self.vertices.len()) {
self.indices.iter().all(|&i| i < n)
} else {
false
}
}
}