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

Improve hovered order in 2D views #8405

Merged
merged 5 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
43 changes: 28 additions & 15 deletions crates/viewer/re_view_spatial/src/picking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ pub struct PickingRayHit {

#[derive(Clone, PartialEq)]
pub struct PickingResult {
/// Picking ray hits. NOT sorted by distance but rather by source of picking.
/// Picking ray hits.
///
/// The hits are ROUGHLY sorted front-to-back, i.e. closest hits are first.
emilk marked this conversation as resolved.
Show resolved Hide resolved
///
/// Typically there is only one hit, but there might be several if there are transparent objects
/// or "aggressive" objects like 2D images which we always want to pick, even if they're in the background.
Expand Down Expand Up @@ -132,29 +134,31 @@ impl PickingContext {
self,
previous_picking_result,
);
let mut rect_hits = picking_textured_rects(self, images);
rect_hits.sort_by(|a, b| b.depth_offset.cmp(&a.depth_offset));
let ui_rect_hits = picking_ui_rects(self, ui_rects);

let mut image_hits = picking_textured_rects(self, images);
image_hits.sort_by(|a, b| b.depth_offset.cmp(&a.depth_offset));

let ui_hits = picking_ui_rects(self, ui_rects);

let mut hits = Vec::new();

// Start with gpu based picking as baseline. This is our prime source of picking information.
//
// ..unless the same object got also picked as part of a textured rect.
// Textured rect picks also know where on the rect, making this the better source!
// Note that whenever this happens, it means that the same object path has a textured rect and something else
// e.g. a camera.
if let Some(gpu_pick) = gpu_pick {
if rect_hits.iter().all(|rect_hit| {
rect_hit.instance_path_hash.entity_path_hash
!= gpu_pick.instance_path_hash.entity_path_hash
}) {
// ..unless the same object got also picked as part of a textured rect.
// Textured rect picks also know where on the rect they hit, making this the better source!
// Note that whenever this happens, it means that the same object path has a textured rect and something else
// e.g. a camera.
let has_image_hit_on_gpu_pick = image_hits.iter().any(|image_hit| {
image_hit.instance_path_hash.entity_path_hash
== gpu_pick.instance_path_hash.entity_path_hash
});
if !has_image_hit_on_gpu_pick {
hits.push(gpu_pick);
}
}

// We never throw away any textured rects, even if they're behind other objects.
hits.extend(rect_hits);
hits.extend(image_hits);

// UI rects are overlaid on top, but we don't let them hide other picking results either.
// Give any other previous hits precedence.
Expand All @@ -163,11 +167,20 @@ impl PickingContext {
.map(|prev_hit| prev_hit.instance_path_hash)
.collect();
hits.extend(
ui_rect_hits
ui_hits
.into_iter()
.filter(|ui_hit| !previously_hit_objects.contains(&ui_hit.instance_path_hash)),
);

// Re-order so that the closest hits are first:
hits.sort_by_key(|hit| match hit.hit_type {
PickingHitType::GuiOverlay => 0, // GUI is closest, so always goes on top

PickingHitType::GpuPickingResult => 1,

PickingHitType::TexturedRect => 2, // Images are usually behind other things (e.g. an image is behind a bounding rectangle in a 2D view), so we put these last (furthest last)
});

PickingResult { hits }
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/viewer/re_view_spatial/src/picking_ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ pub fn picking(
// Depth at pointer used for projecting rays from a hovered 2D view to corresponding 3D view(s).
// TODO(#1818): Depth at pointer only works for depth images so far.
let mut depth_at_pointer = None;

// We iterate front-to-back, putting foreground hits on top, like layers in Photoshop:
for (hit_idx, hit) in picking_result.hits.iter().enumerate() {
let Some(mut instance_path) = hit.instance_path_hash.resolve(ctx.recording()) else {
// Entity no longer exists in db.
Expand Down
Loading