diff --git a/crates/viewer/re_view_spatial/src/eye.rs b/crates/viewer/re_view_spatial/src/eye.rs
index e59b311..b98653a 100644
--- a/crates/viewer/re_view_spatial/src/eye.rs
+++ b/crates/viewer/re_view_spatial/src/eye.rs
@@ -207,0 +208,3 @@ pub struct EyeState {
+    /// World-space point selected when the current orbital drag started.
+    orbit_drag_anchor: Option<Vec3>,
+
@@ -245,0 +249,50 @@ pub(crate) struct EyeController {
+/// NODE.DC's operator follow mode is explicitly toggled outside the embedded
+/// viewer. Unlike Rerun's transient entity tracking, manual orbit/zoom must
+/// therefore keep this rig pivot attached until the operator toggles it off.
+const NODEDC_PERSISTENT_ORBIT_TRACKING_ENTITY: &str = "/world/sensor_pose";
+
+fn keeps_orbital_tracking_after_interaction(
+    kind: Eye3DKind,
+    tracking_entity: &EntityPath,
+) -> bool {
+    kind == Eye3DKind::Orbital
+        && tracking_entity == &EntityPath::from(NODEDC_PERSISTENT_ORBIT_TRACKING_ENTITY)
+}
+
+/// Keeps the last rendered operator eye when a server-side blueprint update
+/// reactivates the same persistent follow view without explicit pose fields.
+///
+/// Rerun normally falls back to the scene bounding box when a blueprint has no
+/// position or look target. Mission Core layer toggles intentionally omit those
+/// fields so that the browser-owned eye remains authoritative.
+fn restore_persistent_orbit_eye_after_blueprint_update(
+    eye_controller: &mut EyeController,
+    previous_eye: Option<&Eye>,
+    previous_orbit_radius: Option<f32>,
+    tracking_entity: Option<&EntityPath>,
+    has_blueprint_position: bool,
+    has_blueprint_look_target: bool,
+) -> bool {
+    let Some(tracking_entity) = tracking_entity else {
+        return false;
+    };
+    let persistent =
+        keeps_orbital_tracking_after_interaction(eye_controller.kind, tracking_entity);
+    if persistent
+        && !has_blueprint_position
+        && !has_blueprint_look_target
+        && let (Some(previous_eye), Some(previous_orbit_radius)) =
+            (previous_eye, previous_orbit_radius)
+    {
+        let pos = previous_eye.pos_in_world();
+        let radius = previous_orbit_radius.max(EyeController::MIN_ORBIT_DISTANCE);
+        eye_controller.pos = pos;
+        eye_controller.look_target = pos + previous_eye.forward_in_world() * radius;
+        eye_controller.eye_up = previous_eye
+            .world_from_rub_view
+            .transform_vector3(Vec3::Y);
+        eye_controller.fov_y = previous_eye.fov_y;
+    }
+    persistent
+}
+
@@ -434 +487 @@ impl EyeController {
-        let mut rot = self.rotation();
+        let rot = self.rotation_after_delta(delta);
@@ -435,0 +489,2 @@ impl EyeController {
+        self.apply_rotation_and_radius(rot, radius);
+    }
@@ -436,0 +492,2 @@ impl EyeController {
+    fn rotation_after_delta(&self, delta: egui::Vec2) -> Quat {
+        let mut rot = self.rotation();
@@ -452 +509,2 @@ impl EyeController {
-        rot = rot.normalize();
+        rot.normalize()
+    }
@@ -454 +512,13 @@ impl EyeController {
-        self.apply_rotation_and_radius(rot, radius);
+    /// Rotate around the point selected under the pointer without moving that
+    /// point on screen.
+    fn rotate_radians_around_anchor(&mut self, delta: egui::Vec2, anchor: Vec3) {
+        let old_rotation = self.rotation();
+        let new_rotation = self.rotation_after_delta(delta);
+        let world_delta = new_rotation * old_rotation.inverse();
+        self.pos = anchor + world_delta * (self.pos - anchor);
+        self.look_target = anchor + world_delta * (self.look_target - anchor);
+    }
+
+    fn rotate_around_anchor(&mut self, delta: egui::Vec2, anchor: Vec3) {
+        let sensitivity = 0.004;
+        self.rotate_radians_around_anchor(sensitivity * delta, anchor);
@@ -491 +561,10 @@ impl EyeController {
-    fn handle_drag(&mut self, response: &egui::Response, drag_threshold: f32) {
+    fn handle_drag(
+        &mut self,
+        eye_state: &mut EyeState,
+        response: &egui::Response,
+        drag_threshold: f32,
+        pointer_space_position: Option<Vec3>,
+    ) {
+        if !response.dragged_by(ROTATE3D_BUTTON) {
+            eye_state.orbit_drag_anchor = None;
+        }
@@ -503 +582,15 @@ impl EyeController {
-                self.rotate(response.drag_delta());
+                if self.kind == Eye3DKind::Orbital {
+                    let anchor = *eye_state.orbit_drag_anchor.get_or_insert_with(|| {
+                        pointer_space_position
+                            .filter(|position| position.is_finite())
+                            .or_else(|| {
+                                response.ctx.pointer_latest_pos().and_then(|pointer| {
+                                    self.pointer_focus_plane_anchor(response.rect, pointer)
+                                })
+                            })
+                            .unwrap_or(self.look_target)
+                    });
+                    self.rotate_around_anchor(response.drag_delta(), anchor);
+                } else {
+                    self.rotate(response.drag_delta());
+                }
@@ -514,0 +608,86 @@ impl EyeController {
+    /// Returns the world-space ray under the pointer.
+    ///
+    /// Keeping this calculation local to the eye controller lets orbital zoom use the pointer
+    /// synchronously. GPU picking arrives a frame later and would make the zoom pivot visibly lag.
+    fn pointer_ray_direction(&self, rect: Rect, pointer: egui::Pos2) -> Option<Vec3> {
+        if !rect.contains(pointer) || rect.width() <= 0.0 || rect.height() <= 0.0 {
+            return None;
+        }
+
+        let fov_y = self.fov_y.unwrap_or(Eye::DEFAULT_FOV_Y);
+        let aspect_ratio = rect.width() / rect.height();
+        let focal_scale = (fov_y * 0.5).tan();
+        let x = (2.0 * (pointer.x - rect.left()) / rect.width() - 1.0) * focal_scale * aspect_ratio;
+        let y = (1.0 - 2.0 * (pointer.y - rect.top()) / rect.height()) * focal_scale;
+
+        (self.rotation() * vec3(x, y, -1.0)).try_normalize()
+    }
+
+    /// Intersect the pointer ray with the plane through the current look
+    /// target. Empty-space drags then have a deterministic pivot even when GPU
+    /// picking did not hit a rendered point.
+    fn pointer_focus_plane_anchor(&self, rect: Rect, pointer: egui::Pos2) -> Option<Vec3> {
+        let ray_direction = self.pointer_ray_direction(rect, pointer)?;
+        let denominator = ray_direction.dot(self.fwd());
+        if denominator <= 1.0e-4 {
+            return None;
+        }
+        Some(self.pos + ray_direction * (self.radius() / denominator))
+    }
+
+    /// Zoom an orbital eye around the point under the pointer on the current focus plane.
+    ///
+    /// The position and look target are scaled around the same anchor. This preserves the
+    /// projected pointer position while retaining the existing orbit direction and controls.
+    /// Once the near-plane safety radius is reached, excess zoom becomes a cursor-directed dolly
+    /// measured in the scene's navigation speed instead of near-plane millimeters.
+    fn zoom_orbit_towards_pointer(
+        &mut self,
+        zoom_factor: f32,
+        max_radius: f32,
+        rect: Rect,
+        pointer: Option<egui::Pos2>,
+    ) {
+        let radius = self.radius();
+        if !radius.is_finite() || radius <= f32::MIN_POSITIVE {
+            return;
+        }
+
+        let requested_radius = radius / zoom_factor;
+        let new_radius = requested_radius.clamp(Self::MIN_ORBIT_DISTANCE, max_radius);
+        let scale = new_radius / radius;
+
+        let pointer_ray = pointer.and_then(|pointer| {
+            Some((
+                self.pointer_ray_direction(rect, pointer)?,
+                self.pointer_focus_plane_anchor(rect, pointer)?,
+            ))
+        });
+        if let Some((ray_direction, anchor)) = pointer_ray {
+            self.pos = anchor + (self.pos - anchor) * scale;
+            self.look_target = anchor + (self.look_target - anchor) * scale;
+
+            if requested_radius < Self::MIN_ORBIT_DISTANCE {
+                // Shrinking the remaining 2 cm orbit radius consumes only part of this input.
+                // Hand the logarithmic remainder to the same scene-scaled speed used by WASD
+                // and first-person scroll. Basing this on the near-plane remainder itself made
+                // each wheel event move by millimeters and felt indistinguishable from a hard
+                // zoom limit on building- and map-scale recordings.
+                let orbit_zoom_factor = (radius / Self::MIN_ORBIT_DISTANCE).max(1.0);
+                let remaining_zoom_factor = zoom_factor / orbit_zoom_factor;
+                if remaining_zoom_factor > 1.0 && remaining_zoom_factor.is_finite() {
+                    let dolly = remaining_zoom_factor.ln() * self.speed as f32;
+                    self.pos += ray_direction * dolly;
+                    self.look_target += ray_direction * dolly;
+                }
+            }
+            self.did_interact = true;
+            return;
+        }
+
+        // Pointer data can be absent for synthetic zoom events. Preserve Rerun's centered zoom
+        // behavior in that case instead of dropping the input.
+        self.pos = self.look_target - self.fwd() * new_radius;
+        self.did_interact = true;
+    }
+
@@ -516 +695,2 @@ impl EyeController {
-    fn handle_zoom(&mut self, egui_ctx: &egui::Context, scene_bounding_box: &macaw::BoundingBox) {
+    fn handle_zoom(&mut self, response: &egui::Response, scene_bounding_box: &macaw::BoundingBox) {
+        let egui_ctx = &response.ctx;
@@ -531,2 +710,0 @@ impl EyeController {
-                let radius = self.pos.distance(self.look_target);
-
@@ -534,0 +713 @@ impl EyeController {
+                let radius = self.radius();
@@ -536,11 +715,2 @@ impl EyeController {
-                let new_radius = (radius / zoom_factor).clamp(Self::MIN_ORBIT_DISTANCE, max_radius);
-
-                // The user may be scrolling to move the camera closer, but are not realizing
-                // the radius is now tiny.
-                // TODO(emilk): inform the users somehow that scrolling won't help, and that they should use WSAD instead.
-                // It might be tempting to start moving the camera here on scroll, but that would is bad for other reasons.
-
-                if f32::MIN_POSITIVE < new_radius {
-                    self.pos = self.look_target - self.fwd() * new_radius;
-                    self.did_interact = true;
-                }
+                let pointer = response.ctx.pointer_latest_pos();
+                self.zoom_orbit_towards_pointer(zoom_factor, max_radius, response.rect, pointer);
@@ -669,0 +840 @@ impl EyeController {
+        pointer_space_position: Option<Vec3>,
@@ -671,0 +843,5 @@ impl EyeController {
+        if self.kind == Eye3DKind::Orbital {
+            self.speed = self
+                .speed
+                .max(minimum_orbital_navigation_speed(scene_bounding_box) as f64);
+        }
@@ -687 +863 @@ impl EyeController {
-        self.handle_drag(response, drag_threshold);
+        self.handle_drag(eye_state, response, drag_threshold, pointer_space_position);
@@ -690 +866 @@ impl EyeController {
-            self.handle_zoom(&response.ctx, scene_bounding_box);
+            self.handle_zoom(response, scene_bounding_box);
@@ -728,0 +905,18 @@ fn max_orbital_radius(scene_bounding_box: &macaw::BoundingBox) -> f32 {
+/// Lower bound for free-flight and post-limit zoom speed in orbital mode.
+///
+/// The default orbital speed is the current orbit radius. Close to the near
+/// limit that makes both WASD and cursor-directed dolly effectively stop.
+fn minimum_orbital_navigation_speed(scene_bounding_box: &macaw::BoundingBox) -> f32 {
+    const FALLBACK: f32 = 0.25;
+    const SCENE_DIAGONAL_FACTOR: f32 = 0.01;
+
+    if !scene_bounding_box.is_finite() || scene_bounding_box.is_nothing() {
+        return FALLBACK;
+    }
+    let scene_diagonal = scene_bounding_box.size().length();
+    if !scene_diagonal.is_finite() || scene_diagonal <= 0.0 {
+        return FALLBACK;
+    }
+    (scene_diagonal * SCENE_DIAGONAL_FACTOR).max(FALLBACK)
+}
+
@@ -773,0 +968 @@ impl EyeState {
+        pointer_space_position: Option<Vec3>,
@@ -775 +970 @@ impl EyeState {
-    ) -> Result<Eye, ViewPropertyQueryError> {
+    ) -> Result<(Eye, bool), ViewPropertyQueryError> {
@@ -777,0 +973,25 @@ impl EyeState {
+        let tracking_entity = eye_property
+            .component_or_empty::<re_sdk_types::components::EntityPath>(
+                EyeControls3D::descriptor_tracking_entity().component,
+            )?
+            .filter(|tracking_entity| !tracking_entity.is_empty());
+        let tracking_entity_path = tracking_entity
+            .as_ref()
+            .map(|tracking_entity| EntityPath::from(tracking_entity.as_str()));
+        let has_blueprint_position = eye_property
+            .component_or_empty::<Position3D>(EyeControls3D::descriptor_position().component)?
+            .is_some();
+        let has_blueprint_look_target = eye_property
+            .component_or_empty::<Position3D>(
+                EyeControls3D::descriptor_look_target().component,
+            )?
+            .is_some();
+        let persistent_orbit_tracking = restore_persistent_orbit_eye_after_blueprint_update(
+            &mut eye_controller,
+            self.last_eye.as_ref(),
+            self.last_orbit_radius,
+            tracking_entity_path.as_ref(),
+            has_blueprint_position,
+            has_blueprint_look_target,
+        );
+
@@ -794,6 +1013,0 @@ impl EyeState {
-        let tracking_entity = eye_property
-            .component_or_empty::<re_sdk_types::components::EntityPath>(
-                EyeControls3D::descriptor_tracking_entity().component,
-            )?
-            .filter(|tracking_entity| !tracking_entity.is_empty());
-
@@ -816,0 +1031 @@ impl EyeState {
+            pointer_space_position,
@@ -865 +1080 @@ impl EyeState {
-            return Ok(tracked_eye);
+            return Ok((tracked_eye, persistent_orbit_tracking));
@@ -872 +1087 @@ impl EyeState {
-        Ok(eye_controller.get_eye())
+        Ok((eye_controller.get_eye(), persistent_orbit_tracking))
@@ -891,0 +1107,2 @@ impl EyeState {
+            let persistent_orbit_tracking =
+                keeps_orbital_tracking_after_interaction(eye_controller.kind, &tracking_entity);
@@ -946 +1163 @@ impl EyeState {
-                    if new_tracking {
+                    if new_tracking && !persistent_orbit_tracking {
@@ -998 +1215,4 @@ impl EyeState {
-                if eye_controller.did_interact && did_eye_orbit_center_change {
+                if eye_controller.did_interact
+                    && did_eye_orbit_center_change
+                    && !persistent_orbit_tracking
+                {
@@ -1189,0 +1410 @@ impl EyeState {
+        pointer_space_position: Option<Vec3>,
@@ -1198 +1419 @@ impl EyeState {
-        let target_eye = self.control_and_sync_with_blueprint(
+        let (target_eye, persistent_orbit_tracking) = self.control_and_sync_with_blueprint(
@@ -1203,0 +1425 @@ impl EyeState {
+            pointer_space_position,
@@ -1210,3 +1432,6 @@ impl EyeState {
-        if eye_property
-            .component_or_empty::<Position3D>(EyeControls3D::descriptor_position().component)?
-            .is_none()
+        if !persistent_orbit_tracking
+            && eye_property
+                .component_or_empty::<Position3D>(
+                    EyeControls3D::descriptor_position().component,
+                )?
+                .is_none()
@@ -1251,0 +1477,196 @@ impl EyeState {
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    fn orbital_controller(pos: Vec3, look_target: Vec3) -> EyeController {
+        EyeController {
+            pos,
+            look_target,
+            kind: Eye3DKind::Orbital,
+            speed: 1.0,
+            eye_up: Vec3::Z,
+            fov_y: Some(Eye::DEFAULT_FOV_Y),
+            did_interact: false,
+        }
+    }
+
+    fn test_rect() -> Rect {
+        Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(800.0, 600.0))
+    }
+
+    fn assert_vec3_close(actual: Vec3, expected: Vec3) {
+        assert!(
+            actual.abs_diff_eq(expected, 1.0e-5),
+            "actual={actual:?}, expected={expected:?}"
+        );
+    }
+
+    #[test]
+    fn centered_pointer_keeps_the_orbit_target() {
+        let rect = test_rect();
+        let mut controller = orbital_controller(vec3(0.0, -10.0, 0.0), Vec3::ZERO);
+
+        controller.zoom_orbit_towards_pointer(2.0, 100.0, rect, Some(rect.center()));
+
+        assert_vec3_close(controller.pos, vec3(0.0, -5.0, 0.0));
+        assert_vec3_close(controller.look_target, Vec3::ZERO);
+        assert!(controller.did_interact);
+    }
+
+    #[test]
+    fn off_center_pointer_stays_on_the_same_view_ray() {
+        let rect = test_rect();
+        let pointer = egui::pos2(650.0, 240.0);
+        let mut controller = orbital_controller(vec3(0.0, -10.0, 0.0), Vec3::ZERO);
+        let original_ray = controller
+            .pointer_ray_direction(rect, pointer)
+            .expect("test pointer must produce a ray");
+        let forward = controller.fwd();
+        let anchor =
+            controller.pos + original_ray * (controller.radius() / original_ray.dot(forward));
+
+        controller.zoom_orbit_towards_pointer(2.0, 100.0, rect, Some(pointer));
+
+        assert!((controller.radius() - 5.0).abs() < 1.0e-5);
+        let ray_after = (anchor - controller.pos).normalize();
+        assert!(ray_after.dot(original_ray) > 0.99999);
+    }
+
+    #[test]
+    fn near_limit_hands_excess_zoom_to_cursor_directed_dolly() {
+        let rect = test_rect();
+        let radius = EyeController::MIN_ORBIT_DISTANCE;
+        let mut controller = orbital_controller(vec3(0.0, -radius, 0.0), Vec3::ZERO);
+        let old_pos = controller.pos;
+        let old_target = controller.look_target;
+
+        controller.zoom_orbit_towards_pointer(2.0, 100.0, rect, Some(rect.center()));
+
+        assert!((controller.radius() - radius).abs() < 1.0e-6);
+        assert_vec3_close(controller.pos, old_pos + Vec3::Y * 2.0_f32.ln());
+        assert_vec3_close(controller.look_target, old_target + Vec3::Y * 2.0_f32.ln());
+        assert_vec3_close(controller.look_target - controller.pos, Vec3::Y * radius);
+    }
+
+    #[test]
+    fn crossing_near_limit_preserves_unconsumed_scene_scaled_zoom() {
+        let rect = test_rect();
+        let mut controller = orbital_controller(vec3(0.0, -0.03, 0.0), Vec3::ZERO);
+
+        controller.zoom_orbit_towards_pointer(3.0, 100.0, rect, Some(rect.center()));
+
+        assert!((controller.radius() - EyeController::MIN_ORBIT_DISTANCE).abs() < 1.0e-6);
+        assert_vec3_close(controller.pos, vec3(0.0, 2.0_f32.ln() - 0.02, 0.0));
+        assert_vec3_close(controller.look_target, vec3(0.0, 2.0_f32.ln(), 0.0));
+    }
+
+    #[test]
+    fn zoom_out_respects_scene_radius_cap() {
+        let rect = test_rect();
+        let mut controller = orbital_controller(vec3(0.0, -10.0, 0.0), Vec3::ZERO);
+
+        controller.zoom_orbit_towards_pointer(0.1, 25.0, rect, Some(rect.center()));
+
+        assert!((controller.radius() - 25.0).abs() < 1.0e-5);
+    }
+
+    #[test]
+    fn orbital_rotation_keeps_selected_anchor_on_the_same_view_ray() {
+        let anchor = vec3(3.0, 2.0, 1.5);
+        let mut controller = orbital_controller(vec3(0.0, -10.0, 4.0), Vec3::ZERO);
+        let original_view_ray = controller.rotation().inverse() * (anchor - controller.pos);
+
+        controller.rotate_radians_around_anchor(egui::vec2(0.35, -0.2), anchor);
+
+        let rotated_view_ray = controller.rotation().inverse() * (anchor - controller.pos);
+        assert!(
+            rotated_view_ray
+                .normalize()
+                .dot(original_view_ray.normalize())
+                > 0.99999
+        );
+        assert!((rotated_view_ray.length() - original_view_ray.length()).abs() < 1.0e-5);
+    }
+
+    #[test]
+    fn orbital_navigation_speed_floor_tracks_scene_scale() {
+        let building_scale = macaw::BoundingBox::from_min_max(Vec3::ZERO, vec3(300.0, 400.0, 0.0));
+
+        assert!((minimum_orbital_navigation_speed(&building_scale) - 5.0).abs() < 1.0e-6);
+        assert_eq!(
+            minimum_orbital_navigation_speed(&macaw::BoundingBox::nothing()),
+            0.25
+        );
+    }
+
+    #[test]
+    fn nodedc_rig_orbit_tracking_is_persistent() {
+        let rig = EntityPath::from(NODEDC_PERSISTENT_ORBIT_TRACKING_ENTITY);
+        let other = EntityPath::from("/world/another_entity");
+
+        assert!(keeps_orbital_tracking_after_interaction(
+            Eye3DKind::Orbital,
+            &rig
+        ));
+        assert!(!keeps_orbital_tracking_after_interaction(
+            Eye3DKind::FirstPerson,
+            &rig
+        ));
+        assert!(!keeps_orbital_tracking_after_interaction(
+            Eye3DKind::Orbital,
+            &other
+        ));
+    }
+
+    #[test]
+    fn persistent_rig_follow_restores_the_last_rendered_eye_after_blueprint_update() {
+        let previous_controller =
+            orbital_controller(vec3(12.0, -7.0, 4.0), vec3(10.0, 2.0, 1.0));
+        let previous_eye = previous_controller.get_eye();
+        let previous_radius = previous_controller.radius();
+        let mut fallback_controller =
+            orbital_controller(vec3(500.0, -800.0, 300.0), Vec3::ZERO);
+        let rig = EntityPath::from(NODEDC_PERSISTENT_ORBIT_TRACKING_ENTITY);
+
+        assert!(restore_persistent_orbit_eye_after_blueprint_update(
+            &mut fallback_controller,
+            Some(&previous_eye),
+            Some(previous_radius),
+            Some(&rig),
+            false,
+            false,
+        ));
+        assert_vec3_close(fallback_controller.pos, previous_eye.pos_in_world());
+        assert!((fallback_controller.radius() - previous_radius).abs() < 1.0e-5);
+        assert!(
+            fallback_controller
+                .fwd()
+                .dot(previous_eye.forward_in_world())
+                > 0.99999
+        );
+    }
+
+    #[test]
+    fn explicit_blueprint_pose_is_not_replaced_by_the_previous_eye() {
+        let previous_controller =
+            orbital_controller(vec3(12.0, -7.0, 4.0), vec3(10.0, 2.0, 1.0));
+        let previous_eye = previous_controller.get_eye();
+        let mut explicit_controller =
+            orbital_controller(vec3(500.0, -800.0, 300.0), Vec3::ZERO);
+        let explicit_pos = explicit_controller.pos;
+        let explicit_target = explicit_controller.look_target;
+        let rig = EntityPath::from(NODEDC_PERSISTENT_ORBIT_TRACKING_ENTITY);
+
+        assert!(restore_persistent_orbit_eye_after_blueprint_update(
+            &mut explicit_controller,
+            Some(&previous_eye),
+            Some(previous_controller.radius()),
+            Some(&rig),
+            true,
+            true,
+        ));
+        assert_vec3_close(explicit_controller.pos, explicit_pos);
+        assert_vec3_close(explicit_controller.look_target, explicit_target);
+    }
+}
diff --git a/crates/viewer/re_view_spatial/src/ui_3d.rs b/crates/viewer/re_view_spatial/src/ui_3d.rs
index 56257a8..07498ab 100644
--- a/crates/viewer/re_view_spatial/src/ui_3d.rs
+++ b/crates/viewer/re_view_spatial/src/ui_3d.rs
@@ -180,0 +181,4 @@ impl SpatialView3D {
+        let pointer_space_position = state
+            .previous_picking_result
+            .as_ref()
+            .and_then(crate::picking::PickingResult::space_position);
@@ -186,0 +191 @@ impl SpatialView3D {
+            pointer_space_position,
