From 70820e60754dc8ec41512474648c5b07b57be9ac Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 28 Nov 2017 15:37:36 -0800 Subject: [PATCH 01/29] laser allways on prototype --- .../controllerModules/farActionGrabEntity.js | 4 ++-- .../controllers/controllerModules/farTrigger.js | 4 ++-- .../controllerModules/hudOverlayPointer.js | 5 +++-- .../controllerModules/webSurfaceLaserInput.js | 10 ++++------ .../system/libraries/controllerDispatcherUtils.js | 12 ++++++++++++ scripts/system/libraries/pointersUtils.js | 13 +++++++++---- 6 files changed, 32 insertions(+), 16 deletions(-) diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 0d421bbaec..5e12252bc3 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -14,7 +14,7 @@ PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic, getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI - Picks, makeLaserLockInfo Xform + Picks, makeLaserLockInfo Xform, makeLaserParams */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -119,7 +119,7 @@ Script.include("/~/system/libraries/Xform.js"); this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], [], 100, - this.hand); + makeLaserParams(this.hand, false)); this.handToController = function() { diff --git a/scripts/system/controllers/controllerModules/farTrigger.js b/scripts/system/controllers/controllerModules/farTrigger.js index 24f336d581..70e9ceff16 100644 --- a/scripts/system/controllers/controllerModules/farTrigger.js +++ b/scripts/system/controllers/controllerModules/farTrigger.js @@ -9,7 +9,7 @@ /* global Script, Controller, LaserPointers, RayPick, RIGHT_HAND, LEFT_HAND, MyAvatar, getGrabPointSphereOffset, makeRunningValues, Entities, enableDispatcherModule, disableDispatcherModule, makeDispatcherModuleParameters, PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, - DEFAULT_SEARCH_SPHERE_DISTANCE, getGrabbableData + DEFAULT_SEARCH_SPHERE_DISTANCE, getGrabbableData, makeLaserParams */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -34,7 +34,7 @@ Script.include("/~/system/libraries/controllers.js"); this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], [], 100, - this.hand); + makeLaserParams(this.hand, false)); this.getTargetProps = function (controllerData) { // nearbyEntityProperties is already sorted by length from controller diff --git a/scripts/system/controllers/controllerModules/hudOverlayPointer.js b/scripts/system/controllers/controllerModules/hudOverlayPointer.js index 38ee29ae3b..ef56440cee 100644 --- a/scripts/system/controllers/controllerModules/hudOverlayPointer.js +++ b/scripts/system/controllers/controllerModules/hudOverlayPointer.js @@ -16,7 +16,8 @@ makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic, - getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI + getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI, + makeLaserParams */ (function() { @@ -36,7 +37,7 @@ this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], [], 100, - (this.hand + HUD_LASER_OFFSET)); + makeLaserParams((this.hand + HUD_LASER_OFFSET), false)); this.getOtherHandController = function() { return (this.hand === RIGHT_HAND) ? Controller.Standard.LeftHand : Controller.Standard.RightHand; diff --git a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js index bf90124cab..92f1d0eb1b 100644 --- a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js +++ b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js @@ -9,7 +9,7 @@ makeRunningValues, Messages, Quat, Vec3, makeDispatcherModuleParameters, Overlays, ZERO_VEC, HMD, INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, getGrabPointSphereOffset, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_ON_VALUE, - TRIGGER_OFF_VALUE, getEnabledModuleByName, PICK_MAX_DISTANCE, LaserPointers, RayPick, ContextOverlay, Picks + TRIGGER_OFF_VALUE, getEnabledModuleByName, PICK_MAX_DISTANCE, LaserPointers, RayPick, ContextOverlay, Picks, makeLaserParams */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -25,7 +25,7 @@ Script.include("/~/system/libraries/controllers.js"); this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], [], 100, - this.hand); + makeLaserParams(hand, true)); this.grabModuleWantsNearbyOverlay = function(controllerData) { if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { @@ -80,16 +80,14 @@ Script.include("/~/system/libraries/controllers.js"); var otherModuleRunning = this.getOtherModule().running; if ((this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData)) && !otherModuleRunning) { - if (controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE) { - return makeRunningValues(true, [], []); - } + return makeRunningValues(true, [], []); } return makeRunningValues(false, [], []); }; this.run = function (controllerData, deltaTime) { var grabModuleNeedsToRun = this.grabModuleWantsNearbyOverlay(controllerData); - if (controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE && !grabModuleNeedsToRun) { + if ((this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData)) && !grabModuleNeedsToRun) { this.running = true; return makeRunningValues(true, [], []); } diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index fd0db91fec..455e9dcd66 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -45,6 +45,7 @@ BUMPER_ON_VALUE:true, getEntityParents:true, findHandChildEntities:true, + makeLaserParams:true, TEAR_AWAY_DISTANCE:true, TEAR_AWAY_COUNT:true, TEAR_AWAY_CHECK_TIME:true, @@ -134,6 +135,17 @@ makeLaserLockInfo = function(targetID, isOverlay, hand, offset) { }; }; +makeLaserParams = function(hand, allwaysOn) { + if (allwaysOn === undefined) { + allwaysOn = false; + } + + return { + hand: hand, + allwaysOn: allwaysOn + }; +}; + makeRunningValues = function (active, targets, requiredDataForRun, laserLockInfo) { return { active: active, diff --git a/scripts/system/libraries/pointersUtils.js b/scripts/system/libraries/pointersUtils.js index 896ef094b8..2af563f8d4 100644 --- a/scripts/system/libraries/pointersUtils.js +++ b/scripts/system/libraries/pointersUtils.js @@ -95,6 +95,7 @@ Pointer = function(hudLayer, pickType, pointerData) { this.pointerID = null; this.visible = false; this.locked = false; + this.allwaysOn = false; this.hand = pointerData.hand; delete pointerData.hand; @@ -150,7 +151,7 @@ Pointer = function(hudLayer, pickType, pointerData) { mode = "hold"; } else if (triggerClicks[this.hand]) { mode = "full"; - } else if (triggerValues[this.hand] > TRIGGER_ON_VALUE) { + } else if (triggerValues[this.hand] > TRIGGER_ON_VALUE || this.allwaysOn) { mode = "half"; } } @@ -172,19 +173,23 @@ PointerManager = function() { return pointer.pointerID; }; - this.makePointerVisible = function(index) { + this.makePointerVisible = function(laserParams) { + var index = laserParams.hand; if (index < this.pointers.length && index >= 0) { this.pointers[index].makeVisible(); + this.pointers[index].allwaysOn = laserParams.allwaysOn; } }; - this.makePointerInvisible = function(index) { + this.makePointerInvisible = function(laserParams) { + var index = laserParams.hand; if (index < this.pointers.length && index >= 0) { this.pointers[index].makeInvisible(); } }; - this.lockPointerEnd = function(index, lockData) { + this.lockPointerEnd = function(laserParams, lockData) { + var index = laserParams.hand; if (index < this.pointers.length && index >= 0) { this.pointers[index].lockEnd(lockData); } From 024dbcd71290d5e39ddb1e11e66f10e1c0804707 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 30 Nov 2017 09:07:19 -0800 Subject: [PATCH 02/29] fix scrolling --- .../controllers/controllerModules/webSurfaceLaserInput.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js index 92f1d0eb1b..94cb2f86cb 100644 --- a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js +++ b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js @@ -87,7 +87,7 @@ Script.include("/~/system/libraries/controllers.js"); this.run = function (controllerData, deltaTime) { var grabModuleNeedsToRun = this.grabModuleWantsNearbyOverlay(controllerData); - if ((this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData)) && !grabModuleNeedsToRun) { + if ((controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE || (this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData))) && !grabModuleNeedsToRun) { this.running = true; return makeRunningValues(true, [], []); } From cbaf59d99ee15181ed84494b4c4f8fe1443a8a60 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 30 Nov 2017 11:13:17 -0800 Subject: [PATCH 03/29] disable stylus --- scripts/system/controllers/controllerScripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 1eb30bbefd..1208b1ab83 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -19,7 +19,7 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/nearParentGrabOverlay.js", "controllerModules/nearActionGrabEntity.js", "controllerModules/farActionGrabEntity.js", - "controllerModules/stylusInput.js", + //"controllerModules/stylusInput.js", "controllerModules/equipEntity.js", "controllerModules/nearTrigger.js", "controllerModules/webSurfaceLaserInput.js", From b9f60aff2a5e8b8c21a6e490689cfe435139122d Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 30 Nov 2017 13:22:26 -0800 Subject: [PATCH 04/29] fix-edit --- .../system/controllers/controllerModules/inEditMode.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index f79b2db1e2..951c3a969e 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -10,7 +10,7 @@ /* global Script, Controller, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule, makeRunningValues, Messages, makeDispatcherModuleParameters, HMD, getGrabPointSphereOffset, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_ON_VALUE, - getEnabledModuleByName, PICK_MAX_DISTANCE, isInEditMode, LaserPointers, RayPick, Picks + getEnabledModuleByName, PICK_MAX_DISTANCE, isInEditMode, LaserPointers, RayPick, Picks, makeLaserParams */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -28,7 +28,7 @@ Script.include("/~/system/libraries/utils.js"); this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip", "rightHandTrigger"] : ["leftHand", "leftHandEquip", "leftHandTrigger"], [], 100, - this.hand); + makeLaserParams(this.hand, false)); this.nearTablet = function(overlays) { for (var i = 0; i < overlays.length; i++) { @@ -99,8 +99,8 @@ Script.include("/~/system/libraries/utils.js"); var overlayLaser = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightWebSurfaceLaserInput" : "LeftWebSurfaceLaserInput"); if (overlayLaser) { var overlayLaserReady = overlayLaser.isReady(controllerData); - - if (overlayLaserReady.active && this.pointingAtTablet(overlayLaser.target)) { + var target = controllerData.rayPicks[this.hand].objectID; + if (overlayLaserReady.active && this.pointingAtTablet(target)) { return this.exitModule(); } } From 42424d32f1d7e3de78853f7c2ae395e30b8baef6 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 7 Dec 2017 16:15:56 -0800 Subject: [PATCH 05/29] Added exponential smoothing filter Tuned coefficients to maximize glitch and vibration damping while minimizing latency. --- interface/resources/controllers/vive.json | 11 ++-- .../src/controllers/impl/Filter.cpp | 14 +++-- .../controllers/impl/RouteBuilderProxy.cpp | 6 ++ .../src/controllers/impl/RouteBuilderProxy.h | 1 + .../filters/ExponentialSmoothingFilter.cpp | 62 +++++++++++++++++++ .../impl/filters/ExponentialSmoothingFilter.h | 42 +++++++++++++ 6 files changed, 124 insertions(+), 12 deletions(-) create mode 100644 libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.cpp create mode 100644 libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.h diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index 02fc09c815..8a7744efb3 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -56,29 +56,28 @@ { "from": "Vive.LeftFoot", "to" : "Standard.LeftFoot", - "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}] + "filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.3}] }, { "from": "Vive.RightFoot", "to" : "Standard.RightFoot", - "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}] + "filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.3}] }, { "from": "Vive.Hips", "to" : "Standard.Hips", - "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}] + "filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.3}] }, { "from": "Vive.Spine2", "to" : "Standard.Spine2", - "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}] + "filters" : [{"type" : "exponentialSmoothing", "rotation" : 0.15, "translation": 0.3}] }, { "from": "Vive.Head", "to" : "Standard.Head"}, - { "from": "Vive.RightArm", "to" : "Standard.RightArm" }, { "from": "Vive.LeftArm", "to" : "Standard.LeftArm" }, - + { "from": "Vive.TrackedObject00", "to" : "Standard.TrackedObject00" }, { "from": "Vive.TrackedObject01", "to" : "Standard.TrackedObject01" }, { "from": "Vive.TrackedObject02", "to" : "Standard.TrackedObject02" }, diff --git a/libraries/controllers/src/controllers/impl/Filter.cpp b/libraries/controllers/src/controllers/impl/Filter.cpp index b6c3ed4d27..6e6dc816d0 100644 --- a/libraries/controllers/src/controllers/impl/Filter.cpp +++ b/libraries/controllers/src/controllers/impl/Filter.cpp @@ -30,6 +30,7 @@ #include "filters/PostTransformFilter.h" #include "filters/RotateFilter.h" #include "filters/LowVelocityFilter.h" +#include "filters/ExponentialSmoothingFilter.h" using namespace controller; @@ -49,6 +50,7 @@ REGISTER_FILTER_CLASS_INSTANCE(TransformFilter, "transform") REGISTER_FILTER_CLASS_INSTANCE(PostTransformFilter, "postTransform") REGISTER_FILTER_CLASS_INSTANCE(RotateFilter, "rotate") REGISTER_FILTER_CLASS_INSTANCE(LowVelocityFilter, "lowVelocity") +REGISTER_FILTER_CLASS_INSTANCE(ExponentialSmoothingFilter, "exponentialSmoothing") const QString JSON_FILTER_TYPE = QStringLiteral("type"); const QString JSON_FILTER_PARAMS = QStringLiteral("params"); @@ -93,7 +95,7 @@ bool Filter::parseSingleFloatParameter(const QJsonValue& parameters, const QStri output = objectParameters[name].toDouble(); return true; } - } + } return false; } @@ -117,7 +119,7 @@ bool Filter::parseVec3Parameter(const QJsonValue& parameters, glm::vec3& output) objectParameters["z"].toDouble()); return true; } - } + } return false; } @@ -126,7 +128,7 @@ bool Filter::parseMat4Parameter(const QJsonValue& parameters, glm::mat4& output) auto objectParameters = parameters.toObject(); - if (objectParameters.contains("r0c0") && + if (objectParameters.contains("r0c0") && objectParameters.contains("r1c0") && objectParameters.contains("r2c0") && objectParameters.contains("r3c0") && @@ -169,9 +171,9 @@ bool Filter::parseMat4Parameter(const QJsonValue& parameters, glm::mat4& output) bool Filter::parseQuatParameter(const QJsonValue& parameters, glm::quat& output) { if (parameters.isObject()) { auto objectParameters = parameters.toObject(); - if (objectParameters.contains("w") && - objectParameters.contains("x") && - objectParameters.contains("y") && + if (objectParameters.contains("w") && + objectParameters.contains("x") && + objectParameters.contains("y") && objectParameters.contains("z")) { output = glm::quat(objectParameters["w"].toDouble(), diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index bc1ef55725..048e23be1c 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -32,6 +32,7 @@ #include "filters/PostTransformFilter.h" #include "filters/RotateFilter.h" #include "filters/LowVelocityFilter.h" +#include "filters/ExponentialSmoothingFilter.h" #include "conditionals/AndConditional.h" using namespace controller; @@ -134,6 +135,11 @@ QObject* RouteBuilderProxy::lowVelocity(float rotationConstant, float translatio return this; } +QObject* RouteBuilderProxy::exponentialSmoothing(float rotationConstant, float translationConstant) { + addFilter(std::make_shared(rotationConstant, translationConstant)); + return this; +} + QObject* RouteBuilderProxy::constrainToInteger() { addFilter(std::make_shared()); return this; diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index 75f3747566..92a87e5e39 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -53,6 +53,7 @@ class RouteBuilderProxy : public QObject { Q_INVOKABLE QObject* postTransform(glm::mat4 transform); Q_INVOKABLE QObject* rotate(glm::quat rotation); Q_INVOKABLE QObject* lowVelocity(float rotationConstant, float translationConstant); + Q_INVOKABLE QObject* exponentialSmoothing(float rotationConstant, float translationConstant); Q_INVOKABLE QObject* logicalNot(); private: diff --git a/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.cpp b/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.cpp new file mode 100644 index 0000000000..355c45ced9 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.cpp @@ -0,0 +1,62 @@ +// +// Created by Anthony Thibault 2017/12/07 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#include "ExponentialSmoothingFilter.h" + +#include +#include +#include "../../UserInputMapper.h" +#include "../../Input.h" +#include + +static const QString JSON_ROTATION = QStringLiteral("rotation"); +static const QString JSON_TRANSLATION = QStringLiteral("translation"); +namespace controller { + + Pose ExponentialSmoothingFilter::apply(Pose value) const { + + // to perform filtering in sensor space, we need to compute the transformations. + auto userInputMapper = DependencyManager::get(); + const InputCalibrationData calibrationData = userInputMapper->getInputCalibrationData(); + glm::mat4 sensorToAvatarMat = glm::inverse(calibrationData.avatarMat) * calibrationData.sensorToWorldMat; + glm::mat4 avatarToSensorMat = glm::inverse(calibrationData.sensorToWorldMat) * calibrationData.avatarMat; + + // transform pose into sensor space. + Pose sensorValue = value.transform(avatarToSensorMat); + + if (value.isValid() && _oldSensorValue.isValid()) { + + // exponential smoothing filter + sensorValue.translation = _translationConstant * sensorValue.getTranslation() + (1.0f - _translationConstant) * _oldSensorValue.getTranslation(); + sensorValue.rotation = safeMix(sensorValue.getRotation(), _oldSensorValue.getRotation(), _rotationConstant); + + _oldSensorValue = sensorValue; + + // transform back into avatar space + return sensorValue.transform(sensorToAvatarMat); + } else { + _oldSensorValue = sensorValue; + return value; + } + } + + bool ExponentialSmoothingFilter::parseParameters(const QJsonValue& parameters) { + + if (parameters.isObject()) { + auto obj = parameters.toObject(); + if (obj.contains(JSON_ROTATION) && obj.contains(JSON_TRANSLATION)) { + _rotationConstant = obj[JSON_ROTATION].toDouble(); + _translationConstant = obj[JSON_TRANSLATION].toDouble(); + return true; + } + } + return false; + } + +} diff --git a/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.h b/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.h new file mode 100644 index 0000000000..d2ce96deca --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.h @@ -0,0 +1,42 @@ +// +// Created by Anthony Thibault 2017/12/17 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_Controllers_Filters_Exponential_Smoothing_h +#define hifi_Controllers_Filters_Exponential_Smoothing_h + +#include "../Filter.h" + +namespace controller { + + class ExponentialSmoothingFilter : public Filter { + REGISTER_FILTER_CLASS(ExponentialSmoothingFilter); + + public: + ExponentialSmoothingFilter() {} + ExponentialSmoothingFilter(float rotationConstant, float translationConstant) : + _translationConstant(translationConstant), _rotationConstant(rotationConstant) {} + + float apply(float value) const override { return value; } + Pose apply(Pose value) const override; + bool parseParameters(const QJsonValue& parameters) override; + + private: + + // Constant between 0 and 1. + // 1 indicates no smoothing at all, poses are passed through unaltered. + // Values near 1 are less smooth with lower latency. + // Values near 0 are more smooth with higher latency. + float _translationConstant { 0.375f }; + float _rotationConstant { 0.375f }; + + mutable Pose _oldSensorValue { Pose() }; // sensor space + }; + +} + +#endif From fba06a74aad09313292ab542ca02401713a7cee3 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 8 Dec 2017 18:15:42 -0800 Subject: [PATCH 06/29] fix laser and stylus deadspot --- interface/src/raypick/LaserPointer.cpp | 90 ++++++++++++++++++++----- interface/src/raypick/LaserPointer.h | 21 +++++- interface/src/raypick/RayPick.cpp | 2 +- interface/src/raypick/StylusPointer.cpp | 38 +++++------ interface/src/raypick/StylusPointer.h | 6 +- libraries/pointers/src/Pointer.cpp | 34 ++++++---- libraries/pointers/src/Pointer.h | 7 +- 7 files changed, 140 insertions(+), 58 deletions(-) diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 35f3a44227..a4fe516590 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -218,14 +218,40 @@ Pointer::PickedObject LaserPointer::getHoveredObject(const PickResultPointer& pi return PickedObject(rayPickResult->objectID, rayPickResult->type); } -Pointer::Buttons LaserPointer::getPressedButtons() { +Pointer::Buttons LaserPointer::getPressedButtons(const PickResultPointer& pickResult) { std::unordered_set toReturn; - for (const PointerTrigger& trigger : _triggers) { - // TODO: right now, LaserPointers don't support axes, only on/off buttons - if (trigger.getEndpoint()->peek() >= 1.0f) { - toReturn.insert(trigger.getButton()); + auto rayPickResult = std::static_pointer_cast(pickResult); + + if (rayPickResult) { + for (const PointerTrigger& trigger : _triggers) { + std::string button = trigger.getButton(); + TriggerState& state = _states[button]; + // TODO: right now, LaserPointers don't support axes, only on/off buttons + if (trigger.getEndpoint()->peek() >= 1.0f) { + toReturn.insert(button); + + if (_previousButtons.find(button) == _previousButtons.end()) { + // start triggering for buttons that were just pressed + state.triggeredObject = PickedObject(rayPickResult->objectID, rayPickResult->type); + state.intersection = rayPickResult->intersection; + state.triggerPos2D = findPos2D(state.triggeredObject, rayPickResult->intersection); + state.triggerStartTime = usecTimestampNow(); + state.surfaceNormal = rayPickResult->surfaceNormal; + state.deadspotExpired = false; + state.wasTriggering = true; + state.triggering = true; + _latestState = state; + } + } else { + // stop triggering for buttons that aren't pressed + state.wasTriggering = state.triggering; + state.triggering = false; + _latestState = state; + } } + _previousButtons = toReturn; } + return toReturn; } @@ -303,7 +329,7 @@ RenderState LaserPointer::buildRenderState(const QVariantMap& propMap) { return RenderState(startID, pathID, endID); } -PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, bool hover) const { +PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, const std::string& button, bool hover) { QUuid pickedID; glm::vec3 intersection, surfaceNormal, direction, origin; auto rayPickResult = std::static_pointer_cast(pickResult); @@ -316,20 +342,48 @@ PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const P pickedID = rayPickResult->objectID; } - glm::vec2 pos2D; if (pickedID != target.objectID) { - if (target.type == ENTITY) { - intersection = RayPick::intersectRayWithEntityXYPlane(target.objectID, origin, direction); - } else if (target.type == OVERLAY) { - intersection = RayPick::intersectRayWithOverlayXYPlane(target.objectID, origin, direction); - } + intersection = findIntersection(target, origin, direction); } - if (target.type == ENTITY) { - pos2D = RayPick::projectOntoEntityXYPlane(target.objectID, intersection); - } else if (target.type == OVERLAY) { - pos2D = RayPick::projectOntoOverlayXYPlane(target.objectID, intersection); - } else if (target.type == HUD) { - pos2D = DependencyManager::get()->calculatePos2DFromHUD(intersection); + glm::vec2 pos2D = findPos2D(target, intersection); + + // If we just started triggering and we haven't moved too much, don't update intersection and pos2D + TriggerState& state = hover ? _latestState : _states[button]; + float sensorToWorldScale = DependencyManager::get()->getMyAvatar()->getSensorToWorldScale(); + float deadspotSquared = TOUCH_PRESS_TO_MOVE_DEADSPOT_SQUARED * sensorToWorldScale * sensorToWorldScale; + bool withinDeadspot = usecTimestampNow() - state.triggerStartTime < POINTER_MOVE_DELAY && glm::distance2(pos2D, state.triggerPos2D) < deadspotSquared; + if ((state.triggering || state.wasTriggering) && !state.deadspotExpired && withinDeadspot) { + pos2D = state.triggerPos2D; + intersection = state.intersection; + surfaceNormal = state.surfaceNormal; } + if (!withinDeadspot) { + state.deadspotExpired = true; + } + return PointerEvent(pos2D, intersection, surfaceNormal, direction); +} + +glm::vec3 LaserPointer::findIntersection(const PickedObject& pickedObject, const glm::vec3& origin, const glm::vec3& direction) { + switch (pickedObject.type) { + case ENTITY: + return RayPick::intersectRayWithEntityXYPlane(pickedObject.objectID, origin, direction); + case OVERLAY: + return RayPick::intersectRayWithOverlayXYPlane(pickedObject.objectID, origin, direction); + default: + return glm::vec3(NAN); + } +} + +glm::vec2 LaserPointer::findPos2D(const PickedObject& pickedObject, const glm::vec3& origin) { + switch (pickedObject.type) { + case ENTITY: + return RayPick::projectOntoEntityXYPlane(pickedObject.objectID, origin); + case OVERLAY: + return RayPick::projectOntoOverlayXYPlane(pickedObject.objectID, origin); + case HUD: + return DependencyManager::get()->calculatePos2DFromHUD(origin); + default: + return glm::vec2(NAN); + } } \ No newline at end of file diff --git a/interface/src/raypick/LaserPointer.h b/interface/src/raypick/LaserPointer.h index 5cc83749bd..efc5c02729 100644 --- a/interface/src/raypick/LaserPointer.h +++ b/interface/src/raypick/LaserPointer.h @@ -80,10 +80,10 @@ public: static RenderState buildRenderState(const QVariantMap& propMap); protected: - PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, bool hover = true) const override; + PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, const std::string& button = "", bool hover = true) override; PickedObject getHoveredObject(const PickResultPointer& pickResult) override; - Pointer::Buttons getPressedButtons() override; + Pointer::Buttons getPressedButtons(const PickResultPointer& pickResult) override; bool shouldHover(const PickResultPointer& pickResult) override { return _currentRenderState != ""; } bool shouldTrigger(const PickResultPointer& pickResult) override { return _currentRenderState != ""; } @@ -105,6 +105,23 @@ private: void updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay, bool defaultState); void disableRenderState(const RenderState& renderState); + struct TriggerState { + PickedObject triggeredObject; + glm::vec3 intersection { NAN }; + glm::vec3 surfaceNormal { NAN }; + glm::vec2 triggerPos2D { NAN }; + quint64 triggerStartTime { 0 }; + bool deadspotExpired { true }; + bool triggering { false }; + bool wasTriggering { false }; + }; + + Pointer::Buttons _previousButtons; + std::unordered_map _states; + TriggerState _latestState; + static glm::vec3 findIntersection(const PickedObject& pickedObject, const glm::vec3& origin, const glm::vec3& direction); + static glm::vec2 findPos2D(const PickedObject& pickedObject, const glm::vec3& origin); + }; #endif // hifi_LaserPointer_h diff --git a/interface/src/raypick/RayPick.cpp b/interface/src/raypick/RayPick.cpp index 2f6e69bc7e..214a80a65b 100644 --- a/interface/src/raypick/RayPick.cpp +++ b/interface/src/raypick/RayPick.cpp @@ -103,4 +103,4 @@ glm::vec2 RayPick::projectOntoOverlayXYPlane(const QUuid& overlayID, const glm:: glm::vec2 RayPick::projectOntoEntityXYPlane(const QUuid& entityID, const glm::vec3& worldPos, bool unNormalized) { auto props = DependencyManager::get()->getEntityProperties(entityID); return projectOntoXYPlane(worldPos, props.getPosition(), props.getRotation(), props.getDimensions(), props.getRegistrationPoint(), unNormalized); -} +} \ No newline at end of file diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index 21d257048c..9ea98a90a6 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -28,10 +28,6 @@ static const float TABLET_MAX_TOUCH_DISTANCE = 0.01f; static const float HOVER_HYSTERESIS = 0.01f; static const float TOUCH_HYSTERESIS = 0.02f; -static const float STYLUS_MOVE_DELAY = 0.33f * USECS_PER_SECOND; -static const float TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.0481f; -static const float TOUCH_PRESS_TO_MOVE_DEADSPOT_SQUARED = TOUCH_PRESS_TO_MOVE_DEADSPOT * TOUCH_PRESS_TO_MOVE_DEADSPOT; - StylusPointer::StylusPointer(const QVariant& props, const OverlayID& stylusOverlay, bool hover, bool enabled) : Pointer(DependencyManager::get()->createStylusPick(props), enabled, hover), _stylusOverlay(stylusOverlay) @@ -112,37 +108,37 @@ bool StylusPointer::shouldHover(const PickResultPointer& pickResult) { bool StylusPointer::shouldTrigger(const PickResultPointer& pickResult) { auto stylusPickResult = std::static_pointer_cast(pickResult); + bool wasTriggering = false; if (_renderState == EVENTS_ON && stylusPickResult) { auto sensorScaleFactor = DependencyManager::get()->getMyAvatar()->getSensorToWorldScale(); float distance = stylusPickResult->distance; // If we're triggering on an object, recalculate the distance instead of using the pickResult glm::vec3 origin = vec3FromVariant(stylusPickResult->pickVariant["position"]); - glm::vec3 direction = -_state.surfaceNormal; - if (!_state.triggeredObject.objectID.isNull() && stylusPickResult->objectID != _state.triggeredObject.objectID) { + glm::vec3 direction = _state.triggering ? -_state.surfaceNormal : -stylusPickResult->surfaceNormal; + if ((_state.triggering || _state.wasTriggering) && stylusPickResult->objectID != _state.triggeredObject.objectID) { distance = glm::dot(findIntersection(_state.triggeredObject, origin, direction) - origin, direction); } float hysteresis = _state.triggering ? TOUCH_HYSTERESIS * sensorScaleFactor : 0.0f; if (isWithinBounds(distance, TABLET_MIN_TOUCH_DISTANCE * sensorScaleFactor, - TABLET_MAX_TOUCH_DISTANCE * sensorScaleFactor, hysteresis)) { - if (_state.triggeredObject.objectID.isNull()) { + TABLET_MAX_TOUCH_DISTANCE * sensorScaleFactor, hysteresis)) { + _state.wasTriggering = _state.triggering; + if (!_state.triggering) { _state.triggeredObject = PickedObject(stylusPickResult->objectID, stylusPickResult->type); - _state.intersection = findIntersection(_state.triggeredObject, origin, direction); + _state.intersection = stylusPickResult->intersection; _state.triggerPos2D = findPos2D(_state.triggeredObject, origin); _state.triggerStartTime = usecTimestampNow(); _state.surfaceNormal = stylusPickResult->surfaceNormal; + _state.deadspotExpired = false; _state.triggering = true; } return true; } + wasTriggering = _state.triggering; } - _state.triggeredObject = PickedObject(); - _state.intersection = glm::vec3(NAN); - _state.triggerPos2D = glm::vec2(NAN); - _state.triggerStartTime = 0; - _state.surfaceNormal = glm::vec3(NAN); + _state.wasTriggering = wasTriggering; _state.triggering = false; return false; } @@ -155,13 +151,13 @@ Pointer::PickedObject StylusPointer::getHoveredObject(const PickResultPointer& p return PickedObject(stylusPickResult->objectID, stylusPickResult->type); } -Pointer::Buttons StylusPointer::getPressedButtons() { +Pointer::Buttons StylusPointer::getPressedButtons(const PickResultPointer& pickResult) { // TODO: custom buttons for styluses Pointer::Buttons toReturn({ "Primary", "Focus" }); return toReturn; } -PointerEvent StylusPointer::buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, bool hover) const { +PointerEvent StylusPointer::buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, const std::string& button, bool hover) { QUuid pickedID; glm::vec2 pos2D; glm::vec3 intersection, surfaceNormal, direction, origin; @@ -177,18 +173,22 @@ PointerEvent StylusPointer::buildPointerEvent(const PickedObject& target, const } // If we just started triggering and we haven't moved too much, don't update intersection and pos2D - if (!_state.triggeredObject.objectID.isNull() && usecTimestampNow() - _state.triggerStartTime < STYLUS_MOVE_DELAY && - glm::distance2(pos2D, _state.triggerPos2D) < TOUCH_PRESS_TO_MOVE_DEADSPOT_SQUARED) { + float sensorToWorldScale = DependencyManager::get()->getMyAvatar()->getSensorToWorldScale(); + float deadspotSquared = TOUCH_PRESS_TO_MOVE_DEADSPOT_SQUARED * sensorToWorldScale * sensorToWorldScale; + bool withinDeadspot = usecTimestampNow() - _state.triggerStartTime < POINTER_MOVE_DELAY && glm::distance2(pos2D, _state.triggerPos2D) < deadspotSquared; + if ((_state.triggering || _state.wasTriggering) && !_state.deadspotExpired && withinDeadspot) { pos2D = _state.triggerPos2D; intersection = _state.intersection; } else if (pickedID != target.objectID) { intersection = findIntersection(target, origin, direction); } + if (!withinDeadspot) { + _state.deadspotExpired = true; + } return PointerEvent(pos2D, intersection, surfaceNormal, direction); } - bool StylusPointer::isWithinBounds(float distance, float min, float max, float hysteresis) { return (distance == glm::clamp(distance, min - hysteresis, max + hysteresis)); } diff --git a/interface/src/raypick/StylusPointer.h b/interface/src/raypick/StylusPointer.h index 9c69915108..950b03b7c9 100644 --- a/interface/src/raypick/StylusPointer.h +++ b/interface/src/raypick/StylusPointer.h @@ -37,11 +37,11 @@ public: protected: PickedObject getHoveredObject(const PickResultPointer& pickResult) override; - Buttons getPressedButtons() override; + Buttons getPressedButtons(const PickResultPointer& pickResult) override; bool shouldHover(const PickResultPointer& pickResult) override; bool shouldTrigger(const PickResultPointer& pickResult) override; - PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, bool hover = true) const override; + PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, const std::string& button = "", bool hover = true) override; private: void show(const StylusTip& tip); @@ -53,7 +53,9 @@ private: glm::vec2 triggerPos2D { NAN }; glm::vec3 surfaceNormal { NAN }; quint64 triggerStartTime { 0 }; + bool deadspotExpired { true }; bool triggering { false }; + bool wasTriggering { false }; bool hovering { false }; }; diff --git a/libraries/pointers/src/Pointer.cpp b/libraries/pointers/src/Pointer.cpp index fcebb2a23f..5307e17355 100644 --- a/libraries/pointers/src/Pointer.cpp +++ b/libraries/pointers/src/Pointer.cpp @@ -11,6 +11,12 @@ #include "PickManager.h" #include "PointerManager.h" +#include "NumericalConstants.h" + +const float Pointer::POINTER_MOVE_DELAY = 0.33f * USECS_PER_SECOND; +const float TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.0481f; +const float Pointer::TOUCH_PRESS_TO_MOVE_DEADSPOT_SQUARED = TOUCH_PRESS_TO_MOVE_DEADSPOT * TOUCH_PRESS_TO_MOVE_DEADSPOT; + Pointer::~Pointer() { DependencyManager::get()->removePick(_pickUID); } @@ -77,7 +83,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin Buttons newButtons; Buttons sameButtons; if (_enabled && shouldTrigger(pickResult)) { - buttons = getPressedButtons(); + buttons = getPressedButtons(pickResult); for (const std::string& button : buttons) { if (_prevButtons.find(button) == _prevButtons.end()) { newButtons.insert(button); @@ -175,17 +181,6 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin } } - // send hoverEnd events if we disable the pointer or disable hovering - if (_hover && ((!_enabled && _prevEnabled) || (!doHover && _prevDoHover))) { - if (_prevHoveredObject.type == ENTITY) { - emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent); - } else if (_prevHoveredObject.type == OVERLAY) { - emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, hoveredEvent); - } else if (_prevHoveredObject.type == HUD) { - emit pointerManager->hoverEndHUD(hoveredEvent); - } - } - // Trigger begin const std::string SHOULD_FOCUS_BUTTON = "Focus"; for (const std::string& button : newButtons) { @@ -204,7 +199,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin // Trigger continue for (const std::string& button : sameButtons) { - PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button], pickResult, false); + PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button], pickResult, button, false); triggeredEvent.setID(pointerID); triggeredEvent.setType(PointerEvent::Move); triggeredEvent.setButton(chooseButton(button)); @@ -219,7 +214,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin // Trigger end for (const std::string& button : _prevButtons) { - PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button], pickResult, false); + PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button], pickResult, button, false); triggeredEvent.setID(pointerID); triggeredEvent.setType(PointerEvent::Release); triggeredEvent.setButton(chooseButton(button)); @@ -233,6 +228,17 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin _triggeredObjects.erase(button); } + // if we disable the pointer or disable hovering, send hoverEnd events after triggerEnd + if (_hover && ((!_enabled && _prevEnabled) || (!doHover && _prevDoHover))) { + if (_prevHoveredObject.type == ENTITY) { + emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent); + } else if (_prevHoveredObject.type == OVERLAY) { + emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, hoveredEvent); + } else if (_prevHoveredObject.type == HUD) { + emit pointerManager->hoverEndHUD(hoveredEvent); + } + } + _prevHoveredObject = hoveredObject; _prevButtons = buttons; _prevEnabled = _enabled; diff --git a/libraries/pointers/src/Pointer.h b/libraries/pointers/src/Pointer.h index 5ee55e7aeb..3197c80cad 100644 --- a/libraries/pointers/src/Pointer.h +++ b/libraries/pointers/src/Pointer.h @@ -82,14 +82,17 @@ protected: bool _enabled; bool _hover; - virtual PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, bool hover = true) const = 0; + virtual PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, const std::string& button = "", bool hover = true) = 0; virtual PickedObject getHoveredObject(const PickResultPointer& pickResult) = 0; - virtual Buttons getPressedButtons() = 0; + virtual Buttons getPressedButtons(const PickResultPointer& pickResult) = 0; virtual bool shouldHover(const PickResultPointer& pickResult) { return true; } virtual bool shouldTrigger(const PickResultPointer& pickResult) { return true; } + static const float POINTER_MOVE_DELAY; + static const float TOUCH_PRESS_TO_MOVE_DEADSPOT_SQUARED; + private: PickedObject _prevHoveredObject; Buttons _prevButtons; From d730b7b87b52d26a053a75ba19748b4369d25272 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Dec 2017 17:00:54 -0800 Subject: [PATCH 07/29] consistent goToUser behavior in PAL, and be able to go to flying user. --- interface/resources/qml/hifi/NameCard.qml | 13 +++---------- interface/src/avatar/MyAvatar.cpp | 14 ++++++++++++++ libraries/networking/src/AddressManager.cpp | 2 +- libraries/physics/src/CharacterController.cpp | 11 +++++++++++ libraries/physics/src/CharacterController.h | 2 ++ 5 files changed, 31 insertions(+), 11 deletions(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 1f9caf747a..308f72345d 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -597,18 +597,11 @@ Item { // Function body by Howard Stearns 2017-01-08 function goToUserInDomain(avatarUuid) { var avatar = AvatarList.getAvatar(avatarUuid); - if (!avatar) { + if (!avatar || !avatar.position || !avatar.orientation) { console.log("This avatar is no longer present. goToUserInDomain() failed."); return; } - // FIXME: We would like the avatar to recompute the avatar's "maybe fly" test at the new position, so that if high enough up, - // the avatar goes into fly mode rather than falling. However, that is not exposed to Javascript right now. - // FIXME: it would be nice if this used the same teleport steps and smoothing as in the teleport.js script. - // Note, however, that this script allows teleporting to a person in the air, while teleport.js is going to a grounded target. - // Position avatar 2 metres from the target in the direction that target avatar was facing. - MyAvatar.position = Vec3.sum(avatar.position, Vec3.multiplyQbyV(avatar.orientation, {x: 0, y: 0, z: -2})); - - // Rotate avatar on Y axis to face target avatar and cancel out any inherited roll and pitch. - MyAvatar.orientation = Quat.cancelOutRollAndPitch(Quat.multiply(avatar.orientation, {y: 1})); + // This is the last step of what AddressManager.goToUser does, but we don't need to resolve the username. + MyAvatar.goToLocation(avatar.position, true, Quat.cancelOutRollAndPitch(avatar.orientation), true) } } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 02a1959a95..832ec7d00e 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -424,6 +424,7 @@ void MyAvatar::update(float deltaTime) { emit positionGoneTo(); // Run safety tests as soon as we can after goToLocation, or clear if we're not colliding. _physicsSafetyPending = getCollisionsEnabled(); + _characterController.recomputeFlying(); // In case we've gone to into the sky. } if (_physicsSafetyPending && qApp->isPhysicsEnabled() && _characterController.isEnabledAndReady()) { // When needed and ready, arrange to check and fix. @@ -2315,6 +2316,19 @@ void MyAvatar::goToLocation(const glm::vec3& newPosition, bool hasOrientation, const glm::quat& newOrientation, bool shouldFaceLocation) { + // Most cases of going to a place or user go through this now. Some possible improvements to think about in the future: + // - It would be nice if this used the same teleport steps and smoothing as in the teleport.js script, as long as it + // still worked if the target is in the air. + // - Sometimes (such as the response from /api/v1/users/:username/location), the location can be stale, but there is a + // node_id supplied by which we could update the information after going to the stale location first and "looking around". + // This could be passed through AddressManager::goToAddressFromObject => AddressManager::handleViewpoint => here. + // The trick is that you have to yield enough time to resolve the node_id. + // - Instead of always doing the same thing for shouldFaceLocation -- which places users uncomfortabley on top of each other -- + // it would be nice to see how many users are already "at" a person or place, and place ourself in semicircle or other shape + // around the target. Avatars and entities (specified by the node_id) could define an adjustable "face me" method that would + // compute the position (e.g., so that if I'm on stage, going to me would compute an available seat in the audience rather than + // being in my face on-stage). Note that this could work for going to an entity as well as to a person. + qCDebug(interfaceapp).nospace() << "MyAvatar goToLocation - moving to " << newPosition.x << ", " << newPosition.y << ", " << newPosition.z; diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index b884dcba17..21815e065a 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -596,7 +596,7 @@ bool AddressManager::handleDomainID(const QString& host) { void AddressManager::handlePath(const QString& path, LookupTrigger trigger, bool wasPathOnly) { if (!handleViewpoint(path, false, trigger, wasPathOnly)) { qCDebug(networking) << "User entered path could not be handled as a viewpoint - " << path << - "- wll attempt to ask domain-server to resolve."; + "- will attempt to ask domain-server to resolve."; if (!wasPathOnly) { // if we received a path with a host then we need to remember what it was here so we can not diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 32e764bd10..f4107951b8 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -391,6 +391,10 @@ void CharacterController::setState(State desiredState) { } } +void CharacterController::recomputeFlying() { + _pendingFlags |= PENDING_FLAG_RECOMPUTE_FLYING; +} + void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const glm::vec3& scale) { float x = scale.x; float z = scale.z; @@ -657,6 +661,13 @@ void CharacterController::updateState() { if (!_dynamicsWorld) { return; } + if (_pendingFlags & PENDING_FLAG_RECOMPUTE_FLYING) { + setState(CharacterController::State::Hover, "recomputeFlying"); + _hasSupport = false; + _stepHeight = _minStepHeight; // clears memory of last step obstacle + _pendingFlags &= ~PENDING_FLAG_RECOMPUTE_FLYING; + } + const btScalar FLY_TO_GROUND_THRESHOLD = 0.1f * _radius; const btScalar GROUND_TO_FLY_THRESHOLD = 0.8f * _radius + _halfHeight; const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 250 * MSECS_PER_SECOND; diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 0f97cc7c16..96e479dcad 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -31,6 +31,7 @@ const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1; const uint32_t PENDING_FLAG_UPDATE_SHAPE = 1U << 2; const uint32_t PENDING_FLAG_JUMP = 1U << 3; const uint32_t PENDING_FLAG_UPDATE_COLLISION_GROUP = 1U << 4; +const uint32_t PENDING_FLAG_RECOMPUTE_FLYING = 1U << 5; const float DEFAULT_MIN_FLOOR_NORMAL_DOT_UP = cosf(PI / 3.0f); class btRigidBody; @@ -54,6 +55,7 @@ public: void setGravity(float gravity); float getGravity(); + void recomputeFlying(); virtual void updateShapeIfNecessary() = 0; From 813a94a79ed5f803aa870f4642eee00d95187cdb Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Dec 2017 17:12:44 -0800 Subject: [PATCH 08/29] function => macro to conditionally take debug arg --- libraries/physics/src/CharacterController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index f4107951b8..d39930ab76 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -662,7 +662,7 @@ void CharacterController::updateState() { return; } if (_pendingFlags & PENDING_FLAG_RECOMPUTE_FLYING) { - setState(CharacterController::State::Hover, "recomputeFlying"); + SET_STATE(CharacterController::State::Hover, "recomputeFlying"); _hasSupport = false; _stepHeight = _minStepHeight; // clears memory of last step obstacle _pendingFlags &= ~PENDING_FLAG_RECOMPUTE_FLYING; From bb499000a6ad7f19dfe232e22f6a3f6f0dcce8ab Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 19 Dec 2017 10:51:32 -0800 Subject: [PATCH 09/29] trailing semicolon --- interface/resources/qml/hifi/NameCard.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 308f72345d..3f5cad655d 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -602,6 +602,6 @@ Item { return; } // This is the last step of what AddressManager.goToUser does, but we don't need to resolve the username. - MyAvatar.goToLocation(avatar.position, true, Quat.cancelOutRollAndPitch(avatar.orientation), true) + MyAvatar.goToLocation(avatar.position, true, Quat.cancelOutRollAndPitch(avatar.orientation), true); } } From 901af8896f2c2b15a1cfc2bbeef2442dc581ecc7 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 3 Jan 2018 17:54:43 +1300 Subject: [PATCH 10/29] Add "Prefer Stylus Over Laser" setting --- interface/src/Application.cpp | 6 ++++++ interface/src/Application.h | 3 +++ interface/src/ui/PreferencesDialog.cpp | 5 +++++ 3 files changed, 14 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f17e06cb35..6bb8f886bc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -737,6 +737,7 @@ const float DEFAULT_HMD_TABLET_SCALE_PERCENT = 100.0f; const float DEFAULT_DESKTOP_TABLET_SCALE_PERCENT = 75.0f; const bool DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR = true; const bool DEFAULT_HMD_TABLET_BECOMES_TOOLBAR = false; +const bool DEFAULT_PREFER_STYLUS_OVER_LASER = false; const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = false; const QString DEFAULT_CURSOR_NAME = "DEFAULT"; @@ -756,6 +757,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _desktopTabletScale("desktopTabletScale", DEFAULT_DESKTOP_TABLET_SCALE_PERCENT), _desktopTabletBecomesToolbarSetting("desktopTabletBecomesToolbar", DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR), _hmdTabletBecomesToolbarSetting("hmdTabletBecomesToolbar", DEFAULT_HMD_TABLET_BECOMES_TOOLBAR), + _preferStylusOverLaserSetting("preferStylusOverLaser", DEFAULT_PREFER_STYLUS_OVER_LASER), _preferAvatarFingerOverStylusSetting("preferAvatarFingerOverStylus", DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS), _constrainToolbarPosition("toolbar/constrainToolbarToCenterX", true), _preferredCursor("preferredCursor", DEFAULT_CURSOR_NAME), @@ -2582,6 +2584,10 @@ void Application::setHmdTabletBecomesToolbarSetting(bool value) { updateSystemTabletMode(); } +void Application::setPreferStylusOverLaser(bool value) { + _preferStylusOverLaserSetting.set(value); +} + void Application::setPreferAvatarFingerOverStylus(bool value) { _preferAvatarFingerOverStylusSetting.set(value); } diff --git a/interface/src/Application.h b/interface/src/Application.h index ee16740f20..b8b6408273 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -207,6 +207,8 @@ public: void setDesktopTabletBecomesToolbarSetting(bool value); bool getHmdTabletBecomesToolbarSetting() { return _hmdTabletBecomesToolbarSetting.get(); } void setHmdTabletBecomesToolbarSetting(bool value); + bool getPreferStylusOverLaser() { return _preferStylusOverLaserSetting.get(); } + void setPreferStylusOverLaser(bool value); bool getPreferAvatarFingerOverStylus() { return _preferAvatarFingerOverStylusSetting.get(); } void setPreferAvatarFingerOverStylus(bool value); @@ -553,6 +555,7 @@ private: Setting::Handle _desktopTabletScale; Setting::Handle _desktopTabletBecomesToolbarSetting; Setting::Handle _hmdTabletBecomesToolbarSetting; + Setting::Handle _preferStylusOverLaserSetting; Setting::Handle _preferAvatarFingerOverStylusSetting; Setting::Handle _constrainToolbarPosition; Setting::Handle _preferredCursor; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 023d3e6e17..bf0c38847d 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -90,6 +90,11 @@ void setupPreferences() { preference->setMax(500); preferences->addPreference(preference); } + { + auto getter = []()->bool { return qApp->getPreferStylusOverLaser(); }; + auto setter = [](bool value) { qApp->setPreferStylusOverLaser(value); }; + preferences->addPreference(new CheckPreference(UI_CATEGORY, "Prefer Stylus Over Laser", getter, setter)); + } { auto getter = []()->bool { return qApp->getPreferAvatarFingerOverStylus(); }; auto setter = [](bool value) { qApp->setPreferAvatarFingerOverStylus(value); }; From 8d4a6f01cbb18c6615175c42f7f74f7df4148c57 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 3 Jan 2018 16:03:37 -0800 Subject: [PATCH 11/29] Fix bugs related to inspecting entities with hand controllers --- .../controllerModules/farActionGrabEntity.js | 53 +++++++++++++------ 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 0d421bbaec..ea625571b9 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -98,6 +98,7 @@ Script.include("/~/system/libraries/Xform.js"); this.targetObject = null; this.actionID = null; // action this script created... this.entityToLockOnto = null; + this.potentialEntityWithContextOverlay = false; this.entityWithContextOverlay = false; this.contextOverlayTimer = false; this.previousCollisionStatus = false; @@ -364,6 +365,7 @@ Script.include("/~/system/libraries/Xform.js"); if (this.entityWithContextOverlay) { ContextOverlay.destroyContextOverlay(this.entityWithContextOverlay); this.entityWithContextOverlay = false; + this.potentialEntityWithContextOverlay = false; } }; @@ -444,9 +446,13 @@ Script.include("/~/system/libraries/Xform.js"); this.targetObject = new TargetObject(entityID, targetProps); this.targetObject.parentProps = getEntityParents(targetProps); + + Script.clearTimeout(this.contextOverlayTimer); + this.contextOverlayTimer = false; if (entityID !== this.entityWithContextOverlay) { this.destroyContextOverlay(); } + var targetEntity = this.targetObject.getTargetEntity(); entityID = targetEntity.id; targetProps = targetEntity.props; @@ -470,26 +476,39 @@ Script.include("/~/system/libraries/Xform.js"); this.startFarGrabAction(controllerData, targetProps); } } - } else if (!this.entityWithContextOverlay && !this.contextOverlayTimer) { + } else if (!this.entityWithContextOverlay) { var _this = this; - _this.contextOverlayTimer = Script.setTimeout(function () { - if (!_this.entityWithContextOverlay && _this.contextOverlayTimer) { - var props = Entities.getEntityProperties(rayPickInfo.objectID); - var pointerEvent = { - type: "Move", - id: this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(rayPickInfo.objectID, rayPickInfo.intersection, props), - pos3D: rayPickInfo.intersection, - normal: rayPickInfo.surfaceNormal, - direction: Vec3.subtract(ZERO_VEC, rayPickInfo.surfaceNormal), - button: "Secondary" - }; - if (ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.objectID, pointerEvent)) { - _this.entityWithContextOverlay = rayPickInfo.objectID; - } + + if (_this.potentialEntityWithContextOverlay !== rayPickInfo.objectID) { + if (_this.contextOverlayTimer) { + Script.clearTimeout(_this.contextOverlayTimer); } _this.contextOverlayTimer = false; - }, 500); + _this.potentialEntityWithContextOverlay = rayPickInfo.objectID; + } + + if (!_this.contextOverlayTimer) { + _this.contextOverlayTimer = Script.setTimeout(function () { + if (!_this.entityWithContextOverlay && + _this.contextOverlayTimer && + _this.potentialEntityWithContextOverlay === rayPickInfo.objectID) { + var props = Entities.getEntityProperties(rayPickInfo.objectID); + var pointerEvent = { + type: "Move", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(rayPickInfo.objectID, rayPickInfo.intersection, props), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.surfaceNormal, + direction: Vec3.subtract(ZERO_VEC, rayPickInfo.surfaceNormal), + button: "Secondary" + }; + if (ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.objectID, pointerEvent)) { + _this.entityWithContextOverlay = rayPickInfo.objectID; + } + } + _this.contextOverlayTimer = false; + }, 500); + } } } else if (this.distanceRotating) { this.distanceRotate(otherFarGrabModule); From ff4f582c7d828f0af65164909d8a9706005fde07 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 3 Jan 2018 17:23:35 -0800 Subject: [PATCH 12/29] Thanks Howard --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 2 +- .../system/controllers/controllerModules/farActionGrabEntity.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 16b771567c..d690880f99 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -151,7 +151,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 normal; boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal); float offsetAngle = -CONTEXT_OVERLAY_OFFSET_ANGLE; - if (DependencyManager::get()->isLeftHand(event.getID())) { + if (event.getID() == 1) { // "1" is left hand offsetAngle *= -1.0f; } contextOverlayPosition = cameraPosition + diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index ea625571b9..4508153c03 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -495,7 +495,7 @@ Script.include("/~/system/libraries/Xform.js"); var props = Entities.getEntityProperties(rayPickInfo.objectID); var pointerEvent = { type: "Move", - id: this.hand + 1, // 0 is reserved for hardware mouse + id: _this.hand + 1, // 0 is reserved for hardware mouse pos2D: projectOntoEntityXYPlane(rayPickInfo.objectID, rayPickInfo.intersection, props), pos3D: rayPickInfo.intersection, normal: rayPickInfo.surfaceNormal, From efa6f1f02fdcead0a13981de851cd2d46d7a4d8d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 4 Jan 2018 15:00:17 +1300 Subject: [PATCH 13/29] Use "Prefer Stylus Over Laser" setting --- .../controllers/controllerModules/stylusInput.js | 5 ++++- .../controllerModules/webSurfaceLaserInput.js | 14 ++++++++++++-- scripts/system/controllers/controllerScripts.js | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/controllerModules/stylusInput.js b/scripts/system/controllers/controllerModules/stylusInput.js index d9fa0f76a9..aa65135289 100644 --- a/scripts/system/controllers/controllerModules/stylusInput.js +++ b/scripts/system/controllers/controllerModules/stylusInput.js @@ -142,7 +142,10 @@ Script.include("/~/system/libraries/controllers.js"); }; this.isReady = function (controllerData) { - if (this.processStylus(controllerData)) { + var PREFER_STYLUS_OVER_LASER = "preferStylusOverLaser"; + var isUsingStylus = Settings.getValue(PREFER_STYLUS_OVER_LASER, false); + + if (isUsingStylus && this.processStylus(controllerData)) { Pointers.enablePointer(this.pointer); return makeRunningValues(true, [], []); } else { diff --git a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js index 94cb2f86cb..4e9171f924 100644 --- a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js +++ b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js @@ -76,18 +76,28 @@ Script.include("/~/system/libraries/controllers.js"); } }; + this.updateAllwaysOn = function () { + var PREFER_STYLUS_OVER_LASER = "preferStylusOverLaser"; + this.parameters.handLaser.allwaysOn = !Settings.getValue(PREFER_STYLUS_OVER_LASER, false); + }; + this.isReady = function (controllerData) { var otherModuleRunning = this.getOtherModule().running; if ((this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData)) && !otherModuleRunning) { - return makeRunningValues(true, [], []); + this.updateAllwaysOn(); + if (this.parameters.handLaser.allwaysOn || controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE) { + return makeRunningValues(true, [], []); + } } return makeRunningValues(false, [], []); }; this.run = function (controllerData, deltaTime) { var grabModuleNeedsToRun = this.grabModuleWantsNearbyOverlay(controllerData); - if ((controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE || (this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData))) && !grabModuleNeedsToRun) { + if (!grabModuleNeedsToRun && (controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE + || this.parameters.handLaser.allwaysOn + && (this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData)))) { this.running = true; return makeRunningValues(true, [], []); } diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 1208b1ab83..1eb30bbefd 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -19,7 +19,7 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/nearParentGrabOverlay.js", "controllerModules/nearActionGrabEntity.js", "controllerModules/farActionGrabEntity.js", - //"controllerModules/stylusInput.js", + "controllerModules/stylusInput.js", "controllerModules/equipEntity.js", "controllerModules/nearTrigger.js", "controllerModules/webSurfaceLaserInput.js", From 138092ac3795a7e0f00ebae6e1bfa2801a8be005 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 4 Jan 2018 15:12:34 +1300 Subject: [PATCH 14/29] Remove "Prefer Avatar Finger Over Stylus" setting --- interface/src/Application.h | 4 +++- interface/src/ui/PreferencesDialog.cpp | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.h b/interface/src/Application.h index b8b6408273..212c70e6bb 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -209,7 +209,9 @@ public: void setHmdTabletBecomesToolbarSetting(bool value); bool getPreferStylusOverLaser() { return _preferStylusOverLaserSetting.get(); } void setPreferStylusOverLaser(bool value); - bool getPreferAvatarFingerOverStylus() { return _preferAvatarFingerOverStylusSetting.get(); } + // FIXME: Remove setting completely or make available through JavaScript API? + //bool getPreferAvatarFingerOverStylus() { return _preferAvatarFingerOverStylusSetting.get(); } + bool getPreferAvatarFingerOverStylus() { return false; } void setPreferAvatarFingerOverStylus(bool value); float getSettingConstrainToolbarPosition() { return _constrainToolbarPosition.get(); } diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index bf0c38847d..1a09af07ab 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -95,11 +95,14 @@ void setupPreferences() { auto setter = [](bool value) { qApp->setPreferStylusOverLaser(value); }; preferences->addPreference(new CheckPreference(UI_CATEGORY, "Prefer Stylus Over Laser", getter, setter)); } + // FIXME: Remove setting completely or make available through JavaScript API? + /* { auto getter = []()->bool { return qApp->getPreferAvatarFingerOverStylus(); }; auto setter = [](bool value) { qApp->setPreferAvatarFingerOverStylus(value); }; preferences->addPreference(new CheckPreference(UI_CATEGORY, "Prefer Avatar Finger Over Stylus", getter, setter)); } + */ { static const QString RETICLE_ICON_NAME = { Cursor::Manager::getIconName(Cursor::Icon::RETICLE) }; auto getter = []()->bool { return qApp->getPreferredCursor() == RETICLE_ICON_NAME; }; From 6c16fcfe651af9539f80a73a8b16bc12757eab5c Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 4 Jan 2018 16:37:20 +1300 Subject: [PATCH 15/29] Fix tablet laser when Shapes app is running --- .../controllerModules/inVREditMode.js | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/controllerModules/inVREditMode.js b/scripts/system/controllers/controllerModules/inVREditMode.js index e3035b26f2..d20872a17f 100644 --- a/scripts/system/controllers/controllerModules/inVREditMode.js +++ b/scripts/system/controllers/controllerModules/inVREditMode.js @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html /* global Script, MyAvatar, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule, - makeDispatcherModuleParameters, makeRunningValues, getEnabledModuleByName + makeDispatcherModuleParameters, makeRunningValues, getEnabledModuleByName, makeLaserParams */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -25,9 +25,17 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); ? ["rightHand", "rightHandEquip", "rightHandTrigger"] : ["leftHand", "leftHandEquip", "leftHandTrigger"], [], - 100 + 100, + makeLaserParams(this.hand, false) ); + this.pointingAtTablet = function (objectID) { + if (objectID === HMD.tabletScreenID || objectID === HMD.homeButtonID) { + return true; + } + return false; + }; + this.isReady = function (controllerData) { if (this.disableModules) { return makeRunningValues(true, [], []); @@ -42,7 +50,6 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); } // Tablet stylus. - // Includes the tablet laser. var tabletStylusInput = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightTabletStylusInput" : "LeftTabletStylusInput"); @@ -53,6 +60,18 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); } } + // Tablet surface. + var overlayLaser = getEnabledModuleByName(this.hand === RIGHT_HAND + ? "RightWebSurfaceLaserInput" + : "LeftWebSurfaceLaserInput"); + if (overlayLaser) { + var overlayLaserReady = overlayLaser.isReady(controllerData); + var target = controllerData.rayPicks[this.hand].objectID; + if (overlayLaserReady.active && this.pointingAtTablet(target)) { + return makeRunningValues(false, [], []); + } + } + // Tablet grabbing. var nearOverlay = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" From 6095a90e6972551f47eae10612c5aaa8249db7cc Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 4 Jan 2018 21:09:38 +1300 Subject: [PATCH 16/29] Don't display default lasers when Shapes app is active --- scripts/system/controllers/controllerModules/inVREditMode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerModules/inVREditMode.js b/scripts/system/controllers/controllerModules/inVREditMode.js index d20872a17f..49cffb5a4f 100644 --- a/scripts/system/controllers/controllerModules/inVREditMode.js +++ b/scripts/system/controllers/controllerModules/inVREditMode.js @@ -26,7 +26,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); : ["leftHand", "leftHandEquip", "leftHandTrigger"], [], 100, - makeLaserParams(this.hand, false) + makeLaserParams(-1, false) // Invalid hand parameter so that default laser is not displayed. ); this.pointingAtTablet = function (objectID) { From 02f4085368d51f18ec824ac07cd8a28457a85303 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 5 Jan 2018 09:00:53 +1300 Subject: [PATCH 17/29] Fix magic number --- scripts/system/controllers/controllerModules/inVREditMode.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerModules/inVREditMode.js b/scripts/system/controllers/controllerModules/inVREditMode.js index 49cffb5a4f..20501d6299 100644 --- a/scripts/system/controllers/controllerModules/inVREditMode.js +++ b/scripts/system/controllers/controllerModules/inVREditMode.js @@ -19,6 +19,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); function InVREditMode(hand) { this.hand = hand; this.disableModules = false; + var NO_HAND_LASER = -1; // Invalid hand parameter so that default laser is not displayed. this.parameters = makeDispatcherModuleParameters( 200, // Not too high otherwise the tablet laser doesn't work. this.hand === RIGHT_HAND @@ -26,7 +27,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); : ["leftHand", "leftHandEquip", "leftHandTrigger"], [], 100, - makeLaserParams(-1, false) // Invalid hand parameter so that default laser is not displayed. + makeLaserParams(NO_HAND_LASER, false) ); this.pointingAtTablet = function (objectID) { From 9f7a2b18e6cbe1fd5a4fc7614161685b6371fbc3 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 5 Jan 2018 10:04:39 +1300 Subject: [PATCH 18/29] Use laser from dominant hand if both hands are pointing at the tablet --- .../controllerModules/webSurfaceLaserInput.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js index 4e9171f924..eb9a426eeb 100644 --- a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js +++ b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js @@ -81,10 +81,15 @@ Script.include("/~/system/libraries/controllers.js"); this.parameters.handLaser.allwaysOn = !Settings.getValue(PREFER_STYLUS_OVER_LASER, false); }; + this.dominantHand = function () { + return MyAvatar.getDominantHand() === "right" ? 1 : 0; + }; + this.isReady = function (controllerData) { var otherModuleRunning = this.getOtherModule().running; - if ((this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData)) && - !otherModuleRunning) { + otherModuleRunning = otherModuleRunning && this.dominantHand() !== this.hand; + if (!otherModuleRunning + && (this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData))) { this.updateAllwaysOn(); if (this.parameters.handLaser.allwaysOn || controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE) { return makeRunningValues(true, [], []); @@ -94,8 +99,10 @@ Script.include("/~/system/libraries/controllers.js"); }; this.run = function (controllerData, deltaTime) { + var otherModuleRunning = this.getOtherModule().running; + otherModuleRunning = otherModuleRunning && this.dominantHand() !== this.hand; var grabModuleNeedsToRun = this.grabModuleWantsNearbyOverlay(controllerData); - if (!grabModuleNeedsToRun && (controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE + if (!otherModuleRunning && !grabModuleNeedsToRun && (controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE || this.parameters.handLaser.allwaysOn && (this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData)))) { this.running = true; From fb09a9724ec776c5f6f2d28a99ca49dbe63f2ea5 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 5 Jan 2018 11:04:09 +1300 Subject: [PATCH 19/29] Change laser hand with trigger squeeze --- .../controllerModules/webSurfaceLaserInput.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js index eb9a426eeb..453ad10618 100644 --- a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js +++ b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js @@ -81,17 +81,24 @@ Script.include("/~/system/libraries/controllers.js"); this.parameters.handLaser.allwaysOn = !Settings.getValue(PREFER_STYLUS_OVER_LASER, false); }; - this.dominantHand = function () { + this.getDominantHand = function() { return MyAvatar.getDominantHand() === "right" ? 1 : 0; }; + this.dominantHandOverride = false; + this.isReady = function (controllerData) { var otherModuleRunning = this.getOtherModule().running; - otherModuleRunning = otherModuleRunning && this.dominantHand() !== this.hand; - if (!otherModuleRunning + otherModuleRunning = otherModuleRunning && this.getDominantHand() !== this.hand; // Auto-swap to dominant hand. + var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE; + if ((!otherModuleRunning || isTriggerPressed) && (this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData))) { this.updateAllwaysOn(); - if (this.parameters.handLaser.allwaysOn || controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE) { + if (isTriggerPressed) { + this.dominantHandOverride = true; // Override dominant hand. + this.getOtherModule().dominantHandOverride = false; + } + if (this.parameters.handLaser.allwaysOn || isTriggerPressed) { return makeRunningValues(true, [], []); } } @@ -100,7 +107,8 @@ Script.include("/~/system/libraries/controllers.js"); this.run = function (controllerData, deltaTime) { var otherModuleRunning = this.getOtherModule().running; - otherModuleRunning = otherModuleRunning && this.dominantHand() !== this.hand; + otherModuleRunning = otherModuleRunning && this.getDominantHand() !== this.hand; // Auto-swap to dominant hand. + otherModuleRunning = otherModuleRunning || this.getOtherModule().dominantHandOverride; // Override dominant hand. var grabModuleNeedsToRun = this.grabModuleWantsNearbyOverlay(controllerData); if (!otherModuleRunning && !grabModuleNeedsToRun && (controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE || this.parameters.handLaser.allwaysOn @@ -110,6 +118,7 @@ Script.include("/~/system/libraries/controllers.js"); } this.deleteContextOverlay(); this.running = false; + this.dominantHandOverride = false; return makeRunningValues(false, [], []); }; } From 27bed8d1f17ceddd13dbcd87118a757926df4e4f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 5 Jan 2018 11:05:49 +1300 Subject: [PATCH 20/29] Tidying --- .../controllerModules/webSurfaceLaserInput.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js index 453ad10618..fa4ba3544d 100644 --- a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js +++ b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js @@ -65,7 +65,8 @@ Script.include("/~/system/libraries/controllers.js"); }; this.deleteContextOverlay = function() { - var farGrabModule = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightFarActionGrabEntity" : "LeftFarActionGrabEntity"); + var farGrabModule = getEnabledModuleByName(this.hand === RIGHT_HAND + ? "RightFarActionGrabEntity" : "LeftFarActionGrabEntity"); if (farGrabModule) { var entityWithContextOverlay = farGrabModule.entityWithContextOverlay; @@ -76,7 +77,7 @@ Script.include("/~/system/libraries/controllers.js"); } }; - this.updateAllwaysOn = function () { + this.updateAllwaysOn = function() { var PREFER_STYLUS_OVER_LASER = "preferStylusOverLaser"; this.parameters.handLaser.allwaysOn = !Settings.getValue(PREFER_STYLUS_OVER_LASER, false); }; @@ -87,7 +88,7 @@ Script.include("/~/system/libraries/controllers.js"); this.dominantHandOverride = false; - this.isReady = function (controllerData) { + this.isReady = function(controllerData) { var otherModuleRunning = this.getOtherModule().running; otherModuleRunning = otherModuleRunning && this.getDominantHand() !== this.hand; // Auto-swap to dominant hand. var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE; @@ -105,14 +106,14 @@ Script.include("/~/system/libraries/controllers.js"); return makeRunningValues(false, [], []); }; - this.run = function (controllerData, deltaTime) { + this.run = function(controllerData, deltaTime) { var otherModuleRunning = this.getOtherModule().running; otherModuleRunning = otherModuleRunning && this.getDominantHand() !== this.hand; // Auto-swap to dominant hand. otherModuleRunning = otherModuleRunning || this.getOtherModule().dominantHandOverride; // Override dominant hand. var grabModuleNeedsToRun = this.grabModuleWantsNearbyOverlay(controllerData); if (!otherModuleRunning && !grabModuleNeedsToRun && (controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE || this.parameters.handLaser.allwaysOn - && (this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData)))) { + && (this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData)))) { this.running = true; return makeRunningValues(true, [], []); } From f0985a8a01e8010d43b93b4f09ccd0543dafb1e4 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 4 Jan 2018 14:21:53 -0800 Subject: [PATCH 21/29] Code review feedback clamp _rotationConstant and _translationConstante between 0 and 1. re-organized apply structure, to prevent transforming invalid values. --- .../filters/ExponentialSmoothingFilter.cpp | 43 +++++++++++-------- .../impl/filters/ExponentialSmoothingFilter.h | 2 +- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.cpp b/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.cpp index 355c45ced9..9cf2673d55 100644 --- a/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.cpp +++ b/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.cpp @@ -21,27 +21,36 @@ namespace controller { Pose ExponentialSmoothingFilter::apply(Pose value) const { - // to perform filtering in sensor space, we need to compute the transformations. - auto userInputMapper = DependencyManager::get(); - const InputCalibrationData calibrationData = userInputMapper->getInputCalibrationData(); - glm::mat4 sensorToAvatarMat = glm::inverse(calibrationData.avatarMat) * calibrationData.sensorToWorldMat; - glm::mat4 avatarToSensorMat = glm::inverse(calibrationData.sensorToWorldMat) * calibrationData.avatarMat; + if (value.isValid()) { - // transform pose into sensor space. - Pose sensorValue = value.transform(avatarToSensorMat); + // to perform filtering in sensor space, we need to compute the transformations. + auto userInputMapper = DependencyManager::get(); + const InputCalibrationData calibrationData = userInputMapper->getInputCalibrationData(); + glm::mat4 sensorToAvatarMat = glm::inverse(calibrationData.avatarMat) * calibrationData.sensorToWorldMat; + glm::mat4 avatarToSensorMat = glm::inverse(calibrationData.sensorToWorldMat) * calibrationData.avatarMat; - if (value.isValid() && _oldSensorValue.isValid()) { + // transform pose into sensor space. + Pose sensorValue = value.transform(avatarToSensorMat); - // exponential smoothing filter - sensorValue.translation = _translationConstant * sensorValue.getTranslation() + (1.0f - _translationConstant) * _oldSensorValue.getTranslation(); - sensorValue.rotation = safeMix(sensorValue.getRotation(), _oldSensorValue.getRotation(), _rotationConstant); + if (_prevSensorValue.isValid()) { + // exponential smoothing filter + sensorValue.translation = _translationConstant * sensorValue.getTranslation() + (1.0f - _translationConstant) * _prevSensorValue.getTranslation(); + sensorValue.rotation = safeMix(sensorValue.getRotation(), _prevSensorValue.getRotation(), _rotationConstant); - _oldSensorValue = sensorValue; + // remember previous sensor space value. + _prevSensorValue = sensorValue; - // transform back into avatar space - return sensorValue.transform(sensorToAvatarMat); + // transform back into avatar space + return sensorValue.transform(sensorToAvatarMat); + } else { + // remember previous sensor space value. + _prevSensorValue = sensorValue; + + // no previous value to smooth with, so return value unchanged + return value; + } } else { - _oldSensorValue = sensorValue; + // return invalid value unchanged return value; } } @@ -51,8 +60,8 @@ namespace controller { if (parameters.isObject()) { auto obj = parameters.toObject(); if (obj.contains(JSON_ROTATION) && obj.contains(JSON_TRANSLATION)) { - _rotationConstant = obj[JSON_ROTATION].toDouble(); - _translationConstant = obj[JSON_TRANSLATION].toDouble(); + _rotationConstant = glm::clamp((float)obj[JSON_ROTATION].toDouble(), 0.0f, 1.0f); + _translationConstant = glm::clamp((float)obj[JSON_TRANSLATION].toDouble(), 0.0f, 1.0f); return true; } } diff --git a/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.h b/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.h index d2ce96deca..134f57243e 100644 --- a/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.h @@ -34,7 +34,7 @@ namespace controller { float _translationConstant { 0.375f }; float _rotationConstant { 0.375f }; - mutable Pose _oldSensorValue { Pose() }; // sensor space + mutable Pose _prevSensorValue { Pose() }; // sensor space }; } From 67eb282815430e4c48726eb6f18d432a6579b6b3 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 5 Jan 2018 12:12:36 +1300 Subject: [PATCH 22/29] Don't swap hands if both triggers are squeezed --- .../controllers/controllerModules/webSurfaceLaserInput.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js index fa4ba3544d..3d9d7979d5 100644 --- a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js +++ b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js @@ -18,6 +18,7 @@ Script.include("/~/system/libraries/controllers.js"); (function() { function WebSurfaceLaserInput(hand) { this.hand = hand; + this.otherHand = this.hand === RIGHT_HAND ? LEFT_HAND : RIGHT_HAND; this.running = false; this.parameters = makeDispatcherModuleParameters( @@ -91,7 +92,8 @@ Script.include("/~/system/libraries/controllers.js"); this.isReady = function(controllerData) { var otherModuleRunning = this.getOtherModule().running; otherModuleRunning = otherModuleRunning && this.getDominantHand() !== this.hand; // Auto-swap to dominant hand. - var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE; + var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE + && controllerData.triggerValues[this.otherHand] <= TRIGGER_OFF_VALUE; if ((!otherModuleRunning || isTriggerPressed) && (this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData))) { this.updateAllwaysOn(); From 623a29b14a7b08ed8052f149b0cd6ef740936a9f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 4 Jan 2018 16:48:06 -0800 Subject: [PATCH 23/29] add ownership infection for constraints --- libraries/physics/src/PhysicsEngine.cpp | 53 ++++++++++++++++++++++++- libraries/physics/src/PhysicsEngine.h | 1 + 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index fe794772e2..2d84f0cef1 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -317,6 +317,7 @@ void PhysicsEngine::stepSimulation() { auto onSubStep = [this]() { this->updateContactMap(); + this->doOwnershipInfectionForConstraints(); }; int numSubsteps = _dynamicsWorld->stepSimulationWithSubstepCallback(timeStep, PHYSICS_ENGINE_MAX_NUM_SUBSTEPS, @@ -451,7 +452,7 @@ void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const // NOTE: we might own the simulation of a kinematic object (A) // but we don't claim ownership of kinematic objects (B) based on collisions here. if (!objectB->isStaticOrKinematicObject() && motionStateB->getSimulatorID() != Physics::getSessionUUID()) { - quint8 priorityA = motionStateA ? motionStateA->getSimulationPriority() : PERSONAL_SIMULATION_PRIORITY; + uint8_t priorityA = motionStateA ? motionStateA->getSimulationPriority() : PERSONAL_SIMULATION_PRIORITY; motionStateB->bump(priorityA); } } else if (motionStateA && @@ -460,7 +461,7 @@ void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const // SIMILARLY: we might own the simulation of a kinematic object (B) // but we don't claim ownership of kinematic objects (A) based on collisions here. if (!objectA->isStaticOrKinematicObject() && motionStateA->getSimulatorID() != Physics::getSessionUUID()) { - quint8 priorityB = motionStateB ? motionStateB->getSimulationPriority() : PERSONAL_SIMULATION_PRIORITY; + uint8_t priorityB = motionStateB ? motionStateB->getSimulationPriority() : PERSONAL_SIMULATION_PRIORITY; motionStateA->bump(priorityB); } } @@ -501,6 +502,54 @@ void PhysicsEngine::updateContactMap() { } } +void PhysicsEngine::doOwnershipInfectionForConstraints() { + BT_PROFILE("ownershipInfectionForConstraints"); + const btCollisionObject* characterObject = _myAvatarController ? _myAvatarController->getCollisionObject() : nullptr; + foreach(const auto& dynamic, _objectDynamics) { + if (!dynamic) { + continue; + } + QList bodies = std::static_pointer_cast(dynamic)->getRigidBodies(); + if (bodies.size() > 1) { + int32_t numOwned = 0; + int32_t numStatic = 0; + uint8_t priority = VOLUNTEER_SIMULATION_PRIORITY; + foreach(btRigidBody* body, bodies) { + ObjectMotionState* motionState = static_cast(body->getUserPointer()); + if (body->isStaticObject()) { + ++numStatic; + } else if (motionState->getType() == MOTIONSTATE_TYPE_AVATAR) { + // we can never take ownership of this constraint + numOwned = 0; + break; + } else { + if (motionState && motionState->getSimulatorID() == Physics::getSessionUUID()) { + priority = glm::max(priority, motionState->getSimulationPriority()); + } else if (body == characterObject) { + priority = glm::max(priority, PERSONAL_SIMULATION_PRIORITY); + } + numOwned++; + } + } + + if (numOwned > 0) { + if (numOwned + numStatic != bodies.size()) { + // we have partial ownership but it isn't complete so we walk each object + // and bump the simulation priority to the highest priority we encountered earlier + foreach(btRigidBody* body, bodies) { + ObjectMotionState* motionState = static_cast(body->getUserPointer()); + if (motionState) { + // NOTE: we submit priority+1 because the default behavior of bump() is to actually use priority - 1 + // and we want all priorities of the objects to be at the SAME level + motionState->bump(priority + 1); + } + } + } + } + } + } +} + const CollisionEvents& PhysicsEngine::getCollisionEvents() { _collisionEvents.clear(); diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 6619a5489d..92d2e6885a 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -64,6 +64,7 @@ public: void harvestPerformanceStats(); void printPerformanceStatsToFile(const QString& filename); void updateContactMap(); + void doOwnershipInfectionForConstraints(); bool hasOutgoingChanges() const { return _hasOutgoingChanges; } From 936b5f357194797eb9fcfdeb3a075d90a95f8114 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 5 Jan 2018 16:15:59 +1300 Subject: [PATCH 24/29] Laser on tablet bezel as well as screen and "x" button --- scripts/system/controllers/controllerDispatcher.js | 12 ++++++------ scripts/system/libraries/WebTablet.js | 3 ++- scripts/system/libraries/utils.js | 3 ++- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 51f927f224..4bce730695 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -145,11 +145,11 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); return deltaTime; }; - this.setIgnoreTablet = function() { + this.setIgnorePointerItems = function() { if (HMD.tabletID !== this.tabletID) { this.tabletID = HMD.tabletID; - Pointers.setIgnoreItems(_this.leftPointer, _this.blacklist.concat([HMD.tabletID])); - Pointers.setIgnoreItems(_this.rightPointer, _this.blacklist.concat([HMD.tabletID])); + Pointers.setIgnoreItems(_this.leftPointer, _this.blacklist); + Pointers.setIgnoreItems(_this.rightPointer, _this.blacklist); } }; @@ -168,7 +168,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); } var sensorScaleFactor = MyAvatar.sensorToWorldScale; var deltaTime = _this.updateTimings(); - _this.setIgnoreTablet(); + _this.setIgnorePointerItems(); if (controllerDispatcherPluginsNeedSort) { _this.orderedPluginNames = []; @@ -388,8 +388,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); }; this.setBlacklist = function() { - RayPick.setIgnoreItems(_this.leftControllerRayPick, this.blacklist.concat(HMD.tabletID)); - RayPick.setIgnoreItems(_this.rightControllerRayPick, this.blacklist.concat(HMD.tabletID)); + RayPick.setIgnoreItems(_this.leftControllerRayPick, this.blacklist); + RayPick.setIgnoreItems(_this.rightControllerRayPick, this.blacklist); }; var MAPPING_NAME = "com.highfidelity.controllerDispatcher"; diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 4217ec503e..05b4963280 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -118,7 +118,8 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { Overlays.deleteOverlay(this.webOverlayID); } - var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2.0) / sensorScaleFactor; + var RAYPICK_OFFSET = 0.0001; // Sufficient for raypick to reliably intersect tablet screen before tablet model. + var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2.0) / sensorScaleFactor + RAYPICK_OFFSET; var WEB_ENTITY_Y_OFFSET = 0.004; var screenWidth = 0.82 * tabletWidth; var screenHeight = 0.81 * tabletHeight; diff --git a/scripts/system/libraries/utils.js b/scripts/system/libraries/utils.js index 4a1fcdf301..4a3e54a5af 100644 --- a/scripts/system/libraries/utils.js +++ b/scripts/system/libraries/utils.js @@ -400,7 +400,8 @@ resizeTablet = function (width, newParentJointIndex, sensorToWorldScaleOverride) }); // update webOverlay - var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2.0) * sensorScaleOffsetOverride; + var RAYPICK_OFFSET = 0.0001; // Sufficient for raypick to reliably intersect tablet screen before tablet model. + var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2.0) * sensorScaleOffsetOverride + RAYPICK_OFFSET; var WEB_ENTITY_Y_OFFSET = 0.004 * sensorScaleFactor * sensorScaleOffsetOverride; var screenWidth = 0.82 * tabletWidth; var screenHeight = 0.81 * tabletHeight; From 4b2ed44322add666005c44b1ff680a3e70866630 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 5 Jan 2018 16:16:42 +1300 Subject: [PATCH 25/29] Reduce incidence of tablet screen being rotated w.r.t. model --- scripts/system/libraries/utils.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/system/libraries/utils.js b/scripts/system/libraries/utils.js index 4a3e54a5af..a6e2751a83 100644 --- a/scripts/system/libraries/utils.js +++ b/scripts/system/libraries/utils.js @@ -418,11 +418,13 @@ resizeTablet = function (width, newParentJointIndex, sensorToWorldScaleOverride) var homeButtonDim = 4.0 * tabletScaleFactor / 3.0; Overlays.editOverlay(HMD.homeButtonID, { localPosition: { x: 0, y: -HOME_BUTTON_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET }, + localRotation: Quat.angleAxis(180, Vec3.UNIT_Y), dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim } }); Overlays.editOverlay(HMD.homeButtonHighlightID, { localPosition: { x: 0, y: -HOME_BUTTON_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET }, + localRotation: Quat.angleAxis(180, Vec3.UNIT_Y), dimensions: { x: homeButtonDim, y: homeButtonDim, z: homeButtonDim } }); }; From 69dff9ac09e97d71c78870c191d422a921986fce Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 5 Jan 2018 16:16:55 +1300 Subject: [PATCH 26/29] Remove unused code --- scripts/system/controllers/controllerDispatcher.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 4bce730695..16f1d086b7 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -182,16 +182,6 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); controllerDispatcherPlugins[b].parameters.priority; }); - var output = "controllerDispatcher -- new plugin order: "; - for (var k = 0; k < _this.orderedPluginNames.length; k++) { - var dbgPluginName = _this.orderedPluginNames[k]; - var priority = controllerDispatcherPlugins[dbgPluginName].parameters.priority; - output += dbgPluginName + ":" + priority; - if (k + 1 < _this.orderedPluginNames.length) { - output += ", "; - } - } - controllerDispatcherPluginsNeedSort = false; } From 437b78dce5ae961ed1f47c3f6e6b52d4be77604d Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 5 Jan 2018 13:35:19 +0300 Subject: [PATCH 27/29] FB10621 "HIFI-Commerce Login" page title change to "Log in to continue" --- interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml b/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml index 404d7e84cf..f6875bb06f 100644 --- a/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml +++ b/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml @@ -53,7 +53,7 @@ Item { // Title Bar text RalewaySemiBold { - text: "HIFI COMMERCE - LOGIN"; + text: "Log in to continue"; // Text size size: hifi.fontSizes.overlayTitle; // Anchors From 5431b7137b2fef5db92882762020f2adf9eed452 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 6 Jan 2018 08:27:24 +1300 Subject: [PATCH 28/29] Code review --- scripts/system/controllers/controllerModules/inEditMode.js | 5 +---- scripts/system/controllers/controllerModules/inVREditMode.js | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index 951c3a969e..763258573d 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -44,10 +44,7 @@ Script.include("/~/system/libraries/utils.js"); }; this.pointingAtTablet = function(objectID) { - if (objectID === HMD.tabletScreenID || objectID === HMD.homeButtonID) { - return true; - } - return false; + return objectID === HMD.tabletScreenID || objectID === HMD.homeButtonID; }; this.sendPickData = function(controllerData) { diff --git a/scripts/system/controllers/controllerModules/inVREditMode.js b/scripts/system/controllers/controllerModules/inVREditMode.js index 20501d6299..38eca65dd3 100644 --- a/scripts/system/controllers/controllerModules/inVREditMode.js +++ b/scripts/system/controllers/controllerModules/inVREditMode.js @@ -31,10 +31,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); ); this.pointingAtTablet = function (objectID) { - if (objectID === HMD.tabletScreenID || objectID === HMD.homeButtonID) { - return true; - } - return false; + return objectID === HMD.tabletScreenID || objectID === HMD.homeButtonID; }; this.isReady = function (controllerData) { From 243dcb88ee8bfdc59ffaa9f0237e1ae1a8d6234a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 5 Jan 2018 16:20:44 -0800 Subject: [PATCH 29/29] Bug fix for sit script. --- interface/src/avatar/MyAvatar.cpp | 8 ++++++++ interface/src/avatar/MyAvatar.h | 4 ++-- interface/src/avatar/MySkeletonModel.cpp | 15 ++++++++++++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 02a1959a95..84ede46589 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3152,6 +3152,7 @@ glm::mat4 MyAvatar::getLeftHandCalibrationMat() const { } bool MyAvatar::pinJoint(int index, const glm::vec3& position, const glm::quat& orientation) { + std::lock_guard guard(_pinnedJointsMutex); auto hipsIndex = getJointIndex("Hips"); if (index != hipsIndex) { qWarning() << "Pinning is only supported for the hips joint at the moment."; @@ -3171,7 +3172,14 @@ bool MyAvatar::pinJoint(int index, const glm::vec3& position, const glm::quat& o return true; } +bool MyAvatar::isJointPinned(int index) { + std::lock_guard guard(_pinnedJointsMutex); + auto it = std::find(_pinnedJoints.begin(), _pinnedJoints.end(), index); + return it != _pinnedJoints.end(); +} + bool MyAvatar::clearPinOnJoint(int index) { + std::lock_guard guard(_pinnedJointsMutex); auto it = std::find(_pinnedJoints.begin(), _pinnedJoints.end(), index); if (it != _pinnedJoints.end()) { _pinnedJoints.erase(it); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 7c9513cb3e..ab74460d4e 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -448,9 +448,8 @@ public: virtual void clearJointData(const QString& name) override; virtual void clearJointsData() override; - - Q_INVOKABLE bool pinJoint(int index, const glm::vec3& position, const glm::quat& orientation); + bool isJointPinned(int index); Q_INVOKABLE bool clearPinOnJoint(int index); Q_INVOKABLE float getIKErrorOnLastSolve() const; @@ -837,6 +836,7 @@ private: bool getIsAway() const { return _isAway; } void setAway(bool value); + std::mutex _pinnedJointsMutex; std::vector _pinnedJoints; // height of user in sensor space, when standing erect. diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index f249be33ea..50e0474831 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -34,12 +34,25 @@ Rig::CharacterControllerState convertCharacterControllerState(CharacterControlle } static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { + + glm::mat4 worldToSensorMat = glm::inverse(myAvatar->getSensorToWorldMatrix()); + + // check for pinned hips. + auto hipsIndex = myAvatar->getJointIndex("Hips"); + if (myAvatar->isJointPinned(hipsIndex)) { + Transform avatarTransform = myAvatar->getTransform(); + AnimPose result = AnimPose(worldToSensorMat * avatarTransform.getMatrix() * Matrices::Y_180); + result.scale() = glm::vec3(1.0f, 1.0f, 1.0f); + return result; + } else { + DebugDraw::getInstance().removeMarker("pinnedHips"); + } + glm::mat4 hipsMat = myAvatar->deriveBodyFromHMDSensor(); glm::vec3 hipsPos = extractTranslation(hipsMat); glm::quat hipsRot = glmExtractRotation(hipsMat); glm::mat4 avatarToWorldMat = myAvatar->getTransform().getMatrix(); - glm::mat4 worldToSensorMat = glm::inverse(myAvatar->getSensorToWorldMatrix()); glm::mat4 avatarToSensorMat = worldToSensorMat * avatarToWorldMat; // dampen hips rotation, by mixing it with the avatar orientation in sensor space