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

Consistent transform visualization for all entities with transforms #2577

Merged
merged 26 commits into from
Jul 17, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
2b0133d
show transform gizmo for each transform
Wumpf Jun 30, 2023
be02be8
change style of axis line - thinner and no gradient
Wumpf Jun 30, 2023
4c8cd94
object properties and heuristics for transform arrow showing and size
Wumpf Jun 30, 2023
95989ce
transform arrow outlines and identification as 3d object
Wumpf Jun 30, 2023
4295c16
Fix clicking object with single instance (of every component) selecti…
Wumpf Jun 30, 2023
7c8b5ee
tweaks. make transform sample from api_demo (somewhat) usable
Wumpf Jun 30, 2023
e70bd6a
fix scene_box_accum not recovering if it is non-finite at any point
Wumpf Jun 30, 2023
de763c2
Merge remote-tracking branch 'origin/main' into andreas/transform-gizmos
Wumpf Jul 10, 2023
586251e
arrow head sizes are now configurable
Wumpf Jul 10, 2023
30df079
minimum triangle cap size
Wumpf Jul 10, 2023
a7a8b7a
nicer transform3d arrows
Wumpf Jul 10, 2023
4c940d6
fix warnings
Wumpf Jul 10, 2023
a09fe93
fix spelling
Wumpf Jul 10, 2023
80ee13b
Merge remote-tracking branch 'origin/main' into andreas/transform-gizmos
Wumpf Jul 11, 2023
4f68572
comment, naming and string fixes/clarifications
Wumpf Jul 11, 2023
86b78b6
[skip ci] Revert partial/incomplete selection changes that were part …
Wumpf Jul 11, 2023
f638057
Merge remote-tracking branch 'origin/main' into andreas/transform-gizmos
Wumpf Jul 13, 2023
64252ac
Merge remote-tracking branch 'origin/main' into andreas/transform-gizmos
Wumpf Jul 17, 2023
89a052c
reenable origin-axis
Wumpf Jul 17, 2023
cbfc6c8
comment and naming fixes
Wumpf Jul 17, 2023
0c9f2ee
simplify code in transform_context
Wumpf Jul 17, 2023
b2623d4
don't re-use auto_size_world_heuristic for default transform arrow size
Wumpf Jul 17, 2023
40c059f
fix bad merge
Wumpf Jul 17, 2023
9fa35df
allow double clicking show-arrow checkbox
Wumpf Jul 17, 2023
a927cf5
better tooltip
Wumpf Jul 17, 2023
3f04b0c
adjust transform arrow length label
Wumpf Jul 17, 2023
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
18 changes: 18 additions & 0 deletions crates/re_data_store/src/entity_properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ pub struct EntityProperties {

/// Used to scale the radii of the points in the resulting point cloud.
pub backproject_radius_scale: EditableAutoValue<f32>,

/// Whether to show the 3D transform visualization at all.
pub transform_3d_visible: EditableAutoValue<bool>,

/// The length of the arrows visualizing a transform.
Wumpf marked this conversation as resolved.
Show resolved Hide resolved
pub transform_3d_size: EditableAutoValue<f32>,
}

#[cfg(feature = "serde")]
Expand All @@ -96,6 +102,8 @@ impl Default for EntityProperties {
backproject_depth: EditableAutoValue::Auto(true),
depth_from_world_scale: EditableAutoValue::default(),
backproject_radius_scale: EditableAutoValue::Auto(1.0),
transform_3d_visible: EditableAutoValue::Auto(false),
transform_3d_size: EditableAutoValue::Auto(1.0),
}
}
}
Expand Down Expand Up @@ -125,6 +133,12 @@ impl EntityProperties {
.backproject_radius_scale
.or(&child.backproject_radius_scale)
.clone(),

transform_3d_visible: self
.transform_3d_visible
.or(&child.transform_3d_visible)
.clone(),
transform_3d_size: self.transform_3d_size.or(&child.transform_3d_size).clone(),
}
}

Expand All @@ -139,6 +153,8 @@ impl EntityProperties {
backproject_depth,
depth_from_world_scale,
backproject_radius_scale,
transform_3d_visible,
transform_3d_size,
} = self;

visible != &other.visible
Expand All @@ -149,6 +165,8 @@ impl EntityProperties {
|| backproject_depth.has_edits(&other.backproject_depth)
|| depth_from_world_scale.has_edits(&other.depth_from_world_scale)
|| backproject_radius_scale.has_edits(&other.backproject_radius_scale)
|| transform_3d_visible.has_edits(&other.transform_3d_visible)
|| transform_3d_size.has_edits(&other.transform_3d_size)
}
}

