From 051616d7c3045d14132ae142453bf3066efc3ad5 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 8 Jul 2016 16:16:06 -0700 Subject: [PATCH 01/25] experimenting --- interface/src/avatar/AvatarActionHold.cpp | 2 +- scripts/system/controllers/handControllerGrab.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index c84cfecb40..1babb478b8 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -168,7 +168,7 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm:: position = palmPosition + rotation * _relativePosition; // update linearVelocity based on offset via _relativePosition; - linearVelocity = linearVelocity + glm::cross(angularVelocity, position - palmPosition); + // linearVelocity = linearVelocity + glm::cross(angularVelocity, position - palmPosition); }); return true; diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 373f203e80..4e4269df28 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -13,7 +13,7 @@ /* global setEntityCustomData, getEntityCustomData, vec3toStr, flatten, Xform */ Script.include("/~/system/libraries/utils.js"); -Script.include("../libraries/Xform.js"); +Script.include("/~/system/libraries/Xform.js"); // // add lines where the hand ray picking is happening From eac144f35476762dfb6d99155320d22a00445b69 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 11 Jul 2016 09:52:24 -0700 Subject: [PATCH 02/25] experimenting --- interface/src/avatar/AvatarActionHold.cpp | 9 ++++++++- interface/src/avatar/AvatarActionHold.h | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 1babb478b8..2baaae6ee4 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -204,8 +204,15 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { } withWriteLock([&]{ + if (_previousSet) { + _measuredLinearVelocity = (_positionalTarget - _previousPositionalTarget) / deltaTimeStep; + } else { + _measuredLinearVelocity = glm::vec3(); + } + if (_kinematicSetVelocity) { - rigidBody->setLinearVelocity(glmToBullet(_linearVelocityTarget)); + // rigidBody->setLinearVelocity(glmToBullet(_linearVelocityTarget)); + rigidBody->setLinearVelocity(glmToBullet(_measuredLinearVelocity)); rigidBody->setAngularVelocity(glmToBullet(_angularVelocityTarget)); } diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index 609fd57ff3..e246ac5f36 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -64,6 +64,8 @@ private: glm::vec3 _palmOffsetFromRigidBody; // leaving this here for future refernece. // glm::quat _palmRotationFromRigidBody; + + glm::vec3 _measuredLinearVelocity; }; #endif // hifi_AvatarActionHold_h From 7295e5c101fe1c0fe06b605f8087d7da53738059 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 11 Jul 2016 10:22:44 -0700 Subject: [PATCH 03/25] experimenting --- interface/src/avatar/AvatarActionHold.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 2baaae6ee4..d1750ddf53 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -206,6 +206,8 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { withWriteLock([&]{ if (_previousSet) { _measuredLinearVelocity = (_positionalTarget - _previousPositionalTarget) / deltaTimeStep; + qDebug() << _measuredLinearVelocity.x << _measuredLinearVelocity.y << _measuredLinearVelocity.z + << deltaTimeStep; } else { _measuredLinearVelocity = glm::vec3(); } From 53a366d4e882c8255aeabc45f8c327f3e9c5671d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 11 Jul 2016 13:25:00 -0700 Subject: [PATCH 04/25] measure velocity over 6 frames --- interface/src/avatar/AvatarActionHold.cpp | 23 ++++++++++++++++------- interface/src/avatar/AvatarActionHold.h | 4 +++- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index d1750ddf53..ce0cf886d5 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -17,11 +17,14 @@ #include "CharacterController.h" const uint16_t AvatarActionHold::holdVersion = 1; +const int AvatarActionHold::velocitySmoothFrames = 6; + AvatarActionHold::AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntity) : ObjectActionSpring(id, ownerEntity) { _type = ACTION_TYPE_HOLD; + _measuredLinearVelocities.resize(AvatarActionHold::velocitySmoothFrames); #if WANT_DEBUG qDebug() << "AvatarActionHold::AvatarActionHold"; #endif @@ -168,7 +171,7 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm:: position = palmPosition + rotation * _relativePosition; // update linearVelocity based on offset via _relativePosition; - // linearVelocity = linearVelocity + glm::cross(angularVelocity, position - palmPosition); + linearVelocity = linearVelocity + glm::cross(angularVelocity, position - palmPosition); }); return true; @@ -205,16 +208,22 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { withWriteLock([&]{ if (_previousSet) { - _measuredLinearVelocity = (_positionalTarget - _previousPositionalTarget) / deltaTimeStep; - qDebug() << _measuredLinearVelocity.x << _measuredLinearVelocity.y << _measuredLinearVelocity.z - << deltaTimeStep; - } else { - _measuredLinearVelocity = glm::vec3(); + glm::vec3 oneFrameVelocity = (_positionalTarget - _previousPositionalTarget) / deltaTimeStep; + _measuredLinearVelocities[_measuredLinearVelocitiesIndex++] = oneFrameVelocity; + if (_measuredLinearVelocitiesIndex >= AvatarActionHold::velocitySmoothFrames) { + _measuredLinearVelocitiesIndex = 0; + } } + glm::vec3 measuredLinearVelocity; + for (int i = 0; i < AvatarActionHold::velocitySmoothFrames; i++) { + measuredLinearVelocity += _measuredLinearVelocities[i]; + } + measuredLinearVelocity /= (float)AvatarActionHold::velocitySmoothFrames; + if (_kinematicSetVelocity) { // rigidBody->setLinearVelocity(glmToBullet(_linearVelocityTarget)); - rigidBody->setLinearVelocity(glmToBullet(_measuredLinearVelocity)); + rigidBody->setLinearVelocity(glmToBullet(measuredLinearVelocity)); rigidBody->setAngularVelocity(glmToBullet(_angularVelocityTarget)); } diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index e246ac5f36..bfa392172d 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -65,7 +65,9 @@ private: // leaving this here for future refernece. // glm::quat _palmRotationFromRigidBody; - glm::vec3 _measuredLinearVelocity; + static const int velocitySmoothFrames; + QVector _measuredLinearVelocities; + int _measuredLinearVelocitiesIndex { 0 }; }; #endif // hifi_AvatarActionHold_h From 9c17890035422ecbc0bc0031ecc72f8d6e699c0f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 11 Jul 2016 14:18:28 -0700 Subject: [PATCH 05/25] try not including the most recent sample in the smoothed velocity --- interface/src/avatar/AvatarActionHold.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index ce0cf886d5..4583813131 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -207,6 +207,15 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { } withWriteLock([&]{ + glm::vec3 measuredLinearVelocity; + // there is a bit of lag between when someone releases the trigger and the software reacts to + // the release. we calculate the velocity from previous frames but not this frame (by having + // this code before where _measuredLinearVelocities is set, below) in order to help mask this. + for (int i = 0; i < AvatarActionHold::velocitySmoothFrames; i++) { + measuredLinearVelocity += _measuredLinearVelocities[i]; + } + measuredLinearVelocity /= (float)AvatarActionHold::velocitySmoothFrames; + if (_previousSet) { glm::vec3 oneFrameVelocity = (_positionalTarget - _previousPositionalTarget) / deltaTimeStep; _measuredLinearVelocities[_measuredLinearVelocitiesIndex++] = oneFrameVelocity; @@ -215,12 +224,6 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { } } - glm::vec3 measuredLinearVelocity; - for (int i = 0; i < AvatarActionHold::velocitySmoothFrames; i++) { - measuredLinearVelocity += _measuredLinearVelocities[i]; - } - measuredLinearVelocity /= (float)AvatarActionHold::velocitySmoothFrames; - if (_kinematicSetVelocity) { // rigidBody->setLinearVelocity(glmToBullet(_linearVelocityTarget)); rigidBody->setLinearVelocity(glmToBullet(measuredLinearVelocity)); From b0f1492ac7b3844fd5440dc9542069bdfe687ea5 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 11 Jul 2016 15:08:00 -0700 Subject: [PATCH 06/25] ignore 3 frames for current velocity rather than just 1 --- interface/src/avatar/AvatarActionHold.cpp | 34 +++++++++++++++++------ 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 4583813131..3ab81c5462 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -207,15 +207,6 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { } withWriteLock([&]{ - glm::vec3 measuredLinearVelocity; - // there is a bit of lag between when someone releases the trigger and the software reacts to - // the release. we calculate the velocity from previous frames but not this frame (by having - // this code before where _measuredLinearVelocities is set, below) in order to help mask this. - for (int i = 0; i < AvatarActionHold::velocitySmoothFrames; i++) { - measuredLinearVelocity += _measuredLinearVelocities[i]; - } - measuredLinearVelocity /= (float)AvatarActionHold::velocitySmoothFrames; - if (_previousSet) { glm::vec3 oneFrameVelocity = (_positionalTarget - _previousPositionalTarget) / deltaTimeStep; _measuredLinearVelocities[_measuredLinearVelocitiesIndex++] = oneFrameVelocity; @@ -224,6 +215,31 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { } } + glm::vec3 measuredLinearVelocity; + for (int i = 0; i < AvatarActionHold::velocitySmoothFrames; i++) { + // there is a bit of lag between when someone releases the trigger and when the software reacts to + // the release. we calculate the velocity from previous frames but we don't include several + // of the most recent. + // + // if _measuredLinearVelocitiesIndex is + // 0 -- ignore i of 3 4 5 + // 1 -- ignore i of 4 5 0 + // 2 -- ignore i of 5 0 1 + // 3 -- ignore i of 0 1 2 + // 4 -- ignore i of 1 2 3 + // 5 -- ignore i of 2 3 4 + if ((i + 1) % 6 == _measuredLinearVelocitiesIndex || + (i + 2) % 6 == _measuredLinearVelocitiesIndex || + (i + 3) % 6 == _measuredLinearVelocitiesIndex) { + continue; + } + + measuredLinearVelocity += _measuredLinearVelocities[i]; + } + measuredLinearVelocity /= (float)AvatarActionHold::velocitySmoothFrames; + + + if (_kinematicSetVelocity) { // rigidBody->setLinearVelocity(glmToBullet(_linearVelocityTarget)); rigidBody->setLinearVelocity(glmToBullet(measuredLinearVelocity)); From 1ef26d39eb43162f1fe5253841ab5ad56bf79a8c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 11 Jul 2016 15:31:15 -0700 Subject: [PATCH 07/25] blah --- interface/src/avatar/AvatarActionHold.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 3ab81c5462..ce623cf604 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -236,7 +236,7 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { measuredLinearVelocity += _measuredLinearVelocities[i]; } - measuredLinearVelocity /= (float)AvatarActionHold::velocitySmoothFrames; + measuredLinearVelocity /= (float)(AvatarActionHold::velocitySmoothFrames - 3); From 0d0e0925e5b27f4944bc4cc074cf3e333da49386 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 11 Jul 2016 16:00:55 -0700 Subject: [PATCH 08/25] try skipping 2 rather than 3 --- interface/src/avatar/AvatarActionHold.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index ce623cf604..3bbdf9f099 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -222,21 +222,20 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { // of the most recent. // // if _measuredLinearVelocitiesIndex is - // 0 -- ignore i of 3 4 5 - // 1 -- ignore i of 4 5 0 - // 2 -- ignore i of 5 0 1 - // 3 -- ignore i of 0 1 2 - // 4 -- ignore i of 1 2 3 - // 5 -- ignore i of 2 3 4 + // 0 -- ignore i of 4 5 + // 1 -- ignore i of 5 0 + // 2 -- ignore i of 0 1 + // 3 -- ignore i of 1 2 + // 4 -- ignore i of 2 3 + // 5 -- ignore i of 3 4 if ((i + 1) % 6 == _measuredLinearVelocitiesIndex || - (i + 2) % 6 == _measuredLinearVelocitiesIndex || - (i + 3) % 6 == _measuredLinearVelocitiesIndex) { + (i + 2) % 6 == _measuredLinearVelocitiesIndex) { continue; } measuredLinearVelocity += _measuredLinearVelocities[i]; } - measuredLinearVelocity /= (float)(AvatarActionHold::velocitySmoothFrames - 3); + measuredLinearVelocity /= (float)(AvatarActionHold::velocitySmoothFrames - 2); From 84c5bef487fe6d16e38c3aa4e8ffd9b6f5802f8a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 11 Jul 2016 17:02:11 -0700 Subject: [PATCH 09/25] back to ignore more recent 3 frames. don't have released entities collide with myAvatar until 0.25 seconds after release --- interface/src/avatar/AvatarActionHold.cpp | 20 ++++++++--------- .../system/controllers/handControllerGrab.js | 22 ++++++++++++++++++- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 3bbdf9f099..32905361dd 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -222,22 +222,20 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { // of the most recent. // // if _measuredLinearVelocitiesIndex is - // 0 -- ignore i of 4 5 - // 1 -- ignore i of 5 0 - // 2 -- ignore i of 0 1 - // 3 -- ignore i of 1 2 - // 4 -- ignore i of 2 3 - // 5 -- ignore i of 3 4 + // 0 -- ignore i of 3 4 5 + // 1 -- ignore i of 4 5 0 + // 2 -- ignore i of 5 0 1 + // 3 -- ignore i of 0 1 2 + // 4 -- ignore i of 1 2 3 + // 5 -- ignore i of 2 3 4 if ((i + 1) % 6 == _measuredLinearVelocitiesIndex || - (i + 2) % 6 == _measuredLinearVelocitiesIndex) { + (i + 2) % 6 == _measuredLinearVelocitiesIndex || + (i + 3) % 6 == _measuredLinearVelocitiesIndex) { continue; } - measuredLinearVelocity += _measuredLinearVelocities[i]; } - measuredLinearVelocity /= (float)(AvatarActionHold::velocitySmoothFrames - 2); - - + measuredLinearVelocity /= (float)(AvatarActionHold::velocitySmoothFrames - 3); if (_kinematicSetVelocity) { // rigidBody->setLinearVelocity(glmToBullet(_linearVelocityTarget)); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 2cef48290c..36cef8ec2d 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -33,6 +33,8 @@ var TRIGGER_ON_VALUE = 0.4; // Squeezed just enough to activate search or near var TRIGGER_GRAB_VALUE = 0.85; // Squeezed far enough to complete distant grab var TRIGGER_OFF_VALUE = 0.15; +var COLLIDE_WITH_AV_AFTER_RELEASE_DELAY = 0.25; // seconds + var BUMPER_ON_VALUE = 0.5; var THUMB_ON_VALUE = 0.5; @@ -263,6 +265,17 @@ function propsArePhysical(props) { return isPhysical; } +function removeMyAvatarFromCollidesWith(origCollidesWith) { + var collidesWithSplit = origCollidesWith.split(","); + // remove myAvatar from the array + for (var i = collidesWithSplit.length - 1; i >= 0; i--) { + if (collidesWithSplit[i] === "myAvatar") { + collidesWithSplit.splice(i, 1); + } + } + return collidesWithSplit.join(); +} + // If another script is managing the reticle (as is done by HandControllerPointer), we should not be setting it here, // and we should not be showing lasers when someone else is using the Reticle to indicate a 2D minor mode. var EXTERNALLY_MANAGED_2D_MINOR_MODE = true; @@ -2128,13 +2141,20 @@ function MyController(hand) { if (data["refCount"] < 1) { deactiveProps = { gravity: data["gravity"], - collidesWith: data["collidesWith"], + // don't set collidesWith back right away, because thrown things tend to bounce off the + // avatar's capsule. + collidesWith: removeMyAvatarFromCollidesWith(data["collidesWith"]), collisionless: data["collisionless"], dynamic: data["dynamic"], parentID: data["parentID"], parentJointIndex: data["parentJointIndex"] }; + Script.setTimeout(function () { + // set collidesWith back to original value a bit later than the rest + Entities.editEntity(entityID, { collidesWith: data["collidesWith"] }); + }, COLLIDE_WITH_AV_AFTER_RELEASE_DELAY); + // things that are held by parenting and dropped with no velocity will end up as "static" in bullet. If // it looks like the dropped thing should fall, give it a little velocity. var props = Entities.getEntityProperties(entityID, ["parentID", "velocity", "dynamic", "shapeType"]); From 367c26a5eef1ba352ac5178d476b5b85cc5412bd Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 11 Jul 2016 18:53:47 -0700 Subject: [PATCH 10/25] wait a bit before resetting collides-with-my-avatar --- .../system/controllers/handControllerGrab.js | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 36cef8ec2d..aaaa7c5962 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -181,6 +181,10 @@ var COLLIDES_WITH_WHILE_MULTI_GRABBED = "dynamic"; var HEART_BEAT_INTERVAL = 5 * MSECS_PER_SEC; var HEART_BEAT_TIMEOUT = 15 * MSECS_PER_SEC; +var avCollideLater; +var setCollidesLaterTimeout; +var collideLaterID; + var CONTROLLER_STATE_MACHINE = {}; CONTROLLER_STATE_MACHINE[STATE_OFF] = { @@ -2060,6 +2064,17 @@ function MyController(hand) { } this.entityActivated = true; + if (setCollidesLaterTimeout && collideLaterID == entityID) { + // we have a timeout waiting to set collisions with myAvatar back on (so that when something + // is thrown it doesn't collide with the avatar's capsule the moment it's released). We've + // regrabbed the entity before the timeout fired, so cancel the timeout, run the function now + // and adjust the grabbedProperties. This will make the saved set of properties (the ones that + // get re-instated after all the grabs have been released) be correct. + Script.clearTimeout(setCollidesLaterTimeout); + setCollidesLaterTimeout = null; + grabbedProperties["collidesWith"] = avCollideLater(); + } + var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); var now = Date.now(); @@ -2150,10 +2165,18 @@ function MyController(hand) { parentJointIndex: data["parentJointIndex"] }; - Script.setTimeout(function () { - // set collidesWith back to original value a bit later than the rest - Entities.editEntity(entityID, { collidesWith: data["collidesWith"] }); - }, COLLIDE_WITH_AV_AFTER_RELEASE_DELAY); + if (data["collidesWith"].indexOf("myAvatar") >= 0) { + var javaScriptScopingIsBrokenData = data; + avCollideLater = function () { + // set collidesWith back to original value a bit later than the rest + _this.setCollidesLaterTimeout = null; + Entities.editEntity(entityID, { collidesWith: javaScriptScopingIsBrokenData["collidesWith"] }); + return javaScriptScopingIsBrokenData["collidesWith"]; + } + setCollidesLaterTimeout = + Script.setTimeout(avCollideLater, COLLIDE_WITH_AV_AFTER_RELEASE_DELAY * MSECS_PER_SEC); + collideLaterID = entityID; + } // things that are held by parenting and dropped with no velocity will end up as "static" in bullet. If // it looks like the dropped thing should fall, give it a little velocity. From 19991939073353002b098df01a6d193e67890832 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 12 Jul 2016 11:42:58 -0700 Subject: [PATCH 11/25] split the grab script's deactivation on an entity into two parts -- one that happens during release and a delayed part which finishes up. This makes collisions with a thrower's capsule less likely --- .../system/controllers/handControllerGrab.js | 65 +++++++++++++------ 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index aaaa7c5962..ac903190c4 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -181,9 +181,9 @@ var COLLIDES_WITH_WHILE_MULTI_GRABBED = "dynamic"; var HEART_BEAT_INTERVAL = 5 * MSECS_PER_SEC; var HEART_BEAT_TIMEOUT = 15 * MSECS_PER_SEC; -var avCollideLater; -var setCollidesLaterTimeout; -var collideLaterID; +var delayedDeactivateFunc; +var delayedDeactivateTimeout; +var delayedDeactivateEntityID; var CONTROLLER_STATE_MACHINE = {}; @@ -2064,15 +2064,15 @@ function MyController(hand) { } this.entityActivated = true; - if (setCollidesLaterTimeout && collideLaterID == entityID) { + if (delayedDeactivateTimeout && delayedDeactivateEntityID == entityID) { // we have a timeout waiting to set collisions with myAvatar back on (so that when something // is thrown it doesn't collide with the avatar's capsule the moment it's released). We've // regrabbed the entity before the timeout fired, so cancel the timeout, run the function now // and adjust the grabbedProperties. This will make the saved set of properties (the ones that // get re-instated after all the grabs have been released) be correct. - Script.clearTimeout(setCollidesLaterTimeout); - setCollidesLaterTimeout = null; - grabbedProperties["collidesWith"] = avCollideLater(); + Script.clearTimeout(delayedDeactivateTimeout); + delayedDeactivateTimeout = null; + grabbedProperties["collidesWith"] = delayedDeactivateFunc(); } var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); @@ -2142,7 +2142,27 @@ function MyController(hand) { }); }; - this.deactivateEntity = function (entityID, noVelocity) { + this.delayedDeactivateEntity = function (entityID, collidesWith) { + // If, before the grab started, the held entity collided with myAvatar, we do the deactivation in + // two parts. Most of it is done in deactivateEntity(), but the final collidesWith and refcount + // are delayed a bit. This keeps thrown things from colliding with the avatar's capsule so often. + // The refcount is handled in this delayed fashion so things don't get confused if someone else + // grabs the entity before the timeout fires. + Entities.editEntity(entityID, { collidesWith: collidesWith }); + var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); + if (data && data["refCount"]) { + data["refCount"] = data["refCount"] - 1; + if (data["refCount"] < 1) { + data = null; + } + } else { + data = null; + } + + setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); + }; + + this.deactivateEntity = function (entityID, noVelocity, delayed) { var deactiveProps; if (!this.entityActivated) { @@ -2151,12 +2171,13 @@ function MyController(hand) { this.entityActivated = false; var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); + var doDelayedDeactivate = false; if (data && data["refCount"]) { data["refCount"] = data["refCount"] - 1; if (data["refCount"] < 1) { deactiveProps = { gravity: data["gravity"], - // don't set collidesWith back right away, because thrown things tend to bounce off the + // don't set collidesWith myAvatar back right away, because thrown things tend to bounce off the // avatar's capsule. collidesWith: removeMyAvatarFromCollidesWith(data["collidesWith"]), collisionless: data["collisionless"], @@ -2165,17 +2186,20 @@ function MyController(hand) { parentJointIndex: data["parentJointIndex"] }; - if (data["collidesWith"].indexOf("myAvatar") >= 0) { - var javaScriptScopingIsBrokenData = data; - avCollideLater = function () { + doDelayedDeactivate = (data["collidesWith"].indexOf("myAvatar") >= 0); + + if (doDelayedDeactivate) { + var delayedCollidesWith = data["collidesWith"]; + var delayedEntityID = entityID; + delayedDeactivateFunc = function () { // set collidesWith back to original value a bit later than the rest - _this.setCollidesLaterTimeout = null; - Entities.editEntity(entityID, { collidesWith: javaScriptScopingIsBrokenData["collidesWith"] }); - return javaScriptScopingIsBrokenData["collidesWith"]; + delayedDeactivateTimeout = null; + _this.delayedDeactivateEntity(delayedEntityID, delayedCollidesWith); + return delayedCollidesWith; } - setCollidesLaterTimeout = - Script.setTimeout(avCollideLater, COLLIDE_WITH_AV_AFTER_RELEASE_DELAY * MSECS_PER_SEC); - collideLaterID = entityID; + delayedDeactivateTimeout = + Script.setTimeout(delayedDeactivateFunc, COLLIDE_WITH_AV_AFTER_RELEASE_DELAY * MSECS_PER_SEC); + delayedDeactivateEntityID = entityID; } // things that are held by parenting and dropped with no velocity will end up as "static" in bullet. If @@ -2220,7 +2244,6 @@ function MyController(hand) { // angularVelocity: this.currentAngularVelocity }); } - data = null; } else if (this.shouldResetParentOnRelease) { // we parent-grabbed this from another parent grab. try to put it back where we found it. @@ -2239,7 +2262,9 @@ function MyController(hand) { } else { data = null; } - setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); + if (!doDelayedDeactivate) { + setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); + } }; this.getOtherHandController = function () { From 6c14ef3392dbdbef2ffa577dabf567f22494d6c6 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 12 Jul 2016 11:56:57 -0700 Subject: [PATCH 12/25] added a comment --- interface/src/avatar/AvatarActionHold.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 32905361dd..a2130240f5 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -235,7 +235,7 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { } measuredLinearVelocity += _measuredLinearVelocities[i]; } - measuredLinearVelocity /= (float)(AvatarActionHold::velocitySmoothFrames - 3); + measuredLinearVelocity /= (float)(AvatarActionHold::velocitySmoothFrames - 3); // 3 because of the 3 we skipped, above if (_kinematicSetVelocity) { // rigidBody->setLinearVelocity(glmToBullet(_linearVelocityTarget)); From 0dd2171b759c1c135485501c32d1b80ada52354b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 12 Jul 2016 11:57:57 -0700 Subject: [PATCH 13/25] remove commented-out code --- interface/src/avatar/AvatarActionHold.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index a2130240f5..bac3b1e02f 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -238,7 +238,6 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { measuredLinearVelocity /= (float)(AvatarActionHold::velocitySmoothFrames - 3); // 3 because of the 3 we skipped, above if (_kinematicSetVelocity) { - // rigidBody->setLinearVelocity(glmToBullet(_linearVelocityTarget)); rigidBody->setLinearVelocity(glmToBullet(measuredLinearVelocity)); rigidBody->setAngularVelocity(glmToBullet(_angularVelocityTarget)); } From b4b7b7ea34aeaf98a07bc8a98a0cab6f52484048 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 13 Jul 2016 13:41:45 -0700 Subject: [PATCH 14/25] Update all asset server responses to be reliable --- assignment-client/src/assets/AssetServer.cpp | 5 +++-- assignment-client/src/assets/UploadAssetTask.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 7f43b86328..905cc6fd30 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -298,7 +298,8 @@ void AssetServer::handleAssetGetInfo(QSharedPointer message, Sh message->readPrimitive(&messageID); assetHash = message->readWithoutCopy(SHA256_HASH_LENGTH); - auto replyPacket = NLPacket::create(PacketType::AssetGetInfoReply); + auto size = qint64(sizeof(MessageID) + SHA256_HASH_LENGTH + sizeof(AssetServerError) + sizeof(qint64)); + auto replyPacket = NLPacket::create(PacketType::AssetGetInfoReply, size, true); QByteArray hexHash = assetHash.toHex(); @@ -347,7 +348,7 @@ void AssetServer::handleAssetUpload(QSharedPointer message, Sha // for now this also means it isn't allowed to add assets // so return a packet with error that indicates that - auto permissionErrorPacket = NLPacket::create(PacketType::AssetUploadReply, sizeof(MessageID) + sizeof(AssetServerError)); + auto permissionErrorPacket = NLPacket::create(PacketType::AssetUploadReply, sizeof(MessageID) + sizeof(AssetServerError), true); MessageID messageID; message->readPrimitive(&messageID); diff --git a/assignment-client/src/assets/UploadAssetTask.cpp b/assignment-client/src/assets/UploadAssetTask.cpp index 5ca9b5bbf1..e09619a3cc 100644 --- a/assignment-client/src/assets/UploadAssetTask.cpp +++ b/assignment-client/src/assets/UploadAssetTask.cpp @@ -43,7 +43,7 @@ void UploadAssetTask::run() { qDebug() << "UploadAssetTask reading a file of " << fileSize << "bytes from" << uuidStringWithoutCurlyBraces(_senderNode->getUUID()); - auto replyPacket = NLPacket::create(PacketType::AssetUploadReply); + auto replyPacket = NLPacket::create(PacketType::AssetUploadReply, -1, true); replyPacket->writePrimitive(messageID); if (fileSize > MAX_UPLOAD_SIZE) { From b6ffabf5003228385b1543f13283aa44d5f7883a Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 13 Jul 2016 09:08:57 -0700 Subject: [PATCH 15/25] attempt to get mac sandbox plugins to work --- cmake/macros/GenerateInstallers.cmake | 26 +++++++++++++++++++ .../macros/SetupHifiClientServerPlugin.cmake | 3 ++- server-console/CMakeLists.txt | 26 +++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake index 0def701739..2d2ede69f0 100644 --- a/cmake/macros/GenerateInstallers.cmake +++ b/cmake/macros/GenerateInstallers.cmake @@ -78,6 +78,32 @@ macro(GENERATE_INSTALLERS) install(CODE "execute_process(COMMAND SetFile -a V \${CMAKE_INSTALL_PREFIX}/${ESCAPED_DMG_SUBFOLDER_NAME}/Icon\\r)") endif () + # this is a bit of a hack, but I couldn't find any other way to get plugins + # into the correct place under the console path. On windows this is handled + # exclusively in the plugins directories with the SetupHiFiClientServerPlugin + # macro + if (APPLE) + set(CONSOLE_PLUGINS_DIR "${CONSOLE_INSTALL_APP_PATH}/Contents/MacOS/Components.app/Contents/PlugIns") + set(SERVER_PLUGINS_DIR "${CMAKE_BINARY_DIR}/assignment-client/${CMAKE_BUILD_TYPE}/plugins") + + message("TARGET_NAME: ${TARGET_NAME}") + message("CONFIGURATION: ${CONFIGURATION}") + message(": $") + message("CONSOLE_PLUGINS_DIR: ${CONSOLE_PLUGINS_DIR}") + message("SERVER_PLUGINS_DIR: ${SERVER_PLUGINS_DIR}") + + add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E make_directory + ${CONSOLE_PLUGINS_DIR} + ) + + add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + ${SERVER_PLUGINS_DIR} + ${CONSOLE_PLUGINS_DIR} + ) + endif () + # configure a cpack properties file for custom variables in template set(CPACK_CONFIGURED_PROP_FILE "${CMAKE_CURRENT_BINARY_DIR}/CPackCustomProperties.cmake") configure_file("${HF_CMAKE_DIR}/templates/CPackProperties.cmake.in" ${CPACK_CONFIGURED_PROP_FILE}) diff --git a/cmake/macros/SetupHifiClientServerPlugin.cmake b/cmake/macros/SetupHifiClientServerPlugin.cmake index dfe3113fbc..a065ee99b6 100644 --- a/cmake/macros/SetupHifiClientServerPlugin.cmake +++ b/cmake/macros/SetupHifiClientServerPlugin.cmake @@ -13,7 +13,8 @@ macro(SETUP_HIFI_CLIENT_SERVER_PLUGIN) if (APPLE) set(CLIENT_PLUGIN_PATH "${INTERFACE_BUNDLE_NAME}.app/Contents/PlugIns") - set(SERVER_PLUGIN_PATH "Components.app/Contents/PlugIns") + #set(SERVER_PLUGIN_PATH "Components.app/Contents/PlugIns") + set(SERVER_PLUGIN_PATH "plugins") else() set(CLIENT_PLUGIN_PATH "plugins") set(SERVER_PLUGIN_PATH "plugins") diff --git a/server-console/CMakeLists.txt b/server-console/CMakeLists.txt index 1c6e40c582..7b9c17ace4 100644 --- a/server-console/CMakeLists.txt +++ b/server-console/CMakeLists.txt @@ -72,3 +72,29 @@ else () set_target_properties(${TARGET_NAME} PROPERTIES EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_DEFAULT_BUILD TRUE) set_target_properties(${TARGET_NAME}-npm-install PROPERTIES EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_DEFAULT_BUILD TRUE) endif () + +# this is a bit of a hack, but I couldn't find any other way to get plugins +# into the correct place under the console path. On windows this is handled +# exclusively in the plugins directories with the SetupHiFiClientServerPlugin +# macro +if (APPLE) + set(CONSOLE_PLUGINS_DIR "${CONSOLE_INSTALL_APP_PATH}/Contents/MacOS/Components.app/Contents/PlugIns") + set(SERVER_PLUGINS_DIR "${CMAKE_BINARY_DIR}/assignment-client/${CMAKE_BUILD_TYPE}/plugins") + + message("TARGET_NAME: ${TARGET_NAME}") + message("CONFIGURATION: ${CONFIGURATION}") + message(": $") + message("CONSOLE_PLUGINS_DIR: ${CONSOLE_PLUGINS_DIR}") + message("SERVER_PLUGINS_DIR: ${SERVER_PLUGINS_DIR}") + + add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E make_directory + ${CONSOLE_PLUGINS_DIR} + ) + + add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + ${SERVER_PLUGINS_DIR} + ${CONSOLE_PLUGINS_DIR} + ) +endif () From 1d77cec125876844b3c4afced504aa475db9a094 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Wed, 13 Jul 2016 07:32:05 -0700 Subject: [PATCH 16/25] Support for scale property on model overlays If "scale" property is present but "dimensions" are not, the model is NOT scale to fit. And the scale of the model's natural dimensions will be affected by the scale properties. If only the "dimensions" property is present, the model will "scale to fit" the dimensions. If both properties are present, the model still be "scale to fit" but the dimension will be scaled by the scale factor. For example: If a model is loaded that is 2cm tall, is loaded with no "dimensions" or "scale" properties. It will be displayed as 2cm tall. {"scale": 2} The above properties will result in a model that is 4cm tall. {"dimensions": 1} This will result in a model that is 1cm tall. {"scale": 2, "dimensions" 2} Will result in a model that is 2cm tall. --- interface/src/ui/overlays/Base3DOverlay.h | 16 +++--- interface/src/ui/overlays/ModelOverlay.cpp | 55 +++++++++++++------ interface/src/ui/overlays/ModelOverlay.h | 5 +- interface/src/ui/overlays/Volume3DOverlay.cpp | 6 +- interface/src/ui/overlays/Volume3DOverlay.h | 14 ++--- 5 files changed, 60 insertions(+), 36 deletions(-) diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index 41e7e517b7..e602dec48c 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -17,7 +17,7 @@ class Base3DOverlay : public Overlay { Q_OBJECT - + public: Base3DOverlay(); Base3DOverlay(const Base3DOverlay* base3DOverlay); @@ -27,10 +27,10 @@ public: const glm::vec3& getPosition() const { return _transform.getTranslation(); } const glm::quat& getRotation() const { return _transform.getRotation(); } const glm::vec3& getScale() const { return _transform.getScale(); } - + // TODO: consider implementing registration points in this class const glm::vec3& getCenter() const { return getPosition(); } - + float getLineWidth() const { return _lineWidth; } bool getIsSolid() const { return _isSolid; } bool getIsDashedLine() const { return _isDashedLine; } @@ -43,7 +43,7 @@ public: void setRotation(const glm::quat& value) { _transform.setRotation(value); } void setScale(float value) { _transform.setScale(value); } void setScale(const glm::vec3& value) { _transform.setScale(value); } - + void setLineWidth(float lineWidth) { _lineWidth = lineWidth; } void setIsSolid(bool isSolid) { _isSolid = isSolid; } void setIsDashedLine(bool isDashedLine) { _isDashedLine = isDashedLine; } @@ -55,22 +55,22 @@ public: void setProperties(const QVariantMap& properties) override; QVariant getProperty(const QString& property) override; - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal); - virtual bool findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction, + virtual bool findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal, QString& extraInfo) { return findRayIntersection(origin, direction, distance, face, surfaceNormal); } protected: Transform _transform; - + float _lineWidth; bool _isSolid; bool _isDashedLine; bool _ignoreRayIntersection; bool _drawInFront; }; - + #endif // hifi_Base3DOverlay_h diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index a857dc39e0..2897c07e64 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -19,8 +19,7 @@ QString const ModelOverlay::TYPE = "model"; ModelOverlay::ModelOverlay() : _model(std::make_shared(std::make_shared())), - _modelTextures(QVariantMap()), - _updateModel(false) + _modelTextures(QVariantMap()) { _model->init(); _isLoaded = false; @@ -44,7 +43,11 @@ void ModelOverlay::update(float deltatime) { if (_updateModel) { _updateModel = false; _model->setSnapModelToCenter(true); - _model->setScaleToFit(true, getDimensions()); + if (_scaleToFit) { + _model->setScaleToFit(true, getScale() * getDimensions()); + } else { + _model->setScale(getScale()); + } _model->setRotation(getRotation()); _model->setTranslation(getPosition()); _model->setURL(_url); @@ -84,16 +87,33 @@ void ModelOverlay::render(RenderArgs* args) { } void ModelOverlay::setProperties(const QVariantMap& properties) { - auto position = getPosition(); - auto rotation = getRotation(); + auto origPosition = getPosition(); + auto origRotation = getRotation(); + auto origDimensions = getDimensions(); + auto origScale = getScale(); - Volume3DOverlay::setProperties(properties); + Base3DOverlay::setProperties(properties); - if (position != getPosition() || rotation != getRotation()) { - _updateModel = true; + auto scale = properties["scale"]; + if (scale.isValid()) { + setScale(vec3FromVariant(scale)); } - _updateModel = true; + auto dimensions = properties["dimensions"]; + if (dimensions.isValid()) { + _scaleToFit = true; + setDimensions(vec3FromVariant(dimensions)); + } else if (scale.isValid()) { + // if "scale" property is set but "dimentions" is not. + // do NOT scale to fit. + if (scale.isValid()) { + _scaleToFit = false; + } + } + + if (origPosition != getPosition() || origRotation != getRotation() || origDimensions != getDimensions() || origScale != getScale()) { + _updateModel = true; + } auto urlValue = properties["url"]; if (urlValue.isValid() && urlValue.canConvert()) { @@ -101,15 +121,15 @@ void ModelOverlay::setProperties(const QVariantMap& properties) { _updateModel = true; _isLoaded = false; } - + auto texturesValue = properties["textures"]; if (texturesValue.isValid() && texturesValue.canConvert(QVariant::Map)) { QVariantMap textureMap = texturesValue.toMap(); foreach(const QString& key, textureMap.keys()) { - + QUrl newTextureURL = textureMap[key].toUrl(); qDebug() << "Updating texture named" << key << "to texture at URL" << newTextureURL; - + QMetaObject::invokeMethod(_model.get(), "setTextureWithNameToURL", Qt::AutoConnection, Q_ARG(const QString&, key), Q_ARG(const QUrl&, newTextureURL)); @@ -123,8 +143,11 @@ QVariant ModelOverlay::getProperty(const QString& property) { if (property == "url") { return _url.toString(); } - if (property == "dimensions" || property == "scale" || property == "size") { - return vec3toVariant(_model->getScaleToFitDimensions()); + if (property == "dimensions" || property == "size") { + return vec3toVariant(getDimensions()); + } + if (property == "scale") { + return vec3toVariant(getScale()); } if (property == "textures") { if (_modelTextures.size() > 0) { @@ -143,14 +166,14 @@ QVariant ModelOverlay::getProperty(const QString& property) { bool ModelOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal) { - + QString subMeshNameTemp; return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, subMeshNameTemp); } bool ModelOverlay::findRayIntersectionExtraInfo(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal, QString& extraInfo) { - + return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, surfaceNormal, extraInfo); } diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index dc4b4a853b..091cab44c9 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -43,9 +43,10 @@ private: ModelPointer _model; QVariantMap _modelTextures; - + QUrl _url; - bool _updateModel; + bool _updateModel = { false }; + bool _scaleToFit = { false }; }; #endif // hifi_ModelOverlay_h diff --git a/interface/src/ui/overlays/Volume3DOverlay.cpp b/interface/src/ui/overlays/Volume3DOverlay.cpp index c8078d35c6..563198c976 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.cpp +++ b/interface/src/ui/overlays/Volume3DOverlay.cpp @@ -22,7 +22,7 @@ AABox Volume3DOverlay::getBounds() const { auto extents = Extents{_localBoundingBox}; extents.rotate(getRotation()); extents.shiftBy(getPosition()); - + return AABox(extents); } @@ -31,7 +31,7 @@ void Volume3DOverlay::setProperties(const QVariantMap& properties) { auto dimensions = properties["dimensions"]; - // if "dimensions" property was not there, check to see if they included aliases: scale + // if "dimensions" property was not there, check to see if they included aliases: scale, size if (!dimensions.isValid()) { dimensions = properties["scale"]; if (!dimensions.isValid()) { @@ -57,7 +57,7 @@ bool Volume3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::ve // extents is the entity relative, scaled, centered extents of the entity glm::mat4 worldToEntityMatrix; _transform.getInverseMatrix(worldToEntityMatrix); - + glm::vec3 overlayFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f)); glm::vec3 overlayFrameDirection = glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f)); diff --git a/interface/src/ui/overlays/Volume3DOverlay.h b/interface/src/ui/overlays/Volume3DOverlay.h index 4d087615d2..04b694b2f8 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.h +++ b/interface/src/ui/overlays/Volume3DOverlay.h @@ -15,13 +15,13 @@ class Volume3DOverlay : public Base3DOverlay { Q_OBJECT - + public: Volume3DOverlay() {} Volume3DOverlay(const Volume3DOverlay* volume3DOverlay); - + virtual AABox getBounds() const override; - + const glm::vec3& getDimensions() const { return _localBoundingBox.getDimensions(); } void setDimensions(float value) { _localBoundingBox.setBox(glm::vec3(-value / 2.0f), value); } void setDimensions(const glm::vec3& value) { _localBoundingBox.setBox(-value / 2.0f, value); } @@ -29,13 +29,13 @@ public: void setProperties(const QVariantMap& properties) override; QVariant getProperty(const QString& property) override; - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, glm::vec3& surfaceNormal) override; - + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, + BoxFace& face, glm::vec3& surfaceNormal) override; + protected: // Centered local bounding box AABox _localBoundingBox{ vec3(0.0f), 1.0f }; }; - + #endif // hifi_Volume3DOverlay_h From 789297d8496cbb9228d12123ffc691f3ae70ab02 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 13 Jul 2016 17:39:39 -0700 Subject: [PATCH 17/25] Remove redundant isValid check. --- interface/src/ui/overlays/ModelOverlay.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 2897c07e64..9c203c0129 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -106,9 +106,7 @@ void ModelOverlay::setProperties(const QVariantMap& properties) { } else if (scale.isValid()) { // if "scale" property is set but "dimentions" is not. // do NOT scale to fit. - if (scale.isValid()) { - _scaleToFit = false; - } + _scaleToFit = false; } if (origPosition != getPosition() || origRotation != getRotation() || origDimensions != getDimensions() || origScale != getScale()) { From c2fd055f0f12992cdd5d64cddf97a610d21badcb Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 13 Jul 2016 17:23:00 -0700 Subject: [PATCH 18/25] more hacking --- cmake/macros/GenerateInstallers.cmake | 26 ------------------- cmake/macros/InstallBesideConsole.cmake | 1 + cmake/macros/SetPackagingParameters.cmake | 2 ++ .../macros/SetupHifiClientServerPlugin.cmake | 1 - plugins/hifiCodec/CMakeLists.txt | 1 + plugins/pcmCodec/CMakeLists.txt | 2 ++ server-console/CMakeLists.txt | 26 ------------------- 7 files changed, 6 insertions(+), 53 deletions(-) diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake index 2d2ede69f0..0def701739 100644 --- a/cmake/macros/GenerateInstallers.cmake +++ b/cmake/macros/GenerateInstallers.cmake @@ -78,32 +78,6 @@ macro(GENERATE_INSTALLERS) install(CODE "execute_process(COMMAND SetFile -a V \${CMAKE_INSTALL_PREFIX}/${ESCAPED_DMG_SUBFOLDER_NAME}/Icon\\r)") endif () - # this is a bit of a hack, but I couldn't find any other way to get plugins - # into the correct place under the console path. On windows this is handled - # exclusively in the plugins directories with the SetupHiFiClientServerPlugin - # macro - if (APPLE) - set(CONSOLE_PLUGINS_DIR "${CONSOLE_INSTALL_APP_PATH}/Contents/MacOS/Components.app/Contents/PlugIns") - set(SERVER_PLUGINS_DIR "${CMAKE_BINARY_DIR}/assignment-client/${CMAKE_BUILD_TYPE}/plugins") - - message("TARGET_NAME: ${TARGET_NAME}") - message("CONFIGURATION: ${CONFIGURATION}") - message(": $") - message("CONSOLE_PLUGINS_DIR: ${CONSOLE_PLUGINS_DIR}") - message("SERVER_PLUGINS_DIR: ${SERVER_PLUGINS_DIR}") - - add_custom_command(TARGET ${TARGET_NAME} POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E make_directory - ${CONSOLE_PLUGINS_DIR} - ) - - add_custom_command(TARGET ${TARGET_NAME} POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy_directory - ${SERVER_PLUGINS_DIR} - ${CONSOLE_PLUGINS_DIR} - ) - endif () - # configure a cpack properties file for custom variables in template set(CPACK_CONFIGURED_PROP_FILE "${CMAKE_CURRENT_BINARY_DIR}/CPackCustomProperties.cmake") configure_file("${HF_CMAKE_DIR}/templates/CPackProperties.cmake.in" ${CPACK_CONFIGURED_PROP_FILE}) diff --git a/cmake/macros/InstallBesideConsole.cmake b/cmake/macros/InstallBesideConsole.cmake index 318a7a3ffe..0eb6025f38 100644 --- a/cmake/macros/InstallBesideConsole.cmake +++ b/cmake/macros/InstallBesideConsole.cmake @@ -16,6 +16,7 @@ macro(install_beside_console) install( TARGETS ${TARGET_NAME} RUNTIME DESTINATION ${COMPONENT_INSTALL_DIR} + LIBRARY DESTINATION ${CONSOLE_PLUGIN_INSTALL_DIR} COMPONENT ${SERVER_COMPONENT} ) else () diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index d8532aa081..76f6812921 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -69,6 +69,8 @@ macro(SET_PACKAGING_PARAMETERS) set(CONSOLE_APP_CONTENTS "${CONSOLE_INSTALL_APP_PATH}/Contents") set(COMPONENT_APP_PATH "${CONSOLE_APP_CONTENTS}/MacOS/Components.app") set(COMPONENT_INSTALL_DIR "${COMPONENT_APP_PATH}/Contents/MacOS") + set(CONSOLE_PLUGIN_INSTALL_DIR "${COMPONENT_APP_PATH}/Contents/PlugIns") + set(INTERFACE_INSTALL_APP_PATH "${CONSOLE_INSTALL_DIR}/${INTERFACE_BUNDLE_NAME}.app") set(INTERFACE_ICON_FILENAME "${INTERFACE_ICON_PREFIX}.icns") diff --git a/cmake/macros/SetupHifiClientServerPlugin.cmake b/cmake/macros/SetupHifiClientServerPlugin.cmake index a065ee99b6..8ba38a09c3 100644 --- a/cmake/macros/SetupHifiClientServerPlugin.cmake +++ b/cmake/macros/SetupHifiClientServerPlugin.cmake @@ -13,7 +13,6 @@ macro(SETUP_HIFI_CLIENT_SERVER_PLUGIN) if (APPLE) set(CLIENT_PLUGIN_PATH "${INTERFACE_BUNDLE_NAME}.app/Contents/PlugIns") - #set(SERVER_PLUGIN_PATH "Components.app/Contents/PlugIns") set(SERVER_PLUGIN_PATH "plugins") else() set(CLIENT_PLUGIN_PATH "plugins") diff --git a/plugins/hifiCodec/CMakeLists.txt b/plugins/hifiCodec/CMakeLists.txt index 3939529c3e..0af7e42ea1 100644 --- a/plugins/hifiCodec/CMakeLists.txt +++ b/plugins/hifiCodec/CMakeLists.txt @@ -15,5 +15,6 @@ if (WIN32 OR APPLE) add_dependency_external_projects(HiFiAudioCodec) target_include_directories(${TARGET_NAME} PRIVATE ${HIFIAUDIOCODEC_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${HIFIAUDIOCODEC_LIBRARIES}) + install_beside_console() endif() diff --git a/plugins/pcmCodec/CMakeLists.txt b/plugins/pcmCodec/CMakeLists.txt index 5dca1f0e14..900a642a88 100644 --- a/plugins/pcmCodec/CMakeLists.txt +++ b/plugins/pcmCodec/CMakeLists.txt @@ -9,3 +9,5 @@ set(TARGET_NAME pcmCodec) setup_hifi_client_server_plugin() link_hifi_libraries(shared plugins) +install_beside_console() + diff --git a/server-console/CMakeLists.txt b/server-console/CMakeLists.txt index 7b9c17ace4..1c6e40c582 100644 --- a/server-console/CMakeLists.txt +++ b/server-console/CMakeLists.txt @@ -72,29 +72,3 @@ else () set_target_properties(${TARGET_NAME} PROPERTIES EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_DEFAULT_BUILD TRUE) set_target_properties(${TARGET_NAME}-npm-install PROPERTIES EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_DEFAULT_BUILD TRUE) endif () - -# this is a bit of a hack, but I couldn't find any other way to get plugins -# into the correct place under the console path. On windows this is handled -# exclusively in the plugins directories with the SetupHiFiClientServerPlugin -# macro -if (APPLE) - set(CONSOLE_PLUGINS_DIR "${CONSOLE_INSTALL_APP_PATH}/Contents/MacOS/Components.app/Contents/PlugIns") - set(SERVER_PLUGINS_DIR "${CMAKE_BINARY_DIR}/assignment-client/${CMAKE_BUILD_TYPE}/plugins") - - message("TARGET_NAME: ${TARGET_NAME}") - message("CONFIGURATION: ${CONFIGURATION}") - message(": $") - message("CONSOLE_PLUGINS_DIR: ${CONSOLE_PLUGINS_DIR}") - message("SERVER_PLUGINS_DIR: ${SERVER_PLUGINS_DIR}") - - add_custom_command(TARGET ${TARGET_NAME} POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E make_directory - ${CONSOLE_PLUGINS_DIR} - ) - - add_custom_command(TARGET ${TARGET_NAME} POST_BUILD - COMMAND "${CMAKE_COMMAND}" -E copy_directory - ${SERVER_PLUGINS_DIR} - ${CONSOLE_PLUGINS_DIR} - ) -endif () From d7399f5781b30db31796f1d9e475b60df2fec03f Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 14 Jul 2016 10:34:42 -0700 Subject: [PATCH 19/25] Improved HRTF. Adds a distance filter to model the frequency-dependent attenuation of sound propagation in open air. Optimized using SIMD and computing all biquads in parallel. Performance impact is almost zero. Filter updates are continuously interpolated and clean to -90dB. Not enabled yet (distance hardcoded to 0.0f) --- assignment-client/src/audio/AudioMixer.cpp | 8 +- libraries/audio-client/src/AudioClient.cpp | 2 +- libraries/audio/src/AudioHRTF.cpp | 353 +++++++++++++++++---- libraries/audio/src/AudioHRTF.h | 18 +- 4 files changed, 308 insertions(+), 73 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 7ba9242306..24893ad1b6 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -240,7 +240,7 @@ void AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData& // this is not done for stereo streams since they do not go through the HRTF static int16_t silentMonoBlock[AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL] = {}; - hrtf.renderSilent(silentMonoBlock, _mixedSamples, HRTF_DATASET_INDEX, azimuth, gain, + hrtf.renderSilent(silentMonoBlock, _mixedSamples, HRTF_DATASET_INDEX, azimuth, 0.0f, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); ++_hrtfSilentRenders;; @@ -287,7 +287,7 @@ void AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData& // silent frame from source // we still need to call renderSilent via the HRTF for mono source - hrtf.renderSilent(streamBlock, _mixedSamples, HRTF_DATASET_INDEX, azimuth, gain, + hrtf.renderSilent(streamBlock, _mixedSamples, HRTF_DATASET_INDEX, azimuth, 0.0f, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); ++_hrtfSilentRenders; @@ -300,7 +300,7 @@ void AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData& // the mixer is struggling so we're going to drop off some streams // we call renderSilent via the HRTF with the actual frame data and a gain of 0.0 - hrtf.renderSilent(streamBlock, _mixedSamples, HRTF_DATASET_INDEX, azimuth, 0.0f, + hrtf.renderSilent(streamBlock, _mixedSamples, HRTF_DATASET_INDEX, azimuth, 0.0f, 0.0f, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); ++_hrtfStruggleRenders; @@ -311,7 +311,7 @@ void AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData& ++_hrtfRenders; // mono stream, call the HRTF with our block and calculated azimuth and gain - hrtf.render(streamBlock, _mixedSamples, HRTF_DATASET_INDEX, azimuth, gain, + hrtf.render(streamBlock, _mixedSamples, HRTF_DATASET_INDEX, azimuth, 0.0f, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); } diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index dd72125d93..bc29ba5c96 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -888,7 +888,7 @@ void AudioClient::mixLocalAudioInjectors(int16_t* inputBuffer) { float azimuth = azimuthForSource(relativePosition); - injector->getLocalHRTF().render(_scratchBuffer, _hrtfBuffer, 1, azimuth, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); + injector->getLocalHRTF().render(_scratchBuffer, _hrtfBuffer, 1, azimuth, 0.0f, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); } } else { diff --git a/libraries/audio/src/AudioHRTF.cpp b/libraries/audio/src/AudioHRTF.cpp index e7cf4436c6..3a193d786a 100644 --- a/libraries/audio/src/AudioHRTF.cpp +++ b/libraries/audio/src/AudioHRTF.cpp @@ -162,40 +162,68 @@ static void interleave_4x4(float* src0, float* src1, float* src2, float* src3, f } } -// 4 channels (interleaved) -static void biquad_4x4(float* src, float* dst, float coef[5][4], float state[2][4], int numFrames) { +// process 2 cascaded biquads on 4 channels (interleaved) +// biquads computed in parallel, by adding one sample of delay +static void biquad2_4x4(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames) { // enable flush-to-zero mode to prevent denormals unsigned int ftz = _MM_GET_FLUSH_ZERO_MODE(); _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); - __m128 w1 = _mm_loadu_ps(state[0]); - __m128 w2 = _mm_loadu_ps(state[1]); + // restore state + __m128 y00 = _mm_loadu_ps(&state[0][0]); + __m128 w10 = _mm_loadu_ps(&state[1][0]); + __m128 w20 = _mm_loadu_ps(&state[2][0]); - __m128 b0 = _mm_loadu_ps(coef[0]); - __m128 b1 = _mm_loadu_ps(coef[1]); - __m128 b2 = _mm_loadu_ps(coef[2]); - __m128 a1 = _mm_loadu_ps(coef[3]); - __m128 a2 = _mm_loadu_ps(coef[4]); + __m128 y01; + __m128 w11 = _mm_loadu_ps(&state[1][4]); + __m128 w21 = _mm_loadu_ps(&state[2][4]); + + // first biquad coefs + __m128 b00 = _mm_loadu_ps(&coef[0][0]); + __m128 b10 = _mm_loadu_ps(&coef[1][0]); + __m128 b20 = _mm_loadu_ps(&coef[2][0]); + __m128 a10 = _mm_loadu_ps(&coef[3][0]); + __m128 a20 = _mm_loadu_ps(&coef[4][0]); + + // second biquad coefs + __m128 b01 = _mm_loadu_ps(&coef[0][4]); + __m128 b11 = _mm_loadu_ps(&coef[1][4]); + __m128 b21 = _mm_loadu_ps(&coef[2][4]); + __m128 a11 = _mm_loadu_ps(&coef[3][4]); + __m128 a21 = _mm_loadu_ps(&coef[4][4]); for (int i = 0; i < numFrames; i++) { + __m128 x00 = _mm_loadu_ps(&src[4*i]); + __m128 x01 = y00; // first biquad output + // transposed Direct Form II - __m128 x0 = _mm_loadu_ps(&src[4*i]); - __m128 y0; + y00 = _mm_add_ps(w10, _mm_mul_ps(x00, b00)); + y01 = _mm_add_ps(w11, _mm_mul_ps(x01, b01)); - y0 = _mm_add_ps(w1, _mm_mul_ps(x0, b0)); - w1 = _mm_add_ps(w2, _mm_mul_ps(x0, b1)); - w2 = _mm_mul_ps(x0, b2); - w1 = _mm_sub_ps(w1, _mm_mul_ps(y0, a1)); - w2 = _mm_sub_ps(w2, _mm_mul_ps(y0, a2)); + w10 = _mm_add_ps(w20, _mm_mul_ps(x00, b10)); + w11 = _mm_add_ps(w21, _mm_mul_ps(x01, b11)); - _mm_storeu_ps(&dst[4*i], y0); + w20 = _mm_mul_ps(x00, b20); + w21 = _mm_mul_ps(x01, b21); + + w10 = _mm_sub_ps(w10, _mm_mul_ps(y00, a10)); + w11 = _mm_sub_ps(w11, _mm_mul_ps(y01, a11)); + + w20 = _mm_sub_ps(w20, _mm_mul_ps(y00, a20)); + w21 = _mm_sub_ps(w21, _mm_mul_ps(y01, a21)); + + _mm_storeu_ps(&dst[4*i], y01); // second biquad output } // save state - _mm_storeu_ps(state[0], w1); - _mm_storeu_ps(state[1], w2); + _mm_storeu_ps(&state[0][0], y00); + _mm_storeu_ps(&state[1][0], w10); + _mm_storeu_ps(&state[2][0], w20); + + _mm_storeu_ps(&state[1][4], w11); + _mm_storeu_ps(&state[2][4], w21); _MM_SET_FLUSH_ZERO_MODE(ftz); } @@ -345,56 +373,105 @@ static void interleave_4x4(float* src0, float* src1, float* src2, float* src3, f } } -// 4 channels (interleaved) -static void biquad_4x4(float* src, float* dst, float coef[5][4], float state[2][4], int numFrames) { +// process 2 cascaded biquads on 4 channels (interleaved) +// biquads are computed in parallel, by adding one sample of delay +static void biquad2_4x4(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames) { - // channel 0 - float w10 = state[0][0]; - float w20 = state[1][0]; + // restore state + float y00 = state[0][0]; + float w10 = state[1][0]; + float w20 = state[2][0]; + float y01 = state[0][1]; + float w11 = state[1][1]; + float w21 = state[2][1]; + + float y02 = state[0][2]; + float w12 = state[1][2]; + float w22 = state[2][2]; + + float y03 = state[0][3]; + float w13 = state[1][3]; + float w23 = state[2][3]; + + float y04; + float w14 = state[1][4]; + float w24 = state[2][4]; + + float y05; + float w15 = state[1][5]; + float w25 = state[2][5]; + + float y06; + float w16 = state[1][6]; + float w26 = state[2][6]; + + float y07; + float w17 = state[1][7]; + float w27 = state[2][7]; + + // first biquad coefs float b00 = coef[0][0]; float b10 = coef[1][0]; float b20 = coef[2][0]; float a10 = coef[3][0]; float a20 = coef[4][0]; - // channel 1 - float w11 = state[0][1]; - float w21 = state[1][1]; - float b01 = coef[0][1]; float b11 = coef[1][1]; float b21 = coef[2][1]; float a11 = coef[3][1]; float a21 = coef[4][1]; - // channel 2 - float w12 = state[0][2]; - float w22 = state[1][2]; - float b02 = coef[0][2]; float b12 = coef[1][2]; float b22 = coef[2][2]; float a12 = coef[3][2]; float a22 = coef[4][2]; - // channel 3 - float w13 = state[0][3]; - float w23 = state[1][3]; - float b03 = coef[0][3]; float b13 = coef[1][3]; float b23 = coef[2][3]; float a13 = coef[3][3]; float a23 = coef[4][3]; + // second biquad coefs + float b04 = coef[0][4]; + float b14 = coef[1][4]; + float b24 = coef[2][4]; + float a14 = coef[3][4]; + float a24 = coef[4][4]; + + float b05 = coef[0][5]; + float b15 = coef[1][5]; + float b25 = coef[2][5]; + float a15 = coef[3][5]; + float a25 = coef[4][5]; + + float b06 = coef[0][6]; + float b16 = coef[1][6]; + float b26 = coef[2][6]; + float a16 = coef[3][6]; + float a26 = coef[4][6]; + + float b07 = coef[0][7]; + float b17 = coef[1][7]; + float b27 = coef[2][7]; + float a17 = coef[3][7]; + float a27 = coef[4][7]; + for (int i = 0; i < numFrames; i++) { + // first biquad input float x00 = src[4*i+0] + 1.0e-20f; // prevent denormals float x01 = src[4*i+1] + 1.0e-20f; float x02 = src[4*i+2] + 1.0e-20f; float x03 = src[4*i+3] + 1.0e-20f; - float y00, y01, y02, y03; + // second biquad input is previous output + float x04 = y00; + float x05 = y01; + float x06 = y02; + float x07 = y03; // transposed Direct Form II y00 = b00 * x00 + w10; @@ -413,24 +490,57 @@ static void biquad_4x4(float* src, float* dst, float coef[5][4], float state[2][ w13 = b13 * x03 - a13 * y03 + w23; w23 = b23 * x03 - a23 * y03; - dst[4*i+0] = y00; - dst[4*i+1] = y01; - dst[4*i+2] = y02; - dst[4*i+3] = y03; + // transposed Direct Form II + y04 = b04 * x04 + w14; + w14 = b14 * x04 - a14 * y04 + w24; + w24 = b24 * x04 - a24 * y04; + + y05 = b05 * x05 + w15; + w15 = b15 * x05 - a15 * y05 + w25; + w25 = b25 * x05 - a25 * y05; + + y06 = b06 * x06 + w16; + w16 = b16 * x06 - a16 * y06 + w26; + w26 = b26 * x06 - a26 * y06; + + y07 = b07 * x07 + w17; + w17 = b17 * x07 - a17 * y07 + w27; + w27 = b27 * x07 - a27 * y07; + + dst[4*i+0] = y04; // second biquad output + dst[4*i+1] = y05; + dst[4*i+2] = y06; + dst[4*i+3] = y07; } // save state - state[0][0] = w10; - state[1][0] = w20; + state[0][0] = y00; + state[1][0] = w10; + state[2][0] = w20; - state[0][1] = w11; - state[1][1] = w21; + state[0][1] = y01; + state[1][1] = w11; + state[2][1] = w21; - state[0][2] = w12; - state[1][2] = w22; + state[0][2] = y02; + state[1][2] = w12; + state[2][2] = w22; - state[0][3] = w13; - state[1][3] = w23; + state[0][3] = y03; + state[1][3] = w13; + state[2][3] = w23; + + state[1][4] = w14; + state[2][4] = w24; + + state[1][5] = w15; + state[2][5] = w25; + + state[1][6] = w16; + state[2][6] = w26; + + state[1][7] = w17; + state[2][7] = w27; } // crossfade 4 inputs into 2 outputs with accumulation (interleaved) @@ -468,9 +578,85 @@ static void ThiranBiquad(float f, float& b0, float& b1, float& b2, float& a1, fl b2 = 1.0f; } -// compute new filters for a given azimuth and gain -static void setAzimuthAndGain(float firCoef[4][HRTF_TAPS], float bqCoef[5][4], int delay[4], - int index, float azimuth, float gain, int channel) { +// returns the gain of analog (s-plane) lowpass evaluated at w +static double analogFilter(double w0, double w) { + double w0sq, wsq; + double num, den; + + w0sq = w0 * w0; + wsq = w * w; + + num = w0sq * w0sq; + den = wsq * wsq + w0sq * w0sq; + + return sqrt(num / den); +} + +// design a lowpass biquad using analog matching +static void LowpassBiquad(double coef[5], double w0) { + double G1; + double wpi, wn, wd; + double wna, wda; + double gn, gd, gnsq, gdsq; + double num, den; + double Wnsq, Wdsq, B, A; + double b0, b1, b2, a0, a1, a2; + double temp, scale; + const double PI = 3.14159265358979323846; + + // compute the Nyquist gain + wpi = w0 + 2.8 * (1.0 - w0/PI); // minimax-like error + wpi = (wpi > PI) ? PI : wpi; + G1 = analogFilter(w0, wpi); + + // approximate wn and wd + wd = 0.5 * w0; + wn = wd * sqrt(1.0/G1); // down G1 at pi, instead of zeros + + Wnsq = wn * wn; + Wdsq = wd * wd; + + // analog freqs of wn and wd + wna = 2.0 * atan(wn); + wda = 2.0 * atan(wd); + + // normalized analog gains at wna and wda + temp = 1.0 / G1; + gn = temp * analogFilter(w0, wna); + gd = temp * analogFilter(w0, wda); + gnsq = gn * gn; + gdsq = gd * gd; + + // compute B, matching gains at wn and wd + temp = 1.0 / (wn * wd); + den = fabs(gnsq - gdsq); + num = gnsq * (Wnsq - Wdsq) * (Wnsq - Wdsq) * (Wnsq + gdsq * Wdsq); + B = temp * sqrt(num / den); + + // compute A, matching gains at wn and wd + num = (Wnsq - Wdsq) * (Wnsq - Wdsq) * (Wnsq + gnsq * Wdsq); + A = temp * sqrt(num / den); + + // design digital filter via bilinear transform + b0 = G1 * (1.0 + B + Wnsq); + b1 = G1 * 2.0 * (Wnsq - 1.0); + b2 = G1 * (1.0 - B + Wnsq); + a0 = 1.0 + A + Wdsq; + a1 = 2.0 * (Wdsq - 1.0); + a2 = 1.0 - A + Wdsq; + + // normalize + scale = 1.0 / a0; + coef[0] = b0 * scale; + coef[1] = b1 * scale; + coef[2] = b2 * scale; + coef[3] = a1 * scale; + coef[4] = a2 * scale; +} + +// compute new filters for a given azimuth, distance and gain +static void setFilters(float firCoef[4][HRTF_TAPS], float bqCoef[5][8], int delay[4], + int index, float azimuth, float distance, float gain, int channel) { // convert from radians to table units azimuth *= HRTF_AZIMUTHS / TWOPI; @@ -551,9 +737,43 @@ static void setAzimuthAndGain(float firCoef[4][HRTF_TAPS], float bqCoef[5][4], i bqCoef[4][channel+1] = a2; delay[channel+1] = itdi; } + + // + // Model the frequency-dependent attenuation of sound propogation in air. + // Fit using linear regression to a log-log model of lowpass cutoff frequency vs distance, + // loosely based on data from Handbook of Acoustics. Only the onset of significant + // attenuation is modelled, not the filter slope. + // + // 1m -> -3dB @ 55kHz + // 10m -> -3dB @ 12kHz + // 100m -> -3dB @ 2.5kHz + // 1km -> -3dB @ 0.6kHz + // 10km -> -3dB @ 0.1kHz + // + distance = (distance < 1.0f) ? 1.0f : distance; + double freq = exp2(-0.666 * log2(distance) + 15.75); + double coef[5]; + LowpassBiquad(coef, TWOPI * freq / 24000); + + // TESTING: compute attn at w=pi + //double num = coef[0] - coef[1] + coef[2]; + //double den = 1.0 - coef[3] + coef[4]; + //double mag = 10 * log10((num * num) / (den * den)); + + bqCoef[0][channel+4] = (float)coef[0]; + bqCoef[1][channel+4] = (float)coef[1]; + bqCoef[2][channel+4] = (float)coef[2]; + bqCoef[3][channel+4] = (float)coef[3]; + bqCoef[4][channel+4] = (float)coef[4]; + + bqCoef[0][channel+5] = (float)coef[0]; + bqCoef[1][channel+5] = (float)coef[1]; + bqCoef[2][channel+5] = (float)coef[2]; + bqCoef[3][channel+5] = (float)coef[3]; + bqCoef[4][channel+5] = (float)coef[4]; } -void AudioHRTF::render(int16_t* input, float* output, int index, float azimuth, float gain, int numFrames) { +void AudioHRTF::render(int16_t* input, float* output, int index, float azimuth, float distance, float gain, int numFrames) { assert(index >= 0); assert(index < HRTF_TABLES); @@ -562,18 +782,19 @@ void AudioHRTF::render(int16_t* input, float* output, int index, float azimuth, float in[HRTF_TAPS + HRTF_BLOCK]; // mono float firCoef[4][HRTF_TAPS]; // 4-channel float firBuffer[4][HRTF_DELAY + HRTF_BLOCK]; // 4-channel - float bqCoef[5][4]; // 4-channel (interleaved) + float bqCoef[5][8]; // 4-channel (interleaved) float bqBuffer[4 * HRTF_BLOCK]; // 4-channel (interleaved) int delay[4]; // 4-channel (interleaved) // to avoid polluting the cache, old filters are recomputed instead of stored - setAzimuthAndGain(firCoef, bqCoef, delay, index, _azimuthState, _gainState, L0); + setFilters(firCoef, bqCoef, delay, index, _azimuthState, _distanceState, _gainState, L0); // compute new filters - setAzimuthAndGain(firCoef, bqCoef, delay, index, azimuth, gain, L1); + setFilters(firCoef, bqCoef, delay, index, azimuth, distance, gain, L1); // new parameters become old _azimuthState = azimuth; + _distanceState = distance; _gainState = gain; // convert mono input to float @@ -611,14 +832,25 @@ void AudioHRTF::render(int16_t* input, float* output, int index, float azimuth, &firBuffer[R1][HRTF_DELAY] - delay[R1], bqBuffer, HRTF_BLOCK); - // process old/new fractional delay - biquad_4x4(bqBuffer, bqBuffer, bqCoef, _bqState, HRTF_BLOCK); + // process old/new biquads + biquad2_4x4(bqBuffer, bqBuffer, bqCoef, _bqState, HRTF_BLOCK); // new state becomes old _bqState[0][L0] = _bqState[0][L1]; _bqState[1][L0] = _bqState[1][L1]; + _bqState[2][L0] = _bqState[2][L1]; + _bqState[0][R0] = _bqState[0][R1]; _bqState[1][R0] = _bqState[1][R1]; + _bqState[2][R0] = _bqState[2][R1]; + + _bqState[0][L2] = _bqState[0][L3]; + _bqState[1][L2] = _bqState[1][L3]; + _bqState[2][L2] = _bqState[2][L3]; + + _bqState[0][R2] = _bqState[0][R3]; + _bqState[1][R2] = _bqState[1][R3]; + _bqState[2][R2] = _bqState[2][R3]; // crossfade old/new output and accumulate crossfade_4x2(bqBuffer, output, crossfadeTable, HRTF_BLOCK); @@ -626,15 +858,16 @@ void AudioHRTF::render(int16_t* input, float* output, int index, float azimuth, _silentState = false; } -void AudioHRTF::renderSilent(int16_t* input, float* output, int index, float azimuth, float gain, int numFrames) { +void AudioHRTF::renderSilent(int16_t* input, float* output, int index, float azimuth, float distance, float gain, int numFrames) { // process the first silent block, to flush internal state if (!_silentState) { - render(input, output, index, azimuth, gain, numFrames); + render(input, output, index, azimuth, distance, gain, numFrames); } // new parameters become old _azimuthState = azimuth; + _distanceState = distance; _gainState = gain; _silentState = true; diff --git a/libraries/audio/src/AudioHRTF.h b/libraries/audio/src/AudioHRTF.h index d3b6237d0c..111409f80c 100644 --- a/libraries/audio/src/AudioHRTF.h +++ b/libraries/audio/src/AudioHRTF.h @@ -33,15 +33,16 @@ public: // output: interleaved stereo mix buffer (accumulates into existing output) // index: HRTF subject index // azimuth: clockwise panning angle in radians + // distance: source distance in meters // gain: gain factor for distance attenuation // numFrames: must be HRTF_BLOCK in this version // - void render(int16_t* input, float* output, int index, float azimuth, float gain, int numFrames); + void render(int16_t* input, float* output, int index, float azimuth, float distance, float gain, int numFrames); // // Fast path when input is known to be silent // - void renderSilent(int16_t* input, float* output, int index, float azimuth, float gain, int numFrames); + void renderSilent(int16_t* input, float* output, int index, float azimuth, float distance, float gain, int numFrames); private: AudioHRTF(const AudioHRTF&) = delete; @@ -49,10 +50,10 @@ private: // SIMD channel assignmentS enum Channel { - L0, - R0, - L1, - R1 + L0, R0, + L1, R1, + L2, R2, + L3, R3 }; // For best cache utilization when processing thousands of instances, only @@ -64,11 +65,12 @@ private: // integer delay history float _delayState[4][HRTF_DELAY] = {}; - // fractional delay history - float _bqState[2][4] = {}; + // biquad history + float _bqState[3][8] = {}; // parameter history float _azimuthState = 0.0f; + float _distanceState = 0.0f; float _gainState = 0.0f; bool _silentState = false; From c7c02d7a59b70c2f85f183a0fff535426d2fc4e4 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 14 Jul 2016 11:24:48 -0700 Subject: [PATCH 20/25] Fix compiler warning --- libraries/audio/src/AudioHRTF.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioHRTF.cpp b/libraries/audio/src/AudioHRTF.cpp index 3a193d786a..658af01408 100644 --- a/libraries/audio/src/AudioHRTF.cpp +++ b/libraries/audio/src/AudioHRTF.cpp @@ -753,7 +753,7 @@ static void setFilters(float firCoef[4][HRTF_TAPS], float bqCoef[5][8], int dela distance = (distance < 1.0f) ? 1.0f : distance; double freq = exp2(-0.666 * log2(distance) + 15.75); double coef[5]; - LowpassBiquad(coef, TWOPI * freq / 24000); + LowpassBiquad(coef, (double)TWOPI * freq / 24000); // TESTING: compute attn at w=pi //double num = coef[0] - coef[1] + coef[2]; From 7a4bdc1779affed20fc0111689211bb2d5b23062 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 14 Jul 2016 11:30:55 -0700 Subject: [PATCH 21/25] enable in Interface and AudioMixer, by passing distance between source and listener --- assignment-client/src/audio/AudioMixer.cpp | 14 +++++++++----- libraries/audio-client/src/AudioClient.cpp | 8 ++++---- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 24893ad1b6..08e6e029a0 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -193,8 +193,12 @@ void AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData& // check if this is a server echo of a source back to itself bool isEcho = (&streamToAdd == &listeningNodeStream); - // figure out the gain for this source at the listener glm::vec3 relativePosition = streamToAdd.getPosition() - listeningNodeStream.getPosition(); + + // figure out the distance between source and listener + float distance = glm::max(glm::length(relativePosition), EPSILON); + + // figure out the gain for this source at the listener float gain = gainForSource(streamToAdd, listeningNodeStream, relativePosition, isEcho); // figure out the azimuth to this source at the listener @@ -240,7 +244,7 @@ void AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData& // this is not done for stereo streams since they do not go through the HRTF static int16_t silentMonoBlock[AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL] = {}; - hrtf.renderSilent(silentMonoBlock, _mixedSamples, HRTF_DATASET_INDEX, azimuth, 0.0f, gain, + hrtf.renderSilent(silentMonoBlock, _mixedSamples, HRTF_DATASET_INDEX, azimuth, distance, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); ++_hrtfSilentRenders;; @@ -287,7 +291,7 @@ void AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData& // silent frame from source // we still need to call renderSilent via the HRTF for mono source - hrtf.renderSilent(streamBlock, _mixedSamples, HRTF_DATASET_INDEX, azimuth, 0.0f, gain, + hrtf.renderSilent(streamBlock, _mixedSamples, HRTF_DATASET_INDEX, azimuth, distance, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); ++_hrtfSilentRenders; @@ -300,7 +304,7 @@ void AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData& // the mixer is struggling so we're going to drop off some streams // we call renderSilent via the HRTF with the actual frame data and a gain of 0.0 - hrtf.renderSilent(streamBlock, _mixedSamples, HRTF_DATASET_INDEX, azimuth, 0.0f, 0.0f, + hrtf.renderSilent(streamBlock, _mixedSamples, HRTF_DATASET_INDEX, azimuth, distance, 0.0f, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); ++_hrtfStruggleRenders; @@ -311,7 +315,7 @@ void AudioMixer::addStreamToMixForListeningNodeWithStream(AudioMixerClientData& ++_hrtfRenders; // mono stream, call the HRTF with our block and calculated azimuth and gain - hrtf.render(streamBlock, _mixedSamples, HRTF_DATASET_INDEX, azimuth, 0.0f, gain, + hrtf.render(streamBlock, _mixedSamples, HRTF_DATASET_INDEX, azimuth, distance, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); } diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index bc29ba5c96..50039ba668 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -882,13 +882,13 @@ void AudioClient::mixLocalAudioInjectors(int16_t* inputBuffer) { } else { - // calculate gain and azimuth for hrtf + // calculate distance, gain and azimuth for hrtf glm::vec3 relativePosition = injector->getPosition() - _positionGetter(); + float distance = glm::max(glm::length(relativePosition), EPSILON); float gain = gainForSource(relativePosition, injector->getVolume()); - float azimuth = azimuthForSource(relativePosition); + float azimuth = azimuthForSource(relativePosition); - - injector->getLocalHRTF().render(_scratchBuffer, _hrtfBuffer, 1, azimuth, 0.0f, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); + injector->getLocalHRTF().render(_scratchBuffer, _hrtfBuffer, 1, azimuth, distance, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); } } else { From fa55fc84f5942c4e6b4dcac523c7f28d816ad22e Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 14 Jul 2016 12:04:05 -0700 Subject: [PATCH 22/25] Optimized compute of distance filters using log-quantized lookup tables. Magnitude error < 0.25dB for entire parameter space. --- libraries/audio/src/AudioHRTF.cpp | 253 +++++++++++++++++++----------- 1 file changed, 159 insertions(+), 94 deletions(-) diff --git a/libraries/audio/src/AudioHRTF.cpp b/libraries/audio/src/AudioHRTF.cpp index 658af01408..378e2154c1 100644 --- a/libraries/audio/src/AudioHRTF.cpp +++ b/libraries/audio/src/AudioHRTF.cpp @@ -16,6 +16,13 @@ #include "AudioHRTF.h" #include "AudioHRTFData.h" +#ifndef MAX +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#endif +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + // // Equal-gain crossfade // @@ -58,6 +65,103 @@ static const float crossfadeTable[HRTF_BLOCK] = { 0.0024846123f, 0.0019026510f, 0.0013981014f, 0.0009710421f, 0.0006215394f, 0.0003496476f, 0.0001554090f, 0.0000388538f, }; +// +// Model the frequency-dependent attenuation of sound propogation in air. +// +// Fit using linear regression to a log-log model of lowpass cutoff frequency vs distance, +// loosely based on data from Handbook of Acoustics. Only the onset of significant +// attenuation is modelled, not the filter slope. +// +// 1m -> -3dB @ 55kHz +// 10m -> -3dB @ 12kHz +// 100m -> -3dB @ 2.5kHz +// 1km -> -3dB @ 0.6kHz +// 10km -> -3dB @ 0.1kHz +// +static const int NLOWPASS = 64; +static const float lowpassTable[NLOWPASS][5] = { // { b0, b1, b2, a1, a2 } + // distance = 1 + { 0.999772371f, 1.399489756f, 0.454495527f, 1.399458985f, 0.454298669f }, + { 0.999631480f, 1.357609808f, 0.425210203f, 1.357549905f, 0.424901586f }, + { 0.999405154f, 1.311503050f, 0.394349994f, 1.311386830f, 0.393871368f }, + { 0.999042876f, 1.260674595f, 0.361869089f, 1.260450057f, 0.361136504f }, + // distance = 2 + { 0.998465222f, 1.204646525f, 0.327757118f, 1.204214978f, 0.326653886f }, + { 0.997548106f, 1.143019308f, 0.292064663f, 1.142195387f, 0.290436690f }, + { 0.996099269f, 1.075569152f, 0.254941286f, 1.074009405f, 0.252600301f }, + { 0.993824292f, 1.002389610f, 0.216688640f, 0.999469185f, 0.213433357f }, + // distance = 4 + { 0.990280170f, 0.924075266f, 0.177827150f, 0.918684864f, 0.173497723f }, + { 0.984818279f, 0.841917936f, 0.139164195f, 0.832151968f, 0.133748443f }, + { 0.976528670f, 0.758036513f, 0.101832398f, 0.740761682f, 0.095635899f }, + { 0.964216485f, 0.675305244f, 0.067243474f, 0.645654855f, 0.061110348f }, + // distance = 8 + { 0.946463038f, 0.596943020f, 0.036899688f, 0.547879974f, 0.032425772f }, + { 0.921823868f, 0.525770189f, 0.012060451f, 0.447952111f, 0.011702396f }, + { 0.890470015f, 0.463334299f, -0.001227816f, 0.347276405f, 0.005300092f }, + { 0.851335343f, 0.407521164f, -0.009353968f, 0.241900234f, 0.007602305f }, + // distance = 16 + { 0.804237360f, 0.358139558f, -0.014293332f, 0.130934213f, 0.017149373f }, + { 0.750073259f, 0.314581568f, -0.016625381f, 0.014505388f, 0.033524057f }, + { 0.690412072f, 0.275936128f, -0.017054561f, -0.106682490f, 0.055976129f }, + { 0.627245545f, 0.241342015f, -0.016246850f, -0.231302564f, 0.083643275f }, + // distance = 32 + { 0.562700627f, 0.210158533f, -0.014740899f, -0.357562697f, 0.115680957f }, + { 0.498787849f, 0.181982455f, -0.012925406f, -0.483461730f, 0.151306628f }, + { 0.437224055f, 0.156585449f, -0.011055180f, -0.607042210f, 0.189796534f }, + { 0.379336998f, 0.133834032f, -0.009281617f, -0.726580065f, 0.230469477f }, + // distance = 64 + { 0.326040627f, 0.113624970f, -0.007683443f, -0.840693542f, 0.272675696f }, + { 0.277861727f, 0.095845793f, -0.006291936f, -0.948380091f, 0.315795676f }, + { 0.234997480f, 0.080357656f, -0.005109519f, -1.049001190f, 0.359246807f }, + { 0.197386484f, 0.066993521f, -0.004122547f, -1.142236313f, 0.402493771f }, + // distance = 128 + { 0.164780457f, 0.055564709f, -0.003309645f, -1.228023442f, 0.445058962f }, + { 0.136808677f, 0.045870650f, -0.002646850f, -1.306498037f, 0.486530514f }, + { 0.113031290f, 0.037708627f, -0.002110591f, -1.377937457f, 0.526566783f }, + { 0.092980475f, 0.030881892f, -0.001679255f, -1.442713983f, 0.564897095f }, + // distance = 256 + { 0.076190239f, 0.025205585f, -0.001333863f, -1.501257246f, 0.601319206f }, + { 0.062216509f, 0.020510496f, -0.001058229f, -1.554025452f, 0.635694228f }, + { 0.050649464f, 0.016644994f, -0.000838826f, -1.601484205f, 0.667939837f }, + { 0.041120009f, 0.013475547f, -0.000664513f, -1.644091518f, 0.698022561f }, + // distance = 512 + { 0.033302044f, 0.010886252f, -0.000526217f, -1.682287704f, 0.725949783f }, + { 0.026911868f, 0.008777712f, -0.000416605f, -1.716488979f, 0.751761953f }, + { 0.021705773f, 0.007065551f, -0.000329788f, -1.747083800f, 0.775525335f }, + { 0.017476603f, 0.005678758f, -0.000261057f, -1.774431204f, 0.797325509f }, + // distance = 1024 + { 0.014049828f, 0.004558012f, -0.000206658f, -1.798860530f, 0.817261711f }, + { 0.011279504f, 0.003654067f, -0.000163610f, -1.820672082f, 0.835442043f }, + { 0.009044384f, 0.002926264f, -0.000129544f, -1.840138412f, 0.851979516f }, + { 0.007244289f, 0.002341194f, -0.000102586f, -1.857505967f, 0.866988864f }, + // distance = 2048 + { 0.005796846f, 0.001871515f, -0.000081250f, -1.872996926f, 0.880584038f }, + { 0.004634607f, 0.001494933f, -0.000064362f, -1.886811124f, 0.892876302f }, + { 0.003702543f, 0.001193324f, -0.000050993f, -1.899127955f, 0.903972829f }, + { 0.002955900f, 0.000951996f, -0.000040407f, -1.910108223f, 0.913975712f }, + // distance = 4096 + { 0.002358382f, 0.000759068f, -0.000032024f, -1.919895894f, 0.922981321f }, + { 0.001880626f, 0.000604950f, -0.000025383f, -1.928619738f, 0.931079931f }, + { 0.001498926f, 0.000481920f, -0.000020123f, -1.936394836f, 0.938355560f }, + { 0.001194182f, 0.000383767f, -0.000015954f, -1.943323983f, 0.944885977f }, + // distance = 8192 + { 0.000951028f, 0.000305502f, -0.000012651f, -1.949498943f, 0.950742822f }, + { 0.000757125f, 0.000243126f, -0.000010033f, -1.955001608f, 0.955991826f }, + { 0.000602572f, 0.000193434f, -0.000007957f, -1.959905036f, 0.960693085f }, + { 0.000479438f, 0.000153861f, -0.000006312f, -1.964274383f, 0.964901371f }, + // distance = 16384 + { 0.000381374f, 0.000122359f, -0.000005007f, -1.968167752f, 0.968666478f }, + { 0.000303302f, 0.000097288f, -0.000003972f, -1.971636944f, 0.972033562f }, + { 0.000241166f, 0.000077342f, -0.000003151f, -1.974728138f, 0.975043493f }, + { 0.000191726f, 0.000061475f, -0.000002500f, -1.977482493f, 0.977733194f }, + // distance = 32768 + { 0.000152399f, 0.000048857f, -0.000001984f, -1.979936697f, 0.980135969f }, + { 0.000121122f, 0.000038825f, -0.000001574f, -1.982123446f, 0.982281818f }, + { 0.000096252f, 0.000030849f, -0.000001249f, -1.984071877f, 0.984197728f }, + { 0.000076480f, 0.000024509f, -0.000000991f, -1.985807957f, 0.985907955f }, +}; + static const float TWOPI = 6.283185307f; // @@ -578,80 +682,58 @@ static void ThiranBiquad(float f, float& b0, float& b1, float& b2, float& a1, fl b2 = 1.0f; } -// returns the gain of analog (s-plane) lowpass evaluated at w -static double analogFilter(double w0, double w) { - double w0sq, wsq; - double num, den; +// split x into exponent and fraction (0.0f to 1.0f) +static void splitf(float x, int& expn, float& frac) { - w0sq = w0 * w0; - wsq = w * w; + union { float f; int i; } mant, bits = { x }; + const int IEEE754_MANT_BITS = 23; + const int IEEE754_EXPN_BIAS = 127; - num = w0sq * w0sq; - den = wsq * wsq + w0sq * w0sq; - - return sqrt(num / den); + mant.i = bits.i & ((1 << IEEE754_MANT_BITS) - 1); + mant.i |= (IEEE754_EXPN_BIAS << IEEE754_MANT_BITS); + + frac = mant.f - 1.0f; + expn = (bits.i >> IEEE754_MANT_BITS) - IEEE754_EXPN_BIAS; } -// design a lowpass biquad using analog matching -static void LowpassBiquad(double coef[5], double w0) { - double G1; - double wpi, wn, wd; - double wna, wda; - double gn, gd, gnsq, gdsq; - double num, den; - double Wnsq, Wdsq, B, A; - double b0, b1, b2, a0, a1, a2; - double temp, scale; - const double PI = 3.14159265358979323846; +static void distanceBiquad(float distance, float& b0, float& b1, float& b2, float& a1, float& a2) { - // compute the Nyquist gain - wpi = w0 + 2.8 * (1.0 - w0/PI); // minimax-like error - wpi = (wpi > PI) ? PI : wpi; - G1 = analogFilter(w0, wpi); + // + // Computed from a lookup table quantized to distance = 2^(N/4) + // and reconstructed by piecewise linear interpolation. + // Approximation error < 0.25dB + // - // approximate wn and wd - wd = 0.5 * w0; - wn = wd * sqrt(1.0/G1); // down G1 at pi, instead of zeros + float x = distance; + x = MIN(MAX(x, 1.0f), 1<<30); + x *= x; + x *= x; // x = distance^4 - Wnsq = wn * wn; - Wdsq = wd * wd; + // split x into e and frac, such that x = 2^(e+0) + frac * (2^(e+1) - 2^(e+0)) + int e; + float frac; + splitf(x, e, frac); - // analog freqs of wn and wd - wna = 2.0 * atan(wn); - wda = 2.0 * atan(wd); + // clamp to table limits + if (e < 0) { + e = 0; + frac = 0.0f; + } + if (e > NLOWPASS-2) { + e = NLOWPASS-2; + frac = 1.0f; + } + assert(frac >= 0.0f); + assert(frac <= 1.0f); + assert(e+0 >= 0); + assert(e+1 < NLOWPASS); - // normalized analog gains at wna and wda - temp = 1.0 / G1; - gn = temp * analogFilter(w0, wna); - gd = temp * analogFilter(w0, wda); - gnsq = gn * gn; - gdsq = gd * gd; - - // compute B, matching gains at wn and wd - temp = 1.0 / (wn * wd); - den = fabs(gnsq - gdsq); - num = gnsq * (Wnsq - Wdsq) * (Wnsq - Wdsq) * (Wnsq + gdsq * Wdsq); - B = temp * sqrt(num / den); - - // compute A, matching gains at wn and wd - num = (Wnsq - Wdsq) * (Wnsq - Wdsq) * (Wnsq + gnsq * Wdsq); - A = temp * sqrt(num / den); - - // design digital filter via bilinear transform - b0 = G1 * (1.0 + B + Wnsq); - b1 = G1 * 2.0 * (Wnsq - 1.0); - b2 = G1 * (1.0 - B + Wnsq); - a0 = 1.0 + A + Wdsq; - a1 = 2.0 * (Wdsq - 1.0); - a2 = 1.0 - A + Wdsq; - - // normalize - scale = 1.0 / a0; - coef[0] = b0 * scale; - coef[1] = b1 * scale; - coef[2] = b2 * scale; - coef[3] = a1 * scale; - coef[4] = a2 * scale; + // piecewise linear interpolation + b0 = lowpassTable[e+0][0] + frac * (lowpassTable[e+1][0] - lowpassTable[e+0][0]); + b1 = lowpassTable[e+0][1] + frac * (lowpassTable[e+1][1] - lowpassTable[e+0][1]); + b2 = lowpassTable[e+0][2] + frac * (lowpassTable[e+1][2] - lowpassTable[e+0][2]); + a1 = lowpassTable[e+0][3] + frac * (lowpassTable[e+1][3] - lowpassTable[e+0][3]); + a2 = lowpassTable[e+0][4] + frac * (lowpassTable[e+1][4] - lowpassTable[e+0][4]); } // compute new filters for a given azimuth, distance and gain @@ -739,38 +821,21 @@ static void setFilters(float firCoef[4][HRTF_TAPS], float bqCoef[5][8], int dela } // - // Model the frequency-dependent attenuation of sound propogation in air. - // Fit using linear regression to a log-log model of lowpass cutoff frequency vs distance, - // loosely based on data from Handbook of Acoustics. Only the onset of significant - // attenuation is modelled, not the filter slope. + // Second biquad implements the distance filter. // - // 1m -> -3dB @ 55kHz - // 10m -> -3dB @ 12kHz - // 100m -> -3dB @ 2.5kHz - // 1km -> -3dB @ 0.6kHz - // 10km -> -3dB @ 0.1kHz - // - distance = (distance < 1.0f) ? 1.0f : distance; - double freq = exp2(-0.666 * log2(distance) + 15.75); - double coef[5]; - LowpassBiquad(coef, (double)TWOPI * freq / 24000); + distanceBiquad(distance, b0, b1, b2, a1, a2); - // TESTING: compute attn at w=pi - //double num = coef[0] - coef[1] + coef[2]; - //double den = 1.0 - coef[3] + coef[4]; - //double mag = 10 * log10((num * num) / (den * den)); + bqCoef[0][channel+4] = b0; + bqCoef[1][channel+4] = b1; + bqCoef[2][channel+4] = b2; + bqCoef[3][channel+4] = a1; + bqCoef[4][channel+4] = a2; - bqCoef[0][channel+4] = (float)coef[0]; - bqCoef[1][channel+4] = (float)coef[1]; - bqCoef[2][channel+4] = (float)coef[2]; - bqCoef[3][channel+4] = (float)coef[3]; - bqCoef[4][channel+4] = (float)coef[4]; - - bqCoef[0][channel+5] = (float)coef[0]; - bqCoef[1][channel+5] = (float)coef[1]; - bqCoef[2][channel+5] = (float)coef[2]; - bqCoef[3][channel+5] = (float)coef[3]; - bqCoef[4][channel+5] = (float)coef[4]; + bqCoef[0][channel+5] = b0; + bqCoef[1][channel+5] = b1; + bqCoef[2][channel+5] = b2; + bqCoef[3][channel+5] = a1; + bqCoef[4][channel+5] = a2; } void AudioHRTF::render(int16_t* input, float* output, int index, float azimuth, float distance, float gain, int numFrames) { From 44500889f8ff0336ad9911ed2f3015f134ea38d2 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 14 Jul 2016 13:34:56 -0700 Subject: [PATCH 23/25] Remove 'updating reverb' logspam --- libraries/audio-client/src/AudioClient.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index dd72125d93..bec30edb4e 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -954,7 +954,6 @@ void AudioClient::processReceivedSamples(const QByteArray& decodedBuffer, QByteA if (hasReverb) { assert(_outputFormat.channelCount() == 2); updateReverbOptions(); - qDebug() << "handling reverb"; _listenerReverb.render(outputSamples, outputSamples, numDeviceOutputSamples/2); } } From d84c7524bf76efd09a213d9557573d22232aa70b Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 14 Jul 2016 14:56:58 -0700 Subject: [PATCH 24/25] Remove global HRTF headroom. The initial HRTF reduced overall gain by -6dB to avoid clipping at spectral peaks. With the addition of a peak limiter, this is no longer necessary. Changing to 0dB improves the loudness match between spatialized and unspatialized sounds. --- libraries/audio/src/AudioHRTF.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioHRTF.h b/libraries/audio/src/AudioHRTF.h index 111409f80c..63d1712980 100644 --- a/libraries/audio/src/AudioHRTF.h +++ b/libraries/audio/src/AudioHRTF.h @@ -21,7 +21,7 @@ static const int HRTF_TABLES = 25; // number of HRTF subjects static const int HRTF_DELAY = 24; // max ITD in samples (1.0ms at 24KHz) static const int HRTF_BLOCK = 256; // block processing size -static const float HRTF_GAIN = 0.5f; // HRTF global gain adjustment +static const float HRTF_GAIN = 1.0f; // HRTF global gain adjustment class AudioHRTF { From 2674df6095382bc6894265e3810c4ceb1aad9824 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 14 Jul 2016 18:34:07 -0700 Subject: [PATCH 25/25] Fix the distance-attenuation model (for injectors only) The original attenuation model seems wrong, under-attenuating at close distance but completely muting after 45m. This uses a physics-based model of -6dB per doubling of distance in free space, for the injectors. The AudioMixer model and domain settings still need to be reworked in a future PR. --- libraries/audio-client/src/AudioClient.cpp | 32 +++++----------------- libraries/audio-client/src/AudioClient.h | 2 +- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 50039ba668..84d0146f3b 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -885,7 +885,7 @@ void AudioClient::mixLocalAudioInjectors(int16_t* inputBuffer) { // calculate distance, gain and azimuth for hrtf glm::vec3 relativePosition = injector->getPosition() - _positionGetter(); float distance = glm::max(glm::length(relativePosition), EPSILON); - float gain = gainForSource(relativePosition, injector->getVolume()); + float gain = gainForSource(distance, injector->getVolume()); float azimuth = azimuthForSource(relativePosition); injector->getLocalHRTF().render(_scratchBuffer, _hrtfBuffer, 1, azimuth, distance, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); @@ -1299,37 +1299,19 @@ float AudioClient::azimuthForSource(const glm::vec3& relativePosition) { } } -float AudioClient::gainForSource(const glm::vec3& relativePosition, float volume) { - // TODO: put these in a place where we can share with AudioMixer! - const float DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE = 0.18f; +float AudioClient::gainForSource(float distance, float volume) { + const float ATTENUATION_BEGINS_AT_DISTANCE = 1.0f; - - //qDebug() << "initial gain is " << volume; - // I'm assuming that the AudioMixer's getting of the stream's attenuation // factor is basically same as getting volume float gain = volume; - float distanceBetween = glm::length(relativePosition); - if (distanceBetween < EPSILON ) { - distanceBetween = EPSILON; + + // attenuate based on distance + if (distance >= ATTENUATION_BEGINS_AT_DISTANCE) { + gain /= distance; // attenuation = -6dB * log2(distance) } - // audio mixer has notion of zones. Unsure how to map that across here... - - // attenuate based on distance now - if (distanceBetween >= ATTENUATION_BEGINS_AT_DISTANCE) { - float distanceCoefficient = 1.0f - (logf(distanceBetween/ATTENUATION_BEGINS_AT_DISTANCE) / logf(2.0f) - * DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE); - if (distanceCoefficient < 0.0f) { - distanceCoefficient = 0.0f; - } - - gain *= distanceCoefficient; - } - - //qDebug() << "calculated gain as " << gain; - return gain; } diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index df7b10ab04..3e4aa931a6 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -217,7 +217,7 @@ private: void outputFormatChanged(); void mixLocalAudioInjectors(int16_t* inputBuffer); float azimuthForSource(const glm::vec3& relativePosition); - float gainForSource(const glm::vec3& relativePosition, float volume); + float gainForSource(float distance, float volume); QByteArray firstInputFrame; QAudioInput* _audioInput;