From 8de4a5907861d689c450bfc4f5ab45d9bb57e917 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sun, 28 Feb 2016 13:06:49 -0800 Subject: [PATCH 1/3] add support for auto-hide reticle and seek lookat on auto-show of reticle --- examples/autoHideReticle.js | 70 ++++++++++++++++++++++ interface/src/ui/ApplicationCompositor.cpp | 26 ++++---- 2 files changed, 84 insertions(+), 12 deletions(-) create mode 100644 examples/autoHideReticle.js diff --git a/examples/autoHideReticle.js b/examples/autoHideReticle.js new file mode 100644 index 0000000000..172dd9b9f7 --- /dev/null +++ b/examples/autoHideReticle.js @@ -0,0 +1,70 @@ +// autoHideReticle.js +// examples +// +// Created by Brad Hefta-Gaub on 2/23/16. +// Copyright 2016 High Fidelity, Inc. +// +// This script will auto-hide the reticle on inactivity... and then if it detects movement after being hidden +// it will automatically move the reticle to the look at position +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var MINIMUM_SEEK_DISTANCE = 0.01; +var NON_LINEAR_DIVISOR = 2; + +var lastMouseMove = Date.now(); +var HIDE_STATIC_MOUSE_AFTER = 5000; // 5 seconds +var seekToLookAt = false; + +Controller.mouseMoveEvent.connect(function(mouseEvent) { + lastMouseMove = Date.now(); + + // if the reticle is hidden, show it... + if (!Reticle.visible) { + Reticle.visible = true; + if (HMD.active) { + seekToLookAt = true; + } + } +}); + + + +Script.update.connect(function(deltaTime) { + + // if we're currently seeking the lookAt move the mouse toward the lookat + if (HMD.active && seekToLookAt) { + var lookAt2D = HMD.getHUDLookAtPosition2D(); + var currentReticlePosition = Reticle.position; + var distanceBetweenX = lookAt2D.x - Reticle.position.x; + var distanceBetweenY = lookAt2D.y - Reticle.position.y; + var moveX = distanceBetweenX / NON_LINEAR_DIVISOR; + var moveY = distanceBetweenY / NON_LINEAR_DIVISOR; + var newPosition = { x: Reticle.position.x + moveX, y: Reticle.position.y + moveY }; + var closeEnoughX = false; + var closeEnoughY = false; + if (moveX < MINIMUM_SEEK_DISTANCE) { + newPosition.x = lookAt2D.x; + closeEnoughX = true; + } + if (moveY < MINIMUM_SEEK_DISTANCE) { + newPosition.y = lookAt2D.y; + closeEnoughY = true; + } + if (closeEnoughX && closeEnoughY) { + seekToLookAt = false; + } + Reticle.position = newPosition; + } + + // if we haven't moved in a long period of time, hide the reticle + if (Reticle.visible) { + var now = Date.now(); + var timeSinceLastMouseMove = now - lastMouseMove; + if (timeSinceLastMouseMove > HIDE_STATIC_MOUSE_AFTER) { + Reticle.visible = false; + } + } +}); diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index 59d794d7cb..e8e6a0a956 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -212,19 +212,21 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) { geometryCache->renderUnitQuad(batch, vec4(vec3(1), _alpha)); //draw the mouse pointer - // Get the mouse coordinates and convert to NDC [-1, 1] - vec2 canvasSize = qApp->getCanvasSize(); // desktop, use actual canvas... - vec2 mousePosition = toNormalizedDeviceScale(vec2(qApp->getMouse()), canvasSize); - // Invert the Y axis - mousePosition.y *= -1.0f; + if (getReticleVisible()) { + // Get the mouse coordinates and convert to NDC [-1, 1] + vec2 canvasSize = qApp->getCanvasSize(); // desktop, use actual canvas... + vec2 mousePosition = toNormalizedDeviceScale(vec2(qApp->getMouse()), canvasSize); + // Invert the Y axis + mousePosition.y *= -1.0f; - Transform model; - model.setTranslation(vec3(mousePosition, 0)); - vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize; - model.setScale(vec3(mouseSize, 1.0f)); - batch.setModelTransform(model); - bindCursorTexture(batch); - geometryCache->renderUnitQuad(batch, vec4(1)); + Transform model; + model.setTranslation(vec3(mousePosition, 0)); + vec2 mouseSize = CURSOR_PIXEL_SIZE / canvasSize; + model.setScale(vec3(mouseSize, 1.0f)); + batch.setModelTransform(model); + bindCursorTexture(batch); + geometryCache->renderUnitQuad(batch, vec4(1)); + } }); } From 4009f03c384a553dcb708941958f84df30da410f Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 29 Feb 2016 08:38:53 -0800 Subject: [PATCH 2/3] move autohide/show behavior to depthReticle, make it a default script --- examples/autoHideReticle.js | 70 ------------------------------------ examples/defaultScripts.js | 1 + examples/depthReticle.js | 71 ++++++++++++++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 71 deletions(-) delete mode 100644 examples/autoHideReticle.js diff --git a/examples/autoHideReticle.js b/examples/autoHideReticle.js deleted file mode 100644 index 172dd9b9f7..0000000000 --- a/examples/autoHideReticle.js +++ /dev/null @@ -1,70 +0,0 @@ -// autoHideReticle.js -// examples -// -// Created by Brad Hefta-Gaub on 2/23/16. -// Copyright 2016 High Fidelity, Inc. -// -// This script will auto-hide the reticle on inactivity... and then if it detects movement after being hidden -// it will automatically move the reticle to the look at position -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -var MINIMUM_SEEK_DISTANCE = 0.01; -var NON_LINEAR_DIVISOR = 2; - -var lastMouseMove = Date.now(); -var HIDE_STATIC_MOUSE_AFTER = 5000; // 5 seconds -var seekToLookAt = false; - -Controller.mouseMoveEvent.connect(function(mouseEvent) { - lastMouseMove = Date.now(); - - // if the reticle is hidden, show it... - if (!Reticle.visible) { - Reticle.visible = true; - if (HMD.active) { - seekToLookAt = true; - } - } -}); - - - -Script.update.connect(function(deltaTime) { - - // if we're currently seeking the lookAt move the mouse toward the lookat - if (HMD.active && seekToLookAt) { - var lookAt2D = HMD.getHUDLookAtPosition2D(); - var currentReticlePosition = Reticle.position; - var distanceBetweenX = lookAt2D.x - Reticle.position.x; - var distanceBetweenY = lookAt2D.y - Reticle.position.y; - var moveX = distanceBetweenX / NON_LINEAR_DIVISOR; - var moveY = distanceBetweenY / NON_LINEAR_DIVISOR; - var newPosition = { x: Reticle.position.x + moveX, y: Reticle.position.y + moveY }; - var closeEnoughX = false; - var closeEnoughY = false; - if (moveX < MINIMUM_SEEK_DISTANCE) { - newPosition.x = lookAt2D.x; - closeEnoughX = true; - } - if (moveY < MINIMUM_SEEK_DISTANCE) { - newPosition.y = lookAt2D.y; - closeEnoughY = true; - } - if (closeEnoughX && closeEnoughY) { - seekToLookAt = false; - } - Reticle.position = newPosition; - } - - // if we haven't moved in a long period of time, hide the reticle - if (Reticle.visible) { - var now = Date.now(); - var timeSinceLastMouseMove = now - lastMouseMove; - if (timeSinceLastMouseMove > HIDE_STATIC_MOUSE_AFTER) { - Reticle.visible = false; - } - } -}); diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index 35af5f4eae..2c024c5bf0 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -22,3 +22,4 @@ Script.load("grab.js"); Script.load("directory.js"); Script.load("dialTone.js"); Script.load("attachedEntitiesManager.js"); +Script.load("depthReticle.js"); diff --git a/examples/depthReticle.js b/examples/depthReticle.js index 4b649f49b6..820125a3c7 100644 --- a/examples/depthReticle.js +++ b/examples/depthReticle.js @@ -5,6 +5,8 @@ // Copyright 2016 High Fidelity, Inc. // // When used in HMD, this script will make the reticle depth track to any clickable item in view. +// This script also handles auto-hiding the reticle after inactivity, as well as having the reticle +// seek the look at position upon waking up. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -17,8 +19,63 @@ var desiredDepth = APPARENT_2D_OVERLAY_DEPTH; var TIME_BETWEEN_DEPTH_CHECKS = 100; var MINIMUM_DEPTH_ADJUST = 0.01; var NON_LINEAR_DIVISOR = 2; +var MINIMUM_SEEK_DISTANCE = 0.01; -Script.update.connect(function(deltaTime) { +var lastMouseMove = Date.now(); +var HIDE_STATIC_MOUSE_AFTER = 3000; // 3 seconds +var shouldSeekToLookAt = false; + +Controller.mouseMoveEvent.connect(function(mouseEvent) { + lastMouseMove = Date.now(); + + // if the reticle is hidden, show it... + if (!Reticle.visible) { + Reticle.visible = true; + if (HMD.active) { + shouldSeekToLookAt = true; + } + } +}); + +function seekToLookAt() { + // if we're currently seeking the lookAt move the mouse toward the lookat + if (shouldSeekToLookAt) { + var lookAt2D = HMD.getHUDLookAtPosition2D(); + var currentReticlePosition = Reticle.position; + var distanceBetweenX = lookAt2D.x - Reticle.position.x; + var distanceBetweenY = lookAt2D.y - Reticle.position.y; + var moveX = distanceBetweenX / NON_LINEAR_DIVISOR; + var moveY = distanceBetweenY / NON_LINEAR_DIVISOR; + var newPosition = { x: Reticle.position.x + moveX, y: Reticle.position.y + moveY }; + var closeEnoughX = false; + var closeEnoughY = false; + if (moveX < MINIMUM_SEEK_DISTANCE) { + newPosition.x = lookAt2D.x; + closeEnoughX = true; + } + if (moveY < MINIMUM_SEEK_DISTANCE) { + newPosition.y = lookAt2D.y; + closeEnoughY = true; + } + if (closeEnoughX && closeEnoughY) { + shouldSeekToLookAt = false; + } + Reticle.position = newPosition; + } +} + +function autoHideReticle() { + // if we haven't moved in a long period of time, hide the reticle + if (Reticle.visible) { + var now = Date.now(); + var timeSinceLastMouseMove = now - lastMouseMove; + if (timeSinceLastMouseMove > HIDE_STATIC_MOUSE_AFTER) { + Reticle.visible = false; + } + } +} + +function checkReticleDepth() { var now = Date.now(); var timeSinceLastDepthCheck = now - lastDepthCheckTime; if (timeSinceLastDepthCheck > TIME_BETWEEN_DEPTH_CHECKS) { @@ -56,6 +113,9 @@ Script.update.connect(function(deltaTime) { } } +} + +function moveToDesiredDepth() { // move the reticle toward the desired depth if (desiredDepth != Reticle.depth) { @@ -69,4 +129,13 @@ Script.update.connect(function(deltaTime) { Reticle.setDepth(newDepth); } +} + +Script.update.connect(function(deltaTime) { + autoHideReticle(); // auto hide reticle for desktop or HMD mode + if (HMD.active) { + seekToLookAt(); // handle moving the reticle toward the look at + checkReticleDepth(); // make sure reticle is at correct depth + moveToDesiredDepth(); // move the fade the reticle to the desired depth + } }); From dc4c4e8c945ff284919e04877d2ffaa5c8a31425 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 29 Feb 2016 14:24:43 -0800 Subject: [PATCH 3/3] some tweaks to auto-hide --- examples/depthReticle.js | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/examples/depthReticle.js b/examples/depthReticle.js index 820125a3c7..14a5ba5ff3 100644 --- a/examples/depthReticle.js +++ b/examples/depthReticle.js @@ -22,11 +22,18 @@ var NON_LINEAR_DIVISOR = 2; var MINIMUM_SEEK_DISTANCE = 0.01; var lastMouseMove = Date.now(); +var lastMouseX = Reticle.position.x; +var lastMouseY = Reticle.position.y; var HIDE_STATIC_MOUSE_AFTER = 3000; // 3 seconds var shouldSeekToLookAt = false; +var fastMouseMoves = 0; +var averageMouseVelocity = 0; +var WEIGHTING = 1/20; // simple moving average over last 20 samples +var ONE_MINUS_WEIGHTING = 1 - WEIGHTING; +var AVERAGE_MOUSE_VELOCITY_FOR_SEEK_TO = 50; Controller.mouseMoveEvent.connect(function(mouseEvent) { - lastMouseMove = Date.now(); + var now = Date.now(); // if the reticle is hidden, show it... if (!Reticle.visible) { @@ -34,12 +41,30 @@ Controller.mouseMoveEvent.connect(function(mouseEvent) { if (HMD.active) { shouldSeekToLookAt = true; } + } else { + // even if the reticle is visible, if we're in HMD mode, and the person is moving their mouse quickly (shaking it) + // then they are probably looking for it, and we should move into seekToLookAt mode + if (HMD.active && !shouldSeekToLookAt) { + var dx = Reticle.position.x - lastMouseX; + var dy = Reticle.position.y - lastMouseY; + var dt = Math.max(1, (now - lastMouseMove)); // mSecs since last mouse move + var mouseMoveDistance = Math.sqrt((dx*dx) + (dy*dy)); + var mouseVelocity = mouseMoveDistance / dt; + averageMouseVelocity = (ONE_MINUS_WEIGHTING * averageMouseVelocity) + (WEIGHTING * mouseVelocity); + if (averageMouseVelocity > AVERAGE_MOUSE_VELOCITY_FOR_SEEK_TO) { + shouldSeekToLookAt = true; + } + } } + lastMouseMove = now; + lastMouseX = mouseEvent.x; + lastMouseY = mouseEvent.y; }); function seekToLookAt() { // if we're currently seeking the lookAt move the mouse toward the lookat if (shouldSeekToLookAt) { + averageMouseVelocity = 0; // reset this, these never count for movement... var lookAt2D = HMD.getHUDLookAtPosition2D(); var currentReticlePosition = Reticle.position; var distanceBetweenX = lookAt2D.x - Reticle.position.x; @@ -57,16 +82,17 @@ function seekToLookAt() { newPosition.y = lookAt2D.y; closeEnoughY = true; } + Reticle.position = newPosition; if (closeEnoughX && closeEnoughY) { shouldSeekToLookAt = false; } - Reticle.position = newPosition; } } function autoHideReticle() { - // if we haven't moved in a long period of time, hide the reticle - if (Reticle.visible) { + // if we haven't moved in a long period of time, and we're not pointing at some + // system overlay (like a window), then hide the reticle + if (Reticle.visible && !Reticle.pointingAtSystemOverlay) { var now = Date.now(); var timeSinceLastMouseMove = now - lastMouseMove; if (timeSinceLastMouseMove > HIDE_STATIC_MOUSE_AFTER) {