openrr_planner/collision/
mesh.rs

1use std::{io, path::Path};
2
3use k::{nalgebra as na, RealField};
4use ncollide3d::shape::TriMesh;
5
6use crate::errors::*;
7
8#[cfg(feature = "assimp")]
9pub(crate) fn load_mesh<P, T>(filename: P, scale: &[f64; 3]) -> Result<TriMesh<T>>
10where
11    P: AsRef<Path>,
12    T: RealField + Copy,
13{
14    let filename = filename.as_ref();
15    let mut importer = assimp::Importer::new();
16    importer.pre_transform_vertices(|x| x.enable = true);
17    importer.collada_ignore_up_direction(true);
18    importer.triangulate(true);
19    if let Some(file_string) = filename.to_str() {
20        match importer.read_file(file_string) {
21            Ok(assimp_scene) => Ok(assimp_scene_to_ncollide_mesh(assimp_scene, scale)),
22            Err(err) => Err(Error::MeshError(err.to_owned())),
23        }
24    } else {
25        // assimp crate only supports UTF-8 path
26        load_mesh_with_mesh_loader(filename, scale)
27    }
28}
29
30#[cfg(not(feature = "assimp"))]
31pub(crate) use load_mesh_with_mesh_loader as load_mesh;
32pub(crate) fn load_mesh_with_mesh_loader<P, T>(filename: P, scale: &[f64; 3]) -> Result<TriMesh<T>>
33where
34    P: AsRef<Path>,
35    T: RealField + Copy,
36{
37    let filename = filename.as_ref();
38    let mesh = mesh_loader::Loader::default()
39        .merge_meshes(true)
40        .load(filename)
41        .map_err(|e| {
42            if e.kind() == io::ErrorKind::Unsupported {
43                if cfg!(feature = "assimp") {
44                    Error::MeshError(format!(
45                        "assimp feature is enabled but filename is not UTF-8: could not parse {filename:?}"
46                    ))
47                } else {
48                    Error::MeshError(format!(
49                        "assimp feature is disabled: could not parse {filename:?}"
50                    ))
51                }
52            } else {
53                e.into()
54            }
55        })?
56        .meshes
57        .pop()
58        .unwrap(); // merge_meshes(true) merges all meshes into one
59
60    let vertices = mesh
61        .vertices
62        .iter()
63        .map(|v| {
64            na::Point3::<T>::new(
65                na::convert(v[0] as f64 * scale[0]),
66                na::convert(v[1] as f64 * scale[1]),
67                na::convert(v[2] as f64 * scale[2]),
68            )
69        })
70        .collect();
71    let indices = mesh
72        .faces
73        .iter()
74        .map(|face| na::Point3::new(face[0] as usize, face[1] as usize, face[2] as usize))
75        .collect();
76
77    Ok(TriMesh::new(vertices, indices, None))
78}
79
80#[cfg(feature = "assimp")]
81fn assimp_scene_to_ncollide_mesh<T>(scene: assimp::Scene<'_>, scale: &[f64; 3]) -> TriMesh<T>
82where
83    T: RealField + Copy,
84{
85    let mut vertices = Vec::new();
86    let mut indices = Vec::new();
87    let mut last_index: usize = 0;
88    for mesh in scene.mesh_iter() {
89        vertices.extend(mesh.vertex_iter().map(|v| {
90            na::Point3::<T>::new(
91                na::convert(v.x as f64 * scale[0]),
92                na::convert(v.y as f64 * scale[1]),
93                na::convert(v.z as f64 * scale[2]),
94            )
95        }));
96        indices.extend(mesh.face_iter().filter_map(|f| {
97            if f.num_indices == 3 {
98                Some(na::Point3::new(
99                    f[0] as usize + last_index,
100                    f[1] as usize + last_index,
101                    f[2] as usize + last_index,
102                ))
103            } else {
104                None
105            }
106        }));
107        last_index = vertices.len();
108    }
109    TriMesh::new(vertices, indices, None)
110}