Skip to content

Commit

Permalink
Allow overriding albedo color on Asset3D (#7458)
Browse files Browse the repository at this point in the history
### What

The idea is to reproduce behavior of `Mesh3D` regarding color
modification on `Asset3D` :
- add `albedo_factor` 
- add `vertex_colors` as in `Mesh3D` 

In order to overwrite white default texture of `Asset3D` loaded from stl
and obj (gltf already have albedo_factor in file).

See: #5253 


![Screenshot from 2024-09-18
16-05-54](https://github.com/user-attachments/assets/3f9f396e-1549-4e88-a933-f6143bddf734)

### Checklist
* [x] I have read and agree to [Contributor
Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and
the [Code of
Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md)
* [x] I've included a screenshot or gif (if applicable)
* [x] I have tested the web demo (if applicable):
* Using examples from latest `main` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/7458?manifest_url=https://app.rerun.io/version/main/examples_manifest.json)
* Using full set of examples from `nightly` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/7458?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json)
* [x] The PR title and labels are set such as to maximize their
usefulness for the next release's CHANGELOG
* [x] If applicable, add a new check to the [release
checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)!
* [x] If have noted any breaking changes to the log API in
`CHANGELOG.md` and the migration guide

- [PR Build Summary](https://build.rerun.io/pr/7458)
- [Recent benchmark results](https://build.rerun.io/graphs/crates.html)
- [Wasm size tracking](https://build.rerun.io/graphs/sizes.html)

To run all checks from `main`, comment on the PR with `@rerun-bot
full-check`.

---------

Co-authored-by: Keyvan Goddard <keyvan.goddard@laas.fr>
Co-authored-by: Andreas Reich <r_andreas2@web.de>
  • Loading branch information
3 people authored Oct 24, 2024
1 parent c63bbea commit 0feef41
Show file tree
Hide file tree
Showing 15 changed files with 178 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,12 @@ table Asset3D (
/// If omitted, the viewer will try to guess from the data blob.
/// If it cannot guess, it won't be able to render the asset.
media_type: rerun.components.MediaType ("attr.rerun.component_recommended", nullable, order: 2000);

// --- Optional ---

/// A color multiplier applied to the whole asset.
///
/// For mesh who already have `albedo_factor` in materials,
/// it will be overwritten by actual `albedo_factor` of [archetypes.Asset3D] (if specified).
albedo_factor: rerun.components.AlbedoFactor ("attr.rerun.component_optional", nullable, order: 3100);
}
58 changes: 50 additions & 8 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 @@ -36,6 +36,7 @@ impl Asset3D {
Self {
blob: contents.into(),
media_type,
albedo_factor: None,
}
}
}
6 changes: 4 additions & 2 deletions crates/store/re_types/tests/types/asset3d.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use re_types::{
archetypes::Asset3D,
components::{Blob, MediaType},
datatypes::Utf8,
datatypes::{Rgba32, Utf8},
Archetype as _, AsComponents as _,
};

Expand All @@ -12,9 +12,11 @@ fn roundtrip() {
let expected = Asset3D {
blob: Blob(BYTES.to_vec().into()),
media_type: Some(MediaType(Utf8(MediaType::GLTF.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()));
let arch = Asset3D::from_file_contents(BYTES.to_vec(), Some(MediaType::gltf()))
.with_albedo_factor(0xEE112233);
similar_asserts::assert_eq!(expected, arch);

// let expected_extensions: HashMap<_, _> = [
Expand Down
2 changes: 1 addition & 1 deletion crates/viewer/re_renderer/src/importer/stl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub fn load_stl_from_buffer(
let num_vertices = triangles.len() * 3;

let material = mesh::Material {
label: "default material".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,
Expand Down
4 changes: 3 additions & 1 deletion crates/viewer/re_space_view_spatial/src/mesh_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ pub struct MeshCache(HashMap<RowId, HashMap<MeshCacheKey, Option<Arc<LoadedMesh>
/// Either a [`re_types::archetypes::Asset3D`] or [`re_types::archetypes::Mesh3D`] to be cached.
#[derive(Debug, Clone, Copy)]
pub enum AnyMesh<'a> {
Asset(&'a re_types::archetypes::Asset3D),
Asset {
asset: &'a re_types::archetypes::Asset3D,
},
Mesh {
mesh: &'a re_types::archetypes::Mesh3D,

Expand Down
27 changes: 22 additions & 5 deletions crates/viewer/re_space_view_spatial/src/mesh_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use re_chunk_store::RowId;
use re_renderer::{mesh::GpuMesh, RenderContext, Rgba32Unmul};
use re_types::{
archetypes::{Asset3D, Mesh3D},
components::MediaType,
components::{AlbedoFactor, MediaType},
};
use re_viewer_context::{gpu_bridge::texture_creation_desc_from_color_image, ImageInfo};

Expand All @@ -27,7 +27,7 @@ impl LoadedMesh {
) -> anyhow::Result<Self> {
// TODO(emilk): load CpuMesh in background thread.
match mesh {
AnyMesh::Asset(asset3d) => Self::load_asset3d(name, asset3d, render_ctx),
AnyMesh::Asset { asset } => Ok(Self::load_asset3d(name, asset, render_ctx)?),
AnyMesh::Mesh { mesh, texture_key } => {
Ok(Self::load_mesh3d(name, mesh, texture_key, render_ctx)?)
}
Expand All @@ -39,10 +39,11 @@ impl LoadedMesh {
media_type: &MediaType,
bytes: &[u8],
render_ctx: &RenderContext,
albedo_factor: &Option<AlbedoFactor>,
) -> anyhow::Result<Self> {
re_tracing::profile_function!();

let cpu_model = match media_type.as_str() {
let mut cpu_model = match media_type.as_str() {
MediaType::GLTF | MediaType::GLB => {
re_renderer::importer::gltf::load_gltf_from_buffer(&name, bytes, render_ctx)?
}
Expand All @@ -51,6 +52,12 @@ impl LoadedMesh {
_ => anyhow::bail!("{media_type} files are not supported"),
};

// Overwriting albedo_factor of CpuMesh in specified in the Asset3D
cpu_model.instances.iter().for_each(|instance| {
cpu_model.meshes[instance.mesh].materials[0].albedo_factor =
albedo_factor.map_or(re_renderer::Rgba::WHITE, |c| c.0.into());
});

let bbox = cpu_model.calculate_bounding_box();
let mesh_instances = cpu_model.into_gpu_meshes(render_ctx)?;

Expand All @@ -68,11 +75,21 @@ impl LoadedMesh {
) -> anyhow::Result<Self> {
re_tracing::profile_function!();

let Asset3D { blob, media_type } = asset3d;
let Asset3D {
blob,
media_type,
albedo_factor,
} = asset3d;

let media_type = MediaType::or_guess_from_data(media_type.clone(), blob.as_slice())
.ok_or_else(|| anyhow::anyhow!("couldn't guess media type"))?;
let slf = Self::load_asset3d_parts(name, &media_type, blob.as_slice(), render_ctx)?;
let slf = Self::load_asset3d_parts(
name,
&media_type,
blob.as_slice(),
render_ctx,
albedo_factor,
)?;

Ok(slf)
}
Expand Down
65 changes: 41 additions & 24 deletions crates/viewer/re_space_view_spatial/src/visualizers/assets3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use re_renderer::renderer::GpuMeshInstance;
use re_renderer::RenderContext;
use re_types::{
archetypes::Asset3D,
components::{Blob, MediaType},
components::{AlbedoFactor, Blob, MediaType},
ArrowBuffer, ArrowString, Loggable as _,
};
use re_viewer_context::{
Expand Down Expand Up @@ -32,48 +32,52 @@ impl Default for Asset3DVisualizer {
}
}

struct Asset3DComponentData {
struct Asset3DComponentData<'a> {
index: (TimeInt, RowId),
query_result_hash: Hash64,

blob: ArrowBuffer<u8>,
media_type: Option<ArrowString>,
albedo_factor: Option<&'a AlbedoFactor>,
}

// NOTE: Do not put profile scopes in these methods. They are called for all entities and all
// timestamps within a time range -- it's _a lot_.
impl Asset3DVisualizer {
fn process_data(
fn process_data<'a>(
&mut self,
ctx: &QueryContext<'_>,
render_ctx: &RenderContext,
instances: &mut Vec<GpuMeshInstance>,
ent_context: &SpatialSceneEntityContext<'_>,
data: impl Iterator<Item = Asset3DComponentData>,
data: impl Iterator<Item = Asset3DComponentData<'a>>,
) {
let entity_path = ctx.target_entity_path;

for data in data {
let mesh = Asset3D {
blob: data.blob.clone().into(),
media_type: data.media_type.clone().map(Into::into),
};

let primary_row_id = data.index.1;
let picking_instance_hash = re_entity_db::InstancePathHash::entity_all(entity_path);
let outline_mask_ids = ent_context.highlight.index_outline_mask(Instance::ALL);

// TODO(#5974): this is subtly wrong, the key should actually be a hash of everything that got
// cached, which includes the media type…
let mesh = ctx.viewer_ctx.cache.entry(|c: &mut MeshCache| {
let key = MeshCacheKey {
versioned_instance_path_hash: picking_instance_hash.versioned(primary_row_id),
query_result_hash: data.query_result_hash,
media_type: data.media_type.clone().map(Into::into),
};

c.entry(
&entity_path.to_string(),
MeshCacheKey {
versioned_instance_path_hash: picking_instance_hash
.versioned(primary_row_id),
query_result_hash: Hash64::ZERO,
media_type: data.media_type.clone().map(Into::into),
key.clone(),
AnyMesh::Asset {
asset: &Asset3D {
blob: data.blob.clone().into(),
media_type: data.media_type.clone().map(Into::into),
albedo_factor: data.albedo_factor.copied(),
},
},
AnyMesh::Asset(&mesh),
render_ctx,
)
});
Expand Down Expand Up @@ -154,16 +158,29 @@ impl VisualizerSystem for Asset3DVisualizer {
let timeline = ctx.query.timeline();
let all_blobs_indexed = iter_buffer::<u8>(&all_blob_chunks, timeline, Blob::name());
let all_media_types = results.iter_as(timeline, MediaType::name());
let all_albedo_factors = results.iter_as(timeline, AlbedoFactor::name());

let query_result_hash = results.query_result_hash();

let data = re_query::range_zip_1x1(all_blobs_indexed, all_media_types.string())
.filter_map(|(index, blobs, media_types)| {
blobs.first().map(|blob| Asset3DComponentData {
index,
blob: blob.clone(),
media_type: media_types
.and_then(|media_types| media_types.first().cloned()),
})
});
let data = re_query::range_zip_1x2(
all_blobs_indexed,
all_media_types.string(),
all_albedo_factors.primitive::<u32>(),
)
.filter_map(|(index, blobs, media_types, albedo_factors)| {
blobs.first().map(|blob| Asset3DComponentData {
index,
query_result_hash,
blob: blob.clone(),
media_type: media_types
.and_then(|media_types| media_types.first().cloned()),
albedo_factor: albedo_factors
.map_or(&[] as &[AlbedoFactor], |albedo_factors| {
bytemuck::cast_slice(albedo_factors)
})
.first(),
})
});

self.process_data(ctx, render_ctx, &mut instances, spatial_ctx, data);

Expand Down
4 changes: 4 additions & 0 deletions crates/viewer/re_viewer/src/reflection/mod.rs

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

2 changes: 2 additions & 0 deletions docs/content/reference/types/archetypes/asset3d.md

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

1 change: 1 addition & 0 deletions docs/content/reference/types/components/albedo_factor.md

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

Loading

0 comments on commit 0feef41

Please sign in to comment.