From 7d3b300037548f26ff8e55cf82d1622fa7f68124 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sun, 11 Oct 2015 14:51:12 +0200 Subject: [PATCH 01/16] Fix set avatar rotations from scripts (different tread) --- interface/src/avatar/MyAvatar.cpp | 35 +++++++++++++++++++------------ 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 22816e9001..de52485407 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1154,32 +1154,41 @@ void MyAvatar::setJointTranslations(QVector jointTranslations) { } void MyAvatar::setJointData(int index, const glm::quat& rotation, const glm::vec3& translation) { - if (QThread::currentThread() == thread()) { - // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority - _rig->setJointState(index, true, rotation, translation, SCRIPT_PRIORITY); + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setJointData", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation), + Q_ARG(const glm::vec3&, translation)); + return; } + // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority + _rig->setJointState(index, true, rotation, translation, SCRIPT_PRIORITY); } void MyAvatar::setJointRotation(int index, const glm::quat& rotation) { - if (QThread::currentThread() == thread()) { - // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority - _rig->setJointRotation(index, true, rotation, SCRIPT_PRIORITY); + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setJointRotation", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation)); + return; } + // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority + _rig->setJointRotation(index, true, rotation, SCRIPT_PRIORITY); } void MyAvatar::setJointTranslation(int index, const glm::vec3& translation) { - if (QThread::currentThread() == thread()) { - // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority - _rig->setJointTranslation(index, true, translation, SCRIPT_PRIORITY); + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setJointTranslation", Q_ARG(int, index), Q_ARG(const glm::vec3&, translation)); + return; } + // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority + _rig->setJointTranslation(index, true, translation, SCRIPT_PRIORITY); } void MyAvatar::clearJointData(int index) { - if (QThread::currentThread() == thread()) { - // HACK: ATM only JS scripts call clearJointData() on MyAvatar so we hardcode the priority - _rig->setJointState(index, false, glm::quat(), glm::vec3(), 0.0f); - _rig->clearJointAnimationPriority(index); + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "clearJointData", Q_ARG(int, index)); + return; } + // HACK: ATM only JS scripts call clearJointData() on MyAvatar so we hardcode the priority + _rig->setJointState(index, false, glm::quat(), glm::vec3(), 0.0f); + _rig->clearJointAnimationPriority(index); } void MyAvatar::clearJointsData() { From a6da91ee35e5a76a9c89089207e6f3016173e1c5 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 12 Oct 2015 19:03:34 +0200 Subject: [PATCH 02/16] Fix walk.js script --- examples/libraries/walkApi.js | 10 +++++----- examples/walk.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/libraries/walkApi.js b/examples/libraries/walkApi.js index 8b99ad2a98..5b42a8fb78 100644 --- a/examples/libraries/walkApi.js +++ b/examples/libraries/walkApi.js @@ -82,7 +82,7 @@ Avatar = function() { // only need to zero right leg IK chain and hips if (IKChain === "RightLeg" || joint === "Hips" ) { - MyAvatar.setJointData(joint, Quat.fromPitchYawRollDegrees(0, 0, 0)); + MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(joint), Quat.fromPitchYawRollDegrees(0, 0, 0)); } } this.calibration.hipsToFeet = MyAvatar.getJointPosition("Hips").y - MyAvatar.getJointPosition("RightToeBase").y; @@ -112,16 +112,16 @@ Avatar = function() { this.poseFingers = function() { for (knuckle in walkAssets.animationReference.leftHand) { if (walkAssets.animationReference.leftHand[knuckle].IKChain === "LeftHandThumb") { - MyAvatar.setJointData(knuckle, Quat.fromPitchYawRollDegrees(0, 0, -4)); + MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(0, 0, -4)); } else { - MyAvatar.setJointData(knuckle, Quat.fromPitchYawRollDegrees(16, 0, 5)); + MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(16, 0, 5)); } } for (knuckle in walkAssets.animationReference.rightHand) { if (walkAssets.animationReference.rightHand[knuckle].IKChain === "RightHandThumb") { - MyAvatar.setJointData(knuckle, Quat.fromPitchYawRollDegrees(0, 0, 4)); + MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(0, 0, 4)); } else { - MyAvatar.setJointData(knuckle, Quat.fromPitchYawRollDegrees(16, 0, -5)); + MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(16, 0, -5)); } } }; diff --git a/examples/walk.js b/examples/walk.js index 8d54fecc92..02bc23a2e2 100644 --- a/examples/walk.js +++ b/examples/walk.js @@ -449,6 +449,6 @@ function renderMotion() { } // apply rotations - MyAvatar.setJointData(jointName, Quat.fromVec3Degrees(jointRotations)); + MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(jointName), Quat.fromVec3Degrees(jointRotations)); } } \ No newline at end of file From cf74cfb50e48ae6b5bafeb2e397cb25fda31e3d3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 10:38:52 -0700 Subject: [PATCH 03/16] add an ATPAssetMigrator for bulk ATP migration --- interface/src/Menu.cpp | 10 +- interface/src/Menu.h | 1 + interface/src/assets/ATPAssetMigrator.cpp | 177 ++++++++++++++++++ interface/src/assets/ATPAssetMigrator.h | 48 +++++ interface/src/ui/AssetUploadDialogFactory.cpp | 51 +++-- interface/src/ui/AssetUploadDialogFactory.h | 3 +- libraries/networking/src/AssetClient.cpp | 66 ++++--- libraries/networking/src/AssetClient.h | 1 + libraries/networking/src/AssetUpload.cpp | 117 ++++++++---- libraries/networking/src/AssetUpload.h | 7 +- 10 files changed, 385 insertions(+), 96 deletions(-) create mode 100644 interface/src/assets/ATPAssetMigrator.cpp create mode 100644 interface/src/assets/ATPAssetMigrator.h diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index ae41ca2493..cc9b259e06 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -24,6 +24,7 @@ #include "Application.h" #include "AccountManager.h" +#include "assets/ATPAssetMigrator.h" #include "audio/AudioScope.h" #include "avatar/AvatarManager.h" #include "devices/DdeFaceTracker.h" @@ -354,7 +355,7 @@ Menu::Menu() { MenuWrapper* assetDeveloperMenu = developerMenu->addMenu("Assets"); auto& assetDialogFactory = AssetUploadDialogFactory::getInstance(); - assetDialogFactory.setParent(this); + assetDialogFactory.setDialogParent(this); QAction* assetUpload = addActionToQMenuAndActionHash(assetDeveloperMenu, MenuOption::UploadAsset, @@ -365,6 +366,13 @@ Menu::Menu() { // disable the asset upload action by default - it gets enabled only if asset server becomes present assetUpload->setEnabled(false); + auto& atpMigrator = ATPAssetMigrator::getInstance(); + atpMigrator.setDialogParent(this); + + QAction* assetMigration = addActionToQMenuAndActionHash(assetDeveloperMenu, MenuOption::AssetMigration, + 0, &atpMigrator, + SLOT(loadEntityServerFile())); + MenuWrapper* avatarDebugMenu = developerMenu->addMenu("Avatar"); MenuWrapper* faceTrackingMenu = avatarDebugMenu->addMenu("Face Tracking"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 4c9c3ef7b5..43388fcbd1 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -135,6 +135,7 @@ namespace MenuOption { const QString AnimDebugDrawAnimPose = "Debug Draw Animation"; const QString AnimDebugDrawBindPose = "Debug Draw Bind Pose"; const QString Antialiasing = "Antialiasing"; + const QString AssetMigration = "ATP Asset Migration"; const QString Atmosphere = "Atmosphere"; const QString Attachments = "Attachments..."; const QString AudioNoiseReduction = "Audio Noise Reduction"; diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp new file mode 100644 index 0000000000..d32378085b --- /dev/null +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -0,0 +1,177 @@ +// +// ATPAssetMigrator.cpp +// interface/src/assets +// +// Created by Stephen Birarda on 2015-10-12. +// Copyright 2015 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 "ATPAssetMigrator.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "../ui/AssetUploadDialogFactory.h" + +ATPAssetMigrator& ATPAssetMigrator::getInstance() { + static ATPAssetMigrator instance; + return instance; +} + +static const QString MODEL_URL_KEY = "modelURL"; + +void ATPAssetMigrator::loadEntityServerFile() { + auto filename = QFileDialog::getOpenFileName(_dialogParent, "Select an entity-server content file to migrate", + QString(), QString("Entity-Server Content (*.gz)")); + + if (!filename.isEmpty()) { + qDebug() << "Selected filename for ATP asset migration: " << filename; + + // try to open the file at the given filename + QFile modelsFile { filename }; + + if (modelsFile.open(QIODevice::ReadOnly)) { + QByteArray compressedJsonData = modelsFile.readAll(); + QByteArray jsonData; + + if (!gunzip(compressedJsonData, jsonData)) { + QMessageBox::warning(_dialogParent, "Error", "The file at" + filename + "was not in gzip format."); + } + + QJsonDocument modelsJSON = QJsonDocument::fromJson(jsonData); + _entitiesArray = modelsJSON.object()["Entities"].toArray(); + + for (auto jsonValue : _entitiesArray) { + QJsonObject entityObject = jsonValue.toObject(); + QString modelURLString = entityObject.value(MODEL_URL_KEY).toString(); + + if (!modelURLString.isEmpty()) { + QUrl modelURL = QUrl(modelURLString); + + if (modelURL.scheme() == URL_SCHEME_HTTP || modelURL.scheme() == URL_SCHEME_HTTPS + || modelURL.scheme() == URL_SCHEME_FILE || modelURL.scheme() == URL_SCHEME_FTP) { + + QMessageBox messageBox; + messageBox.setWindowTitle("Asset Migration"); + messageBox.setText("Would you like to migrate the following file to the asset server?"); + messageBox.setInformativeText(modelURL.toDisplayString()); + messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + messageBox.setDefaultButton(QMessageBox::Ok); + + if (messageBox.exec() == QMessageBox::Ok) { + // user wants to migrate this asset + + if (_pendingReplacements.contains(modelURL)) { + // we already have a request out for this asset, just store the QJsonValueRef + // so we can do the hash replacement when the request comes back + _pendingReplacements.insert(modelURL, jsonValue); + } else if (_uploadedAssets.contains(modelURL)) { + // we already have a hash for this asset + // so just do the replacement immediately + entityObject[MODEL_URL_KEY] = _uploadedAssets.value(modelURL).toString(); + jsonValue = entityObject; + } else { + auto request = ResourceManager::createResourceRequest(this, modelURL); + + qDebug() << "Requesting" << modelURL << "for ATP asset migration"; + + connect(request, &ResourceRequest::finished, this, [=]() { + if (request->getResult() == ResourceRequest::Success) { + migrateResource(request); + } else { + QMessageBox::warning(_dialogParent, "Error", + QString("Could not retreive asset at %1").arg(modelURL.toString())); + } + request->deleteLater(); + }); + + // add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL + // to an ATP one once ready + _pendingReplacements.insert(modelURL, jsonValue); + + request->send(); + } + + } + } + } + } + + _doneReading = true; + + } else { + QMessageBox::warning(_dialogParent, "Error", + "There was a problem loading that entity-server file for ATP asset migration. Please try again"); + } + } +} + +void ATPAssetMigrator::migrateResource(ResourceRequest* request) { + // use an asset client to upload the asset + auto assetClient = DependencyManager::get(); + + QFileInfo assetInfo { request->getUrl().fileName() }; + + auto upload = assetClient->createUpload(request->getData(), assetInfo.completeSuffix()); + + if (upload) { + // add this URL to our hash of AssetUpload to original URL + _originalURLs.insert(upload, request->getUrl()); + + qDebug() << "Starting upload of asset from" << request->getUrl(); + + // connect to the finished signal so we know when the AssetUpload is done + QObject::connect(upload, &AssetUpload::finished, this, &ATPAssetMigrator::assetUploadFinished); + + // start the upload now + upload->start(); + } else { + // show a QMessageBox to say that there is no local asset server + QString messageBoxText = QString("Could not upload \n\n%1\n\nbecause you are currently not connected" \ + " to a local asset-server.").arg(assetInfo.fileName()); + + QMessageBox::information(_dialogParent, "Failed to Upload", messageBoxText); + } +} + +void ATPAssetMigrator::assetUploadFinished(AssetUpload *upload, const QString& hash) { + if (upload->getError() == AssetUpload::NoError) { + + const auto& modelURL = _originalURLs[upload]; + + // successfully uploaded asset - make any required replacements found in the pending replacements + auto values = _pendingReplacements.values(modelURL); + + QString atpURL = QString("%1:%2.%3").arg(ATP_SCHEME).arg(hash).arg(upload->getExtension()); + + for (auto value : values) { + // replace the modelURL in this QJsonValueRef with the hash + QJsonObject valueObject = value.toObject(); + valueObject[MODEL_URL_KEY] = atpURL; + value = valueObject; + } + + // pull the replaced models from _pendingReplacements + _pendingReplacements.remove(modelURL); + + // are we out of pending replacements? if so it is time to save the entity-server file + if (_doneReading && _pendingReplacements.empty()) { + // show a dialog to ask the user where they want to save the file + } + } else { + AssetUploadDialogFactory::showErrorDialog(upload, _dialogParent); + } +} diff --git a/interface/src/assets/ATPAssetMigrator.h b/interface/src/assets/ATPAssetMigrator.h new file mode 100644 index 0000000000..84f05b600d --- /dev/null +++ b/interface/src/assets/ATPAssetMigrator.h @@ -0,0 +1,48 @@ +// +// ATPAssetMigrator.h +// interface/src/assets +// +// Created by Stephen Birarda on 2015-10-12. +// Copyright 2015 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 +// + +#pragma once + +#ifndef hifi_ATPAssetMigrator_h +#define hifi_ATPAssetMigrator_h + +#include +#include +#include + +class AssetUpload; +class ResourceRequest; + +class ATPAssetMigrator : public QObject { + Q_OBJECT +public: + static ATPAssetMigrator& getInstance(); + + void setDialogParent(QWidget* dialogParent) { _dialogParent = dialogParent; } +public slots: + void loadEntityServerFile(); +private slots: + void assetUploadFinished(AssetUpload* upload, const QString& hash); +private: + void migrateResource(ResourceRequest* request); + + QWidget* _dialogParent = nullptr; + QJsonArray _entitiesArray; + + bool _doneReading { false }; + + QMultiHash _pendingReplacements; + QHash _uploadedAssets; + QHash _originalURLs; +}; + + +#endif // hifi_ATPAssetMigrator_h diff --git a/interface/src/ui/AssetUploadDialogFactory.cpp b/interface/src/ui/AssetUploadDialogFactory.cpp index 4910e6d604..7b1611b616 100644 --- a/interface/src/ui/AssetUploadDialogFactory.cpp +++ b/interface/src/ui/AssetUploadDialogFactory.cpp @@ -29,7 +29,7 @@ AssetUploadDialogFactory& AssetUploadDialogFactory::getInstance() { return staticInstance; } -static const QString PERMISSION_DENIED_ERROR = "You do not have permission to upload content to this asset-server."; + void AssetUploadDialogFactory::showDialog() { auto nodeList = DependencyManager::get(); @@ -59,7 +59,7 @@ void AssetUploadDialogFactory::showDialog() { } } else { // we don't have permission to upload to asset server in this domain - show the permission denied error - showErrorDialog(QString(), PERMISSION_DENIED_ERROR); + showErrorDialog(nullptr, _dialogParent, AssetUpload::PERMISSION_DENIED_ERROR); } } @@ -117,42 +117,33 @@ void AssetUploadDialogFactory::handleUploadFinished(AssetUpload* upload, const Q // show the new dialog hashCopyDialog->show(); } else { - // figure out the right error message for the message box - QString additionalError; - - switch (upload->getError()) { - case AssetUpload::PermissionDenied: - additionalError = PERMISSION_DENIED_ERROR; - break; - case AssetUpload::TooLarge: - additionalError = "The uploaded content was too large and could not be stored in the asset-server."; - break; - case AssetUpload::FileOpenError: - additionalError = "The file could not be opened. Please check your permissions and try again."; - break; - case AssetUpload::NetworkError: - additionalError = "The file could not be opened. Please check your network connectivity."; - break; - default: - // not handled, do not show a message box - return; - } - // display a message box with the error - showErrorDialog(QFileInfo(upload->getFilename()).fileName(), additionalError); + showErrorDialog(upload, _dialogParent); } upload->deleteLater(); } -void AssetUploadDialogFactory::showErrorDialog(const QString& filename, const QString& additionalError) { - QString errorMessage; +void AssetUploadDialogFactory::showErrorDialog(AssetUpload* upload, QWidget* dialogParent, const QString& overrideMessage) { + QString filename; - if (!filename.isEmpty()) { - errorMessage += QString("Failed to upload %1.\n\n").arg(filename); + if (upload) { + filename = QFileInfo { upload->getFilename() }.fileName(); } - errorMessage += additionalError; + QString errorMessage = overrideMessage; - QMessageBox::warning(_dialogParent, "Failed Upload", errorMessage); + if (errorMessage.isEmpty() && upload) { + errorMessage = upload->getErrorString(); + } + + QString dialogMessage; + + if (upload) { + dialogMessage += QString("Failed to upload %1.\n\n").arg(filename); + } + + dialogMessage += errorMessage; + + QMessageBox::warning(dialogParent, "Failed Upload", dialogMessage); } diff --git a/interface/src/ui/AssetUploadDialogFactory.h b/interface/src/ui/AssetUploadDialogFactory.h index a8e8765f20..fe3ecf5dc4 100644 --- a/interface/src/ui/AssetUploadDialogFactory.h +++ b/interface/src/ui/AssetUploadDialogFactory.h @@ -25,6 +25,7 @@ public: AssetUploadDialogFactory& operator=(const AssetUploadDialogFactory& rhs) = delete; static AssetUploadDialogFactory& getInstance(); + static void showErrorDialog(AssetUpload* upload, QWidget* dialogParent, const QString& overrideMessage = QString()); void setDialogParent(QWidget* dialogParent) { _dialogParent = dialogParent; } @@ -35,7 +36,7 @@ public slots: private: AssetUploadDialogFactory() = default; - void showErrorDialog(const QString& filename, const QString& additionalError); + QWidget* _dialogParent { nullptr }; }; diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 87af2a5cf8..b7f1205847 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -65,43 +65,65 @@ void AssetClient::init() { } } +bool haveAssetServer() { + auto nodeList = DependencyManager::get(); + SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); + + if (!assetServer) { + qCWarning(asset_client) << "Could not complete AssetClient operation " + << "since you are not currently connected to an asset-server."; + return false; + } + + return true; +} + AssetRequest* AssetClient::createRequest(const QString& hash, const QString& extension) { if (hash.length() != SHA256_HASH_HEX_LENGTH) { qCWarning(asset_client) << "Invalid hash size"; return nullptr; } - auto nodeList = DependencyManager::get(); - SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); - - if (!assetServer) { - qCWarning(asset_client).nospace() << "Could not request " << hash << "." << extension - << " since you are not currently connected to an asset-server."; + if (haveAssetServer()) { + auto request = new AssetRequest(hash, extension); + + // Move to the AssetClient thread in case we are not currently on that thread (which will usually be the case) + request->moveToThread(thread()); + request->setParent(this); + + return request; + } else { return nullptr; } - - auto request = new AssetRequest(hash, extension); - - // Move to the AssetClient thread in case we are not currently on that thread (which will usually be the case) - request->moveToThread(thread()); - - return request; } + + AssetUpload* AssetClient::createUpload(const QString& filename) { - auto nodeList = DependencyManager::get(); - SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); - if (!assetServer) { - qCWarning(asset_client) << "Could not upload" << filename << "since you are not currently connected to an asset-server."; + if (haveAssetServer()) { + auto upload = new AssetUpload(filename); + + upload->moveToThread(thread()); + upload->setParent(this); + + return upload; + } else { return nullptr; } - - auto upload = new AssetUpload(this, filename); +} - upload->moveToThread(thread()); - - return upload; +AssetUpload* AssetClient::createUpload(const QByteArray& data, const QString& extension) { + if (haveAssetServer()) { + auto upload = new AssetUpload(data, extension); + + upload->moveToThread(thread()); + upload->setParent(this); + + return upload; + } else { + return nullptr; + } } bool AssetClient::getAsset(const QString& hash, const QString& extension, DataOffset start, DataOffset end, diff --git a/libraries/networking/src/AssetClient.h b/libraries/networking/src/AssetClient.h index 9b82e63b58..22933ea30b 100644 --- a/libraries/networking/src/AssetClient.h +++ b/libraries/networking/src/AssetClient.h @@ -45,6 +45,7 @@ public: Q_INVOKABLE AssetRequest* createRequest(const QString& hash, const QString& extension); Q_INVOKABLE AssetUpload* createUpload(const QString& filename); + Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data, const QString& extension); private slots: void handleAssetGetInfoReply(QSharedPointer packet, SharedNodePointer senderNode); diff --git a/libraries/networking/src/AssetUpload.cpp b/libraries/networking/src/AssetUpload.cpp index 92632a43e5..0518b1e7a1 100644 --- a/libraries/networking/src/AssetUpload.cpp +++ b/libraries/networking/src/AssetUpload.cpp @@ -17,59 +17,94 @@ #include "AssetClient.h" #include "NetworkLogging.h" -AssetUpload::AssetUpload(QObject* object, const QString& filename) : +const QString AssetUpload::PERMISSION_DENIED_ERROR = "You do not have permission to upload content to this asset-server."; + +AssetUpload::AssetUpload(const QByteArray& data, const QString& extension) : + _data(data), + _extension(extension) +{ + +} + +AssetUpload::AssetUpload(const QString& filename) : _filename(filename) { } +QString AssetUpload::getErrorString() const { + // figure out the right error message for error + switch (_error) { + case AssetUpload::PermissionDenied: + return PERMISSION_DENIED_ERROR; + case AssetUpload::TooLarge: + return "The uploaded content was too large and could not be stored in the asset-server."; + case AssetUpload::FileOpenError: + return "The file could not be opened. Please check your permissions and try again."; + case AssetUpload::NetworkError: + return "The file could not be opened. Please check your network connectivity."; + default: + // not handled, do not show a message box + return QString(); + } +} + void AssetUpload::start() { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "start", Qt::AutoConnection); return; } - // try to open the file at the given filename - QFile file { _filename }; + auto data = _data; - if (file.open(QIODevice::ReadOnly)) { + if (data.isEmpty() && !_filename.isEmpty()) { + // try to open the file at the given filename + QFile file { _filename }; - // file opened, read the data and grab the extension - _extension = QFileInfo(_filename).suffix(); - - auto data = file.readAll(); - - // ask the AssetClient to upload the asset and emit the proper signals from the passed callback - auto assetClient = DependencyManager::get(); - - qCDebug(asset_client) << "Attempting to upload" << _filename << "to asset-server."; - - assetClient->uploadAsset(data, _extension, [this](bool responseReceived, AssetServerError error, const QString& hash){ - if (!responseReceived) { - _error = NetworkError; - } else { - switch (error) { - case AssetServerError::NoError: - _error = NoError; - break; - case AssetServerError::AssetTooLarge: - _error = TooLarge; - break; - case AssetServerError::PermissionDenied: - _error = PermissionDenied; - break; - default: - _error = FileOpenError; - break; - } - } - emit finished(this, hash); - }); - } else { - // we couldn't open the file - set the error result - _error = FileOpenError; - - // emit that we are done - emit finished(this, QString()); + if (file.open(QIODevice::ReadOnly)) { + + // file opened, read the data and grab the extension + _extension = QFileInfo(_filename).suffix(); + + data = file.readAll(); + } else { + // we couldn't open the file - set the error result + _error = FileOpenError; + + // emit that we are done + emit finished(this, QString()); + } } + + // ask the AssetClient to upload the asset and emit the proper signals from the passed callback + auto assetClient = DependencyManager::get(); + + if (!_filename.isEmpty()) { + qCDebug(asset_client) << "Attempting to upload" << _filename << "to asset-server."; + } else { + qCDebug(asset_client) << "Attempting to upload file of" << _data.size() << "bytes to asset-server."; + } + + + assetClient->uploadAsset(data, _extension, [this](bool responseReceived, AssetServerError error, const QString& hash){ + if (!responseReceived) { + _error = NetworkError; + } else { + switch (error) { + case AssetServerError::NoError: + _error = NoError; + break; + case AssetServerError::AssetTooLarge: + _error = TooLarge; + break; + case AssetServerError::PermissionDenied: + _error = PermissionDenied; + break; + default: + _error = FileOpenError; + break; + } + } + emit finished(this, hash); + }); } diff --git a/libraries/networking/src/AssetUpload.h b/libraries/networking/src/AssetUpload.h index 80faa8a9c1..92f677cbf2 100644 --- a/libraries/networking/src/AssetUpload.h +++ b/libraries/networking/src/AssetUpload.h @@ -35,13 +35,17 @@ public: FileOpenError }; - AssetUpload(QObject* parent, const QString& filename); + static const QString PERMISSION_DENIED_ERROR; + + AssetUpload(const QString& filename); + AssetUpload(const QByteArray& data, const QString& extension); Q_INVOKABLE void start(); const QString& getFilename() const { return _filename; } const QString& getExtension() const { return _extension; } const Error& getError() const { return _error; } + QString getErrorString() const; signals: void finished(AssetUpload* upload, const QString& hash); @@ -49,6 +53,7 @@ signals: private: QString _filename; + QByteArray _data; QString _extension; Error _error; }; From 65976f78917104432ffeaaa1ef0710b91266bb31 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 10:51:57 -0700 Subject: [PATCH 04/16] add saving of entity file to ATPAssetMigrator --- interface/src/assets/ATPAssetMigrator.cpp | 33 ++++++++++++++++++++++- interface/src/assets/ATPAssetMigrator.h | 1 + 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index d32378085b..6dd1a04434 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -31,6 +31,7 @@ ATPAssetMigrator& ATPAssetMigrator::getInstance() { return instance; } +static const QString ENTITIES_OBJECT_KEY = "Entities"; static const QString MODEL_URL_KEY = "modelURL"; void ATPAssetMigrator::loadEntityServerFile() { @@ -169,9 +170,39 @@ void ATPAssetMigrator::assetUploadFinished(AssetUpload *upload, const QString& h // are we out of pending replacements? if so it is time to save the entity-server file if (_doneReading && _pendingReplacements.empty()) { - // show a dialog to ask the user where they want to save the file + saveEntityServerFile(); } } else { AssetUploadDialogFactory::showErrorDialog(upload, _dialogParent); } } + +void ATPAssetMigrator::saveEntityServerFile() { + // show a dialog to ask the user where they want to save the file + QString saveName = QFileDialog::getSaveFileName(_dialogParent, "Save Migrated Entities File"); + + QFile saveFile { saveName }; + + if (saveFile.open(QIODevice::WriteOnly)) { + QJsonObject rootObject; + rootObject[ENTITIES_OBJECT_KEY] = _entitiesArray; + + QJsonDocument newDocument { rootObject }; + QByteArray jsonDataForFile; + + if (gzip(newDocument.toJson(), jsonDataForFile, -1)) { + + saveFile.write(jsonDataForFile); + saveFile.close(); + + QMessageBox::information(_dialogParent, "Success", + QString("Your new entities file has been saved at %1").arg(saveName)); + } else { + QMessageBox::warning(_dialogParent, "Error", "Could not gzip JSON data for new entities file."); + } + + } else { + QMessageBox::warning(_dialogParent, "Error", + QString("Could not open file at %1 to write new entities file to.").arg(saveName)); + } +} diff --git a/interface/src/assets/ATPAssetMigrator.h b/interface/src/assets/ATPAssetMigrator.h index 84f05b600d..2c96b55ba2 100644 --- a/interface/src/assets/ATPAssetMigrator.h +++ b/interface/src/assets/ATPAssetMigrator.h @@ -33,6 +33,7 @@ private slots: void assetUploadFinished(AssetUpload* upload, const QString& hash); private: void migrateResource(ResourceRequest* request); + void saveEntityServerFile(); QWidget* _dialogParent = nullptr; QJsonArray _entitiesArray; From 549043f25533ce5506c70b02db11671ac76cd843 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 15:20:37 -0700 Subject: [PATCH 05/16] add an option to bulk upload assets in ATPAssetMigrator --- interface/src/assets/ATPAssetMigrator.cpp | 141 +++++++++++++++------- interface/src/assets/ATPAssetMigrator.h | 6 + 2 files changed, 105 insertions(+), 42 deletions(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index 6dd1a04434..a4325d482f 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -33,6 +33,7 @@ ATPAssetMigrator& ATPAssetMigrator::getInstance() { static const QString ENTITIES_OBJECT_KEY = "Entities"; static const QString MODEL_URL_KEY = "modelURL"; +static const QString MESSAGE_BOX_TITLE = "ATP Asset Migration"; void ATPAssetMigrator::loadEntityServerFile() { auto filename = QFileDialog::getOpenFileName(_dialogParent, "Select an entity-server content file to migrate", @@ -41,6 +42,19 @@ void ATPAssetMigrator::loadEntityServerFile() { if (!filename.isEmpty()) { qDebug() << "Selected filename for ATP asset migration: " << filename; + static const QString MIGRATION_CONFIRMATION_TEXT { + "The ATP Asset Migration process will scan the selected entity-server file, upload discovered resources to the"\ + " current asset-server and then save a new entity-server file with the ATP URLs.\n\nAre you ready to"\ + " continue?\n\nMake sure you are connected to the right domain." + }; + + auto button = QMessageBox::question(_dialogParent, MESSAGE_BOX_TITLE, MIGRATION_CONFIRMATION_TEXT, + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (button == QMessageBox::No) { + return; + } + // try to open the file at the given filename QFile modelsFile { filename }; @@ -62,50 +76,42 @@ void ATPAssetMigrator::loadEntityServerFile() { if (!modelURLString.isEmpty()) { QUrl modelURL = QUrl(modelURLString); - if (modelURL.scheme() == URL_SCHEME_HTTP || modelURL.scheme() == URL_SCHEME_HTTPS - || modelURL.scheme() == URL_SCHEME_FILE || modelURL.scheme() == URL_SCHEME_FTP) { + if (!_ignoredUrls.contains(modelURL) + && (modelURL.scheme() == URL_SCHEME_HTTP || modelURL.scheme() == URL_SCHEME_HTTPS + || modelURL.scheme() == URL_SCHEME_FILE || modelURL.scheme() == URL_SCHEME_FTP)) { - QMessageBox messageBox; - messageBox.setWindowTitle("Asset Migration"); - messageBox.setText("Would you like to migrate the following file to the asset server?"); - messageBox.setInformativeText(modelURL.toDisplayString()); - messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); - messageBox.setDefaultButton(QMessageBox::Ok); - - if (messageBox.exec() == QMessageBox::Ok) { - // user wants to migrate this asset + if (_pendingReplacements.contains(modelURL)) { + // we already have a request out for this asset, just store the QJsonValueRef + // so we can do the hash replacement when the request comes back + _pendingReplacements.insert(modelURL, jsonValue); + } else if (_uploadedAssets.contains(modelURL)) { + // we already have a hash for this asset + // so just do the replacement immediately + entityObject[MODEL_URL_KEY] = _uploadedAssets.value(modelURL).toString(); + jsonValue = entityObject; + } else if (wantsToMigrateResource(modelURL)) { + auto request = ResourceManager::createResourceRequest(this, modelURL); - if (_pendingReplacements.contains(modelURL)) { - // we already have a request out for this asset, just store the QJsonValueRef - // so we can do the hash replacement when the request comes back - _pendingReplacements.insert(modelURL, jsonValue); - } else if (_uploadedAssets.contains(modelURL)) { - // we already have a hash for this asset - // so just do the replacement immediately - entityObject[MODEL_URL_KEY] = _uploadedAssets.value(modelURL).toString(); - jsonValue = entityObject; - } else { - auto request = ResourceManager::createResourceRequest(this, modelURL); - - qDebug() << "Requesting" << modelURL << "for ATP asset migration"; - - connect(request, &ResourceRequest::finished, this, [=]() { - if (request->getResult() == ResourceRequest::Success) { - migrateResource(request); - } else { - QMessageBox::warning(_dialogParent, "Error", - QString("Could not retreive asset at %1").arg(modelURL.toString())); - } - request->deleteLater(); - }); - - // add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL - // to an ATP one once ready - _pendingReplacements.insert(modelURL, jsonValue); - - request->send(); - } - + qDebug() << "Requesting" << modelURL << "for ATP asset migration"; + + // add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL + // to an ATP one once ready + _pendingReplacements.insert(modelURL, jsonValue); + + connect(request, &ResourceRequest::finished, this, [=]() { + if (request->getResult() == ResourceRequest::Success) { + migrateResource(request); + } else { + QMessageBox::warning(_dialogParent, "Error", + QString("Could not retreive asset at %1").arg(modelURL.toString())); + } + request->deleteLater(); + }); + + + request->send(); + } else { + _ignoredUrls.insert(modelURL); } } } @@ -165,16 +171,58 @@ void ATPAssetMigrator::assetUploadFinished(AssetUpload *upload, const QString& h value = valueObject; } + // add this URL to our list of uploaded assets + _uploadedAssets.insert(modelURL, atpURL); + // pull the replaced models from _pendingReplacements _pendingReplacements.remove(modelURL); // are we out of pending replacements? if so it is time to save the entity-server file if (_doneReading && _pendingReplacements.empty()) { saveEntityServerFile(); + + // reset after the attempted save, success or fail + reset(); } } else { AssetUploadDialogFactory::showErrorDialog(upload, _dialogParent); } + + upload->deleteLater(); +} + +bool ATPAssetMigrator::wantsToMigrateResource(const QUrl& url) { + static bool hasAskedForCompleteMigration { false }; + static bool wantsCompleteMigration { false }; + + + + if (!hasAskedForCompleteMigration) { + // this is the first resource migration - ask the user if they just want to migrate everything + static const QString COMPLETE_MIGRATION_TEXT { "Do you want to migrate all assets found in this entity-server file?\n\n"\ + "Select \"Yes\" to upload all discovered assets to the current asset-server immediately.\n\n"\ + "Select \"No\" to be prompted for each discovered asset." + }; + + auto button = QMessageBox::question(_dialogParent, MESSAGE_BOX_TITLE, COMPLETE_MIGRATION_TEXT, + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (button == QMessageBox::Yes) { + wantsCompleteMigration = true; + } + + hasAskedForCompleteMigration = true; + } + + if (wantsCompleteMigration) { + return true; + } else { + // present a dialog asking the user if they want to migrate this specific resource + auto button = QMessageBox::question(_dialogParent, MESSAGE_BOX_TITLE, + "Would you like to migrate the following resource?\n" + url.toString(), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + return button == QMessageBox::Yes; + } } void ATPAssetMigrator::saveEntityServerFile() { @@ -206,3 +254,12 @@ void ATPAssetMigrator::saveEntityServerFile() { QString("Could not open file at %1 to write new entities file to.").arg(saveName)); } } + +void ATPAssetMigrator::reset() { + _entitiesArray = QJsonArray(); + _doneReading = false; + _pendingReplacements.clear(); + _uploadedAssets.clear(); + _originalURLs.clear(); + _ignoredUrls.clear(); +} diff --git a/interface/src/assets/ATPAssetMigrator.h b/interface/src/assets/ATPAssetMigrator.h index 2c96b55ba2..454eb1eac1 100644 --- a/interface/src/assets/ATPAssetMigrator.h +++ b/interface/src/assets/ATPAssetMigrator.h @@ -17,6 +17,7 @@ #include #include #include +#include class AssetUpload; class ResourceRequest; @@ -35,6 +36,10 @@ private: void migrateResource(ResourceRequest* request); void saveEntityServerFile(); + void reset(); + + bool wantsToMigrateResource(const QUrl& url); + QWidget* _dialogParent = nullptr; QJsonArray _entitiesArray; @@ -43,6 +48,7 @@ private: QMultiHash _pendingReplacements; QHash _uploadedAssets; QHash _originalURLs; + QSet _ignoredUrls; }; From 14503053902da26c91daa4e47206ee4cc2495f95 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 16:13:47 -0700 Subject: [PATCH 06/16] fix ATP url scheme constant --- interface/src/assets/ATPAssetMigrator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index a4325d482f..e5c31d917c 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -162,7 +162,7 @@ void ATPAssetMigrator::assetUploadFinished(AssetUpload *upload, const QString& h // successfully uploaded asset - make any required replacements found in the pending replacements auto values = _pendingReplacements.values(modelURL); - QString atpURL = QString("%1:%2.%3").arg(ATP_SCHEME).arg(hash).arg(upload->getExtension()); + QString atpURL = QString("%1:%2.%3").arg(URL_SCHEME_ATP).arg(hash).arg(upload->getExtension()); for (auto value : values) { // replace the modelURL in this QJsonValueRef with the hash From 76bfc6218a8e685acb5a09473aefafeb06ffda91 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 16:36:06 -0700 Subject: [PATCH 07/16] add back block removed in merge --- libraries/networking/src/AssetUpload.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/networking/src/AssetUpload.cpp b/libraries/networking/src/AssetUpload.cpp index fdf1c0efc4..67c1049d1a 100644 --- a/libraries/networking/src/AssetUpload.cpp +++ b/libraries/networking/src/AssetUpload.cpp @@ -100,6 +100,11 @@ void AssetUpload::start() { break; } } + + if (_error == NoError && hash == hashData(_data).toHex()) { + saveToCache(getUrl(hash, _extension), _data); + } + emit finished(this, hash); }); } From cc9376707744c28511edb506902c9c3bfe3654a2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 16:38:00 -0700 Subject: [PATCH 08/16] remove some extra spaces --- interface/src/assets/ATPAssetMigrator.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index e5c31d917c..57c0df16ff 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -195,8 +195,6 @@ bool ATPAssetMigrator::wantsToMigrateResource(const QUrl& url) { static bool hasAskedForCompleteMigration { false }; static bool wantsCompleteMigration { false }; - - if (!hasAskedForCompleteMigration) { // this is the first resource migration - ask the user if they just want to migrate everything static const QString COMPLETE_MIGRATION_TEXT { "Do you want to migrate all assets found in this entity-server file?\n\n"\ From d22c60204137b7e62bfdeb87cb42c30a74fd7616 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 16:44:13 -0700 Subject: [PATCH 09/16] use categorized logging in ATPAssetMigrator --- interface/src/assets/ATPAssetMigrator.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index 57c0df16ff..56adc38f9b 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,9 @@ #include "../ui/AssetUploadDialogFactory.h" +Q_DECLARE_LOGGING_CATEGORY(asset_migrator); +Q_LOGGING_CATEGORY(asset_migrator, "hf.asset_migrator"); + ATPAssetMigrator& ATPAssetMigrator::getInstance() { static ATPAssetMigrator instance; return instance; @@ -40,7 +44,7 @@ void ATPAssetMigrator::loadEntityServerFile() { QString(), QString("Entity-Server Content (*.gz)")); if (!filename.isEmpty()) { - qDebug() << "Selected filename for ATP asset migration: " << filename; + qCDebug(asset_migrator) << "Selected filename for ATP asset migration: " << filename; static const QString MIGRATION_CONFIRMATION_TEXT { "The ATP Asset Migration process will scan the selected entity-server file, upload discovered resources to the"\ @@ -92,7 +96,7 @@ void ATPAssetMigrator::loadEntityServerFile() { } else if (wantsToMigrateResource(modelURL)) { auto request = ResourceManager::createResourceRequest(this, modelURL); - qDebug() << "Requesting" << modelURL << "for ATP asset migration"; + qCDebug(asset_migrator) << "Requesting" << modelURL << "for ATP asset migration"; // add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL // to an ATP one once ready @@ -138,7 +142,7 @@ void ATPAssetMigrator::migrateResource(ResourceRequest* request) { // add this URL to our hash of AssetUpload to original URL _originalURLs.insert(upload, request->getUrl()); - qDebug() << "Starting upload of asset from" << request->getUrl(); + qCDebug(asset_migrator) << "Starting upload of asset from" << request->getUrl(); // connect to the finished signal so we know when the AssetUpload is done QObject::connect(upload, &AssetUpload::finished, this, &ATPAssetMigrator::assetUploadFinished); From 574089530a8c8ba040b708c98b6d1d6614cdedbd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 16:49:12 -0700 Subject: [PATCH 10/16] make sure request is not nullptr before using it --- interface/src/assets/ATPAssetMigrator.cpp | 39 +++++++++++++---------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index 56adc38f9b..c268b351fa 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -96,24 +96,29 @@ void ATPAssetMigrator::loadEntityServerFile() { } else if (wantsToMigrateResource(modelURL)) { auto request = ResourceManager::createResourceRequest(this, modelURL); - qCDebug(asset_migrator) << "Requesting" << modelURL << "for ATP asset migration"; + if (request) { + qCDebug(asset_migrator) << "Requesting" << modelURL << "for ATP asset migration"; + + // add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL + // to an ATP one once ready + _pendingReplacements.insert(modelURL, jsonValue); + + connect(request, &ResourceRequest::finished, this, [=]() { + if (request->getResult() == ResourceRequest::Success) { + migrateResource(request); + } else { + QMessageBox::warning(_dialogParent, "Error", + QString("Could not retrieve asset at %1").arg(modelURL.toString())); + } + request->deleteLater(); + }); + + request->send(); + } else { + QMessageBox::warning(_dialogParent, "Error", + QString("Could not create request for asset at %1").arg(modelURL.toString())); + } - // add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL - // to an ATP one once ready - _pendingReplacements.insert(modelURL, jsonValue); - - connect(request, &ResourceRequest::finished, this, [=]() { - if (request->getResult() == ResourceRequest::Success) { - migrateResource(request); - } else { - QMessageBox::warning(_dialogParent, "Error", - QString("Could not retreive asset at %1").arg(modelURL.toString())); - } - request->deleteLater(); - }); - - - request->send(); } else { _ignoredUrls.insert(modelURL); } From 2caa7f6d647ab3c75d078caab670388e47cf755b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 16:56:20 -0700 Subject: [PATCH 11/16] change getUrl to getATPUrl, use in ATPAssetMigrator --- interface/src/assets/ATPAssetMigrator.cpp | 3 ++- libraries/networking/src/AssetRequest.cpp | 5 ----- libraries/networking/src/AssetRequest.h | 2 +- libraries/networking/src/AssetUpload.cpp | 2 +- libraries/networking/src/AssetUtils.cpp | 4 ++-- libraries/networking/src/AssetUtils.h | 2 +- 6 files changed, 7 insertions(+), 11 deletions(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index c268b351fa..861a1df772 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -171,7 +171,8 @@ void ATPAssetMigrator::assetUploadFinished(AssetUpload *upload, const QString& h // successfully uploaded asset - make any required replacements found in the pending replacements auto values = _pendingReplacements.values(modelURL); - QString atpURL = QString("%1:%2.%3").arg(URL_SCHEME_ATP).arg(hash).arg(upload->getExtension()); + + QString atpURL = getATPUrl(hash, upload->getExtension()).toString(); for (auto value : values) { // replace the modelURL in this QJsonValueRef with the hash diff --git a/libraries/networking/src/AssetRequest.cpp b/libraries/networking/src/AssetRequest.cpp index 121b4cd4fd..7e647e9142 100644 --- a/libraries/networking/src/AssetRequest.cpp +++ b/libraries/networking/src/AssetRequest.cpp @@ -127,8 +127,3 @@ void AssetRequest::start() { }); }); } - -QUrl AssetRequest::getUrl() const { - return ::getUrl(_hash, _extension); -} - diff --git a/libraries/networking/src/AssetRequest.h b/libraries/networking/src/AssetRequest.h index a5275e718a..3c3459b15d 100644 --- a/libraries/networking/src/AssetRequest.h +++ b/libraries/networking/src/AssetRequest.h @@ -46,7 +46,7 @@ public: const QByteArray& getData() const { return _data; } const State& getState() const { return _state; } const Error& getError() const { return _error; } - QUrl getUrl() const; + QUrl getUrl() const { return ::getATPUrl(_hash, _extension); } signals: void finished(AssetRequest* thisRequest); diff --git a/libraries/networking/src/AssetUpload.cpp b/libraries/networking/src/AssetUpload.cpp index 67c1049d1a..e6f467e717 100644 --- a/libraries/networking/src/AssetUpload.cpp +++ b/libraries/networking/src/AssetUpload.cpp @@ -102,7 +102,7 @@ void AssetUpload::start() { } if (_error == NoError && hash == hashData(_data).toHex()) { - saveToCache(getUrl(hash, _extension), _data); + saveToCache(getATPUrl(hash, _extension), _data); } emit finished(this, hash); diff --git a/libraries/networking/src/AssetUtils.cpp b/libraries/networking/src/AssetUtils.cpp index 7311d73525..f37e0af820 100644 --- a/libraries/networking/src/AssetUtils.cpp +++ b/libraries/networking/src/AssetUtils.cpp @@ -19,7 +19,7 @@ #include "ResourceManager.h" -QUrl getUrl(const QString& hash, const QString& extension) { +QUrl getATPUrl(const QString& hash, const QString& extension) { if (!extension.isEmpty()) { return QUrl(QString("%1:%2.%3").arg(URL_SCHEME_ATP, hash, extension)); } else { @@ -66,4 +66,4 @@ bool saveToCache(const QUrl& url, const QByteArray& file) { qCWarning(asset_client) << "No disk cache to save assets to."; } return false; -} \ No newline at end of file +} diff --git a/libraries/networking/src/AssetUtils.h b/libraries/networking/src/AssetUtils.h index 5fd5c9144d..21b6b3f434 100644 --- a/libraries/networking/src/AssetUtils.h +++ b/libraries/networking/src/AssetUtils.h @@ -32,7 +32,7 @@ enum AssetServerError : uint8_t { PermissionDenied }; -QUrl getUrl(const QString& hash, const QString& extension = QString()); +QUrl getATPUrl(const QString& hash, const QString& extension = QString()); QByteArray hashData(const QByteArray& data); From d84994d8e0a1da6bdef40cdb13951e3281882116 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 17:04:45 -0700 Subject: [PATCH 12/16] fix casing for ubuntu include failure --- interface/src/assets/ATPAssetMigrator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index 861a1df772..fadf4ca7ad 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include From e484a904a2da25dba7506411c0759480a2c4134f Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 13 Oct 2015 17:36:00 -0700 Subject: [PATCH 13/16] Rotate the avatar to align with the HMD while moving MyAvatar: refactored updateFromHMDSensorMatrix() a bit by splitting it into several methods, because it was getting quite large and becoming hard to follow. * beginStraighteningLean() - can be called when we would like to trigger a re-centering action. * shouldBeginStraighteningLean() - contains some of the logic to decide if we should begin a re-centering action. for now it encapulates the capsule check. * processStraighteningLean() - performs the actual re-centering calculation. New code was added to MyAvatar::updateFromHMDSensorMatrix() to trigger re-centering when the avatar speed rises over a threshold. Secondly the Rig::computeMotionAnimationState() state machine for animGraph added a state change hysteresis of 100ms. This hysteresis should help smooth over two issues. 1) When the delta position is 0, because the physics timestep was not evaluated. 2) During re-centering due to desired motion, the avatar velocity can fluctuate causing undesired animation state fluctuation. --- interface/src/avatar/MyAvatar.cpp | 53 +++++++- interface/src/avatar/MyAvatar.h | 6 + libraries/animation/src/AnimStateMachine.cpp | 2 +- libraries/animation/src/Rig.cpp | 133 +++++++++++++------ libraries/animation/src/Rig.h | 2 + 5 files changed, 145 insertions(+), 51 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 605a81e9d9..bb16ac6e21 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -320,6 +320,7 @@ static bool capsuleCheck(const glm::vec3& pos, float capsuleLen, float capsuleRa // as it moves through the world. void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { + // calc deltaTime auto now = usecTimestampNow(); auto deltaUsecs = now - _lastUpdateFromHMDTime; _lastUpdateFromHMDTime = now; @@ -334,21 +335,61 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { bool hmdIsAtRest = _hmdAtRestDetector.update(deltaTime, _hmdSensorPosition, _hmdSensorOrientation); - const float STRAIGHTENING_LEAN_DURATION = 0.5f; // seconds + // It can be more accurate/smooth to use velocity rather than position, + // but some modes (e.g., hmd standing) update position without updating velocity. + // So, let's create our own workingVelocity from the worldPosition... + glm::vec3 positionDelta = getPosition() - _lastPosition; + glm::vec3 workingVelocity = positionDelta / deltaTime; + _lastPosition = getPosition(); + const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec + const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec + bool isMoving; + if (_lastIsMoving) { + isMoving = glm::length(workingVelocity) >= MOVE_EXIT_SPEED_THRESHOLD; + } else { + isMoving = glm::length(workingVelocity) > MOVE_ENTER_SPEED_THRESHOLD; + } + + bool justStartedMoving = (_lastIsMoving != isMoving) && isMoving; + _lastIsMoving = isMoving; + + if (shouldBeginStraighteningLean() || hmdIsAtRest || justStartedMoving) { + beginStraighteningLean(); + } + + processStraighteningLean(deltaTime); +} + +void MyAvatar::beginStraighteningLean() { + // begin homing toward derived body position. + if (!_straighteningLean) { + _straighteningLean = true; + _straighteningLeanAlpha = 0.0f; + } +} + +bool MyAvatar::shouldBeginStraighteningLean() const { // define a vertical capsule const float STRAIGHTENING_LEAN_CAPSULE_RADIUS = 0.2f; // meters const float STRAIGHTENING_LEAN_CAPSULE_LENGTH = 0.05f; // length of the cylinder part of the capsule in meters. + // detect if the derived body position is outside of a capsule around the _bodySensorMatrix auto newBodySensorMatrix = deriveBodyFromHMDSensor(); glm::vec3 diff = extractTranslation(newBodySensorMatrix) - extractTranslation(_bodySensorMatrix); - if (!_straighteningLean && (capsuleCheck(diff, STRAIGHTENING_LEAN_CAPSULE_LENGTH, STRAIGHTENING_LEAN_CAPSULE_RADIUS) || hmdIsAtRest)) { + bool isBodyPosOutsideCapsule = capsuleCheck(diff, STRAIGHTENING_LEAN_CAPSULE_LENGTH, STRAIGHTENING_LEAN_CAPSULE_RADIUS); - // begin homing toward derived body position. - _straighteningLean = true; - _straighteningLeanAlpha = 0.0f; + if (isBodyPosOutsideCapsule) { + return true; + } else { + return false; + } +} - } else if (_straighteningLean) { +void MyAvatar::processStraighteningLean(float deltaTime) { + if (_straighteningLean) { + + const float STRAIGHTENING_LEAN_DURATION = 0.5f; // seconds auto newBodySensorMatrix = deriveBodyFromHMDSensor(); auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index cca0c4152f..3b913eb509 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -271,6 +271,10 @@ private: const RecorderPointer getRecorder() const { return _recorder; } const PlayerPointer getPlayer() const { return _player; } + void beginStraighteningLean(); + bool shouldBeginStraighteningLean() const; + void processStraighteningLean(float deltaTime); + bool cameraInsideHead() const; // These are made private for MyAvatar so that you will use the "use" methods instead @@ -366,6 +370,8 @@ private: quint64 _lastUpdateFromHMDTime = usecTimestampNow(); AtRestDetector _hmdAtRestDetector; + glm::vec3 _lastPosition; + bool _lastIsMoving = false; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/libraries/animation/src/AnimStateMachine.cpp b/libraries/animation/src/AnimStateMachine.cpp index 5304cefe46..c0bb66c2a0 100644 --- a/libraries/animation/src/AnimStateMachine.cpp +++ b/libraries/animation/src/AnimStateMachine.cpp @@ -93,7 +93,7 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, State::Pointe const float dt = 0.0f; Triggers triggers; _nextPoses = nextStateNode->evaluate(animVars, dt, triggers); -#if WANT_DEBUGa +#if WANT_DEBUG qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID() << "->" << desiredState->getID() << "duration =" << duration << "targetFrame =" << desiredState->_interpTarget; #endif _currentState = desiredState; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 340c09060a..25f28a3f64 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -437,16 +437,6 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos static float t = 0.0f; _animVars.set("sine", static_cast(0.5 * sin(t) + 0.5)); - // default anim vars to notMoving and notTurning - _animVars.set("isMovingForward", false); - _animVars.set("isMovingBackward", false); - _animVars.set("isMovingLeft", false); - _animVars.set("isMovingRight", false); - _animVars.set("isNotMoving", true); - _animVars.set("isTurningLeft", false); - _animVars.set("isTurningRight", false); - _animVars.set("isNotTurning", true); - const float ANIM_WALK_SPEED = 1.4f; // m/s _animVars.set("walkTimeScale", glm::clamp(0.5f, 2.0f, glm::length(localVel) / ANIM_WALK_SPEED)); @@ -470,47 +460,102 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos } if (glm::length(localVel) > moveThresh) { - if (fabsf(forwardSpeed) > 0.5f * fabsf(lateralSpeed)) { - if (forwardSpeed > 0.0f) { - // forward - _animVars.set("isMovingForward", true); - _animVars.set("isNotMoving", false); - - } else { - // backward - _animVars.set("isMovingBackward", true); - _animVars.set("isNotMoving", false); - } - } else { - if (lateralSpeed > 0.0f) { - // right - _animVars.set("isMovingRight", true); - _animVars.set("isNotMoving", false); - } else { - // left - _animVars.set("isMovingLeft", true); - _animVars.set("isNotMoving", false); - } + if (_desiredState != RigRole::Move) { + _desiredStateAge = 0.0f; } - _state = RigRole::Move; + _desiredState = RigRole::Move; } else { if (fabsf(turningSpeed) > turnThresh) { - if (turningSpeed > 0.0f) { - // turning right - _animVars.set("isTurningRight", true); - _animVars.set("isNotTurning", false); - } else { - // turning left - _animVars.set("isTurningLeft", true); - _animVars.set("isNotTurning", false); + if (_desiredState != RigRole::Turn) { + _desiredStateAge = 0.0f; } - _state = RigRole::Turn; - } else { - // idle - _state = RigRole::Idle; + _desiredState = RigRole::Turn; + } else { // idle + if (_desiredState != RigRole::Idle) { + _desiredStateAge = 0.0f; + } + _desiredState = RigRole::Idle; } } + const float STATE_CHANGE_HYSTERESIS_TIMER = 0.1f; + + if ((_desiredStateAge >= STATE_CHANGE_HYSTERESIS_TIMER) && _desiredState != _state) { + _state = _desiredState; + _desiredStateAge = 0.0f; + } + + _desiredStateAge += deltaTime; + + if (_state == RigRole::Move) { + if (glm::length(localVel) > MOVE_ENTER_SPEED_THRESHOLD) { + if (fabsf(forwardSpeed) > 0.5f * fabsf(lateralSpeed)) { + if (forwardSpeed > 0.0f) { + // forward + _animVars.set("isMovingForward", true); + _animVars.set("isMovingBackward", false); + _animVars.set("isMovingRight", false); + _animVars.set("isMovingLeft", false); + _animVars.set("isNotMoving", false); + + } else { + // backward + _animVars.set("isMovingBackward", true); + _animVars.set("isMovingForward", false); + _animVars.set("isMovingRight", false); + _animVars.set("isMovingLeft", false); + _animVars.set("isNotMoving", false); + } + } else { + if (lateralSpeed > 0.0f) { + // right + _animVars.set("isMovingRight", true); + _animVars.set("isMovingLeft", false); + _animVars.set("isMovingForward", false); + _animVars.set("isMovingBackward", false); + _animVars.set("isNotMoving", false); + } else { + // left + _animVars.set("isMovingLeft", true); + _animVars.set("isMovingRight", false); + _animVars.set("isMovingForward", false); + _animVars.set("isMovingBackward", false); + _animVars.set("isNotMoving", false); + } + } + _animVars.set("isTurningLeft", false); + _animVars.set("isTurningRight", false); + _animVars.set("isNotTurning", true); + } + } else if (_state == RigRole::Turn) { + if (turningSpeed > 0.0f) { + // turning right + _animVars.set("isTurningRight", true); + _animVars.set("isTurningLeft", false); + _animVars.set("isNotTurning", false); + } else { + // turning left + _animVars.set("isTurningLeft", true); + _animVars.set("isTurningRight", false); + _animVars.set("isNotTurning", false); + } + _animVars.set("isMovingForward", false); + _animVars.set("isMovingBackward", false); + _animVars.set("isMovingRight", false); + _animVars.set("isMovingLeft", false); + _animVars.set("isNotMoving", true); + } else { + // default anim vars to notMoving and notTurning + _animVars.set("isMovingForward", false); + _animVars.set("isMovingBackward", false); + _animVars.set("isMovingLeft", false); + _animVars.set("isMovingRight", false); + _animVars.set("isNotMoving", true); + _animVars.set("isTurningLeft", false); + _animVars.set("isTurningRight", false); + _animVars.set("isNotTurning", true); + } + t += deltaTime; } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 6d9f7b4688..71c27e7213 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -236,6 +236,8 @@ public: Move }; RigRole _state = RigRole::Idle; + RigRole _desiredState = RigRole::Idle; + float _desiredStateAge = 0.0f; float _leftHandOverlayAlpha = 0.0f; float _rightHandOverlayAlpha = 0.0f; }; From c4af4c7b101735e1f9e78b9e8a4c004ba5f5607a Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 13 Oct 2015 17:45:29 -0700 Subject: [PATCH 14/16] basic mono preview for the oculus display plugin --- .../oculus/OculusDisplayPlugin.cpp | 30 +++++++++++++++---- .../oculus/OculusDisplayPlugin.h | 4 ++- .../stereo/StereoDisplayPlugin.cpp | 5 ++++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp index 58675eab4d..3e2290f104 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp @@ -11,6 +11,8 @@ #include "OculusHelpers.h" +#include + #if (OVR_MAJOR_VERSION >= 6) // A base class for FBO wrappers that need to use the Oculus C @@ -135,6 +137,19 @@ const QString & OculusDisplayPlugin::getName() const { return NAME; } +static const QString MONO_PREVIEW = "Mono Preview"; +static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate"; + +void OculusDisplayPlugin::activate() { + + CONTAINER->addMenuItem(MENU_PATH(), MONO_PREVIEW, + [this](bool clicked) { + _monoPreview = clicked; + }, true, true); + CONTAINER->removeMenu(FRAMERATE); + OculusBaseDisplayPlugin::activate(); +} + void OculusDisplayPlugin::customizeContext() { WindowOpenGLDisplayPlugin::customizeContext(); #if (OVR_MAJOR_VERSION >= 6) @@ -149,7 +164,7 @@ void OculusDisplayPlugin::customizeContext() { #endif enableVsync(false); // Only enable mirroring if we know vsync is disabled - _enableMirror = !isVsyncEnabled(); + _enablePreview = !isVsyncEnabled(); } void OculusDisplayPlugin::deactivate() { @@ -169,10 +184,15 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi // controlling vsync wglSwapIntervalEXT(0); - // screen mirroring - if (_enableMirror) { + // screen preview mirroring + if (_enablePreview) { auto windowSize = toGlm(_window->size()); - Context::Viewport(windowSize.x, windowSize.y); + if (_monoPreview) { + Context::Viewport(windowSize.x * 2, windowSize.y); + Context::Scissor(0, windowSize.y, windowSize.x, windowSize.y); + } else { + Context::Viewport(windowSize.x, windowSize.y); + } glBindTexture(GL_TEXTURE_2D, finalTexture); GLenum err = glGetError(); Q_ASSERT(0 == err); @@ -216,7 +236,7 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi otherwise the swapbuffer delay will interefere with the framerate of the headset */ void OculusDisplayPlugin::finishFrame() { - if (_enableMirror) { + if (_enablePreview) { swapBuffers(); } doneCurrent(); diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h index 7db83884cd..c1224ecf3a 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h @@ -14,6 +14,7 @@ using SwapFboPtr = QSharedPointer; class OculusDisplayPlugin : public OculusBaseDisplayPlugin { public: + virtual void activate() override; virtual void deactivate() override; virtual const QString & getName() const override; @@ -25,7 +26,8 @@ protected: private: static const QString NAME; - bool _enableMirror{ false }; + bool _enablePreview { false }; + bool _monoPreview { true }; #if (OVR_MAJOR_VERSION >= 6) SwapFboPtr _sceneFbo; diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index 2ea79ed2e0..4f7b0a1a78 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -61,6 +61,8 @@ glm::mat4 StereoDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProje return eyeProjection; } +static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate"; + std::vector _screenActions; void StereoDisplayPlugin::activate() { auto screens = qApp->screens(); @@ -76,6 +78,9 @@ void StereoDisplayPlugin::activate() { [this](bool clicked) { updateScreen(); }, true, checked, "Screens"); _screenActions[i] = action; } + + CONTAINER->removeMenu(FRAMERATE); + CONTAINER->setFullscreen(qApp->primaryScreen()); WindowOpenGLDisplayPlugin::activate(); } From 3e7a6fd490cfbd1bf327d71d4d6a6b6d30389963 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 14 Oct 2015 14:43:59 -0700 Subject: [PATCH 15/16] Change to boom offset for 3rd person HMD camera. Previously the boom offset was computed in HMD sensor space instead of world space, so it did not play well with the vive, or the oculus if you weren't facing the camera directly. Now it is computed in world space, and is always behind the character's position/orientation. This can cause the boom to swing if the character rotation changes but now the avatar rotation in HMD uses comfort mode. It's not that disorienting. --- interface/src/Application.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e0e5003830..c9a07c1d13 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1109,13 +1109,10 @@ void Application::paintGL() { } } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { if (isHMDMode()) { - glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); - _myCamera.setRotation(myAvatar->getWorldAlignedOrientation() * hmdRotation); - // Ignore MenuOption::CenterPlayerInView in HMD view - glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); - _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + myAvatar->getOrientation() - * (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f) + hmdOffset)); + auto hmdWorldMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); + _myCamera.setRotation(glm::normalize(glm::quat_cast(hmdWorldMat))); + auto worldBoomOffset = myAvatar->getOrientation() * (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f)); + _myCamera.setPosition(extractTranslation(hmdWorldMat) + worldBoomOffset); } else { _myCamera.setRotation(myAvatar->getHead()->getOrientation()); if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { From c99573ea64a812c417a1dc2a7a3d3f786eed653b Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 15 Oct 2015 15:35:35 +0200 Subject: [PATCH 16/16] Fixed support for calling setJointMumblers with joint name. Walk.js runs smooth now! --- examples/libraries/walkApi.js | 10 +++++----- examples/walk.js | 2 +- libraries/avatars/src/AvatarData.cpp | 21 +++++++++++++++++++-- libraries/avatars/src/AvatarData.h | 2 ++ 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/examples/libraries/walkApi.js b/examples/libraries/walkApi.js index 5b42a8fb78..d1192deee7 100644 --- a/examples/libraries/walkApi.js +++ b/examples/libraries/walkApi.js @@ -82,7 +82,7 @@ Avatar = function() { // only need to zero right leg IK chain and hips if (IKChain === "RightLeg" || joint === "Hips" ) { - MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(joint), Quat.fromPitchYawRollDegrees(0, 0, 0)); + MyAvatar.setJointRotation(joint, Quat.fromPitchYawRollDegrees(0, 0, 0)); } } this.calibration.hipsToFeet = MyAvatar.getJointPosition("Hips").y - MyAvatar.getJointPosition("RightToeBase").y; @@ -112,16 +112,16 @@ Avatar = function() { this.poseFingers = function() { for (knuckle in walkAssets.animationReference.leftHand) { if (walkAssets.animationReference.leftHand[knuckle].IKChain === "LeftHandThumb") { - MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(0, 0, -4)); + MyAvatar.setJointRotation(knuckle, Quat.fromPitchYawRollDegrees(0, 0, -4)); } else { - MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(16, 0, 5)); + MyAvatar.setJointRotation(knuckle, Quat.fromPitchYawRollDegrees(16, 0, 5)); } } for (knuckle in walkAssets.animationReference.rightHand) { if (walkAssets.animationReference.rightHand[knuckle].IKChain === "RightHandThumb") { - MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(0, 0, 4)); + MyAvatar.setJointRotation(knuckle, Quat.fromPitchYawRollDegrees(0, 0, 4)); } else { - MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(16, 0, -5)); + MyAvatar.setJointRotation(knuckle, Quat.fromPitchYawRollDegrees(16, 0, -5)); } } }; diff --git a/examples/walk.js b/examples/walk.js index 02bc23a2e2..0b5bcab65a 100644 --- a/examples/walk.js +++ b/examples/walk.js @@ -449,6 +449,6 @@ function renderMotion() { } // apply rotations - MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(jointName), Quat.fromVec3Degrees(jointRotations)); + MyAvatar.setJointRotation(jointName, Quat.fromVec3Degrees(jointRotations)); } } \ No newline at end of file diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index a9ff9541ea..a698c6f374 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1032,13 +1032,30 @@ glm::vec3 AvatarData::getJointTranslation(const QString& name) const { void AvatarData::setJointData(const QString& name, const glm::quat& rotation, const glm::vec3& translation) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setJointData", Q_ARG(const QString&, name), - Q_ARG(const glm::quat&, rotation)); + QMetaObject::invokeMethod(this, "setJointData", Q_ARG(const QString&, name), Q_ARG(const glm::quat&, rotation), + Q_ARG(const glm::vec3&, translation)); return; } setJointData(getJointIndex(name), rotation, translation); } +void AvatarData::setJointRotation(const QString& name, const glm::quat& rotation) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setJointRotation", Q_ARG(const QString&, name), Q_ARG(const glm::quat&, rotation)); + return; + } + setJointRotation(getJointIndex(name), rotation); +} + +void AvatarData::setJointTranslation(const QString& name, const glm::vec3& translation) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setJointTranslation", Q_ARG(const QString&, name), + Q_ARG(const glm::vec3&, translation)); + return; + } + setJointTranslation(getJointIndex(name), translation); +} + void AvatarData::setJointRotation(int index, const glm::quat& rotation) { if (index == -1) { return; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index e4022fd474..3abd63bf63 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -251,6 +251,8 @@ public: Q_INVOKABLE virtual glm::vec3 getJointTranslation(int index) const; Q_INVOKABLE void setJointData(const QString& name, const glm::quat& rotation, const glm::vec3& translation); + Q_INVOKABLE void setJointRotation(const QString& name, const glm::quat& rotation); + Q_INVOKABLE void setJointTranslation(const QString& name, const glm::vec3& translation); Q_INVOKABLE void clearJointData(const QString& name); Q_INVOKABLE bool isJointDataValid(const QString& name) const; Q_INVOKABLE glm::quat getJointRotation(const QString& name) const;