From b5a45be7b986995e8be2e5f1fffe071331b3e514 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 15 Mar 2019 01:08:53 +0100 Subject: [PATCH 01/21] avatar doctor documentation urls --- interface/src/avatar/AvatarDoctor.cpp | 65 ++++++++++++++------------- interface/src/avatar/AvatarDoctor.h | 2 + 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/interface/src/avatar/AvatarDoctor.cpp b/interface/src/avatar/AvatarDoctor.cpp index 04a426c3db..43e50ea049 100644 --- a/interface/src/avatar/AvatarDoctor.cpp +++ b/interface/src/avatar/AvatarDoctor.cpp @@ -55,7 +55,7 @@ static QStringList HAND_MAPPING_SUFFIXES = { "HandThumb1", }; -const QUrl DEFAULT_DOCS_URL = QUrl("https://docs.highfidelity.com/create/avatars/create-avatars.html#create-your-own-avatar"); +const QUrl PACKAGE_AVATAR_DOCS_BASE_URL = QUrl("https://docs.highfidelity.com/create/avatars/package-avatar.html"); AvatarDoctor::AvatarDoctor(const QUrl& avatarFSTFileUrl) : _avatarFSTFileUrl(avatarFSTFileUrl) { @@ -85,7 +85,7 @@ void AvatarDoctor::startDiagnosing() { const auto resourceLoaded = [this, resource](bool success) { // MODEL if (!success) { - _errors.push_back({ "Model file cannot be opened.", DEFAULT_DOCS_URL }); + addError("Model file cannot be opened.", "missing-file"); emit complete(getErrors()); return; } @@ -93,45 +93,45 @@ void AvatarDoctor::startDiagnosing() { const auto model = resource.data(); const auto avatarModel = resource.data()->getHFMModel(); if (!avatarModel.originalURL.endsWith(".fbx")) { - _errors.push_back({ "Unsupported avatar model format.", DEFAULT_DOCS_URL }); + addError("Unsupported avatar model format.", "unsupported-format"); emit complete(getErrors()); return; } // RIG if (avatarModel.joints.isEmpty()) { - _errors.push_back({ "Avatar has no rig.", DEFAULT_DOCS_URL }); + addError("Avatar has no rig.", "no-rig"); } else { auto jointNames = avatarModel.getJointNames(); if (avatarModel.joints.length() > NETWORKED_JOINTS_LIMIT) { - _errors.push_back({tr( "Avatar has over %n bones.", "", NETWORKED_JOINTS_LIMIT), DEFAULT_DOCS_URL }); + addError(tr( "Avatar has over %n bones.", "", NETWORKED_JOINTS_LIMIT), "maximum-bone-limit"); } // Avatar does not have Hips bone mapped if (!jointNames.contains("Hips")) { - _errors.push_back({ "Hips are not mapped.", DEFAULT_DOCS_URL }); + addError("Hips are not mapped.", "hips-not-mapped"); } if (!jointNames.contains("Spine")) { - _errors.push_back({ "Spine is not mapped.", DEFAULT_DOCS_URL }); + addError("Spine is not mapped.", "spine-not-mapped"); } if (!jointNames.contains("Spine1")) { - _errors.push_back({ "Chest (Spine1) is not mapped.", DEFAULT_DOCS_URL }); + addError("Chest (Spine1) is not mapped.", "chest-not-mapped"); } if (!jointNames.contains("Neck")) { - _errors.push_back({ "Neck is not mapped.", DEFAULT_DOCS_URL }); + addError("Neck is not mapped.", "neck-not-mapped"); } if (!jointNames.contains("Head")) { - _errors.push_back({ "Head is not mapped.", DEFAULT_DOCS_URL }); + addError("Head is not mapped.", "head-not-mapped"); } if (!jointNames.contains("LeftEye")) { if (jointNames.contains("RightEye")) { - _errors.push_back({ "LeftEye is not mapped.", DEFAULT_DOCS_URL }); + addError("LeftEye is not mapped.", "eye-not-mapped"); } else { - _errors.push_back({ "Eyes are not mapped.", DEFAULT_DOCS_URL }); + addError("Eyes are not mapped.", "eye-not-mapped"); } } else if (!jointNames.contains("RightEye")) { - _errors.push_back({ "RightEye is not mapped.", DEFAULT_DOCS_URL }); + addError("RightEye is not mapped.", "eye-not-mapped"); } const auto checkJointAsymmetry = [jointNames] (const QStringList& jointMappingSuffixes) { @@ -159,13 +159,13 @@ void AvatarDoctor::startDiagnosing() { }; if (checkJointAsymmetry(ARM_MAPPING_SUFFIXES)) { - _errors.push_back({ "Asymmetrical arm bones.", DEFAULT_DOCS_URL }); + addError("Asymmetrical arm bones.", "asymmetrical-bones"); } if (checkJointAsymmetry(HAND_MAPPING_SUFFIXES)) { - _errors.push_back({ "Asymmetrical hand bones.", DEFAULT_DOCS_URL }); + addError("Asymmetrical hand bones.", "asymmetrical-bones"); } if (checkJointAsymmetry(LEG_MAPPING_SUFFIXES)) { - _errors.push_back({ "Asymmetrical leg bones.", DEFAULT_DOCS_URL }); + addError("Asymmetrical leg bones.", "asymmetrical-bones"); } // Multiple skeleton root joints checkup @@ -177,7 +177,7 @@ void AvatarDoctor::startDiagnosing() { } if (skeletonRootJoints > 1) { - _errors.push_back({ "Multiple top-level joints found.", DEFAULT_DOCS_URL }); + addError("Multiple top-level joints found.", "multiple-top-level-joints"); } Rig rig; @@ -191,9 +191,9 @@ void AvatarDoctor::startDiagnosing() { const float RECOMMENDED_MAX_HEIGHT = DEFAULT_AVATAR_HEIGHT * 1.5f; if (avatarHeight < RECOMMENDED_MIN_HEIGHT) { - _errors.push_back({ "Avatar is possibly too short.", DEFAULT_DOCS_URL }); + addError("Avatar is possibly too short.", "short-avatar"); } else if (avatarHeight > RECOMMENDED_MAX_HEIGHT) { - _errors.push_back({ "Avatar is possibly too tall.", DEFAULT_DOCS_URL }); + addError("Avatar is possibly too tall.", "tall-avatar"); } // HipsNotOnGround @@ -204,7 +204,7 @@ void AvatarDoctor::startDiagnosing() { const auto hipJoint = avatarModel.joints.at(avatarModel.getJointIndex("Hips")); if (hipsPosition.y < HIPS_GROUND_MIN_Y) { - _errors.push_back({ "Hips are on ground.", DEFAULT_DOCS_URL }); + addError("Hips are on ground.", "hips-on-ground"); } } } @@ -223,7 +223,7 @@ void AvatarDoctor::startDiagnosing() { const auto hipsToSpine = glm::length(hipsPosition - spinePosition); const auto spineToChest = glm::length(spinePosition - chestPosition); if (hipsToSpine < HIPS_SPINE_CHEST_MIN_SEPARATION && spineToChest < HIPS_SPINE_CHEST_MIN_SEPARATION) { - _errors.push_back({ "Hips/Spine/Chest overlap.", DEFAULT_DOCS_URL }); + addError("Hips/Spine/Chest overlap.", "overlap-error"); } } } @@ -240,21 +240,21 @@ void AvatarDoctor::startDiagnosing() { const auto& uniqueJointValues = jointValues.toSet(); for (const auto& jointName: uniqueJointValues) { if (jointValues.count(jointName) > 1) { - _errors.push_back({ tr("%1 is mapped multiple times.").arg(jointName), DEFAULT_DOCS_URL }); + addError(tr("%1 is mapped multiple times.").arg(jointName), "mapped-multiple-times"); } } } if (!isDescendantOfJointWhenJointsExist("Spine", "Hips")) { - _errors.push_back({ "Spine is not a child of Hips.", DEFAULT_DOCS_URL }); + addError("Spine is not a child of Hips.", "spine-not-child"); } if (!isDescendantOfJointWhenJointsExist("Spine1", "Spine")) { - _errors.push_back({ "Spine1 is not a child of Spine.", DEFAULT_DOCS_URL }); + addError("Spine1 is not a child of Spine.", "spine1-not-child"); } if (!isDescendantOfJointWhenJointsExist("Head", "Spine1")) { - _errors.push_back({ "Head is not a child of Spine1.", DEFAULT_DOCS_URL }); + addError("Head is not a child of Spine1.", "head-not-child"); } } @@ -300,7 +300,7 @@ void AvatarDoctor::startDiagnosing() { connect(resource.data(), &GeometryResource::finished, this, resourceLoaded); } } else { - _errors.push_back({ "Model file cannot be opened", DEFAULT_DOCS_URL }); + addError("Model file cannot be opened", "missing-file"); emit complete(getErrors()); } } @@ -345,7 +345,7 @@ void AvatarDoctor::diagnoseTextures() { QUrl(avatarModel.originalURL)).resolved(QUrl("textures")); if (texturesFound == 0) { - _errors.push_back({ tr("No textures assigned."), DEFAULT_DOCS_URL }); + addError(tr("No textures assigned."), "no-textures-assigned"); } if (!externalTextures.empty()) { @@ -356,11 +356,10 @@ void AvatarDoctor::diagnoseTextures() { auto checkTextureLoadingComplete = [this]() mutable { if (_checkedTextureCount == _externalTextureCount) { if (_missingTextureCount > 0) { - _errors.push_back({ tr("Missing %n texture(s).","", _missingTextureCount), DEFAULT_DOCS_URL }); + addError(tr("Missing %n texture(s).","", _missingTextureCount), "missing-textures"); } if (_unsupportedTextureCount > 0) { - _errors.push_back({ tr("%n unsupported texture(s) found.", "", _unsupportedTextureCount), - DEFAULT_DOCS_URL }); + addError(tr("%n unsupported texture(s) found.", "", _unsupportedTextureCount), "unsupported-textures"); } emit complete(getErrors()); @@ -411,6 +410,12 @@ void AvatarDoctor::diagnoseTextures() { } } +void AvatarDoctor::addError(const QString& errorMessage, const QString& docFragment) { + QUrl documentationURL = PACKAGE_AVATAR_DOCS_BASE_URL; + documentationURL.setFragment(docFragment); + _errors.push_back({ errorMessage, documentationURL }); +} + QVariantList AvatarDoctor::getErrors() const { QVariantList result; for (const auto& error : _errors) { diff --git a/interface/src/avatar/AvatarDoctor.h b/interface/src/avatar/AvatarDoctor.h index 780f600bed..1465a5defc 100644 --- a/interface/src/avatar/AvatarDoctor.h +++ b/interface/src/avatar/AvatarDoctor.h @@ -40,6 +40,8 @@ signals: private: void diagnoseTextures(); + void addError(const QString& errorMessage, const QString& docFragment); + QUrl _avatarFSTFileUrl; QVector _errors; From 714115adef8b485f95872c3a409f0c1e44b8abaf Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 15 Mar 2019 21:36:46 +0100 Subject: [PATCH 02/21] - Video, and new Docs url in QML --- .../hifi/avatarPackager/AvatarPackagerApp.qml | 11 ++++++++-- .../avatarPackager/AvatarPackagerHeader.qml | 20 ++++++++++++++++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml index 8afc60fd90..278ce36362 100644 --- a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml +++ b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerApp.qml @@ -133,7 +133,7 @@ Item { states: [ State { name: AvatarPackagerState.main - PropertyChanges { target: avatarPackagerHeader; title: qsTr("Avatar Packager"); docsEnabled: true; backButtonVisible: false } + PropertyChanges { target: avatarPackagerHeader; title: qsTr("Avatar Packager"); docsEnabled: true; videoEnabled: true; backButtonVisible: false } PropertyChanges { target: avatarPackagerMain; visible: true } PropertyChanges { target: avatarPackagerFooter; content: avatarPackagerMain.footer } }, @@ -229,7 +229,11 @@ Item { } function openDocs() { - Qt.openUrlExternally("https://docs.highfidelity.com/create/avatars/create-avatars#how-to-package-your-avatar"); + Qt.openUrlExternally("https://docs.highfidelity.com/create/avatars/package-avatar.html"); + } + + function openVideo() { + Qt.openUrlExternally("https://youtu.be/zrkEowu_yps"); } AvatarPackagerHeader { @@ -243,6 +247,9 @@ Item { onDocsButtonClicked: { avatarPackager.openDocs(); } + onVideoButtonClicked: { + avatarPackager.openVideo(); + } } Item { diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerHeader.qml b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerHeader.qml index 25201bf81e..31528a8557 100644 --- a/interface/resources/qml/hifi/avatarPackager/AvatarPackagerHeader.qml +++ b/interface/resources/qml/hifi/avatarPackager/AvatarPackagerHeader.qml @@ -13,6 +13,7 @@ ShadowRectangle { property string title: qsTr("Avatar Packager") property alias docsEnabled: docs.visible + property alias videoEnabled: video.visible property bool backButtonVisible: true // If false, is not visible and does not take up space property bool backButtonEnabled: true // If false, is not visible but does not affect space property bool canRename: false @@ -24,6 +25,7 @@ ShadowRectangle { signal backButtonClicked signal docsButtonClicked + signal videoButtonClicked RalewayButton { id: back @@ -126,6 +128,20 @@ ShadowRectangle { } } + RalewayButton { + id: video + visible: false + size: 28 + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: docs.left + anchors.rightMargin: 16 + + text: qsTr("Video") + + onClicked: videoButtonClicked() + } + RalewayButton { id: docs visible: false @@ -137,8 +153,6 @@ ShadowRectangle { text: qsTr("Docs") - onClicked: { - docsButtonClicked(); - } + onClicked: docsButtonClicked() } } From 7a5bbb8f6f42b59e85f8678e066cca7b3dfcfa4c Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 18 Mar 2019 14:43:32 -0700 Subject: [PATCH 03/21] fix laser ghosting --- .../src/RenderablePolyLineEntityItem.cpp | 90 +++++++++++-------- .../src/RenderablePolyLineEntityItem.h | 2 +- 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 98f79780be..df52934b87 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -95,19 +95,18 @@ bool PolyLineEntityRenderer::needsRenderUpdate() const { } bool PolyLineEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { - return ( - entity->pointsChanged() || - entity->widthsChanged() || - entity->normalsChanged() || - entity->texturesChanged() || - entity->colorsChanged() || - _isUVModeStretch != entity->getIsUVModeStretch() || - _glow != entity->getGlow() || - _faceCamera != entity->getFaceCamera() - ); + if (entity->pointsChanged() || entity->widthsChanged() || entity->normalsChanged() || entity->texturesChanged() || entity->colorsChanged()) { + return true; + } + + if (_isUVModeStretch != entity->getIsUVModeStretch() || _glow != entity->getGlow() || _faceCamera != entity->getFaceCamera()) { + return true; + } + + return Parent::needsRenderUpdateFromTypedEntity(entity); } -void PolyLineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { +void PolyLineEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { auto pointsChanged = entity->pointsChanged(); auto widthsChanged = entity->widthsChanged(); auto normalsChanged = entity->normalsChanged(); @@ -119,10 +118,6 @@ void PolyLineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo entity->resetPolyLineChanged(); - // Transform - updateModelTransformAndBound(); - _renderTransform = getModelTransform(); - // Textures if (entity->texturesChanged()) { entity->resetTexturesChanged(); @@ -131,7 +126,9 @@ void PolyLineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo if (!textures.isEmpty()) { entityTextures = QUrl(textures); } - _texture = DependencyManager::get()->getTexture(entityTextures); + withWriteLock([&] { + _texture = DependencyManager::get()->getTexture(entityTextures); + }); _textureAspectRatio = 1.0f; _textureLoaded = false; } @@ -145,11 +142,13 @@ void PolyLineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo // Data bool faceCameraChanged = faceCamera != _faceCamera; - if (faceCameraChanged || glow != _glow) { - _faceCamera = faceCamera; - _glow = glow; - updateData(); - } + withWriteLock([&] { + if (faceCameraChanged || glow != _glow) { + _faceCamera = faceCamera; + _glow = glow; + updateData(); + } + }); // Geometry if (pointsChanged) { @@ -165,10 +164,21 @@ void PolyLineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo _colors = entity->getStrokeColors(); _color = toGlm(entity->getColor()); } - if (_isUVModeStretch != isUVModeStretch || pointsChanged || widthsChanged || normalsChanged || colorsChanged || textureChanged || faceCameraChanged) { - _isUVModeStretch = isUVModeStretch; - updateGeometry(); - } + + bool uvModeStretchChanged = _isUVModeStretch != isUVModeStretch; + _isUVModeStretch = isUVModeStretch; + + void* key = (void*)this; + AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [=]() { + withWriteLock([&] { + updateModelTransformAndBound(); + _renderTransform = getModelTransform(); + + if (uvModeStretchChanged || pointsChanged || widthsChanged || normalsChanged || colorsChanged || textureChanged || faceCameraChanged) { + updateGeometry(); + } + }); + }); } void PolyLineEntityRenderer::updateGeometry() { @@ -267,22 +277,32 @@ void PolyLineEntityRenderer::updateData() { } void PolyLineEntityRenderer::doRender(RenderArgs* args) { - if (_numVertices < 2) { - return; - } - PerformanceTimer perfTimer("RenderablePolyLineEntityItem::render"); Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - if (!_pipeline || !_glowPipeline) { + size_t numVertices; + Transform transform; + gpu::TexturePointer texture; + withReadLock([&] { + numVertices = _numVertices; + transform = _renderTransform; + texture = _textureLoaded ? _texture->getGPUTexture() : DependencyManager::get()->getWhiteTexture(); + + batch.setResourceBuffer(0, _polylineGeometryBuffer); + batch.setUniformBuffer(0, _polylineDataBuffer); + }); + + if (numVertices < 2) { + return; + } + + if (!_pipeline) { buildPipeline(); } batch.setPipeline(_glow ? _glowPipeline : _pipeline); - batch.setModelTransform(_renderTransform); - batch.setResourceTexture(0, _textureLoaded ? _texture->getGPUTexture() : DependencyManager::get()->getWhiteTexture()); - batch.setResourceBuffer(0, _polylineGeometryBuffer); - batch.setUniformBuffer(0, _polylineDataBuffer); - batch.draw(gpu::TRIANGLE_STRIP, (gpu::uint32)(2 * _numVertices), 0); + batch.setModelTransform(transform); + batch.setResourceTexture(0, texture); + batch.draw(gpu::TRIANGLE_STRIP, (gpu::uint32)(2 * numVertices), 0); } diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index b8a3ad3b3e..3815b57671 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -31,7 +31,7 @@ public: protected: virtual bool needsRenderUpdate() const override; virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; - virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override; + virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; virtual ItemKey getKey() override; virtual ShapeKey getShapeKey() override; From 3775633cc3b61f8391d7dc7088265fd3118fd8a1 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Wed, 20 Mar 2019 15:29:13 -0700 Subject: [PATCH 04/21] Case 20407 - Pay-In API doesn't display error when no username specified --- .../resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml index 626ac4da65..e159344d5c 100644 --- a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml +++ b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml @@ -2248,6 +2248,7 @@ Item { if (sendAssetStep.selectedRecipientUserName === "") { console.log("SendAsset: Script didn't specify a recipient username!"); sendAssetHome.visible = false; + root.nextActiveView = 'paymentFailure'; return; } From 61b7b8b66963a5f1fbae027e81fcc0705d074e34 Mon Sep 17 00:00:00 2001 From: Sam Gondelman Date: Thu, 21 Mar 2019 08:36:32 -0700 Subject: [PATCH 05/21] reduce lambda copies --- .../entities-renderer/src/RenderablePolyLineEntityItem.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index df52934b87..2430643ce2 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -167,14 +167,16 @@ void PolyLineEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& bool uvModeStretchChanged = _isUVModeStretch != isUVModeStretch; _isUVModeStretch = isUVModeStretch; + + bool geometryChanged = uvModeStretchChanged || pointsChanged || widthsChanged || normalsChanged || colorsChanged || textureChanged || faceCameraChanged; void* key = (void*)this; - AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [=]() { + AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, geometryChanged] () { withWriteLock([&] { updateModelTransformAndBound(); _renderTransform = getModelTransform(); - if (uvModeStretchChanged || pointsChanged || widthsChanged || normalsChanged || colorsChanged || textureChanged || faceCameraChanged) { + if (geometryChanged) { updateGeometry(); } }); From 7db498a1308d9147613ae8b1be79845d0647cb67 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 22 Mar 2019 16:20:54 -0700 Subject: [PATCH 06/21] fix shadows when tablet is open and misc threading issues --- .../src/RenderableMaterialEntityItem.cpp | 6 +++++- .../src/RenderableShapeEntityItem.cpp | 18 ++++++++++++++---- .../src/RenderableTextEntityItem.cpp | 8 +++----- libraries/render-utils/src/GeometryCache.h | 11 ++++++----- .../render-utils/src/RenderCommonTask.cpp | 6 +++++- .../render-utils/src/RenderDeferredTask.cpp | 8 ++++---- .../render-utils/src/RenderDeferredTask.h | 10 +++++----- .../render-utils/src/RenderForwardTask.cpp | 10 +++++++--- libraries/render-utils/src/RenderForwardTask.h | 3 ++- .../src/simple_transparent_textured.slf | 4 ++-- 10 files changed, 53 insertions(+), 31 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index 2eb877b0e1..15842336f5 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -186,7 +186,11 @@ void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo if (_networkMaterial->isLoaded()) { onMaterialRequestFinished(!_networkMaterial->isFailed()); } else { - connect(_networkMaterial.data(), &Resource::finished, this, onMaterialRequestFinished); + connect(_networkMaterial.data(), &Resource::finished, this, [&](bool success) { + withWriteLock([&] { + onMaterialRequestFinished(success); + }); + }); } } } else if (materialDataChanged && usingMaterialData) { diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index b33eb619c8..28b0bb8f23 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -249,10 +249,14 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { graphics::MultiMaterial materials; auto geometryCache = DependencyManager::get(); GeometryCache::Shape geometryShape; + PrimitiveMode primitiveMode; + RenderLayer renderLayer; bool proceduralRender = false; glm::vec4 outColor; withReadLock([&] { geometryShape = geometryCache->getShapeForEntityShape(_shape); + primitiveMode = _primitiveMode; + renderLayer = _renderLayer; batch.setModelTransform(_renderTransform); // use a transform with scale, rotation, registration point and translation materials = _materials["0"]; auto& schema = materials.getSchemaBuffer().get(); @@ -267,7 +271,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { }); if (proceduralRender) { - if (render::ShapeKey(args->_globalShapeKey).isWireframe()) { + if (render::ShapeKey(args->_globalShapeKey).isWireframe() || primitiveMode == PrimitiveMode::LINES) { geometryCache->renderWireShape(batch, geometryShape, outColor); } else { geometryCache->renderShape(batch, geometryShape, outColor); @@ -275,10 +279,16 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { } else if (!useMaterialPipeline(materials)) { // FIXME, support instanced multi-shape rendering using multidraw indirect outColor.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; - if (render::ShapeKey(args->_globalShapeKey).isWireframe() || _primitiveMode == PrimitiveMode::LINES) { - geometryCache->renderWireShapeInstance(args, batch, geometryShape, outColor, args->_shapePipeline); + render::ShapePipelinePointer pipeline; + if (renderLayer == RenderLayer::WORLD) { + pipeline = GeometryCache::getShapePipeline(false, outColor.a < 1.0f, true, false); } else { - geometryCache->renderSolidShapeInstance(args, batch, geometryShape, outColor, args->_shapePipeline); + pipeline = GeometryCache::getShapePipeline(false, outColor.a < 1.0f, true, false, false, true); + } + if (render::ShapeKey(args->_globalShapeKey).isWireframe() || primitiveMode == PrimitiveMode::LINES) { + geometryCache->renderWireShapeInstance(args, batch, geometryShape, outColor, pipeline); + } else { + geometryCache->renderSolidShapeInstance(args, batch, geometryShape, outColor, pipeline); } } else { if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) { diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index a3e1a2f56d..81f367a956 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -162,10 +162,12 @@ void TextEntityRenderer::doRender(RenderArgs* args) { glm::vec4 backgroundColor; Transform modelTransform; glm::vec3 dimensions; + BillboardMode billboardMode; bool layered; withReadLock([&] { modelTransform = _renderTransform; dimensions = _dimensions; + billboardMode = _billboardMode; float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; textColor = glm::vec4(_textColor, fadeRatio * _textAlpha); @@ -190,7 +192,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) { } auto transformToTopLeft = modelTransform; - transformToTopLeft.setRotation(EntityItem::getBillboardRotation(transformToTopLeft.getTranslation(), transformToTopLeft.getRotation(), _billboardMode, args->getViewFrustum().getPosition())); + transformToTopLeft.setRotation(EntityItem::getBillboardRotation(transformToTopLeft.getTranslation(), transformToTopLeft.getRotation(), billboardMode, args->getViewFrustum().getPosition())); transformToTopLeft.postTranslate(dimensions * glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed @@ -210,10 +212,6 @@ void TextEntityRenderer::doRender(RenderArgs* args) { glm::vec2 bounds = glm::vec2(dimensions.x - (_leftMargin + _rightMargin), dimensions.y - (_topMargin + _bottomMargin)); _textRenderer->draw(batch, _leftMargin / scale, -_topMargin / scale, _text, textColor, bounds / scale, layered); } - - if (layered) { - DependencyManager::get()->unsetKeyLightBatch(batch); - } } QSizeF TextEntityRenderer::textSize(const QString& text) const { diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index cd3454bf38..e84f2e25a4 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -368,6 +368,12 @@ public: const ShapeData * getShapeData(Shape shape) const; graphics::MeshPointer meshFromShape(Shape geometryShape, glm::vec3 color); + + static render::ShapePipelinePointer getShapePipeline(bool textured = false, bool transparent = false, bool culled = true, + bool unlit = false, bool depthBias = false, bool forward = false); + static render::ShapePipelinePointer getFadingShapePipeline(bool textured = false, bool transparent = false, bool culled = true, + bool unlit = false, bool depthBias = false); + private: GeometryCache(); @@ -471,11 +477,6 @@ private: gpu::PipelinePointer _simpleOpaqueWebBrowserPipeline; gpu::ShaderPointer _simpleTransparentWebBrowserShader; gpu::PipelinePointer _simpleTransparentWebBrowserPipeline; - - static render::ShapePipelinePointer getShapePipeline(bool textured = false, bool transparent = false, bool culled = true, - bool unlit = false, bool depthBias = false, bool forward = false); - static render::ShapePipelinePointer getFadingShapePipeline(bool textured = false, bool transparent = false, bool culled = true, - bool unlit = false, bool depthBias = false); }; #endif // hifi_GeometryCache_h diff --git a/libraries/render-utils/src/RenderCommonTask.cpp b/libraries/render-utils/src/RenderCommonTask.cpp index 385e384efe..b1a62625b2 100644 --- a/libraries/render-utils/src/RenderCommonTask.cpp +++ b/libraries/render-utils/src/RenderCommonTask.cpp @@ -95,7 +95,11 @@ void DrawLayered3D::run(const RenderContextPointer& renderContext, const Inputs& // Setup lighting model for all items; batch.setUniformBuffer(ru::Buffer::LightModel, lightingModel->getParametersBuffer()); - renderShapes(renderContext, _shapePlumber, inItems, _maxDrawn); + if (_opaquePass) { + renderStateSortShapes(renderContext, _shapePlumber, inItems, _maxDrawn); + } else { + renderShapes(renderContext, _shapePlumber, inItems, _maxDrawn); + } args->_batch = nullptr; }); } diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 089d267711..ea2b05a6fa 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -216,8 +216,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("DrawHazeDeferred", drawHazeInputs); // Render transparent objects forward in LightingBuffer - const auto transparentsInputs = DrawDeferred::Inputs(transparents, hazeFrame, lightFrame, lightingModel, lightClusters, shadowFrame, jitter).asVarying(); - task.addJob("DrawTransparentDeferred", transparentsInputs, shapePlumber); + const auto transparentsInputs = RenderTransparentDeferred::Inputs(transparents, hazeFrame, lightFrame, lightingModel, lightClusters, shadowFrame, jitter).asVarying(); + task.addJob("DrawTransparentDeferred", transparentsInputs, shapePlumber); const auto outlineRangeTimer = task.addJob("BeginHighlightRangeTimer", "Highlight"); @@ -436,7 +436,7 @@ void RenderDeferredTaskDebug::build(JobModel& task, const render::Varying& input } -void DrawDeferred::run(const RenderContextPointer& renderContext, const Inputs& inputs) { +void RenderTransparentDeferred::run(const RenderContextPointer& renderContext, const Inputs& inputs) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); @@ -453,7 +453,7 @@ void DrawDeferred::run(const RenderContextPointer& renderContext, const Inputs& RenderArgs* args = renderContext->args; - gpu::doInBatch("DrawDeferred::run", args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("RenderTransparentDeferred::run", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; // Setup camera, projection and viewport for all items diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 0a188ec3a6..3eb1153928 100644 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -19,7 +19,7 @@ #include "LightClusters.h" #include "RenderShadowTask.h" -class DrawDeferredConfig : public render::Job::Config { +class RenderTransparentDeferredConfig : public render::Job::Config { Q_OBJECT Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY newStats) Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty) @@ -41,13 +41,13 @@ protected: int _numDrawn{ 0 }; }; -class DrawDeferred { +class RenderTransparentDeferred { public: using Inputs = render::VaryingSet7; - using Config = DrawDeferredConfig; - using JobModel = render::Job::ModelI; + using Config = RenderTransparentDeferredConfig; + using JobModel = render::Job::ModelI; - DrawDeferred(render::ShapePlumberPointer shapePlumber) + RenderTransparentDeferred(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} void configure(const Config& config) { _maxDrawn = config.maxDrawn; } diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 73692b41c2..0bc117bdb9 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -98,7 +98,7 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend // Draw opaques forward const auto opaqueInputs = DrawForward::Inputs(opaques, lightingModel).asVarying(); - task.addJob("DrawOpaques", opaqueInputs, shapePlumber); + task.addJob("DrawOpaques", opaqueInputs, shapePlumber, true); // Similar to light stage, background stage has been filled by several potential render items and resolved for the frame in this job const auto backgroundInputs = DrawBackgroundStage::Inputs(lightingModel, backgroundFrame).asVarying(); @@ -106,7 +106,7 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend // Draw transparent objects forward const auto transparentInputs = DrawForward::Inputs(transparents, lightingModel).asVarying(); - task.addJob("DrawTransparents", transparentInputs, shapePlumber); + task.addJob("DrawTransparents", transparentInputs, shapePlumber, false); // Layered const auto nullJitter = Varying(glm::vec2(0.0f, 0.0f)); @@ -261,7 +261,11 @@ void DrawForward::run(const RenderContextPointer& renderContext, const Inputs& i args->_globalShapeKey = globalKey._flags.to_ulong(); // Render items - renderStateSortShapes(renderContext, _shapePlumber, inItems, -1, globalKey); + if (_opaquePass) { + renderStateSortShapes(renderContext, _shapePlumber, inItems, -1, globalKey); + } else { + renderShapes(renderContext, _shapePlumber, inItems, -1, globalKey); + } args->_batch = nullptr; args->_globalShapeKey = 0; diff --git a/libraries/render-utils/src/RenderForwardTask.h b/libraries/render-utils/src/RenderForwardTask.h index 85b51ad5fa..40d004ddb2 100755 --- a/libraries/render-utils/src/RenderForwardTask.h +++ b/libraries/render-utils/src/RenderForwardTask.h @@ -76,12 +76,13 @@ public: using Inputs = render::VaryingSet2; using JobModel = render::Job::ModelI; - DrawForward(const render::ShapePlumberPointer& shapePlumber) : _shapePlumber(shapePlumber) {} + DrawForward(const render::ShapePlumberPointer& shapePlumber, bool opaquePass) : _shapePlumber(shapePlumber), _opaquePass(opaquePass) {} void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); private: render::ShapePlumberPointer _shapePlumber; + bool _opaquePass; }; #endif // hifi_RenderForwardTask_h diff --git a/libraries/render-utils/src/simple_transparent_textured.slf b/libraries/render-utils/src/simple_transparent_textured.slf index f1bb2b1ea2..9f8a88c7c2 100644 --- a/libraries/render-utils/src/simple_transparent_textured.slf +++ b/libraries/render-utils/src/simple_transparent_textured.slf @@ -16,8 +16,8 @@ <@include gpu/Color.slh@> <@include render-utils/ShaderConstants.h@> -<@include ForwardGlobalLight.slh@> -<$declareEvalGlobalLightingAlphaBlended()$> +<@include DeferredGlobalLight.slh@> +<$declareEvalGlobalLightingAlphaBlendedWithHaze()$> <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> From 57a02bc1d57584de4de914ffc8c960bbe653658a Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 25 Mar 2019 11:29:25 -0700 Subject: [PATCH 07/21] capture lambda by value --- .../entities-renderer/src/RenderableMaterialEntityItem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index 15842336f5..b474fb2266 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -169,7 +169,7 @@ void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo if (urlChanged && !usingMaterialData) { _networkMaterial = MaterialCache::instance().getMaterial(_materialURL); - auto onMaterialRequestFinished = [&, oldParentID, oldParentMaterialName, newCurrentMaterialName](bool success) { + auto onMaterialRequestFinished = [this, oldParentID, oldParentMaterialName, newCurrentMaterialName](bool success) { if (success) { deleteMaterial(oldParentID, oldParentMaterialName); _texturesLoaded = false; @@ -186,7 +186,7 @@ void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo if (_networkMaterial->isLoaded()) { onMaterialRequestFinished(!_networkMaterial->isFailed()); } else { - connect(_networkMaterial.data(), &Resource::finished, this, [&](bool success) { + connect(_networkMaterial.data(), &Resource::finished, this, [this, onMaterialRequestFinished](bool success) { withWriteLock([&] { onMaterialRequestFinished(success); }); From 0173c87695f60b5430d205e6b703348f6d356091 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 22 Mar 2019 02:43:06 +0100 Subject: [PATCH 08/21] Fix clashing hyperlinks in AvatarPackager project page --- interface/resources/qml/hifi/avatarPackager/AvatarProject.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml b/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml index bf8c06d1b3..e5bffa7829 100644 --- a/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml +++ b/interface/resources/qml/hifi/avatarPackager/AvatarProject.qml @@ -339,8 +339,8 @@ Item { visible: AvatarPackagerCore.currentAvatarProject && AvatarPackagerCore.currentAvatarProject.hasErrors anchors { - top: notForSaleMessage.bottom - topMargin: 16 + top: notForSaleMessage.visible ? notForSaleMessage.bottom : infoMessage .bottom + bottom: showFilesText.top horizontalCenter: parent.horizontalCenter } From 1057166418e4c0526c2caa6e7a02c6b06b4fd63c Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Fri, 15 Mar 2019 10:44:59 -0700 Subject: [PATCH 09/21] Add master injector gain to audio-mixer --- .../src/audio/AudioMixerClientData.h | 3 + .../src/audio/AudioMixerSlave.cpp | 56 ++++++++++++------- assignment-client/src/audio/AudioMixerSlave.h | 7 ++- 3 files changed, 43 insertions(+), 23 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index 653749f619..f9d113c53d 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -84,6 +84,8 @@ public: float getMasterAvatarGain() const { return _masterAvatarGain; } void setMasterAvatarGain(float gain) { _masterAvatarGain = gain; } + float getMasterInjectorGain() const { return _masterInjectorGain; } + void setMasterInjectorGain(float gain) { _masterInjectorGain = gain; } AudioLimiter audioLimiter; @@ -189,6 +191,7 @@ private: int _frameToSendStats { 0 }; float _masterAvatarGain { 1.0f }; // per-listener mixing gain, applied only to avatars + float _masterInjectorGain { 1.0f }; // per-listener mixing gain, applied only to injectors CodecPluginPointer _codec; QString _selectedCodecName; diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp index a920b45161..f7f8e8a9c1 100644 --- a/assignment-client/src/audio/AudioMixerSlave.cpp +++ b/assignment-client/src/audio/AudioMixerSlave.cpp @@ -50,7 +50,7 @@ void sendEnvironmentPacket(const SharedNodePointer& node, AudioMixerClientData& // mix helpers inline float approximateGain(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd); -inline float computeGain(float masterListenerGain, const AvatarAudioStream& listeningNodeStream, +inline float computeGain(float masterAvatarGain, float masterInjectorGain, const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd, const glm::vec3& relativePosition, float distance, bool isEcho); inline float computeAzimuth(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd, const glm::vec3& relativePosition); @@ -338,8 +338,8 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) { } if (!isThrottling) { - updateHRTFParameters(stream, *listenerAudioStream, - listenerData->getMasterAvatarGain()); + updateHRTFParameters(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(), + listenerData->getMasterInjectorGain()); } return false; }); @@ -363,8 +363,8 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) { } if (!isThrottling) { - updateHRTFParameters(stream, *listenerAudioStream, - listenerData->getMasterAvatarGain()); + updateHRTFParameters(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(), + listenerData->getMasterInjectorGain()); } return false; }); @@ -381,13 +381,13 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) { stream.approximateVolume = approximateVolume(stream, listenerAudioStream); } else { if (shouldBeSkipped(stream, *listener, *listenerAudioStream, *listenerData)) { - addStream(stream, *listenerAudioStream, 0.0f, isSoloing); + addStream(stream, *listenerAudioStream, 0.0f, 0.0f, isSoloing); streams.skipped.push_back(move(stream)); ++stats.activeToSkipped; return true; } - addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(), + addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(), listenerData->getMasterInjectorGain(), isSoloing); if (shouldBeInactive(stream)) { @@ -423,7 +423,7 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) { return true; } - addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(), + addStream(stream, *listenerAudioStream, listenerData->getMasterAvatarGain(), listenerData->getMasterInjectorGain(), isSoloing); if (shouldBeInactive(stream)) { @@ -491,7 +491,9 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) { void AudioMixerSlave::addStream(AudioMixerClientData::MixableStream& mixableStream, AvatarAudioStream& listeningNodeStream, - float masterListenerGain, bool isSoloing) { + float masterAvatarGain, + float masterInjectorGain, + bool isSoloing) { ++stats.totalMixes; auto streamToAdd = mixableStream.positionalStream; @@ -504,9 +506,10 @@ void AudioMixerSlave::addStream(AudioMixerClientData::MixableStream& mixableStre float distance = glm::max(glm::length(relativePosition), EPSILON); float azimuth = isEcho ? 0.0f : computeAzimuth(listeningNodeStream, listeningNodeStream, relativePosition); - float gain = masterListenerGain; + float gain = masterAvatarGain; if (!isSoloing) { - gain = computeGain(masterListenerGain, listeningNodeStream, *streamToAdd, relativePosition, distance, isEcho); + gain = computeGain(masterAvatarGain, masterInjectorGain, listeningNodeStream, *streamToAdd, relativePosition, + distance, isEcho); } const int HRTF_DATASET_INDEX = 1; @@ -585,8 +588,9 @@ void AudioMixerSlave::addStream(AudioMixerClientData::MixableStream& mixableStre } void AudioMixerSlave::updateHRTFParameters(AudioMixerClientData::MixableStream& mixableStream, - AvatarAudioStream& listeningNodeStream, - float masterListenerGain) { + AvatarAudioStream& listeningNodeStream, + float masterAvatarGain, + float masterInjectorGain) { auto streamToAdd = mixableStream.positionalStream; // check if this is a server echo of a source back to itself @@ -595,7 +599,8 @@ void AudioMixerSlave::updateHRTFParameters(AudioMixerClientData::MixableStream& glm::vec3 relativePosition = streamToAdd->getPosition() - listeningNodeStream.getPosition(); float distance = glm::max(glm::length(relativePosition), EPSILON); - float gain = computeGain(masterListenerGain, listeningNodeStream, *streamToAdd, relativePosition, distance, isEcho); + float gain = computeGain(masterAvatarGain, masterInjectorGain, listeningNodeStream, *streamToAdd, relativePosition, + distance, isEcho); float azimuth = isEcho ? 0.0f : computeAzimuth(listeningNodeStream, listeningNodeStream, relativePosition); mixableStream.hrtf->setParameterHistory(azimuth, distance, gain); @@ -720,6 +725,7 @@ float approximateGain(const AvatarAudioStream& listeningNodeStream, const Positi // injector: apply attenuation if (streamToAdd.getType() == PositionalAudioStream::Injector) { gain *= reinterpret_cast(&streamToAdd)->getAttenuationRatio(); + // injector: skip master gain } // avatar: skip attenuation - it is too costly to approximate @@ -729,16 +735,23 @@ float approximateGain(const AvatarAudioStream& listeningNodeStream, const Positi float distance = glm::length(relativePosition); return gain / distance; - // avatar: skip master gain - it is constant for all streams + // avatar: skip master gain } -float computeGain(float masterListenerGain, const AvatarAudioStream& listeningNodeStream, - const PositionalAudioStream& streamToAdd, const glm::vec3& relativePosition, float distance, bool isEcho) { +float computeGain(float masterAvatarGain, + float masterInjectorGain, + const AvatarAudioStream& listeningNodeStream, + const PositionalAudioStream& streamToAdd, + const glm::vec3& relativePosition, + float distance, + bool isEcho) { float gain = 1.0f; // injector: apply attenuation if (streamToAdd.getType() == PositionalAudioStream::Injector) { gain *= reinterpret_cast(&streamToAdd)->getAttenuationRatio(); + // apply master gain + gain *= masterInjectorGain; // avatar: apply fixed off-axis attenuation to make them quieter as they turn away } else if (!isEcho && (streamToAdd.getType() == PositionalAudioStream::Microphone)) { @@ -754,8 +767,8 @@ float computeGain(float masterListenerGain, const AvatarAudioStream& listeningNo gain *= offAxisCoefficient; - // apply master gain, only to avatars - gain *= masterListenerGain; + // apply master gain + gain *= masterAvatarGain; } auto& audioZones = AudioMixer::getAudioZones(); @@ -797,8 +810,9 @@ float computeGain(float masterListenerGain, const AvatarAudioStream& listeningNo return gain; } -float computeAzimuth(const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd, - const glm::vec3& relativePosition) { +float computeAzimuth(const AvatarAudioStream& listeningNodeStream, + const PositionalAudioStream& streamToAdd, + const glm::vec3& relativePosition) { glm::quat inverseOrientation = glm::inverse(listeningNodeStream.getOrientation()); glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition; diff --git a/assignment-client/src/audio/AudioMixerSlave.h b/assignment-client/src/audio/AudioMixerSlave.h index 3d979da1fc..9765ea8639 100644 --- a/assignment-client/src/audio/AudioMixerSlave.h +++ b/assignment-client/src/audio/AudioMixerSlave.h @@ -57,10 +57,13 @@ private: bool prepareMix(const SharedNodePointer& listener); void addStream(AudioMixerClientData::MixableStream& mixableStream, AvatarAudioStream& listeningNodeStream, - float masterListenerGain, bool isSoloing); + float masterAvatarGain, + float masterInjectorGain, + bool isSoloing); void updateHRTFParameters(AudioMixerClientData::MixableStream& mixableStream, AvatarAudioStream& listeningNodeStream, - float masterListenerGain); + float masterAvatarGain, + float masterInjectorGain); void resetHRTFState(AudioMixerClientData::MixableStream& mixableStream); void addStreams(Node& listener, AudioMixerClientData& listenerData); From b15651f1ebcf161fb50253bc9333e906cd27454b Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Fri, 15 Mar 2019 12:05:51 -0700 Subject: [PATCH 10/21] Handle InjectorGainSet packet at the audio-mixer --- assignment-client/src/audio/AudioMixer.cpp | 1 + .../src/audio/AudioMixerClientData.cpp | 14 ++++++++++++++ assignment-client/src/audio/AudioMixerClientData.h | 1 + libraries/networking/src/udt/PacketHeaders.h | 2 +- 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index f67c54239e..201e24d4b9 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -97,6 +97,7 @@ AudioMixer::AudioMixer(ReceivedMessage& message) : PacketType::RadiusIgnoreRequest, PacketType::RequestsDomainListData, PacketType::PerAvatarGainSet, + PacketType::InjectorGainSet, PacketType::AudioSoloRequest }, this, "queueAudioPacket"); diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 90698bfac8..b8d3ec62a6 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -92,6 +92,9 @@ int AudioMixerClientData::processPackets(ConcurrentAddedStreams& addedStreams) { case PacketType::PerAvatarGainSet: parsePerAvatarGainSet(*packet, node); break; + case PacketType::InjectorGainSet: + parseInjectorGainSet(*packet, node); + break; case PacketType::NodeIgnoreRequest: parseNodeIgnoreRequest(packet, node); break; @@ -205,6 +208,17 @@ void AudioMixerClientData::parsePerAvatarGainSet(ReceivedMessage& message, const } } +void AudioMixerClientData::parseInjectorGainSet(ReceivedMessage& message, const SharedNodePointer& node) { + QUuid uuid = node->getUUID(); + + uint8_t packedGain; + message.readPrimitive(&packedGain); + float gain = unpackFloatGainFromByte(packedGain); + + setMasterInjectorGain(gain); + qCDebug(audio) << "Setting MASTER injector gain for " << uuid << " to " << gain; +} + void AudioMixerClientData::setGainForAvatar(QUuid nodeID, float gain) { auto it = std::find_if(_streams.active.cbegin(), _streams.active.cend(), [nodeID](const MixableStream& mixableStream){ return mixableStream.nodeStreamID.nodeID == nodeID && mixableStream.nodeStreamID.streamID.isNull(); diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index f9d113c53d..4a1ca7f9b5 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -63,6 +63,7 @@ public: void negotiateAudioFormat(ReceivedMessage& message, const SharedNodePointer& node); void parseRequestsDomainListData(ReceivedMessage& message); void parsePerAvatarGainSet(ReceivedMessage& message, const SharedNodePointer& node); + void parseInjectorGainSet(ReceivedMessage& message, const SharedNodePointer& node); void parseNodeIgnoreRequest(QSharedPointer message, const SharedNodePointer& node); void parseRadiusIgnoreRequest(QSharedPointer message, const SharedNodePointer& node); void parseSoloRequest(QSharedPointer message, const SharedNodePointer& node); diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 0ec7c40ca4..413ff14b17 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -57,7 +57,7 @@ public: ICEServerQuery, OctreeStats, SetAvatarTraits, - UNUSED_PACKET_TYPE, + InjectorGainSet, AssignmentClientStatus, NoisyMute, AvatarIdentity, From 755762e8ecf7a7a1d70f21ba6a6c15557f5f2914 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Fri, 15 Mar 2019 17:24:50 -0700 Subject: [PATCH 11/21] Send InjectorGainSet packet to the audio-mixer --- libraries/networking/src/NodeList.cpp | 25 +++++++++++++++++++++++++ libraries/networking/src/NodeList.h | 4 ++++ 2 files changed, 29 insertions(+) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index e6eb6087b0..eec710322e 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -265,6 +265,8 @@ void NodeList::reset(bool skipDomainHandlerReset) { _avatarGainMap.clear(); _avatarGainMapLock.unlock(); + _injectorGain = 0.0f; + if (!skipDomainHandlerReset) { // clear the domain connection information, unless they're the ones that asked us to reset _domainHandler.softReset(); @@ -1087,6 +1089,29 @@ float NodeList::getAvatarGain(const QUuid& nodeID) { return 0.0f; } +void NodeList::setInjectorGain(float gain) { + auto audioMixer = soloNodeOfType(NodeType::AudioMixer); + if (audioMixer) { + // setup the packet + auto setInjectorGainPacket = NLPacket::create(PacketType::InjectorGainSet, sizeof(float), true); + + // We need to convert the gain in dB (from the script) to an amplitude before packing it. + setInjectorGainPacket->writePrimitive(packFloatGainToByte(fastExp2f(gain / 6.02059991f))); + + qCDebug(networking) << "Sending Set Injector Gain packet with Gain:" << gain; + + sendPacket(std::move(setInjectorGainPacket), *audioMixer); + _injectorGain = gain; + + } else { + qWarning() << "Couldn't find audio mixer to send set gain request"; + } +} + +float NodeList::getInjectorGain() { + return _injectorGain; +} + void NodeList::kickNodeBySessionID(const QUuid& nodeID) { // send a request to domain-server to kick the node with the given session ID // the domain-server will handle the persistence of the kick (via username or IP) diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index e135bc937d..d2a1212d64 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -83,6 +83,8 @@ public: bool isPersonalMutingNode(const QUuid& nodeID) const; void setAvatarGain(const QUuid& nodeID, float gain); float getAvatarGain(const QUuid& nodeID); + void setInjectorGain(float gain); + float getInjectorGain(); void kickNodeBySessionID(const QUuid& nodeID); void muteNodeBySessionID(const QUuid& nodeID); @@ -181,6 +183,8 @@ private: mutable QReadWriteLock _avatarGainMapLock; tbb::concurrent_unordered_map _avatarGainMap; + std::atomic _injectorGain { 0.0f }; + void sendIgnoreRadiusStateToNode(const SharedNodePointer& destinationNode); #if defined(Q_OS_ANDROID) Setting::Handle _ignoreRadiusEnabled { "IgnoreRadiusEnabled", false }; From 4a6e495f5fa96eaa4772aaeb615035a050ec3a0f Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Wed, 20 Mar 2019 15:48:05 -0700 Subject: [PATCH 12/21] Add Users.setInjectorGain() and Users.getInjectorGain() to the scripting interface --- .../script-engine/src/UsersScriptingInterface.cpp | 9 +++++++++ .../script-engine/src/UsersScriptingInterface.h | 15 +++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index 9beb52f20a..7b30e087e5 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -51,6 +51,15 @@ float UsersScriptingInterface::getAvatarGain(const QUuid& nodeID) { return DependencyManager::get()->getAvatarGain(nodeID); } +void UsersScriptingInterface::setInjectorGain(float gain) { + // ask the NodeList to set the audio injector gain + DependencyManager::get()->setInjectorGain(gain); +} + +float UsersScriptingInterface::getInjectorGain() { + return DependencyManager::get()->getInjectorGain(); +} + void UsersScriptingInterface::kick(const QUuid& nodeID) { if (_kickConfirmationOperator) { diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index f8ca974b8b..d6750b263d 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -97,6 +97,21 @@ public slots: */ float getAvatarGain(const QUuid& nodeID); + /**jsdoc + * Sets the audio injector gain at the server. + * Units are Decibels (dB) + * @function Users.setInjectorGain + * @param {number} gain (in dB) + */ + void setInjectorGain(float gain); + + /**jsdoc + * Gets the audio injector gain at the server. + * @function Users.getInjectorGain + * @returns {number} gain (in dB) + */ + float getInjectorGain(); + /**jsdoc * Kick/ban another user. Removes them from the server and prevents them from returning. Bans by either user name (if * available) or machine fingerprint otherwise. This will only do anything if you're an admin of the domain you're in. From a2d261d20ca1273b99ab90952945569baa43a153 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 21 Mar 2019 11:51:49 -0700 Subject: [PATCH 13/21] Move the new audio volume API from Users scripting interface to Audio scripting interface --- interface/src/scripting/Audio.cpp | 36 ++++++++++++++++--- interface/src/scripting/Audio.h | 30 ++++++++++++++++ .../src/UsersScriptingInterface.cpp | 9 ----- .../src/UsersScriptingInterface.h | 15 -------- 4 files changed, 61 insertions(+), 29 deletions(-) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index b1b5077e60..f9560c84f7 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -377,6 +377,18 @@ void Audio::handlePushedToTalk(bool enabled) { } } +void Audio::setInputDevice(const QAudioDeviceInfo& device, bool isHMD) { + withWriteLock([&] { + _devices.chooseInputDevice(device, isHMD); + }); +} + +void Audio::setOutputDevice(const QAudioDeviceInfo& device, bool isHMD) { + withWriteLock([&] { + _devices.chooseOutputDevice(device, isHMD); + }); +} + void Audio::setReverb(bool enable) { withWriteLock([&] { DependencyManager::get()->setReverb(enable); @@ -389,14 +401,28 @@ void Audio::setReverbOptions(const AudioEffectOptions* options) { }); } -void Audio::setInputDevice(const QAudioDeviceInfo& device, bool isHMD) { +void Audio::setAvatarGain(float gain) { withWriteLock([&] { - _devices.chooseInputDevice(device, isHMD); + // ask the NodeList to set the master avatar gain + DependencyManager::get()->setAvatarGain("", gain); }); } -void Audio::setOutputDevice(const QAudioDeviceInfo& device, bool isHMD) { - withWriteLock([&] { - _devices.chooseOutputDevice(device, isHMD); +float Audio::getAvatarGain() { + return resultWithReadLock([&] { + return DependencyManager::get()->getAvatarGain(""); + }); +} + +void Audio::setInjectorGain(float gain) { + withWriteLock([&] { + // ask the NodeList to set the audio injector gain + DependencyManager::get()->setInjectorGain(gain); + }); +} + +float Audio::getInjectorGain() { + return resultWithReadLock([&] { + return DependencyManager::get()->getInjectorGain(); }); } diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index 9ee230fc29..14a75d5ffe 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -170,6 +170,36 @@ public: */ Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options); + /**jsdoc + * Sets the master avatar gain at the server. + * Units are Decibels (dB) + * @function Audio.setAvatarGain + * @param {number} gain (in dB) + */ + Q_INVOKABLE void setAvatarGain(float gain); + + /**jsdoc + * Gets the master avatar gain at the server. + * @function Audio.getAvatarGain + * @returns {number} gain (in dB) + */ + Q_INVOKABLE float getAvatarGain(); + + /**jsdoc + * Sets the audio injector gain at the server. + * Units are Decibels (dB) + * @function Audio.setInjectorGain + * @param {number} gain (in dB) + */ + Q_INVOKABLE void setInjectorGain(float gain); + + /**jsdoc + * Gets the audio injector gain at the server. + * @function Audio.getInjectorGain + * @returns {number} gain (in dB) + */ + Q_INVOKABLE float getInjectorGain(); + /**jsdoc * Starts making an audio recording of the audio being played in-world (i.e., not local-only audio) to a file in WAV format. * @function Audio.startRecording diff --git a/libraries/script-engine/src/UsersScriptingInterface.cpp b/libraries/script-engine/src/UsersScriptingInterface.cpp index 7b30e087e5..9beb52f20a 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.cpp +++ b/libraries/script-engine/src/UsersScriptingInterface.cpp @@ -51,15 +51,6 @@ float UsersScriptingInterface::getAvatarGain(const QUuid& nodeID) { return DependencyManager::get()->getAvatarGain(nodeID); } -void UsersScriptingInterface::setInjectorGain(float gain) { - // ask the NodeList to set the audio injector gain - DependencyManager::get()->setInjectorGain(gain); -} - -float UsersScriptingInterface::getInjectorGain() { - return DependencyManager::get()->getInjectorGain(); -} - void UsersScriptingInterface::kick(const QUuid& nodeID) { if (_kickConfirmationOperator) { diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h index d6750b263d..f8ca974b8b 100644 --- a/libraries/script-engine/src/UsersScriptingInterface.h +++ b/libraries/script-engine/src/UsersScriptingInterface.h @@ -97,21 +97,6 @@ public slots: */ float getAvatarGain(const QUuid& nodeID); - /**jsdoc - * Sets the audio injector gain at the server. - * Units are Decibels (dB) - * @function Users.setInjectorGain - * @param {number} gain (in dB) - */ - void setInjectorGain(float gain); - - /**jsdoc - * Gets the audio injector gain at the server. - * @function Users.getInjectorGain - * @returns {number} gain (in dB) - */ - float getInjectorGain(); - /**jsdoc * Kick/ban another user. Removes them from the server and prevents them from returning. Bans by either user name (if * available) or machine fingerprint otherwise. This will only do anything if you're an admin of the domain you're in. From 95b4f954a6a1ed2912a6c47ac2c5c6d86a29d8ea Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Fri, 22 Mar 2019 10:12:31 -0700 Subject: [PATCH 14/21] Add AudioClient mixing gains for local injectors and system sounds --- libraries/audio-client/src/AudioClient.cpp | 4 +++- libraries/audio-client/src/AudioClient.h | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 9d645a1dbf..9fa9a0bc18 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1368,7 +1368,9 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) { memset(_localScratchBuffer, 0, bytesToRead); if (0 < injectorBuffer->readData((char*)_localScratchBuffer, bytesToRead)) { - float gain = options.volume; + bool isSystemSound = !injector->isPositionSet() && !injector->isAmbisonic(); + + float gain = injector->getVolume() * (isSystemSound ? _systemInjectorGain : _localInjectorGain); if (options.ambisonic) { diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index a153f22bf3..7608bf5cdb 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -241,6 +241,8 @@ public slots: void setInputVolume(float volume, bool emitSignal = true); void setReverb(bool reverb); void setReverbOptions(const AudioEffectOptions* options); + void setLocalInjectorGain(float gain) { _localInjectorGain = gain; }; + void setSystemInjectorGain(float gain) { _systemInjectorGain = gain; }; void outputNotify(); @@ -395,6 +397,8 @@ private: int16_t* _outputScratchBuffer { NULL }; // for local audio (used by audio injectors thread) + std::atomic _localInjectorGain { 1.0f }; + std::atomic _systemInjectorGain { 1.0f }; float _localMixBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO]; int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC]; float* _localOutputMixBuffer { NULL }; From 37429a07b8a7a2262582f4b77779d46db3debdc3 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Fri, 22 Mar 2019 10:21:54 -0700 Subject: [PATCH 15/21] Add local injector gains to the Audio scripting interface --- interface/src/scripting/Audio.cpp | 34 ++++++++++++++++++++++++++ interface/src/scripting/Audio.h | 40 +++++++++++++++++++++++++++---- 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index f9560c84f7..b3c7b25745 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -426,3 +426,37 @@ float Audio::getInjectorGain() { return DependencyManager::get()->getInjectorGain(); }); } + +void Audio::setLocalInjectorGain(float gain) { + withWriteLock([&] { + if (_localInjectorGain != gain) { + _localInjectorGain = gain; + // convert dB to amplitude + gain = fastExp2f(gain / 6.02059991f); + DependencyManager::get()->setLocalInjectorGain(gain); + } + }); +} + +float Audio::getLocalInjectorGain() { + return resultWithReadLock([&] { + return _localInjectorGain; + }); +} + +void Audio::setSystemInjectorGain(float gain) { + withWriteLock([&] { + if (_systemInjectorGain != gain) { + _systemInjectorGain = gain; + // convert dB to amplitude + gain = fastExp2f(gain / 6.02059991f); + DependencyManager::get()->setSystemInjectorGain(gain); + } + }); +} + +float Audio::getSystemInjectorGain() { + return resultWithReadLock([&] { + return _systemInjectorGain; + }); +} diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index 14a75d5ffe..d6823ea452 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -171,7 +171,7 @@ public: Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options); /**jsdoc - * Sets the master avatar gain at the server. + * Sets the avatar gain at the server. * Units are Decibels (dB) * @function Audio.setAvatarGain * @param {number} gain (in dB) @@ -179,14 +179,14 @@ public: Q_INVOKABLE void setAvatarGain(float gain); /**jsdoc - * Gets the master avatar gain at the server. + * Gets the avatar gain at the server. * @function Audio.getAvatarGain * @returns {number} gain (in dB) */ Q_INVOKABLE float getAvatarGain(); /**jsdoc - * Sets the audio injector gain at the server. + * Sets the injector gain at the server. * Units are Decibels (dB) * @function Audio.setInjectorGain * @param {number} gain (in dB) @@ -194,12 +194,42 @@ public: Q_INVOKABLE void setInjectorGain(float gain); /**jsdoc - * Gets the audio injector gain at the server. + * Gets the injector gain at the server. * @function Audio.getInjectorGain * @returns {number} gain (in dB) */ Q_INVOKABLE float getInjectorGain(); + /**jsdoc + * Sets the local injector gain in the client. + * Units are Decibels (dB) + * @function Audio.setLocalInjectorGain + * @param {number} gain (in dB) + */ + Q_INVOKABLE void setLocalInjectorGain(float gain); + + /**jsdoc + * Gets the local injector gain in the client. + * @function Audio.getLocalInjectorGain + * @returns {number} gain (in dB) + */ + Q_INVOKABLE float getLocalInjectorGain(); + + /**jsdoc + * Sets the injector gain for system sounds. + * Units are Decibels (dB) + * @function Audio.setSystemInjectorGain + * @param {number} gain (in dB) + */ + Q_INVOKABLE void setSystemInjectorGain(float gain); + + /**jsdoc + * Gets the injector gain for system sounds. + * @function Audio.getSystemInjectorGain + * @returns {number} gain (in dB) + */ + Q_INVOKABLE float getSystemInjectorGain(); + /**jsdoc * Starts making an audio recording of the audio being played in-world (i.e., not local-only audio) to a file in WAV format. * @function Audio.startRecording @@ -380,6 +410,8 @@ private: float _inputVolume { 1.0f }; float _inputLevel { 0.0f }; + float _localInjectorGain { 0.0f }; // in dB + float _systemInjectorGain { 0.0f }; // in dB bool _isClipping { false }; bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled. bool _enableWarnWhenMuted { true }; From 155bd39da6339410682a3c2bfd8b1c2bcb16947f Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Fri, 22 Mar 2019 10:24:30 -0700 Subject: [PATCH 16/21] Quantize and limit the local injector gains to match the network protocol --- interface/src/scripting/Audio.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index b3c7b25745..330ed7abfe 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -433,6 +433,8 @@ void Audio::setLocalInjectorGain(float gain) { _localInjectorGain = gain; // convert dB to amplitude gain = fastExp2f(gain / 6.02059991f); + // quantize and limit to match NodeList::setInjectorGain() + gain = unpackFloatGainFromByte(packFloatGainToByte(gain)); DependencyManager::get()->setLocalInjectorGain(gain); } }); @@ -450,6 +452,8 @@ void Audio::setSystemInjectorGain(float gain) { _systemInjectorGain = gain; // convert dB to amplitude gain = fastExp2f(gain / 6.02059991f); + // quantize and limit to match NodeList::setInjectorGain() + gain = unpackFloatGainFromByte(packFloatGainToByte(gain)); DependencyManager::get()->setSystemInjectorGain(gain); } }); From c15813b44225235a96fd507d031c6e6650d0eedb Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Fri, 22 Mar 2019 17:58:17 -0700 Subject: [PATCH 17/21] Cleanup --- assignment-client/src/audio/AudioMixerClientData.cpp | 6 +++--- interface/src/scripting/Audio.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index b8d3ec62a6..41b72c04d2 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -200,11 +200,11 @@ void AudioMixerClientData::parsePerAvatarGainSet(ReceivedMessage& message, const if (avatarUUID.isNull()) { // set the MASTER avatar gain setMasterAvatarGain(gain); - qCDebug(audio) << "Setting MASTER avatar gain for " << uuid << " to " << gain; + qCDebug(audio) << "Setting MASTER avatar gain for" << uuid << "to" << gain; } else { // set the per-source avatar gain setGainForAvatar(avatarUUID, gain); - qCDebug(audio) << "Setting avatar gain adjustment for hrtf[" << uuid << "][" << avatarUUID << "] to " << gain; + qCDebug(audio) << "Setting avatar gain adjustment for hrtf[" << uuid << "][" << avatarUUID << "] to" << gain; } } @@ -216,7 +216,7 @@ void AudioMixerClientData::parseInjectorGainSet(ReceivedMessage& message, const float gain = unpackFloatGainFromByte(packedGain); setMasterInjectorGain(gain); - qCDebug(audio) << "Setting MASTER injector gain for " << uuid << " to " << gain; + qCDebug(audio) << "Setting MASTER injector gain for" << uuid << "to" << gain; } void AudioMixerClientData::setGainForAvatar(QUuid nodeID, float gain) { diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 330ed7abfe..4f2171d451 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -404,13 +404,13 @@ void Audio::setReverbOptions(const AudioEffectOptions* options) { void Audio::setAvatarGain(float gain) { withWriteLock([&] { // ask the NodeList to set the master avatar gain - DependencyManager::get()->setAvatarGain("", gain); + DependencyManager::get()->setAvatarGain(QUuid(), gain); }); } float Audio::getAvatarGain() { return resultWithReadLock([&] { - return DependencyManager::get()->getAvatarGain(""); + return DependencyManager::get()->getAvatarGain(QUuid()); }); } From 3d7c3e7b6f6fe3d737cc86e038966617397258f5 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Sat, 23 Mar 2019 06:48:37 -0700 Subject: [PATCH 18/21] Persist the audio-mixer settings across domain changes and server resets --- libraries/networking/src/NodeList.cpp | 38 ++++++++++++++++++--------- libraries/networking/src/NodeList.h | 3 ++- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index eec710322e..0021a594bc 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -265,8 +265,6 @@ void NodeList::reset(bool skipDomainHandlerReset) { _avatarGainMap.clear(); _avatarGainMapLock.unlock(); - _injectorGain = 0.0f; - if (!skipDomainHandlerReset) { // clear the domain connection information, unless they're the ones that asked us to reset _domainHandler.softReset(); @@ -1018,6 +1016,14 @@ void NodeList::maybeSendIgnoreSetToNode(SharedNodePointer newNode) { // also send them the current ignore radius state. sendIgnoreRadiusStateToNode(newNode); + + // also send the current avatar and injector gains + if (_avatarGain != 0.0f) { + setAvatarGain(QUuid(), _avatarGain); + } + if (_injectorGain != 0.0f) { + setInjectorGain(_injectorGain); + } } if (newNode->getType() == NodeType::AvatarMixer) { // this is a mixer that we just added - it's unlikely it knows who we were previously ignoring in this session, @@ -1064,13 +1070,17 @@ void NodeList::setAvatarGain(const QUuid& nodeID, float gain) { if (nodeID.isNull()) { qCDebug(networking) << "Sending Set MASTER Avatar Gain packet with Gain:" << gain; - } else { - qCDebug(networking) << "Sending Set Avatar Gain packet with UUID: " << uuidStringWithoutCurlyBraces(nodeID) << "Gain:" << gain; - } - sendPacket(std::move(setAvatarGainPacket), *audioMixer); - QWriteLocker lock{ &_avatarGainMapLock }; - _avatarGainMap[nodeID] = gain; + sendPacket(std::move(setAvatarGainPacket), *audioMixer); + _avatarGain = gain; + + } else { + qCDebug(networking) << "Sending Set Avatar Gain packet with UUID:" << uuidStringWithoutCurlyBraces(nodeID) << "Gain:" << gain; + + sendPacket(std::move(setAvatarGainPacket), *audioMixer); + QWriteLocker lock{ &_avatarGainMapLock }; + _avatarGainMap[nodeID] = gain; + } } else { qWarning() << "Couldn't find audio mixer to send set gain request"; @@ -1081,10 +1091,14 @@ void NodeList::setAvatarGain(const QUuid& nodeID, float gain) { } float NodeList::getAvatarGain(const QUuid& nodeID) { - QReadLocker lock{ &_avatarGainMapLock }; - auto it = _avatarGainMap.find(nodeID); - if (it != _avatarGainMap.cend()) { - return it->second; + if (nodeID.isNull()) { + return _avatarGain; + } else { + QReadLocker lock{ &_avatarGainMapLock }; + auto it = _avatarGainMap.find(nodeID); + if (it != _avatarGainMap.cend()) { + return it->second; + } } return 0.0f; } diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index d2a1212d64..f871560fba 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -183,7 +183,8 @@ private: mutable QReadWriteLock _avatarGainMapLock; tbb::concurrent_unordered_map _avatarGainMap; - std::atomic _injectorGain { 0.0f }; + std::atomic _avatarGain { 0.0f }; // in dB + std::atomic _injectorGain { 0.0f }; // in dB void sendIgnoreRadiusStateToNode(const SharedNodePointer& destinationNode); #if defined(Q_OS_ANDROID) From 7b56bef83815b230cda13711165f60e15c7bfd58 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Sat, 23 Mar 2019 16:00:02 -0700 Subject: [PATCH 19/21] Prototype an updated Audio tab with 3 independent volume controls --- interface/resources/qml/hifi/audio/Audio.qml | 158 ++++++++++++++++-- .../qml/hifi/audio/LoopbackAudio.qml | 16 +- .../qml/hifi/audio/PlaySampleSound.qml | 16 +- 3 files changed, 158 insertions(+), 32 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index cd0f290da4..8fdd0368e2 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -87,8 +87,19 @@ Rectangle { } function updateMyAvatarGainFromQML(sliderValue, isReleased) { - if (Users.getAvatarGain(myAvatarUuid) != sliderValue) { - Users.setAvatarGain(myAvatarUuid, sliderValue); + if (AudioScriptingInterface.getAvatarGain() != sliderValue) { + AudioScriptingInterface.setAvatarGain(sliderValue); + } + } + function updateInjectorGainFromQML(sliderValue, isReleased) { + if (AudioScriptingInterface.getInjectorGain() != sliderValue) { + AudioScriptingInterface.setInjectorGain(sliderValue); // server side + AudioScriptingInterface.setLocalInjectorGain(sliderValue); // client side + } + } + function updateSystemInjectorGainFromQML(sliderValue, isReleased) { + if (AudioScriptingInterface.getSystemInjectorGain() != sliderValue) { + AudioScriptingInterface.setSystemInjectorGain(sliderValue); } } @@ -334,6 +345,7 @@ Rectangle { color: hifi.colors.white; text: qsTr("Choose input device"); } + } ListView { @@ -462,22 +474,22 @@ Rectangle { } Item { - id: gainContainer + id: avatarGainContainer x: margins.paddings; anchors.top: outputView.bottom; anchors.topMargin: 10; width: parent.width - margins.paddings*2 - height: gainSliderTextMetrics.height + height: avatarGainSliderTextMetrics.height HifiControlsUit.Slider { - id: gainSlider + id: avatarGainSlider anchors.right: parent.right height: parent.height width: 200 minimumValue: -60.0 maximumValue: 20.0 stepSize: 5 - value: Users.getAvatarGain(myAvatarUuid) + value: AudioScriptingInterface.getAvatarGain() onValueChanged: { updateMyAvatarGainFromQML(value, false); } @@ -493,7 +505,7 @@ Rectangle { // Do nothing. } onDoubleClicked: { - gainSlider.value = 0.0 + avatarGainSlider.value = 0.0 } onPressed: { // Pass through to Slider @@ -507,13 +519,13 @@ Rectangle { } } TextMetrics { - id: gainSliderTextMetrics - text: gainSliderText.text - font: gainSliderText.font + id: avatarGainSliderTextMetrics + text: avatarGainSliderText.text + font: avatarGainSliderText.font } RalewayRegular { // The slider for my card is special, it controls the master gain - id: gainSliderText; + id: avatarGainSliderText; text: "Avatar volume"; size: 16; anchors.left: parent.left; @@ -523,15 +535,129 @@ Rectangle { } } + Item { + id: injectorGainContainer + x: margins.paddings; + width: parent.width - margins.paddings*2 + height: injectorGainSliderTextMetrics.height + + HifiControlsUit.Slider { + id: injectorGainSlider + anchors.right: parent.right + height: parent.height + width: 200 + minimumValue: -60.0 + maximumValue: 20.0 + stepSize: 5 + value: AudioScriptingInterface.getInjectorGain() + onValueChanged: { + updateInjectorGainFromQML(value, false); + } + onPressedChanged: { + if (!pressed) { + updateInjectorGainFromQML(value, false); + } + } + + MouseArea { + anchors.fill: parent + onWheel: { + // Do nothing. + } + onDoubleClicked: { + injectorGainSlider.value = 0.0 + } + onPressed: { + // Pass through to Slider + mouse.accepted = false + } + onReleased: { + // the above mouse.accepted seems to make this + // never get called, nonetheless... + mouse.accepted = false + } + } + } + TextMetrics { + id: injectorGainSliderTextMetrics + text: injectorGainSliderText.text + font: injectorGainSliderText.font + } + RalewayRegular { + id: injectorGainSliderText; + text: "Environment volume"; + size: 16; + anchors.left: parent.left; + color: hifi.colors.white; + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignTop; + } + } + + Item { + id: systemInjectorGainContainer + x: margins.paddings; + width: parent.width - margins.paddings*2 + height: systemInjectorGainSliderTextMetrics.height + + HifiControlsUit.Slider { + id: systemInjectorGainSlider + anchors.right: parent.right + height: parent.height + width: 200 + minimumValue: -60.0 + maximumValue: 20.0 + stepSize: 5 + value: AudioScriptingInterface.getSystemInjectorGain() + onValueChanged: { + updateSystemInjectorGainFromQML(value, false); + } + onPressedChanged: { + if (!pressed) { + updateSystemInjectorGainFromQML(value, false); + } + } + + MouseArea { + anchors.fill: parent + onWheel: { + // Do nothing. + } + onDoubleClicked: { + systemInjectorGainSlider.value = 0.0 + } + onPressed: { + // Pass through to Slider + mouse.accepted = false + } + onReleased: { + // the above mouse.accepted seems to make this + // never get called, nonetheless... + mouse.accepted = false + } + } + } + TextMetrics { + id: systemInjectorGainSliderTextMetrics + text: systemInjectorGainSliderText.text + font: systemInjectorGainSliderText.font + } + RalewayRegular { + id: systemInjectorGainSliderText; + text: "System Sound volume"; + size: 16; + anchors.left: parent.left; + color: hifi.colors.white; + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignTop; + } + } + AudioControls.PlaySampleSound { id: playSampleSound x: margins.paddings - anchors.top: gainContainer.bottom; + anchors.top: systemInjectorGainContainer.bottom; anchors.topMargin: 10; - - visible: (bar.currentIndex === 1 && isVR) || - (bar.currentIndex === 0 && !isVR); - anchors { left: parent.left; leftMargin: margins.paddings } } } } diff --git a/interface/resources/qml/hifi/audio/LoopbackAudio.qml b/interface/resources/qml/hifi/audio/LoopbackAudio.qml index 8ec0ffc496..74bc0f67dc 100644 --- a/interface/resources/qml/hifi/audio/LoopbackAudio.qml +++ b/interface/resources/qml/hifi/audio/LoopbackAudio.qml @@ -44,7 +44,7 @@ RowLayout { } HifiControlsUit.Button { - text: audioLoopedBack ? qsTr("STOP TESTING YOUR VOICE") : qsTr("TEST YOUR VOICE"); + text: audioLoopedBack ? qsTr("STOP TESTING") : qsTr("TEST YOUR VOICE"); color: audioLoopedBack ? hifi.buttons.red : hifi.buttons.blue; onClicked: { if (audioLoopedBack) { @@ -57,11 +57,11 @@ RowLayout { } } - RalewayRegular { - Layout.leftMargin: 2; - size: 14; - color: "white"; - font.italic: true - text: audioLoopedBack ? qsTr("Speak in your input") : ""; - } +// RalewayRegular { +// Layout.leftMargin: 2; +// size: 14; +// color: "white"; +// font.italic: true +// text: audioLoopedBack ? qsTr("Speak in your input") : ""; +// } } diff --git a/interface/resources/qml/hifi/audio/PlaySampleSound.qml b/interface/resources/qml/hifi/audio/PlaySampleSound.qml index b9d9727dab..0eb78f3efe 100644 --- a/interface/resources/qml/hifi/audio/PlaySampleSound.qml +++ b/interface/resources/qml/hifi/audio/PlaySampleSound.qml @@ -56,16 +56,16 @@ RowLayout { HifiConstants { id: hifi; } HifiControlsUit.Button { - text: isPlaying ? qsTr("STOP TESTING YOUR SOUND") : qsTr("TEST YOUR SOUND"); + text: isPlaying ? qsTr("STOP TESTING") : qsTr("TEST YOUR SOUND"); color: isPlaying ? hifi.buttons.red : hifi.buttons.blue; onClicked: isPlaying ? stopSound() : playSound(); } - RalewayRegular { - Layout.leftMargin: 2; - size: 14; - color: "white"; - font.italic: true - text: isPlaying ? qsTr("Listen to your output") : ""; - } +// RalewayRegular { +// Layout.leftMargin: 2; +// size: 14; +// color: "white"; +// font.italic: true +// text: isPlaying ? qsTr("Listen to your output") : ""; +// } } From a2d754cebd786741ec7d714576d4462ee3618177 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 26 Mar 2019 10:26:29 -0700 Subject: [PATCH 20/21] fixing audio screen with master --- interface/resources/qml/hifi/audio/Audio.qml | 4 ++++ .../qml/hifi/audio/LoopbackAudio.qml | 19 +++++++++++-------- .../qml/hifi/audio/PlaySampleSound.qml | 17 ++++++++++------- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 8fdd0368e2..fe86d7d930 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -540,6 +540,8 @@ Rectangle { x: margins.paddings; width: parent.width - margins.paddings*2 height: injectorGainSliderTextMetrics.height + anchors.top: avatarGainContainer.bottom; + anchors.topMargin: 10; HifiControlsUit.Slider { id: injectorGainSlider @@ -599,6 +601,8 @@ Rectangle { x: margins.paddings; width: parent.width - margins.paddings*2 height: systemInjectorGainSliderTextMetrics.height + anchors.top: injectorGainContainer.bottom; + anchors.topMargin: 10; HifiControlsUit.Slider { id: systemInjectorGainSlider diff --git a/interface/resources/qml/hifi/audio/LoopbackAudio.qml b/interface/resources/qml/hifi/audio/LoopbackAudio.qml index 74bc0f67dc..8d1099d38c 100644 --- a/interface/resources/qml/hifi/audio/LoopbackAudio.qml +++ b/interface/resources/qml/hifi/audio/LoopbackAudio.qml @@ -44,8 +44,11 @@ RowLayout { } HifiControlsUit.Button { - text: audioLoopedBack ? qsTr("STOP TESTING") : qsTr("TEST YOUR VOICE"); + text: audioLoopedBack ? qsTr("STOP TESTING VOICE") : qsTr("TEST YOUR VOICE"); color: audioLoopedBack ? hifi.buttons.red : hifi.buttons.blue; + fontSize: 15; + width: 200; + height: 32; onClicked: { if (audioLoopedBack) { loopbackTimer.stop(); @@ -57,11 +60,11 @@ RowLayout { } } -// RalewayRegular { -// Layout.leftMargin: 2; -// size: 14; -// color: "white"; -// font.italic: true -// text: audioLoopedBack ? qsTr("Speak in your input") : ""; -// } + RalewayRegular { + Layout.leftMargin: 2; + size: 14; + color: "white"; + font.italic: true + text: audioLoopedBack ? qsTr("Speak in your input") : ""; + } } diff --git a/interface/resources/qml/hifi/audio/PlaySampleSound.qml b/interface/resources/qml/hifi/audio/PlaySampleSound.qml index 0eb78f3efe..4675f6087a 100644 --- a/interface/resources/qml/hifi/audio/PlaySampleSound.qml +++ b/interface/resources/qml/hifi/audio/PlaySampleSound.qml @@ -59,13 +59,16 @@ RowLayout { text: isPlaying ? qsTr("STOP TESTING") : qsTr("TEST YOUR SOUND"); color: isPlaying ? hifi.buttons.red : hifi.buttons.blue; onClicked: isPlaying ? stopSound() : playSound(); + fontSize: 15; + width: 200; + height: 32; } -// RalewayRegular { -// Layout.leftMargin: 2; -// size: 14; -// color: "white"; -// font.italic: true -// text: isPlaying ? qsTr("Listen to your output") : ""; -// } + RalewayRegular { + Layout.leftMargin: 2; + size: 14; + color: "white"; + font.italic: true + text: isPlaying ? qsTr("Listen to your output") : ""; + } } From c0b71150eafa60947b5a1390ac8c18795cb25dd8 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 26 Mar 2019 10:58:04 -0700 Subject: [PATCH 21/21] removing an extra button --- interface/resources/qml/hifi/audio/Audio.qml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 69cbad458b..195dd78a0e 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -438,14 +438,6 @@ Rectangle { color: hifi.colors.white; text: qsTr("Choose output device"); } - - AudioControls.PlaySampleSound { - x: margins.paddings - - visible: (bar.currentIndex === 1 && isVR) || - (bar.currentIndex === 0 && !isVR); - anchors { right: parent.right } - } } ListView {