Merge branch 'master' of https://github.com/highfidelity/hifi into metavoxels

This commit is contained in:
Andrzej Kapolka 2014-02-26 13:50:32 -08:00
commit 9d95ff16b7
44 changed files with 966 additions and 1647 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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();

View file

@ -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();

104
examples/menuExample.js Normal file
View file

@ -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);

File diff suppressed because it is too large Load diff

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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() {

View file

@ -19,6 +19,7 @@
#include <QLineEdit>
#include <QMainWindow>
#include <QMenuBar>
#include <QShortcut>
#include <QSlider>
#include <QStandardPaths>
#include <QUuid>
@ -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<QColor>(),
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<QAction*> 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();
};

View file

@ -14,7 +14,9 @@
#include <QKeySequence>
#include <QPointer>
#include <AbstractMenuInterface.h>
#include <EventTypes.h>
#include <MenuItemProperties.h>
#include <OctreeConstants.h>
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<QString, QAction*> _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> _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";
}

View file

@ -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<QAction*>(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);
}

View file

@ -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 <QDebug>
#include <QMutex>
#include <QObject>
#include <QString>
#include "Menu.h"
#include <MenuItemProperties.h>
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__) */

View file

@ -1,183 +0,0 @@
//
// Swatch.h
// interface
//
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include "Swatch.h"
#include <iostream>
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<QColor>() == _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<QColor>() != _colors[_selected - 1]) {
_selected = next;
_colors[_selected - 1] = _action->data().value<QColor>();
}
} 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);
}

View file

@ -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__) */

View file

@ -1,51 +0,0 @@
#include "Tool.h"
#include <QSvgRenderer>
#include <QPainter>
#include <QGLWidget>
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);
}

View file

@ -1,55 +0,0 @@
//
// Tool.h
// interface
//
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __interface__Tool__
#define __interface__Tool__
#include <QAction>
#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__) */

View file

@ -1,88 +0,0 @@
#include "ToolsPalette.h"
#include <QSvgRenderer>
#include <QPainter>
#include <QGLWidget>
#include <SharedUtil.h>
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;
}

View file

@ -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 <vector>
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<Tool*> _tools;
int _top;
int _left;
int _width;
int _height;
};
#endif /* defined(__interface__ToolsPalette__) */

View file

@ -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));

View file

@ -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 );

View file

@ -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) {

View file

@ -177,6 +177,7 @@ private:
bool _initialized;
QScopedPointer<Texture> _billboardTexture;
bool _billboardHysteresis;
void renderBody();
void renderBillboard();

View file

@ -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);
}
}

View file

@ -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<QColor>();
// 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(),

View file

