diff --git a/crates/re_renderer/src/renderer/depth_cloud.rs b/crates/re_renderer/src/renderer/depth_cloud.rs index b66097528b64..fc6c13a36539 100644 --- a/crates/re_renderer/src/renderer/depth_cloud.rs +++ b/crates/re_renderer/src/renderer/depth_cloud.rs @@ -166,6 +166,37 @@ pub struct DepthCloud { pub outline_mask_id: OutlineMaskPreference, } +impl DepthCloud { + /// World-space bounding-box. + pub fn bbox(&self) -> macaw::BoundingBox { + let max_depth = self.max_depth_in_world; + let w = self.depth_dimensions.x as f32; + let h = self.depth_dimensions.y as f32; + let corners = [ + glam::Vec3::ZERO, // camera origin + glam::Vec3::new(0.0, 0.0, max_depth), + glam::Vec3::new(0.0, h, max_depth), + glam::Vec3::new(w, 0.0, max_depth), + glam::Vec3::new(w, h, max_depth), + ]; + + let intrinsics = self.depth_camera_intrinsics; + let focal_length = glam::vec2(intrinsics.col(0).x, intrinsics.col(1).y); + let offset = intrinsics.col(2).truncate(); + + let mut bbox = macaw::BoundingBox::nothing(); + + for corner in corners { + let depth = corner.z; + let pos_in_obj = ((corner.truncate() - offset) * depth / focal_length).extend(depth); + let pos_in_world = self.world_from_obj.project_point3(pos_in_obj); + bbox.extend(pos_in_world); + } + + bbox + } +} + pub struct DepthClouds { pub clouds: Vec, pub radius_boost_in_ui_points_for_outlines: f32, diff --git a/crates/re_ui/src/lib.rs b/crates/re_ui/src/lib.rs index 222473b8ed15..d5d9569cf7b2 100644 --- a/crates/re_ui/src/lib.rs +++ b/crates/re_ui/src/lib.rs @@ -480,11 +480,13 @@ impl ReUi { .inner } - /// Grid to be used in selection view. + /// Two-column grid to be used in selection view. #[allow(clippy::unused_self)] pub fn selection_grid(&self, ui: &mut egui::Ui, id: &str) -> egui::Grid { // Spread rows a bit to make it easier to see the groupings - egui::Grid::new(id).spacing(ui.style().spacing.item_spacing + egui::vec2(0.0, 8.0)) + egui::Grid::new(id) + .num_columns(2) + .spacing(ui.style().spacing.item_spacing + egui::vec2(0.0, 8.0)) } /// Draws a shadow into the given rect with the shadow direction given from dark to light diff --git a/crates/re_viewer/src/ui/view_spatial/scene/primitives.rs b/crates/re_viewer/src/ui/view_spatial/scene/primitives.rs index cf86d5c6d2d7..e8a56e454b27 100644 --- a/crates/re_viewer/src/ui/view_spatial/scene/primitives.rs +++ b/crates/re_viewer/src/ui/view_spatial/scene/primitives.rs @@ -93,7 +93,7 @@ impl SceneSpatialPrimitives { line_strips, points, meshes, - depth_clouds: _, // no bbox for depth clouds + depth_clouds, any_outlines: _, } = self; @@ -133,6 +133,10 @@ impl SceneSpatialPrimitives { *bounding_box = bounding_box.union(mesh.mesh.bbox().transform_affine3(&mesh.world_from_mesh)); } + + for cloud in &depth_clouds.clouds { + *bounding_box = bounding_box.union(cloud.bbox()); + } } pub fn mesh_instances(&self) -> Vec { diff --git a/crates/re_viewer/src/ui/view_spatial/ui.rs b/crates/re_viewer/src/ui/view_spatial/ui.rs index fb4fd4b78181..8542ecc881cf 100644 --- a/crates/re_viewer/src/ui/view_spatial/ui.rs +++ b/crates/re_viewer/src/ui/view_spatial/ui.rs @@ -347,6 +347,7 @@ impl ViewSpatialState { }); }); ui.checkbox(&mut self.state_3d.show_axes, "Show origin axes").on_hover_text("Show X-Y-Z axes"); + ui.checkbox(&mut self.state_3d.show_bbox, "Show bounding box").on_hover_text("Show the current scene bounding box"); }); ui.end_row(); } @@ -354,6 +355,7 @@ impl ViewSpatialState { ctx.re_ui.grid_left_hand_label(ui, "Bounding box") .on_hover_text("The bounding box encompassing all Entities in the view right now."); ui.vertical(|ui| { + ui.style_mut().wrap = Some(false); let BoundingBox { min, max } = self.scene_bbox; ui.label(format!( "x [{} - {}]", diff --git a/crates/re_viewer/src/ui/view_spatial/ui_3d.rs b/crates/re_viewer/src/ui/view_spatial/ui_3d.rs index 01dc139aee03..36d7044d1408 100644 --- a/crates/re_viewer/src/ui/view_spatial/ui_3d.rs +++ b/crates/re_viewer/src/ui/view_spatial/ui_3d.rs @@ -56,6 +56,7 @@ pub struct View3DState { // options: pub spin: bool, pub show_axes: bool, + pub show_bbox: bool, #[serde(skip)] last_eye_interact_time: f64, @@ -77,6 +78,7 @@ impl Default for View3DState { hovered_point: Default::default(), spin: false, show_axes: false, + show_bbox: false, last_eye_interact_time: f64::NEG_INFINITY, space_specs: Default::default(), space_camera: Default::default(), @@ -549,6 +551,26 @@ pub fn view_3d( ); } + if state.state_3d.show_bbox { + let bbox = scene.primitives.bounding_box(); + if bbox.is_something() && bbox.is_finite() { + let scale = bbox.size(); + let translation = bbox.center(); + let bbox_from_unit_cube = glam::Affine3A::from_scale_rotation_translation( + scale, + Default::default(), + translation, + ); + scene + .primitives + .line_strips + .batch("scene_bbox") + .add_box_outline(bbox_from_unit_cube) + .radius(Size::AUTO) + .color(egui::Color32::WHITE); + } + } + { let orbit_center_alpha = egui::remap_clamp( ui.input(|i| i.time) - state.state_3d.last_eye_interact_time, diff --git a/crates/re_viewer/src/ui/view_text/ui.rs b/crates/re_viewer/src/ui/view_text/ui.rs index 29ec21629d60..031472657303 100644 --- a/crates/re_viewer/src/ui/view_text/ui.rs +++ b/crates/re_viewer/src/ui/view_text/ui.rs @@ -37,43 +37,40 @@ impl ViewTextState { row_log_levels, } = &mut self.filters; - re_ui - .selection_grid(ui, "log_config") - .num_columns(2) - .show(ui, |ui| { - re_ui.grid_left_hand_label(ui, "Columns"); - ui.vertical(|ui| { - for (timeline, visible) in col_timelines { - ui.checkbox(visible, timeline.name().to_string()); - } - ui.checkbox(col_entity_path, "Entity path"); - ui.checkbox(col_log_level, "Log level"); - }); - ui.end_row(); + re_ui.selection_grid(ui, "log_config").show(ui, |ui| { + re_ui.grid_left_hand_label(ui, "Columns"); + ui.vertical(|ui| { + for (timeline, visible) in col_timelines { + ui.checkbox(visible, timeline.name().to_string()); + } + ui.checkbox(col_entity_path, "Entity path"); + ui.checkbox(col_log_level, "Log level"); + }); + ui.end_row(); - re_ui.grid_left_hand_label(ui, "Entity Filter"); - ui.vertical(|ui| { - for (entity_path, visible) in row_entity_paths { - ui.checkbox(visible, &entity_path.to_string()); - } - }); - ui.end_row(); + re_ui.grid_left_hand_label(ui, "Entity Filter"); + ui.vertical(|ui| { + for (entity_path, visible) in row_entity_paths { + ui.checkbox(visible, &entity_path.to_string()); + } + }); + ui.end_row(); - re_ui.grid_left_hand_label(ui, "Level Filter"); - ui.vertical(|ui| { - for (log_level, visible) in row_log_levels { - ui.checkbox(visible, level_to_rich_text(ui, log_level)); - } - }); - ui.end_row(); + re_ui.grid_left_hand_label(ui, "Level Filter"); + ui.vertical(|ui| { + for (log_level, visible) in row_log_levels { + ui.checkbox(visible, level_to_rich_text(ui, log_level)); + } + }); + ui.end_row(); - re_ui.grid_left_hand_label(ui, "Text style"); - ui.vertical(|ui| { - ui.radio_value(&mut self.monospace, false, "Proportional"); - ui.radio_value(&mut self.monospace, true, "Monospace"); - }); - ui.end_row(); + re_ui.grid_left_hand_label(ui, "Text style"); + ui.vertical(|ui| { + ui.radio_value(&mut self.monospace, false, "Proportional"); + ui.radio_value(&mut self.monospace, true, "Monospace"); }); + ui.end_row(); + }); } }