merge upstream/master into andrew/bispinor

This commit is contained in:
Andrew Meadows 2015-04-16 12:07:22 -07:00
commit 83d54eb46b
36 changed files with 1293 additions and 332 deletions

4
.gitignore vendored
View file

@ -46,4 +46,6 @@ libraries/audio-client/external/*/*
gvr-interface/assets/oculussig*
gvr-interface/libs/*
TAGS
# ignore files for various dev environments
TAGS
*.swp

View file

@ -549,14 +549,18 @@ void AudioMixer::readPendingDatagram(const QByteArray& receivedPacket, const Hif
nodeList->findNodeAndUpdateWithDataFromPacket(receivedPacket);
} else if (mixerPacketType == PacketTypeMuteEnvironment) {
QByteArray packet = receivedPacket;
populatePacketHeader(packet, PacketTypeMuteEnvironment);
nodeList->eachNode([&](const SharedNodePointer& node){
if (node->getType() == NodeType::Agent && node->getActiveSocket() && node->getLinkedData() && node != nodeList->sendingNodeForPacket(receivedPacket)) {
nodeList->writeDatagram(packet, packet.size(), node);
}
});
SharedNodePointer sendingNode = nodeList->sendingNodeForPacket(receivedPacket);
if (sendingNode->getCanAdjustLocks()) {
QByteArray packet = receivedPacket;
populatePacketHeader(packet, PacketTypeMuteEnvironment);
nodeList->eachNode([&](const SharedNodePointer& node){
if (node->getType() == NodeType::Agent && node->getActiveSocket() &&
node->getLinkedData() && node != sendingNode) {
nodeList->writeDatagram(packet, packet.size(), node);
}
});
}
} else {
// let processNodeData handle it.
nodeList->processNodeData(senderSockAddr, receivedPacket);

View file

@ -508,18 +508,22 @@
// To make this work we block the first mouseup event after the elements
// received focus. If we block all mouseup events the user will not
// be able to click within the selected text.
// We also check to see if the value has changed to make sure we aren't
// blocking a mouse-up event when clicking on an input spinner.
var els = document.querySelectorAll("input, textarea");
for (var i = 0; i < els.length; i++) {
var clicked = false;
var originalText;
els[i].onfocus = function() {
originalText = this.value;
this.select();
clicked = false;
};
els[i].onmouseup = function(e) {
if (!clicked) {
if (!clicked && originalText == this.value) {
e.preventDefault();
clicked = true;
}
clicked = true;
};
}
}

View file

@ -12,17 +12,27 @@
//
var createdRenderMenu = false;
var createdGeneratedAudioMenu = false;
var ENTITIES_MENU = "Developer > Entities";
var DEVELOPER_MENU = "Developer";
var ENTITIES_MENU = DEVELOPER_MENU + " > Entities";
var COLLISION_UPDATES_TO_SERVER = "Don't send collision updates to server";
var RENDER_MENU = "Developer > Render";
var RENDER_MENU = DEVELOPER_MENU + " > Render";
var ENTITIES_ITEM = "Entities";
var AVATARS_ITEM = "Avatars";
var AUDIO_MENU = DEVELOPER_MENU + " > Audio";
var AUDIO_SOURCE_INJECT = "Generated Audio";
var AUDIO_SOURCE_MENU = AUDIO_MENU + " > Generated Audio Source";
var AUDIO_SOURCE_PINK_NOISE = "Pink Noise";
var AUDIO_SOURCE_SINE_440 = "Sine 440hz";
function setupMenus() {
if (!Menu.menuExists("Developer")) {
Menu.addMenu("Developer");
if (!Menu.menuExists(DEVELOPER_MENU)) {
Menu.addMenu(DEVELOPER_MENU);
}
if (!Menu.menuExists(ENTITIES_MENU)) {
Menu.addMenu(ENTITIES_MENU);
@ -54,6 +64,20 @@ function setupMenus() {
if (!Menu.menuItemExists(RENDER_MENU, AVATARS_ITEM)) {
Menu.addMenuItem({ menuName: RENDER_MENU, menuItemName: AVATARS_ITEM, isCheckable: true, isChecked: Scene.shouldRenderAvatars })
}
if (!Menu.menuExists(AUDIO_MENU)) {
Menu.addMenu(AUDIO_MENU);
}
if (!Menu.menuItemExists(AUDIO_MENU, AUDIO_SOURCE_INJECT)) {
Menu.addMenuItem({ menuName: AUDIO_MENU, menuItemName: AUDIO_SOURCE_INJECT, isCheckable: true, isChecked: false });
Menu.addMenu(AUDIO_SOURCE_MENU);
Menu.addMenuItem({ menuName: AUDIO_SOURCE_MENU, menuItemName: AUDIO_SOURCE_PINK_NOISE, isCheckable: true, isChecked: false });
Menu.addMenuItem({ menuName: AUDIO_SOURCE_MENU, menuItemName: AUDIO_SOURCE_SINE_440, isCheckable: true, isChecked: false });
Menu.setIsOptionChecked(AUDIO_SOURCE_PINK_NOISE, true);
Audio.selectPinkNoise();
createdGeneratedAudioMenu = true;
}
}
Menu.menuItemEvent.connect(function (menuItem) {
@ -67,7 +91,15 @@ Menu.menuItemEvent.connect(function (menuItem) {
Scene.shouldRenderEntities = Menu.isOptionChecked(ENTITIES_ITEM);
} else if (menuItem == AVATARS_ITEM) {
Scene.shouldRenderAvatars = Menu.isOptionChecked(AVATARS_ITEM);
}
} else if (menuItem == AUDIO_SOURCE_INJECT && !createdGeneratedAudioMenu) {
Audio.injectGeneratedNoise(Menu.isOptionChecked(AUDIO_SOURCE_INJECT));
} else if (menuItem == AUDIO_SOURCE_PINK_NOISE && !createdGeneratedAudioMenu) {
Audio.selectPinkNoise();
Menu.setIsOptionChecked(AUDIO_SOURCE_SINE_440, false);
} else if (menuItem == AUDIO_SOURCE_SINE_440 && !createdGeneratedAudioMenu) {
Audio.selectSine440();
Menu.setIsOptionChecked(AUDIO_SOURCE_PINK_NOISE, false);
}
});
Scene.shouldRenderAvatarsChanged.connect(function(shouldRenderAvatars) {
@ -87,6 +119,12 @@ function scriptEnding() {
Menu.removeMenuItem(RENDER_MENU, ENTITIES_ITEM);
Menu.removeMenuItem(RENDER_MENU, AVATARS_ITEM);
}
if (createdGeneratedAudioMenu) {
Audio.injectGeneratedNoise(false);
Menu.removeMenuItem(AUDIO_MENU, AUDIO_SOURCE_INJECT);
Menu.removeMenu(AUDIO_SOURCE_MENU);
}
}
setupMenus();

View file

@ -612,6 +612,10 @@ void Application::cleanupBeforeQuit() {
// destroy the AudioClient so it and its thread have a chance to go down safely
DependencyManager::destroy<AudioClient>();
#ifdef HAVE_DDE
DependencyManager::destroy<DdeFaceTracker>();
#endif
}
Application::~Application() {
@ -1743,8 +1747,10 @@ FaceTracker* Application::getActiveFaceTracker() {
void Application::setActiveFaceTracker() {
#ifdef HAVE_FACESHIFT
DependencyManager::get<Faceshift>()->setTCPEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift));
#endif
#endif
#ifdef HAVE_DDE
DependencyManager::get<DdeFaceTracker>()->setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::DDEFaceRegression));
#endif
#ifdef HAVE_VISAGE
DependencyManager::get<Visage>()->updateEnabled();
#endif
@ -3713,17 +3719,7 @@ bool Application::askToSetAvatarUrl(const QString& url) {
}
// Download the FST file, to attempt to determine its model type
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest = QNetworkRequest(url);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
QNetworkReply* reply = networkAccessManager.get(networkRequest);
qCDebug(interfaceapp) << "Downloading avatar file at " << url;
QEventLoop loop;
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
QByteArray fstContents = reply->readAll();
delete reply;
QVariantHash fstMapping = FSTReader::readMapping(fstContents);
QVariantHash fstMapping = FSTReader::downloadMapping(url);
FSTReader::ModelType modelType = FSTReader::predictModelType(fstMapping);
@ -3734,26 +3730,27 @@ bool Application::askToSetAvatarUrl(const QString& url) {
QPushButton* bodyButton = NULL;
QPushButton* bodyAndHeadButton = NULL;
QString modelName = fstMapping["name"].toString();
QString message;
QString typeInfo;
switch (modelType) {
case FSTReader::HEAD_MODEL:
message = QString("Would you like to use '") + fstMapping["name"].toString() + QString("' for your avatar head?");
message = QString("Would you like to use '") + modelName + QString("' for your avatar head?");
headButton = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole);
break;
case FSTReader::BODY_ONLY_MODEL:
message = QString("Would you like to use '") + fstMapping["name"].toString() + QString("' for your avatar body?");
message = QString("Would you like to use '") + modelName + QString("' for your avatar body?");
bodyButton = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole);
break;
case FSTReader::HEAD_AND_BODY_MODEL:
message = QString("Would you like to use '") + fstMapping["name"].toString() + QString("' for your avatar?");
message = QString("Would you like to use '") + modelName + QString("' for your avatar?");
bodyAndHeadButton = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole);
break;
default:
message = QString("Would you like to use '") + fstMapping["name"].toString() + QString("' for some part of your avatar head?");
message = QString("Would you like to use '") + modelName + QString("' for some part of your avatar head?");
headButton = msgBox.addButton(tr("Use for Head"), QMessageBox::ActionRole);
bodyButton = msgBox.addButton(tr("Use for Body"), QMessageBox::ActionRole);
bodyAndHeadButton = msgBox.addButton(tr("Use for Body and Head"), QMessageBox::ActionRole);
@ -3766,34 +3763,18 @@ bool Application::askToSetAvatarUrl(const QString& url) {
msgBox.exec();
if (msgBox.clickedButton() == headButton) {
qCDebug(interfaceapp) << "Chose to use for head: " << url;
_myAvatar->setFaceModelURL(url);
UserActivityLogger::getInstance().changedModel("head", url);
_myAvatar->sendIdentityPacket();
emit faceURLChanged(url);
_myAvatar->useHeadURL(url, modelName);
emit headURLChanged(url, modelName);
} else if (msgBox.clickedButton() == bodyButton) {
qCDebug(interfaceapp) << "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, modelName);
emit bodyURLChanged(url, modelName);
} else if (msgBox.clickedButton() == bodyAndHeadButton) {
qCDebug(interfaceapp) << "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, modelName);
emit fullAvatarURLChanged(url, modelName);
} else {
qCDebug(interfaceapp) << "Declined to use the avatar: " << url;
}
return true;
}
@ -4305,8 +4286,7 @@ void Application::checkSkeleton() {
msgBox.setIcon(QMessageBox::Warning);
msgBox.exec();
_myAvatar->setSkeletonModelURL(DEFAULT_BODY_MODEL_URL);
_myAvatar->sendIdentityPacket();
_myAvatar->useBodyURL(DEFAULT_BODY_MODEL_URL, "Default");
} else {
_myAvatar->updateCharacterController();
_physicsEngine.setCharacterController(_myAvatar->getCharacterController());

View file

@ -210,6 +210,8 @@ public:
bool getLastMouseMoveWasSimulated() const { return _lastMouseMoveWasSimulated; }
FaceTracker* getActiveFaceTracker();
void setActiveFaceTracker();
QSystemTrayIcon* getTrayIcon() { return _trayIcon; }
ApplicationOverlay& getApplicationOverlay() { return _applicationOverlay; }
Overlays& getOverlays() { return _overlays; }
@ -335,8 +337,9 @@ signals:
void checkBackgroundDownloads();
void domainConnectionRefused(const QString& reason);
void faceURLChanged(const QString& newValue);
void skeletonURLChanged(const QString& newValue);
void headURLChanged(const QString& newValue, const QString& modelName);
void bodyURLChanged(const QString& newValue, const QString& modelName);
void fullAvatarURLChanged(const QString& newValue, const QString& modelName);
void beforeAboutToQuit();
@ -390,8 +393,6 @@ public slots:
void notifyPacketVersionMismatch();
void setActiveFaceTracker();
void domainConnectionDenied(const QString& reason);
private slots:

View file

@ -55,6 +55,10 @@ void MainWindow::saveGeometry() {
}
}
void MainWindow::closeEvent(QCloseEvent* event) {
qApp->quit();
}
void MainWindow::moveEvent(QMoveEvent* event) {
emit windowGeometryChanged(QRect(event->pos(), size()));
QMainWindow::moveEvent(event);

View file

@ -30,6 +30,7 @@ signals:
void windowShown(bool shown);
protected:
virtual void closeEvent(QCloseEvent* event);
virtual void moveEvent(QMoveEvent* event);
virtual void resizeEvent(QResizeEvent* event);
virtual void showEvent(QShowEvent* event);

View file

@ -256,7 +256,7 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H, false,
qApp, SLOT(cameraMenuChanged()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools, Qt::META | Qt::Key_H,
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools, Qt::CTRL | Qt::Key_H,
false,
dialogsManager.data(),
SLOT(hmdTools(bool)));
@ -362,28 +362,36 @@ Menu::Menu() {
QAction* noFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::NoFaceTracking,
0, true,
qApp, SLOT(setActiveFaceTracker()));
this, SLOT(setActiveFaceTracker()));
faceTrackerGroup->addAction(noFaceTracker);
#ifdef HAVE_FACESHIFT
QAction* faceshiftFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::Faceshift,
0, false,
qApp, SLOT(setActiveFaceTracker()));
this, SLOT(setActiveFaceTracker()));
faceTrackerGroup->addAction(faceshiftFaceTracker);
#endif
#ifdef HAVE_DDE
QAction* ddeFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::DDEFaceRegression,
0, false,
qApp, SLOT(setActiveFaceTracker()));
this, SLOT(setActiveFaceTracker()));
faceTrackerGroup->addAction(ddeFaceTracker);
#endif
#ifdef HAVE_VISAGE
QAction* visageFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::Visage,
0, false,
qApp, SLOT(setActiveFaceTracker()));
this, SLOT(setActiveFaceTracker()));
faceTrackerGroup->addAction(visageFaceTracker);
#endif
}
#ifdef HAVE_DDE
faceTrackingMenu->addSeparator();
QAction* ddeFaceTrackerReset = addActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::ResetDDETracking,
Qt::CTRL | Qt::Key_Apostrophe,
DependencyManager::get<DdeFaceTracker>().data(), SLOT(resetTracking()));
ddeFaceTrackerReset->setVisible(false);
faceTrackingMenu->addAction(ddeFaceTrackerReset);
#endif
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderSkeletonCollisionShapes);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderHeadCollisionShapes);
@ -483,30 +491,6 @@ Menu::Menu() {
0,
audioIO.data(),
SLOT(sendMuteEnvironmentPacket()));
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioSourceInject,
0,
false,
audioIO.data(),
SLOT(toggleAudioSourceInject()));
QMenu* audioSourceMenu = audioDebugMenu->addMenu("Generated Audio Source");
{
QAction *pinkNoise = addCheckableActionToQMenuAndActionHash(audioSourceMenu, MenuOption::AudioSourcePinkNoise,
0,
false,
audioIO.data(),
SLOT(selectAudioSourcePinkNoise()));
QAction *sine440 = addCheckableActionToQMenuAndActionHash(audioSourceMenu, MenuOption::AudioSourceSine440,
0,
true,
audioIO.data(),
SLOT(selectAudioSourceSine440()));
QActionGroup* audioSourceGroup = new QActionGroup(audioSourceMenu);
audioSourceGroup->addAction(pinkNoise);
audioSourceGroup->addAction(sine440);
}
auto scope = DependencyManager::get<AudioScope>();
@ -1003,3 +987,11 @@ void Menu::visibilityChanged(Discoverability::Mode discoverabilityMode) {
qCDebug(interfaceapp) << "ERROR Menu::visibilityChanged() called with unrecognized value.";
}
}
void Menu::setActiveFaceTracker() {
#ifdef HAVE_DDE
bool isUsingDDE = Menu::getInstance()->isOptionChecked(MenuOption::DDEFaceRegression);
Menu::getInstance()->getActionForOption(MenuOption::ResetDDETracking)->setVisible(isUsingDDE);
#endif
qApp->setActiveFaceTracker();
}

View file

@ -68,6 +68,7 @@ public slots:
private slots:
void setVisibility();
void setActiveFaceTracker();
private:
static Menu* _instance;
@ -123,9 +124,6 @@ namespace MenuOption {
const QString AudioScopeTwentyFrames = "Twenty";
const QString AudioStats = "Audio Stats";
const QString AudioStatsShowInjectedStreams = "Audio Stats Show Injected Streams";
const QString AudioSourceInject = "Generated Audio";
const QString AudioSourcePinkNoise = "Pink Noise";
const QString AudioSourceSine440 = "Sine 440hz";
const QString BandwidthDetails = "Bandwidth Details";
const QString BlueSpeechSphere = "Blue Sphere While Speaking";
const QString BookmarkLocation = "Bookmark Location";
@ -230,6 +228,7 @@ namespace MenuOption {
const QString RenderAmbientLight8 = "CAMPUS_SUNSET";
const QString RenderAmbientLight9 = "FUNSTON_BEACH_SUNSET";
const QString ResetAvatarSize = "Reset Avatar Size";
const QString ResetDDETracking = "Reset DDE Tracking";
const QString ResetSensors = "Reset Sensors";
const QString RunningScripts = "Running Scripts";
const QString RunTimingTests = "Run Timing Tests";

View file

@ -31,6 +31,7 @@
#include <ShapeCollider.h>
#include <SharedUtil.h>
#include <TextRenderer.h>
#include <UserActivityLogger.h>
#include "Application.h"
#include "AvatarManager.h"
@ -126,7 +127,7 @@ void MyAvatar::reset() {
_skeletonModel.reset();
getHead()->reset();
_velocity = glm::vec3(0.0f);
_targetVelocity = glm::vec3(0.0f);
setThrust(glm::vec3(0.0f));
// Reset the pitch and roll components of the avatar's orientation, preserve yaw direction
glm::vec3 eulers = safeEulerAngles(getOrientation());
@ -605,10 +606,15 @@ 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.setValue("headModelName", _headModelName);
settings.setValue("bodyModelName", _bodyModelName);
settings.setValue("fullAvatarModelName", _fullAvatarModelName);
settings.beginWriteArray("attachmentData");
for (int i = 0; i < _attachmentData.size(); i++) {
settings.setArrayIndex(i);
@ -640,7 +646,6 @@ void MyAvatar::saveData() {
settings.setValue("firstFrame", pointer->getFirstFrame());
settings.setValue("lastFrame", pointer->getLastFrame());
settings.setValue("maskedJoints", pointer->getMaskedJoints());
settings.setValue("running", pointer->getLoop() && pointer->isRunning());
}
settings.endArray();
@ -669,9 +674,54 @@ void MyAvatar::loadData() {
_targetScale = loadSetting(settings, "scale", 1.0f);
setScale(_scale);
Application::getInstance()->getCamera()->setScale(_scale);
setFaceModelURL(settings.value("faceModelURL", DEFAULT_HEAD_MODEL_URL).toUrl());
setSkeletonModelURL(settings.value("skeletonModelURL").toUrl());
// 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...
bool isOldSettings = !(settings.contains("useFullAvatar") || settings.contains("fullAvatarURL"));
_useFullAvatar = settings.value("useFullAvatar").toBool();
_headURLFromPreferences = settings.value("faceModelURL", DEFAULT_HEAD_MODEL_URL).toUrl();
_fullAvatarURLFromPreferences = settings.value("fullAvatarURL").toUrl();
_skeletonURLFromPreferences = settings.value("skeletonModelURL").toUrl();
_headModelName = settings.value("headModelName").toString();
_bodyModelName = settings.value("bodyModelName").toString();
_fullAvatarModelName = settings.value("fullAvatarModelName").toString();;
if (isOldSettings) {
bool assumeFullAvatar = _headURLFromPreferences.isEmpty();
_useFullAvatar = assumeFullAvatar;
if (_useFullAvatar) {
_fullAvatarURLFromPreferences = settings.value("skeletonModelURL").toUrl();
_headURLFromPreferences = DEFAULT_HEAD_MODEL_URL;
_skeletonURLFromPreferences = DEFAULT_BODY_MODEL_URL;
QVariantHash fullAvatarFST = FSTReader::downloadMapping(_fullAvatarURLFromPreferences.toString());
_headModelName = "Default";
_bodyModelName = "Default";
_fullAvatarModelName = fullAvatarFST["name"].toString();
} else {
_fullAvatarURLFromPreferences = DEFAULT_FULL_AVATAR_MODEL_URL;
_skeletonURLFromPreferences = settings.value("skeletonModelURL").toUrl();
QVariantHash headFST = FSTReader::downloadMapping(_headURLFromPreferences.toString());
QVariantHash bodyFST = FSTReader::downloadMapping(_skeletonURLFromPreferences.toString());
_headModelName = headFST["name"].toString();
_bodyModelName = bodyFST["name"].toString();
_fullAvatarModelName = "Default";
}
}
if (_useFullAvatar) {
useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName);
} else {
useHeadAndBodyURLs(_headURLFromPreferences, _skeletonURLFromPreferences, _headModelName, _bodyModelName);
}
QVector<AttachmentData> attachmentData;
int attachmentCount = settings.beginReadArray("attachmentData");
@ -710,13 +760,10 @@ void MyAvatar::loadData() {
handle->setPriority(loadSetting(settings, "priority", 1.0f));
handle->setLoop(settings.value("loop", true).toBool());
handle->setHold(settings.value("hold", false).toBool());
handle->setStartAutomatically(settings.value("startAutomatically", true).toBool());
handle->setFirstFrame(settings.value("firstFrame", 0.0f).toFloat());
handle->setLastFrame(settings.value("lastFrame", INT_MAX).toFloat());
handle->setMaskedJoints(settings.value("maskedJoints").toStringList());
if (settings.value("loop", true).toBool() && settings.value("running", false).toBool()) {
handle->setRunning(true);
}
handle->setStartAutomatically(settings.value("startAutomatically", true).toBool());
}
settings.endArray();
@ -897,6 +944,29 @@ void MyAvatar::clearJointAnimationPriorities() {
}
}
QString MyAvatar::getModelDescription() const {
QString result;
if (_useFullAvatar) {
if (!getFullAvartarModelName().isEmpty()) {
result = "Full Avatar \"" + getFullAvartarModelName() + "\"";
} else {
result = "Full Avatar \"" + _fullAvatarURLFromPreferences.fileName() + "\"";
}
} else {
if (!getHeadModelName().isEmpty()) {
result = "Head \"" + getHeadModelName() + "\"";
} else {
result = "Head \"" + _headURLFromPreferences.fileName() + "\"";
}
if (!getBodyModelName().isEmpty()) {
result += " and Body \"" + getBodyModelName() + "\"";
} else {
result += " and Body \"" + _skeletonURLFromPreferences.fileName() + "\"";
}
}
return result;
}
void MyAvatar::setFaceModelURL(const QUrl& faceModelURL) {
Avatar::setFaceModelURL(faceModelURL);
_billboardValid = false;
@ -907,6 +977,74 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
_billboardValid = false;
}
void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName) {
_useFullAvatar = true;
if (_fullAvatarURLFromPreferences != fullAvatarURL) {
_fullAvatarURLFromPreferences = fullAvatarURL;
if (modelName.isEmpty()) {
QVariantHash fullAvatarFST = FSTReader::downloadMapping(_fullAvatarURLFromPreferences.toString());
_fullAvatarModelName = fullAvatarFST["name"].toString();
} else {
_fullAvatarModelName = modelName;
}
}
if (!getFaceModelURLString().isEmpty()) {
setFaceModelURL(QString());
}
if (fullAvatarURL != getSkeletonModelURL()) {
setSkeletonModelURL(fullAvatarURL);
UserActivityLogger::getInstance().changedModel("skeleton", fullAvatarURL.toString());
}
sendIdentityPacket();
}
void MyAvatar::useHeadURL(const QUrl& headURL, const QString& modelName) {
useHeadAndBodyURLs(headURL, _skeletonURLFromPreferences, modelName, _bodyModelName);
}
void MyAvatar::useBodyURL(const QUrl& bodyURL, const QString& modelName) {
useHeadAndBodyURLs(_headURLFromPreferences, bodyURL, _headModelName, modelName);
}
void MyAvatar::useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL, const QString& headName, const QString& bodyName) {
_useFullAvatar = false;
if (_headURLFromPreferences != headURL) {
_headURLFromPreferences = headURL;
if (headName.isEmpty()) {
QVariantHash headFST = FSTReader::downloadMapping(_headURLFromPreferences.toString());
_headModelName = headFST["name"].toString();
} else {
_headModelName = headName;
}
}
if (_skeletonURLFromPreferences != bodyURL) {
_skeletonURLFromPreferences = bodyURL;
if (bodyName.isEmpty()) {
QVariantHash bodyFST = FSTReader::downloadMapping(_skeletonURLFromPreferences.toString());
_bodyModelName = bodyFST["name"].toString();
} else {
_bodyModelName = bodyName;
}
}
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()) {
@ -1240,28 +1378,28 @@ glm::vec3 MyAvatar::applyScriptedMotor(float deltaTime, const glm::vec3& localVe
void MyAvatar::updatePosition(float deltaTime) {
// rotate velocity into camera frame
glm::quat rotation = getHead()->getCameraOrientation();
glm::vec3 localVelocity = glm::inverse(rotation) * _velocity;
glm::vec3 localVelocity = glm::inverse(rotation) * _targetVelocity;
bool isHovering = _characterController.isHovering();
glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, isHovering);
newLocalVelocity = applyScriptedMotor(deltaTime, newLocalVelocity);
// rotate back into world-frame
_velocity = rotation * newLocalVelocity;
_targetVelocity = rotation * newLocalVelocity;
_velocity += _thrust * deltaTime;
_targetVelocity += _thrust * deltaTime;
_thrust = glm::vec3(0.0f);
// cap avatar speed
float speed = glm::length(_velocity);
float speed = glm::length(_targetVelocity);
if (speed > MAX_AVATAR_SPEED) {
_velocity *= MAX_AVATAR_SPEED / speed;
_targetVelocity *= MAX_AVATAR_SPEED / speed;
speed = MAX_AVATAR_SPEED;
}
if (speed > MIN_AVATAR_SPEED && !_characterController.isEnabled()) {
// update position ourselves
applyPositionDelta(deltaTime * _velocity);
applyPositionDelta(deltaTime * _targetVelocity);
measureMotionDerivatives(deltaTime);
} // else physics will move avatar later

View file

@ -116,8 +116,23 @@ 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);
Q_INVOKABLE void useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName = QString());
Q_INVOKABLE void useHeadURL(const QUrl& headURL, const QString& modelName = QString());
Q_INVOKABLE void useBodyURL(const QUrl& bodyURL, const QString& modelName = QString());
Q_INVOKABLE void useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL, const QString& headName = QString(), const QString& bodyName = QString());
Q_INVOKABLE bool getUseFullAvatar() const { return _useFullAvatar; }
Q_INVOKABLE const QUrl& getFullAvatarURLFromPreferences() const { return _fullAvatarURLFromPreferences; }
Q_INVOKABLE const QUrl& getHeadURLFromPreferences() const { return _headURLFromPreferences; }
Q_INVOKABLE const QUrl& getBodyURLFromPreferences() const { return _skeletonURLFromPreferences; }
Q_INVOKABLE const QString& getHeadModelName() const { return _headModelName; }
Q_INVOKABLE const QString& getBodyModelName() const { return _bodyModelName; }
Q_INVOKABLE const QString& getFullAvartarModelName() const { return _fullAvatarModelName; }
Q_INVOKABLE QString getModelDescription() const;
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData);
virtual glm::vec3 getSkeletonPosition() const;
@ -185,6 +200,11 @@ protected:
virtual void renderAttachments(RenderArgs::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 +249,16 @@ 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;
QString _headModelName;
QString _bodyModelName;
QString _fullAvatarModelName;
};
#endif // hifi_MyAvatar_h

View file

@ -11,6 +11,7 @@
#include <SharedUtil.h>
#include <QCoreApplication>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
@ -19,10 +20,21 @@
#include "DdeFaceTracker.h"
#include "FaceshiftConstants.h"
#include "InterfaceLogging.h"
#include "Menu.h"
static const QHostAddress DDE_FEATURE_POINT_SERVER_ADDR("127.0.0.1");
static const quint16 DDE_FEATURE_POINT_SERVER_PORT = 5555;
static const QHostAddress DDE_SERVER_ADDR("127.0.0.1");
static const quint16 DDE_SERVER_PORT = 64204;
static const quint16 DDE_CONTROL_PORT = 64205;
#if defined(Q_OS_WIN)
static const QString DDE_PROGRAM_PATH = QCoreApplication::applicationDirPath() + "/dde/dde.exe";
#elif defined(Q_OS_MAC)
static const QString DDE_PROGRAM_PATH = QCoreApplication::applicationDirPath() + "/dde.app/Contents/MacOS/dde";
#endif
static const QStringList DDE_ARGUMENTS = QStringList()
<< "--udp=" + DDE_SERVER_ADDR.toString() + ":" + QString::number(DDE_SERVER_PORT)
<< "--receiver=" + QString::number(DDE_CONTROL_PORT)
<< "--headless";
static const int NUM_EXPRESSIONS = 46;
static const int MIN_PACKET_SIZE = (8 + NUM_EXPRESSIONS) * sizeof(float) + sizeof(int);
@ -121,14 +133,16 @@ struct Packet {
};
DdeFaceTracker::DdeFaceTracker() :
DdeFaceTracker(QHostAddress::Any, DDE_FEATURE_POINT_SERVER_PORT)
DdeFaceTracker(QHostAddress::Any, DDE_SERVER_PORT, DDE_CONTROL_PORT)
{
}
DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 port) :
DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 serverPort, quint16 controlPort) :
_ddeProcess(NULL),
_host(host),
_port(port),
_serverPort(serverPort),
_controlPort(controlPort),
_lastReceiveTimestamp(0),
_reset(false),
_leftBlinkIndex(0), // see http://support.faceshift.com/support/articles/35129-export-of-blendshapes
@ -157,17 +171,50 @@ DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 port) :
}
DdeFaceTracker::~DdeFaceTracker() {
if (_udpSocket.isOpen()) {
_udpSocket.close();
}
setEnabled(false);
}
void DdeFaceTracker::setEnabled(bool enabled) {
#ifdef HAVE_DDE
// isOpen() does not work as one might expect on QUdpSocket; don't test isOpen() before closing socket.
_udpSocket.close();
if (enabled) {
_udpSocket.bind(_host, _port);
_udpSocket.bind(_host, _serverPort);
}
if (enabled && !_ddeProcess) {
// Terminate any existing DDE process, perhaps left running after an Interface crash
const char* DDE_EXIT_COMMAND = "exit";
_udpSocket.writeDatagram(DDE_EXIT_COMMAND, DDE_SERVER_ADDR, _controlPort);
qDebug() << "[Info] DDE Face Tracker Starting";
_ddeProcess = new QProcess(qApp);
connect(_ddeProcess, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(processFinished(int, QProcess::ExitStatus)));
_ddeProcess->start(QCoreApplication::applicationDirPath() + DDE_PROGRAM_PATH, DDE_ARGUMENTS);
}
if (!enabled && _ddeProcess) {
_ddeProcess->kill(); // More robust than trying to send an "exit" command to DDE
_ddeProcess = NULL;
qDebug() << "[Info] DDE Face Tracker Stopped";
}
#endif
}
void DdeFaceTracker::processFinished(int exitCode, QProcess::ExitStatus exitStatus) {
if (_ddeProcess) {
// DDE crashed or was manually terminated
qDebug() << "[Info] DDE Face Tracker Stopped Unexpectedly";
_udpSocket.close();
_ddeProcess = NULL;
Menu::getInstance()->setIsOptionChecked(MenuOption::NoFaceTracking, true);
}
}
void DdeFaceTracker::resetTracking() {
qDebug() << "[Info] Reset DDE Tracking";
const char* DDE_RESET_COMMAND = "reset";
_udpSocket.writeDatagram(DDE_RESET_COMMAND, DDE_SERVER_ADDR, _controlPort);
}
bool DdeFaceTracker::isActive() const {
@ -239,7 +286,7 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
}
// Compute relative translation
float LEAN_DAMPING_FACTOR = 200.0f;
float LEAN_DAMPING_FACTOR = 75.0f;
translation -= _referenceTranslation;
translation /= LEAN_DAMPING_FACTOR;
translation.x *= -1;

View file

@ -12,6 +12,11 @@
#ifndef hifi_DdeFaceTracker_h
#define hifi_DdeFaceTracker_h
#if defined(Q_OS_WIN) || defined(Q_OS_OSX)
#define HAVE_DDE
#endif
#include <QProcess>
#include <QUdpSocket>
#include <DependencyManager.h>
@ -45,9 +50,11 @@ public:
public slots:
void setEnabled(bool enabled);
void resetTracking();
private slots:
void processFinished(int exitCode, QProcess::ExitStatus exitStatus);
//sockets
void socketErrorOccurred(QAbstractSocket::SocketError socketError);
void readPendingDatagrams();
@ -55,11 +62,14 @@ private slots:
private:
DdeFaceTracker();
DdeFaceTracker(const QHostAddress& host, quint16 port);
DdeFaceTracker(const QHostAddress& host, quint16 serverPort, quint16 controlPort);
~DdeFaceTracker();
QProcess* _ddeProcess;
QHostAddress _host;
quint16 _port;
quint16 _serverPort;
quint16 _controlPort;
float getBlendshapeCoefficient(int index) const;
void decodePacket(const QByteArray& buffer);

View file

@ -114,8 +114,11 @@ void OculusManager::initSdk() {
}
void OculusManager::shutdownSdk() {
ovrHmd_Destroy(_ovrHmd);
ovr_Shutdown();
if (_ovrHmd) {
ovrHmd_Destroy(_ovrHmd);
_ovrHmd = nullptr;
ovr_Shutdown();
}
}
void OculusManager::init() {
@ -124,6 +127,12 @@ void OculusManager::init() {
#endif
}
void OculusManager::deinit() {
#ifdef OVR_DIRECT_MODE
shutdownSdk();
#endif
}
void OculusManager::connect() {
#ifndef OVR_DIRECT_MODE
initSdk();

View file

@ -51,6 +51,7 @@ class Text3DOverlay;
class OculusManager {
public:
static void init();
static void deinit();
static void connect();
static void disconnect();
static bool isConnected();

View file

@ -112,6 +112,7 @@ int main(int argc, const char* argv[]) {
exitCode = app.exec();
}
OculusManager::deinit();
#ifdef Q_OS_WIN
ReleaseMutex(mutex);
#endif

View file

@ -0,0 +1,210 @@
//
// AvatarAppearanceDialog.cpp
// interface/src/ui
//
// Created by Stojce Slavkovski on 2/20/14.
// Copyright 2014 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 <QFileDialog>
#include <QFont>
#include <AudioClient.h>
#include <avatar/AvatarManager.h>
#include <devices/Faceshift.h>
#include <devices/SixenseManager.h>
#include <NetworkingConstants.h>
#include "Application.h"
#include "MainWindow.h"
#include "LODManager.h"
#include "Menu.h"
#include "AvatarAppearanceDialog.h"
#include "Snapshot.h"
#include "UserActivityLogger.h"
#include "UIUtil.h"
#include "ui/DialogsManager.h"
#include "ui/PreferencesDialog.h"
AvatarAppearanceDialog::AvatarAppearanceDialog(QWidget* parent) :
QDialog(parent) {
setAttribute(Qt::WA_DeleteOnClose);
ui.setupUi(this);
loadAvatarAppearance();
connect(ui.defaultButton, &QPushButton::clicked, this, &AvatarAppearanceDialog::accept);
connect(ui.buttonBrowseHead, &QPushButton::clicked, this, &AvatarAppearanceDialog::openHeadModelBrowser);
connect(ui.buttonBrowseBody, &QPushButton::clicked, this, &AvatarAppearanceDialog::openBodyModelBrowser);
connect(ui.buttonBrowseFullAvatar, &QPushButton::clicked, this, &AvatarAppearanceDialog::openFullAvatarModelBrowser);
connect(ui.useSeparateBodyAndHead, &QRadioButton::clicked, this, &AvatarAppearanceDialog::useSeparateBodyAndHead);
connect(ui.useFullAvatar, &QRadioButton::clicked, this, &AvatarAppearanceDialog::useFullAvatar);
connect(Application::getInstance(), &Application::headURLChanged, this, &AvatarAppearanceDialog::headURLChanged);
connect(Application::getInstance(), &Application::bodyURLChanged, this, &AvatarAppearanceDialog::bodyURLChanged);
connect(Application::getInstance(), &Application::fullAvatarURLChanged, this, &AvatarAppearanceDialog::fullAvatarURLChanged);
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
ui.bodyNameLabel->setText("Body - " + myAvatar->getBodyModelName());
ui.headNameLabel->setText("Head - " + myAvatar->getHeadModelName());
ui.fullAvatarNameLabel->setText("Full Avatar - " + myAvatar->getFullAvartarModelName());
UIUtil::scaleWidgetFontSizes(this);
}
void AvatarAppearanceDialog::useSeparateBodyAndHead(bool checked) {
QUrl headURL(ui.faceURLEdit->text());
QUrl bodyURL(ui.skeletonURLEdit->text());
DependencyManager::get<AvatarManager>()->getMyAvatar()->useHeadAndBodyURLs(headURL, bodyURL);
setUseFullAvatar(!checked);
}
void AvatarAppearanceDialog::useFullAvatar(bool checked) {
QUrl fullAvatarURL(ui.fullAvatarURLEdit->text());
DependencyManager::get<AvatarManager>()->getMyAvatar()->useFullAvatarURL(fullAvatarURL);
setUseFullAvatar(checked);
}
void AvatarAppearanceDialog::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);
DependencyManager::get<DialogsManager>()->getPreferencesDialog()->avatarDescriptionChanged();
}
void AvatarAppearanceDialog::headURLChanged(const QString& newValue, const QString& modelName) {
ui.faceURLEdit->setText(newValue);
setUseFullAvatar(false);
ui.headNameLabel->setText("Head - " + modelName);
}
void AvatarAppearanceDialog::bodyURLChanged(const QString& newValue, const QString& modelName) {
ui.skeletonURLEdit->setText(newValue);
setUseFullAvatar(false);
ui.bodyNameLabel->setText("Body - " + modelName);
}
void AvatarAppearanceDialog::fullAvatarURLChanged(const QString& newValue, const QString& modelName) {
ui.fullAvatarURLEdit->setText(newValue);
setUseFullAvatar(true);
ui.fullAvatarNameLabel->setText("Full Avatar - " + modelName);
}
void AvatarAppearanceDialog::accept() {
saveAvatarAppearance();
DependencyManager::get<DialogsManager>()->getPreferencesDialog()->avatarDescriptionChanged();
close();
delete _marketplaceWindow;
_marketplaceWindow = NULL;
}
void AvatarAppearanceDialog::setHeadUrl(QString modelUrl) {
ui.faceURLEdit->setText(modelUrl);
}
void AvatarAppearanceDialog::setSkeletonUrl(QString modelUrl) {
ui.skeletonURLEdit->setText(modelUrl);
}
void AvatarAppearanceDialog::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 AvatarAppearanceDialog::openHeadModelBrowser() {
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 AvatarAppearanceDialog::openBodyModelBrowser() {
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 AvatarAppearanceDialog::resizeEvent(QResizeEvent *resizeEvent) {
// keep buttons panel at the bottom
ui.buttonsPanel->setGeometry(0,
size().height() - ui.buttonsPanel->height(),
size().width(),
ui.buttonsPanel->height());
// set width and height of srcollarea to match bottom panel and width
ui.scrollArea->setGeometry(ui.scrollArea->geometry().x(), ui.scrollArea->geometry().y(),
size().width(),
size().height() - ui.buttonsPanel->height() - ui.scrollArea->geometry().y());
}
void AvatarAppearanceDialog::loadAvatarAppearance() {
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
_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);
}
void AvatarAppearanceDialog::saveAvatarAppearance() {
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
QUrl headURL(ui.faceURLEdit->text());
QString headURLString = headURL.toString();
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 {
myAvatar->useHeadAndBodyURLs(headURL, bodyURL);
}
}
}

View file

@ -0,0 +1,64 @@
//
// AvatarAppearanceDialog.h
// interface/src/ui
//
// Created by Stojce Slavkovski on 2/20/14.
// Copyright 2014 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_AvatarAppearanceDialog_h
#define hifi_AvatarAppearanceDialog_h
#include "ui_avatarAppearance.h"
#include <QDialog>
#include <QString>
#include "scripting/WebWindowClass.h"
class AvatarAppearanceDialog : public QDialog {
Q_OBJECT
public:
AvatarAppearanceDialog(QWidget* parent = nullptr);
protected:
void resizeEvent(QResizeEvent* resizeEvent);
private:
void loadAvatarAppearance();
void saveAvatarAppearance();
void openHeadModelBrowser();
void openBodyModelBrowser();
void openFullAvatarModelBrowser();
void setUseFullAvatar(bool useFullAvatar);
Ui_AvatarAppearanceDialog ui;
bool _useFullAvatar;
QString _headURLString;
QString _bodyURLString;
QString _fullAvatarURLString;
QString _displayNameString;
WebWindowClass* _marketplaceWindow = NULL;
private slots:
void accept();
void setHeadUrl(QString modelUrl);
void setSkeletonUrl(QString modelUrl);
void headURLChanged(const QString& newValue, const QString& modelName);
void bodyURLChanged(const QString& newValue, const QString& modelName);
void fullAvatarURLChanged(const QString& newValue, const QString& modelName);
void useSeparateBodyAndHead(bool checked);
void useFullAvatar(bool checked);
};
#endif // hifi_AvatarAppearanceDialog_h

View file

@ -18,6 +18,7 @@
#include "AddressBarDialog.h"
#include "AnimationsDialog.h"
#include "AttachmentsDialog.h"
#include "AvatarAppearanceDialog.h"
#include "BandwidthDialog.h"
#include "CachesSizeDialog.h"
#include "DiskCacheEditor.h"
@ -85,6 +86,15 @@ void DialogsManager::editPreferences() {
}
}
void DialogsManager::changeAvatarAppearance() {
if (!_avatarAppearanceDialog) {
maybeCreateDialog(_avatarAppearanceDialog);
_avatarAppearanceDialog->show();
} else {
_avatarAppearanceDialog->close();
}
}
void DialogsManager::editAttachments() {
if (!_attachmentsDialog) {
maybeCreateDialog(_attachmentsDialog);

View file

@ -33,6 +33,7 @@ class OctreeStatsDialog;
class PreferencesDialog;
class ScriptEditorWindow;
class QMessageBox;
class AvatarAppearanceDialog;
class DialogsManager : public QObject, public Dependency {
Q_OBJECT
@ -43,6 +44,7 @@ public:
QPointer<HMDToolsDialog> getHMDToolsDialog() const { return _hmdToolsDialog; }
QPointer<LodToolsDialog> getLodToolsDialog() const { return _lodToolsDialog; }
QPointer<OctreeStatsDialog> getOctreeStatsDialog() const { return _octreeStatsDialog; }
QPointer<PreferencesDialog> getPreferencesDialog() const { return _preferencesDialog; }
public slots:
void toggleAddressBar();
@ -59,6 +61,7 @@ public slots:
void hmdTools(bool showTools);
void showScriptEditor();
void showIRCLink();
void changeAvatarAppearance();
private slots:
void toggleToolWindow();
@ -94,6 +97,7 @@ private:
QPointer<OctreeStatsDialog> _octreeStatsDialog;
QPointer<PreferencesDialog> _preferencesDialog;
QPointer<ScriptEditorWindow> _scriptEditor;
QPointer<AvatarAppearanceDialog> _avatarAppearanceDialog;
};
#endif // hifi_DialogsManager_h

View file

@ -19,6 +19,7 @@
#include <NetworkingConstants.h>
#include "Application.h"
#include "DialogsManager.h"
#include "MainWindow.h"
#include "LODManager.h"
#include "Menu.h"
@ -41,30 +42,40 @@ PreferencesDialog::PreferencesDialog(QWidget* parent) :
ui.outputBufferSizeSpinner->setMinimum(MIN_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES);
ui.outputBufferSizeSpinner->setMaximum(MAX_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES);
connect(ui.buttonBrowseHead, &QPushButton::clicked, this, &PreferencesDialog::openHeadModelBrowser);
connect(ui.buttonBrowseBody, &QPushButton::clicked, this, &PreferencesDialog::openBodyModelBrowser);
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);
DialogsManager* dialogsManager = DependencyManager::get<DialogsManager>().data();
connect(ui.buttonChangeApperance, &QPushButton::clicked, dialogsManager, &DialogsManager::changeAvatarAppearance);
connect(Application::getInstance(), &Application::faceURLChanged, this, &PreferencesDialog::faceURLChanged);
connect(Application::getInstance(), &Application::skeletonURLChanged, this, &PreferencesDialog::skeletonURLChanged);
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.apperanceDescription->setText(DependencyManager::get<AvatarManager>()->getMyAvatar()->getModelDescription());
UIUtil::scaleWidgetFontSizes(this);
}
void PreferencesDialog::faceURLChanged(const QString& newValue) {
ui.faceURLEdit->setText(newValue);
void PreferencesDialog::avatarDescriptionChanged() {
ui.apperanceDescription->setText(DependencyManager::get<AvatarManager>()->getMyAvatar()->getModelDescription());
}
void PreferencesDialog::skeletonURLChanged(const QString& newValue) {
ui.skeletonURLEdit->setText(newValue);
void PreferencesDialog::headURLChanged(const QString& newValue, const QString& modelName) {
ui.apperanceDescription->setText(DependencyManager::get<AvatarManager>()->getMyAvatar()->getModelDescription());
}
void PreferencesDialog::bodyURLChanged(const QString& newValue, const QString& modelName) {
ui.apperanceDescription->setText(DependencyManager::get<AvatarManager>()->getMyAvatar()->getModelDescription());
}
void PreferencesDialog::fullAvatarURLChanged(const QString& newValue, const QString& modelName) {
ui.apperanceDescription->setText(DependencyManager::get<AvatarManager>()->getMyAvatar()->getModelDescription());
}
void PreferencesDialog::accept() {
@ -74,34 +85,6 @@ void PreferencesDialog::accept() {
_marketplaceWindow = NULL;
}
void PreferencesDialog::setHeadUrl(QString modelUrl) {
ui.faceURLEdit->setText(modelUrl);
}
void PreferencesDialog::setSkeletonUrl(QString modelUrl) {
ui.skeletonURLEdit->setText(modelUrl);
}
void PreferencesDialog::openHeadModelBrowser() {
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::openBodyModelBrowser() {
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::openSnapshotLocationBrowser() {
QString dir = QFileDialog::getExistingDirectory(this, tr("Snapshots Location"),
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation),
@ -143,12 +126,6 @@ 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);
ui.sendDataCheckBox->setChecked(!menuInstance->isOptionChecked(MenuOption::DisableActivityLogger));
ui.snapshotLocationEdit->setText(Snapshot::snapshotsLocation.get());
@ -208,6 +185,7 @@ void PreferencesDialog::loadPreferences() {
void PreferencesDialog::savePreferences() {
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
bool shouldDispatchIdentityPacket = false;
QString displayNameStr(ui.displayNameEdit->text());
@ -217,34 +195,6 @@ 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 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;
} else {
qDebug() << "ERROR: Skeleton model not FST or blank - " << skeletonModelURLString;
}
}
if (shouldDispatchIdentityPacket) {
myAvatar->sendIdentityPacket();
}

View file

@ -25,31 +25,28 @@ class PreferencesDialog : public QDialog {
public:
PreferencesDialog(QWidget* parent = nullptr);
void avatarDescriptionChanged();
protected:
void resizeEvent(QResizeEvent* resizeEvent);
private:
void loadPreferences();
void savePreferences();
void openHeadModelBrowser();
void openBodyModelBrowser();
Ui_PreferencesDialog ui;
QString _faceURLString;
QString _skeletonURLString;
QString _displayNameString;
WebWindowClass* _marketplaceWindow = NULL;
private slots:
void accept();
void setHeadUrl(QString modelUrl);
void setSkeletonUrl(QString modelUrl);
void openSnapshotLocationBrowser();
void openScriptsLocationBrowser();
void faceURLChanged(const QString& newValue);
void skeletonURLChanged(const QString& newValue);
void headURLChanged(const QString& newValue, const QString& modelName);
void bodyURLChanged(const QString& newValue, const QString& modelName);
void fullAvatarURLChanged(const QString& newValue, const QString& modelName);
};
#endif // hifi_PreferencesDialog_h

View file

@ -0,0 +1,471 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AvatarAppearanceDialog</class>
<widget class="QDialog" name="AvatarAppearanceDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>350</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>500</width>
<height>350</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>500</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>13</pointsize>
</font>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>-107</y>
<width>485</width>
<height>1550</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>30</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>30</number>
</property>
<property name="bottomMargin">
<number>10</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<property name="topMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QRadioButton" name="useFullAvatar">
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="text">
<string>Use single avatar with Body and Head</string>
</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>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<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">
<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>0</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="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="text">
<string>Use separate Body and Head avatar files</string>
</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>
</font>
</property>
<property name="text">
<string>Head</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="faceURLEdit"/>
</item>
<item>
<spacer name="horizontalSpacer">
<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="buttonBrowseHead">
<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>
<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="bodyNameLabel">
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="text">
<string>Body</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
<property name="buddy">
<cstring>skeletonURLEdit</cstring>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_skeletonURL">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="skeletonURLEdit">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_10">
<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="buttonBrowseBody">
<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>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QFrame" name="buttonsPanel">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_12">
<item>
<layout class="QHBoxLayout" name="buttonsHBox_2">
<item>
<spacer name="horizontalSpacer_14">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="defaultButton">
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="text">
<string>Close</string>
</property>
<property name="default">
<bool>true</bool>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
</ui>

View file

@ -128,6 +128,8 @@
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="spacing">
@ -187,8 +189,9 @@
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<layout class="QVBoxLayout" name="verticalLayout_appearance">
<property name="spacing">
<number>0</number>
</property>
@ -199,33 +202,47 @@
<number>7</number>
</property>
<item>
<widget class="QLabel" name="headLabel">
<widget class="QLabel" name="appearanceLabel">
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="text">
<string>Head</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Appearance&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
<property name="buddy">
<cstring>snapshotLocationEdit</cstring>
<cstring>apperanceDescription</cstring>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QHBoxLayout" name="horizontalLayout_1_apperanceDescription">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="faceURLEdit"/>
<widget class="QLineEdit" name="apperanceDescription">
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<spacer name="horizontalSpacer_1_apperanceDescription">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@ -241,96 +258,14 @@
</spacer>
</item>
<item>
<widget class="QPushButton" name="buttonBrowseHead">
<widget class="QPushButton" name="buttonChangeApperance">
<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>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="topMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QLabel" name="headLabel_2">
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="text">
<string>Body</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
<property name="buddy">
<cstring>skeletonURLEdit</cstring>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="skeletonURLEdit">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_10">
<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="buttonBrowseBody">
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="text">
<string>Browse</string>
<string>Change</string>
</property>
<property name="iconSize">
<size>
@ -342,8 +277,11 @@
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
@ -413,6 +351,7 @@
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_1">
<property name="spacing">
@ -463,6 +402,10 @@
</item>
</layout>
</item>
</layout>
</item>
<item>
@ -665,6 +608,7 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="sendDataCheckBox">
<property name="sizePolicy">

View file

@ -493,7 +493,7 @@ void AudioClient::start() {
_sourceGain.initialize();
_noiseSource.initialize();
_toneSource.initialize();
_sourceGain.setParameters(0.25f, 0.0f);
_sourceGain.setParameters(0.05f, 0.0f);
_inputGain.setParameters(1.0f, 0.0f);
}
@ -726,9 +726,9 @@ void AudioClient::handleAudioInput() {
int inputSamplesRequired = (int)((float)AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio);
QByteArray inputByteArray = _inputDevice->readAll();
// Add audio source injection if enabled
if (!_muted && _audioSourceInjectEnabled) {
int16_t* inputFrameData = (int16_t*)inputByteArray.data();
const uint32_t inputFrameCount = inputByteArray.size() / sizeof(int16_t);
@ -737,17 +737,12 @@ void AudioClient::handleAudioInput() {
#if ENABLE_INPUT_GAIN
_inputGain.render(_inputFrameBuffer); // input/mic gain+mute
#endif
// Add audio source injection if enabled
if (_audioSourceInjectEnabled) {
if (_toneSourceEnabled) { // sine generator
_toneSource.render(_inputFrameBuffer);
}
else if(_noiseSourceEnabled) { // pink noise generator
_noiseSource.render(_inputFrameBuffer);
}
_sourceGain.render(_inputFrameBuffer); // post gain
if (_toneSourceEnabled) { // sine generator
_toneSource.render(_inputFrameBuffer);
} else if(_noiseSourceEnabled) { // pink noise generator
_noiseSource.render(_inputFrameBuffer);
}
_sourceGain.render(_inputFrameBuffer); // post gain
_inputFrameBuffer.copyFrames(1, inputFrameCount, inputFrameData, true /*copy out*/);
}
@ -972,8 +967,8 @@ void AudioClient::setIsStereoInput(bool isStereoInput) {
}
}
void AudioClient::toggleAudioSourceInject() {
_audioSourceInjectEnabled = !_audioSourceInjectEnabled;
void AudioClient::enableAudioSourceInject(bool enable) {
_audioSourceInjectEnabled = enable;
}
void AudioClient::selectAudioSourcePinkNoise() {

View file

@ -1,6 +1,6 @@
//
// AudioClient.h
// interface/src
// libraries/audio-client/src
//
// Created by Stephen Birarda on 1/22/13.
// Copyright 2013 High Fidelity, Inc.
@ -143,9 +143,9 @@ public slots:
void audioMixerKilled();
void toggleMute();
void toggleAudioSourceInject();
void selectAudioSourcePinkNoise();
void selectAudioSourceSine440();
virtual void enableAudioSourceInject(bool enable);
virtual void selectAudioSourcePinkNoise();
virtual void selectAudioSourceSine440();
void toggleAudioNoiseReduction() { _isNoiseGateEnabled = !_isNoiseGateEnabled; }

View file

@ -27,6 +27,10 @@ public:
public slots:
virtual bool outputLocalInjector(bool isStereo, qreal volume, AudioInjector* injector) = 0;
virtual void enableAudioSourceInject(bool enable) = 0;
virtual void selectAudioSourcePinkNoise() = 0;
virtual void selectAudioSourceSine440() = 0;
};
Q_DECLARE_METATYPE(AbstractAudioInterface*)

View file

@ -60,6 +60,7 @@ AvatarData::AvatarData() :
_owningAvatarMixer(),
_lastUpdateTimer(),
_velocity(0.0f),
_targetVelocity(0.0f),
_localAABox(DEFAULT_LOCAL_AABOX_CORNER, DEFAULT_LOCAL_AABOX_SCALE)
{
}

View file

@ -103,6 +103,8 @@ const int AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS = 5000;
const QUrl DEFAULT_HEAD_MODEL_URL = QUrl("http://public.highfidelity.io/models/heads/defaultAvatar_head.fst");
const QUrl DEFAULT_BODY_MODEL_URL = QUrl("http://public.highfidelity.io/models/skeletons/defaultAvatar_body.fst");
const QUrl DEFAULT_FULL_AVATAR_MODEL_URL = QUrl("http://public.highfidelity.io/marketplace/contents/029db3d4-da2c-4cb2-9c08-b9612ba576f5/02949063e7c4aed42ad9d1a58461f56d.fst");
// Where one's own Avatar begins in the world (will be overwritten if avatar data file is found).
// This is the start location in the Sandbox (xyz: 6270, 211, 6000).
@ -294,6 +296,7 @@ public:
void setVelocity(const glm::vec3 velocity) { _velocity = velocity; }
Q_INVOKABLE glm::vec3 getVelocity() const { return _velocity; }
glm::vec3 getTargetVelocity() const { return _targetVelocity; }
public slots:
void sendAvatarDataPacket();
@ -384,6 +387,7 @@ protected:
void changeReferential(Referential* ref);
glm::vec3 _velocity;
glm::vec3 _targetVelocity;
AABox _localAABox;

View file

@ -10,6 +10,13 @@
//
#include <QBuffer>
#include <QEventLoop>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <NetworkAccessManager.h>
#include <NetworkingConstants.h>
#include <SharedUtil.h>
#include "FSTReader.h"
@ -169,3 +176,17 @@ FSTReader::ModelType FSTReader::predictModelType(const QVariantHash& mapping) {
return ENTITY_MODEL;
}
QVariantHash FSTReader::downloadMapping(const QString& url) {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest = QNetworkRequest(url);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
QNetworkReply* reply = networkAccessManager.get(networkRequest);
qDebug() << "Downloading avatar file at " << url;
QEventLoop loop;
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
QByteArray fstContents = reply->readAll();
delete reply;
return FSTReader::readMapping(fstContents);
}

View file

@ -51,6 +51,7 @@ public:
static QString getNameFromType(ModelType modelType);
static FSTReader::ModelType getTypeFromName(const QString& name);
static QVariantHash downloadMapping(const QString& url);
private:
static void writeVariant(QBuffer& buffer, QVariantHash::const_iterator& it);

View file

@ -386,7 +386,7 @@ void DynamicCharacterController::preSimulation(btScalar timeStep) {
setHovering(true);
}
_walkVelocity = glmToBullet(_avatarData->getVelocity());
_walkVelocity = glmToBullet(_avatarData->getTargetVelocity());
if (_pendingFlags & PENDING_FLAG_JUMP) {
_pendingFlags &= ~ PENDING_FLAG_JUMP;
@ -408,6 +408,7 @@ void DynamicCharacterController::postSimulation() {
_avatarData->setOrientation(rotation);
_avatarData->setPosition(position - rotation * _shapeLocalOffset);
_avatarData->setVelocity(bulletToGLM(_rigidBody->getLinearVelocity()));
}
}

View file

@ -47,10 +47,11 @@ void AnimationHandle::setPriority(float priority) {
}
void AnimationHandle::setStartAutomatically(bool startAutomatically) {
_animationLoop.setStartAutomatically(startAutomatically);
if (getStartAutomatically() && !isRunning()) {
if (startAutomatically && !isRunning()) {
// Start before setting _animationLoop value so that code in setRunning() is executed
start();
}
_animationLoop.setStartAutomatically(startAutomatically);
}
void AnimationHandle::setMaskedJoints(const QStringList& maskedJoints) {
@ -59,13 +60,10 @@ void AnimationHandle::setMaskedJoints(const QStringList& maskedJoints) {
}
void AnimationHandle::setRunning(bool running) {
if (isRunning() == running) {
if (running && isRunning()) {
// if we're already running, this is the same as a restart
if (running) {
// move back to the beginning
setFrameIndex(getFirstFrame());
return;
}
setFrameIndex(getFirstFrame());
return;
}
_animationLoop.setRunning(running);
if (isRunning()) {

View file

@ -9,9 +9,10 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AudioScriptingInterface.h"
#include "ScriptAudioInjector.h"
#include "ScriptEngineLogging.h"
#include "AudioScriptingInterface.h"
void registerAudioMetaTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, injectorOptionsToScriptValue, injectorOptionsFromScriptValue);
@ -69,3 +70,23 @@ ScriptAudioInjector* AudioScriptingInterface::playSound(Sound* sound, const Audi
return NULL;
}
}
void AudioScriptingInterface::injectGeneratedNoise(bool inject) {
if (_localAudioInterface) {
_localAudioInterface->enableAudioSourceInject(inject);
}
}
void AudioScriptingInterface::selectPinkNoise() {
if (_localAudioInterface) {
_localAudioInterface->selectAudioSourcePinkNoise();
}
}
void AudioScriptingInterface::selectSine440() {
if (_localAudioInterface) {
_localAudioInterface->selectAudioSourceSine440();
}
}

View file

@ -29,6 +29,10 @@ protected:
// this method is protected to stop C++ callers from calling, but invokable from script
Q_INVOKABLE ScriptAudioInjector* playSound(Sound* sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions());
Q_INVOKABLE void injectGeneratedNoise(bool inject);
Q_INVOKABLE void selectPinkNoise();
Q_INVOKABLE void selectSine440();
signals:
void mutedByMixer();
void environmentMuted();