From 6ef77bc46aeab1fb17186855271fd16284da9102 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 17 Dec 2015 15:46:44 -0800 Subject: [PATCH 1/6] Support the marketplace in QML --- examples/edit.js | 47 ++++++++++++------- interface/resources/qml/QmlWebWindow.qml | 16 ++++--- interface/src/Application.cpp | 2 +- interface/src/Application.h | 7 +-- libraries/networking/src/AbstractUriHandler.h | 19 ++++++++ libraries/octree/src/Octree.cpp | 2 +- libraries/ui/src/QmlWebWindowClass.cpp | 43 +++++++++++++++-- 7 files changed, 105 insertions(+), 31 deletions(-) create mode 100644 libraries/networking/src/AbstractUriHandler.h diff --git a/examples/edit.js b/examples/edit.js index 59b6ae3e7d..074b43c8c1 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -140,9 +140,33 @@ var importingSVOTextOverlay = Overlays.addOverlay("text", { }); var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; -var marketplaceWindow = new WebWindow('Marketplace', MARKETPLACE_URL, 900, 700, false); +var marketplaceWindow = new OverlayWebWindow('Marketplace', "about:blank", 900, 700, false); marketplaceWindow.setVisible(false); +function showMarketplace(marketplaceID) { + var url = MARKETPLACE_URL; + if (marketplaceID) { + url = url + "/items/" + marketplaceID; + } + print("setting marketplace URL to " + url); + marketplaceWindow.setURL(url); + marketplaceWindow.setVisible(true); + marketplaceWindow.raise(); +} + +function hideMarketplace() { + marketplaceWindow.setVisible(false); + marketplaceWindow.setURL("about:blank"); +} + +function toggleMarketplace() { + if (marketplaceWindow.visible) { + hideMarketplace(); + } else { + showMarketplace(); + } +} + var toolBar = (function() { var that = {}, toolBar, @@ -413,12 +437,9 @@ var toolBar = (function() { newModelButtonDown = true; return true; } + if (browseMarketplaceButton === toolBar.clicked(clickedOverlay)) { - if (marketplaceWindow.url != MARKETPLACE_URL) { - marketplaceWindow.setURL(MARKETPLACE_URL); - } - marketplaceWindow.setVisible(true); - marketplaceWindow.raise(); + toggleMarketplace(); return true; } @@ -1336,6 +1357,7 @@ function getPositionToCreateEntity() { } function importSVO(importURL) { + print("Import URL requested: " + importURL) if (!Entities.canAdjustLocks()) { Window.alert(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG); return; @@ -1574,11 +1596,7 @@ PropertiesTool = function(opts) { pushCommandForSelections(); selectionManager._update(); } else if (data.type == "showMarketplace") { - if (marketplaceWindow.url != data.url) { - marketplaceWindow.setURL(data.url); - } - marketplaceWindow.setVisible(true); - marketplaceWindow.raise(); + showMarketplace(); } else if (data.type == "action") { if (data.action == "moveSelectionToGrid") { if (selectionManager.hasSelection()) { @@ -1859,12 +1877,7 @@ var propertyMenu = PopupMenu(); propertyMenu.onSelectMenuItem = function(name) { if (propertyMenu.marketplaceID) { - var url = MARKETPLACE_URL + "/items/" + propertyMenu.marketplaceID; - if (marketplaceWindow.url != url) { - marketplaceWindow.setURL(url); - } - marketplaceWindow.setVisible(true); - marketplaceWindow.raise(); + showMarketplace(propertyMenu.marketplaceID); } }; diff --git a/interface/resources/qml/QmlWebWindow.qml b/interface/resources/qml/QmlWebWindow.qml index 3eb01aa9ba..029d50519a 100644 --- a/interface/resources/qml/QmlWebWindow.qml +++ b/interface/resources/qml/QmlWebWindow.qml @@ -13,12 +13,18 @@ VrDialog { HifiConstants { id: hifi } title: "WebWindow" resizable: true + // Don't destroy on close... otherwise the JS/C++ will have a dangling pointer + destroyOnCloseButton: false contentImplicitWidth: clientArea.implicitWidth contentImplicitHeight: clientArea.implicitHeight backgroundColor: "#7f000000" property url source: "about:blank" signal navigating(string url) + function stop() { + webview.stop(); + } + Component.onCompleted: { enabled = true @@ -26,18 +32,14 @@ VrDialog { webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message); }); - webview.loadingChanged.connect(handleWebviewLoading) } function handleWebviewLoading(loadRequest) { - var HIFI_URL_PATTERN = /^hifi:\/\//; if (WebEngineView.LoadStartedStatus == loadRequest.status) { var newUrl = loadRequest.url.toString(); - if (newUrl.match(HIFI_URL_PATTERN)) { - root.navigating(newUrl); - } + root.navigating(newUrl) } } @@ -55,8 +57,10 @@ VrDialog { url: root.source anchors.fill: parent profile: WebEngineProfile { + id: webviewProfile httpUserAgent: "Mozilla/5.0 (HighFidelityInterface)" - } + storageName: "qmlWebEngine" + } } } // item } // dialog diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e22b3913d0..7f27d97416 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4215,7 +4215,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("ScriptDiscoveryService", this->getRunningScriptsWidget()); } -bool Application::canAcceptURL(const QString& urlString) { +bool Application::canAcceptURL(const QString& urlString) const { QUrl url(urlString); if (urlString.startsWith(HIFI_URL_SCHEME)) { return true; diff --git a/interface/src/Application.h b/interface/src/Application.h index a665e925a9..0953aedd8c 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -38,6 +38,7 @@ #include #include #include +#include #include "avatar/AvatarUpdate.h" #include "avatar/MyAvatar.h" @@ -88,7 +89,7 @@ class Application; #endif #define qApp (static_cast(QCoreApplication::instance())) -class Application : public QApplication, public AbstractViewStateInterface, public AbstractScriptingServicesInterface { +class Application : public QApplication, public AbstractViewStateInterface, public AbstractScriptingServicesInterface, public AbstractUriHandler { Q_OBJECT // TODO? Get rid of those @@ -219,8 +220,8 @@ public: QString getScriptsLocation(); void setScriptsLocation(const QString& scriptsLocation); - bool canAcceptURL(const QString& url); - bool acceptURL(const QString& url, bool defaultUpload = false); + virtual bool canAcceptURL(const QString& url) const override; + virtual bool acceptURL(const QString& url, bool defaultUpload = false) override; void setMaxOctreePacketsPerSecond(int maxOctreePPS); int getMaxOctreePacketsPerSecond(); diff --git a/libraries/networking/src/AbstractUriHandler.h b/libraries/networking/src/AbstractUriHandler.h new file mode 100644 index 0000000000..3d6ed472ec --- /dev/null +++ b/libraries/networking/src/AbstractUriHandler.h @@ -0,0 +1,19 @@ +// +// Created by Bradley Austin Davis on 2015/12/17 +// Copyright 2013-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 +// + +#pragma once +#ifndef hifi_network_AbstractUriHandler_h +#define hifi_network_AbstractUriHandler_h + +class AbstractUriHandler { +public: + virtual bool canAcceptURL(const QString& url) const = 0; + virtual bool acceptURL(const QString& url, bool defaultUpload = false) = 0; +}; + +#endif diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 636d1a9a1a..652089ebb1 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -1867,7 +1867,7 @@ bool Octree::readJSONFromStream(unsigned long streamLength, QDataStream& inputSt QByteArray jsonBuffer; char* rawData = new char[READ_JSON_BUFFER_SIZE]; - while (true) { + while (!inputStream.atEnd()) { int got = inputStream.readRawData(rawData, READ_JSON_BUFFER_SIZE - 1); if (got < 0) { qCritical() << "error while reading from json stream"; diff --git a/libraries/ui/src/QmlWebWindowClass.cpp b/libraries/ui/src/QmlWebWindowClass.cpp index d5cdc1fde9..68a45a5bff 100644 --- a/libraries/ui/src/QmlWebWindowClass.cpp +++ b/libraries/ui/src/QmlWebWindowClass.cpp @@ -10,7 +10,10 @@ #include +#include #include +#include +#include #include #include @@ -22,6 +25,7 @@ #include #include +#include #include #include @@ -88,7 +92,7 @@ QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngi QmlWebWindowClass* retVal { nullptr }; const QString title = context->argument(0).toString(); QString url = context->argument(1).toString(); - if (!url.startsWith("http") && !url.startsWith("file://")) { + if (!url.startsWith("http") && !url.startsWith("file://") && !url.startsWith("about:")) { url = QUrl::fromLocalFile(url).toString(); } const int width = std::max(100, std::min(1280, context->argument(2).toInt32()));; @@ -119,7 +123,23 @@ QmlWebWindowClass::QmlWebWindowClass(QObject* qmlWindow) } void QmlWebWindowClass::handleNavigation(const QString& url) { - DependencyManager::get()->handleLookupString(url); + bool handled = false; + + if (url.contains(HIFI_URL_PATTERN)) { + DependencyManager::get()->handleLookupString(url); + handled = true; + } else { + static auto handler = dynamic_cast(qApp); + if (handler) { + if (handler->canAcceptURL(url)) { + handled = handler->acceptURL(url); + } + } + } + + if (handled) { + QMetaObject::invokeMethod(_qmlWindow, "stop", Qt::AutoConnection); + } } void QmlWebWindowClass::setVisible(bool visible) { @@ -202,6 +222,7 @@ QString QmlWebWindowClass::getURL() const { QMetaObject::invokeMethod(const_cast(this), "getURL", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, result)); return result; } + return _qmlWindow->property(URL_PROPERTY).toString(); } @@ -209,7 +230,23 @@ void QmlWebWindowClass::setURL(const QString& urlString) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "setURL", Qt::QueuedConnection, Q_ARG(QString, urlString)); } - _qmlWindow->setProperty(URL_PROPERTY, urlString); + + static const QString ACCESS_TOKEN_PARAMETER = "access_token"; + static const QString ALLOWED_HOST = "metaverse.highfidelity.com"; + + QUrl url(urlString); + qDebug() << "Url: " << urlString; + qDebug() << "Host: " << url.host(); + if (url.host() == ALLOWED_HOST) { + QUrlQuery query(url); + if (query.allQueryItemValues(ACCESS_TOKEN_PARAMETER).empty()) { + AccountManager& accountManager = AccountManager::getInstance(); + query.addQueryItem(ACCESS_TOKEN_PARAMETER, accountManager.getAccountInfo().getAccessToken().token); + url.setQuery(query.query()); + qDebug() << "New URL " << url; + } + } + _qmlWindow->setProperty(URL_PROPERTY, url.toString()); } From 95c524fba1c2e4b78f28991be507a475a8b2e7af Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 21 Dec 2015 15:44:35 -0800 Subject: [PATCH 2/6] Fixing authentication token for followed links --- interface/resources/qml/QmlWebWindow.qml | 7 +++ libraries/ui/src/QmlWebWindowClass.cpp | 63 +++++++++++++----------- libraries/ui/src/QmlWebWindowClass.h | 3 +- 3 files changed, 44 insertions(+), 29 deletions(-) diff --git a/interface/resources/qml/QmlWebWindow.qml b/interface/resources/qml/QmlWebWindow.qml index 029d50519a..6e9502edb2 100644 --- a/interface/resources/qml/QmlWebWindow.qml +++ b/interface/resources/qml/QmlWebWindow.qml @@ -56,6 +56,13 @@ VrDialog { id: webview url: root.source anchors.fill: parent + onUrlChanged: { + var currentUrl = url.toString(); + var newUrl = urlFixer.fixupUrl(currentUrl); + if (newUrl != currentUrl) { + url = newUrl; + } + } profile: WebEngineProfile { id: webviewProfile httpUserAgent: "Mozilla/5.0 (HighFidelityInterface)" diff --git a/libraries/ui/src/QmlWebWindowClass.cpp b/libraries/ui/src/QmlWebWindowClass.cpp index 68a45a5bff..781b8c1b76 100644 --- a/libraries/ui/src/QmlWebWindowClass.cpp +++ b/libraries/ui/src/QmlWebWindowClass.cpp @@ -11,19 +11,17 @@ #include #include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -87,6 +85,29 @@ void QmlWebWindowClass::setupServer() { } } +class UrlFixer : public QObject { + Q_OBJECT +public: + Q_INVOKABLE QString fixupUrl(const QString& originalUrl) { + static const QString ACCESS_TOKEN_PARAMETER = "access_token"; + static const QString ALLOWED_HOST = "metaverse.highfidelity.com"; + QString result = originalUrl; + QUrl url(originalUrl); + QUrlQuery query(url); + if (url.host() == ALLOWED_HOST && query.allQueryItemValues(ACCESS_TOKEN_PARAMETER).empty()) { + qDebug() << "Updating URL with auth token"; + AccountManager& accountManager = AccountManager::getInstance(); + query.addQueryItem(ACCESS_TOKEN_PARAMETER, accountManager.getAccountInfo().getAccessToken().token); + url.setQuery(query.query()); + result = url.toString(); + } + + return result; + } +}; + +static UrlFixer URL_FIXER; + // Method called by Qt scripts to create a new web window in the overlay QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) { QmlWebWindowClass* retVal { nullptr }; @@ -98,6 +119,7 @@ QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngi const int width = std::max(100, std::min(1280, context->argument(2).toInt32()));; const int height = std::max(100, std::min(720, context->argument(3).toInt32()));; + // Build the event bridge and wrapper on the main thread QMetaObject::invokeMethod(DependencyManager::get().data(), "load", Qt::BlockingQueuedConnection, Q_ARG(const QString&, "QmlWebWindow.qml"), @@ -105,6 +127,7 @@ QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngi setupServer(); retVal = new QmlWebWindowClass(object); webChannel.registerObject(url.toLower(), retVal); + context->setContextProperty("urlFixer", &URL_FIXER); retVal->setTitle(title); retVal->setURL(url); retVal->setSize(width, height); @@ -230,23 +253,7 @@ void QmlWebWindowClass::setURL(const QString& urlString) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "setURL", Qt::QueuedConnection, Q_ARG(QString, urlString)); } - - static const QString ACCESS_TOKEN_PARAMETER = "access_token"; - static const QString ALLOWED_HOST = "metaverse.highfidelity.com"; - - QUrl url(urlString); - qDebug() << "Url: " << urlString; - qDebug() << "Host: " << url.host(); - if (url.host() == ALLOWED_HOST) { - QUrlQuery query(url); - if (query.allQueryItemValues(ACCESS_TOKEN_PARAMETER).empty()) { - AccountManager& accountManager = AccountManager::getInstance(); - query.addQueryItem(ACCESS_TOKEN_PARAMETER, accountManager.getAccountInfo().getAccessToken().token); - url.setQuery(query.query()); - qDebug() << "New URL " << url; - } - } - _qmlWindow->setProperty(URL_PROPERTY, url.toString()); + _qmlWindow->setProperty(URL_PROPERTY, urlString); } diff --git a/libraries/ui/src/QmlWebWindowClass.h b/libraries/ui/src/QmlWebWindowClass.h index 2b563e68ba..0f2531deb8 100644 --- a/libraries/ui/src/QmlWebWindowClass.h +++ b/libraries/ui/src/QmlWebWindowClass.h @@ -10,11 +10,12 @@ #define hifi_ui_QmlWebWindowClass_h #include -#include #include #include #include +#include + class QScriptEngine; class QScriptContext; class QmlWebWindowClass; From eff830ad4b36c658075f3ce7557baa10c4ae0d81 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 22 Dec 2015 00:54:48 -0800 Subject: [PATCH 3/6] fix crash on mac when sixense DLL can't be loaded --- plugins/hifiSixense/src/SixenseManager.cpp | 23 ++++++++++++++++++- plugins/hifiSixense/src/SixenseManager.h | 2 ++ plugins/hifiSixense/src/SixenseSupportOSX.cpp | 3 +++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/plugins/hifiSixense/src/SixenseManager.cpp b/plugins/hifiSixense/src/SixenseManager.cpp index b534c09055..455fdd2b5e 100644 --- a/plugins/hifiSixense/src/SixenseManager.cpp +++ b/plugins/hifiSixense/src/SixenseManager.cpp @@ -13,6 +13,9 @@ #ifdef HAVE_SIXENSE #include +#else +#define SIXENSE_FAILURE -1 +#define SIXENSE_SUCCESS 0 #endif #include @@ -43,6 +46,14 @@ static const unsigned int BUTTON_TRIGGER = 1U << 8; const glm::vec3 SixenseManager::DEFAULT_AVATAR_POSITION { -0.25f, -0.35f, -0.3f }; // in hydra frame const float SixenseManager::CONTROLLER_THRESHOLD { 0.35f }; +bool SixenseManager::_sixenseLoaded = false; + +#define BAIL_IF_NOT_LOADED \ + if (!_sixenseLoaded) { \ + return; \ + } + + const QString SixenseManager::NAME = "Sixense"; const QString SixenseManager::HYDRA_ID_STRING = "Razer Hydra"; @@ -89,11 +100,12 @@ void SixenseManager::activate() { userInputMapper->registerDevice(_inputDevice); loadSettings(); - sixenseInit(); + _sixenseLoaded = (sixenseInit() == SIXENSE_SUCCESS); #endif } void SixenseManager::deactivate() { + BAIL_IF_NOT_LOADED InputPlugin::deactivate(); #ifdef HAVE_SIXENSE @@ -114,12 +126,14 @@ void SixenseManager::deactivate() { } void SixenseManager::setSixenseFilter(bool filter) { + BAIL_IF_NOT_LOADED #ifdef HAVE_SIXENSE sixenseSetFilterEnabled(filter ? 1 : 0); #endif } void SixenseManager::pluginUpdate(float deltaTime, bool jointsCaptured) { + BAIL_IF_NOT_LOADED _inputDevice->update(deltaTime, jointsCaptured); if (_inputDevice->_requestReset) { _container->requestReset(); @@ -128,6 +142,7 @@ void SixenseManager::pluginUpdate(float deltaTime, bool jointsCaptured) { } void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { + BAIL_IF_NOT_LOADED #ifdef HAVE_SIXENSE _buttonPressedMap.clear(); @@ -246,6 +261,7 @@ void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { } void SixenseManager::InputDevice::setDebugDrawRaw(bool flag) { + BAIL_IF_NOT_LOADED _debugDrawRaw = flag; if (!flag) { DebugDraw::getInstance().removeMyAvatarMarker("SIXENSE_RAW_LEFT"); @@ -254,6 +270,7 @@ void SixenseManager::InputDevice::setDebugDrawRaw(bool flag) { } void SixenseManager::InputDevice::setDebugDrawCalibrated(bool flag) { + BAIL_IF_NOT_LOADED _debugDrawCalibrated = flag; if (!flag) { DebugDraw::getInstance().removeMyAvatarMarker("SIXENSE_CALIBRATED_LEFT"); @@ -281,6 +298,7 @@ static bool calibrationRequested(SixenseControllerData* controllers) { } void SixenseManager::InputDevice::updateCalibration(SixenseControllerData* controllers) { + BAIL_IF_NOT_LOADED const SixenseControllerData* dataLeft = controllers; const SixenseControllerData* dataRight = controllers + 1; @@ -365,11 +383,13 @@ void SixenseManager::InputDevice::updateCalibration(SixenseControllerData* contr #endif // HAVE_SIXENSE void SixenseManager::InputDevice::focusOutEvent() { + BAIL_IF_NOT_LOADED _axisStateMap.clear(); _buttonPressedMap.clear(); }; void SixenseManager::InputDevice::handleButtonEvent(unsigned int buttons, bool left) { + BAIL_IF_NOT_LOADED using namespace controller; if (buttons & BUTTON_0) { _buttonPressedMap.insert(left ? BACK : START); @@ -395,6 +415,7 @@ void SixenseManager::InputDevice::handleButtonEvent(unsigned int buttons, bool l } void SixenseManager::InputDevice::handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left) { + BAIL_IF_NOT_LOADED #ifdef HAVE_SIXENSE auto hand = left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND; diff --git a/plugins/hifiSixense/src/SixenseManager.h b/plugins/hifiSixense/src/SixenseManager.h index 0de5fe93aa..37da7358e0 100644 --- a/plugins/hifiSixense/src/SixenseManager.h +++ b/plugins/hifiSixense/src/SixenseManager.h @@ -98,6 +98,8 @@ private: static const QString NAME; static const QString HYDRA_ID_STRING; + + static bool _sixenseLoaded; }; #endif // hifi_SixenseManager_h diff --git a/plugins/hifiSixense/src/SixenseSupportOSX.cpp b/plugins/hifiSixense/src/SixenseSupportOSX.cpp index 85e0f3fe48..fce2ea023b 100644 --- a/plugins/hifiSixense/src/SixenseSupportOSX.cpp +++ b/plugins/hifiSixense/src/SixenseSupportOSX.cpp @@ -63,6 +63,9 @@ void unloadSixense() { // sixense.h wrapper for OSX dynamic linking int sixenseInit() { loadSixense(); + if (!SIXENSE || !SIXENSE->isLoaded()) { + return SIXENSE_FAILURE; + } return FORWARD(); } int sixenseExit() { From 243fba461d3af418171479546b694a442ed5d415 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 21 Dec 2015 18:46:02 -0800 Subject: [PATCH 4/6] Add CMake flag for building only server components --- CMakeLists.txt | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 13d5b2bc79..0da8dc7309 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -197,6 +197,10 @@ if (WIN32) add_paths_to_fixup_libs("${QT_DIR}/bin") endif () +if (NOT DEFINED SERVER_ONLY) + set(SERVER_ONLY 0) +endif() + # add subdirectories for all targets if (NOT ANDROID) add_subdirectory(assignment-client) @@ -205,14 +209,16 @@ if (NOT ANDROID) set_target_properties(domain-server PROPERTIES FOLDER "Apps") add_subdirectory(ice-server) set_target_properties(ice-server PROPERTIES FOLDER "Apps") - add_subdirectory(interface) - set_target_properties(interface PROPERTIES FOLDER "Apps") add_subdirectory(stack-manager) set_target_properties(stack-manager PROPERTIES FOLDER "Apps") - add_subdirectory(tests) - add_subdirectory(plugins) + if (NOT SERVER_ONLY) + add_subdirectory(interface) + set_target_properties(interface PROPERTIES FOLDER "Apps") + add_subdirectory(tests) + add_subdirectory(plugins) + endif() add_subdirectory(tools) -endif () +endif() if (ANDROID OR DESKTOP_GVR) add_subdirectory(gvr-interface) From e0e1ae43f5f5bf7f7d48117345b82513138a576d Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 22 Dec 2015 09:28:38 -0800 Subject: [PATCH 5/6] Fixing legacy oculus plugin --- plugins/oculusLegacy/CMakeLists.txt | 3 +- .../src/OculusLegacyDisplayPlugin.cpp | 137 +++++++++--------- .../src/OculusLegacyDisplayPlugin.h | 16 +- 3 files changed, 73 insertions(+), 83 deletions(-) diff --git a/plugins/oculusLegacy/CMakeLists.txt b/plugins/oculusLegacy/CMakeLists.txt index 44cee83a7d..bf9d22410d 100644 --- a/plugins/oculusLegacy/CMakeLists.txt +++ b/plugins/oculusLegacy/CMakeLists.txt @@ -6,8 +6,7 @@ # See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html # -#if (NOT WIN32) -if (FALSE) +if (NOT WIN32) set(TARGET_NAME oculusLegacy) setup_hifi_plugin() diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index 078b6586c1..cc31613beb 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -39,12 +39,6 @@ uvec2 OculusLegacyDisplayPlugin::getRecommendedRenderSize() const { return _desiredFramebufferSize; } -void OculusLegacyDisplayPlugin::preRender() { - ovrHmd_GetEyePoses(_hmd, _frameIndex, _eyeOffsets, _eyePoses, &_trackingState); - ovrHmd_BeginFrame(_hmd, _frameIndex); - WindowOpenGLDisplayPlugin::preRender(); -} - glm::mat4 OculusLegacyDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const { return _eyeProjections[eye]; } @@ -57,12 +51,18 @@ glm::mat4 OculusLegacyDisplayPlugin::getEyeToHeadTransform(Eye eye) const { return toGlm(_eyePoses[eye]); } -// Should NOT be used for rendering as this will mess up timewarp. Use the getModelview() method above for -// any use of head poses for rendering, ensuring you use the correct eye -glm::mat4 OculusLegacyDisplayPlugin::getHeadPose() const { - return toGlm(_trackingState.HeadPose.ThePose); -} +glm::mat4 OculusLegacyDisplayPlugin::getHeadPose(uint32_t frameIndex) const { + static uint32_t lastFrameSeen = 0; + if (frameIndex > lastFrameSeen) { + Lock lock(_mutex); +// auto trackingState = ovr_GetTrackingState(_session, displayTime, frameIndex > lastFrameSeen); +// ovrHmd_GetEyePoses(_hmd, frameIndex, _eyeOffsets, _eyePoses, &_trackingState); + lastFrameSeen = frameIndex; + } +// return toGlm(trackingState.HeadPose.ThePose); + return glm::mat4(); +} bool OculusLegacyDisplayPlugin::isSupported() const { if (!ovr_Initialize(nullptr)) { @@ -92,10 +92,14 @@ bool OculusLegacyDisplayPlugin::isSupported() const { } void OculusLegacyDisplayPlugin::activate() { + WindowOpenGLDisplayPlugin::activate(); + + if (!(ovr_Initialize(nullptr))) { Q_ASSERT(false); qFatal("Failed to Initialize SDK"); } + _hswDismissed = false; _hmd = ovrHmd_Create(0); if (!_hmd) { @@ -107,13 +111,13 @@ void OculusLegacyDisplayPlugin::activate() { _eyeFovs[eye] = _hmd->MaxEyeFov[eye]; ovrEyeRenderDesc erd = _eyeRenderDescs[eye] = ovrHmd_GetRenderDesc(_hmd, eye, _eyeFovs[eye]); ovrMatrix4f ovrPerspectiveProjection = - ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded); + ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded); _eyeProjections[eye] = toGlm(ovrPerspectiveProjection); - + ovrPerspectiveProjection = - ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded); + ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded); _compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection); - + _eyeOffsets[eye] = erd.HmdToEyeViewOffset; eyeSizes[eye] = toGlm(ovrHmd_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f)); }); @@ -121,60 +125,27 @@ void OculusLegacyDisplayPlugin::activate() { combined.LeftTan = std::max(_eyeFovs[Left].LeftTan, _eyeFovs[Right].LeftTan); combined.RightTan = std::max(_eyeFovs[Left].RightTan, _eyeFovs[Right].RightTan); ovrMatrix4f ovrPerspectiveProjection = - ovrMatrix4f_Projection(combined, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded); + ovrMatrix4f_Projection(combined, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded); _eyeProjections[Mono] = toGlm(ovrPerspectiveProjection); - - _desiredFramebufferSize = uvec2( - eyeSizes[0].x + eyeSizes[1].x, - std::max(eyeSizes[0].y, eyeSizes[1].y)); - - _frameIndex = 0; - + + _desiredFramebufferSize = uvec2(eyeSizes[0].x + eyeSizes[1].x, + std::max(eyeSizes[0].y, eyeSizes[1].y)); + if (!ovrHmd_ConfigureTracking(_hmd, - ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0)) { + ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0)) { qFatal("Could not attach to sensor device"); } - WindowOpenGLDisplayPlugin::activate(); - int screen = getHmdScreen(); if (screen != -1) { _container->setFullscreen(qApp->screens()[screen]); } _window->installEventFilter(this); - _window->makeCurrent(); - ovrGLConfig config; memset(&config, 0, sizeof(ovrRenderAPIConfig)); - auto& header = config.Config.Header; - header.API = ovrRenderAPI_OpenGL; - header.BackBufferSize = _hmd->Resolution; - header.Multisample = 1; - int distortionCaps = 0 - | ovrDistortionCap_TimeWarp - ; - - memset(_eyeTextures, 0, sizeof(ovrTexture) * 2); - ovr_for_each_eye([&](ovrEyeType eye) { - auto& header = _eyeTextures[eye].Header; - header.API = ovrRenderAPI_OpenGL; - header.TextureSize = { (int)_desiredFramebufferSize.x, (int)_desiredFramebufferSize.y }; - header.RenderViewport.Size = header.TextureSize; - header.RenderViewport.Size.w /= 2; - if (eye == ovrEye_Right) { - header.RenderViewport.Pos.x = header.RenderViewport.Size.w; - } - }); - - #ifndef NDEBUG - ovrBool result = - #endif - ovrHmd_ConfigureRendering(_hmd, &config.Config, distortionCaps, _eyeFovs, _eyeRenderDescs); - assert(result); } void OculusLegacyDisplayPlugin::deactivate() { _window->removeEventFilter(this); - WindowOpenGLDisplayPlugin::deactivate(); QScreen* riftScreen = nullptr; @@ -194,20 +165,46 @@ void OculusLegacyDisplayPlugin::customizeContext() { glewInit(); glGetError(); WindowOpenGLDisplayPlugin::customizeContext(); -} -void OculusLegacyDisplayPlugin::preDisplay() { - _window->makeCurrent(); -} - -void OculusLegacyDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { - ++_frameIndex; + ovrGLConfig config; memset(&config, 0, sizeof(ovrRenderAPIConfig)); + auto& header = config.Config.Header; + header.API = ovrRenderAPI_OpenGL; + header.BackBufferSize = _hmd->Resolution; + header.Multisample = 1; + int distortionCaps = ovrDistortionCap_TimeWarp; + + memset(_eyeTextures, 0, sizeof(ovrTexture) * 2); ovr_for_each_eye([&](ovrEyeType eye) { - reinterpret_cast(_eyeTextures[eye]).OGL.TexId = finalTexture; + auto& header = _eyeTextures[eye].Header; + header.API = ovrRenderAPI_OpenGL; + header.TextureSize = { (int)_desiredFramebufferSize.x, (int)_desiredFramebufferSize.y }; + header.RenderViewport.Size = header.TextureSize; + header.RenderViewport.Size.w /= 2; + if (eye == ovrEye_Right) { + header.RenderViewport.Pos.x = header.RenderViewport.Size.w; + } + }); + +#ifndef NDEBUG + ovrBool result = +#endif + ovrHmd_ConfigureRendering(_hmd, &config.Config, distortionCaps, _eyeFovs, _eyeRenderDescs); + assert(result); +} + +void OculusLegacyDisplayPlugin::uncustomizeContext() { + WindowOpenGLDisplayPlugin::uncustomizeContext(); +} + +void OculusLegacyDisplayPlugin::internalPresent() { + ovrHmd_BeginFrame(_hmd, 0); + ovr_for_each_eye([&](ovrEyeType eye) { + reinterpret_cast(_eyeTextures[eye]).OGL.TexId = _currentSceneTexture; }); ovrHmd_EndFrame(_hmd, _eyePoses, _eyeTextures); } +/* // Pass input events on to the application bool OculusLegacyDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { if (!_hswDismissed && (event->type() == QEvent::KeyPress)) { @@ -221,17 +218,13 @@ bool OculusLegacyDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { } return WindowOpenGLDisplayPlugin::eventFilter(receiver, event); } - -// FIXME mirroring tot he main window is diffucult on OSX because it requires that we -// trigger a swap, which causes the client to wait for the v-sync of the main screen running -// at 60 Hz. This would introduce judder. Perhaps we can push mirroring to a separate -// thread -// FIXME If we move to the 'batch rendering on a different thread' we can possibly do this. -// however, we need to make sure it doesn't block the event handling. -void OculusLegacyDisplayPlugin::finishFrame() { - _window->doneCurrent(); -}; +*/ int OculusLegacyDisplayPlugin::getHmdScreen() const { return _hmdScreen; } + +float OculusLegacyDisplayPlugin::getTargetFrameRate() { + return TARGET_RATE_OculusLegacy; +} + diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h index d3e68585df..28fa4c6118 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h @@ -24,11 +24,9 @@ public: virtual void activate() override; virtual void deactivate() override; - virtual bool eventFilter(QObject* receiver, QEvent* event) override; +// virtual bool eventFilter(QObject* receiver, QEvent* event) override; virtual int getHmdScreen() const override; - virtual float getTargetFrameRate() override { return TARGET_RATE_OculusLegacy; } - // Stereo specific methods virtual bool isHmd() const override { return true; } virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override; @@ -36,20 +34,20 @@ public: virtual glm::uvec2 getRecommendedUiSize() const override { return uvec2(1920, 1080); } virtual void resetSensors() override; virtual glm::mat4 getEyeToHeadTransform(Eye eye) const override; - virtual glm::mat4 getHeadPose() const override; + virtual glm::mat4 getHeadPose(uint32_t frameIndex) const override; + + virtual float getTargetFrameRate() override; protected: virtual void customizeContext() override; - virtual void preRender() override; - virtual void preDisplay() override; - virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override; - // Do not perform swap in finish - virtual void finishFrame() override; + virtual void uncustomizeContext() override; + virtual void internalPresent() override; private: static const QString NAME; ovrHmd _hmd; + std::mutex _statelock; ovrTrackingState _trackingState; ovrEyeRenderDesc _eyeRenderDescs[2]; ovrPosef _eyePoses[2]; From 79d8b20637334c6d2e33fe41bdf14fa7cc585a8f Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 22 Dec 2015 10:09:45 -0800 Subject: [PATCH 6/6] Simulating Oculus interaction on legacy platforms --- .../src/OculusLegacyDisplayPlugin.cpp | 56 ++++++------------- .../src/OculusLegacyDisplayPlugin.h | 10 ++-- 2 files changed, 21 insertions(+), 45 deletions(-) diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index cc31613beb..f0f200fe1a 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -26,7 +26,7 @@ using namespace oglplus; -const QString OculusLegacyDisplayPlugin::NAME("Oculus Rift (0.5)"); +const QString OculusLegacyDisplayPlugin::NAME("Oculus Rift (0.5) (Simulated)"); const QString & OculusLegacyDisplayPlugin::getName() const { return NAME; @@ -56,12 +56,11 @@ glm::mat4 OculusLegacyDisplayPlugin::getHeadPose(uint32_t frameIndex) const { static uint32_t lastFrameSeen = 0; if (frameIndex > lastFrameSeen) { Lock lock(_mutex); -// auto trackingState = ovr_GetTrackingState(_session, displayTime, frameIndex > lastFrameSeen); -// ovrHmd_GetEyePoses(_hmd, frameIndex, _eyeOffsets, _eyePoses, &_trackingState); + _trackingState = ovrHmd_GetTrackingState(_hmd, ovr_GetTimeInSeconds()); + ovrHmd_GetEyePoses(_hmd, frameIndex, _eyeOffsets, _eyePoses, &_trackingState); lastFrameSeen = frameIndex; } -// return toGlm(trackingState.HeadPose.ThePose); - return glm::mat4(); + return toGlm(_trackingState.HeadPose.ThePose); } bool OculusLegacyDisplayPlugin::isSupported() const { @@ -93,7 +92,6 @@ bool OculusLegacyDisplayPlugin::isSupported() const { void OculusLegacyDisplayPlugin::activate() { WindowOpenGLDisplayPlugin::activate(); - if (!(ovr_Initialize(nullptr))) { Q_ASSERT(false); @@ -135,37 +133,26 @@ void OculusLegacyDisplayPlugin::activate() { ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0)) { qFatal("Could not attach to sensor device"); } - - int screen = getHmdScreen(); - if (screen != -1) { - _container->setFullscreen(qApp->screens()[screen]); - } - - _window->installEventFilter(this); } void OculusLegacyDisplayPlugin::deactivate() { - _window->removeEventFilter(this); WindowOpenGLDisplayPlugin::deactivate(); - - QScreen* riftScreen = nullptr; - if (_hmdScreen >= 0) { - riftScreen = qApp->screens()[_hmdScreen]; - } - _container->unsetFullscreen(riftScreen); - ovrHmd_Destroy(_hmd); _hmd = nullptr; ovr_Shutdown(); } + // DLL based display plugins MUST initialize GLEW inside the DLL code. void OculusLegacyDisplayPlugin::customizeContext() { - glewExperimental = true; - glewInit(); - glGetError(); + static std::once_flag once; + std::call_once(once, []{ + glewExperimental = true; + glewInit(); + glGetError(); + }); WindowOpenGLDisplayPlugin::customizeContext(); - +#if 0 ovrGLConfig config; memset(&config, 0, sizeof(ovrRenderAPIConfig)); auto& header = config.Config.Header; header.API = ovrRenderAPI_OpenGL; @@ -190,8 +177,11 @@ void OculusLegacyDisplayPlugin::customizeContext() { #endif ovrHmd_ConfigureRendering(_hmd, &config.Config, distortionCaps, _eyeFovs, _eyeRenderDescs); assert(result); +#endif + } +#if 0 void OculusLegacyDisplayPlugin::uncustomizeContext() { WindowOpenGLDisplayPlugin::uncustomizeContext(); } @@ -204,21 +194,7 @@ void OculusLegacyDisplayPlugin::internalPresent() { ovrHmd_EndFrame(_hmd, _eyePoses, _eyeTextures); } -/* -// Pass input events on to the application -bool OculusLegacyDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { - if (!_hswDismissed && (event->type() == QEvent::KeyPress)) { - static ovrHSWDisplayState hswState; - ovrHmd_GetHSWDisplayState(_hmd, &hswState); - if (hswState.Displayed) { - ovrHmd_DismissHSWDisplay(_hmd); - } else { - _hswDismissed = true; - } - } - return WindowOpenGLDisplayPlugin::eventFilter(receiver, event); -} -*/ +#endif int OculusLegacyDisplayPlugin::getHmdScreen() const { return _hmdScreen; diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h index 28fa4c6118..bc474634e5 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h @@ -24,7 +24,6 @@ public: virtual void activate() override; virtual void deactivate() override; -// virtual bool eventFilter(QObject* receiver, QEvent* event) override; virtual int getHmdScreen() const override; // Stereo specific methods @@ -40,17 +39,18 @@ public: protected: virtual void customizeContext() override; +#if 0 virtual void uncustomizeContext() override; virtual void internalPresent() override; - +#endif + private: static const QString NAME; ovrHmd _hmd; - std::mutex _statelock; - ovrTrackingState _trackingState; + mutable ovrTrackingState _trackingState; ovrEyeRenderDesc _eyeRenderDescs[2]; - ovrPosef _eyePoses[2]; + mutable ovrPosef _eyePoses[2]; ovrVector3f _eyeOffsets[2]; ovrFovPort _eyeFovs[2]; mat4 _eyeProjections[3];