diff --git a/interface/src/raypick/PointerScriptingInterface.cpp b/interface/src/raypick/PointerScriptingInterface.cpp index ac5a467e76..a334834979 100644 --- a/interface/src/raypick/PointerScriptingInterface.cpp +++ b/interface/src/raypick/PointerScriptingInterface.cpp @@ -175,4 +175,8 @@ QVariantMap PointerScriptingInterface::getPrevPickResult(unsigned int uid) const result = pickResult->toVariantMap(); } return result; -} \ No newline at end of file +} + +void PointerScriptingInterface::setDoesHover(unsigned int uid, bool hover) const { + DependencyManager::get()->setDoesHover(uid, hover); +} diff --git a/interface/src/raypick/PointerScriptingInterface.h b/interface/src/raypick/PointerScriptingInterface.h index 1cc7b56503..451c132769 100644 --- a/interface/src/raypick/PointerScriptingInterface.h +++ b/interface/src/raypick/PointerScriptingInterface.h @@ -202,6 +202,14 @@ public: */ 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 + * Sets whether or not a pointer should generate hover events. + * @function Pointers.setDoesHover + * @param {boolean} uid - The ID of the Pointer, as returned by {@link Pointers.createPointer}. + * @param {boolean} hover - If true then the pointer generates hover events, otherwise it does not. + */ + Q_INVOKABLE void setDoesHover(unsigned int uid, bool hove) const; + /**jsdoc * Check if a Pointer is associated with the left hand. * @function Pointers.isLeftHand diff --git a/libraries/pointers/src/Pointer.cpp b/libraries/pointers/src/Pointer.cpp index 5307e17355..287d5a3c97 100644 --- a/libraries/pointers/src/Pointer.cpp +++ b/libraries/pointers/src/Pointer.cpp @@ -64,6 +64,12 @@ bool Pointer::isMouse() const { return DependencyManager::get()->isMouse(_pickUID); } +void Pointer::setDoesHover(bool doesHover) { + withWriteLock([&] { + _hover = doesHover; + }); +} + void Pointer::update(unsigned int pointerID) { // This only needs to be a read lock because update won't change any of the properties that can be modified from scripts withReadLock([&] { @@ -95,7 +101,8 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin } // Hover events - bool doHover = shouldHover(pickResult); + bool doHover = _hover && shouldHover(pickResult); + Pointer::PickedObject hoveredObject = getHoveredObject(pickResult); PointerEvent hoveredEvent = buildPointerEvent(hoveredObject, pickResult); hoveredEvent.setType(PointerEvent::Move); @@ -104,7 +111,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin hoveredEvent.setMoveOnHoverLeave(moveOnHoverLeave); // if shouldHover && !_prevDoHover, only send hoverBegin - if (_enabled && _hover && doHover && !_prevDoHover) { + if (_enabled && doHover && !_prevDoHover) { if (hoveredObject.type == ENTITY) { emit pointerManager->hoverBeginEntity(hoveredObject.objectID, hoveredEvent); } else if (hoveredObject.type == OVERLAY) { @@ -112,7 +119,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin } else if (hoveredObject.type == HUD) { emit pointerManager->hoverBeginHUD(hoveredEvent); } - } else if (_enabled && _hover && doHover) { + } else if (_enabled && doHover) { if (hoveredObject.type == OVERLAY) { if (_prevHoveredObject.type == OVERLAY) { if (hoveredObject.objectID == _prevHoveredObject.objectID) { @@ -229,7 +236,7 @@ void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPoin } // if we disable the pointer or disable hovering, send hoverEnd events after triggerEnd - if (_hover && ((!_enabled && _prevEnabled) || (!doHover && _prevDoHover))) { + if ((!_enabled && _prevEnabled) || (!doHover && _prevDoHover)) { if (_prevHoveredObject.type == ENTITY) { emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent); } else if (_prevHoveredObject.type == OVERLAY) { diff --git a/libraries/pointers/src/Pointer.h b/libraries/pointers/src/Pointer.h index 3197c80cad..9fd434fb15 100644 --- a/libraries/pointers/src/Pointer.h +++ b/libraries/pointers/src/Pointer.h @@ -62,6 +62,8 @@ public: virtual void setLength(float length) {} virtual void setLockEndUUID(const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) {} + virtual void setDoesHover(bool hover); + void update(unsigned int pointerID); virtual void updateVisuals(const PickResultPointer& pickResult) = 0; void generatePointerEvents(unsigned int pointerID, const PickResultPointer& pickResult); @@ -101,7 +103,6 @@ private: std::unordered_map _triggeredObjects; PointerEvent::Button chooseButton(const std::string& button); - }; #endif // hifi_Pick_h diff --git a/libraries/pointers/src/PointerManager.cpp b/libraries/pointers/src/PointerManager.cpp index be890da392..13b38457b6 100644 --- a/libraries/pointers/src/PointerManager.cpp +++ b/libraries/pointers/src/PointerManager.cpp @@ -122,6 +122,13 @@ void PointerManager::setLockEndUUID(unsigned int uid, const QUuid& objectID, boo } } +void PointerManager::setDoesHover(unsigned int uid, bool hover) const { + auto pointer = find(uid); + if (pointer) { + pointer->setDoesHover(hover); + } +} + 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 b98558622f..2c9a37e129 100644 --- a/libraries/pointers/src/PointerManager.h +++ b/libraries/pointers/src/PointerManager.h @@ -37,6 +37,7 @@ public: void setLength(unsigned int uid, float length) const; void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay, const glm::mat4& offsetMat = glm::mat4()) const; + void setDoesHover(unsigned int uid, bool hover) const; void update(); diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 16f1d086b7..a8658933e7 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -44,7 +44,12 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.highVarianceCount = 0; this.veryhighVarianceCount = 0; this.tabletID = null; + this.TABLET_UI_UUIDS = []; this.blacklist = []; + this.leftPointerDoesHover = true; + this.leftPointerDoesHoverChanged = false; + this.rightPointerDoesHover = true; + this.rightPointerDoesHoverChanged = false; this.pointerManager = new PointerManager(); // a module can occupy one or more "activity" slots while it's running. If all the required slots for a module are @@ -122,6 +127,10 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); return getControllerWorldLocation(Controller.Standard.RightHand, true); }; + this.isTabletID = function (uuid) { + return _this.TABLET_UI_UUIDS.indexOf(uuid) !== -1; + }; + this.updateTimings = function () { _this.intervalCount++; var thisInterval = Date.now(); @@ -148,11 +157,35 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.setIgnorePointerItems = function() { if (HMD.tabletID !== this.tabletID) { this.tabletID = HMD.tabletID; + this.TABLET_UI_UUIDS = [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID, HMD.homeButtonHighlightID]; Pointers.setIgnoreItems(_this.leftPointer, _this.blacklist); Pointers.setIgnoreItems(_this.rightPointer, _this.blacklist); } }; + this.updateDoesHover = function(handLaser, doesHover) { + if (handLaser.doesHover !== undefined) { + if (handLaser.hand === LEFT_HAND && _this.leftPointerDoesHover !== doesHover) { + _this.leftPointerDoesHover = doesHover; + _this.leftPointerDoesHoverChanged = true; + } else if (handLaser.hand === RIGHT_HAND && _this.rightPointerDoesHover !== doesHover) { + _this.rightPointerDoesHover = doesHover; + _this.rightPointerDoesHoverChanged = true; + } + } + } + + this.updateHovering = function () { + if (_this.leftPointerDoesHoverChanged) { + Pointers.setDoesHover(_this.leftPointer, _this.leftPointerDoesHover); + _this.leftPointerDoesHoverChanged = false; + } + if (_this.rightPointerDoesHoverChanged) { + Pointers.setDoesHover(_this.rightPointer, _this.rightPointerDoesHover); + _this.rightPointerDoesHoverChanged = false; + } + }; + this.update = function () { try { _this.updateInternal(); @@ -324,6 +357,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); _this.runningPluginNames[orderedPluginName] = true; _this.markSlots(candidatePlugin, orderedPluginName); _this.pointerManager.makePointerVisible(candidatePlugin.parameters.handLaser); + _this.updateDoesHover(candidatePlugin.parameters.handLaser, + candidatePlugin.parameters.handLaser.doesHover); if (DEBUG) { print("controllerDispatcher running " + orderedPluginName); } @@ -354,12 +389,15 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.beginProfileRange("dispatch.run." + runningPluginName); } var runningness = plugin.run(controllerData, deltaTime); - if (!runningness.active) { + if (runningness.active) { + _this.updateDoesHover(plugin.parameters.handLaser, plugin.parameters.handLaser.doesHover); + } else { // plugin is finished running, for now. remove it from the list // of running plugins and mark its activity-slots as "not in use" delete _this.runningPluginNames[runningPluginName]; _this.markSlots(plugin, false); _this.pointerManager.makePointerInvisible(plugin.parameters.handLaser); + _this.updateDoesHover(plugin.parameters.handLaser, true); if (DEBUG) { print("controllerDispatcher stopping " + runningPluginName); } @@ -372,6 +410,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); } } _this.pointerManager.updatePointersRenderState(controllerData.triggerClicks, controllerData.triggerValues); + _this.updateHovering(); + if (PROFILE) { Script.endProfileRange("dispatch.run"); } diff --git a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js index 3d9d7979d5..cc8378af84 100644 --- a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js +++ b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js @@ -87,41 +87,89 @@ Script.include("/~/system/libraries/controllers.js"); return MyAvatar.getDominantHand() === "right" ? 1 : 0; }; - this.dominantHandOverride = false; + this.letOtherHandRunFirst = function (controllerData, pointingAt) { + // If both hands are ready to run, let the other hand run first if it is the dominant hand so that it gets the + // highlight. + var isOtherTriggerPressed = controllerData.triggerValues[this.otherHand] > TRIGGER_OFF_VALUE; + var isLetOtherHandRunFirst = !this.getOtherModule().running + && this.getDominantHand() === this.otherHand + && (this.parameters.handLaser.allwaysOn || isOtherTriggerPressed); + if (isLetOtherHandRunFirst) { + var otherHandPointingAt = controllerData.rayPicks[this.otherHand].objectID; + if (this.isTabletID(otherHandPointingAt)) { + otherHandPointingAt = HMD.tabletID; + } + isLetOtherHandRunFirst = pointingAt === otherHandPointingAt; + } + return isLetOtherHandRunFirst; + }; + + this.hoverItem = null; + + this.isTabletID = function (uuid) { + return [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID, HMD.homeButtonHighlightID].indexOf(uuid) !== -1; + }; this.isReady = function(controllerData) { - var otherModuleRunning = this.getOtherModule().running; - otherModuleRunning = otherModuleRunning && this.getDominantHand() !== this.hand; // Auto-swap to dominant hand. - var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE - && controllerData.triggerValues[this.otherHand] <= TRIGGER_OFF_VALUE; - if ((!otherModuleRunning || isTriggerPressed) - && (this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData))) { + if (this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData)) { this.updateAllwaysOn(); - if (isTriggerPressed) { - this.dominantHandOverride = true; // Override dominant hand. - this.getOtherModule().dominantHandOverride = false; - } + + var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE; if (this.parameters.handLaser.allwaysOn || isTriggerPressed) { - return makeRunningValues(true, [], []); + var pointingAt = controllerData.rayPicks[this.hand].objectID; + if (this.isTabletID(pointingAt)) { + pointingAt = HMD.tabletID; + } + + if (!this.letOtherHandRunFirst(controllerData, pointingAt)) { + + if (pointingAt !== this.getOtherModule().hoverItem) { + this.parameters.handLaser.doesHover = true; + this.hoverItem = pointingAt; + } else { + this.parameters.handLaser.doesHover = false; + this.hoverItem = null; + } + + return makeRunningValues(true, [], []); + } } } + + this.parameters.handLaser.doesHover = false; + this.hoverItem = null; + return makeRunningValues(false, [], []); }; this.run = function(controllerData, deltaTime) { - var otherModuleRunning = this.getOtherModule().running; - otherModuleRunning = otherModuleRunning && this.getDominantHand() !== this.hand; // Auto-swap to dominant hand. - otherModuleRunning = otherModuleRunning || this.getOtherModule().dominantHandOverride; // Override dominant hand. var grabModuleNeedsToRun = this.grabModuleWantsNearbyOverlay(controllerData); - if (!otherModuleRunning && !grabModuleNeedsToRun && (controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE - || this.parameters.handLaser.allwaysOn + var isTriggerPressed = controllerData.triggerValues[this.hand] > TRIGGER_OFF_VALUE; + if (!grabModuleNeedsToRun && (isTriggerPressed || this.parameters.handLaser.allwaysOn && (this.isPointingAtOverlay(controllerData) || this.isPointingAtWebEntity(controllerData)))) { this.running = true; + + var pointingAt = controllerData.rayPicks[this.hand].objectID; + if (this.isTabletID(pointingAt)) { + pointingAt = HMD.tabletID; + } + + if (pointingAt !== this.getOtherModule().hoverItem || isTriggerPressed) { + this.parameters.handLaser.doesHover = true; + this.hoverItem = pointingAt; + } else { + this.parameters.handLaser.doesHover = false; + this.hoverItem = null; + } + return makeRunningValues(true, [], []); } this.deleteContextOverlay(); this.running = false; - this.dominantHandOverride = false; + + this.parameters.handLaser.doesHover = false; + this.hoverItem = null; + return makeRunningValues(false, [], []); }; }