From 1d014358ee05ab16c7350442ff4b3e337c85b3d5 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 19 Sep 2014 17:06:03 -0700 Subject: [PATCH 01/28] 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 8393fcc6ebe95ddda4ef3b7f58e77eeb1fc6acaa Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 22 Sep 2014 14:01:02 -0700 Subject: [PATCH 02/28] 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 39e52babb191639bfb21873815109865d457cdb1 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 22 Sep 2014 17:56:18 -0700 Subject: [PATCH 03/28] CSG edit improvements. --- .../metavoxels/src/MetavoxelMessages.cpp | 186 ++++++++++++++---- 1 file changed, 144 insertions(+), 42 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 79ac74a1f6..791604f63f 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -667,8 +667,9 @@ int VoxelMaterialBoxEditVisitor::visit(MetavoxelInfo& info) { return DEFAULT_ORDER; } VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue(); - QVector colorContents = (colorPointer && colorPointer->getSize() == VOXEL_BLOCK_SAMPLES) ? + QVector oldColorContents = (colorPointer && colorPointer->getSize() == VOXEL_BLOCK_SAMPLES) ? colorPointer->getContents() : QVector(VOXEL_BLOCK_VOLUME); + QVector colorContents = oldColorContents; Box overlap = info.getBounds().getIntersection(_region); float scale = VOXEL_BLOCK_SIZE / info.size; @@ -725,32 +726,92 @@ int VoxelMaterialBoxEditVisitor::visit(MetavoxelInfo& info) { for (int x = hermiteMinX, hermiteMaxX = x + hermiteSizeX - 1; x <= hermiteMaxX; x++, hermiteDestX += VoxelHermiteData::EDGE_COUNT) { // internal edges are set to zero; border edges (when non-terminal) are set to the intersection values - hermiteDestX[0] = 0x0; if ((x == hermiteMinX || x == hermiteMaxX) && x != VOXEL_BLOCK_SIZE) { - const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; + int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; + const QRgb* color = colorContents.constData() + offset; int alpha0 = qAlpha(color[0]); - if (alpha0 != qAlpha(color[1])) { - hermiteDestX[0] = qRgba(alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0, 0, - ((x == hermiteMinX ? overlap.minimum.x : overlap.maximum.x) - x) * EIGHT_BIT_MAXIMUM); + int alpha1 = qAlpha(color[1]); + if (alpha0 != alpha1) { + const QRgb* oldColor = oldColorContents.constData() + offset; + if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[1]) == alpha1) { + if (x == hermiteMinX) { + int alpha = (overlap.minimum.x - x) * EIGHT_BIT_MAXIMUM; + if (alpha <= qAlpha(hermiteDestX[0])) { + hermiteDestX[0] = qRgba(alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0, 0, alpha); + } + } else { + int alpha = (overlap.maximum.x - x) * EIGHT_BIT_MAXIMUM; + if (alpha >= qAlpha(hermiteDestX[0])) { + hermiteDestX[0] = qRgba(alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0, 0, alpha); + } + } + } else { + hermiteDestX[0] = qRgba(alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0, 0, + ((x == hermiteMinX ? overlap.minimum.x : overlap.maximum.x) - x) * EIGHT_BIT_MAXIMUM); + } + } else { + hermiteDestX[0] = 0x0; } + } else { + hermiteDestX[0] = 0x0; } - hermiteDestX[1] = 0x0; if ((y == hermiteMinY || y == hermiteMaxY) && y != VOXEL_BLOCK_SIZE) { - const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; + int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; + const QRgb* color = colorContents.constData() + offset; int alpha0 = qAlpha(color[0]); - if (alpha0 != qAlpha(color[VOXEL_BLOCK_SAMPLES])) { - hermiteDestX[1] = qRgba(0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0, - ((y == hermiteMinY ? overlap.minimum.y : overlap.maximum.y) - y) * EIGHT_BIT_MAXIMUM); + int alpha2 = qAlpha(color[VOXEL_BLOCK_SAMPLES]); + if (alpha0 != alpha2) { + const QRgb* oldColor = oldColorContents.constData() + offset; + if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[VOXEL_BLOCK_SAMPLES]) == alpha2) { + if (y == hermiteMinY) { + int alpha = (overlap.minimum.y - y) * EIGHT_BIT_MAXIMUM; + if (alpha <= qAlpha(hermiteDestX[1])) { + hermiteDestX[1] = qRgba(0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0, alpha); + } + } else { + int alpha = (overlap.maximum.y - y) * EIGHT_BIT_MAXIMUM; + if (alpha >= qAlpha(hermiteDestX[1])) { + hermiteDestX[1] = qRgba(0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0, alpha); + } + } + } else { + hermiteDestX[1] = qRgba(0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0, + ((y == hermiteMinY ? overlap.minimum.y : overlap.maximum.y) - y) * EIGHT_BIT_MAXIMUM); + } + } else { + hermiteDestX[1] = 0x0; } + } else { + hermiteDestX[1] = 0x0; } - hermiteDestX[2] = 0x0; if ((z == hermiteMinZ || z == hermiteMaxZ) && z != VOXEL_BLOCK_SIZE) { - const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; + int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; + const QRgb* color = colorContents.constData() + offset; int alpha0 = qAlpha(color[0]); - if (alpha0 != qAlpha(color[VOXEL_BLOCK_AREA])) { - hermiteDestX[2] = qRgba(0, 0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, - ((z == hermiteMinZ ? overlap.minimum.z : overlap.maximum.z) - z) * EIGHT_BIT_MAXIMUM); + int alpha4 = qAlpha(color[VOXEL_BLOCK_AREA]); + if (alpha0 != alpha4) { + const QRgb* oldColor = oldColorContents.constData() + offset; + if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[VOXEL_BLOCK_AREA]) == alpha4) { + if (z == hermiteMinZ) { + int alpha = (overlap.minimum.z - z) * EIGHT_BIT_MAXIMUM; + if (alpha <= qAlpha(hermiteDestX[2])) { + hermiteDestX[2] = qRgba(0, 0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, alpha); + } + } else { + int alpha = (overlap.maximum.z - z) * EIGHT_BIT_MAXIMUM; + if (alpha >= qAlpha(hermiteDestX[2])) { + hermiteDestX[2] = qRgba(0, 0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, alpha); + } + } + } else { + hermiteDestX[2] = qRgba(0, 0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, + ((z == hermiteMinZ ? overlap.minimum.z : overlap.maximum.z) - z) * EIGHT_BIT_MAXIMUM); + } + } else { + hermiteDestX[2] = 0x0; } + } else { + hermiteDestX[2] = 0x0; } } } @@ -864,8 +925,9 @@ int VoxelMaterialSphereEditVisitor::visit(MetavoxelInfo& info) { return DEFAULT_ORDER; } VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue(); - QVector colorContents = (colorPointer && colorPointer->getSize() == VOXEL_BLOCK_SAMPLES) ? + QVector oldColorContents = (colorPointer && colorPointer->getSize() == VOXEL_BLOCK_SAMPLES) ? colorPointer->getContents() : QVector(VOXEL_BLOCK_VOLUME); + QVector colorContents = oldColorContents; Box overlap = info.getBounds().getIntersection(_bounds); float scale = VOXEL_BLOCK_SIZE / info.size; @@ -883,6 +945,7 @@ int VoxelMaterialSphereEditVisitor::visit(MetavoxelInfo& info) { float relativeRadiusSquared = relativeRadius * relativeRadius; QRgb rgb = _color.rgba(); + bool flipped = (qAlpha(rgb) == 0); glm::vec3 position(0.0f, 0.0f, minZ); for (QRgb* destZ = colorContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX, *endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z++) { @@ -932,78 +995,117 @@ int VoxelMaterialSphereEditVisitor::visit(MetavoxelInfo& info) { hermiteDestX += VoxelHermiteData::EDGE_COUNT) { // at each intersected non-terminal edge, we check for a transition and, if one is detected, we assign the // crossing and normal values based on intersection with the sphere - hermiteDestX[0] = 0x0; - glm::vec3 offset(x - relativeCenter.x, y - relativeCenter.y, z - relativeCenter.z); + glm::vec3 vector(x - relativeCenter.x, y - relativeCenter.y, z - relativeCenter.z); if (x != VOXEL_BLOCK_SIZE) { - const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; + int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; + const QRgb* color = colorContents.constData() + offset; int alpha0 = qAlpha(color[0]); - if (alpha0 != qAlpha(color[1])) { - float radicand = relativeRadiusSquared - offset.y * offset.y - offset.z * offset.z; + int alpha1 = qAlpha(color[1]); + if (alpha0 != alpha1) { + float radicand = relativeRadiusSquared - vector.y * vector.y - vector.z * vector.z; float parameter = 0.5f; if (radicand >= 0.0f) { float root = glm::sqrt(radicand); - parameter = -offset.x - root; + parameter = -vector.x - root; if (parameter < 0.0f || parameter > 1.0f) { - parameter = glm::clamp(-offset.x + root, 0.0f, 1.0f); + parameter = glm::clamp(-vector.x + root, 0.0f, 1.0f); } } - glm::vec3 normal = offset + glm::vec3(parameter, 0.0f, 0.0f); + glm::vec3 normal = vector + glm::vec3(parameter, 0.0f, 0.0f); float length = glm::length(normal); if (length > EPSILON) { normal /= length; } else { normal = glm::vec3(0.0f, 1.0f, 0.0f); } - hermiteDestX[0] = packNormal(normal, parameter * EIGHT_BIT_MAXIMUM); + const QRgb* oldColor = oldColorContents.constData() + offset; + if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[1]) == alpha1) { + int alpha = parameter * EIGHT_BIT_MAXIMUM; + if (normal.x < 0.0f ? alpha <= qAlpha(hermiteDestX[0]) : alpha >= qAlpha(hermiteDestX[0])) { + hermiteDestX[0] = packNormal(flipped ? -normal : normal, alpha); + } + } else { + hermiteDestX[0] = packNormal(flipped ? -normal : normal, parameter * EIGHT_BIT_MAXIMUM); + } + } else { + hermiteDestX[0] = 0x0; } + } else { + hermiteDestX[0] = 0x0; } - hermiteDestX[1] = 0x0; if (y != VOXEL_BLOCK_SIZE) { - const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; + int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; + const QRgb* color = colorContents.constData() + offset; int alpha0 = qAlpha(color[0]); - if (alpha0 != qAlpha(color[VOXEL_BLOCK_SAMPLES])) { - float radicand = relativeRadiusSquared - offset.x * offset.x - offset.z * offset.z; + int alpha2 = qAlpha(color[VOXEL_BLOCK_SAMPLES]); + if (alpha0 != alpha2) { + float radicand = relativeRadiusSquared - vector.x * vector.x - vector.z * vector.z; float parameter = 0.5f; if (radicand >= 0.0f) { float root = glm::sqrt(radicand); - parameter = -offset.y - root; + parameter = -vector.y - root; if (parameter < 0.0f || parameter > 1.0f) { - parameter = glm::clamp(-offset.y + root, 0.0f, 1.0f); + parameter = glm::clamp(-vector.y + root, 0.0f, 1.0f); } } - glm::vec3 normal = offset + glm::vec3(parameter, 0.0f, 0.0f); + glm::vec3 normal = vector + glm::vec3(parameter, 0.0f, 0.0f); float length = glm::length(normal); if (length > EPSILON) { normal /= length; } else { normal = glm::vec3(1.0f, 0.0f, 0.0f); } - hermiteDestX[1] = packNormal(normal, parameter * EIGHT_BIT_MAXIMUM); + const QRgb* oldColor = oldColorContents.constData() + offset; + if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[VOXEL_BLOCK_SAMPLES]) == alpha2) { + int alpha = parameter * EIGHT_BIT_MAXIMUM; + if (normal.y < 0.0f ? alpha <= qAlpha(hermiteDestX[1]) : alpha >= qAlpha(hermiteDestX[1])) { + hermiteDestX[1] = packNormal(flipped ? -normal : normal, alpha); + } + } else { + hermiteDestX[1] = packNormal(flipped ? -normal : normal, parameter * EIGHT_BIT_MAXIMUM); + } + } else { + hermiteDestX[1] = 0x0; } + } else { + hermiteDestX[1] = 0x0; } - hermiteDestX[2] = 0x0; if (z != VOXEL_BLOCK_SIZE) { - const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; + int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x; + const QRgb* color = colorContents.constData() + offset; int alpha0 = qAlpha(color[0]); - if (alpha0 != qAlpha(color[VOXEL_BLOCK_AREA])) { - float radicand = relativeRadiusSquared - offset.x * offset.x - offset.y * offset.y; + int alpha4 = qAlpha(color[VOXEL_BLOCK_AREA]); + if (alpha0 != alpha4) { + float radicand = relativeRadiusSquared - vector.x * vector.x - vector.y * vector.y; float parameter = 0.5f; if (radicand >= 0.0f) { float root = glm::sqrt(radicand); - parameter = -offset.z - root; + parameter = -vector.z - root; if (parameter < 0.0f || parameter > 1.0f) { - parameter = glm::clamp(-offset.z + root, 0.0f, 1.0f); + parameter = glm::clamp(-vector.z + root, 0.0f, 1.0f); } } - glm::vec3 normal = offset + glm::vec3(parameter, 0.0f, 0.0f); + glm::vec3 normal = vector + glm::vec3(parameter, 0.0f, 0.0f); float length = glm::length(normal); if (length > EPSILON) { normal /= length; } else { normal = glm::vec3(1.0f, 0.0f, 0.0f); } - hermiteDestX[2] = packNormal(normal, parameter * EIGHT_BIT_MAXIMUM); + const QRgb* oldColor = oldColorContents.constData() + offset; + if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[VOXEL_BLOCK_AREA]) == alpha4) { + int alpha = parameter * EIGHT_BIT_MAXIMUM; + if (normal.z < 0.0f ? alpha <= qAlpha(hermiteDestX[2]) : alpha >= qAlpha(hermiteDestX[2])) { + hermiteDestX[2] = packNormal(flipped ? -normal : normal, alpha); + } + } else { + hermiteDestX[2] = packNormal(flipped ? -normal : normal, parameter * EIGHT_BIT_MAXIMUM); + } + } else { + hermiteDestX[2] = 0x0; } + } else { + hermiteDestX[2] = 0x0; } } } From 13a5dbf4896b2243a5309cc8ae25ecc05d79b255 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 23 Sep 2014 11:01:21 -0700 Subject: [PATCH 04/28] 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 05/28] 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 06/28] 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 16cfaa6ea55ade73329d1256e84b3215a10153e7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 23 Sep 2014 13:28:00 -0700 Subject: [PATCH 07/28] Use cubic texture coordinate generation for the dual contour surfaces. --- .../shaders/metavoxel_voxel_splat.frag | 15 ++++++++++---- .../shaders/metavoxel_voxel_splat.vert | 20 ++++++++++++++----- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/interface/resources/shaders/metavoxel_voxel_splat.frag b/interface/resources/shaders/metavoxel_voxel_splat.frag index 66ce2721ac..c6c9860c1a 100644 --- a/interface/resources/shaders/metavoxel_voxel_splat.frag +++ b/interface/resources/shaders/metavoxel_voxel_splat.frag @@ -17,13 +17,20 @@ const int SPLAT_COUNT = 4; // the splat textures uniform sampler2D diffuseMaps[SPLAT_COUNT]; +// the model space normal +varying vec3 normal; + // alpha values for the four splat textures varying vec4 alphaValues; void main(void) { + // determine the cube face to use for texture coordinate generation + vec3 absNormal = abs(normal); + vec2 parameters = step(absNormal.yy, absNormal.xz) * step(absNormal.zx, absNormal.xz); + // blend the splat textures - gl_FragColor = (texture2D(diffuseMaps[0], gl_TexCoord[0].st) * alphaValues.x + - texture2D(diffuseMaps[1], gl_TexCoord[1].st) * alphaValues.y + - texture2D(diffuseMaps[2], gl_TexCoord[2].st) * alphaValues.z + - texture2D(diffuseMaps[3], gl_TexCoord[3].st) * alphaValues.w); + gl_FragColor = (texture2D(diffuseMaps[0], mix(gl_TexCoord[0].xw, gl_TexCoord[0].zy, parameters)) * alphaValues.x + + texture2D(diffuseMaps[1], mix(gl_TexCoord[1].xw, gl_TexCoord[1].zy, parameters)) * alphaValues.y + + texture2D(diffuseMaps[2], mix(gl_TexCoord[2].xw, gl_TexCoord[2].zy, parameters)) * alphaValues.z + + texture2D(diffuseMaps[3], mix(gl_TexCoord[3].xw, gl_TexCoord[3].zy, parameters)) * alphaValues.w); } diff --git a/interface/resources/shaders/metavoxel_voxel_splat.vert b/interface/resources/shaders/metavoxel_voxel_splat.vert index 150a9e7d2e..31ddbef395 100644 --- a/interface/resources/shaders/metavoxel_voxel_splat.vert +++ b/interface/resources/shaders/metavoxel_voxel_splat.vert @@ -29,6 +29,9 @@ attribute vec4 materials; // the weights of each material attribute vec4 materialWeights; +// the model space normal +varying vec3 normal; + // alpha values for the four splat textures varying vec4 alphaValues; @@ -36,12 +39,19 @@ void main(void) { // use the fixed-function position gl_Position = ftransform(); + // pass along the normal + normal = gl_Normal; + // pass along the scaled/offset texture coordinates - vec4 textureSpacePosition = vec4(gl_Vertex.xz, 0.0, 1.0); - gl_TexCoord[0] = textureSpacePosition * vec4(splatTextureScalesS[0], splatTextureScalesT[0], 0.0, 1.0); - gl_TexCoord[1] = textureSpacePosition * vec4(splatTextureScalesS[1], splatTextureScalesT[1], 0.0, 1.0); - gl_TexCoord[2] = textureSpacePosition * vec4(splatTextureScalesS[2], splatTextureScalesT[2], 0.0, 1.0); - gl_TexCoord[3] = textureSpacePosition * vec4(splatTextureScalesS[3], splatTextureScalesT[3], 0.0, 1.0); + vec4 textureSpacePosition = gl_Vertex.xyyz; + gl_TexCoord[0] = textureSpacePosition * vec4(splatTextureScalesS[0], splatTextureScalesT[0], + splatTextureScalesS[0], splatTextureScalesT[0]); + gl_TexCoord[1] = textureSpacePosition * vec4(splatTextureScalesS[1], splatTextureScalesT[1], + splatTextureScalesS[1], splatTextureScalesT[1]); + gl_TexCoord[2] = textureSpacePosition * vec4(splatTextureScalesS[2], splatTextureScalesT[2], + splatTextureScalesS[2], splatTextureScalesT[2]); + gl_TexCoord[3] = textureSpacePosition * vec4(splatTextureScalesS[3], splatTextureScalesT[3], + splatTextureScalesS[3], splatTextureScalesT[3]); // compute the alpha values for each texture float value = materials[0]; From 95c00b1d4ee443c962d889de3153448869e06075 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 23 Sep 2014 14:18:54 -0700 Subject: [PATCH 08/28] 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 09/28] 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 10/28] 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 11/28] 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 94edf2c900f88569d348662f982d685703522ff3 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 24 Sep 2014 15:45:43 -0700 Subject: [PATCH 12/28] Defaulted penumbra filter to true --- assignment-client/src/audio/AudioMixer.cpp | 4 ++-- domain-server/resources/web/settings/describe.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index a8a7ace06f..a5d9a48d0c 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -75,7 +75,7 @@ InboundAudioStream::Settings AudioMixer::_streamSettings; bool AudioMixer::_printStreamStats = false; -bool AudioMixer::_enableFilter = false; +bool AudioMixer::_enableFilter = true; AudioMixer::AudioMixer(const QByteArray& packet) : ThreadedAssignment(packet), @@ -710,7 +710,7 @@ void AudioMixer::run() { } const QString FILTER_KEY = "J-enable-filter"; - _enableFilter = audioGroupObject[FILTER_KEY].toBool(); + _enableFilter = audioGroupObject[FILTER_KEY].toBool(_enableFilter); if (_enableFilter) { qDebug() << "Filter enabled"; } diff --git a/domain-server/resources/web/settings/describe.json b/domain-server/resources/web/settings/describe.json index fee7ff21fc..2ea0aec0c7 100644 --- a/domain-server/resources/web/settings/describe.json +++ b/domain-server/resources/web/settings/describe.json @@ -67,7 +67,7 @@ "type": "checkbox", "label": "Enable Positional Filter", "help": "If enabled, positional audio stream uses lowpass filter", - "default": false + "default": true } } } From 75dc26c9ffec6ff7cb8b7380bd0031f7f89a3590 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 24 Sep 2014 16:19:47 -0700 Subject: [PATCH 13/28] 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 14/28] 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 15/28] 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 16/28] 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 17/28] 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 fee01f2c73d56ec98f65a8e9b24926642cc48b43 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 25 Sep 2014 15:26:13 -0700 Subject: [PATCH 18/28] Fixed unknow function --- assignment-client/src/audio/AudioMixer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index a5d9a48d0c..bbd1fe4c07 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -710,7 +710,9 @@ void AudioMixer::run() { } const QString FILTER_KEY = "J-enable-filter"; - _enableFilter = audioGroupObject[FILTER_KEY].toBool(_enableFilter); + if (audioGroupObject[FILTER_KEY].isBool()) { + _enableFilter = audioGroupObject[FILTER_KEY].toBool(); + } if (_enableFilter) { qDebug() << "Filter enabled"; } From fa8c5b85ff20fc84852b49802276c6470e69f60d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 25 Sep 2014 17:00:35 -0700 Subject: [PATCH 19/28] 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 20/28] 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 21/28] 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 22/28] 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 23/28] 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 24/28] 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 25/28] 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 26/28] 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 27/28] 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 83ad0e4eb0217d31e132f495111dd2a31a0b1f27 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 26 Sep 2014 11:14:27 -0700 Subject: [PATCH 28/28] 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;