From a1110bf28dda82eaff9454b9e7d947cfe504decc Mon Sep 17 00:00:00 2001 From: Daniela Date: Thu, 26 Oct 2017 16:52:58 +0100 Subject: [PATCH 01/21] Rotations now happen using the correct axis taking into account the avatar referential --- scripts/system/libraries/entitySelectionTool.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 3a422bcb8a..d5cc46586a 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -1346,6 +1346,11 @@ SelectionDisplay = (function() { } } + // place yaw, pitch and roll rotations on the avatar referential + yawHandleRotation = Quat.multiply(MyAvatar.orientation , yawHandleRotation); + pitchHandleRotation = Quat.multiply(MyAvatar.orientation , pitchHandleRotation); + rollHandleRotation = Quat.multiply(MyAvatar.orientation , rollHandleRotation); + var rotateHandlesVisible = true; var rotationOverlaysVisible = false; // note: Commented out as these are currently unused here; however, @@ -3498,6 +3503,8 @@ SelectionDisplay = (function() { initialPosition = SelectionManager.worldPosition; rotationNormal = { x: 0, y: 0, z: 0 }; rotationNormal[rotAroundAxis] = 1; + //get the correct axis according to the avatar referencial + rotationNormal = Vec3.multiplyQbyV(MyAvatar.orientation, rotationNormal); // Size the overlays to the current selection size var diagonal = (Vec3.length(SelectionManager.worldDimensions) / 2) * 1.1; @@ -3599,9 +3606,8 @@ SelectionDisplay = (function() { var snapAngle = snapToInner ? innerSnapAngle : 1.0; angleFromZero = Math.floor(angleFromZero / snapAngle) * snapAngle; - var vec3Degrees = { x: 0, y: 0, z: 0 }; - vec3Degrees[rotAroundAxis] = angleFromZero; - var rotChange = Quat.fromVec3Degrees(vec3Degrees); + + var rotChange = Quat.angleAxis(angleFromZero, rotationNormal); updateSelectionsRotation(rotChange); updateRotationDegreesOverlay(angleFromZero, handleRotation, rotCenter); From 2ceba76ba2cac92ad16293889dc86be8188abae1 Mon Sep 17 00:00:00 2001 From: Daniela Date: Fri, 27 Oct 2017 11:28:44 +0100 Subject: [PATCH 02/21] yaw tool --- .../system/libraries/entitySelectionTool.js | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index d5cc46586a..3408b63e7a 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -1099,6 +1099,8 @@ SelectionDisplay = (function() { ------------------------------*/ var cameraPosition = Camera.getPosition(); + var look = Vec3.normalize(Vec3.subtract(cameraPosition, objectCenter)); + if (cameraPosition.x > objectCenter.x) { // must be BRF or BRN if (cameraPosition.z < objectCenter.z) { @@ -1350,6 +1352,38 @@ SelectionDisplay = (function() { yawHandleRotation = Quat.multiply(MyAvatar.orientation , yawHandleRotation); pitchHandleRotation = Quat.multiply(MyAvatar.orientation , pitchHandleRotation); rollHandleRotation = Quat.multiply(MyAvatar.orientation , rollHandleRotation); + var avatarReferential = Quat.multiply(MyAvatar.orientation, Quat.fromVec3Degrees({ + x: 0, + y: 180, + z: 0 + })); + var upVector = Quat.getUp(avatarReferential); + var rightVector = Quat.getRight(avatarReferential); + var frontVector = Quat.getFront(avatarReferential); + // Centers + var xSign = -1.0; + var zSign = -1.0; + if (Vec3.dot(look, rightVector) > 0) { + xSign = 1.0; + } + if (Vec3.dot(look, frontVector) > 0) { + zSign = 1.0; + } + + yawCenter = Vec3.sum(boundsCenter, Vec3.multiply(-(dimensions.y / 2), upVector)); + var myBotom = Vec3.multiply(-(dimensions.y / 2) - rotateHandleOffset, upVector); + var myRight = Vec3.multiply(xSign * (dimensions.x / 2) + xSign * rotateHandleOffset, rightVector); + var myFront = Vec3.multiply(zSign * (dimensions.z / 2) + zSign * rotateHandleOffset, frontVector); + yawCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(myBotom, myRight), myFront)); + + + yawHandleRotation = Quat.lookAt(yawCorner, Vec3.sum(yawCorner, upVector), Vec3.subtract(yawCenter,yawCorner)); + yawHandleRotation = Quat.multiply(Quat.angleAxis(45, upVector), yawHandleRotation); + + //Quat.fromPitchYawRollDegrees(0,270,0) + pitchCenter = Vec3.sum(boundsCenter, Vec3.multiply(xSign * (dimensions.x / 2), rightVector)); + rollCenter = Vec3.sum(boundsCenter, Vec3.multiply(zSign * (dimensions.z / 2), frontVector)); + var rotateHandlesVisible = true; var rotationOverlaysVisible = false; @@ -3504,7 +3538,12 @@ SelectionDisplay = (function() { rotationNormal = { x: 0, y: 0, z: 0 }; rotationNormal[rotAroundAxis] = 1; //get the correct axis according to the avatar referencial - rotationNormal = Vec3.multiplyQbyV(MyAvatar.orientation, rotationNormal); + var avatarReferential = Quat.multiply(MyAvatar.orientation, Quat.fromVec3Degrees({ + x: 0, + y: 180, + z: 0 + })); + rotationNormal = Vec3.multiplyQbyV(avatarReferential, rotationNormal); // Size the overlays to the current selection size var diagonal = (Vec3.length(SelectionManager.worldDimensions) / 2) * 1.1; From b481a6060e867e44f6ac550414cc7388da8320ad Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 22 Nov 2017 18:15:42 -0700 Subject: [PATCH 03/21] improve audio scope --- interface/src/audio/AudioScope.cpp | 126 ++++++++++++------------ interface/src/audio/AudioScope.h | 55 ++++++++++- interface/src/ui/ApplicationOverlay.cpp | 20 ---- interface/src/ui/ApplicationOverlay.h | 1 - 4 files changed, 112 insertions(+), 90 deletions(-) diff --git a/interface/src/audio/AudioScope.cpp b/interface/src/audio/AudioScope.cpp index cf9984e32b..5fbb5cdaf6 100644 --- a/interface/src/audio/AudioScope.cpp +++ b/interface/src/audio/AudioScope.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include @@ -28,6 +29,8 @@ static const unsigned int SCOPE_HEIGHT = 2 * 15 * MULTIPLIER_SCOPE_HEIGHT; AudioScope::AudioScope() : _isEnabled(false), _isPaused(false), + _isTriggered(false), + _autoTrigger(false), _scopeInputOffset(0), _scopeOutputOffset(0), _framesPerScope(DEFAULT_FRAMES_PER_SCOPE), @@ -108,63 +111,19 @@ void AudioScope::freeScope() { } } -void AudioScope::render(RenderArgs* renderArgs, int width, int height) { - - if (!_isEnabled) { - return; - } - - static const glm::vec4 backgroundColor = { 0.4f, 0.4f, 0.4f, 0.6f }; - static const glm::vec4 gridColor = { 0.7f, 0.7f, 0.7f, 1.0f }; - static const glm::vec4 inputColor = { 0.3f, 1.0f, 0.3f, 1.0f }; - static const glm::vec4 outputLeftColor = { 1.0f, 0.3f, 0.3f, 1.0f }; - static const glm::vec4 outputRightColor = { 0.3f, 0.3f, 1.0f, 1.0f }; - static const int gridCols = 2; - int gridRows = _framesPerScope; - - int x = (width - (int)SCOPE_WIDTH) / 2; - int y = (height - (int)SCOPE_HEIGHT) / 2; - int w = (int)SCOPE_WIDTH; - int h = (int)SCOPE_HEIGHT; - - gpu::Batch& batch = *renderArgs->_batch; - auto geometryCache = DependencyManager::get(); - - // Grid uses its own pipeline, so draw it before setting another - const float GRID_EDGE = 0.005f; - geometryCache->renderGrid(batch, glm::vec2(x, y), glm::vec2(x + w, y + h), - gridRows, gridCols, GRID_EDGE, gridColor, true, _audioScopeGrid); - - geometryCache->useSimpleDrawPipeline(batch); - auto textureCache = DependencyManager::get(); - batch.setResourceTexture(0, textureCache->getWhiteTexture()); - - // FIXME - do we really need to reset this here? we know that we're called inside of ApplicationOverlay::renderOverlays - // which already set up our batch for us to have these settings - mat4 legacyProjection = glm::ortho(0, width, height, 0, -1000, 1000); - batch.setProjectionTransform(legacyProjection); - batch.setModelTransform(Transform()); - batch.resetViewTransform(); - - geometryCache->renderQuad(batch, x, y, w, h, backgroundColor, _audioScopeBackground); - renderLineStrip(batch, _inputID, inputColor, x, y, _samplesPerScope, _scopeInputOffset, _scopeInput); - renderLineStrip(batch, _outputLeftID, outputLeftColor, x, y, _samplesPerScope, _scopeOutputOffset, _scopeOutputLeft); - renderLineStrip(batch, _outputRightD, outputRightColor, x, y, _samplesPerScope, _scopeOutputOffset, _scopeOutputRight); +void AudioScope::setTriggerValues(float x, float y) { + _triggerValues.x = x; + _triggerValues.y = y; } -void AudioScope::renderLineStrip(gpu::Batch& batch, int id, const glm::vec4& color, int x, int y, int n, int offset, const QByteArray* byteArray) { - +QVector AudioScope::getScopeVector(const QByteArray* byteArray, int offset) { int16_t sample; - int16_t* samples = ((int16_t*) byteArray->data()) + offset; + QVector points; + if (!_isEnabled || byteArray == NULL) return points; + int16_t* samples = ((int16_t*)byteArray->data()) + offset; int numSamplesToAverage = _framesPerScope / DEFAULT_FRAMES_PER_SCOPE; - int count = (n - offset) / numSamplesToAverage; - int remainder = (n - offset) % numSamplesToAverage; - y += SCOPE_HEIGHT / 2; - - auto geometryCache = DependencyManager::get(); - - QVector points; - + int count = (_samplesPerScope - offset) / numSamplesToAverage; + int remainder = (_samplesPerScope - offset) % numSamplesToAverage; // Compute and draw the sample averages from the offset position for (int i = count; --i >= 0; ) { @@ -173,7 +132,7 @@ void AudioScope::renderLineStrip(gpu::Batch& batch, int id, const glm::vec4& col sample += *samples++; } sample /= numSamplesToAverage; - points << glm::vec2(x++, y - sample); + points << -sample; } // Compute and draw the sample average across the wrap boundary @@ -182,16 +141,17 @@ void AudioScope::renderLineStrip(gpu::Batch& batch, int id, const glm::vec4& col for (int j = remainder; --j >= 0; ) { sample += *samples++; } - - samples = (int16_t*) byteArray->data(); + + samples = (int16_t*)byteArray->data(); for (int j = numSamplesToAverage - remainder; --j >= 0; ) { sample += *samples++; } sample /= numSamplesToAverage; - points << glm::vec2(x++, y - sample); - } else { - samples = (int16_t*) byteArray->data(); + points << -sample; + } + else { + samples = (int16_t*)byteArray->data(); } // Compute and draw the sample average from the beginning to the offset @@ -202,12 +162,46 @@ void AudioScope::renderLineStrip(gpu::Batch& batch, int id, const glm::vec4& col sample += *samples++; } sample /= numSamplesToAverage; - points << glm::vec2(x++, y - sample); + + points << -sample; + } + return points; +} + +bool AudioScope::shouldTrigger(const QVector& scope) { + int threshold = 4; + if (_autoTrigger && _triggerValues.x < scope.size()) { + for (int i = -2*threshold; i < +2*threshold*2; i++) { + int idx = _triggerValues.x + i; + idx = (idx < 0) ? 0 : (idx < scope.size() ? idx : scope.size() - 1); + int dif = abs(_triggerValues.y - scope[idx]); + if (dif < threshold) { + return true; + } + } + } + return false; +} + +void AudioScope::computeInputData() { + _scopeInputData = getScopeVector(_scopeInput, _scopeInputOffset); + if (shouldTrigger(_scopeInputData)) { + _triggerInputData = _scopeInputData; + _isTriggered = true; + } +} + +void AudioScope::computeOutputData() { + _scopeOutputLeftData = getScopeVector(_scopeOutputLeft, _scopeOutputOffset); + if (shouldTrigger(_scopeOutputLeftData)) { + _triggerOutputLeftData = _scopeOutputLeftData; + _isTriggered = true; + } + _scopeOutputRightData = getScopeVector(_scopeOutputRight, _scopeOutputOffset); + if (shouldTrigger(_scopeOutputRightData)) { + _triggerOutputRightData = _scopeOutputRightData; + _isTriggered = true; } - - - geometryCache->updateVertices(id, points, color); - geometryCache->renderVertices(batch, gpu::LINE_STRIP, id); } int AudioScope::addBufferToScope(QByteArray* byteArray, int frameOffset, const int16_t* source, int sourceSamplesPerChannel, @@ -231,7 +225,7 @@ int AudioScope::addBufferToScope(QByteArray* byteArray, int frameOffset, const i } int AudioScope::addSilenceToScope(QByteArray* byteArray, int frameOffset, int silentSamples) { - + // Short int pointer to mapped samples in byte array int16_t* destination = (int16_t*)byteArray->data(); @@ -271,6 +265,7 @@ void AudioScope::addStereoSamplesToScope(const QByteArray& samples) { _scopeOutputOffset = addBufferToScope(_scopeOutputRight, _scopeOutputOffset, samplesData, samplesPerChannel, 1, AudioConstants::STEREO); _scopeLastFrame = samples.right(AudioConstants::NETWORK_FRAME_BYTES_STEREO); + computeOutputData(); } void AudioScope::addLastFrameRepeatedWithFadeToScope(int samplesPerChannel) { @@ -302,4 +297,5 @@ void AudioScope::addInputToScope(const QByteArray& inputSamples) { _scopeInputOffset = addBufferToScope(_scopeInput, _scopeInputOffset, reinterpret_cast(inputSamples.data()), inputSamples.size() / sizeof(int16_t), INPUT_AUDIO_CHANNEL, NUM_INPUT_CHANNELS); + computeInputData(); } diff --git a/interface/src/audio/AudioScope.h b/interface/src/audio/AudioScope.h index e0c8840bb2..dfc198279f 100644 --- a/interface/src/audio/AudioScope.h +++ b/interface/src/audio/AudioScope.h @@ -24,14 +24,21 @@ class AudioScope : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY + + Q_PROPERTY(QVector scopeInput READ getScopeInput) + Q_PROPERTY(QVector scopeOutputLeft READ getScopeOutputLeft) + Q_PROPERTY(QVector scopeOutputRight READ getScopeOutputRight) + + Q_PROPERTY(QVector triggerInput READ getTriggerInput) + Q_PROPERTY(QVector triggerOutputLeft READ getTriggerOutputLeft) + Q_PROPERTY(QVector triggerOutputRight READ getTriggerOutputRight) + public: // Audio scope methods for allocation/deallocation void allocateScope(); void freeScope(); void reallocateScope(int frames); - void render(RenderArgs* renderArgs, int width, int height); - public slots: void toggle() { setVisible(!_isEnabled); } void setVisible(bool visible); @@ -41,9 +48,33 @@ public slots: void setPause(bool paused) { _isPaused = paused; } bool getPause() { return _isPaused; } + void toggleTrigger() { _autoTrigger = !_autoTrigger; } + bool getAutoTrigger() { return _autoTrigger; } + void setAutoTrigger(bool autoTrigger) { + _isTriggered = false; + _autoTrigger = autoTrigger; + } + + void setTriggered(bool triggered) { _isTriggered = triggered; } + bool getTriggered() { return _isTriggered; } + void selectAudioScopeFiveFrames(); void selectAudioScopeTwentyFrames(); void selectAudioScopeFiftyFrames(); + + void setEnabled(bool enabled) { _isEnabled = enabled; } + bool getEnabled() { return _isEnabled; } + const int getAudioScopeBackground() { return _audioScopeBackground; } + + QVector getScopeInput() { return _scopeInputData; }; + QVector getScopeOutputLeft() { return _scopeOutputLeftData; }; + QVector getScopeOutputRight() { return _scopeOutputRightData; }; + + QVector getTriggerInput() { return _triggerInputData; }; + QVector getTriggerOutputLeft() { return _triggerOutputLeftData; }; + QVector getTriggerOutputRight() { return _triggerOutputRightData; }; + + void setTriggerValues(float x, float y); protected: AudioScope(); @@ -55,16 +86,21 @@ private slots: void addInputToScope(const QByteArray& inputSamples); private: - // Audio scope methods for rendering - void renderLineStrip(gpu::Batch& batch, int id, const glm::vec4& color, int x, int y, int n, int offset, const QByteArray* byteArray); // Audio scope methods for data acquisition int addBufferToScope(QByteArray* byteArray, int frameOffset, const int16_t* source, int sourceSamples, unsigned int sourceChannel, unsigned int sourceNumberOfChannels, float fade = 1.0f); int addSilenceToScope(QByteArray* byteArray, int frameOffset, int silentSamples); + QVector getScopeVector(const QByteArray* scope, int offset); + + bool shouldTrigger(const QVector& scope); + void computeInputData(); + void computeOutputData(); + bool _isEnabled; bool _isPaused; + bool _isTriggered; int _scopeInputOffset; int _scopeOutputOffset; int _framesPerScope; @@ -73,6 +109,17 @@ private: QByteArray* _scopeOutputLeft; QByteArray* _scopeOutputRight; QByteArray _scopeLastFrame; + + QVector _scopeInputData; + QVector _scopeOutputLeftData; + QVector _scopeOutputRightData; + + QVector _triggerInputData; + QVector _triggerOutputLeftData; + QVector _triggerOutputRightData; + + bool _autoTrigger; + glm::vec2 _triggerValues; int _audioScopeBackground; int _audioScopeGrid; diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index a99fe002ee..52b53a3298 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -82,7 +82,6 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { // Now render the overlay components together into a single texture renderDomainConnectionStatusBorder(renderArgs); // renders the connected domain line - renderAudioScope(renderArgs); // audio scope in the very back - NOTE: this is the debug audio scope, not the VU meter renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope renderQmlUi(renderArgs); // renders a unit quad with the QML UI texture, and the text overlays from scripts }); @@ -118,25 +117,6 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) { geometryCache->renderUnitQuad(batch, glm::vec4(1), _qmlGeometryId); } -void ApplicationOverlay::renderAudioScope(RenderArgs* renderArgs) { - PROFILE_RANGE(app, __FUNCTION__); - - gpu::Batch& batch = *renderArgs->_batch; - auto geometryCache = DependencyManager::get(); - geometryCache->useSimpleDrawPipeline(batch); - auto textureCache = DependencyManager::get(); - batch.setResourceTexture(0, textureCache->getWhiteTexture()); - int width = renderArgs->_viewport.z; - int height = renderArgs->_viewport.w; - mat4 legacyProjection = glm::ortho(0, width, height, 0, ORTHO_NEAR_CLIP, ORTHO_FAR_CLIP); - batch.setProjectionTransform(legacyProjection); - batch.setModelTransform(Transform()); - batch.resetViewTransform(); - - // Render the audio scope - DependencyManager::get()->render(renderArgs, width, height); -} - void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) { PROFILE_RANGE(app, __FUNCTION__); diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index af4d8779d4..0d30123c61 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -32,7 +32,6 @@ private: void renderStatsAndLogs(RenderArgs* renderArgs); void renderDomainConnectionStatusBorder(RenderArgs* renderArgs); void renderQmlUi(RenderArgs* renderArgs); - void renderAudioScope(RenderArgs* renderArgs); void renderOverlays(RenderArgs* renderArgs); void buildFramebufferObject(); From 5bf83671263fae9b98bab3283f797e38e594ac2c Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 22 Nov 2017 18:17:50 -0700 Subject: [PATCH 04/21] add resources --- interface/resources/qml/AudioScope.qml | 268 ++++++++++++++++++ .../developer/utilities/audio/audioScope.js | 10 + 2 files changed, 278 insertions(+) create mode 100644 interface/resources/qml/AudioScope.qml create mode 100644 scripts/developer/utilities/audio/audioScope.js diff --git a/interface/resources/qml/AudioScope.qml b/interface/resources/qml/AudioScope.qml new file mode 100644 index 0000000000..8956a20643 --- /dev/null +++ b/interface/resources/qml/AudioScope.qml @@ -0,0 +1,268 @@ +// +// AudioScope.qml +// +// Created by Luis Cuenca on 11/22/2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import "styles-uit" +import "controls-uit" as HifiControlsUit + +Item { + id: root + width: parent.width + height: parent.height + + property var _scopedata + property var _triggerdata + property var _triggerValues + property var _triggered + property var _steps + + Component.onCompleted: { + // createValues(); + _triggerValues = { x: width/2, y: height/3 } + _triggered = false + _steps = 5 + } + + + function pullFreshValues() { + if (!AudioScope.getPause()){ + _scopedata = AudioScope.scopeInput + if (AudioScope.getTriggered()) { + _triggered = true + _triggerdata = AudioScope.triggerInput + } + } + mycanvas.requestPaint() + } + + Timer { + interval: 10; running: true; repeat: true + onTriggered: pullFreshValues() + } + + Canvas { + id: mycanvas + anchors.fill:parent + + onPaint: { + var lineHeight = 12; + + function displayTrigger(ctx) { + var size = 3; + ctx.lineWidth="3"; + ctx.strokeStyle="#EFB400"; + ctx.beginPath(); + ctx.moveTo(_triggerValues.x - (size + 2), _triggerValues.y); + ctx.lineTo(_triggerValues.x - 2, _triggerValues.y); + ctx.moveTo(_triggerValues.x + 2, _triggerValues.y); + ctx.lineTo(_triggerValues.x + (size + 2), _triggerValues.y); + + ctx.moveTo(_triggerValues.x, _triggerValues.y - (size + 2)); + ctx.lineTo(_triggerValues.x, _triggerValues.y - 2); + ctx.moveTo(_triggerValues.x, _triggerValues.y + 2); + ctx.lineTo(_triggerValues.x, _triggerValues.y + (size + 2)); + + ctx.stroke(); + } + + function displayBackground(ctx, datawidth, steps, color) { + ctx.fillStyle = Qt.rgba(0, 0, 0, 1); + ctx.fillRect(0, 0, width, height); + + ctx.strokeStyle= color; + ctx.lineWidth="1"; + + ctx.moveTo(0, height/2); + ctx.lineTo(datawidth, height/2); + + var gap = datawidth/steps; + for (var i = 0; i < steps; i++) { + ctx.moveTo(i*gap + 1, 100); + ctx.lineTo(i*gap + 1, height-100); + } + ctx.moveTo(datawidth-1, 100); + ctx.lineTo(datawidth-1, height-100); + ctx.stroke(); + + } + + function drawScope(ctx, data, width, color) { + ctx.beginPath(); + ctx.strokeStyle = color; // Green path + ctx.lineWidth=width; + var x = 0; + for (var i = 0; i < data.length-1; i++) { + ctx.moveTo(x, data[i] + height/2); + ctx.lineTo(++x, data[i+1] + height/2); + } + ctx.stroke(); + } + + + + var ctx = getContext("2d"); + + displayBackground(ctx, _scopedata.length, _steps, "#555555"); + + drawScope(ctx, _scopedata, "2", "#00B4EF"); + if (_triggered) { + drawScope(ctx, _triggerdata, "1", "#EF0000"); + } + if (AudioScope.getAutoTrigger()) { + displayTrigger(ctx); + } + } + } + + MouseArea { + id: hitbox + anchors.fill: mycanvas + hoverEnabled: true + onClicked: { + _triggerValues.x = mouseX + _triggerValues.y = mouseY + AudioScope.setTriggerValues(mouseX, mouseY-height/2); + } + } + + HifiControlsUit.CheckBox { + id: activated + boxSize: 20 + anchors.top: parent.top; + anchors.left: parent.left; + anchors.topMargin: 20; + anchors.leftMargin: 20; + checked: AudioScope.getEnabled(); + onCheckedChanged: { + AudioScope.setEnabled(checked); + activelabel.text = AudioScope.getEnabled() ? "On" : "Off" + } + } + + HifiControlsUit.Label { + id: activelabel + text: AudioScope.getEnabled() ? "On" : "Off" + anchors.top: activated.top; + anchors.left: activated.right; + } + + HifiControlsUit.Button { + id: pauseButton; + color: hifi.buttons.black; + colorScheme: hifi.colorSchemes.dark; + anchors.right: parent.right; + anchors.bottom: parent.bottom; + anchors.rightMargin: 30; + anchors.bottomMargin: 8; + height: 26; + text: "Pause Scope"; + onClicked: { + AudioScope.togglePause(); + pauseButton.text = AudioScope.getPause() ? "Continue Scope" : "Pause Scope"; + } + } + + + + HifiControlsUit.CheckBox { + id: twentyFrames + boxSize: 20 + anchors.horizontalCenter: parent.horizontalCenter; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 8; + onCheckedChanged: { + if (checked){ + fiftyFrames.checked = false; + fiveFrames.checked = false; + AudioScope.selectAudioScopeTwentyFrames(); + _steps = 20; + _triggered = false; + AudioScope.setTriggered(false); + } + } + } + HifiControlsUit.Label { + text: "20 Frames"; + anchors.horizontalCenter: twentyFrames.horizontalCenter; + anchors.bottom: twentyFrames.top; + } + + HifiControlsUit.CheckBox { + id: fiveFrames + boxSize: 20 + anchors.bottom: twentyFrames.bottom; + anchors.right: twentyFrames.left; + anchors.rightMargin: 80; + checked: true; + onCheckedChanged: { + if (checked) { + fiftyFrames.checked = false; + twentyFrames.checked = false; + AudioScope.selectAudioScopeFiveFrames(); + _steps = 5; + _triggered = false; + AudioScope.setTriggered(false); + } + } + } + + HifiControlsUit.Label { + text: "5 Frames"; + anchors.horizontalCenter: fiveFrames.horizontalCenter; + anchors.bottom: fiveFrames.top; + } + + HifiControlsUit.CheckBox { + id: fiftyFrames + boxSize: 20 + anchors.bottom: twentyFrames.bottom; + anchors.left: twentyFrames.right; + anchors.leftMargin: 80; + onCheckedChanged: { + if (checked) { + twentyFrames.checked = false; + fiveFrames.checked = false; + AudioScope.selectAudioScopeFiftyFrames(); + _steps = 50; + _triggered = false; + AudioScope.setTriggered(false); + } + } + } + + HifiControlsUit.Label { + text: "50 Frames"; + anchors.horizontalCenter: fiftyFrames.horizontalCenter; + anchors.bottom: fiftyFrames.top; + } + + HifiControlsUit.Switch { + id: triggerSwitch; + height: 26; + anchors.left: parent.left; + anchors.bottom: parent.bottom; + anchors.leftMargin: 75; + anchors.bottomMargin: 8; + labelTextOff: "Off"; + labelTextOn: "On"; + onCheckedChanged: { + if (!checked) AudioScope.setPause(false); + _triggered = false; + AudioScope.setTriggered(false); + AudioScope.setAutoTrigger(checked); + } + } + HifiControlsUit.Label { + text: "Trigger"; + anchors.left: triggerSwitch.left; + anchors.leftMargin: -15; + anchors.bottom: triggerSwitch.top; + } +} diff --git a/scripts/developer/utilities/audio/audioScope.js b/scripts/developer/utilities/audio/audioScope.js new file mode 100644 index 0000000000..3331482fbd --- /dev/null +++ b/scripts/developer/utilities/audio/audioScope.js @@ -0,0 +1,10 @@ +var qml = Script.resourcesPath() + '/qml/AudioScope.qml'; +var viewdim = Controller.getViewportDimensions(); +var window = new OverlayWindow({ + title: 'Audio Scope', + source: qml, + width: 1200, + height: 500 +}); +//window.setPosition(0.1*viewdim, 0.2*viewdim); +window.closed.connect(function () { Script.stop(); }); \ No newline at end of file From 69299bdd68353c6ec9881986dfd8d39c4cfd5c6e Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 22 Nov 2017 19:25:24 -0700 Subject: [PATCH 05/21] try to fix errors --- interface/src/audio/AudioScope.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interface/src/audio/AudioScope.h b/interface/src/audio/AudioScope.h index dfc198279f..a0f1a620cb 100644 --- a/interface/src/audio/AudioScope.h +++ b/interface/src/audio/AudioScope.h @@ -64,7 +64,6 @@ public slots: void setEnabled(bool enabled) { _isEnabled = enabled; } bool getEnabled() { return _isEnabled; } - const int getAudioScopeBackground() { return _audioScopeBackground; } QVector getScopeInput() { return _scopeInputData; }; QVector getScopeOutputLeft() { return _scopeOutputLeftData; }; @@ -101,10 +100,12 @@ private: bool _isEnabled; bool _isPaused; bool _isTriggered; + bool _autoTrigger; int _scopeInputOffset; int _scopeOutputOffset; int _framesPerScope; int _samplesPerScope; + QByteArray* _scopeInput; QByteArray* _scopeOutputLeft; QByteArray* _scopeOutputRight; @@ -118,7 +119,7 @@ private: QVector _triggerOutputLeftData; QVector _triggerOutputRightData; - bool _autoTrigger; + glm::vec2 _triggerValues; int _audioScopeBackground; From 027d2d323c18289b1f8b9aeb5fa08c26ecf241df Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Sun, 26 Nov 2017 22:28:34 -0700 Subject: [PATCH 06/21] Measure tool and corrections --- interface/resources/qml/AudioScope.qml | 403 ++++++++++++++++++----- interface/src/Menu.cpp | 11 +- interface/src/audio/AudioScope.cpp | 35 +- interface/src/audio/AudioScope.h | 27 +- libraries/audio-client/src/AudioClient.h | 3 + 5 files changed, 372 insertions(+), 107 deletions(-) diff --git a/interface/resources/qml/AudioScope.qml b/interface/resources/qml/AudioScope.qml index 8956a20643..6f78a2d820 100644 --- a/interface/resources/qml/AudioScope.qml +++ b/interface/resources/qml/AudioScope.qml @@ -17,33 +17,92 @@ Item { width: parent.width height: parent.height - property var _scopedata - property var _triggerdata - property var _triggerValues - property var _triggered + property var _scopeInputData + property var _scopeOutputLeftData + property var _scopeOutputRightData + + property var _triggerInputData + property var _triggerOutputLeftData + property var _triggerOutputRightData + + property var _triggerValues: QtObject{ + property int x: parent.width/2 + property int y: parent.height/3 + } + + property var _triggered: false property var _steps + property var _refreshMs: 10 + property var _framesPerSecond: AudioScope.getFramesPerSecond() + property var _isFrameUnits: true - Component.onCompleted: { - // createValues(); - _triggerValues = { x: width/2, y: height/3 } - _triggered = false - _steps = 5 + property var _holdStart: QtObject{ + property int x: 0 + property int y: 0 } + property var _holdEnd: QtObject{ + property int x: 0 + property int y: 0 + } + property var _timeBeforeHold: 100; + property var _pressedTime: 0; + property var _isPressed: false; + + function isHolding() { + return (_pressedTime > _timeBeforeHold); + } + + function updateMeasureUnits() { + timeButton.text = _isFrameUnits ? "Display Frames" : "Milliseconds"; + fiveLabel.text = _isFrameUnits ? "5" : "" + (Math.round(1000 * 5.0/_framesPerSecond)); + twentyLabel.text = _isFrameUnits ? "20" : "" + (Math.round(1000 * 20.0/_framesPerSecond)); + fiftyLabel.text = _isFrameUnits ? "50" : "" + (Math.round(1000 * 50.0/_framesPerSecond)); + } + + function collectScopeData() { + if (inputCh.checked) { + _scopeInputData = AudioScope.scopeInput; + } + if (outputLeftCh.checked) { + _scopeOutputLeftData = AudioScope.scopeOutputLeft; + } + if (outputRightCh.checked) { + _scopeOutputRightData = AudioScope.scopeOutputRight; + } + } + + function collectTriggerData() { + if (inputCh.checked) { + _triggerInputData = AudioScope.triggerInput; + } + if (outputLeftCh.checked) { + _triggerOutputLeftData = AudioScope.triggerOutputLeft; + } + if (outputRightCh.checked) { + _triggerOutputRightData = AudioScope.triggerOutputRight; + } + } + function pullFreshValues() { - if (!AudioScope.getPause()){ - _scopedata = AudioScope.scopeInput + if (!AudioScope.getPause()){ if (AudioScope.getTriggered()) { - _triggered = true - _triggerdata = AudioScope.triggerInput - } + _triggered = true; + collectTriggerData(); + } else { + collectScopeData(); + } } - mycanvas.requestPaint() + if (inputCh.checked || outputLeftCh.checked || outputRightCh.checked) { + mycanvas.requestPaint(); + } } + + Timer { - interval: 10; running: true; repeat: true + interval: _refreshMs; running: true; repeat: true onTriggered: pullFreshValues() } @@ -52,51 +111,74 @@ Item { anchors.fill:parent onPaint: { - var lineHeight = 12; - - function displayTrigger(ctx) { - var size = 3; - ctx.lineWidth="3"; - ctx.strokeStyle="#EFB400"; - ctx.beginPath(); - ctx.moveTo(_triggerValues.x - (size + 2), _triggerValues.y); - ctx.lineTo(_triggerValues.x - 2, _triggerValues.y); - ctx.moveTo(_triggerValues.x + 2, _triggerValues.y); - ctx.lineTo(_triggerValues.x + (size + 2), _triggerValues.y); + + function displayMeasureArea(ctx) { - ctx.moveTo(_triggerValues.x, _triggerValues.y - (size + 2)); - ctx.lineTo(_triggerValues.x, _triggerValues.y - 2); - ctx.moveTo(_triggerValues.x, _triggerValues.y + 2); - ctx.lineTo(_triggerValues.x, _triggerValues.y + (size + 2)); + ctx.fillStyle = Qt.rgba(0.1, 0.1, 0.1, 1); + ctx.fillRect(_holdStart.x, 0, _holdEnd.x - _holdStart.x, height); + + ctx.lineWidth = "2"; + ctx.strokeStyle = "#555555"; + + ctx.beginPath(); + ctx.moveTo(_holdStart.x, 0); + ctx.lineTo(_holdStart.x, height); + ctx.moveTo(_holdEnd.x, 0); + ctx.lineTo(_holdEnd.x, height); + + ctx.moveTo(_holdStart.x, _holdStart.y); + ctx.lineTo(_holdEnd.x, _holdStart.y); + ctx.moveTo(_holdEnd.x, _holdEnd.y); + ctx.lineTo(_holdStart.x, _holdEnd.y); + + ctx.stroke(); + } + + function displayTrigger(ctx, lineWidth, color) { + var crossSize = 3; + var holeSize = 2; + + ctx.lineWidth = lineWidth; + ctx.strokeStyle = color; + + ctx.beginPath(); + ctx.moveTo(_triggerValues.x - (crossSize + holeSize), _triggerValues.y); + ctx.lineTo(_triggerValues.x - holeSize, _triggerValues.y); + ctx.moveTo(_triggerValues.x + holeSize, _triggerValues.y); + ctx.lineTo(_triggerValues.x + (crossSize + holeSize), _triggerValues.y); + + ctx.moveTo(_triggerValues.x, _triggerValues.y - (crossSize + holeSize)); + ctx.lineTo(_triggerValues.x, _triggerValues.y - holeSize); + ctx.moveTo(_triggerValues.x, _triggerValues.y + holeSize); + ctx.lineTo(_triggerValues.x, _triggerValues.y + (crossSize + holeSize)); ctx.stroke(); } - function displayBackground(ctx, datawidth, steps, color) { - ctx.fillStyle = Qt.rgba(0, 0, 0, 1); - ctx.fillRect(0, 0, width, height); + function displayBackground(ctx, datawidth, steps, lineWidth, color) { + var verticalPadding = 100; - ctx.strokeStyle= color; - ctx.lineWidth="1"; + ctx.strokeStyle = color; + ctx.lineWidth = lineWidth; ctx.moveTo(0, height/2); ctx.lineTo(datawidth, height/2); var gap = datawidth/steps; for (var i = 0; i < steps; i++) { - ctx.moveTo(i*gap + 1, 100); - ctx.lineTo(i*gap + 1, height-100); + ctx.moveTo(i*gap + 1, verticalPadding); + ctx.lineTo(i*gap + 1, height-verticalPadding); } - ctx.moveTo(datawidth-1, 100); - ctx.lineTo(datawidth-1, height-100); - ctx.stroke(); + ctx.moveTo(datawidth-1, verticalPadding); + ctx.lineTo(datawidth-1, height-verticalPadding); + ctx.stroke(); } function drawScope(ctx, data, width, color) { ctx.beginPath(); - ctx.strokeStyle = color; // Green path - ctx.lineWidth=width; + ctx.strokeStyle = color; + ctx.lineWidth = width; var x = 0; for (var i = 0; i < data.length-1; i++) { ctx.moveTo(x, data[i] + height/2); @@ -105,18 +187,91 @@ Item { ctx.stroke(); } + function getMeasurementText(dist) { + var datasize = _scopeInputData.length; + var value = 0; + if (fiveFrames.checked) { + value = (_isFrameUnits) ? 5.0*dist/datasize : (Math.round(1000 * 5.0/_framesPerSecond))*dist/datasize; + } else if (twentyFrames.checked) { + value = (_isFrameUnits) ? 20.0*dist/datasize : (Math.round(1000 * 20.0/_framesPerSecond))*dist/datasize; + } else if (fiftyFrames.checked) { + value = (_isFrameUnits) ? 50.0*dist/datasize : (Math.round(1000 * 50.0/_framesPerSecond))*dist/datasize; + } + value = Math.abs(Math.round(value*100)/100); + var measureText = "" + value + (_isFrameUnits ? " frames" : " milliseconds"); + return measureText; + } + function drawMeasurements(ctx, color) { + ctx.fillStyle = color; + ctx.font = "normal 16px sans-serif"; + var fontwidth = 8; + var measureText = getMeasurementText(_holdEnd.x - _holdStart.x); + if (_holdStart.x < _holdEnd.x) { + ctx.fillText("" + height/2 - _holdStart.y, _holdStart.x-40, _holdStart.y); + ctx.fillText("" + height/2 - _holdEnd.y, _holdStart.x-40, _holdEnd.y); + ctx.fillText(measureText, _holdEnd.x+10, _holdEnd.y); + } else { + ctx.fillText("" + height/2 - _holdStart.y, _holdStart.x+10, _holdStart.y); + ctx.fillText("" + height/2 - _holdEnd.y, _holdStart.x+10, _holdEnd.y); + ctx.fillText(measureText, _holdEnd.x-fontwidth*measureText.length, _holdEnd.y); + } + } var ctx = getContext("2d"); - - displayBackground(ctx, _scopedata.length, _steps, "#555555"); + + ctx.fillStyle = Qt.rgba(0, 0, 0, 1); + ctx.fillRect(0, 0, width, height); - drawScope(ctx, _scopedata, "2", "#00B4EF"); - if (_triggered) { - drawScope(ctx, _triggerdata, "1", "#EF0000"); + if (isHolding()) { + displayMeasureArea(ctx); } + + var guideLinesColor = "#555555" + var guideLinesWidth = "1" + + displayBackground(ctx, _scopeInputData.length, _steps, guideLinesWidth, guideLinesColor); + + var triggerWidth = "3" + var triggerColor = "#EFB400" + if (AudioScope.getAutoTrigger()) { - displayTrigger(ctx); + displayTrigger(ctx, triggerWidth, triggerColor); + } + + var scopeWidth = "2" + var scopeInputColor = "#00B4EF" + var scopeOutputLeftColor = "#BB0000" + var scopeOutputRightColor = "#00BB00" + + if (!_triggered) { + if (inputCh.checked) { + drawScope(ctx, _scopeInputData, scopeWidth, scopeInputColor); + } + if (outputLeftCh.checked) { + drawScope(ctx, _scopeOutputLeftData, scopeWidth, scopeOutputLeftColor); + } + if (outputRightCh.checked) { + drawScope(ctx, _scopeOutputRightData, scopeWidth, scopeOutputRightColor); + } + } else { + if (inputCh.checked) { + drawScope(ctx, _triggerInputData, scopeWidth, scopeInputColor); + } + if (outputLeftCh.checked) { + drawScope(ctx, _triggerOutputLeftData, scopeWidth, scopeOutputLeftColor); + } + if (outputRightCh.checked) { + drawScope(ctx, _triggerOutputRightData, scopeWidth, scopeOutputRightColor); + } + } + + if (isHolding()) { + drawMeasurements(ctx, "#eeeeee"); + } + + if (_isPressed) { + _pressedTime += _refreshMs; } } } @@ -125,10 +280,24 @@ Item { id: hitbox anchors.fill: mycanvas hoverEnabled: true - onClicked: { - _triggerValues.x = mouseX - _triggerValues.y = mouseY - AudioScope.setTriggerValues(mouseX, mouseY-height/2); + onPressed: { + _isPressed = true; + _pressedTime = 0; + _holdStart.x = mouseX; + _holdStart.y = mouseY; + } + onPositionChanged: { + _holdEnd.x = mouseX; + _holdEnd.y = mouseY; + } + onReleased: { + if (!isHolding() && AudioScope.getAutoTrigger()) { + _triggerValues.x = mouseX + _triggerValues.y = mouseY + AudioScope.setTriggerValues(mouseX, mouseY-height/2); + } + _isPressed = false; + _pressedTime = 0; } } @@ -139,18 +308,60 @@ Item { anchors.left: parent.left; anchors.topMargin: 20; anchors.leftMargin: 20; - checked: AudioScope.getEnabled(); + checked: AudioScope.getVisible(); + onCheckedChanged: { + AudioScope.setVisible(!AudioScope.getVisible()); + activelabel.text = AudioScope.getVisible() ? "On" : "Off" + } + } + + HifiControlsUit.Label { + id: activelabel + text: AudioScope.getVisible() ? "On" : "Off" + anchors.top: activated.top; + anchors.left: activated.right; + } + + HifiControlsUit.CheckBox { + id: outputLeftCh + boxSize: 20 + text: "Output L" + anchors.horizontalCenter: parent.horizontalCenter; + anchors.top: parent.top; + anchors.topMargin: 8; + onCheckedChanged: { + AudioScope.setServerEcho(outputLeftCh.checked || outputRightCh.checked); + } + } + HifiControlsUit.Label { + text: "Channels"; + anchors.horizontalCenter: outputLeftCh.horizontalCenter; + anchors.bottom: outputLeftCh.top; + anchors.bottomMargin: 8; + } + + HifiControlsUit.CheckBox { + id: inputCh + boxSize: 20 + text: "Input Mono" + anchors.bottom: outputLeftCh.bottom; + anchors.right: outputLeftCh.left; + anchors.rightMargin: 80; + checked: true; onCheckedChanged: { - AudioScope.setEnabled(checked); - activelabel.text = AudioScope.getEnabled() ? "On" : "Off" } } - HifiControlsUit.Label { - id: activelabel - text: AudioScope.getEnabled() ? "On" : "Off" - anchors.top: activated.top; - anchors.left: activated.right; + HifiControlsUit.CheckBox { + id: outputRightCh + boxSize: 20 + text: "Output R" + anchors.bottom: outputLeftCh.bottom; + anchors.left: outputLeftCh.right; + anchors.leftMargin: 80; + onCheckedChanged: { + AudioScope.setServerEcho(outputLeftCh.checked || outputRightCh.checked); + } } HifiControlsUit.Button { @@ -162,19 +373,16 @@ Item { anchors.rightMargin: 30; anchors.bottomMargin: 8; height: 26; - text: "Pause Scope"; + text: " Pause "; onClicked: { AudioScope.togglePause(); - pauseButton.text = AudioScope.getPause() ? "Continue Scope" : "Pause Scope"; } } - - HifiControlsUit.CheckBox { id: twentyFrames boxSize: 20 - anchors.horizontalCenter: parent.horizontalCenter; + anchors.left: parent.horizontalCenter; anchors.bottom: parent.bottom; anchors.bottomMargin: 8; onCheckedChanged: { @@ -188,18 +396,35 @@ Item { } } } + HifiControlsUit.Label { - text: "20 Frames"; + id:twentyLabel + anchors.left: twentyFrames.right; + anchors.verticalCenter: twentyFrames.verticalCenter; + } + + HifiControlsUit.Button { + id: timeButton; + color: hifi.buttons.black; + colorScheme: hifi.colorSchemes.dark; + text: "Display Frames"; anchors.horizontalCenter: twentyFrames.horizontalCenter; anchors.bottom: twentyFrames.top; + anchors.bottomMargin: 8; + height: 26; + onClicked: { + _isFrameUnits = !_isFrameUnits; + updateMeasureUnits(); + } } HifiControlsUit.CheckBox { id: fiveFrames boxSize: 20 - anchors.bottom: twentyFrames.bottom; - anchors.right: twentyFrames.left; - anchors.rightMargin: 80; + anchors.horizontalCenter: parent.horizontalCenter; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 8; + anchors.horizontalCenterOffset: -50; checked: true; onCheckedChanged: { if (checked) { @@ -214,17 +439,18 @@ Item { } HifiControlsUit.Label { - text: "5 Frames"; - anchors.horizontalCenter: fiveFrames.horizontalCenter; - anchors.bottom: fiveFrames.top; + id:fiveLabel + anchors.left: fiveFrames.right; + anchors.verticalCenter: fiveFrames.verticalCenter; } HifiControlsUit.CheckBox { id: fiftyFrames boxSize: 20 - anchors.bottom: twentyFrames.bottom; - anchors.left: twentyFrames.right; - anchors.leftMargin: 80; + anchors.horizontalCenter: parent.horizontalCenter; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 8; + anchors.horizontalCenterOffset: 70; onCheckedChanged: { if (checked) { twentyFrames.checked = false; @@ -238,9 +464,9 @@ Item { } HifiControlsUit.Label { - text: "50 Frames"; - anchors.horizontalCenter: fiftyFrames.horizontalCenter; - anchors.bottom: fiftyFrames.top; + id:fiftyLabel + anchors.left: fiftyFrames.right; + anchors.verticalCenter: fiftyFrames.verticalCenter; } HifiControlsUit.Switch { @@ -259,10 +485,29 @@ Item { AudioScope.setAutoTrigger(checked); } } + HifiControlsUit.Label { text: "Trigger"; anchors.left: triggerSwitch.left; anchors.leftMargin: -15; anchors.bottom: triggerSwitch.top; } + + Component.onCompleted: { + _steps = AudioScope.getFramesPerScope(); + AudioScope.setTriggerValues(_triggerValues.x, _triggerValues.y-root.height/2); + activated.checked = true; + updateMeasureUnits(); + } + + Component.onDestruction: { + AudioScope.setVisible(false); + } + + Connections { + target: AudioScope + onPauseChanged: { + pauseButton.text = AudioScope.getPause() ? "Continue" : " Pause "; + } + } } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 9ec5cc6034..88ae59a803 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -688,8 +688,15 @@ Menu::Menu() { auto scope = DependencyManager::get(); MenuWrapper* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope"); - addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScope, Qt::CTRL | Qt::Key_F2, false, - scope.data(), SLOT(toggle())); + + action = addActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScope, Qt::CTRL | Qt::Key_F2, false); + connect(action, &QAction::triggered, [] { + auto scriptEngines = DependencyManager::get(); + QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation(); + defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/utilities/audio/audioScope.js"); + scriptEngines->loadScript(defaultScriptsLoc.toString()); + }); + addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopePause, Qt::CTRL | Qt::SHIFT | Qt::Key_F2, false, scope.data(), SLOT(togglePause())); diff --git a/interface/src/audio/AudioScope.cpp b/interface/src/audio/AudioScope.cpp index 5fbb5cdaf6..44f25ae5a9 100644 --- a/interface/src/audio/AudioScope.cpp +++ b/interface/src/audio/AudioScope.cpp @@ -22,7 +22,6 @@ #include "AudioScope.h" static const unsigned int DEFAULT_FRAMES_PER_SCOPE = 5; -static const unsigned int SCOPE_WIDTH = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * DEFAULT_FRAMES_PER_SCOPE; static const unsigned int MULTIPLIER_SCOPE_HEIGHT = 20; static const unsigned int SCOPE_HEIGHT = 2 * 15 * MULTIPLIER_SCOPE_HEIGHT; @@ -46,6 +45,7 @@ AudioScope::AudioScope() : _outputRightD(DependencyManager::get()->allocateID()) { auto audioIO = DependencyManager::get(); + connect(&audioIO->getReceivedAudioStream(), &MixedProcessedAudioStream::addedSilence, this, &AudioScope::addStereoSilenceToScope); connect(&audioIO->getReceivedAudioStream(), &MixedProcessedAudioStream::addedLastFrameRepeatedWithFade, @@ -78,6 +78,14 @@ void AudioScope::selectAudioScopeFiftyFrames() { reallocateScope(50); } +void AudioScope::setServerEcho(bool serverEcho) { + DependencyManager::get()->setServerEcho(serverEcho); +} + +float AudioScope::getFramesPerSecond(){ + return AudioConstants::NETWORK_FRAMES_PER_SEC; +} + void AudioScope::allocateScope() { _scopeInputOffset = 0; _scopeOutputOffset = 0; @@ -111,11 +119,6 @@ void AudioScope::freeScope() { } } -void AudioScope::setTriggerValues(float x, float y) { - _triggerValues.x = x; - _triggerValues.y = y; -} - QVector AudioScope::getScopeVector(const QByteArray* byteArray, int offset) { int16_t sample; QVector points; @@ -171,7 +174,7 @@ QVector AudioScope::getScopeVector(const QByteArray* byteArray, int offset) bool AudioScope::shouldTrigger(const QVector& scope) { int threshold = 4; if (_autoTrigger && _triggerValues.x < scope.size()) { - for (int i = -2*threshold; i < +2*threshold*2; i++) { + for (int i = -4*threshold; i < +4*threshold; i++) { int idx = _triggerValues.x + i; idx = (idx < 0) ? 0 : (idx < scope.size() ? idx : scope.size() - 1); int dif = abs(_triggerValues.y - scope[idx]); @@ -183,24 +186,28 @@ bool AudioScope::shouldTrigger(const QVector& scope) { return false; } +void AudioScope::storeTriggerValues() { + _triggerInputData = _scopeInputData; + _triggerOutputLeftData = _scopeOutputLeftData; + _triggerOutputRightData = _scopeOutputRightData; + _isTriggered = true; +} + void AudioScope::computeInputData() { _scopeInputData = getScopeVector(_scopeInput, _scopeInputOffset); if (shouldTrigger(_scopeInputData)) { - _triggerInputData = _scopeInputData; - _isTriggered = true; + storeTriggerValues(); } } void AudioScope::computeOutputData() { _scopeOutputLeftData = getScopeVector(_scopeOutputLeft, _scopeOutputOffset); if (shouldTrigger(_scopeOutputLeftData)) { - _triggerOutputLeftData = _scopeOutputLeftData; - _isTriggered = true; + storeTriggerValues(); } _scopeOutputRightData = getScopeVector(_scopeOutputRight, _scopeOutputOffset); if (shouldTrigger(_scopeOutputRightData)) { - _triggerOutputRightData = _scopeOutputRightData; - _isTriggered = true; + storeTriggerValues(); } } @@ -225,7 +232,7 @@ int AudioScope::addBufferToScope(QByteArray* byteArray, int frameOffset, const i } int AudioScope::addSilenceToScope(QByteArray* byteArray, int frameOffset, int silentSamples) { - + // Short int pointer to mapped samples in byte array int16_t* destination = (int16_t*)byteArray->data(); diff --git a/interface/src/audio/AudioScope.h b/interface/src/audio/AudioScope.h index a0f1a620cb..33d88f8fe0 100644 --- a/interface/src/audio/AudioScope.h +++ b/interface/src/audio/AudioScope.h @@ -44,27 +44,25 @@ public slots: void setVisible(bool visible); bool getVisible() const { return _isEnabled; } - void togglePause() { _isPaused = !_isPaused; } - void setPause(bool paused) { _isPaused = paused; } + void togglePause() { setPause(!_isPaused); } + void setPause(bool paused) { _isPaused = paused; emit pauseChanged(); } bool getPause() { return _isPaused; } void toggleTrigger() { _autoTrigger = !_autoTrigger; } bool getAutoTrigger() { return _autoTrigger; } - void setAutoTrigger(bool autoTrigger) { - _isTriggered = false; - _autoTrigger = autoTrigger; - } + void setAutoTrigger(bool autoTrigger) { _isTriggered = false; _autoTrigger = autoTrigger; } + void setTriggerValues(int x, int y) { _triggerValues.x = x; _triggerValues.y = y; } void setTriggered(bool triggered) { _isTriggered = triggered; } bool getTriggered() { return _isTriggered; } + float getFramesPerSecond(); + int getFramesPerScope() { return _framesPerScope; } + void selectAudioScopeFiveFrames(); void selectAudioScopeTwentyFrames(); void selectAudioScopeFiftyFrames(); - void setEnabled(bool enabled) { _isEnabled = enabled; } - bool getEnabled() { return _isEnabled; } - QVector getScopeInput() { return _scopeInputData; }; QVector getScopeOutputLeft() { return _scopeOutputLeftData; }; QVector getScopeOutputRight() { return _scopeOutputRightData; }; @@ -73,8 +71,11 @@ public slots: QVector getTriggerOutputLeft() { return _triggerOutputLeftData; }; QVector getTriggerOutputRight() { return _triggerOutputRightData; }; - void setTriggerValues(float x, float y); - + void setServerEcho(bool serverEcho); + +signals: + void pauseChanged(); + protected: AudioScope(); @@ -97,6 +98,8 @@ private: void computeInputData(); void computeOutputData(); + void storeTriggerValues(); + bool _isEnabled; bool _isPaused; bool _isTriggered; @@ -120,7 +123,7 @@ private: QVector _triggerOutputRightData; - glm::vec2 _triggerValues; + glm::ivec2 _triggerValues; int _audioScopeBackground; int _audioScopeGrid; diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 01a487455c..4dd7436251 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -191,6 +191,9 @@ public slots: bool isNoiseReductionEnabled() const { return _isNoiseGateEnabled; } void toggleLocalEcho() { _shouldEchoLocally = !_shouldEchoLocally; } + + bool getServerEcho() { return _shouldEchoToServer; } + void setServerEcho(bool serverEcho) { _shouldEchoToServer = serverEcho; } void toggleServerEcho() { _shouldEchoToServer = !_shouldEchoToServer; } void processReceivedSamples(const QByteArray& inputBuffer, QByteArray& outputBuffer); From ad02118588ce445c82ebe981cc4f7d5d90b58e91 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Sun, 26 Nov 2017 23:43:13 -0700 Subject: [PATCH 07/21] fixed menu error --- interface/src/Menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 88ae59a803..7292594b53 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -689,7 +689,7 @@ Menu::Menu() { auto scope = DependencyManager::get(); MenuWrapper* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope"); - action = addActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScope, Qt::CTRL | Qt::Key_F2, false); + action = addActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScope); connect(action, &QAction::triggered, [] { auto scriptEngines = DependencyManager::get(); QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation(); From eb0d9250917155ad3953900780c6796f3b9797d4 Mon Sep 17 00:00:00 2001 From: Daniela Date: Tue, 28 Nov 2017 22:55:09 +0000 Subject: [PATCH 08/21] YAW ROLL PITCH tool gizmos + functionality --- .../system/libraries/entitySelectionTool.js | 414 +++++++----------- 1 file changed, 154 insertions(+), 260 deletions(-) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index a66f897b0b..f0f87d6aa2 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -1031,6 +1031,40 @@ SelectionDisplay = (function() { that.updateHandles(); }; + // Function: Calculate New Bound Extremes + // uses dot product to discover new top and bottom on the new referential (max and min) + that.calculateNewBoundExtremes = function(boundPointList, referenceVector) { + + if (boundPointList.length < 2) { + return [null, null]; + } + + var refMax = boundPointList[0]; + var refMin = boundPointList[1]; + + var dotMax = Vec3.dot(boundPointList[0], referenceVector); + var dotMin = Vec3.dot(boundPointList[1], referenceVector); + + if (dotMin > dotMax) { + dotMax = dotMin; + dotMin = Vec3.dot(boundPointList[0], referenceVector); + refMax = boundPointList[1]; + refMin = boundPointList[0]; + } + + for (var i = 2; i < boundPointList.length ; i++) { + var dotAux = Vec3.dot(boundPointList[i], referenceVector); + if (dotAux > dotMax) { + dotMax = dotAux; + refMax = boundPointList[i]; + } else if (dotAux < dotMin) { + dotMin = dotAux; + refMin = boundPointList[i]; + } + } + return [refMin, refMax]; + } + // FUNCTION: UPDATE ROTATION HANDLES that.updateRotationHandles = function() { var diagonal = (Vec3.length(SelectionManager.worldDimensions) / 2) * 1.1; @@ -1043,7 +1077,7 @@ SelectionDisplay = (function() { } else { outerAlpha = 0.5; } - + // prev 0.05 var rotateHandleOffset = 0.05; var top, far, left, bottom, near, right, boundsCenter, objectCenter, BLN, BRN, BLF, TLN, TRN, TLF, TRF; @@ -1088,253 +1122,6 @@ SelectionDisplay = (function() { var cameraPosition = Camera.getPosition(); var look = Vec3.normalize(Vec3.subtract(cameraPosition, objectCenter)); - if (cameraPosition.x > objectCenter.x) { - // must be BRF or BRN - if (cameraPosition.z < objectCenter.z) { - - yawHandleRotation = Quat.fromVec3Degrees({ - x: 270, - y: 90, - z: 0 - }); - pitchHandleRotation = Quat.fromVec3Degrees({ - x: 0, - y: 90, - z: 0 - }); - rollHandleRotation = Quat.fromVec3Degrees({ - x: 0, - y: 0, - z: 0 - }); - - yawCorner = { - x: left + rotateHandleOffset, - y: bottom - rotateHandleOffset, - z: near - rotateHandleOffset - }; - - pitchCorner = { - x: right - rotateHandleOffset, - y: top + rotateHandleOffset, - z: near - rotateHandleOffset - }; - - rollCorner = { - x: left + rotateHandleOffset, - y: top + rotateHandleOffset, - z: far + rotateHandleOffset - }; - - yawCenter = { - x: boundsCenter.x, - y: bottom, - z: boundsCenter.z - }; - pitchCenter = { - x: right, - y: boundsCenter.y, - z: boundsCenter.z - }; - rollCenter = { - x: boundsCenter.x, - y: boundsCenter.y, - z: far - }; - - - Overlays.editOverlay(pitchHandle, { - url: ROTATE_ARROW_WEST_SOUTH_URL - }); - Overlays.editOverlay(rollHandle, { - url: ROTATE_ARROW_WEST_SOUTH_URL - }); - - - } else { - - yawHandleRotation = Quat.fromVec3Degrees({ - x: 270, - y: 0, - z: 0 - }); - pitchHandleRotation = Quat.fromVec3Degrees({ - x: 180, - y: 270, - z: 0 - }); - rollHandleRotation = Quat.fromVec3Degrees({ - x: 0, - y: 0, - z: 90 - }); - - yawCorner = { - x: left + rotateHandleOffset, - y: bottom - rotateHandleOffset, - z: far + rotateHandleOffset - }; - - pitchCorner = { - x: right - rotateHandleOffset, - y: top + rotateHandleOffset, - z: far + rotateHandleOffset - }; - - rollCorner = { - x: left + rotateHandleOffset, - y: top + rotateHandleOffset, - z: near - rotateHandleOffset - }; - - - yawCenter = { - x: boundsCenter.x, - y: bottom, - z: boundsCenter.z - }; - pitchCenter = { - x: right, - y: boundsCenter.y, - z: boundsCenter.z - }; - rollCenter = { - x: boundsCenter.x, - y: boundsCenter.y, - z: near - }; - - Overlays.editOverlay(pitchHandle, { - url: ROTATE_ARROW_WEST_NORTH_URL - }); - Overlays.editOverlay(rollHandle, { - url: ROTATE_ARROW_WEST_NORTH_URL - }); - } - } else { - - // must be BLF or BLN - if (cameraPosition.z < objectCenter.z) { - - yawHandleRotation = Quat.fromVec3Degrees({ - x: 270, - y: 180, - z: 0 - }); - pitchHandleRotation = Quat.fromVec3Degrees({ - x: 90, - y: 0, - z: 90 - }); - rollHandleRotation = Quat.fromVec3Degrees({ - x: 0, - y: 0, - z: 180 - }); - - yawCorner = { - x: right - rotateHandleOffset, - y: bottom - rotateHandleOffset, - z: near - rotateHandleOffset - }; - - pitchCorner = { - x: left + rotateHandleOffset, - y: top + rotateHandleOffset, - z: near - rotateHandleOffset - }; - - rollCorner = { - x: right - rotateHandleOffset, - y: top + rotateHandleOffset, - z: far + rotateHandleOffset - }; - - yawCenter = { - x: boundsCenter.x, - y: bottom, - z: boundsCenter.z - }; - pitchCenter = { - x: left, - y: boundsCenter.y, - z: boundsCenter.z - }; - rollCenter = { - x: boundsCenter.x, - y: boundsCenter.y, - z: far - }; - - Overlays.editOverlay(pitchHandle, { - url: ROTATE_ARROW_WEST_NORTH_URL - }); - Overlays.editOverlay(rollHandle, { - url: ROTATE_ARROW_WEST_NORTH_URL - }); - - } else { - - yawHandleRotation = Quat.fromVec3Degrees({ - x: 270, - y: 270, - z: 0 - }); - pitchHandleRotation = Quat.fromVec3Degrees({ - x: 180, - y: 270, - z: 0 - }); - rollHandleRotation = Quat.fromVec3Degrees({ - x: 0, - y: 0, - z: 180 - }); - - yawCorner = { - x: right - rotateHandleOffset, - y: bottom - rotateHandleOffset, - z: far + rotateHandleOffset - }; - - rollCorner = { - x: right - rotateHandleOffset, - y: top + rotateHandleOffset, - z: near - rotateHandleOffset - }; - - pitchCorner = { - x: left + rotateHandleOffset, - y: top + rotateHandleOffset, - z: far + rotateHandleOffset - }; - - yawCenter = { - x: boundsCenter.x, - y: bottom, - z: boundsCenter.z - }; - rollCenter = { - x: boundsCenter.x, - y: boundsCenter.y, - z: near - }; - pitchCenter = { - x: left, - y: boundsCenter.y, - z: boundsCenter.z - }; - - Overlays.editOverlay(pitchHandle, { - url: ROTATE_ARROW_WEST_NORTH_URL - }); - Overlays.editOverlay(rollHandle, { - url: ROTATE_ARROW_WEST_NORTH_URL - }); - - } - } - // place yaw, pitch and roll rotations on the avatar referential yawHandleRotation = Quat.multiply(MyAvatar.orientation , yawHandleRotation); pitchHandleRotation = Quat.multiply(MyAvatar.orientation , pitchHandleRotation); @@ -1345,7 +1132,7 @@ SelectionDisplay = (function() { z: 0 })); var upVector = Quat.getUp(avatarReferential); - var rightVector = Quat.getRight(avatarReferential); + var rightVector = Vec3.multiply(-1, Quat.getRight(avatarReferential)); var frontVector = Quat.getFront(avatarReferential); // Centers var xSign = -1.0; @@ -1357,20 +1144,127 @@ SelectionDisplay = (function() { zSign = 1.0; } - yawCenter = Vec3.sum(boundsCenter, Vec3.multiply(-(dimensions.y / 2), upVector)); - var myBotom = Vec3.multiply(-(dimensions.y / 2) - rotateHandleOffset, upVector); - var myRight = Vec3.multiply(xSign * (dimensions.x / 2) + xSign * rotateHandleOffset, rightVector); - var myFront = Vec3.multiply(zSign * (dimensions.z / 2) + zSign * rotateHandleOffset, frontVector); - yawCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(myBotom, myRight), myFront)); + + + + //project all 8 bounding box points (assumes center 0,0,0) onto the new avatar referential + + var projT_UP = Vec3.dot(Vec3.multiply((dimensions.y / 2) + rotateHandleOffset, Vec3.UNIT_Y), upVector); + projT_UP = Vec3.multiply(projT_UP, upVector); + + + var projB_UP = Vec3.dot(Vec3.multiply(-(dimensions.y / 2) - rotateHandleOffset, Vec3.UNIT_Y), upVector); + projB_UP = Vec3.multiply(projB_UP, upVector); + + + var projL_UP = Vec3.dot(Vec3.multiply(xSign * (dimensions.x / 2) + xSign * rotateHandleOffset, Vec3.UNIT_X), upVector); + projL_UP = Vec3.multiply(projL_UP, upVector); + + var projR_UP = Vec3.dot(Vec3.multiply(-xSign * (dimensions.x / 2) - xSign * rotateHandleOffset, Vec3.UNIT_X), upVector); + projR_UP = Vec3.multiply(projR_UP, upVector); + + + var projN_UP = Vec3.dot(Vec3.multiply(zSign * (dimensions.z / 2) + zSign * rotateHandleOffset, Vec3.FRONT), upVector); + projN_UP = Vec3.multiply(projN_UP, upVector); + + var projF_UP = Vec3.dot(Vec3.multiply(-zSign * (dimensions.z / 2) - zSign * rotateHandleOffset, Vec3.FRONT), upVector); + projF_UP = Vec3.multiply(projF_UP, upVector); + + var projUPList = [projT_UP, projB_UP, projL_UP, projR_UP, projN_UP, projF_UP]; + + var projUP = that.calculateNewBoundExtremes(projUPList, upVector); + + //// RIGHT + + var projT_RIGHT = Vec3.dot(Vec3.multiply((dimensions.y / 2) + rotateHandleOffset, Vec3.UNIT_Y), rightVector); + projT_RIGHT = Vec3.multiply(projT_RIGHT, rightVector); + + + var projB_RIGHT = Vec3.dot(Vec3.multiply(-(dimensions.y / 2) - rotateHandleOffset, Vec3.UNIT_Y), rightVector); + projB_RIGHT = Vec3.multiply(projB_RIGHT, rightVector); + + + var projL_RIGHT = Vec3.dot(Vec3.multiply(xSign * (dimensions.x / 2) + xSign * rotateHandleOffset, Vec3.UNIT_X), rightVector); + projL_RIGHT = Vec3.multiply(projL_RIGHT, rightVector); + + var projR_RIGHT = Vec3.dot(Vec3.multiply(-xSign * (dimensions.x / 2) - xSign * rotateHandleOffset, Vec3.UNIT_X), rightVector); + projR_RIGHT = Vec3.multiply(projR_RIGHT, rightVector); + + + var projN_RIGHT = Vec3.dot(Vec3.multiply(zSign * (dimensions.z / 2) + zSign * rotateHandleOffset, Vec3.FRONT), rightVector); + projN_RIGHT = Vec3.multiply(projN_RIGHT, rightVector); + + var projF_RIGHT = Vec3.dot(Vec3.multiply(-zSign * (dimensions.z / 2) - zSign * rotateHandleOffset, Vec3.FRONT), rightVector); + projF_RIGHT = Vec3.multiply(projF_RIGHT, rightVector); + + + var projRIGHTList = [projT_RIGHT, projB_RIGHT, projL_RIGHT, projR_RIGHT, projN_RIGHT, projF_RIGHT]; + + var projRIGHT = that.calculateNewBoundExtremes(projRIGHTList, rightVector); + + //FRONT + + var projT_FRONT = Vec3.dot(Vec3.multiply((dimensions.y / 2) + rotateHandleOffset, Vec3.UNIT_Y), frontVector); + projT_FRONT = Vec3.multiply(projT_FRONT, frontVector); + + + var projB_FRONT = Vec3.dot(Vec3.multiply(-(dimensions.y / 2) - rotateHandleOffset, Vec3.UNIT_Y), frontVector); + projB_FRONT = Vec3.multiply(projB_FRONT, frontVector); + + + var projL_FRONT = Vec3.dot(Vec3.multiply(xSign * (dimensions.x / 2) + xSign * rotateHandleOffset, Vec3.UNIT_X), frontVector); + projL_FRONT = Vec3.multiply(projL_FRONT, frontVector); + + var projR_FRONT = Vec3.dot(Vec3.multiply(-xSign * (dimensions.x / 2) - xSign * rotateHandleOffset, Vec3.UNIT_X), frontVector); + projR_FRONT = Vec3.multiply(projR_FRONT, frontVector); + + + var projN_FRONT = Vec3.dot(Vec3.multiply(zSign * (dimensions.z / 2) + zSign * rotateHandleOffset, Vec3.FRONT), frontVector); + projN_FRONT = Vec3.multiply(projN_FRONT, frontVector); + + var projF_FRONT = Vec3.dot(Vec3.multiply(-zSign * (dimensions.z / 2) - zSign * rotateHandleOffset, Vec3.FRONT), frontVector); + projF_FRONT = Vec3.multiply(projF_FRONT, frontVector); + + var projFRONTList = [projT_FRONT, projB_FRONT, projL_FRONT, projR_FRONT, projN_FRONT, projF_FRONT]; + + var projFRONT = that.calculateNewBoundExtremes(projFRONTList, frontVector); + + + ///// + + Overlays.editOverlay(pitchHandle, { + url: ROTATE_ARROW_WEST_NORTH_URL + }); + Overlays.editOverlay(rollHandle, { + url: ROTATE_ARROW_WEST_NORTH_URL + }); + + yawCenter = Vec3.sum(boundsCenter, projUP[0]); + yawCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(projUP[0], projRIGHT[1]), projFRONT[1])); yawHandleRotation = Quat.lookAt(yawCorner, Vec3.sum(yawCorner, upVector), Vec3.subtract(yawCenter,yawCorner)); yawHandleRotation = Quat.multiply(Quat.angleAxis(45, upVector), yawHandleRotation); - - //Quat.fromPitchYawRollDegrees(0,270,0) - pitchCenter = Vec3.sum(boundsCenter, Vec3.multiply(xSign * (dimensions.x / 2), rightVector)); - rollCenter = Vec3.sum(boundsCenter, Vec3.multiply(zSign * (dimensions.z / 2), frontVector)); - + + + yawCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(projUP[0], projRIGHT[1]), projFRONT[1])); + + pitchCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(projUP[1], projRIGHT[0]), projFRONT[1])); + + pitchCenter = Vec3.sum(boundsCenter, projRIGHT[0]); + + pitchHandleRotation = Quat.lookAt(pitchCorner, Vec3.sum(pitchCorner, rightVector), Vec3.subtract(pitchCenter,pitchCorner)); + pitchHandleRotation = Quat.multiply(Quat.angleAxis(45, rightVector), pitchHandleRotation); + + + rollCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(projUP[1], projRIGHT[1]), projFRONT[0])); + rollCenter = Vec3.sum(boundsCenter, projFRONT[0]); + + rollHandleRotation = Quat.lookAt(rollCorner, Vec3.sum(rollCorner, frontVector), Vec3.subtract(rollCenter,rollCorner)); + + rollHandleRotation = Quat.multiply(Quat.angleAxis(45, frontVector), rollHandleRotation); + + /////////// var rotateHandlesVisible = true; var rotationOverlaysVisible = false; @@ -3433,7 +3327,7 @@ SelectionDisplay = (function() { // FUNCTION: UPDATE ROTATION DEGREES OVERLAY function updateRotationDegreesOverlay(angleFromZero, handleRotation, centerPosition) { - var wantDebug = false; + var wantDebug = true; if (wantDebug) { print("---> updateRotationDegreesOverlay ---"); print(" AngleFromZero: " + angleFromZero); From 68d5d600b747e90d2af5aa100324c8ddb7bbf5ec Mon Sep 17 00:00:00 2001 From: Daniela Date: Tue, 28 Nov 2017 23:16:34 +0000 Subject: [PATCH 09/21] YAW ROLL PITCH tool gizmos + functionality --- scripts/system/libraries/entitySelectionTool.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index f0f87d6aa2..f4f5af8d5b 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -1123,9 +1123,7 @@ SelectionDisplay = (function() { var look = Vec3.normalize(Vec3.subtract(cameraPosition, objectCenter)); // place yaw, pitch and roll rotations on the avatar referential - yawHandleRotation = Quat.multiply(MyAvatar.orientation , yawHandleRotation); - pitchHandleRotation = Quat.multiply(MyAvatar.orientation , pitchHandleRotation); - rollHandleRotation = Quat.multiply(MyAvatar.orientation , rollHandleRotation); + var avatarReferential = Quat.multiply(MyAvatar.orientation, Quat.fromVec3Degrees({ x: 0, y: 180, @@ -1246,7 +1244,6 @@ SelectionDisplay = (function() { yawHandleRotation = Quat.lookAt(yawCorner, Vec3.sum(yawCorner, upVector), Vec3.subtract(yawCenter,yawCorner)); yawHandleRotation = Quat.multiply(Quat.angleAxis(45, upVector), yawHandleRotation); - yawCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(projUP[0], projRIGHT[1]), projFRONT[1])); pitchCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(projUP[1], projRIGHT[0]), projFRONT[1])); @@ -1315,6 +1312,8 @@ SelectionDisplay = (function() { position: rollCorner, rotation: rollHandleRotation }); + + }; // FUNCTION: UPDATE HANDLE SIZES From 8985acd8b68a37e1e802446989aeb00dcc1c197d Mon Sep 17 00:00:00 2001 From: Daniela Date: Wed, 29 Nov 2017 13:00:36 +0000 Subject: [PATCH 10/21] YAW ROLL PITCH tool now represents angles in a usefull way. Refactoring. --- .../system/libraries/entitySelectionTool.js | 290 +++++++----------- 1 file changed, 104 insertions(+), 186 deletions(-) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index f4f5af8d5b..fb79dbab0a 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -1031,40 +1031,75 @@ SelectionDisplay = (function() { that.updateHandles(); }; - // Function: Calculate New Bound Extremes - // uses dot product to discover new top and bottom on the new referential (max and min) - that.calculateNewBoundExtremes = function(boundPointList, referenceVector) { - - if (boundPointList.length < 2) { - return [null, null]; - } - - var refMax = boundPointList[0]; - var refMin = boundPointList[1]; - - var dotMax = Vec3.dot(boundPointList[0], referenceVector); - var dotMin = Vec3.dot(boundPointList[1], referenceVector); - - if (dotMin > dotMax) { - dotMax = dotMin; - dotMin = Vec3.dot(boundPointList[0], referenceVector); - refMax = boundPointList[1]; - refMin = boundPointList[0]; - } - - for (var i = 2; i < boundPointList.length ; i++) { - var dotAux = Vec3.dot(boundPointList[i], referenceVector); - if (dotAux > dotMax) { - dotMax = dotAux; - refMax = boundPointList[i]; - } else if (dotAux < dotMin) { - dotMin = dotAux; - refMin = boundPointList[i]; - } - } - return [refMin, refMax]; - } - + // Function: Calculate New Bound Extremes + // uses dot product to discover new top and bottom on the new referential (max and min) + that.calculateNewBoundExtremes = function(boundPointList, referenceVector) { + + if (boundPointList.length < 2) { + return [null, null]; + } + + var refMax = boundPointList[0]; + var refMin = boundPointList[1]; + + var dotMax = Vec3.dot(boundPointList[0], referenceVector); + var dotMin = Vec3.dot(boundPointList[1], referenceVector); + + if (dotMin > dotMax) { + dotMax = dotMin; + dotMin = Vec3.dot(boundPointList[0], referenceVector); + refMax = boundPointList[1]; + refMin = boundPointList[0]; + } + + for (var i = 2; i < boundPointList.length ; i++) { + var dotAux = Vec3.dot(boundPointList[i], referenceVector); + if (dotAux > dotMax) { + dotMax = dotAux; + refMax = boundPointList[i]; + } else if (dotAux < dotMin) { + dotMin = dotAux; + refMin = boundPointList[i]; + } + } + return [refMin, refMax]; + } + + // Projects all 6 bounding box points: Top, Bottom, Left, Right, Near, Far (assumes center 0,0,0) onto + // one of the basis of the new avatar referencial + // dimensions - dimensions of the AABB (axis aligned bounding box) on the standard basis + // [1, 0, 0], [0, 1, 0], [0, 0, 1] + // v - projection vector + // rotateHandleOffset - offset for the rotation handle gizmo position + that.projectBoundingBoxPoints = function(dimensions, v, rotateHandleOffset){ + //project all 6 bounding box points: Top, Bottom, Left, Right, Near, Far (assumes center 0,0,0) onto the new avatar referential + + var projT_v = Vec3.dot(Vec3.multiply((dimensions.y / 2) + rotateHandleOffset, Vec3.UNIT_Y), v); + projT_v = Vec3.multiply(projT_v, v); + + + var projB_v = Vec3.dot(Vec3.multiply(-(dimensions.y / 2) - rotateHandleOffset, Vec3.UNIT_Y), v); + projB_v = Vec3.multiply(projB_v, v); + + + var projL_v = Vec3.dot(Vec3.multiply((dimensions.x / 2) + rotateHandleOffset, Vec3.UNIT_X), v); + projL_v = Vec3.multiply(projL_v, v); + + var projR_v = Vec3.dot(Vec3.multiply(-1.0 * (dimensions.x / 2) - 1.0 * rotateHandleOffset, Vec3.UNIT_X), v); + projR_v = Vec3.multiply(projR_v, v); + + + var projN_v = Vec3.dot(Vec3.multiply((dimensions.z / 2) + rotateHandleOffset, Vec3.FRONT), v); + projN_v = Vec3.multiply(projN_v, v); + + var projF_v = Vec3.dot(Vec3.multiply(-1.0 * (dimensions.z / 2) - 1.0 * rotateHandleOffset, Vec3.FRONT), v); + projF_v = Vec3.multiply(projF_v, v); + + var projList = [projT_v, projB_v, projL_v, projR_v, projN_v, projF_v]; + + return that.calculateNewBoundExtremes(projList, v); + }; + // FUNCTION: UPDATE ROTATION HANDLES that.updateRotationHandles = function() { var diagonal = (Vec3.length(SelectionManager.worldDimensions) / 2) * 1.1; @@ -1080,7 +1115,7 @@ SelectionDisplay = (function() { // prev 0.05 var rotateHandleOffset = 0.05; - var top, far, left, bottom, near, right, boundsCenter, objectCenter, BLN, BRN, BLF, TLN, TRN, TLF, TRF; + var boundsCenter, objectCenter; var dimensions, rotation; if (spaceMode === SPACE_LOCAL) { @@ -1092,33 +1127,12 @@ SelectionDisplay = (function() { dimensions = SelectionManager.worldDimensions; var position = objectCenter; - top = objectCenter.y + (dimensions.y / 2); - far = objectCenter.z + (dimensions.z / 2); - left = objectCenter.x + (dimensions.x / 2); - - bottom = objectCenter.y - (dimensions.y / 2); - near = objectCenter.z - (dimensions.z / 2); - right = objectCenter.x - (dimensions.x / 2); - boundsCenter = objectCenter; var yawCorner; var pitchCorner; var rollCorner; - // determine which bottom corner we are closest to - /*------------------------------ - example: - - BRF +--------+ BLF - | | - | | - BRN +--------+ BLN - - * - - ------------------------------*/ - var cameraPosition = Camera.getPosition(); var look = Vec3.normalize(Vec3.subtract(cameraPosition, objectCenter)); @@ -1132,136 +1146,39 @@ SelectionDisplay = (function() { var upVector = Quat.getUp(avatarReferential); var rightVector = Vec3.multiply(-1, Quat.getRight(avatarReferential)); var frontVector = Quat.getFront(avatarReferential); - // Centers - var xSign = -1.0; - var zSign = -1.0; - if (Vec3.dot(look, rightVector) > 0) { - xSign = 1.0; - } - if (Vec3.dot(look, frontVector) > 0) { - zSign = 1.0; - } - - - - //project all 8 bounding box points (assumes center 0,0,0) onto the new avatar referential - - var projT_UP = Vec3.dot(Vec3.multiply((dimensions.y / 2) + rotateHandleOffset, Vec3.UNIT_Y), upVector); - projT_UP = Vec3.multiply(projT_UP, upVector); - - - var projB_UP = Vec3.dot(Vec3.multiply(-(dimensions.y / 2) - rotateHandleOffset, Vec3.UNIT_Y), upVector); - projB_UP = Vec3.multiply(projB_UP, upVector); - - - var projL_UP = Vec3.dot(Vec3.multiply(xSign * (dimensions.x / 2) + xSign * rotateHandleOffset, Vec3.UNIT_X), upVector); - projL_UP = Vec3.multiply(projL_UP, upVector); - - var projR_UP = Vec3.dot(Vec3.multiply(-xSign * (dimensions.x / 2) - xSign * rotateHandleOffset, Vec3.UNIT_X), upVector); - projR_UP = Vec3.multiply(projR_UP, upVector); - - - var projN_UP = Vec3.dot(Vec3.multiply(zSign * (dimensions.z / 2) + zSign * rotateHandleOffset, Vec3.FRONT), upVector); - projN_UP = Vec3.multiply(projN_UP, upVector); - - var projF_UP = Vec3.dot(Vec3.multiply(-zSign * (dimensions.z / 2) - zSign * rotateHandleOffset, Vec3.FRONT), upVector); - projF_UP = Vec3.multiply(projF_UP, upVector); - - var projUPList = [projT_UP, projB_UP, projL_UP, projR_UP, projN_UP, projF_UP]; - - var projUP = that.calculateNewBoundExtremes(projUPList, upVector); - - //// RIGHT - - var projT_RIGHT = Vec3.dot(Vec3.multiply((dimensions.y / 2) + rotateHandleOffset, Vec3.UNIT_Y), rightVector); - projT_RIGHT = Vec3.multiply(projT_RIGHT, rightVector); - - - var projB_RIGHT = Vec3.dot(Vec3.multiply(-(dimensions.y / 2) - rotateHandleOffset, Vec3.UNIT_Y), rightVector); - projB_RIGHT = Vec3.multiply(projB_RIGHT, rightVector); - - - var projL_RIGHT = Vec3.dot(Vec3.multiply(xSign * (dimensions.x / 2) + xSign * rotateHandleOffset, Vec3.UNIT_X), rightVector); - projL_RIGHT = Vec3.multiply(projL_RIGHT, rightVector); - - var projR_RIGHT = Vec3.dot(Vec3.multiply(-xSign * (dimensions.x / 2) - xSign * rotateHandleOffset, Vec3.UNIT_X), rightVector); - projR_RIGHT = Vec3.multiply(projR_RIGHT, rightVector); - - - var projN_RIGHT = Vec3.dot(Vec3.multiply(zSign * (dimensions.z / 2) + zSign * rotateHandleOffset, Vec3.FRONT), rightVector); - projN_RIGHT = Vec3.multiply(projN_RIGHT, rightVector); - - var projF_RIGHT = Vec3.dot(Vec3.multiply(-zSign * (dimensions.z / 2) - zSign * rotateHandleOffset, Vec3.FRONT), rightVector); - projF_RIGHT = Vec3.multiply(projF_RIGHT, rightVector); - - - var projRIGHTList = [projT_RIGHT, projB_RIGHT, projL_RIGHT, projR_RIGHT, projN_RIGHT, projF_RIGHT]; - - var projRIGHT = that.calculateNewBoundExtremes(projRIGHTList, rightVector); - - //FRONT - - var projT_FRONT = Vec3.dot(Vec3.multiply((dimensions.y / 2) + rotateHandleOffset, Vec3.UNIT_Y), frontVector); - projT_FRONT = Vec3.multiply(projT_FRONT, frontVector); - - - var projB_FRONT = Vec3.dot(Vec3.multiply(-(dimensions.y / 2) - rotateHandleOffset, Vec3.UNIT_Y), frontVector); - projB_FRONT = Vec3.multiply(projB_FRONT, frontVector); - - - var projL_FRONT = Vec3.dot(Vec3.multiply(xSign * (dimensions.x / 2) + xSign * rotateHandleOffset, Vec3.UNIT_X), frontVector); - projL_FRONT = Vec3.multiply(projL_FRONT, frontVector); - - var projR_FRONT = Vec3.dot(Vec3.multiply(-xSign * (dimensions.x / 2) - xSign * rotateHandleOffset, Vec3.UNIT_X), frontVector); - projR_FRONT = Vec3.multiply(projR_FRONT, frontVector); - - - var projN_FRONT = Vec3.dot(Vec3.multiply(zSign * (dimensions.z / 2) + zSign * rotateHandleOffset, Vec3.FRONT), frontVector); - projN_FRONT = Vec3.multiply(projN_FRONT, frontVector); - - var projF_FRONT = Vec3.dot(Vec3.multiply(-zSign * (dimensions.z / 2) - zSign * rotateHandleOffset, Vec3.FRONT), frontVector); - projF_FRONT = Vec3.multiply(projF_FRONT, frontVector); - - var projFRONTList = [projT_FRONT, projB_FRONT, projL_FRONT, projR_FRONT, projN_FRONT, projF_FRONT]; - - var projFRONT = that.calculateNewBoundExtremes(projFRONTList, frontVector); - - - ///// - - Overlays.editOverlay(pitchHandle, { - url: ROTATE_ARROW_WEST_NORTH_URL - }); - Overlays.editOverlay(rollHandle, { - url: ROTATE_ARROW_WEST_NORTH_URL - }); - - yawCenter = Vec3.sum(boundsCenter, projUP[0]); + //project all 6 bounding box points: Top, Bottom, Left, Right, Near, Far (assumes center 0,0,0) onto the new avatar referential + // UP + var projUP = that.projectBoundingBoxPoints(dimensions, upVector, rotateHandleOffset); + // RIGHT + var projRIGHT = that.projectBoundingBoxPoints(dimensions, rightVector, rotateHandleOffset); + // FRONT + var projFRONT = that.projectBoundingBoxPoints(dimensions, frontVector, rotateHandleOffset); + + ///// + + // YAW + yawCenter = Vec3.sum(boundsCenter, projUP[0]); yawCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(projUP[0], projRIGHT[1]), projFRONT[1])); - yawHandleRotation = Quat.lookAt(yawCorner, Vec3.sum(yawCorner, upVector), Vec3.subtract(yawCenter,yawCorner)); yawHandleRotation = Quat.multiply(Quat.angleAxis(45, upVector), yawHandleRotation); - - yawCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(projUP[0], projRIGHT[1]), projFRONT[1])); - - pitchCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(projUP[1], projRIGHT[0]), projFRONT[1])); - - pitchCenter = Vec3.sum(boundsCenter, projRIGHT[0]); - - pitchHandleRotation = Quat.lookAt(pitchCorner, Vec3.sum(pitchCorner, rightVector), Vec3.subtract(pitchCenter,pitchCorner)); + + // PTCH + pitchCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(projUP[1], projRIGHT[0]), projFRONT[1])); + pitchCenter = Vec3.sum(boundsCenter, projRIGHT[0]); + + pitchHandleRotation = Quat.lookAt(pitchCorner, Vec3.sum(pitchCorner, rightVector), Vec3.subtract(pitchCenter,pitchCorner)); pitchHandleRotation = Quat.multiply(Quat.angleAxis(45, rightVector), pitchHandleRotation); - - - rollCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(projUP[1], projRIGHT[1]), projFRONT[0])); - rollCenter = Vec3.sum(boundsCenter, projFRONT[0]); - - rollHandleRotation = Quat.lookAt(rollCorner, Vec3.sum(rollCorner, frontVector), Vec3.subtract(rollCenter,rollCorner)); - - rollHandleRotation = Quat.multiply(Quat.angleAxis(45, frontVector), rollHandleRotation); - - /////////// + + // ROLL + rollCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(projUP[1], projRIGHT[1]), projFRONT[0])); + rollCenter = Vec3.sum(boundsCenter, projFRONT[0]); + + rollHandleRotation = Quat.lookAt(rollCorner, Vec3.sum(rollCorner, frontVector), Vec3.subtract(rollCenter,rollCorner)); + rollHandleRotation = Quat.multiply(Quat.angleAxis(45, frontVector), rollHandleRotation); + + /////////// var rotateHandlesVisible = true; var rotationOverlaysVisible = false; @@ -1312,8 +1229,8 @@ SelectionDisplay = (function() { position: rollCorner, rotation: rollHandleRotation }); - - + + }; // FUNCTION: UPDATE HANDLE SIZES @@ -3326,7 +3243,7 @@ SelectionDisplay = (function() { // FUNCTION: UPDATE ROTATION DEGREES OVERLAY function updateRotationDegreesOverlay(angleFromZero, handleRotation, centerPosition) { - var wantDebug = true; + var wantDebug = false; if (wantDebug) { print("---> updateRotationDegreesOverlay ---"); print(" AngleFromZero: " + angleFromZero); @@ -3354,7 +3271,7 @@ SelectionDisplay = (function() { y: innerRadius * ROTATION_DISPLAY_SIZE_Y_MULTIPLIER }, lineHeight: innerRadius * ROTATION_DISPLAY_LINE_HEIGHT_MULTIPLIER, - text: normalizeDegrees(angleFromZero) + "°" + text: normalizeDegrees(-angleFromZero) + "°" }; if (wantDebug) { print(" TranslatedPos: " + position.x + ", " + position.y + ", " + position.z); @@ -3418,7 +3335,7 @@ SelectionDisplay = (function() { //get the correct axis according to the avatar referencial var avatarReferential = Quat.multiply(MyAvatar.orientation, Quat.fromVec3Degrees({ x: 0, - y: 180, + y: 0, z: 0 })); rotationNormal = Vec3.multiplyQbyV(avatarReferential, rotationNormal); @@ -3526,7 +3443,8 @@ SelectionDisplay = (function() { var rotChange = Quat.angleAxis(angleFromZero, rotationNormal); updateSelectionsRotation(rotChange); - + //present angle in avatar referencial + angleFromZero = -angleFromZero; updateRotationDegreesOverlay(angleFromZero, handleRotation, rotCenter); // update the rotation display accordingly... From ef49aa0b71d1436867deaab7097292259004b951 Mon Sep 17 00:00:00 2001 From: Daniela Date: Wed, 29 Nov 2017 18:54:08 +0000 Subject: [PATCH 11/21] Refactor to comply with code standard. --- .../system/libraries/entitySelectionTool.js | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index fb79dbab0a..b8ba146757 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -1065,29 +1065,25 @@ SelectionDisplay = (function() { return [refMin, refMax]; } + // Function: Project Bounding Box Points // Projects all 6 bounding box points: Top, Bottom, Left, Right, Near, Far (assumes center 0,0,0) onto // one of the basis of the new avatar referencial // dimensions - dimensions of the AABB (axis aligned bounding box) on the standard basis // [1, 0, 0], [0, 1, 0], [0, 0, 1] // v - projection vector // rotateHandleOffset - offset for the rotation handle gizmo position - that.projectBoundingBoxPoints = function(dimensions, v, rotateHandleOffset){ - //project all 6 bounding box points: Top, Bottom, Left, Right, Near, Far (assumes center 0,0,0) onto the new avatar referential - + that.projectBoundingBoxPoints = function(dimensions, v, rotateHandleOffset) { var projT_v = Vec3.dot(Vec3.multiply((dimensions.y / 2) + rotateHandleOffset, Vec3.UNIT_Y), v); projT_v = Vec3.multiply(projT_v, v); - var projB_v = Vec3.dot(Vec3.multiply(-(dimensions.y / 2) - rotateHandleOffset, Vec3.UNIT_Y), v); projB_v = Vec3.multiply(projB_v, v); - var projL_v = Vec3.dot(Vec3.multiply((dimensions.x / 2) + rotateHandleOffset, Vec3.UNIT_X), v); projL_v = Vec3.multiply(projL_v, v); var projR_v = Vec3.dot(Vec3.multiply(-1.0 * (dimensions.x / 2) - 1.0 * rotateHandleOffset, Vec3.UNIT_X), v); projR_v = Vec3.multiply(projR_v, v); - var projN_v = Vec3.dot(Vec3.multiply((dimensions.z / 2) + rotateHandleOffset, Vec3.FRONT), v); projN_v = Vec3.multiply(projN_v, v); @@ -1147,7 +1143,9 @@ SelectionDisplay = (function() { var rightVector = Vec3.multiply(-1, Quat.getRight(avatarReferential)); var frontVector = Quat.getFront(avatarReferential); - //project all 6 bounding box points: Top, Bottom, Left, Right, Near, Far (assumes center 0,0,0) onto the new avatar referential + // project all 6 bounding box points: Top, Bottom, Left, Right, Near, Far (assumes center 0,0,0) + // onto the new avatar referential + // UP var projUP = that.projectBoundingBoxPoints(dimensions, upVector, rotateHandleOffset); // RIGHT @@ -1155,30 +1153,36 @@ SelectionDisplay = (function() { // FRONT var projFRONT = that.projectBoundingBoxPoints(dimensions, frontVector, rotateHandleOffset); - ///// - // YAW yawCenter = Vec3.sum(boundsCenter, projUP[0]); yawCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(projUP[0], projRIGHT[1]), projFRONT[1])); - yawHandleRotation = Quat.lookAt(yawCorner, Vec3.sum(yawCorner, upVector), Vec3.subtract(yawCenter,yawCorner)); + yawHandleRotation = Quat.lookAt( + yawCorner, + Vec3.sum(yawCorner, upVector), + Vec3.subtract(yawCenter,yawCorner)); yawHandleRotation = Quat.multiply(Quat.angleAxis(45, upVector), yawHandleRotation); // PTCH pitchCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(projUP[1], projRIGHT[0]), projFRONT[1])); pitchCenter = Vec3.sum(boundsCenter, projRIGHT[0]); - pitchHandleRotation = Quat.lookAt(pitchCorner, Vec3.sum(pitchCorner, rightVector), Vec3.subtract(pitchCenter,pitchCorner)); + pitchHandleRotation = Quat.lookAt( + pitchCorner, + Vec3.sum(pitchCorner, rightVector), + Vec3.subtract(pitchCenter,pitchCorner)); pitchHandleRotation = Quat.multiply(Quat.angleAxis(45, rightVector), pitchHandleRotation); // ROLL rollCorner = Vec3.sum(boundsCenter, Vec3.sum(Vec3.sum(projUP[1], projRIGHT[1]), projFRONT[0])); rollCenter = Vec3.sum(boundsCenter, projFRONT[0]); - rollHandleRotation = Quat.lookAt(rollCorner, Vec3.sum(rollCorner, frontVector), Vec3.subtract(rollCenter,rollCorner)); + rollHandleRotation = Quat.lookAt( + rollCorner, + Vec3.sum(rollCorner, frontVector), + Vec3.subtract(rollCenter,rollCorner)); rollHandleRotation = Quat.multiply(Quat.angleAxis(45, frontVector), rollHandleRotation); - /////////// var rotateHandlesVisible = true; var rotationOverlaysVisible = false; From 4030688e7a694af7fc2c2aed6433c582a3f9d436 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Sun, 3 Dec 2017 11:54:37 -0700 Subject: [PATCH 12/21] added record wav files --- interface/resources/qml/AudioScope.qml | 178 +++++++++++++++++--- interface/src/audio/AudioScope.cpp | 5 + interface/src/audio/AudioScope.h | 2 + interface/src/scripting/Audio.cpp | 26 +++ interface/src/scripting/Audio.h | 9 +- libraries/audio-client/src/AudioClient.cpp | 9 +- libraries/audio-client/src/AudioClient.h | 15 +- libraries/audio-client/src/AudioFileWav.cpp | 69 ++++++++ libraries/audio-client/src/AudioFileWav.h | 34 ++++ 9 files changed, 312 insertions(+), 35 deletions(-) create mode 100644 libraries/audio-client/src/AudioFileWav.cpp create mode 100644 libraries/audio-client/src/AudioFileWav.h diff --git a/interface/resources/qml/AudioScope.qml b/interface/resources/qml/AudioScope.qml index 6f78a2d820..1f67b3f090 100644 --- a/interface/resources/qml/AudioScope.qml +++ b/interface/resources/qml/AudioScope.qml @@ -7,6 +7,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // + import QtQuick 2.5 import QtQuick.Controls 1.4 import "styles-uit" @@ -32,7 +33,7 @@ Item { property var _triggered: false property var _steps - property var _refreshMs: 10 + property var _refreshMs: 32 property var _framesPerSecond: AudioScope.getFramesPerSecond() property var _isFrameUnits: true @@ -46,9 +47,17 @@ Item { property int y: 0 } - property var _timeBeforeHold: 100; - property var _pressedTime: 0; - property var _isPressed: false; + property var _timeBeforeHold: 300 + property var _pressedTime: 0 + property var _isPressed: false + + property var _recOpacity : 0.0 + property var _recSign : 0.05 + + property var _outputLeftState: false + property var _outputRightState: false + + property var _wavFilePath: "" function isHolding() { return (_pressedTime > _timeBeforeHold); @@ -84,13 +93,29 @@ Item { _triggerOutputRightData = AudioScope.triggerOutputRight; } } + + function setRecordingLabelOpacity(opacity) { + _recOpacity = opacity; + recCircle.opacity = _recOpacity; + recText.opacity = _recOpacity; + } + + function updateRecordingLabel() { + _recOpacity += _recSign; + if (_recOpacity > 1.0 || _recOpacity < 0.0) { + _recOpacity = _recOpacity > 1.0 ? 1.0 : 0.0; + _recSign *= -1; + } + setRecordingLabelOpacity(_recOpacity); + } function pullFreshValues() { - if (!AudioScope.getPause()){ - if (AudioScope.getTriggered()) { - _triggered = true; - collectTriggerData(); - } else { + if (Audio.getRecording()) { + updateRecordingLabel(); + } + + if (!AudioScope.getPause()) { + if (!_triggered) { collectScopeData(); } } @@ -99,8 +124,49 @@ Item { } } - + function startRecording() { + _wavFilePath = (new Date()).toISOString(); // yyyy-mm-ddThh:mm:ss.sssZ + _wavFilePath = _wavFilePath.replace(/[\-:]|\.\d*Z$/g, "").replace("T", "-") + ".wav"; + // Using controller recording default directory + _wavFilePath = Recording.getDefaultRecordingSaveDirectory() + _wavFilePath; + if (!Audio.startRecording(_wavFilePath)) { + Messages.sendMessage("Hifi-Notifications", JSON.stringify({message:"Error creating: "+_wavFilePath})); + updateRecordingUI(false); + } + } + function stopRecording() { + Audio.stopRecording(); + setRecordingLabelOpacity(0.0); + Messages.sendMessage("Hifi-Notifications", JSON.stringify({message:"Saved: "+_wavFilePath})); + } + + function updateRecordingUI(isRecording) { + if (!isRecording) { + recordButton.text = "Record"; + recordButton.color = hifi.buttons.black; + outputLeftCh.checked = _outputLeftState; + outputRightCh.checked = _outputRightState; + } else { + recordButton.text = "Stop"; + recordButton.color = hifi.buttons.red; + _outputLeftState = outputLeftCh.checked; + _outputRightState = outputRightCh.checked; + outputLeftCh.checked = true; + outputRightCh.checked = true; + } + } + + function toggleRecording() { + if (Audio.getRecording()) { + updateRecordingUI(false); + stopRecording(); + } else { + updateRecordingUI(true); + startRecording(); + } + } + Timer { interval: _refreshMs; running: true; repeat: true onTriggered: pullFreshValues() @@ -306,7 +372,7 @@ Item { boxSize: 20 anchors.top: parent.top; anchors.left: parent.left; - anchors.topMargin: 20; + anchors.topMargin: 8; anchors.leftMargin: 20; checked: AudioScope.getVisible(); onCheckedChanged: { @@ -333,6 +399,7 @@ Item { AudioScope.setServerEcho(outputLeftCh.checked || outputRightCh.checked); } } + HifiControlsUit.Label { text: "Channels"; anchors.horizontalCenter: outputLeftCh.horizontalCenter; @@ -346,9 +413,9 @@ Item { text: "Input Mono" anchors.bottom: outputLeftCh.bottom; anchors.right: outputLeftCh.left; - anchors.rightMargin: 80; - checked: true; + anchors.rightMargin: 40; onCheckedChanged: { + AudioScope.setLocalEcho(checked); } } @@ -358,21 +425,38 @@ Item { text: "Output R" anchors.bottom: outputLeftCh.bottom; anchors.left: outputLeftCh.right; - anchors.leftMargin: 80; + anchors.leftMargin: 40; onCheckedChanged: { AudioScope.setServerEcho(outputLeftCh.checked || outputRightCh.checked); } } + HifiControlsUit.Button { + id: recordButton; + text: "Record"; + color: hifi.buttons.black; + colorScheme: hifi.colorSchemes.dark; + anchors.right: parent.right; + anchors.bottom: parent.bottom; + anchors.rightMargin: 30; + anchors.bottomMargin: 8; + width: 95; + height: 55; + onClicked: { + toggleRecording(); + } + } + HifiControlsUit.Button { id: pauseButton; color: hifi.buttons.black; colorScheme: hifi.colorSchemes.dark; - anchors.right: parent.right; + anchors.right: recordButton.left; anchors.bottom: parent.bottom; anchors.rightMargin: 30; anchors.bottomMargin: 8; - height: 26; + height: 55; + width: 95; text: " Pause "; onClicked: { AudioScope.togglePause(); @@ -391,8 +475,7 @@ Item { fiveFrames.checked = false; AudioScope.selectAudioScopeTwentyFrames(); _steps = 20; - _triggered = false; - AudioScope.setTriggered(false); + AudioScope.setPause(false); } } } @@ -432,8 +515,7 @@ Item { twentyFrames.checked = false; AudioScope.selectAudioScopeFiveFrames(); _steps = 5; - _triggered = false; - AudioScope.setTriggered(false); + AudioScope.setPause(false); } } } @@ -457,8 +539,7 @@ Item { fiveFrames.checked = false; AudioScope.selectAudioScopeFiftyFrames(); _steps = 50; - _triggered = false; - AudioScope.setTriggered(false); + AudioScope.setPause(false); } } } @@ -480,9 +561,9 @@ Item { labelTextOn: "On"; onCheckedChanged: { if (!checked) AudioScope.setPause(false); - _triggered = false; - AudioScope.setTriggered(false); + AudioScope.setPause(false); AudioScope.setAutoTrigger(checked); + AudioScope.setTriggerValues(_triggerValues.x, _triggerValues.y-root.height/2); } } @@ -493,21 +574,68 @@ Item { anchors.bottom: triggerSwitch.top; } + Rectangle { + id: recordIcon; + width:110; + height:40; + anchors.right: parent.right; + anchors.top: parent.top; + anchors.topMargin: 8; + color: "transparent" + + Text { + id: recText + text: "REC" + color: "red" + font.pixelSize: 30; + anchors.left: recCircle.right; + anchors.leftMargin: 10; + opacity: _recOpacity; + y: -8; + } + + Rectangle { + id: recCircle; + width: 25; + height: 25; + radius: width*0.5 + opacity: _recOpacity; + color: "red"; + } + } + Component.onCompleted: { _steps = AudioScope.getFramesPerScope(); AudioScope.setTriggerValues(_triggerValues.x, _triggerValues.y-root.height/2); activated.checked = true; + inputCh.checked = true; updateMeasureUnits(); } Component.onDestruction: { + if (Audio.getRecording()) { + stopRecording(); + } AudioScope.setVisible(false); } Connections { target: AudioScope onPauseChanged: { - pauseButton.text = AudioScope.getPause() ? "Continue" : " Pause "; + if (!AudioScope.getPause()) { + pauseButton.text = "Pause"; + pauseButton.color = hifi.buttons.black; + AudioScope.setTriggered(false); + _triggered = false; + } else { + pauseButton.text = "Continue"; + pauseButton.color = hifi.buttons.blue; + } + } + onTriggered: { + _triggered = true; + collectTriggerData(); + AudioScope.setPause(true); } } } diff --git a/interface/src/audio/AudioScope.cpp b/interface/src/audio/AudioScope.cpp index 44f25ae5a9..1a2e867d51 100644 --- a/interface/src/audio/AudioScope.cpp +++ b/interface/src/audio/AudioScope.cpp @@ -78,6 +78,10 @@ void AudioScope::selectAudioScopeFiftyFrames() { reallocateScope(50); } +void AudioScope::setLocalEcho(bool localEcho) { + DependencyManager::get()->setLocalEcho(localEcho); +} + void AudioScope::setServerEcho(bool serverEcho) { DependencyManager::get()->setServerEcho(serverEcho); } @@ -191,6 +195,7 @@ void AudioScope::storeTriggerValues() { _triggerOutputLeftData = _scopeOutputLeftData; _triggerOutputRightData = _scopeOutputRightData; _isTriggered = true; + emit triggered(); } void AudioScope::computeInputData() { diff --git a/interface/src/audio/AudioScope.h b/interface/src/audio/AudioScope.h index 33d88f8fe0..e99b8378e3 100644 --- a/interface/src/audio/AudioScope.h +++ b/interface/src/audio/AudioScope.h @@ -71,10 +71,12 @@ public slots: QVector getTriggerOutputLeft() { return _triggerOutputLeftData; }; QVector getTriggerOutputRight() { return _triggerOutputRightData; }; + void setLocalEcho(bool serverEcho); void setServerEcho(bool serverEcho); signals: void pauseChanged(); + void triggered(); protected: AudioScope(); diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index f9c1a95fb5..7ed5c2ead9 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -58,6 +58,32 @@ Audio::Audio() : _devices(_contextIsHMD) { enableNoiseReduction(enableNoiseReductionSetting.get()); } +void Audio::onOutputBufferReceived(const QByteArray outputBuffer) { + if (_isRecording) { + _audioFileWav.addRawAudioChunk((char*)outputBuffer.data(), outputBuffer.size()); + } +} + +bool Audio::startRecording(const QString& filepath) { + auto client = DependencyManager::get().data(); + if (!_audioFileWav.create(client->getOutputFormat(), filepath)) { + qDebug() << "Error creating audio file: "+filepath; + return false; + } + connect(client, &AudioClient::outputBufferReceived, this, &Audio::onOutputBufferReceived); + _isRecording = true; + return true; +} + +void Audio::stopRecording() { + auto client = DependencyManager::get().data(); + disconnect(client, &AudioClient::outputBufferReceived, this, 0); + if (_isRecording) { + _isRecording = false; + _audioFileWav.close(); + } +} + void Audio::setMuted(bool isMuted) { if (_isMuted != isMuted) { auto client = DependencyManager::get().data(); diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index abd2312cf0..7e10761970 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -16,6 +16,7 @@ #include "AudioDevices.h" #include "AudioEffectOptions.h" #include "SettingHandle.h" +#include "AudioFileWav.h" namespace scripting { @@ -55,6 +56,10 @@ public: Q_INVOKABLE void setReverb(bool enable); Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options); + Q_INVOKABLE bool startRecording(const QString& filename); + Q_INVOKABLE void stopRecording(); + Q_INVOKABLE bool getRecording() { return _isRecording; }; + signals: void nop(); void mutedChanged(bool isMuted); @@ -71,6 +76,7 @@ private slots: void onNoiseReductionChanged(); void onInputVolumeChanged(float volume); void onInputLoudnessChanged(float loudness); + void onOutputBufferReceived(const QByteArray outputBuffer); protected: // Audio must live on a separate thread from AudioClient to avoid deadlocks @@ -83,9 +89,10 @@ private: bool _isMuted { false }; bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled. bool _contextIsHMD { false }; - + bool _isRecording { false }; AudioDevices* getDevices() { return &_devices; } AudioDevices _devices; + AudioFileWav _audioFileWav; }; }; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 78475f5b68..96b96d2bb1 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -222,8 +222,7 @@ AudioClient::AudioClient() : // initialize wasapi; if getAvailableDevices is called from the CheckDevicesThread before this, it will crash getAvailableDevices(QAudio::AudioInput); getAvailableDevices(QAudio::AudioOutput); - - + // start a thread to detect any device changes _checkDevicesTimer = new QTimer(this); connect(_checkDevicesTimer, &QTimer::timeout, [this] { @@ -1845,11 +1844,9 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { qCDebug(audiostream, "Read %d samples from buffer (%d available, %d requested)", networkSamplesPopped, _receivedAudioStream.getSamplesAvailable(), samplesRequested); AudioRingBuffer::ConstIterator lastPopOutput = _receivedAudioStream.getLastPopOutput(); lastPopOutput.readSamples(scratchBuffer, networkSamplesPopped); - for (int i = 0; i < networkSamplesPopped; i++) { mixBuffer[i] = convertToFloat(scratchBuffer[i]); } - samplesRequested = networkSamplesPopped; } @@ -1911,6 +1908,10 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { bytesWritten = maxSize; } + // send output buffer for recording + QByteArray outputBuffer(reinterpret_cast(scratchBuffer), bytesWritten); + emit _audio->outputBufferReceived(outputBuffer); + int bytesAudioOutputUnplayed = _audio->_audioOutput->bufferSize() - _audio->_audioOutput->bytesFree(); float msecsAudioOutputUnplayed = bytesAudioOutputUnplayed / (float)_audio->_outputFormat.bytesForDuration(USECS_PER_MSEC); _audio->_stats.updateOutputMsUnplayed(msecsAudioOutputUnplayed); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 4dd7436251..eb9de29411 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -47,11 +47,13 @@ #include #include + #include #include #include "AudioIOStats.h" +#include "AudioFileWav.h" #ifdef _WIN32 #pragma warning( push ) @@ -67,7 +69,6 @@ class QAudioInput; class QAudioOutput; class QIODevice; - class Transform; class NLPacket; @@ -118,6 +119,8 @@ public: const MixedProcessedAudioStream& getReceivedAudioStream() const { return _receivedAudioStream; } MixedProcessedAudioStream& getReceivedAudioStream() { return _receivedAudioStream; } + const QAudioFormat& getOutputFormat() const { return _outputFormat; } + float getLastInputLoudness() const { return _lastInputLoudness; } // TODO: relative to noise floor? float getTimeSinceLastClip() const { return _timeSinceLastClip; } @@ -142,7 +145,7 @@ public: void setIsPlayingBackRecording(bool isPlayingBackRecording) { _isPlayingBackRecording = isPlayingBackRecording; } Q_INVOKABLE void setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec3 scale); - + bool outputLocalInjector(const AudioInjectorPointer& injector) override; QAudioDeviceInfo getActiveAudioDevice(QAudio::Mode mode) const; @@ -184,12 +187,13 @@ public slots: void toggleMute(); bool isMuted() { return _muted; } - virtual void setIsStereoInput(bool stereo) override; void setNoiseReduction(bool isNoiseGateEnabled); bool isNoiseReductionEnabled() const { return _isNoiseGateEnabled; } + bool getLocalEcho() { return _shouldEchoLocally; } + void setLocalEcho(bool localEcho) { _shouldEchoLocally = localEcho; } void toggleLocalEcho() { _shouldEchoLocally = !_shouldEchoLocally; } bool getServerEcho() { return _shouldEchoToServer; } @@ -242,6 +246,8 @@ signals: void muteEnvironmentRequested(glm::vec3 position, float radius); + void outputBufferReceived(const QByteArray _outputBuffer); + protected: AudioClient(); ~AudioClient(); @@ -357,9 +363,8 @@ private: int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC]; float* _localOutputMixBuffer { NULL }; Mutex _localAudioMutex; - AudioLimiter _audioLimiter; - + // Adds Reverb void configureReverb(); void updateReverbOptions(); diff --git a/libraries/audio-client/src/AudioFileWav.cpp b/libraries/audio-client/src/AudioFileWav.cpp new file mode 100644 index 0000000000..613628883c --- /dev/null +++ b/libraries/audio-client/src/AudioFileWav.cpp @@ -0,0 +1,69 @@ +// +// AudioWavFile.h +// libraries/audio-client/src +// +// Created by Luis Cuenca on 12/1/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AudioFileWav.h" + +bool AudioFileWav::create(const QAudioFormat& audioFormat, const QString& filepath) { + if (_file.isOpen()) { + _file.close(); + } + _file.setFileName(filepath); + if (!_file.open(QIODevice::WriteOnly)) { + return false; + } + addHeader(audioFormat); + return true; +} + +bool AudioFileWav::addRawAudioChunk(char* chunk, int size) { + if (_file.isOpen()) { + QDataStream stream(&_file); + stream.writeRawData(chunk, size); + return true; + } + return false; +} + +void AudioFileWav::close() { + QDataStream stream(&_file); + stream.setByteOrder(QDataStream::LittleEndian); + + // fill RIFF and size data on header + _file.seek(4); + stream << quint32(_file.size() - 8); + _file.seek(40); + stream << quint32(_file.size() - 44); + _file.close(); +} + +void AudioFileWav::addHeader(const QAudioFormat& audioFormat) { + QDataStream stream(&_file); + + stream.setByteOrder(QDataStream::LittleEndian); + + // RIFF + stream.writeRawData("RIFF", 4); + stream << quint32(0); + stream.writeRawData("WAVE", 4); + + // Format description PCM = 16 + stream.writeRawData("fmt ", 4); + stream << quint32(16); + stream << quint16(1); + stream << quint16(audioFormat.channelCount()); + stream << quint32(audioFormat.sampleRate()); + stream << quint32(audioFormat.sampleRate() * audioFormat.channelCount() * audioFormat.sampleSize() / 8); // bytes per second + stream << quint16(audioFormat.channelCount() * audioFormat.sampleSize() / 8); // block align + stream << quint16(audioFormat.sampleSize()); // bits Per Sample + // Init data chunck + stream.writeRawData("data", 4); + stream << quint32(0); +} diff --git a/libraries/audio-client/src/AudioFileWav.h b/libraries/audio-client/src/AudioFileWav.h new file mode 100644 index 0000000000..7e9c83a23b --- /dev/null +++ b/libraries/audio-client/src/AudioFileWav.h @@ -0,0 +1,34 @@ +// +// AudioWavFile.h +// libraries/audio-client/src +// +// Created by Luis Cuenca on 12/1/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AudioFileWav_h +#define hifi_AudioFileWav_h + +#include +#include +#include +#include +#include + +class AudioFileWav : public QObject { + Q_OBJECT +public: + AudioFileWav() {} + bool create(const QAudioFormat& audioFormat, const QString& filepath); + bool addRawAudioChunk(char* chunk, int size); + void close(); + +private: + void addHeader(const QAudioFormat& audioFormat); + QFile _file; +}; + +#endif // hifi_AudioFileWav_h \ No newline at end of file From d60f0827966a9082ce19129b866edd35912690fd Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Sun, 3 Dec 2017 23:24:34 +0300 Subject: [PATCH 13/21] 9706 Editing Attachments Results In Attachments Window Scrolling To Top --- .../dialogs/content/AttachmentsContent.qml | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml b/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml index 5c9d6822c8..0e0786975e 100644 --- a/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml +++ b/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml @@ -26,6 +26,7 @@ Item { } Connections { + id: onAttachmentsChangedConnection target: MyAvatar onAttachmentsChanged: reload() } @@ -34,6 +35,12 @@ Item { reload() } + function setAttachmentsVariant(attachments) { + onAttachmentsChangedConnection.enabled = false; + MyAvatar.setAttachmentsVariant(attachments); + onAttachmentsChangedConnection.enabled = true; + } + Column { width: pane.width @@ -92,11 +99,15 @@ Item { attachments.splice(index, 1); listView.model.remove(index, 1); } - onUpdateAttachment: MyAvatar.setAttachmentsVariant(attachments); + onUpdateAttachment: { + setAttachmentsVariant(attachments); + } } } - onCountChanged: MyAvatar.setAttachmentsVariant(attachments); + onCountChanged: { + setAttachmentsVariant(attachments); + } /* // DEBUG @@ -220,7 +231,7 @@ Item { }; attachments.push(template); listView.model.append({}); - MyAvatar.setAttachmentsVariant(attachments); + setAttachmentsVariant(attachments); } } @@ -250,7 +261,7 @@ Item { id: cancelAction text: "Cancel" onTriggered: { - MyAvatar.setAttachmentsVariant(originalAttachments); + setAttachmentsVariant(originalAttachments); closeDialog(); } } @@ -263,7 +274,7 @@ Item { console.log("Attachment " + i + ": " + attachments[i]); } - MyAvatar.setAttachmentsVariant(attachments); + setAttachmentsVariant(attachments); closeDialog(); } } From 390c59b45c57591ff87655b28ca5602836f4520a Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 4 Dec 2017 12:11:17 -0800 Subject: [PATCH 14/21] Change menu and minor fixes --- interface/resources/qml/AudioScope.qml | 6 +++- interface/src/Menu.cpp | 29 +------------------ .../developer/utilities/audio/audioScope.js | 13 +++++++-- 3 files changed, 16 insertions(+), 32 deletions(-) diff --git a/interface/resources/qml/AudioScope.qml b/interface/resources/qml/AudioScope.qml index 1f67b3f090..677567c5c2 100644 --- a/interface/resources/qml/AudioScope.qml +++ b/interface/resources/qml/AudioScope.qml @@ -376,7 +376,7 @@ Item { anchors.leftMargin: 20; checked: AudioScope.getVisible(); onCheckedChanged: { - AudioScope.setVisible(!AudioScope.getVisible()); + AudioScope.setVisible(checked); activelabel.text = AudioScope.getVisible() ? "On" : "Off" } } @@ -617,6 +617,10 @@ Item { stopRecording(); } AudioScope.setVisible(false); + AudioScope.setLocalEcho(false); + AudioScope.setServerEcho(false); + AudioScope.selectAudioScopeFiveFrames(); + console.log("Component Destroyed"); } Connections { diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 7292594b53..9bbb72357b 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -679,43 +679,16 @@ Menu::Menu() { }); auto audioIO = DependencyManager::get(); - addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio, 0, false, - audioIO.data(), SLOT(toggleServerEcho())); - addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio, 0, false, - audioIO.data(), SLOT(toggleLocalEcho())); addActionToQMenuAndActionHash(audioDebugMenu, MenuOption::MuteEnvironment, 0, audioIO.data(), SLOT(sendMuteEnvironmentPacket())); - auto scope = DependencyManager::get(); - MenuWrapper* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope"); - - action = addActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScope); + action = addActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioScope); connect(action, &QAction::triggered, [] { auto scriptEngines = DependencyManager::get(); QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation(); defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/utilities/audio/audioScope.js"); scriptEngines->loadScript(defaultScriptsLoc.toString()); }); - - addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopePause, Qt::CTRL | Qt::SHIFT | Qt::Key_F2, false, - scope.data(), SLOT(togglePause())); - - addDisabledActionAndSeparator(audioScopeMenu, "Display Frames"); - { - QAction* fiveFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiveFrames, - 0, true, scope.data(), SLOT(selectAudioScopeFiveFrames())); - - QAction* twentyFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeTwentyFrames, - 0, false, scope.data(), SLOT(selectAudioScopeTwentyFrames())); - - QAction* fiftyFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiftyFrames, - 0, false, scope.data(), SLOT(selectAudioScopeFiftyFrames())); - - QActionGroup* audioScopeFramesGroup = new QActionGroup(audioScopeMenu); - audioScopeFramesGroup->addAction(fiveFrames); - audioScopeFramesGroup->addAction(twentyFrames); - audioScopeFramesGroup->addAction(fiftyFrames); - } // Developer > Physics >>> MenuWrapper* physicsOptionsMenu = developerMenu->addMenu("Physics"); diff --git a/scripts/developer/utilities/audio/audioScope.js b/scripts/developer/utilities/audio/audioScope.js index 3331482fbd..00c9e4b725 100644 --- a/scripts/developer/utilities/audio/audioScope.js +++ b/scripts/developer/utilities/audio/audioScope.js @@ -1,10 +1,17 @@ var qml = Script.resourcesPath() + '/qml/AudioScope.qml'; -var viewdim = Controller.getViewportDimensions(); var window = new OverlayWindow({ title: 'Audio Scope', source: qml, width: 1200, height: 500 }); -//window.setPosition(0.1*viewdim, 0.2*viewdim); -window.closed.connect(function () { Script.stop(); }); \ No newline at end of file +window.closed.connect(function () { + if (Audio.getRecording()) { + Audio.stopRecording(); + } + AudioScope.setVisible(false); + AudioScope.setLocalEcho(false); + AudioScope.setServerEcho(false); + AudioScope.selectAudioScopeFiveFrames(); + Script.stop(); +}); \ No newline at end of file From d70a3685b8bdd570f116461a655da721e980fd00 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 4 Dec 2017 14:41:27 -0800 Subject: [PATCH 15/21] Clean up --- interface/resources/qml/AudioScope.qml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/interface/resources/qml/AudioScope.qml b/interface/resources/qml/AudioScope.qml index 677567c5c2..aea1473c3d 100644 --- a/interface/resources/qml/AudioScope.qml +++ b/interface/resources/qml/AudioScope.qml @@ -611,17 +611,6 @@ Item { inputCh.checked = true; updateMeasureUnits(); } - - Component.onDestruction: { - if (Audio.getRecording()) { - stopRecording(); - } - AudioScope.setVisible(false); - AudioScope.setLocalEcho(false); - AudioScope.setServerEcho(false); - AudioScope.selectAudioScopeFiveFrames(); - console.log("Component Destroyed"); - } Connections { target: AudioScope From bbc022da2d0cc24ab0c147be7256959051950c87 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 5 Dec 2017 14:09:46 -0800 Subject: [PATCH 16/21] fix-far-to-equip --- scripts/system/controllers/controllerModules/equipEntity.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index b8c20d5bd6..7edeae0a53 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -10,7 +10,7 @@ getControllerJointIndex, enableDispatcherModule, disableDispatcherModule, Messages, makeDispatcherModuleParameters, makeRunningValues, Settings, entityHasActions, Vec3, Overlays, flatten, Xform, getControllerWorldLocation, ensureDynamic, entityIsCloneable, - cloneEntity, DISPATCHER_PROPERTIES + cloneEntity, DISPATCHER_PROPERTIES, TEAR_AWAY_DISTANCE */ Script.include("/~/system/libraries/Xform.js"); @@ -162,7 +162,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var ATTACH_POINT_SETTINGS = "io.highfidelity.attachPoints"; - var EQUIP_RADIUS = 0.2; // radius used for palm vs equip-hotspot for equipping. + var EQUIP_RADIUS = 0.25; // radius used for palm vs equip-hotspot for equipping. var HAPTIC_PULSE_STRENGTH = 1.0; var HAPTIC_PULSE_DURATION = 13.0; From c007c6d00f5317c0d9fc6afb7d1ff4da9eceeef6 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Wed, 6 Dec 2017 02:47:19 +0300 Subject: [PATCH 17/21] 10108 marketplace category cannot scroll --- scripts/system/html/js/marketplacesInject.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 1346bcd750..878c3b51f1 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -327,6 +327,19 @@ }); } + // fix for 10108 - marketplace category cannot scroll + function injectAddScrollbarToCategories() { + $('#categories-dropdown').on('show.bs.dropdown', function () { + $('body > div.container').css('display', 'none') + $('#categories-dropdown > ul.dropdown-menu').css({ 'overflow': 'auto', 'height': 'calc(100vh - 110px)' }) + }); + + $('#categories-dropdown').on('hide.bs.dropdown', function () { + $('body > div.container').css('display', '') + $('#categories-dropdown > ul.dropdown-menu').css({ 'overflow': '', 'height': '' }) + }); + } + function injectHiFiCode() { if (commerceMode) { maybeAddLogInButton(); @@ -358,6 +371,7 @@ } injectUnfocusOnSearch(); + injectAddScrollbarToCategories(); } function injectHiFiItemPageCode() { From 20d6615417172663a45c0ee8468e340b60cddfd4 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Tue, 5 Dec 2017 16:13:44 -0800 Subject: [PATCH 18/21] emit only when recording --- interface/src/scripting/Audio.cpp | 16 ++++++++++------ interface/src/scripting/Audio.h | 3 +-- libraries/audio-client/src/AudioClient.cpp | 7 +++++-- libraries/audio-client/src/AudioClient.h | 5 +++++ 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 7ed5c2ead9..0d0a845a7a 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -59,9 +59,7 @@ Audio::Audio() : _devices(_contextIsHMD) { } void Audio::onOutputBufferReceived(const QByteArray outputBuffer) { - if (_isRecording) { - _audioFileWav.addRawAudioChunk((char*)outputBuffer.data(), outputBuffer.size()); - } + _audioFileWav.addRawAudioChunk((char*)outputBuffer.data(), outputBuffer.size()); } bool Audio::startRecording(const QString& filepath) { @@ -71,15 +69,21 @@ bool Audio::startRecording(const QString& filepath) { return false; } connect(client, &AudioClient::outputBufferReceived, this, &Audio::onOutputBufferReceived); - _isRecording = true; + client->setRecording(true); return true; } +bool Audio::getRecording() { + auto client = DependencyManager::get().data(); + return client->getRecording(); +} + void Audio::stopRecording() { auto client = DependencyManager::get().data(); disconnect(client, &AudioClient::outputBufferReceived, this, 0); - if (_isRecording) { - _isRecording = false; + + if (client->getRecording()) { + client->setRecording(false); _audioFileWav.close(); } } diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index 7e10761970..dbedb99a0d 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -58,7 +58,7 @@ public: Q_INVOKABLE bool startRecording(const QString& filename); Q_INVOKABLE void stopRecording(); - Q_INVOKABLE bool getRecording() { return _isRecording; }; + Q_INVOKABLE bool getRecording(); signals: void nop(); @@ -89,7 +89,6 @@ private: bool _isMuted { false }; bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled. bool _contextIsHMD { false }; - bool _isRecording { false }; AudioDevices* getDevices() { return &_devices; } AudioDevices _devices; AudioFileWav _audioFileWav; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 96b96d2bb1..7524641f37 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1909,8 +1909,11 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { } // send output buffer for recording - QByteArray outputBuffer(reinterpret_cast(scratchBuffer), bytesWritten); - emit _audio->outputBufferReceived(outputBuffer); + if (_audio->_isRecording) { + QByteArray outputBuffer(reinterpret_cast(scratchBuffer), bytesWritten); + emit _audio->outputBufferReceived(outputBuffer); + } + int bytesAudioOutputUnplayed = _audio->_audioOutput->bufferSize() - _audio->_audioOutput->bytesFree(); float msecsAudioOutputUnplayed = bytesAudioOutputUnplayed / (float)_audio->_outputFormat.bytesForDuration(USECS_PER_MSEC); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index eb9de29411..5ec85aab0f 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -158,6 +158,9 @@ public: bool getNamedAudioDeviceForModeExists(QAudio::Mode mode, const QString& deviceName); + void setRecording(bool isRecording) { _isRecording = isRecording; }; + bool getRecording() { return _isRecording; }; + #ifdef Q_OS_WIN static QString getWinDeviceName(wchar_t* guid); #endif @@ -420,6 +423,8 @@ private: QTimer* _checkDevicesTimer { nullptr }; QTimer* _checkPeakValuesTimer { nullptr }; + + bool _isRecording { false }; }; From 8abe8b987944a18c2ef71c9db12b3caf6bd527d5 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Tue, 5 Dec 2017 18:22:14 -0800 Subject: [PATCH 19/21] move save logic to audioclient --- interface/src/scripting/Audio.cpp | 19 ++----------------- interface/src/scripting/Audio.h | 2 -- libraries/audio-client/src/AudioClient.cpp | 21 +++++++++++++++++++-- libraries/audio-client/src/AudioClient.h | 6 ++++++ 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 0d0a845a7a..be9b4280f7 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -58,19 +58,9 @@ Audio::Audio() : _devices(_contextIsHMD) { enableNoiseReduction(enableNoiseReductionSetting.get()); } -void Audio::onOutputBufferReceived(const QByteArray outputBuffer) { - _audioFileWav.addRawAudioChunk((char*)outputBuffer.data(), outputBuffer.size()); -} - bool Audio::startRecording(const QString& filepath) { auto client = DependencyManager::get().data(); - if (!_audioFileWav.create(client->getOutputFormat(), filepath)) { - qDebug() << "Error creating audio file: "+filepath; - return false; - } - connect(client, &AudioClient::outputBufferReceived, this, &Audio::onOutputBufferReceived); - client->setRecording(true); - return true; + return client->startRecording(filepath); } bool Audio::getRecording() { @@ -80,12 +70,7 @@ bool Audio::getRecording() { void Audio::stopRecording() { auto client = DependencyManager::get().data(); - disconnect(client, &AudioClient::outputBufferReceived, this, 0); - - if (client->getRecording()) { - client->setRecording(false); - _audioFileWav.close(); - } + client->stopRecording(); } void Audio::setMuted(bool isMuted) { diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index dbedb99a0d..0f0043510c 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -76,7 +76,6 @@ private slots: void onNoiseReductionChanged(); void onInputVolumeChanged(float volume); void onInputLoudnessChanged(float loudness); - void onOutputBufferReceived(const QByteArray outputBuffer); protected: // Audio must live on a separate thread from AudioClient to avoid deadlocks @@ -91,7 +90,6 @@ private: bool _contextIsHMD { false }; AudioDevices* getDevices() { return &_devices; } AudioDevices _devices; - AudioFileWav _audioFileWav; }; }; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 7524641f37..af86499101 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -79,6 +79,7 @@ Setting::Handle staticJitterBufferFrames("staticJitterBufferFrames", using Mutex = std::mutex; using Lock = std::unique_lock; Mutex _deviceMutex; +Mutex _recordMutex; // thread-safe QList getAvailableDevices(QAudio::Mode mode) { @@ -1910,8 +1911,8 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { // send output buffer for recording if (_audio->_isRecording) { - QByteArray outputBuffer(reinterpret_cast(scratchBuffer), bytesWritten); - emit _audio->outputBufferReceived(outputBuffer); + Lock lock(_recordMutex); + _audio->_audioFileWav.addRawAudioChunk(reinterpret_cast(scratchBuffer), bytesWritten); } @@ -1926,6 +1927,22 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { return bytesWritten; } +bool AudioClient::startRecording(const QString& filepath) { + if (!_audioFileWav.create(_outputFormat, filepath)) { + qDebug() << "Error creating audio file: " + filepath; + return false; + } + _isRecording = true; + return true; +} + +void AudioClient::stopRecording() { + if (_isRecording) { + _isRecording = false; + _audioFileWav.close(); + } +} + void AudioClient::loadSettings() { _receivedAudioStream.setDynamicJitterBufferEnabled(dynamicJitterBufferEnabled.get()); _receivedAudioStream.setStaticJitterBufferFrames(staticJitterBufferFrames.get()); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 5ec85aab0f..0ceb9c4dc3 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -161,6 +161,10 @@ public: void setRecording(bool isRecording) { _isRecording = isRecording; }; bool getRecording() { return _isRecording; }; + bool startRecording(const QString& filename); + void stopRecording(); + + #ifdef Q_OS_WIN static QString getWinDeviceName(wchar_t* guid); #endif @@ -402,6 +406,8 @@ private: QList _inputDevices; QList _outputDevices; + AudioFileWav _audioFileWav; + bool _hasReceivedFirstPacket { false }; QVector _activeLocalAudioInjectors; From 32fca16c8579badef6036ceb633547360848a384 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 6 Dec 2017 14:16:32 -0800 Subject: [PATCH 20/21] fix radius equiping --- .../controllers/controllerModules/equipEntity.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index 7edeae0a53..a250f77b2e 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -138,9 +138,9 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var dimensions; if (overlayInfoSet.type === "sphere") { - dimensions = overlayInfoSet.hotspot.radius * 2 * overlayInfoSet.currentSize * EQUIP_SPHERE_SCALE_FACTOR; + dimensions = (overlayInfoSet.hotspot.radius / 2) * overlayInfoSet.currentSize * EQUIP_SPHERE_SCALE_FACTOR; } else { - dimensions = overlayInfoSet.hotspot.radius * 2 * overlayInfoSet.currentSize; + dimensions = (overlayInfoSet.hotspot.radius / 2) * overlayInfoSet.currentSize; } overlayInfoSet.overlays.forEach(function(overlay) { @@ -162,7 +162,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var ATTACH_POINT_SETTINGS = "io.highfidelity.attachPoints"; - var EQUIP_RADIUS = 0.25; // radius used for palm vs equip-hotspot for equipping. + var EQUIP_RADIUS = 1.0; // radius used for palm vs equip-hotspot for equipping. var HAPTIC_PULSE_STRENGTH = 1.0; var HAPTIC_PULSE_DURATION = 13.0; @@ -322,7 +322,9 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa } } else { var wearableProps = getWearableData(props); + var sensorToScaleFactor = MyAvatar.sensorToWorldScale; if (wearableProps && wearableProps.joints) { + result.push({ key: entityID.toString() + "0", entityID: entityID, @@ -332,7 +334,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa z: 0 }, worldPosition: entityXform.pos, - radius: EQUIP_RADIUS, + radius: EQUIP_RADIUS * sensorToScaleFactor, joints: wearableProps.joints, modelURL: null, modelScale: null From 542af47e9edd7e5416643de01893a4d75138b1a6 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 29 Nov 2017 17:20:54 -0800 Subject: [PATCH 21/21] Fix for hand controller avatar scaling. * Added getDomainMaxScale() and getDomainMinScale() to JS api. * Updated scaleAvatar controller module to use this to prevent scaling past the limits. * Made sure that getDomainMaxScale() getDomainMinScale() and getUnscaledEyeHeight are thread safe, so that they can be invoked on the script thread. * Added signals to Model class that can be used to let observers know when the Rig has finished initializing it's skeleton. and also when the skeleton is no longer valid. These hooks are used to cache the unscaled eye height of the avatar. --- interface/src/avatar/MyAvatar.cpp | 3 +- .../src/avatars-renderer/Avatar.cpp | 30 +++++++++++++------ .../src/avatars-renderer/Avatar.h | 13 ++++++-- .../src/avatars-renderer/OtherAvatar.cpp | 2 ++ libraries/avatars/src/AvatarData.h | 18 +++++++++-- libraries/render-utils/src/Model.cpp | 2 ++ libraries/render-utils/src/Model.h | 2 ++ .../controllerModules/scaleAvatar.js | 6 +++- 8 files changed, 61 insertions(+), 15 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1623fa3213..c874205611 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -114,7 +114,8 @@ MyAvatar::MyAvatar(QThread* thread) : _skeletonModel = std::make_shared(this, nullptr); connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); - + connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady); + connect(_skeletonModel.get(), &Model::rigReset, this, &Avatar::rigReset); using namespace recording; _skeletonModel->flagAsCauterized(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 437ac623e3..bb7f141cd9 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1217,6 +1217,15 @@ void Avatar::setModelURLFinished(bool success) { } } +// rig is ready +void Avatar::rigReady() { + buildUnscaledEyeHeightCache(); +} + +// rig has been reset. +void Avatar::rigReset() { + clearUnscaledEyeHeightCache(); +} // create new model, can return an instance of a SoftAttachmentModel rather then Model static std::shared_ptr allocateAttachmentModel(bool isSoft, const Rig& rigOverride, bool isCauterized) { @@ -1584,18 +1593,17 @@ void Avatar::ensureInScene(AvatarSharedPointer self, const render::ScenePointer& } } +// thread-safe float Avatar::getEyeHeight() const { - - if (QThread::currentThread() != thread()) { - float result = DEFAULT_AVATAR_EYE_HEIGHT; - BLOCKING_INVOKE_METHOD(const_cast(this), "getEyeHeight", Q_RETURN_ARG(float, result)); - return result; - } - return getModelScale() * getUnscaledEyeHeight(); } +// thread-safe float Avatar::getUnscaledEyeHeight() const { + return _unscaledEyeHeightCache.get(); +} + +void Avatar::buildUnscaledEyeHeightCache() { float skeletonHeight = getUnscaledEyeHeightFromSkeleton(); // Sanity check by looking at the model extents. @@ -1606,12 +1614,16 @@ float Avatar::getUnscaledEyeHeight() const { // This helps prevent absurdly large avatars from exceeding the domain height limit. const float MESH_SLOP_RATIO = 1.5f; if (meshHeight > skeletonHeight * MESH_SLOP_RATIO) { - return meshHeight; + _unscaledEyeHeightCache.set(meshHeight); } else { - return skeletonHeight; + _unscaledEyeHeightCache.set(skeletonHeight); } } +void Avatar::clearUnscaledEyeHeightCache() { + _unscaledEyeHeightCache.set(DEFAULT_AVATAR_EYE_HEIGHT); +} + float Avatar::getUnscaledEyeHeightFromSkeleton() const { // TODO: if performance becomes a concern we can cache this value rather then computing it everytime. diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 44d4ef4c51..c75b54fdc4 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -257,8 +257,8 @@ public: Q_INVOKABLE virtual float getEyeHeight() const override; - // returns eye height of avatar in meters, ignoreing avatar scale. - // if _targetScale is 1 then this will be identical to getEyeHeight; + // returns eye height of avatar in meters, ignoring avatar scale. + // if _targetScale is 1 then this will be identical to getEyeHeight. virtual float getUnscaledEyeHeight() const override; // returns true, if an acurate eye height estimage can be obtained by inspecting the avatar model skeleton and geometry, @@ -280,10 +280,17 @@ public slots: glm::vec3 getRightPalmPosition() const; glm::quat getRightPalmRotation() const; + // hooked up to Model::setURLFinished signal void setModelURLFinished(bool success); + // hooked up to Model::rigReady & rigReset signals + void rigReady(); + void rigReset(); + protected: float getUnscaledEyeHeightFromSkeleton() const; + void buildUnscaledEyeHeightCache(); + void clearUnscaledEyeHeightCache(); virtual const QString& getSessionDisplayNameForTransport() const override { return _empty; } // Save a tiny bit of bandwidth. Mixer won't look at what we send. QString _empty{}; virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) override { _sessionDisplayName = sessionDisplayName; } // don't use no-op setter! @@ -384,6 +391,8 @@ protected: float _displayNameTargetAlpha { 1.0f }; float _displayNameAlpha { 1.0f }; + + ThreadSafeValueCache _unscaledEyeHeightCache { DEFAULT_AVATAR_EYE_HEIGHT }; }; #endif // hifi_Avatar_h diff --git a/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.cpp index e870e2de12..4382216575 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.cpp @@ -13,4 +13,6 @@ OtherAvatar::OtherAvatar(QThread* thread) : Avatar(thread) { _headData = new Head(this); _skeletonModel = std::make_shared(this, nullptr); connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); + connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady); + connect(_skeletonModel.get(), &Model::rigReset, this, &Avatar::rigReset); } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index de98675733..d7dd2837cb 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -483,8 +483,22 @@ public: virtual void setTargetScale(float targetScale); float getDomainLimitedScale() const; - float getDomainMinScale() const; - float getDomainMaxScale() const; + + /**jsdoc + * returns the minimum scale allowed for this avatar in the current domain. + * This value can change as the user changes avatars or when changing domains. + * @function AvatarData.getDomainMinScale + * @returns {number} minimum scale allowed for this avatar in the current domain. + */ + Q_INVOKABLE float getDomainMinScale() const; + + /**jsdoc + * returns the maximum scale allowed for this avatar in the current domain. + * This value can change as the user changes avatars or when changing domains. + * @function AvatarData.getDomainMaxScale + * @returns {number} maximum scale allowed for this avatar in the current domain. + */ + Q_INVOKABLE float getDomainMaxScale() const; // returns eye height of avatar in meters, ignoreing avatar scale. // if _targetScale is 1 then this will be identical to getEyeHeight; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 199cb29f53..c4bc435691 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -286,6 +286,7 @@ void Model::reset() { if (isLoaded()) { const FBXGeometry& geometry = getFBXGeometry(); _rig.reset(geometry); + emit rigReset(); } } @@ -322,6 +323,7 @@ bool Model::updateGeometry() { _blendedVertexBuffers.push_back(buffer); } needFullUpdate = true; + emit rigReady(); } return needFullUpdate; } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index fadd44b745..7568a17342 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -273,6 +273,8 @@ signals: void setURLFinished(bool success); void setCollisionModelURLFinished(bool success); void requestRenderUpdate(); + void rigReady(); + void rigReset(); protected: diff --git a/scripts/system/controllers/controllerModules/scaleAvatar.js b/scripts/system/controllers/controllerModules/scaleAvatar.js index e4ae3654e1..1868b0228a 100644 --- a/scripts/system/controllers/controllerModules/scaleAvatar.js +++ b/scripts/system/controllers/controllerModules/scaleAvatar.js @@ -12,6 +12,10 @@ (function () { var dispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); + function clamp(val, min, max) { + return Math.max(min, Math.min(max, val)); + } + function ScaleAvatar(hand) { this.hand = hand; this.scalingStartAvatarScale = 0; @@ -61,7 +65,7 @@ controllerData.controllerLocations[this.otherHand()].position)); var newAvatarScale = (scalingCurrentDistance / this.scalingStartDistance) * this.scalingStartAvatarScale; - MyAvatar.scale = newAvatarScale; + MyAvatar.scale = clamp(newAvatarScale, MyAvatar.getDomainMinScale(), MyAvatar.getDomainMaxScale()); MyAvatar.scaleChanged(); } return dispatcherUtils.makeRunningValues(true, [], []);