Skip to main content

rapier3d_meshloader/
lib.rs

1#![doc = include_str!("../README.md")]
2#![deny(missing_docs)]
3
4pub use mesh_loader;
5use mesh_loader::{Material, Mesh};
6use rapier3d::geometry::{MeshConverter, SharedShape};
7use rapier3d::math::{Pose, Vector};
8use rapier3d::prelude::MeshConverterError;
9use std::path::Path;
10
11/// The result of loading a shape.
12pub struct LoadedShape {
13    /// The shape loaded from the file and converted by the [`MeshConverter`].
14    pub shape: SharedShape,
15    /// The shape's pose.
16    pub pose: Pose,
17    /// The raw mesh read from the file without any modification.
18    pub raw_mesh: Mesh,
19    /// The material declared in the source asset for this mesh. For an
20    /// OBJ this is the `.mtl` material referenced by the mesh's
21    /// `usemtl` group (loaded by `mesh-loader` when the `.mtl` sits
22    /// alongside the `.obj`). STL files don't carry materials, so this
23    /// is the default-constructed `Material`.
24    pub material: Material,
25}
26
27/// Error while loading an STL file.
28#[derive(thiserror::Error, Debug)]
29pub enum MeshLoaderError {
30    /// An error triggered by rapier’s [`MeshConverter`].
31    #[error(transparent)]
32    MeshConverter(#[from] MeshConverterError),
33    /// A generic IO error.
34    #[error(transparent)]
35    Io(#[from] std::io::Error),
36}
37
38/// Loads parry shapes from a file.
39///
40/// # Parameters
41/// - `path`: the file’s path.
42/// - `converter`: controls how the shapes are computed from the content. In particular, it lets
43///   you specify if the computed [`SharedShape`] is a triangle mesh, its convex hull,
44///   bounding box, etc.
45/// - `scale`: the scaling factor applied to the geometry input to the `converter`. This scale will
46///   affect at the geometric level the [`LoadedShape::shape`]. Note that raw mesh value stored
47///   in [`LoadedShape::raw_mesh`] remains unscaled.
48pub fn load_from_path(
49    path: impl AsRef<Path>,
50    converter: &MeshConverter,
51    scale: Vector,
52) -> Result<Vec<Result<LoadedShape, MeshConverterError>>, MeshLoaderError> {
53    let loader = mesh_loader::Loader::default();
54    let mut colliders = vec![];
55    let scene = loader.load(path)?;
56    // mesh-loader's OBJ backend aligns `scene.materials[i]` with
57    // `scene.meshes[i]` (it expands per-mesh from the material_index),
58    // so zipping is correct here. STL produces empty materials; pair
59    // each mesh with a default material in that case.
60    let mut materials = scene.materials.into_iter();
61    for raw_mesh in scene.meshes.into_iter() {
62        let material = materials.next().unwrap_or_default();
63        let shape = load_from_raw_mesh(&raw_mesh, converter, scale);
64
65        colliders.push(shape.map(|(shape, pose)| LoadedShape {
66            shape,
67            pose,
68            raw_mesh,
69            material,
70        }));
71    }
72    Ok(colliders)
73}
74
75/// Loads an file as a shape from a preloaded raw [`mesh_loader::Mesh`].
76///
77/// # Parameters
78/// - `raw_mesh`: the raw mesh.
79/// - `converter`: controls how the shape is computed from the STL content. In particular, it lets
80///   you specify if the computed [`SharedShape`] is a triangle mesh, its convex hull,
81///   bounding box, etc.
82/// - `scale`: the scaling factor applied to the geometry input to the `converter`. This scale will
83///   affect at the geometric level the [`LoadedShape::shape`]. Note that raw mesh value stored
84///   in [`LoadedShape::raw_mesh`] remains unscaled.
85pub fn load_from_raw_mesh(
86    raw_mesh: &Mesh,
87    converter: &MeshConverter,
88    scale: Vector,
89) -> Result<(SharedShape, Pose), MeshConverterError> {
90    let vertices: Vec<_> = raw_mesh
91        .vertices
92        .iter()
93        .map(|xyz| Vector::new(xyz[0], xyz[1], xyz[2]) * scale)
94        .collect();
95    let indices: Vec<_> = raw_mesh.faces.clone();
96    converter.convert(vertices, indices)
97}