From cefdbc312640674ca1979fa71090224429886865 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Tue, 2 Jun 2015 19:32:03 -0600 Subject: [PATCH 01/94] Checkpoint auto updater --- interface/CMakeLists.txt | 2 +- interface/src/Application.cpp | 72 +------------ interface/src/Application.h | 8 -- interface/src/ui/UpdateDialog.cpp | 55 ---------- interface/src/ui/UpdateDialog.h | 31 ------ interface/ui/updateDialog.ui | 132 ----------------------- libraries/auto-update/CMakeLists.txt | 3 + libraries/auto-update/src/AutoUpdate.cpp | 87 +++++++++++++++ libraries/auto-update/src/AutoUpdate.h | 63 +++++++++++ 9 files changed, 158 insertions(+), 295 deletions(-) delete mode 100644 interface/src/ui/UpdateDialog.cpp delete mode 100644 interface/src/ui/UpdateDialog.h delete mode 100644 interface/ui/updateDialog.ui create mode 100644 libraries/auto-update/CMakeLists.txt create mode 100644 libraries/auto-update/src/AutoUpdate.cpp create mode 100644 libraries/auto-update/src/AutoUpdate.h diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index a0b258fc10..99478e2b84 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -140,7 +140,7 @@ target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES}) # link required hifi libraries link_hifi_libraries(shared octree environment gpu model fbx networking entities avatars audio audio-client animation script-engine physics - render-utils entities-renderer ui) + render-utils entities-renderer ui auto-update) add_dependency_external_projects(sdl2) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 803955f02c..39d93986d8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -59,6 +59,7 @@ #include #include #include +#include #include #include #include @@ -539,8 +540,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // allow you to move an entity around in your hand _entityEditSender.setPacketsPerSecond(3000); // super high!! - checkVersion(); - _overlays.init(); // do this before scripts load _runningScriptsWidget->setRunningScripts(getRunningScripts()); @@ -612,6 +611,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : ddeTracker->init(); connect(ddeTracker.data(), &FaceTracker::muteToggled, this, &Application::faceTrackerMuteToggled); #endif + + AutoUpdate* applicationUpdater = new AutoUpdate; + applicationUpdater->checkForUpdate(); } @@ -4404,72 +4406,6 @@ void Application::toggleLogDialog() { } } -void Application::checkVersion() { - QNetworkRequest latestVersionRequest((QUrl(CHECK_VERSION_URL))); - latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); - latestVersionRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); - QNetworkReply* reply = NetworkAccessManager::getInstance().get(latestVersionRequest); - connect(reply, SIGNAL(finished()), SLOT(parseVersionXml())); -} - -void Application::parseVersionXml() { - - #ifdef Q_OS_WIN32 - QString operatingSystem("win"); - #endif - - #ifdef Q_OS_MAC - QString operatingSystem("mac"); - #endif - - #ifdef Q_OS_LINUX - QString operatingSystem("ubuntu"); - #endif - - QString latestVersion; - QUrl downloadUrl; - QString releaseNotes("Unavailable"); - QNetworkReply* sender = qobject_cast(QObject::sender()); - - QXmlStreamReader xml(sender); - - while (!xml.atEnd() && !xml.hasError()) { - if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == operatingSystem) { - while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == operatingSystem)) { - if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name().toString() == "version") { - xml.readNext(); - latestVersion = xml.text().toString(); - } - if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name().toString() == "url") { - xml.readNext(); - downloadUrl = QUrl(xml.text().toString()); - } - xml.readNext(); - } - } - xml.readNext(); - } - - if (!shouldSkipVersion(latestVersion) && applicationVersion() != latestVersion) { - new UpdateDialog(_glWidget, releaseNotes, latestVersion, downloadUrl); - } - sender->deleteLater(); -} - -bool Application::shouldSkipVersion(QString latestVersion) { - QFile skipFile(SKIP_FILENAME); - skipFile.open(QIODevice::ReadWrite); - QString skipVersion(skipFile.readAll()); - return (skipVersion == latestVersion || applicationVersion() == "dev"); -} - -void Application::skipVersion(QString latestVersion) { - QFile skipFile(SKIP_FILENAME); - skipFile.open(QIODevice::WriteOnly | QIODevice::Truncate); - skipFile.seek(0); - skipFile.write(latestVersion.toStdString().c_str()); -} - void Application::takeSnapshot() { QMediaPlayer* player = new QMediaPlayer(); QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav"); diff --git a/interface/src/Application.h b/interface/src/Application.h index b6efb6420b..2b3530531f 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -66,7 +66,6 @@ #include "ui/SnapshotShareDialog.h" #include "ui/LodToolsDialog.h" #include "ui/LogDialog.h" -#include "ui/UpdateDialog.h" #include "ui/overlays/Overlays.h" #include "ui/ApplicationOverlay.h" #include "ui/RunningScriptsWidget.h" @@ -311,8 +310,6 @@ public: NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; } - void skipVersion(QString latestVersion); - QStringList getRunningScripts() { return _scriptEnginesHash.keys(); } ScriptEngine* getScriptEngine(QString scriptHash) { return _scriptEnginesHash.contains(scriptHash) ? _scriptEnginesHash[scriptHash] : NULL; } @@ -454,8 +451,6 @@ private slots: void restoreMirrorView(); void shrinkMirrorView(); - void parseVersionXml(); - void manageRunningScriptsWidgetVisibility(bool shown); void runTests(); @@ -624,9 +619,6 @@ private: FileLogger* _logger; - void checkVersion(); - void displayUpdateDialog(); - bool shouldSkipVersion(QString latestVersion); void takeSnapshot(); TouchEvent _lastTouchEvent; diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp deleted file mode 100644 index 40b32d66e3..0000000000 --- a/interface/src/ui/UpdateDialog.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// -// UpdateDialog.cpp -// interface/src/ui -// -// 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 - -#include "ui_updateDialog.h" - -#include "Application.h" -#include "UpdateDialog.h" - - -UpdateDialog::UpdateDialog(QWidget *parent, const QString& releaseNotes, const QString& latestVersion, const QUrl& downloadURL) : - QDialog(parent), - _latestVersion(latestVersion), - _downloadUrl(downloadURL) -{ - Ui::Dialog dialogUI; - dialogUI.setupUi(this); - - QString updateRequired = QString("You are currently running build %1, the latest build released is %2." - "\n\nPlease download and install the most recent release to access the latest features and bug fixes.") - .arg(Application::getInstance()->applicationVersion(), latestVersion); - - setAttribute(Qt::WA_DeleteOnClose); - - QPushButton* downloadButton = findChild("downloadButton"); - QPushButton* skipButton = findChild("skipButton"); - QPushButton* closeButton = findChild("closeButton"); - QLabel* updateContent = findChild("updateContent"); - - updateContent->setText(updateRequired); - - connect(downloadButton, SIGNAL(released()), this, SLOT(handleDownload())); - connect(skipButton, SIGNAL(released()), this, SLOT(handleSkip())); - connect(closeButton, SIGNAL(released()), this, SLOT(close())); - - QMetaObject::invokeMethod(this, "show", Qt::QueuedConnection); -} - -void UpdateDialog::handleDownload() { - QDesktopServices::openUrl(_downloadUrl); - Application::getInstance()->quit(); -} - -void UpdateDialog::handleSkip() { - Application::getInstance()->skipVersion(_latestVersion); - this->close(); -} diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h deleted file mode 100644 index 15a97bf024..0000000000 --- a/interface/src/ui/UpdateDialog.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// UpdateDialog.h -// interface/src/ui -// -// 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 -// - -#ifndef hifi_UpdateDialog_h -#define hifi_UpdateDialog_h - -#include - -class UpdateDialog : public QDialog { - Q_OBJECT - -public: - UpdateDialog(QWidget* parent, const QString& releaseNotes, const QString& latestVersion, const QUrl& downloadURL); - -private: - QString _latestVersion; - QUrl _downloadUrl; - -private slots: - void handleDownload(); - void handleSkip(); -}; - -#endif // hifi_UpdateDialog_h diff --git a/interface/ui/updateDialog.ui b/interface/ui/updateDialog.ui deleted file mode 100644 index fc375e17c2..0000000000 --- a/interface/ui/updateDialog.ui +++ /dev/null @@ -1,132 +0,0 @@ - - - Dialog - - - Qt::NonModal - - - - 0 - 0 - 750 - 213 - - - - PointingHandCursor - - - Update Required - - - background-color: rgb(255, 255, 255); - - - - - 50 - 20 - 641 - 111 - - - - - Arial - -1 - - - - font-family: Arial; -font-size: 20px; - - - - - - true - - - - - - 360 - 160 - 374 - 42 - - - - - - - PointingHandCursor - - - background-color: #333333; -border-width: 0; -border-radius: 9px; -border-radius: 9px; -font-family: Arial; -font-size: 18px; -font-weight: 100; -color: #b7b7b7; -width: 120px; -height: 40px; - - - Download - - - - - - - PointingHandCursor - - - background-color: #333333; -border-width: 0; -border-radius: 9px; -border-radius: 9px; -font-family: Arial; -font-size: 18px; -font-weight: 100; -color: #b7b7b7; -width: 120px; -height: 40px; - - - Skip Version - - - - - - - PointingHandCursor - - - background-color: #333333; -border-width: 0; -border-radius: 9px; -border-radius: 9px; -font-family: Arial; -font-size: 18px; -font-weight: 100; -color: #b7b7b7; -width: 120px; -height: 40px; - - - Close - - - - - - - - - diff --git a/libraries/auto-update/CMakeLists.txt b/libraries/auto-update/CMakeLists.txt new file mode 100644 index 0000000000..0419048169 --- /dev/null +++ b/libraries/auto-update/CMakeLists.txt @@ -0,0 +1,3 @@ +set(TARGET_NAME auto-update) +setup_hifi_library(Network) +link_hifi_libraries(shared networking) diff --git a/libraries/auto-update/src/AutoUpdate.cpp b/libraries/auto-update/src/AutoUpdate.cpp new file mode 100644 index 0000000000..8132f2ee60 --- /dev/null +++ b/libraries/auto-update/src/AutoUpdate.cpp @@ -0,0 +1,87 @@ +// +// AutoUpdate.cpp +// libraries/auto-update/src +// +// Created by Leonardo Murillo on 6/1/2015. +// 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 +#include +#include "AutoUpdate.h" + +AutoUpdate::AutoUpdate() { +#ifdef Q_OS_WIN32 + _operatingSystem = "windows"; +#endif +#ifdef Q_OS_MAC + _operatingSystem = "mac"; +#endif +#ifdef Q_OS_LINUX + _operatingSystem = "ubuntu"; +#endif +} + +AutoUpdate::~AutoUpdate() { + qDebug() << "[LEOTEST] The object is now destroyed"; +} + +void AutoUpdate::checkForUpdate() { + this->getLatestVersionData(); + +} + +void AutoUpdate::getLatestVersionData() { + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkRequest latestVersionRequest(BUILDS_XML_URL); + latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + QNetworkReply* reply = networkAccessManager.get(latestVersionRequest); + connect(reply, SIGNAL(finished()), this, SLOT(parseLatestVersionData())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(handleError(QNetworkReply::NetworkError))); +} + + +void AutoUpdate::parseLatestVersionData() { + QNetworkReply* sender = qobject_cast(QObject::sender()); + + QXmlStreamReader xml(sender); + + while (!xml.atEnd() && !xml.hasError()) { + if (xml.name().toString() == "project" && + xml.attributes().hasAttribute("name") && + xml.attributes().value("name").toString() == "interface") { + xml.readNext(); + + while (!xml.atEnd() && !xml.hasError() && xml.name().toString() != "project") { + if (xml.name().toString() == "platform" && + xml.attributes().hasAttribute("name") && + xml.attributes().value("name").toString() == _operatingSystem) { + xml.readNext(); + while (!xml.atEnd() && !xml.hasError() && + xml.name().toString() != "platform") { + + if (xml.name().toString() == "build" && xml.tokenType() != QXmlStreamReader::EndElement) { + xml.readNext(); + QString version = xml.readElementText(); + xml.readNext(); + QString url = xml.readElementText(); + qDebug() << "[LEOTEST] Release version " << version << " downloadable at: " << url; + } + + xml.readNext(); + } + } + xml.readNext(); + } + + } else { + xml.readNext(); + } + } + sender->deleteLater(); +} \ No newline at end of file diff --git a/libraries/auto-update/src/AutoUpdate.h b/libraries/auto-update/src/AutoUpdate.h new file mode 100644 index 0000000000..ee0434fde8 --- /dev/null +++ b/libraries/auto-update/src/AutoUpdate.h @@ -0,0 +1,63 @@ +// +// AutoUpdate.h +// libraries/auto-update/src +// +// Created by Leonardo Murillo on 6/1/2015. +// 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__AutoUpdate__ +#define __hifi__AutoUpdate__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +const QUrl BUILDS_XML_URL("https://highfidelity.com/builds.xml"); + +class AutoUpdate : public QObject { + Q_OBJECT +public: + // Methods + AutoUpdate(); + ~AutoUpdate(); + + void checkForUpdate(); + QMap> getBuildData() { return _builds; } + void appendBuildData(int versionNumber, + QString downloadURL, + int pullRequestNumber, + QString releaseNotes); + +public slots: + +private: + // Members + QMap> _builds; + QString _operatingSystem; + + // Methods + void getLatestVersionData(); + void performAutoUpdate(); + void downloadUpdateVersion(); + +private slots: + void parseLatestVersionData(); +}; + +#endif /* defined(__hifi__AutoUpdate__) */ From ef27a7660c6bf9f81bf5866f0234ee51e324ed13 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Wed, 3 Jun 2015 10:31:33 -0600 Subject: [PATCH 02/94] Checkpoint: AutoUpdate reads data from builds.xml and stores it in a QMap --- libraries/auto-update/src/AutoUpdate.cpp | 59 ++++++++++++++++++++++-- libraries/auto-update/src/AutoUpdate.h | 16 +++++-- 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/libraries/auto-update/src/AutoUpdate.cpp b/libraries/auto-update/src/AutoUpdate.cpp index 8132f2ee60..32d1845218 100644 --- a/libraries/auto-update/src/AutoUpdate.cpp +++ b/libraries/auto-update/src/AutoUpdate.cpp @@ -25,6 +25,7 @@ AutoUpdate::AutoUpdate() { #ifdef Q_OS_LINUX _operatingSystem = "ubuntu"; #endif + //connect(this, SIGNAL(latestVersionDataParsed()), this, SLOT(debugBuildData())); } AutoUpdate::~AutoUpdate() { @@ -51,6 +52,14 @@ void AutoUpdate::parseLatestVersionData() { QXmlStreamReader xml(sender); + int version; + QString downloadUrl; + QString releaseTime; + QString releaseNotes; + QString commitSha; + QString pullRequestNumber; + + while (!xml.atEnd() && !xml.hasError()) { if (xml.name().toString() == "project" && xml.attributes().hasAttribute("name") && @@ -67,10 +76,26 @@ void AutoUpdate::parseLatestVersionData() { if (xml.name().toString() == "build" && xml.tokenType() != QXmlStreamReader::EndElement) { xml.readNext(); - QString version = xml.readElementText(); + version = xml.readElementText().toInt(); xml.readNext(); - QString url = xml.readElementText(); - qDebug() << "[LEOTEST] Release version " << version << " downloadable at: " << url; + downloadUrl = xml.readElementText(); + xml.readNext(); + releaseTime = xml.readElementText(); + xml.readNext(); + if (xml.name().toString() == "notes" && xml.tokenType() != QXmlStreamReader::EndElement) { + xml.readNext(); + while (!xml.atEnd() && !xml.hasError() && xml.name().toString() != "notes") { + if (xml.name().toString() == "note" && xml.tokenType() != QXmlStreamReader::EndElement) { + releaseNotes = releaseNotes + "\n" + xml.readElementText(); + } + xml.readNext(); + } + } + xml.readNext(); + commitSha = xml.readElementText(); + xml.readNext(); + pullRequestNumber = xml.readElementText(); + appendBuildData(version, downloadUrl, releaseTime, releaseNotes, pullRequestNumber); } xml.readNext(); @@ -84,4 +109,32 @@ void AutoUpdate::parseLatestVersionData() { } } sender->deleteLater(); + emit latestVersionDataParsed(); +} + +void AutoUpdate::debugBuildData() { + qDebug() << "[LEOTEST] We finished parsing the xml build data"; + foreach (int key, _builds.keys()) { + qDebug() << "[LEOTEST] Build number: " << QString::number(key); + foreach (QString detailsKey, _builds[key].keys()) { + qDebug() << "[LEOTEST] Key: " << detailsKey << " Value: " << _builds[key][detailsKey]; + } + } +} + +void AutoUpdate::performAutoUpdate() { + +} + +void AutoUpdate::downloadUpdateVersion() { + +} + +void AutoUpdate::appendBuildData(int versionNumber, QString downloadURL, QString releaseTime, QString releaseNotes, QString pullRequestNumber) { + QMap thisBuildDetails; + thisBuildDetails.insert("downloadUrl", downloadURL); + thisBuildDetails.insert("releaseTime", releaseTime); + thisBuildDetails.insert("releaseNotes", releaseNotes); + thisBuildDetails.insert("pullRequestNumber", pullRequestNumber); + _builds.insert(versionNumber, thisBuildDetails); } \ No newline at end of file diff --git a/libraries/auto-update/src/AutoUpdate.h b/libraries/auto-update/src/AutoUpdate.h index ee0434fde8..0ee9704ccb 100644 --- a/libraries/auto-update/src/AutoUpdate.h +++ b/libraries/auto-update/src/AutoUpdate.h @@ -38,11 +38,6 @@ public: ~AutoUpdate(); void checkForUpdate(); - QMap> getBuildData() { return _builds; } - void appendBuildData(int versionNumber, - QString downloadURL, - int pullRequestNumber, - QString releaseNotes); public slots: @@ -55,9 +50,20 @@ private: void getLatestVersionData(); void performAutoUpdate(); void downloadUpdateVersion(); + QMap> getBuildData() { return _builds; } + void appendBuildData(int versionNumber, + QString downloadURL, + QString releaseTime, + QString releaseNotes, + QString pullRequestNumber); private slots: void parseLatestVersionData(); + void debugBuildData(); + +signals: + void latestVersionDataParsed(); + }; #endif /* defined(__hifi__AutoUpdate__) */ From b60597aa90189ffaac26270644cfffdc4d857dc0 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Wed, 3 Jun 2015 19:38:54 -0600 Subject: [PATCH 03/94] AutoUpdate is now a singleton invoked via Dependency Manager, first steps towards QML update notification --- interface/resources/qml/UpdateDialog.qml | 19 ++++++++++++++ interface/src/Application.cpp | 5 +++- interface/src/ui/DialogsManager.cpp | 6 +++++ interface/src/ui/DialogsManager.h | 4 +++ interface/src/ui/UpdateDialog.cpp | 20 +++++++++++++++ interface/src/ui/UpdateDialog.h | 32 ++++++++++++++++++++++++ libraries/auto-update/src/AutoUpdate.cpp | 19 ++++++++++---- libraries/auto-update/src/AutoUpdate.h | 16 +++++++++--- 8 files changed, 111 insertions(+), 10 deletions(-) create mode 100644 interface/resources/qml/UpdateDialog.qml create mode 100644 interface/src/ui/UpdateDialog.cpp create mode 100644 interface/src/ui/UpdateDialog.h diff --git a/interface/resources/qml/UpdateDialog.qml b/interface/resources/qml/UpdateDialog.qml new file mode 100644 index 0000000000..4d564dd56d --- /dev/null +++ b/interface/resources/qml/UpdateDialog.qml @@ -0,0 +1,19 @@ +import Hifi 1.0 +import QtQuick 2.3 +import QtQuick.Controls.Styles 1.3 +import "controls" +import "styles" + +Rectangle { + id: page + width: 320; height: 480 + color: "lightgray" + + Text { + id: helloText + text: "Hello world!" + y: 30 + anchors.horizontalCenter: page.horizontalCenter + font.pointSize: 24; font.bold: true + } +} \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 39d93986d8..798e698f69 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -143,6 +143,7 @@ #include "ui/StandAloneJSConsole.h" #include "ui/Stats.h" #include "ui/AddressBarDialog.h" +#include "ui/UpdateDialog.h" // ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU #if defined(Q_OS_WIN) @@ -290,6 +291,7 @@ bool setupEssentials(int& argc, char** argv) { auto discoverabilityManager = DependencyManager::set(); auto sceneScriptingInterface = DependencyManager::set(); auto offscreenUi = DependencyManager::set(); + auto autoUpdate = DependencyManager::set(); return true; } @@ -612,7 +614,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(ddeTracker.data(), &FaceTracker::muteToggled, this, &Application::faceTrackerMuteToggled); #endif - AutoUpdate* applicationUpdater = new AutoUpdate; + auto applicationUpdater = DependencyManager::get(); + connect(applicationUpdater.data(), SIGNAL(newVersionIsAvailable()), dialogsManager.data(), SLOT(showUpdateDialog())); applicationUpdater->checkForUpdate(); } diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 1170e3c3a6..ebb329fcfd 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -31,6 +31,7 @@ #include "OctreeStatsDialog.h" #include "PreferencesDialog.h" #include "ScriptEditorWindow.h" +#include "UpdateDialog.h" void DialogsManager::toggleAddressBar() { @@ -50,6 +51,11 @@ void DialogsManager::showLoginDialog() { LoginDialog::show(); } +void DialogsManager::showUpdateDialog() { + qDebug() << "[LEOTEST] We should be showing the update dialog"; + UpdateDialog::show(); +} + void DialogsManager::octreeStatsDetails() { if (!_octreeStatsDialog) { _octreeStatsDialog = new OctreeStatsDialog(qApp->getWindow(), qApp->getOcteeSceneStats()); diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index fc2dad072b..f22773a622 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -35,6 +35,7 @@ class ScriptEditorWindow; class QMessageBox; class AvatarAppearanceDialog; class DomainConnectionDialog; +class UpdateDialog; class DialogsManager : public QObject, public Dependency { Q_OBJECT @@ -64,6 +65,9 @@ public slots: void showIRCLink(); void changeAvatarAppearance(); void showDomainConnectionDialog(); + + // Application Update + void showUpdateDialog(); private slots: void toggleToolWindow(); diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp new file mode 100644 index 0000000000..27fd94fa64 --- /dev/null +++ b/interface/src/ui/UpdateDialog.cpp @@ -0,0 +1,20 @@ +// +// UpdateDialog.cpp +// hifi +// +// Created by Leonardo Murillo on 6/3/15. +// +// + +#include "UpdateDialog.h" +#include "DependencyManager.h" + +HIFI_QML_DEF(UpdateDialog) + +UpdateDialog::UpdateDialog(QQuickItem* parent) : OffscreenQmlDialog(parent) { + +} + +void UpdateDialog::hide() { + ((QQuickItem*)parent())->setEnabled(false); +} diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h new file mode 100644 index 0000000000..bfc69064dd --- /dev/null +++ b/interface/src/ui/UpdateDialog.h @@ -0,0 +1,32 @@ +// +// UpdateDialog.h +// hifi +// +// Created by Leonardo Murillo on 6/3/15. +// +// + +#pragma once +#ifndef __hifi__UpdateDialog__ +#define __hifi__UpdateDialog__ + +#include + +class UpdateDialog : public OffscreenQmlDialog { + Q_OBJECT + HIFI_QML_DECL + +public: + UpdateDialog(QQuickItem* parent = nullptr); + +signals: + +protected: + void hide(); + +private: + + +}; + +#endif /* defined(__hifi__UpdateDialog__) */ diff --git a/libraries/auto-update/src/AutoUpdate.cpp b/libraries/auto-update/src/AutoUpdate.cpp index 32d1845218..2324e69a2e 100644 --- a/libraries/auto-update/src/AutoUpdate.cpp +++ b/libraries/auto-update/src/AutoUpdate.cpp @@ -25,7 +25,7 @@ AutoUpdate::AutoUpdate() { #ifdef Q_OS_LINUX _operatingSystem = "ubuntu"; #endif - //connect(this, SIGNAL(latestVersionDataParsed()), this, SLOT(debugBuildData())); + connect(this, SIGNAL(latestVersionDataParsed()), this, SLOT(checkVersionAndNotify())); } AutoUpdate::~AutoUpdate() { @@ -43,7 +43,6 @@ void AutoUpdate::getLatestVersionData() { latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); QNetworkReply* reply = networkAccessManager.get(latestVersionRequest); connect(reply, SIGNAL(finished()), this, SLOT(parseLatestVersionData())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(handleError(QNetworkReply::NetworkError))); } @@ -96,6 +95,7 @@ void AutoUpdate::parseLatestVersionData() { xml.readNext(); pullRequestNumber = xml.readElementText(); appendBuildData(version, downloadUrl, releaseTime, releaseNotes, pullRequestNumber); + releaseNotes = ""; } xml.readNext(); @@ -122,12 +122,21 @@ void AutoUpdate::debugBuildData() { } } -void AutoUpdate::performAutoUpdate() { +void AutoUpdate::checkVersionAndNotify() { + qDebug() << "[LEOTEST] We are checking and notifying for updates"; + int latestVersionAvailable = _builds.lastKey(); + if (QCoreApplication::applicationVersion() != "dev" && + QCoreApplication::applicationVersion().toInt() < latestVersionAvailable) { + emit newVersionIsAvailable(); + } +} + +void AutoUpdate::performAutoUpdate(int version) { } -void AutoUpdate::downloadUpdateVersion() { - +void AutoUpdate::downloadUpdateVersion(int version) { + emit newVersionIsDownloaded(); } void AutoUpdate::appendBuildData(int versionNumber, QString downloadURL, QString releaseTime, QString releaseNotes, QString pullRequestNumber) { diff --git a/libraries/auto-update/src/AutoUpdate.h b/libraries/auto-update/src/AutoUpdate.h index 0ee9704ccb..d77bdc89da 100644 --- a/libraries/auto-update/src/AutoUpdate.h +++ b/libraries/auto-update/src/AutoUpdate.h @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -27,17 +28,22 @@ #include #include +#include + const QUrl BUILDS_XML_URL("https://highfidelity.com/builds.xml"); -class AutoUpdate : public QObject { +class AutoUpdate : public QObject, public Dependency { Q_OBJECT + SINGLETON_DEPENDENCY + public: // Methods AutoUpdate(); ~AutoUpdate(); void checkForUpdate(); + QMap> getBuildData() { return _builds; } public slots: @@ -48,9 +54,7 @@ private: // Methods void getLatestVersionData(); - void performAutoUpdate(); - void downloadUpdateVersion(); - QMap> getBuildData() { return _builds; } + void downloadUpdateVersion(int version); void appendBuildData(int versionNumber, QString downloadURL, QString releaseTime, @@ -60,9 +64,13 @@ private: private slots: void parseLatestVersionData(); void debugBuildData(); + void checkVersionAndNotify(); + void performAutoUpdate(int version); signals: void latestVersionDataParsed(); + void newVersionIsAvailable(); + void newVersionIsDownloaded(); }; From dea80d175756df43683d9d7137680617265a0210 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Fri, 5 Jun 2015 16:17:54 -0600 Subject: [PATCH 04/94] Styling update dialog and working towards passing build details from c++ to qml --- interface/resources/qml/UpdateDialog.qml | 73 ++++++++++++++++++++---- interface/src/ui/DialogsManager.cpp | 7 ++- interface/src/ui/DialogsManager.h | 1 + interface/src/ui/UpdateDialog.cpp | 27 +++++++++ interface/src/ui/UpdateDialog.h | 14 ++++- libraries/auto-update/src/AutoUpdate.cpp | 13 +++-- libraries/auto-update/src/AutoUpdate.h | 4 +- 7 files changed, 118 insertions(+), 21 deletions(-) diff --git a/interface/resources/qml/UpdateDialog.qml b/interface/resources/qml/UpdateDialog.qml index 4d564dd56d..884b243682 100644 --- a/interface/resources/qml/UpdateDialog.qml +++ b/interface/resources/qml/UpdateDialog.qml @@ -4,16 +4,67 @@ import QtQuick.Controls.Styles 1.3 import "controls" import "styles" -Rectangle { - id: page - width: 320; height: 480 - color: "lightgray" - - Text { - id: helloText - text: "Hello world!" - y: 30 - anchors.horizontalCenter: page.horizontalCenter - font.pointSize: 24; font.bold: true +Item { + id: updateDialog + implicitWidth: backgroundImage.width + implicitHeight: backgroundImage.height + releaseNotes.height + x: parent ? parent.width / 2 - width / 2 : 0 + y: parent ? parent.height / 2 - height / 2 : 0 + + Image { + id: backgroundImage + source: "../images/update-available.svg" + width: 576 + height: 80 + smooth: true + + Text { + id: updateAvailableTitle + text: "Update available" + color: "#000000" + x: 90 + y: 17 + } + + Text { + id: updateAvailableDetails + text: updateDialog.updateAvailableDetails + width: parent.width + anchors.top: updateAvailableTitle.bottom + anchors.topMargin: 3 + font.pixelSize: 15 + color: "#545454" + x: 90 + } } + + Rectangle { + id: releaseNotes + color: "#CCE8E8E8" + anchors.top: backgroundImage.bottom + anchors.topMargin: 0 + width: backgroundImage.width - 90 + height: releaseNotesContent.height + x: 50 + radius: 10 + + + TextEdit { + id: releaseNotesContent + readOnly: true + font.pixelSize: 13 + width: parent.width + x: 10 + y: 10 + color: "#111111" + text: + "These are the release notes: \n" + + "And some more text about what this includes: \n" + + "These are the release notes: \n" + + "And some more text about what this includes: \n" + + "These are the release notes: \n" + + "And some more text about what this includes: \n"; + } + } + } \ No newline at end of file diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index ebb329fcfd..45c9ea4151 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -53,7 +53,12 @@ void DialogsManager::showLoginDialog() { void DialogsManager::showUpdateDialog() { qDebug() << "[LEOTEST] We should be showing the update dialog"; - UpdateDialog::show(); + if (!_updateDialog) { + _updateDialog = new UpdateDialog(); + connect(_updateDialog, SIGNAL(closed()), _updateDialog, SLOT(deleteLater())); + _updateDialog->setUpdateAvailableDetails(""); + _updateDialog->show(); + } } void DialogsManager::octreeStatsDetails() { diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index f22773a622..7eb716b73c 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -105,6 +105,7 @@ private: QPointer _scriptEditor; QPointer _avatarAppearanceDialog; QPointer _domainConnectionDialog; + QPointer _updateDialog; }; #endif // hifi_DialogsManager_h diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index 27fd94fa64..abd7587091 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -8,6 +8,7 @@ #include "UpdateDialog.h" #include "DependencyManager.h" +#include HIFI_QML_DEF(UpdateDialog) @@ -15,6 +16,32 @@ UpdateDialog::UpdateDialog(QQuickItem* parent) : OffscreenQmlDialog(parent) { } +UpdateDialog::~UpdateDialog() { +} + +void UpdateDialog::displayDialog() { + setUpdateAvailableDetails(""); + show(); +} + +void UpdateDialog::setUpdateAvailableDetails(const QString& updateAvailableDetails) { + if (updateAvailableDetails != _updateAvailableDetails) { + auto applicationUpdater = DependencyManager::get(); + foreach (int key, applicationUpdater.data()->getBuildData().keys()) { + qDebug() << "[LEOTEST] Build number: " << QString::number(key); + } + _updateAvailableDetails = "This is just a test " + QString::number(applicationUpdater.data()->getBuildData().lastKey()); + } +} + +QString UpdateDialog::updateAvailableDetails() const { + return _updateAvailableDetails; +} + void UpdateDialog::hide() { ((QQuickItem*)parent())->setEnabled(false); } + +void UpdateDialog::triggerBuildDownload(const int &buildNumber) { + qDebug() << "[LEOTEST] Triggering download of build number: " << QString::number(buildNumber); +} \ No newline at end of file diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h index bfc69064dd..23f2a0faf6 100644 --- a/interface/src/ui/UpdateDialog.h +++ b/interface/src/ui/UpdateDialog.h @@ -16,16 +16,28 @@ class UpdateDialog : public OffscreenQmlDialog { Q_OBJECT HIFI_QML_DECL + Q_PROPERTY(QString updateAvailableDetails READ updateAvailableDetails WRITE setUpdateAvailableDetails NOTIFY updateAvailableDetailsChanged) + public: UpdateDialog(QQuickItem* parent = nullptr); + ~UpdateDialog(); + + void displayDialog(); + void setUpdateAvailableDetails(const QString& updateAvailableDetails); + QString updateAvailableDetails() const; signals: + void updateAvailableDetailsChanged(); protected: void hide(); -private: +private: + QString _updateAvailableDetails; + +protected: + Q_INVOKABLE void triggerBuildDownload(const int& buildNumber); }; diff --git a/libraries/auto-update/src/AutoUpdate.cpp b/libraries/auto-update/src/AutoUpdate.cpp index 2324e69a2e..093a7b5d86 100644 --- a/libraries/auto-update/src/AutoUpdate.cpp +++ b/libraries/auto-update/src/AutoUpdate.cpp @@ -26,6 +26,7 @@ AutoUpdate::AutoUpdate() { _operatingSystem = "ubuntu"; #endif connect(this, SIGNAL(latestVersionDataParsed()), this, SLOT(checkVersionAndNotify())); + _builds = new QMap>; } AutoUpdate::~AutoUpdate() { @@ -114,17 +115,17 @@ void AutoUpdate::parseLatestVersionData() { void AutoUpdate::debugBuildData() { qDebug() << "[LEOTEST] We finished parsing the xml build data"; - foreach (int key, _builds.keys()) { + foreach (int key, _builds->keys()) { qDebug() << "[LEOTEST] Build number: " << QString::number(key); - foreach (QString detailsKey, _builds[key].keys()) { - qDebug() << "[LEOTEST] Key: " << detailsKey << " Value: " << _builds[key][detailsKey]; - } + //foreach (QString detailsKey, _builds[key].keys()) { + // qDebug() << "[LEOTEST] Key: " << detailsKey << " Value: " << _builds[key][detailsKey]; + //} } } void AutoUpdate::checkVersionAndNotify() { qDebug() << "[LEOTEST] We are checking and notifying for updates"; - int latestVersionAvailable = _builds.lastKey(); + int latestVersionAvailable = _builds->lastKey(); if (QCoreApplication::applicationVersion() != "dev" && QCoreApplication::applicationVersion().toInt() < latestVersionAvailable) { emit newVersionIsAvailable(); @@ -145,5 +146,5 @@ void AutoUpdate::appendBuildData(int versionNumber, QString downloadURL, QString thisBuildDetails.insert("releaseTime", releaseTime); thisBuildDetails.insert("releaseNotes", releaseNotes); thisBuildDetails.insert("pullRequestNumber", pullRequestNumber); - _builds.insert(versionNumber, thisBuildDetails); + _builds->insert(versionNumber, thisBuildDetails); } \ No newline at end of file diff --git a/libraries/auto-update/src/AutoUpdate.h b/libraries/auto-update/src/AutoUpdate.h index d77bdc89da..4b32d68c48 100644 --- a/libraries/auto-update/src/AutoUpdate.h +++ b/libraries/auto-update/src/AutoUpdate.h @@ -43,13 +43,13 @@ public: ~AutoUpdate(); void checkForUpdate(); - QMap> getBuildData() { return _builds; } + QMap> &getBuildData() { return *_builds; } public slots: private: // Members - QMap> _builds; + QMap> *_builds; QString _operatingSystem; // Methods From 68b419deecc5dfce78c32328f52ea41fad89da00 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Fri, 5 Jun 2015 17:11:32 -0600 Subject: [PATCH 05/94] Syncing --- interface/resources/qml/UpdateDialog.qml | 4 +++- interface/src/Application.cpp | 1 + interface/src/ui/UpdateDialog.cpp | 7 +++---- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/UpdateDialog.qml b/interface/resources/qml/UpdateDialog.qml index 884b243682..a7e91349a6 100644 --- a/interface/resources/qml/UpdateDialog.qml +++ b/interface/resources/qml/UpdateDialog.qml @@ -4,8 +4,10 @@ import QtQuick.Controls.Styles 1.3 import "controls" import "styles" -Item { +UpdateDialog { + HifiConstants { id: hifi } id: updateDialog + objectName: "UpdateDialog" implicitWidth: backgroundImage.width implicitHeight: backgroundImage.height + releaseNotes.height x: parent ? parent.width / 2 - width / 2 : 0 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5480e34dd8..3ee470cc17 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -809,6 +809,7 @@ void Application::initializeUi() { LoginDialog::registerType(); MessageDialog::registerType(); VrMenu::registerType(); + UpdateDialog::registerType(); auto offscreenUi = DependencyManager::get(); offscreenUi->create(_glWidget->context()->contextHandle()); diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index abd7587091..14502f28fc 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -25,12 +25,11 @@ void UpdateDialog::displayDialog() { } void UpdateDialog::setUpdateAvailableDetails(const QString& updateAvailableDetails) { - if (updateAvailableDetails != _updateAvailableDetails) { + if (updateAvailableDetails == "") { auto applicationUpdater = DependencyManager::get(); - foreach (int key, applicationUpdater.data()->getBuildData().keys()) { - qDebug() << "[LEOTEST] Build number: " << QString::number(key); - } _updateAvailableDetails = "This is just a test " + QString::number(applicationUpdater.data()->getBuildData().lastKey()); + qDebug() << "[LEOTEST] We are updating the text in the dialog"; + emit updateAvailableDetailsChanged(); } } From bd85ce13124532d5be688241840fabbd47605f16 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Mon, 8 Jun 2015 13:55:28 -0600 Subject: [PATCH 06/94] Auto Update checkpoint - dialog is QML and populated with real data --- interface/resources/qml/UpdateDialog.qml | 151 ++++++++++++++--------- interface/src/ui/DialogsManager.cpp | 8 +- interface/src/ui/UpdateDialog.cpp | 28 ++--- interface/src/ui/UpdateDialog.h | 14 +-- libraries/auto-update/src/AutoUpdate.cpp | 8 +- 5 files changed, 123 insertions(+), 86 deletions(-) diff --git a/interface/resources/qml/UpdateDialog.qml b/interface/resources/qml/UpdateDialog.qml index a7e91349a6..a823bfff79 100644 --- a/interface/resources/qml/UpdateDialog.qml +++ b/interface/resources/qml/UpdateDialog.qml @@ -4,69 +4,108 @@ import QtQuick.Controls.Styles 1.3 import "controls" import "styles" -UpdateDialog { +DialogContainer { HifiConstants { id: hifi } - id: updateDialog + id: root objectName: "UpdateDialog" - implicitWidth: backgroundImage.width - implicitHeight: backgroundImage.height + releaseNotes.height + implicitWidth: updateDialog.width + implicitHeight: updateDialog.height x: parent ? parent.width / 2 - width / 2 : 0 y: parent ? parent.height / 2 - height / 2 : 0 - Image { - id: backgroundImage - source: "../images/update-available.svg" - width: 576 - height: 80 - smooth: true + UpdateDialog { + id: updateDialog - Text { - id: updateAvailableTitle - text: "Update available" - color: "#000000" - x: 90 - y: 17 - } + implicitWidth: backgroundRectangle.width + implicitHeight: backgroundRectangle.height - Text { - id: updateAvailableDetails - text: updateDialog.updateAvailableDetails - width: parent.width - anchors.top: updateAvailableTitle.bottom - anchors.topMargin: 3 - font.pixelSize: 15 - color: "#545454" - x: 90 + readonly property int inputWidth: 500 + readonly property int inputHeight: 60 + readonly property int borderWidth: 30 + readonly property int closeMargin: 16 + readonly property int inputSpacing: 16 + + Column { + id: mainContent + width: updateDialog.inputWidth + spacing: updateDialog.inputSpacing + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: parent.verticalCenter + } + + Rectangle { + id: backgroundRectangle + color: "#2c86b1" + opacity: 0.85 + radius: updateDialog.closeMargin * 2 + + width: updateDialog.inputWidth + updateDialog.borderWidth * 2 + height: updateDialog.inputHeight * 6 + updateDialog.closeMargin * 2 + + Rectangle { + id: dialogTitle + width: updateDialog.inputWidth + height: updateDialog.inputHeight + radius: height / 2 + color: "#ebebeb" + + anchors { + top: parent.top + topMargin: updateDialog.inputSpacing + horizontalCenter: parent.horizontalCenter + } + + Text { + id: updateAvailableText + text: "Update Available" + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + leftMargin: updateDialog.inputSpacing + } + } + + Text { + text: updateDialog.updateAvailableDetails + font.pixelSize: 14 + color: hifi.colors.text + anchors { + verticalCenter: parent.verticalCenter + left: updateAvailableText.right + leftMargin: 13 + } + } + } + + Flickable { + id: scrollArea + anchors { + top: dialogTitle.bottom + } + contentWidth: updateDialog.inputWidth + contentHeight: backgroundRectangle.height - (dialogTitle.height * 2) + width: updateDialog.inputWidth + height: backgroundRectangle.height - (dialogTitle.height * 2) + flickableDirection: Flickable.VerticalFlick + clip: true + + TextEdit { + id: releaseNotes + wrapMode: TextEdit.Wrap + width: parent.width + readOnly: true + text: updateDialog.releaseNotes + font.pixelSize: 14 + color: hifi.colors.text + anchors { + left: parent.left + leftMargin: updateDialog.borderWidth + } + } + + } + } } } - - Rectangle { - id: releaseNotes - color: "#CCE8E8E8" - anchors.top: backgroundImage.bottom - anchors.topMargin: 0 - width: backgroundImage.width - 90 - height: releaseNotesContent.height - x: 50 - radius: 10 - - - TextEdit { - id: releaseNotesContent - readOnly: true - font.pixelSize: 13 - width: parent.width - x: 10 - y: 10 - color: "#111111" - text: - "These are the release notes: \n" + - "And some more text about what this includes: \n" + - "These are the release notes: \n" + - "And some more text about what this includes: \n" + - "These are the release notes: \n" + - "And some more text about what this includes: \n"; - } - } - } \ No newline at end of file diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 45c9ea4151..81c7cd2770 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -52,13 +52,7 @@ void DialogsManager::showLoginDialog() { } void DialogsManager::showUpdateDialog() { - qDebug() << "[LEOTEST] We should be showing the update dialog"; - if (!_updateDialog) { - _updateDialog = new UpdateDialog(); - connect(_updateDialog, SIGNAL(closed()), _updateDialog, SLOT(deleteLater())); - _updateDialog->setUpdateAvailableDetails(""); - _updateDialog->show(); - } + UpdateDialog::show(); } void DialogsManager::octreeStatsDetails() { diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index 14502f28fc..c5a6234f2c 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -13,30 +13,30 @@ HIFI_QML_DEF(UpdateDialog) UpdateDialog::UpdateDialog(QQuickItem* parent) : OffscreenQmlDialog(parent) { - + qDebug() << "[LEOTEST] We are creating the dialog"; + auto applicationUpdater = DependencyManager::get(); + int currentVersion = QCoreApplication::applicationVersion().toInt(); + int latestVersion = applicationUpdater.data()->getBuildData().lastKey(); + int versionsBehind = latestVersion - currentVersion; + _updateAvailableDetails = "v." + QString::number(latestVersion) + " released on " + applicationUpdater.data()->getBuildData()[latestVersion]["releaseTime"]; + _updateAvailableDetails += "\nYou are " + QString::number(versionsBehind) + " versions behind"; + _releaseNotes = applicationUpdater.data()->getBuildData()[latestVersion]["releaseNotes"]; + qDebug() << "[LEOTEST] Release time " << applicationUpdater.data()->getBuildData()[latestVersion]["releaseTime"]; + qDebug() << "[LEOTEST] Release notes: " << applicationUpdater.data()->getBuildData()[latestVersion]["releaseNotes"]; } UpdateDialog::~UpdateDialog() { -} -void UpdateDialog::displayDialog() { - setUpdateAvailableDetails(""); - show(); -} - -void UpdateDialog::setUpdateAvailableDetails(const QString& updateAvailableDetails) { - if (updateAvailableDetails == "") { - auto applicationUpdater = DependencyManager::get(); - _updateAvailableDetails = "This is just a test " + QString::number(applicationUpdater.data()->getBuildData().lastKey()); - qDebug() << "[LEOTEST] We are updating the text in the dialog"; - emit updateAvailableDetailsChanged(); - } } QString UpdateDialog::updateAvailableDetails() const { return _updateAvailableDetails; } +QString UpdateDialog::releaseNotes() const { + return _releaseNotes; +} + void UpdateDialog::hide() { ((QQuickItem*)parent())->setEnabled(false); } diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h index 23f2a0faf6..6ba8f0c46b 100644 --- a/interface/src/ui/UpdateDialog.h +++ b/interface/src/ui/UpdateDialog.h @@ -10,31 +10,29 @@ #ifndef __hifi__UpdateDialog__ #define __hifi__UpdateDialog__ + +#include #include class UpdateDialog : public OffscreenQmlDialog { Q_OBJECT HIFI_QML_DECL - Q_PROPERTY(QString updateAvailableDetails READ updateAvailableDetails WRITE setUpdateAvailableDetails NOTIFY updateAvailableDetailsChanged) + Q_PROPERTY(QString updateAvailableDetails READ updateAvailableDetails) + Q_PROPERTY(QString releaseNotes READ releaseNotes) public: UpdateDialog(QQuickItem* parent = nullptr); ~UpdateDialog(); - - void displayDialog(); - void setUpdateAvailableDetails(const QString& updateAvailableDetails); QString updateAvailableDetails() const; - -signals: - void updateAvailableDetailsChanged(); + QString releaseNotes() const; protected: void hide(); - private: QString _updateAvailableDetails; + QString _releaseNotes; protected: Q_INVOKABLE void triggerBuildDownload(const int& buildNumber); diff --git a/libraries/auto-update/src/AutoUpdate.cpp b/libraries/auto-update/src/AutoUpdate.cpp index 093a7b5d86..be52b283c5 100644 --- a/libraries/auto-update/src/AutoUpdate.cpp +++ b/libraries/auto-update/src/AutoUpdate.cpp @@ -140,11 +140,17 @@ void AutoUpdate::downloadUpdateVersion(int version) { emit newVersionIsDownloaded(); } -void AutoUpdate::appendBuildData(int versionNumber, QString downloadURL, QString releaseTime, QString releaseNotes, QString pullRequestNumber) { +void AutoUpdate::appendBuildData(int versionNumber, + QString downloadURL, + QString releaseTime, + QString releaseNotes, + QString pullRequestNumber) { + QMap thisBuildDetails; thisBuildDetails.insert("downloadUrl", downloadURL); thisBuildDetails.insert("releaseTime", releaseTime); thisBuildDetails.insert("releaseNotes", releaseNotes); thisBuildDetails.insert("pullRequestNumber", pullRequestNumber); _builds->insert(versionNumber, thisBuildDetails); + } \ No newline at end of file From edce36f5a120f76a9453f4d805e9e74d2f44382f Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Mon, 8 Jun 2015 14:29:10 -0600 Subject: [PATCH 07/94] Checkpoint - buttons in place --- interface/resources/qml/UpdateDialog.qml | 101 ++++++++++++++++++++++- interface/src/ui/UpdateDialog.cpp | 13 +-- interface/src/ui/UpdateDialog.h | 7 +- 3 files changed, 110 insertions(+), 11 deletions(-) diff --git a/interface/resources/qml/UpdateDialog.qml b/interface/resources/qml/UpdateDialog.qml index a823bfff79..10b0839cca 100644 --- a/interface/resources/qml/UpdateDialog.qml +++ b/interface/resources/qml/UpdateDialog.qml @@ -1,6 +1,7 @@ import Hifi 1.0 import QtQuick 2.3 import QtQuick.Controls.Styles 1.3 +import QtGraphicalEffects 1.0 import "controls" import "styles" @@ -24,6 +25,12 @@ DialogContainer { readonly property int borderWidth: 30 readonly property int closeMargin: 16 readonly property int inputSpacing: 16 + readonly property int buttonWidth: 150 + readonly property int buttonHeight: 50 + readonly property int buttonRadius: 15 + + signal triggerBuildDownload + signal closeUpdateDialog Column { id: mainContent @@ -84,9 +91,9 @@ DialogContainer { top: dialogTitle.bottom } contentWidth: updateDialog.inputWidth - contentHeight: backgroundRectangle.height - (dialogTitle.height * 2) + contentHeight: backgroundRectangle.height - (dialogTitle.height * 2.5) width: updateDialog.inputWidth - height: backgroundRectangle.height - (dialogTitle.height * 2) + height: backgroundRectangle.height - (dialogTitle.height * 2.5) flickableDirection: Flickable.VerticalFlick clip: true @@ -105,6 +112,96 @@ DialogContainer { } } + + Rectangle { + id: downloadButton + width: updateDialog.buttonWidth + height: updateDialog.buttonHeight + radius: updateDialog.buttonRadius + color: "green" + anchors { + top: scrollArea.bottom + topMargin: 10 + right: backgroundRectangle.right + rightMargin: 15 + } + Accessible.name: "Upgrade" + Accessible.description: "Download and update to latest version" + Accessible.role: Accessible.Button + Accessible.onPressAction: { + updateDialog.triggerBuildDownload() + } + + + Text { + text: "Upgrade" + anchors { + verticalCenter: parent.verticalCenter + horizontalCenter: parent.horizontalCenter + } + + } + } + + DropShadow { + anchors.fill: downloadButton + horizontalOffset: 2 + verticalOffset: 2 + radius: updateDialog.buttonRadius + samples: 16 + color: "#80000000" + source: downloadButton + } + + MouseArea { + id: downloadButtonAction + anchors.fill: downloadButton + onClicked: updateDialog.triggerBuildDownload() + } + + Rectangle { + id: cancelButton + width: updateDialog.buttonWidth + height: updateDialog.buttonHeight + radius: updateDialog.buttonRadius + color: "red" + anchors { + top: scrollArea.bottom + topMargin: 10 + right: downloadButton.left + rightMargin: 15 + } + Accessible.name: "Cancel" + Accessible.description: "Do not upgrade your current version" + Accessible.role: Accessible.Button + Accessible.onPressAction: { + updateDialog.closeUpdateDialog() + } + + Text { + text: "Cancel" + anchors { + verticalCenter: parent.verticalCenter + horizontalCenter: parent.horizontalCenter + } + } + } + + DropShadow { + anchors.fill: cancelButton + horizontalOffset: 2 + verticalOffset: 2 + radius: updateDialog.buttonRadius + samples: 16 + color: "#80000000" + source: cancelButton + } + + MouseArea { + id: cancelButtonAction + anchors.fill: cancelButton + onClicked: updateDialog.closeUpdateDialog() + } } } } diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index c5a6234f2c..5cf5a4fbed 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -18,11 +18,9 @@ UpdateDialog::UpdateDialog(QQuickItem* parent) : OffscreenQmlDialog(parent) { int currentVersion = QCoreApplication::applicationVersion().toInt(); int latestVersion = applicationUpdater.data()->getBuildData().lastKey(); int versionsBehind = latestVersion - currentVersion; - _updateAvailableDetails = "v." + QString::number(latestVersion) + " released on " + applicationUpdater.data()->getBuildData()[latestVersion]["releaseTime"]; + _updateAvailableDetails = "v" + QString::number(latestVersion) + " released on " + applicationUpdater.data()->getBuildData()[latestVersion]["releaseTime"]; _updateAvailableDetails += "\nYou are " + QString::number(versionsBehind) + " versions behind"; _releaseNotes = applicationUpdater.data()->getBuildData()[latestVersion]["releaseNotes"]; - qDebug() << "[LEOTEST] Release time " << applicationUpdater.data()->getBuildData()[latestVersion]["releaseTime"]; - qDebug() << "[LEOTEST] Release notes: " << applicationUpdater.data()->getBuildData()[latestVersion]["releaseNotes"]; } UpdateDialog::~UpdateDialog() { @@ -37,10 +35,15 @@ QString UpdateDialog::releaseNotes() const { return _releaseNotes; } +void UpdateDialog::closeUpdateDialog() { + qDebug() << "[LEOTEST] Closing update dialog"; + hide(); +} + void UpdateDialog::hide() { ((QQuickItem*)parent())->setEnabled(false); } -void UpdateDialog::triggerBuildDownload(const int &buildNumber) { - qDebug() << "[LEOTEST] Triggering download of build number: " << QString::number(buildNumber); +void UpdateDialog::triggerUpgrade() { + qDebug() << "[LEOTEST] Triggering download of build number"; } \ No newline at end of file diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h index 6ba8f0c46b..eda7f8ceb2 100644 --- a/interface/src/ui/UpdateDialog.h +++ b/interface/src/ui/UpdateDialog.h @@ -27,15 +27,14 @@ public: QString updateAvailableDetails() const; QString releaseNotes() const; -protected: - void hide(); - private: QString _updateAvailableDetails; QString _releaseNotes; protected: - Q_INVOKABLE void triggerBuildDownload(const int& buildNumber); + void hide(); + Q_INVOKABLE void triggerUpgrade(); + Q_INVOKABLE void closeUpdateDialog(); }; From 7aee293df4e053c2bd21f0e82280750be186521a Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Mon, 8 Jun 2015 14:56:46 -0600 Subject: [PATCH 08/94] Buttons are connected and execting old functions --- interface/resources/qml/UpdateDialog.qml | 60 +++++------------------- interface/src/ui/UpdateDialog.cpp | 11 ++--- interface/src/ui/UpdateDialog.h | 3 +- libraries/auto-update/src/AutoUpdate.cpp | 22 +++------ libraries/auto-update/src/AutoUpdate.h | 5 +- 5 files changed, 24 insertions(+), 77 deletions(-) diff --git a/interface/resources/qml/UpdateDialog.qml b/interface/resources/qml/UpdateDialog.qml index 10b0839cca..e5216ff619 100644 --- a/interface/resources/qml/UpdateDialog.qml +++ b/interface/resources/qml/UpdateDialog.qml @@ -110,7 +110,6 @@ DialogContainer { leftMargin: updateDialog.borderWidth } } - } Rectangle { @@ -125,38 +124,19 @@ DialogContainer { right: backgroundRectangle.right rightMargin: 15 } - Accessible.name: "Upgrade" - Accessible.description: "Download and update to latest version" - Accessible.role: Accessible.Button - Accessible.onPressAction: { - updateDialog.triggerBuildDownload() - } - - Text { text: "Upgrade" anchors { verticalCenter: parent.verticalCenter horizontalCenter: parent.horizontalCenter } - } - } - - DropShadow { - anchors.fill: downloadButton - horizontalOffset: 2 - verticalOffset: 2 - radius: updateDialog.buttonRadius - samples: 16 - color: "#80000000" - source: downloadButton - } - - MouseArea { - id: downloadButtonAction - anchors.fill: downloadButton - onClicked: updateDialog.triggerBuildDownload() + MouseArea { + id: downloadButtonAction + anchors.fill: parent + onClicked: updateDialog.triggerUpgrade() + cursorShape: "PointingHandCursor" + } } Rectangle { @@ -171,12 +151,6 @@ DialogContainer { right: downloadButton.left rightMargin: 15 } - Accessible.name: "Cancel" - Accessible.description: "Do not upgrade your current version" - Accessible.role: Accessible.Button - Accessible.onPressAction: { - updateDialog.closeUpdateDialog() - } Text { text: "Cancel" @@ -185,22 +159,12 @@ DialogContainer { horizontalCenter: parent.horizontalCenter } } - } - - DropShadow { - anchors.fill: cancelButton - horizontalOffset: 2 - verticalOffset: 2 - radius: updateDialog.buttonRadius - samples: 16 - color: "#80000000" - source: cancelButton - } - - MouseArea { - id: cancelButtonAction - anchors.fill: cancelButton - onClicked: updateDialog.closeUpdateDialog() + MouseArea { + id: cancelButtonAction + anchors.fill: parent + onClicked: updateDialog.closeDialog() + cursorShape: "PointingHandCursor" + } } } } diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index 5cf5a4fbed..9a6da9148e 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -13,7 +13,6 @@ HIFI_QML_DEF(UpdateDialog) UpdateDialog::UpdateDialog(QQuickItem* parent) : OffscreenQmlDialog(parent) { - qDebug() << "[LEOTEST] We are creating the dialog"; auto applicationUpdater = DependencyManager::get(); int currentVersion = QCoreApplication::applicationVersion().toInt(); int latestVersion = applicationUpdater.data()->getBuildData().lastKey(); @@ -23,10 +22,6 @@ UpdateDialog::UpdateDialog(QQuickItem* parent) : OffscreenQmlDialog(parent) { _releaseNotes = applicationUpdater.data()->getBuildData()[latestVersion]["releaseNotes"]; } -UpdateDialog::~UpdateDialog() { - -} - QString UpdateDialog::updateAvailableDetails() const { return _updateAvailableDetails; } @@ -35,8 +30,7 @@ QString UpdateDialog::releaseNotes() const { return _releaseNotes; } -void UpdateDialog::closeUpdateDialog() { - qDebug() << "[LEOTEST] Closing update dialog"; +void UpdateDialog::closeDialog() { hide(); } @@ -45,5 +39,6 @@ void UpdateDialog::hide() { } void UpdateDialog::triggerUpgrade() { - qDebug() << "[LEOTEST] Triggering download of build number"; + auto applicationUpdater = DependencyManager::get(); + applicationUpdater.data()->performAutoUpdate(applicationUpdater.data()->getBuildData().lastKey()); } \ No newline at end of file diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h index eda7f8ceb2..25ea42e7bc 100644 --- a/interface/src/ui/UpdateDialog.h +++ b/interface/src/ui/UpdateDialog.h @@ -23,7 +23,6 @@ class UpdateDialog : public OffscreenQmlDialog { public: UpdateDialog(QQuickItem* parent = nullptr); - ~UpdateDialog(); QString updateAvailableDetails() const; QString releaseNotes() const; @@ -34,7 +33,7 @@ private: protected: void hide(); Q_INVOKABLE void triggerUpgrade(); - Q_INVOKABLE void closeUpdateDialog(); + Q_INVOKABLE void closeDialog(); }; diff --git a/libraries/auto-update/src/AutoUpdate.cpp b/libraries/auto-update/src/AutoUpdate.cpp index be52b283c5..3295016f81 100644 --- a/libraries/auto-update/src/AutoUpdate.cpp +++ b/libraries/auto-update/src/AutoUpdate.cpp @@ -29,10 +29,6 @@ AutoUpdate::AutoUpdate() { _builds = new QMap>; } -AutoUpdate::~AutoUpdate() { - qDebug() << "[LEOTEST] The object is now destroyed"; -} - void AutoUpdate::checkForUpdate() { this->getLatestVersionData(); @@ -113,18 +109,7 @@ void AutoUpdate::parseLatestVersionData() { emit latestVersionDataParsed(); } -void AutoUpdate::debugBuildData() { - qDebug() << "[LEOTEST] We finished parsing the xml build data"; - foreach (int key, _builds->keys()) { - qDebug() << "[LEOTEST] Build number: " << QString::number(key); - //foreach (QString detailsKey, _builds[key].keys()) { - // qDebug() << "[LEOTEST] Key: " << detailsKey << " Value: " << _builds[key][detailsKey]; - //} - } -} - void AutoUpdate::checkVersionAndNotify() { - qDebug() << "[LEOTEST] We are checking and notifying for updates"; int latestVersionAvailable = _builds->lastKey(); if (QCoreApplication::applicationVersion() != "dev" && QCoreApplication::applicationVersion().toInt() < latestVersionAvailable) { @@ -133,7 +118,12 @@ void AutoUpdate::checkVersionAndNotify() { } void AutoUpdate::performAutoUpdate(int version) { - + // NOTE: This is not yet auto updating - however this is a checkpoint towards that end + // Next PR will handle the automatic download, upgrading and application restart + QMap chosenVersion = _builds->value(version); + QUrl downloadUrl = chosenVersion.value("downloadUrl"); + QDesktopServices::openUrl(downloadUrl); + QCoreApplication::quit(); } void AutoUpdate::downloadUpdateVersion(int version) { diff --git a/libraries/auto-update/src/AutoUpdate.h b/libraries/auto-update/src/AutoUpdate.h index 4b32d68c48..b060aca2b6 100644 --- a/libraries/auto-update/src/AutoUpdate.h +++ b/libraries/auto-update/src/AutoUpdate.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -40,10 +41,10 @@ class AutoUpdate : public QObject, public Dependency { public: // Methods AutoUpdate(); - ~AutoUpdate(); void checkForUpdate(); QMap> &getBuildData() { return *_builds; } + void performAutoUpdate(int version); public slots: @@ -63,9 +64,7 @@ private: private slots: void parseLatestVersionData(); - void debugBuildData(); void checkVersionAndNotify(); - void performAutoUpdate(int version); signals: void latestVersionDataParsed(); From 873f73ffb4929c5fa44a7574e9777d0c629e9abf Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 9 Jun 2015 00:39:49 -0700 Subject: [PATCH 09/94] Working on cursor manager --- libraries/ui/src/CursorManager.cpp | 32 ++++++++++++++++++++++++++++++ libraries/ui/src/CursorManager.h | 32 ++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 libraries/ui/src/CursorManager.cpp create mode 100644 libraries/ui/src/CursorManager.h diff --git a/libraries/ui/src/CursorManager.cpp b/libraries/ui/src/CursorManager.cpp new file mode 100644 index 0000000000..8b318d549a --- /dev/null +++ b/libraries/ui/src/CursorManager.cpp @@ -0,0 +1,32 @@ +// +// Created by Bradley Austin Davis on 2015/06/08 +// 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 "CursorManager.h" + +namespace Cursor { + enum class Source { + MOUSE, + LEFT_HAND, + RIGHT_HAND, + UNKNOWN, + }; + + class Instance { + Source type; + }; + + class Manager { + public: + static Manager& instance(); + + uint8_t getCount(); + Instance + }; +} + + diff --git a/libraries/ui/src/CursorManager.h b/libraries/ui/src/CursorManager.h new file mode 100644 index 0000000000..a2d26efc58 --- /dev/null +++ b/libraries/ui/src/CursorManager.h @@ -0,0 +1,32 @@ +// +// Created by Bradley Austin Davis on 2015/06/08 +// 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 +// + +#pragma once + +namespace Cursor { + enum class Source { + MOUSE, + LEFT_HAND, + RIGHT_HAND, + UNKNOWN, + }; + + class Instance { + Source type; + }; + + class Manager { + public: + static Manager& instance(); + + uint8_t getCount(); + Instance + }; +} + + From 111284158643fd79bd9b85f5b38a4d9ce98772e1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 9 Jun 2015 19:01:03 -0700 Subject: [PATCH 10/94] fix indentation in AddressManager --- libraries/networking/src/AddressManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 642ca4748d..51575bfde5 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -218,7 +218,7 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const const QString DOMAIN_NETWORK_PORT_KEY = "network_port"; const QString DOMAIN_ICE_SERVER_ADDRESS_KEY = "ice_server_address"; - DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress); + DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress); const QString DOMAIN_ID_KEY = "id"; QString domainIDString = domainObject[DOMAIN_ID_KEY].toString(); From 94c414e4e86ed32a5ebc68f673d3d48c41b9d117 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 10 Jun 2015 08:12:58 -0700 Subject: [PATCH 11/94] Add unit quad, commonly used in compositing the overlays --- libraries/render-utils/src/GeometryCache.cpp | 15 +++++++++++++++ libraries/render-utils/src/GeometryCache.h | 3 +++ 2 files changed, 18 insertions(+) diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 303d63bef8..3066fd4890 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1179,6 +1179,21 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co batch.draw(gpu::QUADS, 4, 0); } +void GeometryCache::renderUnitQuad(const glm::vec4& color, int id) { + gpu::Batch batch; + renderUnitQuad(batch, color, id); + gpu::GLBackend::renderBatch(batch); +} + +void GeometryCache::renderUnitQuad(gpu::Batch& batch, const glm::vec4& color, int id) { + static const glm::vec2 topLeft(-1, 1); + static const glm::vec2 bottomRight(1, -1); + static const glm::vec2 texCoordTopLeft(0.0f, 1.0f); + static const glm::vec2 texCoordBottomRight(1.0f, 0.0f); + renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color, id); +} + + void GeometryCache::renderQuad(const glm::vec2& minCorner, const glm::vec2& maxCorner, const glm::vec2& texCoordMinCorner, const glm::vec2& texCoordMaxCorner, const glm::vec4& color, int id) { diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index b438eb2d3b..76e03f8669 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -155,6 +155,9 @@ public: void renderBevelCornersRect(int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id = UNKNOWN_ID); void renderBevelCornersRect(gpu::Batch& batch, int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id = UNKNOWN_ID); + void renderUnitQuad(const glm::vec4& color = glm::vec4(1), int id = UNKNOWN_ID); + void renderUnitQuad(gpu::Batch& batch, const glm::vec4& color = glm::vec4(1), int id = UNKNOWN_ID); + void renderQuad(int x, int y, int width, int height, const glm::vec4& color, int id = UNKNOWN_ID) { renderQuad(glm::vec2(x,y), glm::vec2(x + width, y + height), color, id); } void renderQuad(gpu::Batch& batch, int x, int y, int width, int height, const glm::vec4& color, int id = UNKNOWN_ID) From 7374fb84e896a046361aee080f23459e25bd9921 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 10 Jun 2015 09:36:14 -0700 Subject: [PATCH 12/94] Working on fixing overlays with team-teaching merge --- interface/src/Application.cpp | 2 +- interface/src/devices/OculusManager.cpp | 29 ++- interface/src/devices/TV3DManager.cpp | 2 +- interface/src/ui/ApplicationOverlay.cpp | 313 ++++++++++++------------ interface/src/ui/ApplicationOverlay.h | 21 +- 5 files changed, 198 insertions(+), 169 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1763623fa6..9d5c43b7cf 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -958,7 +958,7 @@ void Application::paintGL() { GL_COLOR_BUFFER_BIT, GL_NEAREST); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - _applicationOverlay.displayOverlayTexture(); + _applicationOverlay.displayOverlayTexture(&renderArgs); } if (!OculusManager::isConnected() || OculusManager::allowSwap()) { diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index a7383ae4bb..77263935f6 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -615,12 +615,9 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const renderArgs->_renderSide = RenderArgs::MONO; qApp->displaySide(renderArgs, *_camera, false); - qApp->getApplicationOverlay().displayOverlayTextureHmd(*_camera); }); _activeEye = ovrEye_Count; - glPopMatrix(); - gpu::FramebufferPointer finalFbo; //Bind the output texture from the glow shader. If glow effect is disabled, we just grab the texture if (Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect)) { @@ -632,9 +629,35 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const glBindFramebuffer(GL_FRAMEBUFFER, 0); } + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(finalFbo)); + //Render each eye into an fbo + for_each_eye(_ovrHmd, [&](ovrEyeType eye) { + _activeEye = eye; + // Update our camera to what the application camera is doing + _camera->setRotation(toGlm(eyeRenderPose[eye].Orientation)); + _camera->setPosition(toGlm(eyeRenderPose[eye].Position)); + configureCamera(*_camera); + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(glm::value_ptr(_camera->getProjection())); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + ovrRecti & vp = _eyeTextures[eye].Header.RenderViewport; + vp.Size.h = _recommendedTexSize.h * _offscreenRenderScale; + vp.Size.w = _recommendedTexSize.w * _offscreenRenderScale; + + glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h); + qApp->getApplicationOverlay().displayOverlayTextureHmd(renderArgs, *_camera); + }); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + + glPopMatrix(); + glMatrixMode(GL_PROJECTION); glPopMatrix(); + // restore our normal viewport glViewport(0, 0, deviceSize.width(), deviceSize.height()); diff --git a/interface/src/devices/TV3DManager.cpp b/interface/src/devices/TV3DManager.cpp index 09edb03e5a..c14e589389 100644 --- a/interface/src/devices/TV3DManager.cpp +++ b/interface/src/devices/TV3DManager.cpp @@ -120,7 +120,7 @@ void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) { glLoadIdentity(); renderArgs->_renderSide = RenderArgs::MONO; qApp->displaySide(renderArgs, eyeCamera, false); - qApp->getApplicationOverlay().displayOverlayTextureStereo(whichCamera, _aspect, fov); + qApp->getApplicationOverlay().displayOverlayTextureStereo(renderArgs, whichCamera, _aspect, fov); _activeEye = NULL; }, [&]{ // render right side view diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index bc8047fc98..79ec39b506 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -14,13 +14,16 @@ #include #include +#include + #include +#include #include -#include #include #include -#include #include +#include +#include #include "AudioClient.h" #include "audio/AudioIOStatsRenderer.h" @@ -149,7 +152,8 @@ ApplicationOverlay::ApplicationOverlay() : _previousMagnifierBottomLeft(), _previousMagnifierBottomRight(), _previousMagnifierTopLeft(), - _previousMagnifierTopRight() + _previousMagnifierTopRight(), + _framebufferObject(nullptr) { memset(_reticleActive, 0, sizeof(_reticleActive)); memset(_magActive, 0, sizeof(_reticleActive)); @@ -196,16 +200,17 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { //Handle fading and deactivation/activation of UI // Render 2D overlay - glMatrixMode(GL_PROJECTION); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - _overlays.buildFramebufferObject(); - _overlays.bind(); + buildFramebufferObject(); + + _framebufferObject->bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, size.x, size.y); + glMatrixMode(GL_PROJECTION); glPushMatrix(); { const float NEAR_CLIP = -10000; const float FAR_CLIP = 10000; @@ -227,6 +232,22 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { renderPointers(); renderDomainConnectionStatusBorder(); + if (_newUiTexture) { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBindTexture(GL_TEXTURE_2D, _newUiTexture); + DependencyManager::get()->renderUnitQuad(); + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + } + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); } glPopMatrix(); @@ -236,166 +257,159 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { glEnable(GL_LIGHTING); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - _overlays.release(); + _framebufferObject->release(); } // A quick and dirty solution for compositing the old overlay // texture with the new one -template -void with_each_texture(GLuint firstPassTexture, GLuint secondPassTexture, F f) { - glEnable(GL_TEXTURE_2D); - glActiveTexture(GL_TEXTURE0); - if (firstPassTexture) { - glBindTexture(GL_TEXTURE_2D, firstPassTexture); - f(); - } - if (secondPassTexture) { - glBindTexture(GL_TEXTURE_2D, secondPassTexture); - f(); - } - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); -} +//template +//void with_each_texture(GLuint firstPassTexture, GLuint secondPassTexture, F f) { +// glEnable(GL_TEXTURE_2D); +// glActiveTexture(GL_TEXTURE0); +// if (firstPassTexture) { +// glBindTexture(GL_TEXTURE_2D, firstPassTexture); +// f(); +// } +// //if (secondPassTexture) { +// // glBindTexture(GL_TEXTURE_2D, secondPassTexture); +// // f(); +// //} +// glBindTexture(GL_TEXTURE_2D, 0); +// glDisable(GL_TEXTURE_2D); +//} // Draws the FBO texture for the screen -void ApplicationOverlay::displayOverlayTexture() { +void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) { + if (_alpha == 0.0f) { return; } + + if (!_crosshairTexture) { + _crosshairTexture = TextureCache::getImageTexture( + PathUtils::resourcesPath() + "images/sixense-reticle.png"); + } + + /* + FIXME - doesn't work + renderArgs->_context->syncCache(); + gpu::Batch batch; + DependencyManager::get()->bindSimpleProgram(batch, true); + batch.setModelTransform(Transform()); + batch.setProjectionTransform(mat4()); + batch.setViewTransform(Transform()); + batch.setUniformTexture(0, _crosshairTexture); + DependencyManager::get()->renderUnitQuad(batch, vec4(vec3(1), _alpha)); + renderArgs->_context->render(batch); + return; + */ + + + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_TEXTURE_2D); + glViewport(0, 0, qApp->getDeviceSize().width(), qApp->getDeviceSize().height()); + glMatrixMode(GL_PROJECTION); - glPushMatrix(); { - glLoadIdentity(); - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glViewport(0, 0, qApp->getDeviceSize().width(), qApp->getDeviceSize().height()); - - static const glm::vec2 topLeft(-1, 1); - static const glm::vec2 bottomRight(1, -1); - static const glm::vec2 texCoordTopLeft(0.0f, 1.0f); - static const glm::vec2 texCoordBottomRight(1.0f, 0.0f); - with_each_texture(_overlays.getTexture(), _newUiTexture, [&] { - DependencyManager::get()->renderQuad( - topLeft, bottomRight, - texCoordTopLeft, texCoordBottomRight, - glm::vec4(1.0f, 1.0f, 1.0f, _alpha)); - }); - - if (!_crosshairTexture) { - _crosshairTexture = DependencyManager::get()-> - getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png"); - } - + glPushMatrix(); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + { + glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture()); + DependencyManager::get()->renderUnitQuad(vec4(vec3(1), _alpha)); //draw the mouse pointer glm::vec2 canvasSize = qApp->getCanvasSize(); - glm::vec2 mouseSize = 32.0f / canvasSize; - auto mouseTopLeft = topLeft * mouseSize; - auto mouseBottomRight = bottomRight * mouseSize; + + // Get the mouse coordinates and convert to NDC [-1, 1] vec2 mousePosition = vec2(qApp->getMouseX(), qApp->getMouseY()); mousePosition /= canvasSize; mousePosition *= 2.0f; mousePosition -= 1.0f; mousePosition.y *= -1.0f; + mat4 mouseMv = glm::translate(mat4(), vec3(mousePosition, 0)); - glEnable(GL_TEXTURE_2D); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + // Scale the mouse based on the canvasSize (NOT the device size, + // we don't want a smaller mouse on retina displays) + glm::vec2 mouseSize = 32.0f / canvasSize; + mouseMv = glm::scale(mouseMv, vec3(mouseSize, 1.0f)); + + // Push the resulting matrix into modelview + glLoadMatrixf(glm::value_ptr(mouseMv)); glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; - DependencyManager::get()->renderQuad( - mouseTopLeft + mousePosition, mouseBottomRight + mousePosition, - texCoordTopLeft, texCoordBottomRight, - reticleColor); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - glDisable(GL_TEXTURE_2D); - } glPopMatrix(); + DependencyManager::get()->renderUnitQuad(reticleColor); + } + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); } // Draws the FBO texture for Oculus rift. -void ApplicationOverlay::displayOverlayTextureHmd(Camera& whichCamera) { +void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera& whichCamera) { if (_alpha == 0.0f) { return; } - glEnable(GL_BLEND); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); + _overlays.buildVBO(_textureFov, _textureAspectRatio, 80, 80); + glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.01f); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + // The camera here contains only the head pose relative to the avatar position + vec3 pos = whichCamera.getPosition(); + quat rot = whichCamera.getOrientation(); + mat4 overlayXfm = glm::translate(glm::mat4(), pos) * glm::mat4_cast(rot); + glLoadMatrixf(glm::value_ptr(glm::inverse(overlayXfm))); + glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture()); + _overlays.render(); //Update and draw the magnifiers + /* + // FIXME Mangifiers need to be re-thought MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - const glm::quat& orientation = myAvatar->getOrientation(); - // Always display the HMD overlay relative to the camera position but - // remove the HMD pose offset. This results in an overlay that sticks with you - // even in third person mode, but isn't drawn at a fixed distance. - glm::vec3 position = whichCamera.getPosition(); - position -= qApp->getCamera()->getHmdPosition(); const float scale = myAvatar->getScale() * _oculusUIRadius; - -// glm::vec3 eyeOffset = setEyeOffsetPosition; - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); { - glTranslatef(position.x, position.y, position.z); - glm::mat4 rotation = glm::toMat4(orientation); - glMultMatrixf(&rotation[0][0]); - glScalef(scale, scale, scale); - for (int i = 0; i < NUMBER_OF_RETICLES; i++) { - - if (_magActive[i]) { - _magSizeMult[i] += MAG_SPEED; - if (_magSizeMult[i] > 1.0f) { - _magSizeMult[i] = 1.0f; - } - } else { - _magSizeMult[i] -= MAG_SPEED; - if (_magSizeMult[i] < 0.0f) { - _magSizeMult[i] = 0.0f; - } + overlayXfm = glm::scale(overlayXfm, vec3(scale)); + for (int i = 0; i < NUMBER_OF_RETICLES; i++) { + if (_magActive[i]) { + _magSizeMult[i] += MAG_SPEED; + if (_magSizeMult[i] > 1.0f) { + _magSizeMult[i] = 1.0f; } - - if (_magSizeMult[i] > 0.0f) { - //Render magnifier, but dont show border for mouse magnifier - glm::vec2 projection = screenToOverlay(glm::vec2(_reticlePosition[MOUSE].x(), - _reticlePosition[MOUSE].y())); - with_each_texture(_overlays.getTexture(), 0, [&] { - renderMagnifier(projection, _magSizeMult[i], i != MOUSE); - }); + } else { + _magSizeMult[i] -= MAG_SPEED; + if (_magSizeMult[i] < 0.0f) { + _magSizeMult[i] = 0.0f; } } - - glDepthMask(GL_FALSE); - glDisable(GL_ALPHA_TEST); - - static float textureFOV = 0.0f, textureAspectRatio = 1.0f; - if (textureFOV != _textureFov || - textureAspectRatio != _textureAspectRatio) { - textureFOV = _textureFov; - textureAspectRatio = _textureAspectRatio; - _overlays.buildVBO(_textureFov, _textureAspectRatio, 80, 80); + if (_magSizeMult[i] > 0.0f) { + //Render magnifier, but dont show border for mouse magnifier + glm::vec2 projection = screenToOverlay(glm::vec2(_reticlePosition[MOUSE].x(), + _reticlePosition[MOUSE].y())); + with_each_texture(_overlays.getTexture(), 0, [&] { + renderMagnifier(projection, _magSizeMult[i], i != MOUSE); + }); } + } + */ - with_each_texture(_overlays.getTexture(), _newUiTexture, [&] { - _overlays.render(); - }); - - if (!Application::getInstance()->isMouseHidden()) { - renderPointersOculus(); - } - glDepthMask(GL_TRUE); - glDisable(GL_TEXTURE_2D); - - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - glEnable(GL_LIGHTING); - } glPopMatrix(); + if (!Application::getInstance()->isMouseHidden()) { + renderPointersOculus(); + } } // Draws the FBO texture for 3DTV. -void ApplicationOverlay::displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov) { +void ApplicationOverlay::displayOverlayTextureStereo(RenderArgs* renderArgs, Camera& whichCamera, float aspectRatio, float fov) { if (_alpha == 0.0f) { return; } @@ -440,15 +454,15 @@ void ApplicationOverlay::displayOverlayTextureStereo(Camera& whichCamera, float GLfloat y = -halfQuadHeight; glDisable(GL_DEPTH_TEST); - with_each_texture(_overlays.getTexture(), _newUiTexture, [&] { - DependencyManager::get()->renderQuad(glm::vec3(x, y + quadHeight, -distance), - glm::vec3(x + quadWidth, y + quadHeight, -distance), - glm::vec3(x + quadWidth, y, -distance), - glm::vec3(x, y, -distance), - glm::vec2(0.0f, 1.0f), glm::vec2(1.0f, 1.0f), - glm::vec2(1.0f, 0.0f), glm::vec2(0.0f, 0.0f), - overlayColor); - }); + //with_each_texture(_framebufferObject->texture(), _newUiTexture, [&] { + // DependencyManager::get()->renderQuad(glm::vec3(x, y + quadHeight, -distance), + // glm::vec3(x + quadWidth, y + quadHeight, -distance), + // glm::vec3(x + quadWidth, y, -distance), + // glm::vec3(x, y, -distance), + // glm::vec2(0.0f, 1.0f), glm::vec2(1.0f, 1.0f), + // glm::vec2(1.0f, 0.0f), glm::vec2(0.0f, 0.0f), + // overlayColor); + //}); if (!_crosshairTexture) { _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + @@ -1071,29 +1085,24 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder() { ApplicationOverlay::TexturedHemisphere::TexturedHemisphere() : _vertices(0), _indices(0), - _framebufferObject(NULL), _vbo(0, 0) { } ApplicationOverlay::TexturedHemisphere::~TexturedHemisphere() { cleanupVBO(); - if (_framebufferObject != NULL) { - delete _framebufferObject; - } -} - -void ApplicationOverlay::TexturedHemisphere::bind() { - _framebufferObject->bind(); -} - -void ApplicationOverlay::TexturedHemisphere::release() { - _framebufferObject->release(); } void ApplicationOverlay::TexturedHemisphere::buildVBO(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; + if (fov >= PI) { qDebug() << "TexturedHemisphere::buildVBO(): FOV greater or equal than Pi will create issues"; } @@ -1176,7 +1185,11 @@ void ApplicationOverlay::TexturedHemisphere::cleanupVBO() { } } -void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() { +GLuint ApplicationOverlay::getOverlayTexture() { + return _framebufferObject->texture(); +} + +void ApplicationOverlay::buildFramebufferObject() { auto canvasSize = qApp->getCanvasSize(); QSize fboSize = QSize(canvasSize.x, canvasSize.y); if (_framebufferObject != NULL && fboSize == _framebufferObject->size()) { @@ -1189,7 +1202,7 @@ void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() { } _framebufferObject = new QOpenGLFramebufferObject(fboSize, QOpenGLFramebufferObject::Depth); - glBindTexture(GL_TEXTURE_2D, getTexture()); + glBindTexture(GL_TEXTURE_2D, getOverlayTexture()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); @@ -1201,7 +1214,7 @@ void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() { //Renders a hemisphere with texture coordinates. void ApplicationOverlay::TexturedHemisphere::render() { - if (_framebufferObject == NULL || _vbo.first == 0 || _vbo.second == 0) { + if (_vbo.first == 0 || _vbo.second == 0) { qDebug() << "TexturedHemisphere::render(): Incorrect initialisation"; return; } @@ -1227,10 +1240,6 @@ void ApplicationOverlay::TexturedHemisphere::render() { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } -GLuint ApplicationOverlay::TexturedHemisphere::getTexture() { - return _framebufferObject->texture(); -} - glm::vec2 ApplicationOverlay::directionToSpherical(const glm::vec3& direction) { glm::vec2 result; // Compute yaw diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index ee82c9274a..36161dd29d 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -33,9 +33,9 @@ public: ~ApplicationOverlay(); void renderOverlay(RenderArgs* renderArgs); - void displayOverlayTexture(); - void displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov); - void displayOverlayTextureHmd(Camera& whichCamera); + void displayOverlayTexture(RenderArgs* renderArgs); + void displayOverlayTextureStereo(RenderArgs* renderArgs, Camera& whichCamera, float aspectRatio, float fov); + void displayOverlayTextureHmd(RenderArgs* renderArgs, Camera& whichCamera); QPoint getPalmClickLocation(const PalmData *palm) const; bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const; @@ -59,6 +59,7 @@ public: glm::vec2 screenToOverlay(const glm::vec2 & screenPos) const; glm::vec2 overlayToScreen(const glm::vec2 & overlayPos) const; void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const; + GLuint getOverlayTexture(); static glm::vec2 directionToSpherical(const glm::vec3 & direction); static glm::vec3 sphericalToDirection(const glm::vec2 & sphericalPos); @@ -77,12 +78,6 @@ private: public: TexturedHemisphere(); ~TexturedHemisphere(); - - void bind(); - void release(); - GLuint getTexture(); - - void buildFramebufferObject(); void buildVBO(const float fov, const float aspectRatio, const int slices, const int stacks); void render(); @@ -91,14 +86,14 @@ private: GLuint _vertices; GLuint _indices; - QOpenGLFramebufferObject* _framebufferObject; VerticesIndices _vbo; }; float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE; - + QOpenGLFramebufferObject* _framebufferObject; + void renderReticle(glm::quat orientation, float alpha); - void renderPointers();; + void renderPointers(); void renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder); void renderControllerPointers(); @@ -109,6 +104,8 @@ private: void renderStatsAndLogs(); void renderDomainConnectionStatusBorder(); + void buildFramebufferObject(); + TexturedHemisphere _overlays; float _textureFov; From 869461a01bfde937e7d6a11847aed5f8c6a36a4d Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Wed, 10 Jun 2015 14:14:00 -0600 Subject: [PATCH 13/94] Code Review changes --- interface/src/Application.cpp | 3 +- interface/src/ui/UpdateDialog.cpp | 17 +++++-- interface/src/ui/UpdateDialog.h | 17 ++++--- libraries/auto-update/src/AutoUpdate.cpp | 38 ++++++--------- libraries/auto-update/src/AutoUpdate.h | 62 +++++++++++------------- 5 files changed, 66 insertions(+), 71 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 64ad22d25f..88a8a274a3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -625,11 +625,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : #endif auto applicationUpdater = DependencyManager::get(); - connect(applicationUpdater.data(), SIGNAL(newVersionIsAvailable()), dialogsManager.data(), SLOT(showUpdateDialog())); + connect(applicationUpdater.data(), &AutoUpdate::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog); applicationUpdater->checkForUpdate(); } - void Application::aboutToQuit() { emit beforeAboutToQuit(); diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index 9a6da9148e..61a7cb892c 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -1,18 +1,25 @@ // // UpdateDialog.cpp -// hifi +// interface/src/ui // // Created by Leonardo Murillo on 6/3/15. +// 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 "UpdateDialog.h" -#include "DependencyManager.h" + #include +#include "DependencyManager.h" + HIFI_QML_DEF(UpdateDialog) -UpdateDialog::UpdateDialog(QQuickItem* parent) : OffscreenQmlDialog(parent) { +UpdateDialog::UpdateDialog(QQuickItem* parent) : + OffscreenQmlDialog(parent) +{ auto applicationUpdater = DependencyManager::get(); int currentVersion = QCoreApplication::applicationVersion().toInt(); int latestVersion = applicationUpdater.data()->getBuildData().lastKey(); @@ -22,11 +29,11 @@ UpdateDialog::UpdateDialog(QQuickItem* parent) : OffscreenQmlDialog(parent) { _releaseNotes = applicationUpdater.data()->getBuildData()[latestVersion]["releaseNotes"]; } -QString UpdateDialog::updateAvailableDetails() const { +const QString& UpdateDialog::updateAvailableDetails() const { return _updateAvailableDetails; } -QString UpdateDialog::releaseNotes() const { +const QString& UpdateDialog::releaseNotes() const { return _releaseNotes; } diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h index 25ea42e7bc..84d390c942 100644 --- a/interface/src/ui/UpdateDialog.h +++ b/interface/src/ui/UpdateDialog.h @@ -1,17 +1,20 @@ // // UpdateDialog.h -// hifi +// interface/src/ui // // Created by Leonardo Murillo on 6/3/15. +// 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 // #pragma once -#ifndef __hifi__UpdateDialog__ -#define __hifi__UpdateDialog__ - +#ifndef hifi_UpdateDialog_h +#define hifi_UpdateDialog_h #include + #include class UpdateDialog : public OffscreenQmlDialog { @@ -23,8 +26,8 @@ class UpdateDialog : public OffscreenQmlDialog { public: UpdateDialog(QQuickItem* parent = nullptr); - QString updateAvailableDetails() const; - QString releaseNotes() const; + const QString& updateAvailableDetails() const; + const QString& releaseNotes() const; private: QString _updateAvailableDetails; @@ -37,4 +40,4 @@ protected: }; -#endif /* defined(__hifi__UpdateDialog__) */ +#endif // hifi_UpdateDialog_h diff --git a/libraries/auto-update/src/AutoUpdate.cpp b/libraries/auto-update/src/AutoUpdate.cpp index 3295016f81..04bb294fec 100644 --- a/libraries/auto-update/src/AutoUpdate.cpp +++ b/libraries/auto-update/src/AutoUpdate.cpp @@ -5,33 +5,29 @@ // Created by Leonardo Murillo on 6/1/2015. // 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 -#include #include "AutoUpdate.h" +#include +#include + AutoUpdate::AutoUpdate() { -#ifdef Q_OS_WIN32 +#if defined Q_OS_WIN32 _operatingSystem = "windows"; -#endif -#ifdef Q_OS_MAC +#elif defined Q_OS_MAC _operatingSystem = "mac"; -#endif -#ifdef Q_OS_LINUX +#elif defined Q_OS_LINUX _operatingSystem = "ubuntu"; #endif + connect(this, SIGNAL(latestVersionDataParsed()), this, SLOT(checkVersionAndNotify())); - _builds = new QMap>; } void AutoUpdate::checkForUpdate() { this->getLatestVersionData(); - } void AutoUpdate::getLatestVersionData() { @@ -39,10 +35,9 @@ void AutoUpdate::getLatestVersionData() { QNetworkRequest latestVersionRequest(BUILDS_XML_URL); latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); QNetworkReply* reply = networkAccessManager.get(latestVersionRequest); - connect(reply, SIGNAL(finished()), this, SLOT(parseLatestVersionData())); + connect(reply, &QNetworkReply::finished, this, &AutoUpdate::parseLatestVersionData); } - void AutoUpdate::parseLatestVersionData() { QNetworkReply* sender = qobject_cast(QObject::sender()); @@ -55,7 +50,6 @@ void AutoUpdate::parseLatestVersionData() { QString commitSha; QString pullRequestNumber; - while (!xml.atEnd() && !xml.hasError()) { if (xml.name().toString() == "project" && xml.attributes().hasAttribute("name") && @@ -110,7 +104,7 @@ void AutoUpdate::parseLatestVersionData() { } void AutoUpdate::checkVersionAndNotify() { - int latestVersionAvailable = _builds->lastKey(); + int latestVersionAvailable = _builds.lastKey(); if (QCoreApplication::applicationVersion() != "dev" && QCoreApplication::applicationVersion().toInt() < latestVersionAvailable) { emit newVersionIsAvailable(); @@ -120,8 +114,8 @@ void AutoUpdate::checkVersionAndNotify() { void AutoUpdate::performAutoUpdate(int version) { // NOTE: This is not yet auto updating - however this is a checkpoint towards that end // Next PR will handle the automatic download, upgrading and application restart - QMap chosenVersion = _builds->value(version); - QUrl downloadUrl = chosenVersion.value("downloadUrl"); + const QMap& chosenVersion = _builds.value(version); + const QUrl& downloadUrl = chosenVersion.value("downloadUrl"); QDesktopServices::openUrl(downloadUrl); QCoreApplication::quit(); } @@ -131,16 +125,16 @@ void AutoUpdate::downloadUpdateVersion(int version) { } void AutoUpdate::appendBuildData(int versionNumber, - QString downloadURL, - QString releaseTime, - QString releaseNotes, - QString pullRequestNumber) { + const QString& downloadURL, + const QString& releaseTime, + const QString& releaseNotes, + const QString& pullRequestNumber) { QMap thisBuildDetails; thisBuildDetails.insert("downloadUrl", downloadURL); thisBuildDetails.insert("releaseTime", releaseTime); thisBuildDetails.insert("releaseNotes", releaseNotes); thisBuildDetails.insert("pullRequestNumber", pullRequestNumber); - _builds->insert(versionNumber, thisBuildDetails); + _builds.insert(versionNumber, thisBuildDetails); } \ No newline at end of file diff --git a/libraries/auto-update/src/AutoUpdate.h b/libraries/auto-update/src/AutoUpdate.h index b060aca2b6..be8e479f63 100644 --- a/libraries/auto-update/src/AutoUpdate.h +++ b/libraries/auto-update/src/AutoUpdate.h @@ -5,33 +5,30 @@ // Created by Leonardo Murillo on 6/1/2015. // 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__AutoUpdate__ -#define __hifi__AutoUpdate__ +#ifndef hifi_AutoUpdate_h +#define hifi_AutoUpdate_h + -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include - const QUrl BUILDS_XML_URL("https://highfidelity.com/builds.xml"); class AutoUpdate : public QObject, public Dependency { @@ -39,38 +36,33 @@ class AutoUpdate : public QObject, public Dependency { SINGLETON_DEPENDENCY public: - // Methods AutoUpdate(); void checkForUpdate(); - QMap> &getBuildData() { return *_builds; } + const QMap> &getBuildData() { return _builds; } void performAutoUpdate(int version); -public slots: - private: - // Members - QMap> *_builds; + QMap> _builds; QString _operatingSystem; - // Methods void getLatestVersionData(); void downloadUpdateVersion(int version); void appendBuildData(int versionNumber, - QString downloadURL, - QString releaseTime, - QString releaseNotes, - QString pullRequestNumber); - -private slots: - void parseLatestVersionData(); - void checkVersionAndNotify(); + const QString& downloadURL, + const QString& releaseTime, + const QString& releaseNotes, + const QString& pullRequestNumber); signals: void latestVersionDataParsed(); void newVersionIsAvailable(); void newVersionIsDownloaded(); - + +private slots: + void parseLatestVersionData(); + void checkVersionAndNotify(); + }; -#endif /* defined(__hifi__AutoUpdate__) */ +#endif // _hifi_AutoUpdate_h From 3708f5ec96841d9b80fd3fab7a36bd48d343c526 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Wed, 10 Jun 2015 14:15:26 -0600 Subject: [PATCH 14/94] Code Review changes --- libraries/auto-update/src/AutoUpdate.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/auto-update/src/AutoUpdate.h b/libraries/auto-update/src/AutoUpdate.h index be8e479f63..71b55095ee 100644 --- a/libraries/auto-update/src/AutoUpdate.h +++ b/libraries/auto-update/src/AutoUpdate.h @@ -41,7 +41,12 @@ public: void checkForUpdate(); const QMap> &getBuildData() { return _builds; } void performAutoUpdate(int version); - + +signals: + void latestVersionDataParsed(); + void newVersionIsAvailable(); + void newVersionIsDownloaded(); + private: QMap> _builds; QString _operatingSystem; @@ -54,11 +59,6 @@ private: const QString& releaseNotes, const QString& pullRequestNumber); -signals: - void latestVersionDataParsed(); - void newVersionIsAvailable(); - void newVersionIsDownloaded(); - private slots: void parseLatestVersionData(); void checkVersionAndNotify(); From ca1af777637bfaa2d3298822ed1da08acb7f23e0 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 10 Jun 2015 13:24:47 -0700 Subject: [PATCH 15/94] Working on cursor manager --- interface/resources/images/reticleLink.png | Bin 0 -> 6381 bytes interface/resources/images/reticleWhite.png | Bin 0 -> 4749 bytes interface/src/Application.cpp | 39 +++++++-- interface/src/ui/ApplicationOverlay.cpp | 42 ++++----- interface/src/ui/ApplicationOverlay.h | 4 +- libraries/ui/src/CursorManager.cpp | 90 ++++++++++++++++---- libraries/ui/src/CursorManager.h | 35 +++++++- 7 files changed, 164 insertions(+), 46 deletions(-) create mode 100644 interface/resources/images/reticleLink.png create mode 100644 interface/resources/images/reticleWhite.png diff --git a/interface/resources/images/reticleLink.png b/interface/resources/images/reticleLink.png new file mode 100644 index 0000000000000000000000000000000000000000..b36ee79d00b261d2c7d422df39302a46eb19daa4 GIT binary patch literal 6381 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 T{C0ae007unI$FFq>7V#N&}Y{u literal 0 HcmV?d00001 diff --git a/interface/resources/images/reticleWhite.png b/interface/resources/images/reticleWhite.png new file mode 100644 index 0000000000000000000000000000000000000000..4c35a186b2e7aa8939937e909586726ef3a10d09 GIT binary patch literal 4749 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 Har^%O!X>IH literal 0 HcmV?d00001 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1763623fa6..5f17fd0510 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -57,6 +57,7 @@ #include #include +#include #include #include #include @@ -1229,9 +1230,20 @@ void Application::keyPressEvent(QKeyEvent* event) { } break; - case Qt::Key_Apostrophe: - resetSensors(); + case Qt::Key_Apostrophe: { + if (isMeta) { + auto cursor = Cursor::Manager::instance().getCursor(); + auto curIcon = cursor->getIcon(); + if (curIcon == Cursor::Icon::DEFAULT) { + cursor->setIcon(Cursor::Icon::LINK); + } else { + cursor->setIcon(Cursor::Icon::DEFAULT); + } + } else { + resetSensors(); + } break; + } case Qt::Key_A: if (isShifted) { @@ -1355,12 +1367,27 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_Slash: Menu::getInstance()->triggerOption(MenuOption::Stats); break; - case Qt::Key_Plus: - _myAvatar->increaseSize(); + + case Qt::Key_Plus: { + if (isMeta && event->modifiers().testFlag(Qt::KeypadModifier)) { + auto& cursorManager = Cursor::Manager::instance(); + cursorManager.setScale(cursorManager.getScale() * 1.1f); + } else { + _myAvatar->increaseSize(); + } break; - case Qt::Key_Minus: - _myAvatar->decreaseSize(); + } + + case Qt::Key_Minus: { + if (isMeta && event->modifiers().testFlag(Qt::KeypadModifier)) { + auto& cursorManager = Cursor::Manager::instance(); + cursorManager.setScale(cursorManager.getScale() / 1.1f); + } else { + _myAvatar->decreaseSize(); + } break; + } + case Qt::Key_Equal: _myAvatar->resetSize(); break; diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index bc8047fc98..7895232f8a 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "AudioClient.h" #include "audio/AudioIOStatsRenderer.h" @@ -143,7 +144,6 @@ ApplicationOverlay::ApplicationOverlay() : _alpha(1.0f), _oculusUIRadius(1.0f), _trailingAudioLoudness(0.0f), - _crosshairTexture(0), _previousBorderWidth(-1), _previousBorderHeight(-1), _previousMagnifierBottomLeft(), @@ -257,6 +257,18 @@ void with_each_texture(GLuint firstPassTexture, GLuint secondPassTexture, F f) { glDisable(GL_TEXTURE_2D); } +void ApplicationOverlay::bindCursorTexture(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); + } + glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_cursors[iconId])); +} + // Draws the FBO texture for the screen void ApplicationOverlay::displayOverlayTexture() { if (_alpha == 0.0f) { @@ -282,14 +294,10 @@ void ApplicationOverlay::displayOverlayTexture() { glm::vec4(1.0f, 1.0f, 1.0f, _alpha)); }); - if (!_crosshairTexture) { - _crosshairTexture = DependencyManager::get()-> - getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png"); - } - + //draw the mouse pointer glm::vec2 canvasSize = qApp->getCanvasSize(); - glm::vec2 mouseSize = 32.0f / canvasSize; + glm::vec2 mouseSize = 32.0f / canvasSize * Cursor::Manager::instance().getScale(); auto mouseTopLeft = topLeft * mouseSize; auto mouseBottomRight = bottomRight * mouseSize; vec2 mousePosition = vec2(qApp->getMouseX(), qApp->getMouseY()); @@ -300,7 +308,7 @@ void ApplicationOverlay::displayOverlayTexture() { glEnable(GL_TEXTURE_2D); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); + bindCursorTexture(); glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; DependencyManager::get()->renderQuad( mouseTopLeft + mousePosition, mouseBottomRight + mousePosition, @@ -450,18 +458,14 @@ void ApplicationOverlay::displayOverlayTextureStereo(Camera& whichCamera, float overlayColor); }); - if (!_crosshairTexture) { - _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + - "images/sixense-reticle.png"); - } - //draw the mouse pointer glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); + bindCursorTexture(); glm::vec2 canvasSize = qApp->getCanvasSize(); - const float reticleSize = 40.0f / canvasSize.x * quadWidth; + const float reticleSize = 40.0f / canvasSize.x * quadWidth * + Cursor::Manager::instance().getScale(); x -= reticleSize / 2.0f; y += reticleSize / 2.0f; const float mouseX = (qApp->getMouseX() / (float)canvasSize.x) * quadWidth; @@ -583,16 +587,12 @@ bool ApplicationOverlay::calculateRayUICollisionPoint(const glm::vec3& position, //Renders optional pointers void ApplicationOverlay::renderPointers() { - //lazily load crosshair texture - if (_crosshairTexture == 0) { - _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png"); - } glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); + bindCursorTexture(); if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) { //If we are in oculus, render reticle later @@ -757,7 +757,7 @@ void ApplicationOverlay::renderPointersOculus() { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); + bindCursorTexture(); glDisable(GL_DEPTH_TEST); glMatrixMode(GL_MODELVIEW); diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index ee82c9274a..0e8206547a 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -108,6 +108,7 @@ private: void renderCameraToggle(); void renderStatsAndLogs(); void renderDomainConnectionStatusBorder(); + void bindCursorTexture(uint8_t cursorId = 0); TexturedHemisphere _overlays; @@ -127,7 +128,8 @@ private: float _trailingAudioLoudness; - gpu::TexturePointer _crosshairTexture; + QMap _cursors; + GLuint _newUiTexture{ 0 }; int _reticleQuad; diff --git a/libraries/ui/src/CursorManager.cpp b/libraries/ui/src/CursorManager.cpp index 8b318d549a..efadd09142 100644 --- a/libraries/ui/src/CursorManager.cpp +++ b/libraries/ui/src/CursorManager.cpp @@ -8,25 +8,85 @@ #include "CursorManager.h" +#include +#include +#include + +#include + namespace Cursor { - enum class Source { - MOUSE, - LEFT_HAND, - RIGHT_HAND, - UNKNOWN, + + void Instance::setIcon(uint16_t icon) { + _icon = icon; + } + + uint16_t Instance::getIcon() const { + return _icon; + } + + + class MouseInstance : public Instance { + Source getType() const { + return Source::MOUSE; + } + + ivec2 getScreenPosition() const { + return toGlm(QCursor::pos()); + } + + ivec2 getWindowPosition(QWidget* widget) const { + return toGlm(widget->mapFromGlobal(QCursor::pos())); + } + + vec2 getRelativePosition(QWidget* widget) const { + vec2 pos = getWindowPosition(widget); + pos /= vec2(toGlm(widget->size())); + return pos; + } }; - class Instance { - Source type; - }; + static QMap ICONS; + static uint16_t _customIconId = Icon::USER_BASE; - class Manager { - public: - static Manager& instance(); + Manager::Manager() { + ICONS[Icon::DEFAULT] = PathUtils::resourcesPath() + "images/sixense-reticle.png"; + ICONS[Icon::LINK] = PathUtils::resourcesPath() + "images/reticleLink.png"; + } - uint8_t getCount(); - Instance - }; -} + Manager& Manager::instance() { + static Manager instance; + return instance; + } + uint8_t Manager::getCount() { + return 1; + } + Instance* Manager::getCursor(uint8_t index) { + Q_ASSERT(index < getCount()); + static MouseInstance mouseInstance; + if (index == 0) { + return &mouseInstance; + } + return nullptr; + } + + uint16_t Manager::registerIcon(const QString& path) { + ICONS[_customIconId] = path; + return _customIconId++; + } + + const QString& Manager::getIconImage(uint16_t icon) { + Q_ASSERT(ICONS.count(icon)); + return ICONS[icon]; + } + + float Manager::getScale() { + return _scale; + } + + void Manager::setScale(float scale) { + _scale = scale; + } + +} \ No newline at end of file diff --git a/libraries/ui/src/CursorManager.h b/libraries/ui/src/CursorManager.h index a2d26efc58..c5810caf58 100644 --- a/libraries/ui/src/CursorManager.h +++ b/libraries/ui/src/CursorManager.h @@ -7,6 +7,9 @@ // #pragma once +#include + +#include namespace Cursor { enum class Source { @@ -16,16 +19,42 @@ namespace Cursor { UNKNOWN, }; + enum Icon { + DEFAULT, + LINK, + GRAB, + + // Add new system cursors here + + // User cursors will have ids over this value + USER_BASE = 0xFF, + }; + class Instance { - Source type; + public: + virtual Source getType() const = 0; + virtual ivec2 getWindowPosition(QWidget* widget) const = 0; + virtual vec2 getRelativePosition(QWidget* widget) const = 0; + virtual ivec2 getScreenPosition() const = 0; + virtual void setIcon(uint16_t icon); + virtual uint16_t getIcon() const; + private: + uint16_t _icon; }; class Manager { + Manager(); + Manager(const Manager& other) = delete; public: static Manager& instance(); - uint8_t getCount(); - Instance + float getScale(); + void setScale(float scale); + Instance* getCursor(uint8_t index = 0); + uint16_t registerIcon(const QString& path); + const QString& getIconImage(uint16_t icon); + private: + float _scale{ 1.0f }; }; } From 3bd0c7bd87344da6a9f291f9f44cd4d4c66add73 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Wed, 10 Jun 2015 14:35:52 -0600 Subject: [PATCH 16/94] Code Review changes --- interface/CMakeLists.txt | 2 +- interface/src/Application.cpp | 8 +++---- interface/src/ui/UpdateDialog.cpp | 6 ++--- .../CMakeLists.txt | 2 +- .../src/AutoUpdater.cpp} | 22 +++++++++---------- .../src/AutoUpdater.h} | 12 +++++----- 6 files changed, 26 insertions(+), 26 deletions(-) rename libraries/{auto-update => auto-updater}/CMakeLists.txt (69%) rename libraries/{auto-update/src/AutoUpdate.cpp => auto-updater/src/AutoUpdater.cpp} (91%) rename libraries/{auto-update/src/AutoUpdate.h => auto-updater/src/AutoUpdater.h} (90%) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index b28c971298..00f6d4c3b2 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -140,7 +140,7 @@ target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES}) # link required hifi libraries link_hifi_libraries(shared octree environment gpu model render fbx networking entities avatars audio audio-client animation script-engine physics - render-utils entities-renderer ui auto-update) + render-utils entities-renderer ui auto-updater) add_dependency_external_projects(sdl2) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 88a8a274a3..560dccb238 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -59,7 +59,7 @@ #include #include #include -#include +#include #include #include #include @@ -295,7 +295,7 @@ bool setupEssentials(int& argc, char** argv) { auto discoverabilityManager = DependencyManager::set(); auto sceneScriptingInterface = DependencyManager::set(); auto offscreenUi = DependencyManager::set(); - auto autoUpdate = DependencyManager::set(); + auto autoUpdater = DependencyManager::set(); auto pathUtils = DependencyManager::set(); return true; @@ -624,8 +624,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(ddeTracker.data(), &FaceTracker::muteToggled, this, &Application::faceTrackerMuteToggled); #endif - auto applicationUpdater = DependencyManager::get(); - connect(applicationUpdater.data(), &AutoUpdate::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog); + auto applicationUpdater = DependencyManager::get(); + connect(applicationUpdater.data(), &AutoUpdater::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog); applicationUpdater->checkForUpdate(); } diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index 61a7cb892c..69dfc343d9 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -11,7 +11,7 @@ #include "UpdateDialog.h" -#include +#include #include "DependencyManager.h" @@ -20,7 +20,7 @@ HIFI_QML_DEF(UpdateDialog) UpdateDialog::UpdateDialog(QQuickItem* parent) : OffscreenQmlDialog(parent) { - auto applicationUpdater = DependencyManager::get(); + auto applicationUpdater = DependencyManager::get(); int currentVersion = QCoreApplication::applicationVersion().toInt(); int latestVersion = applicationUpdater.data()->getBuildData().lastKey(); int versionsBehind = latestVersion - currentVersion; @@ -46,6 +46,6 @@ void UpdateDialog::hide() { } void UpdateDialog::triggerUpgrade() { - auto applicationUpdater = DependencyManager::get(); + auto applicationUpdater = DependencyManager::get(); applicationUpdater.data()->performAutoUpdate(applicationUpdater.data()->getBuildData().lastKey()); } \ No newline at end of file diff --git a/libraries/auto-update/CMakeLists.txt b/libraries/auto-updater/CMakeLists.txt similarity index 69% rename from libraries/auto-update/CMakeLists.txt rename to libraries/auto-updater/CMakeLists.txt index 0419048169..b3665af2cb 100644 --- a/libraries/auto-update/CMakeLists.txt +++ b/libraries/auto-updater/CMakeLists.txt @@ -1,3 +1,3 @@ -set(TARGET_NAME auto-update) +set(TARGET_NAME auto-updater) setup_hifi_library(Network) link_hifi_libraries(shared networking) diff --git a/libraries/auto-update/src/AutoUpdate.cpp b/libraries/auto-updater/src/AutoUpdater.cpp similarity index 91% rename from libraries/auto-update/src/AutoUpdate.cpp rename to libraries/auto-updater/src/AutoUpdater.cpp index 04bb294fec..445cdaec7b 100644 --- a/libraries/auto-update/src/AutoUpdate.cpp +++ b/libraries/auto-updater/src/AutoUpdater.cpp @@ -1,5 +1,5 @@ // -// AutoUpdate.cpp +// AutoUpdater.cpp // libraries/auto-update/src // // Created by Leonardo Murillo on 6/1/2015. @@ -9,12 +9,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "AutoUpdate.h" +#include "AutoUpdater.h" #include #include -AutoUpdate::AutoUpdate() { +AutoUpdater::AutoUpdater() { #if defined Q_OS_WIN32 _operatingSystem = "windows"; #elif defined Q_OS_MAC @@ -26,19 +26,19 @@ AutoUpdate::AutoUpdate() { connect(this, SIGNAL(latestVersionDataParsed()), this, SLOT(checkVersionAndNotify())); } -void AutoUpdate::checkForUpdate() { +void AutoUpdater::checkForUpdate() { this->getLatestVersionData(); } -void AutoUpdate::getLatestVersionData() { +void AutoUpdater::getLatestVersionData() { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest latestVersionRequest(BUILDS_XML_URL); latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); QNetworkReply* reply = networkAccessManager.get(latestVersionRequest); - connect(reply, &QNetworkReply::finished, this, &AutoUpdate::parseLatestVersionData); + connect(reply, &QNetworkReply::finished, this, &AutoUpdater::parseLatestVersionData); } -void AutoUpdate::parseLatestVersionData() { +void AutoUpdater::parseLatestVersionData() { QNetworkReply* sender = qobject_cast(QObject::sender()); QXmlStreamReader xml(sender); @@ -103,7 +103,7 @@ void AutoUpdate::parseLatestVersionData() { emit latestVersionDataParsed(); } -void AutoUpdate::checkVersionAndNotify() { +void AutoUpdater::checkVersionAndNotify() { int latestVersionAvailable = _builds.lastKey(); if (QCoreApplication::applicationVersion() != "dev" && QCoreApplication::applicationVersion().toInt() < latestVersionAvailable) { @@ -111,7 +111,7 @@ void AutoUpdate::checkVersionAndNotify() { } } -void AutoUpdate::performAutoUpdate(int version) { +void AutoUpdater::performAutoUpdate(int version) { // NOTE: This is not yet auto updating - however this is a checkpoint towards that end // Next PR will handle the automatic download, upgrading and application restart const QMap& chosenVersion = _builds.value(version); @@ -120,11 +120,11 @@ void AutoUpdate::performAutoUpdate(int version) { QCoreApplication::quit(); } -void AutoUpdate::downloadUpdateVersion(int version) { +void AutoUpdater::downloadUpdateVersion(int version) { emit newVersionIsDownloaded(); } -void AutoUpdate::appendBuildData(int versionNumber, +void AutoUpdater::appendBuildData(int versionNumber, const QString& downloadURL, const QString& releaseTime, const QString& releaseNotes, diff --git a/libraries/auto-update/src/AutoUpdate.h b/libraries/auto-updater/src/AutoUpdater.h similarity index 90% rename from libraries/auto-update/src/AutoUpdate.h rename to libraries/auto-updater/src/AutoUpdater.h index 71b55095ee..a02ff11ca9 100644 --- a/libraries/auto-update/src/AutoUpdate.h +++ b/libraries/auto-updater/src/AutoUpdater.h @@ -1,5 +1,5 @@ // -// AutoUpdate.h +// AutoUpdater.h // libraries/auto-update/src // // Created by Leonardo Murillo on 6/1/2015. @@ -9,8 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_AutoUpdate_h -#define hifi_AutoUpdate_h +#ifndef hifi_AutoUpdater_h +#define hifi_AutoUpdater_h #include @@ -31,12 +31,12 @@ const QUrl BUILDS_XML_URL("https://highfidelity.com/builds.xml"); -class AutoUpdate : public QObject, public Dependency { +class AutoUpdater : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY public: - AutoUpdate(); + AutoUpdater(); void checkForUpdate(); const QMap> &getBuildData() { return _builds; } @@ -65,4 +65,4 @@ private slots: }; -#endif // _hifi_AutoUpdate_h +#endif // _hifi_AutoUpdater_h From 1f62fb4b6fbe33f090dc072dd3b0e15efc5b6d2d Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 10 Jun 2015 15:24:29 -0700 Subject: [PATCH 17/94] Adding standard vertex and pixel shaders for drawing texture in applicationOverlay --- interface/src/ui/ApplicationOverlay.cpp | 30 ++++++++++++++--- interface/src/ui/ApplicationOverlay.h | 4 +++ .../render-utils/src/standardDrawTexture.slf | 24 ++++++++++++++ .../src/standardTransformPNTC.slv | 33 +++++++++++++++++++ 4 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 libraries/render-utils/src/standardDrawTexture.slf create mode 100644 libraries/render-utils/src/standardTransformPNTC.slv diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 79ec39b506..f57c10b64e 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -36,6 +36,9 @@ #include "Util.h" #include "ui/Stats.h" +#include "../../libraries/render-utils/standardTransformPNTC_vert.h" +#include "../../libraries/render-utils/standardDrawTexture_frag.h" + // Used to animate the magnification windows const float MAG_SPEED = 0.08f; @@ -278,6 +281,24 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { // glDisable(GL_TEXTURE_2D); //} +gpu::PipelinePointer ApplicationOverlay::getDrawPipeline() { + if (!_standardDrawPipeline) { + auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(standardTransformPNTC_vert))); + auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(standardDrawTexture_frag))); + auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); + gpu::Shader::makeProgram((*program)); + + auto state = gpu::StatePointer(new gpu::State()); + + // enable decal blend + state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); + + _standardDrawPipeline.reset(gpu::Pipeline::create(program, state)); + } + + return _standardDrawPipeline; +} + // Draws the FBO texture for the screen void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) { @@ -290,11 +311,12 @@ void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) { PathUtils::resourcesPath() + "images/sixense-reticle.png"); } - /* - FIXME - doesn't work + + //FIXME - doesn't work renderArgs->_context->syncCache(); gpu::Batch batch; - DependencyManager::get()->bindSimpleProgram(batch, true); + //DependencyManager::get()->bindSimpleProgram(batch, true); + batch.setPipeline(getDrawPipeline()); batch.setModelTransform(Transform()); batch.setProjectionTransform(mat4()); batch.setViewTransform(Transform()); @@ -302,7 +324,7 @@ void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) { DependencyManager::get()->renderUnitQuad(batch, vec4(vec3(1), _alpha)); renderArgs->_context->render(batch); return; - */ + glDisable(GL_DEPTH_TEST); diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 36161dd29d..63ef48bd92 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -143,6 +143,10 @@ private: glm::vec3 _previousMagnifierTopLeft; glm::vec3 _previousMagnifierTopRight; + gpu::PipelinePointer _standardDrawPipeline; + + gpu::PipelinePointer getDrawPipeline(); + }; #endif // hifi_ApplicationOverlay_h diff --git a/libraries/render-utils/src/standardDrawTexture.slf b/libraries/render-utils/src/standardDrawTexture.slf new file mode 100644 index 0000000000..4fbeb6eb7f --- /dev/null +++ b/libraries/render-utils/src/standardDrawTexture.slf @@ -0,0 +1,24 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// standardDrawTexture.frag +// fragment shader +// +// Created by Sam Gateau on 6/10/15. +// 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 +// + +// the texture +uniform sampler2D colorMap; + +varying vec2 varTexcoord; +varying vec4 varColor; + + +void main(void) { + vec4 color = texture2D(colorMap, varTexcoord); + gl_FragColor = color * varColor; +} diff --git a/libraries/render-utils/src/standardTransformPNTC.slv b/libraries/render-utils/src/standardTransformPNTC.slv new file mode 100644 index 0000000000..fd2c28049f --- /dev/null +++ b/libraries/render-utils/src/standardTransformPNTC.slv @@ -0,0 +1,33 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// standardTransformPNTC.slv +// vertex shader +// +// Created by Sam Gateau on 6/10/2015. +// 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 gpu/Transform.slh@> + +<$declareStandardTransform()$> + +varying vec3 varNormal; +varying vec2 varTexcoord; +varying vec4 varColor; + +void main(void) { + varTexcoord = gl_MultiTexCoord0.xy; + varColor = gl_Color; + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$> + <$transformModelToEyeDir(cam, obj, gl_Normal, varNormal)$> + varNormal = normalize(varNormal); +} \ No newline at end of file From ac0fc5d97405ec304bab0f15e18773dab2556d7c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 11 Jun 2015 00:50:24 -0700 Subject: [PATCH 18/94] Working on oculus overlay code --- interface/src/ui/ApplicationOverlay.cpp | 235 ++++++++++++------------ interface/src/ui/ApplicationOverlay.h | 28 +-- 2 files changed, 117 insertions(+), 146 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index f57c10b64e..4be47e342a 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -301,7 +301,6 @@ gpu::PipelinePointer ApplicationOverlay::getDrawPipeline() { // Draws the FBO texture for the screen void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) { - if (_alpha == 0.0f) { return; } @@ -312,80 +311,77 @@ void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) { } - //FIXME - doesn't work renderArgs->_context->syncCache(); + glViewport(0, 0, qApp->getDeviceSize().width(), qApp->getDeviceSize().height()); + gpu::Batch batch; + Transform model; //DependencyManager::get()->bindSimpleProgram(batch, true); batch.setPipeline(getDrawPipeline()); batch.setModelTransform(Transform()); batch.setProjectionTransform(mat4()); - batch.setViewTransform(Transform()); - batch.setUniformTexture(0, _crosshairTexture); + batch.setViewTransform(model); + batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture()); + batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); DependencyManager::get()->renderUnitQuad(batch, vec4(vec3(1), _alpha)); + + //draw the mouse pointer + glm::vec2 canvasSize = qApp->getCanvasSize(); + + // Get the mouse coordinates and convert to NDC [-1, 1] + vec2 mousePosition = vec2(qApp->getMouseX(), qApp->getMouseY()); + mousePosition /= canvasSize; + mousePosition *= 2.0f; + mousePosition -= 1.0f; + mousePosition.y *= -1.0f; + model.setTranslation(vec3(mousePosition, 0)); + glm::vec2 mouseSize = 32.0f / canvasSize; + model.setScale(vec3(mouseSize, 1.0f)); + batch.setModelTransform(model); + batch.setUniformTexture(0, _crosshairTexture); + glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; + DependencyManager::get()->renderUnitQuad(batch, vec4(1)); renderArgs->_context->render(batch); - return; - - - - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_TEXTURE_2D); - glViewport(0, 0, qApp->getDeviceSize().width(), qApp->getDeviceSize().height()); - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - { - glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture()); - DependencyManager::get()->renderUnitQuad(vec4(vec3(1), _alpha)); - //draw the mouse pointer - glm::vec2 canvasSize = qApp->getCanvasSize(); - - // Get the mouse coordinates and convert to NDC [-1, 1] - vec2 mousePosition = vec2(qApp->getMouseX(), qApp->getMouseY()); - mousePosition /= canvasSize; - mousePosition *= 2.0f; - mousePosition -= 1.0f; - mousePosition.y *= -1.0f; - mat4 mouseMv = glm::translate(mat4(), vec3(mousePosition, 0)); - - // Scale the mouse based on the canvasSize (NOT the device size, - // we don't want a smaller mouse on retina displays) - glm::vec2 mouseSize = 32.0f / canvasSize; - mouseMv = glm::scale(mouseMv, vec3(mouseSize, 1.0f)); - - // Push the resulting matrix into modelview - glLoadMatrixf(glm::value_ptr(mouseMv)); - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); - glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; - DependencyManager::get()->renderUnitQuad(reticleColor); - } - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); } + +static gpu::BufferPointer _hemiVertices; +static gpu::BufferPointer _hemiIndices; +static int _hemiIndexCount{ 0 }; + // Draws the FBO texture for Oculus rift. void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera& whichCamera) { if (_alpha == 0.0f) { return; } - _overlays.buildVBO(_textureFov, _textureAspectRatio, 80, 80); - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_TEXTURE_2D); - glActiveTexture(GL_TEXTURE0); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); + auto geometryCache = DependencyManager::get(); + gpu::Batch batch; + //DependencyManager::get()->bindSimpleProgram(batch, true); + batch.setPipeline(getDrawPipeline()); + batch._glDisable(GL_DEPTH_TEST); + batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture()); + batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + batch.setProjectionTransform(whichCamera.getProjection()); + batch.setViewTransform(Transform()); + + Transform model; + model.setTranslation(vec3(0.0f, 0.0f, -2.0f)); + batch.setModelTransform(model); + + // FIXME doesn't work + drawSphereSection(batch); + + // works... + geometryCache->renderUnitQuad(batch, vec4(vec3(1), _alpha)); + + renderArgs->_context->render(batch); + // batch.setUniformTexture(0, gpu::TexturePointer()); + // geometryCache->renderSolidCube(batch, 0.5f, vec4(1)); + + /* // The camera here contains only the head pose relative to the avatar position vec3 pos = whichCamera.getPosition(); quat rot = whichCamera.getOrientation(); @@ -394,6 +390,7 @@ void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera glLoadMatrixf(glm::value_ptr(glm::inverse(overlayXfm))); glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture()); _overlays.render(); + */ //Update and draw the magnifiers /* @@ -423,11 +420,11 @@ void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera }); } } - */ if (!Application::getInstance()->isMouseHidden()) { renderPointersOculus(); } + */ } // Draws the FBO texture for 3DTV. @@ -1104,109 +1101,102 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder() { } } -ApplicationOverlay::TexturedHemisphere::TexturedHemisphere() : - _vertices(0), - _indices(0), - _vbo(0, 0) { -} -ApplicationOverlay::TexturedHemisphere::~TexturedHemisphere() { - cleanupVBO(); -} - -void ApplicationOverlay::TexturedHemisphere::buildVBO(const float fov, - const float aspectRatio, - const int slices, - const int stacks) { +void ApplicationOverlay::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) { + if (_hemiVerticesID != GeometryCache::UNKNOWN_ID && textureFOV == fov && textureAspectRatio == aspectRatio) { return; } + textureFOV = fov; textureAspectRatio = aspectRatio; + auto geometryCache = DependencyManager::get(); + if (_hemiVerticesID == GeometryCache::UNKNOWN_ID) { + _hemiVerticesID = geometryCache->allocateID(); + } + + _hemiVertices = gpu::BufferPointer(new gpu::Buffer()); + _hemiIndices = gpu::BufferPointer(new gpu::Buffer()); + + if (fov >= PI) { qDebug() << "TexturedHemisphere::buildVBO(): FOV greater or equal than Pi will create issues"; } - // Cleanup old VBO if necessary - cleanupVBO(); - + //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm - // Compute number of vertices needed - _vertices = slices * stacks; - + vec3 pos; // Compute vertices positions and texture UV coordinate - TextureVertex* vertexData = new TextureVertex[_vertices]; - TextureVertex* vertexPtr = &vertexData[0]; + // Create and write to buffer + //_hemiVertices->(sizeof(vec3) + sizeof(vec2) + sizeof(vec4)) * stacks * slices); for (int i = 0; i < stacks; i++) { float stacksRatio = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f // abs(theta) <= fov / 2.0f float pitch = -fov * (stacksRatio - 0.5f); - for (int j = 0; j < slices; j++) { float slicesRatio = (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 * (slicesRatio - 0.5f); - - vertexPtr->position = getPoint(yaw, pitch); - vertexPtr->uv.x = slicesRatio; - vertexPtr->uv.y = stacksRatio; - vertexPtr++; + pos = getPoint(yaw, pitch); + _hemiVertices->append(sizeof(pos), (gpu::Byte*)&pos); + _hemiVertices->append(sizeof(vec2), (gpu::Byte*)&vec2(slicesRatio, stacksRatio)); + _hemiVertices->append(sizeof(vec4), (gpu::Byte*)&vec4(1)); } } - // Create and write to buffer - glGenBuffers(1, &_vbo.first); - glBindBuffer(GL_ARRAY_BUFFER, _vbo.first); - static const int BYTES_PER_VERTEX = sizeof(TextureVertex); - glBufferData(GL_ARRAY_BUFFER, _vertices * BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW); - delete[] vertexData; - // 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); - _indices = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE; + _hemiIndexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE; // Compute indices order - GLushort* indexData = new GLushort[_indices]; - GLushort* indexPtr = indexData; + 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; - - *(indexPtr++) = topLeftIndex; - *(indexPtr++) = bottomLeftIndex; - *(indexPtr++) = topRightIndex; - - *(indexPtr++) = topRightIndex; - *(indexPtr++) = bottomLeftIndex; - *(indexPtr++) = bottomRightIndex; + // 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); } } - // Create and write to buffer - glGenBuffers(1, &_vbo.second); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo.second); - static const int BYTES_PER_INDEX = sizeof(GLushort); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, _indices * BYTES_PER_INDEX, indexData, GL_STATIC_DRAW); - delete[] indexData; + _hemiIndices->append(sizeof(GLushort) * indices.size(), (gpu::Byte*)&indices[0]); } -void ApplicationOverlay::TexturedHemisphere::cleanupVBO() { - if (_vbo.first != 0) { - glDeleteBuffers(1, &_vbo.first); - _vbo.first = 0; - } - if (_vbo.second != 0) { - glDeleteBuffers(1, &_vbo.second); - _vbo.second = 0; - } + +void ApplicationOverlay::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; + gpu::Stream::FormatPointer streamFormat(new gpu::Stream::Format()); // 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), sizeof(vec3)); + streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_DATA_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA), sizeof(vec3) + sizeof(vec2)); + batch.setInputFormat(streamFormat); + + static const int VERTEX_STRIDE = sizeof(vec3) + sizeof(vec2) + sizeof(vec4); + 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); } + GLuint ApplicationOverlay::getOverlayTexture() { return _framebufferObject->texture(); } @@ -1234,6 +1224,7 @@ void ApplicationOverlay::buildFramebufferObject() { glBindTexture(GL_TEXTURE_2D, 0); } +/* //Renders a hemisphere with texture coordinates. void ApplicationOverlay::TexturedHemisphere::render() { if (_vbo.first == 0 || _vbo.second == 0) { @@ -1261,7 +1252,7 @@ void ApplicationOverlay::TexturedHemisphere::render() { glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } - +*/ glm::vec2 ApplicationOverlay::directionToSpherical(const glm::vec3& direction) { glm::vec2 result; // Compute yaw diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 63ef48bd92..d78ad4bc9f 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -67,28 +67,8 @@ public: static glm::vec2 sphericalToScreen(const glm::vec2 & sphericalPos); private: - // Interleaved vertex data - struct TextureVertex { - glm::vec3 position; - glm::vec2 uv; - }; - - typedef QPair VerticesIndices; - class TexturedHemisphere { - public: - TexturedHemisphere(); - ~TexturedHemisphere(); - void buildVBO(const float fov, const float aspectRatio, const int slices, const int stacks); - void render(); - - private: - void cleanupVBO(); - - GLuint _vertices; - GLuint _indices; - VerticesIndices _vbo; - }; - + void buildHemiVertices(const float fov, const float aspectRatio, const int slices, const int stacks); + void drawSphereSection(gpu::Batch& batch); float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE; QOpenGLFramebufferObject* _framebufferObject; @@ -105,11 +85,11 @@ private: void renderDomainConnectionStatusBorder(); void buildFramebufferObject(); - - TexturedHemisphere _overlays; float _textureFov; float _textureAspectRatio; + int _hemiVerticesID{ GeometryCache::UNKNOWN_ID }; + enum Reticles { MOUSE, LEFT_CONTROLLER, RIGHT_CONTROLLER, NUMBER_OF_RETICLES }; bool _reticleActive[NUMBER_OF_RETICLES]; From ef520353008fb10205bcd8034b8d14d745966934 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 11 Jun 2015 02:20:51 -0700 Subject: [PATCH 19/94] Working on functional overlays --- interface/src/Application.cpp | 3 +- interface/src/devices/OculusManager.cpp | 38 +++++++++++----------- interface/src/devices/TV3DManager.cpp | 18 +++++------ interface/src/ui/ApplicationOverlay.cpp | 42 ++++++++++++++----------- 4 files changed, 53 insertions(+), 48 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9d5c43b7cf..a93b0186aa 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -941,6 +941,7 @@ void Application::paintGL() { glPushMatrix(); glLoadIdentity(); displaySide(&renderArgs, _myCamera); + _applicationOverlay.displayOverlayTexture(&renderArgs); glPopMatrix(); if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { @@ -957,8 +958,6 @@ void Application::paintGL() { 0, 0, _glWidget->getDeviceSize().width(), _glWidget->getDeviceSize().height(), GL_COLOR_BUFFER_BIT, GL_NEAREST); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - - _applicationOverlay.displayOverlayTexture(&renderArgs); } if (!OculusManager::isConnected() || OculusManager::allowSwap()) { diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 77263935f6..2ee62c85f3 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -615,6 +615,7 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const renderArgs->_renderSide = RenderArgs::MONO; qApp->displaySide(renderArgs, *_camera, false); + qApp->getApplicationOverlay().displayOverlayTextureHmd(renderArgs, *_camera); }); _activeEye = ovrEye_Count; @@ -629,28 +630,27 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const glBindFramebuffer(GL_FRAMEBUFFER, 0); } - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(finalFbo)); - //Render each eye into an fbo - for_each_eye(_ovrHmd, [&](ovrEyeType eye) { - _activeEye = eye; - // Update our camera to what the application camera is doing - _camera->setRotation(toGlm(eyeRenderPose[eye].Orientation)); - _camera->setPosition(toGlm(eyeRenderPose[eye].Position)); - configureCamera(*_camera); - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(glm::value_ptr(_camera->getProjection())); + //glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(finalFbo)); + ////Render each eye into an fbo + //for_each_eye(_ovrHmd, [&](ovrEyeType eye) { + // _activeEye = eye; + // // Update our camera to what the application camera is doing + // _camera->setRotation(toGlm(eyeRenderPose[eye].Orientation)); + // _camera->setPosition(toGlm(eyeRenderPose[eye].Position)); + // configureCamera(*_camera); + // glMatrixMode(GL_PROJECTION); + // glLoadMatrixf(glm::value_ptr(_camera->getProjection())); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); + // glMatrixMode(GL_MODELVIEW); + // glLoadIdentity(); - ovrRecti & vp = _eyeTextures[eye].Header.RenderViewport; - vp.Size.h = _recommendedTexSize.h * _offscreenRenderScale; - vp.Size.w = _recommendedTexSize.w * _offscreenRenderScale; + // ovrRecti & vp = _eyeTextures[eye].Header.RenderViewport; + // vp.Size.h = _recommendedTexSize.h * _offscreenRenderScale; + // vp.Size.w = _recommendedTexSize.w * _offscreenRenderScale; - glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h); - qApp->getApplicationOverlay().displayOverlayTextureHmd(renderArgs, *_camera); - }); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + // glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h); + //}); + //glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glPopMatrix(); diff --git a/interface/src/devices/TV3DManager.cpp b/interface/src/devices/TV3DManager.cpp index c14e589389..b0a2ff7e3b 100644 --- a/interface/src/devices/TV3DManager.cpp +++ b/interface/src/devices/TV3DManager.cpp @@ -12,6 +12,7 @@ #include "InterfaceConfig.h" #include +#include #include #include "gpu/GLBackend.h" @@ -106,21 +107,20 @@ void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) { _activeEye = &eye; glViewport(portalX, portalY, portalW, portalH); glScissor(portalX, portalY, portalW, portalH); + + glm::mat4 projection = glm::frustum(eye.left, eye.right, eye.bottom, eye.top, nearZ, farZ); + float fov = atan(1.0f / projection[1][1]); + projection = glm::translate(projection, vec3(eye.modelTranslation, 0, 0)); + eyeCamera.setProjection(projection); + glMatrixMode(GL_PROJECTION); glLoadIdentity(); // reset projection matrix - glFrustum(eye.left, eye.right, eye.bottom, eye.top, nearZ, farZ); // set left view frustum - GLfloat p[4][4]; - // Really? - glGetFloatv(GL_PROJECTION_MATRIX, &(p[0][0])); - float cotangent = p[1][1]; - GLfloat fov = atan(1.0f / cotangent); - glTranslatef(eye.modelTranslation, 0.0, 0.0); // translate to cancel parallax - + glLoadMatrixf(glm::value_ptr(projection)); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); renderArgs->_renderSide = RenderArgs::MONO; qApp->displaySide(renderArgs, eyeCamera, false); - qApp->getApplicationOverlay().displayOverlayTextureStereo(renderArgs, whichCamera, _aspect, fov); + qApp->getApplicationOverlay().displayOverlayTexture(renderArgs); _activeEye = NULL; }, [&]{ // render right side view diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 4be47e342a..b4824e96a3 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -312,7 +312,6 @@ void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) { renderArgs->_context->syncCache(); - glViewport(0, 0, qApp->getDeviceSize().width(), qApp->getDeviceSize().height()); gpu::Batch batch; Transform model; @@ -356,9 +355,10 @@ void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera return; } + renderArgs->_context->syncCache(); + auto geometryCache = DependencyManager::get(); gpu::Batch batch; - //DependencyManager::get()->bindSimpleProgram(batch, true); batch.setPipeline(getDrawPipeline()); batch._glDisable(GL_DEPTH_TEST); batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture()); @@ -367,19 +367,23 @@ void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera batch.setProjectionTransform(whichCamera.getProjection()); batch.setViewTransform(Transform()); - Transform model; - model.setTranslation(vec3(0.0f, 0.0f, -2.0f)); - batch.setModelTransform(model); + Transform mv; + mv.setTranslation(vec3(0.0f, 0.0f, -1.0f)); + mv.preRotate(glm::inverse(qApp->getCamera()->getHmdRotation())); + mv.setScale(vec3(1.0f, 1.0f / aspect(qApp->getCanvasSize()), 1.0f)); + batch.setModelTransform(mv); + // FIXME doesn't work - drawSphereSection(batch); + // drawSphereSection(batch); // works... geometryCache->renderUnitQuad(batch, vec4(vec3(1), _alpha)); + // sort of works, renders a semi-transparent red quad + // geometryCache->renderSolidCube(batch, 1.0f, vec4(1)); + renderArgs->_context->render(batch); - // batch.setUniformTexture(0, gpu::TexturePointer()); - // geometryCache->renderSolidCube(batch, 0.5f, vec4(1)); /* // The camera here contains only the head pose relative to the avatar position @@ -427,8 +431,9 @@ void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera */ } +/* // Draws the FBO texture for 3DTV. -void ApplicationOverlay::displayOverlayTextureStereo(RenderArgs* renderArgs, Camera& whichCamera, float aspectRatio, float fov) { +void ApplicationOverlay::displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov) { if (_alpha == 0.0f) { return; } @@ -473,15 +478,15 @@ void ApplicationOverlay::displayOverlayTextureStereo(RenderArgs* renderArgs, Cam GLfloat y = -halfQuadHeight; glDisable(GL_DEPTH_TEST); - //with_each_texture(_framebufferObject->texture(), _newUiTexture, [&] { - // DependencyManager::get()->renderQuad(glm::vec3(x, y + quadHeight, -distance), - // glm::vec3(x + quadWidth, y + quadHeight, -distance), - // glm::vec3(x + quadWidth, y, -distance), - // glm::vec3(x, y, -distance), - // glm::vec2(0.0f, 1.0f), glm::vec2(1.0f, 1.0f), - // glm::vec2(1.0f, 0.0f), glm::vec2(0.0f, 0.0f), - // overlayColor); - //}); + with_each_texture(_overlays.getTexture(), _newUiTexture, [&] { + DependencyManager::get()->renderQuad(glm::vec3(x, y + quadHeight, -distance), + glm::vec3(x + quadWidth, y + quadHeight, -distance), + glm::vec3(x + quadWidth, y, -distance), + glm::vec3(x, y, -distance), + glm::vec2(0.0f, 1.0f), glm::vec2(1.0f, 1.0f), + glm::vec2(1.0f, 0.0f), glm::vec2(0.0f, 0.0f), + overlayColor); + }); if (!_crosshairTexture) { _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + @@ -521,6 +526,7 @@ void ApplicationOverlay::displayOverlayTextureStereo(RenderArgs* renderArgs, Cam glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); glEnable(GL_LIGHTING); } +*/ void ApplicationOverlay::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const { cursorPos *= qApp->getCanvasSize(); From bc206633f5b1916a009fad216ee56332065349d3 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 11 Jun 2015 14:56:21 -0700 Subject: [PATCH 20/94] Fixing overlay rendering in HMD --- interface/resources/images/arrow.png | Bin 0 -> 3562 bytes interface/src/ui/ApplicationOverlay.cpp | 342 ++++++------------------ interface/src/ui/ApplicationOverlay.h | 2 +- 3 files changed, 78 insertions(+), 266 deletions(-) create mode 100644 interface/resources/images/arrow.png diff --git a/interface/resources/images/arrow.png b/interface/resources/images/arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..408881b5856eb47a30875e3e56d5864721161587 GIT binary patch literal 3562 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 R^nCyTaI$x`E7(oB_+R=!c8UN1 literal 0 HcmV?d00001 diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index b4824e96a3..e5f701e807 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -120,27 +120,6 @@ bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r, } } -void ApplicationOverlay::renderReticle(glm::quat orientation, float alpha) { - glPushMatrix(); { - glm::vec3 axis = glm::axis(orientation); - glRotatef(glm::degrees(glm::angle(orientation)), axis.x, axis.y, axis.z); - glm::vec3 topLeft = getPoint(reticleSize / 2.0f, -reticleSize / 2.0f); - glm::vec3 topRight = getPoint(-reticleSize / 2.0f, -reticleSize / 2.0f); - glm::vec3 bottomLeft = getPoint(reticleSize / 2.0f, reticleSize / 2.0f); - glm::vec3 bottomRight = getPoint(-reticleSize / 2.0f, reticleSize / 2.0f); - - // TODO: this version of renderQuad() needs to take a color - glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], alpha }; - - - - DependencyManager::get()->renderQuad(topLeft, bottomLeft, bottomRight, topRight, - glm::vec2(0.0f, 0.0f), glm::vec2(1.0f, 0.0f), - glm::vec2(1.0f, 1.0f), glm::vec2(0.0f, 1.0f), - reticleColor, _reticleQuad); - } glPopMatrix(); -} - ApplicationOverlay::ApplicationOverlay() : _textureFov(glm::radians(DEFAULT_HMD_UI_ANGULAR_SIZE)), _textureAspectRatio(1.0f), @@ -307,7 +286,7 @@ void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) { if (!_crosshairTexture) { _crosshairTexture = TextureCache::getImageTexture( - PathUtils::resourcesPath() + "images/sixense-reticle.png"); + PathUtils::resourcesPath() + "images/arrow.png"); } @@ -349,6 +328,40 @@ static gpu::BufferPointer _hemiVertices; static gpu::BufferPointer _hemiIndices; static int _hemiIndexCount{ 0 }; +glm::vec2 getPolarCoordinates(const PalmData& palm) { + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + auto avatarOrientation = myAvatar->getOrientation(); + auto eyePos = myAvatar->getDefaultEyePosition(); + glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm); + // Direction of the tip relative to the eye + glm::vec3 tipDirection = tip - eyePos; + // orient into avatar space + tipDirection = glm::inverse(avatarOrientation) * tipDirection; + // Normalize for trig functions + tipDirection = glm::normalize(tipDirection); + // Convert to polar coordinates + glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y)); + return polar; +} + +void ApplicationOverlay::renderReticle(gpu::Batch& batch, glm::quat orientation, float alpha) { + if (!_crosshairTexture) { + _crosshairTexture = TextureCache::getImageTexture( + PathUtils::resourcesPath() + "images/arrow.png"); + } + + batch.setUniformTexture(0, _crosshairTexture); + glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], alpha }; + + vec3 dist = getPoint(0, 0); + mat4 reticleXfm = glm::translate(mat4(), vec3(0, 0, -1)); + reticleXfm = glm::mat4_cast(orientation) * reticleXfm; + reticleXfm = glm::scale(reticleXfm, vec3(reticleSize, reticleSize, 1.0f)); + batch.setModelTransform(Transform(reticleXfm)); + DependencyManager::get()->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); +} + + // Draws the FBO texture for Oculus rift. void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera& whichCamera) { if (_alpha == 0.0f) { @@ -357,176 +370,60 @@ void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera renderArgs->_context->syncCache(); - auto geometryCache = DependencyManager::get(); gpu::Batch batch; batch.setPipeline(getDrawPipeline()); batch._glDisable(GL_DEPTH_TEST); + batch._glDisable(GL_CULL_FACE); batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture()); batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); batch.setProjectionTransform(whichCamera.getProjection()); batch.setViewTransform(Transform()); - Transform mv; - mv.setTranslation(vec3(0.0f, 0.0f, -1.0f)); - mv.preRotate(glm::inverse(qApp->getCamera()->getHmdRotation())); - mv.setScale(vec3(1.0f, 1.0f / aspect(qApp->getCanvasSize()), 1.0f)); - batch.setModelTransform(mv); + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + const quat& avatarOrientation = myAvatar->getOrientation(); + quat hmdOrientation = qApp->getCamera()->getHmdRotation(); + vec3 hmdPosition = glm::inverse(avatarOrientation) * qApp->getCamera()->getHmdPosition(); + mat4 overlayXfm = glm::mat4_cast(glm::inverse(hmdOrientation)) * glm::translate(mat4(), -hmdPosition); + batch.setModelTransform(Transform(overlayXfm)); + drawSphereSection(batch); - // FIXME doesn't work - // drawSphereSection(batch); + if (!_crosshairTexture) { + _crosshairTexture = TextureCache::getImageTexture( + PathUtils::resourcesPath() + "images/arrow.png"); + } + batch.setUniformTexture(0, _crosshairTexture); + auto geometryCache = DependencyManager::get(); + //Controller Pointers + for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) { + PalmData& palm = myAvatar->getHand()->getPalms()[i]; + if (palm.isActive()) { + glm::vec2 polar = getPolarCoordinates(palm); + // Convert to quaternion + mat4 pointerXfm = glm::mat4_cast(quat(vec3(polar.y, -polar.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); + mat4 reticleXfm = overlayXfm * pointerXfm; + reticleXfm = glm::scale(reticleXfm, vec3(reticleSize, reticleSize, 1.0f)); + batch.setModelTransform(reticleXfm); + // Render reticle at location + geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); + } + } - // works... - geometryCache->renderUnitQuad(batch, vec4(vec3(1), _alpha)); - - // sort of works, renders a semi-transparent red quad - // geometryCache->renderSolidCube(batch, 1.0f, vec4(1)); + //Mouse Pointer + if (_reticleActive[MOUSE]) { + glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(), + _reticlePosition[MOUSE].y())); + 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, vec3(reticleSize, reticleSize, 1.0f)); + batch.setModelTransform(reticleXfm); + geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); + } renderArgs->_context->render(batch); - - /* - // The camera here contains only the head pose relative to the avatar position - vec3 pos = whichCamera.getPosition(); - quat rot = whichCamera.getOrientation(); - mat4 overlayXfm = glm::translate(glm::mat4(), pos) * glm::mat4_cast(rot); - - glLoadMatrixf(glm::value_ptr(glm::inverse(overlayXfm))); - glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture()); - _overlays.render(); - */ - - //Update and draw the magnifiers - /* - // FIXME Mangifiers need to be re-thought - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - const float scale = myAvatar->getScale() * _oculusUIRadius; - overlayXfm = glm::scale(overlayXfm, vec3(scale)); - for (int i = 0; i < NUMBER_OF_RETICLES; i++) { - if (_magActive[i]) { - _magSizeMult[i] += MAG_SPEED; - if (_magSizeMult[i] > 1.0f) { - _magSizeMult[i] = 1.0f; - } - } else { - _magSizeMult[i] -= MAG_SPEED; - if (_magSizeMult[i] < 0.0f) { - _magSizeMult[i] = 0.0f; - } - } - - if (_magSizeMult[i] > 0.0f) { - //Render magnifier, but dont show border for mouse magnifier - glm::vec2 projection = screenToOverlay(glm::vec2(_reticlePosition[MOUSE].x(), - _reticlePosition[MOUSE].y())); - with_each_texture(_overlays.getTexture(), 0, [&] { - renderMagnifier(projection, _magSizeMult[i], i != MOUSE); - }); - } - } - - if (!Application::getInstance()->isMouseHidden()) { - renderPointersOculus(); - } - */ } -/* -// Draws the FBO texture for 3DTV. -void ApplicationOverlay::displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov) { - if (_alpha == 0.0f) { - return; - } - - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - const glm::vec3& viewMatrixTranslation = qApp->getViewMatrixTranslation(); - - glEnable(GL_BLEND); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - glEnable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - - glMatrixMode(GL_MODELVIEW); - - glPushMatrix(); - glLoadIdentity(); - // Transform to world space - glm::quat rotation = whichCamera.getRotation(); - glm::vec3 axis2 = glm::axis(rotation); - glRotatef(-glm::degrees(glm::angle(rotation)), axis2.x, axis2.y, axis2.z); - glTranslatef(viewMatrixTranslation.x, viewMatrixTranslation.y, viewMatrixTranslation.z); - - // Translate to the front of the camera - glm::vec3 pos = whichCamera.getPosition(); - glm::quat rot = myAvatar->getOrientation(); - glm::vec3 axis = glm::axis(rot); - - glTranslatef(pos.x, pos.y, pos.z); - glRotatef(glm::degrees(glm::angle(rot)), axis.x, axis.y, axis.z); - - glm::vec4 overlayColor = {1.0f, 1.0f, 1.0f, _alpha}; - - //Render - const GLfloat distance = 1.0f; - - const GLfloat halfQuadHeight = distance * tan(fov); - const GLfloat halfQuadWidth = halfQuadHeight * aspectRatio; - const GLfloat quadWidth = halfQuadWidth * 2.0f; - const GLfloat quadHeight = halfQuadHeight * 2.0f; - - GLfloat x = -halfQuadWidth; - GLfloat y = -halfQuadHeight; - glDisable(GL_DEPTH_TEST); - - with_each_texture(_overlays.getTexture(), _newUiTexture, [&] { - DependencyManager::get()->renderQuad(glm::vec3(x, y + quadHeight, -distance), - glm::vec3(x + quadWidth, y + quadHeight, -distance), - glm::vec3(x + quadWidth, y, -distance), - glm::vec3(x, y, -distance), - glm::vec2(0.0f, 1.0f), glm::vec2(1.0f, 1.0f), - glm::vec2(1.0f, 0.0f), glm::vec2(0.0f, 0.0f), - overlayColor); - }); - - if (!_crosshairTexture) { - _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + - "images/sixense-reticle.png"); - } - - //draw the mouse pointer - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); - glm::vec2 canvasSize = qApp->getCanvasSize(); - const float reticleSize = 40.0f / canvasSize.x * quadWidth; - x -= reticleSize / 2.0f; - y += reticleSize / 2.0f; - const float mouseX = (qApp->getMouseX() / (float)canvasSize.x) * quadWidth; - const float mouseY = (1.0 - (qApp->getMouseY() / (float)canvasSize.y)) * quadHeight; - - glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; - - DependencyManager::get()->renderQuad(glm::vec3(x + mouseX, y + mouseY, -distance), - glm::vec3(x + mouseX + reticleSize, y + mouseY, -distance), - glm::vec3(x + mouseX + reticleSize, y + mouseY - reticleSize, -distance), - glm::vec3(x + mouseX, y + mouseY - reticleSize, -distance), - glm::vec2(0.0f, 0.0f), glm::vec2(1.0f, 0.0f), - glm::vec2(1.0f, 1.0f), glm::vec2(0.0f, 1.0f), - reticleColor, _reticleQuad); - - glEnable(GL_DEPTH_TEST); - - glPopMatrix(); - - glDepthMask(GL_TRUE); - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); - - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - glEnable(GL_LIGHTING); -} -*/ void ApplicationOverlay::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const { cursorPos *= qApp->getCanvasSize(); @@ -556,22 +453,6 @@ void ApplicationOverlay::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origi direction = glm::normalize(intersectionWithUi - origin); } -glm::vec2 getPolarCoordinates(const PalmData& palm) { - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - auto avatarOrientation = myAvatar->getOrientation(); - auto eyePos = myAvatar->getDefaultEyePosition(); - glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm); - // Direction of the tip relative to the eye - glm::vec3 tipDirection = tip - eyePos; - // orient into avatar space - tipDirection = glm::inverse(avatarOrientation) * tipDirection; - // Normalize for trig functions - tipDirection = glm::normalize(tipDirection); - // Convert to polar coordinates - glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y)); - return polar; -} - //Caculate the click location using one of the sixense controllers. Scale is not applied QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const { QPoint rv; @@ -624,7 +505,7 @@ bool ApplicationOverlay::calculateRayUICollisionPoint(const glm::vec3& position, void ApplicationOverlay::renderPointers() { //lazily load crosshair texture if (_crosshairTexture == 0) { - _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png"); + _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/arrow.png"); } glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); @@ -790,43 +671,6 @@ void ApplicationOverlay::renderControllerPointers() { } } -void ApplicationOverlay::renderPointersOculus() { - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_TEXTURE_2D); - - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); - glDisable(GL_DEPTH_TEST); - - glMatrixMode(GL_MODELVIEW); - - //Controller Pointers - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) { - PalmData& palm = myAvatar->getHand()->getPalms()[i]; - if (palm.isActive()) { - glm::vec2 polar = getPolarCoordinates(palm); - // Convert to quaternion - glm::quat orientation = glm::quat(glm::vec3(polar.y, -polar.x, 0.0f)); - // Render reticle at location - renderReticle(orientation, _alpha); - } - } - - //Mouse Pointer - if (_reticleActive[MOUSE]) { - glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(), - _reticlePosition[MOUSE].y())); - glm::quat orientation(glm::vec3(-projection.y, projection.x, 0.0f)); - renderReticle(orientation, _alpha); - } - - glEnable(GL_DEPTH_TEST); - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); -} - //Renders a small magnification of the currently bound texture at the coordinates void ApplicationOverlay::renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder) { if (!_magnifier) { @@ -1111,7 +955,7 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder() { void ApplicationOverlay::buildHemiVertices( const float fov, const float aspectRatio, const int slices, const int stacks) { static float textureFOV = 0.0f, textureAspectRatio = 1.0f; - if (_hemiVerticesID != GeometryCache::UNKNOWN_ID && textureFOV == fov && textureAspectRatio == aspectRatio) { + if (textureFOV == fov && textureAspectRatio == aspectRatio) { return; } @@ -1119,9 +963,6 @@ void ApplicationOverlay::buildHemiVertices( textureAspectRatio = aspectRatio; auto geometryCache = DependencyManager::get(); - if (_hemiVerticesID == GeometryCache::UNKNOWN_ID) { - _hemiVerticesID = geometryCache->allocateID(); - } _hemiVertices = gpu::BufferPointer(new gpu::Buffer()); _hemiIndices = gpu::BufferPointer(new gpu::Buffer()); @@ -1187,8 +1028,8 @@ void ApplicationOverlay::drawSphereSection(gpu::Batch& batch) { static const int COLOR_DATA_SLOT = 2; gpu::Stream::FormatPointer streamFormat(new gpu::Stream::Format()); // 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), sizeof(vec3)); - streamFormat->setAttribute(gpu::Stream::COLOR, COLOR_DATA_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA), sizeof(vec3) + sizeof(vec2)); + 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); @@ -1230,35 +1071,6 @@ void ApplicationOverlay::buildFramebufferObject() { glBindTexture(GL_TEXTURE_2D, 0); } -/* -//Renders a hemisphere with texture coordinates. -void ApplicationOverlay::TexturedHemisphere::render() { - if (_vbo.first == 0 || _vbo.second == 0) { - qDebug() << "TexturedHemisphere::render(): Incorrect initialisation"; - return; - } - - glBindBuffer(GL_ARRAY_BUFFER, _vbo.first); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo.second); - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - static const int STRIDE = sizeof(TextureVertex); - static const void* VERTEX_POINTER = 0; - static const void* TEX_COORD_POINTER = (void*)sizeof(glm::vec3); - glVertexPointer(3, GL_FLOAT, STRIDE, VERTEX_POINTER); - glTexCoordPointer(2, GL_FLOAT, STRIDE, TEX_COORD_POINTER); - - glDrawRangeElements(GL_TRIANGLES, 0, _vertices - 1, _indices, GL_UNSIGNED_SHORT, 0); - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); -} -*/ glm::vec2 ApplicationOverlay::directionToSpherical(const glm::vec3& direction) { glm::vec2 result; // Compute yaw diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index d78ad4bc9f..609bef745d 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -72,7 +72,7 @@ private: float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE; QOpenGLFramebufferObject* _framebufferObject; - void renderReticle(glm::quat orientation, float alpha); + void renderReticle(gpu::Batch& batch, glm::quat orientation, float alpha); void renderPointers(); void renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder); From d3cdbc389a4c7d3fda287ebe4ac440b0ccb4e051 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 11 Jun 2015 15:05:08 -0700 Subject: [PATCH 21/94] Code cleanup --- interface/src/devices/OculusManager.cpp | 24 ------------------------ interface/src/ui/ApplicationOverlay.cpp | 21 +-------------------- 2 files changed, 1 insertion(+), 44 deletions(-) diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 2ee62c85f3..414c7f6199 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -629,35 +629,11 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const finalFbo = DependencyManager::get()->getPrimaryFramebuffer(); glBindFramebuffer(GL_FRAMEBUFFER, 0); } - - //glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(finalFbo)); - ////Render each eye into an fbo - //for_each_eye(_ovrHmd, [&](ovrEyeType eye) { - // _activeEye = eye; - // // Update our camera to what the application camera is doing - // _camera->setRotation(toGlm(eyeRenderPose[eye].Orientation)); - // _camera->setPosition(toGlm(eyeRenderPose[eye].Position)); - // configureCamera(*_camera); - // glMatrixMode(GL_PROJECTION); - // glLoadMatrixf(glm::value_ptr(_camera->getProjection())); - - // glMatrixMode(GL_MODELVIEW); - // glLoadIdentity(); - - // ovrRecti & vp = _eyeTextures[eye].Header.RenderViewport; - // vp.Size.h = _recommendedTexSize.h * _offscreenRenderScale; - // vp.Size.w = _recommendedTexSize.w * _offscreenRenderScale; - - // glViewport(vp.Pos.x, vp.Pos.y, vp.Size.w, vp.Size.h); - //}); - //glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); - // restore our normal viewport glViewport(0, 0, deviceSize.width(), deviceSize.height()); diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index e5f701e807..887af89cd0 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -242,24 +242,6 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { _framebufferObject->release(); } -// A quick and dirty solution for compositing the old overlay -// texture with the new one -//template -//void with_each_texture(GLuint firstPassTexture, GLuint secondPassTexture, F f) { -// glEnable(GL_TEXTURE_2D); -// glActiveTexture(GL_TEXTURE0); -// if (firstPassTexture) { -// glBindTexture(GL_TEXTURE_2D, firstPassTexture); -// f(); -// } -// //if (secondPassTexture) { -// // glBindTexture(GL_TEXTURE_2D, secondPassTexture); -// // f(); -// //} -// glBindTexture(GL_TEXTURE_2D, 0); -// glDisable(GL_TEXTURE_2D); -//} - gpu::PipelinePointer ApplicationOverlay::getDrawPipeline() { if (!_standardDrawPipeline) { auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(standardTransformPNTC_vert))); @@ -977,7 +959,6 @@ void ApplicationOverlay::buildHemiVertices( vec3 pos; // Compute vertices positions and texture UV coordinate // Create and write to buffer - //_hemiVertices->(sizeof(vec3) + sizeof(vec2) + sizeof(vec4)) * stacks * slices); for (int i = 0; i < stacks; i++) { float stacksRatio = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f // abs(theta) <= fov / 2.0f @@ -1052,7 +1033,7 @@ void ApplicationOverlay::buildFramebufferObject() { auto canvasSize = qApp->getCanvasSize(); QSize fboSize = QSize(canvasSize.x, canvasSize.y); if (_framebufferObject != NULL && fboSize == _framebufferObject->size()) { - // Already build + // Already built return; } From 92f22bc6d52bde359f7b6fc8baa0678e139ba311 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 11 Jun 2015 15:40:16 -0700 Subject: [PATCH 22/94] Magic number and build errors --- interface/src/ui/ApplicationOverlay.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 887af89cd0..f6dd4f5773 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -260,6 +260,8 @@ gpu::PipelinePointer ApplicationOverlay::getDrawPipeline() { return _standardDrawPipeline; } +#define CURSOR_PIXEL_SIZE 32.0f + // Draws the FBO texture for the screen void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) { if (_alpha == 0.0f) { @@ -296,7 +298,7 @@ void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) { mousePosition -= 1.0f; mousePosition.y *= -1.0f; model.setTranslation(vec3(mousePosition, 0)); - glm::vec2 mouseSize = 32.0f / canvasSize; + glm::vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize; model.setScale(vec3(mouseSize, 1.0f)); batch.setModelTransform(model); batch.setUniformTexture(0, _crosshairTexture); @@ -957,20 +959,22 @@ void ApplicationOverlay::buildHemiVertices( //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++) { - float stacksRatio = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f + 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 * (stacksRatio - 0.5f); + float pitch = -fov * (uv.y - 0.5f); for (int j = 0; j < slices; j++) { - float slicesRatio = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f + 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 * (slicesRatio - 0.5f); + 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*)&vec2(slicesRatio, stacksRatio)); - _hemiVertices->append(sizeof(vec4), (gpu::Byte*)&vec4(1)); + _hemiVertices->append(sizeof(vec2), (gpu::Byte*)&uv); + _hemiVertices->append(sizeof(vec4), (gpu::Byte*)&color); } } From e6bafb9bf1a202415540f955fcea739abc108738 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 11 Jun 2015 16:19:54 -0700 Subject: [PATCH 23/94] Draggable toolBar.js, with persistence, and update some scripts for it. --- examples/controllers/hydra/gun.js | 23 ++++++---- examples/dice.js | 24 +++++++---- examples/edit.js | 11 +++++ examples/libraries/toolBars.js | 70 +++++++++++++++++++++++++++++-- examples/pointer.js | 16 +++++-- 5 files changed, 119 insertions(+), 25 deletions(-) diff --git a/examples/controllers/hydra/gun.js b/examples/controllers/hydra/gun.js index 146f9daca3..7d024e2fd3 100644 --- a/examples/controllers/hydra/gun.js +++ b/examples/controllers/hydra/gun.js @@ -99,6 +99,13 @@ var NUM_BUTTONS = 3; var screenSize = Controller.getViewportDimensions(); var startX = screenSize.x / 2 - (NUM_BUTTONS * (BUTTON_SIZE + PADDING)) / 2; +Script.include(["../../libraries/toolBars.js"]); +const persistKey = "highfidelity.gun.toolbar.position"; +var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); +toolBar.save = function () { + Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y])); +}; +var old = JSON.parse(Settings.getValue(persistKey) || '0'); var reticle = Overlays.addOverlay("image", { x: screenSize.x / 2 - (BUTTON_SIZE / 2), y: screenSize.y / 2 - (BUTTON_SIZE / 2), @@ -108,9 +115,9 @@ var reticle = Overlays.addOverlay("image", { alpha: 1 }); -var offButton = Overlays.addOverlay("image", { - x: startX, - y: screenSize.y - (BUTTON_SIZE + PADDING), +var offButton = toolBar.addOverlay("image", { + x: old ? old[0] : startX, + y: old ? old[1] : (screenSize.y - (BUTTON_SIZE + PADDING)), width: BUTTON_SIZE, height: BUTTON_SIZE, imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg", @@ -118,7 +125,7 @@ var offButton = Overlays.addOverlay("image", { }); startX += BUTTON_SIZE + PADDING; -var platformButton = Overlays.addOverlay("image", { +var platformButton = toolBar.addOverlay("image", { x: startX, y: screenSize.y - (BUTTON_SIZE + PADDING), width: BUTTON_SIZE, @@ -128,7 +135,7 @@ var platformButton = Overlays.addOverlay("image", { }); startX += BUTTON_SIZE + PADDING; -var gridButton = Overlays.addOverlay("image", { +var gridButton = toolBar.addOverlay("image", { x: startX, y: screenSize.y - (BUTTON_SIZE + PADDING), width: BUTTON_SIZE, @@ -493,10 +500,8 @@ function mousePressEvent(event) { } function scriptEnding() { - Overlays.deleteOverlay(reticle); - Overlays.deleteOverlay(offButton); - Overlays.deleteOverlay(platformButton); - Overlays.deleteOverlay(gridButton); + Overlays.deleteOverlay(reticle); + toolBar.cleanup(); Overlays.deleteOverlay(pointer[0]); Overlays.deleteOverlay(pointer[1]); Overlays.deleteOverlay(text); diff --git a/examples/dice.js b/examples/dice.js index 515019e740..ac5d1b7426 100644 --- a/examples/dice.js +++ b/examples/dice.js @@ -3,6 +3,7 @@ // examples // // Created by Philip Rosedale on February 2, 2015 +// Persist toolbar by HRS 6/11/15. // Copyright 2015 High Fidelity, Inc. // // Press the dice button to throw some dice from the center of the screen. @@ -31,9 +32,16 @@ var screenSize = Controller.getViewportDimensions(); var BUTTON_SIZE = 32; var PADDING = 3; -var offButton = Overlays.addOverlay("image", { - x: screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING, - y: screenSize.y - (BUTTON_SIZE + PADDING), +Script.include(["libraries/toolBars.js"]); +var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); +const persistKey = "highfidelity.dice.toolbar.position"; +toolBar.save = function () { + Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y])); +}; +var old = JSON.parse(Settings.getValue(persistKey) || '0'); +var offButton = toolBar.addOverlay("image", { + x: old ? old[0] : (screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING), + y: old ? old[1] : (screenSize.y - (BUTTON_SIZE + PADDING)), width: BUTTON_SIZE, height: BUTTON_SIZE, imageURL: HIFI_PUBLIC_BUCKET + "images/close.png", @@ -45,7 +53,7 @@ var offButton = Overlays.addOverlay("image", { alpha: 1 }); -var deleteButton = Overlays.addOverlay("image", { +var deleteButton = toolBar.addOverlay("image", { x: screenSize.x / 2 - BUTTON_SIZE, y: screenSize.y - (BUTTON_SIZE + PADDING), width: BUTTON_SIZE, @@ -59,7 +67,7 @@ var deleteButton = Overlays.addOverlay("image", { alpha: 1 }); -var diceButton = Overlays.addOverlay("image", { +var diceButton = toolBar.addOverlay("image", { x: screenSize.x / 2 + PADDING, y: screenSize.y - (BUTTON_SIZE + PADDING), width: BUTTON_SIZE, @@ -140,10 +148,8 @@ function mousePressEvent(event) { } function scriptEnding() { - Overlays.deleteOverlay(offButton); - Overlays.deleteOverlay(diceButton); - Overlays.deleteOverlay(deleteButton); + toolBar.cleanup(); } Controller.mousePressEvent.connect(mousePressEvent); -Script.scriptEnding.connect(scriptEnding); \ No newline at end of file +Script.scriptEnding.connect(scriptEnding); diff --git a/examples/edit.js b/examples/edit.js index 6055400289..2974397cde 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -2,6 +2,7 @@ // examples // // Created by Brad Hefta-Gaub on 10/2/14. +// Persist toolbar by HRS 6/11/15. // Copyright 2014 High Fidelity, Inc. // // This script allows you to edit entities with a new UI/UX for mouse and trackpad based editing @@ -320,6 +321,7 @@ var toolBar = (function () { } } + const persistKey = "highfidelity.edit.toolbar.position"; that.move = function () { var newViewPort, toolsX, @@ -330,6 +332,15 @@ var toolBar = (function () { if (toolBar === undefined) { initialize(); + toolBar.save = function () { + Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y])); + }; + var old = JSON.parse(Settings.getValue(persistKey) || '0'); + if (old) { + windowDimensions = newViewPort; + toolBar.move(old[0], old[1]); + return; + } } else if (windowDimensions.x === newViewPort.x && windowDimensions.y === newViewPort.y) { return; diff --git a/examples/libraries/toolBars.js b/examples/libraries/toolBars.js index 670a69dec7..94bc1c8af0 100644 --- a/examples/libraries/toolBars.js +++ b/examples/libraries/toolBars.js @@ -236,6 +236,7 @@ ToolBar = function(x, y, direction) { y: y - ToolBar.SPACING }); } + this.save(); } this.setAlpha = function(alpha, tool) { @@ -313,9 +314,8 @@ ToolBar = function(x, y, direction) { this.cleanup = function() { for(var tool in this.tools) { this.tools[tool].cleanup(); - delete this.tools[tool]; } - + if (this.back != null) { Overlays.deleteOverlay(this.back); this.back = null; @@ -327,7 +327,71 @@ ToolBar = function(x, y, direction) { this.width = 0; this.height = 0; } + + var that = this; + this.contains = function (xOrPoint, optionalY) { + var x = (optionalY === undefined) ? xOrPoint.x : xOrPoint, + y = (optionalY === undefined) ? xOrPoint.y : optionalY; + return (that.x <= x) && (x <= (that.x + that.width)) && + (that.y <= y) && (y <= (that.y + that.height)); + } + that.hover = function (enable) { + that.isHovering = enable; + if (that.back) { + if (enable) { + that.oldAlpha = Overlays.getProperty(that.back, 'backgroundAlpha'); + } + Overlays.editOverlay(this.back, { + visible: enable, + backgroundAlpha: enable ? 0.5 : that.oldAlpha + }); + } + }; + // These are currently only doing that which is necessary for toolbar hover and toolbar drag. + // They have not yet been extended to tool hover/click/release, etc. + this.mousePressEvent = function (event) { + if (!that.contains(event)) { + that.mightBeDragging = false; + return; + } + that.mightBeDragging = true; + that.dragOffsetX = that.x - event.x; + that.dragOffsetY = that.y - event.y; + }; + this.mouseMove = function (event) { + if (!that.mightBeDragging || !event.isLeftButton) { + that.mightBeDragging = false; + if (!that.contains(event)) { + if (that.isHovering) { + that.hover(false); + } + return; + } + if (!that.isHovering) { + that.hover(true); + } + return; + } + that.move(that.dragOffsetX + event.x, that.dragOffsetY + event.y); + }; + Controller.mousePressEvent.connect(this.mousePressEvent); + Controller.mouseMoveEvent.connect(this.mouseMove); + // Called on move. A different approach would be to have all this on the prototype, + // and let apps extend where needed. Ex. app defines its toolbar.move() to call this.__proto__.move and then save. + this.save = function () { }; + // This compatability hack breaks the model, but makes converting existing scripts easier: + this.addOverlay = function (ignored, oldSchoolProperties) { + var properties = JSON.parse(JSON.stringify(oldSchoolProperties)); // a copy + if (that.numberOfTools() === 0) { + that.move(properties.x, properties.y); + } + delete properties.x; + delete properties.y; + var index = that.addTool(properties); + var id = that.tools[index].overlay(); + return id; + } } ToolBar.SPACING = 4; ToolBar.VERTICAL = 0; -ToolBar.HORIZONTAL = 1; \ No newline at end of file +ToolBar.HORIZONTAL = 1; diff --git a/examples/pointer.js b/examples/pointer.js index cca46709ee..ea6b0c233f 100644 --- a/examples/pointer.js +++ b/examples/pointer.js @@ -3,6 +3,7 @@ // // Created by Seth Alves on May 15th // Modified by Eric Levin on June 4 +// Persist toolbar by HRS 6/11/15. // Copyright 2015 High Fidelity, Inc. // // Provides a pointer with option to draw on surfaces @@ -31,9 +32,16 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; var screenSize = Controller.getViewportDimensions(); var userCanPoint = false; -var pointerButton = Overlays.addOverlay("image", { - x: screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING, - y: screenSize.y - (BUTTON_SIZE + PADDING), +Script.include(["libraries/toolBars.js"]); +const persistKey = "highfidelity.pointer.toolbar.position"; +var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); +toolBar.save = function () { + Settings.setValue(persistKey, JSON.stringify([toolBar.x, toolBar.y])); +}; +var old = JSON.parse(Settings.getValue(persistKey) || '0'); +var pointerButton = toolBar.addOverlay("image", { + x: old ? old[0] : screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING, + y: old ? old[1] : screenSize.y - (BUTTON_SIZE + PADDING), width: BUTTON_SIZE, height: BUTTON_SIZE, imageURL: HIFI_PUBLIC_BUCKET + "images/laser.png", @@ -150,4 +158,4 @@ Script.scriptEnding.connect(cleanup); Controller.mouseMoveEvent.connect(mouseMoveEvent); Controller.mousePressEvent.connect(mousePressEvent); -Controller.mouseReleaseEvent.connect(mouseReleaseEvent); \ No newline at end of file +Controller.mouseReleaseEvent.connect(mouseReleaseEvent); From 3cb4ee183e615a2fcf9192c5a1802cac70cbc100 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 11 Jun 2015 16:24:22 -0700 Subject: [PATCH 24/94] Update header. --- examples/libraries/toolBars.js | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/libraries/toolBars.js b/examples/libraries/toolBars.js index 94bc1c8af0..6e72795ea5 100644 --- a/examples/libraries/toolBars.js +++ b/examples/libraries/toolBars.js @@ -3,6 +3,7 @@ // examples // // Created by Clément Brisset on 5/7/14. +// Persistable drag position by HRS 6/11/15. // Copyright 2014 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. From 09cfe004a18ab8c9b5622eb6d33dc22a41b7cf36 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 11 Jun 2015 16:55:23 -0700 Subject: [PATCH 25/94] Combining with new cursor render code --- interface/src/ui/ApplicationOverlay.cpp | 16 ++++++++-------- libraries/ui/src/CursorManager.cpp | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 971958ae61..ba0d3a60a9 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -210,7 +210,7 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { overlays.renderHUD(renderArgs); - //renderPointers(); + renderPointers(); renderDomainConnectionStatusBorder(); if (_newUiTexture) { @@ -268,7 +268,7 @@ void ApplicationOverlay::bindCursorTexture(gpu::Batch& batch, uint8_t cursorInde _cursors[iconId] = DependencyManager::get()-> getImageTexture(iconPath); } - glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_cursors[iconId])); + batch.setUniformTexture(0, _cursors[iconId]); } #define CURSOR_PIXEL_SIZE 32.0f @@ -471,11 +471,11 @@ bool ApplicationOverlay::calculateRayUICollisionPoint(const glm::vec3& position, //Renders optional pointers void ApplicationOverlay::renderPointers() { - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //glEnable(GL_TEXTURE_2D); + //glEnable(GL_BLEND); + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glActiveTexture(GL_TEXTURE0); + //glActiveTexture(GL_TEXTURE0); //bindCursorTexture(); if (qApp->isHMDMode() && !qApp->getLastMouseMoveWasSimulated() && !qApp->isMouseHidden()) { @@ -521,8 +521,8 @@ void ApplicationOverlay::renderPointers() { _magActive[MOUSE] = false; renderControllerPointers(); } - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); + //glBindTexture(GL_TEXTURE_2D, 0); + //glDisable(GL_TEXTURE_2D); } void ApplicationOverlay::renderControllerPointers() { diff --git a/libraries/ui/src/CursorManager.cpp b/libraries/ui/src/CursorManager.cpp index efadd09142..38c746994d 100644 --- a/libraries/ui/src/CursorManager.cpp +++ b/libraries/ui/src/CursorManager.cpp @@ -49,7 +49,7 @@ namespace Cursor { static uint16_t _customIconId = Icon::USER_BASE; Manager::Manager() { - ICONS[Icon::DEFAULT] = PathUtils::resourcesPath() + "images/sixense-reticle.png"; + ICONS[Icon::DEFAULT] = PathUtils::resourcesPath() + "images/arrow.png"; ICONS[Icon::LINK] = PathUtils::resourcesPath() + "images/reticleLink.png"; } From 81460e48f46115ff1f3fb3b22c711e2a2126cb6c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 11 Jun 2015 17:25:25 -0700 Subject: [PATCH 26/94] fix for sticking windows audio-mixer --- assignment-client/src/AssignmentClient.cpp | 3 +++ .../src/AssignmentClientMonitor.cpp | 24 +++++++++---------- assignment-client/src/audio/AudioMixer.cpp | 2 ++ 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index cfee18941c..e125a44783 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -66,6 +66,9 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri // set the logging target to the the CHILD_TARGET_NAME LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); + // make sure we output process IDs for a child AC otherwise it's insane to parse + LogHandler::getInstance().setShouldOutputPID(true); + // setup our _requestAssignment member variable from the passed arguments _requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, assignmentPool); diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index d19eb90df6..8c6478b59f 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -39,9 +39,9 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen _walletUUID(walletUUID), _assignmentServerHostname(assignmentServerHostname), _assignmentServerPort(assignmentServerPort) -{ +{ qDebug() << "_requestAssignmentType =" << _requestAssignmentType; - + // start the Logging class with the parent's target name LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME); @@ -77,13 +77,13 @@ void AssignmentClientMonitor::simultaneousWaitOnChildren(int waitMsecs) { while(_childProcesses.size() > 0 && !waitTimer.hasExpired(waitMsecs)) { // continue processing events so we can handle a process finishing up QCoreApplication::processEvents(); - } + } } void AssignmentClientMonitor::childProcessFinished() { QProcess* childProcess = qobject_cast(sender()); qint64 processID = _childProcesses.key(childProcess); - + if (processID > 0) { qDebug() << "Child process" << processID << "has finished. Removing from internal map."; _childProcesses.remove(processID); @@ -98,17 +98,17 @@ void AssignmentClientMonitor::stopChildProcesses() { qDebug() << "Attempting to terminate child process" << childProcess->processId(); childProcess->terminate(); } - + simultaneousWaitOnChildren(WAIT_FOR_CHILD_MSECS); - + if (_childProcesses.size() > 0) { // ask even more firmly foreach(QProcess* childProcess, _childProcesses) { qDebug() << "Attempting to kill child process" << childProcess->processId(); childProcess->kill(); } - - simultaneousWaitOnChildren(WAIT_FOR_CHILD_MSECS); + + simultaneousWaitOnChildren(WAIT_FOR_CHILD_MSECS); } } @@ -122,7 +122,7 @@ void AssignmentClientMonitor::aboutToQuit() { void AssignmentClientMonitor::spawnChildClient() { QProcess* assignmentClient = new QProcess(this); - + // unparse the parts of the command-line that the child cares about QStringList _childArguments; if (_assignmentPool != "") { @@ -153,7 +153,7 @@ void AssignmentClientMonitor::spawnChildClient() { // make sure that the output from the child process appears in our output assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels); - + assignmentClient->start(QCoreApplication::applicationFilePath(), _childArguments); // make sure we hear that this process has finished when it does @@ -194,7 +194,7 @@ void AssignmentClientMonitor::checkSpares() { qDebug() << "asking child" << aSpareId << "to exit."; SharedNodePointer childNode = nodeList->nodeWithUUID(aSpareId); childNode->activateLocalSocket(); - + QByteArray diePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeStopNode); nodeList->writeUnverifiedDatagram(diePacket, childNode); } @@ -239,7 +239,7 @@ void AssignmentClientMonitor::readPendingDatagrams() { // update our records about how to reach this child matchingNode->setLocalSocket(senderSockAddr); - QVariantMap packetVariantMap = + QVariantMap packetVariantMap = JSONBreakableMarshal::fromStringBuffer(receivedPacket.mid(numBytesForPacketHeader(receivedPacket))); QJsonObject unpackedStatsJSON = QJsonObject::fromVariantMap(packetVariantMap); diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index ea7ac0648b..c3ca6796bc 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -850,7 +850,9 @@ void AudioMixer::run() { ++_numStatFrames; + // since we're a while loop we need to help Qt's event processing QCoreApplication::processEvents(); + QCoreApplication::sendPostedEvents(this, 0); if (_isFinished) { break; From 27f40ea8817e7f274c2b97a56cac71823fc4c711 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 11 Jun 2015 20:33:30 -0700 Subject: [PATCH 27/94] Signal Audio.disconnected, and use new hello/goodbye sounds in resources. --- examples/dialTone.js | 10 ++++++++-- interface/resources/sounds/goodbye.wav | Bin 0 -> 62044 bytes interface/resources/sounds/hello.wav | Bin 0 -> 61324 bytes interface/src/Application.cpp | 2 ++ libraries/audio-client/src/AudioClient.cpp | 1 + libraries/audio-client/src/AudioClient.h | 1 + .../script-engine/src/AudioScriptingInterface.h | 1 + 7 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 interface/resources/sounds/goodbye.wav create mode 100644 interface/resources/sounds/hello.wav diff --git a/examples/dialTone.js b/examples/dialTone.js index 135acb17f4..b55e654f25 100644 --- a/examples/dialTone.js +++ b/examples/dialTone.js @@ -3,6 +3,7 @@ // examples // // Created by Stephen Birarda on 06/08/15. +// Added disconnect HRS 6/11/15. // Copyright 2015 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. @@ -10,14 +11,19 @@ // // setup the local sound we're going to use -var connectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/short1.wav"); +var connectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/hello.wav"); +var disconnectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/goodbye.wav"); // setup the options needed for that sound var connectSoundOptions = { localOnly: true -} +}; // play the sound locally once we get the first audio packet from a mixer Audio.receivedFirstPacket.connect(function(){ Audio.playSound(connectSound, connectSoundOptions); }); + +Audio.disconnected.connect(function(){ + Audio.playSound(disconnectSound, connectSoundOptions); +}); diff --git a/interface/resources/sounds/goodbye.wav b/interface/resources/sounds/goodbye.wav new file mode 100644 index 0000000000000000000000000000000000000000..bd6c51a5c0b6de4d407528d6a24ca8da558c11e6 GIT binary patch literal 62044 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 Qqk(kI~7|322#O~g^->3kcv5x zlo=6^Y4IK@@dkFSHGhv>A`I7WcFY540S&v;em? zA2&7YBQctS>za%h{fw&`jTnu=6%9v>hT*Craa}_Ytsiks18`G=5v~5XrGdDm-iXma z+*e=RQ6Jn>e>_lcJXU`^RbM>UK)g^Nyw;G9HBLkDUIPFO0r4XeU>M?K7?NT*l4BH7 zVN8I7?TEwLS~upJ$- z2iRkLB!{2B;(&m%V=cf z4dmo=6yOsS<9n2193q&I5lq6GOwIbt%ErvbRxHSNEW)lV#a=AWK@8^ze!(%U$;qtC z8T^`a*_2Dzj4SvZ*Ru^b@(1o=C;q`sJkG8>%p01?ILf2=le@8#8?cc}v4XR(gp)Cok(kPU7|HhdiOtZH zbUcqN5_wQr+;i+M=EsqmpW&qAH@eN}`zZBDb<2r;;L*5+JqSD&>DJINef0T~`8~ zQJ`a@_Q`3Npx@=RPSj$>(;|JMUlpS1N~9@Drin_U2}-BY%KniNDypF>qERZRNR`(p z)zV1S)o3-*SbeXF>ZHjUpkFjZvoucgG*e47U#qlM>$OFjwO@O5O8fPXPJMiP{O3rH z(?cc3Go{A6|NeKN7!solvY;w*p%x0GDJq~XBG45LF#wG*9PKdyJum}&hw(Q~;tH-H8rN_a&+q`x@EnGhaD0uVe2>(OLuSSyH^KY_i;yf$ zupGgP^r%9zD&L_N4Yl|$>hm?~GZqc`2w(9r8Zib9c?*qr0gZVc4R{=1@h{ZjKGfk> ze90a7l1ovED-q5aD92w>lH*a7kto0)k(*tSl^v0eEs&Ir5yDyktKls^#S4_gUF5|r zWW{-;LKH%90K^}7uix=pYj8*N5UpSFwvlE`doVzp=}D&MwQoU zmC-Vl&^(pW|5R90Ra}!*Kx36p!<1i<%B=y)r~b;WUdpYm%AqdGskX|ccFL*N%AuCZ zspiV6rplv6%BIH3s|L!cuarl1lt*aK6pO|8{e zEi_2q>nDA$k?N@NYOhJ^r5Wm~x$3WZ>Z9cvqE#BIO&XzX8mGOQpaYt$6Pm6w`k&5e zkz%w&H?>mFv{uixQ6IEjrvJ>)`;-*Nl@7<18RwM;S5yemDuM?J!+ll2OI5=g)r6@L z;-MiD<2xin8)QT$WJXWqMsF0tU=+kp_za^^4r5Ujlkf$mpblo>Ys^9u%s~sxMH?(e zJ1jzH{Dz(%S&2bdhCx_~NG!upti~8D$7n3W1pI~xSd2+ngvnThDVT+6n1@N2jA@vP zsThw*7=dXRiisGEpV1rR(H-N^9>dWRqtF}!Q3r!j6Mazu-B1o)P#E7K7g`|`nji`4 zApvURUqv8R6%eChxT*p;uAJDfbl9%sSSezGyr$}rM(dt_)IaK`v-&}Qs*$#%8UD3Cpa*mR#HI6K%3rw%T6UQoCaR z`^X{t*$&zy+h`+ft&Oq;HrVFc5SwHJY>f4>Nb6_)th@EFe%8Ty*mu^?T3Rpr%KBSl z>u)t|u+_95t+EZZsy4>LZJbrKsaD=*Sb3Xk4JI)>)YSVd1vNBJ3}#VrQ+2 zUAJ0x$7)%uHL$nV!~!+5lxlAo)ZX%{hn3O*E3Y9|Q{%0kCRuC!Y8|!M`e}s?(>9x+ zoiW^6U#Z&dc z8}-LK^@LYn1n35bo=Aj_h=;C7g0@J6?~xd-kqC{D1kI2bb&weU^=efRAOax>gQ++i zis6Iu;9q6O8)d>{CBYLV#%)PA^iEgxTz~7aPV1(Q>7w@Qtaj+AwramtYP*)}cg@vG z&D26o)Ete`Bn{Sh4bV_^)L^w&Pc>5)HBxKU)VHdn#tK&*6;pK;QDtRU1!dCz0R#La ACIA2c literal 0 HcmV?d00001 diff --git a/interface/resources/sounds/hello.wav b/interface/resources/sounds/hello.wav new file mode 100644 index 0000000000000000000000000000000000000000..6269dab5db21a869759dfe96cde18449729ebc87 GIT binary patch literal 61324 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≀E{LgyR2Os(I_{2}fkA60O z_Vd#Er5VdsVM1GA*3u|1g2{=t%V94pzWtZSFBsa?yqb|-t=WBkKjC7TyFYzaKImGIFv#1Gp730)T?cYTq=4Mj0G1r^Fb4GVM)7VBax z(OFogW3X6YMcNjBX+zA{3YewEF-@~#yr#rh6%5ty9H>wEyWU_|J<8U)gUxjb zYwApf=r9)7Q0CFr%&N7SKua@@=HVBYicu~$@49b##XZ-f?v8GAXLOZ2sB_##9p_f) zAU9LHxJlZ=g=sC;X)z;P0ww0E$?X`&QrA6!j&2PtQ zK|4na+7(*BZq;CWNDJ9(TE;%mQud36Si>4NHS5^CY;Mc5jcv%^?QiUFhj65w#wm6+ z7uo|{YcFz_eZf;Uz`HgXUf5i)wjAQS#>nV?LkTwmRox7MMds-OS+6VQKiw|ZbiaJivjSe0w7etv z87U!rE{*v~dhoLhqm$`$vXnt)8)KTIjAgDfo_Wle<^$sz%UC8JVwhBjX|h74AXG}> zyHvtQsf`cP2$9kpPoy*MN=ICgo;WYPa9IApUKxmu5{8vB2n%H}X2~G@DMK+t2Hahl~zbB4G=?W<2$P0ElT4Ff^if1aTZx|1nIB?iLe@R z@Gr<2_|6%4%Mpm+VBBUXuJbn>XFD8YLu_GVY+z+9XI0E)Da>Fn#;_2EF*o`$2f8y8 zIx;<4F&!E+CF(FasxmPuF$qdA0g5vb3NRjWG6AwO9x^Z?(l9PkGbxfX9+EH_5-<_s zF&W}9Ibt&v;xR4a{3I*lFf-ya7veBC5-~U8u_%(WFcPygQnNTxvog}L95S#bva<$q zvH|k50Sd7>g4q(K*&b!t0ae%y)z}kt*awX`08KdnZ8!?;`6s$^3V!D_^y54XuNk^|%vPCa=E+<$MaG&i8E$@)KBlR3 zH5H_l36=&XgH$kyq`0xjZQdi5d4xpfB78ZB&$1bhWeKjzOq`OD*eU(7O1faaw8BKG zj{#BzJ){hpOCi*gTqrH+5G-ksPU0e!#0Md;0G=bja0GY>-*^V!xgVdn9noBaFI*LOHxt7 zrK!A=4ieK0mc(YfT>ouakM~wKUhgI_8R3 z(VX)Nn`2%sbI?m^c6kZS2G7YF@1rdCB4xIBTc&syWTbak!n{q=(_1HPy*bjxn=UoH zp;Fl!C`G+4Qow63X}yM$)T=HryowSqh2^8kC*dZO+%_rXf=MFBj3;{yWS#kr-ZT6#p*^j(t7c!ZhNN=_yso9D| zW;0@%jfi2kz{+~4Y{m!KgzvHeuVoY7$$Gq$&4`pucqSVWE?eMm=p2}%N$XP_nIlPd|crF+5N^aqeT*q5^ zfKPG@ALSW7$zufM6~4(UIQamWC{TVNruiR4d=n(`O$}X&(!CpsG!RuwJdOb};Z-{B;4Kf|PF{YuWxH{mn< zo66W_g0aeE!yJc8Gb<-zCEXSpjT$bPEp$wKN87TpoA_>i0Nnw^s0kcX9n~hSwB zue39VrMo#H1I%d|W6sMAb5Z7-tFqQyl6B^W>^E2Cn7Jlr%|*FluE-tppFB1f<(WAm zZ_EjaGW+D4*(RUNHnC=fM4OeOX0Fi86lcbZnqlIbzT%ti5^dVbXVX|dnws*)l#y4a zkUTZnH%(l*U=*j#M;tN{*lBKHgE@~CW*_F6t(a++Vx*ae!Dbw~n}KL&dZDpt ziRz{?%9s%3HKmZvfDK{+=L2T zhB91?GMt8BPCyBcLSgntVTPg*yCFY+MSiwHJ~l!w)lf4LFmnR_?@v3#<)Kj z!2}r1M3~IPn9L-Y!({l2De*Vc;9sW2T4uslX2M?Pz&_@{Y39Wl7Q}TH#2uEvW0t@x zmcu(%z<1VwvN~d-ArhexQlSMh;#cHAM-)bP6hkjmMt@YrU^KvRG{tzd!xVJEEcC$w z^uz}#1X8(X{^E}Y{hkK!9DE76CA`V9Kjo$!FQa-cU(r0TtG~@ ziTH92N#qt%$Ze#T+sG{UkxOnPx7_UiaK!~hIWm$-FvJe$zBFf5ml$1dzE`3l;IwQZdLSFd=*`x~6NnxaxyhtFa5myp| z|C`i}D89iXM&b(Z;wUd+D^FnscVi|uVicF+56(h6jzv8VMmcssA^wVVtbuqe3#<9? zQq$m`#=}|lxlf;Swch1oJ;jN-lf!fsLv<$G>S#99UJTLJETO+JpO#=c&CO(*f-x0z z?yG)wkMy0pqT%kCK5|?2s#~Vl+zdVA#^^~GswZ7HJ>Z(@L041vx)9y(3h8c_QxCXw zy3Zxl!!DK{an_x5U))*u&RufP-EH^K-EsHbGk4WJch}u_ciCBY+Qrn1F0r0+8T7o% zt^c`TJ?|>%SyxxDxR!d^_0T)6zutD^^@*FK&)o`r?Y8PycT&H*iyBKKG@icGwCXXV z#$iFt%3#gKs#=M^Xl=IE7W_@Sv!4#+pE{mXbvFOj6SB35T?mS8~h@v~-Rq$cGfjm69A>luBghc!ZX=uO?AXLOk!)P=fT zr|B}Ctc!J|j@5xWMtf^-?XKOmqqfptwVpQB>e^6CYHcm5)isM&){I(S6KW}qsU?-J zuzqw!^}WlZ5iYOZb9wcu%dY2LE)*=5iTF0-z1>2R6Xuhq~-K+~v?AE~ozC^5|ffS9`ns+TRt_UapY-;fiT5 z7p$SKwDxkPw5KbtyYC^u?pN*Y+G$_cS^K$e z+RydWfi6_T+&~@T2I~+vQb)RRI^0dwF>aQQar1S&Tc}grN}cT1>1?-IXSzMQz#Y(q z?zk>-=k*_VO;@@*y3sw>jqasxckgt!3+R4l^@xkjlP)RGxn#WLa`Lvz&U>yT!(Ayp zb5;4?)#WGGgud%YbzK-o`!S&oW(pn0j5?XwbT0GjQWn+KEUjBvRrj%`9$^!`z?OQ2 zo%8{_>0|cSHyol-{8OVjMIC2pO#H3!u~L&@gQmhx&49g{8K*TDPHA4;)PlIJ!Fa62 z@k&eJotDLSEsbwl0fuGZu^i&F91^k=Qn36_KO;TMAqxv5Gm9Y?^B^a4Bk#|sl@0}& z7P*-ag&7}t2o$7cZhmAQzF`*LXAa(BdY)x^o?r^@WKynYJT74z&L(v{qjfMpX((T4 zYer}@-qeb`rX_ho^YW0UAslDvx+gonFec)!;=WdRD=VsU+ZkiRHY2)csn_B1CY&zQ((z&+0 zF1EFFv2CGiY$si9`|DOaO845Cdcgjp=j{ePXHV!udsZLWry6D7=y&T>TmmL_>6y_L zU~X5A#a&%ic5T?mb!R&_guUHF4tEPU$^FYEZYS5eV?5}t@RWPN`|dfPx*xRvTdm*{ zAg!iE7R`iU&4(&l4E41Fe$}ezsSPkh8)Krj#vJW{<=PdSv={bjADq$u&Cwr>2RZRg8FB;L_cyrNpRV;uQaJX)g&6Oo<2~c_w|drrJvmmed(@g zq`Rb#-6g&4&g*q|UN5<`dfuJalkSWjb7ytGJFN%YIo;vT=uUT5H@ow?#huo5?vieB z=XH&{r0d)j{nuU7Rqm>;cGvY^cSl#byZWztpljSiUEv~hy?d&w+zVanp6fanr5oLQ zUGKi=7WY}VIMr<~TKBjZ-0eIbaB+Fi#pe;1j3-CMLu@b`P$XwYuALITyuVNZ5i!;BeWYm9l+Qc#>6_7NpuV|=`5zxdCaTJnOFa1 zaox(&x{a0e7;ES$*42w_uJ_ndAM!VS!yfvP{qzTiYD|pMc$lgwF-x;xspi58Eso7v z7Q3|?j%Wj%(WbbfopD#YB0>k?gAT(d9S7kY#N`4c=Re5Ab;!l-2?xVjRg1&vCsm7dicRg z_`+a3XAVR%1MV^&u2OK4A9$26xt({oju*L%M>va{IE8CDob%X^Gue&9*_s2`fWNT{ ze`P5)W+VKp+_zL?kZY36A164&o{{;sQ3|Bo^Wj7GWnQVlyUSH3nfh`ePn? zU?$pQ9Dc<})I}KTBNP?U6_wBm!Dxv>_yxI916fcWnNSMJkPpd`4{?wY@sJ)sQjiJZ z7z5v^{7Ro6`IYbao^OAia7HqMuXu;yyvJv}#7Dfwr##OGJjY1>$A>)0r#!}oJi>4u z;$t3QqbKnv4<24H+k|pqgB@o3*_|6bS zvo?IzK@b`v2AUx*+94S_ASrqw9eN=H1|b`Upa90B5XPf8=AsPdAOtH=11rz~o6#8C z(He))5y#OL7tjw^5QYaBg$PW*d(6Zq%!R{Ji2RF$vI$9L4>HODm8*Q8vi~*(A5*fIN`Ha$io%W4SEha!sDdJqed55-!i= ziM*0;~UtoGmv~-eyw3g4(P(Dayc`c>nspOX@l0)uDGPx{qO9f<;0>~`ckU&x) zh9p1$$`^=cB;N2Mp7A1XaSyI?2TpMXj&LD%b1JrQ4AycWR4GUfL54`-?18D*o+Z4gy}ek`M7~qc#bWI!a+EkM{K!{r1ApUx$Hg;eB#t>Q$;>H9X^u+!gBND`m_-Qpx-+<;{GlU}j5YGf_&LKc$KpDaFknQr`5DBBrAhHNQ$B(@^r5UnG|a zkxZtvWHf~&xydETOnQl9l1nTTM*?ExhkV5ciNbSvipO#XcjPiI$$vO42XRQYVY{ry zCiw>|WdZ({nV2QxF-b;Xm<+%m>46^d8@fmvw34Q%FLm&X)IfPDkJ3^G`6U>6BrnoP z9;A{?h$CqcQ&PYpF+L#yJ|Y&v5fc%xyb1C;zVkf3@&vx}DBf@nqPPRExCJk`4sW;` zPxud>b1}lX2oao%C!B-FoQ5Zyj)xqFC!BzX9D_$3g~uF?2ONsW9Ev9#hDRKL2oA#& zh9QDsh~!{IvOiw`d=JBGhT$E<@Roz{nf(#Pq4>%H_{<>)a1a6<3WH%#4o6&!LJ&qH z5&lF%j7KtzK`Kl_8caeqOhXpTL|)8BKKzAX%tuKqM`tVScOLtSh_OKe9g z>_8_RKvx_>D2`(QPGcy}VHB=o5^i7`?qeRpu@sS5fw$O*C~U`9?1RG*i2R3Gasi3t zHj>H%WR`Gbln=-!Z&5&eln{rq5<{v;La8oErHN#eMv_4~N8x4(o;T3ANel7$#>}?0ck5gq?4#L6DO@irLH)sBTj0_cPT9@ z<>ZSLl<$&PK1vq(AgSbyB$8JWQzFI3WBG#n@&ec80WQl;oR*U~CdaT}Hesu*!3J4? zWikbeWDKTBKTMQv7$U6^CUwzODxsB>L?g+Ds*(}qB{m9*hwSp1spJI{$Q@c-<~JN= z1orYi*63F9$A?X zsTcz(=`#`EF+N{0Ht#YfZ_wiz8Xl)`AB8&zuBXRU^thZcxqz`bk8wGJi8+OdIgRN# zmZ>?3IXQ}X_$Lc<42yFVD{%s=aWoroBAas}J8}wpawfw#mm@iklemoYxPr^MjvKh0 zd$^59c$C+Ea)obrgYWo|z$?bad!~S8M#MltBtl7~K_z5I1LQ+fl>4cEen%q=MoavO z&X|HfFdsv)6l1Xtv#}M6un%i-6q|7d2XO%>aSNAmA9wHw5qOE0c!96@h#&ZbSn>^V zB^oKkA+1 z>}N0RVOOkUTdZaSEMzS#U`0$}2~1=GjARxJW?BqjVuUglx{~b7?`*}lY|Gbd$UCgZ zd#uGXti|&T;Xzj7K9=J)mf{8$=V}IXDT{C+3vxCKaXNEx0`qY!vvU}8aR{?=0JHNC zW?~;^WjAJKH)dl;W?@HWVq0cmD`sG8=3rB1WD{m*Q)cBa%*rOr$=b}$2F$}c%*EQw z%i7G(Us!-OSeUg~gf&^5HGWc-zpyN;vOMdt3ahdj>$3)HvNr3nA?yDq1!int3S?tN#?8_zW$E6&|)f~q49L1d+!(E)n zW1Pz4oWTp6$E#exn_R)iT){}L=X-AAM{Z|;d#OCgxH!ppIK$Mq$h5e{?6}SRi2O-e zyk$9jU{yr3E|d-7p#|chEfS#%QlmT4;}2v*f8@a+6vP-5$4HdLbcA3cYGXcXVwhE0CfqEE(S{Q(G=#4Vy zj=boCoM???XpQ8k2jCYtR>oVF!ZQ}dUFO0CX2nUS#4aYn1`3waau(lmB42PY@31eg zusx5nHFvW%H#3CG7|c1$#VO3hp-jYnjKfa!wIx4k9lp{^e4<5oUGwmYrsYvh%p)4a zP5M>0YLqV5$NIP4(nWe+r|BV`rh9d~uG3MvLPzN$9ir29kWSJ8I#m1WFzv5Bw6FHm zzS>^Hw2k)FW;#e4=>V;xL$#(3*AN}4)peql)d^Zr=V)o2t7Y|XEv2ipoUYZ9x?Ria zKCPffw4$EZ5WT83^pRH6XWCFdYF!OzD>ZDTiP=Tdvb$zuA1%fKT9%`=CdX+*&eC>V zpxwA!2XUj0;x?Vkqxv^b>N4KYExf1u`9hELgI=N2+YDkPlkg4G@&|JhD9ku0#}uf_ zG-$$XXwLlT%A)AS@)*PrjA1oQW_|p{hFHRuSkKnj!j9O@t~k!F_>aADg}>t_`{O?Q z;wk&%`A>BchEEK`SB4?LFev|kpf7^Z>*pTP*!UfB(HU{j1qsm>vCt9;&=m2}2ysvw z@lYKxP!T~WjUWWWF&`Xr;v2KzD^ubFli)RD;u!@`7~p+I@jBn~8Y6g;cX*mtxQpkx zmq)mc`?;E1xPjY57J|FhUdazQ*Srjlpvoljl_RfI2;*-*oFwU*@x} z)sMPT-|JF+uZ#7S&e7L8Q=jW(eXbMqnU2v29ib6F{hC4gLs#%pueGhd*Y^5R+vz85r=Rs#{if|T zpdB=zom91h3OlN>i^lm$H%-j0nuNVH1-ofl_R;k0qgmKTb8?{O;Xp0SAzF+hv@Azx zIgZyVoS@Y?RqJw^HsvgB#`)Tgi?ltLX)pexJ-J4QaDxu!Hl4sdI)VFi7LV$Dp425g zr>l8MH}a0|;B7s~=X!*ZdXDe)B0uSEM(YDoBN@auOvDdN$N)3aLnbCdp`Y3IRH(=T z=)leh<49CyW_;IC4B|lk$u_vjnzEQFBqLukE-P>%V<4LCEbTCh#IkTH%{GY;G!RpwjaWR9hnMqP$i~KKQ zkK_=(Rzt&M%z*<4l`rTd!C3pht}|DY_gw^|*bf(^1**tZe$i&^=W4LE4r4DIXMK6X z*tp0-I+~|lL8|6pdvxP)9AgLWVm(dI+&V$O>U`bFn%uyw=jgI$HZ`dREZpOsY3^q~_C2no4)*T1}^!IMGq> zxY=4mN9z~8t{pTUoj&84Gtf&Q=7JeM}(8yBISwUO4-D!SEe)eEi=&%36) zuSNKe`kF%T>k{`^`@4@iM^&e58vdzunMZq*E{s<+3H$2~&7xa1v2M_T?w;;)DY)6S z;zsv|9keSJ=}NrQSTd9uq!rhqDqErn&v1uE=tf;rBq`bQxUODJ6w}A(pc2d!2l#|zxeQk@ z8ZjlmWS6)mlgTYh~_6dmS_ zmeB%QNmpuZy|2}|nWym^LS#C^K#X8&X~hK?%o2#uY259WX!rg`js8{8|R>v-jb_sqRHRdLQ{Lw${d;_S{XETMA{^RL?{liW58)<4-8i#4H1s@F`YJ8Z_-*(Q;_VGh}D-W<2x z%jDjgy?#9z;a}ivm!GYWh;3zp=Ehx{fOi7Xj2Y-*D*A7|3ND-XkE>;i@Zyk(WKxU6hi?Sj_Y1)R)|?p8^;{1>4Q9sl$z2BspTmOVwHSnPI-$O z%xJFII;OBcTwVrNVT(T)gX}77vpdkl_CzvU7Bg%nJa-XX$G%K0o%lggp|J^(Gv=a< z_PR=$pm1ym$}N8fRWR^5o_PbLd(aWN5wu!9 z1jUf~L8WlQ+rzVFA7hx$TrBa?Q4a7aQu8^t=^*{83EXLBw%zf=uZF>P0n57I`Pn7G z0KJXNdPefFuUW#CrWl%;2iPiuB)xo=qiA6wanKBv5U-1g^g4TkfnIb&`DUzaHG9ombKfL47bK3n=WxDv7u;&w-1fA`{HiXFU8;%Q zX~xsM=%}N4*k#dt~f7~{Fy5>&^1@b=nlUQIjSB(d#GM%&Shvm;D)*T;Ny3Cslzl`%Ys1n7sh zSda0jDHCu?CSbKp!V2s|OlFX7?y&T?|Hsl^`GuuRkiz^-q)UL>W11mS#4q0 z41MD&gdy%867T*ZH9gB@v1b;pc}nwx$LMEIYkS-?(zf%o)^qN247!?&)Zsfm(txhfWx8AgdQ5NdqkhH@d@l9b%5-KG)0uxrO)f+P=ku^$(MTPp zuk1t}YM*KbTaL#=qd6lqjXgqb*gsT=e}%k!5=zB7wmL7{1?;FVnUv|Vn?Ilh4Bq29 z`^g$!lBG;x4l=tDWHQz9T9#t9+(ZTO$X)y(JyA-0C?orsTZS@#W;~3>9D;tVjukA0 zH>{14=!L)VJ3gWh8b~GlCGYr1Rx_FD&FrQvi<{BRZq_lvT;mP7$vu$wi9#OO`6QoCbh$D8r#nPWDncUcDQb| z-59i$(M&Jlj6OqoPQqb+VO6x?1w^o}n|?6~c8^(XyO`QGwfVtr z7oV*w$?Y5Dw7;XJ&4`tDE4^BcL-no-d*~dhEsVMLHuBmraxj$BGz#4?Z-c#@NkOkG zckmC_=|C!XufTZsH-TvPF@GV?Ab&4UcK-s;n}lti)d>eZJrlNjN+zuFI0<7s593>V zcElIK5C~RWr7S>&Mt>uF%&tuIpbfxR!l==j#3S zTX)f~W!&~lSNHKR^W2lZ9Cz3G^2wdzOGeNAnEIZTF~dClVs?7U#oYCzj`_y>?sG}+ z<iYJK=K<-hZ=d@zjdV z;4kxZ--D3@3v4o@2AiNZy~$W+uPpuuCw&swLkh?TEmw^`})f3d|!P% zXXZlrg+!sBm~}Ffx2e zuvd8H;KcA3f%V~Y0*Atj2OfsM^nVVY?N1g_+n*!C<*yKNE}>z>f`mR1?Gt{FsFtue zB45I$i0=~)MPy329FaZYenhc^ml1UnVj}t_Bt$Gvun{*BkR++!M%3|Z#2kN2!~_4E zh^&G85j_GIBlZUNL<9m$BB}<*M2rZwjyN7H7x6KeCL(j_O?cJNvGCTR8R3INEyG8L za)plz#e@wD9SG|l8WYwaR4XihC}~)d&|TlP;40t7U@u?$U}<0BV3_Y?;IVf{V7GT* zV7j+dpr_XfRQF!?7xS+7r}d8Yhk1MYV?8bWA3aU|&pmDY&pm_u4?KVRAA8RFA9&RN z+*2*^+_Nz7#uEs<^0W=U^V|-`c+0e<;Y#Ux>N@PW+^w7lw>7EV z%Z%Gy+{C-C%Nj_( z&iBCuPR^ipvIcv(3I*@GiUk|HYX;A{>jo=%x(D}qh6Hna=LT1KR|ms=hl0a>*MiS{ zZ-W)XB(y9nP3Tit?ogTV@}cqJzl08jw+h9DHxFfw=p3pN(IM12qHU;O#BZTd5j8@i zBT9q@MPv$fkI-QKh{wT_5xav)BPIo(g*ORq5C1+mIQ&JRbolx}Y*?qjj<6hoj$x1e z>BIi?|KqFgAMEq^fAk$rxaA#~Fu|KEp^W!c{43AW_|=})@l8A#Xv3FhPVz;?=$IfxhkL~N49^2G4EVi<1NNge3(AaFQQL*2+ro=|L zmc_!gJvQiEi;Z#K#C~#8#=US##yxRb$K7>i#$9j@#+`QF$L)5~$FFr7#?N$y#}9S( z$G3Ez#g}u^B&2tKN%&}bCY(0Q6K0v?3C+yggp4NK|48!r|B#yg+S1mq80O!Enf~Tj z=C|DL-@v2(YCPxvs5ku6^npLWKJ{O+Z~cAjCx1%o_wNrO&^#0ta6%~pyMx~cS_kt4 zG6u^8E(K}@W(L{@8U}_0(gx-Q-uSl!_WQ2{{`7wi^!KL=Ht|;oR`K@?=JhWM=Ja0- zrtu?`&R-~$(%&bP&c8C0*8e<|)&HH%;csjU`WM-f{_D1a|68rhoNE+y2F9SP%Qdctw(oDhJ&vXUiuDD*urEy2cHFrObTjBmU z?uGk(TrQ7|>*Yxszr|BHKE~4|KA(4Vd{^)G_`kjX#oza4PDtTvkx;?6G@*y@MZ#iV ze*au^!X+M3*0vW?v1@eV$4*U`pA7~I(G1ww(TCjcCm0-KD)S=E{4MMHLW`$aW zoe9+niwji=`_bkPt7)@^^|MLCX4rV&TKn90$VT~2+k?JacBSvSo#DH0hx%^W_P%Sj zk?*{%;5%ZA`1aZyzSTC3Z-q_in`YsgY^`^Y{puZT-+TMmci!IiUvDq_uXmt*?ET$7 z^G>&qy-V$L?{53dd)YqszPGQuDfErEgvNL~=x6U-wcfL;UaMig;!Nfn&a}S${N4xT z@YO&O-vSi(y+j#b38~rJC<2Q^_~SRP?>X?dcr%#ayu0KlZ=|I0e#8gQ9GvtN!k?b&?Bp5F zpFCOU_FU0h?vc96T~K?uU)hT8Wj3X|t^L>agFWba8=CDp5bEKY5~|^99m?7;5BD@@P;!!_|WMeeC>1%#yM?*URPu=qpN1Hh^un2 zx~p)oy{kZQm@7l@4_DIQHWz~DT(1Mst~-Gk*NH$9_nJUD_pCr}cke(Ecl|(Vcfmku zcZxtM_X~e<_gQ}~_fmgm_h7%vUElx3^`rl$OZ+=s_Y!8ib|-Xj%}n^&)ioiBt46|o zCr82>CnBMr^D4fCb0^;I?2bQe*2T{;Gva?WW8yQKzVXr0IexvgiXSAu#aEEX_+(N) z{s|h#Z$rcQ@n{j>3~l3!qf2}e42u82A@LVEC4L*{#Lwf(_+i`_-=7jEN5@Zv)zh(?w&}5;9nl$uAR<_tZD5ZWP14bnvwobW}3f* zv)13!+2`Ny-1C2NKKjeJ(ga4jiUdx%ng-nNF@d`7^?_OLJAvD7cQB2oM6ji&OK_2A zMex4oM(|s2>QFsz_0RCg+W*QWASvp@N!+h)G=c91W*&hpjL6~1Y@!*@Y< z`wWlzDss1P0QdMd^KaihF7}0Cyst32`I?}yZ!ik^79h263*LLrAj*3S%e>JT;{Awv z-Y>}NO+bPt4(B{yvD)(i{XMTx&+`abJU3w77jVbD7kk{>G0(jagWdDd+&vBz-GflT z-4mJIZ4u^f4sk~!##JBhTy^on^(&%X4e`v?29I4G@XR$7&s^j2pKB>zxpv`|>mpve z-rSB7j=9);?Zd1th*o3)4 z=B1Ox+3S>ZraMiYrcOU6hcnUnXcjyB%{phe+3Qp^2OO6<>YSFN&Q#g&RF!>BGFj)G z$4X}|raQGU#z~9L&K)*#R`{+bVeSBcFLa6Dp?YmA$>!mB{I}Q3WeH8vQRzw6s#l{f<vOeI37 z0tJyjkO{d1DUdS|WbVLM<_SD!;lLx73|waUz(v*yoM2?&G&=>3uwUR9e-9kxoWLor z2%O=zz$G3JT;avQBR&c|<*UFO#s*@j0YAfn4w47GNE1wfbitHJ7tDxs!8G_bm>DU9 z84(^#16MEw;sfFM7*IY6eC6#xG*1SubAR9{*9O*cQD6oq2L|x>KqK}E{KU?IZ`mgB zRhtB^Xp_KJjSNiGrh&HFGEi2#1=8!FK)jt4xMmjz_Srpw+4f>!gnbieWg~*W*n+`g zwn;FH9TiM%w*0gfvtpq@h6}4Q&iLHaZk$v)I(Oxy@u3*@E_w{n@6|NZVdJ z*_ArVzR^EyVeYmA7-diJgLNZ`)<=F_fChRW12v~C){b&gx5z7fA*q?$lw=#zm5aXXU?~B-^nTuojh{M$uH-f zBC^}bBkP^qGS^8jzdI4q(FviU^Bg}pQON3Shu4{j&!!h{n;JN7e!ynq!hCa+6U}b+ zH{;mFv|&?IoDEHK)-bQNx;d^j%qp#EhG_%SN?Vu;+RkLxfhL)bGaxn+Af&+K^Tqg~(x?N%q19(JM>-kV$@z}eoNUbRe9s6c6F-^^ zykJsrtMPJ@329IBR;!vPn%7)Vk2$38WSw4;Il4ne=>qAdqa;$>OBHP-`Lv8=*1Qs- z86;?Z^1%l1)PBTW8;uM0F3#DjIBHMfuswzYb{7uXEjVod#$mezr|f+EW2fM%9fv!% zFP__;cx_uFXd5GFt0I~HjBm66vgr@Vt!YqFlc1bBsG;$!r!j1-;ms3+x#9+lm?MwaMO>94;_GaV$QwUK1i>f+Np z5^K}QGYh$C|HD~(69?^K?6w=Q-TsBGb{w|a(b#T#V~_2OqqZYX+2*)r+u*KkhL5%_ z5^M*g)UL>)gHck4p}x*Ud!3K*x*kh(ANK1-+|`GO(=W(CpA=^nX~9x5j1A;3_LQTX zE)V&)gm^~2!&4~%V;Ulp>4NfRG+LP17-m*rk=csfW*4rRLwIR+!#Mkq(%FJs&RP_A z=A(i$2{oJ^sN*z4EvFRfI@wXd39*dxlsTNkOzZqfYXQ*Gn-SDQWc=fo75y_CMUR2XWd?zyaGFTWv0EvdZ=L8rRtk+-WCqk8R0w zwjyuZANayL4B1#sqxZFlUeS7bM0@CNovu4{n{Lv}x>aLztES>^EyiOS$+Oy@w-UWU zTliToQ230=>1TF^qX@r4Mdm{T7DFpmMh{lTFxJ6n*2HAi#w^yvbXLGTR>2IG#B>(I zSpJCN%#3dQ1|1lIdITyk#Qc2E)O<&W&-q55@U}kWVZFn3dWW<1K8NW8cGAbJtN*dA zzF|&{VR{YFrEY|53cR+N@!aOYO0cY$maS(U{327|E6B!m((~PN>Lg z$irgzhM6Js;@?D?>1!U{mcXQfoGUv@rbJ8T3 zW9EBjm-*RQV_G@$%>ZYlndWpcE1jBVn^VLbb|TDiCq~XW=j5ETPR=-^C-$+Sd$(;UBn=>3}*07_Q%_gP?tC=P&U`jB(`HnDFpUHDQFaPM@vP~z-AKFevXnE;Q zFW5YcvgvrvIy`OT^_2ZjkJ~$X++NUqcAxIIn{~TgtUK&%U28|`YTH*=*><|f*4JgW ziq5wMbb-yHf7)ca*oN#f``)gy59}s;(QdZ~?Qy%^Ua)`J`*yK?XUAF5aW<6>vITUI zEw6oS3+-tKXdgRU``ZmV(4NuX?QqgenWh|$&SyZPptBzzE9YVEz8E1R)q3y-%wmXm6e%x*cafKbj1$HvW+oc?D*K(je z#)0-Kd)b#9WR?AG28^~PFxEE4d^-?-**~zu9>6JkANQ=0kM>82&^nSshe&l@F6}f* zrf95e)a>S})-_*sgvreHrV8(vesrA0EbN?Tb0?ltoov|UG{GZh3>?>f)6a==x;q=4R?aZzSEs2{&dKNGb39IJ=aC7S zUFMaUZcdx7W~ZrSrkgBgkP-8%c`T*OSxIl!2xWmhm0@yLddOC3A@ih;jFB4BO-f61 zDJ3irvS5>`ET7b9lgxneL*H`+6L+Mdy?_Kd!>Q7U>-)9W2ArVq4+zSXY!Nq<*2 z|I(z~qu=wA7UEkiPcV|{*^&7;l$AM^jkuB>xq}0Ej$?R(Gx&mY8N($c|6(dEWg0AI z7A#^8%;t}Y|2vOiF7#zCbY)gFVg^)a3Y4V>IT*vtd_y;{(CTTv&<%{%MZBsb8KvEL zTv~a-=q=r?(YjJU>T-2+wx;B4&CO|Aiqo|TCu>(O z&`F%7%eX?1C6c_iX^6-5TSRGDJk%!msH0%G3~6}^c@wEeNu&h}Nk2B0nH(k?xKd8> zwEV{xVwu!rLoV|(YMZv`Y(`?NS%#(ND7KqNIBzVjne_6^l#myufy9_@^1+Oi&t{JN zXEw-lvrBH86LQM@BYRAgEHl?-l8KT&CQ2gBaVc(gOM0_j;^hx{Ame4P^pr*Ns|=Ph z(nK=JPvVk{@(dQ|@EEIb789@)9k2-1FabHyAK~bPmu!k#tcM+}iPfxz39O32tc~`p zj^Ee}HP{T5*cYWZ2xT}8rMLuTxf$ho40U(|4fqnB8A2Z>mvPK03t3n;@)tSH`f{D^ zjilIF0}Ccb?`jo@GxSV>cdU3+`ic?&PoB#s*x^I$X_KT*^9J z$a-ABhMdJFoWYiy&W@bUKAg$HoW%*8$7x*1#azyHT*qD9#-lvUD4ya?-r!T-<14=9 zC&n|5E|4jp{2m_s2+I;MD2o8AA%+d`hLL#8=D5b@IL=ns&gNLa-!O>{(Umn2$x0}} zBFMy?u=*|jRp7L~LdGHZ`<=4Ww+=V zyFicHF?z!G(&M&;p0{;0%2w3dwzxjF1@xWGqt@n9ujbTDnoA35L9MQZwUw6BAzD>u zXd~UMt@W(-*Z*|1hH-&r;|8tH zVekn#kwkt$Ug?f1G7ZgT3;M`qjFOL-E#dN~39N=sjKn*3#XAng2hPPO zu0kvi0l0=RyhR!iIgwV1AfHr6Rf$AH>4okx8vSKHrpsE)ltWk{QCKC>*d*_;Ny21{ zgvkcUENdjEERp;&Pm0SpDK3Mgq;!zt(ohOZS;;54C6lC(v_iP$DdKPmFR&9=u^2}% z34fy-W}rC+p*&il2r3~1@**ixAc20qV>BP|G_P|X&u}3RaT<4WAUCo-SFs_NvLgRv zG5(S01o)0qn35A1#*wr-oC!LJpS2fXXji__)_kCi`B3ZgvR2|{Eyq)umq#@R_vp9W zqDi<)gSu3|>U4dslk|}e)oa>a|IrS5QX}=S*3|u4LHBB5-K%+ZmuAr;nnn+3N{!Ma zdQQXizIyeM`t+0fG(nSU7*lFGrq!IxqD7fUE3ue1WL0g&TH2efbtt>(WDe80oTh(q zk*?teUC({Gji+@xuj_U`(d~S#oB37OF{CT#@DGM@I+JlYlW-uvVJrIBkjYq{LCsHU zM!r`sUu&%1)8~3wuj(;9r8{(suF!QlTNmi>I!F8H6m6^Hw1JM(Uv#vV)CpQxr)YMa zt=V*urqhj@L3e0cJ*%1ZhGx+>npaf|>9?$?d09t)VM}exemat4bSanWK5o+oyr}Vv z){FqlA~Rc~3@4&F*JA`PVi~{S1k*?~%SkxeN*+v+NUWCuxGF2~R!$<7d5>HsmDDt4 zq?KtS!_5?#YSzmNb4Avh53LlY?eM|x->C;rJAXi$eaAee4FSweIyTXLQY|| zti@EBhwd^04Wu)QOLJtD8h}*6TNK4L6vPqa!A9i9pU8=6$cs@Zfc_|kt|)^JsEk&q zi&kiY7HEgo=#BOmf^L|A-k6SISd1}PiRoC6Kd}R=upJw50DG_tC$JZ1uo*Y76*sT~ zkFXf`F&WP=3XjnhkI)3S@e8gaFU}$zjv&A-c)=C8z-iddp_tG17{Quo#}cT{?@)w- zbbLh@FY<%#lW>!i?pZy zp}lm1_R*2rSBE8fVTNdL9i$y}xOUQ!+Fplh8y%}{b(pr)-?fzv)@C|dTj?NeqQkVQ z_SQ%pp!K!4*45rxUAt>F?WR9#H!Z6@wTSlC!a7j%>JZJL<21WY)$eq^X4Tc2PB&`~ zJ+2w`n*OLyHHZ4OpoX!O{=o8Dl6AE{n`qRR9K1g}|!4CW+q zn|ml{;!)A0l13)CG&EJEm1!bPO>b#w#z{jnQyQ3MiB9j5X1ioH`y`dwCjr?n(Xvzi zk!`YF{+9W&PzK9nX(Yp>oOF^j(nyHXavwS57{X));xHaJ(G`bL4}YULCL$|(A`FoT zvUnmr6dT|cTO*o15YJHv$5f=lpD2P=D2t6~irr|7Js5>!7>fh=3&*hg-(fSt@i*c*2mf&*?sFhcumcV-66;w7 zOIZwanH`gv2BYc4FvhVz-?JYdvIlRn2hXuPPq7F0vj=yx7q_xMx3E9gaX8m=DA#f< zH*hT1a{@PT61Q>&w{jA9avFDW3iojW4{#z6a3l|NBoA;953x7*u_yPiHMg@hx3DqS zvMyJ!E*G#0=d&s&u>vRXXAWak_UF&+!|H6us_e+x{EZFSoUIwjPHe(HjN~Z(#>s5K zg>1o9?82Sw#>4E#OB~MI9LE=&mG~Y4TtmS|Cc|E)#0h4=Ii|-|X2xBn!2@Q(Yo@|; zCdEg(@L%H2{)x}|fYH3cyWGng+`-eF#eE#d4eZ1P{FPH!hW(h0Et#A(`9<^bxn^XP zT0N*Qbe&$+KXso@*Cje!r|K~6r9HH@_S9drla|(Qnq9kVChesub+Cr%D1}Z_(LXe3 z|5oUBwf2;H^`?gDOZ`?unn_bJm*!`2EzfG&n!oBmcG1}!q&qlMFL1TKe)coBp70(0q=Jp+4T?R;$443mVMGnan*(USkZwwzh!&6*n}ai#n)Pn(OQ%@G$$`=Y97~Q zJgTaDHCA`%OWmoDb+g{q-5RAkG)fQXaXqBR^dCK}7xakU)g$^$Pw87drEz*i-Mp?z z`7n_fnS&p-2otm%UHpYf8OiV1nAzBlMcAID*_~C{gSFW$(Zk%7-Pn=s*nu6`jSbkH z_1TRT*_l7FGk;_&W@a;nvtHt#{%^D_pK4xS)a*Q}DY!+2e`}2Xp-*+H-q106TnFoJ z9jGf3H9&uztUYz44$%HOM7!(n+Fqw>2c562bd7e@ZQ54PXfM5?-Sv$QSL%38&DmOj ztF$_|YFnPx;k=^@`9}BB@Fr996N@r6YOo+WFcM?fAB(vV2e}tF_z3X~{qK85ile+V zO3bntCyTI3_TjkP$0G?KUNTAwQ(AJH=2FTGl6q#Iv@qMHi#ad-%`+KhzRD02Zbq4O zW{AmU2ACqImnmu5n)0TJsbp%H3Z{gqV16_uObS!XgyaYFMlzYp;xmUNAS>jxOp{yE zPmW2e#N^|7QbhZv3j`LPb7 zKGvWl<{=wKCOUNE*Z_C=GyY*N>}FD|U;=0HKaS=l_ThfE=UO)5Y}V#jR%37e%(g7W z#w^6@EX)eb&mzpn{LI4~%+2pvfSH+xX;^?6S%BX#57V*`)3N|lGau8j0Mj!E(=it_ zFdNe{3)BAp@hy|`8zx~gx|obs-3+RmF&fes4d{FQs_zrWTYaz3^@B$17k#J+`bfcB z>fuBEhW9lSpK3ln(^7n=HTY4R(rP!lIFczilUcZmxwxHWd7ia+lg;>=y&1<5^kEiL zVP=f#`!$O(cHy>T=hSP8(VM;8?rxZu|2D?2`lm!R%AI=U?Emw zURGlc)?_9&WEM7KdUj$a_GV^|VpdK~%q%R(bu7ihtip4w!>4S{59~$G zuz`(mggtPLlMu~C@N*9$a30wZjnYU!1EiEr$RVRpR_3F=Y({4}i-GbGQ{*e=i%-@` zCfP2Xd1V7q$y|gm7_ZOX7Qsta zMm!_oMH_sBzQ}=*D1zyzg2iZr6=;X87=&#YgTt7IqxcJlun{M*3x{wJM{p9maSA(d z3F~kk%MgWmID<(zg<&{`?%0Pm*n+xPjjC9L!kC8K7=v`^hh*poWn;v%2HvqW{$)Wt zVrJZCT3lloF4N!w;~2#c{D-gj51;W8AMrBpC2FM`yvc{W%g4OM7reule8l&>$M=k8 z0-y5>Uo*gW4DbU3jAelTt5U`#p1;t~Hw^GKV5&A0yoh~ z)P#rm9gZ+7F0&9WvpAly0-{+NpI95QtOY?NT&RZ>h(vPKKzh_d8dN|AltKpNLuzEj zw@8I>IB?@L6Zn*$d4bQkk2krDCpm??*`G_ zPwGS6ttWJ|?$W$S1&*2a23n`o3a(d*hK zF;Dc9_EirDYbs9AJe;GYxl-$Ki+1919lsX4jn1kII zhs_v-`RIz7Xp0f}6@Bn4+Mx;>;}_IMSyVwq{ETuahRP_28pww_$cLuLjdm!AZpe?} z_z9Cx27jUkHljWbpfxU_2OePv-eDRPi;zk-B8wbFLAi+X@-J%0XEYVJw3gJ;U4D=r zQdkB`MHwJ9WUw@o0n$SHN=NA;U8R%sl1S+#4WyTplde)oI!ZQaE~%uED5}W^{494+ zSS}!wY{fUS4vHywhu`rKU2z`GaS%1J5@j$Sc`zE^qi-S+Fbs7;Rze($;Ulx-B{LzK z$?G&(%tW4Ds{Gz$|QZwIHqFm-L-RC0<3np<%qEY4}t#@n6l&cUpp9wJfQ%>1G|KU`u9T z8~(tq%*S3V!9Fa-fvn6ytjYnb%f773K5W1)jAT33V++ka*=r}VBK&}+Iu59x4%HN#q&YcX%W$}-FbuW$BRa7XMz9SQa}>6539fP21?9C=mygmyRQia@C~=#a;xbFbF{?#ov-oAZ#K=zhSN6yQ*(w)hn;el< zvPG83Qkf%@WwH#FLDEio$gk2^%1Rx{E5#+fCLSkWBU?)o;&3)$Z`NUZ)?^b_W<6G9RaRg*mS<`H%)%_s;{2JpSdK+lp1E0pd0CzX zScQ4`GYha13$QHnvK))DH1n|pi?RswGCvFRN9JWV=4N{4W*UCSWXwjxG<5h4V>KBQ zRQN%`w;Hdn^^3mL5Bf@9>1%zV@AQd&(*HD0AF0*<)W_$Vimx>@-)TO6){+cq4Z0Y~ zRP2&i%lna2S&R!J>@jw2L?6H7;ZX&RUJ{)6GTxA+u=eKyyjEH6$ zeCD@!&s6xrB=|~$FI0Zw8@}fo{>vMD%*(vR1BqS16I{X_oX6E1#f2Qe32e`y{Eb~$ zi_KV>)mefSScLhRk2#q$@d^BIhK4aWRdZ3b2;;Oc16rPOTA4xpl~$V&?7|54Wm1mi zcbw1fxs(ODmnC?VHF%2+7|pg!U>`sl~e+OB;XTXB~sch;39V52v%Y?H+ zupQm86N9k@L$Nh6HDw%DVH_4=9OmM8OvEUR#t`&IKXgKO{D#(v_fsC#Q3AgpJBlF- z@*@Q@Ap&U-q6a|=KJhEx^Bw=?^F-bEls6g8D}0)$hR*Rnp5;rPXP##a&oao1 zR9>VD*BFjlOoF>ijpzIZ(aea~{0<+P5g(Zuam<)_BrrWfObI_zB19kJD2QW_U-^aa z_>ph;oKN_e4|t0=c$t@Yfya4_CwPiGxR={_m}|M4t9XdZxQ~l?mk1vBYqgiI(>}UG zyX#KvtH-pLp3^~kO$X^i9jkA2qQ>cb4dW6`!;PAgyR;0?Xf0mUmVBxG_*Tc$%Y{tN zt<24nEXDh*&-ZLcFNQKBrmz_PW;N_(GhE>SJmXjdxDZLP5jn6QKjA#;;s#pdDf;0h z#^4iXAQpci4r}4ZcEsTT;&2F`aU5@P693^i?&Bn`;}Fhb7mi{lHeoeZV+H16KBi+b zhG7hPqd(fAQ(_ORF6!a`dO8nqpUeOMKiC)T9a(c#6Ul zBOiImLjDk&Cnr}}Pyo%xUCRQcxNA+4(U2sxB_rL*$N=&(ibA|kB|fGO>uJeux^a|# zTx1;oF_mP|QZhyB$QOMVCIVH7E>JuAm-9}ip{xt>v znv%aw#y=+Ix)E2s>lJT#!E2uPvcG%6IG+Q&h@WFI^GoE>a!E9+R#O4hKD#eK|zrZuaW+8eR)h4^}WEIu6{jE}}U;+^rA z@s@Z)yfR)DFN#;iAH<)=GvgKUgm_i_cDyzo8*h%s##`gD@qu_k{6qY1d?EBuTNvY_ zIISDv$J`#5@?iY5zr^jl8u#{IJj(P=H@BZz)*UwVw9k3N!KP<|d01$5w%CS)_T`Eb ziCsiazN7?)d4|8~N}^~uk4NuQIQonR(GHqNztJbU&Y&o9G%?B+O^NbH^P{TKf~bD< zY1ArO7IleMM?IpI(JRsNXh5_$8Whcq21PTYfzgDhe>6Jk6}=kuh+d4^M{S}OQT?c1 zR3WMq6^@EUS)*K0swhKrpG47R{^2y|_<^7KhCOWNb5^pNh0I|VGnvLX-r-HgF@U$| z!x(xphAzBCSH{tf33Oo!?U~N=e8dYZraNos7Z~GChVeaP`I(9Q#w@NdkE^WUKh_en zokY=2QbY$y75zx+=vPujCrKThCrNaUq|qgAagKjE$0bg4f?qkpkL+MC>-dHxY+x!Y z8O=;yW;z{toqF`A0v*UpBQjArScQMg&N-8D$lLDmq8t3#^?vV1ZgZ|{o$fLxIm@@5 z;V36M#_^8u4M#h|p}y{$zUFXWbCd%e=^)4Wnq$J_>yC4%Z#mTQzTr5BJLbX94R?gE zJJbOVwVy-mVSl^X&-V7Pt=(;4N9)_xssru;mawDc>}FN_S>KU1ccRZZ)1EGOpz9sucfRW(=Xu8Ep$C2fhkA%3 zrs0&CxnvIhXC7h;k(eT+r7-C!MrQK!CFmeFL~2nyyQMl z`He?h9qJex{lH~Tbhaa%>_A7@%Yk;5u!%ZFiC| zh^)L#K0cr9A^Yq7|*}VCvmit^wAe&kG|pY=mdcaj4b%j5>+HyMS1^4~R6dVk3el9LpiYu1qo()M%YaXR5kJE?Zyha7aQlCk*WG2rumtK6t0G2X> zC87W7cvdi-#Y|xtGnmi&%x5~&n8p;|<4xXXIHTyt8+7MYp5qmo(1p5mpc+jmO&tnR zncS4%(J=ENCn?EB3ep3qNkk&;f*t0#g6@h4Av#7iFas>i(PasTs-4{_Gy zTs3pBDF2#=Borm06d9?)!_*)rwaL#j6ruseX+cREQ1ghKkgn0@W!+8H!Vg zV&vfo(vhHVOvT?O=Ylsq;6-L!AK7B4kuZ}byjeP?Iex%^GNhF*`mwji>^^LN)#23l0@aA^ijDeOH?Jw8dZq$ zMdhO$QOO5&^FmRsD0h@I${b~i(nSwPiK2x5-!XskKYrtK;5FZHh|k&0GBz-WmAuDX z-eM}RF^2xU&WrTnS-R4eHZ-9r^{Gc4s#Ali;jAQ_ii#AXB84eSJ}Q!rQanM~kl{T} zN%BydY!oFoPmzs+JVpVs@;DiJjEBiUI?|GqL?q!q{|lPjv;N`FUT~j3`K1R#_w-L( z>r_AW9Y1uqQ|#|3dpXEX_OY#rX1-~A$NRjK?CDJVILFsq;Oj1Rw2K_)awoddDK2xWtDNd0XSmGC&T+c4 zeaCm5Z;k&K?}_in zhe93wF)zkNy%|?BkNgJYi0^vUdER%unfTTxIbn6K*@C3> zBr9)FocF24GTO6+m-&HFoMt-zvK;w>hob}J2tMp8WujYDjFLySqjXW7C|lGZ${jU| z@<-|MdhOWQOPKGR4~dGJsxF>9*)vRDI&wXpc`D`EEhP+A@;L} zFWAQCEawxJF^?%sgsr^Ab|$iy860K~$63q?R&btmoMQ`@+0Gw)&1Jsh zBD=W2_ncxEzwsSM*uoyZ=4(C+ipFIuWHz&Shsl9@yux5Q^8$@&M-A%m6qR|LLS!Nb zkdpt5IOi45c)He+uY!K*ZHZ-T<&Nlu!}wE8ZU+UNV~n~QB!crbo^mvZkU}rK2B^N9wHyf$VX~&lR8Xz z${Oa*JVYuI@eu!c+kgDqE1vatk9pL+?hBf_wQg{cpE%RGzU|wNbhLvUV6UJY>10P+ z*xIJHw7N~KWz%3AD%;FbwzPsBEn^2O`l6NXWle`z*P%9XlC7O$XBXHbDA_hR%x|6G zVP|+I)C5<1-z_F%x0(6Le4Mfr*R6>*Cpn$S!YdSD7$td|I!vcApU{cV=*5=|=37Rw zpLaRROn&1derE|6`Hb_d=L(xS#~1v;7o21hzp{zLtYIIk`I;qcWIjun$vh_TE^qJ# z0~knGo~1plsYgvJQih@wBs-Z&Pa5D};9Y-s(PN(Sphw*4KEH6c>-^efZgok>UN$@5 zZDBI(EWdHKJDuY`7kJ2po^Yk7UF|i$^rl~$gda@75g+5MdAMQ;Zdr+B)Fv&>$U!@D z(~Z*fpe(Oai^0_4b(%4X_6(;rW9ZHsJj+OWFr3ceyblce;!eCs8#>XFHi6dEp&nJK zN(D+$lwv%|6J#SNS$UY$q$3&06EJI_iV5}MzXGH9*NfiqlD9nXegE*DX9B-S#CcQj zw`uvyO#EvW?wgm`1m0Ghl$78RDv+5fNJ(;11aFt%@venEeA!c8@^^m^yz?hd`GaRX;O}AW^N&!e zJLC-yd&^_q^@PbeYbyQ-+&w3^%^mv6C!sW{DN9CblbPz|rU|)e#8Wh-08J@PbBfT2 zQsKGRq8K$E=sOCNjVF18M@U5i>q&^C6K?+e?$7S=JGZ;ZC80Y0zM~xDKwt6sP*rPS z^-$GFs8?k75!0H|yYc<_c6=-TJ-!;BkN=1d#~0!w@%ean{Cm7Nz8HTUpNaRzm*SnF z-ghHD5Z{dt#ds`E>4`YK=i;1Ri}QOsE@5OC<*KDtWU z=n~1It0a#u5_6F|oZ^3+;vzqAh93h3*~fOiW;N?s#&TvepJ~kGZ6-35w-~@+dOo12 zZRticI#QRm)S?+xXiE98&l*ya`V^)f#i>gH>QR*16rcf5hR1}de)T9!ee%;VoTq%$ zrvTN+M=f$umRwXKJ0-|UA=2{%X~;qn(s1A8+ze;>x~DzmVSje7yWQ+o*SgY={n!uv z(5X&zWZ)=60@Lg6D|WTJt?glJyV%q&Hu8n=*wCIqQP{*jwsf%VeBBPdX&1-a(|7FS zOb7d+ue-=GE_IwMeb04H^K)mp#W{ZA0=K%*Z7%dn7rMoTZghcbo$CreaG}$JXL{GM zPH>1r>}y}U+0}NovZ?j0W_c@E!n{826X851_aX1cx8lF!>+!Yte0(DQB|aPf6#o?O zkB`NB<0J9z_-MQrB)A648VtgdN79Wal$EV}S-{aK&9cT4coZqCDHk~zn z!e$ooc`N#g4IOPe-}5CGJ2cFE+3jqPyTZ$E6AqY`bLQh8OOlkjWC=Qn-c)87jhIX) z=I}DBf}SWLu3Es)EafztxXup##{uLha*`C$@1%$>lRCOgn&=wIqpLg=T_#a`4?BIJgvm?-trF_N*%;SA#F_!lj&KL$Virx&MOX#2c0xfuv=5(SC zov2Gks?nYrbfz+GsYoZP(~ZhJPfcFoXW)QC>@|^?^FwzkYaG?F|>x;f*ce~lq7wzoxw)AB0Ne_vhXBXDN0uIhm{a*rzbOM!t0oX`$pXGx_^4blm6mS ze|X^4HiR3ZOI_e>XNLHAY~ZpZebrZe+5W!ZOLnoh&)UbnWyQ?GmNH9m_jt2mzm9LEa457^ET^wmo0qAm(1iV7O<0rZ094sW)a(%&t?|# zIUll$nJi&4^BB)`-e3ZQ8A)$mr4ujEiuTl_QON6`3ORdzvXhl$qy-}W35vRe80BJM zSo=KYE)Rw~wF&X!8o%)~zjmcNTpsq|4p+F}HGb!sQ1w6R7Jqk_7v1Z14~O_AF=tJ~ z-)7;qIRocPO&Ky%ja(13SWnTON`Y>^KwWy$jF)LfKibfjjtrm!{pd_TI`Gni)s5zK zr6uiYL>uby47I33MXFJnvJ~Yh@>7`HJV91+l95cL4!XNEBqn*N1t*Zi+hM*+#4VF> z$0Xbi95E%a8Hh+vA|4|RIY>!9vQn7Llpr_d$WK*@Qj6l$qa4kt#52^OeNdb=paTtP zPh&cSmGE&Rnp2Nf)S)3YXb|GrGE}7uCCEon3XzX2v_49Q^N7G!D?o*T!n}?4~t~$<1IgAMswC%A{uUzc`Ia%%X8- zA2OTCe8RLAG=oKa%(7;;vW2Z}5t~}YHdYO}Xir-?#OE9lrt=MOx}#m}L|6Es+x*yF zt_}9@w0peb4?%UAn9HW-rrAgmSaA_DQ<9uirw~t5l=_sX8D)8fY9R(}PHkFKn-R&n4tv8A(El2h=O}e<7E>;YI)Ul7D*IOP=vh zPkPRi{^_rt_f(+CS3K`+69{g?J)>(T4_sTblZJd`q$H1r_q73~X~ok#PhH!*n*Xkew`NKN~p0SNzHjPIHhy_?|!bg=_r86@KL!$GOHa{^S=faENmp z2^{1%zUKhj*cU1jtJ%mhRx+REe87ji!)(Sdk>Mdu92t}#3I4e&-Dyn^+B~>H+9=qb z4%DR$HEBm}T2Y;L)T9kh(=M!wKp<+qa$K zSlO9v5eMmwcz0$7}CdIhP zy>9lP+dS;o{^&RU6z+fT^hbC2gFD>gmwxYNx4G7>E_b!dT;_+)bB5D>E1aWY_O*{a z>~1?-+SG=Dc$TraMa}QyW;2WFO=${~dOyA&$MNm>fAK%@&G>rw_fLE+{yV-J{~P}k z---W@U1ciZr4k&rpG;RH7MG!#PjbiwXO!8}-B9 z?Mn*=(}kgQVH~f7vo)Pze8316Fo7jZVkJ{q%Li;?HlMSQtt?NE>3IN4OvO1te(V zPkY1DA$IyBXr~T%!d+pG+=;N~c6jW;-aGC#kGL!JAV`=qcfxNy=^jtJ*E8-5{Nb#J zJ?8<>ddTx0^o$2R>3&c8efXK9?(~q`-QyN_y3VbxcB6}2oFl-wwZLp>tgCr*3q$d)yv+ zdYtr-=fcdqv)=W(aMN@U>M@UzDsYzs+nArnLVQ?&$3rb5ACFOxN68cPE~!G^p1?hl zaLDkACNFw}dmf)OcqhdZ{fQm5{5RH1?AEj~R%4l%(Vh z^~;3&1G%Y22^vwFX5m(T{SX7aOozbDhSG~s^a}CbBnB{rkxXLhYY-`9fzH^=5xXN9w@=KQozPQa5!7e7`FFX9qeQt1{TY??@&F?(ney@1UtN!L~ zF9uGRod0~7*es+Z4;d&B{BuIR>Ho!EH7P+o%2S^TGz<06>cO8hB!epTsxhYSc5Es;;6xAsmY)yTt(u!)l=PS&z z1*@#i4j<>FDM-*)W#*XW*<~{}*puas;1kF3iBnkWB-T5QFC54ryK%yX{9|z*3K8xd zi*mv;d}m!&Tc1U?V7g71Vq@O5ChuE{nHFT38CY*367J;u;xVuJ)q|Srd~-3w;$fQZ zIBW2>6`5pZ=30`)=4XqU!>yk4{?FKBW@L|rS!Y!~@)@Sti7|E~VXog$doaYVykT1= z*oY4;&vNsz%`_bKmVbHD@ci+$M7W)CLVfq6Dyt>xHfRxX%?M4T}P`z*sMn=#YQjJ7`me1qN& zqpRcS>8tdzA0uqTc_k5FO=jDRxwc`YmDprnj+l%~{$&b&GB-P{ z&U#z%i7)e!gPH4FEO0c7eT((>XS+T5$wr*E0{6^8G9W?yQJDQ!XJdH&&6sLy-mw*9 zY{mp@Gs!AUw=j#$#u`(x+rRw93;yK~MhQJpSZ98gTAEL+#5`-U$Xa}2P1ab6?G|UR zPjK3d{AB_){nIS`WFdB0jy2ZgW9u{Bro3YV#)Yn^m6%{jru#UvO~a?&a+~Ko82r>m zZ@So&%rGnCEyO5`G0gIeupDD7&t!|U!2GQD5e}M+3tl%lC(Xq!pJt8im}@^K`WC}{ nkA8kYFK5uhsq}Uf{q4s{J2KU3EHf{=Ow4I78oS4=toHu^%b}wE literal 0 HcmV?d00001 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 79a03164ea..56fe4188e9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -425,6 +425,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(audioIO.data(), &AudioClient::muteToggled, this, &Application::audioMuteToggled); connect(audioIO.data(), &AudioClient::receivedFirstPacket, &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::receivedFirstPacket); + connect(audioIO.data(), &AudioClient::disconnected, + &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::disconnected); audioThread->start(); diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 340ca9374d..6450f25208 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -156,6 +156,7 @@ void AudioClient::audioMixerKilled() { _hasReceivedFirstPacket = false; _outgoingAvatarAudioSequenceNumber = 0; _stats.reset(); + emit disconnected(); } diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 3b2c1c1ae6..642edde84a 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -186,6 +186,7 @@ signals: void deviceChanged(); void receivedFirstPacket(); + void disconnected(); protected: AudioClient(); diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index d74e1ed1e0..470d038196 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -39,6 +39,7 @@ signals: void mutedByMixer(); void environmentMuted(); void receivedFirstPacket(); + void disconnected(); private: AudioScriptingInterface(); From feb3f68a00bef6b95fc12852dcdbb913f5390401 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 11 Jun 2015 20:49:19 -0700 Subject: [PATCH 28/94] hack to fix culling bugs for some subMesh parts in the windmill scene --- libraries/render-utils/src/Model.cpp | 62 +++++++++++++++++++++------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 8d234cdef5..94a856073e 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1019,7 +1019,7 @@ AABox Model::calculateScaledOffsetAABox(const AABox& box) const { glm::vec3 Model::calculateScaledOffsetPoint(const glm::vec3& point) const { // we need to include any fst scaling, translation, and rotation, which is captured in the offset matrix - glm::vec3 offsetPoint = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(point, 1.0f)); + glm::vec3 offsetPoint = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(point, 1.0f)); /// should this be point??? glm::vec3 scaledPoint = ((offsetPoint + _offset) * _scale); glm::vec3 rotatedPoint = _rotation * scaledPoint; glm::vec3 translatedPoint = rotatedPoint + _translation; @@ -1764,13 +1764,33 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) { if (!_calculatedMeshPartBoxesValid) { recalculateMeshBoxes(true); } + + if (meshIndex < _meshStates.size()) { + const MeshState& state = _meshStates.at(meshIndex); + bool isSkinned = state.clusterMatrices.size() > 1; + if (isSkinned) { + // if we're skinned return the entire mesh extents because we can't know for sure our clusters don't move us + return calculateScaledOffsetAABox(_geometry->getFBXGeometry().meshExtents); + } + } + if (_calculatedMeshPartBoxesValid && _calculatedMeshPartBoxes.contains(QPair(meshIndex, partIndex))) { - return calculateScaledOffsetAABox(_calculatedMeshPartBoxes[QPair(meshIndex, partIndex)]); + + // FIX ME! - This is currently a hack because for some mesh parts our efforts to calculate the bounding + // box of the mesh part fails. It seems to create boxes that are not consistent with where the + // geometry actually renders. If instead we make all the parts share the bounds of the entire subMesh + // things will render properly. + // + // return calculateScaledOffsetAABox(_calculatedMeshPartBoxes[QPair(meshIndex, partIndex)]); + // + // If we not skinned use the bounds of the subMesh for all it's parts + return _calculatedMeshBoxes[meshIndex]; } return AABox(); } void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool translucent) { + if (!_readyWhenAdded) { return; // bail asap } @@ -1785,19 +1805,6 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran gpu::Batch& batch = *(args->_batch); auto mode = args->_renderMode; - // render the part bounding box - #ifdef DEBUG_BOUNDING_PARTS - { - glm::vec4 cubeColor(1.0f,0.0f,0.0f,1.0f); - AABox partBounds = getPartBounds(meshIndex, partIndex); - - glm::mat4 translation = glm::translate(partBounds.calcCenter()); - glm::mat4 scale = glm::scale(partBounds.getDimensions()); - glm::mat4 modelToWorldMatrix = translation * scale; - batch.setModelTransform(modelToWorldMatrix); - DependencyManager::get()->renderWireCube(batch, 1.0f, cubeColor); - } - #endif //def DEBUG_BOUNDING_PARTS // Capture the view matrix once for the rendering of this model if (_transforms.empty()) { @@ -1824,6 +1831,29 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran bool hasLightmap = mesh.hasEmissiveTexture(); bool isSkinned = state.clusterMatrices.size() > 1; bool wireframe = isWireframe(); + + AABox partBounds = getPartBounds(meshIndex, partIndex); + bool inView = args->_viewFrustum->boxInFrustum(partBounds) != ViewFrustum::OUTSIDE; + + // render the part bounding box + #ifdef DEBUG_BOUNDING_PARTS + { + glm::vec4 cubeColor; + if (isSkinned) { + cubeColor = glm::vec4(0.0f,1.0f,1.0f,1.0f); + } else if (inView) { + cubeColor = glm::vec4(1.0f,0.0f,1.0f,1.0f); + } else { + cubeColor = glm::vec4(1.0f,1.0f,0.0f,1.0f); + } + + Transform transform; + transform.setTranslation(partBounds.calcCenter()); + transform.setScale(partBounds.getDimensions()); + batch.setModelTransform(transform); + DependencyManager::get()->renderWireCube(batch, 1.0f, cubeColor); + } + #endif //def DEBUG_BOUNDING_PARTS if (wireframe) { translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false; @@ -1977,7 +2007,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran } qint64 offset = _calculatedMeshPartOffet[QPair(meshIndex, partIndex)]; - + if (part.quadIndices.size() > 0) { batch.drawIndexed(gpu::QUADS, part.quadIndices.size(), offset); offset += part.quadIndices.size() * sizeof(int); From 2fc0233096d1ff65c301dae50ef8a6c539a2aaea Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 11 Jun 2015 20:50:31 -0700 Subject: [PATCH 29/94] hack to fix culling bugs for some subMesh parts in the windmill scene --- libraries/render-utils/src/Model.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 94a856073e..d313ef9b79 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1832,12 +1832,12 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran bool isSkinned = state.clusterMatrices.size() > 1; bool wireframe = isWireframe(); - AABox partBounds = getPartBounds(meshIndex, partIndex); - bool inView = args->_viewFrustum->boxInFrustum(partBounds) != ViewFrustum::OUTSIDE; - // render the part bounding box #ifdef DEBUG_BOUNDING_PARTS { + AABox partBounds = getPartBounds(meshIndex, partIndex); + bool inView = args->_viewFrustum->boxInFrustum(partBounds) != ViewFrustum::OUTSIDE; + glm::vec4 cubeColor; if (isSkinned) { cubeColor = glm::vec4(0.0f,1.0f,1.0f,1.0f); From 565bf8bcb269f2c17c5694f50da71a2a1734fc35 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 12 Jun 2015 15:41:37 +0200 Subject: [PATCH 30/94] Fix text entities wrapping --- .../src/RenderableTextEntityItem.cpp | 5 +++-- libraries/render-utils/src/TextRenderer3D.cpp | 21 ++++++++++--------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 8a67bb99f2..f61ed6f2c5 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -51,8 +51,9 @@ void RenderableTextEntityItem::render(RenderArgs* args) { transformToTopLeft.setScale(scale); // Scale to have the correct line height batch.setModelTransform(transformToTopLeft); - float leftMargin = 0.5f * _lineHeight, topMargin = 0.5f * _lineHeight; - glm::vec2 bounds = glm::vec2(dimensions.x - 2.0f * leftMargin, dimensions.y - 2.0f * topMargin); + float leftMargin = 0.1f * _lineHeight, topMargin = 0.1f * _lineHeight; + glm::vec2 bounds = glm::vec2(dimensions.x - 2.0f * leftMargin, + dimensions.y - 2.0f * topMargin); _textRenderer->draw(batch, leftMargin / scale, -topMargin / scale, _text, textColor, bounds / scale); } diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp index d081c0480a..7dbb7ea4fb 100644 --- a/libraries/render-utils/src/TextRenderer3D.cpp +++ b/libraries/render-utils/src/TextRenderer3D.cpp @@ -101,7 +101,7 @@ struct QuadBuilder { texMin); } QuadBuilder(const Glyph3D& glyph, const glm::vec2& offset) : - QuadBuilder(offset + glyph.offset - glm::vec2(0.0f, glyph.size.y), glyph.size, + QuadBuilder(offset + glm::vec2(glyph.offset.x, glyph.offset.y - glyph.size.y), glyph.size, glyph.texOffset, glyph.texSize) {} }; @@ -113,7 +113,7 @@ public: void read(QIODevice& path); glm::vec2 computeExtent(const QString& str) const; - float getRowHeight() const { return _rowHeight; } + float getRowHeight() const { return _fontSize; } // Render string to batch void drawString(gpu::Batch& batch, float x, float y, const QString& str, @@ -251,13 +251,14 @@ glm::vec2 Font3D::computeTokenExtent(const QString& token) const { glm::vec2 Font3D::computeExtent(const QString& str) const { glm::vec2 extent = glm::vec2(0.0f, 0.0f); - QStringList tokens = splitLines(str); - foreach(const QString& token, tokens) { - glm::vec2 tokenExtent = computeTokenExtent(token); - extent.x = std::max(tokenExtent.x, extent.x); + QStringList lines{ splitLines(str) }; + if (!lines.empty()) { + for(const auto& line : lines) { + glm::vec2 tokenExtent = computeTokenExtent(line); + extent.x = std::max(tokenExtent.x, extent.x); + } + extent.y = lines.count() * _fontSize; } - extent.y = tokens.count() * _rowHeight; - return extent; } @@ -393,7 +394,7 @@ void Font3D::drawString(gpu::Batch& batch, float x, float y, const QString& str, } if (isNewLine || forceNewLine) { // Character return, move the advance to a new line - advance = glm::vec2(x, advance.y - _rowHeight); + advance = glm::vec2(x, advance.y - _fontSize); if (isNewLine) { // No need to draw anything, go directly to next token @@ -413,7 +414,7 @@ void Font3D::drawString(gpu::Batch& batch, float x, float y, const QString& str, for (auto c : token) { auto glyph = _glyphs[c]; - QuadBuilder qd(glyph, advance - glm::vec2(0.0f, _fontSize)); + QuadBuilder qd(glyph, advance - glm::vec2(0.0f, _ascent)); _verticesBuffer->append(sizeof(QuadBuilder), (const gpu::Byte*)&qd); _numVertices += 4; From 228f8c2e61cd37dc3912b4dd41c62d9be18d6b6b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 12 Jun 2015 17:34:16 +0200 Subject: [PATCH 31/94] Fix a few items rendering without pipeline --- interface/src/avatar/Avatar.cpp | 7 ++++++- .../entities-renderer/src/RenderableLineEntityItem.cpp | 1 + .../src/RenderableParticleEffectEntityItem.cpp | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index dbe7d52a3f..71f4621205 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -328,6 +328,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo if (postLighting && glm::distance(DependencyManager::get()->getMyAvatar()->getPosition(), _position) < 10.0f) { auto geometryCache = DependencyManager::get(); + auto deferredLighting = DependencyManager::get(); // render pointing lasers glm::vec3 laserColor = glm::vec3(1.0f, 0.0f, 1.0f); @@ -354,6 +355,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo pointerTransform.setTranslation(position); pointerTransform.setRotation(rotation); batch->setModelTransform(pointerTransform); + deferredLighting->bindSimpleProgram(*batch); geometryCache->renderLine(*batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor); } } @@ -376,6 +378,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo pointerTransform.setTranslation(position); pointerTransform.setRotation(rotation); batch->setModelTransform(pointerTransform); + deferredLighting->bindSimpleProgram(*batch); geometryCache->renderLine(*batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor); } } @@ -462,7 +465,8 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo Transform transform; transform.setTranslation(position); batch->setModelTransform(transform); - DependencyManager::get()->renderSphere(*batch, LOOK_AT_INDICATOR_RADIUS, 15, 15, LOOK_AT_INDICATOR_COLOR); + DependencyManager::get()->renderSolidSphere(*batch, LOOK_AT_INDICATOR_RADIUS + , 15, 15, LOOK_AT_INDICATOR_COLOR); } } @@ -494,6 +498,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition, boo _voiceSphereID = DependencyManager::get()->allocateID(); } + DependencyManager::get()->bindSimpleProgram(*batch); DependencyManager::get()->renderSphere(*batch, sphereRadius, 15, 15, glm::vec4(SPHERE_COLOR[0], SPHERE_COLOR[1], SPHERE_COLOR[2], 1.0f - angle / MAX_SPHERE_ANGLE), true, _voiceSphereID); diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp index 4e5de331bb..65407c74e7 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp @@ -49,6 +49,7 @@ void RenderableLineEntityItem::render(RenderArgs* args) { batch._glLineWidth(getLineWidth()); if (getLinePoints().size() > 1) { + DependencyManager::get()->bindSimpleProgram(batch); DependencyManager::get()->renderVertices(batch, gpu::LINE_STRIP, _lineVerticesID); } batch._glLineWidth(1.0f); diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 6a50cbf1cb..91c89bb183 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -53,6 +53,7 @@ void RenderableParticleEffectEntityItem::render(RenderArgs* args) { batch.setUniformTexture(0, _texture->getGPUTexture()); } batch.setModelTransform(getTransformToCenter()); + DependencyManager::get()->bindSimpleProgram(batch); DependencyManager::get()->renderVertices(batch, gpu::QUADS, _cacheID); }; From c17ae593f0b8520c274a36b3f77c30d2c41c7f3d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 12 Jun 2015 09:04:16 -0700 Subject: [PATCH 32/94] CR feedback --- libraries/render-utils/src/Model.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index d313ef9b79..2379058b92 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1019,7 +1019,7 @@ AABox Model::calculateScaledOffsetAABox(const AABox& box) const { glm::vec3 Model::calculateScaledOffsetPoint(const glm::vec3& point) const { // we need to include any fst scaling, translation, and rotation, which is captured in the offset matrix - glm::vec3 offsetPoint = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(point, 1.0f)); /// should this be point??? + glm::vec3 offsetPoint = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(point, 1.0f)); glm::vec3 scaledPoint = ((offsetPoint + _offset) * _scale); glm::vec3 rotatedPoint = _rotation * scaledPoint; glm::vec3 translatedPoint = rotatedPoint + _translation; @@ -1840,11 +1840,11 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran glm::vec4 cubeColor; if (isSkinned) { - cubeColor = glm::vec4(0.0f,1.0f,1.0f,1.0f); + cubeColor = glm::vec4(0.0f, 1.0f, 1.0f, 1.0f); } else if (inView) { - cubeColor = glm::vec4(1.0f,0.0f,1.0f,1.0f); + cubeColor = glm::vec4(1.0f, 0.0f, 1.0f, 1.0f); } else { - cubeColor = glm::vec4(1.0f,1.0f,0.0f,1.0f); + cubeColor = glm::vec4(1.0f, 1.0f, 0.0f, 1.0f); } Transform transform; From 128b086e82adbc45875e5d928474643c58a99430 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 12 Jun 2015 10:25:34 -0700 Subject: [PATCH 33/94] don't build the polyvox examples --- cmake/externals/polyvox/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/externals/polyvox/CMakeLists.txt b/cmake/externals/polyvox/CMakeLists.txt index 28aec6dab7..c28a8e1319 100644 --- a/cmake/externals/polyvox/CMakeLists.txt +++ b/cmake/externals/polyvox/CMakeLists.txt @@ -5,7 +5,7 @@ ExternalProject_Add( ${EXTERNAL_NAME} URL http://hifi-public.s3.amazonaws.com/dependencies/polyvox.zip URL_MD5 904b840328278c9b36fa7a14be730c34 - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= + CMAKE_ARGS -DENABLE_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX:PATH= BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build LOG_DOWNLOAD 1 LOG_CONFIGURE 1 From 926283956412d1e35fc1ba319f2c513803da2c77 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Fri, 12 Jun 2015 10:33:52 -0700 Subject: [PATCH 34/94] Fix broken identity transforms on OSX --- libraries/gpu/src/gpu/GLBackendTransform.cpp | 25 +++++++------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/libraries/gpu/src/gpu/GLBackendTransform.cpp b/libraries/gpu/src/gpu/GLBackendTransform.cpp index 3f760e4cc8..4e524ec24a 100755 --- a/libraries/gpu/src/gpu/GLBackendTransform.cpp +++ b/libraries/gpu/src/gpu/GLBackendTransform.cpp @@ -133,11 +133,11 @@ void GLBackend::updateTransform() { } if (_transform._invalidModel || _transform._invalidView) { + if (_transform._lastMode != GL_MODELVIEW) { + glMatrixMode(GL_MODELVIEW); + _transform._lastMode = GL_MODELVIEW; + } if (!_transform._model.isIdentity()) { - if (_transform._lastMode != GL_MODELVIEW) { - glMatrixMode(GL_MODELVIEW); - _transform._lastMode = GL_MODELVIEW; - } Transform::Mat4 modelView; if (!_transform._view.isIdentity()) { Transform mvx; @@ -147,19 +147,12 @@ void GLBackend::updateTransform() { _transform._model.getMatrix(modelView); } glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView)); + } else if (!_transform._view.isIdentity()) { + Transform::Mat4 modelView; + _transform._view.getInverseMatrix(modelView); + glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView)); } else { - if (!_transform._view.isIdentity()) { - if (_transform._lastMode != GL_MODELVIEW) { - glMatrixMode(GL_MODELVIEW); - _transform._lastMode = GL_MODELVIEW; - } - Transform::Mat4 modelView; - _transform._view.getInverseMatrix(modelView); - glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView)); - } else { - // TODO: eventually do something about the matrix when neither view nor model is specified? - // glLoadIdentity(); - } + glLoadIdentity(); } (void) CHECK_GL_ERROR(); } From f3d3bd7bec9ab12c83da8d9013f9a510886f3699 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 12 Jun 2015 19:44:15 +0200 Subject: [PATCH 35/94] Remove _rowHeight --- libraries/render-utils/src/TextRenderer3D.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp index 7dbb7ea4fb..5a6ec89c4f 100644 --- a/libraries/render-utils/src/TextRenderer3D.cpp +++ b/libraries/render-utils/src/TextRenderer3D.cpp @@ -139,7 +139,6 @@ private: // Font characteristics QString _family; float _fontSize = 0.0f; - float _rowHeight = 0.0f; float _leading = 0.0f; float _ascent = 0.0f; float _descent = 0.0f; @@ -289,8 +288,7 @@ void Font3D::read(QIODevice& in) { readStream(in, _descent); readStream(in, _spaceWidth); _fontSize = _ascent + _descent; - _rowHeight = _fontSize + _leading; - + // Read character count uint16_t count; readStream(in, count); From 26dd0679827a59bfe8cc25a3a22c692c9fe07ba6 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 12 Jun 2015 19:44:40 +0200 Subject: [PATCH 36/94] Advance with _leading on y --- libraries/render-utils/src/TextRenderer3D.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp index 5a6ec89c4f..4b59877582 100644 --- a/libraries/render-utils/src/TextRenderer3D.cpp +++ b/libraries/render-utils/src/TextRenderer3D.cpp @@ -392,7 +392,7 @@ void Font3D::drawString(gpu::Batch& batch, float x, float y, const QString& str, } if (isNewLine || forceNewLine) { // Character return, move the advance to a new line - advance = glm::vec2(x, advance.y - _fontSize); + advance = glm::vec2(x, advance.y - _leading); if (isNewLine) { // No need to draw anything, go directly to next token From 30ae78e3b6a256bf77ad5b6a1cdd240fa2df6e74 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 12 Jun 2015 19:50:32 +0200 Subject: [PATCH 37/94] Rename getRowHeight --- .../entities-renderer/src/RenderableTextEntityItem.cpp | 2 +- libraries/render-utils/src/TextRenderer3D.cpp | 6 +++--- libraries/render-utils/src/TextRenderer3D.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index f61ed6f2c5..d06ffb9400 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -47,7 +47,7 @@ void RenderableTextEntityItem::render(RenderArgs* args) { glm::vec3 maxCorner = glm::vec3(dimensions.x, 0.0f, SLIGHTLY_BEHIND); DependencyManager::get()->renderQuad(batch, minCorner, maxCorner, backgroundColor); - float scale = _lineHeight / _textRenderer->getRowHeight(); + float scale = _lineHeight / _textRenderer->getFontSize(); transformToTopLeft.setScale(scale); // Scale to have the correct line height batch.setModelTransform(transformToTopLeft); diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp index 4b59877582..0eb560bf72 100644 --- a/libraries/render-utils/src/TextRenderer3D.cpp +++ b/libraries/render-utils/src/TextRenderer3D.cpp @@ -113,7 +113,7 @@ public: void read(QIODevice& path); glm::vec2 computeExtent(const QString& str) const; - float getRowHeight() const { return _fontSize; } + float getFontSize() const { return _fontSize; } // Render string to batch void drawString(gpu::Batch& batch, float x, float y, const QString& str, @@ -476,9 +476,9 @@ glm::vec2 TextRenderer3D::computeExtent(const QString& str) const { return glm::vec2(0.0f, 0.0f); } -float TextRenderer3D::getRowHeight() const { +float TextRenderer3D::getFontSize() const { if (_font) { - return _font->getRowHeight(); + return _font->getFontSize(); } return 0.0f; } diff --git a/libraries/render-utils/src/TextRenderer3D.h b/libraries/render-utils/src/TextRenderer3D.h index 8f55d0c977..e61203b06f 100644 --- a/libraries/render-utils/src/TextRenderer3D.h +++ b/libraries/render-utils/src/TextRenderer3D.h @@ -50,7 +50,7 @@ public: ~TextRenderer3D(); glm::vec2 computeExtent(const QString& str) const; - float getRowHeight() const; + float getFontSize() const; void draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color = glm::vec4(-1.0f), const glm::vec2& bounds = glm::vec2(-1.0f)); From 4ed147418a540c77025dd1abb5fcbedfbc935dab Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 12 Jun 2015 11:19:30 -0700 Subject: [PATCH 38/94] quick hack to fix stats texture bleed through --- interface/src/ui/Stats.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 32df75c46d..8d098b4dc8 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -161,6 +161,10 @@ void Stats::drawBackground(unsigned int rgba, int x, int y, int width, int heigh ((rgba >> 8) & 0xff) / 255.0f, (rgba & 0xff) / 255.0f); + // FIX ME: is this correct? It seems to work to fix textures bleeding into us... + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + DependencyManager::get()->renderQuad(x, y, width, height, color); } From 87951ff7b3bfd19f8d824fda7dfa11553aa49b49 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 12 Jun 2015 11:19:30 -0700 Subject: [PATCH 39/94] quick hack to fix stats texture bleed through --- interface/src/ui/Stats.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 32df75c46d..8d098b4dc8 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -161,6 +161,10 @@ void Stats::drawBackground(unsigned int rgba, int x, int y, int width, int heigh ((rgba >> 8) & 0xff) / 255.0f, (rgba & 0xff) / 255.0f); + // FIX ME: is this correct? It seems to work to fix textures bleeding into us... + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + DependencyManager::get()->renderQuad(x, y, width, height, color); } From 7d7db65fd1550c0944c962c9a0313a69ea153b49 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 12 Jun 2015 11:42:38 -0700 Subject: [PATCH 40/94] fix avatar mesh boxes not staying in sync with avatar position --- libraries/render-utils/src/Model.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 2379058b92..865c225445 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1783,8 +1783,13 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) { // // return calculateScaledOffsetAABox(_calculatedMeshPartBoxes[QPair(meshIndex, partIndex)]); // + // NOTE: we also don't want to use the _calculatedMeshBoxes[] because they don't handle avatar moving correctly + // without recalculating them... + // return _calculatedMeshBoxes[meshIndex]; + // // If we not skinned use the bounds of the subMesh for all it's parts - return _calculatedMeshBoxes[meshIndex]; + const FBXMesh& mesh = _geometry->getFBXGeometry().meshes.at(meshIndex); + return calculateScaledOffsetExtents(mesh.meshExtents); } return AABox(); } From 7ee609396c5045ae976b8b72dee5fddbacdb974c Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Fri, 12 Jun 2015 11:58:43 -0700 Subject: [PATCH 41/94] added lifetime to painted lines to prevent huge model.json files when lots of people draw in one domain. Got rid of gaps in lines when switching line entities --- examples/paint.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/examples/paint.js b/examples/paint.js index 8bba4e2571..c0cc93afc7 100644 --- a/examples/paint.js +++ b/examples/paint.js @@ -12,8 +12,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // Script.include('lineRider.js') -var MAX_POINTS_PER_LINE = 80; - +var MAX_POINTS_PER_LINE = 30; +var LINE_LIFETIME = 60 * 5 //5 minute lifetime var colorPalette = [{ red: 236, @@ -120,10 +120,12 @@ function MousePaint() { y: 10, z: 10 }, - lineWidth: LINE_WIDTH + lineWidth: LINE_WIDTH, + lifetime: LINE_LIFETIME }); points = []; if (point) { + points.push(point); path.push(point); } @@ -133,22 +135,22 @@ function MousePaint() { function mouseMoveEvent(event) { + if (!isDrawing) { + return; + } var pickRay = Camera.computePickRay(event.x, event.y); var addVector = Vec3.multiply(Vec3.normalize(pickRay.direction), DRAWING_DISTANCE); var point = Vec3.sum(Camera.getPosition(), addVector); + points.push(point); + path.push(point); Entities.editEntity(line, { linePoints: points }); Entities.editEntity(brush, { position: point }); - if (!isDrawing) { - return; - } - points.push(point); - path.push(point); if (points.length === MAX_POINTS_PER_LINE) { //We need to start a new line! @@ -253,7 +255,6 @@ function HydraPaint() { var maxLineWidth = 10; var currentLineWidth = minLineWidth; var MIN_PAINT_TRIGGER_THRESHOLD = .01; - var LINE_LIFETIME = 20; var COLOR_CHANGE_TIME_FACTOR = 0.1; var RIGHT_BUTTON_1 = 7 @@ -330,7 +331,7 @@ function HydraPaint() { z: 10 }, lineWidth: 5, - // lifetime: LINE_LIFETIME + lifetime: LINE_LIFETIME }); this.points = []; if (point) { From 546442ff9a26f5dce6325e502ebe8e4435e17e46 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 12 Jun 2015 12:32:40 -0700 Subject: [PATCH 42/94] Fix sound URLs Three slashes needed for Windows. --- examples/dialTone.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/dialTone.js b/examples/dialTone.js index b55e654f25..adbbd26acb 100644 --- a/examples/dialTone.js +++ b/examples/dialTone.js @@ -11,8 +11,8 @@ // // setup the local sound we're going to use -var connectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/hello.wav"); -var disconnectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/goodbye.wav"); +var connectSound = SoundCache.getSound("file:///" + Paths.resources + "sounds/hello.wav"); +var disconnectSound = SoundCache.getSound("file:///" + Paths.resources + "sounds/goodbye.wav"); // setup the options needed for that sound var connectSoundOptions = { From f0b6b0f5481ee2bebfa8f18377dd509c08a44618 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 12 Jun 2015 12:33:17 -0700 Subject: [PATCH 43/94] Play sound upon microphone being muted --- examples/dialTone.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/examples/dialTone.js b/examples/dialTone.js index adbbd26acb..eb1fc5ec1c 100644 --- a/examples/dialTone.js +++ b/examples/dialTone.js @@ -13,17 +13,24 @@ // setup the local sound we're going to use var connectSound = SoundCache.getSound("file:///" + Paths.resources + "sounds/hello.wav"); var disconnectSound = SoundCache.getSound("file:///" + Paths.resources + "sounds/goodbye.wav"); +var micMutedSound = SoundCache.getSound("file:///" + Paths.resources + "sounds/goodbye.wav"); // setup the options needed for that sound -var connectSoundOptions = { +var soundOptions = { localOnly: true }; // play the sound locally once we get the first audio packet from a mixer Audio.receivedFirstPacket.connect(function(){ - Audio.playSound(connectSound, connectSoundOptions); + Audio.playSound(connectSound, soundOptions); }); Audio.disconnected.connect(function(){ - Audio.playSound(disconnectSound, connectSoundOptions); + Audio.playSound(disconnectSound, soundOptions); +}); + +AudioDevice.muteToggled.connect(function () { + if (AudioDevice.getMuted()) { + Audio.playSound(micMutedSound, soundOptions); + } }); From c369e305f84a01b2c95566b29e5319329689ed5d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 12 Jun 2015 12:33:30 -0700 Subject: [PATCH 44/94] Include dialTone.js in default scripts --- examples/defaultScripts.js | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index 61bed8d9b1..b6462b7fc3 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -19,3 +19,4 @@ Script.load("users.js"); Script.load("grab.js"); Script.load("pointer.js"); Script.load("directory.js"); +Script.load("dialTone.js"); From e16116ce7891c486e2449d2160e2201bd80a3b20 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 12 Jun 2015 12:33:42 -0700 Subject: [PATCH 45/94] Have auto mute mic upon losing camera tracking disabled by default --- interface/src/Menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 6242318170..eafcc4c8e8 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -407,7 +407,7 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::MuteFaceTracking, Qt::CTRL | Qt::SHIFT | Qt::Key_F, true, // DDE face tracking is on by default qApp, SLOT(toggleFaceTrackerMute())); - addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::AutoMuteAudio, 0, true); + addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::AutoMuteAudio, 0, false); #endif auto avatarManager = DependencyManager::get(); From 0cdc2b53fe7815bba21a98ecbdfd91bcbb2898f5 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 12 Jun 2015 12:57:24 -0700 Subject: [PATCH 46/94] Revert "Merge pull request #5106 from jherico/render_cursor" This reverts commit 4d18bd7cec710b5188d1f6ad5c58010117d7a77d, reversing changes made to 24fda9a7336e4eff1d1310dd3359dd6cbbb6acb5. --- interface/resources/images/arrow.png | Bin 3562 -> 0 bytes interface/src/Application.cpp | 3 +- interface/src/devices/OculusManager.cpp | 5 +- interface/src/devices/TV3DManager.cpp | 18 +- interface/src/ui/ApplicationOverlay.cpp | 666 +++++++++++------- interface/src/ui/ApplicationOverlay.h | 53 +- libraries/render-utils/src/GeometryCache.cpp | 15 - libraries/render-utils/src/GeometryCache.h | 3 - .../render-utils/src/standardDrawTexture.slf | 24 - .../src/standardTransformPNTC.slv | 33 - 10 files changed, 481 insertions(+), 339 deletions(-) delete mode 100644 interface/resources/images/arrow.png delete mode 100644 libraries/render-utils/src/standardDrawTexture.slf delete mode 100644 libraries/render-utils/src/standardTransformPNTC.slv diff --git a/interface/resources/images/arrow.png b/interface/resources/images/arrow.png deleted file mode 100644 index 408881b5856eb47a30875e3e56d5864721161587..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3562 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 R^nCyTaI$x`E7(oB_+R=!c8UN1 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 56fe4188e9..0f0abb3996 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -955,7 +955,6 @@ void Application::paintGL() { glPushMatrix(); glLoadIdentity(); displaySide(&renderArgs, _myCamera); - _applicationOverlay.displayOverlayTexture(&renderArgs); glPopMatrix(); if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { @@ -972,6 +971,8 @@ void Application::paintGL() { 0, 0, _glWidget->getDeviceSize().width(), _glWidget->getDeviceSize().height(), GL_COLOR_BUFFER_BIT, GL_NEAREST); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + + _applicationOverlay.displayOverlayTexture(); } if (!OculusManager::isConnected() || OculusManager::allowSwap()) { diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 414c7f6199..a7383ae4bb 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -615,10 +615,12 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const renderArgs->_renderSide = RenderArgs::MONO; qApp->displaySide(renderArgs, *_camera, false); - qApp->getApplicationOverlay().displayOverlayTextureHmd(renderArgs, *_camera); + qApp->getApplicationOverlay().displayOverlayTextureHmd(*_camera); }); _activeEye = ovrEye_Count; + glPopMatrix(); + gpu::FramebufferPointer finalFbo; //Bind the output texture from the glow shader. If glow effect is disabled, we just grab the texture if (Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect)) { @@ -629,7 +631,6 @@ void OculusManager::display(QGLWidget * glCanvas, RenderArgs* renderArgs, const finalFbo = DependencyManager::get()->getPrimaryFramebuffer(); glBindFramebuffer(GL_FRAMEBUFFER, 0); } - glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); diff --git a/interface/src/devices/TV3DManager.cpp b/interface/src/devices/TV3DManager.cpp index b0a2ff7e3b..09edb03e5a 100644 --- a/interface/src/devices/TV3DManager.cpp +++ b/interface/src/devices/TV3DManager.cpp @@ -12,7 +12,6 @@ #include "InterfaceConfig.h" #include -#include #include #include "gpu/GLBackend.h" @@ -107,20 +106,21 @@ void TV3DManager::display(RenderArgs* renderArgs, Camera& whichCamera) { _activeEye = &eye; glViewport(portalX, portalY, portalW, portalH); glScissor(portalX, portalY, portalW, portalH); - - glm::mat4 projection = glm::frustum(eye.left, eye.right, eye.bottom, eye.top, nearZ, farZ); - float fov = atan(1.0f / projection[1][1]); - projection = glm::translate(projection, vec3(eye.modelTranslation, 0, 0)); - eyeCamera.setProjection(projection); - glMatrixMode(GL_PROJECTION); glLoadIdentity(); // reset projection matrix - glLoadMatrixf(glm::value_ptr(projection)); + glFrustum(eye.left, eye.right, eye.bottom, eye.top, nearZ, farZ); // set left view frustum + GLfloat p[4][4]; + // Really? + glGetFloatv(GL_PROJECTION_MATRIX, &(p[0][0])); + float cotangent = p[1][1]; + GLfloat fov = atan(1.0f / cotangent); + glTranslatef(eye.modelTranslation, 0.0, 0.0); // translate to cancel parallax + glMatrixMode(GL_MODELVIEW); glLoadIdentity(); renderArgs->_renderSide = RenderArgs::MONO; qApp->displaySide(renderArgs, eyeCamera, false); - qApp->getApplicationOverlay().displayOverlayTexture(renderArgs); + qApp->getApplicationOverlay().displayOverlayTextureStereo(whichCamera, _aspect, fov); _activeEye = NULL; }, [&]{ // render right side view diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index ba0d3a60a9..0c1783f8ac 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -14,16 +14,13 @@ #include #include -#include - #include -#include #include +#include #include #include -#include -#include #include +#include #include "AudioClient.h" #include "audio/AudioIOStatsRenderer.h" @@ -36,9 +33,6 @@ #include "Util.h" #include "ui/Stats.h" -#include "../../libraries/render-utils/standardTransformPNTC_vert.h" -#include "../../libraries/render-utils/standardDrawTexture_frag.h" - // Used to animate the magnification windows const float MAG_SPEED = 0.08f; @@ -120,6 +114,27 @@ bool raySphereIntersect(const glm::vec3 &dir, const glm::vec3 &origin, float r, } } +void ApplicationOverlay::renderReticle(glm::quat orientation, float alpha) { + glPushMatrix(); { + glm::vec3 axis = glm::axis(orientation); + glRotatef(glm::degrees(glm::angle(orientation)), axis.x, axis.y, axis.z); + glm::vec3 topLeft = getPoint(reticleSize / 2.0f, -reticleSize / 2.0f); + glm::vec3 topRight = getPoint(-reticleSize / 2.0f, -reticleSize / 2.0f); + glm::vec3 bottomLeft = getPoint(reticleSize / 2.0f, reticleSize / 2.0f); + glm::vec3 bottomRight = getPoint(-reticleSize / 2.0f, reticleSize / 2.0f); + + // TODO: this version of renderQuad() needs to take a color + glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], alpha }; + + + + DependencyManager::get()->renderQuad(topLeft, bottomLeft, bottomRight, topRight, + glm::vec2(0.0f, 0.0f), glm::vec2(1.0f, 0.0f), + glm::vec2(1.0f, 1.0f), glm::vec2(0.0f, 1.0f), + reticleColor, _reticleQuad); + } glPopMatrix(); +} + ApplicationOverlay::ApplicationOverlay() : _textureFov(glm::radians(DEFAULT_HMD_UI_ANGULAR_SIZE)), _textureAspectRatio(1.0f), @@ -133,8 +148,7 @@ ApplicationOverlay::ApplicationOverlay() : _previousMagnifierBottomLeft(), _previousMagnifierBottomRight(), _previousMagnifierTopLeft(), - _previousMagnifierTopRight(), - _framebufferObject(nullptr) + _previousMagnifierTopRight() { memset(_reticleActive, 0, sizeof(_reticleActive)); memset(_magActive, 0, sizeof(_reticleActive)); @@ -181,17 +195,16 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { //Handle fading and deactivation/activation of UI // Render 2D overlay + glMatrixMode(GL_PROJECTION); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - buildFramebufferObject(); - - _framebufferObject->bind(); + _overlays.buildFramebufferObject(); + _overlays.bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, size.x, size.y); - glMatrixMode(GL_PROJECTION); glPushMatrix(); { const float NEAR_CLIP = -10000; const float FAR_CLIP = 10000; @@ -213,22 +226,6 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { renderPointers(); renderDomainConnectionStatusBorder(); - if (_newUiTexture) { - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glBindTexture(GL_TEXTURE_2D, _newUiTexture); - DependencyManager::get()->renderUnitQuad(); - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); - } - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); } glPopMatrix(); @@ -238,161 +235,259 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { glEnable(GL_LIGHTING); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - _framebufferObject->release(); + _overlays.release(); } -gpu::PipelinePointer ApplicationOverlay::getDrawPipeline() { - if (!_standardDrawPipeline) { - auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(standardTransformPNTC_vert))); - auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(standardDrawTexture_frag))); - auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); - gpu::Shader::makeProgram((*program)); - - auto state = gpu::StatePointer(new gpu::State()); - - // enable decal blend - state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); - - _standardDrawPipeline.reset(gpu::Pipeline::create(program, state)); +// A quick and dirty solution for compositing the old overlay +// texture with the new one +template +void with_each_texture(GLuint firstPassTexture, GLuint secondPassTexture, F f) { + glEnable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + if (firstPassTexture) { + glBindTexture(GL_TEXTURE_2D, firstPassTexture); + f(); } - - return _standardDrawPipeline; -} - -void ApplicationOverlay::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); + if (secondPassTexture) { + glBindTexture(GL_TEXTURE_2D, secondPassTexture); + f(); } - batch.setUniformTexture(0, _cursors[iconId]); + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); } -#define CURSOR_PIXEL_SIZE 32.0f - // Draws the FBO texture for the screen -void ApplicationOverlay::displayOverlayTexture(RenderArgs* renderArgs) { +void ApplicationOverlay::displayOverlayTexture() { + if (_alpha == 0.0f) { + return; + } + glMatrixMode(GL_PROJECTION); + glPushMatrix(); { + glLoadIdentity(); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glViewport(0, 0, qApp->getDeviceSize().width(), qApp->getDeviceSize().height()); + + static const glm::vec2 topLeft(-1, 1); + static const glm::vec2 bottomRight(1, -1); + static const glm::vec2 texCoordTopLeft(0.0f, 1.0f); + static const glm::vec2 texCoordBottomRight(1.0f, 0.0f); + with_each_texture(_overlays.getTexture(), _newUiTexture, [&] { + DependencyManager::get()->renderQuad( + topLeft, bottomRight, + texCoordTopLeft, texCoordBottomRight, + glm::vec4(1.0f, 1.0f, 1.0f, _alpha)); + }); + + if (!_crosshairTexture) { + _crosshairTexture = DependencyManager::get()-> + getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png"); + } + + //draw the mouse pointer + glm::vec2 canvasSize = qApp->getCanvasSize(); + glm::vec2 mouseSize = 32.0f / canvasSize; + auto mouseTopLeft = topLeft * mouseSize; + auto mouseBottomRight = bottomRight * mouseSize; + vec2 mousePosition = vec2(qApp->getMouseX(), qApp->getMouseY()); + mousePosition /= canvasSize; + mousePosition *= 2.0f; + mousePosition -= 1.0f; + mousePosition.y *= -1.0f; + + glEnable(GL_TEXTURE_2D); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); + glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; + DependencyManager::get()->renderQuad( + mouseTopLeft + mousePosition, mouseBottomRight + mousePosition, + texCoordTopLeft, texCoordBottomRight, + reticleColor); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + glDisable(GL_TEXTURE_2D); + } glPopMatrix(); +} + +// Draws the FBO texture for Oculus rift. +void ApplicationOverlay::displayOverlayTextureHmd(Camera& whichCamera) { + if (_alpha == 0.0f) { + return; + } + + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glDisable(GL_LIGHTING); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.01f); + + + //Update and draw the magnifiers + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + const glm::quat& orientation = myAvatar->getOrientation(); + // Always display the HMD overlay relative to the camera position but + // remove the HMD pose offset. This results in an overlay that sticks with you + // even in third person mode, but isn't drawn at a fixed distance. + glm::vec3 position = whichCamera.getPosition(); + position -= qApp->getCamera()->getHmdPosition(); + const float scale = myAvatar->getScale() * _oculusUIRadius; + +// glm::vec3 eyeOffset = setEyeOffsetPosition; + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); { + glTranslatef(position.x, position.y, position.z); + glm::mat4 rotation = glm::toMat4(orientation); + glMultMatrixf(&rotation[0][0]); + glScalef(scale, scale, scale); + for (int i = 0; i < NUMBER_OF_RETICLES; i++) { + + if (_magActive[i]) { + _magSizeMult[i] += MAG_SPEED; + if (_magSizeMult[i] > 1.0f) { + _magSizeMult[i] = 1.0f; + } + } else { + _magSizeMult[i] -= MAG_SPEED; + if (_magSizeMult[i] < 0.0f) { + _magSizeMult[i] = 0.0f; + } + } + + if (_magSizeMult[i] > 0.0f) { + //Render magnifier, but dont show border for mouse magnifier + glm::vec2 projection = screenToOverlay(glm::vec2(_reticlePosition[MOUSE].x(), + _reticlePosition[MOUSE].y())); + with_each_texture(_overlays.getTexture(), 0, [&] { + renderMagnifier(projection, _magSizeMult[i], i != MOUSE); + }); + } + } + + glDepthMask(GL_FALSE); + glDisable(GL_ALPHA_TEST); + + static float textureFOV = 0.0f, textureAspectRatio = 1.0f; + if (textureFOV != _textureFov || + textureAspectRatio != _textureAspectRatio) { + textureFOV = _textureFov; + textureAspectRatio = _textureAspectRatio; + + _overlays.buildVBO(_textureFov, _textureAspectRatio, 80, 80); + } + + with_each_texture(_overlays.getTexture(), _newUiTexture, [&] { + _overlays.render(); + }); + + if (!Application::getInstance()->isMouseHidden()) { + renderPointersOculus(); + } + glDepthMask(GL_TRUE); + glDisable(GL_TEXTURE_2D); + + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + glEnable(GL_LIGHTING); + } glPopMatrix(); +} + +// Draws the FBO texture for 3DTV. +void ApplicationOverlay::displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov) { if (_alpha == 0.0f) { return; } - renderArgs->_context->syncCache(); - - gpu::Batch batch; - Transform model; - //DependencyManager::get()->bindSimpleProgram(batch, true); - batch.setPipeline(getDrawPipeline()); - batch.setModelTransform(Transform()); - batch.setProjectionTransform(mat4()); - batch.setViewTransform(model); - batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture()); - batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - DependencyManager::get()->renderUnitQuad(batch, vec4(vec3(1), _alpha)); + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + const glm::vec3& viewMatrixTranslation = qApp->getViewMatrixTranslation(); + + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + glEnable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + + glMatrixMode(GL_MODELVIEW); + + glPushMatrix(); + glLoadIdentity(); + // Transform to world space + glm::quat rotation = whichCamera.getRotation(); + glm::vec3 axis2 = glm::axis(rotation); + glRotatef(-glm::degrees(glm::angle(rotation)), axis2.x, axis2.y, axis2.z); + glTranslatef(viewMatrixTranslation.x, viewMatrixTranslation.y, viewMatrixTranslation.z); + + // Translate to the front of the camera + glm::vec3 pos = whichCamera.getPosition(); + glm::quat rot = myAvatar->getOrientation(); + glm::vec3 axis = glm::axis(rot); + + glTranslatef(pos.x, pos.y, pos.z); + glRotatef(glm::degrees(glm::angle(rot)), axis.x, axis.y, axis.z); + + glm::vec4 overlayColor = {1.0f, 1.0f, 1.0f, _alpha}; + + //Render + const GLfloat distance = 1.0f; + + const GLfloat halfQuadHeight = distance * tan(fov); + const GLfloat halfQuadWidth = halfQuadHeight * aspectRatio; + const GLfloat quadWidth = halfQuadWidth * 2.0f; + const GLfloat quadHeight = halfQuadHeight * 2.0f; + + GLfloat x = -halfQuadWidth; + GLfloat y = -halfQuadHeight; + glDisable(GL_DEPTH_TEST); + with_each_texture(_overlays.getTexture(), _newUiTexture, [&] { + DependencyManager::get()->renderQuad(glm::vec3(x, y + quadHeight, -distance), + glm::vec3(x + quadWidth, y + quadHeight, -distance), + glm::vec3(x + quadWidth, y, -distance), + glm::vec3(x, y, -distance), + glm::vec2(0.0f, 1.0f), glm::vec2(1.0f, 1.0f), + glm::vec2(1.0f, 0.0f), glm::vec2(0.0f, 0.0f), + overlayColor); + }); + + if (!_crosshairTexture) { + _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + + "images/sixense-reticle.png"); + } + //draw the mouse pointer + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); glm::vec2 canvasSize = qApp->getCanvasSize(); - - // Get the mouse coordinates and convert to NDC [-1, 1] - vec2 mousePosition = vec2(qApp->getMouseX(), qApp->getMouseY()); - mousePosition /= canvasSize; - mousePosition *= 2.0f; - mousePosition -= 1.0f; - mousePosition.y *= -1.0f; - model.setTranslation(vec3(mousePosition, 0)); - glm::vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize; - model.setScale(vec3(mouseSize, 1.0f)); - batch.setModelTransform(model); - bindCursorTexture(batch); + const float reticleSize = 40.0f / canvasSize.x * quadWidth; + x -= reticleSize / 2.0f; + y += reticleSize / 2.0f; + const float mouseX = (qApp->getMouseX() / (float)canvasSize.x) * quadWidth; + const float mouseY = (1.0 - (qApp->getMouseY() / (float)canvasSize.y)) * quadHeight; + glm::vec4 reticleColor = { RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], 1.0f }; - DependencyManager::get()->renderUnitQuad(batch, vec4(1)); - renderArgs->_context->render(batch); + + DependencyManager::get()->renderQuad(glm::vec3(x + mouseX, y + mouseY, -distance), + glm::vec3(x + mouseX + reticleSize, y + mouseY, -distance), + glm::vec3(x + mouseX + reticleSize, y + mouseY - reticleSize, -distance), + glm::vec3(x + mouseX, y + mouseY - reticleSize, -distance), + glm::vec2(0.0f, 0.0f), glm::vec2(1.0f, 0.0f), + glm::vec2(1.0f, 1.0f), glm::vec2(0.0f, 1.0f), + reticleColor, _reticleQuad); + + glEnable(GL_DEPTH_TEST); + + glPopMatrix(); + + glDepthMask(GL_TRUE); + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + glEnable(GL_LIGHTING); } - -static gpu::BufferPointer _hemiVertices; -static gpu::BufferPointer _hemiIndices; -static int _hemiIndexCount{ 0 }; - -glm::vec2 getPolarCoordinates(const PalmData& palm) { - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - auto avatarOrientation = myAvatar->getOrientation(); - auto eyePos = myAvatar->getDefaultEyePosition(); - glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm); - // Direction of the tip relative to the eye - glm::vec3 tipDirection = tip - eyePos; - // orient into avatar space - tipDirection = glm::inverse(avatarOrientation) * tipDirection; - // Normalize for trig functions - tipDirection = glm::normalize(tipDirection); - // Convert to polar coordinates - glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y)); - return polar; -} - -// Draws the FBO texture for Oculus rift. -void ApplicationOverlay::displayOverlayTextureHmd(RenderArgs* renderArgs, Camera& whichCamera) { - if (_alpha == 0.0f) { - return; - } - - renderArgs->_context->syncCache(); - - gpu::Batch batch; - batch.setPipeline(getDrawPipeline()); - batch._glDisable(GL_DEPTH_TEST); - batch._glDisable(GL_CULL_FACE); - batch._glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture()); - batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - batch._glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - batch.setProjectionTransform(whichCamera.getProjection()); - batch.setViewTransform(Transform()); - - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - const quat& avatarOrientation = myAvatar->getOrientation(); - quat hmdOrientation = qApp->getCamera()->getHmdRotation(); - vec3 hmdPosition = glm::inverse(avatarOrientation) * qApp->getCamera()->getHmdPosition(); - mat4 overlayXfm = glm::mat4_cast(glm::inverse(hmdOrientation)) * glm::translate(mat4(), -hmdPosition); - batch.setModelTransform(Transform(overlayXfm)); - drawSphereSection(batch); - - - bindCursorTexture(batch); - auto geometryCache = DependencyManager::get(); - vec3 reticleScale = vec3(Cursor::Manager::instance().getScale() * reticleSize); - //Controller Pointers - for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) { - PalmData& palm = myAvatar->getHand()->getPalms()[i]; - if (palm.isActive()) { - glm::vec2 polar = getPolarCoordinates(palm); - // Convert to quaternion - mat4 pointerXfm = glm::mat4_cast(quat(vec3(polar.y, -polar.x, 0.0f))) * glm::translate(mat4(), vec3(0, 0, -1)); - mat4 reticleXfm = overlayXfm * pointerXfm; - reticleXfm = glm::scale(reticleXfm, reticleScale); - batch.setModelTransform(reticleXfm); - // Render reticle at location - geometryCache->renderUnitQuad(batch, glm::vec4(1), _reticleQuad); - } - } - - //Mouse Pointer - if (_reticleActive[MOUSE]) { - glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(), - _reticlePosition[MOUSE].y())); - 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); - } - - renderArgs->_context->render(batch); -} - - void ApplicationOverlay::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const { cursorPos *= qApp->getCanvasSize(); const glm::vec2 projection = screenToSpherical(cursorPos); @@ -421,6 +516,22 @@ void ApplicationOverlay::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origi direction = glm::normalize(intersectionWithUi - origin); } +glm::vec2 getPolarCoordinates(const PalmData& palm) { + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + auto avatarOrientation = myAvatar->getOrientation(); + auto eyePos = myAvatar->getDefaultEyePosition(); + glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm); + // Direction of the tip relative to the eye + glm::vec3 tipDirection = tip - eyePos; + // orient into avatar space + tipDirection = glm::inverse(avatarOrientation) * tipDirection; + // Normalize for trig functions + tipDirection = glm::normalize(tipDirection); + // Convert to polar coordinates + glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y)); + return polar; +} + //Caculate the click location using one of the sixense controllers. Scale is not applied QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const { QPoint rv; @@ -471,9 +582,13 @@ bool ApplicationOverlay::calculateRayUICollisionPoint(const glm::vec3& position, //Renders optional pointers void ApplicationOverlay::renderPointers() { - //glEnable(GL_TEXTURE_2D); - //glEnable(GL_BLEND); - //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //lazily load crosshair texture + if (_crosshairTexture == 0) { + _crosshairTexture = TextureCache::getImageTexture(PathUtils::resourcesPath() + "images/sixense-reticle.png"); + } + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //glActiveTexture(GL_TEXTURE0); //bindCursorTexture(); @@ -635,6 +750,43 @@ void ApplicationOverlay::renderControllerPointers() { } } +void ApplicationOverlay::renderPointersOculus() { + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture)); + glDisable(GL_DEPTH_TEST); + + glMatrixMode(GL_MODELVIEW); + + //Controller Pointers + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) { + PalmData& palm = myAvatar->getHand()->getPalms()[i]; + if (palm.isActive()) { + glm::vec2 polar = getPolarCoordinates(palm); + // Convert to quaternion + glm::quat orientation = glm::quat(glm::vec3(polar.y, -polar.x, 0.0f)); + // Render reticle at location + renderReticle(orientation, _alpha); + } + } + + //Mouse Pointer + if (_reticleActive[MOUSE]) { + glm::vec2 projection = screenToSpherical(glm::vec2(_reticlePosition[MOUSE].x(), + _reticlePosition[MOUSE].y())); + glm::quat orientation(glm::vec3(-projection.y, projection.x, 0.0f)); + renderReticle(orientation, _alpha); + } + + glEnable(GL_DEPTH_TEST); + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); +} + //Renders a small magnification of the currently bound texture at the coordinates void ApplicationOverlay::renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder) { if (!_magnifier) { @@ -915,109 +1067,119 @@ void ApplicationOverlay::renderDomainConnectionStatusBorder() { } } +ApplicationOverlay::TexturedHemisphere::TexturedHemisphere() : + _vertices(0), + _indices(0), + _framebufferObject(NULL), + _vbo(0, 0) { +} -void ApplicationOverlay::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; +ApplicationOverlay::TexturedHemisphere::~TexturedHemisphere() { + cleanupVBO(); + if (_framebufferObject != NULL) { + delete _framebufferObject; } +} - textureFOV = fov; - textureAspectRatio = aspectRatio; - - auto geometryCache = DependencyManager::get(); - - _hemiVertices = gpu::BufferPointer(new gpu::Buffer()); - _hemiIndices = gpu::BufferPointer(new gpu::Buffer()); +void ApplicationOverlay::TexturedHemisphere::bind() { + _framebufferObject->bind(); +} +void ApplicationOverlay::TexturedHemisphere::release() { + _framebufferObject->release(); +} +void ApplicationOverlay::TexturedHemisphere::buildVBO(const float fov, + const float aspectRatio, + const int slices, + const int stacks) { if (fov >= PI) { qDebug() << "TexturedHemisphere::buildVBO(): FOV greater or equal than Pi will create issues"; } - + // Cleanup old VBO if necessary + cleanupVBO(); + //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm - vec3 pos; - vec2 uv; + // Compute number of vertices needed + _vertices = slices * stacks; + // Compute vertices positions and texture UV coordinate - // Create and write to buffer + TextureVertex* vertexData = new TextureVertex[_vertices]; + TextureVertex* vertexPtr = &vertexData[0]; for (int i = 0; i < stacks; i++) { - uv.y = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f + float stacksRatio = (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); + float pitch = -fov * (stacksRatio - 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 + float slicesRatio = (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); + float yaw = -fov * aspectRatio * (slicesRatio - 0.5f); + + vertexPtr->position = getPoint(yaw, pitch); + vertexPtr->uv.x = slicesRatio; + vertexPtr->uv.y = stacksRatio; + vertexPtr++; } } + // Create and write to buffer + glGenBuffers(1, &_vbo.first); + glBindBuffer(GL_ARRAY_BUFFER, _vbo.first); + static const int BYTES_PER_VERTEX = sizeof(TextureVertex); + glBufferData(GL_ARRAY_BUFFER, _vertices * BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW); + delete[] vertexData; + // 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; + _indices = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE; // Compute indices order - std::vector indices; + GLushort* indexData = new GLushort[_indices]; + GLushort* indexPtr = indexData; 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); + + *(indexPtr++) = topLeftIndex; + *(indexPtr++) = bottomLeftIndex; + *(indexPtr++) = topRightIndex; + + *(indexPtr++) = topRightIndex; + *(indexPtr++) = bottomLeftIndex; + *(indexPtr++) = bottomRightIndex; } } - _hemiIndices->append(sizeof(GLushort) * indices.size(), (gpu::Byte*)&indices[0]); + // Create and write to buffer + glGenBuffers(1, &_vbo.second); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo.second); + static const int BYTES_PER_INDEX = sizeof(GLushort); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, _indices * BYTES_PER_INDEX, indexData, GL_STATIC_DRAW); + delete[] indexData; } - -void ApplicationOverlay::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; - gpu::Stream::FormatPointer streamFormat(new gpu::Stream::Format()); // 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); - 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); +void ApplicationOverlay::TexturedHemisphere::cleanupVBO() { + if (_vbo.first != 0) { + glDeleteBuffers(1, &_vbo.first); + _vbo.first = 0; + } + if (_vbo.second != 0) { + glDeleteBuffers(1, &_vbo.second); + _vbo.second = 0; + } } - -GLuint ApplicationOverlay::getOverlayTexture() { - return _framebufferObject->texture(); -} - -void ApplicationOverlay::buildFramebufferObject() { +void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() { auto canvasSize = qApp->getCanvasSize(); QSize fboSize = QSize(canvasSize.x, canvasSize.y); if (_framebufferObject != NULL && fboSize == _framebufferObject->size()) { - // Already built + // Already build return; } @@ -1026,7 +1188,7 @@ void ApplicationOverlay::buildFramebufferObject() { } _framebufferObject = new QOpenGLFramebufferObject(fboSize, QOpenGLFramebufferObject::Depth); - glBindTexture(GL_TEXTURE_2D, getOverlayTexture()); + glBindTexture(GL_TEXTURE_2D, getTexture()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); @@ -1036,6 +1198,38 @@ void ApplicationOverlay::buildFramebufferObject() { glBindTexture(GL_TEXTURE_2D, 0); } +//Renders a hemisphere with texture coordinates. +void ApplicationOverlay::TexturedHemisphere::render() { + if (_framebufferObject == NULL || _vbo.first == 0 || _vbo.second == 0) { + qDebug() << "TexturedHemisphere::render(): Incorrect initialisation"; + return; + } + + glBindBuffer(GL_ARRAY_BUFFER, _vbo.first); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vbo.second); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + static const int STRIDE = sizeof(TextureVertex); + static const void* VERTEX_POINTER = 0; + static const void* TEX_COORD_POINTER = (void*)sizeof(glm::vec3); + glVertexPointer(3, GL_FLOAT, STRIDE, VERTEX_POINTER); + glTexCoordPointer(2, GL_FLOAT, STRIDE, TEX_COORD_POINTER); + + glDrawRangeElements(GL_TRIANGLES, 0, _vertices - 1, _indices, GL_UNSIGNED_SHORT, 0); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + +GLuint ApplicationOverlay::TexturedHemisphere::getTexture() { + return _framebufferObject->texture(); +} + glm::vec2 ApplicationOverlay::directionToSpherical(const glm::vec3& direction) { glm::vec2 result; // Compute yaw diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index e8b5a77b1f..eb397fe3c6 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -33,9 +33,9 @@ public: ~ApplicationOverlay(); void renderOverlay(RenderArgs* renderArgs); - void displayOverlayTexture(RenderArgs* renderArgs); - void displayOverlayTextureStereo(RenderArgs* renderArgs, Camera& whichCamera, float aspectRatio, float fov); - void displayOverlayTextureHmd(RenderArgs* renderArgs, Camera& whichCamera); + void displayOverlayTexture(); + void displayOverlayTextureStereo(Camera& whichCamera, float aspectRatio, float fov); + void displayOverlayTextureHmd(Camera& whichCamera); QPoint getPalmClickLocation(const PalmData *palm) const; bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const; @@ -59,7 +59,6 @@ public: glm::vec2 screenToOverlay(const glm::vec2 & screenPos) const; glm::vec2 overlayToScreen(const glm::vec2 & overlayPos) const; void computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origin, glm::vec3& direction) const; - GLuint getOverlayTexture(); static glm::vec2 directionToSpherical(const glm::vec3 & direction); static glm::vec3 sphericalToDirection(const glm::vec2 & sphericalPos); @@ -67,12 +66,38 @@ public: static glm::vec2 sphericalToScreen(const glm::vec2 & sphericalPos); private: - void buildHemiVertices(const float fov, const float aspectRatio, const int slices, const int stacks); - void drawSphereSection(gpu::Batch& batch); - float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE; - QOpenGLFramebufferObject* _framebufferObject; + // Interleaved vertex data + struct TextureVertex { + glm::vec3 position; + glm::vec2 uv; + }; - void renderPointers(); + typedef QPair VerticesIndices; + class TexturedHemisphere { + public: + TexturedHemisphere(); + ~TexturedHemisphere(); + + void bind(); + void release(); + GLuint getTexture(); + + void buildFramebufferObject(); + void buildVBO(const float fov, const float aspectRatio, const int slices, const int stacks); + void render(); + + private: + void cleanupVBO(); + + GLuint _vertices; + GLuint _indices; + QOpenGLFramebufferObject* _framebufferObject; + VerticesIndices _vbo; + }; + + float _hmdUIAngularSize = DEFAULT_HMD_UI_ANGULAR_SIZE; + void renderReticle(glm::quat orientation, float alpha); + void renderPointers();; void renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder); void renderControllerPointers(); @@ -84,12 +109,10 @@ private: void renderDomainConnectionStatusBorder(); void bindCursorTexture(gpu::Batch& batch, uint8_t cursorId = 0); - void buildFramebufferObject(); + TexturedHemisphere _overlays; float _textureFov; float _textureAspectRatio; - int _hemiVerticesID{ GeometryCache::UNKNOWN_ID }; - enum Reticles { MOUSE, LEFT_CONTROLLER, RIGHT_CONTROLLER, NUMBER_OF_RETICLES }; bool _reticleActive[NUMBER_OF_RETICLES]; @@ -102,6 +125,8 @@ private: float _alpha = 1.0f; float _oculusUIRadius; float _trailingAudioLoudness; + + gpu::TexturePointer _crosshairTexture; QMap _cursors; @@ -124,10 +149,6 @@ private: glm::vec3 _previousMagnifierTopLeft; glm::vec3 _previousMagnifierTopRight; - gpu::PipelinePointer _standardDrawPipeline; - - gpu::PipelinePointer getDrawPipeline(); - }; #endif // hifi_ApplicationOverlay_h diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 3066fd4890..303d63bef8 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1179,21 +1179,6 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec2& minCorner, co batch.draw(gpu::QUADS, 4, 0); } -void GeometryCache::renderUnitQuad(const glm::vec4& color, int id) { - gpu::Batch batch; - renderUnitQuad(batch, color, id); - gpu::GLBackend::renderBatch(batch); -} - -void GeometryCache::renderUnitQuad(gpu::Batch& batch, const glm::vec4& color, int id) { - static const glm::vec2 topLeft(-1, 1); - static const glm::vec2 bottomRight(1, -1); - static const glm::vec2 texCoordTopLeft(0.0f, 1.0f); - static const glm::vec2 texCoordBottomRight(1.0f, 0.0f); - renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color, id); -} - - void GeometryCache::renderQuad(const glm::vec2& minCorner, const glm::vec2& maxCorner, const glm::vec2& texCoordMinCorner, const glm::vec2& texCoordMaxCorner, const glm::vec4& color, int id) { diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 76e03f8669..b438eb2d3b 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -155,9 +155,6 @@ public: void renderBevelCornersRect(int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id = UNKNOWN_ID); void renderBevelCornersRect(gpu::Batch& batch, int x, int y, int width, int height, int bevelDistance, const glm::vec4& color, int id = UNKNOWN_ID); - void renderUnitQuad(const glm::vec4& color = glm::vec4(1), int id = UNKNOWN_ID); - void renderUnitQuad(gpu::Batch& batch, const glm::vec4& color = glm::vec4(1), int id = UNKNOWN_ID); - void renderQuad(int x, int y, int width, int height, const glm::vec4& color, int id = UNKNOWN_ID) { renderQuad(glm::vec2(x,y), glm::vec2(x + width, y + height), color, id); } void renderQuad(gpu::Batch& batch, int x, int y, int width, int height, const glm::vec4& color, int id = UNKNOWN_ID) diff --git a/libraries/render-utils/src/standardDrawTexture.slf b/libraries/render-utils/src/standardDrawTexture.slf deleted file mode 100644 index 4fbeb6eb7f..0000000000 --- a/libraries/render-utils/src/standardDrawTexture.slf +++ /dev/null @@ -1,24 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// standardDrawTexture.frag -// fragment shader -// -// Created by Sam Gateau on 6/10/15. -// 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 -// - -// the texture -uniform sampler2D colorMap; - -varying vec2 varTexcoord; -varying vec4 varColor; - - -void main(void) { - vec4 color = texture2D(colorMap, varTexcoord); - gl_FragColor = color * varColor; -} diff --git a/libraries/render-utils/src/standardTransformPNTC.slv b/libraries/render-utils/src/standardTransformPNTC.slv deleted file mode 100644 index fd2c28049f..0000000000 --- a/libraries/render-utils/src/standardTransformPNTC.slv +++ /dev/null @@ -1,33 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// standardTransformPNTC.slv -// vertex shader -// -// Created by Sam Gateau on 6/10/2015. -// 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 gpu/Transform.slh@> - -<$declareStandardTransform()$> - -varying vec3 varNormal; -varying vec2 varTexcoord; -varying vec4 varColor; - -void main(void) { - varTexcoord = gl_MultiTexCoord0.xy; - varColor = gl_Color; - - // standard transform - TransformCamera cam = getTransformCamera(); - TransformObject obj = getTransformObject(); - <$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$> - <$transformModelToEyeDir(cam, obj, gl_Normal, varNormal)$> - varNormal = normalize(varNormal); -} \ No newline at end of file From 1feceec0c7b5b3c6d2225c26ddbb56a605bd99a4 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 12 Jun 2015 12:57:37 -0700 Subject: [PATCH 47/94] Revert "Fix broken identity transforms on OSX" This reverts commit 926283956412d1e35fc1ba319f2c513803da2c77. --- libraries/gpu/src/gpu/GLBackendTransform.cpp | 25 +++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/libraries/gpu/src/gpu/GLBackendTransform.cpp b/libraries/gpu/src/gpu/GLBackendTransform.cpp index 4e524ec24a..3f760e4cc8 100755 --- a/libraries/gpu/src/gpu/GLBackendTransform.cpp +++ b/libraries/gpu/src/gpu/GLBackendTransform.cpp @@ -133,11 +133,11 @@ void GLBackend::updateTransform() { } if (_transform._invalidModel || _transform._invalidView) { - if (_transform._lastMode != GL_MODELVIEW) { - glMatrixMode(GL_MODELVIEW); - _transform._lastMode = GL_MODELVIEW; - } if (!_transform._model.isIdentity()) { + if (_transform._lastMode != GL_MODELVIEW) { + glMatrixMode(GL_MODELVIEW); + _transform._lastMode = GL_MODELVIEW; + } Transform::Mat4 modelView; if (!_transform._view.isIdentity()) { Transform mvx; @@ -147,12 +147,19 @@ void GLBackend::updateTransform() { _transform._model.getMatrix(modelView); } glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView)); - } else if (!_transform._view.isIdentity()) { - Transform::Mat4 modelView; - _transform._view.getInverseMatrix(modelView); - glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView)); } else { - glLoadIdentity(); + if (!_transform._view.isIdentity()) { + if (_transform._lastMode != GL_MODELVIEW) { + glMatrixMode(GL_MODELVIEW); + _transform._lastMode = GL_MODELVIEW; + } + Transform::Mat4 modelView; + _transform._view.getInverseMatrix(modelView); + glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView)); + } else { + // TODO: eventually do something about the matrix when neither view nor model is specified? + // glLoadIdentity(); + } } (void) CHECK_GL_ERROR(); } From ad773747325e4d80996c6279333211445a6d5f13 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 12 Jun 2015 13:03:33 -0700 Subject: [PATCH 48/94] Fix setting of _renderMode --- interface/src/Application.cpp | 8 +++----- libraries/render-utils/src/RenderDeferredTask.cpp | 3 --- libraries/render/src/render/DrawTask.cpp | 4 ---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 56fe4188e9..0757e6790f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -958,12 +958,15 @@ void Application::paintGL() { _applicationOverlay.displayOverlayTexture(&renderArgs); glPopMatrix(); + renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { _rearMirrorTools->render(&renderArgs, true, _glWidget->mapFromGlobal(QCursor::pos())); } else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { renderRearViewMirror(&renderArgs, _mirrorViewRect); } + renderArgs._renderMode = RenderArgs::NORMAL_RENDER_MODE; + auto finalFbo = DependencyManager::get()->render(&renderArgs); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); @@ -3439,7 +3442,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se "Application::displaySide() ... entities..."); RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE; - RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE; if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) { renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_HULLS); @@ -3448,10 +3450,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP); } - if (theCamera.getMode() == CAMERA_MODE_MIRROR) { - renderMode = RenderArgs::MIRROR_RENDER_MODE; - } - renderArgs->_renderMode = renderMode; renderArgs->_debugFlags = renderDebugFlags; _entities.render(renderArgs); } diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 3fd7d05666..96a197335a 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -139,7 +139,6 @@ template <> void render::jobRun(const DrawOpaqueDeferred& job, const SceneContex batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - renderContext->args->_renderMode = RenderArgs::NORMAL_RENDER_MODE; { GLenum buffers[3]; int bufferCount = 0; @@ -207,8 +206,6 @@ template <> void render::jobRun(const DrawTransparentDeferred& job, const SceneC batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - args->_renderMode = RenderArgs::NORMAL_RENDER_MODE; - const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f; { diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 53964ac1db..96be8d63b0 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -263,7 +263,6 @@ template <> void render::jobRun(const DrawOpaque& job, const SceneContextPointer renderContext->_numDrawnOpaqueItems = renderedItems.size(); - ItemIDsBounds sortedItems; sortedItems.reserve(culledItems.size()); if (renderContext->_sortOpaque) { @@ -283,7 +282,6 @@ template <> void render::jobRun(const DrawOpaque& job, const SceneContextPointer batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - renderContext->args->_renderMode = RenderArgs::NORMAL_RENDER_MODE; { GLenum buffers[3]; int bufferCount = 0; @@ -350,8 +348,6 @@ template <> void render::jobRun(const DrawTransparent& job, const SceneContextPo batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - args->_renderMode = RenderArgs::NORMAL_RENDER_MODE; - const float MOSTLY_OPAQUE_THRESHOLD = 0.75f; const float TRANSPARENT_ALPHA_THRESHOLD = 0.0f; From 66a65e367511259c699ca6ea382040fccde5c7d1 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 12 Jun 2015 13:04:02 -0700 Subject: [PATCH 49/94] Update model to select clockwise backface culling program when in mirror mode --- libraries/render-utils/src/Model.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 8d234cdef5..636bb4bcbe 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -2047,6 +2047,9 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f Locations*& locations) { RenderKey key(mode, translucent, alphaThreshold, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe); + if (mode == RenderArgs::MIRROR_RENDER_MODE) { + key = RenderKey(key.getRaw() | RenderKey::IS_MIRROR); + } auto pipeline = _renderPipelineLib.find(key.getRaw()); if (pipeline == _renderPipelineLib.end()) { qDebug() << "No good, couldn't find a pipeline from the key ?" << key.getRaw(); From 81d003bdb8e34ca5709bb7e326f7169de55c1e8a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 12 Jun 2015 13:05:10 -0700 Subject: [PATCH 50/94] Add post-scale to view matrix when in mirror mode --- libraries/render-utils/src/RenderDeferredTask.cpp | 6 ++++++ libraries/render/src/render/DrawTask.cpp | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 96a197335a..d9dda279e0 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -136,6 +136,9 @@ template <> void render::jobRun(const DrawOpaqueDeferred& job, const SceneContex Transform viewMat; args->_viewFrustum->evalProjectionMatrix(projMat); args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); @@ -203,6 +206,9 @@ template <> void render::jobRun(const DrawTransparentDeferred& job, const SceneC Transform viewMat; args->_viewFrustum->evalProjectionMatrix(projMat); args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 96be8d63b0..bb4ba00cee 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -279,6 +279,9 @@ template <> void render::jobRun(const DrawOpaque& job, const SceneContextPointer Transform viewMat; args->_viewFrustum->evalProjectionMatrix(projMat); args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); @@ -345,6 +348,9 @@ template <> void render::jobRun(const DrawTransparent& job, const SceneContextPo Transform viewMat; args->_viewFrustum->evalProjectionMatrix(projMat); args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); @@ -430,6 +436,9 @@ template <> void render::jobRun(const DrawBackground& job, const SceneContextPoi Transform viewMat; args->_viewFrustum->evalProjectionMatrix(projMat); args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); @@ -471,6 +480,9 @@ template <> void render::jobRun(const DrawPostLayered& job, const SceneContextPo Transform viewMat; args->_viewFrustum->evalProjectionMatrix(projMat); args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); From 648f9bebdf8676ea31c5aa2d00ead97958c0eca3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 12 Jun 2015 15:58:26 -0700 Subject: [PATCH 51/94] add LOD stats to the default stats --- interface/src/ui/Stats.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 8d098b4dc8..8359480b03 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -464,7 +464,7 @@ void Stats::display( verticalOffset = STATS_PELS_INITIALOFFSET; horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + _geoStatsWidth + 3; - lines = _expanded ? 10 : 2; + lines = _expanded ? 10 : 3; drawBackground(backgroundColor, horizontalOffset, 0, canvasSize.x - horizontalOffset, (lines + 1) * STATS_PELS_PER_LINE); @@ -612,12 +612,10 @@ void Stats::display( } // LOD Details - if (_expanded) { - octreeStats.str(""); - QString displayLODDetails = DependencyManager::get()->getLODFeedbackText(); - octreeStats << "LOD: You can see " << qPrintable(displayLODDetails.trimmed()); - verticalOffset += STATS_PELS_PER_LINE; - drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); - } + octreeStats.str(""); + QString displayLODDetails = DependencyManager::get()->getLODFeedbackText(); + octreeStats << "LOD: You can see " << qPrintable(displayLODDetails.trimmed()); + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, scale, rotation, font, (char*)octreeStats.str().c_str(), color); } From c1668553fd46dfc24b57c4e123ee0ab13a902f3b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 15 Jun 2015 11:22:07 -0700 Subject: [PATCH 52/94] make sure that the root node always appends an entity count (even if it's zero). This keeps the unreasonably-deep-recursion warnings from happening --- libraries/entities/src/EntityTreeElement.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 5a59636fe7..5ccac49728 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -350,6 +350,8 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData } } else { // we we couldn't add the entity count, then we couldn't add anything for this element and we're in a NONE state + if (this == _myTree->getRoot()) + qDebug() << "OctreeElement::NONE 0";; appendElementState = OctreeElement::NONE; } @@ -392,17 +394,20 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData // If we wrote fewer entities than we expected, update the number of entities in our packet bool successUpdateEntityCount = true; - if (!noEntitiesFit && numberOfEntities != actualNumberOfEntities) { + if (numberOfEntities != actualNumberOfEntities) { successUpdateEntityCount = packetData->updatePriorBytes(numberOfEntitiesOffset, (const unsigned char*)&actualNumberOfEntities, sizeof(actualNumberOfEntities)); } // If we weren't able to update our entity count, or we couldn't fit any entities, then // we should discard our element and return a result of NONE - if (!successUpdateEntityCount || noEntitiesFit) { + if (!successUpdateEntityCount) { packetData->discardLevel(elementLevel); appendElementState = OctreeElement::NONE; } else { + if (noEntitiesFit) { + appendElementState = OctreeElement::PARTIAL; + } packetData->endLevel(elementLevel); } return appendElementState; From 5f3b9027986b6e6de6ed43468deced53826abc51 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 15 Jun 2015 11:24:29 -0700 Subject: [PATCH 53/94] remove debugging print --- libraries/entities/src/EntityTreeElement.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 5ccac49728..80333a44e5 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -350,8 +350,6 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData } } else { // we we couldn't add the entity count, then we couldn't add anything for this element and we're in a NONE state - if (this == _myTree->getRoot()) - qDebug() << "OctreeElement::NONE 0";; appendElementState = OctreeElement::NONE; } From 1c3b48446659703005cc0de28634eae752ee1895 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 15 Jun 2015 12:48:51 -0700 Subject: [PATCH 54/94] fix detailed ray picking against models --- libraries/render-utils/src/Model.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 145f352658..71178070c6 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1761,7 +1761,7 @@ void Model::setupBatchTransform(gpu::Batch& batch, RenderArgs* args) { } AABox Model::getPartBounds(int meshIndex, int partIndex) { - if (!_calculatedMeshPartBoxesValid) { + if (!_calculatedMeshPartBoxesValid || !_calculatedMeshBoxesValid) { recalculateMeshBoxes(true); } @@ -1802,7 +1802,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran // we always need these properly calculated before we can render, this will likely already have been done // since the engine will call our getPartBounds() before rendering us. - if (!_calculatedMeshPartBoxesValid) { + if (!_calculatedMeshPartBoxesValid || !_calculatedMeshBoxesValid) { recalculateMeshBoxes(true); } auto textureCache = DependencyManager::get(); From 27c6190665b6139c8fc39d57e65c54fa1bedbef4 Mon Sep 17 00:00:00 2001 From: eric levin Date: Mon, 15 Jun 2015 14:24:18 -0700 Subject: [PATCH 55/94] fixed particles not showing up by fixing billboarding code to account for new winding order --- .../src/RenderableParticleEffectEntityItem.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 91c89bb183..465306c2f0 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -53,7 +53,7 @@ void RenderableParticleEffectEntityItem::render(RenderArgs* args) { batch.setUniformTexture(0, _texture->getGPUTexture()); } batch.setModelTransform(getTransformToCenter()); - DependencyManager::get()->bindSimpleProgram(batch); + DependencyManager::get()->bindSimpleProgram(batch, textured); DependencyManager::get()->renderVertices(batch, gpu::QUADS, _cacheID); }; @@ -96,10 +96,11 @@ void RenderableParticleEffectEntityItem::updateQuads(RenderArgs* args, bool text glm::vec3 pos = (textured) ? positions[i] : _particlePositions[i]; // generate corners of quad aligned to face the camera. - vertices.append(pos - rightOffset + upOffset); vertices.append(pos + rightOffset + upOffset); - vertices.append(pos + rightOffset - upOffset); + vertices.append(pos - rightOffset + upOffset); vertices.append(pos - rightOffset - upOffset); + vertices.append(pos + rightOffset - upOffset); + } if (textured) { From 2663e5a87603ab9852aa76b59e5ea658389ef8c7 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 15 Jun 2015 14:43:13 -0700 Subject: [PATCH 56/94] correct particle count for systems without textures --- .../src/RenderableParticleEffectEntityItem.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 465306c2f0..9f46da4944 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -75,22 +75,24 @@ void RenderableParticleEffectEntityItem::updateQuads(RenderArgs* args, bool text vertices.reserve(getLivingParticleCount() * VERTS_PER_PARTICLE); if (textured) { - positions.reserve(getLivingParticleCount()); - textureCoords.reserve(getLivingParticleCount() * VERTS_PER_PARTICLE); - - for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) { - positions.append(_particlePositions[i]); - + textureCoords.reserve(getLivingParticleCount() * VERTS_PER_PARTICLE); + } + positions.reserve(getLivingParticleCount()); + + + for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) { + positions.append(_particlePositions[i]); + if (textured) { textureCoords.append(glm::vec2(0, 1)); textureCoords.append(glm::vec2(1, 1)); textureCoords.append(glm::vec2(1, 0)); textureCoords.append(glm::vec2(0, 0)); } + } // sort particles back to front ::zSortAxis = args->_viewFrustum->getDirection(); qSort(positions.begin(), positions.end(), zSort); - } for (int i = 0; i < positions.size(); i++) { glm::vec3 pos = (textured) ? positions[i] : _particlePositions[i]; From 67ed1369cc5576f1373bbe796ad3aba9f9f6ab44 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 15 Jun 2015 15:04:38 -0700 Subject: [PATCH 57/94] fixed indentation --- .../src/RenderableParticleEffectEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 9f46da4944..2ea3c99026 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -75,7 +75,7 @@ void RenderableParticleEffectEntityItem::updateQuads(RenderArgs* args, bool text vertices.reserve(getLivingParticleCount() * VERTS_PER_PARTICLE); if (textured) { - textureCoords.reserve(getLivingParticleCount() * VERTS_PER_PARTICLE); + textureCoords.reserve(getLivingParticleCount() * VERTS_PER_PARTICLE); } positions.reserve(getLivingParticleCount()); From 4a00bd3e7f2e30268c3ed4ee46a7f0bfef59d958 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 15 Jun 2015 16:06:20 -0700 Subject: [PATCH 58/94] quiet compiler --- libraries/entities/src/EntityItem.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index f64dad0ef4..d2881e5f3a 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -67,12 +67,12 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) : _simulatorIDChangedTime(0), _marketplaceID(ENTITY_ITEM_DEFAULT_MARKETPLACE_ID), _name(ENTITY_ITEM_DEFAULT_NAME), + _href(""), + _description(""), _dirtyFlags(0), _element(nullptr), _physicsInfo(nullptr), - _simulated(false), - _href(""), - _description("") + _simulated(false) { quint64 now = usecTimestampNow(); _lastSimulated = now; From 445381bb6bf1dcfc8cf5574b63aa33eea49f49d7 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 15 Jun 2015 16:07:28 -0700 Subject: [PATCH 59/94] fix DEFAULT_VOXEL_DATA, recompress voxel data when a script calls setVoxel. Use provided transform when rendering. --- .../src/RenderablePolyVoxEntityItem.cpp | 30 ++++++++++++------- .../src/RenderablePolyVoxEntityItem.h | 3 +- libraries/entities/src/PolyVoxEntityItem.cpp | 21 ++++++++++++- libraries/entities/src/PolyVoxEntityItem.h | 22 +++++++------- 4 files changed, 54 insertions(+), 22 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index bb5932d70c..d8e7ca0a18 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -121,7 +121,7 @@ void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) _volData->setBorderValue(255); #ifdef WANT_DEBUG - qDebug() << " new size is" << _volData->getWidth() << _volData->getHeight() << _volData->getDepth(); + qDebug() << " new voxel-space size is" << _volData->getWidth() << _volData->getHeight() << _volData->getDepth(); #endif // I'm not sure this is needed... the docs say that each element is initialized with its default @@ -220,12 +220,15 @@ uint8_t RenderablePolyVoxEntityItem::getVoxel(int x, int y, int z) { return _volData->getVoxelAt(x, y, z); } -void RenderablePolyVoxEntityItem::setVoxel(int x, int y, int z, uint8_t toValue) { +void RenderablePolyVoxEntityItem::setVoxelInternal(int x, int y, int z, uint8_t toValue) { + // set a voxel without recompressing the voxel data assert(_volData); if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) { return; } + updateOnCount(x, y, z, toValue); + if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC) { _volData->setVoxelAt(x + 1, y + 1, z + 1, toValue); } else { @@ -234,6 +237,11 @@ void RenderablePolyVoxEntityItem::setVoxel(int x, int y, int z, uint8_t toValue) } +void RenderablePolyVoxEntityItem::setVoxel(int x, int y, int z, uint8_t toValue) { + setVoxelInternal(x, y, z, toValue); + compressVolumeData(); +} + void RenderablePolyVoxEntityItem::updateOnCount(int x, int y, int z, uint8_t toValue) { // keep _onCount up to date if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) { @@ -259,7 +267,7 @@ void RenderablePolyVoxEntityItem::setAll(uint8_t toValue) { for (int y = 0; y < _voxelVolumeSize.y; y++) { for (int x = 0; x < _voxelVolumeSize.x; x++) { updateOnCount(x, y, z, toValue); - setVoxel(x, y, z, toValue); + setVoxelInternal(x, y, z, toValue); } } } @@ -267,7 +275,7 @@ void RenderablePolyVoxEntityItem::setAll(uint8_t toValue) { } void RenderablePolyVoxEntityItem::setVoxelInVolume(glm::vec3 position, uint8_t toValue) { - updateOnCount(position.x, position.y, position.z, toValue); + // same as setVoxel but takes a vector rather than 3 floats. setVoxel(position.x, position.y, position.z, toValue); } @@ -283,7 +291,7 @@ void RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radi // If the current voxel is less than 'radius' units from the center then we make it solid. if (fDistToCenter <= radius) { updateOnCount(x, y, z, toValue); - setVoxel(x, y, z, toValue); + setVoxelInternal(x, y, z, toValue); } } } @@ -380,10 +388,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { getModel(); } - Transform transform; - transform.setTranslation(getPosition() - getRegistrationPoint() * getDimensions()); - transform.setRotation(getRotation()); - transform.setScale(getDimensions() / _voxelVolumeSize); + Transform transform(voxelToWorldMatrix()); auto mesh = _modelGeometry.getMesh(); Q_ASSERT(args->_batch); @@ -514,6 +519,11 @@ void RenderablePolyVoxEntityItem::compressVolumeData() { QByteArray newVoxelData; QDataStream writer(&newVoxelData, QIODevice::WriteOnly | QIODevice::Truncate); + + #ifdef WANT_DEBUG + qDebug() << "compressing voxel data of size:" << voxelXSize << voxelYSize << voxelZSize; + #endif + writer << voxelXSize << voxelYSize << voxelZSize; QByteArray compressedData = qCompress(uncompressedData, 9); @@ -573,7 +583,7 @@ void RenderablePolyVoxEntityItem::decompressVolumeData() { for (int x = 0; x < voxelXSize; x++) { int uncompressedIndex = (z * voxelYSize * voxelXSize) + (y * voxelZSize) + x; updateOnCount(x, y, z, uncompressedData[uncompressedIndex]); - setVoxel(x, y, z, uncompressedData[uncompressedIndex]); + setVoxelInternal(x, y, z, uncompressedData[uncompressedIndex]); } } } diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 77aeb24f2a..814f3deb07 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -73,11 +73,12 @@ public: virtual void setVoxelInVolume(glm::vec3 position, uint8_t toValue); SIMPLE_RENDERABLE(); - + private: // The PolyVoxEntityItem class has _voxelData which contains dimensions and compressed voxel data. The dimensions // may not match _voxelVolumeSize. + void setVoxelInternal(int x, int y, int z, uint8_t toValue); void compressVolumeData(); void decompressVolumeData(); diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index 95ab7d1035..84fbf11311 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -23,7 +23,7 @@ const glm::vec3 PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE = glm::vec3(32, 32, 32); const float PolyVoxEntityItem::MAX_VOXEL_DIMENSION = 32.0f; -const QByteArray PolyVoxEntityItem::DEFAULT_VOXEL_DATA(qCompress(QByteArray(0), 9)); // XXX +const QByteArray PolyVoxEntityItem::DEFAULT_VOXEL_DATA(PolyVoxEntityItem::makeEmptyVoxelData()); const PolyVoxEntityItem::PolyVoxSurfaceStyle PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE = PolyVoxEntityItem::SURFACE_MARCHING_CUBES; @@ -31,6 +31,25 @@ EntityItemPointer PolyVoxEntityItem::factory(const EntityItemID& entityID, const return EntityItemPointer(new PolyVoxEntityItem(entityID, properties)); } + + + +QByteArray PolyVoxEntityItem::makeEmptyVoxelData(quint16 voxelXSize, quint16 voxelYSize, quint16 voxelZSize) { + int rawSize = voxelXSize * voxelYSize * voxelZSize; + + QByteArray uncompressedData = QByteArray(rawSize, '\0'); + QByteArray newVoxelData; + QDataStream writer(&newVoxelData, QIODevice::WriteOnly | QIODevice::Truncate); + writer << voxelXSize << voxelYSize << voxelZSize; + + QByteArray compressedData = qCompress(uncompressedData, 9); + writer << compressedData; + + return newVoxelData; +} + + + PolyVoxEntityItem::PolyVoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : EntityItem(entityItemID), _voxelVolumeSize(PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE), diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index bf8214675b..33e127e9b2 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -12,14 +12,14 @@ #ifndef hifi_PolyVoxEntityItem_h #define hifi_PolyVoxEntityItem_h -#include "EntityItem.h" +#include "EntityItem.h" class PolyVoxEntityItem : public EntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); PolyVoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); - + ALLOW_INSTANTIATION // This class can be instantiated // methods for getting/setting all properties of an entity @@ -29,22 +29,22 @@ class PolyVoxEntityItem : public EntityItem { // TODO: eventually only include properties changed since the params.lastViewFrustumSent time virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, + int& propertyCount, OctreeElement::AppendState& appendState) const; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData); - + // never have a ray intersection pick a PolyVoxEntityItem. virtual bool supportsDetailedRayIntersection() const { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, + bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject, bool precisionPicking) const { return false; } virtual void debugDump() const; @@ -58,12 +58,12 @@ class PolyVoxEntityItem : public EntityItem { enum PolyVoxSurfaceStyle { SURFACE_MARCHING_CUBES, SURFACE_CUBIC, - SURFACE_EDGED_CUBIC + SURFACE_EDGED_CUBIC }; virtual void setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) { _voxelSurfaceStyle = voxelSurfaceStyle; } virtual void setVoxelSurfaceStyle(uint16_t voxelSurfaceStyle) { - setVoxelSurfaceStyle((PolyVoxSurfaceStyle) voxelSurfaceStyle); + setVoxelSurfaceStyle((PolyVoxSurfaceStyle) voxelSurfaceStyle); } virtual PolyVoxSurfaceStyle getVoxelSurfaceStyle() const { return _voxelSurfaceStyle; } @@ -82,15 +82,17 @@ class PolyVoxEntityItem : public EntityItem { virtual void setAll(uint8_t toValue) {} virtual void setVoxelInVolume(glm::vec3 position, uint8_t toValue) {} - + virtual uint8_t getVoxel(int x, int y, int z) { return 0; } virtual void setVoxel(int x, int y, int z, uint8_t toValue) {} + static QByteArray makeEmptyVoxelData(quint16 voxelXSize = 16, quint16 voxelYSize = 16, quint16 voxelZSize = 16); protected: glm::vec3 _voxelVolumeSize; // this is always 3 bytes QByteArray _voxelData; PolyVoxSurfaceStyle _voxelSurfaceStyle; + }; #endif // hifi_PolyVoxEntityItem_h From 6def25de92a2409b025f09ba17c2c90a4ea3a607 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 15 Jun 2015 16:22:06 -0700 Subject: [PATCH 60/94] formatting --- libraries/entities/src/PolyVoxEntityItem.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index 33e127e9b2..e5d511c087 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -92,7 +92,6 @@ class PolyVoxEntityItem : public EntityItem { glm::vec3 _voxelVolumeSize; // this is always 3 bytes QByteArray _voxelData; PolyVoxSurfaceStyle _voxelSurfaceStyle; - }; #endif // hifi_PolyVoxEntityItem_h From 8e46043f144d2259f090c3e920929ab2963e5816 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 16 Jun 2015 08:41:21 -0700 Subject: [PATCH 61/94] add fixed back/forward, buttons to menu --- interface/src/Menu.cpp | 17 ++- interface/src/Menu.h | 3 +- libraries/networking/src/AddressManager.cpp | 153 ++++++++++++++------ libraries/networking/src/AddressManager.h | 51 +++++-- libraries/networking/src/NodeList.cpp | 2 +- 5 files changed, 164 insertions(+), 62 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 6242318170..149fd2e043 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -90,6 +90,22 @@ Menu::Menu() { addActionToQMenuAndActionHash(fileMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J, qApp, SLOT(toggleRunningScriptsWidget())); + auto addressManager = DependencyManager::get(); + + addDisabledActionAndSeparator(fileMenu, "History"); + + addActionToQMenuAndActionHash(fileMenu, + MenuOption::Back, + 0, + addressManager.data(), + SLOT(goBack())); + + addActionToQMenuAndActionHash(fileMenu, + MenuOption::Forward, + 0, + addressManager.data(), + SLOT(goForward())); + addDisabledActionAndSeparator(fileMenu, "Location"); qApp->getBookmarks()->setupMenus(this, fileMenu); @@ -98,7 +114,6 @@ Menu::Menu() { Qt::CTRL | Qt::Key_L, dialogsManager.data(), SLOT(toggleAddressBar())); - auto addressManager = DependencyManager::get(); addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyAddress, 0, addressManager.data(), SLOT(copyAddress())); addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyPath, 0, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 6107744abc..6b892ebc3c 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -149,6 +149,7 @@ namespace MenuOption { const QString AudioStatsShowInjectedStreams = "Audio Stats Show Injected Streams"; const QString AutoMuteAudio = "Auto Mute Microphone"; const QString AvatarReceiveStats = "Show Receive Stats"; + const QString Back = "Back"; const QString BandwidthDetails = "Bandwidth Details"; const QString BinaryEyelidControl = "Binary Eyelid Control"; const QString BlueSpeechSphere = "Blue Sphere While Speaking"; @@ -194,12 +195,12 @@ namespace MenuOption { const QString Faceshift = "Faceshift"; const QString FilterSixense = "Smooth Sixense Movement"; const QString FirstPerson = "First Person"; + const QString Forward = "Forward"; const QString FrameTimer = "Show Timer"; const QString Fullscreen = "Fullscreen"; const QString FullscreenMirror = "Fullscreen Mirror"; const QString GlowWhenSpeaking = "Glow When Speaking"; const QString NamesAboveHeads = "Names Above Heads"; - const QString GoToUser = "Go To User"; const QString HMDTools = "HMD Tools"; const QString IncreaseAvatarSize = "Increase Avatar Size"; const QString KeyboardMotorControl = "Enable Keyboard Motor Control"; diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 51575bfde5..f64db747f9 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -54,9 +54,26 @@ const QUrl AddressManager::currentAddress() const { void AddressManager::loadSettings(const QString& lookupString) { if (lookupString.isEmpty()) { - handleLookupString(currentAddressHandle.get().toString()); + handleUrl(currentAddressHandle.get().toString(), LookupTrigger::StartupFromSettings); } else { - handleLookupString(lookupString); + handleUrl(lookupString, LookupTrigger::StartupFromSettings); + } +} + +void AddressManager::goBack() { + if (_backStack.size() > 0) { + // pop a URL from the backStack + QUrl poppedURL = _backStack.pop(); + + // go to that address + handleUrl(poppedURL, LookupTrigger::Back); + } +} + +void AddressManager::goForward() { + if (_forwardStack.size() > 0) { + // pop a URL from the forwardStack and go to that address + handleUrl(_forwardStack.pop(), LookupTrigger::Forward); } } @@ -102,7 +119,7 @@ const JSONCallbackParameters& AddressManager::apiCallbackParameters() { return callbackParams; } -bool AddressManager::handleUrl(const QUrl& lookupUrl) { +bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) { if (lookupUrl.scheme() == HIFI_URL_SCHEME) { qCDebug(networking) << "Trying to go to URL" << lookupUrl.toString(); @@ -121,17 +138,17 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) { // we're assuming this is either a network address or global place name // check if it is a network address first if (handleNetworkAddress(lookupUrl.host() - + (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())))) { + + (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())), trigger)) { // we may have a path that defines a relative viewpoint - if so we should jump to that now - handlePath(lookupUrl.path()); + handlePath(lookupUrl.path(), trigger); } else if (handleDomainID(lookupUrl.host())){ // no place name - this is probably a domain ID // try to look up the domain ID on the metaverse API - attemptDomainIDLookup(lookupUrl.host(), lookupUrl.path()); + attemptDomainIDLookup(lookupUrl.host(), lookupUrl.path(), trigger); } else { // wasn't an address - lookup the place name // we may have a path that defines a relative viewpoint - pass that through the lookup so we can go to it after - attemptPlaceNameLookup(lookupUrl.host(), lookupUrl.path()); + attemptPlaceNameLookup(lookupUrl.host(), lookupUrl.path(), trigger); } } @@ -140,8 +157,10 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) { qCDebug(networking) << "Going to relative path" << lookupUrl.path(); // if this is a relative path then handle it as a relative viewpoint - handlePath(lookupUrl.path()); + handlePath(lookupUrl.path(), trigger, true); emit lookupResultsFinished(); + + return true; } return false; @@ -183,6 +202,7 @@ void AddressManager::handleAPIResponse(QNetworkReply& requestReply) { } const char OVERRIDE_PATH_KEY[] = "override_path"; +const char LOOKUP_TRIGGER_KEY[] = "lookup_trigger"; void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const QNetworkReply& reply) { @@ -243,6 +263,8 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const emit possibleDomainChangeRequiredViaICEForID(iceServerAddress, domainID); } + LookupTrigger trigger = (LookupTrigger) reply.property(LOOKUP_TRIGGER_KEY).toInt(); + // set our current root place id to the ID that came back const QString PLACE_ID_KEY = "id"; _rootPlaceID = rootMap[PLACE_ID_KEY].toUuid(); @@ -251,16 +273,16 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const const QString PLACE_NAME_KEY = "name"; QString placeName = rootMap[PLACE_NAME_KEY].toString(); if (!placeName.isEmpty()) { - setHost(placeName); + setHost(placeName, trigger); } else { - setHost(domainIDString); + setHost(domainIDString, trigger); } // check if we had a path to override the path returned QString overridePath = reply.property(OVERRIDE_PATH_KEY).toString(); if (!overridePath.isEmpty()) { - handlePath(overridePath); + handlePath(overridePath, trigger); } else { // take the path that came back const QString PLACE_PATH_KEY = "path"; @@ -269,10 +291,14 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const bool shouldFaceViewpoint = locationMap.contains(LOCATION_API_ONLINE_KEY); if (!returnedPath.isEmpty()) { - // try to parse this returned path as a viewpoint, that's the only thing it could be for now - if (!handleViewpoint(returnedPath, shouldFaceViewpoint)) { - qCDebug(networking) << "Received a location path that was could not be handled as a viewpoint -" - << returnedPath; + if (shouldFaceViewpoint) { + // try to parse this returned path as a viewpoint, that's the only thing it could be for now + if (!handleViewpoint(returnedPath, shouldFaceViewpoint)) { + qCDebug(networking) << "Received a location path that was could not be handled as a viewpoint -" + << returnedPath; + } + } else { + handlePath(overridePath, trigger); } } else { // we didn't override the path or get one back - ask the DS for the viewpoint of its index path @@ -306,15 +332,20 @@ void AddressManager::handleAPIError(QNetworkReply& errorReply) { const QString GET_PLACE = "/api/v1/places/%1"; -void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath) { +void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath, LookupTrigger trigger) { // assume this is a place name and see if we can get any info on it QString placeName = QUrl::toPercentEncoding(lookupString); QVariantMap requestParams; + + // if the user asked for a specific path with this lookup then keep it with the request so we can use it later if (!overridePath.isEmpty()) { requestParams.insert(OVERRIDE_PATH_KEY, overridePath); } + // remember how this lookup was triggered for history storage handling later + requestParams.insert(LOOKUP_TRIGGER_KEY, static_cast(trigger)); + AccountManager::getInstance().sendRequest(GET_PLACE.arg(placeName), AccountManagerAuth::None, QNetworkAccessManager::GetOperation, @@ -324,15 +355,20 @@ void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const Q const QString GET_DOMAIN_ID = "/api/v1/domains/%1"; -void AddressManager::attemptDomainIDLookup(const QString& lookupString, const QString& overridePath) { +void AddressManager::attemptDomainIDLookup(const QString& lookupString, const QString& overridePath, LookupTrigger trigger) { // assume this is a domain ID and see if we can get any info on it QString domainID = QUrl::toPercentEncoding(lookupString); QVariantMap requestParams; + + // if the user asked for a specific path with this lookup then keep it with the request so we can use it later if (!overridePath.isEmpty()) { requestParams.insert(OVERRIDE_PATH_KEY, overridePath); } + // remember how this lookup was triggered for history storage handling later + requestParams.insert(LOOKUP_TRIGGER_KEY, static_cast(trigger)); + AccountManager::getInstance().sendRequest(GET_DOMAIN_ID.arg(domainID), AccountManagerAuth::None, QNetworkAccessManager::GetOperation, @@ -340,7 +376,7 @@ void AddressManager::attemptDomainIDLookup(const QString& lookupString, const QS QByteArray(), NULL, requestParams); } -bool AddressManager::handleNetworkAddress(const QString& lookupString) { +bool AddressManager::handleNetworkAddress(const QString& lookupString, LookupTrigger trigger) { const QString IP_ADDRESS_REGEX_STRING = "^((?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}" "(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))(?::(\\d{1,5}))?$"; @@ -358,7 +394,7 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString) { } emit lookupResultsFinished(); - setDomainInfo(domainIPString, domainPort); + setDomainInfo(domainIPString, domainPort, trigger); return true; } @@ -375,7 +411,7 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString) { } emit lookupResultsFinished(); - setDomainInfo(domainHostname, domainPort); + setDomainInfo(domainHostname, domainPort, trigger); return true; } @@ -391,15 +427,23 @@ bool AddressManager::handleDomainID(const QString& host) { return (domainIDRegex.indexIn(host) != -1); } -void AddressManager::handlePath(const QString& path) { +void AddressManager::handlePath(const QString& path, LookupTrigger trigger, bool wasPathOnly) { if (!handleViewpoint(path)) { qCDebug(networking) << "User entered path could not be handled as a viewpoint - " << path << "- wll attempt to ask domain-server to resolve."; + + if (!wasPathOnly) { + // if we received a path with a host then we need to remember what it was here so we can not + // double set add to the history stack once handle viewpoint is called with the result + _newHostLookupPath = path; + } + emit pathChangeRequired(path); } } -bool AddressManager::handleViewpoint(const QString& viewpointString, bool shouldFace) { +bool AddressManager::handleViewpoint(const QString& viewpointString, bool shouldFace, + bool definitelyPathOnly, const QString& pathString) { const QString FLOAT_REGEX_STRING = "([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?)"; const QString SPACED_COMMA_REGEX_STRING = "\\s*,\\s*"; const QString POSITION_REGEX_STRING = QString("\\/") + FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING + @@ -416,8 +460,18 @@ bool AddressManager::handleViewpoint(const QString& viewpointString, bool should positionRegex.cap(2).toFloat(), positionRegex.cap(3).toFloat()); - // we're about to jump positions - store the current address in our history - addCurrentAddressToHistory(); + // We need to use definitelyPathOnly, pathString and _newHostLookupPath to determine if the current address + // should be stored in the history before we ask for a position/orientation change. A relative path that was + // not associated with a host lookup should always trigger a history change (definitelyPathOnly) and a viewpointString + // with a non empty pathString (suggesting this is the result of a lookup with the domain-server) that does not match + // _newHostLookupPath should always trigger a history change. + // + // We use _newHostLookupPath to determine if the client has already stored its last address + // before moving to a new host thanks to the information in the same lookup URL. + + if (definitelyPathOnly || (!pathString.isEmpty() && pathString == _newHostLookupPath)) { + addCurrentAddressToHistory(LookupTrigger::UserInput); + } if (!isNaN(newPosition.x) && !isNaN(newPosition.y) && !isNaN(newPosition.z)) { glm::quat newOrientation; @@ -469,11 +523,11 @@ bool AddressManager::handleUsername(const QString& lookupString) { return false; } -void AddressManager::setHost(const QString& host) { +void AddressManager::setHost(const QString& host, LookupTrigger trigger) { if (host != _host) { // if the host is being changed we should store current address in the history - addCurrentAddressToHistory(); + addCurrentAddressToHistory(trigger); _host = host; emit hostChanged(_host); @@ -481,8 +535,8 @@ void AddressManager::setHost(const QString& host) { } -void AddressManager::setDomainInfo(const QString& hostname, quint16 port) { - setHost(hostname); +void AddressManager::setDomainInfo(const QString& hostname, quint16 port, LookupTrigger trigger) { + setHost(hostname, trigger); _rootPlaceID = QUuid(); @@ -495,11 +549,17 @@ void AddressManager::setDomainInfo(const QString& hostname, quint16 port) { void AddressManager::goToUser(const QString& username) { QString formattedUsername = QUrl::toPercentEncoding(username); + + // for history storage handling we remember how this lookup was trigged - for a username it's always user input + QVariantMap requestParams; + requestParams.insert(LOOKUP_TRIGGER_KEY, static_cast(LookupTrigger::UserInput)); + // this is a username - pull the captured name and lookup that user's location AccountManager::getInstance().sendRequest(GET_USER_LOCATION.arg(formattedUsername), AccountManagerAuth::Optional, QNetworkAccessManager::GetOperation, - apiCallbackParameters()); + apiCallbackParameters(), + QByteArray(), nullptr, requestParams); } void AddressManager::copyAddress() { @@ -510,21 +570,28 @@ void AddressManager::copyPath() { QApplication::clipboard()->setText(currentPath()); } -void AddressManager::addCurrentAddressToHistory() { - if (_lastHistoryAppend == 0) { - // we don't store the first address on application load - // just update the last append time so the next is stored - _lastHistoryAppend = usecTimestampNow(); - } else { - const quint64 DOUBLE_STORE_THRESHOLD_USECS = 500000; +void AddressManager::addCurrentAddressToHistory(LookupTrigger trigger) { - // avoid double storing when the host changes and the viewpoint changes immediately after - if (usecTimestampNow() - _lastHistoryAppend > DOUBLE_STORE_THRESHOLD_USECS) { - // add the current address to the history - _history.append(currentAddress()); - - // change our last history append to now - _lastHistoryAppend = usecTimestampNow(); + // if we're cold starting and this is called for the first address (from settings) we don't do anything + if (trigger != LookupTrigger::StartupFromSettings) { + if (trigger == LookupTrigger::UserInput) { + // anyime the user has manually looked up an address we know we should clear the forward stack + qDebug() << "CLEARING THE FORWARD STACK"; + _forwardStack.clear(); } + + if (trigger == LookupTrigger::Back) { + qDebug() << "PUTTING THE CURRENT ADDRESS INTO THE FORWARD STACK"; + // when the user is going back, we move the current address to the forward stack and do not but it into the back stack + _forwardStack.push(currentAddress()); + } else { + qDebug() << "ADDING THE CURRENT ADDRESS TO THE BACK STACK"; + // unless this was triggered from the result of a named path lookup, add the current address to the history + _backStack.push(currentAddress()); + } + + qDebug() << "THE BACK STACK IS NOW:" << _backStack; + qDebug() << "THE FORWARD STACK IS NOW:" << _forwardStack; + } } diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index d950ae0275..d985c5d2c6 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -12,8 +12,8 @@ #ifndef hifi_AddressManager_h #define hifi_AddressManager_h -#include -#include +#include +#include #include #include @@ -38,6 +38,14 @@ class AddressManager : public QObject, public Dependency { Q_PROPERTY(QString hostname READ getHost) Q_PROPERTY(QString pathname READ currentPath) public: + + enum LookupTrigger { + UserInput, + Back, + Forward, + StartupFromSettings + }; + bool isConnected(); const QString& getProtocol() { return HIFI_URL_SCHEME; }; @@ -47,10 +55,7 @@ public: const QUuid& getRootPlaceID() const { return _rootPlaceID; } const QString& getHost() const { return _host; } - void setHost(const QString& host); - void attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath = QString()); - void attemptDomainIDLookup(const QString& lookupString, const QString& overridePath = QString()); void setPositionGetter(PositionGetter positionGetter) { _positionGetter = positionGetter; } void setOrientationGetter(OrientationGetter orientationGetter) { _orientationGetter = orientationGetter; } @@ -60,9 +65,12 @@ public: public slots: void handleLookupString(const QString& lookupString); - void goToUser(const QString& username); - void goToAddressFromObject(const QVariantMap& addressMap, const QNetworkReply& reply); - bool goToViewpoint(const QString& viewpointString) { return handleViewpoint(viewpointString); } + // we currently expect this to be called from NodeList once handleLookupString has been called with a path + bool goToViewpointForPath(const QString& viewpointString, const QString& pathString) + { return handleViewpoint(viewpointString, false, false, pathString); } + + void goBack(); + void goForward(); void storeCurrentAddress(); @@ -85,28 +93,39 @@ protected: private slots: void handleAPIResponse(QNetworkReply& requestReply); void handleAPIError(QNetworkReply& errorReply); + + void goToUser(const QString& username); + void goToAddressFromObject(const QVariantMap& addressMap, const QNetworkReply& reply); private: - void setDomainInfo(const QString& hostname, quint16 port); + void setHost(const QString& host, LookupTrigger trigger); + void setDomainInfo(const QString& hostname, quint16 port, LookupTrigger trigger); const JSONCallbackParameters& apiCallbackParameters(); - bool handleUrl(const QUrl& lookupUrl); + bool handleUrl(const QUrl& lookupUrl, LookupTrigger trigger = UserInput); - bool handleNetworkAddress(const QString& lookupString); - void handlePath(const QString& path); - bool handleViewpoint(const QString& viewpointString, bool shouldFace = false); + bool handleNetworkAddress(const QString& lookupString, LookupTrigger trigger); + void handlePath(const QString& path, LookupTrigger trigger, bool wasPathOnly = false); + bool handleViewpoint(const QString& viewpointString, bool shouldFace = false, + bool definitelyPathOnly = false, const QString& pathString = QString()); bool handleUsername(const QString& lookupString); bool handleDomainID(const QString& host); - void addCurrentAddressToHistory(); + void attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath, LookupTrigger trigger); + void attemptDomainIDLookup(const QString& lookupString, const QString& overridePath, LookupTrigger trigger); + + void addCurrentAddressToHistory(LookupTrigger trigger); QString _host; QUuid _rootPlaceID; PositionGetter _positionGetter; OrientationGetter _orientationGetter; - QList _history; - quint64 _lastHistoryAppend = 0; + QStack _backStack; + QStack _forwardStack; + quint64 _lastBackPush = 0; + + QString _newHostLookupPath; }; #endif // hifi_AddressManager_h diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 44aa5bc644..e7bb4fbb6f 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -467,7 +467,7 @@ void NodeList::handleDSPathQueryResponse(const QByteArray& packet) { QString viewpoint = QString::fromUtf8(currentPosition, numViewpointBytes); // Hand it off to the AddressManager so it can handle it as a relative viewpoint - if (DependencyManager::get()->goToViewpoint(viewpoint)) { + if (DependencyManager::get()->goToViewpointForPath(viewpoint, pathQuery)) { qCDebug(networking) << "Going to viewpoint" << viewpoint << "which was the lookup result for path" << pathQuery; } else { qCDebug(networking) << "Could not go to viewpoint" << viewpoint From ef62f7e9f004491e1a1a5194d69a80a2a3d53ace Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 16 Jun 2015 08:46:04 -0700 Subject: [PATCH 62/94] fix relative path lookup effect on history --- libraries/networking/src/AddressManager.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index f64db747f9..dd8dfeec2b 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -436,6 +436,9 @@ void AddressManager::handlePath(const QString& path, LookupTrigger trigger, bool // if we received a path with a host then we need to remember what it was here so we can not // double set add to the history stack once handle viewpoint is called with the result _newHostLookupPath = path; + } else { + // clear the _newHostLookupPath so it doesn't match when this return comes in + _newHostLookupPath = QString(); } emit pathChangeRequired(path); @@ -469,7 +472,7 @@ bool AddressManager::handleViewpoint(const QString& viewpointString, bool should // We use _newHostLookupPath to determine if the client has already stored its last address // before moving to a new host thanks to the information in the same lookup URL. - if (definitelyPathOnly || (!pathString.isEmpty() && pathString == _newHostLookupPath)) { + if (definitelyPathOnly || (!pathString.isEmpty() && pathString != _newHostLookupPath)) { addCurrentAddressToHistory(LookupTrigger::UserInput); } @@ -576,22 +579,15 @@ void AddressManager::addCurrentAddressToHistory(LookupTrigger trigger) { if (trigger != LookupTrigger::StartupFromSettings) { if (trigger == LookupTrigger::UserInput) { // anyime the user has manually looked up an address we know we should clear the forward stack - qDebug() << "CLEARING THE FORWARD STACK"; _forwardStack.clear(); } if (trigger == LookupTrigger::Back) { - qDebug() << "PUTTING THE CURRENT ADDRESS INTO THE FORWARD STACK"; // when the user is going back, we move the current address to the forward stack and do not but it into the back stack _forwardStack.push(currentAddress()); } else { - qDebug() << "ADDING THE CURRENT ADDRESS TO THE BACK STACK"; // unless this was triggered from the result of a named path lookup, add the current address to the history _backStack.push(currentAddress()); } - - qDebug() << "THE BACK STACK IS NOW:" << _backStack; - qDebug() << "THE FORWARD STACK IS NOW:" << _forwardStack; - } } From 70e042085b7a5f3924d0d68bf52db9a29f49a21b Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 16 Jun 2015 09:11:36 -0700 Subject: [PATCH 63/94] fixed indentation --- .../src/RenderableParticleEffectEntityItem.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 2ea3c99026..d00728a9eb 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -90,9 +90,9 @@ void RenderableParticleEffectEntityItem::updateQuads(RenderArgs* args, bool text } } - // sort particles back to front - ::zSortAxis = args->_viewFrustum->getDirection(); - qSort(positions.begin(), positions.end(), zSort); + // sort particles back to front + ::zSortAxis = args->_viewFrustum->getDirection(); + qSort(positions.begin(), positions.end(), zSort); for (int i = 0; i < positions.size(); i++) { glm::vec3 pos = (textured) ? positions[i] : _particlePositions[i]; From 8241a2170baa0e7d95e757e1e32b54fc78f8532f Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 16 Jun 2015 09:50:42 -0700 Subject: [PATCH 64/94] Update cube, line, and sphere overlays to use batches --- interface/src/ui/overlays/Cube3DOverlay.cpp | 228 +++++++++++------- interface/src/ui/overlays/Line3DOverlay.cpp | 66 +++-- interface/src/ui/overlays/Sphere3DOverlay.cpp | 58 +++-- 3 files changed, 222 insertions(+), 130 deletions(-) diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index 6fc9fe6e27..73406c07a0 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -35,13 +35,6 @@ void Cube3DOverlay::render(RenderArgs* args) { return; // do nothing if we're not visible } - - float glowLevel = getGlowLevel(); - Glower* glower = NULL; - if (glowLevel > 0.0f) { - glower = new Glower(glowLevel); - } - float alpha = getAlpha(); xColor color = getColor(); const float MAX_COLOR = 255.0f; @@ -49,91 +42,162 @@ void Cube3DOverlay::render(RenderArgs* args) { //glDisable(GL_LIGHTING); - // TODO: handle registration point?? + // TODO: handle registration point?? glm::vec3 position = getPosition(); glm::vec3 center = getCenter(); glm::vec3 dimensions = getDimensions(); glm::quat rotation = getRotation(); - - glPushMatrix(); - glTranslatef(position.x, position.y, position.z); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - glPushMatrix(); - glm::vec3 positionToCenter = center - position; - glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); - if (_isSolid) { - if (_borderSize > 0) { - // Draw a cube at a larger size behind the main cube, creating - // a border effect. - // Disable writing to the depth mask so that the "border" cube will not - // occlude the main cube. This means the border could be covered by - // overlays that are further back and drawn later, but this is good - // enough for the use-case. - glDepthMask(GL_FALSE); - glPushMatrix(); - glScalef(dimensions.x * _borderSize, dimensions.y * _borderSize, dimensions.z * _borderSize); - if (_drawOnHUD) { - DependencyManager::get()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha)); - } else { - DependencyManager::get()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha)); - } + auto batch = args->_batch; - glPopMatrix(); - glDepthMask(GL_TRUE); - } + if (batch) { + Transform transform; + transform.setTranslation(position); + transform.setRotation(rotation); + if (_isSolid) { + // if (_borderSize > 0) { + // // Draw a cube at a larger size behind the main cube, creating + // // a border effect. + // // Disable writing to the depth mask so that the "border" cube will not + // // occlude the main cube. This means the border could be covered by + // // overlays that are further back and drawn later, but this is good + // // enough for the use-case. + // transform.setScale(dimensions * _borderSize); + // batch->setModelTransform(transform); + // DependencyManager::get()->renderSolidCube(*batch, 1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha)); + // } + + transform.setScale(dimensions); + batch->setModelTransform(transform); + + DependencyManager::get()->renderSolidCube(*batch, 1.0f, cubeColor); + } else { + + if (getIsDashedLine()) { + transform.setScale(1.0f); + batch->setModelTransform(transform); + + glm::vec3 halfDimensions = dimensions / 2.0f; + glm::vec3 bottomLeftNear(-halfDimensions.x, -halfDimensions.y, -halfDimensions.z); + glm::vec3 bottomRightNear(halfDimensions.x, -halfDimensions.y, -halfDimensions.z); + glm::vec3 topLeftNear(-halfDimensions.x, halfDimensions.y, -halfDimensions.z); + glm::vec3 topRightNear(halfDimensions.x, halfDimensions.y, -halfDimensions.z); + + glm::vec3 bottomLeftFar(-halfDimensions.x, -halfDimensions.y, halfDimensions.z); + glm::vec3 bottomRightFar(halfDimensions.x, -halfDimensions.y, halfDimensions.z); + glm::vec3 topLeftFar(-halfDimensions.x, halfDimensions.y, halfDimensions.z); + glm::vec3 topRightFar(halfDimensions.x, halfDimensions.y, halfDimensions.z); + + auto geometryCache = DependencyManager::get(); + + geometryCache->renderDashedLine(*batch, bottomLeftNear, bottomRightNear, cubeColor); + geometryCache->renderDashedLine(*batch, bottomRightNear, bottomRightFar, cubeColor); + geometryCache->renderDashedLine(*batch, bottomRightFar, bottomLeftFar, cubeColor); + geometryCache->renderDashedLine(*batch, bottomLeftFar, bottomLeftNear, cubeColor); + + geometryCache->renderDashedLine(*batch, topLeftNear, topRightNear, cubeColor); + geometryCache->renderDashedLine(*batch, topRightNear, topRightFar, cubeColor); + geometryCache->renderDashedLine(*batch, topRightFar, topLeftFar, cubeColor); + geometryCache->renderDashedLine(*batch, topLeftFar, topLeftNear, cubeColor); + + geometryCache->renderDashedLine(*batch, bottomLeftNear, topLeftNear, cubeColor); + geometryCache->renderDashedLine(*batch, bottomRightNear, topRightNear, cubeColor); + geometryCache->renderDashedLine(*batch, bottomLeftFar, topLeftFar, cubeColor); + geometryCache->renderDashedLine(*batch, bottomRightFar, topRightFar, cubeColor); - glPushMatrix(); - glScalef(dimensions.x, dimensions.y, dimensions.z); - if (_drawOnHUD) { - DependencyManager::get()->renderSolidCube(1.0f, cubeColor); - } else { - DependencyManager::get()->renderSolidCube(1.0f, cubeColor); - } - glPopMatrix(); } else { - glLineWidth(_lineWidth); - - if (getIsDashedLine()) { - glm::vec3 halfDimensions = dimensions / 2.0f; - glm::vec3 bottomLeftNear(-halfDimensions.x, -halfDimensions.y, -halfDimensions.z); - glm::vec3 bottomRightNear(halfDimensions.x, -halfDimensions.y, -halfDimensions.z); - glm::vec3 topLeftNear(-halfDimensions.x, halfDimensions.y, -halfDimensions.z); - glm::vec3 topRightNear(halfDimensions.x, halfDimensions.y, -halfDimensions.z); - - glm::vec3 bottomLeftFar(-halfDimensions.x, -halfDimensions.y, halfDimensions.z); - glm::vec3 bottomRightFar(halfDimensions.x, -halfDimensions.y, halfDimensions.z); - glm::vec3 topLeftFar(-halfDimensions.x, halfDimensions.y, halfDimensions.z); - glm::vec3 topRightFar(halfDimensions.x, halfDimensions.y, halfDimensions.z); - - auto geometryCache = DependencyManager::get(); - - geometryCache->renderDashedLine(bottomLeftNear, bottomRightNear, cubeColor); - geometryCache->renderDashedLine(bottomRightNear, bottomRightFar, cubeColor); - geometryCache->renderDashedLine(bottomRightFar, bottomLeftFar, cubeColor); - geometryCache->renderDashedLine(bottomLeftFar, bottomLeftNear, cubeColor); - - geometryCache->renderDashedLine(topLeftNear, topRightNear, cubeColor); - geometryCache->renderDashedLine(topRightNear, topRightFar, cubeColor); - geometryCache->renderDashedLine(topRightFar, topLeftFar, cubeColor); - geometryCache->renderDashedLine(topLeftFar, topLeftNear, cubeColor); - - geometryCache->renderDashedLine(bottomLeftNear, topLeftNear, cubeColor); - geometryCache->renderDashedLine(bottomRightNear, topRightNear, cubeColor); - geometryCache->renderDashedLine(bottomLeftFar, topLeftFar, cubeColor); - geometryCache->renderDashedLine(bottomRightFar, topRightFar, cubeColor); - - } else { - glScalef(dimensions.x, dimensions.y, dimensions.z); - DependencyManager::get()->renderWireCube(1.0f, cubeColor); - } + transform.setScale(dimensions); + batch->setModelTransform(transform); + DependencyManager::get()->renderWireCube(*batch, 1.0f, cubeColor); } - glPopMatrix(); - glPopMatrix(); + } + } else { + float glowLevel = getGlowLevel(); + Glower* glower = NULL; + if (glowLevel > 0.0f) { + glower = new Glower(glowLevel); + } - if (glower) { - delete glower; + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + glPushMatrix(); + glm::vec3 positionToCenter = center - position; + glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); + if (_isSolid) { + if (_borderSize > 0) { + // Draw a cube at a larger size behind the main cube, creating + // a border effect. + // Disable writing to the depth mask so that the "border" cube will not + // occlude the main cube. This means the border could be covered by + // overlays that are further back and drawn later, but this is good + // enough for the use-case. + glDepthMask(GL_FALSE); + glPushMatrix(); + glScalef(dimensions.x * _borderSize, dimensions.y * _borderSize, dimensions.z * _borderSize); + + if (_drawOnHUD) { + DependencyManager::get()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha)); + } else { + DependencyManager::get()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha)); + } + + glPopMatrix(); + glDepthMask(GL_TRUE); + } + + glPushMatrix(); + glScalef(dimensions.x, dimensions.y, dimensions.z); + if (_drawOnHUD) { + DependencyManager::get()->renderSolidCube(1.0f, cubeColor); + } else { + DependencyManager::get()->renderSolidCube(1.0f, cubeColor); + } + glPopMatrix(); + } else { + glLineWidth(_lineWidth); + + if (getIsDashedLine()) { + glm::vec3 halfDimensions = dimensions / 2.0f; + glm::vec3 bottomLeftNear(-halfDimensions.x, -halfDimensions.y, -halfDimensions.z); + glm::vec3 bottomRightNear(halfDimensions.x, -halfDimensions.y, -halfDimensions.z); + glm::vec3 topLeftNear(-halfDimensions.x, halfDimensions.y, -halfDimensions.z); + glm::vec3 topRightNear(halfDimensions.x, halfDimensions.y, -halfDimensions.z); + + glm::vec3 bottomLeftFar(-halfDimensions.x, -halfDimensions.y, halfDimensions.z); + glm::vec3 bottomRightFar(halfDimensions.x, -halfDimensions.y, halfDimensions.z); + glm::vec3 topLeftFar(-halfDimensions.x, halfDimensions.y, halfDimensions.z); + glm::vec3 topRightFar(halfDimensions.x, halfDimensions.y, halfDimensions.z); + + auto geometryCache = DependencyManager::get(); + + geometryCache->renderDashedLine(bottomLeftNear, bottomRightNear, cubeColor); + geometryCache->renderDashedLine(bottomRightNear, bottomRightFar, cubeColor); + geometryCache->renderDashedLine(bottomRightFar, bottomLeftFar, cubeColor); + geometryCache->renderDashedLine(bottomLeftFar, bottomLeftNear, cubeColor); + + geometryCache->renderDashedLine(topLeftNear, topRightNear, cubeColor); + geometryCache->renderDashedLine(topRightNear, topRightFar, cubeColor); + geometryCache->renderDashedLine(topRightFar, topLeftFar, cubeColor); + geometryCache->renderDashedLine(topLeftFar, topLeftNear, cubeColor); + + geometryCache->renderDashedLine(bottomLeftNear, topLeftNear, cubeColor); + geometryCache->renderDashedLine(bottomRightNear, topRightNear, cubeColor); + geometryCache->renderDashedLine(bottomLeftFar, topLeftFar, cubeColor); + geometryCache->renderDashedLine(bottomRightFar, topRightFar, cubeColor); + + } else { + glScalef(dimensions.x, dimensions.y, dimensions.z); + DependencyManager::get()->renderWireCube(1.0f, cubeColor); + } + } + glPopMatrix(); + glPopMatrix(); + + if (glower) { + delete glower; + } } } diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index 6672a88e45..34c983d7b2 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -37,41 +37,57 @@ void Line3DOverlay::render(RenderArgs* args) { return; // do nothing if we're not visible } - float glowLevel = getGlowLevel(); - Glower* glower = NULL; - if (glowLevel > 0.0f) { - glower = new Glower(glowLevel); - } - - glPushMatrix(); - - glDisable(GL_LIGHTING); - glLineWidth(_lineWidth); - float alpha = getAlpha(); xColor color = getColor(); const float MAX_COLOR = 255.0f; glm::vec4 colorv4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - glm::vec3 position = getPosition(); - glm::quat rotation = getRotation(); + auto batch = args->_batch; - glTranslatef(position.x, position.y, position.z); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + if (batch) { + Transform transform; + transform.setTranslation(_position); + transform.setRotation(_rotation); + batch->setModelTransform(transform); - if (getIsDashedLine()) { - // TODO: add support for color to renderDashedLine() - DependencyManager::get()->renderDashedLine(_position, _end, colorv4, _geometryCacheID); + if (getIsDashedLine()) { + // TODO: add support for color to renderDashedLine() + DependencyManager::get()->renderDashedLine(*batch, _position, _end, colorv4, _geometryCacheID); + } else { + DependencyManager::get()->renderLine(*batch, _start, _end, colorv4, _geometryCacheID); + } } else { - DependencyManager::get()->renderLine(_start, _end, colorv4, _geometryCacheID); - } - glEnable(GL_LIGHTING); + float glowLevel = getGlowLevel(); + Glower* glower = NULL; + if (glowLevel > 0.0f) { + glower = new Glower(glowLevel); + } - glPopMatrix(); + glPushMatrix(); - if (glower) { - delete glower; + glDisable(GL_LIGHTING); + glLineWidth(_lineWidth); + + glm::vec3 position = getPosition(); + glm::quat rotation = getRotation(); + + glTranslatef(position.x, position.y, position.z); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + + if (getIsDashedLine()) { + // TODO: add support for color to renderDashedLine() + DependencyManager::get()->renderDashedLine(_position, _end, colorv4, _geometryCacheID); + } else { + DependencyManager::get()->renderLine(_start, _end, colorv4, _geometryCacheID); + } + glEnable(GL_LIGHTING); + + glPopMatrix(); + + if (glower) { + delete glower; + } } } diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index a0e8d06b41..f5fba0ed05 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -39,33 +39,45 @@ void Sphere3DOverlay::render(RenderArgs* args) { const float MAX_COLOR = 255.0f; glm::vec4 sphereColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - glDisable(GL_LIGHTING); - - glm::vec3 position = getPosition(); - glm::vec3 center = getCenter(); - glm::vec3 dimensions = getDimensions(); - glm::quat rotation = getRotation(); + auto batch = args->_batch; - float glowLevel = getGlowLevel(); - Glower* glower = NULL; - if (glowLevel > 0.0f) { - glower = new Glower(glowLevel); - } + if (batch) { + Transform transform; + transform.setTranslation(_position); + transform.setRotation(_rotation); + transform.setScale(_dimensions); + + batch->setModelTransform(transform); + DependencyManager::get()->renderSphere(*batch, 1.0f, SLICES, SLICES, sphereColor, _isSolid); + } else { + glDisable(GL_LIGHTING); + + glm::vec3 position = getPosition(); + glm::vec3 center = getCenter(); + glm::vec3 dimensions = getDimensions(); + glm::quat rotation = getRotation(); + + float glowLevel = getGlowLevel(); + Glower* glower = NULL; + if (glowLevel > 0.0f) { + glower = new Glower(glowLevel); + } - glPushMatrix(); - glTranslatef(position.x, position.y, position.z); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); glPushMatrix(); - glm::vec3 positionToCenter = center - position; - glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); - glScalef(dimensions.x, dimensions.y, dimensions.z); - DependencyManager::get()->renderSphere(1.0f, SLICES, SLICES, sphereColor, _isSolid); + glTranslatef(position.x, position.y, position.z); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + glPushMatrix(); + glm::vec3 positionToCenter = center - position; + glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); + glScalef(dimensions.x, dimensions.y, dimensions.z); + DependencyManager::get()->renderSphere(1.0f, SLICES, SLICES, sphereColor, _isSolid); + glPopMatrix(); glPopMatrix(); - glPopMatrix(); - - if (glower) { - delete glower; + + if (glower) { + delete glower; + } } } From a5f7ba51b67618845eecbca526b3029887884423 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 16 Jun 2015 10:13:47 -0700 Subject: [PATCH 65/94] only show polyvox property widgets if we are editing a polyvox. --- examples/html/entityProperties.html | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index f029088b1a..27a2929d1c 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -354,7 +354,8 @@ var elZoneAtmosphereScatteringWavelengthsZ = document.getElementById("property-zone-atmosphere-scattering-wavelengths-z"); var elZoneAtmosphereHasStars = document.getElementById("property-zone-atmosphere-has-stars"); - var elPolyVoxSelections = document.querySelectorAll(".poly-vox-section"); + var elPolyVoxSections = document.querySelectorAll(".poly-vox-section"); + allSections.push(elPolyVoxSections); var elVoxelVolumeSizeX = document.getElementById("property-voxel-volume-size-x"); var elVoxelVolumeSizeY = document.getElementById("property-voxel-volume-size-y"); var elVoxelVolumeSizeZ = document.getElementById("property-voxel-volume-size-z"); @@ -602,6 +603,10 @@ elParticleLocalGravity.value = properties.localGravity.toFixed(2); elParticleRadius.value = properties.particleRadius.toFixed(3); } else if (properties.type == "PolyVox") { + for (var i = 0; i < elPolyVoxSections.length; i++) { + elPolyVoxSections[i].style.display = 'block'; + } + elVoxelVolumeSizeX.value = properties.voxelVolumeSize.x.toFixed(2); elVoxelVolumeSizeY.value = properties.voxelVolumeSize.y.toFixed(2); elVoxelVolumeSizeZ.value = properties.voxelVolumeSize.z.toFixed(2); @@ -1014,9 +1019,9 @@
Voxel Volume Size
-
X
-
Y
-
Z
+
X
+
Y
+
Z
Surface Extractor
From d7eddc398b5b87b73e14dab2f716d71b64877087 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 16 Jun 2015 10:14:01 -0700 Subject: [PATCH 66/94] don't make changes if the polyvox is locked --- .../src/RenderablePolyVoxEntityItem.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index d8e7ca0a18..71c3537a0c 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -238,6 +238,9 @@ void RenderablePolyVoxEntityItem::setVoxelInternal(int x, int y, int z, uint8_t void RenderablePolyVoxEntityItem::setVoxel(int x, int y, int z, uint8_t toValue) { + if (_locked) { + return; + } setVoxelInternal(x, y, z, toValue); compressVolumeData(); } @@ -263,6 +266,10 @@ void RenderablePolyVoxEntityItem::updateOnCount(int x, int y, int z, uint8_t toV } void RenderablePolyVoxEntityItem::setAll(uint8_t toValue) { + if (_locked) { + return; + } + for (int z = 0; z < _voxelVolumeSize.z; z++) { for (int y = 0; y < _voxelVolumeSize.y; y++) { for (int x = 0; x < _voxelVolumeSize.x; x++) { @@ -275,11 +282,19 @@ void RenderablePolyVoxEntityItem::setAll(uint8_t toValue) { } void RenderablePolyVoxEntityItem::setVoxelInVolume(glm::vec3 position, uint8_t toValue) { + if (_locked) { + return; + } + // same as setVoxel but takes a vector rather than 3 floats. - setVoxel(position.x, position.y, position.z, toValue); + setVoxel((int)position.x, (int)position.y, (int)position.z, toValue); } void RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue) { + if (_locked) { + return; + } + // This three-level for loop iterates over every voxel in the volume for (int z = 0; z < _voxelVolumeSize.z; z++) { for (int y = 0; y < _voxelVolumeSize.y; y++) { From 917aa5db6d742ab064214889348965799bc85537 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Tue, 16 Jun 2015 13:21:40 -0600 Subject: [PATCH 67/94] Code review changes --- libraries/auto-updater/src/AutoUpdater.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/auto-updater/src/AutoUpdater.h b/libraries/auto-updater/src/AutoUpdater.h index a02ff11ca9..70867e5a44 100644 --- a/libraries/auto-updater/src/AutoUpdater.h +++ b/libraries/auto-updater/src/AutoUpdater.h @@ -27,6 +27,7 @@ #include #include #include + #include const QUrl BUILDS_XML_URL("https://highfidelity.com/builds.xml"); @@ -39,7 +40,7 @@ public: AutoUpdater(); void checkForUpdate(); - const QMap> &getBuildData() { return _builds; } + const QMap>& getBuildData() { return _builds; } void performAutoUpdate(int version); signals: @@ -62,7 +63,6 @@ private: private slots: void parseLatestVersionData(); void checkVersionAndNotify(); - }; #endif // _hifi_AutoUpdater_h From 1af4b1c54c0ae39dd0df9ed8a1e101a4c0d27955 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 16 Jun 2015 13:17:13 -0700 Subject: [PATCH 68/94] cleanup handleUrl call, add disabling of Menu items --- interface/src/Menu.cpp | 28 +++++++++++------- libraries/networking/src/AddressManager.cpp | 32 +++++++++++++++++---- libraries/networking/src/AddressManager.h | 6 ++++ 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 149fd2e043..f69f8639fb 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -94,17 +94,25 @@ Menu::Menu() { addDisabledActionAndSeparator(fileMenu, "History"); - addActionToQMenuAndActionHash(fileMenu, - MenuOption::Back, - 0, - addressManager.data(), - SLOT(goBack())); + QAction* backAction = addActionToQMenuAndActionHash(fileMenu, + MenuOption::Back, + 0, + addressManager.data(), + SLOT(goBack())); - addActionToQMenuAndActionHash(fileMenu, - MenuOption::Forward, - 0, - addressManager.data(), - SLOT(goForward())); + QAction* forwardAction = addActionToQMenuAndActionHash(fileMenu, + MenuOption::Forward, + 0, + addressManager.data(), + SLOT(goForward())); + + // connect to the AddressManager signal to enable and disable the back and forward menu items + connect(addressManager.data(), &AddressManager::goBackPossible, backAction, &QAction::setEnabled); + connect(addressManager.data(), &AddressManager::goForwardPossible, forwardAction, &QAction::setEnabled); + + // set the two actions to start disabled since the stacks are clear on startup + backAction->setDisabled(true); + forwardAction->setDisabled(true); addDisabledActionAndSeparator(fileMenu, "Location"); qApp->getBookmarks()->setupMenus(this, fileMenu); diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index dd8dfeec2b..e60e7fd41d 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -62,11 +62,13 @@ void AddressManager::loadSettings(const QString& lookupString) { void AddressManager::goBack() { if (_backStack.size() > 0) { - // pop a URL from the backStack - QUrl poppedURL = _backStack.pop(); - // go to that address - handleUrl(poppedURL, LookupTrigger::Back); + handleUrl(_backStack.pop(), LookupTrigger::Back); + + if (_backStack.size() == 0) { + // the back stack is now empty so it is no longer possible to go back - emit that signal + emit goBackPossible(false); + } } } @@ -74,6 +76,11 @@ void AddressManager::goForward() { if (_forwardStack.size() > 0) { // pop a URL from the forwardStack and go to that address handleUrl(_forwardStack.pop(), LookupTrigger::Forward); + + if (_forwardStack.size() == 0) { + // the forward stack is empty so it is no longer possible to go forwards - emit that signal + emit goForwardPossible(false); + } } } @@ -580,12 +587,27 @@ void AddressManager::addCurrentAddressToHistory(LookupTrigger trigger) { if (trigger == LookupTrigger::UserInput) { // anyime the user has manually looked up an address we know we should clear the forward stack _forwardStack.clear(); + + emit goForwardPossible(false); } if (trigger == LookupTrigger::Back) { - // when the user is going back, we move the current address to the forward stack and do not but it into the back stack + // we're about to push to the forward stack + // if it's currently empty emit our signal to say that going forward is now possible + if (_forwardStack.size() == 0) { + emit goForwardPossible(true); + } + + // when the user is going back, we move the current address to the forward stack + // and do not but it into the back stack _forwardStack.push(currentAddress()); } else { + // we're about to push to the back stack + // if it's currently empty emit our signal to say that going forward is now possible + if (_forwardStack.size() == 0) { + emit goBackPossible(true); + } + // unless this was triggered from the result of a named path lookup, add the current address to the history _backStack.push(currentAddress()); } diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index d985c5d2c6..a0fc7eb0be 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -81,13 +81,19 @@ signals: void lookupResultsFinished(); void lookupResultIsOffline(); void lookupResultIsNotFound(); + void possibleDomainChangeRequired(const QString& newHostname, quint16 newPort); void possibleDomainChangeRequiredViaICEForID(const QString& iceServerHostname, const QUuid& domainID); + void locationChangeRequired(const glm::vec3& newPosition, bool hasOrientationChange, const glm::quat& newOrientation, bool shouldFaceLocation); void pathChangeRequired(const QString& newPath); void hostChanged(const QString& newHost); + + void goBackPossible(bool isPossible); + void goForwardPossible(bool isPossible); + protected: AddressManager(); private slots: From e9bdf7e73a00349dd0702642fa901f17457c6e9e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 16 Jun 2015 13:39:11 -0700 Subject: [PATCH 69/94] add missing bools to handleViewpoint call --- libraries/networking/src/AddressManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index e60e7fd41d..6941d7335d 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -435,7 +435,7 @@ bool AddressManager::handleDomainID(const QString& host) { } void AddressManager::handlePath(const QString& path, LookupTrigger trigger, bool wasPathOnly) { - if (!handleViewpoint(path)) { + if (!handleViewpoint(path, false, wasPathOnly)) { qCDebug(networking) << "User entered path could not be handled as a viewpoint - " << path << "- wll attempt to ask domain-server to resolve."; From e1fb01e6ddf93b57a90caac92c2d366280456773 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Tue, 16 Jun 2015 14:49:19 -0600 Subject: [PATCH 70/94] Fixing bug on platforms for which we don't release --- libraries/auto-updater/src/AutoUpdater.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libraries/auto-updater/src/AutoUpdater.cpp b/libraries/auto-updater/src/AutoUpdater.cpp index 445cdaec7b..50d11a2af5 100644 --- a/libraries/auto-updater/src/AutoUpdater.cpp +++ b/libraries/auto-updater/src/AutoUpdater.cpp @@ -104,9 +104,13 @@ void AutoUpdater::parseLatestVersionData() { } void AutoUpdater::checkVersionAndNotify() { + if (QCoreApplication::applicationVersion() == "dev" || _builds.empty()) { + // No version checking is required in dev builds or when not builds + // data was found for the platform + return; + } int latestVersionAvailable = _builds.lastKey(); - if (QCoreApplication::applicationVersion() != "dev" && - QCoreApplication::applicationVersion().toInt() < latestVersionAvailable) { + if (QCoreApplication::applicationVersion().toInt() < latestVersionAvailable) { emit newVersionIsAvailable(); } } From 80ab2f31f44895200f5c05b35d3a7d1872c55fe7 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Tue, 16 Jun 2015 14:49:55 -0600 Subject: [PATCH 71/94] typo --- libraries/auto-updater/src/AutoUpdater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/auto-updater/src/AutoUpdater.cpp b/libraries/auto-updater/src/AutoUpdater.cpp index 50d11a2af5..8c6aa5605d 100644 --- a/libraries/auto-updater/src/AutoUpdater.cpp +++ b/libraries/auto-updater/src/AutoUpdater.cpp @@ -105,7 +105,7 @@ void AutoUpdater::parseLatestVersionData() { void AutoUpdater::checkVersionAndNotify() { if (QCoreApplication::applicationVersion() == "dev" || _builds.empty()) { - // No version checking is required in dev builds or when not builds + // No version checking is required in dev builds or when no build // data was found for the platform return; } From 167e7d13777693ce748ffdc3e145cd7244c8e4c0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 16 Jun 2015 14:05:14 -0700 Subject: [PATCH 72/94] first cut at atmospheres in batch (doesn't work) --- interface/src/Application.cpp | 10 +- interface/src/Environment.cpp | 150 +++++++++++++++--- interface/src/Environment.h | 17 +- libraries/gpu/src/gpu/Batch.h | 2 + libraries/gpu/src/gpu/GLBackend.cpp | 28 ++++ .../model/src/model/SkyFromAtmosphere.slf | 6 +- .../model/src/model/SkyFromAtmosphere.slv | 21 +-- libraries/model/src/model/SkyFromSpace.slf | 4 +- libraries/model/src/model/SkyFromSpace.slv | 25 ++- 9 files changed, 217 insertions(+), 46 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 22cbad6775..4c84e39b64 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3255,6 +3255,9 @@ namespace render { template <> const Item::Bound payloadGetBound(const BackgroundRenderData::Pointer& stuff) { return Item::Bound(); } template <> void payloadRender(const BackgroundRenderData::Pointer& background, RenderArgs* args) { + Q_ASSERT(args->_batch); + gpu::Batch& batch = *args->_batch; + // Background rendering decision auto skyStage = DependencyManager::get()->getSkyStage(); auto skybox = model::SkyboxPointer(); @@ -3322,7 +3325,11 @@ namespace render { PerformanceTimer perfTimer("atmosphere"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... atmosphere..."); - background->_environment->renderAtmospheres(*(args->_viewFrustum)); + //gpu::Batch batch; + background->_environment->renderAtmospheres(batch, *(args->_viewFrustum)); + //gpu::GLBackend::renderBatch(batch, true); + //glUseProgram(0); + } } @@ -3333,7 +3340,6 @@ namespace render { if (skybox) { gpu::Batch batch; model::Skybox::render(batch, *(Application::getInstance()->getDisplayViewFrustum()), *skybox); - gpu::GLBackend::renderBatch(batch, true); glUseProgram(0); } diff --git a/interface/src/Environment.cpp b/interface/src/Environment.cpp index 9f197920d9..1e9c571a57 100644 --- a/interface/src/Environment.cpp +++ b/interface/src/Environment.cpp @@ -28,6 +28,11 @@ #include "Environment.h" +#include "../../build/libraries/model/SkyFromSpace_vert.h" +#include "../../build/libraries/model/SkyFromSpace_frag.h" +#include "../../build/libraries/model/SkyFromAtmosphere_vert.h" +#include "../../build/libraries/model/SkyFromAtmosphere_frag.h" + uint qHash(const HifiSockAddr& sockAddr) { if (sockAddr.getAddress().isNull()) { return 0; // shouldn't happen, but if it does, zero is a perfectly valid hash @@ -56,6 +61,15 @@ void Environment::init() { _skyFromAtmosphereProgram = createSkyProgram("Atmosphere", _skyFromAtmosphereUniformLocations); _skyFromSpaceProgram = createSkyProgram("Space", _skyFromSpaceUniformLocations); + + + + qDebug() << "line:" << __LINE__; + setupAtmosphereProgram(SkyFromSpace_vert, SkyFromSpace_frag, _newSkyFromSpaceProgram, _newSkyFromSpaceUniformLocations); + qDebug() << "line:" << __LINE__; + setupAtmosphereProgram(SkyFromAtmosphere_vert, SkyFromAtmosphere_frag, _newSkyFromAtmosphereProgram, _newSkyFromAtmosphereUniformLocations); + qDebug() << "line:" << __LINE__; + // start off with a default-constructed environment data _data[HifiSockAddr()][0]; @@ -63,22 +77,84 @@ void Environment::init() { _initialized = true; } +void Environment::setupAtmosphereProgram(const char* vertSource, const char* fragSource, gpu::PipelinePointer& pipelineProgram, int* locations) { + + auto VS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(vertSource))); + auto PS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(fragSource))); + + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PS)); + + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setCullMode(gpu::State::CULL_NONE); + state->setDepthTest(false); + state->setBlendFunction(true,gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); + pipelineProgram = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); + + locations[CAMERA_POS_LOCATION] = program->getUniforms().findLocation("v3CameraPos"); + locations[LIGHT_POS_LOCATION] = program->getUniforms().findLocation("v3LightPos"); + locations[INV_WAVELENGTH_LOCATION] = program->getUniforms().findLocation("v3InvWavelength"); + locations[CAMERA_HEIGHT2_LOCATION] = program->getUniforms().findLocation("fCameraHeight2"); + locations[OUTER_RADIUS_LOCATION] = program->getUniforms().findLocation("fOuterRadius"); + locations[OUTER_RADIUS2_LOCATION] = program->getUniforms().findLocation("fOuterRadius2"); + locations[INNER_RADIUS_LOCATION] = program->getUniforms().findLocation("fInnerRadius"); + locations[KR_ESUN_LOCATION] = program->getUniforms().findLocation("fKrESun"); + locations[KM_ESUN_LOCATION] = program->getUniforms().findLocation("fKmESun"); + locations[KR_4PI_LOCATION] = program->getUniforms().findLocation("fKr4PI"); + locations[KM_4PI_LOCATION] = program->getUniforms().findLocation("fKm4PI"); + locations[SCALE_LOCATION] = program->getUniforms().findLocation("fScale"); + locations[SCALE_DEPTH_LOCATION] = program->getUniforms().findLocation("fScaleDepth"); + locations[SCALE_OVER_SCALE_DEPTH_LOCATION] = program->getUniforms().findLocation("fScaleOverScaleDepth"); + locations[G_LOCATION] = program->getUniforms().findLocation("g"); + locations[G2_LOCATION] = program->getUniforms().findLocation("g2"); + + /* + + program.addShaderFromSourceCode(QGLShader::Vertex, vertSource); + program.addShaderFromSourceCode(QGLShader::Fragment, fragSource); + program.link(); + + program.bind(); + + locations[CAMERA_POS_LOCATION] = program.uniformLocation("v3CameraPos"); + locations[LIGHT_POS_LOCATION] = program.uniformLocation("v3LightPos"); + locations[INV_WAVELENGTH_LOCATION] = program.uniformLocation("v3InvWavelength"); + locations[CAMERA_HEIGHT2_LOCATION] = program.uniformLocation("fCameraHeight2"); + locations[OUTER_RADIUS_LOCATION] = program.uniformLocation("fOuterRadius"); + locations[OUTER_RADIUS2_LOCATION] = program.uniformLocation("fOuterRadius2"); + locations[INNER_RADIUS_LOCATION] = program.uniformLocation("fInnerRadius"); + locations[KR_ESUN_LOCATION] = program.uniformLocation("fKrESun"); + locations[KM_ESUN_LOCATION] = program.uniformLocation("fKmESun"); + locations[KR_4PI_LOCATION] = program.uniformLocation("fKr4PI"); + locations[KM_4PI_LOCATION] = program.uniformLocation("fKm4PI"); + locations[SCALE_LOCATION] = program.uniformLocation("fScale"); + locations[SCALE_DEPTH_LOCATION] = program.uniformLocation("fScaleDepth"); + locations[SCALE_OVER_SCALE_DEPTH_LOCATION] = program.uniformLocation("fScaleOverScaleDepth"); + locations[G_LOCATION] = program.uniformLocation("g"); + locations[G2_LOCATION] = program.uniformLocation("g2"); + + program.release(); + */ +} + void Environment::resetToDefault() { _data.clear(); _data[HifiSockAddr()][0]; } -void Environment::renderAtmospheres(ViewFrustum& camera) { +void Environment::renderAtmospheres(gpu::Batch& batch, ViewFrustum& camera) { // get the lock for the duration of the call QMutexLocker locker(&_mutex); if (_environmentIsOverridden) { - renderAtmosphere(camera, _overrideData); + renderAtmosphere(batch, camera, _overrideData); } else { foreach (const ServerData& serverData, _data) { // TODO: do something about EnvironmentData foreach (const EnvironmentData& environmentData, serverData) { - renderAtmosphere(camera, environmentData); + renderAtmosphere(batch, camera, environmentData); } } } @@ -228,30 +304,39 @@ ProgramObject* Environment::createSkyProgram(const char* from, int* locations) { return program; } -void Environment::renderAtmosphere(ViewFrustum& camera, const EnvironmentData& data) { +void Environment::renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const EnvironmentData& data) { glm::vec3 center = data.getAtmosphereCenter(); - glPushMatrix(); - glTranslatef(center.x, center.y, center.z); + //glPushMatrix(); + //glTranslatef(center.x, center.y, center.z); + + Transform transform; + transform.setTranslation(center); + transform.setScale(1.0f); + batch.setModelTransform(transform); glm::vec3 relativeCameraPos = camera.getPosition() - center; float height = glm::length(relativeCameraPos); // use the appropriate shader depending on whether we're inside or outside - ProgramObject* program; + //ProgramObject* program; int* locations; if (height < data.getAtmosphereOuterRadius()) { - program = _skyFromAtmosphereProgram; - locations = _skyFromAtmosphereUniformLocations; + //program = &_newSkyFromAtmosphereProgram; + batch.setPipeline(_newSkyFromAtmosphereProgram); + locations = _newSkyFromAtmosphereUniformLocations; } else { - program = _skyFromSpaceProgram; - locations = _skyFromSpaceUniformLocations; + //program = &_newSkyFromSpaceProgram; + batch.setPipeline(_newSkyFromSpaceProgram); + locations = _newSkyFromSpaceUniformLocations; } // the constants here are from Sean O'Neil's GPU Gems entry // (http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter16.html), GameEngine.cpp - program->bind(); + + //program->bind(); + /* program->setUniform(locations[CAMERA_POS_LOCATION], relativeCameraPos); glm::vec3 lightDirection = glm::normalize(data.getSunLocation()); program->setUniform(locations[LIGHT_POS_LOCATION], lightDirection); @@ -273,15 +358,40 @@ void Environment::renderAtmosphere(ViewFrustum& camera, const EnvironmentData& d (1.0f / (data.getAtmosphereOuterRadius() - data.getAtmosphereInnerRadius())) / 0.25f); program->setUniformValue(locations[G_LOCATION], -0.990f); program->setUniformValue(locations[G2_LOCATION], -0.990f * -0.990f); + */ - glDepthMask(GL_FALSE); - glDisable(GL_DEPTH_TEST); - glDisable(GL_CULL_FACE); - glEnable(GL_BLEND); - DependencyManager::get()->renderSphere(1.0f, 100, 50, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); //Draw a unit sphere - glDepthMask(GL_TRUE); + + batch._glUniform3fv(locations[CAMERA_POS_LOCATION], relativeCameraPos); + glm::vec3 lightDirection = glm::normalize(data.getSunLocation()); + batch._glUniform3fv(locations[LIGHT_POS_LOCATION], lightDirection); + batch._glUniform3f(locations[INV_WAVELENGTH_LOCATION], + 1 / powf(data.getScatteringWavelengths().r, 4.0f), + 1 / powf(data.getScatteringWavelengths().g, 4.0f), + 1 / powf(data.getScatteringWavelengths().b, 4.0f)); + batch._glUniform1f(locations[CAMERA_HEIGHT2_LOCATION], height * height); + batch._glUniform1f(locations[OUTER_RADIUS_LOCATION], data.getAtmosphereOuterRadius()); + batch._glUniform1f(locations[OUTER_RADIUS2_LOCATION], data.getAtmosphereOuterRadius() * data.getAtmosphereOuterRadius()); + batch._glUniform1f(locations[INNER_RADIUS_LOCATION], data.getAtmosphereInnerRadius()); + batch._glUniform1f(locations[KR_ESUN_LOCATION], data.getRayleighScattering() * data.getSunBrightness()); + batch._glUniform1f(locations[KM_ESUN_LOCATION], data.getMieScattering() * data.getSunBrightness()); + batch._glUniform1f(locations[KR_4PI_LOCATION], data.getRayleighScattering() * 4.0f * PI); + batch._glUniform1f(locations[KM_4PI_LOCATION], data.getMieScattering() * 4.0f * PI); + batch._glUniform1f(locations[SCALE_LOCATION], 1.0f / (data.getAtmosphereOuterRadius() - data.getAtmosphereInnerRadius())); + batch._glUniform1f(locations[SCALE_DEPTH_LOCATION], 0.25f); + batch._glUniform1f(locations[SCALE_OVER_SCALE_DEPTH_LOCATION], + (1.0f / (data.getAtmosphereOuterRadius() - data.getAtmosphereInnerRadius())) / 0.25f); + batch._glUniform1f(locations[G_LOCATION], -0.990f); + batch._glUniform1f(locations[G2_LOCATION], -0.990f * -0.990f); + + + //glDepthMask(GL_FALSE); + //glDisable(GL_DEPTH_TEST); + //glDisable(GL_CULL_FACE); + //glEnable(GL_BLEND); + DependencyManager::get()->renderSphere(batch,1.0f, 100, 50, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); //Draw a unit sphere + //glDepthMask(GL_TRUE); - program->release(); + //program->release(); - glPopMatrix(); + //glPopMatrix(); } diff --git a/interface/src/Environment.h b/interface/src/Environment.h index c1b4171947..9048678230 100644 --- a/interface/src/Environment.h +++ b/interface/src/Environment.h @@ -16,6 +16,9 @@ #include #include +#include + +#include #include "EnvironmentData.h" @@ -29,7 +32,7 @@ public: void init(); void resetToDefault(); - void renderAtmospheres(ViewFrustum& camera); + void renderAtmospheres(gpu::Batch& batch, ViewFrustum& camera); void override(const EnvironmentData& overrideData) { _overrideData = overrideData; _environmentIsOverridden = true; } void endOverride() { _environmentIsOverridden = false; } @@ -46,12 +49,12 @@ private: ProgramObject* createSkyProgram(const char* from, int* locations); - void renderAtmosphere(ViewFrustum& camera, const EnvironmentData& data); + void renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const EnvironmentData& data); bool _initialized; ProgramObject* _skyFromAtmosphereProgram; ProgramObject* _skyFromSpaceProgram; - + enum { CAMERA_POS_LOCATION, LIGHT_POS_LOCATION, @@ -74,6 +77,14 @@ private: int _skyFromAtmosphereUniformLocations[LOCATION_COUNT]; int _skyFromSpaceUniformLocations[LOCATION_COUNT]; + + void setupAtmosphereProgram(const char* vertSource, const char* fragSource, gpu::PipelinePointer& pipelineProgram, int* locations); + + + gpu::PipelinePointer _newSkyFromAtmosphereProgram; + gpu::PipelinePointer _newSkyFromSpaceProgram; + int _newSkyFromAtmosphereUniformLocations[LOCATION_COUNT]; + int _newSkyFromSpaceUniformLocations[LOCATION_COUNT]; typedef QHash ServerData; diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 405ea9459d..77c9308af5 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -150,6 +150,7 @@ public: void _glUseProgram(GLuint program); void _glUniform1f(GLint location, GLfloat v0); void _glUniform2f(GLint location, GLfloat v0, GLfloat v1); + void _glUniform3f(GLint location, GLfloat v0, GLfloat v1); void _glUniform4fv(GLint location, GLsizei count, const GLfloat* value); void _glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); @@ -210,6 +211,7 @@ public: COMMAND_glUseProgram, COMMAND_glUniform1f, COMMAND_glUniform2f, + COMMAND_glUniform3f, COMMAND_glUniform4fv, COMMAND_glUniformMatrix4fv, diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index da6979fb27..7ba344dfbd 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -61,6 +61,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_glUseProgram), (&::gpu::GLBackend::do_glUniform1f), (&::gpu::GLBackend::do_glUniform2f), + (&::gpu::GLBackend::do_glUniform3f), (&::gpu::GLBackend::do_glUniform4fv), (&::gpu::GLBackend::do_glUniformMatrix4fv), @@ -462,6 +463,7 @@ void Batch::_glUniform2f(GLint location, GLfloat v0, GLfloat v1) { DO_IT_NOW(_glUniform2f, 1); } + void GLBackend::do_glUniform2f(Batch& batch, uint32 paramOffset) { if (_pipeline._program == 0) { // We should call updatePipeline() to bind the program but we are not doing that @@ -475,6 +477,32 @@ void GLBackend::do_glUniform2f(Batch& batch, uint32 paramOffset) { (void) CHECK_GL_ERROR(); } +void Batch::_glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2) { + ADD_COMMAND_GL(glUniform3f); + + _params.push_back(v2); + _params.push_back(v1); + _params.push_back(v0); + _params.push_back(location); + + DO_IT_NOW(_glUniform3f, 1); +} + +void GLBackend::do_glUniform3f(Batch& batch, uint32 paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + glUniform3f( + batch._params[paramOffset + 3]._int, + batch._params[paramOffset + 2]._float, + batch._params[paramOffset + 1]._float, + batch._params[paramOffset + 0]._float); + (void) CHECK_GL_ERROR(); +} + + void Batch::_glUniform4fv(GLint location, GLsizei count, const GLfloat* value) { ADD_COMMAND_GL(glUniform4fv); diff --git a/libraries/model/src/model/SkyFromAtmosphere.slf b/libraries/model/src/model/SkyFromAtmosphere.slf index 02036d0d7c..b82cddc282 100755 --- a/libraries/model/src/model/SkyFromAtmosphere.slf +++ b/libraries/model/src/model/SkyFromAtmosphere.slf @@ -51,7 +51,8 @@ uniform vec3 v3LightPos; uniform float g; uniform float g2; -varying vec3 position; +varying vec3 myPosition; + float scale(float fCos) { @@ -59,10 +60,11 @@ float scale(float fCos) return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25)))); } + void main (void) { // Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere) - vec3 v3Pos = position; + vec3 v3Pos = myPosition; vec3 v3Ray = v3Pos - v3CameraPos; float fFar = length(v3Ray); v3Ray /= fFar; diff --git a/libraries/model/src/model/SkyFromAtmosphere.slv b/libraries/model/src/model/SkyFromAtmosphere.slv index 785f89bb48..c19cb34c6a 100755 --- a/libraries/model/src/model/SkyFromAtmosphere.slv +++ b/libraries/model/src/model/SkyFromAtmosphere.slv @@ -33,6 +33,9 @@ // Copyright (c) 2004 Sean O'Neil // +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> + uniform vec3 v3CameraPos; // The camera's current position uniform vec3 v3LightPos; // The direction vector to the light source uniform vec3 v3InvWavelength; // 1 / pow(wavelength, 4) for the red, green, and blue channels @@ -50,19 +53,19 @@ uniform float fScaleOverScaleDepth; // fScale / fScaleDepth const int nSamples = 2; const float fSamples = 2.0; -varying vec3 position; +varying vec3 myPosition; -float scale(float fCos) -{ - float x = 1.0 - fCos; - return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25)))); -} - void main(void) { // Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere) - position = gl_Vertex.xyz * fOuterRadius; + myPosition = gl_Vertex.xyz * fOuterRadius; - gl_Position = gl_ModelViewProjectionMatrix * vec4(position, 1.0); + //gl_Position = gl_ModelViewProjectionMatrix * vec4(position, 1.0); + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + vec4 v4pos = vec4(myPosition, 1.0); + <$transformModelToClipPos(cam, obj, v4pos, gl_Position)$> } diff --git a/libraries/model/src/model/SkyFromSpace.slf b/libraries/model/src/model/SkyFromSpace.slf index 5f6ce80efa..4e13438ccb 100755 --- a/libraries/model/src/model/SkyFromSpace.slf +++ b/libraries/model/src/model/SkyFromSpace.slf @@ -53,7 +53,7 @@ uniform float g2; const int nSamples = 2; const float fSamples = 2.0; -varying vec3 position; +varying vec3 myPosition; float scale(float fCos) { @@ -65,7 +65,7 @@ float scale(float fCos) void main (void) { // Get the ray from the camera to the vertex and its length (which is the far point of the ray passing through the atmosphere) - vec3 v3Pos = position; + vec3 v3Pos = myPosition; vec3 v3Ray = v3Pos - v3CameraPos; float fFar = length(v3Ray); v3Ray /= fFar; diff --git a/libraries/model/src/model/SkyFromSpace.slv b/libraries/model/src/model/SkyFromSpace.slv index 6740d1909e..9090d76da2 100755 --- a/libraries/model/src/model/SkyFromSpace.slv +++ b/libraries/model/src/model/SkyFromSpace.slv @@ -1,5 +1,6 @@ -#version 120 - +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> // // For licensing information, see http://http.developer.nvidia.com/GPUGems/gpugems_app01.html: // @@ -32,12 +33,20 @@ // Copyright (c) 2004 Sean O'Neil // +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> + uniform float fOuterRadius; // The outer (atmosphere) radius -varying vec3 position; +varying vec3 myPosition; -void main(void) -{ - position = gl_Vertex.xyz * fOuterRadius; - gl_Position = gl_ModelViewProjectionMatrix * vec4(position, 1.0); -} + +void main(void) { + myPosition = gl_Vertex.xyz * fOuterRadius; + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + vec4 v4pos = vec4(myPosition, 1.0); + <$transformModelToClipPos(cam, obj, v4pos, gl_Position)$> +} \ No newline at end of file From 1beb95cfe7a74cde650c4edff43d70818d075adb Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 16 Jun 2015 15:38:16 -0700 Subject: [PATCH 73/94] fix FPS while still maintaining ray picking --- .../src/RenderableModelEntityItem.h | 6 +- libraries/render-utils/src/Model.cpp | 63 ++++++++++--------- libraries/render-utils/src/Model.h | 2 + 3 files changed, 42 insertions(+), 29 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index ac1d4b494f..4842063af3 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -41,7 +41,11 @@ public: ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData); - virtual void somethingChangedNotification() { _needsInitialSimulation = true; } + virtual void somethingChangedNotification() { + // FIX ME: this is overly aggressive. We only really need to simulate() if something about + // the world space transform has changed and/or if some animation is occurring. + _needsInitialSimulation = true; + } virtual bool readyToAddToScene(RenderArgs* renderArgs = nullptr); virtual bool addToScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 71178070c6..062f08debb 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -83,6 +83,7 @@ Model::Model(QObject* parent) : _blendNumber(0), _appliedBlendNumber(0), _calculatedMeshPartBoxesValid(false), + _calculatedMeshPartOffetValid(false), _calculatedMeshBoxesValid(false), _calculatedMeshTrianglesValid(false), _meshGroupsKnown(false), @@ -544,6 +545,10 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g // we can use the AABox's ray intersection by mapping our origin and direction into the model frame // and testing intersection there. if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face)) { + + if (!_calculatedMeshBoxesValid) { + recalculateMeshBoxes(pickAgainstTriangles); + } float bestDistance = std::numeric_limits::max(); @@ -660,6 +665,25 @@ bool Model::convexHullContains(glm::vec3 point) { return false; } +void Model::recalculateMeshPartOffsets() { + if (!_calculatedMeshPartOffetValid) { + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + int numberOfMeshes = geometry.meshes.size(); + _calculatedMeshPartOffet.clear(); + for (int i = 0; i < numberOfMeshes; i++) { + const FBXMesh& mesh = geometry.meshes.at(i); + qint64 partOffset = 0; + for (int j = 0; j < mesh.parts.size(); j++) { + const FBXMeshPart& part = mesh.parts.at(j); + _calculatedMeshPartOffet[QPair(i, j)] = partOffset; + partOffset += part.quadIndices.size() * sizeof(int); + partOffset += part.triangleIndices.size() * sizeof(int); + + } + } + _calculatedMeshPartOffetValid = true; + } +} // TODO: we seem to call this too often when things haven't actually changed... look into optimizing this // Any script might trigger findRayIntersectionAgainstSubMeshes (and maybe convexHullContains), so these // can occur multiple times. In addition, rendering does it's own ray picking in order to decide which @@ -676,6 +700,7 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { _calculatedMeshTriangles.resize(numberOfMeshes); _calculatedMeshPartBoxes.clear(); _calculatedMeshPartOffet.clear(); + _calculatedMeshPartOffetValid = false; for (int i = 0; i < numberOfMeshes; i++) { const FBXMesh& mesh = geometry.meshes.at(i); Extents scaledMeshExtents = calculateScaledOffsetExtents(mesh.meshExtents); @@ -778,6 +803,7 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { } _calculatedMeshTriangles[i] = thisMeshTriangles; _calculatedMeshPartBoxesValid = true; + _calculatedMeshPartOffetValid = true; } } _calculatedMeshBoxesValid = true; @@ -786,16 +812,6 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { } void Model::renderSetup(RenderArgs* args) { - // if we don't have valid mesh boxes, calculate them now, this only matters in cases - // where our caller has passed RenderArgs which will include a view frustum we can cull - // against. We cache the results of these calculations so long as the model hasn't been - // simulated and the mesh hasn't changed. - if (args && !_calculatedMeshBoxesValid) { - _mutex.lock(); - recalculateMeshBoxes(); - _mutex.unlock(); - } - // set up dilated textures on first render after load/simulate const FBXGeometry& geometry = _geometry->getFBXGeometry(); if (_dilatedTextures.isEmpty()) { @@ -1342,18 +1358,15 @@ void Model::snapToRegistrationPoint() { } void Model::simulate(float deltaTime, bool fullUpdate) { - /* - qDebug() << "Model::simulate()"; - qDebug() << " _translation:" << _translation; - qDebug() << " _rotation:" << _rotation; - */ - fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit) || (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint); if (isActive() && fullUpdate) { - // NOTE: this seems problematic... need to review - _calculatedMeshBoxesValid = false; // if we have to simulate, we need to assume our mesh boxes are all invalid + // NOTE: This is overly aggressive and we are invalidating the MeshBoxes when in fact they may not be invalid + // they really only become invalid if something about the transform to world space has changed. This is + // not too bad at this point, because it doesn't impact rendering. However it does slow down ray picking + // because ray picking needs valid boxes to work + _calculatedMeshBoxesValid = false; _calculatedMeshTrianglesValid = false; // check for scale to fit @@ -1761,10 +1774,6 @@ void Model::setupBatchTransform(gpu::Batch& batch, RenderArgs* args) { } AABox Model::getPartBounds(int meshIndex, int partIndex) { - if (!_calculatedMeshPartBoxesValid || !_calculatedMeshBoxesValid) { - recalculateMeshBoxes(true); - } - if (meshIndex < _meshStates.size()) { const MeshState& state = _meshStates.at(meshIndex); bool isSkinned = state.clusterMatrices.size() > 1; @@ -1774,7 +1783,7 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) { } } - if (_calculatedMeshPartBoxesValid && _calculatedMeshPartBoxes.contains(QPair(meshIndex, partIndex))) { + if (_geometry->getFBXGeometry().meshes.size() > meshIndex) { // FIX ME! - This is currently a hack because for some mesh parts our efforts to calculate the bounding // box of the mesh part fails. It seems to create boxes that are not consistent with where the @@ -1795,15 +1804,13 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) { } void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool translucent) { - if (!_readyWhenAdded) { return; // bail asap } - // we always need these properly calculated before we can render, this will likely already have been done - // since the engine will call our getPartBounds() before rendering us. - if (!_calculatedMeshPartBoxesValid || !_calculatedMeshBoxesValid) { - recalculateMeshBoxes(true); + // We need to make sure we have valid offsets calculated before we can render + if (!_calculatedMeshPartOffetValid) { + recalculateMeshPartOffsets(); } auto textureCache = DependencyManager::get(); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 7c1572418e..84393c99e1 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -370,6 +370,7 @@ private: QHash, AABox> _calculatedMeshPartBoxes; // world coordinate AABoxes for all sub mesh part boxes QHash, qint64> _calculatedMeshPartOffet; + bool _calculatedMeshPartOffetValid; bool _calculatedMeshPartBoxesValid; @@ -381,6 +382,7 @@ private: QMutex _mutex; void recalculateMeshBoxes(bool pickAgainstTriangles = false); + void recalculateMeshPartOffsets(); void segregateMeshGroups(); // used to calculate our list of translucent vs opaque meshes From 5abd608ccc040cd816320dc621bdef332a852bf8 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 16 Jun 2015 15:55:37 -0700 Subject: [PATCH 74/94] fix typo --- libraries/render-utils/src/Model.cpp | 22 +++++++++++----------- libraries/render-utils/src/Model.h | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 062f08debb..3b2943b65b 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -83,7 +83,7 @@ Model::Model(QObject* parent) : _blendNumber(0), _appliedBlendNumber(0), _calculatedMeshPartBoxesValid(false), - _calculatedMeshPartOffetValid(false), + _calculatedMeshPartOffsetValid(false), _calculatedMeshBoxesValid(false), _calculatedMeshTrianglesValid(false), _meshGroupsKnown(false), @@ -666,22 +666,22 @@ bool Model::convexHullContains(glm::vec3 point) { } void Model::recalculateMeshPartOffsets() { - if (!_calculatedMeshPartOffetValid) { + if (!_calculatedMeshPartOffsetValid) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); int numberOfMeshes = geometry.meshes.size(); - _calculatedMeshPartOffet.clear(); + _calculatedMeshPartOffset.clear(); for (int i = 0; i < numberOfMeshes; i++) { const FBXMesh& mesh = geometry.meshes.at(i); qint64 partOffset = 0; for (int j = 0; j < mesh.parts.size(); j++) { const FBXMeshPart& part = mesh.parts.at(j); - _calculatedMeshPartOffet[QPair(i, j)] = partOffset; + _calculatedMeshPartOffset[QPair(i, j)] = partOffset; partOffset += part.quadIndices.size() * sizeof(int); partOffset += part.triangleIndices.size() * sizeof(int); } } - _calculatedMeshPartOffetValid = true; + _calculatedMeshPartOffsetValid = true; } } // TODO: we seem to call this too often when things haven't actually changed... look into optimizing this @@ -699,8 +699,8 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { _calculatedMeshTriangles.clear(); _calculatedMeshTriangles.resize(numberOfMeshes); _calculatedMeshPartBoxes.clear(); - _calculatedMeshPartOffet.clear(); - _calculatedMeshPartOffetValid = false; + _calculatedMeshPartOffset.clear(); + _calculatedMeshPartOffsetValid = false; for (int i = 0; i < numberOfMeshes; i++) { const FBXMesh& mesh = geometry.meshes.at(i); Extents scaledMeshExtents = calculateScaledOffsetExtents(mesh.meshExtents); @@ -795,7 +795,7 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { } } _calculatedMeshPartBoxes[QPair(i, j)] = thisPartBounds; - _calculatedMeshPartOffet[QPair(i, j)] = partOffset; + _calculatedMeshPartOffset[QPair(i, j)] = partOffset; partOffset += part.quadIndices.size() * sizeof(int); partOffset += part.triangleIndices.size() * sizeof(int); @@ -803,7 +803,7 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) { } _calculatedMeshTriangles[i] = thisMeshTriangles; _calculatedMeshPartBoxesValid = true; - _calculatedMeshPartOffetValid = true; + _calculatedMeshPartOffsetValid = true; } } _calculatedMeshBoxesValid = true; @@ -1809,7 +1809,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran } // We need to make sure we have valid offsets calculated before we can render - if (!_calculatedMeshPartOffetValid) { + if (!_calculatedMeshPartOffsetValid) { recalculateMeshPartOffsets(); } auto textureCache = DependencyManager::get(); @@ -2018,7 +2018,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran } } - qint64 offset = _calculatedMeshPartOffet[QPair(meshIndex, partIndex)]; + qint64 offset = _calculatedMeshPartOffset[QPair(meshIndex, partIndex)]; if (part.quadIndices.size() > 0) { batch.drawIndexed(gpu::QUADS, part.quadIndices.size(), offset); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 84393c99e1..6dfe223581 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -369,8 +369,8 @@ private: }; QHash, AABox> _calculatedMeshPartBoxes; // world coordinate AABoxes for all sub mesh part boxes - QHash, qint64> _calculatedMeshPartOffet; - bool _calculatedMeshPartOffetValid; + QHash, qint64> _calculatedMeshPartOffset; + bool _calculatedMeshPartOffsetValid; bool _calculatedMeshPartBoxesValid; From 6f0ae96f56a3570512621ea4545c6705328656b0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 16 Jun 2015 16:01:01 -0700 Subject: [PATCH 75/94] fix warning --- libraries/render-utils/src/Model.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 3b2943b65b..803673a06e 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -82,8 +82,8 @@ Model::Model(QObject* parent) : _isVisible(true), _blendNumber(0), _appliedBlendNumber(0), - _calculatedMeshPartBoxesValid(false), _calculatedMeshPartOffsetValid(false), + _calculatedMeshPartBoxesValid(false), _calculatedMeshBoxesValid(false), _calculatedMeshTrianglesValid(false), _meshGroupsKnown(false), From d47f5a2abb87c47109f62322bab410083e630f1b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 16 Jun 2015 16:44:33 -0700 Subject: [PATCH 76/94] Update Rectangle, Grid, and Billboard overlays to use batches --- .../src/ui/overlays/BillboardOverlay.cpp | 122 +++++++----- interface/src/ui/overlays/Grid3DOverlay.cpp | 181 +++++++++++------- .../src/ui/overlays/Rectangle3DOverlay.cpp | 146 +++++++++----- 3 files changed, 274 insertions(+), 175 deletions(-) diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 3e3e823737..ed0ddd8dc7 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -43,72 +43,88 @@ void BillboardOverlay::render(RenderArgs* args) { return; } - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.5f); + glm::quat rotation; + if (_isFacingAvatar) { + // rotate about vertical to face the camera + rotation = Application::getInstance()->getCamera()->getRotation(); + rotation *= glm::angleAxis(glm::pi(), glm::vec3(0.0f, 1.0f, 0.0f)); + rotation *= getRotation(); + } else { + rotation = getRotation(); + } - glEnable(GL_TEXTURE_2D); - glDisable(GL_LIGHTING); + float imageWidth = _texture->getWidth(); + float imageHeight = _texture->getHeight(); - glBindTexture(GL_TEXTURE_2D, _texture->getID()); + QRect fromImage; + if (_fromImage.isNull()) { + fromImage.setX(0); + fromImage.setY(0); + fromImage.setWidth(imageWidth); + fromImage.setHeight(imageHeight); + } else { + float scaleX = imageWidth / _texture->getOriginalWidth(); + float scaleY = imageHeight / _texture->getOriginalHeight(); - glPushMatrix(); { - glTranslatef(_position.x, _position.y, _position.z); - glm::quat rotation; - if (_isFacingAvatar) { - // rotate about vertical to face the camera - rotation = Application::getInstance()->getCamera()->getRotation(); - rotation *= glm::angleAxis(glm::pi(), glm::vec3(0.0f, 1.0f, 0.0f)); - rotation *= getRotation(); - } else { - rotation = getRotation(); - } - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - glScalef(_scale, _scale, _scale); + fromImage.setX(scaleX * _fromImage.x()); + fromImage.setY(scaleY * _fromImage.y()); + fromImage.setWidth(scaleX * _fromImage.width()); + fromImage.setHeight(scaleY * _fromImage.height()); + } - const float MAX_COLOR = 255.0f; - xColor color = getColor(); - float alpha = getAlpha(); + float maxSize = glm::max(fromImage.width(), fromImage.height()); + float x = fromImage.width() / (2.0f * maxSize); + float y = -fromImage.height() / (2.0f * maxSize); - float imageWidth = _texture->getWidth(); - float imageHeight = _texture->getHeight(); + glm::vec2 topLeft(-x, -y); + glm::vec2 bottomRight(x, y); + glm::vec2 texCoordTopLeft(fromImage.x() / imageWidth, fromImage.y() / imageHeight); + glm::vec2 texCoordBottomRight((fromImage.x() + fromImage.width()) / imageWidth, + (fromImage.y() + fromImage.height()) / imageHeight); - QRect fromImage; - if (_fromImage.isNull()) { - fromImage.setX(0); - fromImage.setY(0); - fromImage.setWidth(imageWidth); - fromImage.setHeight(imageHeight); - } else { - float scaleX = imageWidth / _texture->getOriginalWidth(); - float scaleY = imageHeight / _texture->getOriginalHeight(); + const float MAX_COLOR = 255.0f; + xColor color = getColor(); + float alpha = getAlpha(); - fromImage.setX(scaleX * _fromImage.x()); - fromImage.setY(scaleY * _fromImage.y()); - fromImage.setWidth(scaleX * _fromImage.width()); - fromImage.setHeight(scaleY * _fromImage.height()); - } + auto batch = args->_batch; - float maxSize = glm::max(fromImage.width(), fromImage.height()); - float x = fromImage.width() / (2.0f * maxSize); - float y = -fromImage.height() / (2.0f * maxSize); + if (batch) { + Transform transform; + transform.setTranslation(_position); + transform.setRotation(rotation); + transform.setScale(_scale); - glm::vec2 topLeft(-x, -y); - glm::vec2 bottomRight(x, y); - glm::vec2 texCoordTopLeft(fromImage.x() / imageWidth, fromImage.y() / imageHeight); - glm::vec2 texCoordBottomRight((fromImage.x() + fromImage.width()) / imageWidth, - (fromImage.y() + fromImage.height()) / imageHeight); - - DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, + batch->setModelTransform(transform); + batch->setUniformTexture(0, _texture->getGPUTexture()); + + DependencyManager::get()->renderQuad(*batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha)); + } else { + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.5f); - } glPopMatrix(); + glEnable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); - glDisable(GL_TEXTURE_2D); - glEnable(GL_LIGHTING); - glDisable(GL_ALPHA_TEST); + glBindTexture(GL_TEXTURE_2D, _texture->getID()); - glBindTexture(GL_TEXTURE_2D, 0); + glPushMatrix(); { + glTranslatef(_position.x, _position.y, _position.z); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + glScalef(_scale, _scale, _scale); + + DependencyManager::get()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, + glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha)); + + } glPopMatrix(); + + glDisable(GL_TEXTURE_2D); + glEnable(GL_LIGHTING); + glDisable(GL_ALPHA_TEST); + + glBindTexture(GL_TEXTURE_2D, 0); + } } void BillboardOverlay::setProperties(const QScriptValue &properties) { diff --git a/interface/src/ui/overlays/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp index b39b2c3274..e68e5b47f2 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.cpp +++ b/interface/src/ui/overlays/Grid3DOverlay.cpp @@ -36,87 +36,128 @@ void Grid3DOverlay::render(RenderArgs* args) { return; // do nothing if we're not visible } - if (!_gridProgram.isLinked()) { - if (!_gridProgram.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + "shaders/grid.vert")) { - qDebug() << "Failed to compile: " + _gridProgram.log(); - return; - } - if (!_gridProgram.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + "shaders/grid.frag")) { - qDebug() << "Failed to compile: " + _gridProgram.log(); - return; - } - if (!_gridProgram.link()) { - qDebug() << "Failed to link: " + _gridProgram.log(); - return; - } - } - - // Render code largely taken from MetavoxelEditor::render() - glDisable(GL_LIGHTING); - - glDepthMask(GL_FALSE); - - glPushMatrix(); - - glm::quat rotation = getRotation(); - - glm::vec3 axis = glm::axis(rotation); - - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - - glLineWidth(1.5f); - - // center the grid around the camera position on the plane - glm::vec3 rotated = glm::inverse(rotation) * Application::getInstance()->getCamera()->getPosition(); - float spacing = _minorGridWidth; - - float alpha = getAlpha(); - xColor color = getColor(); - glm::vec3 position = getPosition(); - const int MINOR_GRID_DIVISIONS = 200; const int MAJOR_GRID_DIVISIONS = 100; const float MAX_COLOR = 255.0f; + // center the grid around the camera position on the plane + glm::vec3 rotated = glm::inverse(_rotation) * Application::getInstance()->getCamera()->getPosition(); + float spacing = _minorGridWidth; + + float alpha = getAlpha(); + xColor color = getColor(); glm::vec4 gridColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - _gridProgram.bind(); + auto batch = args->_batch; - // Minor grid - glPushMatrix(); - { - glTranslatef(_minorGridWidth * (floorf(rotated.x / spacing) - MINOR_GRID_DIVISIONS / 2), - spacing * (floorf(rotated.y / spacing) - MINOR_GRID_DIVISIONS / 2), position.z); + if (batch) { + Transform transform; + transform.setRotation(_rotation); - float scale = MINOR_GRID_DIVISIONS * spacing; - glScalef(scale, scale, scale); - DependencyManager::get()->renderGrid(MINOR_GRID_DIVISIONS, MINOR_GRID_DIVISIONS, gridColor); + // Minor grid + { + batch->_glLineWidth(1.0f); + auto position = glm::vec3(_minorGridWidth * (floorf(rotated.x / spacing) - MINOR_GRID_DIVISIONS / 2), + spacing * (floorf(rotated.y / spacing) - MINOR_GRID_DIVISIONS / 2), + _position.z); + float scale = MINOR_GRID_DIVISIONS * spacing; + + transform.setTranslation(position); + transform.setScale(scale); + + batch->setModelTransform(transform); + + DependencyManager::get()->renderGrid(*batch, MINOR_GRID_DIVISIONS, MINOR_GRID_DIVISIONS, gridColor); + } + + // Major grid + { + batch->_glLineWidth(4.0f); + spacing *= _majorGridEvery; + auto position = glm::vec3(spacing * (floorf(rotated.x / spacing) - MAJOR_GRID_DIVISIONS / 2), + spacing * (floorf(rotated.y / spacing) - MAJOR_GRID_DIVISIONS / 2), + _position.z); + float scale = MAJOR_GRID_DIVISIONS * spacing; + + transform.setTranslation(position); + transform.setScale(scale); + + batch->setModelTransform(transform); + + DependencyManager::get()->renderGrid(*batch, MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS, gridColor); + } + } else { + if (!_gridProgram.isLinked()) { + if (!_gridProgram.addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() + "shaders/grid.vert")) { + qDebug() << "Failed to compile: " + _gridProgram.log(); + return; + } + if (!_gridProgram.addShaderFromSourceFile(QGLShader::Fragment, PathUtils::resourcesPath() + "shaders/grid.frag")) { + qDebug() << "Failed to compile: " + _gridProgram.log(); + return; + } + if (!_gridProgram.link()) { + qDebug() << "Failed to link: " + _gridProgram.log(); + return; + } + } + + // Render code largely taken from MetavoxelEditor::render() + glDisable(GL_LIGHTING); + + glDepthMask(GL_FALSE); + + glPushMatrix(); + + glm::quat rotation = getRotation(); + + glm::vec3 axis = glm::axis(rotation); + + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + + glLineWidth(1.5f); + + glm::vec3 position = getPosition(); + + _gridProgram.bind(); + + // Minor grid + glPushMatrix(); + { + glTranslatef(_minorGridWidth * (floorf(rotated.x / spacing) - MINOR_GRID_DIVISIONS / 2), + spacing * (floorf(rotated.y / spacing) - MINOR_GRID_DIVISIONS / 2), position.z); + + float scale = MINOR_GRID_DIVISIONS * spacing; + glScalef(scale, scale, scale); + + DependencyManager::get()->renderGrid(MINOR_GRID_DIVISIONS, MINOR_GRID_DIVISIONS, gridColor); + } + glPopMatrix(); + + // Major grid + glPushMatrix(); + { + glLineWidth(4.0f); + spacing *= _majorGridEvery; + glTranslatef(spacing * (floorf(rotated.x / spacing) - MAJOR_GRID_DIVISIONS / 2), + spacing * (floorf(rotated.y / spacing) - MAJOR_GRID_DIVISIONS / 2), position.z); + + float scale = MAJOR_GRID_DIVISIONS * spacing; + glScalef(scale, scale, scale); + + DependencyManager::get()->renderGrid(MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS, gridColor); + } + glPopMatrix(); + + _gridProgram.release(); + + glPopMatrix(); + + glEnable(GL_LIGHTING); + glDepthMask(GL_TRUE); } - glPopMatrix(); - - // Major grid - glPushMatrix(); - { - glLineWidth(4.0f); - spacing *= _majorGridEvery; - glTranslatef(spacing * (floorf(rotated.x / spacing) - MAJOR_GRID_DIVISIONS / 2), - spacing * (floorf(rotated.y / spacing) - MAJOR_GRID_DIVISIONS / 2), position.z); - - float scale = MAJOR_GRID_DIVISIONS * spacing; - glScalef(scale, scale, scale); - - DependencyManager::get()->renderGrid(MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS, gridColor); - } - glPopMatrix(); - - _gridProgram.release(); - - glPopMatrix(); - - glEnable(GL_LIGHTING); - glDepthMask(GL_TRUE); } void Grid3DOverlay::setProperties(const QScriptValue& properties) { diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp index 8662030e23..dc5fdeabb2 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp @@ -41,74 +41,116 @@ void Rectangle3DOverlay::render(RenderArgs* args) { const float MAX_COLOR = 255.0f; glm::vec4 rectangleColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - glDisable(GL_LIGHTING); - glm::vec3 position = getPosition(); glm::vec3 center = getCenter(); glm::vec2 dimensions = getDimensions(); glm::vec2 halfDimensions = dimensions * 0.5f; glm::quat rotation = getRotation(); - float glowLevel = getGlowLevel(); - Glower* glower = NULL; - if (glowLevel > 0.0f) { - glower = new Glower(glowLevel); - } + auto batch = args->_batch; - glPushMatrix(); - glTranslatef(position.x, position.y, position.z); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); - glPushMatrix(); - glm::vec3 positionToCenter = center - position; - glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); - //glScalef(dimensions.x, dimensions.y, 1.0f); + if (batch) { + Transform transform; + transform.setTranslation(position); + transform.setRotation(rotation); - glLineWidth(_lineWidth); + batch->setModelTransform(transform); + if (getIsSolid()) { + glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, 0.0f); + glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, 0.0f); + DependencyManager::get()->renderQuad(*batch, topLeft, bottomRight, rectangleColor); + } else { auto geometryCache = DependencyManager::get(); + if (getIsDashedLine()) { + glm::vec3 point1(-halfDimensions.x, -halfDimensions.y, 0.0f); + glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f); + glm::vec3 point3(halfDimensions.x, halfDimensions.y, 0.0f); + glm::vec3 point4(-halfDimensions.x, halfDimensions.y, 0.0f); - // for our overlay, is solid means we draw a solid "filled" rectangle otherwise we just draw a border line... - if (getIsSolid()) { - glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, 0.0f); - glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, 0.0f); - DependencyManager::get()->renderQuad(topLeft, bottomRight, rectangleColor); + geometryCache->renderDashedLine(*batch, point1, point2, rectangleColor); + geometryCache->renderDashedLine(*batch, point2, point3, rectangleColor); + geometryCache->renderDashedLine(*batch, point3, point4, rectangleColor); + geometryCache->renderDashedLine(*batch, point4, point1, rectangleColor); } else { - if (getIsDashedLine()) { + if (halfDimensions != _previousHalfDimensions) { + QVector border; + border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); + border << glm::vec3(halfDimensions.x, -halfDimensions.y, 0.0f); + border << glm::vec3(halfDimensions.x, halfDimensions.y, 0.0f); + border << glm::vec3(-halfDimensions.x, halfDimensions.y, 0.0f); + border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); + geometryCache->updateVertices(_geometryCacheID, border, rectangleColor); - glm::vec3 point1(-halfDimensions.x, -halfDimensions.y, 0.0f); - glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f); - glm::vec3 point3(halfDimensions.x, halfDimensions.y, 0.0f); - glm::vec3 point4(-halfDimensions.x, halfDimensions.y, 0.0f); - - geometryCache->renderDashedLine(point1, point2, rectangleColor); - geometryCache->renderDashedLine(point2, point3, rectangleColor); - geometryCache->renderDashedLine(point3, point4, rectangleColor); - geometryCache->renderDashedLine(point4, point1, rectangleColor); - - } else { - - if (halfDimensions != _previousHalfDimensions) { - QVector border; - border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); - border << glm::vec3(halfDimensions.x, -halfDimensions.y, 0.0f); - border << glm::vec3(halfDimensions.x, halfDimensions.y, 0.0f); - border << glm::vec3(-halfDimensions.x, halfDimensions.y, 0.0f); - border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); - geometryCache->updateVertices(_geometryCacheID, border, rectangleColor); - - _previousHalfDimensions = halfDimensions; - - } - geometryCache->renderVertices(gpu::LINE_STRIP, _geometryCacheID); + _previousHalfDimensions = halfDimensions; } + geometryCache->renderVertices(*batch, gpu::LINE_STRIP, _geometryCacheID); } - + } + } else { + glDisable(GL_LIGHTING); + + float glowLevel = getGlowLevel(); + Glower* glower = NULL; + if (glowLevel > 0.0f) { + glower = new Glower(glowLevel); + } + + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + glPushMatrix(); + glm::vec3 positionToCenter = center - position; + glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); + //glScalef(dimensions.x, dimensions.y, 1.0f); + + glLineWidth(_lineWidth); + + auto geometryCache = DependencyManager::get(); + + // for our overlay, is solid means we draw a solid "filled" rectangle otherwise we just draw a border line... + if (getIsSolid()) { + glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, 0.0f); + glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, 0.0f); + DependencyManager::get()->renderQuad(topLeft, bottomRight, rectangleColor); + } else { + if (getIsDashedLine()) { + + glm::vec3 point1(-halfDimensions.x, -halfDimensions.y, 0.0f); + glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f); + glm::vec3 point3(halfDimensions.x, halfDimensions.y, 0.0f); + glm::vec3 point4(-halfDimensions.x, halfDimensions.y, 0.0f); + + geometryCache->renderDashedLine(point1, point2, rectangleColor); + geometryCache->renderDashedLine(point2, point3, rectangleColor); + geometryCache->renderDashedLine(point3, point4, rectangleColor); + geometryCache->renderDashedLine(point4, point1, rectangleColor); + + } else { + + if (halfDimensions != _previousHalfDimensions) { + QVector border; + border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); + border << glm::vec3(halfDimensions.x, -halfDimensions.y, 0.0f); + border << glm::vec3(halfDimensions.x, halfDimensions.y, 0.0f); + border << glm::vec3(-halfDimensions.x, halfDimensions.y, 0.0f); + border << glm::vec3(-halfDimensions.x, -halfDimensions.y, 0.0f); + geometryCache->updateVertices(_geometryCacheID, border, rectangleColor); + + _previousHalfDimensions = halfDimensions; + + } + geometryCache->renderVertices(gpu::LINE_STRIP, _geometryCacheID); + } + } + + glPopMatrix(); glPopMatrix(); - glPopMatrix(); - - if (glower) { - delete glower; + + if (glower) { + delete glower; + } } } From 569971582d3d7f9bffa18803858c8b29a4ef4739 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 16 Jun 2015 18:39:35 -0700 Subject: [PATCH 77/94] more hacking on trying to port atmospheres to the new pipeline --- interface/src/Environment.cpp | 96 +++---------------- libraries/gpu/src/gpu/Batch.h | 4 +- libraries/gpu/src/gpu/GLBackend.cpp | 25 +++++ libraries/gpu/src/gpu/GLBackend.h | 2 + .../model/src/model/SkyFromAtmosphere.slf | 8 +- .../model/src/model/SkyFromAtmosphere.slv | 1 - 6 files changed, 50 insertions(+), 86 deletions(-) diff --git a/interface/src/Environment.cpp b/interface/src/Environment.cpp index 1e9c571a57..66b463949a 100644 --- a/interface/src/Environment.cpp +++ b/interface/src/Environment.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -32,6 +33,8 @@ #include "../../build/libraries/model/SkyFromSpace_frag.h" #include "../../build/libraries/model/SkyFromAtmosphere_vert.h" #include "../../build/libraries/model/SkyFromAtmosphere_frag.h" +#include "../../build/libraries/render-utils/simple_vert.h" +#include "../../build/libraries/render-utils/simple_frag.h" uint qHash(const HifiSockAddr& sockAddr) { if (sockAddr.getAddress().isNull()) { @@ -64,11 +67,11 @@ void Environment::init() { - qDebug() << "line:" << __LINE__; + qDebug() << "here:" << __LINE__; setupAtmosphereProgram(SkyFromSpace_vert, SkyFromSpace_frag, _newSkyFromSpaceProgram, _newSkyFromSpaceUniformLocations); - qDebug() << "line:" << __LINE__; + qDebug() << "here:" << __LINE__; setupAtmosphereProgram(SkyFromAtmosphere_vert, SkyFromAtmosphere_frag, _newSkyFromAtmosphereProgram, _newSkyFromAtmosphereUniformLocations); - qDebug() << "line:" << __LINE__; + qDebug() << "here:" << __LINE__; // start off with a default-constructed environment data @@ -77,7 +80,7 @@ void Environment::init() { _initialized = true; } -void Environment::setupAtmosphereProgram(const char* vertSource, const char* fragSource, gpu::PipelinePointer& pipelineProgram, int* locations) { +void Environment::setupAtmosphereProgram(const char* vertSource, const char* fragSource, gpu::PipelinePointer& pipeline, int* locations) { auto VS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(vertSource))); auto PS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(fragSource))); @@ -91,7 +94,7 @@ void Environment::setupAtmosphereProgram(const char* vertSource, const char* fra state->setCullMode(gpu::State::CULL_NONE); state->setDepthTest(false); state->setBlendFunction(true,gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); - pipelineProgram = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); + pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); locations[CAMERA_POS_LOCATION] = program->getUniforms().findLocation("v3CameraPos"); locations[LIGHT_POS_LOCATION] = program->getUniforms().findLocation("v3LightPos"); @@ -109,34 +112,6 @@ void Environment::setupAtmosphereProgram(const char* vertSource, const char* fra locations[SCALE_OVER_SCALE_DEPTH_LOCATION] = program->getUniforms().findLocation("fScaleOverScaleDepth"); locations[G_LOCATION] = program->getUniforms().findLocation("g"); locations[G2_LOCATION] = program->getUniforms().findLocation("g2"); - - /* - - program.addShaderFromSourceCode(QGLShader::Vertex, vertSource); - program.addShaderFromSourceCode(QGLShader::Fragment, fragSource); - program.link(); - - program.bind(); - - locations[CAMERA_POS_LOCATION] = program.uniformLocation("v3CameraPos"); - locations[LIGHT_POS_LOCATION] = program.uniformLocation("v3LightPos"); - locations[INV_WAVELENGTH_LOCATION] = program.uniformLocation("v3InvWavelength"); - locations[CAMERA_HEIGHT2_LOCATION] = program.uniformLocation("fCameraHeight2"); - locations[OUTER_RADIUS_LOCATION] = program.uniformLocation("fOuterRadius"); - locations[OUTER_RADIUS2_LOCATION] = program.uniformLocation("fOuterRadius2"); - locations[INNER_RADIUS_LOCATION] = program.uniformLocation("fInnerRadius"); - locations[KR_ESUN_LOCATION] = program.uniformLocation("fKrESun"); - locations[KM_ESUN_LOCATION] = program.uniformLocation("fKmESun"); - locations[KR_4PI_LOCATION] = program.uniformLocation("fKr4PI"); - locations[KM_4PI_LOCATION] = program.uniformLocation("fKm4PI"); - locations[SCALE_LOCATION] = program.uniformLocation("fScale"); - locations[SCALE_DEPTH_LOCATION] = program.uniformLocation("fScaleDepth"); - locations[SCALE_OVER_SCALE_DEPTH_LOCATION] = program.uniformLocation("fScaleOverScaleDepth"); - locations[G_LOCATION] = program.uniformLocation("g"); - locations[G2_LOCATION] = program.uniformLocation("g2"); - - program.release(); - */ } void Environment::resetToDefault() { @@ -305,29 +280,24 @@ ProgramObject* Environment::createSkyProgram(const char* from, int* locations) { } void Environment::renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const EnvironmentData& data) { + glm::vec3 center = data.getAtmosphereCenter(); - //glPushMatrix(); - //glTranslatef(center.x, center.y, center.z); - Transform transform; transform.setTranslation(center); - transform.setScale(1.0f); + //transform.setScale(2.0f); batch.setModelTransform(transform); glm::vec3 relativeCameraPos = camera.getPosition() - center; float height = glm::length(relativeCameraPos); // use the appropriate shader depending on whether we're inside or outside - //ProgramObject* program; int* locations; if (height < data.getAtmosphereOuterRadius()) { - //program = &_newSkyFromAtmosphereProgram; batch.setPipeline(_newSkyFromAtmosphereProgram); locations = _newSkyFromAtmosphereUniformLocations; } else { - //program = &_newSkyFromSpaceProgram; batch.setPipeline(_newSkyFromSpaceProgram); locations = _newSkyFromSpaceUniformLocations; } @@ -335,39 +305,13 @@ void Environment::renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const // the constants here are from Sean O'Neil's GPU Gems entry // (http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter16.html), GameEngine.cpp - //program->bind(); - /* - program->setUniform(locations[CAMERA_POS_LOCATION], relativeCameraPos); + batch._glUniform3f(locations[CAMERA_POS_LOCATION], relativeCameraPos.x, relativeCameraPos.y, relativeCameraPos.z); glm::vec3 lightDirection = glm::normalize(data.getSunLocation()); - program->setUniform(locations[LIGHT_POS_LOCATION], lightDirection); - program->setUniformValue(locations[INV_WAVELENGTH_LOCATION], - 1 / powf(data.getScatteringWavelengths().r, 4.0f), - 1 / powf(data.getScatteringWavelengths().g, 4.0f), - 1 / powf(data.getScatteringWavelengths().b, 4.0f)); - program->setUniformValue(locations[CAMERA_HEIGHT2_LOCATION], height * height); - program->setUniformValue(locations[OUTER_RADIUS_LOCATION], data.getAtmosphereOuterRadius()); - program->setUniformValue(locations[OUTER_RADIUS2_LOCATION], data.getAtmosphereOuterRadius() * data.getAtmosphereOuterRadius()); - program->setUniformValue(locations[INNER_RADIUS_LOCATION], data.getAtmosphereInnerRadius()); - program->setUniformValue(locations[KR_ESUN_LOCATION], data.getRayleighScattering() * data.getSunBrightness()); - program->setUniformValue(locations[KM_ESUN_LOCATION], data.getMieScattering() * data.getSunBrightness()); - program->setUniformValue(locations[KR_4PI_LOCATION], data.getRayleighScattering() * 4.0f * PI); - program->setUniformValue(locations[KM_4PI_LOCATION], data.getMieScattering() * 4.0f * PI); - program->setUniformValue(locations[SCALE_LOCATION], 1.0f / (data.getAtmosphereOuterRadius() - data.getAtmosphereInnerRadius())); - program->setUniformValue(locations[SCALE_DEPTH_LOCATION], 0.25f); - program->setUniformValue(locations[SCALE_OVER_SCALE_DEPTH_LOCATION], - (1.0f / (data.getAtmosphereOuterRadius() - data.getAtmosphereInnerRadius())) / 0.25f); - program->setUniformValue(locations[G_LOCATION], -0.990f); - program->setUniformValue(locations[G2_LOCATION], -0.990f * -0.990f); - */ - - - batch._glUniform3fv(locations[CAMERA_POS_LOCATION], relativeCameraPos); - glm::vec3 lightDirection = glm::normalize(data.getSunLocation()); - batch._glUniform3fv(locations[LIGHT_POS_LOCATION], lightDirection); + batch._glUniform3f(locations[LIGHT_POS_LOCATION], lightDirection.x, lightDirection.y, lightDirection.z); batch._glUniform3f(locations[INV_WAVELENGTH_LOCATION], - 1 / powf(data.getScatteringWavelengths().r, 4.0f), - 1 / powf(data.getScatteringWavelengths().g, 4.0f), - 1 / powf(data.getScatteringWavelengths().b, 4.0f)); + 1 / powf(data.getScatteringWavelengths().r, 4.0f), + 1 / powf(data.getScatteringWavelengths().g, 4.0f), + 1 / powf(data.getScatteringWavelengths().b, 4.0f)); batch._glUniform1f(locations[CAMERA_HEIGHT2_LOCATION], height * height); batch._glUniform1f(locations[OUTER_RADIUS_LOCATION], data.getAtmosphereOuterRadius()); batch._glUniform1f(locations[OUTER_RADIUS2_LOCATION], data.getAtmosphereOuterRadius() * data.getAtmosphereOuterRadius()); @@ -383,15 +327,5 @@ void Environment::renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const batch._glUniform1f(locations[G_LOCATION], -0.990f); batch._glUniform1f(locations[G2_LOCATION], -0.990f * -0.990f); - - //glDepthMask(GL_FALSE); - //glDisable(GL_DEPTH_TEST); - //glDisable(GL_CULL_FACE); - //glEnable(GL_BLEND); DependencyManager::get()->renderSphere(batch,1.0f, 100, 50, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); //Draw a unit sphere - //glDepthMask(GL_TRUE); - - //program->release(); - - //glPopMatrix(); } diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 77c9308af5..bbd2121c54 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -150,7 +150,8 @@ public: void _glUseProgram(GLuint program); void _glUniform1f(GLint location, GLfloat v0); void _glUniform2f(GLint location, GLfloat v0, GLfloat v1); - void _glUniform3f(GLint location, GLfloat v0, GLfloat v1); + void _glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); + void _glUniform3fv(GLint location, GLsizei count, const GLfloat* value); void _glUniform4fv(GLint location, GLsizei count, const GLfloat* value); void _glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); @@ -212,6 +213,7 @@ public: COMMAND_glUniform1f, COMMAND_glUniform2f, COMMAND_glUniform3f, + COMMAND_glUniform3fv, COMMAND_glUniform4fv, COMMAND_glUniformMatrix4fv, diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 7ba344dfbd..20a6b60901 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -62,6 +62,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_glUniform1f), (&::gpu::GLBackend::do_glUniform2f), (&::gpu::GLBackend::do_glUniform3f), + (&::gpu::GLBackend::do_glUniform3fv), (&::gpu::GLBackend::do_glUniform4fv), (&::gpu::GLBackend::do_glUniformMatrix4fv), @@ -502,6 +503,30 @@ void GLBackend::do_glUniform3f(Batch& batch, uint32 paramOffset) { (void) CHECK_GL_ERROR(); } +void Batch::_glUniform3fv(GLint location, GLsizei count, const GLfloat* value) { + ADD_COMMAND_GL(glUniform3fv); + + const int VEC3_SIZE = 3 * sizeof(float); + _params.push_back(cacheData(count * VEC3_SIZE, value)); + _params.push_back(count); + _params.push_back(location); + + DO_IT_NOW(_glUniform3fv, 3); +} +void GLBackend::do_glUniform3fv(Batch& batch, uint32 paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + glUniform3fv( + batch._params[paramOffset + 2]._int, + batch._params[paramOffset + 1]._uint, + (const GLfloat*)batch.editData(batch._params[paramOffset + 0]._uint)); + + (void) CHECK_GL_ERROR(); +} + void Batch::_glUniform4fv(GLint location, GLsizei count, const GLfloat* value) { ADD_COMMAND_GL(glUniform4fv); diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index e7a5e62df8..c87394869c 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -379,6 +379,8 @@ protected: void do_glUseProgram(Batch& batch, uint32 paramOffset); void do_glUniform1f(Batch& batch, uint32 paramOffset); void do_glUniform2f(Batch& batch, uint32 paramOffset); + void do_glUniform3f(Batch& batch, uint32 paramOffset); + void do_glUniform3fv(Batch& batch, uint32 paramOffset); void do_glUniform4fv(Batch& batch, uint32 paramOffset); void do_glUniformMatrix4fv(Batch& batch, uint32 paramOffset); diff --git a/libraries/model/src/model/SkyFromAtmosphere.slf b/libraries/model/src/model/SkyFromAtmosphere.slf index b82cddc282..a9e2bc6156 100755 --- a/libraries/model/src/model/SkyFromAtmosphere.slf +++ b/libraries/model/src/model/SkyFromAtmosphere.slf @@ -104,7 +104,9 @@ void main (void) float fCos = dot(v3LightPos, v3Direction) / length(v3Direction); float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos*fCos) / pow(1.0 + g2 - 2.0*g*fCos, 1.5); - gl_FragColor.rgb = frontColor.rgb + fMiePhase * secondaryFrontColor.rgb; - gl_FragColor.a = gl_FragColor.b; - gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(1.0/2.2)); + + vec3 finalColor = frontColor.rgb + fMiePhase * secondaryFrontColor.rgb; + gl_FragData[0].rgb = finalColor; + gl_FragData[0].a = finalColor.b; + gl_FragData[0].rgb = pow(finalColor.rgb, vec3(1.0/2.2)); } diff --git a/libraries/model/src/model/SkyFromAtmosphere.slv b/libraries/model/src/model/SkyFromAtmosphere.slv index c19cb34c6a..99c782adf1 100755 --- a/libraries/model/src/model/SkyFromAtmosphere.slv +++ b/libraries/model/src/model/SkyFromAtmosphere.slv @@ -55,7 +55,6 @@ const float fSamples = 2.0; varying vec3 myPosition; - void main(void) { // Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere) From 006899d73ff5afec4ca538a87bed39ff084997ce Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 16 Jun 2015 22:23:14 -0700 Subject: [PATCH 78/94] more hacking almost working --- interface/src/Application.cpp | 9 +++++---- interface/src/Environment.cpp | 23 ++++++++++++++++++++--- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5726dcacd5..efe7df4240 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3330,10 +3330,11 @@ namespace render { PerformanceTimer perfTimer("atmosphere"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... atmosphere..."); - //gpu::Batch batch; - background->_environment->renderAtmospheres(batch, *(args->_viewFrustum)); - //gpu::GLBackend::renderBatch(batch, true); - //glUseProgram(0); + gpu::Batch batch; + //DependencyManager::get()->renderSolidSphere(batch,0.5f, 100, 50, glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); //Draw a unit sphere + background->_environment->renderAtmospheres(batch, *(args->_viewFrustum)); + gpu::GLBackend::renderBatch(batch, true); + glUseProgram(0); } diff --git a/interface/src/Environment.cpp b/interface/src/Environment.cpp index 66b463949a..b80e4795d4 100644 --- a/interface/src/Environment.cpp +++ b/interface/src/Environment.cpp @@ -36,6 +36,9 @@ #include "../../build/libraries/render-utils/simple_vert.h" #include "../../build/libraries/render-utils/simple_frag.h" +#include "../../build/libraries/render-utils/other_simple_vert.h" +#include "../../build/libraries/render-utils/other_simple_frag.h" + uint qHash(const HifiSockAddr& sockAddr) { if (sockAddr.getAddress().isNull()) { return 0; // shouldn't happen, but if it does, zero is a perfectly valid hash @@ -69,8 +72,10 @@ void Environment::init() { qDebug() << "here:" << __LINE__; setupAtmosphereProgram(SkyFromSpace_vert, SkyFromSpace_frag, _newSkyFromSpaceProgram, _newSkyFromSpaceUniformLocations); + //setupAtmosphereProgram(other_simple_vert, other_simple_frag, _newSkyFromSpaceProgram, _newSkyFromSpaceUniformLocations); qDebug() << "here:" << __LINE__; setupAtmosphereProgram(SkyFromAtmosphere_vert, SkyFromAtmosphere_frag, _newSkyFromAtmosphereProgram, _newSkyFromAtmosphereUniformLocations); + //setupAtmosphereProgram(other_simple_vert, other_simple_frag, _newSkyFromAtmosphereProgram, _newSkyFromAtmosphereUniformLocations); qDebug() << "here:" << __LINE__; @@ -91,9 +96,19 @@ void Environment::setupAtmosphereProgram(const char* vertSource, const char* fra gpu::Shader::makeProgram(*program, slotBindings); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + + /* state->setCullMode(gpu::State::CULL_NONE); state->setDepthTest(false); state->setBlendFunction(true,gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); + */ + + state->setCullMode(gpu::State::CULL_NONE); + state->setDepthTest(false); + state->setBlendFunction(false, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); locations[CAMERA_POS_LOCATION] = program->getUniforms().findLocation("v3CameraPos"); @@ -280,12 +295,13 @@ ProgramObject* Environment::createSkyProgram(const char* from, int* locations) { } void Environment::renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const EnvironmentData& data) { - + glm::vec3 center = data.getAtmosphereCenter(); Transform transform; transform.setTranslation(center); //transform.setScale(2.0f); + //transform.setTranslation(glm::vec3(0,0,0)); batch.setModelTransform(transform); glm::vec3 relativeCameraPos = camera.getPosition() - center; @@ -304,7 +320,8 @@ void Environment::renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const // the constants here are from Sean O'Neil's GPU Gems entry // (http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter16.html), GameEngine.cpp - + + batch._glUniform3f(locations[CAMERA_POS_LOCATION], relativeCameraPos.x, relativeCameraPos.y, relativeCameraPos.z); glm::vec3 lightDirection = glm::normalize(data.getSunLocation()); batch._glUniform3f(locations[LIGHT_POS_LOCATION], lightDirection.x, lightDirection.y, lightDirection.z); @@ -327,5 +344,5 @@ void Environment::renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const batch._glUniform1f(locations[G_LOCATION], -0.990f); batch._glUniform1f(locations[G2_LOCATION], -0.990f * -0.990f); - DependencyManager::get()->renderSphere(batch,1.0f, 100, 50, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); //Draw a unit sphere + DependencyManager::get()->renderSphere(batch,1.0f, 100, 50, glm::vec4(1.0f, 0.0f, 0.0f, 0.5f)); //Draw a unit sphere } From 5c547037f26ab6d51c2c55c9c484fddfd6a85a7e Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 17 Jun 2015 15:54:20 +0200 Subject: [PATCH 79/94] Migrating the overaly 3d rendering in their own job and their own shader --- .../src/ui/overlays/BillboardOverlay.cpp | 2 + interface/src/ui/overlays/Cube3DOverlay.cpp | 3 +- .../render-utils/src/RenderDeferredTask.cpp | 73 ++++++++++++++++++- .../render-utils/src/RenderDeferredTask.h | 11 +++ libraries/render-utils/src/overlay3D.slf | 28 +++++++ libraries/render-utils/src/overlay3D.slv | 40 ++++++++++ libraries/shared/src/RenderArgs.h | 5 ++ 7 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 libraries/render-utils/src/overlay3D.slf create mode 100644 libraries/render-utils/src/overlay3D.slv diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index ed0ddd8dc7..e7b043f44f 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -99,6 +99,8 @@ void BillboardOverlay::render(RenderArgs* args) { DependencyManager::get()->renderQuad(*batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha)); + + batch->setUniformTexture(0, args->_whiteTexture); // restore default white color after me } else { glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.5f); diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index 73406c07a0..d048b1a05a 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -69,8 +69,9 @@ void Cube3DOverlay::render(RenderArgs* args) { transform.setScale(dimensions); batch->setModelTransform(transform); + DependencyManager::get()->renderSolidCube(*batch, 1.0f, cubeColor); - DependencyManager::get()->renderSolidCube(*batch, 1.0f, cubeColor); + //DependencyManager::get()->renderSolidCube(*batch, 1.0f, cubeColor); } else { if (getIsDashedLine()) { diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index d9dda279e0..ce3c6769ca 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -15,9 +15,12 @@ #include "DeferredLightingEffect.h" #include "ViewFrustum.h" #include "RenderArgs.h" +#include "TextureCache.h" #include +#include "overlay3D_vert.h" +#include "overlay3D_frag.h" using namespace render; @@ -50,7 +53,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() { _jobs.push_back(Job(RenderDeferred())); _jobs.push_back(Job(ResolveDeferred())); _jobs.push_back(Job(DrawTransparentDeferred())); - _jobs.push_back(Job(DrawPostLayered())); + _jobs.push_back(Job(DrawOverlay3D())); _jobs.push_back(Job(ResetGLState())); } @@ -225,10 +228,76 @@ template <> void render::jobRun(const DrawTransparentDeferred& job, const SceneC renderItems(sceneContext, renderContext, renderedItems, renderContext->_maxDrawnTransparentItems); + // Before rendering the batch make sure we re in sync with gl state + args->_context->syncCache(); args->_context->render((*args->_batch)); args->_batch = nullptr; // reset blend function to standard... - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + // glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); } } + +const gpu::PipelinePointer& DrawOverlay3D::getOpaquePipeline() const { + if (!_opaquePipeline) { + auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(overlay3D_vert))); + auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(overlay3D_frag))); + + auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); + + auto state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(true, true, gpu::LESS_EQUAL); + + _opaquePipeline.reset(gpu::Pipeline::create(program, state)); + } + return _opaquePipeline; +} + +template <> void render::jobRun(const DrawOverlay3D& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + PerformanceTimer perfTimer("DrawOverlay3D"); + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + + // render backgrounds + auto& scene = sceneContext->_scene; + auto& items = scene->getMasterBucket().at(ItemFilter::Builder::opaqueShape().withLayered()); + + + ItemIDsBounds inItems; + inItems.reserve(items.size()); + for (auto id : items) { + auto& item = scene->getItem(id); + if (item.getKey().isVisible() && (item.getLayer() == 1)) { + inItems.emplace_back(id); + } + } + + RenderArgs* args = renderContext->args; + gpu::Batch batch; + args->_batch = &batch; + args->_whiteTexture = DependencyManager::get()->getWhiteTexture(); + + + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + batch.setPipeline(job.getOpaquePipeline()); + batch.setUniformTexture(0, args->_whiteTexture); + + if (!inItems.empty()) { + batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0); + renderItems(sceneContext, renderContext, inItems); + } + + // Before rendering the batch make sure we re in sync with gl state + args->_context->syncCache(); + args->_context->render((*args->_batch)); + args->_batch = nullptr; + args->_whiteTexture.reset(); +} diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index e2cac53c0d..3b0ffdfc9b 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -14,6 +14,8 @@ #include "render/DrawTask.h" +#include "gpu/Pipeline.h" + class PrepareDeferred { public: }; @@ -50,6 +52,15 @@ namespace render { template <> void jobRun(const DrawTransparentDeferred& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); } +class DrawOverlay3D { + mutable gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable +public: + const gpu::PipelinePointer& getOpaquePipeline() const; +}; +namespace render { +template <> void jobRun(const DrawOverlay3D& job, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); +} + class RenderDeferredTask : public render::Task { public: diff --git a/libraries/render-utils/src/overlay3D.slf b/libraries/render-utils/src/overlay3D.slf new file mode 100644 index 0000000000..8686066dd8 --- /dev/null +++ b/libraries/render-utils/src/overlay3D.slf @@ -0,0 +1,28 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// model.frag +// fragment shader +// +// Created by Sam Gateau on 6/16/15. +// 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 +// + +uniform sampler2D diffuseMap; + +varying vec2 varTexcoord; + +varying vec3 varEyeNormal; + +varying vec4 varColor; + + +void main(void) { + vec4 diffuse = texture2D(diffuseMap, varTexcoord.st); + + + gl_FragColor = vec4(varColor * diffuse); +} diff --git a/libraries/render-utils/src/overlay3D.slv b/libraries/render-utils/src/overlay3D.slv new file mode 100644 index 0000000000..b272b2bfd0 --- /dev/null +++ b/libraries/render-utils/src/overlay3D.slv @@ -0,0 +1,40 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// overlay3D.slv +// +// Created by Sam Gateau on 6/16/15. +// 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 gpu/Transform.slh@> + +<$declareStandardTransform()$> + +attribute vec2 attribTexcoord; + +varying vec2 varTexcoord; + +// interpolated eye position +varying vec4 varEyePosition; + +// the interpolated normal +varying vec3 varEyeNormal; + +varying vec4 varColor; + +void main(void) { + varTexcoord = attribTexcoord; + + // pass along the color + varColor = gl_Color; + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToEyeAndClipPos(cam, obj, gl_Vertex, varEyePosition, gl_Position)$> + <$transformModelToEyeDir(cam, obj, gl_Normal, varEyeNormal.xyz)$> +} diff --git a/libraries/shared/src/RenderArgs.h b/libraries/shared/src/RenderArgs.h index 84b3a202b4..9673623b13 100644 --- a/libraries/shared/src/RenderArgs.h +++ b/libraries/shared/src/RenderArgs.h @@ -13,6 +13,8 @@ #define hifi_RenderArgs_h #include +#include + class AABox; class OctreeRenderer; @@ -20,6 +22,7 @@ class ViewFrustum; namespace gpu { class Batch; class Context; +class Texture; } class RenderDetails { @@ -109,6 +112,8 @@ public: gpu::Batch* _batch = nullptr; ShoudRenderFunctor _shouldRender; + std::shared_ptr _whiteTexture; + RenderDetails _details; float _alphaThreshold = 0.5f; From d703748ec3ad34ad87062bcbe7a8f1bb5792f0ef Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 17 Jun 2015 16:44:02 +0200 Subject: [PATCH 80/94] trying to solve the rendering of overlay3d --- interface/src/ui/overlays/OverlaysPayload.cpp | 8 +++++--- libraries/render-utils/src/overlay3D.slf | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index d5e4b34f6b..bcfba67313 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -66,8 +66,8 @@ namespace render { } template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) { if (args) { - glPushMatrix(); if (overlay->getAnchor() == Overlay::MY_AVATAR) { + glPushMatrix(); MyAvatar* avatar = DependencyManager::get()->getMyAvatar(); glm::quat myAvatarRotation = avatar->getOrientation(); glm::vec3 myAvatarPosition = avatar->getPosition(); @@ -78,9 +78,11 @@ namespace render { glTranslatef(myAvatarPosition.x, myAvatarPosition.y, myAvatarPosition.z); glRotatef(angle, axis.x, axis.y, axis.z); glScalef(myAvatarScale, myAvatarScale, myAvatarScale); + overlay->render(args); + glPopMatrix(); + } else { + overlay->render(args); } - overlay->render(args); - glPopMatrix(); } } } diff --git a/libraries/render-utils/src/overlay3D.slf b/libraries/render-utils/src/overlay3D.slf index 8686066dd8..e38086ebb5 100644 --- a/libraries/render-utils/src/overlay3D.slf +++ b/libraries/render-utils/src/overlay3D.slf @@ -22,7 +22,9 @@ varying vec4 varColor; void main(void) { vec4 diffuse = texture2D(diffuseMap, varTexcoord.st); - + if (diffuse.a < 0.5) { + discard; + } - gl_FragColor = vec4(varColor * diffuse); + gl_FragColor = vec4(varColor.rgb * (1 - diffuse.a) + diffuse.a * diffuse.rgb, 1.0); } From febc3333cd45839381d37733298e58c9aaa5b07b Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 17 Jun 2015 16:50:35 +0200 Subject: [PATCH 81/94] Solving the rendering of textured overlay3d --- libraries/gpu/src/gpu/GLBackendShader.cpp | 6 +++++- libraries/render-utils/src/overlay3D.slv | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp index e0ea2f2d98..45adbcdb3c 100755 --- a/libraries/gpu/src/gpu/GLBackendShader.cpp +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -61,7 +61,11 @@ void makeBindings(GLBackend::GLShader* shader) { if (loc >= 0) { glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "texcoord"); } - + loc = glGetAttribLocation(glprogram, "attribTexcoord"); + if (loc >= 0) { + glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "attribTexcoord"); + } + loc = glGetAttribLocation(glprogram, "tangent"); if (loc >= 0) { glBindAttribLocation(glprogram, gpu::Stream::TANGENT, "tangent"); diff --git a/libraries/render-utils/src/overlay3D.slv b/libraries/render-utils/src/overlay3D.slv index b272b2bfd0..cdb11c1d08 100644 --- a/libraries/render-utils/src/overlay3D.slv +++ b/libraries/render-utils/src/overlay3D.slv @@ -14,7 +14,7 @@ <$declareStandardTransform()$> -attribute vec2 attribTexcoord; +//attribute vec2 texcoord; varying vec2 varTexcoord; @@ -27,7 +27,7 @@ varying vec3 varEyeNormal; varying vec4 varColor; void main(void) { - varTexcoord = attribTexcoord; + varTexcoord = gl_MultiTexCoord0.xy; // pass along the color varColor = gl_Color; From 3176c8e93c1e82aae16b22ae986e16bf6588a9ed Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 17 Jun 2015 17:09:33 +0200 Subject: [PATCH 82/94] polish before PR --- interface/src/ui/overlays/Cube3DOverlay.cpp | 2 -- libraries/render-utils/src/overlay3D.slf | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index d048b1a05a..37b30a5bcb 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -70,8 +70,6 @@ void Cube3DOverlay::render(RenderArgs* args) { transform.setScale(dimensions); batch->setModelTransform(transform); DependencyManager::get()->renderSolidCube(*batch, 1.0f, cubeColor); - - //DependencyManager::get()->renderSolidCube(*batch, 1.0f, cubeColor); } else { if (getIsDashedLine()) { diff --git a/libraries/render-utils/src/overlay3D.slf b/libraries/render-utils/src/overlay3D.slf index e38086ebb5..69ea8c0e76 100644 --- a/libraries/render-utils/src/overlay3D.slf +++ b/libraries/render-utils/src/overlay3D.slf @@ -25,6 +25,5 @@ void main(void) { if (diffuse.a < 0.5) { discard; } - - gl_FragColor = vec4(varColor.rgb * (1 - diffuse.a) + diffuse.a * diffuse.rgb, 1.0); + gl_FragColor = vec4(varColor * diffuse); } From 2356a071dd4298216d1b16e49b83e36054d44ac0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 17 Jun 2015 08:48:04 -0700 Subject: [PATCH 83/94] some cleanup --- interface/src/Environment.cpp | 71 ++++------------------------------- interface/src/Environment.h | 15 ++------ 2 files changed, 11 insertions(+), 75 deletions(-) diff --git a/interface/src/Environment.cpp b/interface/src/Environment.cpp index b80e4795d4..b333d648ef 100644 --- a/interface/src/Environment.cpp +++ b/interface/src/Environment.cpp @@ -33,11 +33,6 @@ #include "../../build/libraries/model/SkyFromSpace_frag.h" #include "../../build/libraries/model/SkyFromAtmosphere_vert.h" #include "../../build/libraries/model/SkyFromAtmosphere_frag.h" -#include "../../build/libraries/render-utils/simple_vert.h" -#include "../../build/libraries/render-utils/simple_frag.h" - -#include "../../build/libraries/render-utils/other_simple_vert.h" -#include "../../build/libraries/render-utils/other_simple_frag.h" uint qHash(const HifiSockAddr& sockAddr) { if (sockAddr.getAddress().isNull()) { @@ -53,10 +48,6 @@ Environment::Environment() } Environment::~Environment() { - if (_initialized) { - delete _skyFromAtmosphereProgram; - delete _skyFromSpaceProgram; - } } void Environment::init() { @@ -65,19 +56,8 @@ void Environment::init() { return; } - _skyFromAtmosphereProgram = createSkyProgram("Atmosphere", _skyFromAtmosphereUniformLocations); - _skyFromSpaceProgram = createSkyProgram("Space", _skyFromSpaceUniformLocations); - - - - qDebug() << "here:" << __LINE__; - setupAtmosphereProgram(SkyFromSpace_vert, SkyFromSpace_frag, _newSkyFromSpaceProgram, _newSkyFromSpaceUniformLocations); - //setupAtmosphereProgram(other_simple_vert, other_simple_frag, _newSkyFromSpaceProgram, _newSkyFromSpaceUniformLocations); - qDebug() << "here:" << __LINE__; - setupAtmosphereProgram(SkyFromAtmosphere_vert, SkyFromAtmosphere_frag, _newSkyFromAtmosphereProgram, _newSkyFromAtmosphereUniformLocations); - //setupAtmosphereProgram(other_simple_vert, other_simple_frag, _newSkyFromAtmosphereProgram, _newSkyFromAtmosphereUniformLocations); - qDebug() << "here:" << __LINE__; - + setupAtmosphereProgram(SkyFromSpace_vert, SkyFromSpace_frag, _skyFromSpaceProgram, _skyFromSpaceUniformLocations); + setupAtmosphereProgram(SkyFromAtmosphere_vert, SkyFromAtmosphere_frag, _skyFromAtmosphereProgram, _skyFromAtmosphereUniformLocations); // start off with a default-constructed environment data _data[HifiSockAddr()][0]; @@ -97,15 +77,9 @@ void Environment::setupAtmosphereProgram(const char* vertSource, const char* fra gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - /* state->setCullMode(gpu::State::CULL_NONE); state->setDepthTest(false); - state->setBlendFunction(true,gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); - */ - - state->setCullMode(gpu::State::CULL_NONE); - state->setDepthTest(false); - state->setBlendFunction(false, + state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); @@ -267,41 +241,12 @@ int Environment::parseData(const HifiSockAddr& senderAddress, const QByteArray& return bytesRead; } -ProgramObject* Environment::createSkyProgram(const char* from, int* locations) { - ProgramObject* program = new ProgramObject(); - QByteArray prefix = QString(PathUtils::resourcesPath() + "/shaders/SkyFrom" + from).toUtf8(); - program->addShaderFromSourceFile(QGLShader::Vertex, prefix + ".vert"); - program->addShaderFromSourceFile(QGLShader::Fragment, prefix + ".frag"); - program->link(); - - locations[CAMERA_POS_LOCATION] = program->uniformLocation("v3CameraPos"); - locations[LIGHT_POS_LOCATION] = program->uniformLocation("v3LightPos"); - locations[INV_WAVELENGTH_LOCATION] = program->uniformLocation("v3InvWavelength"); - locations[CAMERA_HEIGHT2_LOCATION] = program->uniformLocation("fCameraHeight2"); - locations[OUTER_RADIUS_LOCATION] = program->uniformLocation("fOuterRadius"); - locations[OUTER_RADIUS2_LOCATION] = program->uniformLocation("fOuterRadius2"); - locations[INNER_RADIUS_LOCATION] = program->uniformLocation("fInnerRadius"); - locations[KR_ESUN_LOCATION] = program->uniformLocation("fKrESun"); - locations[KM_ESUN_LOCATION] = program->uniformLocation("fKmESun"); - locations[KR_4PI_LOCATION] = program->uniformLocation("fKr4PI"); - locations[KM_4PI_LOCATION] = program->uniformLocation("fKm4PI"); - locations[SCALE_LOCATION] = program->uniformLocation("fScale"); - locations[SCALE_DEPTH_LOCATION] = program->uniformLocation("fScaleDepth"); - locations[SCALE_OVER_SCALE_DEPTH_LOCATION] = program->uniformLocation("fScaleOverScaleDepth"); - locations[G_LOCATION] = program->uniformLocation("g"); - locations[G2_LOCATION] = program->uniformLocation("g2"); - - return program; -} - void Environment::renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const EnvironmentData& data) { glm::vec3 center = data.getAtmosphereCenter(); Transform transform; transform.setTranslation(center); - //transform.setScale(2.0f); - //transform.setTranslation(glm::vec3(0,0,0)); batch.setModelTransform(transform); glm::vec3 relativeCameraPos = camera.getPosition() - center; @@ -310,18 +255,16 @@ void Environment::renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const // use the appropriate shader depending on whether we're inside or outside int* locations; if (height < data.getAtmosphereOuterRadius()) { - batch.setPipeline(_newSkyFromAtmosphereProgram); - locations = _newSkyFromAtmosphereUniformLocations; + batch.setPipeline(_skyFromAtmosphereProgram); + locations = _skyFromAtmosphereUniformLocations; } else { - batch.setPipeline(_newSkyFromSpaceProgram); - locations = _newSkyFromSpaceUniformLocations; + batch.setPipeline(_skyFromSpaceProgram); + locations = _skyFromSpaceUniformLocations; } // the constants here are from Sean O'Neil's GPU Gems entry // (http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter16.html), GameEngine.cpp - - batch._glUniform3f(locations[CAMERA_POS_LOCATION], relativeCameraPos.x, relativeCameraPos.y, relativeCameraPos.z); glm::vec3 lightDirection = glm::normalize(data.getSunLocation()); batch._glUniform3f(locations[LIGHT_POS_LOCATION], lightDirection.x, lightDirection.y, lightDirection.z); diff --git a/interface/src/Environment.h b/interface/src/Environment.h index 9048678230..547f46c9fe 100644 --- a/interface/src/Environment.h +++ b/interface/src/Environment.h @@ -47,13 +47,9 @@ private: bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration); // NOTE: Deprecated - ProgramObject* createSkyProgram(const char* from, int* locations); - void renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const EnvironmentData& data); bool _initialized; - ProgramObject* _skyFromAtmosphereProgram; - ProgramObject* _skyFromSpaceProgram; enum { CAMERA_POS_LOCATION, @@ -75,16 +71,13 @@ private: LOCATION_COUNT }; - int _skyFromAtmosphereUniformLocations[LOCATION_COUNT]; - int _skyFromSpaceUniformLocations[LOCATION_COUNT]; - void setupAtmosphereProgram(const char* vertSource, const char* fragSource, gpu::PipelinePointer& pipelineProgram, int* locations); - gpu::PipelinePointer _newSkyFromAtmosphereProgram; - gpu::PipelinePointer _newSkyFromSpaceProgram; - int _newSkyFromAtmosphereUniformLocations[LOCATION_COUNT]; - int _newSkyFromSpaceUniformLocations[LOCATION_COUNT]; + gpu::PipelinePointer _skyFromAtmosphereProgram; + gpu::PipelinePointer _skyFromSpaceProgram; + int _skyFromAtmosphereUniformLocations[LOCATION_COUNT]; + int _skyFromSpaceUniformLocations[LOCATION_COUNT]; typedef QHash ServerData; From b5731135ddeb2c4feae495d00f4f4aff399f8f3c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 17 Jun 2015 09:08:41 -0700 Subject: [PATCH 84/94] cleanup --- interface/src/Application.cpp | 8 +++++--- interface/src/Environment.cpp | 10 +++------- interface/src/Environment.h | 2 +- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index efe7df4240..1ead8fe4f8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3330,11 +3330,13 @@ namespace render { PerformanceTimer perfTimer("atmosphere"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... atmosphere..."); - gpu::Batch batch; - //DependencyManager::get()->renderSolidSphere(batch,0.5f, 100, 50, glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); //Draw a unit sphere + background->_environment->renderAtmospheres(batch, *(args->_viewFrustum)); + + // FIX ME - If I don't call this renderBatch() here, then the atmosphere doesn't render, but it + // seems like these payloadRender() methods shouldn't be doing this. We need to investigate why + // the engine isn't rendering our batch gpu::GLBackend::renderBatch(batch, true); - glUseProgram(0); } diff --git a/interface/src/Environment.cpp b/interface/src/Environment.cpp index b333d648ef..09dd8f792e 100644 --- a/interface/src/Environment.cpp +++ b/interface/src/Environment.cpp @@ -15,18 +15,15 @@ #include #include -#include +#include #include +#include +#include #include #include #include #include -#include "Application.h" -#include "Camera.h" -#include "world.h" -#include "InterfaceLogging.h" - #include "Environment.h" #include "../../build/libraries/model/SkyFromSpace_vert.h" @@ -52,7 +49,6 @@ Environment::~Environment() { void Environment::init() { if (_initialized) { - qCDebug(interfaceapp, "[ERROR] Environment is already initialized."); return; } diff --git a/interface/src/Environment.h b/interface/src/Environment.h index 547f46c9fe..fe0c564493 100644 --- a/interface/src/Environment.h +++ b/interface/src/Environment.h @@ -18,7 +18,7 @@ #include #include -#include +//#include #include "EnvironmentData.h" From ea98581d22c20cbba734941fc69b0477afed39e8 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 17 Jun 2015 09:18:00 -0700 Subject: [PATCH 85/94] reorganize files to cleanup headers --- libraries/model/CMakeLists.txt | 3 ++- .../render-utils}/src/Environment.cpp | 13 +++++-------- .../render-utils}/src/Environment.h | 0 .../src}/SkyFromAtmosphere.slf | 0 .../src}/SkyFromAtmosphere.slv | 0 .../src/model => render-utils/src}/SkyFromSpace.slf | 0 .../src/model => render-utils/src}/SkyFromSpace.slv | 0 7 files changed, 7 insertions(+), 9 deletions(-) rename {interface => libraries/render-utils}/src/Environment.cpp (97%) rename {interface => libraries/render-utils}/src/Environment.h (100%) rename libraries/{model/src/model => render-utils/src}/SkyFromAtmosphere.slf (100%) rename libraries/{model/src/model => render-utils/src}/SkyFromAtmosphere.slv (100%) rename libraries/{model/src/model => render-utils/src}/SkyFromSpace.slf (100%) rename libraries/{model/src/model => render-utils/src}/SkyFromSpace.slv (100%) diff --git a/libraries/model/CMakeLists.txt b/libraries/model/CMakeLists.txt index 278c40c435..7456716946 100755 --- a/libraries/model/CMakeLists.txt +++ b/libraries/model/CMakeLists.txt @@ -1,5 +1,6 @@ set(TARGET_NAME model) - + + AUTOSCRIBE_SHADER_LIB(gpu) # use setup_hifi_library macro to setup our project and link appropriate Qt modules diff --git a/interface/src/Environment.cpp b/libraries/render-utils/src/Environment.cpp similarity index 97% rename from interface/src/Environment.cpp rename to libraries/render-utils/src/Environment.cpp index 09dd8f792e..411beca0ae 100644 --- a/interface/src/Environment.cpp +++ b/libraries/render-utils/src/Environment.cpp @@ -9,27 +9,24 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "InterfaceConfig.h" - #include #include #include -#include +#include "GeometryCache.h" #include #include #include #include #include -#include #include #include "Environment.h" -#include "../../build/libraries/model/SkyFromSpace_vert.h" -#include "../../build/libraries/model/SkyFromSpace_frag.h" -#include "../../build/libraries/model/SkyFromAtmosphere_vert.h" -#include "../../build/libraries/model/SkyFromAtmosphere_frag.h" +#include "SkyFromSpace_vert.h" +#include "SkyFromSpace_frag.h" +#include "SkyFromAtmosphere_vert.h" +#include "SkyFromAtmosphere_frag.h" uint qHash(const HifiSockAddr& sockAddr) { if (sockAddr.getAddress().isNull()) { diff --git a/interface/src/Environment.h b/libraries/render-utils/src/Environment.h similarity index 100% rename from interface/src/Environment.h rename to libraries/render-utils/src/Environment.h diff --git a/libraries/model/src/model/SkyFromAtmosphere.slf b/libraries/render-utils/src/SkyFromAtmosphere.slf similarity index 100% rename from libraries/model/src/model/SkyFromAtmosphere.slf rename to libraries/render-utils/src/SkyFromAtmosphere.slf diff --git a/libraries/model/src/model/SkyFromAtmosphere.slv b/libraries/render-utils/src/SkyFromAtmosphere.slv similarity index 100% rename from libraries/model/src/model/SkyFromAtmosphere.slv rename to libraries/render-utils/src/SkyFromAtmosphere.slv diff --git a/libraries/model/src/model/SkyFromSpace.slf b/libraries/render-utils/src/SkyFromSpace.slf similarity index 100% rename from libraries/model/src/model/SkyFromSpace.slf rename to libraries/render-utils/src/SkyFromSpace.slf diff --git a/libraries/model/src/model/SkyFromSpace.slv b/libraries/render-utils/src/SkyFromSpace.slv similarity index 100% rename from libraries/model/src/model/SkyFromSpace.slv rename to libraries/render-utils/src/SkyFromSpace.slv From 0e18c75b0bacd7f6f67e8200690de81cc24e507d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 17 Jun 2015 09:23:12 -0700 Subject: [PATCH 86/94] cleanup --- libraries/render-utils/src/SkyFromAtmosphere.slf | 4 ++-- libraries/render-utils/src/SkyFromAtmosphere.slv | 8 +++----- libraries/render-utils/src/SkyFromSpace.slf | 4 ++-- libraries/render-utils/src/SkyFromSpace.slv | 8 ++++---- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/libraries/render-utils/src/SkyFromAtmosphere.slf b/libraries/render-utils/src/SkyFromAtmosphere.slf index a9e2bc6156..53d0573737 100755 --- a/libraries/render-utils/src/SkyFromAtmosphere.slf +++ b/libraries/render-utils/src/SkyFromAtmosphere.slf @@ -51,7 +51,7 @@ uniform vec3 v3LightPos; uniform float g; uniform float g2; -varying vec3 myPosition; +varying vec3 position; float scale(float fCos) @@ -64,7 +64,7 @@ float scale(float fCos) void main (void) { // Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere) - vec3 v3Pos = myPosition; + vec3 v3Pos = position; vec3 v3Ray = v3Pos - v3CameraPos; float fFar = length(v3Ray); v3Ray /= fFar; diff --git a/libraries/render-utils/src/SkyFromAtmosphere.slv b/libraries/render-utils/src/SkyFromAtmosphere.slv index 99c782adf1..29c907b94c 100755 --- a/libraries/render-utils/src/SkyFromAtmosphere.slv +++ b/libraries/render-utils/src/SkyFromAtmosphere.slv @@ -53,18 +53,16 @@ uniform float fScaleOverScaleDepth; // fScale / fScaleDepth const int nSamples = 2; const float fSamples = 2.0; -varying vec3 myPosition; +varying vec3 position; void main(void) { // Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere) - myPosition = gl_Vertex.xyz * fOuterRadius; + position = gl_Vertex.xyz * fOuterRadius; - //gl_Position = gl_ModelViewProjectionMatrix * vec4(position, 1.0); - // standard transform TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); - vec4 v4pos = vec4(myPosition, 1.0); + vec4 v4pos = vec4(position, 1.0); <$transformModelToClipPos(cam, obj, v4pos, gl_Position)$> } diff --git a/libraries/render-utils/src/SkyFromSpace.slf b/libraries/render-utils/src/SkyFromSpace.slf index 4e13438ccb..5f6ce80efa 100755 --- a/libraries/render-utils/src/SkyFromSpace.slf +++ b/libraries/render-utils/src/SkyFromSpace.slf @@ -53,7 +53,7 @@ uniform float g2; const int nSamples = 2; const float fSamples = 2.0; -varying vec3 myPosition; +varying vec3 position; float scale(float fCos) { @@ -65,7 +65,7 @@ float scale(float fCos) void main (void) { // Get the ray from the camera to the vertex and its length (which is the far point of the ray passing through the atmosphere) - vec3 v3Pos = myPosition; + vec3 v3Pos = position; vec3 v3Ray = v3Pos - v3CameraPos; float fFar = length(v3Ray); v3Ray /= fFar; diff --git a/libraries/render-utils/src/SkyFromSpace.slv b/libraries/render-utils/src/SkyFromSpace.slv index 9090d76da2..6427af6715 100755 --- a/libraries/render-utils/src/SkyFromSpace.slv +++ b/libraries/render-utils/src/SkyFromSpace.slv @@ -38,15 +38,15 @@ uniform float fOuterRadius; // The outer (atmosphere) radius -varying vec3 myPosition; +varying vec3 position; void main(void) { - myPosition = gl_Vertex.xyz * fOuterRadius; + position = gl_Vertex.xyz * fOuterRadius; // standard transform TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); - vec4 v4pos = vec4(myPosition, 1.0); + vec4 v4pos = vec4(position, 1.0); <$transformModelToClipPos(cam, obj, v4pos, gl_Position)$> -} \ No newline at end of file +} From 268bb370ccafc9d395cb40e600005410e8bb092c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 17 Jun 2015 09:24:42 -0700 Subject: [PATCH 87/94] cleanup --- libraries/model/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/model/CMakeLists.txt b/libraries/model/CMakeLists.txt index 7456716946..1520378f1a 100755 --- a/libraries/model/CMakeLists.txt +++ b/libraries/model/CMakeLists.txt @@ -1,6 +1,5 @@ set(TARGET_NAME model) - AUTOSCRIBE_SHADER_LIB(gpu) # use setup_hifi_library macro to setup our project and link appropriate Qt modules From 8162c37013131d4b4011057dcc01e4e5a9bcf6c3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 17 Jun 2015 09:33:15 -0700 Subject: [PATCH 88/94] cleanup --- libraries/model/CMakeLists.txt | 2 +- libraries/render-utils/src/Environment.h | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/model/CMakeLists.txt b/libraries/model/CMakeLists.txt index 1520378f1a..278c40c435 100755 --- a/libraries/model/CMakeLists.txt +++ b/libraries/model/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME model) - + AUTOSCRIBE_SHADER_LIB(gpu) # use setup_hifi_library macro to setup our project and link appropriate Qt modules diff --git a/libraries/render-utils/src/Environment.h b/libraries/render-utils/src/Environment.h index fe0c564493..65e0df4b36 100644 --- a/libraries/render-utils/src/Environment.h +++ b/libraries/render-utils/src/Environment.h @@ -18,9 +18,7 @@ #include #include -//#include - -#include "EnvironmentData.h" +#include class ViewFrustum; class ProgramObject; From 467609f2b69e0b6c9382071a99523f978735b5b2 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 17 Jun 2015 09:44:26 -0700 Subject: [PATCH 89/94] standardize skybox and atmosphere batch --- interface/src/Application.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1ead8fe4f8..8d5f8810fd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3332,12 +3332,6 @@ namespace render { "Application::displaySide() ... atmosphere..."); background->_environment->renderAtmospheres(batch, *(args->_viewFrustum)); - - // FIX ME - If I don't call this renderBatch() here, then the atmosphere doesn't render, but it - // seems like these payloadRender() methods shouldn't be doing this. We need to investigate why - // the engine isn't rendering our batch - gpu::GLBackend::renderBatch(batch, true); - } } @@ -3346,12 +3340,13 @@ namespace render { skybox = skyStage->getSkybox(); if (skybox) { - gpu::Batch batch; model::Skybox::render(batch, *(Application::getInstance()->getDisplayViewFrustum()), *skybox); - gpu::GLBackend::renderBatch(batch, true); - glUseProgram(0); } } + // FIX ME - If I don't call this renderBatch() here, then the atmosphere and skybox don't render, but it + // seems like these payloadRender() methods shouldn't be doing this. We need to investigate why the engine + // isn't rendering our batch + gpu::GLBackend::renderBatch(batch, true); } } From 689e10cc27185c0d17973e28fdbe01eba06e3f75 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 17 Jun 2015 09:53:50 -0700 Subject: [PATCH 90/94] build buster fix --- libraries/model/src/model/Stage.cpp | 15 --------------- libraries/model/src/model/Stage.h | 2 -- 2 files changed, 17 deletions(-) diff --git a/libraries/model/src/model/Stage.cpp b/libraries/model/src/model/Stage.cpp index a255a1f7c9..a3a8b9f3fd 100644 --- a/libraries/model/src/model/Stage.cpp +++ b/libraries/model/src/model/Stage.cpp @@ -14,9 +14,6 @@ #include #include -#include "SkyFromAtmosphere_vert.h" -#include "SkyFromAtmosphere_frag.h" - using namespace model; @@ -207,17 +204,6 @@ SunSkyStage::SunSkyStage() : // Begining of march setYearTime(60.0f); - auto skyFromAtmosphereVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(SkyFromAtmosphere_vert))); - auto skyFromAtmosphereFragment = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(SkyFromAtmosphere_frag))); - auto skyShader = gpu::ShaderPointer(gpu::Shader::createProgram(skyFromAtmosphereVertex, skyFromAtmosphereFragment)); - - auto skyState = gpu::StatePointer(new gpu::State()); - // skyState->setStencilEnable(false); - // skyState->setBlendEnable(false); - - _skyPipeline = gpu::PipelinePointer(gpu::Pipeline::create(skyShader, skyState)); - - _skybox.reset(new Skybox()); _skybox->setColor(Color(1.0f, 0.0f, 0.0f)); @@ -310,7 +296,6 @@ void SunSkyStage::updateGraphicsObject() const { static int firstTime = 0; if (firstTime == 0) { firstTime++; - gpu::Shader::makeProgram(*(_skyPipeline->getProgram())); } } diff --git a/libraries/model/src/model/Stage.h b/libraries/model/src/model/Stage.h index fc90b0c903..b4df45e024 100644 --- a/libraries/model/src/model/Stage.h +++ b/libraries/model/src/model/Stage.h @@ -229,8 +229,6 @@ protected: AtmospherePointer _atmosphere; mutable SkyboxPointer _skybox; - gpu::PipelinePointer _skyPipeline; - float _dayTime = 12.0f; int _yearTime = 0; mutable EarthSunModel _earthSunModel; From e9bf553254c2edf9e80caf691c94a5e79db554a0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 17 Jun 2015 09:59:38 -0700 Subject: [PATCH 91/94] CR feedback --- libraries/render-utils/src/SkyFromAtmosphere.slf | 5 ++--- libraries/render-utils/src/SkyFromSpace.slf | 7 ++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/render-utils/src/SkyFromAtmosphere.slf b/libraries/render-utils/src/SkyFromAtmosphere.slf index 53d0573737..1e1718677c 100755 --- a/libraries/render-utils/src/SkyFromAtmosphere.slf +++ b/libraries/render-utils/src/SkyFromAtmosphere.slf @@ -106,7 +106,6 @@ void main (void) float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos*fCos) / pow(1.0 + g2 - 2.0*g*fCos, 1.5); vec3 finalColor = frontColor.rgb + fMiePhase * secondaryFrontColor.rgb; - gl_FragData[0].rgb = finalColor; - gl_FragData[0].a = finalColor.b; - gl_FragData[0].rgb = pow(finalColor.rgb, vec3(1.0/2.2)); + gl_FragColor.a = finalColor.b; + gl_FragColor.rgb = pow(finalColor.rgb, vec3(1.0/2.2)); } diff --git a/libraries/render-utils/src/SkyFromSpace.slf b/libraries/render-utils/src/SkyFromSpace.slf index 5f6ce80efa..2373511932 100755 --- a/libraries/render-utils/src/SkyFromSpace.slf +++ b/libraries/render-utils/src/SkyFromSpace.slf @@ -108,7 +108,8 @@ void main (void) float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos*fCos) / pow(1.0 + g2 - 2.0*g*fCos, 1.5); vec3 color = v3FrontColor * (v3InvWavelength * fKrESun); vec3 secondaryColor = v3FrontColor * fKmESun; - gl_FragColor.rgb = color + fMiePhase * secondaryColor; - gl_FragColor.a = gl_FragColor.b; - gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(1.0/2.2)); + + vec3 finalColor = color + fMiePhase * secondaryColor; + gl_FragColor.a = finalColor.b; + gl_FragColor.rgb = pow(finalColor.rgb, vec3(1.0/2.2)); } From fb59939cf4b2781e045b07837fda4a1f06c1db30 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 17 Jun 2015 10:02:02 -0700 Subject: [PATCH 92/94] CR feedback --- libraries/model/src/model/Stage.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libraries/model/src/model/Stage.cpp b/libraries/model/src/model/Stage.cpp index a3a8b9f3fd..220ee31c65 100644 --- a/libraries/model/src/model/Stage.cpp +++ b/libraries/model/src/model/Stage.cpp @@ -292,11 +292,6 @@ void SunSkyStage::updateGraphicsObject() const { case NUM_BACKGROUND_MODES: Q_UNREACHABLE(); }; - - static int firstTime = 0; - if (firstTime == 0) { - firstTime++; - } } void SunSkyStage::setBackgroundMode(BackgroundMode mode) { From dc4ae082f87e70db6005b3a47d321e1fde86bdb7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 17 Jun 2015 10:13:49 -0700 Subject: [PATCH 93/94] make goToUser public again --- libraries/networking/src/AddressManager.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index a0fc7eb0be..b4c34176a4 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -72,6 +72,8 @@ public slots: void goBack(); void goForward(); + void goToUser(const QString& username); + void storeCurrentAddress(); void copyAddress(); @@ -100,7 +102,6 @@ private slots: void handleAPIResponse(QNetworkReply& requestReply); void handleAPIError(QNetworkReply& errorReply); - void goToUser(const QString& username); void goToAddressFromObject(const QVariantMap& addressMap, const QNetworkReply& reply); private: void setHost(const QString& host, LookupTrigger trigger); From bc20ea6ed284dacc5a324246e56824c94b02b491 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 17 Jun 2015 11:18:43 -0700 Subject: [PATCH 94/94] fix for path handling on return of place info --- libraries/networking/src/AddressManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 6941d7335d..a8ed54fdd1 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -305,7 +305,7 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const << returnedPath; } } else { - handlePath(overridePath, trigger); + handlePath(returnedPath, trigger); } } else { // we didn't override the path or get one back - ask the DS for the viewpoint of its index path