Skip to main content

gl_utils/mesh/
mod.rs

1//! Mesh generation utilities.
2
3use std::path::Path;
4use bytemuck;
5use derive_more::{From, TryInto};
6use gltf;
7use num_traits::NumCast;
8use strum;
9use crate::{math, math::geometry, util, vertex};
10
11pub mod capsule;
12pub mod cube;
13pub mod cylinder;
14pub mod grid;
15pub mod hemisphere;
16pub mod sphere;
17
18#[derive(Clone, Debug, PartialEq, From, TryInto)]
19pub enum Mesh {
20  Points3d (Points3d),
21  Lines3d  (Lines3d),
22  Triangles3d (Triangles3d)
23}
24
25/// Raw data defining a point-cloud mesh
26#[derive(Clone, Debug, PartialEq, From)]
27pub struct Points3d {
28  pub vertices : Vec <vertex::Vert3dInstanced>,
29  pub indices  : Vec <u32>
30}
31
32/// Raw data defining a wireframe mesh
33#[derive(Clone, Debug, PartialEq, From)]
34pub struct Lines3d {
35  pub vertices : Vec <vertex::Vert3dInstanced>,
36  pub indices  : Vec <u32>
37}
38
39/// Raw data defining a triangle mesh
40#[derive(Clone, Debug, PartialEq, From)]
41pub struct Triangles3d {
42  pub vertices : Vec <vertex::Vert3dInstanced>,
43  pub indices  : Vec <u32>
44}
45
46/// A builder type to allow building a Lines3d mesh by incrementally by adding points
47pub struct Points3dBuilder {
48  points : Points3d
49}
50
51/// A builder type to allow building a Lines3d mesh by incrementally by adding edges
52pub struct Lines3dBuilder {
53  lines : Lines3d
54}
55
56/// A builder type to allow building a Triangles3d mesh by incrementally by adding faces
57pub struct Triangles3dBuilder {
58  triangles : Triangles3d
59}
60
61impl Mesh {
62  /// Load a GLTF file containing a single mesh
63  pub fn load_gltf <P : AsRef <Path>> (path : P) -> Self {
64    let (document, buffers, _images) = gltf::import (path).unwrap();
65    let mut meshes = document.meshes();
66    let mesh = meshes.next().unwrap();
67    debug_assert!(meshes.peekable().next().is_none());
68    let mut primitives = mesh.primitives();
69    let primitive = primitives.next().unwrap();
70    debug_assert!(primitives.peekable().next().is_none());
71    let reader = primitive.reader (|buffer| Some (&buffers[buffer.index()]));
72    let vertices = reader.read_positions().unwrap()
73      .map (|inst_position| vertex::Vert3dInstanced { inst_position }).collect();
74    let indices  = reader.read_indices().unwrap().into_u32().collect();
75    match primitive.mode() {
76      gltf::mesh::Mode::Points => Points3d { vertices, indices }.into(),
77      gltf::mesh::Mode::Lines => Lines3d { vertices, indices }.into(),
78      _ => unimplemented!()
79    }
80  }
81
82  /// Write mesh to an embedded `.gltf` file
83  pub fn write_gltf <P : AsRef <Path>> (&self, path : P) {
84    fn bounding_coords (points: &[vertex::Vert3dInstanced])
85      -> ([f32; 3], [f32; 3])
86    {
87      let mut min = [f32::MAX, f32::MAX, f32::MAX];
88      let mut max = [f32::MIN, f32::MIN, f32::MIN];
89      for point in points {
90        let p = point.inst_position;
91        for i in 0..3 {
92          min[i] = f32::min (min[i], p[i]);
93          max[i] = f32::max (max[i], p[i]);
94        }
95      }
96      (min, max)
97    }
98    let (vertices, indices, mode) = match self {
99      Mesh::Points3d (Points3d { vertices, indices }) =>
100        (vertices, indices, gltf::mesh::Mode::Points),
101      Mesh::Lines3d (Lines3d { vertices, indices }) =>
102        (vertices, indices, gltf::mesh::Mode::Lines),
103      Mesh::Triangles3d (Triangles3d { vertices, indices }) =>
104        (vertices, indices, gltf::mesh::Mode::Triangles)
105    };
106    let (min, max)        = bounding_coords (&vertices[..]);
107    let vertices_bytes    = bytemuck::cast_slice (vertices);
108    let indices_bytes     = bytemuck::cast_slice (indices);
109    let (buffer, views)   =
110      util::gltf::json_buffer_views (0, &[vertices_bytes, indices_bytes]);
111    let position_accessor = util::gltf::json_accessor (0, vertices.len() as u32,
112      gltf::json::accessor::Type::Vec3,
113      gltf::json::accessor::ComponentType::F32, Some ([min, max]));
114    let index_accessor    = util::gltf::json_accessor (1, indices.len() as u32,
115      gltf::json::accessor::Type::Scalar,
116      gltf::json::accessor::ComponentType::U32, None);
117    let lines_primitive   = util::gltf::json_primitive (
118      gltf::json::mesh::Semantic::Positions, mode, 1);
119    let mesh              = util::gltf::json_mesh (vec![lines_primitive]);
120    let node              = util::gltf::json_node (0);
121    let root              = util::gltf::json_root (
122      vec![node], vec![mesh], vec![buffer], views,
123      vec![position_accessor, index_accessor]);
124    let writer            = std::fs::File::create (path).unwrap();
125    gltf::json::serialize::to_writer_pretty (writer, &root).unwrap();
126  }
127}
128
129impl Points3d {
130  pub const fn empty() -> Self {
131    Points3d { vertices: vec![], indices: vec![] }
132  }
133}
134
135impl Lines3d {
136  pub const fn empty() -> Self {
137    Lines3d { vertices: vec![], indices: vec![] }
138  }
139}
140
141impl From <geometry::Aabb3 <f32>> for Lines3d {
142  fn from (aabb : geometry::Aabb3 <f32>) -> Self {
143    use strum::IntoEnumIterator;
144    let vertices = math::Octant::iter().map (|octant| vertex::Vert3dInstanced {
145      inst_position: aabb.corner (octant).0.into_array()
146    }).collect();
147    let indices  = vec![
148      0, 1, 0, 2, 0, 4,
149      7, 6, 7, 5, 7, 3,
150      1, 3, 1, 5,
151      2, 3, 2, 6,
152      4, 5, 4, 6
153    ];
154    Lines3d { vertices, indices }
155  }
156}
157
158impl Triangles3d {
159  pub const fn empty() -> Self {
160    Triangles3d { vertices: vec![], indices: vec![] }
161  }
162}
163
164impl Points3dBuilder {
165  pub const fn empty() -> Self {
166    Points3dBuilder {
167      points: Points3d::empty()
168    }
169  }
170
171  pub fn push_point <T : NumCast> (&mut self, point : math::Point3 <T>) -> &mut Self {
172    let vertex = |p : math::Point3 <T>|
173      vertex::Vert3dInstanced { inst_position: p.0.numcast().unwrap().into_array() };
174    self.points.indices.push (self.points.vertices.len() as u32);
175    self.points.vertices.push (vertex (point));
176    self
177  }
178
179  pub fn build (self) -> Points3d {
180    self.points
181  }
182}
183
184impl Lines3dBuilder {
185  pub const fn empty() -> Self {
186    Lines3dBuilder {
187      lines: Lines3d::empty()
188    }
189  }
190
191  pub fn push_edge <T : NumCast> (&mut self, edge : [math::Point3 <T>; 2]) -> &mut Self {
192    // TODO: we are de-duplicating vertices, but checking each vertex is a linear
193    // search; improve the efficiency, can we de-duplicate at the end with a single
194    // pass?
195    let [a, b] : [math::Point3 <f32>; 2] = edge.map (|p| p.numcast().unwrap());
196    let vertex = |p : math::Point3 <f32>|
197      vertex::Vert3dInstanced { inst_position: p.0.into_array() };
198    let mut add_vertex = |v|
199      if let Some(i) = self.lines.vertices.iter().position (|x| *x == v) {
200        self.lines.indices.push (i as u32)
201      } else {
202        self.lines.indices.push (self.lines.vertices.len() as u32);
203        self.lines.vertices.push (v);
204      };
205    add_vertex (vertex (a));
206    add_vertex (vertex (b));
207    self
208  }
209
210  pub fn build (self) -> Lines3d {
211    self.lines
212  }
213}
214
215impl Triangles3dBuilder {
216  pub const fn empty() -> Self {
217    Triangles3dBuilder {
218      triangles: Triangles3d::empty()
219    }
220  }
221
222  pub fn push_face <T : NumCast> (&mut self, face : [math::Point3 <T>; 3]) -> &mut Self {
223    // TODO: we are de-duplicating vertices, but checking each vertex is a linear
224    // search; improve the efficiency, can we de-duplicate at the end with a single
225    // pass?
226    let [a, b, c] : [math::Point3 <f32>; 3] = face.map (|p| p.numcast().unwrap());
227    let vertex = |p : math::Point3 <f32>|
228      vertex::Vert3dInstanced { inst_position: p.0.into_array() };
229    let mut add_vertex = |v|
230      if let Some(i) = self.triangles.vertices.iter().position (|x| *x == v) {
231        self.triangles.indices.push (i as u32)
232      } else {
233        self.triangles.indices.push (self.triangles.vertices.len() as u32);
234        self.triangles.vertices.push (v);
235      };
236    add_vertex (vertex (a));
237    add_vertex (vertex (b));
238    add_vertex (vertex (c));
239    self
240  }
241
242  pub fn build (self) -> Triangles3d {
243    self.triangles
244  }
245}