diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 42f11ee576..5ce9468318 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -63,11 +63,11 @@ #include #include #include -#include #include "Application.h" #include "InterfaceVersion.h" #include "Menu.h" +#include "ModelUploader.h" #include "Util.h" #include "devices/OculusManager.h" #include "devices/TV3DManager.h" @@ -3090,6 +3090,16 @@ void Application::setMenuShortcutsEnabled(bool enabled) { setShortcutsEnabled(_window->menuBar(), enabled); } +void Application::uploadModel(ModelType modelType) { + ModelUploader* uploader = new ModelUploader(modelType); + QThread* thread = new QThread(); + thread->connect(uploader, SIGNAL(destroyed()), SLOT(quit())); + thread->connect(thread, SIGNAL(finished()), SLOT(deleteLater())); + uploader->connect(thread, SIGNAL(started()), SLOT(send())); + + thread->start(); +} + void Application::updateWindowTitle(){ QString buildVersion = " (build " + applicationVersion() + ")"; @@ -3417,22 +3427,16 @@ void Application::toggleRunningScriptsWidget() { } } -void Application::uploadFST(bool isHead) { - ModelUploader* uploader = new ModelUploader(isHead); - QThread* thread = new QThread(); - thread->connect(uploader, SIGNAL(destroyed()), SLOT(quit())); - thread->connect(thread, SIGNAL(finished()), SLOT(deleteLater())); - uploader->connect(thread, SIGNAL(started()), SLOT(send())); - - thread->start(); -} - void Application::uploadHead() { - uploadFST(true); + uploadModel(HEAD_MODEL); } void Application::uploadSkeleton() { - uploadFST(false); + uploadModel(SKELETON_MODEL); +} + +void Application::uploadAttachment() { + uploadModel(ATTACHMENT_MODEL); } ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScriptFromEditor) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 96b472e553..a7073ac4e9 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -71,6 +71,7 @@ #include "scripting/ControllerScriptingInterface.h" #include "ui/BandwidthDialog.h" #include "ui/BandwidthMeter.h" +#include "ui/ModelsBrowser.h" #include "ui/OctreeStatsDialog.h" #include "ui/RearMirrorTools.h" #include "ui/LodToolsDialog.h" @@ -295,9 +296,9 @@ public slots: void reloadAllScripts(); void toggleRunningScriptsWidget(); - void uploadFST(bool isHead); void uploadHead(); void uploadSkeleton(); + void uploadAttachment(); void bumpSettings() { ++_numChangedSettings; } @@ -375,13 +376,11 @@ private: void setMenuShortcutsEnabled(bool enabled); + void uploadModel(ModelType modelType); + static void attachNewHeadToNode(Node *newNode); static void* networkReceive(void* args); // network receive thread - void findAxisAlignment(); - - void displayRearMirrorTools(); - MainWindow* _window; GLCanvas* _glWidget; // our GLCanvas has a couple extra features diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index baa82a2f6a..b6df2f7269 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -157,6 +157,8 @@ Menu::Menu() : addDisabledActionAndSeparator(fileMenu, "Upload Avatar Model"); addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadHead, 0, Application::getInstance(), SLOT(uploadHead())); addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadSkeleton, 0, Application::getInstance(), SLOT(uploadSkeleton())); + addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadAttachment, 0, + Application::getInstance(), SLOT(uploadAttachment())); addDisabledActionAndSeparator(fileMenu, "Settings"); addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsImport, 0, this, SLOT(importSettings())); addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsExport, 0, this, SLOT(exportSettings())); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index da2585f81a..e37d256f62 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -363,6 +363,7 @@ namespace MenuOption { const QString TestPing = "Test Ping"; const QString TransmitterDrive = "Transmitter Drive"; const QString TurnWithHead = "Turn using Head"; + const QString UploadAttachment = "Upload Attachment Model"; const QString UploadHead = "Upload Head Model"; const QString UploadSkeleton = "Upload Skeleton Model"; const QString Visage = "Visage"; diff --git a/interface/src/ModelUploader.cpp b/interface/src/ModelUploader.cpp index 0ffd725716..2b86e04829 100644 --- a/interface/src/ModelUploader.cpp +++ b/interface/src/ModelUploader.cpp @@ -59,11 +59,11 @@ static const int MAX_CHECK = 30; static const int QCOMPRESS_HEADER_POSITION = 0; static const int QCOMPRESS_HEADER_SIZE = 4; -ModelUploader::ModelUploader(bool isHead) : +ModelUploader::ModelUploader(ModelType modelType) : _lodCount(-1), _texturesCount(-1), _totalSize(0), - _isHead(isHead), + _modelType(modelType), _readyToSend(false), _dataMultiPart(new QHttpMultiPart(QHttpMultiPart::FormDataType)), _numberOfChecks(MAX_CHECK) @@ -190,7 +190,7 @@ bool ModelUploader::zip() { } // open the dialog to configure the rest - ModelPropertiesDialog properties(_isHead, mapping, basePath, geometry); + ModelPropertiesDialog properties(_modelType, mapping, basePath, geometry); if (properties.exec() == QDialog::Rejected) { return false; } @@ -202,7 +202,7 @@ bool ModelUploader::zip() { textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"model_name\""); textPart.setBody(nameField); _dataMultiPart->append(textPart); - _url = S3_URL + ((_isHead)? "/models/heads/" : "/models/skeletons/") + nameField + ".fst"; + _url = S3_URL + "/models/" + MODEL_TYPE_NAMES[_modelType] + "/" + nameField + ".fst"; } else { QMessageBox::warning(NULL, QString("ModelUploader::zip()"), @@ -260,11 +260,7 @@ bool ModelUploader::zip() { QHttpPart textPart; textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;" " name=\"model_category\""); - if (_isHead) { - textPart.setBody("heads"); - } else { - textPart.setBody("skeletons"); - } + textPart.setBody(MODEL_TYPE_NAMES[_modelType]); _dataMultiPart->append(textPart); _readyToSend = true; @@ -510,9 +506,9 @@ bool ModelUploader::addPart(const QFile& file, const QByteArray& contents, const return true; } -ModelPropertiesDialog::ModelPropertiesDialog(bool isHead, const QVariantHash& originalMapping, +ModelPropertiesDialog::ModelPropertiesDialog(ModelType modelType, const QVariantHash& originalMapping, const QString& basePath, const FBXGeometry& geometry) : - _isHead(isHead), + _modelType(modelType), _originalMapping(originalMapping), _basePath(basePath), _geometry(geometry) { @@ -531,10 +527,12 @@ ModelPropertiesDialog::ModelPropertiesDialog(bool isHead, const QVariantHash& or _scale->setMaximum(FLT_MAX); _scale->setSingleStep(0.01); - form->addRow("Left Eye Joint:", _leftEyeJoint = createJointBox()); - form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox()); - form->addRow("Neck Joint:", _neckJoint = createJointBox()); - if (!isHead) { + if (_modelType != ATTACHMENT_MODEL) { + form->addRow("Left Eye Joint:", _leftEyeJoint = createJointBox()); + form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox()); + form->addRow("Neck Joint:", _neckJoint = createJointBox()); + } + if (_modelType == SKELETON_MODEL) { form->addRow("Root Joint:", _rootJoint = createJointBox()); form->addRow("Lean Joint:", _leanJoint = createJointBox()); form->addRow("Head Joint:", _headJoint = createJointBox()); @@ -573,10 +571,12 @@ QVariantHash ModelPropertiesDialog::getMapping() const { mapping.insert(JOINT_INDEX_FIELD, jointIndices); QVariantHash joints = mapping.value(JOINT_FIELD).toHash(); - insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText()); - insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText()); - insertJointMapping(joints, "jointNeck", _neckJoint->currentText()); - if (!_isHead) { + if (_modelType != ATTACHMENT_MODEL) { + insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText()); + insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText()); + insertJointMapping(joints, "jointNeck", _neckJoint->currentText()); + } + if (_modelType == SKELETON_MODEL) { insertJointMapping(joints, "jointRoot", _rootJoint->currentText()); insertJointMapping(joints, "jointLean", _leanJoint->currentText()); insertJointMapping(joints, "jointHead", _headJoint->currentText()); @@ -604,10 +604,12 @@ void ModelPropertiesDialog::reset() { _scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble()); QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash(); - setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString()); - setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString()); - setJointText(_neckJoint, jointHash.value("jointNeck").toString()); - if (!_isHead) { + if (_modelType != ATTACHMENT_MODEL) { + setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString()); + setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString()); + setJointText(_neckJoint, jointHash.value("jointNeck").toString()); + } + if (_modelType == SKELETON_MODEL) { setJointText(_rootJoint, jointHash.value("jointRoot").toString()); setJointText(_leanJoint, jointHash.value("jointLean").toString()); setJointText(_headJoint, jointHash.value("jointHead").toString()); diff --git a/interface/src/ModelUploader.h b/interface/src/ModelUploader.h index 11594b3d95..499bfad03b 100644 --- a/interface/src/ModelUploader.h +++ b/interface/src/ModelUploader.h @@ -17,6 +17,8 @@ #include +#include "ui/ModelsBrowser.h" + class QComboBox; class QDoubleSpinBox; class QFileInfo; @@ -30,7 +32,7 @@ class ModelUploader : public QObject { Q_OBJECT public: - ModelUploader(bool isHead); + ModelUploader(ModelType type); ~ModelUploader(); public slots: @@ -49,7 +51,7 @@ private: int _lodCount; int _texturesCount; int _totalSize; - bool _isHead; + ModelType _modelType; bool _readyToSend; QHttpMultiPart* _dataMultiPart; @@ -73,7 +75,7 @@ class ModelPropertiesDialog : public QDialog { Q_OBJECT public: - ModelPropertiesDialog(bool isHead, const QVariantHash& originalMapping, + ModelPropertiesDialog(ModelType modelType, const QVariantHash& originalMapping, const QString& basePath, const FBXGeometry& geometry); QVariantHash getMapping() const; @@ -87,7 +89,7 @@ private: QComboBox* createJointBox(bool withNone = true) const; void insertJointMapping(QVariantHash& joints, const QString& joint, const QString& name) const; - bool _isHead; + ModelType _modelType; QVariantHash _originalMapping; QString _basePath; FBXGeometry _geometry; diff --git a/interface/src/ui/ModelsBrowser.cpp b/interface/src/ui/ModelsBrowser.cpp index 77e056bdd3..f65829a8ac 100644 --- a/interface/src/ui/ModelsBrowser.cpp +++ b/interface/src/ui/ModelsBrowser.cpp @@ -22,10 +22,11 @@ #include "ModelsBrowser.h" +const char* MODEL_TYPE_NAMES[] = { "heads", "skeletons", "attachments" }; + static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com"; static const QString PUBLIC_URL = "http://public.highfidelity.io"; -static const QString HEAD_MODELS_LOCATION = "models/heads"; -static const QString SKELETON_MODELS_LOCATION = "models/skeletons/"; +static const QString MODELS_LOCATION = "models/"; static const QString PREFIX_PARAMETER_NAME = "prefix"; static const QString MARKER_PARAMETER_NAME = "marker"; @@ -243,11 +244,7 @@ void ModelHandler::queryNewFiles(QString marker) { // Build query QUrl url(S3_URL); QUrlQuery query; - if (_type == Head) { - query.addQueryItem(PREFIX_PARAMETER_NAME, HEAD_MODELS_LOCATION); - } else if (_type == Skeleton) { - query.addQueryItem(PREFIX_PARAMETER_NAME, SKELETON_MODELS_LOCATION); - } + query.addQueryItem(PREFIX_PARAMETER_NAME, MODELS_LOCATION + MODEL_TYPE_NAMES[_type]); if (!marker.isEmpty()) { query.addQueryItem(MARKER_PARAMETER_NAME, marker); diff --git a/interface/src/ui/ModelsBrowser.h b/interface/src/ui/ModelsBrowser.h index 81f64c6730..ff273a45bc 100644 --- a/interface/src/ui/ModelsBrowser.h +++ b/interface/src/ui/ModelsBrowser.h @@ -16,12 +16,14 @@ #include #include - enum ModelType { - Head, - Skeleton + HEAD_MODEL, + SKELETON_MODEL, + ATTACHMENT_MODEL }; +extern const char* MODEL_TYPE_NAMES[]; + class ModelHandler : public QObject { Q_OBJECT public: diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 7a70b743bd..eed33fda23 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -48,7 +48,7 @@ void PreferencesDialog::openHeadModelBrowser() { setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint); show(); - ModelsBrowser modelBrowser(Head); + ModelsBrowser modelBrowser(HEAD_MODEL); connect(&modelBrowser, &ModelsBrowser::selected, this, &PreferencesDialog::setHeadUrl); modelBrowser.browse(); @@ -60,7 +60,7 @@ void PreferencesDialog::openBodyModelBrowser() { setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint); show(); - ModelsBrowser modelBrowser(Skeleton); + ModelsBrowser modelBrowser(SKELETON_MODEL); connect(&modelBrowser, &ModelsBrowser::selected, this, &PreferencesDialog::setSkeletonUrl); modelBrowser.browse(); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index be47aed1ba..78b33571e8 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -81,6 +81,7 @@ const glm::vec3 vec3Zero(0.0f); class QNetworkAccessManager; +class AttachmentData; class JointData; class AvatarData : public QObject { @@ -275,6 +276,7 @@ protected: QUrl _faceModelURL; QUrl _skeletonModelURL; + QVector _attachmentData; QString _displayName; QRect _displayNameBoundingRect; @@ -309,4 +311,13 @@ public: glm::quat rotation; }; +class AttachmentData { +public: + QUrl modelURL; + int jointIndex; + glm::vec3 translation; + glm::quat rotation; + float scale; +}; + #endif // hifi_AvatarData_h diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 0955759097..0785b81581 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -49,6 +49,8 @@ PacketVersion versionForPacketType(PacketType type) { switch (type) { case PacketTypeAvatarData: return 3; + case PacketTypeAvatarIdentity: + return 1; case PacketTypeEnvironmentData: return 1; case PacketTypeParticleData: