Skip to content

Commit 111b73c

Browse files
committed
Fix inputs when tracking entity (#11822)
### Related * Fixes regression in #11788 ### What Fixes regression when tracking entity where inputs don't work correctly when tracking an entity. * [x] another round of manual testing on the web viewer build
1 parent 7918db7 commit 111b73c

File tree

3 files changed

+185
-106
lines changed

3 files changed

+185
-106
lines changed

crates/viewer/re_view_spatial/src/eye.rs

Lines changed: 147 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,6 @@ struct ControlEye {
229229
fov_y: Option<f32>,
230230

231231
did_interact: bool,
232-
233-
written_radius: bool,
234232
}
235233

236234
impl ControlEye {
@@ -287,22 +285,13 @@ impl ControlEye {
287285
.0,
288286
);
289287

290-
let has_look_target = eye_property
291-
.component_or_empty::<Position3D>(EyeControls3D::descriptor_look_target().component)?
292-
.is_some();
293-
294-
let has_position = eye_property
295-
.component_or_empty::<Position3D>(EyeControls3D::descriptor_position().component)?
296-
.is_some();
297-
298288
Ok(Self {
299289
pos,
300290
look_target,
301291
kind,
302292
speed,
303293
eye_up,
304294
did_interact: false,
305-
written_radius: has_position && has_look_target,
306295
fov_y,
307296
})
308297
}
@@ -602,7 +591,7 @@ impl ControlEye {
602591
}
603592
}
604593

