diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index a8a7ace06f..bbd1fe4c07 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,9 @@ void AudioMixer::run() { } const QString FILTER_KEY = "J-enable-filter"; - _enableFilter = audioGroupObject[FILTER_KEY].toBool(); + if (audioGroupObject[FILTER_KEY].isBool()) { + _enableFilter = audioGroupObject[FILTER_KEY].toBool(); + } 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 } } } diff --git a/examples/Recorder.js b/examples/Recorder.js index 8efa9408a9..40bf2d2ed1 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,16 @@ function updateTimer() { text: text }) toolBar.changeSpacing(text.length * 8 + ((MyAvatar.isRecording()) ? 15 : 0), spacing); + + if (MyAvatar.isRecording()) { + slider.pos = 1.0; + } else if (MyAvatar.playerLength() > 0) { + slider.pos = MyAvatar.playerElapsed() / MyAvatar.playerLength(); + } + + Overlays.editOverlay(slider.foreground, { + width: slider.pos * slider.w + }); } function formatTime(time) { @@ -163,7 +198,19 @@ function moveUI() { Overlays.editOverlay(timer, { x: relative.x + timerOffset - ToolBar.SPACING, y: windowDimensions.y - relative.y - ToolBar.SPACING - }); + }); + + slider.x = relative.x - ToolBar.SPACING; + slider.y = windowDimensions.y - relative.y - slider.h - ToolBar.SPACING; + + Overlays.editOverlay(slider.background, { + x: slider.x, + y: slider.y, + }); + Overlays.editOverlay(slider.foreground, { + x: slider.x, + y: slider.y, + }); } function mousePressEvent(event) { @@ -188,7 +235,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); @@ -203,7 +250,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); @@ -234,10 +281,30 @@ function mousePressEvent(event) { toolBar.setAlpha(ALPHA_ON, saveIcon); } } - } else { - + } 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()); } } +var isSliding = false; + +function mouseMoveEvent(event) { + if (isSliding) { + slider.pos = (event.x - slider.x) / slider.w; + if (slider.pos < 0.0 || slider.pos > 1.0) { + MyAvatar.stopPlaying(); + slider.pos = 0.0; + } + MyAvatar.setPlayerTime(slider.pos * MyAvatar.playerLength()); + } +} + +function mouseReleaseEvent(event) { + isSliding = false; +} function update() { var newDimensions = Controller.getViewportDimensions(); @@ -264,11 +331,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); 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 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]; diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 4facc03e1e..10623651a0 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) { } @@ -97,17 +98,15 @@ void AudioInjector::injectAudio() { timer.start(); int nextFrame = 0; - int 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(((_options.isStereo()) ? 2 : 1) * NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL, - soundByteArray.size() - currentSendPosition); + soundByteArray.size() - _currentSendPosition); memcpy(injectAudioPacket.data() + positionOptionOffset, &_options.getPosition(), sizeof(_options.getPosition())); @@ -124,7 +123,7 @@ void AudioInjector::injectAudio() { // copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet memcpy(injectAudioPacket.data() + numPreAudioDataBytes, - soundByteArray.data() + currentSendPosition, bytesToCopy); + soundByteArray.data() + _currentSendPosition, bytesToCopy); // grab our audio mixer from the NodeList, if it exists NodeList* nodeList = NodeList::getInstance(); @@ -134,22 +133,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..af9b5e55d1 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -26,16 +26,20 @@ 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*) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 5ac0c69864..126da654ae 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; @@ -625,6 +620,14 @@ qint64 AvatarData::playerLength() { return _player->getRecording()->getLength(); } +int AvatarData::playerCurrentFrame() { + return (_player) ? _player->getCurrentFrame() : 0; +} + +int AvatarData::playerFrameNumber() { + return (_player && _player->getRecording()) ? _player->getRecording()->getFrameNumber() : 0; +} + void AvatarData::loadRecording(QString filename) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "loadRecording", Qt::BlockingQueuedConnection, @@ -649,6 +652,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); @@ -696,6 +711,19 @@ 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 bc68103ca6..9b28fdc258 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -296,10 +296,16 @@ public slots: bool hasReferential(); bool isPlaying(); + bool isPaused(); 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); @@ -307,6 +313,7 @@ public slots: void setPlayerUseHeadModel(bool useHeadModel); void setPlayerUseSkeletonModel(bool useSkeletonModel); void play(); + void pausePlayer(); void stopPlaying(); protected: diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp index b548d452e7..9732ccd780 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 @@ -18,6 +19,8 @@ Player::Player(AvatarData* avatar) : _recording(new Recording()), + _pausedFrame(-1), + _timerOffset(0), _avatar(avatar), _audioThread(NULL), _playFromCurrentPosition(true), @@ -36,16 +39,26 @@ bool Player::isPlaying() const { return _timer.isValid(); } +bool Player::isPaused() const { + return (_pausedFrame != -1); +} + qint64 Player::elapsed() const { if (isPlaying()) { - return _timer.elapsed(); + 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(); @@ -97,9 +110,17 @@ void Player::startPlaying() { _avatar->setForceFaceshiftConnected(true); qDebug() << "Recorder::startPlaying()"; + setupAudioThread(); _currentFrame = 0; + _timerOffset = 0; + _timer.start(); + } else { + qDebug() << "Recorder::startPlaying(): Unpause"; setupAudioThread(); _timer.start(); + + setCurrentFrame(_pausedFrame); + _pausedFrame = -1; } } @@ -107,6 +128,7 @@ void Player::stopPlaying() { if (!isPlaying()) { return; } + _pausedFrame = -1; _timer.invalidate(); cleanupAudioThread(); _avatar->clearJointsData(); @@ -130,6 +152,15 @@ void Player::stopPlaying() { qDebug() << "Recorder::stopPlaying()"; } +void Player::pausePlayer() { + _timerOffset = elapsed(); + _timer.invalidate(); + cleanupAudioThread(); + + _pausedFrame = _currentFrame; + qDebug() << "Recorder::pausePlayer()"; +} + void Player::setupAudioThread() { _audioThread = new QThread(); _options.setPosition(_avatar->getPosition()); @@ -156,6 +187,7 @@ void Player::loopRecording() { cleanupAudioThread(); setupAudioThread(); _currentFrame = 0; + _timerOffset = 0; _timer.restart(); } @@ -166,10 +198,13 @@ void Player::loadFromFile(const QString& file) { _recording = RecordingPointer(new Recording()); } readRecordingFromFile(_recording, file); + + _pausedFrame = -1; } void Player::loadRecording(RecordingPointer recording) { _recording = recording; + _pausedFrame = -1; } void Player::play() { @@ -213,6 +248,77 @@ void Player::play() { _injector->setOptions(_options); } +void Player::setCurrentFrame(int currentFrame) { + if (_recording && (currentFrame < 0 || currentFrame >= _recording->getFrameNumber())) { + stopPlaying(); + return; + } + + _currentFrame = currentFrame; + _timerOffset = _recording->getFrameTimestamp(_currentFrame); + + if (isPlaying()) { + _timer.start(); + setAudionInjectorPosition(); + } else { + _pausedFrame = currentFrame; + } +} + +void Player::setCurrentTime(qint64 currentTime) { + if (currentTime < 0 || currentTime >= _recording->getLength()) { + stopPlaying(); + return; + } + + // Find correct frame + int lowestBound = 0; + int highestBound = _recording->getFrameNumber() - 1; + while (lowestBound + 1 != highestBound) { + assert(lowestBound < highestBound); + + 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 = lowestBound; + _timerOffset = _recording->getFrameTimestamp(lowestBound); + + if (isPlaying()) { + _timer.start(); + setAudionInjectorPosition(); + } else { + _pausedFrame = lowestBound; + } +} + +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) { _playFromCurrentPosition = playFromCurrentLocation; } @@ -227,7 +333,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..51e120917c 100644 --- a/libraries/avatars/src/Player.h +++ b/libraries/avatars/src/Player.h @@ -30,17 +30,23 @@ public: Player(AvatarData* avatar); bool isPlaying() const; + bool isPaused() const; qint64 elapsed() const; RecordingPointer getRecording() const { return _recording; } + int getCurrentFrame() const { return _currentFrame; } public slots: void startPlaying(); void stopPlaying(); + void pausePlayer(); void loadFromFile(const QString& file); 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; } @@ -52,11 +58,14 @@ private: void setupAudioThread(); void cleanupAudioThread(); void loopRecording(); + void setAudionInjectorPosition(); bool computeCurrentFrame(); QElapsedTimer _timer; RecordingPointer _recording; int _currentFrame; + int _pausedFrame; + qint64 _timerOffset; QSharedPointer _injector; AudioInjectorOptions _options; @@ -64,7 +73,6 @@ private: AvatarData* _avatar; QThread* _audioThread; - RecordingContext _currentContext; bool _playFromCurrentPosition; bool _loop; @@ -72,7 +80,6 @@ private: bool _useDisplayName; bool _useHeadURL; bool _useSkeletonURL; - }; #endif // hifi_Player_h \ No newline at end of file diff --git a/libraries/avatars/src/Recording.cpp b/libraries/avatars/src/Recording.cpp index b34391d580..7465eb1aac 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]; } @@ -781,7 +785,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); 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; } } }