Skip to content

Commit 409dcd8

Browse files
authored
Option to show scene bounding box (#1770)
* Include depth clouds in bounding box calculation * Don't wrap text when showing bbox in ui * Handle projective transforms * Nicer selection view: don't wrap second column too early * Add checkbox to show the scene bounding box
1 parent 679e245 commit 409dcd8

File tree

6 files changed

+94
-36
lines changed

6 files changed

+94
-36
lines changed

crates/re_renderer/src/renderer/depth_cloud.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,37 @@ pub struct DepthCloud {
166166
pub outline_mask_id: OutlineMaskPreference,
167167
}
168168

169+
impl DepthCloud {
170+
/// World-space bounding-box.
171+
pub fn bbox(&self) -> macaw::BoundingBox {
172+
let max_depth = self.max_depth_in_world;
173+
let w = self.depth_dimensions.x as f32;
174+
let h = self.depth_dimensions.y as f32;
175+
let corners = [
176+
glam::Vec3::ZERO, // camera origin
177+
glam::Vec3::new(0.0, 0.0, max_depth),
178+
glam::Vec3::new(0.0, h, max_depth),
179+
glam::Vec3::new(w, 0.0, max_depth),
180+
glam::Vec3::new(w, h, max_depth),
181+
];
182+
183+
let intrinsics = self.depth_camera_intrinsics;
184+
let focal_length = glam::vec2(intrinsics.col(0).x, intrinsics.col(1).y);
185+
let offset = intrinsics.col(2).truncate();
186+
187+
let mut bbox = macaw::BoundingBox::nothing();
188+
189+
for corner in corners {
190+
let depth = corner.z;
191+
let pos_in_obj = ((corner.truncate() - offset) * depth / focal_length).extend(depth);
192+
let pos_in_world = self.world_from_obj.project_point3(pos_in_obj);
193+
bbox.extend(pos_in_world);
194+
}
195+
196+
bbox
197+
}
198+
}
199+
169200
pub struct DepthClouds {
170201
pub clouds: Vec<DepthCloud>,
171202
pub radius_boost_in_ui_points_for_outlines: f32,

crates/re_ui/src/lib.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -480,11 +480,13 @@ impl ReUi {
480480
.inner
481481
}
482482

483-
/// Grid to be used in selection view.
483+
/// Two-column grid to be used in selection view.
484484
#[allow(clippy::unused_self)]
485485
pub fn selection_grid(&self, ui: &mut egui::Ui, id: &str) -> egui::Grid {
486486
// Spread rows a bit to make it easier to see the groupings
487-
egui::Grid::new(id).spacing(ui.style().spacing.item_spacing + egui::vec2(0.0, 8.0))
487+
egui::Grid::new(id)
488+
.num_columns(2)
489+
.spacing(ui.style().spacing.item_spacing + egui::vec2(0.0, 8.0))
488490
}
489491

490492
/// Draws a shadow into the given rect with the shadow direction given from dark to light

crates/re_viewer/src/ui/view_spatial/scene/primitives.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ impl SceneSpatialPrimitives {
9393
line_strips,
9494
points,
9595
meshes,
96-
depth_clouds: _, // no bbox for depth clouds
96+
depth_clouds,
9797
any_outlines: _,
9898
} = self;
9999

@@ -133,6 +133,10 @@ impl SceneSpatialPrimitives {
133133
*bounding_box =
134134
bounding_box.union(mesh.mesh.bbox().transform_affine3(&mesh.world_from_mesh));
135135
}
136+
137+
for cloud in &depth_clouds.clouds {
138+
*bounding_box = bounding_box.union(cloud.bbox());
139+
}
136140
}
137141

138142
pub fn mesh_instances(&self) -> Vec<MeshInstance> {

crates/re_viewer/src/ui/view_spatial/ui.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,13 +347,15 @@ impl ViewSpatialState {
347347
});
348348
});
349349
ui.checkbox(&mut self.state_3d.show_axes, "Show origin axes").on_hover_text("Show X-Y-Z axes");
350+
ui.checkbox(&mut self.state_3d.show_bbox, "Show bounding box").on_hover_text("Show the current scene bounding box");
350351
});
351352
ui.end_row();
352353
}
353354

