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

Labels for 3D objects have now a color can now be selected & hovered #1438

Merged
merged 3 commits into from
Feb 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"cSpell.words": [
"andreas",
"bbox",
"emath",
"framebuffer",
"hoverable",
"Keypoint",
Expand Down
2 changes: 1 addition & 1 deletion crates/re_renderer/shader/point_cloud.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ fn fs_main(in: VertexOut) -> @location(0) Vec4 {

// Sphere intersection with anti-aliasing as described by Iq here
// https://www.shadertoy.com/view/MsSSWV
// (but rearranged and labled to it's easier to understand!)
// (but rearranged and labeled to it's easier to understand!)
let d = sphere_distance(ray, in.point_center, in.radius);
let smallest_distance_to_sphere = d.x;
let closest_ray_dist = d.y;
Expand Down
4 changes: 1 addition & 3 deletions crates/re_viewer/src/ui/view_spatial/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ mod ui_2d;
mod ui_3d;
mod ui_renderer_bridge;

pub use self::scene::{
Image, Label2D, Label2DTarget, Label3D, MeshSource, MeshSourceData, SceneSpatial,
};
pub use self::scene::{Image, MeshSource, MeshSourceData, SceneSpatial, UiLabel, UiLabelTarget};
pub use self::space_camera_3d::SpaceCamera3D;
pub use ui::{SpatialNavigationMode, ViewSpatialState};
pub use ui_2d::view_2d;
Expand Down
26 changes: 10 additions & 16 deletions crates/re_viewer/src/ui/view_spatial/scene/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,38 +74,32 @@ pub struct Image {
pub annotations: Arc<Annotations>,
}

pub enum Label2DTarget {
pub enum UiLabelTarget {
/// Labels a given rect (in scene coordinates)
Rect(egui::Rect),

/// Labels a given point (in scene coordinates)
Point(egui::Pos2),
Point2D(egui::Pos2),

/// A point in space.
Position3D(glam::Vec3),
}

// TODO(andreas): Merge Label2D and Label3D
pub struct Label2D {
pub struct UiLabel {
pub text: String,
pub color: Color32,

/// The shape being labeled.
pub target: Label2DTarget,
/// The shape/position being labeled.
pub target: UiLabelTarget,

/// What is hovered if this label is hovered.
pub labled_instance: InstancePathHash,
}

pub struct Label3D {
pub(crate) text: String,

/// Origin of the label
pub(crate) origin: glam::Vec3,
pub labeled_instance: InstancePathHash,
}

/// Data necessary to setup the ui [`SceneSpatial`] but of no interest to `re_renderer`.
#[derive(Default)]
pub struct SceneSpatialUiData {
pub labels_3d: Vec<Label3D>,
pub labels_2d: Vec<Label2D>,
pub labels: Vec<UiLabel>,

/// Picking any any of these rects cause the referred instance to be hovered.
/// Only use this for 2d overlays!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
ui::{
scene::SceneQuery,
view_spatial::{
scene::scene_part::instance_path_hash_for_picking, Label2D, Label2DTarget, SceneSpatial,
scene::scene_part::instance_path_hash_for_picking, SceneSpatial, UiLabel, UiLabelTarget,
},
DefaultColor,
},
Expand Down Expand Up @@ -71,14 +71,14 @@ impl Boxes2DPart {
.user_data(instance_path_hash);

if let Some(label) = label {
scene.ui.labels_2d.push(Label2D {
scene.ui.labels.push(UiLabel {
text: label,
color,
target: Label2DTarget::Rect(egui::Rect::from_min_size(
target: UiLabelTarget::Rect(egui::Rect::from_min_size(
rect.top_left_corner().into(),
egui::vec2(rect.width(), rect.height()),
)),
labled_instance: instance_path_hash,
labeled_instance: instance_path_hash,
});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
misc::{OptionalSpaceViewEntityHighlight, SpaceViewHighlights, TransformCache, ViewerContext},
ui::{
scene::SceneQuery,
view_spatial::{Label3D, SceneSpatial},
view_spatial::{SceneSpatial, UiLabel, UiLabelTarget},
DefaultColor,
},
};
Expand Down Expand Up @@ -82,9 +82,11 @@ impl Boxes3DPart {
.user_data(instance_hash);

if let Some(label) = annotation_info.label(label.as_ref().map(|s| &s.0)) {
scene.ui.labels_3d.push(Label3D {
scene.ui.labels.push(UiLabel {
text: label,
origin: world_from_obj.transform_point3(tran),
target: UiLabelTarget::Position3D(world_from_obj.transform_point3(tran)),
color,
labeled_instance: instance_hash,
});
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
misc::{OptionalSpaceViewEntityHighlight, SpaceViewHighlights, TransformCache, ViewerContext},
ui::{
scene::SceneQuery,
view_spatial::{scene::Keypoints, Label2D, Label2DTarget, SceneSpatial},
view_spatial::{scene::Keypoints, SceneSpatial, UiLabel, UiLabelTarget},
DefaultColor,
},
};
Expand Down Expand Up @@ -101,11 +101,11 @@ impl Points2DPart {

if let Some(label) = label {
if label_batch.len() < max_num_labels {
label_batch.push(Label2D {
label_batch.push(UiLabel {
text: label,
color,
target: Label2DTarget::Point(egui::pos2(pos.x, pos.y)),
labled_instance: instance_hash,
target: UiLabelTarget::Point2D(egui::pos2(pos.x, pos.y)),
labeled_instance: instance_hash,
});
}
}
Expand All @@ -115,7 +115,7 @@ impl Points2DPart {
drop(point_batch); // Drop batch so we have access to the scene again (batches need to be dropped before starting new ones).

if label_batch.len() < max_num_labels {
scene.ui.labels_2d.extend(label_batch.into_iter());
scene.ui.labels.extend(label_batch.into_iter());
}

// Generate keypoint connections if any.
Expand Down
52 changes: 36 additions & 16 deletions crates/re_viewer/src/ui/view_spatial/scene/scene_part/points3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::sync::Arc;
use ahash::{HashMap, HashMapExt};
use glam::Mat4;

use re_data_store::{EntityPath, EntityProperties};
use re_data_store::{EntityPath, EntityProperties, InstancePathHash};
use re_log_types::{
component_types::{ClassId, ColorRGBA, InstanceKey, KeypointId, Label, Point3D, Radius},
msg_bundle::Component,
Expand All @@ -21,7 +21,7 @@ use crate::{
scene::SceneQuery,
view_spatial::{
scene::{scene_part::instance_path_hash_for_picking, Keypoints},
Label3D, SceneSpatial,
SceneSpatial, UiLabel, UiLabelTarget,
},
Annotations, DefaultColor,
},
Expand Down Expand Up @@ -117,24 +117,34 @@ impl Points3DPart {

fn process_labels<'a>(
entity_view: &'a EntityView<Point3D>,
instance_path_hashes: &'a [InstancePathHash],
colors: &'a [egui::Color32],
annotation_infos: &'a [ResolvedAnnotationInfo],
world_from_obj: Mat4,
) -> Result<impl Iterator<Item = Label3D> + 'a, QueryError> {
) -> Result<impl Iterator<Item = UiLabel> + 'a, QueryError> {
let labels = itertools::izip!(
annotation_infos.iter(),
entity_view.iter_primary()?,
entity_view.iter_component::<Label>()?
entity_view.iter_component::<Label>()?,
colors,
instance_path_hashes,
)
.filter_map(move |(annotation_info, point, label)| {
let label = annotation_info.label(label.map(|l| l.0).as_ref());
match (point, label) {
(Some(point), Some(label)) => Some(Label3D {
text: label,
origin: world_from_obj.transform_point3(point.into()),
}),
_ => None,
}
});
.filter_map(
move |(annotation_info, point, label, color, labeled_instance)| {
let label = annotation_info.label(label.map(|l| l.0).as_ref());
match (point, label) {
(Some(point), Some(label)) => Some(UiLabel {
text: label,
color: *color,
target: UiLabelTarget::Position3D(
world_from_obj.transform_point3(point.into()),
),
labeled_instance: *labeled_instance,
}),
_ => None,
}
},
);
Ok(labels)
}

Expand Down Expand Up @@ -192,10 +202,20 @@ impl Points3DPart {
let colors = Self::process_colors(entity_view, ent_path, &highlights, &annotation_infos)?;

let radii = Self::process_radii(entity_view, &highlights)?;
let labels = Self::process_labels(entity_view, &annotation_infos, world_from_obj)?;

if show_labels && instance_path_hashes.len() <= self.max_labels {
scene.ui.labels_3d.extend(labels);
// Max labels is small enough that we can afford iterating on the colors again.
let colors =
Self::process_colors(entity_view, ent_path, &highlights, &annotation_infos)?
.collect::<Vec<_>>();

scene.ui.labels.extend(Self::process_labels(
entity_view,
&instance_path_hashes,
&colors,
&annotation_infos,
world_from_obj,
)?);
}

scene
Expand Down
105 changes: 101 additions & 4 deletions crates/re_viewer/src/ui/view_spatial/ui.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
use eframe::epaint::text::TextWrapping;
use re_data_store::{query_latest_single, EditableAutoValue, EntityPath};
use re_format::format_f32;

use egui::{NumExt, WidgetText};
use macaw::BoundingBox;

use crate::{
misc::{space_info::query_view_coordinates, SpaceViewHighlights, ViewerContext},
ui::{data_blueprint::DataBlueprintTree, SpaceViewId},
misc::{
space_info::query_view_coordinates, SelectionHighlight, SpaceViewHighlights, ViewerContext,
},
ui::{data_blueprint::DataBlueprintTree, view_spatial::UiLabelTarget, SpaceViewId},
};

use super::{ui_2d::View2DState, ui_3d::View3DState, SceneSpatial, SpaceSpecs};
use super::{
eye::Eye, scene::SceneSpatialUiData, ui_2d::View2DState, ui_3d::View3DState, SceneSpatial,
SpaceSpecs,
};

/// Describes how the scene is navigated, determining if it is a 2D or 3D experience.
#[derive(Clone, Copy, Default, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
Expand Down Expand Up @@ -344,7 +350,7 @@ impl ViewSpatialState {
let coordinates =
query_view_coordinates(&ctx.log_db.entity_db, space, &ctx.current_query());
self.state_3d.space_specs = SpaceSpecs::from_view_coordinates(coordinates);
super::view_3d(ctx, ui, self, space, space_view_id, scene);
super::view_3d(ctx, ui, self, space, space_view_id, scene, highlights);
}
SpatialNavigationMode::TwoD => {
let scene_rect_accum = egui::Rect::from_min_max(
Expand Down Expand Up @@ -459,3 +465,94 @@ fn axis_name(axis: Option<glam::Vec3>) -> String {
"—".to_owned()
}
}

pub fn create_labels(
scene_ui: &mut SceneSpatialUiData,
ui_from_space2d: egui::emath::RectTransform,
space2d_from_ui: egui::emath::RectTransform,
eye3d: &Eye,
parent_ui: &mut egui::Ui,
highlights: &SpaceViewHighlights,
) -> Vec<egui::Shape> {
crate::profile_function!();

let mut label_shapes = Vec::with_capacity(scene_ui.labels.len() * 2);

let ui_from_world_3d = eye3d.ui_from_world(ui_from_space2d.to());

for label in &scene_ui.labels {
let (wrap_width, text_anchor_pos) = match label.target {
UiLabelTarget::Rect(rect) => {
let rect_in_ui = ui_from_space2d.transform_rect(rect);
(
// Place the text centered below the rect
(rect_in_ui.width() - 4.0).at_least(60.0),
rect_in_ui.center_bottom() + egui::vec2(0.0, 3.0),
)
}
UiLabelTarget::Point2D(pos) => {
let pos_in_ui = ui_from_space2d.transform_pos(pos);
(f32::INFINITY, pos_in_ui + egui::vec2(0.0, 3.0))
}
UiLabelTarget::Position3D(pos) => {
let pos_in_ui = ui_from_world_3d * pos.extend(1.0);
if pos_in_ui.w <= 0.0 {
continue; // behind camera
}
let pos_in_ui = pos_in_ui / pos_in_ui.w;
(f32::INFINITY, egui::pos2(pos_in_ui.x, pos_in_ui.y))
}
};

let font_id = egui::TextStyle::Body.resolve(parent_ui.style());
let galley = parent_ui.fonts(|fonts| {
fonts.layout_job({
egui::text::LayoutJob {
sections: vec![egui::text::LayoutSection {
leading_space: 0.0,
byte_range: 0..label.text.len(),
format: egui::TextFormat::simple(font_id, label.color),
}],
text: label.text.clone(),
wrap: TextWrapping {
max_width: wrap_width,
..Default::default()
},
break_on_newline: true,
halign: egui::Align::Center,
..Default::default()
}
})
});

let text_rect = egui::Align2::CENTER_TOP
.anchor_rect(egui::Rect::from_min_size(text_anchor_pos, galley.size()));
let bg_rect = text_rect.expand2(egui::vec2(4.0, 2.0));

let hightlight = highlights
.entity_highlight(label.labeled_instance.entity_path_hash)
.index_highlight(label.labeled_instance.instance_key);
let fill_color = match hightlight.hover {
crate::misc::HoverHighlight::None => match hightlight.selection {
SelectionHighlight::None => parent_ui.style().visuals.widgets.inactive.bg_fill,
SelectionHighlight::SiblingSelection => {
parent_ui.style().visuals.widgets.active.bg_fill
}
SelectionHighlight::Selection => parent_ui.style().visuals.widgets.active.bg_fill,
},
crate::misc::HoverHighlight::Hovered => {
parent_ui.style().visuals.widgets.hovered.bg_fill
}
};

label_shapes.push(egui::Shape::rect_filled(bg_rect, 3.0, fill_color));
label_shapes.push(egui::Shape::galley(text_rect.center_top(), galley));

scene_ui.pickable_ui_rects.push((
space2d_from_ui.transform_rect(bg_rect),
label.labeled_instance,
));
}

label_shapes
}
Loading