diff --git a/interface/resources/controllers/oculus_touch.json b/interface/resources/controllers/oculus_touch.json index b59bf54e5b..3def439221 100644 --- a/interface/resources/controllers/oculus_touch.json +++ b/interface/resources/controllers/oculus_touch.json @@ -1,10 +1,10 @@ { "name": "Oculus Touch to Standard", "channels": [ - { "from": "OculusTouch.A", "to": "Standard.A" }, - { "from": "OculusTouch.B", "to": "Standard.B" }, - { "from": "OculusTouch.X", "to": "Standard.X" }, - { "from": "OculusTouch.Y", "to": "Standard.Y" }, + { "from": "OculusTouch.A", "to": "Standard.RightPrimaryThumb" }, + { "from": "OculusTouch.B", "to": "Standard.RightSecondaryThumb" }, + { "from": "OculusTouch.X", "to": "Standard.LeftPrimaryThumb" }, + { "from": "OculusTouch.Y", "to": "Standard.LeftSecondaryThumb" }, { "from": "OculusTouch.LY", "filters": "invert", "to": "Standard.LY" }, { "from": "OculusTouch.LX", "to": "Standard.LX" }, diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 2bf908ab57..711e64f938 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -23,5 +23,6 @@ Script.load("system/controllers/handControllerGrab.js"); Script.load("system/controllers/handControllerPointer.js"); Script.load("system/controllers/squeezeHands.js"); Script.load("system/controllers/grab.js"); +Script.load("system/controllers/teleport.js"); Script.load("system/dialTone.js"); Script.load("system/firstPersonHMD.js"); diff --git a/scripts/developer/utilities/tools/overlayFinder.js b/scripts/developer/utilities/tools/overlayFinder.js new file mode 100644 index 0000000000..75b8aeacfa --- /dev/null +++ b/scripts/developer/utilities/tools/overlayFinder.js @@ -0,0 +1,17 @@ +function mousePressEvent(event) { + var overlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); + // var pickRay = Camera.computePickRay(event.x, event.y); + // var intersection = Overlays.findRayIntersection(pickRay); + // print('intersection is: ' + intersection) +}; + +Controller.mousePressEvent.connect(function(event) { + mousePressEvent(event) +}); + +Script.scriptEnding.connect(function() { + Controller.mousePressEvent.disconnect(mousePressEvent); +}) \ No newline at end of file diff --git a/scripts/system/assets/models/teleport.fbx b/scripts/system/assets/models/teleport.fbx new file mode 100644 index 0000000000..831f152add Binary files /dev/null and b/scripts/system/assets/models/teleport.fbx differ diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 416e6b10d8..c8a7f251af 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2247,11 +2247,18 @@ var handleHandMessages = function (channel, message, sender) { if (channel === 'Hifi-Hand-Disabler') { if (message === 'left') { handToDisable = LEFT_HAND; + leftController.turnOffVisualizations(); } if (message === 'right') { handToDisable = RIGHT_HAND; + rightController.turnOffVisualizations(); } if (message === 'both' || message === 'none') { + if (message === 'both') { + rightController.turnOffVisualizations(); + leftController.turnOffVisualizations(); + + } handToDisable = message; } } else if (channel === 'Hifi-Hand-Grab') { @@ -2331,4 +2338,3 @@ function handleMenuItemEvent(menuItem) { } Menu.menuItemEvent.connect(handleMenuItemEvent); - diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js new file mode 100644 index 0000000000..da0b4cb576 --- /dev/null +++ b/scripts/system/controllers/teleport.js @@ -0,0 +1,619 @@ +// Created by james b. pollack @imgntn on 7/2/2016 +// Copyright 2016 High Fidelity, Inc. +// +// Creates a beam and target and then teleports you there when you let go of either activation button. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +var inTeleportMode = false; + +var currentFadeSphereOpacity = 1; +var fadeSphereInterval = null; +var fadeSphereUpdateInterval = null; +//milliseconds between fading one-tenth -- so this is a half second fade total +var USE_FADE_MODE = false; +var USE_FADE_OUT = true; +var FADE_OUT_INTERVAL = 25; + +// instant +// var NUMBER_OF_STEPS = 0; +// var SMOOTH_ARRIVAL_SPACING = 0; + +// // slow +// var SMOOTH_ARRIVAL_SPACING = 150; +// var NUMBER_OF_STEPS = 2; + +// medium-slow +// var SMOOTH_ARRIVAL_SPACING = 100; +// var NUMBER_OF_STEPS = 4; + +// medium-fast +var SMOOTH_ARRIVAL_SPACING = 33; +var NUMBER_OF_STEPS = 6; + +//fast +// var SMOOTH_ARRIVAL_SPACING = 10; +// var NUMBER_OF_STEPS = 20; + + +var TARGET_MODEL_URL = Script.resolvePath("../assets/models/teleport.fbx"); +var TARGET_MODEL_DIMENSIONS = { + x: 1.15, + y: 0.5, + z: 1.15 + +}; + +function ThumbPad(hand) { + this.hand = hand; + var _thisPad = this; + + this.buttonPress = function(value) { + _thisPad.buttonValue = value; + }; + +} + +function Trigger(hand) { + this.hand = hand; + var _this = this; + + this.buttonPress = function(value) { + _this.buttonValue = value; + + }; + + this.down = function() { + var down = _this.buttonValue === 1 ? 1.0 : 0.0; + return down + }; +} + +function Teleporter() { + var _this = this; + this.intersection = null; + this.rightOverlayLine = null; + this.leftOverlayLine = null; + this.targetOverlay = null; + this.updateConnected = null; + this.smoothArrivalInterval = null; + this.fadeSphere = null; + this.teleportHand = null; + + this.initialize = function() { + this.createMappings(); + this.disableGrab(); + }; + + this.createTargetOverlay = function() { + + var targetOverlayProps = { + url: TARGET_MODEL_URL, + dimensions: TARGET_MODEL_DIMENSIONS, + visible: true, + }; + + _this.targetOverlay = Overlays.addOverlay("model", targetOverlayProps); + }; + + this.createMappings = function() { + teleporter.telporterMappingInternalName = 'Hifi-Teleporter-Internal-Dev-' + Math.random(); + teleporter.teleportMappingInternal = Controller.newMapping(teleporter.telporterMappingInternalName); + + Controller.enableMapping(teleporter.telporterMappingInternalName); + }; + + this.disableMappings = function() { + Controller.disableMapping(teleporter.telporterMappingInternalName); + }; + + this.enterTeleportMode = function(hand) { + if (inTeleportMode === true) { + return; + } + inTeleportMode = true; + if (this.smoothArrivalInterval !== null) { + Script.clearInterval(this.smoothArrivalInterval); + } + if (fadeSphereInterval !== null) { + Script.clearInterval(fadeSphereInterval); + } + this.teleportHand = hand; + this.initialize(); + Script.update.connect(this.update); + this.updateConnected = true; + }; + + this.createFadeSphere = function(avatarHead) { + var sphereProps = { + position: avatarHead, + size: -1, + color: { + red: 0, + green: 0, + blue: 0, + }, + alpha: 1, + solid: true, + visible: true, + ignoreRayIntersection: true, + drawInFront: false + }; + + currentFadeSphereOpacity = 10; + + _this.fadeSphere = Overlays.addOverlay("sphere", sphereProps); + Script.clearInterval(fadeSphereInterval) + Script.update.connect(_this.updateFadeSphere); + if (USE_FADE_OUT === true) { + this.fadeSphereOut(); + } + + + }; + + this.fadeSphereOut = function() { + + fadeSphereInterval = Script.setInterval(function() { + if (currentFadeSphereOpacity <= 0) { + Script.clearInterval(fadeSphereInterval); + _this.deleteFadeSphere(); + fadeSphereInterval = null; + return; + } + if (currentFadeSphereOpacity > 0) { + currentFadeSphereOpacity = currentFadeSphereOpacity - 1; + } + + Overlays.editOverlay(_this.fadeSphere, { + alpha: currentFadeSphereOpacity / 10 + }) + + }, FADE_OUT_INTERVAL); + }; + + + this.updateFadeSphere = function() { + var headPosition = MyAvatar.getHeadPosition(); + Overlays.editOverlay(_this.fadeSphere, { + position: headPosition + }) + }; + + this.deleteFadeSphere = function() { + if (_this.fadeSphere !== null) { + Script.update.disconnect(_this.updateFadeSphere); + Overlays.deleteOverlay(_this.fadeSphere); + _this.fadeSphere = null; + } + + }; + + this.deleteTargetOverlay = function() { + Overlays.deleteOverlay(this.targetOverlay); + this.intersection = null; + this.targetOverlay = null; + } + + this.turnOffOverlayBeams = function() { + this.rightOverlayOff(); + this.leftOverlayOff(); + } + + this.exitTeleportMode = function(value) { + if (this.updateConnected === true) { + Script.update.disconnect(this.update); + } + this.disableMappings(); + this.turnOffOverlayBeams(); + + + this.updateConnected = null; + + Script.setTimeout(function() { + inTeleportMode = false; + _this.enableGrab(); + }, 100); + }; + + + + this.update = function() { + + if (teleporter.teleportHand === 'left') { + teleporter.leftRay(); + + if ((leftPad.buttonValue === 0 || leftTrigger.buttonValue === 0) && inTeleportMode === true) { + _this.teleport(); + return; + } + + } else { + teleporter.rightRay(); + + if ((rightPad.buttonValue === 0 || rightTrigger.buttonValue === 0) && inTeleportMode === true) { + _this.teleport(); + return; + } + } + + }; + + this.rightRay = function() { + + + var rightPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getPoseValue(Controller.Standard.RightHand).translation), MyAvatar.position); + + var rightControllerRotation = Controller.getPoseValue(Controller.Standard.RightHand).rotation; + + var rightRotation = Quat.multiply(MyAvatar.orientation, rightControllerRotation) + + + var rightFinal = Quat.multiply(rightRotation, Quat.angleAxis(90, { + x: 1, + y: 0, + z: 0 + })); + + var rightPickRay = { + origin: rightPosition, + direction: Quat.getUp(rightRotation), + }; + + this.rightPickRay = rightPickRay; + + var location = Vec3.sum(rightPickRay.origin, Vec3.multiply(rightPickRay.direction, 500)); + + + var rightIntersection = Entities.findRayIntersection(teleporter.rightPickRay, true, [], [this.targetEntity]); + + if (rightIntersection.intersects) { + this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, { + red: 7, + green: 36, + blue: 44 + }); + if (this.targetOverlay !== null) { + this.updateTargetOverlay(rightIntersection); + } else { + this.createTargetOverlay(); + } + + } else { + + this.deleteTargetOverlay(); + this.rightLineOn(rightPickRay.origin, location, { + red: 7, + green: 36, + blue: 44 + }); + } + } + + + this.leftRay = function() { + var leftPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getPoseValue(Controller.Standard.LeftHand).translation), MyAvatar.position); + + var leftRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(Controller.Standard.LeftHand).rotation) + + + var leftFinal = Quat.multiply(leftRotation, Quat.angleAxis(90, { + x: 1, + y: 0, + z: 0 + })); + + + var leftPickRay = { + origin: leftPosition, + direction: Quat.getUp(leftRotation), + }; + + this.leftPickRay = leftPickRay; + + var location = Vec3.sum(MyAvatar.position, Vec3.multiply(leftPickRay.direction, 500)); + + + var leftIntersection = Entities.findRayIntersection(teleporter.leftPickRay, true, [], [this.targetEntity]); + + if (leftIntersection.intersects) { + + this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, { + red: 7, + green: 36, + blue: 44 + }); + if (this.targetOverlay !== null) { + this.updateTargetOverlay(leftIntersection); + } else { + this.createTargetOverlay(); + } + + + } else { + + + this.deleteTargetOverlay(); + this.leftLineOn(leftPickRay.origin, location, { + red: 7, + green: 36, + blue: 44 + }); + } + }; + + this.rightLineOn = function(closePoint, farPoint, color) { + if (this.rightOverlayLine === null) { + var lineProperties = { + start: closePoint, + end: farPoint, + color: color, + ignoreRayIntersection: true, // always ignore this + visible: true, + alpha: 1, + solid: true, + drawInFront: true, + glow: 1.0 + }; + + this.rightOverlayLine = Overlays.addOverlay("line3d", lineProperties); + + } else { + var success = Overlays.editOverlay(this.rightOverlayLine, { + lineWidth: 50, + start: closePoint, + end: farPoint, + color: color, + visible: true, + ignoreRayIntersection: true, // always ignore this + alpha: 1, + glow: 1.0 + }); + } + }; + + this.leftLineOn = function(closePoint, farPoint, color) { + if (this.leftOverlayLine === null) { + var lineProperties = { + ignoreRayIntersection: true, // always ignore this + start: closePoint, + end: farPoint, + color: color, + visible: true, + alpha: 1, + solid: true, + glow: 1.0, + drawInFront: true + }; + + this.leftOverlayLine = Overlays.addOverlay("line3d", lineProperties); + + } else { + var success = Overlays.editOverlay(this.leftOverlayLine, { + start: closePoint, + end: farPoint, + color: color, + visible: true, + alpha: 1, + solid: true, + glow: 1.0 + }); + } + }; + this.rightOverlayOff = function() { + if (this.rightOverlayLine !== null) { + Overlays.deleteOverlay(this.rightOverlayLine); + this.rightOverlayLine = null; + } + }; + + this.leftOverlayOff = function() { + if (this.leftOverlayLine !== null) { + Overlays.deleteOverlay(this.leftOverlayLine); + this.leftOverlayLine = null; + } + }; + + this.updateTargetOverlay = function(intersection) { + _this.intersection = intersection; + + var rotation = Quat.lookAt(intersection.intersection, MyAvatar.position, Vec3.UP) + var euler = Quat.safeEulerAngles(rotation) + var position = { + x: intersection.intersection.x, + y: intersection.intersection.y + TARGET_MODEL_DIMENSIONS.y / 2, + z: intersection.intersection.z + } + Overlays.editOverlay(this.targetOverlay, { + position: position, + rotation: Quat.fromPitchYawRollDegrees(0, euler.y, 0), + }); + + }; + + this.disableGrab = function() { + Messages.sendLocalMessage('Hifi-Hand-Disabler', this.teleportHand); + }; + + this.enableGrab = function() { + Messages.sendLocalMessage('Hifi-Hand-Disabler', 'none'); + }; + + this.triggerHaptics = function() { + var hand = this.teleportHand === 'left' ? 0 : 1; + var haptic = Controller.triggerShortHapticPulse(0.2, hand); + }; + + this.teleport = function(value) { + if (value === undefined) { + this.exitTeleportMode(); + } + if (this.intersection !== null) { + if (USE_FADE_MODE === true) { + this.createFadeSphere(); + } + var offset = getAvatarFootOffset(); + this.intersection.intersection.y += offset; + this.exitTeleportMode(); + this.smoothArrival(); + + } + + }; + + + this.findMidpoint = function(start, end) { + var xy = Vec3.sum(start, end); + var midpoint = Vec3.multiply(0.5, xy); + return midpoint + }; + + + + this.getArrivalPoints = function(startPoint, endPoint) { + var arrivalPoints = []; + + + var i; + var lastPoint; + + for (i = 0; i < NUMBER_OF_STEPS; i++) { + if (i === 0) { + lastPoint = startPoint; + } + var newPoint = _this.findMidpoint(lastPoint, endPoint); + lastPoint = newPoint; + arrivalPoints.push(newPoint); + } + + arrivalPoints.push(endPoint) + + return arrivalPoints + }; + + this.smoothArrival = function() { + + _this.arrivalPoints = _this.getArrivalPoints(MyAvatar.position, _this.intersection.intersection); + _this.smoothArrivalInterval = Script.setInterval(function() { + if (_this.arrivalPoints.length === 0) { + Script.clearInterval(_this.smoothArrivalInterval); + return; + } + + var landingPoint = _this.arrivalPoints.shift(); + MyAvatar.position = landingPoint; + + if (_this.arrivalPoints.length === 1 || _this.arrivalPoints.length === 0) { + _this.deleteTargetOverlay(); + } + + + }, SMOOTH_ARRIVAL_SPACING) + } +} + + +//related to repositioning the avatar after you teleport +function getAvatarFootOffset() { + var data = getJointData(); + var upperLeg, lowerLeg, foot, toe, toeTop; + data.forEach(function(d) { + + var jointName = d.joint; + if (jointName === "RightUpLeg") { + upperLeg = d.translation.y; + } + if (jointName === "RightLeg") { + lowerLeg = d.translation.y; + } + if (jointName === "RightFoot") { + foot = d.translation.y; + } + if (jointName === "RightToeBase") { + toe = d.translation.y; + } + if (jointName === "RightToe_End") { + toeTop = d.translation.y + } + }) + + var myPosition = MyAvatar.position; + var offset = upperLeg + lowerLeg + foot + toe + toeTop; + offset = offset / 100; + return offset +}; + +function getJointData() { + var allJointData = []; + var jointNames = MyAvatar.jointNames; + jointNames.forEach(function(joint, index) { + var translation = MyAvatar.getJointTranslation(index); + var rotation = MyAvatar.getJointRotation(index) + allJointData.push({ + joint: joint, + index: index, + translation: translation, + rotation: rotation + }); + }); + + return allJointData; +}; + +var leftPad = new ThumbPad('left'); +var rightPad = new ThumbPad('right'); +var leftTrigger = new Trigger('left'); +var rightTrigger = new Trigger('right'); + +var mappingName, teleportMapping; + +var TELEPORT_DELAY = 100; + + +function registerMappings() { + mappingName = 'Hifi-Teleporter-Dev-' + Math.random(); + teleportMapping = Controller.newMapping(mappingName); + teleportMapping.from(Controller.Standard.RT).peek().to(rightTrigger.buttonPress); + teleportMapping.from(Controller.Standard.LT).peek().to(leftTrigger.buttonPress); + + teleportMapping.from(Controller.Standard.RightPrimaryThumb).peek().to(rightPad.buttonPress); + teleportMapping.from(Controller.Standard.LeftPrimaryThumb).peek().to(leftPad.buttonPress); + + teleportMapping.from(Controller.Standard.LeftPrimaryThumb).when(leftTrigger.down).to(function(value) { + teleporter.enterTeleportMode('left') + return; + }); + teleportMapping.from(Controller.Standard.RightPrimaryThumb).when(rightTrigger.down).to(function(value) { + teleporter.enterTeleportMode('right') + return; + }); + teleportMapping.from(Controller.Standard.RT).when(Controller.Standard.RightPrimaryThumb).to(function(value) { + teleporter.enterTeleportMode('right') + return; + }); + teleportMapping.from(Controller.Standard.LT).when(Controller.Standard.LeftPrimaryThumb).to(function(value) { + teleporter.enterTeleportMode('left') + return; + }); + +} + +registerMappings(); + +var teleporter = new Teleporter(); + +Controller.enableMapping(mappingName); + +Script.scriptEnding.connect(cleanup); + +function cleanup() { + teleportMapping.disable(); + teleporter.disableMappings(); + teleporter.deleteTargetOverlay(); + teleporter.turnOffOverlayBeams(); + teleporter.deleteFadeSphere(); + if (teleporter.updateConnected !== null) { + Script.update.disconnect(teleporter.update); + } +} \ No newline at end of file