diff --git a/examples/controllers/oculus/goTo.js b/examples/controllers/oculus/goTo.js new file mode 100644 index 0000000000..91ae8e6141 --- /dev/null +++ b/examples/controllers/oculus/goTo.js @@ -0,0 +1,248 @@ +// +// goTo.js +// examples +// +// Created by Thijs Wenker on 12/28/14. +// Copyright 2014 High Fidelity, Inc. +// +// Control a virtual keyboard using your favorite HMD. +// Usage: Enable VR-mode and go to First person mode, +// look at the key that you would like to press, and press the spacebar on your "REAL" keyboard. +// +// Enter a location URL using your HMD. Press Enter to pop-up the virtual keyboard and location input. +// Press Space on the keyboard or the X button on your gamepad to press a key that you have selected. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include("../../libraries/globals.js"); +Script.include("../../libraries/virtualKeyboard.js"); + +const MAX_SHOW_INSTRUCTION_TIMES = 2; +const INSTRUCTIONS_SETTING = "GoToInstructionsShowCounter" + +var windowDimensions = Controller.getViewportDimensions(); + +function Instructions(imageURL, originalWidth, originalHeight) { + var tthis = this; + this.originalSize = {w: originalWidth, h: originalHeight}; + this.visible = false; + this.overlay = Overlays.addOverlay("image", { + imageURL: imageURL, + x: 0, + y: 0, + width: originalWidth, + height: originalHeight, + alpha: 1, + visible: this.visible + }); + + this.show = function() { + var timesShown = Settings.getValue(INSTRUCTIONS_SETTING); + timesShown = timesShown === "" ? 0 : parseInt(timesShown); + print(timesShown); + if (timesShown < MAX_SHOW_INSTRUCTION_TIMES) { + Settings.setValue(INSTRUCTIONS_SETTING, timesShown + 1); + tthis.visible = true; + tthis.rescale(); + Overlays.editOverlay(tthis.overlay, {visible: tthis.visible}); + return; + } + tthis.remove(); + } + + this.remove = function() { + Overlays.deleteOverlay(tthis.overlay); + tthis.visible = false; + }; + + this.rescale = function() { + var scale = Math.min(windowDimensions.x / tthis.originalSize.w, windowDimensions.y / tthis.originalSize.h); + var newWidth = tthis.originalSize.w * scale; + var newHeight = tthis.originalSize.h * scale; + Overlays.editOverlay(tthis.overlay, { + x: (windowDimensions.x / 2) - (newWidth / 2), + y: (windowDimensions.y / 2) - (newHeight / 2), + width: newWidth, + height: newHeight + }); + }; + this.rescale(); +}; + +var theInstruction = new Instructions(HIFI_PUBLIC_BUCKET + "images/tutorial-goTo.svg", 457, 284.1); + +var firstControllerPlugged = false; + + +var cursor = new Cursor({visible: false}); +var keyboard = new Keyboard({visible: false}); +var textFontSize = 9; +var text = null; +var locationURL = ""; + +function appendChar(char) { + locationURL += char; + updateTextOverlay(); + Overlays.editOverlay(text, {text: locationURL}); +} + +function deleteChar() { + if (locationURL.length > 0) { + locationURL = locationURL.substring(0, locationURL.length - 1); + updateTextOverlay(); + } +} + +function updateTextOverlay() { + var maxLineWidth = Overlays.textSize(text, locationURL).width; + var suggestedFontSize = (windowDimensions.x / maxLineWidth) * textFontSize * 0.90; + var maxFontSize = 140; + textFontSize = (suggestedFontSize > maxFontSize) ? maxFontSize : suggestedFontSize; + var topMargin = (250 - textFontSize) / 4; + Overlays.editOverlay(text, {text: locationURL, font: {size: textFontSize}, topMargin: topMargin, visible: keyboard.visible}); + maxLineWidth = Overlays.textSize(text, locationURL).width; + Overlays.editOverlay(text, {leftMargin: (windowDimensions.x - maxLineWidth) / 2}); +} + +keyboard.onKeyPress = function(event) { + if (event.event == 'keypress') { + appendChar(event.char); + } +}; + +keyboard.onKeyRelease = function(event) { + // you can cancel a key by releasing its focusing before releasing it + if (event.focus) { + if (event.event == 'delete') { + deleteChar(); + } else if (event.event == 'submit' || event.event == 'enter') { + print("going to hifi://" + locationURL); + location = "hifi://" + locationURL; + locationURL = ""; + keyboard.hide(); + cursor.hide(); + updateTextOverlay(); + } + } +}; + +keyboard.onFullyLoaded = function() { + print("Virtual-keyboard fully loaded."); + var dimensions = Controller.getViewportDimensions(); + text = Overlays.addOverlay("text", { + x: 0, + y: dimensions.y - keyboard.height() - 260, + width: dimensions.x, + height: 250, + backgroundColor: { red: 255, green: 255, blue: 255}, + color: { red: 0, green: 0, blue: 0}, + topMargin: 5, + leftMargin: 0, + font: {size: textFontSize}, + text: "", + alpha: 0.8, + visible: keyboard.visible + }); + updateTextOverlay(); + // the cursor is being loaded after the keyboard, else it will be on the background of the keyboard + cursor.initialize(); + cursor.updateVisibility(keyboard.visible); + cursor.onUpdate = function(position) { + keyboard.setFocusPosition(position.x, position.y); + }; +}; + +function keyPressEvent(event) { + if (theInstruction.visible) { + return; + } + if (event.key === SPACEBAR_CHARCODE) { + keyboard.pressFocussedKey(); + } else if (event.key === ENTER_CHARCODE || event.key === RETURN_CHARCODE) { + keyboard.toggle(); + if (cursor !== undefined) { + cursor.updateVisibility(keyboard.visible); + } + Overlays.editOverlay(text, {visible: keyboard.visible}); + } +} + +function keyReleaseEvent(event) { + if (theInstruction.visible) { + return; + } + if (event.key === SPACEBAR_CHARCODE) { + keyboard.releaseKeys(); + } +} + +function scriptEnding() { + keyboard.remove(); + cursor.remove(); + Overlays.deleteOverlay(text); + Overlays.deleteOverlay(textSizeMeasureOverlay); + Controller.releaseKeyEvents({key: SPACEBAR_CHARCODE}); + Controller.releaseKeyEvents({key: RETURN_CHARCODE}); + Controller.releaseKeyEvents({key: ENTER_CHARCODE}); + theInstruction.remove(); +} + +function reportButtonValue(button, newValue, oldValue) { + if (theInstruction.visible) { + if (button == Joysticks.BUTTON_FACE_BOTTOM && newValue) { + theInstruction.remove(); + } + } else if (button == Joysticks.BUTTON_FACE_BOTTOM) { + if (newValue) { + keyboard.pressFocussedKey(); + } else { + keyboard.releaseKeys(); + } + } else if (button == Joysticks.BUTTON_FACE_RIGHT && newValue) { + deleteChar(); + } else if (button == Joysticks.BUTTON_FACE_LEFT && newValue) { + keyboard.hide(); + if (cursor !== undefined) { + cursor.hide(); + } + Overlays.editOverlay(text, {visible: false}); + } else if (button == Joysticks.BUTTON_RIGHT_SHOULDER && newValue) { + if (keyboard.visible) { + print("going to hifi://" + locationURL); + location = "hifi://" + locationURL; + locationURL = ""; + keyboard.hide(); + cursor.hide(); + updateTextOverlay(); + return; + } + keyboard.show(); + if (cursor !== undefined) { + cursor.show(); + } + Overlays.editOverlay(text, {visible: true}); + } +} + +function addJoystick(gamepad) { + gamepad.buttonStateChanged.connect(reportButtonValue); + if (!firstControllerPlugged) { + firstControllerPlugged = true; + theInstruction.show(); + } +} + +var allJoysticks = Joysticks.getAllJoysticks(); +for (var i = 0; i < allJoysticks.length; i++) { + addJoystick(allJoysticks[i]); +} + +Joysticks.joystickAdded.connect(addJoystick); +Controller.captureKeyEvents({key: RETURN_CHARCODE}); +Controller.captureKeyEvents({key: ENTER_CHARCODE}); +Controller.captureKeyEvents({key: SPACEBAR_CHARCODE}); +Controller.keyPressEvent.connect(keyPressEvent); +Controller.keyReleaseEvent.connect(keyReleaseEvent); +Script.scriptEnding.connect(scriptEnding); diff --git a/examples/controllers/oculus/virtualKeyboardTextEntityExample.js b/examples/controllers/oculus/virtualKeyboardTextEntityExample.js new file mode 100644 index 0000000000..c3cb3c6316 --- /dev/null +++ b/examples/controllers/oculus/virtualKeyboardTextEntityExample.js @@ -0,0 +1,183 @@ +// +// virtualKeyboardTextEntityExample.js +// examples +// +// Created by Thijs Wenker on 12/28/14. +// Copyright 2014 High Fidelity, Inc. +// +// Control a virtual keyboard using your favorite HMD. +// Usage: Enable VR-mode and go to First person mode, +// look at the key that you would like to press, and press the spacebar on your "REAL" keyboard. +// +// leased some code from newEditEntities.js for Text Entity example +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include("../../libraries/globals.js"); +Script.include("../../libraries/virtualKeyboard.js"); + +const SPAWN_DISTANCE = 1; +const DEFAULT_TEXT_DIMENSION_Z = 0.02; + +const TEXT_MARGIN_TOP = 0.15; +const TEXT_MARGIN_LEFT = 0.15; +const TEXT_MARGIN_RIGHT = 0.17; +const TEXT_MARGIN_BOTTOM = 0.17; + +var windowDimensions = Controller.getViewportDimensions(); +var cursor = new Cursor(); +var keyboard = new Keyboard(); +var textFontSize = 9; +var text = null; +var textText = ""; +var textSizeMeasureOverlay = Overlays.addOverlay("text3d", {visible: false}); + +function appendChar(char) { + textText += char; + updateTextOverlay(); + Overlays.editOverlay(text, {text: textText}); +} + +function deleteChar() { + if (textText.length > 0) { + textText = textText.substring(0, textText.length - 1); + updateTextOverlay(); + } +} + +function updateTextOverlay() { + var textLines = textText.split("\n"); + var suggestedFontSize = (windowDimensions.x / Overlays.textSize(text, textText).width) * textFontSize * 0.90; + var maxFontSize = 170 / textLines.length; + textFontSize = (suggestedFontSize > maxFontSize) ? maxFontSize : suggestedFontSize; + var topMargin = (250 - (textFontSize * textLines.length)) / 4; + Overlays.editOverlay(text, {text: textText, font: {size: textFontSize}, topMargin: topMargin}); + Overlays.editOverlay(text, {leftMargin: (windowDimensions.x - Overlays.textSize(text, textLines).width) / 2}); +} + +keyboard.onKeyPress = function(event) { + if (event.event == 'keypress') { + appendChar(event.char); + } else if (event.event == 'enter') { + appendChar("\n"); + } +}; + +keyboard.onKeyRelease = function(event) { + print("Key release event test"); + // you can cancel a key by releasing its focusing before releasing it + if (event.focus) { + if (event.event == 'delete') { + deleteChar(); + } else if (event.event == 'submit') { + print(textText); + + var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE)); + + var textLines = textText.split("\n"); + var maxLineWidth = Overlays.textSize(textSizeMeasureOverlay, textText).width; + var usernameLine = "--" + GlobalServices.myUsername; + var usernameWidth = Overlays.textSize(textSizeMeasureOverlay, usernameLine).width; + if (maxLineWidth < usernameWidth) { + maxLineWidth = usernameWidth; + } else { + var spaceableWidth = maxLineWidth - usernameWidth; + //TODO: WORKAROUND WARNING BELOW Fix this when spaces are not trimmed out of the textsize calculation anymore + var spaceWidth = Overlays.textSize(textSizeMeasureOverlay, "| |").width + - Overlays.textSize(textSizeMeasureOverlay, "||").width; + var numberOfSpaces = Math.floor(spaceableWidth / spaceWidth); + for (var i = 0; i < numberOfSpaces; i++) { + usernameLine = " " + usernameLine; + } + } + var dimension_x = maxLineWidth + TEXT_MARGIN_RIGHT + TEXT_MARGIN_LEFT; + if (position.x > 0 && position.y > 0 && position.z > 0) { + Entities.addEntity({ + type: "Text", + rotation: MyAvatar.orientation, + position: position, + dimensions: { x: dimension_x, y: (textLines.length + 1) * 0.14 + TEXT_MARGIN_TOP + TEXT_MARGIN_BOTTOM, z: DEFAULT_TEXT_DIMENSION_Z }, + backgroundColor: { red: 0, green: 0, blue: 0 }, + textColor: { red: 255, green: 255, blue: 255 }, + text: textText + "\n" + usernameLine, + lineHeight: 0.1 + }); + } + textText = ""; + updateTextOverlay(); + } + } +}; + +keyboard.onFullyLoaded = function() { + print("Virtual-keyboard fully loaded."); + var dimensions = Controller.getViewportDimensions(); + text = Overlays.addOverlay("text", { + x: 0, + y: dimensions.y - keyboard.height() - 260, + width: dimensions.x, + height: 250, + backgroundColor: { red: 255, green: 255, blue: 255}, + color: { red: 0, green: 0, blue: 0}, + topMargin: 5, + leftMargin: 0, + font: {size: textFontSize}, + text: "", + alpha: 0.8 + }); + updateTextOverlay(); + // the cursor is being loaded after the keyboard, else it will be on the background of the keyboard + cursor.initialize(); + cursor.onUpdate = function(position) { + keyboard.setFocusPosition(position.x, position.y); + }; +}; + +function keyPressEvent(event) { + if (event.key === SPACEBAR_CHARCODE) { + keyboard.pressFocussedKey(); + } +} + +function keyReleaseEvent(event) { + if (event.key === SPACEBAR_CHARCODE) { + keyboard.releaseKeys(); + } +} + +function scriptEnding() { + keyboard.remove(); + cursor.remove(); + Overlays.deleteOverlay(text); + Overlays.deleteOverlay(textSizeMeasureOverlay); + Controller.releaseKeyEvents({key: SPACEBAR_CHARCODE}); +} + +function reportButtonValue(button, newValue, oldValue) { + if (button == Joysticks.BUTTON_FACE_BOTTOM) { + if (newValue) { + keyboard.pressFocussedKey(); + } else { + keyboard.releaseKeys(); + } + } else if (button == Joysticks.BUTTON_FACE_RIGHT && newValue) { + deleteChar(); + } +} + +function addJoystick(gamepad) { + gamepad.buttonStateChanged.connect(reportButtonValue); +} + +var allJoysticks = Joysticks.getAllJoysticks(); +for (var i = 0; i < allJoysticks.length; i++) { + addJoystick(allJoysticks[i]); +} + +Joysticks.joystickAdded.connect(addJoystick); +Controller.captureKeyEvents({key: SPACEBAR_CHARCODE}); +Controller.keyPressEvent.connect(keyPressEvent); +Controller.keyReleaseEvent.connect(keyReleaseEvent); +Script.scriptEnding.connect(scriptEnding); diff --git a/examples/controllers/oculus/virtualKeyboard.js b/examples/libraries/virtualKeyboard.js similarity index 61% rename from examples/controllers/oculus/virtualKeyboard.js rename to examples/libraries/virtualKeyboard.js index d17b36ae4f..a1f952a5eb 100644 --- a/examples/controllers/oculus/virtualKeyboard.js +++ b/examples/libraries/virtualKeyboard.js @@ -9,179 +9,50 @@ // Usage: Enable VR-mode and go to First person mode, // look at the key that you would like to press, and press the spacebar on your "REAL" keyboard. // -// leased some code from newEditEntities.js for Text Entity example -// // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; +// experimental 3dmode +THREE_D_MODE = false; -const KBD_UPPERCASE_DEFAULT = 0; -const KBD_LOWERCASE_DEFAULT = 1; -const KBD_UPPERCASE_HOVER = 2; -const KBD_LOWERCASE_HOVER = 3; -const KBD_BACKGROUND = 4; +KBD_UPPERCASE_DEFAULT = 0; +KBD_LOWERCASE_DEFAULT = 1; +KBD_UPPERCASE_HOVER = 2; +KBD_LOWERCASE_HOVER = 3; +KBD_BACKGROUND = 4; -const KEYBOARD_URL = HIFI_PUBLIC_BUCKET + "images/keyboard.svg"; -const CURSOR_URL = HIFI_PUBLIC_BUCKET + "images/cursor.svg"; +KEYBOARD_URL = HIFI_PUBLIC_BUCKET + "images/keyboard.svg"; +CURSOR_URL = HIFI_PUBLIC_BUCKET + "images/cursor.svg"; -const SPACEBAR_CHARCODE = 32; +RETURN_CHARCODE = 0x01000004; +ENTER_CHARCODE = 0x01000005; +SPACEBAR_CHARCODE = 32; -const KEYBOARD_WIDTH = 1174.7; -const KEYBOARD_HEIGHT = 434.1; +KEYBOARD_WIDTH = 1174.7; +KEYBOARD_HEIGHT = 434.1; -const CURSOR_WIDTH = 33.9; -const CURSOR_HEIGHT = 33.9; +CURSOR_WIDTH = 33.9; +CURSOR_HEIGHT = 33.9; + +KEYBOARD_SCALE_MULTIPLIER = 0.50; // VIEW_ANGLE can be adjusted to your likings, the smaller the faster movement. // Try setting it to 60 if it goes too fast for you. const VIEW_ANGLE = 40.0; const VIEW_ANGLE_BY_TWO = VIEW_ANGLE / 2; -const SPAWN_DISTANCE = 1; -const DEFAULT_TEXT_DIMENSION_Z = 0.02; - const BOUND_X = 0; const BOUND_Y = 1; const BOUND_W = 2; const BOUND_H = 3; -const KEY_STATE_LOWER = 0; -const KEY_STATE_UPPER = 1; - -const TEXT_MARGIN_TOP = 0.15; -const TEXT_MARGIN_LEFT = 0.15; -const TEXT_MARGIN_RIGHT = 0.17; -const TEXT_MARGIN_BOTTOM = 0.17; +KEY_STATE_LOWER = 0; +KEY_STATE_UPPER = 1; var windowDimensions = Controller.getViewportDimensions(); -var cursor = null; -var keyboard = new Keyboard(); -var textFontSize = 9; -var text = null; -var textText = ""; -var textSizeMeasureOverlay = Overlays.addOverlay("text3d", {visible: false}); -function appendChar(char) { - textText += char; - updateTextOverlay(); - Overlays.editOverlay(text, {text: textText}); -} - -function deleteChar() { - if (textText.length > 0) { - textText = textText.substring(0, textText.length - 1); - updateTextOverlay(); - } -} - -function updateTextOverlay() { - var textLines = textText.split("\n"); - var maxLineWidth = 0; - for (textLine in textLines) { - var lineWidth = Overlays.textSize(text, textLines[textLine]).width; - if (lineWidth > maxLineWidth) { - maxLineWidth = lineWidth; - } - } - var suggestedFontSize = (windowDimensions.x / maxLineWidth) * textFontSize * 0.90; - var maxFontSize = 190 / textLines.length; - textFontSize = (suggestedFontSize > maxFontSize) ? maxFontSize : suggestedFontSize; - var topMargin = (250 - (textFontSize * textLines.length)) / 4; - Overlays.editOverlay(text, {text: textText, font: {size: textFontSize}, topMargin: topMargin}); - var maxLineWidth = 0; - for (textLine in textLines) { - var lineWidth = Overlays.textSize(text, textLines[textLine]).width; - if (lineWidth > maxLineWidth) { - maxLineWidth = lineWidth; - } - } - Overlays.editOverlay(text, {leftMargin: (windowDimensions.x - maxLineWidth) / 2}); -} - -keyboard.onKeyPress = function(event) { - if (event.event == 'keypress') { - appendChar(event.char); - } else if (event.event == 'enter') { - appendChar("\n"); - } -}; - -keyboard.onKeyRelease = function(event) { - print("Key release event test"); - // you can cancel a key by releasing its focusing before releasing it - if (event.focus) { - if (event.event == 'delete') { - deleteChar(); - } else if (event.event == 'submit') { - print(textText); - - var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE)); - - var textLines = textText.split("\n"); - var maxLineWidth = 0; - for (textLine in textLines) { - var lineWidth = Overlays.textSize(textSizeMeasureOverlay, textLines[textLine]).width; - if (lineWidth > maxLineWidth) { - maxLineWidth = lineWidth; - } - } - var usernameLine = "--" + GlobalServices.myUsername; - var usernameWidth = Overlays.textSize(textSizeMeasureOverlay, usernameLine).width; - if (maxLineWidth < usernameWidth) { - maxLineWidth = usernameWidth; - } else { - var spaceableWidth = maxLineWidth - usernameWidth; - var spaceWidth = Overlays.textSize(textSizeMeasureOverlay, " ").width; - var numberOfSpaces = Math.floor(spaceableWidth / spaceWidth); - for (var i = 0; i < numberOfSpaces; i++) { - usernameLine = " " + usernameLine; - } - } - var dimension_x = maxLineWidth + TEXT_MARGIN_RIGHT + TEXT_MARGIN_LEFT; - if (position.x > 0 && position.y > 0 && position.z > 0) { - Entities.addEntity({ - type: "Text", - rotation: MyAvatar.orientation, - position: position, - dimensions: { x: dimension_x, y: (textLines.length + 1) * 0.14 + TEXT_MARGIN_TOP + TEXT_MARGIN_BOTTOM, z: DEFAULT_TEXT_DIMENSION_Z }, - backgroundColor: { red: 0, green: 0, blue: 0 }, - textColor: { red: 255, green: 255, blue: 255 }, - text: textText + "\n" + usernameLine - }); - } - textText = ""; - updateTextOverlay(); - } - } -}; - -keyboard.onFullyLoaded = function() { - print("Virtual-keyboard fully loaded."); - var dimensions = Controller.getViewportDimensions(); - text = Overlays.addOverlay("text", { - x: 0, - y: dimensions.y - keyboard.height() - 260, - width: dimensions.x, - height: 250, - backgroundColor: { red: 255, green: 255, blue: 255}, - color: { red: 0, green: 0, blue: 0}, - topMargin: 5, - leftMargin: 0, - font: {size: textFontSize}, - text: "", - alpha: 0.8 - }); - updateTextOverlay(); - // the cursor is being loaded after the keyboard, else it will be on the background of the keyboard - cursor = new Cursor(); - cursor.onUpdate = function(position) { - keyboard.setFocusPosition(position.x, position.y); - }; -}; - -function KeyboardKey(keyboard, keyProperties) { +KeyboardKey = (function(keyboard, keyProperties) { var tthis = this; this._focus = false; this._beingPressed = false; @@ -247,6 +118,11 @@ function KeyboardKey(keyboard, keyProperties) { }); } }; + this.updateVisibility = function() { + for (var i = 0; i < tthis.bounds.length; i++) { + Overlays.editOverlay(tthis.overlays[i], {visible: tthis.keyboard.visible}); + } + }; this.rescale = function() { for (var i = 0; i < tthis.bounds.length; i++) { Overlays.editOverlay(tthis.overlays[i], { @@ -271,24 +147,48 @@ function KeyboardKey(keyboard, keyProperties) { return true; }; for (var i = 0; i < this.bounds.length; i++) { - var newOverlay = Overlays.cloneOverlay(this.keyboard.background); - Overlays.editOverlay(newOverlay, { - x: this.keyboard.getX() + this.bounds[i][BOUND_X] * keyboard.scale, - y: this.keyboard.getY() + this.bounds[i][BOUND_Y] * keyboard.scale, - width: this.bounds[i][BOUND_W] * keyboard.scale, - height: this.bounds[i][BOUND_H] * keyboard.scale, - subImage: {width: this.bounds[i][BOUND_W], height: this.bounds[i][BOUND_H], x: this.bounds[i][BOUND_X], y: (KEYBOARD_HEIGHT * this.keyState) + this.bounds[i][BOUND_Y]}, - alpha: 1 - }); - this.overlays.push(newOverlay); + if (THREE_D_MODE) { + this.overlays.push(Overlays.addOverlay("billboard", { + scale: 1, + rotation: MyAvatar.rotation, + isFacingAvatar: false, + url: KEYBOARD_URL, + alpha: 1, + position: { + x: MyAvatar.position.x,// + this.bounds[i][BOUND_X] * 0.01,// /*+ this.keyboard.getX()*/ + this.bounds[i][BOUND_X] * keyboard.scale, + y: MyAvatar.position.y,// - this.bounds[i][BOUND_Y] * 0.01,// /*+ this.keyboard.getY()*/ + this.bounds[i][BOUND_Y] * keyboard.scale, + z: MyAvatar.position.z + }, + width: this.bounds[i][BOUND_W] * keyboard.scale, + height: this.bounds[i][BOUND_H] * keyboard.scale, + subImage: {width: this.bounds[i][BOUND_W], height: this.bounds[i][BOUND_H], x: this.bounds[i][BOUND_X], y: (KEYBOARD_HEIGHT * this.keyState) + this.bounds[i][BOUND_Y]}, + alpha: 1, + visible: tthis.keyboard.visible + })); + } else { + this.overlays.push(Overlays.addOverlay("image", { + imageURL: KEYBOARD_URL, + x: this.keyboard.getX() + this.bounds[i][BOUND_X] * keyboard.scale, + y: this.keyboard.getY() + this.bounds[i][BOUND_Y] * keyboard.scale, + width: this.bounds[i][BOUND_W] * keyboard.scale, + height: this.bounds[i][BOUND_H] * keyboard.scale, + subImage: {width: this.bounds[i][BOUND_W], height: this.bounds[i][BOUND_H], x: this.bounds[i][BOUND_X], y: (KEYBOARD_HEIGHT * this.keyState) + this.bounds[i][BOUND_Y]}, + alpha: 1, + visible: tthis.keyboard.visible + })); + } } -} +}); -function Keyboard() { +Keyboard = (function(params) { + if (params === undefined) { + params = {}; + } var tthis = this; this.focussed_key = -1; - this.scale = windowDimensions.x / KEYBOARD_WIDTH; + this.scale = (windowDimensions.x / KEYBOARD_WIDTH) * KEYBOARD_SCALE_MULTIPLIER; this.shift = false; + this.visible = params.visible != undefined ? params.visible : true; this.width = function() { return KEYBOARD_WIDTH * tthis.scale; }; @@ -301,17 +201,33 @@ function Keyboard() { this.getY = function() { return windowDimensions.y - this.height(); }; - this.background = Overlays.addOverlay("image", { - x: this.getX(), - y: this.getY(), - width: this.width(), - height: this.height(), - subImage: {width: KEYBOARD_WIDTH, height: KEYBOARD_HEIGHT, y: KEYBOARD_HEIGHT * KBD_BACKGROUND}, - imageURL: KEYBOARD_URL, - alpha: 1 - }); + if (THREE_D_MODE) { + this.background = Overlays.addOverlay("billboard", { + scale: 1, + position: MyAvatar.position, + rotation: MyAvatar.rotation, + width: this.width(), + height: this.height(), + subImage: {width: KEYBOARD_WIDTH, height: KEYBOARD_HEIGHT, y: KEYBOARD_HEIGHT * KBD_BACKGROUND}, + isFacingAvatar: false, + url: KEYBOARD_URL, + alpha: 1, + visible: this.visible + }); + } else { + this.background = Overlays.addOverlay("image", { + x: this.getX(), + y: this.getY(), + width: this.width(), + height: this.height(), + subImage: {width: KEYBOARD_WIDTH, height: KEYBOARD_HEIGHT, y: KEYBOARD_HEIGHT * KBD_BACKGROUND}, + imageURL: KEYBOARD_URL, + alpha: 1, + visible: this.visible + }); + } this.rescale = function() { - this.scale = windowDimensions.x / KEYBOARD_WIDTH; + this.scale = (windowDimensions.x / KEYBOARD_WIDTH) * KEYBOARD_SCALE_MULTIPLIER; Overlays.editOverlay(tthis.background, { x: this.getX(), y: this.getY(), @@ -349,6 +265,9 @@ function Keyboard() { }; this.pressFocussedKey = function() { + if (!tthis.visible) { + return tthis; + } if (tthis.focussed_key != -1) { if (tthis.keys[tthis.focussed_key].event == 'shift') { tthis.toggleShift(); @@ -402,6 +321,32 @@ function Keyboard() { for (var i = 0; i < this.keys.length; i++) { this.keys[i].remove(); } + // resets the cursor and magnifier + this.updateVisibility(false); + }; + + this.show = function() { + tthis.updateVisibility(true); + }; + + this.hide = function() { + tthis.updateVisibility(false); + }; + + this.toggle = function() { + tthis.updateVisibility(!tthis.visible); + }; + + this.updateVisibility = function(visible) { + tthis.visible = visible; + if (HMD.magnifier == visible) { + HMD.toggleMagnifier(); + } + Window.cursorVisible = !visible; + Overlays.editOverlay(tthis.background, { visible: tthis.visible }); + for (var i = 0; i < this.keys.length; i++) { + this.keys[i].updateVisibility(); + } }; this.onKeyPress = null; @@ -502,97 +447,94 @@ function Keyboard() { {bounds: [[899, 355, 263, 67]], event: 'submit'} ]; - + for (var i = 0; i < keyProperties.length; i++) { + this.keys.push(new KeyboardKey(this, keyProperties[i])); + } this.keyboardTextureLoaded = function() { if (Overlays.isLoaded(tthis.background)) { Script.clearInterval(tthis.keyboardTextureLoaded_timer); - for (var i = 0; i < keyProperties.length; i++) { - tthis.keys.push(new KeyboardKey(tthis, keyProperties[i])); - } if (keyboard.onFullyLoaded != null) { tthis.onFullyLoaded(); } } }; this.keyboardTextureLoaded_timer = Script.setInterval(this.keyboardTextureLoaded, 250); -} +}); -function Cursor() { +Cursor = (function(params) { + if (params === undefined) { + params = {}; + } var tthis = this; - this.x = windowDimensions.x / 2; - this.y = windowDimensions.y / 2; - this.overlay = Overlays.addOverlay("image", { - x: this.x, - y: this.y, - width: CURSOR_WIDTH, - height: CURSOR_HEIGHT, - imageURL: CURSOR_URL, - alpha: 1 - }); - this.remove = function() { - Overlays.deleteOverlay(this.overlay); + this.initialize = function() { + this.visible = params.visible != undefined ? params.visible : true; + this.x = windowDimensions.x / 2; + this.y = windowDimensions.y / 2; + this.overlay = Overlays.addOverlay("image", { + x: this.x, + y: this.y, + width: CURSOR_WIDTH, + height: CURSOR_HEIGHT, + imageURL: CURSOR_URL, + alpha: 1, + visible: this.visible + }); + this.remove = function() { + Overlays.deleteOverlay(this.overlay); + }; + this.getPosition = function() { + return {x: tthis.getX(), y: tthis.getY()}; + }; + this.getX = function() { + return tthis.x; + }; + this.getY = function() { + return tthis.y; + }; + this.show = function() { + tthis.updateVisibility(true); + }; + this.hide = function() { + tthis.updateVisibility(false); + }; + this.toggle = function() { + tthis.updateVisibility(!tthis.visible); + }; + this.updateVisibility = function(visible) { + tthis.visible = visible; + Overlays.editOverlay(this.overlay, { visible: tthis.visible }); + }; + this.onUpdate = null; + this.update = function() { + var newWindowDimensions = Controller.getViewportDimensions(); + if (newWindowDimensions.x != windowDimensions.x || newWindowDimensions.y != windowDimensions.y) { + windowDimensions = newWindowDimensions; + keyboard.rescale(); + Overlays.editOverlay(text, { + y: windowDimensions.y - keyboard.height() - 260, + width: windowDimensions.x + }); + } + var editobject = {}; + var hudLookatPosition = HMD.getHUDLookAtPosition2D(); + if (hudLookatPosition === null) { + return; + } + if (tthis.x !== hudLookatPosition.x) { + tthis.x = hudLookatPosition.x; + editobject.x = tthis.x - (CURSOR_WIDTH / 2); + } + if (tthis.y !== hudLookatPosition.y) { + tthis.y = hudLookatPosition.y; + editobject.y = tthis.y - (CURSOR_HEIGHT / 2); + } + if (Object.keys(editobject).length > 0) { + Overlays.editOverlay(tthis.overlay, editobject); + if (tthis.onUpdate != null) { + tthis.onUpdate(tthis.getPosition()); + } + } + }; + Script.update.connect(this.update); }; - this.getPosition = function() { - return {x: tthis.getX(), y: tthis.getY()}; - }; - this.getX = function() { - return tthis.x; - }; - this.getY = function() { - return tthis.y; - }; - this.onUpdate = null; - this.update = function() { - var newWindowDimensions = Controller.getViewportDimensions(); - if (newWindowDimensions.x != windowDimensions.x || newWindowDimensions.y != windowDimensions.y) { - windowDimensions = newWindowDimensions; - keyboard.rescale(); - Overlays.editOverlay(text, { - y: windowDimensions.y - keyboard.height() - 260, - width: windowDimensions.x - }); - } - var editobject = {}; - if (MyAvatar.getHeadFinalYaw() <= VIEW_ANGLE_BY_TWO && MyAvatar.getHeadFinalYaw() >= -1 * VIEW_ANGLE_BY_TWO) { - angle = ((-1 * MyAvatar.getHeadFinalYaw()) + VIEW_ANGLE_BY_TWO) / VIEW_ANGLE; - tthis.x = angle * windowDimensions.x; - editobject.x = tthis.x - (CURSOR_WIDTH / 2); - } - if (MyAvatar.getHeadFinalPitch() <= VIEW_ANGLE_BY_TWO && MyAvatar.getHeadFinalPitch() >= -1 * VIEW_ANGLE_BY_TWO) { - angle = ((-1 * MyAvatar.getHeadFinalPitch()) + VIEW_ANGLE_BY_TWO) / VIEW_ANGLE; - tthis.y = angle * windowDimensions.y; - editobject.y = tthis.y - (CURSOR_HEIGHT / 2); - } - if (Object.keys(editobject).length > 0) { - Overlays.editOverlay(tthis.overlay, editobject); - if (tthis.onUpdate != null) { - tthis.onUpdate(tthis.getPosition()); - } - } - }; - Script.update.connect(this.update); -} - -function keyPressEvent(event) { - if (event.key === SPACEBAR_CHARCODE) { - keyboard.pressFocussedKey(); - } -} - -function keyReleaseEvent(event) { - if (event.key === SPACEBAR_CHARCODE) { - keyboard.releaseKeys(); - } -} - -function scriptEnding() { - keyboard.remove(); - cursor.remove(); - Overlays.deleteOverlay(text); - Overlays.deleteOverlay(textSizeMeasureOverlay); - Controller.releaseKeyEvents({key: SPACEBAR_CHARCODE}); -} -Controller.captureKeyEvents({key: SPACEBAR_CHARCODE}); -Controller.keyPressEvent.connect(keyPressEvent); -Controller.keyReleaseEvent.connect(keyReleaseEvent); -Script.scriptEnding.connect(scriptEnding); \ No newline at end of file +}); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d1558c3799..ce33b04777 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -111,6 +111,7 @@ #include "scripting/AccountScriptingInterface.h" #include "scripting/AudioDeviceScriptingInterface.h" #include "scripting/ClipboardScriptingInterface.h" +#include "scripting/HMDScriptingInterface.h" #include "scripting/JoystickScriptingInterface.h" #include "scripting/GlobalServicesScriptingInterface.h" #include "scripting/LocationScriptingInterface.h" @@ -3492,6 +3493,10 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("UndoStack", &_undoStackScriptingInterface); + QScriptValue hmdInterface = scriptEngine->registerGlobalObject("HMD", &HMDScriptingInterface::getInstance()); + scriptEngine->registerFunction(hmdInterface, "getHUDLookAtPosition2D", HMDScriptingInterface::getHUDLookAtPosition2D, 0); + scriptEngine->registerFunction(hmdInterface, "getHUDLookAtPosition3D", HMDScriptingInterface::getHUDLookAtPosition3D, 0); + #ifdef HAVE_RTMIDI scriptEngine->registerGlobalObject("MIDI", &MIDIManager::getInstance()); #endif diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 9b1470ad67..14d1939198 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -688,7 +688,7 @@ void OculusManager::getEulerAngles(float& yaw, float& pitch, float& roll) { roll = 0.0f; #endif } - + glm::vec3 OculusManager::getRelativePosition() { #if (defined(__APPLE__) || defined(_WIN32)) && HAVE_LIBOVR ovrTrackingState trackingState = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds()); diff --git a/interface/src/scripting/HMDScriptingInterface.cpp b/interface/src/scripting/HMDScriptingInterface.cpp new file mode 100644 index 0000000000..fe274b6878 --- /dev/null +++ b/interface/src/scripting/HMDScriptingInterface.cpp @@ -0,0 +1,56 @@ +// +// HMDScriptingInterface.cpp +// interface/src/scripting +// +// Created by Thijs Wenker on 1/12/15. +// Copyright 2015 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 +// + +#include "HMDScriptingInterface.h" + +#include + +HMDScriptingInterface& HMDScriptingInterface::getInstance() { + static HMDScriptingInterface sharedInstance; + return sharedInstance; +} + +bool HMDScriptingInterface::getHUDLookAtPosition3D(glm::vec3& result) const { + Camera* camera = Application::getInstance()->getCamera(); + glm::vec3 position = camera->getPosition(); + glm::quat orientation = camera->getOrientation(); + + glm::vec3 direction = orientation * glm::vec3(0.0f, 0.0f, -1.0f); + + ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay(); + + return applicationOverlay.calculateRayUICollisionPoint(position, direction, result); +} + +QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) { + + glm::vec3 hudIntersection; + + if ((&HMDScriptingInterface::getInstance())->getHUDLookAtPosition3D(hudIntersection)) { + MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); + glm::vec3 sphereCenter = myAvatar->getDefaultEyePosition(); + glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * (hudIntersection - sphereCenter); + glm::quat rotation = ::rotationBetween(glm::vec3(0.0f, 0.0f, -1.0f), direction); + glm::vec3 eulers = ::safeEulerAngles(rotation); + return qScriptValueFromValue(engine, Application::getInstance()->getApplicationOverlay() + .sphericalToOverlay(glm::vec2(eulers.y, -eulers.x))); + } + return QScriptValue::NullValue; +} + +QScriptValue HMDScriptingInterface::getHUDLookAtPosition3D(QScriptContext* context, QScriptEngine* engine) { + glm::vec3 result; + HMDScriptingInterface* hmdInterface = &HMDScriptingInterface::getInstance(); + if ((&HMDScriptingInterface::getInstance())->getHUDLookAtPosition3D(result)) { + return qScriptValueFromValue(engine, result); + } + return QScriptValue::NullValue; +} diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h new file mode 100644 index 0000000000..bcd458ca5e --- /dev/null +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -0,0 +1,42 @@ +// +// HMDScriptingInterface.h +// interface/src/scripting +// +// Created by Thijs Wenker on 1/12/15. +// Copyright 2015 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 +// + +#ifndef hifi_HMDScriptingInterface_h +#define hifi_HMDScriptingInterface_h + +#include + +#include "Application.h" +#include "devices/OculusManager.h" + +class HMDScriptingInterface : public QObject { + Q_OBJECT + Q_PROPERTY(bool magnifier READ getMagnifier) + Q_PROPERTY(bool active READ isHMDMode) +public: + static HMDScriptingInterface& getInstance(); + + static QScriptValue getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine); + static QScriptValue getHUDLookAtPosition3D(QScriptContext* context, QScriptEngine* engine); + +public slots: + void toggleMagnifier() { Application::getInstance()->getApplicationOverlay().toggleMagnifier(); }; + +private: + HMDScriptingInterface() {}; + bool getMagnifier() const { return Application::getInstance()->getApplicationOverlay().hasMagnifier(); }; + bool isHMDMode() const { return Application::getInstance()->isHMDMode(); } + + bool getHUDLookAtPosition3D(glm::vec3& result) const; + +}; + +#endif // hifi_HMDScriptingInterface_h diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index f9437221e5..8e94631789 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -139,6 +139,7 @@ ApplicationOverlay::ApplicationOverlay() : _alpha(1.0f), _oculusUIRadius(1.0f), _crosshairTexture(0), + _magnifier(true), _previousBorderWidth(-1), _previousBorderHeight(-1), _previousMagnifierBottomLeft(), @@ -559,7 +560,7 @@ void ApplicationOverlay::renderPointers() { _reticlePosition[MOUSE] = position; _reticleActive[MOUSE] = true; - _magActive[MOUSE] = true; + _magActive[MOUSE] = _magnifier; _reticleActive[LEFT_CONTROLLER] = false; _reticleActive[RIGHT_CONTROLLER] = false; } else if (qApp->getLastMouseMoveWasSimulated() && Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { @@ -718,6 +719,9 @@ void ApplicationOverlay::renderPointersOculus(const glm::vec3& eyePos) { //Renders a small magnification of the currently bound texture at the coordinates void ApplicationOverlay::renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder) { + if (!_magnifier) { + return; + } auto glCanvas = DependencyManager::get(); const int widgetWidth = glCanvas->width(); diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 68edbda691..64161e4a31 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -37,6 +37,9 @@ public: QPoint getPalmClickLocation(const PalmData *palm) const; bool calculateRayUICollisionPoint(const glm::vec3& position, const glm::vec3& direction, glm::vec3& result) const; + bool hasMagnifier() const { return _magnifier; } + void toggleMagnifier() { _magnifier = !_magnifier; } + float getOculusUIAngularSize() const { return _oculusUIAngularSize; } void setOculusUIAngularSize(float oculusUIAngularSize) { _oculusUIAngularSize = oculusUIAngularSize; } @@ -109,7 +112,8 @@ private: bool _magActive[NUMBER_OF_RETICLES]; float _magSizeMult[NUMBER_OF_RETICLES]; quint64 _lastMouseMove; - + bool _magnifier; + float _alpha; float _oculusUIRadius; float _trailingAudioLoudness; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 062bb0ca06..0956374238 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -272,8 +272,12 @@ QScriptValue ScriptEngine::registerGlobalObject(const QString& name, QObject* ob } void ScriptEngine::registerFunction(const QString& name, QScriptEngine::FunctionSignature fun, int numArguments) { + registerFunction(globalObject(), name, fun, numArguments); +} + +void ScriptEngine::registerFunction(QScriptValue parent, const QString& name, QScriptEngine::FunctionSignature fun, int numArguments) { QScriptValue scriptFun = newFunction(fun, numArguments); - globalObject().setProperty(name, scriptFun); + parent.setProperty(name, scriptFun); } void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter, diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 6cc9f64d4c..f2911842e6 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -58,6 +58,8 @@ public: void registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter, QScriptEngine::FunctionSignature setter, QScriptValue object = QScriptValue::NullValue); void registerFunction(const QString& name, QScriptEngine::FunctionSignature fun, int numArguments = -1); + void registerFunction(QScriptValue parent, const QString& name, QScriptEngine::FunctionSignature fun, + int numArguments = -1); Q_INVOKABLE void setIsAvatar(bool isAvatar); bool isAvatar() const { return _isAvatar; }