From bac6570655037ea52577f0f51a6a3c92080f1751 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 4 Jan 2016 17:50:45 -0800 Subject: [PATCH] Interface support for avatar attachments from Marketplace Drag and drop support of ava.json files is also supported and at the moment the only way to test. To test: 1. Create a cowboyhat.ava.json file on your hard-drive, with the following contents: { "version": "0.1", "name": "Cowboy Hat", "modelUrl": "https://s3.amazonaws.com/hifi-public/tony/cowboy-hat.fbx", "jointName": "Head", "transform": { "rotation": {"x": 0, "y": 0, "z": 0, "w": 1}, "translation": {"x": 0, "y": 0, "z": 0}, "scale": {"x": 1, "y": 1, "z": 1} }, "isSoft": false } 2. Create a leatherjacket.ava.json file, with the following contents: { "version": "0.1", "name": "Leather Jacket", "modelUrl": "https://hifi-content.s3.amazonaws.com/ozan/dev/clothes/coat/coat_rig.fbx", "isSoft": true } 3. Run interface 4. Drag-and-Drop cowboyhat.ava.json onto the main Interface window. 5. You should be prompted wear the 'Cowboy Hat' attachment. 6. If you select 'Yes', the cowboy hat should appear on the avatar. 7. Do the same with leatherjacket.ava.json file. --- interface/src/Application.cpp | 99 ++++++++++++++++++++++++++++++++++- interface/src/Application.h | 8 ++- 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 608f4cd388..f06b9cdbee 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -20,6 +20,8 @@ #include #include +#include +#include #include #include #include @@ -184,6 +186,7 @@ static const QString JS_EXTENSION = ".js"; static const QString FST_EXTENSION = ".fst"; static const QString FBX_EXTENSION = ".fbx"; static const QString OBJ_EXTENSION = ".obj"; +static const QString AVA_JSON_EXTENSION = ".ava.json"; static const int MIRROR_VIEW_TOP_PADDING = 5; static const int MIRROR_VIEW_LEFT_PADDING = 10; @@ -219,7 +222,8 @@ const QHash Application::_acceptedExtensi { SVO_EXTENSION, &Application::importSVOFromURL }, { SVO_JSON_EXTENSION, &Application::importSVOFromURL }, { JS_EXTENSION, &Application::askToLoadScript }, - { FST_EXTENSION, &Application::askToSetAvatarUrl } + { FST_EXTENSION, &Application::askToSetAvatarUrl }, + { AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl } }; #ifdef Q_OS_WIN @@ -4457,6 +4461,99 @@ void Application::modelUploadFinished(AssetUpload* upload, const QString& hash) } } +bool Application::askToWearAvatarAttachmentUrl(const QString& url) { + + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkRequest networkRequest = QNetworkRequest(url); + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + QNetworkReply* reply = networkAccessManager.get(networkRequest); + int requestNumber = ++_avatarAttachmentRequest; + connect(reply, &QNetworkReply::finished, [this, reply, url, requestNumber]() { + + if (requestNumber != _avatarAttachmentRequest) { + // this request has been superseded by another more recent request + reply->deleteLater(); + return; + } + + QNetworkReply::NetworkError networkError = reply->error(); + if (networkError == QNetworkReply::NoError) { + // download success + QByteArray contents = reply->readAll(); + + QJsonParseError jsonError; + auto doc = QJsonDocument::fromJson(contents, &jsonError); + if (jsonError.error == QJsonParseError::NoError) { + + auto jsonObject = doc.object(); + + // retrieve optional name field from JSON + QString name = tr("Unnamed Attachment"); + auto nameValue = jsonObject.value("name"); + if (nameValue.isString()) { + name = nameValue.toString(); + } + + // display confirmation dialog + if (displayAvatarAttachmentConfirmationDialog(name)) { + + // add attachment to avatar + auto myAvatar = getMyAvatar(); + assert(myAvatar); + auto attachmentDataVec = myAvatar->getAttachmentData(); + AttachmentData attachmentData; + attachmentData.fromJson(jsonObject); + attachmentDataVec.push_back(attachmentData); + myAvatar->setAttachmentData(attachmentDataVec); + + } else { + qCDebug(interfaceapp) << "User declined to wear the avatar attachment: " << url; + } + + } else { + // json parse error + auto avatarAttachmentParseErrorString = tr("Error parsing attachment JSON from url: \"%1\""); + displayAvatarAttachmentWarning(avatarAttachmentParseErrorString.arg(url)); + } + } else { + // download failure + auto avatarAttachmentDownloadErrorString = tr("Error downloading attachment JSON from url: \"%1\""); + displayAvatarAttachmentWarning(avatarAttachmentDownloadErrorString.arg(url)); + } + reply->deleteLater(); + }); + return true; +} + +void Application::displayAvatarAttachmentWarning(const QString& message) const { + auto avatarAttachmentWarningTitle = tr("Avatar Attachment Failure"); + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Warning); + msgBox.setWindowTitle(avatarAttachmentWarningTitle); + msgBox.setText(message); + msgBox.exec(); + msgBox.addButton(QMessageBox::Ok); + msgBox.exec(); +} + +bool Application::displayAvatarAttachmentConfirmationDialog(const QString& name) const { + auto avatarAttachmentConfirmationTitle = tr("Avatar Attachment Confirmation"); + auto avatarAttachmentConfirmationMessage = tr("Would you like to wear '%1' on your avatar?"); + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Question); + msgBox.setWindowTitle(avatarAttachmentConfirmationTitle); + QPushButton* button = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole); + QString message = avatarAttachmentConfirmationMessage.arg(name); + msgBox.setText(message); + msgBox.addButton(QMessageBox::Cancel); + msgBox.exec(); + if (msgBox.clickedButton() == button) { + return true; + } else { + return false; + } +} + ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded, bool loadScriptFromEditor, bool activateMainWindow, bool reload) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 0953aedd8c..78b753880d 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -340,7 +340,11 @@ private slots: bool askToLoadScript(const QString& scriptFilenameOrURL); bool askToUploadAsset(const QString& asset); void modelUploadFinished(AssetUpload* upload, const QString& hash); - + + bool askToWearAvatarAttachmentUrl(const QString& url); + void displayAvatarAttachmentWarning(const QString& message) const; + bool displayAvatarAttachmentConfirmationDialog(const QString& name) const; + void setSessionUUID(const QUuid& sessionUUID); void domainChanged(const QString& domainHostname); void updateWindowTitle(); @@ -550,6 +554,8 @@ private: bool _physicsEnabled { false }; bool _reticleClickPressed { false }; + + int _avatarAttachmentRequest = 0; }; #endif // hifi_Application_h