From c3c9c0cb10694b10e09ec80c4e0adf627f3dfa79 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 13 Jul 2016 14:37:41 -0700 Subject: [PATCH 01/25] update so HTML5 media apis work again --- interface/resources/qml/Browser.qml | 5 ++++- interface/resources/qml/controls-uit/WebView.qml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index e9b99331c7..e397242a0c 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -1,4 +1,4 @@ -import QtQuick 2.3 +import QtQuick 2.5 import QtQuick.Controls 1.2 import QtWebEngine 1.1 @@ -131,6 +131,9 @@ ScrollingWindow { anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right + onFeaturePermissionRequested: { + grantFeaturePermission(securityOrigin, feature, true); + } onLoadingChanged: { if (loadRequest.status === WebEngineView.LoadSucceededStatus) { addressBar.text = loadRequest.url diff --git a/interface/resources/qml/controls-uit/WebView.qml b/interface/resources/qml/controls-uit/WebView.qml index e6005d5dcf..acd0e1ae12 100644 --- a/interface/resources/qml/controls-uit/WebView.qml +++ b/interface/resources/qml/controls-uit/WebView.qml @@ -15,7 +15,7 @@ WebEngineView { id: root property var newUrl; - profile.httpUserAgent: "Mozilla/5.0 Chrome (HighFidelityInterface)" + profile.httpUserAgent: "Mozilla/5.0 Chrome/38.0 (HighFidelityInterface)" Component.onCompleted: { console.log("Connecting JS messaging to Hifi Logging") From aacddcf66d360b8a143036e9bb63628d6a566d8c Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 13 Jul 2016 15:02:51 -0700 Subject: [PATCH 02/25] fix pop ups --- interface/resources/qml/Browser.qml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index e397242a0c..3057cd3330 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -142,7 +142,11 @@ ScrollingWindow { onIconChanged: { console.log("New icon: " + icon) } - + onNewViewRequested:{ + var component = Qt.createComponent("Browser.qml"); + var newWindow = component.createObject(desktop); + request.openIn(newWindow.webView) + } //profile: desktop.browserProfile } From faba1a4b4ef82fe6f31c0247942671349bb07a7d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 10 Jun 2016 01:23:57 -0700 Subject: [PATCH 03/25] Fix UI elements triggered by WebViews --- .../QtWebEngine/UIDelegates/AlertDialog.qml | 25 +++++++ .../QtWebEngine/UIDelegates/ConfirmDialog.qml | 31 +++++++++ .../QtWebEngine/UIDelegates/FilePicker.qml | 39 +++++++++++ .../QtWebEngine/UIDelegates/Menu.qml | 68 +++++++++++++++++++ .../QtWebEngine/UIDelegates/MenuItem.qml | 39 +++++++++++ .../QtWebEngine/UIDelegates/MenuSeparator.qml | 6 ++ .../QtWebEngine/UIDelegates/MessageBubble.qml | 4 ++ .../QtWebEngine/UIDelegates/PromptDialog.qml | 42 ++++++++++++ .../resources/QtWebEngine/UIDelegates/qmldir | 8 +++ interface/resources/qml/Browser.qml | 1 + libraries/gl/src/gl/OffscreenQmlSurface.cpp | 5 ++ libraries/ui/src/FileDialogHelper.cpp | 7 ++ libraries/ui/src/FileDialogHelper.h | 1 + tests/ui/qml/main.qml | 9 ++- tests/ui/qmlscratch.pro | 2 + tests/ui/src/main.cpp | 42 ++++++++++-- 16 files changed, 320 insertions(+), 9 deletions(-) create mode 100644 interface/resources/QtWebEngine/UIDelegates/AlertDialog.qml create mode 100644 interface/resources/QtWebEngine/UIDelegates/ConfirmDialog.qml create mode 100644 interface/resources/QtWebEngine/UIDelegates/FilePicker.qml create mode 100644 interface/resources/QtWebEngine/UIDelegates/Menu.qml create mode 100644 interface/resources/QtWebEngine/UIDelegates/MenuItem.qml create mode 100644 interface/resources/QtWebEngine/UIDelegates/MenuSeparator.qml create mode 100644 interface/resources/QtWebEngine/UIDelegates/MessageBubble.qml create mode 100644 interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml create mode 100644 interface/resources/QtWebEngine/UIDelegates/qmldir diff --git a/interface/resources/QtWebEngine/UIDelegates/AlertDialog.qml b/interface/resources/QtWebEngine/UIDelegates/AlertDialog.qml new file mode 100644 index 0000000000..e6dc03fa55 --- /dev/null +++ b/interface/resources/QtWebEngine/UIDelegates/AlertDialog.qml @@ -0,0 +1,25 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.0 + +import "../../qml/dialogs" + +QtObject { + id: root + signal accepted; + property var text; + + property var messageDialogBuilder: Component { MessageDialog { } } + + function open() { + console.log("prompt text " + text) + var dialog = messageDialogBuilder.createObject(desktop, { + text: root.text + }); + + dialog.selected.connect(function(button){ + accepted(); + dialog.destroy(); + }); + } +} diff --git a/interface/resources/QtWebEngine/UIDelegates/ConfirmDialog.qml b/interface/resources/QtWebEngine/UIDelegates/ConfirmDialog.qml new file mode 100644 index 0000000000..97d83f4304 --- /dev/null +++ b/interface/resources/QtWebEngine/UIDelegates/ConfirmDialog.qml @@ -0,0 +1,31 @@ +import QtQuick 2.4 + +import QtQuick.Dialogs 1.1 as OriginalDialogs + +import "../../qml/dialogs" + +QtObject { + id: root + signal accepted; + signal rejected; + property var text; + + property var messageDialogBuilder: Component { MessageDialog { } } + + function open() { + var dialog = messageDialogBuilder.createObject(desktop, { + text: root.text, + icon: OriginalDialogs.StandardIcon.Question, + buttons: OriginalDialogs.StandardButton.Ok | OriginalDialogs.StandardButton.Cancel + }); + + dialog.selected.connect(function(button){ + if (button === OriginalDialogs.StandardButton.Ok) { + accepted() + } else { + rejected(); + } + dialog.destroy(); + }); + } +} diff --git a/interface/resources/QtWebEngine/UIDelegates/FilePicker.qml b/interface/resources/QtWebEngine/UIDelegates/FilePicker.qml new file mode 100644 index 0000000000..cb6552b075 --- /dev/null +++ b/interface/resources/QtWebEngine/UIDelegates/FilePicker.qml @@ -0,0 +1,39 @@ +import QtQuick 2.4 +import QtQuick.Dialogs 1.1 +import QtQuick.Controls 1.4 + +import "../../qml/dialogs" + +QtObject { + id: root + signal filesSelected(var fileList); + signal rejected(); + property var text; + property url fileUrl; + property var fileUrls; + property url folder; + property var nameFilters; + property bool selectExisting; + property bool selectFolder; + property bool selectMultiple; + property string selectedNameFilter; + property string title; + + property var fileDialogBuilder: Component { FileDialog { } } + + function open() { + var foo = root; + var dialog = fileDialogBuilder.createObject(desktop, { + }); + + dialog.canceled.connect(function(){ + root.filesSelected([]); + dialog.destroy(); + }); + + dialog.selectedFile.connect(function(file){ + root.filesSelected(fileDialogHelper.urlToList(file)); + }); + } +} + diff --git a/interface/resources/QtWebEngine/UIDelegates/Menu.qml b/interface/resources/QtWebEngine/UIDelegates/Menu.qml new file mode 100644 index 0000000000..21c5e71394 --- /dev/null +++ b/interface/resources/QtWebEngine/UIDelegates/Menu.qml @@ -0,0 +1,68 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 as Controls + +import "../../qml/menus" +import "../../qml/controls-uit" +import "../../qml/styles-uit" + +Item { + id: menu + HifiConstants { id: hifi } + signal done() + implicitHeight: column.height + implicitWidth: column.width + + Rectangle { + id: background + anchors { + fill: parent + margins: -16 + } + radius: hifi.dimensions.borderRadius + border.width: hifi.dimensions.borderWidth + border.color: hifi.colors.lightGrayText80 + color: hifi.colors.faintGray80 + } + + MouseArea { + id: closer + width: 8192 + height: 8192 + x: -4096 + y: -4096 + propagateComposedEvents: true + acceptedButtons: "AllButtons" + onClicked: { + menu.done(); + mouse.accepted = false; + } + } + + Column { + id: column + } + + function popup() { + var position = Reticle.position; + var localPosition = menu.parent.mapFromItem(desktop, position.x, position.y); + x = localPosition.x + y = localPosition.y + console.log("Popup at " + x + " x " + y) + var moveChildren = []; + for (var i = 0; i < children.length; ++i) { + var child = children[i]; + if (child.objectName !== "MenuItem") { + continue; + } + moveChildren.push(child); + } + + for (i = 0; i < moveChildren.length; ++i) { + child = moveChildren[i]; + child.parent = column; + child.menu = menu + } + } + +} + diff --git a/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml b/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml new file mode 100644 index 0000000000..561a8926e1 --- /dev/null +++ b/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml @@ -0,0 +1,39 @@ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 as Controls + +import "../../qml/controls-uit" +import "../../qml/styles-uit" + +Item { + id: root + objectName: "MenuItem" + + property alias text: label.text + property var menu; + property var shortcut; + signal triggered(); + + HifiConstants { id: hifi } + + implicitHeight: 2 * label.implicitHeight + implicitWidth: 2 * hifi.dimensions.menuPadding.x + label.width + + RalewaySemiBold { + id: label + size: hifi.fontSizes.rootMenu + anchors.left: parent.left + anchors.leftMargin: hifi.dimensions.menuPadding.x + verticalAlignment: Text.AlignVCenter + color: enabled ? hifi.colors.baseGrayShadow : hifi.colors.baseGrayShadow50 + enabled: root.enabled + } + + MouseArea { + anchors.fill: parent + onClicked: { + root.triggered(); + menu.done(); + } + } +} diff --git a/interface/resources/QtWebEngine/UIDelegates/MenuSeparator.qml b/interface/resources/QtWebEngine/UIDelegates/MenuSeparator.qml new file mode 100644 index 0000000000..95d3b0a97b --- /dev/null +++ b/interface/resources/QtWebEngine/UIDelegates/MenuSeparator.qml @@ -0,0 +1,6 @@ +import QtQuick 2.5 + +Item { + width: 100 + height: 20 +} diff --git a/interface/resources/QtWebEngine/UIDelegates/MessageBubble.qml b/interface/resources/QtWebEngine/UIDelegates/MessageBubble.qml new file mode 100644 index 0000000000..1a45563bf7 --- /dev/null +++ b/interface/resources/QtWebEngine/UIDelegates/MessageBubble.qml @@ -0,0 +1,4 @@ +import QtQuick 2.5 + +Item { +} diff --git a/interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml b/interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml new file mode 100644 index 0000000000..01d3262bc0 --- /dev/null +++ b/interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml @@ -0,0 +1,42 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.0 + +import "../../qml/controls-uit" +import "../../qml/styles-uit" +import "../../qml/dialogs" + +QtObject { + id: root + signal input(string text); + signal accepted; + signal rejected; + signal closing(var close) + + property var titleWidth; + property var text; + property var prompt; + + property var inputDialogBuilder: Component { QueryDialog { } } + + function open() { + console.log("prompt text " + text) + console.log("prompt prompt " + prompt) + + var dialog = inputDialogBuilder.createObject(desktop, { + label: root.text, + current: root.prompt + }); + + dialog.selected.connect(function(result){ + root.input(dialog.result) + root.accepted(); + dialog.destroy(); + }); + + dialog.canceled.connect(function(){ + root.rejected(); + dialog.destroy(); + }); + } +} diff --git a/interface/resources/QtWebEngine/UIDelegates/qmldir b/interface/resources/QtWebEngine/UIDelegates/qmldir new file mode 100644 index 0000000000..69ebe1bad7 --- /dev/null +++ b/interface/resources/QtWebEngine/UIDelegates/qmldir @@ -0,0 +1,8 @@ +module QtWebEngine.UIDelegates +AlertDialog 1.0 AlertDialog.qml +ConfirmDialog 1.0 ConfirmDialog.qml +FilePicker 1.0 FilePicker.qml +PromptDialog 1.0 PromptDialog.qml +Menu 1.0 Menu.qml +MenuItem 1.0 MenuItem.qml +MenuSeparator 1.0 MenuSeparator.qml diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index e9b99331c7..ff8dd0987c 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -16,6 +16,7 @@ ScrollingWindow { destroyOnHidden: true width: 800 height: 600 + property alias url: webview.url property alias webView: webview x: 100 y: 100 diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index b4a9080ee8..8c167fafdc 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "OffscreenGLCanvas.h" #include "GLEscrow.h" @@ -400,6 +401,10 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { // Create a QML engine. _qmlEngine = new QQmlEngine; + + auto importList = _qmlEngine->importPathList(); + importList.insert(importList.begin(), PathUtils::resourcesPath()); + _qmlEngine->setImportPathList(importList); if (!_qmlEngine->incubationController()) { _qmlEngine->setIncubationController(_renderer->_quickWindow->incubationController()); } diff --git a/libraries/ui/src/FileDialogHelper.cpp b/libraries/ui/src/FileDialogHelper.cpp index c9a8953f01..3a12e054df 100644 --- a/libraries/ui/src/FileDialogHelper.cpp +++ b/libraries/ui/src/FileDialogHelper.cpp @@ -108,3 +108,10 @@ QStringList FileDialogHelper::drives() { void FileDialogHelper::openDirectory(const QString& path) { QDesktopServices::openUrl(path); } + +QList FileDialogHelper::urlToList(const QUrl& url) { + QList results; + results.push_back(url); + return results; +} + diff --git a/libraries/ui/src/FileDialogHelper.h b/libraries/ui/src/FileDialogHelper.h index 832a5a6951..6058f8f7bb 100644 --- a/libraries/ui/src/FileDialogHelper.h +++ b/libraries/ui/src/FileDialogHelper.h @@ -58,6 +58,7 @@ public: Q_INVOKABLE bool validFolder(const QString& path); Q_INVOKABLE QUrl pathToUrl(const QString& path); Q_INVOKABLE QUrl saveHelper(const QString& saveText, const QUrl& currentFolder, const QStringList& selectionFilters); + Q_INVOKABLE QList urlToList(const QUrl& url); Q_INVOKABLE void openDirectory(const QString& path); }; diff --git a/tests/ui/qml/main.qml b/tests/ui/qml/main.qml index 8ca9399b74..607bd624a1 100644 --- a/tests/ui/qml/main.qml +++ b/tests/ui/qml/main.qml @@ -13,6 +13,7 @@ import "../../../interface/resources/qml/styles-uit" ApplicationWindow { id: appWindow + objectName: "MainWindow" visible: true width: 1280 height: 800 @@ -93,9 +94,6 @@ ApplicationWindow { onClicked: testButtons.lastButton.visible = !testButtons.lastButton.visible } - - - // Error alerts /* Button { @@ -350,6 +348,11 @@ ApplicationWindow { } */ + Browser { + url: "http://s3.amazonaws.com/DreamingContent/testUiDelegates.html" + } + + Window { id: blue closable: true diff --git a/tests/ui/qmlscratch.pro b/tests/ui/qmlscratch.pro index 151893de2f..5c9b91ee52 100644 --- a/tests/ui/qmlscratch.pro +++ b/tests/ui/qmlscratch.pro @@ -16,6 +16,8 @@ QML_IMPORT_PATH = ../../interface/resources/qml DISTFILES += \ qml/*.qml \ + ../../interface/resources/QtWebEngine/UIDelegates/original/*.qml \ + ../../interface/resources/QtWebEngine/UIDelegates/*.qml \ ../../interface/resources/qml/*.qml \ ../../interface/resources/qml/controls/*.qml \ ../../interface/resources/qml/controls-uit/*.qml \ diff --git a/tests/ui/src/main.cpp b/tests/ui/src/main.cpp index e3cf37ba04..312b5f3823 100644 --- a/tests/ui/src/main.cpp +++ b/tests/ui/src/main.cpp @@ -33,6 +33,28 @@ protected: const QString _name; }; +class Reticle : public QObject { + Q_OBJECT + Q_PROPERTY(QPoint position READ getPosition CONSTANT) +public: + + Reticle(QObject* parent) : QObject(parent) { + } + + QPoint getPosition() { + if (!_window) { + return QPoint(0, 0); + } + return _window->mapFromGlobal(QCursor::pos()); + } + + void setWindow(QWindow* window) { + _window = window; + } + +private: + QWindow* _window{nullptr}; +}; QString getRelativeDir(const QString& relativePath = ".") { QDir path(__FILE__); path.cdUp(); @@ -61,10 +83,8 @@ void setChild(QQmlApplicationEngine& engine, const char* name) { qWarning() << "Could not find object named " << name; } -void addImportPath(QQmlApplicationEngine& engine, const QString& relativePath) { - QString resolvedPath = getRelativeDir("../qml"); - QUrl resolvedUrl = QUrl::fromLocalFile(resolvedPath); - resolvedPath = resolvedUrl.toString(); +void addImportPath(QQmlApplicationEngine& engine, const QString& relativePath, bool insert = false) { + QString resolvedPath = getRelativeDir(relativePath); engine.addImportPath(resolvedPath); } @@ -79,8 +99,9 @@ int main(int argc, char *argv[]) { qmlRegisterType("Hifi", 1, 0, "Preference"); QQmlApplicationEngine engine; - addImportPath(engine, "../qml"); - addImportPath(engine, "../../../interface/resources/qml"); + addImportPath(engine, "qml"); + addImportPath(engine, "../../interface/resources/qml"); + addImportPath(engine, "../../interface/resources"); engine.load(QUrl(QStringLiteral("qml/Stubs.qml"))); setChild(engine, "offscreenFlags"); @@ -99,6 +120,15 @@ int main(int argc, char *argv[]) { //engine.load(QUrl(QStringLiteral("qrc:/qml/gallery/main.qml"))); engine.load(QUrl(QStringLiteral("qml/main.qml"))); + for (QObject* rootObject : engine.rootObjects()) { + if (rootObject->objectName() == "MainWindow") { + Reticle* reticle = new Reticle(rootObject); + reticle->setWindow((QWindow*)rootObject); + engine.rootContext()->setContextProperty("Reticle", reticle); + engine.rootContext()->setContextProperty("Window", rootObject); + break; + } + } return app.exec(); } From 2429b82b5e92dc83f9f5e77e28e95e896917f80b Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Thu, 21 Jul 2016 17:17:08 -0700 Subject: [PATCH 04/25] Support for animGraphUrl override in FST file. --- interface/src/avatar/MyAvatar.cpp | 53 ++++++++++++++++--- interface/src/avatar/MyAvatar.h | 12 +++-- interface/src/ui/PreferencesDialog.cpp | 4 +- .../src/model-networking/ModelCache.cpp | 14 +++++ .../src/model-networking/ModelCache.h | 3 ++ 5 files changed, 72 insertions(+), 14 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6a69ee9a9a..24c80cc2fd 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -687,7 +687,9 @@ void MyAvatar::saveData() { _fullAvatarURLFromPreferences.toString()); settings.setValue("fullAvatarModelName", _fullAvatarModelName); - settings.setValue("animGraphURL", _animGraphUrl); + + QUrl animGraphUrl = _prefOverrideAnimGraphUrl.get(); + settings.setValue("animGraphURL", animGraphUrl); settings.beginWriteArray("attachmentData"); for (int i = 0; i < _attachmentData.size(); i++) { @@ -800,7 +802,7 @@ void MyAvatar::loadData() { _targetScale = loadSetting(settings, "scale", 1.0f); setScale(glm::vec3(_targetScale)); - _animGraphUrl = settings.value("animGraphURL", "").toString(); + _prefOverrideAnimGraphUrl.set(QUrl(settings.value("animGraphURL", "").toString())); _fullAvatarURLFromPreferences = settings.value("fullAvatarURL", AvatarData::defaultFullAvatarModelUrl()).toUrl(); _fullAvatarModelName = settings.value("fullAvatarModelName", DEFAULT_FULL_AVATAR_MODEL_NAME).toString(); @@ -1379,21 +1381,55 @@ void MyAvatar::initHeadBones() { } } +QUrl MyAvatar::getAnimGraphOverrideUrl() const { + return _prefOverrideAnimGraphUrl.get(); +} + +void MyAvatar::setAnimGraphOverrideUrl(QUrl value) { + _prefOverrideAnimGraphUrl.set(value); + if (!value.isEmpty()) { + setAnimGraphUrl(value); + } else { + initAnimGraph(); + } +} + +QUrl MyAvatar::getAnimGraphUrl() const { + return _currentAnimGraphUrl.get(); +} + void MyAvatar::setAnimGraphUrl(const QUrl& url) { - if (_animGraphUrl == url) { + + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setAnimGraphUrl", Q_ARG(QUrl, url)); + return; + } + + if (_currentAnimGraphUrl.get() == url) { return; } destroyAnimGraph(); _skeletonModel->reset(); // Why is this necessary? Without this, we crash in the next render. - _animGraphUrl = url; - initAnimGraph(); + + _currentAnimGraphUrl.set(url); + _rig->initAnimGraph(url); + + _bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. + updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes } void MyAvatar::initAnimGraph() { - auto graphUrl =_animGraphUrl.isEmpty() ? - QUrl::fromLocalFile(PathUtils::resourcesPath() + "avatar/avatar-animation.json") : - QUrl(_animGraphUrl); + QUrl graphUrl; + if (!_prefOverrideAnimGraphUrl.get().isEmpty()) { + graphUrl = _prefOverrideAnimGraphUrl.get(); + } else if (!_fstAnimGraphOverrideUrl.isEmpty()) { + graphUrl = _fstAnimGraphOverrideUrl; + } else { + graphUrl = QUrl::fromLocalFile(PathUtils::resourcesPath() + "avatar/avatar-animation.json"); + } + _rig->initAnimGraph(graphUrl); + _currentAnimGraphUrl.set(graphUrl); _bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes @@ -1411,6 +1447,7 @@ void MyAvatar::postUpdate(float deltaTime) { if (_skeletonModel->initWhenReady(scene)) { initHeadBones(); _skeletonModel->setCauterizeBoneSet(_headBoneSet); + _fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl(); initAnimGraph(); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index a21922f1b1..97cb2b35fa 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -288,8 +288,6 @@ public slots: Q_INVOKABLE void updateMotionBehaviorFromMenu(); - Q_INVOKABLE QUrl getAnimGraphUrl() const { return _animGraphUrl; } - void setEnableDebugDrawDefaultPose(bool isEnabled); void setEnableDebugDrawAnimPose(bool isEnabled); void setEnableDebugDrawPosition(bool isEnabled); @@ -299,7 +297,11 @@ public slots: void setEnableMeshVisible(bool isEnabled); void setUseAnimPreAndPostRotations(bool isEnabled); void setEnableInverseKinematics(bool isEnabled); - Q_INVOKABLE void setAnimGraphUrl(const QUrl& url); + + QUrl getAnimGraphOverrideUrl() const; // thread-safe + void setAnimGraphOverrideUrl(QUrl value); // thread-safe + QUrl getAnimGraphUrl() const; // thread-safe + void setAnimGraphUrl(const QUrl& url); // thread-safe glm::vec3 getPositionForAudio(); glm::quat getOrientationForAudio(); @@ -403,7 +405,9 @@ private: // Avatar Preferences QUrl _fullAvatarURLFromPreferences; QString _fullAvatarModelName; - QUrl _animGraphUrl {""}; + ThreadSafeValueCache _currentAnimGraphUrl; + ThreadSafeValueCache _prefOverrideAnimGraphUrl; + QUrl _fstAnimGraphOverrideUrl; bool _useSnapTurn { true }; bool _clearOverlayWhenMoving { true }; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 4007c940c3..7fdafc9bda 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -161,8 +161,8 @@ void setupPreferences() { preferences->addPreference(preference); } { - auto getter = [=]()->QString { return myAvatar->getAnimGraphUrl().toString(); }; - auto setter = [=](const QString& value) { myAvatar->setAnimGraphUrl(value); }; + auto getter = [=]()->QString { return myAvatar->getAnimGraphOverrideUrl().toString(); }; + auto setter = [=](const QString& value) { myAvatar->setAnimGraphOverrideUrl(QUrl(value)); }; auto preference = new EditPreference(AVATAR_TUNING, "Avatar animation JSON", getter, setter); preference->setPlaceholderText("default"); preferences->addPreference(preference); diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 4885186e47..26798070a6 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -67,6 +67,18 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) { _textureBaseUrl = resolveTextureBaseUrl(url, _url.resolved(texdir)); } + auto animGraphVariant = mapping.value("animGraphUrl"); + if (animGraphVariant.isValid()) { + QUrl fstUrl(animGraphVariant.toString()); + if (fstUrl.isValid()) { + _animGraphOverrideUrl = _url.resolved(fstUrl); + } else { + _animGraphOverrideUrl = QUrl(); + } + } else { + _animGraphOverrideUrl = QUrl(); + } + auto modelCache = DependencyManager::get(); GeometryExtra extra{ mapping, _textureBaseUrl }; @@ -284,6 +296,8 @@ Geometry::Geometry(const Geometry& geometry) { for (const auto& material : geometry._materials) { _materials.push_back(std::make_shared(*material)); } + + _animGraphOverrideUrl = geometry._animGraphOverrideUrl; } void Geometry::setTextures(const QVariantMap& textureMap) { diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index aa3ea78db3..4a0a921a04 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -52,6 +52,7 @@ public: void setTextures(const QVariantMap& textureMap); virtual bool areTexturesLoaded() const; + const QUrl& getAnimGraphOverrideUrl() const { return _animGraphOverrideUrl; } protected: friend class GeometryMappingResource; @@ -64,6 +65,8 @@ protected: // Copied to each geometry, mutable throughout lifetime via setTextures NetworkMaterials _materials; + QUrl _animGraphOverrideUrl; + private: mutable bool _areTexturesLoaded { false }; }; From 4cbf804dd4cf55add36c3d8caacd55058b63ca2b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 21 Jul 2016 22:32:13 -0700 Subject: [PATCH 05/25] More work on perf testing --- tests/render-perf/src/main.cpp | 66 ++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index f3e3271a5d..ace637e92a 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -143,6 +144,19 @@ public: } }; +static QString toHumanSize(size_t size, size_t maxUnit = std::numeric_limits::max()) { + static const std::vector SUFFIXES{ { "B", "KB", "MB", "GB", "TB", "PB" } }; + const size_t maxIndex = std::min(maxUnit, SUFFIXES.size() - 1); + size_t suffixIndex = 0; + + while (suffixIndex < maxIndex && size > 1024) { + size >>= 10; + ++suffixIndex; + } + + return QString("%1 %2").arg(size).arg(SUFFIXES[suffixIndex]); +} + // Create a simple OpenGL window that renders text in various ways @@ -211,6 +225,9 @@ public: AbstractViewStateInterface::setInstance(this); _octree = DependencyManager::set(false, this, nullptr); _octree->init(); + // Prevent web entities from rendering + REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, WebEntityItem::factory) + DependencyManager::set(_octree->getTree()); getEntities()->setViewFrustum(_viewFrustum); auto nodeList = DependencyManager::get(); @@ -296,6 +313,10 @@ protected: reloadScene(); return; + case Qt::Key_F4: + toggleStereo(); + return; + case Qt::Key_F5: goTo(); return; @@ -365,6 +386,17 @@ private: renderArgs.setViewFrustum(_viewFrustum); + renderArgs._context->enableStereo(_stereoEnabled); + if (_stereoEnabled) { + mat4 eyeOffsets[2]; + mat4 eyeProjections[2]; + for (size_t i = 0; i < 2; ++i) { + eyeProjections[i] = _viewFrustum.getProjection(); + } + renderArgs._context->setStereoProjections(eyeProjections); + renderArgs._context->setStereoViews(eyeOffsets); + } + // Final framebuffer that will be handled to the display-plugin { auto finalFramebuffer = framebufferCache->getFramebuffer(); @@ -388,7 +420,7 @@ private: } { - _textOverlay->render(); + //_textOverlay->render(); } _context.swapBuffers(this); @@ -429,6 +461,9 @@ private: const qint64& now; }; + + + void updateText() { //qDebug() << "FPS " << fps.rate(); { @@ -438,6 +473,11 @@ private: infoTextBlock.push_back({ vec2(100, 10), std::to_string((uint32_t)_fps), TextOverlay::alignLeft }); infoTextBlock.push_back({ vec2(98, 30), "Culling: ", TextOverlay::alignRight }); infoTextBlock.push_back({ vec2(100, 30), _cullingEnabled ? "Enabled" : "Disabled", TextOverlay::alignLeft }); + + setTitle(QString("FPS %1 Culling %2 TextureMemory GPU %3 CPU %4") + .arg(_fps).arg(_cullingEnabled) + .arg(toHumanSize(gpu::Context::getTextureGPUMemoryUsage(), 2)) + .arg(toHumanSize(gpu::Texture::getTextureCPUMemoryUsage(), 2))); } _textOverlay->beginTextUpdate(); @@ -561,14 +601,29 @@ private: void importScene(const QString& fileName) { auto assetClient = DependencyManager::get(); QFileInfo fileInfo(fileName); - //assetClient->loadLocalMappings(fileInfo.absolutePath() + "/" + fileInfo.baseName() + ".atp"); + QString atpPath = fileInfo.absolutePath() + "/" + fileInfo.baseName() + ".atp"; + qDebug() << atpPath; + QFileInfo atpPathInfo(atpPath); + if (atpPathInfo.exists()) { + QString atpUrl = QUrl::fromLocalFile(atpPath).toString(); + ResourceManager::setUrlPrefixOverride("atp:/", atpUrl + "/"); + } _settings.setValue(LAST_SCENE_KEY, fileName); _octree->clear(); _octree->getTree()->readFromURL(fileName); } void importScene() { - QString fileName = QFileDialog::getOpenFileName(nullptr, tr("Open File"), "/home", tr("Hifi Exports (*.json *.svo)")); + auto lastScene = _settings.value(LAST_SCENE_KEY); + QString openDir; + if (lastScene.isValid()) { + QFileInfo lastSceneInfo(lastScene.toString()); + if (lastSceneInfo.absoluteDir().exists()) { + openDir = lastSceneInfo.absolutePath(); + } + } + + QString fileName = QFileDialog::getOpenFileName(nullptr, tr("Open File"), openDir, tr("Hifi Exports (*.json *.svo)")); if (fileName.isNull()) { return; } @@ -617,6 +672,10 @@ private: _cullingEnabled = !_cullingEnabled; } + void toggleStereo() { + _stereoEnabled = !_stereoEnabled; + } + QSharedPointer getEntities() { return _octree; } @@ -665,6 +724,7 @@ private: float _fps { 0 }; TextOverlay* _textOverlay; bool _cullingEnabled { true }; + bool _stereoEnabled { false }; QSharedPointer _octree; }; From 1d3dc7b1959d565e29b6aa4df4158f86dd13c879 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 22 Jul 2016 09:19:13 -0700 Subject: [PATCH 06/25] remove particle beam and lights --- .../system/controllers/handControllerGrab.js | 193 +----------------- 1 file changed, 1 insertion(+), 192 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 5b78f068c8..2fe73db05e 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -149,10 +149,7 @@ var USE_OVERLAY_LINES_FOR_SEARCHING = true; var USE_ENTITY_LINES_FOR_MOVING = false; var USE_OVERLAY_LINES_FOR_MOVING = false; -var USE_PARTICLE_BEAM_FOR_MOVING = true; -var USE_SPOTLIGHT = false; -var USE_POINTLIGHT = false; var FORBIDDEN_GRAB_NAMES = ["Grab Debug Entity", "grab pointer"]; var FORBIDDEN_GRAB_TYPES = ['Unknown', 'Light', 'PolyLine', 'Zone']; @@ -617,11 +614,8 @@ function MyController(hand) { // for visualizations this.overlayLine = null; - this.particleBeamObject = null; // for lights - this.spotlight = null; - this.pointlight = null; this.overlayLine = null; this.searchSphere = null; @@ -843,88 +837,6 @@ function MyController(hand) { } }; - this.handleDistantParticleBeam = function (handPosition, objectPosition, color) { - - var handToObject = Vec3.subtract(objectPosition, handPosition); - var finalRotationObject = Quat.rotationBetween(Vec3.multiply(-1, Vec3.UP), handToObject); - var distance = Vec3.distance(handPosition, objectPosition); - var speed = distance * 3; - var spread = 0; - var lifespan = distance / speed; - - if (this.particleBeamObject === null) { - this.createParticleBeam(objectPosition, finalRotationObject, color, speed, spread, lifespan); - } else { - this.updateParticleBeam(objectPosition, finalRotationObject, color, speed, spread, lifespan); - } - }; - - this.createParticleBeam = function (positionObject, orientationObject, color, speed, spread, lifespan) { - - var particleBeamPropertiesObject = { - type: "ParticleEffect", - isEmitting: true, - position: positionObject, - visible: false, - lifetime: 60, - "name": "Particle Beam", - "color": color, - "maxParticles": 2000, - "lifespan": lifespan, - "emitRate": 1000, - "emitSpeed": speed, - "speedSpread": spread, - "emitOrientation": { - "x": -1, - "y": 0, - "z": 0, - "w": 1 - }, - "emitDimensions": { - "x": 0, - "y": 0, - "z": 0 - }, - "emitRadiusStart": 0.5, - "polarStart": 0, - "polarFinish": 0, - "azimuthStart": -3.1415927410125732, - "azimuthFinish": 3.1415927410125732, - "emitAcceleration": { - x: 0, - y: 0, - z: 0 - }, - "accelerationSpread": { - "x": 0, - "y": 0, - "z": 0 - }, - "particleRadius": 0.015, - "radiusSpread": 0.005, - "alpha": 1, - "alphaSpread": 0, - "alphaStart": 1, - "alphaFinish": 1, - "additiveBlending": 0, - "textures": "https://hifi-content.s3.amazonaws.com/alan/dev/textures/grabsprite-3.png" - }; - - this.particleBeamObject = Entities.addEntity(particleBeamPropertiesObject); - }; - - this.updateParticleBeam = function (positionObject, orientationObject, color, speed, spread, lifespan) { - Entities.editEntity(this.particleBeamObject, { - rotation: orientationObject, - position: positionObject, - visible: true, - color: color, - emitSpeed: speed, - speedSpread: spread, - lifespan: lifespan - }); - }; - this.evalLightWorldTransform = function (modelPos, modelRot) { var MODEL_LIGHT_POSITION = { @@ -945,75 +857,6 @@ function MyController(hand) { }; }; - this.handleSpotlight = function (parentID) { - var LIFETIME = 100; - - var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); - - var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); - var lightProperties = { - type: "Light", - isSpotlight: true, - dimensions: { - x: 2, - y: 2, - z: 20 - }, - parentID: parentID, - color: { - red: 255, - green: 255, - blue: 255 - }, - intensity: 2, - exponent: 0.3, - cutoff: 20, - lifetime: LIFETIME, - position: lightTransform.p - }; - - if (this.spotlight === null) { - this.spotlight = Entities.addEntity(lightProperties); - } else { - Entities.editEntity(this.spotlight, { - // without this, this light would maintain rotation with its parent - rotation: Quat.fromPitchYawRollDegrees(-90, 0, 0) - }); - } - }; - - this.handlePointLight = function (parentID) { - var LIFETIME = 100; - - var modelProperties = Entities.getEntityProperties(parentID, ['position', 'rotation']); - var lightTransform = this.evalLightWorldTransform(modelProperties.position, modelProperties.rotation); - - var lightProperties = { - type: "Light", - isSpotlight: false, - dimensions: { - x: 2, - y: 2, - z: 20 - }, - parentID: parentID, - color: { - red: 255, - green: 255, - blue: 255 - }, - intensity: 2, - exponent: 0.3, - cutoff: 20, - lifetime: LIFETIME, - position: lightTransform.p - }; - - if (this.pointlight === null) { - this.pointlight = Entities.addEntity(lightProperties); - } - }; - this.lineOff = function () { if (this.pointer !== null) { Entities.deleteEntity(this.pointer); @@ -1037,25 +880,6 @@ function MyController(hand) { } }; - this.particleBeamOff = function () { - if (this.particleBeamObject !== null) { - Entities.deleteEntity(this.particleBeamObject); - this.particleBeamObject = null; - } - }; - - this.turnLightsOff = function () { - if (this.spotlight !== null) { - Entities.deleteEntity(this.spotlight); - this.spotlight = null; - } - - if (this.pointlight !== null) { - Entities.deleteEntity(this.pointlight); - this.pointlight = null; - } - }; - this.turnOffVisualizations = function () { if (USE_ENTITY_LINES_FOR_SEARCHING === true || USE_ENTITY_LINES_FOR_MOVING === true) { this.lineOff(); @@ -1065,9 +889,7 @@ function MyController(hand) { this.overlayLineOff(); } - if (USE_PARTICLE_BEAM_FOR_MOVING === true) { - this.particleBeamOff(); - } + this.searchSphereOff(); restore2DMode(); @@ -1741,15 +1563,6 @@ function MyController(hand) { if (USE_OVERLAY_LINES_FOR_MOVING === true) { this.overlayLineOn(handPosition, grabbedProperties.position, INTERSECT_COLOR); } - if (USE_PARTICLE_BEAM_FOR_MOVING === true) { - this.handleDistantParticleBeam(handPosition, grabbedProperties.position, INTERSECT_COLOR); - } - if (USE_POINTLIGHT === true) { - this.handlePointLight(this.grabbedEntity); - } - if (USE_SPOTLIGHT === true) { - this.handleSpotlight(this.grabbedEntity); - } var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, this.currentObjectPosition)); var success = Entities.updateAction(this.grabbedEntity, this.actionID, { @@ -2154,7 +1967,6 @@ function MyController(hand) { }; this.release = function () { - this.turnLightsOff(); this.turnOffVisualizations(); var noVelocity = false; @@ -2207,9 +2019,6 @@ function MyController(hand) { this.cleanup = function () { this.release(); - Entities.deleteEntity(this.particleBeamObject); - Entities.deleteEntity(this.spotLight); - Entities.deleteEntity(this.pointLight); }; this.heartBeat = function (entityID) { From 54109c6c6f415fe3a478d0044ce637dfe3477d56 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 22 Jul 2016 09:39:38 -0700 Subject: [PATCH 07/25] remove all the old visualization methods --- .../system/controllers/handControllerGrab.js | 451 ++++++++++-------- scripts/system/controllers/teleport.js | 38 +- 2 files changed, 257 insertions(+), 232 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 2fe73db05e..e10e356e87 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -42,7 +42,11 @@ var HAND_HEAD_MIX_RATIO = 0.0; // 0 = only use hands for search/move. 1 = only var PICK_WITH_HAND_RAY = true; -var EQUIP_SPHERE_COLOR = { red: 179, green: 120, blue: 211 }; +var EQUIP_SPHERE_COLOR = { + red: 179, + green: 120, + blue: 211 +}; var EQUIP_SPHERE_ALPHA = 0.15; var EQUIP_SPHERE_SCALE_FACTOR = 0.65; @@ -52,8 +56,8 @@ var EQUIP_SPHERE_SCALE_FACTOR = 0.65; var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position -var DISTANCE_HOLDING_UNITY_MASS = 1200; // The mass at which the distance holding action timeframe is unmodified -var DISTANCE_HOLDING_UNITY_DISTANCE = 6; // The distance at which the distance holding action timeframe is unmodified +var DISTANCE_HOLDING_UNITY_MASS = 1200; // The mass at which the distance holding action timeframe is unmodified +var DISTANCE_HOLDING_UNITY_DISTANCE = 6; // The distance at which the distance holding action timeframe is unmodified var MOVE_WITH_HEAD = true; // experimental head-control of distantly held objects var NO_INTERSECT_COLOR = { @@ -66,12 +70,32 @@ var INTERSECT_COLOR = { green: 10, blue: 10 }; // line color when pick hits -var LINE_ENTITY_DIMENSIONS = { - x: 1000, - y: 1000, - z: 1000 + +var COLORS_GRAB_SEARCHING_HALF_SQUEEZE = { + red: 255, + green: 97, + blue: 129 }; +var COLORS_GRAB_SEARCHING_FULL_SQUEEZE = { + red: 255, + green: 97, + blue: 129 +}; + +var COLORS_GRAB_DISTANCE_HOLD = { + red: 238, + green: 75, + blue: 214 +}; + +var COLORS_EQUIP_SPHERE = { + red: 116, + green: 90, + blue: 238 +}; + + var LINE_LENGTH = 500; var PICK_MAX_DISTANCE = 500; // max length of pick-ray @@ -85,7 +109,7 @@ var EQUIP_HOTSPOT_RENDER_RADIUS = 0.3; // radius used for palm vs equip-hotspot var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position var NEAR_GRAB_RADIUS = 0.15; // radius used for palm vs object for near grabbing. -var NEAR_GRAB_MAX_DISTANCE = 1.0; // you cannot grab objects that are this far away from your hand +var NEAR_GRAB_MAX_DISTANCE = 1.0; // you cannot grab objects that are this far away from your hand var NEAR_GRAB_PICK_RADIUS = 0.25; // radius used for search ray vs object for near grabbing. @@ -143,14 +167,6 @@ var DEFAULT_GRABBABLE_DATA = { var USE_BLACKLIST = true; var blacklist = []; -// we've created various ways of visualizing looking for and moving distant objects -var USE_ENTITY_LINES_FOR_SEARCHING = false; -var USE_OVERLAY_LINES_FOR_SEARCHING = true; - -var USE_ENTITY_LINES_FOR_MOVING = false; -var USE_OVERLAY_LINES_FOR_MOVING = false; - - var FORBIDDEN_GRAB_NAMES = ["Grab Debug Entity", "grab pointer"]; var FORBIDDEN_GRAB_TYPES = ['Unknown', 'Light', 'PolyLine', 'Zone']; @@ -240,6 +256,7 @@ function findRayIntersection(pickRay, precise, include, exclude) { } return overlays; } + function entityIsGrabbedByOther(entityID) { // by convention, a distance grab sets the tag of its action to be grab-*owner-session-id*. var actionIDs = Entities.getActionIDs(entityID); @@ -270,6 +287,7 @@ function propsArePhysical(props) { var USE_ATTACH_POINT_SETTINGS = true; var ATTACH_POINT_SETTINGS = "io.highfidelity.attachPoints"; + function getAttachPointSettings() { try { var str = Settings.getValue(ATTACH_POINT_SETTINGS); @@ -283,10 +301,12 @@ function getAttachPointSettings() { return {}; } } + function setAttachPointSettings(attachPointSettings) { var str = JSON.stringify(attachPointSettings); Settings.setValue(ATTACH_POINT_SETTINGS, str); } + function getAttachPointForHotspotFromSettings(hotspot, hand) { var attachPointSettings = getAttachPointSettings(); var jointName = (hand === RIGHT_HAND) ? "RightHand" : "LeftHand"; @@ -297,6 +317,7 @@ function getAttachPointForHotspotFromSettings(hotspot, hand) { return undefined; } } + function storeAttachPointForHotspotInSettings(hotspot, hand, offsetPosition, offsetRotation) { var attachPointSettings = getAttachPointSettings(); var jointName = (hand === RIGHT_HAND) ? "RightHand" : "LeftHand"; @@ -324,17 +345,20 @@ function removeMyAvatarFromCollidesWith(origCollidesWith) { // and we should not be showing lasers when someone else is using the Reticle to indicate a 2D minor mode. var EXTERNALLY_MANAGED_2D_MINOR_MODE = true; var EDIT_SETTING = "io.highfidelity.isEditting"; + function isEditing() { var actualSettingValue = Settings.getValue(EDIT_SETTING) === "false" ? false : !!Settings.getValue(EDIT_SETTING); return EXTERNALLY_MANAGED_2D_MINOR_MODE && actualSettingValue; } + function isIn2DMode() { // In this version, we make our own determination of whether we're aimed a HUD element, // because other scripts (such as handControllerPointer) might be using some other visualization // instead of setting Reticle.visible. return (EXTERNALLY_MANAGED_2D_MINOR_MODE && - (Reticle.pointingAtSystemOverlay || Overlays.getOverlayAtPoint(Reticle.position))); + (Reticle.pointingAtSystemOverlay || Overlays.getOverlayAtPoint(Reticle.position))); } + function restore2DMode() { if (!EXTERNALLY_MANAGED_2D_MINOR_MODE) { Reticle.setVisible(true); @@ -351,10 +375,10 @@ function restore2DMode() { function EntityPropertiesCache() { this.cache = {}; } -EntityPropertiesCache.prototype.clear = function () { +EntityPropertiesCache.prototype.clear = function() { this.cache = {}; }; -EntityPropertiesCache.prototype.addEntity = function (entityID) { +EntityPropertiesCache.prototype.addEntity = function(entityID) { var cacheEntry = this.cache[entityID]; if (cacheEntry && cacheEntry.refCount) { cacheEntry.refCount += 1; @@ -362,13 +386,13 @@ EntityPropertiesCache.prototype.addEntity = function (entityID) { this._updateCacheEntry(entityID); } }; -EntityPropertiesCache.prototype.addEntities = function (entities) { +EntityPropertiesCache.prototype.addEntities = function(entities) { var _this = this; - entities.forEach(function (entityID) { + entities.forEach(function(entityID) { _this.addEntity(entityID); }); }; -EntityPropertiesCache.prototype._updateCacheEntry = function (entityID) { +EntityPropertiesCache.prototype._updateCacheEntry = function(entityID) { var props = Entities.getEntityProperties(entityID, GRABBABLE_PROPERTIES); // convert props.userData from a string to an object. @@ -385,7 +409,7 @@ EntityPropertiesCache.prototype._updateCacheEntry = function (entityID) { this.cache[entityID] = props; }; -EntityPropertiesCache.prototype.update = function () { +EntityPropertiesCache.prototype.update = function() { // delete any cacheEntries with zero refCounts. var entities = Object.keys(this.cache); for (var i = 0; i < entities.length; i++) { @@ -397,11 +421,11 @@ EntityPropertiesCache.prototype.update = function () { } } }; -EntityPropertiesCache.prototype.getProps = function (entityID) { +EntityPropertiesCache.prototype.getProps = function(entityID) { var obj = this.cache[entityID]; return obj ? obj : undefined; }; -EntityPropertiesCache.prototype.getGrabbableProps = function (entityID) { +EntityPropertiesCache.prototype.getGrabbableProps = function(entityID) { var props = this.cache[entityID]; if (props) { return props.userData.grabbableKey ? props.userData.grabbableKey : DEFAULT_GRABBABLE_DATA; @@ -409,7 +433,7 @@ EntityPropertiesCache.prototype.getGrabbableProps = function (entityID) { return undefined; } }; -EntityPropertiesCache.prototype.getGrabProps = function (entityID) { +EntityPropertiesCache.prototype.getGrabProps = function(entityID) { var props = this.cache[entityID]; if (props) { return props.userData.grabKey ? props.userData.grabKey : {}; @@ -417,7 +441,7 @@ EntityPropertiesCache.prototype.getGrabProps = function (entityID) { return undefined; } }; -EntityPropertiesCache.prototype.getWearableProps = function (entityID) { +EntityPropertiesCache.prototype.getWearableProps = function(entityID) { var props = this.cache[entityID]; if (props) { return props.userData.wearable ? props.userData.wearable : {}; @@ -425,7 +449,7 @@ EntityPropertiesCache.prototype.getWearableProps = function (entityID) { return undefined; } }; -EntityPropertiesCache.prototype.getEquipHotspotsProps = function (entityID) { +EntityPropertiesCache.prototype.getEquipHotspotsProps = function(entityID) { var props = this.cache[entityID]; if (props) { return props.userData.equipHotspots ? props.userData.equipHotspots : {}; @@ -454,7 +478,7 @@ function EquipHotspotBuddy() { // array of all hotspots that are highlighed. this.highlightedHotspots = []; } -EquipHotspotBuddy.prototype.clear = function () { +EquipHotspotBuddy.prototype.clear = function() { var keys = Object.keys(this.map); for (var i = 0; i < keys.length; i++) { var overlayInfoSet = this.map[keys[i]]; @@ -463,10 +487,10 @@ EquipHotspotBuddy.prototype.clear = function () { this.map = {}; this.highlightedHotspots = []; }; -EquipHotspotBuddy.prototype.highlightHotspot = function (hotspot) { +EquipHotspotBuddy.prototype.highlightHotspot = function(hotspot) { this.highlightedHotspots.push(hotspot.key); }; -EquipHotspotBuddy.prototype.updateHotspot = function (hotspot, timestamp) { +EquipHotspotBuddy.prototype.updateHotspot = function(hotspot, timestamp) { var overlayInfoSet = this.map[hotspot.key]; if (!overlayInfoSet) { // create a new overlayInfoSet @@ -487,7 +511,12 @@ EquipHotspotBuddy.prototype.updateHotspot = function (hotspot, timestamp) { overlayInfoSet.overlays.push(Overlays.addOverlay("model", { url: hotspot.modelURL, position: hotspot.worldPosition, - rotation: {x: 0, y: 0, z: 0, w: 1}, + rotation: { + x: 0, + y: 0, + z: 0, + w: 1 + }, dimensions: diameter * EQUIP_SPHERE_SCALE_FACTOR, scale: hotspot.modelScale, ignoreRayIntersection: true @@ -497,7 +526,12 @@ EquipHotspotBuddy.prototype.updateHotspot = function (hotspot, timestamp) { // default sphere overlay overlayInfoSet.overlays.push(Overlays.addOverlay("sphere", { position: hotspot.worldPosition, - rotation: {x: 0, y: 0, z: 0, w: 1}, + rotation: { + x: 0, + y: 0, + z: 0, + w: 1 + }, dimensions: diameter * EQUIP_SPHERE_SCALE_FACTOR, color: EQUIP_SPHERE_COLOR, alpha: EQUIP_SPHERE_ALPHA, @@ -514,14 +548,14 @@ EquipHotspotBuddy.prototype.updateHotspot = function (hotspot, timestamp) { overlayInfoSet.timestamp = timestamp; } }; -EquipHotspotBuddy.prototype.updateHotspots = function (hotspots, timestamp) { +EquipHotspotBuddy.prototype.updateHotspots = function(hotspots, timestamp) { var _this = this; - hotspots.forEach(function (hotspot) { + hotspots.forEach(function(hotspot) { _this.updateHotspot(hotspot, timestamp); }); this.highlightedHotspots = []; }; -EquipHotspotBuddy.prototype.update = function (deltaTime, timestamp) { +EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp) { var HIGHLIGHT_SIZE = 1.1; var NORMAL_SIZE = 1.0; @@ -554,7 +588,7 @@ EquipHotspotBuddy.prototype.update = function (deltaTime, timestamp) { if (overlayInfoSet.timestamp != timestamp && overlayInfoSet.currentSize <= 0.05) { // this is an old overlay, that has finished fading out, delete it! - overlayInfoSet.overlays.forEach(function (overlay) { + overlayInfoSet.overlays.forEach(function(overlay) { Overlays.deleteOverlay(overlay); }); delete this.map[keys[i]]; @@ -572,7 +606,7 @@ EquipHotspotBuddy.prototype.update = function (deltaTime, timestamp) { dimensions = overlayInfoSet.hotspot.radius * 2 * overlayInfoSet.currentSize; } - overlayInfoSet.overlays.forEach(function (overlay) { + overlayInfoSet.overlays.forEach(function(overlay) { Overlays.editOverlay(overlay, { position: position, rotation: props.rotation, @@ -595,7 +629,7 @@ function MyController(hand) { this.getHandPosition = MyAvatar.getLeftPalmPosition; // this.getHandRotation = MyAvatar.getLeftPalmRotation; } - this.getHandRotation = function () { + this.getHandRotation = function() { var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; return Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); }; @@ -638,13 +672,13 @@ function MyController(hand) { var _this = this; var suppressedIn2D = [STATE_OFF, STATE_SEARCHING]; - this.ignoreInput = function () { + this.ignoreInput = function() { // We've made the decision to use 'this' for new code, even though it is fragile, // in order to keep/ the code uniform without making any no-op line changes. return (-1 !== suppressedIn2D.indexOf(this.state)) && isIn2DMode(); }; - this.update = function (deltaTime, timestamp) { + this.update = function(deltaTime, timestamp) { this.updateSmoothedTrigger(); @@ -666,12 +700,12 @@ function MyController(hand) { } }; - this.callEntityMethodOnGrabbed = function (entityMethodName) { + this.callEntityMethodOnGrabbed = function(entityMethodName) { var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.grabbedEntity, entityMethodName, args); }; - this.setState = function (newState, reason) { + this.setState = function(newState, reason) { if (WANT_DEBUG || WANT_DEBUG_STATE) { var oldStateName = stateToName(this.state); @@ -704,58 +738,9 @@ function MyController(hand) { } }; - this.debugLine = function (closePoint, farPoint, color) { - Entities.addEntity({ - type: "Line", - name: "Grab Debug Entity", - dimensions: LINE_ENTITY_DIMENSIONS, - visible: true, - position: closePoint, - linePoints: [ZERO_VEC, farPoint], - color: color, - lifetime: 0.1, - dynamic: false, - ignoreForCollisions: true, - userData: JSON.stringify({ - grabbableKey: { - grabbable: false - } - }) - }); - }; - this.lineOn = function (closePoint, farPoint, color) { - // draw a line - if (this.pointer === null) { - this.pointer = Entities.addEntity({ - type: "Line", - name: "grab pointer", - dimensions: LINE_ENTITY_DIMENSIONS, - visible: true, - position: closePoint, - linePoints: [ZERO_VEC, farPoint], - color: color, - lifetime: LIFETIME, - dynamic: false, - ignoreForCollisions: true, - userData: JSON.stringify({ - grabbableKey: { - grabbable: false - } - }) - }); - } else { - var age = Entities.getEntityProperties(this.pointer, "age").age; - this.pointer = Entities.editEntity(this.pointer, { - position: closePoint, - linePoints: [ZERO_VEC, farPoint], - color: color, - lifetime: age + LIFETIME - }); - } - }; - this.searchSphereOn = function (location, size, color) { + this.searchSphereOn = function(location, size, color) { var rotation = Quat.lookAt(location, Camera.getPosition(), Vec3.UP); var brightColor = colorPow(color, 0.06); @@ -788,7 +773,7 @@ function MyController(hand) { } }; - this.overlayLineOn = function (closePoint, farPoint, color) { + this.overlayLineOn = function(closePoint, farPoint, color) { if (this.overlayLine === null) { var lineProperties = { glow: 1.0, @@ -816,7 +801,7 @@ function MyController(hand) { } }; - this.searchIndicatorOn = function (distantPickRay) { + this.searchIndicatorOn = function(distantPickRay) { var handPosition = distantPickRay.origin; var SEARCH_SPHERE_SIZE = 0.011; var SEARCH_SPHERE_FOLLOW_RATE = 0.50; @@ -828,16 +813,16 @@ function MyController(hand) { } var searchSphereLocation = Vec3.sum(distantPickRay.origin, - Vec3.multiply(distantPickRay.direction, this.searchSphereDistance)); + Vec3.multiply(distantPickRay.direction, this.searchSphereDistance)); this.searchSphereOn(searchSphereLocation, SEARCH_SPHERE_SIZE * this.searchSphereDistance, - (this.triggerSmoothedGrab() || this.secondarySqueezed()) ? INTERSECT_COLOR : NO_INTERSECT_COLOR); - if ((USE_OVERLAY_LINES_FOR_SEARCHING === true) && PICK_WITH_HAND_RAY) { + (this.triggerSmoothedGrab() || this.secondarySqueezed()) ? COLORS_GRAB_SEARCHING_FULL_SQUEEZE : COLORS_GRAB_SEARCHING_HALF_SQUEEZE); + if (PICK_WITH_HAND_RAY) { this.overlayLineOn(handPosition, searchSphereLocation, - (this.triggerSmoothedGrab() || this.secondarySqueezed()) ? INTERSECT_COLOR : NO_INTERSECT_COLOR); + (this.triggerSmoothedGrab() || this.secondarySqueezed()) ? COLORS_GRAB_SEARCHING_FULL_SQUEEZE : COLORS_GRAB_SEARCHING_HALF_SQUEEZE); } }; - this.evalLightWorldTransform = function (modelPos, modelRot) { + this.evalLightWorldTransform = function(modelPos, modelRot) { var MODEL_LIGHT_POSITION = { x: 0, @@ -857,21 +842,21 @@ function MyController(hand) { }; }; - this.lineOff = function () { + this.lineOff = function() { if (this.pointer !== null) { Entities.deleteEntity(this.pointer); } this.pointer = null; }; - this.overlayLineOff = function () { + this.overlayLineOff = function() { if (this.overlayLine !== null) { Overlays.deleteOverlay(this.overlayLine); } this.overlayLine = null; }; - this.searchSphereOff = function () { + this.searchSphereOff = function() { if (this.searchSphere !== null) { Overlays.deleteOverlay(this.searchSphere); this.searchSphere = null; @@ -880,57 +865,51 @@ function MyController(hand) { } }; - this.turnOffVisualizations = function () { - if (USE_ENTITY_LINES_FOR_SEARCHING === true || USE_ENTITY_LINES_FOR_MOVING === true) { - this.lineOff(); - } + this.turnOffVisualizations = function() { - if (USE_OVERLAY_LINES_FOR_SEARCHING === true || USE_OVERLAY_LINES_FOR_MOVING === true) { - this.overlayLineOff(); - } + this.overlayLineOff(); - this.searchSphereOff(); restore2DMode(); }; - this.triggerPress = function (value) { + this.triggerPress = function(value) { _this.rawTriggerValue = value; }; - this.triggerClick = function (value) { + this.triggerClick = function(value) { _this.triggerClicked = value; }; - this.secondaryPress = function (value) { + this.secondaryPress = function(value) { _this.rawSecondaryValue = value; }; - this.updateSmoothedTrigger = function () { + this.updateSmoothedTrigger = function() { var triggerValue = this.rawTriggerValue; // smooth out trigger value this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); }; - this.triggerSmoothedGrab = function () { + this.triggerSmoothedGrab = function() { return this.triggerClicked; }; - this.triggerSmoothedSqueezed = function () { + this.triggerSmoothedSqueezed = function() { return this.triggerValue > TRIGGER_ON_VALUE; }; - this.triggerSmoothedReleased = function () { + this.triggerSmoothedReleased = function() { return this.triggerValue < TRIGGER_OFF_VALUE; }; - this.secondarySqueezed = function () { + this.secondarySqueezed = function() { return _this.rawSecondaryValue > BUMPER_ON_VALUE; }; - this.secondaryReleased = function () { + this.secondaryReleased = function() { return _this.rawSecondaryValue < BUMPER_ON_VALUE; }; @@ -942,19 +921,19 @@ function MyController(hand) { // return triggerSmoothedReleased() && secondaryReleased(); // } - this.thumbPress = function (value) { + this.thumbPress = function(value) { _this.rawThumbValue = value; }; - this.thumbPressed = function () { + this.thumbPressed = function() { return _this.rawThumbValue > THUMB_ON_VALUE; }; - this.thumbReleased = function () { + this.thumbReleased = function() { return _this.rawThumbValue < THUMB_ON_VALUE; }; - this.off = function (deltaTime, timestamp) { + this.off = function(deltaTime, timestamp) { if (this.triggerSmoothedReleased()) { this.waitForTriggerRelease = false; } @@ -982,11 +961,11 @@ function MyController(hand) { } }; - this.clearEquipHaptics = function () { + this.clearEquipHaptics = function() { this.prevPotentialEquipHotspot = null; }; - this.updateEquipHaptics = function (potentialEquipHotspot) { + this.updateEquipHaptics = function(potentialEquipHotspot) { if (potentialEquipHotspot && !this.prevPotentialEquipHotspot || !potentialEquipHotspot && this.prevPotentialEquipHotspot) { Controller.triggerShortHapticPulse(0.5, this.hand); @@ -998,7 +977,7 @@ function MyController(hand) { // @param {number} which hand to use, RIGHT_HAND or LEFT_HAND // @returns {object} returns object with two keys entityID and distance // - this.calcRayPickInfo = function (hand) { + this.calcRayPickInfo = function(hand) { var standardControllerValue = (hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; var pose = Controller.getPoseValue(standardControllerValue); @@ -1008,8 +987,8 @@ function MyController(hand) { var pickRay = { origin: PICK_WITH_HAND_RAY ? worldHandPosition : Camera.position, direction: PICK_WITH_HAND_RAY ? Quat.getUp(worldHandRotation) : Vec3.mix(Quat.getUp(worldHandRotation), - Quat.getFront(Camera.orientation), - HAND_HEAD_MIX_RATIO), + Quat.getFront(Camera.orientation), + HAND_HEAD_MIX_RATIO), length: PICK_MAX_DISTANCE }; @@ -1051,7 +1030,7 @@ function MyController(hand) { } }; - this.entityWantsTrigger = function (entityID) { + this.entityWantsTrigger = function(entityID) { var grabbableProps = entityPropertiesCache.getGrabbableProps(entityID); return grabbableProps && grabbableProps.wantsTrigger; }; @@ -1068,7 +1047,7 @@ function MyController(hand) { // offset position {Vec3} and offset rotation {Quat}, both are in the coordinate system of the joint. // * modelURL {string} url for model to use instead of default sphere. // * modelScale {Vec3} scale factor for model - this.collectEquipHotspots = function (entityID) { + this.collectEquipHotspots = function(entityID) { var result = []; var props = entityPropertiesCache.getProps(entityID); var entityXform = new Xform(props.rotation, props.position); @@ -1096,7 +1075,11 @@ function MyController(hand) { result.push({ key: entityID.toString() + "0", entityID: entityID, - localPosition: {x: 0, y: 0, z: 0}, + localPosition: { + x: 0, + y: 0, + z: 0 + }, worldPosition: entityXform.pos, radius: EQUIP_RADIUS, joints: wearableProps.joints, @@ -1108,15 +1091,15 @@ function MyController(hand) { return result; }; - this.hotspotIsEquippable = function (hotspot) { + this.hotspotIsEquippable = function(hotspot) { var props = entityPropertiesCache.getProps(hotspot.entityID); var grabProps = entityPropertiesCache.getGrabProps(hotspot.entityID); var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME); var refCount = ("refCount" in grabProps) ? grabProps.refCount : 0; var okToEquipFromOtherHand = ((this.getOtherHandController().state == STATE_NEAR_GRABBING || - this.getOtherHandController().state == STATE_DISTANCE_HOLDING) && - this.getOtherHandController().grabbedEntity == hotspot.entityID); + this.getOtherHandController().state == STATE_DISTANCE_HOLDING) && + this.getOtherHandController().grabbedEntity == hotspot.entityID); if (refCount > 0 && !okToEquipFromOtherHand) { if (debug) { print("equip is skipping '" + props.name + "': grabbed by someone else"); @@ -1127,7 +1110,7 @@ function MyController(hand) { return true; }; - this.entityIsGrabbable = function (entityID) { + this.entityIsGrabbable = function(entityID) { var grabbableProps = entityPropertiesCache.getGrabbableProps(entityID); var grabProps = entityPropertiesCache.getGrabProps(entityID); var props = entityPropertiesCache.getProps(entityID); @@ -1179,7 +1162,7 @@ function MyController(hand) { return true; }; - this.entityIsDistanceGrabbable = function (entityID, handPosition) { + this.entityIsDistanceGrabbable = function(entityID, handPosition) { if (!this.entityIsGrabbable(entityID)) { return false; } @@ -1216,7 +1199,7 @@ function MyController(hand) { return true; }; - this.entityIsNearGrabbable = function (entityID, handPosition, maxDistance) { + this.entityIsNearGrabbable = function(entityID, handPosition, maxDistance) { if (!this.entityIsGrabbable(entityID)) { return false; @@ -1237,22 +1220,22 @@ function MyController(hand) { return true; }; - this.chooseNearEquipHotspots = function (candidateEntities, distance) { - var equippableHotspots = flatten(candidateEntities.map(function (entityID) { + this.chooseNearEquipHotspots = function(candidateEntities, distance) { + var equippableHotspots = flatten(candidateEntities.map(function(entityID) { return _this.collectEquipHotspots(entityID); - })).filter(function (hotspot) { + })).filter(function(hotspot) { return (_this.hotspotIsEquippable(hotspot) && - Vec3.distance(hotspot.worldPosition, _this.getHandPosition()) < hotspot.radius + distance); + Vec3.distance(hotspot.worldPosition, _this.getHandPosition()) < hotspot.radius + distance); }); return equippableHotspots; }; - this.chooseBestEquipHotspot = function (candidateEntities) { + this.chooseBestEquipHotspot = function(candidateEntities) { var DISTANCE = 0; var equippableHotspots = this.chooseNearEquipHotspots(candidateEntities, DISTANCE); if (equippableHotspots.length > 0) { // sort by distance - equippableHotspots.sort(function (a, b) { + equippableHotspots.sort(function(a, b) { var aDistance = Vec3.distance(a.worldPosition, this.getHandPosition()); var bDistance = Vec3.distance(b.worldPosition, this.getHandPosition()); return aDistance - bDistance; @@ -1263,7 +1246,7 @@ function MyController(hand) { } }; - this.search = function (deltaTime, timestamp) { + this.search = function(deltaTime, timestamp) { var _this = this; var name; @@ -1293,7 +1276,7 @@ function MyController(hand) { } } - var grabbableEntities = candidateEntities.filter(function (entity) { + var grabbableEntities = candidateEntities.filter(function(entity) { return _this.entityIsNearGrabbable(entity, handPosition, NEAR_GRAB_MAX_DISTANCE); }); @@ -1313,7 +1296,7 @@ function MyController(hand) { var entity; if (grabbableEntities.length > 0) { // sort by distance - grabbableEntities.sort(function (a, b) { + grabbableEntities.sort(function(a, b) { var aDistance = Vec3.distance(entityPropertiesCache.getProps(a).position, handPosition); var bDistance = Vec3.distance(entityPropertiesCache.getProps(b).position, handPosition); return aDistance - bDistance; @@ -1379,18 +1362,12 @@ function MyController(hand) { equipHotspotBuddy.highlightHotspot(potentialEquipHotspot); } - // search line visualizations - if (USE_ENTITY_LINES_FOR_SEARCHING === true) { - this.lineOn(rayPickInfo.searchRay.origin, - Vec3.multiply(rayPickInfo.searchRay.direction, LINE_LENGTH), - NO_INTERSECT_COLOR); - } this.searchIndicatorOn(rayPickInfo.searchRay); Reticle.setVisible(false); }; - this.distanceGrabTimescale = function (mass, distance) { + this.distanceGrabTimescale = function(mass, distance) { var timeScale = DISTANCE_HOLDING_ACTION_TIMEFRAME * mass / DISTANCE_HOLDING_UNITY_MASS * distance / DISTANCE_HOLDING_UNITY_DISTANCE; @@ -1400,11 +1377,11 @@ function MyController(hand) { return timeScale; }; - this.getMass = function (dimensions, density) { + this.getMass = function(dimensions, density) { return (dimensions.x * dimensions.y * dimensions.z) * density; }; - this.distanceHoldingEnter = function () { + this.distanceHoldingEnter = function() { this.clearEquipHaptics(); @@ -1414,7 +1391,7 @@ function MyController(hand) { // transform it into world frame var worldControllerPosition = Vec3.sum(MyAvatar.position, - Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation)); + Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation)); // also transform the position into room space var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix()); @@ -1467,7 +1444,7 @@ function MyController(hand) { this.previousRoomControllerPosition = roomControllerPosition; }; - this.distanceHolding = function (deltaTime, timestamp) { + this.distanceHolding = function(deltaTime, timestamp) { if (this.triggerSmoothedReleased()) { this.callEntityMethodOnGrabbed("releaseGrab"); this.setState(STATE_OFF, "trigger released"); @@ -1482,7 +1459,7 @@ function MyController(hand) { // transform it into world frame var worldControllerPosition = Vec3.sum(MyAvatar.position, - Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation)); + Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation)); var worldControllerRotation = Quat.multiply(MyAvatar.orientation, avatarControllerPose.rotation); // also transform the position into room space @@ -1527,7 +1504,7 @@ function MyController(hand) { var RADIAL_GRAB_AMPLIFIER = 10.0; if (Math.abs(this.grabRadialVelocity) > 0.0) { this.grabRadius = this.grabRadius + (this.grabRadialVelocity * deltaObjectTime * - this.grabRadius * RADIAL_GRAB_AMPLIFIER); + this.grabRadius * RADIAL_GRAB_AMPLIFIER); } var newTargetPosition = Vec3.multiply(this.grabRadius, Quat.getUp(worldControllerRotation)); @@ -1557,12 +1534,8 @@ function MyController(hand) { var handPosition = this.getHandPosition(); // visualizations - if (USE_ENTITY_LINES_FOR_MOVING === true) { - this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); - } - if (USE_OVERLAY_LINES_FOR_MOVING === true) { - this.overlayLineOn(handPosition, grabbedProperties.position, INTERSECT_COLOR); - } + + this.overlayLineOn(handPosition, grabbedProperties.position, INTERSECT_COLOR); var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, this.currentObjectPosition)); var success = Entities.updateAction(this.grabbedEntity, this.actionID, { @@ -1581,7 +1554,7 @@ function MyController(hand) { this.previousRoomControllerPosition = roomControllerPosition; }; - this.setupHoldAction = function () { + this.setupHoldAction = function() { this.actionID = Entities.addAction("hold", this.grabbedEntity, { hand: this.hand === RIGHT_HAND ? "right" : "left", timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, @@ -1601,7 +1574,7 @@ function MyController(hand) { return true; }; - this.projectVectorAlongAxis = function (position, axisStart, axisEnd) { + this.projectVectorAlongAxis = function(position, axisStart, axisEnd) { var aPrime = Vec3.subtract(position, axisStart); var bPrime = Vec3.subtract(axisEnd, axisStart); var bPrimeMagnitude = Vec3.length(bPrime); @@ -1617,18 +1590,30 @@ function MyController(hand) { return projection; }; - this.dropGestureReset = function () { + this.dropGestureReset = function() { this.prevHandIsUpsideDown = false; }; - this.dropGestureProcess = function (deltaTime) { + this.dropGestureProcess = function(deltaTime) { var standardControllerValue = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; var pose = Controller.getPoseValue(standardControllerValue); var worldHandRotation = Quat.multiply(MyAvatar.orientation, pose.rotation); - var localHandUpAxis = this.hand === RIGHT_HAND ? {x: 1, y: 0, z: 0} : {x: -1, y: 0, z: 0}; + var localHandUpAxis = this.hand === RIGHT_HAND ? { + x: 1, + y: 0, + z: 0 + } : { + x: -1, + y: 0, + z: 0 + }; var worldHandUpAxis = Vec3.multiplyQbyV(worldHandRotation, localHandUpAxis); - var DOWN = {x: 0, y: -1, z: 0}; + var DOWN = { + x: 0, + y: -1, + z: 0 + }; var DROP_ANGLE = Math.PI / 7; var HYSTERESIS_FACTOR = 1.1; @@ -1649,7 +1634,7 @@ function MyController(hand) { return handIsUpsideDown; }; - this.nearGrabbingEnter = function () { + this.nearGrabbingEnter = function() { this.lineOff(); this.overlayLineOff(); @@ -1743,8 +1728,16 @@ function MyController(hand) { } Entities.editEntity(this.grabbedEntity, { - velocity: {x: 0, y: 0, z: 0}, - angularVelocity: {x: 0, y: 0, z: 0}, + velocity: { + x: 0, + y: 0, + z: 0 + }, + angularVelocity: { + x: 0, + y: 0, + z: 0 + }, dynamic: false }); @@ -1766,7 +1759,7 @@ function MyController(hand) { this.prevDropDetected = false; }; - this.nearGrabbing = function (deltaTime, timestamp) { + this.nearGrabbing = function(deltaTime, timestamp) { if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { this.callEntityMethodOnGrabbed("releaseGrab"); @@ -1836,7 +1829,7 @@ function MyController(hand) { if (nearPickedCandidateEntities.indexOf(this.grabbedEntity) == -1) { // for whatever reason, the held/equipped entity has been pulled away. ungrab or unequip. print("handControllerGrab -- autoreleasing held or equipped item because it is far from hand." + - props.parentID + " " + vec3toStr(props.position)); + props.parentID + " " + vec3toStr(props.position)); if (this.state == STATE_NEAR_GRABBING) { this.callEntityMethodOnGrabbed("releaseGrab"); @@ -1906,7 +1899,7 @@ function MyController(hand) { } }; - this.nearTriggerEnter = function () { + this.nearTriggerEnter = function() { this.clearEquipHaptics(); @@ -1914,13 +1907,13 @@ function MyController(hand) { this.callEntityMethodOnGrabbed("startNearTrigger"); }; - this.farTriggerEnter = function () { + this.farTriggerEnter = function() { this.clearEquipHaptics(); this.callEntityMethodOnGrabbed("startFarTrigger"); }; - this.nearTrigger = function (deltaTime, timestamp) { + this.nearTrigger = function(deltaTime, timestamp) { if (this.triggerSmoothedReleased()) { this.callEntityMethodOnGrabbed("stopNearTrigger"); this.setState(STATE_OFF, "trigger released"); @@ -1929,7 +1922,7 @@ function MyController(hand) { this.callEntityMethodOnGrabbed("continueNearTrigger"); }; - this.farTrigger = function (deltaTime, timestamp) { + this.farTrigger = function(deltaTime, timestamp) { if (this.triggerSmoothedReleased()) { this.callEntityMethodOnGrabbed("stopFarTrigger"); this.setState(STATE_OFF, "trigger released"); @@ -1962,11 +1955,11 @@ function MyController(hand) { this.callEntityMethodOnGrabbed("continueFarTrigger"); }; - this.offEnter = function () { + this.offEnter = function() { this.release(); }; - this.release = function () { + this.release = function() { this.turnOffVisualizations(); var noVelocity = false; @@ -1979,10 +1972,10 @@ function MyController(hand) { var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "localRotation"]); if (grabbedProperties && grabbedProperties.localPosition && grabbedProperties.localRotation) { print((this.hand === RIGHT_HAND ? '"LeftHand"' : '"RightHand"') + ":" + - '[{"x":' + grabbedProperties.localPosition.x + ', "y":' + grabbedProperties.localPosition.y + - ', "z":' + grabbedProperties.localPosition.z + '}, {"x":' + grabbedProperties.localRotation.x + - ', "y":' + grabbedProperties.localRotation.y + ', "z":' + grabbedProperties.localRotation.z + - ', "w":' + grabbedProperties.localRotation.w + '}]'); + '[{"x":' + grabbedProperties.localPosition.x + ', "y":' + grabbedProperties.localPosition.y + + ', "z":' + grabbedProperties.localPosition.z + '}, {"x":' + grabbedProperties.localRotation.x + + ', "y":' + grabbedProperties.localRotation.y + ', "z":' + grabbedProperties.localRotation.z + + ', "w":' + grabbedProperties.localRotation.w + '}]'); } } @@ -2017,11 +2010,11 @@ function MyController(hand) { } }; - this.cleanup = function () { + this.cleanup = function() { this.release(); }; - this.heartBeat = function (entityID) { + this.heartBeat = function(entityID) { var now = Date.now(); if (now - this.lastHeartBeat > HEART_BEAT_INTERVAL) { var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); @@ -2031,7 +2024,7 @@ function MyController(hand) { } }; - this.resetAbandonedGrab = function (entityID) { + this.resetAbandonedGrab = function(entityID) { print("cleaning up abandoned grab on " + entityID); var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); data["refCount"] = 1; @@ -2039,7 +2032,7 @@ function MyController(hand) { this.deactivateEntity(entityID, false); }; - this.activateEntity = function (entityID, grabbedProperties, wasLoaded) { + this.activateEntity = function(entityID, grabbedProperties, wasLoaded) { if (this.entityActivated) { return; } @@ -2105,31 +2098,37 @@ function MyController(hand) { // people are holding something and one of them will be able (if the other releases at the right time) to // bootstrap themselves with the held object. This happens because the meaning of "otherAvatar" in // the collision mask hinges on who the physics simulation owner is. - Entities.editEntity(entityID, {"collidesWith": COLLIDES_WITH_WHILE_MULTI_GRABBED}); + Entities.editEntity(entityID, { + "collidesWith": COLLIDES_WITH_WHILE_MULTI_GRABBED + }); } } setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); return data; }; - this.checkForStrayChildren = function () { + this.checkForStrayChildren = function() { // sometimes things can get parented to a hand and this script is unaware. Search for such entities and // unhook them. var handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); var children = Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, handJointIndex); - children.forEach(function (childID) { + children.forEach(function(childID) { print("disconnecting stray child of hand: (" + _this.hand + ") " + childID); - Entities.editEntity(childID, {parentID: NULL_UUID}); + Entities.editEntity(childID, { + parentID: NULL_UUID + }); }); }; - this.delayedDeactivateEntity = function (entityID, collidesWith) { + this.delayedDeactivateEntity = function(entityID, collidesWith) { // If, before the grab started, the held entity collided with myAvatar, we do the deactivation in // two parts. Most of it is done in deactivateEntity(), but the final collidesWith and refcount // are delayed a bit. This keeps thrown things from colliding with the avatar's capsule so often. // The refcount is handled in this delayed fashion so things don't get confused if someone else // grabs the entity before the timeout fires. - Entities.editEntity(entityID, { collidesWith: collidesWith }); + Entities.editEntity(entityID, { + collidesWith: collidesWith + }); var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); if (data && data["refCount"]) { data["refCount"] = data["refCount"] - 1; @@ -2143,7 +2142,7 @@ function MyController(hand) { setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); }; - this.deactivateEntity = function (entityID, noVelocity, delayed) { + this.deactivateEntity = function(entityID, noVelocity, delayed) { var deactiveProps; if (!this.entityActivated) { @@ -2172,7 +2171,7 @@ function MyController(hand) { if (doDelayedDeactivate) { var delayedCollidesWith = data["collidesWith"]; var delayedEntityID = entityID; - delayedDeactivateFunc = function () { + delayedDeactivateFunc = function() { // set collidesWith back to original value a bit later than the rest delayedDeactivateTimeout = null; _this.delayedDeactivateEntity(delayedEntityID, delayedCollidesWith); @@ -2204,12 +2203,24 @@ function MyController(hand) { data["dynamic"] && data["parentID"] == NULL_UUID && !data["collisionless"]) { - deactiveProps["velocity"] = {x: 0.0, y: 0.1, z: 0.0}; + deactiveProps["velocity"] = { + x: 0.0, + y: 0.1, + z: 0.0 + }; doSetVelocity = false; } if (noVelocity) { - deactiveProps["velocity"] = {x: 0.0, y: 0.0, z: 0.0}; - deactiveProps["angularVelocity"] = {x: 0.0, y: 0.0, z: 0.0}; + deactiveProps["velocity"] = { + x: 0.0, + y: 0.0, + z: 0.0 + }; + deactiveProps["angularVelocity"] = { + x: 0.0, + y: 0.0, + z: 0.0 + }; doSetVelocity = false; } @@ -2222,7 +2233,7 @@ function MyController(hand) { // be fixed. Entities.editEntity(entityID, { velocity: this.currentVelocity - // angularVelocity: this.currentAngularVelocity + // angularVelocity: this.currentAngularVelocity }); } data = null; @@ -2231,14 +2242,32 @@ function MyController(hand) { deactiveProps = { parentID: this.previousParentID, parentJointIndex: this.previousParentJointIndex, - velocity: {x: 0.0, y: 0.0, z: 0.0}, - angularVelocity: {x: 0.0, y: 0.0, z: 0.0} + velocity: { + x: 0.0, + y: 0.0, + z: 0.0 + }, + angularVelocity: { + x: 0.0, + y: 0.0, + z: 0.0 + } }; Entities.editEntity(entityID, deactiveProps); } else if (noVelocity) { - Entities.editEntity(entityID, {velocity: {x: 0.0, y: 0.0, z: 0.0}, - angularVelocity: {x: 0.0, y: 0.0, z: 0.0}, - dynamic: data["dynamic"]}); + Entities.editEntity(entityID, { + velocity: { + x: 0.0, + y: 0.0, + z: 0.0 + }, + angularVelocity: { + x: 0.0, + y: 0.0, + z: 0.0 + }, + dynamic: data["dynamic"] + }); } } else { data = null; @@ -2248,7 +2277,7 @@ function MyController(hand) { } }; - this.getOtherHandController = function () { + this.getOtherHandController = function() { return (this.hand === RIGHT_HAND) ? leftController : rightController; }; } @@ -2297,7 +2326,7 @@ Messages.subscribe('Hifi-Hand-Grab'); Messages.subscribe('Hifi-Hand-RayPick-Blacklist'); Messages.subscribe('Hifi-Object-Manipulation'); -var handleHandMessages = function (channel, message, sender) { +var handleHandMessages = function(channel, message, sender) { var data; if (sender === MyAvatar.sessionUUID) { if (channel === 'Hifi-Hand-Disabler') { @@ -2362,4 +2391,4 @@ function cleanup() { } Script.scriptEnding.connect(cleanup); -Script.update.connect(update); +Script.update.connect(update); \ No newline at end of file diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 3d40bfb9eb..d84ea65adb 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -45,6 +45,18 @@ var TARGET_MODEL_DIMENSIONS = { }; + +var COLORS_TELEPORT_CAN_TELEPORT = { + red: 136, + green: 255, + blue: 241 +}; +var COLORS_TELEPORT_CANNOT_TELEPORT = { + red: 68, + green: 183, + blue: 180 +}; + function ThumbPad(hand) { this.hand = hand; var _thisPad = this; @@ -275,11 +287,7 @@ function Teleporter() { var rightIntersection = Entities.findRayIntersection(teleporter.rightPickRay, true, [], [this.targetEntity]); if (rightIntersection.intersects) { - this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, { - red: 7, - green: 36, - blue: 44 - }); + this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT); if (this.targetOverlay !== null) { this.updateTargetOverlay(rightIntersection); } else { @@ -289,11 +297,7 @@ function Teleporter() { } else { this.deleteTargetOverlay(); - this.rightLineOn(rightPickRay.origin, location, { - red: 7, - green: 36, - blue: 44 - }); + this.rightLineOn(rightPickRay.origin, location, COLORS_TELEPORT_CANNOT_TELEPORT); } } @@ -325,11 +329,7 @@ function Teleporter() { if (leftIntersection.intersects) { - this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, { - red: 7, - green: 36, - blue: 44 - }); + this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT); if (this.targetOverlay !== null) { this.updateTargetOverlay(leftIntersection); } else { @@ -341,11 +341,7 @@ function Teleporter() { this.deleteTargetOverlay(); - this.leftLineOn(leftPickRay.origin, location, { - red: 7, - green: 36, - blue: 44 - }); + this.leftLineOn(leftPickRay.origin, location, COLORS_TELEPORT_CANNOT_TELEPORT); } }; @@ -622,4 +618,4 @@ function cleanup() { if (teleporter.updateConnected !== null) { Script.update.disconnect(teleporter.update); } -} +} \ No newline at end of file From e81f65d992264521494a7bcbcb6f0c124bf70ca9 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 22 Jul 2016 10:54:57 -0700 Subject: [PATCH 08/25] colors --- .../system/controllers/handControllerGrab.js | 25 +++---------------- scripts/system/controllers/teleport.js | 6 ++--- 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index e10e356e87..be897cf08f 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -43,9 +43,9 @@ var HAND_HEAD_MIX_RATIO = 0.0; // 0 = only use hands for search/move. 1 = only var PICK_WITH_HAND_RAY = true; var EQUIP_SPHERE_COLOR = { - red: 179, - green: 120, - blue: 211 + red: 116, + green: 90, + blue: 238 }; var EQUIP_SPHERE_ALPHA = 0.15; var EQUIP_SPHERE_SCALE_FACTOR = 0.65; @@ -60,17 +60,6 @@ var DISTANCE_HOLDING_UNITY_MASS = 1200; // The mass at which the distance holdi var DISTANCE_HOLDING_UNITY_DISTANCE = 6; // The distance at which the distance holding action timeframe is unmodified var MOVE_WITH_HEAD = true; // experimental head-control of distantly held objects -var NO_INTERSECT_COLOR = { - red: 10, - green: 10, - blue: 255 -}; // line color when pick misses -var INTERSECT_COLOR = { - red: 250, - green: 10, - blue: 10 -}; // line color when pick hits - var COLORS_GRAB_SEARCHING_HALF_SQUEEZE = { red: 255, green: 97, @@ -89,12 +78,6 @@ var COLORS_GRAB_DISTANCE_HOLD = { blue: 214 }; -var COLORS_EQUIP_SPHERE = { - red: 116, - green: 90, - blue: 238 -}; - var LINE_LENGTH = 500; var PICK_MAX_DISTANCE = 500; // max length of pick-ray @@ -1535,7 +1518,7 @@ function MyController(hand) { // visualizations - this.overlayLineOn(handPosition, grabbedProperties.position, INTERSECT_COLOR); + this.overlayLineOn(handPosition, grabbedProperties.position, COLORS_GRAB_DISTANCE_HOLD); var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, this.currentObjectPosition)); var success = Entities.updateAction(this.grabbedEntity, this.actionID, { diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index d84ea65adb..4daf3d353d 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -51,6 +51,7 @@ var COLORS_TELEPORT_CAN_TELEPORT = { green: 255, blue: 241 }; + var COLORS_TELEPORT_CANNOT_TELEPORT = { red: 68, green: 183, @@ -281,7 +282,7 @@ function Teleporter() { this.rightPickRay = rightPickRay; - var location = Vec3.sum(rightPickRay.origin, Vec3.multiply(rightPickRay.direction, 500)); + var location = Vec3.sum(rightPickRay.origin, Vec3.multiply(rightPickRay.direction, 50)); var rightIntersection = Entities.findRayIntersection(teleporter.rightPickRay, true, [], [this.targetEntity]); @@ -322,7 +323,7 @@ function Teleporter() { this.leftPickRay = leftPickRay; - var location = Vec3.sum(MyAvatar.position, Vec3.multiply(leftPickRay.direction, 500)); + var location = Vec3.sum(MyAvatar.position, Vec3.multiply(leftPickRay.direction, 50)); var leftIntersection = Entities.findRayIntersection(teleporter.leftPickRay, true, [], [this.targetEntity]); @@ -339,7 +340,6 @@ function Teleporter() { } else { - this.deleteTargetOverlay(); this.leftLineOn(leftPickRay.origin, location, COLORS_TELEPORT_CANNOT_TELEPORT); } From 3607a34f55c3e76a12ea761ab9c747480a4fca82 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 22 Jul 2016 13:25:33 -0700 Subject: [PATCH 09/25] tweak telecolors --- scripts/system/controllers/teleport.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 4daf3d353d..6ab425b44a 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -47,16 +47,18 @@ var TARGET_MODEL_DIMENSIONS = { var COLORS_TELEPORT_CAN_TELEPORT = { - red: 136, - green: 255, - blue: 241 + red: 97, + green: 247, + blue: 255 }; - +​ +​ var COLORS_TELEPORT_CANNOT_TELEPORT = { - red: 68, - green: 183, - blue: 180 + red: 0, + green: 121, + blue: 141 }; +​ function ThumbPad(hand) { this.hand = hand; From a43e9a5710ed90d07dd596a701a8c9ebe98a294c Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Fri, 22 Jul 2016 16:00:40 -0700 Subject: [PATCH 10/25] Fix for crash on avatars with missing "Hips" joint --- libraries/animation/src/AnimInverseKinematics.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 971694bfa8..95ca493058 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -861,7 +861,11 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele _hipsIndex = _skeleton->nameToJointIndex("Hips"); // also cache the _hipsParentIndex for later - _hipsParentIndex = _skeleton->getParentIndex(_hipsIndex); + if (_hipsIndex >= 0) { + _hipsParentIndex = _skeleton->getParentIndex(_hipsIndex); + } else { + _hipsParentIndex = -1; + } } else { clearConstraints(); _headIndex = -1; From 3b14988577f511f598db433717c80ba95c371f12 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Fri, 22 Jul 2016 16:27:43 -0700 Subject: [PATCH 11/25] Fix another crash if "Hips" joint is not found. --- libraries/animation/src/AnimInverseKinematics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 95ca493058..6ab4aa2127 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -428,7 +428,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars // shift hips according to the _hipsOffset from the previous frame float offsetLength = glm::length(_hipsOffset); const float MIN_HIPS_OFFSET_LENGTH = 0.03f; - if (offsetLength > MIN_HIPS_OFFSET_LENGTH) { + if (offsetLength > MIN_HIPS_OFFSET_LENGTH && _hipsIndex >= 0) { // but only if offset is long enough float scaleFactor = ((offsetLength - MIN_HIPS_OFFSET_LENGTH) / offsetLength); if (_hipsParentIndex == -1) { From c04a41c3cb16420d6fe0701598a07c61b522cd0d Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 22 Jul 2016 16:51:42 -0700 Subject: [PATCH 12/25] fix invisible characters from slack copying --- scripts/system/controllers/teleport.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 6ab425b44a..ca8891f6ab 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -50,15 +50,13 @@ var COLORS_TELEPORT_CAN_TELEPORT = { red: 97, green: 247, blue: 255 -}; -​ -​ +} + var COLORS_TELEPORT_CANNOT_TELEPORT = { red: 0, green: 121, blue: 141 }; -​ function ThumbPad(hand) { this.hand = hand; From 4b48cc7c2e3e87c9d0918cfb53f32ffc18064110 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 22 Jul 2016 16:40:23 -0700 Subject: [PATCH 13/25] Acutally audio was really broken Seems there were 2 issues: * If you couldn't inject locally (say, no audio interface because you are an assignment client), we would not do the network injection at all. That's bad. * When we don't have a codec, like when using an injector, we were serializing the empty string in some problematic way. I just use the built-in serialization, and it seems fine. I tested this by first playing a sound in the console (or maybe you want to tip some cows, that is ok too). Then in _another_ interface client on another machine in the same domain, I better hear the sound. Then, I added a script to play audio as a persistent script, which just loops forever and ever. You should hear that on both interface clients also. A detailed test plan to follow. Also: using @zappoman serialization code, which is safer than using the Qt code which does magical things for nulls. Good to do this cuz you know, things happen... --- libraries/audio/src/AudioInjector.cpp | 46 +++++++++++++-------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 873e9b7923..b2658800a8 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -203,8 +203,8 @@ bool AudioInjector::injectLocally() { } if (!success) { - // we never started so we are finished, call our stop method - stop(); + // we never started so we are finished with local injection + finishLocalInjection(); } return success; @@ -217,8 +217,15 @@ static const int64_t NEXT_FRAME_DELTA_IMMEDIATELY = 0; qint64 writeStringToStream(const QString& string, QDataStream& stream) { QByteArray data = string.toUtf8(); uint32_t length = data.length(); - stream << static_cast(length); - stream << data; + if (length == 0) { + stream << static_cast(length); + } else { + // http://doc.qt.io/qt-5/datastreamformat.html + // QDataStream << QByteArray - + // If the byte array is null : 0xFFFFFFFF (quint32) + // Otherwise : the array size(quint32) followed by the array bytes, i.e.size bytes + stream << data; + } return length + sizeof(uint32_t); } @@ -232,7 +239,7 @@ int64_t AudioInjector::injectNextFrame() { static int positionOptionOffset = -1; static int volumeOptionOffset = -1; static int audioDataOffset = -1; - + if (!_currentPacket) { if (_currentSendOffset < 0 || _currentSendOffset >= _audioData.size()) { @@ -270,7 +277,7 @@ int64_t AudioInjector::injectNextFrame() { // current injectors don't use codecs, so pack in the unknown codec name QString noCodecForInjectors(""); - writeStringToStream(noCodecForInjectors, audioPacketStream); + writeStringToStream(noCodecForInjectors, audioPacketStream); // pack stream identifier (a generated UUID) audioPacketStream << QUuid::createUuid(); @@ -301,7 +308,6 @@ int64_t AudioInjector::injectNextFrame() { volumeOptionOffset = _currentPacket->pos(); quint8 volume = MAX_INJECTOR_VOLUME; audioPacketStream << volume; - audioPacketStream << _options.ignorePenumbra; audioDataOffset = _currentPacket->pos(); @@ -312,7 +318,6 @@ int64_t AudioInjector::injectNextFrame() { return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; } } - if (!_frameTimer->isValid()) { // in the case where we have been restarted, the frame timer will be invalid and we need to start it back over here _frameTimer->restart(); @@ -418,7 +423,7 @@ void AudioInjector::triggerDeleteAfterFinish() { return; } - if (_state == AudioInjectorState::Finished) { + if (stateHas(AudioInjectorState::Finished)) { stopAndDeleteLater(); } else { _state |= AudioInjectorState::PendingDelete; @@ -484,22 +489,17 @@ AudioInjector* AudioInjector::playSound(const QByteArray& buffer, const AudioInj // setup parameters required for injection injector->setupInjection(); - // we always inject locally - // - if (!injector->injectLocally()) { - // failed, so don't bother sending to server - qDebug() << "AudioInjector::playSound failed to inject locally"; - return nullptr; - } + // we always inject locally, except when there is no localInterface + injector->injectLocally(); + // if localOnly, we are done, just return injector. - if (options.localOnly) { - return injector; - } + if (!options.localOnly) { - // send off to server for everyone else - if (!injectorManager->threadInjector(injector)) { - // we failed to thread the new injector (we are at the max number of injector threads) - qDebug() << "AudioInjector::playSound failed to thread injector"; + // send off to server for everyone else + if (!injectorManager->threadInjector(injector)) { + // we failed to thread the new injector (we are at the max number of injector threads) + qDebug() << "AudioInjector::playSound failed to thread injector"; + } } return injector; From 8b8a98bfccbcd55885c474d12268a706d51c3fea Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 22 Jul 2016 16:40:23 -0700 Subject: [PATCH 14/25] Acutally audio was really broken Seems there were 2 issues: * If you couldn't inject locally (say, no audio interface because you are an assignment client), we would not do the network injection at all. That's bad. * When we don't have a codec, like when using an injector, we were serializing the empty string in some problematic way. I just use the built-in serialization, and it seems fine. I tested this by first playing a sound in the console (or maybe you want to tip some cows, that is ok too). Then in _another_ interface client on another machine in the same domain, I better hear the sound. Then, I added a script to play audio as a persistent script, which just loops forever and ever. You should hear that on both interface clients also. A detailed test plan to follow. Also: using @zappoman serialization code, which is safer than using the Qt code which does magical things for nulls. Good to do this cuz you know, things happen... --- libraries/audio/src/AudioInjector.cpp | 53 +++++++++++++++++---------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 878a4c627c..244740320b 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -159,8 +159,8 @@ bool AudioInjector::injectLocally() { } if (!success) { - // we never started so we are finished, call our stop method - stop(); + // we never started so we are finished with local injection + finishLocalInjection(); } return success; @@ -170,6 +170,21 @@ const uchar MAX_INJECTOR_VOLUME = 0xFF; static const int64_t NEXT_FRAME_DELTA_ERROR_OR_FINISHED = -1; static const int64_t NEXT_FRAME_DELTA_IMMEDIATELY = 0; +qint64 writeStringToStream(const QString& string, QDataStream& stream) { + QByteArray data = string.toUtf8(); + uint32_t length = data.length(); + if (length == 0) { + stream << static_cast(length); + } else { + // http://doc.qt.io/qt-5/datastreamformat.html + // QDataStream << QByteArray - + // If the byte array is null : 0xFFFFFFFF (quint32) + // Otherwise : the array size(quint32) followed by the array bytes, i.e.size bytes + stream << data; + } + return length + sizeof(uint32_t); +} + int64_t AudioInjector::injectNextFrame() { if (_state == AudioInjector::State::Finished) { qDebug() << "AudioInjector::injectNextFrame called but AudioInjector has finished and was not restarted. Returning."; @@ -180,7 +195,7 @@ int64_t AudioInjector::injectNextFrame() { static int positionOptionOffset = -1; static int volumeOptionOffset = -1; static int audioDataOffset = -1; - + if (!_currentPacket) { if (_currentSendOffset < 0 || _currentSendOffset >= _audioData.size()) { @@ -216,6 +231,10 @@ int64_t AudioInjector::injectNextFrame() { // pack some placeholder sequence number for now audioPacketStream << (quint16) 0; + // current injectors don't use codecs, so pack in the unknown codec name + QString noCodecForInjectors(""); + writeStringToStream(noCodecForInjectors, audioPacketStream); + // pack stream identifier (a generated UUID) audioPacketStream << QUuid::createUuid(); @@ -243,7 +262,6 @@ int64_t AudioInjector::injectNextFrame() { volumeOptionOffset = _currentPacket->pos(); quint8 volume = MAX_INJECTOR_VOLUME; audioPacketStream << volume; - audioPacketStream << _options.ignorePenumbra; audioDataOffset = _currentPacket->pos(); @@ -254,7 +272,6 @@ int64_t AudioInjector::injectNextFrame() { return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; } } - if (!_frameTimer->isValid()) { // in the case where we have been restarted, the frame timer will be invalid and we need to start it back over here _frameTimer->restart(); @@ -353,7 +370,7 @@ void AudioInjector::triggerDeleteAfterFinish() { return; } - if (_state == State::Finished) { + if (stateHas(AudioInjectorState::Finished)) { stopAndDeleteLater(); } else { _state = State::NotFinishedWithPendingDelete; @@ -419,21 +436,17 @@ AudioInjector* AudioInjector::playSound(const QByteArray& buffer, const AudioInj // setup parameters required for injection injector->setupInjection(); - if (options.localOnly) { - if (injector->injectLocally()) { - // local injection succeeded, return the pointer to injector - return injector; - } else { - // unable to inject locally, return a nullptr - return nullptr; - } - } else { - // attempt to thread the new injector - if (injectorManager->threadInjector(injector)) { - return injector; - } else { + // we always inject locally, except when there is no localInterface + injector->injectLocally(); + + // if localOnly, we are done, just return injector. + if (!options.localOnly) { + + // send off to server for everyone else + if (!injectorManager->threadInjector(injector)) { // we failed to thread the new injector (we are at the max number of injector threads) - return nullptr; + qDebug() << "AudioInjector::playSound failed to thread injector"; } } + return injector; } From 7134d2bfd4baff5b19040c3a9373c41443186d57 Mon Sep 17 00:00:00 2001 From: Christoph Haag Date: Sat, 23 Jul 2016 11:09:16 +0200 Subject: [PATCH 15/25] fix grid shader this shader is used for example in developer - audio - audio scope - show scope --- libraries/render-utils/src/grid.slf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/grid.slf b/libraries/render-utils/src/grid.slf index 901d343268..a680e7093b 100644 --- a/libraries/render-utils/src/grid.slf +++ b/libraries/render-utils/src/grid.slf @@ -20,7 +20,7 @@ struct Grid { }; uniform gridBuffer { Grid grid; }; -Grid getGrid() { return grid; }; +Grid getGrid() { return grid; } in vec2 varTexCoord0; in vec4 varColor; From 7280992806ea11ac6b176677c0d932647da956fa Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sat, 23 Jul 2016 10:30:44 -0700 Subject: [PATCH 16/25] add mutex lock around AvatarData joint data --- libraries/avatars/src/AvatarData.cpp | 101 ++++++++++++++++++--------- libraries/avatars/src/AvatarData.h | 5 +- 2 files changed, 70 insertions(+), 36 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index e73702cd95..9034f58c93 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -256,6 +256,8 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { destinationBuffer += _headData->_blendshapeCoefficients.size() * sizeof(float); } + QReadLocker readLock(&_jointDataLock); + // joint rotation data *destinationBuffer++ = _jointData.size(); unsigned char* validityPosition = destinationBuffer; @@ -378,6 +380,7 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { void AvatarData::doneEncoding(bool cullSmallChanges) { // The server has finished sending this version of the joint-data to other nodes. Update _lastSentJointData. + QReadLocker readLock(&_jointDataLock); _lastSentJointData.resize(_jointData.size()); for (int i = 0; i < _jointData.size(); i ++) { const JointData& data = _jointData[ i ]; @@ -551,8 +554,6 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { PACKET_READ_CHECK(NumJoints, sizeof(uint8_t)); int numJoints = *sourceBuffer++; - _jointData.resize(numJoints); - const int bytesOfValidity = (int)ceil((float)numJoints / (float)BITS_IN_BYTE); PACKET_READ_CHECK(JointRotationValidityBits, bytesOfValidity); @@ -576,6 +577,9 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { } // each joint rotation is stored in 6 bytes. + QWriteLocker writeLock(&_jointDataLock); + _jointData.resize(numJoints); + const int COMPRESSED_QUATERNION_SIZE = 6; PACKET_READ_CHECK(JointRotations, numValidJointRotations * COMPRESSED_QUATERNION_SIZE); for (int i = 0; i < numJoints; i++) { @@ -653,6 +657,7 @@ void AvatarData::setRawJointData(QVector data) { QMetaObject::invokeMethod(this, "setRawJointData", Q_ARG(QVector, data)); return; } + QWriteLocker writeLock(&_jointDataLock); _jointData = data; } @@ -664,6 +669,7 @@ void AvatarData::setJointData(int index, const glm::quat& rotation, const glm::v QMetaObject::invokeMethod(this, "setJointData", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation)); return; } + QWriteLocker writeLock(&_jointDataLock); if (_jointData.size() <= index) { _jointData.resize(index + 1); } @@ -682,6 +688,8 @@ void AvatarData::clearJointData(int index) { QMetaObject::invokeMethod(this, "clearJointData", Q_ARG(int, index)); return; } + QWriteLocker writeLock(&_jointDataLock); + // BUG: I don't understand how this "clears" the joint data at index if (_jointData.size() <= index) { _jointData.resize(index + 1); } @@ -710,6 +718,7 @@ glm::quat AvatarData::getJointRotation(int index) const { Q_RETURN_ARG(glm::quat, result), Q_ARG(int, index)); return result; } + QReadLocker readLock(&_jointDataLock); return index < _jointData.size() ? _jointData.at(index).rotation : glm::quat(); } @@ -724,6 +733,7 @@ glm::vec3 AvatarData::getJointTranslation(int index) const { Q_RETURN_ARG(glm::vec3, result), Q_ARG(int, index)); return result; } + QReadLocker readLock(&_jointDataLock); return index < _jointData.size() ? _jointData.at(index).translation : glm::vec3(); } @@ -771,6 +781,7 @@ void AvatarData::setJointRotation(int index, const glm::quat& rotation) { QMetaObject::invokeMethod(this, "setJointRotation", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation)); return; } + QWriteLocker writeLock(&_jointDataLock); if (_jointData.size() <= index) { _jointData.resize(index + 1); } @@ -787,6 +798,7 @@ void AvatarData::setJointTranslation(int index, const glm::vec3& translation) { QMetaObject::invokeMethod(this, "setJointTranslation", Q_ARG(int, index), Q_ARG(const glm::vec3&, translation)); return; } + QWriteLocker writeLock(&_jointDataLock); if (_jointData.size() <= index) { _jointData.resize(index + 1); } @@ -831,6 +843,7 @@ QVector AvatarData::getJointRotations() const { Q_RETURN_ARG(QVector, result)); return result; } + QReadLocker readLock(&_jointDataLock); QVector jointRotations(_jointData.size()); for (int i = 0; i < _jointData.size(); ++i) { jointRotations[i] = _jointData[i].rotation; @@ -845,6 +858,7 @@ void AvatarData::setJointRotations(QVector jointRotations) { "setJointRotations", Qt::BlockingQueuedConnection, Q_ARG(QVector, jointRotations)); } + QWriteLocker writeLock(&_jointDataLock); if (_jointData.size() < jointRotations.size()) { _jointData.resize(jointRotations.size()); } @@ -862,6 +876,7 @@ void AvatarData::setJointTranslations(QVector jointTranslations) { "setJointTranslations", Qt::BlockingQueuedConnection, Q_ARG(QVector, jointTranslations)); } + QWriteLocker writeLock(&_jointDataLock); if (_jointData.size() < jointTranslations.size()) { _jointData.resize(jointTranslations.size()); } @@ -873,11 +888,23 @@ void AvatarData::setJointTranslations(QVector jointTranslations) { } void AvatarData::clearJointsData() { + // BUG: this method is terribly inefficient and probably doesn't even work + // (see implementation of clearJointData(index)) for (int i = 0; i < _jointData.size(); ++i) { clearJointData(i); } } +int AvatarData::getJointIndex(const QString& name) const { + QReadLocker readLock(&_jointDataLock); + return _jointIndices.value(name) - 1; +} + +QStringList AvatarData::getJointNames() const { + QReadLocker readLock(&_jointDataLock); + return _jointNames; +} + void AvatarData::parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut) { QDataStream packetStream(data); @@ -1027,38 +1054,41 @@ void AvatarData::detachAll(const QString& modelURL, const QString& jointName) { void AvatarData::setJointMappingsFromNetworkReply() { QNetworkReply* networkReply = static_cast(sender()); - QByteArray line; - while (!(line = networkReply->readLine()).isEmpty()) { - line = line.trimmed(); - if (line.startsWith("filename")) { - int filenameIndex = line.indexOf('=') + 1; - if (filenameIndex > 0) { - _skeletonFBXURL = _skeletonModelURL.resolved(QString(line.mid(filenameIndex).trimmed())); + { + QWriteLocker writeLock(&_jointDataLock); + QByteArray line; + while (!(line = networkReply->readLine()).isEmpty()) { + line = line.trimmed(); + if (line.startsWith("filename")) { + int filenameIndex = line.indexOf('=') + 1; + if (filenameIndex > 0) { + _skeletonFBXURL = _skeletonModelURL.resolved(QString(line.mid(filenameIndex).trimmed())); + } } + if (!line.startsWith("jointIndex")) { + continue; } - if (!line.startsWith("jointIndex")) { - continue; - } - int jointNameIndex = line.indexOf('=') + 1; - if (jointNameIndex == 0) { - continue; - } - int secondSeparatorIndex = line.indexOf('=', jointNameIndex); - if (secondSeparatorIndex == -1) { - continue; - } - QString jointName = line.mid(jointNameIndex, secondSeparatorIndex - jointNameIndex).trimmed(); - bool ok; - int jointIndex = line.mid(secondSeparatorIndex + 1).trimmed().toInt(&ok); - if (ok) { - while (_jointNames.size() < jointIndex + 1) { - _jointNames.append(QString()); + int jointNameIndex = line.indexOf('=') + 1; + if (jointNameIndex == 0) { + continue; + } + int secondSeparatorIndex = line.indexOf('=', jointNameIndex); + if (secondSeparatorIndex == -1) { + continue; + } + QString jointName = line.mid(jointNameIndex, secondSeparatorIndex - jointNameIndex).trimmed(); + bool ok; + int jointIndex = line.mid(secondSeparatorIndex + 1).trimmed().toInt(&ok); + if (ok) { + while (_jointNames.size() < jointIndex + 1) { + _jointNames.append(QString()); + } + _jointNames[jointIndex] = jointName; } - _jointNames[jointIndex] = jointName; } - } - for (int i = 0; i < _jointNames.size(); i++) { - _jointIndices.insert(_jointNames.at(i), i + 1); + for (int i = 0; i < _jointNames.size(); i++) { + _jointIndices.insert(_jointNames.at(i), i + 1); + } } networkReply->deleteLater(); @@ -1101,16 +1131,19 @@ void AvatarData::sendIdentityPacket() { } void AvatarData::updateJointMappings() { - _jointIndices.clear(); - _jointNames.clear(); - _jointData.clear(); + { + QWriteLocker writeLock(&_jointDataLock); + _jointIndices.clear(); + _jointNames.clear(); + _jointData.clear(); + } if (_skeletonModelURL.fileName().toLower().endsWith(".fst")) { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(_skeletonModelURL); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); QNetworkReply* networkReply = networkAccessManager.get(networkRequest); - connect(networkReply, SIGNAL(finished()), this, SLOT(setJointMappingsFromNetworkReply())); + connect(networkReply, &QNetworkReply::finished, this, &AvatarData::setJointMappingsFromNetworkReply); } } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 14b4f07471..b88cec3e05 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -271,9 +271,9 @@ public: Q_INVOKABLE virtual void clearJointsData(); /// Returns the index of the joint with the specified name, or -1 if not found/unknown. - Q_INVOKABLE virtual int getJointIndex(const QString& name) const { return _jointIndices.value(name) - 1; } + Q_INVOKABLE virtual int getJointIndex(const QString& name) const; - Q_INVOKABLE virtual QStringList getJointNames() const { return _jointNames; } + Q_INVOKABLE virtual QStringList getJointNames() const; Q_INVOKABLE void setBlendshape(QString name, float val) { _headData->setBlendshape(name, val); } @@ -374,6 +374,7 @@ protected: QVector _jointData; ///< the state of the skeleton joints QVector _lastSentJointData; ///< the state of the skeleton joints last time we transmitted + mutable QReadWriteLock _jointDataLock; // key state KeyState _keyState; From 85b0440e32acbfc9d995678de6a51554c7399e5f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 23 Jul 2016 10:58:35 -0700 Subject: [PATCH 17/25] Disable glow lines (and the use of geometry shaders) on non-Windows platforms for now --- .../src/display-plugins/hmd/HmdDisplayPlugin.cpp | 10 +++++++++- .../src/display-plugins/hmd/HmdDisplayPlugin.h | 8 ++++++++ libraries/render-utils/src/GeometryCache.cpp | 6 ++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 1bfa6c7921..306bc26a17 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -113,10 +113,12 @@ void HmdDisplayPlugin::customizeContext() { updateReprojectionProgram(); updateOverlayProgram(); +#ifdef HMD_HAND_LASER_SUPPORT updateLaserProgram(); - _laserGeometry = loadLaser(_laserProgram); +#endif } + //#define LIVE_SHADER_RELOAD 1 static QString readFile(const QString& filename) { @@ -162,6 +164,7 @@ void HmdDisplayPlugin::updateReprojectionProgram() { } +#ifdef HMD_HAND_LASER_SUPPORT void HmdDisplayPlugin::updateLaserProgram() { static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.vert"; static const QString gsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.geom"; @@ -202,6 +205,7 @@ void HmdDisplayPlugin::updateLaserProgram() { } } } +#endif void HmdDisplayPlugin::updateOverlayProgram() { static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.vert"; @@ -249,8 +253,10 @@ void HmdDisplayPlugin::uncustomizeContext() { _compositeFramebuffer.reset(); _previewProgram.reset(); _reprojectionProgram.reset(); +#ifdef HMD_HAND_LASER_SUPPORT _laserProgram.reset(); _laserGeometry.reset(); +#endif Parent::uncustomizeContext(); } @@ -516,6 +522,7 @@ bool HmdDisplayPlugin::setHandLaser(uint32_t hands, HandLaserMode mode, const ve } void HmdDisplayPlugin::compositeExtra() { +#ifdef HMD_HAND_LASER_SUPPORT // If neither hand laser is activated, exit if (!_presentHandLasers[0].valid() && !_presentHandLasers[1].valid()) { return; @@ -584,4 +591,5 @@ void HmdDisplayPlugin::compositeExtra() { } }); glDisable(GL_BLEND); +#endif } diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h index f168ec9607..79e52f1406 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h @@ -14,6 +14,10 @@ #include "../OpenGLDisplayPlugin.h" +#ifdef Q_OS_WIN +#define HMD_HAND_LASER_SUPPORT +#endif + class HmdDisplayPlugin : public OpenGLDisplayPlugin { using Parent = OpenGLDisplayPlugin; public: @@ -93,7 +97,9 @@ protected: private: void updateOverlayProgram(); +#ifdef HMD_HAND_LASER_SUPPORT void updateLaserProgram(); +#endif void updateReprojectionProgram(); bool _enablePreview { false }; @@ -130,11 +136,13 @@ private: ShapeWrapperPtr _sphereSection; +#ifdef HMD_HAND_LASER_SUPPORT ProgramPtr _laserProgram; struct LaserUniforms { int32_t mvp { -1 }; int32_t color { -1 }; } _laserUniforms; ShapeWrapperPtr _laserGeometry; +#endif }; diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 50a93d2200..cebd8ad37f 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1552,6 +1552,12 @@ void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, const glm::vec4& color, float glowIntensity, float glowWidth, int id) { + + // Disable glow lines on OSX +#ifndef Q_OS_WIN + glowIntensity = 0.0f; +#endif + if (glowIntensity <= 0) { renderLine(batch, p1, p2, color, id); return; From 121a0f0f5ecc37f556acfbf861ba153a8a4652a7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 23 Jul 2016 11:41:58 -0700 Subject: [PATCH 18/25] Allow atomic editing of multiple overlays from scripts --- interface/src/ui/overlays/Overlays.cpp | 23 +++++++++++++++++++++++ interface/src/ui/overlays/Overlays.h | 4 ++++ 2 files changed, 27 insertions(+) diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index e99ca3a9e0..0b4bcc8652 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -234,6 +234,29 @@ bool Overlays::editOverlay(unsigned int id, const QVariant& properties) { return false; } +bool Overlays::editOverlays(const QVariant& propertiesById) { + QVariantMap map = propertiesById.toMap(); + bool success = true; + QWriteLocker lock(&_lock); + for (const auto& key : map.keys()) { + bool convertSuccess; + unsigned int id = key.toUInt(&convertSuccess); + if (!convertSuccess) { + success = false; + continue; + } + + Overlay::Pointer thisOverlay = getOverlay(id); + if (!thisOverlay) { + success = false; + continue; + } + QVariant properties = map[key]; + thisOverlay->setProperties(properties.toMap()); + } + return success; +} + void Overlays::deleteOverlay(unsigned int id) { Overlay::Pointer overlayToDelete; diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index f47f8de153..99f74fa0f9 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -89,6 +89,10 @@ public slots: /// successful edit, if the input id is for an unknown overlay this function will have no effect bool editOverlay(unsigned int id, const QVariant& properties); + /// edits an overlay updating only the included properties, will return the identified OverlayID in case of + /// successful edit, if the input id is for an unknown overlay this function will have no effect + bool editOverlays(const QVariant& propertiesById); + /// deletes a particle void deleteOverlay(unsigned int id); From c85d6800c37b9dfeb84a976c359e778b18437e00 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sun, 24 Jul 2016 11:44:25 -0700 Subject: [PATCH 19/25] reduce activity logging spam --- libraries/networking/src/UserActivityLogger.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 4b71423883..e2dd110cfd 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -53,7 +53,6 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall detailsPart.setBody(QJsonDocument(details).toJson(QJsonDocument::Compact)); multipart->append(detailsPart); } - qCDebug(networking) << "Logging activity" << action; // if no callbacks specified, call our owns if (params.isEmpty()) { From 8acb30da8e1f19d8c906539cd0e16b1280e1fca2 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sun, 24 Jul 2016 16:54:00 -0700 Subject: [PATCH 20/25] add permissions bar --- interface/resources/qml/Browser.qml | 86 ++++++++++++++++++- .../resources/qml/controls-uit/WebView.qml | 4 - 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 3057cd3330..9d709a309f 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -16,6 +16,7 @@ ScrollingWindow { destroyOnHidden: true width: 800 height: 600 + property variant permissionsBar: {'securityOrigin':'none','feature':'none'} property alias webView: webview x: 100 y: 100 @@ -32,6 +33,21 @@ ScrollingWindow { } } + function showPermissionsBar(){ + console.log('should show permissions bar') + permissionsContainer.visible=true; + } + + function hidePermissionsBar(){ + console.log('should hide permissions bar') + permissionsContainer.visible=false; + } + + function allowPermissions(){ + webview.grantFeaturePermission(permissionsBar.securityOrigin, permissionsBar.feature, true); + hidePermissionsBar(); + } + Item { id:item width: pane.contentWidth @@ -70,6 +86,7 @@ ScrollingWindow { size: 48 MouseArea { anchors.fill: parent; onClicked: webview.goForward() } } + } Item { @@ -116,6 +133,7 @@ ScrollingWindow { if (text.indexOf("http") != 0) { text = "http://" + text } + root.hidePermissionsBar(); webview.url = text break; } @@ -123,16 +141,78 @@ ScrollingWindow { } } + Rectangle { + id:permissionsContainer + visible:false + color: "#000000" + width: parent.width + anchors.top: buttons.bottom + height:40 + z:100 + gradient: Gradient { + GradientStop { position: 0.0; color: "black" } + GradientStop { position: 1.0; color: "grey" } + } + + RalewayLight { + id: permissionsInfo + anchors.right:permissionsRow.left + anchors.rightMargin: 32 + anchors.topMargin:8 + anchors.top:parent.top + text: "This site wants to use your microphone/camera" + size: 18 + color: hifi.colors.white + } + + Row { + id: permissionsRow + spacing: 4 + anchors.top:parent.top + anchors.topMargin: 8 + anchors.right: parent.right + visible: true + z:101 + + Button { + id:allow + text: "Allow" + color: hifi.buttons.blue + colorScheme: root.colorScheme + width: 120 + enabled: true + onClicked: root.allowPermissions(); + z:101 + } + + Button { + id:block + text: "Block" + color: hifi.buttons.red + colorScheme: root.colorScheme + width: 120 + enabled: true + onClicked: root.hidePermissionsBar(); + z:101 + } + } + } + + + + WebEngineView { id: webview - url: "http://highfidelity.com" + url: "https://highfidelity.com" anchors.top: buttons.bottom anchors.topMargin: 8 anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right onFeaturePermissionRequested: { - grantFeaturePermission(securityOrigin, feature, true); + permissionsBar.securityOrigin =securityOrigin; + permissionsBar.feature = feature; + root.showPermissionsBar(); } onLoadingChanged: { if (loadRequest.status === WebEngineView.LoadSucceededStatus) { @@ -164,4 +244,4 @@ ScrollingWindow { break; } } -} // dialog +} // dialog \ No newline at end of file diff --git a/interface/resources/qml/controls-uit/WebView.qml b/interface/resources/qml/controls-uit/WebView.qml index acd0e1ae12..4c165fc587 100644 --- a/interface/resources/qml/controls-uit/WebView.qml +++ b/interface/resources/qml/controls-uit/WebView.qml @@ -48,10 +48,6 @@ WebEngineView { } } - onFeaturePermissionRequested: { - grantFeaturePermission(securityOrigin, feature, true); - } - onLoadingChanged: { // Required to support clicking on "hifi://" links if (WebEngineView.LoadStartedStatus == loadRequest.status) { From 3b25b36db32ee5586b8fb98e1e2ec0195eb91470 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sun, 24 Jul 2016 16:57:15 -0700 Subject: [PATCH 21/25] cleanup --- interface/resources/qml/Browser.qml | 70 +++++++++++++---------------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 9d709a309f..3dfcf98e25 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -34,12 +34,10 @@ ScrollingWindow { } function showPermissionsBar(){ - console.log('should show permissions bar') permissionsContainer.visible=true; } function hidePermissionsBar(){ - console.log('should hide permissions bar') permissionsContainer.visible=false; } @@ -141,39 +139,39 @@ ScrollingWindow { } } - Rectangle { - id:permissionsContainer - visible:false - color: "#000000" - width: parent.width - anchors.top: buttons.bottom - height:40 - z:100 - gradient: Gradient { - GradientStop { position: 0.0; color: "black" } - GradientStop { position: 1.0; color: "grey" } - } - - RalewayLight { - id: permissionsInfo - anchors.right:permissionsRow.left - anchors.rightMargin: 32 - anchors.topMargin:8 - anchors.top:parent.top - text: "This site wants to use your microphone/camera" - size: 18 - color: hifi.colors.white + Rectangle { + id:permissionsContainer + visible:false + color: "#000000" + width: parent.width + anchors.top: buttons.bottom + height:40 + z:100 + gradient: Gradient { + GradientStop { position: 0.0; color: "black" } + GradientStop { position: 1.0; color: "grey" } } - - Row { - id: permissionsRow - spacing: 4 - anchors.top:parent.top - anchors.topMargin: 8 - anchors.right: parent.right - visible: true - z:101 + RalewayLight { + id: permissionsInfo + anchors.right:permissionsRow.left + anchors.rightMargin: 32 + anchors.topMargin:8 + anchors.top:parent.top + text: "This site wants to use your microphone/camera" + size: 18 + color: hifi.colors.white + } + + Row { + id: permissionsRow + spacing: 4 + anchors.top:parent.top + anchors.topMargin: 8 + anchors.right: parent.right + visible: true + z:101 + Button { id:allow text: "Allow" @@ -196,10 +194,7 @@ ScrollingWindow { z:101 } } - } - - - + } WebEngineView { id: webview @@ -228,7 +223,6 @@ ScrollingWindow { request.openIn(newWindow.webView) } //profile: desktop.browserProfile - } } // item From d876251f1e9e36987c3094e52b119ae15aa180a9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sun, 24 Jul 2016 19:17:39 -0700 Subject: [PATCH 22/25] release geometry refs in Model::deleteGeometry() --- libraries/render-utils/src/Model.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index e2363d0cca..b04a1d8023 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1192,6 +1192,8 @@ void Model::deleteGeometry() { _meshStates.clear(); _rig->destroyAnimGraph(); _blendedBlendshapeCoefficients.clear(); + _renderGeometry.reset(); + _collisionGeometry.reset(); } AABox Model::getRenderableMeshBound() const { From 1df3b59aa19e19ae07ec009deaf1f0f92b7d248a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sun, 24 Jul 2016 19:19:35 -0700 Subject: [PATCH 23/25] change BUG to FIXME --- libraries/avatars/src/AvatarData.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 9034f58c93..dee0d1cb20 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -689,7 +689,7 @@ void AvatarData::clearJointData(int index) { return; } QWriteLocker writeLock(&_jointDataLock); - // BUG: I don't understand how this "clears" the joint data at index + // FIXME: I don't understand how this "clears" the joint data at index if (_jointData.size() <= index) { _jointData.resize(index + 1); } @@ -888,7 +888,7 @@ void AvatarData::setJointTranslations(QVector jointTranslations) { } void AvatarData::clearJointsData() { - // BUG: this method is terribly inefficient and probably doesn't even work + // FIXME: this method is terribly inefficient and probably doesn't even work // (see implementation of clearJointData(index)) for (int i = 0; i < _jointData.size(); ++i) { clearJointData(i); From 84e4de4bad8043136a560d6ad855155a9e12dde8 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 25 Jul 2016 11:11:37 -0700 Subject: [PATCH 24/25] update formatting --- interface/resources/qml/Browser.qml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 1287102bed..be9154dc77 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -141,17 +141,17 @@ ScrollingWindow { } Rectangle { - id:permissionsContainer - visible:false - color: "#000000" - width: parent.width - anchors.top: buttons.bottom - height:40 - z:100 - gradient: Gradient { - GradientStop { position: 0.0; color: "black" } - GradientStop { position: 1.0; color: "grey" } - } + id:permissionsContainer + visible:false + color: "#000000" + width: parent.width + anchors.top: buttons.bottom + height:40 + z:100 + gradient: Gradient { + GradientStop { position: 0.0; color: "black" } + GradientStop { position: 1.0; color: "grey" } + } RalewayLight { id: permissionsInfo @@ -206,7 +206,7 @@ ScrollingWindow { anchors.left: parent.left anchors.right: parent.right onFeaturePermissionRequested: { - permissionsBar.securityOrigin =securityOrigin; + permissionsBar.securityOrigin = securityOrigin; permissionsBar.feature = feature; root.showPermissionsBar(); } From 38765e6fa4b59e1f14ac79f800d09dd0916b5242 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 25 Jul 2016 11:12:34 -0700 Subject: [PATCH 25/25] extra space --- interface/resources/qml/Browser.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index be9154dc77..47231957c2 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -207,7 +207,7 @@ ScrollingWindow { anchors.right: parent.right onFeaturePermissionRequested: { permissionsBar.securityOrigin = securityOrigin; - permissionsBar.feature = feature; + permissionsBar.feature = feature; root.showPermissionsBar(); } onLoadingChanged: {