From fea1433cedc28e4f4beeb2166673207cce946cb1 Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Sat, 7 Jun 2014 10:32:36 +0200 Subject: [PATCH 001/102] Add initial mybalance script --- examples/myBalance.js | 104 ++++++++++++++++++++++++++++++++++ interface/src/Application.cpp | 1 + 2 files changed, 105 insertions(+) create mode 100644 examples/myBalance.js diff --git a/examples/myBalance.js b/examples/myBalance.js new file mode 100644 index 0000000000..8cfad159e5 --- /dev/null +++ b/examples/myBalance.js @@ -0,0 +1,104 @@ +// +// myBalance.js +// examples +// +// Created by Stojce Slavkovski on June 5, 2014 +// Copyright 2014 High Fidelity, Inc. +// +// Show wallet balance +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var Controller = Controller || {}; +var Overlays = Overlays || {}; +var Script = Script || {}; +var AccountManager = AccountManager || {}; + +(function () { + "use strict"; + var iconUrl = 'http://localhost/~stojce/', + overlayWidth = 150, + overlayHeight = 150, + redColor = { + red: 255, + green: 0, + blue: 0 + }, + greenColor = { + red: 0, + green: 255, + blue: 0 + }, + whiteColor = { + red: 255, + green: 255, + blue: 255 + }, + balance = 0, + voxelTool = Overlays.addOverlay("image", { + x: 0, + y: 0, + width: 50, + height: 50, + subImage: { + x: 0, + y: 50, + width: 50, + height: 50 + }, + imageURL: iconUrl + "wallet.svg", + alpha: 1 + }), + textOverlay = Overlays.addOverlay("text", { + x: 0, + y: 0, + width: 55, + height: 13, + topMargin: 5, + text: balance, + alpha: 0 + }); + + function scriptEnding() { + Overlays.deleteOverlay(voxelTool); + Overlays.deleteOverlay(textOverlay); + } + + function update(deltaTime) { + var xPos = Controller.getViewportDimensions().x; + Overlays.editOverlay(voxelTool, { + x: xPos - 150 + }); + + Overlays.editOverlay(textOverlay, { + x: xPos - 100 + }); + } + + function updateBalance(newBalance) { + if (balance === newBalance) { + return; + } + + var change = balance - newBalance, + textColor = change > 0 ? redColor : greenColor; + + balance = newBalance; + Overlays.editOverlay(textOverlay, { + text: balance, + color: textColor + }); + + Script.setTimeout(function () { + Overlays.editOverlay(textOverlay, { + color: whiteColor + }); + }, 1000); + } + + AccountManager.balanceChanged.connect(updateBalance); + Script.scriptEnding.connect(scriptEnding); + Script.update.connect(update); +}()); \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 533d03d9e7..e3df9c17bb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3475,6 +3475,7 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("AnimationCache", &_animationCache); scriptEngine->registerGlobalObject("AudioReflector", &_audioReflector); + scriptEngine->registerGlobalObject("AccountManager", &AccountManager::getInstance()); QThread* workerThread = new QThread(this); From 6365f6b85766fda91e9fbb6230e9054ce95bcfcf Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Sat, 7 Jun 2014 22:24:35 +0200 Subject: [PATCH 002/102] Extend TextOverlay to accept font size Fix TextRenderer color rendering --- interface/src/ui/TextRenderer.cpp | 8 ++++---- interface/src/ui/TextRenderer.h | 7 ++++++- interface/src/ui/overlays/TextOverlay.cpp | 17 +++++++++++++---- interface/src/ui/overlays/TextOverlay.h | 4 +++- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/interface/src/ui/TextRenderer.cpp b/interface/src/ui/TextRenderer.cpp index 2743e3e572..18279d3914 100644 --- a/interface/src/ui/TextRenderer.cpp +++ b/interface/src/ui/TextRenderer.cpp @@ -26,9 +26,9 @@ Glyph::Glyph(int textureID, const QPoint& location, const QRect& bounds, int wid } TextRenderer::TextRenderer(const char* family, int pointSize, int weight, - bool italic, EffectType effectType, int effectThickness) + bool italic, EffectType effectType, int effectThickness, QColor color) : _font(family, pointSize, weight, italic), _metrics(_font), _effectType(effectType), - _effectThickness(effectThickness), _x(IMAGE_SIZE), _y(IMAGE_SIZE), _rowHeight(0) { + _effectThickness(effectThickness), _x(IMAGE_SIZE), _y(IMAGE_SIZE), _rowHeight(0), _color(color) { _font.setKerning(false); } @@ -157,7 +157,7 @@ const Glyph& TextRenderer::getGlyph(char c) { // render the glyph into an image and copy it into the texture QImage image(bounds.width(), bounds.height(), QImage::Format_ARGB32); if (c == SOLID_BLOCK_CHAR) { - image.fill(QColor(255, 255, 255)); + image.fill(_color); } else { image.fill(0); @@ -180,7 +180,7 @@ const Glyph& TextRenderer::getGlyph(char c) { painter.setRenderHint(QPainter::Antialiasing); painter.drawPath(path); } - painter.setPen(QColor(255, 255, 255)); + painter.setPen(_color); painter.drawText(-bounds.x(), -bounds.y(), ch); } glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, bounds.width(), bounds.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.constBits()); diff --git a/interface/src/ui/TextRenderer.h b/interface/src/ui/TextRenderer.h index 813f15a5ac..2daba79c8f 100644 --- a/interface/src/ui/TextRenderer.h +++ b/interface/src/ui/TextRenderer.h @@ -12,6 +12,7 @@ #ifndef hifi_TextRenderer_h #define hifi_TextRenderer_h +#include #include #include #include @@ -41,7 +42,8 @@ public: enum EffectType { NO_EFFECT, SHADOW_EFFECT, OUTLINE_EFFECT }; TextRenderer(const char* family, int pointSize = -1, int weight = -1, bool italic = false, - EffectType effect = NO_EFFECT, int effectThickness = 1); + EffectType effect = NO_EFFECT, int effectThickness = 1, + QColor color = QColor(255, 255, 255)); ~TextRenderer(); const QFontMetrics& metrics() const { return _metrics; } @@ -85,6 +87,9 @@ private: // the list of all texture ids for which we're responsible QVector _allTextureIDs; + + // text color + QColor _color; }; class Glyph { diff --git a/interface/src/ui/overlays/TextOverlay.cpp b/interface/src/ui/overlays/TextOverlay.cpp index e26c772b06..797d0be1a2 100644 --- a/interface/src/ui/overlays/TextOverlay.cpp +++ b/interface/src/ui/overlays/TextOverlay.cpp @@ -19,7 +19,8 @@ TextOverlay::TextOverlay() : _leftMargin(DEFAULT_MARGIN), - _topMargin(DEFAULT_MARGIN) + _topMargin(DEFAULT_MARGIN), + _fontSize(DEFAULT_FONTSIZE) { } @@ -32,7 +33,7 @@ void TextOverlay::render() { } const float MAX_COLOR = 255; - glColor4f(_color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR, _alpha); + glColor4f(0 / MAX_COLOR, 0 / MAX_COLOR, 0 / MAX_COLOR, _alpha); glBegin(GL_QUADS); glVertex2f(_bounds.left(), _bounds.top()); @@ -43,8 +44,9 @@ void TextOverlay::render() { //TextRenderer(const char* family, int pointSize = -1, int weight = -1, bool italic = false, // EffectType effect = NO_EFFECT, int effectThickness = 1); - - TextRenderer textRenderer(SANS_FONT_FAMILY, 11, 50); + TextRenderer textRenderer(SANS_FONT_FAMILY, _fontSize, 50, false, TextRenderer::NO_EFFECT, 1, + QColor(_color.red, _color.green, _color.blue)); + const int leftAdjust = -1; // required to make text render relative to left edge of bounds const int topAdjust = -2; // required to make text render relative to top edge of bounds int x = _bounds.left() + _leftMargin + leftAdjust; @@ -67,6 +69,13 @@ void TextOverlay::render() { void TextOverlay::setProperties(const QScriptValue& properties) { Overlay2D::setProperties(properties); + + QScriptValue font = properties.property("font"); + if (font.isObject()) { + if (font.property("size").isValid()) { + setFontSize(font.property("size").toInt32()); + } + } QScriptValue text = properties.property("text"); if (text.isValid()) { diff --git a/interface/src/ui/overlays/TextOverlay.h b/interface/src/ui/overlays/TextOverlay.h index fc04966d07..78a037762e 100644 --- a/interface/src/ui/overlays/TextOverlay.h +++ b/interface/src/ui/overlays/TextOverlay.h @@ -29,6 +29,7 @@ #include "Overlay2D.h" const int DEFAULT_MARGIN = 10; +const int DEFAULT_FONTSIZE = 11; class TextOverlay : public Overlay2D { Q_OBJECT @@ -47,6 +48,7 @@ public: void setText(const QString& text) { _text = text; } void setLeftMargin(int margin) { _leftMargin = margin; } void setTopMargin(int margin) { _topMargin = margin; } + void setFontSize(int fontSize) { _fontSize = fontSize; } virtual void setProperties(const QScriptValue& properties); @@ -55,7 +57,7 @@ private: QString _text; int _leftMargin; int _topMargin; - + int _fontSize; }; From 976b76ac852a0303107a6ef43cc74dcd38a08b31 Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Sat, 7 Jun 2014 22:29:06 +0200 Subject: [PATCH 003/102] myBalance new layout changes --- examples/myBalance.js | 59 ++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/examples/myBalance.js b/examples/myBalance.js index 8cfad159e5..df47bad3a9 100644 --- a/examples/myBalance.js +++ b/examples/myBalance.js @@ -20,44 +20,43 @@ var AccountManager = AccountManager || {}; "use strict"; var iconUrl = 'http://localhost/~stojce/', overlayWidth = 150, - overlayHeight = 150, - redColor = { - red: 255, + overlayHeight = 50, + overlayTopOffset = 15, + overlayRightOffset = 100, + textRightOffset = 75, + downColor = { + red: 0, green: 0, - blue: 0 + blue: 255 }, - greenColor = { + upColor = { red: 0, green: 255, blue: 0 }, - whiteColor = { - red: 255, - green: 255, - blue: 255 + normalColor = { + red: 204, + green: 204, + blue: 204 }, balance = 0, voxelTool = Overlays.addOverlay("image", { x: 0, - y: 0, - width: 50, - height: 50, - subImage: { - x: 0, - y: 50, - width: 50, - height: 50 - }, + y: overlayTopOffset, + width: 92, + height: 32, imageURL: iconUrl + "wallet.svg", alpha: 1 }), textOverlay = Overlays.addOverlay("text", { x: 0, - y: 0, - width: 55, - height: 13, - topMargin: 5, - text: balance, + y: overlayTopOffset, + topMargin: 9, + text: balance.toFixed(4), + font: { + size: 15 + }, + color: normalColor, alpha: 0 }); @@ -69,11 +68,11 @@ var AccountManager = AccountManager || {}; function update(deltaTime) { var xPos = Controller.getViewportDimensions().x; Overlays.editOverlay(voxelTool, { - x: xPos - 150 + x: xPos - overlayRightOffset }); Overlays.editOverlay(textOverlay, { - x: xPos - 100 + x: xPos - textRightOffset }); } @@ -82,18 +81,20 @@ var AccountManager = AccountManager || {}; return; } - var change = balance - newBalance, - textColor = change > 0 ? redColor : greenColor; + var change = newBalance - balance, + textColor = change < 0 ? downColor : upColor, + integers = balance.toFixed(0).length, + decimals = integers > 4 ? 0 : integers - 4; balance = newBalance; Overlays.editOverlay(textOverlay, { - text: balance, + text: balance.toFixed(decimals), color: textColor }); Script.setTimeout(function () { Overlays.editOverlay(textOverlay, { - color: whiteColor + color: normalColor }); }, 1000); } From 8de2bdfb995d58e757576eebf1efa343a1a4124b Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Sat, 7 Jun 2014 23:17:49 +0200 Subject: [PATCH 004/102] expose account details via AccountScriptingInterface --- examples/myBalance.js | 19 ++++++---- interface/src/Application.cpp | 3 +- .../scripting/AccountScriptingInterface.cpp | 37 +++++++++++++++++++ .../src/scripting/AccountScriptingInterface.h | 35 ++++++++++++++++++ 4 files changed, 85 insertions(+), 9 deletions(-) create mode 100644 interface/src/scripting/AccountScriptingInterface.cpp create mode 100644 interface/src/scripting/AccountScriptingInterface.h diff --git a/examples/myBalance.js b/examples/myBalance.js index df47bad3a9..433967c25d 100644 --- a/examples/myBalance.js +++ b/examples/myBalance.js @@ -14,7 +14,7 @@ var Controller = Controller || {}; var Overlays = Overlays || {}; var Script = Script || {}; -var AccountManager = AccountManager || {}; +var Account = Account || {}; (function () { "use strict"; @@ -24,6 +24,7 @@ var AccountManager = AccountManager || {}; overlayTopOffset = 15, overlayRightOffset = 100, textRightOffset = 75, + maxDecimals = 5, downColor = { red: 0, green: 0, @@ -39,7 +40,7 @@ var AccountManager = AccountManager || {}; green: 204, blue: 204 }, - balance = 0, + balance = -1, voxelTool = Overlays.addOverlay("image", { x: 0, y: overlayTopOffset, @@ -52,7 +53,6 @@ var AccountManager = AccountManager || {}; x: 0, y: overlayTopOffset, topMargin: 9, - text: balance.toFixed(4), font: { size: 15 }, @@ -68,11 +68,13 @@ var AccountManager = AccountManager || {}; function update(deltaTime) { var xPos = Controller.getViewportDimensions().x; Overlays.editOverlay(voxelTool, { - x: xPos - overlayRightOffset + x: xPos - overlayRightOffset, + visible: Account.isLoggedIn() }); Overlays.editOverlay(textOverlay, { - x: xPos - textRightOffset + x: xPos - textRightOffset, + visible: Account.isLoggedIn() }); } @@ -83,8 +85,8 @@ var AccountManager = AccountManager || {}; var change = newBalance - balance, textColor = change < 0 ? downColor : upColor, - integers = balance.toFixed(0).length, - decimals = integers > 4 ? 0 : integers - 4; + integers = newBalance.toFixed(0).length, + decimals = integers > maxDecimals ? 0 : maxDecimals - integers; balance = newBalance; Overlays.editOverlay(textOverlay, { @@ -99,7 +101,8 @@ var AccountManager = AccountManager || {}; }, 1000); } - AccountManager.balanceChanged.connect(updateBalance); + updateBalance(Account.getBalance()); + Account.balanceChanged.connect(updateBalance); Script.scriptEnding.connect(scriptEnding); Script.update.connect(update); }()); \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2df6103c74..1f6f1a2c92 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -73,6 +73,7 @@ #include "devices/TV3DManager.h" #include "renderer/ProgramObject.h" +#include "scripting/AccountScriptingInterface.h" #include "scripting/AudioDeviceScriptingInterface.h" #include "scripting/ClipboardScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" @@ -3469,7 +3470,7 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("AnimationCache", &_animationCache); scriptEngine->registerGlobalObject("AudioReflector", &_audioReflector); - scriptEngine->registerGlobalObject("AccountManager", &AccountManager::getInstance()); + scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance()); QThread* workerThread = new QThread(this); diff --git a/interface/src/scripting/AccountScriptingInterface.cpp b/interface/src/scripting/AccountScriptingInterface.cpp new file mode 100644 index 0000000000..5c41298800 --- /dev/null +++ b/interface/src/scripting/AccountScriptingInterface.cpp @@ -0,0 +1,37 @@ +// +// AccountScriptingInterface.cpp +// interface/src/scripting +// +// Created by Stojce Slavkovski on 6/07/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AccountScriptingInterface.h" + +AccountScriptingInterface::AccountScriptingInterface() { + AccountManager& accountManager = AccountManager::getInstance(); + connect(&accountManager, &AccountManager::balanceChanged, this, + &AccountScriptingInterface::updateBalance); +} + +AccountScriptingInterface* AccountScriptingInterface::getInstance() { + static AccountScriptingInterface sharedInstance; + return &sharedInstance; +} + +qint64 AccountScriptingInterface::getBalance() { + AccountManager& accountManager = AccountManager::getInstance(); + return accountManager.getAccountInfo().getBalance(); +} + +bool AccountScriptingInterface::isLoggedIn() { + AccountManager& accountManager = AccountManager::getInstance(); + return accountManager.isLoggedIn(); +} + +void AccountScriptingInterface::updateBalance(qint16 newBalance) { + emit balanceChanged(newBalance); +} diff --git a/interface/src/scripting/AccountScriptingInterface.h b/interface/src/scripting/AccountScriptingInterface.h new file mode 100644 index 0000000000..de9a7727cd --- /dev/null +++ b/interface/src/scripting/AccountScriptingInterface.h @@ -0,0 +1,35 @@ +// +// AccountScriptingInterface.h +// interface/src/scripting +// +// Created by Stojce Slavkovski on 6/07/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AccountScriptingInterface_h +#define hifi_AccountScriptingInterface_h + +#include +#include +#include + +#include "Application.h" + +class AccountScriptingInterface : public QObject { + Q_OBJECT + AccountScriptingInterface(); + +signals: + void balanceChanged(qint64 newBalance); + +public slots: + static AccountScriptingInterface* getInstance(); + qint64 getBalance(); + bool isLoggedIn(); + void updateBalance(qint16 newBalance); +}; + +#endif // hifi_AccountScriptingInterface_h From b3279e03ca08bd64d6db36adb94a963d1ba75ea2 Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Sat, 7 Jun 2014 23:27:51 +0200 Subject: [PATCH 005/102] references fix --- interface/src/scripting/AccountScriptingInterface.cpp | 2 ++ interface/src/scripting/AccountScriptingInterface.h | 4 ---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/interface/src/scripting/AccountScriptingInterface.cpp b/interface/src/scripting/AccountScriptingInterface.cpp index 5c41298800..f09e2d0890 100644 --- a/interface/src/scripting/AccountScriptingInterface.cpp +++ b/interface/src/scripting/AccountScriptingInterface.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "AccountManager.h" + #include "AccountScriptingInterface.h" AccountScriptingInterface::AccountScriptingInterface() { diff --git a/interface/src/scripting/AccountScriptingInterface.h b/interface/src/scripting/AccountScriptingInterface.h index de9a7727cd..69d8ca4004 100644 --- a/interface/src/scripting/AccountScriptingInterface.h +++ b/interface/src/scripting/AccountScriptingInterface.h @@ -12,11 +12,7 @@ #ifndef hifi_AccountScriptingInterface_h #define hifi_AccountScriptingInterface_h -#include #include -#include - -#include "Application.h" class AccountScriptingInterface : public QObject { Q_OBJECT From 939f8c49defe7ce848b66f231ce7891c98be52fd Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Sun, 8 Jun 2014 15:55:16 +0200 Subject: [PATCH 006/102] return balance in satoshis --- interface/src/scripting/AccountScriptingInterface.cpp | 9 +++++---- interface/src/scripting/AccountScriptingInterface.h | 6 +++--- libraries/networking/src/DataServerAccountInfo.h | 1 + 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/interface/src/scripting/AccountScriptingInterface.cpp b/interface/src/scripting/AccountScriptingInterface.cpp index f09e2d0890..c7b47c7866 100644 --- a/interface/src/scripting/AccountScriptingInterface.cpp +++ b/interface/src/scripting/AccountScriptingInterface.cpp @@ -24,9 +24,9 @@ AccountScriptingInterface* AccountScriptingInterface::getInstance() { return &sharedInstance; } -qint64 AccountScriptingInterface::getBalance() { +float AccountScriptingInterface::getBalance() { AccountManager& accountManager = AccountManager::getInstance(); - return accountManager.getAccountInfo().getBalance(); + return accountManager.getAccountInfo().getBalanceInSatoshis(); } bool AccountScriptingInterface::isLoggedIn() { @@ -34,6 +34,7 @@ bool AccountScriptingInterface::isLoggedIn() { return accountManager.isLoggedIn(); } -void AccountScriptingInterface::updateBalance(qint16 newBalance) { - emit balanceChanged(newBalance); +void AccountScriptingInterface::updateBalance(qint64 newBalance) { + AccountManager& accountManager = AccountManager::getInstance(); + emit balanceChanged(accountManager.getAccountInfo().getBalanceInSatoshis()); } diff --git a/interface/src/scripting/AccountScriptingInterface.h b/interface/src/scripting/AccountScriptingInterface.h index 69d8ca4004..38ce9c1b35 100644 --- a/interface/src/scripting/AccountScriptingInterface.h +++ b/interface/src/scripting/AccountScriptingInterface.h @@ -19,13 +19,13 @@ class AccountScriptingInterface : public QObject { AccountScriptingInterface(); signals: - void balanceChanged(qint64 newBalance); + void balanceChanged(float newBalance); public slots: static AccountScriptingInterface* getInstance(); - qint64 getBalance(); + float getBalance(); bool isLoggedIn(); - void updateBalance(qint16 newBalance); + void updateBalance(qint64 newBalance); }; #endif // hifi_AccountScriptingInterface_h diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h index e0209326f9..2846d2a964 100644 --- a/libraries/networking/src/DataServerAccountInfo.h +++ b/libraries/networking/src/DataServerAccountInfo.h @@ -38,6 +38,7 @@ public: void setDiscourseApiKey(const QString& discourseApiKey); qint64 getBalance() const { return _balance; } + float getBalanceInSatoshis() const { return _balance / SATOSHIS_PER_CREDIT; } void setBalance(qint64 balance); bool hasBalance() const { return _hasBalance; } void setHasBalance(bool hasBalance) { _hasBalance = hasBalance; } From 83b096ca1df8169a448c9be3cc930cae2a3f64ca Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Sun, 8 Jun 2014 15:56:37 +0200 Subject: [PATCH 007/102] myBalance - balance formatting - symbol layout changes --- examples/myBalance.js | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/examples/myBalance.js b/examples/myBalance.js index 433967c25d..2142060f61 100644 --- a/examples/myBalance.js +++ b/examples/myBalance.js @@ -5,7 +5,7 @@ // Created by Stojce Slavkovski on June 5, 2014 // Copyright 2014 High Fidelity, Inc. // -// Show wallet balance +// Show wallet ₵ balance // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -22,9 +22,9 @@ var Account = Account || {}; overlayWidth = 150, overlayHeight = 50, overlayTopOffset = 15, - overlayRightOffset = 100, - textRightOffset = 75, - maxDecimals = 5, + overlayRightOffset = 140, + textRightOffset = 105, + maxIntegers = 5, downColor = { red: 0, green: 0, @@ -41,12 +41,12 @@ var Account = Account || {}; blue: 204 }, balance = -1, - voxelTool = Overlays.addOverlay("image", { + walletBox = Overlays.addOverlay("image", { x: 0, y: overlayTopOffset, - width: 92, + width: 122, height: 32, - imageURL: iconUrl + "wallet.svg", + imageURL: iconUrl + "walletsymbol.svg", alpha: 1 }), textOverlay = Overlays.addOverlay("text", { @@ -54,20 +54,19 @@ var Account = Account || {}; y: overlayTopOffset, topMargin: 9, font: { - size: 15 + size: 16 }, - color: normalColor, - alpha: 0 + color: normalColor }); function scriptEnding() { - Overlays.deleteOverlay(voxelTool); + Overlays.deleteOverlay(walletBox); Overlays.deleteOverlay(textOverlay); } function update(deltaTime) { var xPos = Controller.getViewportDimensions().x; - Overlays.editOverlay(voxelTool, { + Overlays.editOverlay(walletBox, { x: xPos - overlayRightOffset, visible: Account.isLoggedIn() }); @@ -78,19 +77,31 @@ var Account = Account || {}; }); } + function formatedBalance() { + var integers = balance.toFixed(0).length, + decimals = Math.abs(maxIntegers - integers) + 2; + + var x = balance.toFixed(decimals).split('.'), + x1 = x[0], + x2 = x.length > 1 ? '.' + x[1] : ''; + var rgx = /(\d+)(\d{3})/; + while (rgx.test(x1)) { + x1 = x1.replace(rgx, '$1' + ',' + '$2'); + } + return x1 + x2; + } + function updateBalance(newBalance) { if (balance === newBalance) { return; } var change = newBalance - balance, - textColor = change < 0 ? downColor : upColor, - integers = newBalance.toFixed(0).length, - decimals = integers > maxDecimals ? 0 : maxDecimals - integers; + textColor = change < 0 ? downColor : upColor; balance = newBalance; Overlays.editOverlay(textOverlay, { - text: balance.toFixed(decimals), + text: formatedBalance(), color: textColor }); From 2cde2f530395a0c6b2c0b7326e3a24c3ff31432b Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Sun, 8 Jun 2014 16:03:43 +0200 Subject: [PATCH 008/102] use worklist for assets --- examples/myBalance.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/myBalance.js b/examples/myBalance.js index 2142060f61..e0dbd7c370 100644 --- a/examples/myBalance.js +++ b/examples/myBalance.js @@ -18,7 +18,7 @@ var Account = Account || {}; (function () { "use strict"; - var iconUrl = 'http://localhost/~stojce/', + var iconUrl = 'http://dev.worklist.net/~stojce/', overlayWidth = 150, overlayHeight = 50, overlayTopOffset = 15, @@ -52,7 +52,7 @@ var Account = Account || {}; textOverlay = Overlays.addOverlay("text", { x: 0, y: overlayTopOffset, - topMargin: 9, + topMargin: 10, font: { size: 16 }, From e25817cd88d3794800fc205fec5142083a2ab29e Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Tue, 10 Jun 2014 00:08:10 +0200 Subject: [PATCH 009/102] switch to amazon image resources --- examples/myBalance.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/myBalance.js b/examples/myBalance.js index e0dbd7c370..bd48e8fd21 100644 --- a/examples/myBalance.js +++ b/examples/myBalance.js @@ -18,7 +18,7 @@ var Account = Account || {}; (function () { "use strict"; - var iconUrl = 'http://dev.worklist.net/~stojce/', + var iconUrl = 'http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/', overlayWidth = 150, overlayHeight = 50, overlayTopOffset = 15, @@ -46,7 +46,7 @@ var Account = Account || {}; y: overlayTopOffset, width: 122, height: 32, - imageURL: iconUrl + "walletsymbol.svg", + imageURL: iconUrl + "wallet.svg", alpha: 1 }), textOverlay = Overlays.addOverlay("text", { From 39e3deb9b950e0976c33fc9deca50b441daa695f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 10 Jun 2014 15:03:07 -0700 Subject: [PATCH 010/102] Add menu item that toggles VR display mode Toggles the display mode if a Rift is found connected at program start. --- interface/src/Application.cpp | 3 +++ interface/src/Application.h | 1 + interface/src/Menu.cpp | 5 +++++ interface/src/Menu.h | 1 + interface/src/devices/OculusManager.cpp | 8 ++++++++ interface/src/devices/OculusManager.h | 2 +- 6 files changed, 19 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3cfec3190e..3075d6ccaf 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1369,6 +1369,9 @@ void Application::setEnable3DTVMode(bool enable3DTVMode) { resizeGL(_glWidget->width(),_glWidget->height()); } +void Application::setEnableVRMode(bool enableVRMode) { + resizeGL(_glWidget->width(), _glWidget->height()); +} void Application::setRenderVoxels(bool voxelRender) { _voxelEditSender.setShouldSend(voxelRender); diff --git a/interface/src/Application.h b/interface/src/Application.h index 170be43493..f164c28be1 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -345,6 +345,7 @@ private slots: void setFullscreen(bool fullscreen); void setEnable3DTVMode(bool enable3DTVMode); + void setEnableVRMode(bool enableVRMode); void cameraMenuChanged(); glm::vec2 getScaledScreenPoint(glm::vec2 projectedPoint); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f791d20588..d59a4b23e1 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -253,6 +253,11 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H, false, appInstance, SLOT(cameraMenuChanged())); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::EnableVRMode, 0, + false, + appInstance, + SLOT(setEnableVRMode(bool))); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Enable3DTVMode, 0, false, appInstance, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 69015a938b..b39aefb0ba 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -334,6 +334,7 @@ namespace MenuOption { const QString EchoLocalAudio = "Echo Local Audio"; const QString EchoServerAudio = "Echo Server Audio"; const QString Enable3DTVMode = "Enable 3DTV Mode"; + const QString EnableVRMode = "Enable VR Mode"; const QString ExpandMiscAvatarTiming = "Expand Misc MyAvatar Timing"; const QString ExpandAvatarUpdateTiming = "Expand MyAvatar update Timing"; const QString ExpandAvatarSimulateTiming = "Expand MyAvatar simulate Timing"; diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index fe8cc37168..b2ee4e8c18 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -72,6 +72,14 @@ void OculusManager::connect() { #endif } +bool OculusManager::isConnected() { +#ifdef HAVE_LIBOVR + return _isConnected && Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode); +#else + return false; +#endif +} + void OculusManager::configureCamera(Camera& camera, int screenWidth, int screenHeight) { #ifdef HAVE_LIBOVR _stereoConfig.SetFullViewport(Viewport(0, 0, screenWidth, screenHeight)); diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index 6376df05ca..caff38a6b0 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -27,7 +27,7 @@ class OculusManager { public: static void connect(); - static bool isConnected() { return _isConnected; } + static bool isConnected(); static void configureCamera(Camera& camera, int screenWidth, int screenHeight); From 69899f4d37bfa28bf1f29f48a0bd3121d6a6a46b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 10 Jun 2014 16:53:00 -0700 Subject: [PATCH 011/102] Edging towards generic container types. --- libraries/metavoxels/src/Bitstream.cpp | 95 ++++++++++++++++++++++---- libraries/metavoxels/src/Bitstream.h | 68 +++++++++++++++--- 2 files changed, 140 insertions(+), 23 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index d7e15a9dd6..ca6cd5dd83 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -44,6 +44,9 @@ static int quatStreamer = Bitstream::registerTypeStreamer(qMetaTypeId static int metaObjectStreamer = Bitstream::registerTypeStreamer(qMetaTypeId(), new SimpleTypeStreamer()); +static int genericValueStreamer = Bitstream::registerTypeStreamer( + qRegisterMetaType(), new GenericTypeStreamer()); + IDStreamer::IDStreamer(Bitstream& stream) : _stream(stream), _bits(1) { @@ -92,7 +95,10 @@ int Bitstream::registerMetaObject(const char* className, const QMetaObject* meta } int Bitstream::registerTypeStreamer(int type, TypeStreamer* streamer) { - streamer->setType(type); + streamer->_type = type; + if (!streamer->_self) { + streamer->_self = TypeStreamerPointer(streamer); + } getTypeStreamers().insert(type, streamer); return 0; } @@ -109,12 +115,13 @@ QList Bitstream::getMetaObjectSubClasses(const QMetaObject* return getMetaObjectSubClasses().values(metaObject); } -Bitstream::Bitstream(QDataStream& underlying, MetadataType metadataType, QObject* parent) : +Bitstream::Bitstream(QDataStream& underlying, MetadataType metadataType, GenericsMode genericsMode, QObject* parent) : QObject(parent), _underlying(underlying), _byte(0), _position(0), _metadataType(metadataType), + _genericsMode(genericsMode), _metaObjectStreamer(*this), _typeStreamerStreamer(*this), _attributeStreamer(*this), @@ -751,7 +758,7 @@ Bitstream& Bitstream::operator<<(const QVariant& value) { } const TypeStreamer* streamer = getTypeStreamers().value(value.userType()); if (streamer) { - _typeStreamerStreamer << streamer; + _typeStreamerStreamer << streamer->getStreamerToWrite(value); streamer->write(*this, value); } else { qWarning() << "Non-streamable type: " << value.typeName() << "\n"; @@ -793,6 +800,16 @@ Bitstream& Bitstream::operator>>(OwnedAttributeValue& attributeValue) { return *this; } +Bitstream& Bitstream::operator<<(const GenericValue& value) { + value.getStreamer()->write(*this, value.getValue()); + return *this; +} + +Bitstream& Bitstream::operator>>(GenericValue& value) { + value = GenericValue(); + return *this; +} + Bitstream& Bitstream::operator<<(const QObject* object) { if (!object) { _metaObjectStreamer << NULL; @@ -1232,20 +1249,26 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { streamer = getEnumStreamersByName().value(typeName); } } - if (!streamer) { - qWarning() << "Unknown type name: " << typeName << "\n"; - } if (_metadataType == NO_METADATA) { + if (!streamer) { + qWarning() << "Unknown type name:" << typeName; + } reader = TypeReader(typeName, streamer); return *this; } int type; *this >> type; + if (type == TypeReader::SIMPLE_TYPE) { + if (!streamer) { + qWarning() << "Unknown type name:" << typeName; + } + reader = TypeReader(typeName, streamer); + return *this; + } + if (_genericsMode == ALL_GENERICS) { + streamer = NULL; + } switch (type) { - case TypeReader::SIMPLE_TYPE: - reader = TypeReader(typeName, streamer); - return *this; - case TypeReader::ENUM_TYPE: { if (_metadataType == FULL_METADATA) { int keyCount; @@ -1910,6 +1933,38 @@ const char* TypeStreamer::getName() const { return QMetaType::typeName(_type); } +const TypeStreamer* TypeStreamer::getStreamerToWrite(const QVariant& value) const { + return this; +} + +bool TypeStreamer::equal(const QVariant& first, const QVariant& second) const { + return first == second; +} + +void TypeStreamer::write(Bitstream& out, const QVariant& value) const { + // nothing by default +} + +QVariant TypeStreamer::read(Bitstream& in) const { + return QVariant(); +} + +void TypeStreamer::writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { + // nothing by default +} + +void TypeStreamer::readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const { + value = reference; +} + +void TypeStreamer::writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { + // nothing by default +} + +void TypeStreamer::readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const { + value = reference; +} + void TypeStreamer::setEnumValue(QVariant& object, int value, const QHash& mappings) const { // nothing by default } @@ -1993,15 +2048,17 @@ EnumTypeStreamer::EnumTypeStreamer(const QMetaObject* metaObject, const char* na _name(QByteArray(metaObject->className()) + "::" + name), _bits(-1) { - setType(QMetaType::Int); + _type = QMetaType::Int; + _self = TypeStreamerPointer(this); } EnumTypeStreamer::EnumTypeStreamer(const QMetaEnum& metaEnum) : _name(QByteArray(metaEnum.scope()) + "::" + metaEnum.name()), _metaEnum(metaEnum), _bits(-1) { - - setType(QMetaType::Int); + + _type = QMetaType::Int; + _self = TypeStreamerPointer(this); } const char* EnumTypeStreamer::getName() const { @@ -2094,3 +2151,15 @@ void EnumTypeStreamer::setEnumValue(QVariant& object, int value, const QHash().getStreamer().data(); +} diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index d05f6574c0..dadfe52911 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -36,6 +36,7 @@ class Attribute; class AttributeValue; class Bitstream; class FieldReader; +class GenericValue; class ObjectReader; class OwnedAttributeValue; class PropertyReader; @@ -232,8 +233,17 @@ public: enum MetadataType { NO_METADATA, HASH_METADATA, FULL_METADATA }; + enum GenericsMode { NO_GENERICS, FALLBACK_GENERICS, ALL_GENERICS }; + /// Creates a new bitstream. Note: the stream may be used for reading or writing, but not both. - Bitstream(QDataStream& underlying, MetadataType metadataType = NO_METADATA, QObject* parent = NULL); + /// \param metadataType the metadata type, which determines the amount of version-resiliency: with no metadata, all types + /// must match exactly; with hash metadata, any types which do not match exactly will be ignored; with full metadata, + /// fields (and enum values, etc.) will be remapped by name + /// \param genericsMode the generics mode, which determines which types will be replaced by generic equivalents: with + /// no generics, no generics will be created; with fallback generics, generics will be created for any unknown types; with + /// all generics, generics will be created for all non-simple types + Bitstream(QDataStream& underlying, MetadataType metadataType = NO_METADATA, + GenericsMode = NO_GENERICS, QObject* parent = NULL); /// Substitutes the supplied metaobject for the given class name's default mapping. void addMetaObjectSubstitution(const QByteArray& className, const QMetaObject* metaObject); @@ -364,6 +374,9 @@ public: Bitstream& operator<<(const AttributeValue& attributeValue); Bitstream& operator>>(OwnedAttributeValue& attributeValue); + Bitstream& operator<<(const GenericValue& value); + Bitstream& operator>>(GenericValue& value); + template Bitstream& operator<<(const QList& list); template Bitstream& operator>>(QList& list); @@ -429,6 +442,7 @@ private: int _position; MetadataType _metadataType; + GenericsMode _genericsMode; RepeatedValueStreamer _metaObjectStreamer; RepeatedValueStreamer _typeStreamerStreamer; @@ -890,27 +904,32 @@ Q_DECLARE_METATYPE(const QMetaObject*) /// Macro for registering streamable meta-objects. #define REGISTER_META_OBJECT(x) static int x##Registration = Bitstream::registerMetaObject(#x, &x::staticMetaObject); +typedef QSharedPointer TypeStreamerPointer; + /// Interface for objects that can write values to and read values from bitstreams. class TypeStreamer { public: virtual ~TypeStreamer(); - void setType(int type) { _type = type; } int getType() const { return _type; } + const TypeStreamerPointer& getSelf() const { return _self; } + virtual const char* getName() const; - virtual bool equal(const QVariant& first, const QVariant& second) const = 0; + virtual const TypeStreamer* getStreamerToWrite(const QVariant& value) const; - virtual void write(Bitstream& out, const QVariant& value) const = 0; - virtual QVariant read(Bitstream& in) const = 0; + virtual bool equal(const QVariant& first, const QVariant& second) const; + + virtual void write(Bitstream& out, const QVariant& value) const; + virtual QVariant read(Bitstream& in) const; - virtual void writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const = 0; - virtual void readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const = 0; + virtual void writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const; + virtual void readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const; - virtual void writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const = 0; - virtual void readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const = 0; + virtual void writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const; + virtual void readRawDelta(Bitstream& in, QVariant& value, const QVariant& reference) const; virtual void setEnumValue(QVariant& object, int value, const QHash& mappings) const; @@ -937,9 +956,12 @@ public: virtual QVariant getValue(const QVariant& object, int index) const; virtual void setValue(QVariant& object, int index, const QVariant& value) const; -private: +protected: + + friend class Bitstream; int _type; + TypeStreamerPointer _self; }; QDebug& operator<<(QDebug& debug, const TypeStreamer* typeStreamer); @@ -992,6 +1014,32 @@ private: int _bits; }; +/// Contains a value along with a pointer to its streamer. +class GenericValue { +public: + + GenericValue(const TypeStreamerPointer& streamer = TypeStreamerPointer(), const QVariant& value = QVariant()); + + const TypeStreamerPointer& getStreamer() const { return _streamer; } + const QVariant& getValue() const { return _value; } + + bool operator==(const GenericValue& other) const; + +private: + + TypeStreamerPointer _streamer; + QVariant _value; +}; + +Q_DECLARE_METATYPE(GenericValue) + +/// A streamer class for generic values. +class GenericTypeStreamer : public SimpleTypeStreamer { +public: + + virtual const TypeStreamer* getStreamerToWrite(const QVariant& value) const; +}; + /// A streamer for types compiled by mtc. template class StreamableTypeStreamer : public SimpleTypeStreamer { public: From 9aa152f43ff01938c5b8e87105426541c2357ba3 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 10 Jun 2014 18:29:21 -0700 Subject: [PATCH 012/102] More TypeReader refactoring progress. --- libraries/metavoxels/src/Bitstream.cpp | 157 ++++++++++++++++++++++++- libraries/metavoxels/src/Bitstream.h | 60 +++++++++- 2 files changed, 213 insertions(+), 4 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index ca6cd5dd83..83ec86013c 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -1950,11 +1950,22 @@ QVariant TypeStreamer::read(Bitstream& in) const { } void TypeStreamer::writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { - // nothing by default + if (value == reference) { + out << false; + } else { + out << true; + writeRawDelta(out, value, reference); + } } void TypeStreamer::readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const { - value = reference; + bool changed; + in >> changed; + if (changed) { + readRawDelta(in, value, reference); + } else { + value = reference; + } } void TypeStreamer::writeRawDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { @@ -2151,6 +2162,30 @@ void EnumTypeStreamer::setEnumValue(QVariant& object, int value, const QHash& mappings) : + _baseStreamer(baseStreamer), + _bits(bits), + _mappings(mappings) { +} + +QVariant MappedEnumTypeStreamer::read(Bitstream& in) const { + QVariant object = _baseStreamer ? QVariant(_baseStreamer->getType(), 0) : QVariant(); + int value = 0; + in.read(&value, _bits); + if (_baseStreamer) { + _baseStreamer->setEnumValue(object, value, _mappings); + } + return object; +} + +void MappedEnumTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { + int value = 0; + in.read(&value, _bits); + if (_baseStreamer) { + _baseStreamer->setEnumValue(object, value, _mappings); + } +} + GenericValue::GenericValue(const TypeStreamerPointer& streamer, const QVariant& value) : _streamer(streamer), _value(value) { @@ -2163,3 +2198,121 @@ bool GenericValue::operator==(const GenericValue& other) const { const TypeStreamer* GenericTypeStreamer::getStreamerToWrite(const QVariant& value) const { return value.value().getStreamer().data(); } + +MappedListTypeStreamer::MappedListTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& valueStreamer) : + _baseStreamer(baseStreamer), + _valueStreamer(valueStreamer) { +} + +QVariant MappedListTypeStreamer::read(Bitstream& in) const { + QVariant object = _baseStreamer ? QVariant(_baseStreamer->getType(), 0) : QVariant(); + int size; + in >> size; + for (int i = 0; i < size; i++) { + QVariant value = _valueStreamer->read(in); + if (_baseStreamer) { + _baseStreamer->insert(object, value); + } + } + return object; +} + +void MappedListTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { + object = reference; + int size, referenceSize; + in >> size >> referenceSize; + if (_baseStreamer) { + if (size < referenceSize) { + _baseStreamer->prune(object, size); + } + for (int i = 0; i < size; i++) { + if (i < referenceSize) { + QVariant value; + _valueStreamer->readDelta(in, value, _baseStreamer->getValue(reference, i)); + _baseStreamer->setValue(object, i, value); + } else { + _baseStreamer->insert(object, _valueStreamer->read(in)); + } + } + } else { + for (int i = 0; i < size; i++) { + if (i < referenceSize) { + QVariant value; + _valueStreamer->readDelta(in, value, QVariant()); + } else { + _valueStreamer->read(in); + } + } + } +} + +MappedSetTypeStreamer::MappedSetTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& valueStreamer) : + MappedListTypeStreamer(baseStreamer, valueStreamer) { +} + +void MappedSetTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { + object = reference; + int addedOrRemoved; + in >> addedOrRemoved; + for (int i = 0; i < addedOrRemoved; i++) { + QVariant value = _valueStreamer->read(in); + if (_baseStreamer && !_baseStreamer->remove(object, value)) { + _baseStreamer->insert(object, value); + } + } +} + +MappedMapTypeStreamer::MappedMapTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& keyStreamer, + const TypeStreamerPointer& valueStreamer) : + _baseStreamer(baseStreamer), + _keyStreamer(keyStreamer), + _valueStreamer(valueStreamer) { +} + +QVariant MappedMapTypeStreamer::read(Bitstream& in) const { + QVariant object = _baseStreamer ? QVariant(_baseStreamer->getType(), 0) : QVariant(); + int size; + in >> size; + for (int i = 0; i < size; i++) { + QVariant key = _keyStreamer->read(in); + QVariant value = _valueStreamer->read(in); + if (_baseStreamer) { + _baseStreamer->insert(object, key, value); + } + } + return object; +} + +void MappedMapTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { + object = reference; + int added; + in >> added; + for (int i = 0; i < added; i++) { + QVariant key = _keyStreamer->read(in); + QVariant value = _valueStreamer->read(in); + if (_baseStreamer) { + _baseStreamer->insert(object, key, value); + } + } + int modified; + in >> modified; + for (int i = 0; i < modified; i++) { + QVariant key = _keyStreamer->read(in); + QVariant value; + if (_baseStreamer) { + _valueStreamer->readDelta(in, value, _baseStreamer->getValue(reference, key)); + _baseStreamer->insert(object, key, value); + } else { + _valueStreamer->readDelta(in, value, QVariant()); + } + } + int removed; + in >> removed; + for (int i = 0; i < removed; i++) { + QVariant key = _keyStreamer->read(in); + if (_baseStreamer) { + _baseStreamer->remove(object, key); + } + } +} + diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index dadfe52911..cd7a0439aa 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -47,6 +47,7 @@ class TypeStreamer; typedef SharedObjectPointerTemplate AttributePointer; typedef QPair ScopeNamePair; +typedef QSharedPointer TypeStreamerPointer; typedef QVector PropertyReaderVector; typedef QVector PropertyWriterVector; @@ -904,8 +905,6 @@ Q_DECLARE_METATYPE(const QMetaObject*) /// Macro for registering streamable meta-objects. #define REGISTER_META_OBJECT(x) static int x##Registration = Bitstream::registerMetaObject(#x, &x::staticMetaObject); -typedef QSharedPointer TypeStreamerPointer; - /// Interface for objects that can write values to and read values from bitstreams. class TypeStreamer { public: @@ -1014,6 +1013,22 @@ private: int _bits; }; +/// A streamer class for enums that maps to a local type. +class MappedEnumTypeStreamer : public TypeStreamer { +public: + + MappedEnumTypeStreamer(const TypeStreamer* baseStreamer, int bits, const QHash& mappings); + + virtual QVariant read(Bitstream& in) const; + virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; + +private: + + const TypeStreamer* _baseStreamer; + int _bits; + QHash _mappings; +}; + /// Contains a value along with a pointer to its streamer. class GenericValue { public: @@ -1089,6 +1104,21 @@ public: static_cast*>(object.data())->replace(index, value.value()); } }; +/// A streamer for lists that maps to a local type. +class MappedListTypeStreamer : public TypeStreamer { +public: + + MappedListTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& valueStreamer); + + virtual QVariant read(Bitstream& in) const; + virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; + +protected: + + const TypeStreamer* _baseStreamer; + TypeStreamerPointer _valueStreamer; +}; + /// A streamer for set types. template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: @@ -1101,6 +1131,15 @@ public: return static_cast*>(object.data())->remove(key.value()); } }; +/// A streamer for sets that maps to a local type. +class MappedSetTypeStreamer : public MappedListTypeStreamer { +public: + + MappedSetTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& valueStreamer); + + virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; +}; + /// A streamer for hash types. template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: @@ -1116,6 +1155,23 @@ public: return QVariant::fromValue(static_cast*>(object.constData())->value(key.value())); } }; +/// A streamer for maps that maps to a local type. +class MappedMapTypeStreamer : public TypeStreamer { +public: + + MappedMapTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& keyStreamer, + const TypeStreamerPointer& valueStreamer); + + virtual QVariant read(Bitstream& in) const; + virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; + +private: + + const TypeStreamer* _baseStreamer; + TypeStreamerPointer _keyStreamer; + TypeStreamerPointer _valueStreamer; +}; + /// Macro for registering simple type streamers. #define REGISTER_SIMPLE_TYPE_STREAMER(X) static int X##Streamer = \ Bitstream::registerTypeStreamer(qMetaTypeId(), new SimpleTypeStreamer()); From 760627003f353a1552e8e94b050698b2c233bd5a Mon Sep 17 00:00:00 2001 From: wangyix Date: Wed, 11 Jun 2014 11:11:04 -0700 Subject: [PATCH 013/102] killNode() to nodeKilled() --- libraries/networking/src/ReceivedPacketProcessor.cpp | 4 ++-- libraries/networking/src/ReceivedPacketProcessor.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index d556e8a059..e919017128 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -50,6 +50,6 @@ bool ReceivedPacketProcessor::process() { return isStillRunning(); // keep running till they terminate us } -void ReceivedPacketProcessor::killNode(const SharedNodePointer& node) { - _nodePacketCounts.remove(node->getUUID()); +void ReceivedPacketProcessor::nodeKilled(const SharedNodePointer& node) { + int numRemoved = _nodePacketCounts.remove(node->getUUID()); } diff --git a/libraries/networking/src/ReceivedPacketProcessor.h b/libraries/networking/src/ReceivedPacketProcessor.h index 80fe75aaa7..23303cbb55 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.h +++ b/libraries/networking/src/ReceivedPacketProcessor.h @@ -42,7 +42,7 @@ public: int packetsToProcessCount() const { return _packets.size(); } public slots: - void killNode(const SharedNodePointer& node); + void nodeKilled(const SharedNodePointer& node); protected: /// Callback for processing of recieved packets. Implement this to process the incoming packets. From 6b48554fdd39d685bcffb56bbcaed94ffe4b3275 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 11 Jun 2014 11:26:21 -0700 Subject: [PATCH 014/102] Added explanatory comment. --- libraries/metavoxels/src/ScriptCache.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/metavoxels/src/ScriptCache.cpp b/libraries/metavoxels/src/ScriptCache.cpp index 9648a047cb..ffd5200a2e 100644 --- a/libraries/metavoxels/src/ScriptCache.cpp +++ b/libraries/metavoxels/src/ScriptCache.cpp @@ -90,6 +90,7 @@ bool operator==(const QScriptValue& first, const QScriptValue& second) { return true; } else { + // if none of the above tests apply, first must be invalid return !second.isValid(); } } From decd6109fbda6682d97ae23432bfe9b2ac5c4cd8 Mon Sep 17 00:00:00 2001 From: wangyix Date: Wed, 11 Jun 2014 11:57:54 -0700 Subject: [PATCH 015/102] made nodeKilled signatures match --- libraries/networking/src/ReceivedPacketProcessor.cpp | 2 +- libraries/networking/src/ReceivedPacketProcessor.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index e919017128..59165b45b6 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -50,6 +50,6 @@ bool ReceivedPacketProcessor::process() { return isStillRunning(); // keep running till they terminate us } -void ReceivedPacketProcessor::nodeKilled(const SharedNodePointer& node) { +void ReceivedPacketProcessor::nodeKilled(SharedNodePointer node) { int numRemoved = _nodePacketCounts.remove(node->getUUID()); } diff --git a/libraries/networking/src/ReceivedPacketProcessor.h b/libraries/networking/src/ReceivedPacketProcessor.h index 23303cbb55..94ad2d9c41 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.h +++ b/libraries/networking/src/ReceivedPacketProcessor.h @@ -42,7 +42,7 @@ public: int packetsToProcessCount() const { return _packets.size(); } public slots: - void nodeKilled(const SharedNodePointer& node); + void nodeKilled(SharedNodePointer node); protected: /// Callback for processing of recieved packets. Implement this to process the incoming packets. From 2080cc3dee753d072b005bbac3e868ab8bd0716a Mon Sep 17 00:00:00 2001 From: wangyix Date: Wed, 11 Jun 2014 11:59:15 -0700 Subject: [PATCH 016/102] removed debug variable --- libraries/networking/src/ReceivedPacketProcessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index 59165b45b6..32fc4e26f3 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -51,5 +51,5 @@ bool ReceivedPacketProcessor::process() { } void ReceivedPacketProcessor::nodeKilled(SharedNodePointer node) { - int numRemoved = _nodePacketCounts.remove(node->getUUID()); + _nodePacketCounts.remove(node->getUUID()); } From a8c97800048043e4908743601ae95b3c92fe80d9 Mon Sep 17 00:00:00 2001 From: wangyix Date: Wed, 11 Jun 2014 12:13:27 -0700 Subject: [PATCH 017/102] updated connect() to qt5 format --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2b3256141e..a5cb6a0c12 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -244,7 +244,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer))); connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), &_voxels, SLOT(nodeAdded(SharedNodePointer))); connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_voxels, SLOT(nodeKilled(SharedNodePointer))); - connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_octreeProcessor, SLOT(nodeKilled(SharedNodePointer))); + connect(NodeList::getInstance(), &NodeList::nodeKilled, &_octreeProcessor, &ReceivedPacketProcessor::nodeKilled); connect(nodeList, &NodeList::uuidChanged, this, &Application::updateWindowTitle); connect(nodeList, SIGNAL(uuidChanged(const QUuid&)), _myAvatar, SLOT(setSessionUUID(const QUuid&))); connect(nodeList, &NodeList::limitOfSilentDomainCheckInsReached, nodeList, &NodeList::reset); From a529bc7e28579c5663c03f70c6fdf1020d69e2b9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 11 Jun 2014 13:40:12 -0700 Subject: [PATCH 018/102] Refactoring. --- libraries/metavoxels/src/Bitstream.cpp | 463 +++++++------------------ libraries/metavoxels/src/Bitstream.h | 117 ++----- 2 files changed, 157 insertions(+), 423 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 83ec86013c..ce46429393 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -301,9 +301,9 @@ void Bitstream::writeRawDelta(const QVariant& value, const QVariant& reference) } void Bitstream::readRawDelta(QVariant& value, const QVariant& reference) { - TypeReader typeReader; - _typeStreamerStreamer >> typeReader; - typeReader.readRawDelta(*this, value, reference); + TypeStreamerPointer typeStreamer; + _typeStreamerStreamer >> typeStreamer; + typeStreamer->readRawDelta(*this, value, reference); } void Bitstream::writeRawDelta(const QObject* value, const QObject* reference) { @@ -767,12 +767,12 @@ Bitstream& Bitstream::operator<<(const QVariant& value) { } Bitstream& Bitstream::operator>>(QVariant& value) { - TypeReader reader; - _typeStreamerStreamer >> reader; - if (reader.getTypeName().isEmpty()) { + TypeStreamerPointer streamer; + _typeStreamerStreamer >> streamer; + if (!streamer) { value = QVariant(); } else { - value = reader.read(*this); + value = streamer->read(*this); } return *this; } @@ -853,14 +853,14 @@ Bitstream& Bitstream::operator<<(const TypeStreamer* streamer) { } Bitstream& Bitstream::operator>>(const TypeStreamer*& streamer) { - TypeReader typeReader; - _typeStreamerStreamer >> typeReader; - streamer = typeReader.getStreamer(); + TypeStreamerPointer typeStreamer; + _typeStreamerStreamer >> typeStreamer; + streamer = typeStreamer.data(); return *this; } -Bitstream& Bitstream::operator>>(TypeReader& reader) { - _typeStreamerStreamer >> reader; +Bitstream& Bitstream::operator>>(TypeStreamerPointer& streamer) { + _typeStreamerStreamer >> streamer; return *this; } @@ -1122,8 +1122,8 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { *this >> storedPropertyCount; PropertyReaderVector properties(storedPropertyCount); for (int i = 0; i < storedPropertyCount; i++) { - TypeReader typeReader; - *this >> typeReader; + TypeStreamerPointer typeStreamer; + *this >> typeStreamer; QMetaProperty property = QMetaProperty(); if (_metadataType == FULL_METADATA) { QByteArray propertyName; @@ -1132,7 +1132,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { property = metaObject->property(metaObject->indexOfProperty(propertyName)); } } - properties[i] = PropertyReader(typeReader, property); + properties[i] = PropertyReader(typeStreamer, property); } // for hash metadata, check the names/types of the properties as well as the name hash against our own class if (_metadataType == HASH_METADATA) { @@ -1143,7 +1143,7 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { if (propertyWriters.size() == properties.size()) { for (int i = 0; i < propertyWriters.size(); i++) { const PropertyWriter& propertyWriter = propertyWriters.at(i); - if (!properties.at(i).getReader().matchesExactly(propertyWriter.getStreamer())) { + if (properties.at(i).getStreamer() != propertyWriter.getStreamer()) { matches = false; break; } @@ -1176,13 +1176,13 @@ Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { if (_metadataType == NO_METADATA) { return *this; } - TypeReader::Type type = streamer->getReaderType(); - *this << (int)type; - switch (type) { - case TypeReader::SIMPLE_TYPE: + TypeStreamer::Category category = streamer->getCategory(); + *this << (int)category; + switch (category) { + case TypeStreamer::SIMPLE_CATEGORY: return *this; - case TypeReader::ENUM_TYPE: { + case TypeStreamer::ENUM_CATEGORY: { QMetaEnum metaEnum = streamer->getMetaEnum(); if (_metadataType == FULL_METADATA) { *this << metaEnum.keyCount(); @@ -1203,11 +1203,11 @@ Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { } return *this; } - case TypeReader::LIST_TYPE: - case TypeReader::SET_TYPE: + case TypeStreamer::LIST_CATEGORY: + case TypeStreamer::SET_CATEGORY: return *this << streamer->getValueStreamer(); - case TypeReader::MAP_TYPE: + case TypeStreamer::MAP_CATEGORY: return *this << streamer->getKeyStreamer() << streamer->getValueStreamer(); default: @@ -1235,46 +1235,50 @@ Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { return *this; } -Bitstream& Bitstream::operator>(TypeReader& reader) { +Bitstream& Bitstream::operator>(TypeStreamerPointer& streamer) { QByteArray typeName; *this >> typeName; if (typeName.isEmpty()) { - reader = TypeReader(); + streamer = TypeStreamerPointer(); return *this; } - const TypeStreamer* streamer = _typeStreamerSubstitutions.value(typeName); - if (!streamer) { - streamer = getTypeStreamers().value(QMetaType::type(typeName.constData())); - if (!streamer) { - streamer = getEnumStreamersByName().value(typeName); + const TypeStreamer* baseStreamer = _typeStreamerSubstitutions.value(typeName); + if (!baseStreamer) { + baseStreamer = getTypeStreamers().value(QMetaType::type(typeName.constData())); + if (!baseStreamer) { + baseStreamer = getEnumStreamersByName().value(typeName); } } + // start out with the base, if any + if (baseStreamer) { + streamer = baseStreamer->getSelf(); + } else { + streamer = TypeStreamerPointer(); + } if (_metadataType == NO_METADATA) { - if (!streamer) { + if (!baseStreamer) { qWarning() << "Unknown type name:" << typeName; } - reader = TypeReader(typeName, streamer); return *this; } - int type; - *this >> type; - if (type == TypeReader::SIMPLE_TYPE) { + int category; + *this >> category; + if (category == TypeStreamer::SIMPLE_CATEGORY) { if (!streamer) { qWarning() << "Unknown type name:" << typeName; } - reader = TypeReader(typeName, streamer); return *this; } if (_genericsMode == ALL_GENERICS) { - streamer = NULL; + baseStreamer = NULL; } - switch (type) { - case TypeReader::ENUM_TYPE: { + switch (category) { + case TypeStreamer::ENUM_CATEGORY: { if (_metadataType == FULL_METADATA) { int keyCount; *this >> keyCount; - QMetaEnum metaEnum = (streamer && streamer->getReaderType() == TypeReader::ENUM_TYPE) ? - streamer->getMetaEnum() : QMetaEnum(); + QMetaEnum metaEnum = (baseStreamer && baseStreamer->getCategory() == TypeStreamer::ENUM_CATEGORY) ? + baseStreamer->getMetaEnum() : QMetaEnum(); QHash mappings; bool matches = (keyCount == metaEnum.keyCount()); int highestValue = 0; @@ -1289,17 +1293,16 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { } matches &= (value == localValue); } - if (matches) { - reader = TypeReader(typeName, streamer); - } else { - reader = TypeReader(typeName, streamer, getBitsForHighestValue(highestValue), mappings); + if (!matches) { + streamer = TypeStreamerPointer(new MappedEnumTypeStreamer(baseStreamer, + getBitsForHighestValue(highestValue), mappings)); } } else { int bits; *this >> bits; QCryptographicHash hash(QCryptographicHash::Md5); - if (streamer && streamer->getReaderType() == TypeReader::ENUM_TYPE) { - QMetaEnum metaEnum = streamer->getMetaEnum(); + if (baseStreamer && baseStreamer->getCategory() == TypeStreamer::ENUM_CATEGORY) { + QMetaEnum metaEnum = baseStreamer->getMetaEnum(); for (int i = 0; i < metaEnum.keyCount(); i++) { hash.addData(metaEnum.key(i), strlen(metaEnum.key(i)) + 1); qint32 value = metaEnum.value(i); @@ -1309,37 +1312,30 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { QByteArray localHashResult = hash.result(); QByteArray remoteHashResult(localHashResult.size(), 0); read(remoteHashResult.data(), remoteHashResult.size() * BITS_IN_BYTE); - if (localHashResult == remoteHashResult) { - reader = TypeReader(typeName, streamer); - } else { - reader = TypeReader(typeName, streamer, bits, QHash()); + if (localHashResult != remoteHashResult) { + streamer = TypeStreamerPointer(new MappedEnumTypeStreamer(baseStreamer, bits, QHash())); } } return *this; } - case TypeReader::LIST_TYPE: - case TypeReader::SET_TYPE: { - TypeReader valueReader; - *this >> valueReader; - if (streamer && streamer->getReaderType() == type && - valueReader.matchesExactly(streamer->getValueStreamer())) { - reader = TypeReader(typeName, streamer); - } else { - reader = TypeReader(typeName, streamer, (TypeReader::Type)type, - TypeReaderPointer(new TypeReader(valueReader))); + case TypeStreamer::LIST_CATEGORY: + case TypeStreamer::SET_CATEGORY: { + TypeStreamerPointer valueStreamer; + *this >> valueStreamer; + if (!(baseStreamer && baseStreamer->getCategory() == category && + valueStreamer == baseStreamer->getValueStreamer())) { + streamer = TypeStreamerPointer(category == TypeStreamer::LIST_CATEGORY ? + new MappedListTypeStreamer(baseStreamer, valueStreamer) : + new MappedSetTypeStreamer(baseStreamer, valueStreamer)); } return *this; } - case TypeReader::MAP_TYPE: { - TypeReader keyReader, valueReader; - *this >> keyReader >> valueReader; - if (streamer && streamer->getReaderType() == TypeReader::MAP_TYPE && - keyReader.matchesExactly(streamer->getKeyStreamer()) && - valueReader.matchesExactly(streamer->getValueStreamer())) { - reader = TypeReader(typeName, streamer); - } else { - reader = TypeReader(typeName, streamer, TypeReaderPointer(new TypeReader(keyReader)), - TypeReaderPointer(new TypeReader(valueReader))); + case TypeStreamer::MAP_CATEGORY: { + TypeStreamerPointer keyStreamer, valueStreamer; + *this >> keyStreamer >> valueStreamer; + if (!(baseStreamer && baseStreamer->getCategory() == TypeStreamer::MAP_CATEGORY && + keyStreamer == baseStreamer->getKeyStreamer() && valueStreamer == baseStreamer->getValueStreamer())) { + streamer = TypeStreamerPointer(new MappedMapTypeStreamer(baseStreamer, keyStreamer, valueStreamer)); } return *this; } @@ -1347,37 +1343,36 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { // streamable type int fieldCount; *this >> fieldCount; - QVector fields(fieldCount); + QVector fields(fieldCount); for (int i = 0; i < fieldCount; i++) { - TypeReader typeReader; - *this >> typeReader; + TypeStreamerPointer typeStreamer; + *this >> typeStreamer; int index = -1; if (_metadataType == FULL_METADATA) { QByteArray fieldName; *this >> fieldName; - if (streamer) { - index = streamer->getFieldIndex(fieldName); + if (baseStreamer) { + index = baseStreamer->getFieldIndex(fieldName); } } - fields[i] = FieldReader(typeReader, index); + fields[i] = StreamerIndexPair(typeStreamer, index); } // for hash metadata, check the names/types of the fields as well as the name hash against our own class if (_metadataType == HASH_METADATA) { QCryptographicHash hash(QCryptographicHash::Md5); bool matches = true; - if (streamer) { - const QVector& localFields = streamer->getMetaFields(); + if (baseStreamer) { + const QVector& localFields = baseStreamer->getMetaFields(); if (fieldCount != localFields.size()) { matches = false; } else { if (fieldCount == 0) { - reader = TypeReader(typeName, streamer); return *this; } for (int i = 0; i < fieldCount; i++) { const MetaField& localField = localFields.at(i); - if (!fields.at(i).getReader().matchesExactly(localField.getStreamer())) { + if (fields.at(i).first != localField.getStreamer()) { matches = false; break; } @@ -1388,29 +1383,27 @@ Bitstream& Bitstream::operator>(TypeReader& reader) { QByteArray localHashResult = hash.result(); QByteArray remoteHashResult(localHashResult.size(), 0); read(remoteHashResult.data(), remoteHashResult.size() * BITS_IN_BYTE); - if (streamer && matches && localHashResult == remoteHashResult) { + if (baseStreamer && matches && localHashResult == remoteHashResult) { // since everything is the same, we can use the default streamer - reader = TypeReader(typeName, streamer); return *this; } - } else if (streamer) { + } else if (baseStreamer) { // if all fields are the same type and in the right order, we can use the (more efficient) default streamer - const QVector& localFields = streamer->getMetaFields(); + const QVector& localFields = baseStreamer->getMetaFields(); if (fieldCount != localFields.size()) { - reader = TypeReader(typeName, streamer, fields); + streamer = TypeStreamerPointer(new MappedStreamableTypeStreamer(baseStreamer, fields)); return *this; } for (int i = 0; i < fieldCount; i++) { - const FieldReader& fieldReader = fields.at(i); - if (!fieldReader.getReader().matchesExactly(localFields.at(i).getStreamer()) || fieldReader.getIndex() != i) { - reader = TypeReader(typeName, streamer, fields); + const StreamerIndexPair& field = fields.at(i); + if (field.first != localFields.at(i).getStreamer() || field.second != i) { + streamer = TypeStreamerPointer(new MappedStreamableTypeStreamer(baseStreamer, fields)); return *this; } } - reader = TypeReader(typeName, streamer); return *this; } - reader = TypeReader(typeName, streamer, fields); + streamer = TypeStreamerPointer(new MappedStreamableTypeStreamer(baseStreamer, fields)); return *this; } @@ -1575,7 +1568,7 @@ QHash Bitstream::createPropertyReaders streamer = getTypeStreamers().value(property.userType()); } if (streamer) { - readers.append(PropertyReader(TypeReader(QByteArray(), streamer), property)); + readers.append(PropertyReader(streamer->getSelf(), property)); } } } @@ -1613,245 +1606,6 @@ QHash Bitstream::createPropertyWriters return propertyWriters; } -TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer) : - _typeName(typeName), - _streamer(streamer), - _exactMatch(true) { -} - -TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, int bits, const QHash& mappings) : - _typeName(typeName), - _streamer(streamer), - _exactMatch(false), - _type(ENUM_TYPE), - _bits(bits), - _mappings(mappings) { -} - -TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, const QVector& fields) : - _typeName(typeName), - _streamer(streamer), - _exactMatch(false), - _type(STREAMABLE_TYPE), - _fields(fields) { -} - -TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, - Type type, const TypeReaderPointer& valueReader) : - _typeName(typeName), - _streamer(streamer), - _exactMatch(false), - _type(type), - _valueReader(valueReader) { -} - -TypeReader::TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, - const TypeReaderPointer& keyReader, const TypeReaderPointer& valueReader) : - _typeName(typeName), - _streamer(streamer), - _exactMatch(false), - _type(MAP_TYPE), - _keyReader(keyReader), - _valueReader(valueReader) { -} - -QVariant TypeReader::read(Bitstream& in) const { - if (_exactMatch) { - return _streamer->read(in); - } - QVariant object = _streamer ? QVariant(_streamer->getType(), 0) : QVariant(); - switch (_type) { - case ENUM_TYPE: { - int value = 0; - in.read(&value, _bits); - if (_streamer) { - _streamer->setEnumValue(object, value, _mappings); - } - break; - } - case STREAMABLE_TYPE: { - foreach (const FieldReader& field, _fields) { - field.read(in, _streamer, object); - } - break; - } - case LIST_TYPE: - case SET_TYPE: { - int size; - in >> size; - for (int i = 0; i < size; i++) { - QVariant value = _valueReader->read(in); - if (_streamer) { - _streamer->insert(object, value); - } - } - break; - } - case MAP_TYPE: { - int size; - in >> size; - for (int i = 0; i < size; i++) { - QVariant key = _keyReader->read(in); - QVariant value = _valueReader->read(in); - if (_streamer) { - _streamer->insert(object, key, value); - } - } - break; - } - default: - break; - } - return object; -} - -void TypeReader::readDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { - if (_exactMatch) { - _streamer->readDelta(in, object, reference); - return; - } - bool changed; - in >> changed; - if (changed) { - readRawDelta(in, object, reference); - } else { - object = reference; - } -} - -void TypeReader::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { - if (_exactMatch) { - _streamer->readRawDelta(in, object, reference); - return; - } - switch (_type) { - case ENUM_TYPE: { - int value = 0; - in.read(&value, _bits); - if (_streamer) { - _streamer->setEnumValue(object, value, _mappings); - } - break; - } - case STREAMABLE_TYPE: { - foreach (const FieldReader& field, _fields) { - field.readDelta(in, _streamer, object, reference); - } - break; - } - case LIST_TYPE: { - object = reference; - int size, referenceSize; - in >> size >> referenceSize; - if (_streamer) { - if (size < referenceSize) { - _streamer->prune(object, size); - } - for (int i = 0; i < size; i++) { - if (i < referenceSize) { - QVariant value; - _valueReader->readDelta(in, value, _streamer->getValue(reference, i)); - _streamer->setValue(object, i, value); - } else { - _streamer->insert(object, _valueReader->read(in)); - } - } - } else { - for (int i = 0; i < size; i++) { - if (i < referenceSize) { - QVariant value; - _valueReader->readDelta(in, value, QVariant()); - } else { - _valueReader->read(in); - } - } - } - break; - } - case SET_TYPE: { - object = reference; - int addedOrRemoved; - in >> addedOrRemoved; - for (int i = 0; i < addedOrRemoved; i++) { - QVariant value = _valueReader->read(in); - if (_streamer && !_streamer->remove(object, value)) { - _streamer->insert(object, value); - } - } - break; - } - case MAP_TYPE: { - object = reference; - int added; - in >> added; - for (int i = 0; i < added; i++) { - QVariant key = _keyReader->read(in); - QVariant value = _valueReader->read(in); - if (_streamer) { - _streamer->insert(object, key, value); - } - } - int modified; - in >> modified; - for (int i = 0; i < modified; i++) { - QVariant key = _keyReader->read(in); - QVariant value; - if (_streamer) { - _valueReader->readDelta(in, value, _streamer->getValue(reference, key)); - _streamer->insert(object, key, value); - } else { - _valueReader->readDelta(in, value, QVariant()); - } - } - int removed; - in >> removed; - for (int i = 0; i < removed; i++) { - QVariant key = _keyReader->read(in); - if (_streamer) { - _streamer->remove(object, key); - } - } - break; - } - default: - break; - } -} - -bool TypeReader::matchesExactly(const TypeStreamer* streamer) const { - return _exactMatch && _streamer == streamer; -} - -uint qHash(const TypeReader& typeReader, uint seed) { - return qHash(typeReader.getTypeName(), seed); -} - -QDebug& operator<<(QDebug& debug, const TypeReader& typeReader) { - return debug << typeReader.getTypeName(); -} - -FieldReader::FieldReader(const TypeReader& reader, int index) : - _reader(reader), - _index(index) { -} - -void FieldReader::read(Bitstream& in, const TypeStreamer* streamer, QVariant& object) const { - QVariant value = _reader.read(in); - if (_index != -1 && streamer) { - streamer->setField(object, _index, value); - } -} - -void FieldReader::readDelta(Bitstream& in, const TypeStreamer* streamer, QVariant& object, const QVariant& reference) const { - QVariant value; - if (_index != -1 && streamer) { - _reader.readDelta(in, value, streamer->getField(reference, _index)); - streamer->setField(object, _index, value); - } else { - _reader.readDelta(in, value, QVariant()); - } -} - ObjectReader::ObjectReader(const QByteArray& className, const QMetaObject* metaObject, const PropertyReaderVector& properties) : _className(className), @@ -1887,13 +1641,13 @@ QDebug& operator<<(QDebug& debug, const ObjectReader& objectReader) { return debug << objectReader.getClassName(); } -PropertyReader::PropertyReader(const TypeReader& reader, const QMetaProperty& property) : - _reader(reader), +PropertyReader::PropertyReader(const TypeStreamerPointer& streamer, const QMetaProperty& property) : + _streamer(streamer), _property(property) { } void PropertyReader::read(Bitstream& in, QObject* object) const { - QVariant value = _reader.read(in); + QVariant value = _streamer->read(in); if (_property.isValid() && object) { _property.write(object, value); } @@ -1901,7 +1655,7 @@ void PropertyReader::read(Bitstream& in, QObject* object) const { void PropertyReader::readDelta(Bitstream& in, QObject* object, const QObject* reference) const { QVariant value; - _reader.readDelta(in, value, (_property.isValid() && reference) ? _property.read(reference) : QVariant()); + _streamer->readDelta(in, value, (_property.isValid() && reference) ? _property.read(reference) : QVariant()); if (_property.isValid() && object) { _property.write(object, value); } @@ -1997,8 +1751,8 @@ QVariant TypeStreamer::getField(const QVariant& object, int index) const { return QVariant(); } -TypeReader::Type TypeStreamer::getReaderType() const { - return TypeReader::SIMPLE_TYPE; +TypeStreamer::Category TypeStreamer::getCategory() const { + return SIMPLE_CATEGORY; } int TypeStreamer::getBits() const { @@ -2076,8 +1830,8 @@ const char* EnumTypeStreamer::getName() const { return _name.constData(); } -TypeReader::Type EnumTypeStreamer::getReaderType() const { - return TypeReader::ENUM_TYPE; +TypeStreamer::Category EnumTypeStreamer::getCategory() const { + return ENUM_CATEGORY; } int EnumTypeStreamer::getBits() const { @@ -2199,6 +1953,35 @@ const TypeStreamer* GenericTypeStreamer::getStreamerToWrite(const QVariant& valu return value.value().getStreamer().data(); } +MappedStreamableTypeStreamer::MappedStreamableTypeStreamer(const TypeStreamer* baseStreamer, + const QVector& fields) : + _baseStreamer(baseStreamer), + _fields(fields) { +} + +QVariant MappedStreamableTypeStreamer::read(Bitstream& in) const { + QVariant object = _baseStreamer ? QVariant(_baseStreamer->getType(), 0) : QVariant(); + foreach (const StreamerIndexPair& pair, _fields) { + QVariant value = pair.first->read(in); + if (pair.second != -1 && _baseStreamer) { + _baseStreamer->setField(object, pair.second, value); + } + } + return object; +} + +void MappedStreamableTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { + foreach (const StreamerIndexPair& pair, _fields) { + QVariant value; + if (pair.second != -1 && _baseStreamer) { + pair.first->readDelta(in, value, _baseStreamer->getField(reference, pair.second)); + _baseStreamer->setField(object, pair.second, value); + } else { + pair.first->readDelta(in, value, QVariant()); + } + } +} + MappedListTypeStreamer::MappedListTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& valueStreamer) : _baseStreamer(baseStreamer), _valueStreamer(valueStreamer) { diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index cd7a0439aa..08b326d687 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -35,13 +35,11 @@ class QUrl; class Attribute; class AttributeValue; class Bitstream; -class FieldReader; class GenericValue; class ObjectReader; class OwnedAttributeValue; class PropertyReader; class PropertyWriter; -class TypeReader; class TypeStreamer; typedef SharedObjectPointerTemplate AttributePointer; @@ -209,7 +207,7 @@ public: class ReadMappings { public: QHash metaObjectValues; - QHash typeStreamerValues; + QHash typeStreamerValues; QHash attributeValues; QHash scriptStringValues; QHash sharedObjectValues; @@ -399,7 +397,7 @@ public: Bitstream& operator<<(const TypeStreamer* streamer); Bitstream& operator>>(const TypeStreamer*& streamer); - Bitstream& operator>>(TypeReader& reader); + Bitstream& operator>>(TypeStreamerPointer& streamer); Bitstream& operator<<(const AttributePointer& attribute); Bitstream& operator>>(AttributePointer& attribute); @@ -417,7 +415,7 @@ public: Bitstream& operator>(ObjectReader& objectReader); Bitstream& operator<(const TypeStreamer* streamer); - Bitstream& operator>(TypeReader& reader); + Bitstream& operator>(TypeStreamerPointer& reader); Bitstream& operator<(const AttributePointer& attribute); Bitstream& operator>(AttributePointer& attribute); @@ -446,7 +444,7 @@ private: GenericsMode _genericsMode; RepeatedValueStreamer _metaObjectStreamer; - RepeatedValueStreamer _typeStreamerStreamer; + RepeatedValueStreamer _typeStreamerStreamer; RepeatedValueStreamer _attributeStreamer; RepeatedValueStreamer _scriptStringStreamer; RepeatedValueStreamer _sharedObjectStreamer; @@ -756,73 +754,6 @@ template inline Bitstream& Bitstream::operator>>(QHash& return *this; } -typedef QSharedPointer TypeReaderPointer; - -/// Contains the information required to read a type from the stream. -class TypeReader { -public: - - enum Type { SIMPLE_TYPE, ENUM_TYPE, STREAMABLE_TYPE, LIST_TYPE, SET_TYPE, MAP_TYPE }; - - TypeReader(const QByteArray& typeName = QByteArray(), const TypeStreamer* streamer = NULL); - - TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, int bits, const QHash& mappings); - - TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, const QVector& fields); - - TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, Type type, - const TypeReaderPointer& valueReader); - - TypeReader(const QByteArray& typeName, const TypeStreamer* streamer, - const TypeReaderPointer& keyReader, const TypeReaderPointer& valueReader); - - const QByteArray& getTypeName() const { return _typeName; } - const TypeStreamer* getStreamer() const { return _streamer; } - - QVariant read(Bitstream& in) const; - void readDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; - void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; - - bool matchesExactly(const TypeStreamer* streamer) const; - - bool operator==(const TypeReader& other) const { return _typeName == other._typeName; } - bool operator!=(const TypeReader& other) const { return _typeName != other._typeName; } - -private: - - QByteArray _typeName; - const TypeStreamer* _streamer; - bool _exactMatch; - Type _type; - int _bits; - QHash _mappings; - TypeReaderPointer _keyReader; - TypeReaderPointer _valueReader; - QVector _fields; -}; - -uint qHash(const TypeReader& typeReader, uint seed = 0); - -QDebug& operator<<(QDebug& debug, const TypeReader& typeReader); - -/// Contains the information required to read a metatype field from the stream and apply it. -class FieldReader { -public: - - FieldReader(const TypeReader& reader = TypeReader(), int index = -1); - - const TypeReader& getReader() const { return _reader; } - int getIndex() const { return _index; } - - void read(Bitstream& in, const TypeStreamer* streamer, QVariant& object) const; - void readDelta(Bitstream& in, const TypeStreamer* streamer, QVariant& object, const QVariant& reference) const; - -private: - - TypeReader _reader; - int _index; -}; - /// Contains the information required to read an object from the stream. class ObjectReader { public: @@ -854,16 +785,17 @@ QDebug& operator<<(QDebug& debug, const ObjectReader& objectReader); class PropertyReader { public: - PropertyReader(const TypeReader& reader = TypeReader(), const QMetaProperty& property = QMetaProperty()); + PropertyReader(const TypeStreamerPointer& streamer = TypeStreamerPointer(), + const QMetaProperty& property = QMetaProperty()); - const TypeReader& getReader() const { return _reader; } + const TypeStreamerPointer& getStreamer() const { return _streamer; } void read(Bitstream& in, QObject* object) const; void readDelta(Bitstream& in, QObject* object, const QObject* reference) const; private: - TypeReader _reader; + TypeStreamerPointer _streamer; QMetaProperty _property; }; @@ -909,6 +841,8 @@ Q_DECLARE_METATYPE(const QMetaObject*) class TypeStreamer { public: + enum Category { SIMPLE_CATEGORY, ENUM_CATEGORY, STREAMABLE_CATEGORY, LIST_CATEGORY, SET_CATEGORY, MAP_CATEGORY }; + virtual ~TypeStreamer(); int getType() const { return _type; } @@ -937,7 +871,7 @@ public: virtual void setField(QVariant& object, int index, const QVariant& value) const; virtual QVariant getField(const QVariant& object, int index) const; - virtual TypeReader::Type getReaderType() const; + virtual Category getCategory() const; virtual int getBits() const; virtual QMetaEnum getMetaEnum() const; @@ -992,7 +926,7 @@ public: EnumTypeStreamer(const QMetaEnum& metaEnum); virtual const char* getName() const; - virtual TypeReader::Type getReaderType() const; + virtual Category getCategory() const; virtual int getBits() const; virtual QMetaEnum getMetaEnum() const; virtual bool equal(const QVariant& first, const QVariant& second) const; @@ -1059,7 +993,7 @@ public: template class StreamableTypeStreamer : public SimpleTypeStreamer { public: - virtual TypeReader::Type getReaderType() const { return TypeReader::STREAMABLE_TYPE; } + virtual TypeStreamer::Category getCategory() const { return TypeStreamer::STREAMABLE_CATEGORY; } virtual const QVector& getMetaFields() const { return T::getMetaFields(); } virtual int getFieldIndex(const QByteArray& name) const { return T::getFieldIndex(name); } virtual void setField(QVariant& object, int index, const QVariant& value) const { @@ -1068,6 +1002,23 @@ public: return static_cast(object.constData())->getField(index); } }; +typedef QPair StreamerIndexPair; + +/// A streamer class for streamables that maps to a local type. +class MappedStreamableTypeStreamer : public TypeStreamer { +public: + + MappedStreamableTypeStreamer(const TypeStreamer* baseStreamer, const QVector& fields); + + virtual QVariant read(Bitstream& in) const; + virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; + +private: + + const TypeStreamer* _baseStreamer; + QVector _fields; +}; + /// Base template for collection streamers. template class CollectionTypeStreamer : public SimpleTypeStreamer { }; @@ -1076,7 +1027,7 @@ template class CollectionTypeStreamer : public SimpleTypeStreamer { template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: - virtual TypeReader::Type getReaderType() const { return TypeReader::LIST_TYPE; } + virtual TypeStreamer::Category getCategory() const { return TypeStreamer::LIST_CATEGORY; } virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } virtual void insert(QVariant& object, const QVariant& value) const { static_cast*>(object.data())->append(value.value()); } @@ -1092,7 +1043,7 @@ public: template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: - virtual TypeReader::Type getReaderType() const { return TypeReader::LIST_TYPE; } + virtual TypeStreamer::Category getCategory() const { return TypeStreamer::LIST_CATEGORY; } virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } virtual void insert(QVariant& object, const QVariant& value) const { static_cast*>(object.data())->append(value.value()); } @@ -1123,7 +1074,7 @@ protected: template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: - virtual TypeReader::Type getReaderType() const { return TypeReader::SET_TYPE; } + virtual TypeStreamer::Category getCategory() const { return TypeStreamer::SET_CATEGORY; } virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } virtual void insert(QVariant& object, const QVariant& value) const { static_cast*>(object.data())->insert(value.value()); } @@ -1144,7 +1095,7 @@ public: template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: - virtual TypeReader::Type getReaderType() const { return TypeReader::MAP_TYPE; } + virtual TypeStreamer::Category getCategory() const { return TypeStreamer::MAP_CATEGORY; } virtual const TypeStreamer* getKeyStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } virtual void insert(QVariant& object, const QVariant& key, const QVariant& value) const { From c045595ccbbb6228ca5287751c06198be41d0037 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 11 Jun 2014 14:25:45 -0700 Subject: [PATCH 019/102] More progress on refactoring. --- libraries/metavoxels/src/Bitstream.cpp | 93 +++++++++++--------------- libraries/metavoxels/src/Bitstream.h | 3 + 2 files changed, 42 insertions(+), 54 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index ce46429393..a6d5967fd3 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -1269,8 +1269,8 @@ Bitstream& Bitstream::operator>(TypeStreamerPointer& streamer) { } return *this; } - if (_genericsMode == ALL_GENERICS) { - baseStreamer = NULL; + if (!baseStreamer) { + baseStreamer = getInvalidTypeStreamer(); } switch (category) { case TypeStreamer::ENUM_CATEGORY: { @@ -1544,6 +1544,18 @@ QHash Bitstream::createEnumStreamersByName() { return enumStreamersByName; } +const TypeStreamer* Bitstream::getInvalidTypeStreamer() { + const TypeStreamer* streamer = createInvalidTypeStreamer(); + return streamer; +} + +const TypeStreamer* Bitstream::createInvalidTypeStreamer() { + TypeStreamer* streamer = new TypeStreamer(); + streamer->_type = QMetaType::UnknownType; + streamer->_self = TypeStreamerPointer(streamer); + return streamer; +} + const QHash& Bitstream::getPropertyReaders() { static QHash propertyReaders = createPropertyReaders(); return propertyReaders; @@ -1923,21 +1935,17 @@ MappedEnumTypeStreamer::MappedEnumTypeStreamer(const TypeStreamer* baseStreamer, } QVariant MappedEnumTypeStreamer::read(Bitstream& in) const { - QVariant object = _baseStreamer ? QVariant(_baseStreamer->getType(), 0) : QVariant(); + QVariant object = QVariant(_baseStreamer->getType(), 0); int value = 0; in.read(&value, _bits); - if (_baseStreamer) { - _baseStreamer->setEnumValue(object, value, _mappings); - } + _baseStreamer->setEnumValue(object, value, _mappings); return object; } void MappedEnumTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { int value = 0; in.read(&value, _bits); - if (_baseStreamer) { - _baseStreamer->setEnumValue(object, value, _mappings); - } + _baseStreamer->setEnumValue(object, value, _mappings); } GenericValue::GenericValue(const TypeStreamerPointer& streamer, const QVariant& value) : @@ -1960,10 +1968,10 @@ MappedStreamableTypeStreamer::MappedStreamableTypeStreamer(const TypeStreamer* b } QVariant MappedStreamableTypeStreamer::read(Bitstream& in) const { - QVariant object = _baseStreamer ? QVariant(_baseStreamer->getType(), 0) : QVariant(); + QVariant object = QVariant(_baseStreamer->getType(), 0); foreach (const StreamerIndexPair& pair, _fields) { QVariant value = pair.first->read(in); - if (pair.second != -1 && _baseStreamer) { + if (pair.second != -1) { _baseStreamer->setField(object, pair.second, value); } } @@ -1973,7 +1981,7 @@ QVariant MappedStreamableTypeStreamer::read(Bitstream& in) const { void MappedStreamableTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const { foreach (const StreamerIndexPair& pair, _fields) { QVariant value; - if (pair.second != -1 && _baseStreamer) { + if (pair.second != -1) { pair.first->readDelta(in, value, _baseStreamer->getField(reference, pair.second)); _baseStreamer->setField(object, pair.second, value); } else { @@ -1988,14 +1996,12 @@ MappedListTypeStreamer::MappedListTypeStreamer(const TypeStreamer* baseStreamer, } QVariant MappedListTypeStreamer::read(Bitstream& in) const { - QVariant object = _baseStreamer ? QVariant(_baseStreamer->getType(), 0) : QVariant(); + QVariant object = QVariant(_baseStreamer->getType(), 0); int size; in >> size; for (int i = 0; i < size; i++) { QVariant value = _valueStreamer->read(in); - if (_baseStreamer) { - _baseStreamer->insert(object, value); - } + _baseStreamer->insert(object, value); } return object; } @@ -2004,27 +2010,16 @@ void MappedListTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const object = reference; int size, referenceSize; in >> size >> referenceSize; - if (_baseStreamer) { - if (size < referenceSize) { - _baseStreamer->prune(object, size); - } - for (int i = 0; i < size; i++) { - if (i < referenceSize) { - QVariant value; - _valueStreamer->readDelta(in, value, _baseStreamer->getValue(reference, i)); - _baseStreamer->setValue(object, i, value); - } else { - _baseStreamer->insert(object, _valueStreamer->read(in)); - } - } - } else { - for (int i = 0; i < size; i++) { - if (i < referenceSize) { - QVariant value; - _valueStreamer->readDelta(in, value, QVariant()); - } else { - _valueStreamer->read(in); - } + if (size < referenceSize) { + _baseStreamer->prune(object, size); + } + for (int i = 0; i < size; i++) { + if (i < referenceSize) { + QVariant value; + _valueStreamer->readDelta(in, value, _baseStreamer->getValue(reference, i)); + _baseStreamer->setValue(object, i, value); + } else { + _baseStreamer->insert(object, _valueStreamer->read(in)); } } } @@ -2039,7 +2034,7 @@ void MappedSetTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const in >> addedOrRemoved; for (int i = 0; i < addedOrRemoved; i++) { QVariant value = _valueStreamer->read(in); - if (_baseStreamer && !_baseStreamer->remove(object, value)) { + if (!_baseStreamer->remove(object, value)) { _baseStreamer->insert(object, value); } } @@ -2053,15 +2048,13 @@ MappedMapTypeStreamer::MappedMapTypeStreamer(const TypeStreamer* baseStreamer, c } QVariant MappedMapTypeStreamer::read(Bitstream& in) const { - QVariant object = _baseStreamer ? QVariant(_baseStreamer->getType(), 0) : QVariant(); + QVariant object = QVariant(_baseStreamer->getType(), 0); int size; in >> size; for (int i = 0; i < size; i++) { QVariant key = _keyStreamer->read(in); QVariant value = _valueStreamer->read(in); - if (_baseStreamer) { - _baseStreamer->insert(object, key, value); - } + _baseStreamer->insert(object, key, value); } return object; } @@ -2073,29 +2066,21 @@ void MappedMapTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const for (int i = 0; i < added; i++) { QVariant key = _keyStreamer->read(in); QVariant value = _valueStreamer->read(in); - if (_baseStreamer) { - _baseStreamer->insert(object, key, value); - } + _baseStreamer->insert(object, key, value); } int modified; in >> modified; for (int i = 0; i < modified; i++) { QVariant key = _keyStreamer->read(in); QVariant value; - if (_baseStreamer) { - _valueStreamer->readDelta(in, value, _baseStreamer->getValue(reference, key)); - _baseStreamer->insert(object, key, value); - } else { - _valueStreamer->readDelta(in, value, QVariant()); - } + _valueStreamer->readDelta(in, value, _baseStreamer->getValue(reference, key)); + _baseStreamer->insert(object, key, value); } int removed; in >> removed; for (int i = 0; i < removed; i++) { QVariant key = _keyStreamer->read(in); - if (_baseStreamer) { - _baseStreamer->remove(object, key); - } + _baseStreamer->remove(object, key); } } diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 08b326d687..434b14ce69 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -466,6 +466,9 @@ private: static const QHash& getEnumStreamersByName(); static QHash createEnumStreamersByName(); + static const TypeStreamer* getInvalidTypeStreamer(); + static const TypeStreamer* createInvalidTypeStreamer(); + static const QHash& getPropertyReaders(); static QHash createPropertyReaders(); From 82bf6002e2c3c0aaa471b8f7f5233e645cfe5174 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 11 Jun 2014 14:57:05 -0700 Subject: [PATCH 020/102] only display the OAuthWebViewHandler when we need to ask user for authorization --- interface/src/Menu.cpp | 9 ++++++--- interface/src/Menu.h | 2 ++ interface/src/ui/OAuthWebViewHandler.cpp | 17 ++++++++++++++++- interface/src/ui/OAuthWebViewHandler.h | 1 + 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f791d20588..c2b6c293b9 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -103,6 +103,7 @@ Menu::Menu() : _fastFPSAverage(ONE_SECOND_OF_FRAMES), _loginAction(NULL), _preferencesDialog(NULL), + _loginDialog(NULL), _snapshotsLocation() { Application *appInstance = Application::getInstance(); @@ -913,9 +914,11 @@ void sendFakeEnterEvent() { const float DIALOG_RATIO_OF_WINDOW = 0.30f; void Menu::loginForCurrentDomain() { - LoginDialog* loginDialog = new LoginDialog(Application::getInstance()->getWindow()); - loginDialog->show(); - loginDialog->resizeAndPosition(false); + if (!_loginDialog) { + _loginDialog = new LoginDialog(Application::getInstance()->getWindow()); + _loginDialog->show(); + _loginDialog->resizeAndPosition(false); + } } void Menu::editPreferences() { diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 69015a938b..a0fafc0514 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -27,6 +27,7 @@ #include "ui/PreferencesDialog.h" #include "ui/ChatWindow.h" #include "ui/JSConsole.h" +#include "ui/LoginDialog.h" #include "ui/ScriptEditorWindow.h" const float ADJUST_LOD_DOWN_FPS = 40.0; @@ -270,6 +271,7 @@ private: QPointer _preferencesDialog; QPointer _attachmentsDialog; QPointer _animationsDialog; + QPointer _loginDialog; QAction* _chatAction; QString _snapshotsLocation; }; diff --git a/interface/src/ui/OAuthWebViewHandler.cpp b/interface/src/ui/OAuthWebViewHandler.cpp index 5415d5d9c3..5b4431bd0f 100644 --- a/interface/src/ui/OAuthWebViewHandler.cpp +++ b/interface/src/ui/OAuthWebViewHandler.cpp @@ -103,8 +103,9 @@ void OAuthWebViewHandler::displayWebviewForAuthorizationURL(const QUrl& authoriz codedAuthorizationURL.setQuery(authQuery); } + connect(_activeWebView.data(), &QWebView::urlChanged, this, &OAuthWebViewHandler::handleURLChanged); + _activeWebView->load(codedAuthorizationURL); - _activeWebView->show(); connect(_activeWebView->page()->networkAccessManager(), &QNetworkAccessManager::sslErrors, this, &OAuthWebViewHandler::handleSSLErrors); @@ -137,3 +138,17 @@ void OAuthWebViewHandler::handleLoadFinished(bool success) { void OAuthWebViewHandler::handleWebViewDestroyed(QObject* destroyedObject) { _webViewRedisplayTimer.restart(); } + +void OAuthWebViewHandler::handleURLChanged(const QUrl& url) { + // check if this is the authorization screen - if it is then we need to show the OAuthWebViewHandler + const QString ACCESS_TOKEN_URL_REGEX_STRING = "redirect_uri=[\\w:\\/\\.]+&access_token="; + QRegExp accessTokenRegex(ACCESS_TOKEN_URL_REGEX_STRING); + + if (accessTokenRegex.indexIn(url.toString()) != -1) { + _activeWebView->show(); + } else if (url.toString() == DEFAULT_NODE_AUTH_URL.toString() + "/login") { + // this is a login request - we're going to close the webview and signal the AccountManager that we need a login + _activeWebView->close(); + AccountManager::getInstance().checkAndSignalForAccessToken(); + } +} diff --git a/interface/src/ui/OAuthWebViewHandler.h b/interface/src/ui/OAuthWebViewHandler.h index 3a30705f88..8f0c01c90d 100644 --- a/interface/src/ui/OAuthWebViewHandler.h +++ b/interface/src/ui/OAuthWebViewHandler.h @@ -32,6 +32,7 @@ private slots: void handleSSLErrors(QNetworkReply* networkReply, const QList& errorList); void handleLoadFinished(bool success); void handleWebViewDestroyed(QObject* destroyedObject); + void handleURLChanged(const QUrl& url); private: QPointer _activeWebView; QElapsedTimer _webViewRedisplayTimer; From ac0b037f0f8398fc97efbe187dd50d845e3cad44 Mon Sep 17 00:00:00 2001 From: wangyix Date: Wed, 11 Jun 2014 15:25:00 -0700 Subject: [PATCH 021/102] removed connect(); put call into Application::nodeKilled OctreePacketReceiver::nodeKilled() doesn't get called when NodeList::nodeKilled() is emitted for some unknown reason --- interface/src/Application.cpp | 6 +++++- libraries/networking/src/LimitedNodeList.cpp | 1 + libraries/networking/src/ReceivedPacketProcessor.cpp | 6 +++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a5cb6a0c12..7a7d7c10f8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -244,7 +244,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer))); connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), &_voxels, SLOT(nodeAdded(SharedNodePointer))); connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_voxels, SLOT(nodeKilled(SharedNodePointer))); - connect(NodeList::getInstance(), &NodeList::nodeKilled, &_octreeProcessor, &ReceivedPacketProcessor::nodeKilled); connect(nodeList, &NodeList::uuidChanged, this, &Application::updateWindowTitle); connect(nodeList, SIGNAL(uuidChanged(const QUuid&)), _myAvatar, SLOT(setSessionUUID(const QUuid&))); connect(nodeList, &NodeList::limitOfSilentDomainCheckInsReached, nodeList, &NodeList::reset); @@ -3249,6 +3248,11 @@ void Application::nodeAdded(SharedNodePointer node) { } void Application::nodeKilled(SharedNodePointer node) { + + // this is here because connecting NodeList::nodeKilled to OctreePacketProcessor::nodeKilled doesn't work: + // OctreePacketProcessor::nodeKilled is not called when NodeList::nodeKilled is emitted for some reason. + _octreeProcessor.nodeKilled(node); + if (node->getType() == NodeType::VoxelServer) { QUuid nodeUUID = node->getUUID(); // see if this is the first we've heard of this node... diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index c0d7941edf..e1babb24ce 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -367,6 +367,7 @@ void LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) { NodeHash::iterator LimitedNodeList::killNodeAtHashIterator(NodeHash::iterator& nodeItemToKill) { qDebug() << "Killed" << *nodeItemToKill.value(); emit nodeKilled(nodeItemToKill.value()); +printf("\t\t\t emitting nodeKilled: %s\n", nodeItemToKill.value()->getUUID().toString().toLatin1().data()); return _nodeHash.erase(nodeItemToKill); } diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index 32fc4e26f3..28a9397c60 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -51,5 +51,9 @@ bool ReceivedPacketProcessor::process() { } void ReceivedPacketProcessor::nodeKilled(SharedNodePointer node) { - _nodePacketCounts.remove(node->getUUID()); + lock(); + int numRemoved = _nodePacketCounts.remove(node->getUUID()); + unlock(); + qDebug() << "RPP::killNode *************************************"; + printf("\t\t RPP::killNode: %s killed %d nodes\n", node->getUUID().toString().toLatin1().data(), numRemoved); } From 0601c24b2485370a8e01a7147f9a68ff09f42a23 Mon Sep 17 00:00:00 2001 From: wangyix Date: Wed, 11 Jun 2014 15:26:22 -0700 Subject: [PATCH 022/102] removed debug prints --- libraries/networking/src/LimitedNodeList.cpp | 1 - libraries/networking/src/ReceivedPacketProcessor.cpp | 2 -- 2 files changed, 3 deletions(-) diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index e1babb24ce..c0d7941edf 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -367,7 +367,6 @@ void LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) { NodeHash::iterator LimitedNodeList::killNodeAtHashIterator(NodeHash::iterator& nodeItemToKill) { qDebug() << "Killed" << *nodeItemToKill.value(); emit nodeKilled(nodeItemToKill.value()); -printf("\t\t\t emitting nodeKilled: %s\n", nodeItemToKill.value()->getUUID().toString().toLatin1().data()); return _nodeHash.erase(nodeItemToKill); } diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index 28a9397c60..5513376c65 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -54,6 +54,4 @@ void ReceivedPacketProcessor::nodeKilled(SharedNodePointer node) { lock(); int numRemoved = _nodePacketCounts.remove(node->getUUID()); unlock(); - qDebug() << "RPP::killNode *************************************"; - printf("\t\t RPP::killNode: %s killed %d nodes\n", node->getUUID().toString().toLatin1().data(), numRemoved); } From 7aef5edb8fcd7f9c2839101f33239eac8743556e Mon Sep 17 00:00:00 2001 From: wangyix Date: Wed, 11 Jun 2014 15:27:24 -0700 Subject: [PATCH 023/102] removed debug var --- libraries/networking/src/ReceivedPacketProcessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index 5513376c65..3ef518bbc2 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -52,6 +52,6 @@ bool ReceivedPacketProcessor::process() { void ReceivedPacketProcessor::nodeKilled(SharedNodePointer node) { lock(); - int numRemoved = _nodePacketCounts.remove(node->getUUID()); + _nodePacketCounts.remove(node->getUUID()); unlock(); } From 6260d661f32d4645a631a332ea482f6086ca50ed Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 11 Jun 2014 16:54:35 -0700 Subject: [PATCH 024/102] More work on generic containers. --- libraries/metavoxels/src/Bitstream.cpp | 259 +++++++++++++++++++------ libraries/metavoxels/src/Bitstream.h | 104 +++++++++- 2 files changed, 301 insertions(+), 62 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index a6d5967fd3..336cd36521 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -45,7 +45,9 @@ static int metaObjectStreamer = Bitstream::registerTypeStreamer(qMetaTypeId()); static int genericValueStreamer = Bitstream::registerTypeStreamer( - qRegisterMetaType(), new GenericTypeStreamer()); + qRegisterMetaType(), new GenericValueStreamer()); + +static int qVariantPairListMetaTypeId = qRegisterMetaType(); IDStreamer::IDStreamer(Bitstream& stream) : _stream(stream), @@ -1173,65 +1175,10 @@ Bitstream& Bitstream::operator<(const TypeStreamer* streamer) { } const char* typeName = streamer->getName(); *this << QByteArray::fromRawData(typeName, strlen(typeName)); - if (_metadataType == NO_METADATA) { - return *this; - } - TypeStreamer::Category category = streamer->getCategory(); - *this << (int)category; - switch (category) { - case TypeStreamer::SIMPLE_CATEGORY: - return *this; - - case TypeStreamer::ENUM_CATEGORY: { - QMetaEnum metaEnum = streamer->getMetaEnum(); - if (_metadataType == FULL_METADATA) { - *this << metaEnum.keyCount(); - for (int i = 0; i < metaEnum.keyCount(); i++) { - *this << QByteArray::fromRawData(metaEnum.key(i), strlen(metaEnum.key(i))); - *this << metaEnum.value(i); - } - } else { - *this << streamer->getBits(); - QCryptographicHash hash(QCryptographicHash::Md5); - for (int i = 0; i < metaEnum.keyCount(); i++) { - hash.addData(metaEnum.key(i), strlen(metaEnum.key(i)) + 1); - qint32 value = metaEnum.value(i); - hash.addData((const char*)&value, sizeof(qint32)); - } - QByteArray hashResult = hash.result(); - write(hashResult.constData(), hashResult.size() * BITS_IN_BYTE); - } - return *this; - } - case TypeStreamer::LIST_CATEGORY: - case TypeStreamer::SET_CATEGORY: - return *this << streamer->getValueStreamer(); - - case TypeStreamer::MAP_CATEGORY: - return *this << streamer->getKeyStreamer() << streamer->getValueStreamer(); - - default: - break; // fall through - } - // streamable type - const QVector& metaFields = streamer->getMetaFields(); - *this << metaFields.size(); - if (metaFields.isEmpty()) { - return *this; - } - QCryptographicHash hash(QCryptographicHash::Md5); - foreach (const MetaField& metaField, metaFields) { - _typeStreamerStreamer << metaField.getStreamer(); - if (_metadataType == FULL_METADATA) { - *this << metaField.getName(); - } else { - hash.addData(metaField.getName().constData(), metaField.getName().size() + 1); - } - } - if (_metadataType == HASH_METADATA) { - QByteArray hashResult = hash.result(); - write(hashResult.constData(), hashResult.size() * BITS_IN_BYTE); - } + if (_metadataType != NO_METADATA) { + *this << (int)streamer->getCategory(); + streamer->writeMetadata(*this, _metadataType == FULL_METADATA); + } return *this; } @@ -1703,6 +1650,31 @@ const TypeStreamer* TypeStreamer::getStreamerToWrite(const QVariant& value) cons return this; } +void TypeStreamer::writeMetadata(Bitstream& out, bool full) const { + if (getCategory() != STREAMABLE_CATEGORY) { + return; + } + // streamable type + const QVector& metaFields = getMetaFields(); + out << metaFields.size(); + if (metaFields.isEmpty()) { + return; + } + QCryptographicHash hash(QCryptographicHash::Md5); + foreach (const MetaField& metaField, metaFields) { + out << metaField.getStreamer(); + if (full) { + out << metaField.getName(); + } else { + hash.addData(metaField.getName().constData(), metaField.getName().size() + 1); + } + } + if (!full) { + QByteArray hashResult = hash.result(); + out.write(hashResult.constData(), hashResult.size() * BITS_IN_BYTE); + } +} + bool TypeStreamer::equal(const QVariant& first, const QVariant& second) const { return first == second; } @@ -1842,6 +1814,27 @@ const char* EnumTypeStreamer::getName() const { return _name.constData(); } +void EnumTypeStreamer::writeMetadata(Bitstream& out, bool full) const { + QMetaEnum metaEnum = getMetaEnum(); + if (full) { + out << metaEnum.keyCount(); + for (int i = 0; i < metaEnum.keyCount(); i++) { + out << QByteArray::fromRawData(metaEnum.key(i), strlen(metaEnum.key(i))); + out << metaEnum.value(i); + } + } else { + out << getBits(); + QCryptographicHash hash(QCryptographicHash::Md5); + for (int i = 0; i < metaEnum.keyCount(); i++) { + hash.addData(metaEnum.key(i), strlen(metaEnum.key(i)) + 1); + qint32 value = metaEnum.value(i); + hash.addData((const char*)&value, sizeof(qint32)); + } + QByteArray hashResult = hash.result(); + out.write(hashResult.constData(), hashResult.size() * BITS_IN_BYTE); + } +} + TypeStreamer::Category EnumTypeStreamer::getCategory() const { return ENUM_CATEGORY; } @@ -1948,6 +1941,54 @@ void MappedEnumTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const _baseStreamer->setEnumValue(object, value, _mappings); } +GenericTypeStreamer::GenericTypeStreamer(const QByteArray& name) : + _name(name) { +} + +const char* GenericTypeStreamer::getName() const { + return _name.constData(); +} + +GenericEnumTypeStreamer::GenericEnumTypeStreamer(const QByteArray& name, const QVector& values, + int bits, const QByteArray& hash) : + GenericTypeStreamer(name), + _values(values), + _bits(bits), + _hash(hash) { + + _type = qMetaTypeId(); +} + +void GenericEnumTypeStreamer::writeMetadata(Bitstream& out, bool full) const { + if (full) { + out << _values.size(); + foreach (const NameIntPair& value, _values) { + out << value.first << value.second; + } + } else { + out << _bits; + if (_hash.isEmpty()) { + QCryptographicHash hash(QCryptographicHash::Md5); + foreach (const NameIntPair& value, _values) { + hash.addData(value.first.constData(), value.first.size() + 1); + qint32 intValue = value.second; + hash.addData((const char*)&intValue, sizeof(qint32)); + } + const_cast(this)->_hash = hash.result(); + } + out.write(_hash.constData(), _hash.size() * BITS_IN_BYTE); + } +} + +void GenericEnumTypeStreamer::write(Bitstream& out, const QVariant& value) const { + int intValue = value.toInt(); + out.write(&intValue, _bits); +} + +TypeStreamer::Category GenericEnumTypeStreamer::getCategory() const { + return ENUM_CATEGORY; +} + GenericValue::GenericValue(const TypeStreamerPointer& streamer, const QVariant& value) : _streamer(streamer), _value(value) { @@ -1957,7 +1998,7 @@ bool GenericValue::operator==(const GenericValue& other) const { return _streamer == other._streamer && _value == other._value; } -const TypeStreamer* GenericTypeStreamer::getStreamerToWrite(const QVariant& value) const { +const TypeStreamer* GenericValueStreamer::getStreamerToWrite(const QVariant& value) const { return value.value().getStreamer().data(); } @@ -1990,6 +2031,46 @@ void MappedStreamableTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, } } +GenericStreamableTypeStreamer::GenericStreamableTypeStreamer(const QByteArray& name, + const QVector& fields, const QByteArray& hash) : + GenericTypeStreamer(name), + _fields(fields), + _hash(hash) { + + _type = qMetaTypeId(); +} + +void GenericStreamableTypeStreamer::writeMetadata(Bitstream& out, bool full) const { + out << _fields.size(); + foreach (const StreamerNamePair& field, _fields) { + out << field.first.data(); + if (full) { + out << field.second; + } + } + if (!full) { + if (_hash.isEmpty()) { + QCryptographicHash hash(QCryptographicHash::Md5); + foreach (const StreamerNamePair& field, _fields) { + hash.addData(field.second.constData(), field.second.size() + 1); + } + const_cast(this)->_hash = hash.result(); + } + out.write(_hash.constData(), _hash.size() * BITS_IN_BYTE); + } +} + +void GenericStreamableTypeStreamer::write(Bitstream& out, const QVariant& value) const { + QVariantList values = value.toList(); + for (int i = 0; i < _fields.size(); i++) { + _fields.at(i).first->write(out, values.at(i)); + } +} + +TypeStreamer::Category GenericStreamableTypeStreamer::getCategory() const { + return STREAMABLE_CATEGORY; +} + MappedListTypeStreamer::MappedListTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& valueStreamer) : _baseStreamer(baseStreamer), _valueStreamer(valueStreamer) { @@ -2024,6 +2105,29 @@ void MappedListTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const } } +GenericListTypeStreamer::GenericListTypeStreamer(const QByteArray& name, const TypeStreamerPointer& valueStreamer) : + GenericTypeStreamer(name), + _valueStreamer(valueStreamer) { + + _type = qMetaTypeId(); +} + +void GenericListTypeStreamer::writeMetadata(Bitstream& out, bool full) const { + out << _valueStreamer.data(); +} + +void GenericListTypeStreamer::write(Bitstream& out, const QVariant& value) const { + QVariantList values = value.toList(); + out << values.size(); + foreach (const QVariant& element, values) { + _valueStreamer->write(out, element); + } +} + +TypeStreamer::Category GenericListTypeStreamer::getCategory() const { + return LIST_CATEGORY; +} + MappedSetTypeStreamer::MappedSetTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& valueStreamer) : MappedListTypeStreamer(baseStreamer, valueStreamer) { } @@ -2040,6 +2144,14 @@ void MappedSetTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const } } +GenericSetTypeStreamer::GenericSetTypeStreamer(const QByteArray& name, const TypeStreamerPointer& valueStreamer) : + GenericListTypeStreamer(name, valueStreamer) { +} + +TypeStreamer::Category GenericSetTypeStreamer::getCategory() const { + return SET_CATEGORY; +} + MappedMapTypeStreamer::MappedMapTypeStreamer(const TypeStreamer* baseStreamer, const TypeStreamerPointer& keyStreamer, const TypeStreamerPointer& valueStreamer) : _baseStreamer(baseStreamer), @@ -2084,3 +2196,28 @@ void MappedMapTypeStreamer::readRawDelta(Bitstream& in, QVariant& object, const } } +GenericMapTypeStreamer::GenericMapTypeStreamer(const QByteArray& name, const TypeStreamerPointer& keyStreamer, + const TypeStreamerPointer& valueStreamer) : + GenericTypeStreamer(name), + _keyStreamer(keyStreamer), + _valueStreamer(valueStreamer) { + + _type = qMetaTypeId(); +} + +void GenericMapTypeStreamer::writeMetadata(Bitstream& out, bool full) const { + out << _keyStreamer.data() << _valueStreamer.data(); +} + +void GenericMapTypeStreamer::write(Bitstream& out, const QVariant& value) const { + QVariantPairList values = value.value(); + out << values.size(); + foreach (const QVariantPair& pair, values) { + _keyStreamer->write(out, pair.first); + _valueStreamer->write(out, pair.second); + } +} + +TypeStreamer::Category GenericMapTypeStreamer::getCategory() const { + return MAP_CATEGORY; +} diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 434b14ce69..6a3fa88c94 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -45,10 +45,16 @@ class TypeStreamer; typedef SharedObjectPointerTemplate AttributePointer; typedef QPair ScopeNamePair; +typedef QPair NameIntPair; typedef QSharedPointer TypeStreamerPointer; typedef QVector PropertyReaderVector; typedef QVector PropertyWriterVector; +typedef QPair QVariantPair; +typedef QList QVariantPairList; + +Q_DECLARE_METATYPE(QVariantPairList) + /// Streams integer identifiers that conform to the following pattern: each ID encountered in the stream is either one that /// has been sent (received) before, or is one more than the highest previously encountered ID (starting at zero). This allows /// us to use the minimum number of bits to encode the IDs. @@ -856,6 +862,8 @@ public: virtual const TypeStreamer* getStreamerToWrite(const QVariant& value) const; + virtual void writeMetadata(Bitstream& out, bool full) const; + virtual bool equal(const QVariant& first, const QVariant& second) const; virtual void write(Bitstream& out, const QVariant& value) const; @@ -929,6 +937,7 @@ public: EnumTypeStreamer(const QMetaEnum& metaEnum); virtual const char* getName() const; + virtual void writeMetadata(Bitstream& out, bool full) const; virtual Category getCategory() const; virtual int getBits() const; virtual QMetaEnum getMetaEnum() const; @@ -966,6 +975,36 @@ private: QHash _mappings; }; +/// Base class for generic type streamers, which contain all the metadata required to write out a type. +class GenericTypeStreamer : public TypeStreamer { +public: + + GenericTypeStreamer(const QByteArray& name); + + virtual const char* getName() const; + +private: + + QByteArray _name; +}; + +/// A streamer for generic enums. +class GenericEnumTypeStreamer : public GenericTypeStreamer { +public: + + GenericEnumTypeStreamer(const QByteArray& name, const QVector& values, int bits, const QByteArray& hash); + + virtual void writeMetadata(Bitstream& out, bool full) const; + virtual void write(Bitstream& out, const QVariant& value) const; + virtual Category getCategory() const; + +private: + + QVector _values; + int _bits; + QByteArray _hash; +}; + /// Contains a value along with a pointer to its streamer. class GenericValue { public: @@ -986,7 +1025,7 @@ private: Q_DECLARE_METATYPE(GenericValue) /// A streamer class for generic values. -class GenericTypeStreamer : public SimpleTypeStreamer { +class GenericValueStreamer : public SimpleTypeStreamer { public: virtual const TypeStreamer* getStreamerToWrite(const QVariant& value) const; @@ -1022,6 +1061,24 @@ private: QVector _fields; }; +typedef QPair StreamerNamePair; + +/// A streamer for generic enums. +class GenericStreamableTypeStreamer : public GenericTypeStreamer { +public: + + GenericStreamableTypeStreamer(const QByteArray& name, const QVector& fields, const QByteArray& hash); + + virtual void writeMetadata(Bitstream& out, bool full) const; + virtual void write(Bitstream& out, const QVariant& value) const; + virtual Category getCategory() const; + +private: + + QVector _fields; + QByteArray _hash; +}; + /// Base template for collection streamers. template class CollectionTypeStreamer : public SimpleTypeStreamer { }; @@ -1030,6 +1087,7 @@ template class CollectionTypeStreamer : public SimpleTypeStreamer { template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: + virtual void writeMetadata(Bitstream& out, bool full) const { out << getValueStreamer(); } virtual TypeStreamer::Category getCategory() const { return TypeStreamer::LIST_CATEGORY; } virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } virtual void insert(QVariant& object, const QVariant& value) const { @@ -1046,6 +1104,7 @@ public: template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: + virtual void writeMetadata(Bitstream& out, bool full) const { out << getValueStreamer(); } virtual TypeStreamer::Category getCategory() const { return TypeStreamer::LIST_CATEGORY; } virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } virtual void insert(QVariant& object, const QVariant& value) const { @@ -1073,10 +1132,26 @@ protected: TypeStreamerPointer _valueStreamer; }; +/// A streamer for generic lists. +class GenericListTypeStreamer : public GenericTypeStreamer { +public: + + GenericListTypeStreamer(const QByteArray& name, const TypeStreamerPointer& valueStreamer); + + virtual void writeMetadata(Bitstream& out, bool full) const; + virtual void write(Bitstream& out, const QVariant& value) const; + virtual Category getCategory() const; + +private: + + TypeStreamerPointer _valueStreamer; +}; + /// A streamer for set types. template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: + virtual void writeMetadata(Bitstream& out, bool full) const { out << getValueStreamer(); } virtual TypeStreamer::Category getCategory() const { return TypeStreamer::SET_CATEGORY; } virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } virtual void insert(QVariant& object, const QVariant& value) const { @@ -1094,10 +1169,20 @@ public: virtual void readRawDelta(Bitstream& in, QVariant& object, const QVariant& reference) const; }; +/// A streamer for generic sets. +class GenericSetTypeStreamer : public GenericListTypeStreamer { +public: + + GenericSetTypeStreamer(const QByteArray& name, const TypeStreamerPointer& valueStreamer); + + virtual Category getCategory() const; +}; + /// A streamer for hash types. template class CollectionTypeStreamer > : public SimpleTypeStreamer > { public: + virtual void writeMetadata(Bitstream& out, bool full) const { out << getKeyStreamer() << getValueStreamer(); } virtual TypeStreamer::Category getCategory() const { return TypeStreamer::MAP_CATEGORY; } virtual const TypeStreamer* getKeyStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } virtual const TypeStreamer* getValueStreamer() const { return Bitstream::getTypeStreamer(qMetaTypeId()); } @@ -1126,6 +1211,23 @@ private: TypeStreamerPointer _valueStreamer; }; +/// A streamer for generic maps. +class GenericMapTypeStreamer : public GenericTypeStreamer { +public: + + GenericMapTypeStreamer(const QByteArray& name, const TypeStreamerPointer& keyStreamer, + const TypeStreamerPointer& valueStreamer); + + virtual void writeMetadata(Bitstream& out, bool full) const; + virtual void write(Bitstream& out, const QVariant& value) const; + virtual Category getCategory() const; + +private: + + TypeStreamerPointer _keyStreamer; + TypeStreamerPointer _valueStreamer; +}; + /// Macro for registering simple type streamers. #define REGISTER_SIMPLE_TYPE_STREAMER(X) static int X##Streamer = \ Bitstream::registerTypeStreamer(qMetaTypeId(), new SimpleTypeStreamer()); From eff097638ce6c12e700e561331594b8b6976f986 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Wed, 11 Jun 2014 17:02:08 -0700 Subject: [PATCH 025/102] Implementd sixense mouse emulation --- interface/src/Application.cpp | 10 +++- interface/src/devices/SixenseManager.cpp | 68 +++++++++++++++++++++ interface/src/devices/SixenseManager.h | 9 +++ interface/src/ui/ApplicationOverlay.cpp | 75 +++++++++++++++++++++++- 4 files changed, 159 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2b3256141e..6035cdd7cb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1090,6 +1090,13 @@ void Application::focusOutEvent(QFocusEvent* event) { } void Application::mouseMoveEvent(QMouseEvent* event) { + + bool showMouse = true; + // If this mouse move event is emitted by a controller, dont show the mouse cursor + if (event->type() == CONTROLLER_MOVE_EVENT) { + showMouse = false; + } + _controllerScriptingInterface.emitMouseMoveEvent(event); // send events to any registered scripts // if one of our scripts have asked to capture this event, then stop processing it @@ -1097,10 +1104,9 @@ void Application::mouseMoveEvent(QMouseEvent* event) { return; } - _lastMouseMove = usecTimestampNow(); - if (_mouseHidden && !OculusManager::isConnected()) { + if (_mouseHidden && showMouse && !OculusManager::isConnected()) { getGLWidget()->setCursor(Qt::ArrowCursor); _mouseHidden = false; _seenMouseMove = true; diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 8cd3cc059e..4ecb416a92 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -39,6 +39,10 @@ SixenseManager::SixenseManager() { sixenseInit(); #endif + _triggerPressed = false; + _bumperPressed = false; + _oldPos.setX(-1); + _oldPos.setY(-1); } SixenseManager::~SixenseManager() { @@ -107,6 +111,12 @@ void SixenseManager::update(float deltaTime) { palm->setTrigger(data->trigger); palm->setJoystick(data->joystick_x, data->joystick_y); + + // Emulate the mouse so we can use scripts + if (numActiveControllers == 2) { + emulateMouse(palm); + } + // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters. glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]); position *= METERS_PER_MILLIMETER; @@ -313,5 +323,63 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers) } } } + +//Injecting mouse movements and clicks +void SixenseManager::emulateMouse(PalmData *palm) { + MyAvatar* avatar = Application::getInstance()->getAvatar(); + QPoint pos; + // Get directon relative to avatar orientation + glm::vec3 direction = glm::inverse(avatar->getOrientation()) * palm->getFingerDirection(); + + // Get the angles, scaled between 0-1 + float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; + float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); + + float cursorRange = Application::getInstance()->getGLWidget()->width(); + + pos.setX(cursorRange * xAngle); + pos.setY(cursorRange * yAngle); + + //If position has changed, emit a mouse move to the application + if (pos.x() != _oldPos.x() || pos.y() != _oldPos.y()) { + QMouseEvent mouseEvent(static_cast(CONTROLLER_MOVE_EVENT), pos, Qt::NoButton, Qt::NoButton, 0); + + Application::getInstance()->mouseMoveEvent(&mouseEvent); + } + _oldPos = pos; + + //Check for bumper press ( Right Click ) + if (palm->getControllerButtons() & BUTTON_FWD) { + if (!_bumperPressed) { + _bumperPressed = true; + QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, Qt::RightButton, Qt::RightButton, 0); + + Application::getInstance()->mousePressEvent(&mouseEvent); + } + } else if (_bumperPressed) { + QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, Qt::RightButton, Qt::RightButton, 0); + + Application::getInstance()->mouseReleaseEvent(&mouseEvent); + + _bumperPressed = false; + } + + //Check for trigger press ( Left Click ) + if (palm->getTrigger() == 1.0f) { + if (!_triggerPressed) { + _triggerPressed = true; + QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, Qt::LeftButton, Qt::LeftButton, 0); + + Application::getInstance()->mousePressEvent(&mouseEvent); + } + } else if (_triggerPressed) { + QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, Qt::LeftButton, Qt::LeftButton, 0); + + Application::getInstance()->mouseReleaseEvent(&mouseEvent); + + _triggerPressed = false; + } +} + #endif // HAVE_SIXENSE diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index a98d4c0e4e..061b3cc625 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -27,6 +27,9 @@ const unsigned int BUTTON_3 = 1U << 3; const unsigned int BUTTON_4 = 1U << 4; const unsigned int BUTTON_FWD = 1U << 7; +// Event type that represents moving the controller +const unsigned int CONTROLLER_MOVE_EVENT = 1500U; + /// Handles interaction with the Sixense SDK (e.g., Razer Hydra). class SixenseManager : public QObject { Q_OBJECT @@ -44,6 +47,7 @@ public slots: private: #ifdef HAVE_SIXENSE void updateCalibration(const sixenseControllerData* controllers); + void emulateMouse(PalmData *palm); int _calibrationState; @@ -65,6 +69,11 @@ private: #endif quint64 _lastMovement; glm::vec3 _amountMoved; + + // for mouse emulation + bool _triggerPressed; + bool _bumperPressed; + QPoint _oldPos; }; #endif // hifi_SixenseManager_h diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index a48e0a2c1a..750be96cc9 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -35,6 +35,78 @@ ApplicationOverlay::~ApplicationOverlay() { const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; +void renderControllerPointer() +{ + Application* application = Application::getInstance(); + QGLWidget* glWidget = application->getGLWidget(); + MyAvatar* myAvatar = application->getAvatar(); + + const HandData* handData = Application::getInstance()->getAvatar()->getHandData(); + int numberOfPalms = handData->getNumPalms(); + + + int palmIndex = 3; + const PalmData* palmData = NULL; + + if (palmIndex >= handData->getPalms().size()) { + return; + } + + if (handData->getPalms()[palmIndex].isActive()) { + palmData = &handData->getPalms()[palmIndex]; + } else { + return; + } + + // Get directon relative to avatar orientation + glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); + + // Get the angles, scaled between 0-1 + float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; + float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); + + float cursorRange = glWidget->width(); + + int mouseX = cursorRange * xAngle; + int mouseY = cursorRange * yAngle; + + if (mouseX < 0) { + mouseX = 0; + } else if (mouseX > glWidget->width()) { + mouseX = glWidget->width(); + } + if (mouseY < 0) { + mouseY = 0; + } else if (mouseY > glWidget->width()) { + mouseY = glWidget->width(); + } + + const float pointerWidth = 40; + const float pointerHeight = 40; + const float crossPad = 16; + + mouseX -= pointerWidth / 2.0f; + mouseY += pointerHeight / 2.0f; + + glBegin(GL_QUADS); + + glColor3f(0, 0, 1); + + //Horizontal crosshair + glVertex2i(mouseX, mouseY - crossPad); + glVertex2i(mouseX + pointerWidth, mouseY - crossPad); + glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight + crossPad); + glVertex2i(mouseX, mouseY - pointerHeight + crossPad); + + //Vertical crosshair + glVertex2i(mouseX + crossPad, mouseY); + glVertex2i(mouseX + pointerWidth - crossPad, mouseY); + glVertex2i(mouseX + pointerWidth - crossPad, mouseY - pointerHeight); + glVertex2i(mouseX + crossPad, mouseY - pointerHeight); + + glEnd(); +} + // Renders the overlays either to a texture or to the screen void ApplicationOverlay::renderOverlay(bool renderToTexture) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); @@ -254,8 +326,9 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { glVertex2i(mouseX + crossPad, mouseY - pointerHeight); glEnd(); + } else { //only render controller pointer if we aren't already rendering a mouse pointer + renderControllerPointer(); } - glPopMatrix(); glMatrixMode(GL_MODELVIEW); From 2fec7400e2d06d2f9f730d32f42db8be802ec511 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Wed, 11 Jun 2014 17:21:59 -0700 Subject: [PATCH 026/102] Added menu options for sixense mouse input --- interface/src/Menu.cpp | 5 ++++ interface/src/Menu.h | 3 ++ interface/src/devices/SixenseManager.cpp | 35 ++++++++++++++++++++---- interface/src/ui/ApplicationOverlay.cpp | 10 +++++-- 4 files changed, 45 insertions(+), 8 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f791d20588..f78b8a822f 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -379,6 +379,11 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(oculusOptionsMenu, MenuOption::AllowOculusCameraModeChange, 0, false); addCheckableActionToQMenuAndActionHash(oculusOptionsMenu, MenuOption::DisplayOculusOverlays, 0, true); + QMenu* sixenseOptionsMenu = developerMenu->addMenu("Sixense Options"); + addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true); + addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseLeftHanded, 0, false); + addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseInvertInputButtons, 0, false); + QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options"); addCheckableActionToQMenuAndActionHash(handOptionsMenu, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 69015a938b..40c6e646d9 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -397,6 +397,9 @@ namespace MenuOption { const QString SettingsExport = "Export Settings"; const QString SettingsImport = "Import Settings"; const QString SimpleShadows = "Simple"; + const QString SixenseInvertInputButtons = "Invert Sixense Mouse Input Buttons"; + const QString SixenseLeftHanded = "Left Handed Sixense Mouse Input"; + const QString SixenseMouseInput = "Enable Sixense Mouse Input"; const QString ShowBordersVoxelNodes = "Show Voxel Nodes"; const QString ShowBordersModelNodes = "Show Model Nodes"; const QString ShowBordersParticleNodes = "Show Particle Nodes"; diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 4ecb416a92..f347d09af9 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -113,8 +113,16 @@ void SixenseManager::update(float deltaTime) { // Emulate the mouse so we can use scripts - if (numActiveControllers == 2) { - emulateMouse(palm); + if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { + if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded)) { + if (numActiveControllers == 1){ + emulateMouse(palm); + } + } else { + if (numActiveControllers == 2) { + emulateMouse(palm); + } + } } // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters. @@ -348,16 +356,28 @@ void SixenseManager::emulateMouse(PalmData *palm) { } _oldPos = pos; + Qt::MouseButton bumperButton; + Qt::MouseButton triggerButton; + + if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseInvertInputButtons)) { + bumperButton = Qt::LeftButton; + triggerButton = Qt::RightButton; + } else { + bumperButton = Qt::RightButton; + triggerButton = Qt::LeftButton; + } + //Check for bumper press ( Right Click ) if (palm->getControllerButtons() & BUTTON_FWD) { if (!_bumperPressed) { _bumperPressed = true; - QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, Qt::RightButton, Qt::RightButton, 0); + + QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, bumperButton, bumperButton, 0); Application::getInstance()->mousePressEvent(&mouseEvent); } } else if (_bumperPressed) { - QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, Qt::RightButton, Qt::RightButton, 0); + QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, bumperButton, bumperButton, 0); Application::getInstance()->mouseReleaseEvent(&mouseEvent); @@ -368,17 +388,20 @@ void SixenseManager::emulateMouse(PalmData *palm) { if (palm->getTrigger() == 1.0f) { if (!_triggerPressed) { _triggerPressed = true; - QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, Qt::LeftButton, Qt::LeftButton, 0); + + QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, triggerButton, triggerButton, 0); Application::getInstance()->mousePressEvent(&mouseEvent); } } else if (_triggerPressed) { - QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, Qt::LeftButton, Qt::LeftButton, 0); + QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, triggerButton, triggerButton, 0); Application::getInstance()->mouseReleaseEvent(&mouseEvent); _triggerPressed = false; } + + } #endif // HAVE_SIXENSE diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 750be96cc9..401062bb36 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -45,7 +45,12 @@ void renderControllerPointer() int numberOfPalms = handData->getNumPalms(); - int palmIndex = 3; + int palmIndex; + if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded)) { + palmIndex = 2; + } else { + palmIndex = 3; + } const PalmData* palmData = NULL; if (palmIndex >= handData->getPalms().size()) { @@ -326,7 +331,8 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { glVertex2i(mouseX + crossPad, mouseY - pointerHeight); glEnd(); - } else { //only render controller pointer if we aren't already rendering a mouse pointer + } else if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { + //only render controller pointer if we aren't already rendering a mouse pointer renderControllerPointer(); } glPopMatrix(); From d993fd52f2d678e02fed0aafb5142b218634f13f Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Wed, 11 Jun 2014 17:27:00 -0700 Subject: [PATCH 027/102] Cleaned up a small bit of code --- interface/src/devices/SixenseManager.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index f347d09af9..2a0db69d52 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -114,14 +114,9 @@ void SixenseManager::update(float deltaTime) { // Emulate the mouse so we can use scripts if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { - if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded)) { - if (numActiveControllers == 1){ - emulateMouse(palm); - } - } else { - if (numActiveControllers == 2) { - emulateMouse(palm); - } + // Check if we are on the correct palm + if ((Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded) && numActiveControllers == 1) || numActiveControllers == 2) { + emulateMouse(palm); } } From 14a591c5d853631102e6003cbae2f108bfabc43f Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Wed, 11 Jun 2014 17:35:50 -0700 Subject: [PATCH 028/102] Move to previous line --- interface/src/ui/ApplicationOverlay.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 401062bb36..924c2e9d84 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -35,8 +35,7 @@ ApplicationOverlay::~ApplicationOverlay() { const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; -void renderControllerPointer() -{ +void renderControllerPointer() { Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); From 32a9eed88d8728f1221b14f6799a0cda672305bd Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Wed, 11 Jun 2014 18:05:10 -0700 Subject: [PATCH 029/102] Fixed build failure --- interface/src/devices/SixenseManager.cpp | 9 +++++---- interface/src/devices/SixenseManager.h | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 2a0db69d52..abbb32ec5e 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -41,8 +41,8 @@ SixenseManager::SixenseManager() { #endif _triggerPressed = false; _bumperPressed = false; - _oldPos.setX(-1); - _oldPos.setY(-1); + _oldX = -1; + _oldY = -1; } SixenseManager::~SixenseManager() { @@ -344,12 +344,13 @@ void SixenseManager::emulateMouse(PalmData *palm) { pos.setY(cursorRange * yAngle); //If position has changed, emit a mouse move to the application - if (pos.x() != _oldPos.x() || pos.y() != _oldPos.y()) { + if (pos.x() != _oldX || pos.y() != _oldY) { QMouseEvent mouseEvent(static_cast(CONTROLLER_MOVE_EVENT), pos, Qt::NoButton, Qt::NoButton, 0); Application::getInstance()->mouseMoveEvent(&mouseEvent); } - _oldPos = pos; + _oldX = pos.x(); + _oldY = pos.y(); Qt::MouseButton bumperButton; Qt::MouseButton triggerButton; diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index 061b3cc625..93888ce944 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -73,7 +73,8 @@ private: // for mouse emulation bool _triggerPressed; bool _bumperPressed; - QPoint _oldPos; + int _oldX; + int _oldY; }; #endif // hifi_SixenseManager_h From 6c98248e507cc6a29a35bce8c7176b1351bd0ff8 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 11 Jun 2014 18:30:01 -0700 Subject: [PATCH 030/102] More progress on generics. --- libraries/metavoxels/src/Bitstream.cpp | 193 ++++++++++++++++++++----- libraries/metavoxels/src/Bitstream.h | 12 +- 2 files changed, 164 insertions(+), 41 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 336cd36521..d285e4aa3f 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -1216,7 +1216,15 @@ Bitstream& Bitstream::operator>(TypeStreamerPointer& streamer) { } return *this; } + if (_genericsMode == ALL_GENERICS) { + streamer = readGenericTypeStreamer(typeName, category); + return *this; + } if (!baseStreamer) { + if (_genericsMode == FALLBACK_GENERICS) { + streamer = readGenericTypeStreamer(typeName, category); + return *this; + } baseStreamer = getInvalidTypeStreamer(); } switch (category) { @@ -1224,8 +1232,7 @@ Bitstream& Bitstream::operator>(TypeStreamerPointer& streamer) { if (_metadataType == FULL_METADATA) { int keyCount; *this >> keyCount; - QMetaEnum metaEnum = (baseStreamer && baseStreamer->getCategory() == TypeStreamer::ENUM_CATEGORY) ? - baseStreamer->getMetaEnum() : QMetaEnum(); + QMetaEnum metaEnum = baseStreamer->getMetaEnum(); QHash mappings; bool matches = (keyCount == metaEnum.keyCount()); int highestValue = 0; @@ -1248,7 +1255,7 @@ Bitstream& Bitstream::operator>(TypeStreamerPointer& streamer) { int bits; *this >> bits; QCryptographicHash hash(QCryptographicHash::Md5); - if (baseStreamer && baseStreamer->getCategory() == TypeStreamer::ENUM_CATEGORY) { + if (baseStreamer->getCategory() == TypeStreamer::ENUM_CATEGORY) { QMetaEnum metaEnum = baseStreamer->getMetaEnum(); for (int i = 0; i < metaEnum.keyCount(); i++) { hash.addData(metaEnum.key(i), strlen(metaEnum.key(i)) + 1); @@ -1269,8 +1276,7 @@ Bitstream& Bitstream::operator>(TypeStreamerPointer& streamer) { case TypeStreamer::SET_CATEGORY: { TypeStreamerPointer valueStreamer; *this >> valueStreamer; - if (!(baseStreamer && baseStreamer->getCategory() == category && - valueStreamer == baseStreamer->getValueStreamer())) { + if (!(baseStreamer->getCategory() == category && valueStreamer == baseStreamer->getValueStreamer())) { streamer = TypeStreamerPointer(category == TypeStreamer::LIST_CATEGORY ? new MappedListTypeStreamer(baseStreamer, valueStreamer) : new MappedSetTypeStreamer(baseStreamer, valueStreamer)); @@ -1280,7 +1286,7 @@ Bitstream& Bitstream::operator>(TypeStreamerPointer& streamer) { case TypeStreamer::MAP_CATEGORY: { TypeStreamerPointer keyStreamer, valueStreamer; *this >> keyStreamer >> valueStreamer; - if (!(baseStreamer && baseStreamer->getCategory() == TypeStreamer::MAP_CATEGORY && + if (!(baseStreamer->getCategory() == TypeStreamer::MAP_CATEGORY && keyStreamer == baseStreamer->getKeyStreamer() && valueStreamer == baseStreamer->getValueStreamer())) { streamer = TypeStreamerPointer(new MappedMapTypeStreamer(baseStreamer, keyStreamer, valueStreamer)); } @@ -1298,9 +1304,7 @@ Bitstream& Bitstream::operator>(TypeStreamerPointer& streamer) { if (_metadataType == FULL_METADATA) { QByteArray fieldName; *this >> fieldName; - if (baseStreamer) { - index = baseStreamer->getFieldIndex(fieldName); - } + index = baseStreamer->getFieldIndex(fieldName); } fields[i] = StreamerIndexPair(typeStreamer, index); } @@ -1308,49 +1312,44 @@ Bitstream& Bitstream::operator>(TypeStreamerPointer& streamer) { if (_metadataType == HASH_METADATA) { QCryptographicHash hash(QCryptographicHash::Md5); bool matches = true; - if (baseStreamer) { - const QVector& localFields = baseStreamer->getMetaFields(); - if (fieldCount != localFields.size()) { - matches = false; - - } else { - if (fieldCount == 0) { - return *this; - } - for (int i = 0; i < fieldCount; i++) { - const MetaField& localField = localFields.at(i); - if (fields.at(i).first != localField.getStreamer()) { - matches = false; - break; - } - hash.addData(localField.getName().constData(), localField.getName().size() + 1); - } + const QVector& localFields = baseStreamer->getMetaFields(); + if (fieldCount != localFields.size()) { + matches = false; + + } else { + if (fieldCount == 0) { + return *this; } + for (int i = 0; i < fieldCount; i++) { + const MetaField& localField = localFields.at(i); + if (fields.at(i).first != localField.getStreamer()) { + matches = false; + break; + } + hash.addData(localField.getName().constData(), localField.getName().size() + 1); + } } QByteArray localHashResult = hash.result(); QByteArray remoteHashResult(localHashResult.size(), 0); read(remoteHashResult.data(), remoteHashResult.size() * BITS_IN_BYTE); - if (baseStreamer && matches && localHashResult == remoteHashResult) { + if (matches && localHashResult == remoteHashResult) { // since everything is the same, we can use the default streamer return *this; } - } else if (baseStreamer) { - // if all fields are the same type and in the right order, we can use the (more efficient) default streamer - const QVector& localFields = baseStreamer->getMetaFields(); - if (fieldCount != localFields.size()) { + } + // if all fields are the same type and in the right order, we can use the (more efficient) default streamer + const QVector& localFields = baseStreamer->getMetaFields(); + if (fieldCount != localFields.size()) { + streamer = TypeStreamerPointer(new MappedStreamableTypeStreamer(baseStreamer, fields)); + return *this; + } + for (int i = 0; i < fieldCount; i++) { + const StreamerIndexPair& field = fields.at(i); + if (field.first != localFields.at(i).getStreamer() || field.second != i) { streamer = TypeStreamerPointer(new MappedStreamableTypeStreamer(baseStreamer, fields)); return *this; } - for (int i = 0; i < fieldCount; i++) { - const StreamerIndexPair& field = fields.at(i); - if (field.first != localFields.at(i).getStreamer() || field.second != i) { - streamer = TypeStreamerPointer(new MappedStreamableTypeStreamer(baseStreamer, fields)); - return *this; - } - } - return *this; } - streamer = TypeStreamerPointer(new MappedStreamableTypeStreamer(baseStreamer, fields)); return *this; } @@ -1444,6 +1443,81 @@ void Bitstream::clearSharedObject(QObject* object) { } } +const int MD5_HASH_SIZE = 16; + +TypeStreamerPointer Bitstream::readGenericTypeStreamer(const QByteArray& name, int category) { + TypeStreamerPointer streamer; + switch (category) { + case TypeStreamer::ENUM_CATEGORY: { + QVector values; + int bits; + QByteArray hash; + if (_metadataType == FULL_METADATA) { + int keyCount; + *this >> keyCount; + values.resize(keyCount); + int highestValue = 0; + for (int i = 0; i < keyCount; i++) { + QByteArray name; + int value; + *this >> name >> value; + values[i] = NameIntPair(name, value); + highestValue = qMax(highestValue, value); + } + bits = getBitsForHighestValue(highestValue); + + } else { + *this >> bits; + hash.resize(MD5_HASH_SIZE); + read(hash.data(), hash.size() * BITS_IN_BYTE); + } + streamer = TypeStreamerPointer(new GenericEnumTypeStreamer(name, values, bits, hash)); + break; + } + case TypeStreamer::STREAMABLE_CATEGORY: { + int fieldCount; + *this >> fieldCount; + QVector fields(fieldCount); + QByteArray hash; + if (fieldCount == 0) { + streamer = TypeStreamerPointer(new GenericStreamableTypeStreamer(name, fields, hash)); + break; + } + for (int i = 0; i < fieldCount; i++) { + TypeStreamerPointer streamer; + *this >> streamer; + QByteArray name; + if (_metadataType == FULL_METADATA) { + *this >> name; + } + fields[i] = StreamerNamePair(streamer, name); + } + if (_metadataType == HASH_METADATA) { + hash.resize(MD5_HASH_SIZE); + read(hash.data(), hash.size() * BITS_IN_BYTE); + } + streamer = TypeStreamerPointer(new GenericStreamableTypeStreamer(name, fields, hash)); + break; + } + case TypeStreamer::LIST_CATEGORY: + case TypeStreamer::SET_CATEGORY: { + TypeStreamerPointer valueStreamer; + *this >> valueStreamer; + streamer = TypeStreamerPointer(category == TypeStreamer::LIST_CATEGORY ? + new GenericListTypeStreamer(name, valueStreamer) : new GenericSetTypeStreamer(name, valueStreamer)); + break; + } + case TypeStreamer::MAP_CATEGORY: { + TypeStreamerPointer keyStreamer, valueStreamer; + *this >> keyStreamer >> valueStreamer; + streamer = TypeStreamerPointer(new GenericMapTypeStreamer(name, keyStreamer, valueStreamer)); + break; + } + } + static_cast(streamer.data())->_weakSelf = streamer; + return streamer; +} + QHash& Bitstream::getMetaObjects() { static QHash metaObjects; return metaObjects; @@ -1985,6 +2059,12 @@ void GenericEnumTypeStreamer::write(Bitstream& out, const QVariant& value) const out.write(&intValue, _bits); } +QVariant GenericEnumTypeStreamer::read(Bitstream& in) const { + int intValue = 0; + in.read(&intValue, _bits); + return QVariant::fromValue(GenericValue(_weakSelf, intValue)); +} + TypeStreamer::Category GenericEnumTypeStreamer::getCategory() const { return ENUM_CATEGORY; } @@ -2042,6 +2122,9 @@ GenericStreamableTypeStreamer::GenericStreamableTypeStreamer(const QByteArray& n void GenericStreamableTypeStreamer::writeMetadata(Bitstream& out, bool full) const { out << _fields.size(); + if (_fields.isEmpty()) { + return; + } foreach (const StreamerNamePair& field, _fields) { out << field.first.data(); if (full) { @@ -2067,6 +2150,14 @@ void GenericStreamableTypeStreamer::write(Bitstream& out, const QVariant& value) } } +QVariant GenericStreamableTypeStreamer::read(Bitstream& in) const { + QVariantList values; + foreach (const StreamerNamePair& field, _fields) { + values.append(field.first->read(in)); + } + return QVariant::fromValue(GenericValue(_weakSelf, values)); +} + TypeStreamer::Category GenericStreamableTypeStreamer::getCategory() const { return STREAMABLE_CATEGORY; } @@ -2124,6 +2215,16 @@ void GenericListTypeStreamer::write(Bitstream& out, const QVariant& value) const } } +QVariant GenericListTypeStreamer::read(Bitstream& in) const { + QVariantList values; + int size; + in >> size; + for (int i = 0; i < size; i++) { + values.append(_valueStreamer->read(in)); + } + return QVariant::fromValue(GenericValue(_weakSelf, values)); +} + TypeStreamer::Category GenericListTypeStreamer::getCategory() const { return LIST_CATEGORY; } @@ -2218,6 +2319,18 @@ void GenericMapTypeStreamer::write(Bitstream& out, const QVariant& value) const } } +QVariant GenericMapTypeStreamer::read(Bitstream& in) const { + QVariantPairList values; + int size; + in >> size; + for (int i = 0; i < size; i++) { + QVariant key = _keyStreamer->read(in); + QVariant value = _valueStreamer->read(in); + values.append(QVariantPair(key, value)); + } + return QVariant::fromValue(GenericValue(_weakSelf, QVariant::fromValue(values))); +} + TypeStreamer::Category GenericMapTypeStreamer::getCategory() const { return MAP_CATEGORY; } diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 6a3fa88c94..ee9b8b472c 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -47,6 +47,7 @@ typedef SharedObjectPointerTemplate AttributePointer; typedef QPair ScopeNamePair; typedef QPair NameIntPair; typedef QSharedPointer TypeStreamerPointer; +typedef QWeakPointer WeakTypeStreamerPointer; typedef QVector PropertyReaderVector; typedef QVector PropertyWriterVector; @@ -442,6 +443,8 @@ private slots: private: + TypeStreamerPointer readGenericTypeStreamer(const QByteArray& name, int category); + QDataStream& _underlying; quint8 _byte; int _position; @@ -983,9 +986,12 @@ public: virtual const char* getName() const; -private: +protected: + + friend class Bitstream; QByteArray _name; + WeakTypeStreamerPointer _weakSelf; }; /// A streamer for generic enums. @@ -996,6 +1002,7 @@ public: virtual void writeMetadata(Bitstream& out, bool full) const; virtual void write(Bitstream& out, const QVariant& value) const; + virtual QVariant read(Bitstream& in) const; virtual Category getCategory() const; private: @@ -1071,6 +1078,7 @@ public: virtual void writeMetadata(Bitstream& out, bool full) const; virtual void write(Bitstream& out, const QVariant& value) const; + virtual QVariant read(Bitstream& in) const; virtual Category getCategory() const; private: @@ -1140,6 +1148,7 @@ public: virtual void writeMetadata(Bitstream& out, bool full) const; virtual void write(Bitstream& out, const QVariant& value) const; + virtual QVariant read(Bitstream& in) const; virtual Category getCategory() const; private: @@ -1220,6 +1229,7 @@ public: virtual void writeMetadata(Bitstream& out, bool full) const; virtual void write(Bitstream& out, const QVariant& value) const; + virtual QVariant read(Bitstream& in) const; virtual Category getCategory() const; private: From 1503946b7c0046ca751c4487d0bf65287395f567 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 11 Jun 2014 21:11:49 -0700 Subject: [PATCH 031/102] Fix build warning in Audio.cpp \interface\src\Audio.cpp(309): warning C4018: '<' : signed/unsigned mismatch --- interface/src/Audio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index ebc228a13d..2cbe3a062f 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -306,7 +306,7 @@ void linearResampling(int16_t* sourceSamples, int16_t* destinationSamples, } else { // this is a 48 to 24 resampling but both source and destination are two channels // squish two samples into one in each channel - for (int i = 0; i < numSourceSamples; i += 4) { + for (unsigned int i = 0; i < numSourceSamples; i += 4) { destinationSamples[i / 2] = (sourceSamples[i] / 2) + (sourceSamples[i + 2] / 2); destinationSamples[(i / 2) + 1] = (sourceSamples[i + 1] / 2) + (sourceSamples[i + 3] / 2); } From d0b1880ee4e65a87a3c78d14ab0bfaae2ed17604 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 12 Jun 2014 10:20:35 -0700 Subject: [PATCH 032/102] Experimenting with ambidextrous sixense mouse input --- interface/src/devices/SixenseManager.cpp | 96 ++++++++++------- interface/src/devices/SixenseManager.h | 12 +-- interface/src/ui/ApplicationOverlay.cpp | 126 ++++++++++++----------- 3 files changed, 132 insertions(+), 102 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index abbb32ec5e..37bad28093 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -39,10 +39,14 @@ SixenseManager::SixenseManager() { sixenseInit(); #endif - _triggerPressed = false; - _bumperPressed = false; - _oldX = -1; - _oldY = -1; + _triggerPressed[0] = false; + _bumperPressed[0] = false; + _oldX[0] = -1; + _oldY[0] = -1; + _triggerPressed[1] = false; + _bumperPressed[1] = false; + _oldX[1] = -1; + _oldY[1] = -1; } SixenseManager::~SixenseManager() { @@ -115,9 +119,9 @@ void SixenseManager::update(float deltaTime) { // Emulate the mouse so we can use scripts if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { // Check if we are on the correct palm - if ((Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded) && numActiveControllers == 1) || numActiveControllers == 2) { - emulateMouse(palm); - } + //if ((Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded) && numActiveControllers == 1) || numActiveControllers == 2) { + emulateMouse(palm, numActiveControllers - 1); + //} } // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters. @@ -328,30 +332,13 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers) } //Injecting mouse movements and clicks -void SixenseManager::emulateMouse(PalmData *palm) { +void SixenseManager::emulateMouse(PalmData* palm, int index) { MyAvatar* avatar = Application::getInstance()->getAvatar(); + QGLWidget* widget = Application::getInstance()->getGLWidget(); QPoint pos; // Get directon relative to avatar orientation glm::vec3 direction = glm::inverse(avatar->getOrientation()) * palm->getFingerDirection(); - // Get the angles, scaled between 0-1 - float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; - float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); - - float cursorRange = Application::getInstance()->getGLWidget()->width(); - - pos.setX(cursorRange * xAngle); - pos.setY(cursorRange * yAngle); - - //If position has changed, emit a mouse move to the application - if (pos.x() != _oldX || pos.y() != _oldY) { - QMouseEvent mouseEvent(static_cast(CONTROLLER_MOVE_EVENT), pos, Qt::NoButton, Qt::NoButton, 0); - - Application::getInstance()->mouseMoveEvent(&mouseEvent); - } - _oldX = pos.x(); - _oldY = pos.y(); - Qt::MouseButton bumperButton; Qt::MouseButton triggerButton; @@ -362,42 +349,79 @@ void SixenseManager::emulateMouse(PalmData *palm) { bumperButton = Qt::RightButton; triggerButton = Qt::LeftButton; } + + // Get the angles, scaled between 0-1 + float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; + float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); + + float cursorRange = widget->width(); + + + pos.setX(cursorRange * xAngle); + pos.setY(cursorRange * yAngle); + + //If we are off screen then we should stop processing, and if a trigger or bumper is pressed, + //we should unpress them. + if (pos.x() < 0 || pos.x() > widget->width() || pos.y() < 0 || pos.y() > widget->height()) { + if (_bumperPressed[index]) { + QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, bumperButton, bumperButton, 0); + + Application::getInstance()->mouseReleaseEvent(&mouseEvent); + + _bumperPressed[index] = false; + } + if (_triggerPressed[index]) { + QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, triggerButton, triggerButton, 0); + + Application::getInstance()->mouseReleaseEvent(&mouseEvent); + + _triggerPressed[index] = false; + } + return; + } + + //If position has changed, emit a mouse move to the application + if (pos.x() != _oldX[index] || pos.y() != _oldY[index]) { + QMouseEvent mouseEvent(static_cast(CONTROLLER_MOVE_EVENT), pos, Qt::NoButton, Qt::NoButton, 0); + + Application::getInstance()->mouseMoveEvent(&mouseEvent); + } + _oldX[index] = pos.x(); + _oldY[index] = pos.y(); //Check for bumper press ( Right Click ) if (palm->getControllerButtons() & BUTTON_FWD) { - if (!_bumperPressed) { - _bumperPressed = true; + if (!_bumperPressed[index]) { + _bumperPressed[index] = true; QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, bumperButton, bumperButton, 0); Application::getInstance()->mousePressEvent(&mouseEvent); } - } else if (_bumperPressed) { + } else if (_bumperPressed[index]) { QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, bumperButton, bumperButton, 0); Application::getInstance()->mouseReleaseEvent(&mouseEvent); - _bumperPressed = false; + _bumperPressed[index] = false; } //Check for trigger press ( Left Click ) if (palm->getTrigger() == 1.0f) { - if (!_triggerPressed) { - _triggerPressed = true; + if (!_triggerPressed[index]) { + _triggerPressed[index] = true; QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, triggerButton, triggerButton, 0); Application::getInstance()->mousePressEvent(&mouseEvent); } - } else if (_triggerPressed) { + } else if (_triggerPressed[index]) { QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, triggerButton, triggerButton, 0); Application::getInstance()->mouseReleaseEvent(&mouseEvent); - _triggerPressed = false; + _triggerPressed[index] = false; } - - } #endif // HAVE_SIXENSE diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index 93888ce944..f3c5633f90 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -47,7 +47,7 @@ public slots: private: #ifdef HAVE_SIXENSE void updateCalibration(const sixenseControllerData* controllers); - void emulateMouse(PalmData *palm); + void emulateMouse(PalmData* palm, int index); int _calibrationState; @@ -70,11 +70,11 @@ private: quint64 _lastMovement; glm::vec3 _amountMoved; - // for mouse emulation - bool _triggerPressed; - bool _bumperPressed; - int _oldX; - int _oldY; + // for mouse emulation with the two controllers + bool _triggerPressed[2]; + bool _bumperPressed[2]; + int _oldX[2]; + int _oldY[2]; }; #endif // hifi_SixenseManager_h diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 924c2e9d84..1deab117ac 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -45,70 +45,76 @@ void renderControllerPointer() { int palmIndex; - if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded)) { + /*if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded)) { palmIndex = 2; } else { palmIndex = 3; + }*/ + for (palmIndex = 2; palmIndex < 4; palmIndex++) { + const PalmData* palmData = NULL; + + if (palmIndex >= handData->getPalms().size()) { + return; + } + + if (handData->getPalms()[palmIndex].isActive()) { + palmData = &handData->getPalms()[palmIndex]; + } else { + return; + } + + // Get directon relative to avatar orientation + glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); + + // Get the angles, scaled between 0-1 + float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; + float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); + + float cursorRange = glWidget->width(); + + int mouseX = cursorRange * xAngle; + int mouseY = cursorRange * yAngle; + + if (mouseX < 0) { + //mouseX = 0; + continue; + } else if (mouseX > glWidget->width()) { + //mouseX = glWidget->width(); + continue; + } + if (mouseY < 0) { + //mouseY = 0; + continue; + } else if (mouseY > glWidget->width()) { + //mouseY = glWidget->width(); + continue; + } + + const float pointerWidth = 40; + const float pointerHeight = 40; + const float crossPad = 16; + + mouseX -= pointerWidth / 2.0f; + mouseY += pointerHeight / 2.0f; + + glBegin(GL_QUADS); + + glColor3f(0.0f, 0.0f, 1.0f); + + //Horizontal crosshair + glVertex2i(mouseX, mouseY - crossPad); + glVertex2i(mouseX + pointerWidth, mouseY - crossPad); + glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight + crossPad); + glVertex2i(mouseX, mouseY - pointerHeight + crossPad); + + //Vertical crosshair + glVertex2i(mouseX + crossPad, mouseY); + glVertex2i(mouseX + pointerWidth - crossPad, mouseY); + glVertex2i(mouseX + pointerWidth - crossPad, mouseY - pointerHeight); + glVertex2i(mouseX + crossPad, mouseY - pointerHeight); + + glEnd(); } - const PalmData* palmData = NULL; - - if (palmIndex >= handData->getPalms().size()) { - return; - } - - if (handData->getPalms()[palmIndex].isActive()) { - palmData = &handData->getPalms()[palmIndex]; - } else { - return; - } - - // Get directon relative to avatar orientation - glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); - - // Get the angles, scaled between 0-1 - float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; - float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); - - float cursorRange = glWidget->width(); - - int mouseX = cursorRange * xAngle; - int mouseY = cursorRange * yAngle; - - if (mouseX < 0) { - mouseX = 0; - } else if (mouseX > glWidget->width()) { - mouseX = glWidget->width(); - } - if (mouseY < 0) { - mouseY = 0; - } else if (mouseY > glWidget->width()) { - mouseY = glWidget->width(); - } - - const float pointerWidth = 40; - const float pointerHeight = 40; - const float crossPad = 16; - - mouseX -= pointerWidth / 2.0f; - mouseY += pointerHeight / 2.0f; - - glBegin(GL_QUADS); - - glColor3f(0, 0, 1); - - //Horizontal crosshair - glVertex2i(mouseX, mouseY - crossPad); - glVertex2i(mouseX + pointerWidth, mouseY - crossPad); - glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight + crossPad); - glVertex2i(mouseX, mouseY - pointerHeight + crossPad); - - //Vertical crosshair - glVertex2i(mouseX + crossPad, mouseY); - glVertex2i(mouseX + pointerWidth - crossPad, mouseY); - glVertex2i(mouseX + pointerWidth - crossPad, mouseY - pointerHeight); - glVertex2i(mouseX + crossPad, mouseY - pointerHeight); - - glEnd(); } // Renders the overlays either to a texture or to the screen From db94ae7449adeb9f117295567681b724197c7ccf Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 12 Jun 2014 10:27:54 -0700 Subject: [PATCH 033/102] Invert hydra move view and thrust controllers to match consoles --- examples/hydraMove.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/hydraMove.js b/examples/hydraMove.js index f211a450a3..675a885b6d 100644 --- a/examples/hydraMove.js +++ b/examples/hydraMove.js @@ -19,8 +19,8 @@ var damping = 0.9; var position = { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z }; var joysticksCaptured = false; -var THRUST_CONTROLLER = 1; -var VIEW_CONTROLLER = 0; +var THRUST_CONTROLLER = 0; +var VIEW_CONTROLLER = 1; var INITIAL_THRUST_MULTPLIER = 1.0; var THRUST_INCREASE_RATE = 1.05; var MAX_THRUST_MULTIPLIER = 75.0; From 762751ef6a35e19ed82f97aa1211209a1efd39e6 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 12 Jun 2014 10:47:11 -0700 Subject: [PATCH 034/102] Store the last mouse move input type for application overlay --- interface/src/Application.cpp | 4 ++++ interface/src/Application.h | 3 +++ 2 files changed, 7 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 24fcb3dc70..9f9cdb698c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -150,6 +150,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _mouseX(0), _mouseY(0), _lastMouseMove(usecTimestampNow()), + _lastMouseMoveType(QEvent::MouseMove), _mouseHidden(false), _seenMouseMove(false), _touchAvgX(0.0f), @@ -1096,6 +1097,9 @@ void Application::mouseMoveEvent(QMouseEvent* event) { showMouse = false; } + // Used by application overlay to determine how to draw cursor(s) + _lastMouseMoveType = event->type(); + _controllerScriptingInterface.emitMouseMoveEvent(event); // send events to any registered scripts // if one of our scripts have asked to capture this event, then stop processing it diff --git a/interface/src/Application.h b/interface/src/Application.h index 2889dcb301..536a54e65d 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -206,6 +206,7 @@ public: const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; } int getMouseX() const { return _mouseX; } int getMouseY() const { return _mouseY; } + unsigned int getLastMouseMoveType() const { return _lastMouseMoveType; } Faceplus* getFaceplus() { return &_faceplus; } Faceshift* getFaceshift() { return &_faceshift; } Visage* getVisage() { return &_visage; } @@ -505,6 +506,7 @@ private: int _mouseDragStartedX; int _mouseDragStartedY; quint64 _lastMouseMove; + unsigned int _lastMouseMoveType; bool _mouseHidden; bool _seenMouseMove; @@ -521,6 +523,7 @@ private: QSet _keysPressed; + GeometryCache _geometryCache; AnimationCache _animationCache; TextureCache _textureCache; From 18543ff3130a0051ab0d23a83b95b1dbd976e53e Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 12 Jun 2014 12:07:25 -0700 Subject: [PATCH 035/102] Support for multiple oculus overlay magnifiers --- interface/src/ui/ApplicationOverlay.cpp | 256 +++++++++++++----------- interface/src/ui/ApplicationOverlay.h | 4 + 2 files changed, 139 insertions(+), 121 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 1deab117ac..83b6dde93b 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -16,6 +16,7 @@ #include "Application.h" #include "ApplicationOverlay.h" +#include "devices/OculusManager.h" #include "ui/Stats.h" @@ -35,7 +36,7 @@ ApplicationOverlay::~ApplicationOverlay() { const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; -void renderControllerPointer() { +void ApplicationOverlay::renderControllerPointer() { Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); @@ -60,7 +61,7 @@ void renderControllerPointer() { if (handData->getPalms()[palmIndex].isActive()) { palmData = &handData->getPalms()[palmIndex]; } else { - return; + continue; } // Get directon relative to avatar orientation @@ -75,24 +76,25 @@ void renderControllerPointer() { int mouseX = cursorRange * xAngle; int mouseY = cursorRange * yAngle; - if (mouseX < 0) { - //mouseX = 0; - continue; - } else if (mouseX > glWidget->width()) { - //mouseX = glWidget->width(); - continue; - } - if (mouseY < 0) { - //mouseY = 0; - continue; - } else if (mouseY > glWidget->width()) { - //mouseY = glWidget->width(); + if (mouseX < 0 || mouseX >= glWidget->width() || mouseY < 0 || mouseY >= glWidget->height()) { continue; } - const float pointerWidth = 40; - const float pointerHeight = 40; - const float crossPad = 16; + float pointerWidth = 40; + float pointerHeight = 40; + float crossPad = 16; + //if we have the oculus, we should make the cursor smaller since it will be + //magnified + if (OculusManager::isConnected()) { + pointerWidth /= 4; + pointerHeight /= 4; + crossPad /= 4; + + _mouseX[_numMagnifiers] = mouseX; + _mouseY[_numMagnifiers] = mouseY; + _numMagnifiers++; + + } mouseX -= pointerWidth / 2.0f; mouseY += pointerHeight / 2.0f; @@ -131,6 +133,8 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { BandwidthMeter* bandwidthMeter = application->getBandwidthMeter(); NodeBounds& nodeBoundsDisplay = application->getNodeBoundsDisplay(); + _numMagnifiers = 0; + int mouseX = application->getMouseX(); int mouseY = application->getMouseY(); bool renderPointer = renderToTexture; @@ -310,15 +314,21 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { overlays.render2D(); - // Render a crosshair over the pointer when in Oculus - if (renderPointer) { + // Render a crosshair over the mouse when in Oculus + if (renderPointer && application->getLastMouseMoveType() == QEvent::MouseMove) { const float pointerWidth = 10; const float pointerHeight = 10; const float crossPad = 4; + + _numMagnifiers = 1; + _mouseX[0] = application->getMouseX(); + _mouseY[0] = application->getMouseY(); + mouseX -= pointerWidth / 2.0f; mouseY += pointerHeight / 2.0f; + glBegin(GL_QUADS); glColor3f(1, 0, 0); @@ -336,7 +346,7 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { glVertex2i(mouseX + crossPad, mouseY - pointerHeight); glEnd(); - } else if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { + } else if (application->getLastMouseMoveType() == CONTROLLER_MOVE_EVENT && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { //only render controller pointer if we aren't already rendering a mouse pointer renderControllerPointer(); } @@ -418,12 +428,8 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { MyAvatar* myAvatar = application->getAvatar(); const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); - int mouseX = application->getMouseX(); - int mouseY = application->getMouseY(); const int widgetWidth = glWidget->width(); const int widgetHeight = glWidget->height(); - float magnifyWidth = 80.0f; - float magnifyHeight = 60.0f; const float magnification = 4.0f; // Get vertical FoV of the displayed overlay texture @@ -480,114 +486,122 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.01f); - //Draw the magnifying glass - - mouseX -= magnifyWidth / 2; - mouseY -= magnifyHeight / 2; - - //clamp the magnification - if (mouseX < 0) { - magnifyWidth += mouseX; - mouseX = 0; - } else if (mouseX + magnifyWidth > widgetWidth) { - magnifyWidth = widgetWidth - mouseX; - } - if (mouseY < 0) { - magnifyHeight += mouseY; - mouseY = 0; - } else if (mouseY + magnifyHeight > widgetHeight) { - magnifyHeight = widgetHeight - mouseY; - } - - const float halfMagnifyHeight = magnifyHeight / 2.0f; - - float newWidth = magnifyWidth * magnification; - float newHeight = magnifyHeight * magnification; - - // Magnification Texture Coordinates - float magnifyULeft = mouseX / (float)widgetWidth; - float magnifyURight = (mouseX + magnifyWidth) / (float)widgetWidth; - float magnifyVBottom = 1.0f - mouseY / (float)widgetHeight; - float magnifyVTop = 1.0f - (mouseY + magnifyHeight) / (float)widgetHeight; - - // Coordinates of magnification overlay - float newMouseX = (mouseX + magnifyWidth / 2) - newWidth / 2.0f; - float newMouseY = (mouseY + magnifyHeight / 2) + newHeight / 2.0f; - - // Get angle on the UI - float leftAngle = (newMouseX / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; - float rightAngle = ((newMouseX + newWidth) / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; - - float bottomAngle = (newMouseY / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; - float topAngle = ((newMouseY - newHeight) / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; - float leftX, rightX, leftZ, rightZ, topZ, bottomZ; - - // Get position on hemisphere using angle - if (_uiType == HEMISPHERE) { - //Get new UV coordinates from our magnification window - float newULeft = newMouseX / widgetWidth; - float newURight = (newMouseX + newWidth) / widgetWidth; - float newVBottom = 1.0 - newMouseY / widgetHeight; - float newVTop = 1.0 - (newMouseY - newHeight) / widgetHeight; + //Draw the magnifiers + for (int i = 0; i < _numMagnifiers; i++) { - // Project our position onto the hemisphere using the UV coordinates - float lX = sin((newULeft - 0.5f) * textureFov); - float rX = sin((newURight - 0.5f) * textureFov); - float bY = sin((newVBottom - 0.5f) * textureFov); - float tY = sin((newVTop - 0.5f) * textureFov); - - float dist; - //Bottom Left - dist = sqrt(lX * lX + bY * bY); - float blZ = sqrt(1.0f - dist * dist); - //Top Left - dist = sqrt(lX * lX + tY * tY); - float tlZ = sqrt(1.0f - dist * dist); - //Bottom Right - dist = sqrt(rX * rX + bY * bY); - float brZ = sqrt(1.0f - dist * dist); - //Top Right - dist = sqrt(rX * rX + tY * tY); - float trZ = sqrt(1.0f - dist * dist); + float magnifyWidth = 80.0f; + float magnifyHeight = 60.0f; - glBegin(GL_QUADS); + int mouseX = _mouseX[i]; + int mouseY = _mouseY[i]; + mouseX -= magnifyWidth / 2; + mouseY -= magnifyHeight / 2; - glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(lX, tY, -tlZ); - glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rX, tY, -trZ); - glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rX, bY, -brZ); - glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(lX, bY, -blZ); - - glEnd(); - - } else { - leftX = sin(leftAngle) * _distance; - rightX = sin(rightAngle) * _distance; - leftZ = -cos(leftAngle) * _distance; - rightZ = -cos(rightAngle) * _distance; - if (_uiType == CURVED_SEMICIRCLE) { - topZ = -cos(topAngle * overlayAspectRatio) * _distance; - bottomZ = -cos(bottomAngle * overlayAspectRatio) * _distance; - } else { - // Dont want to use topZ or bottomZ for SEMICIRCLE - topZ = -99999; - bottomZ = -99999; + //clamp the magnification + if (mouseX < 0) { + magnifyWidth += mouseX; + mouseX = 0; + } else if (mouseX + magnifyWidth > widgetWidth) { + magnifyWidth = widgetWidth - mouseX; + } + if (mouseY < 0) { + magnifyHeight += mouseY; + mouseY = 0; + } else if (mouseY + magnifyHeight > widgetHeight) { + magnifyHeight = widgetHeight - mouseY; } - float bottomY = (1.0 - newMouseY / (float)widgetHeight) * halfOverlayHeight * 2.0f - halfOverlayHeight; - float topY = bottomY + (newHeight / widgetHeight) * halfOverlayHeight * 2; + const float halfMagnifyHeight = magnifyHeight / 2.0f; - //TODO: Remove immediate mode in favor of VBO - glBegin(GL_QUADS); + float newWidth = magnifyWidth * magnification; + float newHeight = magnifyHeight * magnification; - glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(leftX, topY, max(topZ, leftZ)); - glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rightX, topY, max(topZ, rightZ)); - glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rightX, bottomY, max(bottomZ, rightZ)); - glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(leftX, bottomY, max(bottomZ, leftZ)); + // Magnification Texture Coordinates + float magnifyULeft = mouseX / (float)widgetWidth; + float magnifyURight = (mouseX + magnifyWidth) / (float)widgetWidth; + float magnifyVBottom = 1.0f - mouseY / (float)widgetHeight; + float magnifyVTop = 1.0f - (mouseY + magnifyHeight) / (float)widgetHeight; - glEnd(); + // Coordinates of magnification overlay + float newMouseX = (mouseX + magnifyWidth / 2) - newWidth / 2.0f; + float newMouseY = (mouseY + magnifyHeight / 2) + newHeight / 2.0f; + + // Get angle on the UI + float leftAngle = (newMouseX / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; + float rightAngle = ((newMouseX + newWidth) / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; + + float bottomAngle = (newMouseY / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; + float topAngle = ((newMouseY - newHeight) / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; + + // Get position on hemisphere using angle + if (_uiType == HEMISPHERE) { + + //Get new UV coordinates from our magnification window + float newULeft = newMouseX / widgetWidth; + float newURight = (newMouseX + newWidth) / widgetWidth; + float newVBottom = 1.0 - newMouseY / widgetHeight; + float newVTop = 1.0 - (newMouseY - newHeight) / widgetHeight; + + // Project our position onto the hemisphere using the UV coordinates + float lX = sin((newULeft - 0.5f) * textureFov); + float rX = sin((newURight - 0.5f) * textureFov); + float bY = sin((newVBottom - 0.5f) * textureFov); + float tY = sin((newVTop - 0.5f) * textureFov); + + float dist; + //Bottom Left + dist = sqrt(lX * lX + bY * bY); + float blZ = sqrt(1.0f - dist * dist); + //Top Left + dist = sqrt(lX * lX + tY * tY); + float tlZ = sqrt(1.0f - dist * dist); + //Bottom Right + dist = sqrt(rX * rX + bY * bY); + float brZ = sqrt(1.0f - dist * dist); + //Top Right + dist = sqrt(rX * rX + tY * tY); + float trZ = sqrt(1.0f - dist * dist); + + glBegin(GL_QUADS); + + glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(lX, tY, -tlZ); + glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rX, tY, -trZ); + glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rX, bY, -brZ); + glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(lX, bY, -blZ); + + glEnd(); + + } else { + leftX = sin(leftAngle) * _distance; + rightX = sin(rightAngle) * _distance; + leftZ = -cos(leftAngle) * _distance; + rightZ = -cos(rightAngle) * _distance; + if (_uiType == CURVED_SEMICIRCLE) { + topZ = -cos(topAngle * overlayAspectRatio) * _distance; + bottomZ = -cos(bottomAngle * overlayAspectRatio) * _distance; + } else { + // Dont want to use topZ or bottomZ for SEMICIRCLE + topZ = -99999; + bottomZ = -99999; + } + + float bottomY = (1.0 - newMouseY / (float)widgetHeight) * halfOverlayHeight * 2.0f - halfOverlayHeight; + float topY = bottomY + (newHeight / widgetHeight) * halfOverlayHeight * 2; + + //TODO: Remove immediate mode in favor of VBO + glBegin(GL_QUADS); + + glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(leftX, topY, max(topZ, leftZ)); + glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rightX, topY, max(topZ, rightZ)); + glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rightX, bottomY, max(bottomZ, rightZ)); + glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(leftX, bottomY, max(bottomZ, leftZ)); + + glEnd(); + } } + glDepthMask(GL_FALSE); glDisable(GL_ALPHA_TEST); diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 1bf0e18816..6dbcaa7afb 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -46,6 +46,7 @@ private: typedef QPair VerticesIndices; + void renderControllerPointer(); void renderTexturedHemisphere(); QOpenGLFramebufferObject* _framebufferObject; @@ -53,6 +54,9 @@ private: float _oculusAngle; float _distance; UIType _uiType; + int _mouseX[2]; + int _mouseY[2]; + int _numMagnifiers; }; #endif // hifi_ApplicationOverlay_h \ No newline at end of file From bc293071d6193d68734c0cc5a152046caae76d44 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 12 Jun 2014 12:25:22 -0700 Subject: [PATCH 036/102] Removed left handed menu option --- interface/src/Menu.cpp | 1 - interface/src/Menu.h | 1 - interface/src/devices/SixenseManager.cpp | 3 --- interface/src/ui/ApplicationOverlay.cpp | 10 ++-------- 4 files changed, 2 insertions(+), 13 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 6cf99418e3..0c6a3efa24 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -382,7 +382,6 @@ Menu::Menu() : QMenu* sixenseOptionsMenu = developerMenu->addMenu("Sixense Options"); addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true); - addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseLeftHanded, 0, false); addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseInvertInputButtons, 0, false); QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 8415fb68c4..47ada75287 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -400,7 +400,6 @@ namespace MenuOption { const QString SettingsImport = "Import Settings"; const QString SimpleShadows = "Simple"; const QString SixenseInvertInputButtons = "Invert Sixense Mouse Input Buttons"; - const QString SixenseLeftHanded = "Left Handed Sixense Mouse Input"; const QString SixenseMouseInput = "Enable Sixense Mouse Input"; const QString ShowBordersVoxelNodes = "Show Voxel Nodes"; const QString ShowBordersModelNodes = "Show Model Nodes"; diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 37bad28093..bce55ae362 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -118,10 +118,7 @@ void SixenseManager::update(float deltaTime) { // Emulate the mouse so we can use scripts if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { - // Check if we are on the correct palm - //if ((Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded) && numActiveControllers == 1) || numActiveControllers == 2) { emulateMouse(palm, numActiveControllers - 1); - //} } // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters. diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 83b6dde93b..873f7c29f3 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -44,14 +44,7 @@ void ApplicationOverlay::renderControllerPointer() { const HandData* handData = Application::getInstance()->getAvatar()->getHandData(); int numberOfPalms = handData->getNumPalms(); - - int palmIndex; - /*if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLeftHanded)) { - palmIndex = 2; - } else { - palmIndex = 3; - }*/ - for (palmIndex = 2; palmIndex < 4; palmIndex++) { + for (int palmIndex = 2; palmIndex < 4; palmIndex++) { const PalmData* palmData = NULL; if (palmIndex >= handData->getPalms().size()) { @@ -76,6 +69,7 @@ void ApplicationOverlay::renderControllerPointer() { int mouseX = cursorRange * xAngle; int mouseY = cursorRange * yAngle; + //If the cursor is out of the screen then don't render it if (mouseX < 0 || mouseX >= glWidget->width() || mouseY < 0 || mouseY >= glWidget->height()) { continue; } From d689c6b39fe31753cd4b5b5519d34cd566516e63 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Jun 2014 13:15:54 -0700 Subject: [PATCH 037/102] fix reference to attribute in STUN packet --- libraries/networking/src/NodeList.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 9a298ce26c..b872ec12cf 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -281,7 +281,7 @@ void NodeList::processSTUNResponse(const QByteArray& packet) { int byteIndex = attributeStartIndex + NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH + NUM_BYTES_FAMILY_ALIGN; uint8_t addressFamily = 0; - memcpy(&addressFamily, packet.data(), sizeof(addressFamily)); + memcpy(&addressFamily, packet.data() + byteIndex, sizeof(addressFamily)); byteIndex += sizeof(addressFamily); From a13ef5c3bf4e4dc82cb49f66e32592de3c7f2a0b Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 12 Jun 2014 13:19:02 -0700 Subject: [PATCH 038/102] Made applicationoverlay more readable --- interface/src/ui/ApplicationOverlay.cpp | 812 +++++++++++++----------- interface/src/ui/ApplicationOverlay.h | 5 + 2 files changed, 439 insertions(+), 378 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 873f7c29f3..9af3ed6d02 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -20,10 +20,20 @@ #include "ui/Stats.h" +// Fast helper functions +inline float max(float a, float b) { + return (a > b) ? a : b; +} + +inline float min(float a, float b) { + return (a < b) ? a : b; +} + ApplicationOverlay::ApplicationOverlay() : _framebufferObject(NULL), _oculusAngle(65.0f * RADIANS_PER_DEGREE), _distance(0.5f), + _textureFov(PI / 2.5f), _uiType(HEMISPHERE) { } @@ -36,6 +46,273 @@ ApplicationOverlay::~ApplicationOverlay() { const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; +// Renders the overlays either to a texture or to the screen +void ApplicationOverlay::renderOverlay(bool renderToTexture) { + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); + + Application* application = Application::getInstance(); + + Overlays& overlays = application->getOverlays(); + QGLWidget* glWidget = application->getGLWidget(); + MyAvatar* myAvatar = application->getAvatar(); + + if (renderToTexture) { + getFramebufferObject()->bind(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // Render 2D overlay + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + + glLoadIdentity(); + gluOrtho2D(0, glWidget->width(), glWidget->height(), 0); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + + renderAudioMeter(); + + if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse)) { + myAvatar->renderHeadMouse(glWidget->width(), glWidget->height()); + } + + renderStatsAndLogs(); + + // give external parties a change to hook in + emit application->renderingOverlay(); + + overlays.render2D(); + + renderPointers(); + + glPopMatrix(); + + glMatrixMode(GL_MODELVIEW); + glEnable(GL_DEPTH_TEST); + glEnable(GL_LIGHTING); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + + if (renderToTexture) { + getFramebufferObject()->release(); + } +} + +// Draws the FBO texture for the screen +void ApplicationOverlay::displayOverlayTexture(Camera& whichCamera) { + + Application* application = Application::getInstance(); + QGLWidget* glWidget = application->getGLWidget(); + + glEnable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, getFramebufferObject()->texture()); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + + glLoadIdentity(); + gluOrtho2D(0, glWidget->width(), glWidget->height(), 0); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + + glBegin(GL_QUADS); + glTexCoord2f(0, 0); glVertex2i(0, glWidget->height()); + glTexCoord2f(1, 0); glVertex2i(glWidget->width(), glWidget->height()); + glTexCoord2f(1, 1); glVertex2i(glWidget->width(), 0); + glTexCoord2f(0, 1); glVertex2i(0, 0); + glEnd(); + + glPopMatrix(); + glDisable(GL_TEXTURE_2D); +} + +void ApplicationOverlay::computeOculusPickRay(float x, float y, glm::vec3& direction) const { + glm::quat rot = Application::getInstance()->getAvatar()->getOrientation(); + + //invert y direction + y = 1.0 - y; + + //Get position on hemisphere UI + x = sin((x - 0.5f) * _textureFov); + y = sin((y - 0.5f) * _textureFov); + + float dist = sqrt(x * x + y * y); + float z = -sqrt(1.0f - dist * dist); + + //Rotate the UI pick ray by the avatar orientation + direction = glm::normalize(rot * glm::vec3(x, y, z)); +} + +// Draws the FBO texture for Oculus rift. TODO: Draw a curved texture instead of plane. +void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { + + Application* application = Application::getInstance(); + + QGLWidget* glWidget = application->getGLWidget(); + MyAvatar* myAvatar = application->getAvatar(); + const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); + + const int widgetWidth = glWidget->width(); + const int widgetHeight = glWidget->height(); + const float magnification = 4.0f; + + // Get vertical FoV of the displayed overlay texture + const float halfVerticalAngle = _oculusAngle / 2.0f; + const float overlayAspectRatio = glWidget->width() / (float)glWidget->height(); + const float halfOverlayHeight = _distance * tan(halfVerticalAngle); + const float overlayHeight = halfOverlayHeight * 2.0f; + + // The more vertices, the better the curve + const int numHorizontalVertices = 20; + const int numVerticalVertices = 20; + // U texture coordinate width at each quad + const float quadTexWidth = 1.0f / (numHorizontalVertices - 1); + const float quadTexHeight = 1.0f / (numVerticalVertices - 1); + + // Get horizontal angle and angle increment from vertical angle and aspect ratio + const float horizontalAngle = halfVerticalAngle * 2.0f * overlayAspectRatio; + const float angleIncrement = horizontalAngle / (numHorizontalVertices - 1); + const float halfHorizontalAngle = horizontalAngle / 2; + + const float verticalAngleIncrement = _oculusAngle / (numVerticalVertices - 1); + + glActiveTexture(GL_TEXTURE0); + + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + glBindTexture(GL_TEXTURE_2D, getFramebufferObject()->texture()); + glEnable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glEnable(GL_TEXTURE_2D); + + 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); + + glColor3f(1.0f, 1.0f, 1.0f); + + glDepthMask(GL_TRUE); + + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.01f); + + float leftX, rightX, leftZ, rightZ, topZ, bottomZ; + + //Draw the magnifiers + for (int i = 0; i < _numMagnifiers; i++) { + renderMagnifier(_mouseX[i], _mouseY[i]); + } + + glDepthMask(GL_FALSE); + glDisable(GL_ALPHA_TEST); + + //TODO: Remove immediate mode in favor of VBO + if (_uiType == HEMISPHERE) { + renderTexturedHemisphere(); + } else{ + glBegin(GL_QUADS); + // Place the vertices in a semicircle curve around the camera + for (int i = 0; i < numHorizontalVertices - 1; i++) { + for (int j = 0; j < numVerticalVertices - 1; j++) { + + // Calculate the X and Z coordinates from the angles and radius from camera + leftX = sin(angleIncrement * i - halfHorizontalAngle) * _distance; + rightX = sin(angleIncrement * (i + 1) - halfHorizontalAngle) * _distance; + leftZ = -cos(angleIncrement * i - halfHorizontalAngle) * _distance; + rightZ = -cos(angleIncrement * (i + 1) - halfHorizontalAngle) * _distance; + if (_uiType == 2) { + topZ = -cos((verticalAngleIncrement * (j + 1) - halfVerticalAngle) * overlayAspectRatio) * _distance; + bottomZ = -cos((verticalAngleIncrement * j - halfVerticalAngle) * overlayAspectRatio) * _distance; + } else { + topZ = -99999; + bottomZ = -99999; + } + + glTexCoord2f(quadTexWidth * i, (j + 1) * quadTexHeight); + glVertex3f(leftX, (j + 1) * quadTexHeight * overlayHeight - halfOverlayHeight, max(topZ, leftZ)); + glTexCoord2f(quadTexWidth * (i + 1), (j + 1) * quadTexHeight); + glVertex3f(rightX, (j + 1) * quadTexHeight * overlayHeight - halfOverlayHeight, max(topZ, rightZ)); + glTexCoord2f(quadTexWidth * (i + 1), j * quadTexHeight); + glVertex3f(rightX, j * quadTexHeight * overlayHeight - halfOverlayHeight, max(bottomZ, rightZ)); + glTexCoord2f(quadTexWidth * i, j * quadTexHeight); + glVertex3f(leftX, j * quadTexHeight * overlayHeight - halfOverlayHeight, max(bottomZ, leftZ)); + } + } + + glEnd(); + } + + 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); + +} + +//Renders optional pointers +void ApplicationOverlay::renderPointers() { + Application* application = Application::getInstance(); + // Render a crosshair over the mouse when in Oculus + _numMagnifiers = 0; + int mouseX = application->getMouseX(); + int mouseY = application->getMouseY(); + + if (OculusManager::isConnected() && application->getLastMouseMoveType() == QEvent::MouseMove) { + const float pointerWidth = 10; + const float pointerHeight = 10; + const float crossPad = 4; + + _numMagnifiers = 1; + _mouseX[0] = application->getMouseX(); + _mouseY[0] = application->getMouseY(); + + mouseX -= pointerWidth / 2.0f; + mouseY += pointerHeight / 2.0f; + + + glBegin(GL_QUADS); + + glColor3f(1, 0, 0); + + //Horizontal crosshair + glVertex2i(mouseX, mouseY - crossPad); + glVertex2i(mouseX + pointerWidth, mouseY - crossPad); + glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight + crossPad); + glVertex2i(mouseX, mouseY - pointerHeight + crossPad); + + //Vertical crosshair + glVertex2i(mouseX + crossPad, mouseY); + glVertex2i(mouseX + pointerWidth - crossPad, mouseY); + glVertex2i(mouseX + pointerWidth - crossPad, mouseY - pointerHeight); + glVertex2i(mouseX + crossPad, mouseY - pointerHeight); + + glEnd(); + } else if (application->getLastMouseMoveType() == CONTROLLER_MOVE_EVENT && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { + //only render controller pointer if we aren't already rendering a mouse pointer + renderControllerPointer(); + } +} + void ApplicationOverlay::renderControllerPointer() { Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); @@ -87,7 +364,7 @@ void ApplicationOverlay::renderControllerPointer() { _mouseX[_numMagnifiers] = mouseX; _mouseY[_numMagnifiers] = mouseY; _numMagnifiers++; - + } mouseX -= pointerWidth / 2.0f; @@ -113,41 +390,154 @@ void ApplicationOverlay::renderControllerPointer() { } } -// Renders the overlays either to a texture or to the screen -void ApplicationOverlay::renderOverlay(bool renderToTexture) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); +//Renders a small magnification of the currently bound texture at the coordinates +void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) +{ + Application* application = Application::getInstance(); + QGLWidget* glWidget = application->getGLWidget(); + MyAvatar* myAvatar = application->getAvatar(); + const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); + + float leftX, rightX, leftZ, rightZ, topZ, bottomZ; + + const int widgetWidth = glWidget->width(); + const int widgetHeight = glWidget->height(); + const float magnification = 4.0f; + + // Get vertical FoV of the displayed overlay texture + const float halfVerticalAngle = _oculusAngle / 2.0f; + const float overlayAspectRatio = glWidget->width() / (float)glWidget->height(); + const float halfOverlayHeight = _distance * tan(halfVerticalAngle); + const float overlayHeight = halfOverlayHeight * 2.0f; + + // The more vertices, the better the curve + const int numHorizontalVertices = 20; + const int numVerticalVertices = 20; + // U texture coordinate width at each quad + const float quadTexWidth = 1.0f / (numHorizontalVertices - 1); + const float quadTexHeight = 1.0f / (numVerticalVertices - 1); + + // Get horizontal angle and angle increment from vertical angle and aspect ratio + const float horizontalAngle = halfVerticalAngle * 2.0f * overlayAspectRatio; + const float angleIncrement = horizontalAngle / (numHorizontalVertices - 1); + const float halfHorizontalAngle = horizontalAngle / 2; + + + float magnifyWidth = 80.0f; + float magnifyHeight = 60.0f; + + mouseX -= magnifyWidth / 2; + mouseY -= magnifyHeight / 2; + + //clamp the magnification + if (mouseX < 0) { + magnifyWidth += mouseX; + mouseX = 0; + } else if (mouseX + magnifyWidth > widgetWidth) { + magnifyWidth = widgetWidth - mouseX; + } + if (mouseY < 0) { + magnifyHeight += mouseY; + mouseY = 0; + } else if (mouseY + magnifyHeight > widgetHeight) { + magnifyHeight = widgetHeight - mouseY; + } + + const float halfMagnifyHeight = magnifyHeight / 2.0f; + + float newWidth = magnifyWidth * magnification; + float newHeight = magnifyHeight * magnification; + + // Magnification Texture Coordinates + float magnifyULeft = mouseX / (float)widgetWidth; + float magnifyURight = (mouseX + magnifyWidth) / (float)widgetWidth; + float magnifyVBottom = 1.0f - mouseY / (float)widgetHeight; + float magnifyVTop = 1.0f - (mouseY + magnifyHeight) / (float)widgetHeight; + + // Coordinates of magnification overlay + float newMouseX = (mouseX + magnifyWidth / 2) - newWidth / 2.0f; + float newMouseY = (mouseY + magnifyHeight / 2) + newHeight / 2.0f; + + // Get angle on the UI + float leftAngle = (newMouseX / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; + float rightAngle = ((newMouseX + newWidth) / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; + + float bottomAngle = (newMouseY / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; + float topAngle = ((newMouseY - newHeight) / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; + + // Get position on hemisphere using angle + if (_uiType == HEMISPHERE) { + + //Get new UV coordinates from our magnification window + float newULeft = newMouseX / widgetWidth; + float newURight = (newMouseX + newWidth) / widgetWidth; + float newVBottom = 1.0 - newMouseY / widgetHeight; + float newVTop = 1.0 - (newMouseY - newHeight) / widgetHeight; + + // Project our position onto the hemisphere using the UV coordinates + float lX = sin((newULeft - 0.5f) * _textureFov); + float rX = sin((newURight - 0.5f) * _textureFov); + float bY = sin((newVBottom - 0.5f) * _textureFov); + float tY = sin((newVTop - 0.5f) * _textureFov); + + float dist; + //Bottom Left + dist = sqrt(lX * lX + bY * bY); + float blZ = sqrt(1.0f - dist * dist); + //Top Left + dist = sqrt(lX * lX + tY * tY); + float tlZ = sqrt(1.0f - dist * dist); + //Bottom Right + dist = sqrt(rX * rX + bY * bY); + float brZ = sqrt(1.0f - dist * dist); + //Top Right + dist = sqrt(rX * rX + tY * tY); + float trZ = sqrt(1.0f - dist * dist); + + glBegin(GL_QUADS); + + glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(lX, tY, -tlZ); + glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rX, tY, -trZ); + glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rX, bY, -brZ); + glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(lX, bY, -blZ); + + glEnd(); + + } else { + leftX = sin(leftAngle) * _distance; + rightX = sin(rightAngle) * _distance; + leftZ = -cos(leftAngle) * _distance; + rightZ = -cos(rightAngle) * _distance; + if (_uiType == CURVED_SEMICIRCLE) { + topZ = -cos(topAngle * overlayAspectRatio) * _distance; + bottomZ = -cos(bottomAngle * overlayAspectRatio) * _distance; + } else { + // Dont want to use topZ or bottomZ for SEMICIRCLE + topZ = -99999; + bottomZ = -99999; + } + + float bottomY = (1.0 - newMouseY / (float)widgetHeight) * halfOverlayHeight * 2.0f - halfOverlayHeight; + float topY = bottomY + (newHeight / widgetHeight) * halfOverlayHeight * 2; + + //TODO: Remove immediate mode in favor of VBO + glBegin(GL_QUADS); + + glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(leftX, topY, max(topZ, leftZ)); + glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rightX, topY, max(topZ, rightZ)); + glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rightX, bottomY, max(bottomZ, rightZ)); + glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(leftX, bottomY, max(bottomZ, leftZ)); + + glEnd(); + } +} + +void ApplicationOverlay::renderAudioMeter() { Application* application = Application::getInstance(); - Overlays& overlays = application->getOverlays(); QGLWidget* glWidget = application->getGLWidget(); - MyAvatar* myAvatar = application->getAvatar(); Audio* audio = application->getAudio(); - const OctreePacketProcessor& octreePacketProcessor = application->getOctreePacketProcessor(); - BandwidthMeter* bandwidthMeter = application->getBandwidthMeter(); - NodeBounds& nodeBoundsDisplay = application->getNodeBoundsDisplay(); - - _numMagnifiers = 0; - - int mouseX = application->getMouseX(); - int mouseY = application->getMouseY(); - bool renderPointer = renderToTexture; - - if (renderToTexture) { - getFramebufferObject()->bind(); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - } - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - // Render 2D overlay: I/O level bar graphs and text - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - - glLoadIdentity(); - gluOrtho2D(0, glWidget->width(), glWidget->height(), 0); - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); // Display a single screen-size quad to create an alpha blended 'collision' flash if (audio->getCollisionFlashesScreen()) { @@ -267,11 +657,16 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET + audioLevel, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); glVertex2i(AUDIO_METER_X + AUDIO_METER_INSET, audioMeterY + AUDIO_METER_HEIGHT - AUDIO_METER_INSET); glEnd(); +} +void ApplicationOverlay::renderStatsAndLogs() { - if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse)) { - myAvatar->renderHeadMouse(glWidget->width(), glWidget->height()); - } + Application* application = Application::getInstance(); + + QGLWidget* glWidget = application->getGLWidget(); + const OctreePacketProcessor& octreePacketProcessor = application->getOctreePacketProcessor(); + BandwidthMeter* bandwidthMeter = application->getBandwidthMeter(); + NodeBounds& nodeBoundsDisplay = application->getNodeBoundsDisplay(); // Display stats and log text onscreen glLineWidth(1.0f); @@ -302,357 +697,18 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { drawText(glWidget->width() - 100, glWidget->height() - timerBottom, 0.30f, 0.0f, 0, frameTimer, WHITE_TEXT); } nodeBoundsDisplay.drawOverlay(); - - // give external parties a change to hook in - emit application->renderingOverlay(); - - overlays.render2D(); - - // Render a crosshair over the mouse when in Oculus - if (renderPointer && application->getLastMouseMoveType() == QEvent::MouseMove) { - const float pointerWidth = 10; - const float pointerHeight = 10; - const float crossPad = 4; - - - _numMagnifiers = 1; - _mouseX[0] = application->getMouseX(); - _mouseY[0] = application->getMouseY(); - - mouseX -= pointerWidth / 2.0f; - mouseY += pointerHeight / 2.0f; - - - glBegin(GL_QUADS); - - glColor3f(1, 0, 0); - - //Horizontal crosshair - glVertex2i(mouseX, mouseY - crossPad); - glVertex2i(mouseX + pointerWidth, mouseY - crossPad); - glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight + crossPad); - glVertex2i(mouseX, mouseY - pointerHeight + crossPad); - - //Vertical crosshair - glVertex2i(mouseX + crossPad, mouseY); - glVertex2i(mouseX + pointerWidth - crossPad, mouseY); - glVertex2i(mouseX + pointerWidth - crossPad, mouseY - pointerHeight); - glVertex2i(mouseX + crossPad, mouseY - pointerHeight); - - glEnd(); - } else if (application->getLastMouseMoveType() == CONTROLLER_MOVE_EVENT && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { - //only render controller pointer if we aren't already rendering a mouse pointer - renderControllerPointer(); - } - glPopMatrix(); - - glMatrixMode(GL_MODELVIEW); - glEnable(GL_DEPTH_TEST); - glEnable(GL_LIGHTING); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - - if (renderToTexture) { - getFramebufferObject()->release(); - } -} - -// Draws the FBO texture for the screen -void ApplicationOverlay::displayOverlayTexture(Camera& whichCamera) { - - Application* application = Application::getInstance(); - QGLWidget* glWidget = application->getGLWidget(); - - glEnable(GL_TEXTURE_2D); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, getFramebufferObject()->texture()); - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - - glLoadIdentity(); - gluOrtho2D(0, glWidget->width(), glWidget->height(), 0); - glDisable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - - glBegin(GL_QUADS); - glTexCoord2f(0, 0); glVertex2i(0, glWidget->height()); - glTexCoord2f(1, 0); glVertex2i(glWidget->width(), glWidget->height()); - glTexCoord2f(1, 1); glVertex2i(glWidget->width(), 0); - glTexCoord2f(0, 1); glVertex2i(0, 0); - glEnd(); - - glPopMatrix(); - glDisable(GL_TEXTURE_2D); -} - -const float textureFov = PI / 2.5f; - -void ApplicationOverlay::computeOculusPickRay(float x, float y, glm::vec3& direction) const { - glm::quat rot = Application::getInstance()->getAvatar()->getOrientation(); - - //invert y direction - y = 1.0 - y; - - //Get position on hemisphere UI - x = sin((x - 0.5f) * textureFov); - y = sin((y - 0.5f) * textureFov); - - float dist = sqrt(x * x + y * y); - float z = -sqrt(1.0f - dist * dist); - - //Rotate the UI pick ray by the avatar orientation - direction = glm::normalize(rot * glm::vec3(x, y, z)); -} - -// Fast helper functions -inline float max(float a, float b) { - return (a > b) ? a : b; -} - -inline float min(float a, float b) { - return (a < b) ? a : b; -} - -// Draws the FBO texture for Oculus rift. TODO: Draw a curved texture instead of plane. -void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { - - Application* application = Application::getInstance(); - - QGLWidget* glWidget = application->getGLWidget(); - MyAvatar* myAvatar = application->getAvatar(); - const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); - - const int widgetWidth = glWidget->width(); - const int widgetHeight = glWidget->height(); - const float magnification = 4.0f; - - // Get vertical FoV of the displayed overlay texture - const float halfVerticalAngle = _oculusAngle / 2.0f; - const float overlayAspectRatio = glWidget->width() / (float)glWidget->height(); - const float halfOverlayHeight = _distance * tan(halfVerticalAngle); - const float overlayHeight = halfOverlayHeight * 2.0f; - - // The more vertices, the better the curve - const int numHorizontalVertices = 20; - const int numVerticalVertices = 20; - // U texture coordinate width at each quad - const float quadTexWidth = 1.0f / (numHorizontalVertices - 1); - const float quadTexHeight = 1.0f / (numVerticalVertices - 1); - - // Get horizontal angle and angle increment from vertical angle and aspect ratio - const float horizontalAngle = halfVerticalAngle * 2.0f * overlayAspectRatio; - const float angleIncrement = horizontalAngle / (numHorizontalVertices - 1); - const float halfHorizontalAngle = horizontalAngle / 2; - - const float verticalAngleIncrement = _oculusAngle / (numVerticalVertices - 1); - - glActiveTexture(GL_TEXTURE0); - - glEnable(GL_BLEND); - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); - glBindTexture(GL_TEXTURE_2D, getFramebufferObject()->texture()); - glEnable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); - glEnable(GL_TEXTURE_2D); - - 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); - - glColor3f(1.0f, 1.0f, 1.0f); - - glDepthMask(GL_TRUE); - - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.01f); - - float leftX, rightX, leftZ, rightZ, topZ, bottomZ; - - //Draw the magnifiers - for (int i = 0; i < _numMagnifiers; i++) { - - float magnifyWidth = 80.0f; - float magnifyHeight = 60.0f; - - int mouseX = _mouseX[i]; - int mouseY = _mouseY[i]; - mouseX -= magnifyWidth / 2; - mouseY -= magnifyHeight / 2; - - //clamp the magnification - if (mouseX < 0) { - magnifyWidth += mouseX; - mouseX = 0; - } else if (mouseX + magnifyWidth > widgetWidth) { - magnifyWidth = widgetWidth - mouseX; - } - if (mouseY < 0) { - magnifyHeight += mouseY; - mouseY = 0; - } else if (mouseY + magnifyHeight > widgetHeight) { - magnifyHeight = widgetHeight - mouseY; - } - - const float halfMagnifyHeight = magnifyHeight / 2.0f; - - float newWidth = magnifyWidth * magnification; - float newHeight = magnifyHeight * magnification; - - // Magnification Texture Coordinates - float magnifyULeft = mouseX / (float)widgetWidth; - float magnifyURight = (mouseX + magnifyWidth) / (float)widgetWidth; - float magnifyVBottom = 1.0f - mouseY / (float)widgetHeight; - float magnifyVTop = 1.0f - (mouseY + magnifyHeight) / (float)widgetHeight; - - // Coordinates of magnification overlay - float newMouseX = (mouseX + magnifyWidth / 2) - newWidth / 2.0f; - float newMouseY = (mouseY + magnifyHeight / 2) + newHeight / 2.0f; - - // Get angle on the UI - float leftAngle = (newMouseX / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; - float rightAngle = ((newMouseX + newWidth) / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; - - float bottomAngle = (newMouseY / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; - float topAngle = ((newMouseY - newHeight) / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; - - // Get position on hemisphere using angle - if (_uiType == HEMISPHERE) { - - //Get new UV coordinates from our magnification window - float newULeft = newMouseX / widgetWidth; - float newURight = (newMouseX + newWidth) / widgetWidth; - float newVBottom = 1.0 - newMouseY / widgetHeight; - float newVTop = 1.0 - (newMouseY - newHeight) / widgetHeight; - - // Project our position onto the hemisphere using the UV coordinates - float lX = sin((newULeft - 0.5f) * textureFov); - float rX = sin((newURight - 0.5f) * textureFov); - float bY = sin((newVBottom - 0.5f) * textureFov); - float tY = sin((newVTop - 0.5f) * textureFov); - - float dist; - //Bottom Left - dist = sqrt(lX * lX + bY * bY); - float blZ = sqrt(1.0f - dist * dist); - //Top Left - dist = sqrt(lX * lX + tY * tY); - float tlZ = sqrt(1.0f - dist * dist); - //Bottom Right - dist = sqrt(rX * rX + bY * bY); - float brZ = sqrt(1.0f - dist * dist); - //Top Right - dist = sqrt(rX * rX + tY * tY); - float trZ = sqrt(1.0f - dist * dist); - - glBegin(GL_QUADS); - - glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(lX, tY, -tlZ); - glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rX, tY, -trZ); - glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rX, bY, -brZ); - glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(lX, bY, -blZ); - - glEnd(); - - } else { - leftX = sin(leftAngle) * _distance; - rightX = sin(rightAngle) * _distance; - leftZ = -cos(leftAngle) * _distance; - rightZ = -cos(rightAngle) * _distance; - if (_uiType == CURVED_SEMICIRCLE) { - topZ = -cos(topAngle * overlayAspectRatio) * _distance; - bottomZ = -cos(bottomAngle * overlayAspectRatio) * _distance; - } else { - // Dont want to use topZ or bottomZ for SEMICIRCLE - topZ = -99999; - bottomZ = -99999; - } - - float bottomY = (1.0 - newMouseY / (float)widgetHeight) * halfOverlayHeight * 2.0f - halfOverlayHeight; - float topY = bottomY + (newHeight / widgetHeight) * halfOverlayHeight * 2; - - //TODO: Remove immediate mode in favor of VBO - glBegin(GL_QUADS); - - glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(leftX, topY, max(topZ, leftZ)); - glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rightX, topY, max(topZ, rightZ)); - glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rightX, bottomY, max(bottomZ, rightZ)); - glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(leftX, bottomY, max(bottomZ, leftZ)); - - glEnd(); - } - } - - glDepthMask(GL_FALSE); - glDisable(GL_ALPHA_TEST); - - //TODO: Remove immediate mode in favor of VBO - if (_uiType == HEMISPHERE) { - renderTexturedHemisphere(); - } else{ - glBegin(GL_QUADS); - // Place the vertices in a semicircle curve around the camera - for (int i = 0; i < numHorizontalVertices - 1; i++) { - for (int j = 0; j < numVerticalVertices - 1; j++) { - - // Calculate the X and Z coordinates from the angles and radius from camera - leftX = sin(angleIncrement * i - halfHorizontalAngle) * _distance; - rightX = sin(angleIncrement * (i + 1) - halfHorizontalAngle) * _distance; - leftZ = -cos(angleIncrement * i - halfHorizontalAngle) * _distance; - rightZ = -cos(angleIncrement * (i + 1) - halfHorizontalAngle) * _distance; - if (_uiType == 2) { - topZ = -cos((verticalAngleIncrement * (j + 1) - halfVerticalAngle) * overlayAspectRatio) * _distance; - bottomZ = -cos((verticalAngleIncrement * j - halfVerticalAngle) * overlayAspectRatio) * _distance; - } else { - topZ = -99999; - bottomZ = -99999; - } - - glTexCoord2f(quadTexWidth * i, (j + 1) * quadTexHeight); - glVertex3f(leftX, (j + 1) * quadTexHeight * overlayHeight - halfOverlayHeight, max(topZ, leftZ)); - glTexCoord2f(quadTexWidth * (i + 1), (j + 1) * quadTexHeight); - glVertex3f(rightX, (j + 1) * quadTexHeight * overlayHeight - halfOverlayHeight, max(topZ, rightZ)); - glTexCoord2f(quadTexWidth * (i + 1), j * quadTexHeight); - glVertex3f(rightX, j * quadTexHeight * overlayHeight - halfOverlayHeight, max(bottomZ, rightZ)); - glTexCoord2f(quadTexWidth * i, j * quadTexHeight); - glVertex3f(leftX, j * quadTexHeight * overlayHeight - halfOverlayHeight, max(bottomZ, leftZ)); - } - } - - glEnd(); - } - - 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); - } +//Renders a hemisphere with texture coordinates. void ApplicationOverlay::renderTexturedHemisphere() { const int slices = 80; const int stacks = 80; + //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm static VerticesIndices vbo(0, 0); int vertices = slices * (stacks - 1) + 1; int indices = slices * 2 * 3 * (stacks - 2) + slices * 3; + //We only generate the VBO once if (vbo.first == 0) { TextureVertex* vertexData = new TextureVertex[vertices]; TextureVertex* vertex = vertexData; @@ -666,8 +722,8 @@ void ApplicationOverlay::renderTexturedHemisphere() { vertex->position.x = sinf(theta) * radius; vertex->position.y = cosf(theta) * radius; vertex->position.z = z; - vertex->uv.x = asin(vertex->position.x) / (textureFov) + 0.5f; - vertex->uv.y = asin(vertex->position.y) / (textureFov) + 0.5f; + vertex->uv.x = asin(vertex->position.x) / (_textureFov) + 0.5f; + vertex->uv.y = asin(vertex->position.y) / (_textureFov) + 0.5f; vertex++; } } diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 6dbcaa7afb..03a323cd5d 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -46,13 +46,18 @@ private: typedef QPair VerticesIndices; + void renderPointers(); void renderControllerPointer(); + void renderMagnifier(int mouseX, int mouseY); + void renderAudioMeter(); + void renderStatsAndLogs(); void renderTexturedHemisphere(); QOpenGLFramebufferObject* _framebufferObject; float _trailingAudioLoudness; float _oculusAngle; float _distance; + float _textureFov; UIType _uiType; int _mouseX[2]; int _mouseY[2]; From 91f23fcf2d7ac3c918e63e838d5c8e22d591cdc9 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 12 Jun 2014 15:39:40 -0700 Subject: [PATCH 039/102] Fix for click and drag with sixense --- interface/src/devices/SixenseManager.cpp | 12 +++++++++++- interface/src/ui/ApplicationOverlay.cpp | 1 - 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index bce55ae362..4df08c6966 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -381,7 +381,17 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { if (pos.x() != _oldX[index] || pos.y() != _oldY[index]) { QMouseEvent mouseEvent(static_cast(CONTROLLER_MOVE_EVENT), pos, Qt::NoButton, Qt::NoButton, 0); - Application::getInstance()->mouseMoveEvent(&mouseEvent); + //Only send the mouse event if the opposite left button isnt held down. + //This is specifically for edit voxels + if (triggerButton == Qt::LeftButton) { + if (!_triggerPressed[(int)(!index)]) { + Application::getInstance()->mouseMoveEvent(&mouseEvent); + } + } else { + if (!_bumperPressed[(int)(!index)]) { + Application::getInstance()->mouseMoveEvent(&mouseEvent); + } + } } _oldX[index] = pos.x(); _oldY[index] = pos.y(); diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 9af3ed6d02..ae6b49c560 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -289,7 +289,6 @@ void ApplicationOverlay::renderPointers() { mouseX -= pointerWidth / 2.0f; mouseY += pointerHeight / 2.0f; - glBegin(GL_QUADS); glColor3f(1, 0, 0); From 16f9b8c3ed0a836435f246ef146a9ebdd70b091c Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 12 Jun 2014 15:49:00 -0700 Subject: [PATCH 040/102] Removed some unused variables. --- interface/src/devices/SixenseManager.cpp | 1 - interface/src/ui/ApplicationOverlay.cpp | 15 ++------------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 4df08c6966..fa902be46f 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -353,7 +353,6 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { float cursorRange = widget->width(); - pos.setX(cursorRange * xAngle); pos.setY(cursorRange * yAngle); diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index ae6b49c560..95ac803e37 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -154,9 +154,7 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { MyAvatar* myAvatar = application->getAvatar(); const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); - const int widgetWidth = glWidget->width(); - const int widgetHeight = glWidget->height(); - const float magnification = 4.0f; + // Get vertical FoV of the displayed overlay texture const float halfVerticalAngle = _oculusAngle / 2.0f; @@ -320,7 +318,7 @@ void ApplicationOverlay::renderControllerPointer() { const HandData* handData = Application::getInstance()->getAvatar()->getHandData(); int numberOfPalms = handData->getNumPalms(); - for (int palmIndex = 2; palmIndex < 4; palmIndex++) { + for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) { const PalmData* palmData = NULL; if (palmIndex >= handData->getPalms().size()) { @@ -407,18 +405,9 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) const float halfVerticalAngle = _oculusAngle / 2.0f; const float overlayAspectRatio = glWidget->width() / (float)glWidget->height(); const float halfOverlayHeight = _distance * tan(halfVerticalAngle); - const float overlayHeight = halfOverlayHeight * 2.0f; - - // The more vertices, the better the curve - const int numHorizontalVertices = 20; - const int numVerticalVertices = 20; - // U texture coordinate width at each quad - const float quadTexWidth = 1.0f / (numHorizontalVertices - 1); - const float quadTexHeight = 1.0f / (numVerticalVertices - 1); // Get horizontal angle and angle increment from vertical angle and aspect ratio const float horizontalAngle = halfVerticalAngle * 2.0f * overlayAspectRatio; - const float angleIncrement = horizontalAngle / (numHorizontalVertices - 1); const float halfHorizontalAngle = horizontalAngle / 2; From 3e50443ea157fe19c50a2dee61e818ab3bbd5891 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Jun 2014 16:03:18 -0700 Subject: [PATCH 041/102] let QUrl do its automatic percent encoding for address lookup --- interface/src/location/LocationManager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/location/LocationManager.cpp b/interface/src/location/LocationManager.cpp index f80c331df4..5567c93e58 100644 --- a/interface/src/location/LocationManager.cpp +++ b/interface/src/location/LocationManager.cpp @@ -78,7 +78,6 @@ void LocationManager::goTo(QString destination) { _userData = QJsonObject(); _placeData = QJsonObject(); - destination = QString(QUrl::toPercentEncoding(destination)); JSONCallbackParameters callbackParams; callbackParams.jsonCallbackReceiver = this; callbackParams.jsonCallbackMethod = "goToUserFromResponse"; From b4bd774789a310a6570ee025e4f454542775e47b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 12 Jun 2014 16:22:32 -0700 Subject: [PATCH 042/102] Midpoint on generic object streaming. --- libraries/metavoxels/src/Bitstream.cpp | 177 +++++++++++++++++++++++-- libraries/metavoxels/src/Bitstream.h | 138 +++++++++++++++---- 2 files changed, 274 insertions(+), 41 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index d285e4aa3f..f1c927451b 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -1639,6 +1639,153 @@ QHash Bitstream::createPropertyWriters return propertyWriters; } +MappedObjectStreamer::MappedObjectStreamer(const QVector& properties) : + _properties(properties) { +} + +const char* MappedObjectStreamer::getName() const { + return _metaObject->className(); +} + +void MappedObjectStreamer::writeMetadata(Bitstream& out, bool full) const { + out << _properties.size(); + if (_properties.isEmpty()) { + return; + } + QCryptographicHash hash(QCryptographicHash::Md5); + foreach (const StreamerPropertyPair& property, _properties) { + out << property.first.data(); + if (full) { + out << QByteArray::fromRawData(property.second.name(), strlen(property.second.name())); + } else { + hash.addData(property.second.name(), strlen(property.second.name()) + 1); + } + } + if (!full) { + QByteArray hashResult = hash.result(); + out.write(hashResult.constData(), hashResult.size() * BITS_IN_BYTE); + } +} + +void MappedObjectStreamer::write(Bitstream& out, QObject* object) const { + foreach (const StreamerPropertyPair& property, _properties) { + property.first->write(out, property.second.read(object)); + } +} + +void MappedObjectStreamer::writeDelta(Bitstream& out, QObject* object, QObject* reference) const { + foreach (const StreamerPropertyPair& property, _properties) { + property.first->writeDelta(out, property.second.read(object), (reference && reference->metaObject() == _metaObject) ? + property.second.read(reference) : QVariant()); + } +} + +QObject* MappedObjectStreamer::read(Bitstream& in, QObject* object) const { + if (!object && _metaObject) { + object = _metaObject->newInstance(); + } + foreach (const StreamerPropertyPair& property, _properties) { + QVariant value = property.first->read(in); + if (property.second.isValid() && object) { + property.second.write(object, value); + } + } + return object; +} + +QObject* MappedObjectStreamer::readDelta(Bitstream& in, const QObject* reference, QObject* object) const { + if (!object && _metaObject) { + object = _metaObject->newInstance(); + } + foreach (const StreamerPropertyPair& property, _properties) { + QVariant value; + property.first->readDelta(in, value, (property.second.isValid() && reference) ? + property.second.read(reference) : QVariant()); + if (property.second.isValid() && object) { + property.second.write(object, value); + } + } + return object; +} + +GenericObjectStreamer::GenericObjectStreamer(const QByteArray& name, const QVector& properties, + const QByteArray& hash) : + _name(name), + _properties(properties), + _hash(hash) { + + _metaObject = &GenericSharedObject::staticMetaObject; +} + +const char* GenericObjectStreamer::getName() const { + return _name.constData(); +} + +void GenericObjectStreamer::writeMetadata(Bitstream& out, bool full) const { + out << _properties.size(); + if (_properties.isEmpty()) { + return; + } + foreach (const StreamerNamePair& property, _properties) { + out << property.first.data(); + if (full) { + out << property.second; + } + } + if (!full) { + if (_hash.isEmpty()) { + QCryptographicHash hash(QCryptographicHash::Md5); + foreach (const StreamerNamePair& property, _properties) { + hash.addData(property.second.constData(), property.second.size() + 1); + } + const_cast(this)->_hash = hash.result(); + } + out.write(_hash.constData(), _hash.size() * BITS_IN_BYTE); + } +} + +void GenericObjectStreamer::write(Bitstream& out, QObject* object) const { + const QVariantList& values = static_cast(object)->getValues(); + for (int i = 0; i < _properties.size(); i++) { + _properties.at(i).first->write(out, values.at(i)); + } +} + +void GenericObjectStreamer::writeDelta(Bitstream& out, QObject* object, QObject* reference) const { + for (int i = 0; i < _properties.size(); i++) { + _properties.at(i).first->writeDelta(out, static_cast(object)->getValues().at(i), reference ? + static_cast(reference)->getValues().at(i) : QVariant()); + } +} + +QObject* GenericObjectStreamer::read(Bitstream& in, QObject* object) const { + if (!object) { + object = new GenericSharedObject(_weakSelf); + } + QVariantList values; + foreach (const StreamerNamePair& property, _properties) { + values.append(property.first->read(in)); + } + static_cast(object)->setValues(values); + return object; +} + +QObject* GenericObjectStreamer::readDelta(Bitstream& in, const QObject* reference, QObject* object) const { + if (!object) { + object = new GenericSharedObject(_weakSelf); + } + QVariantList values; + for (int i = 0; i < _properties.size(); i++) { + const StreamerNamePair& property = _properties.at(i); + QVariant value; + property.first->readDelta(in, value, reference ? + static_cast(reference)->getValues().at(i) : QVariant()); + values.append(value); + } + static_cast(object)->setValues(values); + return object; +} + ObjectReader::ObjectReader(const QByteArray& className, const QMetaObject* metaObject, const PropertyReaderVector& properties) : _className(className), @@ -1713,6 +1860,19 @@ MetaField::MetaField(const QByteArray& name, const TypeStreamer* streamer) : _streamer(streamer) { } +GenericValue::GenericValue(const TypeStreamerPointer& streamer, const QVariant& value) : + _streamer(streamer), + _value(value) { +} + +bool GenericValue::operator==(const GenericValue& other) const { + return _streamer == other._streamer && _value == other._value; +} + +GenericSharedObject::GenericSharedObject(const ObjectStreamerPointer& streamer) : + _streamer(streamer) { +} + TypeStreamer::~TypeStreamer() { } @@ -2069,19 +2229,6 @@ TypeStreamer::Category GenericEnumTypeStreamer::getCategory() const { return ENUM_CATEGORY; } -GenericValue::GenericValue(const TypeStreamerPointer& streamer, const QVariant& value) : - _streamer(streamer), - _value(value) { -} - -bool GenericValue::operator==(const GenericValue& other) const { - return _streamer == other._streamer && _value == other._value; -} - -const TypeStreamer* GenericValueStreamer::getStreamerToWrite(const QVariant& value) const { - return value.value().getStreamer().data(); -} - MappedStreamableTypeStreamer::MappedStreamableTypeStreamer(const TypeStreamer* baseStreamer, const QVector& fields) : _baseStreamer(baseStreamer), @@ -2334,3 +2481,7 @@ QVariant GenericMapTypeStreamer::read(Bitstream& in) const { TypeStreamer::Category GenericMapTypeStreamer::getCategory() const { return MAP_CATEGORY; } + +const TypeStreamer* GenericValueStreamer::getStreamerToWrite(const QVariant& value) const { + return value.value().getStreamer().data(); +} diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index ee9b8b472c..07e4b94568 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -37,6 +37,7 @@ class AttributeValue; class Bitstream; class GenericValue; class ObjectReader; +class ObjectStreamer; class OwnedAttributeValue; class PropertyReader; class PropertyWriter; @@ -46,6 +47,8 @@ typedef SharedObjectPointerTemplate AttributePointer; typedef QPair ScopeNamePair; typedef QPair NameIntPair; +typedef QSharedPointer ObjectStreamerPointer; +typedef QWeakPointer WeakObjectStreamerPointer; typedef QSharedPointer TypeStreamerPointer; typedef QWeakPointer WeakTypeStreamerPointer; typedef QVector PropertyReaderVector; @@ -766,6 +769,68 @@ template inline Bitstream& Bitstream::operator>>(QHash& return *this; } +/// Contains the information required to stream an object. +class ObjectStreamer { +public: + + virtual const char* getName() const = 0; + virtual void writeMetadata(Bitstream& out, bool full) const = 0; + virtual void write(Bitstream& out, QObject* object) const = 0; + virtual void writeDelta(Bitstream& out, QObject* object, QObject* reference) const = 0; + virtual QObject* read(Bitstream& in, QObject* object = NULL) const = 0; + virtual QObject* readDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const = 0; + +protected: + + const QMetaObject* _metaObject; + ObjectStreamerPointer _self; +}; + +typedef QPair StreamerPropertyPair; + +/// A streamer that maps to a local class. +class MappedObjectStreamer : public ObjectStreamer { +public: + + MappedObjectStreamer(const QVector& properties); + + virtual const char* getName() const; + virtual void writeMetadata(Bitstream& out, bool full) const; + virtual void write(Bitstream& out, QObject* object) const; + virtual void writeDelta(Bitstream& out, QObject* object, QObject* reference) const; + virtual QObject* read(Bitstream& in, QObject* object = NULL) const; + virtual QObject* readDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const; + +private: + + QVector _properties; +}; + +typedef QPair StreamerNamePair; + +/// A streamer for generic objects. +class GenericObjectStreamer : public ObjectStreamer { +public: + + GenericObjectStreamer(const QByteArray& name, const QVector& properties, const QByteArray& hash); + + virtual const char* getName() const; + virtual void writeMetadata(Bitstream& out, bool full) const; + virtual void write(Bitstream& out, QObject* object) const; + virtual void writeDelta(Bitstream& out, QObject* object, QObject* reference) const; + virtual QObject* read(Bitstream& in, QObject* object = NULL) const; + virtual QObject* readDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const; + +private: + + friend class Bitstream; + + QByteArray _name; + WeakObjectStreamerPointer _weakSelf; + QVector _properties; + QByteArray _hash; +}; + /// Contains the information required to read an object from the stream. class ObjectReader { public: @@ -849,6 +914,44 @@ Q_DECLARE_METATYPE(const QMetaObject*) /// Macro for registering streamable meta-objects. #define REGISTER_META_OBJECT(x) static int x##Registration = Bitstream::registerMetaObject(#x, &x::staticMetaObject); +/// Contains a value along with a pointer to its streamer. +class GenericValue { +public: + + GenericValue(const TypeStreamerPointer& streamer = TypeStreamerPointer(), const QVariant& value = QVariant()); + + const TypeStreamerPointer& getStreamer() const { return _streamer; } + const QVariant& getValue() const { return _value; } + + bool operator==(const GenericValue& other) const; + +private: + + TypeStreamerPointer _streamer; + QVariant _value; +}; + +Q_DECLARE_METATYPE(GenericValue) + +/// Contains a list of property values along with a pointer to their metadata. +class GenericSharedObject : public SharedObject { + Q_OBJECT + +public: + + GenericSharedObject(const ObjectStreamerPointer& streamer); + + const ObjectStreamerPointer& getStreamer() const { return _streamer; } + + void setValues(const QVariantList& values) { _values = values; } + const QVariantList& getValues() const { return _values; } + +private: + + ObjectStreamerPointer _streamer; + QVariantList _values; +}; + /// Interface for objects that can write values to and read values from bitstreams. class TypeStreamer { public: @@ -1012,32 +1115,6 @@ private: QByteArray _hash; }; -/// Contains a value along with a pointer to its streamer. -class GenericValue { -public: - - GenericValue(const TypeStreamerPointer& streamer = TypeStreamerPointer(), const QVariant& value = QVariant()); - - const TypeStreamerPointer& getStreamer() const { return _streamer; } - const QVariant& getValue() const { return _value; } - - bool operator==(const GenericValue& other) const; - -private: - - TypeStreamerPointer _streamer; - QVariant _value; -}; - -Q_DECLARE_METATYPE(GenericValue) - -/// A streamer class for generic values. -class GenericValueStreamer : public SimpleTypeStreamer { -public: - - virtual const TypeStreamer* getStreamerToWrite(const QVariant& value) const; -}; - /// A streamer for types compiled by mtc. template class StreamableTypeStreamer : public SimpleTypeStreamer { public: @@ -1068,8 +1145,6 @@ private: QVector _fields; }; -typedef QPair StreamerNamePair; - /// A streamer for generic enums. class GenericStreamableTypeStreamer : public GenericTypeStreamer { public: @@ -1238,6 +1313,13 @@ private: TypeStreamerPointer _valueStreamer; }; +/// A streamer class for generic values. +class GenericValueStreamer : public SimpleTypeStreamer { +public: + + virtual const TypeStreamer* getStreamerToWrite(const QVariant& value) const; +}; + /// Macro for registering simple type streamers. #define REGISTER_SIMPLE_TYPE_STREAMER(X) static int X##Streamer = \ Bitstream::registerTypeStreamer(qMetaTypeId(), new SimpleTypeStreamer()); From 8e74398ed7fa4f04f7bbb954ee8aca06b0f430c4 Mon Sep 17 00:00:00 2001 From: John Grosen Date: Thu, 12 Jun 2014 17:06:59 -0700 Subject: [PATCH 043/102] Fixed command line args parsing bug --- libraries/shared/src/HifiConfigVariantMap.cpp | 47 +++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/libraries/shared/src/HifiConfigVariantMap.cpp b/libraries/shared/src/HifiConfigVariantMap.cpp index e8ab59ce2d..7975059736 100644 --- a/libraries/shared/src/HifiConfigVariantMap.cpp +++ b/libraries/shared/src/HifiConfigVariantMap.cpp @@ -19,71 +19,70 @@ #include "HifiConfigVariantMap.h" QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringList& argumentList) { - + QVariantMap mergedMap; - + // Add anything in the CL parameter list to the variant map. // Take anything with a dash in it as a key, and the values after it as the value. - + const QString DASHED_KEY_REGEX_STRING = "(^-{1,2})([\\w-]+)"; QRegExp dashedKeyRegex(DASHED_KEY_REGEX_STRING); - + int keyIndex = argumentList.indexOf(dashedKeyRegex); int nextKeyIndex = 0; - + // check if there is a config file to read where we can pull config info not passed on command line const QString CONFIG_FILE_OPTION = "--config"; - + while (keyIndex != -1) { if (argumentList[keyIndex] != CONFIG_FILE_OPTION) { // we have a key - look forward to see how many values associate to it QString key = dashedKeyRegex.cap(2); - + nextKeyIndex = argumentList.indexOf(dashedKeyRegex, keyIndex + 1); - + if (nextKeyIndex == keyIndex + 1 || keyIndex == argumentList.size() - 1) { - // there's no value associated with this option, it's a boolean - // so add it to the variant map with NULL as value - mergedMap.insertMulti(key, QVariant()); + // this option is simply a switch, so add it to the map with a value of `true` + mergedMap.insertMulti(key, QVariant(true)); } else { - int maxIndex = (nextKeyIndex == -1) ? argumentList.size() - 1: nextKeyIndex; - + int maxIndex = (nextKeyIndex == -1) ? argumentList.size(): nextKeyIndex; + // there's at least one value associated with the option // pull the first value to start QString value = argumentList[keyIndex + 1]; - + // for any extra values, append them, with a space, to the value string - for (int i = keyIndex + 2; i <= maxIndex; i++) { + for (int i = keyIndex + 2; i < maxIndex; i++) { value += " " + argumentList[i]; } - + // add the finalized value to the merged map mergedMap.insert(key, value); } - + keyIndex = nextKeyIndex; } else { keyIndex = argumentList.indexOf(dashedKeyRegex, keyIndex + 1); } } - + int configIndex = argumentList.indexOf(CONFIG_FILE_OPTION); - + if (configIndex != -1) { // we have a config file - try and read it QString configFilePath = argumentList[configIndex + 1]; QFile configFile(configFilePath); - + if (configFile.exists()) { qDebug() << "Reading JSON config file at" << configFilePath; configFile.open(QIODevice::ReadOnly); - + QJsonDocument configDocument = QJsonDocument::fromJson(configFile.readAll()); QJsonObject rootObject = configDocument.object(); - + // enumerate the keys of the configDocument object foreach(const QString& key, rootObject.keys()) { - + if (!mergedMap.contains(key)) { // no match in existing list, add it mergedMap.insert(key, QVariant(rootObject[key])); @@ -93,6 +92,6 @@ QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringL qDebug() << "Could not find JSON config file at" << configFilePath; } } - + return mergedMap; } From 47a88a2713dd98fa51265a43761bf98a6875a917 Mon Sep 17 00:00:00 2001 From: John Grosen Date: Thu, 12 Jun 2014 17:08:23 -0700 Subject: [PATCH 044/102] Updated AssignmentClient to use HifiConfigVariantMap... ...and also updated DomainServer to appropriately use boolean options --- assignment-client/src/AssignmentClient.cpp | 117 ++--- domain-server/src/DomainServer.cpp | 573 +++++++++++---------- 2 files changed, 342 insertions(+), 348 deletions(-) diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 009bd42e88..da1feda7a0 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -41,73 +42,65 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : setOrganizationDomain("highfidelity.io"); setApplicationName("assignment-client"); QSettings::setDefaultFormat(QSettings::IniFormat); - - QStringList argumentList = arguments(); - - // register meta type is required for queued invoke method on Assignment subclasses - + // set the logging target to the the CHILD_TARGET_NAME Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); - - const QString ASSIGNMENT_TYPE_OVVERIDE_OPTION = "-t"; - int argumentIndex = argumentList.indexOf(ASSIGNMENT_TYPE_OVVERIDE_OPTION); - + + const QVariantMap argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments()); + + const QString ASSIGNMENT_TYPE_OVERRIDE_OPTION = "t"; + const QString ASSIGNMENT_POOL_OPTION = "pool"; + const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "wallet"; + const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "a"; + Assignment::Type requestAssignmentType = Assignment::AllTypes; - - if (argumentIndex != -1) { - requestAssignmentType = (Assignment::Type) argumentList[argumentIndex + 1].toInt(); + + // check for an assignment type passed on the command line or in the config + if (argumentVariantMap.contains(ASSIGNMENT_TYPE_OVERRIDE_OPTION)) { + requestAssignmentType = (Assignment::Type) argumentVariantMap.value(ASSIGNMENT_TYPE_OVERRIDE_OPTION).toInt(); } - - const QString ASSIGNMENT_POOL_OPTION = "--pool"; - - argumentIndex = argumentList.indexOf(ASSIGNMENT_POOL_OPTION); - + QString assignmentPool; - - if (argumentIndex != -1) { - assignmentPool = argumentList[argumentIndex + 1]; + + // check for an assignment pool passed on the command line or in the config + if (argumentVariantMap.contains(ASSIGNMENT_POOL_OPTION)) { + assignmentPool = argumentVariantMap.value(ASSIGNMENT_POOL_OPTION).toString(); } - + // setup our _requestAssignment member variable from the passed arguments _requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, assignmentPool); - - // check if we were passed a wallet UUID on the command line + + // check for a wallet UUID on the command line or in the config // this would represent where the user running AC wants funds sent to - - const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "--wallet"; - if ((argumentIndex = argumentList.indexOf(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) != -1) { - QUuid walletUUID = QString(argumentList[argumentIndex + 1]); + if (argumentVariantMap.contains(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) { + QUuid walletUUID = argumentVariantMap.value(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION).toString(); qDebug() << "The destination wallet UUID for credits is" << uuidStringWithoutCurlyBraces(walletUUID); _requestAssignment.setWalletUUID(walletUUID); } - + // create a NodeList as an unassigned client NodeList* nodeList = NodeList::createInstance(NodeType::Unassigned); - + // check for an overriden assignment server hostname - const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "-a"; - - argumentIndex = argumentList.indexOf(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION); - - if (argumentIndex != -1) { - _assignmentServerHostname = argumentList[argumentIndex + 1]; - + if (argumentVariantMap.contains(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION)) { + _assignmentServerHostname = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION).toString(); + // set the custom assignment socket on our NodeList HifiSockAddr customAssignmentSocket = HifiSockAddr(_assignmentServerHostname, DEFAULT_DOMAIN_SERVER_PORT); - + nodeList->setAssignmentServerSocket(customAssignmentSocket); } - + // call a timer function every ASSIGNMENT_REQUEST_INTERVAL_MSECS to ask for assignment, if required qDebug() << "Waiting for assignment -" << _requestAssignment; - + QTimer* timer = new QTimer(this); connect(timer, SIGNAL(timeout()), SLOT(sendAssignmentRequest())); timer->start(ASSIGNMENT_REQUEST_INTERVAL_MSECS); - + // connect our readPendingDatagrams method to the readyRead() signal of the socket connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams); - + // connections to AccountManager for authentication connect(&AccountManager::getInstance(), &AccountManager::authRequired, this, &AssignmentClient::handleAuthenticationRequest); @@ -121,49 +114,49 @@ void AssignmentClient::sendAssignmentRequest() { void AssignmentClient::readPendingDatagrams() { NodeList* nodeList = NodeList::getInstance(); - + QByteArray receivedPacket; HifiSockAddr senderSockAddr; - + while (nodeList->getNodeSocket().hasPendingDatagrams()) { receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize()); nodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(), senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); - + if (nodeList->packetVersionAndHashMatch(receivedPacket)) { if (packetTypeForPacket(receivedPacket) == PacketTypeCreateAssignment) { // construct the deployed assignment from the packet data _currentAssignment = SharedAssignmentPointer(AssignmentFactory::unpackAssignment(receivedPacket)); - + if (_currentAssignment) { qDebug() << "Received an assignment -" << *_currentAssignment; - + // switch our DomainHandler hostname and port to whoever sent us the assignment - + nodeList->getDomainHandler().setSockAddr(senderSockAddr, _assignmentServerHostname); nodeList->getDomainHandler().setAssignmentUUID(_currentAssignment->getUUID()); - + qDebug() << "Destination IP for assignment is" << nodeList->getDomainHandler().getIP().toString(); - + // start the deployed assignment AssignmentThread* workerThread = new AssignmentThread(_currentAssignment, this); - + connect(workerThread, &QThread::started, _currentAssignment.data(), &ThreadedAssignment::run); connect(_currentAssignment.data(), &ThreadedAssignment::finished, workerThread, &QThread::quit); connect(_currentAssignment.data(), &ThreadedAssignment::finished, this, &AssignmentClient::assignmentCompleted); connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater); - + _currentAssignment->moveToThread(workerThread); - + // move the NodeList to the thread used for the _current assignment nodeList->moveToThread(workerThread); - + // let the assignment handle the incoming datagrams for its duration disconnect(&nodeList->getNodeSocket(), 0, this, 0); connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _currentAssignment.data(), &ThreadedAssignment::readPendingDatagrams); - + // Starts an event loop, and emits workerThread->started() workerThread->start(); } else { @@ -180,15 +173,15 @@ void AssignmentClient::readPendingDatagrams() { void AssignmentClient::handleAuthenticationRequest() { const QString DATA_SERVER_USERNAME_ENV = "HIFI_AC_USERNAME"; const QString DATA_SERVER_PASSWORD_ENV = "HIFI_AC_PASSWORD"; - + // this node will be using an authentication server, let's make sure we have a username/password QProcessEnvironment sysEnvironment = QProcessEnvironment::systemEnvironment(); - + QString username = sysEnvironment.value(DATA_SERVER_USERNAME_ENV); QString password = sysEnvironment.value(DATA_SERVER_PASSWORD_ENV); - + AccountManager& accountManager = AccountManager::getInstance(); - + if (!username.isEmpty() && !password.isEmpty()) { // ask the account manager to log us in from the env variables accountManager.requestAccessToken(username, password); @@ -196,7 +189,7 @@ void AssignmentClient::handleAuthenticationRequest() { qDebug() << "Authentication was requested against" << qPrintable(accountManager.getAuthURL().toString()) << "but both or one of" << qPrintable(DATA_SERVER_USERNAME_ENV) << "/" << qPrintable(DATA_SERVER_PASSWORD_ENV) << "are not set. Unable to authenticate."; - + return; } } @@ -204,15 +197,15 @@ void AssignmentClient::handleAuthenticationRequest() { void AssignmentClient::assignmentCompleted() { // reset the logging target to the the CHILD_TARGET_NAME Logging::setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); - + qDebug("Assignment finished or never started - waiting for new assignment."); - + NodeList* nodeList = NodeList::getInstance(); // have us handle incoming NodeList datagrams again disconnect(&nodeList->getNodeSocket(), 0, _currentAssignment.data(), 0); connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams); - + // clear our current assignment shared pointer now that we're done with it // if the assignment thread is still around it has its own shared pointer to the assignment _currentAssignment.clear(); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index b5c2d00d3e..f7185063a9 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -47,15 +47,15 @@ DomainServer::DomainServer(int argc, char* argv[]) : setOrganizationDomain("highfidelity.io"); setApplicationName("domain-server"); QSettings::setDefaultFormat(QSettings::IniFormat); - + _argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments()); - + _networkAccessManager = new QNetworkAccessManager(this); - + if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth() && optionallySetupAssignmentPayment()) { // we either read a certificate and private key or were not passed one // and completed login or did not need to - + qDebug() << "Setting up LimitedNodeList and assignments."; setupNodeListAndAssignments(); } @@ -65,58 +65,58 @@ bool DomainServer::optionallyReadX509KeyAndCertificate() { const QString X509_CERTIFICATE_OPTION = "cert"; const QString X509_PRIVATE_KEY_OPTION = "key"; const QString X509_KEY_PASSPHRASE_ENV = "DOMAIN_SERVER_KEY_PASSPHRASE"; - + QString certPath = _argumentVariantMap.value(X509_CERTIFICATE_OPTION).toString(); QString keyPath = _argumentVariantMap.value(X509_PRIVATE_KEY_OPTION).toString(); - + if (!certPath.isEmpty() && !keyPath.isEmpty()) { // the user wants to use DTLS to encrypt communication with nodes // let's make sure we can load the key and certificate // _x509Credentials = new gnutls_certificate_credentials_t; // gnutls_certificate_allocate_credentials(_x509Credentials); - + QString keyPassphraseString = QProcessEnvironment::systemEnvironment().value(X509_KEY_PASSPHRASE_ENV); - + qDebug() << "Reading certificate file at" << certPath << "for DTLS."; qDebug() << "Reading key file at" << keyPath << "for DTLS."; - + // int gnutlsReturn = gnutls_certificate_set_x509_key_file2(*_x509Credentials, // certPath.toLocal8Bit().constData(), // keyPath.toLocal8Bit().constData(), // GNUTLS_X509_FMT_PEM, // keyPassphraseString.toLocal8Bit().constData(), // 0); -// +// // if (gnutlsReturn < 0) { // qDebug() << "Unable to load certificate or key file." << "Error" << gnutlsReturn << "- domain-server will now quit."; // QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); // return false; // } - + // qDebug() << "Successfully read certificate and private key."; - + // we need to also pass this certificate and private key to the HTTPS manager // this is used for Oauth callbacks when authorizing users against a data server - + QFile certFile(certPath); certFile.open(QIODevice::ReadOnly); - + QFile keyFile(keyPath); keyFile.open(QIODevice::ReadOnly); - + QSslCertificate sslCertificate(&certFile); QSslKey privateKey(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, keyPassphraseString.toUtf8()); - + _httpsManager = new HTTPSManager(DOMAIN_SERVER_HTTPS_PORT, sslCertificate, privateKey, QString(), this, this); - + qDebug() << "TCP server listening for HTTPS connections on" << DOMAIN_SERVER_HTTPS_PORT; - + } else if (!certPath.isEmpty() || !keyPath.isEmpty()) { qDebug() << "Missing certificate or private key. domain-server will now quit."; QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); return false; } - + return true; } @@ -125,12 +125,12 @@ bool DomainServer::optionallySetupOAuth() { const QString OAUTH_CLIENT_ID_OPTION = "oauth-client-id"; const QString OAUTH_CLIENT_SECRET_ENV = "DOMAIN_SERVER_CLIENT_SECRET"; const QString REDIRECT_HOSTNAME_OPTION = "hostname"; - + _oauthProviderURL = QUrl(_argumentVariantMap.value(OAUTH_PROVIDER_URL_OPTION).toString()); _oauthClientID = _argumentVariantMap.value(OAUTH_CLIENT_ID_OPTION).toString(); _oauthClientSecret = QProcessEnvironment::systemEnvironment().value(OAUTH_CLIENT_SECRET_ENV); _hostname = _argumentVariantMap.value(REDIRECT_HOSTNAME_OPTION).toString(); - + if (!_oauthClientID.isEmpty()) { if (_oauthProviderURL.isEmpty() || _hostname.isEmpty() @@ -144,47 +144,47 @@ bool DomainServer::optionallySetupOAuth() { qDebug() << "OAuth Client ID is" << _oauthClientID; } } - + return true; } void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) { - + const QString CUSTOM_PORT_OPTION = "port"; unsigned short domainServerPort = DEFAULT_DOMAIN_SERVER_PORT; - + if (_argumentVariantMap.contains(CUSTOM_PORT_OPTION)) { domainServerPort = (unsigned short) _argumentVariantMap.value(CUSTOM_PORT_OPTION).toUInt(); } - + unsigned short domainServerDTLSPort = 0; - + if (_isUsingDTLS) { domainServerDTLSPort = DEFAULT_DOMAIN_SERVER_DTLS_PORT; - + const QString CUSTOM_DTLS_PORT_OPTION = "dtls-port"; - + if (_argumentVariantMap.contains(CUSTOM_DTLS_PORT_OPTION)) { domainServerDTLSPort = (unsigned short) _argumentVariantMap.value(CUSTOM_DTLS_PORT_OPTION).toUInt(); } } - + QSet parsedTypes; parseAssignmentConfigs(parsedTypes); - + populateDefaultStaticAssignmentsExcludingTypes(parsedTypes); - + LimitedNodeList* nodeList = LimitedNodeList::createInstance(domainServerPort, domainServerDTLSPort); - + connect(nodeList, &LimitedNodeList::nodeAdded, this, &DomainServer::nodeAdded); connect(nodeList, &LimitedNodeList::nodeKilled, this, &DomainServer::nodeKilled); - + QTimer* silentNodeTimer = new QTimer(this); connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); silentNodeTimer->start(NODE_SILENCE_THRESHOLD_MSECS); - + connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), SLOT(readAvailableDatagrams())); - + // add whatever static assignments that have been parsed to the queue addStaticAssignmentsToQueue(); } @@ -194,21 +194,22 @@ bool DomainServer::optionallySetupAssignmentPayment() { const QString PAY_FOR_ASSIGNMENTS_OPTION = "pay-for-assignments"; const QString HIFI_USERNAME_ENV_KEY = "DOMAIN_SERVER_USERNAME"; const QString HIFI_PASSWORD_ENV_KEY = "DOMAIN_SERVER_PASSWORD"; - - if (_argumentVariantMap.contains(PAY_FOR_ASSIGNMENTS_OPTION)) { + + if (_argumentVariantMap.contains(PAY_FOR_ASSIGNMENTS_OPTION) && + _argumentVariantMap.value(PAY_FOR_ASSIGNMENTS_OPTION).toBool()) { if (!_oauthProviderURL.isEmpty()) { - + AccountManager& accountManager = AccountManager::getInstance(); accountManager.setAuthURL(_oauthProviderURL); - + if (!accountManager.hasValidAccessToken()) { // we don't have a valid access token so we need to get one QString username = QProcessEnvironment::systemEnvironment().value(HIFI_USERNAME_ENV_KEY); QString password = QProcessEnvironment::systemEnvironment().value(HIFI_PASSWORD_ENV_KEY); - + if (!username.isEmpty() && !password.isEmpty()) { accountManager.requestAccessToken(username, password); - + // connect to loginFailed signal from AccountManager so we can quit if that is the case connect(&accountManager, &AccountManager::loginFailed, this, &DomainServer::loginFailed); } else { @@ -217,31 +218,31 @@ bool DomainServer::optionallySetupAssignmentPayment() { return false; } } - + qDebug() << "Assignments will be paid for via" << qPrintable(_oauthProviderURL.toString()); - + // assume that the fact we are authing against HF data server means we will pay for assignments // setup a timer to send transactions to pay assigned nodes every 30 seconds QTimer* creditSetupTimer = new QTimer(this); connect(creditSetupTimer, &QTimer::timeout, this, &DomainServer::setupPendingAssignmentCredits); - + const qint64 CREDIT_CHECK_INTERVAL_MSECS = 5 * 1000; creditSetupTimer->start(CREDIT_CHECK_INTERVAL_MSECS); - + QTimer* nodePaymentTimer = new QTimer(this); connect(nodePaymentTimer, &QTimer::timeout, this, &DomainServer::sendPendingTransactionsToServer); - + const qint64 TRANSACTION_SEND_INTERVAL_MSECS = 30 * 1000; nodePaymentTimer->start(TRANSACTION_SEND_INTERVAL_MSECS); - + } else { qDebug() << "Missing OAuth provider URL, but assigned node payment was enabled. domain-server will now quit."; QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); - + return false; } } - + return true; } @@ -254,35 +255,35 @@ void DomainServer::parseAssignmentConfigs(QSet& excludedTypes) // check for configs from the command line, these take precedence const QString ASSIGNMENT_CONFIG_REGEX_STRING = "config-([\\d]+)"; QRegExp assignmentConfigRegex(ASSIGNMENT_CONFIG_REGEX_STRING); - + // scan for assignment config keys QStringList variantMapKeys = _argumentVariantMap.keys(); int configIndex = variantMapKeys.indexOf(assignmentConfigRegex); - + while (configIndex != -1) { // figure out which assignment type this matches Assignment::Type assignmentType = (Assignment::Type) assignmentConfigRegex.cap(1).toInt(); - + if (assignmentType < Assignment::AllTypes && !excludedTypes.contains(assignmentType)) { QVariant mapValue = _argumentVariantMap[variantMapKeys[configIndex]]; QJsonArray assignmentArray; - + if (mapValue.type() == QVariant::String) { QJsonDocument deserializedDocument = QJsonDocument::fromJson(mapValue.toString().toUtf8()); assignmentArray = deserializedDocument.array(); } else { assignmentArray = mapValue.toJsonValue().toArray(); } - + if (assignmentType != Assignment::AgentType) { createStaticAssignmentsForType(assignmentType, assignmentArray); } else { createScriptedAssignmentsFromArray(assignmentArray); } - + excludedTypes.insert(assignmentType); } - + configIndex = variantMapKeys.indexOf(assignmentConfigRegex, configIndex + 1); } } @@ -293,34 +294,34 @@ void DomainServer::addStaticAssignmentToAssignmentHash(Assignment* newAssignment _allAssignments.insert(newAssignment->getUUID(), SharedAssignmentPointer(newAssignment)); } -void DomainServer::createScriptedAssignmentsFromArray(const QJsonArray &configArray) { +void DomainServer::createScriptedAssignmentsFromArray(const QJsonArray &configArray) { foreach(const QJsonValue& jsonValue, configArray) { if (jsonValue.isObject()) { QJsonObject jsonObject = jsonValue.toObject(); - + // make sure we were passed a URL, otherwise this is an invalid scripted assignment const QString ASSIGNMENT_URL_KEY = "url"; QString assignmentURL = jsonObject[ASSIGNMENT_URL_KEY].toString(); - + if (!assignmentURL.isEmpty()) { // check the json for a pool const QString ASSIGNMENT_POOL_KEY = "pool"; QString assignmentPool = jsonObject[ASSIGNMENT_POOL_KEY].toString(); - + // check for a number of instances, if not passed then default is 1 const QString ASSIGNMENT_INSTANCES_KEY = "instances"; int numInstances = jsonObject[ASSIGNMENT_INSTANCES_KEY].toInt(); numInstances = (numInstances == 0 ? 1 : numInstances); - + qDebug() << "Adding a static scripted assignment from" << assignmentURL; - + for (int i = 0; i < numInstances; i++) { // add a scripted assignment to the queue for this instance Assignment* scriptAssignment = new Assignment(Assignment::CreateCommand, Assignment::AgentType, assignmentPool); scriptAssignment->setPayload(assignmentURL.toUtf8()); - + // scripts passed on CL or via JSON are static - so they are added back to the queue if the node dies addStaticAssignmentToAssignmentHash(scriptAssignment); } @@ -332,38 +333,38 @@ void DomainServer::createScriptedAssignmentsFromArray(const QJsonArray &configAr void DomainServer::createStaticAssignmentsForType(Assignment::Type type, const QJsonArray& configArray) { // we have a string for config for this type qDebug() << "Parsing config for assignment type" << type; - + int configCounter = 0; - + foreach(const QJsonValue& jsonValue, configArray) { if (jsonValue.isObject()) { QJsonObject jsonObject = jsonValue.toObject(); - + // check the config string for a pool const QString ASSIGNMENT_POOL_KEY = "pool"; QString assignmentPool; - + QJsonValue poolValue = jsonObject[ASSIGNMENT_POOL_KEY]; if (!poolValue.isUndefined()) { assignmentPool = poolValue.toString(); - + jsonObject.remove(ASSIGNMENT_POOL_KEY); } - + ++configCounter; qDebug() << "Type" << type << "config" << configCounter << "=" << jsonObject; - + Assignment* configAssignment = new Assignment(Assignment::CreateCommand, type, assignmentPool); - + // setup the payload as a semi-colon separated list of key = value QStringList payloadStringList; foreach(const QString& payloadKey, jsonObject.keys()) { QString dashes = payloadKey.size() == 1 ? "-" : "--"; payloadStringList << QString("%1%2 %3").arg(dashes).arg(payloadKey).arg(jsonObject[payloadKey].toString()); } - + configAssignment->setPayload(payloadStringList.join(' ').toUtf8()); - + addStaticAssignmentToAssignmentHash(configAssignment); } } @@ -393,27 +394,27 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock NodeType_t nodeType; HifiSockAddr publicSockAddr, localSockAddr; - + int numPreInterestBytes = parseNodeDataFromByteArray(nodeType, publicSockAddr, localSockAddr, packet, senderSockAddr); - + QUuid packetUUID = uuidFromPacketHeader(packet); // check if this connect request matches an assignment in the queue bool isAssignment = _pendingAssignedNodes.contains(packetUUID); SharedAssignmentPointer matchingQueuedAssignment = SharedAssignmentPointer(); PendingAssignedNodeData* pendingAssigneeData = NULL; - + if (isAssignment) { pendingAssigneeData = _pendingAssignedNodes.value(packetUUID); - + if (pendingAssigneeData) { matchingQueuedAssignment = matchingQueuedAssignmentForCheckIn(pendingAssigneeData->getAssignmentUUID(), nodeType); - + if (matchingQueuedAssignment) { qDebug() << "Assignment deployed with" << uuidStringWithoutCurlyBraces(packetUUID) << "matches unfulfilled assignment" << uuidStringWithoutCurlyBraces(matchingQueuedAssignment->getUUID()); - + // remove this unique assignment deployment from the hash of pending assigned nodes // cleanup of the PendingAssignedNodeData happens below after the node has been added to the LimitedNodeList _pendingAssignedNodes.remove(packetUUID); @@ -424,9 +425,9 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock return; } } - + } - + if (!isAssignment && !_oauthProviderURL.isEmpty() && _argumentVariantMap.contains(ALLOWED_ROLES_CONFIG_KEY)) { // this is an Agent, and we require authentication so we can compare the user's roles to our list of allowed ones if (_sessionAuthenticationHash.contains(packetUUID)) { @@ -445,37 +446,37 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock QDataStream oauthRequestStream(&oauthRequestByteArray, QIODevice::Append); QUrl authorizationURL = packetUUID.isNull() ? oauthAuthorizationURL() : oauthAuthorizationURL(packetUUID); oauthRequestStream << authorizationURL; - + // send this oauth request datagram back to the client LimitedNodeList::getInstance()->writeUnverifiedDatagram(oauthRequestByteArray, senderSockAddr); - + return; } } - + if ((!isAssignment && !STATICALLY_ASSIGNED_NODES.contains(nodeType)) || (isAssignment && matchingQueuedAssignment)) { // this was either not a static assignment or it was and we had a matching one in the queue - + // create a new session UUID for this node QUuid nodeUUID = QUuid::createUuid(); - + SharedNodePointer newNode = LimitedNodeList::getInstance()->addOrUpdateNode(nodeUUID, nodeType, publicSockAddr, localSockAddr); // when the newNode is created the linked data is also created // if this was a static assignment set the UUID, set the sendingSockAddr DomainServerNodeData* nodeData = reinterpret_cast(newNode->getLinkedData()); - + if (isAssignment) { nodeData->setAssignmentUUID(matchingQueuedAssignment->getUUID()); nodeData->setWalletUUID(pendingAssigneeData->getWalletUUID()); - + // now that we've pulled the wallet UUID and added the node to our list, delete the pending assignee data delete pendingAssigneeData; } - + nodeData->setSendingSockAddr(senderSockAddr); - + // reply back to the user with a PacketTypeDomainList sendDomainListToNode(newNode, senderSockAddr, nodeInterestListFromPacket(packet, numPreInterestBytes)); } @@ -492,26 +493,26 @@ QUrl DomainServer::oauthAuthorizationURL(const QUuid& stateUUID) { // for now these are all interface clients that have a GUI // so just send them back the full authorization URL QUrl authorizationURL = _oauthProviderURL; - + const QString OAUTH_AUTHORIZATION_PATH = "/oauth/authorize"; authorizationURL.setPath(OAUTH_AUTHORIZATION_PATH); - + QUrlQuery authorizationQuery; - + authorizationQuery.addQueryItem(OAUTH_CLIENT_ID_QUERY_KEY, _oauthClientID); - + const QString OAUTH_RESPONSE_TYPE_QUERY_KEY = "response_type"; const QString OAUTH_REPSONSE_TYPE_QUERY_VALUE = "code"; authorizationQuery.addQueryItem(OAUTH_RESPONSE_TYPE_QUERY_KEY, OAUTH_REPSONSE_TYPE_QUERY_VALUE); - + const QString OAUTH_STATE_QUERY_KEY = "state"; // create a new UUID that will be the state parameter for oauth authorization AND the new session UUID for that node authorizationQuery.addQueryItem(OAUTH_STATE_QUERY_KEY, uuidStringWithoutCurlyBraces(stateUUID)); - + authorizationQuery.addQueryItem(OAUTH_REDIRECT_URI_QUERY_KEY, oauthRedirectURL().toString()); - + authorizationURL.setQuery(authorizationQuery); - + return authorizationURL; } @@ -520,14 +521,14 @@ int DomainServer::parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& const HifiSockAddr& senderSockAddr) { QDataStream packetStream(packet); packetStream.skipRawData(numBytesForPacketHeader(packet)); - + packetStream >> nodeType; packetStream >> publicSockAddr >> localSockAddr; - + if (publicSockAddr.getAddress().isNull()) { // this node wants to use us its STUN server // so set the node public address to whatever we perceive the public address to be - + // if the sender is on our box then leave its public address to 0 so that // other users attempt to reach it on the same address they have for the domain-server if (senderSockAddr.getAddress().isLoopback()) { @@ -536,95 +537,95 @@ int DomainServer::parseNodeDataFromByteArray(NodeType_t& nodeType, HifiSockAddr& publicSockAddr.setAddress(senderSockAddr.getAddress()); } } - + return packetStream.device()->pos(); } NodeSet DomainServer::nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes) { QDataStream packetStream(packet); packetStream.skipRawData(numPreceedingBytes); - + quint8 numInterestTypes = 0; packetStream >> numInterestTypes; - + quint8 nodeType; NodeSet nodeInterestSet; - + for (int i = 0; i < numInterestTypes; i++) { packetStream >> nodeType; nodeInterestSet.insert((NodeType_t) nodeType); } - + return nodeInterestSet; } void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr &senderSockAddr, const NodeSet& nodeInterestList) { - + QByteArray broadcastPacket = byteArrayWithPopulatedHeader(PacketTypeDomainList); - + // always send the node their own UUID back QDataStream broadcastDataStream(&broadcastPacket, QIODevice::Append); broadcastDataStream << node->getUUID(); - + int numBroadcastPacketLeadBytes = broadcastDataStream.device()->pos(); - + DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); - + LimitedNodeList* nodeList = LimitedNodeList::getInstance(); - + if (nodeInterestList.size() > 0) { - + // DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL; int dataMTU = MAX_PACKET_SIZE; - + if (nodeData->isAuthenticated()) { // if this authenticated node has any interest types, send back those nodes as well foreach (const SharedNodePointer& otherNode, nodeList->getNodeHash()) { - + // reset our nodeByteArray and nodeDataStream QByteArray nodeByteArray; QDataStream nodeDataStream(&nodeByteArray, QIODevice::Append); - + if (otherNode->getUUID() != node->getUUID() && nodeInterestList.contains(otherNode->getType())) { - + // don't send avatar nodes to other avatars, that will come from avatar mixer nodeDataStream << *otherNode.data(); - + // pack the secret that these two nodes will use to communicate with each other QUuid secretUUID = nodeData->getSessionSecretHash().value(otherNode->getUUID()); if (secretUUID.isNull()) { // generate a new secret UUID these two nodes can use secretUUID = QUuid::createUuid(); - + // set that on the current Node's sessionSecretHash nodeData->getSessionSecretHash().insert(otherNode->getUUID(), secretUUID); - + // set it on the other Node's sessionSecretHash reinterpret_cast(otherNode->getLinkedData()) ->getSessionSecretHash().insert(node->getUUID(), secretUUID); - + } - + nodeDataStream << secretUUID; - + if (broadcastPacket.size() + nodeByteArray.size() > dataMTU) { // we need to break here and start a new packet // so send the current one - + nodeList->writeDatagram(broadcastPacket, node, senderSockAddr); - + // reset the broadcastPacket structure broadcastPacket.resize(numBroadcastPacketLeadBytes); broadcastDataStream.device()->seek(numBroadcastPacketLeadBytes); } - + // append the nodeByteArray to the current state of broadcastDataStream broadcastPacket.append(nodeByteArray); } } } - + // always write the last broadcastPacket nodeList->writeDatagram(broadcastPacket, node, senderSockAddr); } @@ -635,7 +636,7 @@ void DomainServer::readAvailableDatagrams() { HifiSockAddr senderSockAddr; QByteArray receivedPacket; - + static QByteArray assignmentPacket = byteArrayWithPopulatedHeader(PacketTypeCreateAssignment); static int numAssignmentPacketHeaderBytes = assignmentPacket.size(); @@ -648,44 +649,44 @@ void DomainServer::readAvailableDatagrams() { // construct the requested assignment from the packet data Assignment requestAssignment(receivedPacket); - + // Suppress these for Assignment::AgentType to once per 5 seconds static QElapsedTimer noisyMessageTimer; static bool wasNoisyTimerStarted = false; - + if (!wasNoisyTimerStarted) { noisyMessageTimer.start(); wasNoisyTimerStarted = true; } - + const quint64 NOISY_MESSAGE_INTERVAL_MSECS = 5 * 1000; - + if (requestAssignment.getType() != Assignment::AgentType || noisyMessageTimer.elapsed() > NOISY_MESSAGE_INTERVAL_MSECS) { qDebug() << "Received a request for assignment type" << requestAssignment.getType() << "from" << senderSockAddr; noisyMessageTimer.restart(); } - + SharedAssignmentPointer assignmentToDeploy = deployableAssignmentForRequest(requestAssignment); - + if (assignmentToDeploy) { qDebug() << "Deploying assignment -" << *assignmentToDeploy.data() << "- to" << senderSockAddr; - + // give this assignment out, either the type matches or the requestor said they will take any assignmentPacket.resize(numAssignmentPacketHeaderBytes); - + // setup a copy of this assignment that will have a unique UUID, for packaging purposes Assignment uniqueAssignment(*assignmentToDeploy.data()); uniqueAssignment.setUUID(QUuid::createUuid()); - + QDataStream assignmentStream(&assignmentPacket, QIODevice::Append); - + assignmentStream << uniqueAssignment; - + nodeList->getNodeSocket().writeDatagram(assignmentPacket, senderSockAddr.getAddress(), senderSockAddr.getPort()); - + // add the information for that deployed assignment to the hash of pending assigned nodes PendingAssignedNodeData* pendingNodeData = new PendingAssignedNodeData(assignmentToDeploy->getUUID(), requestAssignment.getWalletUUID()); @@ -705,7 +706,7 @@ void DomainServer::readAvailableDatagrams() { // we're using DTLS, so tell the sender to get back to us using DTLS static QByteArray dtlsRequiredPacket = byteArrayWithPopulatedHeader(PacketTypeDomainServerRequireDTLS); static int numBytesDTLSHeader = numBytesForPacketHeaderGivenPacketType(PacketTypeDomainServerRequireDTLS); - + if (dtlsRequiredPacket.size() == numBytesDTLSHeader) { // pack the port that we accept DTLS traffic on unsigned short dtlsPort = nodeList->getDTLSSocket().localPort(); @@ -721,12 +722,12 @@ void DomainServer::setupPendingAssignmentCredits() { // enumerate the NodeList to find the assigned nodes foreach (const SharedNodePointer& node, LimitedNodeList::getInstance()->getNodeHash()) { DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); - + if (!nodeData->getAssignmentUUID().isNull() && !nodeData->getWalletUUID().isNull()) { // check if we have a non-finalized transaction for this node to add this amount to TransactionHash::iterator i = _pendingAssignmentCredits.find(nodeData->getWalletUUID()); WalletTransaction* existingTransaction = NULL; - + while (i != _pendingAssignmentCredits.end() && i.key() == nodeData->getWalletUUID()) { if (!i.value()->isFinalized()) { existingTransaction = i.value(); @@ -735,16 +736,16 @@ void DomainServer::setupPendingAssignmentCredits() { ++i; } } - + qint64 elapsedMsecsSinceLastPayment = nodeData->getPaymentIntervalTimer().elapsed(); nodeData->getPaymentIntervalTimer().restart(); - + const float CREDITS_PER_HOUR = 0.10f; const float CREDITS_PER_MSEC = CREDITS_PER_HOUR / (60 * 60 * 1000); const int SATOSHIS_PER_MSEC = CREDITS_PER_MSEC * SATOSHIS_PER_CREDIT; - + float pendingCredits = elapsedMsecsSinceLastPayment * SATOSHIS_PER_MSEC; - + if (existingTransaction) { existingTransaction->incrementAmount(pendingCredits); } else { @@ -757,30 +758,30 @@ void DomainServer::setupPendingAssignmentCredits() { } void DomainServer::sendPendingTransactionsToServer() { - + AccountManager& accountManager = AccountManager::getInstance(); - + if (accountManager.hasValidAccessToken()) { - + // enumerate the pending transactions and send them to the server to complete payment TransactionHash::iterator i = _pendingAssignmentCredits.begin(); - + JSONCallbackParameters transactionCallbackParams; - + transactionCallbackParams.jsonCallbackReceiver = this; transactionCallbackParams.jsonCallbackMethod = "transactionJSONCallback"; - + while (i != _pendingAssignmentCredits.end()) { accountManager.authenticatedRequest("api/v1/transactions", QNetworkAccessManager::PostOperation, transactionCallbackParams, i.value()->postJson().toJson()); - + // set this transaction to finalized so we don't add additional credits to it i.value()->setIsFinalized(true); - + ++i; } } - + } void DomainServer::transactionJSONCallback(const QJsonObject& data) { @@ -789,18 +790,18 @@ void DomainServer::transactionJSONCallback(const QJsonObject& data) { // create a dummy wallet transaction to unpack the JSON to WalletTransaction dummyTransaction; dummyTransaction.loadFromJson(data); - + TransactionHash::iterator i = _pendingAssignmentCredits.find(dummyTransaction.getDestinationUUID()); - + while (i != _pendingAssignmentCredits.end() && i.key() == dummyTransaction.getDestinationUUID()) { if (i.value()->getUUID() == dummyTransaction.getUUID()) { // we have a match - we can remove this from the hash of pending credits // and delete it for clean up - + WalletTransaction* matchingTransaction = i.value(); _pendingAssignmentCredits.erase(i); delete matchingTransaction; - + break; } else { ++i; @@ -811,28 +812,28 @@ void DomainServer::transactionJSONCallback(const QJsonObject& data) { void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) { LimitedNodeList* nodeList = LimitedNodeList::getInstance(); - + if (nodeList->packetVersionAndHashMatch(receivedPacket)) { PacketType requestType = packetTypeForPacket(receivedPacket); - + if (requestType == PacketTypeDomainConnectRequest) { handleConnectRequest(receivedPacket, senderSockAddr); } else if (requestType == PacketTypeDomainListRequest) { QUuid nodeUUID = uuidFromPacketHeader(receivedPacket); - + if (!nodeUUID.isNull() && nodeList->nodeWithUUID(nodeUUID)) { NodeType_t throwawayNodeType; HifiSockAddr nodePublicAddress, nodeLocalAddress; - + int numNodeInfoBytes = parseNodeDataFromByteArray(throwawayNodeType, nodePublicAddress, nodeLocalAddress, receivedPacket, senderSockAddr); - + SharedNodePointer checkInNode = nodeList->updateSocketsForNode(nodeUUID, nodePublicAddress, nodeLocalAddress); - + // update last receive to now quint64 timeNow = usecTimestampNow(); checkInNode->setLastHeardMicrostamp(timeNow); - + sendDomainListToNode(checkInNode, senderSockAddr, nodeInterestListFromPacket(receivedPacket, numNodeInfoBytes)); } } else if (requestType == PacketTypeNodeJsonStats) { @@ -871,36 +872,36 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { // add the node UUID nodeJson[JSON_KEY_UUID] = uuidStringWithoutCurlyBraces(node->getUUID()); - + // add the node type nodeJson[JSON_KEY_TYPE] = nodeTypeName; // add the node socket information nodeJson[JSON_KEY_PUBLIC_SOCKET] = jsonForSocket(node->getPublicSocket()); nodeJson[JSON_KEY_LOCAL_SOCKET] = jsonForSocket(node->getLocalSocket()); - + // add the node uptime in our list nodeJson[JSON_KEY_WAKE_TIMESTAMP] = QString::number(node->getWakeTimestamp()); - + // if the node has pool information, add it DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); SharedAssignmentPointer matchingAssignment = _allAssignments.value(nodeData->getAssignmentUUID()); if (matchingAssignment) { nodeJson[JSON_KEY_POOL] = matchingAssignment->getPool(); - + if (!nodeData->getWalletUUID().isNull()) { TransactionHash::iterator i = _pendingAssignmentCredits.find(nodeData->getWalletUUID()); float pendingCreditAmount = 0; - + while (i != _pendingAssignmentCredits.end() && i.key() == nodeData->getWalletUUID()) { pendingCreditAmount += i.value()->getAmount() / SATOSHIS_PER_CREDIT; ++i; } - + nodeJson[JSON_KEY_PENDING_CREDITS] = pendingCreditAmount; } } - + return nodeJson; } @@ -916,157 +917,157 @@ QString pathForAssignmentScript(const QUuid& assignmentUUID) { bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url) { const QString JSON_MIME_TYPE = "application/json"; - + const QString URI_ASSIGNMENT = "/assignment"; const QString URI_NODES = "/nodes"; - + const QString UUID_REGEX_STRING = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"; - + if (connection->requestOperation() == QNetworkAccessManager::GetOperation) { if (url.path() == "/assignments.json") { // user is asking for json list of assignments - + // setup the JSON QJsonObject assignmentJSON; QJsonObject assignedNodesJSON; - + // enumerate the NodeList to find the assigned nodes foreach (const SharedNodePointer& node, LimitedNodeList::getInstance()->getNodeHash()) { DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); - + if (!nodeData->getAssignmentUUID().isNull()) { // add the node using the UUID as the key QString uuidString = uuidStringWithoutCurlyBraces(nodeData->getAssignmentUUID()); assignedNodesJSON[uuidString] = jsonObjectForNode(node); } } - + assignmentJSON["fulfilled"] = assignedNodesJSON; - + QJsonObject queuedAssignmentsJSON; - + // add the queued but unfilled assignments to the json foreach(const SharedAssignmentPointer& assignment, _unfulfilledAssignments) { QJsonObject queuedAssignmentJSON; - + QString uuidString = uuidStringWithoutCurlyBraces(assignment->getUUID()); queuedAssignmentJSON[JSON_KEY_TYPE] = QString(assignment->getTypeName()); - + // if the assignment has a pool, add it if (!assignment->getPool().isEmpty()) { queuedAssignmentJSON[JSON_KEY_POOL] = assignment->getPool(); } - + // add this queued assignment to the JSON queuedAssignmentsJSON[uuidString] = queuedAssignmentJSON; } - + assignmentJSON["queued"] = queuedAssignmentsJSON; - + // print out the created JSON QJsonDocument assignmentDocument(assignmentJSON); connection->respond(HTTPConnection::StatusCode200, assignmentDocument.toJson(), qPrintable(JSON_MIME_TYPE)); - + // we've processed this request return true; } else if (url.path() == "/transactions.json") { // enumerate our pending transactions and display them in an array QJsonObject rootObject; QJsonArray transactionArray; - + TransactionHash::iterator i = _pendingAssignmentCredits.begin(); while (i != _pendingAssignmentCredits.end()) { transactionArray.push_back(i.value()->toJson()); ++i; } - + rootObject["pending_transactions"] = transactionArray; - + // print out the created JSON QJsonDocument transactionsDocument(rootObject); connection->respond(HTTPConnection::StatusCode200, transactionsDocument.toJson(), qPrintable(JSON_MIME_TYPE)); - + return true; } else if (url.path() == QString("%1.json").arg(URI_NODES)) { // setup the JSON QJsonObject rootJSON; QJsonArray nodesJSONArray; - + // enumerate the NodeList to find the assigned nodes LimitedNodeList* nodeList = LimitedNodeList::getInstance(); - + foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { // add the node using the UUID as the key nodesJSONArray.append(jsonObjectForNode(node)); } - + rootJSON["nodes"] = nodesJSONArray; - + // print out the created JSON QJsonDocument nodesDocument(rootJSON); - + // send the response connection->respond(HTTPConnection::StatusCode200, nodesDocument.toJson(), qPrintable(JSON_MIME_TYPE)); - + return true; } else { // check if this is for json stats for a node const QString NODE_JSON_REGEX_STRING = QString("\\%1\\/(%2).json\\/?$").arg(URI_NODES).arg(UUID_REGEX_STRING); QRegExp nodeShowRegex(NODE_JSON_REGEX_STRING); - + if (nodeShowRegex.indexIn(url.path()) != -1) { QUuid matchingUUID = QUuid(nodeShowRegex.cap(1)); - + // see if we have a node that matches this ID SharedNodePointer matchingNode = LimitedNodeList::getInstance()->nodeWithUUID(matchingUUID); if (matchingNode) { // create a QJsonDocument with the stats QJsonObject QJsonObject statsObject = reinterpret_cast(matchingNode->getLinkedData())->getStatsJSONObject(); - + // add the node type to the JSON data for output purposes statsObject["node_type"] = NodeType::getNodeTypeName(matchingNode->getType()).toLower().replace(' ', '-'); - + QJsonDocument statsDocument(statsObject); - + // send the response connection->respond(HTTPConnection::StatusCode200, statsDocument.toJson(), qPrintable(JSON_MIME_TYPE)); - + // tell the caller we processed the request return true; } - + return false; } - + // check if this is a request for a scripted assignment (with a temp unique UUID) const QString ASSIGNMENT_REGEX_STRING = QString("\\%1\\/(%2)\\/?$").arg(URI_ASSIGNMENT).arg(UUID_REGEX_STRING); QRegExp assignmentRegex(ASSIGNMENT_REGEX_STRING); - + if (assignmentRegex.indexIn(url.path()) != -1) { QUuid matchingUUID = QUuid(assignmentRegex.cap(1)); - + SharedAssignmentPointer matchingAssignment = _allAssignments.value(matchingUUID); if (!matchingAssignment) { // check if we have a pending assignment that matches this temp UUID, and it is a scripted assignment PendingAssignedNodeData* pendingData = _pendingAssignedNodes.value(matchingUUID); if (pendingData) { matchingAssignment = _allAssignments.value(pendingData->getAssignmentUUID()); - + if (matchingAssignment && matchingAssignment->getType() == Assignment::AgentType) { // we have a matching assignment and it is for the right type, have the HTTP manager handle it // via correct URL for the script so the client can download - + QUrl scriptURL = url; scriptURL.setPath(URI_ASSIGNMENT + "/" + uuidStringWithoutCurlyBraces(pendingData->getAssignmentUUID())); - + // have the HTTPManager serve the appropriate script file return _httpManager.handleHTTPRequest(connection, scriptURL); } } } - + // request not handled return false; } @@ -1075,92 +1076,92 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url if (url.path() == URI_ASSIGNMENT) { // this is a script upload - ask the HTTPConnection to parse the form data QList formData = connection->parseFormData(); - + // check optional headers for # of instances and pool const QString ASSIGNMENT_INSTANCES_HEADER = "ASSIGNMENT-INSTANCES"; const QString ASSIGNMENT_POOL_HEADER = "ASSIGNMENT-POOL"; - + QByteArray assignmentInstancesValue = connection->requestHeaders().value(ASSIGNMENT_INSTANCES_HEADER.toLocal8Bit()); - + int numInstances = 1; - + if (!assignmentInstancesValue.isEmpty()) { // the user has requested a specific number of instances // so set that on the created assignment - + numInstances = assignmentInstancesValue.toInt(); } - + QString assignmentPool = emptyPool; QByteArray assignmentPoolValue = connection->requestHeaders().value(ASSIGNMENT_POOL_HEADER.toLocal8Bit()); - + if (!assignmentPoolValue.isEmpty()) { // specific pool requested, set that on the created assignment assignmentPool = QString(assignmentPoolValue); } - - + + for (int i = 0; i < numInstances; i++) { - + // create an assignment for this saved script Assignment* scriptAssignment = new Assignment(Assignment::CreateCommand, Assignment::AgentType, assignmentPool); - + QString newPath = pathForAssignmentScript(scriptAssignment->getUUID()); - + // create a file with the GUID of the assignment in the script host location QFile scriptFile(newPath); scriptFile.open(QIODevice::WriteOnly); scriptFile.write(formData[0].second); - + qDebug() << qPrintable(QString("Saved a script for assignment at %1%2") .arg(newPath).arg(assignmentPool == emptyPool ? "" : " - pool is " + assignmentPool)); - + // add the script assigment to the assignment queue SharedAssignmentPointer sharedScriptedAssignment(scriptAssignment); _unfulfilledAssignments.enqueue(sharedScriptedAssignment); _allAssignments.insert(sharedScriptedAssignment->getUUID(), sharedScriptedAssignment); } - + // respond with a 200 code for successful upload connection->respond(HTTPConnection::StatusCode200); - + return true; } } else if (connection->requestOperation() == QNetworkAccessManager::DeleteOperation) { const QString ALL_NODE_DELETE_REGEX_STRING = QString("\\%1\\/?$").arg(URI_NODES); const QString NODE_DELETE_REGEX_STRING = QString("\\%1\\/(%2)\\/$").arg(URI_NODES).arg(UUID_REGEX_STRING); - + QRegExp allNodesDeleteRegex(ALL_NODE_DELETE_REGEX_STRING); QRegExp nodeDeleteRegex(NODE_DELETE_REGEX_STRING); - + if (nodeDeleteRegex.indexIn(url.path()) != -1) { // this is a request to DELETE one node by UUID - + // pull the captured string, if it exists QUuid deleteUUID = QUuid(nodeDeleteRegex.cap(1)); - + SharedNodePointer nodeToKill = LimitedNodeList::getInstance()->nodeWithUUID(deleteUUID); - + if (nodeToKill) { // start with a 200 response connection->respond(HTTPConnection::StatusCode200); - + // we have a valid UUID and node - kill the node that has this assignment QMetaObject::invokeMethod(LimitedNodeList::getInstance(), "killNodeWithUUID", Q_ARG(const QUuid&, deleteUUID)); - + // successfully processed request return true; } - + return true; } else if (allNodesDeleteRegex.indexIn(url.path()) != -1) { qDebug() << "Received request to kill all nodes."; LimitedNodeList::getInstance()->eraseAllNodes(); - + return true; } } - + // didn't process the request, let the HTTPManager try and handle return false; } @@ -1168,44 +1169,44 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &url) { const QString URI_OAUTH = "/oauth"; if (url.path() == URI_OAUTH) { - + QUrlQuery codeURLQuery(url); - + const QString CODE_QUERY_KEY = "code"; QString authorizationCode = codeURLQuery.queryItemValue(CODE_QUERY_KEY); - + const QString STATE_QUERY_KEY = "state"; QUuid stateUUID = QUuid(codeURLQuery.queryItemValue(STATE_QUERY_KEY)); - - + + if (!authorizationCode.isEmpty() && !stateUUID.isNull()) { // fire off a request with this code and state to get an access token for the user - + const QString OAUTH_TOKEN_REQUEST_PATH = "/oauth/token"; QUrl tokenRequestUrl = _oauthProviderURL; tokenRequestUrl.setPath(OAUTH_TOKEN_REQUEST_PATH); - + const QString OAUTH_GRANT_TYPE_POST_STRING = "grant_type=authorization_code"; QString tokenPostBody = OAUTH_GRANT_TYPE_POST_STRING; tokenPostBody += QString("&code=%1&redirect_uri=%2&client_id=%3&client_secret=%4") .arg(authorizationCode, oauthRedirectURL().toString(), _oauthClientID, _oauthClientSecret); - + QNetworkRequest tokenRequest(tokenRequestUrl); tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - + QNetworkReply* tokenReply = _networkAccessManager->post(tokenRequest, tokenPostBody.toLocal8Bit()); - + qDebug() << "Requesting a token for user with session UUID" << uuidStringWithoutCurlyBraces(stateUUID); - + // insert this to our pending token replies so we can associate the returned access token with the right UUID _networkReplyUUIDMap.insert(tokenReply, stateUUID); - + connect(tokenReply, &QNetworkReply::finished, this, &DomainServer::handleTokenRequestFinished); } - + // respond with a 200 code indicating that login is complete connection->respond(HTTPConnection::StatusCode200); - + return true; } else { return false; @@ -1217,25 +1218,25 @@ const QString OAUTH_JSON_ACCESS_TOKEN_KEY = "access_token"; void DomainServer::handleTokenRequestFinished() { QNetworkReply* networkReply = reinterpret_cast(sender()); QUuid matchingSessionUUID = _networkReplyUUIDMap.take(networkReply); - + if (!matchingSessionUUID.isNull() && networkReply->error() == QNetworkReply::NoError) { // pull the access token from the returned JSON and store it with the matching session UUID QJsonDocument returnedJSON = QJsonDocument::fromJson(networkReply->readAll()); QString accessToken = returnedJSON.object()[OAUTH_JSON_ACCESS_TOKEN_KEY].toString(); - + qDebug() << "Received access token for user with UUID" << uuidStringWithoutCurlyBraces(matchingSessionUUID); - + // fire off a request to get this user's identity so we can see if we will let them in QUrl profileURL = _oauthProviderURL; profileURL.setPath("/api/v1/users/profile"); profileURL.setQuery(QString("%1=%2").arg(OAUTH_JSON_ACCESS_TOKEN_KEY, accessToken)); - + QNetworkReply* profileReply = _networkAccessManager->get(QNetworkRequest(profileURL)); - + qDebug() << "Requesting access token for user with session UUID" << uuidStringWithoutCurlyBraces(matchingSessionUUID); - + connect(profileReply, &QNetworkReply::finished, this, &DomainServer::handleProfileRequestFinished); - + _networkReplyUUIDMap.insert(profileReply, matchingSessionUUID); } } @@ -1243,18 +1244,18 @@ void DomainServer::handleTokenRequestFinished() { void DomainServer::handleProfileRequestFinished() { QNetworkReply* networkReply = reinterpret_cast(sender()); QUuid matchingSessionUUID = _networkReplyUUIDMap.take(networkReply); - + if (!matchingSessionUUID.isNull() && networkReply->error() == QNetworkReply::NoError) { QJsonDocument profileJSON = QJsonDocument::fromJson(networkReply->readAll()); - + if (profileJSON.object()["status"].toString() == "success") { // pull the user roles from the response QJsonArray userRolesArray = profileJSON.object()["data"].toObject()["user"].toObject()["roles"].toArray(); - + QJsonArray allowedRolesArray = _argumentVariantMap.value(ALLOWED_ROLES_CONFIG_KEY).toJsonValue().toArray(); - + bool shouldAllowUserToConnect = false; - + foreach(const QJsonValue& roleValue, userRolesArray) { if (allowedRolesArray.contains(roleValue)) { // the user has a role that lets them in @@ -1263,10 +1264,10 @@ void DomainServer::handleProfileRequestFinished() { break; } } - + qDebug() << "Confirmed authentication state for user" << uuidStringWithoutCurlyBraces(matchingSessionUUID) << "-" << shouldAllowUserToConnect; - + // insert this UUID and a flag that indicates if they are allowed to connect _sessionAuthenticationHash.insert(matchingSessionUUID, shouldAllowUserToConnect); } @@ -1276,15 +1277,15 @@ void DomainServer::handleProfileRequestFinished() { void DomainServer::refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment) { QUuid oldUUID = assignment->getUUID(); assignment->resetUUID(); - + qDebug() << "Reset UUID for assignment -" << *assignment.data() << "- and added to queue. Old UUID was" << uuidStringWithoutCurlyBraces(oldUUID); - + if (assignment->getType() == Assignment::AgentType && assignment->getPayload().isEmpty()) { // if this was an Agent without a script URL, we need to rename the old file so it can be retrieved at the new UUID QFile::rename(pathForAssignmentScript(oldUUID), pathForAssignmentScript(assignment->getUUID())); } - + // add the static assignment back under the right UUID, and to the queue _allAssignments.insert(assignment->getUUID(), assignment); _unfulfilledAssignments.enqueue(assignment); @@ -1296,19 +1297,19 @@ void DomainServer::nodeAdded(SharedNodePointer node) { } void DomainServer::nodeKilled(SharedNodePointer node) { - + DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); - + if (nodeData) { // if this node's UUID matches a static assignment we need to throw it back in the assignment queue if (!nodeData->getAssignmentUUID().isNull()) { SharedAssignmentPointer matchedAssignment = _allAssignments.take(nodeData->getAssignmentUUID()); - + if (matchedAssignment && matchedAssignment->isStatic()) { refreshStaticAssignmentAndAddToQueue(matchedAssignment); } } - + // cleanup the connection secrets that we set up for this node (on the other nodes) foreach (const QUuid& otherNodeSessionUUID, nodeData->getSessionSecretHash().keys()) { SharedNodePointer otherNode = LimitedNodeList::getInstance()->nodeWithUUID(otherNodeSessionUUID); @@ -1321,19 +1322,19 @@ void DomainServer::nodeKilled(SharedNodePointer node) { SharedAssignmentPointer DomainServer::matchingQueuedAssignmentForCheckIn(const QUuid& assignmentUUID, NodeType_t nodeType) { QQueue::iterator i = _unfulfilledAssignments.begin(); - + while (i != _unfulfilledAssignments.end()) { if (i->data()->getType() == Assignment::typeForNodeType(nodeType) && i->data()->getUUID() == assignmentUUID) { // we have an unfulfilled assignment to return - + // return the matching assignment return _unfulfilledAssignments.takeAt(i - _unfulfilledAssignments.begin()); } else { ++i; } } - + return SharedAssignmentPointer(); } @@ -1348,17 +1349,17 @@ SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assig bool assignmentTypesMatch = assignment->getType() == requestAssignment.getType(); bool nietherHasPool = assignment->getPool().isEmpty() && requestAssignment.getPool().isEmpty(); bool assignmentPoolsMatch = assignment->getPool() == requestAssignment.getPool(); - + if ((requestIsAllTypes || assignmentTypesMatch) && (nietherHasPool || assignmentPoolsMatch)) { - + // remove the assignment from the queue SharedAssignmentPointer deployableAssignment = _unfulfilledAssignments.takeAt(sharedAssignment - _unfulfilledAssignments.begin()); - + // until we get a connection for this assignment // put assignment back in queue but stick it at the back so the others have a chance to go out _unfulfilledAssignments.enqueue(deployableAssignment); - + // stop looping, we've handed out an assignment return deployableAssignment; } else { @@ -1366,7 +1367,7 @@ SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assig ++sharedAssignment; } } - + return SharedAssignmentPointer(); } @@ -1375,7 +1376,7 @@ void DomainServer::removeMatchingAssignmentFromQueue(const SharedAssignmentPoint while (potentialMatchingAssignment != _unfulfilledAssignments.end()) { if (potentialMatchingAssignment->data()->getUUID() == removableAssignment->getUUID()) { _unfulfilledAssignments.erase(potentialMatchingAssignment); - + // we matched and removed an assignment, bail out break; } else { @@ -1393,19 +1394,19 @@ void DomainServer::addStaticAssignmentsToQueue() { while (staticAssignment != staticHashCopy.end()) { // add any of the un-matched static assignments to the queue bool foundMatchingAssignment = false; - + // enumerate the nodes and check if there is one with an attached assignment with matching UUID foreach (const SharedNodePointer& node, LimitedNodeList::getInstance()->getNodeHash()) { if (node->getUUID() == staticAssignment->data()->getUUID()) { foundMatchingAssignment = true; } } - + if (!foundMatchingAssignment) { // this assignment has not been fulfilled - reset the UUID and add it to the assignment queue refreshStaticAssignmentAndAddToQueue(*staticAssignment); } - + ++staticAssignment; } } From f0cb49ef5c415f2c35e0811444e0886aa099828a Mon Sep 17 00:00:00 2001 From: John Grosen Date: Thu, 12 Jun 2014 17:24:56 -0700 Subject: [PATCH 045/102] added space between expressions in ternary operator --- libraries/shared/src/HifiConfigVariantMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/HifiConfigVariantMap.cpp b/libraries/shared/src/HifiConfigVariantMap.cpp index 7975059736..02cce80104 100644 --- a/libraries/shared/src/HifiConfigVariantMap.cpp +++ b/libraries/shared/src/HifiConfigVariantMap.cpp @@ -45,7 +45,7 @@ QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringL // this option is simply a switch, so add it to the map with a value of `true` mergedMap.insertMulti(key, QVariant(true)); } else { - int maxIndex = (nextKeyIndex == -1) ? argumentList.size(): nextKeyIndex; + int maxIndex = (nextKeyIndex == -1) ? argumentList.size() : nextKeyIndex; // there's at least one value associated with the option // pull the first value to start From 9e151001e032ac78ef796e3318e3942bb527d3fc Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 12 Jun 2014 18:47:18 -0700 Subject: [PATCH 046/102] More object streaming refactory. --- libraries/metavoxels/src/Bitstream.cpp | 392 ++++++++++++------------- libraries/metavoxels/src/Bitstream.h | 129 +++----- 2 files changed, 216 insertions(+), 305 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index f1c927451b..760b1fc646 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -124,7 +124,7 @@ Bitstream::Bitstream(QDataStream& underlying, MetadataType metadataType, Generic _position(0), _metadataType(metadataType), _genericsMode(genericsMode), - _metaObjectStreamer(*this), + _objectStreamerStreamer(*this), _typeStreamerStreamer(*this), _attributeStreamer(*this), _scriptStringStreamer(*this), @@ -198,7 +198,7 @@ void Bitstream::reset() { } Bitstream::WriteMappings Bitstream::getAndResetWriteMappings() { - WriteMappings mappings = { _metaObjectStreamer.getAndResetTransientOffsets(), + WriteMappings mappings = { _objectStreamerStreamer.getAndResetTransientOffsets(), _typeStreamerStreamer.getAndResetTransientOffsets(), _attributeStreamer.getAndResetTransientOffsets(), _scriptStringStreamer.getAndResetTransientOffsets(), @@ -207,7 +207,7 @@ Bitstream::WriteMappings Bitstream::getAndResetWriteMappings() { } void Bitstream::persistWriteMappings(const WriteMappings& mappings) { - _metaObjectStreamer.persistTransientOffsets(mappings.metaObjectOffsets); + _objectStreamerStreamer.persistTransientOffsets(mappings.objectStreamerOffsets); _typeStreamerStreamer.persistTransientOffsets(mappings.typeStreamerOffsets); _attributeStreamer.persistTransientOffsets(mappings.attributeOffsets); _scriptStringStreamer.persistTransientOffsets(mappings.scriptStringOffsets); @@ -235,7 +235,7 @@ void Bitstream::persistAndResetWriteMappings() { } Bitstream::ReadMappings Bitstream::getAndResetReadMappings() { - ReadMappings mappings = { _metaObjectStreamer.getAndResetTransientValues(), + ReadMappings mappings = { _objectStreamerStreamer.getAndResetTransientValues(), _typeStreamerStreamer.getAndResetTransientValues(), _attributeStreamer.getAndResetTransientValues(), _scriptStringStreamer.getAndResetTransientValues(), @@ -244,7 +244,7 @@ Bitstream::ReadMappings Bitstream::getAndResetReadMappings() { } void Bitstream::persistReadMappings(const ReadMappings& mappings) { - _metaObjectStreamer.persistTransientValues(mappings.metaObjectValues); + _objectStreamerStreamer.persistTransientValues(mappings.objectStreamerValues); _typeStreamerStreamer.persistTransientValues(mappings.typeStreamerValues); _attributeStreamer.persistTransientValues(mappings.attributeValues); _scriptStringStreamer.persistTransientValues(mappings.scriptStringValues); @@ -310,20 +310,20 @@ void Bitstream::readRawDelta(QVariant& value, const QVariant& reference) { void Bitstream::writeRawDelta(const QObject* value, const QObject* reference) { if (!value) { - _metaObjectStreamer << NULL; + _objectStreamerStreamer << NULL; return; } const QMetaObject* metaObject = value->metaObject(); - _metaObjectStreamer << metaObject; - foreach (const PropertyWriter& propertyWriter, getPropertyWriters().value(metaObject)) { - propertyWriter.writeDelta(*this, value, reference); - } + const ObjectStreamer* streamer = (metaObject == &GenericSharedObject::staticMetaObject) ? + static_cast(value)->getStreamer().data() : getObjectStreamers().value(metaObject); + _objectStreamerStreamer << streamer; + streamer->writeRawDelta(*this, value, reference); } void Bitstream::readRawDelta(QObject*& value, const QObject* reference) { - ObjectReader objectReader; - _metaObjectStreamer >> objectReader; - value = objectReader.readDelta(*this, reference); + ObjectStreamerPointer streamer; + _objectStreamerStreamer >> streamer; + value = streamer ? streamer->readRawDelta(*this, reference) : NULL; } void Bitstream::writeRawDelta(const QScriptValue& value, const QScriptValue& reference) { @@ -814,38 +814,50 @@ Bitstream& Bitstream::operator>>(GenericValue& value) { Bitstream& Bitstream::operator<<(const QObject* object) { if (!object) { - _metaObjectStreamer << NULL; + _objectStreamerStreamer << NULL; return *this; } const QMetaObject* metaObject = object->metaObject(); - _metaObjectStreamer << metaObject; - foreach (const PropertyWriter& propertyWriter, getPropertyWriters().value(metaObject)) { - propertyWriter.write(*this, object); - } + const ObjectStreamer* streamer = (metaObject == &GenericSharedObject::staticMetaObject) ? + static_cast(object)->getStreamer().data() : getObjectStreamers().value(metaObject); + _objectStreamerStreamer << streamer; + streamer->write(*this, object); return *this; } Bitstream& Bitstream::operator>>(QObject*& object) { - ObjectReader objectReader; - _metaObjectStreamer >> objectReader; - object = objectReader.read(*this); + ObjectStreamerPointer streamer; + _objectStreamerStreamer >> streamer; + object = streamer ? streamer->read(*this) : NULL; return *this; } Bitstream& Bitstream::operator<<(const QMetaObject* metaObject) { - _metaObjectStreamer << metaObject; + _objectStreamerStreamer << getObjectStreamers().value(metaObject); return *this; } Bitstream& Bitstream::operator>>(const QMetaObject*& metaObject) { - ObjectReader objectReader; - _metaObjectStreamer >> objectReader; - metaObject = objectReader.getMetaObject(); + ObjectStreamerPointer streamer; + _objectStreamerStreamer >> streamer; + metaObject = streamer->getMetaObject(); return *this; } -Bitstream& Bitstream::operator>>(ObjectReader& objectReader) { - _metaObjectStreamer >> objectReader; +Bitstream& Bitstream::operator<<(const ObjectStreamer* streamer) { + _objectStreamerStreamer << streamer; + return *this; +} + +Bitstream& Bitstream::operator>>(const ObjectStreamer*& streamer) { + ObjectStreamerPointer objectStreamer; + _objectStreamerStreamer >> objectStreamer; + streamer = objectStreamer.data(); + return *this; +} + +Bitstream& Bitstream::operator>>(ObjectStreamerPointer& streamer) { + _objectStreamerStreamer >> streamer; return *this; } @@ -1075,55 +1087,53 @@ Bitstream& Bitstream::operator>>(SharedObjectPointer& object) { return *this; } -Bitstream& Bitstream::operator<(const QMetaObject* metaObject) { - if (!metaObject) { +Bitstream& Bitstream::operator<(const ObjectStreamer* streamer) { + if (!streamer) { return *this << QByteArray(); } - *this << QByteArray::fromRawData(metaObject->className(), strlen(metaObject->className())); - if (_metadataType == NO_METADATA) { - return *this; - } - const PropertyWriterVector& propertyWriters = getPropertyWriters().value(metaObject); - *this << propertyWriters.size(); - QCryptographicHash hash(QCryptographicHash::Md5); - foreach (const PropertyWriter& propertyWriter, propertyWriters) { - _typeStreamerStreamer << propertyWriter.getStreamer(); - const QMetaProperty& property = propertyWriter.getProperty(); - if (_metadataType == FULL_METADATA) { - *this << QByteArray::fromRawData(property.name(), strlen(property.name())); - } else { - hash.addData(property.name(), strlen(property.name()) + 1); - } - } - if (_metadataType == HASH_METADATA) { - QByteArray hashResult = hash.result(); - write(hashResult.constData(), hashResult.size() * BITS_IN_BYTE); + const char* name = streamer->getName(); + *this << QByteArray::fromRawData(name, strlen(name)); + if (_metadataType != NO_METADATA) { + streamer->writeMetadata(*this, _metadataType == FULL_METADATA); } return *this; } -Bitstream& Bitstream::operator>(ObjectReader& objectReader) { +Bitstream& Bitstream::operator>(ObjectStreamerPointer& streamer) { QByteArray className; *this >> className; if (className.isEmpty()) { - objectReader = ObjectReader(); + streamer = ObjectStreamerPointer(); return *this; } const QMetaObject* metaObject = _metaObjectSubstitutions.value(className); if (!metaObject) { metaObject = getMetaObjects().value(className); } - if (!metaObject) { - qWarning() << "Unknown class name: " << className << "\n"; + // start out with the streamer for the named class, if any + if (metaObject) { + streamer = getObjectStreamers().value(metaObject)->getSelf(); + } else { + streamer = ObjectStreamerPointer(); } if (_metadataType == NO_METADATA) { - objectReader = ObjectReader(className, metaObject, getPropertyReaders().value(metaObject)); + if (!metaObject) { + qWarning() << "Unknown class name:" << className; + } return *this; } - int storedPropertyCount; - *this >> storedPropertyCount; - PropertyReaderVector properties(storedPropertyCount); - for (int i = 0; i < storedPropertyCount; i++) { + if (_genericsMode == ALL_GENERICS) { + streamer = readGenericObjectStreamer(className); + return *this; + } + if (!metaObject && _genericsMode == FALLBACK_GENERICS) { + streamer = readGenericObjectStreamer(className); + return *this; + } + int propertyCount; + *this >> propertyCount; + QVector properties(propertyCount); + for (int i = 0; i < propertyCount; i++) { TypeStreamerPointer typeStreamer; *this >> typeStreamer; QMetaProperty property = QMetaProperty(); @@ -1134,23 +1144,22 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { property = metaObject->property(metaObject->indexOfProperty(propertyName)); } } - properties[i] = PropertyReader(typeStreamer, property); + properties[i] = StreamerPropertyPair(typeStreamer, property); } // for hash metadata, check the names/types of the properties as well as the name hash against our own class if (_metadataType == HASH_METADATA) { QCryptographicHash hash(QCryptographicHash::Md5); bool matches = true; if (metaObject) { - const PropertyWriterVector& propertyWriters = getPropertyWriters().value(metaObject); - if (propertyWriters.size() == properties.size()) { - for (int i = 0; i < propertyWriters.size(); i++) { - const PropertyWriter& propertyWriter = propertyWriters.at(i); - if (properties.at(i).getStreamer() != propertyWriter.getStreamer()) { + const QVector& localProperties = streamer->getProperties(); + if (localProperties.size() == properties.size()) { + for (int i = 0; i < localProperties.size(); i++) { + const StreamerPropertyPair& localProperty = localProperties.at(i); + if (localProperty.first != properties.at(i).first) { matches = false; break; } - const QMetaProperty& property = propertyWriter.getProperty(); - hash.addData(property.name(), strlen(property.name()) + 1); + hash.addData(localProperty.second.name(), strlen(localProperty.second.name()) + 1); } } else { matches = false; @@ -1160,11 +1169,24 @@ Bitstream& Bitstream::operator>(ObjectReader& objectReader) { QByteArray remoteHashResult(localHashResult.size(), 0); read(remoteHashResult.data(), remoteHashResult.size() * BITS_IN_BYTE); if (metaObject && matches && localHashResult == remoteHashResult) { - objectReader = ObjectReader(className, metaObject, getPropertyReaders().value(metaObject)); return *this; } + } else if (metaObject) { + const QVector& localProperties = streamer->getProperties(); + if (localProperties.size() != properties.size()) { + streamer = ObjectStreamerPointer(new MappedObjectStreamer(metaObject, properties)); + return *this; + } + for (int i = 0; i < localProperties.size(); i++) { + const StreamerPropertyPair& property = properties.at(i); + if (localProperties.at(i).first != property.first || property.second.propertyIndex() != i) { + streamer = ObjectStreamerPointer(new MappedObjectStreamer(metaObject, properties)); + return *this; + } + } + return *this; } - objectReader = ObjectReader(className, metaObject, properties); + streamer = ObjectStreamerPointer(new MappedObjectStreamer(metaObject, properties)); return *this; } @@ -1405,12 +1427,12 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) { QPointer reference = _sharedObjectReferences.value(originID); QPointer& pointer = _weakSharedObjectHash[id]; if (pointer) { - ObjectReader objectReader; - _metaObjectStreamer >> objectReader; + ObjectStreamerPointer objectStreamer; + _objectStreamerStreamer >> objectStreamer; if (reference) { - objectReader.readDelta(*this, reference.data(), pointer.data()); + objectStreamer->readRawDelta(*this, reference.data(), pointer.data()); } else { - objectReader.read(*this, pointer.data()); + objectStreamer->read(*this, pointer.data()); } } else { QObject* rawObject; @@ -1445,6 +1467,31 @@ void Bitstream::clearSharedObject(QObject* object) { const int MD5_HASH_SIZE = 16; +ObjectStreamerPointer Bitstream::readGenericObjectStreamer(const QByteArray& name) { + int propertyCount; + *this >> propertyCount; + QVector properties(propertyCount); + QByteArray hash; + if (propertyCount > 0) { + for (int i = 0; i < propertyCount; i++) { + TypeStreamerPointer streamer; + *this >> streamer; + QByteArray name; + if (_metadataType == FULL_METADATA) { + *this >> name; + } + properties[i] = StreamerNamePair(streamer, name); + } + if (_metadataType == HASH_METADATA) { + hash.resize(MD5_HASH_SIZE); + read(hash.data(), hash.size() * BITS_IN_BYTE); + } + } + ObjectStreamerPointer streamer = ObjectStreamerPointer(new GenericObjectStreamer(name, properties, hash)); + static_cast(streamer.data())->_weakSelf = streamer; + return streamer; +} + TypeStreamerPointer Bitstream::readGenericTypeStreamer(const QByteArray& name, int category) { TypeStreamerPointer streamer; switch (category) { @@ -1528,6 +1575,40 @@ QMultiHash& Bitstream::getMetaObjectSubC return metaObjectSubClasses; } +const QHash& Bitstream::getObjectStreamers() { + static QHash objectStreamers = createObjectStreamers(); + return objectStreamers; +} + +QHash Bitstream::createObjectStreamers() { + QHash objectStreamers; + foreach (const QMetaObject* metaObject, getMetaObjects()) { + QVector properties; + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (!property.isStored()) { + continue; + } + const TypeStreamer* streamer; + if (property.isEnumType()) { + QMetaEnum metaEnum = property.enumerator(); + streamer = getEnumStreamers().value(ScopeNamePair( + QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())), + QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name())))); + } else { + streamer = getTypeStreamers().value(property.userType()); + } + if (streamer) { + properties.append(StreamerPropertyPair(streamer->getSelf(), property)); + } + } + ObjectStreamerPointer streamer = ObjectStreamerPointer(new MappedObjectStreamer(metaObject, properties)); + streamer->_self = streamer; + objectStreamers.insert(metaObject, streamer.data()); + } + return objectStreamers; +} + QHash& Bitstream::getTypeStreamers() { static QHash typeStreamers; return typeStreamers; @@ -1577,69 +1658,17 @@ const TypeStreamer* Bitstream::createInvalidTypeStreamer() { return streamer; } -const QHash& Bitstream::getPropertyReaders() { - static QHash propertyReaders = createPropertyReaders(); - return propertyReaders; +ObjectStreamer::ObjectStreamer(const QMetaObject* metaObject) : + _metaObject(metaObject) { } -QHash Bitstream::createPropertyReaders() { - QHash propertyReaders; - foreach (const QMetaObject* metaObject, getMetaObjects()) { - PropertyReaderVector& readers = propertyReaders[metaObject]; - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (!property.isStored()) { - continue; - } - const TypeStreamer* streamer; - if (property.isEnumType()) { - QMetaEnum metaEnum = property.enumerator(); - streamer = getEnumStreamers().value(ScopeNamePair( - QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())), - QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name())))); - } else { - streamer = getTypeStreamers().value(property.userType()); - } - if (streamer) { - readers.append(PropertyReader(streamer->getSelf(), property)); - } - } - } - return propertyReaders; +const QVector& ObjectStreamer::getProperties() const { + static QVector emptyProperties; + return emptyProperties; } -const QHash& Bitstream::getPropertyWriters() { - static QHash propertyWriters = createPropertyWriters(); - return propertyWriters; -} - -QHash Bitstream::createPropertyWriters() { - QHash propertyWriters; - foreach (const QMetaObject* metaObject, getMetaObjects()) { - PropertyWriterVector& writers = propertyWriters[metaObject]; - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (!property.isStored()) { - continue; - } - const TypeStreamer* streamer; - if (property.isEnumType()) { - QMetaEnum metaEnum = property.enumerator(); - streamer = getEnumStreamers().value(ScopeNamePair( - QByteArray::fromRawData(metaEnum.scope(), strlen(metaEnum.scope())), - QByteArray::fromRawData(metaEnum.name(), strlen(metaEnum.name())))); - } else { - streamer = getTypeStreamers().value(property.userType()); - } - if (streamer) { - writers.append(PropertyWriter(property, streamer)); - } - } - } - return propertyWriters; -} - -MappedObjectStreamer::MappedObjectStreamer(const QVector& properties) : +MappedObjectStreamer::MappedObjectStreamer(const QMetaObject* metaObject, const QVector& properties) : + ObjectStreamer(metaObject), _properties(properties) { } @@ -1647,6 +1676,10 @@ const char* MappedObjectStreamer::getName() const { return _metaObject->className(); } +const QVector& MappedObjectStreamer::getProperties() const { + return _properties; +} + void MappedObjectStreamer::writeMetadata(Bitstream& out, bool full) const { out << _properties.size(); if (_properties.isEmpty()) { @@ -1667,13 +1700,13 @@ void MappedObjectStreamer::writeMetadata(Bitstream& out, bool full) const { } } -void MappedObjectStreamer::write(Bitstream& out, QObject* object) const { +void MappedObjectStreamer::write(Bitstream& out, const QObject* object) const { foreach (const StreamerPropertyPair& property, _properties) { property.first->write(out, property.second.read(object)); } } -void MappedObjectStreamer::writeDelta(Bitstream& out, QObject* object, QObject* reference) const { +void MappedObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const { foreach (const StreamerPropertyPair& property, _properties) { property.first->writeDelta(out, property.second.read(object), (reference && reference->metaObject() == _metaObject) ? property.second.read(reference) : QVariant()); @@ -1693,14 +1726,14 @@ QObject* MappedObjectStreamer::read(Bitstream& in, QObject* object) const { return object; } -QObject* MappedObjectStreamer::readDelta(Bitstream& in, const QObject* reference, QObject* object) const { +QObject* MappedObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const { if (!object && _metaObject) { object = _metaObject->newInstance(); } foreach (const StreamerPropertyPair& property, _properties) { QVariant value; - property.first->readDelta(in, value, (property.second.isValid() && reference) ? - property.second.read(reference) : QVariant()); + property.first->readDelta(in, value, (property.second.isValid() && reference && + reference->metaObject() == _metaObject) ? property.second.read(reference) : QVariant()); if (property.second.isValid() && object) { property.second.write(object, value); } @@ -1710,11 +1743,10 @@ QObject* MappedObjectStreamer::readDelta(Bitstream& in, const QObject* reference GenericObjectStreamer::GenericObjectStreamer(const QByteArray& name, const QVector& properties, const QByteArray& hash) : + ObjectStreamer(&GenericSharedObject::staticMetaObject), _name(name), _properties(properties), _hash(hash) { - - _metaObject = &GenericSharedObject::staticMetaObject; } const char* GenericObjectStreamer::getName() const { @@ -1744,17 +1776,22 @@ void GenericObjectStreamer::writeMetadata(Bitstream& out, bool full) const { } } -void GenericObjectStreamer::write(Bitstream& out, QObject* object) const { - const QVariantList& values = static_cast(object)->getValues(); +void GenericObjectStreamer::write(Bitstream& out, const QObject* object) const { + const QVariantList& values = static_cast(object)->getValues(); for (int i = 0; i < _properties.size(); i++) { _properties.at(i).first->write(out, values.at(i)); } } -void GenericObjectStreamer::writeDelta(Bitstream& out, QObject* object, QObject* reference) const { +void GenericObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const { + const GenericSharedObject* genericObject = static_cast(object); + const GenericSharedObject* genericReference = (reference && + reference->metaObject() == &GenericSharedObject::staticMetaObject) ? + static_cast(reference) : NULL; for (int i = 0; i < _properties.size(); i++) { - _properties.at(i).first->writeDelta(out, static_cast(object)->getValues().at(i), reference ? - static_cast(reference)->getValues().at(i) : QVariant()); + _properties.at(i).first->writeDelta(out, genericObject->getValues().at(i), + (genericReference && genericReference->getStreamer() == genericObject->getStreamer()) ? + genericReference->getValues().at(i) : QVariant()); } } @@ -1770,7 +1807,7 @@ QObject* GenericObjectStreamer::read(Bitstream& in, QObject* object) const { return object; } -QObject* GenericObjectStreamer::readDelta(Bitstream& in, const QObject* reference, QObject* object) const { +QObject* GenericObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const { if (!object) { object = new GenericSharedObject(_weakSelf); } @@ -1786,75 +1823,6 @@ QObject* GenericObjectStreamer::readDelta(Bitstream& in, const QObject* referenc return object; } -ObjectReader::ObjectReader(const QByteArray& className, const QMetaObject* metaObject, - const PropertyReaderVector& properties) : - _className(className), - _metaObject(metaObject), - _properties(properties) { -} - -QObject* ObjectReader::read(Bitstream& in, QObject* object) const { - if (!object && _metaObject) { - object = _metaObject->newInstance(); - } - foreach (const PropertyReader& property, _properties) { - property.read(in, object); - } - return object; -} - -QObject* ObjectReader::readDelta(Bitstream& in, const QObject* reference, QObject* object) const { - if (!object && _metaObject) { - object = _metaObject->newInstance(); - } - foreach (const PropertyReader& property, _properties) { - property.readDelta(in, object, reference); - } - return object; -} - -uint qHash(const ObjectReader& objectReader, uint seed) { - return qHash(objectReader.getClassName(), seed); -} - -QDebug& operator<<(QDebug& debug, const ObjectReader& objectReader) { - return debug << objectReader.getClassName(); -} - -PropertyReader::PropertyReader(const TypeStreamerPointer& streamer, const QMetaProperty& property) : - _streamer(streamer), - _property(property) { -} - -void PropertyReader::read(Bitstream& in, QObject* object) const { - QVariant value = _streamer->read(in); - if (_property.isValid() && object) { - _property.write(object, value); - } -} - -void PropertyReader::readDelta(Bitstream& in, QObject* object, const QObject* reference) const { - QVariant value; - _streamer->readDelta(in, value, (_property.isValid() && reference) ? _property.read(reference) : QVariant()); - if (_property.isValid() && object) { - _property.write(object, value); - } -} - -PropertyWriter::PropertyWriter(const QMetaProperty& property, const TypeStreamer* streamer) : - _property(property), - _streamer(streamer) { -} - -void PropertyWriter::write(Bitstream& out, const QObject* object) const { - _streamer->write(out, _property.read(object)); -} - -void PropertyWriter::writeDelta(Bitstream& out, const QObject* object, const QObject* reference) const { - _streamer->writeDelta(out, _property.read(object), reference && object->metaObject() == reference->metaObject() ? - _property.read(reference) : QVariant()); -} - MetaField::MetaField(const QByteArray& name, const TypeStreamer* streamer) : _name(name), _streamer(streamer) { diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 07e4b94568..10c28b9821 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -39,8 +39,6 @@ class GenericValue; class ObjectReader; class ObjectStreamer; class OwnedAttributeValue; -class PropertyReader; -class PropertyWriter; class TypeStreamer; typedef SharedObjectPointerTemplate AttributePointer; @@ -51,8 +49,6 @@ typedef QSharedPointer ObjectStreamerPointer; typedef QWeakPointer WeakObjectStreamerPointer; typedef QSharedPointer TypeStreamerPointer; typedef QWeakPointer WeakTypeStreamerPointer; -typedef QVector PropertyReaderVector; -typedef QVector PropertyWriterVector; typedef QPair QVariantPair; typedef QList QVariantPairList; @@ -207,7 +203,7 @@ public: class WriteMappings { public: - QHash metaObjectOffsets; + QHash objectStreamerOffsets; QHash typeStreamerOffsets; QHash attributeOffsets; QHash scriptStringOffsets; @@ -216,7 +212,7 @@ public: class ReadMappings { public: - QHash metaObjectValues; + QHash objectStreamerValues; QHash typeStreamerValues; QHash attributeValues; QHash scriptStringValues; @@ -403,7 +399,10 @@ public: Bitstream& operator<<(const QMetaObject* metaObject); Bitstream& operator>>(const QMetaObject*& metaObject); - Bitstream& operator>>(ObjectReader& objectReader); + + Bitstream& operator<<(const ObjectStreamer* streamer); + Bitstream& operator>>(const ObjectStreamer*& streamer); + Bitstream& operator>>(ObjectStreamerPointer& streamer); Bitstream& operator<<(const TypeStreamer* streamer); Bitstream& operator>>(const TypeStreamer*& streamer); @@ -421,11 +420,11 @@ public: Bitstream& operator<<(const SharedObjectPointer& object); Bitstream& operator>>(SharedObjectPointer& object); - Bitstream& operator<(const QMetaObject* metaObject); - Bitstream& operator>(ObjectReader& objectReader); + Bitstream& operator<(const ObjectStreamer* streamer); + Bitstream& operator>(ObjectStreamerPointer& streamer); Bitstream& operator<(const TypeStreamer* streamer); - Bitstream& operator>(TypeStreamerPointer& reader); + Bitstream& operator>(TypeStreamerPointer& streamer); Bitstream& operator<(const AttributePointer& attribute); Bitstream& operator>(AttributePointer& attribute); @@ -446,6 +445,7 @@ private slots: private: + ObjectStreamerPointer readGenericObjectStreamer(const QByteArray& name); TypeStreamerPointer readGenericTypeStreamer(const QByteArray& name, int category); QDataStream& _underlying; @@ -455,7 +455,7 @@ private: MetadataType _metadataType; GenericsMode _genericsMode; - RepeatedValueStreamer _metaObjectStreamer; + RepeatedValueStreamer _objectStreamerStreamer; RepeatedValueStreamer _typeStreamerStreamer; RepeatedValueStreamer _attributeStreamer; RepeatedValueStreamer _scriptStringStreamer; @@ -472,6 +472,9 @@ private: static QMultiHash& getMetaObjectSubClasses(); static QHash& getTypeStreamers(); + static const QHash& getObjectStreamers(); + static QHash createObjectStreamers(); + static const QHash& getEnumStreamers(); static QHash createEnumStreamers(); @@ -480,12 +483,6 @@ private: static const TypeStreamer* getInvalidTypeStreamer(); static const TypeStreamer* createInvalidTypeStreamer(); - - static const QHash& getPropertyReaders(); - static QHash createPropertyReaders(); - - static const QHash& getPropertyWriters(); - static QHash createPropertyWriters(); }; template inline void Bitstream::writeDelta(const T& value, const T& reference) { @@ -769,37 +766,46 @@ template inline Bitstream& Bitstream::operator>>(QHash& return *this; } +typedef QPair StreamerPropertyPair; + /// Contains the information required to stream an object. class ObjectStreamer { public: - virtual const char* getName() const = 0; + ObjectStreamer(const QMetaObject* metaObject); + + const QMetaObject* getMetaObject() const { return _metaObject; } + const ObjectStreamerPointer& getSelf() const { return _self; } + + virtual const char* getName() const = 0; + virtual const QVector& getProperties() const; virtual void writeMetadata(Bitstream& out, bool full) const = 0; - virtual void write(Bitstream& out, QObject* object) const = 0; - virtual void writeDelta(Bitstream& out, QObject* object, QObject* reference) const = 0; + virtual void write(Bitstream& out, const QObject* object) const = 0; + virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const = 0; virtual QObject* read(Bitstream& in, QObject* object = NULL) const = 0; - virtual QObject* readDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const = 0; + virtual QObject* readRawDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const = 0; protected: + + friend class Bitstream; const QMetaObject* _metaObject; ObjectStreamerPointer _self; }; -typedef QPair StreamerPropertyPair; - /// A streamer that maps to a local class. class MappedObjectStreamer : public ObjectStreamer { public: - MappedObjectStreamer(const QVector& properties); + MappedObjectStreamer(const QMetaObject* metaObject, const QVector& properties); virtual const char* getName() const; + virtual const QVector& getProperties() const; virtual void writeMetadata(Bitstream& out, bool full) const; - virtual void write(Bitstream& out, QObject* object) const; - virtual void writeDelta(Bitstream& out, QObject* object, QObject* reference) const; + virtual void write(Bitstream& out, const QObject* object) const; + virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const; virtual QObject* read(Bitstream& in, QObject* object = NULL) const; - virtual QObject* readDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const; + virtual QObject* readRawDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const; private: @@ -816,10 +822,10 @@ public: virtual const char* getName() const; virtual void writeMetadata(Bitstream& out, bool full) const; - virtual void write(Bitstream& out, QObject* object) const; - virtual void writeDelta(Bitstream& out, QObject* object, QObject* reference) const; + virtual void write(Bitstream& out, const QObject* object) const; + virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const; virtual QObject* read(Bitstream& in, QObject* object = NULL) const; - virtual QObject* readDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const; + virtual QObject* readRawDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const; private: @@ -831,69 +837,6 @@ private: QByteArray _hash; }; -/// Contains the information required to read an object from the stream. -class ObjectReader { -public: - - ObjectReader(const QByteArray& className = QByteArray(), const QMetaObject* metaObject = NULL, - const QVector& properties = QVector()); - - const QByteArray& getClassName() const { return _className; } - const QMetaObject* getMetaObject() const { return _metaObject; } - - QObject* read(Bitstream& in, QObject* object = NULL) const; - QObject* readDelta(Bitstream& in, const QObject* reference, QObject* object = NULL) const; - - bool operator==(const ObjectReader& other) const { return _className == other._className; } - bool operator!=(const ObjectReader& other) const { return _className != other._className; } - -private: - - QByteArray _className; - const QMetaObject* _metaObject; - QVector _properties; -}; - -uint qHash(const ObjectReader& objectReader, uint seed = 0); - -QDebug& operator<<(QDebug& debug, const ObjectReader& objectReader); - -/// Contains the information required to read an object property from the stream and apply it. -class PropertyReader { -public: - - PropertyReader(const TypeStreamerPointer& streamer = TypeStreamerPointer(), - const QMetaProperty& property = QMetaProperty()); - - const TypeStreamerPointer& getStreamer() const { return _streamer; } - - void read(Bitstream& in, QObject* object) const; - void readDelta(Bitstream& in, QObject* object, const QObject* reference) const; - -private: - - TypeStreamerPointer _streamer; - QMetaProperty _property; -}; - -/// Contains the information required to obtain an object property and write it to the stream. -class PropertyWriter { -public: - - PropertyWriter(const QMetaProperty& property = QMetaProperty(), const TypeStreamer* streamer = NULL); - - const QMetaProperty& getProperty() const { return _property; } - const TypeStreamer* getStreamer() const { return _streamer; } - - void write(Bitstream& out, const QObject* object) const; - void writeDelta(Bitstream& out, const QObject* object, const QObject* reference) const; - -private: - - QMetaProperty _property; - const TypeStreamer* _streamer; -}; - /// Describes a metatype field. class MetaField { public: From 14f56310f605cfe67abc1b43bd7b6a6856527f21 Mon Sep 17 00:00:00 2001 From: John Grosen Date: Thu, 12 Jun 2014 23:28:43 -0700 Subject: [PATCH 047/102] Changed AccountManager to only rely on a proper OAuth response. This involved making another request after the initial OAuth authorization. Because of this now-two-step process, some methods and signals were renamed to make their purpose more clear. Additionally, a _hasProfile member variable was added to DataServerAccountInfo in order to allow for knowledge of whether the profile elements had been fetched when being deserialized from disk. Error handling for the whole process is still nonexistant. --- interface/src/Menu.cpp | 9 +- interface/src/XmppClient.cpp | 2 +- libraries/networking/src/AccountManager.cpp | 175 +++++++++++------- libraries/networking/src/AccountManager.h | 35 ++-- .../networking/src/DataServerAccountInfo.cpp | 31 +++- .../networking/src/DataServerAccountInfo.h | 17 +- 6 files changed, 172 insertions(+), 97 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 3015a638f6..0b49c08f51 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -127,7 +127,7 @@ Menu::Menu() : toggleLoginMenuItem(); // connect to the appropriate slots of the AccountManager so that we can change the Login/Logout menu item - connect(&accountManager, &AccountManager::accessTokenChanged, this, &Menu::toggleLoginMenuItem); + connect(&accountManager, &AccountManager::profileChanged, this, &Menu::toggleLoginMenuItem); connect(&accountManager, &AccountManager::logoutComplete, this, &Menu::toggleLoginMenuItem); addDisabledActionAndSeparator(fileMenu, "Scripts"); @@ -326,7 +326,7 @@ Menu::Menu() : shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, "None", 0, true)); shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::SimpleShadows, 0, false)); shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::CascadedShadows, 0, false)); - + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, true); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::BuckyBalls, 0, false); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Particles, 0, true); @@ -407,7 +407,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::DisableNackPackets, 0, false); addDisabledActionAndSeparator(developerMenu, "Testing"); - + QMenu* timingMenu = developerMenu->addMenu("Timing and Statistics Tools"); QMenu* perfTimerMenu = timingMenu->addMenu("Performance Timer"); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayTimingDetails, 0, true); @@ -462,7 +462,7 @@ Menu::Menu() : false, appInstance->getAudio(), SLOT(toggleToneInjection())); - addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScope, + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScope, Qt::CTRL | Qt::Key_P, false, appInstance->getAudio(), SLOT(toggleScope())); @@ -1775,4 +1775,3 @@ QString Menu::getSnapshotsLocation() const { } return _snapshotsLocation; } - diff --git a/interface/src/XmppClient.cpp b/interface/src/XmppClient.cpp index 666906681c..ef9db55620 100644 --- a/interface/src/XmppClient.cpp +++ b/interface/src/XmppClient.cpp @@ -23,7 +23,7 @@ XmppClient::XmppClient() : _xmppMUCManager() { AccountManager& accountManager = AccountManager::getInstance(); - connect(&accountManager, SIGNAL(accessTokenChanged()), this, SLOT(connectToServer())); + connect(&accountManager, SIGNAL(profileChanged()), this, SLOT(connectToServer())); connect(&accountManager, SIGNAL(logoutComplete()), this, SLOT(disconnectFromServer())); } diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index b4aedbcb7c..918261a953 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -55,13 +55,13 @@ AccountManager::AccountManager() : { qRegisterMetaType("OAuthAccessToken"); qRegisterMetaTypeStreamOperators("OAuthAccessToken"); - + qRegisterMetaType("DataServerAccountInfo"); qRegisterMetaTypeStreamOperators("DataServerAccountInfo"); - + qRegisterMetaType("QNetworkAccessManager::Operation"); qRegisterMetaType("JSONCallbackParameters"); - + connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged); } @@ -70,18 +70,18 @@ const QString DOUBLE_SLASH_SUBSTITUTE = "slashslash"; void AccountManager::logout() { // a logout means we want to delete the DataServerAccountInfo we currently have for this URL, in-memory and in file _accountInfo = DataServerAccountInfo(); - + emit balanceChanged(0); connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged); - + QSettings settings; settings.beginGroup(ACCOUNTS_GROUP); - + QString keyURLString(_authURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE)); settings.remove(keyURLString); - + qDebug() << "Removed account info for" << _authURL << "from in-memory accounts and .ini file"; - + emit logoutComplete(); // the username has changed to blank emit usernameChanged(QString()); @@ -93,7 +93,7 @@ void AccountManager::updateBalance() { JSONCallbackParameters callbackParameters; callbackParameters.jsonCallbackReceiver = &_accountInfo; callbackParameters.jsonCallbackMethod = "setBalanceFromJSON"; - + authenticatedRequest("/api/v1/wallets/mine", QNetworkAccessManager::GetOperation, callbackParameters); } } @@ -105,28 +105,33 @@ void AccountManager::accountInfoBalanceChanged(qint64 newBalance) { void AccountManager::setAuthURL(const QUrl& authURL) { if (_authURL != authURL) { _authURL = authURL; - + qDebug() << "URL for node authentication has been changed to" << qPrintable(_authURL.toString()); qDebug() << "Re-setting authentication flow."; - + // check if there are existing access tokens to load from settings QSettings settings; settings.beginGroup(ACCOUNTS_GROUP); - + foreach(const QString& key, settings.allKeys()) { // take a key copy to perform the double slash replacement QString keyCopy(key); QUrl keyURL(keyCopy.replace("slashslash", "//")); - + if (keyURL == _authURL) { // pull out the stored access token and store it in memory _accountInfo = settings.value(key).value(); qDebug() << "Found a data-server access token for" << qPrintable(keyURL.toString()); - - emit accessTokenChanged(); + + // profile info isn't guaranteed to be saved too + if (_accountInfo.hasProfile()) { + emit profileChanged(); + } else { + requestProfile(); + } } } - + // tell listeners that the auth endpoint has changed emit authEndpointChanged(); } @@ -147,36 +152,36 @@ void AccountManager::authenticatedRequest(const QString& path, QNetworkAccessMan void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager::Operation operation, const JSONCallbackParameters& callbackParams, const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart) { - + if (!_networkAccessManager) { _networkAccessManager = new QNetworkAccessManager(this); } - + if (hasValidAccessToken()) { QNetworkRequest authenticatedRequest; - + QUrl requestURL = _authURL; - + if (path.startsWith("/")) { requestURL.setPath(path); } else { requestURL.setPath("/" + path); } - + requestURL.setQuery("access_token=" + _accountInfo.getAccessToken().token); - + authenticatedRequest.setUrl(requestURL); - + if (VERBOSE_HTTP_REQUEST_DEBUGGING) { qDebug() << "Making an authenticated request to" << qPrintable(requestURL.toString()); - + if (!dataByteArray.isEmpty()) { qDebug() << "The POST/PUT body -" << QString(dataByteArray); } } - + QNetworkReply* networkReply = NULL; - + switch (operation) { case QNetworkAccessManager::GetOperation: networkReply = _networkAccessManager->get(authenticatedRequest); @@ -198,24 +203,24 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager:: networkReply = _networkAccessManager->put(authenticatedRequest, dataByteArray); } } - + break; default: // other methods not yet handled break; } - + if (networkReply) { if (!callbackParams.isEmpty()) { // if we have information for a callback, insert the callbackParams into our local map _pendingCallbackMap.insert(networkReply, callbackParams); - + if (callbackParams.updateReciever && !callbackParams.updateSlot.isEmpty()) { callbackParams.updateReciever->connect(networkReply, SIGNAL(uploadProgress(qint64, qint64)), callbackParams.updateSlot.toStdString().c_str()); } } - + // if we ended up firing of a request, hook up to it now connect(networkReply, SIGNAL(finished()), SLOT(processReply())); } @@ -224,7 +229,7 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager:: void AccountManager::processReply() { QNetworkReply* requestReply = reinterpret_cast(sender()); - + if (requestReply->error() == QNetworkReply::NoError) { passSuccessToCallback(requestReply); } else { @@ -235,17 +240,17 @@ void AccountManager::processReply() { void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) { QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll()); - + JSONCallbackParameters callbackParams = _pendingCallbackMap.value(requestReply); - + if (callbackParams.jsonCallbackReceiver) { // invoke the right method on the callback receiver QMetaObject::invokeMethod(callbackParams.jsonCallbackReceiver, qPrintable(callbackParams.jsonCallbackMethod), Q_ARG(const QJsonObject&, jsonResponse.object())); - + // remove the related reply-callback group from the map _pendingCallbackMap.remove(requestReply); - + } else { if (VERBOSE_HTTP_REQUEST_DEBUGGING) { qDebug() << "Received JSON response from data-server that has no matching callback."; @@ -256,13 +261,13 @@ void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) { void AccountManager::passErrorToCallback(QNetworkReply* requestReply) { JSONCallbackParameters callbackParams = _pendingCallbackMap.value(requestReply); - + if (callbackParams.errorCallbackReceiver) { // invoke the right method on the callback receiver QMetaObject::invokeMethod(callbackParams.errorCallbackReceiver, qPrintable(callbackParams.errorCallbackMethod), Q_ARG(QNetworkReply::NetworkError, requestReply->error()), Q_ARG(const QString&, requestReply->errorString())); - + // remove the related reply-callback group from the map _pendingCallbackMap.remove(requestReply); } else { @@ -274,12 +279,12 @@ void AccountManager::passErrorToCallback(QNetworkReply* requestReply) { } bool AccountManager::hasValidAccessToken() { - + if (_accountInfo.getAccessToken().token.isEmpty() || _accountInfo.getAccessToken().isExpired()) { if (VERBOSE_HTTP_REQUEST_DEBUGGING) { qDebug() << "An access token is required for requests to" << qPrintable(_authURL.toString()); } - + return false; } else { return true; @@ -288,12 +293,12 @@ bool AccountManager::hasValidAccessToken() { bool AccountManager::checkAndSignalForAccessToken() { bool hasToken = hasValidAccessToken(); - + if (!hasToken) { // emit a signal so somebody can call back to us and request an access token given a username and password emit authRequired(); } - + return hasToken; } @@ -304,36 +309,36 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas } QNetworkRequest request; - + QUrl grantURL = _authURL; grantURL.setPath("/oauth/token"); - + const QString ACCOUNT_MANAGER_REQUESTED_SCOPE = "owner"; - + QByteArray postData; postData.append("grant_type=password&"); postData.append("username=" + login + "&"); postData.append("password=" + QUrl::toPercentEncoding(password) + "&"); postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE); - + request.setUrl(grantURL); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - + QNetworkReply* requestReply = _networkAccessManager->post(request, postData); - connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestFinished); - connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError))); + connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished); + connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError))); } -void AccountManager::requestFinished() { +void AccountManager::requestAccessTokenFinished() { QNetworkReply* requestReply = reinterpret_cast(sender()); - + QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll()); const QJsonObject& rootObject = jsonResponse.object(); - + if (!rootObject.contains("error")) { // construct an OAuthAccessToken from the json object - + if (!rootObject.contains("access_token") || !rootObject.contains("expires_in") || !rootObject.contains("token_type")) { // TODO: error handling - malformed token response @@ -342,23 +347,21 @@ void AccountManager::requestFinished() { // clear the path from the response URL so we have the right root URL for this access token QUrl rootURL = requestReply->url(); rootURL.setPath(""); - + qDebug() << "Storing an account with access-token for" << qPrintable(rootURL.toString()); - - _accountInfo = DataServerAccountInfo(rootObject); - + + _accountInfo = DataServerAccountInfo(); + _accountInfo.setAccessTokenFromJSON(rootObject); + emit loginComplete(rootURL); - // the username has changed to whatever came back - emit usernameChanged(_accountInfo.getUsername()); - - // we have found or requested an access token - emit accessTokenChanged(); - + // store this access token into the local settings QSettings localSettings; localSettings.beginGroup(ACCOUNTS_GROUP); localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE), QVariant::fromValue(_accountInfo)); + + requestProfile(); } } else { // TODO: error handling @@ -367,7 +370,53 @@ void AccountManager::requestFinished() { } } -void AccountManager::requestError(QNetworkReply::NetworkError error) { +void AccountManager::requestAccessTokenError(QNetworkReply::NetworkError error) { // TODO: error handling qDebug() << "AccountManager requestError - " << error; } + +void AccountManager::requestProfile() { + if (!_networkAccessManager) { + _networkAccessManager = new QNetworkAccessManager(this); + } + + QUrl profileURL = _authURL; + profileURL.setPath("/api/v1/users/profile"); + profileURL.setQuery("access_token=" + _accountInfo.getAccessToken().token); + + QNetworkReply* profileReply = _networkAccessManager->get(QNetworkRequest(profileURL)); + connect(profileReply, &QNetworkReply::finished, this, &AccountManager::requestProfileFinished); + connect(profileReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestProfileError(QNetworkReply::NetworkError))); +} + +void AccountManager::requestProfileFinished() { + QNetworkReply* profileReply = reinterpret_cast(sender()); + + QJsonDocument jsonResponse = QJsonDocument::fromJson(profileReply->readAll()); + const QJsonObject& rootObject = jsonResponse.object(); + + if (rootObject.contains("status") && rootObject["status"].toString() == "success") { + _accountInfo.setProfileInfoFromJSON(rootObject); + + emit profileChanged(); + + // the username has changed to whatever came back + emit usernameChanged(_accountInfo.getUsername()); + + // store the whole profile into the local settings + QUrl rootURL = profileReply->url(); + rootURL.setPath(""); + QSettings localSettings; + localSettings.beginGroup(ACCOUNTS_GROUP); + localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE), + QVariant::fromValue(_accountInfo)); + } else { + // TODO: error handling + qDebug() << "Error in response for profile"; + } +} + +void AccountManager::requestProfileError(QNetworkReply::NetworkError error) { + // TODO: error handling + qDebug() << "AccountManager requestProfileError - " << error; +} diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index 628b084ea8..c18836ca54 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -23,9 +23,9 @@ class JSONCallbackParameters { public: JSONCallbackParameters(); - + bool isEmpty() const { return !jsonCallbackReceiver && !errorCallbackReceiver; } - + QObject* jsonCallbackReceiver; QString jsonCallbackMethod; QObject* errorCallbackReceiver; @@ -38,30 +38,33 @@ class AccountManager : public QObject { Q_OBJECT public: static AccountManager& getInstance(); - + void authenticatedRequest(const QString& path, QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation, const JSONCallbackParameters& callbackParams = JSONCallbackParameters(), const QByteArray& dataByteArray = QByteArray(), QHttpMultiPart* dataMultiPart = NULL); - + const QUrl& getAuthURL() const { return _authURL; } void setAuthURL(const QUrl& authURL); bool hasAuthEndpoint() { return !_authURL.isEmpty(); } - + bool isLoggedIn() { return !_authURL.isEmpty() && hasValidAccessToken(); } bool hasValidAccessToken(); Q_INVOKABLE bool checkAndSignalForAccessToken(); - + void requestAccessToken(const QString& login, const QString& password); - + void requestProfile(); + const DataServerAccountInfo& getAccountInfo() const { return _accountInfo; } - + void destroy() { delete _networkAccessManager; } - + public slots: - void requestFinished(); - void requestError(QNetworkReply::NetworkError error); + void requestAccessTokenFinished(); + void requestProfileFinished(); + void requestAccessTokenError(QNetworkReply::NetworkError error); + void requestProfileError(QNetworkReply::NetworkError error); void logout(); void updateBalance(); void accountInfoBalanceChanged(qint64 newBalance); @@ -69,7 +72,7 @@ signals: void authRequired(); void authEndpointChanged(); void usernameChanged(const QString& username); - void accessTokenChanged(); + void profileChanged(); void loginComplete(const QUrl& authURL); void loginFailed(); void logoutComplete(); @@ -80,19 +83,19 @@ private: AccountManager(); AccountManager(AccountManager const& other); // not implemented void operator=(AccountManager const& other); // not implemented - + void passSuccessToCallback(QNetworkReply* reply); void passErrorToCallback(QNetworkReply* reply); - + Q_INVOKABLE void invokedRequest(const QString& path, QNetworkAccessManager::Operation operation, const JSONCallbackParameters& callbackParams, const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart); - + QUrl _authURL; QNetworkAccessManager* _networkAccessManager; QMap _pendingCallbackMap; - + DataServerAccountInfo _accountInfo; }; diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index 809f083e35..7f3b40ab82 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -19,11 +19,13 @@ DataServerAccountInfo::DataServerAccountInfo() : _xmppPassword(), _discourseApiKey(), _balance(0), - _hasBalance(false) + _hasBalance(false), + _hasProfile(false) { - + } +/* DataServerAccountInfo::DataServerAccountInfo(const QJsonObject& jsonObject) : _accessToken(jsonObject), _username(), @@ -36,6 +38,7 @@ DataServerAccountInfo::DataServerAccountInfo(const QJsonObject& jsonObject) : setXMPPPassword(userJSONObject["xmpp_password"].toString()); setDiscourseApiKey(userJSONObject["discourse_api_key"].toString()); } +*/ DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherInfo) { _accessToken = otherInfo._accessToken; @@ -44,6 +47,7 @@ DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherI _discourseApiKey = otherInfo._discourseApiKey; _balance = otherInfo._balance; _hasBalance = otherInfo._hasBalance; + _hasProfile = otherInfo._hasProfile; } DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountInfo& otherInfo) { @@ -54,19 +58,24 @@ DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountI void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) { using std::swap; - + swap(_accessToken, otherInfo._accessToken); swap(_username, otherInfo._username); swap(_xmppPassword, otherInfo._xmppPassword); swap(_discourseApiKey, otherInfo._discourseApiKey); swap(_balance, otherInfo._balance); swap(_hasBalance, otherInfo._hasBalance); + swap(_hasProfile, otherInfo._hasProfile); +} + +void DataServerAccountInfo::setAccessTokenFromJSON(const QJsonObject& jsonObject) { + _accessToken = OAuthAccessToken(jsonObject); } void DataServerAccountInfo::setUsername(const QString& username) { if (_username != username) { _username = username; - + qDebug() << "Username changed to" << username; } } @@ -87,7 +96,7 @@ void DataServerAccountInfo::setBalance(qint64 balance) { if (!_hasBalance || _balance != balance) { _balance = balance; _hasBalance = true; - + emit balanceChanged(_balance); } } @@ -99,12 +108,20 @@ void DataServerAccountInfo::setBalanceFromJSON(const QJsonObject& jsonObject) { } } +void DataServerAccountInfo::setProfileInfoFromJSON(const QJsonObject& jsonObject) { + QJsonObject user = jsonObject["data"].toObject()["user"].toObject(); + setUsername(user["username"].toString()); + setXMPPPassword(user["xmpp_password"].toString()); + setDiscourseApiKey(user["discourse_api_key"].toString()); + setHasProfile(true); +} + QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) { - out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey; + out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey << info._hasProfile; return out; } QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info) { - in >> info._accessToken >> info._username >> info._xmppPassword >> info._discourseApiKey; + in >> info._accessToken >> info._username >> info._xmppPassword >> info._discourseApiKey >> info._hasProfile; return in; } diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h index e0209326f9..27999aba2f 100644 --- a/libraries/networking/src/DataServerAccountInfo.h +++ b/libraries/networking/src/DataServerAccountInfo.h @@ -22,12 +22,13 @@ class DataServerAccountInfo : public QObject { Q_OBJECT public: DataServerAccountInfo(); - DataServerAccountInfo(const QJsonObject& jsonObject); + // DataServerAccountInfo(const QJsonObject& jsonObject); DataServerAccountInfo(const DataServerAccountInfo& otherInfo); DataServerAccountInfo& operator=(const DataServerAccountInfo& otherInfo); - + const OAuthAccessToken& getAccessToken() const { return _accessToken; } - + void setAccessTokenFromJSON(const QJsonObject& jsonObject); + const QString& getUsername() const { return _username; } void setUsername(const QString& username); @@ -36,26 +37,32 @@ public: const QString& getDiscourseApiKey() const { return _discourseApiKey; } void setDiscourseApiKey(const QString& discourseApiKey); - + qint64 getBalance() const { return _balance; } void setBalance(qint64 balance); bool hasBalance() const { return _hasBalance; } void setHasBalance(bool hasBalance) { _hasBalance = hasBalance; } Q_INVOKABLE void setBalanceFromJSON(const QJsonObject& jsonObject); + bool hasProfile() const { return _hasProfile; } + void setHasProfile(bool hasProfile) { _hasProfile = hasProfile; } + + void setProfileInfoFromJSON(const QJsonObject& jsonObject); + friend QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info); friend QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info); signals: qint64 balanceChanged(qint64 newBalance); private: void swap(DataServerAccountInfo& otherInfo); - + OAuthAccessToken _accessToken; QString _username; QString _xmppPassword; QString _discourseApiKey; qint64 _balance; bool _hasBalance; + bool _hasProfile; }; #endif // hifi_DataServerAccountInfo_h From e4a0275f57fdc2455bdc4880e37a5e828dcd21ce Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 10:53:11 -0700 Subject: [PATCH 048/102] Exposed sixense and mouse options and oculus UI angle to preferences --- interface/src/Menu.cpp | 6 +- interface/src/Menu.h | 10 +- interface/src/devices/OculusManager.h | 2 + interface/src/devices/SixenseManager.cpp | 4 +- interface/src/devices/SixenseManager.h | 3 + interface/src/ui/ApplicationOverlay.cpp | 8 +- interface/src/ui/ApplicationOverlay.h | 5 +- interface/src/ui/PreferencesDialog.cpp | 13 + interface/ui/preferencesDialog.ui | 331 ++++++++++++++++++++++- 9 files changed, 367 insertions(+), 15 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 3015a638f6..644b08f03d 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -45,6 +45,7 @@ #include "ui/ModelsBrowser.h" #include "ui/LoginDialog.h" #include "ui/NodeBounds.h" +#include "devices/OculusManager.h" Menu* Menu::_instance = NULL; @@ -83,6 +84,7 @@ Menu::Menu() : _audioJitterBufferSamples(0), _bandwidthDialog(NULL), _fieldOfView(DEFAULT_FIELD_OF_VIEW_DEGREES), + _realWorldFieldOfView(DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES), _faceshiftEyeDeflection(DEFAULT_FACESHIFT_EYE_DEFLECTION), _frustumDrawMode(FRUSTUM_DRAW_MODE_ALL), _viewFrustumOffset(DEFAULT_FRUSTUM_OFFSET), @@ -91,6 +93,9 @@ Menu::Menu() : _lodToolsDialog(NULL), _maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM), _voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE), + _oculusUIAngularSize(DEFAULT_OCULUS_UI_ANGULAR_SIZE), + _sixenseReticleMoveSpeed(DEFAULT_SIXENSE_RETICLE_MOVE_SPEED), + _invertSixenseButtons(DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS), _automaticAvatarLOD(true), _avatarLODDecreaseFPS(DEFAULT_ADJUST_AVATAR_LOD_DOWN_FPS), _avatarLODIncreaseFPS(ADJUST_LOD_UP_FPS), @@ -387,7 +392,6 @@ Menu::Menu() : QMenu* sixenseOptionsMenu = developerMenu->addMenu("Sixense Options"); addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true); - addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseInvertInputButtons, 0, false); QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 41f0b41498..4d2174a448 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -90,6 +90,12 @@ public: void setFieldOfView(float fieldOfView) { _fieldOfView = fieldOfView; } float getRealWorldFieldOfView() const { return _realWorldFieldOfView; } void setRealWorldFieldOfView(float realWorldFieldOfView) { _realWorldFieldOfView = realWorldFieldOfView; } + float getOculusUIAngularSize() const { return _oculusUIAngularSize; } + void setOculusUIAngularSize(float oculusUIAngularSize) { _oculusUIAngularSize = oculusUIAngularSize; } + float getSixenseReticleMoveSpeed() const { return _sixenseReticleMoveSpeed; } + void setSixenseReticleMoveSpeed(float sixenseReticleMoveSpeed) { _sixenseReticleMoveSpeed = sixenseReticleMoveSpeed; } + bool getInvertSixenseButtons() const { return _invertSixenseButtons; } + void setInvertSixenseButtons(bool invertSixenseButtons) { _invertSixenseButtons = invertSixenseButtons; } float getFaceshiftEyeDeflection() const { return _faceshiftEyeDeflection; } void setFaceshiftEyeDeflection(float faceshiftEyeDeflection) { _faceshiftEyeDeflection = faceshiftEyeDeflection; } @@ -255,6 +261,9 @@ private: LodToolsDialog* _lodToolsDialog; int _maxVoxels; float _voxelSizeScale; + float _oculusUIAngularSize; + float _sixenseReticleMoveSpeed; + bool _invertSixenseButtons; bool _automaticAvatarLOD; float _avatarLODDecreaseFPS; float _avatarLODIncreaseFPS; @@ -400,7 +409,6 @@ namespace MenuOption { const QString SettingsExport = "Export Settings"; const QString SettingsImport = "Import Settings"; const QString SimpleShadows = "Simple"; - const QString SixenseInvertInputButtons = "Invert Sixense Mouse Input Buttons"; const QString SixenseMouseInput = "Enable Sixense Mouse Input"; const QString ShowBordersVoxelNodes = "Show Voxel Nodes"; const QString ShowBordersModelNodes = "Show Model Nodes"; diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index caff38a6b0..21b9d67f4d 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -20,6 +20,8 @@ #include "renderer/ProgramObject.h" +const float DEFAULT_OCULUS_UI_ANGULAR_SIZE = 72.0f; + class Camera; /// Handles interaction with the Oculus Rift. diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index fa902be46f..5257a777e0 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -339,7 +339,7 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { Qt::MouseButton bumperButton; Qt::MouseButton triggerButton; - if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseInvertInputButtons)) { + if (Menu::getInstance()->getInvertSixenseButtons()) { bumperButton = Qt::LeftButton; triggerButton = Qt::RightButton; } else { @@ -351,7 +351,7 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); - float cursorRange = widget->width(); + float cursorRange = widget->width() * (1.0f - Menu::getInstance()->getSixenseReticleMoveSpeed() * 0.01f) * 2.0f; pos.setX(cursorRange * xAngle); pos.setY(cursorRange * yAngle); diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index f3c5633f90..71511cd06b 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -30,6 +30,9 @@ const unsigned int BUTTON_FWD = 1U << 7; // Event type that represents moving the controller const unsigned int CONTROLLER_MOVE_EVENT = 1500U; +const float DEFAULT_SIXENSE_RETICLE_MOVE_SPEED = 1.0f; +const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = true; + /// Handles interaction with the Sixense SDK (e.g., Razer Hydra). class SixenseManager : public QObject { Q_OBJECT diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 95ac803e37..a45e42dc0b 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -33,7 +33,7 @@ ApplicationOverlay::ApplicationOverlay() : _framebufferObject(NULL), _oculusAngle(65.0f * RADIANS_PER_DEGREE), _distance(0.5f), - _textureFov(PI / 2.5f), + _textureFov(DEFAULT_OCULUS_UI_ANGULAR_SIZE * RADIANS_PER_DEGREE), _uiType(HEMISPHERE) { } @@ -50,6 +50,8 @@ const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; void ApplicationOverlay::renderOverlay(bool renderToTexture) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); + _textureFov = Menu::getInstance()->getOculusUIAngularSize() * RADIANS_PER_DEGREE; + Application* application = Application::getInstance(); Overlays& overlays = application->getOverlays(); @@ -154,8 +156,6 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { MyAvatar* myAvatar = application->getAvatar(); const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); - - // Get vertical FoV of the displayed overlay texture const float halfVerticalAngle = _oculusAngle / 2.0f; const float overlayAspectRatio = glWidget->width() / (float)glWidget->height(); @@ -338,7 +338,7 @@ void ApplicationOverlay::renderControllerPointer() { float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); - float cursorRange = glWidget->width(); + float cursorRange = glWidget->width() * (1.0f - Menu::getInstance()->getSixenseReticleMoveSpeed() * 0.01f) * 2.0f; int mouseX = cursorRange * xAngle; int mouseY = cursorRange * yAngle; diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 03a323cd5d..b6066099fa 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -31,10 +31,7 @@ public: // Getters QOpenGLFramebufferObject* getFramebufferObject(); - float getOculusAngle() const { return _oculusAngle; } - - // Setters - void setOculusAngle(float oculusAngle) { _oculusAngle = oculusAngle; } + void setUIType(UIType uiType) { _uiType = uiType; } private: diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index d5c6079d4b..5e6c6984eb 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -137,6 +137,13 @@ void PreferencesDialog::loadPreferences() { ui.maxVoxelsSpin->setValue(menuInstance->getMaxVoxels()); ui.maxVoxelsPPSSpin->setValue(menuInstance->getMaxVoxelPacketsPerSecond()); + + ui.oculusUIAngularSizeSpin->setValue(menuInstance->getOculusUIAngularSize()); + + ui.sixenseReticleMoveSpeedSpin->setValue(menuInstance->getSixenseReticleMoveSpeed()); + + ui.invertSixenseButtonsCheckBox->setChecked(menuInstance->getInvertSixenseButtons()); + } void PreferencesDialog::savePreferences() { @@ -189,6 +196,12 @@ void PreferencesDialog::savePreferences() { (float)ui.faceshiftEyeDeflectionSider->maximum()); Menu::getInstance()->setMaxVoxelPacketsPerSecond(ui.maxVoxelsPPSSpin->value()); + Menu::getInstance()->setOculusUIAngularSize(ui.oculusUIAngularSizeSpin->value()); + + Menu::getInstance()->setSixenseReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value()); + + Menu::getInstance()->setInvertSixenseButtons(ui.invertSixenseButtonsCheckBox->isChecked()); + Menu::getInstance()->setAudioJitterBufferSamples(ui.audioJitterSpin->value()); Application::getInstance()->getAudio()->setJitterBufferSamples(ui.audioJitterSpin->value()); diff --git a/interface/ui/preferencesDialog.ui b/interface/ui/preferencesDialog.ui index a1c2073ab6..f00d7c4788 100644 --- a/interface/ui/preferencesDialog.ui +++ b/interface/ui/preferencesDialog.ui @@ -154,9 +154,9 @@ color: #0e7077 0 - -204 - 494 - 1091 + -1002 + 477 + 1386 @@ -1605,6 +1605,331 @@ padding: 10px;margin-top:10px + + + + + 0 + 0 + + + + + 0 + 40 + + + + + Arial + 20 + 50 + false + + + + color: #0e7077 + + + Oculus Rift + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + 0 + + + 10 + + + 0 + + + 10 + + + + + + Arial + + + + color: rgb(51, 51, 51) + + + User Interface Angular Size + + + 15 + + + maxVoxelsSpin + + + + + + + + Arial + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 125 + 36 + + + + + Arial + + + + 30 + + + 160 + + + 1 + + + 72 + + + + + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + Arial + 20 + 50 + false + + + + color: #0e7077 + + + Sixense Controllers + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + 0 + + + 10 + + + 0 + + + 10 + + + + + + Arial + + + + color: rgb(51, 51, 51) + + + Invert Mouse Buttons + + + 15 + + + maxVoxelsSpin + + + + + + + + Arial + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 32 + 0 + + + + + 0 + 0 + + + + + + + + 32 + 32 + + + + + + + + + + 0 + + + 10 + + + 0 + + + 10 + + + + + + Arial + + + + color: rgb(51, 51, 51) + + + Reticle Movement Speed + + + 15 + + + maxVoxelsSpin + + + + + + + + Arial + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 125 + 36 + + + + + Arial + + + + 100 + + + 1 + + + 50 + + + + + From 4a1307fa382ff92a25877f21474c57c42a55825d Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 10:54:50 -0700 Subject: [PATCH 049/102] Killed two warnings --- interface/src/ui/ApplicationOverlay.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index a45e42dc0b..f6afc15223 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -316,7 +316,6 @@ void ApplicationOverlay::renderControllerPointer() { MyAvatar* myAvatar = application->getAvatar(); const HandData* handData = Application::getInstance()->getAvatar()->getHandData(); - int numberOfPalms = handData->getNumPalms(); for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) { const PalmData* palmData = NULL; @@ -431,8 +430,6 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) magnifyHeight = widgetHeight - mouseY; } - const float halfMagnifyHeight = magnifyHeight / 2.0f; - float newWidth = magnifyWidth * magnification; float newHeight = magnifyHeight * magnification; From 3af4e32c813260b4edf55c60d3294cc032f9d58d Mon Sep 17 00:00:00 2001 From: John Grosen Date: Fri, 13 Jun 2014 11:08:38 -0700 Subject: [PATCH 050/102] Now determines hasProfile from presence of username --- libraries/networking/src/DataServerAccountInfo.cpp | 14 +++++++------- libraries/networking/src/DataServerAccountInfo.h | 4 +--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index 7f3b40ab82..99d92d8738 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -19,8 +19,7 @@ DataServerAccountInfo::DataServerAccountInfo() : _xmppPassword(), _discourseApiKey(), _balance(0), - _hasBalance(false), - _hasProfile(false) + _hasBalance(false) { } @@ -47,7 +46,6 @@ DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherI _discourseApiKey = otherInfo._discourseApiKey; _balance = otherInfo._balance; _hasBalance = otherInfo._hasBalance; - _hasProfile = otherInfo._hasProfile; } DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountInfo& otherInfo) { @@ -65,7 +63,6 @@ void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) { swap(_discourseApiKey, otherInfo._discourseApiKey); swap(_balance, otherInfo._balance); swap(_hasBalance, otherInfo._hasBalance); - swap(_hasProfile, otherInfo._hasProfile); } void DataServerAccountInfo::setAccessTokenFromJSON(const QJsonObject& jsonObject) { @@ -108,20 +105,23 @@ void DataServerAccountInfo::setBalanceFromJSON(const QJsonObject& jsonObject) { } } +bool DataServerAccountInfo::hasProfile() const { + return _username.length() > 0; +} + void DataServerAccountInfo::setProfileInfoFromJSON(const QJsonObject& jsonObject) { QJsonObject user = jsonObject["data"].toObject()["user"].toObject(); setUsername(user["username"].toString()); setXMPPPassword(user["xmpp_password"].toString()); setDiscourseApiKey(user["discourse_api_key"].toString()); - setHasProfile(true); } QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) { - out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey << info._hasProfile; + out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey; return out; } QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info) { - in >> info._accessToken >> info._username >> info._xmppPassword >> info._discourseApiKey >> info._hasProfile; + in >> info._accessToken >> info._username >> info._xmppPassword >> info._discourseApiKey; return in; } diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h index 27999aba2f..8ea7972392 100644 --- a/libraries/networking/src/DataServerAccountInfo.h +++ b/libraries/networking/src/DataServerAccountInfo.h @@ -44,8 +44,7 @@ public: void setHasBalance(bool hasBalance) { _hasBalance = hasBalance; } Q_INVOKABLE void setBalanceFromJSON(const QJsonObject& jsonObject); - bool hasProfile() const { return _hasProfile; } - void setHasProfile(bool hasProfile) { _hasProfile = hasProfile; } + bool hasProfile() const; void setProfileInfoFromJSON(const QJsonObject& jsonObject); @@ -62,7 +61,6 @@ private: QString _discourseApiKey; qint64 _balance; bool _hasBalance; - bool _hasProfile; }; #endif // hifi_DataServerAccountInfo_h From 422db14812a20a63cb7c3a987733832253c7e883 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 11:34:57 -0700 Subject: [PATCH 051/102] Fixed some improper logic and used better defaults --- interface/src/devices/SixenseManager.cpp | 17 +++++++++++------ interface/src/devices/SixenseManager.h | 5 +++-- interface/src/ui/ApplicationOverlay.cpp | 13 ++++++------- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 5257a777e0..ed55dc4c66 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -185,6 +185,11 @@ void SixenseManager::update(float deltaTime) { #endif // HAVE_SIXENSE } +float SixenseManager::getCursorPixelRangeMultiplier() const { + //scales (0,100) to (0.4,2.0) + return ((Menu::getInstance()->getSixenseReticleMoveSpeed()) * 0.008f + 0.2f) * 2.0f; +} + #ifdef HAVE_SIXENSE // the calibration sequence is: @@ -347,14 +352,14 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { triggerButton = Qt::LeftButton; } - // Get the angles, scaled between 0-1 - float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; - float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); + // Get the angles, scaled between (-0.5,0.5) + float xAngle = (atan2(direction.z, direction.x) + M_PI_2); + float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); - float cursorRange = widget->width() * (1.0f - Menu::getInstance()->getSixenseReticleMoveSpeed() * 0.01f) * 2.0f; + float cursorRange = widget->width() * getCursorPixelRangeMultiplier(); - pos.setX(cursorRange * xAngle); - pos.setY(cursorRange * yAngle); + pos.setX(widget->width() / 2.0f + cursorRange * xAngle); + pos.setY(widget->height() / 2.0f + cursorRange * yAngle); //If we are off screen then we should stop processing, and if a trigger or bumper is pressed, //we should unpress them. diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index 71511cd06b..41e061a25b 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -30,8 +30,8 @@ const unsigned int BUTTON_FWD = 1U << 7; // Event type that represents moving the controller const unsigned int CONTROLLER_MOVE_EVENT = 1500U; -const float DEFAULT_SIXENSE_RETICLE_MOVE_SPEED = 1.0f; -const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = true; +const float DEFAULT_SIXENSE_RETICLE_MOVE_SPEED = 37.5f; +const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false; /// Handles interaction with the Sixense SDK (e.g., Razer Hydra). class SixenseManager : public QObject { @@ -42,6 +42,7 @@ public: ~SixenseManager(); void update(float deltaTime); + float getCursorPixelRangeMultiplier() const; public slots: diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index f6afc15223..8ba0b42910 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -333,14 +333,14 @@ void ApplicationOverlay::renderControllerPointer() { // Get directon relative to avatar orientation glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); - // Get the angles, scaled between 0-1 - float xAngle = (atan2(direction.z, direction.x) + M_PI_2) + 0.5f; - float yAngle = 1.0f - ((atan2(direction.z, direction.y) + M_PI_2) + 0.5f); + // Get the angles, scaled between (-0.5,0.5) + float xAngle = (atan2(direction.z, direction.x) + M_PI_2) ; + float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); - float cursorRange = glWidget->width() * (1.0f - Menu::getInstance()->getSixenseReticleMoveSpeed() * 0.01f) * 2.0f; + float cursorRange = glWidget->width() * application->getSixenseManager()->getCursorPixelRangeMultiplier(); - int mouseX = cursorRange * xAngle; - int mouseY = cursorRange * yAngle; + int mouseX = glWidget->width() / 2.0f + cursorRange * xAngle; + int mouseY = glWidget->height() / 2.0f + cursorRange * yAngle; //If the cursor is out of the screen then don't render it if (mouseX < 0 || mouseX >= glWidget->width() || mouseY < 0 || mouseY >= glWidget->height()) { @@ -409,7 +409,6 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) const float horizontalAngle = halfVerticalAngle * 2.0f * overlayAspectRatio; const float halfHorizontalAngle = horizontalAngle / 2; - float magnifyWidth = 80.0f; float magnifyHeight = 60.0f; From 9fc84d6358abe2942cf8808faba84a56feda0588 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 13 Jun 2014 12:00:42 -0700 Subject: [PATCH 052/102] Generics up and running. --- libraries/metavoxels/src/Bitstream.cpp | 33 ++++++++++++++++++------- libraries/metavoxels/src/Bitstream.h | 6 ++++- libraries/metavoxels/src/SharedObject.h | 2 ++ tests/metavoxels/src/MetavoxelTests.cpp | 29 ++++++++++++++++++++++ 4 files changed, 60 insertions(+), 10 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 760b1fc646..80aa07b026 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -760,8 +760,7 @@ Bitstream& Bitstream::operator<<(const QVariant& value) { } const TypeStreamer* streamer = getTypeStreamers().value(value.userType()); if (streamer) { - _typeStreamerStreamer << streamer->getStreamerToWrite(value); - streamer->write(*this, value); + streamer->writeVariant(*this, value); } else { qWarning() << "Non-streamable type: " << value.typeName() << "\n"; } @@ -774,7 +773,7 @@ Bitstream& Bitstream::operator>>(QVariant& value) { if (!streamer) { value = QVariant(); } else { - value = streamer->read(*this); + value = streamer->readVariant(*this); } return *this; } @@ -1889,6 +1888,15 @@ QVariant TypeStreamer::read(Bitstream& in) const { return QVariant(); } +void TypeStreamer::writeVariant(Bitstream& out, const QVariant& value) const { + out << this; + write(out, value); +} + +QVariant TypeStreamer::readVariant(Bitstream& in) const { + return read(in); +} + void TypeStreamer::writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const { if (value == reference) { out << false; @@ -2151,6 +2159,10 @@ const char* GenericTypeStreamer::getName() const { return _name.constData(); } +QVariant GenericTypeStreamer::readVariant(Bitstream& in) const { + return QVariant::fromValue(GenericValue(_weakSelf, read(in))); +} + GenericEnumTypeStreamer::GenericEnumTypeStreamer(const QByteArray& name, const QVector& values, int bits, const QByteArray& hash) : GenericTypeStreamer(name), @@ -2190,7 +2202,7 @@ void GenericEnumTypeStreamer::write(Bitstream& out, const QVariant& value) const QVariant GenericEnumTypeStreamer::read(Bitstream& in) const { int intValue = 0; in.read(&intValue, _bits); - return QVariant::fromValue(GenericValue(_weakSelf, intValue)); + return intValue; } TypeStreamer::Category GenericEnumTypeStreamer::getCategory() const { @@ -2270,7 +2282,7 @@ QVariant GenericStreamableTypeStreamer::read(Bitstream& in) const { foreach (const StreamerNamePair& field, _fields) { values.append(field.first->read(in)); } - return QVariant::fromValue(GenericValue(_weakSelf, values)); + return values; } TypeStreamer::Category GenericStreamableTypeStreamer::getCategory() const { @@ -2337,7 +2349,7 @@ QVariant GenericListTypeStreamer::read(Bitstream& in) const { for (int i = 0; i < size; i++) { values.append(_valueStreamer->read(in)); } - return QVariant::fromValue(GenericValue(_weakSelf, values)); + return values; } TypeStreamer::Category GenericListTypeStreamer::getCategory() const { @@ -2443,13 +2455,16 @@ QVariant GenericMapTypeStreamer::read(Bitstream& in) const { QVariant value = _valueStreamer->read(in); values.append(QVariantPair(key, value)); } - return QVariant::fromValue(GenericValue(_weakSelf, QVariant::fromValue(values))); + return QVariant::fromValue(values); } TypeStreamer::Category GenericMapTypeStreamer::getCategory() const { return MAP_CATEGORY; } -const TypeStreamer* GenericValueStreamer::getStreamerToWrite(const QVariant& value) const { - return value.value().getStreamer().data(); +void GenericValueStreamer::writeVariant(Bitstream& out, const QVariant& value) const { + GenericValue genericValue = value.value(); + out << genericValue.getStreamer().data(); + genericValue.getStreamer()->write(out, genericValue.getValue()); } + diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 10c28b9821..1589473b0e 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -918,6 +918,9 @@ public: virtual void write(Bitstream& out, const QVariant& value) const; virtual QVariant read(Bitstream& in) const; + virtual void writeVariant(Bitstream& out, const QVariant& value) const; + virtual QVariant readVariant(Bitstream& in) const; + virtual void writeDelta(Bitstream& out, const QVariant& value, const QVariant& reference) const; virtual void readDelta(Bitstream& in, QVariant& value, const QVariant& reference) const; @@ -1031,6 +1034,7 @@ public: GenericTypeStreamer(const QByteArray& name); virtual const char* getName() const; + virtual QVariant readVariant(Bitstream& in) const; protected: @@ -1260,7 +1264,7 @@ private: class GenericValueStreamer : public SimpleTypeStreamer { public: - virtual const TypeStreamer* getStreamerToWrite(const QVariant& value) const; + virtual void writeVariant(Bitstream& out, const QVariant& value) const; }; /// Macro for registering simple type streamers. diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index 41c3c01ffe..15cd5eb0a1 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -41,6 +41,8 @@ public: /// Returns the unique local ID for this object. int getID() const { return _id; } + void setID(int id) { _weakHash.insert(_id = id, this); } + /// Returns the local origin ID for this object. int getOriginID() const { return _originID; } diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 6ec2331b14..6bd99a6c82 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -214,6 +214,35 @@ static bool testSerialization(Bitstream::MetadataType metadataType) { return true; } + // go back to the beginning and read everything as generics + inStream.device()->seek(0); + Bitstream genericIn(inStream, metadataType, Bitstream::ALL_GENERICS); + genericIn >> testObjectReadA; + genericIn >> testObjectReadB; + genericIn >> messageRead; + genericIn >> endRead; + + // reassign the ids + testObjectReadA->setID(testObjectWrittenA->getID()); + testObjectReadA->setOriginID(testObjectWrittenA->getOriginID()); + testObjectReadB->setID(testObjectWrittenB->getID()); + testObjectReadB->setOriginID(testObjectWrittenB->getOriginID()); + + // write it back out and compare + QByteArray compareArray; + QDataStream compareOutStream(&compareArray, QIODevice::WriteOnly); + Bitstream compareOut(compareOutStream, metadataType); + compareOut << testObjectReadA; + compareOut << testObjectReadB; + compareOut << messageRead; + compareOut << endRead; + compareOut.flush(); + + if (array != compareArray) { + qDebug() << "Mismatch between written/generic written streams."; + return true; + } + return false; } From 10875c5e61cfb056e0bbe1f260d8cd154e61b6fe Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 13 Jun 2014 12:12:17 -0700 Subject: [PATCH 053/102] Fix for setID. --- libraries/metavoxels/src/SharedObject.cpp | 5 +++++ libraries/metavoxels/src/SharedObject.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index 47d69f4abe..05af5f1bf8 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -30,6 +30,11 @@ SharedObject::SharedObject() : _weakHash.insert(_id, this); } +void SharedObject::setID(int id) { + _weakHash.remove(_id); + _weakHash.insert(_id = id, this); +} + void SharedObject::incrementReferenceCount() { _referenceCount.ref(); } diff --git a/libraries/metavoxels/src/SharedObject.h b/libraries/metavoxels/src/SharedObject.h index 15cd5eb0a1..ba643b449c 100644 --- a/libraries/metavoxels/src/SharedObject.h +++ b/libraries/metavoxels/src/SharedObject.h @@ -41,7 +41,7 @@ public: /// Returns the unique local ID for this object. int getID() const { return _id; } - void setID(int id) { _weakHash.insert(_id = id, this); } + void setID(int id); /// Returns the local origin ID for this object. int getOriginID() const { return _originID; } From 3747a5eeae79bee2b14b6bd43db226ac37316808 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 13:38:28 -0700 Subject: [PATCH 054/102] update hemisphere VBO on ui size change --- interface/src/ui/ApplicationOverlay.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 8ba0b42910..e228e39976 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -692,8 +692,11 @@ void ApplicationOverlay::renderTexturedHemisphere() { static VerticesIndices vbo(0, 0); int vertices = slices * (stacks - 1) + 1; int indices = slices * 2 * 3 * (stacks - 2) + slices * 3; - //We only generate the VBO once - if (vbo.first == 0) { + + static float oldTextureFOV = _textureFov; + //We only generate the VBO when the _textureFov changes + if (vbo.first == 0 || oldTextureFOV != _textureFov) { + oldTextureFOV = _textureFov; TextureVertex* vertexData = new TextureVertex[vertices]; TextureVertex* vertex = vertexData; for (int i = 0; i < stacks - 1; i++) { @@ -718,7 +721,9 @@ void ApplicationOverlay::renderTexturedHemisphere() { vertex->uv.y = 0.5f; vertex++; - glGenBuffers(1, &vbo.first); + if (vbo.first == 0){ + glGenBuffers(1, &vbo.first); + } glBindBuffer(GL_ARRAY_BUFFER, vbo.first); const int BYTES_PER_VERTEX = sizeof(TextureVertex); glBufferData(GL_ARRAY_BUFFER, vertices * BYTES_PER_VERTEX, vertexData, GL_STATIC_DRAW); From 230af7f5a7820ddf632c28bf2258f279440fd537 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 13:49:35 -0700 Subject: [PATCH 055/102] Stopped clamping the magnification --- interface/src/ui/ApplicationOverlay.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index e228e39976..830a3f843b 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -415,20 +415,6 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) mouseX -= magnifyWidth / 2; mouseY -= magnifyHeight / 2; - //clamp the magnification - if (mouseX < 0) { - magnifyWidth += mouseX; - mouseX = 0; - } else if (mouseX + magnifyWidth > widgetWidth) { - magnifyWidth = widgetWidth - mouseX; - } - if (mouseY < 0) { - magnifyHeight += mouseY; - mouseY = 0; - } else if (mouseY + magnifyHeight > widgetHeight) { - magnifyHeight = widgetHeight - mouseY; - } - float newWidth = magnifyWidth * magnification; float newHeight = magnifyHeight * magnification; From 1a3de1ef28d343146b9c2312466d4ad7f5586e91 Mon Sep 17 00:00:00 2001 From: John Grosen Date: Fri, 13 Jun 2014 15:01:20 -0700 Subject: [PATCH 056/102] Totally removed DataServerAccountInfo's JSON constructor --- .../networking/src/DataServerAccountInfo.cpp | 15 --------------- libraries/networking/src/DataServerAccountInfo.h | 1 - 2 files changed, 16 deletions(-) diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index 99d92d8738..c60a17e0d8 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -24,21 +24,6 @@ DataServerAccountInfo::DataServerAccountInfo() : } -/* -DataServerAccountInfo::DataServerAccountInfo(const QJsonObject& jsonObject) : - _accessToken(jsonObject), - _username(), - _xmppPassword(), - _balance(0), - _hasBalance(false) -{ - QJsonObject userJSONObject = jsonObject["user"].toObject(); - setUsername(userJSONObject["username"].toString()); - setXMPPPassword(userJSONObject["xmpp_password"].toString()); - setDiscourseApiKey(userJSONObject["discourse_api_key"].toString()); -} -*/ - DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherInfo) { _accessToken = otherInfo._accessToken; _username = otherInfo._username; diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h index 8ea7972392..5800942376 100644 --- a/libraries/networking/src/DataServerAccountInfo.h +++ b/libraries/networking/src/DataServerAccountInfo.h @@ -22,7 +22,6 @@ class DataServerAccountInfo : public QObject { Q_OBJECT public: DataServerAccountInfo(); - // DataServerAccountInfo(const QJsonObject& jsonObject); DataServerAccountInfo(const DataServerAccountInfo& otherInfo); DataServerAccountInfo& operator=(const DataServerAccountInfo& otherInfo); From c593117ac5bccd39d09625298d54c596846bf411 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 15:03:09 -0700 Subject: [PATCH 057/102] New reticle for sixense and oculus mouse movement --- .../resources/images/sixense-reticle.png | Bin 0 -> 26350 bytes interface/src/ui/ApplicationOverlay.cpp | 62 ++++++++++-------- interface/src/ui/ApplicationOverlay.h | 2 + 3 files changed, 35 insertions(+), 29 deletions(-) create mode 100644 interface/resources/images/sixense-reticle.png diff --git a/interface/resources/images/sixense-reticle.png b/interface/resources/images/sixense-reticle.png new file mode 100644 index 0000000000000000000000000000000000000000..b168b25106b99693112c3b2f3a351b4a49733325 GIT binary patch literal 26350 zcmXtfc{G&o`~NdDma$JLBg7OkBFo#FeIy}@8rhd7B*u~?>x`vHLK0a*3N?1JWF3_) zYspO3nUImOkA0c>oj%|5d(OH4c%HxR`+BbPI`{Qjo?Nps<>Nld4FCY2+2xBi00091 zEhq;708aPbdjbH!am&K=BJjUw&$t-}0039OW&0oi;3NNAAe3CrLjV9c@0(ega87gZ z9amQ2YcC%L04c!iqS1|q<4dg*55!F$Xf&QtJ#!`nt(7Kq(d>rfN|yH(*WIqRKaX6a zk1p`F;v;`IN3qe5GkMygS?YS5ztS8FFLDEc1cGii;>>*EX}n>LAs}rSW7y?TO}HFm zD5Ki$k2`>cpD9=8^|AZXl+Pp39^=mEBZYV8^Ra_~{`c`}pJWvK&fPZ_QByGZNvNA$ zKH~lNa4?e)SHM{V|M$+Z5J^-T59m1P!#lgKwY7^JvE2FCl*1Gy5ako7(2A=-e5P&Qw0nI#1MBkm(sgFBeHF)01XG7MYiqX}u3HDY1Sn(wt z)Y`3iWlH~)x$JHS0Zn=j?{rv+R|#?cLI4+Av8?H-pNN@#05p zjvD8~I8@L7l4>O5=+(iABPG2{lGFit80Q4c|D2OE;EFvvApBZzj)s2LAE3A_gi z?z=dv-Wf!2WZoa-QRVlM!Vht5MxMJW?t?YT0}SVv-Zd!hVmLP=KV1#Ock5ZO-WU;0 zz7A_&lnOf^UB^7n)z}>H;7@8S`uJx=oC&DQosTWx+#kIa`!MpiX4|Qi=VKQ0>i^*+ zbzCL2ZS@z}_XZ({h3QhnQJ(1Om-}>~7%}Kv@`XF%c=BTyH_!ueyXz9f*zvfks9?jn z?Iqy%*$`+L9CzH4(6X6!*gXa$@%`%N-!!)_k-XZO` za>u|>O*@fR%)!uhIm`E9D<}icYueJ(l`O=gti4(^l;lipZR{xE*53r3N`~NIYf}k& zf1{__4{K^g?8ANjIupZJ*QUOocJnD8n!Fm9T8~?4Z-Z?5w>BAYA~sK`XyHh!2{QNf zP*sU;&*fifO~|li^0MU)DDYlXh7nEK+v>h;iVs%;L_(G2@^>)kl+iY8>GqCZW1(MytXDV1iuIQtU zIm9p?c7dxP3)kSli|R z&&u<);7$%3<`Irr{KVzgLhRx!JE;}0T`hE_EUY>v8p;7~)S#MnB|7@F&*PcVd$lk3 zNeu!A8*To74oFj=7WNQwu=Clth5qn2>4FYE#pyFP=DpfCbAvkJ*Ugd9u)DfLr4bZ= z-64zv-3t1qDBTu`w^{6G$0U{i;iT*wL$D#@S}N%8(^VIn&VP3>g=0|2P9qpq3R<;# zpx>os2(?a9S=UY3ZCgEt(j8*xQg-w#1S3%a$5B@$HmBl9PmbG>sW4z$;;ew%u$Q2z!NUuj46s32?rD{QA zhIfuqVS;d*HChDKvElbc1ORO4R<6q0p{2r%zGNX{AjUxWd@u#pu_p6(I7)o4aXApPC3G@pxao0|4`nEYh=FOqj zP{h5#P{9JRzxuSG+T8sJ$z=_u|6HHdg2zEX<_(@-1pM;&bJH%)j z^Cd+dEH^lhsBr_--Q8Uqfl&Jl0H~^|xo0!g*xXDvL$+4XTZ+9?X`$N=)i&QM3Gx6w z6(-1LWXRWu8DiL&d!J-8^6YV2f}t)!tJ$8ze?XB%Khr>nxr?H|%3?|5#?Y-=mFK-( zo1^0uV>*w8oybIz;3Rz)kGmSZ`YrOV^x?12ho?l>aPFtxsdsm$h*KR|r_ybpfN!d$ z8S*%-MF<5Q)b^RE_nxTtOnQl(m`|!kVtnO`axWv#<}z|sY@vw63i`JSy7^$}l$zyJ z*xlv|jE#8*oStr@npnsWTu7>($@Ky7M@Nx%E-s{;oSgm6ry~0uv79FX>JW~}2-lmW zMD9p{0WB>pLq|u)J6>L#%d4wzx3+?*xyISWmywV7R35@H`8JE9xcs~2U*r-$OF?f9 zF>HnyHmJ*&kOMJN@V&P7r30pF(fubjO~Q|VC)i{^c8ABi!`Y1tdvnn(p=HM~LZ!q@ z$KB#m$!EY8Gv|_H9RHK9qBnBm1%Pq>Cvs7Z@L1oj3URN@kzO|6gr20p z?y7Vx-@kA?-3A2Q)5r<7F|TWAC~2s#|M^rzX_G`EiQ;$6@mqX{D@1wmOevR^htc)* z^}|m^l-9bsI5r|BQ@Sw%KtvbZ-ofFt`G;#h5)L9AJ4Zivj#Lo0kR!%||sl8I7>_iT|lYCA;r8NejpZuv@dF&N@*imLJCzRFI$opyb z#QhyhzDW_V-R-UNe1An|Cb0GkPuXH%fC2}fnTwkT#G9yuWEEL08g`=IazqX7Og0%& zdr|D2oL#I9Ly4A z^r`JpJ7(D7O9&-0p1BrpP{7)1XNnZ3&*&qh%>?h18>22Sg}R(%>(GM6$e91VCu~w0 zm%1gFNO~*h@T~BTcJi!3V&T{KF~bkJ@>UNX9y|7`KRQP}U>2eStepg0v=fP4oGY(S zHVUu>|rqwJb z)GY1ETV)mWx5eK4(nf}1I0@M z#pB)KX;RP#R6;p2;HuL2E)Y9{I!zaB35!2T4T$GSSUC-9>088A6BjG9rShMxG~bgf z`$@|UNm6kk2RowC_QIWI3Vm-0gy|dTI!E;Ad5mwRcXnjguWFmSp5=O&r0~46wl{A! zhu`{Za9LDbLL$_ee9YV%W^#-#Mydmzf-E{`Ev#3N?k8=wA!n9m>Yc5BIqkM$Urk_| zw6}QonDIr9wXuB&s`qK z;@d*G>2!}zj>4UOBWlfP^m5Q($`$_hk0j<6oh$*|cXV_dw>toBicEGdFJm`nI(gWD z>Dlg-t2d%o50Hr4;%psv)1(Co<-*cE#@?z~N_yCDEBDoms##{vXj#o@C4W|yW=4#a zb$E;tl^(+$SoTZB*9JpE=JltKgYBgE%13d$fNRcCQT4vh)3Uag2gMIdFiB5}y~mp> z`Ur&L&^*bP9(%GwjHyDo2T6Y@m4wC1=GuyVHL68TGrcd)-mBUsmvFnZMf}AinWag; z3FOO2leV`^n=?w2PN`9t5PxQ(eqyi82w zD=)#t=6S(Mc5r}2DAqbSWzJTk6t(eaZ%IHJ(|B&i;2j~@L$}%LAZW@iG{AOa>)NBxZd0)Y0@#AhT3E3&uq9i%_lugPJfP{nPmlFI!*v?-Pdi0+yCHKY6Tt<|e1<#LIX4PD9y!rjUWy7A!N=Gs~S zz*LlqS$~+zVN@mDz3z}EE%unL?efU|k+0mO87-H@S*_$kIcN;=l;L72XoBF&&G=$c z$pwAF%jv~eWG^9}+jw&sw{(M#=;kTcU`cK%ZS&rDE(ZVKy zP8ok(>@zCqP0Tt1fowUlG#?su4L$l1J2A7n+h}e6BvZ-tTglaYej9%sb^v+ic9wQq*<%|%#&6@2U#xdCr08HdbO6#oGM zMTp}l4$QB3)^pGyCF|%Vh*k6Q(7}h|Qt7j0=}_6jIMfj#t6n{Ei7XMQwYu58E%BQmU!=P-BH`Wr*;yX zbm{F&X){$v1FG;bP=AU;h&2P_wTa6(-?D=+l$H&H=zv#UPL)i(NJcAY9$(Hp>vec$s@3I@zrp)CUz)%Xo z&LXDP!u?_*d-cdCUQU-ndYL@_nq+4Ty2r-vACb%#Q{YR5`-hj3W~p6zp?%pBMZA1&fDyi~c%S2e3=wVf5U zfZP#1)=}#<^s}q$Qa4;EK0bc=#}AtU?V*YK*L7GOAymH>tq3;r+~#f(z3I7`=ze|m z2kpBN#X71jdKptP;2gxR?`0w@iReEPA=>{myiGZ}t^GfJR;Dae>mD(sjwDsY_F{TA z*jZDygynhRH8X>@*Z24SexI+#!Io|*%b&IFA3MtGPoN&Gw%?qXn7HNTWeUOzqt@gS zPmXvYR-63f<6a?)5-aGZERd4s$jqjSr=OLj9ejp%B9#YBp)Gp!?-G<1D?fRvn&m^f z8IjdbG>8ekuv!f|qI7sC<0ODf4s;k|&bz$&*+$SHdPJ>7p^qS!Xl5;}V`6=%nlliU zZB?RFN(e&>ce-t@MxT67Z+dA%vN4~CC{m7u!)Z=XgzdX@Ghq`D@Jj*20o+O_h8nvn zf}~z~!Kgt}Al82n7G!po_4@hIRjS$m?b80$!^pu;W%O(j6YD|G+S$MN_Z{ss433+u z8+h0=oihH=-@bhtTPWul(!1yy63}4azZQgsB&nD|f7@r&Ig>|$Yut#%2W@P$7J5^~ zh(ihBmXj)#q@wDaXRPIjtC5p+Gb?CamZnnft%d8Y;jnntPS*y`{TAp0Df@FvBd0b} z-<}-oC~Ttt+DafLB?XsbRboI3Wn$211C=fp3#33DNv5muNW7fc{cWbjC-)caJ;z5< z4MF~~ieFB#d-29dy|b4CT^;C2w>ehyGs%ERlKK<~@(1lIQkUE6RH7%Y0C}^FO0hnc zB5*h@hqr_<*{seTTq6fd6@5guH7WG{_0rYY=f*TtkVxA4I)6{Z&q%fX_!@xb+kJ}rP!%N?%*g& zHo5Ijw>k1>utdNhdZNBXp-+B@VJ!5AaKYAgEbyi1}rvj zX;nZsc!+0hqL}T_kvl59&|k>vdgRr8YJ+wZ53|_&p|vnNj~4%;cW(6lg`<4kAy%_o zV*5*vJ%U`KahF|nZS9vIYOQD{GxE-07*DSuj&-!NI9BUbsL&^$jLD*DYX*sk(wuts zE@XY?1-!+(0U*!YdL_%Jnxij6kVi|x@W+-@j3Z-muK{+blAE!kA3bt|A|evquF!Yy z!s}C(B_po?8ZLLhlckq45T!;nSl?m&&1e3#P2ZTNeYnz)z&rSrUG~M3ncR@vpt1UL ziEm-5$y0~e!c;tR+c81b@Ryw9(2JGPDQGA{rbcv9U09{wU zJ!?zi3viBZ<8C}|`sB!wEo$O39g$mn;2XMnp}!VH+IVY1&A%eseo9i3^)Mph(6x4K zDPDKTb-=UMn6lF+8MSM4_(uu%mxC&BF~Hi1`gf${P?GsuGI7ke7z8MHHEDj@RTQjJ z0RaWjH~n0|H+Ja)x2VLm7n*Ucd~DR>gGrQRcvW}Cj!k(TC1txggQdI1>c$<$KV%;6 zorSP~!=XgzCWAKAytNg4w7WigPb0^9bE`rypceAh#qipuOZnF7$~jQu-?p!pBO1Q$ z@a}f>GL*pWFOM3y4k2p>j;E_SjH2uXFQ?7Lv6U5?K%a%Hfc)xp9?rce<~+K}8>Knr zb#?oQ-zsIZsbs&Ze699F+nDczbBZkBP{)W(Bn|H^b*q`L#99~;m+E*HmhJURL(;>u z;W5-nX3cB{#5@abr|+b=Pq%z|z^Hy5kueBre@`G}4|tmXYq$0F^%PT6(+2P%WNlUa zSgr-w9LWbp8wWu?tPf$g8gpI1kHH4Xwba*i61o1#QqN&@xz3!n=gn#Y+(L;M#4hmy3o68ef1D*y0p0$#` zd93TvIDpxD4dVYCC=yogN`TE|S)tl!p=aYC!%%I30{flVemo@&2)ayt+YLFq3NWME zSUy_eW#sZzPNa--SGx9(oFUIz&3_P6C>N!WIOCtoxanbkO0|p7&fKbm>~gBFRbK|Y z%LCz$U0`fqca8we6_u{9oB3aXVSo8s&+;t56*|L_OAm?NviP>mhU?+Jn)$p|2UV{x z9!}K%F|kgczr5-bUR9#dchl6=)HF0N)8KFo!m^`E;&J|?cqa2;b#$cUYUe7Bb)X6b zr_(;As1$d3Si(>URDxSlH7zt+TL}*kO`&66ltD>3_i3Z|aSe`16eKv{Ept1LBEr9m z6&mWPDUtX-T3;@gSeQ59*==fSs`H|E?jKWND8Vz-qk-+23Q( zrbbGx?lGBBk6;%rLamE#Oa?nA!V;Cce&!rNdYV9fVZ*v~-uY)2d1}vT>1iB(zf5E$ zP(zAXk3q~#zu_U+SF_&?9Y!wb(P>|egHH;5k_=rIa)>e2RB;wNp)r5CTF>I=&Cd^#rZC-n z#UwuFaN+&N5_O6mRgAecA}T zKUV9dYL=G$k5*bMC2$wY6`EMb8NsdziEkc5{px~*{rN0#g_lUcZ(y3hOjCdr`h)d# zm9ykm_x_QGJt;I#(*Cb&#-E|2YH}{)OqX47K!8-90j`2$O@J=c#CGc4GIIc`(EmQz)?%>9>P0c;#VDHYVmFx@G;*r07H43gG zCV^V7t^U@Rn) zu?z6?f2*MLgQV7V{VzgAIgL^|j9_V@ox>hZ(V_~cjG~qWWZR6uRY!;2msSiOkGyAn z`q54xjPouYDh@GRzttSp$t7wgRogq0mn}JuIMxKrsd}@ZyFo#V;jEI+;ie=%I{5Or-k=;A1`IrW8&&JF`pwzv8-C|*1HDh zjt2VMK4kqkD9!rO{6#xoTtY(qMep3D*eJ*@r_5TkbV0sexNK%-W{y_!*|ybHVG|YZ zF9BDO64vIJ>*{yHISX7~76h{wE9YO(eN?*zn!?@$j<#J$`?%k`N2^KuA|}=Tm+rUc z4tQ3UjQq8>7CsInq>>~6XZ!22y1B%Z|AE_eHvgR5qn72kdmslB$JlZ6eo&*+w=u`Kdvm2k{~<;WZy z<9V+lc1eVHslG3%#()+W zZwzQXZnz%XU;|PxKhRU7;^xe8Ubh=EY#vySR5ewUc-ZG%W)*us(4q+;F4i_Qob@L> zrvXeOuF1G_RSgYiAHgjDM=w5DY+@EE^ttxcOqSBa#_O@?;ug|v{A`I2x@#UYo<{XA zHKB>Y&A_fWz1?qaz|8PV?&b(~BC#IZM%MUfnPPmXW8Oad!9iPUABkfrig}iGX|j^9 z%8Olt7o^*QJdUr-y5dA{ci(o5HfU(Q=}ac=uiij!HF;<275$8}348kkXZNM)?;ksp zW7_}U3(y1xl(E^KLvqot2?YL90wK)1BP!EZ3^v(_JRTrHdGCMJeQCoVE;_3!vRHD| z@1Cz|Vtr^U-03$_-)%W^Lq|tvhQd0caQ-zY)A`Me><@5Ki~37S|<>`70M-V=u*lFcS|bhvBlo^2DS6A z$S%qjeuV7CZ&i5Ba^!syx>+EX2nP5!bSZDr^?APpO#Ot$#lZwHOWJjyNcC1u;6S-j z(3koVwe~_DAn(WP(cAf3>!**xaO8bjXj?ZtN7&^14Rr9CSRKwa0s3{_GhNSKnpmqP zRhyyF;h}k}=^lH<1cLX7nj+0kyTwn8bLw`i_Yd)}x6>MB;U<1&?>?P?L@Yyd?&<9_XC!fte)JrT3gg-_g zzIMaI+g5ilch6T$6I)Pg4mXJLf`$gc)t+!z)az({0S3tRB=yz6!tn zOP>Y9ICIfg{~Nk`?;hHS|WtA3tm1=Gw8P1Qpwr z)m2K*uepwW0s0Bu|Mvfml*pS24#Lk_8uU%oD1=yED)=ZGT|wVpyrBN7rfZag)EnQl zE@=+4!!i*nIOwqnG1(^Km?&H|hIk z7tiHBt)N>ifqIJ-Jr~Uiu8woFZM3B`KHMs#A!J) zm!u+Fv=kN$@(ZTQD2={Mr!H%d6e5{}!5eVC&m~QL@ zKM7~IW6LgA#0K2RWy}_PZ_}Hm{U;v$sK?ey!S5VzK!Zv-!o7rdEQRo5he3Z>iL(d! z?_{0!C~B6YY6F4JWKQhFDfuGLq-s*{fq;+QjyZcv#GZ|6*Lb1aMm<(wqJFB99(F5* zd%_sSZ(Gg{*J-r)4cX^tO99(So0b{en{R2iF%KHl>4N)Y%WCQmA~3!-XoQY#wNw#t(f*a^@1hz-AU>$Wzg>^Le{Tl)6A=XjycP4<=$OX7hX(6AZOTg}us z9eQt}P0hXfxGyJ3*qb0Uy0`cC4+n^S#vk_) zwUJ`)yDDDz7j6YXm*w<1!*339P|w9s6psdyDDA%RzvZ7LksjUu6t{WH5%D@~vnzVE z+J61rN?3N@c@&-sb>&lHZ>B-Gbt@+$la&Y8V__B|!969+qEg19jbG-#qBSi6vASOk#IDa-wp3SoPSms07I1-gDS#ucW%t z(o*sTDyE8g(xgIY04o#MtOwc2j4WW|JSACVM zD(K_8EYsqgfL{143R*-fUFEpV5o-~7JZkLVapkVmrwxoRd_j7b)2dGiS#&$8E~&Z@ zJAoP8Q=zp~b+prcchv1zkMKMFvh&Qi`8Q7n^!oab#Ikmt>^1a6VBMING}sqIsOCGc zOMBE57s6pM^t;+Xbw|XOp=Ba83_J`&~`ShkMp=8*(AoXf2-q zNXqV{>MR?=>9~a0SV-`Tpo!H_A4AGpWB^KwAZ=(Rw$S}l>PtW>S24WVpf<{F+Q)&D zlT*G=RpnI5ImFlWps30E<)k1N9>gW{55ybfabJ8Xxa%?CdtvPdEm6J(x9RCR+E+UKOY`!JH3RSv#w54uO ztv?rf8r013_UXzq+@tvZ6Ntu|+5T$RZq{lX>)s$dCxiBxJGY;HUMSik1e!{0n|LGJqd24H#k{urpLf*?~FbRn+w?9427=d9j#q zM|?AAC{K@m${?ygBQui+`R6Nq2xUI@z&Z03N{TGepbo;AvfJHjNL+Rh#!u{yaGbI# zWX<<)aDn8C-lf<4&J{8L@Fe}7Y}|W#(+NjM$JoPm{ij#?3b0qYPQ)vG@qXU(o%YM4>{FL`OMxD6d!{npLKuFu(ycnXW>=Epf&E3;+a&a*1 zYQVL2xt?H%6?lVu91`#c_pyi7u;GeizndJ@q3y3tW2fno9YmmSY@vaQg_4nv|P zUxm9nnvDH7Q}6LpKDRjD#&n;v6^^$6{CM#>Q}9xQ|ELCFJmghFi9SBmzv+WD^x0eJ zie)g(vJsS2ZR_I{vOkYM~(FL0MGGWDt6dC-nTt%L?6CwqbfDHpO8};-1I_nXv zP(xCmmPPF~GXTy9xBB^scGy|@mu;%wJt!`}qf#$hoMPS!ei-2%8O zsvbOeusJ_pJeRAPRQ&@!Y;NBBF8%cfPD40m*dZK>n1RoKrK&bsvE##PMz&;*qJV4W zV`fN4#6@IWPaTQ9tC1bh&bq>}$BLphe0R|a+&c&Cj<<~e?ekNC{spxQ>@w6!UDjN* zccwY;*s>0d82jjvvf<<>Lc#$TMwnKmZS>qT%2`*^5qMj3dnn=_;$pA1movGyg8p&! zK!cy^2~p56rZHW2-<&rFe(fQuLwjy5s3WSFIC3zFrBsyu307!hUOnbZ!eX(1|7i$S z95*5DddyG!fGe1&>50SkxRuim_LA>H8XbNQo=yvx5@>iY7Z`Dsjw>F7Bv=B^MKj$3 zj?Q<;I669pY2*aYuD?PS$q$B}kCS_s{tSl7u5r`krz#RRTZOH-TuKpg7oM7wF35Kr!UWY@uQzBv*`QO-U5EKVoH;?_S2D z1Uq+Yd)YEWoNu0vtC(W7dr@&FQmD}qgU(xbzBdSuRMHWS@?_?<5B z<5m2cE6!Oe6sWQro!tUtIfjRZ?IDpLw1Bob$X^bie01IN$UxyeP}X0SLZCMZv2cy+ znfuNY*4OA=Lk(1xME~2Pb3C_{7fwXXe}#?60*9~n`f>gh?o&=9FH=gVX<7iL-*bc; zaj{dI)~ext1b4ol_mqh^k8kbpv)s!P^B%{I2}}L=jCZjfo0Y!daKzG)XF1DWbvtFx z`T_KQV_J4^95g^LuXv?|XSCypJ&J-&L7gb_UyjSjtK5s@^;kIO87wB)*{eH2=^n%> z$>ke6F*7yP7WB(M%H~octOoznnf#%G9+#dLI4D*tiYc3zm>4l;vTQE;coFqf3s|J* zM-~mR!>5;t9On^v3hTvS)_(#FXrat5O19OIO` z{ggP}$G(uS!Smafz=en-ew6<)ZyX4`hoNMEpK-7gCf1twS)V>`H562v=vVwIK28E#s63fYI!1}v^; zqPm~Ov9nQ&Lx)eZ13(?AX(O_ITQ@}jM6>{P@!N+2jFu*LR^k(`2&<0TC`!}uGpu%1 z@-{G-Tz`rbQ&(44ge;OkC18@o%7fVf5hU>A44x+^ZpLD=$E1yY#RD*ezWEaoSahSb zZXS-=8_=Zif`}ok#9dbQotaO2VXOVbc3w|SDV(+Fxxy|+U>S+ok{taYUr{kCo^+5U6Iy#fo!seJ>~We6 zOIdjAM%A`J7G)RzeV5)szu>3*p!npl+q!N`TLX_9Yie18uDJX7B;qF`P7C$k2s)Za zQ@1hy9qQ8X>612$*!-s*OZU*(`x^3fU&#jaRMz}D{4V%J#Xl7!#JNPd3yk=AI__?H zFsAB3@u^*L@S46F2ek?0H5Ye=u8w;D1s;?XxO@;L%4Q_dt-^?8jo$@Uu5lDlJjRm4 z9>nWT*DW-Lx7x3Utxg91_6f7y+TU;5j%;V>raW@DJBH$>?D!;APhfkWffTY2$2{#XqM?$DK%jQa5!|uA5IN^M+=4 zkR2caoX&V=dePTvW{9Pr(n&=x9jJ-28n1RpBx6968>8?-+8vIW4?SfHH3P0e-@Ldb zMZ1D0Pe|r+d~Nmj3oZj=^w78<xb*OeGQ;h z{URsORVU|e_bEb85wvR^NCoY6RE`0EiPuD0(JrV zC!t%4T=$(hQP5PI#ioika*4nB^H|NnL;e%z`!erR1G;Y*<^zxG+>V>Li>=^cpRFhk zY)ppmK{yiMRz{@lvd*%(nHfS=ChPi;z(nUC^YYwPLvbS3#b~vo4!2g*Sff?TRwXry z|GoTMiAKQpH3AE{2Kn|G(8s|=aKrZJvQ4Jsl{{sM7<{Ik2!MMY)*kTNV)`Rb$Etj^ zyYu4CN`}2qBlE#@!xW#?+Jla7RVT`76s}G;|Dec#&dT8sE|uoMmnM5Dr*Ai#W90!Q zsa7LU1lU5Y?Ehjfk#j}A?{il!=ipo`CM0z4HbVrCzimhdHcd342sS8!2#=-laWmw;l)XAWQo@EQLO5uwfJlL@Xj_lXH{A?&2;dz{K zFsV4+5c(|`l(DAC0VN48>O@RcZ21q=)rJQwOSULfC=5;;u@M?5-i_-rb{DrIMjI{@ z1px$G1jRH^+&IJIi?a?Zq*NUClLLBq3QrUSG|5gnbDE6+txTa0z$7Vfxg|ZPbLR>< ze!AiNSM@x83*F3w-0et#q4H=5%8m+xElc3(KWip$$m$d~RTn(GlMZlb5|2ExbPX`P z*5=Pw4}MX3mAeA?xfDK^M!ABuQmj7;-8X#vTp8744Bc}k?>Uoyx3=nSPK8kTK20%> zs2qU&D}llH5`v9=RFU~6i=gEr1mcS-y?M3G_}sa3mywS^#OvmJaEv5kZs7Oh!632@ zErej8#xcBQ=V2`lc$Xhl(7|1Hzv{8qgdedOLk0rY9SmIM58Xp{V2*>gjCOa*gw?K2 z9U3Uba$m4xvD0b!xGr;KJ3*e1Ug%6_ckq$S{T>mt>%AaKUr`+rY@A;mJ}xL(L0}t zB&f_wNJ?^|65eAcF8;HaL;i!ZX(NrMu4itj`i-J*0bas}IQb_w^4H8eZN8-kogxQY z7Nt*WxRtRxX zwkYYPjaPEAPWj#qfy$%3r#J8Nte7X7Fs6oJc=w{qJ`y3$BFLi44va(|swf1Lo57^0A*l3dush%{K!Ra%S8f5_N6zqdNG6?+Tt`y?M<`#1Xq{C_%Z ze5}-3q1;BH+(wOpF`Z7o1vazSmZIF{Uq=i1+0wpZYwO#q&*hecfPxFsPn#o? z3!x3zr$y-rLHd)uPp9yc_*)|L4A9gE?d@HK{VSE~s zBYdIU=l|tA#2+J6>olnR3+nPj8N7fE5V>8?=4Nm5DV7T>0U*Bed>L-gNyfC))TsUh zw;b&Bx##ig#O<}TNEmbMd96t9aQRW3CMp6U=8eswDFQAN6B7#y3sCR!Kp2|~{IPz~ z&z(rhf9D&*y@ zu}2Sh=z;&i`Gm>a;orO5<22%94A?R$pT2d|BQ8Z!x*dEd4Bf@hQ!;UEaWKkGWYRo+ zZU@Wp2+CQyWQ+lTr&s-+utyu-CcFRx3CB=R(p4)6^6->yV@2Yh8;0%*EFWO^xK#;_ zl%JJ9nX)gL`~FQkYZXP;Sn*uPSq$*Ht0R z-wd)`+2S3=t-=Hy9UbNRREHS4gmiO2${5-SeIo=mgnm*7&RdDRR{V4iR;Ba@o~u_+ zNLr0{Vy7NN#i>~74Lw!q62>gGKU@b3_~N{eR|znp_8$1!;_Mc%5~m798r7PCH9;WC z!R;u%howUr9OO4o`}zfbeTZ>uh#?p^Y{HG-5hgx*W_#X|tMM{Ot`q=((7brPp)+l( zBT~@QsLSGPprZ6JF)^__;o;%eKq5j|9!45f57-q2n4Z8#FJP1?BM=&KdhKamS|}I5 z!c=&WoxZ^K+AjjBHi_M{)vE_lhR`R9c~^vC6E47@DBh&Hi6^Ju$rkiW%qPV^&pjYW z?bzPGG?ZMC8h4D;c4Yp3+tmn4JIRnLQFbn%D2Sbrgn!ML&d(|Ny(DgQ|EYfXvK=!Y zpmG`mm6lRfZAEMk&Oz{Pt1qOWpOtyfn0sG@zR9EQ%}cwvf*LJt_u-?G=D^AxHr&nR zbX+>pjBSVkLwr5+FZ_Z+TEioC#j&)vf{m9~!c@pJu>OhDwuyf>AwL^2EHYhVDM4DYyATloZ)O zZV#hh)-p3QRd}^2ec*5NgHus^E8mv}Qw@0;9jABClt#CO z+wI(D32+=LNvzcx?YsIZDk;tWy}xK`EgY7Yx!;yAdgW2^+_WIRv~5YnXTovD21`0W zB4DU}-f$EHe+2uXA>`%+^f{b)3qIWif0I5#PlQZ%gyBP;6UAnYSt7t8j>E~%iP$=( ziFG~zo%PwGz%5u1;IL$`%phk-i@tXhk_J>TvL+`dFIrewM8VjO6{*R@CGi%6DLbt} zb1YPZOFU1&sV}+D;l+w@S;lcrqtq-3B}K*O_1O4%sZ35$t#$?zAuw^N42-t~?AYT=*i_ha!iMVAurcTJ@PB8>uJJv6j(jEQetp_P}zwK%jACM~# zWF{%m3oSNp4>}2ierrb(;AqBk@ zm)gH`lmMHV-E(+%q8$AGx9@SO2sw}fH=D+afQlbblLb(Aj8T`(p`wU5)uI~@rvmXh z*?e|v7sF!@d<|kp^uam68ZaN9a7k9ft%QGKFZSutV6n*a4my;L#cNX;DsP5!hKXL9IZ12Vdg_u7#jms@J zl!ax4buM`biNHHr8Vr|U(K3)sKw&{~O>wte@DEtP{TUmK1zlwPe8htuoM>*sJZnt8&h%z8CN#J3))6M1GbQAyP zXj%SDaYL1|r{w+p3nx%8$_@tA)3bBr@wU*IvU7EVWwk%l6{`Sh6vbOi@vyiBUf(}W zJP*qK3Bh{{%->-1G=eDzp)z!bEVOBx>$BYhA(Y6o@$rAg6NuP}c|oIvrJhj#<9Ny$ zAjHm_zQi$@2MG)}<|nondyL?$g{^^}2#RZQ`gwMv)X+SUusoX1AKVc>U}lzV0pI17Lf(&{4Bf{%a8L1QkP+w0?)PEO?5prJ{;$qsw2*m?2OYvDNx z37}0$IZY0?=xKkIDbAzTqd%?OzN}l*`BWhwn^((EJ3gpG_cKg59EI}vq?h) zIPn$aX|>i}45T5A z?CV)!l}5N18~HXT-I9b*3F_IiQqWtd1V!MXu72|W_X6De)9wU&(R`1RP$~y#N}#p< z@Dkp~L3kQ3_ZG4UQKZK6C7|>Fm2@WFQ2*Z_e-DGnIwB^-h{{A6rI3AJB2o4*5|W+l z#yU~4ldFKtb#>nb}Ffm90C#(Pb zb(YAvd8g3uU~|{S@IYqFzS{)!xkemQ;yS`qf*2%hc%4(e-wjr#3kCZN=;Sg(s#(95 zcPlzu&x2Y!{`&gw$%>Okv4E62$A+p=~(CwuxM-Y_Tvn_%!^Lp>C3T z5RNeIu~Vm&v(2JPvF1U*XPB2Rw949y!UvMog=X)AbjGz0-Q9zPRA0$Q&Nc_xX}+fR zftCe|yGOq^|7qqHNFl_HMS`?R8%$`|?M?48hkEC~P?R z0G2F}BkRxwE5}-^2xz{GcbiaO5)3eTUwJ;B`L6u^KGc>Y5SXtLM>T@5LnC`EpCE}^BP-?9lE+%Hha=HD%cHbw z+n)A44Ag$H?32zokUak zUUZF`)>bcZMiixdzbzz2T$jtF{)R)y2wcA|R8~chSK>CIPD^Mh?)c#?voqMCSy@x^ zkg{_IHNpVFmjn+SiyO_2tER?EsVyFKc$(s~*aE#e#wr@A90`!Xqy#JBGeHuH#Gp@- z$~FA=AXffE&8QJ-sU;X|GxK%J$u)B3^s6mC zI`JmpZ}U2oo!?DY2x82!=LY*H6EWxzo?60%r`NLSBr2UH6>E3es-SLydKeN_+I=qo zo#WsxZrqle1W`Z^*Y5@^I>zi8L+rVy0Ly!=yNfM*(qMSBKjCR58&vNbA19|SRBSR% zlefqC>_|OU)^zW`uerJ$yh|<)EN$j`mP?MTTYec84w1yT?a8H1$a&N9CM8 zDL;Aeog}ZwNiFoz*%L&hc4pMLm+TTJSXZYTX+ERsVln(sCX>pSyJ^mK0$Fw7c~n`! z{ek?R7U|;#(@LdA^t6JWXdcl2V@~K+ULm1ht#eg_m7hUwRRB}BF_T=VN+ub zEp^Mx@At4{epyzIY$VoW^a*#o_=~{H*@Qa(uFD~Jj}SQTJP#iuhPK)8!z-pazsi^z z^tU=>PL^2`Vk~QrKNVd&&I%QbuVnMJAiR-|XIFg%2{|jGgV>%?;}uu$CZ7<9_)aRExBZ z1n~9+x0ZQdXI{Be;3TG@B(=c@Xka35efx4Ku^cH0)eD3r>*mOM=Xq3G3~M|X+YzM2 zxFW8&r#GpqtM7bSY`Vnwc^PG2{Efj53^XX1(0neK=2(;hbf*`k_zyyIr@Z7sNB2B*2DP1$+oh*9MRw3|IphT zkw%R`D~tJwvDsVX$R6uK*5Tu7sR0X93mK6COTIN8-Q-Xxq2wC@tq+JJ=~l(@-uiw{ z$tO=h&zLs+YF^Rr8xc*fM-S}3U1~d@dV0NF`^dD5kM||a1Z6M9b7=H6M=tgFXSB&0G;r#uulc8 zYWf-|dntVeJCIRwA1nKn-E85xl`#5*;h@a$+1h}kiTpV6n=RzHs0lKw?SzgiheUVG z?cV`&Yy)pELiMH&w77e+F>WtB9h&nzMyU$@_<@GyG$gNGqr!C{e*E@ub^;p*wo zEJ$2eEu--eqGPDwY>z(i6u!B+-LwnB-4Gj%t zw6i50hY{j#4NaOb|3QAw9r83yhLw4GkadRI9R%0a)&ccE{imz~S%7k)?#g%K++CNK z6d^mHF#h!gezW0+-v3YH)f$x!K9R3$Ym1^fBqV@2*Ib`(xjH&JhP`D`N(b-DSPa)A z7!JaFA@v(=DbIY~GY;&*KwwzJ8yisV4=w629E0@}JDx0Unj>~68FeuHew$Ktl(AGVY&_gjEHbK*y%u?1`SYfSl3H^!O{kA zuoaJ>-*?gCh>rSWSM|+N0L!NHD0~u4+s9iPms;g{j50PhHmHO5xBlZEikpmQ*{O;v zcW!(r9@7ExzzoqBVr;ONyTWrlx@(2(b2T~=#5d5prjT_Lh}DDY5J0ZI#;1uK?B!*w ziDqE6YCNt?0#~){KudUbz6`U{`R$guMZ(3?|G;=s6y;$aL4+SS$ zR}^VPmK145mvyq3(O0qwby{cka%H6*@;;XlXQ{HLu_}^TohLJlvWV1mv;4AkHLEyL z(c15^>?$vI4(yfUwT|6MUGTMqL)cX0*8-Kr(63~_wwUG|WQ`dZi^DZ>$d zD7gCBP&DQ&$(siQk;ebvzUR=aCYBMaB9_67)sT#9Y-nhx$KzwI&mSr|rV7X+?T-v~ zI|Dk8@-m6k^>5$4u_ph1yg&?c@`(Ut=$B>vF6aR<}-b* zud%j8v@0noDcrHNlBw+Dyfd9u^XD)T*<}kVrGxVV)b%}w{IaZ4Ik6NsIhZ>JnkcbE zui2jLytowi3}}@*vG@70cJgePoBsoly^n=b-t@bPzO*{r4Ubv=c=XX>q28iO= zp0dXaiK0Fo4=U$uB0nWZS@<}EblzakKeKMaHvD$1q|~~SKtK&QTOTKXMZ0#lHiF^j z?{7W1t3SC*$&npRr|w1Od5{_v4x*IBstv7#k4cXV?MKHeksX(8yw{wBjs(8bYQHmF zMFGZpf|i08o8IOZP&^wzc67&N8$;@wxXYSuV66q7S%*?WL&useY-~>HnV|R9bUR~@ zqJnIouyI1i32mP-a*^M9sIXz2$?(=AhqX;)CRLSJzfX;bfgT(nFxl$2Dqts9r?Exa8sgQ?R*-S$}tcUDSZCqXXm@?(!Hkt>}7K7eN z+wQywRGD$S6T~Ag5-d27z)dINUyiwO2pjHK>pzRAS64;laLNE-eO<}d-~VEBOUtZ{ zE-H|H*Z45^>%HUPE{a1QT3sxI`NCr~ZSekwlf-pZNFpk-hK4ijES6YeO}ACCLfL^R zj;GRk9PR^I4B2;1@o%UeDv&+gXixMBt3aV2 z|D$0!%OMY4P_{q`lCWGBan*z1UpLaDZ>=2+>P($d_3|Q|_E4w=(?BkZeTHIL% zy=$Mzqy#r+`^30TMCtekOF!r(eGg&tY|IXV^loZ^0eeUnN-ZsLy7dNx7v9>`&APi@ z?%gJ0XeA6zP#YX;MlcI4FL|X4;Wxy%b8P@a2SitVnA9@)(Fy(vM zfCseOohdX0krn{7ctCoPz=1b%+!6a59{8rFGa#Ln<>h%0IBAVEk?vSZ?_~WQ8yj=S z(^HCQ)n`mM3};lXyol9%hqH<l_{`_-V;73@uCfe5VbhQ&_WM-3?S?9OzOW(L<~Ayxvv~mRJP!mUzUsHRXpCQUeM%az^X0UODj0bEZ~f{ zxiOW?9)FpiurKKu<^=D9UA+m6d1Whb^}y1~yb}lS|0j==)0-D^uNoO0{YY*jIHz=3 zOc<@q9Rx{mIW7eW8)kj4!Fy5H_xA5AM?K)*kl@mVwq58j9|-V^1C={PAIz}8dj8TqU2 z7C_zUhw>HoAMHI0F}&gSGfcF!i1z4jgMg4BWFI1(pd^y6R76v&T*^O<(UO#qP$(Pr z4i#Et>$J6}@I7)O%W?cuap9pEK%f;G7j8DGW~%fcVI8vf>MFMW~-NBQdu*cW+^UQ778)3i4TnKWYByh3vVhenVQ zD#0^zuU!(jwz&_M{*OK0O9d*8S$?3m99iUbL$<7O^m{p$vriL)$=)?KHcoM-%nmPn zz%TeuW%BHVKVig-=BWtl-3yY?R<3R@#(6UoGO6W35&oKExpH6!?~iGLPvtQU+(FGeXw&FOcCuf zc4%*YUhXVr`P8XX7X1yMp82C-&yCU22A9M#&gw#a-+D7Fa%3$V6{7FvM``f(1PX%_ zpn5m;9((O>?hdRr@@X>JY2}RZQo8@AJrJOAYp;#3RO$#v1(XY&b?%YGbbxrJK9p1X zr~^n3LWFS@(Kul4Ze_<1(!Dj8f5P0I^Kh{n|2<(IOtQ307V`|zq)j>LURoj1O1Rp? zVaFy%mei;a?H#`9;jnX0SxnPx_@Qv}n+rRU9iEbWg_`S(b25i-_)|wN=Br{2@|P|Iqcta zK@!?Z#8A!0)CgfiE0kk+d-VRnt)Vcx;`V0Ng~EZ&-ExNiXU*UO37|@jZRb_Irq24k zKR12Qm(cg^AY#ynKE6P+_izl@ldOYEM`NHK>5=J5YZ?L==vu#GC!v_Rb#CO(pG1{_ zzr4Zi4&B7Q{lTWDE7J$Dm&7v2Gpbro7^Q>rS=9AC!#od5i(jM}RsKiN@uEM}{|;7_ zc2feIhgg>OnxtC)+`WKY_Q20{JmF*R!B~F0^2Ilt@to4i$;0knAi#)-dl4=2F?42d zc1?|zKtB+qX`IkYpWEDjS5fg+p+DTl2ECyXaP`%V{{5JXEzQk;R095vx2YV~9Fxjx z-(4GP3HTa5i6+jdiUdgzG1*U`Gf$y+FK1HExxw13wD}U@!g|OMDU_E-amVv-{48Q7c=&aM(sLB9whaOhIl6N4pm{kX3_i^OyGhd^R3UzslhH(#An>XW05(+Z9C z9veWEzHMsA_XleflXj0ubD6#GDf1Q~6>BRNYoY&qLLYygKpQYv&~Ss}q$#{jV{hr=q(@kl2kd2=;Lji2w< z52(c<3x4b%`{;5wss09yilwOa!~M-~WpH*#&5b=hNPxS05-46I zzU9Q+Zo&A5_}vp=-&Na_ z>rN0Xum%M#sXD8f*nR|=y(g-bH#ce)hK&H=LjJWP7Y$(u;jPdPdsb*4Btb{GPkrMlOseL>>WI!>MQ8TNJ z8P-=He;3fQd2vd_Vsj!C01BPur`X;IBs_Y5(ib4~z6H-*QX#XWZ56#yj!6afSZum%Gp3BeU><_R}_GO*UKu|s}ii)G$_Dk}0D z6#8S-GDXrp6Gwe_-0JO@Nwm$Q28G>C4c5=l3F7CwL7(gug3Y|QS0~!f_BZUTt=&9Y z35Xj`lHc3d4T_Ey_i#uS5D@rUYMga{SUFbM&`sD7fqQrw6CsDGE15)|TmMJLaq2>! zc$8Sadf(As!h9;YUnI!t{6WRPXSeUC&mDx%pbf$U=^q~fO5K_1KcxVI9VajegKI3d zgMhW~zog}Gua~Y(G*{0^U}+QN%DI!ABu03yHG-vO^>H4?VNO%#woj{XPk5wh?$&3 zempV~HWVn4%Qvu6zI3pS9#C^nkHkR35p-G!la#r(%L`@XxlT0g@=grS36c3C>U@u% zPXBVwXsdvetvHn3aVZ4;u3`}kHnftMjnul3I-_qTOiwKtQS9H)x3Q60YSU8a54IKl ztY_u`=vY9!W10K3)u|~RKR>?-FIeGY=8poE2sHXsfWLo~-~-d^xysoIvFn zkJqdzd{9k_*YT-tuNk%&e%Dsh-d5w`kZ0qNmsTQ|nO5>5&m$6r3WVb{v`;uavV7!`P93z+&GWIm*f!&+-Y5&?IKTV{<&UZO749Ok~H z3$5bPUDJWoOM<&(A&Ivkg2$v|2Gpp9(ai*}z9(xx&Ps}J(~6%kj71HO+D{_rx{B9$ zKoq`S(bfb$z&>U6;hRwQbKVJ5$cIpHsB^f*@RtUK%8H7O|M!>^F$}U7{`^5MHOs~Z z{jOqB=bts%hKZS)o!PAQw+4m&eCt2T7Q^|)<@^Eyzbox(w@v}wPo~1d!>#f>MkMl| zAaHNJ#m-}<(v=dF_)ogmL+|#&3ij}ws|U;^IE0)&b$N34vb4d>L985R4S`D!S#>-T zn%-T;XE2C$g*}s<@I_Lzm220jaL@T7<d9 zx;_xe4y>%N2R$$IZfR*Lw*#in>P_Ec)ywY@v!qI?RCh&-TLOTIGewzK|9_LEkBVr9 z*rDFtBSClH?`p*_M)S7ekh6M^|FycIr?=3q$iA39Sm@ja2_kjl`MP6!vHC}R)1kJy zAwF7kn=S+3d-+B_Ov!KFpCF8w@{F8^j{JJ<=QPpmcMj)^!#30`wN+Gns;G#xu`%{= zkj(Rlw72+gj0dWxBrS`> zW|{{DEFUoKAy1Du<;1@B9wEFxKq1*tUwbUWw77tJ0ju znjc(xIX^Ms3;Okge=aR8F@9Iti2+l!aYvQRCO*9n^%`HrJX2@U7=pA!3V{1XB<12L?cC<&$YOV39^>b=W5_6y29lUo**U?7-1CK1a zOxkSqZtFIGE1*eA`(E_i%LR;>Ixv>5k9)SSb1duqqL+ySqP<3)z`w}$bzLumqe2otx2Sh-m z3&dI%K=-SkSqT1V_A8jpwiy29Hae_Edx9U?qG9 zVfYB@0tMSc5-cPKo>}W5PUPzqq0%|Zh}};{PP#u8jQRo-^5kiLjSV(KG|GoneO9?J zPH5Wie{Smo%$w)(xBnUDX5|Vpqh$`m=crjXiUL&DTCYda1B9&}{x}Z+oOb_q1W06- zJH8aDlZ5y_So=wsdUH$4M8X(Ffkya8q3lH2;zK#kJ#F(Ft692pR=L4%tbz;k$vgj3 z&C(EnN`N}vuMWZ8wz08s%EO)SZ^*U2T10z>$-V=zN_Mjs?|N4v2LP|V^PefQ;e-Jo z5e{s&M@JTIHYga5HM>zl6P1h}L5rYl2Y5?!I_9R&DYtZ)z86!4JZ9b|2)^jV-Bb0q zNShGp|9Txp5+#Aw%AGp}bdg=(-eR>#X0!}rH}iM+BSm`SgcPtg3tzL1CzNq6aRMT2 zKAAF)PeFgWyqc0dMsMRk2_sy(brTJLh=!XWJcgB_Q z=S&GbE`1xSC!+V51NVXK*kUm}g8Q)(uDY2LT@=m+8devS#+dxiy6v6cn2FD+h$12e zT0}o6`nr2kL0ypp@+MxX4}lxMz0645z0)P`6EF}8`Y-bDONUIC*h$syVs$}QsSI;> z?qd@JJsqC3#swVaGXOjQeksUo3*ubLl&O2KiyBVC%j-ZA$YP^+3!MGl{jlf2lzCs< z{50rz9f$qp_^)VdZFUBCJJeYocRLAsUa616y-EjSS{>>ILZxC+5?{X}(L!CD8lpfE zIo}k)j=TN3Nd)HpO^ukY^c~i7Z-JuL_d*);Vxfoj@`p+#I6(dKs7(~A$h|eF$-<|l)5r&@E2F{ONR!3M?#kj8Zvp7GJ@smGs!;aycj>n~M zcQ0t*PcVN8vW$syq#qQQd)q1EXn+vM2M!~{1MhT=-0|UL69LPV-+;Cn>WgyT$Pe_y z52XHX1K&C#^GgtbnR{->_1;|$nc)Vo&gH1r!3dx*|B8BVX1iLZeh zyO#z39_2>u{7uC>2(EY;joa?sl3`fRH4NedD;WROyK@Y(yd|wtl9?`Vk}*%@2P;5) zU%`<#y2~KHOof(D%Zz>5l69`rnb+PiSoyoxMw1&rck%$DVE^9Lqm?fwS`N^wr9Mli zy2fn%S3mL};rXt1Y6oMqu4Ea{P0ktZHP1T5T?>FRf%wT2;3@lv7Sxwl!qP#gY&FW4 z_d*79K8Cds_v6lTHVSkZ*;Q|=$!(qL@gQmM#=64)dq#?$^a_dKkqpY>}{C8k`9Z?B9U?(Y)LfS>%*8P*bdF{u& zJHrA`3Nuc3Ktzg<*ez=QrhGYbmm=@mk+Cr(7`tj8Bft8R9XL=^sjZDFq1$bxh(`j1 zj9`wdhJmkirwo{1Kst+Wx!&ZSZhg;2$8q0C)%!pzva)dYNPf) zMGtMl86Y5BgmcwAk%KK%@&E-Xq~SLMRzGX8Cx$Eu*gkgM1;@Qy!^xoRzW?T4qT4P_ z^#7pQ*{r1$gaCwB$q7y>1kqOTA0d?`=kdt5&w=;C0aH@QLf7{1Z~`VR)(O z->F;fFgxWhYsc^G{}k0Ik(6NQ1j?u`=q6S{H5W$XRQTd~06=sk?zj9#lcrmX{2hIY zPbWOus&^ zJ9+ZZQRnrE_kA=^K#O*B>kP$%O{!N5K*3lZa#hDSXj;tU>#k=VkL0x_`+#`2JAyD< zbb2g}6#!!gl7rF!rF6GK@ra0H$x7)Upa<56getMouseX(); int mouseY = application->getMouseY(); + + //lazily load crosshair texture + if (_crosshairTexture == 0) { + _crosshairTexture = Application::getInstance()->getGLWidget()->bindTexture(QImage(Application::resourcesPath() + "images/sixense-reticle.png")); + } + glEnable(GL_TEXTURE_2D); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, _crosshairTexture); + + + + if (OculusManager::isConnected() && application->getLastMouseMoveType() == QEvent::MouseMove) { const float pointerWidth = 10; const float pointerHeight = 10; - const float crossPad = 4; _numMagnifiers = 1; _mouseX[0] = application->getMouseX(); @@ -289,25 +304,22 @@ void ApplicationOverlay::renderPointers() { glBegin(GL_QUADS); - glColor3f(1, 0, 0); + glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); //Horizontal crosshair - glVertex2i(mouseX, mouseY - crossPad); - glVertex2i(mouseX + pointerWidth, mouseY - crossPad); - glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight + crossPad); - glVertex2i(mouseX, mouseY - pointerHeight + crossPad); - - //Vertical crosshair - glVertex2i(mouseX + crossPad, mouseY); - glVertex2i(mouseX + pointerWidth - crossPad, mouseY); - glVertex2i(mouseX + pointerWidth - crossPad, mouseY - pointerHeight); - glVertex2i(mouseX + crossPad, mouseY - pointerHeight); + glTexCoord2d(0.0f, 0.0f); glVertex2i(mouseX, mouseY); + glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + pointerWidth, mouseY); + glTexCoord2d(1.0f, 1.0f); glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight); + glTexCoord2d(0.0f, 1.0f); glVertex2i(mouseX, mouseY - pointerHeight); glEnd(); } else if (application->getLastMouseMoveType() == CONTROLLER_MOVE_EVENT && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { //only render controller pointer if we aren't already rendering a mouse pointer renderControllerPointer(); } + + glDisable(GL_TEXTURE_2D); + } void ApplicationOverlay::renderControllerPointer() { @@ -349,13 +361,12 @@ void ApplicationOverlay::renderControllerPointer() { float pointerWidth = 40; float pointerHeight = 40; - float crossPad = 16; + //if we have the oculus, we should make the cursor smaller since it will be //magnified if (OculusManager::isConnected()) { - pointerWidth /= 4; - pointerHeight /= 4; - crossPad /= 4; + pointerWidth /= 2; + pointerHeight /= 2; _mouseX[_numMagnifiers] = mouseX; _mouseY[_numMagnifiers] = mouseY; @@ -368,19 +379,12 @@ void ApplicationOverlay::renderControllerPointer() { glBegin(GL_QUADS); - glColor3f(0.0f, 0.0f, 1.0f); + glColor3f(0.0f, 198.0f/255.0f, 244.0f/255.0f); - //Horizontal crosshair - glVertex2i(mouseX, mouseY - crossPad); - glVertex2i(mouseX + pointerWidth, mouseY - crossPad); - glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight + crossPad); - glVertex2i(mouseX, mouseY - pointerHeight + crossPad); - - //Vertical crosshair - glVertex2i(mouseX + crossPad, mouseY); - glVertex2i(mouseX + pointerWidth - crossPad, mouseY); - glVertex2i(mouseX + pointerWidth - crossPad, mouseY - pointerHeight); - glVertex2i(mouseX + crossPad, mouseY - pointerHeight); + glTexCoord2d(0.0f, 0.0f); glVertex2i(mouseX, mouseY); + glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + pointerWidth, mouseY); + glTexCoord2d(1.0f, 1.0f); glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight); + glTexCoord2d(0.0f, 1.0f); glVertex2i(mouseX, mouseY - pointerHeight); glEnd(); } diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index b6066099fa..dc1d3d641f 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -59,6 +59,8 @@ private: int _mouseX[2]; int _mouseY[2]; int _numMagnifiers; + + GLuint _crosshairTexture; }; #endif // hifi_ApplicationOverlay_h \ No newline at end of file From cb1669653d771c510667caef7ee899d4640a148f Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 15:05:23 -0700 Subject: [PATCH 058/102] Mouse reticle bigger in oculus --- interface/src/ui/ApplicationOverlay.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 2b98360b02..9222cb092d 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -292,8 +292,8 @@ void ApplicationOverlay::renderPointers() { if (OculusManager::isConnected() && application->getLastMouseMoveType() == QEvent::MouseMove) { - const float pointerWidth = 10; - const float pointerHeight = 10; + const float pointerWidth = 20; + const float pointerHeight = 20; _numMagnifiers = 1; _mouseX[0] = application->getMouseX(); From 587c0e5a9d1ca50b661b8ee310b8186a895d082c Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 16:01:59 -0700 Subject: [PATCH 059/102] Render Oculus pointers separate from texture for better quality --- interface/src/ui/ApplicationOverlay.cpp | 108 +++++++++++++++++++----- interface/src/ui/ApplicationOverlay.h | 3 +- 2 files changed, 88 insertions(+), 23 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 9222cb092d..fe6baadf66 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -37,8 +37,6 @@ ApplicationOverlay::ApplicationOverlay() : _uiType(HEMISPHERE), _crosshairTexture(0) { - - } ApplicationOverlay::~ApplicationOverlay() { @@ -259,6 +257,8 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { glEnd(); } + renderControllerPointersOculus(); + glPopMatrix(); glDepthMask(GL_TRUE); @@ -289,8 +289,6 @@ void ApplicationOverlay::renderPointers() { glBindTexture(GL_TEXTURE_2D, _crosshairTexture); - - if (OculusManager::isConnected() && application->getLastMouseMoveType() == QEvent::MouseMove) { const float pointerWidth = 20; const float pointerHeight = 20; @@ -299,30 +297,36 @@ void ApplicationOverlay::renderPointers() { _mouseX[0] = application->getMouseX(); _mouseY[0] = application->getMouseY(); - mouseX -= pointerWidth / 2.0f; - mouseY += pointerHeight / 2.0f; + //If we are in oculus, render reticle later - glBegin(GL_QUADS); - glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); + if (!OculusManager::isConnected()) { - //Horizontal crosshair - glTexCoord2d(0.0f, 0.0f); glVertex2i(mouseX, mouseY); - glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + pointerWidth, mouseY); - glTexCoord2d(1.0f, 1.0f); glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight); - glTexCoord2d(0.0f, 1.0f); glVertex2i(mouseX, mouseY - pointerHeight); + mouseX -= pointerWidth / 2.0f; + mouseY += pointerHeight / 2.0f; - glEnd(); + glBegin(GL_QUADS); + + glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); + + //Horizontal crosshair + glTexCoord2d(0.0f, 0.0f); glVertex2i(mouseX, mouseY); + glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + pointerWidth, mouseY); + glTexCoord2d(1.0f, 1.0f); glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight); + glTexCoord2d(0.0f, 1.0f); glVertex2i(mouseX, mouseY - pointerHeight); + + glEnd(); + } } else if (application->getLastMouseMoveType() == CONTROLLER_MOVE_EVENT && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { //only render controller pointer if we aren't already rendering a mouse pointer - renderControllerPointer(); + renderControllerPointers(); } - + glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); } -void ApplicationOverlay::renderControllerPointer() { +void ApplicationOverlay::renderControllerPointers() { Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); @@ -365,13 +369,12 @@ void ApplicationOverlay::renderControllerPointer() { //if we have the oculus, we should make the cursor smaller since it will be //magnified if (OculusManager::isConnected()) { - pointerWidth /= 2; - pointerHeight /= 2; _mouseX[_numMagnifiers] = mouseX; _mouseY[_numMagnifiers] = mouseY; _numMagnifiers++; - + //If oculus is enabled, we draw the crosshairs later + continue; } mouseX -= pointerWidth / 2.0f; @@ -390,13 +393,74 @@ void ApplicationOverlay::renderControllerPointer() { } } +void ApplicationOverlay::renderControllerPointersOculus() { + Application* application = Application::getInstance(); + QGLWidget* glWidget = application->getGLWidget(); + + const int widgetWidth = glWidget->width(); + const int widgetHeight = glWidget->height(); + + const float reticleSize = 50.0f; + + glBindTexture(GL_TEXTURE_2D, _crosshairTexture); + glDisable(GL_DEPTH_TEST); + + for (int i = 0; i < _numMagnifiers; i++) { + + float mouseX = (float)_mouseX[i]; + float mouseY = (float)_mouseY[i]; + mouseX -= reticleSize / 2; + mouseY += reticleSize / 2; + + // Get position on hemisphere using angle + if (_uiType == HEMISPHERE) { + + //Get new UV coordinates from our magnification window + float newULeft = mouseX / widgetWidth; + float newURight = (mouseX + reticleSize) / widgetWidth; + float newVBottom = 1.0 - mouseY / widgetHeight; + float newVTop = 1.0 - (mouseY - reticleSize) / widgetHeight; + + // Project our position onto the hemisphere using the UV coordinates + float lX = sin((newULeft - 0.5f) * _textureFov); + float rX = sin((newURight - 0.5f) * _textureFov); + float bY = sin((newVBottom - 0.5f) * _textureFov); + float tY = sin((newVTop - 0.5f) * _textureFov); + + float dist; + //Bottom Left + dist = sqrt(lX * lX + bY * bY); + float blZ = sqrt(1.0f - dist * dist); + //Top Left + dist = sqrt(lX * lX + tY * tY); + float tlZ = sqrt(1.0f - dist * dist); + //Bottom Right + dist = sqrt(rX * rX + bY * bY); + float brZ = sqrt(1.0f - dist * dist); + //Top Right + dist = sqrt(rX * rX + tY * tY); + float trZ = sqrt(1.0f - dist * dist); + + glBegin(GL_QUADS); + + glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); + + glTexCoord2f(0.0f, 0.0f); glVertex3f(lX, tY, -tlZ); + glTexCoord2f(1.0f, 0.0f); glVertex3f(rX, tY, -trZ); + glTexCoord2f(1.0f, 1.0f); glVertex3f(rX, bY, -brZ); + glTexCoord2f(0.0f, 1.0f); glVertex3f(lX, bY, -blZ); + + glEnd(); + } + } + glEnable(GL_DEPTH_TEST); +} + //Renders a small magnification of the currently bound texture at the coordinates void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) { Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); - MyAvatar* myAvatar = application->getAvatar(); - const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); float leftX, rightX, leftZ, rightZ, topZ, bottomZ; diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index dc1d3d641f..71f78d606d 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -44,7 +44,8 @@ private: typedef QPair VerticesIndices; void renderPointers(); - void renderControllerPointer(); + void renderControllerPointers(); + void renderControllerPointersOculus(); void renderMagnifier(int mouseX, int mouseY); void renderAudioMeter(); void renderStatsAndLogs(); From 53d4cc795a62b767f27c975267ba37de596afe7f Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 16:09:45 -0700 Subject: [PATCH 060/102] Removed unused UI types that clutter code --- interface/src/ui/ApplicationOverlay.cpp | 272 ++++++++---------------- interface/src/ui/ApplicationOverlay.h | 7 +- 2 files changed, 85 insertions(+), 194 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index fe6baadf66..2ef5665bee 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -34,7 +34,6 @@ ApplicationOverlay::ApplicationOverlay() : _oculusAngle(65.0f * RADIANS_PER_DEGREE), _distance(0.5f), _textureFov(DEFAULT_OCULUS_UI_ANGULAR_SIZE * RADIANS_PER_DEGREE), - _uiType(HEMISPHERE), _crosshairTexture(0) { } @@ -157,25 +156,6 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { MyAvatar* myAvatar = application->getAvatar(); const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); - // Get vertical FoV of the displayed overlay texture - const float halfVerticalAngle = _oculusAngle / 2.0f; - const float overlayAspectRatio = glWidget->width() / (float)glWidget->height(); - const float halfOverlayHeight = _distance * tan(halfVerticalAngle); - const float overlayHeight = halfOverlayHeight * 2.0f; - - // The more vertices, the better the curve - const int numHorizontalVertices = 20; - const int numVerticalVertices = 20; - // U texture coordinate width at each quad - const float quadTexWidth = 1.0f / (numHorizontalVertices - 1); - const float quadTexHeight = 1.0f / (numVerticalVertices - 1); - - // Get horizontal angle and angle increment from vertical angle and aspect ratio - const float horizontalAngle = halfVerticalAngle * 2.0f * overlayAspectRatio; - const float angleIncrement = horizontalAngle / (numHorizontalVertices - 1); - const float halfHorizontalAngle = horizontalAngle / 2; - - const float verticalAngleIncrement = _oculusAngle / (numVerticalVertices - 1); glActiveTexture(GL_TEXTURE0); @@ -211,8 +191,6 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.01f); - float leftX, rightX, leftZ, rightZ, topZ, bottomZ; - //Draw the magnifiers for (int i = 0; i < _numMagnifiers; i++) { renderMagnifier(_mouseX[i], _mouseY[i]); @@ -221,42 +199,8 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { glDepthMask(GL_FALSE); glDisable(GL_ALPHA_TEST); - //TODO: Remove immediate mode in favor of VBO - if (_uiType == HEMISPHERE) { - renderTexturedHemisphere(); - } else{ - glBegin(GL_QUADS); - // Place the vertices in a semicircle curve around the camera - for (int i = 0; i < numHorizontalVertices - 1; i++) { - for (int j = 0; j < numVerticalVertices - 1; j++) { - - // Calculate the X and Z coordinates from the angles and radius from camera - leftX = sin(angleIncrement * i - halfHorizontalAngle) * _distance; - rightX = sin(angleIncrement * (i + 1) - halfHorizontalAngle) * _distance; - leftZ = -cos(angleIncrement * i - halfHorizontalAngle) * _distance; - rightZ = -cos(angleIncrement * (i + 1) - halfHorizontalAngle) * _distance; - if (_uiType == 2) { - topZ = -cos((verticalAngleIncrement * (j + 1) - halfVerticalAngle) * overlayAspectRatio) * _distance; - bottomZ = -cos((verticalAngleIncrement * j - halfVerticalAngle) * overlayAspectRatio) * _distance; - } else { - topZ = -99999; - bottomZ = -99999; - } - - glTexCoord2f(quadTexWidth * i, (j + 1) * quadTexHeight); - glVertex3f(leftX, (j + 1) * quadTexHeight * overlayHeight - halfOverlayHeight, max(topZ, leftZ)); - glTexCoord2f(quadTexWidth * (i + 1), (j + 1) * quadTexHeight); - glVertex3f(rightX, (j + 1) * quadTexHeight * overlayHeight - halfOverlayHeight, max(topZ, rightZ)); - glTexCoord2f(quadTexWidth * (i + 1), j * quadTexHeight); - glVertex3f(rightX, j * quadTexHeight * overlayHeight - halfOverlayHeight, max(bottomZ, rightZ)); - glTexCoord2f(quadTexWidth * i, j * quadTexHeight); - glVertex3f(leftX, j * quadTexHeight * overlayHeight - halfOverlayHeight, max(bottomZ, leftZ)); - } - } - - glEnd(); - } - + renderTexturedHemisphere(); + renderControllerPointersOculus(); glPopMatrix(); @@ -411,106 +355,12 @@ void ApplicationOverlay::renderControllerPointersOculus() { float mouseY = (float)_mouseY[i]; mouseX -= reticleSize / 2; mouseY += reticleSize / 2; - - // Get position on hemisphere using angle - if (_uiType == HEMISPHERE) { - - //Get new UV coordinates from our magnification window - float newULeft = mouseX / widgetWidth; - float newURight = (mouseX + reticleSize) / widgetWidth; - float newVBottom = 1.0 - mouseY / widgetHeight; - float newVTop = 1.0 - (mouseY - reticleSize) / widgetHeight; - - // Project our position onto the hemisphere using the UV coordinates - float lX = sin((newULeft - 0.5f) * _textureFov); - float rX = sin((newURight - 0.5f) * _textureFov); - float bY = sin((newVBottom - 0.5f) * _textureFov); - float tY = sin((newVTop - 0.5f) * _textureFov); - - float dist; - //Bottom Left - dist = sqrt(lX * lX + bY * bY); - float blZ = sqrt(1.0f - dist * dist); - //Top Left - dist = sqrt(lX * lX + tY * tY); - float tlZ = sqrt(1.0f - dist * dist); - //Bottom Right - dist = sqrt(rX * rX + bY * bY); - float brZ = sqrt(1.0f - dist * dist); - //Top Right - dist = sqrt(rX * rX + tY * tY); - float trZ = sqrt(1.0f - dist * dist); - - glBegin(GL_QUADS); - - glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); - - glTexCoord2f(0.0f, 0.0f); glVertex3f(lX, tY, -tlZ); - glTexCoord2f(1.0f, 0.0f); glVertex3f(rX, tY, -trZ); - glTexCoord2f(1.0f, 1.0f); glVertex3f(rX, bY, -brZ); - glTexCoord2f(0.0f, 1.0f); glVertex3f(lX, bY, -blZ); - - glEnd(); - } - } - glEnable(GL_DEPTH_TEST); -} - -//Renders a small magnification of the currently bound texture at the coordinates -void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) -{ - Application* application = Application::getInstance(); - QGLWidget* glWidget = application->getGLWidget(); - - float leftX, rightX, leftZ, rightZ, topZ, bottomZ; - - const int widgetWidth = glWidget->width(); - const int widgetHeight = glWidget->height(); - const float magnification = 4.0f; - - // Get vertical FoV of the displayed overlay texture - const float halfVerticalAngle = _oculusAngle / 2.0f; - const float overlayAspectRatio = glWidget->width() / (float)glWidget->height(); - const float halfOverlayHeight = _distance * tan(halfVerticalAngle); - - // Get horizontal angle and angle increment from vertical angle and aspect ratio - const float horizontalAngle = halfVerticalAngle * 2.0f * overlayAspectRatio; - const float halfHorizontalAngle = horizontalAngle / 2; - - float magnifyWidth = 80.0f; - float magnifyHeight = 60.0f; - - mouseX -= magnifyWidth / 2; - mouseY -= magnifyHeight / 2; - - float newWidth = magnifyWidth * magnification; - float newHeight = magnifyHeight * magnification; - - // Magnification Texture Coordinates - float magnifyULeft = mouseX / (float)widgetWidth; - float magnifyURight = (mouseX + magnifyWidth) / (float)widgetWidth; - float magnifyVBottom = 1.0f - mouseY / (float)widgetHeight; - float magnifyVTop = 1.0f - (mouseY + magnifyHeight) / (float)widgetHeight; - - // Coordinates of magnification overlay - float newMouseX = (mouseX + magnifyWidth / 2) - newWidth / 2.0f; - float newMouseY = (mouseY + magnifyHeight / 2) + newHeight / 2.0f; - - // Get angle on the UI - float leftAngle = (newMouseX / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; - float rightAngle = ((newMouseX + newWidth) / (float)widgetWidth) * horizontalAngle - halfHorizontalAngle; - - float bottomAngle = (newMouseY / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; - float topAngle = ((newMouseY - newHeight) / (float)widgetHeight) * _oculusAngle - halfVerticalAngle; - - // Get position on hemisphere using angle - if (_uiType == HEMISPHERE) { - + //Get new UV coordinates from our magnification window - float newULeft = newMouseX / widgetWidth; - float newURight = (newMouseX + newWidth) / widgetWidth; - float newVBottom = 1.0 - newMouseY / widgetHeight; - float newVTop = 1.0 - (newMouseY - newHeight) / widgetHeight; + float newULeft = mouseX / widgetWidth; + float newURight = (mouseX + reticleSize) / widgetWidth; + float newVBottom = 1.0 - mouseY / widgetHeight; + float newVTop = 1.0 - (mouseY - reticleSize) / widgetHeight; // Project our position onto the hemisphere using the UV coordinates float lX = sin((newULeft - 0.5f) * _textureFov); @@ -534,40 +384,86 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) glBegin(GL_QUADS); - glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(lX, tY, -tlZ); - glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rX, tY, -trZ); - glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rX, bY, -brZ); - glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(lX, bY, -blZ); - - glEnd(); - - } else { - leftX = sin(leftAngle) * _distance; - rightX = sin(rightAngle) * _distance; - leftZ = -cos(leftAngle) * _distance; - rightZ = -cos(rightAngle) * _distance; - if (_uiType == CURVED_SEMICIRCLE) { - topZ = -cos(topAngle * overlayAspectRatio) * _distance; - bottomZ = -cos(bottomAngle * overlayAspectRatio) * _distance; - } else { - // Dont want to use topZ or bottomZ for SEMICIRCLE - topZ = -99999; - bottomZ = -99999; - } - - float bottomY = (1.0 - newMouseY / (float)widgetHeight) * halfOverlayHeight * 2.0f - halfOverlayHeight; - float topY = bottomY + (newHeight / widgetHeight) * halfOverlayHeight * 2; - - //TODO: Remove immediate mode in favor of VBO - glBegin(GL_QUADS); - - glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(leftX, topY, max(topZ, leftZ)); - glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rightX, topY, max(topZ, rightZ)); - glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rightX, bottomY, max(bottomZ, rightZ)); - glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(leftX, bottomY, max(bottomZ, leftZ)); + glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); + + glTexCoord2f(0.0f, 0.0f); glVertex3f(lX, tY, -tlZ); + glTexCoord2f(1.0f, 0.0f); glVertex3f(rX, tY, -trZ); + glTexCoord2f(1.0f, 1.0f); glVertex3f(rX, bY, -brZ); + glTexCoord2f(0.0f, 1.0f); glVertex3f(lX, bY, -blZ); glEnd(); + } + glEnable(GL_DEPTH_TEST); +} + +//Renders a small magnification of the currently bound texture at the coordinates +void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) +{ + Application* application = Application::getInstance(); + QGLWidget* glWidget = application->getGLWidget(); + + + const int widgetWidth = glWidget->width(); + const int widgetHeight = glWidget->height(); + const float magnification = 4.0f; + + float magnifyWidth = 80.0f; + float magnifyHeight = 60.0f; + + mouseX -= magnifyWidth / 2; + mouseY -= magnifyHeight / 2; + + float newWidth = magnifyWidth * magnification; + float newHeight = magnifyHeight * magnification; + + // Magnification Texture Coordinates + float magnifyULeft = mouseX / (float)widgetWidth; + float magnifyURight = (mouseX + magnifyWidth) / (float)widgetWidth; + float magnifyVBottom = 1.0f - mouseY / (float)widgetHeight; + float magnifyVTop = 1.0f - (mouseY + magnifyHeight) / (float)widgetHeight; + + // Coordinates of magnification overlay + float newMouseX = (mouseX + magnifyWidth / 2) - newWidth / 2.0f; + float newMouseY = (mouseY + magnifyHeight / 2) + newHeight / 2.0f; + + // Get position on hemisphere using angle + + //Get new UV coordinates from our magnification window + float newULeft = newMouseX / widgetWidth; + float newURight = (newMouseX + newWidth) / widgetWidth; + float newVBottom = 1.0 - newMouseY / widgetHeight; + float newVTop = 1.0 - (newMouseY - newHeight) / widgetHeight; + + // Project our position onto the hemisphere using the UV coordinates + float lX = sin((newULeft - 0.5f) * _textureFov); + float rX = sin((newURight - 0.5f) * _textureFov); + float bY = sin((newVBottom - 0.5f) * _textureFov); + float tY = sin((newVTop - 0.5f) * _textureFov); + + float dist; + //Bottom Left + dist = sqrt(lX * lX + bY * bY); + float blZ = sqrt(1.0f - dist * dist); + //Top Left + dist = sqrt(lX * lX + tY * tY); + float tlZ = sqrt(1.0f - dist * dist); + //Bottom Right + dist = sqrt(rX * rX + bY * bY); + float brZ = sqrt(1.0f - dist * dist); + //Top Right + dist = sqrt(rX * rX + tY * tY); + float trZ = sqrt(1.0f - dist * dist); + + glBegin(GL_QUADS); + + glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(lX, tY, -tlZ); + glTexCoord2f(magnifyURight, magnifyVBottom); glVertex3f(rX, tY, -trZ); + glTexCoord2f(magnifyURight, magnifyVTop); glVertex3f(rX, bY, -brZ); + glTexCoord2f(magnifyULeft, magnifyVTop); glVertex3f(lX, bY, -blZ); + + glEnd(); + } void ApplicationOverlay::renderAudioMeter() { diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 71f78d606d..53a0125dae 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -19,8 +19,6 @@ class QOpenGLFramebufferObject; class ApplicationOverlay { public: - enum UIType { HEMISPHERE, SEMICIRCLE, CURVED_SEMICIRCLE }; - ApplicationOverlay(); ~ApplicationOverlay(); @@ -31,9 +29,7 @@ public: // Getters QOpenGLFramebufferObject* getFramebufferObject(); - - void setUIType(UIType uiType) { _uiType = uiType; } - + private: // Interleaved vertex data struct TextureVertex { @@ -56,7 +52,6 @@ private: float _oculusAngle; float _distance; float _textureFov; - UIType _uiType; int _mouseX[2]; int _mouseY[2]; int _numMagnifiers; From bef625d237a14dd12bf36c79d61518ab919bc515 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Fri, 13 Jun 2014 16:13:43 -0700 Subject: [PATCH 061/102] Removed pointless Code --- interface/src/ui/ApplicationOverlay.cpp | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 2ef5665bee..13630290fd 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -234,33 +234,10 @@ void ApplicationOverlay::renderPointers() { if (OculusManager::isConnected() && application->getLastMouseMoveType() == QEvent::MouseMove) { - const float pointerWidth = 20; - const float pointerHeight = 20; - + //If we are in oculus, render reticle later _numMagnifiers = 1; _mouseX[0] = application->getMouseX(); _mouseY[0] = application->getMouseY(); - - //If we are in oculus, render reticle later - - - if (!OculusManager::isConnected()) { - - mouseX -= pointerWidth / 2.0f; - mouseY += pointerHeight / 2.0f; - - glBegin(GL_QUADS); - - glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); - - //Horizontal crosshair - glTexCoord2d(0.0f, 0.0f); glVertex2i(mouseX, mouseY); - glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + pointerWidth, mouseY); - glTexCoord2d(1.0f, 1.0f); glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight); - glTexCoord2d(0.0f, 1.0f); glVertex2i(mouseX, mouseY - pointerHeight); - - glEnd(); - } } else if (application->getLastMouseMoveType() == CONTROLLER_MOVE_EVENT && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { //only render controller pointer if we aren't already rendering a mouse pointer renderControllerPointers(); From 3fbde70f96694979d62a5ae22ae0f8bf17f147f1 Mon Sep 17 00:00:00 2001 From: Kai Ludwig Date: Sun, 15 Jun 2014 21:05:23 +0200 Subject: [PATCH 062/102] Added functions to test usleep, msleep and sleep for accuracy. --- libraries/shared/src/SharedUtil.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index f29e8e3345..06a5ab8137 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include "OctalCode.h" #include "SharedUtil.h" @@ -415,13 +416,16 @@ void printVoxelCode(unsigned char* voxelCode) { #ifdef _WIN32 void usleep(int waitTime) { - __int64 time1 = 0, time2 = 0, sysFreq = 0; - - QueryPerformanceCounter((LARGE_INTEGER *)&time1); - QueryPerformanceFrequency((LARGE_INTEGER *)&sysFreq); - do { - QueryPerformanceCounter((LARGE_INTEGER *)&time2); - } while( (time2 - time1) < waitTime); + quint64 compTime = waitTime + usecTimestampNow(); + quint64 compTimeSleep = compTime - 2000; + while (true) { + if (usecTimestampNow() < compTimeSleep) { + QThread::msleep(1); + } + if (usecTimestampNow() >= compTime) { + break; + } + } } #endif From 05900420a56ee37087f88a72fa84ee955850a5ae Mon Sep 17 00:00:00 2001 From: Kai Ludwig Date: Sun, 15 Jun 2014 21:07:03 +0200 Subject: [PATCH 063/102] Replaced windows usleep version with a non busy waiting high accuracy version. This fixes the high CPU load for the windows servers too! --- interface/src/Util.cpp | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 07ca65b286..5ce1435bd6 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -21,6 +21,8 @@ #include +#include + #include "InterfaceConfig.h" #include "ui/TextRenderer.h" #include "VoxelConstants.h" @@ -409,8 +411,44 @@ void runTimingTests() { float NSEC_TO_USEC = 1.0f / 1000.0f; elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; - qDebug("QElapsedTimer::nsecElapsed() usecs: %f", elapsedUsecs / (float) numTests); + qDebug("QElapsedTimer::nsecElapsed() usecs: %f", elapsedUsecs); + // Test sleep functions for accuracy + startTime.start(); + QThread::msleep(1); + elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; + qDebug("QThread::msleep(1) ms: %f", elapsedUsecs / 1000.0f); + + startTime.start(); + QThread::sleep(1); + elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; + qDebug("QThread::sleep(1) ms: %f", elapsedUsecs / 1000.0f); + + startTime.start(); + usleep(1); + elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; + qDebug("usleep(1) ms: %f", elapsedUsecs / 1000.0f); + + startTime.start(); + usleep(10); + elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; + qDebug("usleep(10) ms: %f", elapsedUsecs / 1000.0f); + + startTime.start(); + usleep(100); + elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; + qDebug("usleep(100) ms: %f", elapsedUsecs / 1000.0f); + + startTime.start(); + usleep(1000); + elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; + qDebug("usleep(1000) ms: %f", elapsedUsecs / 1000.0f); + + startTime.start(); + usleep(15000); + elapsedUsecs = (float)startTime.nsecsElapsed() * NSEC_TO_USEC; + qDebug("usleep(15000) ms: %f", elapsedUsecs / 1000.0f); + // Random number generation startTime.start(); for (int i = 0; i < numTests; i++) { From 8c700d43f34ef26bc389ad2f2374b9e57b2c1289 Mon Sep 17 00:00:00 2001 From: Kai Ludwig Date: Sun, 15 Jun 2014 21:08:14 +0200 Subject: [PATCH 064/102] Modified CALLBACK_ACCELERATOR_RATIO for windows because some systems need larger input buffer to work fine. --- interface/src/Audio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 2cbe3a062f..271bcd5279 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -1419,7 +1419,7 @@ bool Audio::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo) // proportional to the accelerator ratio. #ifdef Q_OS_WIN -const float Audio::CALLBACK_ACCELERATOR_RATIO = 0.4f; +const float Audio::CALLBACK_ACCELERATOR_RATIO = 0.1f; #endif #ifdef Q_OS_MAC From 94e58e9559bff84fd60233acdf3aaa3f9b4d314f Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 16 Jun 2014 10:04:56 -0700 Subject: [PATCH 065/102] Fixed coding standard and made getCursorPixelRangeMultiplier more clear --- interface/src/devices/SixenseManager.cpp | 10 ++++++++-- interface/src/ui/ApplicationOverlay.cpp | 8 +++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index ed55dc4c66..43dadaef1c 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -185,9 +185,14 @@ void SixenseManager::update(float deltaTime) { #endif // HAVE_SIXENSE } +const float MAXIMUM_PIXEL_RANGE_MULT = 2.0f; +const float MINIMUM_PIXEL_RANGE_MULT = 0.4f; +const float RANGE_MULT = (MAXIMUM_PIXEL_RANGE_MULT - MINIMUM_PIXEL_RANGE_MULT) * 0.01; + +//Returns a multiplier to be applied to the cursor range for the controllers float SixenseManager::getCursorPixelRangeMultiplier() const { - //scales (0,100) to (0.4,2.0) - return ((Menu::getInstance()->getSixenseReticleMoveSpeed()) * 0.008f + 0.2f) * 2.0f; + //scales (0,100) to (MINIMUM_PIXEL_RANGE_MULT, MAXIMUM_PIXEL_RANGE_MULT) + return Menu::getInstance()->getSixenseReticleMoveSpeed() * RANGE_MULT + MINIMUM_PIXEL_RANGE_MULT; } #ifdef HAVE_SIXENSE @@ -356,6 +361,7 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { float xAngle = (atan2(direction.z, direction.x) + M_PI_2); float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); + // Get the pixel range over which the xAngle and yAngle are scaled float cursorRange = widget->width() * getCursorPixelRangeMultiplier(); pos.setX(widget->width() / 2.0f + cursorRange * xAngle); diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 13630290fd..aedc81facc 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -46,6 +46,8 @@ ApplicationOverlay::~ApplicationOverlay() { const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; +const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f }; + // Renders the overlays either to a texture or to the screen void ApplicationOverlay::renderOverlay(bool renderToTexture) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); @@ -222,7 +224,6 @@ void ApplicationOverlay::renderPointers() { int mouseX = application->getMouseX(); int mouseY = application->getMouseY(); - //lazily load crosshair texture if (_crosshairTexture == 0) { _crosshairTexture = Application::getInstance()->getGLWidget()->bindTexture(QImage(Application::resourcesPath() + "images/sixense-reticle.png")); @@ -274,6 +275,7 @@ void ApplicationOverlay::renderControllerPointers() { float xAngle = (atan2(direction.z, direction.x) + M_PI_2) ; float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); + // Get the pixel range over which the xAngle and yAngle are scaled float cursorRange = glWidget->width() * application->getSixenseManager()->getCursorPixelRangeMultiplier(); int mouseX = glWidget->width() / 2.0f + cursorRange * xAngle; @@ -303,7 +305,7 @@ void ApplicationOverlay::renderControllerPointers() { glBegin(GL_QUADS); - glColor3f(0.0f, 198.0f/255.0f, 244.0f/255.0f); + glColor3f(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2]); glTexCoord2d(0.0f, 0.0f); glVertex2i(mouseX, mouseY); glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + pointerWidth, mouseY); @@ -361,7 +363,7 @@ void ApplicationOverlay::renderControllerPointersOculus() { glBegin(GL_QUADS); - glColor3f(0.0f, 198.0f / 255.0f, 244.0f / 255.0f); + glColor3f(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2]); glTexCoord2f(0.0f, 0.0f); glVertex3f(lX, tY, -tlZ); glTexCoord2f(1.0f, 0.0f); glVertex3f(rX, tY, -trZ); From 031fe323ea5e98b5d37167da3c983ce7960d45b8 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 16 Jun 2014 10:12:09 -0700 Subject: [PATCH 066/102] Got rid of some verbosity --- interface/src/devices/SixenseManager.cpp | 13 +++++++------ interface/src/devices/SixenseManager.h | 2 +- interface/src/ui/ApplicationOverlay.cpp | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 43dadaef1c..2c62a58800 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -185,14 +185,15 @@ void SixenseManager::update(float deltaTime) { #endif // HAVE_SIXENSE } -const float MAXIMUM_PIXEL_RANGE_MULT = 2.0f; -const float MINIMUM_PIXEL_RANGE_MULT = 0.4f; -const float RANGE_MULT = (MAXIMUM_PIXEL_RANGE_MULT - MINIMUM_PIXEL_RANGE_MULT) * 0.01; +//Constants for getCursorPixelRangeMultiplier() +const float MIN_PIXEL_RANGE_MULT = 0.4f; +const float MAX_PIXEL_RANGE_MULT = 2.0f; +const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01; //Returns a multiplier to be applied to the cursor range for the controllers -float SixenseManager::getCursorPixelRangeMultiplier() const { +float SixenseManager::getCursorPixelRangeMult() const { //scales (0,100) to (MINIMUM_PIXEL_RANGE_MULT, MAXIMUM_PIXEL_RANGE_MULT) - return Menu::getInstance()->getSixenseReticleMoveSpeed() * RANGE_MULT + MINIMUM_PIXEL_RANGE_MULT; + return Menu::getInstance()->getSixenseReticleMoveSpeed() * RANGE_MULT + MIN_PIXEL_RANGE_MULT; } #ifdef HAVE_SIXENSE @@ -362,7 +363,7 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); // Get the pixel range over which the xAngle and yAngle are scaled - float cursorRange = widget->width() * getCursorPixelRangeMultiplier(); + float cursorRange = widget->width() * getCursorPixelRangeMult(); pos.setX(widget->width() / 2.0f + cursorRange * xAngle); pos.setY(widget->height() / 2.0f + cursorRange * yAngle); diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index 41e061a25b..8803c2c006 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -42,7 +42,7 @@ public: ~SixenseManager(); void update(float deltaTime); - float getCursorPixelRangeMultiplier() const; + float getCursorPixelRangeMult() const; public slots: diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index aedc81facc..471ff20d66 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -276,7 +276,7 @@ void ApplicationOverlay::renderControllerPointers() { float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2)); // Get the pixel range over which the xAngle and yAngle are scaled - float cursorRange = glWidget->width() * application->getSixenseManager()->getCursorPixelRangeMultiplier(); + float cursorRange = glWidget->width() * application->getSixenseManager()->getCursorPixelRangeMult(); int mouseX = glWidget->width() / 2.0f + cursorRange * xAngle; int mouseY = glWidget->height() / 2.0f + cursorRange * yAngle; From af3b2a9d0f6c4f8e332e5a766915d53d309afad0 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 16 Jun 2014 10:43:13 -0700 Subject: [PATCH 067/102] Update LocationManager::goTo to use new addresses API The new API address is now used for goTo when # or @ isn't present Fixed the multiple locations popup to show when multiple locations are found --- interface/src/Menu.cpp | 8 ++- interface/src/avatar/MyAvatar.cpp | 64 +++++++++++----------- interface/src/avatar/MyAvatar.h | 1 + interface/src/location/LocationManager.cpp | 57 ++++++------------- interface/src/location/LocationManager.h | 7 +-- 5 files changed, 58 insertions(+), 79 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 0c6a3efa24..46b2bfc806 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1002,7 +1002,9 @@ bool Menu::goToDestination(QString destination) { } void Menu::goTo(QString destination) { - LocationManager::getInstance().goTo(destination); + LocationManager* manager = &LocationManager::getInstance(); + manager->goTo(destination); + connect(manager, &LocationManager::multipleDestinationsFound, getInstance(), &Menu::multipleDestinationsDecision); } void Menu::goTo() { @@ -1091,9 +1093,9 @@ void Menu::multipleDestinationsDecision(const QJsonObject& userData, const QJson int userResponse = msgBox.exec(); if (userResponse == QMessageBox::Ok) { - Application::getInstance()->getAvatar()->goToLocationFromResponse(userData); + Application::getInstance()->getAvatar()->goToLocationFromAddress(userData["address"].toObject()); } else if (userResponse == QMessageBox::Open) { - Application::getInstance()->getAvatar()->goToLocationFromResponse(userData); + Application::getInstance()->getAvatar()->goToLocationFromAddress(placeData["address"].toObject()); } LocationManager* manager = reinterpret_cast(sender()); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index a14152aa04..54ed641d72 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1626,44 +1626,46 @@ void MyAvatar::resetSize() { } void MyAvatar::goToLocationFromResponse(const QJsonObject& jsonObject) { - if (jsonObject["status"].toString() == "success") { - - // send a node kill request, indicating to other clients that they should play the "disappeared" effect - sendKillAvatar(); - QJsonObject locationObject = jsonObject["data"].toObject()["address"].toObject(); - QString positionString = locationObject["position"].toString(); - QString orientationString = locationObject["orientation"].toString(); - QString domainHostnameString = locationObject["domain"].toString(); - - qDebug() << "Changing domain to" << domainHostnameString << - ", position to" << positionString << - ", and orientation to" << orientationString; - - QStringList coordinateItems = positionString.split(','); - QStringList orientationItems = orientationString.split(','); - - NodeList::getInstance()->getDomainHandler().setHostname(domainHostnameString); - - // orient the user to face the target - glm::quat newOrientation = glm::quat(glm::radians(glm::vec3(orientationItems[0].toFloat(), - orientationItems[1].toFloat(), - orientationItems[2].toFloat()))) - * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); - setOrientation(newOrientation); - - // move the user a couple units away - const float DISTANCE_TO_USER = 2.0f; - glm::vec3 newPosition = glm::vec3(coordinateItems[0].toFloat(), coordinateItems[1].toFloat(), - coordinateItems[2].toFloat()) - newOrientation * IDENTITY_FRONT * DISTANCE_TO_USER; - setPosition(newPosition); - emit transformChanged(); + goToLocationFromAddress(locationObject); } else { QMessageBox::warning(Application::getInstance()->getWindow(), "", "That user or location could not be found."); } } +void MyAvatar::goToLocationFromAddress(const QJsonObject& locationObject) { + // send a node kill request, indicating to other clients that they should play the "disappeared" effect + sendKillAvatar(); + + QString positionString = locationObject["position"].toString(); + QString orientationString = locationObject["orientation"].toString(); + QString domainHostnameString = locationObject["domain"].toString(); + + qDebug() << "Changing domain to" << domainHostnameString << + ", position to" << positionString << + ", and orientation to" << orientationString; + + QStringList coordinateItems = positionString.split(','); + QStringList orientationItems = orientationString.split(','); + + NodeList::getInstance()->getDomainHandler().setHostname(domainHostnameString); + + // orient the user to face the target + glm::quat newOrientation = glm::quat(glm::radians(glm::vec3(orientationItems[0].toFloat(), + orientationItems[1].toFloat(), + orientationItems[2].toFloat()))) + * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); + setOrientation(newOrientation); + + // move the user a couple units away + const float DISTANCE_TO_USER = 2.0f; + glm::vec3 newPosition = glm::vec3(coordinateItems[0].toFloat(), coordinateItems[1].toFloat(), + coordinateItems[2].toFloat()) - newOrientation * IDENTITY_FRONT * DISTANCE_TO_USER; + setPosition(newPosition); + emit transformChanged(); +} + void MyAvatar::updateMotionBehaviorsFromMenu() { Menu* menu = Menu::getInstance(); if (menu->isOptionChecked(MenuOption::ObeyEnvironmentalGravity)) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index d99102c356..2fbc488feb 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -129,6 +129,7 @@ public slots: void resetSize(); void goToLocationFromResponse(const QJsonObject& jsonObject); + void goToLocationFromAddress(const QJsonObject& jsonObject); // Set/Get update the thrust that will move the avatar around void addThrust(glm::vec3 newThrust) { _thrust += newThrust; }; diff --git a/interface/src/location/LocationManager.cpp b/interface/src/location/LocationManager.cpp index f80c331df4..f1fd5e71f1 100644 --- a/interface/src/location/LocationManager.cpp +++ b/interface/src/location/LocationManager.cpp @@ -16,10 +16,11 @@ const QString GET_USER_ADDRESS = "/api/v1/users/%1/address"; const QString GET_PLACE_ADDRESS = "/api/v1/places/%1/address"; +const QString GET_ADDRESSES = "/api/v1/addresses/%1"; const QString POST_PLACE_CREATE = "/api/v1/places/"; -LocationManager::LocationManager() : _userData(), _placeData() { +LocationManager::LocationManager() { }; @@ -74,59 +75,37 @@ void LocationManager::goTo(QString destination) { // go to coordinate destination or to Username if (!goToDestination(destination)) { - // reset data on local variables - _userData = QJsonObject(); - _placeData = QJsonObject(); - destination = QString(QUrl::toPercentEncoding(destination)); JSONCallbackParameters callbackParams; callbackParams.jsonCallbackReceiver = this; - callbackParams.jsonCallbackMethod = "goToUserFromResponse"; - AccountManager::getInstance().authenticatedRequest(GET_USER_ADDRESS.arg(destination), - QNetworkAccessManager::GetOperation, - callbackParams); - - callbackParams.jsonCallbackMethod = "goToLocationFromResponse"; - AccountManager::getInstance().authenticatedRequest(GET_PLACE_ADDRESS.arg(destination), + callbackParams.jsonCallbackMethod = "goToAddressFromResponse"; + AccountManager::getInstance().authenticatedRequest(GET_ADDRESSES.arg(destination), QNetworkAccessManager::GetOperation, callbackParams); } } -void LocationManager::goToUserFromResponse(const QJsonObject& jsonObject) { - _userData = jsonObject; - checkForMultipleDestinations(); -} +void LocationManager::goToAddressFromResponse(const QJsonObject& responseData) { + QJsonValue status = responseData["status"]; + qDebug() << responseData; + if (!status.isUndefined() && status.toString() == "success") { + const QJsonObject& data = responseData["data"].toObject(); + const QJsonValue& userObject = data["user"]; + const QJsonValue& placeObject = data["place"]; -void LocationManager::goToLocationFromResponse(const QJsonObject& jsonObject) { - _placeData = jsonObject; - checkForMultipleDestinations(); -} - -void LocationManager::checkForMultipleDestinations() { - if (!_userData.isEmpty() && !_placeData.isEmpty()) { - if (_userData.contains("status") && _userData["status"].toString() == "success" && - _placeData.contains("status") && _placeData["status"].toString() == "success") { - emit multipleDestinationsFound(_userData, _placeData); - return; + if (!placeObject.isUndefined() && !userObject.isUndefined()) { + emit multipleDestinationsFound(userObject.toObject(), placeObject.toObject()); + } else if (placeObject.isUndefined()) { + Application::getInstance()->getAvatar()->goToLocationFromAddress(userObject.toObject()["address"].toObject()); + } else { + Application::getInstance()->getAvatar()->goToLocationFromAddress(placeObject.toObject()["address"].toObject()); } - - if (_userData.contains("status") && _userData["status"].toString() == "success") { - Application::getInstance()->getAvatar()->goToLocationFromResponse(_userData); - return; - } - - if (_placeData.contains("status") && _placeData["status"].toString() == "success") { - Application::getInstance()->getAvatar()->goToLocationFromResponse(_placeData); - return; - } - + } else { QMessageBox::warning(Application::getInstance()->getWindow(), "", "That user or location could not be found."); } } void LocationManager::goToUser(QString userName) { - JSONCallbackParameters callbackParams; callbackParams.jsonCallbackReceiver = Application::getInstance()->getAvatar(); callbackParams.jsonCallbackMethod = "goToLocationFromResponse"; diff --git a/interface/src/location/LocationManager.h b/interface/src/location/LocationManager.h index ac66b3d08b..b781f3f54e 100644 --- a/interface/src/location/LocationManager.h +++ b/interface/src/location/LocationManager.h @@ -39,11 +39,7 @@ public: bool goToDestination(QString destination); private: - QJsonObject _userData; - QJsonObject _placeData; - void replaceLastOccurrence(const QChar search, const QChar replace, QString& string); - void checkForMultipleDestinations(); signals: void creationCompleted(LocationManager::NamedLocationCreateResponse response); @@ -52,8 +48,7 @@ signals: private slots: void namedLocationDataReceived(const QJsonObject& data); void errorDataReceived(QNetworkReply::NetworkError error, const QString& message); - void goToLocationFromResponse(const QJsonObject& jsonObject); - void goToUserFromResponse(const QJsonObject& jsonObject); + void goToAddressFromResponse(const QJsonObject& jsonObject); }; From d5aa12db7d9bb99d634f9e08796a2141baca57b2 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 16 Jun 2014 10:55:05 -0700 Subject: [PATCH 068/102] Added models support to inspect.js --- examples/inspect.js | 49 ++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/examples/inspect.js b/examples/inspect.js index 28db1e7735..af63957ec3 100644 --- a/examples/inspect.js +++ b/examples/inspect.js @@ -177,30 +177,45 @@ function keyReleaseEvent(event) { function mousePressEvent(event) { if (alt && !isActive) { - isActive = true; mouseLastX = event.x; mouseLastY = event.y; // Compute trajectories related values var pickRay = Camera.computePickRay(mouseLastX, mouseLastY); - var intersection = Voxels.findRayIntersection(pickRay); + var voxelIntersection = Voxels.findRayIntersection(pickRay); + var modelIntersection = Models.findRayIntersection(pickRay); position = Camera.getPosition(); - avatarTarget = MyAvatar.getTargetAvatarPosition(); - voxelTarget = intersection.intersection; - if (Vec3.length(Vec3.subtract(avatarTarget, position)) < Vec3.length(Vec3.subtract(voxelTarget, position))) { - if (avatarTarget.x != 0 || avatarTarget.y != 0 || avatarTarget.z != 0) { - center = avatarTarget; - } else { - center = voxelTarget; - } - } else { - if (voxelTarget.x != 0 || voxelTarget.y != 0 || voxelTarget.z != 0) { - center = voxelTarget; - } else { - center = avatarTarget; - } + var avatarTarget = MyAvatar.getTargetAvatarPosition(); + var voxelTarget = voxelIntersection.intersection; + + + var distance = -1; + var string; + + if (modelIntersection.intersects && modelIntersection.accurate) { + distance = modelIntersection.distance; + center = modelIntersection.modelProperties.position; + string = "Inspecting model"; + } + + if ((distance == -1 || Vec3.length(Vec3.subtract(avatarTarget, position)) < distance) && + (avatarTarget.x != 0 || avatarTarget.y != 0 || avatarTarget.z != 0)) { + distance = Vec3.length(Vec3.subtract(avatarTarget, position)); + center = avatarTarget; + string = "Inspecting avatar"; + } + + if ((distance == -1 || Vec3.length(Vec3.subtract(voxelTarget, position)) < distance) && + (voxelTarget.x != 0 || voxelTarget.y != 0 || voxelTarget.z != 0)) { + distance = Vec3.length(Vec3.subtract(voxelTarget, position)); + center = voxelTarget; + string = "Inspecting voxel"; + } + + if (distance == -1) { + return; } vector = Vec3.subtract(position, center); @@ -209,6 +224,8 @@ function mousePressEvent(event) { altitude = Math.asin(vector.y / Vec3.length(vector)); Camera.keepLookingAt(center); + print(string); + isActive = true; } } From b3b3a72946202da452d1846879fa5f271047c899 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 16 Jun 2014 10:55:34 -0700 Subject: [PATCH 069/102] Disable editModels.js mouse events when ALT is pressed. (no conflict with inspect.js) --- examples/editModels.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/examples/editModels.js b/examples/editModels.js index 93a34b9a3a..53633d3c0c 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -691,6 +691,10 @@ function rayPlaneIntersection(pickRay, point, normal) { } function mousePressEvent(event) { + if (altIsPressed) { + return; + } + mouseLastPosition = { x: event.x, y: event.y }; modelSelected = false; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); @@ -790,6 +794,10 @@ var oldModifier = 0; var modifier = 0; var wasShifted = false; function mouseMoveEvent(event) { + if (altIsPressed) { + return; + } + var pickRay = Camera.computePickRay(event.x, event.y); if (!modelSelected) { @@ -894,6 +902,10 @@ function mouseMoveEvent(event) { } function mouseReleaseEvent(event) { + if (altIsPressed) { + return; + } + modelSelected = false; glowedModelID.id = -1; @@ -962,4 +974,16 @@ Menu.menuItemEvent.connect(function(menuItem){ } }); +// handling of inspect.js concurrence +altIsPressed = false; +Controller.keyPressEvent.connect(function(event) { + if (event.text == "ALT") { + altIsPressed = true; + } +}); +Controller.keyReleaseEvent.connect(function(event) { + if (event.text == "ALT") { + altIsPressed = false; + } +}); From 978b7521dba30fb90e5eb39a2a5075dcd9fcd1ff Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 16 Jun 2014 11:30:47 -0700 Subject: [PATCH 070/102] Move connect call for multiple destinations to constructor --- interface/src/Menu.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 46b2bfc806..8f023e607f 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -165,6 +165,8 @@ Menu::Menu() : Qt::Key_At, this, SLOT(goTo())); + connect(&LocationManager::getInstance(), &LocationManager::multipleDestinationsFound, + this, &Menu::multipleDestinationsDecision); addDisabledActionAndSeparator(fileMenu, "Upload Avatar Model"); addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadHead, 0, Application::getInstance(), SLOT(uploadHead())); @@ -1002,9 +1004,7 @@ bool Menu::goToDestination(QString destination) { } void Menu::goTo(QString destination) { - LocationManager* manager = &LocationManager::getInstance(); - manager->goTo(destination); - connect(manager, &LocationManager::multipleDestinationsFound, getInstance(), &Menu::multipleDestinationsDecision); + LocationManager::getInstance().goTo(destination); } void Menu::goTo() { From 9faf9d7749d4f50d4a03d6b44cbb43314ad86b36 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 16 Jun 2014 11:43:04 -0700 Subject: [PATCH 071/102] Remove extra connect and disconnect for multipleDestinationsDecision --- interface/src/Menu.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f2f68a3594..f4fb8a363f 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1074,9 +1074,7 @@ bool Menu::goToURL(QString location) { } void Menu::goToUser(const QString& user) { - LocationManager* manager = &LocationManager::getInstance(); - manager->goTo(user); - connect(manager, &LocationManager::multipleDestinationsFound, this, &Menu::multipleDestinationsDecision); + LocationManager::getInstance().goTo(user); } /// Open a url, shortcutting any "hifi" scheme URLs to the local application. @@ -1104,7 +1102,6 @@ void Menu::multipleDestinationsDecision(const QJsonObject& userData, const QJson } LocationManager* manager = reinterpret_cast(sender()); - disconnect(manager, &LocationManager::multipleDestinationsFound, this, &Menu::multipleDestinationsDecision); } void Menu::muteEnvironment() { From d08b63b2476244721ae1d601ad47d681bae28ea3 Mon Sep 17 00:00:00 2001 From: Kai Ludwig Date: Mon, 16 Jun 2014 23:30:28 +0200 Subject: [PATCH 072/102] changed 2000 into a constant defined inline --- libraries/shared/src/SharedUtil.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 06a5ab8137..e4d2e1c835 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -416,8 +416,9 @@ void printVoxelCode(unsigned char* voxelCode) { #ifdef _WIN32 void usleep(int waitTime) { + const quint64 BUSY_LOOP_USECS = 2000; quint64 compTime = waitTime + usecTimestampNow(); - quint64 compTimeSleep = compTime - 2000; + quint64 compTimeSleep = compTime - BUSY_LOOP_USECS; while (true) { if (usecTimestampNow() < compTimeSleep) { QThread::msleep(1); From 84d183b2bc1a9c3c929870e6e1bc0b8746ae38a4 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 16 Jun 2014 14:46:40 -0700 Subject: [PATCH 073/102] Switched Oculus magnify method to a manual mode --- interface/src/ui/ApplicationOverlay.cpp | 81 +++++++++++++++++-------- interface/src/ui/ApplicationOverlay.h | 18 ++++-- 2 files changed, 69 insertions(+), 30 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 471ff20d66..8d118e90c4 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -36,6 +36,9 @@ ApplicationOverlay::ApplicationOverlay() : _textureFov(DEFAULT_OCULUS_UI_ANGULAR_SIZE * RADIANS_PER_DEGREE), _crosshairTexture(0) { + _magActive[MOUSE] = false; + _magActive[LEFT_CONTROLLER] = false; + _magActive[RIGHT_CONTROLLER] = false; } ApplicationOverlay::~ApplicationOverlay() { @@ -45,7 +48,6 @@ ApplicationOverlay::~ApplicationOverlay() { } const float WHITE_TEXT[] = { 0.93f, 0.93f, 0.93f }; - const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f }; // Renders the overlays either to a texture or to the screen @@ -158,7 +160,6 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { MyAvatar* myAvatar = application->getAvatar(); const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); - glActiveTexture(GL_TEXTURE0); glEnable(GL_BLEND); @@ -194,8 +195,11 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { glAlphaFunc(GL_GREATER, 0.01f); //Draw the magnifiers - for (int i = 0; i < _numMagnifiers; i++) { - renderMagnifier(_mouseX[i], _mouseY[i]); + for (int i = 0; i < 3; i++) { + + if (_magActive[i]) { + renderMagnifier(_magX[i], _magY[i]); + } } glDepthMask(GL_FALSE); @@ -220,7 +224,6 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { void ApplicationOverlay::renderPointers() { Application* application = Application::getInstance(); // Render a crosshair over the mouse when in Oculus - _numMagnifiers = 0; int mouseX = application->getMouseX(); int mouseY = application->getMouseY(); @@ -236,11 +239,19 @@ void ApplicationOverlay::renderPointers() { if (OculusManager::isConnected() && application->getLastMouseMoveType() == QEvent::MouseMove) { //If we are in oculus, render reticle later - _numMagnifiers = 1; - _mouseX[0] = application->getMouseX(); - _mouseY[0] = application->getMouseY(); + _reticleActive[MOUSE] = true; + _magActive[MOUSE] = true; + _mouseX[MOUSE] = application->getMouseX(); + _mouseY[MOUSE] = application->getMouseY(); + _magX[MOUSE] = _mouseX[MOUSE]; + _magY[MOUSE] = _mouseY[MOUSE]; + + _reticleActive[LEFT_CONTROLLER] = false; + _reticleActive[RIGHT_CONTROLLER] = false; + } else if (application->getLastMouseMoveType() == CONTROLLER_MOVE_EVENT && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { //only render controller pointer if we aren't already rendering a mouse pointer + _reticleActive[MOUSE] = false; renderControllerPointers(); } glBindTexture(GL_TEXTURE_2D, 0); @@ -256,6 +267,8 @@ void ApplicationOverlay::renderControllerPointers() { const HandData* handData = Application::getInstance()->getAvatar()->getHandData(); for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) { + const int index = palmIndex - 1; + const PalmData* palmData = NULL; if (palmIndex >= handData->getPalms().size()) { @@ -268,6 +281,8 @@ void ApplicationOverlay::renderControllerPointers() { continue; } + int controllerButtons = palmData->getControllerButtons(); + // Get directon relative to avatar orientation glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); @@ -281,22 +296,35 @@ void ApplicationOverlay::renderControllerPointers() { int mouseX = glWidget->width() / 2.0f + cursorRange * xAngle; int mouseY = glWidget->height() / 2.0f + cursorRange * yAngle; + // If the 2 button is pressed, we disable the magnifier for this controller + if (controllerButtons & BUTTON_2) { + _magActive[index] = false; + _magX[index] = mouseX; + _magY[index] = mouseY; + } + //If the cursor is out of the screen then don't render it if (mouseX < 0 || mouseX >= glWidget->width() || mouseY < 0 || mouseY >= glWidget->height()) { continue; } - + _reticleActive[index] = true; float pointerWidth = 40; float pointerHeight = 40; //if we have the oculus, we should make the cursor smaller since it will be //magnified if (OculusManager::isConnected()) { + + _mouseX[index] = mouseX; + _mouseY[index] = mouseY; - _mouseX[_numMagnifiers] = mouseX; - _mouseY[_numMagnifiers] = mouseY; - _numMagnifiers++; - //If oculus is enabled, we draw the crosshairs later + if (controllerButtons & BUTTON_3) { + _magActive[index] = true; + _magX[index] = mouseX; + _magY[index] = mouseY; + } + + // If oculus is enabled, we draw the crosshairs later continue; } @@ -328,7 +356,12 @@ void ApplicationOverlay::renderControllerPointersOculus() { glBindTexture(GL_TEXTURE_2D, _crosshairTexture); glDisable(GL_DEPTH_TEST); - for (int i = 0; i < _numMagnifiers; i++) { + for (int i = 0; i < 3; i++) { + + //Dont render the reticle if its inactive + if (!_reticleActive[i]) { + continue; + } float mouseX = (float)_mouseX[i]; float mouseY = (float)_mouseY[i]; @@ -385,26 +418,22 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) const int widgetWidth = glWidget->width(); const int widgetHeight = glWidget->height(); - const float magnification = 4.0f; - float magnifyWidth = 80.0f; - float magnifyHeight = 60.0f; + mouseX -= MAGNIFY_WIDTH / 2; + mouseY -= MAGNIFY_HEIGHT / 2; - mouseX -= magnifyWidth / 2; - mouseY -= magnifyHeight / 2; - - float newWidth = magnifyWidth * magnification; - float newHeight = magnifyHeight * magnification; + float newWidth = MAGNIFY_WIDTH * MAGNIFY_MULT; + float newHeight = MAGNIFY_HEIGHT * MAGNIFY_MULT; // Magnification Texture Coordinates float magnifyULeft = mouseX / (float)widgetWidth; - float magnifyURight = (mouseX + magnifyWidth) / (float)widgetWidth; + float magnifyURight = (mouseX + MAGNIFY_WIDTH) / (float)widgetWidth; float magnifyVBottom = 1.0f - mouseY / (float)widgetHeight; - float magnifyVTop = 1.0f - (mouseY + magnifyHeight) / (float)widgetHeight; + float magnifyVTop = 1.0f - (mouseY + MAGNIFY_HEIGHT) / (float)widgetHeight; // Coordinates of magnification overlay - float newMouseX = (mouseX + magnifyWidth / 2) - newWidth / 2.0f; - float newMouseY = (mouseY + magnifyHeight / 2) + newHeight / 2.0f; + float newMouseX = (mouseX + MAGNIFY_WIDTH / 2) - newWidth / 2.0f; + float newMouseY = (mouseY + MAGNIFY_HEIGHT / 2) + newHeight / 2.0f; // Get position on hemisphere using angle diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 53a0125dae..80c7cd1ad8 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -15,18 +15,22 @@ class Overlays; class QOpenGLFramebufferObject; +const float MAGNIFY_WIDTH = 160.0f; +const float MAGNIFY_HEIGHT = 80.0f; +const float MAGNIFY_MULT = 4.0f; + // Handles the drawing of the overlays to the screen class ApplicationOverlay { public: ApplicationOverlay(); + ~ApplicationOverlay(); void renderOverlay(bool renderToTexture = false); void displayOverlayTexture(Camera& whichCamera); void displayOverlayTextureOculus(Camera& whichCamera); void computeOculusPickRay(float x, float y, glm::vec3& direction) const; - // Getters QOpenGLFramebufferObject* getFramebufferObject(); @@ -37,6 +41,8 @@ private: glm::vec2 uv; }; + enum MousePointerDevice { MOUSE, LEFT_CONTROLLER, RIGHT_CONTROLLER }; + typedef QPair VerticesIndices; void renderPointers(); @@ -52,9 +58,13 @@ private: float _oculusAngle; float _distance; float _textureFov; - int _mouseX[2]; - int _mouseY[2]; - int _numMagnifiers; + // 0 = Mouse, 1 = Left Controller, 2 = Right Controller + bool _reticleActive[3]; + int _mouseX[3]; + int _mouseY[3]; + bool _magActive[3]; + int _magX[3]; + int _magY[3]; GLuint _crosshairTexture; }; From 2b374470b0e382525d181712d8759f9a8a9cdf0e Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 16 Jun 2014 15:24:53 -0700 Subject: [PATCH 074/102] Allow clicking on magnified controls --- interface/src/devices/SixenseManager.cpp | 32 ++++++++++++++++-------- interface/src/ui/ApplicationOverlay.cpp | 22 ++++++++++++++++ interface/src/ui/ApplicationOverlay.h | 3 ++- 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 2c62a58800..07536d0af8 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -341,8 +341,9 @@ void SixenseManager::updateCalibration(const sixenseControllerData* controllers) //Injecting mouse movements and clicks void SixenseManager::emulateMouse(PalmData* palm, int index) { - MyAvatar* avatar = Application::getInstance()->getAvatar(); - QGLWidget* widget = Application::getInstance()->getGLWidget(); + Application* application = Application::getInstance(); + MyAvatar* avatar = application->getAvatar(); + QGLWidget* widget = application->getGLWidget(); QPoint pos; // Get directon relative to avatar orientation glm::vec3 direction = glm::inverse(avatar->getOrientation()) * palm->getFingerDirection(); @@ -374,14 +375,14 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { if (_bumperPressed[index]) { QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, bumperButton, bumperButton, 0); - Application::getInstance()->mouseReleaseEvent(&mouseEvent); + application->mouseReleaseEvent(&mouseEvent); _bumperPressed[index] = false; } if (_triggerPressed[index]) { QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, triggerButton, triggerButton, 0); - Application::getInstance()->mouseReleaseEvent(&mouseEvent); + application->mouseReleaseEvent(&mouseEvent); _triggerPressed[index] = false; } @@ -396,17 +397,28 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { //This is specifically for edit voxels if (triggerButton == Qt::LeftButton) { if (!_triggerPressed[(int)(!index)]) { - Application::getInstance()->mouseMoveEvent(&mouseEvent); + application->mouseMoveEvent(&mouseEvent); } } else { if (!_bumperPressed[(int)(!index)]) { - Application::getInstance()->mouseMoveEvent(&mouseEvent); + application->mouseMoveEvent(&mouseEvent); } } } _oldX[index] = pos.x(); _oldY[index] = pos.y(); + + //We need separate coordinates for clicks, since we need to check if + //a magnification window was clicked on + int clickX = pos.x(); + int clickY = pos.y(); + //Checks for magnification window click + application->getApplicationOverlay().getClickLocation(clickX, clickY); + //Set pos to the new click location, which may be the same if no magnification window is open + pos.setX(clickX); + pos.setY(clickY); + //Check for bumper press ( Right Click ) if (palm->getControllerButtons() & BUTTON_FWD) { if (!_bumperPressed[index]) { @@ -414,12 +426,12 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, bumperButton, bumperButton, 0); - Application::getInstance()->mousePressEvent(&mouseEvent); + application->mousePressEvent(&mouseEvent); } } else if (_bumperPressed[index]) { QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, bumperButton, bumperButton, 0); - Application::getInstance()->mouseReleaseEvent(&mouseEvent); + application->mouseReleaseEvent(&mouseEvent); _bumperPressed[index] = false; } @@ -431,12 +443,12 @@ void SixenseManager::emulateMouse(PalmData* palm, int index) { QMouseEvent mouseEvent(QEvent::MouseButtonPress, pos, triggerButton, triggerButton, 0); - Application::getInstance()->mousePressEvent(&mouseEvent); + application->mousePressEvent(&mouseEvent); } } else if (_triggerPressed[index]) { QMouseEvent mouseEvent(QEvent::MouseButtonRelease, pos, triggerButton, triggerButton, 0); - Application::getInstance()->mouseReleaseEvent(&mouseEvent); + application->mouseReleaseEvent(&mouseEvent); _triggerPressed[index] = false; } diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 8d118e90c4..39d6956249 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -151,6 +151,28 @@ void ApplicationOverlay::computeOculusPickRay(float x, float y, glm::vec3& direc direction = glm::normalize(rot * glm::vec3(x, y, z)); } +// Calculates the click location on the screen by taking into account any +// opened magnification windows. +void ApplicationOverlay::getClickLocation(int &x, int &y) const { + int dx; + int dy; + + //Loop through all magnification windows + for (int i = 0; i < 3; i++) { + if (_magActive[i]) { + dx = x - _magX[i]; + dy = y - _magY[i]; + //Check to see if they clicked inside a mag window + if (abs(dx) <= MAGNIFY_WIDTH * MAGNIFY_MULT && abs(dy) <= MAGNIFY_HEIGHT * MAGNIFY_MULT) { + //Move the click to the actual UI location by inverting the magnification + x = dx / MAGNIFY_MULT + _magX[i]; + y = dy / MAGNIFY_MULT + _magY[i]; + return; + } + } + } +} + // Draws the FBO texture for Oculus rift. TODO: Draw a curved texture instead of plane. void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 80c7cd1ad8..de6b376de6 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -24,13 +24,14 @@ class ApplicationOverlay { public: ApplicationOverlay(); - ~ApplicationOverlay(); void renderOverlay(bool renderToTexture = false); void displayOverlayTexture(Camera& whichCamera); void displayOverlayTextureOculus(Camera& whichCamera); void computeOculusPickRay(float x, float y, glm::vec3& direction) const; + void getClickLocation(int &x, int &y) const; + // Getters QOpenGLFramebufferObject* getFramebufferObject(); From b0e89c58101413dbc65ce5614d96d1f9a8ac9226 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 16 Jun 2014 17:34:18 -0700 Subject: [PATCH 075/102] Made magnification use only button 3 --- interface/src/ui/ApplicationOverlay.cpp | 59 ++++++++++++++++++++----- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 39d6956249..db703944c1 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -36,6 +36,9 @@ ApplicationOverlay::ApplicationOverlay() : _textureFov(DEFAULT_OCULUS_UI_ANGULAR_SIZE * RADIANS_PER_DEGREE), _crosshairTexture(0) { + _reticleActive[MOUSE] = false; + _reticleActive[LEFT_CONTROLLER] = false; + _reticleActive[RIGHT_CONTROLLER] = false; _magActive[MOUSE] = false; _magActive[LEFT_CONTROLLER] = false; _magActive[RIGHT_CONTROLLER] = false; @@ -156,14 +159,16 @@ void ApplicationOverlay::computeOculusPickRay(float x, float y, glm::vec3& direc void ApplicationOverlay::getClickLocation(int &x, int &y) const { int dx; int dy; - + const float xRange = MAGNIFY_WIDTH * MAGNIFY_MULT / 2.0f; + const float yRange = MAGNIFY_WIDTH * MAGNIFY_MULT / 2.0f; + //Loop through all magnification windows for (int i = 0; i < 3; i++) { if (_magActive[i]) { dx = x - _magX[i]; dy = y - _magY[i]; //Check to see if they clicked inside a mag window - if (abs(dx) <= MAGNIFY_WIDTH * MAGNIFY_MULT && abs(dy) <= MAGNIFY_HEIGHT * MAGNIFY_MULT) { + if (abs(dx) <= xRange && abs(dy) <= yRange) { //Move the click to the actual UI location by inverting the magnification x = dx / MAGNIFY_MULT + _magX[i]; y = dy / MAGNIFY_MULT + _magY[i]; @@ -274,6 +279,7 @@ void ApplicationOverlay::renderPointers() { } else if (application->getLastMouseMoveType() == CONTROLLER_MOVE_EVENT && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { //only render controller pointer if we aren't already rendering a mouse pointer _reticleActive[MOUSE] = false; + _magActive[MOUSE] = false; renderControllerPointers(); } glBindTexture(GL_TEXTURE_2D, 0); @@ -286,6 +292,10 @@ void ApplicationOverlay::renderControllerPointers() { QGLWidget* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); + static unsigned int pressedTime[2] = { 0, 0 }; + static bool isPressed[2] = { false, false }; + static bool stateWhenPressed[2] = { false, false }; + const HandData* handData = Application::getInstance()->getAvatar()->getHandData(); for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) { @@ -305,6 +315,24 @@ void ApplicationOverlay::renderControllerPointers() { int controllerButtons = palmData->getControllerButtons(); + //Check for if we should toggle or drag the magnification window + if (controllerButtons & BUTTON_3) { + if (isPressed[index] == false) { + //We are now dragging the window + isPressed[index] = true; + //set the pressed time in ms + pressedTime[index] = SDL_GetTicks(); + stateWhenPressed[index] = _magActive[index]; + } + } else if (isPressed[index]) { + isPressed[index] = false; + //If the button has been pressed for less than 250 ms + //then disable it. + if (SDL_GetTicks() - pressedTime[index] < 250) { + _magActive[index] = !stateWhenPressed[index]; + } + } + // Get directon relative to avatar orientation glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); @@ -318,13 +346,6 @@ void ApplicationOverlay::renderControllerPointers() { int mouseX = glWidget->width() / 2.0f + cursorRange * xAngle; int mouseY = glWidget->height() / 2.0f + cursorRange * yAngle; - // If the 2 button is pressed, we disable the magnifier for this controller - if (controllerButtons & BUTTON_2) { - _magActive[index] = false; - _magX[index] = mouseX; - _magY[index] = mouseY; - } - //If the cursor is out of the screen then don't render it if (mouseX < 0 || mouseX >= glWidget->width() || mouseY < 0 || mouseY >= glWidget->height()) { continue; @@ -340,7 +361,8 @@ void ApplicationOverlay::renderControllerPointers() { _mouseX[index] = mouseX; _mouseY[index] = mouseY; - if (controllerButtons & BUTTON_3) { + //When button 2 is pressed we drag the mag window + if (isPressed[index]) { _magActive[index] = true; _magX[index] = mouseX; _magY[index] = mouseY; @@ -437,7 +459,6 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); - const int widgetWidth = glWidget->width(); const int widgetHeight = glWidget->height(); @@ -484,6 +505,22 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) //Top Right dist = sqrt(rX * rX + tY * tY); float trZ = sqrt(1.0f - dist * dist); + glDisable(GL_TEXTURE_2D); + glLineWidth(1.0f); + //Outer Line + glBegin(GL_LINE_STRIP); + glColor3f(1.0f, 0.0f, 0.0f); + + glVertex3f(lX, tY, -tlZ); + glVertex3f(rX, tY, -trZ); + glVertex3f(rX, bY, -brZ); + glVertex3f(lX, bY, -blZ); + glVertex3f(lX, tY, -tlZ); + + glColor3f(1.0f, 1.0f, 1.0f); + + glEnd(); + glEnable(GL_TEXTURE_2D); glBegin(GL_QUADS); From 5843425db90a5269ba47d46db00854de2e54f630 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 17 Jun 2014 09:18:58 -0700 Subject: [PATCH 076/102] Fix scripts being loaded on start even if they don't exist Application::loadScript should not store a reference to a script until it has been successfully loaded. Previously if a script didn't exist it would be "loaded" and show up in the running scripts window, but wouldn't have been successfully loaded, and wouldn't be running anything at all. --- interface/src/Application.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6b44503af4..bc6841e626 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3523,12 +3523,13 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript } else { // start the script on a new thread... scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface); - _scriptEnginesHash.insert(scriptURLString, scriptEngine); if (!scriptEngine->hasScript()) { qDebug() << "Application::loadScript(), script failed to load..."; return NULL; } + + _scriptEnginesHash.insert(scriptURLString, scriptEngine); _runningScriptsWidget->setRunningScripts(getRunningScripts()); } From 3052996a9e62e9bac2824d1765923114ba9ec451 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Tue, 17 Jun 2014 10:37:15 -0700 Subject: [PATCH 077/102] Pressing trigger and bumper at same time toggles UI --- interface/src/Application.cpp | 9 +++- interface/src/ui/ApplicationOverlay.cpp | 72 ++++++++++++++++++++++--- interface/src/ui/ApplicationOverlay.h | 6 ++- 3 files changed, 78 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6b44503af4..aed511601e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -654,7 +654,14 @@ void Application::paintGL() { { PerformanceTimer perfTimer("paintGL/renderOverlay"); - _applicationOverlay.renderOverlay(); + //If alpha is 1, we can render directly to the screen. + if (_applicationOverlay.getAlpha() == 1.0f) { + _applicationOverlay.renderOverlay(); + } else { + //Render to to texture so we can fade it + _applicationOverlay.renderOverlay(true); + _applicationOverlay.displayOverlayTexture(); + } } } diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index db703944c1..2f94776046 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -20,6 +20,8 @@ #include "ui/Stats.h" +const float FADE_SPEED = 0.02f; + // Fast helper functions inline float max(float a, float b) { return (a > b) ? a : b; @@ -34,7 +36,9 @@ ApplicationOverlay::ApplicationOverlay() : _oculusAngle(65.0f * RADIANS_PER_DEGREE), _distance(0.5f), _textureFov(DEFAULT_OCULUS_UI_ANGULAR_SIZE * RADIANS_PER_DEGREE), - _crosshairTexture(0) { + _crosshairTexture(0), + _alpha(1.0f), + _active(true) { _reticleActive[MOUSE] = false; _reticleActive[LEFT_CONTROLLER] = false; @@ -65,6 +69,20 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { QGLWidget* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); + //Handle fadeing and deactivation/activation of UI + printf("%f\n", _alpha); + if (_active) { + _alpha += FADE_SPEED; + if (_alpha > 1.0f) { + _alpha = 1.0f; + } + } else { + _alpha -= FADE_SPEED; + if (_alpha <= 0.0f) { + _alpha = 0.0f; + } + } + if (renderToTexture) { getFramebufferObject()->bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -72,6 +90,7 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + // Render 2D overlay glMatrixMode(GL_PROJECTION); glPushMatrix(); @@ -109,7 +128,11 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { } // Draws the FBO texture for the screen -void ApplicationOverlay::displayOverlayTexture(Camera& whichCamera) { +void ApplicationOverlay::displayOverlayTexture() { + + if (_alpha == 0.0f) { + return; + } Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); @@ -125,13 +148,16 @@ void ApplicationOverlay::displayOverlayTexture(Camera& whichCamera) { gluOrtho2D(0, glWidget->width(), glWidget->height(), 0); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); + glEnable(GL_BLEND); glBegin(GL_QUADS); + glColor4f(1.0f, 1.0f, 1.0f, _alpha); glTexCoord2f(0, 0); glVertex2i(0, glWidget->height()); glTexCoord2f(1, 0); glVertex2i(glWidget->width(), glWidget->height()); glTexCoord2f(1, 1); glVertex2i(glWidget->width(), 0); glTexCoord2f(0, 1); glVertex2i(0, 0); glEnd(); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glPopMatrix(); glDisable(GL_TEXTURE_2D); @@ -181,6 +207,10 @@ void ApplicationOverlay::getClickLocation(int &x, int &y) const { // Draws the FBO texture for Oculus rift. TODO: Draw a curved texture instead of plane. void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { + if (_alpha == 0.0f) { + return; + } + Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); @@ -214,8 +244,6 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { glTranslatef(pos.x, pos.y, pos.z); glRotatef(glm::degrees(glm::angle(rot)), axis.x, axis.y, axis.z); - glColor3f(1.0f, 1.0f, 1.0f); - glDepthMask(GL_TRUE); glEnable(GL_ALPHA_TEST); @@ -232,6 +260,8 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { glDepthMask(GL_FALSE); glDisable(GL_ALPHA_TEST); + glColor4f(1.0f, 1.0f, 1.0f, _alpha); + renderTexturedHemisphere(); renderControllerPointersOculus(); @@ -245,6 +275,8 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); glEnable(GL_LIGHTING); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + } //Renders optional pointers @@ -292,9 +324,12 @@ void ApplicationOverlay::renderControllerPointers() { QGLWidget* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); + //Static variables used for storing controller state static unsigned int pressedTime[2] = { 0, 0 }; static bool isPressed[2] = { false, false }; static bool stateWhenPressed[2] = { false, false }; + static bool triggerPressed[2] = { false, false }; + static bool bumperPressed[2] = { false, false }; const HandData* handData = Application::getInstance()->getAvatar()->getHandData(); @@ -333,6 +368,29 @@ void ApplicationOverlay::renderControllerPointers() { } } + //Check for UI active toggle + if (palmData->getTrigger() == 1.0f) { + if (!triggerPressed[index]) { + if (bumperPressed[index]) { + _active = !_active; + } + triggerPressed[index] = true; + } + } else { + triggerPressed[index] = false; + } + if ((controllerButtons & BUTTON_FWD)) { + if (!bumperPressed[index]) { + if (triggerPressed[index]) { + _active = !_active; + } + bumperPressed[index] = true; + } + } else { + bumperPressed[index] = false; + } + + // Get directon relative to avatar orientation glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); @@ -440,7 +498,7 @@ void ApplicationOverlay::renderControllerPointersOculus() { glBegin(GL_QUADS); - glColor3f(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2]); + glColor4f(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2], _alpha); glTexCoord2f(0.0f, 0.0f); glVertex3f(lX, tY, -tlZ); glTexCoord2f(1.0f, 0.0f); glVertex3f(rX, tY, -trZ); @@ -509,7 +567,7 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) glLineWidth(1.0f); //Outer Line glBegin(GL_LINE_STRIP); - glColor3f(1.0f, 0.0f, 0.0f); + glColor4f(1.0f, 0.0f, 0.0f, _alpha); glVertex3f(lX, tY, -tlZ); glVertex3f(rX, tY, -trZ); @@ -517,7 +575,7 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) glVertex3f(lX, bY, -blZ); glVertex3f(lX, tY, -tlZ); - glColor3f(1.0f, 1.0f, 1.0f); + glColor4f(1.0f, 1.0f, 1.0f, _alpha); glEnd(); glEnable(GL_TEXTURE_2D); diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index de6b376de6..f8893420ac 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -27,13 +27,14 @@ public: ~ApplicationOverlay(); void renderOverlay(bool renderToTexture = false); - void displayOverlayTexture(Camera& whichCamera); + void displayOverlayTexture(); void displayOverlayTextureOculus(Camera& whichCamera); void computeOculusPickRay(float x, float y, glm::vec3& direction) const; void getClickLocation(int &x, int &y) const; // Getters QOpenGLFramebufferObject* getFramebufferObject(); + float getAlpha() const { return _alpha; } private: // Interleaved vertex data @@ -67,6 +68,9 @@ private: int _magX[3]; int _magY[3]; + float _alpha; + bool _active; + GLuint _crosshairTexture; }; From f367e1840d20e9c758c5b41a9e01d65da6b22cbd Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 17 Jun 2014 10:37:35 -0700 Subject: [PATCH 078/102] Added a detached mode by default in inpect.js --- examples/inspect.js | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/examples/inspect.js b/examples/inspect.js index af63957ec3..b292d5f609 100644 --- a/examples/inspect.js +++ b/examples/inspect.js @@ -34,6 +34,7 @@ var noMode = 0; var orbitMode = 1; var radialMode = 2; var panningMode = 3; +var detachedMode = 4; var mode = noMode; @@ -48,6 +49,9 @@ var radius = 0.0; var azimuth = 0.0; var altitude = 0.0; +var avatarPosition; +var avatarOrientation; + function handleRadialMode(dx, dy) { azimuth += dx / AZIMUTH_RATE; @@ -108,7 +112,7 @@ function restoreCameraState() { } function handleModes() { - var newMode = noMode; + var newMode = (mode == noMode) ? noMode : detachedMode; if (alt) { if (control) { if (shift) { @@ -121,6 +125,22 @@ function handleModes() { } } + // if entering detachMode + if (newMode == detachedMode && mode != detachedMode) { + avatarPosition = MyAvatar.position; + avatarOrientation = MyAvatar.orientation; + } + // if leaving detachMode + if (mode == detachedMode && newMode == detachedMode && + (avatarPosition.x != MyAvatar.position.x || + avatarPosition.y != MyAvatar.position.y || + avatarPosition.z != MyAvatar.position.z || + avatarOrientation.x != MyAvatar.orientation.x || + avatarOrientation.y != MyAvatar.orientation.y || + avatarOrientation.z != MyAvatar.orientation.z || + avatarOrientation.w != MyAvatar.orientation.w)) { + newMode = noMode; + } // if leaving noMode if (mode == noMode && newMode != noMode) { saveCameraState(); @@ -252,6 +272,10 @@ function mouseMoveEvent(event) { } } +function update() { + handleModes(); +} + function scriptEnding() { if (mode != noMode) { restoreCameraState(); @@ -265,4 +289,5 @@ Controller.mousePressEvent.connect(mousePressEvent); Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Controller.mouseMoveEvent.connect(mouseMoveEvent); +Script.update.connect(update); Script.scriptEnding.connect(scriptEnding); \ No newline at end of file From dc663dedf8bec14572097c0e5338035fc7831d9e Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Tue, 17 Jun 2014 10:45:59 -0700 Subject: [PATCH 079/102] Fixed compiler warnings --- interface/src/ui/ApplicationOverlay.cpp | 7 ------- interface/src/ui/ApplicationOverlay.h | 2 -- 2 files changed, 9 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 2f94776046..4b0592d805 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -33,8 +33,6 @@ inline float min(float a, float b) { ApplicationOverlay::ApplicationOverlay() : _framebufferObject(NULL), - _oculusAngle(65.0f * RADIANS_PER_DEGREE), - _distance(0.5f), _textureFov(DEFAULT_OCULUS_UI_ANGULAR_SIZE * RADIANS_PER_DEGREE), _crosshairTexture(0), _alpha(1.0f), @@ -70,7 +68,6 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { MyAvatar* myAvatar = application->getAvatar(); //Handle fadeing and deactivation/activation of UI - printf("%f\n", _alpha); if (_active) { _alpha += FADE_SPEED; if (_alpha > 1.0f) { @@ -213,7 +210,6 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { Application* application = Application::getInstance(); - QGLWidget* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); @@ -282,9 +278,6 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { //Renders optional pointers void ApplicationOverlay::renderPointers() { Application* application = Application::getInstance(); - // Render a crosshair over the mouse when in Oculus - int mouseX = application->getMouseX(); - int mouseY = application->getMouseY(); //lazily load crosshair texture if (_crosshairTexture == 0) { diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index f8893420ac..172b16518c 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -57,8 +57,6 @@ private: QOpenGLFramebufferObject* _framebufferObject; float _trailingAudioLoudness; - float _oculusAngle; - float _distance; float _textureFov; // 0 = Mouse, 1 = Left Controller, 2 = Right Controller bool _reticleActive[3]; From e75340f8ceb2b966e5ae7c1b8acf346ea6490d3a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 17 Jun 2014 10:48:29 -0700 Subject: [PATCH 080/102] Add PlaneShape::getNormal() method. --- libraries/shared/src/PlaneShape.cpp | 4 ++++ libraries/shared/src/PlaneShape.h | 1 + 2 files changed, 5 insertions(+) diff --git a/libraries/shared/src/PlaneShape.cpp b/libraries/shared/src/PlaneShape.cpp index a8b4468c93..0617feb863 100644 --- a/libraries/shared/src/PlaneShape.cpp +++ b/libraries/shared/src/PlaneShape.cpp @@ -30,6 +30,10 @@ PlaneShape::PlaneShape(const glm::vec4& coefficients) : } } +glm::vec3 PlaneShape::getNormal() const { + return _rotation * UNROTATED_NORMAL; +} + glm::vec4 PlaneShape::getCoefficients() const { glm::vec3 normal = _rotation * UNROTATED_NORMAL; return glm::vec4(normal.x, normal.y, normal.z, -glm::dot(normal, _position)); diff --git a/libraries/shared/src/PlaneShape.h b/libraries/shared/src/PlaneShape.h index 524d53ec73..24a3f1a2bc 100644 --- a/libraries/shared/src/PlaneShape.h +++ b/libraries/shared/src/PlaneShape.h @@ -18,6 +18,7 @@ class PlaneShape : public Shape { public: PlaneShape(const glm::vec4& coefficients = glm::vec4(0.0f, 1.0f, 0.0f, 0.0f)); + glm::vec3 getNormal() const; glm::vec4 getCoefficients() const; }; From ab3d582d79ca08001ef87cdf486ef3d28e63b5e5 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 17 Jun 2014 10:49:14 -0700 Subject: [PATCH 081/102] Add ray intersection tests against most shapes. --- libraries/shared/src/ShapeCollider.cpp | 81 ++++++++++++++++++++++++ libraries/shared/src/ShapeCollider.h | 88 +++++++++++++++----------- 2 files changed, 132 insertions(+), 37 deletions(-) diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index 7c29fbae00..24d7fdb01a 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -765,5 +765,86 @@ bool capsuleAACube(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, fl return sphereAACube(nearestApproach, capsuleA->getRadius(), cubeCenter, cubeSide, collisions); } +bool findRayIntersectionWithShapes(const QVector shapes, const glm::vec3& rayStart, const glm::vec3& rayDirection, float& minDistance) { + float hitDistance = FLT_MAX; + int numShapes = shapes.size(); + for (int i = 0; i < numShapes; ++i) { + Shape* shape = shapes.at(i); + if (shape) { + float distance; + if (findRayIntersectionWithShape(shape, rayStart, rayDirection, distance)) { + if (distance < hitDistance) { + hitDistance = distance; + } + } + } + } + if (hitDistance < FLT_MAX) { + minDistance = hitDistance; + } + return false; +} + +bool findRayIntersectionWithShape(const Shape* shape, const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) { + // NOTE: rayDirection is assumed to be normalized + int typeA = shape->getType(); + if (typeA == Shape::SPHERE_SHAPE) { + const SphereShape* sphere = static_cast(shape); + glm::vec3 sphereCenter = sphere->getPosition(); + float r2 = sphere->getRadius() * sphere->getRadius(); // r2 = radius^2 + + // compute closest approach (CA) + float a = glm::dot(sphere->getPosition() - rayStart, rayDirection); // a = distance from ray-start to CA + float b2 = glm::distance2(sphereCenter, rayStart + a * rayDirection); // b2 = squared distance from sphere-center to CA + if (b2 > r2) { + // ray does not hit sphere + return false; + } + float c = sqrtf(r2 - b2); // c = distance from CA to sphere surface along rayDirection + float d2 = glm::distance2(rayStart, sphereCenter); // d2 = squared distance from sphere-center to ray-start + if (a < 0.0f) { + // ray points away from sphere-center + if (d2 > r2) { + // ray starts outside sphere + return false; + } + // ray starts inside sphere + distance = c + a; + } else if (d2 > r2) { + // ray starts outside sphere + distance = a - c; + } else { + // ray starts inside sphere + distance = a + c; + } + return true; + } else if (typeA == Shape::CAPSULE_SHAPE) { + const CapsuleShape* capsule = static_cast(shape); + float radius = capsule->getRadius(); + glm::vec3 capsuleStart, capsuleEnd; + capsule->getStartPoint(capsuleStart); + capsule->getEndPoint(capsuleEnd); + // NOTE: findRayCapsuleIntersection returns 'true' with distance = 0 when rayStart is inside capsule. + // TODO: implement the raycast to return inside surface intersection for the internal rayStart. + return findRayCapsuleIntersection(rayStart, rayDirection, capsuleStart, capsuleEnd, radius, distance); + } else if (typeA == Shape::PLANE_SHAPE) { + const PlaneShape* plane = static_cast(shape); + glm::vec3 n = plane->getNormal(); + glm::vec3 P = plane->getPosition(); + float denominator = glm::dot(n, rayDirection); + if (fabsf(denominator) < EPSILON) { + // line is parallel to plane + return glm::dot(P - rayStart, n) < EPSILON; + } else { + float d = glm::dot(P - rayStart, n) / denominator; + if (d > 0.0f) { + // ray points toward plane + distance = d; + return true; + } + } + } + return false; +} } // namespace ShapeCollider diff --git a/libraries/shared/src/ShapeCollider.h b/libraries/shared/src/ShapeCollider.h index 9e83e31571..308a8cf10b 100644 --- a/libraries/shared/src/ShapeCollider.h +++ b/libraries/shared/src/ShapeCollider.h @@ -21,8 +21,8 @@ namespace ShapeCollider { - /// \param shapeA pointer to first shape - /// \param shapeB pointer to second shape + /// \param shapeA pointer to first shape (cannot be NULL) + /// \param shapeB pointer to second shape (cannot be NULL) /// \param collisions[out] collision details /// \return true if shapes collide bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions); @@ -33,123 +33,137 @@ namespace ShapeCollider { /// \return true if any shapes collide bool collideShapesCoarse(const QVector& shapesA, const QVector& shapesB, CollisionInfo& collision); - /// \param shapeA a pointer to a shape + /// \param shapeA a pointer to a shape (cannot be NULL) /// \param cubeCenter center of cube /// \param cubeSide lenght of side of cube /// \param collisions[out] average collision details /// \return true if shapeA collides with axis aligned cube bool collideShapeWithAACube(const Shape* shapeA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions); - /// \param sphereA pointer to first shape - /// \param sphereB pointer to second shape + /// \param sphereA pointer to first shape (cannot be NULL) + /// \param sphereB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool sphereSphere(const SphereShape* sphereA, const SphereShape* sphereB, CollisionList& collisions); - /// \param sphereA pointer to first shape - /// \param capsuleB pointer to second shape + /// \param sphereA pointer to first shape (cannot be NULL) + /// \param capsuleB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, CollisionList& collisions); - /// \param sphereA pointer to first shape - /// \param planeB pointer to second shape + /// \param sphereA pointer to first shape (cannot be NULL) + /// \param planeB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool spherePlane(const SphereShape* sphereA, const PlaneShape* planeB, CollisionList& collisions); - /// \param capsuleA pointer to first shape - /// \param sphereB pointer to second shape + /// \param capsuleA pointer to first shape (cannot be NULL) + /// \param sphereB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, CollisionList& collisions); - /// \param capsuleA pointer to first shape - /// \param capsuleB pointer to second shape + /// \param capsuleA pointer to first shape (cannot be NULL) + /// \param capsuleB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB, CollisionList& collisions); - /// \param capsuleA pointer to first shape - /// \param planeB pointer to second shape + /// \param capsuleA pointer to first shape (cannot be NULL) + /// \param planeB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool capsulePlane(const CapsuleShape* capsuleA, const PlaneShape* planeB, CollisionList& collisions); - /// \param planeA pointer to first shape - /// \param sphereB pointer to second shape + /// \param planeA pointer to first shape (cannot be NULL) + /// \param sphereB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool planeSphere(const PlaneShape* planeA, const SphereShape* sphereB, CollisionList& collisions); - /// \param planeA pointer to first shape - /// \param capsuleB pointer to second shape + /// \param planeA pointer to first shape (cannot be NULL) + /// \param capsuleB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool planeCapsule(const PlaneShape* planeA, const CapsuleShape* capsuleB, CollisionList& collisions); - /// \param planeA pointer to first shape - /// \param planeB pointer to second shape + /// \param planeA pointer to first shape (cannot be NULL) + /// \param planeB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool planePlane(const PlaneShape* planeA, const PlaneShape* planeB, CollisionList& collisions); - /// \param sphereA pointer to first shape - /// \param listB pointer to second shape + /// \param sphereA pointer to first shape (cannot be NULL) + /// \param listB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool sphereList(const SphereShape* sphereA, const ListShape* listB, CollisionList& collisions); - /// \param capuleA pointer to first shape - /// \param listB pointer to second shape + /// \param capuleA pointer to first shape (cannot be NULL) + /// \param listB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool capsuleList(const CapsuleShape* capsuleA, const ListShape* listB, CollisionList& collisions); - /// \param planeA pointer to first shape - /// \param listB pointer to second shape + /// \param planeA pointer to first shape (cannot be NULL) + /// \param listB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool planeList(const PlaneShape* planeA, const ListShape* listB, CollisionList& collisions); - /// \param listA pointer to first shape - /// \param sphereB pointer to second shape + /// \param listA pointer to first shape (cannot be NULL) + /// \param sphereB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool listSphere(const ListShape* listA, const SphereShape* sphereB, CollisionList& collisions); - /// \param listA pointer to first shape - /// \param capsuleB pointer to second shape + /// \param listA pointer to first shape (cannot be NULL) + /// \param capsuleB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool listCapsule(const ListShape* listA, const CapsuleShape* capsuleB, CollisionList& collisions); - /// \param listA pointer to first shape - /// \param planeB pointer to second shape + /// \param listA pointer to first shape (cannot be NULL) + /// \param planeB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool listPlane(const ListShape* listA, const PlaneShape* planeB, CollisionList& collisions); - /// \param listA pointer to first shape - /// \param capsuleB pointer to second shape + /// \param listA pointer to first shape (cannot be NULL) + /// \param capsuleB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide bool listList(const ListShape* listA, const ListShape* listB, CollisionList& collisions); - /// \param sphereA pointer to sphere + /// \param sphereA pointer to sphere (cannot be NULL) /// \param cubeCenter center of cube /// \param cubeSide lenght of side of cube /// \param[out] collisions where to append collision details /// \return true if sphereA collides with axis aligned cube bool sphereAACube(const SphereShape* sphereA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions); - /// \param capsuleA pointer to capsule + /// \param capsuleA pointer to capsule (cannot be NULL) /// \param cubeCenter center of cube /// \param cubeSide lenght of side of cube /// \param[out] collisions where to append collision details /// \return true if capsuleA collides with axis aligned cube bool capsuleAACube(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions); + /// \param shapes list of pointers to shapes (shape pointers may be NULL) + /// \param startPoint beginning of ray + /// \param direction direction of ray + /// \param minDistance[out] shortest distance to intersection of ray with a shapes + /// \return true if ray hits any shape in shapes + bool findRayIntersectionWithShapes(const QVector shapes, const glm::vec3& startPoint, const glm::vec3& direction, float& minDistance); + + /// \param shapeA pointer to shape (cannot be NULL) + /// \param startPoint beginning of ray + /// \param direction direction of ray + /// \param distance[out] distance to intersection of shape and ray + /// \return true if ray hits shapeA + bool findRayIntersectionWithShape(const Shape* shapeA, const glm::vec3& startPoint, const glm::vec3& direction, float& distance); + } // namespace ShapeCollider #endif // hifi_ShapeCollider_h From 1e200c3b9ce4d366611d79eed656b4309406c833 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 17 Jun 2014 19:49:35 +0200 Subject: [PATCH 082/102] The balance display didn't get over ~21.1 because the parsed json object int32 is maximal 2147483647, fixed this by parsing it from json to double. --- libraries/networking/src/DataServerAccountInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index c60a17e0d8..507c085d26 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -85,7 +85,7 @@ void DataServerAccountInfo::setBalance(qint64 balance) { void DataServerAccountInfo::setBalanceFromJSON(const QJsonObject& jsonObject) { if (jsonObject["status"].toString() == "success") { - qint64 balanceInSatoshis = jsonObject["data"].toObject()["wallet"].toObject()["balance"].toInt(); + qint64 balanceInSatoshis = jsonObject["data"].toObject()["wallet"].toObject()["balance"].toDouble(); setBalance(balanceInSatoshis); } } From fa6aed3e01a9d004cca5de4fa12c7538f37c35e9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 17 Jun 2014 10:49:53 -0700 Subject: [PATCH 083/102] Add unit tests for ray intersections with shapes. --- tests/physics/src/ShapeColliderTests.cpp | 408 +++++++++++++++++++++++ tests/physics/src/ShapeColliderTests.h | 8 + 2 files changed, 416 insertions(+) diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index 7b3d956065..3387ba6aba 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -11,6 +11,7 @@ //#include #include +#include #include #include @@ -897,6 +898,405 @@ void ShapeColliderTests::sphereMissesAACube() { } } +void ShapeColliderTests::rayHitsSphere() { + float startDistance = 3.0f; + glm::vec3 rayStart(-startDistance, 0.0f, 0.0f); + glm::vec3 rayDirection(1.0f, 0.0f, 0.0f); + + float radius = 1.0f; + glm::vec3 center(0.0f); + + SphereShape sphere(radius, center); + + // very simple ray along xAxis + { + float distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should intersect sphere" << std::endl; + } + + float expectedDistance = startDistance - radius; + float relativeError = fabsf(distance - expectedDistance) / startDistance; + if (relativeError > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray sphere intersection distance error = " << relativeError << std::endl; + } + } + + // ray along a diagonal axis + { + rayStart = glm::vec3(startDistance, startDistance, 0.0f); + rayDirection = - glm::normalize(rayStart); + + float distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should intersect sphere" << std::endl; + } + + float expectedDistance = SQUARE_ROOT_OF_2 * startDistance - radius; + float relativeError = fabsf(distance - expectedDistance) / startDistance; + if (relativeError > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray sphere intersection distance error = " << relativeError << std::endl; + } + } + + // rotated and displaced ray and sphere + { + startDistance = 7.41f; + radius = 3.917f; + + glm::vec3 axis = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f)); + glm::quat rotation = glm::angleAxis(0.987654321f, axis); + glm::vec3 translation(35.7f, 2.46f, -1.97f); + + glm::vec3 unrotatedRayDirection(-1.0f, 0.0f, 0.0f); + glm::vec3 untransformedRayStart(startDistance, 0.0f, 0.0f); + + rayStart = rotation * (untransformedRayStart + translation); + rayDirection = rotation * unrotatedRayDirection; + + sphere.setRadius(radius); + sphere.setPosition(rotation * translation); + + float distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should intersect sphere" << std::endl; + } + + float expectedDistance = startDistance - radius; + float relativeError = fabsf(distance - expectedDistance) / startDistance; + if (relativeError > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray sphere intersection distance error = " << relativeError << std::endl; + } + } +} + +void ShapeColliderTests::rayBarelyHitsSphere() { + float radius = 1.0f; + glm::vec3 center(0.0f); + float delta = 2.0f * EPSILON; + + float startDistance = 3.0f; + glm::vec3 rayStart(-startDistance, radius - delta, 0.0f); + glm::vec3 rayDirection(1.0f, 0.0f, 0.0f); + + SphereShape sphere(radius, center); + + // very simple ray along xAxis + float distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely hit sphere" << std::endl; + } + + // translate and rotate the whole system... + glm::vec3 axis = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f)); + glm::quat rotation = glm::angleAxis(0.987654321f, axis); + glm::vec3 translation(35.7f, 2.46f, -1.97f); + + rayStart = rotation * (rayStart + translation); + rayDirection = rotation * rayDirection; + sphere.setPosition(rotation * translation); + + // ...and test again + distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely hit sphere" << std::endl; + } +} + + +void ShapeColliderTests::rayBarelyMissesSphere() { + // same as the barely-hits case, but this time we move the ray away from sphere + float radius = 1.0f; + glm::vec3 center(0.0f); + float delta = 2.0f * EPSILON; + + float startDistance = 3.0f; + glm::vec3 rayStart(-startDistance, radius + delta, 0.0f); + glm::vec3 rayDirection(1.0f, 0.0f, 0.0f); + + SphereShape sphere(radius, center); + + // very simple ray along xAxis + float distance = FLT_MAX; + if (ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely miss sphere" << std::endl; + } + if (distance != FLT_MAX) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + } + + // translate and rotate the whole system... + glm::vec3 axis = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f)); + glm::quat rotation = glm::angleAxis(0.987654321f, axis); + glm::vec3 translation(35.7f, 2.46f, -1.97f); + + rayStart = rotation * (rayStart + translation); + rayDirection = rotation * rayDirection; + sphere.setPosition(rotation * translation); + + // ...and test again + distance = FLT_MAX; + if (ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely miss sphere" << std::endl; + } + if (distance != FLT_MAX) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + } +} + +void ShapeColliderTests::rayHitsCapsule() { + float startDistance = 3.0f; + float radius = 1.0f; + float halfHeight = 2.0f; + glm::vec3 center(0.0f); + CapsuleShape capsule(radius, halfHeight); + + { // simple test along xAxis + // toward capsule center + glm::vec3 rayStart(startDistance, 0.0f, 0.0f); + glm::vec3 rayDirection(-1.0f, 0.0f, 0.0f); + float distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; + } + float expectedDistance = startDistance - radius; + float relativeError = fabsf(distance - expectedDistance) / startDistance; + if (relativeError > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; + } + + // toward top of cylindrical wall + rayStart.y = halfHeight; + distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; + } + relativeError = fabsf(distance - expectedDistance) / startDistance; + if (relativeError > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; + } + + // toward top cap + float delta = 2.0f * EPSILON; + rayStart.y = halfHeight + delta; + distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; + } + relativeError = fabsf(distance - expectedDistance) / startDistance; + if (relativeError > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; + } + + const float EDGE_CASE_SLOP_FACTOR = 20.0f; + + // toward tip of top cap + rayStart.y = halfHeight + radius - delta; + distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; + } + expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine + relativeError = fabsf(distance - expectedDistance) / startDistance; + // for edge cases we allow a LOT of error + if (relativeError > EDGE_CASE_SLOP_FACTOR * EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; + } + + // toward tip of bottom cap + rayStart.y = - halfHeight - radius + delta; + distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; + } + expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine + relativeError = fabsf(distance - expectedDistance) / startDistance; + // for edge cases we allow a LOT of error + if (relativeError > EDGE_CASE_SLOP_FACTOR * EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; + } + + // toward edge of capsule cylindrical face + rayStart.y = 0.0f; + rayStart.z = radius - delta; + distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; + } + expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine + relativeError = fabsf(distance - expectedDistance) / startDistance; + // for edge cases we allow a LOT of error + if (relativeError > EDGE_CASE_SLOP_FACTOR * EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; + } + } + // TODO: test at steep angles near cylinder/cap junction +} + +void ShapeColliderTests::rayMissesCapsule() { + // same as edge case hit tests, but shifted in the opposite direction + float startDistance = 3.0f; + float radius = 1.0f; + float halfHeight = 2.0f; + glm::vec3 center(0.0f); + CapsuleShape capsule(radius, halfHeight); + + { // simple test along xAxis + // toward capsule center + glm::vec3 rayStart(startDistance, 0.0f, 0.0f); + glm::vec3 rayDirection(-1.0f, 0.0f, 0.0f); + float delta = 2.0f * EPSILON; + + // over top cap + rayStart.y = halfHeight + radius + delta; + float distance = FLT_MAX; + if (ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss capsule" << std::endl; + } + if (distance != FLT_MAX) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + } + + // below bottom cap + rayStart.y = - halfHeight - radius - delta; + distance = FLT_MAX; + if (ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss capsule" << std::endl; + } + if (distance != FLT_MAX) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + } + + // past edge of capsule cylindrical face + rayStart.y = 0.0f; + rayStart.z = radius + delta; + distance = FLT_MAX; + if (ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss capsule" << std::endl; + } + if (distance != FLT_MAX) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + } + } + // TODO: test at steep angles near edge +} + +void ShapeColliderTests::rayHitsPlane() { + // make a simple plane + float planeDistanceFromOrigin = 3.579; + glm::vec3 planePosition(0.0f, planeDistanceFromOrigin, 0.0f); + PlaneShape plane; + plane.setPosition(planePosition); + + // make a simple ray + float startDistance = 1.234f; + glm::vec3 rayStart(-startDistance, 0.0f, 0.0f); + glm::vec3 rayDirection = glm::normalize(glm::vec3(1.0f, 1.0f, 1.0f)); + + float distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit plane" << std::endl; + } + + float expectedDistance = SQUARE_ROOT_OF_3 * planeDistanceFromOrigin; + float relativeError = fabsf(distance - expectedDistance) / planeDistanceFromOrigin; + if (relativeError > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray plane intersection distance error = " << relativeError << std::endl; + } + + // rotate the whole system and try again + float angle = 37.8f; + glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) ); + glm::quat rotation = glm::angleAxis(angle, axis); + + plane.setPosition(rotation * planePosition); + plane.setRotation(rotation); + rayStart = rotation * rayStart; + rayDirection = rotation * rayDirection; + + distance = FLT_MAX; + if (!ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit plane" << std::endl; + } + + expectedDistance = SQUARE_ROOT_OF_3 * planeDistanceFromOrigin; + relativeError = fabsf(distance - expectedDistance) / planeDistanceFromOrigin; + if (relativeError > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray plane intersection distance error = " << relativeError << std::endl; + } +} + +void ShapeColliderTests::rayMissesPlane() { + // make a simple plane + float planeDistanceFromOrigin = 3.579; + glm::vec3 planePosition(0.0f, planeDistanceFromOrigin, 0.0f); + PlaneShape plane; + plane.setPosition(planePosition); + + { // parallel rays should miss + float startDistance = 1.234f; + glm::vec3 rayStart(-startDistance, 0.0f, 0.0f); + glm::vec3 rayDirection = glm::normalize(glm::vec3(-1.0f, 0.0f, -1.0f)); + + float distance = FLT_MAX; + if (ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; + } + if (distance != FLT_MAX) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + } + + // rotate the whole system and try again + float angle = 37.8f; + glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) ); + glm::quat rotation = glm::angleAxis(angle, axis); + + plane.setPosition(rotation * planePosition); + plane.setRotation(rotation); + rayStart = rotation * rayStart; + rayDirection = rotation * rayDirection; + + distance = FLT_MAX; + if (ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; + } + if (distance != FLT_MAX) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + } + } + + { // make a simple ray that points away from plane + float startDistance = 1.234f; + glm::vec3 rayStart(-startDistance, 0.0f, 0.0f); + glm::vec3 rayDirection = glm::normalize(glm::vec3(-1.0f, -1.0f, -1.0f)); + + float distance = FLT_MAX; + if (ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; + } + if (distance != FLT_MAX) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + } + + // rotate the whole system and try again + float angle = 37.8f; + glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) ); + glm::quat rotation = glm::angleAxis(angle, axis); + + plane.setPosition(rotation * planePosition); + plane.setRotation(rotation); + rayStart = rotation * rayStart; + rayDirection = rotation * rayDirection; + + distance = FLT_MAX; + if (ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; + } + if (distance != FLT_MAX) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + } + } +} void ShapeColliderTests::runAllTests() { sphereMissesSphere(); @@ -911,4 +1311,12 @@ void ShapeColliderTests::runAllTests() { sphereTouchesAACubeFaces(); sphereTouchesAACubeEdges(); sphereMissesAACube(); + + rayHitsSphere(); + rayBarelyHitsSphere(); + rayBarelyMissesSphere(); + rayHitsCapsule(); + rayMissesCapsule(); + rayHitsPlane(); + rayMissesPlane(); } diff --git a/tests/physics/src/ShapeColliderTests.h b/tests/physics/src/ShapeColliderTests.h index b51c48a61e..fd9f1f9706 100644 --- a/tests/physics/src/ShapeColliderTests.h +++ b/tests/physics/src/ShapeColliderTests.h @@ -27,6 +27,14 @@ namespace ShapeColliderTests { void sphereTouchesAACubeEdges(); void sphereMissesAACube(); + void rayHitsSphere(); + void rayBarelyHitsSphere(); + void rayBarelyMissesSphere(); + void rayHitsCapsule(); + void rayMissesCapsule(); + void rayHitsPlane(); + void rayMissesPlane(); + void runAllTests(); } From 7bb8e4d5710136bab44e071d401faf20291eee8f Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Tue, 17 Jun 2014 11:20:02 -0700 Subject: [PATCH 084/102] Animated magnification opening and closing --- interface/src/ui/ApplicationOverlay.cpp | 53 ++++++++++++++++--------- interface/src/ui/ApplicationOverlay.h | 3 +- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 4b0592d805..8a0ede2d1e 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -20,7 +20,10 @@ #include "ui/Stats.h" -const float FADE_SPEED = 0.02f; +// Used to fade the UI +const float FADE_SPEED = 0.08f; +// Used to animate the magnification windows +const float MAG_SPEED = 0.08f; // Fast helper functions inline float max(float a, float b) { @@ -38,12 +41,9 @@ ApplicationOverlay::ApplicationOverlay() : _alpha(1.0f), _active(true) { - _reticleActive[MOUSE] = false; - _reticleActive[LEFT_CONTROLLER] = false; - _reticleActive[RIGHT_CONTROLLER] = false; - _magActive[MOUSE] = false; - _magActive[LEFT_CONTROLLER] = false; - _magActive[RIGHT_CONTROLLER] = false; + memset(_reticleActive, 0, sizeof(_reticleActive)); + memset(_magActive, 0, sizeof(_reticleActive)); + memset(_magSizeMult, 0, sizeof(_magSizeMult)); } ApplicationOverlay::~ApplicationOverlay() { @@ -245,12 +245,24 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.01f); - //Draw the magnifiers + //Update and draw the magnifiers for (int i = 0; i < 3; i++) { if (_magActive[i]) { - renderMagnifier(_magX[i], _magY[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) { + renderMagnifier(_magX[i], _magY[i], _magSizeMult[i]); + } } glDepthMask(GL_FALSE); @@ -505,7 +517,7 @@ void ApplicationOverlay::renderControllerPointersOculus() { } //Renders a small magnification of the currently bound texture at the coordinates -void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) +void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY, float sizeMult) const { Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); @@ -513,21 +525,24 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY) const int widgetWidth = glWidget->width(); const int widgetHeight = glWidget->height(); - mouseX -= MAGNIFY_WIDTH / 2; - mouseY -= MAGNIFY_HEIGHT / 2; + const float magnifyWidth = MAGNIFY_WIDTH * sizeMult; + const float magnifyHeight = MAGNIFY_HEIGHT * sizeMult; - float newWidth = MAGNIFY_WIDTH * MAGNIFY_MULT; - float newHeight = MAGNIFY_HEIGHT * MAGNIFY_MULT; + mouseX -= magnifyWidth; + mouseY -= magnifyHeight; + + float newWidth = magnifyWidth * MAGNIFY_MULT; + float newHeight = magnifyHeight * MAGNIFY_MULT; // Magnification Texture Coordinates float magnifyULeft = mouseX / (float)widgetWidth; - float magnifyURight = (mouseX + MAGNIFY_WIDTH) / (float)widgetWidth; + float magnifyURight = (mouseX + magnifyWidth) / (float)widgetWidth; float magnifyVBottom = 1.0f - mouseY / (float)widgetHeight; - float magnifyVTop = 1.0f - (mouseY + MAGNIFY_HEIGHT) / (float)widgetHeight; + float magnifyVTop = 1.0f - (mouseY + magnifyHeight) / (float)widgetHeight; // Coordinates of magnification overlay - float newMouseX = (mouseX + MAGNIFY_WIDTH / 2) - newWidth / 2.0f; - float newMouseY = (mouseY + MAGNIFY_HEIGHT / 2) + newHeight / 2.0f; + float newMouseX = (mouseX + magnifyWidth / 2) - newWidth / 2.0f; + float newMouseY = (mouseY + magnifyHeight / 2) + newHeight / 2.0f; // Get position on hemisphere using angle diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 172b16518c..2ed2d7e2db 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -50,7 +50,7 @@ private: void renderPointers(); void renderControllerPointers(); void renderControllerPointersOculus(); - void renderMagnifier(int mouseX, int mouseY); + void renderMagnifier(int mouseX, int mouseY, float sizeMult) const; void renderAudioMeter(); void renderStatsAndLogs(); void renderTexturedHemisphere(); @@ -65,6 +65,7 @@ private: bool _magActive[3]; int _magX[3]; int _magY[3]; + float _magSizeMult[3]; float _alpha; bool _active; From b37b9fb09745f6468904c22df80ae97b14bb3f76 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 17 Jun 2014 11:59:26 -0700 Subject: [PATCH 085/102] remove warnings about unused variables --- interface/src/Menu.cpp | 2 -- interface/src/ui/ApplicationOverlay.cpp | 3 --- 2 files changed, 5 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 0dccf6256b..5c8c2e97aa 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1104,8 +1104,6 @@ void Menu::multipleDestinationsDecision(const QJsonObject& userData, const QJson } else if (userResponse == QMessageBox::Open) { Application::getInstance()->getAvatar()->goToLocationFromAddress(placeData["address"].toObject()); } - - LocationManager* manager = reinterpret_cast(sender()); } void Menu::muteEnvironment() { diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 471ff20d66..af4648244f 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -154,7 +154,6 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { Application* application = Application::getInstance(); - QGLWidget* glWidget = application->getGLWidget(); MyAvatar* myAvatar = application->getAvatar(); const glm::vec3& viewMatrixTranslation = application->getViewMatrixTranslation(); @@ -221,8 +220,6 @@ void ApplicationOverlay::renderPointers() { Application* application = Application::getInstance(); // Render a crosshair over the mouse when in Oculus _numMagnifiers = 0; - int mouseX = application->getMouseX(); - int mouseY = application->getMouseY(); //lazily load crosshair texture if (_crosshairTexture == 0) { From 3f3632564254fc5c1a164cfe04eb02a74aed739d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 17 Jun 2014 11:59:37 -0700 Subject: [PATCH 086/102] remove warnings about signed/unsigned comparison --- assignment-client/src/audio/AudioMixer.cpp | 2 +- assignment-client/src/audio/AudioMixerClientData.cpp | 10 +++++----- domain-server/src/DomainServer.cpp | 2 +- libraries/audio/src/Sound.cpp | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index b3909660e2..d96fce450a 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -335,7 +335,7 @@ void AudioMixer::prepareMixForListeningNode(Node* node) { AudioMixerClientData* otherNodeClientData = (AudioMixerClientData*) otherNode->getLinkedData(); // enumerate the ARBs attached to the otherNode and add all that should be added to mix - for (unsigned int i = 0; i < otherNodeClientData->getRingBuffers().size(); i++) { + for (int i = 0; i < otherNodeClientData->getRingBuffers().size(); i++) { PositionalAudioRingBuffer* otherNodeBuffer = otherNodeClientData->getRingBuffers()[i]; if ((*otherNode != *node diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 7fb2a7dcab..9494e927a9 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -25,14 +25,14 @@ AudioMixerClientData::AudioMixerClientData() : } AudioMixerClientData::~AudioMixerClientData() { - for (unsigned int i = 0; i < _ringBuffers.size(); i++) { + for (int i = 0; i < _ringBuffers.size(); i++) { // delete this attached PositionalAudioRingBuffer delete _ringBuffers[i]; } } AvatarAudioRingBuffer* AudioMixerClientData::getAvatarAudioRingBuffer() const { - for (unsigned int i = 0; i < _ringBuffers.size(); i++) { + for (int i = 0; i < _ringBuffers.size(); i++) { if (_ringBuffers[i]->getType() == PositionalAudioRingBuffer::Microphone) { return (AvatarAudioRingBuffer*) _ringBuffers[i]; } @@ -79,7 +79,7 @@ int AudioMixerClientData::parseData(const QByteArray& packet) { InjectedAudioRingBuffer* matchingInjectedRingBuffer = NULL; - for (unsigned int i = 0; i < _ringBuffers.size(); i++) { + for (int i = 0; i < _ringBuffers.size(); i++) { if (_ringBuffers[i]->getType() == PositionalAudioRingBuffer::Injector && ((InjectedAudioRingBuffer*) _ringBuffers[i])->getStreamIdentifier() == streamIdentifier) { matchingInjectedRingBuffer = (InjectedAudioRingBuffer*) _ringBuffers[i]; @@ -99,7 +99,7 @@ int AudioMixerClientData::parseData(const QByteArray& packet) { } void AudioMixerClientData::checkBuffersBeforeFrameSend(int jitterBufferLengthSamples) { - for (unsigned int i = 0; i < _ringBuffers.size(); i++) { + for (int i = 0; i < _ringBuffers.size(); i++) { if (_ringBuffers[i]->shouldBeAddedToMix(jitterBufferLengthSamples)) { // this is a ring buffer that is ready to go // set its flag so we know to push its buffer when all is said and done @@ -113,7 +113,7 @@ void AudioMixerClientData::checkBuffersBeforeFrameSend(int jitterBufferLengthSam } void AudioMixerClientData::pushBuffersAfterFrameSend() { - for (unsigned int i = 0; i < _ringBuffers.size(); i++) { + for (int i = 0; i < _ringBuffers.size(); i++) { // this was a used buffer, push the output pointer forwards PositionalAudioRingBuffer* audioBuffer = _ringBuffers[i]; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index f7185063a9..d55a9b52ca 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -659,7 +659,7 @@ void DomainServer::readAvailableDatagrams() { wasNoisyTimerStarted = true; } - const quint64 NOISY_MESSAGE_INTERVAL_MSECS = 5 * 1000; + const qint64 NOISY_MESSAGE_INTERVAL_MSECS = 5 * 1000; if (requestAssignment.getType() != Assignment::AgentType || noisyMessageTimer.elapsed() > NOISY_MESSAGE_INTERVAL_MSECS) { diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 9bc78f2303..7ef3afdf29 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -258,7 +258,7 @@ void Sound::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& ou // Now pull out the data quint32 outputAudioByteArraySize = qFromLittleEndian(dataHeader.descriptor.size); outputAudioByteArray.resize(outputAudioByteArraySize); - if (waveStream.readRawData(outputAudioByteArray.data(), outputAudioByteArraySize) != outputAudioByteArraySize) { + if (waveStream.readRawData(outputAudioByteArray.data(), outputAudioByteArraySize) != (int)outputAudioByteArraySize) { qDebug() << "Error reading WAV file"; } From 9a3f8508cfaf3e7662a131f3d647a97b67ea5de2 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 17 Jun 2014 12:12:53 -0700 Subject: [PATCH 087/102] add support for inside out ray intersection on AACube and AABox --- libraries/octree/src/AABox.cpp | 37 ++++++++++++++++++++++++++++++++ libraries/octree/src/AACube.cpp | 38 +++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/libraries/octree/src/AABox.cpp b/libraries/octree/src/AABox.cpp index 7aa4d76134..409b362b24 100644 --- a/libraries/octree/src/AABox.cpp +++ b/libraries/octree/src/AABox.cpp @@ -180,6 +180,18 @@ static bool findIntersection(float origin, float direction, float corner, float return false; } +// finds the intersection between a ray and the inside facing plane on one axis +static bool findInsideOutIntersection(float origin, float direction, float corner, float size, float& distance) { + if (direction > EPSILON) { + distance = -1.0f * (origin - (corner + size)) / direction; + return true; + } else if (direction < -EPSILON) { + distance = -1.0f * (origin - corner) / direction; + return true; + } + return false; +} + bool AABox::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const { // handle the trivial cases where the expanded box contains the start or end if (expandedContains(start, expansion) || expandedContains(end, expansion)) { @@ -207,9 +219,34 @@ bool AABox::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& e bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const { // handle the trivial case where the box contains the origin if (contains(origin)) { + // We still want to calculate the distance from the origin to the inside out plane + float axisDistance; + if ((findInsideOutIntersection(origin.x, direction.x, _corner.x, _scale.x, axisDistance) && axisDistance >= 0 && + isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale.y) && + isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale.z))) { + distance = axisDistance; + face = direction.x > 0 ? MIN_X_FACE : MAX_X_FACE; + return true; + } + if ((findInsideOutIntersection(origin.y, direction.y, _corner.y, _scale.y, axisDistance) && axisDistance >= 0 && + isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale.x) && + isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale.z))) { + distance = axisDistance; + face = direction.y > 0 ? MIN_Y_FACE : MAX_Y_FACE; + return true; + } + if ((findInsideOutIntersection(origin.z, direction.z, _corner.z, _scale.z, axisDistance) && axisDistance >= 0 && + isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale.y) && + isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale.x))) { + distance = axisDistance; + face = direction.z > 0 ? MIN_Z_FACE : MAX_Z_FACE; + return true; + } + // This case is unexpected, but mimics the previous behavior for inside out intersections distance = 0; return true; } + // check each axis float axisDistance; if ((findIntersection(origin.x, direction.x, _corner.x, _scale.x, axisDistance) && axisDistance >= 0 && diff --git a/libraries/octree/src/AACube.cpp b/libraries/octree/src/AACube.cpp index 443d725a38..5a8839db4e 100644 --- a/libraries/octree/src/AACube.cpp +++ b/libraries/octree/src/AACube.cpp @@ -169,6 +169,18 @@ static bool findIntersection(float origin, float direction, float corner, float return false; } +// finds the intersection between a ray and the inside facing plane on one axis +static bool findInsideOutIntersection(float origin, float direction, float corner, float size, float& distance) { + if (direction > EPSILON) { + distance = -1.0f * (origin - (corner + size)) / direction; + return true; + } else if (direction < -EPSILON) { + distance = -1.0f * (origin - corner) / direction; + return true; + } + return false; +} + bool AACube::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const { // handle the trivial cases where the expanded box contains the start or end if (expandedContains(start, expansion) || expandedContains(end, expansion)) { @@ -196,9 +208,35 @@ bool AACube::expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& bool AACube::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const { // handle the trivial case where the box contains the origin if (contains(origin)) { + + // We still want to calculate the distance from the origin to the inside out plane + float axisDistance; + if ((findInsideOutIntersection(origin.x, direction.x, _corner.x, _scale, axisDistance) && axisDistance >= 0 && + isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale) && + isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale))) { + distance = axisDistance; + face = direction.x > 0 ? MIN_X_FACE : MAX_X_FACE; + return true; + } + if ((findInsideOutIntersection(origin.y, direction.y, _corner.y, _scale, axisDistance) && axisDistance >= 0 && + isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale) && + isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale))) { + distance = axisDistance; + face = direction.y > 0 ? MIN_Y_FACE : MAX_Y_FACE; + return true; + } + if ((findInsideOutIntersection(origin.z, direction.z, _corner.z, _scale, axisDistance) && axisDistance >= 0 && + isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale) && + isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale))) { + distance = axisDistance; + face = direction.z > 0 ? MIN_Z_FACE : MAX_Z_FACE; + return true; + } + // This case is unexpected, but mimics the previous behavior for inside out intersections distance = 0; return true; } + // check each axis float axisDistance; if ((findIntersection(origin.x, direction.x, _corner.x, _scale, axisDistance) && axisDistance >= 0 && From e775d645a76daad5d4460b1cc28b2444760fb383 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Tue, 17 Jun 2014 12:19:15 -0700 Subject: [PATCH 088/102] Fixed NAN error with magnification windows --- interface/src/ui/ApplicationOverlay.cpp | 51 ++++++++++++++++++------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 8a0ede2d1e..9bc69a4438 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -411,11 +411,10 @@ void ApplicationOverlay::renderControllerPointers() { //If the cursor is out of the screen then don't render it if (mouseX < 0 || mouseX >= glWidget->width() || mouseY < 0 || mouseY >= glWidget->height()) { + _reticleActive[index] = false; continue; } _reticleActive[index] = true; - float pointerWidth = 40; - float pointerHeight = 40; //if we have the oculus, we should make the cursor smaller since it will be //magnified @@ -435,17 +434,19 @@ void ApplicationOverlay::renderControllerPointers() { continue; } - mouseX -= pointerWidth / 2.0f; - mouseY += pointerHeight / 2.0f; + const float reticleSize = 40.0f; + + mouseX -= reticleSize / 2.0f; + mouseY += reticleSize / 2.0f; glBegin(GL_QUADS); glColor3f(RETICLE_COLOR[0], RETICLE_COLOR[1], RETICLE_COLOR[2]); glTexCoord2d(0.0f, 0.0f); glVertex2i(mouseX, mouseY); - glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + pointerWidth, mouseY); - glTexCoord2d(1.0f, 1.0f); glVertex2i(mouseX + pointerWidth, mouseY - pointerHeight); - glTexCoord2d(0.0f, 1.0f); glVertex2i(mouseX, mouseY - pointerHeight); + glTexCoord2d(1.0f, 0.0f); glVertex2i(mouseX + reticleSize, mouseY); + glTexCoord2d(1.0f, 1.0f); glVertex2i(mouseX + reticleSize, mouseY - reticleSize); + glTexCoord2d(0.0f, 1.0f); glVertex2i(mouseX, mouseY - reticleSize); glEnd(); } @@ -528,8 +529,8 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY, float sizeMult) const float magnifyWidth = MAGNIFY_WIDTH * sizeMult; const float magnifyHeight = MAGNIFY_HEIGHT * sizeMult; - mouseX -= magnifyWidth; - mouseY -= magnifyHeight; + mouseX -= magnifyWidth / 2; + mouseY -= magnifyHeight / 2; float newWidth = magnifyWidth * MAGNIFY_MULT; float newHeight = magnifyHeight * MAGNIFY_MULT; @@ -558,19 +559,43 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY, float sizeMult) float bY = sin((newVBottom - 0.5f) * _textureFov); float tY = sin((newVTop - 0.5f) * _textureFov); + float blZ, tlZ, brZ, trZ; + float dist; + float discriminant; //Bottom Left dist = sqrt(lX * lX + bY * bY); - float blZ = sqrt(1.0f - dist * dist); + discriminant = 1.0f - dist * dist; + if (discriminant > 0) { + blZ = sqrt(discriminant); + } else { + blZ = 0; + } //Top Left dist = sqrt(lX * lX + tY * tY); - float tlZ = sqrt(1.0f - dist * dist); + discriminant = 1.0f - dist * dist; + if (discriminant > 0) { + tlZ = sqrt(discriminant); + } else { + tlZ = 0; + } //Bottom Right dist = sqrt(rX * rX + bY * bY); - float brZ = sqrt(1.0f - dist * dist); + discriminant = 1.0f - dist * dist; + if (discriminant > 0) { + brZ = sqrt(discriminant); + } else { + brZ = 0; + } //Top Right dist = sqrt(rX * rX + tY * tY); - float trZ = sqrt(1.0f - dist * dist); + discriminant = 1.0f - dist * dist; + if (discriminant > 0) { + trZ = sqrt(discriminant); + } else { + trZ = 0; + } + glDisable(GL_TEXTURE_2D); glLineWidth(1.0f); //Outer Line From f18864bd72019f7b69a8d13b8aaa2d505237f721 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 17 Jun 2014 12:36:36 -0700 Subject: [PATCH 089/102] Moved findRayIntersection() to the Shape classes --- libraries/shared/src/CapsuleShape.cpp | 10 ++++ libraries/shared/src/CapsuleShape.h | 2 + libraries/shared/src/ListShape.h | 3 ++ libraries/shared/src/PlaneShape.cpp | 17 +++++++ libraries/shared/src/PlaneShape.h | 2 + libraries/shared/src/Shape.h | 2 + libraries/shared/src/ShapeCollider.cpp | 64 +----------------------- libraries/shared/src/ShapeCollider.h | 7 --- libraries/shared/src/SphereShape.h | 2 + tests/physics/src/ShapeColliderTests.cpp | 44 ++++++++-------- 10 files changed, 61 insertions(+), 92 deletions(-) diff --git a/libraries/shared/src/CapsuleShape.cpp b/libraries/shared/src/CapsuleShape.cpp index 8e887107dc..5416ff92a6 100644 --- a/libraries/shared/src/CapsuleShape.cpp +++ b/libraries/shared/src/CapsuleShape.cpp @@ -13,6 +13,8 @@ #include #include "CapsuleShape.h" + +#include "GeometryUtil.h" #include "SharedUtil.h" @@ -84,3 +86,11 @@ void CapsuleShape::setEndPoints(const glm::vec3& startPoint, const glm::vec3& en updateBoundingRadius(); } +bool CapsuleShape::findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const { + glm::vec3 capsuleStart, capsuleEnd; + getStartPoint(capsuleStart); + getEndPoint(capsuleEnd); + // NOTE: findRayCapsuleIntersection returns 'true' with distance = 0 when rayStart is inside capsule. + // TODO: implement the raycast to return inside surface intersection for the internal rayStart. + return findRayCapsuleIntersection(rayStart, rayDirection, capsuleStart, capsuleEnd, _radius, distance); +} diff --git a/libraries/shared/src/CapsuleShape.h b/libraries/shared/src/CapsuleShape.h index 756ae18911..fdd6c3eda6 100644 --- a/libraries/shared/src/CapsuleShape.h +++ b/libraries/shared/src/CapsuleShape.h @@ -39,6 +39,8 @@ public: void setRadiusAndHalfHeight(float radius, float height); void setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint); + bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const; + protected: void updateBoundingRadius() { _boundingRadius = _radius + _halfHeight; } diff --git a/libraries/shared/src/ListShape.h b/libraries/shared/src/ListShape.h index 7ba2410a23..17e7d7b2b6 100644 --- a/libraries/shared/src/ListShape.h +++ b/libraries/shared/src/ListShape.h @@ -55,6 +55,9 @@ public: void setShapes(QVector& shapes); + // TODO: either implement this or remove ListShape altogether + bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const { return false; } + protected: void clear(); void computeBoundingRadius(); diff --git a/libraries/shared/src/PlaneShape.cpp b/libraries/shared/src/PlaneShape.cpp index 0617feb863..e9563c6d8b 100644 --- a/libraries/shared/src/PlaneShape.cpp +++ b/libraries/shared/src/PlaneShape.cpp @@ -38,3 +38,20 @@ glm::vec4 PlaneShape::getCoefficients() const { glm::vec3 normal = _rotation * UNROTATED_NORMAL; return glm::vec4(normal.x, normal.y, normal.z, -glm::dot(normal, _position)); } + +bool PlaneShape::findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const { + glm::vec3 n = getNormal(); + float denominator = glm::dot(n, rayDirection); + if (fabsf(denominator) < EPSILON) { + // line is parallel to plane + return glm::dot(_position - rayStart, n) < EPSILON; + } else { + float d = glm::dot(_position - rayStart, n) / denominator; + if (d > 0.0f) { + // ray points toward plane + distance = d; + return true; + } + } + return false; +} diff --git a/libraries/shared/src/PlaneShape.h b/libraries/shared/src/PlaneShape.h index 24a3f1a2bc..b8a93324b7 100644 --- a/libraries/shared/src/PlaneShape.h +++ b/libraries/shared/src/PlaneShape.h @@ -20,6 +20,8 @@ public: glm::vec3 getNormal() const; glm::vec4 getCoefficients() const; + + bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const; }; #endif // hifi_PlaneShape_h diff --git a/libraries/shared/src/Shape.h b/libraries/shared/src/Shape.h index 87b84ea73b..3926f6cd07 100644 --- a/libraries/shared/src/Shape.h +++ b/libraries/shared/src/Shape.h @@ -38,6 +38,8 @@ public: virtual void setPosition(const glm::vec3& position) { _position = position; } virtual void setRotation(const glm::quat& rotation) { _rotation = rotation; } + virtual bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const = 0; + protected: // these ctors are protected (used by derived classes only) Shape(Type type) : _type(type), _boundingRadius(0.f), _position(0.f), _rotation() {} diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index 24d7fdb01a..bbedeb401d 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -772,7 +772,7 @@ bool findRayIntersectionWithShapes(const QVector shapes, const glm::vec3 Shape* shape = shapes.at(i); if (shape) { float distance; - if (findRayIntersectionWithShape(shape, rayStart, rayDirection, distance)) { + if (shape->findRayIntersection(rayStart, rayDirection, distance)) { if (distance < hitDistance) { hitDistance = distance; } @@ -785,66 +785,4 @@ bool findRayIntersectionWithShapes(const QVector shapes, const glm::vec3 return false; } -bool findRayIntersectionWithShape(const Shape* shape, const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) { - // NOTE: rayDirection is assumed to be normalized - int typeA = shape->getType(); - if (typeA == Shape::SPHERE_SHAPE) { - const SphereShape* sphere = static_cast(shape); - glm::vec3 sphereCenter = sphere->getPosition(); - float r2 = sphere->getRadius() * sphere->getRadius(); // r2 = radius^2 - - // compute closest approach (CA) - float a = glm::dot(sphere->getPosition() - rayStart, rayDirection); // a = distance from ray-start to CA - float b2 = glm::distance2(sphereCenter, rayStart + a * rayDirection); // b2 = squared distance from sphere-center to CA - if (b2 > r2) { - // ray does not hit sphere - return false; - } - float c = sqrtf(r2 - b2); // c = distance from CA to sphere surface along rayDirection - float d2 = glm::distance2(rayStart, sphereCenter); // d2 = squared distance from sphere-center to ray-start - if (a < 0.0f) { - // ray points away from sphere-center - if (d2 > r2) { - // ray starts outside sphere - return false; - } - // ray starts inside sphere - distance = c + a; - } else if (d2 > r2) { - // ray starts outside sphere - distance = a - c; - } else { - // ray starts inside sphere - distance = a + c; - } - return true; - } else if (typeA == Shape::CAPSULE_SHAPE) { - const CapsuleShape* capsule = static_cast(shape); - float radius = capsule->getRadius(); - glm::vec3 capsuleStart, capsuleEnd; - capsule->getStartPoint(capsuleStart); - capsule->getEndPoint(capsuleEnd); - // NOTE: findRayCapsuleIntersection returns 'true' with distance = 0 when rayStart is inside capsule. - // TODO: implement the raycast to return inside surface intersection for the internal rayStart. - return findRayCapsuleIntersection(rayStart, rayDirection, capsuleStart, capsuleEnd, radius, distance); - } else if (typeA == Shape::PLANE_SHAPE) { - const PlaneShape* plane = static_cast(shape); - glm::vec3 n = plane->getNormal(); - glm::vec3 P = plane->getPosition(); - float denominator = glm::dot(n, rayDirection); - if (fabsf(denominator) < EPSILON) { - // line is parallel to plane - return glm::dot(P - rayStart, n) < EPSILON; - } else { - float d = glm::dot(P - rayStart, n) / denominator; - if (d > 0.0f) { - // ray points toward plane - distance = d; - return true; - } - } - } - return false; -} - } // namespace ShapeCollider diff --git a/libraries/shared/src/ShapeCollider.h b/libraries/shared/src/ShapeCollider.h index 308a8cf10b..8261aceaf3 100644 --- a/libraries/shared/src/ShapeCollider.h +++ b/libraries/shared/src/ShapeCollider.h @@ -157,13 +157,6 @@ namespace ShapeCollider { /// \return true if ray hits any shape in shapes bool findRayIntersectionWithShapes(const QVector shapes, const glm::vec3& startPoint, const glm::vec3& direction, float& minDistance); - /// \param shapeA pointer to shape (cannot be NULL) - /// \param startPoint beginning of ray - /// \param direction direction of ray - /// \param distance[out] distance to intersection of shape and ray - /// \return true if ray hits shapeA - bool findRayIntersectionWithShape(const Shape* shapeA, const glm::vec3& startPoint, const glm::vec3& direction, float& distance); - } // namespace ShapeCollider #endif // hifi_ShapeCollider_h diff --git a/libraries/shared/src/SphereShape.h b/libraries/shared/src/SphereShape.h index 62783ab340..e87b8acab1 100644 --- a/libraries/shared/src/SphereShape.h +++ b/libraries/shared/src/SphereShape.h @@ -29,6 +29,8 @@ public: float getRadius() const { return _boundingRadius; } void setRadius(float radius) { _boundingRadius = radius; } + + bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const; }; #endif // hifi_SphereShape_h diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index 3387ba6aba..608e012998 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -911,7 +911,7 @@ void ShapeColliderTests::rayHitsSphere() { // very simple ray along xAxis { float distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + if (!sphere.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should intersect sphere" << std::endl; } @@ -928,7 +928,7 @@ void ShapeColliderTests::rayHitsSphere() { rayDirection = - glm::normalize(rayStart); float distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + if (!sphere.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should intersect sphere" << std::endl; } @@ -958,7 +958,7 @@ void ShapeColliderTests::rayHitsSphere() { sphere.setPosition(rotation * translation); float distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + if (!sphere.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should intersect sphere" << std::endl; } @@ -983,7 +983,7 @@ void ShapeColliderTests::rayBarelyHitsSphere() { // very simple ray along xAxis float distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + if (!sphere.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely hit sphere" << std::endl; } @@ -998,7 +998,7 @@ void ShapeColliderTests::rayBarelyHitsSphere() { // ...and test again distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + if (!sphere.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely hit sphere" << std::endl; } } @@ -1018,7 +1018,7 @@ void ShapeColliderTests::rayBarelyMissesSphere() { // very simple ray along xAxis float distance = FLT_MAX; - if (ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + if (sphere.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely miss sphere" << std::endl; } if (distance != FLT_MAX) { @@ -1036,7 +1036,7 @@ void ShapeColliderTests::rayBarelyMissesSphere() { // ...and test again distance = FLT_MAX; - if (ShapeCollider::findRayIntersectionWithShape(&sphere, rayStart, rayDirection, distance)) { + if (sphere.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely miss sphere" << std::endl; } if (distance != FLT_MAX) { @@ -1056,7 +1056,7 @@ void ShapeColliderTests::rayHitsCapsule() { glm::vec3 rayStart(startDistance, 0.0f, 0.0f); glm::vec3 rayDirection(-1.0f, 0.0f, 0.0f); float distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } float expectedDistance = startDistance - radius; @@ -1068,7 +1068,7 @@ void ShapeColliderTests::rayHitsCapsule() { // toward top of cylindrical wall rayStart.y = halfHeight; distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } relativeError = fabsf(distance - expectedDistance) / startDistance; @@ -1080,7 +1080,7 @@ void ShapeColliderTests::rayHitsCapsule() { float delta = 2.0f * EPSILON; rayStart.y = halfHeight + delta; distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } relativeError = fabsf(distance - expectedDistance) / startDistance; @@ -1093,7 +1093,7 @@ void ShapeColliderTests::rayHitsCapsule() { // toward tip of top cap rayStart.y = halfHeight + radius - delta; distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine @@ -1106,7 +1106,7 @@ void ShapeColliderTests::rayHitsCapsule() { // toward tip of bottom cap rayStart.y = - halfHeight - radius + delta; distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine @@ -1120,7 +1120,7 @@ void ShapeColliderTests::rayHitsCapsule() { rayStart.y = 0.0f; rayStart.z = radius - delta; distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine @@ -1150,7 +1150,7 @@ void ShapeColliderTests::rayMissesCapsule() { // over top cap rayStart.y = halfHeight + radius + delta; float distance = FLT_MAX; - if (ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + if (capsule.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss capsule" << std::endl; } if (distance != FLT_MAX) { @@ -1160,7 +1160,7 @@ void ShapeColliderTests::rayMissesCapsule() { // below bottom cap rayStart.y = - halfHeight - radius - delta; distance = FLT_MAX; - if (ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + if (capsule.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss capsule" << std::endl; } if (distance != FLT_MAX) { @@ -1171,7 +1171,7 @@ void ShapeColliderTests::rayMissesCapsule() { rayStart.y = 0.0f; rayStart.z = radius + delta; distance = FLT_MAX; - if (ShapeCollider::findRayIntersectionWithShape(&capsule, rayStart, rayDirection, distance)) { + if (capsule.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss capsule" << std::endl; } if (distance != FLT_MAX) { @@ -1194,7 +1194,7 @@ void ShapeColliderTests::rayHitsPlane() { glm::vec3 rayDirection = glm::normalize(glm::vec3(1.0f, 1.0f, 1.0f)); float distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + if (!plane.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit plane" << std::endl; } @@ -1215,7 +1215,7 @@ void ShapeColliderTests::rayHitsPlane() { rayDirection = rotation * rayDirection; distance = FLT_MAX; - if (!ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + if (!plane.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit plane" << std::endl; } @@ -1239,7 +1239,7 @@ void ShapeColliderTests::rayMissesPlane() { glm::vec3 rayDirection = glm::normalize(glm::vec3(-1.0f, 0.0f, -1.0f)); float distance = FLT_MAX; - if (ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + if (plane.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; } if (distance != FLT_MAX) { @@ -1257,7 +1257,7 @@ void ShapeColliderTests::rayMissesPlane() { rayDirection = rotation * rayDirection; distance = FLT_MAX; - if (ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + if (plane.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; } if (distance != FLT_MAX) { @@ -1271,7 +1271,7 @@ void ShapeColliderTests::rayMissesPlane() { glm::vec3 rayDirection = glm::normalize(glm::vec3(-1.0f, -1.0f, -1.0f)); float distance = FLT_MAX; - if (ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + if (plane.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; } if (distance != FLT_MAX) { @@ -1289,7 +1289,7 @@ void ShapeColliderTests::rayMissesPlane() { rayDirection = rotation * rayDirection; distance = FLT_MAX; - if (ShapeCollider::findRayIntersectionWithShape(&plane, rayStart, rayDirection, distance)) { + if (plane.findRayIntersection(rayStart, rayDirection, distance)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; } if (distance != FLT_MAX) { From bee84a064cafe93de5c9358f94192d95f2ade548 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Tue, 17 Jun 2014 12:41:53 -0700 Subject: [PATCH 090/102] Dont render red border for mouse magnifier --- interface/src/ui/ApplicationOverlay.cpp | 32 +++++++++++++------------ interface/src/ui/ApplicationOverlay.h | 2 +- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 9bc69a4438..cb96a2182c 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -261,7 +261,8 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { } if (_magSizeMult[i] > 0.0f) { - renderMagnifier(_magX[i], _magY[i], _magSizeMult[i]); + //Render magnifier, but dont show border for mouse magnifier + renderMagnifier(_magX[i], _magY[i], _magSizeMult[i], i != MOUSE); } } @@ -518,7 +519,7 @@ void ApplicationOverlay::renderControllerPointersOculus() { } //Renders a small magnification of the currently bound texture at the coordinates -void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY, float sizeMult) const +void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY, float sizeMult, bool showBorder) const { Application* application = Application::getInstance(); QGLWidget* glWidget = application->getGLWidget(); @@ -596,23 +597,24 @@ void ApplicationOverlay::renderMagnifier(int mouseX, int mouseY, float sizeMult) trZ = 0; } - glDisable(GL_TEXTURE_2D); - glLineWidth(1.0f); - //Outer Line - glBegin(GL_LINE_STRIP); - glColor4f(1.0f, 0.0f, 0.0f, _alpha); + if (showBorder) { + glDisable(GL_TEXTURE_2D); + glLineWidth(1.0f); + //Outer Line + glBegin(GL_LINE_STRIP); + glColor4f(1.0f, 0.0f, 0.0f, _alpha); - glVertex3f(lX, tY, -tlZ); - glVertex3f(rX, tY, -trZ); - glVertex3f(rX, bY, -brZ); - glVertex3f(lX, bY, -blZ); - glVertex3f(lX, tY, -tlZ); + glVertex3f(lX, tY, -tlZ); + glVertex3f(rX, tY, -trZ); + glVertex3f(rX, bY, -brZ); + glVertex3f(lX, bY, -blZ); + glVertex3f(lX, tY, -tlZ); + glEnd(); + glEnable(GL_TEXTURE_2D); + } glColor4f(1.0f, 1.0f, 1.0f, _alpha); - glEnd(); - glEnable(GL_TEXTURE_2D); - glBegin(GL_QUADS); glTexCoord2f(magnifyULeft, magnifyVBottom); glVertex3f(lX, tY, -tlZ); diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 2ed2d7e2db..3a59dcd7d1 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -50,7 +50,7 @@ private: void renderPointers(); void renderControllerPointers(); void renderControllerPointersOculus(); - void renderMagnifier(int mouseX, int mouseY, float sizeMult) const; + void renderMagnifier(int mouseX, int mouseY, float sizeMult, bool showBorder) const; void renderAudioMeter(); void renderStatsAndLogs(); void renderTexturedHemisphere(); From b6cecf3cfd4ef651f1fcc8120dae7880265b37e7 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 17 Jun 2014 12:50:49 -0700 Subject: [PATCH 091/102] Add SphereShape.cpp to project --- libraries/shared/src/SphereShape.cpp | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 libraries/shared/src/SphereShape.cpp diff --git a/libraries/shared/src/SphereShape.cpp b/libraries/shared/src/SphereShape.cpp new file mode 100644 index 0000000000..49137fac43 --- /dev/null +++ b/libraries/shared/src/SphereShape.cpp @@ -0,0 +1,44 @@ +// +// SphereShape.cpp +// libraries/shared/src +// +// Created by Andrew Meadows on 2014.06.17 +// 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 "SphereShape.h" + +bool SphereShape::findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const { + float r2 = _boundingRadius * _boundingRadius; + + // compute closest approach (CA) + float a = glm::dot(_position - rayStart, rayDirection); // a = distance from ray-start to CA + float b2 = glm::distance2(_position, rayStart + a * rayDirection); // b2 = squared distance from sphere-center to CA + if (b2 > r2) { + // ray does not hit sphere + return false; + } + float c = sqrtf(r2 - b2); // c = distance from CA to sphere surface along rayDirection + float d2 = glm::distance2(rayStart, _position); // d2 = squared distance from sphere-center to ray-start + if (a < 0.0f) { + // ray points away from sphere-center + if (d2 > r2) { + // ray starts outside sphere + return false; + } + // ray starts inside sphere + distance = c + a; + } else if (d2 > r2) { + // ray starts outside sphere + distance = a - c; + } else { + // ray starts inside sphere + distance = a + c; + } + return true; +} From 4413049302d1a308bbb4870c4d8dff163e18402b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 17 Jun 2014 13:05:35 -0700 Subject: [PATCH 092/102] fixed a bug in inside out ray casting returning the wrong face --- libraries/octree/src/AABox.cpp | 6 +- libraries/octree/src/AACube.cpp | 6 +- tests/octree/src/AABoxCubeTests.cpp | 100 ++++++++++++++++++++++++++++ tests/octree/src/AABoxCubeTests.h | 20 ++++++ tests/octree/src/OctreeTests.cpp | 2 +- tests/octree/src/OctreeTests.h | 2 +- tests/octree/src/main.cpp | 2 + 7 files changed, 130 insertions(+), 8 deletions(-) create mode 100644 tests/octree/src/AABoxCubeTests.cpp create mode 100644 tests/octree/src/AABoxCubeTests.h diff --git a/libraries/octree/src/AABox.cpp b/libraries/octree/src/AABox.cpp index 409b362b24..60f26a5533 100644 --- a/libraries/octree/src/AABox.cpp +++ b/libraries/octree/src/AABox.cpp @@ -225,21 +225,21 @@ bool AABox::findRayIntersection(const glm::vec3& origin, const glm::vec3& direct isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale.y) && isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale.z))) { distance = axisDistance; - face = direction.x > 0 ? MIN_X_FACE : MAX_X_FACE; + face = direction.x > 0 ? MAX_X_FACE : MIN_X_FACE; return true; } if ((findInsideOutIntersection(origin.y, direction.y, _corner.y, _scale.y, axisDistance) && axisDistance >= 0 && isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale.x) && isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale.z))) { distance = axisDistance; - face = direction.y > 0 ? MIN_Y_FACE : MAX_Y_FACE; + face = direction.y > 0 ? MAX_Y_FACE : MIN_Y_FACE; return true; } if ((findInsideOutIntersection(origin.z, direction.z, _corner.z, _scale.z, axisDistance) && axisDistance >= 0 && isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale.y) && isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale.x))) { distance = axisDistance; - face = direction.z > 0 ? MIN_Z_FACE : MAX_Z_FACE; + face = direction.z > 0 ? MAX_Z_FACE : MIN_Z_FACE; return true; } // This case is unexpected, but mimics the previous behavior for inside out intersections diff --git a/libraries/octree/src/AACube.cpp b/libraries/octree/src/AACube.cpp index 5a8839db4e..e359eac9e9 100644 --- a/libraries/octree/src/AACube.cpp +++ b/libraries/octree/src/AACube.cpp @@ -215,21 +215,21 @@ bool AACube::findRayIntersection(const glm::vec3& origin, const glm::vec3& direc isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale) && isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale))) { distance = axisDistance; - face = direction.x > 0 ? MIN_X_FACE : MAX_X_FACE; + face = direction.x > 0 ? MAX_X_FACE : MIN_X_FACE; return true; } if ((findInsideOutIntersection(origin.y, direction.y, _corner.y, _scale, axisDistance) && axisDistance >= 0 && isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale) && isWithin(origin.z + axisDistance*direction.z, _corner.z, _scale))) { distance = axisDistance; - face = direction.y > 0 ? MIN_Y_FACE : MAX_Y_FACE; + face = direction.y > 0 ? MAX_Y_FACE : MIN_Y_FACE; return true; } if ((findInsideOutIntersection(origin.z, direction.z, _corner.z, _scale, axisDistance) && axisDistance >= 0 && isWithin(origin.y + axisDistance*direction.y, _corner.y, _scale) && isWithin(origin.x + axisDistance*direction.x, _corner.x, _scale))) { distance = axisDistance; - face = direction.z > 0 ? MIN_Z_FACE : MAX_Z_FACE; + face = direction.z > 0 ? MAX_Z_FACE : MIN_Z_FACE; return true; } // This case is unexpected, but mimics the previous behavior for inside out intersections diff --git a/tests/octree/src/AABoxCubeTests.cpp b/tests/octree/src/AABoxCubeTests.cpp new file mode 100644 index 0000000000..85787e279b --- /dev/null +++ b/tests/octree/src/AABoxCubeTests.cpp @@ -0,0 +1,100 @@ +// +// AABoxCubeTests.h +// tests/octree/src +// +// Created by Brad Hefta-Gaub on 06/04/2014. +// 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 +#include + +#include "AABoxCubeTests.h" + +void AABoxCubeTests::AABoxCubeTests() { + qDebug() << "******************************************************************************************"; + qDebug() << "AABoxCubeTests::AABoxCubeTests()"; + + { + qDebug() << "Test 1: AABox.findRayIntersection() inside out MIN_X_FACE"; + + glm::vec3 corner(0.0f, 0.0f, 0.0f); + float size = 1.0f; + + AABox box(corner, size); + glm::vec3 origin(0.5f, 0.5f, 0.5f); + glm::vec3 direction(-1.0f, 0.0f, 0.0f); + float distance; + BoxFace face; + + bool intersects = box.findRayIntersection(origin, direction, distance, face); + + if (intersects && distance == 0.5f && face == MIN_X_FACE) { + qDebug() << "Test 1: PASSED"; + } else { + qDebug() << "intersects=" << intersects << "expected=" << true; + qDebug() << "distance=" << distance << "expected=" << 0.5f; + qDebug() << "face=" << face << "expected=" << MIN_X_FACE; + + } + } + + { + qDebug() << "Test 2: AABox.findRayIntersection() inside out MAX_X_FACE"; + + glm::vec3 corner(0.0f, 0.0f, 0.0f); + float size = 1.0f; + + AABox box(corner, size); + glm::vec3 origin(0.5f, 0.5f, 0.5f); + glm::vec3 direction(1.0f, 0.0f, 0.0f); + float distance; + BoxFace face; + + bool intersects = box.findRayIntersection(origin, direction, distance, face); + + if (intersects && distance == 0.5f && face == MAX_X_FACE) { + qDebug() << "Test 2: PASSED"; + } else { + qDebug() << "intersects=" << intersects << "expected=" << true; + qDebug() << "distance=" << distance << "expected=" << 0.5f; + qDebug() << "face=" << face << "expected=" << MAX_X_FACE; + + } + } + + { + qDebug() << "Test 3: AABox.findRayIntersection() outside in"; + + glm::vec3 corner(0.5f, 0.0f, 0.0f); + float size = 0.5f; + + AABox box(corner, size); + glm::vec3 origin(0.25f, 0.25f, 0.25f); + glm::vec3 direction(1.0f, 0.0f, 0.0f); + float distance; + BoxFace face; + + bool intersects = box.findRayIntersection(origin, direction, distance, face); + + if (intersects && distance == 0.25f && face == MIN_X_FACE) { + qDebug() << "Test 3: PASSED"; + } else { + qDebug() << "intersects=" << intersects << "expected=" << true; + qDebug() << "distance=" << distance << "expected=" << 0.5f; + qDebug() << "face=" << face << "expected=" << MIN_X_FACE; + + } + } + + qDebug() << "******************************************************************************************"; +} + +void AABoxCubeTests::runAllTests() { + AABoxCubeTests(); +} diff --git a/tests/octree/src/AABoxCubeTests.h b/tests/octree/src/AABoxCubeTests.h new file mode 100644 index 0000000000..8d1ece51f8 --- /dev/null +++ b/tests/octree/src/AABoxCubeTests.h @@ -0,0 +1,20 @@ +// +// AABoxCubeTests.h +// tests/octree/src +// +// Created by Brad Hefta-Gaub on 06/04/2014. +// 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_AABoxCubeTests_h +#define hifi_AABoxCubeTests_h + +namespace AABoxCubeTests { + void AABoxCubeTests(); + void runAllTests(); +} + +#endif // hifi_AABoxCubeTests_h diff --git a/tests/octree/src/OctreeTests.cpp b/tests/octree/src/OctreeTests.cpp index ddc3f2c74d..70ef97f225 100644 --- a/tests/octree/src/OctreeTests.cpp +++ b/tests/octree/src/OctreeTests.cpp @@ -1,6 +1,6 @@ // // OctreeTests.h -// tests/physics/src +// tests/octree/src // // Created by Brad Hefta-Gaub on 06/04/2014. // Copyright 2014 High Fidelity, Inc. diff --git a/tests/octree/src/OctreeTests.h b/tests/octree/src/OctreeTests.h index 53b0d9fb83..5eae6c6158 100644 --- a/tests/octree/src/OctreeTests.h +++ b/tests/octree/src/OctreeTests.h @@ -1,6 +1,6 @@ // // OctreeTests.h -// tests/physics/src +// tests/octree/src // // Created by Brad Hefta-Gaub on 06/04/2014. // Copyright 2014 High Fidelity, Inc. diff --git a/tests/octree/src/main.cpp b/tests/octree/src/main.cpp index ec3dc19e01..de7b3926ae 100644 --- a/tests/octree/src/main.cpp +++ b/tests/octree/src/main.cpp @@ -9,8 +9,10 @@ // #include "OctreeTests.h" +#include "AABoxCubeTests.h" int main(int argc, char** argv) { OctreeTests::runAllTests(); + AABoxCubeTests::runAllTests(); return 0; } From 89fbeb0b6d42e9a7c61b524c8080ad4f70161722 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 17 Jun 2014 13:09:48 -0700 Subject: [PATCH 093/102] Fix invalid remote scripts being loaded --- libraries/script-engine/src/ScriptEngine.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index a4aae61248..b0cce114a9 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -147,7 +147,12 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL, QEventLoop loop; QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); loop.exec(); - _scriptContents = reply->readAll(); + if (reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 200) { + _scriptContents = reply->readAll(); + } else { + qDebug() << "ERROR Loading file:" << url.toString(); + emit errorMessage("ERROR Loading file:" + url.toString()); + } } } } From 9824072300fcdccf67cea024349e89a2cecde64f Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Tue, 17 Jun 2014 23:51:02 +0200 Subject: [PATCH 094/102] remove unused parameter in `AccountScriptingInterface::updateBalance` --- interface/src/scripting/AccountScriptingInterface.cpp | 5 +++-- interface/src/scripting/AccountScriptingInterface.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/scripting/AccountScriptingInterface.cpp b/interface/src/scripting/AccountScriptingInterface.cpp index c7b47c7866..ec52eef416 100644 --- a/interface/src/scripting/AccountScriptingInterface.cpp +++ b/interface/src/scripting/AccountScriptingInterface.cpp @@ -15,8 +15,9 @@ AccountScriptingInterface::AccountScriptingInterface() { AccountManager& accountManager = AccountManager::getInstance(); - connect(&accountManager, &AccountManager::balanceChanged, this, + bool succ = connect(&accountManager, &AccountManager::balanceChanged, this, &AccountScriptingInterface::updateBalance); + } AccountScriptingInterface* AccountScriptingInterface::getInstance() { @@ -34,7 +35,7 @@ bool AccountScriptingInterface::isLoggedIn() { return accountManager.isLoggedIn(); } -void AccountScriptingInterface::updateBalance(qint64 newBalance) { +void AccountScriptingInterface::updateBalance() { AccountManager& accountManager = AccountManager::getInstance(); emit balanceChanged(accountManager.getAccountInfo().getBalanceInSatoshis()); } diff --git a/interface/src/scripting/AccountScriptingInterface.h b/interface/src/scripting/AccountScriptingInterface.h index 38ce9c1b35..e9cf0ede5f 100644 --- a/interface/src/scripting/AccountScriptingInterface.h +++ b/interface/src/scripting/AccountScriptingInterface.h @@ -25,7 +25,7 @@ public slots: static AccountScriptingInterface* getInstance(); float getBalance(); bool isLoggedIn(); - void updateBalance(qint64 newBalance); + void updateBalance(); }; #endif // hifi_AccountScriptingInterface_h From 591ab448a12c58ec1214a65da08f3324505c81a5 Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Tue, 17 Jun 2014 23:54:55 +0200 Subject: [PATCH 095/102] remove unused variable --- interface/src/scripting/AccountScriptingInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/scripting/AccountScriptingInterface.cpp b/interface/src/scripting/AccountScriptingInterface.cpp index ec52eef416..87ea3220a4 100644 --- a/interface/src/scripting/AccountScriptingInterface.cpp +++ b/interface/src/scripting/AccountScriptingInterface.cpp @@ -15,7 +15,7 @@ AccountScriptingInterface::AccountScriptingInterface() { AccountManager& accountManager = AccountManager::getInstance(); - bool succ = connect(&accountManager, &AccountManager::balanceChanged, this, + connect(&accountManager, &AccountManager::balanceChanged, this, &AccountScriptingInterface::updateBalance); } From 6c74650552d5ef97fa5904edf64f665eefd146d0 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Tue, 17 Jun 2014 15:40:11 -0700 Subject: [PATCH 096/102] Attempt to fix build errors --- interface/src/ui/ApplicationOverlay.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index cb96a2182c..4a77253304 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "Application.h" #include "ApplicationOverlay.h" @@ -57,6 +58,7 @@ const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f }; // Renders the overlays either to a texture or to the screen void ApplicationOverlay::renderOverlay(bool renderToTexture) { + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()"); _textureFov = Menu::getInstance()->getOculusUIAngularSize() * RADIANS_PER_DEGREE; From 7cbb19551a36b7b9ee3bbaa78d6f35f00df72781 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Tue, 17 Jun 2014 16:38:19 -0700 Subject: [PATCH 097/102] Switched time query method and fixed horrifying bug --- interface/src/ui/ApplicationOverlay.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 4a77253304..ee3c271267 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -13,7 +13,6 @@ #include #include -#include #include "Application.h" #include "ApplicationOverlay.h" @@ -116,6 +115,7 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { glPopMatrix(); + glMatrixMode(GL_MODELVIEW); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); @@ -210,6 +210,7 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { return; } + Application* application = Application::getInstance(); MyAvatar* myAvatar = application->getAvatar(); @@ -333,11 +334,11 @@ void ApplicationOverlay::renderControllerPointers() { MyAvatar* myAvatar = application->getAvatar(); //Static variables used for storing controller state - static unsigned int pressedTime[2] = { 0, 0 }; - static bool isPressed[2] = { false, false }; - static bool stateWhenPressed[2] = { false, false }; - static bool triggerPressed[2] = { false, false }; - static bool bumperPressed[2] = { false, false }; + static quint64 pressedTime[3] = { 0, 0, 0 }; + static bool isPressed[3] = { false, false, false }; + static bool stateWhenPressed[3] = { false, false, false }; + static bool triggerPressed[3] = { false, false, false }; + static bool bumperPressed[3] = { false, false, false }; const HandData* handData = Application::getInstance()->getAvatar()->getHandData(); @@ -363,15 +364,15 @@ void ApplicationOverlay::renderControllerPointers() { if (isPressed[index] == false) { //We are now dragging the window isPressed[index] = true; - //set the pressed time in ms - pressedTime[index] = SDL_GetTicks(); + //set the pressed time in us + pressedTime[index] = usecTimestampNow(); stateWhenPressed[index] = _magActive[index]; } } else if (isPressed[index]) { - isPressed[index] = false; - //If the button has been pressed for less than 250 ms + isPressed[index] = false; + //If the button was only pressed for < 250 ms //then disable it. - if (SDL_GetTicks() - pressedTime[index] < 250) { + if (usecTimestampNow() - pressedTime[index] < 250000) { _magActive[index] = !stateWhenPressed[index]; } } From 9f66eeb37ff84b853aefb69a657a04572987165c Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Tue, 17 Jun 2014 16:48:06 -0700 Subject: [PATCH 098/102] Added a const variable for readability --- interface/src/ui/ApplicationOverlay.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index ee3c271267..b7c3feca29 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -25,6 +25,8 @@ const float FADE_SPEED = 0.08f; // Used to animate the magnification windows const float MAG_SPEED = 0.08f; +const quint64 MSECS_TO_USECS = 1000ULL; + // Fast helper functions inline float max(float a, float b) { return (a > b) ? a : b; @@ -334,7 +336,7 @@ void ApplicationOverlay::renderControllerPointers() { MyAvatar* myAvatar = application->getAvatar(); //Static variables used for storing controller state - static quint64 pressedTime[3] = { 0, 0, 0 }; + static quint64 pressedTime[3] = { 0ULL, 0ULL, 0ULL }; static bool isPressed[3] = { false, false, false }; static bool stateWhenPressed[3] = { false, false, false }; static bool triggerPressed[3] = { false, false, false }; @@ -372,7 +374,9 @@ void ApplicationOverlay::renderControllerPointers() { isPressed[index] = false; //If the button was only pressed for < 250 ms //then disable it. - if (usecTimestampNow() - pressedTime[index] < 250000) { + + const int MAX_BUTTON_PRESS_TIME = 250 * MSECS_TO_USECS; + if (usecTimestampNow() - pressedTime[index] < MAX_BUTTON_PRESS_TIME) { _magActive[index] = !stateWhenPressed[index]; } } From fa32ad5b117f73df3c598491fb05ae05113e2cec Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Tue, 17 Jun 2014 17:14:26 -0700 Subject: [PATCH 099/102] De-janked code by removing magic numbers --- interface/src/ui/ApplicationOverlay.cpp | 17 ++++++++--------- interface/src/ui/ApplicationOverlay.h | 17 ++++++++--------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index b7c3feca29..9be556cf62 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -190,7 +190,7 @@ void ApplicationOverlay::getClickLocation(int &x, int &y) const { const float yRange = MAGNIFY_WIDTH * MAGNIFY_MULT / 2.0f; //Loop through all magnification windows - for (int i = 0; i < 3; i++) { + for (int i = 0; i < NUMBER_OF_MAGNIFIERS; i++) { if (_magActive[i]) { dx = x - _magX[i]; dy = y - _magY[i]; @@ -212,7 +212,6 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { return; } - Application* application = Application::getInstance(); MyAvatar* myAvatar = application->getAvatar(); @@ -251,7 +250,7 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { glAlphaFunc(GL_GREATER, 0.01f); //Update and draw the magnifiers - for (int i = 0; i < 3; i++) { + for (int i = 0; i < NUMBER_OF_MAGNIFIERS; i++) { if (_magActive[i]) { _magSizeMult[i] += MAG_SPEED; @@ -336,11 +335,11 @@ void ApplicationOverlay::renderControllerPointers() { MyAvatar* myAvatar = application->getAvatar(); //Static variables used for storing controller state - static quint64 pressedTime[3] = { 0ULL, 0ULL, 0ULL }; - static bool isPressed[3] = { false, false, false }; - static bool stateWhenPressed[3] = { false, false, false }; - static bool triggerPressed[3] = { false, false, false }; - static bool bumperPressed[3] = { false, false, false }; + static quint64 pressedTime[NUMBER_OF_MAGNIFIERS] = { 0ULL, 0ULL, 0ULL }; + static bool isPressed[NUMBER_OF_MAGNIFIERS] = { false, false, false }; + static bool stateWhenPressed[NUMBER_OF_MAGNIFIERS] = { false, false, false }; + static bool triggerPressed[NUMBER_OF_MAGNIFIERS] = { false, false, false }; + static bool bumperPressed[NUMBER_OF_MAGNIFIERS] = { false, false, false }; const HandData* handData = Application::getInstance()->getAvatar()->getHandData(); @@ -472,7 +471,7 @@ void ApplicationOverlay::renderControllerPointersOculus() { glBindTexture(GL_TEXTURE_2D, _crosshairTexture); glDisable(GL_DEPTH_TEST); - for (int i = 0; i < 3; i++) { + for (int i = 0; i < NUMBER_OF_MAGNIFIERS; i++) { //Dont render the reticle if its inactive if (!_reticleActive[i]) { diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 3a59dcd7d1..f8fd95d2f7 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -43,8 +43,6 @@ private: glm::vec2 uv; }; - enum MousePointerDevice { MOUSE, LEFT_CONTROLLER, RIGHT_CONTROLLER }; - typedef QPair VerticesIndices; void renderPointers(); @@ -59,13 +57,14 @@ private: float _trailingAudioLoudness; float _textureFov; // 0 = Mouse, 1 = Left Controller, 2 = Right Controller - bool _reticleActive[3]; - int _mouseX[3]; - int _mouseY[3]; - bool _magActive[3]; - int _magX[3]; - int _magY[3]; - float _magSizeMult[3]; + enum MagnifyDevices { MOUSE, LEFT_CONTROLLER, RIGHT_CONTROLLER, NUMBER_OF_MAGNIFIERS = RIGHT_CONTROLLER + 1 }; + bool _reticleActive[NUMBER_OF_MAGNIFIERS]; + int _mouseX[NUMBER_OF_MAGNIFIERS]; + int _mouseY[NUMBER_OF_MAGNIFIERS]; + bool _magActive[NUMBER_OF_MAGNIFIERS]; + int _magX[NUMBER_OF_MAGNIFIERS]; + int _magY[NUMBER_OF_MAGNIFIERS]; + float _magSizeMult[NUMBER_OF_MAGNIFIERS]; float _alpha; bool _active; From 64725dd21bf001e3111dcc76ebf32d77f18a1044 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Tue, 17 Jun 2014 17:15:43 -0700 Subject: [PATCH 100/102] Unneeded comment --- interface/src/ui/ApplicationOverlay.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index f8fd95d2f7..b9f9596ccf 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -56,7 +56,7 @@ private: QOpenGLFramebufferObject* _framebufferObject; float _trailingAudioLoudness; float _textureFov; - // 0 = Mouse, 1 = Left Controller, 2 = Right Controller + enum MagnifyDevices { MOUSE, LEFT_CONTROLLER, RIGHT_CONTROLLER, NUMBER_OF_MAGNIFIERS = RIGHT_CONTROLLER + 1 }; bool _reticleActive[NUMBER_OF_MAGNIFIERS]; int _mouseX[NUMBER_OF_MAGNIFIERS]; From 4467ee266d1bed0a58ddf1dfc037ac39be7308ee Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 17 Jun 2014 17:19:16 -0700 Subject: [PATCH 101/102] Added snapped/smoothed rotation for models + tooltip --- examples/editModels.js | 148 +++++++++++++++++++++++++++++++++++------ 1 file changed, 127 insertions(+), 21 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 53633d3c0c..3cd727570c 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -5,6 +5,19 @@ // Created by Clément Brisset on 4/24/14. // Copyright 2014 High Fidelity, Inc. // +// This script allows you to edit models either with the razor hydras or with your mouse +// +// If using the hydras : +// grab grab models with the triggers, you can then move the models around or scale them with both hands. +// You can switch mode using the bumpers so that you can move models roud more easily. +// +// If using the mouse : +// - left click lets you move the model in the plane facing you. +// If pressing shift, it will move on the horizontale plane it's in. +// - right click lets you rotate the model. z and x give you access to more axix of rotation while shift allows for finer control. +// - left + right click lets you scale the model. +// - you can press r while holding the model to reset its rotation +// // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // @@ -653,16 +666,18 @@ function initToolBar() { } function moveOverlays() { + var newViewPort = Controller.getViewportDimensions(); + if (typeof(toolBar) === 'undefined') { initToolBar(); - } else if (windowDimensions.x == Controller.getViewportDimensions().x && - windowDimensions.y == Controller.getViewportDimensions().y) { + } else if (windowDimensions.x == newViewPort.x && + windowDimensions.y == newViewPort.y) { return; } - windowDimensions = Controller.getViewportDimensions(); + windowDimensions = newViewPort; var toolsX = windowDimensions.x - 8 - toolBar.width; var toolsY = (windowDimensions.y - toolBar.height) / 2; @@ -680,8 +695,6 @@ var intersection; var SCALE_FACTOR = 200.0; -var TRANSLATION_FACTOR = 100.0; -var ROTATION_FACTOR = 100.0; function rayPlaneIntersection(pickRay, point, normal) { var d = -Vec3.dot(point, normal); @@ -690,8 +703,50 @@ function rayPlaneIntersection(pickRay, point, normal) { return Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, t)); } +function Tooltip() { + this.x = 285; + this.y = 115; + this.width = 110; + this.height = 115 ; + this.margin = 5; + this.decimals = 3; + + this.textOverlay = Overlays.addOverlay("text", { + x: this.x, + y: this.y, + width: this.width, + height: this.height, + margin: this.margin, + text: "", + color: { red: 128, green: 128, blue: 128 }, + alpha: 0.2, + visible: false + }); + this.show = function(doShow) { + Overlays.editOverlay(this.textOverlay, { visible: doShow }); + } + this.updateText = function(properties) { + var angles = Quat.safeEulerAngles(properties.modelRotation); + var text = "Model Properties:\n" + text += "x: " + properties.position.x.toFixed(this.decimals) + "\n" + text += "y: " + properties.position.y.toFixed(this.decimals) + "\n" + text += "z: " + properties.position.z.toFixed(this.decimals) + "\n" + text += "pitch: " + angles.x.toFixed(this.decimals) + "\n" + text += "yaw: " + angles.y.toFixed(this.decimals) + "\n" + text += "roll: " + angles.z.toFixed(this.decimals) + "\n" + text += "Scale: " + 2 * properties.radius.toFixed(this.decimals) + "\n" + + Overlays.editOverlay(this.textOverlay, { text: text }); + } + + this.cleanup = function() { + Overlays.deleteOverlay(this.textOverlay); + } +} +var tooltip = new Tooltip(); + function mousePressEvent(event) { - if (altIsPressed) { + if (event.isAlt) { return; } @@ -786,6 +841,8 @@ function mousePressEvent(event) { selectedModelProperties.glowLevel = 0.0; print("Clicked on " + selectedModelID.id + " " + modelSelected); + tooltip.updateText(selectedModelProperties); + tooltip.show(true); } } @@ -794,7 +851,7 @@ var oldModifier = 0; var modifier = 0; var wasShifted = false; function mouseMoveEvent(event) { - if (altIsPressed) { + if (event.isAlt) { return; } @@ -886,26 +943,54 @@ function mouseMoveEvent(event) { break; case 3: // Let's rotate - var rotation = Quat.fromVec3Degrees({ x: event.y - mouseLastPosition.y, y: event.x - mouseLastPosition.x, z: 0 }); - if (event.isShifted) { - rotation = Quat.fromVec3Degrees({ x: event.y - mouseLastPosition.y, y: 0, z: mouseLastPosition.x - event.x }); + if (somethingChanged) { + selectedModelProperties.oldRotation.x = selectedModelProperties.modelRotation.x; + selectedModelProperties.oldRotation.y = selectedModelProperties.modelRotation.y; + selectedModelProperties.oldRotation.z = selectedModelProperties.modelRotation.z; + selectedModelProperties.oldRotation.w = selectedModelProperties.modelRotation.w; + mouseLastPosition.x = event.x; + mouseLastPosition.y = event.y; + somethingChanged = false; } - var newRotation = Quat.multiply(orientation, rotation); - newRotation = Quat.multiply(newRotation, Quat.inverse(orientation)); - selectedModelProperties.modelRotation = Quat.multiply(newRotation, selectedModelProperties.oldRotation); + var pixelPerDegrees = windowDimensions.y / (1 * 360); // the entire height of the window allow you to make 2 full rotations + + var STEP = 15; + var delta = Math.floor((event.x - mouseLastPosition.x) / pixelPerDegrees); + + if (!event.isShifted) { + delta = Math.floor(delta / STEP) * STEP; + } + + var rotation = Quat.fromVec3Degrees({ + x: (!zIsPressed && xIsPressed) ? delta : 0, // z is pressed + y: (!zIsPressed && !xIsPressed) ? delta : 0, // x is pressed + z: (zIsPressed && !xIsPressed) ? delta : 0 // neither is pressed + }); + rotation = Quat.multiply(selectedModelProperties.oldRotation, rotation); + + selectedModelProperties.modelRotation.x = rotation.x; + selectedModelProperties.modelRotation.y = rotation.y; + selectedModelProperties.modelRotation.z = rotation.z; + selectedModelProperties.modelRotation.w = rotation.w; break; } Models.editModel(selectedModelID, selectedModelProperties); + tooltip.updateText(selectedModelProperties); } + function mouseReleaseEvent(event) { - if (altIsPressed) { + if (event.isAlt) { return; } + if (modelSelected) { + tooltip.show(false); + } + modelSelected = false; glowedModelID.id = -1; @@ -943,6 +1028,7 @@ function scriptEnding() { rightController.cleanup(); toolBar.cleanup(); cleanupModelMenus(); + tooltip.cleanup(); } Script.scriptEnding.connect(scriptEnding); @@ -974,16 +1060,36 @@ Menu.menuItemEvent.connect(function(menuItem){ } }); + // handling of inspect.js concurrence -altIsPressed = false; +var zIsPressed = false; +var xIsPressed = false; +var somethingChanged = false; Controller.keyPressEvent.connect(function(event) { - if (event.text == "ALT") { - altIsPressed = true; + if ((event.text == "z" || event.text == "Z") && !zIsPressed) { + zIsPressed = true; + somethingChanged = true; + } + if ((event.text == "x" || event.text == "X") && !xIsPressed) { + xIsPressed = true; + somethingChanged = true; + } + + // resets model orientation when holding with mouse + if (event.text == "r" && modelSelected) { + selectedModelProperties.modelRotation = Quat.fromVec3Degrees({ x: 0, y: 0, z: 0 }); + Models.editModel(selectedModelID, selectedModelProperties); + tooltip.updateText(selectedModelProperties); + somethingChanged = true; } }); Controller.keyReleaseEvent.connect(function(event) { - if (event.text == "ALT") { - altIsPressed = false; + if (event.text == "z" || event.text == "Z") { + zIsPressed = false; + somethingChanged = true; } -}); - + if (event.text == "x" || event.text == "X") { + xIsPressed = false; + somethingChanged = true; + } +}); \ No newline at end of file From 97dd6952132e52872ea1c21db146a7027a1a7dbb Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 17 Jun 2014 18:32:45 -0700 Subject: [PATCH 102/102] if model takes too much angular size, don't select it --- examples/editModels.js | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 3cd727570c..1ab6ce0008 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -34,6 +34,8 @@ var LASER_COLOR = { red: 255, green: 0, blue: 0 }; var LASER_LENGTH_FACTOR = 500 ; +var MAX_ANGULAR_SIZE = 45; + var LEFT = 0; var RIGHT = 1; @@ -276,6 +278,11 @@ function controller(wichSide) { var d = Vec3.length(Vec3.subtract(P, X)); if (0 < x && x < LASER_LENGTH_FACTOR) { + if (2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14 > MAX_ANGULAR_SIZE) { + print("Angular size too big: " + 2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14); + return { valid: false }; + } + return { valid: true, x: x, y: y, z: z }; } return { valid: false }; @@ -526,9 +533,13 @@ function controller(wichSide) { if (isLocked(newProperties)) { print("Model locked " + newProperties.id); } else { + var check = this.checkModel(newProperties); + if (!check.valid) { + return; + } + this.grab(newModel, newProperties); - var check = this.checkModel(newProperties); this.x = check.x; this.y = check.y; this.z = check.z; @@ -815,12 +826,16 @@ function mousePressEvent(event) { var d = Vec3.length(Vec3.subtract(P, X)); if (0 < x && x < LASER_LENGTH_FACTOR) { - modelSelected = true; - selectedModelID = foundModel; - selectedModelProperties = properties; - - orientation = MyAvatar.orientation; - intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation)); + if (2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14 < MAX_ANGULAR_SIZE) { + modelSelected = true; + selectedModelID = foundModel; + selectedModelProperties = properties; + + orientation = MyAvatar.orientation; + intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation)); + } else { + print("Angular size too big: " + 2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14); + } } } }