hedron/solid/
mesh.rs

1use std::io::Write;
2
3use crate::kernel::{fxx, vec2, vec3, Vec2, Vec3};
4
5use crate::{core::PointBased, data::Grid2};
6
7#[derive(Default, Debug)]
8pub struct Mesh {
9    pub verts: Vec<Vec3>,
10    pub uvs: Vec<Vec2>,
11    pub tri: Vec<usize>,
12}
13
14impl Mesh {
15    pub fn new(verts: Vec<Vec3>, tri: Vec<usize>, uvs: Vec<Vec2>) -> Self {
16        Self { verts, tri, uvs }
17    }
18
19    // Get a grid mesh from weaving vertices of a grid.
20    // The grid can deal with holes, use None vertices to indicate holes
21    // We do this by finding 'valid cells' of 4 vertices, and lacing them like this:
22    // Corners of 3 vertices are also created.
23    // ```
24    //  (d)----(c) .. ( )
25    //   |  \   |      .
26    //   |   \  |      .
27    //  (b)----(a) .. ( )
28    //   .      .      .
29    //   .      .      .
30    //  ( ) .. ( ) .. ( )
31    // ```
32    pub fn new_weave(verts: Grid2<Option<(Vec3, Vec2)>>) -> Mesh {
33        let mut mesh = Mesh::default();
34
35        // add all verts, and dummy zero vectors at zero spots
36        for (i, res) in verts.items.iter().enumerate() {
37            let (x, y) = verts.to_xy(i);
38            match res {
39                Some((pos, uv)) => {
40                    mesh.verts.push(pos.clone());
41                    mesh.uvs.push(uv.clone());
42                }
43                None => {
44                    mesh.verts.push(vec3(0., 0., 0.));
45                    mesh.uvs.push(vec2(0., 0.));
46                }
47            }
48        }
49
50        // add triangles by lacing patches
51        for (i, res) in verts.items.iter().enumerate() {
52            let (x, y) = verts.to_xy(i);
53
54            if x == 0 || y == 0 {
55                continue;
56            }
57
58            let ia = i;
59            let ib = verts.to_index(x - 1, y).unwrap();
60            let ic = verts.to_index(x, y - 1).unwrap();
61            let id = verts.to_index(x - 1, y - 1).unwrap();
62
63            let a = verts.get_unsafe(ia);
64            let b = verts.get_unsafe(ib);
65            let c = verts.get_unsafe(ic);
66            let d = verts.get_unsafe(id);
67
68            // do some bitmask checking to get corner triangles
69            // there is no elegant way to do this,
70            // since we have to get the triangle counter-clockwise direction right
71            if a.is_some() && b.is_some() && c.is_some() && d.is_some() {
72                mesh.tri.append(&mut vec![ia, id, ib]);
73                mesh.tri.append(&mut vec![ia, ic, id]);
74            } else if a.is_some() && b.is_some() && c.is_some() {
75                mesh.tri.append(&mut vec![ia, ic, ib]);
76            } else if a.is_some() && b.is_some() && d.is_some() {
77                mesh.tri.append(&mut vec![ia, id, ib]);
78            } else if a.is_some() && c.is_some() && d.is_some() {
79                mesh.tri.append(&mut vec![ia, ic, id]);
80            } else if b.is_some() && c.is_some() && d.is_some() {
81                mesh.tri.append(&mut vec![ib, ic, id]);
82            }
83        }
84
85        // TODO clean away all unused vertices
86        // return mesh.to_clean();
87        mesh
88    }
89
90    //
91    pub fn new_diamonds(points: Vec<Vec3>, size: fxx) -> Mesh {
92        let mut meshes = Vec::new();
93        for point in points {
94            meshes.push(Mesh::new_diamond(point, size))
95        }
96        Mesh::from_join(meshes)
97    }
98
99    pub fn new_diamond(center: Vec3, size: fxx) -> Mesh {
100        let mut mesh = Mesh::default();
101
102        mesh.verts
103            .push(Vec3::new(center.x + size, center.y, center.z));
104        mesh.verts
105            .push(Vec3::new(center.x - size, center.y, center.z));
106        mesh.verts
107            .push(Vec3::new(center.x, center.y + size, center.z));
108        mesh.verts
109            .push(Vec3::new(center.x, center.y - size, center.z));
110        mesh.verts
111            .push(Vec3::new(center.x, center.y, center.z + size));
112        mesh.verts
113            .push(Vec3::new(center.x, center.y, center.z - size));
114
115        mesh.tri.append(&mut vec![
116            4, 0, 2, 4, 2, 1, 4, 1, 3, 4, 3, 0, 5, 2, 0, 5, 1, 2, 5, 3, 1, 5, 0, 3,
117        ]);
118
119        mesh
120    }
121
122    // simple join, not taking common verts into account
123    pub fn from_join(meshes: Vec<Mesh>) -> Mesh {
124        let mut mesh = Mesh::default();
125
126        let mut vertcount = 0;
127        for mut other in meshes {
128            let length = other.verts.len();
129            mesh.verts.append(&mut other.verts);
130            mesh.tri
131                .append(&mut other.tri.iter().map(|t| t + vertcount).collect());
132            vertcount += length;
133        }
134
135        mesh
136    }
137
138    pub fn get_triangles(&self) -> Vec<(usize, usize, usize)> {
139        let mut data = Vec::new();
140        assert!(self.tri.len() % 3 == 0);
141        for i in (0..self.tri.len()).step_by(3) {
142            data.push((self.tri[i], self.tri[i + 1], self.tri[i + 2]))
143        }
144        data
145    }
146
147    pub fn get_edges(&self) -> Vec<(usize, usize)> {
148        let tri = self.get_triangles();
149        let mut edges = Vec::new();
150        for (a, b, c) in tri {
151            edges.push((a, b));
152            edges.push((b, c));
153            edges.push((c, a));
154        }
155        edges
156    }
157
158    pub fn to_clean(&self) -> Mesh {
159        // TODO: identify all vertices which are not references by any triangle,
160        // exclude them
161        // then update the triangle vertex pointers.
162        todo!();
163    }
164
165    pub fn write_obj(&self, path: &str) -> Result<(), std::io::Error> {
166        let obj = self.gen_obj_buffer("obj generated by Hedron", None, None)?;
167        let mut obj_file = std::fs::File::create(&path)?;
168        obj_file.write_all(&obj)?;
169        Ok(())
170    }
171
172    pub fn write_obj_mtl(
173        &self,
174        path: &str,
175        name_obj: &str,
176        name_mtl: &str,
177        name_texture: &str,
178    ) -> Result<(), std::io::Error> {
179        let mat_name = "Material";
180
181        // both the texture and mtl should be in the same folder
182        let mtl = Mesh::gen_mtl_buffer("mtl generated by Hedron", mat_name, Some(name_texture))?;
183        let obj = self.gen_obj_buffer("obj generated by Hedron", Some(mat_name), Some(name_mtl))?;
184
185        let texture_path = path.to_owned() + name_texture;
186        let mtl_path = path.to_owned() + name_mtl;
187        let obj_path = path.to_owned() + name_obj;
188
189        let mut obj_file = std::fs::File::create(obj_path)?;
190        obj_file.write_all(&obj)?;
191        let mut mtl_path = std::fs::File::create(mtl_path)?;
192        mtl_path.write_all(&mtl)?;
193
194        Ok(())
195    }
196
197    pub fn gen_mtl_buffer(
198        header: &str,
199        mat_name: &str,
200        texture_path: Option<&str>,
201    ) -> Result<Vec<u8>, std::io::Error> {
202        let mut mtl = Vec::new();
203        writeln!(&mut mtl, "# {}", header)?;
204        writeln!(&mut mtl, "newmtl {}", mat_name)?;
205        writeln!(&mut mtl, "Ns 250.000000")?;
206        writeln!(&mut mtl, "Ka 1.000000 1.000000 1.000000")?;
207        writeln!(&mut mtl, "Kd 0.000000 0.000000 0.000000")?;
208        writeln!(&mut mtl, "Ks 0.000000 0.000000 0.000000")?;
209        writeln!(&mut mtl, "Ke 0.000000 0.000000 0.000000")?;
210        writeln!(&mut mtl, "Ni 1.450000")?;
211        writeln!(&mut mtl, "d 1.000000")?;
212        writeln!(&mut mtl, "illum 2")?;
213        if let Some(path) = texture_path {
214            writeln!(&mut mtl, "{}", format!("map_Ka {}", texture_path.unwrap()))?;
215            writeln!(&mut mtl, "{}", format!("map_Kd {}", texture_path.unwrap()))?;
216            writeln!(&mut mtl, "{}", format!("map_Ks {}", texture_path.unwrap()))?;
217        }
218        Ok(mtl)
219    }
220
221    pub fn gen_obj_buffer(
222        &self,
223        header: &str,
224        mat_name: Option<&str>,
225        mtl_path: Option<&str>,
226    ) -> Result<Vec<u8>, std::io::Error> {
227        let mut obj = Vec::new();
228        let o = &mut obj;
229
230        writeln!(o, "# {}", header)?;
231
232        if mtl_path.is_some() && mat_name.is_some() {
233            writeln!(o, "mtllib {}", mtl_path.unwrap())?;
234            writeln!(o, "usemtl {}", mat_name.unwrap())?;
235        }
236        for vert in self.verts.iter() {
237            writeln!(o, "v {} {} {}", vert.x, vert.y, vert.z)?;
238        }
239        for uv in self.uvs.iter() {
240            writeln!(o, "vt {} {}", uv.x, uv.y)?;
241        }
242
243        if self.uvs.len() == self.verts.len() {
244            for (a, b, c) in self.get_triangles() {
245                let (a, b, c) = (a + 1, b + 1, c + 1);
246                writeln!(o, "f {a}/{a} {b}/{b} {c}/{c}")?;
247            }
248        } else {
249            for (a, b, c) in self.get_triangles() {
250                let (a, b, c) = (a + 1, b + 1, c + 1);
251                writeln!(o, "f {a} {b} {c}")?;
252            }
253        }
254        Ok(obj)
255    }
256}
257
258///////////////////////////////////////////////////////////////////////////////
259
260impl PointBased for Mesh {
261    // TODO how to IntoIterator, so we don't have to iter / collect
262    fn mutate_points<'a>(&'a mut self) -> Vec<&'a mut Vec3> {
263        self.verts.iter_mut().collect() // its a bit sad we have to do this.
264    }
265}
266
267#[cfg(test)]
268mod test {
269    use super::Mesh;
270    use crate::core::Geometry;
271    use crate::kernel::vec3;
272    use std::io::Write;
273
274    // #[test]
275    // fn write_some_obj() {
276    //     let mesh = Mesh::new_diamond(vec3(0.5, 0.5, 0.5), 1.333);
277
278    //     mesh.write_obj_mtl("../data-results/", "some.obj", "some.mtl", "some.png")
279    //         .expect("something went wrong!");
280    // }
281
282    #[test]
283    fn write_file() {
284        let mut buffer = Vec::new();
285        writeln!(&mut buffer, "test").unwrap();
286        writeln!(&mut buffer, "formatted {}", "arguments").unwrap();
287
288        let mut file = std::fs::File::create("data.txt").expect("create failed");
289        file.write_all(&buffer).expect("write failed");
290    }
291
292    #[test]
293    fn transform_mesh() {
294        let mut mesh = Mesh::new_diamond(vec3(0.5, 0.5, 0.5), 0.5);
295        mesh = mesh.mv(&-vec3(0.5, 0.5, 0.5));
296        mesh = mesh.scale_u(2.0);
297        assert_eq!(
298            mesh.verts,
299            vec![
300                vec3(1.0, 0.0, 0.0),
301                vec3(-1.0, 0.0, 0.0),
302                vec3(0.0, 1.0, 0.0),
303                vec3(0.0, -1.0, 0.0),
304                vec3(0.0, 0.0, 1.0),
305                vec3(0.0, 0.0, -1.0)
306            ]
307        );
308    }
309}