epaint/
mesh.rs

1use crate::{Color32, TextureId, WHITE_UV, emath};
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(), "Other mesh is invalid");
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(), "Other mesh is invalid");
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!(
159            self.texture_id == TextureId::default(),
160            "Mesh has an assigned texture"
161        );
162        self.vertices.push(Vertex {
163            pos,
164            uv: WHITE_UV,
165            color,
166        });
167    }
168
169    /// Add a triangle.
170    #[inline(always)]
171    pub fn add_triangle(&mut self, a: u32, b: u32, c: u32) {
172        self.indices.push(a);
173        self.indices.push(b);
174        self.indices.push(c);
175    }
176
177    /// Make room for this many additional triangles (will reserve 3x as many indices).
178    /// See also `reserve_vertices`.
179    #[inline(always)]
180    pub fn reserve_triangles(&mut self, additional_triangles: usize) {
181        self.indices.reserve(3 * additional_triangles);
182    }
183
184    /// Make room for this many additional vertices.
185    /// See also `reserve_triangles`.
186    #[inline(always)]
187    pub fn reserve_vertices(&mut self, additional: usize) {
188        self.vertices.reserve(additional);
189    }
190
191    /// Rectangle with a texture and color.
192    pub fn add_rect_with_uv(&mut self, rect: Rect, uv: Rect, color: Color32) {
193        #![allow(clippy::identity_op)]
194
195        let idx = self.vertices.len() as u32;
196        self.add_triangle(idx + 0, idx + 1, idx + 2);
197        self.add_triangle(idx + 2, idx + 1, idx + 3);
198
199        self.vertices.push(Vertex {
200            pos: rect.left_top(),
201            uv: uv.left_top(),
202            color,
203        });
204        self.vertices.push(Vertex {
205            pos: rect.right_top(),
206            uv: uv.right_top(),
207            color,
208        });
209        self.vertices.push(Vertex {
210            pos: rect.left_bottom(),
211            uv: uv.left_bottom(),
212            color,
213        });
214        self.vertices.push(Vertex {
215            pos: rect.right_bottom(),
216            uv: uv.right_bottom(),
217            color,
218        });
219    }
220
221    /// Uniformly colored rectangle.
222    #[inline(always)]
223    pub fn add_colored_rect(&mut self, rect: Rect, color: Color32) {
224        debug_assert!(
225            self.texture_id == TextureId::default(),
226            "Mesh has an assigned texture"
227        );
228        self.add_rect_with_uv(rect, [WHITE_UV, WHITE_UV].into(), color);
229    }
230
231    /// This is for platforms that only support 16-bit index buffers.
232    ///
233    /// Splits this mesh into many smaller meshes (if needed)
234    /// where the smaller meshes have 16-bit indices.
235    pub fn split_to_u16(self) -> Vec<Mesh16> {
236        debug_assert!(self.is_valid(), "Mesh is invalid");
237
238        const MAX_SIZE: u32 = u16::MAX as u32;
239
240        if self.vertices.len() <= MAX_SIZE as usize {
241            // Common-case optimization:
242            return vec![Mesh16 {
243                indices: self.indices.iter().map(|&i| i as u16).collect(),
244                vertices: self.vertices,
245                texture_id: self.texture_id,
246            }];
247        }
248
249        let mut output = vec![];
250        let mut index_cursor = 0;
251
252        while index_cursor < self.indices.len() {
253            let span_start = index_cursor;
254            let mut min_vindex = self.indices[index_cursor];
255            let mut max_vindex = self.indices[index_cursor];
256
257            while index_cursor < self.indices.len() {
258                let (mut new_min, mut new_max) = (min_vindex, max_vindex);
259                for i in 0..3 {
260                    let idx = self.indices[index_cursor + i];
261                    new_min = new_min.min(idx);
262                    new_max = new_max.max(idx);
263                }
264
265                let new_span_size = new_max - new_min + 1; // plus one, because it is an inclusive range
266                if new_span_size <= MAX_SIZE {
267                    // Triangle fits
268                    min_vindex = new_min;
269                    max_vindex = new_max;
270                    index_cursor += 3;
271                } else {
272                    break;
273                }
274            }
275
276            assert!(
277                index_cursor > span_start,
278                "One triangle spanned more than {MAX_SIZE} vertices"
279            );
280
281            let mesh = Mesh16 {
282                indices: self.indices[span_start..index_cursor]
283                    .iter()
284                    .map(|vi| u16::try_from(vi - min_vindex).unwrap())
285                    .collect(),
286                vertices: self.vertices[(min_vindex as usize)..=(max_vindex as usize)].to_vec(),
287                texture_id: self.texture_id,
288            };
289            debug_assert!(mesh.is_valid(), "Mesh is invalid");
290            output.push(mesh);
291        }
292        output
293    }
294
295    /// Translate location by this much, in-place
296    pub fn translate(&mut self, delta: Vec2) {
297        for v in &mut self.vertices {
298            v.pos += delta;
299        }
300    }
301
302    /// Transform the mesh in-place with the given transform.
303    pub fn transform(&mut self, transform: TSTransform) {
304        for v in &mut self.vertices {
305            v.pos = transform * v.pos;
306        }
307    }
308
309    /// Rotate by some angle about an origin, in-place.
310    ///
311    /// Origin is a position in screen space.
312    pub fn rotate(&mut self, rot: Rot2, origin: Pos2) {
313        for v in &mut self.vertices {
314            v.pos = origin + rot * (v.pos - origin);
315        }
316    }
317}
318
319// ----------------------------------------------------------------------------
320
321/// A version of [`Mesh`] that uses 16-bit indices.
322///
323/// This is produced by [`Mesh::split_to_u16`] and is meant to be used for legacy render backends.
324pub struct Mesh16 {
325    /// Draw as triangles (i.e. the length is always multiple of three).
326    ///
327    /// egui is NOT consistent with what winding order it uses, so turn off backface culling.
328    pub indices: Vec<u16>,
329
330    /// The vertex data indexed by `indices`.
331    pub vertices: Vec<Vertex>,
332
333    /// The texture to use when drawing these triangles.
334    pub texture_id: TextureId,
335}
336
337impl Mesh16 {
338    /// Are all indices within the bounds of the contained vertices?
339    pub fn is_valid(&self) -> bool {
340        if let Ok(n) = u16::try_from(self.vertices.len()) {
341            self.indices.iter().all(|&i| i < n)
342        } else {
343            false
344        }
345    }
346}