From aaf191b6dc5b27bb6d32a4c1e15191dae9689e31 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 3 Apr 2015 14:31:43 -0700 Subject: [PATCH] first cut at new avatar preferences UI --- interface/src/Application.cpp | 29 +--- interface/src/Application.h | 5 +- interface/src/avatar/MyAvatar.cpp | 71 +++++++- interface/src/avatar/MyAvatar.h | 24 ++- interface/src/ui/PreferencesDialog.cpp | 125 ++++++++++---- interface/src/ui/PreferencesDialog.h | 18 +- interface/ui/preferencesDialog.ui | 223 ++++++++++++++++++++++++- 7 files changed, 418 insertions(+), 77 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f0324b7361..c2560a9a54 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3743,30 +3743,16 @@ bool Application::askToSetAvatarUrl(const QString& url) { if (msgBox.clickedButton() == headButton) { qDebug() << "Chose to use for head: " << url; - _myAvatar->setFaceModelURL(url); - UserActivityLogger::getInstance().changedModel("head", url); - _myAvatar->sendIdentityPacket(); - emit faceURLChanged(url); + _myAvatar->useHeadURL(url); + emit headURLChanged(url); } else if (msgBox.clickedButton() == bodyButton) { qDebug() << "Chose to use for body: " << url; - _myAvatar->setSkeletonModelURL(url); - // if the head is empty, reset it to the default head. - if (_myAvatar->getFaceModelURLString().isEmpty()) { - _myAvatar->setFaceModelURL(DEFAULT_HEAD_MODEL_URL); - emit faceURLChanged(DEFAULT_HEAD_MODEL_URL.toString()); - UserActivityLogger::getInstance().changedModel("head", DEFAULT_HEAD_MODEL_URL.toString()); - } - UserActivityLogger::getInstance().changedModel("skeleton", url); - _myAvatar->sendIdentityPacket(); - emit skeletonURLChanged(url); + _myAvatar->useBodyURL(url); + emit bodyURLChanged(url); } else if (msgBox.clickedButton() == bodyAndHeadButton) { qDebug() << "Chose to use for body + head: " << url; - _myAvatar->setFaceModelURL(QString()); - _myAvatar->setSkeletonModelURL(url); - UserActivityLogger::getInstance().changedModel("skeleton", url); - _myAvatar->sendIdentityPacket(); - emit faceURLChanged(QString()); - emit skeletonURLChanged(url); + _myAvatar->useFullAvatarURL(url); + emit fullAvatarURLChanged(url); } else { qDebug() << "Declined to use the avatar: " << url; } @@ -4281,8 +4267,7 @@ void Application::checkSkeleton() { msgBox.setIcon(QMessageBox::Warning); msgBox.exec(); - _myAvatar->setSkeletonModelURL(DEFAULT_BODY_MODEL_URL); - _myAvatar->sendIdentityPacket(); + _myAvatar->useBodyURL(DEFAULT_BODY_MODEL_URL); } else { _myAvatar->updateCharacterController(); _physicsEngine.setCharacterController(_myAvatar->getCharacterController()); diff --git a/interface/src/Application.h b/interface/src/Application.h index e9b8deff55..387de78e49 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -335,8 +335,9 @@ signals: void checkBackgroundDownloads(); void domainConnectionRefused(const QString& reason); - void faceURLChanged(const QString& newValue); - void skeletonURLChanged(const QString& newValue); + void headURLChanged(const QString& newValue); + void bodyURLChanged(const QString& newValue); + void fullAvatarURLChanged(const QString& newValue); public slots: void domainChanged(const QString& domainHostname); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index f92523c58f..418f978451 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include "Application.h" #include "AvatarManager.h" @@ -604,9 +605,11 @@ void MyAvatar::saveData() { settings.setValue("leanScale", _leanScale); settings.setValue("scale", _targetScale); - - settings.setValue("faceModelURL", _faceModelURL); - settings.setValue("skeletonModelURL", _skeletonModelURL); + + settings.setValue("useFullAvatar", _useFullAvatar); + settings.setValue("fullAvatarURL", _fullAvatarURLFromPreferences); + settings.setValue("faceModelURL", _headURLFromPreferences); + settings.setValue("skeletonModelURL", _skeletonURLFromPreferences); settings.beginWriteArray("attachmentData"); for (int i = 0; i < _attachmentData.size(); i++) { @@ -667,9 +670,26 @@ void MyAvatar::loadData() { _targetScale = loadSetting(settings, "scale", 1.0f); setScale(_scale); Application::getInstance()->getCamera()->setScale(_scale); + + + // The old preferences only stored the face and skeleton URLs, we didn't track if the user wanted to use 1 or 2 urls + // for their avatar, So we need to attempt to detect this old case and set our new preferences accordingly. If + // the head URL is empty, then we will assume they are using a full url... + _headURLFromPreferences = settings.value("faceModelURL", DEFAULT_HEAD_MODEL_URL).toUrl(); - setFaceModelURL(settings.value("faceModelURL", DEFAULT_HEAD_MODEL_URL).toUrl()); - setSkeletonModelURL(settings.value("skeletonModelURL").toUrl()); + bool assumeFullAvatar = _headURLFromPreferences.isEmpty(); + + _useFullAvatar = settings.value("useFullAvatar", assumeFullAvatar).toBool(); + _fullAvatarURLFromPreferences = settings.value("fullAvatarURL").toUrl(); + _skeletonURLFromPreferences = settings.value("skeletonModelURL").toUrl(); + + if (_useFullAvatar) { + setFaceModelURL(QUrl()); + setSkeletonModelURL(_fullAvatarURLFromPreferences); + } else { + setFaceModelURL(_headURLFromPreferences); + setSkeletonModelURL(_skeletonURLFromPreferences); + } QVector attachmentData; int attachmentCount = settings.beginReadArray("attachmentData"); @@ -902,6 +922,47 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { _billboardValid = false; } +void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL) { + _useFullAvatar = true; + _fullAvatarURLFromPreferences = fullAvatarURL; + + if (!getFaceModelURLString().isEmpty()) { + setFaceModelURL(QString()); + } + + if (fullAvatarURL != getSkeletonModelURL()) { + setSkeletonModelURL(fullAvatarURL); + UserActivityLogger::getInstance().changedModel("skeleton", fullAvatarURL.toString()); + } + sendIdentityPacket(); +} + +void MyAvatar::useHeadURL(const QUrl& headURL) { + useHeadAndBodyURLs(headURL, _skeletonURLFromPreferences); +} + +void MyAvatar::useBodyURL(const QUrl& bodyURL) { + useHeadAndBodyURLs(_headURLFromPreferences, bodyURL); +} + +void MyAvatar::useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL) { + _useFullAvatar = false; + _headURLFromPreferences = headURL; + _skeletonURLFromPreferences = bodyURL; + + if (headURL != getFaceModelURL()) { + setFaceModelURL(headURL); + UserActivityLogger::getInstance().changedModel("head", headURL.toString()); + } + + if (bodyURL != getSkeletonModelURL()) { + setSkeletonModelURL(bodyURL); + UserActivityLogger::getInstance().changedModel("skeleton", bodyURL.toString()); + } + sendIdentityPacket(); +} + + void MyAvatar::setAttachmentData(const QVector& attachmentData) { Avatar::setAttachmentData(attachmentData); if (QThread::currentThread() != thread()) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 2bc3a4e4ba..57c8ca9e96 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -116,8 +116,17 @@ public: virtual void setJointData(int index, const glm::quat& rotation); virtual void clearJointData(int index); virtual void clearJointsData(); - virtual void setFaceModelURL(const QUrl& faceModelURL); - virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); + + void useFullAvatarURL(const QUrl& fullAvatarURL); + void useHeadURL(const QUrl& headURL); + void useBodyURL(const QUrl& bodyURL); + void useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL); + + bool getUseFullAvatar() const { return _useFullAvatar; } + const QUrl& getFullAvatarURLFromPreferences() const { return _fullAvatarURLFromPreferences; } + const QUrl& getHeadURLFromPreferences() const { return _headURLFromPreferences; } + const QUrl& getBodyURLFromPreferences() const { return _skeletonURLFromPreferences; } + virtual void setAttachmentData(const QVector& attachmentData); virtual glm::vec3 getSkeletonPosition() const; @@ -185,6 +194,11 @@ protected: virtual void renderAttachments(RenderMode renderMode, RenderArgs* args); private: + + // These are made private for MyAvatar so that you will use the "use" methods instead + virtual void setFaceModelURL(const QUrl& faceModelURL); + virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); + float _turningKeyPressTime; glm::vec3 _gravity; @@ -229,6 +243,12 @@ private: void updatePosition(float deltaTime); void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency); void maybeUpdateBillboard(); + + // Avatar Preferences + bool _useFullAvatar = false; + QUrl _fullAvatarURLFromPreferences; + QUrl _headURLFromPreferences; + QUrl _skeletonURLFromPreferences; }; #endif // hifi_MyAvatar_h diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index bd32fc7c34..4879bbfffc 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -43,28 +43,71 @@ PreferencesDialog::PreferencesDialog(QWidget* parent) : connect(ui.buttonBrowseHead, &QPushButton::clicked, this, &PreferencesDialog::openHeadModelBrowser); connect(ui.buttonBrowseBody, &QPushButton::clicked, this, &PreferencesDialog::openBodyModelBrowser); + connect(ui.buttonBrowseFullAvatar, &QPushButton::clicked, this, &PreferencesDialog::openFullAvatarModelBrowser); + connect(ui.buttonBrowseLocation, &QPushButton::clicked, this, &PreferencesDialog::openSnapshotLocationBrowser); connect(ui.buttonBrowseScriptsLocation, &QPushButton::clicked, this, &PreferencesDialog::openScriptsLocationBrowser); - connect(ui.buttonReloadDefaultScripts, &QPushButton::clicked, - Application::getInstance(), &Application::loadDefaultScripts); + connect(ui.buttonReloadDefaultScripts, &QPushButton::clicked, Application::getInstance(), &Application::loadDefaultScripts); - - connect(Application::getInstance(), &Application::faceURLChanged, this, &PreferencesDialog::faceURLChanged); - connect(Application::getInstance(), &Application::skeletonURLChanged, this, &PreferencesDialog::skeletonURLChanged); + connect(ui.useSeparateBodyAndHead, &QRadioButton::clicked, this, &PreferencesDialog::useSeparateBodyAndHead); + connect(ui.useFullAvatar, &QRadioButton::clicked, this, &PreferencesDialog::useFullAvatar); + + + connect(Application::getInstance(), &Application::headURLChanged, this, &PreferencesDialog::headURLChanged); + connect(Application::getInstance(), &Application::bodyURLChanged, this, &PreferencesDialog::bodyURLChanged); + connect(Application::getInstance(), &Application::fullAvatarURLChanged, this, &PreferencesDialog::fullAvatarURLChanged); // move dialog to left side move(parentWidget()->geometry().topLeft()); setFixedHeight(parentWidget()->size().height() - PREFERENCES_HEIGHT_PADDING); + + ui.bodyNameLabel->setText("Body - name of model here"); + ui.headNameLabel->setText("Head - name of model here"); + ui.fullAvatarNameLabel->setText("Full Avatar - name of model here"); + + UIUtil::scaleWidgetFontSizes(this); } -void PreferencesDialog::faceURLChanged(const QString& newValue) { - ui.faceURLEdit->setText(newValue); +void PreferencesDialog::useSeparateBodyAndHead(bool checked) { + setUseFullAvatar(!checked); + + QUrl headURL(ui.faceURLEdit->text()); + QUrl bodyURL(ui.skeletonURLEdit->text()); + + DependencyManager::get()->getMyAvatar()->useHeadAndBodyURLs(headURL, bodyURL); } -void PreferencesDialog::skeletonURLChanged(const QString& newValue) { +void PreferencesDialog::useFullAvatar(bool checked) { + setUseFullAvatar(checked); + QUrl fullAvatarURL(ui.fullAvatarURLEdit->text()); + DependencyManager::get()->getMyAvatar()->useFullAvatarURL(fullAvatarURL); +} + +void PreferencesDialog::setUseFullAvatar(bool useFullAvatar) { + _useFullAvatar = useFullAvatar; + ui.faceURLEdit->setEnabled(!_useFullAvatar); + ui.skeletonURLEdit->setEnabled(!_useFullAvatar); + ui.fullAvatarURLEdit->setEnabled(_useFullAvatar); + + ui.useFullAvatar->setChecked(_useFullAvatar); + ui.useSeparateBodyAndHead->setChecked(!_useFullAvatar); +} + +void PreferencesDialog::headURLChanged(const QString& newValue) { + ui.faceURLEdit->setText(newValue); + setUseFullAvatar(false); +} + +void PreferencesDialog::bodyURLChanged(const QString& newValue) { ui.skeletonURLEdit->setText(newValue); + setUseFullAvatar(false); +} + +void PreferencesDialog::fullAvatarURLChanged(const QString& newValue) { + ui.fullAvatarURLEdit->setText(newValue); + setUseFullAvatar(true); } void PreferencesDialog::accept() { @@ -82,6 +125,16 @@ void PreferencesDialog::setSkeletonUrl(QString modelUrl) { ui.skeletonURLEdit->setText(modelUrl); } +void PreferencesDialog::openFullAvatarModelBrowser() { + auto MARKETPLACE_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/marketplace?category=avatars"; + auto WIDTH = 900; + auto HEIGHT = 700; + if (!_marketplaceWindow) { + _marketplaceWindow = new WebWindowClass("Marketplace", MARKETPLACE_URL, WIDTH, HEIGHT, false); + } + _marketplaceWindow->setVisible(true); +} + void PreferencesDialog::openHeadModelBrowser() { auto MARKETPLACE_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/marketplace?category=avatars"; auto WIDTH = 900; @@ -143,11 +196,18 @@ void PreferencesDialog::loadPreferences() { _displayNameString = myAvatar->getDisplayName(); ui.displayNameEdit->setText(_displayNameString); - _faceURLString = myAvatar->getHead()->getFaceModel().getURL().toString(); - ui.faceURLEdit->setText(_faceURLString); - _skeletonURLString = myAvatar->getSkeletonModel().getURL().toString(); - ui.skeletonURLEdit->setText(_skeletonURLString); + _useFullAvatar = myAvatar->getUseFullAvatar(); + _fullAvatarURLString = myAvatar->getFullAvatarURLFromPreferences().toString(); + _headURLString = myAvatar->getHeadURLFromPreferences().toString(); + _bodyURLString = myAvatar->getBodyURLFromPreferences().toString(); + + ui.fullAvatarURLEdit->setText(_fullAvatarURLString); + ui.faceURLEdit->setText(_headURLString); + ui.skeletonURLEdit->setText(_bodyURLString); + setUseFullAvatar(_useFullAvatar); + + // TODO: load the names for the models. ui.sendDataCheckBox->setChecked(!menuInstance->isOptionChecked(MenuOption::DisableActivityLogger)); @@ -217,31 +277,26 @@ void PreferencesDialog::savePreferences() { shouldDispatchIdentityPacket = true; } - auto AVATAR_FILE_EXTENSION = ".fst"; - - QUrl faceModelURL(ui.faceURLEdit->text()); - QString faceModelURLString = faceModelURL.toString(); - if (faceModelURLString != _faceURLString) { - if (faceModelURLString.isEmpty() || faceModelURLString.toLower().contains(AVATAR_FILE_EXTENSION)) { - // change the faceModelURL in the profile, it will also update this user's BlendFace - myAvatar->setFaceModelURL(faceModelURL); - UserActivityLogger::getInstance().changedModel("head", faceModelURLString); - shouldDispatchIdentityPacket = true; - } else { - qDebug() << "ERROR: Head model not FST or blank - " << faceModelURLString; - } - } + QUrl headURL(ui.faceURLEdit->text()); + QString headURLString = headURL.toString(); - QUrl skeletonModelURL(ui.skeletonURLEdit->text()); - QString skeletonModelURLString = skeletonModelURL.toString(); - if (skeletonModelURLString != _skeletonURLString) { - if (skeletonModelURLString.isEmpty() || skeletonModelURLString.toLower().contains(AVATAR_FILE_EXTENSION)) { - // change the skeletonModelURL in the profile, it will also update this user's Body - myAvatar->setSkeletonModelURL(skeletonModelURL); - UserActivityLogger::getInstance().changedModel("skeleton", skeletonModelURLString); - shouldDispatchIdentityPacket = true; + QUrl bodyURL(ui.skeletonURLEdit->text()); + QString bodyURLString = bodyURL.toString(); + + QUrl fullAvatarURL(ui.fullAvatarURLEdit->text()); + QString fullAvatarURLString = fullAvatarURL.toString(); + + bool somethingChanged = + _useFullAvatar != myAvatar->getUseFullAvatar() || + fullAvatarURLString != myAvatar->getFullAvatarURLFromPreferences().toString() || + headURLString != myAvatar->getHeadURLFromPreferences().toString() || + bodyURLString != myAvatar->getBodyURLFromPreferences().toString(); + + if (somethingChanged) { + if (_useFullAvatar) { + myAvatar->useFullAvatarURL(fullAvatarURL); } else { - qDebug() << "ERROR: Skeleton model not FST or blank - " << skeletonModelURLString; + myAvatar->useHeadAndBodyURLs(headURL, bodyURL); } } diff --git a/interface/src/ui/PreferencesDialog.h b/interface/src/ui/PreferencesDialog.h index 4daa2d9696..961ef6287d 100644 --- a/interface/src/ui/PreferencesDialog.h +++ b/interface/src/ui/PreferencesDialog.h @@ -33,10 +33,17 @@ private: void savePreferences(); void openHeadModelBrowser(); void openBodyModelBrowser(); + void openFullAvatarModelBrowser(); + void setUseFullAvatar(bool useFullAvatar); Ui_PreferencesDialog ui; - QString _faceURLString; - QString _skeletonURLString; + + bool _useFullAvatar; + QString _headURLString; + QString _bodyURLString; + QString _fullAvatarURLString; + + QString _displayNameString; WebWindowClass* _marketplaceWindow = NULL; @@ -47,8 +54,11 @@ private slots: void setSkeletonUrl(QString modelUrl); void openSnapshotLocationBrowser(); void openScriptsLocationBrowser(); - void faceURLChanged(const QString& newValue); - void skeletonURLChanged(const QString& newValue); + void headURLChanged(const QString& newValue); + void bodyURLChanged(const QString& newValue); + void fullAvatarURLChanged(const QString& newValue); + void useSeparateBodyAndHead(bool checked); + void useFullAvatar(bool checked); }; diff --git a/interface/ui/preferencesDialog.ui b/interface/ui/preferencesDialog.ui index d295d094c2..4a0b1e961a 100644 --- a/interface/ui/preferencesDialog.ui +++ b/interface/ui/preferencesDialog.ui @@ -198,8 +198,194 @@ 7 + + + + + + + + 0 + 0 + + + + + 32 + 28 + + + + + 0 + 0 + + + + + Arial + + + + Use single avatar with Body and Head + + + + 0 + 0 + + + + + + + + + + + 0 + + + 10 + + + 7 + + + 7 + + + - + + + + Arial + + + + Full Avatar + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + fullAvatarURLEdit + + + + + + + + 0 + + + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + + Arial + + + + Browse + + + + 0 + 0 + + + + + + + + + + + + + + 0 + 0 + + + + + 32 + 28 + + + + + 0 + 0 + + + + + Arial + + + + Use separate Body and Head avatar files + + + + 0 + 0 + + + + + + + + + + 0 + + + 10 + + + 7 + + + 7 + + + + + + Arial @@ -211,11 +397,11 @@ Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft - - snapshotLocationEdit - + + + @@ -260,21 +446,28 @@ + + + - + 0 + + 10 + 7 7 + - + Arial @@ -290,9 +483,10 @@ skeletonURLEdit + - + 0 @@ -344,6 +538,20 @@ + + + + + + + + + + + + + + @@ -665,6 +873,7 @@ +