From a88872a21f9cf943ef0bb2ce796831f51ff219d0 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 14 Mar 2014 17:41:22 -0700 Subject: [PATCH 01/42] Simple class to download files (sync/async) --- libraries/shared/src/FileDownloader.cpp | 42 +++++++++++++++++++++++++ libraries/shared/src/FileDownloader.h | 35 +++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 libraries/shared/src/FileDownloader.cpp create mode 100644 libraries/shared/src/FileDownloader.h diff --git a/libraries/shared/src/FileDownloader.cpp b/libraries/shared/src/FileDownloader.cpp new file mode 100644 index 0000000000..5d4a82ca00 --- /dev/null +++ b/libraries/shared/src/FileDownloader.cpp @@ -0,0 +1,42 @@ +// +// FileDownloader.cpp +// hifi +// +// Created by Clement Brisset on 3/14/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// + +#include +#include +#include +#include + +#include "FileDownloader.h" + +FileDownloader::FileDownloader(const QUrl dataURL, QObject* parent) : QObject(parent) { + connect(&_networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(processReply(QNetworkReply*))); + + QNetworkRequest request(dataURL); + _networkAccessManager.get(request); +} + +void FileDownloader::processReply(QNetworkReply *reply) { + if (reply->error() != QNetworkReply::NoError) { + emit done(reply->error()); + return; + } + + _downloadedData = reply->readAll(); + reply->deleteLater(); + + emit done(QNetworkReply::NoError); +} + +QByteArray FileDownloader::download(const QUrl dataURL) { + QEventLoop loop; + FileDownloader downloader(dataURL); + connect(&downloader, SIGNAL(done()), &loop, SLOT(quit())); + loop.exec(); + return downloader.getData(); +} \ No newline at end of file diff --git a/libraries/shared/src/FileDownloader.h b/libraries/shared/src/FileDownloader.h new file mode 100644 index 0000000000..892fd10a96 --- /dev/null +++ b/libraries/shared/src/FileDownloader.h @@ -0,0 +1,35 @@ +// +// FileDownloader.h +// hifi +// +// Created by Clement Brisset on 3/14/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// + +#ifndef __hifi__FileDownloader__ +#define __hifi__FileDownloader__ + +#include +#include + +class FileDownloader : public QObject { +public: + FileDownloader(const QUrl dataURL, QObject* parent = NULL); + QByteArray getData() const { return _downloadedData; } + + static QByteArray download(const QUrl dataURL); + +signals: + void done(QNetworkReply::NetworkError); + +private slots: + void processReply(QNetworkReply* reply); + +private: + QNetworkAccessManager _networkAccessManager; + QByteArray _downloadedData; +}; + + +#endif /* defined(__hifi__FileDownloader__) */ From 6f50ea0faa45112e4b5ec7b1c17e34eec6dc26cc Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 18 Mar 2014 13:55:14 -0700 Subject: [PATCH 02/42] Improved FileDownloader --- libraries/shared/src/FileDownloader.cpp | 43 +++++++++++++++++++------ libraries/shared/src/FileDownloader.h | 13 ++++++-- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/libraries/shared/src/FileDownloader.cpp b/libraries/shared/src/FileDownloader.cpp index 5d4a82ca00..2b65bbd8df 100644 --- a/libraries/shared/src/FileDownloader.cpp +++ b/libraries/shared/src/FileDownloader.cpp @@ -9,12 +9,15 @@ #include #include -#include #include +#include #include "FileDownloader.h" -FileDownloader::FileDownloader(const QUrl dataURL, QObject* parent) : QObject(parent) { +FileDownloader::FileDownloader(const QUrl dataURL, QObject* parent) : + QObject(parent), + _done(false) +{ connect(&_networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(processReply(QNetworkReply*))); QNetworkRequest request(dataURL); @@ -22,21 +25,41 @@ FileDownloader::FileDownloader(const QUrl dataURL, QObject* parent) : QObject(pa } void FileDownloader::processReply(QNetworkReply *reply) { - if (reply->error() != QNetworkReply::NoError) { - emit done(reply->error()); - return; + if (reply->error() == QNetworkReply::NoError) { + _downloadedData = reply->readAll(); } - _downloadedData = reply->readAll(); reply->deleteLater(); - - emit done(QNetworkReply::NoError); + _done = true; + emit done(reply->error()); } -QByteArray FileDownloader::download(const QUrl dataURL) { +void FileDownloader::waitForFile(int timeout) { + QTimer timer; QEventLoop loop; + connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + connect(this, SIGNAL(done(QNetworkReply::NetworkError)), &loop, SLOT(quit())); + + if (!_done) { + if (timeout > 0) { + timer.start(timeout); + } + loop.exec(); + } +} + +QByteArray FileDownloader::download(const QUrl dataURL, int timeout) { + QTimer timer; + QEventLoop loop; + connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit)); + FileDownloader downloader(dataURL); - connect(&downloader, SIGNAL(done()), &loop, SLOT(quit())); + connect(&downloader, SIGNAL(done(QNetworkReply::NetworkError)), &loop, SLOT(quit())); + + if (timeout > 0) { + timer.start(timeout); + } loop.exec(); + return downloader.getData(); } \ No newline at end of file diff --git a/libraries/shared/src/FileDownloader.h b/libraries/shared/src/FileDownloader.h index 892fd10a96..a2ed0b8ccb 100644 --- a/libraries/shared/src/FileDownloader.h +++ b/libraries/shared/src/FileDownloader.h @@ -12,13 +12,20 @@ #include #include +#include class FileDownloader : public QObject { + Q_OBJECT + public: FileDownloader(const QUrl dataURL, QObject* parent = NULL); - QByteArray getData() const { return _downloadedData; } - static QByteArray download(const QUrl dataURL); + void waitForFile(int timeout = 0); + + QByteArray getData() const { return _downloadedData; } + bool done() { return _done; } + + static QByteArray download(const QUrl dataURL, int timeout = 0); signals: void done(QNetworkReply::NetworkError); @@ -29,6 +36,8 @@ private slots: private: QNetworkAccessManager _networkAccessManager; QByteArray _downloadedData; + + bool _done; }; From 388f8d480a3b59fe791c8b1abd31832ecabf0627 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 18 Mar 2014 13:58:23 -0700 Subject: [PATCH 03/42] Added the model browser --- interface/src/ModelBrowser.cpp | 127 +++++++++++++++++++++++++++++++++ interface/src/ModelBrowser.h | 59 +++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 interface/src/ModelBrowser.cpp create mode 100644 interface/src/ModelBrowser.h diff --git a/interface/src/ModelBrowser.cpp b/interface/src/ModelBrowser.cpp new file mode 100644 index 0000000000..cf401d6b75 --- /dev/null +++ b/interface/src/ModelBrowser.cpp @@ -0,0 +1,127 @@ +// +// ModelBrowser.cpp +// hifi +// +// Created by Clement on 3/17/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ModelBrowser.h" + +static const int DOWNLOAD_TIMEOUT = 1000; +static const QString CONTAINER_NAME = "Contents"; +static const QString KEY_NAME = "Key"; + +ModelBrowser::ModelBrowser(QWidget* parent) : + QWidget(parent), + _downloader(QUrl(S3_URL)) +{ +} + +QString ModelBrowser::browse(ModelType modelType) { + _models.clear(); + if (!parseXML(modelType)) { + return QString(); + } + + + QDialog dialog(this); + dialog.setWindowTitle("Browse models"); + + QGridLayout* layout = new QGridLayout(&dialog); + dialog.setLayout(layout); + + QLineEdit* searchBar = new QLineEdit(&dialog); + layout->addWidget(searchBar, 0, 0); + + ListView* listView = new ListView(&dialog); + listView->setEditTriggers(QAbstractItemView::NoEditTriggers); + layout->addWidget(listView, 1, 0); + listView->connect(searchBar, SIGNAL(textChanged(const QString&)), SLOT(keyboardSearch(const QString&))); + dialog.connect(listView, SIGNAL(doubleClicked(const QModelIndex&)), SLOT(accept())); + + QStringListModel* model = new QStringListModel(_models.keys(), listView); + model->sort(0); + listView->setModel(model); + + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + layout->addWidget(buttons, 2, 0); + dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept())); + dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject())); + + if (dialog.exec() == QDialog::Rejected) { + return QString(); + } + + QString selectedKey = model->data(listView->currentIndex(), Qt::DisplayRole).toString(); + return _models[selectedKey]; +} + +void ModelBrowser::browseHead() { + QString model = browse(Head); + emit selectedHead(model); +} + +void ModelBrowser::browseSkeleton() { + QString model = browse(Skeleton); + emit selectedSkeleton(model); +} + +bool ModelBrowser::parseXML(ModelType modelType) { + _downloader.waitForFile(DOWNLOAD_TIMEOUT); + QString location; + switch (modelType) { + case Head: + location = HEAD_MODELS_LOCATION; + break; + case Skeleton: + location = SKELETON_MODELS_LOCATION; + break; + default: + return false; + } + + QXmlStreamReader xml(_downloader.getData()); + QRegExp rx(location + "[^/]*fst"); + + // Read xml until the end or an error is detected + while(!xml.atEnd() && !xml.hasError()) { + if(xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == CONTAINER_NAME) { + while(!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == CONTAINER_NAME)) { + // If a file is find, process it + if(xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == KEY_NAME) { + xml.readNext(); + if (rx.exactMatch(xml.text().toString())) { + // Add the found file to the list + _models.insert(QFileInfo(xml.text().toString()).baseName(), + S3_URL + "/" + xml.text().toString()); + } + } + xml.readNext(); + } + } + xml.readNext(); + } + + // Error handling + if(xml.hasError()) { + _models.clear(); + QMessageBox::critical(this, + "ModelBrowser::ModelBrowser()", + xml.errorString(), + QMessageBox::Ok); + return false; + } + return true; +} \ No newline at end of file diff --git a/interface/src/ModelBrowser.h b/interface/src/ModelBrowser.h new file mode 100644 index 0000000000..9b7cf51ab3 --- /dev/null +++ b/interface/src/ModelBrowser.h @@ -0,0 +1,59 @@ +// +// ModelBrowser.h +// hifi +// +// Created by Clement on 3/17/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#ifndef __hifi__ModelBrowser__ +#define __hifi__ModelBrowser__ + +#include + +#include +#include + +static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com"; +static const QString HEAD_MODELS_LOCATION = "meshes/"; +static const QString SKELETON_MODELS_LOCATION = "meshes/"; + +enum ModelType { + Head, + Skeleton +}; + +class ModelBrowser : public QWidget { + Q_OBJECT + +public: + ModelBrowser(QWidget* parent = NULL); + + QString browse(ModelType modelType); + +signals: + void selectedHead(QString); + void selectedSkeleton(QString); + +public slots: + void browseHead(); + void browseSkeleton(); + +private: + FileDownloader _downloader; + QHash _models; + + bool parseXML(ModelType modelType); +}; + +class ListView : public QListView { + Q_OBJECT +public: + ListView(QWidget* parent) : QListView(parent) {} + public slots: + void keyboardSearch(const QString& text) { + QAbstractItemView::keyboardSearch(text); + } +}; + +#endif /* defined(__hifi__ModelBrowser__) */ From 0a80f8fcd6b8e124f37cb113d088a5e4155756b9 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 18 Mar 2014 13:59:08 -0700 Subject: [PATCH 04/42] Added browse buttons in the menu --- interface/src/Menu.cpp | 53 ++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index ca059a53da..e0f2ba14ff 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -24,11 +24,12 @@ #include #include #include -#include +#include #include #include #include +#include #include "Application.h" #include "Menu.h" @@ -36,6 +37,7 @@ #include "Util.h" #include "InfoView.h" #include "ui/MetavoxelEditor.h" +#include "ModelBrowser.h" Menu* Menu::_instance = NULL; @@ -692,6 +694,9 @@ void Menu::loginForCurrentDomain() { void Menu::editPreferences() { Application* applicationInstance = Application::getInstance(); + ModelBrowser browser; + + const QString BROWSE_BUTTON_TEXT = "Browse"; QDialog dialog(applicationInstance->getWindow()); dialog.setWindowTitle("Interface Preferences"); @@ -702,17 +707,33 @@ void Menu::editPreferences() { QFormLayout* form = new QFormLayout(); layout->addLayout(form, 1); + + QHBoxLayout headModelLayout; QString faceURLString = applicationInstance->getAvatar()->getHead()->getFaceModel().getURL().toString(); - QLineEdit* faceURLEdit = new QLineEdit(faceURLString); - faceURLEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH); - faceURLEdit->setPlaceholderText(DEFAULT_HEAD_MODEL_URL.toString()); - form->addRow("Face URL:", faceURLEdit); - + QLineEdit headURLEdit(faceURLString); + QPushButton headBrowseButton(BROWSE_BUTTON_TEXT); + connect(&headBrowseButton, SIGNAL(clicked()), &browser, SLOT(browseHead())); + connect(&browser, SIGNAL(selectedHead(QString)), &headURLEdit, SLOT(setText(QString))); + headURLEdit.setReadOnly(true); + headURLEdit.setMinimumWidth(QLINE_MINIMUM_WIDTH); + headURLEdit.setPlaceholderText(DEFAULT_HEAD_MODEL_URL.toString()); + headModelLayout.addWidget(&headURLEdit); + headModelLayout.addWidget(&headBrowseButton); + form->addRow("Head URL:", &headModelLayout); + + QHBoxLayout skeletonModelLayout; QString skeletonURLString = applicationInstance->getAvatar()->getSkeletonModel().getURL().toString(); - QLineEdit* skeletonURLEdit = new QLineEdit(skeletonURLString); - skeletonURLEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH); - skeletonURLEdit->setPlaceholderText(DEFAULT_BODY_MODEL_URL.toString()); - form->addRow("Skeleton URL:", skeletonURLEdit); + QLineEdit skeletonURLEdit(skeletonURLString); + QPushButton SkeletonBrowseButton(BROWSE_BUTTON_TEXT); + connect(&SkeletonBrowseButton, SIGNAL(clicked()), &browser, SLOT(browseSkeleton())); + connect(&browser, SIGNAL(selectedSkeleton(QString)), &skeletonURLEdit, SLOT(setText(QString))); + skeletonURLEdit.setReadOnly(true); + skeletonURLEdit.setMinimumWidth(QLINE_MINIMUM_WIDTH); + skeletonURLEdit.setPlaceholderText(DEFAULT_BODY_MODEL_URL.toString()); + skeletonModelLayout.addWidget(&skeletonURLEdit); + skeletonModelLayout.addWidget(&SkeletonBrowseButton); + form->addRow("Skeleton URL:", &skeletonModelLayout); + QString displayNameString = applicationInstance->getAvatar()->getDisplayName(); QLineEdit* displayNameEdit = new QLineEdit(displayNameString); @@ -774,21 +795,17 @@ void Menu::editPreferences() { int ret = dialog.exec(); if (ret == QDialog::Accepted) { - QUrl faceModelURL(faceURLEdit->text()); - bool shouldDispatchIdentityPacket = false; - if (faceModelURL.toString() != faceURLString) { + if (headURLEdit.text() != faceURLString && !headURLEdit.text().isEmpty()) { // change the faceModelURL in the profile, it will also update this user's BlendFace - applicationInstance->getAvatar()->setFaceModelURL(faceModelURL); + applicationInstance->getAvatar()->setFaceModelURL(QUrl(headURLEdit.text())); shouldDispatchIdentityPacket = true; } - QUrl skeletonModelURL(skeletonURLEdit->text()); - - if (skeletonModelURL.toString() != skeletonURLString) { + if (skeletonURLEdit.text() != skeletonURLString && !skeletonURLEdit.text().isEmpty()) { // change the skeletonModelURL in the profile, it will also update this user's Body - applicationInstance->getAvatar()->setSkeletonModelURL(skeletonModelURL); + applicationInstance->getAvatar()->setSkeletonModelURL(QUrl(skeletonURLEdit.text())); shouldDispatchIdentityPacket = true; } From 1e2dabfd5e04f2d202e9d000b9ae8d9e077de698 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 18 Mar 2014 15:09:54 -0700 Subject: [PATCH 05/42] Changed addresses to new models locations --- interface/src/ModelBrowser.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ModelBrowser.h b/interface/src/ModelBrowser.h index 9b7cf51ab3..42dd65ab27 100644 --- a/interface/src/ModelBrowser.h +++ b/interface/src/ModelBrowser.h @@ -15,8 +15,8 @@ #include static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com"; -static const QString HEAD_MODELS_LOCATION = "meshes/"; -static const QString SKELETON_MODELS_LOCATION = "meshes/"; +static const QString HEAD_MODELS_LOCATION = "models/heads/"; +static const QString SKELETON_MODELS_LOCATION = "models/skeletons/"; enum ModelType { Head, From 76c2a9fdad11521763fa4a525b569d10e90cca46 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 18 Mar 2014 17:36:05 -0700 Subject: [PATCH 06/42] Modified Browser to be able to dowload all the fst file when there are more than 1000 --- interface/src/Menu.cpp | 11 ++-- interface/src/ModelBrowser.cpp | 105 ++++++++++++++++++++------------- interface/src/ModelBrowser.h | 21 ++++--- 3 files changed, 82 insertions(+), 55 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index e0f2ba14ff..8352adf9f2 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -694,7 +694,8 @@ void Menu::loginForCurrentDomain() { void Menu::editPreferences() { Application* applicationInstance = Application::getInstance(); - ModelBrowser browser; + ModelBrowser headBrowser(Head); + ModelBrowser skeletonBrowser(Skeleton); const QString BROWSE_BUTTON_TEXT = "Browse"; @@ -712,8 +713,8 @@ void Menu::editPreferences() { QString faceURLString = applicationInstance->getAvatar()->getHead()->getFaceModel().getURL().toString(); QLineEdit headURLEdit(faceURLString); QPushButton headBrowseButton(BROWSE_BUTTON_TEXT); - connect(&headBrowseButton, SIGNAL(clicked()), &browser, SLOT(browseHead())); - connect(&browser, SIGNAL(selectedHead(QString)), &headURLEdit, SLOT(setText(QString))); + connect(&headBrowseButton, SIGNAL(clicked()), &headBrowser, SLOT(browse())); + connect(&headBrowser, SIGNAL(selected (QString)), &headURLEdit, SLOT(setText(QString))); headURLEdit.setReadOnly(true); headURLEdit.setMinimumWidth(QLINE_MINIMUM_WIDTH); headURLEdit.setPlaceholderText(DEFAULT_HEAD_MODEL_URL.toString()); @@ -725,8 +726,8 @@ void Menu::editPreferences() { QString skeletonURLString = applicationInstance->getAvatar()->getSkeletonModel().getURL().toString(); QLineEdit skeletonURLEdit(skeletonURLString); QPushButton SkeletonBrowseButton(BROWSE_BUTTON_TEXT); - connect(&SkeletonBrowseButton, SIGNAL(clicked()), &browser, SLOT(browseSkeleton())); - connect(&browser, SIGNAL(selectedSkeleton(QString)), &skeletonURLEdit, SLOT(setText(QString))); + connect(&SkeletonBrowseButton, SIGNAL(clicked()), &skeletonBrowser, SLOT(browse())); + connect(&skeletonBrowser, SIGNAL(selected(QString)), &skeletonURLEdit, SLOT(setText(QString))); skeletonURLEdit.setReadOnly(true); skeletonURLEdit.setMinimumWidth(QLINE_MINIMUM_WIDTH); skeletonURLEdit.setPlaceholderText(DEFAULT_BODY_MODEL_URL.toString()); diff --git a/interface/src/ModelBrowser.cpp b/interface/src/ModelBrowser.cpp index cf401d6b75..e03081273f 100644 --- a/interface/src/ModelBrowser.cpp +++ b/interface/src/ModelBrowser.cpp @@ -19,23 +19,36 @@ #include "ModelBrowser.h" -static const int DOWNLOAD_TIMEOUT = 1000; +static const QString PREFIX_PARAMETER_NAME = "prefix"; +static const QString MARKER_PARAMETER_NAME = "marker"; +static const QString IS_TRUNCATED_NAME = "IsTruncated"; static const QString CONTAINER_NAME = "Contents"; static const QString KEY_NAME = "Key"; -ModelBrowser::ModelBrowser(QWidget* parent) : - QWidget(parent), - _downloader(QUrl(S3_URL)) -{ +ModelBrowser::ModelBrowser(ModelType modelType, QWidget* parent) : QWidget(parent), _type(modelType) { + QUrl url(S3_URL); + QUrlQuery query; + + if (_type == Head) { + query.addQueryItem(PREFIX_PARAMETER_NAME, HEAD_MODELS_LOCATION); + } else if (_type == Skeleton) { + query.addQueryItem(PREFIX_PARAMETER_NAME, SKELETON_MODELS_LOCATION); + } + url.setQuery(query); + + _downloader = new FileDownloader(url); + connect(_downloader, SIGNAL(done(QNetworkReply::NetworkError)), SLOT(downloadFinished())); } -QString ModelBrowser::browse(ModelType modelType) { - _models.clear(); - if (!parseXML(modelType)) { - return QString(); - } - - +ModelBrowser::~ModelBrowser() { + delete _downloader; +} + +void ModelBrowser::downloadFinished() { + parseXML(_downloader->getData()); +} + +void ModelBrowser::browse() { QDialog dialog(this); dialog.setWindowTitle("Browse models"); @@ -61,47 +74,38 @@ QString ModelBrowser::browse(ModelType modelType) { dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject())); if (dialog.exec() == QDialog::Rejected) { - return QString(); + return; } QString selectedKey = model->data(listView->currentIndex(), Qt::DisplayRole).toString(); - return _models[selectedKey]; -} - -void ModelBrowser::browseHead() { - QString model = browse(Head); - emit selectedHead(model); -} - -void ModelBrowser::browseSkeleton() { - QString model = browse(Skeleton); - emit selectedSkeleton(model); -} - -bool ModelBrowser::parseXML(ModelType modelType) { - _downloader.waitForFile(DOWNLOAD_TIMEOUT); - QString location; - switch (modelType) { - case Head: - location = HEAD_MODELS_LOCATION; - break; - case Skeleton: - location = SKELETON_MODELS_LOCATION; - break; - default: - return false; - } - QXmlStreamReader xml(_downloader.getData()); - QRegExp rx(location + "[^/]*fst"); + emit selected(_models[selectedKey]); +} + +bool ModelBrowser::parseXML(QByteArray xmlFile) { + QXmlStreamReader xml(xmlFile); + QRegExp rx(".*fst"); + bool truncated = false; + QString lastKey; // Read xml until the end or an error is detected while(!xml.atEnd() && !xml.hasError()) { + if(xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == IS_TRUNCATED_NAME) { + while(!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == IS_TRUNCATED_NAME)) { + // Let's check if there is more + xml.readNext(); + if (xml.text().toString() == "True") { + truncated = true; + } + } + } + if(xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == CONTAINER_NAME) { while(!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == CONTAINER_NAME)) { // If a file is find, process it if(xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == KEY_NAME) { xml.readNext(); + lastKey = xml.text().toString(); if (rx.exactMatch(xml.text().toString())) { // Add the found file to the list _models.insert(QFileInfo(xml.text().toString()).baseName(), @@ -123,5 +127,24 @@ bool ModelBrowser::parseXML(ModelType modelType) { QMessageBox::Ok); return false; } + + // If we didn't all the files, download the next ones + if (truncated) { + QUrl url(S3_URL); + QUrlQuery query; + + if (_type == Head) { + query.addQueryItem(PREFIX_PARAMETER_NAME, HEAD_MODELS_LOCATION); + } else if (_type == Skeleton) { + query.addQueryItem(PREFIX_PARAMETER_NAME, SKELETON_MODELS_LOCATION); + } + query.addQueryItem(MARKER_PARAMETER_NAME, lastKey); + url.setQuery(query); + + delete _downloader; + _downloader = new FileDownloader(url); + connect(_downloader, SIGNAL(done(QNetworkReply::NetworkError)), SLOT(downloadFinished())); + } + return true; } \ No newline at end of file diff --git a/interface/src/ModelBrowser.h b/interface/src/ModelBrowser.h index 42dd65ab27..9eb27f5db6 100644 --- a/interface/src/ModelBrowser.h +++ b/interface/src/ModelBrowser.h @@ -27,25 +27,28 @@ class ModelBrowser : public QWidget { Q_OBJECT public: - ModelBrowser(QWidget* parent = NULL); - - QString browse(ModelType modelType); + ModelBrowser(ModelType modelType, QWidget* parent = NULL); + ~ModelBrowser(); signals: - void selectedHead(QString); - void selectedSkeleton(QString); + void selected(QString); public slots: - void browseHead(); - void browseSkeleton(); + void browse(); + +private slots: + void downloadFinished(); private: - FileDownloader _downloader; + ModelType _type; + FileDownloader* _downloader; QHash _models; - bool parseXML(ModelType modelType); + bool parseXML(QByteArray xmlFile); }; + + class ListView : public QListView { Q_OBJECT public: From a80f81c2462954c43340a87818c5e1676f353191 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 18 Mar 2014 17:48:17 -0700 Subject: [PATCH 07/42] typo --- interface/src/Menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8352adf9f2..da5ca8e17c 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -714,7 +714,7 @@ void Menu::editPreferences() { QLineEdit headURLEdit(faceURLString); QPushButton headBrowseButton(BROWSE_BUTTON_TEXT); connect(&headBrowseButton, SIGNAL(clicked()), &headBrowser, SLOT(browse())); - connect(&headBrowser, SIGNAL(selected (QString)), &headURLEdit, SLOT(setText(QString))); + connect(&headBrowser, SIGNAL(selected(QString)), &headURLEdit, SLOT(setText(QString))); headURLEdit.setReadOnly(true); headURLEdit.setMinimumWidth(QLINE_MINIMUM_WIDTH); headURLEdit.setPlaceholderText(DEFAULT_HEAD_MODEL_URL.toString()); From 24c779e551dc500d3de7806bba311fc108fee604 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 18 Mar 2014 22:05:51 -0700 Subject: [PATCH 08/42] Fix bug in extrusion deleting first voxel made --- examples/editVoxels.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 31d483d798..62990e1d70 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -1271,7 +1271,6 @@ function mouseMoveEvent(event) { var dy = event.y - mouseY; if (Math.sqrt(dx*dx + dy*dy) > PIXELS_PER_EXTRUDE_VOXEL) { lastVoxelPosition = Vec3.sum(lastVoxelPosition, extrudeDirection); - Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s); Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z, extrudeScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue); mouseX = event.x; From 83026fa2ca830c802a99103010e3a02a79410600 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 18 Mar 2014 22:22:40 -0700 Subject: [PATCH 09/42] added skeet to gun, display joint names to log in crazy legs --- examples/crazylegs.js | 4 ++ examples/gun.js | 44 +++++++++++--- examples/voxelBird.js | 133 ------------------------------------------ 3 files changed, 40 insertions(+), 141 deletions(-) delete mode 100644 examples/voxelBird.js diff --git a/examples/crazylegs.js b/examples/crazylegs.js index c098758a38..099387e000 100644 --- a/examples/crazylegs.js +++ b/examples/crazylegs.js @@ -12,6 +12,10 @@ var AMPLITUDE = 45.0; var cumulativeTime = 0.0; +print("Joint List:"); +var jointList = MyAvatar.getJointNames(); +print(jointList); + Script.update.connect(function(deltaTime) { cumulativeTime += deltaTime; MyAvatar.setJointData("joint_R_hip", Quat.fromPitchYawRollDegrees(0.0, 0.0, AMPLITUDE * Math.sin(cumulativeTime * FREQUENCY))); diff --git a/examples/gun.js b/examples/gun.js index 29b60a94ad..94f3fd4ee3 100644 --- a/examples/gun.js +++ b/examples/gun.js @@ -12,6 +12,11 @@ // // + +function getRandomFloat(min, max) { + return Math.random() * (max - min) + min; +} + var lastX = 0; var lastY = 0; var yawFromMouse = 0; @@ -19,17 +24,22 @@ var pitchFromMouse = 0; var isMouseDown = false; var BULLET_VELOCITY = 5.0; +var MIN_THROWER_DELAY = 1000; +var MAX_THROWER_DELAY = 1000; var LEFT_BUTTON_3 = 3; // Load some sound to use for loading and firing var fireSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guns/GUN-SHOT2.raw"); var loadSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guns/Gun_Reload_Weapon22.raw"); var impactSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guns/BulletImpact2.raw"); -var targetLaunchSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Guns/GUN-SHOT2.raw"); +var targetHitSound = new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/Space%20Invaders/hit.raw"); +var targetLaunchSound = new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/Space%20Invaders/shoot.raw"); var audioOptions = new AudioInjectionOptions(); audioOptions.volume = 0.9; +var shotTime = new Date(); + // initialize our triggers var triggerPulled = new Array(); var numberOfTriggers = Controller.getNumberOfTriggers(); @@ -94,7 +104,9 @@ function shootTarget() { var DISTANCE_TO_LAUNCH_FROM = 3.0; var camera = Camera.getPosition(); //printVector("camera", camera); - var forwardVector = Quat.getFront(Camera.getOrientation()); + var targetDirection = Quat.angleAxis(getRandomFloat(-20.0, 20.0), { x:0, y:1, z:0 }); + targetDirection = Quat.multiply(Camera.getOrientation(), targetDirection); + var forwardVector = Quat.getFront(targetDirection); //printVector("forwardVector", forwardVector); var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_TO_LAUNCH_FROM)); //printVector("newPosition", newPosition); @@ -111,6 +123,9 @@ function shootTarget() { lifetime: 1000.0, damping: 0.99 }); + // Record start time + shotTime = new Date(); + // Play target shoot sound audioOptions.position = newPosition; Audio.playSound(targetLaunchSound, audioOptions); @@ -119,31 +134,43 @@ function shootTarget() { function particleCollisionWithVoxel(particle, voxel, penetration) { - Vec3.print('particleCollisionWithVoxel() ... penetration=', penetration); - var HOLE_SIZE = 0.125; var particleProperties = Particles.getParticleProperties(particle); var position = particleProperties.position; Particles.deleteParticle(particle); // Make a hole in this voxel + Vec3.print("penetration", penetration); + Vec3.print("position", position); + var pointOfEntry = Vec3.subtract(position, penetration); + Vec3.print("pointOfEntry", pointOfEntry); + Voxels.eraseVoxel(pointOfEntry.x, pointOfEntry.y, pointOfEntry.z, HOLE_SIZE); Voxels.eraseVoxel(position.x, position.y, position.z, HOLE_SIZE); //audioOptions.position = position; audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); - Audio.playSound(impactSound, audioOptions); + Audio.playSound(targetHitSound, audioOptions); } function particleCollisionWithParticle(particle1, particle2) { - print("Particle/Particle!"); score++; Overlays.editOverlay(text, { text: "Score: " + score } ); + // Sort out which particle is which + + // Record shot time + var endTime = new Date(); + var msecs = endTime.valueOf() - shotTime.valueOf(); + print("hit, msecs = " + msecs); Particles.deleteParticle(particle1); Particles.deleteParticle(particle2); + audioOptions.position = newPosition; + audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); + Audio.playSound(targetHitSound, audioOptions); } function keyPressEvent(event) { // if our tools are off, then don't do anything if (event.text == "t") { - shootTarget(); + var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY; + Script.setTimeout(shootTarget, time); } } @@ -164,7 +191,8 @@ function update(deltaTime) { // Check hydra controller for launch button press if (!isLaunchButtonPressed && Controller.isButtonPressed(LEFT_BUTTON_3)) { isLaunchButtonPressed = true; - shootTarget(); + var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY; + Script.setTimeout(shootTarget, time); } else if (isLaunchButtonPressed && !Controller.isButtonPressed(LEFT_BUTTON_3)) { isLaunchButtonPressed = false; diff --git a/examples/voxelBird.js b/examples/voxelBird.js deleted file mode 100644 index 1e33851ff6..0000000000 --- a/examples/voxelBird.js +++ /dev/null @@ -1,133 +0,0 @@ -// -// This sample script moves a voxel around like a bird and sometimes makes tweeting noises -// - -function vLength(v) { - return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z); -} - -function printVector(v) { - print(v.x + ", " + v.y + ", " + v.z + "\n"); -} - -// Create a random vector with individual lengths between a,b -function randVector(a, b) { - var rval = { x: a + Math.random() * (b - a), y: a + Math.random() * (b - a), z: a + Math.random() * (b - a) }; - return rval; -} - -function vMinus(a, b) { - var rval = { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z }; - return rval; -} - -function vPlus(a, b) { - var rval = { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z }; - return rval; -} - -function vCopy(a, b) { - a.x = b.x; - a.y = b.y; - a.z = b.z; - return; -} - -// Returns a vector which is fraction of the way between a and b -function vInterpolate(a, b, fraction) { - var rval = { x: a.x + (b.x - a.x) * fraction, y: a.y + (b.y - a.y) * fraction, z: a.z + (b.z - a.z) * fraction }; - return rval; -} - -// Decide what kind of bird we are -var tweet; - -var which = Math.random(); -if (which < 0.2) { - tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/bushtit_1.raw"); -} else if (which < 0.4) { - tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/rosyfacedlovebird.raw"); -} else if (which < 0.6) { - tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/saysphoebe.raw"); -} else if (which < 0.8) { - tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/mexicanWhipoorwill.raw"); -} else { - tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/westernscreechowl.raw"); -} - -var position = { x: 0, y: 0, z: 0 }; -var lastPosition = { x: 0, y: 0, z: 0 }; -var oldPosition = { x: 0, y: 0, z:0 }; -var targetPosition = { x: 0, y: 0, z: 0 }; - -var size = 0.125; -var range = 50.0; // Over what distance in meters do you want your bird to fly around -var color = { r: 100, g: 50, b: 150 }; -var colorEdge = { r:255, g:250, b:175 }; -var frame = 0; -var thisColor = color; -var moving = false; -var tweeting = 0; -var moved = true; - -var CHANCE_OF_MOVING = 0.05; -var CHANCE_OF_TWEETING = 0.05; - -function moveBird(deltaTime) { - frame++; - if (frame % 3 == 0) { - // Tweeting behavior - if (tweeting == 0) { - if (Math.random() < CHANCE_OF_TWEETING) { - //print("tweet!" + "\n"); - var options = new AudioInjectionOptions();
 - options.position = position; - options.volume = 0.75; - Audio.playSound(tweet, options); - tweeting = 10; - } - } else { - tweeting -= 1; - } - // Moving behavior - if (moving == false) { - if (Math.random() < CHANCE_OF_MOVING) { - targetPosition = randVector(0, range); - //printVector(position); - moving = true; - } - } - if (moving) { - position = vInterpolate(position, targetPosition, 0.5); - if (vLength(vMinus(position, targetPosition)) < (size / 2.0)) { - moved = false; - moving = false; - } else { - moved = true; - } - } - - if (tweeting > 0) { - // Change color of voxel to blinky red a bit while playing the sound - var blinkColor = { r: Math.random() * 255, g: 0, b: 0 }; - Voxels.setVoxel(position.x, - position.y, - position.z, - size, - blinkColor.r, blinkColor.g, blinkColor.b); - } - if (moved) { - Voxels.setVoxel(position.x, position.y, position.z, size, thisColor.r, thisColor.g, thisColor.b); - // delete old voxel - - Voxels.eraseVoxel(oldPosition.x, oldPosition.y, oldPosition.z, size); - // Copy old location to new - vCopy(oldPosition, position); - moved = false; - } - } -} - -Voxels.setPacketsPerSecond(10000); -// Connect a call back that happens every frame -Script.update.connect(moveBird); \ No newline at end of file From 9c8a4828f2ca6337562ec5f61c2797c950b62e85 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Tue, 18 Mar 2014 22:41:09 -0700 Subject: [PATCH 10/42] remove unused variable, function --- interface/src/avatar/MyAvatar.cpp | 5 ----- libraries/avatars/src/HandData.cpp | 3 --- 2 files changed, 8 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6c51026097..fb0d704c6a 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -424,11 +424,6 @@ void MyAvatar::updateFromGyros(float deltaTime) { } } -static TextRenderer* textRenderer() { - static TextRenderer* renderer = new TextRenderer(SANS_FONT_FAMILY, 24, -1, false, TextRenderer::SHADOW_EFFECT); - return renderer; -} - void MyAvatar::renderDebugBodyPoints() { glm::vec3 torsoPosition(getPosition()); glm::vec3 headPosition(getHead()->getEyePosition()); diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index c84ed77dae..3334dbb41d 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -14,9 +14,6 @@ #include -// When converting between fixed and float, use this as the radix. -const int fingerVectorRadix = 4; - HandData::HandData(AvatarData* owningAvatar) : _owningAvatarData(owningAvatar) { From d239aecbfbec8eab1983508b9c3466de76fcb196 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 19 Mar 2014 10:17:28 -0700 Subject: [PATCH 11/42] Added names to signals argument --- interface/src/ModelBrowser.h | 2 +- libraries/shared/src/FileDownloader.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ModelBrowser.h b/interface/src/ModelBrowser.h index 9eb27f5db6..4628642e89 100644 --- a/interface/src/ModelBrowser.h +++ b/interface/src/ModelBrowser.h @@ -31,7 +31,7 @@ public: ~ModelBrowser(); signals: - void selected(QString); + void selected(QString filename); public slots: void browse(); diff --git a/libraries/shared/src/FileDownloader.h b/libraries/shared/src/FileDownloader.h index a2ed0b8ccb..593b39b013 100644 --- a/libraries/shared/src/FileDownloader.h +++ b/libraries/shared/src/FileDownloader.h @@ -28,7 +28,7 @@ public: static QByteArray download(const QUrl dataURL, int timeout = 0); signals: - void done(QNetworkReply::NetworkError); + void done(QNetworkReply::NetworkError error); private slots: void processReply(QNetworkReply* reply); From 69a83ee94d7e8479df945178a9a7bed6074559fb Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 19 Mar 2014 16:27:27 -0700 Subject: [PATCH 12/42] add bots able to see voxels --- examples/bot.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/examples/bot.js b/examples/bot.js index 8485a2b993..d6f9f8344c 100644 --- a/examples/bot.js +++ b/examples/bot.js @@ -28,7 +28,9 @@ var CHANCE_OF_HEAD_TURNING = 0.05; var CHANCE_OF_BIG_MOVE = 0.1; var CHANCE_OF_WAVING = 0.000; // Currently this isn't working -var shouldReceiveVoxels = false; +var shouldReceiveVoxels = true; +var VOXEL_FPS = 60.0; +var lastVoxelQueryTime = 0.0; var isMoving = false; var isTurningHead = false; @@ -130,6 +132,17 @@ function updateBehavior(deltaTime) { cumulativeTime += deltaTime; + if (shouldReceiveVoxels && ((cumulativeTime - lastVoxelQueryTime) > (1.0 / VOXEL_FPS))) { + VoxelViewer.setPosition(Avatar.position); + VoxelViewer.setOrientation(Avatar.orientation); + VoxelViewer.queryOctree(); + lastVoxelQueryTime = cumulativeTime; + /* + if (Math.random() < (1.0 / VOXEL_FPS)) { + print("Voxels in view = " + VoxelViewer.getOctreeElementsCount()); + }*/ + } + if (!isWaving && (Math.random() < CHANCE_OF_WAVING)) { isWaving = true; waveFrequency = 1.0 + Math.random() * 5.0; From fb73b6e1cea0d5ed064df6af695c27eb061f417e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 19 Mar 2014 17:57:01 -0700 Subject: [PATCH 13/42] collect min and max loudness for each frame --- assignment-client/src/audio/AudioMixer.cpp | 11 +++++++++-- assignment-client/src/audio/AudioMixer.h | 3 +++ .../src/audio/AudioMixerClientData.cpp | 14 +++++++++++++- assignment-client/src/audio/AudioMixerClientData.h | 2 +- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 988bcc1da7..2bece55ac4 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -62,7 +62,9 @@ void attachNewBufferToNode(Node *newNode) { } AudioMixer::AudioMixer(const QByteArray& packet) : - ThreadedAssignment(packet) + ThreadedAssignment(packet), + _minSourceLoudnessInFrame(1.0f), + _maxSourceLoudnessInFrame(0.0f) { } @@ -353,9 +355,14 @@ void AudioMixer::run() { while (!_isFinished) { + _minSourceLoudnessInFrame = 1.0f; + _maxSourceLoudnessInFrame = 0.0f; + foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { if (node->getLinkedData()) { - ((AudioMixerClientData*) node->getLinkedData())->checkBuffersBeforeFrameSend(JITTER_BUFFER_SAMPLES); + ((AudioMixerClientData*) node->getLinkedData())->checkBuffersBeforeFrameSend(JITTER_BUFFER_SAMPLES, + _minSourceLoudnessInFrame, + _maxSourceLoudnessInFrame); } } diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 5a68b0023f..6056dbd318 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -38,6 +38,9 @@ private: // client samples capacity is larger than what will be sent to optimize mixing int16_t _clientSamples[NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + SAMPLE_PHASE_DELAY_AT_90]; + + float _minSourceLoudnessInFrame; + float _maxSourceLoudnessInFrame; }; #endif /* defined(__hifi__AudioMixer__) */ diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index b2da0a0aaa..9b8cdb8709 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -6,6 +6,8 @@ // Copyright (c) 2013 HighFidelity, Inc. All rights reserved. // +#include + #include #include @@ -82,7 +84,9 @@ int AudioMixerClientData::parseData(const QByteArray& packet) { return 0; } -void AudioMixerClientData::checkBuffersBeforeFrameSend(int jitterBufferLengthSamples) { +void AudioMixerClientData::checkBuffersBeforeFrameSend(int jitterBufferLengthSamples, + float& currentMinLoudness, + float& currentMaxLoudness) { for (unsigned int i = 0; i < _ringBuffers.size(); i++) { if (_ringBuffers[i]->shouldBeAddedToMix(jitterBufferLengthSamples)) { // this is a ring buffer that is ready to go @@ -92,6 +96,14 @@ void AudioMixerClientData::checkBuffersBeforeFrameSend(int jitterBufferLengthSam // calculate the average loudness for the next NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL // that would be mixed in _nextOutputLoudness = _ringBuffers[i]->averageLoudnessForBoundarySamples(NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); + + if (_nextOutputLoudness < currentMinLoudness) { + currentMinLoudness = _nextOutputLoudness; + } + + if (_nextOutputLoudness > currentMaxLoudness) { + currentMaxLoudness = _nextOutputLoudness; + } } } } diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index bb10098e23..701b2befba 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -27,7 +27,7 @@ public: float getNextOutputLoudness() const { return _nextOutputLoudness; } int parseData(const QByteArray& packet); - void checkBuffersBeforeFrameSend(int jitterBufferLengthSamples); + void checkBuffersBeforeFrameSend(int jitterBufferLengthSamples, float& currentMinLoudness, float& currentMaxLoudness); void pushBuffersAfterFrameSend(); private: std::vector _ringBuffers; From 3ab0288c4f133a9841452540e4240f1ff1803d9b Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 20 Mar 2014 08:54:12 -0700 Subject: [PATCH 14/42] testing numbered joint movement --- examples/bot.js | 8 ++------ libraries/avatars/src/HandData.cpp | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/examples/bot.js b/examples/bot.js index 201a629a92..f2cc3e23a1 100644 --- a/examples/bot.js +++ b/examples/bot.js @@ -26,7 +26,7 @@ var CHANCE_OF_MOVING = 0.005; var CHANCE_OF_SOUND = 0.005; var CHANCE_OF_HEAD_TURNING = 0.05; var CHANCE_OF_BIG_MOVE = 0.1; -var CHANCE_OF_WAVING = 0.000; // Currently this isn't working +var CHANCE_OF_WAVING = 0.005; // Currently this isn't working var shouldReceiveVoxels = true; var VOXEL_FPS = 60.0; @@ -141,17 +141,13 @@ function updateBehavior(deltaTime) { waveAmplitude = 5.0 + Math.random() * 60.0; Script.setTimeout(stopWaving, 1000 + Math.random() * 2000); } else if (isWaving) { - Avatar.setJointData("joint_L_shoulder", Quat.fromPitchYawRollDegrees(0.0, 0.0, waveAmplitude * Math.sin(cumulativeTime * waveFrequency))); + Avatar.setJointData(15, Quat.fromPitchYawRollDegrees(0.0, 0.0, waveAmplitude * Math.sin(cumulativeTime * waveFrequency))); } if (Math.random() < CHANCE_OF_SOUND) { playRandomSound(); } - if (Agent.isPlayingAvatarSound) { - Avatar.handPosition = Vec3.sum(Avatar.position, Quat.getFront(Avatar.orientation)); - } - if (!isTurningHead && (Math.random() < CHANCE_OF_HEAD_TURNING)) { targetHeadPitch = getRandomFloat(-PITCH_RANGE, PITCH_RANGE); isTurningHead = true; diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index e983c0e705..78ebf4b921 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -11,7 +11,7 @@ #include #include -#include "AvatarData.h" +#include "AvatarData.h" #include "HandData.h" From 6440dd1b5fa9ed1fc3e5b27e1e3a873e06dcf633 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 20 Mar 2014 11:05:14 -0700 Subject: [PATCH 15/42] explicitly send neck angles in avatar data --- libraries/avatars/src/AvatarData.cpp | 26 ++++++++++++++++++++++++++ libraries/shared/src/PacketHeaders.cpp | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index e5e0e4b3d7..4e57e311eb 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -88,6 +88,17 @@ QByteArray AvatarData::toByteArray() { // Body scale destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _targetScale); + // Head rotation (NOTE: This needs to become a quaternion to save two bytes) + destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->getTweakedYaw()); + destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->getTweakedPitch()); + destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _headData->getTweakedRoll()); + + // Head lean X,Z (head lateral and fwd/back motion relative to torso) + memcpy(destinationBuffer, &_headData->_leanSideways, sizeof(_headData->_leanSideways)); + destinationBuffer += sizeof(_headData->_leanSideways); + memcpy(destinationBuffer, &_headData->_leanForward, sizeof(_headData->_leanForward)); + destinationBuffer += sizeof(_headData->_leanForward); + // Lookat Position memcpy(destinationBuffer, &_headData->_lookAtPosition, sizeof(_headData->_lookAtPosition)); destinationBuffer += sizeof(_headData->_lookAtPosition); @@ -191,6 +202,21 @@ int AvatarData::parseDataAtOffset(const QByteArray& packet, int offset) { // Body scale sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer, _targetScale); + // Head rotation (NOTE: This needs to become a quaternion to save two bytes) + float headYaw, headPitch, headRoll; + sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headYaw); + sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headPitch); + sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &headRoll); + _headData->setYaw(headYaw); + _headData->setPitch(headPitch); + _headData->setRoll(headRoll); + + // Head position relative to pelvis + memcpy(&_headData->_leanSideways, sourceBuffer, sizeof(_headData->_leanSideways)); + sourceBuffer += sizeof(float); + memcpy(&_headData->_leanForward, sourceBuffer, sizeof(_headData->_leanForward)); + sourceBuffer += sizeof(_headData->_leanForward); + // Lookat Position memcpy(&_headData->_lookAtPosition, sourceBuffer, sizeof(_headData->_lookAtPosition)); sourceBuffer += sizeof(_headData->_lookAtPosition); diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp index 307453d8bf..7d436b9ca6 100644 --- a/libraries/shared/src/PacketHeaders.cpp +++ b/libraries/shared/src/PacketHeaders.cpp @@ -45,7 +45,7 @@ int packArithmeticallyCodedValue(int value, char* destination) { PacketVersion versionForPacketType(PacketType type) { switch (type) { case PacketTypeAvatarData: - return 2; + return 3; case PacketTypeParticleData: return 1; case PacketTypeDomainList: From f92f45df63f7460a81193c50b865fab76cb097ab Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 20 Mar 2014 11:17:36 -0700 Subject: [PATCH 16/42] Keep track of keys pressed, synthesize release events when focus is lost. Closes #2375. --- interface/interface_en.ts | 8 +-- interface/src/Application.cpp | 92 +++++++++++++++++++---------------- interface/src/Application.h | 5 ++ interface/src/GLCanvas.cpp | 4 ++ interface/src/GLCanvas.h | 2 + 5 files changed, 66 insertions(+), 45 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 75ada1910c..6ec3f65acb 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -4,22 +4,22 @@ Application - + Export Voxels - + Sparse Voxel Octree Files (*.svo) - + Open Script - + JavaScript Files (*.js) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 50dbf1b51a..3854247e22 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -692,6 +692,8 @@ bool Application::event(QEvent* event) { void Application::keyPressEvent(QKeyEvent* event) { + _keysPressed.insert(event->key()); + _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts // if one of our scripts have asked to capture this event, then stop processing it @@ -914,6 +916,8 @@ void Application::keyPressEvent(QKeyEvent* event) { void Application::keyReleaseEvent(QKeyEvent* event) { + _keysPressed.remove(event->key()); + _controllerScriptingInterface.emitKeyReleaseEvent(event); // send events to any registered scripts // if one of our scripts have asked to capture this event, then stop processing it @@ -921,60 +925,66 @@ void Application::keyReleaseEvent(QKeyEvent* event) { return; } + switch (event->key()) { + case Qt::Key_E: + _myAvatar->setDriveKeys(UP, 0.f); + break; - if (activeWindow() == _window) { - switch (event->key()) { - case Qt::Key_E: - _myAvatar->setDriveKeys(UP, 0.f); - break; + case Qt::Key_C: + _myAvatar->setDriveKeys(DOWN, 0.f); + break; - case Qt::Key_C: - _myAvatar->setDriveKeys(DOWN, 0.f); - break; + case Qt::Key_W: + _myAvatar->setDriveKeys(FWD, 0.f); + break; - case Qt::Key_W: - _myAvatar->setDriveKeys(FWD, 0.f); - break; + case Qt::Key_S: + _myAvatar->setDriveKeys(BACK, 0.f); + break; - case Qt::Key_S: - _myAvatar->setDriveKeys(BACK, 0.f); - break; + case Qt::Key_A: + _myAvatar->setDriveKeys(ROT_LEFT, 0.f); + break; - case Qt::Key_A: - _myAvatar->setDriveKeys(ROT_LEFT, 0.f); - break; + case Qt::Key_D: + _myAvatar->setDriveKeys(ROT_RIGHT, 0.f); + break; - case Qt::Key_D: - _myAvatar->setDriveKeys(ROT_RIGHT, 0.f); - break; + case Qt::Key_Up: + _myAvatar->setDriveKeys(FWD, 0.f); + _myAvatar->setDriveKeys(UP, 0.f); + break; - case Qt::Key_Up: - _myAvatar->setDriveKeys(FWD, 0.f); - _myAvatar->setDriveKeys(UP, 0.f); - break; + case Qt::Key_Down: + _myAvatar->setDriveKeys(BACK, 0.f); + _myAvatar->setDriveKeys(DOWN, 0.f); + break; - case Qt::Key_Down: - _myAvatar->setDriveKeys(BACK, 0.f); - _myAvatar->setDriveKeys(DOWN, 0.f); - break; + case Qt::Key_Left: + _myAvatar->setDriveKeys(LEFT, 0.f); + _myAvatar->setDriveKeys(ROT_LEFT, 0.f); + break; - case Qt::Key_Left: - _myAvatar->setDriveKeys(LEFT, 0.f); - _myAvatar->setDriveKeys(ROT_LEFT, 0.f); - break; + case Qt::Key_Right: + _myAvatar->setDriveKeys(RIGHT, 0.f); + _myAvatar->setDriveKeys(ROT_RIGHT, 0.f); + break; - case Qt::Key_Right: - _myAvatar->setDriveKeys(RIGHT, 0.f); - _myAvatar->setDriveKeys(ROT_RIGHT, 0.f); - break; - - default: - event->ignore(); - break; - } + default: + event->ignore(); + break; } } +void Application::focusOutEvent(QFocusEvent* event) { + // synthesize events for keys currently pressed, since we may not get their release events + foreach (int key, _keysPressed) { + QKeyEvent event(QEvent::KeyRelease, key, Qt::NoModifier); + keyReleaseEvent(&event); + } + _keysPressed.clear(); +} + void Application::mouseMoveEvent(QMouseEvent* event) { _controllerScriptingInterface.emitMouseMoveEvent(event); // send events to any registered scripts diff --git a/interface/src/Application.h b/interface/src/Application.h index cbfbf4166d..28060113a9 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -122,6 +123,8 @@ public: void keyPressEvent(QKeyEvent* event); void keyReleaseEvent(QKeyEvent* event); + void focusOutEvent(QFocusEvent* event); + void mouseMoveEvent(QMouseEvent* event); void mousePressEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event); @@ -432,6 +435,8 @@ private: bool _mousePressed; // true if mouse has been pressed (clear when finished) + QSet _keysPressed; + GeometryCache _geometryCache; TextureCache _textureCache; diff --git a/interface/src/GLCanvas.cpp b/interface/src/GLCanvas.cpp index a91452c06d..513dcfe40c 100644 --- a/interface/src/GLCanvas.cpp +++ b/interface/src/GLCanvas.cpp @@ -55,6 +55,10 @@ void GLCanvas::keyReleaseEvent(QKeyEvent* event) { Application::getInstance()->keyReleaseEvent(event); } +void GLCanvas::focusOutEvent(QFocusEvent* event) { + Application::getInstance()->focusOutEvent(event); +} + void GLCanvas::mouseMoveEvent(QMouseEvent* event) { Application::getInstance()->mouseMoveEvent(event); } diff --git a/interface/src/GLCanvas.h b/interface/src/GLCanvas.h index ad396a48ce..f7f7fb7c20 100644 --- a/interface/src/GLCanvas.h +++ b/interface/src/GLCanvas.h @@ -31,6 +31,8 @@ protected: virtual void keyPressEvent(QKeyEvent* event); virtual void keyReleaseEvent(QKeyEvent* event); + virtual void focusOutEvent(QFocusEvent* event); + virtual void mouseMoveEvent(QMouseEvent* event); virtual void mousePressEvent(QMouseEvent* event); virtual void mouseReleaseEvent(QMouseEvent* event); From 4513e1675d9bf9ea151963d3870a132204338547 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 20 Mar 2014 11:35:57 -0700 Subject: [PATCH 17/42] More line number changes in the translation file. --- interface/interface_en.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 6ec3f65acb..1ef7f1656f 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file From 20ae5c15f74f761e556019a864c67e5e18f89a17 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 20 Mar 2014 12:09:20 -0700 Subject: [PATCH 18/42] Added a "flat" mode for environments that makes them follow you around on the X/Z axes. The default environment is flat. Closes #2378. --- interface/interface_en.ts | 12 ++++++------ interface/src/Application.cpp | 6 ++++-- interface/src/Environment.cpp | 16 ++++++++++------ libraries/shared/src/PacketHeaders.cpp | 2 ++ libraries/voxels/src/EnvironmentData.cpp | 15 +++++++++++++++ libraries/voxels/src/EnvironmentData.h | 8 ++++++++ 6 files changed, 45 insertions(+), 14 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 6ec3f65acb..34e3614716 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -14,12 +14,12 @@ - + Open Script - + JavaScript Files (*.js) @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3854247e22..2243c49ac8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2150,7 +2150,8 @@ void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) { } glm::vec3 Application::getSunDirection() { - return glm::normalize(_environment.getClosestData(_myCamera.getPosition()).getSunLocation() - _myCamera.getPosition()); + return glm::normalize(_environment.getClosestData(_myCamera.getPosition()).getSunLocation(_myCamera.getPosition()) - + _myCamera.getPosition()); } void Application::updateShadowMap() { @@ -2312,7 +2313,8 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { float alpha = 1.0f; if (Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) { const EnvironmentData& closestData = _environment.getClosestData(whichCamera.getPosition()); - float height = glm::distance(whichCamera.getPosition(), closestData.getAtmosphereCenter()); + float height = glm::distance(whichCamera.getPosition(), + closestData.getAtmosphereCenter(whichCamera.getPosition())); if (height < closestData.getAtmosphereInnerRadius()) { alpha = 0.0f; diff --git a/interface/src/Environment.cpp b/interface/src/Environment.cpp index 1f9e23bee1..096b8770fb 100644 --- a/interface/src/Environment.cpp +++ b/interface/src/Environment.cpp @@ -92,7 +92,7 @@ glm::vec3 Environment::getGravity (const glm::vec3& position) { foreach (const ServerData& serverData, _data) { foreach (const EnvironmentData& environmentData, serverData) { - glm::vec3 vector = environmentData.getAtmosphereCenter() - position; + glm::vec3 vector = environmentData.getAtmosphereCenter(position) - position; float surfaceRadius = environmentData.getAtmosphereInnerRadius(); if (glm::length(vector) <= surfaceRadius) { // At or inside a planet, gravity is as set for the planet @@ -116,7 +116,7 @@ const EnvironmentData Environment::getClosestData(const glm::vec3& position) { float closestDistance = FLT_MAX; foreach (const ServerData& serverData, _data) { foreach (const EnvironmentData& environmentData, serverData) { - float distance = glm::distance(position, environmentData.getAtmosphereCenter()) - + float distance = glm::distance(position, environmentData.getAtmosphereCenter(position)) - environmentData.getAtmosphereOuterRadius(); if (distance < closestDistance) { closest = environmentData; @@ -132,6 +132,8 @@ bool Environment::findCapsulePenetration(const glm::vec3& start, const glm::vec3 // collide with the "floor" bool found = findCapsulePlanePenetration(start, end, radius, glm::vec4(0.0f, 1.0f, 0.0f, 0.0f), penetration); + glm::vec3 middle = (start + end) * 0.5f; + // get the lock for the duration of the call QMutexLocker locker(&_mutex); @@ -141,7 +143,7 @@ bool Environment::findCapsulePenetration(const glm::vec3& start, const glm::vec3 continue; // don't bother colliding with gravity-less environments } glm::vec3 environmentPenetration; - if (findCapsuleSpherePenetration(start, end, radius, environmentData.getAtmosphereCenter(), + if (findCapsuleSpherePenetration(start, end, radius, environmentData.getAtmosphereCenter(middle), environmentData.getAtmosphereInnerRadius(), environmentPenetration)) { penetration = addPenetrations(penetration, environmentPenetration); found = true; @@ -203,10 +205,12 @@ ProgramObject* Environment::createSkyProgram(const char* from, int* locations) { } void Environment::renderAtmosphere(Camera& camera, const EnvironmentData& data) { + glm::vec3 center = data.getAtmosphereCenter(camera.getPosition()); + glPushMatrix(); - glTranslatef(data.getAtmosphereCenter().x, data.getAtmosphereCenter().y, data.getAtmosphereCenter().z); - - glm::vec3 relativeCameraPos = camera.getPosition() - data.getAtmosphereCenter(); + glTranslatef(center.x, center.y, center.z); + + glm::vec3 relativeCameraPos = camera.getPosition() - center; float height = glm::length(relativeCameraPos); // use the appropriate shader depending on whether we're inside or outside diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp index 7d436b9ca6..c7518708ce 100644 --- a/libraries/shared/src/PacketHeaders.cpp +++ b/libraries/shared/src/PacketHeaders.cpp @@ -46,6 +46,8 @@ PacketVersion versionForPacketType(PacketType type) { switch (type) { case PacketTypeAvatarData: return 3; + case PacketTypeEnvironmentData: + return 1; case PacketTypeParticleData: return 1; case PacketTypeDomainList: diff --git a/libraries/voxels/src/EnvironmentData.cpp b/libraries/voxels/src/EnvironmentData.cpp index 5cee88e127..1c9af55abd 100644 --- a/libraries/voxels/src/EnvironmentData.cpp +++ b/libraries/voxels/src/EnvironmentData.cpp @@ -14,6 +14,7 @@ // GameEngine.cpp EnvironmentData::EnvironmentData(int id) : _id(id), + _flat(true), _gravity(0.0f), _atmosphereCenter(0, -1000, 0), _atmosphereInnerRadius(1000), @@ -25,12 +26,23 @@ EnvironmentData::EnvironmentData(int id) : _sunBrightness(20.0f) { } +glm::vec3 EnvironmentData::getAtmosphereCenter(const glm::vec3& cameraPosition) const { + return _atmosphereCenter + (_flat ? glm::vec3(cameraPosition.x, 0.0f, cameraPosition.z) : glm::vec3()); +} + +glm::vec3 EnvironmentData::getSunLocation(const glm::vec3& cameraPosition) const { + return _sunLocation + (_flat ? glm::vec3(cameraPosition.x, 0.0f, cameraPosition.z) : glm::vec3()); +} + int EnvironmentData::getBroadcastData(unsigned char* destinationBuffer) const { unsigned char* bufferStart = destinationBuffer; memcpy(destinationBuffer, &_id, sizeof(_id)); destinationBuffer += sizeof(_id); + memcpy(destinationBuffer, &_flat, sizeof(_flat)); + destinationBuffer += sizeof(_flat); + memcpy(destinationBuffer, &_gravity, sizeof(_gravity)); destinationBuffer += sizeof(_gravity); @@ -67,6 +79,9 @@ int EnvironmentData::parseData(const unsigned char* sourceBuffer, int numBytes) memcpy(&_id, sourceBuffer, sizeof(_id)); sourceBuffer += sizeof(_id); + memcpy(&_flat, sourceBuffer, sizeof(_flat)); + sourceBuffer += sizeof(_flat); + memcpy(&_gravity, sourceBuffer, sizeof(_gravity)); sourceBuffer += sizeof(_gravity); diff --git a/libraries/voxels/src/EnvironmentData.h b/libraries/voxels/src/EnvironmentData.h index 90cc0763fe..627a661e1c 100644 --- a/libraries/voxels/src/EnvironmentData.h +++ b/libraries/voxels/src/EnvironmentData.h @@ -19,6 +19,9 @@ public: void setID(int id) { _id = id; } int getID() const { return _id; } + void setFlat(bool flat) { _flat = flat; } + bool isFlat() const { return _flat; } + void setGravity(float gravity) { _gravity = gravity; } float getGravity() const { return _gravity; } @@ -42,6 +45,9 @@ public: const glm::vec3& getSunLocation() const { return _sunLocation; } float getSunBrightness() const { return _sunBrightness; } + glm::vec3 getAtmosphereCenter(const glm::vec3& cameraPosition) const; + glm::vec3 getSunLocation(const glm::vec3& cameraPosition) const; + int getBroadcastData(unsigned char* destinationBuffer) const; int parseData(const unsigned char* sourceBuffer, int numBytes); @@ -49,6 +55,8 @@ private: int _id; + bool _flat; + float _gravity; glm::vec3 _atmosphereCenter; From 39a6e0347650943398e34cc89b57048e67c55cc0 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 20 Mar 2014 12:09:33 -0700 Subject: [PATCH 19/42] Modified MyAvatar to support more JS --- interface/src/Application.cpp | 2 +- interface/src/avatar/MyAvatar.cpp | 5 ++--- interface/src/avatar/MyAvatar.h | 2 ++ 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 50dbf1b51a..8fcc49cdf1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1680,7 +1680,7 @@ void Application::updateMyAvatarLookAtPosition() { } else { // look in direction of the mouse ray, but use distance from intersection, if any float distance = TREE_SCALE; - if (_myAvatar->getLookAtTargetAvatar()) { + if (_myAvatar->getLookAtTargetAvatar() && _myAvatar != _myAvatar->getLookAtTargetAvatar()) { distance = glm::distance(_mouseRayOrigin, static_cast(_myAvatar->getLookAtTargetAvatar())->getHead()->calculateAverageEyePosition()); } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6c51026097..f1c8c6eedf 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -587,16 +587,15 @@ void MyAvatar::updateLookAtTargetAvatar() { foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) { Avatar* avatar = static_cast(avatarPointer.data()); - if (avatar == static_cast(this)) { - continue; - } float distance; if (avatar->findRayIntersection(mouseOrigin, mouseDirection, distance)) { _lookAtTargetAvatar = avatarPointer; + _targetAvatarPosition = avatarPointer->getPosition(); return; } } _lookAtTargetAvatar.clear(); + _targetAvatarPosition = glm::vec3(0, 0, 0); } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 3544fb1a61..d958103fa6 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -76,6 +76,7 @@ public: void orbit(const glm::vec3& position, int deltaX, int deltaY); + Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; } AvatarData* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); } void updateLookAtTargetAvatar(); void clearLookAtTargetAvatar(); @@ -116,6 +117,7 @@ private: glm::vec3 _moveTarget; int _moveTargetStepCounter; QWeakPointer _lookAtTargetAvatar; + glm::vec3 _targetAvatarPosition; bool _shouldRender; bool _billboardValid; From a95befb02e7a936691161bf34db5b3dd7b4551d4 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 20 Mar 2014 12:10:17 -0700 Subject: [PATCH 20/42] Added new script inspect.js --- examples/inspect.js | 240 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 examples/inspect.js diff --git a/examples/inspect.js b/examples/inspect.js new file mode 100644 index 0000000000..bbddb77883 --- /dev/null +++ b/examples/inspect.js @@ -0,0 +1,240 @@ +/ +// inspect.js +// hifi +// +// Created by Clément Brisset on March 20, 2014 +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// Allows you to inspect non moving objects (Voxels or Avatars) using Atl, Control (Command on Mac) and Shift +// +// radial mode = hold ALT +// orbit mode = hold ALT + CONTROL +// pan mode = hold ALT + CONTROL + SHIFT +// Once you are in a mode left click on the object to inspect and hold the click +// Dragging the mouse will move your camera according to the mode you are in. +// + +var AZIMUTH_RATE = 90.0; +var ALTITUDE_RATE = 200.0; +var RADIUS_RATE = 20.0; +var PAN_RATE = 50.0; + +var alt = false; +var shift = false; +var control = false; + +var isActive = false; + +var noMode = 0; +var orbitMode = 1; +var radialMode = 2; +var panningMode = 3; + +var mode = noMode; + +var mouseLastX = 0; +var mouseLastY = 0; + + +var center = { x: 0, y: 0, z: 0 }; +var position = { x: 0, y: 0, z: 0 }; +var vector = { x: 0, y: 0, z: 0 }; +var radius = 0.0; +var azimuth = 0.0; +var altitude = 0.0; + + +function handleRadialMode(dx, dy) { + azimuth += dx / AZIMUTH_RATE; + radius += radius * dy / RADIUS_RATE; + if (radius < 1) { + radius = 1; + } + + vector = { x: (Math.cos(altitude) * Math.cos(azimuth)) * radius, + y: Math.sin(altitude) * radius, + z: (Math.cos(altitude) * Math.sin(azimuth)) * radius }; + position = Vec3.sum(center, vector); + Camera.setPosition(position); +} + +function handleOrbitMode(dx, dy) { + azimuth += dx / AZIMUTH_RATE; + altitude += dy / ALTITUDE_RATE; + + vector = { x:(Math.cos(altitude) * Math.cos(azimuth)) * radius, + y:Math.sin(altitude) * radius, + z:(Math.cos(altitude) * Math.sin(azimuth)) * radius }; + position = Vec3.sum(center, vector); + Camera.setPosition(position); +} + + +function handlePanMode(dx, dy) { + var up = Quat.getUp(Camera.getOrientation()); + var right = Quat.getRight(Camera.getOrientation()); + var distance = Vec3.length(vector); + + var dv = Vec3.sum(Vec3.multiply(up, - distance * dy / PAN_RATE), Vec3.multiply(right, distance * dx / PAN_RATE)); + + center = Vec3.sum(center, dv); + position = Vec3.sum(position, dv); + + Camera.setPosition(position); + Camera.keepLookingAt(center); +} + +function saveCameraState() { + oldMode = Camera.getMode(); + var oldPosition = Camera.getPosition(); + Camera.setMode("independent"); + Camera.setPosition(oldPosition); +} + +function restoreCameraState() { + Camera.stopLooking(); + Camera.setMode(oldMode); +} + +function handleModes() { + var newMode = noMode; + if (alt) { + if (control) { + if (shift) { + newMode = panningMode; + } else { + newMode = orbitMode; + } + } else { + newMode = radialMode; + } + } + + // if leaving noMode + if (mode == noMode && newMode != noMode) { + saveCameraState(); + } + // if entering noMode + if (newMode == noMode && mode != noMode) { + restoreCameraState(); + } + + mode = newMode; +} + +function keyPressEvent(event) { + var changed = false; + + if (event.text == "ALT") { + alt = true; + changed = true; + } + if (event.text == "CONTROL") { + control = true; + changed = true; + } + if (event.text == "SHIFT") { + shift = true; + changed = true; + } + + if (changed) { + handleModes(); + } +} + +function keyReleaseEvent(event) { + var changed = false; + + if (event.text == "ALT") { + alt = false; + changed = true; + } + if (event.text == "CONTROL") { + control = false; + changed = true; + } + if (event.text == "SHIFT") { + shift = false; + changed = true; + } + + if (changed) { + handleModes(); + } +} + +function mousePressEvent(event) { + if (alt) { + isActive = true; + mouseLastX = event.x; + mouseLastY = event.y; + + // Compute trajectories related values + var pickRay = Camera.computePickRay(mouseLastX, mouseLastY); + var intersection = Voxels.findRayIntersection(pickRay); + + position = Camera.getPosition(); + + avatarTarget = MyAvatar.getTargetAvatarPosition(); + voxelTarget = intersection.intersection; + if (Vec3.length(Vec3.subtract(avatarTarget, position)) < Vec3.length(Vec3.subtract(voxelTarget, position))) { + if (avatarTarget.x != 0 || avatarTarget.y != 0 || avatarTarget.z != 0) { + center = avatarTarget; + } else { + center = voxelTarget; + } + } else { + if (voxelTarget.x != 0 || voxelTarget.y != 0 || voxelTarget.z != 0) { + center = voxelTarget; + } else { + center = avatarTarget; + } + } + + vector = Vec3.subtract(position, center); + radius = Vec3.length(vector); + azimuth = Math.atan2(vector.z, vector.x); + altitude = Math.asin(vector.y / Vec3.length(vector)); + + Camera.keepLookingAt(center); + } +} + +function mouseReleaseEvent(event) { + if (isActive) { + isActive = false; + } +} + +function mouseMoveEvent(event) { + if (isActive && mode != noMode) { + if (mode == radialMode) { + handleRadialMode(event.x - mouseLastX, event.y - mouseLastY); + } + if (mode == orbitMode) { + handleOrbitMode(event.x - mouseLastX, event.y - mouseLastY); + } + if (mode == panningMode) { + handlePanMode(event.x - mouseLastX, event.y - mouseLastY); + } + + mouseLastX = event.x; + mouseLastY = event.y; + } +} + +function scriptEnding() { + if (mode != noMode) { + restoreCameraState(); + } +} + +Controller.keyPressEvent.connect(keyPressEvent); +Controller.keyReleaseEvent.connect(keyReleaseEvent); + +Controller.mousePressEvent.connect(mousePressEvent); +Controller.mouseReleaseEvent.connect(mouseReleaseEvent); +Controller.mouseMoveEvent.connect(mouseMoveEvent); + +Script.scriptEnding.connect(scriptEnding); \ No newline at end of file From 947dde3715efe431fd8db961bedcf00bda2cf131 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 20 Mar 2014 12:44:37 -0700 Subject: [PATCH 21/42] Modified models uploader to diferrenciate heads and skeletons --- libraries/shared/src/FstReader.cpp | 23 +++++++++++++++++++---- libraries/shared/src/FstReader.h | 1 + 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/libraries/shared/src/FstReader.cpp b/libraries/shared/src/FstReader.cpp index d82ddf68a3..8a156d11b9 100644 --- a/libraries/shared/src/FstReader.cpp +++ b/libraries/shared/src/FstReader.cpp @@ -24,6 +24,7 @@ static const QString NAME_FIELD = "name"; static const QString FILENAME_FIELD = "filename"; static const QString TEXDIR_FIELD = "texdir"; static const QString LOD_FIELD = "lod"; +static const QString HEAD_SPECIFIC_FIELD = "bs"; static const QString MODEL_URL = "/api/v1/models"; @@ -32,6 +33,7 @@ static const int MAX_SIZE = 10 * 1024 * 1024; // 10 MB FstReader::FstReader() : _lodCount(-1), _texturesCount(-1), + _isHead(false), _readyToSend(false), _dataMultiPart(new QHttpMultiPart(QHttpMultiPart::FormDataType)) { @@ -81,13 +83,15 @@ bool FstReader::zip() { } // according to what is read, we modify the command - if (line.first() == NAME_FIELD) { + if (line[1] == HEAD_SPECIFIC_FIELD) { + _isHead = true; + } else if (line[1] == NAME_FIELD) { QHttpPart textPart; textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;" " name=\"model_name\""); textPart.setBody(line[1].toUtf8()); _dataMultiPart->append(textPart); - } else if (line.first() == FILENAME_FIELD) { + } else if (line[1] == FILENAME_FIELD) { QFileInfo fbx(QFileInfo(fst).path() + "/" + line[1]); if (!fbx.exists() || !fbx.isFile()) { // Check existence qDebug() << "[ERROR] FBX file " << fbx.absoluteFilePath() << " doesn't exist."; @@ -101,7 +105,7 @@ bool FstReader::zip() { if (!addPart(_zipDir.path() + "/" + line[1], "fbx")) { return false; } - } else if (line.first() == TEXDIR_FIELD) { // Check existence + } else if (line[1] == TEXDIR_FIELD) { // Check existence QFileInfo texdir(QFileInfo(fst).path() + "/" + line[1]); if (!texdir.exists() || !texdir.isDir()) { qDebug() << "[ERROR] Texture directory " << texdir.absolutePath() << " doesn't exist."; @@ -110,7 +114,7 @@ bool FstReader::zip() { if (!addTextures(texdir)) { // Recursive compress and copy return false; } - } else if (line.first() == LOD_FIELD) { + } else if (line[1] == LOD_FIELD) { QFileInfo lod(QFileInfo(fst).path() + "/" + line[1]); if (!lod.exists() || !lod.isFile()) { // Check existence qDebug() << "[ERROR] FBX file " << lod.absoluteFilePath() << " doesn't exist."; @@ -127,6 +131,17 @@ bool FstReader::zip() { } } + + QHttpPart textPart; + textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;" + " name=\"model_type\""); + if (_isHead) { + textPart.setBody("head"); + } else { + textPart.setBody("skeleton"); + } + _dataMultiPart->append(textPart); + _readyToSend = true; return true; } diff --git a/libraries/shared/src/FstReader.h b/libraries/shared/src/FstReader.h index aab42bd967..1d9da71641 100644 --- a/libraries/shared/src/FstReader.h +++ b/libraries/shared/src/FstReader.h @@ -27,6 +27,7 @@ private: int _lodCount; int _texturesCount; int _totalSize; + bool _isHead; bool _readyToSend; QHttpMultiPart* _dataMultiPart; From c658b1ddedcde8b12b6ed5bc5cf3abb2c589ec93 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 20 Mar 2014 12:45:19 -0700 Subject: [PATCH 22/42] Modified menu upload categories --- interface/src/Menu.cpp | 5 ++--- interface/src/Menu.h | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index d8a7ce5acb..f609dc8434 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -138,9 +138,8 @@ Menu::Menu() : this, SLOT(goTo())); - addDisabledActionAndSeparator(fileMenu, "Upload/Browse"); - addActionToQMenuAndActionHash(fileMenu, MenuOption::UploaderAvatarHead, 0, Application::getInstance(), SLOT(uploadFST())); - addActionToQMenuAndActionHash(fileMenu, MenuOption::UploaderAvatarSkeleton, 0, Application::getInstance(), SLOT(uploadFST())); + addDisabledActionAndSeparator(fileMenu, "Upload Avatar Model"); + addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadFST, 0, Application::getInstance(), SLOT(uploadFST())); addDisabledActionAndSeparator(fileMenu, "Settings"); addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsImport, 0, this, SLOT(importSettings())); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index f99ebe0685..cb0ca4c5c4 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -295,8 +295,7 @@ namespace MenuOption { const QString StopAllScripts = "Stop All Scripts"; const QString TestPing = "Test Ping"; const QString TransmitterDrive = "Transmitter Drive"; - const QString UploaderAvatarHead = "Upload Avatar Head"; - const QString UploaderAvatarSkeleton = "Upload Avatar Skeleton"; + const QString UploadFST = "Upload FST file"; const QString Visage = "Visage"; const QString Quit = "Quit"; const QString Voxels = "Voxels"; From e68500e11fbdc220630f97499954a7925e8dddb4 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 20 Mar 2014 12:49:51 -0700 Subject: [PATCH 23/42] Made models line edit editable --- interface/src/Menu.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f609dc8434..e62c7e1102 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -714,7 +714,6 @@ void Menu::editPreferences() { QPushButton headBrowseButton(BROWSE_BUTTON_TEXT); connect(&headBrowseButton, SIGNAL(clicked()), &headBrowser, SLOT(browse())); connect(&headBrowser, SIGNAL(selected(QString)), &headURLEdit, SLOT(setText(QString))); - headURLEdit.setReadOnly(true); headURLEdit.setMinimumWidth(QLINE_MINIMUM_WIDTH); headURLEdit.setPlaceholderText(DEFAULT_HEAD_MODEL_URL.toString()); headModelLayout.addWidget(&headURLEdit); @@ -727,7 +726,6 @@ void Menu::editPreferences() { QPushButton SkeletonBrowseButton(BROWSE_BUTTON_TEXT); connect(&SkeletonBrowseButton, SIGNAL(clicked()), &skeletonBrowser, SLOT(browse())); connect(&skeletonBrowser, SIGNAL(selected(QString)), &skeletonURLEdit, SLOT(setText(QString))); - skeletonURLEdit.setReadOnly(true); skeletonURLEdit.setMinimumWidth(QLINE_MINIMUM_WIDTH); skeletonURLEdit.setPlaceholderText(DEFAULT_BODY_MODEL_URL.toString()); skeletonModelLayout.addWidget(&skeletonURLEdit); From a8ef64e0cecd266ee46245002485d367c35dff0d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 20 Mar 2014 12:52:17 -0700 Subject: [PATCH 24/42] calculate a cutoff loudness for mixer recovery --- assignment-client/src/audio/AudioMixer.cpp | 42 ++++++++++++++++++- assignment-client/src/audio/AudioMixer.h | 5 ++- .../src/audio/AudioMixerClientData.cpp | 2 +- .../audio/src/PositionalAudioRingBuffer.cpp | 1 + 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 2bece55ac4..1f403e0f3d 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -64,7 +64,9 @@ void attachNewBufferToNode(Node *newNode) { AudioMixer::AudioMixer(const QByteArray& packet) : ThreadedAssignment(packet), _minSourceLoudnessInFrame(1.0f), - _maxSourceLoudnessInFrame(0.0f) + _maxSourceLoudnessInFrame(0.0f), + _loudnessCutoffRatio(0.0f), + _minRequiredLoudness(0.0f) { } @@ -352,6 +354,9 @@ void AudioMixer::run() { char* clientMixBuffer = new char[NETWORK_BUFFER_LENGTH_BYTES_STEREO + numBytesForPacketHeaderGivenPacketType(PacketTypeMixedAudio)]; + + int usecToSleep = 0; + bool isFirstRun = true; while (!_isFinished) { @@ -365,6 +370,39 @@ void AudioMixer::run() { _maxSourceLoudnessInFrame); } } + + if (!isFirstRun) { + const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10; + const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.30; + const float CUTOFF_EPSILON = 0.0001; + + float percentageSleep = (usecToSleep / (float) BUFFER_SEND_INTERVAL_USECS); + + float lastCutoffRatio = _loudnessCutoffRatio; + + if (percentageSleep <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD || usecToSleep < 0) { + // we're struggling - change our min required loudness to reduce some load + _loudnessCutoffRatio += (1 - _loudnessCutoffRatio) / 2; + + qDebug() << "Mixer is struggling, sleeping" << percentageSleep * 100 << "% of frame time. Old cutoff was" + << lastCutoffRatio << "and is now" << _loudnessCutoffRatio; + } else if (percentageSleep >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _loudnessCutoffRatio != 0) { + // we've recovered and can back off the required loudness + _loudnessCutoffRatio -= _loudnessCutoffRatio / 2; + + if (_loudnessCutoffRatio < CUTOFF_EPSILON) { + _loudnessCutoffRatio = 0; + } + + qDebug() << "Mixer is recovering, sleeping" << percentageSleep * 100 << "% of frame time. Old cutoff was" + << lastCutoffRatio << "and is now" << _loudnessCutoffRatio; + } + + // set out min required loudness from the new ratio + _minRequiredLoudness = _loudnessCutoffRatio * (_maxSourceLoudnessInFrame - _minSourceLoudnessInFrame); + } else { + isFirstRun = false; + } foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { if (node->getType() == NodeType::Agent && node->getActiveSocket() && node->getLinkedData() @@ -391,7 +429,7 @@ void AudioMixer::run() { break; } - int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow(); + usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow(); if (usecToSleep > 0) { usleep(usecToSleep); diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 6056dbd318..3827b2917a 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -37,10 +37,13 @@ private: void prepareMixForListeningNode(Node* node); // client samples capacity is larger than what will be sent to optimize mixing - int16_t _clientSamples[NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + SAMPLE_PHASE_DELAY_AT_90]; + // we are MMX adding 4 samples at a time so we need client samples to have an extra 4 + int16_t _clientSamples[NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + (SAMPLE_PHASE_DELAY_AT_90 * 2)]; float _minSourceLoudnessInFrame; float _maxSourceLoudnessInFrame; + float _loudnessCutoffRatio; + float _minRequiredLoudness; }; #endif /* defined(__hifi__AudioMixer__) */ diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 9b8cdb8709..6830e67aa3 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -97,7 +97,7 @@ void AudioMixerClientData::checkBuffersBeforeFrameSend(int jitterBufferLengthSam // that would be mixed in _nextOutputLoudness = _ringBuffers[i]->averageLoudnessForBoundarySamples(NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); - if (_nextOutputLoudness < currentMinLoudness) { + if (_nextOutputLoudness != 0 && _nextOutputLoudness < currentMinLoudness) { currentMinLoudness = _nextOutputLoudness; } diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 66a27647d6..ccbe5d3f23 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -50,6 +50,7 @@ int PositionalAudioRingBuffer::parseData(const QByteArray& packet) { int16_t numSilentSamples; memcpy(&numSilentSamples, packet.data() + readBytes, sizeof(int16_t)); + readBytes += sizeof(int16_t); addSilentFrame(numSilentSamples); From 64f946b6405ce158ed75b3c61e762ec10b1c6bdd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 20 Mar 2014 13:01:52 -0700 Subject: [PATCH 25/42] require that buffers be above min loudness to be mixed in --- assignment-client/src/audio/AudioMixer.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 1f403e0f3d..d8017c0a02 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -379,13 +379,15 @@ void AudioMixer::run() { float percentageSleep = (usecToSleep / (float) BUFFER_SEND_INTERVAL_USECS); float lastCutoffRatio = _loudnessCutoffRatio; + bool hasRatioChanged = false; if (percentageSleep <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD || usecToSleep < 0) { // we're struggling - change our min required loudness to reduce some load _loudnessCutoffRatio += (1 - _loudnessCutoffRatio) / 2; qDebug() << "Mixer is struggling, sleeping" << percentageSleep * 100 << "% of frame time. Old cutoff was" - << lastCutoffRatio << "and is now" << _loudnessCutoffRatio; + << lastCutoffRatio << "and is now" << _loudnessCutoffRatio; + hasRatioChanged = true; } else if (percentageSleep >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _loudnessCutoffRatio != 0) { // we've recovered and can back off the required loudness _loudnessCutoffRatio -= _loudnessCutoffRatio / 2; @@ -396,10 +398,17 @@ void AudioMixer::run() { qDebug() << "Mixer is recovering, sleeping" << percentageSleep * 100 << "% of frame time. Old cutoff was" << lastCutoffRatio << "and is now" << _loudnessCutoffRatio; + hasRatioChanged = true; } - // set out min required loudness from the new ratio - _minRequiredLoudness = _loudnessCutoffRatio * (_maxSourceLoudnessInFrame - _minSourceLoudnessInFrame); + if (hasRatioChanged) { + // set out min required loudness from the new ratio + _minRequiredLoudness = _loudnessCutoffRatio * (_maxSourceLoudnessInFrame - _minSourceLoudnessInFrame); + qDebug() << "Minimum loudness required to be mixed is now" << _minRequiredLoudness; + } + + + } else { isFirstRun = false; } From bf85f8d5e58690d357c1cb441f4a89e3000e13d9 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 20 Mar 2014 14:00:27 -0700 Subject: [PATCH 26/42] Changed model_type to model_categorie in FstReader --- libraries/shared/src/FstReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/FstReader.cpp b/libraries/shared/src/FstReader.cpp index 8a156d11b9..0031716024 100644 --- a/libraries/shared/src/FstReader.cpp +++ b/libraries/shared/src/FstReader.cpp @@ -134,7 +134,7 @@ bool FstReader::zip() { QHttpPart textPart; textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;" - " name=\"model_type\""); + " name=\"model_categorie\""); if (_isHead) { textPart.setBody("head"); } else { From 56ff423cf5a535e0e840fcefb077c24ffdf24594 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 20 Mar 2014 14:27:38 -0700 Subject: [PATCH 27/42] Let's actually use the result of our format conversion. --- interface/src/renderer/TextureCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index 2b43c89998..b3820abf25 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -313,7 +313,7 @@ void ImageReader::run() { int imageArea = image.width() * image.height(); if (opaquePixels == imageArea) { qDebug() << "Image with alpha channel is completely opaque:" << url; - image.convertToFormat(QImage::Format_RGB888); + image = image.convertToFormat(QImage::Format_RGB888); } QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image), Q_ARG(bool, translucentPixels >= imageArea / 2)); From b4f5a6d1ce7d6ac0189125a44aeb9da5ea467896 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 20 Mar 2014 14:44:47 -0700 Subject: [PATCH 28/42] add _isShuttingDown to OctreeQueryNode and bail as fast as possible when shutting down --- .../src/octree/OctreeQueryNode.cpp | 66 ++++++++++++++++++- .../src/octree/OctreeQueryNode.h | 5 ++ .../src/octree/OctreeSendThread.cpp | 25 +++++-- assignment-client/src/octree/OctreeServer.cpp | 16 ++++- 4 files changed, 101 insertions(+), 11 deletions(-) diff --git a/assignment-client/src/octree/OctreeQueryNode.cpp b/assignment-client/src/octree/OctreeQueryNode.cpp index a1922f980d..2ceb9e1040 100644 --- a/assignment-client/src/octree/OctreeQueryNode.cpp +++ b/assignment-client/src/octree/OctreeQueryNode.cpp @@ -36,18 +36,34 @@ OctreeQueryNode::OctreeQueryNode() : _lodChanged(false), _lodInitialized(false), _sequenceNumber(0), - _lastRootTimestamp(0) + _lastRootTimestamp(0), + _myPacketType(PacketTypeUnknown), + _isShuttingDown(false) { } OctreeQueryNode::~OctreeQueryNode() { + _isShuttingDown = true; + const bool extraDebugging = false; + if (extraDebugging) { + qDebug() << "OctreeQueryNode::~OctreeQueryNode()"; + } if (_octreeSendThread) { + if (extraDebugging) { + qDebug() << "OctreeQueryNode::~OctreeQueryNode()... calling _octreeSendThread->terminate()"; + } _octreeSendThread->terminate(); + if (extraDebugging) { + qDebug() << "OctreeQueryNode::~OctreeQueryNode()... calling delete _octreeSendThread"; + } delete _octreeSendThread; } delete[] _octreePacket; delete[] _lastOctreePacket; + if (extraDebugging) { + qDebug() << "OctreeQueryNode::~OctreeQueryNode()... DONE..."; + } } @@ -59,9 +75,13 @@ void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* octreeServer, con } bool OctreeQueryNode::packetIsDuplicate() const { + // if shutting down, return immediately + if (_isShuttingDown) { + return false; + } // since our packets now include header information, like sequence number, and createTime, we can't just do a memcmp // of the entire packet, we need to compare only the packet content... - int numBytesPacketHeader = numBytesForPacketHeaderGivenPacketType(getMyPacketType()); + int numBytesPacketHeader = numBytesForPacketHeaderGivenPacketType(_myPacketType); if (_lastOctreePacketLength == getPacketLength()) { if (memcmp(_lastOctreePacket + (numBytesPacketHeader + OCTREE_PACKET_EXTRA_HEADERS_SIZE), @@ -74,6 +94,11 @@ bool OctreeQueryNode::packetIsDuplicate() const { } bool OctreeQueryNode::shouldSuppressDuplicatePacket() { + // if shutting down, return immediately + if (_isShuttingDown) { + return true; + } + bool shouldSuppress = false; // assume we won't suppress // only consider duplicate packets @@ -107,7 +132,17 @@ bool OctreeQueryNode::shouldSuppressDuplicatePacket() { return shouldSuppress; } +void OctreeQueryNode::init() { + _myPacketType = getMyPacketType(); + resetOctreePacket(true); // don't bump sequence +} + void OctreeQueryNode::resetOctreePacket(bool lastWasSurpressed) { + // if shutting down, return immediately + if (_isShuttingDown) { + return; + } + // Whenever we call this, we will keep a copy of the last packet, so we can determine if the last packet has // changed since we last reset it. Since we know that no two packets can ever be identical without being the same // scene information, (e.g. the root node packet of a static scene), we can use this as a strategy for reducing @@ -128,7 +163,7 @@ void OctreeQueryNode::resetOctreePacket(bool lastWasSurpressed) { } _octreePacketAvailableBytes = MAX_PACKET_SIZE; - int numBytesPacketHeader = populatePacketHeader(reinterpret_cast(_octreePacket), getMyPacketType()); + int numBytesPacketHeader = populatePacketHeader(reinterpret_cast(_octreePacket), _myPacketType); _octreePacketAt = _octreePacket + numBytesPacketHeader; _octreePacketAvailableBytes -= numBytesPacketHeader; @@ -158,6 +193,11 @@ void OctreeQueryNode::resetOctreePacket(bool lastWasSurpressed) { } void OctreeQueryNode::writeToPacket(const unsigned char* buffer, unsigned int bytes) { + // if shutting down, return immediately + if (_isShuttingDown) { + return; + } + // compressed packets include lead bytes which contain compressed size, this allows packing of // multiple compressed portions together if (_currentPacketIsCompressed) { @@ -174,6 +214,11 @@ void OctreeQueryNode::writeToPacket(const unsigned char* buffer, unsigned int by } bool OctreeQueryNode::updateCurrentViewFrustum() { + // if shutting down, return immediately + if (_isShuttingDown) { + return false; + } + bool currentViewFrustumChanged = false; ViewFrustum newestViewFrustum; // get position and orientation details from the camera @@ -233,6 +278,11 @@ void OctreeQueryNode::setViewSent(bool viewSent) { } void OctreeQueryNode::updateLastKnownViewFrustum() { + // if shutting down, return immediately + if (_isShuttingDown) { + return; + } + bool frustumChanges = !_lastKnownViewFrustum.isVerySimilar(_currentViewFrustum); if (frustumChanges) { @@ -247,6 +297,11 @@ void OctreeQueryNode::updateLastKnownViewFrustum() { bool OctreeQueryNode::moveShouldDump() const { + // if shutting down, return immediately + if (_isShuttingDown) { + return false; + } + glm::vec3 oldPosition = _lastKnownViewFrustum.getPosition(); glm::vec3 newPosition = _currentViewFrustum.getPosition(); @@ -259,6 +314,11 @@ bool OctreeQueryNode::moveShouldDump() const { } void OctreeQueryNode::dumpOutOfView() { + // if shutting down, return immediately + if (_isShuttingDown) { + return; + } + int stillInView = 0; int outOfView = 0; OctreeElementBag tempBag; diff --git a/assignment-client/src/octree/OctreeQueryNode.h b/assignment-client/src/octree/OctreeQueryNode.h index 20fa2c5858..eab8cb5d0a 100644 --- a/assignment-client/src/octree/OctreeQueryNode.h +++ b/assignment-client/src/octree/OctreeQueryNode.h @@ -28,6 +28,7 @@ public: OctreeQueryNode(); virtual ~OctreeQueryNode(); + void init(); // called after creation to set up some virtual items virtual PacketType getMyPacketType() const = 0; void resetOctreePacket(bool lastWasSurpressed = false); // resets octree packet to after "V" header @@ -91,6 +92,7 @@ public: unsigned int getlastOctreePacketLength() const { return _lastOctreePacketLength; } int getDuplicatePacketCount() const { return _duplicatePacketCount; } + bool isShuttingDown() const { return _isShuttingDown; } private: OctreeQueryNode(const OctreeQueryNode &); @@ -127,6 +129,9 @@ private: OCTREE_PACKET_SEQUENCE _sequenceNumber; quint64 _lastRootTimestamp; + + PacketType _myPacketType; + bool _isShuttingDown; }; #endif /* defined(__hifi__OctreeQueryNode__) */ diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index caa729e340..9c04c4a1ad 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -65,7 +65,7 @@ bool OctreeSendThread::process() { nodeData = (OctreeQueryNode*) node->getLinkedData(); // Sometimes the node data has not yet been linked, in which case we can't really do anything - if (nodeData) { + if (nodeData && !nodeData->isShuttingDown()) { bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); packetDistributor(node, nodeData, viewFrustumChanged); } @@ -99,7 +99,14 @@ quint64 OctreeSendThread::_totalBytes = 0; quint64 OctreeSendThread::_totalWastedBytes = 0; quint64 OctreeSendThread::_totalPackets = 0; -int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) { +int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, + OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) { + + // if we're shutting down, then exit early + if (nodeData->isShuttingDown()) { + return 0; + } + bool debug = _myServer->wantsDebugSending(); quint64 now = usecTimestampNow(); @@ -136,7 +143,7 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, OctreeQuer // If we've got a stats message ready to send, then see if we can piggyback them together - if (nodeData->stats.isReadyToSend()) { + if (nodeData->stats.isReadyToSend() && !nodeData->isShuttingDown()) { // Send the stats message to the client unsigned char* statsMessage = nodeData->stats.getStatsMessage(); int statsMessageLength = nodeData->stats.getStatsMessageLength(); @@ -203,7 +210,7 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, OctreeQuer nodeData->stats.markAsSent(); } else { // If there's actually a packet waiting, then send it. - if (nodeData->isPacketWaiting()) { + if (nodeData->isPacketWaiting() && !nodeData->isShuttingDown()) { // just send the voxel packet NodeList::getInstance()->writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(), SharedNodePointer(node)); @@ -234,6 +241,12 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, OctreeQuer /// Version of voxel distributor that sends the deepest LOD level at once int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQueryNode* nodeData, bool viewFrustumChanged) { + + // if shutting down, exit early + if (nodeData->isShuttingDown()) { + return 0; + } + int truePacketsSent = 0; int trueBytesSent = 0; int packetsSentThisInterval = 0; @@ -336,7 +349,7 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue int extraPackingAttempts = 0; bool completedScene = false; - while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval) { + while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval && !nodeData->isShuttingDown()) { float lockWaitElapsedUsec = OctreeServer::SKIP_TIME; float encodeElapsedUsec = OctreeServer::SKIP_TIME; float compressAndWriteElapsedUsec = OctreeServer::SKIP_TIME; @@ -503,7 +516,7 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue // Here's where we can/should allow the server to send other data... // send the environment packet // TODO: should we turn this into a while loop to better handle sending multiple special packets - if (_myServer->hasSpecialPacketToSend(node)) { + if (_myServer->hasSpecialPacketToSend(node) && !nodeData->isShuttingDown()) { trueBytesSent += _myServer->sendSpecialPacket(node); truePacketsSent++; packetsSentThisInterval++; diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 7e6ffe52da..496f9af1a0 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -168,7 +168,7 @@ void OctreeServer::trackPacketSendingTime(float time) { void OctreeServer::attachQueryNodeToNode(Node* newNode) { if (!newNode->getLinkedData()) { OctreeQueryNode* newQueryNodeData = _instance->createOctreeQueryNode(); - newQueryNodeData->resetOctreePacket(true); // don't bump sequence + newQueryNodeData->init(); newNode->setLinkedData(newQueryNodeData); } } @@ -784,16 +784,26 @@ void OctreeServer::readPendingDatagrams() { if (packetType == getMyQueryMessageType()) { bool debug = false; if (debug) { - qDebug() << "Got PacketTypeVoxelQuery at" << usecTimestampNow(); + if (matchingNode) { + qDebug() << "Got PacketTypeVoxelQuery at" << usecTimestampNow() << "node:" << *matchingNode; + } else { + qDebug() << "Got PacketTypeVoxelQuery at" << usecTimestampNow() << "node: ??????"; + } } // If we got a PacketType_VOXEL_QUERY, then we're talking to an NodeType_t_AVATAR, and we // need to make sure we have it in our nodeList. if (matchingNode) { + if (debug) { + qDebug() << "calling updateNodeWithDataFromPacket()... node:" << *matchingNode; + } nodeList->updateNodeWithDataFromPacket(matchingNode, receivedPacket); OctreeQueryNode* nodeData = (OctreeQueryNode*) matchingNode->getLinkedData(); if (nodeData && !nodeData->isOctreeSendThreadInitalized()) { + if (debug) { + qDebug() << "calling initializeOctreeSendThread()... node:" << *matchingNode; + } nodeData->initializeOctreeSendThread(this, matchingNode->getUUID()); } } @@ -999,6 +1009,8 @@ void OctreeServer::nodeKilled(SharedNodePointer node) { node->setLinkedData(NULL); // set this first in case another thread comes through and tryes to acces this qDebug() << qPrintable(_safeServerName) << "server deleting Linked Data for node:" << *node; nodeData->deleteLater(); + } else { + qDebug() << qPrintable(_safeServerName) << "server node missing linked data node:" << *node; } } From 65603b063185fdd9437420820ba1e4f4741055c6 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 20 Mar 2014 15:30:28 -0700 Subject: [PATCH 29/42] Fixed typo --- libraries/shared/src/FstReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/FstReader.cpp b/libraries/shared/src/FstReader.cpp index 0031716024..a803583598 100644 --- a/libraries/shared/src/FstReader.cpp +++ b/libraries/shared/src/FstReader.cpp @@ -134,7 +134,7 @@ bool FstReader::zip() { QHttpPart textPart; textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;" - " name=\"model_categorie\""); + " name=\"model_category\""); if (_isHead) { textPart.setBody("head"); } else { From a30c4268da49b27a69ddeda135d318cd36ad6bd4 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 20 Mar 2014 15:47:42 -0700 Subject: [PATCH 30/42] wave work for bot --- examples/bot.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/examples/bot.js b/examples/bot.js index 808830b30d..ea78f40de9 100644 --- a/examples/bot.js +++ b/examples/bot.js @@ -44,6 +44,7 @@ var X_MAX = 5.0; var Z_MIN = 0.0; var Z_MAX = 5.0; var Y_PELVIS = 2.5; +var SHOULDER_JOINT_NUMBER = 15; var MOVE_RANGE_SMALL = 0.5; var MOVE_RANGE_BIG = Math.max(X_MAX - X_MIN, Z_MAX - Z_MIN) / 2.0; @@ -107,17 +108,13 @@ Avatar.billboardURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/me Agent.isAvatar = true; Agent.isListeningToAudioStream = true; -print("Joint List:"); -var jointList = Avatar.getJointNames(); -print(jointList); - // change the avatar's position to the random one Avatar.position = firstPosition; printVector("New bot, position = ", Avatar.position); function stopWaving() { isWaving = false; - Avatar.clearJointData("joint_L_shoulder"); + Avatar.clearJointData(SHOULDER_JOINT_NUMBER); } function updateBehavior(deltaTime) { @@ -141,7 +138,7 @@ function updateBehavior(deltaTime) { waveAmplitude = 5.0 + Math.random() * 60.0; Script.setTimeout(stopWaving, 1000 + Math.random() * 2000); } else if (isWaving) { - Avatar.setJointData(15, Quat.fromPitchYawRollDegrees(0.0, 0.0, waveAmplitude * Math.sin(cumulativeTime * waveFrequency))); + Avatar.setJointData(SHOULDER_JOINT_NUMBER, Quat.fromPitchYawRollDegrees(0.0, 0.0, waveAmplitude * Math.sin(cumulativeTime * waveFrequency))); } if (Math.random() < CHANCE_OF_SOUND) { From 6b118939fd467a7bae200a9f457fabe1edbfc8f1 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 20 Mar 2014 16:09:25 -0700 Subject: [PATCH 31/42] Removed orbit from editVoxels.js so there is no conflict with inspect.js --- examples/editVoxels.js | 250 +---------------------------------------- examples/inspect.js | 2 +- 2 files changed, 3 insertions(+), 249 deletions(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 31d483d798..43bc485274 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -20,8 +20,6 @@ var windowDimensions = Controller.getViewportDimensions(); var NEW_VOXEL_SIZE = 1.0; var NEW_VOXEL_DISTANCE_FROM_CAMERA = 3.0; -var ORBIT_RATE_ALTITUDE = 200.0; -var ORBIT_RATE_AZIMUTH = 90.0; var PIXELS_PER_EXTRUDE_VOXEL = 16; var WHEEL_PIXELS_PER_SCALE_CHANGE = 100; var MAX_VOXEL_SCALE = 1.0; @@ -37,19 +35,7 @@ var previewLineWidth = 1.5; var oldMode = Camera.getMode(); var isAdding = false; -var isExtruding = false; -var isOrbiting = false; -var isOrbitingFromTouch = false; -var isPanning = false; -var isPanningFromTouch = false; -var touchPointsToOrbit = 2; // you can change these, but be mindful that on some track pads 2 touch points = right click+drag -var touchPointsToPan = 3; -var orbitAzimuth = 0.0; -var orbitAltitude = 0.0; -var orbitCenter = { x: 0, y: 0, z: 0 }; -var orbitPosition = { x: 0, y: 0, z: 0 }; -var torsoToEyeVector = { x: 0, y: 0, z: 0 }; -var orbitRadius = 0.0; +var isExtruding = false; var extrudeDirection = { x: 0, y: 0, z: 0 }; var extrudeScale = 0.0; var lastVoxelPosition = { x: 0, y: 0, z: 0 }; @@ -444,24 +430,11 @@ function getNewVoxelPosition() { return newPosition; } -function fixEulerAngles(eulers) { - var rVal = { x: 0, y: 0, z: eulers.z }; - if (eulers.x >= 90.0) { - rVal.x = 180.0 - eulers.x; - rVal.y = eulers.y - 180.0; - } else if (eulers.x <= -90.0) { - rVal.x = 180.0 - eulers.x; - rVal.y = eulers.y - 180.0; - } - return rVal; -} - var trackLastMouseX = 0; var trackLastMouseY = 0; var trackAsDelete = false; var trackAsRecolor = false; var trackAsEyedropper = false; -var trackAsOrbitOrPan = false; var voxelToolSelected = true; var recolorToolSelected = false; @@ -761,10 +734,6 @@ function trackKeyPressEvent(event) { trackAsEyedropper = true; moveTools(); } - if (event.text == "ALT") { - trackAsOrbitOrPan = true; - moveTools(); - } showPreviewGuides(); } @@ -802,10 +771,6 @@ function trackKeyReleaseEvent(event) { trackAsEyedropper = false; moveTools(); } - if (event.text == "ALT") { - trackAsOrbitOrPan = false; - moveTools(); - } // on F1 toggle the preview mode between cubes and lines if (event.text == "F1") { @@ -816,74 +781,6 @@ function trackKeyReleaseEvent(event) { } } -function startOrbitMode(event) { - mouseX = event.x; - mouseY = event.y; - var pickRay = Camera.computePickRay(event.x, event.y); - var intersection = Voxels.findRayIntersection(pickRay); - - // start orbit camera! - var cameraPosition = Camera.getPosition(); - torsoToEyeVector = Vec3.subtract(cameraPosition, MyAvatar.position); - torsoToEyeVector.x = 0.0; - torsoToEyeVector.z = 0.0; - oldMode = Camera.getMode(); - Camera.setMode("independent"); - Camera.keepLookingAt(intersection.intersection); - // get position for initial azimuth, elevation - orbitCenter = intersection.intersection; - var orbitVector = Vec3.subtract(cameraPosition, orbitCenter); - orbitRadius = Vec3.length(orbitVector); - orbitAzimuth = Math.atan2(orbitVector.z, orbitVector.x); - orbitAltitude = Math.asin(orbitVector.y / Vec3.length(orbitVector)); - - //print("startOrbitMode..."); -} - -function handleOrbitingMove(event) { - var cameraOrientation = Camera.getOrientation(); - var origEulers = Quat.safeEulerAngles(cameraOrientation); - var newEulers = fixEulerAngles(Quat.safeEulerAngles(cameraOrientation)); - var dx = event.x - mouseX; - var dy = event.y - mouseY; - orbitAzimuth += dx / ORBIT_RATE_AZIMUTH; - orbitAltitude += dy / ORBIT_RATE_ALTITUDE; - var orbitVector = { x:(Math.cos(orbitAltitude) * Math.cos(orbitAzimuth)) * orbitRadius, - y:Math.sin(orbitAltitude) * orbitRadius, - z:(Math.cos(orbitAltitude) * Math.sin(orbitAzimuth)) * orbitRadius }; - orbitPosition = Vec3.sum(orbitCenter, orbitVector); - Camera.setPosition(orbitPosition); - - mouseX = event.x; - mouseY = event.y; - //print("handleOrbitingMove..."); -} - -function endOrbitMode(event) { - var cameraOrientation = Camera.getOrientation(); - MyAvatar.position = Vec3.subtract(Camera.getPosition(), torsoToEyeVector); - MyAvatar.headOrientation = cameraOrientation; - Camera.stopLooking(); - Camera.setMode(oldMode); - Camera.setOrientation(cameraOrientation); - //print("endOrbitMode..."); -} - -function startPanMode(event, intersection) { - // start pan camera! - print("handle PAN mode!!!"); -} - -function handlePanMove(event) { - print("PANNING mode!!! "); - //print("isPanning="+isPanning + " inPanningFromTouch="+isPanningFromTouch + " trackAsOrbitOrPan="+trackAsOrbitOrPan); -} - -function endPanMode(event) { - print("ending PAN mode!!!"); -} - - function mousePressEvent(event) { // if our tools are off, then don't do anything @@ -891,31 +788,6 @@ function mousePressEvent(event) { return; } - // Normally, if we're panning or orbiting from touch, ignore these... because our touch takes precedence. - // but In the case of a button="RIGHT" click, we may get some touch messages first, and we actually want to - // cancel any touch mode, and then let the right-click through - if (isOrbitingFromTouch || isPanningFromTouch) { - - // if the user is holding the ALT key AND they are clicking the RIGHT button (or on multi-touch doing a two - // finger touch, then we want to let the new panning behavior take over. - // if it's any other case we still want to bail - if (event.button == "RIGHT" && trackAsOrbitOrPan) { - // cancel our current multitouch operation... - if (isOrbitingFromTouch) { - endOrbitMode(event); - isOrbitingFromTouch = false; - } - if (isPanningFromTouch) { - //print("mousePressEvent... calling endPanMode()"); - endPanMode(event); - isPanningFromTouch = false; - } - // let things fall through - } else { - return; - } - } - // no clicking on overlays while in panning mode if (!trackAsOrbitOrPan) { var clickedOnSomething = false; @@ -1008,17 +880,7 @@ function mousePressEvent(event) { calcThumbFromScale(intersection.voxel.s); } - // Note: touch and mouse events can cross paths, so we want to ignore any mouse events that would - // start a pan or orbit if we're already doing a pan or orbit via touch... - if ((event.isAlt || trackAsOrbitOrPan) && !(isOrbitingFromTouch || isPanningFromTouch)) { - if (event.isLeftButton && !event.isRightButton) { - startOrbitMode(event); - isOrbiting = true; - } else if (event.isRightButton && !event.isLeftButton) { - startPanMode(event); - isPanning = true; - } - } else if (trackAsDelete || event.isRightButton && !trackAsEyedropper) { + if (trackAsDelete || event.isRightButton && !trackAsEyedropper) { // Delete voxel voxelDetails = calculateVoxelFromIntersection(intersection,"delete"); Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s); @@ -1209,23 +1071,7 @@ function mouseMoveEvent(event) { return; } - // if we're panning or orbiting from touch, ignore these... because our touch takes precedence. - if (isOrbitingFromTouch || isPanningFromTouch) { - return; - } - // double check that we didn't accidentally miss a pan or orbit click request - if (trackAsOrbitOrPan && !isPanning && !isOrbiting) { - if (event.isLeftButton && !event.isRightButton) { - startOrbitMode(event); - isOrbiting = true; - } - if (!event.isLeftButton && event.isRightButton) { - startPanMode(event); - isPanning = true; - } - } - if (!trackAsOrbitOrPan && isMovingSlider) { thumbX = (event.x - thumbClickOffsetX) - sliderX; if (thumbX < minThumbX) { @@ -1236,10 +1082,6 @@ function mouseMoveEvent(event) { } calcScaleFromThumb(thumbX); - } else if (isOrbiting) { - handleOrbitingMove(event); - } else if (isPanning) { - handlePanMove(event); } else if (!trackAsOrbitOrPan && isAdding) { // Watch the drag direction to tell which way to 'extrude' this voxel if (!isExtruding) { @@ -1293,16 +1135,6 @@ function mouseReleaseEvent(event) { if (isMovingSlider) { isMovingSlider = false; } - - if (isOrbiting) { - endOrbitMode(event); - isOrbiting = false; - } - if (isPanning) { - print("mouseReleaseEvent... calling endPanMode()"); - endPanMode(event); - isPanning = false; - } isAdding = false; isExtruding = false; } @@ -1396,96 +1228,18 @@ function touchBeginEvent(event) { if (!editToolsOn) { return; } - - // if we're already in the middle of orbiting or panning, then ignore these multi-touch events... - if (isOrbiting || isPanning) { - return; - } - - if (event.isAlt || trackAsOrbitOrPan) { - if (event.touchPoints == touchPointsToOrbit) { - // we need to double check that we didn't start an orbit, because the touch events will sometimes - // come in as 2 then 3 touches... - if (isPanningFromTouch) { - print("touchBeginEvent... calling endPanMode()"); - endPanMode(event); - isPanningFromTouch = false; - } - startOrbitMode(event); - isOrbitingFromTouch = true; - } else if (event.touchPoints == touchPointsToPan) { - // we need to double check that we didn't start an orbit, because the touch events will sometimes - // come in as 2 then 3 touches... - if (isOrbitingFromTouch) { - endOrbitMode(event); - isOrbitingFromTouch = false; - } - startPanMode(event); - isPanningFromTouch = true; - } - } } function touchUpdateEvent(event) { if (!editToolsOn) { return; } - - // if we're already in the middle of orbiting or panning, then ignore these multi-touch events... - if (isOrbiting || isPanning) { - return; - } - - if (isOrbitingFromTouch) { - // we need to double check that we didn't start an orbit, because the touch events will sometimes - // come in as 2 then 3 touches... - if (event.touchPoints == touchPointsToPan) { - //print("we now have touchPointsToPan touches... switch to pan..."); - endOrbitMode(event); - isOrbitingFromTouch = false; - startPanMode(event); - isPanningFromTouch = true; - } else { - handleOrbitingMove(event); - } - } - if (isPanningFromTouch) { - //print("touchUpdateEvent... isPanningFromTouch... event.touchPoints=" + event.touchPoints); - // we need to double check that we didn't start an orbit, because the touch events will sometimes - // come in as 2 then 3 touches... - if (event.touchPoints == touchPointsToOrbit) { - //print("we now have touchPointsToOrbit touches... switch to orbit..."); - //print("touchUpdateEvent... calling endPanMode()"); - endPanMode(event); - isPanningFromTouch = false; - startOrbitMode(event); - isOrbitingFromTouch = true; - handleOrbitingMove(event); - } else { - handlePanMove(event); - } - } } function touchEndEvent(event) { if (!editToolsOn) { return; } - - // if we're already in the middle of orbiting or panning, then ignore these multi-touch events... - if (isOrbiting || isPanning) { - return; - } - - if (isOrbitingFromTouch) { - endOrbitMode(event); - isOrbitingFromTouch = false; - } - if (isPanningFromTouch) { - print("touchEndEvent... calling endPanMode()"); - endPanMode(event); - isPanningFromTouch = false; - } } var lastFingerAddVoxel = { x: -1, y: -1, z: -1}; // off of the build-able area diff --git a/examples/inspect.js b/examples/inspect.js index bbddb77883..9292450784 100644 --- a/examples/inspect.js +++ b/examples/inspect.js @@ -1,4 +1,4 @@ -/ +// // inspect.js // hifi // From 7f96c71793dd6d5467813cd7a4559033929710c9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 20 Mar 2014 16:17:09 -0700 Subject: [PATCH 32/42] actually require minRequiredLoudness for a stream to be mixed in --- assignment-client/src/audio/AudioMixer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index d8017c0a02..77c778813c 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -305,7 +305,7 @@ void AudioMixer::prepareMixForListeningNode(Node* node) { if ((*otherNode != *node || otherNodeBuffer->shouldLoopbackForNode()) && otherNodeBuffer->willBeAddedToMix() - && otherNodeClientData->getNextOutputLoudness() > 0) { + && otherNodeClientData->getNextOutputLoudness() > _minRequiredLoudness) { addBufferToMixForListeningNodeWithBuffer(otherNodeBuffer, nodeRingBuffer); } } From 551514179282151625e3b653499f31bfa05c01fa Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 20 Mar 2014 16:50:20 -0700 Subject: [PATCH 33/42] use a trailing average for _nextOutputLoudness --- assignment-client/src/audio/AudioMixerClientData.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 6830e67aa3..1a7492e4e8 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -93,9 +93,13 @@ void AudioMixerClientData::checkBuffersBeforeFrameSend(int jitterBufferLengthSam // set its flag so we know to push its buffer when all is said and done _ringBuffers[i]->setWillBeAddedToMix(true); + const int TRAILING_AVERAGE_FRAMES = 100; + const float CURRENT_FRAME_RATIO = 1 / TRAILING_AVERAGE_FRAMES; + const float PREVIOUS_FRAMES_RATIO = 1 - CURRENT_FRAME_RATIO; + // calculate the average loudness for the next NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL // that would be mixed in - _nextOutputLoudness = _ringBuffers[i]->averageLoudnessForBoundarySamples(NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); + _nextOutputLoudness = (_nextOutputLoudness * PREVIOUS_FRAMES_RATIO) + (CURRENT_FRAME_RATIO * _ringBuffers[i]->averageLoudnessForBoundarySamples(NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL)); if (_nextOutputLoudness != 0 && _nextOutputLoudness < currentMinLoudness) { currentMinLoudness = _nextOutputLoudness; From 5ae63c5b0fdcab8b08bfb24598753283b4c98400 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 20 Mar 2014 17:10:24 -0700 Subject: [PATCH 34/42] use a trailing average for sleep time, move loudness to RB --- assignment-client/src/audio/AudioMixer.cpp | 86 ++++++++++--------- assignment-client/src/audio/AudioMixer.h | 1 + .../src/audio/AudioMixerClientData.cpp | 18 ++-- .../src/audio/AudioMixerClientData.h | 3 - libraries/audio/src/AudioRingBuffer.cpp | 19 ++-- libraries/audio/src/AudioRingBuffer.h | 5 +- 6 files changed, 69 insertions(+), 63 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 77c778813c..e86ecda36e 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -63,6 +63,7 @@ void attachNewBufferToNode(Node *newNode) { AudioMixer::AudioMixer(const QByteArray& packet) : ThreadedAssignment(packet), + _trailingSleepRatio(1.0f), _minSourceLoudnessInFrame(1.0f), _maxSourceLoudnessInFrame(0.0f), _loudnessCutoffRatio(0.0f), @@ -305,7 +306,7 @@ void AudioMixer::prepareMixForListeningNode(Node* node) { if ((*otherNode != *node || otherNodeBuffer->shouldLoopbackForNode()) && otherNodeBuffer->willBeAddedToMix() - && otherNodeClientData->getNextOutputLoudness() > _minRequiredLoudness) { + && otherNodeBuffer->getAverageLoudness() > _minRequiredLoudness) { addBufferToMixForListeningNodeWithBuffer(otherNodeBuffer, nodeRingBuffer); } } @@ -355,8 +356,7 @@ void AudioMixer::run() { char* clientMixBuffer = new char[NETWORK_BUFFER_LENGTH_BYTES_STEREO + numBytesForPacketHeaderGivenPacketType(PacketTypeMixedAudio)]; - int usecToSleep = 0; - bool isFirstRun = true; + int usecToSleep = BUFFER_SEND_INTERVAL_USECS; while (!_isFinished) { @@ -371,46 +371,48 @@ void AudioMixer::run() { } } - if (!isFirstRun) { - const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10; - const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.30; - const float CUTOFF_EPSILON = 0.0001; - - float percentageSleep = (usecToSleep / (float) BUFFER_SEND_INTERVAL_USECS); - - float lastCutoffRatio = _loudnessCutoffRatio; - bool hasRatioChanged = false; - - if (percentageSleep <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD || usecToSleep < 0) { - // we're struggling - change our min required loudness to reduce some load - _loudnessCutoffRatio += (1 - _loudnessCutoffRatio) / 2; - - qDebug() << "Mixer is struggling, sleeping" << percentageSleep * 100 << "% of frame time. Old cutoff was" - << lastCutoffRatio << "and is now" << _loudnessCutoffRatio; - hasRatioChanged = true; - } else if (percentageSleep >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _loudnessCutoffRatio != 0) { - // we've recovered and can back off the required loudness - _loudnessCutoffRatio -= _loudnessCutoffRatio / 2; - - if (_loudnessCutoffRatio < CUTOFF_EPSILON) { - _loudnessCutoffRatio = 0; - } - - qDebug() << "Mixer is recovering, sleeping" << percentageSleep * 100 << "% of frame time. Old cutoff was" - << lastCutoffRatio << "and is now" << _loudnessCutoffRatio; - hasRatioChanged = true; - } - - if (hasRatioChanged) { - // set out min required loudness from the new ratio - _minRequiredLoudness = _loudnessCutoffRatio * (_maxSourceLoudnessInFrame - _minSourceLoudnessInFrame); - qDebug() << "Minimum loudness required to be mixed is now" << _minRequiredLoudness; - } - - + const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10; + const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.30; + const float CUTOFF_EPSILON = 0.0001; - } else { - isFirstRun = false; + const int TRAILING_AVERAGE_FRAMES = 100; + const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES; + const float PREVIOUS_FRAMES_RATIO = 1 - CURRENT_FRAME_RATIO; + + if (usecToSleep < 0) { + usecToSleep = 0; + } + + _trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio) + + (usecToSleep * CURRENT_FRAME_RATIO / (float) BUFFER_SEND_INTERVAL_USECS); + + float lastCutoffRatio = _loudnessCutoffRatio; + bool hasRatioChanged = false; + + if (_trailingSleepRatio <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD) { + // we're struggling - change our min required loudness to reduce some load + _loudnessCutoffRatio += (1 - _loudnessCutoffRatio) / 2; + + qDebug() << "Mixer is struggling, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was" + << lastCutoffRatio << "and is now" << _loudnessCutoffRatio; + hasRatioChanged = true; + } else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _loudnessCutoffRatio != 0) { + // we've recovered and can back off the required loudness + _loudnessCutoffRatio -= _loudnessCutoffRatio / 2; + + if (_loudnessCutoffRatio < CUTOFF_EPSILON) { + _loudnessCutoffRatio = 0; + } + + qDebug() << "Mixer is recovering, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was" + << lastCutoffRatio << "and is now" << _loudnessCutoffRatio; + hasRatioChanged = true; + } + + if (hasRatioChanged) { + // set out min required loudness from the new ratio + _minRequiredLoudness = _loudnessCutoffRatio * (_maxSourceLoudnessInFrame - _minSourceLoudnessInFrame); + qDebug() << "Minimum loudness required to be mixed is now" << _minRequiredLoudness; } foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 3827b2917a..4ba8cdebd3 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -40,6 +40,7 @@ private: // we are MMX adding 4 samples at a time so we need client samples to have an extra 4 int16_t _clientSamples[NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + (SAMPLE_PHASE_DELAY_AT_90 * 2)]; + float _trailingSleepRatio; float _minSourceLoudnessInFrame; float _maxSourceLoudnessInFrame; float _loudnessCutoffRatio; diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 1a7492e4e8..41fd72e54e 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -16,8 +16,7 @@ #include "AudioMixerClientData.h" AudioMixerClientData::AudioMixerClientData() : - _ringBuffers(), - _nextOutputLoudness(0) + _ringBuffers() { } @@ -93,20 +92,19 @@ void AudioMixerClientData::checkBuffersBeforeFrameSend(int jitterBufferLengthSam // set its flag so we know to push its buffer when all is said and done _ringBuffers[i]->setWillBeAddedToMix(true); - const int TRAILING_AVERAGE_FRAMES = 100; - const float CURRENT_FRAME_RATIO = 1 / TRAILING_AVERAGE_FRAMES; - const float PREVIOUS_FRAMES_RATIO = 1 - CURRENT_FRAME_RATIO; // calculate the average loudness for the next NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL // that would be mixed in - _nextOutputLoudness = (_nextOutputLoudness * PREVIOUS_FRAMES_RATIO) + (CURRENT_FRAME_RATIO * _ringBuffers[i]->averageLoudnessForBoundarySamples(NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL)); + _ringBuffers[i]->updateAverageLoudnessForBoundarySamples(NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); - if (_nextOutputLoudness != 0 && _nextOutputLoudness < currentMinLoudness) { - currentMinLoudness = _nextOutputLoudness; + float ringBufferLoudness = _ringBuffers[i]->getAverageLoudness(); + + if (ringBufferLoudness != 0 && ringBufferLoudness < currentMinLoudness) { + currentMinLoudness = ringBufferLoudness; } - if (_nextOutputLoudness > currentMaxLoudness) { - currentMaxLoudness = _nextOutputLoudness; + if (ringBufferLoudness > currentMaxLoudness) { + currentMaxLoudness = ringBufferLoudness; } } } diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index 701b2befba..d41563bbca 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -24,14 +24,11 @@ public: const std::vector getRingBuffers() const { return _ringBuffers; } AvatarAudioRingBuffer* getAvatarAudioRingBuffer() const; - float getNextOutputLoudness() const { return _nextOutputLoudness; } - int parseData(const QByteArray& packet); void checkBuffersBeforeFrameSend(int jitterBufferLengthSamples, float& currentMinLoudness, float& currentMaxLoudness); void pushBuffersAfterFrameSend(); private: std::vector _ringBuffers; - float _nextOutputLoudness; }; #endif /* defined(__hifi__AudioMixerClientData__) */ diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp index 0e92ab4eb5..cf35b6a20f 100644 --- a/libraries/audio/src/AudioRingBuffer.cpp +++ b/libraries/audio/src/AudioRingBuffer.cpp @@ -19,7 +19,8 @@ AudioRingBuffer::AudioRingBuffer(int numFrameSamples) : NodeData(), _sampleCapacity(numFrameSamples * RING_BUFFER_LENGTH_FRAMES), _isStarved(true), - _hasStarted(false) + _hasStarted(false), + _averageLoudness(0) { if (numFrameSamples) { _buffer = new int16_t[_sampleCapacity]; @@ -55,18 +56,22 @@ int AudioRingBuffer::parseData(const QByteArray& packet) { return writeData(packet.data() + numBytesPacketHeader, packet.size() - numBytesPacketHeader); } -float AudioRingBuffer::averageLoudnessForBoundarySamples(int numSamples) { +void AudioRingBuffer::updateAverageLoudnessForBoundarySamples(int numSamples) { // ForBoundarySamples means that we expect the number of samples not to roll of the end of the ring buffer - float averageLoudness = 0; + float nextLoudness = 0; for (int i = 0; i < numSamples; ++i) { - averageLoudness += fabsf(_nextOutput[i]); + nextLoudness += fabsf(_nextOutput[i]); } - averageLoudness /= numSamples; - averageLoudness /= MAX_SAMPLE_VALUE; + nextLoudness /= numSamples; + nextLoudness /= MAX_SAMPLE_VALUE; + + const int TRAILING_AVERAGE_FRAMES = 100; + const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES; + const float PREVIOUS_FRAMES_RATIO = 1 - CURRENT_FRAME_RATIO; - return averageLoudness; + _averageLoudness = (_averageLoudness * PREVIOUS_FRAMES_RATIO) + (CURRENT_FRAME_RATIO * nextLoudness); } qint64 AudioRingBuffer::readSamples(int16_t* destination, qint64 maxSamples) { diff --git a/libraries/audio/src/AudioRingBuffer.h b/libraries/audio/src/AudioRingBuffer.h index 0beb45ca18..efc85ac94b 100644 --- a/libraries/audio/src/AudioRingBuffer.h +++ b/libraries/audio/src/AudioRingBuffer.h @@ -50,7 +50,8 @@ public: const int16_t* getNextOutput() { return _nextOutput; } const int16_t* getBuffer() { return _buffer; } - float averageLoudnessForBoundarySamples(int numSamples); + void updateAverageLoudnessForBoundarySamples(int numSamples); + float getAverageLoudness() const { return _averageLoudness; } qint64 readSamples(int16_t* destination, qint64 maxSamples); qint64 writeSamples(const int16_t* source, qint64 maxSamples); @@ -85,6 +86,8 @@ protected: int16_t* _buffer; bool _isStarved; bool _hasStarted; + + float _averageLoudness; }; #endif /* defined(__interface__AudioRingBuffer__) */ From 24e9446814d8936ca420c8198d8d16d57290679e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 21 Mar 2014 09:52:07 -0700 Subject: [PATCH 35/42] remove mixer cutoff strategy for testing --- assignment-client/src/audio/AudioMixer.cpp | 44 ------------------- .../src/audio/AudioMixerClientData.cpp | 11 +---- 2 files changed, 1 insertion(+), 54 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index e86ecda36e..f5bed48a86 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -370,50 +370,6 @@ void AudioMixer::run() { _maxSourceLoudnessInFrame); } } - - const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10; - const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.30; - const float CUTOFF_EPSILON = 0.0001; - - const int TRAILING_AVERAGE_FRAMES = 100; - const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES; - const float PREVIOUS_FRAMES_RATIO = 1 - CURRENT_FRAME_RATIO; - - if (usecToSleep < 0) { - usecToSleep = 0; - } - - _trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio) - + (usecToSleep * CURRENT_FRAME_RATIO / (float) BUFFER_SEND_INTERVAL_USECS); - - float lastCutoffRatio = _loudnessCutoffRatio; - bool hasRatioChanged = false; - - if (_trailingSleepRatio <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD) { - // we're struggling - change our min required loudness to reduce some load - _loudnessCutoffRatio += (1 - _loudnessCutoffRatio) / 2; - - qDebug() << "Mixer is struggling, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was" - << lastCutoffRatio << "and is now" << _loudnessCutoffRatio; - hasRatioChanged = true; - } else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _loudnessCutoffRatio != 0) { - // we've recovered and can back off the required loudness - _loudnessCutoffRatio -= _loudnessCutoffRatio / 2; - - if (_loudnessCutoffRatio < CUTOFF_EPSILON) { - _loudnessCutoffRatio = 0; - } - - qDebug() << "Mixer is recovering, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was" - << lastCutoffRatio << "and is now" << _loudnessCutoffRatio; - hasRatioChanged = true; - } - - if (hasRatioChanged) { - // set out min required loudness from the new ratio - _minRequiredLoudness = _loudnessCutoffRatio * (_maxSourceLoudnessInFrame - _minSourceLoudnessInFrame); - qDebug() << "Minimum loudness required to be mixed is now" << _minRequiredLoudness; - } foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { if (node->getType() == NodeType::Agent && node->getActiveSocket() && node->getLinkedData() diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 41fd72e54e..f370a1509f 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -96,16 +96,7 @@ void AudioMixerClientData::checkBuffersBeforeFrameSend(int jitterBufferLengthSam // calculate the average loudness for the next NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL // that would be mixed in _ringBuffers[i]->updateAverageLoudnessForBoundarySamples(NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); - - float ringBufferLoudness = _ringBuffers[i]->getAverageLoudness(); - - if (ringBufferLoudness != 0 && ringBufferLoudness < currentMinLoudness) { - currentMinLoudness = ringBufferLoudness; - } - - if (ringBufferLoudness > currentMaxLoudness) { - currentMaxLoudness = ringBufferLoudness; - } + } } } From 939265356ba43f5ec69bdb509a9698645e2abbfc Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 21 Mar 2014 10:03:01 -0700 Subject: [PATCH 36/42] fix editVoxels.js mouse clicking --- examples/editVoxels.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 43bc485274..ac0b67407b 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -33,7 +33,7 @@ var zFightingSizeAdjust = 0.002; // used to adjust preview voxels to prevent z f var previewLineWidth = 1.5; var oldMode = Camera.getMode(); - +var trackAsOrbitOrPan = false; var isAdding = false; var isExtruding = false; var extrudeDirection = { x: 0, y: 0, z: 0 }; @@ -792,7 +792,6 @@ function mousePressEvent(event) { if (!trackAsOrbitOrPan) { var clickedOnSomething = false; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); - // If the user clicked on the thumb, handle the slider logic if (clickedOverlay == thumb) { From 0105029d9cfc185ffb01a5138f770c7df3f15b4c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 21 Mar 2014 10:10:25 -0700 Subject: [PATCH 37/42] output number of no reply domain checkins when finished --- assignment-client/src/audio/AudioMixer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index f5bed48a86..4a064087f1 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -393,6 +393,7 @@ void AudioMixer::run() { QCoreApplication::processEvents(); if (_isFinished) { + qDebug() << "NRDC:" << NodeList::getInstance()->getNumNoReplyDomainCheckIns(); break; } From 6f37bb54f889ff4f9c5ed4bb49bf29f3eb204719 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 21 Mar 2014 10:19:17 -0700 Subject: [PATCH 38/42] suppress useless audio debug --- libraries/audio/src/PositionalAudioRingBuffer.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index ccbe5d3f23..8fb3d64e7d 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -80,13 +80,11 @@ int PositionalAudioRingBuffer::parsePositionalData(const QByteArray& positionalB bool PositionalAudioRingBuffer::shouldBeAddedToMix(int numJitterBufferSamples) { if (!isNotStarvedOrHasMinimumSamples(NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL + numJitterBufferSamples)) { if (_shouldOutputStarveDebug) { - qDebug() << "Starved and do not have minimum samples to start. Buffer held back."; _shouldOutputStarveDebug = false; } return false; } else if (samplesAvailable() < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL) { - qDebug() << "Do not have number of samples needed for interval. Buffer starved."; _isStarved = true; // reset our _shouldOutputStarveDebug to true so the next is printed From c1cbc7980705bd02a83565042c3c2d356f1d9902 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 21 Mar 2014 10:26:09 -0700 Subject: [PATCH 39/42] move debug for no reply domain checkins to ThreadedAssignment --- assignment-client/src/audio/AudioMixer.cpp | 1 - libraries/shared/src/ThreadedAssignment.cpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 4a064087f1..f5bed48a86 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -393,7 +393,6 @@ void AudioMixer::run() { QCoreApplication::processEvents(); if (_isFinished) { - qDebug() << "NRDC:" << NodeList::getInstance()->getNumNoReplyDomainCheckIns(); break; } diff --git a/libraries/shared/src/ThreadedAssignment.cpp b/libraries/shared/src/ThreadedAssignment.cpp index c4282028ae..b3a54b1488 100644 --- a/libraries/shared/src/ThreadedAssignment.cpp +++ b/libraries/shared/src/ThreadedAssignment.cpp @@ -56,6 +56,7 @@ void ThreadedAssignment::commonInit(const QString& targetName, NodeType_t nodeTy void ThreadedAssignment::checkInWithDomainServerOrExit() { if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { + qDebug() << "NRDC:" << NodeList::getInstance()->getNumNoReplyDomainCheckIns(); setFinished(true); } else { NodeList::getInstance()->sendDomainServerCheckIn(); From 5b3340ce49edbbadd002db3d8b7b19dea64205d7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 21 Mar 2014 10:38:29 -0700 Subject: [PATCH 40/42] Add Ryan's updated smoothing factors. --- interface/resources/visage/tracker.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/visage/tracker.cfg b/interface/resources/visage/tracker.cfg index 10744da6e5..2efb7f3463 100644 --- a/interface/resources/visage/tracker.cfg +++ b/interface/resources/visage/tracker.cfg @@ -40,7 +40,7 @@ detect_strip_roi_width 2 detect_strip_roi_height 4 smoothing_factors - 150 5 -2 100 -1 50 50 0 + 5 25 -2 100 -1 50 25 0 #translation rotation action_units eyebrows mouth gaze eye_closure other process_eyes 1 From 5a6456c29f699f132920a550063d3bfaf77f517b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 21 Mar 2014 11:29:16 -0700 Subject: [PATCH 41/42] use an epsilon value to drop average loudness to zero --- libraries/audio/src/AudioRingBuffer.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp index cf35b6a20f..3b36d4cf4b 100644 --- a/libraries/audio/src/AudioRingBuffer.cpp +++ b/libraries/audio/src/AudioRingBuffer.cpp @@ -70,8 +70,13 @@ void AudioRingBuffer::updateAverageLoudnessForBoundarySamples(int numSamples) { const int TRAILING_AVERAGE_FRAMES = 100; const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES; const float PREVIOUS_FRAMES_RATIO = 1 - CURRENT_FRAME_RATIO; + const float LOUDNESS_EPSILON = 0.05; _averageLoudness = (_averageLoudness * PREVIOUS_FRAMES_RATIO) + (CURRENT_FRAME_RATIO * nextLoudness); + + if (_averageLoudness < LOUDNESS_EPSILON) { + _averageLoudness = 0; + } } qint64 AudioRingBuffer::readSamples(int16_t* destination, qint64 maxSamples) { From 1580896ec4bf7137a3b7d97823c8a824c51e6941 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 21 Mar 2014 11:42:02 -0700 Subject: [PATCH 42/42] don't use a trailing average for loudness --- libraries/audio/src/AudioRingBuffer.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp index 3b36d4cf4b..800f4f64ee 100644 --- a/libraries/audio/src/AudioRingBuffer.cpp +++ b/libraries/audio/src/AudioRingBuffer.cpp @@ -66,17 +66,8 @@ void AudioRingBuffer::updateAverageLoudnessForBoundarySamples(int numSamples) { nextLoudness /= numSamples; nextLoudness /= MAX_SAMPLE_VALUE; - - const int TRAILING_AVERAGE_FRAMES = 100; - const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES; - const float PREVIOUS_FRAMES_RATIO = 1 - CURRENT_FRAME_RATIO; - const float LOUDNESS_EPSILON = 0.05; - _averageLoudness = (_averageLoudness * PREVIOUS_FRAMES_RATIO) + (CURRENT_FRAME_RATIO * nextLoudness); - - if (_averageLoudness < LOUDNESS_EPSILON) { - _averageLoudness = 0; - } + _averageLoudness = nextLoudness; } qint64 AudioRingBuffer::readSamples(int16_t* destination, qint64 maxSamples) {