diff --git a/interface/resources/qml/hifi/AvatarPackager.qml b/interface/resources/qml/hifi/AvatarPackager.qml
index 2044529427..434cd4128f 100644
--- a/interface/resources/qml/hifi/AvatarPackager.qml
+++ b/interface/resources/qml/hifi/AvatarPackager.qml
@@ -23,14 +23,13 @@ Windows.ScrollingWindow {
//HifiConstants { id: hifi }
Rectangle {
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: parent.top
- anchors.bottom: parent.bottom
+ anchors.fill: parent
+
AvatarProject {
id: avatarProject
colorScheme: root.colorScheme
visible: false
+ anchors.fill: parent
}
Rectangle {
diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml b/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml
index 0a6ed6c459..c365d7436e 100644
--- a/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml
+++ b/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml
@@ -13,26 +13,23 @@ Rectangle {
property int colorScheme;
+ color: "blue"
+
visible: true
-
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: parent.top
- anchors.bottom: parent.bottom
+
+ anchors.fill: parent
RalewaySemiBold {
id: avatarProjectLabel
size: 24;
- anchors.left: parent.left
- anchors.top: parent.top
+ width: parent.width
anchors.topMargin: 25
anchors.bottomMargin: 25
text: 'Avatar Project'
}
HifiControls.Button {
id: openFolderButton
- anchors.left: parent.left
- anchors.right: parent.right
+ width: parent.width
anchors.top: avatarProjectLabel.bottom
text: qsTr("Open Project Folder")
colorScheme: root.colorScheme
@@ -43,15 +40,32 @@ Rectangle {
}
HifiControls.Button {
id: uploadButton
- anchors.left: parent.left
- anchors.right: parent.right
+ width: parent.width
anchors.top: openFolderButton.bottom
text: qsTr("Upload")
color: hifi.buttons.blue
colorScheme: root.colorScheme
height: 30
onClicked: function() {
-
+ }
+ }
+ Text {
+ id: modelText
+ anchors.top: uploadButton.bottom
+ height: 30
+ text: parent.height
+ }
+ Rectangle {
+ color: 'white'
+ visible: AvatarPackagerCore.currentAvatarProject !== null
+ width: parent.width
+ anchors.top: modelText.bottom
+ height: 1000
+
+ ListView {
+ anchors.fill: parent
+ model: AvatarPackagerCore.currentAvatarProject === null ? [] : AvatarPackagerCore.currentAvatarProject.projectFiles
+ delegate: Text { text: 'File: ' + modelData }
}
}
}
diff --git a/interface/src/avatar/AvatarPackager.cpp b/interface/src/avatar/AvatarPackager.cpp
index 1088c862d4..3fdf193087 100644
--- a/interface/src/avatar/AvatarPackager.cpp
+++ b/interface/src/avatar/AvatarPackager.cpp
@@ -15,6 +15,18 @@
#include
#include
+#include "ModelSelector.h"
+#include
+
+#include
+#include
+
+std::once_flag setupQMLTypesFlag;
+AvatarPackager::AvatarPackager() {
+ std::call_once(setupQMLTypesFlag, []() {
+ qmlRegisterType();
+ });
+}
bool AvatarPackager::open() {
static const QUrl url{ "hifi/AvatarPackager.qml" };
@@ -32,5 +44,11 @@ QObject* AvatarPackager::openAvatarProject(QString avatarProjectFSTPath) {
//_currentAvatarProject = nullptr;
}
_currentAvatarProject = AvatarProject::openAvatarProject(avatarProjectFSTPath);
+ emit avatarProjectChanged();
return _currentAvatarProject;
}
+
+QObject* AvatarPackager::uploadItem() {
+ std::vector filePaths;
+ return new MarketplaceItemUploader(QUuid(), filePaths);
+}
diff --git a/interface/src/avatar/AvatarPackager.h b/interface/src/avatar/AvatarPackager.h
index a8ef6c6421..f002631f17 100644
--- a/interface/src/avatar/AvatarPackager.h
+++ b/interface/src/avatar/AvatarPackager.h
@@ -21,16 +21,23 @@
class AvatarPackager : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
- Q_PROPERTY(QObject* currentAvatarProject READ getAvatarProject)
+ Q_PROPERTY(QObject* currentAvatarProject READ getAvatarProject NOTIFY avatarProjectChanged)
public:
+ AvatarPackager();
bool open();
Q_INVOKABLE QObject* openAvatarProject(QString avatarProjectFSTPath);
+signals:
+ void avatarProjectChanged();
+
private:
Q_INVOKABLE AvatarProject* getAvatarProject() const { return _currentAvatarProject; };
- AvatarProject* _currentAvatarProject { nullptr };
+ //Q_INVOKABLE QObject* openAvatarProject();
+ Q_INVOKABLE QObject* uploadItem();
+
+ AvatarProject* _currentAvatarProject{ nullptr };
};
-#endif // hifi_AvatarPackager_h
+#endif // hifi_AvatarPackager_h
diff --git a/interface/src/avatar/AvatarProject.cpp b/interface/src/avatar/AvatarProject.cpp
index a3b22ab2c2..32fe4febcd 100644
--- a/interface/src/avatar/AvatarProject.cpp
+++ b/interface/src/avatar/AvatarProject.cpp
@@ -11,17 +11,56 @@
#include "AvatarProject.h"
-AvatarProject* AvatarProject::openAvatarProject(QString path) {
+#include
+
+#include
+#include
+#include
+#include
+
+AvatarProject* AvatarProject::openAvatarProject(const QString& path) {
const auto pathToLower = path.toLower();
if (pathToLower.endsWith(".fst")) {
- // TODO: do we open FSTs from any path?
- return new AvatarProject(path);
+ QFile file{ path };
+ if (!file.open(QIODevice::ReadOnly)) {
+ return nullptr;
+ }
+ return new AvatarProject(path, file.readAll());
}
if (pathToLower.endsWith(".fbx")) {
// TODO: Create FST here:
-
}
return nullptr;
}
+
+AvatarProject::AvatarProject(const QString& fstPath, const QByteArray& data) :
+ _fstPath(fstPath), _fst(fstPath, FSTReader::readMapping(data)) {
+
+ _directory = QFileInfo(_fstPath).absoluteDir();
+
+ //_projectFiles = _directory.entryList();
+ refreshProjectFiles();
+
+ auto fileInfo = QFileInfo(_fstPath);
+ _projectPath = fileInfo.absoluteDir().absolutePath();
+}
+
+void AvatarProject::appendDirectory(QString prefix, QDir dir) {
+ qDebug() << "Inside of " << prefix << dir.absolutePath();
+ auto flags = QDir::Dirs | QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot | QDir::Hidden;
+ for (auto& entry : dir.entryInfoList({}, flags)) {
+ if (entry.isFile()) {
+ _projectFiles.append(prefix + "/" + entry.fileName());
+ } else if (entry.isDir()) {
+ qDebug() << "Found dir " << entry.absoluteFilePath() << " in " << dir.absolutePath();
+ appendDirectory(prefix + dir.dirName() + "/", entry.absoluteFilePath());
+ }
+ }
+}
+
+void AvatarProject::refreshProjectFiles() {
+ _projectFiles.clear();
+ appendDirectory("", _directory);
+}
diff --git a/interface/src/avatar/AvatarProject.h b/interface/src/avatar/AvatarProject.h
index 2114b147dd..6dc64cda6f 100644
--- a/interface/src/avatar/AvatarProject.h
+++ b/interface/src/avatar/AvatarProject.h
@@ -13,12 +13,21 @@
#ifndef hifi_AvatarProject_h
#define hifi_AvatarProject_h
+#include "FST.h"
+
+#include
#include
#include
#include
+#include
+#include
class AvatarProject : public QObject {
Q_OBJECT
+ Q_PROPERTY(FST* fst READ getFST)
+
+ Q_PROPERTY(QStringList projectFiles MEMBER _projectFiles)
+
Q_PROPERTY(QString projectFolderPath READ getProjectPath)
Q_PROPERTY(QString projectFSTPath READ getFSTPath)
Q_PROPERTY(QString projectFBXPath READ getFBXPath)
@@ -37,17 +46,10 @@ public:
/**
* returns the AvatarProject or a nullptr on failure.
*/
- static AvatarProject* openAvatarProject(QString path);
+ static AvatarProject* openAvatarProject(const QString& path);
private:
- AvatarProject(QString fstPath) :
- _fstPath(fstPath) {
- auto fileInfo = QFileInfo(_fstPath);
- _projectPath = fileInfo.absoluteDir().absolutePath();
-
- _fstPath = _projectPath + "TemporaryFBX.fbx";
-
- }
+ AvatarProject(const QString& fstPath, const QByteArray& data);
~AvatarProject() {
// TODO: cleanup FST / AvatarProjectUploader etc.
@@ -55,13 +57,19 @@ private:
Q_INVOKABLE QString getProjectPath() const { return _projectPath; }
Q_INVOKABLE QString getFSTPath() const { return _fstPath; }
- Q_INVOKABLE QString getFBXPath() const { return _fbxPath; }
+ Q_INVOKABLE QString getFBXPath() const { return _fst.getModelPath(); }
+ FST* getFST() { return &_fst; }
+
+ void refreshProjectFiles();
+ void appendDirectory(QString prefix, QDir dir);
+
+ FST _fst;
+
+ QDir _directory;
+ QStringList _projectFiles{};
QString _projectPath;
QString _fstPath;
- // TODO: handle this in the FST Class
- QString _fbxPath;
-
};
-#endif // hifi_AvatarProject_h
+#endif // hifi_AvatarProject_h
diff --git a/interface/src/avatar/MarketplaceItemUploader.cpp b/interface/src/avatar/MarketplaceItemUploader.cpp
new file mode 100644
index 0000000000..c2671aadec
--- /dev/null
+++ b/interface/src/avatar/MarketplaceItemUploader.cpp
@@ -0,0 +1,57 @@
+//
+// MarketplaceItemUploader.cpp
+//
+//
+// Created by Ryan Huffman on 12/10/2018
+// Copyright 2018 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 "MarketplaceItemUploader.h"
+
+#include
+#include
+
+#include
+
+MarketplaceItemUploader::MarketplaceItemUploader(QUuid marketplaceID, std::vector filePaths)
+ : _filePaths(filePaths), _marketplaceID(marketplaceID) {
+
+}
+
+void MarketplaceItemUploader::send() {
+ auto accountManager = DependencyManager::get();
+ auto request = accountManager->createRequest("/marketplace/item", AccountManagerAuth::Required);
+
+ QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
+ QByteArray data;
+
+ /*
+ auto reply = networkAccessManager.post(request, data);
+
+ connect(reply, &QNetworkReply::uploadProgress, this, &MarketplaceItemUploader::uploadProgress);
+
+ connect(reply, &QNetworkReply::finished, this, [this, reply]() {
+ auto error = reply->error();
+ if (error == QNetworkReply::NoError) {
+ } else {
+ }
+ emit complete();
+ });
+ */
+
+ QTimer* timer = new QTimer();
+ timer->setInterval(1000);
+ connect(timer, &QTimer::timeout, this, [this, timer]() {
+ if (progress <= 1.0f) {
+ progress += 0.1;
+ emit uploadProgress(progress * 100.0f, 100.0f);
+ } else {
+ emit complete();
+ timer->stop();
+ }
+ });
+ timer->start();
+}
diff --git a/interface/src/avatar/MarketplaceItemUploader.h b/interface/src/avatar/MarketplaceItemUploader.h
new file mode 100644
index 0000000000..2a3071244e
--- /dev/null
+++ b/interface/src/avatar/MarketplaceItemUploader.h
@@ -0,0 +1,55 @@
+//
+// MarketplaceItemUploader.h
+//
+//
+// Created by Ryan Huffman on 12/10/2018
+// Copyright 2018 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_MarketplaceItemUploader_h
+#define hifi_MarketplaceItemUploader_h
+
+#include
+#include
+
+class QNetworkReply;
+
+class MarketplaceItemUploader : public QObject {
+ Q_OBJECT
+public:
+ enum class Error
+ {
+ None,
+ ItemNotUpdateable,
+ ItemDoesNotExist,
+ RequestTimedOut,
+ Unknown
+ };
+ enum class State
+ {
+ Ready,
+ Sent
+ };
+
+ MarketplaceItemUploader(QUuid markertplaceID, std::vector filePaths);
+
+ float progress{ 0.0f };
+
+ Q_INVOKABLE void send();
+
+signals:
+ void uploadProgress(qint64 bytesSent, qint64 bytesTotal);
+ void complete();
+
+private:
+
+ QNetworkReply* _reply;
+ QUuid _marketplaceID;
+ std::vector _filePaths;
+};
+
+#endif // hifi_MarketplaceItemUploader_h
diff --git a/libraries/fbx/src/FST.cpp b/libraries/fbx/src/FST.cpp
new file mode 100644
index 0000000000..6574b66e51
--- /dev/null
+++ b/libraries/fbx/src/FST.cpp
@@ -0,0 +1,45 @@
+//
+// FST.cpp
+//
+// Created by Ryan Huffman on 12/11/15.
+// Copyright 2018 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 "FST.h"
+
+#include
+#include
+
+FST::FST(QString fstPath, QVariantHash data) : _fstPath(fstPath) {
+ if (data.contains("name")) {
+ _name = data["name"].toString();
+ data.remove("name");
+ }
+
+ if (data.contains("filename")) {
+ _modelPath = data["filename"].toString();
+ data.remove("filename");
+ }
+
+ _other = data;
+}
+
+QString FST::absoluteModelPath() const {
+ QFileInfo fileInfo{ _fstPath };
+ QDir dir{ fileInfo.absoluteDir() };
+ return dir.absoluteFilePath(_modelPath);
+}
+
+void FST::setName(const QString& name) {
+ _name = name;
+ emit nameChanged(name);
+}
+
+void FST::setModelPath(const QString& modelPath) {
+ _modelPath = modelPath;
+ emit modelPathChanged(modelPath);
+}
\ No newline at end of file
diff --git a/libraries/fbx/src/FST.h b/libraries/fbx/src/FST.h
new file mode 100644
index 0000000000..e8c67c6c6b
--- /dev/null
+++ b/libraries/fbx/src/FST.h
@@ -0,0 +1,49 @@
+//
+// FST.h
+//
+// Created by Ryan Huffman on 12/11/15.
+// Copyright 2018 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_FST_h
+#define hifi_FST_h
+
+#include
+#include
+
+class FST : public QObject {
+ Q_OBJECT
+ Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)
+ Q_PROPERTY(QString modelPath READ getModelPath WRITE setModelPath NOTIFY modelPathChanged)
+ Q_PROPERTY(QUuid marketplaceID READ getMarketplaceID)
+public:
+ FST(QString fstPath, QVariantHash data);
+
+ QString absoluteModelPath() const;
+
+ QString getName() const { return _name; }
+ void setName(const QString& name);
+
+ QString getModelPath() const { return _modelPath; }
+ void setModelPath(const QString& modelPath);
+
+ QUuid getMarketplaceID() const { return _marketplaceID; }
+
+signals:
+ void nameChanged(const QString& name);
+ void modelPathChanged(const QString& modelPath);
+
+private:
+ QString _fstPath;
+
+ QString _name{};
+ QString _modelPath{};
+ QUuid _marketplaceID{};
+
+ QVariantHash _other{};
+};
+
+#endif // hifi_FST_h
diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp
index 5721ac9334..989661cb81 100644
--- a/libraries/networking/src/AccountManager.cpp
+++ b/libraries/networking/src/AccountManager.cpp
@@ -208,6 +208,44 @@ void AccountManager::setSessionID(const QUuid& sessionID) {
}
}
+QNetworkRequest AccountManager::createRequest(QString path, AccountManagerAuth::Type authType) {
+ QNetworkRequest networkRequest;
+ networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
+ networkRequest.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter());
+
+ networkRequest.setRawHeader(METAVERSE_SESSION_ID_HEADER,
+ uuidStringWithoutCurlyBraces(_sessionID).toLocal8Bit());
+
+ QUrl requestURL = _authURL;
+
+ if (requestURL.isEmpty()) { // Assignment client doesn't set _authURL.
+ requestURL = getMetaverseServerURL();
+ }
+
+ if (path.startsWith("/")) {
+ requestURL.setPath(path);
+ } else {
+ requestURL.setPath("/" + path);
+ }
+
+ if (authType != AccountManagerAuth::None ) {
+ if (hasValidAccessToken()) {
+ networkRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER,
+ _accountInfo.getAccessToken().authorizationHeaderValue());
+ } else {
+ if (authType == AccountManagerAuth::Required) {
+ qCDebug(networking) << "No valid access token present. Bailing on invoked request to"
+ << path << "that requires authentication";
+ return QNetworkRequest();
+ }
+ }
+ }
+
+ networkRequest.setUrl(requestURL);
+
+ return networkRequest;
+}
+
void AccountManager::sendRequest(const QString& path,
AccountManagerAuth::Type authType,
QNetworkAccessManager::Operation operation,
@@ -231,46 +269,10 @@ void AccountManager::sendRequest(const QString& path,
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
- QNetworkRequest networkRequest;
- networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
- networkRequest.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter());
-
- networkRequest.setRawHeader(METAVERSE_SESSION_ID_HEADER,
- uuidStringWithoutCurlyBraces(_sessionID).toLocal8Bit());
-
- QUrl requestURL = _authURL;
-
- if (requestURL.isEmpty()) { // Assignment client doesn't set _authURL.
- requestURL = getMetaverseServerURL();
- }
-
- if (path.startsWith("/")) {
- requestURL.setPath(path);
- } else {
- requestURL.setPath("/" + path);
- }
-
- if (!query.isEmpty()) {
- requestURL.setQuery(query);
- }
-
- if (authType != AccountManagerAuth::None ) {
- if (hasValidAccessToken()) {
- networkRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER,
- _accountInfo.getAccessToken().authorizationHeaderValue());
- } else {
- if (authType == AccountManagerAuth::Required) {
- qCDebug(networking) << "No valid access token present. Bailing on invoked request to"
- << path << "that requires authentication";
- return;
- }
- }
- }
-
- networkRequest.setUrl(requestURL);
+ QNetworkRequest networkRequest = createRequest(path, authType);
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
- qCDebug(networking) << "Making a request to" << qPrintable(requestURL.toString());
+ qCDebug(networking) << "Making a request to" << qPrintable(networkRequest.url().toString());
if (!dataByteArray.isEmpty()) {
qCDebug(networking) << "The POST/PUT body -" << QString(dataByteArray);
diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h
index d5406707e7..77f20472fa 100644
--- a/libraries/networking/src/AccountManager.h
+++ b/libraries/networking/src/AccountManager.h
@@ -28,7 +28,8 @@
class JSONCallbackParameters {
public:
- JSONCallbackParameters(QObject* callbackReceiver = nullptr, const QString& jsonCallbackMethod = QString(),
+ JSONCallbackParameters(QObject* callbackReceiver = nullptr,
+ const QString& jsonCallbackMethod = QString(),
const QString& errorCallbackMethod = QString());
bool isEmpty() const { return !callbackReceiver; }
@@ -39,11 +40,12 @@ public:
};
namespace AccountManagerAuth {
- enum Type {
- None,
- Required,
- Optional
- };
+enum Type
+{
+ None,
+ Required,
+ Optional
+};
}
Q_DECLARE_METATYPE(AccountManagerAuth::Type);
@@ -60,6 +62,7 @@ class AccountManager : public QObject, public Dependency {
public:
AccountManager(UserAgentGetter userAgentGetter = DEFAULT_USER_AGENT_GETTER);
+ QNetworkRequest createRequest(QString path, AccountManagerAuth::Type authType);
Q_INVOKABLE void sendRequest(const QString& path,
AccountManagerAuth::Type authType,
QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
@@ -84,7 +87,7 @@ public:
void requestProfile();
DataServerAccountInfo& getAccountInfo() { return _accountInfo; }
- void setAccountInfo(const DataServerAccountInfo &newAccountInfo);
+ void setAccountInfo(const DataServerAccountInfo& newAccountInfo);
static QJsonObject dataObjectFromResponse(QNetworkReply* requestReply);
@@ -104,7 +107,10 @@ public:
public slots:
void requestAccessToken(const QString& login, const QString& password);
void requestAccessTokenWithSteam(QByteArray authSessionTicket);
- void requestAccessTokenWithAuthCode(const QString& authCode, const QString& clientId, const QString& clientSecret, const QString& redirectUri);
+ void requestAccessTokenWithAuthCode(const QString& authCode,
+ const QString& clientId,
+ const QString& clientSecret,
+ const QString& redirectUri);
void refreshAccessToken();
void requestAccessTokenFinished();
@@ -148,15 +154,15 @@ private:
QUrl _authURL;
DataServerAccountInfo _accountInfo;
- bool _isWaitingForTokenRefresh { false };
- bool _isAgent { false };
+ bool _isWaitingForTokenRefresh{ false };
+ bool _isAgent{ false };
- bool _isWaitingForKeypairResponse { false };
+ bool _isWaitingForKeypairResponse{ false };
QByteArray _pendingPrivateKey;
- QUuid _sessionID { QUuid::createUuid() };
+ QUuid _sessionID{ QUuid::createUuid() };
- bool _limitedCommerce { false };
+ bool _limitedCommerce{ false };
};
-#endif // hifi_AccountManager_h
+#endif // hifi_AccountManager_h