overte-HifiExperiments/interface/src/raypick/PickScriptingInterface.h
2024-06-18 17:20:16 -07:00

494 lines
23 KiB
C++

//
// Created by Sam Gondelman 10/20/2017
// Copyright 2017 High Fidelity, Inc.
// Copyright 2022-2023 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// SPDX-License-Identifier: Apache-2.0
//
#ifndef hifi_PickScriptingInterface_h
#define hifi_PickScriptingInterface_h
#include <QtCore/QObject>
#include <DependencyManager.h>
#include <PhysicsEngine.h>
#include <Pick.h>
#include <PickFilter.h>
#include <SettingHandle.h>
class ScriptEngine;
class ScriptValue;
/*@jsdoc
* The <code>Picks</code> API lets you create and manage objects for repeatedly calculating intersections.
*
* @namespace Picks
*
* @hifi-interface
* @hifi-client-entity
* @hifi-avatar
*
* @property {FilterFlags} PICK_DOMAIN_ENTITIES - Include domain entities when intersecting. <em>Read-only.</em>
* @property {FilterFlags} PICK_AVATAR_ENTITIES - Include avatar entities when intersecting. <em>Read-only.</em>
* @property {FilterFlags} PICK_LOCAL_ENTITIES - Include local entities when intersecting. <em>Read-only.</em>
* @property {FilterFlags} PICK_AVATARS - Include avatars when intersecting. <em>Read-only.</em>
* @property {FilterFlags} PICK_HUD - Include the HUD surface when intersecting in HMD mode. <em>Read-only.</em>
*
* @property {FilterFlags} PICK_ENTITIES - Include domain and avatar entities when intersecting. <em>Read-only.</em>
* <p class="important">Deprecated: This property is deprecated and will be removed. Use <code>PICK_DOMAIN_ENTITIES |
* PICK_AVATAR_ENTITIES</code> instead.</p>
* @property {FilterFlags} PICK_OVERLAYS - Include local entities when intersecting. <em>Read-only.</em>
* <p class="important">Deprecated: This property is deprecated and will be removed. Use <code>PICK_LOCAL_ENTITIES</code>
* instead.</p>
*
* @property {FilterFlags} PICK_INCLUDE_VISIBLE - Include visible objects when intersecting. <em>Read-only.</em>
* <p><strong>Warning:</strong> Is currently always enabled by default but may not be in the future.</p>
* @property {FilterFlags} PICK_INCLUDE_INVISIBLE - Include invisible objects when intersecting. <em>Read-only.</em>
*
* @property {FilterFlags} PICK_INCLUDE_COLLIDABLE - Include collidable objects when intersecting. <em>Read-only.</em>
* <p><strong>Warning:</strong> Is currently always enabled by default but may not be in the future.</p>
* @property {FilterFlags} PICK_INCLUDE_NONCOLLIDABLE - Include non-collidable objects when intersecting. <em>Read-only.</em>
*
* @property {FilterFlags} PICK_PRECISE - Pick against exact meshes. <em>Read-only.</em>
* @property {FilterFlags} PICK_COARSE - Pick against coarse meshes. <em>Read-only.</em>
*
* @property {FilterFlags} PICK_ALL_INTERSECTIONS - If set, returns all intersections instead of just the closest.
* <em>Read-only.</em>
* <p><strong>Warning:</strong> Not yet implemented.</p>
*
* @property {FilterFlags} PICK_BYPASS_IGNORE - Allows pick to intersect entities even when their
* <code>ignorePickIntersection</code> property value is <code>true</code>. For debug purposes.
* <em>Read-only.</em>
*
* @property {IntersectionType} INTERSECTED_NONE - Intersected nothing. <em>Read-only.</em>
* @property {IntersectionType} INTERSECTED_ENTITY - Intersected an entity. <em>Read-only.</em>
* @property {IntersectionType} INTERSECTED_LOCAL_ENTITY - Intersected a local entity. <em>Read-only.</em>
* @property {IntersectionType} INTERSECTED_OVERLAY - Intersected a local entity. (3D overlays no longer exist.)
* <em>Read-only.</em>
* <p class="important">Deprecated: This property is deprecated and will be removed. Use
* <code>INTERSECTED_LOCAL_ENTITY</code> instead.</p>
* @property {IntersectionType} INTERSECTED_AVATAR - Intersected an avatar. <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} handLaserDelay - The delay, in seconds, applied to the hand lasers to smooth their movement.
*/
class PickScriptingInterface : public QObject, public Dependency {
Q_OBJECT
Q_PROPERTY(unsigned int PICK_ENTITIES READ getPickEntities CONSTANT)
Q_PROPERTY(unsigned int PICK_OVERLAYS READ getPickOverlays CONSTANT)
Q_PROPERTY(unsigned int PICK_DOMAIN_ENTITIES READ getPickDomainEntities CONSTANT)
Q_PROPERTY(unsigned int PICK_AVATAR_ENTITIES READ getPickAvatarEntities CONSTANT)
Q_PROPERTY(unsigned int PICK_LOCAL_ENTITIES READ getPickLocalEntities CONSTANT)
Q_PROPERTY(unsigned int PICK_AVATARS READ getPickAvatars CONSTANT)
Q_PROPERTY(unsigned int PICK_HUD READ getPickHud CONSTANT)
Q_PROPERTY(unsigned int PICK_INCLUDE_VISIBLE READ getPickIncludeVisible CONSTANT)
Q_PROPERTY(unsigned int PICK_INCLUDE_INVISIBLE READ getPickIncludeInvisible CONSTANT)
Q_PROPERTY(unsigned int PICK_INCLUDE_COLLIDABLE READ getPickIncludeCollidable CONSTANT)
Q_PROPERTY(unsigned int PICK_INCLUDE_NONCOLLIDABLE READ getPickIncludeNoncollidable CONSTANT)
Q_PROPERTY(unsigned int PICK_PRECISE READ getPickPrecise CONSTANT)
Q_PROPERTY(unsigned int PICK_COARSE READ getPickCoarse CONSTANT)
Q_PROPERTY(unsigned int PICK_ALL_INTERSECTIONS READ getPickAllIntersections CONSTANT)
Q_PROPERTY(unsigned int PICK_BYPASS_IGNORE READ getPickBypassIgnore CONSTANT)
Q_PROPERTY(unsigned int INTERSECTED_NONE READ getIntersectedNone CONSTANT)
Q_PROPERTY(unsigned int INTERSECTED_ENTITY READ getIntersectedEntity CONSTANT)
Q_PROPERTY(unsigned int INTERSECTED_LOCAL_ENTITY READ getIntersectedLocalEntity CONSTANT)
Q_PROPERTY(unsigned int INTERSECTED_OVERLAY READ getIntersectedOverlay CONSTANT)
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:
static void registerMetaTypes(ScriptEngine* engine);
void registerProperties(ScriptEngine* engine);
/*@jsdoc
* Creates a new pick. Different {@link PickType}s use different properties, and within one PickType the properties you
* choose can lead to a wide range of behaviors. For example, with <code>PickType.Ray</code>, the properties could
* configure a mouse ray pick, an avatar head ray pick, or a joint ray pick.
* <p><strong>Warning:</strong> Picks created using this method currently always intersect at least visible and collidable
* things but this may not always be the case.</p>
* @function Picks.createPick
* @param {PickType} type - The type of picking to use.
* @param {Picks.RayPickProperties|Picks.ParabolaPickProperties|Picks.StylusPickProperties|Picks.CollisionPickProperties}
* properties - Properties of the pick, per the pick <code>type</code>.
* @returns {number} The ID of the pick created. <code>0</code> if invalid.
*/
// TODO: expand Pointers to be able to be fully configurable with PickFilters
Q_INVOKABLE unsigned int createPick(const PickQuery::PickType type, const QVariant& properties);
/*@jsdoc
* Enables a pick. Enabled picks update their pick results.
* @function Picks.enablePick
* @param {number} id - The ID of the pick.
*/
Q_INVOKABLE void enablePick(unsigned int uid);
/*@jsdoc
* Disables a pick. Disabled picks do not update their pick results.
* @function Picks.disablePick
* @param {number} id - The ID of the pick.
*/
Q_INVOKABLE void disablePick(unsigned int uid);
/*@jsdoc
* Get the enabled status of a pick. Enabled picks update their pick results.
* @function Picks.isPickEnabled
* @param {number} id - The ID of the pick.
* @returns {boolean} enabled - Whether or not the pick is enabled.
*/
Q_INVOKABLE bool isPickEnabled(unsigned int uid) const;
/*@jsdoc
* Removes (deletes) a pick.
* @function Picks.removePick
* @param {number} id - The ID of the pick.
*/
Q_INVOKABLE void removePick(unsigned int uid);
/*@jsdoc
* Gets the current properties of the pick.
* @function Picks.getPickProperties
* @param {number} id - The ID of the pick.
* @returns {Picks.RayPickProperties|Picks.ParabolaPickProperties|Picks.StylusPickProperties|Picks.CollisionPickProperties}
* Properties of the pick, per the pick <code>type</code>.
*/
Q_INVOKABLE QVariantMap getPickProperties(unsigned int uid) const;
/*@jsdoc
* Gets the parameters that were passed in to {@link Picks.createPick} to create the pick, if the pick was created through
* a script. Note that these properties do not reflect the current state of the pick.
* See {@link Picks.getPickProperties}.
* @function Picks.getPickScriptParameters
* @param {number} id - The ID of the pick.
* @returns {Picks.RayPickProperties|Picks.ParabolaPickProperties|Picks.StylusPickProperties|Picks.CollisionPickProperties}
* Script-provided properties, per the pick <code>type</code>.
*/
Q_INVOKABLE QVariantMap getPickScriptParameters(unsigned int uid) const;
/*@jsdoc
* Gets all picks which currently exist, including disabled picks.
* @function Picks.getPicks
* @returns {number[]} picks - The IDs of the picks.
*/
Q_INVOKABLE QVector<unsigned int> getPicks() const;
/*@jsdoc
* Gets the most recent result from a pick. A pick continues to be updated ready to return a result, as long as it is
* enabled.
* <p><strong>Note:</strong> Stylus picks only intersect with objects in their include list, set using
* {@link Picks.setIncludeItems|setIncludeItems}.</p>
* @function Picks.getPrevPickResult
* @param {number} id - The ID of the pick.
* @returns {RayPickResult|ParabolaPickResult|StylusPickResult|CollisionPickResult} The most recent intersection result.
* @example <caption>Highlight entities under your mouse in desktop mode or that you're looking at in HMD mode.</caption>
* // Highlight.
* var HIGHLIGHT_LIST_NAME = "highlightEntitiesExampleList";
* var HIGHLIGHT_LIST_TYPE = "entity";
* Selection.enableListHighlight(HIGHLIGHT_LIST_NAME, {});
*
* // Ray pick.
* var PICK_FILTER = Picks.PICK_DOMAIN_ENTITIES | Picks.PICK_AVATAR_ENTITIES
* | Picks.PICK_INCLUDE_COLLIDABLE | Picks.PICK_INCLUDE_NONCOLLIDABLE;
* var rayPick = Picks.createPick(PickType.Ray, {
* enabled: true,
* filter: PICK_FILTER,
* joint: HMD.active ? "Avatar" : "Mouse"
* });
*
* // Highlight intersected entity.
* var highlightedEntityID = null;
* Script.update.connect(function () {
* var rayPickResult = Picks.getPrevPickResult(rayPick);
* if (rayPickResult.intersects) {
* if (rayPickResult.objectID !== highlightedEntityID) {
* if (highlightedEntityID) {
* Selection.removeFromSelectedItemsList(HIGHLIGHT_LIST_NAME, HIGHLIGHT_LIST_TYPE, highlightedEntityID);
* }
* highlightedEntityID = rayPickResult.objectID;
* Selection.addToSelectedItemsList(HIGHLIGHT_LIST_NAME, HIGHLIGHT_LIST_TYPE, highlightedEntityID);
* }
* } else {
* if (highlightedEntityID) {
* Selection.removeFromSelectedItemsList(HIGHLIGHT_LIST_NAME, HIGHLIGHT_LIST_TYPE, highlightedEntityID);
* highlightedEntityID = null;
* }
* }
* });
*
* // Clean up.
* Script.scriptEnding.connect(function () {
* if (highlightedEntityID) {
* Selection.removeFromSelectedItemsList(HIGHLIGHT_LIST_NAME, HIGHLIGHT_LIST_TYPE, highlightedEntityID);
* }
* });
*/
Q_INVOKABLE QVariantMap getPrevPickResult(unsigned int uid);
/*@jsdoc
* Sets whether or not a pick should use precision picking, i.e., whether it should pick against precise meshes or coarse
* meshes.
* This has the same effect as using the <code>PICK_PRECISE</code> or <code>PICK_COARSE</code> filter flags.
* @function Picks.setPrecisionPicking
* @param {number} id - The ID of the pick.
* @param {boolean} precisionPicking - <code>true</code> to use precision picking, <code>false</code> to use coarse picking.
*/
Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking);
/*@jsdoc
* Sets a list of entity and avatar IDs that a pick should ignore during intersection.
* <p><strong>Note:</strong> Not used by stylus picks.</p>
* @function Picks.setIgnoreItems
* @param {number} id - The ID of the pick.
* @param {Uuid[]} ignoreItems - The list of IDs to ignore.
*/
Q_INVOKABLE void setIgnoreItems(unsigned int uid, const ScriptValue& ignoreItems);
/*@jsdoc
* Sets a list of entity and avatar IDs that a pick should include during intersection, instead of intersecting with
* everything.
* <p><strong>Note:</strong> Stylus picks only intersect with items in their include list.</p>
* @function Picks.setIncludeItems
* @param {number} id - The ID of the pick.
* @param {Uuid[]} includeItems - The list of IDs to include.
*/
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
* 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>hand</code> property set to <code>0</code>.
* @function Picks.isLeftHand
* @param {number} id - The ID of the pick.
* @returns {boolean} <code>true</code> if the pick is associated with the left hand, <code>false</code> if it isn't.
*/
Q_INVOKABLE bool isLeftHand(unsigned int uid);
/*@jsdoc
* Checks if a pick is associated with the right hand: a ray or parabola pick with <code>joint</code> property set to
* <code>"_CONTROLLER_RIGHTHAND"</code> or <code>"_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND"</code>, or a stylus pick with
* <code>hand</code> property set to <code>1</code>.
* @function Picks.isRightHand
* @param {number} id - The ID of the pick.
* @returns {boolean} <code>true</code> if the pick is associated with the right hand, <code>false</code> if it isn't.
*/
Q_INVOKABLE bool isRightHand(unsigned int uid);
/*@jsdoc
* Checks if a pick is associated with the system mouse: a ray or parabola pick with <code>joint</code> property set to
* <code>"Mouse"</code>.
* @function Picks.isMouse
* @param {number} id - The ID of the pick.
* @returns {boolean} <code>true</code> if the pick is associated with the system mouse, <code>false</code> if it isn't.
*/
Q_INVOKABLE bool isMouse(unsigned int uid);
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); }
/*@jsdoc
* @function Picks.PICK_ENTITIES
* @deprecated This function is deprecated and will be removed. Use the <code>Picks.PICK_DOMAIN_ENTITIES |
* Picks.PICK_AVATAR_ENTITIES</code> properties expression instead.
* @returns {number}
*/
static constexpr unsigned int getPickEntities() { return PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); }
/*@jsdoc
* @function Picks.PICK_OVERLAYS
* @deprecated This function is deprecated and will be removed. Use the <code>Picks.PICK_LOCAL_ENTITIES</code> property
* instead.
* @returns {number}
*/
static constexpr unsigned int getPickOverlays() { return PickFilter::getBitMask(PickFilter::FlagBit::LOCAL_ENTITIES); }
/*@jsdoc
* @function Picks.PICK_DOMAIN_ENTITIES
* @deprecated This function is deprecated and will be removed. Use the <code>Picks.PICK_DOMAIN_ENTITIES</code> property
* instead.
* @returns {number}
*/
static constexpr unsigned int getPickDomainEntities() { return PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES); }
/*@jsdoc
* @function Picks.PICK_AVATAR_ENTITIES
* @deprecated This function is deprecated and will be removed. Use the <code>Picks.PICK_AVATAR_ENTITIES</code> property
* instead.
* @returns {number}
*/
static constexpr unsigned int getPickAvatarEntities() { return PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES); }
/*@jsdoc
* @function Picks.PICK_LOCAL_ENTITIES
* @deprecated This function is deprecated and will be removed. Use the <code>Picks.PICK_LOCAL_ENTITIES</code> property
* instead.
* @returns {number}
*/
static constexpr unsigned int getPickLocalEntities() { return PickFilter::getBitMask(PickFilter::FlagBit::LOCAL_ENTITIES); }
/*@jsdoc
* @function Picks.PICK_AVATARS
* @deprecated This function is deprecated and will be removed. Use the <code>Picks.PICK_AVATARS</code> property
* instead.
* @returns {number}
*/
static constexpr unsigned int getPickAvatars() { return PickFilter::getBitMask(PickFilter::FlagBit::AVATARS); }
/*@jsdoc
* @function Picks.PICK_HUD
* @deprecated This function is deprecated and will be removed. Use the <code>Picks.PICK_HUD</code> property instead.
* @returns {number}
*/
static constexpr unsigned int getPickHud() { return PickFilter::getBitMask(PickFilter::FlagBit::HUD); }
/*@jsdoc
* @function Picks.PICK_INCLUDE_VISIBLE
* @deprecated This function is deprecated and will be removed. Use the <code>Picks.PICK_INCLUDE_VISIBLE</code> property
* instead.
* @returns {number}
*/
static constexpr unsigned int getPickIncludeVisible() { return PickFilter::getBitMask(PickFilter::FlagBit::VISIBLE); }
/*@jsdoc
* @function Picks.PICK_INCLUDE_INVISIBLE
* @deprecated This function is deprecated and will be removed. Use the <code>Picks.PICK_INCLUDE_INVISIBLE</code> property
* instead.
* @returns {number}
*/
static constexpr unsigned int getPickIncludeInvisible() { return PickFilter::getBitMask(PickFilter::FlagBit::INVISIBLE); }
/*@jsdoc
* @function Picks.PICK_INCLUDE_COLLIDABLE
* @deprecated This function is deprecated and will be removed. Use the <code>Picks.PICK_INCLUDE_COLLIDABLE</code> property
* instead.
* @returns {number}
*/
static constexpr unsigned int getPickIncludeCollidable() { return PickFilter::getBitMask(PickFilter::FlagBit::COLLIDABLE); }
/*@jsdoc
* @function Picks.PICK_INCLUDE_NONCOLLIDABLE
* @deprecated This function is deprecated and will be removed. Use the <code>Picks.PICK_INCLUDE_NONCOLLIDABLE</code>
* property instead.
* @returns {number}
*/
static constexpr unsigned int getPickIncludeNoncollidable() { return PickFilter::getBitMask(PickFilter::FlagBit::NONCOLLIDABLE); }
/*@jsdoc
* @function Picks.PICK_PRECISE
* @deprecated This function is deprecated and will be removed. Use the <code>Picks.PICK_PRECISE</code> property instead.
* @returns {number}
*/
static constexpr unsigned int getPickPrecise() { return PickFilter::getBitMask(PickFilter::FlagBit::PRECISE); }
/*@jsdoc
* @function Picks.PICK_COARSE
* @deprecated This function is deprecated and will be removed. Use the <code>Picks.PICK_COARSE</code> property instead.
* @returns {number}
*/
static constexpr unsigned int getPickCoarse() { return PickFilter::getBitMask(PickFilter::FlagBit::COARSE); }
/*@jsdoc
* @function Picks.PICK_ALL_INTERSECTIONS
* @deprecated This function is deprecated and will be removed. Use the <code>Picks.PICK_ALL_INTERSECTIONS</code> property
* instead.
* @returns {number}
*/
static constexpr unsigned int getPickAllIntersections() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_ALL_INTERSECTIONS); }
/*@jsdoc
* @function Picks.INTERSECTED_NONE
* @deprecated This function is deprecated and will be removed. Use the <code>Picks.INTERSECTED_NONE</code> property
* instead.
* @returns {number}
*/
static constexpr unsigned int getIntersectedNone() { return IntersectionType::NONE; }
/*@jsdoc
* @function Picks.INTERSECTED_ENTITY
* @deprecated This function is deprecated and will be removed. Use the <code>Picks.INTERSECTED_ENTITY</code> property
* instead.
* @returns {number}
*/
static constexpr unsigned int getIntersectedEntity() { return IntersectionType::ENTITY; }
/*@jsdoc
* @function Picks.INTERSECTED_LOCAL_ENTITY
* @deprecated This function is deprecated and will be removed. Use the <code>Picks.INTERSECTED_LOCAL_ENTITY</code>
* property instead.
* @returns {number}
*/
static constexpr unsigned int getIntersectedLocalEntity() { return IntersectionType::LOCAL_ENTITY; }
/*@jsdoc
* @function Picks.INTERSECTED_OVERLAY
* @deprecated This function is deprecated and will be removed. Use the <code>Picks.INTERSECTED_LOCAL_ENTITY</code>
* property instead.
* @returns {number}
*/
static constexpr unsigned int getIntersectedOverlay() { return getIntersectedLocalEntity(); }
/*@jsdoc
* @function Picks.INTERSECTED_AVATAR
* @deprecated This function is deprecated and will be removed. Use the <code>Picks.INTERSECTED_AVATAR</code> property
* instead.
* @returns {number}
*/
static constexpr unsigned int getIntersectedAvatar() { return IntersectionType::AVATAR; }
/*@jsdoc
* @function Picks.INTERSECTED_HUD
* @deprecated This function is deprecated and will be removed. Use the <code>Picks.INTERSECTED_HUD</code> property
* instead.
* @returns {number}
*/
static constexpr unsigned int getIntersectedHud() { return IntersectionType::HUD; }
signals:
void handLaserDelayChanged(float delay);
protected:
static std::shared_ptr<PickQuery> buildRayPick(const QVariantMap& properties);
static std::shared_ptr<PickQuery> buildStylusPick(const QVariantMap& properties);
static std::shared_ptr<PickQuery> buildCollisionPick(const QVariantMap& properties);
static std::shared_ptr<PickQuery> buildParabolaPick(const QVariantMap& properties);
static void setParentTransform(std::shared_ptr<PickQuery> pick, const QVariantMap& propMap);
private:
Setting::Handle<float> _handLaserDelaySetting { "handLaserDelay", 0.35f };
};
#endif // hifi_PickScriptingInterface_h