Expand Down
75 changes: 62 additions & 13 deletions crates/re_data_ui/src/item_ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

use re_data_store::InstancePath;
use re_log_types::{ComponentPath, EntityPath, TimeInt, Timeline};
use re_query::get_component_with_instances;
use re_viewer_context::{
DataBlueprintGroupHandle, HoverHighlight, Item, SelectionState, SpaceViewId, UiVerbosity,
ViewerContext,
DataBlueprintGroupHandle, HoverHighlight, Item, SpaceViewId, UiVerbosity, ViewerContext,
};

use super::DataUi;
Expand Down Expand Up @@ -109,7 +109,7 @@ pub fn instance_path_button_to(
instance_path.data_ui(ctx, ui, UiVerbosity::Reduced, &ctx.current_query());
});

cursor_interact_with_selectable(ctx.selection_state_mut(), response, item)
cursor_interact_with_selectable(ctx, response, item)
}

/// Show a component path and make it selectable.
Expand All @@ -135,7 +135,7 @@ pub fn component_path_button_to(
) -> egui::Response {
let item = Item::ComponentPath(component_path.clone());
let response = ui.selectable_label(ctx.selection().contains(&item), text);
cursor_interact_with_selectable(ctx.selection_state_mut(), response, item)
cursor_interact_with_selectable(ctx, response, item)
}

pub fn data_blueprint_group_button_to(
Expand All @@ -155,7 +155,7 @@ pub fn data_blueprint_group_button_to(
ctx.selection().contains(&item),
)
.on_hover_text("Group");
cursor_interact_with_selectable(ctx.selection_state_mut(), response, item)
cursor_interact_with_selectable(ctx, response, item)
}

pub fn data_blueprint_button_to(
Expand All @@ -176,7 +176,7 @@ pub fn data_blueprint_button_to(
ui.label(format!("Path: {entity_path}"));
entity_path.data_ui(ctx, ui, UiVerbosity::Reduced, &ctx.current_query());
});
cursor_interact_with_selectable(ctx.selection_state_mut(), response, item)
cursor_interact_with_selectable(ctx, response, item)
}

