mirror of
https://github.com/baketnk/frame-yap.git
synced 2026-10-06 04:04:07 +02:00
Use controller-tagged compositor scroll for grab depth
This commit is contained in:
1 parent
fe8726864e
commit
f43848731b
6 files changed
+96
-13
No files matched your search
+9
-3
@@ -390,10 +390,16 @@ current depth; the other hand's stick and scale drags do not change it. A lost
|
||||
axis stops adding depth without resetting placement. Frame controller bindings
|
||||
provide left/right vector2 actions in a separate `/actions/grab` set, including
|
||||
in generated custom button manifests. Only a grab activates this set, restricted
|
||||
to the grabbing hand at overlay priority; it requires SteamVR Experimental
|
||||
overlay input overrides. It does not promote recording or text actions, and does
|
||||
to the grabbing hand at overlay priority; that app-action route requires SteamVR Experimental
|
||||
overlay input overrides. If the app axis is unavailable, controller-tagged
|
||||
compositor smooth-scroll events provide depth at 0.04 m per scroll unit, bounded
|
||||
to 3 cm per event and the same total travel limit. Only the grabbing controller
|
||||
is accepted. Smooth events are requested only during grab, integrated once
|
||||
without multiplying by elapsed time, and discarded whenever the app axis is
|
||||
available to avoid applying both streams. It does not promote recording or text actions, and does
|
||||
not depend on FrameYap's ordinary `input_priority` preference. Axis delivery during laser drag and dashboard/game focus needs a
|
||||
separate headset check. Laser scroll events are ignored while dragging.
|
||||
separate headset check. Grab scroll events affect only depth; scale scroll
|
||||
events are ignored, and ordinary UI scrolling resumes after release.
|
||||
Release leaves the last pose in the chosen mount frame; head/wrist mounts continue
|
||||
following that anchor afterward. A completed grab or scale on Head, Left wrist or
|
||||
Right wrist saves the full device-relative canvas position, rotation and scale
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ import urllib.request
|
||||
KEY = "local.frameyap.overlay"
|
||||
# The pinned release this installer belongs to (set in the release commit that
|
||||
# is tagged vRELEASE_VERSION); empty in a development tree.
|
||||
RELEASE_VERSION = "0.1.202610050153"
|
||||
RELEASE_VERSION = "0.1.202610050203"
|
||||
MARKER = "# FrameYap managed launcher v1\n"
|
||||
ARCHIVE_LIMIT = 12 * 1024**3
|
||||
MEMBER_LIMIT = 50000
|
||||
|
||||
@@ -25,7 +25,7 @@ import urllib.request
|
||||
KEY = "local.frameyap.overlay"
|
||||
# The pinned release this installer belongs to (set in the release commit that
|
||||
# is tagged vRELEASE_VERSION); empty in a development tree.
|
||||
RELEASE_VERSION = "0.1.202610050153"
|
||||
RELEASE_VERSION = "0.1.202610050203"
|
||||
MARKER = "# FrameYap managed launcher v1\n"
|
||||
ARCHIVE_LIMIT = 12 * 1024**3
|
||||
MEMBER_LIMIT = 50000
|
||||
|
||||
+33
-6
@@ -145,6 +145,8 @@ struct Overlay::Impl {
|
||||
bool enabled = [] { const char* flag = std::getenv("FRAMEYAP_PROFILE");
|
||||
return flag && std::string_view(flag) == "1"; }();
|
||||
uint64_t renders = 0, uploads = 0, motion_ticks = 0, retained_frames = 0, motion_redraws = 0, motion_uploads = 0;
|
||||
uint64_t depth_samples = 0, depth_axis_active = 0, depth_scroll_events = 0, depth_scroll_accepted = 0;
|
||||
double max_depth = 0;
|
||||
double render_ms = 0, upload_ms = 0, motion_ms = 0, motion_max_ms = 0;
|
||||
static double elapsed(std::chrono::steady_clock::time_point start) {
|
||||
return std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start).count();
|
||||
@@ -155,7 +157,10 @@ struct Overlay::Impl {
|
||||
<< " uploads=" << uploads << " upload_ms=" << upload_ms
|
||||
<< " motion_ticks=" << motion_ticks << " motion_ms=" << motion_ms
|
||||
<< " motion_max_ms=" << motion_max_ms << " retained_frames=" << retained_frames
|
||||
<< " motion_redraws=" << motion_redraws << " motion_uploads=" << motion_uploads << std::endl;
|
||||
<< " motion_redraws=" << motion_redraws << " motion_uploads=" << motion_uploads
|
||||
<< " depth_samples=" << depth_samples << " depth_axis_active=" << depth_axis_active
|
||||
<< " depth_scroll_events=" << depth_scroll_events << " depth_scroll_accepted=" << depth_scroll_accepted
|
||||
<< " max_depth=" << max_depth << std::endl;
|
||||
}
|
||||
} performance;
|
||||
|
||||
@@ -233,9 +238,9 @@ struct Overlay::Impl {
|
||||
persistence.laser_change_failed = true;
|
||||
}
|
||||
overlay_check(overlay->SetOverlayFlag(handle, vr::VROverlayFlags_SendVRDiscreteScrollEvents, true), overlay, "Laser scroll");
|
||||
// Smooth events are optional on some runtimes; discrete wheel events
|
||||
// keep Settings usable if the compositor declines the smooth mode.
|
||||
overlay->SetOverlayFlag(handle, vr::VROverlayFlags_SendVRSmoothScrollEvents, true);
|
||||
// Normal UI scrolling uses discrete events. Request the compositor
|
||||
// smooth stream only while grabbing for the depth fallback.
|
||||
overlay->SetOverlayFlag(handle, vr::VROverlayFlags_SendVRSmoothScrollEvents, false);
|
||||
surface.set_lasers_anytime(lasers_anytime);
|
||||
surface.set_wrist_world_fallback(config.wrist_world_fallback);
|
||||
surface.set_advanced_debug(config.advanced_debug);
|
||||
@@ -422,6 +427,8 @@ struct Overlay::Impl {
|
||||
}
|
||||
dragging.scale = size_scale; dragging.canvas = canvas_pose;
|
||||
dragging.started = dragging.last_depth_sample = std::chrono::steady_clock::now();
|
||||
if (kind == PanelDragKind::Grab)
|
||||
overlay->SetOverlayFlag(handle, vr::VROverlayFlags_SendVRSmoothScrollEvents, true);
|
||||
vr::VRControllerState_t state{};
|
||||
dragging.trigger_observed = system->GetControllerState(dragging.device, &state, sizeof(state)) &&
|
||||
(state.ulButtonPressed & vr::ButtonMaskFromId(vr::k_EButton_SteamVR_Trigger));
|
||||
@@ -429,6 +436,7 @@ struct Overlay::Impl {
|
||||
" device=" + std::to_string(dragging.device) + " trigger-watch=" + (dragging.trigger_observed ? "Y" : "N");
|
||||
}
|
||||
void finish_drag(bool released) {
|
||||
overlay->SetOverlayFlag(handle, vr::VROverlayFlags_SendVRSmoothScrollEvents, false);
|
||||
if (!released && applied_mount && *applied_mount == mount) {
|
||||
canvas_pose = dragging.canvas;
|
||||
size_scale = dragging.scale;
|
||||
@@ -477,8 +485,16 @@ struct Overlay::Impl {
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const double dt = std::chrono::duration<double>(now - dragging.last_depth_sample).count();
|
||||
dragging.last_depth_sample = now;
|
||||
if (dragging.kind == PanelDragKind::Grab && diagnostics.action_update_error == vr::VRInputError_None)
|
||||
if (auto axis = depth_axis()) dragging.panel.step_depth(*axis, dt);
|
||||
if (dragging.kind == PanelDragKind::Grab) {
|
||||
const auto axis = diagnostics.action_update_error == vr::VRInputError_None ? depth_axis() : std::nullopt;
|
||||
if (performance.enabled) {
|
||||
++performance.depth_samples;
|
||||
performance.depth_axis_active += bool(axis);
|
||||
}
|
||||
dragging.panel.update_depth(axis, dt);
|
||||
if (performance.enabled)
|
||||
performance.max_depth = std::max(performance.max_depth, std::abs(dragging.panel.depth()));
|
||||
}
|
||||
const auto change = dragging.panel.update(*source);
|
||||
if (!change) { finish_drag(false); return; }
|
||||
const float scale = dragging.kind == PanelDragKind::Scale ? std::clamp(dragging.scale * change->factor, .5f, 2.f) : size_scale;
|
||||
@@ -654,6 +670,17 @@ struct Overlay::Impl {
|
||||
}
|
||||
break;
|
||||
case vr::VREvent_ScrollDiscrete: case vr::VREvent_ScrollSmooth:
|
||||
if (dragging.panel.active()) {
|
||||
if (event.eventType == vr::VREvent_ScrollSmooth && dragging.kind == PanelDragKind::Grab) {
|
||||
const bool accepted = dragging.panel.queue_scroll_depth(event.trackedDeviceIndex,
|
||||
dragging.device, event.data.scroll.ydelta);
|
||||
if (performance.enabled) {
|
||||
++performance.depth_scroll_events;
|
||||
performance.depth_scroll_accepted += accepted;
|
||||
}
|
||||
}
|
||||
break; // depth consumes the grab input; never scroll the UI
|
||||
}
|
||||
if (!dragging.panel.active() && event.data.scroll.cursorIndex < cursor_positions.size() &&
|
||||
cursor_positions[event.data.scroll.cursorIndex]) {
|
||||
const auto [x, y] = *cursor_positions[event.data.scroll.cursorIndex];
|
||||
|
||||
+22
-2
@@ -4,6 +4,9 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
#include <optional>
|
||||
|
||||
namespace frameyap {
|
||||
@@ -140,6 +143,23 @@ public:
|
||||
depth_ = std::clamp(depth_ + velocity * std::min(seconds, .05), -1.5, 1.5);
|
||||
}
|
||||
|
||||
// Compositor smooth scroll is already integrated over time. Accept only
|
||||
// the captured controller, cap each event at 3 cm, and consume it once.
|
||||
bool queue_scroll_depth(uint32_t source, uint32_t grabbing_device, float delta) {
|
||||
if (!active_ || kind_ != PanelDragKind::Grab ||
|
||||
source == std::numeric_limits<uint32_t>::max() || source != grabbing_device ||
|
||||
!std::isfinite(delta)) return false;
|
||||
pending_scroll_ = std::clamp(pending_scroll_ + std::clamp(double(delta) * .04, -.03, .03), -1.5, 1.5);
|
||||
return true;
|
||||
}
|
||||
|
||||
void update_depth(std::optional<float> axis, double seconds) {
|
||||
const double scroll = std::exchange(pending_scroll_, 0.0);
|
||||
if (!active_ || kind_ != PanelDragKind::Grab) return;
|
||||
if (axis && std::isfinite(*axis)) step_depth(*axis, seconds);
|
||||
else depth_ = std::clamp(depth_ + scroll, -1.5, 1.5);
|
||||
}
|
||||
|
||||
double depth() const { return depth_; }
|
||||
|
||||
std::optional<PanelDragUpdate> update(const Matrix34& controller_pose) const {
|
||||
@@ -167,7 +187,7 @@ public:
|
||||
return PanelDragUpdate{panel_pose_, static_cast<float>(factor)};
|
||||
}
|
||||
|
||||
void reset() { active_ = false; depth_ = 0; }
|
||||
void reset() { active_ = false; depth_ = pending_scroll_ = 0; }
|
||||
bool active() const { return active_; }
|
||||
|
||||
private:
|
||||
@@ -177,7 +197,7 @@ private:
|
||||
Matrix34 panel_pose_{}, relative_panel_{};
|
||||
Vec3 center_{}, normal_{}, anchor_{}, down_hit_{}, local_ray_{};
|
||||
double baseline_sq_ = 0;
|
||||
double depth_ = 0;
|
||||
double depth_ = 0, pending_scroll_ = 0;
|
||||
};
|
||||
|
||||
} // namespace frameyap
|
||||
@@ -146,6 +146,36 @@ int main() {
|
||||
}
|
||||
drag.reset();
|
||||
|
||||
// Compositor events are source-tagged and already time-integrated.
|
||||
assert(drag.begin(PanelDragKind::Grab, angled_panel, 1, 1, .5f, .5f, down));
|
||||
assert(!drag.queue_scroll_depth(2, 1, 1.f)); // the other hand
|
||||
assert(!drag.queue_scroll_depth(std::numeric_limits<uint32_t>::max(), 1, 1.f));
|
||||
assert(!drag.queue_scroll_depth(1, 1, std::numeric_limits<float>::quiet_NaN()));
|
||||
assert(drag.queue_scroll_depth(1, 1, .5f));
|
||||
drag.update_depth({}, .001); near(float(drag.depth()), .02f);
|
||||
drag.update_depth({}, .05); near(float(drag.depth()), .02f); // no replay
|
||||
depth_pose = drag.update(down);
|
||||
assert(depth_pose); near_pose(depth_pose->pose, translated(angled_panel, -.02f, 0, 0));
|
||||
assert(drag.queue_scroll_depth(1, 1, 100.f));
|
||||
drag.update_depth({}, 100.); near(float(drag.depth()), .05f); // capped, no dt multiplication
|
||||
assert(drag.queue_scroll_depth(1, 1, -.5f));
|
||||
drag.update_depth({}, 0); near(float(drag.depth()), .03f); // opposite direction
|
||||
assert(drag.queue_scroll_depth(1, 1, 1.f));
|
||||
drag.update_depth(0.f, .05); near(float(drag.depth()), .03f); // active centered axis wins
|
||||
assert(drag.queue_scroll_depth(1, 1, 1.f));
|
||||
drag.update_depth(1.f, .05); near(float(drag.depth()), .06f); // no double application
|
||||
for (int i = 0; i < 100; ++i) assert(drag.queue_scroll_depth(1, 1, 1.f));
|
||||
drag.update_depth({}, .01); near(float(drag.depth()), 1.5f);
|
||||
assert(drag.queue_scroll_depth(1, 1, -.5f));
|
||||
drag.reset(); // queued events cannot leak into a later grab
|
||||
assert(drag.begin(PanelDragKind::Grab, angled_panel, 1, 1, .5f, .5f, down));
|
||||
drag.update_depth({}, .05); near(float(drag.depth()), 0);
|
||||
assert(drag.begin(PanelDragKind::Scale, identity, 1, 1, .5f, .5f, down));
|
||||
assert(!drag.queue_scroll_depth(1, 1, 1.f));
|
||||
drag.update_depth({}, .05); near(float(drag.depth()), 0);
|
||||
drag.reset();
|
||||
assert(!drag.queue_scroll_depth(1, 1, 1.f));
|
||||
|
||||
grab_rotation_case(pitch(), Matrix34{{{{1.f, 0.f, 0.f, .25f}},
|
||||
{{0.f, 0.f, -1.f, 1.3f}},
|
||||
{{0.f, 1.f, 0.f, 1.4f}}}});
|
||||
|
||||
Reference in new issue
Block a user