Skip to content

Commit

Permalink
Add vertex_colors to stl/obj Asset3D (Rust/Python/C++)
Browse files Browse the repository at this point in the history
- add attribute `vertex_colors` and method `with_vertex_colors` to archetype `Asset3D`.
- update stl/obj loader : `load_stl_from_buffer` and `load_obj_from_buffer` to process `vertex_colors`.
  • Loading branch information
EtaLoop committed Sep 30, 2024
1 parent 7eddd5b commit 161224d
Show file tree
Hide file tree
Showing 16 changed files with 183 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ table Asset3D (

// --- Optional ---

/// An optional color for each vertex.
vertex_colors: [rerun.components.Color] ("attr.rerun.component_optional", nullable, order: 3100);

/// A color multiplier applied to the whole asset.
albedo_factor: rerun.components.AlbedoFactor ("attr.rerun.component_optional", nullable, order: 3100);
albedo_factor: rerun.components.AlbedoFactor ("attr.rerun.component_optional", nullable, order: 3200);
}
48 changes: 43 additions & 5 deletions crates/store/re_types/src/archetypes/asset3d.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/store/re_types/src/archetypes/asset3d_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ impl Asset3D {
Self {
blob: contents.into(),
media_type,
vertex_colors: None,
albedo_factor: None,
}
}
Expand Down
5 changes: 5 additions & 0 deletions crates/store/re_types/tests/types/asset3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@ fn roundtrip() {
let expected = Asset3D {
blob: Blob(BYTES.to_vec().into()),
media_type: Some(MediaType(Utf8(MediaType::GLTF.into()))),
vertex_colors: Some(vec![
Rgba32::from_unmultiplied_rgba(0xAA, 0x00, 0x00, 0xCC).into(), //
Rgba32::from_unmultiplied_rgba(0x00, 0xBB, 0x00, 0xDD).into(),
]),
albedo_factor: Some(Rgba32::from_unmultiplied_rgba(0xEE, 0x11, 0x22, 0x33).into()),
};

let arch = Asset3D::from_file_contents(BYTES.to_vec(), Some(MediaType::gltf()))
.with_vertex_colors([0xAA0000CC, 0x00BB00DD])
.with_albedo_factor(0xEE112233);
similar_asserts::assert_eq!(expected, arch);

Expand Down
33 changes: 18 additions & 15 deletions crates/viewer/re_renderer/src/importer/obj.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::sync::Arc;

use re_types::components::AlbedoFactor;
use re_types::components::{AlbedoFactor, Color};
use smallvec::smallvec;

use crate::{
Expand All @@ -9,6 +9,8 @@ use crate::{
RenderContext, Rgba32Unmul,
};

use super::stl::clamped_vec_or_empty_color;

#[derive(thiserror::Error, Debug)]
pub enum ObjImportError {
#[error(transparent)]
Expand All @@ -23,7 +25,8 @@ pub enum ObjImportError {
pub fn load_obj_from_buffer(
buffer: &[u8],
ctx: &RenderContext,
albedo_factor: Option<AlbedoFactor>,
vertex_colors: &Option<Vec<Color>>,
albedo_factor: &Option<AlbedoFactor>,
) -> Result<Vec<MeshInstance>, ObjImportError> {
re_tracing::profile_function!();

Expand Down Expand Up @@ -56,19 +59,19 @@ pub fn load_obj_from_buffer(
.map(|p| glam::uvec3(p[0], p[1], p[2]))
.collect();

let mut vertex_colors: Vec<Rgba32Unmul> = mesh
.vertex_color
.chunks_exact(3)
.map(|c| {
Rgba32Unmul::from_rgb(
// It is not specified if the color is in linear or gamma space, but gamma seems a safe bet.
(c[0] * 255.0).round() as u8,
(c[1] * 255.0).round() as u8,
(c[2] * 255.0).round() as u8,
)
})
.collect();
vertex_colors.resize(vertex_positions.len(), Rgba32Unmul::WHITE);
let num_positions = vertex_positions.len();

let vertex_colors = if let Some(vertex_colors) = vertex_colors {
let vertex_colors_arr =
clamped_vec_or_empty_color(vertex_colors.as_slice(), vertex_positions.len());
re_tracing::profile_scope!("copy_colors");
vertex_colors_arr
.iter()
.map(|c| Rgba32Unmul::from_rgba_unmul_array(c.to_array()))
.collect()
} else {
vec![Rgba32Unmul::WHITE; num_positions]
};

let mut vertex_normals: Vec<glam::Vec3> = mesh
.normals
Expand Down
47 changes: 43 additions & 4 deletions crates/viewer/re_renderer/src/importer/stl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ use tinystl::StlData;
use crate::{
mesh::{self, GpuMesh},
renderer::MeshInstance,
RenderContext,
RenderContext, Rgba32Unmul,
};
use re_types::archetypes::Asset3D;
use re_types::{archetypes::Asset3D, components::Color};

#[derive(thiserror::Error, Debug)]
pub enum StlImportError {
Expand All @@ -30,6 +30,7 @@ pub fn load_stl_from_buffer(

let Asset3D {
blob,
vertex_colors,
albedo_factor,
..
} = asset3d;
Expand All @@ -45,6 +46,20 @@ pub fn load_stl_from_buffer(
} = StlData::read_buffer(std::io::BufReader::new(cursor)).map_err(StlImportError::TinyStl)?;

let num_vertices = triangles.len() * 3;
let vertex_positions: &[glam::Vec3] = bytemuck::cast_slice(&triangles);
let num_positions = vertex_positions.len();

let vertex_colors = if let Some(vertex_colors) = vertex_colors {
let vertex_colors_arr =
clamped_vec_or_empty_color(vertex_colors.as_slice(), vertex_positions.len());
re_tracing::profile_scope!("copy_colors");
vertex_colors_arr
.iter()
.map(|c| Rgba32Unmul::from_rgba_unmul_array(c.to_array()))
.collect()
} else {
vec![Rgba32Unmul::WHITE; num_positions]
};

let material = mesh::Material {
label: name.clone().into(),
Expand All @@ -71,8 +86,8 @@ pub fn load_stl_from_buffer(
})
.collect(),

// STL has neither colors nor texcoords.
vertex_colors: vec![crate::Rgba32Unmul::WHITE; num_vertices],
vertex_colors,
// STL has no texcoords.
vertex_texcoords: vec![glam::Vec2::ZERO; num_vertices],

materials: smallvec![material],
Expand All @@ -85,3 +100,27 @@ pub fn load_stl_from_buffer(
Some(Arc::new(mesh)),
)])
}

pub fn clamped_vec_or_empty_color(values: &[Color], clamped_len: usize) -> Vec<Color> {
if values.len() == clamped_len {
// Happy path
values.to_vec() // TODO(emilk): return a slice reference instead, in a `Cow` or similar
} else if let Some(last) = values.last() {
if values.len() == 1 {
// Commo happy path
return vec![*last; clamped_len];
} else if values.len() < clamped_len {
// Clamp
let mut vec = Vec::with_capacity(clamped_len);
vec.extend(values.iter());
vec.extend(std::iter::repeat(last).take(clamped_len - values.len()));
vec
} else {
// Trim
values.iter().take(clamped_len).copied().collect()
}
} else {
// Empty input
Vec::new()
}
}
7 changes: 1 addition & 6 deletions crates/viewer/re_renderer_examples/framework.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,12 +332,7 @@ pub fn load_rerun_mesh(re_ctx: &RenderContext) -> Vec<re_renderer::renderer::Mes
let mut zipped_obj = zip.by_name("rerun.obj").unwrap();
let mut obj_data = Vec::new();
std::io::Read::read_to_end(&mut zipped_obj, &mut obj_data).unwrap();
re_renderer::importer::obj::load_obj_from_buffer(
&obj_data,
re_ctx,
None,
)
.unwrap()
re_renderer::importer::obj::load_obj_from_buffer(&obj_data, re_ctx, &None, &None).unwrap()
}

struct WrapApp<E: Example + 'static> {
Expand Down
11 changes: 5 additions & 6 deletions crates/viewer/re_space_view_spatial/src/mesh_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,14 @@ impl LoadedMesh {
let bytes = asset3d.blob.as_slice();

let mesh_instances = match media_type.as_str() {
MediaType::GLTF | MediaType::GLB => re_renderer::importer::gltf::load_gltf_from_buffer(
&name,
bytes,
render_ctx,
)?,
MediaType::GLTF | MediaType::GLB => {
re_renderer::importer::gltf::load_gltf_from_buffer(&name, bytes, render_ctx)?
}
MediaType::OBJ => re_renderer::importer::obj::load_obj_from_buffer(
bytes,
render_ctx,
asset3d.albedo_factor,
&asset3d.vertex_colors,
&asset3d.albedo_factor,
)?,
MediaType::STL => {
re_renderer::importer::stl::load_stl_from_buffer(asset3d, render_ctx, texture_key)?
Expand Down
Loading

0 comments on commit 161224d

Please sign in to comment.