diff --git a/Cargo.lock b/Cargo.lock index cc9270e..ab8b9fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -705,18 +705,18 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytemuck" -version = "1.23.2" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.10.1" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", @@ -975,6 +975,7 @@ version = "0.20.1" dependencies = [ "bevy_asset", "bevy_mesh", + "bytemuck", "chull", "contour_tracing", "core2", diff --git a/Cargo.toml b/Cargo.toml index 476a74c..c022ed7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,6 +82,7 @@ js-sys = { version = "0.3.81", optional = true } serde-wasm-bindgen = { version = "0.6.5", optional = true } serde_json = { version = "1.0.145", optional = true } serde = { version = "1.0.228", optional = true } +bytemuck = "1.24.0" [target.'cfg(target_arch = "wasm32")'.dependencies] uuid = { version = "1.18", features = ["js"] } diff --git a/src/mesh/mod.rs b/src/mesh/mod.rs index ae8bbab..c14b9c2 100644 --- a/src/mesh/mod.rs +++ b/src/mesh/mod.rs @@ -15,13 +15,16 @@ use crate::float_types::{ use crate::mesh::{bsp::Node, plane::Plane, polygon::Polygon, vertex::Vertex}; use crate::sketch::Sketch; use crate::traits::CSG; +use bytemuck::cast; use geo::{CoordsIter, Geometry, Polygon as GeoPolygon}; use hashbrown::HashMap; use nalgebra::{ Isometry3, Matrix4, Point3, Quaternion, Unit, Vector3, partial_max, partial_min, }; + use std::{ cmp::{Ordering, PartialEq}, + collections::HashMap, fmt::Debug, num::NonZeroU32, sync::OnceLock, @@ -53,6 +56,18 @@ pub mod smoothing; pub mod tpms; pub mod vertex; +/// Stored as `(position: [f32; 3], normal: [f32; 3])` +pub type GraphicsMeshVertex = ([f32; 3], [f32; 3]); + +/// Mesh ready for rendering. Uses f32s and provides vertices, indices and +/// normals. +#[derive(Debug, Clone)] +pub struct GraphicsMesh { + /// Vertices contain both position and normal + pub vertices: Vec, + pub indices: Vec, +} + #[derive(Clone, Debug)] pub struct Mesh { /// 3D polygons for volumetric shapes @@ -396,7 +411,51 @@ impl Mesh { dot.acos() } + /// Converts a mesh to a graphics mesh which is more appropriate for rendering. + /// + /// Allocates a hashmap and a storage vec for removing redundant vertices. + pub fn build_graphics_mesh(&self) -> GraphicsMesh { + let triangles = self.triangulate().polygons; + + let triangle_count = triangles.len(); + + let mut indices: Vec = Vec::with_capacity(triangle_count * 3); + let mut vertices: Vec = Vec::with_capacity(triangle_count * 3); + const VERT_DIM_SIZE: usize = std::mem::size_of::<[f32; 3]>(); + let mut vertices_hash: HashMap<([u8; VERT_DIM_SIZE], [u8; VERT_DIM_SIZE]), u32> = + HashMap::with_capacity(triangle_count * 3); + + let mut i_new_vertex: u32 = 0; + + for triangle in triangles { + for vertex in triangle.vertices { + let pos_xyz: [f32; 3] = vertex.pos.cast::().coords.into(); + let norm_xyz: [f32; 3] = vertex.normal.cast::().into(); + + let pos_xyz_bytes: [u8; VERT_DIM_SIZE] = cast(pos_xyz); + let norm_xyz_bytes: [u8; VERT_DIM_SIZE] = cast(norm_xyz); + + let vertex_f32_bytes = (pos_xyz_bytes, norm_xyz_bytes); + + let index = *vertices_hash.entry(vertex_f32_bytes).or_insert_with(|| { + let new_index = i_new_vertex; + vertices.push((pos_xyz, norm_xyz)); + i_new_vertex += 1; + new_index + }); + + indices.push(index); + } + } + + vertices.shrink_to_fit(); + + GraphicsMesh { vertices, indices } + } + /// Extracts vertices and indices from the Mesh's tessellated polygons. + /// + /// Implementation does *not* remove redundant vertices. pub fn get_vertices_and_indices(&self) -> (Vec>, Vec<[u32; 3]>) { let tri_csg = self.triangulate(); let vertices = tri_csg