From 28103cf5ef95793cb493d0ae8ca4b5c42c8d7488 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 24 Feb 2016 17:38:01 -0800 Subject: [PATCH] Migrate composition of the overlay and pointer to the present thread --- interface/src/Application.cpp | 110 ++- interface/src/Application.h | 11 +- interface/src/CrashHandler.cpp | 1 + interface/src/Menu.cpp | 544 +------------- interface/src/Menu.h | 140 +--- interface/src/PluginContainerProxy.cpp | 4 +- interface/src/PluginContainerProxy.h | 4 +- .../src/scripting/HMDScriptingInterface.cpp | 3 +- .../src/scripting/MenuScriptingInterface.cpp | 5 +- interface/src/ui/ApplicationCompositor.cpp | 704 ------------------ interface/src/ui/ApplicationOverlay.cpp | 79 +- interface/src/ui/ApplicationOverlay.h | 3 +- interface/src/ui/OverlayConductor.cpp | 1 + interface/src/ui/PreferencesDialog.cpp | 1 + libraries/display-plugins/CMakeLists.txt | 9 +- .../Basic2DWindowOpenGLDisplayPlugin.cpp | 4 +- .../Basic2DWindowOpenGLDisplayPlugin.h | 2 +- .../src/display-plugins/CompositorHelper.cpp | 485 ++++++++++++ .../src/display-plugins/CompositorHelper.h | 100 ++- .../src/display-plugins/NullDisplayPlugin.cpp | 4 +- .../src/display-plugins/NullDisplayPlugin.h | 4 +- .../display-plugins/OpenGLDisplayPlugin.cpp | 186 ++++- .../src/display-plugins/OpenGLDisplayPlugin.h | 45 +- .../display-plugins/hmd/HmdDisplayPlugin.cpp | 91 ++- .../display-plugins/hmd/HmdDisplayPlugin.h | 21 +- .../stereo/InterleavedStereoDisplayPlugin.cpp | 22 +- .../stereo/InterleavedStereoDisplayPlugin.h | 14 +- .../stereo/SideBySideStereoDisplayPlugin.cpp | 6 +- .../stereo/SideBySideStereoDisplayPlugin.h | 1 + .../stereo/StereoDisplayPlugin.cpp | 22 +- .../stereo/StereoDisplayPlugin.h | 4 +- libraries/gl/src/gl/GLEscrow.h | 26 +- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 4 +- libraries/gl/src/gl/OglplusHelpers.cpp | 7 +- libraries/gpu/src/gpu/GLBackend.h | 2 +- libraries/gpu/src/gpu/GLBackendTexture.cpp | 9 +- .../src/input-plugins/KeyboardMouseDevice.cpp | 0 libraries/plugins/src/plugins/DisplayPlugin.h | 9 +- .../plugins/src/plugins/PluginContainer.h | 9 +- libraries/script-engine/CMakeLists.txt | 2 +- .../script-engine/src/MenuItemProperties.cpp | 22 +- .../script-engine/src/MenuItemProperties.h | 18 +- libraries/ui/src/CursorManager.cpp | 4 + libraries/ui/src/CursorManager.h | 1 + libraries/ui/src/ui/Logging.cpp | 11 + libraries/ui/src/ui/Logging.h | 16 + libraries/ui/src/ui/Menu.cpp | 561 ++++++++++++++ libraries/ui/src/ui/Menu.h | 155 ++++ plugins/oculus/CMakeLists.txt | 2 +- .../oculus/src/OculusBaseDisplayPlugin.cpp | 7 +- .../oculus/src/OculusDebugDisplayPlugin.cpp | 5 - plugins/oculus/src/OculusDebugDisplayPlugin.h | 8 +- plugins/oculus/src/OculusDisplayPlugin.cpp | 65 +- plugins/oculus/src/OculusDisplayPlugin.h | 17 +- plugins/oculusLegacy/CMakeLists.txt | 2 +- .../src/OculusLegacyDisplayPlugin.h | 1 + plugins/openvr/src/OpenVrDisplayPlugin.cpp | 10 +- plugins/openvr/src/OpenVrDisplayPlugin.h | 3 +- tests/controllers/src/main.cpp | 4 +- 59 files changed, 1874 insertions(+), 1736 deletions(-) delete mode 100644 interface/src/ui/ApplicationCompositor.cpp create mode 100644 libraries/display-plugins/src/display-plugins/CompositorHelper.cpp rename interface/src/ui/ApplicationCompositor.h => libraries/display-plugins/src/display-plugins/CompositorHelper.h (73%) mode change 100755 => 100644 libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp create mode 100644 libraries/ui/src/ui/Logging.cpp create mode 100644 libraries/ui/src/ui/Logging.h create mode 100644 libraries/ui/src/ui/Menu.cpp create mode 100644 libraries/ui/src/ui/Menu.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 81b633a1aa..34301ca871 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -120,6 +120,7 @@ #include #include #include +#include #include "AnimDebugDraw.h" #include "AudioClient.h" @@ -376,6 +377,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(true, qApp, qApp); + DependencyManager::set(); return true; } @@ -770,7 +772,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : } if (action == controller::toInt(controller::Action::RETICLE_CLICK)) { - auto reticlePos = _compositor.getReticlePosition(); + auto reticlePos = getApplicationCompositor().getReticlePosition(); QPoint globalPos(reticlePos.x, reticlePos.y); // FIXME - it would be nice if this was self contained in the _compositor or Reticle class @@ -793,14 +795,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : } else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) { cycleCamera(); } else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) { - auto reticlePosition = _compositor.getReticlePosition(); + auto reticlePosition = getApplicationCompositor().getReticlePosition(); offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QPoint(reticlePosition.x, reticlePosition.y))); } else if (action == controller::toInt(controller::Action::RETICLE_X)) { - auto oldPos = _compositor.getReticlePosition(); - _compositor.setReticlePosition({ oldPos.x + state, oldPos.y }); + auto oldPos = getApplicationCompositor().getReticlePosition(); + getApplicationCompositor().setReticlePosition({ oldPos.x + state, oldPos.y }); } else if (action == controller::toInt(controller::Action::RETICLE_Y)) { - auto oldPos = _compositor.getReticlePosition(); - _compositor.setReticlePosition({ oldPos.x, oldPos.y + state }); + auto oldPos = getApplicationCompositor().getReticlePosition(); + getApplicationCompositor().setReticlePosition({ oldPos.x, oldPos.y + state }); } else if (action == controller::toInt(controller::Action::TOGGLE_OVERLAY)) { toggleOverlays(); } @@ -1253,17 +1255,17 @@ void Application::initializeUi() { rootContext->setContextProperty("HMD", DependencyManager::get().data()); rootContext->setContextProperty("Scene", DependencyManager::get().data()); rootContext->setContextProperty("Render", _renderEngine->getConfiguration().get()); - rootContext->setContextProperty("Reticle", _compositor.getReticleInterface()); + rootContext->setContextProperty("Reticle", getApplicationCompositor().getReticleInterface()); - rootContext->setContextProperty("ApplicationCompositor", &_compositor); + rootContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor()); _glWidget->installEventFilter(offscreenUi.data()); offscreenUi->setMouseTranslator([=](const QPointF& pt) { QPointF result = pt; auto displayPlugin = getActiveDisplayPlugin(); if (displayPlugin->isHmd()) { - _compositor.handleRealMouseMoveEvent(false); - auto resultVec = _compositor.getReticlePosition(); + getApplicationCompositor().handleRealMouseMoveEvent(false); + auto resultVec = getApplicationCompositor().getReticlePosition(); result = QPointF(resultVec.x, resultVec.y); } return result.toPoint(); @@ -1287,7 +1289,15 @@ void Application::initializeUi() { _keyboardMouseDevice = std::dynamic_pointer_cast(inputPlugin); } } + Menu::setInstance(); updateInputModes(); + + auto compositorHelper = DependencyManager::get(); + connect(compositorHelper.data(), &CompositorHelper::allowMouseCaptureChanged, [=] { + if (isHMDMode()) { + showCursor(compositorHelper->getAllowMouseCapture() ? Qt::BlankCursor : Qt::ArrowCursor); + } + }); } void Application::paintGL() { @@ -1381,9 +1391,10 @@ void Application::paintGL() { QSize size = getDeviceSize(); renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height()); _applicationOverlay.renderOverlay(&renderArgs); - gpu::FramebufferPointer overlayFramebuffer = _applicationOverlay.getOverlayFramebuffer(); - - + auto overlayTexture = _applicationOverlay.acquireOverlay(); + if (overlayTexture) { + displayPlugin->submitOverlayTexture(overlayTexture); + } } glm::vec3 boomOffset; @@ -1484,6 +1495,8 @@ void Application::paintGL() { myAvatar->endCapture(); } + getApplicationCompositor().setFrameInfo(_frameCount, _myCamera.getTransform()); + // Primary rendering pass auto framebufferCache = DependencyManager::get(); const QSize size = framebufferCache->getFrameBufferSize(); @@ -1536,7 +1549,7 @@ void Application::paintGL() { mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * IPDScale); eyeOffsets[eye] = eyeOffsetTransform; - displayPlugin->setEyeRenderPose(_frameCount, eye, headPose); + displayPlugin->setEyeRenderPose(_frameCount, eye, headPose * glm::inverse(eyeOffsetTransform)); eyeProjections[eye] = displayPlugin->getEyeProjection(eye, baseProjection); }); @@ -1550,44 +1563,12 @@ void Application::paintGL() { renderArgs._context->enableStereo(false); } - // Overlay Composition, needs to occur after screen space effects have completed - // FIXME migrate composition into the display plugins - { - PROFILE_RANGE(__FUNCTION__ "/compositor"); - PerformanceTimer perfTimer("compositor"); - - auto primaryFbo = finalFramebuffer; - - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFbo)); - if (displayPlugin->isStereo()) { - QRect currentViewport(QPoint(0, 0), QSize(size.width() / 2, size.height())); - glClear(GL_DEPTH_BUFFER_BIT); - for_each_eye([&](Eye eye) { - renderArgs._viewport = toGlm(currentViewport); - if (displayPlugin->isHmd()) { - _compositor.displayOverlayTextureHmd(&renderArgs, eye); - } else { - _compositor.displayOverlayTexture(&renderArgs); - } - }, [&] { - currentViewport.moveLeft(currentViewport.width()); - }); - } else { - glViewport(0, 0, size.width(), size.height()); - _compositor.displayOverlayTexture(&renderArgs); - } - } - // deliver final composited scene to the display plugin { PROFILE_RANGE(__FUNCTION__ "/pluginOutput"); PerformanceTimer perfTimer("pluginOutput"); - auto finalTexturePointer = finalFramebuffer->getRenderBuffer(0); - - GLuint finalTexture = gpu::GLBackend::getTextureID(finalTexturePointer); - Q_ASSERT(0 != finalTexture); - + auto finalTexture = finalFramebuffer->getRenderBuffer(0); Q_ASSERT(!_lockedFramebufferMap.contains(finalTexture)); _lockedFramebufferMap[finalTexture] = finalFramebuffer; @@ -1595,10 +1576,9 @@ void Application::paintGL() { { PROFILE_RANGE(__FUNCTION__ "/pluginSubmitScene"); PerformanceTimer perfTimer("pluginSubmitScene"); - displayPlugin->submitSceneTexture(_frameCount, finalTexture, toGlm(size)); + displayPlugin->submitSceneTexture(_frameCount, finalTexture); } Q_ASSERT(isCurrentContext(_offscreenContext->getContext())); - } { @@ -1795,7 +1775,7 @@ bool Application::event(QEvent* event) { bool Application::eventFilter(QObject* object, QEvent* event) { if (event->type() == QEvent::Leave) { - _compositor.handleLeaveEvent(); + getApplicationCompositor().handleLeaveEvent(); } if (event->type() == QEvent::ShortcutOverride) { @@ -2083,7 +2063,7 @@ void Application::keyPressEvent(QKeyEvent* event) { void Application::keyReleaseEvent(QKeyEvent* event) { if (event->key() == Qt::Key_Alt && _altPressed && hasFocus()) { auto offscreenUi = DependencyManager::get(); - auto reticlePosition = _compositor.getReticlePosition(); + auto reticlePosition = getApplicationCompositor().getReticlePosition(); offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QPoint(reticlePosition.x, reticlePosition.y))); } @@ -2172,13 +2152,6 @@ void Application::maybeToggleMenuVisible(QMouseEvent* event) { #endif } -/// called by ApplicationCompositor when in HMD mode and we're faking our mouse movement -void Application::fakeMouseEvent(QMouseEvent* event) { - _fakedMouseEvent = true; - sendEvent(_glWidget, event); - _fakedMouseEvent = false; -} - void Application::mouseMoveEvent(QMouseEvent* event) { PROFILE_RANGE(__FUNCTION__); @@ -2190,15 +2163,15 @@ void Application::mouseMoveEvent(QMouseEvent* event) { // if this is a real mouse event, and we're in HMD mode, then we should use it to move the // compositor reticle - if (!_fakedMouseEvent) { + if (event->spontaneous()) { // handleRealMouseMoveEvent() will return true, if we shouldn't process the event further - if (_compositor.handleRealMouseMoveEvent()) { + if (getApplicationCompositor().handleRealMouseMoveEvent()) { return; // bail } } auto offscreenUi = DependencyManager::get(); - auto eventPosition = _compositor.getMouseEventPosition(event); + auto eventPosition = getApplicationCompositor().getMouseEventPosition(event); QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget); auto button = event->button(); auto buttons = event->buttons(); @@ -2240,7 +2213,7 @@ void Application::mousePressEvent(QMouseEvent* event) { // will consume all keyboard events. offscreenUi->unfocusWindows(); - auto eventPosition = _compositor.getMouseEventPosition(event); + auto eventPosition = getApplicationCompositor().getMouseEventPosition(event); QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget); QMouseEvent mappedEvent(event->type(), transformedPos, @@ -2286,7 +2259,7 @@ void Application::mouseDoublePressEvent(QMouseEvent* event) { void Application::mouseReleaseEvent(QMouseEvent* event) { auto offscreenUi = DependencyManager::get(); - auto eventPosition = _compositor.getMouseEventPosition(event); + auto eventPosition = getApplicationCompositor().getMouseEventPosition(event); QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget); QMouseEvent mappedEvent(event->type(), transformedPos, @@ -2569,7 +2542,7 @@ void Application::setLowVelocityFilter(bool lowVelocityFilter) { } ivec2 Application::getMouse() { - auto reticlePosition = _compositor.getReticlePosition(); + auto reticlePosition = getApplicationCompositor().getReticlePosition(); // in the HMD, the reticlePosition is the mouse position if (isHMDMode()) { @@ -4208,7 +4181,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("Render", _renderEngine->getConfiguration().get()); scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get().data()); - scriptEngine->registerGlobalObject("Reticle", _compositor.getReticleInterface()); + scriptEngine->registerGlobalObject("Reticle", getApplicationCompositor().getReticleInterface()); } bool Application::canAcceptURL(const QString& urlString) const { @@ -4709,7 +4682,7 @@ static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool acti auto action = menu->addActionToQMenuAndActionHash(parent, name, 0, qApp, SLOT(updateDisplayMode()), - QAction::NoRole, UNSPECIFIED_POSITION, groupingMenu); + QAction::NoRole, Menu::UNSPECIFIED_POSITION, groupingMenu); action->setCheckable(true); action->setChecked(active); @@ -4814,6 +4787,7 @@ void Application::updateDisplayMode() { oldDisplayPlugin = _displayPlugin; _displayPlugin = newDisplayPlugin; + getApplicationCompositor().setDisplayPlugin(_displayPlugin); // If the displayPlugin is a screen based HMD, then it will want the HMDTools displayed // Direct Mode HMDs (like windows Oculus) will be isHmd() but will have a screen of -1 @@ -5031,3 +5005,7 @@ void Application::showDesktop() { _overlayConductor.setEnabled(true); } } + +CompositorHelper& Application::getApplicationCompositor() const { + return *DependencyManager::get(); +} diff --git a/interface/src/Application.h b/interface/src/Application.h index c16175a333..3a727db533 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -51,7 +51,6 @@ #include "render/Engine.h" #include "scripting/ControllerScriptingInterface.h" #include "scripting/DialogsManagerScriptingInterface.h" -#include "ui/ApplicationCompositor.h" #include "ui/ApplicationOverlay.h" #include "ui/AudioStatsDialog.h" #include "ui/BandwidthDialog.h" @@ -67,6 +66,7 @@ class GLCanvas; class FaceTracker; class MainWindow; class AssetUpload; +class CompositorHelper; namespace controller { class StateController; @@ -125,6 +125,7 @@ public: bool isThrottleRendering() const; Camera* getCamera() { return &_myCamera; } + const Camera* getCamera() const { return &_myCamera; } // Represents the current view frustum of the avatar. ViewFrustum* getViewFrustum(); const ViewFrustum* getViewFrustum() const; @@ -149,8 +150,7 @@ public: ApplicationOverlay& getApplicationOverlay() { return _applicationOverlay; } const ApplicationOverlay& getApplicationOverlay() const { return _applicationOverlay; } - ApplicationCompositor& getApplicationCompositor() { return _compositor; } - const ApplicationCompositor& getApplicationCompositor() const { return _compositor; } + CompositorHelper& getApplicationCompositor() const; Overlays& getOverlays() { return _overlays; } @@ -221,8 +221,6 @@ public: float getAverageSimsPerSecond(); - void fakeMouseEvent(QMouseEvent* event); - signals: void svoImportRequested(const QString& url); @@ -390,7 +388,7 @@ private: InputPluginList _activeInputPlugins; bool _activatingDisplayPlugin { false }; - QMap _lockedFramebufferMap; + QMap _lockedFramebufferMap; MainWindow* _window; @@ -484,7 +482,6 @@ private: Overlays _overlays; ApplicationOverlay _applicationOverlay; - ApplicationCompositor _compositor; OverlayConductor _overlayConductor; DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface(); diff --git a/interface/src/CrashHandler.cpp b/interface/src/CrashHandler.cpp index f9cd7679ae..5a194973b4 100644 --- a/interface/src/CrashHandler.cpp +++ b/interface/src/CrashHandler.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "Menu.h" diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 72e138e0cb..d605516380 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "Application.h" #include "AccountManager.h" @@ -45,11 +46,15 @@ #include "Menu.h" +// Fixme make static member of Menu static const char* const MENU_PROPERTY_NAME = "com.highfidelity.Menu"; +void Menu::setInstance() { + globalInstance(MENU_PROPERTY_NAME); +} + Menu* Menu::getInstance() { - static Menu* instance = globalInstance(MENU_PROPERTY_NAME); - return instance; + return static_cast(ui::Menu::getInstance()); } Menu::Menu() { @@ -621,418 +626,6 @@ Menu::Menu() { #endif } -void Menu::toggleAdvancedMenus() { - setGroupingIsVisible("Advanced", !getGroupingIsVisible("Advanced")); -} - -void Menu::toggleDeveloperMenus() { - setGroupingIsVisible("Developer", !getGroupingIsVisible("Developer")); -} - -void Menu::loadSettings() { - scanMenuBar(&Menu::loadAction); -} - -void Menu::saveSettings() { - scanMenuBar(&Menu::saveAction); -} - -void Menu::loadAction(Settings& settings, QAction& action) { - if (action.isChecked() != settings.value(action.text(), action.isChecked()).toBool()) { - action.trigger(); - } -} - -void Menu::saveAction(Settings& settings, QAction& action) { - settings.setValue(action.text(), action.isChecked()); -} - -void Menu::scanMenuBar(settingsAction modifySetting) { - Settings settings; - foreach (QMenu* menu, findChildren()) { - scanMenu(*menu, modifySetting, settings); - } -} - -void Menu::scanMenu(QMenu& menu, settingsAction modifySetting, Settings& settings) { - settings.beginGroup(menu.title()); - foreach (QAction* action, menu.actions()) { - if (action->menu()) { - scanMenu(*action->menu(), modifySetting, settings); - } else if (action->isCheckable()) { - modifySetting(settings, *action); - } - } - settings.endGroup(); -} - -void Menu::addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName, - int menuItemLocation, const QString& grouping) { - QAction* actionBefore = NULL; - QAction* separator; - QAction* separatorText; - - if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) { - actionBefore = destinationMenu->actions()[menuItemLocation]; - } - if (actionBefore) { - separator = new QAction("",destinationMenu); - destinationMenu->insertAction(actionBefore, separator); - separator->setSeparator(true); - - separatorText = new QAction(actionName,destinationMenu); - separatorText->setEnabled(false); - destinationMenu->insertAction(actionBefore, separatorText); - - } else { - separator = destinationMenu->addSeparator(); - separatorText = destinationMenu->addAction(actionName); - separatorText->setEnabled(false); - } - - if (isValidGrouping(grouping)) { - _groupingActions[grouping] << separator; - _groupingActions[grouping] << separatorText; - bool isVisible = getGroupingIsVisible(grouping); - separator->setVisible(isVisible); - separatorText->setVisible(isVisible); - } -} - -QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu, - const QString& actionName, - const QKeySequence& shortcut, - const QObject* receiver, - const char* member, - QAction::MenuRole role, - int menuItemLocation, - const QString& grouping) { - QAction* action = NULL; - QAction* actionBefore = NULL; - - if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) { - actionBefore = destinationMenu->actions()[menuItemLocation]; - } - - if (!actionBefore) { - if (receiver && member) { - action = destinationMenu->addAction(actionName, receiver, member, shortcut); - } else { - action = destinationMenu->addAction(actionName); - action->setShortcut(shortcut); - } - } else { - action = new QAction(actionName, destinationMenu); - action->setShortcut(shortcut); - destinationMenu->insertAction(actionBefore, action); - - if (receiver && member) { - connect(action, SIGNAL(triggered()), receiver, member); - } - } - action->setMenuRole(role); - - _actionHash.insert(actionName, action); - - if (isValidGrouping(grouping)) { - _groupingActions[grouping] << action; - action->setVisible(getGroupingIsVisible(grouping)); - } - - return action; -} - -QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu, - QAction* action, - const QString& actionName, - const QKeySequence& shortcut, - QAction::MenuRole role, - int menuItemLocation, - const QString& grouping) { - QAction* actionBefore = NULL; - - if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) { - actionBefore = destinationMenu->actions()[menuItemLocation]; - } - - if (!actionName.isEmpty()) { - action->setText(actionName); - } - - if (shortcut != 0) { - action->setShortcut(shortcut); - } - - if (role != QAction::NoRole) { - action->setMenuRole(role); - } - - if (!actionBefore) { - destinationMenu->addAction(action); - } else { - destinationMenu->insertAction(actionBefore, action); - } - - _actionHash.insert(action->text(), action); - - if (isValidGrouping(grouping)) { - _groupingActions[grouping] << action; - action->setVisible(getGroupingIsVisible(grouping)); - } - - return action; -} - -QAction* Menu::addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu, - const QString& actionName, - const QKeySequence& shortcut, - const bool checked, - const QObject* receiver, - const char* member, - int menuItemLocation, - const QString& grouping) { - - QAction* action = addActionToQMenuAndActionHash(destinationMenu, actionName, shortcut, receiver, member, - QAction::NoRole, menuItemLocation); - action->setCheckable(true); - action->setChecked(checked); - - if (isValidGrouping(grouping)) { - _groupingActions[grouping] << action; - action->setVisible(getGroupingIsVisible(grouping)); - } - - return action; -} - -void Menu::removeAction(MenuWrapper* menu, const QString& actionName) { - auto action = _actionHash.value(actionName); - menu->removeAction(action); - _actionHash.remove(actionName); - for (auto& grouping : _groupingActions) { - grouping.remove(action); - } -} - -void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) { - if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(Menu::getInstance(), "setIsOptionChecked", Qt::BlockingQueuedConnection, - Q_ARG(const QString&, menuOption), - Q_ARG(bool, isChecked)); - return; - } - QAction* menu = _actionHash.value(menuOption); - if (menu) { - menu->setChecked(isChecked); - } -} - -bool Menu::isOptionChecked(const QString& menuOption) const { - const QAction* menu = _actionHash.value(menuOption); - if (menu) { - return menu->isChecked(); - } - return false; -} - -void Menu::triggerOption(const QString& menuOption) { - QAction* action = _actionHash.value(menuOption); - if (action) { - action->trigger(); - } else { - qCDebug(interfaceapp) << "NULL Action for menuOption '" << menuOption << "'"; - } -} - -QAction* Menu::getActionForOption(const QString& menuOption) { - return _actionHash.value(menuOption); -} - -QAction* Menu::getActionFromName(const QString& menuName, MenuWrapper* menu) { - QList menuActions; - if (menu) { - menuActions = menu->actions(); - } else { - menuActions = actions(); - } - - foreach (QAction* menuAction, menuActions) { - QString actionText = menuAction->text(); - if (menuName == menuAction->text()) { - return menuAction; - } - } - return NULL; -} - -MenuWrapper* Menu::getSubMenuFromName(const QString& menuName, MenuWrapper* menu) { - QAction* action = getActionFromName(menuName, menu); - if (action) { - return MenuWrapper::fromMenu(action->menu()); - } - return NULL; -} - -MenuWrapper* Menu::getMenuParent(const QString& menuName, QString& finalMenuPart) { - QStringList menuTree = menuName.split(">"); - MenuWrapper* parent = NULL; - MenuWrapper* menu = NULL; - foreach (QString menuTreePart, menuTree) { - parent = menu; - finalMenuPart = menuTreePart.trimmed(); - menu = getSubMenuFromName(finalMenuPart, parent); - if (!menu) { - break; - } - } - return parent; -} - -MenuWrapper* Menu::getMenu(const QString& menuName) { - QStringList menuTree = menuName.split(">"); - MenuWrapper* parent = NULL; - MenuWrapper* menu = NULL; - int item = 0; - foreach (QString menuTreePart, menuTree) { - menu = getSubMenuFromName(menuTreePart.trimmed(), parent); - if (!menu) { - break; - } - parent = menu; - item++; - } - return menu; -} - -QAction* Menu::getMenuAction(const QString& menuName) { - QStringList menuTree = menuName.split(">"); - MenuWrapper* parent = NULL; - QAction* action = NULL; - foreach (QString menuTreePart, menuTree) { - action = getActionFromName(menuTreePart.trimmed(), parent); - if (!action) { - break; - } - parent = MenuWrapper::fromMenu(action->menu()); - } - return action; -} - -int Menu::findPositionOfMenuItem(MenuWrapper* menu, const QString& searchMenuItem) { - int position = 0; - foreach(QAction* action, menu->actions()) { - if (action->text() == searchMenuItem) { - return position; - } - position++; - } - return UNSPECIFIED_POSITION; // not found -} - -int Menu::positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition) { - QList menuActions = menu->actions(); - if (requestedPosition > 1 && requestedPosition < menuActions.size()) { - QAction* beforeRequested = menuActions[requestedPosition - 1]; - if (beforeRequested->isSeparator()) { - requestedPosition--; - } - } - return requestedPosition; -} - -bool Menu::_isSomeSubmenuShown = false; - -MenuWrapper* Menu::addMenu(const QString& menuName, const QString& grouping) { - QStringList menuTree = menuName.split(">"); - MenuWrapper* addTo = NULL; - MenuWrapper* menu = NULL; - foreach (QString menuTreePart, menuTree) { - menu = getSubMenuFromName(menuTreePart.trimmed(), addTo); - if (!menu) { - if (!addTo) { - menu = new MenuWrapper(QMenuBar::addMenu(menuTreePart.trimmed())); - } else { - menu = addTo->addMenu(menuTreePart.trimmed()); - } - } - addTo = menu; - } - - if (isValidGrouping(grouping)) { - auto action = getMenuAction(menuName); - if (action) { - _groupingActions[grouping] << action; - action->setVisible(getGroupingIsVisible(grouping)); - } - } - - QMenuBar::repaint(); - - // hook our show/hide for popup menus, so we can keep track of whether or not one - // of our submenus is currently showing. - connect(menu->_realMenu, &QMenu::aboutToShow, []() { _isSomeSubmenuShown = true; }); - connect(menu->_realMenu, &QMenu::aboutToHide, []() { _isSomeSubmenuShown = false; }); - - return menu; -} - -void Menu::removeMenu(const QString& menuName) { - QAction* action = getMenuAction(menuName); - - // only proceed if the menu actually exists - if (action) { - QString finalMenuPart; - MenuWrapper* parent = getMenuParent(menuName, finalMenuPart); - if (parent) { - parent->removeAction(action); - } else { - QMenuBar::removeAction(action); - } - - QMenuBar::repaint(); - } -} - -bool Menu::menuExists(const QString& menuName) { - QAction* action = getMenuAction(menuName); - - // only proceed if the menu actually exists - if (action) { - return true; - } - return false; -} - -void Menu::addSeparator(const QString& menuName, const QString& separatorName, const QString& grouping) { - MenuWrapper* menuObj = getMenu(menuName); - if (menuObj) { - addDisabledActionAndSeparator(menuObj, separatorName); - } -} - -void Menu::removeSeparator(const QString& menuName, const QString& separatorName) { - MenuWrapper* menu = getMenu(menuName); - bool separatorRemoved = false; - if (menu) { - int textAt = findPositionOfMenuItem(menu, separatorName); - QList menuActions = menu->actions(); - QAction* separatorText = menuActions[textAt]; - if (textAt > 0 && textAt < menuActions.size()) { - QAction* separatorLine = menuActions[textAt - 1]; - if (separatorLine) { - if (separatorLine->isSeparator()) { - menu->removeAction(separatorText); - menu->removeAction(separatorLine); - separatorRemoved = true; - } - } - } - } - if (separatorRemoved) { - QMenuBar::repaint(); - } -} - void Menu::addMenuItem(const MenuItemProperties& properties) { MenuWrapper* menuObj = getMenu(properties.menuName); if (menuObj) { @@ -1075,126 +668,3 @@ void Menu::addMenuItem(const MenuItemProperties& properties) { QMenuBar::repaint(); } } - -void Menu::removeMenuItem(const QString& menu, const QString& menuitem) { - MenuWrapper* menuObj = getMenu(menu); - if (menuObj) { - removeAction(menuObj, menuitem); - QMenuBar::repaint(); - } -} - -bool Menu::menuItemExists(const QString& menu, const QString& menuitem) { - QAction* menuItemAction = _actionHash.value(menuitem); - if (menuItemAction) { - return (getMenu(menu) != NULL); - } - return false; -} - -bool Menu::getGroupingIsVisible(const QString& grouping) { - if (grouping.isEmpty() || grouping.isNull()) { - return true; - } - if (_groupingVisible.contains(grouping)) { - return _groupingVisible[grouping]; - } - return false; -} - -void Menu::setGroupingIsVisible(const QString& grouping, bool isVisible) { - // NOTE: Default grouping always visible - if (grouping.isEmpty() || grouping.isNull()) { - return; - } - _groupingVisible[grouping] = isVisible; - - for (auto action: _groupingActions[grouping]) { - action->setVisible(isVisible); - } - - QMenuBar::repaint(); -} - -void Menu::addActionGroup(const QString& groupName, const QStringList& actionList, const QString& selected) { - auto menu = addMenu(groupName); - - QActionGroup* actionGroup = new QActionGroup(menu); - actionGroup->setExclusive(true); - - auto menuScriptingInterface = MenuScriptingInterface::getInstance(); - for (auto action : actionList) { - auto item = addCheckableActionToQMenuAndActionHash(menu, action, 0, action == selected, - menuScriptingInterface, - SLOT(menuItemTriggered())); - actionGroup->addAction(item); - } - - QMenuBar::repaint(); -} - -void Menu::removeActionGroup(const QString& groupName) { - removeMenu(groupName); -} - -MenuWrapper::MenuWrapper(QMenu* menu) : _realMenu(menu) { - VrMenu::executeOrQueue([=](VrMenu* vrMenu) { - vrMenu->addMenu(menu); - }); - _backMap[menu] = this; -} - -QList MenuWrapper::actions() { - return _realMenu->actions(); -} - -MenuWrapper* MenuWrapper::addMenu(const QString& menuName) { - return new MenuWrapper(_realMenu->addMenu(menuName)); -} - -void MenuWrapper::setEnabled(bool enabled) { - _realMenu->setEnabled(enabled); -} - -QAction* MenuWrapper::addSeparator() { - return _realMenu->addSeparator(); -} - -void MenuWrapper::addAction(QAction* action) { - _realMenu->addAction(action); - VrMenu::executeOrQueue([=](VrMenu* vrMenu) { - vrMenu->addAction(_realMenu, action); - }); -} - -QAction* MenuWrapper::addAction(const QString& menuName) { - QAction* action = _realMenu->addAction(menuName); - VrMenu::executeOrQueue([=](VrMenu* vrMenu) { - vrMenu->addAction(_realMenu, action); - }); - return action; -} - -QAction* MenuWrapper::addAction(const QString& menuName, const QObject* receiver, const char* member, const QKeySequence& shortcut) { - QAction* action = _realMenu->addAction(menuName, receiver, member, shortcut); - VrMenu::executeOrQueue([=](VrMenu* vrMenu) { - vrMenu->addAction(_realMenu, action); - }); - return action; -} - -void MenuWrapper::removeAction(QAction* action) { - _realMenu->removeAction(action); - VrMenu::executeOrQueue([=](VrMenu* vrMenu) { - vrMenu->removeAction(action); - }); -} - -void MenuWrapper::insertAction(QAction* before, QAction* action) { - _realMenu->insertAction(before, action); - VrMenu::executeOrQueue([=](VrMenu* vrMenu) { - vrMenu->insertAction(before, action); - }); -} - -QHash MenuWrapper::_backMap; diff --git a/interface/src/Menu.h b/interface/src/Menu.h index ca5bf295db..fb00416af0 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -9,144 +9,21 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #ifndef hifi_Menu_h #define hifi_Menu_h -#include -#include -#include -#include -#include -#include +class MenuItemProperties; -#include - -#include "DiscoverabilityManager.h" - -class Settings; - -class MenuWrapper : public QObject { -public: - QList actions(); - MenuWrapper* addMenu(const QString& menuName); - void setEnabled(bool enabled = true); - QAction* addSeparator(); - void addAction(QAction* action); - - QAction* addAction(const QString& menuName); - void insertAction(QAction* before, QAction* menuName); - - QAction* addAction(const QString& menuName, const QObject* receiver, const char* member, const QKeySequence& shortcut = 0); - void removeAction(QAction* action); - - QAction* newAction() { - return new QAction(_realMenu); - } - -private: - MenuWrapper(QMenu* menu); - - static MenuWrapper* fromMenu(QMenu* menu) { - return _backMap[menu]; - } - - QMenu* const _realMenu; - static QHash _backMap; - friend class Menu; -}; - -class Menu : public QMenuBar { +class Menu : public ui::Menu { Q_OBJECT + public: - Menu(); + static void setInstance(); static Menu* getInstance(); - - void loadSettings(); - void saveSettings(); - - MenuWrapper* getMenu(const QString& menuName); - MenuWrapper* getSubMenuFromName(const QString& menuName, MenuWrapper* menu); - - void triggerOption(const QString& menuOption); - QAction* getActionForOption(const QString& menuOption); - - QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu, - const QString& actionName, - const QKeySequence& shortcut = 0, - const QObject* receiver = NULL, - const char* member = NULL, - QAction::MenuRole role = QAction::NoRole, - int menuItemLocation = UNSPECIFIED_POSITION, - const QString& grouping = QString()); - - QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu, - QAction* action, - const QString& actionName = QString(), - const QKeySequence& shortcut = 0, - QAction::MenuRole role = QAction::NoRole, - int menuItemLocation = UNSPECIFIED_POSITION, - const QString& grouping = QString()); - - QAction* addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu, - const QString& actionName, - const QKeySequence& shortcut = 0, - const bool checked = false, - const QObject* receiver = NULL, - const char* member = NULL, - int menuItemLocation = UNSPECIFIED_POSITION, - const QString& grouping = QString()); - - void removeAction(MenuWrapper* menu, const QString& actionName); - -public slots: - MenuWrapper* addMenu(const QString& menuName, const QString& grouping = QString()); - void removeMenu(const QString& menuName); - bool menuExists(const QString& menuName); - void addSeparator(const QString& menuName, const QString& separatorName, const QString& grouping = QString()); - void removeSeparator(const QString& menuName, const QString& separatorName); - void addMenuItem(const MenuItemProperties& properties); - void removeMenuItem(const QString& menuName, const QString& menuitem); - bool menuItemExists(const QString& menuName, const QString& menuitem); - void addActionGroup(const QString& groupName, const QStringList& actionList, const QString& selected = QString()); - void removeActionGroup(const QString& groupName); - bool isOptionChecked(const QString& menuOption) const; - void setIsOptionChecked(const QString& menuOption, bool isChecked); - - bool getGroupingIsVisible(const QString& grouping); - void setGroupingIsVisible(const QString& grouping, bool isVisible); /// NOTE: the "" grouping is always visible - - void toggleDeveloperMenus(); - void toggleAdvancedMenus(); - - static bool isSomeSubmenuShown() { return _isSomeSubmenuShown; } - -private: - typedef void(*settingsAction)(Settings&, QAction&); - static void loadAction(Settings& settings, QAction& action); - static void saveAction(Settings& settings, QAction& action); - void scanMenuBar(settingsAction modifySetting); - void scanMenu(QMenu& menu, settingsAction modifySetting, Settings& settings); - - /// helper method to have separators with labels that are also compatible with OS X - void addDisabledActionAndSeparator(MenuWrapper* destinationMenu, - const QString& actionName, - int menuItemLocation = UNSPECIFIED_POSITION, - const QString& grouping = QString()); - - QAction* getActionFromName(const QString& menuName, MenuWrapper* menu); - MenuWrapper* getMenuParent(const QString& menuName, QString& finalMenuPart); - - QAction* getMenuAction(const QString& menuName); - int findPositionOfMenuItem(MenuWrapper* menu, const QString& searchMenuItem); - int positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition); - - QHash _actionHash; - - bool isValidGrouping(const QString& grouping) const { return grouping == "Advanced" || grouping == "Developer"; } - QHash _groupingVisible; - QHash> _groupingActions; - - static bool _isSomeSubmenuShown; + Menu(); + Q_INVOKABLE void addMenuItem(const MenuItemProperties& properties); }; namespace MenuOption { @@ -302,3 +179,4 @@ namespace MenuOption { } #endif // hifi_Menu_h + diff --git a/interface/src/PluginContainerProxy.cpp b/interface/src/PluginContainerProxy.cpp index f8a9d907bb..8e5aca86a9 100644 --- a/interface/src/PluginContainerProxy.cpp +++ b/interface/src/PluginContainerProxy.cpp @@ -170,7 +170,7 @@ bool PluginContainerProxy::makeRenderingContextCurrent() { return qApp->_offscreenContext->makeCurrent(); } -void PluginContainerProxy::releaseSceneTexture(uint32_t texture) { +void PluginContainerProxy::releaseSceneTexture(const gpu::TexturePointer& texture) { Q_ASSERT(QThread::currentThread() == qApp->thread()); auto& framebufferMap = qApp->_lockedFramebufferMap; Q_ASSERT(framebufferMap.contains(texture)); @@ -180,7 +180,7 @@ void PluginContainerProxy::releaseSceneTexture(uint32_t texture) { framebufferCache->releaseFramebuffer(framebufferPointer); } -void PluginContainerProxy::releaseOverlayTexture(uint32_t texture) { +void PluginContainerProxy::releaseOverlayTexture(const gpu::TexturePointer& texture) { // FIXME implement present thread compositing } diff --git a/interface/src/PluginContainerProxy.h b/interface/src/PluginContainerProxy.h index c08cf97abb..eff1dcf938 100644 --- a/interface/src/PluginContainerProxy.h +++ b/interface/src/PluginContainerProxy.h @@ -25,8 +25,8 @@ class PluginContainerProxy : public QObject, PluginContainer { virtual void showDisplayPluginsTools() override; virtual void requestReset() override; virtual bool makeRenderingContextCurrent() override; - virtual void releaseSceneTexture(uint32_t texture) override; - virtual void releaseOverlayTexture(uint32_t texture) override; + virtual void releaseSceneTexture(const gpu::TexturePointer& texture) override; + virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) override; virtual GLWidget* getPrimaryWidget() override; virtual QWindow* getPrimaryWindow() override; virtual QOpenGLContext* getPrimaryContext() override; diff --git a/interface/src/scripting/HMDScriptingInterface.cpp b/interface/src/scripting/HMDScriptingInterface.cpp index d7013ad407..26f1c1dc77 100644 --- a/interface/src/scripting/HMDScriptingInterface.cpp +++ b/interface/src/scripting/HMDScriptingInterface.cpp @@ -13,7 +13,8 @@ #include -#include "display-plugins/DisplayPlugin.h" +#include +#include #include #include "Application.h" diff --git a/interface/src/scripting/MenuScriptingInterface.cpp b/interface/src/scripting/MenuScriptingInterface.cpp index 016c8df7e3..da55a8829f 100644 --- a/interface/src/scripting/MenuScriptingInterface.cpp +++ b/interface/src/scripting/MenuScriptingInterface.cpp @@ -86,10 +86,13 @@ bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString& void MenuScriptingInterface::addActionGroup(const QString& groupName, const QStringList& actionList, const QString& selected) { + static const char* slot = SLOT(menuItemTriggered()); QMetaObject::invokeMethod(Menu::getInstance(), "addActionGroup", Q_ARG(const QString&, groupName), Q_ARG(const QStringList&, actionList), - Q_ARG(const QString&, selected)); + Q_ARG(const QString&, selected), + Q_ARG(QObject*, this), + Q_ARG(const char*, slot)); } void MenuScriptingInterface::removeActionGroup(const QString& groupName) { diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp deleted file mode 100644 index efd69c7859..0000000000 --- a/interface/src/ui/ApplicationCompositor.cpp +++ /dev/null @@ -1,704 +0,0 @@ -// -// ApplicationCompositor.cpp -// interface/src/ui/overlays -// -// Created by Benjamin Arnold on 5/27/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "ApplicationCompositor.h" - -#include - -#include - -#include - -#include -#include -#include -#include - -#include "CursorManager.h" -#include "Tooltip.h" - -#include "Application.h" -#include - - -// Used to animate the magnification windows - -static const quint64 MSECS_TO_USECS = 1000ULL; -static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS; - -static const float reticleSize = TWO_PI / 100.0f; - -static const float CURSOR_PIXEL_SIZE = 32.0f; - -static gpu::BufferPointer _hemiVertices; -static gpu::BufferPointer _hemiIndices; -static int _hemiIndexCount{ 0 }; -EntityItemID ApplicationCompositor::_noItemId; -static QString _tooltipId; - -// Return a point's cartesian coordinates on a sphere from pitch and yaw -glm::vec3 getPoint(float yaw, float pitch) { - return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)), - glm::sin(-pitch), - glm::cos(-pitch) * (-glm::cos(yaw))); -} - -//Checks if the given ray intersects the sphere at the origin. result will store a multiplier that should -//be multiplied by dir and added to origin to get the location of the collision -bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r, float* result) -{ - //Source: http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection - - //Compute A, B and C coefficients - float a = glm::dot(dir, dir); - float b = 2 * glm::dot(dir, origin); - float c = glm::dot(origin, origin) - (r * r); - - //Find discriminant - float disc = b * b - 4 * a * c; - - // if discriminant is negative there are no real roots, so return - // false as ray misses sphere - if (disc < 0) { - return false; - } - - // compute q as described above - float distSqrt = sqrtf(disc); - float q; - if (b < 0) { - q = (-b - distSqrt) / 2.0f; - } else { - q = (-b + distSqrt) / 2.0f; - } - - // compute t0 and t1 - float t0 = q / a; - float t1 = c / q; - - // make sure t0 is smaller than t1 - if (t0 > t1) { - // if t0 is bigger than t1 swap them around - float temp = t0; - t0 = t1; - t1 = temp; - } - - // if t1 is less than zero, the object is in the ray's negative direction - // and consequently the ray misses the sphere - if (t1 < 0) { - return false; - } - - // if t0 is less than zero, the intersection point is at t1 - if (t0 < 0) { - *result = t1; - return true; - } else { // else the intersection point is at t0 - *result = t0; - return true; - } -} - -ApplicationCompositor::ApplicationCompositor() : - _alphaPropertyAnimation(new QPropertyAnimation(this, "alpha")), - _reticleInterface(new ReticleInterface(this)) - -{ - auto geometryCache = DependencyManager::get(); - - _reticleQuad = geometryCache->allocateID(); - - auto entityScriptingInterface = DependencyManager::get(); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) { - if (_hoverItemId != entityItemID) { - _hoverItemId = entityItemID; - _hoverItemEnterUsecs = usecTimestampNow(); - auto properties = entityScriptingInterface->getEntityProperties(_hoverItemId); - - // check the format of this href string before we parse it - QString hrefString = properties.getHref(); - - auto cursor = Cursor::Manager::instance().getCursor(); - if (!hrefString.isEmpty()) { - if (!hrefString.startsWith("hifi:")) { - hrefString.prepend("hifi://"); - } - - // parse out a QUrl from the hrefString - QUrl href = QUrl(hrefString); - - _hoverItemTitle = href.host(); - _hoverItemDescription = properties.getDescription(); - cursor->setIcon(Cursor::Icon::LINK); - } else { - _hoverItemTitle.clear(); - _hoverItemDescription.clear(); - cursor->setIcon(Cursor::Icon::DEFAULT); - } - } - }); - - connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) { - if (_hoverItemId == entityItemID) { - _hoverItemId = _noItemId; - - _hoverItemTitle.clear(); - _hoverItemDescription.clear(); - - auto cursor = Cursor::Manager::instance().getCursor(); - cursor->setIcon(Cursor::Icon::DEFAULT); - if (!_tooltipId.isEmpty()) { - qDebug() << "Closing tooltip " << _tooltipId; - Tooltip::closeTip(_tooltipId); - _tooltipId.clear(); - } - } - }); - - _alphaPropertyAnimation.reset(new QPropertyAnimation(this, "alpha")); -} - -ApplicationCompositor::~ApplicationCompositor() { -} - - -void ApplicationCompositor::bindCursorTexture(gpu::Batch& batch, uint8_t cursorIndex) { - auto& cursorManager = Cursor::Manager::instance(); - auto cursor = cursorManager.getCursor(cursorIndex); - auto iconId = cursor->getIcon(); - if (!_cursors.count(iconId)) { - auto iconPath = cursorManager.getIconImage(cursor->getIcon()); - _cursors[iconId] = DependencyManager::get()-> - getImageTexture(iconPath); - } - batch.setResourceTexture(0, _cursors[iconId]); -} - -// Draws the FBO texture for the screen -void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) { - PROFILE_RANGE(__FUNCTION__); - - if (_alpha <= 0.0f) { - return; - } - - gpu::FramebufferPointer overlayFramebuffer = qApp->getApplicationOverlay().getOverlayFramebuffer(); - if (!overlayFramebuffer) { - return; - } - - updateTooltips(); - - //Handle fading and deactivation/activation of UI - gpu::doInBatch(renderArgs->_context, [&](gpu::Batch& batch) { - - auto geometryCache = DependencyManager::get(); - - geometryCache->useSimpleDrawPipeline(batch); - batch.setViewportTransform(renderArgs->_viewport); - batch.setModelTransform(Transform()); - batch.setViewTransform(Transform()); - batch.setProjectionTransform(mat4()); - batch.setResourceTexture(0, overlayFramebuffer->getRenderBuffer(0)); - geometryCache->renderUnitQuad(batch, vec4(vec3(1), _alpha)); - - //draw the mouse pointer - if (getReticleVisible()) { - // Get the mouse coordinates and convert to NDC [-1, 1] - vec2 canvasSize = qApp->getCanvasSize(); // desktop, use actual canvas... - vec2 mousePosition = toNormalizedDeviceScale(vec2(qApp->getMouse()), canvasSize); - // Invert the Y axis - mousePosition.y *= -1.0f; - - Transform model; - model.setTranslation(vec3(mousePosition, 0)); - vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize; - model.setScale(vec3(mouseSize, 1.0f)); - batch.setModelTransform(model); - bindCursorTexture(batch); - geometryCache->renderUnitQuad(batch, vec4(1)); - } - }); -} - -// Draws the FBO texture for Oculus rift. -void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int eye) { - PROFILE_RANGE(__FUNCTION__); - - if (_alpha <= 0.0f) { - return; - } - - gpu::FramebufferPointer overlayFramebuffer = qApp->getApplicationOverlay().getOverlayFramebuffer(); - if (!overlayFramebuffer) { - return; - } - - updateTooltips(); - - glm::uvec2 screenSize = qApp->getUiSize(); // HMD use virtual screen size - vec2 canvasSize = screenSize; - _textureAspectRatio = aspect(canvasSize); - - auto geometryCache = DependencyManager::get(); - - gpu::doInBatch(renderArgs->_context, [&](gpu::Batch& batch) { - geometryCache->useSimpleDrawPipeline(batch); - - batch.setResourceTexture(0, overlayFramebuffer->getRenderBuffer(0)); - - mat4 camMat; - _cameraBaseTransform.getMatrix(camMat); - auto displayPlugin = qApp->getActiveDisplayPlugin(); - auto headPose = qApp->getHMDSensorPose(); - auto eyeToHead = displayPlugin->getEyeToHeadTransform((Eye)eye); - camMat = (headPose * eyeToHead) * camMat; // FIXME - why are not all transforms are doing this aditioanl eyeToHead - batch.setViewportTransform(renderArgs->_viewport); - batch.setViewTransform(camMat); - batch.setProjectionTransform(qApp->getEyeProjection(eye)); - - #ifdef DEBUG_OVERLAY - { - batch.setModelTransform(glm::translate(mat4(), vec3(0, 0, -2))); - geometryCache->renderUnitQuad(batch, glm::vec4(1)); - } - #else - { - batch.setModelTransform(_modelTransform); - drawSphereSection(batch); - } - #endif - - - vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize); - - bindCursorTexture(batch); - - //Mouse Pointer - if (getReticleVisible()) { - if (getReticleDepth() != 1.0f) { - // calculate the "apparent location" based on the depth and the current ray - glm::vec3 origin, direction; - auto reticlePosition = getReticlePosition(); - computeHmdPickRay(reticlePosition, origin, direction); - auto apparentPosition = origin + (direction * getReticleDepth()); - - // same code as used to render for apparent location - auto myCamera = qApp->getCamera(); - mat4 cameraMat = myCamera->getTransform(); - auto UITransform = cameraMat * glm::inverse(headPose); - auto relativePosition4 = glm::inverse(UITransform) * vec4(apparentPosition, 1); - auto relativePosition = vec3(relativePosition4) / relativePosition4.w; - auto relativeDistance = glm::length(relativePosition); - - // look at borrowed from overlays - float elevation = -asinf(relativePosition.y / glm::length(relativePosition)); - float azimuth = atan2f(relativePosition.x, relativePosition.z); - glm::quat faceCamera = glm::quat(glm::vec3(elevation, azimuth, 0)) * quat(vec3(0, -PI, 0)); // this extra *quat(vec3(0,-PI,0)) was required to get the quad to flip this seems like we could optimize - - Transform transform; - transform.setTranslation(relativePosition); - transform.setScale(reticleScale); - transform.postScale(relativeDistance); // scale not quite working, distant things too large - transform.setRotation(faceCamera); - batch.setModelTransform(transform); - } else { - glm::mat4 overlayXfm; - _modelTransform.getMatrix(overlayXfm); - - auto reticlePosition = getReticlePosition(); - glm::vec2 projection = overlayToSpherical(reticlePosition); - mat4 pointerXfm = glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); - mat4 reticleXfm = overlayXfm * pointerXfm; - reticleXfm = glm::scale(reticleXfm, reticleScale); - batch.setModelTransform(reticleXfm); - } - geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); - } - }); -} - -QPointF ApplicationCompositor::getMouseEventPosition(QMouseEvent* event) { - if (qApp->isHMDMode()) { - QMutexLocker locker(&_reticleLock); - return QPointF(_reticlePositionInHMD.x, _reticlePositionInHMD.y); - } - return event->localPos(); -} - -bool ApplicationCompositor::shouldCaptureMouse() const { - // if we're in HMD mode, and some window of ours is active, but we're not currently showing a popup menu - return _allowMouseCapture && qApp->isHMDMode() && QApplication::activeWindow() && !Menu::isSomeSubmenuShown(); -} - -void ApplicationCompositor::setAllowMouseCapture(bool capture) { - if (qApp->isHMDMode()) { - if (capture) { - qApp->showCursor(Qt::BlankCursor); - } else { - qApp->showCursor(Qt::ArrowCursor); - } - } - _allowMouseCapture = capture; -} - - -void ApplicationCompositor::handleLeaveEvent() { - - if (shouldCaptureMouse()) { - QWidget* mainWidget = (QWidget*)qApp->getWindow(); - QRect mainWidgetFrame = qApp->getRenderingGeometry(); - QRect uncoveredRect = mainWidgetFrame; - foreach(QWidget* widget, QApplication::topLevelWidgets()) { - if (widget->isWindow() && widget->isVisible() && widget != mainWidget) { - QRect widgetFrame = widget->frameGeometry(); - if (widgetFrame.intersects(uncoveredRect)) { - QRect intersection = uncoveredRect & widgetFrame; - if (intersection.top() > uncoveredRect.top()) { - uncoveredRect.setBottom(intersection.top() - 1); - } else if (intersection.bottom() < uncoveredRect.bottom()) { - uncoveredRect.setTop(intersection.bottom() + 1); - } - - if (intersection.left() > uncoveredRect.left()) { - uncoveredRect.setRight(intersection.left() - 1); - } else if (intersection.right() < uncoveredRect.right()) { - uncoveredRect.setLeft(intersection.right() + 1); - } - } - } - } - - _ignoreMouseMove = true; - auto sendToPos = uncoveredRect.center(); - QCursor::setPos(sendToPos); - _lastKnownRealMouse = sendToPos; - } -} - -bool ApplicationCompositor::handleRealMouseMoveEvent(bool sendFakeEvent) { - - // If the mouse move came from a capture mouse related move, we completely ignore it. - if (_ignoreMouseMove) { - _ignoreMouseMove = false; - return true; // swallow the event - } - - // If we're in HMD mode - if (shouldCaptureMouse()) { - QMutexLocker locker(&_reticleLock); - auto newPosition = QCursor::pos(); - auto changeInRealMouse = newPosition - _lastKnownRealMouse; - auto newReticlePosition = _reticlePositionInHMD + toGlm(changeInRealMouse); - setReticlePosition(newReticlePosition, sendFakeEvent); - _ignoreMouseMove = true; - QCursor::setPos(QPoint(_lastKnownRealMouse.x(), _lastKnownRealMouse.y())); // move cursor back to where it was - return true; // swallow the event - } else { - _lastKnownRealMouse = QCursor::pos(); - } - return false; // let the caller know to process the event -} - -glm::vec2 ApplicationCompositor::getReticlePosition() const { - if (qApp->isHMDMode()) { - QMutexLocker locker(&_reticleLock); - return _reticlePositionInHMD; - } - return toGlm(QCursor::pos()); -} - -bool ApplicationCompositor::getReticleOverDesktop() const { - // if the QML/Offscreen UI thinks we're over the desktop, then we are... - // but... if we're outside of the overlay area, we also want to call ourselves - // as being over the desktop. - if (qApp->isHMDMode()) { - QMutexLocker locker(&_reticleLock); - glm::vec2 maxOverlayPosition = qApp->getUiSize(); - if (_reticlePositionInHMD.x < 0 || _reticlePositionInHMD.y < 0 || - _reticlePositionInHMD.x > maxOverlayPosition.x || _reticlePositionInHMD.y > maxOverlayPosition.y) { - return true; // we are outside the overlay area, consider ourselves over the desktop - } - } - return _isOverDesktop; -} - - -void ApplicationCompositor::setReticlePosition(glm::vec2 position, bool sendFakeEvent) { - if (qApp->isHMDMode()) { - QMutexLocker locker(&_reticleLock); - const float MOUSE_EXTENTS_VERT_ANGULAR_SIZE = 170.0f; // 5deg from poles - const float MOUSE_EXTENTS_VERT_PIXELS = VIRTUAL_SCREEN_SIZE_Y * (MOUSE_EXTENTS_VERT_ANGULAR_SIZE / DEFAULT_HMD_UI_VERT_ANGULAR_SIZE); - const float MOUSE_EXTENTS_HORZ_ANGULAR_SIZE = 360.0f; // full sphere - const float MOUSE_EXTENTS_HORZ_PIXELS = VIRTUAL_SCREEN_SIZE_X * (MOUSE_EXTENTS_HORZ_ANGULAR_SIZE / DEFAULT_HMD_UI_HORZ_ANGULAR_SIZE); - - glm::vec2 maxOverlayPosition = qApp->getUiSize(); - float extaPixelsX = (MOUSE_EXTENTS_HORZ_PIXELS - maxOverlayPosition.x) / 2.0f; - float extaPixelsY = (MOUSE_EXTENTS_VERT_PIXELS - maxOverlayPosition.y) / 2.0f; - glm::vec2 mouseExtra { extaPixelsX, extaPixelsY }; - glm::vec2 minMouse = vec2(0) - mouseExtra; - glm::vec2 maxMouse = maxOverlayPosition + mouseExtra; - - _reticlePositionInHMD = glm::clamp(position, minMouse, maxMouse); - - if (sendFakeEvent) { - // in HMD mode we need to fake our mouse moves... - QPoint globalPos(_reticlePositionInHMD.x, _reticlePositionInHMD.y); - auto button = Qt::NoButton; - auto buttons = QApplication::mouseButtons(); - auto modifiers = QApplication::keyboardModifiers(); - QMouseEvent event(QEvent::MouseMove, globalPos, button, buttons, modifiers); - qApp->fakeMouseEvent(&event); - } - } else { - // NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies, - // remove it after we're done - const float REASONABLE_CHANGE = 50.0f; - glm::vec2 oldPos = toGlm(QCursor::pos()); - auto distance = glm::distance(oldPos, position); - if (distance > REASONABLE_CHANGE) { - qDebug() << "Contrller::ScriptingInterface ---- UNREASONABLE CHANGE! distance:" << distance << " oldPos:" << oldPos << " newPos:" << position; - } - - QCursor::setPos(position.x, position.y); - } -} - -#include - -glm::vec2 ApplicationCompositor::getReticleMaximumPosition() const { - glm::vec2 result; - if (qApp->isHMDMode()) { - result = glm::vec2(VIRTUAL_SCREEN_SIZE_X, VIRTUAL_SCREEN_SIZE_Y); - } else { - QRect rec = QApplication::desktop()->screenGeometry(); - result = glm::vec2(rec.right(), rec.bottom()); - } - return result; -} - -void ApplicationCompositor::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const { - auto surfacePointAt = sphereSurfaceFromOverlay(cursorPos); // in world space - glm::vec3 worldSpaceCameraPosition = qApp->getCamera()->getPosition(); - origin = worldSpaceCameraPosition; - direction = glm::normalize(surfacePointAt - worldSpaceCameraPosition); -} - -//Finds the collision point of a world space ray -bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const { - auto headPose = qApp->getHMDSensorPose(); - auto myCamera = qApp->getCamera(); - mat4 cameraMat = myCamera->getTransform(); - auto UITransform = cameraMat * glm::inverse(headPose); - auto relativePosition4 = glm::inverse(UITransform) * vec4(position, 1); - auto relativePosition = vec3(relativePosition4) / relativePosition4.w; - auto relativeDirection = glm::inverse(glm::quat_cast(UITransform)) * direction; - - float uiRadius = _oculusUIRadius; // * myAvatar->getUniformScale(); // FIXME - how do we want to handle avatar scale - - float instersectionDistance; - if (raySphereIntersect(relativeDirection, relativePosition, uiRadius, &instersectionDistance)){ - result = position + glm::normalize(direction) * instersectionDistance; - return true; - } - - return false; -} - -void ApplicationCompositor::buildHemiVertices( - const float fov, const float aspectRatio, const int slices, const int stacks) { - static float textureFOV = 0.0f, textureAspectRatio = 1.0f; - if (textureFOV == fov && textureAspectRatio == aspectRatio) { - return; - } - - textureFOV = fov; - textureAspectRatio = aspectRatio; - - auto geometryCache = DependencyManager::get(); - - _hemiVertices = std::make_shared(); - _hemiIndices = std::make_shared(); - - - if (fov >= PI) { - qDebug() << "TexturedHemisphere::buildVBO(): FOV greater or equal than Pi will create issues"; - } - - //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm - - vec3 pos; - vec2 uv; - // Compute vertices positions and texture UV coordinate - // Create and write to buffer - for (int i = 0; i < stacks; i++) { - uv.y = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f - // abs(theta) <= fov / 2.0f - float pitch = -fov * (uv.y - 0.5f); - for (int j = 0; j < slices; j++) { - uv.x = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f - // abs(phi) <= fov * aspectRatio / 2.0f - float yaw = -fov * aspectRatio * (uv.x - 0.5f); - pos = getPoint(yaw, pitch); - static const vec4 color(1); - _hemiVertices->append(sizeof(pos), (gpu::Byte*)&pos); - _hemiVertices->append(sizeof(vec2), (gpu::Byte*)&uv); - _hemiVertices->append(sizeof(vec4), (gpu::Byte*)&color); - } - } - - // Compute number of indices needed - static const int VERTEX_PER_TRANGLE = 3; - static const int TRIANGLE_PER_RECTANGLE = 2; - int numberOfRectangles = (slices - 1) * (stacks - 1); - _hemiIndexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE; - - // Compute indices order - std::vector indices; - for (int i = 0; i < stacks - 1; i++) { - for (int j = 0; j < slices - 1; j++) { - GLushort bottomLeftIndex = i * slices + j; - GLushort bottomRightIndex = bottomLeftIndex + 1; - GLushort topLeftIndex = bottomLeftIndex + slices; - GLushort topRightIndex = topLeftIndex + 1; - // FIXME make a z-order curve for better vertex cache locality - indices.push_back(topLeftIndex); - indices.push_back(bottomLeftIndex); - indices.push_back(topRightIndex); - - indices.push_back(topRightIndex); - indices.push_back(bottomLeftIndex); - indices.push_back(bottomRightIndex); - } - } - _hemiIndices->append(sizeof(GLushort) * indices.size(), (gpu::Byte*)&indices[0]); -} - - -void ApplicationCompositor::drawSphereSection(gpu::Batch& batch) { - buildHemiVertices(_textureFov, _textureAspectRatio, 80, 80); - static const int VERTEX_DATA_SLOT = 0; - static const int TEXTURE_DATA_SLOT = 1; - static const int COLOR_DATA_SLOT = 2; - auto streamFormat = std::make_shared(); // 1 for everyone - streamFormat->setAttribute(gpu::Stream::POSITION, VERTEX_DATA_SLOT, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); - streamFormat->setAttribute(gpu::Stream::TEXCOORD, TEXTURE_DATA_SLOT, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); - streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_DATA_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA)); - batch.setInputFormat(streamFormat); - - static const int VERTEX_STRIDE = sizeof(vec3) + sizeof(vec2) + sizeof(vec4); - - if (_prevAlpha != _alpha) { - // adjust alpha by munging vertex color alpha. - // FIXME we should probably just use a uniform for this. - float* floatPtr = reinterpret_cast(_hemiVertices->editData()); - const auto ALPHA_FLOAT_OFFSET = (sizeof(vec3) + sizeof(vec2) + sizeof(vec3)) / sizeof(float); - const auto VERTEX_FLOAT_STRIDE = (sizeof(vec3) + sizeof(vec2) + sizeof(vec4)) / sizeof(float); - const auto NUM_VERTS = _hemiVertices->getSize() / VERTEX_STRIDE; - for (size_t i = 0; i < NUM_VERTS; i++) { - floatPtr[i * VERTEX_FLOAT_STRIDE + ALPHA_FLOAT_OFFSET] = _alpha; - } - } - - gpu::BufferView posView(_hemiVertices, 0, _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::POSITION)._element); - gpu::BufferView uvView(_hemiVertices, sizeof(vec3), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::TEXCOORD)._element); - gpu::BufferView colView(_hemiVertices, sizeof(vec3) + sizeof(vec2), _hemiVertices->getSize(), VERTEX_STRIDE, streamFormat->getAttributes().at(gpu::Stream::COLOR)._element); - batch.setInputBuffer(VERTEX_DATA_SLOT, posView); - batch.setInputBuffer(TEXTURE_DATA_SLOT, uvView); - batch.setInputBuffer(COLOR_DATA_SLOT, colView); - batch.setIndexBuffer(gpu::UINT16, _hemiIndices, 0); - batch.drawIndexed(gpu::TRIANGLES, _hemiIndexCount); -} - -glm::vec2 ApplicationCompositor::sphericalToOverlay(const glm::vec2& sphericalPos) const { - glm::vec2 result = sphericalPos; - result.x *= -1.0f; - result /= _textureFov; - result.x /= _textureAspectRatio; - result += 0.5f; - result *= qApp->getUiSize(); - return result; -} - -glm::vec2 ApplicationCompositor::overlayToSpherical(const glm::vec2& overlayPos) const { - glm::vec2 result = overlayPos; - result /= qApp->getUiSize(); - result -= 0.5f; - result *= _textureFov; - result.x *= _textureAspectRatio; - result.x *= -1.0f; - return result; -} - -glm::vec2 ApplicationCompositor::overlayFromSphereSurface(const glm::vec3& sphereSurfacePoint) const { - auto headPose = qApp->getHMDSensorPose(); - auto myCamera = qApp->getCamera(); - mat4 cameraMat = myCamera->getTransform(); - auto UITransform = cameraMat * glm::inverse(headPose); - auto relativePosition4 = glm::inverse(UITransform) * vec4(sphereSurfacePoint, 1); - auto relativePosition = vec3(relativePosition4) / relativePosition4.w; - auto center = vec3(0); // center of HUD in HUD space - auto direction = relativePosition - center; // direction to relative position in HUD space - glm::vec2 polar = glm::vec2(glm::atan(direction.x, -direction.z), glm::asin(direction.y)) * -1.0f; - auto overlayPos = sphericalToOverlay(polar); - return overlayPos; -} - -glm::vec3 ApplicationCompositor::sphereSurfaceFromOverlay(const glm::vec2& overlay) const { - auto spherical = overlayToSpherical(overlay); - auto sphereSurfacePoint = getPoint(spherical.x, spherical.y); - auto headPose = qApp->getHMDSensorPose(); - auto myCamera = qApp->getCamera(); - mat4 cameraMat = myCamera->getTransform(); - auto UITransform = cameraMat * glm::inverse(headPose); - auto position4 = UITransform * vec4(sphereSurfacePoint, 1); - auto position = vec3(position4) / position4.w; - return position; -} - - -void ApplicationCompositor::updateTooltips() { - if (_hoverItemId != _noItemId) { - quint64 hoverDuration = usecTimestampNow() - _hoverItemEnterUsecs; - if (_hoverItemEnterUsecs != UINT64_MAX && !_hoverItemTitle.isEmpty() && hoverDuration > TOOLTIP_DELAY) { - // TODO Enable and position the tooltip - _hoverItemEnterUsecs = UINT64_MAX; - _tooltipId = Tooltip::showTip(_hoverItemTitle, _hoverItemDescription); - } - } -} - -static const float FADE_DURATION = 500.0f; -void ApplicationCompositor::fadeIn() { - _fadeInAlpha = true; - - _alphaPropertyAnimation->setDuration(FADE_DURATION); - _alphaPropertyAnimation->setStartValue(_alpha); - _alphaPropertyAnimation->setEndValue(1.0f); - _alphaPropertyAnimation->start(); -} -void ApplicationCompositor::fadeOut() { - _fadeInAlpha = false; - - _alphaPropertyAnimation->setDuration(FADE_DURATION); - _alphaPropertyAnimation->setStartValue(_alpha); - _alphaPropertyAnimation->setEndValue(0.0f); - _alphaPropertyAnimation->start(); -} - -void ApplicationCompositor::toggle() { - if (_fadeInAlpha) { - fadeOut(); - } else { - fadeIn(); - } -} diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 998a64d81a..14a3475610 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -244,34 +244,69 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderAr } } +static const auto COLOR_FORMAT = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); +static const auto DEFAULT_SAMPLER = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR); +static const auto DEPTH_FORMAT = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); + +std::mutex _textureGuard; +using Lock = std::unique_lock; +std::queue _availableTextures; + void ApplicationOverlay::buildFramebufferObject() { PROFILE_RANGE(__FUNCTION__); auto uiSize = qApp->getUiSize(); + if (!_overlayFramebuffer || uiSize != _overlayFramebuffer->getSize()) { + _overlayFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); + } - if (_overlayFramebuffer && uiSize == _overlayFramebuffer->getSize()) { - // Already built - return; + auto width = uiSize.x; + auto height = uiSize.y; + if (!_overlayFramebuffer->getDepthStencilBuffer()) { + auto overlayDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(DEPTH_FORMAT, width, height, DEFAULT_SAMPLER)); + _overlayFramebuffer->setDepthStencilBuffer(overlayDepthTexture, DEPTH_FORMAT); + } + + if (!_overlayFramebuffer->getRenderBuffer(0)) { + gpu::TexturePointer newColorAttachment; + { + Lock lock(_textureGuard); + if (!_availableTextures.empty()) { + newColorAttachment = _availableTextures.front(); + _availableTextures.pop(); + } + } + if (newColorAttachment) { + newColorAttachment->resize2D(width, height, newColorAttachment->getNumSamples()); + _overlayFramebuffer->setRenderBuffer(0, newColorAttachment); + } } - if (_overlayFramebuffer) { - _overlayFramebuffer.reset(); - _overlayDepthTexture.reset(); - _overlayColorTexture.reset(); + // If the overlay framebuffer still has no color attachment, no textures were available for rendering, so build a new one + if (!_overlayFramebuffer->getRenderBuffer(0)) { + auto colorBuffer = gpu::TexturePointer(gpu::Texture::create2D(COLOR_FORMAT, width, height, DEFAULT_SAMPLER)); + _overlayFramebuffer->setRenderBuffer(0, colorBuffer); + } +} + +gpu::TexturePointer ApplicationOverlay::acquireOverlay() { + if (!_overlayFramebuffer) { + return gpu::TexturePointer(); + } + auto result = _overlayFramebuffer->getRenderBuffer(0); + auto textureId = gpu::GLBackend::getTextureID(result, false); + if (!textureId) { + qDebug() << "Missing texture"; + } + _overlayFramebuffer->setRenderBuffer(0, gpu::TexturePointer()); + return result; +} + +void ApplicationOverlay::releaseOverlay(gpu::TexturePointer texture) { + if (texture) { + Lock lock(_textureGuard); + _availableTextures.push(texture); + } else { + qWarning() << "Attempted to release null texture"; } - - _overlayFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); - - auto colorFormat = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); - auto width = uiSize.x; - auto height = uiSize.y; - - auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR); - _overlayColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler)); - _overlayFramebuffer->setRenderBuffer(0, _overlayColorTexture); - - auto depthFormat = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); - _overlayDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, width, height, defaultSampler)); - - _overlayFramebuffer->setDepthStencilBuffer(_overlayDepthTexture, depthFormat); } diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index d4a88afff5..b77fcc6f89 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -26,7 +26,8 @@ public: void renderOverlay(RenderArgs* renderArgs); - gpu::FramebufferPointer getOverlayFramebuffer() const { return _overlayFramebuffer; } + gpu::TexturePointer acquireOverlay(); + void releaseOverlay(gpu::TexturePointer pointer); private: void renderStatsAndLogs(RenderArgs* renderArgs); diff --git a/interface/src/ui/OverlayConductor.cpp b/interface/src/ui/OverlayConductor.cpp index 113dad1ab5..95054869e5 100644 --- a/interface/src/ui/OverlayConductor.cpp +++ b/interface/src/ui/OverlayConductor.cpp @@ -9,6 +9,7 @@ // #include +#include #include "Application.h" #include "avatar/AvatarManager.h" diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 9f416f3117..9b1146340e 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "Application.h" #include "DialogsManager.h" diff --git a/libraries/display-plugins/CMakeLists.txt b/libraries/display-plugins/CMakeLists.txt index fad244fa5f..cfa2ac0710 100644 --- a/libraries/display-plugins/CMakeLists.txt +++ b/libraries/display-plugins/CMakeLists.txt @@ -1,16 +1,9 @@ set(TARGET_NAME display-plugins) setup_hifi_library(OpenGL) -link_hifi_libraries(shared plugins gl) +link_hifi_libraries(shared plugins gl ui) target_opengl() GroupSources("src/display-plugins") target_oglplus() - -if (WIN32) - add_dependency_external_projects(OpenVR) - find_package(OpenVR REQUIRED) - target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS}) - target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES}) -endif() diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index 6b111ad751..208307b49b 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -35,9 +35,9 @@ void Basic2DWindowOpenGLDisplayPlugin::activate() { updateFramerate(); } -void Basic2DWindowOpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) { +void Basic2DWindowOpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) { _wantVsync = true; // always - WindowOpenGLDisplayPlugin::submitSceneTexture(frameIndex, sceneTexture, sceneSize); + WindowOpenGLDisplayPlugin::submitSceneTexture(frameIndex, sceneTexture); } void Basic2DWindowOpenGLDisplayPlugin::internalPresent() { diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h index 23b5d32252..9c1ea204f3 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h @@ -24,7 +24,7 @@ public: virtual void activate() override; - virtual void submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) override; + virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override; virtual void internalPresent() override; diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp new file mode 100644 index 0000000000..ceaff0c0a1 --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp @@ -0,0 +1,485 @@ +// +// Created by Benjamin Arnold on 5/27/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "CompositorHelper.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// Used to animate the magnification windows + +static const quint64 MSECS_TO_USECS = 1000ULL; +//static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS; + +static const float reticleSize = TWO_PI / 100.0f; + +//EntityItemID CompositorHelper::_noItemId; +static QString _tooltipId; + +const uvec2 CompositorHelper::VIRTUAL_SCREEN_SIZE = uvec2(3960, 1188); // ~10% more pixel density than old version, 72dx240d FOV +const float CompositorHelper::VIRTUAL_UI_ASPECT_RATIO = (float)VIRTUAL_SCREEN_SIZE.x / (float)VIRTUAL_SCREEN_SIZE.y; +const vec2 CompositorHelper::VIRTUAL_UI_TARGET_FOV = vec2(PI * 3.0f / 2.0f, PI * 3.0f / 2.0f / VIRTUAL_UI_ASPECT_RATIO); +const vec2 CompositorHelper::MOUSE_EXTENTS_ANGULAR_SIZE = vec2(PI * 2.0f, PI * 0.95f); // horizontal: full sphere, vertical: ~5deg from poles +const vec2 CompositorHelper::MOUSE_EXTENTS_PIXELS = vec2(VIRTUAL_SCREEN_SIZE) * (MOUSE_EXTENTS_ANGULAR_SIZE / VIRTUAL_UI_TARGET_FOV); + +// Return a point's cartesian coordinates on a sphere from pitch and yaw +glm::vec3 getPoint(float yaw, float pitch) { + return glm::vec3(glm::cos(-pitch) * (-glm::sin(yaw)), + glm::sin(-pitch), + glm::cos(-pitch) * (-glm::cos(yaw))); +} + +// FIXME move to GLMHelpers +//Checks if the given ray intersects the sphere at the origin. result will store a multiplier that should +//be multiplied by dir and added to origin to get the location of the collision +bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r, float* result) +{ + //Source: http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection + + //Compute A, B and C coefficients + float a = glm::dot(dir, dir); + float b = 2 * glm::dot(dir, origin); + float c = glm::dot(origin, origin) - (r * r); + + //Find discriminant + float disc = b * b - 4 * a * c; + + // if discriminant is negative there are no real roots, so return + // false as ray misses sphere + if (disc < 0) { + return false; + } + + // compute q as described above + float distSqrt = sqrtf(disc); + float q; + if (b < 0) { + q = (-b - distSqrt) / 2.0f; + } else { + q = (-b + distSqrt) / 2.0f; + } + + // compute t0 and t1 + float t0 = q / a; + float t1 = c / q; + + // make sure t0 is smaller than t1 + if (t0 > t1) { + // if t0 is bigger than t1 swap them around + float temp = t0; + t0 = t1; + t1 = temp; + } + + // if t1 is less than zero, the object is in the ray's negative direction + // and consequently the ray misses the sphere + if (t1 < 0) { + return false; + } + + // if t0 is less than zero, the intersection point is at t1 + if (t0 < 0) { + *result = t1; + return true; + } else { // else the intersection point is at t0 + *result = t0; + return true; + } +} + +CompositorHelper::CompositorHelper() : + _alphaPropertyAnimation(new QPropertyAnimation(this, "alpha")), + _reticleInterface(new ReticleInterface(this)) +{ + // FIX in a separate PR addressing the current mouse over entity bug + //auto entityScriptingInterface = DependencyManager::get(); + //connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) { + // if (_hoverItemId != entityItemID) { + // _hoverItemId = entityItemID; + // _hoverItemEnterUsecs = usecTimestampNow(); + // auto properties = entityScriptingInterface->getEntityProperties(_hoverItemId); + + // // check the format of this href string before we parse it + // QString hrefString = properties.getHref(); + + // auto cursor = Cursor::Manager::instance().getCursor(); + // if (!hrefString.isEmpty()) { + // if (!hrefString.startsWith("hifi:")) { + // hrefString.prepend("hifi://"); + // } + + // // parse out a QUrl from the hrefString + // QUrl href = QUrl(hrefString); + + // _hoverItemTitle = href.host(); + // _hoverItemDescription = properties.getDescription(); + // cursor->setIcon(Cursor::Icon::LINK); + // } else { + // _hoverItemTitle.clear(); + // _hoverItemDescription.clear(); + // cursor->setIcon(Cursor::Icon::DEFAULT); + // } + // } + //}); + + //connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) { + // if (_hoverItemId == entityItemID) { + // _hoverItemId = _noItemId; + + // _hoverItemTitle.clear(); + // _hoverItemDescription.clear(); + + // auto cursor = Cursor::Manager::instance().getCursor(); + // cursor->setIcon(Cursor::Icon::DEFAULT); + // if (!_tooltipId.isEmpty()) { + // qDebug() << "Closing tooltip " << _tooltipId; + // Tooltip::closeTip(_tooltipId); + // _tooltipId.clear(); + // } + // } + //}); +} + + +bool CompositorHelper::isHMD() const { + return _currentDisplayPlugin && _currentDisplayPlugin->isHmd(); +} + +QPointF CompositorHelper::getMouseEventPosition(QMouseEvent* event) { + if (isHMD()) { + QMutexLocker locker(&_reticleLock); + return QPointF(_reticlePositionInHMD.x, _reticlePositionInHMD.y); + } + return event->localPos(); +} + +bool CompositorHelper::shouldCaptureMouse() const { + // if we're in HMD mode, and some window of ours is active, but we're not currently showing a popup menu + return _allowMouseCapture && isHMD() && QApplication::activeWindow() && !ui::Menu::isSomeSubmenuShown(); +} + +void CompositorHelper::setAllowMouseCapture(bool capture) { + if (capture != _allowMouseCapture) { + _allowMouseCapture = capture; + emit allowMouseCaptureChanged(); + } + _allowMouseCapture = capture; +} + +void CompositorHelper::handleLeaveEvent() { + if (shouldCaptureMouse()) { + + //QWidget* mainWidget = (QWidget*)qApp->getWindow(); + static auto renderingWidget = PluginContainer::getInstance().getPrimaryWidget(); + static QWidget* mainWidget = nullptr; + if (mainWidget == nullptr) { + mainWidget = renderingWidget->parentWidget(); + } + QRect mainWidgetFrame; + { + mainWidgetFrame = renderingWidget->geometry(); + auto topLeft = mainWidgetFrame.topLeft(); + auto topLeftScreen = renderingWidget->mapToGlobal(topLeft); + mainWidgetFrame.moveTopLeft(topLeftScreen); + } + QRect uncoveredRect = mainWidgetFrame; + foreach(QWidget* widget, QApplication::topLevelWidgets()) { + if (widget->isWindow() && widget->isVisible() && widget != mainWidget) { + QRect widgetFrame = widget->frameGeometry(); + if (widgetFrame.intersects(uncoveredRect)) { + QRect intersection = uncoveredRect & widgetFrame; + if (intersection.top() > uncoveredRect.top()) { + uncoveredRect.setBottom(intersection.top() - 1); + } else if (intersection.bottom() < uncoveredRect.bottom()) { + uncoveredRect.setTop(intersection.bottom() + 1); + } + + if (intersection.left() > uncoveredRect.left()) { + uncoveredRect.setRight(intersection.left() - 1); + } else if (intersection.right() < uncoveredRect.right()) { + uncoveredRect.setLeft(intersection.right() + 1); + } + } + } + } + + _ignoreMouseMove = true; + auto sendToPos = uncoveredRect.center(); + QCursor::setPos(sendToPos); + _lastKnownRealMouse = sendToPos; + } +} + + +bool CompositorHelper::handleRealMouseMoveEvent(bool sendFakeEvent) { + // If the mouse move came from a capture mouse related move, we completely ignore it. + if (_ignoreMouseMove) { + _ignoreMouseMove = false; + return true; // swallow the event + } + + // If we're in HMD mode + if (shouldCaptureMouse()) { + QMutexLocker locker(&_reticleLock); + auto newPosition = QCursor::pos(); + auto changeInRealMouse = newPosition - _lastKnownRealMouse; + auto newReticlePosition = _reticlePositionInHMD + toGlm(changeInRealMouse); + setReticlePosition(newReticlePosition, sendFakeEvent); + _ignoreMouseMove = true; + QCursor::setPos(QPoint(_lastKnownRealMouse.x(), _lastKnownRealMouse.y())); // move cursor back to where it was + return true; // swallow the event + } else { + _lastKnownRealMouse = QCursor::pos(); + } + return false; // let the caller know to process the event +} + +glm::vec2 CompositorHelper::getReticlePosition() const { + if (isHMD()) { + QMutexLocker locker(&_reticleLock); + return _reticlePositionInHMD; + } + return toGlm(QCursor::pos()); +} + +bool CompositorHelper::getReticleOverDesktop() const { + // if the QML/Offscreen UI thinks we're over the desktop, then we are... + // but... if we're outside of the overlay area, we also want to call ourselves + // as being over the desktop. + if (isHMD()) { + QMutexLocker locker(&_reticleLock); + glm::vec2 maxOverlayPosition = _currentDisplayPlugin->getRecommendedUiSize(); + static const glm::vec2 minOverlayPosition; + if (glm::any(glm::lessThan(_reticlePositionInHMD, minOverlayPosition)) || + glm::any(glm::greaterThan(_reticlePositionInHMD, maxOverlayPosition))) { + return true; + } + } + return _isOverDesktop; +} + +glm::vec2 CompositorHelper::getReticleMaximumPosition() const { + glm::vec2 result; + if (isHMD()) { + result = VIRTUAL_SCREEN_SIZE; + } else { + QRect rec = QApplication::desktop()->screenGeometry(); + result = glm::vec2(rec.right(), rec.bottom()); + } + return result; +} + +void CompositorHelper::setReticlePosition(const glm::vec2& position, bool sendFakeEvent) { + if (isHMD()) { + QMutexLocker locker(&_reticleLock); + glm::vec2 maxOverlayPosition = _currentDisplayPlugin->getRecommendedUiSize(); + // FIXME don't allow negative mouseExtra + glm::vec2 mouseExtra = (MOUSE_EXTENTS_PIXELS - maxOverlayPosition) / 2.0f; + glm::vec2 minMouse = vec2(0) - mouseExtra; + glm::vec2 maxMouse = maxOverlayPosition + mouseExtra; + + _reticlePositionInHMD = glm::clamp(position, minMouse, maxMouse); + + if (sendFakeEvent) { + // in HMD mode we need to fake our mouse moves... + QPoint globalPos(_reticlePositionInHMD.x, _reticlePositionInHMD.y); + auto button = Qt::NoButton; + auto buttons = QApplication::mouseButtons(); + auto modifiers = QApplication::keyboardModifiers(); + if (qApp->thread() == QThread::currentThread()) { + QMouseEvent event(QEvent::MouseMove, globalPos, button, buttons, modifiers); + qApp->sendEvent(qApp, &event); + } else { + qApp->postEvent(qApp, new QMouseEvent(QEvent::MouseMove, globalPos, button, buttons, modifiers)); + } + } + } else { + // NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies, + // remove it after we're done + const float REASONABLE_CHANGE = 50.0f; + glm::vec2 oldPos = toGlm(QCursor::pos()); + auto distance = glm::distance(oldPos, position); + if (distance > REASONABLE_CHANGE) { + qDebug() << "Contrller::ScriptingInterface ---- UNREASONABLE CHANGE! distance:" << + distance << " oldPos:" << oldPos.x << "," << oldPos.y << " newPos:" << position.x << "," << position.y; + } + + QCursor::setPos(position.x, position.y); + } +} + +void CompositorHelper::computeHmdPickRay(const glm::vec2& cursorPos, glm::vec3& origin, glm::vec3& direction) const { + auto surfacePointAt = sphereSurfaceFromOverlay(cursorPos); // in world space + origin = vec3(_currentCamera[3]); + direction = glm::normalize(surfacePointAt - origin); +} + +glm::mat4 CompositorHelper::getUiTransform() const { + return _currentCamera * glm::inverse(_currentDisplayPlugin->getHeadPose(_currentFrame)); +} + +//Finds the collision point of a world space ray +bool CompositorHelper::calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const { + auto UITransform = getUiTransform(); + auto relativePosition4 = glm::inverse(UITransform) * vec4(position, 1); + auto relativePosition = vec3(relativePosition4) / relativePosition4.w; + auto relativeDirection = glm::inverse(glm::quat_cast(UITransform)) * direction; + + float uiRadius = _oculusUIRadius; // * myAvatar->getUniformScale(); // FIXME - how do we want to handle avatar scale + + float instersectionDistance; + if (raySphereIntersect(relativeDirection, relativePosition, uiRadius, &instersectionDistance)){ + result = position + glm::normalize(direction) * instersectionDistance; + return true; + } + + return false; +} + +glm::vec2 CompositorHelper::sphericalToOverlay(const glm::vec2& sphericalPos) const { + glm::vec2 result = sphericalPos; + result.x *= -1.0f; + result /= _textureFov; + result.x /= _textureAspectRatio; + result += 0.5f; + result *= _currentDisplayPlugin->getRecommendedUiSize(); + return result; +} + +glm::vec2 CompositorHelper::overlayToSpherical(const glm::vec2& overlayPos) const { + glm::vec2 result = overlayPos; + result /= _currentDisplayPlugin->getRecommendedUiSize(); + result -= 0.5f; + result *= _textureFov; + result.x *= _textureAspectRatio; + result.x *= -1.0f; + return result; +} + +glm::vec2 CompositorHelper::overlayFromSphereSurface(const glm::vec3& sphereSurfacePoint) const { + auto UITransform = getUiTransform(); + auto relativePosition4 = glm::inverse(UITransform) * vec4(sphereSurfacePoint, 1); + auto direction = vec3(relativePosition4) / relativePosition4.w; + // FIXME use a GLMHelper cartesianToSpherical after fixing the rotation signs. + glm::vec2 polar = glm::vec2(glm::atan(direction.x, -direction.z), glm::asin(direction.y)) * -1.0f; + auto overlayPos = sphericalToOverlay(polar); + return overlayPos; +} + +glm::vec3 CompositorHelper::sphereSurfaceFromOverlay(const glm::vec2& overlay) const { + auto spherical = overlayToSpherical(overlay); + // FIXME use a GLMHelper sphericalToCartesian after fixing the rotation signs. + auto sphereSurfacePoint = getPoint(spherical.x, spherical.y); + auto UITransform = getUiTransform(); + auto position4 = UITransform * vec4(sphereSurfacePoint, 1); + return vec3(position4) / position4.w; +} + +void CompositorHelper::updateTooltips() { + //if (_hoverItemId != _noItemId) { + // quint64 hoverDuration = usecTimestampNow() - _hoverItemEnterUsecs; + // if (_hoverItemEnterUsecs != UINT64_MAX && !_hoverItemTitle.isEmpty() && hoverDuration > TOOLTIP_DELAY) { + // // TODO Enable and position the tooltip + // _hoverItemEnterUsecs = UINT64_MAX; + // _tooltipId = Tooltip::showTip(_hoverItemTitle, _hoverItemDescription); + // } + //} +} + +static const float FADE_DURATION = 500.0f; +void CompositorHelper::fadeIn() { + _fadeInAlpha = true; + + _alphaPropertyAnimation->setDuration(FADE_DURATION); + _alphaPropertyAnimation->setStartValue(_alpha); + _alphaPropertyAnimation->setEndValue(1.0f); + _alphaPropertyAnimation->start(); +} + +void CompositorHelper::fadeOut() { + _fadeInAlpha = false; + + _alphaPropertyAnimation->setDuration(FADE_DURATION); + _alphaPropertyAnimation->setStartValue(_alpha); + _alphaPropertyAnimation->setEndValue(0.0f); + _alphaPropertyAnimation->start(); +} + +void CompositorHelper::toggle() { + if (_fadeInAlpha) { + fadeOut(); + } else { + fadeIn(); + } +} + + +glm::mat4 CompositorHelper::getReticleTransform(const glm::mat4& eyePose, const glm::mat4& headPose) const { + glm::mat4 result; + if (isHMD()) { + vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize); + auto reticlePosition = getReticlePosition(); + if (getReticleDepth() != 1.0f) { + auto spherical = overlayToSpherical(reticlePosition); + auto sphereSurfacePoint = getPoint(spherical.x, spherical.y); + auto origin = vec3(headPose[3]); + auto direction = glm::normalize(sphereSurfacePoint - origin); + auto apparentPosition = (direction * getReticleDepth()); + + // same code as used to render for apparent location + auto relativePosition4 = glm::inverse(eyePose) * vec4(apparentPosition, 1); + auto relativePosition = vec3(relativePosition4) / relativePosition4.w; + auto relativeDistance = glm::length(relativePosition); + + // look at borrowed from overlays + float elevation = -asinf(relativePosition.y / glm::length(relativePosition)); + float azimuth = atan2f(relativePosition.x, relativePosition.z); + glm::quat faceCamera = glm::quat(glm::vec3(elevation, azimuth, 0)) * quat(vec3(0, -PI, 0)); // this extra *quat(vec3(0,-PI,0)) was required to get the quad to flip this seems like we could optimize + + Transform transform; + transform.setTranslation(relativePosition); + transform.setScale(reticleScale); + transform.postScale(relativeDistance); + transform.setRotation(faceCamera); + transform.getMatrix(result); + } else { + glm::mat4 overlayXfm; + _modelTransform.getMatrix(overlayXfm); + glm::vec2 projection = overlayToSpherical(reticlePosition); + mat4 pointerXfm = glm::inverse(eyePose) * glm::mat4_cast(quat(vec3(-projection.y, projection.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); + mat4 reticleXfm = overlayXfm * pointerXfm; + result = glm::scale(reticleXfm, reticleScale); + } + } else { + static const float CURSOR_PIXEL_SIZE = 32.0f; + static auto renderingWidget = PluginContainer::getInstance().getPrimaryWidget(); + const auto canvasSize = vec2(toGlm(renderingWidget->size()));; + vec2 mousePosition = toGlm(renderingWidget->mapFromGlobal(QCursor::pos())); + mousePosition /= canvasSize; + mousePosition *= 2.0; + mousePosition -= 1.0; + mousePosition.y *= -1.0f; + + vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize; + return glm::scale(glm::translate(glm::mat4(), vec3(mousePosition, 0.0f)), vec3(mouseSize, 1.0f)); + } + return result; +} diff --git a/interface/src/ui/ApplicationCompositor.h b/libraries/display-plugins/src/display-plugins/CompositorHelper.h similarity index 73% rename from interface/src/ui/ApplicationCompositor.h rename to libraries/display-plugins/src/display-plugins/CompositorHelper.h index 324250deb1..d5424282c7 100644 --- a/interface/src/ui/ApplicationCompositor.h +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.h @@ -6,59 +6,54 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_ApplicationCompositor_h -#define hifi_ApplicationCompositor_h +#ifndef hifi_display_plugins_Compositor_h +#define hifi_display_plugins_Compositor_h #include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include -#include -#include #include -#include -#include +#include +#include + +#include "DisplayPlugin.h" -class Camera; -class PalmData; -class RenderArgs; class ReticleInterface; - const float DEFAULT_RETICLE_DEPTH = 1.0f; // FIXME - probably should be based on UI radius const float MAGNIFY_WIDTH = 220.0f; const float MAGNIFY_HEIGHT = 100.0f; const float MAGNIFY_MULT = 2.0f; -const int VIRTUAL_SCREEN_SIZE_X = 3960; // ~10% more pixel density than old version, 72dx240d FOV -const int VIRTUAL_SCREEN_SIZE_Y = 1188; // ~10% more pixel density than old version, 72dx240d FOV -const float DEFAULT_HMD_UI_HORZ_ANGULAR_SIZE = 240.0f; -const float DEFAULT_HMD_UI_VERT_ANGULAR_SIZE = DEFAULT_HMD_UI_HORZ_ANGULAR_SIZE * (float)VIRTUAL_SCREEN_SIZE_Y / (float)VIRTUAL_SCREEN_SIZE_X; - // Handles the drawing of the overlays to the screen // TODO, move divide up the rendering, displaying and input handling // facilities of this class -class ApplicationCompositor : public QObject { +class CompositorHelper : public QObject, public Dependency { Q_OBJECT Q_PROPERTY(float alpha READ getAlpha WRITE setAlpha) Q_PROPERTY(bool reticleOverDesktop READ getReticleOverDesktop WRITE setReticleOverDesktop) public: - ApplicationCompositor(); - ~ApplicationCompositor(); + static const uvec2 VIRTUAL_SCREEN_SIZE; + static const float VIRTUAL_UI_ASPECT_RATIO; + static const vec2 VIRTUAL_UI_TARGET_FOV; + static const vec2 MOUSE_EXTENTS_ANGULAR_SIZE; + static const vec2 MOUSE_EXTENTS_PIXELS; - void displayOverlayTexture(RenderArgs* renderArgs); - void displayOverlayTextureHmd(RenderArgs* renderArgs, int eye); + CompositorHelper(); bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const; float getHmdUIAngularSize() const { return _hmdUIAngularSize; } void setHmdUIAngularSize(float hmdUIAngularSize) { _hmdUIAngularSize = hmdUIAngularSize; } + bool isHMD() const; // Converter from one frame of reference to another. // Frame of reference: @@ -66,8 +61,7 @@ public: // Overlay: Position on the overlay (x,y) glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const; glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const; - void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const; - uint32_t getOverlayTexture() const; + void computeHmdPickRay(const glm::vec2& cursorPos, glm::vec3& origin, glm::vec3& direction) const; glm::vec2 overlayFromSphereSurface(const glm::vec3& sphereSurfacePoint) const; glm::vec3 sphereSurfaceFromOverlay(const glm::vec2& overlay) const; @@ -93,10 +87,12 @@ public: void resetReticleDepth() { _reticleDepth = DEFAULT_RETICLE_DEPTH; } glm::vec2 getReticlePosition() const; - void setReticlePosition(glm::vec2 position, bool sendFakeEvent = true); + void setReticlePosition(const glm::vec2& position, bool sendFakeEvent = true); glm::vec2 getReticleMaximumPosition() const; + glm::mat4 getReticleTransform(const glm::mat4& eyePose = glm::mat4(), const glm::mat4& headPose = glm::mat4()) const; + ReticleInterface* getReticleInterface() { return _reticleInterface; } /// return value - true means the caller should not process the event further @@ -113,34 +109,38 @@ public: bool getReticleOverDesktop() const; void setReticleOverDesktop(bool value) { _isOverDesktop = value; } -private: - bool _isOverDesktop { true }; + void setDisplayPlugin(const DisplayPluginPointer& displayPlugin) { _currentDisplayPlugin = displayPlugin; } + void setFrameInfo(uint32_t frame, const glm::mat4& camera) { _currentCamera = camera; _currentFrame = frame; } - void displayOverlayTextureStereo(RenderArgs* renderArgs, float aspectRatio, float fov); - void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0); - void buildHemiVertices(const float fov, const float aspectRatio, const int slices, const int stacks); - void drawSphereSection(gpu::Batch& batch); +signals: + void allowMouseCaptureChanged(); + +private: + glm::mat4 getUiTransform() const; void updateTooltips(); - // Support for hovering and tooltips - static EntityItemID _noItemId; - EntityItemID _hoverItemId { _noItemId }; - QString _hoverItemTitle; - QString _hoverItemDescription; - quint64 _hoverItemEnterUsecs { 0 }; + DisplayPluginPointer _currentDisplayPlugin; + glm::mat4 _currentCamera; + uint32_t _currentFrame { 0 }; - float _hmdUIAngularSize { DEFAULT_HMD_UI_VERT_ANGULAR_SIZE }; - float _textureFov { glm::radians(DEFAULT_HMD_UI_VERT_ANGULAR_SIZE) }; - float _textureAspectRatio { 1.0f }; - int _hemiVerticesID { GeometryCache::UNKNOWN_ID }; + //// Support for hovering and tooltips + //static EntityItemID _noItemId; + //EntityItemID _hoverItemId { _noItemId }; - float _alpha { 0.0f }; // hidden by default + //QString _hoverItemTitle; + //QString _hoverItemDescription; + //quint64 _hoverItemEnterUsecs { 0 }; + + bool _isOverDesktop { true }; + float _hmdUIAngularSize { glm::degrees(VIRTUAL_UI_TARGET_FOV.y) }; + float _textureFov { VIRTUAL_UI_TARGET_FOV.y }; + float _textureAspectRatio { VIRTUAL_UI_ASPECT_RATIO }; + + float _alpha { 1.0f }; float _prevAlpha { 1.0f }; float _fadeInAlpha { true }; float _oculusUIRadius { 1.0f }; - QMap _cursors; - int _reticleQuad; int _previousBorderWidth { -1 }; @@ -182,7 +182,7 @@ class ReticleInterface : public QObject { Q_PROPERTY(bool pointingAtSystemOverlay READ isPointingAtSystemOverlay) public: - ReticleInterface(ApplicationCompositor* outer) : QObject(outer), _compositor(outer) {} + ReticleInterface(CompositorHelper* outer) : QObject(outer), _compositor(outer) {} Q_INVOKABLE bool isMouseCaptured() { return _compositor->shouldCaptureMouse(); } @@ -203,9 +203,7 @@ public: Q_INVOKABLE glm::vec2 getMaximumPosition() { return _compositor->getReticleMaximumPosition(); } private: - ApplicationCompositor* _compositor; + CompositorHelper* _compositor; }; - - -#endif // hifi_ApplicationCompositor_h +#endif // hifi_CompositorHelper_h diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp index e8145db900..6a8a69f634 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp @@ -22,11 +22,11 @@ bool NullDisplayPlugin::hasFocus() const { return false; } -void NullDisplayPlugin::submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) { +void NullDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) { _container->releaseSceneTexture(sceneTexture); } -void NullDisplayPlugin::submitOverlayTexture(uint32_t overlayTexture, const glm::uvec2& overlaySize) { +void NullDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overlayTexture) { _container->releaseOverlayTexture(overlayTexture); } diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h index 14b5529a29..ef0100f0b2 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h @@ -20,8 +20,8 @@ public: virtual glm::uvec2 getRecommendedRenderSize() const override; virtual bool hasFocus() const override; - virtual void submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) override; - virtual void submitOverlayTexture(uint32_t overlayTexture, const glm::uvec2& overlaySize) override; + virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override; + virtual void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override; virtual QImage getScreenshot() const override; private: static const QString NAME; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index ab9017d0fc..5cda3130d0 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include "CompositorHelper.h" #if THREADED_PRESENT @@ -184,13 +187,16 @@ private: #endif OpenGLDisplayPlugin::OpenGLDisplayPlugin() { - _sceneTextureEscrow.setRecycler([this](GLuint texture){ + _sceneTextureEscrow.setRecycler([this](const gpu::TexturePointer& texture){ cleanupForSceneTexture(texture); _container->releaseSceneTexture(texture); }); + _overlayTextureEscrow.setRecycler([this](const gpu::TexturePointer& texture) { + _container->releaseOverlayTexture(texture); + }); } -void OpenGLDisplayPlugin::cleanupForSceneTexture(uint32_t sceneTexture) { +void OpenGLDisplayPlugin::cleanupForSceneTexture(const gpu::TexturePointer& sceneTexture) { Lock lock(_mutex); Q_ASSERT(_sceneTextureToFrameIndexMap.contains(sceneTexture)); _sceneTextureToFrameIndexMap.remove(sceneTexture); @@ -198,15 +204,26 @@ void OpenGLDisplayPlugin::cleanupForSceneTexture(uint32_t sceneTexture) { void OpenGLDisplayPlugin::activate() { - _vsyncSupported = _container->getPrimaryWidget()->isVsyncSupported(); + if (!_cursorsData.size()) { + auto& cursorManager = Cursor::Manager::instance(); + for (const auto iconId : cursorManager.registeredIcons()) { + auto& cursorData = _cursorsData[iconId]; + auto iconPath = cursorManager.getIconImage(iconId); + auto image = QImage(iconPath); + image = image.mirrored(); + image = image.convertToFormat(QImage::Format_RGBA8888); + cursorData.image = image; + cursorData.size = toGlm(image.size()); + cursorData.hotSpot = vec2(0.5f); + } + } + _vsyncSupported = _container->getPrimaryWidget()->isVsyncSupported(); #if THREADED_PRESENT // Start the present thread if necessary auto presentThread = DependencyManager::get(); if (!presentThread) { auto widget = _container->getPrimaryWidget(); - - DependencyManager::set(); presentThread = DependencyManager::get(); presentThread->setObjectName("Presentation Thread"); @@ -242,6 +259,7 @@ void OpenGLDisplayPlugin::deactivate() { DisplayPlugin::deactivate(); } + void OpenGLDisplayPlugin::customizeContext() { #if THREADED_PRESENT _uncustomized = false; @@ -250,6 +268,20 @@ void OpenGLDisplayPlugin::customizeContext() { #endif enableVsync(); + for (auto& cursorValue : _cursorsData) { + auto& cursorData = cursorValue.second; + if (!cursorData.texture) { + const auto& image = cursorData.image; + glGenTextures(1, &cursorData.texture); + glBindTexture(GL_TEXTURE_2D, cursorData.texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image.constBits()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glGenerateMipmap(GL_TEXTURE_2D); + } + glBindTexture(GL_TEXTURE_2D, 0); + } + using namespace oglplus; Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha); Context::Disable(Capability::Blend); @@ -257,10 +289,26 @@ void OpenGLDisplayPlugin::customizeContext() { Context::Disable(Capability::CullFace); _program = loadDefaultShader(); + + auto uniforms = _program->ActiveUniforms(); + while (!uniforms.Empty()) { + auto uniform = uniforms.Front(); + if (uniform.Name() == "Projection") { + _projectionUniform = uniform.Index(); + } else if (uniform.Name() == "ModelView") { + _modelViewUniform = uniform.Index(); + } + uniforms.Next(); + } + _plane = loadPlane(_program); + + _compositeFramebuffer = std::make_shared(); + _compositeFramebuffer->Init(getRecommendedRenderSize()); } void OpenGLDisplayPlugin::uncustomizeContext() { + _compositeFramebuffer.reset(); _program.reset(); _plane.reset(); } @@ -308,7 +356,7 @@ bool OpenGLDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { return false; } -void OpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) { +void OpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) { { Lock lock(_mutex); _sceneTextureToFrameIndexMap[sceneTexture] = frameIndex; @@ -326,15 +374,27 @@ void OpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, uint32_t scene #endif } -void OpenGLDisplayPlugin::submitOverlayTexture(GLuint overlayTexture, const glm::uvec2& overlaySize) { +void OpenGLDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overlayTexture) { // Submit it to the presentation thread via escrow - _currentOverlayTexture = overlayTexture; + _overlayTextureEscrow.submit(overlayTexture); } void OpenGLDisplayPlugin::updateTextures() { + auto oldSceneTexture = _currentSceneTexture; _currentSceneTexture = _sceneTextureEscrow.fetchAndRelease(_currentSceneTexture); + if (oldSceneTexture != _currentSceneTexture) { + updateFrameData(); + } + + _currentOverlayTexture = _overlayTextureEscrow.fetchAndRelease(_currentOverlayTexture); } +void OpenGLDisplayPlugin::updateFrameData() { + Lock lock(_mutex); + _currentRenderFrameIndex = _sceneTextureToFrameIndexMap[_currentSceneTexture]; +} + + void OpenGLDisplayPlugin::updateFramerate() { uint64_t now = usecTimestampNow(); static uint64_t lastSwapEnd { now }; @@ -346,14 +406,85 @@ void OpenGLDisplayPlugin::updateFramerate() { } } +void OpenGLDisplayPlugin::compositeOverlay() { + using namespace oglplus; + // Overlay draw + if (isStereo()) { + Uniform(*_program, _projectionUniform).Set(mat4()); + Uniform(*_program, _modelViewUniform).Set(mat4()); + for_each_eye([&](Eye eye) { + eyeViewport(eye); + drawUnitQuad(); + }); + } else { + // Overlay draw + Uniform(*_program, _projectionUniform).Set(mat4()); + Uniform(*_program, _modelViewUniform).Set(mat4()); + drawUnitQuad(); + } +} + +void OpenGLDisplayPlugin::compositePointer() { + using namespace oglplus; + auto compositorHelper = DependencyManager::get(); + Uniform(*_program, _modelViewUniform).Set(compositorHelper->getReticleTransform(glm::mat4())); + if (isStereo()) { + for_each_eye([&](Eye eye) { + eyeViewport(eye); + drawUnitQuad(); + }); + } else { + drawUnitQuad(); + } + Uniform(*_program, _projectionUniform).Set(mat4()); + Uniform(*_program, _modelViewUniform).Set(mat4()); +} + +void OpenGLDisplayPlugin::compositeLayers() { + using namespace oglplus; + auto targetRenderSize = getRecommendedRenderSize(); + if (!_compositeFramebuffer || _compositeFramebuffer->size != targetRenderSize) { + _compositeFramebuffer = std::make_shared(); + _compositeFramebuffer->Init(targetRenderSize); + } + _compositeFramebuffer->Bound(Framebuffer::Target::Draw, [&] { + Context::Viewport(targetRenderSize.x, targetRenderSize.y); + Context::Clear().DepthBuffer(); + glBindTexture(GL_TEXTURE_2D, getSceneTextureId()); + _program->Bind(); + Uniform(*_program, _projectionUniform).Set(mat4()); + Uniform(*_program, _modelViewUniform).Set(mat4()); + drawUnitQuad(); + auto overlayTextureId = getOverlayTextureId(); + if (overlayTextureId) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBindTexture(GL_TEXTURE_2D, overlayTextureId); + compositeOverlay(); + + auto compositorHelper = DependencyManager::get(); + if (compositorHelper->getReticleVisible()) { + auto& cursorManager = Cursor::Manager::instance(); + const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()]; + glBindTexture(GL_TEXTURE_2D, cursorData.texture); + compositePointer(); + } + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_BLEND); + } + }); +} void OpenGLDisplayPlugin::internalPresent() { using namespace oglplus; - uvec2 size = getSurfacePixels(); - Context::Viewport(size.x, size.y); - Context::Clear().DepthBuffer(); - glBindTexture(GL_TEXTURE_2D, _currentSceneTexture); - drawUnitQuad(); + const uvec2& srcSize = _compositeFramebuffer->size; + uvec2 dstSize = getSurfacePixels(); + _compositeFramebuffer->Bound(FramebufferTarget::Read, [&] { + Context::BlitFramebuffer( + 0, 0, srcSize.x, srcSize.y, + 0, 0, dstSize.x, dstSize.y, + BufferSelectBit::ColorBuffer, BlitFilter::Nearest); + }); swapBuffers(); } @@ -361,6 +492,9 @@ void OpenGLDisplayPlugin::present() { incrementPresentCount(); updateTextures(); if (_currentSceneTexture) { + // Write all layers to a local framebuffer + compositeLayers(); + // Take the composite framebuffer and send it to the output device internalPresent(); updateFramerate(); } @@ -379,6 +513,7 @@ float OpenGLDisplayPlugin::presentRate() { void OpenGLDisplayPlugin::drawUnitQuad() { _program->Bind(); + _plane->Use(); _plane->Draw(); } @@ -436,3 +571,28 @@ void OpenGLDisplayPlugin::enableDeactivate() { _deactivateWait.notify_one(); } #endif + +uint32_t OpenGLDisplayPlugin::getSceneTextureId() const { + if (!_currentSceneTexture) { + return 0; + } + return gpu::GLBackend::getTextureID(_currentSceneTexture, false); +} + +uint32_t OpenGLDisplayPlugin::getOverlayTextureId() const { + if (!_currentOverlayTexture) { + return 0; + } + return gpu::GLBackend::getTextureID(_currentOverlayTexture, false); +} + +void OpenGLDisplayPlugin::eyeViewport(Eye eye) const { + using namespace oglplus; + uvec2 vpSize = _compositeFramebuffer->size; + vpSize.x /= 2; + uvec2 vpPos; + if (eye == Eye::Right) { + vpPos.x = vpSize.x; + } + Context::Viewport(vpPos.x, vpPos.y, vpSize.x, vpSize.y); +} diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 5ff9b22e1a..590123437f 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -25,6 +26,7 @@ protected: using Mutex = std::mutex; using Lock = std::unique_lock; using Condition = std::condition_variable; + using TextureEscrow = GLEscrow; public: OpenGLDisplayPlugin(); virtual void activate() override; @@ -32,8 +34,8 @@ public: virtual void stop() override; virtual bool eventFilter(QObject* receiver, QEvent* event) override; - virtual void submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) override; - virtual void submitOverlayTexture(uint32_t overlayTexture, const glm::uvec2& overlaySize) override; + virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override; + virtual void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override; virtual float presentRate() override; virtual glm::uvec2 getRecommendedRenderSize() const override { @@ -50,7 +52,13 @@ protected: #if THREADED_PRESENT friend class PresentThread; #endif + uint32_t getSceneTextureId() const; + uint32_t getOverlayTextureId() const; + void compositeLayers(); + virtual void compositeOverlay(); + virtual void compositePointer(); + virtual glm::uvec2 getSurfaceSize() const = 0; virtual glm::uvec2 getSurfacePixels() const = 0; @@ -61,32 +69,49 @@ protected: // These functions must only be called on the presentation thread virtual void customizeContext(); virtual void uncustomizeContext(); - virtual void cleanupForSceneTexture(uint32_t sceneTexture); - void withMainThreadContext(std::function f) const; + virtual void cleanupForSceneTexture(const gpu::TexturePointer& sceneTexture); + // Plugin specific functionality to send the composed scene to the output window or device + virtual void internalPresent(); + void withMainThreadContext(std::function f) const; void present(); void updateTextures(); void updateFramerate(); void drawUnitQuad(); void swapBuffers(); - // Plugin specific functionality to composite the scene and overlay and present the result - virtual void internalPresent(); + void eyeViewport(Eye eye) const; + + virtual void updateFrameData(); ProgramPtr _program; + int32_t _modelViewUniform { -1 }; + int32_t _projectionUniform { -1 }; ShapeWrapperPtr _plane; mutable Mutex _mutex; SimpleMovingAverage _usecsPerFrame { 10 }; - QMap _sceneTextureToFrameIndexMap; + QMap _sceneTextureToFrameIndexMap; + uint32_t _currentRenderFrameIndex { 0 }; - GLuint _currentSceneTexture { 0 }; - GLuint _currentOverlayTexture { 0 }; + gpu::TexturePointer _currentSceneTexture; + gpu::TexturePointer _currentOverlayTexture; - GLTextureEscrow _sceneTextureEscrow; + TextureEscrow _sceneTextureEscrow; + TextureEscrow _overlayTextureEscrow; bool _vsyncSupported { false }; + struct CursorData { + QImage image; + vec2 hotSpot; + uvec2 size; + uint32_t texture { 0 }; + }; + + std::map _cursorsData; + BasicFramebufferWrapperPtr _compositeFramebuffer; + private: #if THREADED_PRESENT void enableDeactivate(); diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 7b96987348..056378e094 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -11,16 +11,25 @@ #include #include +#include #include #include #include +#include +#include + #include "../Logging.h" +#include "../CompositorHelper.h" static const QString MONO_PREVIEW = "Mono Preview"; static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate"; static const bool DEFAULT_MONO_VIEW = true; +glm::uvec2 HmdDisplayPlugin::getRecommendedUiSize() const { + return CompositorHelper::VIRTUAL_SCREEN_SIZE; +} + void HmdDisplayPlugin::activate() { _monoPreview = _container->getBoolSetting("monoPreview", DEFAULT_MONO_VIEW); @@ -30,21 +39,60 @@ void HmdDisplayPlugin::activate() { _container->setBoolSetting("monoPreview", _monoPreview); }, true, _monoPreview); _container->removeMenu(FRAMERATE); - WindowOpenGLDisplayPlugin::activate(); + Parent::activate(); } void HmdDisplayPlugin::deactivate() { - WindowOpenGLDisplayPlugin::deactivate(); + Parent::deactivate(); } void HmdDisplayPlugin::customizeContext() { - WindowOpenGLDisplayPlugin::customizeContext(); + Parent::customizeContext(); // Only enable mirroring if we know vsync is disabled enableVsync(false); _enablePreview = !isVsyncEnabled(); + _sphereSection = loadSphereSection(_program, CompositorHelper::VIRTUAL_UI_TARGET_FOV.y, CompositorHelper::VIRTUAL_UI_ASPECT_RATIO); +} + +void HmdDisplayPlugin::uncustomizeContext() { + _sphereSection.reset(); + _compositeFramebuffer.reset(); + Parent::uncustomizeContext(); +} + +void HmdDisplayPlugin::compositeOverlay() { + using namespace oglplus; + _sphereSection->Use(); + for_each_eye([&](Eye eye) { + eyeViewport(eye); + auto modelView = glm::inverse(_currentRenderEyePoses[eye]); // *glm::translate(mat4(), vec3(0, 0, -1)); + Uniform(*_program, _modelViewUniform).Set(modelView); + Uniform(*_program, _projectionUniform).Set(_eyeProjections[eye]); + _sphereSection->Draw(); + }); +} + +void HmdDisplayPlugin::compositePointer() { + //Mouse Pointer + auto compositorHelper = DependencyManager::get(); + _plane->Use(); + // Reconstruct the headpose from the eye poses + auto currentHeadPose = _currentRenderEyePoses[Left]; + currentHeadPose[3] = vec4((vec3(_currentRenderEyePoses[Left][3]) + vec3(_currentRenderEyePoses[Right][3])) / 2.0f, 1.0f); + for_each_eye([&](Eye eye) { + using namespace oglplus; + eyeViewport(eye); + auto reticleTransform = compositorHelper->getReticleTransform(_currentRenderEyePoses[eye], currentHeadPose); + Uniform(*_program, _modelViewUniform).Set(reticleTransform); + Uniform(*_program, _projectionUniform).Set(_eyeProjections[eye]); + _plane->Draw(); + }); } void HmdDisplayPlugin::internalPresent() { + // Composite together the scene, overlay and mouse cursor + hmdPresent(); + // screen preview mirroring if (_enablePreview) { auto windowSize = toGlm(_window->size()); @@ -69,19 +117,30 @@ void HmdDisplayPlugin::internalPresent() { targetViewportPosition.y = (windowSize.y - targetViewportSize.y) / 2; } - glClear(GL_COLOR_BUFFER_BIT); - glViewport( - targetViewportPosition.x, targetViewportPosition.y, - targetViewportSize.x * (_monoPreview ? 2 : 1), targetViewportSize.y); - glEnable(GL_SCISSOR_TEST); - glScissor( - targetViewportPosition.x, targetViewportPosition.y, - targetViewportSize.x, targetViewportSize.y); - glBindTexture(GL_TEXTURE_2D, _currentSceneTexture); - GLenum err = glGetError(); - Q_ASSERT(0 == err); - drawUnitQuad(); - glDisable(GL_SCISSOR_TEST); + using namespace oglplus; + Context::Clear().ColorBuffer(); + auto sourceSize = _compositeFramebuffer->size; + if (_monoPreview) { + sourceSize.x /= 2; + } + _compositeFramebuffer->Bound(Framebuffer::Target::Read, [&] { + Context::BlitFramebuffer( + 0, 0, sourceSize.x, sourceSize.y, + targetViewportPosition.x, targetViewportPosition.y, + targetViewportPosition.x + targetViewportSize.x, targetViewportPosition.y + targetViewportSize.y, + BufferSelectBit::ColorBuffer, BlitFilter::Nearest); + }); swapBuffers(); } } + +void HmdDisplayPlugin::setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) { + Lock lock(_mutex); + _renderEyePoses[frameIndex][eye] = pose; +} + +void HmdDisplayPlugin::updateFrameData() { + Parent::updateFrameData(); + Lock lock(_mutex); + _currentRenderEyePoses = _renderEyePoses[_currentRenderFrameIndex]; +} \ No newline at end of file diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h index 227f377e1b..f16a474f74 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h @@ -12,34 +12,41 @@ #include "../WindowOpenGLDisplayPlugin.h" class HmdDisplayPlugin : public WindowOpenGLDisplayPlugin { + using Parent = WindowOpenGLDisplayPlugin; public: bool isHmd() const override final { return true; } float getIPD() const override final { return _ipd; } glm::mat4 getEyeToHeadTransform(Eye eye) const override final { return _eyeOffsets[eye]; } glm::mat4 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const override final { return _eyeProjections[eye]; } glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override final { return _cullingProjection; } - glm::uvec2 getRecommendedUiSize() const override final { - // FIXME - would be good to have these values sync with ApplicationCompositor in a better way. - const int VIRTUAL_SCREEN_SIZE_X = 3960; // ~10% more pixel density than old version, 72dx240d FOV - const int VIRTUAL_SCREEN_SIZE_Y = 1188; // ~10% more pixel density than old version, 72dx240d FOV - auto result = uvec2(VIRTUAL_SCREEN_SIZE_X, VIRTUAL_SCREEN_SIZE_Y); - return result; - } + glm::uvec2 getRecommendedUiSize() const override final; glm::uvec2 getRecommendedRenderSize() const override final { return _renderTargetSize; } + void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) override final; + void activate() override; void deactivate() override; protected: + virtual void hmdPresent() = 0; + void compositeOverlay() override; + void compositePointer() override; void internalPresent() override; void customizeContext() override; + void uncustomizeContext() override; + void updateFrameData() override; std::array _eyeOffsets; std::array _eyeProjections; glm::mat4 _cullingProjection; glm::uvec2 _renderTargetSize; float _ipd { 0.064f }; + using EyePoses = std::array; + QMap _renderEyePoses; + EyePoses _currentRenderEyePoses; + private: bool _enablePreview { false }; bool _monoPreview { true }; + ShapeWrapperPtr _sphereSection; }; diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp index 40c6e6306b..b7aa680984 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp @@ -46,13 +46,15 @@ void main() { const QString InterleavedStereoDisplayPlugin::NAME("3D TV - Interleaved"); -InterleavedStereoDisplayPlugin::InterleavedStereoDisplayPlugin() { -} - void InterleavedStereoDisplayPlugin::customizeContext() { StereoDisplayPlugin::customizeContext(); // Set up the stencil buffers? Or use a custom shader? - compileProgram(_program, INTERLEAVED_TEXTURED_VS, INTERLEAVED_TEXTURED_FS); + compileProgram(_interleavedProgram, INTERLEAVED_TEXTURED_VS, INTERLEAVED_TEXTURED_FS); +} + +void InterleavedStereoDisplayPlugin::uncustomizeContext() { + _interleavedProgram.reset(); + StereoDisplayPlugin::uncustomizeContext(); } glm::uvec2 InterleavedStereoDisplayPlugin::getRecommendedRenderSize() const { @@ -64,8 +66,14 @@ glm::uvec2 InterleavedStereoDisplayPlugin::getRecommendedRenderSize() const { void InterleavedStereoDisplayPlugin::internalPresent() { using namespace oglplus; - _program->Bind(); auto sceneSize = getRecommendedRenderSize(); - Uniform(*_program, "textureSize").SetValue(sceneSize); - WindowOpenGLDisplayPlugin::internalPresent(); + _interleavedProgram->Bind(); + Uniform(*_interleavedProgram, "textureSize").SetValue(sceneSize); + auto surfaceSize = getSurfacePixels(); + Context::Viewport(0, 0, surfaceSize.x, surfaceSize.y); + glBindTexture(GL_TEXTURE_2D, GetName(_compositeFramebuffer->color)); + _plane->Use(); + _plane->Draw(); + swapBuffers(); } + diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h index df2a9f4800..8c133fc877 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h @@ -12,17 +12,17 @@ class InterleavedStereoDisplayPlugin : public StereoDisplayPlugin { Q_OBJECT public: - InterleavedStereoDisplayPlugin(); - - virtual const QString& getName() const override { return NAME; } - virtual grouping getGrouping() const override { return ADVANCED; } + const QString& getName() const override { return NAME; } + grouping getGrouping() const override { return ADVANCED; } + glm::uvec2 getRecommendedRenderSize() const override; +protected: // initialize OpenGL context settings needed by the plugin - virtual void customizeContext() override; - - virtual glm::uvec2 getRecommendedRenderSize() const override; + void customizeContext() override; + void uncustomizeContext() override; void internalPresent() override; private: + ProgramPtr _interleavedProgram; static const QString NAME; }; diff --git a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp index e8a83d2b08..46533d1c16 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp @@ -8,6 +8,10 @@ #include "SideBySideStereoDisplayPlugin.h" #include +#include +#include +#include +#include "../CompositorHelper.h" const QString SideBySideStereoDisplayPlugin::NAME("3D TV - Side by Side Stereo"); @@ -19,5 +23,3 @@ glm::uvec2 SideBySideStereoDisplayPlugin::getRecommendedRenderSize() const { result.x *= 2; return result; } - - diff --git a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h index 70f69ba4cb..ab4e37f4da 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h @@ -18,6 +18,7 @@ public: virtual const QString& getName() const override { return NAME; } virtual grouping getGrouping() const override { return ADVANCED; } virtual glm::uvec2 getRecommendedRenderSize() const override; + private: static const QString NAME; }; diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index 47a80cb6ba..13df8d519b 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -8,19 +8,18 @@ #include "StereoDisplayPlugin.h" -#include -#include -#include +#include +#include +#include +#include #include #include #include #include -#include -#include - -StereoDisplayPlugin::StereoDisplayPlugin() { -} +#include +#include +#include "../CompositorHelper.h" bool StereoDisplayPlugin::isSupported() const { // FIXME this should attempt to do a scan for supported 3D output @@ -76,14 +75,16 @@ void StereoDisplayPlugin::activate() { _container->removeMenu(FRAMERATE); - _container->setFullscreen(qApp->primaryScreen()); + _screen = qApp->primaryScreen(); + _container->setFullscreen(_screen); WindowOpenGLDisplayPlugin::activate(); } void StereoDisplayPlugin::updateScreen() { for (uint32_t i = 0; i < _screenActions.size(); ++i) { if (_screenActions[i]->isChecked()) { - _container->setFullscreen(qApp->screens().at(i)); + _screen = qApp->screens().at(i); + _container->setFullscreen(_screen); break; } } @@ -100,3 +101,4 @@ void StereoDisplayPlugin::deactivate() { float StereoDisplayPlugin::getRecommendedAspectRatio() const { return aspect(WindowOpenGLDisplayPlugin::getRecommendedRenderSize()); } + diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h index c9997fd7ec..1f650bd3d6 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h @@ -8,11 +8,12 @@ #pragma once #include "../WindowOpenGLDisplayPlugin.h" +class QScreen; class StereoDisplayPlugin : public WindowOpenGLDisplayPlugin { Q_OBJECT + using Parent = WindowOpenGLDisplayPlugin; public: - StereoDisplayPlugin(); virtual bool isStereo() const override final { return true; } virtual bool isSupported() const override final; @@ -33,4 +34,5 @@ public: protected: void updateScreen(); float _ipd{ 0.064f }; + QScreen* _screen; }; diff --git a/libraries/gl/src/gl/GLEscrow.h b/libraries/gl/src/gl/GLEscrow.h index 7e126835ab..f3332a2482 100644 --- a/libraries/gl/src/gl/GLEscrow.h +++ b/libraries/gl/src/gl/GLEscrow.h @@ -42,13 +42,19 @@ // in use by the GPU. Fence sync objects are used to moderate the actual release of // resources in either direction. template < - typename T, - // Only accept numeric types - typename = typename std::enable_if::value, T>::type + typename T + //, + //// Only accept numeric types + //typename = typename std::enable_if::value, T>::type > class GLEscrow { public: static const uint64_t MAX_UNSIGNALED_TIME = USECS_PER_SECOND / 2; + + const T& invalid() const { + static const T INVALID_RESULT; + return INVALID_RESULT; + } struct Item { const T _value; @@ -133,7 +139,7 @@ public: // or if none is available (which could mean either the submission // list is empty or that the first item on the list isn't yet signaled T fetch() { - T result{0}; + T result = invalid(); // On the one hand using try_lock() reduces the chance of blocking the consumer thread, // but if the produce thread is going fast enough, it could effectively // starve the consumer out of ever actually getting resources. @@ -151,7 +157,7 @@ public: // or if none is available (which could mean either the submission // list is empty or that the first item on the list isn't yet signaled // Also releases any previous texture held by the caller - T fetchAndRelease(T oldValue) { + T fetchAndRelease(const T& oldValue) { T result = fetch(); if (!result) { return oldValue; @@ -164,7 +170,7 @@ public: // If fetch returns a non-zero value, it's the responsibility of the // client to release it at some point - void release(T t, GLsync readSync = 0) { + void release(const T& t, GLsync readSync = 0) { if (!readSync) { // FIXME should the release and submit actually force the creation of a fence? readSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); @@ -240,7 +246,7 @@ private: // inside the locked sections, so it cannot have any latency if (item.signaled()) { // if the sync is signaled, queue it for deletion - _trash.push_front(Item(0, item._sync)); + _trash.push_front(Item(invalid(), item._sync)); // And change the stored value to 0 so we don't check it again item._sync = 0; return true; @@ -259,6 +265,12 @@ private: List _trash; }; +template<> +inline const GLuint& GLEscrow::invalid() const { + static const GLuint INVALID_RESULT { 0 }; + return INVALID_RESULT; +} + using GLTextureEscrow = GLEscrow; #endif diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index 6ec162585b..d725d6b1ee 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -485,7 +485,9 @@ void OffscreenQmlSurface::updateQuick() { if (_render) { QMutexLocker lock(&(_renderer->_mutex)); _renderer->post(RENDER); - _renderer->_cond.wait(&(_renderer->_mutex)); + while (!_renderer->_cond.wait(&(_renderer->_mutex), 100)) { + qApp->processEvents(); + } _render = false; } diff --git a/libraries/gl/src/gl/OglplusHelpers.cpp b/libraries/gl/src/gl/OglplusHelpers.cpp index 619ef656d2..aa129bb755 100644 --- a/libraries/gl/src/gl/OglplusHelpers.cpp +++ b/libraries/gl/src/gl/OglplusHelpers.cpp @@ -15,14 +15,17 @@ using namespace oglplus::shapes; static const char * SIMPLE_TEXTURED_VS = R"VS(#version 410 core #pragma line __LINE__ +uniform mat4 Projection = mat4(1); +uniform mat4 ModelView = mat4(1); + in vec3 Position; in vec2 TexCoord; out vec2 vTexCoord; void main() { - gl_Position = vec4(Position, 1); - vTexCoord = TexCoord; + gl_Position = Projection * ModelView * vec4(Position, 1); + vTexCoord = TexCoord ; } )VS"; diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 15338d7587..39b54c109b 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -83,7 +83,7 @@ public: ~GLTexture(); }; static GLTexture* syncGPUObject(const Texture& texture); - static GLuint getTextureID(const TexturePointer& texture); + static GLuint getTextureID(const TexturePointer& texture, bool sync = true); // very specific for now static void syncSampler(const Sampler& sampler, Texture::Type type, GLTexture* object); diff --git a/libraries/gpu/src/gpu/GLBackendTexture.cpp b/libraries/gpu/src/gpu/GLBackendTexture.cpp index efcafc9d60..a70904a4bf 100755 --- a/libraries/gpu/src/gpu/GLBackendTexture.cpp +++ b/libraries/gpu/src/gpu/GLBackendTexture.cpp @@ -578,11 +578,16 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) { -GLuint GLBackend::getTextureID(const TexturePointer& texture) { +GLuint GLBackend::getTextureID(const TexturePointer& texture, bool sync) { if (!texture) { return 0; } - GLTexture* object = GLBackend::syncGPUObject(*texture); + GLTexture* object { nullptr }; + if (sync) { + object = GLBackend::syncGPUObject(*texture); + } else { + object = Backend::getGPUObject(*texture); + } if (object) { return object->_texture; } else { diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp old mode 100755 new mode 100644 diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index 6dcc79b601..c8fba1670d 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -50,6 +50,11 @@ class QWindow; #define AVERAGE_HUMAN_IPD 0.064f +namespace gpu { + class Texture; + using TexturePointer = std::shared_ptr; +} + class DisplayPlugin : public Plugin { Q_OBJECT public: @@ -70,12 +75,12 @@ public: /** * Sends the scene texture to the display plugin. */ - virtual void submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) = 0; + virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) = 0; /** * Sends the scene texture to the display plugin. */ - virtual void submitOverlayTexture(uint32_t overlayTexture, const glm::uvec2& overlaySize) = 0; + virtual void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) = 0; // Does the rendering surface have current focus? virtual bool hasFocus() const = 0; diff --git a/libraries/plugins/src/plugins/PluginContainer.h b/libraries/plugins/src/plugins/PluginContainer.h index a32b50cb94..b5ef4b0d55 100644 --- a/libraries/plugins/src/plugins/PluginContainer.h +++ b/libraries/plugins/src/plugins/PluginContainer.h @@ -23,6 +23,11 @@ class QWindow; class DisplayPlugin; +namespace gpu { + class Texture; + using TexturePointer = std::shared_ptr; +} + class PluginContainer { public: static PluginContainer& getInstance(); @@ -39,8 +44,8 @@ public: virtual void showDisplayPluginsTools() = 0; virtual void requestReset() = 0; virtual bool makeRenderingContextCurrent() = 0; - virtual void releaseSceneTexture(uint32_t texture) = 0; - virtual void releaseOverlayTexture(uint32_t texture) = 0; + virtual void releaseSceneTexture(const gpu::TexturePointer& texture) = 0; + virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) = 0; virtual GLWidget* getPrimaryWidget() = 0; virtual QWindow* getPrimaryWindow() = 0; virtual QOpenGLContext* getPrimaryContext() = 0; diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 3796abd92a..8ff13aba70 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -1,3 +1,3 @@ set(TARGET_NAME script-engine) setup_hifi_library(Gui Network Script WebSockets Widgets) -link_hifi_libraries(shared networking octree gpu procedural model model-networking recording avatars fbx entities controllers animation audio physics) +link_hifi_libraries(shared networking octree gpu ui procedural model model-networking recording avatars fbx entities controllers animation audio physics) diff --git a/libraries/script-engine/src/MenuItemProperties.cpp b/libraries/script-engine/src/MenuItemProperties.cpp index c5f037eba8..d181aee18d 100644 --- a/libraries/script-engine/src/MenuItemProperties.cpp +++ b/libraries/script-engine/src/MenuItemProperties.cpp @@ -13,20 +13,7 @@ #include #include "MenuItemProperties.h" -MenuItemProperties::MenuItemProperties() : - menuName(""), - menuItemName(""), - shortcutKey(""), - shortcutKeyEvent(), - shortcutKeySequence(), - position(UNSPECIFIED_POSITION), - beforeItem(""), - afterItem(""), - isCheckable(false), - isChecked(false), - isSeparator(false) -{ -}; + MenuItemProperties::MenuItemProperties(const QString& menuName, const QString& menuItemName, const QString& shortcutKey, bool checkable, bool checked, bool separator) : @@ -35,9 +22,6 @@ MenuItemProperties::MenuItemProperties(const QString& menuName, const QString& m shortcutKey(shortcutKey), shortcutKeyEvent(), shortcutKeySequence(shortcutKey), - position(UNSPECIFIED_POSITION), - beforeItem(""), - afterItem(""), isCheckable(checkable), isChecked(checked), isSeparator(separator) @@ -48,12 +32,8 @@ MenuItemProperties::MenuItemProperties(const QString& menuName, const QString& m const KeyEvent& shortcutKeyEvent, bool checkable, bool checked, bool separator) : menuName(menuName), menuItemName(menuItemName), - shortcutKey(""), shortcutKeyEvent(shortcutKeyEvent), shortcutKeySequence(shortcutKeyEvent), - position(UNSPECIFIED_POSITION), - beforeItem(""), - afterItem(""), isCheckable(checkable), isChecked(checked), isSeparator(separator) diff --git a/libraries/script-engine/src/MenuItemProperties.h b/libraries/script-engine/src/MenuItemProperties.h index 68fd32a6fa..9b95fc66e1 100644 --- a/libraries/script-engine/src/MenuItemProperties.h +++ b/libraries/script-engine/src/MenuItemProperties.h @@ -14,35 +14,35 @@ #include +#include #include "KeyEvent.h" -const int UNSPECIFIED_POSITION = -1; class MenuItemProperties { public: - MenuItemProperties(); - MenuItemProperties(const QString& menuName, const QString& menuItemName, + MenuItemProperties() {} + MenuItemProperties(const QString& menuName, const QString& menuItemName, const QString& shortcutKey = QString(""), bool checkable = false, bool checked = false, bool separator = false); - MenuItemProperties(const QString& menuName, const QString& menuItemName, + MenuItemProperties(const QString& menuName, const QString& menuItemName, const KeyEvent& shortcutKeyEvent, bool checkable = false, bool checked = false, bool separator = false); QString menuName; QString menuItemName; - + // Shortcut key items: in order of priority QString shortcutKey; KeyEvent shortcutKeyEvent; QKeySequence shortcutKeySequence; // this is what we actually use, it's set from one of the above // location related items: in order of priority - int position; + int position { ui::Menu::UNSPECIFIED_POSITION }; QString beforeItem; QString afterItem; // other properties - bool isCheckable; - bool isChecked; - bool isSeparator; + bool isCheckable { false }; + bool isChecked { false }; + bool isSeparator { false }; QString grouping; /// Either: "", "Advanced", or "Developer" }; diff --git a/libraries/ui/src/CursorManager.cpp b/libraries/ui/src/CursorManager.cpp index cfa96b7992..a4c7cf9fb2 100644 --- a/libraries/ui/src/CursorManager.cpp +++ b/libraries/ui/src/CursorManager.cpp @@ -44,6 +44,10 @@ namespace Cursor { return instance; } + QList Manager::registeredIcons() const { + return ICONS.keys(); + } + uint8_t Manager::getCount() { return 1; } diff --git a/libraries/ui/src/CursorManager.h b/libraries/ui/src/CursorManager.h index 7ecab76be8..99d5ccdc77 100644 --- a/libraries/ui/src/CursorManager.h +++ b/libraries/ui/src/CursorManager.h @@ -47,6 +47,7 @@ namespace Cursor { void setScale(float scale); Instance* getCursor(uint8_t index = 0); uint16_t registerIcon(const QString& path); + QList registeredIcons() const; const QString& getIconImage(uint16_t icon); private: float _scale{ 1.0f }; diff --git a/libraries/ui/src/ui/Logging.cpp b/libraries/ui/src/ui/Logging.cpp new file mode 100644 index 0000000000..8a6495b8d8 --- /dev/null +++ b/libraries/ui/src/ui/Logging.cpp @@ -0,0 +1,11 @@ +// +// Created by Bradley Austin Davis 2016/03/01 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Logging.h" + +Q_LOGGING_CATEGORY(uiLogging, "hifi.ui") diff --git a/libraries/ui/src/ui/Logging.h b/libraries/ui/src/ui/Logging.h new file mode 100644 index 0000000000..6d31b0e86a --- /dev/null +++ b/libraries/ui/src/ui/Logging.h @@ -0,0 +1,16 @@ +// +// Created by Bradley Austin Davis 2015/10/11 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_Controllers_Logging_h +#define hifi_Controllers_Logging_h + +#include + +Q_DECLARE_LOGGING_CATEGORY(uiLogging) + +#endif diff --git a/libraries/ui/src/ui/Menu.cpp b/libraries/ui/src/ui/Menu.cpp new file mode 100644 index 0000000000..47f7aa4eac --- /dev/null +++ b/libraries/ui/src/ui/Menu.cpp @@ -0,0 +1,561 @@ +// +// Created by Stephen Birarda on 8/12/13. +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Menu.h" + +#include +#include +#include + +#include + +#include "../VrMenu.h" +#include "Logging.h" + +using namespace ui; +static const char* const MENU_PROPERTY_NAME = "com.highfidelity.Menu"; + +Menu* Menu::getInstance() { + static Menu* instance = globalInstance(MENU_PROPERTY_NAME); + return instance; +} + +Menu::Menu() { +} + +void Menu::toggleAdvancedMenus() { + setGroupingIsVisible("Advanced", !getGroupingIsVisible("Advanced")); +} + +void Menu::toggleDeveloperMenus() { + setGroupingIsVisible("Developer", !getGroupingIsVisible("Developer")); +} + +void Menu::loadSettings() { + scanMenuBar(&Menu::loadAction); +} + +void Menu::saveSettings() { + scanMenuBar(&Menu::saveAction); +} + +void Menu::loadAction(Settings& settings, QAction& action) { + if (action.isChecked() != settings.value(action.text(), action.isChecked()).toBool()) { + action.trigger(); + } +} + +void Menu::saveAction(Settings& settings, QAction& action) { + settings.setValue(action.text(), action.isChecked()); +} + +void Menu::scanMenuBar(settingsAction modifySetting) { + Settings settings; + foreach (QMenu* menu, findChildren()) { + scanMenu(*menu, modifySetting, settings); + } +} + +void Menu::scanMenu(QMenu& menu, settingsAction modifySetting, Settings& settings) { + settings.beginGroup(menu.title()); + foreach (QAction* action, menu.actions()) { + if (action->menu()) { + scanMenu(*action->menu(), modifySetting, settings); + } else if (action->isCheckable()) { + modifySetting(settings, *action); + } + } + settings.endGroup(); +} + +void Menu::addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName, + int menuItemLocation, const QString& grouping) { + QAction* actionBefore = NULL; + QAction* separator; + QAction* separatorText; + + if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) { + actionBefore = destinationMenu->actions()[menuItemLocation]; + } + if (actionBefore) { + separator = new QAction("",destinationMenu); + destinationMenu->insertAction(actionBefore, separator); + separator->setSeparator(true); + + separatorText = new QAction(actionName,destinationMenu); + separatorText->setEnabled(false); + destinationMenu->insertAction(actionBefore, separatorText); + + } else { + separator = destinationMenu->addSeparator(); + separatorText = destinationMenu->addAction(actionName); + separatorText->setEnabled(false); + } + + if (isValidGrouping(grouping)) { + _groupingActions[grouping] << separator; + _groupingActions[grouping] << separatorText; + bool isVisible = getGroupingIsVisible(grouping); + separator->setVisible(isVisible); + separatorText->setVisible(isVisible); + } +} + +QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu, + const QString& actionName, + const QKeySequence& shortcut, + const QObject* receiver, + const char* member, + QAction::MenuRole role, + int menuItemLocation, + const QString& grouping) { + QAction* action = NULL; + QAction* actionBefore = NULL; + + if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) { + actionBefore = destinationMenu->actions()[menuItemLocation]; + } + + if (!actionBefore) { + if (receiver && member) { + action = destinationMenu->addAction(actionName, receiver, member, shortcut); + } else { + action = destinationMenu->addAction(actionName); + action->setShortcut(shortcut); + } + } else { + action = new QAction(actionName, destinationMenu); + action->setShortcut(shortcut); + destinationMenu->insertAction(actionBefore, action); + + if (receiver && member) { + connect(action, SIGNAL(triggered()), receiver, member); + } + } + action->setMenuRole(role); + + _actionHash.insert(actionName, action); + + if (isValidGrouping(grouping)) { + _groupingActions[grouping] << action; + action->setVisible(getGroupingIsVisible(grouping)); + } + + return action; +} + +QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu, + QAction* action, + const QString& actionName, + const QKeySequence& shortcut, + QAction::MenuRole role, + int menuItemLocation, + const QString& grouping) { + QAction* actionBefore = NULL; + + if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) { + actionBefore = destinationMenu->actions()[menuItemLocation]; + } + + if (!actionName.isEmpty()) { + action->setText(actionName); + } + + if (shortcut != 0) { + action->setShortcut(shortcut); + } + + if (role != QAction::NoRole) { + action->setMenuRole(role); + } + + if (!actionBefore) { + destinationMenu->addAction(action); + } else { + destinationMenu->insertAction(actionBefore, action); + } + + _actionHash.insert(action->text(), action); + + if (isValidGrouping(grouping)) { + _groupingActions[grouping] << action; + action->setVisible(getGroupingIsVisible(grouping)); + } + + return action; +} + +QAction* Menu::addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu, + const QString& actionName, + const QKeySequence& shortcut, + const bool checked, + const QObject* receiver, + const char* member, + int menuItemLocation, + const QString& grouping) { + + QAction* action = addActionToQMenuAndActionHash(destinationMenu, actionName, shortcut, receiver, member, + QAction::NoRole, menuItemLocation); + action->setCheckable(true); + action->setChecked(checked); + + if (isValidGrouping(grouping)) { + _groupingActions[grouping] << action; + action->setVisible(getGroupingIsVisible(grouping)); + } + + return action; +} + +void Menu::removeAction(MenuWrapper* menu, const QString& actionName) { + auto action = _actionHash.value(actionName); + menu->removeAction(action); + _actionHash.remove(actionName); + for (auto& grouping : _groupingActions) { + grouping.remove(action); + } +} + +void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) { + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(Menu::getInstance(), "setIsOptionChecked", Qt::BlockingQueuedConnection, + Q_ARG(const QString&, menuOption), + Q_ARG(bool, isChecked)); + return; + } + QAction* menu = _actionHash.value(menuOption); + if (menu) { + menu->setChecked(isChecked); + } +} + +bool Menu::isOptionChecked(const QString& menuOption) const { + const QAction* menu = _actionHash.value(menuOption); + if (menu) { + return menu->isChecked(); + } + return false; +} + +void Menu::triggerOption(const QString& menuOption) { + QAction* action = _actionHash.value(menuOption); + if (action) { + action->trigger(); + } else { + qCDebug(uiLogging) << "NULL Action for menuOption '" << menuOption << "'"; + } +} + +QAction* Menu::getActionForOption(const QString& menuOption) { + return _actionHash.value(menuOption); +} + +QAction* Menu::getActionFromName(const QString& menuName, MenuWrapper* menu) { + QList menuActions; + if (menu) { + menuActions = menu->actions(); + } else { + menuActions = actions(); + } + + foreach (QAction* menuAction, menuActions) { + QString actionText = menuAction->text(); + if (menuName == menuAction->text()) { + return menuAction; + } + } + return NULL; +} + +MenuWrapper* Menu::getSubMenuFromName(const QString& menuName, MenuWrapper* menu) { + QAction* action = getActionFromName(menuName, menu); + if (action) { + return MenuWrapper::fromMenu(action->menu()); + } + return NULL; +} + +MenuWrapper* Menu::getMenuParent(const QString& menuName, QString& finalMenuPart) { + QStringList menuTree = menuName.split(">"); + MenuWrapper* parent = NULL; + MenuWrapper* menu = NULL; + foreach (QString menuTreePart, menuTree) { + parent = menu; + finalMenuPart = menuTreePart.trimmed(); + menu = getSubMenuFromName(finalMenuPart, parent); + if (!menu) { + break; + } + } + return parent; +} + +MenuWrapper* Menu::getMenu(const QString& menuName) { + QStringList menuTree = menuName.split(">"); + MenuWrapper* parent = NULL; + MenuWrapper* menu = NULL; + int item = 0; + foreach (QString menuTreePart, menuTree) { + menu = getSubMenuFromName(menuTreePart.trimmed(), parent); + if (!menu) { + break; + } + parent = menu; + item++; + } + return menu; +} + +QAction* Menu::getMenuAction(const QString& menuName) { + QStringList menuTree = menuName.split(">"); + MenuWrapper* parent = NULL; + QAction* action = NULL; + foreach (QString menuTreePart, menuTree) { + action = getActionFromName(menuTreePart.trimmed(), parent); + if (!action) { + break; + } + parent = MenuWrapper::fromMenu(action->menu()); + } + return action; +} + +int Menu::findPositionOfMenuItem(MenuWrapper* menu, const QString& searchMenuItem) { + int position = 0; + foreach(QAction* action, menu->actions()) { + if (action->text() == searchMenuItem) { + return position; + } + position++; + } + return UNSPECIFIED_POSITION; // not found +} + +int Menu::positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition) { + QList menuActions = menu->actions(); + if (requestedPosition > 1 && requestedPosition < menuActions.size()) { + QAction* beforeRequested = menuActions[requestedPosition - 1]; + if (beforeRequested->isSeparator()) { + requestedPosition--; + } + } + return requestedPosition; +} + +bool Menu::_isSomeSubmenuShown = false; + +MenuWrapper* Menu::addMenu(const QString& menuName, const QString& grouping) { + QStringList menuTree = menuName.split(">"); + MenuWrapper* addTo = NULL; + MenuWrapper* menu = NULL; + foreach (QString menuTreePart, menuTree) { + menu = getSubMenuFromName(menuTreePart.trimmed(), addTo); + if (!menu) { + if (!addTo) { + menu = new MenuWrapper(QMenuBar::addMenu(menuTreePart.trimmed())); + } else { + menu = addTo->addMenu(menuTreePart.trimmed()); + } + } + addTo = menu; + } + + if (isValidGrouping(grouping)) { + auto action = getMenuAction(menuName); + if (action) { + _groupingActions[grouping] << action; + action->setVisible(getGroupingIsVisible(grouping)); + } + } + + QMenuBar::repaint(); + + // hook our show/hide for popup menus, so we can keep track of whether or not one + // of our submenus is currently showing. + connect(menu->_realMenu, &QMenu::aboutToShow, []() { _isSomeSubmenuShown = true; }); + connect(menu->_realMenu, &QMenu::aboutToHide, []() { _isSomeSubmenuShown = false; }); + + return menu; +} + +void Menu::removeMenu(const QString& menuName) { + QAction* action = getMenuAction(menuName); + + // only proceed if the menu actually exists + if (action) { + QString finalMenuPart; + MenuWrapper* parent = getMenuParent(menuName, finalMenuPart); + if (parent) { + parent->removeAction(action); + } else { + QMenuBar::removeAction(action); + } + + QMenuBar::repaint(); + } +} + +bool Menu::menuExists(const QString& menuName) { + QAction* action = getMenuAction(menuName); + + // only proceed if the menu actually exists + if (action) { + return true; + } + return false; +} + +void Menu::addSeparator(const QString& menuName, const QString& separatorName, const QString& grouping) { + MenuWrapper* menuObj = getMenu(menuName); + if (menuObj) { + addDisabledActionAndSeparator(menuObj, separatorName); + } +} + +void Menu::removeSeparator(const QString& menuName, const QString& separatorName) { + MenuWrapper* menu = getMenu(menuName); + bool separatorRemoved = false; + if (menu) { + int textAt = findPositionOfMenuItem(menu, separatorName); + QList menuActions = menu->actions(); + QAction* separatorText = menuActions[textAt]; + if (textAt > 0 && textAt < menuActions.size()) { + QAction* separatorLine = menuActions[textAt - 1]; + if (separatorLine) { + if (separatorLine->isSeparator()) { + menu->removeAction(separatorText); + menu->removeAction(separatorLine); + separatorRemoved = true; + } + } + } + } + if (separatorRemoved) { + QMenuBar::repaint(); + } +} + +void Menu::removeMenuItem(const QString& menu, const QString& menuitem) { + MenuWrapper* menuObj = getMenu(menu); + if (menuObj) { + removeAction(menuObj, menuitem); + QMenuBar::repaint(); + } +} + +bool Menu::menuItemExists(const QString& menu, const QString& menuitem) { + QAction* menuItemAction = _actionHash.value(menuitem); + if (menuItemAction) { + return (getMenu(menu) != NULL); + } + return false; +} + +bool Menu::getGroupingIsVisible(const QString& grouping) { + if (grouping.isEmpty() || grouping.isNull()) { + return true; + } + if (_groupingVisible.contains(grouping)) { + return _groupingVisible[grouping]; + } + return false; +} + +void Menu::setGroupingIsVisible(const QString& grouping, bool isVisible) { + // NOTE: Default grouping always visible + if (grouping.isEmpty() || grouping.isNull()) { + return; + } + _groupingVisible[grouping] = isVisible; + + for (auto action: _groupingActions[grouping]) { + action->setVisible(isVisible); + } + + QMenuBar::repaint(); +} + +void Menu::addActionGroup(const QString& groupName, const QStringList& actionList, const QString& selected, QObject* receiver, const char* slot) { + auto menu = addMenu(groupName); + + QActionGroup* actionGroup = new QActionGroup(menu); + actionGroup->setExclusive(true); + + for (auto action : actionList) { + auto item = addCheckableActionToQMenuAndActionHash(menu, action, 0, action == selected, receiver, slot); + actionGroup->addAction(item); + } + + QMenuBar::repaint(); +} + +void Menu::removeActionGroup(const QString& groupName) { + removeMenu(groupName); +} + +MenuWrapper::MenuWrapper(QMenu* menu) : _realMenu(menu) { + VrMenu::executeOrQueue([=](VrMenu* vrMenu) { + vrMenu->addMenu(menu); + }); + _backMap[menu] = this; +} + +QList MenuWrapper::actions() { + return _realMenu->actions(); +} + +MenuWrapper* MenuWrapper::addMenu(const QString& menuName) { + return new MenuWrapper(_realMenu->addMenu(menuName)); +} + +void MenuWrapper::setEnabled(bool enabled) { + _realMenu->setEnabled(enabled); +} + +QAction* MenuWrapper::addSeparator() { + return _realMenu->addSeparator(); +} + +void MenuWrapper::addAction(QAction* action) { + _realMenu->addAction(action); + VrMenu::executeOrQueue([=](VrMenu* vrMenu) { + vrMenu->addAction(_realMenu, action); + }); +} + +QAction* MenuWrapper::addAction(const QString& menuName) { + QAction* action = _realMenu->addAction(menuName); + VrMenu::executeOrQueue([=](VrMenu* vrMenu) { + vrMenu->addAction(_realMenu, action); + }); + return action; +} + +QAction* MenuWrapper::addAction(const QString& menuName, const QObject* receiver, const char* member, const QKeySequence& shortcut) { + QAction* action = _realMenu->addAction(menuName, receiver, member, shortcut); + VrMenu::executeOrQueue([=](VrMenu* vrMenu) { + vrMenu->addAction(_realMenu, action); + }); + return action; +} + +void MenuWrapper::removeAction(QAction* action) { + _realMenu->removeAction(action); + VrMenu::executeOrQueue([=](VrMenu* vrMenu) { + vrMenu->removeAction(action); + }); +} + +void MenuWrapper::insertAction(QAction* before, QAction* action) { + _realMenu->insertAction(before, action); + VrMenu::executeOrQueue([=](VrMenu* vrMenu) { + vrMenu->insertAction(before, action); + }); +} + +QHash MenuWrapper::_backMap; diff --git a/libraries/ui/src/ui/Menu.h b/libraries/ui/src/ui/Menu.h new file mode 100644 index 0000000000..44aa661a21 --- /dev/null +++ b/libraries/ui/src/ui/Menu.h @@ -0,0 +1,155 @@ +// +// Created by Stephen Birarda on 8/12/13. +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ui_Menu_h +#define hifi_ui_Menu_h + +#include +#include +#include +#include +#include +#include + +class Settings; +namespace ui { + class Menu; +} + +class MenuWrapper : public QObject { +public: + + QList actions(); + MenuWrapper* addMenu(const QString& menuName); + void setEnabled(bool enabled = true); + QAction* addSeparator(); + void addAction(QAction* action); + + QAction* addAction(const QString& menuName); + void insertAction(QAction* before, QAction* menuName); + + QAction* addAction(const QString& menuName, const QObject* receiver, const char* member, const QKeySequence& shortcut = 0); + void removeAction(QAction* action); + + QAction* newAction() { + return new QAction(_realMenu); + } + +private: + MenuWrapper(QMenu* menu); + + static MenuWrapper* fromMenu(QMenu* menu) { + return _backMap[menu]; + } + + QMenu* const _realMenu; + static QHash _backMap; + friend class ui::Menu; +}; + +namespace ui { + +class Menu : public QMenuBar { + Q_OBJECT +public: + static const int UNSPECIFIED_POSITION = -1; + + Menu(); + static Menu* getInstance(); + + void loadSettings(); + void saveSettings(); + + MenuWrapper* getMenu(const QString& menuName); + MenuWrapper* getSubMenuFromName(const QString& menuName, MenuWrapper* menu); + + void triggerOption(const QString& menuOption); + QAction* getActionForOption(const QString& menuOption); + + QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu, + const QString& actionName, + const QKeySequence& shortcut = 0, + const QObject* receiver = NULL, + const char* member = NULL, + QAction::MenuRole role = QAction::NoRole, + int menuItemLocation = UNSPECIFIED_POSITION, + const QString& grouping = QString()); + + QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu, + QAction* action, + const QString& actionName = QString(), + const QKeySequence& shortcut = 0, + QAction::MenuRole role = QAction::NoRole, + int menuItemLocation = UNSPECIFIED_POSITION, + const QString& grouping = QString()); + + QAction* addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu, + const QString& actionName, + const QKeySequence& shortcut = 0, + const bool checked = false, + const QObject* receiver = NULL, + const char* member = NULL, + int menuItemLocation = UNSPECIFIED_POSITION, + const QString& grouping = QString()); + + void removeAction(MenuWrapper* menu, const QString& actionName); + +public slots: + MenuWrapper* addMenu(const QString& menuName, const QString& grouping = QString()); + void removeMenu(const QString& menuName); + bool menuExists(const QString& menuName); + void addSeparator(const QString& menuName, const QString& separatorName, const QString& grouping = QString()); + void removeSeparator(const QString& menuName, const QString& separatorName); + void removeMenuItem(const QString& menuName, const QString& menuitem); + bool menuItemExists(const QString& menuName, const QString& menuitem); + void addActionGroup(const QString& groupName, const QStringList& actionList, const QString& selected = QString(), + QObject* receiver = nullptr, const char* slot = nullptr); + void removeActionGroup(const QString& groupName); + bool isOptionChecked(const QString& menuOption) const; + void setIsOptionChecked(const QString& menuOption, bool isChecked); + + bool getGroupingIsVisible(const QString& grouping); + void setGroupingIsVisible(const QString& grouping, bool isVisible); /// NOTE: the "" grouping is always visible + + void toggleDeveloperMenus(); + void toggleAdvancedMenus(); + + static bool isSomeSubmenuShown() { return _isSomeSubmenuShown; } + +protected: + typedef void(*settingsAction)(Settings&, QAction&); + static void loadAction(Settings& settings, QAction& action); + static void saveAction(Settings& settings, QAction& action); + void scanMenuBar(settingsAction modifySetting); + void scanMenu(QMenu& menu, settingsAction modifySetting, Settings& settings); + + /// helper method to have separators with labels that are also compatible with OS X + void addDisabledActionAndSeparator(MenuWrapper* destinationMenu, + const QString& actionName, + int menuItemLocation = UNSPECIFIED_POSITION, + const QString& grouping = QString()); + + QAction* getActionFromName(const QString& menuName, MenuWrapper* menu); + MenuWrapper* getMenuParent(const QString& menuName, QString& finalMenuPart); + + QAction* getMenuAction(const QString& menuName); + int findPositionOfMenuItem(MenuWrapper* menu, const QString& searchMenuItem); + int positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition); + + QHash _actionHash; + + bool isValidGrouping(const QString& grouping) const { return grouping == "Advanced" || grouping == "Developer"; } + QHash _groupingVisible; + QHash> _groupingActions; + + static bool _isSomeSubmenuShown; +}; + +} // namespace ui + +#endif // hifi_Menu_h diff --git a/plugins/oculus/CMakeLists.txt b/plugins/oculus/CMakeLists.txt index fe1a87d6b6..5928224bad 100644 --- a/plugins/oculus/CMakeLists.txt +++ b/plugins/oculus/CMakeLists.txt @@ -13,7 +13,7 @@ if (WIN32) set(TARGET_NAME oculus) setup_hifi_plugin() - link_hifi_libraries(shared gl plugins display-plugins) + link_hifi_libraries(shared gl plugins gpu display-plugins input-plugins) include_hifi_library_headers(octree) diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp index c558b6d31f..11db3d5149 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp @@ -76,8 +76,6 @@ void OculusBaseDisplayPlugin::activate() { qFatal("Failed to acquire HMD"); } - HmdDisplayPlugin::activate(); - _hmdDesc = ovr_GetHmdDesc(_session); _ipd = ovr_GetFloat(_session, OVR_KEY_IPD, _ipd); @@ -123,6 +121,11 @@ void OculusBaseDisplayPlugin::activate() { ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) { qFatal("Could not attach to sensor device"); } + + // This must come after the initialization, so that the values calculated + // above are available during the customizeContext call (when not running + // in threaded present mode) + HmdDisplayPlugin::activate(); } void OculusBaseDisplayPlugin::deactivate() { diff --git a/plugins/oculus/src/OculusDebugDisplayPlugin.cpp b/plugins/oculus/src/OculusDebugDisplayPlugin.cpp index 94a3670e4b..7653fba764 100644 --- a/plugins/oculus/src/OculusDebugDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDebugDisplayPlugin.cpp @@ -19,8 +19,3 @@ bool OculusDebugDisplayPlugin::isSupported() const { } return OculusBaseDisplayPlugin::isSupported(); } - -void OculusDebugDisplayPlugin::customizeContext() { - OculusBaseDisplayPlugin::customizeContext(); - enableVsync(false); -} diff --git a/plugins/oculus/src/OculusDebugDisplayPlugin.h b/plugins/oculus/src/OculusDebugDisplayPlugin.h index c62fe12c73..a13315ab05 100644 --- a/plugins/oculus/src/OculusDebugDisplayPlugin.h +++ b/plugins/oculus/src/OculusDebugDisplayPlugin.h @@ -11,12 +11,12 @@ class OculusDebugDisplayPlugin : public OculusBaseDisplayPlugin { public: - virtual const QString& getName() const override { return NAME; } - virtual grouping getGrouping() const override { return DEVELOPER; } - virtual bool isSupported() const override; + const QString& getName() const override { return NAME; } + grouping getGrouping() const override { return DEVELOPER; } + bool isSupported() const override; protected: - virtual void customizeContext() override; + void hmdPresent() override {} private: static const QString NAME; diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp index e19c658c87..dbad725cf6 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDisplayPlugin.cpp @@ -37,57 +37,38 @@ void OculusDisplayPlugin::uncustomizeContext() { OculusBaseDisplayPlugin::uncustomizeContext(); } -void OculusDisplayPlugin::internalPresent() { + +template +void blit(const SrcFbo& srcFbo, const DstFbo& dstFbo) { + using namespace oglplus; + srcFbo->Bound(FramebufferTarget::Read, [&] { + dstFbo->Bound(FramebufferTarget::Draw, [&] { + Context::BlitFramebuffer( + 0, 0, srcFbo->size.x, srcFbo->size.y, + 0, 0, dstFbo->size.x, dstFbo->size.y, + BufferSelectBit::ColorBuffer, BlitFilter::Linear); + }); + }); +} + +void OculusDisplayPlugin::updateFrameData() { + Parent::updateFrameData(); + _sceneLayer.RenderPose[ovrEyeType::ovrEye_Left] = ovrPoseFromGlm(_currentRenderEyePoses[Left]); + _sceneLayer.RenderPose[ovrEyeType::ovrEye_Right] = ovrPoseFromGlm(_currentRenderEyePoses[Right]); +} + +void OculusDisplayPlugin::hmdPresent() { if (!_currentSceneTexture) { return; } - using namespace oglplus; - const auto& size = _sceneFbo->size; - _sceneFbo->Bound([&] { - Context::Viewport(size.x, size.y); - glBindTexture(GL_TEXTURE_2D, _currentSceneTexture); - //glEnable(GL_FRAMEBUFFER_SRGB); - GLenum err = glGetError(); - drawUnitQuad(); - //glDisable(GL_FRAMEBUFFER_SRGB); - }); - - uint32_t frameIndex { 0 }; - EyePoses eyePoses; + blit(_compositeFramebuffer, _sceneFbo); { - Lock lock(_mutex); - Q_ASSERT(_sceneTextureToFrameIndexMap.contains(_currentSceneTexture)); - frameIndex = _sceneTextureToFrameIndexMap[_currentSceneTexture]; - Q_ASSERT(_frameEyePoses.contains(frameIndex)); - eyePoses = _frameEyePoses[frameIndex]; - } - - _sceneLayer.RenderPose[ovrEyeType::ovrEye_Left] = eyePoses.first; - _sceneLayer.RenderPose[ovrEyeType::ovrEye_Right] = eyePoses.second; - - { - ovrLayerHeader* layers = &_sceneLayer.Header; - ovrResult result = ovr_SubmitFrame(_session, frameIndex, &_viewScaleDesc, &layers, 1); + ovrResult result = ovr_SubmitFrame(_session, _currentRenderFrameIndex, &_viewScaleDesc, &layers, 1); if (!OVR_SUCCESS(result)) { qDebug() << result; } } _sceneFbo->Increment(); - - // Handle mirroring to screen in base class - HmdDisplayPlugin::internalPresent(); -} - -void OculusDisplayPlugin::setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) { - auto ovrPose = ovrPoseFromGlm(pose); - { - Lock lock(_mutex); - if (eye == Eye::Left) { - _frameEyePoses[frameIndex].first = ovrPose; - } else { - _frameEyePoses[frameIndex].second = ovrPose; - } - } } diff --git a/plugins/oculus/src/OculusDisplayPlugin.h b/plugins/oculus/src/OculusDisplayPlugin.h index 98535a1790..fedb70abdb 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.h +++ b/plugins/oculus/src/OculusDisplayPlugin.h @@ -15,24 +15,23 @@ using SwapFboPtr = QSharedPointer; const float TARGET_RATE_Oculus = 75.0f; class OculusDisplayPlugin : public OculusBaseDisplayPlugin { + using Parent = OculusBaseDisplayPlugin; public: - virtual void activate() override; - virtual const QString& getName() const override { return NAME; } - virtual void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) override final; + void activate() override; + const QString& getName() const override { return NAME; } - virtual float getTargetFrameRate() override { return TARGET_RATE_Oculus; } + float getTargetFrameRate() override { return TARGET_RATE_Oculus; } protected: - virtual void internalPresent() override; - virtual void customizeContext() override; - virtual void uncustomizeContext() override; + void hmdPresent() override; + void customizeContext() override; + void uncustomizeContext() override; + void updateFrameData() override; private: - using EyePoses = std::pair; static const QString NAME; bool _enablePreview { false }; bool _monoPreview { true }; - QMap _frameEyePoses; SwapFboPtr _sceneFbo; }; diff --git a/plugins/oculusLegacy/CMakeLists.txt b/plugins/oculusLegacy/CMakeLists.txt index bf9d22410d..88b55b7a79 100644 --- a/plugins/oculusLegacy/CMakeLists.txt +++ b/plugins/oculusLegacy/CMakeLists.txt @@ -10,7 +10,7 @@ if (NOT WIN32) set(TARGET_NAME oculusLegacy) setup_hifi_plugin() - link_hifi_libraries(shared gl plugins display-plugins) + link_hifi_libraries(shared gl gpu plugins display-plugins input-plugins) include_hifi_library_headers(octree) diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h index 3ecabc9a37..8708fb9204 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h @@ -34,6 +34,7 @@ public: protected: virtual void customizeContext() override; + void hmdPresent() override {} #if 0 virtual void uncustomizeContext() override; virtual void internalPresent() override; diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 559ea5bcfb..e5708572f6 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include "OpenVrHelpers.h" @@ -128,19 +127,16 @@ glm::mat4 OpenVrDisplayPlugin::getHeadPose(uint32_t frameIndex) const { return _trackedDevicePoseMat4[0]; } -void OpenVrDisplayPlugin::internalPresent() { +void OpenVrDisplayPlugin::hmdPresent() { // Flip y-axis since GL UV coords are backwards. static vr::VRTextureBounds_t leftBounds{ 0, 0, 0.5f, 1 }; static vr::VRTextureBounds_t rightBounds{ 0.5f, 0, 1, 1 }; - - vr::Texture_t texture{ (void*)_currentSceneTexture, vr::API_OpenGL, vr::ColorSpace_Auto }; + + vr::Texture_t texture { (void*)oglplus::GetName(_compositeFramebuffer->color), vr::API_OpenGL, vr::ColorSpace_Auto }; _compositor->Submit(vr::Eye_Left, &texture, &leftBounds); _compositor->Submit(vr::Eye_Right, &texture, &rightBounds); vr::TrackedDevicePose_t currentTrackedDevicePose[vr::k_unMaxTrackedDeviceCount]; _compositor->WaitGetPoses(currentTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, nullptr, 0); - - // Handle the mirroring in the base class - HmdDisplayPlugin::internalPresent(); } diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h index 4344c3c48f..2114594b23 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -32,11 +32,10 @@ public: virtual glm::mat4 getHeadPose(uint32_t frameIndex) const override; protected: - virtual void internalPresent() override; + void hmdPresent() override; private: vr::IVRSystem* _system { nullptr }; static const QString NAME; mutable Mutex _poseMutex; }; - diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index 13b6b51d82..8d5b6cc7f7 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -92,8 +92,8 @@ public: virtual void showDisplayPluginsTools() override {} virtual void requestReset() override {} virtual bool makeRenderingContextCurrent() override { return true; } - virtual void releaseSceneTexture(uint32_t texture) override {} - virtual void releaseOverlayTexture(uint32_t texture) override {} + virtual void releaseSceneTexture(const gpu::TexturePointer& texture) override {} + virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) override {} virtual GLWidget* getPrimaryWidget() override { return nullptr; } virtual QWindow* getPrimaryWindow() override { return nullptr; } virtual QOpenGLContext* getPrimaryContext() override { return nullptr; }