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