diff --git a/doc/bino-manual.md b/doc/bino-manual.md index 43b35d1..c47c33a 100644 --- a/doc/bino-manual.md +++ b/doc/bino-manual.md @@ -164,6 +164,10 @@ Bino is a video player with a focus on 3D and Virtual Reality: Set surround mode (360, 180, off). +- `--surround-vfov` *degrees* + + Set surround vertical field of view (default 50, range 5-115). + - `-S`, `--swap-eyes` Swap left/right eye. @@ -307,6 +311,10 @@ The following commands are supported: Set the given output mode. See the command line option `--output` for a list of modes. +- `set-surround-vfov` *degrees* + + Set surround vertical field of view (default 50, range 5-115). + - `set-swap-eyes` `on`|`off` Set left/right eye swap. diff --git a/src/commandinterpreter.cpp b/src/commandinterpreter.cpp index e1c5185..c7c9410 100644 --- a/src/commandinterpreter.cpp +++ b/src/commandinterpreter.cpp @@ -301,6 +301,16 @@ void CommandInterpreter::processNextCommand() if (gui) gui->setOutputMode(outputMode); } + } else if (cmd.startsWith("set-surround-vfov ")) { + bool ok; + float surroundVerticalFOV = cmd.mid(18).toFloat(&ok); + if (!ok || surroundVerticalFOV < 5.0f || surroundVerticalFOV > 115.0f) { + LOG_FATAL("%s", qPrintable(tr("Invalid argument in %1 line %2").arg(_file.fileName()).arg(_lineIndex))); + } else { + Gui* gui = Gui::instance(); + if (gui) + gui->setSurroundVerticalFieldOfView(surroundVerticalFOV); + } } else if (cmd == "play") { Bino::instance()->play(); } else if (cmd == "stop") { diff --git a/src/gui.cpp b/src/gui.cpp index 3b1b806..f29f3a2 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -60,9 +60,9 @@ void Gui::addBinoAction(QAction* action, QMenu* menu) static Gui* GuiSingleton = nullptr; -Gui::Gui(OutputMode outputMode, bool fullscreen) : +Gui::Gui(OutputMode outputMode, float surroundVerticalFOV, bool fullscreen) : QMainWindow(), - _widget(new Widget(outputMode, this)), + _widget(new Widget(outputMode, surroundVerticalFOV, this)), _contextMenu(new QMenu(this)) { setWindowTitle("Bino"); @@ -442,6 +442,11 @@ Gui::Gui(OutputMode outputMode, bool fullscreen) : _viewToggleSwapEyesAction->setCheckable(true); connect(_viewToggleSwapEyesAction, SIGNAL(triggered()), this, SLOT(viewToggleSwapEyes())); addBinoAction(_viewToggleSwapEyesAction, viewMenu); + viewMenu->addSeparator(); + _viewResetSurroundAction = new QAction(tr("Reset surround view"), this); + _viewResetSurroundAction->setShortcuts({ Qt::Key_Z }); + connect(_viewResetSurroundAction, SIGNAL(triggered()), this, SLOT(viewResetSurround())); + addBinoAction(_viewResetSurroundAction, viewMenu); QMenu* helpMenu = addBinoMenu(tr("&Help")); QAction* helpAboutAction = new QAction(tr("&About..."), this); @@ -839,6 +844,12 @@ void Gui::viewToggleSwapEyes() _widget->update(); } +void Gui::viewResetSurround() +{ + _widget->resetSurroundView(); + _widget->update(); +} + void Gui::helpAbout() { QMessageBox::about(this, tr("About Bino"), @@ -938,6 +949,7 @@ void Gui::updateActions() } SurroundMode surroundMode = Bino::instance()->assumeSurroundMode(); + _viewResetSurroundAction->setEnabled(surroundMode != Surround_Off); for (int i = 0; i < _3dSurroundActionGroup->actions().size(); i++) { QAction* a = _3dSurroundActionGroup->actions()[i]; a->setChecked(a->data().toInt() == int(surroundMode)); @@ -980,6 +992,12 @@ void Gui::setOutputMode(OutputMode mode) _widget->update(); } +void Gui::setSurroundVerticalFieldOfView(float vfov) +{ + _widget->setSurroundVerticalFieldOfView(vfov); + _widget->update(); +} + void Gui::setFullscreen(bool f) { if (f && !(windowState() & Qt::WindowFullScreen)) { diff --git a/src/gui.hpp b/src/gui.hpp index fbcd6a5..370c10f 100644 --- a/src/gui.hpp +++ b/src/gui.hpp @@ -58,6 +58,7 @@ Q_OBJECT QAction* _mediaSeekBwd10MinsAction; QAction* _viewToggleFullscreenAction; QAction* _viewToggleSwapEyesAction; + QAction* _viewResetSurroundAction; QMenu* addBinoMenu(const QString& title); void addBinoAction(QAction* action, QMenu* menu); @@ -94,6 +95,7 @@ public slots: void mediaSeekBwd10Mins(); void viewToggleFullscreen(); void viewToggleSwapEyes(); + void viewResetSurround(); void helpAbout(); void updateActions(); @@ -107,10 +109,11 @@ public slots: virtual void moveEvent(QMoveEvent*) override; public: - Gui(OutputMode outputMode, bool fullscreen); + Gui(OutputMode outputMode, float surroundVerticalFOV, bool fullscreen); static Gui* instance(); void setOutputMode(OutputMode mode); + void setSurroundVerticalFieldOfView(float vfov); void setFullscreen(bool f); }; diff --git a/src/main.cpp b/src/main.cpp index 6737edb..391aeb5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -215,6 +215,9 @@ int main(int argc, char* argv[]) parser.addOption({ "surround", QCommandLineParser::tr("Set surround mode (%1).").arg("360, 180, off"), "mode" }); + parser.addOption({ "surround-vfov", + QCommandLineParser::tr("Set surround vertical field of view (default 50, range 5-115)."), + "degrees" }); parser.addOption({ { "S", "swap-eyes" }, QCommandLineParser::tr("Swap left/right eye.") }); parser.addOption({ { "f", "fullscreen" }, @@ -261,6 +264,15 @@ int main(int argc, char* argv[]) return 1; } } + float surroundVerticalFOV = 50.0f; + if (parser.isSet("surround-vfov")) { + bool ok; + surroundVerticalFOV = parser.value("surround-vfov").toFloat(&ok); + if (!ok || surroundVerticalFOV < 5.0f || surroundVerticalFOV > 115.0f) { + LOG_FATAL("%s", qPrintable(QCommandLineParser::tr("Invalid argument for option %1").arg("--surround-vfov"))); + return 1; + } + } InputMode inputMode = Input_Unknown; if (parser.isSet("input")) { bool ok; @@ -745,7 +757,7 @@ int main(int argc, char* argv[]) return 1; #endif } else { - Gui gui(outputMode, parser.isSet("fullscreen")); + Gui gui(outputMode, surroundVerticalFOV, parser.isSet("fullscreen")); gui.show(); // wait for several seconds to process all events before starting // the playlist, because otherwise playing might be finished before diff --git a/src/widget.cpp b/src/widget.cpp index a47747e..2c50250 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -40,7 +40,7 @@ static const QSize SizeBase(16, 9); -Widget::Widget(OutputMode outputMode, QWidget* parent) : +Widget::Widget(OutputMode outputMode, float surroundVerticalFOV, QWidget* parent) : QOpenGLWidget(parent), _sizeHint(0.5f * SizeBase), _outputMode(outputMode), @@ -52,6 +52,8 @@ Widget::Widget(OutputMode outputMode, QWidget* parent) : _surroundHorizontalAngleCurrent(0.0f), _surroundVerticalAngleCurrent(0.0f) { + setSurroundVerticalFieldOfView(surroundVerticalFOV); + _surroundVerticalFOVDefault = _surroundVerticalFOV; // to make sure clamping was applied setUpdateBehavior(QOpenGLWidget::PartialUpdate); setMouseTracking(true); setMinimumSize(8, 8); @@ -79,6 +81,20 @@ void Widget::setOutputMode(enum OutputMode mode) _outputMode = mode; } +void Widget::setSurroundVerticalFieldOfView(float vfov) +{ + _surroundVerticalFOV = qBound(5.0f, vfov, 115.0f); +} + +void Widget::resetSurroundView() +{ + _surroundVerticalFOV = _surroundVerticalFOVDefault; + _surroundHorizontalAngleBase = 0.0f; + _surroundVerticalAngleBase = 0.0f; + _surroundHorizontalAngleCurrent = 0.0f; + _surroundVerticalAngleCurrent = 0.0f; +} + QSize Widget::sizeHint() const { return _sizeHint; @@ -287,9 +303,9 @@ void Widget::paintGL() QMatrix4x4 orientationMatrix; QMatrix4x4 viewMatrix; if (Bino::instance()->assumeSurroundMode() != Surround_Off) { - float verticalVieldOfView = qDegreesToRadians(50.0f); - float aspectRatio = float(width) / height; - float top = qTan(verticalVieldOfView * 0.5f); + float verticalFieldOfView = qDegreesToRadians(_surroundVerticalFOV); + float aspectRatio = 2.0f; // always 2:1 for surround video! + float top = qTan(verticalFieldOfView * 0.5f); float bottom = -top; float right = top * aspectRatio; float left = -right; @@ -420,6 +436,12 @@ void Widget::mouseMoveEvent(QMouseEvent* e) } } +void Widget::wheelEvent(QWheelEvent* e) +{ + setSurroundVerticalFieldOfView(_surroundVerticalFOV - e->angleDelta().y() / 120.0f); + update(); +} + void Widget::mediaChanged(PlaylistEntry) { _inSurroundMovement = false; diff --git a/src/widget.hpp b/src/widget.hpp index 275e26c..e9d3dd5 100644 --- a/src/widget.hpp +++ b/src/widget.hpp @@ -1,7 +1,7 @@ /* * This file is part of Bino, a 3D video player. * - * Copyright (C) 2022 + * Copyright (C) 2022, 2023, 2024 * Martin Lambers * * This program is free software; you can redistribute it and/or modify @@ -39,6 +39,8 @@ Q_OBJECT bool _openGLStereo; // is this widget in quad-buffered stereo mode? int _alternatingLastView; // last view displayed in Mode_Alternating (0 or 1) + float _surroundVerticalFOVDefault; + float _surroundVerticalFOV; bool _inSurroundMovement; QPointF _surroundMovementStart; float _surroundHorizontalAngleBase; @@ -55,11 +57,13 @@ Q_OBJECT void rebuildDisplayPrgIfNecessary(OutputMode outputMode); public: - Widget(OutputMode outputMode, QWidget* parent = nullptr); + Widget(OutputMode outputMode, float surroundVerticalFOV, QWidget* parent = nullptr); bool isOpenGLStereo() const; OutputMode outputMode() const; void setOutputMode(OutputMode mode); + void setSurroundVerticalFieldOfView(float vfov); + void resetSurroundView(); virtual QSize sizeHint() const override; virtual void initializeGL() override; @@ -70,6 +74,7 @@ Q_OBJECT virtual void mousePressEvent(QMouseEvent* e) override; virtual void mouseReleaseEvent(QMouseEvent* e) override; virtual void mouseMoveEvent(QMouseEvent* e) override; + virtual void wheelEvent(QWheelEvent* e) override; public slots: void mediaChanged(PlaylistEntry entry);