mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 18:23:54 +02:00
merge upstream/master into andrew/ragdoll
This commit is contained in:
commit
2f3eba2896
19 changed files with 251 additions and 516 deletions
|
@ -68,6 +68,7 @@
|
|||
#include <UUID.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "ui/DataWebDialog.h"
|
||||
#include "InterfaceVersion.h"
|
||||
#include "Menu.h"
|
||||
#include "ModelUploader.h"
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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<DataWebDialog> _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";
|
||||
|
|
|
@ -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 <QDebug>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QMessageBox>
|
||||
|
||||
#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<UserLocation*>::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);
|
||||
}
|
|
@ -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 <QAbstractListModel>
|
||||
#include <QModelIndex>
|
||||
#include <QVariant>
|
||||
|
||||
|
||||
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<UserLocation*> _locations;
|
||||
};
|
||||
|
||||
#endif // hifi_UserLocationsModel_h
|
|
@ -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) {
|
||||
|
|
|
@ -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<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, cons
|
|||
return getResource(url, fallback, delayLoad).staticCast<NetworkGeometry>();
|
||||
}
|
||||
|
||||
void GeometryCache::setBlendedVertices(const QPointer<Model>& model, const QWeakPointer<NetworkGeometry>& geometry,
|
||||
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& 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>& model, int blendNumber,
|
||||
const QWeakPointer<NetworkGeometry>& geometry, const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<NetworkGeometry> 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>& model, const QWeakPointer<NetworkGeometry>& geometry,
|
||||
void setBlendedVertices(const QPointer<Model>& model, int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
|
||||
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
|
||||
|
||||
protected:
|
||||
|
@ -68,6 +72,9 @@ private:
|
|||
QHash<IntPair, QOpenGLBuffer> _gridBuffers;
|
||||
|
||||
QHash<QUrl, QWeakPointer<NetworkGeometry> > _networkGeometry;
|
||||
|
||||
QList<QPointer<Model> > _modelsRequiringBlends;
|
||||
int _pendingBlenders;
|
||||
};
|
||||
|
||||
/// Geometry loaded from the network.
|
||||
|
|
|
@ -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<NetworkGeometry>& geometry,
|
||||
Blender(Model* model, int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
|
||||
const QVector<FBXMesh>& meshes, const QVector<float>& blendshapeCoefficients);
|
||||
|
||||
virtual void run();
|
||||
|
@ -834,55 +834,55 @@ public:
|
|||
private:
|
||||
|
||||
QPointer<Model> _model;
|
||||
int _blendNumber;
|
||||
QWeakPointer<NetworkGeometry> _geometry;
|
||||
QVector<FBXMesh> _meshes;
|
||||
QVector<float> _blendshapeCoefficients;
|
||||
};
|
||||
|
||||
Blender::Blender(Model* model, const QWeakPointer<NetworkGeometry>& geometry,
|
||||
Blender::Blender(Model* model, int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
|
||||
const QVector<FBXMesh>& meshes, const QVector<float>& 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<glm::vec3> 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>&, _model), Q_ARG(const QWeakPointer<NetworkGeometry>&, _geometry),
|
||||
Q_ARG(const QVector<glm::vec3>&, vertices), Q_ARG(const QVector<glm::vec3>&, normals));
|
||||
Q_ARG(const QPointer<Model>&, _model), Q_ARG(int, _blendNumber),
|
||||
Q_ARG(const QWeakPointer<NetworkGeometry>&, _geometry), Q_ARG(const QVector<glm::vec3>&, vertices),
|
||||
Q_ARG(const QVector<glm::vec3>&, 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<NetworkGeometry>& geometry, const QVector<glm::vec3>& vertices,
|
||||
const QVector<glm::vec3>& 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<NetworkGeometry>& geometry,
|
||||
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& 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) {
|
||||
|
|
|
@ -165,9 +165,11 @@ public:
|
|||
|
||||
virtual void renderJointCollisionShapes(float alpha);
|
||||
|
||||
bool maybeStartBlender();
|
||||
|
||||
/// Sets blended vertices computed in a separate thread.
|
||||
void setBlendedVertices(const QWeakPointer<NetworkGeometry>& geometry, const QVector<glm::vec3>& vertices,
|
||||
const QVector<glm::vec3>& normals);
|
||||
void setBlendedVertices(int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
|
||||
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& 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<float> _blendedBlendshapeCoefficients;
|
||||
int _blendNumber;
|
||||
int _appliedBlendNumber;
|
||||
|
||||
static ProgramObject _program;
|
||||
static ProgramObject _normalMapProgram;
|
||||
|
|
39
interface/src/ui/DataWebDialog.cpp
Normal file
39
interface/src/ui/DataWebDialog.cpp
Normal file
|
@ -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 <qwebview.h>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <LimitedNodeList.h>
|
||||
#include <OAuthNetworkAccessManager.h>
|
||||
|
||||
#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;
|
||||
}
|
25
interface/src/ui/DataWebDialog.h
Normal file
25
interface/src/ui/DataWebDialog.h
Normal file
|
@ -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 <qobject.h>
|
||||
#include <qwebview.h>
|
||||
|
||||
class DataWebDialog : public QWebView {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DataWebDialog();
|
||||
static DataWebDialog* dialogForPath(const QString& path);
|
||||
};
|
||||
|
||||
#endif // hifi_WebkitDialog_h
|
|
@ -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 <QDebug>
|
||||
#include <QInputDialog>
|
||||
#include <QPushButton>
|
||||
|
||||
#include <AddressManager.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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)));
|
||||
}
|
||||
|
|
|
@ -37,6 +37,8 @@ public:
|
|||
QString updateSlot;
|
||||
};
|
||||
|
||||
const QByteArray ACCESS_TOKEN_AUTHORIZATION_HEADER = "Authorization";
|
||||
|
||||
class AccountManager : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
|
|
@ -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(); }
|
||||
|
||||
|
|
43
libraries/networking/src/OAuthNetworkAccessManager.cpp
Normal file
43
libraries/networking/src/OAuthNetworkAccessManager.cpp
Normal file
|
@ -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 <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QThreadStorage>
|
||||
|
||||
#include "AccountManager.h"
|
||||
|
||||
#include "OAuthNetworkAccessManager.h"
|
||||
|
||||
QThreadStorage<OAuthNetworkAccessManager*> 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);
|
||||
}
|
||||
}
|
24
libraries/networking/src/OAuthNetworkAccessManager.h
Normal file
24
libraries/networking/src/OAuthNetworkAccessManager.h
Normal file
|
@ -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 <QNetworkAccessManager>
|
||||
|
||||
class OAuthNetworkAccessManager : public QNetworkAccessManager {
|
||||
public:
|
||||
static OAuthNetworkAccessManager* getInstance();
|
||||
protected:
|
||||
virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& req, QIODevice* outgoingData = 0);
|
||||
};
|
||||
|
||||
#endif // hifi_OAuthNetworkAccessManager_h
|
Loading…
Reference in a new issue