epaint/
mesh.rs

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