From 1d014358ee05ab16c7350442ff4b3e337c85b3d5 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 19 Sep 2014 17:06:03 -0700 Subject: [PATCH 01/36] Added possibility to change AudioInjector position --- libraries/audio/src/AudioInjector.cpp | 21 +++++++++++---------- libraries/audio/src/AudioInjector.h | 5 +++++ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 17b082f07a..f6dd7fa058 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -25,7 +25,8 @@ AudioInjector::AudioInjector(QObject* parent) : QObject(parent), _sound(NULL), _options(), - _shouldStop(false) + _shouldStop(false), + _currentSendPosition(0) { } @@ -95,17 +96,17 @@ void AudioInjector::injectAudio() { timer.start(); int nextFrame = 0; - int currentSendPosition = 0; + _currentSendPosition = 0; int numPreAudioDataBytes = injectAudioPacket.size(); bool shouldLoop = _options.getLoop(); // loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks quint16 outgoingInjectedAudioSequenceNumber = 0; - while (currentSendPosition < soundByteArray.size() && !_shouldStop) { + while (_currentSendPosition < soundByteArray.size() && !_shouldStop) { int bytesToCopy = std::min(NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL, - soundByteArray.size() - currentSendPosition); + soundByteArray.size() - _currentSendPosition); memcpy(injectAudioPacket.data() + positionOptionOffset, &_options.getPosition(), sizeof(_options.getPosition())); @@ -120,7 +121,7 @@ void AudioInjector::injectAudio() { memcpy(injectAudioPacket.data() + numPreSequenceNumberBytes, &outgoingInjectedAudioSequenceNumber, sizeof(quint16)); // copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet - memcpy(injectAudioPacket.data() + numPreAudioDataBytes, soundByteArray.data() + currentSendPosition, bytesToCopy); + memcpy(injectAudioPacket.data() + numPreAudioDataBytes, soundByteArray.data() + _currentSendPosition, bytesToCopy); // grab our audio mixer from the NodeList, if it exists NodeList* nodeList = NodeList::getInstance(); @@ -130,22 +131,22 @@ void AudioInjector::injectAudio() { nodeList->writeDatagram(injectAudioPacket, audioMixer); outgoingInjectedAudioSequenceNumber++; - currentSendPosition += bytesToCopy; + _currentSendPosition += bytesToCopy; // send two packets before the first sleep so the mixer can start playback right away - if (currentSendPosition != bytesToCopy && currentSendPosition < soundByteArray.size()) { + if (_currentSendPosition != bytesToCopy && _currentSendPosition < soundByteArray.size()) { // not the first packet and not done // sleep for the appropriate time int usecToSleep = (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - timer.nsecsElapsed() / 1000; if (usecToSleep > 0) { usleep(usecToSleep); - } + } } - if (shouldLoop && currentSendPosition == soundByteArray.size()) { - currentSendPosition = 0; + if (shouldLoop && _currentSendPosition >= soundByteArray.size()) { + _currentSendPosition = 0; } } } diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 966a4dd1cf..7f52cb3a29 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -26,16 +26,21 @@ class AudioInjector : public QObject { public: AudioInjector(QObject* parent); AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions); + + int getCurrentSendPosition() const { return _currentSendPosition; } public slots: void injectAudio(); void stop() { _shouldStop = true; } void setOptions(AudioInjectorOptions& options); + void setCurrentSendPosition(int currentSendPosition) { _currentSendPosition = currentSendPosition; } signals: void finished(); private: Sound* _sound; AudioInjectorOptions _options; bool _shouldStop; + int _currentSendPosition; + }; Q_DECLARE_METATYPE(AudioInjector*) From a11bf7f15ba2f7810f3837713a3fc9dba4c7b4db Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 19 Sep 2014 14:26:44 -0700 Subject: [PATCH 02/36] Decouple Rift head tracking from avatar's head position This makes the Rift's view reflect the user's physical movements exactly while the avatar follows the movements as best it can. --- interface/src/Application.cpp | 2 +- interface/src/avatar/Avatar.cpp | 4 ---- interface/src/avatar/Avatar.h | 1 - interface/src/avatar/MyAvatar.cpp | 2 +- interface/src/avatar/SkeletonModel.cpp | 7 +++++++ interface/src/avatar/SkeletonModel.h | 6 ++++++ 6 files changed, 15 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a20a6553d4..00cf3b9d01 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -682,7 +682,7 @@ void Application::paintGL() { if (whichCamera.getMode() == CAMERA_MODE_MIRROR) { OculusManager::display(whichCamera.getRotation(), whichCamera.getPosition(), whichCamera); } else { - OculusManager::display(_myAvatar->getWorldAlignedOrientation(), whichCamera.getPosition(), whichCamera); + OculusManager::display(_myAvatar->getWorldAlignedOrientation(), _myAvatar->getUprightHeadPosition(), whichCamera); } } else if (TV3DManager::isConnected()) { diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 6f421965db..22e7697b40 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1028,10 +1028,6 @@ float Avatar::getPelvisFloatingHeight() const { return -_skeletonModel.getBindExtents().minimum.y; } -float Avatar::getPelvisToHeadLength() const { - return glm::distance(_position, getHead()->getPosition()); -} - void Avatar::setShowDisplayName(bool showDisplayName) { if (!Menu::getInstance()->isOptionChecked(MenuOption::NamesAboveHeads)) { _displayNameAlpha = 0.0f; diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index c449a0d1b9..58add5aecd 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -223,7 +223,6 @@ protected: float getSkeletonHeight() const; float getHeadHeight() const; float getPelvisFloatingHeight() const; - float getPelvisToHeadLength() const; glm::vec3 getDisplayNamePosition(); void renderDisplayName(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 27f74f185d..e59de3628a 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -977,7 +977,7 @@ void MyAvatar::clearLookAtTargetAvatar() { } glm::vec3 MyAvatar::getUprightHeadPosition() const { - return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, getPelvisToHeadLength(), 0.0f); + return _position + getWorldAlignedOrientation() * _skeletonModel.getDefaultHeadModelPosition(); } const float SCRIPT_PRIORITY = DEFAULT_PRIORITY + 1.0f; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 86ca42b15e..2e53412be4 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -656,6 +656,13 @@ void SkeletonModel::buildShapes() { // This method moves the shapes to their default positions in Model frame. computeBoundingShape(geometry); + int headJointIndex = _geometry->getFBXGeometry().headJointIndex; + if (0 <= headJointIndex && headJointIndex < _jointStates.size()) { + getJointPosition(headJointIndex, _defaultHeadModelPosition); + } else { + _defaultHeadModelPosition = glm::vec3(0.f, 0.f, 0.f); + } + // While the shapes are in their default position we disable collisions between // joints that are currently colliding. disableCurrentSelfCollisions(); diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index ca0007ddb4..036811a926 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -97,6 +97,10 @@ public: /// \return whether or not both eye meshes were found bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; + /// Gets the default position of the head in model frame coordinates. + /// \return whether or not the head was found. + glm::vec3 getDefaultHeadModelPosition() const { return _defaultHeadModelPosition; } + virtual void updateVisibleJointStates(); SkeletonRagdoll* buildRagdoll(); @@ -146,6 +150,8 @@ private: CapsuleShape _boundingShape; glm::vec3 _boundingShapeLocalOffset; SkeletonRagdoll* _ragdoll; + + glm::vec3 _defaultHeadModelPosition; }; #endif // hifi_SkeletonModel_h From 466dbdcee563e4a89d65c20080a50dc2fdfcaf95 Mon Sep 17 00:00:00 2001 From: stojce Date: Mon, 22 Sep 2014 20:51:19 +0200 Subject: [PATCH 03/36] added address bar dialog --- interface/src/Application.cpp | 9 +- interface/src/Menu.cpp | 20 +---- interface/src/Menu.h | 3 + interface/src/ui/AddressBarDialog.cpp | 91 +++++++++++++++++++++ interface/src/ui/AddressBarDialog.h | 46 +++++++++++ libraries/networking/src/AddressManager.cpp | 6 +- libraries/networking/src/AddressManager.h | 2 +- 7 files changed, 153 insertions(+), 24 deletions(-) create mode 100644 interface/src/ui/AddressBarDialog.cpp create mode 100644 interface/src/ui/AddressBarDialog.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a20a6553d4..d2dc48ed59 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -923,12 +923,11 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_Return: case Qt::Key_Enter: - if (isMeta) { - Menu::getInstance()->triggerOption(MenuOption::AddressBar); - } else { - Menu::getInstance()->triggerOption(MenuOption::Chat); - } + Menu::getInstance()->triggerOption(MenuOption::AddressBar); + break; + case Qt::Key_CapsLock: + Menu::getInstance()->triggerOption(MenuOption::Chat); break; case Qt::Key_N: diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 454d21be20..026d532559 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -173,7 +173,7 @@ Menu::Menu() : SLOT(toggleLocationList())); addActionToQMenuAndActionHash(fileMenu, MenuOption::AddressBar, - Qt::CTRL | Qt::Key_Enter, + Qt::Key_Enter, this, SLOT(toggleAddressBar())); @@ -1154,22 +1154,10 @@ void Menu::changePrivateKey() { } void Menu::toggleAddressBar() { - - QInputDialog addressBarDialog(Application::getInstance()->getWindow()); - addressBarDialog.setWindowTitle("Address Bar"); - addressBarDialog.setWindowFlags(Qt::Sheet); - addressBarDialog.setLabelText("place, domain, @user, example.com, /position/orientation"); - - addressBarDialog.resize(addressBarDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, - addressBarDialog.size().height()); - - int dialogReturn = addressBarDialog.exec(); - if (dialogReturn == QDialog::Accepted && !addressBarDialog.textValue().isEmpty()) { - // let the AddressManger figure out what to do with this - AddressManager::getInstance().handleLookupString(addressBarDialog.textValue()); + if (!_addressBarDialog) { + _addressBarDialog = new AddressBarDialog(); + _addressBarDialog->show(); } - - sendFakeEnterEvent(); } void Menu::displayAddressOfflineMessage() { diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 5a2240b3d8..8a0653c4ec 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -28,6 +28,8 @@ #endif #include "location/LocationManager.h" + +#include "ui/AddressBarDialog.h" #include "ui/ChatWindow.h" #include "ui/DataWebDialog.h" #include "ui/JSConsole.h" @@ -299,6 +301,7 @@ private: QPointer _attachmentsDialog; QPointer _animationsDialog; QPointer _loginDialog; + QPointer _addressBarDialog; bool _hasLoginDialogDisplayed; QAction* _chatAction; QString _snapshotsLocation; diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp new file mode 100644 index 0000000000..f24dcb1ba6 --- /dev/null +++ b/interface/src/ui/AddressBarDialog.cpp @@ -0,0 +1,91 @@ +// +// AddressBarDialog.cpp +// interface/src/ui +// +// Created by Stojce Slavkovski on 9/22/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AddressBarDialog.h" +#include "AddressManager.h" +#include "Application.h" + +AddressBarDialog::AddressBarDialog() : + FramelessDialog(Application::getInstance()->getWindow(), 0, FramelessDialog::POSITION_TOP){ + setupUI(); +} + +void AddressBarDialog::setupUI() { + + setModal(true); + setWindowModality(Qt::WindowModal); + setHideOnBlur(false); + + QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + setSizePolicy(sizePolicy); + setMinimumSize(QSize(560, 100)); + setStyleSheet("font-family: Helvetica, Arial, sans-serif;"); + + verticalLayout = new QVBoxLayout(this); + + addressLayout = new QHBoxLayout(); + addressLayout->setContentsMargins(0, 0, 10, 0); + + leftSpacer = new QSpacerItem(20, 20, QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); + addressLayout->addItem(leftSpacer); + + addressLineEdit = new QLineEdit(this); + addressLineEdit->setPlaceholderText("Go to: domain, @user, #location"); + QSizePolicy sizePolicyLineEdit(QSizePolicy::Preferred, QSizePolicy::Fixed); + sizePolicyLineEdit.setHorizontalStretch(60); + addressLineEdit->setSizePolicy(sizePolicyLineEdit); + addressLineEdit->setMinimumSize(QSize(200, 50)); + addressLineEdit->setMaximumSize(QSize(615, 50)); + QFont font("Helvetica,Arial,sans-serif", 20); + addressLineEdit->setFont(font); + addressLineEdit->setStyleSheet("padding: 0 10px;"); + addressLayout->addWidget(addressLineEdit); + + buttonSpacer = new QSpacerItem(10, 20, QSizePolicy::Fixed, QSizePolicy::Minimum); + addressLayout->addItem(buttonSpacer); + + goButton = new QPushButton(this); + goButton->setSizePolicy(sizePolicy); + goButton->setMinimumSize(QSize(54, 54)); + goButton->setFont(font); + goButton->setText("➡"); + goButton->setStyleSheet("background: #0e7077; color: #e7eeee; border-radius: 4px;"); + goButton->setIconSize(QSize(32, 32)); + goButton->setDefault(true); + goButton->setFlat(true); + addressLayout->addWidget(goButton); + + rightSpacer = new QSpacerItem(20, 20, QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); + addressLayout->addItem(rightSpacer); + + closeButton = new QPushButton(this); + closeButton->setSizePolicy(sizePolicy); + closeButton->setMinimumSize(QSize(16, 16)); + closeButton->setMaximumSize(QSize(16, 16)); + closeButton->setStyleSheet("color: #333"); + QIcon icon(Application::resourcesPath() + "styles/close.svg"); + closeButton->setIcon(icon); + closeButton->setFlat(true); + addressLayout->addWidget(closeButton, 0, Qt::AlignRight); + + verticalLayout->addLayout(addressLayout); + + connect(addressLineEdit, &QLineEdit::returnPressed, this, &AddressBarDialog::accept); + connect(goButton, &QPushButton::clicked, this, &AddressBarDialog::accept); + connect(closeButton, &QPushButton::clicked, this, &QDialog::close); +} + +void AddressBarDialog::accept() { + // let the AddressManger figure out what to do with this + if (AddressManager::getInstance().handleLookupString(addressLineEdit->text())) { + close(); + } +} \ No newline at end of file diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h new file mode 100644 index 0000000000..0f77250097 --- /dev/null +++ b/interface/src/ui/AddressBarDialog.h @@ -0,0 +1,46 @@ +// +// AddressBarDialog.h +// interface/src/ui +// +// Created by Stojce Slavkovski on 9/22/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AddressBarDialog_h +#define hifi_AddressBarDialog_h + +#include "FramelessDialog.h" + +#include +#include +#include +#include + + +class AddressBarDialog : public FramelessDialog { + Q_OBJECT + +public: + AddressBarDialog(); + +private: + void setupUI(); + + QVBoxLayout *verticalLayout; + QHBoxLayout *addressLayout; + QSpacerItem *leftSpacer; + QLineEdit *addressLineEdit; + QSpacerItem *buttonSpacer; + QPushButton *goButton; + QSpacerItem *rightSpacer; + QPushButton *closeButton; + +private slots: + void accept(); + +}; + +#endif // hifi_AddressBarDialog_h diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 8ea6d1107a..0fd0d72755 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -85,7 +85,7 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) { return false; } -void AddressManager::handleLookupString(const QString& lookupString) { +bool AddressManager::handleLookupString(const QString& lookupString) { if (!lookupString.isEmpty()) { // make this a valid hifi URL and handle it off to handleUrl QString sanitizedString = lookupString; @@ -100,8 +100,10 @@ void AddressManager::handleLookupString(const QString& lookupString) { lookupURL = QUrl(lookupString); } - handleUrl(lookupURL); + return handleUrl(lookupURL); } + + return false; } void AddressManager::handleAPIResponse(const QJsonObject &jsonObject) { diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 2590e8f80c..4715ff0592 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -31,7 +31,7 @@ public: void attemptPlaceNameLookup(const QString& lookupString); public slots: - void handleLookupString(const QString& lookupString); + bool handleLookupString(const QString& lookupString); void handleAPIResponse(const QJsonObject& jsonObject); void handleAPIError(QNetworkReply& errorReply); From 8393fcc6ebe95ddda4ef3b7f58e77eeb1fc6acaa Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 22 Sep 2014 14:01:02 -0700 Subject: [PATCH 04/36] Frame computation + timerOffset member When jumping inside a recording --- libraries/avatars/src/Player.cpp | 46 +++++++++++++++++++++++++++++--- libraries/avatars/src/Player.h | 4 +++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp index b548d452e7..64cfeaf3da 100644 --- a/libraries/avatars/src/Player.cpp +++ b/libraries/avatars/src/Player.cpp @@ -18,6 +18,7 @@ Player::Player(AvatarData* avatar) : _recording(new Recording()), + _timerOffset(0), _avatar(avatar), _audioThread(NULL), _playFromCurrentPosition(true), @@ -38,7 +39,7 @@ bool Player::isPlaying() const { qint64 Player::elapsed() const { if (isPlaying()) { - return _timer.elapsed(); + return _timerOffset + _timer.elapsed(); } else { return 0; } @@ -97,8 +98,9 @@ void Player::startPlaying() { _avatar->setForceFaceshiftConnected(true); qDebug() << "Recorder::startPlaying()"; - _currentFrame = 0; setupAudioThread(); + _currentFrame = 0; + _timerOffset = 0; _timer.start(); } } @@ -156,6 +158,7 @@ void Player::loopRecording() { cleanupAudioThread(); setupAudioThread(); _currentFrame = 0; + _timerOffset = 0; _timer.restart(); } @@ -213,6 +216,43 @@ void Player::play() { _injector->setOptions(_options); } +void Player::setCurrentFrame(int currentFrame) { + if (_recording && currentFrame >= _recording->getFrameNumber()) { + stopPlaying(); + return; + } + + _currentFrame = currentFrame; + _timerOffset = _recording->getFrameTimestamp(_currentFrame); +} + +void Player::setCurrentTime(qint64 currentTime) { + if (currentTime < 0 || currentTime >= _recording->getLength()) { + stopPlaying(); + return; + } + + _timerOffset = currentTime; + + // Find correct frame + int bestGuess = 0; + int lowestBound = 0; + int highestBound = _recording->getFrameNumber() - 1; + while (_recording->getFrameTimestamp(bestGuess) <= _timerOffset && + _recording->getFrameTimestamp(bestGuess + 1) > _timerOffset) { + if (_recording->getFrameTimestamp(bestGuess) < _timerOffset) { + lowestBound = bestGuess; + } else { + highestBound = bestGuess; + } + + bestGuess = lowestBound + + (highestBound - lowestBound) * + (_timerOffset - _recording->getFrameTimestamp(lowestBound)) / + (_recording->getFrameTimestamp(highestBound) - _recording->getFrameTimestamp(lowestBound)); + } +} + void Player::setPlayFromCurrentLocation(bool playFromCurrentLocation) { _playFromCurrentPosition = playFromCurrentLocation; } @@ -227,7 +267,7 @@ bool Player::computeCurrentFrame() { } while (_currentFrame < _recording->getFrameNumber() - 1 && - _recording->getFrameTimestamp(_currentFrame) < _timer.elapsed()) { + _recording->getFrameTimestamp(_currentFrame) < elapsed()) { ++_currentFrame; } diff --git a/libraries/avatars/src/Player.h b/libraries/avatars/src/Player.h index 772209d435..2156648389 100644 --- a/libraries/avatars/src/Player.h +++ b/libraries/avatars/src/Player.h @@ -41,6 +41,9 @@ public slots: void loadRecording(RecordingPointer recording); void play(); + void setCurrentFrame(int currentFrame); + void setCurrentTime(qint64 currentTime); + void setPlayFromCurrentLocation(bool playFromCurrentPosition); void setLoop(bool loop) { _loop = loop; } void useAttachements(bool useAttachments) { _useAttachments = useAttachments; } @@ -57,6 +60,7 @@ private: QElapsedTimer _timer; RecordingPointer _recording; int _currentFrame; + qint64 _timerOffset; QSharedPointer _injector; AudioInjectorOptions _options; From f95e20153c3a366eb1a04022546290c290ffc926 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 22 Sep 2014 15:13:08 -0700 Subject: [PATCH 05/36] Fix upright head model position calculation for avatar scale changes --- interface/src/avatar/SkeletonModel.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 2e53412be4..9522e54e26 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -658,7 +658,11 @@ void SkeletonModel::buildShapes() { int headJointIndex = _geometry->getFBXGeometry().headJointIndex; if (0 <= headJointIndex && headJointIndex < _jointStates.size()) { + int rootJointIndex = _geometry->getFBXGeometry().rootJointIndex; + glm::vec3 rootModelPosition; getJointPosition(headJointIndex, _defaultHeadModelPosition); + getJointPosition(rootJointIndex, rootModelPosition); + _defaultHeadModelPosition = _defaultHeadModelPosition - rootModelPosition; } else { _defaultHeadModelPosition = glm::vec3(0.f, 0.f, 0.f); } From 5a7b7cca44d2bc611928cb76118dfd846461a3ef Mon Sep 17 00:00:00 2001 From: stojce Date: Tue, 23 Sep 2014 11:32:16 +0200 Subject: [PATCH 06/36] Add finish lookup event --- interface/src/ui/AddressBarDialog.cpp | 10 ++++++---- libraries/networking/src/AddressManager.cpp | 11 +++++++---- libraries/networking/src/AddressManager.h | 3 ++- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp index f24dcb1ba6..274152742f 100644 --- a/interface/src/ui/AddressBarDialog.cpp +++ b/interface/src/ui/AddressBarDialog.cpp @@ -78,14 +78,16 @@ void AddressBarDialog::setupUI() { verticalLayout->addLayout(addressLayout); - connect(addressLineEdit, &QLineEdit::returnPressed, this, &AddressBarDialog::accept); connect(goButton, &QPushButton::clicked, this, &AddressBarDialog::accept); connect(closeButton, &QPushButton::clicked, this, &QDialog::close); } void AddressBarDialog::accept() { - // let the AddressManger figure out what to do with this - if (AddressManager::getInstance().handleLookupString(addressLineEdit->text())) { - close(); + if (!addressLineEdit->text().isEmpty()) { + goButton->setStyleSheet("background: #333; color: #e7eeee; border-radius: 4px;"); + + AddressManager& addressManager = AddressManager::getInstance(); + connect(&addressManager, &AddressManager::lookupResultsFinished, this, &QDialog::close); + addressManager.handleLookupString(addressLineEdit->text()); } } \ No newline at end of file diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 0fd0d72755..4b3a84c603 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -80,12 +80,13 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) { // if this is a relative path then handle it as a relative viewpoint handleRelativeViewpoint(lookupUrl.path()); + emit lookupResultsFinished(); } return false; } -bool AddressManager::handleLookupString(const QString& lookupString) { +void AddressManager::handleLookupString(const QString& lookupString) { if (!lookupString.isEmpty()) { // make this a valid hifi URL and handle it off to handleUrl QString sanitizedString = lookupString; @@ -100,10 +101,8 @@ bool AddressManager::handleLookupString(const QString& lookupString) { lookupURL = QUrl(lookupString); } - return handleUrl(lookupURL); + handleUrl(lookupURL); } - - return false; } void AddressManager::handleAPIResponse(const QJsonObject &jsonObject) { @@ -151,6 +150,7 @@ void AddressManager::handleAPIResponse(const QJsonObject &jsonObject) { // we've been told that this result exists but is offline, emit our signal so the application can handle emit lookupResultIsOffline(); } + emit lookupResultsFinished(); } void AddressManager::handleAPIError(QNetworkReply& errorReply) { @@ -159,6 +159,7 @@ void AddressManager::handleAPIError(QNetworkReply& errorReply) { if (errorReply.error() == QNetworkReply::ContentNotFoundError) { emit lookupResultIsNotFound(); } + emit lookupResultsFinished(); } const QString GET_PLACE = "/api/v1/places/%1"; @@ -182,6 +183,7 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString) { if (hostnameRegex.indexIn(lookupString) != -1) { emit possibleDomainChangeRequired(hostnameRegex.cap(0)); + emit lookupResultsFinished(); return true; } @@ -189,6 +191,7 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString) { if (ipAddressRegex.indexIn(lookupString) != -1) { emit possibleDomainChangeRequired(ipAddressRegex.cap(0)); + emit lookupResultsFinished(); return true; } diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 4715ff0592..f7cc7c52ee 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -31,12 +31,13 @@ public: void attemptPlaceNameLookup(const QString& lookupString); public slots: - bool handleLookupString(const QString& lookupString); + void handleLookupString(const QString& lookupString); void handleAPIResponse(const QJsonObject& jsonObject); void handleAPIError(QNetworkReply& errorReply); void goToUser(const QString& username); signals: + void lookupResultsFinished(); void lookupResultIsOffline(); void lookupResultIsNotFound(); void possibleDomainChangeRequired(const QString& newHostname); From 13a5dbf4896b2243a5309cc8ae25ecc05d79b255 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 23 Sep 2014 11:01:21 -0700 Subject: [PATCH 07/36] Added frame computation and JS hooks --- libraries/avatars/src/AvatarData.cpp | 38 ++++++++++++++++++++++++++++ libraries/avatars/src/AvatarData.h | 5 ++++ libraries/avatars/src/Player.cpp | 34 +++++++++++++++++++------ libraries/avatars/src/Player.h | 2 ++ libraries/avatars/src/Recording.cpp | 5 +++- 5 files changed, 75 insertions(+), 9 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 5ac0c69864..020a993f4f 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -625,6 +625,32 @@ qint64 AvatarData::playerLength() { return _player->getRecording()->getLength(); } +int AvatarData::playerCurrentFrame() { + if (!_player) { + return 0; + } + if (QThread::currentThread() != thread()) { + int result; + QMetaObject::invokeMethod(this, "playerCurrentFrame", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(int, result)); + return result; + } + return _player->getCurrentFrame(); +} + +int AvatarData::playerFrameNumber() { + if (!_player) { + return 0; + } + if (QThread::currentThread() != thread()) { + int result; + QMetaObject::invokeMethod(this, "playerFrameNumber", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(int, result)); + return result; + } + return _player->getRecording()->getFrameNumber(); +} + void AvatarData::loadRecording(QString filename) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "loadRecording", Qt::BlockingQueuedConnection, @@ -649,6 +675,18 @@ void AvatarData::startPlaying() { _player->startPlaying(); } +void AvatarData::setPlayerFrame(int frame) { + if (_player) { + _player->setCurrentFrame(frame); + } +} + +void AvatarData::setPlayerTime(qint64 time) { + if (_player) { + _player->setCurrentTime(time); + } +} + void AvatarData::setPlayFromCurrentLocation(bool playFromCurrentLocation) { if (_player) { _player->setPlayFromCurrentLocation(playFromCurrentLocation); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index bc68103ca6..46061b162b 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -298,8 +298,13 @@ public slots: bool isPlaying(); qint64 playerElapsed(); qint64 playerLength(); + int playerCurrentFrame(); + int playerFrameNumber(); + void loadRecording(QString filename); void startPlaying(); + void setPlayerFrame(int frame); + void setPlayerTime(qint64 time); void setPlayFromCurrentLocation(bool playFromCurrentLocation); void setPlayerLoop(bool loop); void setPlayerUseDisplayName(bool useDisplayName); diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp index 64cfeaf3da..ca5d0eec8d 100644 --- a/libraries/avatars/src/Player.cpp +++ b/libraries/avatars/src/Player.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include #include @@ -224,6 +225,9 @@ void Player::setCurrentFrame(int currentFrame) { _currentFrame = currentFrame; _timerOffset = _recording->getFrameTimestamp(_currentFrame); + _timer.restart(); + + setAudionInjectorPosition(); } void Player::setCurrentTime(qint64 currentTime) { @@ -232,15 +236,14 @@ void Player::setCurrentTime(qint64 currentTime) { return; } - _timerOffset = currentTime; - // Find correct frame - int bestGuess = 0; int lowestBound = 0; int highestBound = _recording->getFrameNumber() - 1; - while (_recording->getFrameTimestamp(bestGuess) <= _timerOffset && - _recording->getFrameTimestamp(bestGuess + 1) > _timerOffset) { - if (_recording->getFrameTimestamp(bestGuess) < _timerOffset) { + int bestGuess = 0; + while (!(_recording->getFrameTimestamp(bestGuess) <= currentTime && + _recording->getFrameTimestamp(bestGuess + 1) > currentTime)) { + + if (_recording->getFrameTimestamp(bestGuess) <= currentTime) { lowestBound = bestGuess; } else { highestBound = bestGuess; @@ -248,9 +251,24 @@ void Player::setCurrentTime(qint64 currentTime) { bestGuess = lowestBound + (highestBound - lowestBound) * - (_timerOffset - _recording->getFrameTimestamp(lowestBound)) / - (_recording->getFrameTimestamp(highestBound) - _recording->getFrameTimestamp(lowestBound)); + (float)(currentTime - _recording->getFrameTimestamp(lowestBound)) / + (float)(_recording->getFrameTimestamp(highestBound) - _recording->getFrameTimestamp(lowestBound)); } + + _currentFrame = bestGuess; + _timerOffset = _recording->getFrameTimestamp(bestGuess); + _timer.restart(); + + setAudionInjectorPosition(); +} + +void Player::setAudionInjectorPosition() { + int MSEC_PER_SEC = 1000; + int SAMPLE_SIZE = 2; // 16 bits + int CHANNEL_COUNT = 1; + int FRAME_SIZE = SAMPLE_SIZE * CHANNEL_COUNT; + int currentAudioFrame = elapsed() * FRAME_SIZE * (SAMPLE_RATE / MSEC_PER_SEC); + _injector->setCurrentSendPosition(currentAudioFrame); } void Player::setPlayFromCurrentLocation(bool playFromCurrentLocation) { diff --git a/libraries/avatars/src/Player.h b/libraries/avatars/src/Player.h index 2156648389..749722df28 100644 --- a/libraries/avatars/src/Player.h +++ b/libraries/avatars/src/Player.h @@ -33,6 +33,7 @@ public: qint64 elapsed() const; RecordingPointer getRecording() const { return _recording; } + int getCurrentFrame() const { return _currentFrame; } public slots: void startPlaying(); @@ -55,6 +56,7 @@ private: void setupAudioThread(); void cleanupAudioThread(); void loopRecording(); + void setAudionInjectorPosition(); bool computeCurrentFrame(); QElapsedTimer _timer; diff --git a/libraries/avatars/src/Recording.cpp b/libraries/avatars/src/Recording.cpp index f84b803c5f..a2d6a322a0 100644 --- a/libraries/avatars/src/Recording.cpp +++ b/libraries/avatars/src/Recording.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include #include @@ -60,6 +61,9 @@ qint32 Recording::getFrameTimestamp(int i) const { if (i >= _timestamps.size()) { return getLength(); } + if (i < 0) { + return 0; + } return _timestamps[i]; } @@ -770,7 +774,6 @@ RecordingPointer readRecordingFromRecFile(RecordingPointer recording, const QStr fileStream >> audioArray; // Cut down audio if necessary - int SAMPLE_RATE = 48000; // 48 kHz int SAMPLE_SIZE = 2; // 16 bits int MSEC_PER_SEC = 1000; int audioLength = recording->getLength() * SAMPLE_SIZE * (SAMPLE_RATE / MSEC_PER_SEC); From 33236b6c40d92a0dbfdc50413e69279b6b573945 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 23 Sep 2014 11:02:25 -0700 Subject: [PATCH 08/36] Removed extra space --- examples/editVoxels.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index f4e468fd40..3543f062e7 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -1127,8 +1127,6 @@ function keyPressEvent(event) { } else if (event.text == "z") { undoSound.playRandom(); } - - } trackKeyPressEvent(event); // used by preview support From fb982870c6bd331821dab59efee694e1e387a970 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 23 Sep 2014 11:51:19 -0700 Subject: [PATCH 09/36] Added passive play bar to Recorder.js --- examples/Recorder.js | 71 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 4 deletions(-) diff --git a/examples/Recorder.js b/examples/Recorder.js index 8efa9408a9..168f1161c2 100644 --- a/examples/Recorder.js +++ b/examples/Recorder.js @@ -40,6 +40,7 @@ var timerOffset; setupToolBar(); var timer = null; +var slider = null; setupTimer(); var watchStop = false; @@ -115,6 +116,30 @@ function setupTimer() { alpha: 1.0, visible: true }); + + slider = { x: 0, y: 0, + w: 200, h: 20, + pos: 0.0, // 0.0 <= pos <= 1.0 + }; + slider.background = Overlays.addOverlay("text", { + text: "", + backgroundColor: { red: 128, green: 128, blue: 128 }, + x: slider.x, y: slider.y, + width: slider.w, + height: slider.h, + alpha: 1.0, + visible: true + }); + slider.foreground = Overlays.addOverlay("text", { + text: "", + backgroundColor: { red: 200, green: 200, blue: 200 }, + x: slider.x, y: slider.y, + width: slider.pos * slider.w, + height: slider.h, + alpha: 1.0, + visible: true + }); + } function updateTimer() { @@ -131,6 +156,18 @@ function updateTimer() { text: text }) toolBar.changeSpacing(text.length * 8 + ((MyAvatar.isRecording()) ? 15 : 0), spacing); + + if (MyAvatar.isRecording()) { + slider.pos = 1.0; + } else if (!MyAvatar.isPlaying) { + slider.pos = 0.0; + } else { + slider.pos = MyAvatar.playerElapsed() / MyAvatar.playerLength(); + } + + Overlays.editOverlay(slider.foreground, { + width: slider.pos * slider.w + }); } function formatTime(time) { @@ -163,7 +200,19 @@ function moveUI() { Overlays.editOverlay(timer, { x: relative.x + timerOffset - ToolBar.SPACING, y: windowDimensions.y - relative.y - ToolBar.SPACING - }); + }); + + slider.x = relative.x; + slider.y = windowDimensions.y - relative.y - 100; + + Overlays.editOverlay(slider.background, { + x: slider.x, + y: slider.y, + }); + Overlays.editOverlay(slider.foreground, { + x: slider.x, + y: slider.y, + }); } function mousePressEvent(event) { @@ -234,11 +283,21 @@ function mousePressEvent(event) { toolBar.setAlpha(ALPHA_ON, saveIcon); } } - } else { + } else if (slider.x < event.x < slider.x + slider.w && + slider.y < event.y < slider.y + slider.h) { + } } +function mouseMoveEvent(event) { + +} + +function mouseReleaseEvent(event) { + +} + function update() { var newDimensions = Controller.getViewportDimensions(); if (windowDimensions.x != newDimensions.x || @@ -264,11 +323,15 @@ function scriptEnding() { if (MyAvatar.isPlaying()) { MyAvatar.stopPlaying(); } - toolBar.cleanup(); - Overlays.deleteOverlay(timer); + toolBar.cleanup(); + Overlays.deleteOverlay(timer); + Overlays.deleteOverlay(slider.background); + Overlays.deleteOverlay(slider.foreground); } Controller.mousePressEvent.connect(mousePressEvent); +Controller.mouseMoveEvent.connect(mouseMoveEvent); +Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Script.update.connect(update); Script.scriptEnding.connect(scriptEnding); From 95c00b1d4ee443c962d889de3153448869e06075 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 23 Sep 2014 14:18:54 -0700 Subject: [PATCH 10/36] Added pause behavior + change frame computation --- libraries/avatars/src/Player.cpp | 74 +++++++++++++++++++++++++------- libraries/avatars/src/Player.h | 5 ++- 2 files changed, 62 insertions(+), 17 deletions(-) diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp index ca5d0eec8d..fc1e7269a6 100644 --- a/libraries/avatars/src/Player.cpp +++ b/libraries/avatars/src/Player.cpp @@ -22,6 +22,7 @@ Player::Player(AvatarData* avatar) : _timerOffset(0), _avatar(avatar), _audioThread(NULL), + _isPaused(false), _playFromCurrentPosition(true), _loop(false), _useAttachments(true), @@ -38,16 +39,26 @@ bool Player::isPlaying() const { return _timer.isValid(); } +bool Player::isPaused() const { + return _isPaused; +} + qint64 Player::elapsed() const { if (isPlaying()) { return _timerOffset + _timer.elapsed(); + } else if (isPaused()) { + return _timerOffset; } else { return 0; } } void Player::startPlaying() { - if (_recording && _recording->getFrameNumber() > 0) { + if (!_recording || _recording->getFrameNumber() <= 1) { + return; + } + + if (!_isPaused) { _currentContext.globalTimestamp = usecTimestampNow(); _currentContext.domain = NodeList::getInstance()->getDomainHandler().getHostname(); _currentContext.position = _avatar->getPosition(); @@ -103,6 +114,13 @@ void Player::startPlaying() { _currentFrame = 0; _timerOffset = 0; _timer.start(); + } else { + qDebug() << "Recorder::startPlaying(): Unpause"; + setupAudioThread(); + _timer.start(); + _isPaused = false; + + setCurrentFrame(_pausedFrame); } } @@ -110,6 +128,7 @@ void Player::stopPlaying() { if (!isPlaying()) { return; } + _isPaused = false; _timer.invalidate(); cleanupAudioThread(); _avatar->clearJointsData(); @@ -133,6 +152,16 @@ void Player::stopPlaying() { qDebug() << "Recorder::stopPlaying()"; } +void Player::pausePlayer() { + _timerOffset = elapsed(); + _timer.invalidate(); + cleanupAudioThread(); + + _isPaused = true; + _pausedFrame = _currentFrame; + qDebug() << "Recorder::pausePlayer()"; +} + void Player::setupAudioThread() { _audioThread = new QThread(); _options.setPosition(_avatar->getPosition()); @@ -170,10 +199,13 @@ void Player::loadFromFile(const QString& file) { _recording = RecordingPointer(new Recording()); } readRecordingFromFile(_recording, file); + + _isPaused = false; } void Player::loadRecording(RecordingPointer recording) { _recording = recording; + _isPaused = false; } void Player::play() { @@ -218,14 +250,14 @@ void Player::play() { } void Player::setCurrentFrame(int currentFrame) { - if (_recording && currentFrame >= _recording->getFrameNumber()) { + if (_recording && (currentFrame < 0 || currentFrame >= _recording->getFrameNumber())) { stopPlaying(); return; } _currentFrame = currentFrame; _timerOffset = _recording->getFrameTimestamp(_currentFrame); - _timer.restart(); + _timer.start(); setAudionInjectorPosition(); } @@ -239,25 +271,35 @@ void Player::setCurrentTime(qint64 currentTime) { // Find correct frame int lowestBound = 0; int highestBound = _recording->getFrameNumber() - 1; - int bestGuess = 0; - while (!(_recording->getFrameTimestamp(bestGuess) <= currentTime && - _recording->getFrameTimestamp(bestGuess + 1) > currentTime)) { + while (lowestBound + 1 != highestBound) { + assert(lowestBound < highestBound); - if (_recording->getFrameTimestamp(bestGuess) <= currentTime) { - lowestBound = bestGuess; - } else { - highestBound = bestGuess; - } - - bestGuess = lowestBound + + int bestGuess = lowestBound + (highestBound - lowestBound) * (float)(currentTime - _recording->getFrameTimestamp(lowestBound)) / (float)(_recording->getFrameTimestamp(highestBound) - _recording->getFrameTimestamp(lowestBound)); + + + if (_recording->getFrameTimestamp(bestGuess) <= currentTime) { + if (currentTime < _recording->getFrameTimestamp(bestGuess + 1)) { + lowestBound = bestGuess; + highestBound = bestGuess + 1; + } else { + lowestBound = bestGuess + 1; + } + } else { + if (_recording->getFrameTimestamp(bestGuess - 1) <= currentTime) { + lowestBound = bestGuess - 1; + highestBound = bestGuess; + } else { + highestBound = bestGuess - 1; + } + } } - _currentFrame = bestGuess; - _timerOffset = _recording->getFrameTimestamp(bestGuess); - _timer.restart(); + _currentFrame = lowestBound; + _timerOffset = _recording->getFrameTimestamp(lowestBound); + _timer.start(); setAudionInjectorPosition(); } diff --git a/libraries/avatars/src/Player.h b/libraries/avatars/src/Player.h index 749722df28..5746279bac 100644 --- a/libraries/avatars/src/Player.h +++ b/libraries/avatars/src/Player.h @@ -30,6 +30,7 @@ public: Player(AvatarData* avatar); bool isPlaying() const; + bool isPaused() const; qint64 elapsed() const; RecordingPointer getRecording() const { return _recording; } @@ -38,6 +39,7 @@ public: public slots: void startPlaying(); void stopPlaying(); + void pausePlayer(); void loadFromFile(const QString& file); void loadRecording(RecordingPointer recording); void play(); @@ -70,6 +72,8 @@ private: AvatarData* _avatar; QThread* _audioThread; + bool _isPaused; + int _pausedFrame; RecordingContext _currentContext; bool _playFromCurrentPosition; @@ -78,7 +82,6 @@ private: bool _useDisplayName; bool _useHeadURL; bool _useSkeletonURL; - }; #endif // hifi_Player_h \ No newline at end of file From 3b33db9a6b5cc0bc5efcfa0dd64b5a5cc0acd3fc Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 23 Sep 2014 14:19:16 -0700 Subject: [PATCH 11/36] Added pause/setCurrentTime/setCurrentFrame JShooks --- libraries/avatars/src/AvatarData.cpp | 49 ++++++++++++---------------- libraries/avatars/src/AvatarData.h | 2 ++ 2 files changed, 22 insertions(+), 29 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 020a993f4f..636096124c 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -587,18 +587,13 @@ bool AvatarData::hasReferential() { } bool AvatarData::isPlaying() { - if (!_player) { - return false; - } - if (QThread::currentThread() != thread()) { - bool result; - QMetaObject::invokeMethod(this, "isPlaying", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(bool, result)); - return result; - } return _player && _player->isPlaying(); } +bool AvatarData::isPaused() { + return _player && _player->isPaused(); +} + qint64 AvatarData::playerElapsed() { if (!_player) { return 0; @@ -626,29 +621,11 @@ qint64 AvatarData::playerLength() { } int AvatarData::playerCurrentFrame() { - if (!_player) { - return 0; - } - if (QThread::currentThread() != thread()) { - int result; - QMetaObject::invokeMethod(this, "playerCurrentFrame", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(int, result)); - return result; - } - return _player->getCurrentFrame(); + return (_player) ? _player->getCurrentFrame() : 0; } int AvatarData::playerFrameNumber() { - if (!_player) { - return 0; - } - if (QThread::currentThread() != thread()) { - int result; - QMetaObject::invokeMethod(this, "playerFrameNumber", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(int, result)); - return result; - } - return _player->getRecording()->getFrameNumber(); + return (_player) ? _player->getRecording()->getFrameNumber() : 0; } void AvatarData::loadRecording(QString filename) { @@ -734,6 +711,20 @@ void AvatarData::play() { } } +void AvatarData::pausePlayer() { + if (!_player) { + return; + } + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "pausePlayer", Qt::BlockingQueuedConnection); + return; + } + if (_player) { + _player->pausePlayer(); + } + +} + void AvatarData::stopPlaying() { if (!_player) { return; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 46061b162b..9b28fdc258 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -296,6 +296,7 @@ public slots: bool hasReferential(); bool isPlaying(); + bool isPaused(); qint64 playerElapsed(); qint64 playerLength(); int playerCurrentFrame(); @@ -312,6 +313,7 @@ public slots: void setPlayerUseHeadModel(bool useHeadModel); void setPlayerUseSkeletonModel(bool useSkeletonModel); void play(); + void pausePlayer(); void stopPlaying(); protected: From af341247af83b210d505bd7a2913c5697106e22f Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 23 Sep 2014 14:29:00 -0700 Subject: [PATCH 12/36] Recorder.js UI + pause and slider bar implementation --- examples/Recorder.js | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/examples/Recorder.js b/examples/Recorder.js index 168f1161c2..46b2a2b70c 100644 --- a/examples/Recorder.js +++ b/examples/Recorder.js @@ -202,8 +202,8 @@ function moveUI() { y: windowDimensions.y - relative.y - ToolBar.SPACING }); - slider.x = relative.x; - slider.y = windowDimensions.y - relative.y - 100; + slider.x = relative.x - ToolBar.SPACING; + slider.y = windowDimensions.y - relative.y - slider.h - ToolBar.SPACING; Overlays.editOverlay(slider.background, { x: slider.x, @@ -237,7 +237,7 @@ function mousePressEvent(event) { } } else if (playIcon === toolBar.clicked(clickedOverlay) && !MyAvatar.isRecording()) { if (MyAvatar.isPlaying()) { - MyAvatar.stopPlaying(); + MyAvatar.pausePlayer(); toolBar.setAlpha(ALPHA_ON, recordIcon); toolBar.setAlpha(ALPHA_ON, saveIcon); toolBar.setAlpha(ALPHA_ON, loadIcon); @@ -252,7 +252,7 @@ function mousePressEvent(event) { } } else if (playLoopIcon === toolBar.clicked(clickedOverlay) && !MyAvatar.isRecording()) { if (MyAvatar.isPlaying()) { - MyAvatar.stopPlaying(); + MyAvatar.pausePlayer(); toolBar.setAlpha(ALPHA_ON, recordIcon); toolBar.setAlpha(ALPHA_ON, saveIcon); toolBar.setAlpha(ALPHA_ON, loadIcon); @@ -283,19 +283,31 @@ function mousePressEvent(event) { toolBar.setAlpha(ALPHA_ON, saveIcon); } } - } else if (slider.x < event.x < slider.x + slider.w && + } else if (MyAvatar.isPlaying() && + slider.x < event.x < slider.x + slider.w && slider.y < event.y < slider.y + slider.h) { - - + isSliding = true; + slider.pos = (event.x - slider.x) / slider.w; + MyAvatar.setPlayerTime(slider.pos * MyAvatar.playerLength()); + } +} +var isSliding = false; + + +function mouseMoveEvent(event) { + if (isSliding) { + slider.pos = (event.x - slider.x) / slider.w; + if (slider.pos < 0.0) { + slider.pos = 0.0; + } else if (slider.pos > 1.0) { + slider.pos = 1.0; + } + MyAvatar.setPlayerTime(slider.pos * MyAvatar.playerLength()); } } -function mouseMoveEvent(event) { - -} - function mouseReleaseEvent(event) { - + isSliding = false; } function update() { From 7ab8c7750cdf84d952bb67760835945a4a996cd1 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 23 Sep 2014 14:37:43 -0700 Subject: [PATCH 13/36] Extra spaces --- libraries/audio/src/AudioInjector.h | 1 - libraries/avatars/src/AvatarData.cpp | 3 +-- libraries/avatars/src/Player.cpp | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 7f52cb3a29..af9b5e55d1 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -40,7 +40,6 @@ private: AudioInjectorOptions _options; bool _shouldStop; int _currentSendPosition; - }; Q_DECLARE_METATYPE(AudioInjector*) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 636096124c..126da654ae 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -625,7 +625,7 @@ int AvatarData::playerCurrentFrame() { } int AvatarData::playerFrameNumber() { - return (_player) ? _player->getRecording()->getFrameNumber() : 0; + return (_player && _player->getRecording()) ? _player->getRecording()->getFrameNumber() : 0; } void AvatarData::loadRecording(QString filename) { @@ -722,7 +722,6 @@ void AvatarData::pausePlayer() { if (_player) { _player->pausePlayer(); } - } void AvatarData::stopPlaying() { diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp index fc1e7269a6..f28b3da547 100644 --- a/libraries/avatars/src/Player.cpp +++ b/libraries/avatars/src/Player.cpp @@ -279,7 +279,6 @@ void Player::setCurrentTime(qint64 currentTime) { (float)(currentTime - _recording->getFrameTimestamp(lowestBound)) / (float)(_recording->getFrameTimestamp(highestBound) - _recording->getFrameTimestamp(lowestBound)); - if (_recording->getFrameTimestamp(bestGuess) <= currentTime) { if (currentTime < _recording->getFrameTimestamp(bestGuess + 1)) { lowestBound = bestGuess; From c14bf84af837a4347d26322fb518418ea917dc5e Mon Sep 17 00:00:00 2001 From: stojce Date: Wed, 24 Sep 2014 00:14:00 +0200 Subject: [PATCH 14/36] layout fix --- interface/resources/images/arrow.svg | 7 +++++++ interface/src/ui/AddressBarDialog.cpp | 7 +++---- libraries/networking/src/AddressManager.cpp | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 interface/resources/images/arrow.svg diff --git a/interface/resources/images/arrow.svg b/interface/resources/images/arrow.svg new file mode 100644 index 0000000000..dc1c36c2b3 --- /dev/null +++ b/interface/resources/images/arrow.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp index 274152742f..04740ae799 100644 --- a/interface/src/ui/AddressBarDialog.cpp +++ b/interface/src/ui/AddressBarDialog.cpp @@ -42,8 +42,8 @@ void AddressBarDialog::setupUI() { QSizePolicy sizePolicyLineEdit(QSizePolicy::Preferred, QSizePolicy::Fixed); sizePolicyLineEdit.setHorizontalStretch(60); addressLineEdit->setSizePolicy(sizePolicyLineEdit); - addressLineEdit->setMinimumSize(QSize(200, 50)); - addressLineEdit->setMaximumSize(QSize(615, 50)); + addressLineEdit->setMinimumSize(QSize(200, 54)); + addressLineEdit->setMaximumSize(QSize(615, 54)); QFont font("Helvetica,Arial,sans-serif", 20); addressLineEdit->setFont(font); addressLineEdit->setStyleSheet("padding: 0 10px;"); @@ -55,8 +55,7 @@ void AddressBarDialog::setupUI() { goButton = new QPushButton(this); goButton->setSizePolicy(sizePolicy); goButton->setMinimumSize(QSize(54, 54)); - goButton->setFont(font); - goButton->setText("➡"); + goButton->setIcon(QIcon(Application::resourcesPath() + "images/arrow.svg")); goButton->setStyleSheet("background: #0e7077; color: #e7eeee; border-radius: 4px;"); goButton->setIconSize(QSize(32, 32)); goButton->setDefault(true); diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 4b3a84c603..9a7da955d6 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -167,7 +167,7 @@ const QString GET_PLACE = "/api/v1/places/%1"; void AddressManager::attemptPlaceNameLookup(const QString& lookupString) { // assume this is a place name and see if we can get any info on it QString placeName = QUrl::toPercentEncoding(lookupString); - AccountManager::getInstance().authenticatedRequest(GET_PLACE.arg(placeName), + AccountManager::getInstance().unauthenticatedRequest(GET_PLACE.arg(placeName), QNetworkAccessManager::GetOperation, apiCallbackParameters()); } @@ -268,7 +268,7 @@ bool AddressManager::handleUsername(const QString& lookupString) { void AddressManager::goToUser(const QString& username) { QString formattedUsername = QUrl::toPercentEncoding(username); // this is a username - pull the captured name and lookup that user's location - AccountManager::getInstance().authenticatedRequest(GET_USER_LOCATION.arg(formattedUsername), + AccountManager::getInstance().unauthenticatedRequest(GET_USER_LOCATION.arg(formattedUsername), QNetworkAccessManager::GetOperation, apiCallbackParameters()); } From 8031454f616cbb4f37c204f3cf54795aefc0b40f Mon Sep 17 00:00:00 2001 From: stojce Date: Wed, 24 Sep 2014 00:22:51 +0200 Subject: [PATCH 15/36] use backslash to activate chat --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6fc4acd905..09f81060f8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -926,7 +926,7 @@ void Application::keyPressEvent(QKeyEvent* event) { Menu::getInstance()->triggerOption(MenuOption::AddressBar); break; - case Qt::Key_CapsLock: + case Qt::Key_Backslash: Menu::getInstance()->triggerOption(MenuOption::Chat); break; From 039deb8b4215cd169b2310fc65b32cbb6a6343b5 Mon Sep 17 00:00:00 2001 From: stojce Date: Wed, 24 Sep 2014 23:26:34 +0200 Subject: [PATCH 16/36] added go button active state icon --- .../images/address-bar-submit-active.svg | 18 ++++++++++++++++++ .../resources/images/address-bar-submit.svg | 18 ++++++++++++++++++ interface/resources/images/arrow.svg | 7 ------- interface/src/ui/AddressBarDialog.cpp | 11 +++++------ 4 files changed, 41 insertions(+), 13 deletions(-) create mode 100644 interface/resources/images/address-bar-submit-active.svg create mode 100644 interface/resources/images/address-bar-submit.svg delete mode 100644 interface/resources/images/arrow.svg diff --git a/interface/resources/images/address-bar-submit-active.svg b/interface/resources/images/address-bar-submit-active.svg new file mode 100644 index 0000000000..313b366033 --- /dev/null +++ b/interface/resources/images/address-bar-submit-active.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + diff --git a/interface/resources/images/address-bar-submit.svg b/interface/resources/images/address-bar-submit.svg new file mode 100644 index 0000000000..df4d7e90f6 --- /dev/null +++ b/interface/resources/images/address-bar-submit.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + diff --git a/interface/resources/images/arrow.svg b/interface/resources/images/arrow.svg deleted file mode 100644 index dc1c36c2b3..0000000000 --- a/interface/resources/images/arrow.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp index 04740ae799..c9ac412aeb 100644 --- a/interface/src/ui/AddressBarDialog.cpp +++ b/interface/src/ui/AddressBarDialog.cpp @@ -54,10 +54,10 @@ void AddressBarDialog::setupUI() { goButton = new QPushButton(this); goButton->setSizePolicy(sizePolicy); - goButton->setMinimumSize(QSize(54, 54)); - goButton->setIcon(QIcon(Application::resourcesPath() + "images/arrow.svg")); - goButton->setStyleSheet("background: #0e7077; color: #e7eeee; border-radius: 4px;"); - goButton->setIconSize(QSize(32, 32)); + goButton->setMinimumSize(QSize(55, 55)); + goButton->setMaximumSize(QSize(55, 55)); + goButton->setIcon(QIcon(Application::resourcesPath() + "images/address-bar-submit.svg")); + goButton->setIconSize(QSize(55, 55)); goButton->setDefault(true); goButton->setFlat(true); addressLayout->addWidget(goButton); @@ -83,8 +83,7 @@ void AddressBarDialog::setupUI() { void AddressBarDialog::accept() { if (!addressLineEdit->text().isEmpty()) { - goButton->setStyleSheet("background: #333; color: #e7eeee; border-radius: 4px;"); - + goButton->setIcon(QIcon(Application::resourcesPath() + "images/address-bar-submit-active.svg")); AddressManager& addressManager = AddressManager::getInstance(); connect(&addressManager, &AddressManager::lookupResultsFinished, this, &QDialog::close); addressManager.handleLookupString(addressLineEdit->text()); From 75dc26c9ffec6ff7cb8b7380bd0031f7f89a3590 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 24 Sep 2014 16:19:47 -0700 Subject: [PATCH 17/36] Slider can moved when recording paused or stoped --- examples/Recorder.js | 16 ++++++---------- libraries/audio/src/AudioInjector.cpp | 2 -- libraries/avatars/src/Player.cpp | 18 ++++++++++++++---- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/examples/Recorder.js b/examples/Recorder.js index 46b2a2b70c..40bf2d2ed1 100644 --- a/examples/Recorder.js +++ b/examples/Recorder.js @@ -159,9 +159,7 @@ function updateTimer() { if (MyAvatar.isRecording()) { slider.pos = 1.0; - } else if (!MyAvatar.isPlaying) { - slider.pos = 0.0; - } else { + } else if (MyAvatar.playerLength() > 0) { slider.pos = MyAvatar.playerElapsed() / MyAvatar.playerLength(); } @@ -283,9 +281,9 @@ function mousePressEvent(event) { toolBar.setAlpha(ALPHA_ON, saveIcon); } } - } else if (MyAvatar.isPlaying() && - slider.x < event.x < slider.x + slider.w && - slider.y < event.y < slider.y + slider.h) { + } else if (MyAvatar.playerLength() > 0 && + slider.x < event.x && event.x < slider.x + slider.w && + slider.y < event.y && event.y < slider.y + slider.h) { isSliding = true; slider.pos = (event.x - slider.x) / slider.w; MyAvatar.setPlayerTime(slider.pos * MyAvatar.playerLength()); @@ -293,14 +291,12 @@ function mousePressEvent(event) { } var isSliding = false; - function mouseMoveEvent(event) { if (isSliding) { slider.pos = (event.x - slider.x) / slider.w; - if (slider.pos < 0.0) { + if (slider.pos < 0.0 || slider.pos > 1.0) { + MyAvatar.stopPlaying(); slider.pos = 0.0; - } else if (slider.pos > 1.0) { - slider.pos = 1.0; } MyAvatar.setPlayerTime(slider.pos * MyAvatar.playerLength()); } diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index f6dd7fa058..c3b40d60b0 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -96,8 +96,6 @@ void AudioInjector::injectAudio() { timer.start(); int nextFrame = 0; - _currentSendPosition = 0; - int numPreAudioDataBytes = injectAudioPacket.size(); bool shouldLoop = _options.getLoop(); diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp index f28b3da547..94696e5ace 100644 --- a/libraries/avatars/src/Player.cpp +++ b/libraries/avatars/src/Player.cpp @@ -257,9 +257,14 @@ void Player::setCurrentFrame(int currentFrame) { _currentFrame = currentFrame; _timerOffset = _recording->getFrameTimestamp(_currentFrame); - _timer.start(); - setAudionInjectorPosition(); + if (isPlaying()) { + _timer.start(); + setAudionInjectorPosition(); + } else { + _isPaused = true; + _pausedFrame = currentFrame; + } } void Player::setCurrentTime(qint64 currentTime) { @@ -298,9 +303,14 @@ void Player::setCurrentTime(qint64 currentTime) { _currentFrame = lowestBound; _timerOffset = _recording->getFrameTimestamp(lowestBound); - _timer.start(); - setAudionInjectorPosition(); + if (isPlaying()) { + _timer.start(); + setAudionInjectorPosition(); + } else { + _isPaused = true; + _pausedFrame = lowestBound; + } } void Player::setAudionInjectorPosition() { From eda168a6d9b42d225491522eea3d317cacc9c6f2 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 24 Sep 2014 19:51:37 -0700 Subject: [PATCH 18/36] first cut at shape collisions --- libraries/entities/src/EntityTreeElement.cpp | 30 ++++++++++++++++++++ libraries/entities/src/EntityTreeElement.h | 2 ++ libraries/octree/src/Octree.cpp | 14 ++++----- libraries/octree/src/OctreeElement.cpp | 8 +++++- libraries/octree/src/OctreeElement.h | 4 +++ 5 files changed, 50 insertions(+), 8 deletions(-) diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index a58dd9065f..bfc0f9af30 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -11,6 +11,9 @@ #include +#include +#include + #include #include @@ -546,6 +549,33 @@ bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float rad return false; } +bool EntityTreeElement::findShapeCollisions(const Shape* shape, CollisionList& collisions) const { + bool atLeastOneCollision = false; + //AACube cube = getAACube(); + //return ShapeCollider::collideShapeWithAACubeLegacy(shape, cube.calcCenter(), cube.getScale(), collisions); + + QList::iterator entityItr = _entityItems->begin(); + QList::const_iterator entityEnd = _entityItems->end(); + while(entityItr != entityEnd) { + EntityItem* entity = (*entityItr); + glm::vec3 entityCenter = entity->getPosition(); + float entityRadius = entity->getRadius(); + + // don't collide with yourself??? + //if (entityCenter == center && entityRadius == radius) { + // return false; + //} + AACube entityAACube = entity->getMinimumAACube(); + AACubeShape aaCube(entityAACube.getScale(), entityAACube.calcCenter()); + + if (ShapeCollider::collideShapes(shape, &aaCube, collisions)) { + atLeastOneCollision = true; + } + ++entityItr; + } + return atLeastOneCollision; +} + void EntityTreeElement::updateEntityItemID(const EntityItemID& creatorTokenEntityID, const EntityItemID& knownIDEntityID) { uint16_t numberOfEntities = _entityItems->size(); for (uint16_t i = 0; i < numberOfEntities; i++) { diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index 5790903411..ab3754749b 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -142,6 +142,8 @@ public: virtual bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, void** penetratedObject) const; + virtual bool findShapeCollisions(const Shape* shape, CollisionList& collisions) const; + const QList& getEntities() const { return *_entityItems; } QList& getEntities() { return *_entityItems; } bool hasEntities() const { return _entityItems ? _entityItems->size() > 0 : false; } diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index df2e40d96f..a834fb887a 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -817,9 +817,6 @@ bool findCapsulePenetrationOp(OctreeElement* element, void* extraData) { if (!box.expandedIntersectsSegment(args->start, args->end, args->radius)) { return false; } - if (!element->isLeaf()) { - return true; // recurse on children - } if (element->hasContent()) { glm::vec3 nodePenetration; if (box.findCapsulePenetration(args->start, args->end, args->radius, nodePenetration)) { @@ -827,6 +824,9 @@ bool findCapsulePenetrationOp(OctreeElement* element, void* extraData) { args->found = true; } } + if (!element->isLeaf()) { + return true; // recurse on children + } return false; } @@ -839,15 +839,15 @@ bool findShapeCollisionsOp(OctreeElement* element, void* extraData) { if (!cube.expandedContains(args->shape->getTranslation(), args->shape->getBoundingRadius())) { return false; } - if (!element->isLeaf()) { - return true; // recurse on children - } if (element->hasContent()) { - if (ShapeCollider::collideShapeWithAACubeLegacy(args->shape, cube.calcCenter(), cube.getScale(), args->collisions)) { + if (element->findShapeCollisions(args->shape, args->collisions)) { args->found = true; return true; } } + if (!element->isLeaf()) { + return true; // recurse on children + } return false; } diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index e641ddece2..61187dea03 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include #include @@ -17,7 +18,8 @@ #include #include -#include +#include +#include #include "AACube.h" #include "OctalCode.h" @@ -1369,6 +1371,10 @@ bool OctreeElement::findSpherePenetration(const glm::vec3& center, float radius, return _cube.findSpherePenetration(center, radius, penetration); } +bool OctreeElement::findShapeCollisions(const Shape* shape, CollisionList& collisions) const { + AACube cube = getAACube(); + return ShapeCollider::collideShapeWithAACubeLegacy(shape, cube.calcCenter(), cube.getScale(), collisions); +} // TODO: consider removing this, or switching to using getOrCreateChildElementContaining(const AACube& box)... OctreeElement* OctreeElement::getOrCreateChildElementAt(float x, float y, float z, float s) { diff --git a/libraries/octree/src/OctreeElement.h b/libraries/octree/src/OctreeElement.h index 093a35720f..31a9dfddc1 100644 --- a/libraries/octree/src/OctreeElement.h +++ b/libraries/octree/src/OctreeElement.h @@ -25,6 +25,7 @@ #include "ViewFrustum.h" #include "OctreeConstants.h" +class CollisionList; class EncodeBitstreamParams; class Octree; class OctreeElement; @@ -32,6 +33,7 @@ class OctreeElementBag; class OctreeElementDeleteHook; class OctreePacketData; class ReadBitstreamToTreeParams; +class Shape; class VoxelSystem; const float SMALLEST_REASONABLE_OCTREE_ELEMENT_SCALE = (1.0f / TREE_SCALE) / 10000.0f; // 1/10,000th of a meter @@ -128,6 +130,8 @@ public: virtual bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, void** penetratedObject) const; + virtual bool findShapeCollisions(const Shape* shape, CollisionList& collisions) const; + // Base class methods you don't need to implement const unsigned char* getOctalCode() const { return (_octcodePointer) ? _octalCode.pointer : &_octalCode.buffer[0]; } OctreeElement* getChildAtIndex(int childIndex) const; From 9cd76983a2f5ffed6f00485de8bab953019418f0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 25 Sep 2014 14:35:20 -0700 Subject: [PATCH 19/36] swith to using shape collider instead of spheres --- .../entities/src/EntityCollisionSystem.cpp | 24 +++- libraries/entities/src/EntityItem.cpp | 15 ++- libraries/entities/src/EntityItem.h | 17 ++- libraries/entities/src/EntityTreeElement.cpp | 22 ++-- libraries/octree/src/Octree.cpp | 1 - libraries/shared/src/AACubeShape.h | 14 +++ libraries/shared/src/CollisionInfo.h | 2 + libraries/shared/src/Shape.h | 24 ++++ libraries/shared/src/ShapeCollider.cpp | 107 ++++++++++++++++++ libraries/shared/src/SphereShape.h | 22 ++++ 10 files changed, 224 insertions(+), 24 deletions(-) diff --git a/libraries/entities/src/EntityCollisionSystem.cpp b/libraries/entities/src/EntityCollisionSystem.cpp index c9040c7a6e..1bba7c4764 100644 --- a/libraries/entities/src/EntityCollisionSystem.cpp +++ b/libraries/entities/src/EntityCollisionSystem.cpp @@ -13,8 +13,10 @@ #include #include #include +#include #include #include +#include #include "EntityItem.h" #include "EntityCollisionSystem.h" @@ -106,8 +108,26 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { glm::vec3 center = entityA->getPosition() * (float)(TREE_SCALE); float radius = entityA->getRadius() * (float)(TREE_SCALE); glm::vec3 penetration; - EntityItem* entityB; - if (_entities->findSpherePenetration(center, radius, penetration, (void**)&entityB, Octree::NoLock)) { + EntityItem* entityB = NULL; + + const float MAX_COLLISIONS_PER_ENTITY = 32; + CollisionList collisions(MAX_COLLISIONS_PER_ENTITY); + bool shapeCollisionsAccurate = false; + bool shapeCollisions = _entities->findShapeCollisions(&entityA->getCollisionShapeInMeters(), + collisions, Octree::NoLock, &shapeCollisionsAccurate); + + if (shapeCollisions) { + for(int i = 0; i < collisions.size(); i++) { + CollisionInfo* collision = collisions[i]; + penetration = collision->_penetration; + entityB = static_cast(collision->_extraData); + + // TODO: how to handle multiple collisions? + break; + } + } + + if (shapeCollisions) { // NOTE: 'penetration' is the depth that 'entityA' overlaps 'entityB'. It points from A into B. glm::vec3 penetrationInTreeUnits = penetration / (float)(TREE_SCALE); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index cfed16c443..84ba0bd45d 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -70,6 +70,8 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) { _angularVelocity = DEFAULT_ANGULAR_VELOCITY; _angularDamping = DEFAULT_ANGULAR_DAMPING; _visible = DEFAULT_VISIBLE; + + recalculateCollisionShape(); } EntityItem::EntityItem(const EntityItemID& entityItemID) { @@ -490,6 +492,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData); + recalculateCollisionShape(); } return bytesRead; } @@ -675,7 +678,7 @@ void EntityItem::update(const quint64& updateTime) { velocity = NO_VELOCITY; } - setPosition(position); + setPosition(position); // this will automatically recalculate our collision shape setVelocity(velocity); if (wantDebug) { @@ -749,7 +752,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties, bool forc } } - SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, setPositionInMeters); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, setPositionInMeters); // this will call recalculate collision shape if needed SET_ENTITY_PROPERTY_FROM_PROPERTIES(dimensions, setDimensionsInMeters); // NOTE: radius is obsolete SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, setRotation); SET_ENTITY_PROPERTY_FROM_PROPERTIES(mass, setMass); @@ -903,3 +906,11 @@ float EntityItem::getRadius() const { return radius; } +void EntityItem::recalculateCollisionShape() { + AACube entityAACube = getMinimumAACube(); + entityAACube.scale(TREE_SCALE); // scale to meters + _collisionShape.setTranslation(entityAACube.calcCenter()); + _collisionShape.setScale(entityAACube.getScale()); +} + + diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index bbfba30e9c..bdd0d572d3 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -16,6 +16,7 @@ #include +#include #include // for Animation, AnimationCache, and AnimationPointer classes #include // for EncodeBitstreamParams class #include // for OctreeElement::AppendState @@ -123,7 +124,9 @@ public: EntityTypes::EntityType getType() const { return _type; } const glm::vec3& getPosition() const { return _position; } /// get position in domain scale units (0.0 - 1.0) glm::vec3 getPositionInMeters() const { return _position * (float) TREE_SCALE; } /// get position in meters - void setPosition(const glm::vec3& value) { _position = value; } /// set position in domain scale units (0.0 - 1.0) + + /// set position in domain scale units (0.0 - 1.0) + void setPosition(const glm::vec3& value) { _position = value; recalculateCollisionShape(); } void setPositionInMeters(const glm::vec3& value) /// set position in meter units (0.0 - TREE_SCALE) { setPosition(glm::clamp(value / (float) TREE_SCALE, 0.0f, 1.0f)); } @@ -137,14 +140,14 @@ public: float getLargestDimension() const { return glm::length(_dimensions); } /// get the largest possible dimension /// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately - void setDimensions(const glm::vec3& value) { _dimensions = value; } + void setDimensions(const glm::vec3& value) { _dimensions = value; ; recalculateCollisionShape(); } /// set dimensions in meter units (0.0 - TREE_SCALE) this will also reset radius appropriately void setDimensionsInMeters(const glm::vec3& value) { setDimensions(value / (float) TREE_SCALE); } static const glm::quat DEFAULT_ROTATION; const glm::quat& getRotation() const { return _rotation; } - void setRotation(const glm::quat& rotation) { _rotation = rotation; } + void setRotation(const glm::quat& rotation) { _rotation = rotation; ; recalculateCollisionShape(); } static const float DEFAULT_GLOW_LEVEL; float getGlowLevel() const { return _glowLevel; } @@ -207,7 +210,10 @@ public: static const glm::vec3 DEFAULT_REGISTRATION_POINT; const glm::vec3& getRegistrationPoint() const { return _registrationPoint; } /// registration point as ratio of entity - void setRegistrationPoint(const glm::vec3& value) { _registrationPoint = glm::clamp(value, 0.0f, 1.0f); } /// registration point as ratio of entity + + /// registration point as ratio of entity + void setRegistrationPoint(const glm::vec3& value) + { _registrationPoint = glm::clamp(value, 0.0f, 1.0f); recalculateCollisionShape(); } static const glm::vec3 NO_ANGULAR_VELOCITY; static const glm::vec3 DEFAULT_ANGULAR_VELOCITY; @@ -229,9 +235,11 @@ public: float getRadius() const; void applyHardCollision(const CollisionInfo& collisionInfo); + virtual const Shape& getCollisionShapeInMeters() const { return _collisionShape; } protected: virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init + virtual void recalculateCollisionShape(); EntityTypes::EntityType _type; QUuid _id; @@ -264,6 +272,7 @@ protected: /// set radius in domain scale units (0.0 - 1.0) this will also reset dimensions to be equal for each axis void setRadius(float value); + AACubeShape _collisionShape; }; diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index bfc0f9af30..6e9ecbbd9a 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -551,25 +551,17 @@ bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float rad bool EntityTreeElement::findShapeCollisions(const Shape* shape, CollisionList& collisions) const { bool atLeastOneCollision = false; - //AACube cube = getAACube(); - //return ShapeCollider::collideShapeWithAACubeLegacy(shape, cube.calcCenter(), cube.getScale(), collisions); - QList::iterator entityItr = _entityItems->begin(); QList::const_iterator entityEnd = _entityItems->end(); while(entityItr != entityEnd) { EntityItem* entity = (*entityItr); - glm::vec3 entityCenter = entity->getPosition(); - float entityRadius = entity->getRadius(); - - // don't collide with yourself??? - //if (entityCenter == center && entityRadius == radius) { - // return false; - //} - AACube entityAACube = entity->getMinimumAACube(); - AACubeShape aaCube(entityAACube.getScale(), entityAACube.calcCenter()); - - if (ShapeCollider::collideShapes(shape, &aaCube, collisions)) { - atLeastOneCollision = true; + const Shape* otherCollisionShape = &entity->getCollisionShapeInMeters(); + if (shape != otherCollisionShape) { + if (ShapeCollider::collideShapes(shape, otherCollisionShape, collisions)) { + CollisionInfo* lastCollision = collisions.getLastCollision(); + lastCollision->_extraData = entity; + atLeastOneCollision = true; + } } ++entityItr; } diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index a834fb887a..ceda91a441 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -832,7 +832,6 @@ bool findCapsulePenetrationOp(OctreeElement* element, void* extraData) { bool findShapeCollisionsOp(OctreeElement* element, void* extraData) { ShapeArgs* args = static_cast(extraData); - // coarse check against bounds AACube cube = element->getAACube(); cube.scale(TREE_SCALE); diff --git a/libraries/shared/src/AACubeShape.h b/libraries/shared/src/AACubeShape.h index 4b834aa1bf..da7ba9d53f 100644 --- a/libraries/shared/src/AACubeShape.h +++ b/libraries/shared/src/AACubeShape.h @@ -12,6 +12,7 @@ #ifndef hifi_AACubeShape_h #define hifi_AACubeShape_h +#include #include "Shape.h" class AACubeShape : public Shape { @@ -28,9 +29,22 @@ public: bool findRayIntersection(RayIntersectionInfo& intersection) const; float getVolume() const { return _scale * _scale * _scale; } + virtual QDebug& dumpToDebug(QDebug& debugConext) const; protected: float _scale; }; +inline QDebug& AACubeShape::dumpToDebug(QDebug& debugConext) const { + debugConext << "AACubeShape[ (" + << "type: " << getType() + << "position: " + << getTranslation().x << ", " << getTranslation().y << ", " << getTranslation().z + << "scale: " + << getScale() + << "]"; + + return debugConext; +} + #endif // hifi_AACubeShape_h diff --git a/libraries/shared/src/CollisionInfo.h b/libraries/shared/src/CollisionInfo.h index 6e70654d15..0f134c1b23 100644 --- a/libraries/shared/src/CollisionInfo.h +++ b/libraries/shared/src/CollisionInfo.h @@ -54,6 +54,8 @@ public: const Shape* _shapeA; // pointer to shapeA in this collision const Shape* _shapeB; // pointer to shapeB in this collision + void* _extraData; // pointer to extraData for this collision, opaque to the collision info, useful for external data + float _damping; // range [0,1] of friction coeficient float _elasticity; // range [0,1] of energy conservation glm::vec3 _contactPoint; // world-frame point on BodyA that is deepest into BodyB diff --git a/libraries/shared/src/Shape.h b/libraries/shared/src/Shape.h index 4b85234eb3..cdf3ba72e5 100644 --- a/libraries/shared/src/Shape.h +++ b/libraries/shared/src/Shape.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -80,6 +81,8 @@ public: virtual float getVolume() const { return 1.0; } virtual void getVerletPoints(QVector& points) {} + + virtual QDebug& dumpToDebug(QDebug& debugConext) const; protected: // these ctors are protected (used by derived classes only) @@ -113,4 +116,25 @@ protected: float _mass; }; +inline QDebug& Shape::dumpToDebug(QDebug& debugConext) const { + debugConext << "Shape[ (" + << "type: " << getType() + << "position: " + << getTranslation().x << ", " << getTranslation().y << ", " << getTranslation().z + << "radius: " + << getBoundingRadius() + << "]"; + + return debugConext; +} + +inline QDebug operator<<(QDebug debug, const Shape& shape) { + return shape.dumpToDebug(debug); +} + +inline QDebug operator<<(QDebug debug, const Shape* shape) { + return shape->dumpToDebug(debug); +} + + #endif // hifi_Shape_h diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index be3b086776..3f79fa081a 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -20,6 +20,8 @@ #include "PlaneShape.h" #include "SphereShape.h" +#include "StreamUtils.h" + // NOTE: // // * Large ListShape's are inefficient keep the lists short. @@ -978,7 +980,112 @@ bool aaCubeVsCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& co return capsuleVsAACube(shapeB, shapeA, collisions); } +// helper function +CollisionInfo* aaCubeVsAACubeHelper(const glm::vec3& cubeCenterA, float cubeSideA, const glm::vec3& cubeCenterB, + float cubeSideB, CollisionList& collisions) { + // cube is A + // cube is B + // BA = B - A = from center of A to center of B + float halfCubeSideA = 0.5f * cubeSideA; + float halfCubeSideB = 0.5f * cubeSideB; + glm::vec3 BA = cubeCenterB - cubeCenterA; + + float distance = glm::length(BA); + + if (distance > EPSILON) { + float maxBA = glm::max(glm::max(glm::abs(BA.x), glm::abs(BA.y)), glm::abs(BA.z)); + if (maxBA > halfCubeSideB + halfCubeSideA) { + // cube misses cube entirely + return NULL; + } + CollisionInfo* collision = collisions.getNewCollision(); + if (!collision) { + return NULL; // no more room for collisions + } + if (maxBA > halfCubeSideB) { + // cube hits cube but its center is outside cube + // compute contact anti-pole on cube (in cube frame) + glm::vec3 cubeContact = glm::abs(BA); + if (cubeContact.x > halfCubeSideB) { + cubeContact.x = halfCubeSideB; + } + if (cubeContact.y > halfCubeSideB) { + cubeContact.y = halfCubeSideB; + } + if (cubeContact.z > halfCubeSideB) { + cubeContact.z = halfCubeSideB; + } + glm::vec3 signs = glm::sign(BA); + cubeContact.x *= signs.x; + cubeContact.y *= signs.y; + cubeContact.z *= signs.z; + + // compute penetration direction + glm::vec3 direction = BA - cubeContact; + + float lengthDirection = glm::length(direction); + + if (lengthDirection < EPSILON) { + // cubeCenterA is touching cube B surface, so we can't use the difference between those two + // points to compute the penetration direction. Instead we use the unitary components of + // cubeContact. + glm::modf(cubeContact / halfCubeSideB, direction); + lengthDirection = glm::length(direction); + } else if (lengthDirection > halfCubeSideA) { + collisions.deleteLastCollision(); + return NULL; + } + direction /= lengthDirection; + + // compute collision details + collision->_contactPoint = cubeCenterA + halfCubeSideA * direction; + collision->_penetration = halfCubeSideA * direction - (BA - cubeContact); + } else { + // cube center is inside cube + // --> push out nearest face + glm::vec3 direction; + BA /= maxBA; + glm::modf(BA, direction); + float lengthDirection = glm::length(direction); + direction /= lengthDirection; + + // compute collision details + collision->_floatData = cubeSideB; + collision->_vecData = cubeCenterB; + collision->_penetration = (halfCubeSideB * lengthDirection + halfCubeSideA - maxBA * glm::dot(BA, direction)) * direction; + collision->_contactPoint = cubeCenterA + halfCubeSideA * direction; + } + collision->_shapeA = NULL; + collision->_shapeB = NULL; + return collision; + } else if (halfCubeSideA + halfCubeSideB > distance) { + // NOTE: for cocentric approximation we collide sphere and cube as two spheres which means + // this algorithm will probably be wrong when both sphere and cube are very small (both ~EPSILON) + CollisionInfo* collision = collisions.getNewCollision(); + if (collision) { + // the penetration and contactPoint are undefined, so we pick a penetration direction (-yAxis) + collision->_penetration = (halfCubeSideA + halfCubeSideB) * glm::vec3(0.0f, -1.0f, 0.0f); + // contactPoint is on surface of A + collision->_contactPoint = cubeCenterA + collision->_penetration; + collision->_shapeA = NULL; + collision->_shapeB = NULL; + return collision; + } + } + return NULL; +} + bool aaCubeVsAACube(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { + // BA = B - A = from center of A to center of B + const AACubeShape* cubeA = static_cast(shapeA); + const AACubeShape* cubeB = static_cast(shapeB); + CollisionInfo* collision = aaCubeVsAACubeHelper( cubeA->getTranslation(), cubeA->getScale(), + cubeB->getTranslation(), cubeB->getScale(), collisions); + if (collision) { + collision->_shapeA = shapeA; + collision->_shapeB = shapeB; + return true; + } return false; } diff --git a/libraries/shared/src/SphereShape.h b/libraries/shared/src/SphereShape.h index b5f2c50d8f..59a53c97d6 100644 --- a/libraries/shared/src/SphereShape.h +++ b/libraries/shared/src/SphereShape.h @@ -39,4 +39,26 @@ public: float getVolume() const { return 1.3333333333f * PI * _boundingRadius * _boundingRadius * _boundingRadius; } }; +inline QDebug operator<<(QDebug debug, const SphereShape& shape) { + debug << "SphereShape[ (" + << "position: " + << shape.getTranslation().x << ", " << shape.getTranslation().y << ", " << shape.getTranslation().z + << "radius: " + << shape.getRadius() + << "]"; + + return debug; +} + +inline QDebug operator<<(QDebug debug, const SphereShape* shape) { + debug << "SphereShape[ (" + << "center: " + << shape->getTranslation().x << ", " << shape->getTranslation().y << ", " << shape->getTranslation().z + << "radius: " + << shape->getRadius() + << "]"; + + return debug; +} + #endif // hifi_SphereShape_h From 2f0a9b517fb7ba9eecabeb50634f08ad2b642bf1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 25 Sep 2014 14:49:52 -0700 Subject: [PATCH 20/36] make sphere entities use sphere shape for better collisions --- libraries/entities/src/EntityCollisionSystem.cpp | 3 +-- libraries/entities/src/SphereEntityItem.cpp | 9 ++++++++- libraries/entities/src/SphereEntityItem.h | 8 +++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/libraries/entities/src/EntityCollisionSystem.cpp b/libraries/entities/src/EntityCollisionSystem.cpp index 1bba7c4764..2a5450e91f 100644 --- a/libraries/entities/src/EntityCollisionSystem.cpp +++ b/libraries/entities/src/EntityCollisionSystem.cpp @@ -105,14 +105,13 @@ void EntityCollisionSystem::updateCollisionWithVoxels(EntityItem* entity) { } void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { - glm::vec3 center = entityA->getPosition() * (float)(TREE_SCALE); - float radius = entityA->getRadius() * (float)(TREE_SCALE); glm::vec3 penetration; EntityItem* entityB = NULL; const float MAX_COLLISIONS_PER_ENTITY = 32; CollisionList collisions(MAX_COLLISIONS_PER_ENTITY); bool shapeCollisionsAccurate = false; + bool shapeCollisions = _entities->findShapeCollisions(&entityA->getCollisionShapeInMeters(), collisions, Octree::NoLock, &shapeCollisionsAccurate); diff --git a/libraries/entities/src/SphereEntityItem.cpp b/libraries/entities/src/SphereEntityItem.cpp index 09364ddbfe..5da218c11a 100644 --- a/libraries/entities/src/SphereEntityItem.cpp +++ b/libraries/entities/src/SphereEntityItem.cpp @@ -88,4 +88,11 @@ void SphereEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBi bool successPropertyFits = true; APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, getColor()); -} \ No newline at end of file +} + +void SphereEntityItem::recalculateCollisionShape() { + _sphereShape.setTranslation(getCenterInMeters()); + glm::vec3 dimensionsInMeters = getDimensionsInMeters(); + float largestDiameter = glm::max(dimensionsInMeters.x, dimensionsInMeters.y, dimensionsInMeters.z); + _sphereShape.setRadius(largestDiameter / 2.0f); +} diff --git a/libraries/entities/src/SphereEntityItem.h b/libraries/entities/src/SphereEntityItem.h index 337f229a69..d57ada760b 100644 --- a/libraries/entities/src/SphereEntityItem.h +++ b/libraries/entities/src/SphereEntityItem.h @@ -12,7 +12,8 @@ #ifndef hifi_SphereEntityItem_h #define hifi_SphereEntityItem_h -#include "EntityItem.h" +#include +#include "EntityItem.h" class SphereEntityItem : public EntityItem { public: @@ -49,9 +50,14 @@ public: _color[GREEN_INDEX] = value.green; _color[BLUE_INDEX] = value.blue; } + + virtual const Shape& getCollisionShapeInMeters() const { return _sphereShape; } protected: + virtual void recalculateCollisionShape(); + rgbColor _color; + SphereShape _sphereShape; }; #endif // hifi_SphereEntityItem_h From c880f00a1441330fa7dfea10ff1c6450936e805c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 25 Sep 2014 14:54:06 -0700 Subject: [PATCH 21/36] make sure to scale default octree shape collision to meters --- libraries/octree/src/OctreeElement.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index 61187dea03..271e885d17 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -1373,6 +1373,7 @@ bool OctreeElement::findSpherePenetration(const glm::vec3& center, float radius, bool OctreeElement::findShapeCollisions(const Shape* shape, CollisionList& collisions) const { AACube cube = getAACube(); + cube.scale(TREE_SCALE); return ShapeCollider::collideShapeWithAACubeLegacy(shape, cube.calcCenter(), cube.getScale(), collisions); } From 5610cfeb34e2c311ae024b9379012510c18960f8 Mon Sep 17 00:00:00 2001 From: stojce Date: Fri, 26 Sep 2014 00:57:56 +0200 Subject: [PATCH 22/36] CR fixes --- interface/src/Menu.cpp | 3 + interface/src/ui/AddressBarDialog.cpp | 134 +++++++++++++++++--------- interface/src/ui/AddressBarDialog.h | 18 ++-- 3 files changed, 99 insertions(+), 56 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index ab6e30b7d9..cd5677e9e5 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1158,6 +1158,9 @@ void Menu::changePrivateKey() { void Menu::toggleAddressBar() { if (!_addressBarDialog) { _addressBarDialog = new AddressBarDialog(); + } + + if (!_addressBarDialog->isVisible()) { _addressBarDialog->show(); } } diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp index c9ac412aeb..25a4299d6c 100644 --- a/interface/src/ui/AddressBarDialog.cpp +++ b/interface/src/ui/AddressBarDialog.cpp @@ -13,79 +13,119 @@ #include "AddressManager.h" #include "Application.h" +const QString ADDRESSBAR_GO_BUTTON_ICON = "images/address-bar-submit.svg"; +const QString ADDRESSBAR_GO_BUTTON_ACTIVE_ICON = "images/address-bar-submit-active.svg"; + AddressBarDialog::AddressBarDialog() : - FramelessDialog(Application::getInstance()->getWindow(), 0, FramelessDialog::POSITION_TOP){ + FramelessDialog(Application::getInstance()->getWindow(), 0, FramelessDialog::POSITION_TOP) { + + setAttribute(Qt::WA_DeleteOnClose, false); setupUI(); } void AddressBarDialog::setupUI() { + const QString DIALOG_STYLESHEET = "font-family: Helvetica, Arial, sans-serif;"; + const QString ADDRESSBAR_PLACEHOLDER = "Go to: domain, @user, #location"; + const QString ADDRESSBAR_STYLESHEET = "padding: 0 10px;"; + const QString ADDRESSBAR_FONT_FAMILY = "Helvetica,Arial,sans-serif"; + const int ADDRESSBAR_FONT_SIZE = 20; + + const int ADDRESSBAR_MIN_WIDTH = 200; + const int ADDRESSBAR_MAX_WIDTH = 615; + const int ADDRESSBAR_HEIGHT = 54; + const int ADDRESSBAR_STRETCH = 60; + + const int BUTTON_SPACER_SIZE = 10; + const int DEFAULT_SPACER_SIZE = 20; + const int ADDRESS_LAYOUT_RIGHT_MARGIN = 10; + + const int GO_BUTTON_SIZE = 55; + const int CLOSE_BUTTON_SIZE = 16; + const QString CLOSE_BUTTON_ICON = "styles/close.svg"; + + const int DIALOG_HEIGHT = 100; + const int DIALOG_INITIAL_WIDTH = 560; + setModal(true); setWindowModality(Qt::WindowModal); setHideOnBlur(false); QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); setSizePolicy(sizePolicy); - setMinimumSize(QSize(560, 100)); - setStyleSheet("font-family: Helvetica, Arial, sans-serif;"); + setMinimumSize(QSize(DIALOG_INITIAL_WIDTH, DIALOG_HEIGHT)); + setStyleSheet(DIALOG_STYLESHEET); - verticalLayout = new QVBoxLayout(this); + _verticalLayout = new QVBoxLayout(this); - addressLayout = new QHBoxLayout(); - addressLayout->setContentsMargins(0, 0, 10, 0); + _addressLayout = new QHBoxLayout(); + _addressLayout->setContentsMargins(0, 0, ADDRESS_LAYOUT_RIGHT_MARGIN, 0); - leftSpacer = new QSpacerItem(20, 20, QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); - addressLayout->addItem(leftSpacer); + _leftSpacer = new QSpacerItem(DEFAULT_SPACER_SIZE, + DEFAULT_SPACER_SIZE, + QSizePolicy::MinimumExpanding, + QSizePolicy::Minimum); - addressLineEdit = new QLineEdit(this); - addressLineEdit->setPlaceholderText("Go to: domain, @user, #location"); + _addressLayout->addItem(_leftSpacer); + + _addressLineEdit = new QLineEdit(this); + _addressLineEdit->setPlaceholderText(ADDRESSBAR_PLACEHOLDER); QSizePolicy sizePolicyLineEdit(QSizePolicy::Preferred, QSizePolicy::Fixed); - sizePolicyLineEdit.setHorizontalStretch(60); - addressLineEdit->setSizePolicy(sizePolicyLineEdit); - addressLineEdit->setMinimumSize(QSize(200, 54)); - addressLineEdit->setMaximumSize(QSize(615, 54)); - QFont font("Helvetica,Arial,sans-serif", 20); - addressLineEdit->setFont(font); - addressLineEdit->setStyleSheet("padding: 0 10px;"); - addressLayout->addWidget(addressLineEdit); + sizePolicyLineEdit.setHorizontalStretch(ADDRESSBAR_STRETCH); + _addressLineEdit->setSizePolicy(sizePolicyLineEdit); + _addressLineEdit->setMinimumSize(QSize(ADDRESSBAR_MIN_WIDTH, ADDRESSBAR_HEIGHT)); + _addressLineEdit->setMaximumSize(QSize(ADDRESSBAR_MAX_WIDTH, ADDRESSBAR_HEIGHT)); + QFont font(ADDRESSBAR_FONT_FAMILY, ADDRESSBAR_FONT_SIZE); + _addressLineEdit->setFont(font); + _addressLineEdit->setStyleSheet(ADDRESSBAR_STYLESHEET); + _addressLayout->addWidget(_addressLineEdit); - buttonSpacer = new QSpacerItem(10, 20, QSizePolicy::Fixed, QSizePolicy::Minimum); - addressLayout->addItem(buttonSpacer); + _buttonSpacer = new QSpacerItem(BUTTON_SPACER_SIZE, BUTTON_SPACER_SIZE, QSizePolicy::Fixed, QSizePolicy::Minimum); + _addressLayout->addItem(_buttonSpacer); - goButton = new QPushButton(this); - goButton->setSizePolicy(sizePolicy); - goButton->setMinimumSize(QSize(55, 55)); - goButton->setMaximumSize(QSize(55, 55)); - goButton->setIcon(QIcon(Application::resourcesPath() + "images/address-bar-submit.svg")); - goButton->setIconSize(QSize(55, 55)); - goButton->setDefault(true); - goButton->setFlat(true); - addressLayout->addWidget(goButton); + _goButton = new QPushButton(this); + _goButton->setSizePolicy(sizePolicy); + _goButton->setMinimumSize(QSize(GO_BUTTON_SIZE, GO_BUTTON_SIZE)); + _goButton->setMaximumSize(QSize(GO_BUTTON_SIZE, GO_BUTTON_SIZE)); + _goButton->setIcon(QIcon(Application::resourcesPath() + ADDRESSBAR_GO_BUTTON_ICON)); + _goButton->setIconSize(QSize(GO_BUTTON_SIZE, GO_BUTTON_SIZE)); + _goButton->setDefault(true); + _goButton->setFlat(true); + _addressLayout->addWidget(_goButton); - rightSpacer = new QSpacerItem(20, 20, QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); - addressLayout->addItem(rightSpacer); + _rightSpacer = new QSpacerItem(DEFAULT_SPACER_SIZE, + DEFAULT_SPACER_SIZE, + QSizePolicy::MinimumExpanding, + QSizePolicy::Minimum); - closeButton = new QPushButton(this); - closeButton->setSizePolicy(sizePolicy); - closeButton->setMinimumSize(QSize(16, 16)); - closeButton->setMaximumSize(QSize(16, 16)); - closeButton->setStyleSheet("color: #333"); - QIcon icon(Application::resourcesPath() + "styles/close.svg"); - closeButton->setIcon(icon); - closeButton->setFlat(true); - addressLayout->addWidget(closeButton, 0, Qt::AlignRight); + _addressLayout->addItem(_rightSpacer); - verticalLayout->addLayout(addressLayout); + _closeButton = new QPushButton(this); + _closeButton->setSizePolicy(sizePolicy); + _closeButton->setMinimumSize(QSize(CLOSE_BUTTON_SIZE, CLOSE_BUTTON_SIZE)); + _closeButton->setMaximumSize(QSize(CLOSE_BUTTON_SIZE, CLOSE_BUTTON_SIZE)); + QIcon icon(Application::resourcesPath() + CLOSE_BUTTON_ICON); + _closeButton->setIcon(icon); + _closeButton->setFlat(true); + _addressLayout->addWidget(_closeButton, 0, Qt::AlignRight); - connect(goButton, &QPushButton::clicked, this, &AddressBarDialog::accept); - connect(closeButton, &QPushButton::clicked, this, &QDialog::close); + _verticalLayout->addLayout(_addressLayout); + + connect(_goButton, &QPushButton::clicked, this, &AddressBarDialog::accept); + connect(_closeButton, &QPushButton::clicked, this, &QDialog::close); +} + +void AddressBarDialog::showEvent(QShowEvent* event) { + _goButton->setIcon(QIcon(Application::resourcesPath() + ADDRESSBAR_GO_BUTTON_ICON)); + _addressLineEdit->setText(QString()); + FramelessDialog::showEvent(event); } void AddressBarDialog::accept() { - if (!addressLineEdit->text().isEmpty()) { - goButton->setIcon(QIcon(Application::resourcesPath() + "images/address-bar-submit-active.svg")); + if (!_addressLineEdit->text().isEmpty()) { + _goButton->setIcon(QIcon(Application::resourcesPath() + ADDRESSBAR_GO_BUTTON_ACTIVE_ICON)); AddressManager& addressManager = AddressManager::getInstance(); - connect(&addressManager, &AddressManager::lookupResultsFinished, this, &QDialog::close); - addressManager.handleLookupString(addressLineEdit->text()); + connect(&addressManager, &AddressManager::lookupResultsFinished, this, &QDialog::hide); + addressManager.handleLookupString(_addressLineEdit->text()); } } \ No newline at end of file diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h index 0f77250097..8f2cf2d7b8 100644 --- a/interface/src/ui/AddressBarDialog.h +++ b/interface/src/ui/AddressBarDialog.h @@ -19,7 +19,6 @@ #include #include - class AddressBarDialog : public FramelessDialog { Q_OBJECT @@ -28,15 +27,16 @@ public: private: void setupUI(); + void showEvent(QShowEvent* event); - QVBoxLayout *verticalLayout; - QHBoxLayout *addressLayout; - QSpacerItem *leftSpacer; - QLineEdit *addressLineEdit; - QSpacerItem *buttonSpacer; - QPushButton *goButton; - QSpacerItem *rightSpacer; - QPushButton *closeButton; + QVBoxLayout *_verticalLayout; + QHBoxLayout *_addressLayout; + QSpacerItem *_leftSpacer; + QSpacerItem *_rightSpacer; + QSpacerItem *_buttonSpacer; + QPushButton *_goButton; + QPushButton *_closeButton; + QLineEdit *_addressLineEdit; private slots: void accept(); From fa8c5b85ff20fc84852b49802276c6470e69f60d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 25 Sep 2014 17:00:35 -0700 Subject: [PATCH 23/36] allow you to edit mass --- examples/editModels.js | 4 ++++ libraries/entities/src/EntityItemProperties.cpp | 1 + 2 files changed, 5 insertions(+) diff --git a/examples/editModels.js b/examples/editModels.js index 740c992888..356b228380 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -2449,6 +2449,7 @@ function Tooltip() { text += "Lifetime: " + properties.lifetime + "\n" } text += "Age: " + properties.ageAsText + "\n" + text += "Mass: " + properties.mass + "\n" text += "Script: " + properties.script + "\n" @@ -2906,6 +2907,8 @@ function handeMenuEvent(menuItem) { index++; array.push({ label: "Linear Damping:", value: properties.damping.toFixed(decimals) }); index++; + array.push({ label: "Mass:", value: properties.mass.toFixed(decimals) }); + index++; array.push({ label: "Angular Pitch:", value: properties.angularVelocity.x.toFixed(decimals) }); index++; array.push({ label: "Angular Yaw:", value: properties.angularVelocity.y.toFixed(decimals) }); @@ -3052,6 +3055,7 @@ Window.nonBlockingFormClosed.connect(function() { properties.velocity.y = array[index++].value; properties.velocity.z = array[index++].value; properties.damping = array[index++].value; + properties.mass = array[index++].value; properties.angularVelocity.x = array[index++].value; properties.angularVelocity.y = array[index++].value; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 7cc5ebb545..207ed376d8 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -134,6 +134,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(velocity); COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(gravity); COPY_PROPERTY_TO_QSCRIPTVALUE(damping); + COPY_PROPERTY_TO_QSCRIPTVALUE(mass); COPY_PROPERTY_TO_QSCRIPTVALUE(lifetime); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(age, getAge()); // gettable, but not settable COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(ageAsText, formatSecondsElapsed(getAge())); // gettable, but not settable From 7dc07a1c51cdc2f51899d30376cb51d1078928b4 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 25 Sep 2014 17:54:11 -0700 Subject: [PATCH 24/36] Compute correct number of bytes to send when stereo --- libraries/audio/src/AudioInjector.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 17b082f07a..fe97594803 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -104,7 +104,7 @@ void AudioInjector::injectAudio() { quint16 outgoingInjectedAudioSequenceNumber = 0; while (currentSendPosition < soundByteArray.size() && !_shouldStop) { - int bytesToCopy = std::min(NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL, + int bytesToCopy = std::min(((_options.isStereo()) ? 2 : 1) * NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL, soundByteArray.size() - currentSendPosition); memcpy(injectAudioPacket.data() + positionOptionOffset, &_options.getPosition(), From 7f4ece2be131c46d13f977b2d23b3634e0dd4009 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 25 Sep 2014 17:54:53 -0700 Subject: [PATCH 25/36] Resize ringbuffer of injected stereo stream --- libraries/audio/src/InjectedAudioStream.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/audio/src/InjectedAudioStream.cpp b/libraries/audio/src/InjectedAudioStream.cpp index 84d33c53e3..d1bbc71898 100644 --- a/libraries/audio/src/InjectedAudioStream.cpp +++ b/libraries/audio/src/InjectedAudioStream.cpp @@ -38,6 +38,7 @@ int InjectedAudioStream::parseStreamProperties(PacketType type, const QByteArray packetStream.skipRawData(NUM_BYTES_RFC4122_UUID); packetStream >> _isStereo; + _ringBuffer.resizeForFrameSize(isStereo() ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); // pull the loopback flag and set our boolean uchar shouldLoopback; From 453869c8e99686f8723c38e80f1ff70a2f5467c6 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 25 Sep 2014 18:11:48 -0700 Subject: [PATCH 26/36] Stereo flag + specific resample in Sound class --- .../audio/src/AudioScriptingInterface.cpp | 3 +++ libraries/audio/src/Sound.cpp | 25 +++++++++++++------ libraries/audio/src/Sound.h | 4 ++- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/libraries/audio/src/AudioScriptingInterface.cpp b/libraries/audio/src/AudioScriptingInterface.cpp index fa0d3a9565..43c7d35c1d 100644 --- a/libraries/audio/src/AudioScriptingInterface.cpp +++ b/libraries/audio/src/AudioScriptingInterface.cpp @@ -13,6 +13,9 @@ AudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions* injectorOptions) { + if (sound->isStereo()) { + const_cast(injectorOptions)->setIsStereo(true); + } AudioInjector* injector = new AudioInjector(sound, *injectorOptions); QThread* injectorThread = new QThread(); diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 9edb04aa2c..3ac433ca31 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -31,7 +31,8 @@ // procedural audio version of Sound Sound::Sound(float volume, float frequency, float duration, float decay, QObject* parent) : - QObject(parent) + QObject(parent), + _isStereo(false) { static char monoAudioData[MAX_PACKET_SIZE]; static int16_t* monoAudioSamples = (int16_t*)(monoAudioData); @@ -69,8 +70,9 @@ Sound::Sound(float volume, float frequency, float duration, float decay, QObject } } -Sound::Sound(const QUrl& sampleURL, QObject* parent) : +Sound::Sound(const QUrl& sampleURL, bool isStereo, QObject* parent) : QObject(parent), + _isStereo(isStereo), _hasDownloaded(false) { // assume we have a QApplication or QCoreApplication instance and use the @@ -88,6 +90,7 @@ Sound::Sound(const QUrl& sampleURL, QObject* parent) : Sound::Sound(const QByteArray byteArray, QObject* parent) : QObject(parent), _byteArray(byteArray), + _isStereo(false), _hasDownloaded(true) { } @@ -149,11 +152,19 @@ void Sound::downSample(const QByteArray& rawAudioByteArray) { int16_t* sourceSamples = (int16_t*) rawAudioByteArray.data(); int16_t* destinationSamples = (int16_t*) _byteArray.data(); - for (int i = 1; i < numSourceSamples; i += 2) { - if (i + 1 >= numSourceSamples) { - destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] / 2) + (sourceSamples[i] / 2); - } else { - destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] / 4) + (sourceSamples[i] / 2) + (sourceSamples[i + 1] / 4); + + if (_isStereo) { + for (int i = 0; i < numSourceSamples; i += 4) { + destinationSamples[i / 2] = (sourceSamples[i] / 2) + (sourceSamples[i + 2] / 2); + destinationSamples[(i / 2) + 1] = (sourceSamples[i + 1] / 2) + (sourceSamples[i + 3] / 2); + } + } else { + for (int i = 1; i < numSourceSamples; i += 2) { + if (i + 1 >= numSourceSamples) { + destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] / 2) + (sourceSamples[i] / 2); + } else { + destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] / 4) + (sourceSamples[i] / 2) + (sourceSamples[i + 1] / 4); + } } } } diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h index fa2dd97903..b8fdc6b458 100644 --- a/libraries/audio/src/Sound.h +++ b/libraries/audio/src/Sound.h @@ -20,17 +20,19 @@ class Sound : public QObject { Q_PROPERTY(bool downloaded READ hasDownloaded) public: - Sound(const QUrl& sampleURL, QObject* parent = NULL); + Sound(const QUrl& sampleURL, bool isStereo = false, QObject* parent = NULL); Sound(float volume, float frequency, float duration, float decay, QObject* parent = NULL); Sound(const QByteArray byteArray, QObject* parent = NULL); void append(const QByteArray byteArray); + bool isStereo() const { return _isStereo; } bool hasDownloaded() const { return _hasDownloaded; } const QByteArray& getByteArray() { return _byteArray; } private: QByteArray _byteArray; + bool _isStereo; bool _hasDownloaded; void trimFrames(); From 11a5358a6eea8fbec449bed03fc5bb6a982a89da Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 25 Sep 2014 18:12:30 -0700 Subject: [PATCH 27/36] Adapted radio.js to new funciton calls --- examples/radio.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/radio.js b/examples/radio.js index 83e81e7e02..c7a6fdcc97 100644 --- a/examples/radio.js +++ b/examples/radio.js @@ -10,7 +10,7 @@ // -var modelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/models/entities/radio/Speakers2Finished.fbx"; +var modelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/models/entities/radio/Speakers2_DeletedPlanes.fbx"; var soundURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/FamilyStereo.raw"; var AudioRotationOffset = Quat.fromPitchYawRollDegrees(0, -90, 0); @@ -20,7 +20,7 @@ audioOptions.loop = true; audioOptions.isStereo = true; var injector = null; -var sound = new Sound(soundURL); +var sound = new Sound(soundURL, audioOptions.isStereo); var entity = null; var properties = null; @@ -38,7 +38,7 @@ function update() { type: "Model", position: position, rotation: rotation, - dimensions: { x: 0.5, y: 0.5, z: 0.5 }, + dimensions: { x: 0.2, y: 0.2, z: 0.2 }, modelURL: modelURL }); properties = Entities.getEntityProperties(entity); From 3baa992bc3b17c1615f38a9e855d0fb2a11d578c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 25 Sep 2014 18:56:37 -0700 Subject: [PATCH 28/36] Fixed radio model --- examples/radio.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/radio.js b/examples/radio.js index c7a6fdcc97..575d9d70c8 100644 --- a/examples/radio.js +++ b/examples/radio.js @@ -10,7 +10,7 @@ // -var modelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/models/entities/radio/Speakers2_DeletedPlanes.fbx"; +var modelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/models/entities/radio/Speakers.fbx"; var soundURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/FamilyStereo.raw"; var AudioRotationOffset = Quat.fromPitchYawRollDegrees(0, -90, 0); @@ -31,14 +31,16 @@ function update() { print("Sound file downloaded"); var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, - { x: 0, y: 0.3, z: -1 })); + { x: 0, y: -0.3, z: -2 })); var rotation = Quat.multiply(MyAvatar.orientation, Quat.fromPitchYawRollDegrees(0, -90, 0)); entity = Entities.addEntity({ type: "Model", position: position, rotation: rotation, - dimensions: { x: 0.2, y: 0.2, z: 0.2 }, + dimensions: { x: 0.391, + y: 1.000, + z: 1.701 }, modelURL: modelURL }); properties = Entities.getEntityProperties(entity); From f50b42fd16eb68be60a8e0a55fa306a4cf96df90 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 25 Sep 2014 19:15:16 -0700 Subject: [PATCH 29/36] Read new Sound() arg --- libraries/script-engine/src/ScriptEngine.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 545e4709f9..47e0ced6bf 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -48,7 +48,8 @@ EntityScriptingInterface ScriptEngine::_entityScriptingInterface; static QScriptValue soundConstructor(QScriptContext* context, QScriptEngine* engine) { QUrl soundURL = QUrl(context->argument(0).toString()); - QScriptValue soundScriptValue = engine->newQObject(new Sound(soundURL), QScriptEngine::ScriptOwnership); + bool isStereo = context->argument(1).toBool(); + QScriptValue soundScriptValue = engine->newQObject(new Sound(soundURL, isStereo), QScriptEngine::ScriptOwnership); return soundScriptValue; } From 72f5b9b77b752d8abf8d6c8d8d6a55a350183128 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 25 Sep 2014 19:19:03 -0700 Subject: [PATCH 30/36] code cleanup --- libraries/audio/src/AudioInjector.cpp | 12 ++++++++---- libraries/audio/src/InjectedAudioStream.cpp | 7 +++++-- libraries/audio/src/Sound.cpp | 6 ++++-- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index fe97594803..4facc03e1e 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -77,11 +77,13 @@ void AudioInjector::injectAudio() { // pack the position for injected audio int positionOptionOffset = injectAudioPacket.size(); - packetStream.writeRawData(reinterpret_cast(&_options.getPosition()), sizeof(_options.getPosition())); + packetStream.writeRawData(reinterpret_cast(&_options.getPosition()), + sizeof(_options.getPosition())); // pack our orientation for injected audio int orientationOptionOffset = injectAudioPacket.size(); - packetStream.writeRawData(reinterpret_cast(&_options.getOrientation()), sizeof(_options.getOrientation())); + packetStream.writeRawData(reinterpret_cast(&_options.getOrientation()), + sizeof(_options.getOrientation())); // pack zero for radius float radius = 0; @@ -117,10 +119,12 @@ void AudioInjector::injectAudio() { injectAudioPacket.resize(numPreAudioDataBytes + bytesToCopy); // pack the sequence number - memcpy(injectAudioPacket.data() + numPreSequenceNumberBytes, &outgoingInjectedAudioSequenceNumber, sizeof(quint16)); + memcpy(injectAudioPacket.data() + numPreSequenceNumberBytes, + &outgoingInjectedAudioSequenceNumber, sizeof(quint16)); // copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet - memcpy(injectAudioPacket.data() + numPreAudioDataBytes, soundByteArray.data() + currentSendPosition, bytesToCopy); + memcpy(injectAudioPacket.data() + numPreAudioDataBytes, + soundByteArray.data() + currentSendPosition, bytesToCopy); // grab our audio mixer from the NodeList, if it exists NodeList* nodeList = NodeList::getInstance(); diff --git a/libraries/audio/src/InjectedAudioStream.cpp b/libraries/audio/src/InjectedAudioStream.cpp index d1bbc71898..762a40003a 100644 --- a/libraries/audio/src/InjectedAudioStream.cpp +++ b/libraries/audio/src/InjectedAudioStream.cpp @@ -30,7 +30,9 @@ InjectedAudioStream::InjectedAudioStream(const QUuid& streamIdentifier, const In const uchar MAX_INJECTOR_VOLUME = 255; -int InjectedAudioStream::parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) { +int InjectedAudioStream::parseStreamProperties(PacketType type, + const QByteArray& packetAfterSeqNum, + int& numAudioSamples) { // setup a data stream to read from this packet QDataStream packetStream(packetAfterSeqNum); @@ -38,7 +40,8 @@ int InjectedAudioStream::parseStreamProperties(PacketType type, const QByteArray packetStream.skipRawData(NUM_BYTES_RFC4122_UUID); packetStream >> _isStereo; - _ringBuffer.resizeForFrameSize(isStereo() ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); + _ringBuffer.resizeForFrameSize(isStereo() ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : + NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); // pull the loopback flag and set our boolean uchar shouldLoopback; diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 3ac433ca31..6fa002a664 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -84,7 +84,8 @@ Sound::Sound(const QUrl& sampleURL, bool isStereo, QObject* parent) : QNetworkReply* soundDownload = networkAccessManager.get(QNetworkRequest(sampleURL)); connect(soundDownload, &QNetworkReply::finished, this, &Sound::replyFinished); - connect(soundDownload, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(replyError(QNetworkReply::NetworkError))); + connect(soundDownload, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(replyError(QNetworkReply::NetworkError))); } Sound::Sound(const QByteArray byteArray, QObject* parent) : @@ -163,7 +164,8 @@ void Sound::downSample(const QByteArray& rawAudioByteArray) { if (i + 1 >= numSourceSamples) { destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] / 2) + (sourceSamples[i] / 2); } else { - destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] / 4) + (sourceSamples[i] / 2) + (sourceSamples[i + 1] / 4); + destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] / 4) + (sourceSamples[i] / 2) + + (sourceSamples[i + 1] / 4); } } } From cf453a8953c03715aa7d6cd29b571072b940fd7a Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 25 Sep 2014 19:32:57 -0700 Subject: [PATCH 31/36] removed _isPaused --- libraries/avatars/src/Player.cpp | 17 +++++++---------- libraries/avatars/src/Player.h | 4 +--- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp index 94696e5ace..9732ccd780 100644 --- a/libraries/avatars/src/Player.cpp +++ b/libraries/avatars/src/Player.cpp @@ -19,10 +19,10 @@ Player::Player(AvatarData* avatar) : _recording(new Recording()), + _pausedFrame(-1), _timerOffset(0), _avatar(avatar), _audioThread(NULL), - _isPaused(false), _playFromCurrentPosition(true), _loop(false), _useAttachments(true), @@ -40,7 +40,7 @@ bool Player::isPlaying() const { } bool Player::isPaused() const { - return _isPaused; + return (_pausedFrame != -1); } qint64 Player::elapsed() const { @@ -58,7 +58,7 @@ void Player::startPlaying() { return; } - if (!_isPaused) { + if (!isPaused()) { _currentContext.globalTimestamp = usecTimestampNow(); _currentContext.domain = NodeList::getInstance()->getDomainHandler().getHostname(); _currentContext.position = _avatar->getPosition(); @@ -118,9 +118,9 @@ void Player::startPlaying() { qDebug() << "Recorder::startPlaying(): Unpause"; setupAudioThread(); _timer.start(); - _isPaused = false; setCurrentFrame(_pausedFrame); + _pausedFrame = -1; } } @@ -128,7 +128,7 @@ void Player::stopPlaying() { if (!isPlaying()) { return; } - _isPaused = false; + _pausedFrame = -1; _timer.invalidate(); cleanupAudioThread(); _avatar->clearJointsData(); @@ -157,7 +157,6 @@ void Player::pausePlayer() { _timer.invalidate(); cleanupAudioThread(); - _isPaused = true; _pausedFrame = _currentFrame; qDebug() << "Recorder::pausePlayer()"; } @@ -200,12 +199,12 @@ void Player::loadFromFile(const QString& file) { } readRecordingFromFile(_recording, file); - _isPaused = false; + _pausedFrame = -1; } void Player::loadRecording(RecordingPointer recording) { _recording = recording; - _isPaused = false; + _pausedFrame = -1; } void Player::play() { @@ -262,7 +261,6 @@ void Player::setCurrentFrame(int currentFrame) { _timer.start(); setAudionInjectorPosition(); } else { - _isPaused = true; _pausedFrame = currentFrame; } } @@ -308,7 +306,6 @@ void Player::setCurrentTime(qint64 currentTime) { _timer.start(); setAudionInjectorPosition(); } else { - _isPaused = true; _pausedFrame = lowestBound; } } diff --git a/libraries/avatars/src/Player.h b/libraries/avatars/src/Player.h index 5746279bac..51e120917c 100644 --- a/libraries/avatars/src/Player.h +++ b/libraries/avatars/src/Player.h @@ -64,6 +64,7 @@ private: QElapsedTimer _timer; RecordingPointer _recording; int _currentFrame; + int _pausedFrame; qint64 _timerOffset; QSharedPointer _injector; @@ -72,9 +73,6 @@ private: AvatarData* _avatar; QThread* _audioThread; - bool _isPaused; - int _pausedFrame; - RecordingContext _currentContext; bool _playFromCurrentPosition; bool _loop; From aa9511c263bbb8cd046392fce30589ede794788d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 25 Sep 2014 22:49:12 -0700 Subject: [PATCH 32/36] Position Rift camera at avatar model's mid-eye location --- interface/src/Application.cpp | 2 +- interface/src/avatar/Avatar.cpp | 4 +++ interface/src/avatar/Avatar.h | 1 + interface/src/avatar/MyAvatar.cpp | 6 ++++- interface/src/avatar/MyAvatar.h | 1 + interface/src/avatar/SkeletonModel.cpp | 34 ++++++++++++++++++-------- interface/src/avatar/SkeletonModel.h | 8 +++--- interface/src/renderer/Model.cpp | 8 ++++++ interface/src/renderer/Model.h | 5 ++++ 9 files changed, 54 insertions(+), 15 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 00cf3b9d01..03743c5f69 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -682,7 +682,7 @@ void Application::paintGL() { if (whichCamera.getMode() == CAMERA_MODE_MIRROR) { OculusManager::display(whichCamera.getRotation(), whichCamera.getPosition(), whichCamera); } else { - OculusManager::display(_myAvatar->getWorldAlignedOrientation(), _myAvatar->getUprightHeadPosition(), whichCamera); + OculusManager::display(_myAvatar->getWorldAlignedOrientation(), _myAvatar->getDefaultEyePosition(), whichCamera); } } else if (TV3DManager::isConnected()) { diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 22e7697b40..6f421965db 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1028,6 +1028,10 @@ float Avatar::getPelvisFloatingHeight() const { return -_skeletonModel.getBindExtents().minimum.y; } +float Avatar::getPelvisToHeadLength() const { + return glm::distance(_position, getHead()->getPosition()); +} + void Avatar::setShowDisplayName(bool showDisplayName) { if (!Menu::getInstance()->isOptionChecked(MenuOption::NamesAboveHeads)) { _displayNameAlpha = 0.0f; diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 58add5aecd..c449a0d1b9 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -223,6 +223,7 @@ protected: float getSkeletonHeight() const; float getHeadHeight() const; float getPelvisFloatingHeight() const; + float getPelvisToHeadLength() const; glm::vec3 getDisplayNamePosition(); void renderDisplayName(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e59de3628a..2f6b728a24 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -977,7 +977,11 @@ void MyAvatar::clearLookAtTargetAvatar() { } glm::vec3 MyAvatar::getUprightHeadPosition() const { - return _position + getWorldAlignedOrientation() * _skeletonModel.getDefaultHeadModelPosition(); + return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, getPelvisToHeadLength(), 0.0f); +} + +glm::vec3 MyAvatar::getDefaultEyePosition() const { + return _position + getWorldAlignedOrientation() * _skeletonModel.getDefaultEyeModelPosition(); } const float SCRIPT_PRIORITY = DEFAULT_PRIORITY + 1.0f; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index d553d4f367..249e1f91b0 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -66,6 +66,7 @@ public: const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; } glm::vec3 getGravity() const { return _gravity; } glm::vec3 getUprightHeadPosition() const; + glm::vec3 getDefaultEyePosition() const; bool getShouldRenderLocally() const { return _shouldRender; } const QList& getAnimationHandles() const { return _animationHandles; } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 9522e54e26..ca60ff8ac0 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -470,23 +470,23 @@ bool SkeletonModel::getNeckParentRotationFromDefaultOrientation(glm::quat& neckP return success; } -bool SkeletonModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const { +bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const { if (!isActive()) { return false; - } + } const FBXGeometry& geometry = _geometry->getFBXGeometry(); - if (getJointPositionInWorldFrame(geometry.leftEyeJointIndex, firstEyePosition) && - getJointPositionInWorldFrame(geometry.rightEyeJointIndex, secondEyePosition)) { + if (getJointPosition(geometry.leftEyeJointIndex, firstEyePosition) && + getJointPosition(geometry.rightEyeJointIndex, secondEyePosition)) { return true; } // no eye joints; try to estimate based on head/neck joints glm::vec3 neckPosition, headPosition; - if (getJointPositionInWorldFrame(geometry.neckJointIndex, neckPosition) && - getJointPositionInWorldFrame(geometry.headJointIndex, headPosition)) { + if (getJointPosition(geometry.neckJointIndex, neckPosition) && + getJointPosition(geometry.headJointIndex, headPosition)) { const float EYE_PROPORTION = 0.6f; glm::vec3 baseEyePosition = glm::mix(neckPosition, headPosition, EYE_PROPORTION); glm::quat headRotation; - getJointRotationInWorldFrame(geometry.headJointIndex, headRotation); + getJointRotation(geometry.headJointIndex, headRotation); const float EYES_FORWARD = 0.25f; const float EYE_SEPARATION = 0.1f; float headHeight = glm::distance(neckPosition, headPosition); @@ -497,6 +497,15 @@ bool SkeletonModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& seco return false; } +bool SkeletonModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const { + if (getEyeModelPositions(firstEyePosition, secondEyePosition)) { + firstEyePosition = _translation + _rotation * firstEyePosition; + secondEyePosition = _translation + _rotation * secondEyePosition; + return true; + } + return false; +} + void SkeletonModel::renderRagdoll() { if (!_ragdoll) { return; @@ -658,13 +667,18 @@ void SkeletonModel::buildShapes() { int headJointIndex = _geometry->getFBXGeometry().headJointIndex; if (0 <= headJointIndex && headJointIndex < _jointStates.size()) { + glm::vec3 leftEyePosition, rightEyePosition; + getEyeModelPositions(leftEyePosition, rightEyePosition); + glm::vec3 midEyePosition = (leftEyePosition + rightEyePosition) / 2.f; + int rootJointIndex = _geometry->getFBXGeometry().rootJointIndex; glm::vec3 rootModelPosition; - getJointPosition(headJointIndex, _defaultHeadModelPosition); getJointPosition(rootJointIndex, rootModelPosition); - _defaultHeadModelPosition = _defaultHeadModelPosition - rootModelPosition; + + _defaultEyeModelPosition = midEyePosition - rootModelPosition; + _defaultEyeModelPosition.z = -_defaultEyeModelPosition.z; } else { - _defaultHeadModelPosition = glm::vec3(0.f, 0.f, 0.f); + _defaultEyeModelPosition = glm::vec3(0.f, 0.f, 0.f); } // While the shapes are in their default position we disable collisions between diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 036811a926..35122d5e18 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -97,9 +97,9 @@ public: /// \return whether or not both eye meshes were found bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; - /// Gets the default position of the head in model frame coordinates. + /// Gets the default position of the mid eye point in model frame coordinates. /// \return whether or not the head was found. - glm::vec3 getDefaultHeadModelPosition() const { return _defaultHeadModelPosition; } + glm::vec3 getDefaultEyeModelPosition() const { return _defaultEyeModelPosition; } virtual void updateVisibleJointStates(); @@ -144,6 +144,8 @@ private: /// \param position position of joint in model-frame /// \param rotation rotation of joint in model-frame void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation); + + bool getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; Avatar* _owningAvatar; @@ -151,7 +153,7 @@ private: glm::vec3 _boundingShapeLocalOffset; SkeletonRagdoll* _ragdoll; - glm::vec3 _defaultHeadModelPosition; + glm::vec3 _defaultEyeModelPosition; }; #endif // hifi_SkeletonModel_h diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 19d711a69d..e4ab8b24d9 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -768,6 +768,14 @@ bool Model::getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) co return true; } +bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + rotation = _jointStates[jointIndex].getRotation(); + return true; +} + bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const { if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return false; diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 63b2058a20..2361133878 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -153,6 +153,11 @@ public: /// \return true if joint exists bool getJointPosition(int jointIndex, glm::vec3& position) const; + /// \param jointIndex index of joint in model structure + /// \param rotation[out] rotation of joint in model-frame + /// \return true if joint exists + bool getJointRotation(int jointIndex, glm::quat& rotation) const; + QStringList getJointNames() const; AnimationHandlePointer createAnimationHandle(); From c0419c61f437cc503a459d9fbdd735e6f5edf036 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 26 Sep 2014 09:15:17 -0700 Subject: [PATCH 33/36] Fix missing constructor initialization --- interface/src/avatar/SkeletonModel.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index ca60ff8ac0..1af5cfc893 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -27,7 +27,8 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : _owningAvatar(owningAvatar), _boundingShape(), _boundingShapeLocalOffset(0.0f), - _ragdoll(NULL) { + _ragdoll(NULL), + _defaultEyeModelPosition(glm::vec3(0.f, 0.f, 0.f)) { } SkeletonModel::~SkeletonModel() { @@ -677,8 +678,6 @@ void SkeletonModel::buildShapes() { _defaultEyeModelPosition = midEyePosition - rootModelPosition; _defaultEyeModelPosition.z = -_defaultEyeModelPosition.z; - } else { - _defaultEyeModelPosition = glm::vec3(0.f, 0.f, 0.f); } // While the shapes are in their default position we disable collisions between From 83ad0e4eb0217d31e132f495111dd2a31a0b1f27 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 26 Sep 2014 11:14:27 -0700 Subject: [PATCH 34/36] Fix mono injection --- libraries/audio/src/InjectedAudioStream.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/audio/src/InjectedAudioStream.cpp b/libraries/audio/src/InjectedAudioStream.cpp index 762a40003a..0cc13eae05 100644 --- a/libraries/audio/src/InjectedAudioStream.cpp +++ b/libraries/audio/src/InjectedAudioStream.cpp @@ -40,8 +40,9 @@ int InjectedAudioStream::parseStreamProperties(PacketType type, packetStream.skipRawData(NUM_BYTES_RFC4122_UUID); packetStream >> _isStereo; - _ringBuffer.resizeForFrameSize(isStereo() ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : - NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); + if (isStereo()) { + _ringBuffer.resizeForFrameSize(NETWORK_BUFFER_LENGTH_SAMPLES_STEREO); + } // pull the loopback flag and set our boolean uchar shouldLoopback; From 2d81d7c5548d111848afb30e52a59d6699efe3f8 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 26 Sep 2014 11:43:56 -0700 Subject: [PATCH 35/36] add support for ignore collisions and collisions will/wont move entities --- examples/editModels.js | 18 ++++- .../entities/src/EntityCollisionSystem.cpp | 76 +++++++++++++------ libraries/entities/src/EntityItem.cpp | 16 ++++ libraries/entities/src/EntityItem.h | 10 +++ .../entities/src/EntityItemProperties.cpp | 14 ++++ libraries/entities/src/EntityItemProperties.h | 14 +++- libraries/entities/src/EntityTreeElement.cpp | 4 +- 7 files changed, 123 insertions(+), 29 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 356b228380..43e73104e2 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -2907,8 +2907,6 @@ function handeMenuEvent(menuItem) { index++; array.push({ label: "Linear Damping:", value: properties.damping.toFixed(decimals) }); index++; - array.push({ label: "Mass:", value: properties.mass.toFixed(decimals) }); - index++; array.push({ label: "Angular Pitch:", value: properties.angularVelocity.x.toFixed(decimals) }); index++; array.push({ label: "Angular Yaw:", value: properties.angularVelocity.y.toFixed(decimals) }); @@ -2925,6 +2923,15 @@ function handeMenuEvent(menuItem) { array.push({ label: "Gravity Z:", value: properties.gravity.z.toFixed(decimals) }); index++; + array.push({ label: "Collisions:", type: "header" }); + index++; + array.push({ label: "Mass:", value: properties.mass.toFixed(decimals) }); + index++; + array.push({ label: "Ignore for Collisions:", value: properties.ignoreForCollisions }); + index++; + array.push({ label: "Collisions Will Move:", value: properties.collisionsWillMove }); + index++; + array.push({ label: "Lifetime:", value: properties.lifetime.toFixed(decimals) }); index++; @@ -3055,7 +3062,6 @@ Window.nonBlockingFormClosed.connect(function() { properties.velocity.y = array[index++].value; properties.velocity.z = array[index++].value; properties.damping = array[index++].value; - properties.mass = array[index++].value; properties.angularVelocity.x = array[index++].value; properties.angularVelocity.y = array[index++].value; @@ -3065,6 +3071,12 @@ Window.nonBlockingFormClosed.connect(function() { properties.gravity.x = array[index++].value; properties.gravity.y = array[index++].value; properties.gravity.z = array[index++].value; + + index++; // skip header + properties.mass = array[index++].value; + properties.ignoreForCollisions = array[index++].value; + properties.collisionsWillMove = array[index++].value; + properties.lifetime = array[index++].value; properties.visible = array[index++].value; diff --git a/libraries/entities/src/EntityCollisionSystem.cpp b/libraries/entities/src/EntityCollisionSystem.cpp index 2a5450e91f..609b91f3a1 100644 --- a/libraries/entities/src/EntityCollisionSystem.cpp +++ b/libraries/entities/src/EntityCollisionSystem.cpp @@ -105,10 +105,15 @@ void EntityCollisionSystem::updateCollisionWithVoxels(EntityItem* entity) { } void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { + + if (entityA->getIgnoreForCollisions()) { + return; // bail early if this entity is to be ignored... + } + glm::vec3 penetration; EntityItem* entityB = NULL; - const float MAX_COLLISIONS_PER_ENTITY = 32; + const int MAX_COLLISIONS_PER_ENTITY = 32; CollisionList collisions(MAX_COLLISIONS_PER_ENTITY); bool shapeCollisionsAccurate = false; @@ -134,8 +139,13 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { // we don't want to count this as a collision. glm::vec3 relativeVelocity = entityA->getVelocity() - entityB->getVelocity(); + bool wantToMoveA = entityA->getCollisionsWillMove(); + bool wantToMoveB = entityB->getCollisionsWillMove(); bool movingTowardEachOther = glm::dot(relativeVelocity, penetrationInTreeUnits) > 0.0f; - bool doCollisions = movingTowardEachOther; // don't do collisions if the entities are moving away from each other + + // only do collisions if the entities are moving toward each other and one or the other + // of the entities are movable from collisions + bool doCollisions = movingTowardEachOther && (wantToMoveA || wantToMoveB); if (doCollisions) { quint64 now = usecTimestampNow(); @@ -152,35 +162,53 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) { float massA = entityA->getMass(); float massB = entityB->getMass(); float totalMass = massA + massB; + float massRatioA = (2.0f * massB / totalMass); + float massRatioB = (2.0f * massA / totalMass); - // handle Entity A - glm::vec3 newVelocityA = entityA->getVelocity() - axialVelocity * (2.0f * massB / totalMass); - glm::vec3 newPositionA = entityA->getPosition() - 0.5f * penetrationInTreeUnits; + // in the event that one of our entities is non-moving, then fix up these ratios + if (wantToMoveA && !wantToMoveB) { + massRatioA = 2.0f; + massRatioB = 0.0f; + } - EntityItemProperties propertiesA = entityA->getProperties(); - EntityItemID idA(entityA->getID()); - propertiesA.setVelocity(newVelocityA * (float)TREE_SCALE); - propertiesA.setPosition(newPositionA * (float)TREE_SCALE); - propertiesA.setLastEdited(now); + if (!wantToMoveA && wantToMoveB) { + massRatioA = 0.0f; + massRatioB = 2.0f; + } - _entities->updateEntity(idA, propertiesA); - _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idA, propertiesA); + // unless the entity is configured to not be moved by collision, calculate it's new position + // and velocity and apply it + if (wantToMoveA) { + // handle Entity A + glm::vec3 newVelocityA = entityA->getVelocity() - axialVelocity * massRatioA; + glm::vec3 newPositionA = entityA->getPosition() - 0.5f * penetrationInTreeUnits; - glm::vec3 newVelocityB = entityB->getVelocity() + axialVelocity * (2.0f * massA / totalMass); - glm::vec3 newPositionB = entityB->getPosition() + 0.5f * penetrationInTreeUnits; + EntityItemProperties propertiesA = entityA->getProperties(); + EntityItemID idA(entityA->getID()); + propertiesA.setVelocity(newVelocityA * (float)TREE_SCALE); + propertiesA.setPosition(newPositionA * (float)TREE_SCALE); + propertiesA.setLastEdited(now); + + _entities->updateEntity(idA, propertiesA); + _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idA, propertiesA); + } - EntityItemProperties propertiesB = entityB->getProperties(); + // unless the entity is configured to not be moved by collision, calculate it's new position + // and velocity and apply it + if (wantToMoveB) { + glm::vec3 newVelocityB = entityB->getVelocity() + axialVelocity * massRatioB; + glm::vec3 newPositionB = entityB->getPosition() + 0.5f * penetrationInTreeUnits; - EntityItemID idB(entityB->getID()); - propertiesB.setVelocity(newVelocityB * (float)TREE_SCALE); - propertiesB.setPosition(newPositionB * (float)TREE_SCALE); - propertiesB.setLastEdited(now); + EntityItemProperties propertiesB = entityB->getProperties(); - _entities->updateEntity(idB, propertiesB); - _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idB, propertiesB); - - // TODO: Do we need this? - //_packetSender->releaseQueuedMessages(); + EntityItemID idB(entityB->getID()); + propertiesB.setVelocity(newVelocityB * (float)TREE_SCALE); + propertiesB.setPosition(newPositionB * (float)TREE_SCALE); + propertiesB.setLastEdited(now); + + _entities->updateEntity(idB, propertiesB); + _packetSender->queueEditEntityMessage(PacketTypeEntityAddOrEdit, idB, propertiesB); + } } } } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 84ba0bd45d..86fac0997e 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -42,6 +42,8 @@ const glm::vec3 EntityItem::NO_ANGULAR_VELOCITY = glm::vec3(0.0f, 0.0f, 0.0f); const glm::vec3 EntityItem::DEFAULT_ANGULAR_VELOCITY = NO_ANGULAR_VELOCITY; const float EntityItem::DEFAULT_ANGULAR_DAMPING = 0.5f; const bool EntityItem::DEFAULT_VISIBLE = true; +const bool EntityItem::DEFAULT_IGNORE_FOR_COLLISIONS = false; +const bool EntityItem::DEFAULT_COLLISIONS_WILL_MOVE = false; void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) { _id = entityItemID.id; @@ -70,6 +72,8 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) { _angularVelocity = DEFAULT_ANGULAR_VELOCITY; _angularDamping = DEFAULT_ANGULAR_DAMPING; _visible = DEFAULT_VISIBLE; + _ignoreForCollisions = DEFAULT_IGNORE_FOR_COLLISIONS; + _collisionsWillMove = DEFAULT_COLLISIONS_WILL_MOVE; recalculateCollisionShape(); } @@ -111,6 +115,8 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_ANGULAR_VELOCITY; requestedProperties += PROP_ANGULAR_DAMPING; requestedProperties += PROP_VISIBLE; + requestedProperties += PROP_IGNORE_FOR_COLLISIONS; + requestedProperties += PROP_COLLISIONS_WILL_MOVE; return requestedProperties; } @@ -224,6 +230,8 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, appendValue, getAngularVelocity()); APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, appendValue, getAngularDamping()); APPEND_ENTITY_PROPERTY(PROP_VISIBLE, appendValue, getVisible()); + APPEND_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, appendValue, getIgnoreForCollisions()); + APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, appendValue, getCollisionsWillMove()); appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, @@ -484,10 +492,14 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, _angularVelocity); READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, _angularDamping); READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, _visible); + READ_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, bool, _ignoreForCollisions); + READ_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, bool, _collisionsWillMove); if (wantDebug) { qDebug() << " readEntityDataFromBuffer() _registrationPoint:" << _registrationPoint; qDebug() << " readEntityDataFromBuffer() _visible:" << _visible; + qDebug() << " readEntityDataFromBuffer() _ignoreForCollisions:" << _ignoreForCollisions; + qDebug() << " readEntityDataFromBuffer() _collisionsWillMove:" << _collisionsWillMove; } bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData); @@ -734,6 +746,8 @@ EntityItemProperties EntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(angularDamping, getAngularDamping); COPY_ENTITY_PROPERTY_TO_PROPERTIES(glowLevel, getGlowLevel); COPY_ENTITY_PROPERTY_TO_PROPERTIES(visible, getVisible); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(ignoreForCollisions, getIgnoreForCollisions); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionsWillMove, getCollisionsWillMove); properties._defaultSettings = false; @@ -766,6 +780,8 @@ bool EntityItem::setProperties(const EntityItemProperties& properties, bool forc SET_ENTITY_PROPERTY_FROM_PROPERTIES(angularDamping, setAngularDamping); SET_ENTITY_PROPERTY_FROM_PROPERTIES(glowLevel, setGlowLevel); SET_ENTITY_PROPERTY_FROM_PROPERTIES(visible, setVisible); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(ignoreForCollisions, setIgnoreForCollisions); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, setCollisionsWillMove); if (somethingChanged) { somethingChangedNotification(); // notify derived classes that something has changed diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index bdd0d572d3..621bd94287 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -230,6 +230,14 @@ public: void setVisible(bool value) { _visible = value; } bool isVisible() const { return _visible; } bool isInvisible() const { return !_visible; } + + static const bool DEFAULT_IGNORE_FOR_COLLISIONS; + bool getIgnoreForCollisions() const { return _ignoreForCollisions; } + void setIgnoreForCollisions(bool value) { _ignoreForCollisions = value; } + + static const bool DEFAULT_COLLISIONS_WILL_MOVE; + bool getCollisionsWillMove() const { return _collisionsWillMove; } + void setCollisionsWillMove(bool value) { _collisionsWillMove = value; } // TODO: We need to get rid of these users of getRadius()... float getRadius() const; @@ -265,6 +273,8 @@ protected: glm::vec3 _angularVelocity; float _angularDamping; bool _visible; + bool _ignoreForCollisions; + bool _collisionsWillMove; // NOTE: Radius support is obsolete, but these private helper functions are available for this class to // parse old data streams diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 207ed376d8..f54ce274f9 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -41,6 +41,8 @@ EntityItemProperties::EntityItemProperties() : _angularVelocity(EntityItem::DEFAULT_ANGULAR_VELOCITY), _angularDamping(EntityItem::DEFAULT_ANGULAR_DAMPING), _visible(EntityItem::DEFAULT_VISIBLE), + _ignoreForCollisions(EntityItem::DEFAULT_IGNORE_FOR_COLLISIONS), + _collisionsWillMove(EntityItem::DEFAULT_COLLISIONS_WILL_MOVE), _positionChanged(false), _dimensionsChanged(false), @@ -55,6 +57,8 @@ EntityItemProperties::EntityItemProperties() : _angularVelocityChanged(false), _angularDampingChanged(false), _visibleChanged(false), + _ignoreForCollisionsChanged(false), + _collisionsWillMoveChanged(false), _color(), _modelURL(""), @@ -112,6 +116,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_REGISTRATION_POINT, registrationPoint); CHECK_PROPERTY_CHANGE(PROP_ANGULAR_VELOCITY, angularVelocity); CHECK_PROPERTY_CHANGE(PROP_ANGULAR_DAMPING, angularDamping); + CHECK_PROPERTY_CHANGE(PROP_IGNORE_FOR_COLLISIONS, ignoreForCollisions); + CHECK_PROPERTY_CHANGE(PROP_COLLISIONS_WILL_MOVE, collisionsWillMove); return changedProperties; } @@ -150,6 +156,8 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons COPY_PROPERTY_TO_QSCRIPTVALUE(animationFrameIndex); COPY_PROPERTY_TO_QSCRIPTVALUE(animationFPS); COPY_PROPERTY_TO_QSCRIPTVALUE(glowLevel); + COPY_PROPERTY_TO_QSCRIPTVALUE(ignoreForCollisions); + COPY_PROPERTY_TO_QSCRIPTVALUE(collisionsWillMove); // Sitting properties support QScriptValue sittingPoints = engine->newObject(); @@ -194,6 +202,8 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) { COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(animationFPS, setAnimationFPS); COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(animationFrameIndex, setAnimationFrameIndex); COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(glowLevel, setGlowLevel); + COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(ignoreForCollisions, setIgnoreForCollisions); + COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(collisionsWillMove, setCollisionsWillMove); _lastEdited = usecTimestampNow(); } @@ -342,6 +352,8 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, appendValue, properties.getAngularVelocity()); APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, appendValue, properties.getAngularDamping()); APPEND_ENTITY_PROPERTY(PROP_VISIBLE, appendValue, properties.getVisible()); + APPEND_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, appendValue, properties.getIgnoreForCollisions()); + APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, appendValue, properties.getCollisionsWillMove()); } if (propertyCount > 0) { int endOfEntityItemData = packetData->getUncompressedByteOffset(); @@ -538,6 +550,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_VELOCITY, glm::vec3, setAngularVelocity); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_DAMPING, float, setAngularDamping); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VISIBLE, bool, setVisible); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IGNORE_FOR_COLLISIONS, bool, setIgnoreForCollisions); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISIONS_WILL_MOVE, bool, setCollisionsWillMove); return valid; } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index f4f9343f76..0d993acda9 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -62,8 +62,10 @@ enum EntityPropertyList { PROP_REGISTRATION_POINT, PROP_ANGULAR_VELOCITY, PROP_ANGULAR_DAMPING, + PROP_IGNORE_FOR_COLLISIONS, + PROP_COLLISIONS_WILL_MOVE, - PROP_LAST_ITEM = PROP_ANGULAR_DAMPING + PROP_LAST_ITEM = PROP_COLLISIONS_WILL_MOVE }; typedef PropertyFlags EntityPropertyFlags; @@ -221,6 +223,12 @@ public: bool getVisible() const { return _visible; } void setVisible(bool value) { _visible = value; _visibleChanged = true; } + bool getIgnoreForCollisions() const { return _ignoreForCollisions; } + void setIgnoreForCollisions(bool value) { _ignoreForCollisions = value; _ignoreForCollisionsChanged = true; } + + bool getCollisionsWillMove() const { return _collisionsWillMove; } + void setCollisionsWillMove(bool value) { _collisionsWillMove = value; _collisionsWillMoveChanged = true; } + void setLastEdited(quint64 usecTime) { _lastEdited = usecTime; } private: @@ -247,6 +255,8 @@ private: glm::vec3 _angularVelocity; float _angularDamping; bool _visible; + bool _ignoreForCollisions; + bool _collisionsWillMove; bool _positionChanged; bool _dimensionsChanged; @@ -261,6 +271,8 @@ private: bool _angularVelocityChanged; bool _angularDampingChanged; bool _visibleChanged; + bool _ignoreForCollisionsChanged; + bool _collisionsWillMoveChanged; // TODO: this need to be more generic. for now, we're going to have the properties class support these as // named getter/setters, but we want to move them to generic types... diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 6e9ecbbd9a..5df98d43a8 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -555,8 +555,10 @@ bool EntityTreeElement::findShapeCollisions(const Shape* shape, CollisionList& c QList::const_iterator entityEnd = _entityItems->end(); while(entityItr != entityEnd) { EntityItem* entity = (*entityItr); + + // entities that are set for ignore for collisions then don't consider them for collision const Shape* otherCollisionShape = &entity->getCollisionShapeInMeters(); - if (shape != otherCollisionShape) { + if (shape != otherCollisionShape && !entity->getIgnoreForCollisions()) { if (ShapeCollider::collideShapes(shape, otherCollisionShape, collisions)) { CollisionInfo* lastCollision = collisions.getLastCollision(); lastCollision->_extraData = entity; From efa22d931f23d427066925ee0be3591c540bc9fd Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 26 Sep 2014 13:48:11 -0700 Subject: [PATCH 36/36] If we're rendering the billboard, don't render the body. --- interface/src/avatar/Avatar.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 3eae0ef382..c82eaa2bac 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -505,10 +505,11 @@ void Avatar::renderBody(RenderMode renderMode, bool postLighting, float glowLeve { Glower glower(glowLevel); - if ((_shouldRenderBillboard || !(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) && - (postLighting || renderMode == SHADOW_RENDER_MODE)) { - // render the billboard until both models are loaded - renderBillboard(); + if (_shouldRenderBillboard || !(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) { + if (postLighting || renderMode == SHADOW_RENDER_MODE) { + // render the billboard until both models are loaded + renderBillboard(); + } return; }