bevy_earcutr/
lib.rs

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    /// Call for `add_earcutr_input` for each polygon you want to add to the mesh.
43    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        // Earcut the first polygon
57        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        // Earcut any additional polygons and merge the results into the result of the first polygon
66        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}