mirror of
https://github.com/lubosz/overte.git
synced 2025-08-07 20:31:29 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into replace_qnetworkaccessmanager
This commit is contained in:
commit
106e5b578b
20 changed files with 932 additions and 60 deletions
|
@ -12,6 +12,9 @@ set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
|
||||||
# setup for find modules
|
# setup for find modules
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/")
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/")
|
||||||
|
|
||||||
|
find_package(Qt5 COMPONENTS Script)
|
||||||
|
include_directories(SYSTEM "${Qt5Script_INCLUDE_DIRS}")
|
||||||
|
|
||||||
# set up the external glm library
|
# set up the external glm library
|
||||||
include("${MACRO_DIR}/IncludeGLM.cmake")
|
include("${MACRO_DIR}/IncludeGLM.cmake")
|
||||||
include_glm(${TARGET_NAME} "${ROOT_DIR}")
|
include_glm(${TARGET_NAME} "${ROOT_DIR}")
|
||||||
|
@ -35,4 +38,4 @@ link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
|
||||||
# add a definition for ssize_t so that windows doesn't bail
|
# add a definition for ssize_t so that windows doesn't bail
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
add_definitions(-Dssize_t=long)
|
add_definitions(-Dssize_t=long)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
|
@ -1379,8 +1379,10 @@ bool Audio::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo) {
|
||||||
|
|
||||||
// cleanup any previously initialized device
|
// cleanup any previously initialized device
|
||||||
if (_audioInput) {
|
if (_audioInput) {
|
||||||
|
// The call to stop() causes _inputDevice to be destructed.
|
||||||
|
// That in turn causes it to be disconnected (see for example
|
||||||
|
// http://stackoverflow.com/questions/9264750/qt-signals-and-slots-object-disconnect).
|
||||||
_audioInput->stop();
|
_audioInput->stop();
|
||||||
disconnect(_inputDevice);
|
|
||||||
_inputDevice = NULL;
|
_inputDevice = NULL;
|
||||||
|
|
||||||
delete _audioInput;
|
delete _audioInput;
|
||||||
|
|
|
@ -91,6 +91,7 @@ Menu::Menu() :
|
||||||
_jsConsole(NULL),
|
_jsConsole(NULL),
|
||||||
_octreeStatsDialog(NULL),
|
_octreeStatsDialog(NULL),
|
||||||
_lodToolsDialog(NULL),
|
_lodToolsDialog(NULL),
|
||||||
|
_userLocationsDialog(NULL),
|
||||||
_maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM),
|
_maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM),
|
||||||
_voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE),
|
_voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE),
|
||||||
_oculusUIAngularSize(DEFAULT_OCULUS_UI_ANGULAR_SIZE),
|
_oculusUIAngularSize(DEFAULT_OCULUS_UI_ANGULAR_SIZE),
|
||||||
|
@ -166,6 +167,11 @@ Menu::Menu() :
|
||||||
Qt::CTRL | Qt::Key_N,
|
Qt::CTRL | Qt::Key_N,
|
||||||
this,
|
this,
|
||||||
SLOT(nameLocation()));
|
SLOT(nameLocation()));
|
||||||
|
addActionToQMenuAndActionHash(fileMenu,
|
||||||
|
MenuOption::MyLocations,
|
||||||
|
Qt::CTRL | Qt::Key_K,
|
||||||
|
this,
|
||||||
|
SLOT(toggleLocationList()));
|
||||||
addActionToQMenuAndActionHash(fileMenu,
|
addActionToQMenuAndActionHash(fileMenu,
|
||||||
MenuOption::GoTo,
|
MenuOption::GoTo,
|
||||||
Qt::Key_At,
|
Qt::Key_At,
|
||||||
|
@ -1184,6 +1190,17 @@ void Menu::namedLocationCreated(LocationManager::NamedLocationCreateResponse res
|
||||||
msgBox.exec();
|
msgBox.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Menu::toggleLocationList() {
|
||||||
|
if (!_userLocationsDialog) {
|
||||||
|
_userLocationsDialog = new UserLocationsDialog(Application::getInstance()->getWindow());
|
||||||
|
}
|
||||||
|
if (_userLocationsDialog->isVisible()) {
|
||||||
|
_userLocationsDialog->hide();
|
||||||
|
} else {
|
||||||
|
_userLocationsDialog->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Menu::nameLocation() {
|
void Menu::nameLocation() {
|
||||||
// check if user is logged in or show login dialog if not
|
// check if user is logged in or show login dialog if not
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "ui/JSConsole.h"
|
#include "ui/JSConsole.h"
|
||||||
#include "ui/LoginDialog.h"
|
#include "ui/LoginDialog.h"
|
||||||
#include "ui/ScriptEditorWindow.h"
|
#include "ui/ScriptEditorWindow.h"
|
||||||
|
#include "ui/UserLocationsDialog.h"
|
||||||
|
|
||||||
const float ADJUST_LOD_DOWN_FPS = 40.0;
|
const float ADJUST_LOD_DOWN_FPS = 40.0;
|
||||||
const float ADJUST_LOD_UP_FPS = 55.0;
|
const float ADJUST_LOD_UP_FPS = 55.0;
|
||||||
|
@ -199,6 +200,7 @@ private slots:
|
||||||
void goToDomainDialog();
|
void goToDomainDialog();
|
||||||
void goToLocation();
|
void goToLocation();
|
||||||
void nameLocation();
|
void nameLocation();
|
||||||
|
void toggleLocationList();
|
||||||
void bandwidthDetailsClosed();
|
void bandwidthDetailsClosed();
|
||||||
void octreeStatsDetailsClosed();
|
void octreeStatsDetailsClosed();
|
||||||
void lodToolsClosed();
|
void lodToolsClosed();
|
||||||
|
@ -265,6 +267,7 @@ private:
|
||||||
QDialog* _jsConsole;
|
QDialog* _jsConsole;
|
||||||
OctreeStatsDialog* _octreeStatsDialog;
|
OctreeStatsDialog* _octreeStatsDialog;
|
||||||
LodToolsDialog* _lodToolsDialog;
|
LodToolsDialog* _lodToolsDialog;
|
||||||
|
UserLocationsDialog* _userLocationsDialog;
|
||||||
int _maxVoxels;
|
int _maxVoxels;
|
||||||
float _voxelSizeScale;
|
float _voxelSizeScale;
|
||||||
float _oculusUIAngularSize;
|
float _oculusUIAngularSize;
|
||||||
|
@ -395,6 +398,7 @@ namespace MenuOption {
|
||||||
const QString MoveWithLean = "Move with Lean";
|
const QString MoveWithLean = "Move with Lean";
|
||||||
const QString MuteAudio = "Mute Microphone";
|
const QString MuteAudio = "Mute Microphone";
|
||||||
const QString MuteEnvironment = "Mute Environment";
|
const QString MuteEnvironment = "Mute Environment";
|
||||||
|
const QString MyLocations = "My Locations...";
|
||||||
const QString NameLocation = "Name this location";
|
const QString NameLocation = "Name this location";
|
||||||
const QString NewVoxelCullingMode = "New Voxel Culling Mode";
|
const QString NewVoxelCullingMode = "New Voxel Culling Mode";
|
||||||
const QString OctreeStats = "Voxel and Particle Statistics";
|
const QString OctreeStats = "Voxel and Particle Statistics";
|
||||||
|
|
246
interface/src/UserLocationsModel.cpp
Normal file
246
interface/src/UserLocationsModel.cpp
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
//
|
||||||
|
// 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 PLACES_GET = "/api/v1/places";
|
||||||
|
static const QString PLACES_UPDATE = "/api/v1/places/%1";
|
||||||
|
static const QString PLACES_DELETE= "/api/v1/places/%1";
|
||||||
|
|
||||||
|
UserLocation::UserLocation(QString id, QString name, QString location) :
|
||||||
|
_id(id),
|
||||||
|
_name(name),
|
||||||
|
_location(location),
|
||||||
|
_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", QJsonValue(newName));
|
||||||
|
QJsonDocument jsonDocument(jsonNameObject);
|
||||||
|
AccountManager::getInstance().authenticatedRequest(PLACES_UPDATE.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") {
|
||||||
|
QString updatedName = responseData["data"].toObject()["name"].toString();
|
||||||
|
_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::NetworkError error, const QString& errorString) {
|
||||||
|
_updating = false;
|
||||||
|
|
||||||
|
QString msg = "There was an error renaming location '" + _name + "': " + 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(PLACES_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::NetworkError error, const QString& errorString) {
|
||||||
|
_updating = false;
|
||||||
|
|
||||||
|
QString msg = "There was an error deleting location '" + _name + "': " + 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(PLACES_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()["places"].toArray();
|
||||||
|
for (QJsonArray::const_iterator it = locations.constBegin(); it != locations.constEnd(); it++) {
|
||||||
|
QJsonObject location = (*it).toObject();
|
||||||
|
QJsonObject address = location["address"].toObject();
|
||||||
|
UserLocation* userLocation = new UserLocation(location["id"].toString(), location["name"].toString(),
|
||||||
|
"hifi://" + address["domain"].toString()
|
||||||
|
+ "/" + address["position"].toString()
|
||||||
|
+ "/" + address["orientation"].toString());
|
||||||
|
_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() == LocationColumn) {
|
||||||
|
return QVariant(_locations[index.row()]->location());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 LocationColumn: return "Location";
|
||||||
|
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);
|
||||||
|
}
|
82
interface/src/UserLocationsModel.h
Normal file
82
interface/src/UserLocationsModel.h
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
//
|
||||||
|
// 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(QString id, QString name, QString location);
|
||||||
|
bool isUpdating() { return _updating; }
|
||||||
|
void requestRename(const QString& newName);
|
||||||
|
void requestDelete();
|
||||||
|
|
||||||
|
QString id() { return _id; }
|
||||||
|
QString name() { return _name; }
|
||||||
|
QString location() { return _location; }
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void handleRenameResponse(const QJsonObject& responseData);
|
||||||
|
void handleRenameError(QNetworkReply::NetworkError error, const QString& errorString);
|
||||||
|
void handleDeleteResponse(const QJsonObject& responseData);
|
||||||
|
void handleDeleteError(QNetworkReply::NetworkError error, const QString& errorString);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void updated(const QString& name);
|
||||||
|
void deleted(const QString& name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString _id;
|
||||||
|
QString _name;
|
||||||
|
QString _location;
|
||||||
|
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,
|
||||||
|
LocationColumn
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
|
@ -51,10 +51,10 @@ void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBX
|
||||||
glm::mat3 axes = glm::mat3_cast(glm::quat());
|
glm::mat3 axes = glm::mat3_cast(glm::quat());
|
||||||
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(state.getDefaultTranslationInParentFrame()) *
|
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(state.getDefaultTranslationInParentFrame()) *
|
||||||
joint.preTransform * glm::mat4_cast(joint.preRotation)));
|
joint.preTransform * glm::mat4_cast(joint.preRotation)));
|
||||||
state._rotationInParentFrame = glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalRoll(), glm::normalize(inverse * axes[2]))
|
state.setRotationInParentFrame(glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalRoll(), glm::normalize(inverse * axes[2]))
|
||||||
* glm::angleAxis(RADIANS_PER_DEGREE * _owningHead->getFinalYaw(), glm::normalize(inverse * axes[1]))
|
* glm::angleAxis(RADIANS_PER_DEGREE * _owningHead->getFinalYaw(), glm::normalize(inverse * axes[1]))
|
||||||
* glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalPitch(), glm::normalize(inverse * axes[0]))
|
* glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalPitch(), glm::normalize(inverse * axes[0]))
|
||||||
* joint.rotation;
|
* joint.rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||||
|
@ -68,8 +68,8 @@ void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJ
|
||||||
_owningHead->getSaccade() - _translation, 1.0f));
|
_owningHead->getSaccade() - _translation, 1.0f));
|
||||||
glm::quat between = rotationBetween(front, lookAt);
|
glm::quat between = rotationBetween(front, lookAt);
|
||||||
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
|
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
|
||||||
state._rotationInParentFrame = glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) *
|
state.setRotationInParentFrame(glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) *
|
||||||
joint.rotation;
|
joint.rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FaceModel::updateJointState(int index) {
|
void FaceModel::updateJointState(int index) {
|
||||||
|
|
|
@ -219,10 +219,9 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
|
||||||
JointState& parentState = _jointStates[parentJointIndex];
|
JointState& parentState = _jointStates[parentJointIndex];
|
||||||
parentState.setRotationFromBindFrame(palmRotation, PALM_PRIORITY);
|
parentState.setRotationFromBindFrame(palmRotation, PALM_PRIORITY);
|
||||||
// lock hand to forearm by slamming its rotation (in parent-frame) to identity
|
// lock hand to forearm by slamming its rotation (in parent-frame) to identity
|
||||||
_jointStates[jointIndex]._rotationInParentFrame = glm::quat();
|
_jointStates[jointIndex].setRotationInParentFrame(glm::quat());
|
||||||
} else {
|
} else {
|
||||||
setJointPosition(jointIndex, palmPosition, palmRotation,
|
inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY);
|
||||||
true, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,9 +257,9 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const
|
||||||
glm::mat3 axes = glm::mat3_cast(glm::quat());
|
glm::mat3 axes = glm::mat3_cast(glm::quat());
|
||||||
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(state.getDefaultTranslationInParentFrame()) *
|
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(state.getDefaultTranslationInParentFrame()) *
|
||||||
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)));
|
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)));
|
||||||
state._rotationInParentFrame = glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(),
|
state.setRotationInParentFrame(glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(),
|
||||||
glm::normalize(inverse * axes[2])) * glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(),
|
glm::normalize(inverse * axes[2])) * glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(),
|
||||||
glm::normalize(inverse * axes[0])) * joint.rotation;
|
glm::normalize(inverse * axes[0])) * joint.rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
void SkeletonModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||||
|
@ -537,6 +536,11 @@ void SkeletonModel::buildRagdollConstraints() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SkeletonModel::updateVisibleJointStates() {
|
||||||
|
Model::updateVisibleJointStates();
|
||||||
|
// TODO: implement this to move visible joints to agree with joint shape positions
|
||||||
|
}
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
void SkeletonModel::stepRagdollForward(float deltaTime) {
|
void SkeletonModel::stepRagdollForward(float deltaTime) {
|
||||||
const float RAGDOLL_FOLLOWS_JOINTS_TIMESCALE = 0.03f;
|
const float RAGDOLL_FOLLOWS_JOINTS_TIMESCALE = 0.03f;
|
||||||
|
@ -644,7 +648,6 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
|
||||||
_ragdollPoints[i]._lastPosition = _ragdollPoints[i]._position;
|
_ragdollPoints[i]._lastPosition = _ragdollPoints[i]._position;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
assert(parentIndex != -1);
|
|
||||||
|
|
||||||
glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation;
|
glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation;
|
||||||
transforms[i] = transforms[parentIndex] * glm::translate(joint.translation)
|
transforms[i] = transforms[parentIndex] * glm::translate(joint.translation)
|
||||||
|
@ -700,7 +703,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
|
||||||
_boundingRadius = 0.5f * glm::length(diagonal);
|
_boundingRadius = 0.5f * glm::length(diagonal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkeletonModel::resetShapePositions() {
|
void SkeletonModel::resetShapePositionsToDefaultPose() {
|
||||||
// DEBUG method.
|
// DEBUG method.
|
||||||
// Moves shapes to the joint default locations for debug visibility into
|
// Moves shapes to the joint default locations for debug visibility into
|
||||||
// how the bounding shape is computed.
|
// how the bounding shape is computed.
|
||||||
|
|
|
@ -93,6 +93,8 @@ public:
|
||||||
/// Retrieve the positions of up to two eye meshes.
|
/// Retrieve the positions of up to two eye meshes.
|
||||||
/// \return whether or not both eye meshes were found
|
/// \return whether or not both eye meshes were found
|
||||||
bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
|
bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
|
||||||
|
|
||||||
|
virtual void updateVisibleJointStates();
|
||||||
|
|
||||||
// virtual overrride from Ragdoll
|
// virtual overrride from Ragdoll
|
||||||
virtual void stepRagdollForward(float deltaTime);
|
virtual void stepRagdollForward(float deltaTime);
|
||||||
|
@ -104,7 +106,7 @@ public:
|
||||||
float getBoundingShapeRadius() const { return _boundingShape.getRadius(); }
|
float getBoundingShapeRadius() const { return _boundingShape.getRadius(); }
|
||||||
const CapsuleShape& getBoundingShape() const { return _boundingShape; }
|
const CapsuleShape& getBoundingShape() const { return _boundingShape; }
|
||||||
|
|
||||||
void resetShapePositions(); // DEBUG method
|
void resetShapePositionsToDefaultPose(); // DEBUG method
|
||||||
|
|
||||||
void renderRagdoll();
|
void renderRagdoll();
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -11,14 +11,26 @@
|
||||||
|
|
||||||
#include <glm/gtx/norm.hpp>
|
#include <glm/gtx/norm.hpp>
|
||||||
|
|
||||||
//#include <GeometryUtil.h>
|
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
|
|
||||||
#include "JointState.h"
|
#include "JointState.h"
|
||||||
|
|
||||||
JointState::JointState() :
|
JointState::JointState() :
|
||||||
_animationPriority(0.0f),
|
_animationPriority(0.0f),
|
||||||
_fbxJoint(NULL) {
|
_fbxJoint(NULL),
|
||||||
|
_isConstrained(false) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void JointState::copyState(const JointState& state) {
|
||||||
|
_animationPriority = state._animationPriority;
|
||||||
|
_transform = state._transform;
|
||||||
|
_rotation = extractRotation(_transform);
|
||||||
|
_rotationInParentFrame = state._rotationInParentFrame;
|
||||||
|
|
||||||
|
_visibleTransform = state._visibleTransform;
|
||||||
|
_visibleRotation = extractRotation(_visibleTransform);
|
||||||
|
_visibleRotationInParentFrame = state._visibleRotationInParentFrame;
|
||||||
|
// DO NOT copy _fbxJoint
|
||||||
}
|
}
|
||||||
|
|
||||||
void JointState::setFBXJoint(const FBXJoint* joint) {
|
void JointState::setFBXJoint(const FBXJoint* joint) {
|
||||||
|
@ -26,14 +38,10 @@ void JointState::setFBXJoint(const FBXJoint* joint) {
|
||||||
_rotationInParentFrame = joint->rotation;
|
_rotationInParentFrame = joint->rotation;
|
||||||
// NOTE: JointState does not own the FBXJoint to which it points.
|
// NOTE: JointState does not own the FBXJoint to which it points.
|
||||||
_fbxJoint = joint;
|
_fbxJoint = joint;
|
||||||
}
|
// precompute whether there are any constraints or not
|
||||||
|
float distanceMin = glm::distance(_fbxJoint->rotationMin, glm::vec3(-PI));
|
||||||
void JointState::copyState(const JointState& state) {
|
float distanceMax = glm::distance(_fbxJoint->rotationMax, glm::vec3(PI));
|
||||||
_rotationInParentFrame = state._rotationInParentFrame;
|
_isConstrained = distanceMin > EPSILON || distanceMax > EPSILON;
|
||||||
_transform = state._transform;
|
|
||||||
_rotation = extractRotation(_transform);
|
|
||||||
_animationPriority = state._animationPriority;
|
|
||||||
// DO NOT copy _fbxJoint
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JointState::computeTransform(const glm::mat4& parentTransform) {
|
void JointState::computeTransform(const glm::mat4& parentTransform) {
|
||||||
|
@ -43,6 +51,13 @@ void JointState::computeTransform(const glm::mat4& parentTransform) {
|
||||||
_rotation = extractRotation(_transform);
|
_rotation = extractRotation(_transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JointState::computeVisibleTransform(const glm::mat4& parentTransform) {
|
||||||
|
glm::quat modifiedRotation = _fbxJoint->preRotation * _visibleRotationInParentFrame * _fbxJoint->postRotation;
|
||||||
|
glm::mat4 modifiedTransform = _fbxJoint->preTransform * glm::mat4_cast(modifiedRotation) * _fbxJoint->postTransform;
|
||||||
|
_visibleTransform = parentTransform * glm::translate(_fbxJoint->translation) * modifiedTransform;
|
||||||
|
_visibleRotation = extractRotation(_visibleTransform);
|
||||||
|
}
|
||||||
|
|
||||||
glm::quat JointState::getRotationFromBindToModelFrame() const {
|
glm::quat JointState::getRotationFromBindToModelFrame() const {
|
||||||
return _rotation * _fbxJoint->inverseBindRotation;
|
return _rotation * _fbxJoint->inverseBindRotation;
|
||||||
}
|
}
|
||||||
|
@ -50,16 +65,16 @@ glm::quat JointState::getRotationFromBindToModelFrame() const {
|
||||||
void JointState::restoreRotation(float fraction, float priority) {
|
void JointState::restoreRotation(float fraction, float priority) {
|
||||||
assert(_fbxJoint != NULL);
|
assert(_fbxJoint != NULL);
|
||||||
if (priority == _animationPriority || _animationPriority == 0.0f) {
|
if (priority == _animationPriority || _animationPriority == 0.0f) {
|
||||||
_rotationInParentFrame = safeMix(_rotationInParentFrame, _fbxJoint->rotation, fraction);
|
setRotationInParentFrame(safeMix(_rotationInParentFrame, _fbxJoint->rotation, fraction));
|
||||||
_animationPriority = 0.0f;
|
_animationPriority = 0.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void JointState::setRotationFromBindFrame(const glm::quat& rotation, float priority) {
|
void JointState::setRotationFromBindFrame(const glm::quat& rotation, float priority) {
|
||||||
|
// rotation is from bind- to model-frame
|
||||||
assert(_fbxJoint != NULL);
|
assert(_fbxJoint != NULL);
|
||||||
if (priority >= _animationPriority) {
|
if (priority >= _animationPriority) {
|
||||||
// rotation is from bind- to model-frame
|
setRotationInParentFrame(_rotationInParentFrame * glm::inverse(_rotation) * rotation * glm::inverse(_fbxJoint->inverseBindRotation));
|
||||||
_rotationInParentFrame = _rotationInParentFrame * glm::inverse(_rotation) * rotation * glm::inverse(_fbxJoint->inverseBindRotation);
|
|
||||||
_animationPriority = priority;
|
_animationPriority = priority;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,6 +83,9 @@ void JointState::clearTransformTranslation() {
|
||||||
_transform[3][0] = 0.0f;
|
_transform[3][0] = 0.0f;
|
||||||
_transform[3][1] = 0.0f;
|
_transform[3][1] = 0.0f;
|
||||||
_transform[3][2] = 0.0f;
|
_transform[3][2] = 0.0f;
|
||||||
|
_visibleTransform[3][0] = 0.0f;
|
||||||
|
_visibleTransform[3][1] = 0.0f;
|
||||||
|
_visibleTransform[3][2] = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JointState::setRotation(const glm::quat& rotation, bool constrain, float priority) {
|
void JointState::setRotation(const glm::quat& rotation, bool constrain, float priority) {
|
||||||
|
@ -75,27 +93,59 @@ void JointState::setRotation(const glm::quat& rotation, bool constrain, float pr
|
||||||
}
|
}
|
||||||
|
|
||||||
void JointState::applyRotationDelta(const glm::quat& delta, bool constrain, float priority) {
|
void JointState::applyRotationDelta(const glm::quat& delta, bool constrain, float priority) {
|
||||||
// NOTE: delta is in jointParent-frame
|
// NOTE: delta is in model-frame
|
||||||
assert(_fbxJoint != NULL);
|
assert(_fbxJoint != NULL);
|
||||||
if (priority < _animationPriority) {
|
if (priority < _animationPriority) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_animationPriority = priority;
|
_animationPriority = priority;
|
||||||
if (!constrain || (_fbxJoint->rotationMin == glm::vec3(-PI, -PI, -PI) &&
|
if (!constrain || !_isConstrained) {
|
||||||
_fbxJoint->rotationMax == glm::vec3(PI, PI, PI))) {
|
|
||||||
// no constraints
|
// no constraints
|
||||||
_rotationInParentFrame = _rotationInParentFrame * glm::inverse(_rotation) * delta * _rotation;
|
_rotationInParentFrame = _rotationInParentFrame * glm::inverse(_rotation) * delta * _rotation;
|
||||||
_rotation = delta * _rotation;
|
_rotation = delta * _rotation;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
glm::quat targetRotation = delta * _rotation;
|
glm::quat targetRotation = _rotationInParentFrame * glm::inverse(_rotation) * delta * _rotation;
|
||||||
glm::vec3 eulers = safeEulerAngles(_rotationInParentFrame * glm::inverse(_rotation) * targetRotation);
|
setRotationInParentFrame(targetRotation);
|
||||||
glm::quat newRotation = glm::quat(glm::clamp(eulers, _fbxJoint->rotationMin, _fbxJoint->rotationMax));
|
}
|
||||||
_rotation = _rotation * glm::inverse(_rotationInParentFrame) * newRotation;
|
|
||||||
_rotationInParentFrame = newRotation;
|
/// Applies delta rotation to joint but mixes a little bit of the default pose as well.
|
||||||
|
/// This helps keep an IK solution stable.
|
||||||
|
void JointState::mixRotationDelta(const glm::quat& delta, float mixFactor, float priority) {
|
||||||
|
// NOTE: delta is in model-frame
|
||||||
|
assert(_fbxJoint != NULL);
|
||||||
|
if (priority < _animationPriority) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_animationPriority = priority;
|
||||||
|
glm::quat targetRotation = _rotationInParentFrame * glm::inverse(_rotation) * delta * _rotation;
|
||||||
|
if (mixFactor > 0.0f && mixFactor <= 1.0f) {
|
||||||
|
targetRotation = safeMix(targetRotation, _fbxJoint->rotation, mixFactor);
|
||||||
|
}
|
||||||
|
setRotationInParentFrame(targetRotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
glm::quat JointState::computeParentRotation() const {
|
||||||
|
// R = Rp * Rpre * r * Rpost
|
||||||
|
// Rp = R * (Rpre * r * Rpost)^
|
||||||
|
return _rotation * glm::inverse(_fbxJoint->preRotation * _rotationInParentFrame * _fbxJoint->postRotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JointState::setRotationInParentFrame(const glm::quat& targetRotation) {
|
||||||
|
glm::quat parentRotation = computeParentRotation();
|
||||||
|
_rotationInParentFrame = targetRotation;
|
||||||
|
// R' = Rp * Rpre * r' * Rpost
|
||||||
|
_rotation = parentRotation * _fbxJoint->preRotation * _rotationInParentFrame * _fbxJoint->postRotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
const glm::vec3& JointState::getDefaultTranslationInParentFrame() const {
|
const glm::vec3& JointState::getDefaultTranslationInParentFrame() const {
|
||||||
assert(_fbxJoint != NULL);
|
assert(_fbxJoint != NULL);
|
||||||
return _fbxJoint->translation;
|
return _fbxJoint->translation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JointState::slaveVisibleTransform() {
|
||||||
|
_visibleTransform = _transform;
|
||||||
|
_visibleRotation = _rotation;
|
||||||
|
_visibleRotationInParentFrame = _rotationInParentFrame;
|
||||||
|
}
|
||||||
|
|
|
@ -22,12 +22,19 @@ class JointState {
|
||||||
public:
|
public:
|
||||||
JointState();
|
JointState();
|
||||||
|
|
||||||
|
void copyState(const JointState& state);
|
||||||
|
|
||||||
void setFBXJoint(const FBXJoint* joint);
|
void setFBXJoint(const FBXJoint* joint);
|
||||||
const FBXJoint& getFBXJoint() const { return *_fbxJoint; }
|
const FBXJoint& getFBXJoint() const { return *_fbxJoint; }
|
||||||
|
|
||||||
void copyState(const JointState& state);
|
|
||||||
|
|
||||||
void computeTransform(const glm::mat4& parentTransform);
|
void computeTransform(const glm::mat4& parentTransform);
|
||||||
|
|
||||||
|
void computeVisibleTransform(const glm::mat4& parentTransform);
|
||||||
|
const glm::mat4& getVisibleTransform() const { return _visibleTransform; }
|
||||||
|
glm::quat getVisibleRotation() const { return _visibleRotation; }
|
||||||
|
glm::vec3 getVisiblePosition() const { return extractTranslation(_visibleTransform); }
|
||||||
|
|
||||||
const glm::mat4& getTransform() const { return _transform; }
|
const glm::mat4& getTransform() const { return _transform; }
|
||||||
|
|
||||||
glm::quat getRotation() const { return _rotation; }
|
glm::quat getRotation() const { return _rotation; }
|
||||||
|
@ -39,11 +46,19 @@ public:
|
||||||
/// \param rotation rotation of joint in model-frame
|
/// \param rotation rotation of joint in model-frame
|
||||||
void setRotation(const glm::quat& rotation, bool constrain, float priority);
|
void setRotation(const glm::quat& rotation, bool constrain, float priority);
|
||||||
|
|
||||||
/// \param delta is in the jointParent-frame
|
/// \param delta is in the model-frame
|
||||||
void applyRotationDelta(const glm::quat& delta, bool constrain = true, float priority = 1.0f);
|
void applyRotationDelta(const glm::quat& delta, bool constrain = true, float priority = 1.0f);
|
||||||
|
|
||||||
const glm::vec3& getDefaultTranslationInParentFrame() const;
|
/// Applies delta rotation to joint but mixes a little bit of the default pose as well.
|
||||||
|
/// This helps keep an IK solution stable.
|
||||||
|
/// \param delta rotation change in model-frame
|
||||||
|
/// \param mixFactor fraction in range [0,1] of how much default pose to blend in (0 is none, 1 is all)
|
||||||
|
/// \param priority priority level of this animation blend
|
||||||
|
void mixRotationDelta(const glm::quat& delta, float mixFactor, float priority = 1.0f);
|
||||||
|
|
||||||
|
/// Blends a fraciton of default pose into joint rotation.
|
||||||
|
/// \param fraction fraction in range [0,1] of how much default pose to blend in (0 is none, 1 is all)
|
||||||
|
/// \param priority priority level of this animation blend
|
||||||
void restoreRotation(float fraction, float priority);
|
void restoreRotation(float fraction, float priority);
|
||||||
|
|
||||||
/// \param rotation is from bind- to model-frame
|
/// \param rotation is from bind- to model-frame
|
||||||
|
@ -51,16 +66,36 @@ public:
|
||||||
/// NOTE: the JointState's model-frame transform/rotation are NOT updated!
|
/// NOTE: the JointState's model-frame transform/rotation are NOT updated!
|
||||||
void setRotationFromBindFrame(const glm::quat& rotation, float priority);
|
void setRotationFromBindFrame(const glm::quat& rotation, float priority);
|
||||||
|
|
||||||
|
void setRotationInParentFrame(const glm::quat& targetRotation);
|
||||||
|
const glm::quat& getRotationInParentFrame() const { return _rotationInParentFrame; }
|
||||||
|
|
||||||
|
const glm::vec3& getDefaultTranslationInParentFrame() const;
|
||||||
|
|
||||||
|
|
||||||
void clearTransformTranslation();
|
void clearTransformTranslation();
|
||||||
|
|
||||||
glm::quat _rotationInParentFrame; // joint- to parentJoint-frame
|
void slaveVisibleTransform();
|
||||||
|
|
||||||
float _animationPriority; // the priority of the animation affecting this joint
|
float _animationPriority; // the priority of the animation affecting this joint
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/// \return parent model-frame rotation
|
||||||
|
// (used to keep _rotation consistent when modifying _rotationInWorldFrame directly)
|
||||||
|
glm::quat computeParentRotation() const;
|
||||||
|
|
||||||
|
/// debug helper function
|
||||||
|
void loadBindRotation();
|
||||||
|
|
||||||
glm::mat4 _transform; // joint- to model-frame
|
glm::mat4 _transform; // joint- to model-frame
|
||||||
glm::quat _rotation; // joint- to model-frame
|
glm::quat _rotation; // joint- to model-frame
|
||||||
|
glm::quat _rotationInParentFrame; // joint- to parentJoint-frame
|
||||||
|
|
||||||
|
glm::mat4 _visibleTransform;
|
||||||
|
glm::quat _visibleRotation;
|
||||||
|
glm::quat _visibleRotationInParentFrame;
|
||||||
|
|
||||||
const FBXJoint* _fbxJoint; // JointState does NOT own its FBXJoint
|
const FBXJoint* _fbxJoint; // JointState does NOT own its FBXJoint
|
||||||
|
bool _isConstrained;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_JointState_h
|
#endif // hifi_JointState_h
|
||||||
|
|
|
@ -39,8 +39,8 @@ Model::Model(QObject* parent) :
|
||||||
_scaledToFit(false),
|
_scaledToFit(false),
|
||||||
_snapModelToCenter(false),
|
_snapModelToCenter(false),
|
||||||
_snappedToCenter(false),
|
_snappedToCenter(false),
|
||||||
|
_showTrueJointTransforms(false),
|
||||||
_rootIndex(-1),
|
_rootIndex(-1),
|
||||||
//_enableCollisionShapes(false),
|
|
||||||
_lodDistance(0.0f),
|
_lodDistance(0.0f),
|
||||||
_pupilDilation(0.0f),
|
_pupilDilation(0.0f),
|
||||||
_url("http://invalid.com") {
|
_url("http://invalid.com") {
|
||||||
|
@ -460,7 +460,7 @@ void Model::reset() {
|
||||||
}
|
}
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
for (int i = 0; i < _jointStates.size(); i++) {
|
for (int i = 0; i < _jointStates.size(); i++) {
|
||||||
_jointStates[i]._rotationInParentFrame = geometry.joints.at(i).rotation;
|
_jointStates[i].setRotationInParentFrame(geometry.joints.at(i).rotation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -571,6 +571,9 @@ void Model::setJointStates(QVector<JointState> states) {
|
||||||
radius = distance;
|
radius = distance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (int i = 0; i < _jointStates.size(); i++) {
|
||||||
|
_jointStates[i].slaveVisibleTransform();
|
||||||
|
}
|
||||||
_boundingRadius = radius;
|
_boundingRadius = radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -686,7 +689,7 @@ bool Model::getJointState(int index, glm::quat& rotation) const {
|
||||||
if (index == -1 || index >= _jointStates.size()) {
|
if (index == -1 || index >= _jointStates.size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
rotation = _jointStates.at(index)._rotationInParentFrame;
|
rotation = _jointStates.at(index).getRotationInParentFrame();
|
||||||
const glm::quat& defaultRotation = _geometry->getFBXGeometry().joints.at(index).rotation;
|
const glm::quat& defaultRotation = _geometry->getFBXGeometry().joints.at(index).rotation;
|
||||||
return glm::abs(rotation.x - defaultRotation.x) >= EPSILON ||
|
return glm::abs(rotation.x - defaultRotation.x) >= EPSILON ||
|
||||||
glm::abs(rotation.y - defaultRotation.y) >= EPSILON ||
|
glm::abs(rotation.y - defaultRotation.y) >= EPSILON ||
|
||||||
|
@ -699,7 +702,7 @@ void Model::setJointState(int index, bool valid, const glm::quat& rotation, floa
|
||||||
JointState& state = _jointStates[index];
|
JointState& state = _jointStates[index];
|
||||||
if (priority >= state._animationPriority) {
|
if (priority >= state._animationPriority) {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
state._rotationInParentFrame = rotation;
|
state.setRotationInParentFrame(rotation);
|
||||||
state._animationPriority = priority;
|
state._animationPriority = priority;
|
||||||
} else {
|
} else {
|
||||||
state.restoreRotation(1.0f, priority);
|
state.restoreRotation(1.0f, priority);
|
||||||
|
@ -765,6 +768,23 @@ bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Model::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const {
|
||||||
|
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// position is in world-frame
|
||||||
|
position = _translation + _rotation * _jointStates[jointIndex].getVisiblePosition();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Model::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const {
|
||||||
|
if (jointIndex == -1 || jointIndex >= _jointStates.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
rotation = _rotation * _jointStates[jointIndex].getVisibleRotation();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
QStringList Model::getJointNames() const {
|
QStringList Model::getJointNames() const {
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
QStringList result;
|
QStringList result;
|
||||||
|
@ -918,6 +938,8 @@ void Model::simulateInternal(float deltaTime) {
|
||||||
for (int i = 0; i < _jointStates.size(); i++) {
|
for (int i = 0; i < _jointStates.size(); i++) {
|
||||||
updateJointState(i);
|
updateJointState(i);
|
||||||
}
|
}
|
||||||
|
updateVisibleJointStates();
|
||||||
|
|
||||||
_shapesAreDirty = ! _shapes.isEmpty();
|
_shapesAreDirty = ! _shapes.isEmpty();
|
||||||
|
|
||||||
// update the attachment transforms and simulate them
|
// update the attachment transforms and simulate them
|
||||||
|
@ -928,8 +950,13 @@ void Model::simulateInternal(float deltaTime) {
|
||||||
|
|
||||||
glm::vec3 jointTranslation = _translation;
|
glm::vec3 jointTranslation = _translation;
|
||||||
glm::quat jointRotation = _rotation;
|
glm::quat jointRotation = _rotation;
|
||||||
getJointPositionInWorldFrame(attachment.jointIndex, jointTranslation);
|
if (_showTrueJointTransforms) {
|
||||||
getJointRotationInWorldFrame(attachment.jointIndex, jointRotation);
|
getJointPositionInWorldFrame(attachment.jointIndex, jointTranslation);
|
||||||
|
getJointRotationInWorldFrame(attachment.jointIndex, jointRotation);
|
||||||
|
} else {
|
||||||
|
getVisibleJointPositionInWorldFrame(attachment.jointIndex, jointTranslation);
|
||||||
|
getVisibleJointRotationInWorldFrame(attachment.jointIndex, jointRotation);
|
||||||
|
}
|
||||||
|
|
||||||
model->setTranslation(jointTranslation + jointRotation * attachment.translation * _scale);
|
model->setTranslation(jointTranslation + jointRotation * attachment.translation * _scale);
|
||||||
model->setRotation(jointRotation * attachment.rotation);
|
model->setRotation(jointRotation * attachment.rotation);
|
||||||
|
@ -944,9 +971,16 @@ void Model::simulateInternal(float deltaTime) {
|
||||||
for (int i = 0; i < _meshStates.size(); i++) {
|
for (int i = 0; i < _meshStates.size(); i++) {
|
||||||
MeshState& state = _meshStates[i];
|
MeshState& state = _meshStates[i];
|
||||||
const FBXMesh& mesh = geometry.meshes.at(i);
|
const FBXMesh& mesh = geometry.meshes.at(i);
|
||||||
for (int j = 0; j < mesh.clusters.size(); j++) {
|
if (_showTrueJointTransforms) {
|
||||||
const FBXCluster& cluster = mesh.clusters.at(j);
|
for (int j = 0; j < mesh.clusters.size(); j++) {
|
||||||
state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix;
|
const FBXCluster& cluster = mesh.clusters.at(j);
|
||||||
|
state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int j = 0; j < mesh.clusters.size(); j++) {
|
||||||
|
const FBXCluster& cluster = mesh.clusters.at(j);
|
||||||
|
state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getVisibleTransform() * cluster.inverseBindMatrix;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -972,6 +1006,14 @@ void Model::updateJointState(int index) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Model::updateVisibleJointStates() {
|
||||||
|
if (!_showTrueJointTransforms) {
|
||||||
|
for (int i = 0; i < _jointStates.size(); i++) {
|
||||||
|
_jointStates[i].slaveVisibleTransform();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation,
|
bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation,
|
||||||
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) {
|
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) {
|
||||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||||
|
@ -1058,6 +1100,128 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const gl
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority) {
|
||||||
|
// NOTE: targetRotation is from bind- to model-frame
|
||||||
|
|
||||||
|
if (endIndex == -1 || _jointStates.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
|
const QVector<int>& freeLineage = geometry.joints.at(endIndex).freeLineage;
|
||||||
|
if (freeLineage.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int numFree = freeLineage.size();
|
||||||
|
|
||||||
|
// store and remember topmost parent transform
|
||||||
|
glm::mat4 topParentTransform;
|
||||||
|
{
|
||||||
|
int index = freeLineage.last();
|
||||||
|
const JointState& state = _jointStates.at(index);
|
||||||
|
const FBXJoint& joint = state.getFBXJoint();
|
||||||
|
int parentIndex = joint.parentIndex;
|
||||||
|
if (parentIndex == -1) {
|
||||||
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
|
topParentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||||
|
} else {
|
||||||
|
topParentTransform = _jointStates[parentIndex].getTransform();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is a cyclic coordinate descent algorithm: see
|
||||||
|
// http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d
|
||||||
|
|
||||||
|
// keep track of the position of the end-effector
|
||||||
|
JointState& endState = _jointStates[endIndex];
|
||||||
|
glm::vec3 endPosition = endState.getPosition();
|
||||||
|
float distanceToGo = glm::distance(targetPosition, endPosition);
|
||||||
|
|
||||||
|
const int MAX_ITERATION_COUNT = 2;
|
||||||
|
const float ACCEPTABLE_IK_ERROR = 0.005f; // 5mm
|
||||||
|
int numIterations = 0;
|
||||||
|
do {
|
||||||
|
++numIterations;
|
||||||
|
// moving up, rotate each free joint to get endPosition closer to target
|
||||||
|
for (int j = 1; j < numFree; j++) {
|
||||||
|
int nextIndex = freeLineage.at(j);
|
||||||
|
JointState& nextState = _jointStates[nextIndex];
|
||||||
|
FBXJoint nextJoint = nextState.getFBXJoint();
|
||||||
|
if (! nextJoint.isFree) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 pivot = nextState.getPosition();
|
||||||
|
glm::vec3 leverArm = endPosition - pivot;
|
||||||
|
float leverLength = glm::length(leverArm);
|
||||||
|
if (leverLength < EPSILON) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
glm::quat deltaRotation = rotationBetween(leverArm, targetPosition - pivot);
|
||||||
|
|
||||||
|
/* DON'T REMOVE! This code provides the gravitational effect on the IK solution.
|
||||||
|
* It is commented out for the moment because we're blending the IK solution with
|
||||||
|
* the default pose which provides similar stability, but we might want to use
|
||||||
|
* gravity again later.
|
||||||
|
|
||||||
|
// We want to mix the shortest rotation with one that will pull the system down with gravity.
|
||||||
|
// So we compute a simplified center of mass, where each joint has a mass of 1.0 and we don't
|
||||||
|
// bother averaging it because we only need direction.
|
||||||
|
if (j > 1) {
|
||||||
|
|
||||||
|
glm::vec3 centerOfMass(0.0f);
|
||||||
|
for (int k = 0; k < j; ++k) {
|
||||||
|
int massIndex = freeLineage.at(k);
|
||||||
|
centerOfMass += _jointStates[massIndex].getPosition() - pivot;
|
||||||
|
}
|
||||||
|
// the gravitational effect is a rotation that tends to align the two cross products
|
||||||
|
const glm::vec3 worldAlignment = glm::vec3(0.0f, -1.f, 0.0f);
|
||||||
|
glm::quat gravityDelta = rotationBetween(glm::cross(centerOfMass, leverArm),
|
||||||
|
glm::cross(worldAlignment, leverArm));
|
||||||
|
|
||||||
|
float gravityAngle = glm::angle(gravityDelta);
|
||||||
|
const float MIN_GRAVITY_ANGLE = 0.1f;
|
||||||
|
float mixFactor = 0.5f;
|
||||||
|
if (gravityAngle < MIN_GRAVITY_ANGLE) {
|
||||||
|
// the final rotation is a mix of the two
|
||||||
|
mixFactor = 0.5f * gravityAngle / MIN_GRAVITY_ANGLE;
|
||||||
|
}
|
||||||
|
deltaRotation = safeMix(deltaRotation, gravityDelta, mixFactor);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Apply the rotation, but use mixRotationDelta() which blends a bit of the default pose
|
||||||
|
// at in the process. This provides stability to the IK solution and removes the necessity
|
||||||
|
// for the gravity effect.
|
||||||
|
glm::quat oldNextRotation = nextState.getRotation();
|
||||||
|
float mixFactor = 0.03f;
|
||||||
|
nextState.mixRotationDelta(deltaRotation, mixFactor, priority);
|
||||||
|
|
||||||
|
// measure the result of the rotation which may have been modified by
|
||||||
|
// blending and constraints
|
||||||
|
glm::quat actualDelta = nextState.getRotation() * glm::inverse(oldNextRotation);
|
||||||
|
endPosition = pivot + actualDelta * leverArm;
|
||||||
|
}
|
||||||
|
|
||||||
|
// recompute transforms from the top down
|
||||||
|
glm::mat4 parentTransform = topParentTransform;
|
||||||
|
for (int j = numFree - 1; j >= 0; --j) {
|
||||||
|
JointState& freeState = _jointStates[freeLineage.at(j)];
|
||||||
|
freeState.computeTransform(parentTransform);
|
||||||
|
parentTransform = freeState.getTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
// measure our success
|
||||||
|
endPosition = endState.getPosition();
|
||||||
|
distanceToGo = glm::distance(targetPosition, endPosition);
|
||||||
|
} while (numIterations < MAX_ITERATION_COUNT && distanceToGo < ACCEPTABLE_IK_ERROR);
|
||||||
|
|
||||||
|
// set final rotation of the end joint
|
||||||
|
endState.setRotationFromBindFrame(targetRotation, priority);
|
||||||
|
|
||||||
|
_shapesAreDirty = !_shapes.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) {
|
bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) {
|
||||||
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
if (jointIndex == -1 || _jointStates.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1605,7 +1769,7 @@ void AnimationHandle::applyFrame(float frameIndex) {
|
||||||
if (mapping != -1) {
|
if (mapping != -1) {
|
||||||
JointState& state = _model->_jointStates[mapping];
|
JointState& state = _model->_jointStates[mapping];
|
||||||
if (_priority >= state._animationPriority) {
|
if (_priority >= state._animationPriority) {
|
||||||
state._rotationInParentFrame = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction);
|
state.setRotationInParentFrame(safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction));
|
||||||
state._animationPriority = _priority;
|
state._animationPriority = _priority;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,6 +120,9 @@ public:
|
||||||
bool getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const;
|
bool getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const;
|
||||||
bool getJointCombinedRotation(int jointIndex, glm::quat& rotation) const;
|
bool getJointCombinedRotation(int jointIndex, glm::quat& rotation) const;
|
||||||
|
|
||||||
|
bool getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const;
|
||||||
|
bool getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const;
|
||||||
|
|
||||||
/// \param jointIndex index of joint in model structure
|
/// \param jointIndex index of joint in model structure
|
||||||
/// \param position[out] position of joint in model-frame
|
/// \param position[out] position of joint in model-frame
|
||||||
/// \return true if joint exists
|
/// \return true if joint exists
|
||||||
|
@ -152,6 +155,7 @@ protected:
|
||||||
|
|
||||||
bool _snapModelToCenter; /// is the model's offset automatically adjusted to center around 0,0,0 in model space
|
bool _snapModelToCenter; /// is the model's offset automatically adjusted to center around 0,0,0 in model space
|
||||||
bool _snappedToCenter; /// are we currently snapped to center
|
bool _snappedToCenter; /// are we currently snapped to center
|
||||||
|
bool _showTrueJointTransforms;
|
||||||
int _rootIndex;
|
int _rootIndex;
|
||||||
|
|
||||||
QVector<JointState> _jointStates;
|
QVector<JointState> _jointStates;
|
||||||
|
@ -176,6 +180,8 @@ protected:
|
||||||
|
|
||||||
/// Updates the state of the joint at the specified index.
|
/// Updates the state of the joint at the specified index.
|
||||||
virtual void updateJointState(int index);
|
virtual void updateJointState(int index);
|
||||||
|
|
||||||
|
virtual void updateVisibleJointStates();
|
||||||
|
|
||||||
/// \param jointIndex index of joint in model structure
|
/// \param jointIndex index of joint in model structure
|
||||||
/// \param position position of joint in model-frame
|
/// \param position position of joint in model-frame
|
||||||
|
@ -188,6 +194,8 @@ protected:
|
||||||
bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation = glm::quat(),
|
bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation = glm::quat(),
|
||||||
bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false,
|
bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false,
|
||||||
const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f), float priority = 1.0f);
|
const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f), float priority = 1.0f);
|
||||||
|
|
||||||
|
void inverseKinematics(int jointIndex, glm::vec3 position, const glm::quat& rotation, float priority);
|
||||||
|
|
||||||
/// Restores the indexed joint to its default position.
|
/// Restores the indexed joint to its default position.
|
||||||
/// \param fraction the fraction of the default position to apply (i.e., 0.25f to slerp one fourth of the way to
|
/// \param fraction the fraction of the default position to apply (i.e., 0.25f to slerp one fourth of the way to
|
||||||
|
|
77
interface/src/ui/UserLocationsDialog.cpp
Normal file
77
interface/src/ui/UserLocationsDialog.cpp
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
//
|
||||||
|
// 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 "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 location = _proxyModel.data(index.sibling(index.row(), UserLocationsModel::LocationColumn));
|
||||||
|
Menu::getInstance()->goToURL(location.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
interface/src/ui/UserLocationsDialog.h
Normal file
35
interface/src/ui/UserLocationsDialog.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
//
|
||||||
|
// 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
|
130
interface/ui/userLocationsDialog.ui
Normal file
130
interface/ui/userLocationsDialog.ui
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>UserLocationsDialog</class>
|
||||||
|
<widget class="QWidget" name="UserLocationsDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>929</width>
|
||||||
|
<height>633</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>-1</number>
|
||||||
|
</property>
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>12</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>12</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>12</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>12</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="widget_2" native="true">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">font-size: 16px</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>My Locations</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="refreshButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Refresh</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTreeView" name="locationsTreeView">
|
||||||
|
<property name="indentation">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rootIsDecorated">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="widget" native="true">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>-1</number>
|
||||||
|
</property>
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>12</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>12</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>12</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>12</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="renameButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Rename</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="deleteButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Delete</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
|
@ -12,6 +12,9 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cm
|
||||||
|
|
||||||
set(TARGET_NAME audio)
|
set(TARGET_NAME audio)
|
||||||
|
|
||||||
|
find_package(Qt5 COMPONENTS Script)
|
||||||
|
include_directories(SYSTEM "${Qt5Script_INCLUDE_DIRS}")
|
||||||
|
|
||||||
# set up the external glm library
|
# set up the external glm library
|
||||||
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
|
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
|
||||||
setup_hifi_library(${TARGET_NAME})
|
setup_hifi_library(${TARGET_NAME})
|
||||||
|
@ -26,4 +29,4 @@ link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}")
|
||||||
# add a definition for ssize_t so that windows doesn't bail
|
# add a definition for ssize_t so that windows doesn't bail
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
add_definitions(-Dssize_t=long)
|
add_definitions(-Dssize_t=long)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
|
@ -37,13 +37,15 @@ Q_DECLARE_METATYPE(JSONCallbackParameters)
|
||||||
|
|
||||||
const QString ACCOUNTS_GROUP = "accounts";
|
const QString ACCOUNTS_GROUP = "accounts";
|
||||||
|
|
||||||
JSONCallbackParameters::JSONCallbackParameters() :
|
JSONCallbackParameters::JSONCallbackParameters(QObject* jsonCallbackReceiver, const QString& jsonCallbackMethod,
|
||||||
jsonCallbackReceiver(NULL),
|
QObject* errorCallbackReceiver, const QString& errorCallbackMethod,
|
||||||
jsonCallbackMethod(),
|
QObject* updateReceiver, const QString& updateSlot) :
|
||||||
errorCallbackReceiver(NULL),
|
jsonCallbackReceiver(jsonCallbackReceiver),
|
||||||
errorCallbackMethod(),
|
jsonCallbackMethod(jsonCallbackMethod),
|
||||||
updateReciever(NULL),
|
errorCallbackReceiver(errorCallbackReceiver),
|
||||||
updateSlot()
|
errorCallbackMethod(errorCallbackMethod),
|
||||||
|
updateReciever(updateReceiver),
|
||||||
|
updateSlot(updateSlot)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,6 +203,9 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager::
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case QNetworkAccessManager::DeleteOperation:
|
||||||
|
networkReply = _networkAccessManager->sendCustomRequest(authenticatedRequest, "DELETE");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// other methods not yet handled
|
// other methods not yet handled
|
||||||
|
|
|
@ -23,7 +23,9 @@
|
||||||
|
|
||||||
class JSONCallbackParameters {
|
class JSONCallbackParameters {
|
||||||
public:
|
public:
|
||||||
JSONCallbackParameters();
|
JSONCallbackParameters(QObject* jsonCallbackReceiver = NULL, const QString& jsonCallbackMethod = QString(),
|
||||||
|
QObject* errorCallbackReceiver = NULL, const QString& errorCallbackMethod = QString(),
|
||||||
|
QObject* updateReceiver = NULL, const QString& updateSlot = QString());
|
||||||
|
|
||||||
bool isEmpty() const { return !jsonCallbackReceiver && !errorCallbackReceiver; }
|
bool isEmpty() const { return !jsonCallbackReceiver && !errorCallbackReceiver; }
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ set(MACRO_DIR "${ROOT_DIR}/cmake/macros")
|
||||||
set(TARGET_NAME shared)
|
set(TARGET_NAME shared)
|
||||||
project(${TARGET_NAME})
|
project(${TARGET_NAME})
|
||||||
|
|
||||||
find_package(Qt5 COMPONENTS Network Widgets Xml)
|
find_package(Qt5 COMPONENTS Network Widgets Xml Script)
|
||||||
|
|
||||||
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
|
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
|
||||||
setup_hifi_library(${TARGET_NAME})
|
setup_hifi_library(${TARGET_NAME})
|
||||||
|
@ -32,4 +32,8 @@ if (UNIX AND NOT APPLE)
|
||||||
target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}")
|
target_link_libraries(${TARGET_NAME} "${CMAKE_THREAD_LIBS_INIT}")
|
||||||
endif (UNIX AND NOT APPLE)
|
endif (UNIX AND NOT APPLE)
|
||||||
|
|
||||||
target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets)
|
# There is something special (bug) about Qt5Scripts, that we have to explicitly add its include
|
||||||
|
# directory when Qt5 (5.2.1) is compiled from source and is not in a standard place.
|
||||||
|
include_directories(SYSTEM "${Qt5Script_INCLUDE_DIRS}")
|
||||||
|
|
||||||
|
target_link_libraries(${TARGET_NAME} Qt5::Network Qt5::Widgets)
|
||||||
|
|
Loading…
Reference in a new issue