first cut at new avatar preferences UI

This commit is contained in:
ZappoMan 2015-04-03 14:31:43 -07:00
parent 88ee1c313a
commit aaf191b6dc
7 changed files with 418 additions and 77 deletions

View file

@ -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());

View file

@ -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);

View file

@ -31,6 +31,7 @@
#include <ShapeCollider.h>
#include <SharedUtil.h>
#include <TextRenderer.h>
#include <UserActivityLogger.h>
#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> 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>& attachmentData) {
Avatar::setAttachmentData(attachmentData);
if (QThread::currentThread() != thread()) {

View file

@ -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>& 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

View file

@ -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<AvatarManager>()->getMyAvatar()->useHeadAndBodyURLs(headURL, bodyURL);
}
void PreferencesDialog::skeletonURLChanged(const QString& newValue) {
void PreferencesDialog::useFullAvatar(bool checked) {
setUseFullAvatar(checked);
QUrl fullAvatarURL(ui.fullAvatarURLEdit->text());
DependencyManager::get<AvatarManager>()->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);
}
}

View file

@ -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);
};

View file

@ -198,8 +198,194 @@
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QRadioButton" name="useFullAvatar">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>28</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="text">
<string>Use single avatar with Body and Head</string>
</property>
<property name="iconSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_fullAvatar">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QLabel" name="headLabel">
<widget class="QLabel" name="fullAvatarNameLabel">
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="text">
<string>Full Avatar</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
<property name="buddy">
<cstring>fullAvatarURLEdit</cstring>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_fullAvatar">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="fullAvatarURLEdit">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_fullAvatar">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="buttonBrowseFullAvatar">
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="text">
<string>Browse</string>
</property>
<property name="iconSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QRadioButton" name="useSeparateBodyAndHead">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>28</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="text">
<string>Use separate Body and Head avatar files</string>
</property>
<property name="iconSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_separateParts_head">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QLabel" name="headNameLabel">
<property name="font">
<font>
<family>Arial</family>
@ -211,11 +397,11 @@
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
<property name="buddy">
<cstring>snapshotLocationEdit</cstring>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
@ -260,21 +446,28 @@
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<layout class="QVBoxLayout" name="verticalLayout_separateParts_body">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QLabel" name="headLabel_2">
<widget class="QLabel" name="bodyNameLabel">
<property name="font">
<font>
<family>Arial</family>
@ -290,9 +483,10 @@
<cstring>skeletonURLEdit</cstring>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<layout class="QHBoxLayout" name="horizontalLayout_skeletonURL">
<property name="spacing">
<number>0</number>
</property>
@ -344,6 +538,20 @@
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
@ -665,6 +873,7 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="sendDataCheckBox">
<property name="sizePolicy">