diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 45240619f9..44fc8d7cec 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -68,6 +68,7 @@ #include #include "Application.h" +#include "ui/DataWebDialog.h" #include "InterfaceVersion.h" #include "Menu.h" #include "ModelUploader.h" diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 6a873861b2..454d21be20 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -273,6 +273,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ScriptedMotorControl, 0, true, avatar, SLOT(updateMotionBehavior())); addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ChatCircling, 0, false); + addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true); addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::GlowWhenSpeaking, 0, true); addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::BlueSpeechSphere, 0, true); addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ObeyEnvironmentalGravity, Qt::SHIFT | Qt::Key_G, false, @@ -1216,13 +1217,17 @@ void Menu::displayNameLocationResponse(const QString& errorString) { void Menu::toggleLocationList() { if (!_userLocationsDialog) { - _userLocationsDialog = new UserLocationsDialog(Application::getInstance()->getWindow()); + _userLocationsDialog = DataWebDialog::dialogForPath("/locations"); } - if (_userLocationsDialog->isVisible()) { - _userLocationsDialog->hide(); - } else { + + if (!_userLocationsDialog->isVisible()) { _userLocationsDialog->show(); } + + _userLocationsDialog->raise(); + _userLocationsDialog->activateWindow(); + _userLocationsDialog->showNormal(); + } void Menu::nameLocation() { diff --git a/interface/src/Menu.h b/interface/src/Menu.h index aa518dbefe..5a2240b3d8 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -28,12 +28,12 @@ #endif #include "location/LocationManager.h" -#include "ui/PreferencesDialog.h" #include "ui/ChatWindow.h" +#include "ui/DataWebDialog.h" #include "ui/JSConsole.h" #include "ui/LoginDialog.h" +#include "ui/PreferencesDialog.h" #include "ui/ScriptEditorWindow.h" -#include "ui/UserLocationsDialog.h" const float ADJUST_LOD_DOWN_FPS = 40.0; const float ADJUST_LOD_UP_FPS = 55.0; @@ -273,7 +273,7 @@ private: QDialog* _jsConsole; OctreeStatsDialog* _octreeStatsDialog; LodToolsDialog* _lodToolsDialog; - UserLocationsDialog* _userLocationsDialog; + QPointer _userLocationsDialog; #ifdef Q_OS_MAC SpeechRecognizer _speechRecognizer; #endif @@ -395,6 +395,7 @@ namespace MenuOption { const QString FullscreenMirror = "Fullscreen Mirror"; const QString GlowMode = "Cycle Glow Mode"; const QString GlowWhenSpeaking = "Glow When Speaking"; + const QString NamesAboveHeads = "Names Above Heads"; const QString GoToUser = "Go To User"; const QString HeadMouse = "Head Mouse"; const QString IncreaseAvatarSize = "Increase Avatar Size"; diff --git a/interface/src/UserLocationsModel.cpp b/interface/src/UserLocationsModel.cpp deleted file mode 100644 index 0fae0d8800..0000000000 --- a/interface/src/UserLocationsModel.cpp +++ /dev/null @@ -1,251 +0,0 @@ -// -// UserLocationsModel.cpp -// interface/src -// -// Created by Ryan Huffman on 06/24/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include -#include - -#include "AccountManager.h" -#include "Application.h" -#include "UserLocationsModel.h" - -static const QString LOCATIONS_GET = "/api/v1/locations"; -static const QString LOCATION_UPDATE_OR_DELETE = "/api/v1/locations/%1"; - -UserLocation::UserLocation(const QString& id, const QString& name, const QString& address) : - _id(id), - _name(name), - _address(address), - _previousName(name), - _updating(false) { -} - -void UserLocation::requestRename(const QString& newName) { - if (!_updating && newName.toLower() != _name) { - _updating = true; - - JSONCallbackParameters callbackParams(this, "handleRenameResponse", this, "handleRenameError"); - - QJsonObject jsonNameObject; - jsonNameObject.insert("name", newName); - - QJsonObject locationObject; - locationObject.insert("location", jsonNameObject); - - QJsonDocument jsonDocument(jsonNameObject); - AccountManager::getInstance().authenticatedRequest(LOCATION_UPDATE_OR_DELETE.arg(_id), - QNetworkAccessManager::PutOperation, - callbackParams, - jsonDocument.toJson()); - _previousName = _name; - _name = newName; - - emit updated(_name); - } -} - -void UserLocation::handleRenameResponse(const QJsonObject& responseData) { - _updating = false; - - QJsonValue status = responseData["status"]; - if (!status.isUndefined() && status.toString() == "success") { - qDebug() << responseData; - QString updatedName = responseData["data"].toObject()["location"].toObject()["name"].toString(); - qDebug() << "The updated name is" << updatedName; - _name = updatedName; - } else { - _name = _previousName; - - QString msg = "There was an error renaming location '" + _name + "'"; - - QJsonValue data = responseData["data"]; - if (!data.isUndefined()) { - QJsonValue nameError = data.toObject()["name"]; - if (!nameError.isUndefined()) { - msg += ": " + nameError.toString(); - } - } - qDebug() << msg; - QMessageBox::warning(Application::getInstance()->getWindow(), "Error", msg); - } - - emit updated(_name); -} - -void UserLocation::handleRenameError(QNetworkReply& errorReply) { - _updating = false; - - QString msg = "There was an error renaming location '" + _name + "': " + errorReply.errorString(); - qDebug() << msg; - QMessageBox::warning(Application::getInstance()->getWindow(), "Error", msg); - - emit updated(_name); -} - -void UserLocation::requestDelete() { - if (!_updating) { - _updating = true; - - JSONCallbackParameters callbackParams(this, "handleDeleteResponse", this, "handleDeleteError"); - AccountManager::getInstance().authenticatedRequest(LOCATION_UPDATE_OR_DELETE.arg(_id), - QNetworkAccessManager::DeleteOperation, - callbackParams); - } -} - -void UserLocation::handleDeleteResponse(const QJsonObject& responseData) { - _updating = false; - - QJsonValue status = responseData["status"]; - if (!status.isUndefined() && status.toString() == "success") { - emit deleted(_name); - } else { - QString msg = "There was an error deleting location '" + _name + "'"; - qDebug() << msg; - QMessageBox::warning(Application::getInstance()->getWindow(), "Error", msg); - } -} - -void UserLocation::handleDeleteError(QNetworkReply& errorReply) { - _updating = false; - - QString msg = "There was an error deleting location '" + _name + "': " + errorReply.errorString(); - qDebug() << msg; - QMessageBox::warning(Application::getInstance()->getWindow(), "Error", msg); -} - -UserLocationsModel::UserLocationsModel(QObject* parent) : - QAbstractListModel(parent), - _updating(false) { - - refresh(); -} - -UserLocationsModel::~UserLocationsModel() { - qDeleteAll(_locations); - _locations.clear(); -} - -void UserLocationsModel::update() { - beginResetModel(); - endResetModel(); -} - -void UserLocationsModel::deleteLocation(const QModelIndex& index) { - UserLocation* location = _locations[index.row()]; - location->requestDelete(); -} - -void UserLocationsModel::renameLocation(const QModelIndex& index, const QString& newName) { - UserLocation* location = _locations[index.row()]; - location->requestRename(newName); -} - -void UserLocationsModel::refresh() { - if (!_updating) { - beginResetModel(); - qDeleteAll(_locations); - _locations.clear(); - _updating = true; - endResetModel(); - - JSONCallbackParameters callbackParams(this, "handleLocationsResponse"); - AccountManager::getInstance().authenticatedRequest(LOCATIONS_GET, - QNetworkAccessManager::GetOperation, - callbackParams); - } -} - -void UserLocationsModel::handleLocationsResponse(const QJsonObject& responseData) { - _updating = false; - - QJsonValue status = responseData["status"]; - if (!status.isUndefined() && status.toString() == "success") { - beginResetModel(); - QJsonArray locations = responseData["data"].toObject()["locations"].toArray(); - for (QJsonArray::const_iterator it = locations.constBegin(); it != locations.constEnd(); it++) { - QJsonObject location = (*it).toObject(); - QString locationAddress = "hifi://" + location["domain"].toObject()["name"].toString() - + location["path"].toString(); - UserLocation* userLocation = new UserLocation(location["id"].toString(), location["name"].toString(), - locationAddress); - _locations.append(userLocation); - connect(userLocation, &UserLocation::deleted, this, &UserLocationsModel::removeLocation); - connect(userLocation, &UserLocation::updated, this, &UserLocationsModel::update); - } - endResetModel(); - } else { - qDebug() << "Error loading location data"; - } -} - -void UserLocationsModel::removeLocation(const QString& name) { - beginResetModel(); - for (QList::iterator it = _locations.begin(); it != _locations.end(); it++) { - if ((*it)->name() == name) { - _locations.erase(it); - break; - } - } - endResetModel(); -} - -int UserLocationsModel::rowCount(const QModelIndex& parent) const { - if (parent.isValid()) { - return 0; - } - - if (_updating) { - return 1; - } - - return _locations.length(); -} - -QVariant UserLocationsModel::data(const QModelIndex& index, int role) const { - if (role == Qt::DisplayRole) { - if (_updating) { - return QVariant("Updating..."); - } else if (index.row() > _locations.length()) { - return QVariant(); - } else if (index.column() == NameColumn) { - return _locations[index.row()]->name(); - } else if (index.column() == AddressColumn) { - return QVariant(_locations[index.row()]->address()); - } - } - - return QVariant(); - -} -QVariant UserLocationsModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { - switch (section) { - case NameColumn: return "Name"; - case AddressColumn: return "Address"; - default: return QVariant(); - } - } - - return QVariant(); -} - -Qt::ItemFlags UserLocationsModel::flags(const QModelIndex& index) const { - if (index.row() < _locations.length()) { - UserLocation* ul = _locations[index.row()]; - if (ul->isUpdating()) { - return Qt::NoItemFlags; - } - } - - return QAbstractListModel::flags(index); -} diff --git a/interface/src/UserLocationsModel.h b/interface/src/UserLocationsModel.h deleted file mode 100644 index 54518d72e1..0000000000 --- a/interface/src/UserLocationsModel.h +++ /dev/null @@ -1,82 +0,0 @@ -// -// UserLocationsModel.h -// interface/src -// -// Created by Ryan Huffman on 06/24/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_UserLocationsModel_h -#define hifi_UserLocationsModel_h - -#include -#include -#include - - -class UserLocation : public QObject { - Q_OBJECT -public: - UserLocation(const QString& id, const QString& name, const QString& address); - bool isUpdating() { return _updating; } - void requestRename(const QString& newName); - void requestDelete(); - - const QString& id() { return _id; } - const QString& name() { return _name; } - const QString& address() { return _address; } - -public slots: - void handleRenameResponse(const QJsonObject& responseData); - void handleRenameError(QNetworkReply& errorReply); - void handleDeleteResponse(const QJsonObject& responseData); - void handleDeleteError(QNetworkReply& errorReply); - -signals: - void updated(const QString& name); - void deleted(const QString& name); - -private: - QString _id; - QString _name; - QString _address; - QString _previousName; - bool _updating; - -}; - -class UserLocationsModel : public QAbstractListModel { - Q_OBJECT -public: - UserLocationsModel(QObject* parent = NULL); - ~UserLocationsModel(); - - virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; - virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; - virtual int columnCount(const QModelIndex& parent = QModelIndex()) const { return 2; }; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - virtual Qt::ItemFlags flags(const QModelIndex& index) const; - - void deleteLocation(const QModelIndex& index); - void renameLocation(const QModelIndex& index, const QString& newName); - - enum Columns { - NameColumn = 0, - AddressColumn - }; - -public slots: - void refresh(); - void update(); - void handleLocationsResponse(const QJsonObject& responseData); - void removeLocation(const QString& name); - -private: - bool _updating; - QList _locations; -}; - -#endif // hifi_UserLocationsModel_h diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index b62c744fa2..11537294ea 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1027,6 +1027,11 @@ float Avatar::getPelvisToHeadLength() const { } void Avatar::setShowDisplayName(bool showDisplayName) { + if (!Menu::getInstance()->isOptionChecked(MenuOption::NamesAboveHeads)) { + _displayNameAlpha = 0.0f; + return; + } + // For myAvatar, the alpha update is not done (called in simulate for other avatars) if (Application::getInstance()->getAvatar() == this) { if (showDisplayName) { diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 1ec6ea8f23..7c3c2dc1ca 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -20,6 +20,10 @@ #include "Model.h" #include "world.h" +GeometryCache::GeometryCache() : + _pendingBlenders(0) { +} + GeometryCache::~GeometryCache() { foreach (const VerticesIndices& vbo, _hemisphereVBOs) { glDeleteBuffers(1, &vbo.first); @@ -296,10 +300,30 @@ QSharedPointer GeometryCache::getGeometry(const QUrl& url, cons return getResource(url, fallback, delayLoad).staticCast(); } -void GeometryCache::setBlendedVertices(const QPointer& model, const QWeakPointer& geometry, - const QVector& vertices, const QVector& normals) { +void GeometryCache::noteRequiresBlend(Model* model) { + if (_pendingBlenders < QThread::idealThreadCount()) { + if (model->maybeStartBlender()) { + _pendingBlenders++; + } + return; + } + if (!_modelsRequiringBlends.contains(model)) { + _modelsRequiringBlends.append(model); + } +} + +void GeometryCache::setBlendedVertices(const QPointer& model, int blendNumber, + const QWeakPointer& geometry, const QVector& vertices, const QVector& normals) { if (!model.isNull()) { - model->setBlendedVertices(geometry, vertices, normals); + model->setBlendedVertices(blendNumber, geometry, vertices, normals); + } + _pendingBlenders--; + while (!_modelsRequiringBlends.isEmpty()) { + Model* nextModel = _modelsRequiringBlends.takeFirst(); + if (nextModel && nextModel->maybeStartBlender()) { + _pendingBlenders++; + return; + } } } diff --git a/interface/src/renderer/GeometryCache.h b/interface/src/renderer/GeometryCache.h index 2e5725e1b1..647e0592fd 100644 --- a/interface/src/renderer/GeometryCache.h +++ b/interface/src/renderer/GeometryCache.h @@ -35,6 +35,7 @@ class GeometryCache : public ResourceCache { public: + GeometryCache(); virtual ~GeometryCache(); void renderHemisphere(int slices, int stacks); @@ -47,9 +48,12 @@ public: /// \param delayLoad if true, don't load the geometry immediately; wait until load is first requested QSharedPointer getGeometry(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false); + /// Adds the specified model to the list requiring vertex blends. + void noteRequiresBlend(Model* model); + public slots: - void setBlendedVertices(const QPointer& model, const QWeakPointer& geometry, + void setBlendedVertices(const QPointer& model, int blendNumber, const QWeakPointer& geometry, const QVector& vertices, const QVector& normals); protected: @@ -68,6 +72,9 @@ private: QHash _gridBuffers; QHash > _networkGeometry; + + QList > _modelsRequiringBlends; + int _pendingBlenders; }; /// Geometry loaded from the network. diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 7f83147bb8..19d711a69d 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -62,8 +62,8 @@ Model::Model(QObject* parent) : _lodDistance(0.0f), _pupilDilation(0.0f), _url("http://invalid.com"), - _blenderPending(false), - _blendRequired(false) { + _blendNumber(0), + _appliedBlendNumber(0) { // we may have been created in the network thread, but we live in the main thread moveToThread(Application::getInstance()->thread()); @@ -826,7 +826,7 @@ void Model::updateShapePositions() { class Blender : public QRunnable { public: - Blender(Model* model, const QWeakPointer& geometry, + Blender(Model* model, int blendNumber, const QWeakPointer& geometry, const QVector& meshes, const QVector& blendshapeCoefficients); virtual void run(); @@ -834,55 +834,55 @@ public: private: QPointer _model; + int _blendNumber; QWeakPointer _geometry; QVector _meshes; QVector _blendshapeCoefficients; }; -Blender::Blender(Model* model, const QWeakPointer& geometry, +Blender::Blender(Model* model, int blendNumber, const QWeakPointer& geometry, const QVector& meshes, const QVector& blendshapeCoefficients) : _model(model), + _blendNumber(blendNumber), _geometry(geometry), _meshes(meshes), _blendshapeCoefficients(blendshapeCoefficients) { } void Blender::run() { - // make sure the model still exists - if (_model.isNull()) { - return; - } QVector vertices, normals; - int offset = 0; - foreach (const FBXMesh& mesh, _meshes) { - if (mesh.blendshapes.isEmpty()) { - continue; - } - vertices += mesh.vertices; - normals += mesh.normals; - glm::vec3* meshVertices = vertices.data() + offset; - glm::vec3* meshNormals = normals.data() + offset; - offset += mesh.vertices.size(); - const float NORMAL_COEFFICIENT_SCALE = 0.01f; - for (int i = 0, n = qMin(_blendshapeCoefficients.size(), mesh.blendshapes.size()); i < n; i++) { - float vertexCoefficient = _blendshapeCoefficients.at(i); - if (vertexCoefficient < EPSILON) { + if (!_model.isNull()) { + int offset = 0; + foreach (const FBXMesh& mesh, _meshes) { + if (mesh.blendshapes.isEmpty()) { continue; } - float normalCoefficient = vertexCoefficient * NORMAL_COEFFICIENT_SCALE; - const FBXBlendshape& blendshape = mesh.blendshapes.at(i); - for (int j = 0; j < blendshape.indices.size(); j++) { - int index = blendshape.indices.at(j); - meshVertices[index] += blendshape.vertices.at(j) * vertexCoefficient; - meshNormals[index] += blendshape.normals.at(j) * normalCoefficient; + vertices += mesh.vertices; + normals += mesh.normals; + glm::vec3* meshVertices = vertices.data() + offset; + glm::vec3* meshNormals = normals.data() + offset; + offset += mesh.vertices.size(); + const float NORMAL_COEFFICIENT_SCALE = 0.01f; + for (int i = 0, n = qMin(_blendshapeCoefficients.size(), mesh.blendshapes.size()); i < n; i++) { + float vertexCoefficient = _blendshapeCoefficients.at(i); + if (vertexCoefficient < EPSILON) { + continue; + } + float normalCoefficient = vertexCoefficient * NORMAL_COEFFICIENT_SCALE; + const FBXBlendshape& blendshape = mesh.blendshapes.at(i); + for (int j = 0; j < blendshape.indices.size(); j++) { + int index = blendshape.indices.at(j); + meshVertices[index] += blendshape.vertices.at(j) * vertexCoefficient; + meshNormals[index] += blendshape.normals.at(j) * normalCoefficient; + } } } } - // post the result to the geometry cache, which will dispatch to the model if still alive QMetaObject::invokeMethod(Application::getInstance()->getGeometryCache(), "setBlendedVertices", - Q_ARG(const QPointer&, _model), Q_ARG(const QWeakPointer&, _geometry), - Q_ARG(const QVector&, vertices), Q_ARG(const QVector&, normals)); + Q_ARG(const QPointer&, _model), Q_ARG(int, _blendNumber), + Q_ARG(const QWeakPointer&, _geometry), Q_ARG(const QVector&, vertices), + Q_ARG(const QVector&, normals)); } void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions) { @@ -1020,14 +1020,9 @@ void Model::simulateInternal(float deltaTime) { } // post the blender if we're not currently waiting for one to finish - if (geometry.hasBlendedMeshes()) { - if (_blenderPending) { - _blendRequired = true; - } else { - _blendRequired = false; - _blenderPending = true; - QThreadPool::globalInstance()->start(new Blender(this, _geometry, geometry.meshes, _blendshapeCoefficients)); - } + if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + _blendedBlendshapeCoefficients = _blendshapeCoefficients; + Application::getInstance()->getGeometryCache()->noteRequiresBlend(this); } } @@ -1290,22 +1285,23 @@ void Model::renderJointCollisionShapes(float alpha) { // implement this when we have shapes for regular models } -void Model::setBlendedVertices(const QWeakPointer& geometry, const QVector& vertices, - const QVector& normals) { - _blenderPending = false; - - // start the next blender if required +bool Model::maybeStartBlender() { const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry(); - if (_blendRequired) { - _blendRequired = false; - if (fbxGeometry.hasBlendedMeshes()) { - _blenderPending = true; - QThreadPool::globalInstance()->start(new Blender(this, _geometry, fbxGeometry.meshes, _blendshapeCoefficients)); - } + if (fbxGeometry.hasBlendedMeshes()) { + QThreadPool::globalInstance()->start(new Blender(this, ++_blendNumber, _geometry, + fbxGeometry.meshes, _blendshapeCoefficients)); + return true; } - if (_geometry != geometry || _blendedVertexBuffers.isEmpty()) { + return false; +} + +void Model::setBlendedVertices(int blendNumber, const QWeakPointer& geometry, + const QVector& vertices, const QVector& normals) { + if (_geometry != geometry || _blendedVertexBuffers.isEmpty() || blendNumber < _appliedBlendNumber) { return; } + _appliedBlendNumber = blendNumber; + const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry(); int index = 0; for (int i = 0; i < fbxGeometry.meshes.size(); i++) { const FBXMesh& mesh = fbxGeometry.meshes.at(i); @@ -1358,6 +1354,8 @@ void Model::deleteGeometry() { if (_geometry) { _geometry->clearLoadPriority(this); } + + _blendedBlendshapeCoefficients.clear(); } void Model::renderMeshes(RenderMode mode, bool translucent, bool receiveShadows) { diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index f2d98ac589..63b2058a20 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -165,9 +165,11 @@ public: virtual void renderJointCollisionShapes(float alpha); + bool maybeStartBlender(); + /// Sets blended vertices computed in a separate thread. - void setBlendedVertices(const QWeakPointer& geometry, const QVector& vertices, - const QVector& normals); + void setBlendedVertices(int blendNumber, const QWeakPointer& geometry, + const QVector& vertices, const QVector& normals); class LocalLight { public: @@ -285,8 +287,9 @@ private: glm::vec4 _localLightColors[MAX_LOCAL_LIGHTS]; glm::vec4 _localLightDirections[MAX_LOCAL_LIGHTS]; - bool _blenderPending; - bool _blendRequired; + QVector _blendedBlendshapeCoefficients; + int _blendNumber; + int _appliedBlendNumber; static ProgramObject _program; static ProgramObject _normalMapProgram; diff --git a/interface/src/ui/DataWebDialog.cpp b/interface/src/ui/DataWebDialog.cpp new file mode 100644 index 0000000000..9914383220 --- /dev/null +++ b/interface/src/ui/DataWebDialog.cpp @@ -0,0 +1,39 @@ +// +// DataWebDialog.cpp +// interface/src/ui +// +// Created by Stephen Birarda on 2014-09-17. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include +#include +#include + +#include "DataWebDialog.h" + +DataWebDialog::DataWebDialog() { + // make sure the dialog deletes itself when it closes + setAttribute(Qt::WA_DeleteOnClose); + + // use an OAuthNetworkAccessManager instead of regular QNetworkAccessManager so our requests are authed + page()->setNetworkAccessManager(OAuthNetworkAccessManager::getInstance()); +} + +DataWebDialog* DataWebDialog::dialogForPath(const QString& path) { + DataWebDialog* dialogWebView = new DataWebDialog(); + + QUrl dataWebUrl(DEFAULT_NODE_AUTH_URL); + dataWebUrl.setPath(path); + + qDebug() << "Opening a data web dialog for" << dataWebUrl.toString(); + + dialogWebView->load(dataWebUrl); + + return dialogWebView; +} \ No newline at end of file diff --git a/interface/src/ui/DataWebDialog.h b/interface/src/ui/DataWebDialog.h new file mode 100644 index 0000000000..c69a9207b9 --- /dev/null +++ b/interface/src/ui/DataWebDialog.h @@ -0,0 +1,25 @@ +// +// DataWebDialog.h +// interface/src/ui +// +// Created by Stephen Birarda on 2014-09-17. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_DataWebDialog_h +#define hifi_DataWebDialog_h + +#include +#include + +class DataWebDialog : public QWebView { + Q_OBJECT +public: + DataWebDialog(); + static DataWebDialog* dialogForPath(const QString& path); +}; + +#endif // hifi_WebkitDialog_h \ No newline at end of file diff --git a/interface/src/ui/UserLocationsDialog.cpp b/interface/src/ui/UserLocationsDialog.cpp deleted file mode 100644 index 31f388d045..0000000000 --- a/interface/src/ui/UserLocationsDialog.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// -// UserLocationsDialog.cpp -// interface/src/ui -// -// Created by Ryan Huffman on 06/24/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include - -#include - -#include "Menu.h" -#include "UserLocationsDialog.h" - -UserLocationsDialog::UserLocationsDialog(QWidget* parent) : - QDialog(parent), - _ui(), - _proxyModel(this), - _userLocationsModel(this) { - - _ui.setupUi(this); - - _proxyModel.setSourceModel(&_userLocationsModel); - _proxyModel.setDynamicSortFilter(true); - - _ui.locationsTreeView->setModel(&_proxyModel); - _ui.locationsTreeView->setSortingEnabled(true); - _ui.locationsTreeView->sortByColumn(UserLocationsModel::NameColumn, Qt::AscendingOrder); - - connect(_ui.locationsTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, - this, &UserLocationsDialog::updateEnabled); - connect(&_userLocationsModel, &UserLocationsModel::modelReset, this, &UserLocationsDialog::updateEnabled); - connect(&_userLocationsModel, &UserLocationsModel::modelReset, &_proxyModel, &QSortFilterProxyModel::invalidate); - connect(_ui.locationsTreeView, &QTreeView::doubleClicked, this, &UserLocationsDialog::goToModelIndex); - - connect(_ui.deleteButton, &QPushButton::clicked, this, &UserLocationsDialog::deleteSelection); - connect(_ui.renameButton, &QPushButton::clicked, this, &UserLocationsDialog::renameSelection); - connect(_ui.refreshButton, &QPushButton::clicked, &_userLocationsModel, &UserLocationsModel::refresh); - - this->setWindowTitle("My Locations"); -} - -void UserLocationsDialog::updateEnabled() { - bool enabled = _ui.locationsTreeView->selectionModel()->hasSelection(); - _ui.renameButton->setEnabled(enabled); - _ui.deleteButton->setEnabled(enabled); -} - -void UserLocationsDialog::goToModelIndex(const QModelIndex& index) { - QVariant address = _proxyModel.data(index.sibling(index.row(), UserLocationsModel::AddressColumn)); - AddressManager::getInstance().handleLookupString(address.toString()); -} - -void UserLocationsDialog::deleteSelection() { - QModelIndex selection = _ui.locationsTreeView->selectionModel()->currentIndex(); - selection = _proxyModel.mapToSource(selection); - if (selection.isValid()) { - _userLocationsModel.deleteLocation(selection); - } -} - -void UserLocationsDialog::renameSelection() { - QModelIndex selection = _ui.locationsTreeView->selectionModel()->currentIndex(); - selection = _proxyModel.mapToSource(selection); - if (selection.isValid()) { - bool ok; - QString name = _userLocationsModel.data(selection.sibling(selection.row(), UserLocationsModel::NameColumn)).toString(); - QString newName = QInputDialog::getText(this, "Rename '" + name + "'", "Set name to:", QLineEdit::Normal, name, &ok); - if (ok && !newName.isEmpty()) { - _userLocationsModel.renameLocation(selection, newName); - } - } -} diff --git a/interface/src/ui/UserLocationsDialog.h b/interface/src/ui/UserLocationsDialog.h deleted file mode 100644 index 0e596ece87..0000000000 --- a/interface/src/ui/UserLocationsDialog.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// UserLocationsDialog.h -// interface/src/ui -// -// Created by Ryan Huffman on 06/24/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_UserLocationsDialog_h -#define hifi_UserLocationsDialog_h - -#include "ui_userLocationsDialog.h" -#include "UserLocationsModel.h" - -class UserLocationsDialog : public QDialog { - Q_OBJECT -public: - UserLocationsDialog(QWidget* parent = NULL); - -protected slots: - void updateEnabled(); - void goToModelIndex(const QModelIndex& index); - void deleteSelection(); - void renameSelection(); - -private: - Ui::UserLocationsDialog _ui; - QSortFilterProxyModel _proxyModel; - UserLocationsModel _userLocationsModel; -}; - -#endif // hifi_UserLocationsDialog_h diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 1100371ac9..88e4bad7b2 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -188,7 +188,8 @@ void AccountManager::invokedRequest(const QString& path, if (requiresAuthentication) { if (hasValidAccessToken()) { - requestURL.setQuery("access_token=" + _accountInfo.getAccessToken().token); + networkRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER, + _accountInfo.getAccessToken().authorizationHeaderValue()); } else { qDebug() << "No valid access token present. Bailing on authenticated invoked request."; return; @@ -405,9 +406,11 @@ void AccountManager::requestProfile() { QUrl profileURL = _authURL; profileURL.setPath("/api/v1/users/profile"); - profileURL.setQuery("access_token=" + _accountInfo.getAccessToken().token); + + QNetworkRequest profileRequest(profileURL); + profileRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER, _accountInfo.getAccessToken().authorizationHeaderValue()); - QNetworkReply* profileReply = networkAccessManager.get(QNetworkRequest(profileURL)); + QNetworkReply* profileReply = networkAccessManager.get(profileRequest); connect(profileReply, &QNetworkReply::finished, this, &AccountManager::requestProfileFinished); connect(profileReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestProfileError(QNetworkReply::NetworkError))); } diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index 64d62cd1c2..edccab0b75 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -37,6 +37,8 @@ public: QString updateSlot; }; +const QByteArray ACCESS_TOKEN_AUTHORIZATION_HEADER = "Authorization"; + class AccountManager : public QObject { Q_OBJECT public: diff --git a/libraries/networking/src/OAuthAccessToken.h b/libraries/networking/src/OAuthAccessToken.h index 36859b79f8..167bb824da 100644 --- a/libraries/networking/src/OAuthAccessToken.h +++ b/libraries/networking/src/OAuthAccessToken.h @@ -23,6 +23,8 @@ public: OAuthAccessToken(const QJsonObject& jsonObject); OAuthAccessToken(const OAuthAccessToken& otherToken); OAuthAccessToken& operator=(const OAuthAccessToken& otherToken); + + QByteArray authorizationHeaderValue() const { return QString("Bearer %1").arg(token).toUtf8(); } bool isExpired() const { return expiryTimestamp <= QDateTime::currentMSecsSinceEpoch(); } diff --git a/libraries/networking/src/OAuthNetworkAccessManager.cpp b/libraries/networking/src/OAuthNetworkAccessManager.cpp new file mode 100644 index 0000000000..493398230a --- /dev/null +++ b/libraries/networking/src/OAuthNetworkAccessManager.cpp @@ -0,0 +1,43 @@ +// +// OAuthNetworkAccessManager.cpp +// libraries/networking/src +// +// Created by Stephen Birarda on 2014-09-18. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include + +#include "AccountManager.h" + +#include "OAuthNetworkAccessManager.h" + +QThreadStorage oauthNetworkAccessManagers; + +OAuthNetworkAccessManager* OAuthNetworkAccessManager::getInstance() { + if (!oauthNetworkAccessManagers.hasLocalData()) { + oauthNetworkAccessManagers.setLocalData(new OAuthNetworkAccessManager()); + } + + return oauthNetworkAccessManagers.localData(); +} + +QNetworkReply* OAuthNetworkAccessManager::createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest& req, + QIODevice* outgoingData) { + AccountManager& accountManager = AccountManager::getInstance(); + + if (accountManager.hasValidAccessToken()) { + QNetworkRequest authenticatedRequest(req); + authenticatedRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER, + accountManager.getAccountInfo().getAccessToken().authorizationHeaderValue()); + + return QNetworkAccessManager::createRequest(op, authenticatedRequest, outgoingData); + } else { + return QNetworkAccessManager::createRequest(op, req, outgoingData); + } +} diff --git a/libraries/networking/src/OAuthNetworkAccessManager.h b/libraries/networking/src/OAuthNetworkAccessManager.h new file mode 100644 index 0000000000..acfd52e18f --- /dev/null +++ b/libraries/networking/src/OAuthNetworkAccessManager.h @@ -0,0 +1,24 @@ +// +// OAuthNetworkAccessManager.h +// libraries/networking/src +// +// Created by Stephen Birarda on 2014-09-18. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_OAuthNetworkAccessManager_h +#define hifi_OAuthNetworkAccessManager_h + +#include + +class OAuthNetworkAccessManager : public QNetworkAccessManager { +public: + static OAuthNetworkAccessManager* getInstance(); +protected: + virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& req, QIODevice* outgoingData = 0); +}; + +#endif // hifi_OAuthNetworkAccessManager_h \ No newline at end of file