History

Moved ModelUploader to interface + New error messages

Atlante45 committed 12 years ago

Commit 8ad7c3b1 parent 8e097be2 files · +48 −36Browse files

Showing the changes to interface/src/ModelUploader.cpp (1 of 2 files). Show all

Unified · Split

interface/src/ModelUploader.cpp+342−0
@@ -0,0 +1,342 @@
1//
2// ModelUploader.cpp
3// hifi
4//
5// Created by Clément Brisset on 3/4/14.
6// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
7//
8//
9
10#include <QDebug>
11#include <QFile>
12#include <QFileDialog>
13#include <QGridLayout>
14#include <QHttpMultiPart>
15#include <QMessageBox>
16#include <QProgressBar>
17#include <QStandardPaths>
18#include <QTextStream>
19#include <QVariant>
20
21#include <AccountManager.h>
22
23#include "renderer/FBXReader.h"
24#include "ModelUploader.h"
25
26
27static const QString NAME_FIELD = "name";
28static const QString FILENAME_FIELD = "filename";
29static const QString TEXDIR_FIELD = "texdir";
30static const QString LOD_FIELD = "lod";
31
32static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com";
33static const QString MODEL_URL = "/api/v1/models";
34
35static const int MAX_SIZE = 10 * 1024 * 1024; // 10 MB
36static const int TIMEOUT = 1000;
37static const int MAX_CHECK = 30;
38
39static const int QCOMPRESS_HEADER_POSITION = 0;
40static const int QCOMPRESS_HEADER_SIZE = 4;
41
42ModelUploader::ModelUploader(bool isHead) :
43 _lodCount(-1),
44 _texturesCount(-1),
45 _totalSize(0),
46 _isHead(isHead),
47 _readyToSend(false),
48 _dataMultiPart(new QHttpMultiPart(QHttpMultiPart::FormDataType)),
49 _numberOfChecks(MAX_CHECK)
50{
51 connect(&_timer, SIGNAL(timeout()), SLOT(checkS3()));
52}
53
54ModelUploader::~ModelUploader() {
55 delete _dataMultiPart;
56}
57
58bool ModelUploader::zip() {
59 // File Dialog
60 QString filename = QFileDialog::getOpenFileName(NULL,
61 "Select your .fst file ...",
62 QStandardPaths::writableLocation(QStandardPaths::HomeLocation),
63 "*.fst");
64 if (filename == "") {
65 // If the user canceled we return.
66 return false;
67 }
68 QString texDir;
69 QString fbxFile;
70
71
72 // First we check the FST file
73 QFile fst(filename);
74 if (!fst.open(QFile::ReadOnly | QFile::Text)) {
75 QMessageBox::warning(NULL,
76 QString("ModelUploader::zip()"),
77 QString("Could not open FST file."),
78 QMessageBox::Ok);
79 qDebug() << "[Warning] " << QString("Could not open FST file.");
80 return false;
81 }
82 qDebug() << "Reading FST file : " << QFileInfo(fst).filePath();
83
84 // Compress and copy the fst
85 if (!addPart(QFileInfo(fst).filePath(), QString("fst"))) {
86 return false;
87 }
88
89 // Let's read through the FST file
90 QTextStream stream(&fst);
91 QList<QString> line;
92 while (!stream.atEnd()) {
93 line = stream.readLine().split(QRegExp("[ =]"), QString::SkipEmptyParts);
94 if (line.isEmpty()) {
95 continue;
96 }
97
98 // according to what is read, we modify the command
99 if (line[0] == NAME_FIELD) {
100 QHttpPart textPart;
101 textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;"
102 " name=\"model_name\"");
103 textPart.setBody(line[1].toUtf8());
104 _dataMultiPart->append(textPart);
105 _url = S3_URL + ((_isHead)? "/models/heads/" : "/models/skeletons/") + line[1].toUtf8() + ".fst";
106 } else if (line[0] == FILENAME_FIELD) {
107 fbxFile = QFileInfo(fst).path() + "/" + line[1];
108 QFileInfo fbxInfo(fbxFile);
109 if (!fbxInfo.exists() || !fbxInfo.isFile()) { // Check existence
110 QMessageBox::warning(NULL,
111 QString("ModelUploader::zip()"),
112 QString("FBX file %1 could not be found.").arg(fbxInfo.fileName()),
113 QMessageBox::Ok);
114 qDebug() << "[Warning] " << QString("FBX file %1 could not be found.").arg(fbxInfo.fileName());
115 return false;
116 }
117 // Compress and copy
118 if (!addPart(fbxInfo.filePath(), "fbx")) {
119 return false;
120 }
121 } else if (line[0] == TEXDIR_FIELD) { // Check existence
122 texDir = QFileInfo(fst).path() + "/" + line[1];
123 QFileInfo texInfo(texDir);
124 if (!texInfo.exists() || !texInfo.isDir()) {
125 QMessageBox::warning(NULL,
126 QString("ModelUploader::zip()"),
127 QString("Texture directory could not be found."),
128 QMessageBox::Ok);
129 qDebug() << "[Warning] " << QString("Texture directory could not be found.");
130 return false;
131 }
132 } else if (line[0] == LOD_FIELD) {
133 QFileInfo lod(QFileInfo(fst).path() + "/" + line[1]);
134 if (!lod.exists() || !lod.isFile()) { // Check existence
135 QMessageBox::warning(NULL,
136 QString("ModelUploader::zip()"),
137 QString("LOD file %1 could not be found.").arg(lod.fileName()),
138 QMessageBox::Ok);
139 qDebug() << "[Warning] " << QString("FBX file %1 could not be found.").arg(lod.fileName());
140 }
141 // Compress and copy
142 if (!addPart(lod.filePath(), QString("lod%1").arg(++_lodCount))) {
143 return false;
144 }
145 }
146 }
147
148 if (!addTextures(texDir, fbxFile)) {
149 return false;
150 }
151
152 QHttpPart textPart;
153 textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;"
154 " name=\"model_category\"");
155 if (_isHead) {
156 textPart.setBody("heads");
157 } else {
158 textPart.setBody("skeletons");
159 }
160 _dataMultiPart->append(textPart);
161
162 _readyToSend = true;
163 return true;
164}
165
166void ModelUploader::send() {
167 if (!zip()) {
168 deleteLater();
169 return;
170 }
171
172 JSONCallbackParameters callbackParams;
173 callbackParams.jsonCallbackReceiver = this;
174 callbackParams.jsonCallbackMethod = "uploadSuccess";
175 callbackParams.errorCallbackReceiver = this;
176 callbackParams.errorCallbackMethod = "uploadFailed";
177 callbackParams.updateReciever = this;
178 callbackParams.updateSlot = SLOT(uploadUpdate(qint64, qint64));
179
180 AccountManager::getInstance().authenticatedRequest(MODEL_URL, QNetworkAccessManager::PostOperation, callbackParams, QByteArray(), _dataMultiPart);
181 _dataMultiPart = NULL;
182 qDebug() << "Sending model...";
183 _progressDialog = new QDialog();
184 _progressBar = new QProgressBar(_progressDialog);
185 _progressBar->setRange(0, 100);
186 _progressBar->setValue(0);
187
188 _progressDialog->setWindowTitle("Uploading model...");
189 _progressDialog->setLayout(new QGridLayout(_progressDialog));
190 _progressDialog->layout()->addWidget(_progressBar);
191
192 _progressDialog->exec();
193
194 delete _progressDialog;
195 _progressDialog = NULL;
196 _progressBar = NULL;
197}
198
199void ModelUploader::uploadUpdate(qint64 bytesSent, qint64 bytesTotal) {
200 if (_progressDialog) {
201 _progressBar->setRange(0, bytesTotal);
202 _progressBar->setValue(bytesSent);
203 }
204}
205
206void ModelUploader::uploadSuccess(const QJsonObject& jsonResponse) {
207 if (_progressDialog) {
208 _progressDialog->accept();
209 }
210 QMessageBox::information(NULL,
211 QString("ModelUploader::uploadSuccess()"),
212 QString("We are reading your model information."),
213 QMessageBox::Ok);
214 qDebug() << "Model sent with success";
215 checkS3();
216}
217
218void ModelUploader::uploadFailed(QNetworkReply::NetworkError errorCode, const QString& errorString) {
219 if (_progressDialog) {
220 _progressDialog->reject();
221 }
222 QMessageBox::warning(NULL,
223 QString("ModelUploader::uploadFailed()"),
224 QString("There was a problem with your upload, please try again later."),
225 QMessageBox::Ok);
226 qDebug() << "Model upload failed (" << errorCode << "): " << errorString;
227 deleteLater();
228}
229
230void ModelUploader::checkS3() {
231 qDebug() << "Checking S3 for " << _url;
232 QNetworkRequest request(_url);
233 QNetworkReply* reply = _networkAccessManager.head(request);
234 connect(reply, SIGNAL(finished()), SLOT(processCheck()));
235}
236
237void ModelUploader::processCheck() {
238 QNetworkReply* reply = static_cast<QNetworkReply*>(sender());
239 _timer.stop();
240
241 switch (reply->error()) {
242 case QNetworkReply::NoError:
243 QMessageBox::information(NULL,
244 QString("ModelUploader::processCheck()"),
245 QString("Your model is now available in the browser."),
246 QMessageBox::Ok);
247 deleteLater();
248 break;
249 case QNetworkReply::ContentNotFoundError:
250 if (--_numberOfChecks) {
251 _timer.start(TIMEOUT);
252 break;
253 }
254 default:
255 QMessageBox::warning(NULL,
256 QString("ModelUploader::processCheck()"),
257 QString("We could not verify that your model was sent sucessfully\n"
258 "but it may have. If you do not see it in the model browser, try to upload again."),
259 QMessageBox::Ok);
260 deleteLater();
261 break;
262 }
263
264 delete reply;
265}
266
267bool ModelUploader::addTextures(const QString& texdir, const QString fbxFile) {
268 QFile fbx(fbxFile);
269 if (!fbx.open(QIODevice::ReadOnly)) {
270 return false;
271 }
272
273 QByteArray buffer = fbx.readAll();
274 QVariantHash variantHash = readMapping(buffer);
275 FBXGeometry geometry = readFBX(buffer, variantHash);
276
277 foreach (FBXMesh mesh, geometry.meshes) {
278 foreach (FBXMeshPart part, mesh.parts) {
279 if (!part.diffuseFilename.isEmpty()) {
280 if (!addPart(QFileInfo(fbxFile).path() + "/" + part.diffuseFilename,
281 QString("texture%1").arg(++_texturesCount))) {
282 return false;
283 }
284 }
285 if (!part.normalFilename.isEmpty()) {
286 if (!addPart(QFileInfo(fbxFile).path() + "/" + part.normalFilename,
287 QString("texture%1").arg(++_texturesCount))) {
288 return false;
289 }
290 }
291 }
292 }
293
294 return true;
295}
296
297bool ModelUploader::addPart(const QString &path, const QString& name) {
298 qDebug() << path;
299 QFile file(path);
300 if (!file.open(QIODevice::ReadOnly)) {
301 QMessageBox::warning(NULL,
302 QString("ModelUploader::addPart()"),
303 QString("Could not open %1").arg(path),
304 QMessageBox::Ok);
305 qDebug() << "[Warning] " << QString("Could not open %1").arg(path);
306 return false;
307 }
308 QByteArray buffer = qCompress(file.readAll());
309
310 // Qt's qCompress() default compression level (-1) is the standard zLib compression.
311 // Here remove Qt's custom header that prevent the data server from uncompressing the files with zLib.
312 buffer.remove(QCOMPRESS_HEADER_POSITION, QCOMPRESS_HEADER_SIZE);
313
314 QHttpPart part;
315 part.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data;"
316 " name=\"" + name.toUtf8() + "\";"
317 " filename=\"" + QFileInfo(file).fileName().toUtf8() + "\""));
318 part.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
319 part.setBody(buffer);
320 _dataMultiPart->append(part);
321
322
323 qDebug() << "File " << QFileInfo(file).fileName() << " added to model.";
324 _totalSize += file.size();
325 if (_totalSize > MAX_SIZE) {
326 QMessageBox::warning(NULL,
327 QString("ModelUploader::zip()"),
328 QString("Model too big, over %1 Bytes.").arg(MAX_SIZE),
329 QMessageBox::Ok);
330 qDebug() << "[Warning] " << QString("Model too big, over %1 Bytes.").arg(MAX_SIZE);
331 return false;
332 }
333 qDebug() << "Current model size: " << _totalSize;
334
335 return true;
336}
337
338
339
340
341
342

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t