pub fn time_button(
Expand Down Expand Up @@ -225,14 +225,14 @@ pub fn timeline_button_to(

// TODO(andreas): Move elsewhere, this is not directly part of the item_ui.
pub fn cursor_interact_with_selectable(
selection_state: &mut SelectionState,
ctx: &mut ViewerContext<'_>,
response: egui::Response,
item: Item,
) -> egui::Response {
let is_item_hovered =
selection_state.highlight_for_ui_element(&item) == HoverHighlight::Hovered;
ctx.selection_state().highlight_for_ui_element(&item) == HoverHighlight::Hovered;

select_hovered_on_click(&response, selection_state, &[item]);
select_hovered_on_click(ctx, &response, &[item]);
// TODO(andreas): How to deal with shift click for selecting ranges?

if is_item_hovered {
Expand All @@ -244,19 +244,68 @@ pub fn cursor_interact_with_selectable(

// TODO(andreas): Move elsewhere, this is not directly part of the item_ui.
pub fn select_hovered_on_click(
ctx: &mut ViewerContext<'_>,
response: &egui::Response,
selection_state: &mut SelectionState,
items: &[Item],
) {
if response.hovered() {
selection_state.set_hovered(items.iter().cloned());
ctx.selection_state_mut().set_hovered(items.iter().cloned());
}

if response.clicked() {
// Resolve to entity path if there's only a single instance.
let mut selected_items = Vec::new();
for item in items {
match item {
Item::InstancePath(space_view, instance) => {
selected_items.push(Item::InstancePath(
*space_view,
resolve_to_entity_if_single_instance(ctx, instance),
));
}
Item::ComponentPath(_) | Item::SpaceView(_) | Item::DataBlueprintGroup(_, _) => {
selected_items.push(item.clone());
}
}
}

if response.ctx.input(|i| i.modifiers.command) {
selection_state.toggle_selection(items.to_vec());
ctx.selection_state_mut().toggle_selection(selected_items);
} else {
selection_state.set_selection(items.iter().cloned());
ctx.selection_state_mut()
.set_selection(selected_items.into_iter());
}
}
}

fn resolve_to_entity_if_single_instance(
Wumpf marked this conversation as resolved.
Show resolved Hide resolved
ctx: &ViewerContext<'_>,
instance: &InstancePath,
) -> InstancePath {
if instance.instance_key.0 == 0 {
let Some(components) = ctx
.store_db
.store()
.all_components(&ctx.current_query().timeline, &instance.entity_path) else {
// No components at all, return splatted entity.
return InstancePath::entity_splat(instance.entity_path.clone());
};
for component in components {
if let Some((_row_id, instances)) = get_component_with_instances(
ctx.store_db.store(),
&ctx.current_query(),
&instance.entity_path,
component,
) {
if instances.len() > 1 {
return instance.clone();
}
}
}

// All instances had only a single element or less, resolve to splatted entity.
return InstancePath::entity_splat(instance.entity_path.clone());
}

instance.clone()
}
24 changes: 23 additions & 1 deletion crates/re_renderer/examples/2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,28 @@ impl framework::Example for Render2D {
}
}

// Lines with non-default arrow heads - long thin arrows.
{
let mut line_batch = line_strip_builder
.batch("larger arrowheads")
.triangle_cap_length_factor(15.0)
.triangle_cap_width_factor(3.0);
for (i, flags) in [
LineStripFlags::FLAG_CAP_START_TRIANGLE | LineStripFlags::FLAG_CAP_END_ROUND,
LineStripFlags::FLAG_CAP_START_ROUND | LineStripFlags::FLAG_CAP_END_TRIANGLE,
LineStripFlags::FLAG_CAP_START_TRIANGLE | LineStripFlags::FLAG_CAP_END_TRIANGLE,
]
.iter()
.enumerate()
{
let y = (i + 1) as f32 * 40.0 + 650.0;
line_batch
.add_segment_2d(glam::vec2(70.0, y), glam::vec2(400.0, y))
.radius(Size::new_scene(5.0))
.flags(*flags);
}
}

// Lines with different kinds of radius
// The first two lines are the same thickness if there no (!) scaling.
// Moving the windows to a high dpi screen makes the second one bigger.
Expand Down Expand Up @@ -170,7 +192,7 @@ impl framework::Example for Render2D {
// Do in individual batches to test depth offset.
{
let num_lines = 20_i16;
let y_range = 700.0..780.0;
let y_range = 800.0..880.0;

// Cycle through which line is on top.
let top_line = ((time.seconds_since_startup() * 6.0) as i16 % (num_lines * 2 - 1)
Expand Down
30 changes: 20 additions & 10 deletions crates/re_renderer/shader/lines.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ struct BatchUniformBuffer {
outline_mask_ids: UVec2,
picking_layer_object_id: UVec2,
depth_offset: f32,
triangle_cap_length_factor: f32,
triangle_cap_width_factor: f32,
};
@group(2) @binding(0)
var<uniform> batch: BatchUniformBuffer;
Expand Down Expand Up @@ -198,7 +200,8 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut {
} else {
quad_dir = pos_data_quad_end.pos - pos_data_quad_begin.pos;
}
quad_dir = normalize(quad_dir);
let quad_length = length(quad_dir);
quad_dir = quad_dir / quad_length;

// Resolve radius.
// (slight inaccuracy: End caps are going to adjust their center_position)
Expand All @@ -211,38 +214,45 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut {
let camera_distance = distance(camera_ray.origin, center_position);
var strip_radius = unresolved_size_to_world(strip_data.unresolved_radius, camera_distance, frame.auto_size_lines);

// If the triangle cap is longer than the quad would be otherwise, we need to stunt it, otherwise we'd get artifacts.
var triangle_cap_length = batch.triangle_cap_length_factor * strip_radius;
let max_triangle_cap_length = quad_length * 0.75; // Having the entire arrow be just triangle head already looks pretty bad, so we're stopping at 75% of the quad length.
let triangle_cap_size_factor = min(1.0, max_triangle_cap_length / triangle_cap_length);
triangle_cap_length *= triangle_cap_size_factor;

// Make space for the end cap if this is either the cap itself or the cap follows right after/before this quad.
if !has_any_flag(strip_data.flags, FLAG_CAP_END_EXTEND_OUTWARDS) &&
(is_end_cap_triangle || (is_at_quad_end && pos_data_current.strip_index != pos_data_quad_after.strip_index)) {
var cap_length =
f32(has_any_flag(strip_data.flags, FLAG_CAP_END_ROUND)) +
f32(has_any_flag(strip_data.flags, FLAG_CAP_END_TRIANGLE)) * 4.0;
center_position -= quad_dir * (cap_length * strip_radius);
f32(has_any_flag(strip_data.flags, FLAG_CAP_END_ROUND)) * strip_radius +
f32(has_any_flag(strip_data.flags, FLAG_CAP_END_TRIANGLE)) * triangle_cap_length;
center_position -= quad_dir * cap_length;
}
if !has_any_flag(strip_data.flags, FLAG_CAP_START_EXTEND_OUTWARDS) &&
(is_start_cap_triangle || (!is_at_quad_end && pos_data_current.strip_index != pos_data_quad_before.strip_index)) {
var cap_length =
f32(has_any_flag(strip_data.flags, FLAG_CAP_START_ROUND)) +
f32(has_any_flag(strip_data.flags, FLAG_CAP_START_TRIANGLE)) * 4.0;
center_position += quad_dir * (cap_length * strip_radius);
f32(has_any_flag(strip_data.flags, FLAG_CAP_START_ROUND)) * strip_radius +
f32(has_any_flag(strip_data.flags, FLAG_CAP_START_TRIANGLE)) * triangle_cap_length;
center_position += quad_dir * cap_length;
}

// Boost radius only now that we subtracted/added the cap length.
// This way we don't get a gap for the outline at the cap.
if draw_data.radius_boost_in_ui_points > 0.0 {
let size_boost = world_size_from_point_size(draw_data.radius_boost_in_ui_points, camera_distance);
strip_radius += size_boost;
triangle_cap_length += size_boost;
// Push out positions as well along the quad dir.
// This is especially important if there's no miters on a line-strip (TODO(#829)),
// as this would enhance gaps between lines otherwise.
center_position += quad_dir * (size_boost * select(-1.0, 1.0, is_at_quad_end));
}

var active_radius = strip_radius;
// If this is a triangle cap, we blow up our ("virtual") quad by twice the size.
// If this is a triangle cap, we blow up our ("virtual") quad by a given factor.
if (is_end_cap_triangle && has_any_flag(strip_data.flags, FLAG_CAP_END_TRIANGLE)) ||
(is_start_cap_triangle && has_any_flag(strip_data.flags, FLAG_CAP_START_TRIANGLE)) {
active_radius *= 2.0;
active_radius *= batch.triangle_cap_width_factor * triangle_cap_size_factor;
}

// Span up the vertex away from the line's axis, orthogonal to the direction to the camera
Expand All @@ -256,7 +266,7 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut {
// and far enough to do rounded caps without any visible clipping.
// There is _some_ clipping, but we can't see it ;)
// If we want to do it properly, we would extend the radius for rounded caps too.
center_position += quad_dir * (strip_radius * 4.0 * select(-1.0, 1.0, is_right_triangle));
center_position += quad_dir * (triangle_cap_length * select(-1.0, 1.0, is_right_triangle));
pos = center_position;
} else {
pos = center_position + (active_radius * top_bottom) * dir_up;
Expand Down
27 changes: 21 additions & 6 deletions crates/re_renderer/src/line_strip_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,7 @@ impl LineStripSeriesBuilder {
pub fn batch(&mut self, label: impl Into<DebugLabel>) -> LineBatchBuilder<'_> {
self.batches.push(LineBatchInfo {
label: label.into(),
world_from_obj: glam::Affine3A::IDENTITY,
line_vertex_count: 0,
overall_outline_mask_ids: OutlineMaskPreference::NONE,
additional_outline_mask_ids_vertex_ranges: Vec::new(),
picking_object_id: PickingLayerObjectId::default(),
depth_offset: 0,
..LineBatchInfo::default()
});

LineBatchBuilder(self)
Expand Down Expand Up @@ -171,6 +166,26 @@ impl<'a> LineBatchBuilder<'a> {
self
}

/// Sets the length factor as multiple of a line's radius applied to all triangle caps in this batch.
///
/// This controls how far the "pointy end" of the triangle/arrow-head extends.
/// (defaults to 4.0)
#[inline]
pub fn triangle_cap_length_factor(mut self, triangle_cap_length_factor: f32) -> Self {
self.batch_mut().triangle_cap_length_factor = triangle_cap_length_factor;
self
}

/// Sets the width factor as multiple of a line's radius applied to all triangle caps in this batch.
///
/// This controls how wide the triangle/arrow-head is orthogonal to the line's direction.
/// (defaults to 2.0)
#[inline]
pub fn triangle_cap_width_factor(mut self, triangle_cap_width_factor: f32) -> Self {
self.batch_mut().triangle_cap_width_factor = triangle_cap_width_factor;
self
}

/// Adds a 3D series of line connected points.
pub fn add_strip(&mut self, points: impl Iterator<Item = glam::Vec3>) -> LineStripBuilder<'_> {
let old_strip_count = self.0.strips.len();
Expand Down
Loading