1use bevy::prelude::*;
2
3type EarcutrIndices = Vec<usize>;
4type EarcutrVertices<T> = Vec<T>;
5type BevyIndices = Vec<u32>;
6type BevyVertices = Vec<[f32; 3]>;
7
8#[derive(Debug)]
9pub struct EarcutrInput<T: num_traits::Float> {
10 pub vertices: EarcutrVertices<T>,
11 pub interior_indices: EarcutrIndices,
12}
13
14#[derive(Debug)]
15pub struct EarcutrResult<T: num_traits::Float> {
16 pub vertices: EarcutrVertices<T>,
17 pub triangle_indices: EarcutrIndices,
18}
19
20impl<T: num_traits::Float> EarcutrResult<T> {
21 fn merge(&mut self, mut other: EarcutrResult<T>) {
22 let base_triangle_index = self.vertices.len() / 2;
23 for other_triangle_index in other.triangle_indices {
24 self.triangle_indices
25 .push(other_triangle_index + base_triangle_index);
26 }
27 self.vertices.append(&mut other.vertices);
28 }
29}
30
31pub struct PolygonMeshBuilder<T: num_traits::Float> {
32 earcutr_inputs: Vec<EarcutrInput<T>>,
33 z_index: T,
34}
35
36impl<T: num_traits::Float> PolygonMeshBuilder<T> {
37 pub fn with_z_index(mut self, z_index: T) -> Self {
38 self.z_index = z_index;
39 self
40 }
41
42 pub fn add_earcutr_input(&mut self, earcutr_input: EarcutrInput<T>) {
44 self.earcutr_inputs.push(earcutr_input);
45 }
46
47 pub fn build(self) -> Result<Mesh, Error> {
48 let z_index = self.z_index;
49 let result = self.run_earcutr()?;
50 build_mesh_from_earcutr(result, z_index)
51 }
52
53 fn run_earcutr(self) -> Result<EarcutrResult<T>, Error> {
54 let mut earcutr_inputs_iter = self.earcutr_inputs.into_iter();
55
56 let first_input = earcutr_inputs_iter.next().ok_or(Error::EmptyInput)?;
58 let first_triangle_indices =
59 earcutr::earcut(&first_input.vertices, &first_input.interior_indices, 2).unwrap();
60 let mut earcutr_result = EarcutrResult {
61 triangle_indices: first_triangle_indices,
62 vertices: first_input.vertices,
63 };
64
65 for earcutr_input in earcutr_inputs_iter {
67 let EarcutrInput {
68 vertices,
69 interior_indices,
70 } = earcutr_input;
71 let next_earcutr_result = earcutr::earcut(&vertices, &interior_indices, 2).unwrap();
72 earcutr_result.merge(EarcutrResult {
73 triangle_indices: next_earcutr_result,
74 vertices,
75 });
76 }
77
78 Ok(earcutr_result)
79 }
80}
81
82impl<T: num_traits::Float> Default for PolygonMeshBuilder<T> {
83 fn default() -> Self {
84 PolygonMeshBuilder {
85 earcutr_inputs: vec![],
86 z_index: T::zero(),
87 }
88 }
89}
90
91#[derive(Debug)]
92pub enum Error {
93 EmptyInput,
94 CouldNotConvertToF32,
95}
96
97pub fn build_mesh_from_earcutr<T: num_traits::Float>(
98 earcutr_result: EarcutrResult<T>,
99 z_index: T,
100) -> Result<Mesh, Error> {
101 let indices = earcutr_result
102 .triangle_indices
103 .into_iter()
104 .map(|n| u32::try_from(n).unwrap())
105 .collect::<Vec<_>>();
106 let vertices = earcutr_result
107 .vertices
108 .chunks(2)
109 .map(|n| {
110 let x = n[0].to_f32().ok_or(Error::CouldNotConvertToF32)?;
111 let y = n[1].to_f32().ok_or(Error::CouldNotConvertToF32)?;
112 let z = z_index.to_f32().ok_or(Error::CouldNotConvertToF32)?;
113 Ok([x, y, z])
114 })
115 .collect::<Result<Vec<_>, _>>()?;
116 Ok(build_mesh_from_bevy(indices, vertices))
117}
118
119fn build_mesh_from_bevy(triangle_indices: BevyIndices, vertices: BevyVertices) -> Mesh {
120 let num_vertices = vertices.len();
121 let mut mesh = Mesh::new(
122 bevy::render::render_resource::PrimitiveTopology::TriangleList,
123 Default::default(),
124 );
125 mesh.insert_indices(bevy::render::mesh::Indices::U32(triangle_indices));
126 mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vertices);
127
128 let normals = vec![[0.0, 0.0, 0.0]; num_vertices];
129 let uvs = vec![[0.0, 0.0]; num_vertices];
130
131 mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals);
132 mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs);
133
134 mesh
135}