diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8c8fe23b93..d93ca36d45 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -194,8 +194,8 @@ #include #include -#include -#include +#include +#include #include #include #include diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 5a41b79c7c..3eacfefa11 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -14,7 +14,7 @@ #include "avatar/AvatarManager.h" #include -#include +#include #include "PickScriptingInterface.h" #include "RayPick.h" diff --git a/interface/src/raypick/LaserPointer.h b/interface/src/raypick/LaserPointer.h index 40d5a2b45f..3ecc1780d2 100644 --- a/interface/src/raypick/LaserPointer.h +++ b/interface/src/raypick/LaserPointer.h @@ -16,8 +16,8 @@ #include "ui/overlays/Overlay.h" -#include -#include +#include +#include struct LockEndObject { QUuid id { QUuid() }; diff --git a/interface/src/raypick/LaserPointerScriptingInterface.h b/interface/src/raypick/LaserPointerScriptingInterface.h index 132a7115d4..c2e6c8f113 100644 --- a/interface/src/raypick/LaserPointerScriptingInterface.h +++ b/interface/src/raypick/LaserPointerScriptingInterface.h @@ -14,7 +14,7 @@ #include #include "DependencyManager.h" -#include +#include class LaserPointerScriptingInterface : public QObject, public Dependency { Q_OBJECT diff --git a/interface/src/raypick/PickScriptingInterface.cpp b/interface/src/raypick/PickScriptingInterface.cpp index dad4cde3b7..43e0c059f0 100644 --- a/interface/src/raypick/PickScriptingInterface.cpp +++ b/interface/src/raypick/PickScriptingInterface.cpp @@ -11,14 +11,13 @@ #include #include "GLMHelpers.h" -#include +#include #include "StaticRayPick.h" #include "JointRayPick.h" #include "MouseRayPick.h" #include "StylusPick.h" -#include #include unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type, const QVariant& properties) { @@ -105,7 +104,12 @@ unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties filter = PickFilter(propMap["filter"].toUInt()); } - return DependencyManager::get()->addPick(PickQuery::Stylus, std::make_shared(filter, side, enabled)); + float maxDistance = 0.0f; + if (propMap["maxDistance"].isValid()) { + maxDistance = propMap["maxDistance"].toFloat(); + } + + return DependencyManager::get()->addPick(PickQuery::Stylus, std::make_shared(side, filter, maxDistance, enabled)); } void PickScriptingInterface::enablePick(unsigned int uid) { diff --git a/interface/src/raypick/PickScriptingInterface.h b/interface/src/raypick/PickScriptingInterface.h index 108ee99473..0e0b3e113b 100644 --- a/interface/src/raypick/PickScriptingInterface.h +++ b/interface/src/raypick/PickScriptingInterface.h @@ -12,7 +12,26 @@ #include #include -#include +#include + +/**jsdoc + * The Picks API lets you create and manage objects for repeatedly calculating intersections in different ways. + * + * @namespace Picks + * @property PICK_NOTHING {number} A filter flag. Don't intersect with anything. + * @property PICK_ENTITIES {number} A filter flag. Include entities when intersecting. + * @property PICK_OVERLAYS {number} A filter flag. Include overlays when intersecting. + * @property PICK_AVATARS {number} A filter flag. Include avatars when intersecting. + * @property PICK_HUD {number} A filter flag. Include the HUD sphere when intersecting in HMD mode. + * @property PICK_COARSE {number} A filter flag. Pick against coarse meshes, instead of exact meshes. + * @property PICK_INCLUDE_INVISIBLE {number} A filter flag. Include invisible objects when intersecting. + * @property PICK_INCLUDE_NONCOLLIDABLE {number} A filter flag. Include non-collidable objects when intersecting. + * @property INTERSECTED_NONE {number} An intersection type. Intersected nothing with the given filter flags. + * @property INTERSECTED_ENTITY {number} An intersection type. Intersected an entity. + * @property INTERSECTED_OVERLAY {number} An intersection type. Intersected an overlay. + * @property INTERSECTED_AVATAR {number} An intersection type. Intersected an avatar. + * @property INTERSECTED_HUD {number} An intersection type. Intersected the HUD sphere. + */ class PickScriptingInterface : public QObject, public Dependency { Q_OBJECT @@ -38,18 +57,130 @@ public: void registerMetaTypes(QScriptEngine* engine); + /**jsdoc + * A set of properties that can be passed to {@link Picks.createPick} to create a new Pick. + * + * Different {@link Picks.PickType}s use different properties, and within one PickType, the properties you choose can lead to a wide range of behaviors. For example, + * with PickType.Ray, depending on which optional parameters you pass, you could create a Static Ray Pick, a Mouse Ray Pick, or a Joint Ray Pick. + * + * @typedef {Object} Picks.PickProperties + * @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results. + * @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR. + * @property {float} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid. + * @property {string} [joint] Only for Joint or Mouse Ray Picks. If "Mouse", it will create a Ray Pick that follows the system mouse, in desktop or HMD. + * If "Avatar", it will create a Joint Ray Pick that follows your avatar's head. Otherwise, it will create a Joint Ray Pick that follows the given joint, if it + * exists on your current avatar. + * @property {Vec3} [posOffset=Vec3.ZERO] Only for Joint Ray Picks. A local joint position offset, in meters. x = upward, y = forward, z = lateral + * @property {Vec3} [dirOffset=Vec3.UP] Only for Joint Ray Picks. A local joint direction offset. x = upward, y = forward, z = lateral + * @property {Vec3} [position] Only for Static Ray Picks. The world-space origin of the ray. + * @property {Vec3} [direction=-Vec3.UP] Only for Static Ray Picks. The world-space direction of the ray. + * @property {number} [hand=-1] Only for Stylus Picks. An integer. 0 == left, 1 == right. Invalid otherwise. + */ + + /**jsdoc + * Adds a new Pick. + * @function Picks.createPick + * @param {Picks.PickType} type A PickType that specifies the method of picking to use + * @param {Picks.PickProperties} properties A PickProperties object, containing all the properties for initializing this Pick + * @returns {number} The ID of the created Pick. Used for managing the Pick. 0 if invalid. + */ Q_INVOKABLE unsigned int createPick(const PickQuery::PickType type, const QVariant& properties); + /**jsdoc + * Enables a Pick. + * @function Picks.enablePick + * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. + */ Q_INVOKABLE void enablePick(unsigned int uid); + /**jsdoc + * Disables a Pick. + * @function Picks.disablePick + * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. + */ Q_INVOKABLE void disablePick(unsigned int uid); + /**jsdoc + * Removes a Pick. + * @function Picks.removePick + * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. + */ Q_INVOKABLE void removePick(unsigned int uid); + + /**jsdoc + * An intersection result for a Ray Pick. + * + * @typedef {Object} Picks.RayPickResult + * @property {number} type The intersection type. + * @property {bool} intersects If there was a valid intersection (type != INTERSECTED_NONE) + * @property {Uuid} objectID The ID of the intersected object. Uuid.NULL for the HUD or invalid intersections. + * @property {float} distance The distance to the intersection point from the origin of the ray. + * @property {Vec3} intersection The intersection point in world-space. + * @property {Vec3} surfaceNormal The surface normal at the intersected point. All NANs if type == INTERSECTED_HUD. + * @property {PickRay} searchRay The PickRay that was used. Valid even if there was no intersection. + */ + + /**jsdoc + * An intersection result for a Stylus Pick. + * + * @typedef {Object} Picks.StylusPickResult + * @property {number} type The intersection type. + * @property {bool} intersects If there was a valid intersection (type != INTERSECTED_NONE) + * @property {Uuid} objectID The ID of the intersected object. Uuid.NULL for the HUD or invalid intersections. + * @property {float} distance The distance to the intersection point from the origin of the ray. + * @property {Vec3} intersection The intersection point in world-space. + * @property {Vec3} surfaceNormal The surface normal at the intersected point. All NANs if type == INTERSECTED_HUD. + * @property {StylusTip} stylusTip The StylusTip that was used. Valid even if there was no intersection. + */ + + /**jsdoc + * Get the most recent pick result from this Pick. This will be updated as long as the Pick is enabled. + * @function Picks.getPrevPickResult + * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. + * @returns {PickResult} The most recent intersection result. This will be slightly different for different PickTypes. See {@link Picks.RayPickResult} and {@link Picks.StylusPickResult}. + */ Q_INVOKABLE QVariantMap getPrevPickResult(unsigned int uid); + /**jsdoc + * Sets whether or not to use precision picking. + * @function Picks.setPrecisionPicking + * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. + * @param {boolean} precisionPicking Whether or not to use precision picking + */ Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking); - Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities); - Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities); + /**jsdoc + * Sets a list of Entity IDs, Overlay IDs, and/or Avatar IDs to ignore during intersection. Not used by Stylus Picks. + * @function Picks.setIgnoreItems + * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. + * @param {Uuid[]} ignoreItems A list of IDs to ignore. + */ + Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems); + /**jsdoc + * Sets a list of Entity IDs, Overlay IDs, and/or Avatar IDs to include during intersection, instead of intersecting with everything. Stylus + * Picks only intersect with objects in their include list. + * @function Picks.setIncludeItems + * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. + * @param {Uuid[]} includeItems A list of IDs to include. + */ + Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeItems); + /**jsdoc + * Check if a Pick is associated with the left hand. + * @function Picks.isLeftHand + * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. + * @returns {boolean} True if the Pick is a Joint Ray Pick with joint == "_CONTROLLER_LEFTHAND" or "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", or a Stylus Pick with hand == 0. + */ Q_INVOKABLE bool isLeftHand(unsigned int uid); + /**jsdoc + * Check if a Pick is associated with the right hand. + * @function Picks.isRightHand + * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. + * @returns {boolean} True if the Pick is a Joint Ray Pick with joint == "_CONTROLLER_RIGHTHAND" or "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND", or a Stylus Pick with hand == 1. + */ Q_INVOKABLE bool isRightHand(unsigned int uid); + /**jsdoc + * Check if a Pick is associated with the system mouse. + * @function Picks.isMouse + * @param {number} uid The ID of the Pick, as returned by {@link Picks.createPick}. + * @returns {boolean} True if the Pick is a Mouse Ray Pick, false otherwise. + */ Q_INVOKABLE bool isMouse(unsigned int uid); public slots: diff --git a/interface/src/raypick/PointerScriptingInterface.h b/interface/src/raypick/PointerScriptingInterface.h index 25b2c4b4b1..c426cf315d 100644 --- a/interface/src/raypick/PointerScriptingInterface.h +++ b/interface/src/raypick/PointerScriptingInterface.h @@ -11,8 +11,15 @@ #include #include "DependencyManager.h" -#include -#include +#include +#include + +/**jsdoc + * The Pointers API lets you create and manage objects for repeatedly calculating intersections in different ways, as well as the visual representation of those objects. + * Pointers can also be configured to automatically generate PointerEvents. + * + * @namespace Pointers + */ class PointerScriptingInterface : public QObject, public Dependency { Q_OBJECT @@ -22,33 +29,200 @@ public: unsigned int createLaserPointer(const QVariant& properties) const; unsigned int createStylus(const QVariant& properties) const; + /**jsdoc + * A set of properties that can be passed to {@link Pointers.createPointer} to create a new Pointer. Also contains the relevant {@link Picks.PickProperties} to define the underlying 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 PickType.Ray, depending on which optional parameters you pass, you could create a Static Ray Pointer, a Mouse Ray Pointer, or a Joint Ray Pointer. + * + * @typedef {Object} Pointers.PointerProperties + * @property {boolean} [hover=false] If this Pointer should generate hover events. + * @property {boolean} [faceAvatar=false] Ray Pointers only. If true, the end of the Pointer will always rotate to face the avatar. + * @property {boolean} [centerEndY=true] Ray Pointers only. If false, the end of the Pointer will be moved up by half of its height. + * @property {boolean} [lockEnd=false] Ray Pointers only. If true, the end of the Pointer will lock on to the center of the object at which the laser is pointing. + * @property {boolean} [distanceScaleEnd=false] Ray Pointers only. If true, the dimensions of the end of the Pointer will scale linearly with distance. + * @property {Pointers.RayPointerRenderState[]} [renderStates] Ray Pointers only. A list of different visual states to switch between. + * @property {Pointers.DefaultRayPointerRenderState[]} [defaultRenderStates] Ray Pointers only. A list of different visual states to use if there is no intersection. + * @property {Pointers.Trigger[]} [triggers] Ray Pointers only. A list of different triggers mechanisms that control this Pointer's click event generation. + */ + + /**jsdoc + * A set of properties used to define the visual aspect of a Ray Pointer in the case that the Pointer is intersecting something. + * + * @typedef {Object} Pointers.RayPointerRenderState + * @property {string} name The name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState} + * @property {OverlayProperties} [start] All of the properties you would normally pass to {@Overlays.addOverlay}, plus the type (as a type field). + * An overlay to represent the beginning of the Ray Pointer, if desired. + * @property {OverlayProperties} [path] All of the properties you would normally pass to {@Overlays.addOverlay}, plus the type (as a type field), which must be "line3d". + * An overlay to represent the path of the Ray Pointer, if desired. + * @property {OverlayProperties} [end] All of the properties you would normally pass to {@Overlays.addOverlay}, plus the type (as a type field). + * An overlay to represent the end of the Ray Pointer, if desired. + */ + + /**jsdoc + * A set of properties used to define the visual aspect of a Ray Pointer in the case that the Pointer is not intersecting something. Same as a {@link Pointers.RayPointerRenderState}, + * but with an additional distance field. + * + * @typedef {Object} Pointers.DefaultRayPointerRenderState + * @augments Pointers.RayPointerRenderState + * @property {number} distance The distance at which to render the end of this Ray Pointer, if one is defined. + */ + + /**jsdoc + * A trigger mechanism for Ray Pointers. + * + * @typedef {Object} Pointers.Trigger + * @property {Controller.Action} action This can be a built-in Controller action, like Controller.Standard.LTClick, or a function that evaluates to >= 1.0 when you want to trigger button. + * @property {string} button Which button to trigger. "Primary", "Secondary", "Tertiary", and "Focus" are currently supported. Only "Primary" will trigger clicks on web surfaces. If "Focus" is triggered, + * it will try to set the entity or overlay focus to the object at which the Pointer is aimed. Buttons besides the first three will still trigger events, but event.button will be "None". + */ + + /**jsdoc + * Adds a new Pointer + * @function Pointers.createPointer + * @param {Picks.PickType} type A PickType that specifies the method of picking to use + * @param {Pointers.PointerProperties} properties A PointerProperties object, containing all the properties for initializing this Pointer and the {@link Picks.PickProperties} for the Pick that + * this Pointer will use to do its picking. + * @returns {number} The ID of the created Pointer. Used for managing the Pointer. 0 if invalid. + * + * @example Create a left hand Ray Pointer that triggers events on left controller trigger click and changes color when it's intersecting something. + * + * var end = { + * type: "sphere", + * dimensions: {x:0.5, y:0.5, z:0.5}, + * solid: true, + * color: {red:0, green:255, blue:0}, + * ignoreRayIntersection: true + * }; + * var end2 = { + * type: "sphere", + * dimensions: {x:0.5, y:0.5, z:0.5}, + * solid: true, + * color: {red:255, green:0, blue:0}, + * ignoreRayIntersection: true + * }; + * + * var renderStates = [ {name: "test", end: end} ]; + * var defaultRenderStates = [ {name: "test", distance: 10.0, end: end2} ]; + * var pointer = Pointers.createPointer(PickType.Ray, { + * joint: "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", + * filter: Picks.PICK_OVERLAYS | Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_NONCOLLIDABLE, + * renderStates: renderStates, + * defaultRenderStates: defaultRenderStates, + * distanceScaleEnd: true, + * triggers: [ {action: Controller.Standard.LTClick, button: "Focus"}, {action: Controller.Standard.LTClick, button: "Primary"} ], + * hover: true, + * enabled: true + * }); + * Pointers.setRenderState(pointer, "test"); + */ Q_INVOKABLE unsigned int createPointer(const PickQuery::PickType& type, const QVariant& properties); + /**jsdoc + * Enables a Pointer. + * @function Pointers.enablePointer + * @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}. + */ Q_INVOKABLE void enablePointer(unsigned int uid) const { DependencyManager::get()->enablePointer(uid); } + /**jsdoc + * Disables a Pointer. + * @function Pointers.disablePointer + * @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}. + */ Q_INVOKABLE void disablePointer(unsigned int uid) const { DependencyManager::get()->disablePointer(uid); } + /**jsdoc + * Removes a Pointer. + * @function Pointers.removePointer + * @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}. + */ Q_INVOKABLE void removePointer(unsigned int uid) const { DependencyManager::get()->removePointer(uid); } + /**jsdoc + * Edit some visual aspect of a Pointer. Currently only supported for Ray Pointers. + * @function Pointers.editRenderState + * @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}. + * @param {string} renderState The name of the render state you want to edit. + * @param {RenderState} properties The new properties for renderState. For Ray Pointers, a {@link Pointers.RayPointerRenderState}. + */ Q_INVOKABLE void editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const; + /**jsdoc + * Set the render state of a Pointer. For Ray Pointers, this means switching between their {@link Pointers.RayPointerRenderState}s, or "" to turn off rendering and hover/trigger events. + * For Stylus Pointers, there are three built-in options: "events on" (render and send events, the default), "events off" (render but don't send events), and "disabled" (don't render, don't send events). + * @function Pointers.setRenderState + * @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}. + * @param {string} renderState The name of the render state to which you want to switch. + */ Q_INVOKABLE void setRenderState(unsigned int uid, const QString& renderState) const { DependencyManager::get()->setRenderState(uid, renderState.toStdString()); } + + /**jsdoc + * Get the most recent pick result from this Pointer. This will be updated as long as the Pointer is enabled, regardless of the render state. + * @function Pointers.getPrevPickResult + * @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}. + * @returns {PickResult} The most recent intersection result. This will be slightly different for different PickTypes. See {@link Picks.RayPickResult} and {@link Picks.StylusPickResult}. + */ Q_INVOKABLE QVariantMap getPrevPickResult(unsigned int uid) const; + /**jsdoc + * Sets whether or not to use precision picking. + * @function Pointers.setPrecisionPicking + * @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}. + * @param {boolean} precisionPicking Whether or not to use precision picking + */ Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking) const { DependencyManager::get()->setPrecisionPicking(uid, precisionPicking); } - Q_INVOKABLE void setLaserLength(unsigned int uid, float laserLength) const { DependencyManager::get()->setLength(uid, laserLength); } + /**jsdoc + * Sets the length of this Pointer. No effect on Stylus Pointers. + * @function Pointers.setLength + * @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}. + * @param {float} length The desired length of the Pointer. + */ + Q_INVOKABLE void setLength(unsigned int uid, float length) const { DependencyManager::get()->setLength(uid, length); } + /**jsdoc + * Sets a list of Entity IDs, Overlay IDs, and/or Avatar IDs to ignore during intersection. Not used by Stylus Pointers. + * @function Pointers.setIgnoreItems + * @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}. + * @param {Uuid[]} ignoreItems A list of IDs to ignore. + */ Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities) const; + /**jsdoc + * Sets a list of Entity IDs, Overlay IDs, and/or Avatar IDs to include during intersection, instead of intersecting with everything. Stylus + * Pointers only intersect with objects in their include list. + * @function Pointers.setIncludeItems + * @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}. + * @param {Uuid[]} includeItems A list of IDs to include. + */ Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities) const; - Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay) const { DependencyManager::get()->setLockEndUUID(uid, objectID, isOverlay); } + /**jsdoc + * Lock a Pointer onto a specific object (overlay, entity, or avatar). Optionally, provide an offset in object-space, otherwise the Pointer will lock on to the center of the object. + * Not used by Stylus Pointers. + * @function Pointers.setLockEndUUID + * @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}. + * @param {QUuid} objectID The ID of the object to which to lock on. + * @param {boolean} isOverlay False for entities or avatars, true for overlays + * @param {Mat4} [offsetMat] The offset matrix to use if you do not want to lock on to the center of the object. + */ + Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) const { DependencyManager::get()->setLockEndUUID(uid, objectID, isOverlay, offsetMat); } + /**jsdoc + * Check if a Pointer is associated with the left hand. + * @function Pointers.isLeftHand + * @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}. + * @returns {boolean} True if the Pointer is a Joint Ray Pointer with joint == "_CONTROLLER_LEFTHAND" or "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", or a Stylus Pointer with hand == 0 + */ Q_INVOKABLE bool isLeftHand(unsigned int uid) { return DependencyManager::get()->isLeftHand(uid); } + /**jsdoc + * Check if a Pointer is associated with the right hand. + * @function Pointers.isRightHand + * @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}. + * @returns {boolean} True if the Pointer is a Joint Ray Pointer with joint == "_CONTROLLER_RIGHTHAND" or "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND", or a Stylus Pointer with hand == 1 + */ Q_INVOKABLE bool isRightHand(unsigned int uid) { return DependencyManager::get()->isRightHand(uid); } + /**jsdoc + * Check if a Pointer is associated with the system mouse. + * @function Pointers.isMouse + * @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}. + * @returns {boolean} True if the Pointer is a Mouse Ray Pointer, false otherwise. + */ Q_INVOKABLE bool isMouse(unsigned int uid) { return DependencyManager::get()->isMouse(uid); } -signals: - void triggerBegin(const QUuid& id, const PointerEvent& pointerEvent); - void triggerContinue(const QUuid& id, const PointerEvent& pointerEvent); - void triggerEnd(const QUuid& id, const PointerEvent& pointerEvent); - void hoverBegin(const QUuid& id, const PointerEvent& pointerEvent); - void hoverContinue(const QUuid& id, const PointerEvent& pointerEvent); - void hoverEnd(const QUuid& id, const PointerEvent& pointerEvent); - }; #endif // hifi_PointerScriptingInterface_h diff --git a/interface/src/raypick/RayPick.cpp b/interface/src/raypick/RayPick.cpp index 7689b295db..2f6e69bc7e 100644 --- a/interface/src/raypick/RayPick.cpp +++ b/interface/src/raypick/RayPick.cpp @@ -60,8 +60,7 @@ glm::vec3 RayPick::intersectRayWithXYPlane(const glm::vec3& origin, const glm::v glm::vec3 RayPick::intersectRayWithOverlayXYPlane(const QUuid& overlayID, const glm::vec3& origin, const glm::vec3& direction) { glm::vec3 position = vec3FromVariant(qApp->getOverlays().getProperty(overlayID, "position").value); glm::quat rotation = quatFromVariant(qApp->getOverlays().getProperty(overlayID, "rotation").value); - const glm::vec3 DEFAULT_REGISTRATION_POINT = glm::vec3(0.5f); - return intersectRayWithXYPlane(origin, direction, position, rotation, DEFAULT_REGISTRATION_POINT); + return intersectRayWithXYPlane(origin, direction, position, rotation, ENTITY_ITEM_DEFAULT_REGISTRATION_POINT); } glm::vec3 RayPick::intersectRayWithEntityXYPlane(const QUuid& entityID, const glm::vec3& origin, const glm::vec3& direction) { @@ -98,8 +97,7 @@ glm::vec2 RayPick::projectOntoOverlayXYPlane(const QUuid& overlayID, const glm:: dimensions = glm::vec3(vec2FromVariant(qApp->getOverlays().getProperty(overlayID, "dimensions").value), 0.01); } - const glm::vec3 DEFAULT_REGISTRATION_POINT = glm::vec3(0.5f); - return projectOntoXYPlane(worldPos, position, rotation, dimensions, DEFAULT_REGISTRATION_POINT, unNormalized); + return projectOntoXYPlane(worldPos, position, rotation, dimensions, ENTITY_ITEM_DEFAULT_REGISTRATION_POINT, unNormalized); } glm::vec2 RayPick::projectOntoEntityXYPlane(const QUuid& entityID, const glm::vec3& worldPos, bool unNormalized) { diff --git a/interface/src/raypick/RayPick.h b/interface/src/raypick/RayPick.h index 4a4ac69edd..25ad4df1f3 100644 --- a/interface/src/raypick/RayPick.h +++ b/interface/src/raypick/RayPick.h @@ -9,7 +9,7 @@ #define hifi_RayPick_h #include -#include +#include class EntityItemID; class OverlayID; diff --git a/interface/src/raypick/RayPickScriptingInterface.cpp b/interface/src/raypick/RayPickScriptingInterface.cpp index ba1cc0a57b..c90f5d6c6c 100644 --- a/interface/src/raypick/RayPickScriptingInterface.cpp +++ b/interface/src/raypick/RayPickScriptingInterface.cpp @@ -14,7 +14,7 @@ #include #include "GLMHelpers.h" -#include +#include #include "StaticRayPick.h" #include "JointRayPick.h" diff --git a/interface/src/raypick/StylusPick.cpp b/interface/src/raypick/StylusPick.cpp index f2b650b5bf..81e775432b 100644 --- a/interface/src/raypick/StylusPick.cpp +++ b/interface/src/raypick/StylusPick.cpp @@ -26,7 +26,7 @@ using namespace bilateral; static Setting::Handle USE_FINGER_AS_STYLUS("preferAvatarFingerOverStylus", false); static const float WEB_STYLUS_LENGTH = 0.2f; static const float WEB_TOUCH_Y_OFFSET = 0.105f; // how far forward (or back with a negative number) to slide stylus in hand -static const glm::vec3 TIP_OFFSET{ 0.0f, WEB_STYLUS_LENGTH - WEB_TOUCH_Y_OFFSET, 0.0f }; +static const glm::vec3 TIP_OFFSET = glm::vec3(0.0f, WEB_STYLUS_LENGTH - WEB_TOUCH_Y_OFFSET, 0.0f); struct SideData { QString avatarJoint; @@ -65,8 +65,8 @@ bool StylusPickResult::checkOrFilterAgainstMaxDistance(float maxDistance) { return distance < maxDistance; } -StylusPick::StylusPick(const PickFilter& filter, Side side, bool enabled) : - Pick(filter, 0.0f, enabled), +StylusPick::StylusPick(Side side, const PickFilter& filter, float maxDistance, bool enabled) : + Pick(filter, maxDistance, enabled), _side(side) { } @@ -150,7 +150,6 @@ PickResultPointer StylusPick::getEntityIntersection(const StylusTip& pick) { } auto entity = qApp->getEntities()->getTree()->findEntityByEntityItemID(target); - // Don't interact with non-3D or invalid overlays if (!entity) { continue; } diff --git a/interface/src/raypick/StylusPick.h b/interface/src/raypick/StylusPick.h index 9b465b9cc8..f19e343f8d 100644 --- a/interface/src/raypick/StylusPick.h +++ b/interface/src/raypick/StylusPick.h @@ -8,7 +8,7 @@ #ifndef hifi_StylusPick_h #define hifi_StylusPick_h -#include "pointers/Pick.h" +#include #include "RegisteredMetaTypes.h" class StylusPickResult : public PickResult { @@ -58,7 +58,7 @@ public: class StylusPick : public Pick { using Side = bilateral::Side; public: - StylusPick(const PickFilter& filter, Side side, bool enabled); + StylusPick(Side side, const PickFilter& filter, float maxDistance, bool enabled); StylusTip getMathematicalPick() const override; PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override; @@ -67,12 +67,11 @@ public: PickResultPointer getAvatarIntersection(const StylusTip& pick) override; PickResultPointer getHUDIntersection(const StylusTip& pick) override; + bool isLeftHand() const override { return _side == Side::Left; } + bool isRightHand() const override { return _side == Side::Right; } + private: const Side _side; }; -#endif // hifi_StylusPick_h - - - - +#endif // hifi_StylusPick_h \ No newline at end of file diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index 1a172b8894..0752c84df2 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -15,7 +15,7 @@ #include #include "PickScriptingInterface.h" -#include +#include // TODO: make these configurable per pointer static const float WEB_STYLUS_LENGTH = 0.2f; diff --git a/interface/src/raypick/StylusPointer.h b/interface/src/raypick/StylusPointer.h index fb2d492798..9c69915108 100644 --- a/interface/src/raypick/StylusPointer.h +++ b/interface/src/raypick/StylusPointer.h @@ -8,7 +8,7 @@ #ifndef hifi_StylusPointer_h #define hifi_StylusPointer_h -#include +#include #include #include diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 3a961a6c05..9f15b06156 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -19,7 +19,7 @@ #include #include -#include +#include #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 60c1905db6..ecd8f61b61 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -37,7 +37,7 @@ #include "Web3DOverlay.h" #include -#include +#include Q_LOGGING_CATEGORY(trace_render_overlays, "trace.render.overlays") diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index da579681a3..127327ef79 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -341,7 +341,9 @@ void Web3DOverlay::hoverEnterOverlay(const PointerEvent& event) { if (_inputMode == Mouse) { handlePointerEvent(event); } else if (_webSurface) { - _webSurface->hoverBeginEvent(event, _touchDevice); + PointerEvent webEvent = event; + webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi)); + _webSurface->hoverBeginEvent(webEvent, _touchDevice); } } @@ -354,7 +356,9 @@ void Web3DOverlay::hoverLeaveOverlay(const PointerEvent& event) { PointerEvent endMoveEvent(PointerEvent::Move, event.getID()); handlePointerEvent(endMoveEvent); } else if (_webSurface) { - _webSurface->hoverEndEvent(event, _touchDevice); + PointerEvent webEvent = event; + webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi)); + _webSurface->hoverEndEvent(webEvent, _touchDevice); } } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index fe576b2599..d395e6800a 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -37,7 +37,7 @@ #include "RenderableWebEntityItem.h" -#include +#include size_t std::hash::operator()(const EntityItemID& id) const { return qHash(id); } std::function EntityTreeRenderer::_entitiesShouldFadeFunction; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 95ab51628b..beca8a3d67 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -316,13 +316,17 @@ void WebEntityRenderer::loadSourceURL() { void WebEntityRenderer::hoverEnterEntity(const PointerEvent& event) { if (!_lastLocked && _webSurface) { - _webSurface->hoverBeginEvent(event, _touchDevice); + PointerEvent webEvent = event; + webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _lastDPI)); + _webSurface->hoverBeginEvent(webEvent, _touchDevice); } } void WebEntityRenderer::hoverLeaveEntity(const PointerEvent& event) { if (!_lastLocked && _webSurface) { - _webSurface->hoverEndEvent(event, _touchDevice); + PointerEvent webEvent = event; + webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _lastDPI)); + _webSurface->hoverEndEvent(webEvent, _touchDevice); } } diff --git a/libraries/pointers/src/pointers/Pick.cpp b/libraries/pointers/src/Pick.cpp similarity index 100% rename from libraries/pointers/src/pointers/Pick.cpp rename to libraries/pointers/src/Pick.cpp diff --git a/libraries/pointers/src/pointers/Pick.h b/libraries/pointers/src/Pick.h similarity index 94% rename from libraries/pointers/src/pointers/Pick.h rename to libraries/pointers/src/Pick.h index a4afbdbf8f..68c89a59cb 100644 --- a/libraries/pointers/src/pointers/Pick.h +++ b/libraries/pointers/src/Pick.h @@ -134,6 +134,16 @@ class PickQuery : protected ReadWriteLockable { public: PickQuery(const PickFilter& filter, const float maxDistance, const bool enabled); + /**jsdoc + * @namespace + * @augments Picks + * + * Enum for different types of Picks and Pointers. + * + * @typedef {enum} Picks.PickType + * @property {number} Ray Ray Picks intersect a ray with the nearest object in front of them, along a given direction. + * @property {number} Stylus Stylus Picks provide "tapping" functionality on/into flat surfaces. + */ enum PickType { Ray = 0, Stylus, diff --git a/libraries/pointers/src/pointers/PickCacheOptimizer.h b/libraries/pointers/src/PickCacheOptimizer.h similarity index 100% rename from libraries/pointers/src/pointers/PickCacheOptimizer.h rename to libraries/pointers/src/PickCacheOptimizer.h diff --git a/libraries/pointers/src/pointers/PickManager.cpp b/libraries/pointers/src/PickManager.cpp similarity index 100% rename from libraries/pointers/src/pointers/PickManager.cpp rename to libraries/pointers/src/PickManager.cpp diff --git a/libraries/pointers/src/pointers/PickManager.h b/libraries/pointers/src/PickManager.h similarity index 100% rename from libraries/pointers/src/pointers/PickManager.h rename to libraries/pointers/src/PickManager.h diff --git a/libraries/pointers/src/pointers/Pointer.cpp b/libraries/pointers/src/Pointer.cpp similarity index 94% rename from libraries/pointers/src/pointers/Pointer.cpp rename to libraries/pointers/src/Pointer.cpp index 4138ae5e4c..e6c0ae271c 100644 --- a/libraries/pointers/src/pointers/Pointer.cpp +++ b/libraries/pointers/src/Pointer.cpp @@ -163,11 +163,8 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin } } } - } - if (_hover) { - // send hoverEnd events if we disable the pointer, disable hovering, or actually stop hovering over an object - if ((!_enabled && _prevEnabled) || (!doHover && _prevDoHover) || (hoveredObject.type == NONE && _prevHoveredObject.type != NONE)) { + if (hoveredObject.type == NONE) { if (_prevHoveredObject.type == ENTITY) { emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent); } else if (_prevHoveredObject.type == OVERLAY) { @@ -178,6 +175,17 @@ 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) { diff --git a/libraries/pointers/src/pointers/Pointer.h b/libraries/pointers/src/Pointer.h similarity index 100% rename from libraries/pointers/src/pointers/Pointer.h rename to libraries/pointers/src/Pointer.h diff --git a/libraries/pointers/src/pointers/PointerManager.cpp b/libraries/pointers/src/PointerManager.cpp similarity index 100% rename from libraries/pointers/src/pointers/PointerManager.cpp rename to libraries/pointers/src/PointerManager.cpp diff --git a/libraries/pointers/src/pointers/PointerManager.h b/libraries/pointers/src/PointerManager.h similarity index 100% rename from libraries/pointers/src/pointers/PointerManager.h rename to libraries/pointers/src/PointerManager.h diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 8bb51c88f6..42b32004d6 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -134,6 +134,13 @@ class MathPick { class PickRay : public MathPick { public: + /**jsdoc + * The mathematical definition of a ray. + * + * @typedef {Object} PickRay + * @property {Vec3} origin The origin of the ray. + * @property {Vec3} direction The direction of the ray. + */ PickRay() : origin(NAN), direction(NAN) { } PickRay(const QVariantMap& pickVariant) : origin(vec3FromVariant(pickVariant["origin"])), direction(vec3FromVariant(pickVariant["direction"])) {} PickRay(const glm::vec3& origin, const glm::vec3 direction) : origin(origin), direction(direction) {} @@ -156,6 +163,15 @@ public: class StylusTip : public MathPick { public: + /**jsdoc + * The mathematical definition of a stylus tip. + * + * @typedef {Object} StylusTip + * @property {number} side The hand the tip is attached to. 0 == left, 1 == right. + * @property {Vec3} position The position of the tip. + * @property {Quat} orientation The orientation of the tip. + * @property {Vec3} velocity The velocity of the tip. + */ StylusTip() : position(NAN), velocity(NAN) {} StylusTip(const QVariantMap& pickVariant) : side(bilateral::Side(pickVariant["side"].toInt())), position(vec3FromVariant(pickVariant["position"])), orientation(quatFromVariant(pickVariant["orientation"])), velocity(vec3FromVariant(pickVariant["velocity"])) {} diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 32a02774c0..38a5f6d4f3 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -1109,16 +1109,19 @@ bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) { case QEvent::MouseMove: { QMouseEvent* mouseEvent = static_cast(event); QPointF transformedPos = mapToVirtualScreen(mouseEvent->localPos()); - PointerEvent pointerEvent(choosePointerEventType(mouseEvent->type()), PointerManager::MOUSE_POINTER_ID, glm::vec2(transformedPos.x(), transformedPos.y()), - PointerEvent::Button(mouseEvent->button()), mouseEvent->buttons(), mouseEvent->modifiers()); - result = OffscreenQmlSurface::handlePointerEvent(pointerEvent, _touchDevice); + // FIXME: touch events are always being accepted. Use mouse events on the OffScreenUi for now, and investigate properly switching to touch events + // (using handlePointerEvent) later + QMouseEvent mappedEvent(mouseEvent->type(), transformedPos, mouseEvent->screenPos(), mouseEvent->button(), mouseEvent->buttons(), mouseEvent->modifiers()); + mappedEvent.ignore(); + if (QCoreApplication::sendEvent(getWindow(), &mappedEvent)) { + return mappedEvent.isAccepted(); + } break; } default: break; } - // Check if this is a key press/release event that might need special attention auto type = event->type(); if (type != QEvent::KeyPress && type != QEvent::KeyRelease) { diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 4a187e4b61..b3203eb003 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -1031,6 +1031,7 @@ bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QT touchEvent.setTarget(_rootItem); touchEvent.setTouchPoints(touchPoints); touchEvent.setTouchPointStates(touchPointStates); + touchEvent.ignore(); } // Send mouse events to the surface so that HTML dialog elements work with mouse press and hover. @@ -1046,29 +1047,39 @@ bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QT buttons |= Qt::LeftButton; } - bool eventsAccepted = false; + bool eventSent = false; + bool eventsAccepted = true; if (event.getType() == PointerEvent::Move) { QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, event.getKeyboardModifiers()); // TODO - this line necessary for the QML Tooltop to work (which is not currently being used), but it causes interface to crash on launch on a fresh install // need to investigate into why this crash is happening. //_qmlContext->setContextProperty("lastMousePosition", windowPoint); - QCoreApplication::sendEvent(_quickWindow, &mouseEvent); - eventsAccepted &= mouseEvent.isAccepted(); + mouseEvent.ignore(); + if (QCoreApplication::sendEvent(_quickWindow, &mouseEvent)) { + eventSent = true; + eventsAccepted &= mouseEvent.isAccepted(); + } } if (touchType == QEvent::TouchBegin) { _touchBeginAccepted = QCoreApplication::sendEvent(_quickWindow, &touchEvent); + if (_touchBeginAccepted) { + eventSent = true; + eventsAccepted &= touchEvent.isAccepted(); + } } else if (_touchBeginAccepted) { - QCoreApplication::sendEvent(_quickWindow, &touchEvent); + if (QCoreApplication::sendEvent(_quickWindow, &touchEvent)) { + eventSent = true; + eventsAccepted &= touchEvent.isAccepted(); + } } - eventsAccepted &= touchEvent.isAccepted(); if (removeTouchPoint) { _activeTouchPoints.erase(event.getID()); } - return eventsAccepted; + return eventSent && eventsAccepted; } void OffscreenQmlSurface::pause() { diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js index c15f01efe9..3170352ac8 100644 --- a/tools/jsdoc/plugins/hifi.js +++ b/tools/jsdoc/plugins/hifi.js @@ -18,11 +18,13 @@ exports.handlers = { '../../interface/src/avatar', '../../interface/src/scripting', '../../interface/src/ui/overlays', + '../../interface/src/raypick', '../../libraries/animation/src', '../../libraries/avatars/src', '../../libraries/controllers/src/controllers/', '../../libraries/entities/src', '../../libraries/networking/src', + '../../libraries/pointers/src', '../../libraries/shared/src', '../../libraries/script-engine/src', ];