use re_chunk_store::RowId;
use re_log_types::hash::Hash64;
use re_log_types::{Instance, TimeInt};
use re_renderer::RenderContext;
use re_renderer::renderer::GpuMeshInstance;
use re_sdk_types::Archetype as _;
use re_sdk_types::archetypes::Mesh3D;
use re_sdk_types::components::{ImageFormat, Position3D};
use re_viewer_context::{
IdentifiedViewSystem, QueryContext, ViewContext, ViewContextCollection, ViewQuery,
ViewSystemExecutionError, VisualizerExecutionOutput, VisualizerQueryInfo, VisualizerSystem,
};
use super::SpatialViewVisualizerData;
use crate::caches::{AnyMesh, MeshCache, MeshCacheKey};
use crate::contexts::SpatialSceneVisualizerInstructionContext;
use crate::mesh_loader::NativeMesh3D;
use crate::view_kind::SpatialViewKind;
pub struct Mesh3DVisualizer(SpatialViewVisualizerData);
impl Default for Mesh3DVisualizer {
fn default() -> Self {
Self(SpatialViewVisualizerData::new(Some(
SpatialViewKind::ThreeD,
)))
}
}
struct Mesh3DComponentData<'a> {
index: (TimeInt, RowId),
query_result_hash: Hash64,
native_mesh: NativeMesh3D<'a>,
cull_mode: Option<re_renderer::external::wgpu::Face>,
}
impl Mesh3DVisualizer {
fn process_data<'a>(
&mut self,
ctx: &QueryContext<'_>,
render_ctx: &RenderContext,
instances: &mut Vec<GpuMeshInstance>,
ent_context: &SpatialSceneVisualizerInstructionContext<'_>,
data: impl Iterator<Item = Mesh3DComponentData<'a>>,
) {
let entity_path = ctx.target_entity_path;
for data in data {
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);
if data.native_mesh.vertex_positions.is_empty() {
continue;
}
let mesh = ctx.store_ctx().memoizer(|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: None,
};
c.entry(
&entity_path.to_string(),
key.clone(),
AnyMesh::Mesh {
mesh: data.native_mesh,
texture_key: re_log_types::hash::Hash64::hash(&key).hash64(),
},
render_ctx,
)
});
if let Some(mesh) = mesh {
for &world_from_instance in ent_context.transform_info.target_from_instances() {
let world_from_instance = world_from_instance.as_affine3a();
instances.extend(mesh.mesh_instances.iter().map(move |mesh_instance| {
let entity_from_mesh = mesh_instance.world_from_mesh;
let world_from_mesh = world_from_instance * entity_from_mesh;
GpuMeshInstance {
gpu_mesh: mesh_instance.gpu_mesh.clone(),
world_from_mesh,
outline_mask_ids,
picking_layer_id: re_view::picking_layer_id_from_instance_path_hash(
picking_instance_hash,
),
additive_tint: re_renderer::Color32::BLACK,
cull_mode: data.cull_mode,
}
}));
self.0
.add_bounding_box(entity_path.hash(), mesh.bbox(), world_from_instance);
}
}
}
}
}
impl IdentifiedViewSystem for Mesh3DVisualizer {
fn identifier() -> re_viewer_context::ViewSystemIdentifier {
"Mesh3D".into()
}
}
impl VisualizerSystem for Mesh3DVisualizer {
fn visualizer_query_info(
&self,
_app_options: &re_viewer_context::AppOptions,
) -> VisualizerQueryInfo {
VisualizerQueryInfo::single_required_component::<Position3D>(
&Mesh3D::descriptor_vertex_positions(),
&Mesh3D::all_components(),
)
}
fn execute(
&mut self,
ctx: &ViewContext<'_>,
view_query: &ViewQuery<'_>,
context_systems: &ViewContextCollection,
) -> Result<VisualizerExecutionOutput, ViewSystemExecutionError> {
re_tracing::profile_function!();
let output = VisualizerExecutionOutput::default();
let mut instances = Vec::new();
use super::entity_iterator::process_archetype;
process_archetype::<Self, Mesh3D, _>(
ctx,
view_query,
context_systems,
&output,
self.0.preferred_view_kind,
|ctx, spatial_ctx, results| {
let all_vertex_positions =
results.iter_required(Mesh3D::descriptor_vertex_positions().component);
if all_vertex_positions.is_empty() {
return Ok(());
}
let all_vertex_normals =
results.iter_optional(Mesh3D::descriptor_vertex_normals().component);
let all_vertex_colors =
results.iter_optional(Mesh3D::descriptor_vertex_colors().component);
let all_vertex_texcoords =
results.iter_optional(Mesh3D::descriptor_vertex_texcoords().component);
let all_triangle_indices =
results.iter_optional(Mesh3D::descriptor_triangle_indices().component);
let all_albedo_factors =
results.iter_optional(Mesh3D::descriptor_albedo_factor().component);
let all_albedo_buffers =
results.iter_optional(Mesh3D::descriptor_albedo_texture_buffer().component);
let all_albedo_formats =
results.iter_optional(Mesh3D::descriptor_albedo_texture_format().component);
let all_face_rendering =
results.iter_optional(Mesh3D::descriptor_face_rendering().component);
let query_result_hash = results.query_result_hash();
let data = re_query::range_zip_1x8(
all_vertex_positions.slice::<[f32; 3]>(),
all_vertex_normals.slice::<[f32; 3]>(),
all_vertex_colors.slice::<u32>(),
all_vertex_texcoords.slice::<[f32; 2]>(),
all_triangle_indices.slice::<[u32; 3]>(),
all_albedo_factors.slice::<u32>(),
all_albedo_buffers.slice::<&[u8]>(),
all_albedo_formats.component_slow::<ImageFormat>(),
all_face_rendering.slice::<u8>(),
)
.map(
|(
index,
vertex_positions,
vertex_normals,
vertex_colors,
vertex_texcoords,
triangle_indices,
albedo_factors,
albedo_buffers,
albedo_formats,
face_rendering,
)| {
Mesh3DComponentData {
index,
query_result_hash,
native_mesh: NativeMesh3D {
vertex_positions: bytemuck::cast_slice(vertex_positions),
vertex_normals: vertex_normals.map(bytemuck::cast_slice),
vertex_colors: vertex_colors.map(bytemuck::cast_slice),
vertex_texcoords: vertex_texcoords.map(bytemuck::cast_slice),
triangle_indices: triangle_indices.map(bytemuck::cast_slice),
albedo_factor: albedo_factors
.map(bytemuck::cast_slice)
.and_then(|albedo_factors| albedo_factors.first().copied()),
albedo_texture_buffer: albedo_buffers
.unwrap_or_default()
.first()
.cloned()
.map(Into::into), albedo_texture_format: albedo_formats
.unwrap_or_default()
.first()
.map(|format| format.0),
},
cull_mode: face_rendering
.and_then(|s| s.first().copied())
.and_then(face_rendering_to_wgpu_cull),
}
},
);
self.process_data(ctx, ctx.render_ctx(), &mut instances, spatial_ctx, data);
Ok(())
},
)?;
Ok(
output.with_draw_data([re_renderer::renderer::MeshDrawData::new(
ctx.viewer_ctx.render_ctx(),
&instances,
)?
.into()]),
)
}
fn data(&self) -> Option<&dyn std::any::Any> {
Some(self.0.as_any())
}
}
fn face_rendering_to_wgpu_cull(value: u8) -> Option<re_renderer::external::wgpu::Face> {
use re_sdk_types::components::MeshFaceRendering;
use re_sdk_types::reflection::Enum as _;
match MeshFaceRendering::try_from_integer(value)? {
MeshFaceRendering::DoubleSided => None,
MeshFaceRendering::Front => Some(re_renderer::external::wgpu::Face::Back),
MeshFaceRendering::Back => Some(re_renderer::external::wgpu::Face::Front),
}
}