From 3bdcb612c890dce25a81f7b4c09eb90aff92777d Mon Sep 17 00:00:00 2001 From: Pixelplanet Date: Sun, 11 Oct 2026 19:44:08 +0200 Subject: [PATCH] Fix VR180 detection (360 vs 3600), center sphere tilt, viewpoint on right stick (no accidental seeks), seek diagnostic --- src/app/app.cpp | 44 +++++++++++++++++++++++++------------ src/gfx/vulkan_renderer.cpp | 6 ++--- src/media/media_engine.cpp | 5 ++++- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/app/app.cpp b/src/app/app.cpp index d0fc4db..33ec2f9 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -26,6 +26,19 @@ bool contains(const std::string& s, const char* token) { return s.find(token) != std::string::npos; } +// True if `needle` appears not immediately followed by a digit, so a marker like +// "360" matches but a resolution like "3600" (or "3600p") does not. +bool contains_num(const std::string& s, const char* needle) { + const std::string n = needle; + std::size_t pos = 0; + while ((pos = s.find(n, pos)) != std::string::npos) { + const std::size_t after = pos + n.size(); + if (after >= s.size() || s[after] < '0' || s[after] > '9') return true; + pos = after; + } + return false; +} + // Infers projection and stereo layout from common filename conventions, e.g. // "clip_360_tb.mp4", "movie_180_sbs.mkv", "trailer_vr180.mp4". `stereoFromName` // reports whether the stereo layout was stated explicitly (so the caller can @@ -34,10 +47,12 @@ void detect_format(const std::string& path, VulkanRenderer::ProjectionMode& proj VulkanRenderer::StereoMode& stereo, bool& stereoFromName) { const std::string p = to_lower(path); - if (contains(p, "360") || contains(p, "equirect")) { - proj = VulkanRenderer::ProjectionMode::Equirect360; - } else if (contains(p, "180") || contains(p, "vr180")) { + // Check 180 before 360 and ignore digit runs so "vr180.3600p" is 180, not 360 + // (the resolution "3600" must not match the "360" marker). + if (contains(p, "vr180") || contains_num(p, "180")) { proj = VulkanRenderer::ProjectionMode::Equirect180; + } else if (contains_num(p, "360") || contains(p, "equirect")) { + proj = VulkanRenderer::ProjectionMode::Equirect360; } else { proj = VulkanRenderer::ProjectionMode::Flat; } @@ -558,23 +573,23 @@ int App::run() { renderer_.recenterScreen(in.headPos, in.headForward); interacted = true; } - // Viewpoint: flat screens move nearer/farther; immersive 360/180 - // content scales (distance = left stick Y) and tilts (angle = - // right stick Y). + // Viewpoint: flat screens move nearer/farther (thumbstick Y); + // immersive 360/180 uses the RIGHT stick -- up/down tilts (angle), + // left/right pushes content nearer/farther (distance). if (settings_.projectionMode() == 0) { if (in.thumbstickY > 0.15f || in.thumbstickY < -0.15f) { renderer_.adjustScreenDistance(in.thumbstickY * 0.04f); interacted = true; } } else if (dt > 0.0f) { - if (std::fabs(in.thumbstickLY) > 0.15f) { - renderer_.adjustSphereZoom(in.thumbstickLY * dt * 0.6f); - interacted = true; - } if (std::fabs(in.thumbstickRY) > 0.15f) { renderer_.adjustSpherePitch(in.thumbstickRY * dt * 0.6f); interacted = true; } + if (std::fabs(in.thumbstickRX) > 0.15f) { + renderer_.adjustSphereZoom(in.thumbstickRX * dt * 0.6f); + interacted = true; + } } #ifdef PIXELVR_HAVE_MEDIA if (in.togglePlay && mediaOpen_) { @@ -582,14 +597,15 @@ int App::run() { interacted = true; } if (mediaOpen_) { - // Right stick left/right steps the video -/+30s (debounced, - // acts as a d-pad jump). + // Right stick left/right steps -/+30s (flat only; in 360/180 the + // right stick drives the viewpoint instead). constexpr double kStep = 30.0; - if (in.thumbstickRX > 0.6f && seekStepArmed_) { + const bool sphereMode = settings_.projectionMode() != 0; + if (!sphereMode && in.thumbstickRX > 0.6f && seekStepArmed_) { media_.seekRelative(kStep); seekStepArmed_ = false; interacted = true; - } else if (in.thumbstickRX < -0.6f && seekStepArmed_) { + } else if (!sphereMode && in.thumbstickRX < -0.6f && seekStepArmed_) { media_.seekRelative(-kStep); seekStepArmed_ = false; interacted = true; diff --git a/src/gfx/vulkan_renderer.cpp b/src/gfx/vulkan_renderer.cpp index 7a8eee0..14de92a 100644 --- a/src/gfx/vulkan_renderer.cpp +++ b/src/gfx/vulkan_renderer.cpp @@ -1242,10 +1242,10 @@ void VulkanRenderer::renderView(uint32_t viewIndex, uint32_t imageIndex, view.pose.orientation.x, view.pose.orientation.y, view.pose.orientation.z, view.pose.orientation.w}); const Mat4 adjust = multiply( - fromQuat(Quat{0.0f, std::sin(0.5f * sphereYaw_), 0.0f, - std::cos(0.5f * sphereYaw_)}), fromQuat(Quat{std::sin(0.5f * spherePitch_), 0.0f, 0.0f, - std::cos(0.5f * spherePitch_)})); + std::cos(0.5f * spherePitch_)}), + fromQuat(Quat{0.0f, std::sin(0.5f * sphereYaw_), 0.0f, + std::cos(0.5f * sphereYaw_)})); const Mat4 rot = multiply(adjust, headRot); SpherePush push{}; std::memcpy(push.viewRotation, rot.m, sizeof(push.viewRotation)); diff --git a/src/media/media_engine.cpp b/src/media/media_engine.cpp index b87dd03..1629bcb 100644 --- a/src/media/media_engine.cpp +++ b/src/media/media_engine.cpp @@ -653,9 +653,12 @@ void MediaEngine::Impl::decodeLoop() { } const int64_t ts = static_cast(seekTo * AV_TIME_BASE); avformat_seek_file(format, -1, INT64_MIN, ts, INT64_MAX, 0); - if (!reopenDecoder()) { // a fresh decoder avoids the HW wedge on seek + const bool reopened = reopenDecoder(); // fresh decoder avoids HW wedge + if (!reopened) { PIXELVR_LOG_ERROR("MediaEngine: video decoder reopen failed on seek"); } + PIXELVR_LOG_INFO("MediaEngine: seek %.2fs reopen=%d", seekTo, reopened); + drmDiag = 0; // re-log the first frames so seek recovery is visible if (audioCodec != nullptr) { avcodec_flush_buffers(audioCodec); }