From a5b32a64069f629dbc07a1dcfc5916895d5262cb Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Fri, 14 Jun 2024 20:15:24 -0700 Subject: [PATCH] allow configurable delay --- .../src/raypick/PickScriptingInterface.cpp | 13 ++++++++++ .../src/raypick/PickScriptingInterface.h | 21 ++++++++++++++++ .../src/raypick/PointerScriptingInterface.h | 8 +++++++ interface/src/raypick/RayPick.cpp | 16 +++++++++++-- interface/src/raypick/RayPick.h | 2 ++ interface/src/ui/PreferencesDialog.cpp | 24 ++++++++++++++----- libraries/pointers/src/Pick.h | 2 ++ libraries/pointers/src/PickManager.cpp | 7 ++++++ libraries/pointers/src/PickManager.h | 1 + libraries/pointers/src/Pointer.cpp | 4 ++++ libraries/pointers/src/Pointer.h | 1 + libraries/pointers/src/PointerManager.cpp | 7 ++++++ libraries/pointers/src/PointerManager.h | 1 + .../controllers/controllerDispatcher.js | 21 ++++++++++------ 14 files changed, 113 insertions(+), 15 deletions(-) diff --git a/interface/src/raypick/PickScriptingInterface.cpp b/interface/src/raypick/PickScriptingInterface.cpp index 5538252d57..5323c52faf 100644 --- a/interface/src/raypick/PickScriptingInterface.cpp +++ b/interface/src/raypick/PickScriptingInterface.cpp @@ -463,6 +463,10 @@ void PickScriptingInterface::setIncludeItems(unsigned int uid, const ScriptValue DependencyManager::get()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems)); } +void PickScriptingInterface::setDelay(unsigned int uid, float delay) { + DependencyManager::get()->setDelay(uid, delay); +} + bool PickScriptingInterface::isLeftHand(unsigned int uid) { return DependencyManager::get()->isLeftHand(uid); } @@ -505,6 +509,15 @@ void PickScriptingInterface::setPerFrameTimeBudget(unsigned int numUsecs) { DependencyManager::get()->setPerFrameTimeBudget(numUsecs); } +float PickScriptingInterface::getHandLaserDelay() const { + return _handLaserDelaySetting.get(); +} + +void PickScriptingInterface::setHandLaserDelay(float delay) { + _handLaserDelaySetting.set(delay); + emit handLaserDelayChanged(delay); +} + void PickScriptingInterface::setParentTransform(std::shared_ptr pick, const QVariantMap& propMap) { QUuid parentUuid; int parentJointIndex = 0; diff --git a/interface/src/raypick/PickScriptingInterface.h b/interface/src/raypick/PickScriptingInterface.h index e6af97e662..00d5420f86 100644 --- a/interface/src/raypick/PickScriptingInterface.h +++ b/interface/src/raypick/PickScriptingInterface.h @@ -16,6 +16,7 @@ #include #include #include +#include class ScriptEngine; class ScriptValue; @@ -72,6 +73,7 @@ class ScriptValue; * @property {IntersectionType} INTERSECTED_HUD - Intersected the HUD surface. Read-only. * * @property {number} perFrameTimeBudget - The maximum time, in microseconds, to spend per frame updating pick results. + * @property {number} handLaserDelay - The delay, in seconds, applied to the hand lasers to smooth their movement. */ class PickScriptingInterface : public QObject, public Dependency { @@ -105,6 +107,7 @@ class PickScriptingInterface : public QObject, public Dependency { Q_PROPERTY(unsigned int INTERSECTED_AVATAR READ getIntersectedAvatar CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_HUD READ getIntersectedHud CONSTANT) Q_PROPERTY(unsigned int perFrameTimeBudget READ getPerFrameTimeBudget WRITE setPerFrameTimeBudget) + Q_PROPERTY(float handLaserDelay READ getHandLaserDelay WRITE setHandLaserDelay) SINGLETON_DEPENDENCY public: @@ -263,6 +266,15 @@ public: */ Q_INVOKABLE void setIncludeItems(unsigned int uid, const ScriptValue& includeItems); + /*@jsdoc + * Sets the delay of a Ray pick. + *

Note: Not used by other pick types.

+ * @function Picks.setDelay + * @param {number} id - The ID of the pointer. + * @param {number} delay - The desired delay in seconds. + */ + Q_INVOKABLE void setDelay(unsigned int uid, float delay); + /*@jsdoc * Checks if a pick is associated with the left hand: a ray or parabola pick with joint property set to * "_CONTROLLER_LEFTHAND" or "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", or a stylus pick with @@ -295,6 +307,9 @@ public: unsigned int getPerFrameTimeBudget() const; void setPerFrameTimeBudget(unsigned int numUsecs); + float getHandLaserDelay() const; + void setHandLaserDelay(float delay); + public slots: static constexpr unsigned int getPickBypassIgnore() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_BYPASS_IGNORE); } @@ -461,6 +476,9 @@ public slots: */ static constexpr unsigned int getIntersectedHud() { return IntersectionType::HUD; } +signals: + void handLaserDelayChanged(float delay); + protected: static std::shared_ptr buildRayPick(const QVariantMap& properties); static std::shared_ptr buildStylusPick(const QVariantMap& properties); @@ -468,6 +486,9 @@ protected: static std::shared_ptr buildParabolaPick(const QVariantMap& properties); static void setParentTransform(std::shared_ptr pick, const QVariantMap& propMap); + +private: + Setting::Handle _handLaserDelaySetting { "handLaserDelay", 0.0f }; }; #endif // hifi_PickScriptingInterface_h diff --git a/interface/src/raypick/PointerScriptingInterface.h b/interface/src/raypick/PointerScriptingInterface.h index 080067e9ae..b45bd46dcf 100644 --- a/interface/src/raypick/PointerScriptingInterface.h +++ b/interface/src/raypick/PointerScriptingInterface.h @@ -423,6 +423,14 @@ public: */ Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isAvatar, const glm::mat4& offsetMat = glm::mat4()) const { DependencyManager::get()->setLockEndUUID(uid, objectID, isAvatar, offsetMat); } + /*@jsdoc + * Sets the delay of a Ray pointer. + *

