use std::path::Path;
use bytemuck;
use derive_more::{From, TryInto};
use gltf;
use num_traits::NumCast;
use strum;
use crate::{math, math::geometry, util, vertex};
pub mod capsule;
pub mod cube;
pub mod cylinder;
pub mod grid;
pub mod hemisphere;
pub mod sphere;
#[derive(Clone, Debug, PartialEq, From, TryInto)]
pub enum Mesh {
Points3d (Points3d),
Lines3d (Lines3d),
Triangles3d (Triangles3d)
}
#[derive(Clone, Debug, PartialEq, From)]
pub struct Points3d {
pub vertices : Vec <vertex::Vert3dInstanced>,
pub indices : Vec <u32>
}
#[derive(Clone, Debug, PartialEq, From)]
pub struct Lines3d {
pub vertices : Vec <vertex::Vert3dInstanced>,
pub indices : Vec <u32>
}
#[derive(Clone, Debug, PartialEq, From)]
pub struct Triangles3d {
pub vertices : Vec <vertex::Vert3dInstanced>,
pub indices : Vec <u32>
}
pub struct Points3dBuilder {
points : Points3d
}
pub struct Lines3dBuilder {
lines : Lines3d
}
pub struct Triangles3dBuilder {
triangles : Triangles3d
}
impl Mesh {
pub fn load_gltf <P : AsRef <Path>> (path : P) -> Self {
let (document, buffers, _images) = gltf::import (path).unwrap();
let mut meshes = document.meshes();
let mesh = meshes.next().unwrap();
debug_assert!(meshes.peekable().next().is_none());
let mut primitives = mesh.primitives();
let primitive = primitives.next().unwrap();
debug_assert!(primitives.peekable().next().is_none());
let reader = primitive.reader (|buffer| Some (&buffers[buffer.index()]));
let vertices = reader.read_positions().unwrap()
.map (|inst_position| vertex::Vert3dInstanced { inst_position }).collect();
let indices = reader.read_indices().unwrap().into_u32().collect();
match primitive.mode() {
gltf::mesh::Mode::Points => Points3d { vertices, indices }.into(),
gltf::mesh::Mode::Lines => Lines3d { vertices, indices }.into(),
_ => unimplemented!()
}
}
pub fn write_gltf <P : AsRef <Path>> (&self, path : P) {
fn bounding_coords (points: &[vertex::Vert3dInstanced])
-> ([f32; 3], [f32; 3])
{
let mut min = [f32::MAX, f32::MAX, f32::MAX];
let mut max = [f32::MIN, f32::MIN, f32::MIN];
for point in points {
let p = point.inst_position;
for i in 0..3 {
min[i] = f32::min (min[i], p[i]);
max[i] = f32::max (max[i], p[i]);
}
}
(min, max)
}
let (vertices, indices, mode) = match self {
Mesh::Points3d (Points3d { vertices, indices }) =>
(vertices, indices, gltf::mesh::Mode::Points),
Mesh::Lines3d (Lines3d { vertices, indices }) =>
(vertices, indices, gltf::mesh::Mode::Lines),
Mesh::Triangles3d (Triangles3d { vertices, indices }) =>
(vertices, indices, gltf::mesh::Mode::Triangles)
};
let (min, max) = bounding_coords (&vertices[..]);
let vertices_bytes = bytemuck::cast_slice (vertices);
let indices_bytes = bytemuck::cast_slice (indices);
let (buffer, views) =
util::gltf::json_buffer_views (0, &[vertices_bytes, indices_bytes]);
let position_accessor = util::gltf::json_accessor (0, vertices.len() as u32,
gltf::json::accessor::Type::Vec3,
gltf::json::accessor::ComponentType::F32, Some ([min, max]));
let index_accessor = util::gltf::json_accessor (1, indices.len() as u32,
gltf::json::accessor::Type::Scalar,
gltf::json::accessor::ComponentType::U32, None);
let lines_primitive = util::gltf::json_primitive (
gltf::json::mesh::Semantic::Positions, mode, 1);
let mesh = util::gltf::json_mesh (vec![lines_primitive]);
let node = util::gltf::json_node (0);
let root = util::gltf::json_root (
vec![node], vec![mesh], vec![buffer], views,
vec![position_accessor, index_accessor]);
let writer = std::fs::File::create (path).unwrap();
gltf::json::serialize::to_writer_pretty (writer, &root).unwrap();
}
}
impl Points3d {
pub const fn empty() -> Self {
Points3d { vertices: vec![], indices: vec![] }
}
}
impl Lines3d {
pub const fn empty() -> Self {
Lines3d { vertices: vec![], indices: vec![] }
}
}
impl From <geometry::Aabb3 <f32>> for Lines3d {
fn from (aabb : geometry::Aabb3 <f32>) -> Self {
use strum::IntoEnumIterator;
let vertices = math::Octant::iter().map (|octant| vertex::Vert3dInstanced {
inst_position: aabb.corner (octant).0.into_array()
}).collect();
let indices = vec![
0, 1, 0, 2, 0, 4,
7, 6, 7, 5, 7, 3,
1, 3, 1, 5,
2, 3, 2, 6,
4, 5, 4, 6
];
Lines3d { vertices, indices }
}
}
impl Triangles3d {
pub const fn empty() -> Self {
Triangles3d { vertices: vec![], indices: vec![] }
}
}
impl Points3dBuilder {
pub const fn empty() -> Self {
Points3dBuilder {
points: Points3d::empty()
}
}
pub fn push_point <T : NumCast> (&mut self, point : math::Point3 <T>) -> &mut Self {
let vertex = |p : math::Point3 <T>|
vertex::Vert3dInstanced { inst_position: p.0.numcast().unwrap().into_array() };
self.points.indices.push (self.points.vertices.len() as u32);
self.points.vertices.push (vertex (point));
self
}
pub fn build (self) -> Points3d {
self.points
}
}
impl Lines3dBuilder {
pub const fn empty() -> Self {
Lines3dBuilder {
lines: Lines3d::empty()
}
}
pub fn push_edge <T : NumCast> (&mut self, edge : [math::Point3 <T>; 2]) -> &mut Self {
let [a, b] : [math::Point3 <f32>; 2] = edge.map (|p| p.numcast().unwrap());
let vertex = |p : math::Point3 <f32>|
vertex::Vert3dInstanced { inst_position: p.0.into_array() };
let mut add_vertex = |v|
if let Some(i) = self.lines.vertices.iter().position (|x| *x == v) {
self.lines.indices.push (i as u32)
} else {
self.lines.indices.push (self.lines.vertices.len() as u32);
self.lines.vertices.push (v);
};
add_vertex (vertex (a));
add_vertex (vertex (b));
self
}
pub fn build (self) -> Lines3d {
self.lines
}
}
impl Triangles3dBuilder {
pub const fn empty() -> Self {
Triangles3dBuilder {
triangles: Triangles3d::empty()
}
}
pub fn push_face <T : NumCast> (&mut self, face : [math::Point3 <T>; 3]) -> &mut Self {
let [a, b, c] : [math::Point3 <f32>; 3] = face.map (|p| p.numcast().unwrap());
let vertex = |p : math::Point3 <f32>|
vertex::Vert3dInstanced { inst_position: p.0.into_array() };
let mut add_vertex = |v|
if let Some(i) = self.triangles.vertices.iter().position (|x| *x == v) {
self.triangles.indices.push (i as u32)
} else {
self.triangles.indices.push (self.triangles.vertices.len() as u32);
self.triangles.vertices.push (v);
};
add_vertex (vertex (a));
add_vertex (vertex (b));
add_vertex (vertex (c));
self
}
pub fn build (self) -> Triangles3d {
self.triangles
}
}