605-
fn find_camera(space_cameras: &[SpaceCamera3D], needle: &EntityPath) -> Option<Eye> {
594+
pub fn find_camera(space_cameras: &[SpaceCamera3D], needle: &EntityPath) -> Option<Eye> {
606595
let mut found_camera = None;
607596

608597
for camera in space_cameras {
@@ -618,52 +607,6 @@ fn find_camera(space_cameras: &[SpaceCamera3D], needle: &EntityPath) -> Option<E
618607
found_camera.and_then(Eye::from_camera)
619608
}
620609

621-
/// Returns the new center and orbit radius for the eye.
622-
fn entity_target_eye(
623-
entity_path: &EntityPath,
624-
bounding_boxes: &SceneBoundingBoxes,
625-
eye: &mut ControlEye,
626-
) {
627-
// Note that we may want to focus on an _instance_ instead in the future:
628-
// The problem with that is that there may be **many** instances (think point cloud)
629-
// and they may not be consistent over time.
630-
// -> we don't know the bounding box of every instance (right now)
631-
// -> tracking instances over time may not be desired
632-
// (this can happen with entities as well, but is less likely).
633-
//
634-
// For future reference, it's also worth pointing out that for interactions in the view we
635-
// already have the 3D position:
636-
// if let Some(SelectedSpaceContext::ThreeD {
637-
// pos: Some(clicked_point),
638-
// ..
639-
// }) = ctx.selection_state().hovered_space_context()
640-
641-
if let Some(entity_bbox) = bounding_boxes.per_entity.get(&entity_path.hash()) {
642-
let radius = entity_bbox.centered_bounding_sphere_radius() * 1.5;
643-
let orbit_radius = if radius < 0.0001 {
644-
// Handle zero-sized bounding boxes:
645-
(bounding_boxes.current.centered_bounding_sphere_radius() * 1.5).at_least(0.01)
646-
} else if eye.written_radius {
647-
eye.pos.distance(eye.look_target)
648-
} else {
649-
radius
650-
};
651-
652-
let fwd = eye.fwd();
653-
654-
match eye.kind {
655-
Eye3DKind::FirstPerson => {
656-
eye.pos = entity_bbox.center();
657-
eye.look_target = entity_bbox.center() + fwd;
658-
}
659-
Eye3DKind::Orbital => {
660-
eye.look_target = entity_bbox.center();
661-
eye.pos = entity_bbox.center() - fwd * orbit_radius;
662-
}
663-
}
664-
}
665-
}
666-
667610
fn ease_out(t: f32) -> f32 {
668611
1. - (1. - t) * (1. - t)
669612
}
@@ -681,6 +624,10 @@ impl EyeState {
681624
}
682625
}
683626

627+
fn stop_interpolation(&mut self) {
628+
self.interpolation = None;
629+
}
630+
684631
/// Gets and updates the current target eye from/to the blueprint.
685632
fn control_and_sync_with_blueprint(
686633
&mut self,
@@ -702,9 +649,17 @@ impl EyeState {
702649

703650
let mut drag_threshold = 0.0;
704651

705-
let tracking_entity = eye_property.component_or_empty::<re_types::components::EntityPath>(
706-
EyeControls3D::descriptor_tracking_entity().component,
707-
)?;
652+
let tracking_entity = eye_property
653+
.component_or_empty::<re_types::components::EntityPath>(
654+
EyeControls3D::descriptor_tracking_entity().component,
655+
)?
656+
.and_then(|tracking_entity| {
657+
if tracking_entity.is_empty() {
658+
None
659+
} else {
660+
Some(tracking_entity)
661+
}
662+
});
708663

709664
if let Some(tracking_entity) = &tracking_entity {
710665
let tracking_entity = EntityPath::from(tracking_entity.as_str());
@@ -717,6 +672,17 @@ impl EyeState {
717672
// to stop tracking.
718673
eye.handle_input(self, response, drag_threshold);
719674

675+
eye.save_to_blueprint(
676+
ctx.viewer_ctx,
677+
eye_property,
678+
old_pos,
679+
old_look_target,
680+
old_eye_up,
681+
);
682+
683+
// Handle spinning after saving to blueprint to not continuously write to the blueprint.
684+
self.handle_spinning(ctx, eye_property, &mut eye)?;
685+
720686
if let Some(tracked_eye) = self.handle_tracking_entity(
721687
ctx,
722688
eye_property,
@@ -730,17 +696,6 @@ impl EyeState {
730696
return Ok(tracked_eye);
731697
}
732698

733-
eye.save_to_blueprint(
734-
ctx.viewer_ctx,
735-
eye_property,
736-
old_pos,
737-
old_look_target,
738-
old_eye_up,
739-
);
740-
741-
// Handle spinning after saving to blueprint to not continuously write to the blueprint.
742-
self.handle_spinning(ctx, eye_property, &mut eye)?;
743-
744699
self.last_look_target = Some(eye.look_target);
745700
self.last_orbit_radius = Some(eye.pos.distance(eye.look_target));
746701
self.last_eye_up = Some(eye.up());
@@ -764,10 +719,13 @@ impl EyeState {
764719
tracking_entity: Option<&re_types::components::EntityPath>,
765720
) -> Option<Eye> {
766721
if let Some(tracking_entity) = &tracking_entity {
722+
// Don't do normal interpolation when tracking.
723+
self.stop_interpolation();
767724
let tracking_entity = EntityPath::from(tracking_entity.as_str());
768-
if self.last_tracked_entity.as_ref() == Some(&tracking_entity) {
725+
726+
let new_tracking = self.last_tracked_entity.as_ref() != Some(&tracking_entity);
727+
if new_tracking {
769728
self.last_tracked_entity = Some(tracking_entity.clone());
770-
self.start_interpolation();
771729
}
772730

773731
let did_eye_change = match eye.kind {
@@ -790,8 +748,69 @@ impl EyeState {
790748
EyeControls3D::descriptor_tracking_entity(),
791749
);
792750
} else {
793-
self.start_interpolation();
794-
entity_target_eye(&tracking_entity, bounding_boxes, eye);
751+
// Note that we may want to focus on an _instance_ instead in the future:
752+
// The problem with that is that there may be **many** instances (think point cloud)
753+
// and they may not be consistent over time.
754+
// -> we don't know the bounding box of every instance (right now)
755+
// -> tracking instances over time may not be desired
756+
// (this can happen with entities as well, but is less likely).
757+
//
758+
// For future reference, it's also worth pointing out that for interactions in the view we
759+
// already have the 3D position:
760+
// if let Some(SelectedSpaceContext::ThreeD {
761+
// pos: Some(clicked_point),
762+
// ..
763+
// }) = ctx.selection_state().hovered_space_context()
764+
765+
if let Some(entity_bbox) = bounding_boxes.per_entity.get(&tracking_entity.hash()) {
766+
// If we're tracking something new, set the current position & look target to the correct view.
767+
if new_tracking {
768+
let fwd = eye.fwd();
769+
let radius = entity_bbox.centered_bounding_sphere_radius() * 1.5;
770+
let radius = if radius < 0.0001 {
771+
// Handle zero-sized bounding boxes:
772+
(bounding_boxes.current.centered_bounding_sphere_radius() * 1.5)
773+
.at_least(0.02)
774+
} else {
775+
radius
776+
};
777+
eye.pos = eye.look_target - fwd * radius;
778+
// Force write of pos and look target to not use fallbacks for that.
779+
eye_property.save_blueprint_component(
780+
ctx.viewer_ctx,
781+
&EyeControls3D::descriptor_position(),
782+
&Position3D::from(eye.pos),
783+
);
784+
785+
eye_property.save_blueprint_component(
786+
ctx.viewer_ctx,
787+
&EyeControls3D::descriptor_look_target(),
788+
&Position3D::from(eye.look_target),
789+
);
790+
}
791+
792+
let orbit_radius = eye.pos.distance(eye.look_target);
793+
794+
let pos = entity_bbox.center();
795+
796+
let fwd = eye.fwd();
797+
798+
match eye.kind {
799+
Eye3DKind::FirstPerson => {
800+
eye.pos = pos;
801+
eye.look_target = pos + fwd;
802+
}
803+
Eye3DKind::Orbital => {
804+
eye.look_target = pos;
805+
eye.pos = pos - fwd * orbit_radius;
806+
}
807+
}
808+
}
809+
810+
self.last_look_target = Some(eye.look_target);
811+
self.last_eye_up = Some(eye.eye_up);
812+
self.last_orbit_radius = Some(eye.pos.distance(eye.look_target));
813+
795814
return Some(eye.get_eye());
796815
}
797816
} else {
@@ -841,6 +860,57 @@ impl EyeState {
841860
Ok(())
842861
}
843862

863+
pub fn focus_entity(
864+
&self,
865+
ctx: &ViewContext<'_>,
866+
space_cameras: &[SpaceCamera3D],
867+
bounding_boxes: &SceneBoundingBoxes,
868+
eye_property: &ViewProperty,
869+
focused_entity: &EntityPath,
870+
) -> Result<(), ViewPropertyQueryError> {
871+
let mut eye = ControlEye::from_blueprint(ctx, eye_property, self.fov_y)?;
872+
eye.did_interact = true;
873+
let ControlEye {
874+
pos: old_pos,
875+
look_target: old_look_target,
876+
eye_up: old_eye_up,
877+
..
878+
} = eye;
879+
// Focusing cameras is not something that happens now, since those are always tracked.
880+
if let Some(target_eye) = find_camera(space_cameras, focused_entity) {
881+
eye.pos = target_eye.pos_in_world();
882+
eye.look_target = target_eye.pos_in_world() + target_eye.forward_in_world();
883+
eye.eye_up = target_eye.world_from_rub_view.transform_vector3(Vec3::Y);
884+
} else if let Some(entity_bbox) = bounding_boxes.per_entity.get(&focused_entity.hash()) {
885+
let fwd = self
886+
.last_eye
887+
.map(|eye| eye.forward_in_world())
888+
.unwrap_or_else(|| Vec3::splat(f32::sqrt(1.0 / 3.0)));
889+
let radius = entity_bbox.centered_bounding_sphere_radius() * 1.5;
890+
let radius = if radius < 0.0001 {
891+
// Handle zero-sized bounding boxes:
892+
(bounding_boxes.current.centered_bounding_sphere_radius() * 1.5).at_least(0.02)
893+
} else {
894+
radius
895+
};
896+
eye.look_target = entity_bbox.center();
897+
eye.pos = eye.look_target - fwd * radius;
898+
}
899+
900+
eye.save_to_blueprint(
901+
ctx.viewer_ctx,
902+
eye_property,
903+
old_pos,
904+
old_look_target,
905+
old_eye_up,
906+
);
907+
908+
eye_property
909+
.clear_blueprint_component(ctx.viewer_ctx, EyeControls3D::descriptor_tracking_entity());
910+
911+
Ok(())
912+
}
913+
844914
pub fn update(
845915
&mut self,
846916
ctx: &ViewContext<'_>,
@@ -893,13 +963,10 @@ impl EyeState {
893963

894964
interpolation.start.lerp(&target_eye, t)
895965
} else {
966+
self.stop_interpolation();
896967
target_eye
897968
};
898969

899-
if eye == target_eye {
900-
self.interpolation = None;
901-
}
902-
903970
self.last_eye = Some(eye);
904971

905972
Ok(eye)

crates/viewer/re_view_spatial/src/ui_3d.rs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use re_viewport_blueprint::ViewProperty;
2727

2828
use crate::{
2929
SpatialView3D,
30+
eye::find_camera,
3031
space_camera_3d::SpaceCamera3D,
3132
ui::{SpatialViewState, create_labels},
3233
view_kind::SpatialViewKind,
@@ -313,13 +314,26 @@ impl SpatialView3D {
313314
};
314315

315316
if let Some(entity_path) = focused_entity {
316-
if state.last_tracked_entity() != Some(entity_path) {
317-
eye_property.save_blueprint_component(
318-
ctx,
319-
&EyeControls3D::descriptor_tracking_entity(),
320-
&re_types::components::EntityPath::from(entity_path),
321-
);
322-
state.state_3d.eye_state.last_interaction_time = Some(ui.time());
317+
if ui.ctx().input(|i| i.modifiers.alt)
318+
|| find_camera(space_cameras, entity_path).is_some()
319+
{
320+
if state.last_tracked_entity() != Some(entity_path) {
321+
eye_property.save_blueprint_component(
322+
ctx,
323+
&EyeControls3D::descriptor_tracking_entity(),
324+
&re_types::components::EntityPath::from(entity_path),
325+
);
326+
state.state_3d.eye_state.last_interaction_time = Some(ui.time());
327+
}
328+
} else {
329+
state.state_3d.eye_state.start_interpolation();
330+
state.state_3d.eye_state.focus_entity(
331+
&self.view_context(ctx, query.view_id, state),
332+
space_cameras,
333+
&state.bounding_boxes,
334+
&eye_property,
335+
entity_path,
336+
)?;
323337
}
324338
}
325339

0 commit comments

Comments
 (0)