diff --git a/CHANGELOG.md b/CHANGELOG.md index 4705a261..7e9e7763 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * `DragValue::range` is now called `clamp_range` and also clamps incoming values. * Renamed `Triangles` to `Mesh`. * The tesselator now wraps the clip rectangle and mesh in `struct ClippedMesh(Rect, Mesh)`. +* `Mesh::split_to_u16` now returns a 16-bit indexed `Mesh16`. ### Fixed 🐛 diff --git a/egui_web/src/webgl1.rs b/egui_web/src/webgl1.rs index b4b61ff1..b8ed31fe 100644 --- a/egui_web/src/webgl1.rs +++ b/egui_web/src/webgl1.rs @@ -6,7 +6,7 @@ use { use egui::{ math::clamp, - paint::{Color32, Mesh, Texture}, + paint::{Color32, Texture}, vec2, }; @@ -265,9 +265,8 @@ impl WebGlPainter { } } - fn paint_mesh(&self, mesh: &Mesh) -> Result<(), JsValue> { + fn paint_mesh(&self, mesh: &egui::paint::Mesh16) -> Result<(), JsValue> { debug_assert!(mesh.is_valid()); - let indices: Vec = mesh.indices.iter().map(|idx| *idx as u16).collect(); let mut positions: Vec = Vec::with_capacity(2 * mesh.vertices.len()); let mut tex_coords: Vec = Vec::with_capacity(2 * mesh.vertices.len()); @@ -293,9 +292,9 @@ impl WebGlPainter { let indices_memory_buffer = wasm_bindgen::memory() .dyn_into::()? .buffer(); - let indices_ptr = indices.as_ptr() as u32 / 2; + let indices_ptr = mesh.indices.as_ptr() as u32 / 2; let indices_array = js_sys::Int16Array::new(&indices_memory_buffer) - .subarray(indices_ptr, indices_ptr + indices.len() as u32); + .subarray(indices_ptr, indices_ptr + mesh.indices.len() as u32); gl.bind_buffer(Gl::ELEMENT_ARRAY_BUFFER, Some(&self.index_buffer)); gl.buffer_data_with_array_buffer_view( @@ -379,7 +378,12 @@ impl WebGlPainter { // -------------------------------------------------------------------- - gl.draw_elements_with_i32(Gl::TRIANGLES, indices.len() as i32, Gl::UNSIGNED_SHORT, 0); + gl.draw_elements_with_i32( + Gl::TRIANGLES, + mesh.indices.len() as i32, + Gl::UNSIGNED_SHORT, + 0, + ); Ok(()) } diff --git a/egui_web/src/webgl2.rs b/egui_web/src/webgl2.rs index 92faf208..c46ec25e 100644 --- a/egui_web/src/webgl2.rs +++ b/egui_web/src/webgl2.rs @@ -8,7 +8,7 @@ use { use egui::{ math::clamp, - paint::{Color32, Mesh, Texture}, + paint::{Color32, Texture}, vec2, }; @@ -255,9 +255,8 @@ impl WebGl2Painter { } } - fn paint_mesh(&self, mesh: &Mesh) -> Result<(), JsValue> { + fn paint_mesh(&self, mesh: &egui::paint::Mesh16) -> Result<(), JsValue> { debug_assert!(mesh.is_valid()); - let indices: Vec = mesh.indices.iter().map(|idx| *idx as u16).collect(); let mut positions: Vec = Vec::with_capacity(2 * mesh.vertices.len()); let mut tex_coords: Vec = Vec::with_capacity(2 * mesh.vertices.len()); @@ -283,9 +282,9 @@ impl WebGl2Painter { let indices_memory_buffer = wasm_bindgen::memory() .dyn_into::()? .buffer(); - let indices_ptr = indices.as_ptr() as u32 / 2; + let indices_ptr = mesh.indices.as_ptr() as u32 / 2; let indices_array = js_sys::Int16Array::new(&indices_memory_buffer) - .subarray(indices_ptr, indices_ptr + indices.len() as u32); + .subarray(indices_ptr, indices_ptr + mesh.indices.len() as u32); gl.bind_buffer(Gl::ELEMENT_ARRAY_BUFFER, Some(&self.index_buffer)); gl.buffer_data_with_array_buffer_view( @@ -369,7 +368,12 @@ impl WebGl2Painter { // -------------------------------------------------------------------- - gl.draw_elements_with_i32(Gl::TRIANGLES, indices.len() as i32, Gl::UNSIGNED_SHORT, 0); + gl.draw_elements_with_i32( + Gl::TRIANGLES, + mesh.indices.len() as i32, + Gl::UNSIGNED_SHORT, + 0, + ); Ok(()) } diff --git a/epaint/src/lib.rs b/epaint/src/lib.rs index 18b32e08..b7bdb231 100644 --- a/epaint/src/lib.rs +++ b/epaint/src/lib.rs @@ -57,7 +57,7 @@ mod texture_atlas; pub use { color::{Color32, Rgba}, - mesh::{Mesh, Vertex}, + mesh::{Mesh, Mesh16, Vertex}, shadow::Shadow, shape::Shape, stats::PaintStats, diff --git a/epaint/src/mesh.rs b/epaint/src/mesh.rs index 7151bde3..45394e7d 100644 --- a/epaint/src/mesh.rs +++ b/epaint/src/mesh.rs @@ -25,6 +25,8 @@ pub struct Vertex { pub struct Mesh { /// Draw as triangles (i.e. the length is always multiple of three). /// + /// If you only support 16-bit indices you can use [`Mesh::split_to_u16`]. + /// /// egui is NOT consistent with what winding order it uses, so turn off backface culling. pub indices: Vec, @@ -51,8 +53,12 @@ impl Mesh { /// Are all indices within the bounds of the contained vertices? pub fn is_valid(&self) -> bool { - let n = self.vertices.len() as u32; - self.indices.iter().all(|&i| i < n) + if self.vertices.len() <= u32::MAX as usize { + let n = self.vertices.len() as u32; + self.indices.iter().all(|&i| i < n) + } else { + false + } } pub fn is_empty(&self) -> bool { @@ -149,13 +155,20 @@ impl Mesh { /// This is for platforms that only support 16-bit index buffers. /// - /// Splits this mesh into many smaller meshes (if needed). - /// All the returned meshes will have indices that fit into a `u16`. - pub fn split_to_u16(self) -> Vec { + /// Splits this mesh into many smaller meshes (if needed) + /// where the smaller meshes have 16-bit indices. + pub fn split_to_u16(self) -> Vec { + debug_assert!(self.is_valid()); + const MAX_SIZE: u32 = 1 << 16; if self.vertices.len() < MAX_SIZE as usize { - return vec![self]; // Common-case optimization + // Common-case optimization: + return vec![Mesh16 { + indices: self.indices.iter().map(|&i| i as u16).collect(), + vertices: self.vertices, + texture_id: self.texture_id, + }]; } let mut output = vec![]; @@ -190,14 +203,17 @@ impl Mesh { MAX_SIZE ); - output.push(Mesh { + use std::convert::TryFrom; + let mesh = Mesh16 { indices: self.indices[span_start..index_cursor] .iter() - .map(|vi| vi - min_vindex) + .map(|vi| u16::try_from(vi - min_vindex).unwrap()) .collect(), vertices: self.vertices[(min_vindex as usize)..=(max_vindex as usize)].to_vec(), texture_id: self.texture_id, - }); + }; + debug_assert!(mesh.is_valid()); + output.push(mesh); } output } @@ -209,3 +225,33 @@ impl Mesh { } } } + +// ---------------------------------------------------------------------------- + +/// A version of [`Mesh`] that uses 16-bit indices. +/// +/// This is produced by [`Mesh::split_to_u16`] and is meant to be used for legacy render backends. +pub struct Mesh16 { + /// Draw as triangles (i.e. the length is always multiple of three). + /// + /// egui is NOT consistent with what winding order it uses, so turn off backface culling. + pub indices: Vec, + + /// The vertex data indexed by `indices`. + pub vertices: Vec, + + /// The texture to use when drawing these triangles. + pub texture_id: TextureId, +} + +impl Mesh16 { + /// Are all indices within the bounds of the contained vertices? + pub fn is_valid(&self) -> bool { + if self.vertices.len() <= u16::MAX as usize { + let n = self.vertices.len() as u16; + self.indices.iter().all(|&i| i < n) + } else { + false + } + } +}