@ -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()) {

View file

@ -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;

View file

@ -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<QByteArray, int> createBlendshapeIndices() {
QHash<QByteArray, QByteArray> 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<QByteArray, QPair<int, float> > createActionUnitNameMap() {
QMultiHash<QByteArray, QPair<QByteArray, float> > blendshapeMap;
blendshapeMap.insert("Sneer", QPair<QByteArray, float>("au_nose_wrinkler", 1.0f));
blendshapeMap.insert("JawFwd", QPair<QByteArray, float>("au_jaw_z_push", 1.0f));
blendshapeMap.insert("JawLeft", QPair<QByteArray, float>("au_jaw_x_push", 1.0f));
blendshapeMap.insert("JawOpen", QPair<QByteArray, float>("au_jaw_drop", 1.0f));
blendshapeMap.insert("LipsLowerDown", QPair<QByteArray, float>("au_lower_lip_drop", 1.0f));
blendshapeMap.insert("LipsUpperUp", QPair<QByteArray, float>("au_upper_lip_raiser", 1.0f));
blendshapeMap.insert("LipsStretch_R", QPair<QByteArray, float>("au_lip_stretcher_left", 1.0f));
blendshapeMap.insert("MouthSmile_L", QPair<QByteArray, float>("au_lip_corner_depressor", -1.0f));
blendshapeMap.insert("MouthSmile_R", QPair<QByteArray, float>("au_lip_corner_depressor", -1.0f));
blendshapeMap.insert("LipsPucker", QPair<QByteArray, float>("au_lip_presser", 0.0f));
blendshapeMap.insert("BrowsU_R", QPair<QByteArray, float>("au_left_outer_brow_raiser", 1.0f));
blendshapeMap.insert("BrowsU_C", QPair<QByteArray, float>("au_left_inner_brow_raiser", 0.5f));
blendshapeMap.insert("BrowsD_R", QPair<QByteArray, float>("au_left_brow_lowerer", 1.0f));
blendshapeMap.insert("EyeBlink_L", QPair<QByteArray, float>("au_leye_closed", 1.0f));
blendshapeMap.insert("EyeBlink_R", QPair<QByteArray, float>("au_leye_closed", 1.0f));
blendshapeMap.insert("EyeSquint_L", QPair<QByteArray, float>("au_lid_tightener", 1.0f));
blendshapeMap.insert("EyeSquint_R", QPair<QByteArray, float>("au_lid_tightener", 1.0f));
blendshapeMap.insert("EyeOpen_L", QPair<QByteArray, float>("au_upper_lid_raiser", 1.0f));
blendshapeMap.insert("EyeOpen_R", QPair<QByteArray, float>("au_upper_lid_raiser", 1.0f));
blendshapeMap.insert("MouthLeft", QPair<QByteArray, float>("au_lower_lip_x_push", 0.0f));
blendshapeMap.insert("LipsStretch_L", QPair<QByteArray, float>("au_lip_stretcher_right", 1.0f));
blendshapeMap.insert("BrowsU_L", QPair<QByteArray, float>("au_right_outer_brow_raiser", 1.0f));
blendshapeMap.insert("BrowsU_C", QPair<QByteArray, float>("au_right_inner_brow_raiser", 0.5f));
blendshapeMap.insert("BrowsD_L", QPair<QByteArray, float>("au_right_brow_lowerer", 1.0f));
QHash<QByteArray, int> blendshapeIndices;
QMultiHash<QByteArray, QPair<int, float> > actionUnitNameMap;
for (int i = 0;; i++) {
QByteArray blendshape = FACESHIFT_BLENDSHAPES[i];
if (blendshape.isEmpty()) {
@ -94,23 +101,20 @@ static QHash<QByteArray, int> 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<QByteArray, QPair<QByteArray, float> >::const_iterator it = blendshapeMap.constFind(blendshape);
it != blendshapeMap.constEnd() && it.key() == blendshape; it++) {
actionUnitNameMap.insert(it.value().first, QPair<int, float>(i, it.value().second));
}
}
return blendshapeIndices;
return actionUnitNameMap;
}
static const QHash<QByteArray, int>& getBlendshapeIndices() {
static QHash<QByteArray, int> blendshapeIndices = createBlendshapeIndices();
return blendshapeIndices;
static const QMultiHash<QByteArray, QPair<int, float> >& getActionUnitNameMap() {
static QMultiHash<QByteArray, QPair<int, float> > 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<QByteArray, QPair<int, float> >::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<int, QPair<int, float> >::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
}

View file

@ -11,6 +11,8 @@
#include <vector>
#include <QMultiHash>
#include <QPair>
#include <QVector>
#include <glm/glm.hpp>
@ -46,9 +48,7 @@ private:
#ifdef HAVE_VISAGE
VisageSDK::VisageTracker2* _tracker;
VisageSDK::FaceData* _data;
int _leftInnerBrowIndex;
int _rightInnerBrowIndex;
QVector<int> _blendshapeIndices;
QMultiHash<int, QPair<int, float> > _actionUnitIndexMap;
#endif
bool _active;

View file

@ -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'.";

View file

@ -11,6 +11,9 @@
#include <stdint.h>
#include <QtCore/QDataStream>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
#include <NodeList.h>
#include <PacketHeaders.h>
@ -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<QNetworkReply*>(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);
}
}

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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 <QDebug>
#include <RegisteredMetaTypes.h>
#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();
}

View file

@ -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 <QtScript/QScriptEngine>
#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__

View file

@ -24,6 +24,7 @@
#include <Sound.h>
#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()) {

View file

@ -15,7 +15,6 @@
#include <QtCore/QUrl>
#include <QtScript/QScriptEngine>
#include <AbstractMenuInterface.h>
#include <AudioScriptingInterface.h>
#include <VoxelsScriptingInterface.h>
@ -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<QTimer*, QScriptValue> _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;

View file

@ -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 <QAction>
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__) */

View file

@ -37,7 +37,7 @@ AccountManager::AccountManager() :
_authURL(),
_networkAccessManager(),
_pendingCallbackMap(),
_accounts()
_accountInfo()
{
qRegisterMetaType<OAuthAccessToken>("OAuthAccessToken");
qRegisterMetaTypeStreamOperators<OAuthAccessToken>("OAuthAccessToken");
@ -47,27 +47,13 @@ AccountManager::AccountManager() :
qRegisterMetaType<QNetworkAccessManager::Operation>("QNetworkAccessManager::Operation");
qRegisterMetaType<JSONCallbackParameters>("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<DataServerAccountInfo>());
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<DataServerAccountInfo>();
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

View file

@ -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<QNetworkReply*, JSONCallbackParameters> _pendingCallbackMap;
QMap<QUrl, DataServerAccountInfo> _accounts;
DataServerAccountInfo _accountInfo;
};
#endif /* defined(__hifi__AccountManager__) */

View file

@ -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()) {

View file

@ -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:

View file

@ -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())
{