354355
ctx.re_ui.grid_left_hand_label(ui, "Bounding box")
355356
.on_hover_text("The bounding box encompassing all Entities in the view right now.");
356357
ui.vertical(|ui| {
358+
ui.style_mut().wrap = Some(false);
357359
let BoundingBox { min, max } = self.scene_bbox;
358360
ui.label(format!(
359361
"x [{} - {}]",

crates/re_viewer/src/ui/view_spatial/ui_3d.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ pub struct View3DState {
5656
// options:
5757
pub spin: bool,
5858
pub show_axes: bool,
59+
pub show_bbox: bool,
5960

6061
#[serde(skip)]
6162
last_eye_interact_time: f64,
@@ -77,6 +78,7 @@ impl Default for View3DState {
7778
hovered_point: Default::default(),
7879
spin: false,
7980
show_axes: false,
81+
show_bbox: false,
8082
last_eye_interact_time: f64::NEG_INFINITY,
8183
space_specs: Default::default(),
8284
space_camera: Default::default(),
@@ -549,6 +551,26 @@ pub fn view_3d(
549551
);
550552
}
551553

554+
if state.state_3d.show_bbox {
555+
let bbox = scene.primitives.bounding_box();
556+
if bbox.is_something() && bbox.is_finite() {
557+
let scale = bbox.size();
558+
let translation = bbox.center();
559+
let bbox_from_unit_cube = glam::Affine3A::from_scale_rotation_translation(
560+
scale,
561+
Default::default(),
562+
translation,
563+
);
564+
scene
565+
.primitives
566+
.line_strips
567+
.batch("scene_bbox")
568+
.add_box_outline(bbox_from_unit_cube)
569+
.radius(Size::AUTO)
570+
.color(egui::Color32::WHITE);
571+
}
572+
}
573+
552574
{
553575
let orbit_center_alpha = egui::remap_clamp(
554576
ui.input(|i| i.time) - state.state_3d.last_eye_interact_time,

crates/re_viewer/src/ui/view_text/ui.rs

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -37,43 +37,40 @@ impl ViewTextState {
3737
row_log_levels,
3838
} = &mut self.filters;
3939

40-
re_ui
41-
.selection_grid(ui, "log_config")
42-
.num_columns(2)
43-
.show(ui, |ui| {
44-
re_ui.grid_left_hand_label(ui, "Columns");
45-
ui.vertical(|ui| {
46-
for (timeline, visible) in col_timelines {
47-
ui.checkbox(visible, timeline.name().to_string());
48-
}
49-
ui.checkbox(col_entity_path, "Entity path");
50-
ui.checkbox(col_log_level, "Log level");
51-
});
52-
ui.end_row();
40+
re_ui.selection_grid(ui, "log_config").show(ui, |ui| {
41+
re_ui.grid_left_hand_label(ui, "Columns");
42+
ui.vertical(|ui| {
43+
for (timeline, visible) in col_timelines {
44+
ui.checkbox(visible, timeline.name().to_string());
45+
}
46+
ui.checkbox(col_entity_path, "Entity path");
47+
ui.checkbox(col_log_level, "Log level");
48+
});
49+
ui.end_row();
5350

54-
re_ui.grid_left_hand_label(ui, "Entity Filter");
55-
ui.vertical(|ui| {
56-
for (entity_path, visible) in row_entity_paths {
57-
ui.checkbox(visible, &entity_path.to_string());
58-
}
59-
});
60-
ui.end_row();
51+
re_ui.grid_left_hand_label(ui, "Entity Filter");
52+
ui.vertical(|ui| {
53+
for (entity_path, visible) in row_entity_paths {
54+
ui.checkbox(visible, &entity_path.to_string());
55+
}
56+
});
57+
ui.end_row();
6158

62-
re_ui.grid_left_hand_label(ui, "Level Filter");
63-
ui.vertical(|ui| {
64-
for (log_level, visible) in row_log_levels {
65-
ui.checkbox(visible, level_to_rich_text(ui, log_level));
66-
}
67-
});
68-
ui.end_row();
59+
re_ui.grid_left_hand_label(ui, "Level Filter");
60+
ui.vertical(|ui| {
61+
for (log_level, visible) in row_log_levels {
62+
ui.checkbox(visible, level_to_rich_text(ui, log_level));
63+
}
64+
});
65+
ui.end_row();
6966

70-
re_ui.grid_left_hand_label(ui, "Text style");
71-
ui.vertical(|ui| {
72-
ui.radio_value(&mut self.monospace, false, "Proportional");
73-
ui.radio_value(&mut self.monospace, true, "Monospace");
74-
});
75-
ui.end_row();
67+
re_ui.grid_left_hand_label(ui, "Text style");
68+
ui.vertical(|ui| {
69+
ui.radio_value(&mut self.monospace, false, "Proportional");
70+
ui.radio_value(&mut self.monospace, true, "Monospace");
7671
});
72+
ui.end_row();
73+
});
7774
}
7875
}
7976

0 commit comments

Comments
 (0)