re_renderer/importer/
stl.rs

1use itertools::Itertools as _;
2use smallvec::smallvec;
3
4use crate::{CpuModel, DebugLabel, RenderContext, mesh};
5
6#[derive(thiserror::Error, Debug)]
7pub enum StlImportError {
8    #[error("Error loading STL mesh: {0}")]
9    StlIoError(std::io::Error),
10
11    #[error(transparent)]
12    MeshError(#[from] mesh::MeshError),
13}
14
15/// Load a [STL .stl file](https://en.wikipedia.org/wiki/STL_(file_format)) into the mesh manager.
16pub fn load_stl_from_buffer(
17    buffer: &[u8],
18    ctx: &RenderContext,
19) -> Result<CpuModel, StlImportError> {
20    re_tracing::profile_function!();
21
22    let mut cursor = std::io::Cursor::new(buffer);
23    let reader = stl_io::create_stl_reader(&mut cursor).map_err(StlImportError::StlIoError)?;
24
25    // TODO(hmeyer/stl_io#26): use optional name from ascii stl files.
26    // https://github.com/hmeyer/stl_io/pull/26
27    let name = DebugLabel::from("");
28
29    let (normals, triangles): (Vec<_>, Vec<_>) = reader
30        .into_iter()
31        .map(|triangle_res| {
32            triangle_res.map(|triangle| {
33                (
34                    [triangle.normal.0, triangle.normal.0, triangle.normal.0],
35                    [
36                        triangle.vertices[0].0,
37                        triangle.vertices[1].0,
38                        triangle.vertices[2].0,
39                    ],
40                )
41            })
42        })
43        .collect::<Result<Vec<_>, _>>()
44        .map_err(StlImportError::StlIoError)?
45        .into_iter()
46        .unzip();
47
48    let num_vertices = triangles.len() * 3;
49
50    let material = mesh::Material {
51        label: name.clone(),
52        index_range: 0..num_vertices as u32,
53        albedo: ctx.texture_manager_2d.white_texture_unorm_handle().clone(),
54        albedo_factor: crate::Rgba::WHITE,
55    };
56
57    let vertex_positions = bytemuck::cast_vec(triangles);
58    let bbox = macaw::BoundingBox::from_points(vertex_positions.iter().copied());
59
60    let mesh = mesh::CpuMesh {
61        label: name.clone(),
62        triangle_indices: (0..num_vertices as u32)
63            .tuples::<(_, _, _)>()
64            .map(glam::UVec3::from)
65            .collect::<Vec<_>>(),
66
67        vertex_positions,
68
69        // Normals on STL are per triangle, not per vertex.
70        // Yes, this makes STL always look faceted.
71        vertex_normals: bytemuck::cast_vec(normals),
72
73        // STL has neither colors nor texcoords.
74        vertex_colors: vec![crate::Rgba32Unmul::WHITE; num_vertices],
75        vertex_texcoords: vec![glam::Vec2::ZERO; num_vertices],
76
77        materials: smallvec![material],
78
79        bbox,
80    };
81
82    mesh.sanity_check()?;
83
84    Ok(CpuModel::from_single_mesh(mesh))
85}