diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index cdaa825ad8..104f65d7d9 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -92,6 +92,9 @@ void Agent::run() { loop.exec(); + // let the AvatarData class use our QNetworkAcessManager + AvatarData::setNetworkAccessManager(networkManager); + QString scriptContents(reply->readAll()); qDebug() << "Downloaded script:" << scriptContents; diff --git a/examples/bot.js b/examples/bot.js index 53f723381c..c8280e063d 100644 --- a/examples/bot.js +++ b/examples/bot.js @@ -50,5 +50,6 @@ if (botNumber <= 20) { // there is no need to change the body model - we're using the default Avatar.faceModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newFaceFilePrefix + ".fst"; Avatar.skeletonModelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/meshes/" + newBodyFilePrefix + ".fst"; +Avatar.billboardURL = "https://dl.dropboxusercontent.com/u/1864924/bot-billboard.png"; Agent.isAvatar = true; \ No newline at end of file diff --git a/examples/clipboardExample.js b/examples/clipboardExample.js index 81f0daae10..4972505dfe 100644 --- a/examples/clipboardExample.js +++ b/examples/clipboardExample.js @@ -12,67 +12,67 @@ var selectedVoxel = { x: 0, y: 0, z: 0, s: 0 }; var selectedSize = 4; -function printKeyEvent(eventName, event) { - print(eventName); - print(" event.key=" + event.key); - print(" event.text=" + event.text); - print(" event.isShifted=" + event.isShifted); - print(" event.isControl=" + event.isControl); - print(" event.isMeta=" + event.isMeta); - print(" event.isAlt=" + event.isAlt); - print(" event.isKeypad=" + event.isKeypad); +function setupMenus() { + // hook up menus + Menu.menuItemEvent.connect(menuItemEvent); + + // delete the standard application menu item + Menu.removeMenuItem("Edit", "Cut"); + Menu.removeMenuItem("Edit", "Copy"); + Menu.removeMenuItem("Edit", "Paste"); + Menu.removeMenuItem("Edit", "Delete"); + Menu.removeMenuItem("Edit", "Nudge"); + Menu.removeMenuItem("File", "Export Voxels"); + Menu.removeMenuItem("File", "Import Voxels"); + + // delete the standard application menu item + Menu.addMenuItem({ menuName: "Edit", menuItemName: "Cut", shortcutKey: "CTRL+X", afterItem: "Voxels" }); + Menu.addMenuItem({ menuName: "Edit", menuItemName: "Copy", shortcutKey: "CTRL+C", afterItem: "Cut" }); + Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste", shortcutKey: "CTRL+V", afterItem: "Copy" }); + Menu.addMenuItem({ menuName: "Edit", menuItemName: "Nudge", shortcutKey: "CTRL+N", afterItem: "Paste" }); + Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete", shortcutKeyEvent: { text: "backspace" }, afterItem: "Nudge" }); + Menu.addMenuItem({ menuName: "File", menuItemName: "Export Voxels", shortcutKey: "CTRL+E", afterItem: "Voxels" }); + Menu.addMenuItem({ menuName: "File", menuItemName: "Import Voxels", shortcutKey: "CTRL+I", afterItem: "Export Voxels" }); } - -function keyPressEvent(event) { - var debug = false; +function menuItemEvent(menuItem) { + var debug = true; if (debug) { - printKeyEvent("keyPressEvent", event); - } -} - -function keyReleaseEvent(event) { - var debug = false; - if (debug) { - printKeyEvent("keyReleaseEvent", event); + print("menuItemEvent " + menuItem); } // Note: this sample uses Alt+ as the key codes for these clipboard items - if ((event.key == 199 || event.key == 67 || event.text == "C" || event.text == "c") && event.isAlt) { - print("the Alt+C key was pressed"); + if (menuItem == "Copy") { + print("copying..."); Clipboard.copyVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); } - if ((event.key == 8776 || event.key == 88 || event.text == "X" || event.text == "x") && event.isAlt) { - print("the Alt+X key was pressed"); + if (menuItem == "Cut") { + print("cutting..."); Clipboard.cutVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); } - if ((event.key == 8730 || event.key == 86 || event.text == "V" || event.text == "v") && event.isAlt) { - print("the Alt+V key was pressed"); + if (menuItem == "Paste") { + print("pasting..."); Clipboard.pasteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); } - if (event.text == "DELETE" || event.text == "BACKSPACE") { - print("the DELETE/BACKSPACE key was pressed"); + if (menuItem == "Delete") { + print("deleting..."); Clipboard.deleteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); } - if ((event.text == "E" || event.text == "e") && event.isMeta) { - print("the Ctl+E key was pressed"); + if (menuItem == "Export Voxels") { + print("export"); Clipboard.exportVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); } - if ((event.text == "I" || event.text == "i") && event.isMeta) { - print("the Ctl+I key was pressed"); + if (menuItem == "Import Voxels") { + print("import"); Clipboard.importVoxels(); } - if ((event.key == 78 || event.text == "N" || event.text == "n") && event.isMeta) { - print("the Ctl+N key was pressed, nudging to left 1 meter"); + if (menuItem == "Nudge") { + print("nudge"); Clipboard.nudgeVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s, { x: -1, y: 0, z: 0 }); } } -// Map keyPress and mouse move events to our callbacks -Controller.keyPressEvent.connect(keyPressEvent); -Controller.keyReleaseEvent.connect(keyReleaseEvent); - var selectCube = Overlays.addOverlay("cube", { position: { x: 0, y: 0, z: 0}, size: selectedSize, @@ -149,3 +149,5 @@ function scriptEnding() { } Script.scriptEnding.connect(scriptEnding); + +setupMenus(); \ No newline at end of file diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 53d3869075..a910601c20 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -164,7 +164,7 @@ for (s = 0; s < numColors; s++) { width: swatchWidth, height: swatchHeight, subImage: { x: imageFromX, y: imageFromY, width: (swatchWidth - 1), height: swatchHeight }, - imageURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/testing-swatches.svg", + imageURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/swatches.svg", color: colors[s], alpha: 1, visible: editToolsOn @@ -1012,43 +1012,72 @@ function keyReleaseEvent(event) { // handle clipboard items if (selectToolSelected) { - var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY); - var intersection = Voxels.findRayIntersection(pickRay); - selectedVoxel = calculateVoxelFromIntersection(intersection,"select"); - - // Note: this sample uses Alt+ as the key codes for these clipboard items - if ((event.key == 199 || event.key == 67 || event.text == "C" || event.text == "c") && event.isAlt) { - print("the Alt+C key was pressed... copy"); - Clipboard.copyVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); - } - if ((event.key == 8776 || event.key == 88 || event.text == "X" || event.text == "x") && event.isAlt) { - print("the Alt+X key was pressed... cut"); - Clipboard.cutVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); - } - if ((event.key == 8730 || event.key == 86 || event.text == "V" || event.text == "v") && event.isAlt) { - print("the Alt+V key was pressed... paste"); - Clipboard.pasteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); - } - if (event.text == "DELETE" || event.text == "BACKSPACE") { - print("the DELETE/BACKSPACE key was pressed... delete"); + // menu tied to BACKSPACE, so we handle DELETE key here... + if (event.text == "DELETE") { + var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY); + var intersection = Voxels.findRayIntersection(pickRay); + selectedVoxel = calculateVoxelFromIntersection(intersection,"select"); + print("the DELETE key was pressed... delete"); Clipboard.deleteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); } - - if ((event.text == "E" || event.text == "e") && event.isMeta) { - print("the Ctl+E key was pressed... export"); - Clipboard.exportVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); - } - if ((event.text == "I" || event.text == "i") && event.isMeta) { - print("the Ctl+I key was pressed... import"); - Clipboard.importVoxels(); - } - if ((event.key == 78 || event.text == "N" || event.text == "n") && event.isMeta) { - print("the Ctl+N key was pressed, nudging to left 1 meter... nudge"); - Clipboard.nudgeVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s, { x: -1, y: 0, z: 0 }); - } } } +function setupMenus() { + // hook up menus + Menu.menuItemEvent.connect(menuItemEvent); + + // delete the standard application menu item + Menu.addSeparator("Edit", "Voxels"); + Menu.addMenuItem({ menuName: "Edit", menuItemName: "Cut", shortcutKey: "CTRL+X", afterItem: "Voxels" }); + Menu.addMenuItem({ menuName: "Edit", menuItemName: "Copy", shortcutKey: "CTRL+C", afterItem: "Cut" }); + Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste", shortcutKey: "CTRL+V", afterItem: "Copy" }); + Menu.addMenuItem({ menuName: "Edit", menuItemName: "Nudge", shortcutKey: "CTRL+N", afterItem: "Paste" }); + Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete", shortcutKeyEvent: { text: "backspace" }, afterItem: "Nudge" }); + + Menu.addSeparator("File", "Voxels"); + Menu.addMenuItem({ menuName: "File", menuItemName: "Export Voxels", shortcutKey: "CTRL+E", afterItem: "Voxels" }); + Menu.addMenuItem({ menuName: "File", menuItemName: "Import Voxels", shortcutKey: "CTRL+I", afterItem: "Export Voxels" }); +} + +function menuItemEvent(menuItem) { + + // handle clipboard items + if (selectToolSelected) { + var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY); + var intersection = Voxels.findRayIntersection(pickRay); + selectedVoxel = calculateVoxelFromIntersection(intersection,"select"); + if (menuItem == "Copy") { + print("copying..."); + Clipboard.copyVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + } + if (menuItem == "Cut") { + print("cutting..."); + Clipboard.cutVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + } + if (menuItem == "Paste") { + print("pasting..."); + Clipboard.pasteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + } + if (menuItem == "Delete") { + print("deleting..."); + Clipboard.deleteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + } + + if (menuItem == "Export Voxels") { + print("export"); + Clipboard.exportVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); + } + if (menuItem == "Import Voxels") { + print("import"); + Clipboard.importVoxels(); + } + if (menuItem == "Nudge") { + print("nudge"); + Clipboard.nudgeVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s, { x: -1, y: 0, z: 0 }); + } + } +} function mouseMoveEvent(event) { if (!editToolsOn) { @@ -1403,8 +1432,6 @@ function scriptEnding() { } Script.scriptEnding.connect(scriptEnding); - Script.willSendVisualDataCallback.connect(update); - - +setupMenus(); diff --git a/examples/menuExample.js b/examples/menuExample.js new file mode 100644 index 0000000000..3b18021302 --- /dev/null +++ b/examples/menuExample.js @@ -0,0 +1,104 @@ +// +// menuExample.js +// hifi +// +// Created by Brad Hefta-Gaub on 2/24/14 +// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// +// This is an example script that demonstrates use of the Menu object +// + + +function setupMenus() { + Menu.addMenu("Foo"); + Menu.addMenuItem("Foo","Foo item 1", "SHIFT+CTRL+F" ); + Menu.addMenuItem("Foo","Foo item 2", "SHIFT+F" ); + Menu.addMenuItem("Foo","Foo item 3", "META+F" ); + Menu.addMenuItem({ + menuName: "Foo", + menuItemName: "Foo item 4", + isCheckable: true, + isChecked: true + }); + + Menu.addMenuItem({ + menuName: "Foo", + menuItemName: "Foo item 5", + shortcutKey: "ALT+F", + isCheckable: true + }); + + + Menu.addSeparator("Foo","Removable Tools"); + Menu.addMenuItem("Foo","Remove Foo item 4"); + Menu.addMenuItem("Foo","Remove Foo"); + Menu.addMenu("Bar"); + + Menu.addMenuItem("Bar","Bar item 1", "b"); + Menu.addMenuItem({ + menuName: "Bar", + menuItemName: "Bar item 2", + shortcutKeyEvent: { text: "B", isControl: true } + }); + + Menu.addMenu("Bar > Spam"); + Menu.addMenuItem("Bar > Spam","Spam item 1"); + Menu.addMenuItem({ + menuName: "Bar > Spam", + menuItemName: "Spam item 2", + isCheckable: true, + isChecked: false + }); + + Menu.addSeparator("Bar > Spam","Other Items"); + Menu.addMenuItem("Bar > Spam","Remove Spam item 2"); + Menu.addMenuItem("Foo","Remove Spam item 2"); + + Menu.addMenuItem({ + menuName: "Foo", + menuItemName: "Remove Spam item 2" + }); + + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "before Cut", + beforeItem: "Cut" + }); + + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "after Nudge", + afterItem: "Nudge" + }); + +} + +function scriptEnding() { + print("SCRIPT ENDNG!!!\n"); + + Menu.removeMenu("Foo"); + Menu.removeMenu("Bar"); +} + +function menuItemEvent(menuItem) { + print("menuItemEvent() in JS... menuItem=" + menuItem); + if (menuItem == "Foo item 4") { + print(" checked=" + Menu.isOptionChecked("Foo item 4")); + } + if (menuItem == "Remove Foo item 4") { + Menu.removeMenuItem("Foo", "Foo item 4"); + } + if (menuItem == "Remove Foo") { + Menu.removeMenu("Foo"); + } + if (menuItem == "Remove Spam item 2") { + Menu.removeMenuItem("Bar > Spam", "Spam item 2"); + } +} + +setupMenus(); + +// register our scriptEnding callback +Script.scriptEnding.connect(scriptEnding); + +Menu.menuItemEvent.connect(menuItemEvent); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 035845eda4..2fb91a8ef1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -65,7 +65,7 @@ #include "ClipboardScriptingInterface.h" #include "InterfaceVersion.h" #include "Menu.h" -#include "Swatch.h" +#include "MenuScriptingInterface.h" #include "Util.h" #include "devices/OculusManager.h" #include "devices/TV3DManager.h" @@ -139,13 +139,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _isTouchPressed(false), _mousePressed(false), _isHoverVoxel(false), - _mouseVoxelScale(1.0f / 1024.0f), - _mouseVoxelScaleInitialized(false), - _justEditedVoxel(false), _isHighlightVoxel(false), - _nudgeStarted(false), - _lookingAlongX(false), - _lookingAwayFromOrigin(true), _chatEntryOn(false), _audio(&_audioScope, STARTUP_JITTER_SAMPLES), _enableProcessVoxelsThread(true), @@ -155,8 +149,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _bytesPerSecond(0), _recentMaxPackets(0), _resetRecentMaxPacketsSoon(true), - _swatch(NULL), - _pasteMode(false), _logger(new FileLogger(this)) { switchToResourcesParentIfRequired(); @@ -227,6 +219,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), &_voxels, SLOT(nodeAdded(SharedNodePointer))); connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_voxels, SLOT(nodeKilled(SharedNodePointer))); connect(nodeList, &NodeList::uuidChanged, this, &Application::updateWindowTitle); + connect(nodeList, &NodeList::limitOfSilentDomainCheckInsReached, nodeList, &NodeList::reset); // connect to appropriate slots on AccountManager AccountManager& accountManager = AccountManager::getInstance(); @@ -702,11 +695,6 @@ void Application::keyPressEvent(QKeyEvent* event) { bool isMeta = event->modifiers().testFlag(Qt::ControlModifier); switch (event->key()) { break; - case Qt::Key_Shift: - if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode)) { - _pasteMode = true; - } - break; case Qt::Key_BracketLeft: case Qt::Key_BracketRight: case Qt::Key_BraceLeft: @@ -731,14 +719,10 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_E: - if (_nudgeStarted) { - _nudgeGuidePosition.y += _mouseVoxel.s; - } else { - if (!_myAvatar->getDriveKeys(UP)) { - _myAvatar->jump(); - } - _myAvatar->setDriveKeys(UP, 1); + if (!_myAvatar->getDriveKeys(UP)) { + _myAvatar->jump(); } + _myAvatar->setDriveKeys(UP, 1); break; case Qt::Key_Asterisk: @@ -746,31 +730,11 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_C: - if (_nudgeStarted) { - _nudgeGuidePosition.y -= _mouseVoxel.s; - } else { - _myAvatar->setDriveKeys(DOWN, 1); - } + _myAvatar->setDriveKeys(DOWN, 1); break; case Qt::Key_W: - if (_nudgeStarted) { - if (_lookingAlongX) { - if (_lookingAwayFromOrigin) { - _nudgeGuidePosition.x += _mouseVoxel.s; - } else { - _nudgeGuidePosition.x -= _mouseVoxel.s; - } - } else { - if (_lookingAwayFromOrigin) { - _nudgeGuidePosition.z += _mouseVoxel.s; - } else { - _nudgeGuidePosition.z -= _mouseVoxel.s; - } - } - } else { - _myAvatar->setDriveKeys(FWD, 1); - } + _myAvatar->setDriveKeys(FWD, 1); break; case Qt::Key_S: @@ -780,20 +744,6 @@ void Application::keyPressEvent(QKeyEvent* event) { Menu::getInstance()->triggerOption(MenuOption::SuppressShortTimings); } else if (!isShifted && isMeta) { takeSnapshot(); - } else if (_nudgeStarted) { - if (_lookingAlongX) { - if (_lookingAwayFromOrigin) { - _nudgeGuidePosition.x -= _mouseVoxel.s; - } else { - _nudgeGuidePosition.x += _mouseVoxel.s; - } - } else { - if (_lookingAwayFromOrigin) { - _nudgeGuidePosition.z -= _mouseVoxel.s; - } else { - _nudgeGuidePosition.z += _mouseVoxel.s; - } - } } else { _myAvatar->setDriveKeys(BACK, 1); } @@ -806,147 +756,43 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_G: if (isShifted) { Menu::getInstance()->triggerOption(MenuOption::Gravity); - } else { - Menu::getInstance()->triggerOption(MenuOption::VoxelGetColorMode); } break; case Qt::Key_A: if (isShifted) { Menu::getInstance()->triggerOption(MenuOption::Atmosphere); - } else if (_nudgeStarted) { - if (_lookingAlongX) { - if (_lookingAwayFromOrigin) { - _nudgeGuidePosition.z -= _mouseVoxel.s; - } else { - _nudgeGuidePosition.z += _mouseVoxel.s; - } - } else { - if (_lookingAwayFromOrigin) { - _nudgeGuidePosition.x += _mouseVoxel.s; - } else { - _nudgeGuidePosition.x -= _mouseVoxel.s; - } - } } else { _myAvatar->setDriveKeys(ROT_LEFT, 1); } break; case Qt::Key_D: - if (_nudgeStarted) { - if (_lookingAlongX) { - if (_lookingAwayFromOrigin) { - _nudgeGuidePosition.z += _mouseVoxel.s; - } else { - _nudgeGuidePosition.z -= _mouseVoxel.s; - } - } else { - if (_lookingAwayFromOrigin) { - _nudgeGuidePosition.x -= _mouseVoxel.s; - } else { - _nudgeGuidePosition.x += _mouseVoxel.s; - } - } - } else { - _myAvatar->setDriveKeys(ROT_RIGHT, 1); - } + _myAvatar->setDriveKeys(ROT_RIGHT, 1); break; case Qt::Key_Return: case Qt::Key_Enter: - if (_nudgeStarted) { - nudgeVoxels(); - } else { - _chatEntryOn = true; - _myAvatar->setKeyState(NO_KEY_DOWN); - _myAvatar->setChatMessage(string()); - setMenuShortcutsEnabled(false); - } + _chatEntryOn = true; + _myAvatar->setKeyState(NO_KEY_DOWN); + _myAvatar->setChatMessage(string()); + setMenuShortcutsEnabled(false); break; case Qt::Key_Up: - if (_nudgeStarted && !isShifted) { - if (_lookingAlongX) { - if (_lookingAwayFromOrigin) { - _nudgeGuidePosition.x += _mouseVoxel.s; - } else { - _nudgeGuidePosition.x -= _mouseVoxel.s; - } - } else { - if (_lookingAwayFromOrigin) { - _nudgeGuidePosition.z += _mouseVoxel.s; - } else { - _nudgeGuidePosition.z -= _mouseVoxel.s; - } - } - } else if (_nudgeStarted && isShifted) { - _nudgeGuidePosition.y += _mouseVoxel.s; - } else { - _myAvatar->setDriveKeys(isShifted ? UP : FWD, 1); - } + _myAvatar->setDriveKeys(isShifted ? UP : FWD, 1); break; case Qt::Key_Down: - if (_nudgeStarted && !isShifted) { - if (_lookingAlongX) { - if (_lookingAwayFromOrigin) { - _nudgeGuidePosition.x -= _mouseVoxel.s; - } else { - _nudgeGuidePosition.x += _mouseVoxel.s; - } - } else { - if (_lookingAwayFromOrigin) { - _nudgeGuidePosition.z -= _mouseVoxel.s; - } else { - _nudgeGuidePosition.z += _mouseVoxel.s; - } - } - } else if (_nudgeStarted && isShifted) { - _nudgeGuidePosition.y -= _mouseVoxel.s; - } else { - _myAvatar->setDriveKeys(isShifted ? DOWN : BACK, 1); - } + _myAvatar->setDriveKeys(isShifted ? DOWN : BACK, 1); break; case Qt::Key_Left: - if (_nudgeStarted) { - if (_lookingAlongX) { - if (_lookingAwayFromOrigin) { - _nudgeGuidePosition.z -= _mouseVoxel.s; - } else { - _nudgeGuidePosition.z += _mouseVoxel.s; - } - } else { - if (_lookingAwayFromOrigin) { - _nudgeGuidePosition.x += _mouseVoxel.s; - } else { - _nudgeGuidePosition.x -= _mouseVoxel.s; - } - } - } else { - _myAvatar->setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1); - } + _myAvatar->setDriveKeys(isShifted ? LEFT : ROT_LEFT, 1); break; case Qt::Key_Right: - if (_nudgeStarted) { - if (_lookingAlongX) { - if (_lookingAwayFromOrigin) { - _nudgeGuidePosition.z += _mouseVoxel.s; - } else { - _nudgeGuidePosition.z -= _mouseVoxel.s; - } - } else { - if (_lookingAwayFromOrigin) { - _nudgeGuidePosition.x -= _mouseVoxel.s; - } else { - _nudgeGuidePosition.x += _mouseVoxel.s; - } - } - } else { - _myAvatar->setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1); - } + _myAvatar->setDriveKeys(isShifted ? RIGHT : ROT_RIGHT, 1); break; case Qt::Key_I: @@ -1028,9 +874,6 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_V: if (isShifted) { Menu::getInstance()->triggerOption(MenuOption::Voxels); - } else { - Menu::getInstance()->triggerOption(MenuOption::VoxelAddMode); - _nudgeStarted = false; } break; case Qt::Key_P: @@ -1039,29 +882,12 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_R: if (isShifted) { Menu::getInstance()->triggerOption(MenuOption::FrustumRenderMode); - } else { - Menu::getInstance()->triggerOption(MenuOption::VoxelDeleteMode); - _nudgeStarted = false; } break; - case Qt::Key_B: - Menu::getInstance()->triggerOption(MenuOption::VoxelColorMode); - _nudgeStarted = false; - break; - case Qt::Key_O: - Menu::getInstance()->triggerOption(MenuOption::VoxelSelectMode); - _nudgeStarted = false; break; case Qt::Key_Slash: Menu::getInstance()->triggerOption(MenuOption::Stats); break; - case Qt::Key_Backspace: - case Qt::Key_Delete: - if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelDeleteMode) || - Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode)) { - deleteVoxelUnderCursor(); - } - break; case Qt::Key_Plus: _myAvatar->increaseSize(); break; @@ -1072,16 +898,6 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar->resetSize(); break; - case Qt::Key_1: - case Qt::Key_2: - case Qt::Key_3: - case Qt::Key_4: - case Qt::Key_5: - case Qt::Key_6: - case Qt::Key_7: - case Qt::Key_8: - _swatch.handleEvent(event->key(), Menu::getInstance()->isOptionChecked(MenuOption::VoxelGetColorMode)); - break; case Qt::Key_At: Menu::getInstance()->goTo(); break; @@ -1109,9 +925,6 @@ void Application::keyReleaseEvent(QKeyEvent* event) { } switch (event->key()) { - case Qt::Key_Shift: - _pasteMode = false; - break; case Qt::Key_E: _myAvatar->setDriveKeys(UP, 0); break; @@ -1179,35 +992,10 @@ void Application::mouseMoveEvent(QMouseEvent* event) { _seenMouseMove = true; } - int deltaX = event->x() - _mouseX; - int deltaY = event->y() - _mouseY; _mouseX = event->x(); _mouseY = event->y(); if (activeWindow() == _window) { - // orbit behavior - if (_mousePressed && !Menu::getInstance()->isVoxelModeActionChecked()) { - if (_myAvatar->getLookAtTargetAvatar()) { - _myAvatar->orbit(_myAvatar->getLookAtTargetAvatar()->getPosition(), deltaX, deltaY); - return; - } - if (_isHoverVoxel) { - //_myAvatar->orbit(getMouseVoxelWorldCoordinates(_hoverVoxel), deltaX, deltaY); - return; - } - } - - // detect drag - glm::vec3 mouseVoxelPos(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z); - if (!_justEditedVoxel && mouseVoxelPos != _lastMouseVoxelPos) { - if (event->buttons().testFlag(Qt::LeftButton)) { - maybeEditVoxelUnderCursor(); - - } else if (event->buttons().testFlag(Qt::RightButton) && Menu::getInstance()->isVoxelModeActionChecked()) { - deleteVoxelUnderCursor(); - } - } - _pieMenu.mouseMoveEvent(_mouseX, _mouseY); } } @@ -1227,11 +1015,8 @@ void Application::mousePressEvent(QMouseEvent* event) { _mouseY = event->y(); _mouseDragStartedX = _mouseX; _mouseDragStartedY = _mouseY; - _mouseVoxelDragging = _mouseVoxel; _mousePressed = true; - maybeEditVoxelUnderCursor(); - if (_audio.mousePressEvent(_mouseX, _mouseY)) { // stop propagation return; @@ -1242,23 +1027,13 @@ void Application::mousePressEvent(QMouseEvent* event) { return; } - if (!_palette.isActive() && (!_isHoverVoxel || _myAvatar->getLookAtTargetAvatar())) { + if (!_isHoverVoxel || _myAvatar->getLookAtTargetAvatar()) { // disable for now // _pieMenu.mousePressEvent(_mouseX, _mouseY); } - if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode) && _pasteMode) { - pasteVoxels(); - } - } else if (event->button() == Qt::RightButton) { - if (Menu::getInstance()->isVoxelModeActionChecked()) { - deleteVoxelUnderCursor(); - } - if (_pasteMode) { - _pasteMode = false; - } - + // right click items here } } } @@ -1363,19 +1138,6 @@ void Application::wheelEvent(QWheelEvent* event) { if (_controllerScriptingInterface.isWheelCaptured()) { return; } - - // Wheel Events disabled for now because they are also activated by touch look pitch up/down. - if (USE_MOUSEWHEEL && (activeWindow() == _window)) { - if (!Menu::getInstance()->isVoxelModeActionChecked()) { - event->ignore(); - return; - } - if (event->delta() > 0) { - increaseVoxelSize(); - } else { - decreaseVoxelSize(); - } - } } void Application::dropEvent(QDropEvent *event) { @@ -1436,29 +1198,6 @@ void Application::timer() { } -static glm::vec3 getFaceVector(BoxFace face) { - switch (face) { - case MIN_X_FACE: - return glm::vec3(-1, 0, 0); - - case MAX_X_FACE: - return glm::vec3(1, 0, 0); - - case MIN_Y_FACE: - return glm::vec3(0, -1, 0); - - case MAX_Y_FACE: - return glm::vec3(0, 1, 0); - - case MIN_Z_FACE: - return glm::vec3(0, 0, -1); - - default: // quiet windows warnings - case MAX_Z_FACE: - return glm::vec3(0, 0, 1); - } -} - void Application::idle() { // Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing // details if we're in ExtraDebugging mode. However, the ::update() and it's subcomponents will show their timing @@ -1580,28 +1319,6 @@ glm::vec3 Application::getMouseVoxelWorldCoordinates(const VoxelDetail& mouseVox (mouseVoxel.z + mouseVoxel.s / 2.f) * TREE_SCALE); } -const float NUDGE_PRECISION_MIN = 1 / pow(2.0, 12.0); - -void Application::decreaseVoxelSize() { - if (_nudgeStarted) { - if (_mouseVoxelScale >= NUDGE_PRECISION_MIN) { - _mouseVoxelScale /= 2; - } - } else { - _mouseVoxelScale /= 2; - } -} - -void Application::increaseVoxelSize() { - if (_nudgeStarted) { - if (_mouseVoxelScale < _nudgeVoxel.s) { - _mouseVoxelScale *= 2; - } - } else { - _mouseVoxelScale *= 2; - } -} - const int MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE = 1500; struct SendVoxelsOperationArgs { const unsigned char* newBaseOctCode; @@ -1643,10 +1360,6 @@ bool Application::sendVoxelsOperation(OctreeElement* element, void* extraData) { return true; // keep going } -void Application::exportVoxels() { - exportVoxels(_mouseVoxel); -} - void Application::exportVoxels(const VoxelDetail& sourceVoxel) { QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); QString suggestedName = desktopLocation.append("/voxels.svo"); @@ -1666,41 +1379,11 @@ void Application::exportVoxels(const VoxelDetail& sourceVoxel) { _window->activateWindow(); } -void Application::importVoxels() { - if (!_voxelImporter) { - _voxelImporter = new VoxelImporter(_window); - _voxelImporter->loadSettings(_settings); - } - - if (!_voxelImporter->exec()) { - qDebug() << "[DEBUG] Import succeeded." << endl; - Menu::getInstance()->setIsOptionChecked(MenuOption::VoxelSelectMode, true); - _pasteMode = true; - } else { - qDebug() << "[DEBUG] Import failed." << endl; - if (_sharedVoxelSystem.getTree() == _voxelImporter->getVoxelTree()) { - _sharedVoxelSystem.killLocalVoxels(); - _sharedVoxelSystem.changeTree(&_clipboard); - } - } - - // restore the main window's active state - _window->activateWindow(); -} - -void Application::cutVoxels() { - cutVoxels(_mouseVoxel); -} - void Application::cutVoxels(const VoxelDetail& sourceVoxel) { copyVoxels(sourceVoxel); deleteVoxelAt(sourceVoxel); } -void Application::copyVoxels() { - copyVoxels(_mouseVoxel); -} - void Application::copyVoxels(const VoxelDetail& sourceVoxel) { // switch to and clear the clipboard first... _sharedVoxelSystem.killLocalVoxels(); @@ -1731,10 +1414,6 @@ void Application::pasteVoxelsToOctalCode(const unsigned char* octalCodeDestinati _voxelEditSender.releaseQueuedMessages(); } -void Application::pasteVoxels() { - pasteVoxels(_mouseVoxel); -} - void Application::pasteVoxels(const VoxelDetail& sourceVoxel) { unsigned char* calculatedOctCode = NULL; VoxelTreeElement* selectedNode = _voxels.getVoxelAt(sourceVoxel.x, sourceVoxel.y, sourceVoxel.z, sourceVoxel.s); @@ -1754,45 +1433,6 @@ void Application::pasteVoxels(const VoxelDetail& sourceVoxel) { if (calculatedOctCode) { delete[] calculatedOctCode; } - _pasteMode = false; -} - -void Application::findAxisAlignment() { - glm::vec3 direction = _myAvatar->getMouseRayDirection(); - if (fabs(direction.z) > fabs(direction.x)) { - _lookingAlongX = false; - if (direction.z < 0) { - _lookingAwayFromOrigin = false; - } else { - _lookingAwayFromOrigin = true; - } - } else { - _lookingAlongX = true; - if (direction.x < 0) { - _lookingAwayFromOrigin = false; - } else { - _lookingAwayFromOrigin = true; - } - } -} - -void Application::nudgeVoxels() { - VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); - if (!Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode) && selectedNode) { - Menu::getInstance()->triggerOption(MenuOption::VoxelSelectMode); - } - - if (!_nudgeStarted && selectedNode) { - _nudgeVoxel = _mouseVoxel; - _nudgeStarted = true; - _nudgeGuidePosition = glm::vec3(_nudgeVoxel.x, _nudgeVoxel.y, _nudgeVoxel.z); - findAxisAlignment(); - } else { - // calculate nudgeVec - glm::vec3 nudgeVec(_nudgeGuidePosition.x - _nudgeVoxel.x, _nudgeGuidePosition.y - _nudgeVoxel.y, _nudgeGuidePosition.z - _nudgeVoxel.z); - - nudgeVoxelsByVector(_nudgeVoxel, nudgeVec); - } } void Application::nudgeVoxelsByVector(const VoxelDetail& sourceVoxel, const glm::vec3& nudgeVec) { @@ -1802,10 +1442,6 @@ void Application::nudgeVoxelsByVector(const VoxelDetail& sourceVoxel, const glm: } } -void Application::deleteVoxels() { - deleteVoxelUnderCursor(); -} - void Application::initDisplay() { glEnable(GL_BLEND); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); @@ -1851,8 +1487,7 @@ void Application::init() { _myCamera.setModeShiftPeriod(1.0f); _mirrorCamera.setMode(CAMERA_MODE_MIRROR); - _mirrorCamera.setAspectRatio((float)MIRROR_VIEW_WIDTH / (float)MIRROR_VIEW_HEIGHT); - _mirrorCamera.setFieldOfView(30); + _mirrorCamera.setModeShiftPeriod(0.0f); OculusManager::connect(); if (OculusManager::isConnected()) { @@ -1905,14 +1540,6 @@ void Application::init() { ScriptEngine::getParticlesScriptingInterface(), SLOT(forwardParticleCollisionWithParticle(const ParticleID&, const ParticleID&))); - _palette.init(_glWidget->width(), _glWidget->height()); - _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelAddMode), 0, 0); - _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelDeleteMode), 0, 1); - _palette.addTool(&_swatch); - _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelColorMode), 0, 2); - _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelGetColorMode), 0, 3); - _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelSelectMode), 0, 4); - _pieMenu.init("./resources/images/hifi-interface-tools-v2-pie.svg", _glWidget->width(), _glWidget->height()); @@ -2055,7 +1682,10 @@ void Application::updateMyAvatarLookAtPosition() { } else if (_isHoverVoxel) { distance = glm::distance(_mouseRayOrigin, getMouseVoxelWorldCoordinates(_hoverVoxel)); } - lookAtSpot = _mouseRayOrigin + _mouseRayDirection * distance; + const float FIXED_MIN_EYE_DISTANCE = 0.3f; + float minEyeDistance = FIXED_MIN_EYE_DISTANCE + (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON ? 0.0f : + glm::distance(_mouseRayOrigin, _myAvatar->getHead()->calculateAverageEyePosition())); + lookAtSpot = _mouseRayOrigin + _mouseRayDirection * qMax(minEyeDistance, distance); } bool trackerActive = false; float eyePitch, eyeYaw; @@ -2092,94 +1722,6 @@ void Application::updateHoverVoxels(float deltaTime, float& distance, BoxFace& f } } -void Application::updateMouseVoxels(float deltaTime, float& distance, BoxFace& face) { - - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::updateMouseVoxels()"); - - _mouseVoxel.s = 0.0f; - bool wasInitialized = _mouseVoxelScaleInitialized; - if (Menu::getInstance()->isVoxelModeActionChecked() && - (fabs(_myAvatar->getVelocity().x) + - fabs(_myAvatar->getVelocity().y) + - fabs(_myAvatar->getVelocity().z)) / 3 < MAX_AVATAR_EDIT_VELOCITY) { - - if (_voxels.findRayIntersection(_mouseRayOrigin, _mouseRayDirection, _mouseVoxel, distance, face)) { - if (distance < MAX_VOXEL_EDIT_DISTANCE) { - // set the voxel scale to that of the first moused-over voxel - if (!wasInitialized) { - _mouseVoxelScale = _mouseVoxel.s; - } - _mouseVoxelScaleInitialized = true; - - // find the nearest voxel with the desired scale - if (_mouseVoxelScale > _mouseVoxel.s) { - // choose the larger voxel that encompasses the one selected - _mouseVoxel.x = _mouseVoxelScale * floorf(_mouseVoxel.x / _mouseVoxelScale); - _mouseVoxel.y = _mouseVoxelScale * floorf(_mouseVoxel.y / _mouseVoxelScale); - _mouseVoxel.z = _mouseVoxelScale * floorf(_mouseVoxel.z / _mouseVoxelScale); - _mouseVoxel.s = _mouseVoxelScale; - - } else { - glm::vec3 faceVector = getFaceVector(face); - if (_mouseVoxelScale < _mouseVoxel.s) { - // find the closest contained voxel - glm::vec3 pt = (_mouseRayOrigin + _mouseRayDirection * distance) / (float)TREE_SCALE - - faceVector * (_mouseVoxelScale * 0.5f); - _mouseVoxel.x = _mouseVoxelScale * floorf(pt.x / _mouseVoxelScale); - _mouseVoxel.y = _mouseVoxelScale * floorf(pt.y / _mouseVoxelScale); - _mouseVoxel.z = _mouseVoxelScale * floorf(pt.z / _mouseVoxelScale); - _mouseVoxel.s = _mouseVoxelScale; - } - if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode)) { - // use the face to determine the side on which to create a neighbor - _mouseVoxel.x += faceVector.x * _mouseVoxel.s; - _mouseVoxel.y += faceVector.y * _mouseVoxel.s; - _mouseVoxel.z += faceVector.z * _mouseVoxel.s; - } - } - } else { - _mouseVoxel.s = 0.0f; - } - } else if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode) - || Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode)) { - // place the voxel a fixed distance away - float worldMouseVoxelScale = _mouseVoxelScale * TREE_SCALE; - glm::vec3 pt = _mouseRayOrigin + _mouseRayDirection * (2.0f + worldMouseVoxelScale * 0.5f); - _mouseVoxel.x = _mouseVoxelScale * floorf(pt.x / worldMouseVoxelScale); - _mouseVoxel.y = _mouseVoxelScale * floorf(pt.y / worldMouseVoxelScale); - _mouseVoxel.z = _mouseVoxelScale * floorf(pt.z / worldMouseVoxelScale); - _mouseVoxel.s = _mouseVoxelScale; - } - - if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelDeleteMode)) { - // red indicates deletion - _mouseVoxel.red = 255; - _mouseVoxel.green = _mouseVoxel.blue = 0; - } else if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode)) { - if (_nudgeStarted) { - _mouseVoxel.red = _mouseVoxel.green = _mouseVoxel.blue = 255; - } else { - // yellow indicates selection - _mouseVoxel.red = _mouseVoxel.green = 255; - _mouseVoxel.blue = 0; - } - } else { // _addVoxelMode->isChecked() || _colorVoxelMode->isChecked() - QColor paintColor = Menu::getInstance()->getActionForOption(MenuOption::VoxelPaintColor)->data().value(); - _mouseVoxel.red = paintColor.red(); - _mouseVoxel.green = paintColor.green(); - _mouseVoxel.blue = paintColor.blue(); - } - - // if we just edited, use the currently selected voxel as the "last" for drag detection - if (_justEditedVoxel) { - _lastMouseVoxelPos = glm::vec3(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z); - _justEditedVoxel = false; - } - } -} - - void Application::updateHandAndTouch(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateHandAndTouch()"); @@ -2333,7 +1875,6 @@ void Application::update(float deltaTime) { BoxFace face; updateHoverVoxels(deltaTime, distance, face); // clicking on voxels and making sounds - updateMouseVoxels(deltaTime, distance, face); // UI/UX related to voxels updateHandAndTouch(deltaTime); // Update state for touch sensors updateLeap(deltaTime); // Leap finger-sensing device updateSixense(deltaTime); // Razer Hydra controllers @@ -2782,9 +2323,6 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { glEnable(GL_LIGHTING); glEnable(GL_DEPTH_TEST); - // Enable to show line from me to the voxel I am touching - //renderThrustAtVoxel(_voxelThrust); - if (!selfAvatarOnly) { // draw a red sphere float originSphereRadius = 0.05f; @@ -2845,72 +2383,6 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { if (_isHighlightVoxel) { renderHighlightVoxel(_highlightVoxel); } - - // indicate what we'll be adding/removing in mouse mode, if anything - if (_mouseVoxel.s != 0 && whichCamera.getMode() != CAMERA_MODE_MIRROR) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::displaySide() ... voxels TOOLS UX..."); - - glDisable(GL_LIGHTING); - glPushMatrix(); - glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE); - const float CUBE_EXPANSION = 1.01f; - if (_nudgeStarted) { - renderNudgeGuide(_nudgeGuidePosition.x, _nudgeGuidePosition.y, _nudgeGuidePosition.z, _nudgeVoxel.s); - renderNudgeGrid(_nudgeVoxel.x, _nudgeVoxel.y, _nudgeVoxel.z, _nudgeVoxel.s, _mouseVoxel.s); - glPushMatrix(); - glTranslatef(_nudgeVoxel.x + _nudgeVoxel.s * 0.5f, - _nudgeVoxel.y + _nudgeVoxel.s * 0.5f, - _nudgeVoxel.z + _nudgeVoxel.s * 0.5f); - glColor3ub(255, 255, 255); - glLineWidth(4.0f); - glutWireCube(_nudgeVoxel.s * CUBE_EXPANSION); - glPopMatrix(); - } else { - renderMouseVoxelGrid(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); - } - - if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode)) { - // use a contrasting color so that we can see what we're doing - glColor3ub(_mouseVoxel.red + 128, _mouseVoxel.green + 128, _mouseVoxel.blue + 128); - } else { - glColor3ub(_mouseVoxel.red, _mouseVoxel.green, _mouseVoxel.blue); - } - - if (_nudgeStarted) { - // render nudge guide cube - glTranslatef(_nudgeGuidePosition.x + _nudgeVoxel.s*0.5f, - _nudgeGuidePosition.y + _nudgeVoxel.s*0.5f, - _nudgeGuidePosition.z + _nudgeVoxel.s*0.5f); - glLineWidth(4.0f); - glutWireCube(_nudgeVoxel.s * CUBE_EXPANSION); - } else { - glTranslatef(_mouseVoxel.x + _mouseVoxel.s*0.5f, - _mouseVoxel.y + _mouseVoxel.s*0.5f, - _mouseVoxel.z + _mouseVoxel.s*0.5f); - glLineWidth(4.0f); - glutWireCube(_mouseVoxel.s * CUBE_EXPANSION); - } - glLineWidth(1.0f); - glPopMatrix(); - glEnable(GL_LIGHTING); - } - - if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode) && _pasteMode && whichCamera.getMode() != CAMERA_MODE_MIRROR) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::displaySide() ... PASTE Preview..."); - - glPushMatrix(); - glTranslatef(_mouseVoxel.x * TREE_SCALE, - _mouseVoxel.y * TREE_SCALE, - _mouseVoxel.z * TREE_SCALE); - glScalef(_mouseVoxel.s, - _mouseVoxel.s, - _mouseVoxel.s); - - _sharedVoxelSystem.render(); - glPopMatrix(); - } } bool forceRenderMyHead = (whichCamera.getInterpolatedMode() == CAMERA_MODE_MIRROR); @@ -3049,54 +2521,6 @@ void Application::displayOverlay() { drawtext(_glWidget->width() - 102, _glWidget->height() - timerBottom - 2, 0.30f, 0, 1.0f, 0, frameTimer, 1, 1, 1); } - _palette.render(_glWidget->width(), _glWidget->height()); - - QAction* paintColorAction = NULL; - if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelGetColorMode) - && (paintColorAction = Menu::getInstance()->getActionForOption(MenuOption::VoxelPaintColor))->data().value() - != _swatch.getColor()) { - QColor color = paintColorAction->data().value(); - TextRenderer textRenderer(SANS_FONT_FAMILY, 11, 50); - const char line1[] = "Assign this color to a swatch"; - const char line2[] = "by choosing a key from 1 to 8."; - - int left = (_glWidget->width() - POPUP_WIDTH - 2 * POPUP_MARGIN) / 2; - int top = _glWidget->height() / 40; - - glBegin(GL_POLYGON); - glColor3f(0.0f, 0.0f, 0.0f); - for (double a = M_PI; a < 1.5f * M_PI; a += POPUP_STEP) { - glVertex2f(left + POPUP_MARGIN * cos(a) , top + POPUP_MARGIN * sin(a)); - } - for (double a = 1.5f * M_PI; a < 2.0f * M_PI; a += POPUP_STEP) { - glVertex2f(left + POPUP_WIDTH + POPUP_MARGIN * cos(a), top + POPUP_MARGIN * sin(a)); - } - for (double a = 0.0f; a < 0.5f * M_PI; a += POPUP_STEP) { - glVertex2f(left + POPUP_WIDTH + POPUP_MARGIN * cos(a), top + POPUP_HEIGHT + POPUP_MARGIN * sin(a)); - } - for (double a = 0.5f * M_PI; a < 1.0f * M_PI; a += POPUP_STEP) { - glVertex2f(left + POPUP_MARGIN * cos(a) , top + POPUP_HEIGHT + POPUP_MARGIN * sin(a)); - } - glEnd(); - - glBegin(GL_QUADS); - glColor3f(color.redF(), - color.greenF(), - color.blueF()); - glVertex2f(left , top); - glVertex2f(left + SWATCH_WIDTH, top); - glVertex2f(left + SWATCH_WIDTH, top + SWATCH_HEIGHT); - glVertex2f(left , top + SWATCH_HEIGHT); - glEnd(); - - glColor3f(1.0f, 1.0f, 1.0f); - textRenderer.draw(left + SWATCH_WIDTH + POPUP_MARGIN, top + FIRST_LINE_OFFSET , line1); - textRenderer.draw(left + SWATCH_WIDTH + POPUP_MARGIN, top + SECOND_LINE_OFFSET, line2); - } - else { - _swatch.checkColor(); - } - if (_pieMenu.isDisplayed()) { _pieMenu.render(); } @@ -3484,19 +2908,6 @@ void Application::toggleStatsExpanded() { _statsExpanded = !_statsExpanded; } -void Application::renderThrustAtVoxel(const glm::vec3& thrust) { - if (_mousePressed) { - glColor3f(1, 0, 0); - glLineWidth(2.0f); - glBegin(GL_LINES); - glm::vec3 voxelTouched = getMouseVoxelWorldCoordinates(_mouseVoxelDragging); - glVertex3f(voxelTouched.x, voxelTouched.y, voxelTouched.z); - glVertex3f(voxelTouched.x + thrust.x, voxelTouched.y + thrust.y, voxelTouched.z + thrust.z); - glEnd(); - } -} - - glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) { float horizontalScale = _glWidget->width() / 2.0f; float verticalScale = _glWidget->height() / 2.0f; @@ -3871,47 +3282,6 @@ void Application::renderViewFrustum(ViewFrustum& viewFrustum) { } } -bool Application::maybeEditVoxelUnderCursor() { - if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode) - || Menu::getInstance()->isOptionChecked(MenuOption::VoxelColorMode)) { - if (_mouseVoxel.s != 0) { - makeVoxel(glm::vec3(_mouseVoxel.x * TREE_SCALE, - _mouseVoxel.y * TREE_SCALE, - _mouseVoxel.z * TREE_SCALE), - _mouseVoxel.s * TREE_SCALE, - _mouseVoxel.red, - _mouseVoxel.green, - _mouseVoxel.blue, - Menu::getInstance()->isOptionChecked(MenuOption::DestructiveAddVoxel)); - - // remember the position for drag detection - _justEditedVoxel = true; - - } - } else if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelDeleteMode)) { - deleteVoxelUnderCursor(); - VoxelFade fade(VoxelFade::FADE_OUT, 1.0f, 1.0f, 1.0f); - const float VOXEL_BOUNDS_ADJUST = 0.01f; - float slightlyBigger = _mouseVoxel.s * VOXEL_BOUNDS_ADJUST; - fade.voxelDetails.x = _mouseVoxel.x - slightlyBigger; - fade.voxelDetails.y = _mouseVoxel.y - slightlyBigger; - fade.voxelDetails.z = _mouseVoxel.z - slightlyBigger; - fade.voxelDetails.s = _mouseVoxel.s + slightlyBigger + slightlyBigger; - _voxelFades.push_back(fade); - - } else if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelGetColorMode)) { - eyedropperVoxelUnderCursor(); - } else { - return false; - } - - return true; -} - -void Application::deleteVoxelUnderCursor() { - deleteVoxelAt(_mouseVoxel); -} - void Application::deleteVoxels(const VoxelDetail& voxel) { deleteVoxelAt(voxel); } @@ -3923,28 +3293,10 @@ void Application::deleteVoxelAt(const VoxelDetail& voxel) { // delete it locally to see the effect immediately (and in case no voxel server is present) _voxels.deleteVoxelAt(voxel.x, voxel.y, voxel.z, voxel.s); - - } - // remember the position for drag detection - _justEditedVoxel = true; -} - - -void Application::eyedropperVoxelUnderCursor() { - VoxelTreeElement* selectedNode = _voxels.getVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); - if (selectedNode && selectedNode->isColored()) { - QColor selectedColor(selectedNode->getColor()[RED_INDEX], - selectedNode->getColor()[GREEN_INDEX], - selectedNode->getColor()[BLUE_INDEX]); - - if (selectedColor.isValid()) { - QAction* voxelPaintColorAction = Menu::getInstance()->getActionForOption(MenuOption::VoxelPaintColor); - voxelPaintColorAction->setData(selectedColor); - voxelPaintColorAction->setIcon(Swatch::createIcon(selectedColor)); - } } } + void Application::resetSensors() { _mouseX = _glWidget->width() / 2; _mouseY = _glWidget->height() / 2; @@ -4235,6 +3587,10 @@ void Application::removeScriptName(const QString& fileNameString) { _activeScripts.removeOne(fileNameString); } +void Application::cleanupScriptMenuItem(const QString& scriptMenuName) { + Menu::getInstance()->removeAction(Menu::getInstance()->getActiveScriptsMenu(), scriptMenuName); +} + void Application::loadScript(const QString& fileNameString) { _activeScripts.append(fileNameString); QByteArray fileNameAscii = fileNameString.toLocal8Bit(); @@ -4263,9 +3619,11 @@ void Application::loadScript(const QString& fileNameString) { // start the script on a new thread... bool wantMenuItems = true; // tells the ScriptEngine object to add menu items for itself - ScriptEngine* scriptEngine = new ScriptEngine(script, wantMenuItems, fileName, Menu::getInstance(), - &_controllerScriptingInterface); - scriptEngine->setupMenuItems(); + ScriptEngine* scriptEngine = new ScriptEngine(script, wantMenuItems, fileName, &_controllerScriptingInterface); + + // add a stop menu item + Menu::getInstance()->addActionToQMenuAndActionHash(Menu::getInstance()->getActiveScriptsMenu(), + scriptEngine->getScriptMenuName(), 0, scriptEngine, SLOT(stop())); // setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so // we can use the same ones from the application. @@ -4286,6 +3644,7 @@ void Application::loadScript(const QString& fileNameString) { connect(scriptEngine, SIGNAL(finished(const QString&)), clipboardScriptable, SLOT(deleteLater())); scriptEngine->registerGlobalObject("Overlays", &_overlays); + scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance()); QThread* workerThread = new QThread(this); @@ -4296,6 +3655,7 @@ void Application::loadScript(const QString& fileNameString) { connect(scriptEngine, SIGNAL(finished(const QString&)), scriptEngine, SLOT(deleteLater())); connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater())); connect(scriptEngine, SIGNAL(finished(const QString&)), this, SLOT(removeScriptName(const QString&))); + connect(scriptEngine, SIGNAL(cleanupMenuItem(const QString&)), this, SLOT(cleanupScriptMenuItem(const QString&))); // when the application is about to quit, stop our script engine so it unwinds properly connect(this, SIGNAL(aboutToQuit()), scriptEngine, SLOT(stop())); diff --git a/interface/src/Application.h b/interface/src/Application.h index e35c4371f8..846d952e5f 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -37,12 +37,11 @@ #include "DatagramProcessor.h" #include "Environment.h" #include "GLCanvas.h" +#include "Menu.h" #include "MetavoxelSystem.h" #include "PacketHeaders.h" #include "PieMenu.h" #include "Stars.h" -#include "Swatch.h" -#include "ToolsPalette.h" #include "ViewFrustum.h" #include "VoxelFade.h" #include "VoxelEditPacketSender.h" @@ -169,7 +168,6 @@ public: SixenseManager* getSixenseManager() { return &_sixenseManager; } BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; } QSettings* getSettings() { return _settings; } - Swatch* getSwatch() { return &_swatch; } QMainWindow* getWindow() { return _window; } NodeToVoxelSceneStats* getOcteeSceneStats() { return &_octreeServerSceneStats; } void lockVoxelSceneStats() { _voxelSceneStatsLock.lockForRead(); } @@ -236,14 +234,6 @@ public slots: void nodeAdded(SharedNodePointer node); void nodeKilled(SharedNodePointer node); void packetSent(quint64 length); - - void cutVoxels(); - void copyVoxels(); - void pasteVoxels(); - void deleteVoxels(); - void exportVoxels(); - void importVoxels(); - void nudgeVoxels(); void cutVoxels(const VoxelDetail& sourceVoxel); void copyVoxels(const VoxelDetail& sourceVoxel); @@ -254,8 +244,6 @@ public slots: void setRenderVoxels(bool renderVoxels); void doKillLocalVoxels(); - void decreaseVoxelSize(); - void increaseVoxelSize(); void loadDialog(); void toggleLogDialog(); void initAvatarAndViewFrustum(); @@ -272,8 +260,6 @@ private slots: void setEnable3DTVMode(bool enable3DTVMode); void cameraMenuChanged(); - void renderThrustAtVoxel(const glm::vec3& thrust); - void renderCoverageMap(); void renderCoverageMapsRecursively(CoverageMap* map); @@ -290,6 +276,7 @@ private slots: void parseVersionXml(); void removeScriptName(const QString& fileNameString); + void cleanupScriptMenuItem(const QString& scriptMenuName); private: void resetCamerasOnResizeGL(Camera& camera, int width, int height); @@ -310,7 +297,6 @@ private: void updateVisage(); void updateMyAvatarLookAtPosition(); void updateHoverVoxels(float deltaTime, float& distance, BoxFace& face); - void updateMouseVoxels(float deltaTime, float& distance, BoxFace& face); void updateHandAndTouch(float deltaTime); void updateLeap(float deltaTime); void updateSixense(float deltaTime); @@ -345,8 +331,6 @@ private: void checkBandwidthMeterClick(); - bool maybeEditVoxelUnderCursor(); - void deleteVoxelUnderCursor(); void deleteVoxelAt(const VoxelDetail& voxel); void eyedropperVoxelUnderCursor(); @@ -447,27 +431,14 @@ private: float _touchDragStartedAvgY; bool _isTouchPressed; // true if multitouch has been pressed (clear when finished) - VoxelDetail _mouseVoxelDragging; bool _mousePressed; // true if mouse has been pressed (clear when finished) VoxelDetail _hoverVoxel; // Stuff about the voxel I am hovering or clicking bool _isHoverVoxel; - VoxelDetail _mouseVoxel; // details of the voxel to be edited - float _mouseVoxelScale; // the scale for adding/removing voxels - bool _mouseVoxelScaleInitialized; - glm::vec3 _lastMouseVoxelPos; // the position of the last mouse voxel edit - bool _justEditedVoxel; // set when we've just added/deleted/colored a voxel - VoxelDetail _highlightVoxel; bool _isHighlightVoxel; - VoxelDetail _nudgeVoxel; // details of the voxel to be nudged - bool _nudgeStarted; - bool _lookingAlongX; - bool _lookingAwayFromOrigin; - glm::vec3 _nudgeGuidePosition; - ChatEntry _chatEntry; // chat entry field bool _chatEntryOn; // Whether to show the chat entry @@ -496,11 +467,6 @@ private: StDev _idleLoopStdev; float _idleLoopMeasuredJitter; - ToolsPalette _palette; - Swatch _swatch; - - bool _pasteMode; - PieMenu _pieMenu; int parseOctreeStats(const QByteArray& packet, const SharedNodePointer& sendingNode); diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index be7e7bac50..6cb44f5919 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -126,7 +126,7 @@ void Camera::setModeShiftPeriod (float period) { // if a zero period was requested, we clearly want to snap immediately to the target if (period == 0.0f) { - update(MIN_PERIOD); + update(MAX_PERIOD); } } diff --git a/interface/src/ClipboardScriptingInterface.cpp b/interface/src/ClipboardScriptingInterface.cpp index 644ea84cdb..c669b465ad 100644 --- a/interface/src/ClipboardScriptingInterface.cpp +++ b/interface/src/ClipboardScriptingInterface.cpp @@ -70,8 +70,8 @@ void ClipboardScriptingInterface::exportVoxel(float x, float y, float z, float s z / (float)TREE_SCALE, s / (float)TREE_SCALE }; - // TODO: should we be calling invokeMethod() in all these cases? - Application::getInstance()->exportVoxels(sourceVoxel); + QMetaObject::invokeMethod(Application::getInstance(), "exportVoxels", + Q_ARG(const VoxelDetail&, sourceVoxel)); } void ClipboardScriptingInterface::importVoxels() { diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index a0710cf463..89bebefcdd 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,7 @@ #include "Application.h" #include "Menu.h" +#include "MenuScriptingInterface.h" #include "Util.h" #include "InfoView.h" #include "ui/MetavoxelEditor.h" @@ -63,7 +65,6 @@ Menu::Menu() : _faceshiftEyeDeflection(DEFAULT_FACESHIFT_EYE_DEFLECTION), _frustumDrawMode(FRUSTUM_DRAW_MODE_ALL), _viewFrustumOffset(DEFAULT_FRUSTUM_OFFSET), - _voxelModeActionsGroup(NULL), _voxelStatsDialog(NULL), _lodToolsDialog(NULL), _maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM), @@ -94,7 +95,7 @@ Menu::Menu() : toggleLoginMenuItem(); // connect to the appropriate slots of the AccountManager so that we can change the Login/Logout menu item - connect(&accountManager, &AccountManager::loginComplete, this, &Menu::toggleLoginMenuItem); + connect(&accountManager, &AccountManager::accessTokenChanged, this, &Menu::toggleLoginMenuItem); connect(&accountManager, &AccountManager::logoutComplete, this, &Menu::toggleLoginMenuItem); addDisabledActionAndSeparator(fileMenu, "Scripts"); @@ -103,10 +104,6 @@ Menu::Menu() : addActionToQMenuAndActionHash(fileMenu, MenuOption::ReloadAllScripts, 0, appInstance, SLOT(reloadAllScripts())); _activeScriptsMenu = fileMenu->addMenu("Running Scripts"); - addDisabledActionAndSeparator(fileMenu, "Voxels"); - addActionToQMenuAndActionHash(fileMenu, MenuOption::ExportVoxels, Qt::CTRL | Qt::Key_E, appInstance, SLOT(exportVoxels())); - addActionToQMenuAndActionHash(fileMenu, MenuOption::ImportVoxels, Qt::CTRL | Qt::Key_I, appInstance, SLOT(importVoxels())); - addDisabledActionAndSeparator(fileMenu, "Go"); addActionToQMenuAndActionHash(fileMenu, MenuOption::GoHome, @@ -151,19 +148,6 @@ Menu::Menu() : SLOT(editPreferences()), QAction::PreferencesRole); - addDisabledActionAndSeparator(editMenu, "Voxels"); - - addActionToQMenuAndActionHash(editMenu, MenuOption::CutVoxels, Qt::CTRL | Qt::Key_X, appInstance, SLOT(cutVoxels())); - addActionToQMenuAndActionHash(editMenu, MenuOption::CopyVoxels, Qt::CTRL | Qt::Key_C, appInstance, SLOT(copyVoxels())); - addActionToQMenuAndActionHash(editMenu, MenuOption::PasteVoxels, Qt::CTRL | Qt::Key_V, appInstance, SLOT(pasteVoxels())); - addActionToQMenuAndActionHash(editMenu, MenuOption::NudgeVoxels, Qt::CTRL | Qt::Key_N, appInstance, SLOT(nudgeVoxels())); - - #ifdef __APPLE__ - addActionToQMenuAndActionHash(editMenu, MenuOption::DeleteVoxels, Qt::Key_Backspace, appInstance, SLOT(deleteVoxels())); - #else - addActionToQMenuAndActionHash(editMenu, MenuOption::DeleteVoxels, Qt::Key_Delete, appInstance, SLOT(deleteVoxels())); - #endif - addDisabledActionAndSeparator(editMenu, "Physics"); addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::Gravity, Qt::SHIFT | Qt::Key_G, false); @@ -171,57 +155,8 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ClickToFly); addAvatarCollisionSubMenu(editMenu); - + QMenu* toolsMenu = addMenu("Tools"); - - _voxelModeActionsGroup = new QActionGroup(this); - _voxelModeActionsGroup->setExclusive(false); - - QAction* addVoxelMode = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelAddMode, Qt::Key_V); - _voxelModeActionsGroup->addAction(addVoxelMode); - - QAction* deleteVoxelMode = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelDeleteMode, Qt::Key_R); - _voxelModeActionsGroup->addAction(deleteVoxelMode); - - QAction* colorVoxelMode = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelColorMode, Qt::Key_B); - _voxelModeActionsGroup->addAction(colorVoxelMode); - - QAction* selectVoxelMode = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelSelectMode, Qt::Key_O); - _voxelModeActionsGroup->addAction(selectVoxelMode); - - QAction* getColorMode = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelGetColorMode, Qt::Key_G); - _voxelModeActionsGroup->addAction(getColorMode); - - - // connect each of the voxel mode actions to the updateVoxelModeActionsSlot - foreach (QAction* action, _voxelModeActionsGroup->actions()) { - connect(action, SIGNAL(triggered()), this, SLOT(updateVoxelModeActions())); - } - - QAction* voxelPaintColor = addActionToQMenuAndActionHash(toolsMenu, - MenuOption::VoxelPaintColor, - Qt::META | Qt::Key_C, - this, - SLOT(chooseVoxelPaintColor())); - - Application::getInstance()->getSwatch()->setAction(voxelPaintColor); - - QColor paintColor(128, 128, 128); - voxelPaintColor->setData(paintColor); - voxelPaintColor->setIcon(Swatch::createIcon(paintColor)); - - addActionToQMenuAndActionHash(toolsMenu, - MenuOption::DecreaseVoxelSize, - QKeySequence::ZoomOut, - appInstance, - SLOT(decreaseVoxelSize())); - addActionToQMenuAndActionHash(toolsMenu, - MenuOption::IncreaseVoxelSize, - QKeySequence::ZoomIn, - appInstance, - SLOT(increaseVoxelSize())); - addActionToQMenuAndActionHash(toolsMenu, MenuOption::ResetSwatchColors, 0, this, SLOT(resetSwatchColors())); - addActionToQMenuAndActionHash(toolsMenu, MenuOption::MetavoxelEditor, 0, this, SLOT(showMetavoxelEditor())); @@ -496,7 +431,6 @@ Menu::Menu() : QAction* helpAction = helpMenu->addAction(MenuOption::AboutApp); connect(helpAction, SIGNAL(triggered()), this, SLOT(aboutApp())); #endif - } Menu::~Menu() { @@ -528,7 +462,6 @@ void Menu::loadSettings(QSettings* settings) { scanMenuBar(&loadAction, settings); Application::getInstance()->getAvatar()->loadData(settings); - Application::getInstance()->getSwatch()->loadData(settings); Application::getInstance()->updateWindowTitle(); NodeList::getInstance()->loadData(settings); @@ -560,7 +493,6 @@ void Menu::saveSettings(QSettings* settings) { scanMenuBar(&saveAction, settings); Application::getInstance()->getAvatar()->saveData(settings); - Application::getInstance()->getSwatch()->saveData(settings); NodeList::getInstance()->saveData(settings); } @@ -678,18 +610,34 @@ void Menu::addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& } QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu, - const QString actionName, + const QString& actionName, const QKeySequence& shortcut, const QObject* receiver, const char* member, - QAction::MenuRole role) { - QAction* action; + QAction::MenuRole role, + int menuItemLocation) { + QAction* action = NULL; + QAction* actionBefore = NULL; - if (receiver && member) { - action = destinationMenu->addAction(actionName, receiver, member, shortcut); + if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) { + actionBefore = destinationMenu->actions()[menuItemLocation]; + } + + if (!actionBefore) { + if (receiver && member) { + action = destinationMenu->addAction(actionName, receiver, member, shortcut); + } else { + action = destinationMenu->addAction(actionName); + action->setShortcut(shortcut); + } } else { - action = destinationMenu->addAction(actionName); + action = new QAction(actionName, destinationMenu); action->setShortcut(shortcut); + destinationMenu->insertAction(actionBefore, action); + + if (receiver && member) { + connect(action, SIGNAL(triggered()), receiver, member); + } } action->setMenuRole(role); @@ -699,12 +647,15 @@ QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu, } QAction* Menu::addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu, - const QString actionName, + const QString& actionName, const QKeySequence& shortcut, const bool checked, const QObject* receiver, - const char* member) { - QAction* action = addActionToQMenuAndActionHash(destinationMenu, actionName, shortcut, receiver, member); + const char* member, + int menuItemLocation) { + + QAction* action = addActionToQMenuAndActionHash(destinationMenu, actionName, shortcut, receiver, member, + QAction::NoRole, menuItemLocation); action->setCheckable(true); action->setChecked(checked); @@ -731,15 +682,6 @@ QAction* Menu::getActionForOption(const QString& menuOption) { return _actionHash.value(menuOption); } -bool Menu::isVoxelModeActionChecked() { - foreach (QAction* action, _voxelModeActionsGroup->actions()) { - if (action->isChecked()) { - return true; - } - } - return false; -} - void Menu::aboutApp() { InfoView::forcedShow(); } @@ -1148,7 +1090,6 @@ void Menu::showMetavoxelEditor() { _MetavoxelEditor = new MetavoxelEditor(); } _MetavoxelEditor->raise(); - _MetavoxelEditor->activateWindow(); } void Menu::audioMuteToggled() { @@ -1235,39 +1176,10 @@ void Menu::cycleFrustumRenderMode() { updateFrustumRenderModeAction(); } -void Menu::updateVoxelModeActions() { - // only the sender can be checked - foreach (QAction* action, _voxelModeActionsGroup->actions()) { - if (action->isChecked() && action != sender()) { - action->setChecked(false); - } - } -} - -void Menu::chooseVoxelPaintColor() { - Application* appInstance = Application::getInstance(); - QAction* paintColor = _actionHash.value(MenuOption::VoxelPaintColor); - - QColor selected = QColorDialog::getColor(paintColor->data().value(), - appInstance->getGLWidget(), - "Voxel Paint Color"); - if (selected.isValid()) { - paintColor->setData(selected); - paintColor->setIcon(Swatch::createIcon(selected)); - } - - // restore the main window's active state - appInstance->getWindow()->activateWindow(); -} - void Menu::runTests() { runTimingTests(); } -void Menu::resetSwatchColors() { - Application::getInstance()->getSwatch()->reset(); -} - void Menu::updateFrustumRenderModeAction() { QAction* frustumRenderModeAction = _actionHash.value(MenuOption::FrustumRenderMode); switch (_frustumDrawMode) { @@ -1319,3 +1231,174 @@ QString Menu::replaceLastOccurrence(QChar search, QChar replace, QString string) return string; } + +QAction* Menu::getActionFromName(const QString& menuName, QMenu* menu) { + QList menuActions; + if (menu) { + menuActions = menu->actions(); + } else { + menuActions = actions(); + } + + foreach (QAction* menuAction, menuActions) { + if (menuName == menuAction->text()) { + return menuAction; + } + } + return NULL; +} + +QMenu* Menu::getSubMenuFromName(const QString& menuName, QMenu* menu) { + QAction* action = getActionFromName(menuName, menu); + if (action) { + return action->menu(); + } + return NULL; +} + +QMenu* Menu::getMenuParent(const QString& menuName, QString& finalMenuPart) { + QStringList menuTree = menuName.split(">"); + QMenu* parent = NULL; + QMenu* menu = NULL; + foreach (QString menuTreePart, menuTree) { + parent = menu; + finalMenuPart = menuTreePart.trimmed(); + menu = getSubMenuFromName(finalMenuPart, parent); + if (!menu) { + break; + } + } + return parent; +} + +QMenu* Menu::getMenu(const QString& menuName) { + QStringList menuTree = menuName.split(">"); + QMenu* parent = NULL; + QMenu* menu = NULL; + int item = 0; + foreach (QString menuTreePart, menuTree) { + menu = getSubMenuFromName(menuTreePart.trimmed(), parent); + if (!menu) { + break; + } + parent = menu; + item++; + } + return menu; +} + +QAction* Menu::getMenuAction(const QString& menuName) { + QStringList menuTree = menuName.split(">"); + QMenu* parent = NULL; + QAction* action = NULL; + foreach (QString menuTreePart, menuTree) { + action = getActionFromName(menuTreePart.trimmed(), parent); + if (!action) { + break; + } + parent = action->menu(); + } + return action; +} + +int Menu::findPositionOfMenuItem(QMenu* menu, const QString& searchMenuItem) { + int position = 0; + foreach(QAction* action, menu->actions()) { + if (action->text() == searchMenuItem) { + return position; + } + position++; + } + return UNSPECIFIED_POSITION; // not found +} + +QMenu* Menu::addMenu(const QString& menuName) { + QStringList menuTree = menuName.split(">"); + QMenu* addTo = NULL; + QMenu* menu = NULL; + foreach (QString menuTreePart, menuTree) { + menu = getSubMenuFromName(menuTreePart.trimmed(), addTo); + if (!menu) { + if (!addTo) { + menu = QMenuBar::addMenu(menuTreePart.trimmed()); + } else { + menu = addTo->addMenu(menuTreePart.trimmed()); + } + } + addTo = menu; + } + + QMenuBar::repaint(); + return menu; +} + +void Menu::removeMenu(const QString& menuName) { + QAction* action = getMenuAction(menuName); + + // only proceed if the menu actually exists + if (action) { + QString finalMenuPart; + QMenu* parent = getMenuParent(menuName, finalMenuPart); + + if (parent) { + removeAction(parent, finalMenuPart); + } else { + QMenuBar::removeAction(action); + } + + QMenuBar::repaint(); + } +} + +void Menu::addSeparator(const QString& menuName, const QString& separatorName) { + QMenu* menuObj = getMenu(menuName); + if (menuObj) { + addDisabledActionAndSeparator(menuObj, separatorName); + } +} + +void Menu::addMenuItem(const MenuItemProperties& properties) { + QMenu* menuObj = getMenu(properties.menuName); + if (menuObj) { + QShortcut* shortcut = NULL; + if (!properties.shortcutKeySequence.isEmpty()) { + shortcut = new QShortcut(properties.shortcutKeySequence, this); + } + + // check for positioning requests + int requestedPosition = properties.position; + if (requestedPosition == UNSPECIFIED_POSITION && !properties.beforeItem.isEmpty()) { + requestedPosition = findPositionOfMenuItem(menuObj, properties.beforeItem); + } + if (requestedPosition == UNSPECIFIED_POSITION && !properties.afterItem.isEmpty()) { + int afterPosition = findPositionOfMenuItem(menuObj, properties.afterItem); + if (afterPosition != UNSPECIFIED_POSITION) { + requestedPosition = afterPosition + 1; + } + } + + QAction* menuItemAction; + if (properties.isCheckable) { + menuItemAction = addCheckableActionToQMenuAndActionHash(menuObj, properties.menuItemName, + properties.shortcutKeySequence, properties.isChecked, + MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()), requestedPosition); + } else { + menuItemAction = addActionToQMenuAndActionHash(menuObj, properties.menuItemName, properties.shortcutKeySequence, + MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()), + QAction::NoRole, requestedPosition); + } + if (shortcut) { + connect(shortcut, SIGNAL(activated()), menuItemAction, SLOT(trigger())); + } + QMenuBar::repaint(); + } +} + +void Menu::removeMenuItem(const QString& menu, const QString& menuitem) { + QMenu* menuObj = getMenu(menu); + if (menuObj) { + removeAction(menuObj, menuitem); + } + QMenuBar::repaint(); +}; + diff --git a/interface/src/Menu.h b/interface/src/Menu.h index c5c6ba7e7a..041f1fcd50 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -14,7 +14,9 @@ #include #include -#include +#include +#include +#include const float ADJUST_LOD_DOWN_FPS = 40.0; const float ADJUST_LOD_UP_FPS = 55.0; @@ -52,8 +54,9 @@ class BandwidthDialog; class LodToolsDialog; class MetavoxelEditor; class VoxelStatsDialog; +class MenuItemProperties; -class Menu : public QMenuBar, public AbstractMenuInterface { +class Menu : public QMenuBar { Q_OBJECT public: static Menu* getInstance(); @@ -63,7 +66,6 @@ public: void setIsOptionChecked(const QString& menuOption, bool isChecked); void triggerOption(const QString& menuOption); QAction* getActionForOption(const QString& menuOption); - bool isVoxelModeActionChecked(); float getAudioJitterBufferSamples() const { return _audioJitterBufferSamples; } float getFieldOfView() const { return _fieldOfView; } @@ -88,19 +90,24 @@ public: // User Tweakable PPS from Voxel Server int getMaxVoxelPacketsPerSecond() const { return _maxVoxelPacketsPerSecond; } - virtual QMenu* getActiveScriptsMenu() { return _activeScriptsMenu;} - virtual QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu, - const QString actionName, + QMenu* getActiveScriptsMenu() { return _activeScriptsMenu;} + + QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu, + const QString& actionName, const QKeySequence& shortcut = 0, const QObject* receiver = NULL, const char* member = NULL, - QAction::MenuRole role = QAction::NoRole); - virtual void removeAction(QMenu* menu, const QString& actionName); + QAction::MenuRole role = QAction::NoRole, + int menuItemLocation = UNSPECIFIED_POSITION); + + void removeAction(QMenu* menu, const QString& actionName); + bool goToDestination(QString destination); void goToOrientation(QString orientation); void goToDomain(const QString newDomain); public slots: + void loginForCurrentDomain(); void bandwidthDetails(); void voxelStatsDetails(); @@ -114,6 +121,12 @@ public slots: void toggleLoginMenuItem(); + QMenu* addMenu(const QString& menuName); + void removeMenu(const QString& menuName); + void addSeparator(const QString& menuName, const QString& separatorName); + void addMenuItem(const MenuItemProperties& properties); + void removeMenuItem(const QString& menuName, const QString& menuitem); + private slots: void aboutApp(); void editPreferences(); @@ -123,10 +136,7 @@ private slots: void voxelStatsDetailsClosed(); void lodToolsClosed(); void cycleFrustumRenderMode(); - void updateVoxelModeActions(); - void chooseVoxelPaintColor(); void runTests(); - void resetSwatchColors(); void showMetavoxelEditor(); void audioMuteToggled(); @@ -145,16 +155,26 @@ private: void addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& actionName); QAction* addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu, - const QString actionName, + const QString& actionName, const QKeySequence& shortcut = 0, const bool checked = false, const QObject* receiver = NULL, - const char* member = NULL); + const char* member = NULL, + int menuItemLocation = UNSPECIFIED_POSITION); void updateFrustumRenderModeAction(); void addAvatarCollisionSubMenu(QMenu* overMenu); + QAction* getActionFromName(const QString& menuName, QMenu* menu); + QMenu* getSubMenuFromName(const QString& menuName, QMenu* menu); + QMenu* getMenuParent(const QString& menuName, QString& finalMenuPart); + + QAction* getMenuAction(const QString& menuName); + int findPositionOfMenuItem(QMenu* menu, const QString& searchMenuItem); + QMenu* getMenu(const QString& menuName); + + QHash _actionHash; int _audioJitterBufferSamples; /// number of extra samples to wait before starting audio playback BandwidthDialog* _bandwidthDialog; @@ -162,7 +182,6 @@ private: float _faceshiftEyeDeflection; FrustumDrawMode _frustumDrawMode; ViewFrustumOffset _viewFrustumOffset; - QActionGroup* _voxelModeActionsGroup; QPointer _MetavoxelEditor; VoxelStatsDialog* _voxelStatsDialog; LodToolsDialog* _lodToolsDialog; @@ -193,14 +212,11 @@ namespace MenuOption { const QString CollideWithParticles = "Collide With Particles"; const QString CollideWithVoxels = "Collide With Voxels"; const QString CollideWithEnvironment = "Collide With World Boundaries"; - const QString CopyVoxels = "Copy"; const QString CoverageMap = "Render Coverage Map"; const QString CoverageMapV2 = "Render Coverage Map V2"; const QString CullSharedFaces = "Cull Shared Voxel Faces"; - const QString CutVoxels = "Cut"; const QString DecreaseAvatarSize = "Decrease Avatar Size"; const QString DecreaseVoxelSize = "Decrease Voxel Size"; - const QString DeleteVoxels = "Delete"; const QString DestructiveAddVoxel = "Create Voxel is Destructive"; const QString DisableColorVoxels = "Disable Colored Voxels"; const QString DisableDeltaSending = "Disable Delta Sending"; @@ -218,7 +234,6 @@ namespace MenuOption { const QString EchoServerAudio = "Echo Server Audio"; const QString EchoLocalAudio = "Echo Local Audio"; const QString MuteAudio = "Mute Microphone"; - const QString ExportVoxels = "Export Voxels"; const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes"; const QString HeadMouse = "Head Mouse"; const QString HandsCollideWithSelf = "Collide With Self"; @@ -239,8 +254,6 @@ namespace MenuOption { const QString GoToDomain = "Go To Domain..."; const QString GoToLocation = "Go To Location..."; const QString GoTo = "Go To..."; - const QString ImportVoxels = "Import Voxels"; - const QString ImportVoxelsClipboard = "Import Voxels to Clipboard"; const QString IncreaseAvatarSize = "Increase Avatar Size"; const QString IncreaseVoxelSize = "Increase Voxel Size"; const QString KillLocalVoxels = "Kill Local Voxels"; @@ -256,7 +269,6 @@ namespace MenuOption { const QString Mirror = "Mirror"; const QString MoveWithLean = "Move with Lean"; const QString NewVoxelCullingMode = "New Voxel Culling Mode"; - const QString NudgeVoxels = "Nudge"; const QString OffAxisProjection = "Off-Axis Projection"; const QString OldVoxelCullingMode = "Old Voxel Culling Mode"; const QString TurnWithHead = "Turn using Head"; @@ -265,7 +277,6 @@ namespace MenuOption { const QString Oscilloscope = "Audio Oscilloscope"; const QString Pair = "Pair"; const QString Particles = "Particles"; - const QString PasteVoxels = "Paste"; const QString PasteToVoxel = "Paste to Voxel..."; const QString PipelineWarnings = "Show Render Pipeline Warnings"; const QString PlaySlaps = "Play Slaps"; @@ -275,7 +286,6 @@ namespace MenuOption { const QString RenderSkeletonCollisionProxies = "Skeleton Collision Proxies"; const QString RenderHeadCollisionProxies = "Head Collision Proxies"; const QString ResetAvatarSize = "Reset Avatar Size"; - const QString ResetSwatchColors = "Reset Swatch Colors"; const QString RunTimingTests = "Run Timing Tests"; const QString SettingsImport = "Import Settings"; const QString Shadows = "Shadows"; @@ -294,14 +304,8 @@ namespace MenuOption { const QString UseVoxelShader = "Use Voxel Shader"; const QString VoxelsAsPoints = "Draw Voxels as Points"; const QString Voxels = "Voxels"; - const QString VoxelAddMode = "Add Voxel Mode"; - const QString VoxelColorMode = "Color Voxel Mode"; - const QString VoxelDeleteMode = "Delete Voxel Mode"; const QString VoxelDrumming = "Voxel Drumming"; - const QString VoxelGetColorMode = "Get Color Mode"; const QString VoxelMode = "Cycle Voxel Mode"; - const QString VoxelPaintColor = "Voxel Paint Color"; - const QString VoxelSelectMode = "Select Voxel Mode"; const QString VoxelStats = "Voxel Stats"; const QString VoxelTextures = "Voxel Textures"; } diff --git a/interface/src/MenuScriptingInterface.cpp b/interface/src/MenuScriptingInterface.cpp new file mode 100644 index 0000000000..f1a44422b3 --- /dev/null +++ b/interface/src/MenuScriptingInterface.cpp @@ -0,0 +1,67 @@ +// +// MenuScriptingInterface.cpp +// hifi +// +// Created by Brad Hefta-Gaub on 2/25/14 +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#include "Application.h" + +#include "MenuScriptingInterface.h" + + +MenuScriptingInterface* MenuScriptingInterface::getInstance() { + static MenuScriptingInterface sharedInstance; + return &sharedInstance; +} + +void MenuScriptingInterface::menuItemTriggered() { + QAction* menuItemAction = dynamic_cast(sender()); + if (menuItemAction) { + // emit the event + emit menuItemEvent(menuItemAction->text()); + } +} + +void MenuScriptingInterface::addMenu(const QString& menu) { + QMetaObject::invokeMethod(Menu::getInstance(), "addMenu", Q_ARG(const QString&, menu)); +} + +void MenuScriptingInterface::removeMenu(const QString& menu) { + QMetaObject::invokeMethod(Menu::getInstance(), "removeMenu", Q_ARG(const QString&, menu)); +} + +void MenuScriptingInterface::addSeparator(const QString& menuName, const QString& separatorName) { + QMetaObject::invokeMethod(Menu::getInstance(), "addSeparator", + Q_ARG(const QString&, menuName), + Q_ARG(const QString&, separatorName)); +} + +void MenuScriptingInterface::addMenuItem(const MenuItemProperties& properties) { + QMetaObject::invokeMethod(Menu::getInstance(), "addMenuItem", Q_ARG(const MenuItemProperties&, properties)); +} + +void MenuScriptingInterface::addMenuItem(const QString& menu, const QString& menuitem, const QString& shortcutKey) { + MenuItemProperties properties(menu, menuitem, shortcutKey); + QMetaObject::invokeMethod(Menu::getInstance(), "addMenuItem", Q_ARG(const MenuItemProperties&, properties)); +} + +void MenuScriptingInterface::addMenuItem(const QString& menu, const QString& menuitem) { + MenuItemProperties properties(menu, menuitem); + QMetaObject::invokeMethod(Menu::getInstance(), "addMenuItem", Q_ARG(const MenuItemProperties&, properties)); +} + +void MenuScriptingInterface::removeMenuItem(const QString& menu, const QString& menuitem) { + QMetaObject::invokeMethod(Menu::getInstance(), "removeMenuItem", + Q_ARG(const QString&, menu), + Q_ARG(const QString&, menuitem)); +}; + +bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) { + return Menu::getInstance()->isOptionChecked(menuOption); +} + +void MenuScriptingInterface::setIsOptionChecked(const QString& menuOption, bool isChecked) { + return Menu::getInstance()->setIsOptionChecked(menuOption, isChecked); +} diff --git a/interface/src/MenuScriptingInterface.h b/interface/src/MenuScriptingInterface.h new file mode 100644 index 0000000000..7379f2c00b --- /dev/null +++ b/interface/src/MenuScriptingInterface.h @@ -0,0 +1,49 @@ +// +// MenuScriptingInterface.h +// hifi +// +// Created by Brad Hefta-Gaub on 2/25/14 +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#ifndef __hifi__MenuScriptingInterface__ +#define __hifi__MenuScriptingInterface__ + +#include +#include +#include +#include + +#include "Menu.h" +#include + +class MenuScriptingInterface : public QObject { + Q_OBJECT + MenuScriptingInterface() { }; +public: + static MenuScriptingInterface* getInstance(); + +private slots: + friend class Menu; + void menuItemTriggered(); + +public slots: + void addMenu(const QString& menuName); + void removeMenu(const QString& menuName); + + void addSeparator(const QString& menuName, const QString& separatorName); + + void addMenuItem(const MenuItemProperties& properties); + void addMenuItem(const QString& menuName, const QString& menuitem, const QString& shortcutKey); + void addMenuItem(const QString& menuName, const QString& menuitem); + + void removeMenuItem(const QString& menuName, const QString& menuitem); + + bool isOptionChecked(const QString& menuOption); + void setIsOptionChecked(const QString& menuOption, bool isChecked); + +signals: + void menuItemEvent(const QString& menuItem); +}; + +#endif /* defined(__hifi__MenuScriptingInterface__) */ diff --git a/interface/src/Swatch.cpp b/interface/src/Swatch.cpp deleted file mode 100644 index d3d2a2341b..0000000000 --- a/interface/src/Swatch.cpp +++ /dev/null @@ -1,183 +0,0 @@ -// -// Swatch.h -// interface -// -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#include "Swatch.h" -#include - -QIcon Swatch::createIcon(const QColor& color) { - QPixmap map(16, 16); - map.fill(color); - return QIcon(map); -} - - -Swatch::Swatch(QAction* action) : - Tool(action, 0, -1, -1), - _textRenderer(MONO_FONT_FAMILY, 10, 100), - _selected(1) { -} - -void Swatch::reset() { - for (int i = 0; i < 8; ++i) { - _colors[i].setRgb(colorBase[i][0], - colorBase[i][1], - colorBase[i][2]); - } -} - -QColor Swatch::getColor() { - return _colors[_selected - 1]; -} - -void Swatch::checkColor() { - if (_action->data().value() == _colors[_selected - 1]) { - return; - } - - QPixmap map(16, 16); - map.fill(_colors[_selected - 1]); - _action->setData(_colors[_selected - 1]) ; - _action->setIcon(map); -} - -void Swatch::saveData(QSettings* settings) { - settings->beginGroup("Swatch"); - - for (int i(0); i < SWATCH_SIZE; ++i) { - QString rx("R1"), gx("G1"), bx("B1"); - rx[1] = '1' + i; - gx[1] = rx[1]; - bx[1] = rx[1]; - settings->setValue(rx, _colors[i].red()); - settings->setValue(gx, _colors[i].green()); - settings->setValue(bx, _colors[i].blue()); - } - - settings->endGroup(); -} - -void Swatch::loadData(QSettings* settings) { - settings->beginGroup("Swatch"); - - for (int i = 0; i < SWATCH_SIZE; ++i) { - QString rx("R1"), gx("G1"), bx("B1"); - rx[1] = '1' + i; - gx[1] = rx[1]; - bx[1] = rx[1]; - _colors[i].setRgb(settings->value(rx, colorBase[i][0]).toInt(), - settings->value(gx, colorBase[i][1]).toInt(), - settings->value(bx, colorBase[i][2]).toInt()); - } - - settings->endGroup(); - - checkColor(); -} - -void Swatch::handleEvent(int key, bool getColor) { - int next(0); - - switch (key) { - case Qt::Key_1: - next = 1; - break; - case Qt::Key_2: - next = 2; - break; - case Qt::Key_3: - next = 3; - break; - case Qt::Key_4: - next = 4; - break; - case Qt::Key_5: - next = 5; - break; - case Qt::Key_6: - next = 6; - break; - case Qt::Key_7: - next = 7; - break; - case Qt::Key_8: - next = 8; - break; - default: - break; - } - - if (getColor) { - if (_action->data().value() != _colors[_selected - 1]) { - _selected = next; - _colors[_selected - 1] = _action->data().value(); - } - } else { - _selected = next; - QPixmap map(16, 16); - map.fill(_colors[_selected - 1]); - _action->setData(_colors[_selected - 1]) ; - _action->setIcon(map); - } -} - -void Swatch::render(int width, int height) { - char str[2]; - int margin = 0.10f * height; - height = 0.75f * height; - - glBegin(GL_QUADS); - glColor3f(0.0f, 0.0f, 0.0f); - glVertex2f(0, 8 * (height - margin) + margin); - glVertex2f(width, 8 * (height - margin) + margin); - glVertex2f(width, 0); - glVertex2f(0, 0); - glEnd(); - - for (unsigned int i = 0; i < SWATCH_SIZE; ++i) { - glBegin(GL_QUADS); - glColor3f(_colors[i].redF(), - _colors[i].greenF(), - _colors[i].blueF()); - glVertex2f(margin , (i + 1) * (height - margin)); - glVertex2f(width - margin, (i + 1) * (height - margin)); - glVertex2f(width - margin, i * (height - margin) + margin); - glVertex2f(margin , i * (height - margin) + margin); - glEnd(); - - if (_colors[i].lightness() < 50) { - glBegin(GL_LINES); - glColor3f(1.0f, 1.0f, 1.0f); - glVertex2f(margin , (i + 1) * (height - margin)); - glVertex2f(width - margin, (i + 1) * (height - margin)); - - glVertex2f(width - margin, (i + 1) * (height - margin)); - glVertex2f(width - margin, i * (height - margin) + margin); - - glVertex2f(width - margin, i * (height - margin) + margin); - glVertex2f(margin , i * (height - margin) + margin); - - glVertex2f(margin , i * (height - margin) + margin); - glVertex2f(margin , (i + 1) * (height - margin)); - glEnd(); - } else { - glColor3f(0.0f, 0.0f, 0.0f); - } - - if (_selected == i + 1) { - glBegin(GL_TRIANGLES); - glVertex2f(margin , (i + 1) * (height - margin) - margin); - glVertex2f(width/4 - margin, i * (height - margin) + height / 2.0f); - glVertex2f(margin , i * (height - margin) + margin + margin); - glEnd(); - } - - sprintf(str, "%d", i + 1); - _textRenderer.draw(3 * width/4, (i + 1) * (height - margin) - 0.2f * height, str); - } - - glTranslated(0, 8 * (height - margin) + margin + 0.075f * height, 0); -} diff --git a/interface/src/Swatch.h b/interface/src/Swatch.h deleted file mode 100644 index e1245fb4f3..0000000000 --- a/interface/src/Swatch.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// Swatch.h -// interface -// -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#ifndef __interface__Swatch__ -#define __interface__Swatch__ - -#include "Tool.h" -#include "ui/TextRenderer.h" - -static const int SWATCH_SIZE = 8; -static const int colorBase[8][3] = {{237, 175, 0}, - {61, 211, 72}, - {51, 204, 204}, - {63, 169, 245}, - {193, 99, 122}, - {255, 54, 69}, - {124, 36, 36}, - {63, 35, 19}}; - -class Swatch : public Tool { -public: - static QIcon createIcon(const QColor& color); - - Swatch(QAction* action); - - QColor getColor(); - void checkColor(); - void saveData(QSettings* settings); - void loadData(QSettings* settings); - void reset(); - - void render(int width, int height); - void handleEvent(int key, bool getColor); - -private: - TextRenderer _textRenderer; - QColor _colors[SWATCH_SIZE]; - int _selected; -}; - -#endif /* defined(__interface__Swatch__) */ diff --git a/interface/src/Tool.cpp b/interface/src/Tool.cpp deleted file mode 100644 index ccb42381dd..0000000000 --- a/interface/src/Tool.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "Tool.h" - -#include -#include -#include - -Tool::Tool(QAction *action, GLuint texture, int x, int y) : - _action(action), - _texture(texture), - _x(x), - _y(y) { -} - -void Tool::setAction(QAction* action) { - _action = action; -} - -bool Tool::isActive() { - return _action->isChecked(); -} - -void Tool::render(int width, int height) { - glEnable(GL_TEXTURE_2D); - - glBindTexture(GL_TEXTURE_2D, _texture); - - - if (_action == 0 || _action->isChecked()) { - glColor3f(1.0f, 1.0f, 1.0f); // reset gl color - } else { - glColor3f(0.3f, 0.3f, 0.3f); - } - - glBegin(GL_QUADS); - glTexCoord2f( _x / NUM_TOOLS_COLS, 1.0f - (_y + 1) / NUM_TOOLS_ROWS); - glVertex2f(0 , height); - - glTexCoord2f((_x + 1) / NUM_TOOLS_COLS, 1.0f - (_y + 1) / NUM_TOOLS_ROWS); - glVertex2f(width, height); - - glTexCoord2f((_x + 1) / NUM_TOOLS_COLS, 1.0f - _y / NUM_TOOLS_ROWS); - glVertex2f(width, 0); - - glTexCoord2f( _x / NUM_TOOLS_COLS, 1.0f - _y / NUM_TOOLS_ROWS); - glVertex2f(0 , 0); - glEnd(); - - glDisable(GL_TEXTURE_2D); - - glTranslated(0, 1.10f * height, 0); -} diff --git a/interface/src/Tool.h b/interface/src/Tool.h deleted file mode 100644 index f0e1bae4b0..0000000000 --- a/interface/src/Tool.h +++ /dev/null @@ -1,55 +0,0 @@ -// -// Tool.h -// interface -// -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#ifndef __interface__Tool__ -#define __interface__Tool__ - -#include - -#include "InterfaceConfig.h" -#include "Util.h" - -class QAction; - - -// Number of rows and columns in the SVG file for the tool palette -static const int NUM_TOOLS_ROWS = 10; -static const int NUM_TOOLS_COLS = 2; -static const int SWATCHS_TOOLS_COUNT = 13; // 8 swatch + 5 tools - -static const int WIDTH_MIN = 47; // Minimal tools width -static const float TOOLS_RATIO = 40.0f / 60.0f; // ratio height/width of tools icons -static const float PAL_SCREEN_RATIO = 3.0f / 100.0f; // Percentage of the screeen width the palette is going to occupy - -// Swatch popup consts -static const float POPUP_STEP = 0.05f; -static const float POPUP_MARGIN = 10.0f; -static const int POPUP_WIDTH = 280; -static const int POPUP_HEIGHT = 30; -static const int SWATCH_WIDTH = 64; -static const int SWATCH_HEIGHT = 30; -static const int FIRST_LINE_OFFSET = 12; -static const int SECOND_LINE_OFFSET = 28; - -class Tool { -public: - Tool(QAction* action, GLuint texture, int x, int y); - - void setAction(QAction* action); - bool isActive(); - virtual void render(int width, int height); - -protected: - QAction* _action; - GLuint _texture; - - // position in the SVG grid - double _x; - double _y; -}; - -#endif /* defined(__interface__Tool__) */ diff --git a/interface/src/ToolsPalette.cpp b/interface/src/ToolsPalette.cpp deleted file mode 100644 index 58ec12dc92..0000000000 --- a/interface/src/ToolsPalette.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "ToolsPalette.h" - -#include -#include -#include -#include - -void ToolsPalette::init(int screenWidth, int screenHeight) { - _width = (PAL_SCREEN_RATIO * screenWidth < WIDTH_MIN) ? WIDTH_MIN : PAL_SCREEN_RATIO * screenWidth; - _height = TOOLS_RATIO * _width; - - _left = screenWidth / 150; - _top = (screenHeight - SWATCHS_TOOLS_COUNT * _height) / 2; - - // Load SVG - switchToResourcesParentIfRequired(); - QSvgRenderer renderer(QString("./resources/images/hifi-interface-tools.svg")); - - // Prepare a QImage with desired characteritisc - QImage image(NUM_TOOLS_COLS * _width, NUM_TOOLS_ROWS * _height, QImage::Format_ARGB32); - - // Get QPainter that paints to the image - QPainter painter(&image); - renderer.render(&painter); - - //get the OpenGL-friendly image - _textureImage = QGLWidget::convertToGLFormat(image); - - glGenTextures(1, &_textureID); - glBindTexture(GL_TEXTURE_2D, _textureID); - - //generate the texture - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, - _textureImage.width(), - _textureImage.height(), - 0, GL_RGBA, GL_UNSIGNED_BYTE, - _textureImage.bits()); - - //texture parameters - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); -} - - -void ToolsPalette::addAction(QAction* action, int x, int y) { - Tool* tmp = new Tool(action, _textureID, x, y); - _tools.push_back(tmp); -} - -void ToolsPalette::addTool(Tool* tool) { - _tools.push_back(tool); -} - -void ToolsPalette::render(int screenWidth, int screenHeight) { - _width = (PAL_SCREEN_RATIO * screenWidth < WIDTH_MIN) ? WIDTH_MIN : PAL_SCREEN_RATIO * screenWidth; - _height = TOOLS_RATIO * _width; - - _left = screenWidth / 150; - _top = (screenHeight - SWATCHS_TOOLS_COUNT * _height) / 2; - - glPushMatrix(); - glTranslated(_left, _top, 0); - - bool show = false; - for (unsigned int i = 0; i < _tools.size(); ++i) { - if (_tools[i]->isActive()) { - show = true; - break; - } - } - - if (show) { - for (unsigned int i = 0; i < _tools.size(); ++i) { - _tools[i]->render(_width, _height); - } - } - - glPopMatrix(); -} - -bool ToolsPalette::isActive() { - for (unsigned int i = 0; i < _tools.size(); ++i) { - if (_tools[i]->isActive()) { - return true; - } - } - return false; -} diff --git a/interface/src/ToolsPalette.h b/interface/src/ToolsPalette.h deleted file mode 100644 index b6d446f51d..0000000000 --- a/interface/src/ToolsPalette.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// ToolsPalette.h -// interface -// -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// - -#ifndef __interface__ToolsPalette__ -#define __interface__ToolsPalette__ - -#include "Tool.h" -#include "Swatch.h" - -#include - -class ToolsPalette { -public: - void init(int screenWidth, int screenHeight); - void addAction(QAction* action, int x, int y); - void addTool(Tool* tool); - void render(int screenWidth, int screenHeight); - - bool isActive(); - -private: - QImage _textureImage; - GLuint _textureID; - std::vector _tools; - - int _top; - int _left; - - int _width; - int _height; -}; - -#endif /* defined(__interface__ToolsPalette__) */ diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 183ef116e6..f601618eae 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -419,97 +419,6 @@ void renderCollisionOverlay(int width, int height, float magnitude) { } } -void renderMouseVoxelGrid(const float& mouseVoxelX, const float& mouseVoxelY, const float& mouseVoxelZ, const float& mouseVoxelS) { - glm::vec3 origin = glm::vec3(mouseVoxelX, mouseVoxelY, mouseVoxelZ); - - glLineWidth(3.0); - - const int HALF_GRID_DIMENSIONS = 4; - glBegin(GL_LINES); - - glm::vec3 xColor(0.0, 0.6, 0.0); - glColor3fv(&xColor.x); - - glVertex3f(origin.x + HALF_GRID_DIMENSIONS * mouseVoxelS, 0, origin.z); - glVertex3f(origin.x - HALF_GRID_DIMENSIONS * mouseVoxelS, 0, origin.z); - - glm::vec3 zColor(0.0, 0.0, 0.6); - glColor3fv(&zColor.x); - - glVertex3f(origin.x, 0, origin.z + HALF_GRID_DIMENSIONS * mouseVoxelS); - glVertex3f(origin.x, 0, origin.z - HALF_GRID_DIMENSIONS * mouseVoxelS); - - glm::vec3 yColor(0.6, 0.0, 0.0); - glColor3fv(&yColor.x); - - glVertex3f(origin.x, 0, origin.z); - glVertex3f(origin.x, origin.y, origin.z); - glEnd(); -} - -void renderNudgeGrid(float voxelX, float voxelY, float voxelZ, float voxelS, float voxelPrecision) { - glm::vec3 origin = glm::vec3(voxelX, voxelY, voxelZ); - - glLineWidth(1.0); - - const int GRID_DIMENSIONS = 4; - const int GRID_SCALER = voxelS / voxelPrecision; - const int GRID_SEGMENTS = GRID_DIMENSIONS * GRID_SCALER; - glBegin(GL_LINES); - - for (int xz = - (GRID_SEGMENTS / 2); xz <= GRID_SEGMENTS / 2 + GRID_SCALER; xz++) { - glm::vec3 xColor(0.0, 0.6, 0.0); - glColor3fv(&xColor.x); - - glVertex3f(origin.x + GRID_DIMENSIONS * voxelS, 0, origin.z + xz * voxelPrecision); - glVertex3f(origin.x - (GRID_DIMENSIONS - 1) * voxelS, 0, origin.z + xz * voxelPrecision); - - glm::vec3 zColor(0.0, 0.0, 0.6); - glColor3fv(&zColor.x); - - glVertex3f(origin.x + xz * voxelPrecision, 0, origin.z + GRID_DIMENSIONS * voxelS); - glVertex3f(origin.x + xz * voxelPrecision, 0, origin.z - (GRID_DIMENSIONS - 1) * voxelS); - } - glEnd(); - - glColor3f(1.0f,1.0f,1.0f); - - glBegin(GL_POLYGON);//begin drawing of square - glVertex3f(voxelX, 0.0f, voxelZ);//first vertex - glVertex3f(voxelX + voxelS, 0.0f, voxelZ);//second vertex - glVertex3f(voxelX + voxelS, 0.0f, voxelZ + voxelS);//third vertex - glVertex3f(voxelX, 0.0f, voxelZ + voxelS);//fourth vertex - glEnd();//end drawing of polygon -} - -void renderNudgeGuide(float voxelX, float voxelY, float voxelZ, float voxelS) { - glm::vec3 origin = glm::vec3(voxelX, voxelY, voxelZ); - - glLineWidth(3.0); - - glBegin(GL_LINES); - - glm::vec3 guideColor(1.0, 1.0, 1.0); - glColor3fv(&guideColor.x); - - glVertex3f(origin.x + voxelS, 0, origin.z); - glVertex3f(origin.x, 0, origin.z); - - glVertex3f(origin.x, 0, origin.z); - glVertex3f(origin.x, 0, origin.z + voxelS); - - glVertex3f(origin.x + voxelS, 0, origin.z); - glVertex3f(origin.x + voxelS, 0, origin.z + voxelS); - - glVertex3f(origin.x, 0, origin.z + voxelS); - glVertex3f(origin.x + voxelS, 0, origin.z + voxelS); - - glEnd(); -} - - - - void renderSphereOutline(glm::vec3 position, float radius, int numSides, glm::vec3 cameraPosition) { glm::vec3 vectorToPosition(glm::normalize(position - cameraPosition)); glm::vec3 right = glm::cross(vectorToPosition, glm::vec3(0.0f, 1.0f, 0.0f)); diff --git a/interface/src/Util.h b/interface/src/Util.h index 5710be5f2b..04403aea16 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -61,12 +61,6 @@ float extractUniformScale(const glm::vec3& scale); double diffclock(timeval *clock1,timeval *clock2); -void renderMouseVoxelGrid(const float& mouseVoxelX, const float& mouseVoxelY, const float& mouseVoxelZ, const float& mouseVoxelS); - -void renderNudgeGrid(float voxelX, float voxelY, float voxelZ, float voxelS, float voxelPrecision); - -void renderNudgeGuide(float voxelX, float voxelY, float voxelZ, float voxelS); - void renderCollisionOverlay(int width, int height, float magnitude); void renderOrientationDirections( glm::vec3 position, const glm::quat& orientation, float size ); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 4940550180..73032bf368 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -77,7 +77,8 @@ Avatar::Avatar() : _moving(false), _owningAvatarMixer(), _collisionFlags(0), - _initialized(false) + _initialized(false), + _billboardHysteresis(false) { // we may have been created in the network thread, but we live in the main thread moveToThread(Application::getInstance()->thread()); @@ -210,7 +211,7 @@ void Avatar::render() { // render voice intensity sphere for avatars that are farther away const float MAX_SPHERE_ANGLE = 10.f; const float MIN_SPHERE_ANGLE = 1.f; - const float MIN_SPHERE_SIZE = 0.01; + const float MIN_SPHERE_SIZE = 0.01f; const float SPHERE_LOUDNESS_SCALING = 0.0005f; const float SPHERE_COLOR[] = { 0.5f, 0.8f, 0.8f }; float height = getSkeletonHeight(); @@ -290,11 +291,22 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { return glm::angleAxis(angle * proportion, axis); } -void Avatar::renderBody() { - const float BILLBOARD_DISTANCE = 40.0f; - if (!_billboard.isEmpty() && getLODDistance() >= BILLBOARD_DISTANCE) { - renderBillboard(); - return; +const float BILLBOARD_LOD_DISTANCE = 40.0f; + +void Avatar::renderBody() { + if (!_billboard.isEmpty()) { + const float BILLBOARD_HYSTERESIS_PROPORTION = 0.1f; + if (_billboardHysteresis) { + if (getLODDistance() < BILLBOARD_LOD_DISTANCE * (1.0f - BILLBOARD_HYSTERESIS_PROPORTION)) { + _billboardHysteresis = false; + } + } else if (getLODDistance() > BILLBOARD_LOD_DISTANCE * (1.0f + BILLBOARD_HYSTERESIS_PROPORTION)) { + _billboardHysteresis = true; + } + if (_billboardHysteresis) { + renderBillboard(); + return; + } } _skeletonModel.render(1.0f); getHead()->render(1.0f); @@ -565,6 +577,9 @@ void Avatar::setBillboard(const QByteArray& billboard) { // clear out any existing billboard texture _billboardTexture.reset(); + + // reset the hysteresis value + _billboardHysteresis = (getLODDistance() >= BILLBOARD_LOD_DISTANCE); } int Avatar::parseData(const QByteArray& packet) { diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 9a693f4a86..9ca6173089 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -177,6 +177,7 @@ private: bool _initialized; QScopedPointer _billboardTexture; + bool _billboardHysteresis; void renderBody(); void renderBillboard(); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index fed6716ffa..847baf782f 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -92,7 +92,7 @@ void AvatarManager::renderAvatars(bool forceRenderMyHead, bool selfAvatarOnly) { renderAvatarFades(); } else { // just render myAvatar - _myAvatar->render(forceRenderMyHead); + _myAvatar->render(forceRenderMyHead, true); _myAvatar->setDisplayingLookatVectors(renderLookAtVectors); } } diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 9c62551b00..51f919130b 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -68,7 +68,8 @@ void Hand::simulate(float deltaTime, bool isMine) { if (palm.getControllerButtons() & BUTTON_1) { if (glm::length(fingerTipPosition - _lastFingerAddVoxel) > (FINGERTIP_VOXEL_SIZE / 2.f)) { - QColor paintColor = Menu::getInstance()->getActionForOption(MenuOption::VoxelPaintColor)->data().value(); + // TODO: we need to move this code to JS so it can access the editVoxels.js color palette + QColor paintColor(128,128,128); Application::getInstance()->makeVoxel(fingerTipPosition, FINGERTIP_VOXEL_SIZE, paintColor.red(), diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 2e4309ac18..7b22201bfb 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -452,7 +452,7 @@ void MyAvatar::renderDebugBodyPoints() { } -void MyAvatar::render(bool forceRenderHead) { +void MyAvatar::render(bool forceRenderHead, bool avatarOnly) { // don't render if we've been asked to disable local rendering if (!_shouldRender) { return; // exit early @@ -468,7 +468,10 @@ void MyAvatar::render(bool forceRenderHead) { if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) { renderBody(forceRenderHead); } - setShowDisplayName(true); + setShowDisplayName(!avatarOnly); + if (avatarOnly) { + return; + } renderDisplayName(); if (!_chatMessage.empty()) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index c1c217136d..aeb9e6d968 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -35,7 +35,7 @@ public: void simulate(float deltaTime); void updateFromGyros(float deltaTime); - void render(bool forceRenderHead); + void render(bool forceRenderHead, bool avatarOnly = false); void renderDebugBodyPoints(); void renderHeadMouse() const; diff --git a/interface/src/devices/Visage.cpp b/interface/src/devices/Visage.cpp index c3dfeab6b2..f9d3f89552 100644 --- a/interface/src/devices/Visage.cpp +++ b/interface/src/devices/Visage.cpp @@ -30,10 +30,6 @@ using namespace VisageSDK; const glm::vec3 DEFAULT_HEAD_ORIGIN(0.0f, 0.0f, 0.7f); Visage::Visage() : -#ifdef HAVE_VISAGE - _leftInnerBrowIndex(0), - _rightInnerBrowIndex(0), -#endif _active(false), _headOrigin(DEFAULT_HEAD_ORIGIN), _estimatedEyePitch(0.0f), @@ -67,24 +63,35 @@ Visage::~Visage() { #ifdef HAVE_VISAGE static int leftEyeBlinkIndex = 0; static int rightEyeBlinkIndex = 1; -static int centerBrowIndex = 16; -static QHash createBlendshapeIndices() { - QHash blendshapeMap; - blendshapeMap.insert("Sneer", "au_nose_wrinkler"); - blendshapeMap.insert("JawFwd", "au_jaw_z_push"); - blendshapeMap.insert("JawLeft", "au_jaw_x_push"); - blendshapeMap.insert("JawOpen", "au_jaw_drop"); - blendshapeMap.insert("LipsLowerDown", "au_lower_lip_drop"); - blendshapeMap.insert("LipsUpperUp", "au_upper_lip_raiser"); - blendshapeMap.insert("LipsStretch_R", "au_lip_stretcher_left"); - blendshapeMap.insert("BrowsU_R", "au_left_outer_brow_raiser"); - blendshapeMap.insert("BrowsD_R", "au_left_brow_lowerer"); - blendshapeMap.insert("LipsStretch_L", "au_lip_stretcher_right"); - blendshapeMap.insert("BrowsU_L", "au_right_outer_brow_raiser"); - blendshapeMap.insert("BrowsD_L", "au_right_brow_lowerer"); +static QMultiHash > createActionUnitNameMap() { + QMultiHash > blendshapeMap; + blendshapeMap.insert("Sneer", QPair("au_nose_wrinkler", 1.0f)); + blendshapeMap.insert("JawFwd", QPair("au_jaw_z_push", 1.0f)); + blendshapeMap.insert("JawLeft", QPair("au_jaw_x_push", 1.0f)); + blendshapeMap.insert("JawOpen", QPair("au_jaw_drop", 1.0f)); + blendshapeMap.insert("LipsLowerDown", QPair("au_lower_lip_drop", 1.0f)); + blendshapeMap.insert("LipsUpperUp", QPair("au_upper_lip_raiser", 1.0f)); + blendshapeMap.insert("LipsStretch_R", QPair("au_lip_stretcher_left", 1.0f)); + blendshapeMap.insert("MouthSmile_L", QPair("au_lip_corner_depressor", -1.0f)); + blendshapeMap.insert("MouthSmile_R", QPair("au_lip_corner_depressor", -1.0f)); + blendshapeMap.insert("LipsPucker", QPair("au_lip_presser", 0.0f)); + blendshapeMap.insert("BrowsU_R", QPair("au_left_outer_brow_raiser", 1.0f)); + blendshapeMap.insert("BrowsU_C", QPair("au_left_inner_brow_raiser", 0.5f)); + blendshapeMap.insert("BrowsD_R", QPair("au_left_brow_lowerer", 1.0f)); + blendshapeMap.insert("EyeBlink_L", QPair("au_leye_closed", 1.0f)); + blendshapeMap.insert("EyeBlink_R", QPair("au_leye_closed", 1.0f)); + blendshapeMap.insert("EyeSquint_L", QPair("au_lid_tightener", 1.0f)); + blendshapeMap.insert("EyeSquint_R", QPair("au_lid_tightener", 1.0f)); + blendshapeMap.insert("EyeOpen_L", QPair("au_upper_lid_raiser", 1.0f)); + blendshapeMap.insert("EyeOpen_R", QPair("au_upper_lid_raiser", 1.0f)); + blendshapeMap.insert("MouthLeft", QPair("au_lower_lip_x_push", 0.0f)); + blendshapeMap.insert("LipsStretch_L", QPair("au_lip_stretcher_right", 1.0f)); + blendshapeMap.insert("BrowsU_L", QPair("au_right_outer_brow_raiser", 1.0f)); + blendshapeMap.insert("BrowsU_C", QPair("au_right_inner_brow_raiser", 0.5f)); + blendshapeMap.insert("BrowsD_L", QPair("au_right_brow_lowerer", 1.0f)); - QHash blendshapeIndices; + QMultiHash > actionUnitNameMap; for (int i = 0;; i++) { QByteArray blendshape = FACESHIFT_BLENDSHAPES[i]; if (blendshape.isEmpty()) { @@ -94,23 +101,20 @@ static QHash createBlendshapeIndices() { leftEyeBlinkIndex = i; } else if (blendshape == "EyeBlink_R") { - rightEyeBlinkIndex = i; - - } else if (blendshape == "BrowsU_C") { - centerBrowIndex = i; + rightEyeBlinkIndex = i; } - QByteArray mapping = blendshapeMap.value(blendshape); - if (!mapping.isEmpty()) { - blendshapeIndices.insert(mapping, i + 1); + for (QMultiHash >::const_iterator it = blendshapeMap.constFind(blendshape); + it != blendshapeMap.constEnd() && it.key() == blendshape; it++) { + actionUnitNameMap.insert(it.value().first, QPair(i, it.value().second)); } } - return blendshapeIndices; + return actionUnitNameMap; } -static const QHash& getBlendshapeIndices() { - static QHash blendshapeIndices = createBlendshapeIndices(); - return blendshapeIndices; +static const QMultiHash >& getActionUnitNameMap() { + static QMultiHash > actionUnitNameMap = createActionUnitNameMap(); + return actionUnitNameMap; } #endif @@ -128,18 +132,15 @@ void Visage::update() { _estimatedEyePitch = glm::degrees(-_data->gazeDirection[1]); _estimatedEyeYaw = glm::degrees(-_data->gazeDirection[0]); - if (_blendshapeIndices.isEmpty()) { - _blendshapeIndices.resize(_data->actionUnitCount); + if (_actionUnitIndexMap.isEmpty()) { int maxIndex = -1; for (int i = 0; i < _data->actionUnitCount; i++) { QByteArray name = _data->actionUnitsNames[i]; - if (name == "au_left_inner_brow_raiser") { - _leftInnerBrowIndex = i; - } else if (name == "au_right_inner_brow_raiser") { - _rightInnerBrowIndex = i; + for (QMultiHash >::const_iterator it = getActionUnitNameMap().constFind(name); + it != getActionUnitNameMap().constEnd() && it.key() == name; it++) { + _actionUnitIndexMap.insert(i, it.value()); + maxIndex = qMax(maxIndex, it.value().first); } - int index = getBlendshapeIndices().value(name) - 1; - maxIndex = qMax(maxIndex, _blendshapeIndices[i] = index); } _blendshapeCoefficients.resize(maxIndex + 1); } @@ -149,15 +150,13 @@ void Visage::update() { if (!_data->actionUnitsUsed[i]) { continue; } - int index = _blendshapeIndices.at(i); - if (index != -1) { - _blendshapeCoefficients[index] = _data->actionUnits[i]; + for (QMultiHash >::const_iterator it = _actionUnitIndexMap.constFind(i); + it != _actionUnitIndexMap.constEnd() && it.key() == i; it++) { + _blendshapeCoefficients[it.value().first] += _data->actionUnits[i] * it.value().second; } } _blendshapeCoefficients[leftEyeBlinkIndex] = 1.0f - _data->eyeClosure[1]; _blendshapeCoefficients[rightEyeBlinkIndex] = 1.0f - _data->eyeClosure[0]; - _blendshapeCoefficients[centerBrowIndex] = (_data->actionUnits[_leftInnerBrowIndex] + - _data->actionUnits[_rightInnerBrowIndex]) * 0.5f; #endif } diff --git a/interface/src/devices/Visage.h b/interface/src/devices/Visage.h index a5c826d1bf..c238b3bb8c 100644 --- a/interface/src/devices/Visage.h +++ b/interface/src/devices/Visage.h @@ -11,6 +11,8 @@ #include +#include +#include #include #include @@ -46,9 +48,7 @@ private: #ifdef HAVE_VISAGE VisageSDK::VisageTracker2* _tracker; VisageSDK::FaceData* _data; - int _leftInnerBrowIndex; - int _rightInnerBrowIndex; - QVector _blendshapeIndices; + QMultiHash > _actionUnitIndexMap; #endif bool _active; diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index a403439ec6..07eec031b5 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -44,13 +44,8 @@ void Sound::replyFinished(QNetworkReply* reply) { QByteArray headerContentType = reply->rawHeader("Content-Type"); - // RAW audio file encountered - if (headerContentType == "application/octet-stream") { - downSample(rawAudioByteArray); - } - // WAV audio file encountered - else if (headerContentType == "audio/x-wav" + if (headerContentType == "audio/x-wav" || headerContentType == "audio/wav" || headerContentType == "audio/wave") { @@ -59,7 +54,8 @@ void Sound::replyFinished(QNetworkReply* reply) { interpretAsWav(rawAudioByteArray, outputAudioByteArray); downSample(outputAudioByteArray); } else { - qDebug() << "Unknown audio file 'Content-Type'."; + // Process as RAW file + downSample(rawAudioByteArray); } } else { qDebug() << "Network reply without 'Content-Type'."; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 2cb719c446..482cd7564a 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -11,6 +11,9 @@ #include #include +#include +#include +#include #include #include @@ -24,6 +27,8 @@ using namespace std; static const float fingerVectorRadix = 4; // bits of precision when converting from float<->fixed +QNetworkAccessManager* AvatarData::networkAccessManager = NULL; + AvatarData::AvatarData() : NodeData(), _handPosition(0,0,0), @@ -38,7 +43,8 @@ AvatarData::AvatarData() : _handData(NULL), _displayNameBoundingRect(), _displayNameTargetAlpha(0.0f), - _displayNameAlpha(0.0f) + _displayNameAlpha(0.0f), + _billboard() { } @@ -338,6 +344,28 @@ void AvatarData::setBillboard(const QByteArray& billboard) { qDebug() << "Changing billboard for avatar."; } +void AvatarData::setBillboardFromURL(const QString &billboardURL) { + _billboardURL = billboardURL; + + if (AvatarData::networkAccessManager) { + qDebug() << "Changing billboard for avatar to PNG at" << qPrintable(billboardURL); + + QNetworkRequest billboardRequest; + billboardRequest.setUrl(QUrl(billboardURL)); + + QNetworkReply* networkReply = AvatarData::networkAccessManager->get(billboardRequest); + connect(networkReply, SIGNAL(finished()), this, SLOT(setBillboardFromNetworkReply())); + + } else { + qDebug() << "Billboard PNG download requested but no network access manager is available."; + } +} + +void AvatarData::setBillboardFromNetworkReply() { + QNetworkReply* networkReply = reinterpret_cast(sender()); + setBillboard(networkReply->readAll()); +} + void AvatarData::setClampedTargetScale(float targetScale) { targetScale = glm::clamp(targetScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); @@ -361,8 +389,10 @@ void AvatarData::sendIdentityPacket() { } void AvatarData::sendBillboardPacket() { - QByteArray billboardPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard); - billboardPacket.append(_billboard); - - NodeList::getInstance()->broadcastToNodes(billboardPacket, NodeSet() << NodeType::AvatarMixer); + if (!_billboard.isEmpty()) { + QByteArray billboardPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard); + billboardPacket.append(_billboard); + + NodeList::getInstance()->broadcastToNodes(billboardPacket, NodeSet() << NodeType::AvatarMixer); + } } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index c5cd72c184..0ce2910398 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -68,6 +68,8 @@ enum KeyState { const glm::vec3 vec3Zero(0.0f); +class QNetworkAccessManager; + class AvatarData : public NodeData { Q_OBJECT @@ -88,6 +90,7 @@ class AvatarData : public NodeData { Q_PROPERTY(QString faceModelURL READ getFaceModelURLFromScript WRITE setFaceModelURLFromScript) Q_PROPERTY(QString skeletonModelURL READ getSkeletonModelURLFromScript WRITE setSkeletonModelURLFromScript) + Q_PROPERTY(QString billboardURL READ getBillboardURL WRITE setBillboardFromURL) public: AvatarData(); ~AvatarData(); @@ -170,6 +173,9 @@ public: virtual void setBillboard(const QByteArray& billboard); const QByteArray& getBillboard() const { return _billboard; } + void setBillboardFromURL(const QString& billboardURL); + const QString& getBillboardURL() { return _billboardURL; } + QString getFaceModelURLFromScript() const { return _faceModelURL.toString(); } void setFaceModelURLFromScript(const QString& faceModelString) { setFaceModelURL(faceModelString); } @@ -177,11 +183,13 @@ public: void setSkeletonModelURLFromScript(const QString& skeletonModelString) { setSkeletonModelURL(QUrl(skeletonModelString)); } virtual float getBoundingRadius() const { return 1.f; } + + static void setNetworkAccessManager(QNetworkAccessManager* sharedAccessManager) { networkAccessManager = sharedAccessManager; } public slots: void sendIdentityPacket(); void sendBillboardPacket(); - + void setBillboardFromNetworkReply(); protected: glm::vec3 _position; glm::vec3 _handPosition; @@ -217,6 +225,9 @@ protected: float _displayNameAlpha; QByteArray _billboard; + QString _billboardURL; + + static QNetworkAccessManager* networkAccessManager; private: // privatize the copy constructor and assignment operator so they cannot be called diff --git a/libraries/script-engine/src/EventTypes.cpp b/libraries/script-engine/src/EventTypes.cpp index c8451d84a7..d016dc6fc4 100644 --- a/libraries/script-engine/src/EventTypes.cpp +++ b/libraries/script-engine/src/EventTypes.cpp @@ -126,6 +126,30 @@ bool KeyEvent::operator==(const KeyEvent& other) const { && other.isKeypad == isKeypad; } + +KeyEvent::operator QKeySequence() const { + int resultCode = 0; + if (text.size() == 1 && text >= "a" && text <= "z") { + resultCode = text.toUpper().at(0).unicode(); + } else { + resultCode = key; + } + + if (isMeta) { + resultCode |= Qt::META; + } + if (isAlt) { + resultCode |= Qt::ALT; + } + if (isControl) { + resultCode |= Qt::CTRL; + } + if (isShifted) { + resultCode |= Qt::SHIFT; + } + return QKeySequence(resultCode); +} + QScriptValue keyEventToScriptValue(QScriptEngine* engine, const KeyEvent& event) { QScriptValue obj = engine->newObject(); obj.setProperty("key", event.key); diff --git a/libraries/script-engine/src/EventTypes.h b/libraries/script-engine/src/EventTypes.h index a416747a85..5cd31f190c 100644 --- a/libraries/script-engine/src/EventTypes.h +++ b/libraries/script-engine/src/EventTypes.h @@ -24,6 +24,8 @@ public: KeyEvent(); KeyEvent(const QKeyEvent& event); bool operator==(const KeyEvent& other) const; + operator QKeySequence() const; + int key; QString text; bool isShifted; diff --git a/libraries/script-engine/src/MenuItemProperties.cpp b/libraries/script-engine/src/MenuItemProperties.cpp new file mode 100644 index 0000000000..fc1e94f40c --- /dev/null +++ b/libraries/script-engine/src/MenuItemProperties.cpp @@ -0,0 +1,95 @@ +// +// MenuItemProperties.cpp +// hifi +// +// Created by Brad Hefta-Gaub on 1/28/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// Used to register meta-types with Qt for very various event types so that they can be exposed to our +// scripting engine + +#include +#include +#include "MenuItemProperties.h" + +MenuItemProperties::MenuItemProperties() : + menuName(""), + menuItemName(""), + shortcutKey(""), + shortcutKeyEvent(), + shortcutKeySequence(), + position(UNSPECIFIED_POSITION), + beforeItem(""), + afterItem(""), + isCheckable(false), + isChecked(false) +{ +}; + +MenuItemProperties::MenuItemProperties(const QString& menuName, const QString& menuItemName, + const QString& shortcutKey, bool checkable, bool checked) : + menuName(menuName), + menuItemName(menuItemName), + shortcutKey(shortcutKey), + shortcutKeyEvent(), + shortcutKeySequence(shortcutKey), + position(UNSPECIFIED_POSITION), + beforeItem(""), + afterItem(""), + isCheckable(checkable), + isChecked(checked) +{ +} + +MenuItemProperties::MenuItemProperties(const QString& menuName, const QString& menuItemName, + const KeyEvent& shortcutKeyEvent, bool checkable, bool checked) : + menuName(menuName), + menuItemName(menuItemName), + shortcutKey(""), + shortcutKeyEvent(shortcutKeyEvent), + shortcutKeySequence(shortcutKeyEvent), + position(UNSPECIFIED_POSITION), + beforeItem(""), + afterItem(""), + isCheckable(checkable), + isChecked(checked) +{ +} + +void registerMenuItemProperties(QScriptEngine* engine) { + qScriptRegisterMetaType(engine, menuItemPropertiesToScriptValue, menuItemPropertiesFromScriptValue); +} + +QScriptValue menuItemPropertiesToScriptValue(QScriptEngine* engine, const MenuItemProperties& properties) { + QScriptValue obj = engine->newObject(); + // not supported + return obj; +} + +void menuItemPropertiesFromScriptValue(const QScriptValue& object, MenuItemProperties& properties) { + properties.menuName = object.property("menuName").toVariant().toString(); + properties.menuItemName = object.property("menuItemName").toVariant().toString(); + properties.isCheckable = object.property("isCheckable").toVariant().toBool(); + properties.isChecked = object.property("isChecked").toVariant().toBool(); + + // handle the shortcut key options in order... + QScriptValue shortcutKeyValue = object.property("shortcutKey"); + if (shortcutKeyValue.isValid()) { + properties.shortcutKey = shortcutKeyValue.toVariant().toString(); + properties.shortcutKeySequence = properties.shortcutKey; + } else { + QScriptValue shortcutKeyEventValue = object.property("shortcutKeyEvent"); + if (shortcutKeyEventValue.isValid()) { + keyEventFromScriptValue(shortcutKeyEventValue, properties.shortcutKeyEvent); + properties.shortcutKeySequence = properties.shortcutKeyEvent; + } + } + + if (object.property("position").isValid()) { + properties.position = object.property("position").toVariant().toInt(); + } + properties.beforeItem = object.property("beforeItem").toVariant().toString(); + properties.afterItem = object.property("afterItem").toVariant().toString(); +} + + diff --git a/libraries/script-engine/src/MenuItemProperties.h b/libraries/script-engine/src/MenuItemProperties.h new file mode 100644 index 0000000000..6e0b3309b1 --- /dev/null +++ b/libraries/script-engine/src/MenuItemProperties.h @@ -0,0 +1,50 @@ +// +// MenuItemProperties.h +// hifi +// +// Created by Brad Hefta-Gaub on 1/28/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#ifndef __hifi_MenuItemProperties_h__ +#define __hifi_MenuItemProperties_h__ + +#include + +#include "EventTypes.h" + +const int UNSPECIFIED_POSITION = -1; + +class MenuItemProperties { +public: + MenuItemProperties(); + MenuItemProperties(const QString& menuName, const QString& menuItemName, + const QString& shortcutKey = QString(""), bool checkable = false, bool checked = false); + MenuItemProperties(const QString& menuName, const QString& menuItemName, + const KeyEvent& shortcutKeyEvent, bool checkable = false, bool checked = false); + + QString menuName; + QString menuItemName; + + // Shortcut key items: in order of priority + QString shortcutKey; + KeyEvent shortcutKeyEvent; + QKeySequence shortcutKeySequence; // this is what we actually use, it's set from one of the above + + // location related items: in order of priority + int position; + QString beforeItem; + QString afterItem; + + // other properties + bool isCheckable; + bool isChecked; +}; +Q_DECLARE_METATYPE(MenuItemProperties) +QScriptValue menuItemPropertiesToScriptValue(QScriptEngine* engine, const MenuItemProperties& props); +void menuItemPropertiesFromScriptValue(const QScriptValue& object, MenuItemProperties& props); +void registerMenuItemProperties(QScriptEngine* engine); + + + +#endif // __hifi_MenuItemProperties_h__ diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 076f941222..e08317f8b4 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -24,6 +24,7 @@ #include +#include "MenuItemProperties.h" #include "ScriptEngine.h" const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000; @@ -41,10 +42,10 @@ static QScriptValue soundConstructor(QScriptContext* context, QScriptEngine* eng ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, const QString& fileNameString, - AbstractMenuInterface* menu, AbstractControllerScriptingInterface* controllerScriptingInterface) : _isAvatar(false), _avatarIdentityTimer(NULL), + _avatarBillboardTimer(NULL), _avatarData(NULL) { _scriptContents = scriptContents; @@ -67,7 +68,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, co _scriptMenuName.append(_scriptNumber); } _scriptNumber++; - _menu = menu; _controllerScriptingInterface = controllerScriptingInterface; } @@ -79,14 +79,17 @@ void ScriptEngine::setIsAvatar(bool isAvatar) { _isAvatar = isAvatar; if (_isAvatar && !_avatarIdentityTimer) { - // set up the avatar identity timer + // set up the avatar timers _avatarIdentityTimer = new QTimer(this); + _avatarBillboardTimer = new QTimer(this); // connect our slot connect(_avatarIdentityTimer, &QTimer::timeout, this, &ScriptEngine::sendAvatarIdentityPacket); + connect(_avatarBillboardTimer, &QTimer::timeout, this, &ScriptEngine::sendAvatarBillboardPacket); - // start the timer + // start the timers _avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); + _avatarBillboardTimer->start(AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS); } } @@ -100,16 +103,9 @@ void ScriptEngine::setAvatarData(AvatarData* avatarData, const QString& objectNa registerGlobalObject(objectName, _avatarData); } - -void ScriptEngine::setupMenuItems() { - if (_menu && _wantMenuItems) { - _menu->addActionToQMenuAndActionHash(_menu->getActiveScriptsMenu(), _scriptMenuName, 0, this, SLOT(stop())); - } -} - -void ScriptEngine::cleanMenuItems() { - if (_menu && _wantMenuItems) { - _menu->removeAction(_menu->getActiveScriptsMenu(), _scriptMenuName); +void ScriptEngine::cleanupMenuItems() { + if (_wantMenuItems) { + emit cleanupMenuItem(_scriptMenuName); } } @@ -136,8 +132,8 @@ void ScriptEngine::init() { // register various meta-types registerMetaTypes(&_engine); registerVoxelMetaTypes(&_engine); - //registerParticleMetaTypes(&_engine); registerEventTypes(&_engine); + registerMenuItemProperties(&_engine); qScriptRegisterMetaType(&_engine, ParticlePropertiesToScriptValue, ParticlePropertiesFromScriptValue); qScriptRegisterMetaType(&_engine, ParticleIDtoScriptValue, ParticleIDfromScriptValue); @@ -167,7 +163,6 @@ void ScriptEngine::init() { _voxelsScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS); _particlesScriptingInterface.getParticlePacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS); - //qDebug() << "Script:\n" << _scriptContents << "\n"; } void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { @@ -196,6 +191,12 @@ void ScriptEngine::sendAvatarIdentityPacket() { } } +void ScriptEngine::sendAvatarBillboardPacket() { + if (_isAvatar && _avatarData) { + _avatarData->sendBillboardPacket(); + } +} + void ScriptEngine::run() { if (!_isInitialized) { init(); @@ -290,7 +291,7 @@ void ScriptEngine::run() { } } - cleanMenuItems(); + cleanupMenuItems(); // If we were on a thread, then wait till it's done if (thread()) { diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index cf5ad1a68b..00906de7b3 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -15,7 +15,6 @@ #include #include -#include #include #include @@ -33,7 +32,7 @@ class ScriptEngine : public QObject { Q_OBJECT public: ScriptEngine(const QString& scriptContents = NO_SCRIPT, bool wantMenuItems = false, - const QString& scriptMenuName = QString(""), AbstractMenuInterface* menu = NULL, + const QString& scriptMenuName = QString(""), AbstractControllerScriptingInterface* controllerScriptingInterface = NULL); ~ScriptEngine(); @@ -47,8 +46,8 @@ public: /// sets the script contents, will return false if failed, will fail if script is already running bool setScriptContents(const QString& scriptContents); - void setupMenuItems(); - void cleanMenuItems(); + const QString& getScriptMenuName() const { return _scriptMenuName; } + void cleanupMenuItems(); void registerGlobalObject(const QString& name, QObject* object); /// registers a global object by name @@ -76,6 +75,7 @@ signals: void willSendVisualDataCallback(); void scriptEnding(); void finished(const QString& fileNameString); + void cleanupMenuItem(const QString& menuItemString); protected: QString _scriptContents; @@ -85,10 +85,12 @@ protected: QScriptEngine _engine; bool _isAvatar; QTimer* _avatarIdentityTimer; + QTimer* _avatarBillboardTimer; QHash _timerFunctionMap; private: void sendAvatarIdentityPacket(); + void sendAvatarBillboardPacket(); QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot); void stopTimer(QTimer* timer); @@ -101,7 +103,6 @@ private: bool _wantMenuItems; QString _scriptMenuName; QString _fileNameString; - AbstractMenuInterface* _menu; static int _scriptNumber; Quat _quatLibrary; Vec3 _vec3Library; diff --git a/libraries/shared/src/AbstractMenuInterface.h b/libraries/shared/src/AbstractMenuInterface.h deleted file mode 100644 index 66083e4ed4..0000000000 --- a/libraries/shared/src/AbstractMenuInterface.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// AbstractMenuInterface.h -// hifi -// -// Created by Brad Hefta-Gaub on 12/16/13. -// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// -// - -#ifndef __hifi__AbstractMenuInterface__ -#define __hifi__AbstractMenuInterface__ - -#include - -class QMenu; -class QString; -class QObject; -class QKeySequence; - -class AbstractMenuInterface { -public: - virtual QMenu* getActiveScriptsMenu() = 0; - virtual QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu, - const QString actionName, - const QKeySequence& shortcut = 0, - const QObject* receiver = NULL, - const char* member = NULL, - QAction::MenuRole role = QAction::NoRole) = 0; - virtual void removeAction(QMenu* menu, const QString& actionName) = 0; -}; - -#endif /* defined(__hifi__AbstractMenuInterface__) */ diff --git a/libraries/shared/src/AccountManager.cpp b/libraries/shared/src/AccountManager.cpp index b8fc1061f8..0f39ba4146 100644 --- a/libraries/shared/src/AccountManager.cpp +++ b/libraries/shared/src/AccountManager.cpp @@ -37,7 +37,7 @@ AccountManager::AccountManager() : _authURL(), _networkAccessManager(), _pendingCallbackMap(), - _accounts() + _accountInfo() { qRegisterMetaType("OAuthAccessToken"); qRegisterMetaTypeStreamOperators("OAuthAccessToken"); @@ -47,27 +47,13 @@ AccountManager::AccountManager() : qRegisterMetaType("QNetworkAccessManager::Operation"); qRegisterMetaType("JSONCallbackParameters"); - - // check if there are existing access tokens to load from settings - QSettings settings; - settings.beginGroup(ACCOUNTS_GROUP); - - foreach(const QString& key, settings.allKeys()) { - // take a key copy to perform the double slash replacement - QString keyCopy(key); - QUrl keyURL(keyCopy.replace("slashslash", "//")); - - // pull out the stored access token and put it in our in memory array - _accounts.insert(keyURL, settings.value(key).value()); - qDebug() << "Found a data-server access token for" << qPrintable(keyURL.toString()); - } } const QString DOUBLE_SLASH_SUBSTITUTE = "slashslash"; void AccountManager::logout() { // a logout means we want to delete the DataServerAccountInfo we currently have for this URL, in-memory and in file - _accounts.remove(_authURL); + _accountInfo = DataServerAccountInfo(); QSettings settings; settings.beginGroup(ACCOUNTS_GROUP); @@ -80,7 +66,6 @@ void AccountManager::logout() { emit logoutComplete(); // the username has changed to blank emit usernameChanged(QString()); - } void AccountManager::setAuthURL(const QUrl& authURL) { @@ -90,6 +75,24 @@ void AccountManager::setAuthURL(const QUrl& authURL) { qDebug() << "URL for node authentication has been changed to" << qPrintable(_authURL.toString()); qDebug() << "Re-setting authentication flow."; + // check if there are existing access tokens to load from settings + QSettings settings; + settings.beginGroup(ACCOUNTS_GROUP); + + foreach(const QString& key, settings.allKeys()) { + // take a key copy to perform the double slash replacement + QString keyCopy(key); + QUrl keyURL(keyCopy.replace("slashslash", "//")); + + if (keyURL == _authURL) { + // pull out the stored access token and store it in memory + _accountInfo = settings.value(key).value(); + qDebug() << "Found a data-server access token for" << qPrintable(keyURL.toString()); + + emit accessTokenChanged(); + } + } + // tell listeners that the auth endpoint has changed emit authEndpointChanged(); } @@ -111,7 +114,7 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager:: QUrl requestURL = _authURL; requestURL.setPath(path); - requestURL.setQuery("access_token=" + _accounts.value(_authURL).getAccessToken().token); + requestURL.setQuery("access_token=" + _accountInfo.getAccessToken().token); authenticatedRequest.setUrl(requestURL); @@ -202,9 +205,8 @@ void AccountManager::passErrorToCallback(QNetworkReply::NetworkError errorCode) } bool AccountManager::hasValidAccessToken() { - DataServerAccountInfo accountInfo = _accounts.value(_authURL); - if (accountInfo.getAccessToken().token.isEmpty() || accountInfo.getAccessToken().isExpired()) { + if (_accountInfo.getAccessToken().token.isEmpty() || _accountInfo.getAccessToken().isExpired()) { if (VERBOSE_HTTP_REQUEST_DEBUGGING) { qDebug() << "An access token is required for requests to" << qPrintable(_authURL.toString()); } @@ -266,18 +268,20 @@ void AccountManager::requestFinished() { qDebug() << "Storing an account with access-token for" << qPrintable(rootURL.toString()); - DataServerAccountInfo freshAccountInfo(rootObject); - _accounts.insert(rootURL, freshAccountInfo); + _accountInfo = DataServerAccountInfo(rootObject); emit loginComplete(rootURL); // the username has changed to whatever came back - emit usernameChanged(freshAccountInfo.getUsername()); + emit usernameChanged(_accountInfo.getUsername()); + + // we have found or requested an access token + emit accessTokenChanged(); // store this access token into the local settings QSettings localSettings; localSettings.beginGroup(ACCOUNTS_GROUP); localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE), - QVariant::fromValue(freshAccountInfo)); + QVariant::fromValue(_accountInfo)); } } else { // TODO: error handling diff --git a/libraries/shared/src/AccountManager.h b/libraries/shared/src/AccountManager.h index 35819f2c9a..c741beb461 100644 --- a/libraries/shared/src/AccountManager.h +++ b/libraries/shared/src/AccountManager.h @@ -51,7 +51,7 @@ public: void requestAccessToken(const QString& login, const QString& password); - QString getUsername() const { return _accounts[_authURL].getUsername(); } + QString getUsername() const { return _accountInfo.getUsername(); } public slots: void requestFinished(); @@ -61,6 +61,7 @@ signals: void authRequired(); void authEndpointChanged(); void usernameChanged(const QString& username); + void accessTokenChanged(); void loginComplete(const QUrl& authURL); void logoutComplete(); private slots: @@ -78,7 +79,7 @@ private: QNetworkAccessManager _networkAccessManager; QMap _pendingCallbackMap; - QMap _accounts; + DataServerAccountInfo _accountInfo; }; #endif /* defined(__hifi__AccountManager__) */ diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 98387ce420..d8c6cd09f1 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -549,6 +549,12 @@ void NodeList::sendDomainServerCheckIn() { sendSTUNRequest(); } + if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { + // we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS + // so emit our signal that indicates that + emit limitOfSilentDomainCheckInsReached(); + } + // increment the count of un-replied check-ins _numNoReplyDomainCheckIns++; } else if (AccountManager::getInstance().hasValidAccessToken()) { diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index a66bcf3535..590a2ce83f 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -132,6 +132,7 @@ signals: void uuidChanged(const QUuid& ownerUUID); void nodeAdded(SharedNodePointer); void nodeKilled(SharedNodePointer); + void limitOfSilentDomainCheckInsReached(); private slots: void domainServerAuthReply(const QJsonObject& jsonObject); private: diff --git a/libraries/shared/src/OAuthAccessToken.cpp b/libraries/shared/src/OAuthAccessToken.cpp index 522a3700cd..20a9dc3aa5 100644 --- a/libraries/shared/src/OAuthAccessToken.cpp +++ b/libraries/shared/src/OAuthAccessToken.cpp @@ -13,7 +13,7 @@ OAuthAccessToken::OAuthAccessToken() : token(), refreshToken(), - expiryTimestamp(), + expiryTimestamp(0), tokenType() { @@ -22,7 +22,7 @@ OAuthAccessToken::OAuthAccessToken() : OAuthAccessToken::OAuthAccessToken(const QJsonObject& jsonObject) : token(jsonObject["access_token"].toString()), refreshToken(jsonObject["refresh_token"].toString()), - expiryTimestamp(QDateTime::currentMSecsSinceEpoch() + jsonObject["expires_in"].toDouble()), + expiryTimestamp(QDateTime::currentMSecsSinceEpoch() + (jsonObject["expires_in"].toDouble() * 1000)), tokenType(jsonObject["token_type"].toString()) {