From e7d9f39f63f39f515b2287f155f0764cbd162072 Mon Sep 17 00:00:00 2001 From: Gijs de Jong Date: Sat, 17 May 2025 21:29:14 +0100 Subject: [PATCH 1/6] Replace `tinystl` with `stl_io` to support more stl files --- Cargo.lock | 26 +++++++----- Cargo.toml | 2 +- crates/viewer/re_renderer/Cargo.toml | 4 +- crates/viewer/re_renderer/src/importer/stl.rs | 41 +++++++++++-------- 4 files changed, 43 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f9eee72d1a2c..06b333cf8c00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2741,7 +2741,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.7.4", + "libloading 0.8.5", ] [[package]] @@ -3338,6 +3338,9 @@ name = "float-cmp" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] [[package]] name = "fnv" @@ -7390,8 +7393,8 @@ dependencies = [ "slotmap", "smallvec", "static_assertions", + "stl_io", "thiserror 1.0.65", - "tinystl", "tobj", "type-map", "unindent", @@ -9457,6 +9460,16 @@ dependencies = [ "rerun", ] +[[package]] +name = "stl_io" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff2e145168af9fef3b518ac0c6f9849c407b3df8a28582ced9f1fda510aa34c" +dependencies = [ + "byteorder", + "float-cmp", +] + [[package]] name = "strict-num" version = "0.1.1" @@ -9829,15 +9842,6 @@ dependencies = [ "log", ] -[[package]] -name = "tinystl" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdbcdda2f86a57b89b5d9ac17cd4c9f3917ec8edcde403badf3d992d2947af2a" -dependencies = [ - "bytemuck", -] - [[package]] name = "tinystr" version = "0.7.6" diff --git a/Cargo.toml b/Cargo.toml index 59683a26eaa3..2ea0282e4f03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -296,6 +296,7 @@ similar-asserts = "1.4.2" slotmap = { version = "1.0.6", features = ["serde"] } smallvec = { version = "1.0", features = ["const_generics", "union"] } static_assertions = "1.1" +stl_io = "0.8.5" strum = { version = "0.26", features = ["derive"] } strum_macros = "0.26" sublime_fuzzy = "0.7" @@ -310,7 +311,6 @@ time = { version = "0.3.36", default-features = false, features = [ "wasm-bindgen", ] } tiny_http = { version = "0.12", default-features = false } -tinystl = { version = "0.0.3", default-features = false } tobj = "4.0" tokio = { version = "1.44.2", default-features = false } tokio-stream = "0.1.16" diff --git a/crates/viewer/re_renderer/Cargo.toml b/crates/viewer/re_renderer/Cargo.toml index f219e0dad143..7a231230ce97 100644 --- a/crates/viewer/re_renderer/Cargo.toml +++ b/crates/viewer/re_renderer/Cargo.toml @@ -45,7 +45,7 @@ import-obj = ["dep:tobj"] import-gltf = ["dep:gltf"] ## Support importing binary & ascii .stl files -import-stl = ["dep:tinystl"] +import-stl = ["dep:stl_io"] ## Enable (de)serialization using serde. serde = ["dep:serde"] @@ -85,7 +85,7 @@ wgpu.workspace = true # optional gltf = { workspace = true, optional = true } -tinystl = { workspace = true, features = ["bytemuck"], optional = true } +stl_io = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"], optional = true } tobj = { workspace = true, optional = true } diff --git a/crates/viewer/re_renderer/src/importer/stl.rs b/crates/viewer/re_renderer/src/importer/stl.rs index 935e4459d60b..e8983081c814 100644 --- a/crates/viewer/re_renderer/src/importer/stl.rs +++ b/crates/viewer/re_renderer/src/importer/stl.rs @@ -1,13 +1,13 @@ use itertools::Itertools as _; use smallvec::smallvec; -use tinystl::StlData; +use stl_io::IndexedMesh; use crate::{CpuModel, RenderContext, mesh}; #[derive(thiserror::Error, Debug)] pub enum StlImportError { #[error("Error loading STL mesh: {0}")] - TinyStl(tinystl::Error), + StlIoError(std::io::Error), #[error(transparent)] MeshError(#[from] mesh::MeshError), @@ -20,37 +20,46 @@ pub fn load_stl_from_buffer( ) -> Result { re_tracing::profile_function!(); - let cursor = std::io::Cursor::new(buffer); - let StlData { - name, - triangles, - normals, - .. - } = StlData::read_buffer(std::io::BufReader::new(cursor)).map_err(StlImportError::TinyStl)?; + let mut cursor = std::io::Cursor::new(buffer); + let IndexedMesh { vertices, faces } = + stl_io::read_stl(&mut cursor).map_err(StlImportError::StlIoError)?; + let num_vertices = faces.len() * 3; - let num_vertices = triangles.len() * 3; + // TODO(gijsd): parse name from ASCII? + let name = ""; let material = mesh::Material { - label: name.clone().into(), + label: name.into(), index_range: 0..num_vertices as u32, albedo: ctx.texture_manager_2d.white_texture_unorm_handle().clone(), albedo_factor: crate::Rgba::WHITE, }; + // TODO(gijsd): Does this not support sparse meshes/vertex positions? let mesh = mesh::CpuMesh { label: name.into(), triangle_indices: (0..num_vertices as u32) .tuples::<(_, _, _)>() .map(glam::UVec3::from) .collect::>(), - vertex_positions: bytemuck::cast_slice(&triangles).to_vec(), + + vertex_positions: faces + .iter() + .flat_map(|f| { + [ + glam::Vec3::from_array(vertices[f.vertices[0]].0), + glam::Vec3::from_array(vertices[f.vertices[1]].0), + glam::Vec3::from_array(vertices[f.vertices[2]].0), + ] + }) + .collect(), // Normals on STL are per triangle, not per vertex. // Yes, this makes STL always look faceted. - vertex_normals: normals - .into_iter() - .flat_map(|n| { - let n = glam::Vec3::from_array(n); + vertex_normals: faces + .iter() + .flat_map(|f| { + let n = glam::Vec3::from_array(f.normal.0); [n, n, n] }) .collect(), From c3cb182b39aac95a386e388151fe21100be93c61 Mon Sep 17 00:00:00 2001 From: Gijs de Jong Date: Mon, 19 May 2025 12:20:53 +0200 Subject: [PATCH 2/6] Link to stl_io PR --- crates/viewer/re_renderer/src/importer/stl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/viewer/re_renderer/src/importer/stl.rs b/crates/viewer/re_renderer/src/importer/stl.rs index e8983081c814..5db88919e2ee 100644 --- a/crates/viewer/re_renderer/src/importer/stl.rs +++ b/crates/viewer/re_renderer/src/importer/stl.rs @@ -26,6 +26,7 @@ pub fn load_stl_from_buffer( let num_vertices = faces.len() * 3; // TODO(gijsd): parse name from ASCII? + // https://github.com/hmeyer/stl_io/pull/26 let name = ""; let material = mesh::Material { @@ -35,7 +36,6 @@ pub fn load_stl_from_buffer( albedo_factor: crate::Rgba::WHITE, }; - // TODO(gijsd): Does this not support sparse meshes/vertex positions? let mesh = mesh::CpuMesh { label: name.into(), triangle_indices: (0..num_vertices as u32) From 8ae5d3e281ddff90a70698755dbf4c83660e6d47 Mon Sep 17 00:00:00 2001 From: Gijs de Jong Date: Wed, 21 May 2025 13:16:33 +0200 Subject: [PATCH 3/6] Use bytemuck --- Cargo.lock | 5 +- Cargo.toml | 2 +- crates/viewer/re_renderer/src/importer/stl.rs | 47 ++++++++++--------- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 06b333cf8c00..a09628cd4445 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9462,9 +9462,8 @@ dependencies = [ [[package]] name = "stl_io" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff2e145168af9fef3b518ac0c6f9849c407b3df8a28582ced9f1fda510aa34c" +version = "0.9.0" +source = "git+https://github.com/oxkitsune/stl_io?branch=gijs%2Fmesh-name#b4325a398f16ed7b3ce9c35f21d49d7269687e6e" dependencies = [ "byteorder", "float-cmp", diff --git a/Cargo.toml b/Cargo.toml index 2ea0282e4f03..fed223f7a1ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -296,7 +296,7 @@ similar-asserts = "1.4.2" slotmap = { version = "1.0.6", features = ["serde"] } smallvec = { version = "1.0", features = ["const_generics", "union"] } static_assertions = "1.1" -stl_io = "0.8.5" +stl_io = { git = "https://github.com/oxkitsune/stl_io", branch = "gijs/mesh-name" } strum = { version = "0.26", features = ["derive"] } strum_macros = "0.26" sublime_fuzzy = "0.7" diff --git a/crates/viewer/re_renderer/src/importer/stl.rs b/crates/viewer/re_renderer/src/importer/stl.rs index 5db88919e2ee..1a538573cc5a 100644 --- a/crates/viewer/re_renderer/src/importer/stl.rs +++ b/crates/viewer/re_renderer/src/importer/stl.rs @@ -21,45 +21,50 @@ pub fn load_stl_from_buffer( re_tracing::profile_function!(); let mut cursor = std::io::Cursor::new(buffer); - let IndexedMesh { vertices, faces } = - stl_io::read_stl(&mut cursor).map_err(StlImportError::StlIoError)?; - let num_vertices = faces.len() * 3; - - // TODO(gijsd): parse name from ASCII? + let reader = stl_io::create_stl_reader(&mut cursor).map_err(StlImportError::StlIoError)?; + // TODO(hmeyer/stl_io#26): parse name from ASCII? // https://github.com/hmeyer/stl_io/pull/26 - let name = ""; + let name = reader.name().cloned().unwrap_or_default(); + + let (normals, vertices): (Vec<_>, Vec<_>) = reader + .into_iter() + .map(|triangle| triangle.unwrap()) + .map(|triangle| { + ( + triangle.normal, + [ + triangle.vertices[0].0, + triangle.vertices[1].0, + triangle.vertices[2].0, + ], + ) + }) + .unzip(); + + let num_vertices = vertices.len() * 3; let material = mesh::Material { - label: name.into(), + label: name.clone().into(), index_range: 0..num_vertices as u32, albedo: ctx.texture_manager_2d.white_texture_unorm_handle().clone(), albedo_factor: crate::Rgba::WHITE, }; let mesh = mesh::CpuMesh { - label: name.into(), + label: name.clone().into(), triangle_indices: (0..num_vertices as u32) .tuples::<(_, _, _)>() .map(glam::UVec3::from) .collect::>(), - vertex_positions: faces - .iter() - .flat_map(|f| { - [ - glam::Vec3::from_array(vertices[f.vertices[0]].0), - glam::Vec3::from_array(vertices[f.vertices[1]].0), - glam::Vec3::from_array(vertices[f.vertices[2]].0), - ] - }) - .collect(), + vertex_positions: bytemuck::cast_vec(vertices), // Normals on STL are per triangle, not per vertex. // Yes, this makes STL always look faceted. - vertex_normals: faces + vertex_normals: normals .iter() - .flat_map(|f| { - let n = glam::Vec3::from_array(f.normal.0); + .flat_map(|n| { + let n = glam::Vec3::from_array(n.0); [n, n, n] }) .collect(), From 4d987f4b512bae659d15db6533b9c906366e7b6f Mon Sep 17 00:00:00 2001 From: Gijs de Jong Date: Wed, 21 May 2025 13:21:26 +0200 Subject: [PATCH 4/6] Remove unused import --- crates/viewer/re_renderer/src/importer/stl.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/viewer/re_renderer/src/importer/stl.rs b/crates/viewer/re_renderer/src/importer/stl.rs index 1a538573cc5a..ea6cad0c4c31 100644 --- a/crates/viewer/re_renderer/src/importer/stl.rs +++ b/crates/viewer/re_renderer/src/importer/stl.rs @@ -1,6 +1,5 @@ use itertools::Itertools as _; use smallvec::smallvec; -use stl_io::IndexedMesh; use crate::{CpuModel, RenderContext, mesh}; From e7b6458a375426f99d6905eb1e1b63f0bad4eaca Mon Sep 17 00:00:00 2001 From: Gijs de Jong Date: Wed, 21 May 2025 13:24:51 +0200 Subject: [PATCH 5/6] these are triangles --- crates/viewer/re_renderer/src/importer/stl.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/viewer/re_renderer/src/importer/stl.rs b/crates/viewer/re_renderer/src/importer/stl.rs index ea6cad0c4c31..b490fbaaa26c 100644 --- a/crates/viewer/re_renderer/src/importer/stl.rs +++ b/crates/viewer/re_renderer/src/importer/stl.rs @@ -25,7 +25,7 @@ pub fn load_stl_from_buffer( // https://github.com/hmeyer/stl_io/pull/26 let name = reader.name().cloned().unwrap_or_default(); - let (normals, vertices): (Vec<_>, Vec<_>) = reader + let (normals, triangles): (Vec<_>, Vec<_>) = reader .into_iter() .map(|triangle| triangle.unwrap()) .map(|triangle| { @@ -40,7 +40,7 @@ pub fn load_stl_from_buffer( }) .unzip(); - let num_vertices = vertices.len() * 3; + let num_vertices = triangles.len() * 3; let material = mesh::Material { label: name.clone().into(), @@ -56,7 +56,7 @@ pub fn load_stl_from_buffer( .map(glam::UVec3::from) .collect::>(), - vertex_positions: bytemuck::cast_vec(vertices), + vertex_positions: bytemuck::cast_vec(triangles), // Normals on STL are per triangle, not per vertex. // Yes, this makes STL always look faceted. From 7f0d9b9b19cc0824f4a0be19dad0222ce24f978d Mon Sep 17 00:00:00 2001 From: Gijs de Jong Date: Wed, 21 May 2025 14:39:25 +0200 Subject: [PATCH 6/6] feedback + less normal collecting --- Cargo.lock | 5 +++-- Cargo.toml | 2 +- crates/viewer/re_renderer/src/importer/stl.rs | 21 +++++++------------ 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a09628cd4445..06b333cf8c00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9462,8 +9462,9 @@ dependencies = [ [[package]] name = "stl_io" -version = "0.9.0" -source = "git+https://github.com/oxkitsune/stl_io?branch=gijs%2Fmesh-name#b4325a398f16ed7b3ce9c35f21d49d7269687e6e" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff2e145168af9fef3b518ac0c6f9849c407b3df8a28582ced9f1fda510aa34c" dependencies = [ "byteorder", "float-cmp", diff --git a/Cargo.toml b/Cargo.toml index fed223f7a1ed..2ea0282e4f03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -296,7 +296,7 @@ similar-asserts = "1.4.2" slotmap = { version = "1.0.6", features = ["serde"] } smallvec = { version = "1.0", features = ["const_generics", "union"] } static_assertions = "1.1" -stl_io = { git = "https://github.com/oxkitsune/stl_io", branch = "gijs/mesh-name" } +stl_io = "0.8.5" strum = { version = "0.26", features = ["derive"] } strum_macros = "0.26" sublime_fuzzy = "0.7" diff --git a/crates/viewer/re_renderer/src/importer/stl.rs b/crates/viewer/re_renderer/src/importer/stl.rs index b490fbaaa26c..cb82c5cb63ef 100644 --- a/crates/viewer/re_renderer/src/importer/stl.rs +++ b/crates/viewer/re_renderer/src/importer/stl.rs @@ -1,7 +1,7 @@ use itertools::Itertools as _; use smallvec::smallvec; -use crate::{CpuModel, RenderContext, mesh}; +use crate::{CpuModel, DebugLabel, RenderContext, mesh}; #[derive(thiserror::Error, Debug)] pub enum StlImportError { @@ -21,16 +21,17 @@ pub fn load_stl_from_buffer( let mut cursor = std::io::Cursor::new(buffer); let reader = stl_io::create_stl_reader(&mut cursor).map_err(StlImportError::StlIoError)?; - // TODO(hmeyer/stl_io#26): parse name from ASCII? + + // TODO(hmeyer/stl_io#26): use optional name from ascii stl files. // https://github.com/hmeyer/stl_io/pull/26 - let name = reader.name().cloned().unwrap_or_default(); + let name = DebugLabel::from(""); let (normals, triangles): (Vec<_>, Vec<_>) = reader .into_iter() .map(|triangle| triangle.unwrap()) .map(|triangle| { ( - triangle.normal, + [triangle.normal.0, triangle.normal.0, triangle.normal.0], [ triangle.vertices[0].0, triangle.vertices[1].0, @@ -43,14 +44,14 @@ pub fn load_stl_from_buffer( let num_vertices = triangles.len() * 3; let material = mesh::Material { - label: name.clone().into(), + label: name.clone(), index_range: 0..num_vertices as u32, albedo: ctx.texture_manager_2d.white_texture_unorm_handle().clone(), albedo_factor: crate::Rgba::WHITE, }; let mesh = mesh::CpuMesh { - label: name.clone().into(), + label: name.clone(), triangle_indices: (0..num_vertices as u32) .tuples::<(_, _, _)>() .map(glam::UVec3::from) @@ -60,13 +61,7 @@ pub fn load_stl_from_buffer( // Normals on STL are per triangle, not per vertex. // Yes, this makes STL always look faceted. - vertex_normals: normals - .iter() - .flat_map(|n| { - let n = glam::Vec3::from_array(n.0); - [n, n, n] - }) - .collect(), + vertex_normals: bytemuck::cast_vec(normals), // STL has neither colors nor texcoords. vertex_colors: vec![crate::Rgba32Unmul::WHITE; num_vertices],