epaint/
mesh.rs

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