Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow color specification on Asset3D #7458

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
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 Asset3D (if specified).
EtaLoop marked this conversation as resolved.
Show resolved Hide resolved
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>,
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I pass the whole Asset3D or just bytes and albedo_factor?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't matter much, but I reckon all callsites have Asset3D ready already? Then might as well pass the whole thing

) -> 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
Loading