allow configurable delay

This commit is contained in:
HifiExperiments 2024-06-14 20:15:24 -07:00
parent 3064647d05
commit a5b32a6406
14 changed files with 113 additions and 15 deletions

View file

@ -463,6 +463,10 @@ void PickScriptingInterface::setIncludeItems(unsigned int uid, const ScriptValue
DependencyManager::get<PickManager>()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems)); DependencyManager::get<PickManager>()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems));
} }
void PickScriptingInterface::setDelay(unsigned int uid, float delay) {
DependencyManager::get<PickManager>()->setDelay(uid, delay);
}
bool PickScriptingInterface::isLeftHand(unsigned int uid) { bool PickScriptingInterface::isLeftHand(unsigned int uid) {
return DependencyManager::get<PickManager>()->isLeftHand(uid); return DependencyManager::get<PickManager>()->isLeftHand(uid);
} }
@ -505,6 +509,15 @@ void PickScriptingInterface::setPerFrameTimeBudget(unsigned int numUsecs) {
DependencyManager::get<PickManager>()->setPerFrameTimeBudget(numUsecs); DependencyManager::get<PickManager>()->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<PickQuery> pick, const QVariantMap& propMap) { void PickScriptingInterface::setParentTransform(std::shared_ptr<PickQuery> pick, const QVariantMap& propMap) {
QUuid parentUuid; QUuid parentUuid;
int parentJointIndex = 0; int parentJointIndex = 0;

View file

@ -16,6 +16,7 @@
#include <PhysicsEngine.h> #include <PhysicsEngine.h>
#include <Pick.h> #include <Pick.h>
#include <PickFilter.h> #include <PickFilter.h>
#include <SettingHandle.h>
class ScriptEngine; class ScriptEngine;
class ScriptValue; class ScriptValue;
@ -72,6 +73,7 @@ class ScriptValue;
* @property {IntersectionType} INTERSECTED_HUD - Intersected the HUD surface. <em>Read-only.</em> * @property {IntersectionType} INTERSECTED_HUD - Intersected the HUD surface. <em>Read-only.</em>
* *
* @property {number} perFrameTimeBudget - The maximum time, in microseconds, to spend per frame updating pick results. * @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 { 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_AVATAR READ getIntersectedAvatar CONSTANT)
Q_PROPERTY(unsigned int INTERSECTED_HUD READ getIntersectedHud CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_HUD READ getIntersectedHud CONSTANT)
Q_PROPERTY(unsigned int perFrameTimeBudget READ getPerFrameTimeBudget WRITE setPerFrameTimeBudget) Q_PROPERTY(unsigned int perFrameTimeBudget READ getPerFrameTimeBudget WRITE setPerFrameTimeBudget)
Q_PROPERTY(float handLaserDelay READ getHandLaserDelay WRITE setHandLaserDelay)
SINGLETON_DEPENDENCY SINGLETON_DEPENDENCY
public: public:
@ -263,6 +266,15 @@ public:
*/ */
Q_INVOKABLE void setIncludeItems(unsigned int uid, const ScriptValue& includeItems); Q_INVOKABLE void setIncludeItems(unsigned int uid, const ScriptValue& includeItems);
/*@jsdoc
* Sets the delay of a Ray pick.
* <p><strong>Note:</strong> Not used by other pick types.</p>
* @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 /*@jsdoc
* Checks if a pick is associated with the left hand: a ray or parabola pick with <code>joint</code> property set to * Checks if a pick is associated with the left hand: a ray or parabola pick with <code>joint</code> property set to
* <code>"_CONTROLLER_LEFTHAND"</code> or <code>"_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"</code>, or a stylus pick with * <code>"_CONTROLLER_LEFTHAND"</code> or <code>"_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"</code>, or a stylus pick with
@ -295,6 +307,9 @@ public:
unsigned int getPerFrameTimeBudget() const; unsigned int getPerFrameTimeBudget() const;
void setPerFrameTimeBudget(unsigned int numUsecs); void setPerFrameTimeBudget(unsigned int numUsecs);
float getHandLaserDelay() const;
void setHandLaserDelay(float delay);
public slots: public slots:
static constexpr unsigned int getPickBypassIgnore() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_BYPASS_IGNORE); } 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; } static constexpr unsigned int getIntersectedHud() { return IntersectionType::HUD; }
signals:
void handLaserDelayChanged(float delay);
protected: protected:
static std::shared_ptr<PickQuery> buildRayPick(const QVariantMap& properties); static std::shared_ptr<PickQuery> buildRayPick(const QVariantMap& properties);
static std::shared_ptr<PickQuery> buildStylusPick(const QVariantMap& properties); static std::shared_ptr<PickQuery> buildStylusPick(const QVariantMap& properties);
@ -468,6 +486,9 @@ protected:
static std::shared_ptr<PickQuery> buildParabolaPick(const QVariantMap& properties); static std::shared_ptr<PickQuery> buildParabolaPick(const QVariantMap& properties);
static void setParentTransform(std::shared_ptr<PickQuery> pick, const QVariantMap& propMap); static void setParentTransform(std::shared_ptr<PickQuery> pick, const QVariantMap& propMap);
private:
Setting::Handle<float> _handLaserDelaySetting { "handLaserDelay", 0.0f };
}; };
#endif // hifi_PickScriptingInterface_h #endif // hifi_PickScriptingInterface_h

View file

@ -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<PointerManager>()->setLockEndUUID(uid, objectID, isAvatar, offsetMat); } Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isAvatar, const glm::mat4& offsetMat = glm::mat4()) const { DependencyManager::get<PointerManager>()->setLockEndUUID(uid, objectID, isAvatar, offsetMat); }
/*@jsdoc
* Sets the delay of a Ray pointer.
* <p><strong>Note:</strong> Not used by stylus or parabola pointers.</p>
* @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<PointerManager>()->setDelay(uid, delay); }
/*@jsdoc /*@jsdoc
* Checks if a pointer is associated with the left hand: a ray or parabola pointer with <code>joint</code> property set to * Checks if a pointer is associated with the left hand: a ray or parabola pointer with <code>joint</code> property set to

View file

@ -19,14 +19,18 @@ PickRay RayPick::getMathematicalPick() const {
return _mathPick; 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 now = secTimestampNow();
const float dt = now - _prevUpdate; const float dt = now - _prevUpdate;
float alpha = 0.0f; float alpha = 0.0f;
if (hasDelay) { if (hasDelay) {
// This equation gives a framerate-independent lerp for a moving target // This equation gives a framerate-independent lerp for a moving target
// https://twitter.com/FreyaHolmer/status/1757836988495847568 // https://twitter.com/FreyaHolmer/status/1757836988495847568
alpha = 1 - exp2(-dt / _delayHalf); alpha = 1 - exp2(-dt / delayHalf);
} }
Transform currentParentTransform = parentTransform->getTransform(); Transform currentParentTransform = parentTransform->getTransform();
@ -94,6 +98,14 @@ PickResultPointer RayPick::getHUDIntersection(const PickRay& pick) {
return std::make_shared<RayPickResult>(IntersectionType::HUD, QUuid(), glm::distance(pick.origin, hudRes), hudRes, pick); return std::make_shared<RayPickResult>(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 <delay> seconds
// https://twitter.com/FreyaHolmer/status/1757836988495847568
_delayHalf = -std::max(delay, 0.0f) / log2(0.001);
});
}
Transform RayPick::getResultTransform() const { Transform RayPick::getResultTransform() const {
PickResultPointer result = getPrevPickResult(); PickResultPointer result = getPrevPickResult();
if (!result) { if (!result) {

View file

@ -97,6 +97,8 @@ public:
PickResultPointer getHUDIntersection(const PickRay& pick) override; PickResultPointer getHUDIntersection(const PickRay& pick) override;
Transform getResultTransform() const override; Transform getResultTransform() const override;
void setDelay(float delay) override;
// These are helper functions for projecting and intersecting rays // 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::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); static glm::vec2 projectOntoEntityXYPlane(const QUuid& entityID, const glm::vec3& worldPos, bool unNormalized = true);

View file

@ -18,6 +18,7 @@
#include <plugins/PluginManager.h> #include <plugins/PluginManager.h>
#include <display-plugins/CompositorHelper.h> #include <display-plugins/CompositorHelper.h>
#include <display-plugins/hmd/HmdDisplayPlugin.h> #include <display-plugins/hmd/HmdDisplayPlugin.h>
#include <raypick/PickScriptingInterface.h>
#include "scripting/RenderScriptingInterface.h" #include "scripting/RenderScriptingInterface.h"
#include "Application.h" #include "Application.h"
#include "DialogsManager.h" #include "DialogsManager.h"
@ -207,6 +208,23 @@ void setupPreferences() {
preferences->addPreference(preference); 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<PickScriptingInterface>()->getHandLaserDelay(); };
auto setter = [](float value) { DependencyManager::get<PickScriptingInterface>()->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" }; static const QString VIEW_CATEGORY{ "View" };
{ {
auto getter = [myAvatar]()->float { return myAvatar->getRealWorldFieldOfView(); }; auto getter = [myAvatar]()->float { return myAvatar->getRealWorldFieldOfView(); };
@ -226,12 +244,6 @@ void setupPreferences() {
preferences->addPreference(preference); 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 // Snapshots
static const QString SNAPSHOTS { "Snapshots" }; static const QString SNAPSHOTS { "Snapshots" };
{ {

View file

@ -170,6 +170,8 @@ public:
void setIgnoreItems(const QVector<QUuid>& items); void setIgnoreItems(const QVector<QUuid>& items);
void setIncludeItems(const QVector<QUuid>& items); void setIncludeItems(const QVector<QUuid>& items);
virtual void setDelay(float delay) {}
virtual QVariantMap toVariantMap() const { virtual QVariantMap toVariantMap() const {
QVariantMap properties; QVariantMap properties;

View file

@ -129,6 +129,13 @@ void PickManager::setIncludeItems(unsigned int uid, const QVector<QUuid>& 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 { Transform PickManager::getParentTransform(unsigned int uid) const {
auto pick = findPick(uid); auto pick = findPick(uid);
if (pick) { if (pick) {

View file

@ -48,6 +48,7 @@ public:
void setPrecisionPicking(unsigned int uid, bool precisionPicking) const; void setPrecisionPicking(unsigned int uid, bool precisionPicking) const;
void setIgnoreItems(unsigned int uid, const QVector<QUuid>& ignore) const; void setIgnoreItems(unsigned int uid, const QVector<QUuid>& ignore) const;
void setIncludeItems(unsigned int uid, const QVector<QUuid>& include) const; void setIncludeItems(unsigned int uid, const QVector<QUuid>& include) const;
void setDelay(unsigned int uid, float delay) const;
Transform getParentTransform(unsigned int uid) const; Transform getParentTransform(unsigned int uid) const;
Transform getResultTransform(unsigned int uid) const; Transform getResultTransform(unsigned int uid) const;

View file

@ -74,6 +74,10 @@ void Pointer::setIncludeItems(const QVector<QUuid>& includeItems) const {
DependencyManager::get<PickManager>()->setIncludeItems(_pickUID, includeItems); DependencyManager::get<PickManager>()->setIncludeItems(_pickUID, includeItems);
} }
void Pointer::setDelay(float delay) const {
DependencyManager::get<PickManager>()->setDelay(_pickUID, delay);
}
bool Pointer::isLeftHand() const { bool Pointer::isLeftHand() const {
return DependencyManager::get<PickManager>()->isLeftHand(_pickUID); return DependencyManager::get<PickManager>()->isLeftHand(_pickUID);
} }

View file

@ -59,6 +59,7 @@ public:
virtual void setPrecisionPicking(bool precisionPicking); virtual void setPrecisionPicking(bool precisionPicking);
virtual void setIgnoreItems(const QVector<QUuid>& ignoreItems) const; virtual void setIgnoreItems(const QVector<QUuid>& ignoreItems) const;
virtual void setIncludeItems(const QVector<QUuid>& includeItems) const; virtual void setIncludeItems(const QVector<QUuid>& includeItems) const;
virtual void setDelay(float delay) const;
bool isLeftHand() const; bool isLeftHand() const;
bool isRightHand() const; bool isRightHand() const;

View file

@ -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) { bool PointerManager::isLeftHand(unsigned int uid) {
auto pointer = find(uid); auto pointer = find(uid);
if (pointer) { if (pointer) {

View file

@ -45,6 +45,7 @@ public:
void setLength(unsigned int uid, float length) const; 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 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(); void update();

View file

@ -17,8 +17,7 @@
controllerDispatcherPlugins:true, controllerDispatcherPluginsNeedSort:true, controllerDispatcherPlugins:true, controllerDispatcherPluginsNeedSort:true,
LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS, DEFAULT_SEARCH_SPHERE_DISTANCE, DISPATCHER_PROPERTIES, LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS, DEFAULT_SEARCH_SPHERE_DISTANCE, DISPATCHER_PROPERTIES,
getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, Picks, PickType, Pointers, getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, Picks, PickType, Pointers,
PointerManager, getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, Picks, PickType, Pointers, PointerManager, getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, print, Keyboard
PointerManager, print, Keyboard
*/ */
var controllerDispatcherPlugins = {}; var controllerDispatcherPlugins = {};
@ -577,7 +576,6 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
Controller.enableMapping(MAPPING_NAME); Controller.enableMapping(MAPPING_NAME);
const POINTER_DELAY = 0.5;
this.leftPointer = this.pointerManager.createPointer(false, PickType.Ray, { this.leftPointer = this.pointerManager.createPointer(false, PickType.Ray, {
joint: "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", joint: "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND",
filter: Picks.PICK_OVERLAYS | Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_NONCOLLIDABLE, filter: Picks.PICK_OVERLAYS | Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_NONCOLLIDABLE,
@ -587,7 +585,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
scaleWithParent: true, scaleWithParent: true,
distanceScaleEnd: true, distanceScaleEnd: true,
hand: LEFT_HAND, hand: LEFT_HAND,
delay: POINTER_DELAY delay: Picks.handLaserDelay
}); });
Keyboard.setLeftHandLaser(this.leftPointer); Keyboard.setLeftHandLaser(this.leftPointer);
this.rightPointer = this.pointerManager.createPointer(false, PickType.Ray, { this.rightPointer = this.pointerManager.createPointer(false, PickType.Ray, {
@ -599,7 +597,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
scaleWithParent: true, scaleWithParent: true,
distanceScaleEnd: true, distanceScaleEnd: true,
hand: RIGHT_HAND, hand: RIGHT_HAND,
delay: POINTER_DELAY delay: Picks.handLaserDelay
}); });
Keyboard.setRightHandLaser(this.rightPointer); Keyboard.setRightHandLaser(this.rightPointer);
this.leftHudPointer = this.pointerManager.createPointer(true, PickType.Ray, { this.leftHudPointer = this.pointerManager.createPointer(true, PickType.Ray, {
@ -612,7 +610,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
scaleWithParent: true, scaleWithParent: true,
distanceScaleEnd: true, distanceScaleEnd: true,
hand: LEFT_HAND, hand: LEFT_HAND,
delay: POINTER_DELAY delay: Picks.handLaserDelay
}); });
this.rightHudPointer = this.pointerManager.createPointer(true, PickType.Ray, { this.rightHudPointer = this.pointerManager.createPointer(true, PickType.Ray, {
joint: "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND", joint: "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND",
@ -624,7 +622,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
scaleWithParent: true, scaleWithParent: true,
distanceScaleEnd: true, distanceScaleEnd: true,
hand: RIGHT_HAND, hand: RIGHT_HAND,
delay: POINTER_DELAY delay: Picks.handLaserDelay
}); });
this.mouseRayPointer = Pointers.createRayPointer({ 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 () { this.cleanup = function () {
Controller.disableMapping(MAPPING_NAME); Controller.disableMapping(MAPPING_NAME);
_this.pointerManager.removePointers(); _this.pointerManager.removePointers();
@ -735,6 +740,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
Messages.subscribe('Hifi-Hand-RayPick-Blacklist'); Messages.subscribe('Hifi-Hand-RayPick-Blacklist');
Messages.messageReceived.connect(controllerDispatcher.handleMessage); Messages.messageReceived.connect(controllerDispatcher.handleMessage);
Picks.handLaserDelayChanged.connect(controllerDispatcher.handLaserDelayChanged);
Script.scriptEnding.connect(function () { Script.scriptEnding.connect(function () {
controllerDispatcher.cleanup(); controllerDispatcher.cleanup();
}); });