Note: Not used by stylus or parabola pointers.

+ * @function Pointers.setDelay + * @param {number} id - The ID of the pointer. + * @param {number} delay - The desired delay in seconds. + */ + Q_INVOKABLE void setDelay(unsigned int uid, float delay) const { DependencyManager::get()->setDelay(uid, delay); } /*@jsdoc * Checks if a pointer is associated with the left hand: a ray or parabola pointer with joint property set to diff --git a/interface/src/raypick/RayPick.cpp b/interface/src/raypick/RayPick.cpp index 274e341ae8..6237dcba90 100644 --- a/interface/src/raypick/RayPick.cpp +++ b/interface/src/raypick/RayPick.cpp @@ -19,14 +19,18 @@ PickRay RayPick::getMathematicalPick() const { return _mathPick; } - const bool hasDelay = _prevUpdate != 0 && _delayHalf > 0.0f && !isNaN(_prevDirection.x); + float delayHalf = 0.0f; + withReadLock([&] { + delayHalf = _delayHalf; + }); + const bool hasDelay = _prevUpdate != 0 && delayHalf > 0.0f && !isNaN(_prevDirection.x); const float now = secTimestampNow(); const float dt = now - _prevUpdate; float alpha = 0.0f; if (hasDelay) { // This equation gives a framerate-independent lerp for a moving target // https://twitter.com/FreyaHolmer/status/1757836988495847568 - alpha = 1 - exp2(-dt / _delayHalf); + alpha = 1 - exp2(-dt / delayHalf); } Transform currentParentTransform = parentTransform->getTransform(); @@ -94,6 +98,14 @@ PickResultPointer RayPick::getHUDIntersection(const PickRay& pick) { return std::make_shared(IntersectionType::HUD, QUuid(), glm::distance(pick.origin, hudRes), hudRes, pick); } +void RayPick::setDelay(float delay) { + withWriteLock([&] { + // We want to be within 0.1% of the target in seconds + // https://twitter.com/FreyaHolmer/status/1757836988495847568 + _delayHalf = -std::max(delay, 0.0f) / log2(0.001); + }); +} + Transform RayPick::getResultTransform() const { PickResultPointer result = getPrevPickResult(); if (!result) { diff --git a/interface/src/raypick/RayPick.h b/interface/src/raypick/RayPick.h index 966bc1d11d..c11f2ce835 100644 --- a/interface/src/raypick/RayPick.h +++ b/interface/src/raypick/RayPick.h @@ -97,6 +97,8 @@ public: PickResultPointer getHUDIntersection(const PickRay& pick) override; Transform getResultTransform() const override; + void setDelay(float delay) override; + // These are helper functions for projecting and intersecting rays static glm::vec3 intersectRayWithEntityXYPlane(const QUuid& entityID, const glm::vec3& origin, const glm::vec3& direction); static glm::vec2 projectOntoEntityXYPlane(const QUuid& entityID, const glm::vec3& worldPos, bool unNormalized = true); diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index ce5a588776..f44b105e0f 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "scripting/RenderScriptingInterface.h" #include "Application.h" #include "DialogsManager.h" @@ -207,6 +208,23 @@ void setupPreferences() { preferences->addPreference(preference); } + { + 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)); + } + + { + auto getter = []() -> float { return DependencyManager::get()->getHandLaserDelay(); }; + auto setter = [](float value) { DependencyManager::get()->setHandLaserDelay(value); }; + auto delaySlider = new SpinnerSliderPreference(UI_CATEGORY, "Laser Delay (seconds)", getter, setter); + delaySlider->setMin(0.0f); + delaySlider->setMax(2.0f); + delaySlider->setStep(0.02f); + delaySlider->setDecimals(2.0f); + preferences->addPreference(delaySlider); + } + static const QString VIEW_CATEGORY{ "View" }; { auto getter = [myAvatar]()->float { return myAvatar->getRealWorldFieldOfView(); }; @@ -226,12 +244,6 @@ void setupPreferences() { preferences->addPreference(preference); } - { - 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)); - } - // Snapshots static const QString SNAPSHOTS { "Snapshots" }; { diff --git a/libraries/pointers/src/Pick.h b/libraries/pointers/src/Pick.h index 557fc93750..bb65953fe4 100644 --- a/libraries/pointers/src/Pick.h +++ b/libraries/pointers/src/Pick.h @@ -170,6 +170,8 @@ public: void setIgnoreItems(const QVector& items); void setIncludeItems(const QVector& items); + virtual void setDelay(float delay) {} + virtual QVariantMap toVariantMap() const { QVariantMap properties; diff --git a/libraries/pointers/src/PickManager.cpp b/libraries/pointers/src/PickManager.cpp index cad44b1864..dc3a482150 100644 --- a/libraries/pointers/src/PickManager.cpp +++ b/libraries/pointers/src/PickManager.cpp @@ -129,6 +129,13 @@ void PickManager::setIncludeItems(unsigned int uid, const QVector& includ } } +void PickManager::setDelay(unsigned int uid, float delay) const { + auto pick = findPick(uid); + if (pick) { + pick->setDelay(delay); + } +} + Transform PickManager::getParentTransform(unsigned int uid) const { auto pick = findPick(uid); if (pick) { diff --git a/libraries/pointers/src/PickManager.h b/libraries/pointers/src/PickManager.h index 79ab6ca2c8..b8e3f8d97d 100644 --- a/libraries/pointers/src/PickManager.h +++ b/libraries/pointers/src/PickManager.h @@ -48,6 +48,7 @@ public: void setPrecisionPicking(unsigned int uid, bool precisionPicking) const; void setIgnoreItems(unsigned int uid, const QVector& ignore) const; void setIncludeItems(unsigned int uid, const QVector& include) const; + void setDelay(unsigned int uid, float delay) const; Transform getParentTransform(unsigned int uid) const; Transform getResultTransform(unsigned int uid) const; diff --git a/libraries/pointers/src/Pointer.cpp b/libraries/pointers/src/Pointer.cpp index f435482865..6e9ed88508 100644 --- a/libraries/pointers/src/Pointer.cpp +++ b/libraries/pointers/src/Pointer.cpp @@ -74,6 +74,10 @@ void Pointer::setIncludeItems(const QVector& includeItems) const { DependencyManager::get()->setIncludeItems(_pickUID, includeItems); } +void Pointer::setDelay(float delay) const { + DependencyManager::get()->setDelay(_pickUID, delay); +} + bool Pointer::isLeftHand() const { return DependencyManager::get()->isLeftHand(_pickUID); } diff --git a/libraries/pointers/src/Pointer.h b/libraries/pointers/src/Pointer.h index 7a7c51477a..681ceb10c9 100644 --- a/libraries/pointers/src/Pointer.h +++ b/libraries/pointers/src/Pointer.h @@ -59,6 +59,7 @@ public: virtual void setPrecisionPicking(bool precisionPicking); virtual void setIgnoreItems(const QVector& ignoreItems) const; virtual void setIncludeItems(const QVector& includeItems) const; + virtual void setDelay(float delay) const; bool isLeftHand() const; bool isRightHand() const; diff --git a/libraries/pointers/src/PointerManager.cpp b/libraries/pointers/src/PointerManager.cpp index de403a46e8..31e8e0d814 100644 --- a/libraries/pointers/src/PointerManager.cpp +++ b/libraries/pointers/src/PointerManager.cpp @@ -157,6 +157,13 @@ void PointerManager::setLockEndUUID(unsigned int uid, const QUuid& objectID, boo } } +void PointerManager::setDelay(unsigned int uid, float delay) const { + auto pointer = find(uid); + if (pointer) { + pointer->setDelay(delay); + } +} + bool PointerManager::isLeftHand(unsigned int uid) { auto pointer = find(uid); if (pointer) { diff --git a/libraries/pointers/src/PointerManager.h b/libraries/pointers/src/PointerManager.h index 609f944101..cb39870c84 100644 --- a/libraries/pointers/src/PointerManager.h +++ b/libraries/pointers/src/PointerManager.h @@ -45,6 +45,7 @@ public: void setLength(unsigned int uid, float length) const; void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isAvatar, const glm::mat4& offsetMat = glm::mat4()) const; + void setDelay(unsigned int uid, float delay) const; void update(); diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index f098e5325a..f2b0efcfa1 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -17,8 +17,7 @@ controllerDispatcherPlugins:true, controllerDispatcherPluginsNeedSort:true, LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS, DEFAULT_SEARCH_SPHERE_DISTANCE, DISPATCHER_PROPERTIES, getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, Picks, PickType, Pointers, - PointerManager, getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, Picks, PickType, Pointers, - PointerManager, print, Keyboard + PointerManager, getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, print, Keyboard */ var controllerDispatcherPlugins = {}; @@ -577,7 +576,6 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Controller.enableMapping(MAPPING_NAME); - const POINTER_DELAY = 0.5; this.leftPointer = this.pointerManager.createPointer(false, PickType.Ray, { joint: "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", filter: Picks.PICK_OVERLAYS | Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_NONCOLLIDABLE, @@ -587,7 +585,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); scaleWithParent: true, distanceScaleEnd: true, hand: LEFT_HAND, - delay: POINTER_DELAY + delay: Picks.handLaserDelay }); Keyboard.setLeftHandLaser(this.leftPointer); this.rightPointer = this.pointerManager.createPointer(false, PickType.Ray, { @@ -599,7 +597,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); scaleWithParent: true, distanceScaleEnd: true, hand: RIGHT_HAND, - delay: POINTER_DELAY + delay: Picks.handLaserDelay }); Keyboard.setRightHandLaser(this.rightPointer); this.leftHudPointer = this.pointerManager.createPointer(true, PickType.Ray, { @@ -612,7 +610,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); scaleWithParent: true, distanceScaleEnd: true, hand: LEFT_HAND, - delay: POINTER_DELAY + delay: Picks.handLaserDelay }); this.rightHudPointer = this.pointerManager.createPointer(true, PickType.Ray, { joint: "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND", @@ -624,7 +622,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); scaleWithParent: true, distanceScaleEnd: true, hand: RIGHT_HAND, - delay: POINTER_DELAY + delay: Picks.handLaserDelay }); this.mouseRayPointer = Pointers.createRayPointer({ @@ -673,6 +671,13 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); } }; + this.handLaserDelayChanged = function (delay) { + Pointers.setDelay(_this.leftPointer, delay); + Pointers.setDelay(_this.rightPointer, delay); + Pointers.setDelay(_this.leftHudPointer, delay); + Pointers.setDelay(_this.rightHudPointer, delay); + }; + this.cleanup = function () { Controller.disableMapping(MAPPING_NAME); _this.pointerManager.removePointers(); @@ -735,6 +740,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Messages.subscribe('Hifi-Hand-RayPick-Blacklist'); Messages.messageReceived.connect(controllerDispatcher.handleMessage); + Picks.handLaserDelayChanged.connect(controllerDispatcher.handLaserDelayChanged); + Script.scriptEnding.connect(function () { controllerDispatcher.cleanup(); });