diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp index 2f57cc29d0..294a3f6e55 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp @@ -279,6 +279,18 @@ bool CompositorHelper::getReticleOverDesktop() const { return _isOverDesktop; } +bool CompositorHelper::isPositionOverDesktop(glm::vec2 position) const { + if (isHMD()) { + glm::vec2 maxOverlayPosition = _currentDisplayPlugin->getRecommendedUiSize(); + static const glm::vec2 minOverlayPosition; + if (glm::any(glm::lessThan(position, minOverlayPosition)) || + glm::any(glm::greaterThan(position, maxOverlayPosition))) { + return true; + } + } + return _isOverDesktop; +} + glm::vec2 CompositorHelper::getReticleMaximumPosition() const { glm::vec2 result; if (isHMD()) { @@ -468,3 +480,7 @@ void ReticleInterface::setScale(float scale) { auto& cursorManager = Cursor::Manager::instance(); cursorManager.setScale(scale); } + +bool ReticleInterface::isPointOnSystemOverlay(QVariant position) { + return !_compositor->isPositionOverDesktop(vec2FromVariant(position)); +} diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.h b/libraries/display-plugins/src/display-plugins/CompositorHelper.h index b1d2815f65..8534de6b9d 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.h +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.h @@ -106,6 +106,7 @@ public: /// if the reticle is pointing to a system overlay (a dialog box for example) then the function returns true otherwise false bool getReticleOverDesktop() const; + bool isPositionOverDesktop(glm::vec2 position) const; void setReticleOverDesktop(bool value) { _isOverDesktop = value; } void setDisplayPlugin(const DisplayPluginPointer& displayPlugin) { _currentDisplayPlugin = displayPlugin; } @@ -195,6 +196,7 @@ public: Q_INVOKABLE void setAllowMouseCapture(bool value) { return _compositor->setAllowMouseCapture(value); } Q_INVOKABLE bool isPointingAtSystemOverlay() { return !_compositor->getReticleOverDesktop(); } + Q_INVOKABLE bool isPointOnSystemOverlay(QVariant position); Q_INVOKABLE bool getVisible() { return _compositor->getReticleVisible(); } Q_INVOKABLE void setVisible(bool visible) { _compositor->setReticleVisible(visible); } diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index eb73b0f908..72da3c3f70 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -13,7 +13,7 @@ makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, AVATAR_SELF_ID, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic, - getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays + getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI */ @@ -21,7 +21,6 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); (function() { - var PICK_WITH_HAND_RAY = true; var halfPath = { @@ -423,12 +422,9 @@ Script.include("/~/system/libraries/controllers.js"); this.isPointingAtUI = function(controllerData) { var hudRayPickInfo = controllerData.hudRayPicks[this.hand]; - var hudPoint2d = HMD.overlayFromWorldPoint(hudRayPickInfo.intersection); - if (Reticle.pointingAtSystemOverlay || Overlays.getOverlayAtPoint(hudPoint2d || Reticle.position)) { - return true; - } - - return false; + var result = isPointingAtUI(hudRayPickInfo); + print(result); + return result; }; this.run = function (controllerData) { diff --git a/scripts/system/controllers/controllerModules/hudOverlayPointer.js b/scripts/system/controllers/controllerModules/hudOverlayPointer.js new file mode 100644 index 0000000000..0015a4a2d6 --- /dev/null +++ b/scripts/system/controllers/controllerModules/hudOverlayPointer.js @@ -0,0 +1,255 @@ +// +// hudOverlayPointer.js +// +// scripts/system/controllers/controllerModules/ +// +// Created by Dante Ruiz 2017-9-21 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +/* global Script, HMD, WebTablet, UIWebTablet, UserActivityLogger, Settings, Entities, Messages, Tablet, Overlays, + MyAvatar, Menu, AvatarInputs, Vec3 */ +(function() { + Script.include("/~/system/libraries/controllers.js") + var ControllerDispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); + var halfPath = { + type: "line3d", + color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, + visible: true, + alpha: 1, + solid: true, + glow: 1.0, + lineWidth: 5, + ignoreRayIntersection: true, // always ignore this + drawHUDLayer: true, // Even when burried inside of something, show it. + parentID: AVATAR_SELF_ID + }; + var halfEnd = { + type: "sphere", + solid: true, + color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, + alpha: 0.9, + ignoreRayIntersection: true, + drawHUDLayer: true, // Even when burried inside of something, show it. + visible: true + }; + var fullPath = { + type: "line3d", + color: COLORS_GRAB_SEARCHING_FULL_SQUEEZE, + visible: true, + alpha: 1, + solid: true, + glow: 1.0, + lineWidth: 5, + ignoreRayIntersection: true, // always ignore this + drawHUDLayer: true, // Even when burried inside of something, show it. + parentID: AVATAR_SELF_ID + }; + var fullEnd = { + type: "sphere", + solid: true, + color: COLORS_GRAB_SEARCHING_FULL_SQUEEZE, + alpha: 0.9, + ignoreRayIntersection: true, + drawHUDLayer: true, // Even when burried inside of something, show it. + visible: true + }; + var holdPath = { + type: "line3d", + color: COLORS_GRAB_DISTANCE_HOLD, + visible: true, + alpha: 1, + solid: true, + glow: 1.0, + lineWidth: 5, + ignoreRayIntersection: true, // always ignore this + drawHUDLayer: true, // Even when burried inside of something, show it. + parentID: AVATAR_SELF_ID + }; + + var renderStates = [ + {name: "half", path: halfPath, end: halfEnd}, + {name: "full", path: fullPath, end: fullEnd}, + {name: "hold", path: holdPath} + ]; + + var defaultRenderStates = [ + {name: "half", distance: DEFAULT_SEARCH_SPHERE_DISTANCE, path: halfPath}, + {name: "full", distance: DEFAULT_SEARCH_SPHERE_DISTANCE, path: fullPath}, + {name: "hold", distance: DEFAULT_SEARCH_SPHERE_DISTANCE, path: holdPath} + ]; + + + // triggered when stylus presses a web overlay/entity + var HAPTIC_STYLUS_STRENGTH = 1.0; + var HAPTIC_STYLUS_DURATION = 20.0; + var MARGIN = 25; + + function distance2D(a, b) { + var dx = (a.x - b.x); + var dy = (a.y - b.y); + return Math.sqrt(dx * dx + dy * dy); + } + + function HudOverlayPointer(hand) { + var _this = this; + this.hand = hand; + this.reticleMinX = MARGIN; + this.reticleMaxX; + this.reticleMinY = MARGIN; + this.reticleMaxY; + this.clicked = false; + this.triggerClicked = 0; + this.parameters = ControllerDispatcherUtils.makeDispatcherModuleParameters( + 540, + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100); + + this.getOtherHandController = function() { + return (this.hand === RIGHT_HAND) ? Controller.Standard.LeftHand : Controller.Standard.RightHand; + }; + + this.clicked = function() { + return this.clicked; + }; + + this.getOtherModule = function() { + return (this.hand === RIGHT_HAND) ? leftOverlayLaserInput : rightOverlayLaserInput; + }; + + this.handToController = function() { + return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + }; + + this.updateRecommendedArea = function() { + var dims = Controller.getViewportDimensions(); + this.reticleMaxX = dims.x - MARGIN; + this.reticleMaxY = dims.y - MARGIN; + }; + + this.hasNotSentClick = function() { + if (!_this.clicked) { + _this.clicked = true; + return true; + } + return false; + }; + + this.updateLaserPointer = function(controllerData) { + var RADIUS = 0.005; + var dim = { x: RADIUS, y: RADIUS, z: RADIUS }; + + if (this.mode === "full") { + this.fullEnd.dimensions = dim; + LaserPointers.editRenderState(this.laserPointer, this.mode, {path: fullPath, end: this.fullEnd}); + } else if (this.mode === "half") { + this.halfEnd.dimensions = dim; + LaserPointers.editRenderState(this.laserPointer, this.mode, {path: halfPath, end: this.halfEnd}); + } + + LaserPointers.enableLaserPointer(this.laserPointer); + LaserPointers.setRenderState(this.laserPointer, this.mode); + }; + + this.processControllerTriggers = function(controllerData) { + if (controllerData.triggerClicks[this.hand]) { + this.mode = "full"; + } else if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { + this.clicked = false; + this.mode = "half"; + } else { + this.mode = "none"; + } + }; + + this.calculateNewReticlePosition = function(intersection) { + this.updateRecommendedArea(); + var point2d = HMD.overlayFromWorldPoint(intersection); + point2d.x = Math.max(this.reticleMinX, Math.min(point2d.x, this.reticleMaxX)); + point2d.y = Math.max(this.reticleMinY, Math.min(point2d.y, this.reticleMaxY)); + return point2d; + }; + + this.setReticlePosition = function(point2d) { + Reticle.setPosition(point2d); + }; + + this.processLaser = function(controllerData) { + var controllerLocation = controllerData.controllerLocations[this.hand]; + if (controllerData.triggerValues[this.hand] < ControllerDispatcherUtils.TRIGGER_OFF_VALUE || !controllerLocation.valid) { + this.exitModule(); + return false; + } + + var hudRayPick = controllerData.hudRayPicks[this.hand]; + var controllerLocation = controllerData.controllerLocations[this.hand]; + var point2d = this.calculateNewReticlePosition(hudRayPick.intersection); + this.setReticlePosition(point2d); + print(Reticle.isPointOnSystemOverlay(point2d)); + if (!Reticle.isPointOnSystemOverlay(point2d)) { + this.exitModule(); + print("----> exiting <------"); + return false; + } + + //this.setReticlePosition(point2d); + + this.clicked = controllerData.triggerClicked[this.hand]; + + this.processControllerTriggers(controllerData); + this.updateLaserPointer(controllerData); + return true; + }; + + this.exitModule = function() { + LaserPointers.disableLaserPointer(this.laserPointer); + }; + + this.isReady = function (controllerData) { + if (this.processLaser(controllerData)) { + return ControllerDispatcherUtils.makeRunningValues(true, [], []); + } else { + return ControllerDispatcherUtils.makeRunningValues(false, [], []); + } + }; + + this.run = function (controllerData, deltaTime) { + return this.isReady(controllerData); + }; + + this.cleanup = function () { + LaserPointers.disableLaserPointer(this.laserPointer); + LaserPointers.removeLaserPointer(this.laserPointer); + }; + + this.halfEnd = halfEnd; + this.fullEnd = fullEnd; + this.laserPointer = LaserPointers.createLaserPointer({ + joint: (this.hand === RIGHT_HAND) ? "_CONTROLLER_RIGHTHAND" : "_CONTROLLER_LEFTHAND", + filter: RayPick.PICK_HUD, + maxDistance: PICK_MAX_DISTANCE, + posOffset: getGrabPointSphereOffset(this.handToController(), true), + renderStates: renderStates, + enabled: true, + defaultRenderStates: defaultRenderStates + }); + } + + + var leftHudOverlayPointer = new HudOverlayPointer(LEFT_HAND); + var rightHudOverlayPointer = new HudOverlayPointer(RIGHT_HAND); + + var clickMapping = Controller.newMapping('HudOverlayPointer-click'); + clickMapping.from(rightHudOverlayPointer.clicked()).when(rightHudOverlayPointer.hasNotSentClick()).to(Controller.Actions.ReticleClick); + clickMapping.from(leftHudOverlayPointer.clicked()).when(leftHudOverlayPointer.hasNotSentClick()).to(Controller.Actions.ReticleClick); + clickMapping.enable(); + + enableDispatcherModule("LeftHudOverlayPointer", leftHudOverlayPointer); + enableDispatcherModule("RightHudOverlayPointer", rightHudOverlayPointer); + + +})(); diff --git a/scripts/system/controllers/controllerModules/scaleAvatar.js b/scripts/system/controllers/controllerModules/scaleAvatar.js index 05804c967b..de0434258c 100644 --- a/scripts/system/controllers/controllerModules/scaleAvatar.js +++ b/scripts/system/controllers/controllerModules/scaleAvatar.js @@ -1,4 +1,4 @@ -// handControllerGrab.js +// scaleAvatar.js // // Created by Dante Ruiz on 9/11/17 // @@ -76,8 +76,9 @@ dispatcherUtils.enableDispatcherModule("LeftScaleAvatar", leftScaleAvatar); dispatcherUtils.enableDispatcherModule("RightScaleAvatar", rightScaleAvatar); - this.cleanup = function() { + function cleanup() { dispatcherUtils.disableDispatcherModule("LeftScaleAvatar"); dispatcherUtils.disableDispatcherModule("RightScaleAvatar"); }; + Script.scriptEnding.connect(cleanup); })(); diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index e8b07c623d..695dea7b2c 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -12,14 +12,14 @@ var CONTOLLER_SCRIPTS = [ "squeezeHands.js", "controllerDisplayManager.js", - "handControllerPointer.js", + //"handControllerPointer.js", "grab.js", "toggleAdvancedMovementForHandControllers.js", "controllerDispatcher.js", "controllerModules/nearParentGrabEntity.js", "controllerModules/nearParentGrabOverlay.js", "controllerModules/nearActionGrabEntity.js", - "controllerModules/farActionGrabEntity.js", + //"controllerModules/farActionGrabEntity.js", "controllerModules/tabletStylusInput.js", "controllerModules/equipEntity.js", "controllerModules/nearTrigger.js", @@ -29,7 +29,8 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/disableOtherModule.js", "controllerModules/farTrigger.js", "controllerModules/teleport.js", - "controllerModules/scaleAvatar.js" + "controllerModules/scaleAvatar.js", + "controllerModules/hudOverlayPointer.js" ]; var DEBUG_MENU_ITEM = "Debug defaultScripts.js"; diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 832fe10d5f..1c988bfd34 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -314,7 +314,7 @@ function activeHudPoint2dGamePad() { var hudPoint2d = overlayFromWorldPoint(hudPoint3d); // We don't know yet if we'll want to make the cursor or laser visble, but we need to move it to see if - // it's pointing at a QML tool (aka system overlay). + // it's pointing at aQML tool (aka system overlay). setReticlePosition(hudPoint2d); return hudPoint2d; @@ -328,7 +328,7 @@ function activeHudPoint2d(activeHand) { // if controller is valid, update reticl } var controllerPosition = controllerPose.position; var controllerDirection = Quat.getUp(controllerPose.rotation); - + var hudPoint3d = calculateRayUICollisionPoint(controllerPosition, controllerDirection, true); if (!hudPoint3d) { if (Menu.isOptionChecked("Overlays")) { // With our hud resetting strategy, hudPoint3d should be valid here @@ -523,21 +523,21 @@ var wantsMenu = 0; clickMapping.from(function () { return wantsMenu; }).to(Controller.Actions.ContextMenu); clickMapping.from(Controller.Standard.RightSecondaryThumb).peek().to(function (clicked) { if (clicked) { - activeHudPoint2d(Controller.Standard.RightHand); + //activeHudPoint2d(Controller.Standard.RightHand); Messages.sendLocalMessage("toggleHand", Controller.Standard.RightHand); } wantsMenu = clicked; }); clickMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(function (clicked) { if (clicked) { - activeHudPoint2d(Controller.Standard.LeftHand); + //activeHudPoint2d(Controller.Standard.LeftHand); Messages.sendLocalMessage("toggleHand", Controller.Standard.LeftHand); } wantsMenu = clicked; }); clickMapping.from(Controller.Standard.Start).peek().to(function (clicked) { if (clicked) { - activeHudPoint2dGamePad(); + //activeHudPoint2dGamePad(); var noHands = -1; Messages.sendLocalMessage("toggleHand", Controller.Standard.LeftHand); } diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index 10931e4e93..652bd5765b 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -40,7 +40,8 @@ entityHasActions:true, ensureDynamic:true, findGroupParent:true, - BUMPER_ON_VALUE:true + BUMPER_ON_VALUE:true, + isPointingAtUI: true */ MSECS_PER_SEC = 1000.0; @@ -310,6 +311,18 @@ findGroupParent = function (controllerData, targetProps) { return targetProps; }; +isPointingAtUI = function(intersection) { + var MARGIN = 25; + var reticleMinX = MARGIN, reticleMaxX, reticleMinY = MARGIN, reticleMaxY; + var dims = Controller.getViewportDimensions(); + reticleMaxX = dims.x - MARGIN; + reticleMaxY = dims.y - MARGIN; + var point2d = HMD.overlayFromWorldPoint(intersection.intersection); + point2d.x = Math.max(reticleMinX, Math.min(point2d.x, reticleMaxX)); + point2d.y = Math.max(reticleMinY, Math.min(point2d.y, reticleMaxY)); + return point2d; +} + if (typeof module !== 'undefined') { module.exports = { makeDispatcherModuleParameters: makeDispatcherModuleParameters, @@ -318,8 +331,11 @@ if (typeof module !== 'undefined') { makeRunningValues: makeRunningValues, LEFT_HAND: LEFT_HAND, RIGHT_HAND: RIGHT_HAND, + isPointingAtUI: isPointingAtUI, BUMPER_ON_VALUE: BUMPER_ON_VALUE, projectOntoOverlayXYPlane: projectOntoOverlayXYPlane, - projectOntoEntityXYPlane: projectOntoEntityXYPlane + projectOntoEntityXYPlane: projectOntoEntityXYPlane, + TRIGGER_OFF_VALUE: TRIGGER_OFF_VALUE, + TRIGGER_ON_VALUE: TRIGGER_ON_VALUE }; } diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index 63c1cc51aa..9d2382b3f8 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -272,6 +272,36 @@ Messages.subscribe("home"); Messages.messageReceived.connect(handleMessage); + var clickMapping = Controller.newMapping('tabletToggle-click'); + var wantsMenu = 0; + clickMapping.from(function () { return wantsMenu; }).to(Controller.Actions.ContextMenu); + clickMapping.from(Controller.Standard.RightSecondaryThumb).peek().to(function (clicked) { + if (clicked) { + //activeHudPoint2d(Controller.Standard.RightHand); + Messages.sendLocalMessage("toggleHand", Controller.Standard.RightHand); + } + wantsMenu = clicked; + }); + + clickMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(function (clicked) { + if (clicked) { + //activeHudPoint2d(Controller.Standard.LeftHand); + Messages.sendLocalMessage("toggleHand", Controller.Standard.LeftHand); + } + wantsMenu = clicked; + }); + + clickMapping.from(Controller.Standard.Start).peek().to(function (clicked) { + if (clicked) { + //activeHudPoint2dGamePad(); + var noHands = -1; + Messages.sendLocalMessage("toggleHand", Controller.Standard.LeftHand); + } + + wantsMenu = clicked; + }); + clickMapping.enable(); + Script.setInterval(updateShowTablet, 100); Script.scriptEnding.connect(function () {