1use crate::{Color32, TextureId, WHITE_UV, emath};
2use emath::{Pos2, Rect, Rot2, TSTransform, Vec2};
3
4#[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 pub pos: Pos2, pub uv: Pos2, pub color: Color32, }
25
26impl Vertex {
27 #[inline]
29 pub fn untextured(pos: Pos2, color: Color32) -> Self {
30 Self {
31 pos,
32 uv: WHITE_UV,
33 color,
34 }
35 }
36}
37
38#[repr(C)]
39#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
40#[cfg(all(feature = "unity", not(feature = "_override_unity")))]
41#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
42#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
43pub struct Vertex {
44 pub pos: Pos2, pub color: Color32, pub uv: Pos2, }
56
57#[derive(Clone, Debug, Default, PartialEq, Eq)]
59#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
60pub struct Mesh {
61 pub indices: Vec<u32>,
67
68 pub vertices: Vec<Vertex>,
70
71 pub texture_id: TextureId,
73 }
75
76impl Mesh {
77 pub fn with_texture(texture_id: TextureId) -> Self {
78 Self {
79 texture_id,
80 ..Default::default()
81 }
82 }
83
84 pub fn clear(&mut self) {
86 self.indices.clear();
87 self.vertices.clear();
88 self.vertices = Default::default();
89 }
90
91 pub fn bytes_used(&self) -> usize {
93 std::mem::size_of::<Self>()
94 + self.vertices.len() * std::mem::size_of::<Vertex>()
95 + self.indices.len() * std::mem::size_of::<u32>()
96 }
97
98 pub fn is_valid(&self) -> bool {
100 profiling::function_scope!();
101
102 if let Ok(n) = u32::try_from(self.vertices.len()) {
103 self.indices.iter().all(|&i| i < n)
104 } else {
105 false
106 }
107 }
108
109 pub fn is_empty(&self) -> bool {
110 self.indices.is_empty() && self.vertices.is_empty()
111 }
112
113 pub fn triangles(&self) -> impl Iterator<Item = [u32; 3]> + '_ {
115 self.indices
116 .chunks_exact(3)
117 .map(|chunk| [chunk[0], chunk[1], chunk[2]])
118 }
119
120 pub fn calc_bounds(&self) -> Rect {
122 let mut bounds = Rect::NOTHING;
123 for v in &self.vertices {
124 bounds.extend_with(v.pos);
125 }
126 bounds
127 }
128
129 pub fn append(&mut self, other: Self) {
133 profiling::function_scope!();
134 debug_assert!(other.is_valid(), "Other mesh is invalid");
135
136 if self.is_empty() {
137 *self = other;
138 } else {
139 self.append_ref(&other);
140 }
141 }
142
143 pub fn append_ref(&mut self, other: &Self) {
148 debug_assert!(other.is_valid(), "Other mesh is invalid");
149
150 if self.is_empty() {
151 self.texture_id = other.texture_id;
152 } else {
153 assert_eq!(
154 self.texture_id, other.texture_id,
155 "Can't merge Mesh using different textures"
156 );
157 }
158
159 let index_offset = self.vertices.len() as u32;
160 self.indices
161 .extend(other.indices.iter().map(|index| index + index_offset));
162 self.vertices.extend(other.vertices.iter());
163 }
164
165 #[inline(always)]
169 pub fn colored_vertex(&mut self, pos: Pos2, color: Color32) {
170 debug_assert!(
171 self.texture_id == TextureId::default(),
172 "Mesh has an assigned texture"
173 );
174 self.vertices.push(Vertex::untextured(pos, color));
175 }
176
177 #[inline(always)]
179 pub fn add_triangle(&mut self, a: u32, b: u32, c: u32) {
180 self.indices.extend_from_slice(&[a, b, c]);
181 }
182
183 #[inline(always)]
186 pub fn reserve_triangles(&mut self, additional_triangles: usize) {
187 self.indices.reserve(3 * additional_triangles);
188 }
189
190 #[inline(always)]
193 pub fn reserve_vertices(&mut self, additional: usize) {
194 self.vertices.reserve(additional);
195 }
196
197 #[inline(always)]
199 pub fn add_rect_with_uv(&mut self, rect: Rect, uv: Rect, color: Color32) {
200 #![expect(clippy::identity_op)]
201 let idx = self.vertices.len() as u32;
202 self.indices
203 .extend_from_slice(&[idx + 0, idx + 1, idx + 2, idx + 2, idx + 1, idx + 3]);
204
205 self.vertices.extend_from_slice(&[
206 Vertex {
207 pos: rect.left_top(),
208 uv: uv.left_top(),
209 color,
210 },
211 Vertex {
212 pos: rect.right_top(),
213 uv: uv.right_top(),
214 color,
215 },
216 Vertex {
217 pos: rect.left_bottom(),
218 uv: uv.left_bottom(),
219 color,
220 },
221 Vertex {
222 pos: rect.right_bottom(),
223 uv: uv.right_bottom(),
224 color,
225 },
226 ]);
227 }
228
229 #[inline(always)]
231 pub fn add_colored_rect(&mut self, rect: Rect, color: Color32) {
232 debug_assert!(
233 self.texture_id == TextureId::default(),
234 "Mesh has an assigned texture"
235 );
236 self.add_rect_with_uv(rect, [WHITE_UV, WHITE_UV].into(), color);
237 }
238
239 pub fn split_to_u16(self) -> Vec<Mesh16> {
244 debug_assert!(self.is_valid(), "Mesh is invalid");
245
246 const MAX_SIZE: u32 = u16::MAX as u32;
247
248 if self.vertices.len() <= MAX_SIZE as usize {
249 return vec![Mesh16 {
251 indices: self.indices.iter().map(|&i| i as u16).collect(),
252 vertices: self.vertices,
253 texture_id: self.texture_id,
254 }];
255 }
256
257 let mut output = vec![];
258 let mut index_cursor = 0;
259
260 while index_cursor < self.indices.len() {
261 let span_start = index_cursor;
262 let mut min_vindex = self.indices[index_cursor];
263 let mut max_vindex = self.indices[index_cursor];
264
265 while index_cursor < self.indices.len() {
266 let (mut new_min, mut new_max) = (min_vindex, max_vindex);
267 for i in 0..3 {
268 let idx = self.indices[index_cursor + i];
269 new_min = new_min.min(idx);
270 new_max = new_max.max(idx);
271 }
272
273 let new_span_size = new_max - new_min + 1; if new_span_size <= MAX_SIZE {
275 min_vindex = new_min;
277 max_vindex = new_max;
278 index_cursor += 3;
279 } else {
280 break;
281 }
282 }
283
284 assert!(
285 index_cursor > span_start,
286 "One triangle spanned more than {MAX_SIZE} vertices"
287 );
288
289 let mesh = Mesh16 {
290 indices: self.indices[span_start..index_cursor]
291 .iter()
292 .map(|vi| {
293 #[expect(clippy::unwrap_used)]
294 {
295 u16::try_from(vi - min_vindex).unwrap()
296 }
297 })
298 .collect(),
299 vertices: self.vertices[(min_vindex as usize)..=(max_vindex as usize)].to_vec(),
300 texture_id: self.texture_id,
301 };
302 debug_assert!(mesh.is_valid(), "Mesh is invalid");
303 output.push(mesh);
304 }
305 output
306 }
307
308 pub fn translate(&mut self, delta: Vec2) {
310 for v in &mut self.vertices {
311 v.pos += delta;
312 }
313 }
314
315 pub fn transform(&mut self, transform: TSTransform) {
317 for v in &mut self.vertices {
318 v.pos = transform * v.pos;
319 }
320 }
321
322 pub fn rotate(&mut self, rot: Rot2, origin: Pos2) {
326 for v in &mut self.vertices {
327 v.pos = origin + rot * (v.pos - origin);
328 }
329 }
330}
331
332pub struct Mesh16 {
338 pub indices: Vec<u16>,
342
343 pub vertices: Vec<Vertex>,
345
346 pub texture_id: TextureId,
348}
349
350impl Mesh16 {
351 pub fn is_valid(&self) -> bool {
353 if let Ok(n) = u16::try_from(self.vertices.len()) {
354 self.indices.iter().all(|&i| i < n)
355 } else {
356 false
357 }
358 }
359}