diff --git a/Cargo.toml b/Cargo.toml index f3ac61b..8a1e204 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,4 @@ edition = "2021" bevy = "0.10.1" bevy_egui = "0.20.3" bevy-inspector-egui = "0.18.3" +earcutr = "0.4.2" diff --git a/src/polygon.rs b/src/polygon.rs index 3edde6a..0effeea 100644 --- a/src/polygon.rs +++ b/src/polygon.rs @@ -6,6 +6,8 @@ use bevy::{ sprite::MaterialMesh2dBundle, }; +use earcutr::earcut; + use crate::global_state::GlobalState; #[derive(Default, Debug, PartialEq)] @@ -145,8 +147,6 @@ impl Polygon { } } - self.order_points(); - // create triangle mesh let triangle_mesh = self.create_triangulated_mesh(); @@ -161,48 +161,10 @@ impl Polygon { .id(); // create 3d object - let vertices: Vec = self - .points - .iter() - .map(|p| vec![p.extend(0.0), p.extend(global_state.extrusion_height)]) - .flatten() - .collect(); - - let indices = Indices::U32({ - let mut v = Vec::new(); - - for i in 0..self.points.len() { - let index = (i as u32) * 2; - - // create quads from 4 adjacent points - v.push(index); - v.push((index + 2) % vertices.len() as u32); - v.push(index + 1); - v.push(index + 1); - v.push((index + 2) % vertices.len() as u32); - v.push((index + 3) % vertices.len() as u32); - } - - // TODO: proper ear cutting algo - for i in 2..self.points.len() as u32 { - let index = (i as u32) * 2 + 1; - - v.push(1); - v.push(index - 2); - v.push(index); - } - - v - }); - - let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - mesh.set_indices(Some(indices)); - mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vertices); - let mesh_id = commands .spawn(( MaterialMeshBundle { - mesh: meshes.add(mesh).into(), + mesh: meshes.add(self.create_3d_mesh(global_state)).into(), transform: Transform::default(), material: standard_materials.add(StandardMaterial::from(Color::BLACK)), ..default() @@ -274,23 +236,6 @@ impl Polygon { } } - fn order_points(&mut self) { - let n = Vec3::Z; - - let v1 = (self.points[0] - self.points[1]).normalize().extend(0.0); - let v2 = (self.points[1] - self.points[2]).normalize().extend(0.0); - - let c1 = v1.cross(n); - - let d = c1.dot(v2); - - // dot product is bigger than 0 if normal and connection line are facing in the same direction - // reverse the order of points, thus the face of the 3d object is directed to the outside - if d >= 0.0 { - self.points = self.points.iter().cloned().rev().collect(); - } - } - fn check_all_for_proximity(&self, point: Vec2) -> bool { self.points .iter() @@ -306,18 +251,22 @@ impl Polygon { fn create_triangulated_mesh(&self) -> Mesh { let vertices: Vec<_> = self.points.iter().map(|p| p.extend(0.0)).collect(); - // TODO: proper ear cutting algo - let indices = Indices::U32({ - let mut v = Vec::new(); - - for i in 2..self.points.len() as u32 { - v.push(0); - v.push(i - 1); - v.push(i); - } - - v - }); + let indices = Indices::U32( + earcut( + self.points + .iter() + .map(|p| vec![p.x, p.y]) + .flatten() + .collect::>() + .as_slice(), + &[], + 2, + ) + .unwrap() + .into_iter() + .map(|i| i as u32) + .collect(), + ); let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); mesh.set_indices(Some(indices)); @@ -326,6 +275,89 @@ impl Polygon { mesh } + fn create_3d_mesh(&self, global_state: &GlobalState) -> Mesh { + let mut outer_faces = { + let mean = + (self.points.iter().cloned().sum::() / self.points.len() as f32).extend(0.0); + + let v1: Vec = self + .points + .iter() + .map(|p| vec![p.extend(0.0), p.extend(global_state.extrusion_height)]) + .flatten() + .collect(); + + let mut v = Vec::new(); + + for i in 0..self.points.len() { + let index = (i) * 2; + + // create quads from 4 adjacent points + let mut p = vec![ + index, + (index + 2) % v1.len(), + index + 1, + index + 1, + (index + 2) % v1.len(), + (index + 3) % v1.len(), + ]; + + // check if the normal of the face is facing towards the center of the polygon + // if so, reverse the order that the normal is facing outwards + let n = (v1[p[0]] - v1[p[1]]).cross(v1[p[1]] - v1[p[2]]); + + if 0.0 > n.dot(v1[p[0]] - mean) { + p.reverse(); + } + + v.extend(p); + } + + let mut vertices = Vec::new(); + + for i in v { + vertices.push(v1[i]); + } + + vertices + }; + + let top = { + let top_vertices: Vec = self + .points + .iter() + .map(|p| p.extend(global_state.extrusion_height)) + .collect(); + + let indices = earcut( + self.points + .iter() + .map(|v| [v.x, v.y]) + .flatten() + .collect::>() + .as_slice(), + &[], + 2, + ) + .unwrap(); + + let mut vertices = Vec::new(); + + for i in indices.into_iter() { + vertices.push(top_vertices[i]); + } + + vertices + }; + + outer_faces.extend(top); + + let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); + mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, outer_faces); + + mesh + } + fn create_line_mesh(start: Vec2, end: Vec2) -> Mesh { let d = start.distance(end);