From f0c9f1d120522f18c232a55d06520f6b81508b1c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 27 Jul 2017 15:32:13 -0700 Subject: [PATCH 01/59] starting on controller-dispatcher experiment --- scripts/defaultScripts.js | 6 +- .../controllers/controllerDispatcher.js | 122 ++++++++++ .../system/controllers/controllerScripts.js | 11 +- scripts/system/controllers/nearGrab.js | 208 ++++++++++++++++++ 4 files changed, 340 insertions(+), 7 deletions(-) create mode 100644 scripts/system/controllers/controllerDispatcher.js create mode 100644 scripts/system/controllers/nearGrab.js diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 2270118861..f4c7b42ee2 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -20,15 +20,15 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/bubble.js", "system/snapshot.js", "system/help.js", - "system/pal.js", // "system/mod.js", // older UX, if you prefer + // "system/pal.js", // "system/mod.js", // older UX, if you prefer "system/makeUserConnection.js", "system/tablet-goto.js", "system/marketplaces/marketplaces.js", - "system/edit.js", + // "system/edit.js", "system/notifications.js", "system/dialTone.js", "system/firstPersonHMD.js", - "system/tablet-ui/tabletUI.js" + // "system/tablet-ui/tabletUI.js" ]; var DEFAULT_SCRIPTS_SEPARATE = [ "system/controllers/controllerScripts.js", diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js new file mode 100644 index 0000000000..5470c64438 --- /dev/null +++ b/scripts/system/controllers/controllerDispatcher.js @@ -0,0 +1,122 @@ +"use strict"; + +// controllerDispatcher.js +// +// 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, Entities, controllerDispatcherPlugins, Controller, Vec3, getControllerWorldLocation */ + +Script.include("/~/system/libraries/utils.js"); +Script.include("/~/system/libraries/controllers.js"); + +(function() { + var _this = this; + + // var LEFT_HAND = 0; + // var RIGHT_HAND = 1; + + var NEAR_GRAB_RADIUS = 0.1; + var DISPATCHER_PROPERTIES = [ + "position", + "registrationPoint", + "rotation", + "gravity", + "collidesWith", + "dynamic", + "collisionless", + "locked", + "name", + "shapeType", + "parentID", + "parentJointIndex", + "density", + "dimensions", + "userData" + ]; + + this.runningPluginName = null; + + this.leftTriggerPress = function(value) { + _this.leftTriggerValue = value; + }; + + this.leftTriggerClick = function(value) { + _this.leftTriggerClicked = value; + }; + + this.rightTriggerPress = function(value) { + _this.rightTriggerValue = value; + }; + + this.rightTriggerClick = function(value) { + _this.rightTriggerClicked = value; + }; + + this.update = function () { + + var leftControllerLocation = getControllerWorldLocation(Controller.Standard.LeftHand, true); + var rightControllerLocation = getControllerWorldLocation(Controller.Standard.RightHand, true); + + var leftNearbyEntityIDs = Entities.findEntities(leftControllerLocation, NEAR_GRAB_RADIUS); + var rightNearbyEntityIDs = Entities.findEntities(rightControllerLocation, NEAR_GRAB_RADIUS); + + var leftNearbyEntityProperties = {}; + leftNearbyEntityIDs.forEach(function (entityID) { + var props = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); + props.id = entityID; + props.distanceFromController = Vec3.length(Vec3.subtract(leftControllerLocation, props.position)); + leftNearbyEntityProperties.push(props); + }); + + var rightNearbyEntityProperties = {}; + rightNearbyEntityIDs.forEach(function (entityID) { + var props = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); + props.id = entityID; + props.distanceFromController = Vec3.length(Vec3.subtract(rightControllerLocation, props.position)); + rightNearbyEntityProperties.push(props); + }); + + + var controllerData = { + triggerValues: [this.leftTriggerValue, this.rightTriggerValue], + triggerPresses: [this.leftTriggerPress, this.rightTriggerPress], + controllerLocations: [ leftControllerLocation, rightControllerLocation ], + nearbyEntityProperties: [ leftNearbyEntityProperties, rightNearbyEntityProperties ], + }; + + if (this.runningPluginName) { + var plugin = controllerDispatcherPlugins[this.runningPluginName]; + if (!plugin || !plugin.run(controllerData)) { + this.runningPluginName = null; + } + } else if (controllerDispatcherPlugins) { + for (var pluginName in controllerDispatcherPlugins) { + // TODO sort names by plugin.priority + if (controllerDispatcherPlugins.hasOwnProperty(pluginName)) { + var candidatePlugin = controllerDispatcherPlugins[pluginName]; + if (candidatePlugin.isReady(controllerData)) { + this.runningPluginName = candidatePlugin; + break; + } + } + } + } + }; + + var MAPPING_NAME = "com.highfidelity.controllerDispatcher"; + var mapping = Controller.newMapping(MAPPING_NAME); + mapping.from([Controller.Standard.RT]).peek().to(this.rightTriggerPress); + mapping.from([Controller.Standard.RTClick]).peek().to(this.rightTriggerClicked); + mapping.from([Controller.Standard.LT]).peek().to(this.leftTriggerPress); + mapping.from([Controller.Standard.LTClick]).peek().to(this.leftTriggerClicked); + Controller.enableMapping(MAPPING_NAME); + + this.cleanup = function () { + Script.update.disconnect(this.update); + Controller.disableMapping(MAPPING_NAME); + }; + + Script.scriptEnding.connect(this.cleanup); + Script.update.connect(this.update); +}()); diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index df11a1e5be..5adfd93745 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -12,11 +12,14 @@ var CONTOLLER_SCRIPTS = [ "squeezeHands.js", "controllerDisplayManager.js", - "handControllerGrab.js", - "handControllerPointer.js", - "grab.js", - "teleport.js", + // "handControllerGrab.js", + // "handControllerPointer.js", + // "grab.js", + // "teleport.js", "toggleAdvancedMovementForHandControllers.js", + + "ControllerDispatcher.js", + "nearGrab.js" ]; var DEBUG_MENU_ITEM = "Debug defaultScripts.js"; diff --git a/scripts/system/controllers/nearGrab.js b/scripts/system/controllers/nearGrab.js new file mode 100644 index 0000000000..691f66c4d3 --- /dev/null +++ b/scripts/system/controllers/nearGrab.js @@ -0,0 +1,208 @@ +"use strict"; + +// nearGrab.js +// +// 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, Entities, HMD, Camera, MyAvatar, Controller, controllerDispatcherPlugins */ + + +(function() { + + var LEFT_HAND = 0; + var RIGHT_HAND = 1; + + var HAPTIC_PULSE_STRENGTH = 1.0; + var HAPTIC_PULSE_DURATION = 13.0; + + var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; + var FORBIDDEN_GRAB_NAMES = ["Grab Debug Entity", "grab pointer"]; + + var NULL_UUID = "{00000000-0000-0000-0000-000000000000}"; + var AVATAR_SELF_ID = "{00000000-0000-0000-0000-000000000001}"; + + function getControllerJointIndex(hand) { + if (HMD.isHandControllerAvailable()) { + var controllerJointIndex = -1; + if (Camera.mode === "first person") { + controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? + "_CONTROLLER_RIGHTHAND" : + "_CONTROLLER_LEFTHAND"); + } else if (Camera.mode === "third person") { + controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? + "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : + "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); + } + + return controllerJointIndex; + } + + return MyAvatar.getJointIndex("Head"); + } + + function propsArePhysical(props) { + if (!props.dynamic) { + return false; + } + var isPhysical = (props.shapeType && props.shapeType != 'none'); + return isPhysical; + } + + function entityIsGrabbable(props) { + var grabbableProps = {}; + var userDataParsed = JSON.parse(props.userData); + if (userDataParsed && userDataParsed.grabbable) { + grabbableProps = userDataParsed.grabbable; + } + var grabbable = propsArePhysical(props); + if (grabbableProps.hasOwnProperty("grabbable")) { + grabbable = grabbableProps.grabbable; + } + if (!grabbable) { + return false; + } + if (FORBIDDEN_GRAB_TYPES.indexOf(props.type) >= 0) { + return false; + } + if (props.locked) { + return false; + } + if (FORBIDDEN_GRAB_NAMES.indexOf(props.name) >= 0) { + return false; + } + return true; + } + + + function NearGrab(hand) { + this.priority = 5; + + this.hand = hand; + this.grabbedThingID = null; + this.previousParentID = {}; + this.previousParentJointIndex = {}; + this.previouslyUnhooked = {}; + + + this.handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); + + this.thisHandIsParent = function(props) { + if (props.parentID !== MyAvatar.sessionUUID && props.parentID !== AVATAR_SELF_ID) { + return false; + } + + var handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); + if (props.parentJointIndex == handJointIndex) { + return true; + } + + var controllerJointIndex = this.controllerJointIndex; + if (props.parentJointIndex == controllerJointIndex) { + return true; + } + + var controllerCRJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? + "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : + "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); + if (props.parentJointIndex == controllerCRJointIndex) { + return true; + } + + return false; + }; + + this.startNearGrab = function (controllerData) { + var grabbedProperties = controllerData.nearbyEntityProperties[this.hand][this.grabbedThingID]; + Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); + + var reparentProps = { + parentID: AVATAR_SELF_ID, + parentJointIndex: getControllerJointIndex(this.hand), + velocity: {x: 0, y: 0, z: 0}, + angularVelocity: {x: 0, y: 0, z: 0} + }; + + if (this.thisHandIsParent(grabbedProperties)) { + // this should never happen, but if it does, don't set previous parent to be this hand. + // this.previousParentID[this.grabbedThingID] = NULL; + // this.previousParentJointIndex[this.grabbedThingID] = -1; + } else { + this.previousParentID[this.grabbedThingID] = grabbedProperties.parentID; + this.previousParentJointIndex[this.grabbedThingID] = grabbedProperties.parentJointIndex; + } + Entities.editEntity(this.grabbedThingID, reparentProps); + }; + + this.endNearGrab = function (controllerData) { + if (this.previousParentID[this.grabbedThingID] === NULL_UUID) { + Entities.editEntity(this.grabbedThingID, { + parentID: this.previousParentID[this.grabbedThingID], + parentJointIndex: this.previousParentJointIndex[this.grabbedThingID] + }); + } else { + // we're putting this back as a child of some other parent, so zero its velocity + Entities.editEntity(this.grabbedThingID, { + parentID: this.previousParentID[this.grabbedThingID], + parentJointIndex: this.previousParentJointIndex[this.grabbedThingID], + velocity: {x: 0, y: 0, z: 0}, + angularVelocity: {x: 0, y: 0, z: 0} + }); + } + this.grabbedThingID = null; + }; + + this.isReady = function (controllerData) { + if (!controllerData.triggerPresses[this.hand]) { + return false; + } + + var grabbable = null; + var bestDistance = 1000; + controllerData.nearbyEntityProperties[this.hand].forEach(function(nearbyEntityProperties) { + if (entityIsGrabbable(nearbyEntityProperties)) { + if (nearbyEntityProperties.distanceFromController < bestDistance) { + bestDistance = nearbyEntityProperties.distanceFromController; + grabbable = nearbyEntityProperties; + } + } + }); + + if (grabbable) { + this.grabbedThingID = grabbable.id; + this.startNearGrab(); + return true; + } else { + return false; + } + }; + + this.run = function (controllerData) { + if (!controllerData.triggerPresses[this.hand]) { + this.endNearGrab(controllerData); + return false; + } + return true; + }; + } + + var leftNearGrab = new NearGrab(LEFT_HAND); + leftNearGrab.name = "leftNearGrab"; + + var rightNearGrab = new NearGrab(RIGHT_HAND); + rightNearGrab.name = "rightNearGrab"; + + if (!controllerDispatcherPlugins) { + controllerDispatcherPlugins = {}; + } + controllerDispatcherPlugins.leftNearGrab = leftNearGrab; + controllerDispatcherPlugins.rightNearGrab = rightNearGrab; + + + this.cleanup = function () { + delete controllerDispatcherPlugins.leftNearGrab; + delete controllerDispatcherPlugins.rightNearGrab; + }; + Script.scriptEnding.connect(this.cleanup); +}()); From fcf1dc839a8bb81ab3e321d0e18d33b4c6306875 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 27 Jul 2017 17:52:23 -0700 Subject: [PATCH 02/59] parenting near grab works --- .../controllers/controllerDispatcher.js | 72 ++++++++++--------- scripts/system/controllers/nearGrab.js | 40 ++++++----- 2 files changed, 63 insertions(+), 49 deletions(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 5470c64438..8ffa0f0ca3 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -7,16 +7,21 @@ /* global Script, Entities, controllerDispatcherPlugins, Controller, Vec3, getControllerWorldLocation */ +controllerDispatcherPlugins = {}; + Script.include("/~/system/libraries/utils.js"); Script.include("/~/system/libraries/controllers.js"); (function() { var _this = this; - // var LEFT_HAND = 0; - // var RIGHT_HAND = 1; + var LEFT_HAND = 0; + var RIGHT_HAND = 1; var NEAR_GRAB_RADIUS = 0.1; + var NEAR_GRAB_MAX_DISTANCE = 1.0; // you cannot grab objects that are this far away from your hand + + var DISPATCHER_PROPERTIES = [ "position", "registrationPoint", @@ -36,6 +41,11 @@ Script.include("/~/system/libraries/controllers.js"); ]; this.runningPluginName = null; + this.leftTriggerValue = 0; + this.leftTriggerClicked = 0; + this.rightTriggerValue = 0; + this.rightTriggerClicked = 0; + this.leftTriggerPress = function(value) { _this.leftTriggerValue = value; @@ -55,40 +65,36 @@ Script.include("/~/system/libraries/controllers.js"); this.update = function () { - var leftControllerLocation = getControllerWorldLocation(Controller.Standard.LeftHand, true); - var rightControllerLocation = getControllerWorldLocation(Controller.Standard.RightHand, true); - - var leftNearbyEntityIDs = Entities.findEntities(leftControllerLocation, NEAR_GRAB_RADIUS); - var rightNearbyEntityIDs = Entities.findEntities(rightControllerLocation, NEAR_GRAB_RADIUS); - - var leftNearbyEntityProperties = {}; - leftNearbyEntityIDs.forEach(function (entityID) { - var props = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); - props.id = entityID; - props.distanceFromController = Vec3.length(Vec3.subtract(leftControllerLocation, props.position)); - leftNearbyEntityProperties.push(props); - }); - - var rightNearbyEntityProperties = {}; - rightNearbyEntityIDs.forEach(function (entityID) { - var props = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); - props.id = entityID; - props.distanceFromController = Vec3.length(Vec3.subtract(rightControllerLocation, props.position)); - rightNearbyEntityProperties.push(props); - }); + var controllerLocations = [getControllerWorldLocation(Controller.Standard.LeftHand, true), + getControllerWorldLocation(Controller.Standard.RightHand, true)]; + var nearbyEntityProperties = [[], []]; + for (var i = LEFT_HAND; i <= RIGHT_HAND; i ++) { + // todo: check controllerLocations[i].valid + var controllerPosition = controllerLocations[i].position; + var nearbyEntityIDs = Entities.findEntities(controllerPosition, NEAR_GRAB_RADIUS); + for (var j = 0; j < nearbyEntityIDs.length; j++) { + var entityID = nearbyEntityIDs[j]; + var props = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); + props.id = entityID; + props.distanceFromController = Vec3.length(Vec3.subtract(controllerPosition, props.position)); + if (props.distanceFromController < NEAR_GRAB_MAX_DISTANCE) { + nearbyEntityProperties[i].push(props); + } + } + } var controllerData = { - triggerValues: [this.leftTriggerValue, this.rightTriggerValue], - triggerPresses: [this.leftTriggerPress, this.rightTriggerPress], - controllerLocations: [ leftControllerLocation, rightControllerLocation ], - nearbyEntityProperties: [ leftNearbyEntityProperties, rightNearbyEntityProperties ], + triggerValues: [_this.leftTriggerValue, _this.rightTriggerValue], + triggerClicks: [_this.leftTriggerClicked, _this.rightTriggerClicked], + controllerLocations: controllerLocations, + nearbyEntityProperties: nearbyEntityProperties, }; - if (this.runningPluginName) { - var plugin = controllerDispatcherPlugins[this.runningPluginName]; + if (_this.runningPluginName) { + var plugin = controllerDispatcherPlugins[_this.runningPluginName]; if (!plugin || !plugin.run(controllerData)) { - this.runningPluginName = null; + _this.runningPluginName = null; } } else if (controllerDispatcherPlugins) { for (var pluginName in controllerDispatcherPlugins) { @@ -96,7 +102,7 @@ Script.include("/~/system/libraries/controllers.js"); if (controllerDispatcherPlugins.hasOwnProperty(pluginName)) { var candidatePlugin = controllerDispatcherPlugins[pluginName]; if (candidatePlugin.isReady(controllerData)) { - this.runningPluginName = candidatePlugin; + _this.runningPluginName = pluginName; break; } } @@ -107,9 +113,9 @@ Script.include("/~/system/libraries/controllers.js"); var MAPPING_NAME = "com.highfidelity.controllerDispatcher"; var mapping = Controller.newMapping(MAPPING_NAME); mapping.from([Controller.Standard.RT]).peek().to(this.rightTriggerPress); - mapping.from([Controller.Standard.RTClick]).peek().to(this.rightTriggerClicked); + mapping.from([Controller.Standard.RTClick]).peek().to(this.rightTriggerClick); mapping.from([Controller.Standard.LT]).peek().to(this.leftTriggerPress); - mapping.from([Controller.Standard.LTClick]).peek().to(this.leftTriggerClicked); + mapping.from([Controller.Standard.LTClick]).peek().to(this.leftTriggerClick); Controller.enableMapping(MAPPING_NAME); this.cleanup = function () { diff --git a/scripts/system/controllers/nearGrab.js b/scripts/system/controllers/nearGrab.js index 691f66c4d3..fc805114ee 100644 --- a/scripts/system/controllers/nearGrab.js +++ b/scripts/system/controllers/nearGrab.js @@ -35,7 +35,7 @@ "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); } - + return controllerJointIndex; } @@ -52,7 +52,11 @@ function entityIsGrabbable(props) { var grabbableProps = {}; - var userDataParsed = JSON.parse(props.userData); + var userDataParsed = null; + try { + userDataParsed = JSON.parse(props.userData); + } catch (err) { + } if (userDataParsed && userDataParsed.grabbable) { grabbableProps = userDataParsed.grabbable; } @@ -60,6 +64,7 @@ if (grabbableProps.hasOwnProperty("grabbable")) { grabbable = grabbableProps.grabbable; } + if (!grabbable) { return false; } @@ -86,6 +91,7 @@ this.previouslyUnhooked = {}; + // todo: does this change if the avatar changes? this.handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); this.thisHandIsParent = function(props) { @@ -113,8 +119,7 @@ return false; }; - this.startNearGrab = function (controllerData) { - var grabbedProperties = controllerData.nearbyEntityProperties[this.hand][this.grabbedThingID]; + this.startNearGrab = function (controllerData, grabbedProperties) { Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); var reparentProps = { @@ -154,24 +159,27 @@ }; this.isReady = function (controllerData) { - if (!controllerData.triggerPresses[this.hand]) { + if (controllerData.triggerClicks[this.hand] == 0) { return false; } - var grabbable = null; + var grabbedProperties = null; var bestDistance = 1000; - controllerData.nearbyEntityProperties[this.hand].forEach(function(nearbyEntityProperties) { - if (entityIsGrabbable(nearbyEntityProperties)) { - if (nearbyEntityProperties.distanceFromController < bestDistance) { - bestDistance = nearbyEntityProperties.distanceFromController; - grabbable = nearbyEntityProperties; + var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand]; + + for (var i = 0; i < nearbyEntityProperties.length; i ++) { + var props = nearbyEntityProperties[i]; + if (entityIsGrabbable(props)) { + if (props.distanceFromController < bestDistance) { + bestDistance = props.distanceFromController; + grabbedProperties = props; } } - }); + } - if (grabbable) { - this.grabbedThingID = grabbable.id; - this.startNearGrab(); + if (grabbedProperties) { + this.grabbedThingID = grabbedProperties.id; + this.startNearGrab(controllerData, grabbedProperties); return true; } else { return false; @@ -179,7 +187,7 @@ }; this.run = function (controllerData) { - if (!controllerData.triggerPresses[this.hand]) { + if (controllerData.triggerClicks[this.hand] == 0) { this.endNearGrab(controllerData); return false; } From cfe3981bc2a5c0192fba6b340cdd6b2efb2a38b0 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 27 Jul 2017 18:22:17 -0700 Subject: [PATCH 03/59] cleanups --- .../controllers/controllerDispatcher.js | 15 ++- .../controllers/controllerDispatcherUtils.js | 54 +++++++++++ scripts/system/controllers/nearGrab.js | 97 +++++-------------- 3 files changed, 84 insertions(+), 82 deletions(-) create mode 100644 scripts/system/controllers/controllerDispatcherUtils.js diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 8ffa0f0ca3..820bfe942c 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -5,21 +5,20 @@ // 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, Entities, controllerDispatcherPlugins, Controller, Vec3, getControllerWorldLocation */ +/* global Script, Entities, controllerDispatcherPlugins, Controller, Vec3, getControllerWorldLocation, + LEFT_HAND, RIGHT_HAND */ controllerDispatcherPlugins = {}; Script.include("/~/system/libraries/utils.js"); Script.include("/~/system/libraries/controllers.js"); +Script.include("/~/system/controllers/controllerDispatcherUtils.js"); (function() { var _this = this; - var LEFT_HAND = 0; - var RIGHT_HAND = 1; - - var NEAR_GRAB_RADIUS = 0.1; - var NEAR_GRAB_MAX_DISTANCE = 1.0; // you cannot grab objects that are this far away from your hand + var NEAR_MIN_RADIUS = 0.1; + var NEAR_MAX_RADIUS = 1.0; var DISPATCHER_PROPERTIES = [ @@ -72,13 +71,13 @@ Script.include("/~/system/libraries/controllers.js"); for (var i = LEFT_HAND; i <= RIGHT_HAND; i ++) { // todo: check controllerLocations[i].valid var controllerPosition = controllerLocations[i].position; - var nearbyEntityIDs = Entities.findEntities(controllerPosition, NEAR_GRAB_RADIUS); + var nearbyEntityIDs = Entities.findEntities(controllerPosition, NEAR_MIN_RADIUS); for (var j = 0; j < nearbyEntityIDs.length; j++) { var entityID = nearbyEntityIDs[j]; var props = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); props.id = entityID; props.distanceFromController = Vec3.length(Vec3.subtract(controllerPosition, props.position)); - if (props.distanceFromController < NEAR_GRAB_MAX_DISTANCE) { + if (props.distanceFromController < NEAR_MAX_RADIUS) { nearbyEntityProperties[i].push(props); } } diff --git a/scripts/system/controllers/controllerDispatcherUtils.js b/scripts/system/controllers/controllerDispatcherUtils.js new file mode 100644 index 0000000000..d1c8143f98 --- /dev/null +++ b/scripts/system/controllers/controllerDispatcherUtils.js @@ -0,0 +1,54 @@ +"use strict"; + +// controllerDispatcherUtils.js +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + + +/* global Camera, HMD, MyAvatar, getControllerJointIndex, + LEFT_HAND, RIGHT_HAND, NULL_UUID, AVATAR_SELF_ID, getGrabbableData */ + +LEFT_HAND = 0; +RIGHT_HAND = 1; + +NULL_UUID = "{00000000-0000-0000-0000-000000000000}"; +AVATAR_SELF_ID = "{00000000-0000-0000-0000-000000000001}"; + +getGrabbableData = function (props) { + // look in userData for a "grabbable" key, return the value or some defaults + var grabbableData = {}; + var userDataParsed = null; + try { + userDataParsed = JSON.parse(props.userData); + } catch (err) { + } + if (userDataParsed && userDataParsed.grabbable) { + grabbableData = userDataParsed.grabbable; + } + if (!grabbableData.hasOwnProperty("grabbable")) { + grabbableData.grabbable = true; + } + + return grabbableData; +}; + + +getControllerJointIndex = function (hand) { + if (HMD.isHandControllerAvailable()) { + var controllerJointIndex = -1; + if (Camera.mode === "first person") { + controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? + "_CONTROLLER_RIGHTHAND" : + "_CONTROLLER_LEFTHAND"); + } else if (Camera.mode === "third person") { + controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? + "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : + "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); + } + + return controllerJointIndex; + } + + return MyAvatar.getJointIndex("Head"); +}; diff --git a/scripts/system/controllers/nearGrab.js b/scripts/system/controllers/nearGrab.js index fc805114ee..bf02858ada 100644 --- a/scripts/system/controllers/nearGrab.js +++ b/scripts/system/controllers/nearGrab.js @@ -6,82 +6,31 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Entities, HMD, Camera, MyAvatar, Controller, controllerDispatcherPlugins */ +/* global Script, Entities, MyAvatar, Controller, controllerDispatcherPlugins, + RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, getControllerJointIndex, getGrabbableData, NULL_UUID */ +Script.include("/~/system/controllers/controllerDispatcherUtils.js"); (function() { - var LEFT_HAND = 0; - var RIGHT_HAND = 1; - var HAPTIC_PULSE_STRENGTH = 1.0; var HAPTIC_PULSE_DURATION = 13.0; var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; var FORBIDDEN_GRAB_NAMES = ["Grab Debug Entity", "grab pointer"]; - var NULL_UUID = "{00000000-0000-0000-0000-000000000000}"; - var AVATAR_SELF_ID = "{00000000-0000-0000-0000-000000000001}"; - - function getControllerJointIndex(hand) { - if (HMD.isHandControllerAvailable()) { - var controllerJointIndex = -1; - if (Camera.mode === "first person") { - controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? - "_CONTROLLER_RIGHTHAND" : - "_CONTROLLER_LEFTHAND"); - } else if (Camera.mode === "third person") { - controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? - "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : - "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); - } - - return controllerJointIndex; - } - - return MyAvatar.getJointIndex("Head"); - } - - function propsArePhysical(props) { - if (!props.dynamic) { - return false; - } - var isPhysical = (props.shapeType && props.shapeType != 'none'); - return isPhysical; - } - - function entityIsGrabbable(props) { - var grabbableProps = {}; - var userDataParsed = null; - try { - userDataParsed = JSON.parse(props.userData); - } catch (err) { - } - if (userDataParsed && userDataParsed.grabbable) { - grabbableProps = userDataParsed.grabbable; - } - var grabbable = propsArePhysical(props); - if (grabbableProps.hasOwnProperty("grabbable")) { - grabbable = grabbableProps.grabbable; - } - - if (!grabbable) { - return false; - } - if (FORBIDDEN_GRAB_TYPES.indexOf(props.type) >= 0) { - return false; - } - if (props.locked) { - return false; - } - if (FORBIDDEN_GRAB_NAMES.indexOf(props.name) >= 0) { + function entityIsParentingGrabbable(props) { + var grabbable = getGrabbableData(props).grabbable; + if (!grabbable || + props.locked || + FORBIDDEN_GRAB_TYPES.indexOf(props.type) >= 0 || + FORBIDDEN_GRAB_NAMES.indexOf(props.name) >= 0) { return false; } return true; } - - function NearGrab(hand) { + function NearGrabParenting(hand) { this.priority = 5; this.hand = hand; @@ -119,7 +68,7 @@ return false; }; - this.startNearGrab = function (controllerData, grabbedProperties) { + this.startNearGrabParenting = function (controllerData, grabbedProperties) { Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); var reparentProps = { @@ -140,7 +89,7 @@ Entities.editEntity(this.grabbedThingID, reparentProps); }; - this.endNearGrab = function (controllerData) { + this.endNearGrabParenting = function (controllerData) { if (this.previousParentID[this.grabbedThingID] === NULL_UUID) { Entities.editEntity(this.grabbedThingID, { parentID: this.previousParentID[this.grabbedThingID], @@ -169,7 +118,7 @@ for (var i = 0; i < nearbyEntityProperties.length; i ++) { var props = nearbyEntityProperties[i]; - if (entityIsGrabbable(props)) { + if (entityIsParentingGrabbable(props)) { if (props.distanceFromController < bestDistance) { bestDistance = props.distanceFromController; grabbedProperties = props; @@ -179,7 +128,7 @@ if (grabbedProperties) { this.grabbedThingID = grabbedProperties.id; - this.startNearGrab(controllerData, grabbedProperties); + this.startNearGrabParenting(controllerData, grabbedProperties); return true; } else { return false; @@ -188,29 +137,29 @@ this.run = function (controllerData) { if (controllerData.triggerClicks[this.hand] == 0) { - this.endNearGrab(controllerData); + this.endNearGrabParenting(controllerData); return false; } return true; }; } - var leftNearGrab = new NearGrab(LEFT_HAND); - leftNearGrab.name = "leftNearGrab"; + var leftNearGrabParenting = new NearGrabParenting(LEFT_HAND); + leftNearGrabParenting.name = "leftNearGrabParenting"; - var rightNearGrab = new NearGrab(RIGHT_HAND); - rightNearGrab.name = "rightNearGrab"; + var rightNearGrabParenting = new NearGrabParenting(RIGHT_HAND); + rightNearGrabParenting.name = "rightNearGrabParenting"; if (!controllerDispatcherPlugins) { controllerDispatcherPlugins = {}; } - controllerDispatcherPlugins.leftNearGrab = leftNearGrab; - controllerDispatcherPlugins.rightNearGrab = rightNearGrab; + controllerDispatcherPlugins.leftNearGrabParenting = leftNearGrabParenting; + controllerDispatcherPlugins.rightNearGrabParenting = rightNearGrabParenting; this.cleanup = function () { - delete controllerDispatcherPlugins.leftNearGrab; - delete controllerDispatcherPlugins.rightNearGrab; + delete controllerDispatcherPlugins.leftNearGrabParenting; + delete controllerDispatcherPlugins.rightNearGrabParenting; }; Script.scriptEnding.connect(this.cleanup); }()); From 0d0f147056b734804a246af7506e23009b0c712e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 9 Aug 2017 17:48:35 -0700 Subject: [PATCH 04/59] port action-near-grab to dispatcher --- scripts/defaultScripts.js | 4 +- .../controllers/controllerDispatcher.js | 14 +- .../controllers/controllerDispatcherUtils.js | 54 ++++- .../controllerModules/nearActionGrabEntity.js | 203 ++++++++++++++++++ .../nearParentGrabEntity.js} | 102 ++++----- .../system/controllers/controllerScripts.js | 3 +- 6 files changed, 321 insertions(+), 59 deletions(-) create mode 100644 scripts/system/controllers/controllerModules/nearActionGrabEntity.js rename scripts/system/controllers/{nearGrab.js => controllerModules/nearParentGrabEntity.js} (59%) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index f4c7b42ee2..63f37f3199 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -20,11 +20,11 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/bubble.js", "system/snapshot.js", "system/help.js", - // "system/pal.js", // "system/mod.js", // older UX, if you prefer + "system/pal.js", // "system/mod.js", // older UX, if you prefer "system/makeUserConnection.js", "system/tablet-goto.js", "system/marketplaces/marketplaces.js", - // "system/edit.js", + "system/edit.js", "system/notifications.js", "system/dialTone.js", "system/firstPersonHMD.js", diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 820bfe942c..1560ac5b44 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -68,9 +68,9 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); getControllerWorldLocation(Controller.Standard.RightHand, true)]; var nearbyEntityProperties = [[], []]; - for (var i = LEFT_HAND; i <= RIGHT_HAND; i ++) { - // todo: check controllerLocations[i].valid - var controllerPosition = controllerLocations[i].position; + for (var h = LEFT_HAND; h <= RIGHT_HAND; h ++) { + // todo: check controllerLocations[h].valid + var controllerPosition = controllerLocations[h].position; var nearbyEntityIDs = Entities.findEntities(controllerPosition, NEAR_MIN_RADIUS); for (var j = 0; j < nearbyEntityIDs.length; j++) { var entityID = nearbyEntityIDs[j]; @@ -78,9 +78,15 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); props.id = entityID; props.distanceFromController = Vec3.length(Vec3.subtract(controllerPosition, props.position)); if (props.distanceFromController < NEAR_MAX_RADIUS) { - nearbyEntityProperties[i].push(props); + nearbyEntityProperties[h].push(props); } } + // sort by distance from each hand + nearbyEntityProperties[h].sort(function (a, b) { + var aDistance = Vec3.distance(a.position, controllerLocations[h]); + var bDistance = Vec3.distance(b.position, controllerLocations[h]); + return aDistance - bDistance; + }); } var controllerData = { diff --git a/scripts/system/controllers/controllerDispatcherUtils.js b/scripts/system/controllers/controllerDispatcherUtils.js index d1c8143f98..8717be34d5 100644 --- a/scripts/system/controllers/controllerDispatcherUtils.js +++ b/scripts/system/controllers/controllerDispatcherUtils.js @@ -6,8 +6,18 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Camera, HMD, MyAvatar, getControllerJointIndex, - LEFT_HAND, RIGHT_HAND, NULL_UUID, AVATAR_SELF_ID, getGrabbableData */ +/* global Camera, HMD, MyAvatar, controllerDispatcherPlugins, + MSECS_PER_SEC, LEFT_HAND, RIGHT_HAND, NULL_UUID, AVATAR_SELF_ID, FORBIDDEN_GRAB_TYPES, + HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, + enableDispatcherModule, + disableDispatcherModule, + getGrabbableData, + entityIsGrabbable, + getControllerJointIndex, + propsArePhysical +*/ + +MSECS_PER_SEC = 1000.0; LEFT_HAND = 0; RIGHT_HAND = 1; @@ -15,6 +25,23 @@ RIGHT_HAND = 1; NULL_UUID = "{00000000-0000-0000-0000-000000000000}"; AVATAR_SELF_ID = "{00000000-0000-0000-0000-000000000001}"; +FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; + +HAPTIC_PULSE_STRENGTH = 1.0; +HAPTIC_PULSE_DURATION = 13.0; + + +enableDispatcherModule = function (moduleName, module, priority) { + if (!controllerDispatcherPlugins) { + controllerDispatcherPlugins = {}; + } + controllerDispatcherPlugins[moduleName] = module; +}; + +disableDispatcherModule = function (moduleName) { + delete controllerDispatcherPlugins[moduleName]; +}; + getGrabbableData = function (props) { // look in userData for a "grabbable" key, return the value or some defaults var grabbableData = {}; @@ -29,10 +56,25 @@ getGrabbableData = function (props) { if (!grabbableData.hasOwnProperty("grabbable")) { grabbableData.grabbable = true; } + if (!grabbableData.hasOwnProperty("ignoreIK")) { + grabbableData.ignoreIK = true; + } + if (!grabbableData.hasOwnProperty("kinematicGrab")) { + grabbableData.kinematicGrab = false; + } return grabbableData; }; +entityIsGrabbable = function (props) { + var grabbable = getGrabbableData(props).grabbable; + if (!grabbable || + props.locked || + FORBIDDEN_GRAB_TYPES.indexOf(props.type) >= 0) { + return false; + } + return true; +}; getControllerJointIndex = function (hand) { if (HMD.isHandControllerAvailable()) { @@ -52,3 +94,11 @@ getControllerJointIndex = function (hand) { return MyAvatar.getJointIndex("Head"); }; + +propsArePhysical = function (props) { + if (!props.dynamic) { + return false; + } + var isPhysical = (props.shapeType && props.shapeType != 'none'); + return isPhysical; +}; diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js new file mode 100644 index 0000000000..57df123c1d --- /dev/null +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -0,0 +1,203 @@ +"use strict"; + +// nearActionGrabEntity.js +// +// 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, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, + getControllerJointIndex, getGrabbableData, NULL_UUID, enableDispatcherModule, disableDispatcherModule, + propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, entityIsGrabbable, + Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation +*/ + +Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllers.js"); + +(function() { + + function NearActionGrabEntity(hand) { + this.hand = hand; + this.grabbedThingID = null; + this.actionID = null; // action this script created... + + var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position + var ACTION_TTL = 15; // seconds + var ACTION_TTL_REFRESH = 5; + + // XXX does handJointIndex change if the avatar changes? + this.handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); + this.controllerJointIndex = getControllerJointIndex(this.hand); + + + // handPosition is where the avatar's hand appears to be, in-world. + this.getHandPosition = function () { + if (this.hand === RIGHT_HAND) { + return MyAvatar.getRightPalmPosition(); + } else { + return MyAvatar.getLeftPalmPosition(); + } + }; + + this.getHandRotation = function () { + if (this.hand === RIGHT_HAND) { + return MyAvatar.getRightPalmRotation(); + } else { + return MyAvatar.getLeftPalmRotation(); + } + }; + + + this.startNearGrabAction = function (controllerData, grabbedProperties) { + Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); + + var grabbableData = getGrabbableData(grabbedProperties); + this.ignoreIK = grabbableData.ignoreIK; + this.kinematicGrab = grabbableData.kinematicGrab; + + var handRotation; + var handPosition; + if (this.ignoreIK) { + var controllerID = + (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + var controllerLocation = getControllerWorldLocation(controllerID, false); + handRotation = controllerLocation.orientation; + handPosition = controllerLocation.position; + } else { + handRotation = this.getHandRotation(); + handPosition = this.getHandPosition(); + } + + var objectRotation = grabbedProperties.rotation; + this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); + + var currentObjectPosition = grabbedProperties.position; + var offset = Vec3.subtract(currentObjectPosition, handPosition); + this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); + + var now = Date.now(); + this.actionTimeout = now + (ACTION_TTL * MSECS_PER_SEC); + + if (this.actionID) { + Entities.deleteAction(this.grabbedThingID, this.actionID); + } + this.actionID = Entities.addAction("hold", this.grabbedThingID, { + hand: this.hand === RIGHT_HAND ? "right" : "left", + timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, + relativePosition: this.offsetPosition, + relativeRotation: this.offsetRotation, + ttl: ACTION_TTL, + kinematic: this.kinematicGrab, + kinematicSetVelocity: true, + ignoreIK: this.ignoreIK + }); + if (this.actionID === NULL_UUID) { + this.actionID = null; + return; + } + + Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ + action: 'grab', + grabbedEntity: this.grabbedThingID, + joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" + })); + }; + + // this is for when the action creation failed, before + this.restartNearGrabAction = function (controllerData) { + var props = Entities.getEntityProperties(this.grabbedThingID, ["position", "rotation", "userData"]); + if (props && entityIsGrabbable(props)) { + this.startNearGrabAction(controllerData, props); + } + }; + + // this is for when the action is going to time-out + this.refreshNearGrabAction = function (controllerData) { + var now = Date.now(); + if (this.actionID && this.actionTimeout - now < ACTION_TTL_REFRESH * MSECS_PER_SEC) { + // if less than a 5 seconds left, refresh the actions ttl + var success = Entities.updateAction(this.grabbedThingID, this.actionID, { + hand: this.hand === RIGHT_HAND ? "right" : "left", + timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, + relativePosition: this.offsetPosition, + relativeRotation: this.offsetRotation, + ttl: ACTION_TTL, + kinematic: this.kinematicGrab, + kinematicSetVelocity: true, + ignoreIK: this.ignoreIK + }); + if (success) { + this.actionTimeout = now + (ACTION_TTL * MSECS_PER_SEC); + } else { + print("continueNearGrabbing -- updateAction failed"); + this.restartNearGrabAction(controllerData); + } + } + }; + + this.endNearGrabAction = function (controllerData) { + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.grabbedThingID, "releaseGrab", args); + + Entities.deleteAction(this.grabbedThingID, this.actionID); + this.actionID = null; + + this.grabbedThingID = null; + }; + + this.isReady = function (controllerData) { + if (controllerData.triggerClicks[this.hand] == 0) { + return false; + } + + var grabbedProperties = null; + // nearbyEntityProperties is already sorted by length from controller + var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand]; + for (var i = 0; i < nearbyEntityProperties.length; i++) { + var props = nearbyEntityProperties[i]; + if (entityIsGrabbable(props)) { + grabbedProperties = props; + break; + } + } + + if (grabbedProperties) { + if (!propsArePhysical(grabbedProperties)) { + return false; // let nearParentGrabEntity handle it + } + this.grabbedThingID = grabbedProperties.id; + this.startNearGrabAction(controllerData, grabbedProperties); + return true; + } else { + return false; + } + }; + + this.run = function (controllerData) { + if (controllerData.triggerClicks[this.hand] == 0) { + this.endNearGrabAction(controllerData); + return false; + } + + if (!this.actionID) { + this.restartNearGrabAction(controllerData); + } + + this.refreshNearGrabAction(controllerData); + + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.grabbedThingID, "continueNearGrab", args); + + return true; + }; + } + + enableDispatcherModule("LeftNearActionGrabEntity", new NearActionGrabEntity(LEFT_HAND), 500); + enableDispatcherModule("RightNearActionGrabEntity", new NearActionGrabEntity(RIGHT_HAND), 500); + + this.cleanup = function () { + disableDispatcherModule("LeftNearActionGrabEntity"); + disableDispatcherModule("RightNearActionGrabEntity"); + }; + Script.scriptEnding.connect(this.cleanup); +}()); diff --git a/scripts/system/controllers/nearGrab.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js similarity index 59% rename from scripts/system/controllers/nearGrab.js rename to scripts/system/controllers/controllerModules/nearParentGrabEntity.js index bf02858ada..70e8bb363a 100644 --- a/scripts/system/controllers/nearGrab.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -1,38 +1,24 @@ "use strict"; -// nearGrab.js +// nearParentGrabEntity.js // // 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, Entities, MyAvatar, Controller, controllerDispatcherPlugins, - RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, getControllerJointIndex, getGrabbableData, NULL_UUID */ +/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, + getControllerJointIndex, getGrabbableData, NULL_UUID, enableDispatcherModule, disableDispatcherModule, + FORBIDDEN_GRAB_TYPES, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION +*/ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); (function() { - var HAPTIC_PULSE_STRENGTH = 1.0; - var HAPTIC_PULSE_DURATION = 13.0; - - var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; - var FORBIDDEN_GRAB_NAMES = ["Grab Debug Entity", "grab pointer"]; - - function entityIsParentingGrabbable(props) { - var grabbable = getGrabbableData(props).grabbable; - if (!grabbable || - props.locked || - FORBIDDEN_GRAB_TYPES.indexOf(props.type) >= 0 || - FORBIDDEN_GRAB_NAMES.indexOf(props.name) >= 0) { - return false; - } - return true; - } - - function NearGrabParenting(hand) { - this.priority = 5; + // XXX this.ignoreIK = (grabbableData.ignoreIK !== undefined) ? grabbableData.ignoreIK : true; + // XXX this.kinematicGrab = (grabbableData.kinematic !== undefined) ? grabbableData.kinematic : NEAR_GRABBING_KINEMATIC; + function NearParentingGrabEntity(hand) { this.hand = hand; this.grabbedThingID = null; this.previousParentID = {}; @@ -40,8 +26,9 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); this.previouslyUnhooked = {}; - // todo: does this change if the avatar changes? + // XXX does handJointIndex change if the avatar changes? this.handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); + this.controllerJointIndex = getControllerJointIndex(this.hand); this.thisHandIsParent = function(props) { if (props.parentID !== MyAvatar.sessionUUID && props.parentID !== AVATAR_SELF_ID) { @@ -68,12 +55,23 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); return false; }; - this.startNearGrabParenting = function (controllerData, grabbedProperties) { + this.startNearParentingGrabEntity = function (controllerData, grabbedProperties) { Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); + var handJointIndex; + // if (this.ignoreIK) { + // handJointIndex = this.controllerJointIndex; + // } else { + // handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); + // } + handJointIndex = this.controllerJointIndex; + + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.grabbedThingID, "startNearGrab", args); + var reparentProps = { parentID: AVATAR_SELF_ID, - parentJointIndex: getControllerJointIndex(this.hand), + parentJointIndex: handJointIndex, velocity: {x: 0, y: 0, z: 0}, angularVelocity: {x: 0, y: 0, z: 0} }; @@ -87,9 +85,15 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); this.previousParentJointIndex[this.grabbedThingID] = grabbedProperties.parentJointIndex; } Entities.editEntity(this.grabbedThingID, reparentProps); + + Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ + action: 'grab', + grabbedEntity: this.grabbedThingID, + joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" + })); }; - this.endNearGrabParenting = function (controllerData) { + this.endNearParentingGrabEntity = function (controllerData) { if (this.previousParentID[this.grabbedThingID] === NULL_UUID) { Entities.editEntity(this.grabbedThingID, { parentID: this.previousParentID[this.grabbedThingID], @@ -104,6 +108,10 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); angularVelocity: {x: 0, y: 0, z: 0} }); } + + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.grabbedThingID, "releaseGrab", args); + this.grabbedThingID = null; }; @@ -113,22 +121,22 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); } var grabbedProperties = null; - var bestDistance = 1000; + // nearbyEntityProperties is already sorted by length from controller var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand]; - - for (var i = 0; i < nearbyEntityProperties.length; i ++) { + for (var i = 0; i < nearbyEntityProperties.length; i++) { var props = nearbyEntityProperties[i]; - if (entityIsParentingGrabbable(props)) { - if (props.distanceFromController < bestDistance) { - bestDistance = props.distanceFromController; - grabbedProperties = props; - } + if (entityIsGrabbable(props)) { + grabbedProperties = props; + break; } } if (grabbedProperties) { + if (propsArePhysical(grabbedProperties)) { + return false; // let nearActionGrabEntity handle it + } this.grabbedThingID = grabbedProperties.id; - this.startNearGrabParenting(controllerData, grabbedProperties); + this.startNearParentingGrabEntity(controllerData, grabbedProperties); return true; } else { return false; @@ -137,29 +145,23 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); this.run = function (controllerData) { if (controllerData.triggerClicks[this.hand] == 0) { - this.endNearGrabParenting(controllerData); + this.endNearParentingGrabEntity(controllerData); return false; } + + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.grabbedThingID, "continueNearGrab", args); + return true; }; } - var leftNearGrabParenting = new NearGrabParenting(LEFT_HAND); - leftNearGrabParenting.name = "leftNearGrabParenting"; - - var rightNearGrabParenting = new NearGrabParenting(RIGHT_HAND); - rightNearGrabParenting.name = "rightNearGrabParenting"; - - if (!controllerDispatcherPlugins) { - controllerDispatcherPlugins = {}; - } - controllerDispatcherPlugins.leftNearGrabParenting = leftNearGrabParenting; - controllerDispatcherPlugins.rightNearGrabParenting = rightNearGrabParenting; - + enableDispatcherModule("LeftNearParentingGrabEntity", new NearParentingGrabEntity(LEFT_HAND), 500); + enableDispatcherModule("RightNearParentingGrabEntity", new NearParentingGrabEntity(RIGHT_HAND), 500); this.cleanup = function () { - delete controllerDispatcherPlugins.leftNearGrabParenting; - delete controllerDispatcherPlugins.rightNearGrabParenting; + disableDispatcherModule("LeftNearParentingGrabEntity"); + disableDispatcherModule("RightNearParentingGrabEntity"); }; Script.scriptEnding.connect(this.cleanup); }()); diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 5adfd93745..efc187dd86 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -19,7 +19,8 @@ var CONTOLLER_SCRIPTS = [ "toggleAdvancedMovementForHandControllers.js", "ControllerDispatcher.js", - "nearGrab.js" + "controllerModules/nearParentGrabEntity.js", + "controllerModules/nearActionGrabEntity.js" ]; var DEBUG_MENU_ITEM = "Debug defaultScripts.js"; From b4a54b017ac173be7c6184117b4fbb7d567b4d17 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 11 Aug 2017 12:15:41 -0700 Subject: [PATCH 05/59] plugins occupy activity-slots and more than one plugin may run at a time, as long as their slots aren't in conflict --- .../controllers/controllerDispatcher.js | 93 ++++++++++++++----- .../controllers/controllerDispatcherUtils.js | 15 +++ .../controllerModules/nearActionGrabEntity.js | 12 ++- .../controllerModules/nearParentGrabEntity.js | 15 ++- 4 files changed, 105 insertions(+), 30 deletions(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 1560ac5b44..9e454eafc0 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -20,7 +20,6 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); var NEAR_MIN_RADIUS = 0.1; var NEAR_MAX_RADIUS = 1.0; - var DISPATCHER_PROPERTIES = [ "position", "registrationPoint", @@ -39,7 +38,39 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); "userData" ]; - this.runningPluginName = null; + // a module can occupy one or more slots while it's running. If all the required slots for a module are + // not set to false (not in use), a module cannot start. When a module is using a slot, that module's name + // is stored as the value, rather than false. + this.activitySlots = { + leftHand: false, + rightHand: false + }; + + this.slotsAreAvailable = function (plugin) { + for (var i = 0; i < plugin.parameters.activitySlots.length; i++) { + if (_this.activitySlots[plugin.parameters.activitySlots[i]]) { + return false; // something is already using a slot which _this plugin requires + } + } + return true; + }; + + this.markSlots = function (plugin, used) { + for (var i = 0; i < plugin.parameters.activitySlots.length; i++) { + _this.activitySlots[plugin.parameters.activitySlots[i]] = used; + } + }; + + this.unmarkSlotsForPluginName = function (runningPluginName) { + // this is used to free activity-slots when a plugin is deactivated while it's running. + for (var activitySlot in _this.activitySlots) { + if (activitySlot.hasOwnProperty(activitySlot) && _this.activitySlots[activitySlot] == runningPluginName) { + _this.activitySlots[activitySlot] = false; + } + } + }; + + this.runningPluginNames = {}; this.leftTriggerValue = 0; this.leftTriggerClicked = 0; this.rightTriggerValue = 0; @@ -68,7 +99,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); getControllerWorldLocation(Controller.Standard.RightHand, true)]; var nearbyEntityProperties = [[], []]; - for (var h = LEFT_HAND; h <= RIGHT_HAND; h ++) { + for (var h = LEFT_HAND; h <= RIGHT_HAND; h++) { // todo: check controllerLocations[h].valid var controllerPosition = controllerLocations[h].position; var nearbyEntityIDs = Entities.findEntities(controllerPosition, NEAR_MIN_RADIUS); @@ -82,11 +113,12 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); } } // sort by distance from each hand - nearbyEntityProperties[h].sort(function (a, b) { + var sorter = function (a, b) { var aDistance = Vec3.distance(a.position, controllerLocations[h]); var bDistance = Vec3.distance(b.position, controllerLocations[h]); return aDistance - bDistance; - }); + }; + nearbyEntityProperties[h].sort(sorter); } var controllerData = { @@ -96,20 +128,35 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); nearbyEntityProperties: nearbyEntityProperties, }; - if (_this.runningPluginName) { - var plugin = controllerDispatcherPlugins[_this.runningPluginName]; - if (!plugin || !plugin.run(controllerData)) { - _this.runningPluginName = null; + + // check for plugins that would like to start + for (var pluginName in controllerDispatcherPlugins) { + // TODO sort names by plugin.priority + if (controllerDispatcherPlugins.hasOwnProperty(pluginName)) { + var candidatePlugin = controllerDispatcherPlugins[pluginName]; + if (_this.slotsAreAvailable(candidatePlugin) && candidatePlugin.isReady(controllerData)) { + // this plugin will start. add it to the list of running plugins and mark the + // activity-slots which this plugin consumes as "in use" + _this.runningPluginNames[pluginName] = true; + _this.markSlots(candidatePlugin, pluginName); + } } - } else if (controllerDispatcherPlugins) { - for (var pluginName in controllerDispatcherPlugins) { - // TODO sort names by plugin.priority - if (controllerDispatcherPlugins.hasOwnProperty(pluginName)) { - var candidatePlugin = controllerDispatcherPlugins[pluginName]; - if (candidatePlugin.isReady(controllerData)) { - _this.runningPluginName = pluginName; - break; - } + } + + // give time to running plugins + for (var runningPluginName in _this.runningPluginNames) { + if (_this.runningPluginNames.hasOwnProperty(runningPluginName)) { + var plugin = controllerDispatcherPlugins[runningPluginName]; + if (!plugin) { + // plugin was deactivated while running. find the activity-slots it was using and make + // them available. + delete _this.runningPluginNames[runningPluginName]; + _this.unmarkSlotsForPluginName(runningPluginName); + } else if (!plugin.run(controllerData)) { + // 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); } } } @@ -117,14 +164,14 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); var MAPPING_NAME = "com.highfidelity.controllerDispatcher"; var mapping = Controller.newMapping(MAPPING_NAME); - mapping.from([Controller.Standard.RT]).peek().to(this.rightTriggerPress); - mapping.from([Controller.Standard.RTClick]).peek().to(this.rightTriggerClick); - mapping.from([Controller.Standard.LT]).peek().to(this.leftTriggerPress); - mapping.from([Controller.Standard.LTClick]).peek().to(this.leftTriggerClick); + mapping.from([Controller.Standard.RT]).peek().to(_this.rightTriggerPress); + mapping.from([Controller.Standard.RTClick]).peek().to(_this.rightTriggerClick); + mapping.from([Controller.Standard.LT]).peek().to(_this.leftTriggerPress); + mapping.from([Controller.Standard.LTClick]).peek().to(_this.leftTriggerClick); Controller.enableMapping(MAPPING_NAME); this.cleanup = function () { - Script.update.disconnect(this.update); + Script.update.disconnect(_this.update); Controller.disableMapping(MAPPING_NAME); }; diff --git a/scripts/system/controllers/controllerDispatcherUtils.js b/scripts/system/controllers/controllerDispatcherUtils.js index 8717be34d5..7f494761f5 100644 --- a/scripts/system/controllers/controllerDispatcherUtils.js +++ b/scripts/system/controllers/controllerDispatcherUtils.js @@ -9,6 +9,7 @@ /* global Camera, HMD, MyAvatar, controllerDispatcherPlugins, MSECS_PER_SEC, LEFT_HAND, RIGHT_HAND, NULL_UUID, AVATAR_SELF_ID, FORBIDDEN_GRAB_TYPES, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, + makeDispatcherModuleParameters, enableDispatcherModule, disableDispatcherModule, getGrabbableData, @@ -31,6 +32,20 @@ HAPTIC_PULSE_STRENGTH = 1.0; HAPTIC_PULSE_DURATION = 13.0; +// priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step +// activitySlots -- indicates which "slots" must not yet be in use for this module to start +// requiredDataForStart -- which "situation" parts this module looks at to decide if it will start +// sleepMSBetweenRuns -- how long to wait between calls to this module's "run" method +makeDispatcherModuleParameters = function (priority, activitySlots, requiredDataForStart, sleepMSBetweenRuns) { + return { + priority: priority, + activitySlots: activitySlots, + requiredDataForStart: requiredDataForStart, + sleepMSBetweenRuns: sleepMSBetweenRuns + }; +}; + + enableDispatcherModule = function (moduleName, module, priority) { if (!controllerDispatcherPlugins) { controllerDispatcherPlugins = {}; diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index 57df123c1d..d6b5ffe4f7 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -8,7 +8,7 @@ /* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, getControllerJointIndex, getGrabbableData, NULL_UUID, enableDispatcherModule, disableDispatcherModule, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, entityIsGrabbable, - Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation + Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation, makeDispatcherModuleParameters */ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); @@ -21,6 +21,12 @@ Script.include("/~/system/libraries/controllers.js"); this.grabbedThingID = null; this.actionID = null; // action this script created... + this.parameters = makeDispatcherModuleParameters( + 500, + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100); + var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position var ACTION_TTL = 15; // seconds var ACTION_TTL_REFRESH = 5; @@ -192,8 +198,8 @@ Script.include("/~/system/libraries/controllers.js"); }; } - enableDispatcherModule("LeftNearActionGrabEntity", new NearActionGrabEntity(LEFT_HAND), 500); - enableDispatcherModule("RightNearActionGrabEntity", new NearActionGrabEntity(RIGHT_HAND), 500); + enableDispatcherModule("LeftNearActionGrabEntity", new NearActionGrabEntity(LEFT_HAND)); + enableDispatcherModule("RightNearActionGrabEntity", new NearActionGrabEntity(RIGHT_HAND)); this.cleanup = function () { disableDispatcherModule("LeftNearActionGrabEntity"); diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index 70e8bb363a..58bd3d2dab 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -7,8 +7,9 @@ /* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, - getControllerJointIndex, getGrabbableData, NULL_UUID, enableDispatcherModule, disableDispatcherModule, - FORBIDDEN_GRAB_TYPES, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION + getControllerJointIndex, NULL_UUID, enableDispatcherModule, disableDispatcherModule, + propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, + makeDispatcherModuleParameters, entityIsGrabbable */ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); @@ -25,6 +26,12 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); this.previousParentJointIndex = {}; this.previouslyUnhooked = {}; + this.parameters = makeDispatcherModuleParameters( + 500, + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100); + // XXX does handJointIndex change if the avatar changes? this.handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); @@ -156,8 +163,8 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); }; } - enableDispatcherModule("LeftNearParentingGrabEntity", new NearParentingGrabEntity(LEFT_HAND), 500); - enableDispatcherModule("RightNearParentingGrabEntity", new NearParentingGrabEntity(RIGHT_HAND), 500); + enableDispatcherModule("LeftNearParentingGrabEntity", new NearParentingGrabEntity(LEFT_HAND)); + enableDispatcherModule("RightNearParentingGrabEntity", new NearParentingGrabEntity(RIGHT_HAND)); this.cleanup = function () { disableDispatcherModule("LeftNearParentingGrabEntity"); From 7ae41064db48b596161c73e1fa31700c88be6e09 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 12 Aug 2017 11:19:39 -0700 Subject: [PATCH 06/59] tablet stylus works again --- interface/src/Application.cpp | 1 + scripts/defaultScripts.js | 2 +- .../controllers/controllerDispatcher.js | 73 +- .../controllers/controllerDispatcherUtils.js | 8 +- .../controllerModules/tabletStylusInput.js | 685 ++++++++++++++++++ .../system/controllers/controllerScripts.js | 5 +- 6 files changed, 756 insertions(+), 18 deletions(-) create mode 100644 scripts/system/controllers/controllerModules/tabletStylusInput.js diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 67bb96fa9f..d34f08d052 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1795,6 +1795,7 @@ QString Application::getUserAgent() { void Application::toggleTabletUI(bool shouldOpen) const { auto tabletScriptingInterface = DependencyManager::get(); auto hmd = DependencyManager::get(); + qDebug() << "Application::toggleTabletUI" << shouldOpen << hmd->getShouldShowTablet(); if (!(shouldOpen && hmd->getShouldShowTablet())) { auto HMD = DependencyManager::get(); HMD->toggleShouldShowTablet(); diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 63f37f3199..2270118861 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -28,7 +28,7 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/notifications.js", "system/dialTone.js", "system/firstPersonHMD.js", - // "system/tablet-ui/tabletUI.js" + "system/tablet-ui/tabletUI.js" ]; var DEFAULT_SCRIPTS_SEPARATE = [ "system/controllers/controllerScripts.js", diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 9e454eafc0..6283b3c583 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -38,6 +38,16 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); "userData" ]; + + var TARGET_UPDATE_HZ = 60; // 50hz good enough, but we're using update + var BASIC_TIMER_INTERVAL_MS = 1000 / TARGET_UPDATE_HZ; + var lastInterval = Date.now(); + var intervalCount = 0; + var totalDelta = 0; + var totalVariance = 0; + var highVarianceCount = 0; + var veryhighVarianceCount = 0; + // a module can occupy one or more slots while it's running. If all the required slots for a module are // not set to false (not in use), a module cannot start. When a module is using a slot, that module's name // is stored as the value, rather than false. @@ -77,26 +87,58 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); this.rightTriggerClicked = 0; - this.leftTriggerPress = function(value) { + this.leftTriggerPress = function (value) { _this.leftTriggerValue = value; }; - this.leftTriggerClick = function(value) { + this.leftTriggerClick = function (value) { _this.leftTriggerClicked = value; }; - this.rightTriggerPress = function(value) { + this.rightTriggerPress = function (value) { _this.rightTriggerValue = value; }; - this.rightTriggerClick = function(value) { + this.rightTriggerClick = function (value) { _this.rightTriggerClicked = value; }; - this.update = function () { + this.dataGatherers = {}; + this.dataGatherers.leftControllerLocation = function () { + return getControllerWorldLocation(Controller.Standard.LeftHand, true); + }; + this.dataGatherers.rightControllerLocation = function () { + return getControllerWorldLocation(Controller.Standard.RightHand, true); + }; - var controllerLocations = [getControllerWorldLocation(Controller.Standard.LeftHand, true), - getControllerWorldLocation(Controller.Standard.RightHand, true)]; + this.updateTimings = function () { + intervalCount++; + var thisInterval = Date.now(); + var deltaTimeMsec = thisInterval - lastInterval; + var deltaTime = deltaTimeMsec / 1000; + lastInterval = thisInterval; + + totalDelta += deltaTimeMsec; + + var variance = Math.abs(deltaTimeMsec - BASIC_TIMER_INTERVAL_MS); + totalVariance += variance; + + if (variance > 1) { + highVarianceCount++; + } + + if (variance > 5) { + veryhighVarianceCount++; + } + + return deltaTime; + }; + + this.update = function () { + var deltaTime = this.updateTimings(); + + var controllerLocations = [_this.dataGatherers.leftControllerLocation(), + _this.dataGatherers.rightControllerLocation()]; var nearbyEntityProperties = [[], []]; for (var h = LEFT_HAND; h <= RIGHT_HAND; h++) { @@ -113,12 +155,14 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); } } // sort by distance from each hand - var sorter = function (a, b) { - var aDistance = Vec3.distance(a.position, controllerLocations[h]); - var bDistance = Vec3.distance(b.position, controllerLocations[h]); - return aDistance - bDistance; + var makeSorter = function (handIndex) { + return function (a, b) { + var aDistance = Vec3.distance(a.position, controllerLocations[handIndex]); + var bDistance = Vec3.distance(b.position, controllerLocations[handIndex]); + return aDistance - bDistance; + }; }; - nearbyEntityProperties[h].sort(sorter); + nearbyEntityProperties[h].sort(makeSorter(h)); } var controllerData = { @@ -128,13 +172,14 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); nearbyEntityProperties: nearbyEntityProperties, }; + // print("QQQ dispatcher " + JSON.stringify(_this.runningPluginNames) + " : " + JSON.stringify(_this.activitySlots)); // check for plugins that would like to start for (var pluginName in controllerDispatcherPlugins) { // TODO sort names by plugin.priority if (controllerDispatcherPlugins.hasOwnProperty(pluginName)) { var candidatePlugin = controllerDispatcherPlugins[pluginName]; - if (_this.slotsAreAvailable(candidatePlugin) && candidatePlugin.isReady(controllerData)) { + if (_this.slotsAreAvailable(candidatePlugin) && candidatePlugin.isReady(controllerData, deltaTime)) { // this plugin will start. add it to the list of running plugins and mark the // activity-slots which this plugin consumes as "in use" _this.runningPluginNames[pluginName] = true; @@ -152,7 +197,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); // them available. delete _this.runningPluginNames[runningPluginName]; _this.unmarkSlotsForPluginName(runningPluginName); - } else if (!plugin.run(controllerData)) { + } else if (!plugin.run(controllerData, deltaTime)) { // 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]; diff --git a/scripts/system/controllers/controllerDispatcherUtils.js b/scripts/system/controllers/controllerDispatcherUtils.js index 7f494761f5..39fcf1ae66 100644 --- a/scripts/system/controllers/controllerDispatcherUtils.js +++ b/scripts/system/controllers/controllerDispatcherUtils.js @@ -8,7 +8,7 @@ /* global Camera, HMD, MyAvatar, controllerDispatcherPlugins, MSECS_PER_SEC, LEFT_HAND, RIGHT_HAND, NULL_UUID, AVATAR_SELF_ID, FORBIDDEN_GRAB_TYPES, - HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, + HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, ZERO_VEC, ONE_VEC, DEFAULT_REGISTRATION_POINT, INCHES_TO_METERS, makeDispatcherModuleParameters, enableDispatcherModule, disableDispatcherModule, @@ -19,6 +19,10 @@ */ MSECS_PER_SEC = 1000.0; +INCHES_TO_METERS = 1.0 / 39.3701; + +ZERO_VEC = { x: 0, y: 0, z: 0 }; +ONE_VEC = { x: 1, y: 1, z: 1 }; LEFT_HAND = 0; RIGHT_HAND = 1; @@ -31,6 +35,8 @@ FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; HAPTIC_PULSE_STRENGTH = 1.0; HAPTIC_PULSE_DURATION = 13.0; +DEFAULT_REGISTRATION_POINT = { x: 0.5, y: 0.5, z: 0.5 }; + // priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step // activitySlots -- indicates which "slots" must not yet be in use for this module to start diff --git a/scripts/system/controllers/controllerModules/tabletStylusInput.js b/scripts/system/controllers/controllerModules/tabletStylusInput.js new file mode 100644 index 0000000000..7e9f69959f --- /dev/null +++ b/scripts/system/controllers/controllerModules/tabletStylusInput.js @@ -0,0 +1,685 @@ +"use strict"; + +// tabletStylusInput.js +// +// 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, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, + NULL_UUID, enableDispatcherModule, disableDispatcherModule, + Messages, Quat, Vec3, getControllerWorldLocation, makeDispatcherModuleParameters, Overlays, ZERO_VEC, + AVATAR_SELF_ID, HMD, INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, Settings, getGrabPointSphereOffset +*/ + +Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllers.js"); + +(function() { + + // triggered when stylus presses a web overlay/entity + var HAPTIC_STYLUS_STRENGTH = 1.0; + var HAPTIC_STYLUS_DURATION = 20.0; + + var WEB_DISPLAY_STYLUS_DISTANCE = 0.5; + var WEB_STYLUS_LENGTH = 0.2; + var WEB_TOUCH_Y_OFFSET = 0.05; // how far forward (or back with a negative number) to slide stylus in hand + + + function stylusTargetHasKeyboardFocus(stylusTarget) { + if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { + return Entities.keyboardFocusEntity === stylusTarget.entityID; + } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { + return Overlays.keyboardFocusOverlay === stylusTarget.overlayID; + } + } + + function setKeyboardFocusOnStylusTarget(stylusTarget) { + if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID && + Entities.wantsHandControllerPointerEvents(stylusTarget.entityID)) { + Overlays.keyboardFocusOverlay = NULL_UUID; + Entities.keyboardFocusEntity = stylusTarget.entityID; + } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { + Overlays.keyboardFocusOverlay = stylusTarget.overlayID; + Entities.keyboardFocusEntity = NULL_UUID; + } + } + + function sendHoverEnterEventToStylusTarget(hand, stylusTarget) { + var pointerEvent = { + type: "Move", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: stylusTarget.position2D, + pos3D: stylusTarget.position, + normal: stylusTarget.normal, + direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), + button: "None" + }; + + if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { + Entities.sendHoverEnterEntity(stylusTarget.entityID, pointerEvent); + } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { + Overlays.sendHoverEnterOverlay(stylusTarget.overlayID, pointerEvent); + } + } + + function sendHoverOverEventToStylusTarget(hand, stylusTarget) { + var pointerEvent = { + type: "Move", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: stylusTarget.position2D, + pos3D: stylusTarget.position, + normal: stylusTarget.normal, + direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), + button: "None" + }; + + if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { + Entities.sendMouseMoveOnEntity(stylusTarget.entityID, pointerEvent); + Entities.sendHoverOverEntity(stylusTarget.entityID, pointerEvent); + } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { + Overlays.sendMouseMoveOnOverlay(stylusTarget.overlayID, pointerEvent); + Overlays.sendHoverOverOverlay(stylusTarget.overlayID, pointerEvent); + } + } + + function sendTouchStartEventToStylusTarget(hand, stylusTarget) { + var pointerEvent = { + type: "Press", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: stylusTarget.position2D, + pos3D: stylusTarget.position, + normal: stylusTarget.normal, + direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), + button: "Primary", + isPrimaryHeld: true + }; + + if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { + Entities.sendMousePressOnEntity(stylusTarget.entityID, pointerEvent); + Entities.sendClickDownOnEntity(stylusTarget.entityID, pointerEvent); + } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { + Overlays.sendMousePressOnOverlay(stylusTarget.overlayID, pointerEvent); + } + } + + function sendTouchEndEventToStylusTarget(hand, stylusTarget) { + var pointerEvent = { + type: "Release", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: stylusTarget.position2D, + pos3D: stylusTarget.position, + normal: stylusTarget.normal, + direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), + button: "Primary" + }; + + if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { + Entities.sendMouseReleaseOnEntity(stylusTarget.entityID, pointerEvent); + Entities.sendClickReleaseOnEntity(stylusTarget.entityID, pointerEvent); + Entities.sendHoverLeaveEntity(stylusTarget.entityID, pointerEvent); + } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { + Overlays.sendMouseReleaseOnOverlay(stylusTarget.overlayID, pointerEvent); + } + } + + function sendTouchMoveEventToStylusTarget(hand, stylusTarget) { + var pointerEvent = { + type: "Move", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: stylusTarget.position2D, + pos3D: stylusTarget.position, + normal: stylusTarget.normal, + direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), + button: "Primary", + isPrimaryHeld: true + }; + + if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { + Entities.sendMouseMoveOnEntity(stylusTarget.entityID, pointerEvent); + Entities.sendHoldingClickOnEntity(stylusTarget.entityID, pointerEvent); + } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { + Overlays.sendMouseMoveOnOverlay(stylusTarget.overlayID, pointerEvent); + } + } + + // will return undefined if overlayID does not exist. + function calculateStylusTargetFromOverlay(stylusTip, overlayID) { + var overlayPosition = Overlays.getProperty(overlayID, "position"); + if (overlayPosition === undefined) { + return; + } + + // project stylusTip onto overlay plane. + var overlayRotation = Overlays.getProperty(overlayID, "rotation"); + if (overlayRotation === undefined) { + return; + } + var normal = Vec3.multiplyQbyV(overlayRotation, {x: 0, y: 0, z: 1}); + var distance = Vec3.dot(Vec3.subtract(stylusTip.position, overlayPosition), normal); + var position = Vec3.subtract(stylusTip.position, Vec3.multiply(normal, distance)); + + // calclulate normalized position + var invRot = Quat.inverse(overlayRotation); + var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, overlayPosition)); + var dpi = Overlays.getProperty(overlayID, "dpi"); + + var dimensions; + if (dpi) { + // Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property + // is used as a scale. + var resolution = Overlays.getProperty(overlayID, "resolution"); + if (resolution === undefined) { + return; + } + resolution.z = 1; // Circumvent divide-by-zero. + var scale = Overlays.getProperty(overlayID, "dimensions"); + if (scale === undefined) { + return; + } + scale.z = 0.01; // overlay dimensions are 2D, not 3D. + dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale); + } else { + dimensions = Overlays.getProperty(overlayID, "dimensions"); + if (dimensions === undefined) { + return; + } + if (!dimensions.z) { + dimensions.z = 0.01; // sometimes overlay dimensions are 2D, not 3D. + } + } + var invDimensions = { x: 1 / dimensions.x, y: 1 / dimensions.y, z: 1 / dimensions.z }; + var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), DEFAULT_REGISTRATION_POINT); + + // 2D position on overlay plane in meters, relative to the bounding box upper-left hand corner. + var position2D = { x: normalizedPosition.x * dimensions.x, + y: (1 - normalizedPosition.y) * dimensions.y }; // flip y-axis + + return { + entityID: null, + overlayID: overlayID, + distance: distance, + position: position, + position2D: position2D, + normal: normal, + normalizedPosition: normalizedPosition, + dimensions: dimensions, + valid: true + }; + } + + // will return undefined if entity does not exist. + function calculateStylusTargetFromEntity(stylusTip, props) { + if (props.rotation === undefined) { + // if rotation is missing from props object, then this entity has probably been deleted. + return; + } + + // project stylus tip onto entity plane. + var normal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1}); + Vec3.multiplyQbyV(props.rotation, {x: 0, y: 1, z: 0}); + var distance = Vec3.dot(Vec3.subtract(stylusTip.position, props.position), normal); + var position = Vec3.subtract(stylusTip.position, Vec3.multiply(normal, distance)); + + // generate normalized coordinates + var invRot = Quat.inverse(props.rotation); + var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, props.position)); + var invDimensions = { x: 1 / props.dimensions.x, y: 1 / props.dimensions.y, z: 1 / props.dimensions.z }; + var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), props.registrationPoint); + + // 2D position on entity plane in meters, relative to the bounding box upper-left hand corner. + var position2D = { x: normalizedPosition.x * props.dimensions.x, + y: (1 - normalizedPosition.y) * props.dimensions.y }; // flip y-axis + + return { + entityID: props.id, + entityProps: props, + overlayID: null, + distance: distance, + position: position, + position2D: position2D, + normal: normal, + normalizedPosition: normalizedPosition, + dimensions: props.dimensions, + valid: true + }; + } + + function isNearStylusTarget(stylusTargets, edgeBorder, minNormalDistance, maxNormalDistance) { + for (var i = 0; i < stylusTargets.length; i++) { + var stylusTarget = stylusTargets[i]; + + // check to see if the projected stylusTip is within within the 2d border + var borderMin = {x: -edgeBorder, y: -edgeBorder}; + var borderMax = {x: stylusTarget.dimensions.x + edgeBorder, y: stylusTarget.dimensions.y + edgeBorder}; + if (stylusTarget.distance >= minNormalDistance && stylusTarget.distance <= maxNormalDistance && + stylusTarget.position2D.x >= borderMin.x && stylusTarget.position2D.y >= borderMin.y && + stylusTarget.position2D.x <= borderMax.x && stylusTarget.position2D.y <= borderMax.y) { + return true; + } + } + return false; + } + + function calculateNearestStylusTarget(stylusTargets) { + var nearestStylusTarget; + + for (var i = 0; i < stylusTargets.length; i++) { + var stylusTarget = stylusTargets[i]; + + if ((!nearestStylusTarget || stylusTarget.distance < nearestStylusTarget.distance) && + stylusTarget.normalizedPosition.x >= 0 && stylusTarget.normalizedPosition.y >= 0 && + stylusTarget.normalizedPosition.x <= 1 && stylusTarget.normalizedPosition.y <= 1) { + nearestStylusTarget = stylusTarget; + } + } + + return nearestStylusTarget; + } + + function getFingerWorldLocation(hand) { + var fingerJointName = (hand === RIGHT_HAND) ? "RightHandIndex4" : "LeftHandIndex4"; + + var fingerJointIndex = MyAvatar.getJointIndex(fingerJointName); + var fingerPosition = MyAvatar.getAbsoluteJointTranslationInObjectFrame(fingerJointIndex); + var fingerRotation = MyAvatar.getAbsoluteJointRotationInObjectFrame(fingerJointIndex); + var worldFingerRotation = Quat.multiply(MyAvatar.orientation, fingerRotation); + var worldFingerPosition = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, fingerPosition)); + + return { + position: worldFingerPosition, + orientation: worldFingerRotation, + rotation: worldFingerRotation, + valid: true + }; + } + + function distance2D(a, b) { + var dx = (a.x - b.x); + var dy = (a.y - b.y); + return Math.sqrt(dx * dx + dy * dy); + } + + function TabletStylusInput(hand) { + this.hand = hand; + this.previousStylusTouchingTarget = false; + this.stylusTouchingTarget = false; + + this.useFingerInsteadOfStylus = false; + this.fingerPointing = false; + + // initialize stylus tip + var DEFAULT_STYLUS_TIP = { + position: {x: 0, y: 0, z: 0}, + orientation: {x: 0, y: 0, z: 0, w: 0}, + rotation: {x: 0, y: 0, z: 0, w: 0}, + velocity: {x: 0, y: 0, z: 0}, + valid: false + }; + this.stylusTip = DEFAULT_STYLUS_TIP; + + + this.parameters = makeDispatcherModuleParameters( + 400, + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100); + + this.getOtherHandController = function() { + return (this.hand === RIGHT_HAND) ? Controller.Standard.LeftHand : Controller.Standard.RightHand; + }; + + this.handToController = function() { + return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + }; + + this.updateFingerAsStylusSetting = function () { + var DEFAULT_USE_FINGER_AS_STYLUS = false; + var USE_FINGER_AS_STYLUS = Settings.getValue("preferAvatarFingerOverStylus"); + if (USE_FINGER_AS_STYLUS === "") { + USE_FINGER_AS_STYLUS = DEFAULT_USE_FINGER_AS_STYLUS; + } + if (USE_FINGER_AS_STYLUS && MyAvatar.getJointIndex("LeftHandIndex4") !== -1) { + this.useFingerInsteadOfStylus = true; + } else { + this.useFingerInsteadOfStylus = false; + } + }; + + this.updateStylusTip = function() { + if (this.useFingerInsteadOfStylus) { + this.stylusTip = getFingerWorldLocation(this.hand); + } else { + this.stylusTip = getControllerWorldLocation(this.handToController(), true); + + // translate tip forward according to constant. + var TIP_OFFSET = {x: 0, y: WEB_STYLUS_LENGTH - WEB_TOUCH_Y_OFFSET, z: 0}; + this.stylusTip.position = Vec3.sum(this.stylusTip.position, + Vec3.multiplyQbyV(this.stylusTip.orientation, TIP_OFFSET)); + } + + // compute tip velocity from hand controller motion, it is more accurate than computing it from previous positions. + var pose = Controller.getPoseValue(this.handToController()); + if (pose.valid) { + var worldControllerPos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation)); + var worldControllerLinearVel = Vec3.multiplyQbyV(MyAvatar.orientation, pose.velocity); + var worldControllerAngularVel = Vec3.multiplyQbyV(MyAvatar.orientation, pose.angularVelocity); + var tipVelocity = Vec3.sum(worldControllerLinearVel, + Vec3.cross(worldControllerAngularVel, + Vec3.subtract(this.stylusTip.position, worldControllerPos))); + this.stylusTip.velocity = tipVelocity; + } else { + this.stylusTip.velocity = {x: 0, y: 0, z: 0}; + } + }; + + this.showStylus = function() { + if (this.stylus) { + return; + } + + var stylusProperties = { + name: "stylus", + url: Script.resourcesPath() + "meshes/tablet-stylus-fat.fbx", + loadPriority: 10.0, + localPosition: Vec3.sum({ x: 0.0, + y: WEB_TOUCH_Y_OFFSET, + z: 0.0 }, + getGrabPointSphereOffset(this.handToController())), + localRotation: Quat.fromVec3Degrees({ x: -90, y: 0, z: 0 }), + dimensions: { x: 0.01, y: 0.01, z: WEB_STYLUS_LENGTH }, + solid: true, + visible: true, + ignoreRayIntersection: true, + drawInFront: false, + parentID: AVATAR_SELF_ID, + parentJointIndex: MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? + "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : + "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND") + }; + this.stylus = Overlays.addOverlay("model", stylusProperties); + }; + + this.hideStylus = function() { + if (!this.stylus) { + return; + } + Overlays.deleteOverlay(this.stylus); + this.stylus = null; + }; + + this.stealTouchFocus = function(stylusTarget) { + // send hover events to target + // record the entity or overlay we are hovering over. + if ((stylusTarget.entityID === this.getOtherHandController().hoverEntity) || + (stylusTarget.overlayID === this.getOtherHandController().hoverOverlay)) { + this.getOtherHandController().relinquishTouchFocus(); + } + this.requestTouchFocus(stylusTarget); + }; + + this.requestTouchFocus = function(stylusTarget) { + + // send hover events to target if we can. + // record the entity or overlay we are hovering over. + if (stylusTarget.entityID && + stylusTarget.entityID !== this.hoverEntity && + stylusTarget.entityID !== this.getOtherHandController().hoverEntity) { + this.hoverEntity = stylusTarget.entityID; + sendHoverEnterEventToStylusTarget(this.hand, stylusTarget); + } else if (stylusTarget.overlayID && + stylusTarget.overlayID !== this.hoverOverlay && + stylusTarget.overlayID !== this.getOtherHandController().hoverOverlay) { + this.hoverOverlay = stylusTarget.overlayID; + sendHoverEnterEventToStylusTarget(this.hand, stylusTarget); + } + }; + + this.hasTouchFocus = function(stylusTarget) { + return ((stylusTarget.entityID && stylusTarget.entityID === this.hoverEntity) || + (stylusTarget.overlayID && stylusTarget.overlayID === this.hoverOverlay)); + }; + + this.relinquishTouchFocus = function() { + // send hover leave event. + var pointerEvent = { type: "Move", id: this.hand + 1 }; + if (this.hoverEntity) { + Entities.sendHoverLeaveEntity(this.hoverEntity, pointerEvent); + this.hoverEntity = null; + } else if (this.hoverOverlay) { + Overlays.sendMouseMoveOnOverlay(this.hoverOverlay, pointerEvent); + Overlays.sendHoverOverOverlay(this.hoverOverlay, pointerEvent); + Overlays.sendHoverLeaveOverlay(this.hoverOverlay, pointerEvent); + this.hoverOverlay = null; + } + }; + + this.pointFinger = function(value) { + var HIFI_POINT_INDEX_MESSAGE_CHANNEL = "Hifi-Point-Index"; + if (this.fingerPointing !== value) { + var message; + if (this.hand === RIGHT_HAND) { + message = { pointRightIndex: value }; + } else { + message = { pointLeftIndex: value }; + } + Messages.sendMessage(HIFI_POINT_INDEX_MESSAGE_CHANNEL, JSON.stringify(message), true); + this.fingerPointing = value; + } + }; + + this.processStylus = function(controllerData) { + this.updateStylusTip(); + + if (!this.stylusTip.valid) { + this.pointFinger(false); + this.hideStylus(); + return false; + } + + if (this.useFingerInsteadOfStylus) { + this.hideStylus(); + } + + // build list of stylus targets, near the stylusTip + var stylusTargets = []; + var candidateEntities = controllerData.nearbyEntityProperties; + var i, props, stylusTarget; + for (i = 0; i < candidateEntities.length; i++) { + props = candidateEntities[i]; + if (props && props.type === "Web") { + stylusTarget = calculateStylusTargetFromEntity(this.stylusTip, candidateEntities[i]); + if (stylusTarget) { + stylusTargets.push(stylusTarget); + } + } + } + + // add the tabletScreen, if it is valid + if (HMD.tabletScreenID && HMD.tabletScreenID !== NULL_UUID && + Overlays.getProperty(HMD.tabletScreenID, "visible")) { + stylusTarget = calculateStylusTargetFromOverlay(this.stylusTip, HMD.tabletScreenID); + if (stylusTarget) { + stylusTargets.push(stylusTarget); + } + } + + // add the tablet home button. + if (HMD.homeButtonID && HMD.homeButtonID !== NULL_UUID && + Overlays.getProperty(HMD.homeButtonID, "visible")) { + stylusTarget = calculateStylusTargetFromOverlay(this.stylusTip, HMD.homeButtonID); + if (stylusTarget) { + stylusTargets.push(stylusTarget); + } + } + + var TABLET_MIN_HOVER_DISTANCE = 0.01; + var TABLET_MAX_HOVER_DISTANCE = 0.1; + var TABLET_MIN_TOUCH_DISTANCE = -0.05; + var TABLET_MAX_TOUCH_DISTANCE = TABLET_MIN_HOVER_DISTANCE; + var EDGE_BORDER = 0.075; + + var hysteresisOffset = 0.0; + if (this.isNearStylusTarget) { + hysteresisOffset = 0.05; + } + + this.isNearStylusTarget = isNearStylusTarget(stylusTargets, EDGE_BORDER + hysteresisOffset, + TABLET_MIN_TOUCH_DISTANCE - hysteresisOffset, + WEB_DISPLAY_STYLUS_DISTANCE + hysteresisOffset); + + if (this.isNearStylusTarget) { + if (!this.useFingerInsteadOfStylus) { + this.showStylus(); + } else { + this.pointFinger(true); + } + } else { + this.hideStylus(); + this.pointFinger(false); + } + + var nearestStylusTarget = calculateNearestStylusTarget(stylusTargets); + + if (nearestStylusTarget && nearestStylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE && + nearestStylusTarget.distance < TABLET_MAX_HOVER_DISTANCE) { + + this.requestTouchFocus(nearestStylusTarget); + + if (!stylusTargetHasKeyboardFocus(nearestStylusTarget)) { + setKeyboardFocusOnStylusTarget(nearestStylusTarget); + } + + if (this.hasTouchFocus(nearestStylusTarget)) { + sendHoverOverEventToStylusTarget(this.hand, nearestStylusTarget); + } + + // filter out presses when tip is moving away from tablet. + // ensure that stylus is within bounding box by checking normalizedPosition + if (nearestStylusTarget.valid && nearestStylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE && + nearestStylusTarget.distance < TABLET_MAX_TOUCH_DISTANCE && + Vec3.dot(this.stylusTip.velocity, nearestStylusTarget.normal) < 0 && + nearestStylusTarget.normalizedPosition.x >= 0 && nearestStylusTarget.normalizedPosition.x <= 1 && + nearestStylusTarget.normalizedPosition.y >= 0 && nearestStylusTarget.normalizedPosition.y <= 1) { + + this.stylusTarget = nearestStylusTarget; + this.stylusTouchingTarget = true; + } + } else { + this.relinquishTouchFocus(); + } + + this.homeButtonTouched = false; + + if (this.isNearStylusTarget) { + return true; + } else { + this.pointFinger(false); + this.hideStylus(); + return false; + } + }; + + this.stylusTouchingEnter = function () { + this.stealTouchFocus(this.stylusTarget); + sendTouchStartEventToStylusTarget(this.hand, this.stylusTarget); + Controller.triggerHapticPulse(HAPTIC_STYLUS_STRENGTH, HAPTIC_STYLUS_DURATION, this.hand); + + this.touchingEnterTimer = 0; + this.touchingEnterStylusTarget = this.stylusTarget; + this.deadspotExpired = false; + + var TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.0381; + this.deadspotRadius = TOUCH_PRESS_TO_MOVE_DEADSPOT; + }; + + this.stylusTouchingExit = function () { + + if (this.stylusTarget === undefined) { + return; + } + + // special case to handle home button. + if (this.stylusTarget.overlayID === HMD.homeButtonID) { + Messages.sendLocalMessage("home", this.stylusTarget.overlayID); + } + + // send press event + if (this.deadspotExpired) { + sendTouchEndEventToStylusTarget(this.hand, this.stylusTarget); + } else { + sendTouchEndEventToStylusTarget(this.hand, this.touchingEnterStylusTarget); + } + }; + + this.stylusTouching = function (controllerData, dt) { + + this.touchingEnterTimer += dt; + + if (this.stylusTarget.entityID) { + this.stylusTarget = calculateStylusTargetFromEntity(this.stylusTip, this.stylusTarget.entityProps); + } else if (this.stylusTarget.overlayID) { + this.stylusTarget = calculateStylusTargetFromOverlay(this.stylusTip, this.stylusTarget.overlayID); + } + + var TABLET_MIN_TOUCH_DISTANCE = -0.1; + var TABLET_MAX_TOUCH_DISTANCE = 0.01; + + if (this.stylusTarget) { + if (this.stylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE && + this.stylusTarget.distance < TABLET_MAX_TOUCH_DISTANCE) { + var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds + if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || + distance2D(this.stylusTarget.position2D, + this.touchingEnterStylusTarget.position2D) > this.deadspotRadius) { + sendTouchMoveEventToStylusTarget(this.hand, this.stylusTarget); + this.deadspotExpired = true; + } + } else { + this.stylusTouchingTarget = false; + } + } else { + this.stylusTouchingTarget = false; + } + }; + + this.isReady = function (controllerData) { + return this.processStylus(controllerData); + }; + + this.run = function (controllerData, deltaTime) { + this.updateFingerAsStylusSetting(); + + if (!this.previousStylusTouchingTarget && this.stylusTouchingTarget) { + this.stylusTouchingEnter(); + } + if (this.previousStylusTouchingTarget && !this.stylusTouchingTarget) { + this.stylusTouchingExit(); + } + this.previousStylusTouchingTarget = this.stylusTouchingTarget; + + if (this.stylusTouchingTarget) { + this.stylusTouching(controllerData, deltaTime); + } + return this.processStylus(controllerData); + }; + + this.cleanup = function () { + this.hideStylus(); + }; + } + + var leftTabletStylusInput = new TabletStylusInput(LEFT_HAND); + var rightTabletStylusInput = new TabletStylusInput(RIGHT_HAND); + + enableDispatcherModule("LeftTabletStylusInput", leftTabletStylusInput); + enableDispatcherModule("RightTabletStylusInput", rightTabletStylusInput); + + + this.cleanup = function () { + disableDispatcherModule("LeftTabletStylusInput"); + disableDispatcherModule("RightTabletStylusInput"); + leftTabletStylusInput.cleanup(); + rightTabletStylusInput.cleanup(); + }; + Script.scriptEnding.connect(this.cleanup); +}()); diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index efc187dd86..31c464ee49 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -13,14 +13,15 @@ var CONTOLLER_SCRIPTS = [ "squeezeHands.js", "controllerDisplayManager.js", // "handControllerGrab.js", - // "handControllerPointer.js", + "handControllerPointer.js", // "grab.js", // "teleport.js", "toggleAdvancedMovementForHandControllers.js", "ControllerDispatcher.js", "controllerModules/nearParentGrabEntity.js", - "controllerModules/nearActionGrabEntity.js" + "controllerModules/nearActionGrabEntity.js", + "controllerModules/tabletStylusInput.js" ]; var DEBUG_MENU_ITEM = "Debug defaultScripts.js"; From 9fb1835a26433817fef0958903575f3a6297f572 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 12 Aug 2017 12:38:36 -0700 Subject: [PATCH 07/59] grabbing overlays works, again --- .../controllers/controllerDispatcher.js | 32 +++- .../nearParentGrabOverlay.js | 167 ++++++++++++++++++ .../system/controllers/controllerScripts.js | 1 + 3 files changed, 195 insertions(+), 5 deletions(-) create mode 100644 scripts/system/controllers/controllerModules/nearParentGrabOverlay.js diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 6283b3c583..d2b6418ee9 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -5,7 +5,7 @@ // 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, Entities, controllerDispatcherPlugins, Controller, Vec3, getControllerWorldLocation, +/* global Script, Entities, Overlays, controllerDispatcherPlugins, Controller, Vec3, getControllerWorldLocation, LEFT_HAND, RIGHT_HAND */ controllerDispatcherPlugins = {}; @@ -48,7 +48,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); var highVarianceCount = 0; var veryhighVarianceCount = 0; - // a module can occupy one or more slots while it's running. If all the required slots for a module are + // a module can occupy one or more "activity" slots while it's running. If all the required slots for a module are // not set to false (not in use), a module cannot start. When a module is using a slot, that module's name // is stored as the value, rather than false. this.activitySlots = { @@ -56,7 +56,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); rightHand: false }; - this.slotsAreAvailable = function (plugin) { + this.slotsAreAvailableForPlugin = function (plugin) { for (var i = 0; i < plugin.parameters.activitySlots.length; i++) { if (_this.activitySlots[plugin.parameters.activitySlots[i]]) { return false; // something is already using a slot which _this plugin requires @@ -140,8 +140,28 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); var controllerLocations = [_this.dataGatherers.leftControllerLocation(), _this.dataGatherers.rightControllerLocation()]; + // find 3d overlays near each hand + var nearbyOverlayIDs = []; + var h; + for (h = LEFT_HAND; h <= RIGHT_HAND; h++) { + // todo: check controllerLocations[h].valid + var nearbyOverlays = Overlays.findOverlays(controllerLocations[h].position, NEAR_MIN_RADIUS); + var makeOverlaySorter = function (handIndex) { + return function (a, b) { + var aPosition = Overlays.getProperty(a, "position"); + var aDistance = Vec3.distance(aPosition, controllerLocations[handIndex]); + var bPosition = Overlays.getProperty(b, "position"); + var bDistance = Vec3.distance(bPosition, controllerLocations[handIndex]); + return aDistance - bDistance; + }; + }; + nearbyOverlays.sort(makeOverlaySorter(h)); + nearbyOverlayIDs.push(nearbyOverlays); + } + + // find entities near each hand var nearbyEntityProperties = [[], []]; - for (var h = LEFT_HAND; h <= RIGHT_HAND; h++) { + for (h = LEFT_HAND; h <= RIGHT_HAND; h++) { // todo: check controllerLocations[h].valid var controllerPosition = controllerLocations[h].position; var nearbyEntityIDs = Entities.findEntities(controllerPosition, NEAR_MIN_RADIUS); @@ -165,11 +185,13 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); nearbyEntityProperties[h].sort(makeSorter(h)); } + // bundle up all the data about the current situation var controllerData = { triggerValues: [_this.leftTriggerValue, _this.rightTriggerValue], triggerClicks: [_this.leftTriggerClicked, _this.rightTriggerClicked], controllerLocations: controllerLocations, nearbyEntityProperties: nearbyEntityProperties, + nearbyOverlayIDs: nearbyOverlayIDs }; // print("QQQ dispatcher " + JSON.stringify(_this.runningPluginNames) + " : " + JSON.stringify(_this.activitySlots)); @@ -179,7 +201,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); // TODO sort names by plugin.priority if (controllerDispatcherPlugins.hasOwnProperty(pluginName)) { var candidatePlugin = controllerDispatcherPlugins[pluginName]; - if (_this.slotsAreAvailable(candidatePlugin) && candidatePlugin.isReady(controllerData, deltaTime)) { + if (_this.slotsAreAvailableForPlugin(candidatePlugin) && candidatePlugin.isReady(controllerData, deltaTime)) { // this plugin will start. add it to the list of running plugins and mark the // activity-slots which this plugin consumes as "in use" _this.runningPluginNames[pluginName] = true; diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js new file mode 100644 index 0000000000..4a70b31820 --- /dev/null +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -0,0 +1,167 @@ +"use strict"; + +// nearParentGrabOverlay.js +// +// 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, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, + getControllerJointIndex, NULL_UUID, enableDispatcherModule, disableDispatcherModule, + Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, + makeDispatcherModuleParameters, Overlays +*/ + +Script.include("/~/system/controllers/controllerDispatcherUtils.js"); + +(function() { + + // XXX this.ignoreIK = (grabbableData.ignoreIK !== undefined) ? grabbableData.ignoreIK : true; + // XXX this.kinematicGrab = (grabbableData.kinematic !== undefined) ? grabbableData.kinematic : NEAR_GRABBING_KINEMATIC; + + function NearParentingGrabOverlay(hand) { + this.hand = hand; + this.grabbedThingID = null; + this.previousParentID = {}; + this.previousParentJointIndex = {}; + this.previouslyUnhooked = {}; + + this.parameters = makeDispatcherModuleParameters( + 500, + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100); + + + // XXX does handJointIndex change if the avatar changes? + this.handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); + this.controllerJointIndex = getControllerJointIndex(this.hand); + + this.thisHandIsParent = function(props) { + if (props.parentID !== MyAvatar.sessionUUID && props.parentID !== AVATAR_SELF_ID) { + return false; + } + + var handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); + if (props.parentJointIndex == handJointIndex) { + return true; + } + + var controllerJointIndex = this.controllerJointIndex; + if (props.parentJointIndex == controllerJointIndex) { + return true; + } + + var controllerCRJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? + "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : + "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); + if (props.parentJointIndex == controllerCRJointIndex) { + return true; + } + + return false; + }; + + this.startNearParentingGrabOverlay = function (controllerData) { + Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); + + var handJointIndex; + // if (this.ignoreIK) { + // handJointIndex = this.controllerJointIndex; + // } else { + // handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); + // } + handJointIndex = this.controllerJointIndex; + + var grabbedProperties = { + position: Overlays.getProperty(this.grabbedThingID, "position"), + rotation: Overlays.getProperty(this.grabbedThingID, "rotation"), + parentID: Overlays.getProperty(this.grabbedThingID, "parentID"), + parentJointIndex: Overlays.getProperty(this.grabbedThingID, "parentJointIndex"), + dynamic: false, + shapeType: "none" + }; + + var reparentProps = { + parentID: AVATAR_SELF_ID, + parentJointIndex: handJointIndex, + velocity: {x: 0, y: 0, z: 0}, + angularVelocity: {x: 0, y: 0, z: 0} + }; + + if (this.thisHandIsParent(grabbedProperties)) { + // this should never happen, but if it does, don't set previous parent to be this hand. + // this.previousParentID[this.grabbedThingID] = NULL; + // this.previousParentJointIndex[this.grabbedThingID] = -1; + } else { + this.previousParentID[this.grabbedThingID] = grabbedProperties.parentID; + this.previousParentJointIndex[this.grabbedThingID] = grabbedProperties.parentJointIndex; + } + Overlays.editOverlay(this.grabbedThingID, reparentProps); + + Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ + action: 'grab', + grabbedEntity: this.grabbedThingID, + joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" + })); + }; + + this.endNearParentingGrabOverlay = function (controllerData) { + if (this.previousParentID[this.grabbedThingID] === NULL_UUID) { + Overlays.editOverlay(this.grabbedThingID, { + parentID: NULL_UUID, + parentJointIndex: -1 + }); + } else { + // before we grabbed it, overlay was a child of something; put it back. + Overlays.editOverlay(this.grabbedThingID, { + parentID: this.previousParentID[this.grabbedThingID], + parentJointIndex: this.previousParentJointIndex[this.grabbedThingID], + }); + } + + this.grabbedThingID = null; + }; + + this.isReady = function (controllerData) { + if (controllerData.triggerClicks[this.hand] == 0) { + return false; + } + + this.grabbedThingID = null; + + var candidateOverlays = controllerData.nearbyOverlayIDs[this.hand]; + print("candidateOverlays.length = " + candidateOverlays.length); + var grabbableOverlays = candidateOverlays.filter(function(overlayID) { + return Overlays.getProperty(overlayID, "grabbable"); + }); + print("grabbableOverlays.length = " + grabbableOverlays.length); + + if (grabbableOverlays.length > 0) { + this.grabbedThingID = grabbableOverlays[0]; + this.startNearParentingGrabOverlay(controllerData); + return true; + } else { + return false; + } + }; + + this.run = function (controllerData) { + if (controllerData.triggerClicks[this.hand] == 0) { + this.endNearParentingGrabOverlay(controllerData); + return false; + } else { + return true; + } + }; + } + + enableDispatcherModule("LeftNearParentingGrabOverlay", new NearParentingGrabOverlay(LEFT_HAND)); + enableDispatcherModule("RightNearParentingGrabOverlay", new NearParentingGrabOverlay(RIGHT_HAND)); + + this.cleanup = function () { + disableDispatcherModule("LeftNearParentingGrabOverlay"); + disableDispatcherModule("RightNearParentingGrabOverlay"); + }; + Script.scriptEnding.connect(this.cleanup); +}()); diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 31c464ee49..5a067e44c6 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -20,6 +20,7 @@ var CONTOLLER_SCRIPTS = [ "ControllerDispatcher.js", "controllerModules/nearParentGrabEntity.js", + "controllerModules/nearParentGrabOverlay.js", "controllerModules/nearActionGrabEntity.js", "controllerModules/tabletStylusInput.js" ]; From 505b564c62122f81dda9c50d7b977cc02b7cd01d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 12 Aug 2017 13:14:00 -0700 Subject: [PATCH 08/59] sort plugins isReady calls by plugin priority. hook up pickrays in data-gathering phase --- .../controllers/controllerDispatcher.js | 82 +++++++++++++++---- .../controllers/controllerDispatcherUtils.js | 2 + 2 files changed, 69 insertions(+), 15 deletions(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index d2b6418ee9..e666cd8c5b 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -5,10 +5,14 @@ // 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, Entities, Overlays, controllerDispatcherPlugins, Controller, Vec3, getControllerWorldLocation, +/*jslint bitwise: true */ + +/* global Script, Entities, Overlays, Controller, Vec3, getControllerWorldLocation, RayPick, + controllerDispatcherPlugins, controllerDispatcherPluginsNeedSort, LEFT_HAND, RIGHT_HAND */ controllerDispatcherPlugins = {}; +controllerDispatcherPluginsNeedSort = false; Script.include("/~/system/libraries/utils.js"); Script.include("/~/system/libraries/controllers.js"); @@ -137,9 +141,38 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); this.update = function () { var deltaTime = this.updateTimings(); + if (controllerDispatcherPluginsNeedSort) { + this.orderedPluginNames = []; + for (var pluginName in controllerDispatcherPlugins) { + if (controllerDispatcherPlugins.hasOwnProperty(pluginName)) { + this.orderedPluginNames.push(pluginName); + } + } + this.orderedPluginNames.sort(function (a, b) { + return controllerDispatcherPlugins[a].priority < controllerDispatcherPlugins[b].priority; + }); + controllerDispatcherPluginsNeedSort = false; + } + var controllerLocations = [_this.dataGatherers.leftControllerLocation(), _this.dataGatherers.rightControllerLocation()]; + + // interface/src/raypick/LaserPointerManager.cpp | 62 +++++++++++++-------------- + // interface/src/raypick/LaserPointerManager.h | 13 +++--- + // interface/src/raypick/RayPickManager.cpp | 56 ++++++++++++------------ + // interface/src/raypick/RayPickManager.h | 13 +++--- + + + // raypick for each controller + var rayPicks = [ + RayPick.getPrevRayPickResult(_this.leftControllerRayPick), + RayPick.getPrevRayPickResult(_this.rightControllerRayPick) + ]; + // result.intersects + // result.distance + + // find 3d overlays near each hand var nearbyOverlayIDs = []; var h; @@ -191,22 +224,19 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); triggerClicks: [_this.leftTriggerClicked, _this.rightTriggerClicked], controllerLocations: controllerLocations, nearbyEntityProperties: nearbyEntityProperties, - nearbyOverlayIDs: nearbyOverlayIDs + nearbyOverlayIDs: nearbyOverlayIDs, + rayPicks: rayPicks }; - // print("QQQ dispatcher " + JSON.stringify(_this.runningPluginNames) + " : " + JSON.stringify(_this.activitySlots)); - - // check for plugins that would like to start - for (var pluginName in controllerDispatcherPlugins) { - // TODO sort names by plugin.priority - if (controllerDispatcherPlugins.hasOwnProperty(pluginName)) { - var candidatePlugin = controllerDispatcherPlugins[pluginName]; - if (_this.slotsAreAvailableForPlugin(candidatePlugin) && candidatePlugin.isReady(controllerData, deltaTime)) { - // this plugin will start. add it to the list of running plugins and mark the - // activity-slots which this plugin consumes as "in use" - _this.runningPluginNames[pluginName] = true; - _this.markSlots(candidatePlugin, pluginName); - } + // check for plugins that would like to start. ask in order of increasing priority value + for (var pluginIndex = 0; pluginIndex < this.orderedPluginNames.length; pluginIndex++) { + var orderedPluginName = this.orderedPluginNames[pluginIndex]; + var candidatePlugin = controllerDispatcherPlugins[orderedPluginName]; + if (_this.slotsAreAvailableForPlugin(candidatePlugin) && candidatePlugin.isReady(controllerData, deltaTime)) { + // this plugin will start. add it to the list of running plugins and mark the + // activity-slots which this plugin consumes as "in use" + _this.runningPluginNames[orderedPluginName] = true; + _this.markSlots(candidatePlugin, orderedPluginName); } } @@ -237,9 +267,31 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); mapping.from([Controller.Standard.LTClick]).peek().to(_this.leftTriggerClick); Controller.enableMapping(MAPPING_NAME); + + this.mouseRayPick = RayPick.createRayPick({ + joint: "Mouse", + filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, + enabled: true + }); + + this.leftControllerRayPick = RayPick.createRayPick({ + joint: "Mouse", + filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, + enabled: true + }); + this.rightControllerRayPick = RayPick.createRayPick({ + joint: "Mouse", + filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, + enabled: true + }); + + this.cleanup = function () { Script.update.disconnect(_this.update); Controller.disableMapping(MAPPING_NAME); + // RayPick.removeRayPick(_this.mouseRayPick); + RayPick.removeRayPick(_this.leftControllerRayPick); + RayPick.removeRayPick(_this.rightControllerRayPick); }; Script.scriptEnding.connect(this.cleanup); diff --git a/scripts/system/controllers/controllerDispatcherUtils.js b/scripts/system/controllers/controllerDispatcherUtils.js index 39fcf1ae66..d1ac6a842a 100644 --- a/scripts/system/controllers/controllerDispatcherUtils.js +++ b/scripts/system/controllers/controllerDispatcherUtils.js @@ -57,10 +57,12 @@ enableDispatcherModule = function (moduleName, module, priority) { controllerDispatcherPlugins = {}; } controllerDispatcherPlugins[moduleName] = module; + controllerDispatcherPluginsNeedSort = true; }; disableDispatcherModule = function (moduleName) { delete controllerDispatcherPlugins[moduleName]; + controllerDispatcherPluginsNeedSort = true; }; getGrabbableData = function (props) { From ec93172676e051b76531dedc39737217af06706b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 12 Aug 2017 17:27:48 -0700 Subject: [PATCH 09/59] far-search rays work, but nothing else about far-grab. --- .../controllers/controllerDispatcher.js | 4 + .../controllers/controllerDispatcherUtils.js | 18 ++ .../controllerModules/farActionGrabEntity.js | 213 ++++++++++++++++++ .../nearParentGrabOverlay.js | 2 - .../system/controllers/controllerScripts.js | 1 + 5 files changed, 236 insertions(+), 2 deletions(-) create mode 100644 scripts/system/controllers/controllerModules/farActionGrabEntity.js diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index e666cd8c5b..28252a0888 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -151,6 +151,9 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); this.orderedPluginNames.sort(function (a, b) { return controllerDispatcherPlugins[a].priority < controllerDispatcherPlugins[b].priority; }); + + print("controllerDispatcher: new plugin order: " + JSON.stringify(this.orderedPluginNames)); + controllerDispatcherPluginsNeedSort = false; } @@ -232,6 +235,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); for (var pluginIndex = 0; pluginIndex < this.orderedPluginNames.length; pluginIndex++) { var orderedPluginName = this.orderedPluginNames[pluginIndex]; var candidatePlugin = controllerDispatcherPlugins[orderedPluginName]; + if (_this.slotsAreAvailableForPlugin(candidatePlugin) && candidatePlugin.isReady(controllerData, deltaTime)) { // this plugin will start. add it to the list of running plugins and mark the // activity-slots which this plugin consumes as "in use" diff --git a/scripts/system/controllers/controllerDispatcherUtils.js b/scripts/system/controllers/controllerDispatcherUtils.js index d1ac6a842a..94d58c1c4d 100644 --- a/scripts/system/controllers/controllerDispatcherUtils.js +++ b/scripts/system/controllers/controllerDispatcherUtils.js @@ -9,6 +9,13 @@ /* global Camera, HMD, MyAvatar, controllerDispatcherPlugins, MSECS_PER_SEC, LEFT_HAND, RIGHT_HAND, NULL_UUID, AVATAR_SELF_ID, FORBIDDEN_GRAB_TYPES, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, ZERO_VEC, ONE_VEC, DEFAULT_REGISTRATION_POINT, INCHES_TO_METERS, + TRIGGER_OFF_VALUE, + TRIGGER_ON_VALUE, + PICK_MAX_DISTANCE, + DEFAULT_SEARCH_SPHERE_DISTANCE, + COLORS_GRAB_SEARCHING_HALF_SQUEEZE, + COLORS_GRAB_SEARCHING_FULL_SQUEEZE, + COLORS_GRAB_DISTANCE_HOLD, makeDispatcherModuleParameters, enableDispatcherModule, disableDispatcherModule, @@ -37,6 +44,17 @@ HAPTIC_PULSE_DURATION = 13.0; DEFAULT_REGISTRATION_POINT = { x: 0.5, y: 0.5, z: 0.5 }; +TRIGGER_OFF_VALUE = 0.1; +TRIGGER_ON_VALUE = TRIGGER_OFF_VALUE + 0.05; // Squeezed just enough to activate search or near grab + +PICK_MAX_DISTANCE = 500; // max length of pick-ray +DEFAULT_SEARCH_SPHERE_DISTANCE = 1000; // how far from camera to search intersection? + +COLORS_GRAB_SEARCHING_HALF_SQUEEZE = { red: 10, green: 10, blue: 255 }; +COLORS_GRAB_SEARCHING_FULL_SQUEEZE = { red: 250, green: 10, blue: 10 }; +COLORS_GRAB_DISTANCE_HOLD = { red: 238, green: 75, blue: 214 }; + + // priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step // activitySlots -- indicates which "slots" must not yet be in use for this module to start diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js new file mode 100644 index 0000000000..3d1964d639 --- /dev/null +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -0,0 +1,213 @@ +"use strict"; + +// farActionGrabEntity.js +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +/*jslint bitwise: true */ + +/* global Script, Controller, LaserPointers, RayPick, RIGHT_HAND, LEFT_HAND, + getGrabPointSphereOffset, entityIsGrabbable, + enableDispatcherModule, disableDispatcherModule, + makeDispatcherModuleParameters, + 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, + +*/ + +Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllers.js"); + +(function() { + + var PICK_WITH_HAND_RAY = true; + + 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 + drawInFront: 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, + drawInFront: 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 + drawInFront: 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, + drawInFront: 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 + drawInFront: 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}]; + + + function FarActionGrabEntity(hand) { + this.hand = hand; + this.grabbedThingID = null; + this.actionID = null; // action this script created... + + this.parameters = makeDispatcherModuleParameters( + 550, + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100); + + this.updateLaserPointer = function(controllerData, distanceHolding, distanceRotating) { + var SEARCH_SPHERE_SIZE = 0.011; + var MIN_SPHERE_SIZE = 0.0005; + var radius = Math.max(1.2 * SEARCH_SPHERE_SIZE * this.intersectionDistance, MIN_SPHERE_SIZE); + var dim = {x: radius, y: radius, z: radius}; + var mode = "hold"; + if (!distanceHolding && !distanceRotating) { + // mode = (this.triggerSmoothedGrab() || this.secondarySqueezed()) ? "full" : "half"; + if (controllerData.triggerClicks[this.hand] + // || this.secondarySqueezed() // XXX + ) { + mode = "full"; + } else { + mode = "half"; + } + } + + var laserPointerID = PICK_WITH_HAND_RAY ? this.laserPointer : this.headLaserPointer; + if (mode === "full") { + var fullEndToEdit = PICK_WITH_HAND_RAY ? this.fullEnd : fullEnd; + fullEndToEdit.dimensions = dim; + LaserPointers.editRenderState(laserPointerID, mode, {path: fullPath, end: fullEndToEdit}); + } else if (mode === "half") { + var halfEndToEdit = PICK_WITH_HAND_RAY ? this.halfEnd : halfEnd; + halfEndToEdit.dimensions = dim; + LaserPointers.editRenderState(laserPointerID, mode, {path: halfPath, end: halfEndToEdit}); + } + LaserPointers.enableLaserPointer(laserPointerID); + LaserPointers.setRenderState(laserPointerID, mode); + if (distanceHolding || distanceRotating) { + LaserPointers.setLockEndUUID(laserPointerID, this.grabbedThingID, this.grabbedIsOverlay); + } else { + LaserPointers.setLockEndUUID(laserPointerID, null, false); + } + }; + + this.laserPointerOff = function() { + var laserPointerID = PICK_WITH_HAND_RAY ? this.laserPointer : this.headLaserPointer; + LaserPointers.disableLaserPointer(laserPointerID); + }; + + + this.handToController = function() { + return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + }; + + this.isReady = function (controllerData) { + if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { + this.updateLaserPointer(controllerData, false, false); + return true; + } else { + return false; + } + }; + + this.run = function (controllerData) { + if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE) { + this.laserPointerOff(); + return false; + } + + + // if we are doing a distance search and this controller moves into a position + // where it could near-grab something, stop searching. + var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand]; + for (var i = 0; i < nearbyEntityProperties.length; i++) { + var props = nearbyEntityProperties[i]; + if (entityIsGrabbable(props)) { + this.laserPointerOff(); + return false; + } + } + + // this.updateLaserPointer(controllerData, false, false); + + // var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + // Entities.callEntityMethod(this.grabbedThingID, "continueFarGrab", args); + + return true; + }; + + 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) ? "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", + filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, + maxDistance: PICK_MAX_DISTANCE, + posOffset: getGrabPointSphereOffset(this.handToController()), + renderStates: renderStates, + faceAvatar: true, + defaultRenderStates: defaultRenderStates + }); + } + + var leftFarActionGrabEntity = new FarActionGrabEntity(LEFT_HAND); + var rightFarActionGrabEntity = new FarActionGrabEntity(RIGHT_HAND); + + enableDispatcherModule("LeftFarActionGrabEntity", leftFarActionGrabEntity); + enableDispatcherModule("RightFarActionGrabEntity", rightFarActionGrabEntity); + + this.cleanup = function () { + leftFarActionGrabEntity.cleanup(); + rightFarActionGrabEntity.cleanup(); + disableDispatcherModule("LeftFarActionGrabEntity"); + disableDispatcherModule("RightFarActionGrabEntity"); + }; + Script.scriptEnding.connect(this.cleanup); +}()); diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js index 4a70b31820..9ce0b95abb 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -131,11 +131,9 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); this.grabbedThingID = null; var candidateOverlays = controllerData.nearbyOverlayIDs[this.hand]; - print("candidateOverlays.length = " + candidateOverlays.length); var grabbableOverlays = candidateOverlays.filter(function(overlayID) { return Overlays.getProperty(overlayID, "grabbable"); }); - print("grabbableOverlays.length = " + grabbableOverlays.length); if (grabbableOverlays.length > 0) { this.grabbedThingID = grabbableOverlays[0]; diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 5a067e44c6..552aec20a4 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -22,6 +22,7 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/nearParentGrabEntity.js", "controllerModules/nearParentGrabOverlay.js", "controllerModules/nearActionGrabEntity.js", + "controllerModules/farActionGrabEntity.js", "controllerModules/tabletStylusInput.js" ]; From e7437d66efdcb05cea4d1eb4e9026eb5b8a71f34 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 12 Aug 2017 17:49:02 -0700 Subject: [PATCH 10/59] if a raypick hits something that's very close, put it into the nearby-entities list --- .../controllers/controllerDispatcher.js | 40 ++++++++++--------- .../controllers/controllerDispatcherUtils.js | 5 ++- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 28252a0888..4fc8c84d5f 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -8,8 +8,8 @@ /*jslint bitwise: true */ /* global Script, Entities, Overlays, Controller, Vec3, getControllerWorldLocation, RayPick, - controllerDispatcherPlugins, controllerDispatcherPluginsNeedSort, - LEFT_HAND, RIGHT_HAND */ + controllerDispatcherPlugins, controllerDispatcherPluginsNeedSort, entityIsGrabbable, + LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS */ controllerDispatcherPlugins = {}; controllerDispatcherPluginsNeedSort = false; @@ -160,22 +160,6 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); var controllerLocations = [_this.dataGatherers.leftControllerLocation(), _this.dataGatherers.rightControllerLocation()]; - - // interface/src/raypick/LaserPointerManager.cpp | 62 +++++++++++++-------------- - // interface/src/raypick/LaserPointerManager.h | 13 +++--- - // interface/src/raypick/RayPickManager.cpp | 56 ++++++++++++------------ - // interface/src/raypick/RayPickManager.h | 13 +++--- - - - // raypick for each controller - var rayPicks = [ - RayPick.getPrevRayPickResult(_this.leftControllerRayPick), - RayPick.getPrevRayPickResult(_this.rightControllerRayPick) - ]; - // result.intersects - // result.distance - - // find 3d overlays near each hand var nearbyOverlayIDs = []; var h; @@ -221,6 +205,26 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); nearbyEntityProperties[h].sort(makeSorter(h)); } + // raypick for each controller + var rayPicks = [ + RayPick.getPrevRayPickResult(_this.leftControllerRayPick), + RayPick.getPrevRayPickResult(_this.rightControllerRayPick) + ]; + // if the pickray hit something very nearby, put it into the nearby entities list + for (h = LEFT_HAND; h <= RIGHT_HAND; h++) { + var nearEntityID = rayPicks[h].entityID; + if (nearEntityID) { + // XXX check to make sure this one isn't already in nearbyEntityProperties? + if (rayPicks[h].distance < NEAR_GRAB_PICK_RADIUS) { + var nearbyProps = Entities.getEntityProperties(nearEntityID, DISPATCHER_PROPERTIES); + nearbyProps.id = nearEntityID; + if (entityIsGrabbable(nearbyProps)) { + nearbyEntityProperties[h].push(nearbyProps); + } + } + } + } + // bundle up all the data about the current situation var controllerData = { triggerValues: [_this.leftTriggerValue, _this.rightTriggerValue], diff --git a/scripts/system/controllers/controllerDispatcherUtils.js b/scripts/system/controllers/controllerDispatcherUtils.js index 94d58c1c4d..e2cea96a42 100644 --- a/scripts/system/controllers/controllerDispatcherUtils.js +++ b/scripts/system/controllers/controllerDispatcherUtils.js @@ -13,6 +13,7 @@ TRIGGER_ON_VALUE, PICK_MAX_DISTANCE, DEFAULT_SEARCH_SPHERE_DISTANCE, + NEAR_GRAB_PICK_RADIUS, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, @@ -22,7 +23,8 @@ getGrabbableData, entityIsGrabbable, getControllerJointIndex, - propsArePhysical + propsArePhysical, + controllerDispatcherPluginsNeedSort */ MSECS_PER_SEC = 1000.0; @@ -49,6 +51,7 @@ TRIGGER_ON_VALUE = TRIGGER_OFF_VALUE + 0.05; // Squeezed just enough to activate PICK_MAX_DISTANCE = 500; // max length of pick-ray DEFAULT_SEARCH_SPHERE_DISTANCE = 1000; // how far from camera to search intersection? +NEAR_GRAB_PICK_RADIUS = 0.25; // radius used for search ray vs object for near grabbing. COLORS_GRAB_SEARCHING_HALF_SQUEEZE = { red: 10, green: 10, blue: 255 }; COLORS_GRAB_SEARCHING_FULL_SQUEEZE = { red: 250, green: 10, blue: 10 }; From 327bc23b5c728ebb32deb403cd38d8d039e2d4ce Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 13 Aug 2017 11:41:12 -0700 Subject: [PATCH 11/59] get ready for highlighting --- .../controllers/controllerDispatcher.js | 26 ++-- .../controllers/controllerDispatcherUtils.js | 22 ++- .../controllerModules/farActionGrabEntity.js | 37 +++-- .../controllerModules/nearActionGrabEntity.js | 130 ++++++++++-------- .../controllerModules/nearParentGrabEntity.js | 128 +++++++++++------ .../nearParentGrabOverlay.js | 31 +++-- .../controllerModules/tabletStylusInput.js | 19 ++- 7 files changed, 251 insertions(+), 142 deletions(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 4fc8c84d5f..d79c2bd236 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -240,11 +240,14 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); var orderedPluginName = this.orderedPluginNames[pluginIndex]; var candidatePlugin = controllerDispatcherPlugins[orderedPluginName]; - if (_this.slotsAreAvailableForPlugin(candidatePlugin) && candidatePlugin.isReady(controllerData, deltaTime)) { - // this plugin will start. add it to the list of running plugins and mark the - // activity-slots which this plugin consumes as "in use" - _this.runningPluginNames[orderedPluginName] = true; - _this.markSlots(candidatePlugin, orderedPluginName); + if (_this.slotsAreAvailableForPlugin(candidatePlugin)) { + var readiness = candidatePlugin.isReady(controllerData, deltaTime); + if (readiness.active) { + // this plugin will start. add it to the list of running plugins and mark the + // activity-slots which this plugin consumes as "in use" + _this.runningPluginNames[orderedPluginName] = true; + _this.markSlots(candidatePlugin, orderedPluginName); + } } } @@ -257,11 +260,14 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); // them available. delete _this.runningPluginNames[runningPluginName]; _this.unmarkSlotsForPluginName(runningPluginName); - } else if (!plugin.run(controllerData, deltaTime)) { - // 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); + } else { + var runningness = plugin.run(controllerData, deltaTime); + if (!runningness.active) { + // 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); + } } } } diff --git a/scripts/system/controllers/controllerDispatcherUtils.js b/scripts/system/controllers/controllerDispatcherUtils.js index e2cea96a42..e52c158219 100644 --- a/scripts/system/controllers/controllerDispatcherUtils.js +++ b/scripts/system/controllers/controllerDispatcherUtils.js @@ -18,8 +18,10 @@ COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, makeDispatcherModuleParameters, + makeRunningValues, enableDispatcherModule, disableDispatcherModule, + getEnabledModuleByName, getGrabbableData, entityIsGrabbable, getControllerJointIndex, @@ -61,17 +63,24 @@ COLORS_GRAB_DISTANCE_HOLD = { red: 238, green: 75, blue: 214 }; // priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step // activitySlots -- indicates which "slots" must not yet be in use for this module to start -// requiredDataForStart -- which "situation" parts this module looks at to decide if it will start +// requiredDataForReady -- which "situation" parts this module looks at to decide if it will start // sleepMSBetweenRuns -- how long to wait between calls to this module's "run" method -makeDispatcherModuleParameters = function (priority, activitySlots, requiredDataForStart, sleepMSBetweenRuns) { +makeDispatcherModuleParameters = function (priority, activitySlots, requiredDataForReady, sleepMSBetweenRuns) { return { priority: priority, activitySlots: activitySlots, - requiredDataForStart: requiredDataForStart, + requiredDataForReady: requiredDataForReady, sleepMSBetweenRuns: sleepMSBetweenRuns }; }; +makeRunningValues = function (active, targets, requiredDataForRun) { + return { + active: active, + targets: targets, + requiredDataForRun: requiredDataForRun + }; +}; enableDispatcherModule = function (moduleName, module, priority) { if (!controllerDispatcherPlugins) { @@ -86,6 +95,13 @@ disableDispatcherModule = function (moduleName) { controllerDispatcherPluginsNeedSort = true; }; +getEnabledModuleByName = function (moduleName) { + if (controllerDispatcherPlugins.hasOwnProperty(moduleName)) { + return controllerDispatcherPlugins[moduleName]; + } + return null; +}; + getGrabbableData = function (props) { // look in userData for a "grabbable" key, return the value or some defaults var grabbableData = {}; diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 3d1964d639..6b54b0fe25 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -8,7 +8,7 @@ /*jslint bitwise: true */ /* global Script, Controller, LaserPointers, RayPick, RIGHT_HAND, LEFT_HAND, - getGrabPointSphereOffset, entityIsGrabbable, + getGrabPointSphereOffset, getEnabledModuleByName, makeRunningValues, enableDispatcherModule, disableDispatcherModule, makeDispatcherModuleParameters, PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, @@ -135,8 +135,8 @@ Script.include("/~/system/libraries/controllers.js"); }; this.laserPointerOff = function() { - var laserPointerID = PICK_WITH_HAND_RAY ? this.laserPointer : this.headLaserPointer; - LaserPointers.disableLaserPointer(laserPointerID); + LaserPointers.disableLaserPointer(this.laserPointer); + LaserPointers.disableLaserPointer(this.headLaserPointer); }; @@ -145,38 +145,49 @@ Script.include("/~/system/libraries/controllers.js"); }; this.isReady = function (controllerData) { + if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { this.updateLaserPointer(controllerData, false, false); - return true; + return makeRunningValues(true, [], []); } else { - return false; + return makeRunningValues(false, [], []); } }; this.run = function (controllerData) { if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE) { this.laserPointerOff(); - return false; + return makeRunningValues(false, [], []); } + // gather up the readiness of the near-grab modules + var nearGrabNames = [ + this.hand === RIGHT_HAND ? "RightNearActionGrabEntity" : "LeftNearActionGrabEntity", + this.hand === RIGHT_HAND ? "RightNearParentingGrabEntity" : "LeftNearParentingGrabEntity", + this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay" + ]; + var nearGrabReadiness = []; + for (var i = 0; i < nearGrabNames.length; i++) { + var nearGrabModule = getEnabledModuleByName(nearGrabNames[i]); + var ready = nearGrabModule ? nearGrabModule.isReady(controllerData) : makeRunningValues(false, [], []); + nearGrabReadiness.push(ready); + } // if we are doing a distance search and this controller moves into a position // where it could near-grab something, stop searching. - var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand]; - for (var i = 0; i < nearbyEntityProperties.length; i++) { - var props = nearbyEntityProperties[i]; - if (entityIsGrabbable(props)) { + for (var j = 0; j < nearGrabReadiness.length; j++) { + if (nearGrabReadiness[j].active) { this.laserPointerOff(); - return false; + return makeRunningValues(false, [], []); } } - + // this.updateLaserPointer(controllerData, false, false); // var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; // Entities.callEntityMethod(this.grabbedThingID, "continueFarGrab", args); - return true; + return makeRunningValues(true, [], []); }; this.cleanup = function () { diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index d6b5ffe4f7..8017f457fe 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -8,7 +8,8 @@ /* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, getControllerJointIndex, getGrabbableData, NULL_UUID, enableDispatcherModule, disableDispatcherModule, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, entityIsGrabbable, - Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation, makeDispatcherModuleParameters + Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation, makeDispatcherModuleParameters, makeRunningValues, + TRIGGER_OFF_VALUE */ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); @@ -18,7 +19,7 @@ Script.include("/~/system/libraries/controllers.js"); function NearActionGrabEntity(hand) { this.hand = hand; - this.grabbedThingID = null; + this.targetEntityID = null; this.actionID = null; // action this script created... this.parameters = makeDispatcherModuleParameters( @@ -54,10 +55,10 @@ Script.include("/~/system/libraries/controllers.js"); }; - this.startNearGrabAction = function (controllerData, grabbedProperties) { + this.startNearGrabAction = function (controllerData, targetProps) { Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); - var grabbableData = getGrabbableData(grabbedProperties); + var grabbableData = getGrabbableData(targetProps); this.ignoreIK = grabbableData.ignoreIK; this.kinematicGrab = grabbableData.kinematicGrab; @@ -74,10 +75,10 @@ Script.include("/~/system/libraries/controllers.js"); handPosition = this.getHandPosition(); } - var objectRotation = grabbedProperties.rotation; + var objectRotation = targetProps.rotation; this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); - var currentObjectPosition = grabbedProperties.position; + var currentObjectPosition = targetProps.position; var offset = Vec3.subtract(currentObjectPosition, handPosition); this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); @@ -85,9 +86,9 @@ Script.include("/~/system/libraries/controllers.js"); this.actionTimeout = now + (ACTION_TTL * MSECS_PER_SEC); if (this.actionID) { - Entities.deleteAction(this.grabbedThingID, this.actionID); + Entities.deleteAction(this.targetEntityID, this.actionID); } - this.actionID = Entities.addAction("hold", this.grabbedThingID, { + this.actionID = Entities.addAction("hold", this.targetEntityID, { hand: this.hand === RIGHT_HAND ? "right" : "left", timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, relativePosition: this.offsetPosition, @@ -104,25 +105,17 @@ Script.include("/~/system/libraries/controllers.js"); Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ action: 'grab', - grabbedEntity: this.grabbedThingID, + grabbedEntity: this.targetEntityID, joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" })); }; - // this is for when the action creation failed, before - this.restartNearGrabAction = function (controllerData) { - var props = Entities.getEntityProperties(this.grabbedThingID, ["position", "rotation", "userData"]); - if (props && entityIsGrabbable(props)) { - this.startNearGrabAction(controllerData, props); - } - }; - // this is for when the action is going to time-out this.refreshNearGrabAction = function (controllerData) { var now = Date.now(); if (this.actionID && this.actionTimeout - now < ACTION_TTL_REFRESH * MSECS_PER_SEC) { // if less than a 5 seconds left, refresh the actions ttl - var success = Entities.updateAction(this.grabbedThingID, this.actionID, { + var success = Entities.updateAction(this.targetEntityID, this.actionID, { hand: this.hand === RIGHT_HAND ? "right" : "left", timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, relativePosition: this.offsetPosition, @@ -134,74 +127,101 @@ Script.include("/~/system/libraries/controllers.js"); }); if (success) { this.actionTimeout = now + (ACTION_TTL * MSECS_PER_SEC); - } else { - print("continueNearGrabbing -- updateAction failed"); - this.restartNearGrabAction(controllerData); } } }; - this.endNearGrabAction = function (controllerData) { + this.endNearGrabAction = function () { var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; - Entities.callEntityMethod(this.grabbedThingID, "releaseGrab", args); + Entities.callEntityMethod(this.targetEntityID, "releaseGrab", args); - Entities.deleteAction(this.grabbedThingID, this.actionID); + Entities.deleteAction(this.targetEntityID, this.actionID); this.actionID = null; - this.grabbedThingID = null; + this.targetEntityID = null; }; - this.isReady = function (controllerData) { - if (controllerData.triggerClicks[this.hand] == 0) { - return false; - } - - var grabbedProperties = null; - // nearbyEntityProperties is already sorted by length from controller + this.getTargetProps = function (controllerData) { + // nearbyEntityProperties is already sorted by distance from controller var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand]; for (var i = 0; i < nearbyEntityProperties.length; i++) { var props = nearbyEntityProperties[i]; if (entityIsGrabbable(props)) { - grabbedProperties = props; - break; + return props; } } + return null; + }; - if (grabbedProperties) { - if (!propsArePhysical(grabbedProperties)) { - return false; // let nearParentGrabEntity handle it + this.isReady = function (controllerData) { + this.targetEntityID = null; + + if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE) { + makeRunningValues(false, [], []); + } + + var targetProps = this.getTargetProps(controllerData); + if (targetProps) { + if (!propsArePhysical(targetProps)) { + // XXX make sure no highlights are enabled from this module + return makeRunningValues(false, [], []); // let nearParentGrabEntity handle it + } else { + this.targetEntityID = targetProps.id; + // XXX highlight this.targetEntityID here + return makeRunningValues(true, [this.targetEntityID], []); } - this.grabbedThingID = grabbedProperties.id; - this.startNearGrabAction(controllerData, grabbedProperties); - return true; } else { - return false; + // XXX make sure no highlights are enabled from this module + return makeRunningValues(false, [], []); } }; this.run = function (controllerData) { - if (controllerData.triggerClicks[this.hand] == 0) { - this.endNearGrabAction(controllerData); - return false; + if (this.actionID) { + if (controllerData.triggerClicks[this.hand] == 0) { + this.endNearGrabAction(); + return makeRunningValues(false, [], []); + } + + this.refreshNearGrabAction(controllerData); + + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.targetEntityID, "continueNearGrab", args); + } else { + // still searching / highlighting + var readiness = this.isReady (controllerData); + if (!readiness.active) { + return readiness; + } + if (controllerData.triggerClicks[this.hand] == 1) { + // stop highlighting, switch to grabbing + // XXX stop highlight here + var targetProps = this.getTargetProps(controllerData); + if (targetProps) { + this.startNearGrabAction(controllerData, targetProps); + } + } } - if (!this.actionID) { - this.restartNearGrabAction(controllerData); + return makeRunningValues(true, [this.targetEntityID], []); + }; + + this.cleanup = function () { + if (this.targetEntityID) { + this.endNearGrabAction(); } - - this.refreshNearGrabAction(controllerData); - - var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; - Entities.callEntityMethod(this.grabbedThingID, "continueNearGrab", args); - - return true; }; } - enableDispatcherModule("LeftNearActionGrabEntity", new NearActionGrabEntity(LEFT_HAND)); - enableDispatcherModule("RightNearActionGrabEntity", new NearActionGrabEntity(RIGHT_HAND)); + var leftNearActionGrabEntity = new NearActionGrabEntity(LEFT_HAND); + var rightNearActionGrabEntity = new NearActionGrabEntity(RIGHT_HAND); + + enableDispatcherModule("LeftNearActionGrabEntity", leftNearActionGrabEntity); + enableDispatcherModule("RightNearActionGrabEntity", rightNearActionGrabEntity); this.cleanup = function () { + leftNearActionGrabEntity.cleanup(); + rightNearActionGrabEntity.cleanup(); disableDispatcherModule("LeftNearActionGrabEntity"); disableDispatcherModule("RightNearActionGrabEntity"); }; diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index 58bd3d2dab..1de2fee5ea 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -8,8 +8,8 @@ /* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, getControllerJointIndex, NULL_UUID, enableDispatcherModule, disableDispatcherModule, - propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, - makeDispatcherModuleParameters, entityIsGrabbable + propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, + makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues */ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); @@ -21,7 +21,8 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); function NearParentingGrabEntity(hand) { this.hand = hand; - this.grabbedThingID = null; + this.targetEntityID = null; + this.grabbing = false; this.previousParentID = {}; this.previousParentJointIndex = {}; this.previouslyUnhooked = {}; @@ -62,7 +63,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); return false; }; - this.startNearParentingGrabEntity = function (controllerData, grabbedProperties) { + this.startNearParentingGrabEntity = function (controllerData, targetProps) { Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); var handJointIndex; @@ -74,7 +75,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); handJointIndex = this.controllerJointIndex; var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; - Entities.callEntityMethod(this.grabbedThingID, "startNearGrab", args); + Entities.callEntityMethod(targetProps.id, "startNearGrab", args); var reparentProps = { parentID: AVATAR_SELF_ID, @@ -83,90 +84,127 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); angularVelocity: {x: 0, y: 0, z: 0} }; - if (this.thisHandIsParent(grabbedProperties)) { + if (this.thisHandIsParent(targetProps)) { // this should never happen, but if it does, don't set previous parent to be this hand. - // this.previousParentID[this.grabbedThingID] = NULL; - // this.previousParentJointIndex[this.grabbedThingID] = -1; + // this.previousParentID[targetProps.id] = NULL; + // this.previousParentJointIndex[targetProps.id] = -1; } else { - this.previousParentID[this.grabbedThingID] = grabbedProperties.parentID; - this.previousParentJointIndex[this.grabbedThingID] = grabbedProperties.parentJointIndex; + this.previousParentID[targetProps.id] = targetProps.parentID; + this.previousParentJointIndex[targetProps.id] = targetProps.parentJointIndex; } - Entities.editEntity(this.grabbedThingID, reparentProps); + Entities.editEntity(targetProps.id, reparentProps); Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ action: 'grab', - grabbedEntity: this.grabbedThingID, + grabbedEntity: targetProps.id, joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" })); }; - this.endNearParentingGrabEntity = function (controllerData) { - if (this.previousParentID[this.grabbedThingID] === NULL_UUID) { - Entities.editEntity(this.grabbedThingID, { - parentID: this.previousParentID[this.grabbedThingID], - parentJointIndex: this.previousParentJointIndex[this.grabbedThingID] + this.endNearParentingGrabEntity = function () { + if (this.previousParentID[this.targetEntityID] === NULL_UUID) { + Entities.editEntity(this.targetEntityID, { + parentID: this.previousParentID[this.targetEntityID], + parentJointIndex: this.previousParentJointIndex[this.targetEntityID] }); } else { // we're putting this back as a child of some other parent, so zero its velocity - Entities.editEntity(this.grabbedThingID, { - parentID: this.previousParentID[this.grabbedThingID], - parentJointIndex: this.previousParentJointIndex[this.grabbedThingID], + Entities.editEntity(this.targetEntityID, { + parentID: this.previousParentID[this.targetEntityID], + parentJointIndex: this.previousParentJointIndex[this.targetEntityID], velocity: {x: 0, y: 0, z: 0}, angularVelocity: {x: 0, y: 0, z: 0} }); } var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; - Entities.callEntityMethod(this.grabbedThingID, "releaseGrab", args); + Entities.callEntityMethod(this.targetEntityID, "releaseGrab", args); - this.grabbedThingID = null; + this.grabbing = false; + this.targetEntityID = null; }; - this.isReady = function (controllerData) { - if (controllerData.triggerClicks[this.hand] == 0) { - return false; - } - - var grabbedProperties = null; + this.getTargetProps = function (controllerData) { // nearbyEntityProperties is already sorted by length from controller var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand]; for (var i = 0; i < nearbyEntityProperties.length; i++) { var props = nearbyEntityProperties[i]; if (entityIsGrabbable(props)) { - grabbedProperties = props; - break; + return props; } } + return null; + }; - if (grabbedProperties) { - if (propsArePhysical(grabbedProperties)) { - return false; // let nearActionGrabEntity handle it + this.isReady = function (controllerData) { + this.targetEntityID = null; + this.grabbing = false; + + if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE) { + makeRunningValues(false, [], []); + } + + var targetProps = this.getTargetProps(controllerData); + if (targetProps) { + if (propsArePhysical(targetProps)) { + // XXX make sure no highlights are enabled from this module + return makeRunningValues(false, [], []); // let nearActionGrabEntity handle it + } else { + this.targetEntityID = targetProps.id; + // XXX highlight this.targetEntityID here + return makeRunningValues(true, [this.targetEntityID], []); } - this.grabbedThingID = grabbedProperties.id; - this.startNearParentingGrabEntity(controllerData, grabbedProperties); - return true; } else { - return false; + // XXX make sure no highlights are enabled from this module + return makeRunningValues(false, [], []); } }; this.run = function (controllerData) { - if (controllerData.triggerClicks[this.hand] == 0) { - this.endNearParentingGrabEntity(controllerData); - return false; + if (this.grabbing) { + if (controllerData.triggerClicks[this.hand] == 0) { + this.endNearParentingGrabEntity(); + return makeRunningValues(false, [], []); + } + + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.targetEntityID, "continueNearGrab", args); + } else { + // still searching / highlighting + var readiness = this.isReady (controllerData); + if (!readiness.active) { + return readiness; + } + if (controllerData.triggerClicks[this.hand] == 1) { + // stop highlighting, switch to grabbing + // XXX stop highlight here + var targetProps = this.getTargetProps(controllerData); + if (targetProps) { + this.grabbing = true; + this.startNearParentingGrabEntity(controllerData, targetProps); + } + } } - var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; - Entities.callEntityMethod(this.grabbedThingID, "continueNearGrab", args); + return makeRunningValues(true, [this.targetEntityID], []); + }; - return true; + this.cleanup = function () { + if (this.targetEntityID) { + this.endNearParentingGrabEntity(); + } }; } - enableDispatcherModule("LeftNearParentingGrabEntity", new NearParentingGrabEntity(LEFT_HAND)); - enableDispatcherModule("RightNearParentingGrabEntity", new NearParentingGrabEntity(RIGHT_HAND)); + var leftNearParentingGrabEntity = new NearParentingGrabEntity(LEFT_HAND); + var rightNearParentingGrabEntity = new NearParentingGrabEntity(RIGHT_HAND); + + enableDispatcherModule("LeftNearParentingGrabEntity", leftNearParentingGrabEntity); + enableDispatcherModule("RightNearParentingGrabEntity", rightNearParentingGrabEntity); this.cleanup = function () { + leftNearParentingGrabEntity.cleanup(); + rightNearParentingGrabEntity.cleanup(); disableDispatcherModule("LeftNearParentingGrabEntity"); disableDispatcherModule("RightNearParentingGrabEntity"); }; diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js index 9ce0b95abb..58b6a12090 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -9,7 +9,7 @@ /* global Script, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, getControllerJointIndex, NULL_UUID, enableDispatcherModule, disableDispatcherModule, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, - makeDispatcherModuleParameters, Overlays + makeDispatcherModuleParameters, Overlays, makeRunningValues */ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); @@ -106,7 +106,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); })); }; - this.endNearParentingGrabOverlay = function (controllerData) { + this.endNearParentingGrabOverlay = function () { if (this.previousParentID[this.grabbedThingID] === NULL_UUID) { Overlays.editOverlay(this.grabbedThingID, { parentID: NULL_UUID, @@ -125,7 +125,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); this.isReady = function (controllerData) { if (controllerData.triggerClicks[this.hand] == 0) { - return false; + return makeRunningValues(false, [], []); } this.grabbedThingID = null; @@ -138,26 +138,37 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); if (grabbableOverlays.length > 0) { this.grabbedThingID = grabbableOverlays[0]; this.startNearParentingGrabOverlay(controllerData); - return true; + return makeRunningValues(true, [this.grabbedThingID], []); } else { - return false; + return makeRunningValues(false, [], []); } }; this.run = function (controllerData) { if (controllerData.triggerClicks[this.hand] == 0) { - this.endNearParentingGrabOverlay(controllerData); - return false; + this.endNearParentingGrabOverlay(); + return makeRunningValues(false, [], []); } else { - return true; + return makeRunningValues(true, [this.grabbedThingID], []); + } + }; + + this.cleanup = function () { + if (this.grabbedThingID) { + this.endNearParentingGrabOverlay(); } }; } - enableDispatcherModule("LeftNearParentingGrabOverlay", new NearParentingGrabOverlay(LEFT_HAND)); - enableDispatcherModule("RightNearParentingGrabOverlay", new NearParentingGrabOverlay(RIGHT_HAND)); + var leftNearParentingGrabOverlay = new NearParentingGrabOverlay(LEFT_HAND); + var rightNearParentingGrabOverlay = new NearParentingGrabOverlay(RIGHT_HAND); + + enableDispatcherModule("LeftNearParentingGrabOverlay", leftNearParentingGrabOverlay); + enableDispatcherModule("RightNearParentingGrabOverlay", rightNearParentingGrabOverlay); this.cleanup = function () { + leftNearParentingGrabOverlay.cleanup(); + rightNearParentingGrabOverlay.cleanup(); disableDispatcherModule("LeftNearParentingGrabOverlay"); disableDispatcherModule("RightNearParentingGrabOverlay"); }; diff --git a/scripts/system/controllers/controllerModules/tabletStylusInput.js b/scripts/system/controllers/controllerModules/tabletStylusInput.js index 7e9f69959f..8ada1b31d7 100644 --- a/scripts/system/controllers/controllerModules/tabletStylusInput.js +++ b/scripts/system/controllers/controllerModules/tabletStylusInput.js @@ -6,7 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html /* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, - NULL_UUID, enableDispatcherModule, disableDispatcherModule, + NULL_UUID, enableDispatcherModule, disableDispatcherModule, makeRunningValues, Messages, Quat, Vec3, getControllerWorldLocation, makeDispatcherModuleParameters, Overlays, ZERO_VEC, AVATAR_SELF_ID, HMD, INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, Settings, getGrabPointSphereOffset */ @@ -643,7 +643,11 @@ Script.include("/~/system/libraries/controllers.js"); }; this.isReady = function (controllerData) { - return this.processStylus(controllerData); + if (this.processStylus(controllerData)) { + return makeRunningValues(true, [], []); + } else { + return makeRunningValues(false, [], []); + } }; this.run = function (controllerData, deltaTime) { @@ -660,7 +664,11 @@ Script.include("/~/system/libraries/controllers.js"); if (this.stylusTouchingTarget) { this.stylusTouching(controllerData, deltaTime); } - return this.processStylus(controllerData); + if (this.processStylus(controllerData)) { + return makeRunningValues(true, [], []); + } else { + return makeRunningValues(false, [], []); + } }; this.cleanup = function () { @@ -674,12 +682,11 @@ Script.include("/~/system/libraries/controllers.js"); enableDispatcherModule("LeftTabletStylusInput", leftTabletStylusInput); enableDispatcherModule("RightTabletStylusInput", rightTabletStylusInput); - this.cleanup = function () { - disableDispatcherModule("LeftTabletStylusInput"); - disableDispatcherModule("RightTabletStylusInput"); leftTabletStylusInput.cleanup(); rightTabletStylusInput.cleanup(); + disableDispatcherModule("LeftTabletStylusInput"); + disableDispatcherModule("RightTabletStylusInput"); }; Script.scriptEnding.connect(this.cleanup); }()); From c2861d29d2227a7775311ad21c4ae30eec808a26 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 13 Aug 2017 13:49:17 -0700 Subject: [PATCH 12/59] trying to get highlighting to work --- .../controllers/controllerDispatcher.js | 10 ++++- .../controllers/controllerDispatcherUtils.js | 43 ++++++++++++++++++- .../controllerModules/nearActionGrabEntity.js | 33 +++++++++++--- 3 files changed, 78 insertions(+), 8 deletions(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index d79c2bd236..06853fa4ba 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -7,7 +7,7 @@ /*jslint bitwise: true */ -/* global Script, Entities, Overlays, Controller, Vec3, getControllerWorldLocation, RayPick, +/* global Script, Entities, Overlays, Controller, Vec3, Quat, getControllerWorldLocation, RayPick, controllerDispatcherPlugins, controllerDispatcherPluginsNeedSort, entityIsGrabbable, LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS */ @@ -212,6 +212,14 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); ]; // if the pickray hit something very nearby, put it into the nearby entities list for (h = LEFT_HAND; h <= RIGHT_HAND; h++) { + + // XXX find a way to extract searchRay from samuel's stuff + rayPicks[h].searchRay = { + origin: controllerLocations[h].position, + direction: Quat.getUp(controllerLocations[h].orientation), + length: 1000 + }; + var nearEntityID = rayPicks[h].entityID; if (nearEntityID) { // XXX check to make sure this one isn't already in nearbyEntityProperties? diff --git a/scripts/system/controllers/controllerDispatcherUtils.js b/scripts/system/controllers/controllerDispatcherUtils.js index e52c158219..cdf8729d9c 100644 --- a/scripts/system/controllers/controllerDispatcherUtils.js +++ b/scripts/system/controllers/controllerDispatcherUtils.js @@ -26,7 +26,10 @@ entityIsGrabbable, getControllerJointIndex, propsArePhysical, - controllerDispatcherPluginsNeedSort + controllerDispatcherPluginsNeedSort, + projectOntoXYPlane, + projectOntoEntityXYPlane, + projectOntoOverlayXYPlane */ MSECS_PER_SEC = 1000.0; @@ -162,3 +165,41 @@ propsArePhysical = function (props) { var isPhysical = (props.shapeType && props.shapeType != 'none'); return isPhysical; }; + +projectOntoXYPlane = function (worldPos, position, rotation, dimensions, registrationPoint) { + var invRot = Quat.inverse(rotation); + var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(worldPos, position)); + var invDimensions = { x: 1 / dimensions.x, + y: 1 / dimensions.y, + z: 1 / dimensions.z }; + var normalizedPos = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), registrationPoint); + return { x: normalizedPos.x * dimensions.x, + y: (1 - normalizedPos.y) * dimensions.y }; // flip y-axis +} + +projectOntoEntityXYPlane = function (entityID, worldPos, props) { + return projectOntoXYPlane(worldPos, props.position, props.rotation, props.dimensions, props.registrationPoint); +} + +projectOntoOverlayXYPlane = function projectOntoOverlayXYPlane(overlayID, worldPos) { + var position = Overlays.getProperty(overlayID, "position"); + var rotation = Overlays.getProperty(overlayID, "rotation"); + var dimensions; + + var dpi = Overlays.getProperty(overlayID, "dpi"); + if (dpi) { + // Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property is used as a scale. + var resolution = Overlays.getProperty(overlayID, "resolution"); + resolution.z = 1; // Circumvent divide-by-zero. + var scale = Overlays.getProperty(overlayID, "dimensions"); + scale.z = 0.01; // overlay dimensions are 2D, not 3D. + dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale); + } else { + dimensions = Overlays.getProperty(overlayID, "dimensions"); + if (dimensions.z) { + dimensions.z = 0.01; // overlay dimensions are 2D, not 3D. + } + } + + return projectOntoXYPlane(worldPos, position, rotation, dimensions, DEFAULT_REGISTRATION_POINT); +} diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index 8017f457fe..42859f3e19 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -167,6 +167,8 @@ Script.include("/~/system/libraries/controllers.js"); return makeRunningValues(false, [], []); // let nearParentGrabEntity handle it } else { this.targetEntityID = targetProps.id; + ContextOverlay.entityWithContextOverlay = this.targetEntityID; + ContextOverlay.enabled = true; // XXX highlight this.targetEntityID here return makeRunningValues(true, [this.targetEntityID], []); } @@ -188,16 +190,35 @@ Script.include("/~/system/libraries/controllers.js"); var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.targetEntityID, "continueNearGrab", args); } else { - // still searching / highlighting + + // still searching / highlighting var readiness = this.isReady (controllerData); if (!readiness.active) { return readiness; } - if (controllerData.triggerClicks[this.hand] == 1) { - // stop highlighting, switch to grabbing - // XXX stop highlight here - var targetProps = this.getTargetProps(controllerData); - if (targetProps) { + + var targetProps = this.getTargetProps(controllerData); + if (targetProps) { + + // XXX + var rayPickInfo = controllerData.rayPicks[this.hand]; + var pointerEvent = { + type: "Move", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection, targetProps), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "Secondary" + }; + if (ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.entityID, pointerEvent)) { + } + // XXX + + + if (controllerData.triggerClicks[this.hand] == 1) { + // stop highlighting, switch to grabbing + // XXX stop highlight here this.startNearGrabAction(controllerData, targetProps); } } From 58b199e9f9f06a9cb52b67fb0a788eadc11d3fe4 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 13 Aug 2017 15:54:03 -0700 Subject: [PATCH 13/59] far-grab sort-of works --- .../controllers/controllerDispatcher.js | 13 +- .../controllers/controllerDispatcherUtils.js | 35 ++- .../controllerModules/farActionGrabEntity.js | 261 ++++++++++++++++-- 3 files changed, 280 insertions(+), 29 deletions(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 06853fa4ba..3398d535f4 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -9,7 +9,8 @@ /* global Script, Entities, Overlays, Controller, Vec3, Quat, getControllerWorldLocation, RayPick, controllerDispatcherPlugins, controllerDispatcherPluginsNeedSort, entityIsGrabbable, - LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS */ + LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS, DEFAULT_SEARCH_SPHERE_DISTANCE +*/ controllerDispatcherPlugins = {}; controllerDispatcherPluginsNeedSort = false; @@ -297,14 +298,16 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); }); this.leftControllerRayPick = RayPick.createRayPick({ - joint: "Mouse", + joint: "_CONTROLLER_LEFTHAND", filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, - enabled: true + enabled: true, + maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE }); this.rightControllerRayPick = RayPick.createRayPick({ - joint: "Mouse", + joint: "_CONTROLLER_RIGHTHAND", filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, - enabled: true + enabled: true, + maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE }); diff --git a/scripts/system/controllers/controllerDispatcherUtils.js b/scripts/system/controllers/controllerDispatcherUtils.js index cdf8729d9c..1c9edd6002 100644 --- a/scripts/system/controllers/controllerDispatcherUtils.js +++ b/scripts/system/controllers/controllerDispatcherUtils.js @@ -6,7 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Camera, HMD, MyAvatar, controllerDispatcherPlugins, +/* global Camera, HMD, MyAvatar, controllerDispatcherPlugins, Quat, Vec3, Overlays, MSECS_PER_SEC, LEFT_HAND, RIGHT_HAND, NULL_UUID, AVATAR_SELF_ID, FORBIDDEN_GRAB_TYPES, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, ZERO_VEC, ONE_VEC, DEFAULT_REGISTRATION_POINT, INCHES_TO_METERS, TRIGGER_OFF_VALUE, @@ -24,6 +24,7 @@ getEnabledModuleByName, getGrabbableData, entityIsGrabbable, + entityIsDistanceGrabbable, getControllerJointIndex, propsArePhysical, controllerDispatcherPluginsNeedSort, @@ -139,6 +140,32 @@ entityIsGrabbable = function (props) { return true; }; +entityIsDistanceGrabbable = function(props) { + if (!entityIsGrabbable(props)) { + return false; + } + + // we can't distance-grab non-physical + var isPhysical = propsArePhysical(props); + if (!isPhysical) { + return false; + } + + // XXX + // var distance = Vec3.distance(props.position, handPosition); + // this.otherGrabbingUUID = entityIsGrabbedByOther(entityID); + // if (this.otherGrabbingUUID !== null) { + // // don't distance grab something that is already grabbed. + // if (debug) { + // print("distance grab is skipping '" + props.name + "': already grabbed by another."); + // } + // return false; + // } + + return true; +}; + + getControllerJointIndex = function (hand) { if (HMD.isHandControllerAvailable()) { var controllerJointIndex = -1; @@ -175,11 +202,11 @@ projectOntoXYPlane = function (worldPos, position, rotation, dimensions, registr var normalizedPos = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), registrationPoint); return { x: normalizedPos.x * dimensions.x, y: (1 - normalizedPos.y) * dimensions.y }; // flip y-axis -} +}; projectOntoEntityXYPlane = function (entityID, worldPos, props) { return projectOntoXYPlane(worldPos, props.position, props.rotation, props.dimensions, props.registrationPoint); -} +}; projectOntoOverlayXYPlane = function projectOntoOverlayXYPlane(overlayID, worldPos) { var position = Overlays.getProperty(overlayID, "position"); @@ -202,4 +229,4 @@ projectOntoOverlayXYPlane = function projectOntoOverlayXYPlane(overlayID, worldP } return projectOntoXYPlane(worldPos, position, rotation, dimensions, DEFAULT_REGISTRATION_POINT); -} +}; diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 6b54b0fe25..0664c8eee3 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -7,10 +7,10 @@ /*jslint bitwise: true */ -/* global Script, Controller, LaserPointers, RayPick, RIGHT_HAND, LEFT_HAND, - getGrabPointSphereOffset, getEnabledModuleByName, makeRunningValues, - enableDispatcherModule, disableDispatcherModule, - makeDispatcherModuleParameters, +/* global Script, Controller, LaserPointers, RayPick, RIGHT_HAND, LEFT_HAND, Mat4, MyAvatar, Vec3, Camera, Quat, + getGrabPointSphereOffset, getEnabledModuleByName, makeRunningValues, Entities, NULL_UUID, + enableDispatcherModule, disableDispatcherModule, entityIsDistanceGrabbable, + 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, @@ -92,19 +92,26 @@ Script.include("/~/system/libraries/controllers.js"); this.grabbedThingID = null; this.actionID = null; // action this script created... + var ACTION_TTL = 15; // seconds + + var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object + var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position + var DISTANCE_HOLDING_UNITY_MASS = 1200; // The mass at which the distance holding action timeframe is unmodified + var DISTANCE_HOLDING_UNITY_DISTANCE = 6; // The distance at which the distance holding action timeframe is unmodified + this.parameters = makeDispatcherModuleParameters( 550, this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], [], 100); - this.updateLaserPointer = function(controllerData, distanceHolding, distanceRotating) { + this.updateLaserPointer = function(controllerData) { var SEARCH_SPHERE_SIZE = 0.011; var MIN_SPHERE_SIZE = 0.0005; var radius = Math.max(1.2 * SEARCH_SPHERE_SIZE * this.intersectionDistance, MIN_SPHERE_SIZE); var dim = {x: radius, y: radius, z: radius}; var mode = "hold"; - if (!distanceHolding && !distanceRotating) { + if (!this.distanceHolding && !this.distanceRotating) { // mode = (this.triggerSmoothedGrab() || this.secondarySqueezed()) ? "full" : "half"; if (controllerData.triggerClicks[this.hand] // || this.secondarySqueezed() // XXX @@ -127,7 +134,7 @@ Script.include("/~/system/libraries/controllers.js"); } LaserPointers.enableLaserPointer(laserPointerID); LaserPointers.setRenderState(laserPointerID, mode); - if (distanceHolding || distanceRotating) { + if (this.distanceHolding || this.distanceRotating) { LaserPointers.setLockEndUUID(laserPointerID, this.grabbedThingID, this.grabbedIsOverlay); } else { LaserPointers.setLockEndUUID(laserPointerID, null, false); @@ -144,10 +151,194 @@ Script.include("/~/system/libraries/controllers.js"); return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; }; + this.distanceGrabTimescale = function(mass, distance) { + var timeScale = DISTANCE_HOLDING_ACTION_TIMEFRAME * mass / + DISTANCE_HOLDING_UNITY_MASS * distance / + DISTANCE_HOLDING_UNITY_DISTANCE; + if (timeScale < DISTANCE_HOLDING_ACTION_TIMEFRAME) { + timeScale = DISTANCE_HOLDING_ACTION_TIMEFRAME; + } + return timeScale; + }; + + this.getMass = function(dimensions, density) { + return (dimensions.x * dimensions.y * dimensions.z) * density; + }; + + this.startFarGrabAction = function (controllerData, grabbedProperties) { + var controllerLocation = controllerData.controllerLocations[this.hand]; + var worldControllerPosition = controllerLocation.position; + var worldControllerRotation = controllerLocation.orientation; + + // transform the position into room space + var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix()); + var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition); + + var now = Date.now(); + + // add the action and initialize some variables + this.currentObjectPosition = grabbedProperties.position; + this.currentObjectRotation = grabbedProperties.rotation; + this.currentObjectTime = now; + this.currentCameraOrientation = Camera.orientation; + + this.grabRadius = this.grabbedDistance; + this.grabRadialVelocity = 0.0; + + // offset between controller vector at the grab radius and the entity position + var targetPosition = Vec3.multiply(this.grabRadius, Quat.getUp(worldControllerRotation)); + targetPosition = Vec3.sum(targetPosition, worldControllerPosition); + this.offsetPosition = Vec3.subtract(this.currentObjectPosition, targetPosition); + + // compute a constant based on the initial conditions which we use below to exaggerate hand motion + // onto the held object + this.radiusScalar = Math.log(this.grabRadius + 1.0); + if (this.radiusScalar < 1.0) { + this.radiusScalar = 1.0; + } + + // compute the mass for the purpose of energy and how quickly to move object + this.mass = this.getMass(grabbedProperties.dimensions, grabbedProperties.density); + var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, grabbedProperties.position)); + var timeScale = this.distanceGrabTimescale(this.mass, distanceToObject); + this.linearTimeScale = timeScale; + this.actionID = NULL_UUID; + this.actionID = Entities.addAction("far-grab", this.grabbedThingID, { + targetPosition: this.currentObjectPosition, + linearTimeScale: timeScale, + targetRotation: this.currentObjectRotation, + angularTimeScale: timeScale, + tag: "far-grab-" + MyAvatar.sessionUUID, + ttl: ACTION_TTL + }); + if (this.actionID === NULL_UUID) { + this.actionID = null; + } + + // XXX + // if (this.actionID !== null) { + // this.callEntityMethodOnGrabbed("startDistanceGrab"); + // } + + Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); + this.turnOffVisualizations(); + this.previousRoomControllerPosition = roomControllerPosition; + }; + + this.continueDistanceHolding = function(controllerData) { + + var controllerLocation = controllerData.controllerLocations[this.hand]; + var worldControllerPosition = controllerLocation.position; + var worldControllerRotation = controllerLocation.orientation; + + // also transform the position into room space + var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix()); + var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition); + + var grabbedProperties = Entities.getEntityProperties(this.grabbedThingID, ["position"]); + var now = Date.now(); + var deltaObjectTime = (now - this.currentObjectTime) / MSECS_PER_SEC; // convert to seconds + this.currentObjectTime = now; + + // the action was set up when this.distanceHolding was called. update the targets. + var radius = Vec3.distance(this.currentObjectPosition, worldControllerPosition) * + this.radiusScalar * DISTANCE_HOLDING_RADIUS_FACTOR; + if (radius < 1.0) { + radius = 1.0; + } + + var roomHandDelta = Vec3.subtract(roomControllerPosition, this.previousRoomControllerPosition); + var worldHandDelta = Mat4.transformVector(MyAvatar.getSensorToWorldMatrix(), roomHandDelta); + var handMoved = Vec3.multiply(worldHandDelta, radius); + this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, handMoved); + + // XXX + // this.callEntityMethodOnGrabbed("continueDistantGrab"); + + // Update radialVelocity + var lastVelocity = Vec3.multiply(worldHandDelta, 1.0 / deltaObjectTime); + var delta = Vec3.normalize(Vec3.subtract(grabbedProperties.position, worldControllerPosition)); + var newRadialVelocity = Vec3.dot(lastVelocity, delta); + + var VELOCITY_AVERAGING_TIME = 0.016; + var blendFactor = deltaObjectTime / VELOCITY_AVERAGING_TIME; + if (blendFactor < 0.0) { + blendFactor = 0.0; + } else if (blendFactor > 1.0) { + blendFactor = 1.0; + } + this.grabRadialVelocity = blendFactor * newRadialVelocity + (1.0 - blendFactor) * this.grabRadialVelocity; + + var RADIAL_GRAB_AMPLIFIER = 10.0; + if (Math.abs(this.grabRadialVelocity) > 0.0) { + this.grabRadius = this.grabRadius + (this.grabRadialVelocity * deltaObjectTime * + this.grabRadius * RADIAL_GRAB_AMPLIFIER); + } + + // don't let grabRadius go all the way to zero, because it can't come back from that + var MINIMUM_GRAB_RADIUS = 0.1; + if (this.grabRadius < MINIMUM_GRAB_RADIUS) { + this.grabRadius = MINIMUM_GRAB_RADIUS; + } + var newTargetPosition = Vec3.multiply(this.grabRadius, Quat.getUp(worldControllerRotation)); + newTargetPosition = Vec3.sum(newTargetPosition, worldControllerPosition); + newTargetPosition = Vec3.sum(newTargetPosition, this.offsetPosition); + + // XXX + // this.maybeScale(grabbedProperties); + + // visualizations + this.updateLaserPointer(controllerData); + + var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, this.currentObjectPosition)); + + this.linearTimeScale = (this.linearTimeScale / 2); + if (this.linearTimeScale <= DISTANCE_HOLDING_ACTION_TIMEFRAME) { + this.linearTimeScale = DISTANCE_HOLDING_ACTION_TIMEFRAME; + } + var success = Entities.updateAction(this.grabbedThingID, this.actionID, { + targetPosition: newTargetPosition, + linearTimeScale: this.linearTimeScale, + targetRotation: this.currentObjectRotation, + angularTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject), + ttl: ACTION_TTL + }); + if (!success) { + print("continueDistanceHolding -- updateAction failed: " + this.actionID); + this.actionID = null; + } + + this.previousRoomControllerPosition = roomControllerPosition; + }; + + this.ensureDynamic = function () { + // if we distance hold something and keep it very still before releasing it, it ends up + // non-dynamic in bullet. If it's too still, give it a little bounce so it will fall. + var props = Entities.getEntityProperties(this.grabbedThingID, ["velocity", "dynamic", "parentID"]); + if (props.dynamic && props.parentID == NULL_UUID) { + var velocity = props.velocity; + if (Vec3.length(velocity) < 0.05) { // see EntityMotionState.cpp DYNAMIC_LINEAR_VELOCITY_THRESHOLD + velocity = { x: 0.0, y: 0.2, z: 0.0 }; + Entities.editEntity(this.grabbedThingID, { velocity: velocity }); + } + } + }; + + this.endNearGrabAction = function () { + this.ensureDynamic(); + this.distanceHolding = false; + this.distanceRotating = false; + Entities.deleteAction(this.grabbedThingID, this.actionID); + this.actionID = null; + this.grabbedThingID = null; + }; + this.isReady = function (controllerData) { + this.distanceHolding = false; + this.distanceRotating = false; if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { - this.updateLaserPointer(controllerData, false, false); + this.updateLaserPointer(controllerData); return makeRunningValues(true, [], []); } else { return makeRunningValues(false, [], []); @@ -156,6 +347,7 @@ Script.include("/~/system/libraries/controllers.js"); this.run = function (controllerData) { if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE) { + this.endNearGrabAction(); this.laserPointerOff(); return makeRunningValues(false, [], []); } @@ -173,20 +365,49 @@ Script.include("/~/system/libraries/controllers.js"); nearGrabReadiness.push(ready); } - // if we are doing a distance search and this controller moves into a position - // where it could near-grab something, stop searching. - for (var j = 0; j < nearGrabReadiness.length; j++) { - if (nearGrabReadiness[j].active) { - this.laserPointerOff(); - return makeRunningValues(false, [], []); + if (this.actionID) { + this.continueDistanceHolding(controllerData); + // this.updateLaserPointer(controllerData, false, false); + + // var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + // Entities.callEntityMethod(this.grabbedThingID, "continueFarGrab", args); + } else { + var rayPickInfo = controllerData.rayPicks[this.hand]; + if (rayPickInfo.type == RayPick.INTERSECTED_ENTITY) { + var entityID = rayPickInfo.objectID; + print("QQQ rayPickInfo.entityID = " + entityID); + var targetProps = Entities.getEntityProperties(entityID, ["dynamic", "shapeType", "position", + "rotation", "dimensions", "density", + "userData", "locked", "type"]); + if (entityIsDistanceGrabbable(targetProps)) { + print("QQQ is distance grabbable"); + this.grabbedThingID = entityID; + this.grabbedDistance = rayPickInfo.distance; + var otherModuleName = this.hand == RIGHT_HAND ? "LeftFarActionGrabEntity" : "RightFarActionGrabEntity"; + var otherFarGrabModule = getEnabledModuleByName(otherModuleName); + if (otherFarGrabModule.grabbedThingID == this.grabbedThingID) { + this.distanceRotating = true; + this.distanceHolding = false; + // XXX rotate + } else { + this.distanceHolding = true; + this.distanceRotating = false; + this.startFarGrabAction(controllerData, targetProps); + } + } else { + print("QQQ is NOT distance grabbable"); + } + } + + // if we are doing a distance search and this controller moves into a position + // where it could near-grab something, stop searching. + for (var j = 0; j < nearGrabReadiness.length; j++) { + if (nearGrabReadiness[j].active) { + this.laserPointerOff(); + return makeRunningValues(false, [], []); + } } } - - // this.updateLaserPointer(controllerData, false, false); - - // var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; - // Entities.callEntityMethod(this.grabbedThingID, "continueFarGrab", args); - return makeRunningValues(true, [], []); }; From f10dc7cbd592e061a66be33a2dab5d22ee210545 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 13 Aug 2017 16:54:37 -0700 Subject: [PATCH 14/59] allow far to near-grab transition --- .../controllerModules/farActionGrabEntity.js | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 0664c8eee3..448f21a592 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -366,21 +366,38 @@ Script.include("/~/system/libraries/controllers.js"); } if (this.actionID) { + // if we are doing a distance grab and the object gets close enough to the controller, + // stop the far-grab so the near-grab or equip can take over. + for (var k = 0; k < nearGrabReadiness.length; k++) { + if (nearGrabReadiness[k].active && nearGrabReadiness[k].targets[0] == this.grabbedThingID) { + this.laserPointerOff(); + this.endNearGrabAction(); + return makeRunningValues(false, [], []); + } + } + this.continueDistanceHolding(controllerData); // this.updateLaserPointer(controllerData, false, false); // var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; // Entities.callEntityMethod(this.grabbedThingID, "continueFarGrab", args); } else { + // if we are doing a distance search and this controller moves into a position + // where it could near-grab something, stop searching. + for (var j = 0; j < nearGrabReadiness.length; j++) { + if (nearGrabReadiness[j].active) { + this.laserPointerOff(); + return makeRunningValues(false, [], []); + } + } + var rayPickInfo = controllerData.rayPicks[this.hand]; if (rayPickInfo.type == RayPick.INTERSECTED_ENTITY) { var entityID = rayPickInfo.objectID; - print("QQQ rayPickInfo.entityID = " + entityID); var targetProps = Entities.getEntityProperties(entityID, ["dynamic", "shapeType", "position", "rotation", "dimensions", "density", "userData", "locked", "type"]); if (entityIsDistanceGrabbable(targetProps)) { - print("QQQ is distance grabbable"); this.grabbedThingID = entityID; this.grabbedDistance = rayPickInfo.distance; var otherModuleName = this.hand == RIGHT_HAND ? "LeftFarActionGrabEntity" : "RightFarActionGrabEntity"; @@ -394,17 +411,6 @@ Script.include("/~/system/libraries/controllers.js"); this.distanceRotating = false; this.startFarGrabAction(controllerData, targetProps); } - } else { - print("QQQ is NOT distance grabbable"); - } - } - - // if we are doing a distance search and this controller moves into a position - // where it could near-grab something, stop searching. - for (var j = 0; j < nearGrabReadiness.length; j++) { - if (nearGrabReadiness[j].active) { - this.laserPointerOff(); - return makeRunningValues(false, [], []); } } } From 9015d69c71dc9c1fb5925ba53163243a96049e85 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 13 Aug 2017 17:21:55 -0700 Subject: [PATCH 15/59] fix pop at start of far-grab --- .../controllers/controllerModules/farActionGrabEntity.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 448f21a592..f16b6c52c2 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -202,7 +202,6 @@ Script.include("/~/system/libraries/controllers.js"); var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, grabbedProperties.position)); var timeScale = this.distanceGrabTimescale(this.mass, distanceToObject); this.linearTimeScale = timeScale; - this.actionID = NULL_UUID; this.actionID = Entities.addAction("far-grab", this.grabbedThingID, { targetPosition: this.currentObjectPosition, linearTimeScale: timeScale, @@ -221,12 +220,10 @@ Script.include("/~/system/libraries/controllers.js"); // } Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); - this.turnOffVisualizations(); this.previousRoomControllerPosition = roomControllerPosition; }; this.continueDistanceHolding = function(controllerData) { - var controllerLocation = controllerData.controllerLocations[this.hand]; var worldControllerPosition = controllerLocation.position; var worldControllerRotation = controllerLocation.orientation; From 13424eb792b387a6c1398b0d388ccd998a20a45f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 13 Aug 2017 17:42:08 -0700 Subject: [PATCH 16/59] kinematic-grab defaults to true --- scripts/system/controllers/controllerDispatcherUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerDispatcherUtils.js b/scripts/system/controllers/controllerDispatcherUtils.js index 1c9edd6002..72cf9362d1 100644 --- a/scripts/system/controllers/controllerDispatcherUtils.js +++ b/scripts/system/controllers/controllerDispatcherUtils.js @@ -124,7 +124,7 @@ getGrabbableData = function (props) { grabbableData.ignoreIK = true; } if (!grabbableData.hasOwnProperty("kinematicGrab")) { - grabbableData.kinematicGrab = false; + grabbableData.kinematicGrab = true; } return grabbableData; From b52a406ff18547f6be42e8156e0cc2c2c1a81c55 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 14 Aug 2017 18:25:57 -0700 Subject: [PATCH 17/59] trying to get equipping working --- .../controllers/controllerDispatcher.js | 89 ++- .../controllers/controllerDispatcherUtils.js | 16 +- .../controllerModules/equipEntity.js | 634 ++++++++++++++++++ .../controllerModules/farActionGrabEntity.js | 3 +- .../system/controllers/controllerScripts.js | 3 +- 5 files changed, 704 insertions(+), 41 deletions(-) create mode 100644 scripts/system/controllers/controllerModules/equipEntity.js diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 3398d535f4..b35891e71e 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -90,23 +90,28 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); this.leftTriggerClicked = 0; this.rightTriggerValue = 0; this.rightTriggerClicked = 0; - + this.leftSecondaryValue = 0; + this.rightSecondaryValue = 0; this.leftTriggerPress = function (value) { _this.leftTriggerValue = value; }; - this.leftTriggerClick = function (value) { _this.leftTriggerClicked = value; }; - this.rightTriggerPress = function (value) { _this.rightTriggerValue = value; }; - this.rightTriggerClick = function (value) { _this.rightTriggerClicked = value; }; + this.leftSecondaryPress = function (value) { + _this.leftSecondaryValue = value; + }; + this.rightSecondaryPress = function (value) { + _this.rightSecondaryValue = value; + }; + this.dataGatherers = {}; this.dataGatherers.leftControllerLocation = function () { @@ -150,10 +155,21 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); } } this.orderedPluginNames.sort(function (a, b) { - return controllerDispatcherPlugins[a].priority < controllerDispatcherPlugins[b].priority; + return controllerDispatcherPlugins[a].parameters.priority - + controllerDispatcherPlugins[b].parameters.priority; }); - print("controllerDispatcher: new plugin order: " + JSON.stringify(this.orderedPluginNames)); + // print("controllerDispatcher -- new plugin order: " + JSON.stringify(this.orderedPluginNames)); + var output = "controllerDispatcher -- new plugin order: "; + for (var k = 0; k < this.orderedPluginNames.length; k++) { + var dbgPluginName = this.orderedPluginNames[k]; + var priority = controllerDispatcherPlugins[dbgPluginName].parameters.priority; + output += dbgPluginName + ":" + priority; + if (k + 1 < this.orderedPluginNames.length) { + output += ", "; + } + } + print(output); controllerDispatcherPluginsNeedSort = false; } @@ -166,44 +182,31 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); var h; for (h = LEFT_HAND; h <= RIGHT_HAND; h++) { // todo: check controllerLocations[h].valid - var nearbyOverlays = Overlays.findOverlays(controllerLocations[h].position, NEAR_MIN_RADIUS); - var makeOverlaySorter = function (handIndex) { - return function (a, b) { - var aPosition = Overlays.getProperty(a, "position"); - var aDistance = Vec3.distance(aPosition, controllerLocations[handIndex]); - var bPosition = Overlays.getProperty(b, "position"); - var bDistance = Vec3.distance(bPosition, controllerLocations[handIndex]); - return aDistance - bDistance; - }; - }; - nearbyOverlays.sort(makeOverlaySorter(h)); + var nearbyOverlays = Overlays.findOverlays(controllerLocations[h].position, NEAR_MAX_RADIUS); + nearbyOverlays.sort(function (a, b) { + var aPosition = Overlays.getProperty(a, "position"); + var aDistance = Vec3.distance(aPosition, controllerLocations[h].position); + var bPosition = Overlays.getProperty(b, "position"); + var bDistance = Vec3.distance(bPosition, controllerLocations[h].position); + return aDistance - bDistance; + }); nearbyOverlayIDs.push(nearbyOverlays); } // find entities near each hand var nearbyEntityProperties = [[], []]; + var nearbyEntityPropertiesByID = {}; for (h = LEFT_HAND; h <= RIGHT_HAND; h++) { // todo: check controllerLocations[h].valid var controllerPosition = controllerLocations[h].position; - var nearbyEntityIDs = Entities.findEntities(controllerPosition, NEAR_MIN_RADIUS); + var nearbyEntityIDs = Entities.findEntities(controllerPosition, NEAR_MAX_RADIUS); for (var j = 0; j < nearbyEntityIDs.length; j++) { var entityID = nearbyEntityIDs[j]; var props = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); props.id = entityID; - props.distanceFromController = Vec3.length(Vec3.subtract(controllerPosition, props.position)); - if (props.distanceFromController < NEAR_MAX_RADIUS) { - nearbyEntityProperties[h].push(props); - } + nearbyEntityPropertiesByID[entityID] = props; + nearbyEntityProperties[h].push(props); } - // sort by distance from each hand - var makeSorter = function (handIndex) { - return function (a, b) { - var aDistance = Vec3.distance(a.position, controllerLocations[handIndex]); - var bDistance = Vec3.distance(b.position, controllerLocations[handIndex]); - return aDistance - bDistance; - }; - }; - nearbyEntityProperties[h].sort(makeSorter(h)); } // raypick for each controller @@ -221,25 +224,33 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); length: 1000 }; - var nearEntityID = rayPicks[h].entityID; - if (nearEntityID) { + if (rayPicks[h].type == RayPick.INTERSECTED_ENTITY) { // XXX check to make sure this one isn't already in nearbyEntityProperties? if (rayPicks[h].distance < NEAR_GRAB_PICK_RADIUS) { + var nearEntityID = rayPicks[h].objectID; var nearbyProps = Entities.getEntityProperties(nearEntityID, DISPATCHER_PROPERTIES); nearbyProps.id = nearEntityID; - if (entityIsGrabbable(nearbyProps)) { - nearbyEntityProperties[h].push(nearbyProps); - } + nearbyEntityPropertiesByID[nearEntityID] = nearbyProps; + nearbyEntityProperties[h].push(nearbyProps); } } + + // sort by distance from each hand + nearbyEntityProperties[h].sort(function (a, b) { + var aDistance = Vec3.distance(a.position, controllerLocations[h].position); + var bDistance = Vec3.distance(b.position, controllerLocations[h].position); + return aDistance - bDistance; + }); } // bundle up all the data about the current situation var controllerData = { triggerValues: [_this.leftTriggerValue, _this.rightTriggerValue], triggerClicks: [_this.leftTriggerClicked, _this.rightTriggerClicked], + secondaryValues: [_this.leftSecondaryValue, _this.rightSecondaryValue], controllerLocations: controllerLocations, nearbyEntityProperties: nearbyEntityProperties, + nearbyEntityPropertiesByID: nearbyEntityPropertiesByID, nearbyOverlayIDs: nearbyOverlayIDs, rayPicks: rayPicks }; @@ -288,6 +299,12 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); mapping.from([Controller.Standard.RTClick]).peek().to(_this.rightTriggerClick); mapping.from([Controller.Standard.LT]).peek().to(_this.leftTriggerPress); mapping.from([Controller.Standard.LTClick]).peek().to(_this.leftTriggerClick); + + mapping.from([Controller.Standard.RB]).peek().to(_this.rightSecondaryPress); + mapping.from([Controller.Standard.LB]).peek().to(_this.leftSecondaryPress); + mapping.from([Controller.Standard.LeftGrip]).peek().to(_this.leftSecondaryPress); + mapping.from([Controller.Standard.RightGrip]).peek().to(_this.rightSecondaryPress); + Controller.enableMapping(MAPPING_NAME); diff --git a/scripts/system/controllers/controllerDispatcherUtils.js b/scripts/system/controllers/controllerDispatcherUtils.js index 72cf9362d1..daf6b667ed 100644 --- a/scripts/system/controllers/controllerDispatcherUtils.js +++ b/scripts/system/controllers/controllerDispatcherUtils.js @@ -17,6 +17,7 @@ COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, + Entities, makeDispatcherModuleParameters, makeRunningValues, enableDispatcherModule, @@ -30,7 +31,8 @@ controllerDispatcherPluginsNeedSort, projectOntoXYPlane, projectOntoEntityXYPlane, - projectOntoOverlayXYPlane + projectOntoOverlayXYPlane, + entityHasActions */ MSECS_PER_SEC = 1000.0; @@ -111,10 +113,14 @@ getGrabbableData = function (props) { var grabbableData = {}; var userDataParsed = null; try { - userDataParsed = JSON.parse(props.userData); + if (!props.userDataParsed) { + props.userDataParsed = JSON.parse(props.userData); + } + userDataParsed = props.userDataParsed; } catch (err) { + userDataParsed = {}; } - if (userDataParsed && userDataParsed.grabbable) { + if (userDataParsed.grabbable) { grabbableData = userDataParsed.grabbable; } if (!grabbableData.hasOwnProperty("grabbable")) { @@ -230,3 +236,7 @@ projectOntoOverlayXYPlane = function projectOntoOverlayXYPlane(overlayID, worldP return projectOntoXYPlane(worldPos, position, rotation, dimensions, DEFAULT_REGISTRATION_POINT); }; + +entityHasActions = function (entityID) { + return Entities.getActionIDs(entityID).length > 0; +}; diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js new file mode 100644 index 0000000000..65668f0d23 --- /dev/null +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -0,0 +1,634 @@ +"use strict"; + +// equipEntity.js +// +// 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, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, + getControllerJointIndex, NULL_UUID, enableDispatcherModule, disableDispatcherModule, + Messages, makeDispatcherModuleParameters, makeRunningValues, Settings, entityHasActions, + Vec3, Overlays, flatten, Xform, getControllerWorldLocation +*/ + +Script.include("/~/system/libraries/Xform.js"); +Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllers.js"); + + +var DEFAULT_SPHERE_MODEL_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/equip-Fresnel-3.fbx"; +var EQUIP_SPHERE_SCALE_FACTOR = 0.65; + + +// Each overlayInfoSet describes a single equip hotspot. +// It is an object with the following keys: +// timestamp - last time this object was updated, used to delete stale hotspot overlays. +// entityID - entity assosicated with this hotspot +// localPosition - position relative to the entity +// hotspot - hotspot object +// overlays - array of overlay objects created by Overlay.addOverlay() +// currentSize - current animated scale value +// targetSize - the target of our scale animations +// type - "sphere" or "model". +function EquipHotspotBuddy() { + // holds map from {string} hotspot.key to {object} overlayInfoSet. + this.map = {}; + + // array of all hotspots that are highlighed. + this.highlightedHotspots = []; +} +EquipHotspotBuddy.prototype.clear = function() { + var keys = Object.keys(this.map); + for (var i = 0; i < keys.length; i++) { + var overlayInfoSet = this.map[keys[i]]; + this.deleteOverlayInfoSet(overlayInfoSet); + } + this.map = {}; + this.highlightedHotspots = []; +}; +EquipHotspotBuddy.prototype.highlightHotspot = function(hotspot) { + this.highlightedHotspots.push(hotspot.key); +}; +EquipHotspotBuddy.prototype.updateHotspot = function(hotspot, timestamp) { + var overlayInfoSet = this.map[hotspot.key]; + if (!overlayInfoSet) { + // create a new overlayInfoSet + overlayInfoSet = { + timestamp: timestamp, + entityID: hotspot.entityID, + localPosition: hotspot.localPosition, + hotspot: hotspot, + currentSize: 0, + targetSize: 1, + overlays: [] + }; + + var diameter = hotspot.radius * 2; + + // override default sphere with a user specified model, if it exists. + overlayInfoSet.overlays.push(Overlays.addOverlay("model", { + name: "hotspot overlay", + url: hotspot.modelURL ? hotspot.modelURL : DEFAULT_SPHERE_MODEL_URL, + position: hotspot.worldPosition, + rotation: { + x: 0, + y: 0, + z: 0, + w: 1 + }, + dimensions: diameter * EQUIP_SPHERE_SCALE_FACTOR, + scale: hotspot.modelScale, + ignoreRayIntersection: true + })); + overlayInfoSet.type = "model"; + print("QQQ adding hopspot: " + hotspot.key); + this.map[hotspot.key] = overlayInfoSet; + } else { + print("QQQ updating hopspot: " + hotspot.key); + overlayInfoSet.timestamp = timestamp; + } +}; +EquipHotspotBuddy.prototype.updateHotspots = function(hotspots, timestamp) { + var _this = this; + hotspots.forEach(function(hotspot) { + _this.updateHotspot(hotspot, timestamp); + }); + this.highlightedHotspots = []; +}; +EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerData) { + + var HIGHLIGHT_SIZE = 1.1; + var NORMAL_SIZE = 1.0; + + var keys = Object.keys(this.map); + for (var i = 0; i < keys.length; i++) { + var overlayInfoSet = this.map[keys[i]]; + + // this overlayInfo is highlighted. + if (this.highlightedHotspots.indexOf(keys[i]) != -1) { + overlayInfoSet.targetSize = HIGHLIGHT_SIZE; + } else { + overlayInfoSet.targetSize = NORMAL_SIZE; + } + + // start to fade out this hotspot. + if (overlayInfoSet.timestamp != timestamp) { + print("QQQ fading " + overlayInfoSet.entityID + " " + overlayInfoSet.timestamp + " != " + timestamp); + overlayInfoSet.targetSize = 0; + } + + // animate the size. + var SIZE_TIMESCALE = 0.1; + var tau = deltaTime / SIZE_TIMESCALE; + if (tau > 1.0) { + tau = 1.0; + } + overlayInfoSet.currentSize += (overlayInfoSet.targetSize - overlayInfoSet.currentSize) * tau; + + if (overlayInfoSet.timestamp != timestamp && overlayInfoSet.currentSize <= 0.05) { + print("QQQ deleting " + overlayInfoSet.entityID + " " + overlayInfoSet.timestamp + " != " + timestamp); + + // this is an old overlay, that has finished fading out, delete it! + overlayInfoSet.overlays.forEach(Overlays.deleteOverlay); + delete this.map[keys[i]]; + } else { + // update overlay position, rotation to follow the object it's attached to. + + print("QQQ grew " + overlayInfoSet.entityID + " " + overlayInfoSet.timestamp + " != " + timestamp); + + var props = controllerData.nearbyEntityPropertiesByID[overlayInfoSet.entityID]; + if (props) { + var entityXform = new Xform(props.rotation, props.position); + var position = entityXform.xformPoint(overlayInfoSet.localPosition); + + var dimensions; + if (overlayInfoSet.type == "sphere") { + dimensions = overlayInfoSet.hotspot.radius * 2 * overlayInfoSet.currentSize * EQUIP_SPHERE_SCALE_FACTOR; + } else { + dimensions = overlayInfoSet.hotspot.radius * 2 * overlayInfoSet.currentSize; + } + + overlayInfoSet.overlays.forEach(function(overlay) { + Overlays.editOverlay(overlay, { + position: position, + rotation: props.rotation, + dimensions: dimensions + }); + }); + } else { + print("QQQ but no props for " + overlayInfoSet.entityID); + overlayInfoSet.overlays.forEach(Overlays.deleteOverlay); + delete this.map[keys[i]]; + } + } + } +}; + + + +(function() { + + var debug = true; + var ATTACH_POINT_SETTINGS = "io.highfidelity.attachPoints"; + + var EQUIP_RADIUS = 0.2; // radius used for palm vs equip-hotspot for equipping. + + var HAPTIC_PULSE_STRENGTH = 1.0; + var HAPTIC_PULSE_DURATION = 13.0; + var HAPTIC_TEXTURE_STRENGTH = 0.1; + var HAPTIC_TEXTURE_DURATION = 3.0; + var HAPTIC_TEXTURE_DISTANCE = 0.002; + var HAPTIC_DEQUIP_STRENGTH = 0.75; + var HAPTIC_DEQUIP_DURATION = 50.0; + + var TRIGGER_SMOOTH_RATIO = 0.1; // Time averaging of trigger - 0.0 disables smoothing + var TRIGGER_OFF_VALUE = 0.1; + var TRIGGER_ON_VALUE = TRIGGER_OFF_VALUE + 0.05; // Squeezed just enough to activate search or near grab + var BUMPER_ON_VALUE = 0.5; + + + function getWearableData(props) { + var wearable = {}; + try { + if (!props.userDataParsed) { + props.userDataParsed = JSON.parse(props.userData); + } + wearable = props.userDataParsed.wearable ? props.userDataParsed.wearable : {}; + } catch (err) { + } + return wearable; + } + function getEquipHotspotsData(props) { + var equipHotspots = []; + try { + if (!props.userDataParsed) { + props.userDataParsed = JSON.parse(props.userData); + } + equipHotspots = props.userDataParsed.equipHotspots ? props.userDataParsed.equipHotspots : []; + } catch (err) { + } + return equipHotspots; + } + + function getAttachPointSettings() { + try { + var str = Settings.getValue(ATTACH_POINT_SETTINGS); + if (str === "false") { + return {}; + } else { + return JSON.parse(str); + } + } catch (err) { + print("Error parsing attachPointSettings: " + err); + return {}; + } + } + + function setAttachPointSettings(attachPointSettings) { + var str = JSON.stringify(attachPointSettings); + Settings.setValue(ATTACH_POINT_SETTINGS, str); + } + + function getAttachPointForHotspotFromSettings(hotspot, hand) { + var attachPointSettings = getAttachPointSettings(); + var jointName = (hand === RIGHT_HAND) ? "RightHand" : "LeftHand"; + var joints = attachPointSettings[hotspot.key]; + if (joints) { + return joints[jointName]; + } else { + return undefined; + } + } + + function storeAttachPointForHotspotInSettings(hotspot, hand, offsetPosition, offsetRotation) { + var attachPointSettings = getAttachPointSettings(); + var jointName = (hand === RIGHT_HAND) ? "RightHand" : "LeftHand"; + var joints = attachPointSettings[hotspot.key]; + if (!joints) { + joints = {}; + attachPointSettings[hotspot.key] = joints; + } + joints[jointName] = [offsetPosition, offsetRotation]; + setAttachPointSettings(attachPointSettings); + } + + function EquipEntity(hand) { + this.hand = hand; + this.targetEntityID = null; + this.prevHandIsUpsideDown = false; + + this.parameters = makeDispatcherModuleParameters( + 300, + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100); + + var equipHotspotBuddy = new EquipHotspotBuddy(); + + // returns a list of all equip-hotspots assosiated with this entity. + // @param {UUID} entityID + // @returns {Object[]} array of objects with the following fields. + // * key {string} a string that can be used to uniquely identify this hotspot + // * entityID {UUID} + // * localPosition {Vec3} position of the hotspot in object space. + // * worldPosition {vec3} position of the hotspot in world space. + // * radius {number} radius of equip hotspot + // * joints {Object} keys are joint names values are arrays of two elements: + // offset position {Vec3} and offset rotation {Quat}, both are in the coordinate system of the joint. + // * modelURL {string} url for model to use instead of default sphere. + // * modelScale {Vec3} scale factor for model + this.collectEquipHotspots = function(props) { + var result = []; + var entityID = props.id; + var entityXform = new Xform(props.rotation, props.position); + + var equipHotspotsProps = getEquipHotspotsData(props); + if (equipHotspotsProps && equipHotspotsProps.length > 0) { + var i, length = equipHotspotsProps.length; + for (i = 0; i < length; i++) { + var hotspot = equipHotspotsProps[i]; + if (hotspot.position && hotspot.radius && hotspot.joints) { + result.push({ + key: entityID.toString() + i.toString(), + entityID: entityID, + localPosition: hotspot.position, + worldPosition: entityXform.xformPoint(hotspot.position), + radius: hotspot.radius, + joints: hotspot.joints, + modelURL: hotspot.modelURL, + modelScale: hotspot.modelScale + }); + } + } + } else { + var wearableProps = getWearableData(props); + if (wearableProps && wearableProps.joints) { + result.push({ + key: entityID.toString() + "0", + entityID: entityID, + localPosition: { + x: 0, + y: 0, + z: 0 + }, + worldPosition: entityXform.pos, + radius: EQUIP_RADIUS, + joints: wearableProps.joints, + modelURL: null, + modelScale: null + }); + } + } + return result; + }; + + this.hotspotIsEquippable = function(hotspot, controllerData) { + var props = controllerData.nearbyEntityPropertiesByID[hotspot.entityID]; + + var hasParent = true; + if (props.parentID === NULL_UUID) { + hasParent = false; + } + if (hasParent || entityHasActions(hotspot.entityID)) { + if (debug) { + print("equip is skipping '" + props.name + "': grabbed by someone else: " + + hasParent + " : " + entityHasActions(hotspot.entityID)); + } + return false; + } + + return true; + }; + + this.handToController = function() { + return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + }; + + + + this.triggerPress = function(value) { + this.rawTriggerValue = value; + }; + + this.triggerClick = function(value) { + this.triggerClicked = value; + }; + + this.secondaryPress = function(value) { + this.rawSecondaryValue = value; + }; + + this.updateSmoothedTrigger = function(controllerData) { + var triggerValue = controllerData.triggerValues[this.hand]; + // smooth out trigger value + this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + + (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); + }; + + this.triggerSmoothedGrab = function() { + return this.triggerClicked; + }; + + this.triggerSmoothedSqueezed = function() { + return this.triggerValue > TRIGGER_ON_VALUE; + }; + + this.triggerSmoothedReleased = function() { + return this.triggerValue < TRIGGER_OFF_VALUE; + }; + + this.secondarySqueezed = function() { + return this.rawSecondaryValue > BUMPER_ON_VALUE; + }; + + this.secondaryReleased = function() { + return this.rawSecondaryValue < BUMPER_ON_VALUE; + }; + + + + this.chooseNearEquipHotspots = function(candidateEntityProps, controllerData) { + var _this = this; + var collectedHotspots = flatten(candidateEntityProps.map(function(props) { + return _this.collectEquipHotspots(props); + })); + var controllerLocation = controllerData.controllerLocations[_this.hand]; + var worldControllerPosition = controllerLocation.position; + var equippableHotspots = collectedHotspots.filter(function(hotspot) { + var hotspotDistance = Vec3.distance(hotspot.worldPosition, worldControllerPosition); + return _this.hotspotIsEquippable(hotspot, controllerData) && + hotspotDistance < hotspot.radius; + }); + return equippableHotspots; + }; + + this.chooseBestEquipHotspot = function(candidateEntityProps, controllerData) { + var equippableHotspots = this.chooseNearEquipHotspots(candidateEntityProps, controllerData); + if (equippableHotspots.length > 0) { + // sort by distance; + var controllerLocation = controllerData.controllerLocations[this.hand]; + var worldControllerPosition = controllerLocation.position; + equippableHotspots.sort(function(a, b) { + var aDistance = Vec3.distance(a.worldPosition, worldControllerPosition); + var bDistance = Vec3.distance(b.worldPosition, worldControllerPosition); + return aDistance - bDistance; + }); + return equippableHotspots[0]; + } else { + return null; + } + }; + + this.dropGestureReset = function() { + this.prevHandIsUpsideDown = false; + }; + + this.dropGestureProcess = function (deltaTime) { + var worldHandRotation = getControllerWorldLocation(this.handToController(), true).orientation; + var localHandUpAxis = this.hand === RIGHT_HAND ? { x: 1, y: 0, z: 0 } : { x: -1, y: 0, z: 0 }; + var worldHandUpAxis = Vec3.multiplyQbyV(worldHandRotation, localHandUpAxis); + var DOWN = { x: 0, y: -1, z: 0 }; + + var DROP_ANGLE = Math.PI / 3; + var HYSTERESIS_FACTOR = 1.1; + var ROTATION_ENTER_THRESHOLD = Math.cos(DROP_ANGLE); + var ROTATION_EXIT_THRESHOLD = Math.cos(DROP_ANGLE * HYSTERESIS_FACTOR); + var rotationThreshold = this.prevHandIsUpsideDown ? ROTATION_EXIT_THRESHOLD : ROTATION_ENTER_THRESHOLD; + + var handIsUpsideDown = false; + if (Vec3.dot(worldHandUpAxis, DOWN) > rotationThreshold) { + handIsUpsideDown = true; + } + + if (handIsUpsideDown != this.prevHandIsUpsideDown) { + this.prevHandIsUpsideDown = handIsUpsideDown; + Controller.triggerHapticPulse(HAPTIC_DEQUIP_STRENGTH, HAPTIC_DEQUIP_DURATION, this.hand); + } + + return handIsUpsideDown; + }; + + this.clearEquipHaptics = function() { + this.prevPotentialEquipHotspot = null; + }; + + this.updateEquipHaptics = function(potentialEquipHotspot, currentLocation) { + if (potentialEquipHotspot && !this.prevPotentialEquipHotspot || + !potentialEquipHotspot && this.prevPotentialEquipHotspot) { + Controller.triggerHapticPulse(HAPTIC_TEXTURE_STRENGTH, HAPTIC_TEXTURE_DURATION, this.hand); + this.lastHapticPulseLocation = currentLocation; + } else if (potentialEquipHotspot && + Vec3.distance(this.lastHapticPulseLocation, currentLocation) > HAPTIC_TEXTURE_DISTANCE) { + Controller.triggerHapticPulse(HAPTIC_TEXTURE_STRENGTH, HAPTIC_TEXTURE_DURATION, this.hand); + this.lastHapticPulseLocation = currentLocation; + } + this.prevPotentialEquipHotspot = potentialEquipHotspot; + }; + + this.startEquipEntity = function (controllerData) { + this.dropGestureReset(); + this.clearEquipHaptics(); + Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); + + // if an object is "equipped" and has a predefined offset, use it. + var offsets = getAttachPointForHotspotFromSettings(this.grabbedHotspot, this.hand); + if (offsets) { + this.offsetPosition = offsets[0]; + this.offsetRotation = offsets[1]; + } else { + var handJointName = this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"; + if (this.grabbedHotspot.joints[handJointName]) { + this.offsetPosition = this.grabbedHotspot.joints[handJointName][0]; + this.offsetRotation = this.grabbedHotspot.joints[handJointName][1]; + } + } + + var handJointIndex; + if (this.ignoreIK) { + handJointIndex = this.controllerJointIndex; + } else { + handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); + } + + var reparentProps = { + parentID: AVATAR_SELF_ID, + parentJointIndex: handJointIndex, + velocity: {x: 0, y: 0, z: 0}, + angularVelocity: {x: 0, y: 0, z: 0}, + localPosition: this.offsetPosition, + localRotation: this.offsetRotation + }; + Entities.editEntity(this.grabbedThingID, reparentProps); + + Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ + action: 'equip', + grabbedEntity: this.grabbedThingID, + joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" + })); + }; + + this.endEquipEntity = function () { + Entities.editEntity(this.targetEntityID, { + parentID: NULL_UUID, + parentJointIndex: -1 + }); + + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.targetEntityID, "releaseEquip", args); + + this.targetEntityID = null; + }; + + this.isReady = function (controllerData, deltaTime) { + + this.rawTriggerValue = controllerData.triggerValues[this.hand]; + this.triggerClicked = controllerData.triggerClicks[this.hand]; + this.rawSecondaryValue = controllerData.secondaryValues[this.hand]; + this.updateSmoothedTrigger(controllerData); + + this.controllerJointIndex = getControllerJointIndex(this.hand); + + if (this.triggerSmoothedReleased() && this.secondaryReleased()) { + this.waitForTriggerRelease = false; + } + + var controllerLocation = getControllerWorldLocation(this.handToController(), true); + var worldHandPosition = controllerLocation.position; + + // var candidateEntities = controllerData.nearbyEntityProperties[this.hand].map(function (props) { + // return props.id; + // }); + + var candidateEntityProps = controllerData.nearbyEntityProperties[this.hand]; + + var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntityProps, controllerData); + if (!this.waitForTriggerRelease) { + this.updateEquipHaptics(potentialEquipHotspot, worldHandPosition); + } + + var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntityProps, controllerData); + var timestamp = Date.now(); + equipHotspotBuddy.updateHotspots(nearEquipHotspots, timestamp); + if (potentialEquipHotspot) { + equipHotspotBuddy.highlightHotspot(potentialEquipHotspot); + } + + equipHotspotBuddy.update(deltaTime, timestamp, controllerData); + + return makeRunningValues(false, [], []); + }; + + this.run = function (controllerData, deltaTime) { + + if (controllerData.secondaryValues[this.hand]) { + // this.secondaryReleased() will always be true when not depressed + // so we cannot simply rely on that for release - ensure that the + // trigger was first "prepared" by being pushed in before the release + this.preparingHoldRelease = true; + } + + if (this.preparingHoldRelease && !controllerData.secondaryValues[this.hand]) { + // we have an equipped object and the secondary trigger was released + // short-circuit the other checks and release it + this.preparingHoldRelease = false; + this.endEquipEntity(); + return makeRunningValues(false, [], []); + } + + var dropDetected = this.dropGestureProcess(deltaTime); + + if (this.triggerSmoothedReleased()) { + this.waitForTriggerRelease = false; + } + + if (dropDetected && this.prevDropDetected != dropDetected) { + this.waitForTriggerRelease = true; + } + + // highlight the grabbed hotspot when the dropGesture is detected. + var timestamp = Date.now(); + if (dropDetected) { + equipHotspotBuddy.updateHotspot(this.grabbedHotspot, timestamp); + equipHotspotBuddy.highlightHotspot(this.grabbedHotspot); + } + + if (dropDetected && !this.waitForTriggerRelease && controllerData.triggerClicks[this.hand]) { + // store the offset attach points into preferences. + if (this.grabbedHotspot && this.grabbedThingID) { + var prefprops = Entities.getEntityProperties(this.grabbedThingID, ["localPosition", "localRotation"]); + if (prefprops && prefprops.localPosition && prefprops.localRotation) { + storeAttachPointForHotspotInSettings(this.grabbedHotspot, this.hand, + prefprops.localPosition, prefprops.localRotation); + } + } + + this.endEquipEntity(); + return makeRunningValues(false, [], []); + } + this.prevDropDetected = dropDetected; + + return makeRunningValues(true, [this.targetEntityID], []); + }; + + this.cleanup = function () { + if (this.targetEntityID) { + this.endEquipEntity(); + } + }; + } + + var leftEquipEntity = new EquipEntity(LEFT_HAND); + var rightEquipEntity = new EquipEntity(RIGHT_HAND); + + enableDispatcherModule("LeftEquipEntity", leftEquipEntity); + enableDispatcherModule("RightEquipEntity", rightEquipEntity); + + this.cleanup = function () { + leftEquipEntity.cleanup(); + rightEquipEntity.cleanup(); + disableDispatcherModule("LeftEquipEntity"); + disableDispatcherModule("RightEquipEntity"); + }; + Script.scriptEnding.connect(this.cleanup); +}()); diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index f16b6c52c2..a2edab4102 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -397,7 +397,8 @@ Script.include("/~/system/libraries/controllers.js"); if (entityIsDistanceGrabbable(targetProps)) { this.grabbedThingID = entityID; this.grabbedDistance = rayPickInfo.distance; - var otherModuleName = this.hand == RIGHT_HAND ? "LeftFarActionGrabEntity" : "RightFarActionGrabEntity"; + var otherModuleName = + this.hand == RIGHT_HAND ? "LeftFarActionGrabEntity" : "RightFarActionGrabEntity"; var otherFarGrabModule = getEnabledModuleByName(otherModuleName); if (otherFarGrabModule.grabbedThingID == this.grabbedThingID) { this.distanceRotating = true; diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 552aec20a4..ea45f0a2c6 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -23,7 +23,8 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/nearParentGrabOverlay.js", "controllerModules/nearActionGrabEntity.js", "controllerModules/farActionGrabEntity.js", - "controllerModules/tabletStylusInput.js" + "controllerModules/tabletStylusInput.js", + "controllerModules/equipEntity.js" ]; var DEBUG_MENU_ITEM = "Debug defaultScripts.js"; From b2d7eefc976f226ea303e9e14bcf44fde3e0044d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 15 Aug 2017 10:30:47 -0700 Subject: [PATCH 18/59] equip highlight works now --- .../controllers/controllerDispatcher.js | 2 ++ .../controllers/controllerDispatcherUtils.js | 4 ++++ .../controllerModules/equipEntity.js | 20 +++++++++++++++---- .../controllerModules/nearActionGrabEntity.js | 7 ++++++- .../controllerModules/nearParentGrabEntity.js | 7 ++++++- 5 files changed, 34 insertions(+), 6 deletions(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index b35891e71e..959f217eb4 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -271,6 +271,8 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); } } + // print("QQQ running plugins: " + JSON.stringify(_this.runningPluginNames)); + // give time to running plugins for (var runningPluginName in _this.runningPluginNames) { if (_this.runningPluginNames.hasOwnProperty(runningPluginName)) { diff --git a/scripts/system/controllers/controllerDispatcherUtils.js b/scripts/system/controllers/controllerDispatcherUtils.js index daf6b667ed..a7bd3339d0 100644 --- a/scripts/system/controllers/controllerDispatcherUtils.js +++ b/scripts/system/controllers/controllerDispatcherUtils.js @@ -17,6 +17,7 @@ COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, + NEAR_GRAB_RADIUS, Entities, makeDispatcherModuleParameters, makeRunningValues, @@ -66,6 +67,9 @@ COLORS_GRAB_SEARCHING_FULL_SQUEEZE = { red: 250, green: 10, blue: 10 }; COLORS_GRAB_DISTANCE_HOLD = { red: 238, green: 75, blue: 214 }; +NEAR_GRAB_RADIUS = 0.1; + + // priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step // activitySlots -- indicates which "slots" must not yet be in use for this module to start diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index 65668f0d23..c009d74764 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -82,7 +82,7 @@ EquipHotspotBuddy.prototype.updateHotspot = function(hotspot, timestamp) { ignoreRayIntersection: true })); overlayInfoSet.type = "model"; - print("QQQ adding hopspot: " + hotspot.key); + print("QQQ adding hotspot: " + hotspot.key); this.map[hotspot.key] = overlayInfoSet; } else { print("QQQ updating hopspot: " + hotspot.key); @@ -520,7 +520,9 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.targetEntityID = null; }; - this.isReady = function (controllerData, deltaTime) { + this.checkNearbyHotspots = function (controllerData, deltaTime) { + + var timestamp = Date.now(); this.rawTriggerValue = controllerData.triggerValues[this.hand]; this.triggerClicked = controllerData.triggerClicks[this.hand]; @@ -548,7 +550,6 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa } var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntityProps, controllerData); - var timestamp = Date.now(); equipHotspotBuddy.updateHotspots(nearEquipHotspots, timestamp); if (potentialEquipHotspot) { equipHotspotBuddy.highlightHotspot(potentialEquipHotspot); @@ -556,11 +557,22 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa equipHotspotBuddy.update(deltaTime, timestamp, controllerData); - return makeRunningValues(false, [], []); + if (potentialEquipHotspot) { + return makeRunningValues(true, [potentialEquipHotspot.entityID], []); + } else { + return makeRunningValues(false, [], []); + } + + }; + + this.isReady = function (controllerData, deltaTime) { + return this.checkNearbyHotspots(controllerData, deltaTime); }; this.run = function (controllerData, deltaTime) { + return this.checkNearbyHotspots(controllerData, deltaTime); + if (controllerData.secondaryValues[this.hand]) { // this.secondaryReleased() will always be true when not depressed // so we cannot simply rely on that for release - ensure that the diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index 42859f3e19..217e90ef88 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -9,7 +9,7 @@ getControllerJointIndex, getGrabbableData, NULL_UUID, enableDispatcherModule, disableDispatcherModule, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, entityIsGrabbable, Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation, makeDispatcherModuleParameters, makeRunningValues, - TRIGGER_OFF_VALUE + TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS */ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); @@ -146,6 +146,11 @@ Script.include("/~/system/libraries/controllers.js"); var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand]; for (var i = 0; i < nearbyEntityProperties.length; i++) { var props = nearbyEntityProperties[i]; + var handPosition = controllerData.controllerLocations[this.hand].position; + var distance = Vec3.distance(props.position, handPosition); + if (distance > NEAR_GRAB_RADIUS) { + break; + } if (entityIsGrabbable(props)) { return props; } diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index 1de2fee5ea..c5ca95fe3b 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -9,7 +9,7 @@ /* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, getControllerJointIndex, NULL_UUID, enableDispatcherModule, disableDispatcherModule, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, - makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues + makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS */ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); @@ -129,6 +129,11 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand]; for (var i = 0; i < nearbyEntityProperties.length; i++) { var props = nearbyEntityProperties[i]; + var handPosition = controllerData.controllerLocations[this.hand].position; + var distance = Vec3.distance(props.position, handPosition); + if (distance > NEAR_GRAB_RADIUS) { + break; + } if (entityIsGrabbable(props)) { return props; } From e6ac07d43dc375638bdcfa83f16f7e8196c81806 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 15 Aug 2017 11:45:45 -0700 Subject: [PATCH 19/59] equipping mostly works --- .../controllers/controllerDispatcherUtils.js | 16 ++++- .../controllerModules/equipEntity.js | 66 ++++++------------- .../controllerModules/farActionGrabEntity.js | 15 +---- 3 files changed, 35 insertions(+), 62 deletions(-) diff --git a/scripts/system/controllers/controllerDispatcherUtils.js b/scripts/system/controllers/controllerDispatcherUtils.js index a7bd3339d0..5b6d624892 100644 --- a/scripts/system/controllers/controllerDispatcherUtils.js +++ b/scripts/system/controllers/controllerDispatcherUtils.js @@ -33,7 +33,8 @@ projectOntoXYPlane, projectOntoEntityXYPlane, projectOntoOverlayXYPlane, - entityHasActions + entityHasActions, + ensureDynamic */ MSECS_PER_SEC = 1000.0; @@ -244,3 +245,16 @@ projectOntoOverlayXYPlane = function projectOntoOverlayXYPlane(overlayID, worldP entityHasActions = function (entityID) { return Entities.getActionIDs(entityID).length > 0; }; + +ensureDynamic = function (entityID) { + // if we distance hold something and keep it very still before releasing it, it ends up + // non-dynamic in bullet. If it's too still, give it a little bounce so it will fall. + var props = Entities.getEntityProperties(entityID, ["velocity", "dynamic", "parentID"]); + if (props.dynamic && props.parentID == NULL_UUID) { + var velocity = props.velocity; + if (Vec3.length(velocity) < 0.05) { // see EntityMotionState.cpp DYNAMIC_LINEAR_VELOCITY_THRESHOLD + velocity = { x: 0.0, y: 0.2, z: 0.0 }; + Entities.editEntity(entityID, { velocity: velocity }); + } + } +}; diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index c009d74764..f44ce4ff78 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -9,7 +9,7 @@ /* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, getControllerJointIndex, NULL_UUID, enableDispatcherModule, disableDispatcherModule, Messages, makeDispatcherModuleParameters, makeRunningValues, Settings, entityHasActions, - Vec3, Overlays, flatten, Xform, getControllerWorldLocation + Vec3, Overlays, flatten, Xform, getControllerWorldLocation, ensureDynamic */ Script.include("/~/system/libraries/Xform.js"); @@ -82,10 +82,8 @@ EquipHotspotBuddy.prototype.updateHotspot = function(hotspot, timestamp) { ignoreRayIntersection: true })); overlayInfoSet.type = "model"; - print("QQQ adding hotspot: " + hotspot.key); this.map[hotspot.key] = overlayInfoSet; } else { - print("QQQ updating hopspot: " + hotspot.key); overlayInfoSet.timestamp = timestamp; } }; @@ -114,7 +112,6 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa // start to fade out this hotspot. if (overlayInfoSet.timestamp != timestamp) { - print("QQQ fading " + overlayInfoSet.entityID + " " + overlayInfoSet.timestamp + " != " + timestamp); overlayInfoSet.targetSize = 0; } @@ -127,16 +124,11 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa overlayInfoSet.currentSize += (overlayInfoSet.targetSize - overlayInfoSet.currentSize) * tau; if (overlayInfoSet.timestamp != timestamp && overlayInfoSet.currentSize <= 0.05) { - print("QQQ deleting " + overlayInfoSet.entityID + " " + overlayInfoSet.timestamp + " != " + timestamp); - // this is an old overlay, that has finished fading out, delete it! overlayInfoSet.overlays.forEach(Overlays.deleteOverlay); delete this.map[keys[i]]; } else { // update overlay position, rotation to follow the object it's attached to. - - print("QQQ grew " + overlayInfoSet.entityID + " " + overlayInfoSet.timestamp + " != " + timestamp); - var props = controllerData.nearbyEntityPropertiesByID[overlayInfoSet.entityID]; if (props) { var entityXform = new Xform(props.rotation, props.position); @@ -157,7 +149,6 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa }); }); } else { - print("QQQ but no props for " + overlayInfoSet.entityID); overlayInfoSet.overlays.forEach(Overlays.deleteOverlay); delete this.map[keys[i]]; } @@ -214,7 +205,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa function getAttachPointSettings() { try { var str = Settings.getValue(ATTACH_POINT_SETTINGS); - if (str === "false") { + if (str === "false" || str === "") { return {}; } else { return JSON.parse(str); @@ -257,6 +248,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.hand = hand; this.targetEntityID = null; this.prevHandIsUpsideDown = false; + this.triggerValue = 0; this.parameters = makeDispatcherModuleParameters( 300, @@ -333,7 +325,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa if (hasParent || entityHasActions(hotspot.entityID)) { if (debug) { print("equip is skipping '" + props.name + "': grabbed by someone else: " + - hasParent + " : " + entityHasActions(hotspot.entityID)); + hasParent + " : " + entityHasActions(hotspot.entityID) + " : " + this.hand); } return false; } @@ -345,20 +337,6 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; }; - - - this.triggerPress = function(value) { - this.rawTriggerValue = value; - }; - - this.triggerClick = function(value) { - this.triggerClicked = value; - }; - - this.secondaryPress = function(value) { - this.rawSecondaryValue = value; - }; - this.updateSmoothedTrigger = function(controllerData) { var triggerValue = controllerData.triggerValues[this.hand]; // smooth out trigger value @@ -366,10 +344,6 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); }; - this.triggerSmoothedGrab = function() { - return this.triggerClicked; - }; - this.triggerSmoothedSqueezed = function() { return this.triggerValue > TRIGGER_ON_VALUE; }; @@ -378,16 +352,10 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa return this.triggerValue < TRIGGER_OFF_VALUE; }; - this.secondarySqueezed = function() { - return this.rawSecondaryValue > BUMPER_ON_VALUE; - }; - this.secondaryReleased = function() { return this.rawSecondaryValue < BUMPER_ON_VALUE; }; - - this.chooseNearEquipHotspots = function(candidateEntityProps, controllerData) { var _this = this; var collectedHotspots = flatten(candidateEntityProps.map(function(props) { @@ -499,11 +467,11 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa localPosition: this.offsetPosition, localRotation: this.offsetRotation }; - Entities.editEntity(this.grabbedThingID, reparentProps); + Entities.editEntity(this.targetEntityID, reparentProps); Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ action: 'equip', - grabbedEntity: this.grabbedThingID, + grabbedEntity: this.targetEntityID, joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" })); }; @@ -517,6 +485,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.targetEntityID, "releaseEquip", args); + ensureDynamic(this.targetEntityID); this.targetEntityID = null; }; @@ -537,11 +506,6 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var controllerLocation = getControllerWorldLocation(this.handToController(), true); var worldHandPosition = controllerLocation.position; - - // var candidateEntities = controllerData.nearbyEntityProperties[this.hand].map(function (props) { - // return props.id; - // }); - var candidateEntityProps = controllerData.nearbyEntityProperties[this.hand]; var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntityProps, controllerData); @@ -558,11 +522,15 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa equipHotspotBuddy.update(deltaTime, timestamp, controllerData); if (potentialEquipHotspot) { + if (this.triggerSmoothedSqueezed()) { + this.grabbedHotspot = potentialEquipHotspot; + this.targetEntityID = this.grabbedHotspot.entityID; + this.startEquipEntity(controllerData); + } return makeRunningValues(true, [potentialEquipHotspot.entityID], []); } else { return makeRunningValues(false, [], []); } - }; this.isReady = function (controllerData, deltaTime) { @@ -571,7 +539,11 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.run = function (controllerData, deltaTime) { - return this.checkNearbyHotspots(controllerData, deltaTime); + if (!this.targetEntityID) { + return this.checkNearbyHotspots(controllerData, deltaTime); + } + + equipHotspotBuddy.update(deltaTime, timestamp, controllerData); if (controllerData.secondaryValues[this.hand]) { // this.secondaryReleased() will always be true when not depressed @@ -607,8 +579,8 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa if (dropDetected && !this.waitForTriggerRelease && controllerData.triggerClicks[this.hand]) { // store the offset attach points into preferences. - if (this.grabbedHotspot && this.grabbedThingID) { - var prefprops = Entities.getEntityProperties(this.grabbedThingID, ["localPosition", "localRotation"]); + if (this.grabbedHotspot && this.targetEntityID) { + var prefprops = Entities.getEntityProperties(this.targetEntityID, ["localPosition", "localRotation"]); if (prefprops && prefprops.localPosition && prefprops.localRotation) { storeAttachPointForHotspotInSettings(this.grabbedHotspot, this.hand, prefprops.localPosition, prefprops.localRotation); diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index a2edab4102..c1b87bc6e8 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -308,21 +308,8 @@ Script.include("/~/system/libraries/controllers.js"); this.previousRoomControllerPosition = roomControllerPosition; }; - this.ensureDynamic = function () { - // if we distance hold something and keep it very still before releasing it, it ends up - // non-dynamic in bullet. If it's too still, give it a little bounce so it will fall. - var props = Entities.getEntityProperties(this.grabbedThingID, ["velocity", "dynamic", "parentID"]); - if (props.dynamic && props.parentID == NULL_UUID) { - var velocity = props.velocity; - if (Vec3.length(velocity) < 0.05) { // see EntityMotionState.cpp DYNAMIC_LINEAR_VELOCITY_THRESHOLD - velocity = { x: 0.0, y: 0.2, z: 0.0 }; - Entities.editEntity(this.grabbedThingID, { velocity: velocity }); - } - } - }; - this.endNearGrabAction = function () { - this.ensureDynamic(); + ensureDynamic(this.grabbedThingID); this.distanceHolding = false; this.distanceRotating = false; Entities.deleteAction(this.grabbedThingID, this.actionID); From 1d24523be997313a5a326f674a8dd6c1ef02bd42 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 15 Aug 2017 13:09:17 -0700 Subject: [PATCH 20/59] equip drop gesture works --- .../controllerModules/equipEntity.js | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index f44ce4ff78..34db9709b3 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -160,7 +160,6 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa (function() { - var debug = true; var ATTACH_POINT_SETTINGS = "io.highfidelity.attachPoints"; var EQUIP_RADIUS = 0.2; // radius used for palm vs equip-hotspot for equipping. @@ -323,10 +322,6 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa hasParent = false; } if (hasParent || entityHasActions(hotspot.entityID)) { - if (debug) { - print("equip is skipping '" + props.name + "': grabbed by someone else: " + - hasParent + " : " + entityHasActions(hotspot.entityID) + " : " + this.hand); - } return false; } @@ -344,6 +339,10 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); }; + this.triggerSmoothedGrab = function() { + return this.triggerClicked; + }; + this.triggerSmoothedSqueezed = function() { return this.triggerValue > TRIGGER_ON_VALUE; }; @@ -489,15 +488,14 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.targetEntityID = null; }; - this.checkNearbyHotspots = function (controllerData, deltaTime) { - - var timestamp = Date.now(); - + this.updateInputs = function (controllerData) { this.rawTriggerValue = controllerData.triggerValues[this.hand]; this.triggerClicked = controllerData.triggerClicks[this.hand]; this.rawSecondaryValue = controllerData.secondaryValues[this.hand]; this.updateSmoothedTrigger(controllerData); + }; + this.checkNearbyHotspots = function (controllerData, deltaTime, timestamp) { this.controllerJointIndex = getControllerJointIndex(this.hand); if (this.triggerSmoothedReleased() && this.secondaryReleased()) { @@ -522,7 +520,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa equipHotspotBuddy.update(deltaTime, timestamp, controllerData); if (potentialEquipHotspot) { - if (this.triggerSmoothedSqueezed()) { + if (this.triggerSmoothedSqueezed() && !this.waitForTriggerRelease) { this.grabbedHotspot = potentialEquipHotspot; this.targetEntityID = this.grabbedHotspot.entityID; this.startEquipEntity(controllerData); @@ -534,17 +532,19 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa }; this.isReady = function (controllerData, deltaTime) { - return this.checkNearbyHotspots(controllerData, deltaTime); + var timestamp = Date.now(); + this.updateInputs(controllerData); + return this.checkNearbyHotspots(controllerData, deltaTime, timestamp); }; this.run = function (controllerData, deltaTime) { + var timestamp = Date.now(); + this.updateInputs(controllerData); if (!this.targetEntityID) { - return this.checkNearbyHotspots(controllerData, deltaTime); + return this.checkNearbyHotspots(controllerData, deltaTime, timestamp); } - equipHotspotBuddy.update(deltaTime, timestamp, controllerData); - if (controllerData.secondaryValues[this.hand]) { // this.secondaryReleased() will always be true when not depressed // so we cannot simply rely on that for release - ensure that the @@ -571,13 +571,13 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa } // highlight the grabbed hotspot when the dropGesture is detected. - var timestamp = Date.now(); if (dropDetected) { equipHotspotBuddy.updateHotspot(this.grabbedHotspot, timestamp); equipHotspotBuddy.highlightHotspot(this.grabbedHotspot); } - if (dropDetected && !this.waitForTriggerRelease && controllerData.triggerClicks[this.hand]) { + if (dropDetected && !this.waitForTriggerRelease && this.triggerSmoothedGrab()) { + this.waitForTriggerRelease = true; // store the offset attach points into preferences. if (this.grabbedHotspot && this.targetEntityID) { var prefprops = Entities.getEntityProperties(this.targetEntityID, ["localPosition", "localRotation"]); @@ -592,6 +592,8 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa } this.prevDropDetected = dropDetected; + equipHotspotBuddy.update(deltaTime, timestamp, controllerData); + return makeRunningValues(true, [this.targetEntityID], []); }; From ddca25672fd1e56763eefe159ceb8a3e5069133b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 15 Aug 2017 14:14:28 -0700 Subject: [PATCH 21/59] trying to get near-triggering working --- .../controllers/controllerDispatcher.js | 21 +--- .../controllers/controllerDispatcherUtils.js | 42 ++++++- .../controllerModules/nearActionGrabEntity.js | 34 ++--- .../controllerModules/nearParentGrabEntity.js | 19 +-- .../controllerModules/nearTrigger.js | 119 ++++++++++++++++++ .../system/controllers/controllerScripts.js | 3 +- 6 files changed, 182 insertions(+), 56 deletions(-) create mode 100644 scripts/system/controllers/controllerModules/nearTrigger.js diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 959f217eb4..94bc9851da 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -9,7 +9,7 @@ /* global Script, Entities, Overlays, Controller, Vec3, Quat, getControllerWorldLocation, RayPick, controllerDispatcherPlugins, controllerDispatcherPluginsNeedSort, entityIsGrabbable, - LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS, DEFAULT_SEARCH_SPHERE_DISTANCE + LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS, DEFAULT_SEARCH_SPHERE_DISTANCE, DISPATCHER_PROPERTIES */ controllerDispatcherPlugins = {}; @@ -25,25 +25,6 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); var NEAR_MIN_RADIUS = 0.1; var NEAR_MAX_RADIUS = 1.0; - var DISPATCHER_PROPERTIES = [ - "position", - "registrationPoint", - "rotation", - "gravity", - "collidesWith", - "dynamic", - "collisionless", - "locked", - "name", - "shapeType", - "parentID", - "parentJointIndex", - "density", - "dimensions", - "userData" - ]; - - var TARGET_UPDATE_HZ = 60; // 50hz good enough, but we're using update var BASIC_TIMER_INTERVAL_MS = 1000 / TARGET_UPDATE_HZ; var lastInterval = Date.now(); diff --git a/scripts/system/controllers/controllerDispatcherUtils.js b/scripts/system/controllers/controllerDispatcherUtils.js index 5b6d624892..913faf60bf 100644 --- a/scripts/system/controllers/controllerDispatcherUtils.js +++ b/scripts/system/controllers/controllerDispatcherUtils.js @@ -18,6 +18,7 @@ COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, NEAR_GRAB_RADIUS, + DISPATCHER_PROPERTIES, Entities, makeDispatcherModuleParameters, makeRunningValues, @@ -34,7 +35,8 @@ projectOntoEntityXYPlane, projectOntoOverlayXYPlane, entityHasActions, - ensureDynamic + ensureDynamic, + findGroupParent */ MSECS_PER_SEC = 1000.0; @@ -72,6 +74,27 @@ NEAR_GRAB_RADIUS = 0.1; +DISPATCHER_PROPERTIES = [ + "position", + "registrationPoint", + "rotation", + "gravity", + "collidesWith", + "dynamic", + "collisionless", + "locked", + "name", + "shapeType", + "parentID", + "parentJointIndex", + "density", + "dimensions", + "userData" +]; + + + + // priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step // activitySlots -- indicates which "slots" must not yet be in use for this module to start // requiredDataForReady -- which "situation" parts this module looks at to decide if it will start @@ -137,6 +160,9 @@ getGrabbableData = function (props) { if (!grabbableData.hasOwnProperty("kinematicGrab")) { grabbableData.kinematicGrab = true; } + if (!grabbableData.hasOwnProperty("wantsTrigger")) { + grabbableData.wantsTrigger = false; + } return grabbableData; }; @@ -258,3 +284,17 @@ ensureDynamic = function (entityID) { } } }; + +findGroupParent = function (controllerData, targetProps) { + while (targetProps.parentID && targetProps.parentID != NULL_UUID) { + var parentProps = Entities.getEntityProperties(targetProps.parentID, DISPATCHER_PROPERTIES); + if (!parentProps) { + break; + } + parentProps.id = targetProps.parentID; + targetProps = parentProps; + controllerData.nearbyEntityPropertiesByID[targetProps.id] = targetProps; + } + + return targetProps; +}; diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index 217e90ef88..a2673f8bca 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -9,7 +9,7 @@ getControllerJointIndex, getGrabbableData, NULL_UUID, enableDispatcherModule, disableDispatcherModule, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, entityIsGrabbable, Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation, makeDispatcherModuleParameters, makeRunningValues, - TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS + TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, findGroupParent */ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); @@ -152,6 +152,11 @@ Script.include("/~/system/libraries/controllers.js"); break; } if (entityIsGrabbable(props)) { + // if we've attempted to grab a child, roll up to the root of the tree + var groupRootProps = findGroupParent(controllerData, props); + if (entityIsGrabbable(groupRootProps)) { + return groupRootProps; + } return props; } } @@ -162,23 +167,18 @@ Script.include("/~/system/libraries/controllers.js"); this.targetEntityID = null; if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE) { - makeRunningValues(false, [], []); + return makeRunningValues(false, [], []); } var targetProps = this.getTargetProps(controllerData); if (targetProps) { if (!propsArePhysical(targetProps)) { - // XXX make sure no highlights are enabled from this module return makeRunningValues(false, [], []); // let nearParentGrabEntity handle it } else { this.targetEntityID = targetProps.id; - ContextOverlay.entityWithContextOverlay = this.targetEntityID; - ContextOverlay.enabled = true; - // XXX highlight this.targetEntityID here return makeRunningValues(true, [this.targetEntityID], []); } } else { - // XXX make sure no highlights are enabled from this module return makeRunningValues(false, [], []); } }; @@ -204,26 +204,8 @@ Script.include("/~/system/libraries/controllers.js"); var targetProps = this.getTargetProps(controllerData); if (targetProps) { - - // XXX - var rayPickInfo = controllerData.rayPicks[this.hand]; - var pointerEvent = { - type: "Move", - id: this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(rayPickInfo.entityID, rayPickInfo.intersection, targetProps), - pos3D: rayPickInfo.intersection, - normal: rayPickInfo.normal, - direction: rayPickInfo.searchRay.direction, - button: "Secondary" - }; - if (ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.entityID, pointerEvent)) { - } - // XXX - - if (controllerData.triggerClicks[this.hand] == 1) { - // stop highlighting, switch to grabbing - // XXX stop highlight here + // switch to grabbing this.startNearGrabAction(controllerData, targetProps); } } diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index c5ca95fe3b..7d5d1163bf 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -9,7 +9,8 @@ /* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, getControllerJointIndex, NULL_UUID, enableDispatcherModule, disableDispatcherModule, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, - makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS + makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS, + findGroupParent, Vec3 */ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); @@ -29,7 +30,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); this.parameters = makeDispatcherModuleParameters( 500, - this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + this.hand === RIGHT_HAND ? ["rightHand", "rightHandTrigger"] : ["leftHand", "leftHandTrigger"], [], 100); @@ -64,6 +65,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); }; this.startNearParentingGrabEntity = function (controllerData, targetProps) { + Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); var handJointIndex; @@ -135,6 +137,11 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); break; } if (entityIsGrabbable(props)) { + // if we've attempted to grab a child, roll up to the root of the tree + var groupRootProps = findGroupParent(controllerData, props); + if (entityIsGrabbable(groupRootProps)) { + return groupRootProps; + } return props; } } @@ -146,21 +153,18 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); this.grabbing = false; if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE) { - makeRunningValues(false, [], []); + return makeRunningValues(false, [], []); } var targetProps = this.getTargetProps(controllerData); if (targetProps) { if (propsArePhysical(targetProps)) { - // XXX make sure no highlights are enabled from this module return makeRunningValues(false, [], []); // let nearActionGrabEntity handle it } else { this.targetEntityID = targetProps.id; - // XXX highlight this.targetEntityID here return makeRunningValues(true, [this.targetEntityID], []); } } else { - // XXX make sure no highlights are enabled from this module return makeRunningValues(false, [], []); } }; @@ -181,8 +185,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); return readiness; } if (controllerData.triggerClicks[this.hand] == 1) { - // stop highlighting, switch to grabbing - // XXX stop highlight here + // switch to grab var targetProps = this.getTargetProps(controllerData); if (targetProps) { this.grabbing = true; diff --git a/scripts/system/controllers/controllerModules/nearTrigger.js b/scripts/system/controllers/controllerModules/nearTrigger.js new file mode 100644 index 0000000000..93f509c6b4 --- /dev/null +++ b/scripts/system/controllers/controllerModules/nearTrigger.js @@ -0,0 +1,119 @@ +"use strict"; + +// nearParentGrabEntity.js +// +// 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, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, + enableDispatcherModule, disableDispatcherModule, getGrabbableData, Vec3, + TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, makeRunningValues, NEAR_GRAB_RADIUS +*/ + +Script.include("/~/system/controllers/controllerDispatcherUtils.js"); + +(function() { + + function entityWantsNearTrigger(props) { + return getGrabbableData(props).triggerable; + } + + function NearTriggerEntity(hand) { + this.hand = hand; + this.targetEntityID = null; + this.grabbing = false; + this.previousParentID = {}; + this.previousParentJointIndex = {}; + this.previouslyUnhooked = {}; + + this.parameters = makeDispatcherModuleParameters( + 200, + this.hand === RIGHT_HAND ? ["rightHandTrigger"] : ["leftHandTrigger"], + [], + 100); + + this.getTargetProps = function (controllerData) { + // nearbyEntityProperties is already sorted by length from controller + var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand]; + for (var i = 0; i < nearbyEntityProperties.length; i++) { + var props = nearbyEntityProperties[i]; + var handPosition = controllerData.controllerLocations[this.hand].position; + var distance = Vec3.distance(props.position, handPosition); + // if (distance > NEAR_GRAB_RADIUS) { + // print("QQQ nop 0"); + // break; + // } + if (entityWantsNearTrigger(props)) { + return props; + } else { + print("QQQ nop 1"); + } + } + return null; + }; + + this.startNearTrigger = function (controllerData) { + Controller.triggerShortHapticPulse(1.0, this.hand); + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.targetEntityID, "startNearTrigger", args); + }; + + this.continueNearTrigger = function (controllerData) { + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.targetEntityID, "continueNearTrigger", args); + }; + + this.endNearTrigger = function (controllerData) { + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.targetEntityID, "endNearTrigger", args); + }; + + this.isReady = function (controllerData) { + this.targetEntityID = null; + + if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE) { + return makeRunningValues(false, [], []); + } + + var targetProps = this.getTargetProps(controllerData); + if (targetProps) { + this.targetEntityID = targetProps.id; + this.startNearTrigger(controllerData); + return makeRunningValues(true, [this.targetEntityID], []); + } else { + return makeRunningValues(false, [], []); + } + }; + + this.run = function (controllerData) { + if (controllerData.triggerClicks[this.hand] == 0) { + this.endNearTrigger(controllerData); + return makeRunningValues(false, [], []); + } + + this.continueNearTrigger(controllerData); + return makeRunningValues(true, [this.targetEntityID], []); + }; + + this.cleanup = function () { + if (this.targetEntityID) { + this.endNearParentingGrabEntity(); + } + }; + } + + var leftNearParentingGrabEntity = new NearTriggerEntity(LEFT_HAND); + var rightNearParentingGrabEntity = new NearTriggerEntity(RIGHT_HAND); + + enableDispatcherModule("LeftNearParentingGrabEntity", leftNearParentingGrabEntity); + enableDispatcherModule("RightNearParentingGrabEntity", rightNearParentingGrabEntity); + + this.cleanup = function () { + leftNearParentingGrabEntity.cleanup(); + rightNearParentingGrabEntity.cleanup(); + disableDispatcherModule("LeftNearParentingGrabEntity"); + disableDispatcherModule("RightNearParentingGrabEntity"); + }; + Script.scriptEnding.connect(this.cleanup); +}()); diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index ea45f0a2c6..d41dec6de1 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -24,7 +24,8 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/nearActionGrabEntity.js", "controllerModules/farActionGrabEntity.js", "controllerModules/tabletStylusInput.js", - "controllerModules/equipEntity.js" + "controllerModules/equipEntity.js", + "controllerModules/nearTrigger.js" ]; var DEBUG_MENU_ITEM = "Debug defaultScripts.js"; From 3b61e8518ff734dad7f99e7695fef971acc0aebc Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 15 Aug 2017 14:46:48 -0700 Subject: [PATCH 22/59] near-trigger works? equip calls its entity methods now --- .../system/controllers/controllerDispatcherUtils.js | 7 +++++-- .../controllers/controllerModules/equipEntity.js | 10 ++++++++-- .../controllers/controllerModules/nearTrigger.js | 13 +++++-------- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/scripts/system/controllers/controllerDispatcherUtils.js b/scripts/system/controllers/controllerDispatcherUtils.js index 913faf60bf..a1bf2726da 100644 --- a/scripts/system/controllers/controllerDispatcherUtils.js +++ b/scripts/system/controllers/controllerDispatcherUtils.js @@ -148,8 +148,8 @@ getGrabbableData = function (props) { } catch (err) { userDataParsed = {}; } - if (userDataParsed.grabbable) { - grabbableData = userDataParsed.grabbable; + if (userDataParsed.grabbableKey) { + grabbableData = userDataParsed.grabbableKey; } if (!grabbableData.hasOwnProperty("grabbable")) { grabbableData.grabbable = true; @@ -163,6 +163,9 @@ getGrabbableData = function (props) { if (!grabbableData.hasOwnProperty("wantsTrigger")) { grabbableData.wantsTrigger = false; } + if (!grabbableData.hasOwnProperty("triggerable")) { + grabbableData.triggerable = false; + } return grabbableData; }; diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index 34db9709b3..823247ea29 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -461,13 +461,16 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var reparentProps = { parentID: AVATAR_SELF_ID, parentJointIndex: handJointIndex, - velocity: {x: 0, y: 0, z: 0}, - angularVelocity: {x: 0, y: 0, z: 0}, + localVelocity: {x: 0, y: 0, z: 0}, + localAngularVelocity: {x: 0, y: 0, z: 0}, localPosition: this.offsetPosition, localRotation: this.offsetRotation }; Entities.editEntity(this.targetEntityID, reparentProps); + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.targetEntityID, "startEquip", args); + Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ action: 'equip', grabbedEntity: this.targetEntityID, @@ -594,6 +597,9 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa equipHotspotBuddy.update(deltaTime, timestamp, controllerData); + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.targetEntityID, "continueEquip", args); + return makeRunningValues(true, [this.targetEntityID], []); }; diff --git a/scripts/system/controllers/controllerModules/nearTrigger.js b/scripts/system/controllers/controllerModules/nearTrigger.js index 93f509c6b4..fbaa300e82 100644 --- a/scripts/system/controllers/controllerModules/nearTrigger.js +++ b/scripts/system/controllers/controllerModules/nearTrigger.js @@ -16,7 +16,8 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); (function() { function entityWantsNearTrigger(props) { - return getGrabbableData(props).triggerable; + var grabbableData = getGrabbableData(props); + return grabbableData.triggerable || grabbableData.wantsTrigger; } function NearTriggerEntity(hand) { @@ -40,21 +41,17 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); var props = nearbyEntityProperties[i]; var handPosition = controllerData.controllerLocations[this.hand].position; var distance = Vec3.distance(props.position, handPosition); - // if (distance > NEAR_GRAB_RADIUS) { - // print("QQQ nop 0"); - // break; - // } + if (distance > NEAR_GRAB_RADIUS) { + break; + } if (entityWantsNearTrigger(props)) { return props; - } else { - print("QQQ nop 1"); } } return null; }; this.startNearTrigger = function (controllerData) { - Controller.triggerShortHapticPulse(1.0, this.hand); var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.targetEntityID, "startNearTrigger", args); }; From 03977334a0c90133832be10786a62c2827426fdb Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 15 Aug 2017 14:50:39 -0700 Subject: [PATCH 23/59] near-trigger works? --- scripts/system/controllers/controllerModules/nearTrigger.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerModules/nearTrigger.js b/scripts/system/controllers/controllerModules/nearTrigger.js index fbaa300e82..c2c6e697d6 100644 --- a/scripts/system/controllers/controllerModules/nearTrigger.js +++ b/scripts/system/controllers/controllerModules/nearTrigger.js @@ -95,7 +95,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); this.cleanup = function () { if (this.targetEntityID) { - this.endNearParentingGrabEntity(); + this.endNearTrigger(); } }; } From 4bfbab294d3ef19a7dfea4874d43a42e397f5945 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 15 Aug 2017 14:53:58 -0700 Subject: [PATCH 24/59] near-trigger works? --- .../controllerModules/nearTrigger.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/scripts/system/controllers/controllerModules/nearTrigger.js b/scripts/system/controllers/controllerModules/nearTrigger.js index c2c6e697d6..8b7ef65710 100644 --- a/scripts/system/controllers/controllerModules/nearTrigger.js +++ b/scripts/system/controllers/controllerModules/nearTrigger.js @@ -1,6 +1,6 @@ "use strict"; -// nearParentGrabEntity.js +// nearTrigger.js // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -100,17 +100,17 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); }; } - var leftNearParentingGrabEntity = new NearTriggerEntity(LEFT_HAND); - var rightNearParentingGrabEntity = new NearTriggerEntity(RIGHT_HAND); + var leftNearTriggerEntity = new NearTriggerEntity(LEFT_HAND); + var rightNearTriggerEntity = new NearTriggerEntity(RIGHT_HAND); - enableDispatcherModule("LeftNearParentingGrabEntity", leftNearParentingGrabEntity); - enableDispatcherModule("RightNearParentingGrabEntity", rightNearParentingGrabEntity); + enableDispatcherModule("LeftNearTriggerEntity", leftNearTriggerEntity); + enableDispatcherModule("RightNearTriggerEntity", rightNearTriggerEntity); this.cleanup = function () { - leftNearParentingGrabEntity.cleanup(); - rightNearParentingGrabEntity.cleanup(); - disableDispatcherModule("LeftNearParentingGrabEntity"); - disableDispatcherModule("RightNearParentingGrabEntity"); + leftNearTriggerEntity.cleanup(); + rightNearTriggerEntity.cleanup(); + disableDispatcherModule("LeftNearTriggerEntity"); + disableDispatcherModule("RightNearTriggerEntity"); }; Script.scriptEnding.connect(this.cleanup); }()); From 3082c357f9f85b1fd617c2e7012ca589f3be22b0 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 15 Aug 2017 17:54:04 -0700 Subject: [PATCH 25/59] cloning sort-of works... cloning an equipable doesn't --- interface/src/avatar/AvatarManager.h | 6 +- .../controllers/controllerDispatcherUtils.js | 1 + .../controllerModules/cloneEntity.js | 160 ++++++++++++++++++ .../controllerModules/equipEntity.js | 2 +- .../controllerModules/nearParentGrabEntity.js | 8 +- .../system/controllers/controllerScripts.js | 3 +- 6 files changed, 171 insertions(+), 9 deletions(-) create mode 100644 scripts/system/controllers/controllerModules/cloneEntity.js diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index c21214484b..f595f148a8 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -73,9 +73,9 @@ public: Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersection(const PickRay& ray, const QScriptValue& avatarIdsToInclude = QScriptValue(), const QScriptValue& avatarIdsToDiscard = QScriptValue()); - Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersection(const PickRay& ray, - const QVector& avatarsToInclude, - const QVector& avatarsToDiscard); + /* Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersection(const PickRay& ray, */ + /* const QVector& avatarsToInclude, */ + /* const QVector& avatarsToDiscard); */ // TODO: remove this HACK once we settle on optimal default sort coefficients Q_INVOKABLE float getAvatarSortCoefficient(const QString& name); diff --git a/scripts/system/controllers/controllerDispatcherUtils.js b/scripts/system/controllers/controllerDispatcherUtils.js index a1bf2726da..808623fc79 100644 --- a/scripts/system/controllers/controllerDispatcherUtils.js +++ b/scripts/system/controllers/controllerDispatcherUtils.js @@ -290,6 +290,7 @@ ensureDynamic = function (entityID) { findGroupParent = function (controllerData, targetProps) { while (targetProps.parentID && targetProps.parentID != NULL_UUID) { + // XXX use controllerData.nearbyEntityPropertiesByID ? var parentProps = Entities.getEntityProperties(targetProps.parentID, DISPATCHER_PROPERTIES); if (!parentProps) { break; diff --git a/scripts/system/controllers/controllerModules/cloneEntity.js b/scripts/system/controllers/controllerModules/cloneEntity.js new file mode 100644 index 0000000000..0539ee983a --- /dev/null +++ b/scripts/system/controllers/controllerModules/cloneEntity.js @@ -0,0 +1,160 @@ +"use strict"; + +// cloneEntity.js +// +// 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, Entities, RIGHT_HAND, LEFT_HAND, + enableDispatcherModule, disableDispatcherModule, getGrabbableData, Vec3, + TRIGGER_ON_VALUE, TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, makeRunningValues, NEAR_GRAB_RADIUS +*/ + +Script.include("/~/system/controllers/controllerDispatcherUtils.js"); + +// Object assign polyfill +if (typeof Object.assign != 'function') { + Object.assign = function(target, varArgs) { + if (target === null) { + throw new TypeError('Cannot convert undefined or null to object'); + } + var to = Object(target); + for (var index = 1; index < arguments.length; index++) { + var nextSource = arguments[index]; + if (nextSource !== null) { + for (var nextKey in nextSource) { + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + return to; + }; +} + +(function() { + + function entityIsCloneable(props) { + var grabbableData = getGrabbableData(props); + return grabbableData.cloneable; + } + + function CloneEntity(hand) { + this.hand = hand; + this.grabbing = false; + this.previousParentID = {}; + this.previousParentJointIndex = {}; + this.previouslyUnhooked = {}; + + this.parameters = makeDispatcherModuleParameters( + 150, + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100); + + this.getTargetProps = function (controllerData) { + // nearbyEntityProperties is already sorted by length from controller + var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand]; + for (var i = 0; i < nearbyEntityProperties.length; i++) { + var props = nearbyEntityProperties[i]; + var handPosition = controllerData.controllerLocations[this.hand].position; + var distance = Vec3.distance(props.position, handPosition); + if (distance > NEAR_GRAB_RADIUS) { + break; + } + if (entityIsCloneable(props)) { + return props; + } + } + return null; + }; + + this.isReady = function (controllerData) { + if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE) { + this.waiting = false; + return makeRunningValues(false, [], []); + } + + if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { + if (this.waiting) { + return makeRunningValues(false, [], []); + } + this.waiting = true; + } + + var cloneableProps = this.getTargetProps(controllerData); + if (!cloneableProps) { + return makeRunningValues(false, [], []); + } + + // we need all the properties, for this + cloneableProps = Entities.getEntityProperties(cloneableProps.id); + + var worldEntityProps = controllerData.nearbyEntityProperties[this.hand]; + var count = 0; + worldEntityProps.forEach(function(itemWE) { + if (itemWE.name.indexOf('-clone-' + cloneableProps.id) !== -1) { + count++; + } + }); + + var grabInfo = getGrabbableData(cloneableProps); + + var limit = grabInfo.cloneLimit ? grabInfo.cloneLimit : 0; + if (count >= limit && limit !== 0) { + return makeRunningValues(false, [], []); + } + + cloneableProps.name = cloneableProps.name + '-clone-' + cloneableProps.id; + var lifetime = grabInfo.cloneLifetime ? grabInfo.cloneLifetime : 300; + var dynamic = grabInfo.cloneDynamic ? grabInfo.cloneDynamic : false; + var cUserData = Object.assign({}, cloneableProps.userData); + var cProperties = Object.assign({}, cloneableProps); + + try { + delete cUserData.grabbableKey.cloneLifetime; + delete cUserData.grabbableKey.cloneable; + delete cUserData.grabbableKey.cloneDynamic; + delete cUserData.grabbableKey.cloneLimit; + delete cProperties.id; + } catch(e) { + } + + cProperties.dynamic = dynamic; + cProperties.locked = false; + if (!cUserData.grabbableKey) { + cUserData.grabbableKey = {}; + } + cUserData.grabbableKey.triggerable = true; + cUserData.grabbableKey.grabbable = true; + cProperties.lifetime = lifetime; + cProperties.userData = JSON.stringify(cUserData); + // var cloneID = + Entities.addEntity(cProperties); + + return makeRunningValues(false, [], []); + }; + + this.run = function (controllerData) { + }; + + this.cleanup = function () { + }; + } + + var leftNearParentingGrabEntity = new CloneEntity(LEFT_HAND); + var rightNearParentingGrabEntity = new CloneEntity(RIGHT_HAND); + + enableDispatcherModule("LeftNearParentingGrabEntity", leftNearParentingGrabEntity); + enableDispatcherModule("RightNearParentingGrabEntity", rightNearParentingGrabEntity); + + this.cleanup = function () { + leftNearParentingGrabEntity.cleanup(); + rightNearParentingGrabEntity.cleanup(); + disableDispatcherModule("LeftNearParentingGrabEntity"); + disableDispatcherModule("RightNearParentingGrabEntity"); + }; + Script.scriptEnding.connect(this.cleanup); +}()); diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index 823247ea29..2197fdca28 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -321,7 +321,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa if (props.parentID === NULL_UUID) { hasParent = false; } - if (hasParent || entityHasActions(hotspot.entityID)) { + if (hasParent || props.locked || entityHasActions(hotspot.entityID)) { return false; } diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index 7d5d1163bf..2332fdd32c 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -82,8 +82,8 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); var reparentProps = { parentID: AVATAR_SELF_ID, parentJointIndex: handJointIndex, - velocity: {x: 0, y: 0, z: 0}, - angularVelocity: {x: 0, y: 0, z: 0} + localVelocity: {x: 0, y: 0, z: 0}, + localAngularVelocity: {x: 0, y: 0, z: 0} }; if (this.thisHandIsParent(targetProps)) { @@ -114,8 +114,8 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); Entities.editEntity(this.targetEntityID, { parentID: this.previousParentID[this.targetEntityID], parentJointIndex: this.previousParentJointIndex[this.targetEntityID], - velocity: {x: 0, y: 0, z: 0}, - angularVelocity: {x: 0, y: 0, z: 0} + localVelocity: {x: 0, y: 0, z: 0}, + localAngularVelocity: {x: 0, y: 0, z: 0} }); } diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index d41dec6de1..01b0d2ef11 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -25,7 +25,8 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/farActionGrabEntity.js", "controllerModules/tabletStylusInput.js", "controllerModules/equipEntity.js", - "controllerModules/nearTrigger.js" + "controllerModules/nearTrigger.js", + "controllerModules/cloneEntity.js" ]; var DEBUG_MENU_ITEM = "Debug defaultScripts.js"; From 243cffb98d48eb2c31f460923145bff120c32c37 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 15 Aug 2017 18:05:06 -0700 Subject: [PATCH 26/59] oops --- interface/src/avatar/AvatarManager.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index f595f148a8..c21214484b 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -73,9 +73,9 @@ public: Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersection(const PickRay& ray, const QScriptValue& avatarIdsToInclude = QScriptValue(), const QScriptValue& avatarIdsToDiscard = QScriptValue()); - /* Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersection(const PickRay& ray, */ - /* const QVector& avatarsToInclude, */ - /* const QVector& avatarsToDiscard); */ + Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersection(const PickRay& ray, + const QVector& avatarsToInclude, + const QVector& avatarsToDiscard); // TODO: remove this HACK once we settle on optimal default sort coefficients Q_INVOKABLE float getAvatarSortCoefficient(const QString& name); From 8d04047d8ac2a6f3752e328d35df9c3277a05046 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 18 Aug 2017 13:23:44 -0700 Subject: [PATCH 27/59] starting to integrate teleport.js --- .../system/controllers/controllerScripts.js | 4 +- scripts/system/controllers/teleport.js | 378 ++++++++++-------- 2 files changed, 207 insertions(+), 175 deletions(-) diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 01b0d2ef11..b69b2a6e3f 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -15,7 +15,6 @@ var CONTOLLER_SCRIPTS = [ // "handControllerGrab.js", "handControllerPointer.js", // "grab.js", - // "teleport.js", "toggleAdvancedMovementForHandControllers.js", "ControllerDispatcher.js", @@ -26,7 +25,8 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/tabletStylusInput.js", "controllerModules/equipEntity.js", "controllerModules/nearTrigger.js", - "controllerModules/cloneEntity.js" + "controllerModules/cloneEntity.js", + "teleport.js" ]; var DEBUG_MENU_ITEM = "Debug defaultScripts.js"; diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 17ca2f91c5..6d5a129b98 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -8,6 +8,17 @@ // 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, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, + getControllerJointIndex, NULL_UUID, enableDispatcherModule, disableDispatcherModule, + Messages, makeDispatcherModuleParameters, makeRunningValues, Settings, entityHasActions, + Vec3, Overlays, flatten, Xform, getControllerWorldLocation, ensureDynamic +*/ + +Script.include("/~/system/libraries/Xform.js"); +Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllers.js"); + (function() { // BEGIN LOCAL_SCOPE var inTeleportMode = false; @@ -149,8 +160,9 @@ var TARGET = { SEAT: 'seat', // The current target is a seat }; -function Teleporter() { +function Teleporter(hand) { var _this = this; + this.hand = hand; this.active = false; this.state = TELEPORTER_STATES.IDLE; this.currentTarget = TARGET.INVALID; @@ -276,6 +288,21 @@ function Teleporter() { inTeleportMode = false; }; + this.parameters = makeDispatcherModuleParameters( + 300, + //this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100); + + this.isReady = function(controllerData, deltaTime) { + print("--------> is teleport plugin ready <--------"); + return false; + }; + + this.run = function(controllerData, deltaTime) { + print("---------> running teleport plugin <--------"); + }; + this.update = function() { if (_this.state === TELEPORTER_STATES.IDLE) { return; @@ -385,191 +412,196 @@ function Teleporter() { }; } -// related to repositioning the avatar after you teleport -var FOOT_JOINT_NAMES = ["RightToe_End", "RightToeBase", "RightFoot"]; -var DEFAULT_ROOT_TO_FOOT_OFFSET = 0.5; -function getAvatarFootOffset() { + // related to repositioning the avatar after you teleport + var FOOT_JOINT_NAMES = ["RightToe_End", "RightToeBase", "RightFoot"]; + var DEFAULT_ROOT_TO_FOOT_OFFSET = 0.5; + function getAvatarFootOffset() { - // find a valid foot jointIndex - var footJointIndex = -1; - var i, l = FOOT_JOINT_NAMES.length; - for (i = 0; i < l; i++) { - footJointIndex = MyAvatar.getJointIndex(FOOT_JOINT_NAMES[i]); + // find a valid foot jointIndex + var footJointIndex = -1; + var i, l = FOOT_JOINT_NAMES.length; + for (i = 0; i < l; i++) { + footJointIndex = MyAvatar.getJointIndex(FOOT_JOINT_NAMES[i]); + if (footJointIndex != -1) { + break; + } + } if (footJointIndex != -1) { - break; - } - } - if (footJointIndex != -1) { - // default vertical offset from foot to avatar root. - var footPos = MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(footJointIndex); - if (footPos.x === 0 && footPos.y === 0 && footPos.z === 0.0) { - // if footPos is exactly zero, it's probably wrong because avatar is currently loading, fall back to default. + // default vertical offset from foot to avatar root. + var footPos = MyAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(footJointIndex); + if (footPos.x === 0 && footPos.y === 0 && footPos.z === 0.0) { + // if footPos is exactly zero, it's probably wrong because avatar is currently loading, fall back to default. + return DEFAULT_ROOT_TO_FOOT_OFFSET * MyAvatar.scale; + } else { + return -footPos.y; + } + } else { return DEFAULT_ROOT_TO_FOOT_OFFSET * MyAvatar.scale; - } else { - return -footPos.y; - } - } else { - return DEFAULT_ROOT_TO_FOOT_OFFSET * MyAvatar.scale; - } -} - -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 = 0; - -function isMoving() { - var LY = Controller.getValue(Controller.Standard.LY); - var LX = Controller.getValue(Controller.Standard.LX); - if (LY !== 0 || LX !== 0) { - return true; - } else { - return false; - } -} - -function parseJSON(json) { - try { - return JSON.parse(json); - } catch (e) { - return undefined; - } -} -// When determininig whether you can teleport to a location, the normal of the -// point that is being intersected with is looked at. If this normal is more -// than MAX_ANGLE_FROM_UP_TO_TELEPORT degrees from <0, 1, 0> (straight up), then -// you can't teleport there. -var MAX_ANGLE_FROM_UP_TO_TELEPORT = 70; -function getTeleportTargetType(result) { - if (result.type == RayPick.INTERSECTED_NONE) { - return TARGET.NONE; - } - - var props = Entities.getEntityProperties(result.objectID, ['userData', 'visible']); - var data = parseJSON(props.userData); - if (data !== undefined && data.seat !== undefined) { - var avatarUuid = Uuid.fromString(data.seat.user); - if (Uuid.isNull(avatarUuid) || !AvatarList.getAvatar(avatarUuid)) { - return TARGET.SEAT; - } else { - return TARGET.INVALID; } } - if (!props.visible) { - return TARGET.INVISIBLE; + var leftPad = new ThumbPad('left'); + var rightPad = new ThumbPad('right'); + + var mappingName, teleportMapping; + + var TELEPORT_DELAY = 0; + + function isMoving() { + var LY = Controller.getValue(Controller.Standard.LY); + var LX = Controller.getValue(Controller.Standard.LX); + if (LY !== 0 || LX !== 0) { + return true; + } else { + return false; + } } - var surfaceNormal = result.surfaceNormal; - var adj = Math.sqrt(surfaceNormal.x * surfaceNormal.x + surfaceNormal.z * surfaceNormal.z); - var angleUp = Math.atan2(surfaceNormal.y, adj) * (180 / Math.PI); + function parseJSON(json) { + try { + return JSON.parse(json); + } catch (e) { + return undefined; + } + } + // When determininig whether you can teleport to a location, the normal of the + // point that is being intersected with is looked at. If this normal is more + // than MAX_ANGLE_FROM_UP_TO_TELEPORT degrees from <0, 1, 0> (straight up), then + // you can't teleport there. + var MAX_ANGLE_FROM_UP_TO_TELEPORT = 70; + function getTeleportTargetType(result) { + if (result.type == RayPick.INTERSECTED_NONE) { + return TARGET.NONE; + } + + var props = Entities.getEntityProperties(result.objectID, ['userData', 'visible']); + var data = parseJSON(props.userData); + if (data !== undefined && data.seat !== undefined) { + var avatarUuid = Uuid.fromString(data.seat.user); + if (Uuid.isNull(avatarUuid) || !AvatarList.getAvatar(avatarUuid)) { + return TARGET.SEAT; + } else { + return TARGET.INVALID; + } + } - if (angleUp < (90 - MAX_ANGLE_FROM_UP_TO_TELEPORT) || + if (!props.visible) { + return TARGET.INVISIBLE; + } + + var surfaceNormal = result.surfaceNormal; + var adj = Math.sqrt(surfaceNormal.x * surfaceNormal.x + surfaceNormal.z * surfaceNormal.z); + var angleUp = Math.atan2(surfaceNormal.y, adj) * (180 / Math.PI); + + if (angleUp < (90 - MAX_ANGLE_FROM_UP_TO_TELEPORT) || angleUp > (90 + MAX_ANGLE_FROM_UP_TO_TELEPORT) || Vec3.distance(MyAvatar.position, result.intersection) <= TELEPORT_CANCEL_RANGE) { - return TARGET.INVALID; - } else { - return TARGET.SURFACE; - } -} - -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) - .to(function(value) { - if (isDisabled === 'left' || isDisabled === 'both') { - return; - } - if (leftTrigger.down()) { - return; - } - if (isMoving() === true) { - return; - } - teleporter.enterTeleportMode('left'); - return; - }); - teleportMapping.from(Controller.Standard.RightPrimaryThumb) - .to(function(value) { - if (isDisabled === 'right' || isDisabled === 'both') { - return; - } - if (rightTrigger.down()) { - return; - } - if (isMoving() === true) { - return; - } - - teleporter.enterTeleportMode('right'); - return; - }); -} - -registerMappings(); - -var teleporter = new Teleporter(); - -Controller.enableMapping(mappingName); - -function cleanup() { - teleportMapping.disable(); - teleporter.cleanup(); -} -Script.scriptEnding.connect(cleanup); - -var setIgnoreEntities = function() { - LaserPointers.setIgnoreEntities(teleporter.teleportRayRightVisible, ignoredEntities); - LaserPointers.setIgnoreEntities(teleporter.teleportRayRightInvisible, ignoredEntities); - LaserPointers.setIgnoreEntities(teleporter.teleportRayLeftVisible, ignoredEntities); - LaserPointers.setIgnoreEntities(teleporter.teleportRayLeftInvisible, ignoredEntities); - LaserPointers.setIgnoreEntities(teleporter.teleportRayHeadVisible, ignoredEntities); - LaserPointers.setIgnoreEntities(teleporter.teleportRayHeadInvisible, ignoredEntities); -} - -var isDisabled = false; -var handleTeleportMessages = function(channel, message, sender) { - if (sender === MyAvatar.sessionUUID) { - if (channel === 'Hifi-Teleport-Disabler') { - if (message === 'both') { - isDisabled = 'both'; - } - if (message === 'left') { - isDisabled = 'left'; - } - if (message === 'right') { - isDisabled = 'right'; - } - if (message === 'none') { - isDisabled = false; - } - } else if (channel === 'Hifi-Teleport-Ignore-Add' && !Uuid.isNull(message) && ignoredEntities.indexOf(message) === -1) { - ignoredEntities.push(message); - setIgnoreEntities(); - } else if (channel === 'Hifi-Teleport-Ignore-Remove' && !Uuid.isNull(message)) { - var removeIndex = ignoredEntities.indexOf(message); - if (removeIndex > -1) { - ignoredEntities.splice(removeIndex, 1); - setIgnoreEntities(); - } + return TARGET.INVALID; + } else { + return TARGET.SURFACE; } } -}; -Messages.subscribe('Hifi-Teleport-Disabler'); -Messages.subscribe('Hifi-Teleport-Ignore-Add'); -Messages.subscribe('Hifi-Teleport-Ignore-Remove'); -Messages.messageReceived.connect(handleTeleportMessages); + 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) + .to(function(value) { + if (isDisabled === 'left' || isDisabled === 'both') { + return; + } + if (leftTrigger.down()) { + return; + } + if (isMoving() === true) { + return; + } + //teleporter.enterTeleportMode('left'); + return; + }); + teleportMapping.from(Controller.Standard.RightPrimaryThumb) + .to(function(value) { + if (isDisabled === 'right' || isDisabled === 'both') { + return; + } + if (rightTrigger.down()) { + return; + } + if (isMoving() === true) { + return; + } + + //teleporter.enterTeleportMode('right'); + return; + });*/ + } + + registerMappings(); + + var teleporter = new Teleporter(LEFT_HAND); + var leftTeleporter = new Teleporter(LEFT_HAND); + var rightTeleporter = new Teleporter(RIGHT_HAND); + + enableDispatcherModule("LeftTeleporter", leftTeleporter); + enableDispatcherModule("RightTeleporter", rightTeleporter); + + Controller.enableMapping(mappingName); + + function cleanup() { + teleportMapping.disable(); + //teleporter.cleanup(); + disableDispatcherModule("LeftTeleporter"); + disableDispatcherModule("RightTeleporter"); + } + Script.scriptEnding.connect(cleanup); + + var setIgnoreEntities = function() { + LaserPointers.setIgnoreEntities(teleporter.teleportRayRightVisible, ignoredEntities); + LaserPointers.setIgnoreEntities(teleporter.teleportRayRightInvisible, ignoredEntities); + LaserPointers.setIgnoreEntities(teleporter.teleportRayLeftVisible, ignoredEntities); + LaserPointers.setIgnoreEntities(teleporter.teleportRayLeftInvisible, ignoredEntities); + LaserPointers.setIgnoreEntities(teleporter.teleportRayHeadVisible, ignoredEntities); + LaserPointers.setIgnoreEntities(teleporter.teleportRayHeadInvisible, ignoredEntities); + } + + var isDisabled = false; + var handleTeleportMessages = function(channel, message, sender) { + if (sender === MyAvatar.sessionUUID) { + if (channel === 'Hifi-Teleport-Disabler') { + if (message === 'both') { + isDisabled = 'both'; + } + if (message === 'left') { + isDisabled = 'left'; + } + if (message === 'right') { + isDisabled = 'right'; + } + if (message === 'none') { + isDisabled = false; + } + } else if (channel === 'Hifi-Teleport-Ignore-Add' && !Uuid.isNull(message) && ignoredEntities.indexOf(message) === -1) { + ignoredEntities.push(message); + setIgnoreEntities(); + } else if (channel === 'Hifi-Teleport-Ignore-Remove' && !Uuid.isNull(message)) { + var removeIndex = ignoredEntities.indexOf(message); + if (removeIndex > -1) { + ignoredEntities.splice(removeIndex, 1); + setIgnoreEntities(); + } + } + } + }; + + Messages.subscribe('Hifi-Teleport-Disabler'); + Messages.subscribe('Hifi-Teleport-Ignore-Add'); + Messages.subscribe('Hifi-Teleport-Ignore-Remove'); + Messages.messageReceived.connect(handleTeleportMessages); }()); // END LOCAL_SCOPE From f6151fae85dc022d6fdf1a556d59362406f7dbb5 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 21 Aug 2017 16:53:28 -0700 Subject: [PATCH 28/59] slowly intergrating teleport.js --- scripts/system/controllers/teleport.js | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 6d5a129b98..857d88ee9d 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -237,10 +237,6 @@ function Teleporter(hand) { LaserPointers.removeLaserPointer(this.teleportRayRightInvisible); LaserPointers.removeLaserPointer(this.teleportRayHeadVisible); LaserPointers.removeLaserPointer(this.teleportRayHeadInvisible); - - if (this.updateConnected === true) { - Script.update.disconnect(this, this.update); - } }; this.enterTeleportMode = function(hand) { @@ -266,15 +262,9 @@ function Teleporter(hand) { this.activeHand = hand; this.enableMappings(); - Script.update.connect(this, this.update); - this.updateConnected = true; }; this.exitTeleportMode = function(value) { - if (this.updateConnected === true) { - Script.update.disconnect(this, this.update); - } - this.disableMappings(); LaserPointers.disableLaserPointer(this.teleportRayLeftVisible); LaserPointers.disableLaserPointer(this.teleportRayLeftInvisible); @@ -298,12 +288,8 @@ function Teleporter(hand) { print("--------> is teleport plugin ready <--------"); return false; }; - + this.run = function(controllerData, deltaTime) { - print("---------> running teleport plugin <--------"); - }; - - this.update = function() { if (_this.state === TELEPORTER_STATES.IDLE) { return; } From cdd184e32d6c8fae8c6e22ea3281009cced8087b Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 22 Aug 2017 11:06:11 -0700 Subject: [PATCH 29/59] intergradted teleport --- .../controllers/controllerDispatcher.js | 3 +- scripts/system/controllers/grab.js | 8 + scripts/system/controllers/teleport.js | 227 +++++------------- 3 files changed, 76 insertions(+), 162 deletions(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 94bc9851da..116abe2c9a 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -39,7 +39,8 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); // is stored as the value, rather than false. this.activitySlots = { leftHand: false, - rightHand: false + rightHand: false, + mouse: false }; this.slotsAreAvailableForPlugin = function (plugin) { diff --git a/scripts/system/controllers/grab.js b/scripts/system/controllers/grab.js index 2844940d2b..50f9a5c5f0 100644 --- a/scripts/system/controllers/grab.js +++ b/scripts/system/controllers/grab.js @@ -21,6 +21,8 @@ (function() { // BEGIN LOCAL_SCOPE Script.include("/~/system/libraries/utils.js"); +Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllers.js"); var MAX_SOLID_ANGLE = 0.01; // objects that appear smaller than this can't be grabbed var DELAY_FOR_30HZ = 33; // milliseconds @@ -251,6 +253,12 @@ function Grabber() { z: 0 }; + this.paramters = makeDispatcherModuleParameters( + 300, + "mouse", + [], + 100); + this.targetPosition = null; this.targetRotation = null; diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 857d88ee9d..6246c0f03a 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -163,41 +163,27 @@ var TARGET = { function Teleporter(hand) { var _this = this; this.hand = hand; + this.buttonValue = 0; this.active = false; this.state = TELEPORTER_STATES.IDLE; this.currentTarget = TARGET.INVALID; + this.currentResult = null; - this.teleportRayLeftVisible = LaserPointers.createLaserPointer({ - joint: "LeftHand", + this.teleportRayHandVisible = LaserPointers.createLaserPointer({ + joint: (_this.hand === RIGHT_HAND) ? "RightHand" : "LeftHand", filter: RayPick.PICK_ENTITIES, faceAvatar: true, centerEndY: false, renderStates: teleportRenderStates, defaultRenderStates: teleportDefaultRenderStates }); - this.teleportRayLeftInvisible = LaserPointers.createLaserPointer({ - joint: "LeftHand", + this.teleportRayHandInvisible = LaserPointers.createLaserPointer({ + joint: (_this.hand === RIGHT_HAND) ? "RightHand" : "LeftHand", filter: RayPick.PICK_ENTITIES | RayPick.PICK_INCLUDE_INVISIBLE, faceAvatar: true, centerEndY: false, renderStates: teleportRenderStates }); - this.teleportRayRightVisible = LaserPointers.createLaserPointer({ - joint: "RightHand", - filter: RayPick.PICK_ENTITIES, - faceAvatar: true, - centerEndY: false, - renderStates: teleportRenderStates, - defaultRenderStates: teleportDefaultRenderStates - }); - this.teleportRayRightInvisible = LaserPointers.createLaserPointer({ - joint: "RightHand", - filter: RayPick.PICK_ENTITIES | RayPick.PICK_INCLUDE_INVISIBLE, - faceAvatar: true, - centerEndY: false, - renderStates: teleportRenderStates - }); - this.teleportRayHeadVisible = LaserPointers.createLaserPointer({ joint: "Avatar", filter: RayPick.PICK_ENTITIES, @@ -214,9 +200,6 @@ function Teleporter(hand) { renderStates: teleportRenderStates }); - this.updateConnected = null; - this.activeHand = null; - this.teleporterMappingInternalName = 'Hifi-Teleporter-Internal-Dev-' + Math.random(); this.teleportMappingInternal = Controller.newMapping(this.teleporterMappingInternalName); @@ -231,90 +214,43 @@ function Teleporter(hand) { this.cleanup = function() { this.disableMappings(); - LaserPointers.removeLaserPointer(this.teleportRayLeftVisible); - LaserPointers.removeLaserPointer(this.teleportRayLeftInvisible); - LaserPointers.removeLaserPointer(this.teleportRayRightVisible); - LaserPointers.removeLaserPointer(this.teleportRayRightInvisible); + LaserPointers.removeLaserPointer(this.teleportRayHandVisible); + LaserPointers.removeLaserPointer(this.teleportRayHandInvisible); LaserPointers.removeLaserPointer(this.teleportRayHeadVisible); LaserPointers.removeLaserPointer(this.teleportRayHeadInvisible); }; - this.enterTeleportMode = function(hand) { - if (inTeleportMode === true) { - return; - } - if (isDisabled === 'both' || isDisabled === hand) { - return; - } - - inTeleportMode = true; - - if (coolInTimeout !== null) { - Script.clearTimeout(coolInTimeout); - } - - this.state = TELEPORTER_STATES.COOL_IN; - coolInTimeout = Script.setTimeout(function() { - if (_this.state === TELEPORTER_STATES.COOL_IN) { - _this.state = TELEPORTER_STATES.TARGETTING; - } - }, COOL_IN_DURATION); - - this.activeHand = hand; - this.enableMappings(); - }; - - this.exitTeleportMode = function(value) { - this.disableMappings(); - LaserPointers.disableLaserPointer(this.teleportRayLeftVisible); - LaserPointers.disableLaserPointer(this.teleportRayLeftInvisible); - LaserPointers.disableLaserPointer(this.teleportRayRightVisible); - LaserPointers.disableLaserPointer(this.teleportRayRightInvisible); - LaserPointers.disableLaserPointer(this.teleportRayHeadVisible); - LaserPointers.disableLaserPointer(this.teleportRayHeadInvisible); - - this.updateConnected = null; - this.state = TELEPORTER_STATES.IDLE; - inTeleportMode = false; - }; - + this.buttonPress = function(value) { + _this.buttonValue = value; + } + this.parameters = makeDispatcherModuleParameters( 300, - //this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], [], 100); this.isReady = function(controllerData, deltaTime) { - print("--------> is teleport plugin ready <--------"); - return false; + if (_this.buttonValue !== 0) { + return makeRunningValues(true, [], []); + } + return makeRunningValues(false, [], []); }; this.run = function(controllerData, deltaTime) { - if (_this.state === TELEPORTER_STATES.IDLE) { - return; - } + _this.state = TELEPORTER_STATES.TARGETTING; // Get current hand pose information to see if the pose is valid - var pose = Controller.getPoseValue(handInfo[_this.activeHand].controllerInput); - var mode = pose.valid ? _this.activeHand : 'head'; + var pose = Controller.getPoseValue(handInfo[(_this.hand === RIGHT_HAND) ? 'right' : 'left'].controllerInput); + var mode = pose.valid ? _this.hand : 'head'; if (!pose.valid) { - if (mode === 'right') { - LaserPointers.disableLaserPointer(_this.teleportRayRightVisible); - LaserPointers.disableLaserPointer(_this.teleportRayRightInvisible); - } else { - LaserPointers.disableLaserPointer(_this.teleportRayLeftVisible); - LaserPointers.disableLaserPointer(_this.teleportRayLeftInvisible); - } + LaserPointers.disableLaserPointer(_this.teleportRayHandVisible); + LaserPointers.disableLaserPointer(_this.teleportRayHandInvisible); LaserPointers.enableLaserPointer(_this.teleportRayHeadVisible); LaserPointers.enableLaserPointer(_this.teleportRayHeadInvisible); } else { - if (mode === 'right') { - LaserPointers.enableLaserPointer(_this.teleportRayRightVisible); - LaserPointers.enableLaserPointer(_this.teleportRayRightInvisible); - } else { - LaserPointers.enableLaserPointer(_this.teleportRayLeftVisible); - LaserPointers.enableLaserPointer(_this.teleportRayLeftInvisible); - } + LaserPointers.enableLaserPointer(_this.teleportRayHandVisible); + LaserPointers.enableLaserPointer(_this.teleportRayHandInvisible); LaserPointers.disableLaserPointer(_this.teleportRayHeadVisible); LaserPointers.disableLaserPointer(_this.teleportRayHeadInvisible); } @@ -329,23 +265,20 @@ function Teleporter(hand) { // * In the second pass we pick against visible entities only. // var result; - if (mode === 'right') { - result = LaserPointers.getPrevRayPickResult(_this.teleportRayRightInvisible); - } else if (mode === 'left') { - result = LaserPointers.getPrevRayPickResult(_this.teleportRayLeftInvisible); - } else { + if (mode === 'head') { result = LaserPointers.getPrevRayPickResult(_this.teleportRayHeadInvisible); + } else { + result = LaserPointers.getPrevRayPickResult(_this.teleportRayHandInvisible); } var teleportLocationType = getTeleportTargetType(result); if (teleportLocationType === TARGET.INVISIBLE) { - if (mode === 'right') { - result = LaserPointers.getPrevRayPickResult(_this.teleportRayRightVisible); - } else if (mode === 'left') { - result = LaserPointers.getPrevRayPickResult(_this.teleportRayLeftVisible); - } else { + if (mode === 'head') { result = LaserPointers.getPrevRayPickResult(_this.teleportRayHeadVisible); + } else { + result = LaserPointers.getPrevRayPickResult(_this.teleportRayHandVisible); } + teleportLocationType = getTeleportTargetType(result); } @@ -363,37 +296,45 @@ function Teleporter(hand) { } else if (teleportLocationType === TARGET.SEAT) { this.setTeleportState(mode, "", "seat"); } + return this.teleport(result, teleportLocationType); + }; - - if (((_this.activeHand === 'left' ? leftPad : rightPad).buttonValue === 0) && inTeleportMode === true) { - // remember the state before we exit teleport mode and set it back to IDLE - var previousState = this.state; - this.exitTeleportMode(); - - if (teleportLocationType === TARGET.NONE || teleportLocationType === TARGET.INVALID || previousState === TELEPORTER_STATES.COOL_IN) { - // Do nothing - } else if (teleportLocationType === TARGET.SEAT) { - Entities.callEntityMethod(result.objectID, 'sit'); - } else if (teleportLocationType === TARGET.SURFACE) { - var offset = getAvatarFootOffset(); - result.intersection.y += offset; - MyAvatar.goToLocation(result.intersection, false, {x: 0, y: 0, z: 0, w: 1}, false); - HMD.centerUI(); - MyAvatar.centerBody(); - } + this.teleport = function(newResult, target) { + var result = newResult; + if (_this.buttonValue !== 0) { + return makeRunningValues(true, [], []); } + + if (target === TARGET.NONE || target === TARGET.INVALID) { + // Do nothing + } else if (target === TARGET.SEAT) { + Entities.callEntityMethod(result.objectID, 'sit'); + } else if (target === TARGET.SURFACE) { + var offset = getAvatarFootOffset(); + result.intersection.y += offset; + MyAvatar.goToLocation(result.intersection, false, {x: 0, y: 0, z: 0, w: 1}, false); + HMD.centerUI(); + MyAvatar.centerBody(); + } + + this.disableLasers(); + return makeRunningValues(false, [], []); + }; + + this.disableLasers = function() { + LaserPointers.disableLaserPointer(_this.teleportRayHandVisible); + LaserPointers.disableLaserPointer(_this.teleportRayHandInvisible); + LaserPointers.disableLaserPointer(_this.teleportRayHeadVisible); + LaserPointers.disableLaserPointer(_this.teleportRayHeadInvisible); }; this.setTeleportState = function(mode, visibleState, invisibleState) { - if (mode === 'right') { - LaserPointers.setRenderState(_this.teleportRayRightVisible, visibleState); - LaserPointers.setRenderState(_this.teleportRayRightInvisible, invisibleState); - } else if (mode === 'left') { - LaserPointers.setRenderState(_this.teleportRayLeftVisible, visibleState); - LaserPointers.setRenderState(_this.teleportRayLeftInvisible, invisibleState); - } else { + if (mode === 'head') { LaserPointers.setRenderState(_this.teleportRayHeadVisible, visibleState); LaserPointers.setRenderState(_this.teleportRayHeadInvisible, invisibleState); + } else { + LaserPointers.setRenderState(_this.teleportRayHandVisible, visibleState); + LaserPointers.setRenderState(_this.teleportRayHandInvisible, invisibleState); } }; } @@ -491,57 +432,21 @@ function Teleporter(hand) { 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) - .to(function(value) { - if (isDisabled === 'left' || isDisabled === 'both') { - return; - } - if (leftTrigger.down()) { - return; - } - if (isMoving() === true) { - return; - } - //teleporter.enterTeleportMode('left'); - return; - }); - teleportMapping.from(Controller.Standard.RightPrimaryThumb) - .to(function(value) { - if (isDisabled === 'right' || isDisabled === 'both') { - return; - } - if (rightTrigger.down()) { - return; - } - if (isMoving() === true) { - return; - } - - //teleporter.enterTeleportMode('right'); - return; - });*/ + teleportMapping.from(Controller.Standard.RightPrimaryThumb).peek().to(rightTeleporter.buttonPress); + teleportMapping.from(Controller.Standard.LeftPrimaryThumb).peek().to(leftTeleporter.buttonPress); } - - registerMappings(); - var teleporter = new Teleporter(LEFT_HAND); var leftTeleporter = new Teleporter(LEFT_HAND); var rightTeleporter = new Teleporter(RIGHT_HAND); enableDispatcherModule("LeftTeleporter", leftTeleporter); enableDispatcherModule("RightTeleporter", rightTeleporter); - + registerMappings(); Controller.enableMapping(mappingName); function cleanup() { teleportMapping.disable(); - //teleporter.cleanup(); disableDispatcherModule("LeftTeleporter"); disableDispatcherModule("RightTeleporter"); } From dc8d0c960428bfb3cca36bc3de914464dd204eac Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 22 Aug 2017 16:51:13 -0700 Subject: [PATCH 30/59] disable far grab when on hud --- .../controllers/controllerDispatcher.js | 27 ++++++++++++++++++- .../controllerModules/farActionGrabEntity.js | 6 +++++ .../controllerModules/nearTrigger.js | 1 + .../controllers/handControllerPointer.js | 1 + 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 116abe2c9a..26edc8609c 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -196,6 +196,10 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); RayPick.getPrevRayPickResult(_this.leftControllerRayPick), RayPick.getPrevRayPickResult(_this.rightControllerRayPick) ]; + var hudRayPicks = [ + RayPick.getPrevRayPickResult(_this.leftControllerHudRayPick), + RayPick.getPrevRayPickResult(_this.rightControllerHudRayPick) + ]; // if the pickray hit something very nearby, put it into the nearby entities list for (h = LEFT_HAND; h <= RIGHT_HAND; h++) { @@ -234,7 +238,8 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); nearbyEntityProperties: nearbyEntityProperties, nearbyEntityPropertiesByID: nearbyEntityPropertiesByID, nearbyOverlayIDs: nearbyOverlayIDs, - rayPicks: rayPicks + rayPicks: rayPicks, + hudRayPicks: hudRayPicks }; // check for plugins that would like to start. ask in order of increasing priority value @@ -298,18 +303,36 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); enabled: true }); + this.mouseHudRayPick = RayPick.createRayPick({ + joint: "Mouse", + filter: RayPick.PICK_HUD, + enabled: true + }); + this.leftControllerRayPick = RayPick.createRayPick({ joint: "_CONTROLLER_LEFTHAND", filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, enabled: true, maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE }); + this.leftControllerHudRayPick = RayPick.createRayPick({ + joint: "_CONTROLLER_LEFTHAND", + filter: RayPick.PICK_HUD, + enabled: true, + maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE + }); this.rightControllerRayPick = RayPick.createRayPick({ joint: "_CONTROLLER_RIGHTHAND", filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, enabled: true, maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE }); + this.rightControllerHudRayPick = RayPick.createRayPick({ + joint: "_CONTROLLER_RIGHTHAND", + filter: RayPick.PICK_HUD, + enabled: true, + maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE + }); this.cleanup = function () { @@ -318,6 +341,8 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); // RayPick.removeRayPick(_this.mouseRayPick); RayPick.removeRayPick(_this.leftControllerRayPick); RayPick.removeRayPick(_this.rightControllerRayPick); + RayPick.removeRayPick(_this.rightControllerHudRayPick); + RayPick.removeRayPick(_this.leftControllerHudRayPick); }; Script.scriptEnding.connect(this.cleanup); diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index c1b87bc6e8..a82a97b6eb 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -376,6 +376,8 @@ Script.include("/~/system/libraries/controllers.js"); } var rayPickInfo = controllerData.rayPicks[this.hand]; + var hudRayPickInfo = controllerData.hudRayPicks[this.hand]; + var hudPoint2d = HMD.overlayFromWorldPoint(hudRayPickInfo.intersection); if (rayPickInfo.type == RayPick.INTERSECTED_ENTITY) { var entityID = rayPickInfo.objectID; var targetProps = Entities.getEntityProperties(entityID, ["dynamic", "shapeType", "position", @@ -397,6 +399,10 @@ Script.include("/~/system/libraries/controllers.js"); this.startFarGrabAction(controllerData, targetProps); } } + } else if (Reticle.pointingAtSystemOverlay || Overlays.getOverlayAtPoint(hudPoint2d || Reticle.position)) { + this.endNearGrabAction(); + this.laserPointerOff(); + return makeRunningValues(false, [], []); } } return makeRunningValues(true, [], []); diff --git a/scripts/system/controllers/controllerModules/nearTrigger.js b/scripts/system/controllers/controllerModules/nearTrigger.js index 8b7ef65710..141244b60b 100644 --- a/scripts/system/controllers/controllerModules/nearTrigger.js +++ b/scripts/system/controllers/controllerModules/nearTrigger.js @@ -57,6 +57,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); }; this.continueNearTrigger = function (controllerData) { + print("-------> continue near trigger <-------"); var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.targetEntityID, "continueNearTrigger", args); }; diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 538fe0b1e4..fbbb708a92 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -639,6 +639,7 @@ function update() { // If there's a HUD element at the (newly moved) reticle, just make it visible and bail. if (isPointingAtOverlay(hudPoint2d) && isPointerEnabled) { + //print("--------> pointing at HUD <--------"); if (HMD.active) { Reticle.depth = hudReticleDistance(); From dbe8aa77d77b50044986fd6288fafe14b3873d41 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 23 Aug 2017 17:32:41 -0700 Subject: [PATCH 31/59] working on laser v tablet --- .../controllers/controllerDispatcher.js | 1 + .../controllerModules/cloneEntity.js | 23 +- .../controllerModules/farActionGrabEntity.js | 15 +- .../controllerModules/nearParentGrabEntity.js | 8 +- .../nearParentGrabOverlay.js | 19 +- .../controllerModules/nearTrigger.js | 5 +- .../controllerModules/tabletLaserInput.js | 626 ++++++++++++++++++ .../system/controllers/controllerScripts.js | 1 + 8 files changed, 676 insertions(+), 22 deletions(-) create mode 100644 scripts/system/controllers/controllerModules/tabletLaserInput.js diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 26edc8609c..b5681fef3f 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -248,6 +248,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); var candidatePlugin = controllerDispatcherPlugins[orderedPluginName]; if (_this.slotsAreAvailableForPlugin(candidatePlugin)) { + //print(orderedPluginName); var readiness = candidatePlugin.isReady(controllerData, deltaTime); if (readiness.active) { // this plugin will start. add it to the list of running plugins and mark the diff --git a/scripts/system/controllers/controllerModules/cloneEntity.js b/scripts/system/controllers/controllerModules/cloneEntity.js index 0539ee983a..cfe9cb56da 100644 --- a/scripts/system/controllers/controllerModules/cloneEntity.js +++ b/scripts/system/controllers/controllerModules/cloneEntity.js @@ -49,7 +49,7 @@ if (typeof Object.assign != 'function') { this.previouslyUnhooked = {}; this.parameters = makeDispatcherModuleParameters( - 150, + 300, this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], [], 100); @@ -78,12 +78,15 @@ if (typeof Object.assign != 'function') { } if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { - if (this.waiting) { - return makeRunningValues(false, [], []); + if (!this.waiting) { + this.waiting = true; + return makeRunningValues(true, [], []); } - this.waiting = true; } + return makeRunningValues(false, [], []); + }; + this.run = function (controllerData, deltaTime) { var cloneableProps = this.getTargetProps(controllerData); if (!cloneableProps) { return makeRunningValues(false, [], []); @@ -133,22 +136,18 @@ if (typeof Object.assign != 'function') { cProperties.userData = JSON.stringify(cUserData); // var cloneID = Entities.addEntity(cProperties); - return makeRunningValues(false, [], []); }; - this.run = function (controllerData) { - }; - this.cleanup = function () { }; } - var leftNearParentingGrabEntity = new CloneEntity(LEFT_HAND); - var rightNearParentingGrabEntity = new CloneEntity(RIGHT_HAND); + var leftCloneEntity = new CloneEntity(LEFT_HAND); + var rightCloneEntity = new CloneEntity(RIGHT_HAND); - enableDispatcherModule("LeftNearParentingGrabEntity", leftNearParentingGrabEntity); - enableDispatcherModule("RightNearParentingGrabEntity", rightNearParentingGrabEntity); + enableDispatcherModule("LeftCloneEntity", leftCloneEntity); + enableDispatcherModule("RightCloneEntity", rightCloneEntity); this.cleanup = function () { leftNearParentingGrabEntity.cleanup(); diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index a82a97b6eb..3c8fba6c24 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -317,7 +317,20 @@ Script.include("/~/system/libraries/controllers.js"); this.grabbedThingID = null; }; + this.pointingOnTablet = function(controllerData) { + var target = controllerData.rayPicks[this.hand].objectID; + + if (target === HMD.homeButtonID || target === HMD.tabletScreenID) { + return true; + } + return false + }; + this.isReady = function (controllerData) { + if (this.pointingOnTablet(controllerData)) { + return makeRunningValues(false, [], []); + } + this.distanceHolding = false; this.distanceRotating = false; @@ -330,7 +343,7 @@ Script.include("/~/system/libraries/controllers.js"); }; this.run = function (controllerData) { - if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE) { + if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || this.pointingOnTablet(controllerData)) { this.endNearGrabAction(); this.laserPointerOff(); return makeRunningValues(false, [], []); diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index 2332fdd32c..cb004acd03 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -30,7 +30,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); this.parameters = makeDispatcherModuleParameters( 500, - this.hand === RIGHT_HAND ? ["rightHand", "rightHandTrigger"] : ["leftHand", "leftHandTrigger"], + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], [], 100); @@ -134,7 +134,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); var handPosition = controllerData.controllerLocations[this.hand].position; var distance = Vec3.distance(props.position, handPosition); if (distance > NEAR_GRAB_RADIUS) { - break; + continue; } if (entityIsGrabbable(props)) { // if we've attempted to grab a child, roll up to the root of the tree @@ -148,7 +148,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); return null; }; - this.isReady = function (controllerData) { + this.isReady = function (controllerData, deltaTime) { this.targetEntityID = null; this.grabbing = false; @@ -169,7 +169,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); } }; - this.run = function (controllerData) { + this.run = function (controllerData, deltaTime) { if (this.grabbing) { if (controllerData.triggerClicks[this.hand] == 0) { this.endNearParentingGrabEntity(); diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js index 58b6a12090..399814a0ed 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -13,6 +13,7 @@ */ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +var GRAB_RADIUS = 0.35; (function() { @@ -123,6 +124,19 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); this.grabbedThingID = null; }; + this.getTargetID = function(overlays, controllerData) { + for (var i = 0; i < overlays.length; i++) { + var overlayPosition = Overlays.getProperty(overlays[i], "position"); + var handPosition = controllerData.controllerLocations[this.hand].position; + var distance = Vec3.distance(overlayPosition, handPosition); + if (distance <= GRAB_RADIUS) { + return overlays[i]; + } + } + return null; + }; + + this.isReady = function (controllerData) { if (controllerData.triggerClicks[this.hand] == 0) { return makeRunningValues(false, [], []); @@ -135,8 +149,9 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); return Overlays.getProperty(overlayID, "grabbable"); }); - if (grabbableOverlays.length > 0) { - this.grabbedThingID = grabbableOverlays[0]; + var targetID = this.getTargetID(grabbableOverlays, controllerData); + if (targetID) { + this.grabbedThingID = targetID; this.startNearParentingGrabOverlay(controllerData); return makeRunningValues(true, [this.grabbedThingID], []); } else { diff --git a/scripts/system/controllers/controllerModules/nearTrigger.js b/scripts/system/controllers/controllerModules/nearTrigger.js index 141244b60b..fd24aeefa2 100644 --- a/scripts/system/controllers/controllerModules/nearTrigger.js +++ b/scripts/system/controllers/controllerModules/nearTrigger.js @@ -29,7 +29,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); this.previouslyUnhooked = {}; this.parameters = makeDispatcherModuleParameters( - 200, + 520, this.hand === RIGHT_HAND ? ["rightHandTrigger"] : ["leftHandTrigger"], [], 100); @@ -42,7 +42,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); var handPosition = controllerData.controllerLocations[this.hand].position; var distance = Vec3.distance(props.position, handPosition); if (distance > NEAR_GRAB_RADIUS) { - break; + continue; } if (entityWantsNearTrigger(props)) { return props; @@ -57,7 +57,6 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); }; this.continueNearTrigger = function (controllerData) { - print("-------> continue near trigger <-------"); var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.targetEntityID, "continueNearTrigger", args); }; diff --git a/scripts/system/controllers/controllerModules/tabletLaserInput.js b/scripts/system/controllers/controllerModules/tabletLaserInput.js new file mode 100644 index 0000000000..c7098d4e9a --- /dev/null +++ b/scripts/system/controllers/controllerModules/tabletLaserInput.js @@ -0,0 +1,626 @@ +"use strict" + +// tabletLaserInput.js +// +// 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, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, + NULL_UUID, enableDispatcherModule, disableDispatcherModule, makeRunningValues, + Messages, Quat, Vec3, getControllerWorldLocation, makeDispatcherModuleParameters, Overlays, ZERO_VEC, + AVATAR_SELF_ID, HMD, INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, Settings, getGrabPointSphereOffset +*/ + + + +Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllers.js"); + +(function() { + 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 + drawInFront: 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, + drawInFront: 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 + drawInFront: 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, + drawInFront: 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 + drawInFront: 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; + + function stylusTargetHasKeyboardFocus(stylusTarget) { + if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { + return Entities.keyboardFocusEntity === stylusTarget.entityID; + } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { + return Overlays.keyboardFocusOverlay === stylusTarget.overlayID; + } + } + + function setKeyboardFocusOnStylusTarget(stylusTarget) { + if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID && + Entities.wantsHandControllerPointerEvents(stylusTarget.entityID)) { + Overlays.keyboardFocusOverlay = NULL_UUID; + Entities.keyboardFocusEntity = stylusTarget.entityID; + } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { + Overlays.keyboardFocusOverlay = stylusTarget.overlayID; + Entities.keyboardFocusEntity = NULL_UUID; + } + } + + function sendHoverEnterEventToStylusTarget(hand, stylusTarget) { + var pointerEvent = { + type: "Move", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: stylusTarget.position2D, + pos3D: stylusTarget.position, + normal: stylusTarget.normal, + direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), + button: "None" + }; + + if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { + Entities.sendHoverEnterEntity(stylusTarget.entityID, pointerEvent); + } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { + Overlays.sendHoverEnterOverlay(stylusTarget.overlayID, pointerEvent); + } + } + + function sendHoverOverEventToStylusTarget(hand, stylusTarget) { + var pointerEvent = { + type: "Move", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: stylusTarget.position2D, + pos3D: stylusTarget.position, + normal: stylusTarget.normal, + direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), + button: "None" + }; + + if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { + Entities.sendMouseMoveOnEntity(stylusTarget.entityID, pointerEvent); + Entities.sendHoverOverEntity(stylusTarget.entityID, pointerEvent); + } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { + Overlays.sendMouseMoveOnOverlay(stylusTarget.overlayID, pointerEvent); + Overlays.sendHoverOverOverlay(stylusTarget.overlayID, pointerEvent); + } + } + + function sendTouchStartEventToStylusTarget(hand, stylusTarget) { + var pointerEvent = { + type: "Press", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: stylusTarget.position2D, + pos3D: stylusTarget.position, + normal: stylusTarget.normal, + direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), + button: "Primary", + isPrimaryHeld: true + }; + + if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { + Entities.sendMousePressOnEntity(stylusTarget.entityID, pointerEvent); + Entities.sendClickDownOnEntity(stylusTarget.entityID, pointerEvent); + } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { + Overlays.sendMousePressOnOverlay(stylusTarget.overlayID, pointerEvent); + } + } + + function sendTouchEndEventToStylusTarget(hand, stylusTarget) { + var pointerEvent = { + type: "Release", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: stylusTarget.position2D, + pos3D: stylusTarget.position, + normal: stylusTarget.normal, + direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), + button: "Primary" + }; + + if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { + Entities.sendMouseReleaseOnEntity(stylusTarget.entityID, pointerEvent); + Entities.sendClickReleaseOnEntity(stylusTarget.entityID, pointerEvent); + Entities.sendHoverLeaveEntity(stylusTarget.entityID, pointerEvent); + } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { + Overlays.sendMouseReleaseOnOverlay(stylusTarget.overlayID, pointerEvent); + } + } + + function sendTouchMoveEventToStylusTarget(hand, stylusTarget) { + var pointerEvent = { + type: "Move", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: stylusTarget.position2D, + pos3D: stylusTarget.position, + normal: stylusTarget.normal, + direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), + button: "Primary", + isPrimaryHeld: true + }; + + if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { + Entities.sendMouseMoveOnEntity(stylusTarget.entityID, pointerEvent); + Entities.sendHoldingClickOnEntity(stylusTarget.entityID, pointerEvent); + } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { + Overlays.sendMouseMoveOnOverlay(stylusTarget.overlayID, pointerEvent); + } + } + + // will return undefined if overlayID does not exist. + function calculateStylusTargetFromOverlay(stylusTip, overlayID) { + var overlayPosition = Overlays.getProperty(overlayID, "position"); + if (overlayPosition === undefined) { + return; + } + + // project stylusTip onto overlay plane. + var overlayRotation = Overlays.getProperty(overlayID, "rotation"); + if (overlayRotation === undefined) { + return; + } + var normal = Vec3.multiplyQbyV(overlayRotation, {x: 0, y: 0, z: 1}); + var distance = Vec3.dot(Vec3.subtract(stylusTip.position, overlayPosition), normal); + var position = Vec3.subtract(stylusTip.position, Vec3.multiply(normal, distance)); + + // calclulate normalized position + var invRot = Quat.inverse(overlayRotation); + var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, overlayPosition)); + var dpi = Overlays.getProperty(overlayID, "dpi"); + + var dimensions; + if (dpi) { + // Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property + // is used as a scale. + var resolution = Overlays.getProperty(overlayID, "resolution"); + if (resolution === undefined) { + return; + } + resolution.z = 1; // Circumvent divide-by-zero. + var scale = Overlays.getProperty(overlayID, "dimensions"); + if (scale === undefined) { + return; + } + scale.z = 0.01; // overlay dimensions are 2D, not 3D. + dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale); + } else { + dimensions = Overlays.getProperty(overlayID, "dimensions"); + if (dimensions === undefined) { + return; + } + if (!dimensions.z) { + dimensions.z = 0.01; // sometimes overlay dimensions are 2D, not 3D. + } + } + var invDimensions = { x: 1 / dimensions.x, y: 1 / dimensions.y, z: 1 / dimensions.z }; + var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), DEFAULT_REGISTRATION_POINT); + + // 2D position on overlay plane in meters, relative to the bounding box upper-left hand corner. + var position2D = { x: normalizedPosition.x * dimensions.x, + y: (1 - normalizedPosition.y) * dimensions.y }; // flip y-axis + + return { + entityID: null, + overlayID: overlayID, + distance: distance, + position: position, + position2D: position2D, + normal: normal, + normalizedPosition: normalizedPosition, + dimensions: dimensions, + valid: true + }; + } + + // will return undefined if entity does not exist. + function calculateStylusTargetFromEntity(stylusTip, props) { + if (props.rotation === undefined) { + // if rotation is missing from props object, then this entity has probably been deleted. + return; + } + + // project stylus tip onto entity plane. + var normal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1}); + Vec3.multiplyQbyV(props.rotation, {x: 0, y: 1, z: 0}); + var distance = Vec3.dot(Vec3.subtract(stylusTip.position, props.position), normal); + var position = Vec3.subtract(stylusTip.position, Vec3.multiply(normal, distance)); + + // generate normalized coordinates + var invRot = Quat.inverse(props.rotation); + var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, props.position)); + var invDimensions = { x: 1 / props.dimensions.x, y: 1 / props.dimensions.y, z: 1 / props.dimensions.z }; + var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), props.registrationPoint); + + // 2D position on entity plane in meters, relative to the bounding box upper-left hand corner. + var position2D = { x: normalizedPosition.x * props.dimensions.x, + y: (1 - normalizedPosition.y) * props.dimensions.y }; // flip y-axis + + return { + entityID: props.id, + entityProps: props, + overlayID: null, + distance: distance, + position: position, + position2D: position2D, + normal: normal, + normalizedPosition: normalizedPosition, + dimensions: props.dimensions, + valid: true + }; + } + + function isNearStylusTarget(stylusTargets, edgeBorder, minNormalDistance, maxNormalDistance) { + for (var i = 0; i < stylusTargets.length; i++) { + var stylusTarget = stylusTargets[i]; + + // check to see if the projected stylusTip is within within the 2d border + var borderMin = {x: -edgeBorder, y: -edgeBorder}; + var borderMax = {x: stylusTarget.dimensions.x + edgeBorder, y: stylusTarget.dimensions.y + edgeBorder}; + if (stylusTarget.distance >= minNormalDistance && stylusTarget.distance <= maxNormalDistance && + stylusTarget.position2D.x >= borderMin.x && stylusTarget.position2D.y >= borderMin.y && + stylusTarget.position2D.x <= borderMax.x && stylusTarget.position2D.y <= borderMax.y) { + return true; + } + } + return false; + } + + function calculateNearestStylusTarget(stylusTargets) { + var nearestStylusTarget; + + for (var i = 0; i < stylusTargets.length; i++) { + var stylusTarget = stylusTargets[i]; + + if ((!nearestStylusTarget || stylusTarget.distance < nearestStylusTarget.distance) && + stylusTarget.normalizedPosition.x >= 0 && stylusTarget.normalizedPosition.y >= 0 && + stylusTarget.normalizedPosition.x <= 1 && stylusTarget.normalizedPosition.y <= 1) { + nearestStylusTarget = stylusTarget; + } + } + + return nearestStylusTarget; + } + + function getFingerWorldLocation(hand) { + var fingerJointName = (hand === RIGHT_HAND) ? "RightHandIndex4" : "LeftHandIndex4"; + + var fingerJointIndex = MyAvatar.getJointIndex(fingerJointName); + var fingerPosition = MyAvatar.getAbsoluteJointTranslationInObjectFrame(fingerJointIndex); + var fingerRotation = MyAvatar.getAbsoluteJointRotationInObjectFrame(fingerJointIndex); + var worldFingerRotation = Quat.multiply(MyAvatar.orientation, fingerRotation); + var worldFingerPosition = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, fingerPosition)); + + return { + position: worldFingerPosition, + orientation: worldFingerRotation, + rotation: worldFingerRotation, + valid: true + }; + } + + function distance2D(a, b) { + var dx = (a.x - b.x); + var dy = (a.y - b.y); + return Math.sqrt(dx * dx + dy * dy); + } + + function TabletLaserInput(hand) { + this.hand = hand; + this.previousStylusTouchingTarget = false; + this.stylusTouchingTarget = false; + this.tabletScreenID = HMD.tabletScreenID; + + this.useFingerInsteadOfStylus = false; + this.fingerPointing = false; + + + this.parameters = makeDispatcherModuleParameters( + 200, + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100); + + this.getOtherHandController = function() { + return (this.hand === RIGHT_HAND) ? Controller.Standard.LeftHand : Controller.Standard.RightHand; + }; + + this.handToController = function() { + return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + }; + + this.stealTouchFocus = function(laserTarget) { + // send hover events to target + // record the entity or overlay we are hovering over. + if ((stylusTarget.entityID === this.getOtherHandController().hoverEntity) || + (stylusTarget.overlayID === this.getOtherHandController().hoverOverlay)) { + this.getOtherHandController().relinquishTouchFocus(); + } + this.requestTouchFocus(stylusTarget); + }; + + this.requestTouchFocus = function(laserTarget) { + + // send hover events to target if we can. + // record the entity or overlay we are hovering over. + if (stylusTarget.entityID && + stylusTarget.entityID !== this.hoverEntity && + stylusTarget.entityID !== this.getOtherHandController().hoverEntity) { + this.hoverEntity = stylusTarget.entityID; + sendHoverEnterEventToStylusTarget(this.hand, stylusTarget); + } else if (stylusTarget.overlayID && + stylusTarget.overlayID !== this.hoverOverlay && + stylusTarget.overlayID !== this.getOtherHandController().hoverOverlay) { + this.hoverOverlay = stylusTarget.overlayID; + sendHoverEnterEventToStylusTarget(this.hand, stylusTarget); + } + }; + + this.hasTouchFocus = function(laserTarget) { + return ((stylusTarget.entityID && stylusTarget.entityID === this.hoverEntity) || + (stylusTarget.overlayID && stylusTarget.overlayID === this.hoverOverlay)); + }; + + this.relinquishTouchFocus = function() { + // send hover leave event. + var pointerEvent = { type: "Move", id: this.hand + 1 }; + if (this.hoverEntity) { + Entities.sendHoverLeaveEntity(this.hoverEntity, pointerEvent); + this.hoverEntity = null; + } else if (this.hoverOverlay) { + Overlays.sendMouseMoveOnOverlay(HMD.tabletScreenID, pointerEvent); + Overlays.sendHoverOverOverlay(this.hoverOverlay, pointerEvent); + Overlays.sendHoverLeaveOverlay(this.hoverOverlay, pointerEvent); + this.hoverOverlay = null; + } + }; + + this.updateLaserPointer = function(controllerData) { + var RADIUS = 0.005; + var dim = { x: RADIUS, y: RADIUS, z: RADIUS }; + + var mode = "none"; + + if (controllerData.triggerClicks[this.hand]) { + mode = "full"; + } else if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { + mode = "half"; + } else { + LaserPointers.disableLaserPointer(this.laserPointer); + return; + } + + if (mode === "full") { + this.fullEnd.dimensions = dim; + LaserPointers.editRenderState(this.laserPointer, mode, {path: fullPath, end: this.fullEnd}); + } else if (mode === "half") { + this.halfEnd.dimensions = dim; + LaserPointers.editRenderState(this.laserPointer, mode, {path: halfPath, end: this.halfEnd}); + } + + LaserPointers.enableLaserPointer(this.laserPointer); + LaserPointers.setRenderState(this.laserPointer, mode); + }; + + this.pointFinger = function(value) { + var HIFI_POINT_INDEX_MESSAGE_CHANNEL = "Hifi-Point-Index"; + if (this.fingerPointing !== value) { + var message; + if (this.hand === RIGHT_HAND) { + message = { pointRightIndex: value }; + } else { + message = { pointLeftIndex: value }; + } + Messages.sendMessage(HIFI_POINT_INDEX_MESSAGE_CHANNEL, JSON.stringify(message), true); + this.fingerPointing = value; + } + }; + + this.processLaser = function(controllerData) { + if (controllerData.rayPicks[this.hand].objectID === HMD.tabletScreenID) { + + } + this.homeButtonTouched = false; + }; + + this.laserTouchingEnter = function () { + this.stealTouchFocus(this.stylusTarget); + sendTouchStartEventToStylusTarget(this.hand, this.stylusTarget); + Controller.triggerHapticPulse(HAPTIC_STYLUS_STRENGTH, HAPTIC_STYLUS_DURATION, this.hand); + + this.touchingEnterTimer = 0; + this.touchingEnterStylusTarget = this.stylusTarget; + this.deadspotExpired = false; + + var TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.0381; + this.deadspotRadius = TOUCH_PRESS_TO_MOVE_DEADSPOT; + }; + + this.laserTouchingExit = function () { + + if (this.stylusTarget === undefined) { + return; + } + + // special case to handle home button. + if (this.stylusTarget.overlayID === HMD.homeButtonID) { + Messages.sendLocalMessage("home", this.stylusTarget.overlayID); + } + + // send press event + if (this.deadspotExpired) { + sendTouchEndEventToStylusTarget(this.hand, this.stylusTarget); + } else { + sendTouchEndEventToStylusTarget(this.hand, this.touchingEnterStylusTarget); + } + }; + + this.laserTouching = function (controllerData, dt) { + + this.touchingEnterTimer += dt; + + if (this.stylusTarget.entityID) { + this.stylusTarget = calculateStylusTargetFromEntity(this.stylusTip, this.stylusTarget.entityProps); + } else if (this.stylusTarget.overlayID) { + this.stylusTarget = calculateStylusTargetFromOverlay(this.stylusTip, this.stylusTarget.overlayID); + } + + var TABLET_MIN_TOUCH_DISTANCE = -0.1; + var TABLET_MAX_TOUCH_DISTANCE = 0.01; + + if (this.stylusTarget) { + if (this.stylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE && + this.stylusTarget.distance < TABLET_MAX_TOUCH_DISTANCE) { + var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds + if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || + distance2D(this.stylusTarget.position2D, + this.touchingEnterStylusTarget.position2D) > this.deadspotRadius) { + sendTouchMoveEventToStylusTarget(this.hand, this.stylusTarget); + this.deadspotExpired = true; + } + } else { + this.stylusTouchingTarget = false; + } + } else { + this.stylusTouchingTarget = false; + } + }; + + this.pointingAtTablet = function(target) { + return (target === HMD.tabletID); + }; + + this.pointingAtTabletScreen = function(target) { + return (target === HMD.tabletScreenID); + } + + this.pointingAtHomeButton = function(target) { + return (target === HMD.homeButtonID); + } + + this.isReady = function (controllerData) { + var target = controllerData.rayPicks[this.hand].objectID; + if (this.pointingAtTabletScreen(target) || this.pointingAtHomeButton(target) || this.pointingAtTablet(target)) { + if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { + return makeRunningValues(true, [], []); + } + } + //this.processLaser(controllerData); + return makeRunningValues(false, [], []); + }; + + this.run = function (controllerData, deltaTime) { + var target = controllerData.rayPicks[this.hand].objectID; + if (!this.pointingAtTabletScreen(target) && !this.pointingAtHomeButton(target) && !this.pointingAtTablet(target)) { + LaserPointers.disableLaserPointer(this.laserPointer); + return makeRunningValues(false, [], []); + } + this.updateLaserPointer(controllerData); + //this.updateFingerAsStylusSetting(); + +/* if (!this.previousStylusTouchingTarget && this.stylusTouchingTarget) { + this.stylusTouchingEnter(); + } + if (this.previousStylusTouchingTarget && !this.stylusTouchingTarget) { + this.stylusTouchingExit(); + } + this.previousStylusTouchingTarget = this.stylusTouchingTarget; + + if (this.stylusTouchingTarget) { + this.stylusTouching(controllerData, deltaTime); + }*/ + /*if (this.processLaser(controllerData)) { + return makeRunningValues(true, [], []); + } else { + return makeRunningValues(false, [], []); + }*/ + + return makeRunningValues(true, [], []); + }; + + 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) ? "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", + filter: RayPick.PICK_OVERLAYS, + maxDistance: PICK_MAX_DISTANCE, + posOffset: getGrabPointSphereOffset(this.handToController()), + renderStates: renderStates, + faceAvatar: true, + defaultRenderStates: defaultRenderStates + }); + }; + + var leftTabletLaserInput = new TabletLaserInput(LEFT_HAND); + var rightTabletLaserInput = new TabletLaserInput(RIGHT_HAND); + + enableDispatcherModule("LeftTabletLaserInput", leftTabletLaserInput); + enableDispatcherModule("RightTabletLaserInput", rightTabletLaserInput); + + this.cleanup = function () { + leftTabletStylusInput.cleanup(); + rightTabletStylusInput.cleanup(); + disableDispatcherModule("LeftTabletLaserInput"); + disableDispatcherModule("RightTabletLaserInput"); + }; + Script.scriptEnding.connect(this.cleanup); +}()); diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index b69b2a6e3f..65d63b7bec 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -26,6 +26,7 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/equipEntity.js", "controllerModules/nearTrigger.js", "controllerModules/cloneEntity.js", + "controllerModules/tabletLaserInput.js", "teleport.js" ]; From fa676aaadf687ce3839ef2fcae12066a8ec7984a Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 24 Aug 2017 17:42:27 -0700 Subject: [PATCH 32/59] lasers on tablet working --- .../controllerModules/tabletLaserInput.js | 444 +++++++----------- 1 file changed, 170 insertions(+), 274 deletions(-) diff --git a/scripts/system/controllers/controllerModules/tabletLaserInput.js b/scripts/system/controllers/controllerModules/tabletLaserInput.js index c7098d4e9a..992caeac48 100644 --- a/scripts/system/controllers/controllerModules/tabletLaserInput.js +++ b/scripts/system/controllers/controllerModules/tabletLaserInput.js @@ -85,125 +85,121 @@ Script.include("/~/system/libraries/controllers.js"); var HAPTIC_STYLUS_STRENGTH = 1.0; var HAPTIC_STYLUS_DURATION = 20.0; - function stylusTargetHasKeyboardFocus(stylusTarget) { - if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { - return Entities.keyboardFocusEntity === stylusTarget.entityID; - } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { - return Overlays.keyboardFocusOverlay === stylusTarget.overlayID; + function stylusTargetHasKeyboardFocus(laserTarget) { + if (laserTarget && laserTarget !== NULL_UUID) { + return Overlays.keyboardFocusOverlay === laserTarget; } } - function setKeyboardFocusOnStylusTarget(stylusTarget) { - if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID && - Entities.wantsHandControllerPointerEvents(stylusTarget.entityID)) { - Overlays.keyboardFocusOverlay = NULL_UUID; - Entities.keyboardFocusEntity = stylusTarget.entityID; - } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { - Overlays.keyboardFocusOverlay = stylusTarget.overlayID; + function setKeyboardFocusOnStylusTarget(laserTarget) { + if (laserTarget && laserTarget !== NULL_UUID) { + Overlays.keyboardFocusOverlay = laserTarget; Entities.keyboardFocusEntity = NULL_UUID; } } - function sendHoverEnterEventToStylusTarget(hand, stylusTarget) { + function sendHoverEnterEventToStylusTarget(hand, laserTarget) { + if (!laserTarget) { + return; + } var pointerEvent = { type: "Move", id: hand + 1, // 0 is reserved for hardware mouse - pos2D: stylusTarget.position2D, - pos3D: stylusTarget.position, - normal: stylusTarget.normal, - direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), + pos2D: laserTarget.position2D, + pos3D: laserTarget.position, + normal: laserTarget.normal, + direction: Vec3.subtract(ZERO_VEC, laserTarget.normal), button: "None" }; - if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { - Entities.sendHoverEnterEntity(stylusTarget.entityID, pointerEvent); - } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { - Overlays.sendHoverEnterOverlay(stylusTarget.overlayID, pointerEvent); + if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) { + Overlays.sendHoverEnterOverlay(laserTarget.overlayID, pointerEvent); } } - function sendHoverOverEventToStylusTarget(hand, stylusTarget) { + function sendHoverOverEventToStylusTarget(hand, laserTarget) { + + if (!laserTarget) { + return; + } var pointerEvent = { type: "Move", id: hand + 1, // 0 is reserved for hardware mouse - pos2D: stylusTarget.position2D, - pos3D: stylusTarget.position, - normal: stylusTarget.normal, - direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), + pos2D: laserTarget.position2D, + pos3D: laserTarget.position, + normal: laserTarget.normal, + direction: Vec3.subtract(ZERO_VEC, laserTarget.normal), button: "None" }; - if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { - Entities.sendMouseMoveOnEntity(stylusTarget.entityID, pointerEvent); - Entities.sendHoverOverEntity(stylusTarget.entityID, pointerEvent); - } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { - Overlays.sendMouseMoveOnOverlay(stylusTarget.overlayID, pointerEvent); - Overlays.sendHoverOverOverlay(stylusTarget.overlayID, pointerEvent); + if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) { + Overlays.sendMouseMoveOnOverlay(laserTarget.overlayID, pointerEvent); + Overlays.sendHoverOverOverlay(laserTarget.overlayID, pointerEvent); } } - function sendTouchStartEventToStylusTarget(hand, stylusTarget) { + function sendTouchStartEventToStylusTarget(hand, laserTarget) { + if (!laserTarget) { + return; + } + var pointerEvent = { type: "Press", id: hand + 1, // 0 is reserved for hardware mouse - pos2D: stylusTarget.position2D, - pos3D: stylusTarget.position, - normal: stylusTarget.normal, - direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), + pos2D: laserTarget.position2D, + pos3D: laserTarget.position, + normal: laserTarget.normal, + direction: Vec3.subtract(ZERO_VEC, laserTarget.normal), button: "Primary", isPrimaryHeld: true }; - if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { - Entities.sendMousePressOnEntity(stylusTarget.entityID, pointerEvent); - Entities.sendClickDownOnEntity(stylusTarget.entityID, pointerEvent); - } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { - Overlays.sendMousePressOnOverlay(stylusTarget.overlayID, pointerEvent); + if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) { + Overlays.sendMousePressOnOverlay(laserTarget.overlayID, pointerEvent); } } - function sendTouchEndEventToStylusTarget(hand, stylusTarget) { + function sendTouchEndEventToStylusTarget(hand, laserTarget) { + if (!laserTarget) { + return; + } var pointerEvent = { type: "Release", id: hand + 1, // 0 is reserved for hardware mouse - pos2D: stylusTarget.position2D, - pos3D: stylusTarget.position, - normal: stylusTarget.normal, - direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), + pos2D: laserTarget.position2D, + pos3D: laserTarget.position, + normal: laserTarget.normal, + direction: Vec3.subtract(ZERO_VEC, laserTarget.normal), button: "Primary" }; - if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { - Entities.sendMouseReleaseOnEntity(stylusTarget.entityID, pointerEvent); - Entities.sendClickReleaseOnEntity(stylusTarget.entityID, pointerEvent); - Entities.sendHoverLeaveEntity(stylusTarget.entityID, pointerEvent); - } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { - Overlays.sendMouseReleaseOnOverlay(stylusTarget.overlayID, pointerEvent); + if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) { + Overlays.sendMouseReleaseOnOverlay(laserTarget.overlayID, pointerEvent); } } - function sendTouchMoveEventToStylusTarget(hand, stylusTarget) { + function sendTouchMoveEventToStylusTarget(hand, laserTarget) { + if (!laserTarget) { + return; + } var pointerEvent = { type: "Move", id: hand + 1, // 0 is reserved for hardware mouse - pos2D: stylusTarget.position2D, - pos3D: stylusTarget.position, - normal: stylusTarget.normal, - direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), + pos2D: laserTarget.position2D, + pos3D: laserTarget.position, + normal: laserTarget.normal, + direction: Vec3.subtract(ZERO_VEC, laserTarget.normal), button: "Primary", isPrimaryHeld: true }; - - if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { - Entities.sendMouseMoveOnEntity(stylusTarget.entityID, pointerEvent); - Entities.sendHoldingClickOnEntity(stylusTarget.entityID, pointerEvent); - } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { - Overlays.sendMouseMoveOnOverlay(stylusTarget.overlayID, pointerEvent); + + if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) { + Overlays.sendMouseReleaseOnOverlay(laserTarget.overlayID, pointerEvent); } } // will return undefined if overlayID does not exist. - function calculateStylusTargetFromOverlay(stylusTip, overlayID) { + function calculateLaserTargetFromOverlay(laserTip, overlayID) { var overlayPosition = Overlays.getProperty(overlayID, "position"); if (overlayPosition === undefined) { return; @@ -215,8 +211,8 @@ Script.include("/~/system/libraries/controllers.js"); return; } var normal = Vec3.multiplyQbyV(overlayRotation, {x: 0, y: 0, z: 1}); - var distance = Vec3.dot(Vec3.subtract(stylusTip.position, overlayPosition), normal); - var position = Vec3.subtract(stylusTip.position, Vec3.multiply(normal, distance)); + var distance = Vec3.dot(Vec3.subtract(laserTip, overlayPosition), normal); + var position = Vec3.subtract(laserTip, Vec3.multiply(normal, distance)); // calclulate normalized position var invRot = Quat.inverse(overlayRotation); @@ -267,92 +263,6 @@ Script.include("/~/system/libraries/controllers.js"); }; } - // will return undefined if entity does not exist. - function calculateStylusTargetFromEntity(stylusTip, props) { - if (props.rotation === undefined) { - // if rotation is missing from props object, then this entity has probably been deleted. - return; - } - - // project stylus tip onto entity plane. - var normal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1}); - Vec3.multiplyQbyV(props.rotation, {x: 0, y: 1, z: 0}); - var distance = Vec3.dot(Vec3.subtract(stylusTip.position, props.position), normal); - var position = Vec3.subtract(stylusTip.position, Vec3.multiply(normal, distance)); - - // generate normalized coordinates - var invRot = Quat.inverse(props.rotation); - var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, props.position)); - var invDimensions = { x: 1 / props.dimensions.x, y: 1 / props.dimensions.y, z: 1 / props.dimensions.z }; - var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), props.registrationPoint); - - // 2D position on entity plane in meters, relative to the bounding box upper-left hand corner. - var position2D = { x: normalizedPosition.x * props.dimensions.x, - y: (1 - normalizedPosition.y) * props.dimensions.y }; // flip y-axis - - return { - entityID: props.id, - entityProps: props, - overlayID: null, - distance: distance, - position: position, - position2D: position2D, - normal: normal, - normalizedPosition: normalizedPosition, - dimensions: props.dimensions, - valid: true - }; - } - - function isNearStylusTarget(stylusTargets, edgeBorder, minNormalDistance, maxNormalDistance) { - for (var i = 0; i < stylusTargets.length; i++) { - var stylusTarget = stylusTargets[i]; - - // check to see if the projected stylusTip is within within the 2d border - var borderMin = {x: -edgeBorder, y: -edgeBorder}; - var borderMax = {x: stylusTarget.dimensions.x + edgeBorder, y: stylusTarget.dimensions.y + edgeBorder}; - if (stylusTarget.distance >= minNormalDistance && stylusTarget.distance <= maxNormalDistance && - stylusTarget.position2D.x >= borderMin.x && stylusTarget.position2D.y >= borderMin.y && - stylusTarget.position2D.x <= borderMax.x && stylusTarget.position2D.y <= borderMax.y) { - return true; - } - } - return false; - } - - function calculateNearestStylusTarget(stylusTargets) { - var nearestStylusTarget; - - for (var i = 0; i < stylusTargets.length; i++) { - var stylusTarget = stylusTargets[i]; - - if ((!nearestStylusTarget || stylusTarget.distance < nearestStylusTarget.distance) && - stylusTarget.normalizedPosition.x >= 0 && stylusTarget.normalizedPosition.y >= 0 && - stylusTarget.normalizedPosition.x <= 1 && stylusTarget.normalizedPosition.y <= 1) { - nearestStylusTarget = stylusTarget; - } - } - - return nearestStylusTarget; - } - - function getFingerWorldLocation(hand) { - var fingerJointName = (hand === RIGHT_HAND) ? "RightHandIndex4" : "LeftHandIndex4"; - - var fingerJointIndex = MyAvatar.getJointIndex(fingerJointName); - var fingerPosition = MyAvatar.getAbsoluteJointTranslationInObjectFrame(fingerJointIndex); - var fingerRotation = MyAvatar.getAbsoluteJointRotationInObjectFrame(fingerJointIndex); - var worldFingerRotation = Quat.multiply(MyAvatar.orientation, fingerRotation); - var worldFingerPosition = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, fingerPosition)); - - return { - position: worldFingerPosition, - orientation: worldFingerRotation, - rotation: worldFingerRotation, - valid: true - }; - } - function distance2D(a, b) { var dx = (a.x - b.x); var dy = (a.y - b.y); @@ -361,12 +271,14 @@ Script.include("/~/system/libraries/controllers.js"); function TabletLaserInput(hand) { this.hand = hand; - this.previousStylusTouchingTarget = false; - this.stylusTouchingTarget = false; + this.previousLaserClikcedTarget = false; + this.laserPressingTarget = false; this.tabletScreenID = HMD.tabletScreenID; - - this.useFingerInsteadOfStylus = false; - this.fingerPointing = false; + this.mode = "none"; + this.laserTargetID = null; + this.laserTarget = null; + this.pressEnterLaserTarget = null; + this.hover = false; this.parameters = makeDispatcherModuleParameters( @@ -384,159 +296,118 @@ Script.include("/~/system/libraries/controllers.js"); }; this.stealTouchFocus = function(laserTarget) { - // send hover events to target - // record the entity or overlay we are hovering over. - if ((stylusTarget.entityID === this.getOtherHandController().hoverEntity) || - (stylusTarget.overlayID === this.getOtherHandController().hoverOverlay)) { - this.getOtherHandController().relinquishTouchFocus(); - } - this.requestTouchFocus(stylusTarget); + this.requestTouchFocus(laserTarget); }; this.requestTouchFocus = function(laserTarget) { - - // send hover events to target if we can. - // record the entity or overlay we are hovering over. - if (stylusTarget.entityID && - stylusTarget.entityID !== this.hoverEntity && - stylusTarget.entityID !== this.getOtherHandController().hoverEntity) { - this.hoverEntity = stylusTarget.entityID; - sendHoverEnterEventToStylusTarget(this.hand, stylusTarget); - } else if (stylusTarget.overlayID && - stylusTarget.overlayID !== this.hoverOverlay && - stylusTarget.overlayID !== this.getOtherHandController().hoverOverlay) { - this.hoverOverlay = stylusTarget.overlayID; - sendHoverEnterEventToStylusTarget(this.hand, stylusTarget); - } + sendHoverEnterEventToStylusTarget(this.hand, this.laserTarget); }; this.hasTouchFocus = function(laserTarget) { - return ((stylusTarget.entityID && stylusTarget.entityID === this.hoverEntity) || - (stylusTarget.overlayID && stylusTarget.overlayID === this.hoverOverlay)); + return (this.laserTargetID === HMD.tabletScreenID); }; this.relinquishTouchFocus = function() { // send hover leave event. var pointerEvent = { type: "Move", id: this.hand + 1 }; - if (this.hoverEntity) { - Entities.sendHoverLeaveEntity(this.hoverEntity, pointerEvent); - this.hoverEntity = null; - } else if (this.hoverOverlay) { - Overlays.sendMouseMoveOnOverlay(HMD.tabletScreenID, pointerEvent); - Overlays.sendHoverOverOverlay(this.hoverOverlay, pointerEvent); - Overlays.sendHoverLeaveOverlay(this.hoverOverlay, pointerEvent); - this.hoverOverlay = null; - } + Overlays.sendMouseMoveOnOverlay(this.tabletScreenID, pointerEvent); + Overlays.sendHoverOverOverlay(this.tabletScreenID, pointerEvent); + Overlays.sendHoverLeaveOverlay(this.tabletScreenID, pointerEvent); }; this.updateLaserPointer = function(controllerData) { var RADIUS = 0.005; var dim = { x: RADIUS, y: RADIUS, z: RADIUS }; - - var mode = "none"; - if (controllerData.triggerClicks[this.hand]) { - mode = "full"; - } else if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { - mode = "half"; - } else { - LaserPointers.disableLaserPointer(this.laserPointer); - return; - } - - if (mode === "full") { + if (this.mode === "full") { this.fullEnd.dimensions = dim; - LaserPointers.editRenderState(this.laserPointer, mode, {path: fullPath, end: this.fullEnd}); - } else if (mode === "half") { + LaserPointers.editRenderState(this.laserPointer, this.mode, {path: fullPath, end: this.fullEnd}); + } else if (this.mode === "half") { this.halfEnd.dimensions = dim; - LaserPointers.editRenderState(this.laserPointer, mode, {path: halfPath, end: this.halfEnd}); + LaserPointers.editRenderState(this.laserPointer, this.mode, {path: halfPath, end: this.halfEnd}); } LaserPointers.enableLaserPointer(this.laserPointer); - LaserPointers.setRenderState(this.laserPointer, mode); + LaserPointers.setRenderState(this.laserPointer, this.mode); }; - this.pointFinger = function(value) { - var HIFI_POINT_INDEX_MESSAGE_CHANNEL = "Hifi-Point-Index"; - if (this.fingerPointing !== value) { - var message; - if (this.hand === RIGHT_HAND) { - message = { pointRightIndex: value }; - } else { - message = { pointLeftIndex: value }; - } - Messages.sendMessage(HIFI_POINT_INDEX_MESSAGE_CHANNEL, JSON.stringify(message), true); - this.fingerPointing = value; - } - }; - - this.processLaser = function(controllerData) { - if (controllerData.rayPicks[this.hand].objectID === HMD.tabletScreenID) { + this.processControllerTriggers = function(controllerData) { + var rayPickIntersection = controllerData.rayPicks[this.hand].intersection; + this.laserTarget = calculateLaserTargetFromOverlay(rayPickIntersection, HMD.tabletScreenID); + if (controllerData.triggerClicks[this.hand]) { + this.mode = "full"; + this.laserPressingTarget = true; + this.hover = false; + } else if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { + this.mode = "half"; + this.laserPressingTarget = false; + this.hover = true; + this.requestTouchFocus(HMD.tabletScreenID); + } else { + this.mode = "none"; + this.laserPressingTarget = false; + this.hover = false; + this.relinquishTouchFocus(); - } + } this.homeButtonTouched = false; }; - this.laserTouchingEnter = function () { - this.stealTouchFocus(this.stylusTarget); - sendTouchStartEventToStylusTarget(this.hand, this.stylusTarget); + this.hovering = function() { + if (this.hasTouchFocus(this.laserTargetID)) { + sendHoverOverEventToStylusTarget(this.hand, this.laserTarget); + } + }; + + this.laserPressEnter = function () { + this.stealTouchFocus(this.laserTarget.overlayID); + sendTouchStartEventToStylusTarget(this.hand, this.laserTarget); Controller.triggerHapticPulse(HAPTIC_STYLUS_STRENGTH, HAPTIC_STYLUS_DURATION, this.hand); this.touchingEnterTimer = 0; - this.touchingEnterStylusTarget = this.stylusTarget; + this.pressEnterLaserTarget = this.laserTarget; this.deadspotExpired = false; var TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.0381; this.deadspotRadius = TOUCH_PRESS_TO_MOVE_DEADSPOT; }; - this.laserTouchingExit = function () { + this.laserPressExit = function () { - if (this.stylusTarget === undefined) { + if (this.laserTarget === undefined) { return; } // special case to handle home button. - if (this.stylusTarget.overlayID === HMD.homeButtonID) { - Messages.sendLocalMessage("home", this.stylusTarget.overlayID); + if (this.laserTargetID === HMD.homeButtonID) { + Messages.sendLocalMessage("home", this.laserTargetID); } // send press event if (this.deadspotExpired) { - sendTouchEndEventToStylusTarget(this.hand, this.stylusTarget); + sendTouchEndEventToStylusTarget(this.hand, this.laserTarget); } else { - sendTouchEndEventToStylusTarget(this.hand, this.touchingEnterStylusTarget); + print(this.pressEnterLaserTarget); + sendTouchEndEventToStylusTarget(this.hand, this.pressEnterLaserTarget); } }; - this.laserTouching = function (controllerData, dt) { + this.laserPressing = function (controllerData, dt) { this.touchingEnterTimer += dt; - if (this.stylusTarget.entityID) { - this.stylusTarget = calculateStylusTargetFromEntity(this.stylusTip, this.stylusTarget.entityProps); - } else if (this.stylusTarget.overlayID) { - this.stylusTarget = calculateStylusTargetFromOverlay(this.stylusTip, this.stylusTarget.overlayID); - } - - var TABLET_MIN_TOUCH_DISTANCE = -0.1; - var TABLET_MAX_TOUCH_DISTANCE = 0.01; - - if (this.stylusTarget) { - if (this.stylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE && - this.stylusTarget.distance < TABLET_MAX_TOUCH_DISTANCE) { - var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds - if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || - distance2D(this.stylusTarget.position2D, - this.touchingEnterStylusTarget.position2D) > this.deadspotRadius) { - sendTouchMoveEventToStylusTarget(this.hand, this.stylusTarget); - this.deadspotExpired = true; - } - } else { - this.stylusTouchingTarget = false; + if (this.laserTarget) { + + var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds + if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || + distance2D(this.laserTarget.position2D, + this.pressEnterLaserTarget.position2D) > this.deadspotRadius) { + sendTouchMoveEventToStylusTarget(this.hand, this.laserTarget); + this.deadspotExpired = true; } + } else { - this.stylusTouchingTarget = false; + this.laserPressingTarget = false; } }; @@ -556,38 +427,61 @@ Script.include("/~/system/libraries/controllers.js"); var target = controllerData.rayPicks[this.hand].objectID; if (this.pointingAtTabletScreen(target) || this.pointingAtHomeButton(target) || this.pointingAtTablet(target)) { if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { + this.tabletScrenID = HMD.tabletScreenID; return makeRunningValues(true, [], []); } } - //this.processLaser(controllerData); return makeRunningValues(false, [], []); }; + this.reset = function() { + this.laserPressExit(); + this.hover = false; + this.pressEnterLaserTarget = null; + this.laserTarget = null; + this.laserTargetID = null; + this.laserPressingTarget = false; + this.previousLaserClickedTarget = null; + this.mode = "none"; + }; + this.run = function (controllerData, deltaTime) { var target = controllerData.rayPicks[this.hand].objectID; + + if (!this.pointingAtTabletScreen(target) && !this.pointingAtHomeButton(target) && !this.pointingAtTablet(target)) { LaserPointers.disableLaserPointer(this.laserPointer); + this.laserPressExit(); + this.relinquishTouchFocus(); + this.reset(); + this.updateLaserPointer(); return makeRunningValues(false, [], []); } + + this.processControllerTriggers(controllerData); this.updateLaserPointer(controllerData); - //this.updateFingerAsStylusSetting(); - -/* if (!this.previousStylusTouchingTarget && this.stylusTouchingTarget) { - this.stylusTouchingEnter(); - } - if (this.previousStylusTouchingTarget && !this.stylusTouchingTarget) { - this.stylusTouchingExit(); - } - this.previousStylusTouchingTarget = this.stylusTouchingTarget; - - if (this.stylusTouchingTarget) { - this.stylusTouching(controllerData, deltaTime); - }*/ - /*if (this.processLaser(controllerData)) { + var intersection = LaserPointers.getPrevRayPickResult(this.laserPointer); + this.laserTargetID = intersection.objectID; + this.laserTarget = calculateLaserTargetFromOverlay(intersection.intersection, intersection.objectID); + if (this.laserTarget === undefined) { return makeRunningValues(true, [], []); - } else { - return makeRunningValues(false, [], []); - }*/ + } + + if (!this.previousLaserClickedTarget && this.laserPressingTarget) { + this.laserPressEnter(); + } + if (this.previousLaserClickedTarget && !this.laserPressingTarget) { + this.laserPressExit(); + } + this.previousLaserClickedTarget = this.laserPressingTarget; + + if (this.laserPressingTarget) { + this.laserPressing(controllerData, deltaTime); + } + + if (this.hover) { + this.hovering(); + } return makeRunningValues(true, [], []); }; @@ -608,6 +502,8 @@ Script.include("/~/system/libraries/controllers.js"); faceAvatar: true, defaultRenderStates: defaultRenderStates }); + + LaserPointers.setIgnoreOverlays(this.laserPointer, [HMD.tabletID]); }; var leftTabletLaserInput = new TabletLaserInput(LEFT_HAND); From b3ffa7b6b42ff503b655904463d383b1bcdaf604 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 25 Aug 2017 14:20:31 -0700 Subject: [PATCH 33/59] Working laser vs tablet --- .../controllerModules/tabletLaserInput.js | 155 ++++++++++-------- 1 file changed, 86 insertions(+), 69 deletions(-) diff --git a/scripts/system/controllers/controllerModules/tabletLaserInput.js b/scripts/system/controllers/controllerModules/tabletLaserInput.js index 992caeac48..27bfae9b2e 100644 --- a/scripts/system/controllers/controllerModules/tabletLaserInput.js +++ b/scripts/system/controllers/controllerModules/tabletLaserInput.js @@ -71,7 +71,7 @@ Script.include("/~/system/libraries/controllers.js"); drawInFront: 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}]; @@ -79,26 +79,26 @@ Script.include("/~/system/libraries/controllers.js"); 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; - function stylusTargetHasKeyboardFocus(laserTarget) { + function laserTargetHasKeyboardFocus(laserTarget) { if (laserTarget && laserTarget !== NULL_UUID) { return Overlays.keyboardFocusOverlay === laserTarget; } } - function setKeyboardFocusOnStylusTarget(laserTarget) { + function setKeyboardFocusOnLaserTarget(laserTarget) { if (laserTarget && laserTarget !== NULL_UUID) { Overlays.keyboardFocusOverlay = laserTarget; Entities.keyboardFocusEntity = NULL_UUID; } } - function sendHoverEnterEventToStylusTarget(hand, laserTarget) { + function sendHoverEnterEventToLaserTarget(hand, laserTarget) { if (!laserTarget) { return; } @@ -117,8 +117,8 @@ Script.include("/~/system/libraries/controllers.js"); } } - function sendHoverOverEventToStylusTarget(hand, laserTarget) { - + function sendHoverOverEventToLaserTarget(hand, laserTarget) { + if (!laserTarget) { return; } @@ -138,11 +138,11 @@ Script.include("/~/system/libraries/controllers.js"); } } - function sendTouchStartEventToStylusTarget(hand, laserTarget) { + function sendTouchStartEventToLaserTarget(hand, laserTarget) { if (!laserTarget) { return; } - + var pointerEvent = { type: "Press", id: hand + 1, // 0 is reserved for hardware mouse @@ -159,7 +159,7 @@ Script.include("/~/system/libraries/controllers.js"); } } - function sendTouchEndEventToStylusTarget(hand, laserTarget) { + function sendTouchEndEventToLaserTarget(hand, laserTarget) { if (!laserTarget) { return; } @@ -175,10 +175,11 @@ Script.include("/~/system/libraries/controllers.js"); if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) { Overlays.sendMouseReleaseOnOverlay(laserTarget.overlayID, pointerEvent); + Overlays.sendMouseReleaseOnOverlay(laserTarget.overlayID, pointerEvent); } } - function sendTouchMoveEventToStylusTarget(hand, laserTarget) { + function sendTouchMoveEventToLaserTarget(hand, laserTarget) { if (!laserTarget) { return; } @@ -192,7 +193,7 @@ Script.include("/~/system/libraries/controllers.js"); button: "Primary", isPrimaryHeld: true }; - + if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) { Overlays.sendMouseReleaseOnOverlay(laserTarget.overlayID, pointerEvent); } @@ -202,13 +203,13 @@ Script.include("/~/system/libraries/controllers.js"); function calculateLaserTargetFromOverlay(laserTip, overlayID) { var overlayPosition = Overlays.getProperty(overlayID, "position"); if (overlayPosition === undefined) { - return; + return null; } // project stylusTip onto overlay plane. var overlayRotation = Overlays.getProperty(overlayID, "rotation"); if (overlayRotation === undefined) { - return; + return null; } var normal = Vec3.multiplyQbyV(overlayRotation, {x: 0, y: 0, z: 1}); var distance = Vec3.dot(Vec3.subtract(laserTip, overlayPosition), normal); @@ -225,19 +226,19 @@ Script.include("/~/system/libraries/controllers.js"); // is used as a scale. var resolution = Overlays.getProperty(overlayID, "resolution"); if (resolution === undefined) { - return; + return null; } resolution.z = 1; // Circumvent divide-by-zero. var scale = Overlays.getProperty(overlayID, "dimensions"); if (scale === undefined) { - return; + return null; } scale.z = 0.01; // overlay dimensions are 2D, not 3D. dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale); } else { dimensions = Overlays.getProperty(overlayID, "dimensions"); if (dimensions === undefined) { - return; + return null; } if (!dimensions.z) { dimensions.z = 0.01; // sometimes overlay dimensions are 2D, not 3D. @@ -271,6 +272,7 @@ Script.include("/~/system/libraries/controllers.js"); function TabletLaserInput(hand) { this.hand = hand; + this.active = false; this.previousLaserClikcedTarget = false; this.laserPressingTarget = false; this.tabletScreenID = HMD.tabletScreenID; @@ -279,6 +281,7 @@ Script.include("/~/system/libraries/controllers.js"); this.laserTarget = null; this.pressEnterLaserTarget = null; this.hover = false; + this.lastValidTargetID = this.tabletTargetID; this.parameters = makeDispatcherModuleParameters( @@ -291,6 +294,10 @@ Script.include("/~/system/libraries/controllers.js"); return (this.hand === RIGHT_HAND) ? Controller.Standard.LeftHand : Controller.Standard.RightHand; }; + this.getOtherModule = function() { + return (this.hand === RIGHT_HAND) ? leftTabletLaserInput : rightTabletLaserInput; + }; + this.handToController = function() { return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; }; @@ -300,7 +307,10 @@ Script.include("/~/system/libraries/controllers.js"); }; this.requestTouchFocus = function(laserTarget) { - sendHoverEnterEventToStylusTarget(this.hand, this.laserTarget); + if (laserTarget !== null || laserTarget !== undefined) { + sendHoverEnterEventToLaserTarget(this.hand, this.laserTarget); + this.lastValidTargetID = laserTarget; + } }; this.hasTouchFocus = function(laserTarget) { @@ -310,15 +320,15 @@ Script.include("/~/system/libraries/controllers.js"); this.relinquishTouchFocus = function() { // send hover leave event. var pointerEvent = { type: "Move", id: this.hand + 1 }; - Overlays.sendMouseMoveOnOverlay(this.tabletScreenID, pointerEvent); - Overlays.sendHoverOverOverlay(this.tabletScreenID, pointerEvent); - Overlays.sendHoverLeaveOverlay(this.tabletScreenID, pointerEvent); + Overlays.sendMouseMoveOnOverlay(this.lastValidTargetID, pointerEvent); + Overlays.sendHoverOverOverlay(this.lastValidTargetID, pointerEvent); + Overlays.sendHoverLeaveOverlay(this.lastValidID, pointerEvent); }; 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}); @@ -332,8 +342,6 @@ Script.include("/~/system/libraries/controllers.js"); }; this.processControllerTriggers = function(controllerData) { - var rayPickIntersection = controllerData.rayPicks[this.hand].intersection; - this.laserTarget = calculateLaserTargetFromOverlay(rayPickIntersection, HMD.tabletScreenID); if (controllerData.triggerClicks[this.hand]) { this.mode = "full"; this.laserPressingTarget = true; @@ -342,39 +350,36 @@ Script.include("/~/system/libraries/controllers.js"); this.mode = "half"; this.laserPressingTarget = false; this.hover = true; - this.requestTouchFocus(HMD.tabletScreenID); + this.requestTouchFocus(this.laserTargetID); } else { this.mode = "none"; this.laserPressingTarget = false; this.hover = false; this.relinquishTouchFocus(); - + } - this.homeButtonTouched = false; }; this.hovering = function() { if (this.hasTouchFocus(this.laserTargetID)) { - sendHoverOverEventToStylusTarget(this.hand, this.laserTarget); + sendHoverOverEventToLaserTarget(this.hand, this.laserTarget); } }; this.laserPressEnter = function () { - this.stealTouchFocus(this.laserTarget.overlayID); - sendTouchStartEventToStylusTarget(this.hand, this.laserTarget); + sendTouchStartEventToLaserTarget(this.hand, this.laserTarget); Controller.triggerHapticPulse(HAPTIC_STYLUS_STRENGTH, HAPTIC_STYLUS_DURATION, this.hand); this.touchingEnterTimer = 0; this.pressEnterLaserTarget = this.laserTarget; this.deadspotExpired = false; - var TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.0381; - this.deadspotRadius = TOUCH_PRESS_TO_MOVE_DEADSPOT; + var LASER_PRESS_TO_MOVE_DEADSPOT = 0.026; + this.deadspotRadius = Math.tan(LASER_PRESS_TO_MOVE_DEADSPOT) * this.laserTarget.distance; }; this.laserPressExit = function () { - - if (this.laserTarget === undefined) { + if (this.laserTarget === null) { return; } @@ -385,27 +390,23 @@ Script.include("/~/system/libraries/controllers.js"); // send press event if (this.deadspotExpired) { - sendTouchEndEventToStylusTarget(this.hand, this.laserTarget); + sendTouchEndEventToLaserTarget(this.hand, this.laserTarget); } else { - print(this.pressEnterLaserTarget); - sendTouchEndEventToStylusTarget(this.hand, this.pressEnterLaserTarget); + sendTouchEndEventToLaserTarget(this.hand, this.pressEnterLaserTarget); } }; this.laserPressing = function (controllerData, dt) { - this.touchingEnterTimer += dt; if (this.laserTarget) { - var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || distance2D(this.laserTarget.position2D, this.pressEnterLaserTarget.position2D) > this.deadspotRadius) { - sendTouchMoveEventToStylusTarget(this.hand, this.laserTarget); + sendTouchMoveEventToLaserTarget(this.hand, this.laserTarget); this.deadspotExpired = true; } - } else { this.laserPressingTarget = false; } @@ -423,19 +424,32 @@ Script.include("/~/system/libraries/controllers.js"); return (target === HMD.homeButtonID); } - this.isReady = function (controllerData) { - var target = controllerData.rayPicks[this.hand].objectID; - if (this.pointingAtTabletScreen(target) || this.pointingAtHomeButton(target) || this.pointingAtTablet(target)) { - if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { - this.tabletScrenID = HMD.tabletScreenID; - return makeRunningValues(true, [], []); - } - } - return makeRunningValues(false, [], []); + this.releaseTouchEvent = function() { + sendTouchEndEventToLaserTarget(this.hand, this.pressEnterLaserTarget); + } + + + this.updateLaserTargets = function() { + var intersection = LaserPointers.getPrevRayPickResult(this.laserPointer); + this.laserTargetID = intersection.objectID; + this.laserTarget = calculateLaserTargetFromOverlay(intersection.intersection, intersection.objectID); }; + this.shouldExit = function(controllerData) { + var target = controllerData.rayPicks[this.hand].objectID; + var isLaserOffTablet = (!this.pointingAtTabletScreen(target) && !this.pointingAtHomeButton(target) && !this.pointingAtTablet(target)); + return isLaserOffTablet; + } + + this.exitModule = function() { + this.releaseTouchEvent(); + this.relinquishTouchFocus(); + this.reset(); + this.updateLaserPointer(); + LaserPointers.disableLaserPointer(this.laserPointer); + } + this.reset = function() { - this.laserPressExit(); this.hover = false; this.pressEnterLaserTarget = null; this.laserTarget = null; @@ -443,29 +457,32 @@ Script.include("/~/system/libraries/controllers.js"); this.laserPressingTarget = false; this.previousLaserClickedTarget = null; this.mode = "none"; + this.active = false; + }; + + this.isReady = function (controllerData) { + var target = controllerData.rayPicks[this.hand].objectID; + if (this.pointingAtTabletScreen(target) || this.pointingAtHomeButton(target) || this.pointingAtTablet(target)) { + if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE && !this.getOtherModule().active) { + this.tabletScrenID = HMD.tabletScreenID; + this.active = true; + return makeRunningValues(true, [], []); + } + } + this.reset(); + return makeRunningValues(false, [], []); }; this.run = function (controllerData, deltaTime) { - var target = controllerData.rayPicks[this.hand].objectID; - - if (!this.pointingAtTabletScreen(target) && !this.pointingAtHomeButton(target) && !this.pointingAtTablet(target)) { - LaserPointers.disableLaserPointer(this.laserPointer); - this.laserPressExit(); - this.relinquishTouchFocus(); - this.reset(); - this.updateLaserPointer(); + if (this.shouldExit(controllerData)) { + this.exitModule(); return makeRunningValues(false, [], []); } + this.updateLaserTargets(); this.processControllerTriggers(controllerData); this.updateLaserPointer(controllerData); - var intersection = LaserPointers.getPrevRayPickResult(this.laserPointer); - this.laserTargetID = intersection.objectID; - this.laserTarget = calculateLaserTargetFromOverlay(intersection.intersection, intersection.objectID); - if (this.laserTarget === undefined) { - return makeRunningValues(true, [], []); - } if (!this.previousLaserClickedTarget && this.laserPressingTarget) { this.laserPressEnter(); @@ -478,7 +495,7 @@ Script.include("/~/system/libraries/controllers.js"); if (this.laserPressingTarget) { this.laserPressing(controllerData, deltaTime); } - + if (this.hover) { this.hovering(); } @@ -505,7 +522,7 @@ Script.include("/~/system/libraries/controllers.js"); LaserPointers.setIgnoreOverlays(this.laserPointer, [HMD.tabletID]); }; - + var leftTabletLaserInput = new TabletLaserInput(LEFT_HAND); var rightTabletLaserInput = new TabletLaserInput(RIGHT_HAND); @@ -513,8 +530,8 @@ Script.include("/~/system/libraries/controllers.js"); enableDispatcherModule("RightTabletLaserInput", rightTabletLaserInput); this.cleanup = function () { - leftTabletStylusInput.cleanup(); - rightTabletStylusInput.cleanup(); + leftTabletLaserInput.cleanup(); + rightTabletLaserInput.cleanup(); disableDispatcherModule("LeftTabletLaserInput"); disableDispatcherModule("RightTabletLaserInput"); }; From 0eceb7b382425c897657cf7e7a3b978bc162051c Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 25 Aug 2017 17:40:30 -0700 Subject: [PATCH 34/59] unviersial web3d vs laser --- .../controllers/controllerDispatcher.js | 12 ++++++ .../controllerModules/farActionGrabEntity.js | 13 ++++--- ...aserInput.js => web3DOverlayLaserInput.js} | 38 +++++++------------ .../system/controllers/controllerScripts.js | 2 +- 4 files changed, 33 insertions(+), 32 deletions(-) rename scripts/system/controllers/controllerModules/{tabletLaserInput.js => web3DOverlayLaserInput.js} (93%) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index b5681fef3f..18b4c287c0 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -33,6 +33,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); var totalVariance = 0; var highVarianceCount = 0; var veryhighVarianceCount = 0; + this.tabletID = null; // a module can occupy one or more "activity" slots while it's running. If all the required slots for a module are // not set to false (not in use), a module cannot start. When a module is using a slot, that module's name @@ -126,8 +127,17 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); return deltaTime; }; + this.setIgnoreTablet = function() { + if (HMD.tabletID !== _this.tabletID) { + RayPick.setIgnoreOverlays(_this.leftControllerRayPick, [HMD.tabletID]); + RayPick.setIgnoreOverlays(_this.rightControllerRayPick, [HMD.tabletID]); + tabletIgnored = true + } + } + this.update = function () { var deltaTime = this.updateTimings(); + this.setIgnoreTablet() if (controllerDispatcherPluginsNeedSort) { this.orderedPluginNames = []; @@ -336,6 +346,8 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); }); + + this.cleanup = function () { Script.update.disconnect(_this.update); Controller.disableMapping(MAPPING_NAME); diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 3c8fba6c24..44bb500605 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -317,17 +317,18 @@ Script.include("/~/system/libraries/controllers.js"); this.grabbedThingID = null; }; - this.pointingOnTablet = function(controllerData) { - var target = controllerData.rayPicks[this.hand].objectID; - - if (target === HMD.homeButtonID || target === HMD.tabletScreenID) { + this.pointingAtWebOverlay = function(controllerData) { + var intersection = controllerData.rayPicks[this.hand]; + var overlayType = Overlays.getOverlayType(intersection.objectID); + print(JSON.stringify(Entities.getEntityProperties(intersection.objectID))); + if ((intersection.type === RayPick.INTERSECTED_OVERLAY && overlayType === "web3d") || intersection.objectID === HMD.tabletButtonID) { return true; } return false }; this.isReady = function (controllerData) { - if (this.pointingOnTablet(controllerData)) { + if (this.pointingAtWebOverlay(controllerData)) { return makeRunningValues(false, [], []); } @@ -343,7 +344,7 @@ Script.include("/~/system/libraries/controllers.js"); }; this.run = function (controllerData) { - if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || this.pointingOnTablet(controllerData)) { + if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || this.pointingAtWebOverlay(controllerData)) { this.endNearGrabAction(); this.laserPointerOff(); return makeRunningValues(false, [], []); diff --git a/scripts/system/controllers/controllerModules/tabletLaserInput.js b/scripts/system/controllers/controllerModules/web3DOverlayLaserInput.js similarity index 93% rename from scripts/system/controllers/controllerModules/tabletLaserInput.js rename to scripts/system/controllers/controllerModules/web3DOverlayLaserInput.js index 27bfae9b2e..501c37fd35 100644 --- a/scripts/system/controllers/controllerModules/tabletLaserInput.js +++ b/scripts/system/controllers/controllerModules/web3DOverlayLaserInput.js @@ -275,7 +275,7 @@ Script.include("/~/system/libraries/controllers.js"); this.active = false; this.previousLaserClikcedTarget = false; this.laserPressingTarget = false; - this.tabletScreenID = HMD.tabletScreenID; + this.tabletScreenID = null; this.mode = "none"; this.laserTargetID = null; this.laserTarget = null; @@ -361,9 +361,10 @@ Script.include("/~/system/libraries/controllers.js"); }; this.hovering = function() { - if (this.hasTouchFocus(this.laserTargetID)) { - sendHoverOverEventToLaserTarget(this.hand, this.laserTarget); + if (!laserTargetHasKeyboardFocus(this.laserTagetID)) { + setKeyboardFocusOnLaserTarget(this.laserTargetID); } + sendHoverOverEventToLaserTarget(this.hand, this.laserTarget); }; this.laserPressEnter = function () { @@ -412,33 +413,21 @@ Script.include("/~/system/libraries/controllers.js"); } }; - this.pointingAtTablet = function(target) { - return (target === HMD.tabletID); - }; - - this.pointingAtTabletScreen = function(target) { - return (target === HMD.tabletScreenID); - } - - this.pointingAtHomeButton = function(target) { - return (target === HMD.homeButtonID); - } - this.releaseTouchEvent = function() { sendTouchEndEventToLaserTarget(this.hand, this.pressEnterLaserTarget); } - this.updateLaserTargets = function() { - var intersection = LaserPointers.getPrevRayPickResult(this.laserPointer); + this.updateLaserTargets = function(controllerData) { + var intersection = controllerData.rayPicks[this.hand]; this.laserTargetID = intersection.objectID; this.laserTarget = calculateLaserTargetFromOverlay(intersection.intersection, intersection.objectID); }; this.shouldExit = function(controllerData) { - var target = controllerData.rayPicks[this.hand].objectID; - var isLaserOffTablet = (!this.pointingAtTabletScreen(target) && !this.pointingAtHomeButton(target) && !this.pointingAtTablet(target)); - return isLaserOffTablet; + var intersection = controllerData.rayPicks[this.hand]; + var offOverlay = (intersection.type !== RayPick.INTERSECTED_OVERLAY) + return offOverlay; } this.exitModule = function() { @@ -461,10 +450,9 @@ Script.include("/~/system/libraries/controllers.js"); }; this.isReady = function (controllerData) { - var target = controllerData.rayPicks[this.hand].objectID; - if (this.pointingAtTabletScreen(target) || this.pointingAtHomeButton(target) || this.pointingAtTablet(target)) { + var intersection = controllerData.rayPicks[this.hand]; + if (intersection.type === RayPick.INTERSECTED_OVERLAY) { if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE && !this.getOtherModule().active) { - this.tabletScrenID = HMD.tabletScreenID; this.active = true; return makeRunningValues(true, [], []); } @@ -480,7 +468,7 @@ Script.include("/~/system/libraries/controllers.js"); return makeRunningValues(false, [], []); } - this.updateLaserTargets(); + this.updateLaserTargets(controllerData); this.processControllerTriggers(controllerData); this.updateLaserPointer(controllerData); @@ -511,7 +499,7 @@ Script.include("/~/system/libraries/controllers.js"); this.halfEnd = halfEnd; this.fullEnd = fullEnd; this.laserPointer = LaserPointers.createLaserPointer({ - joint: (this.hand == RIGHT_HAND) ? "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", + joint: (this.hand == RIGHT_HAND) ? "_CONTROLLER_RIGHTHAND" : "_CONTROLLER_LEFTHAND", filter: RayPick.PICK_OVERLAYS, maxDistance: PICK_MAX_DISTANCE, posOffset: getGrabPointSphereOffset(this.handToController()), diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 65d63b7bec..9e6fd0c745 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -26,7 +26,7 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/equipEntity.js", "controllerModules/nearTrigger.js", "controllerModules/cloneEntity.js", - "controllerModules/tabletLaserInput.js", + "controllerModules/web3DOverlayLaserInput.js", "teleport.js" ]; From 4b592687b76ebe7edaea25915a32fd64458cca71 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 28 Aug 2017 17:35:34 -0700 Subject: [PATCH 35/59] finished webEntityLaserInput, now working on cloning --- .../controllerModules/equipEntity.js | 1 + .../controllerModules/farActionGrabEntity.js | 97 +++- .../controllerModules/nearParentGrabEntity.js | 14 +- .../controllerModules/webEntityLaserInput.js | 478 ++++++++++++++++++ .../system/controllers/controllerScripts.js | 2 +- scripts/system/libraries/cloneEntityUtils.js | 91 ++++ 6 files changed, 667 insertions(+), 16 deletions(-) create mode 100644 scripts/system/controllers/controllerModules/webEntityLaserInput.js create mode 100644 scripts/system/libraries/cloneEntityUtils.js diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index 2197fdca28..b9b8279dbd 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -434,6 +434,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa }; this.startEquipEntity = function (controllerData) { + print("------> starting to equip entity <-------"); this.dropGestureReset(); this.clearEquipHaptics(); Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 44bb500605..b84ee4b822 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -86,6 +86,24 @@ Script.include("/~/system/libraries/controllers.js"); {name: "full", distance: DEFAULT_SEARCH_SPHERE_DISTANCE, path: fullPath}, {name: "hold", distance: DEFAULT_SEARCH_SPHERE_DISTANCE, path: holdPath}]; + var GRABBABLE_PROPERTIES = [ + "position", + "registrationPoint", + "rotation", + "gravity", + "collidesWith", + "dynamic", + "collisionless", + "locked", + "name", + "shapeType", + "parentID", + "parentJointIndex", + "density", + "dimensions", + "userData" + ]; + function FarActionGrabEntity(hand) { this.hand = hand; @@ -317,18 +335,61 @@ Script.include("/~/system/libraries/controllers.js"); this.grabbedThingID = null; }; - this.pointingAtWebOverlay = function(controllerData) { + this.pointingAtWebEntity = function(controllerData) { var intersection = controllerData.rayPicks[this.hand]; - var overlayType = Overlays.getOverlayType(intersection.objectID); - print(JSON.stringify(Entities.getEntityProperties(intersection.objectID))); - if ((intersection.type === RayPick.INTERSECTED_OVERLAY && overlayType === "web3d") || intersection.objectID === HMD.tabletButtonID) { + var entityProperty = Entities.getEntityProperties(intersection.objectID); + var entityType = entityProperty.type; + if ((intersection.type === RayPick.INTERSECTED_ENTITY && entityType === "Web") || intersection.objectID === HMD.tabletButtonID) { return true; } return false }; + this.distanceRotate = function(otherFarGrabModule) { + this.distanceRotating = true; + this.distanceHolding = false; + + var worldControllerRotation = getControllerWorldLocation(this.handToController(), true).orientation; + var controllerRotationDelta = Quat.multiply(worldControllerRotation, Quat.inverse(this.previousWorldControllerRotation)); + // Rotate entity by twice the delta rotation. + controllerRotationDelta = Quat.multiply(controllerRotationDelta, controllerRotationDelta); + + // Perform the rotation in the translation controller's action update. + otherFarGrabModule.currentObjectRotation = Quat.multiply(controllerRotationDelta, + otherFarGrabModule.currentObjectRotation); + + // Rotate about the translation controller's target position. + this.offsetPosition = Vec3.multiplyQbyV(controllerRotationDelta, this.offsetPosition); + otherFarGrabModule.offsetPosition = Vec3.multiplyQbyV(controllerRotationDelta, + otherFarGrabModule.offsetPosition); + + this.updateLaserPointer(); + + this.previousWorldControllerRotation = worldControllerRotation; + }; + + this.prepareDistanceRotatingData = function(controllerData) { + var intersection = controllerData.rayPicks[this.hand]; + + var controllerLocation = getControllerWorldLocation(this.handToController(), true); + var worldControllerPosition = controllerLocation.position; + var worldControllerRotation = controllerLocation.orientation; + + var grabbedProperties = Entities.getEntityProperties(intersection.objectID, GRABBABLE_PROPERTIES); + this.currentObjectPosition = grabbedProperties.position; + this.grabRadius = intersection.distance; + + // Offset between controller vector at the grab radius and the entity position. + var targetPosition = Vec3.multiply(this.grabRadius, Quat.getUp(worldControllerRotation)); + targetPosition = Vec3.sum(targetPosition, worldControllerPosition); + this.offsetPosition = Vec3.subtract(this.currentObjectPosition, targetPosition); + + // Initial controller rotation. + this.previousWorldControllerRotation = worldControllerRotation; + }; + this.isReady = function (controllerData) { - if (this.pointingAtWebOverlay(controllerData)) { + if (this.pointingAtWebEntity(controllerData)) { return makeRunningValues(false, [], []); } @@ -337,6 +398,7 @@ Script.include("/~/system/libraries/controllers.js"); if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { this.updateLaserPointer(controllerData); + this.prepareDistanceRotatingData(controllerData); return makeRunningValues(true, [], []); } else { return makeRunningValues(false, [], []); @@ -344,7 +406,7 @@ Script.include("/~/system/libraries/controllers.js"); }; this.run = function (controllerData) { - if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || this.pointingAtWebOverlay(controllerData)) { + if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || this.pointingAtWebEntity(controllerData)) { this.endNearGrabAction(); this.laserPointerOff(); return makeRunningValues(false, [], []); @@ -374,6 +436,7 @@ Script.include("/~/system/libraries/controllers.js"); } } + this.continueDistanceHolding(controllerData); // this.updateLaserPointer(controllerData, false, false); @@ -398,26 +461,32 @@ Script.include("/~/system/libraries/controllers.js"); "rotation", "dimensions", "density", "userData", "locked", "type"]); if (entityIsDistanceGrabbable(targetProps)) { - this.grabbedThingID = entityID; - this.grabbedDistance = rayPickInfo.distance; + if (!this.distanceRotating) { + this.grabbedThingID = entityID; + this.grabbedDistance = rayPickInfo.distance; + } var otherModuleName = this.hand == RIGHT_HAND ? "LeftFarActionGrabEntity" : "RightFarActionGrabEntity"; var otherFarGrabModule = getEnabledModuleByName(otherModuleName); - if (otherFarGrabModule.grabbedThingID == this.grabbedThingID) { - this.distanceRotating = true; - this.distanceHolding = false; - // XXX rotate + if (otherFarGrabModule.grabbedThingID == this.grabbedThingID && otherFarGrabModule.distanceHolding) { + this.distanceRotate(otherFarGrabModule); } else { this.distanceHolding = true; this.distanceRotating = false; this.startFarGrabAction(controllerData, targetProps); } } - } else if (Reticle.pointingAtSystemOverlay || Overlays.getOverlayAtPoint(hudPoint2d || Reticle.position)) { + } else if (this.distanceRotating) { + var otherModuleName = + this.hand == RIGHT_HAND ? "LeftFarActionGrabEntity" : "RightFarActionGrabEntity"; + var otherFarGrabModule = getEnabledModuleByName(otherModuleName); + this.distanceRotate(otherFarGrabModule); + } + /* else if (Reticle.pointingAtSystemOverlay || Overlays.getOverlayAtPoint(hudPoint2d || Reticle.position)) { this.endNearGrabAction(); this.laserPointerOff(); return makeRunningValues(false, [], []); - } + }*/ } return makeRunningValues(true, [], []); }; diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index cb004acd03..f05eab348d 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -14,6 +14,7 @@ */ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/cloneEntityUtils.js"); (function() { @@ -187,7 +188,18 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); if (controllerData.triggerClicks[this.hand] == 1) { // switch to grab var targetProps = this.getTargetProps(controllerData); - if (targetProps) { + var targetCloneable = entityIsCloneable(targetProps); + + if (targetCloneable) { + var worldEntityProps = controllerData.nearbyEntityProperties[this.hand]; + var cloneID = cloneEntity(targetProps, worldEntityProps); + var cloneProps = Entities.getEntityProperties(cloneID); + + this.grabbing = true; + this.targetEntityID = cloneID + this.startNearParentingGrabEntity(controllerData, cloneProps); + + } else if (targetProps) { this.grabbing = true; this.startNearParentingGrabEntity(controllerData, targetProps); } diff --git a/scripts/system/controllers/controllerModules/webEntityLaserInput.js b/scripts/system/controllers/controllerModules/webEntityLaserInput.js new file mode 100644 index 0000000000..31e3f50bb6 --- /dev/null +++ b/scripts/system/controllers/controllerModules/webEntityLaserInput.js @@ -0,0 +1,478 @@ +"use strict"; + +// webEntityLaserInput.js +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +/*jslint bitwise: true */ + +/* global Script, Controller, LaserPointers, RayPick, RIGHT_HAND, LEFT_HAND, Mat4, MyAvatar, Vec3, Camera, Quat, + getGrabPointSphereOffset, getEnabledModuleByName, makeRunningValues, Entities, NULL_UUID, + enableDispatcherModule, disableDispatcherModule, entityIsDistanceGrabbable, + 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, + +*/ + +Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllers.js"); + +(function() { + 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 + drawInFront: 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, + drawInFront: 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 + drawInFront: 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, + drawInFront: 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 + drawInFront: 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; + + function laserTargetHasKeyboardFocus(laserTarget) { + if (laserTarget && laserTarget !== NULL_UUID) { + return Entities.keyboardFocusOverlay === laserTarget; + } + } + + function setKeyboardFocusOnLaserTarget(laserTarget) { + if (laserTarget && laserTarget !== NULL_UUID) { + Entities.wantsHandControllerPointerEvents(laserTarget); + Overlays.keyboardFocusOverlay = NULL_UUID; + Entities.keyboardFocusEntity = laserTarget; + } + } + + function sendHoverEnterEventToLaserTarget(hand, laserTarget) { + if (!laserTarget) { + return; + } + var pointerEvent = { + type: "Move", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: laserTarget.position2D, + pos3D: laserTarget.position, + normal: laserTarget.normal, + direction: Vec3.subtract(ZERO_VEC, laserTarget.normal), + button: "None" + }; + + if (laserTarget.entityID && laserTarget.entityID !== NULL_UUID) { + Entities.sendHoverEnterEntity(laserTarget.entityID, pointerEvent); + } + } + + function sendHoverOverEventToLaserTarget(hand, laserTarget) { + + if (!laserTarget) { + return; + } + var pointerEvent = { + type: "Move", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: laserTarget.position2D, + pos3D: laserTarget.position, + normal: laserTarget.normal, + direction: Vec3.subtract(ZERO_VEC, laserTarget.normal), + button: "None" + }; + + if (laserTarget.entityID && laserTarget.entityID !== NULL_UUID) { + Entities.sendMouseMoveOnEntity(laserTarget.entityID, pointerEvent); + Entities.sendHoverOverEntity(laserTarget.entityID, pointerEvent); + } + } + + + function sendTouchStartEventToLaserTarget(hand, laserTarget) { + var pointerEvent = { + type: "Press", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: laserTarget.position2D, + pos3D: laserTarget.position, + normal: laserTarget.normal, + direction: Vec3.subtract(ZERO_VEC, laserTarget.normal), + button: "Primary", + isPrimaryHeld: true + }; + + if (laserTarget.entityID && laserTarget.entityID !== NULL_UUID) { + Entities.sendMousePressOnEntity(laserTarget.entityID, pointerEvent); + Entities.sendClickDownOnEntity(laserTarget.entityID, pointerEvent); + } + } + + function sendTouchEndEventToLaserTarget(hand, laserTarget) { + var pointerEvent = { + type: "Release", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: laserTarget.position2D, + pos3D: laserTarget.position, + normal: laserTarget.normal, + direction: Vec3.subtract(ZERO_VEC, laserTarget.normal), + button: "Primary" + }; + + if (laserTarget.entityID && laserTarget.entityID !== NULL_UUID) { + Entities.sendMouseReleaseOnEntity(laserTarget.entityID, pointerEvent); + Entities.sendClickReleaseOnEntity(laserTarget.entityID, pointerEvent); + Entities.sendHoverLeaveEntity(laserTarget.entityID, pointerEvent); + } + } + + function sendTouchMoveEventToLaserTarget(hand, laserTarget) { + var pointerEvent = { + type: "Move", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: laserTarget.position2D, + pos3D: laserTarget.position, + normal: laserTarget.normal, + direction: Vec3.subtract(ZERO_VEC, laserTarget.normal), + button: "Primary", + isPrimaryHeld: true + }; + + if (laserTarget.entityID && laserTarget.entityID !== NULL_UUID) { + Entities.sendMouseMoveOnEntity(laserTarget.entityID, pointerEvent); + Entities.sendHoldingClickOnEntity(laserTarget.entityID, pointerEvent); + } + } + + function calculateTargetFromEntity(intersection, props) { + if (props.rotation === undefined) { + // if rotation is missing from props object, then this entity has probably been deleted. + return null; + } + + // project stylus tip onto entity plane. + var normal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1}); + Vec3.multiplyQbyV(props.rotation, {x: 0, y: 1, z: 0}); + var distance = Vec3.dot(Vec3.subtract(intersection, props.position), normal); + var position = Vec3.subtract(intersection, Vec3.multiply(normal, distance)); + + // generate normalized coordinates + var invRot = Quat.inverse(props.rotation); + var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, props.position)); + var invDimensions = { x: 1 / props.dimensions.x, y: 1 / props.dimensions.y, z: 1 / props.dimensions.z }; + var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), props.registrationPoint); + + // 2D position on entity plane in meters, relative to the bounding box upper-left hand corner. + var position2D = { x: normalizedPosition.x * props.dimensions.x, + y: (1 - normalizedPosition.y) * props.dimensions.y }; // flip y-axis + + return { + entityID: props.id, + entityProps: props, + overlayID: null, + distance: distance, + position: position, + position2D: position2D, + normal: normal, + normalizedPosition: normalizedPosition, + dimensions: props.dimensions, + valid: true + }; + } + + function distance2D(a, b) { + var dx = (a.x - b.x); + var dy = (a.y - b.y); + return Math.sqrt(dx * dx + dy * dy); + } + + function WebEntityLaserInput(hand) { + this.hand = hand; + this.active = false; + this.previousLaserClickedTarget = false; + this.laserPressingTarget = false; + this.hover = false; + this.mode = "none"; + this.pressEnterLaserTarget = null; + this.laserTarget = null; + this.laserTargetID = null; + this.lastValidTargetID = null; + + this.handToController = function() { + return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + }; + + this.getOtherModule = function() { + return (this.hand === RIGHT_HAND) ? leftWebEntityLaserInput : rightWebEntityLaserInput; + }; + + this.parameters = makeDispatcherModuleParameters( + 550, + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100); + + this.requestTouchFocus = function(laserTarget) { + if (laserTarget !== null || laserTarget !== undefined) { + sendHoverEnterEventToLaserTarget(this.hand, this.laserTarget); + this.lastValidTargetID = laserTarget; + } + }; + + this.relinquishTouchFocus = function() { + // send hover leave event. + var pointerEvent = { type: "Move", id: this.hand + 1 }; + Entities.sendMouseMoveOnEntity(this.lastValidTargetID, pointerEvent); + Entities.sendHoverOverEntity(this.lastValidTargetID, pointerEvent); + Entities.sendHoverLeaveEntity(this.lastValidID, pointerEvent); + }; + + this.updateLaserTargets = function(controllerData) { + var intersection = controllerData.rayPicks[this.hand]; + this.laserTargetID = intersection.objectID; + var props = Entities.getEntityProperties(intersection.objectID); + this.laserTarget = calculateTargetFromEntity(intersection.intersection, props); + }; + + this.processControllerTriggers = function(controllerData) { + if (controllerData.triggerClicks[this.hand]) { + this.mode = "full"; + this.laserPressingTarget = true; + this.hover = false; + } else if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { + this.mode = "half"; + this.laserPressingTarget = false; + this.hover = true; + this.requestTouchFocus(this.laserTargetID); + } else { + this.mode = "none"; + this.laserPressingTarget = false; + this.hover = false; + this.relinquishTouchFocus(); + + } + }; + + this.hovering = function() { + if (!laserTargetHasKeyboardFocus(this.laserTagetID)) { + setKeyboardFocusOnLaserTarget(this.laserTargetID); + } + sendHoverOverEventToLaserTarget(this.hand, this.laserTarget); + }; + + this.laserPressEnter = function () { + sendTouchStartEventToLaserTarget(this.hand, this.laserTarget); + Controller.triggerHapticPulse(HAPTIC_STYLUS_STRENGTH, HAPTIC_STYLUS_DURATION, this.hand); + + this.touchingEnterTimer = 0; + this.pressEnterLaserTarget = this.laserTarget; + this.deadspotExpired = false; + + var LASER_PRESS_TO_MOVE_DEADSPOT = 0.026; + this.deadspotRadius = Math.tan(LASER_PRESS_TO_MOVE_DEADSPOT) * this.laserTarget.distance; + }; + + this.laserPressExit = function () { + if (this.laserTarget === null) { + return; + } + + // send press event + if (this.deadspotExpired) { + sendTouchEndEventToLaserTarget(this.hand, this.laserTarget); + } else { + sendTouchEndEventToLaserTarget(this.hand, this.pressEnterLaserTarget); + } + }; + + this.laserPressing = function (controllerData, dt) { + this.touchingEnterTimer += dt; + + if (this.laserTarget) { + var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds + if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || + distance2D(this.laserTarget.position2D, + this.pressEnterLaserTarget.position2D) > this.deadspotRadius) { + sendTouchMoveEventToLaserTarget(this.hand, this.laserTarget); + this.deadspotExpired = true; + } + } else { + this.laserPressingTarget = false; + } + }; + + this.releaseTouchEvent = function() { + if (this.pressEnterLaserTarget === null) { + return; + } + + sendTouchEndEventToLaserTarget(this.hand, this.pressEnterLaserTarget); + } + + this.updateLaserPointer = function(controllerData) { + var RADIUS = 0.005; + var dim = { x: RADIUS, y: RADIUS, z: RADIUS }; + + if (this.mode === "full") { + fullEnd.dimensions = dim; + LaserPointers.editRenderState(this.laserPointer, this.mode, {path: fullPath, end: fullEnd}); + } else if (this.mode === "half") { + halfEnd.dimensions = dim; + LaserPointers.editRenderState(this.laserPointer, this.mode, {path: halfPath, end: halfEnd}); + } + + LaserPointers.enableLaserPointer(this.laserPointer); + LaserPointers.setRenderState(this.laserPointer, this.mode); + }; + + this.isPointingAtWebEntity = function(controllerData) { + var intersection = controllerData.rayPicks[this.hand]; + var entityProperty = Entities.getEntityProperties(intersection.objectID); + var entityType = entityProperty.type; + + if ((intersection.type === RayPick.INTERSECTED_ENTITY && entityType === "Web")) { + return true; + } + return false + }; + + this.exitModule = function() { + this.releaseTouchEvent(); + this.relinquishTouchFocus(); + this.reset(); + this.updateLaserPointer(); + LaserPointers.disableLaserPointer(this.laserPointer); + }; + + this.reset = function() { + this.pressEnterLaserTarget = null; + this.laserTarget = null; + this.laserTargetID = null; + this.laserPressingTarget = false; + this.previousLaserClickedTarget = null; + this.mode = "none"; + this.active = false; + }; + + this.isReady = function(controllerData) { + var otherModule = this.getOtherModule(); + if (this.isPointingAtWebEntity(controllerData) && !otherModule.active) { + return makeRunningValues(true, [], []); + } + + return makeRunningValues(false, [], []); + }; + + this.run = function(controllerData, deltaTime) { + if (!this.isPointingAtWebEntity(controllerData)) { + this.exitModule(); + return makeRunningValues(false, [], []); + } + + this.updateLaserTargets(controllerData); + this.processControllerTriggers(controllerData); + this.updateLaserPointer(controllerData); + + if (!this.previousLaserClickedTarget && this.laserPressingTarget) { + this.laserPressEnter(); + } + if (this.previousLaserClickedTarget && !this.laserPressingTarget) { + this.laserPressExit(); + } + this.previousLaserClickedTarget = this.laserPressingTarget; + + if (this.laserPressingTarget) { + this.laserPressing(controllerData, deltaTime); + } + + if (this.hover) { + this.hovering(); + } + return makeRunningValues(true, [], []); + }; + + this.cleanup = function() { + LaserPointers.disableLaserPointer(this.laserPointer); + LaserPointers.removeLaserPointer(this.laserPointer); + } + + this.laserPointer = LaserPointers.createLaserPointer({ + joint: (this.hand == RIGHT_HAND) ? "_CONTROLLER_RIGHTHAND" : "_CONTROLLER_LEFTHAND", + filter: RayPick.PICK_ENTITIES, + maxDistance: PICK_MAX_DISTANCE, + posOffset: getGrabPointSphereOffset(this.handToController()), + renderStates: renderStates, + faceAvatar: true, + defaultRenderStates: defaultRenderStates + }); + }; + + + var leftWebEntityLaserInput = new WebEntityLaserInput(LEFT_HAND); + var rightWebEntityLaserInput = new WebEntityLaserInput(RIGHT_HAND); + + enableDispatcherModule("LeftWebEntityLaserInput", leftWebEntityLaserInput); + enableDispatcherModule("RightWebEntityLaserInput", rightWebEntityLaserInput); + + this.cleanup = function() { + leftWebEntityLaserInput.cleanup(); + rightWebEntityLaserInput.cleanup(); + disableDispatcherModule("LeftWebEntityLaserInput"); + disableDispatcherModule("RightWebEntityLaserInput"); + }; + Script.scriptEnding.connect(this.cleanup); + +}()); diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 9e6fd0c745..c9f030231a 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -25,8 +25,8 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/tabletStylusInput.js", "controllerModules/equipEntity.js", "controllerModules/nearTrigger.js", - "controllerModules/cloneEntity.js", "controllerModules/web3DOverlayLaserInput.js", + "controllerModules/webEntityLaserInput.js", "teleport.js" ]; diff --git a/scripts/system/libraries/cloneEntityUtils.js b/scripts/system/libraries/cloneEntityUtils.js new file mode 100644 index 0000000000..a862108a87 --- /dev/null +++ b/scripts/system/libraries/cloneEntityUtils.js @@ -0,0 +1,91 @@ +"use strict"; + +// cloneEntity.js +// +// 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("/~/system/controllers/controllerDispatcherUtils.js"); + + +// Object assign polyfill +if (typeof Object.assign != 'function') { + Object.assign = function(target, varArgs) { + if (target === null) { + throw new TypeError('Cannot convert undefined or null to object'); + } + var to = Object(target); + for (var index = 1; index < arguments.length; index++) { + var nextSource = arguments[index]; + if (nextSource !== null) { + for (var nextKey in nextSource) { + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + return to; + }; +} + +entityIsCloneable = function(props) { + if (props) { + var grabbableData = getGrabbableData(props); + return grabbableData.cloneable; + } + + return false; +} + + +cloneEntity = function(cloneableProps, worldEntityProps) { + if (!cloneableProps) { + return null; + } + + // we need all the properties, for this + cloneableProps = Entities.getEntityProperties(cloneableProps.id); + + var count = 0; + worldEntityProps.forEach(function(itemWE) { + if (itemWE.name.indexOf('-clone-' + cloneableProps.id) !== -1) { + count++; + } + }); + + var grabInfo = getGrabbableData(cloneableProps); + + var limit = grabInfo.cloneLimit ? grabInfo.cloneLimit : 0; + if (count >= limit && limit !== 0) { + return null; + } + + cloneableProps.name = cloneableProps.name + '-clone-' + cloneableProps.id; + var lifetime = grabInfo.cloneLifetime ? grabInfo.cloneLifetime : 300; + var dynamic = grabInfo.cloneDynamic ? grabInfo.cloneDynamic : false; + var cUserData = Object.assign({}, cloneableProps.userData); + var cProperties = Object.assign({}, cloneableProps); + + try { + delete cUserData.grabbableKey.cloneLifetime; + delete cUserData.grabbableKey.cloneable; + delete cUserData.grabbableKey.cloneDynamic; + delete cUserData.grabbableKey.cloneLimit; + delete cProperties.id; + } catch(e) { + } + + cProperties.dynamic = dynamic; + cProperties.locked = false; + if (!cUserData.grabbableKey) { + cUserData.grabbableKey = {}; + } + cUserData.grabbableKey.triggerable = true; + cUserData.grabbableKey.grabbable = true; + cProperties.lifetime = lifetime; + cProperties.userData = JSON.stringify(cUserData); + + var cloneID = Entities.addEntity(cProperties); + return cloneID; +} From ed52136fc5324d1203eba08256e1a8b79b8bd240 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 29 Aug 2017 15:18:28 -0700 Subject: [PATCH 36/59] finished equiping locked clone-able entities --- .../controllerModules/equipEntity.js | 46 +++++++++++++++++-- scripts/system/libraries/cloneEntityUtils.js | 29 ++++-------- 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index b9b8279dbd..08e4113c5d 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -15,6 +15,7 @@ Script.include("/~/system/libraries/Xform.js"); Script.include("/~/system/controllers/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); +Script.include("/~/system/libraries/cloneEntityUtils.js"); var DEFAULT_SPHERE_MODEL_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/equip-Fresnel-3.fbx"; @@ -321,7 +322,8 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa if (props.parentID === NULL_UUID) { hasParent = false; } - if (hasParent || props.locked || entityHasActions(hotspot.entityID)) { + + if (hasParent || entityHasActions(hotspot.entityID)) { return false; } @@ -370,6 +372,16 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa return equippableHotspots; }; + this.cloneHotspot = function(props, controllerData) { + if (entityIsCloneable(props)) { + var worldEntityProps = controllerData.nearbyEntityProperties[this.hand]; + var cloneID = cloneEntity(props, worldEntityProps); + return cloneID + } + + return null; + }; + this.chooseBestEquipHotspot = function(candidateEntityProps, controllerData) { var equippableHotspots = this.chooseNearEquipHotspots(candidateEntityProps, controllerData); if (equippableHotspots.length > 0) { @@ -434,11 +446,12 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa }; this.startEquipEntity = function (controllerData) { - print("------> starting to equip entity <-------"); this.dropGestureReset(); this.clearEquipHaptics(); Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); + var grabbedProperties = Entities.getEntityProperties(this.targetEntityID); + // if an object is "equipped" and has a predefined offset, use it. var offsets = getAttachPointForHotspotFromSettings(this.grabbedHotspot, this.hand); if (offsets) { @@ -467,7 +480,19 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa localPosition: this.offsetPosition, localRotation: this.offsetRotation }; - Entities.editEntity(this.targetEntityID, reparentProps); + var isClone = false; + if (entityIsCloneable(grabbedProperties)) { + var cloneID = this.cloneHotspot(grabbedProperties, controllerData); + this.targetEntityID = cloneID; + Entities.editEntity(this.targetEntityID, reparentProps); + isClone = true; + } else if (!grabbedProperties.locked) { + Entities.editEntity(this.targetEntityID, reparentProps); + } else { + this.grabbedHotspot = null; + this.targetEntityID = null; + return; + } var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.targetEntityID, "startEquip", args); @@ -477,6 +502,18 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa grabbedEntity: this.targetEntityID, joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" })); + + var _this = this; + var grabEquipCheck = function() { + var args = [_this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(_this.targetEntityID, "startEquip", args); + }; + + if (isClone) { + // 100 ms seems to be sufficient time to force the check even occur after the object has been initialized. + Script.setTimeout(grabEquipCheck, 100); + } + }; this.endEquipEntity = function () { @@ -523,6 +560,9 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa equipHotspotBuddy.update(deltaTime, timestamp, controllerData); + //if the potentialHotspot is cloneable, clone it and return it + // if the potentialHotspot os not cloneable and locked return null + if (potentialEquipHotspot) { if (this.triggerSmoothedSqueezed() && !this.waitForTriggerRelease) { this.grabbedHotspot = potentialEquipHotspot; diff --git a/scripts/system/libraries/cloneEntityUtils.js b/scripts/system/libraries/cloneEntityUtils.js index a862108a87..69c91fc398 100644 --- a/scripts/system/libraries/cloneEntityUtils.js +++ b/scripts/system/libraries/cloneEntityUtils.js @@ -39,13 +39,9 @@ entityIsCloneable = function(props) { } -cloneEntity = function(cloneableProps, worldEntityProps) { - if (!cloneableProps) { - return null; - } - +cloneEntity = function(props, worldEntityProps) { // we need all the properties, for this - cloneableProps = Entities.getEntityProperties(cloneableProps.id); + var cloneableProps = Entities.getEntityProperties(props.id); var count = 0; worldEntityProps.forEach(function(itemWE) { @@ -55,7 +51,6 @@ cloneEntity = function(cloneableProps, worldEntityProps) { }); var grabInfo = getGrabbableData(cloneableProps); - var limit = grabInfo.cloneLimit ? grabInfo.cloneLimit : 0; if (count >= limit && limit !== 0) { return null; @@ -64,23 +59,19 @@ cloneEntity = function(cloneableProps, worldEntityProps) { cloneableProps.name = cloneableProps.name + '-clone-' + cloneableProps.id; var lifetime = grabInfo.cloneLifetime ? grabInfo.cloneLifetime : 300; var dynamic = grabInfo.cloneDynamic ? grabInfo.cloneDynamic : false; - var cUserData = Object.assign({}, cloneableProps.userData); + var cUserData = Object.assign({}, JSON.parse(cloneableProps.userData)); var cProperties = Object.assign({}, cloneableProps); - try { - delete cUserData.grabbableKey.cloneLifetime; - delete cUserData.grabbableKey.cloneable; - delete cUserData.grabbableKey.cloneDynamic; - delete cUserData.grabbableKey.cloneLimit; - delete cProperties.id; - } catch(e) { - } + + delete cUserData.grabbableKey.cloneLifetime; + delete cUserData.grabbableKey.cloneable; + delete cUserData.grabbableKey.cloneDynamic; + delete cUserData.grabbableKey.cloneLimit; + delete cProperties.id; + cProperties.dynamic = dynamic; cProperties.locked = false; - if (!cUserData.grabbableKey) { - cUserData.grabbableKey = {}; - } cUserData.grabbableKey.triggerable = true; cUserData.grabbableKey.grabbable = true; cProperties.lifetime = lifetime; From 3960ec7d5a31194cf565f28cae09ef62e45bfea1 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 30 Aug 2017 15:20:03 -0700 Subject: [PATCH 37/59] finish main handControllerGrab refactoring --- .../controllerModules/farActionGrabEntity.js | 95 +++++++++++++----- .../controllerModules/inEditMode.js | 99 +++++++++++++++++++ .../nearParentGrabOverlay.js | 2 +- ...rlayLaserInput.js => overlayLaserInput.js} | 50 ++++++---- .../controllerModules/tabletStylusInput.js | 2 +- .../system/controllers/controllerScripts.js | 7 +- 6 files changed, 205 insertions(+), 50 deletions(-) create mode 100644 scripts/system/controllers/controllerModules/inEditMode.js rename scripts/system/controllers/controllerModules/{web3DOverlayLaserInput.js => overlayLaserInput.js} (92%) diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index b84ee4b822..8c5a6bf5a3 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -109,6 +109,8 @@ Script.include("/~/system/libraries/controllers.js"); this.hand = hand; this.grabbedThingID = null; this.actionID = null; // action this script created... + this.entityWithContextOverlay = false; + this.contextOverlayTimer = false; var ACTION_TTL = 15; // seconds @@ -335,11 +337,11 @@ Script.include("/~/system/libraries/controllers.js"); this.grabbedThingID = null; }; - this.pointingAtWebEntity = function(controllerData) { + this.notPointingAtEntity = function(controllerData) { var intersection = controllerData.rayPicks[this.hand]; var entityProperty = Entities.getEntityProperties(intersection.objectID); var entityType = entityProperty.type; - if ((intersection.type === RayPick.INTERSECTED_ENTITY && entityType === "Web") || intersection.objectID === HMD.tabletButtonID) { + if ((intersection.type === RayPick.INTERSECTED_ENTITY && entityType === "Web") || intersection.type === RayPick.INTERSECTED_OVERLAY) { return true; } return false @@ -388,8 +390,15 @@ Script.include("/~/system/libraries/controllers.js"); this.previousWorldControllerRotation = worldControllerRotation; }; + this.destroyContextOverlay = function(controllerData) { + if (this.entityWithContextOverlay) { + ContextOverlay.destroyContextOverlay(this.entityWithContextOverlay); + this.entityWithContextOverlay = false; + } + } + this.isReady = function (controllerData) { - if (this.pointingAtWebEntity(controllerData)) { + if (this.notPointingAtEntity(controllerData)) { return makeRunningValues(false, [], []); } @@ -401,17 +410,28 @@ Script.include("/~/system/libraries/controllers.js"); this.prepareDistanceRotatingData(controllerData); return makeRunningValues(true, [], []); } else { + this.destroyContextOverlay(); return makeRunningValues(false, [], []); } }; + this.isPointingAtUI = function(controllerData) { + var hudRayPickInfo = controllerData.hudRayPicks[this.hand]; + var hudPoint2d = HMD.overlayFromWorldPoint(hudRayPickInfo.intersection); + } + this.run = function (controllerData) { - if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || this.pointingAtWebEntity(controllerData)) { + if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || this.notPointingAtEntity(controllerData)) { this.endNearGrabAction(); this.laserPointerOff(); return makeRunningValues(false, [], []); } + var targetEntity = controllerData.rayPicks[this.hand].objectID; + if (targetEntity !== this.entityWithContextOverlay) { + this.destroyContextOverlay(); + } + // gather up the readiness of the near-grab modules var nearGrabNames = [ this.hand === RIGHT_HAND ? "RightNearActionGrabEntity" : "LeftNearActionGrabEntity", @@ -456,37 +476,58 @@ Script.include("/~/system/libraries/controllers.js"); var hudRayPickInfo = controllerData.hudRayPicks[this.hand]; var hudPoint2d = HMD.overlayFromWorldPoint(hudRayPickInfo.intersection); if (rayPickInfo.type == RayPick.INTERSECTED_ENTITY) { - var entityID = rayPickInfo.objectID; - var targetProps = Entities.getEntityProperties(entityID, ["dynamic", "shapeType", "position", - "rotation", "dimensions", "density", - "userData", "locked", "type"]); - if (entityIsDistanceGrabbable(targetProps)) { - if (!this.distanceRotating) { - this.grabbedThingID = entityID; - this.grabbedDistance = rayPickInfo.distance; - } - var otherModuleName = - this.hand == RIGHT_HAND ? "LeftFarActionGrabEntity" : "RightFarActionGrabEntity"; - var otherFarGrabModule = getEnabledModuleByName(otherModuleName); - if (otherFarGrabModule.grabbedThingID == this.grabbedThingID && otherFarGrabModule.distanceHolding) { - this.distanceRotate(otherFarGrabModule); - } else { - this.distanceHolding = true; - this.distanceRotating = false; - this.startFarGrabAction(controllerData, targetProps); + if (controllerData.triggerClicks[this.hand]) { + var entityID = rayPickInfo.objectID; + var targetProps = Entities.getEntityProperties(entityID, ["dynamic", "shapeType", "position", + "rotation", "dimensions", "density", + "userData", "locked", "type"]); + if (entityIsDistanceGrabbable(targetProps)) { + if (!this.distanceRotating) { + this.grabbedThingID = entityID; + this.grabbedDistance = rayPickInfo.distance; + } + var otherModuleName = + this.hand == RIGHT_HAND ? "LeftFarActionGrabEntity" : "RightFarActionGrabEntity"; + var otherFarGrabModule = getEnabledModuleByName(otherModuleName); + if (otherFarGrabModule.grabbedThingID == this.grabbedThingID && otherFarGrabModule.distanceHolding) { + this.distanceRotate(otherFarGrabModule); + } else { + this.distanceHolding = true; + this.distanceRotating = false; + this.startFarGrabAction(controllerData, targetProps); + } } + } else if (!this.entityWithContextOverlay && !this.contextOverlayTimer) { + var _this = this; + _this.contextOverlayTimer = Script.setTimeout(function () { + if (!_this.entityWithContextOverlay && _this.contextOverlayTimer) { + var props = Entities.getEntityProperties(rayPickInfo.objectID); + var pointerEvent = { + type: "Move", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(rayPickInfo.objectID, rayPickInfo.intersection, props), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.surfaceNormal, + direction: Vec3.subtract(ZERO_VEC, rayPickInfo.surfaceNormal), + button: "Secondary" + }; + if (ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.objectID, pointerEvent)) { + _this.entityWithContextOverlay = rayPickInfo.objectID; + } + } + _this.contextOverlayTimer = false; + }, 500); } } else if (this.distanceRotating) { - var otherModuleName = - this.hand == RIGHT_HAND ? "LeftFarActionGrabEntity" : "RightFarActionGrabEntity"; + var otherModuleName = + this.hand == RIGHT_HAND ? "LeftFarActionGrabEntity" : "RightFarActionGrabEntity"; var otherFarGrabModule = getEnabledModuleByName(otherModuleName); this.distanceRotate(otherFarGrabModule); - } - /* else if (Reticle.pointingAtSystemOverlay || Overlays.getOverlayAtPoint(hudPoint2d || Reticle.position)) { + } else if (Reticle.pointingAtSystemOverlay || Overlays.getOverlayAtPoint(hudPoint2d || Reticle.position)) { this.endNearGrabAction(); this.laserPointerOff(); return makeRunningValues(false, [], []); - }*/ + } } return makeRunningValues(true, [], []); }; diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js new file mode 100644 index 0000000000..67da6c18ac --- /dev/null +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -0,0 +1,99 @@ +"use strict" + +// inEditMode.js +// +// 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, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, + NULL_UUID, enableDispatcherModule, disableDispatcherModule, makeRunningValues, + Messages, Quat, Vec3, getControllerWorldLocation, makeDispatcherModuleParameters, Overlays, ZERO_VEC, + AVATAR_SELF_ID, HMD, INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, Settings, getGrabPointSphereOffset +*/ + +Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllers.js"); +Script.include("/~/system/libraries/utils.js"); + +(function () { + + function InEditMode(hand) { + this.hand = hand; + + this.parameters = makeDispatcherModuleParameters( + 160, + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + 100); + + this.nearTablet = function(overlays) { + for (var i = 0; i < overlays.length; i++) { + if (overlays[i] === HMD.tabletID) { + return true; + } + } + + return false; + }; + + this.pointingAtTablet = function(objectID) { + if (objectID === HMD.tabletScreenID || objectID === HMD.tabletButtonID) { + return true; + } + return false; + }; + + this.isReady = function(controllerData) { + var overlays = controllerData.nearbyOverlayIDs[this.hand]; + var objectID = controllerData.rayPicks[this.hand].objectID; + + if (isInEditMode()) { + return makeRunningValues(true, [], []); + } + + return makeRunningValues(false, [], []); + }; + + this.run = function(controllerData) { + var tabletStylusInput = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightTabletStylusInput" : "LeftTabletStylusInput"); + if (tabletStylusInput) { + var tabletReady = tabletStylusInput.isReady(controllerData); + + if (tabletReady.active) { + return makeRunningValues(false, [], []); + } + } + + var overlayLaser = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightOverlayLaserInput" : "LeftOverlayLaserInput"); + if (overlayLaser) { + var overlayLaserReady = overlayLaser.isReady(controllerData); + + if (overlayLaserReady.active && this.pointingAtTablet(overlayLaser.target)) { + return makeRunningValues(false, [], []); + } + } + + var nearOverlay = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay"); + if (nearOverlay) { + var nearOverlayReady = nearOverlay.isReady(controllerData); + + if (nearOverlayReady.active && nearOverlay.grabbedThingID === HMD.tabletID) { + return makeRunningValues(false, [], []); + } + } + + return this.isReady(controllerData); + }; + }; + + + var leftHandInEditMode = new InEditMode(LEFT_HAND); + var rightHandInEditMode = new InEditMode(RIGHT_HAND); + + enableDispatcherModule("LeftHandInEditMode", leftHandInEditMode); + enableDispatcherModule("RightHandInEditMode", rightHandInEditMode); + + this.cleanup = function() { + disableDispatcherModule("LeftHandInEditMode"); + disableDispatcherModule("RightHandInEditMode"); + }; +}()); diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js index 399814a0ed..2cea81ce18 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -28,7 +28,7 @@ var GRAB_RADIUS = 0.35; this.previouslyUnhooked = {}; this.parameters = makeDispatcherModuleParameters( - 500, + 140, this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], [], 100); diff --git a/scripts/system/controllers/controllerModules/web3DOverlayLaserInput.js b/scripts/system/controllers/controllerModules/overlayLaserInput.js similarity index 92% rename from scripts/system/controllers/controllerModules/web3DOverlayLaserInput.js rename to scripts/system/controllers/controllerModules/overlayLaserInput.js index 501c37fd35..8a14fc49ae 100644 --- a/scripts/system/controllers/controllerModules/web3DOverlayLaserInput.js +++ b/scripts/system/controllers/controllerModules/overlayLaserInput.js @@ -1,6 +1,6 @@ "use strict" -// tabletLaserInput.js +// overlayLaserInput.js // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -270,7 +270,7 @@ Script.include("/~/system/libraries/controllers.js"); return Math.sqrt(dx * dx + dy * dy); } - function TabletLaserInput(hand) { + function OverlayLaserInput(hand) { this.hand = hand; this.active = false; this.previousLaserClikcedTarget = false; @@ -281,11 +281,12 @@ Script.include("/~/system/libraries/controllers.js"); this.laserTarget = null; this.pressEnterLaserTarget = null; this.hover = false; + this.target = null; this.lastValidTargetID = this.tabletTargetID; this.parameters = makeDispatcherModuleParameters( - 200, + 120, this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], [], 100); @@ -295,7 +296,7 @@ Script.include("/~/system/libraries/controllers.js"); }; this.getOtherModule = function() { - return (this.hand === RIGHT_HAND) ? leftTabletLaserInput : rightTabletLaserInput; + return (this.hand === RIGHT_HAND) ? leftOverlayLaserInput : rightOverlayLaserInput; }; this.handToController = function() { @@ -313,10 +314,6 @@ Script.include("/~/system/libraries/controllers.js"); } }; - this.hasTouchFocus = function(laserTarget) { - return (this.laserTargetID === HMD.tabletScreenID); - }; - this.relinquishTouchFocus = function() { // send hover leave event. var pointerEvent = { type: "Move", id: this.hand + 1 }; @@ -449,12 +446,28 @@ Script.include("/~/system/libraries/controllers.js"); this.active = false; }; + this.deleteContextOverlay = function() { + var farGrabModule = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightFarActionGrabEntity" : "LeftFarActionGrabEntity"); + if (farGrabModule) { + var entityWithContextOverlay = farGrabModule.entityWithContextOverlay; + + if (entityWithContextOverlay) { + ContextOverlay.destroyContextOverlay(entityWithContextOverlay); + farGrabModule.entityWithContextOverlay = false; + } + } + }; + this.isReady = function (controllerData) { + this.target = null; var intersection = controllerData.rayPicks[this.hand]; if (intersection.type === RayPick.INTERSECTED_OVERLAY) { if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE && !this.getOtherModule().active) { + this.target = intersection.objectID; this.active = true; return makeRunningValues(true, [], []); + } else { + this.deleteContextOverlay(); } } this.reset(); @@ -462,12 +475,15 @@ Script.include("/~/system/libraries/controllers.js"); }; this.run = function (controllerData, deltaTime) { - if (this.shouldExit(controllerData)) { this.exitModule(); return makeRunningValues(false, [], []); } + if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE) { + this.deleteContextOverlay(); + } + this.updateLaserTargets(controllerData); this.processControllerTriggers(controllerData); this.updateLaserPointer(controllerData); @@ -511,17 +527,17 @@ Script.include("/~/system/libraries/controllers.js"); LaserPointers.setIgnoreOverlays(this.laserPointer, [HMD.tabletID]); }; - var leftTabletLaserInput = new TabletLaserInput(LEFT_HAND); - var rightTabletLaserInput = new TabletLaserInput(RIGHT_HAND); + var leftOverlayLaserInput = new OverlayLaserInput(LEFT_HAND); + var rightOverlayLaserInput = new OverlayLaserInput(RIGHT_HAND); - enableDispatcherModule("LeftTabletLaserInput", leftTabletLaserInput); - enableDispatcherModule("RightTabletLaserInput", rightTabletLaserInput); + enableDispatcherModule("LeftOverlayLaserInput", leftOverlayLaserInput); + enableDispatcherModule("RightOverlayLaserInput", rightOverlayLaserInput); this.cleanup = function () { - leftTabletLaserInput.cleanup(); - rightTabletLaserInput.cleanup(); - disableDispatcherModule("LeftTabletLaserInput"); - disableDispatcherModule("RightTabletLaserInput"); + leftOverlayLaserInput.cleanup(); + rightOverlayLaserInput.cleanup(); + disableDispatcherModule("LeftOverlayLaserInput"); + disableDispatcherModule("RightOverlayLaserInput"); }; Script.scriptEnding.connect(this.cleanup); }()); diff --git a/scripts/system/controllers/controllerModules/tabletStylusInput.js b/scripts/system/controllers/controllerModules/tabletStylusInput.js index 8ada1b31d7..bbe8f935ae 100644 --- a/scripts/system/controllers/controllerModules/tabletStylusInput.js +++ b/scripts/system/controllers/controllerModules/tabletStylusInput.js @@ -319,7 +319,7 @@ Script.include("/~/system/libraries/controllers.js"); this.parameters = makeDispatcherModuleParameters( - 400, + 100, this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], [], 100); diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index c9f030231a..cd572b901a 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -12,11 +12,9 @@ var CONTOLLER_SCRIPTS = [ "squeezeHands.js", "controllerDisplayManager.js", - // "handControllerGrab.js", "handControllerPointer.js", - // "grab.js", + "grab.js", "toggleAdvancedMovementForHandControllers.js", - "ControllerDispatcher.js", "controllerModules/nearParentGrabEntity.js", "controllerModules/nearParentGrabOverlay.js", @@ -25,8 +23,9 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/tabletStylusInput.js", "controllerModules/equipEntity.js", "controllerModules/nearTrigger.js", - "controllerModules/web3DOverlayLaserInput.js", + "controllerModules/overlayLaserInput.js", "controllerModules/webEntityLaserInput.js", + "controllerModules/inEditMode.js", "teleport.js" ]; From 47699d4439c304004892a0371c2ecb48decbe883 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 30 Aug 2017 16:52:59 -0700 Subject: [PATCH 38/59] fixed edit.js --- .../controllerModules/inEditMode.js | 145 +++++++++++++++++- .../controllerModules/webEntityLaserInput.js | 4 +- 2 files changed, 146 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index 67da6c18ac..782fb5dbc2 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -16,9 +16,73 @@ Script.include("/~/system/libraries/controllers.js"); Script.include("/~/system/libraries/utils.js"); (function () { + 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 + drawInFront: 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, + drawInFront: 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 + drawInFront: 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, + drawInFront: 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 + drawInFront: 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}]; function InEditMode(hand) { this.hand = hand; + this.triggerClicked = false; + this.mode = "none"; this.parameters = makeDispatcherModuleParameters( 160, @@ -31,10 +95,40 @@ Script.include("/~/system/libraries/utils.js"); return true; } } - return false; }; + + this.handToController = function() { + return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + }; + + this.processControllerTriggers = function(controllerData) { + if (controllerData.triggerClicks[this.hand]) { + this.mode = "full"; + } else if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { + this.mode = "half"; + } else { + this.mode = "none"; + } + }; + + 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.pointingAtTablet = function(objectID) { if (objectID === HMD.tabletScreenID || objectID === HMD.tabletButtonID) { return true; @@ -42,11 +136,33 @@ Script.include("/~/system/libraries/utils.js"); return false; }; + this.sendPickData = function(controllerData) { + if (controllerData.triggerClicks[this.hand] && !this.triggerClicked) { + var intersection = controllerData.rayPicks[this.hand]; + if (intersection.type === RayPick.INTERSECTED_ENTITY) { + Messages.sendLocalMessage("entityToolUpdates", JSON.stringify({ + method: "selectEntity", + entityID: intersection.objectID + })); + } else if (intersection.type === RayPick.INTERSECTED_OVERLAY) { + Messages.sendLocalMessage("entityToolUpdates", JSON.stringify({ + method: "selectOverlay", + overlayID: intersection.objectID + })); + } + + this.triggerClicked = true; + } else { + this.triggerClicked = false; + } + }; + this.isReady = function(controllerData) { var overlays = controllerData.nearbyOverlayIDs[this.hand]; var objectID = controllerData.rayPicks[this.hand].objectID; if (isInEditMode()) { + this.triggerClicked = false; return makeRunningValues(true, [], []); } @@ -80,9 +196,34 @@ Script.include("/~/system/libraries/utils.js"); return makeRunningValues(false, [], []); } } + this.processControllerTriggers(controllerData); + this.updateLaserPointer(controllerData); + this.sendPickData(controllerData); + 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_ENTITIES | RayPick.PICK_OVERLAYS, + maxDistance: PICK_MAX_DISTANCE, + posOffset: getGrabPointSphereOffset(this.handToController()), + renderStates: renderStates, + faceAvatar: true, + defaultRenderStates: defaultRenderStates + }); + + LaserPointers.setIgnoreOverlays(this.laserPointer, [HMD.tabletID, HMD.tabletButtonID, HMD.tabletScreenID]); }; @@ -93,6 +234,8 @@ Script.include("/~/system/libraries/utils.js"); enableDispatcherModule("RightHandInEditMode", rightHandInEditMode); this.cleanup = function() { + leftHandInEditMode.cleanup(); + rightHandInEditMode.cleanup(); disableDispatcherModule("LeftHandInEditMode"); disableDispatcherModule("RightHandInEditMode"); }; diff --git a/scripts/system/controllers/controllerModules/webEntityLaserInput.js b/scripts/system/controllers/controllerModules/webEntityLaserInput.js index 31e3f50bb6..e88e75684c 100644 --- a/scripts/system/controllers/controllerModules/webEntityLaserInput.js +++ b/scripts/system/controllers/controllerModules/webEntityLaserInput.js @@ -256,7 +256,7 @@ Script.include("/~/system/libraries/controllers.js"); }; this.getOtherModule = function() { - return (this.hand === RIGHT_HAND) ? leftWebEntityLaserInput : rightWebEntityLaserInput; + return (this.hand === RIGHT_HAND) ? leftWebEntityLaserInput : rightWebEntityLaserInput; }; this.parameters = makeDispatcherModuleParameters( @@ -450,7 +450,7 @@ Script.include("/~/system/libraries/controllers.js"); } this.laserPointer = LaserPointers.createLaserPointer({ - joint: (this.hand == RIGHT_HAND) ? "_CONTROLLER_RIGHTHAND" : "_CONTROLLER_LEFTHAND", + joint: (this.hand === RIGHT_HAND) ? "_CONTROLLER_RIGHTHAND" : "_CONTROLLER_LEFTHAND", filter: RayPick.PICK_ENTITIES, maxDistance: PICK_MAX_DISTANCE, posOffset: getGrabPointSphereOffset(this.handToController()), From 7cf27c18d3450b83908ca0fe0c140031bbb6419f Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 31 Aug 2017 18:06:55 -0700 Subject: [PATCH 39/59] code cleanup and fix broken features --- .eslintrc.js | 5 +- .../controllers/controllerDispatcher.js | 51 +++++- .../controllerModules/cloneEntity.js | 159 ------------------ .../controllerModules/equipEntity.js | 98 ++++++++--- .../controllerModules/farActionGrabEntity.js | 106 ++++++------ .../controllerModules/inEditMode.js | 71 ++++---- .../nearParentGrabOverlay.js | 2 +- .../controllerModules/overlayLaserInput.js | 63 +++---- .../controllerModules/tabletStylusInput.js | 44 ++--- .../controllerModules/webEntityLaserInput.js | 68 ++++---- 10 files changed, 316 insertions(+), 351 deletions(-) delete mode 100644 scripts/system/controllers/controllerModules/cloneEntity.js diff --git a/.eslintrc.js b/.eslintrc.js index b4d88777f2..83fda730e5 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -54,7 +54,10 @@ module.exports = { "Window": false, "XMLHttpRequest": false, "location": false, - "print": false + "print": false, + "RayPick": false, + "LaserPointers": false, + "ContextOverlay": false }, "rules": { "brace-style": ["error", "1tbs", { "allowSingleLine": false }], diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 18b4c287c0..9469b59a95 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -34,6 +34,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); var highVarianceCount = 0; var veryhighVarianceCount = 0; this.tabletID = null; + this.blacklist = []; // a module can occupy one or more "activity" slots while it's running. If all the required slots for a module are // not set to false (not in use), a module cannot start. When a module is using a slot, that module's name @@ -293,6 +294,12 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); } }; + this.setBlacklist = function() { + RayPick.setIgnoreEntities(_this.leftControllerRayPick, this.blacklist); + RayPick.setIgnoreEntities(_this.rightControllerRayPick, this.blacklist); + + }; + var MAPPING_NAME = "com.highfidelity.controllerDispatcher"; var mapping = Controller.newMapping(MAPPING_NAME); mapping.from([Controller.Standard.RT]).peek().to(_this.rightTriggerPress); @@ -324,27 +331,60 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); joint: "_CONTROLLER_LEFTHAND", filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, enabled: true, - maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE + maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE, + posOffset: getGrabPointSphereOffset(Controller.Standard.LeftHand) }); this.leftControllerHudRayPick = RayPick.createRayPick({ joint: "_CONTROLLER_LEFTHAND", filter: RayPick.PICK_HUD, enabled: true, - maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE + maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE, + posOffset: getGrabPointSphereOffset(Controller.Standard.LeftHand) }); this.rightControllerRayPick = RayPick.createRayPick({ joint: "_CONTROLLER_RIGHTHAND", filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, enabled: true, - maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE + maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE, + posOffset: getGrabPointSphereOffset(Controller.Standard.RightHand) }); this.rightControllerHudRayPick = RayPick.createRayPick({ joint: "_CONTROLLER_RIGHTHAND", filter: RayPick.PICK_HUD, enabled: true, - maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE + maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE, + posOffset: getGrabPointSphereOffset(Controller.Standard.RightHand) }); + this.handleHandMessage = function(channel, message, sender) { + var data + if (sender === MyAvatar.sessionUUID) { + try { + if (channel === 'Hifi-Hand-RayPick-Blacklist') { + data = JSON.parse(message); + var action = data.action; + var id = data.id; + var index = this.blacklis.indexOf(id); + + if (action === 'add' && index === -1) { + this.blacklist.push(id); + //this.setBlacklist(); + } + + if (action === 'remove') { + if (index > -1) { + blacklist.splice(index, 1); + //this.setBlacklist(); + } + } + } + + } catch (e) { + print("WARNING: handControllerGrab.js -- error parsing Hifi-Hand-RayPick-Blacklist message: " + message); + } + } + }; + @@ -357,7 +397,8 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); RayPick.removeRayPick(_this.rightControllerHudRayPick); RayPick.removeRayPick(_this.leftControllerHudRayPick); }; - + Messages.subscribe('Hifi-Hand-RayPick-Blacklist'); + Messages.messageReceived.connect(this.handleHandMessage); Script.scriptEnding.connect(this.cleanup); Script.update.connect(this.update); }()); diff --git a/scripts/system/controllers/controllerModules/cloneEntity.js b/scripts/system/controllers/controllerModules/cloneEntity.js deleted file mode 100644 index cfe9cb56da..0000000000 --- a/scripts/system/controllers/controllerModules/cloneEntity.js +++ /dev/null @@ -1,159 +0,0 @@ -"use strict"; - -// cloneEntity.js -// -// 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, Entities, RIGHT_HAND, LEFT_HAND, - enableDispatcherModule, disableDispatcherModule, getGrabbableData, Vec3, - TRIGGER_ON_VALUE, TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, makeRunningValues, NEAR_GRAB_RADIUS -*/ - -Script.include("/~/system/controllers/controllerDispatcherUtils.js"); - -// Object assign polyfill -if (typeof Object.assign != 'function') { - Object.assign = function(target, varArgs) { - if (target === null) { - throw new TypeError('Cannot convert undefined or null to object'); - } - var to = Object(target); - for (var index = 1; index < arguments.length; index++) { - var nextSource = arguments[index]; - if (nextSource !== null) { - for (var nextKey in nextSource) { - if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { - to[nextKey] = nextSource[nextKey]; - } - } - } - } - return to; - }; -} - -(function() { - - function entityIsCloneable(props) { - var grabbableData = getGrabbableData(props); - return grabbableData.cloneable; - } - - function CloneEntity(hand) { - this.hand = hand; - this.grabbing = false; - this.previousParentID = {}; - this.previousParentJointIndex = {}; - this.previouslyUnhooked = {}; - - this.parameters = makeDispatcherModuleParameters( - 300, - this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], - [], - 100); - - this.getTargetProps = function (controllerData) { - // nearbyEntityProperties is already sorted by length from controller - var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand]; - for (var i = 0; i < nearbyEntityProperties.length; i++) { - var props = nearbyEntityProperties[i]; - var handPosition = controllerData.controllerLocations[this.hand].position; - var distance = Vec3.distance(props.position, handPosition); - if (distance > NEAR_GRAB_RADIUS) { - break; - } - if (entityIsCloneable(props)) { - return props; - } - } - return null; - }; - - this.isReady = function (controllerData) { - if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE) { - this.waiting = false; - return makeRunningValues(false, [], []); - } - - if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { - if (!this.waiting) { - this.waiting = true; - return makeRunningValues(true, [], []); - } - } - return makeRunningValues(false, [], []); - }; - - this.run = function (controllerData, deltaTime) { - var cloneableProps = this.getTargetProps(controllerData); - if (!cloneableProps) { - return makeRunningValues(false, [], []); - } - - // we need all the properties, for this - cloneableProps = Entities.getEntityProperties(cloneableProps.id); - - var worldEntityProps = controllerData.nearbyEntityProperties[this.hand]; - var count = 0; - worldEntityProps.forEach(function(itemWE) { - if (itemWE.name.indexOf('-clone-' + cloneableProps.id) !== -1) { - count++; - } - }); - - var grabInfo = getGrabbableData(cloneableProps); - - var limit = grabInfo.cloneLimit ? grabInfo.cloneLimit : 0; - if (count >= limit && limit !== 0) { - return makeRunningValues(false, [], []); - } - - cloneableProps.name = cloneableProps.name + '-clone-' + cloneableProps.id; - var lifetime = grabInfo.cloneLifetime ? grabInfo.cloneLifetime : 300; - var dynamic = grabInfo.cloneDynamic ? grabInfo.cloneDynamic : false; - var cUserData = Object.assign({}, cloneableProps.userData); - var cProperties = Object.assign({}, cloneableProps); - - try { - delete cUserData.grabbableKey.cloneLifetime; - delete cUserData.grabbableKey.cloneable; - delete cUserData.grabbableKey.cloneDynamic; - delete cUserData.grabbableKey.cloneLimit; - delete cProperties.id; - } catch(e) { - } - - cProperties.dynamic = dynamic; - cProperties.locked = false; - if (!cUserData.grabbableKey) { - cUserData.grabbableKey = {}; - } - cUserData.grabbableKey.triggerable = true; - cUserData.grabbableKey.grabbable = true; - cProperties.lifetime = lifetime; - cProperties.userData = JSON.stringify(cUserData); - // var cloneID = - Entities.addEntity(cProperties); - return makeRunningValues(false, [], []); - }; - - this.cleanup = function () { - }; - } - - var leftCloneEntity = new CloneEntity(LEFT_HAND); - var rightCloneEntity = new CloneEntity(RIGHT_HAND); - - enableDispatcherModule("LeftCloneEntity", leftCloneEntity); - enableDispatcherModule("RightCloneEntity", rightCloneEntity); - - this.cleanup = function () { - leftNearParentingGrabEntity.cleanup(); - rightNearParentingGrabEntity.cleanup(); - disableDispatcherModule("LeftNearParentingGrabEntity"); - disableDispatcherModule("RightNearParentingGrabEntity"); - }; - Script.scriptEnding.connect(this.cleanup); -}()); diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index 08e4113c5d..5ad7fc9e16 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -9,7 +9,8 @@ /* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, getControllerJointIndex, NULL_UUID, enableDispatcherModule, disableDispatcherModule, Messages, makeDispatcherModuleParameters, makeRunningValues, Settings, entityHasActions, - Vec3, Overlays, flatten, Xform, getControllerWorldLocation, ensureDynamic + Vec3, Overlays, flatten, Xform, getControllerWorldLocation, ensureDynamic, entityIsCloneable, + cloneEntity */ Script.include("/~/system/libraries/Xform.js"); @@ -105,14 +106,14 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var overlayInfoSet = this.map[keys[i]]; // this overlayInfo is highlighted. - if (this.highlightedHotspots.indexOf(keys[i]) != -1) { + if (this.highlightedHotspots.indexOf(keys[i]) !== -1) { overlayInfoSet.targetSize = HIGHLIGHT_SIZE; } else { overlayInfoSet.targetSize = NORMAL_SIZE; } // start to fade out this hotspot. - if (overlayInfoSet.timestamp != timestamp) { + if (overlayInfoSet.timestamp !== timestamp) { overlayInfoSet.targetSize = 0; } @@ -124,7 +125,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa } overlayInfoSet.currentSize += (overlayInfoSet.targetSize - overlayInfoSet.currentSize) * tau; - if (overlayInfoSet.timestamp != timestamp && overlayInfoSet.currentSize <= 0.05) { + if (overlayInfoSet.timestamp !== timestamp && overlayInfoSet.currentSize <= 0.05) { // this is an old overlay, that has finished fading out, delete it! overlayInfoSet.overlays.forEach(Overlays.deleteOverlay); delete this.map[keys[i]]; @@ -136,7 +137,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var position = entityXform.xformPoint(overlayInfoSet.localPosition); var dimensions; - if (overlayInfoSet.type == "sphere") { + if (overlayInfoSet.type === "sphere") { dimensions = overlayInfoSet.hotspot.radius * 2 * overlayInfoSet.currentSize * EQUIP_SPHERE_SCALE_FACTOR; } else { dimensions = overlayInfoSet.hotspot.radius * 2 * overlayInfoSet.currentSize; @@ -157,8 +158,6 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa } }; - - (function() { var ATTACH_POINT_SETTINGS = "io.highfidelity.attachPoints"; @@ -185,6 +184,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa if (!props.userDataParsed) { props.userDataParsed = JSON.parse(props.userData); } + wearable = props.userDataParsed.wearable ? props.userDataParsed.wearable : {}; } catch (err) { } @@ -196,6 +196,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa if (!props.userDataParsed) { props.userDataParsed = JSON.parse(props.userData); } + equipHotspots = props.userDataParsed.equipHotspots ? props.userDataParsed.equipHotspots : []; } catch (err) { } @@ -249,6 +250,8 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.targetEntityID = null; this.prevHandIsUpsideDown = false; this.triggerValue = 0; + this.messageGrabEntity = false; + this.grabEntityProps = null; this.parameters = makeDispatcherModuleParameters( 300, @@ -258,6 +261,13 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var equipHotspotBuddy = new EquipHotspotBuddy(); + this.setMessageGrabData = function(entityProperties) { + if (entityProperties) { + this.messageGrabEntity = true; + this.grabEntityProps = entityProperties; + } + }; + // returns a list of all equip-hotspots assosiated with this entity. // @param {UUID} entityID // @returns {Object[]} array of objects with the following fields. @@ -322,7 +332,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa if (props.parentID === NULL_UUID) { hasParent = false; } - + if (hasParent || entityHasActions(hotspot.entityID)) { return false; } @@ -376,7 +386,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa if (entityIsCloneable(props)) { var worldEntityProps = controllerData.nearbyEntityProperties[this.hand]; var cloneID = cloneEntity(props, worldEntityProps); - return cloneID + return cloneID; } return null; @@ -420,7 +430,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa handIsUpsideDown = true; } - if (handIsUpsideDown != this.prevHandIsUpsideDown) { + if (handIsUpsideDown !== this.prevHandIsUpsideDown) { this.prevHandIsUpsideDown = handIsUpsideDown; Controller.triggerHapticPulse(HAPTIC_DEQUIP_STRENGTH, HAPTIC_DEQUIP_DURATION, this.hand); } @@ -486,7 +496,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.targetEntityID = cloneID; Entities.editEntity(this.targetEntityID, reparentProps); isClone = true; - } else if (!grabbedProperties.locked) { + } else if (!grabbedProperties.locked) { Entities.editEntity(this.targetEntityID, reparentProps); } else { this.grabbedHotspot = null; @@ -508,12 +518,12 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var args = [_this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(_this.targetEntityID, "startEquip", args); }; - + if (isClone) { // 100 ms seems to be sufficient time to force the check even occur after the object has been initialized. Script.setTimeout(grabEquipCheck, 100); - } - + } + }; this.endEquipEntity = function () { @@ -546,8 +556,18 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var controllerLocation = getControllerWorldLocation(this.handToController(), true); var worldHandPosition = controllerLocation.position; var candidateEntityProps = controllerData.nearbyEntityProperties[this.hand]; - - var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntityProps, controllerData); + + + var potentialEquipHotspot = null; + if (this.messageGrabEntity) { + var hotspots = this.collectEquipHotspots(this.grabEntityProps); + if (hotspots.length > -1) { + potentialEquipHotspot = hotspots[0]; + } + } else { + potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntityProps, controllerData); + } + if (!this.waitForTriggerRelease) { this.updateEquipHaptics(potentialEquipHotspot, worldHandPosition); } @@ -560,17 +580,19 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa equipHotspotBuddy.update(deltaTime, timestamp, controllerData); - //if the potentialHotspot is cloneable, clone it and return it + // if the potentialHotspot is cloneable, clone it and return it // if the potentialHotspot os not cloneable and locked return null - + if (potentialEquipHotspot) { - if (this.triggerSmoothedSqueezed() && !this.waitForTriggerRelease) { + if ((this.triggerSmoothedSqueezed() && !this.waitForTriggerRelease) || this.messageGrabEntity) { this.grabbedHotspot = potentialEquipHotspot; this.targetEntityID = this.grabbedHotspot.entityID; this.startEquipEntity(controllerData); + this.messageGrabEnity = false; } return makeRunningValues(true, [potentialEquipHotspot.entityID], []); } else { + this.messageGrabEnity = false; return makeRunningValues(false, [], []); } }; @@ -610,7 +632,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.waitForTriggerRelease = false; } - if (dropDetected && this.prevDropDetected != dropDetected) { + if (dropDetected && this.prevDropDetected !== dropDetected) { this.waitForTriggerRelease = true; } @@ -627,7 +649,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var prefprops = Entities.getEntityProperties(this.targetEntityID, ["localPosition", "localRotation"]); if (prefprops && prefprops.localPosition && prefprops.localRotation) { storeAttachPointForHotspotInSettings(this.grabbedHotspot, this.hand, - prefprops.localPosition, prefprops.localRotation); + prefprops.localPosition, prefprops.localRotation); } } @@ -651,6 +673,40 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa }; } + var handleMessage = function(channel, message, sender) { + var data; + print(channel); + if (sender === MyAvatar.sessionUUID) { + if (channel === 'Hifi-Hand-Grab') { + try { + data = JSON.parse(message); + var equipModule = (data.hand === 'left') ? leftEquipEntity : rightEquipEntity; + var entityProperties = Entities.getEntityProperties(data.entityID, DISPATCHER_PROPERTIES); + entityProperties.id = data.entityID; + equipModule.setMessageGrabData(entityProperties); + + } catch (e) { + print("WARNING: equipEntity.js -- error parsing Hifi-Hand-Grab message: " + message); + } + } + } else if (channel === 'Hifi-Hand-Drop') { + data = JSON.parse(message); + if (data.hand === 'left') { + leftEquipEntity.endEquipEntity(); + } else if (data.hand === 'right') { + rightEquipEntity.endEquipEntity(); + } else if (data.hand === 'both') { + leftEquipEntity.endEquipEntity(); + rightEquipEntity.endEquipEntity(); + } + } + + }; + + Messages.subscribe('Hifi-Hand-Grab'); + Messages.subscribe('Hifi-Hand-Drop'); + Messages.messageReceived.connect(handleMessage); + var leftEquipEntity = new EquipEntity(LEFT_HAND); var rightEquipEntity = new EquipEntity(RIGHT_HAND); diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 8c5a6bf5a3..2b748e60d6 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -5,14 +5,15 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/*jslint bitwise: true */ +/* jslint bitwise: true */ /* global Script, Controller, LaserPointers, RayPick, RIGHT_HAND, LEFT_HAND, Mat4, MyAvatar, Vec3, Camera, Quat, getGrabPointSphereOffset, getEnabledModuleByName, makeRunningValues, Entities, NULL_UUID, enableDispatcherModule, disableDispatcherModule, entityIsDistanceGrabbable, 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, + AVATAR_SELF_ID, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic, + getControllerWorldLocation, projectOntoEntityXYPlane */ @@ -77,14 +78,18 @@ Script.include("/~/system/libraries/controllers.js"); drawInFront: 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}]; + 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} + ]; var GRABBABLE_PROPERTIES = [ "position", @@ -132,10 +137,7 @@ Script.include("/~/system/libraries/controllers.js"); var dim = {x: radius, y: radius, z: radius}; var mode = "hold"; if (!this.distanceHolding && !this.distanceRotating) { - // mode = (this.triggerSmoothedGrab() || this.secondarySqueezed()) ? "full" : "half"; - if (controllerData.triggerClicks[this.hand] - // || this.secondarySqueezed() // XXX - ) { + if (controllerData.triggerClicks[this.hand]) { mode = "full"; } else { mode = "half"; @@ -339,44 +341,44 @@ Script.include("/~/system/libraries/controllers.js"); this.notPointingAtEntity = function(controllerData) { var intersection = controllerData.rayPicks[this.hand]; - var entityProperty = Entities.getEntityProperties(intersection.objectID); + var entityProperty = Entities.getEntityProperties(intersection.objectID); var entityType = entityProperty.type; - if ((intersection.type === RayPick.INTERSECTED_ENTITY && entityType === "Web") || intersection.type === RayPick.INTERSECTED_OVERLAY) { + if ((intersection.type === RayPick.INTERSECTED_ENTITY && entityType === "Web") || intersection.type === RayPick.INTERSECTED_OVERLAY) { return true; } - return false + return false; }; this.distanceRotate = function(otherFarGrabModule) { - this.distanceRotating = true; + this.distanceRotating = true; this.distanceHolding = false; var worldControllerRotation = getControllerWorldLocation(this.handToController(), true).orientation; var controllerRotationDelta = Quat.multiply(worldControllerRotation, Quat.inverse(this.previousWorldControllerRotation)); // Rotate entity by twice the delta rotation. controllerRotationDelta = Quat.multiply(controllerRotationDelta, controllerRotationDelta); - + // Perform the rotation in the translation controller's action update. otherFarGrabModule.currentObjectRotation = Quat.multiply(controllerRotationDelta, - otherFarGrabModule.currentObjectRotation); - + otherFarGrabModule.currentObjectRotation); + // Rotate about the translation controller's target position. this.offsetPosition = Vec3.multiplyQbyV(controllerRotationDelta, this.offsetPosition); otherFarGrabModule.offsetPosition = Vec3.multiplyQbyV(controllerRotationDelta, - otherFarGrabModule.offsetPosition); - + otherFarGrabModule.offsetPosition); + this.updateLaserPointer(); - + this.previousWorldControllerRotation = worldControllerRotation; }; this.prepareDistanceRotatingData = function(controllerData) { var intersection = controllerData.rayPicks[this.hand]; - + var controllerLocation = getControllerWorldLocation(this.handToController(), true); var worldControllerPosition = controllerLocation.position; var worldControllerRotation = controllerLocation.orientation; - + var grabbedProperties = Entities.getEntityProperties(intersection.objectID, GRABBABLE_PROPERTIES); this.currentObjectPosition = grabbedProperties.position; this.grabRadius = intersection.distance; @@ -385,7 +387,7 @@ Script.include("/~/system/libraries/controllers.js"); var targetPosition = Vec3.multiply(this.grabRadius, Quat.getUp(worldControllerRotation)); targetPosition = Vec3.sum(targetPosition, worldControllerPosition); this.offsetPosition = Vec3.subtract(this.currentObjectPosition, targetPosition); - + // Initial controller rotation. this.previousWorldControllerRotation = worldControllerRotation; }; @@ -395,13 +397,13 @@ Script.include("/~/system/libraries/controllers.js"); ContextOverlay.destroyContextOverlay(this.entityWithContextOverlay); this.entityWithContextOverlay = false; } - } + }; this.isReady = function (controllerData) { if (this.notPointingAtEntity(controllerData)) { return makeRunningValues(false, [], []); } - + this.distanceHolding = false; this.distanceRotating = false; @@ -418,10 +420,15 @@ 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; + }; this.run = function (controllerData) { - if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || this.notPointingAtEntity(controllerData)) { + if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || this.notPointingAtEntity(controllerData) || this.isPointingAtUI(controllerData)) { this.endNearGrabAction(); this.laserPointerOff(); return makeRunningValues(false, [], []); @@ -432,12 +439,16 @@ Script.include("/~/system/libraries/controllers.js"); this.destroyContextOverlay(); } + var otherModuleName =this.hand === RIGHT_HAND ? "LeftFarActionGrabEntity" : "RightFarActionGrabEntity"; + var otherFarGrabModule = getEnabledModuleByName(otherModuleName); + // gather up the readiness of the near-grab modules var nearGrabNames = [ this.hand === RIGHT_HAND ? "RightNearActionGrabEntity" : "LeftNearActionGrabEntity", this.hand === RIGHT_HAND ? "RightNearParentingGrabEntity" : "LeftNearParentingGrabEntity", this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay" ]; + var nearGrabReadiness = []; for (var i = 0; i < nearGrabNames.length; i++) { var nearGrabModule = getEnabledModuleByName(nearGrabNames[i]); @@ -449,19 +460,14 @@ Script.include("/~/system/libraries/controllers.js"); // if we are doing a distance grab and the object gets close enough to the controller, // stop the far-grab so the near-grab or equip can take over. for (var k = 0; k < nearGrabReadiness.length; k++) { - if (nearGrabReadiness[k].active && nearGrabReadiness[k].targets[0] == this.grabbedThingID) { + if (nearGrabReadiness[k].active && nearGrabReadiness[k].targets[0] === this.grabbedThingID) { this.laserPointerOff(); this.endNearGrabAction(); return makeRunningValues(false, [], []); } } - this.continueDistanceHolding(controllerData); - // this.updateLaserPointer(controllerData, false, false); - - // var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; - // Entities.callEntityMethod(this.grabbedThingID, "continueFarGrab", args); } else { // if we are doing a distance search and this controller moves into a position // where it could near-grab something, stop searching. @@ -473,23 +479,22 @@ Script.include("/~/system/libraries/controllers.js"); } var rayPickInfo = controllerData.rayPicks[this.hand]; - var hudRayPickInfo = controllerData.hudRayPicks[this.hand]; - var hudPoint2d = HMD.overlayFromWorldPoint(hudRayPickInfo.intersection); - if (rayPickInfo.type == RayPick.INTERSECTED_ENTITY) { + if (rayPickInfo.type === RayPick.INTERSECTED_ENTITY) { if (controllerData.triggerClicks[this.hand]) { var entityID = rayPickInfo.objectID; - var targetProps = Entities.getEntityProperties(entityID, ["dynamic", "shapeType", "position", - "rotation", "dimensions", "density", - "userData", "locked", "type"]); + var targetProps = Entities.getEntityProperties(entityID, [ + "dynamic", "shapeType", "position", + "rotation", "dimensions", "density", + "userData", "locked", "type" + ]); + if (entityIsDistanceGrabbable(targetProps)) { if (!this.distanceRotating) { this.grabbedThingID = entityID; this.grabbedDistance = rayPickInfo.distance; } - var otherModuleName = - this.hand == RIGHT_HAND ? "LeftFarActionGrabEntity" : "RightFarActionGrabEntity"; - var otherFarGrabModule = getEnabledModuleByName(otherModuleName); - if (otherFarGrabModule.grabbedThingID == this.grabbedThingID && otherFarGrabModule.distanceHolding) { + + if (otherFarGrabModule.grabbedThingID === this.grabbedThingID && otherFarGrabModule.distanceHolding) { this.distanceRotate(otherFarGrabModule); } else { this.distanceHolding = true; @@ -519,14 +524,7 @@ Script.include("/~/system/libraries/controllers.js"); }, 500); } } else if (this.distanceRotating) { - var otherModuleName = - this.hand == RIGHT_HAND ? "LeftFarActionGrabEntity" : "RightFarActionGrabEntity"; - var otherFarGrabModule = getEnabledModuleByName(otherModuleName); this.distanceRotate(otherFarGrabModule); - } else if (Reticle.pointingAtSystemOverlay || Overlays.getOverlayAtPoint(hudPoint2d || Reticle.position)) { - this.endNearGrabAction(); - this.laserPointerOff(); - return makeRunningValues(false, [], []); } } return makeRunningValues(true, [], []); @@ -540,7 +538,7 @@ Script.include("/~/system/libraries/controllers.js"); this.halfEnd = halfEnd; this.fullEnd = fullEnd; this.laserPointer = LaserPointers.createLaserPointer({ - joint: (this.hand == RIGHT_HAND) ? "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", + joint: (this.hand === RIGHT_HAND) ? "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, maxDistance: PICK_MAX_DISTANCE, posOffset: getGrabPointSphereOffset(this.handToController()), diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index 782fb5dbc2..fc7e0b526e 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -1,4 +1,4 @@ -"use strict" +"use strict"; // inEditMode.js // @@ -8,7 +8,10 @@ /* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, NULL_UUID, enableDispatcherModule, disableDispatcherModule, makeRunningValues, Messages, Quat, Vec3, getControllerWorldLocation, makeDispatcherModuleParameters, Overlays, ZERO_VEC, - AVATAR_SELF_ID, HMD, INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, Settings, getGrabPointSphereOffset + AVATAR_SELF_ID, HMD, INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, Settings, getGrabPointSphereOffset, + COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, + DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_ON_VALUE, TRIGGER_OFF_VALUE, getEnabledModuleByName, PICK_MAX_DISTANCE, + isInEditMode */ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); @@ -71,13 +74,17 @@ Script.include("/~/system/libraries/utils.js"); 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}]; + 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} + ]; function InEditMode(hand) { this.hand = hand; @@ -97,7 +104,7 @@ Script.include("/~/system/libraries/utils.js"); } return false; }; - + this.handToController = function() { return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; }; @@ -116,7 +123,7 @@ Script.include("/~/system/libraries/utils.js"); 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}); @@ -124,11 +131,11 @@ Script.include("/~/system/libraries/utils.js"); 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.pointingAtTablet = function(objectID) { if (objectID === HMD.tabletScreenID || objectID === HMD.tabletButtonID) { return true; @@ -157,16 +164,21 @@ Script.include("/~/system/libraries/utils.js"); } }; - this.isReady = function(controllerData) { - var overlays = controllerData.nearbyOverlayIDs[this.hand]; - var objectID = controllerData.rayPicks[this.hand].objectID; + this.exitModule = function() { + this.disableLasers(); + return makeRunningValues(false, [], []); + }; + this.disableLasers = function() { + LaserPointers.disableLaserPointer(this.laserPointer); + }; + + this.isReady = function(controllerData) { if (isInEditMode()) { this.triggerClicked = false; return makeRunningValues(true, [], []); } - - return makeRunningValues(false, [], []); + return this.exitModule(); }; this.run = function(controllerData) { @@ -175,44 +187,44 @@ Script.include("/~/system/libraries/utils.js"); var tabletReady = tabletStylusInput.isReady(controllerData); if (tabletReady.active) { - return makeRunningValues(false, [], []); + return this.exitModule(); } } - var overlayLaser = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightOverlayLaserInput" : "LeftOverlayLaserInput"); + var overlayLaser = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightOverlayLaserInput" : "LeftOverlayLaserInput"); if (overlayLaser) { var overlayLaserReady = overlayLaser.isReady(controllerData); if (overlayLaserReady.active && this.pointingAtTablet(overlayLaser.target)) { - return makeRunningValues(false, [], []); + return this.exitModule(); } } - var nearOverlay = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay"); + var nearOverlay = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay"); if (nearOverlay) { var nearOverlayReady = nearOverlay.isReady(controllerData); if (nearOverlayReady.active && nearOverlay.grabbedThingID === HMD.tabletID) { - return makeRunningValues(false, [], []); + return this.exitModule(); } } this.processControllerTriggers(controllerData); this.updateLaserPointer(controllerData); this.sendPickData(controllerData); - + return this.isReady(controllerData); }; - this.cleanup = function() { + 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_ENTITIES | RayPick.PICK_OVERLAYS, @@ -222,10 +234,9 @@ Script.include("/~/system/libraries/utils.js"); faceAvatar: true, defaultRenderStates: defaultRenderStates }); - - LaserPointers.setIgnoreOverlays(this.laserPointer, [HMD.tabletID, HMD.tabletButtonID, HMD.tabletScreenID]); - }; + LaserPointers.setIgnoreOverlays(this.laserPointer, [HMD.tabletID, HMD.tabletButtonID, HMD.tabletScreenID]); + } var leftHandInEditMode = new InEditMode(LEFT_HAND); var rightHandInEditMode = new InEditMode(RIGHT_HAND); diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js index 2cea81ce18..f0a5b9f07d 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -28,7 +28,7 @@ var GRAB_RADIUS = 0.35; this.previouslyUnhooked = {}; this.parameters = makeDispatcherModuleParameters( - 140, + 90, this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], [], 100); diff --git a/scripts/system/controllers/controllerModules/overlayLaserInput.js b/scripts/system/controllers/controllerModules/overlayLaserInput.js index 8a14fc49ae..ec2aa7750a 100644 --- a/scripts/system/controllers/controllerModules/overlayLaserInput.js +++ b/scripts/system/controllers/controllerModules/overlayLaserInput.js @@ -1,4 +1,4 @@ -"use strict" +"use strict"; // overlayLaserInput.js // @@ -8,16 +8,16 @@ /* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, NULL_UUID, enableDispatcherModule, disableDispatcherModule, makeRunningValues, Messages, Quat, Vec3, getControllerWorldLocation, makeDispatcherModuleParameters, Overlays, ZERO_VEC, - AVATAR_SELF_ID, HMD, INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, Settings, getGrabPointSphereOffset + AVATAR_SELF_ID, HMD, INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, Settings, getGrabPointSphereOffset, + COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, + DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_ON_VALUE, TRIGGER_OFF_VALUE, getEnabledModuleByName, PICK_MAX_DISTANCE */ - - Script.include("/~/system/controllers/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); (function() { - var halfPath = { + var halfPath = { type: "line3d", color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, visible: true, @@ -72,13 +72,17 @@ Script.include("/~/system/libraries/controllers.js"); parentID: AVATAR_SELF_ID }; - var renderStates = [{name: "half", path: halfPath, end: halfEnd}, - {name: "full", path: fullPath, end: fullEnd}, - {name: "hold", path: holdPath}]; + 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}]; + 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 @@ -200,7 +204,7 @@ Script.include("/~/system/libraries/controllers.js"); } // will return undefined if overlayID does not exist. - function calculateLaserTargetFromOverlay(laserTip, overlayID) { + function calculateLaserTargetFromOverlay(worldPos, overlayID) { var overlayPosition = Overlays.getProperty(overlayID, "position"); if (overlayPosition === undefined) { return null; @@ -212,12 +216,11 @@ Script.include("/~/system/libraries/controllers.js"); return null; } var normal = Vec3.multiplyQbyV(overlayRotation, {x: 0, y: 0, z: 1}); - var distance = Vec3.dot(Vec3.subtract(laserTip, overlayPosition), normal); - var position = Vec3.subtract(laserTip, Vec3.multiply(normal, distance)); + var distance = Vec3.dot(Vec3.subtract(worldPos, overlayPosition), normal); // calclulate normalized position var invRot = Quat.inverse(overlayRotation); - var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, overlayPosition)); + var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(worldPos, overlayPosition)); var dpi = Overlays.getProperty(overlayID, "dpi"); var dimensions; @@ -228,12 +231,12 @@ Script.include("/~/system/libraries/controllers.js"); if (resolution === undefined) { return null; } - resolution.z = 1; // Circumvent divide-by-zero. + resolution.z = 1;// Circumvent divide-by-zero. var scale = Overlays.getProperty(overlayID, "dimensions"); if (scale === undefined) { return null; } - scale.z = 0.01; // overlay dimensions are 2D, not 3D. + scale.z = 0.01;// overlay dimensions are 2D, not 3D. dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale); } else { dimensions = Overlays.getProperty(overlayID, "dimensions"); @@ -241,21 +244,23 @@ Script.include("/~/system/libraries/controllers.js"); return null; } if (!dimensions.z) { - dimensions.z = 0.01; // sometimes overlay dimensions are 2D, not 3D. + dimensions.z = 0.01;// sometimes overlay dimensions are 2D, not 3D. } } var invDimensions = { x: 1 / dimensions.x, y: 1 / dimensions.y, z: 1 / dimensions.z }; var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), DEFAULT_REGISTRATION_POINT); // 2D position on overlay plane in meters, relative to the bounding box upper-left hand corner. - var position2D = { x: normalizedPosition.x * dimensions.x, - y: (1 - normalizedPosition.y) * dimensions.y }; // flip y-axis + var position2D = { + x: normalizedPosition.x * dimensions.x, + y: (1 - normalizedPosition.y) * dimensions.y // flip y-axis + }; return { entityID: null, overlayID: overlayID, distance: distance, - position: position, + position: worldPos, position2D: position2D, normal: normal, normalizedPosition: normalizedPosition, @@ -400,8 +405,8 @@ Script.include("/~/system/libraries/controllers.js"); if (this.laserTarget) { var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || - distance2D(this.laserTarget.position2D, - this.pressEnterLaserTarget.position2D) > this.deadspotRadius) { + distance2D( this.laserTarget.position2D, + this.pressEnterLaserTarget.position2D) > this.deadspotRadius) { sendTouchMoveEventToLaserTarget(this.hand, this.laserTarget); this.deadspotExpired = true; } @@ -412,7 +417,7 @@ Script.include("/~/system/libraries/controllers.js"); this.releaseTouchEvent = function() { sendTouchEndEventToLaserTarget(this.hand, this.pressEnterLaserTarget); - } + }; this.updateLaserTargets = function(controllerData) { @@ -423,9 +428,9 @@ Script.include("/~/system/libraries/controllers.js"); this.shouldExit = function(controllerData) { var intersection = controllerData.rayPicks[this.hand]; - var offOverlay = (intersection.type !== RayPick.INTERSECTED_OVERLAY) + var offOverlay = (intersection.type !== RayPick.INTERSECTED_OVERLAY); return offOverlay; - } + }; this.exitModule = function() { this.releaseTouchEvent(); @@ -433,7 +438,7 @@ Script.include("/~/system/libraries/controllers.js"); this.reset(); this.updateLaserPointer(); LaserPointers.disableLaserPointer(this.laserPointer); - } + }; this.reset = function() { this.hover = false; @@ -515,7 +520,7 @@ Script.include("/~/system/libraries/controllers.js"); this.halfEnd = halfEnd; this.fullEnd = fullEnd; this.laserPointer = LaserPointers.createLaserPointer({ - joint: (this.hand == RIGHT_HAND) ? "_CONTROLLER_RIGHTHAND" : "_CONTROLLER_LEFTHAND", + joint: (this.hand === RIGHT_HAND) ? "_CONTROLLER_RIGHTHAND" : "_CONTROLLER_LEFTHAND", filter: RayPick.PICK_OVERLAYS, maxDistance: PICK_MAX_DISTANCE, posOffset: getGrabPointSphereOffset(this.handToController()), @@ -525,7 +530,7 @@ Script.include("/~/system/libraries/controllers.js"); }); LaserPointers.setIgnoreOverlays(this.laserPointer, [HMD.tabletID]); - }; + } var leftOverlayLaserInput = new OverlayLaserInput(LEFT_HAND); var rightOverlayLaserInput = new OverlayLaserInput(RIGHT_HAND); diff --git a/scripts/system/controllers/controllerModules/tabletStylusInput.js b/scripts/system/controllers/controllerModules/tabletStylusInput.js index bbe8f935ae..9720bc8022 100644 --- a/scripts/system/controllers/controllerModules/tabletStylusInput.js +++ b/scripts/system/controllers/controllerModules/tabletStylusInput.js @@ -171,12 +171,12 @@ Script.include("/~/system/libraries/controllers.js"); if (resolution === undefined) { return; } - resolution.z = 1; // Circumvent divide-by-zero. + resolution.z = 1; // Circumvent divide-by-zero. var scale = Overlays.getProperty(overlayID, "dimensions"); if (scale === undefined) { return; } - scale.z = 0.01; // overlay dimensions are 2D, not 3D. + scale.z = 0.01; // overlay dimensions are 2D, not 3D. dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale); } else { dimensions = Overlays.getProperty(overlayID, "dimensions"); @@ -184,15 +184,17 @@ Script.include("/~/system/libraries/controllers.js"); return; } if (!dimensions.z) { - dimensions.z = 0.01; // sometimes overlay dimensions are 2D, not 3D. + dimensions.z = 0.01; // sometimes overlay dimensions are 2D, not 3D. } } var invDimensions = { x: 1 / dimensions.x, y: 1 / dimensions.y, z: 1 / dimensions.z }; var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), DEFAULT_REGISTRATION_POINT); // 2D position on overlay plane in meters, relative to the bounding box upper-left hand corner. - var position2D = { x: normalizedPosition.x * dimensions.x, - y: (1 - normalizedPosition.y) * dimensions.y }; // flip y-axis + var position2D = { + x: normalizedPosition.x * dimensions.x, + y: (1 - normalizedPosition.y) * dimensions.y // flip y-axis + }; return { entityID: null, @@ -227,8 +229,10 @@ Script.include("/~/system/libraries/controllers.js"); var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), props.registrationPoint); // 2D position on entity plane in meters, relative to the bounding box upper-left hand corner. - var position2D = { x: normalizedPosition.x * props.dimensions.x, - y: (1 - normalizedPosition.y) * props.dimensions.y }; // flip y-axis + var position2D = { + x: normalizedPosition.x * props.dimensions.x, + y: (1 - normalizedPosition.y) * props.dimensions.y // flip y-axis + }; return { entityID: props.id, @@ -354,7 +358,7 @@ Script.include("/~/system/libraries/controllers.js"); // translate tip forward according to constant. var TIP_OFFSET = {x: 0, y: WEB_STYLUS_LENGTH - WEB_TOUCH_Y_OFFSET, z: 0}; this.stylusTip.position = Vec3.sum(this.stylusTip.position, - Vec3.multiplyQbyV(this.stylusTip.orientation, TIP_OFFSET)); + Vec3.multiplyQbyV(this.stylusTip.orientation, TIP_OFFSET)); } // compute tip velocity from hand controller motion, it is more accurate than computing it from previous positions. @@ -363,9 +367,8 @@ Script.include("/~/system/libraries/controllers.js"); var worldControllerPos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation)); var worldControllerLinearVel = Vec3.multiplyQbyV(MyAvatar.orientation, pose.velocity); var worldControllerAngularVel = Vec3.multiplyQbyV(MyAvatar.orientation, pose.angularVelocity); - var tipVelocity = Vec3.sum(worldControllerLinearVel, - Vec3.cross(worldControllerAngularVel, - Vec3.subtract(this.stylusTip.position, worldControllerPos))); + var tipVelocity = Vec3.sum(worldControllerLinearVel, Vec3.cross(worldControllerAngularVel, + Vec3.subtract(this.stylusTip.position, worldControllerPos))); this.stylusTip.velocity = tipVelocity; } else { this.stylusTip.velocity = {x: 0, y: 0, z: 0}; @@ -381,10 +384,11 @@ Script.include("/~/system/libraries/controllers.js"); name: "stylus", url: Script.resourcesPath() + "meshes/tablet-stylus-fat.fbx", loadPriority: 10.0, - localPosition: Vec3.sum({ x: 0.0, - y: WEB_TOUCH_Y_OFFSET, - z: 0.0 }, - getGrabPointSphereOffset(this.handToController())), + localPosition: Vec3.sum({ + x: 0.0, + y: WEB_TOUCH_Y_OFFSET, + z: 0.0 + }, getGrabPointSphereOffset(this.handToController())), localRotation: Quat.fromVec3Degrees({ x: -90, y: 0, z: 0 }), dimensions: { x: 0.01, y: 0.01, z: WEB_STYLUS_LENGTH }, solid: true, @@ -393,8 +397,8 @@ Script.include("/~/system/libraries/controllers.js"); drawInFront: false, parentID: AVATAR_SELF_ID, parentJointIndex: MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? - "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : - "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND") + "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : + "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND") }; this.stylus = Overlays.addOverlay("model", stylusProperties); }; @@ -524,8 +528,8 @@ Script.include("/~/system/libraries/controllers.js"); } this.isNearStylusTarget = isNearStylusTarget(stylusTargets, EDGE_BORDER + hysteresisOffset, - TABLET_MIN_TOUCH_DISTANCE - hysteresisOffset, - WEB_DISPLAY_STYLUS_DISTANCE + hysteresisOffset); + TABLET_MIN_TOUCH_DISTANCE - hysteresisOffset, + WEB_DISPLAY_STYLUS_DISTANCE + hysteresisOffset); if (this.isNearStylusTarget) { if (!this.useFingerInsteadOfStylus) { @@ -630,7 +634,7 @@ Script.include("/~/system/libraries/controllers.js"); var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || distance2D(this.stylusTarget.position2D, - this.touchingEnterStylusTarget.position2D) > this.deadspotRadius) { + this.touchingEnterStylusTarget.position2D) > this.deadspotRadius) { sendTouchMoveEventToStylusTarget(this.hand, this.stylusTarget); this.deadspotExpired = true; } diff --git a/scripts/system/controllers/controllerModules/webEntityLaserInput.js b/scripts/system/controllers/controllerModules/webEntityLaserInput.js index e88e75684c..747e1bae44 100644 --- a/scripts/system/controllers/controllerModules/webEntityLaserInput.js +++ b/scripts/system/controllers/controllerModules/webEntityLaserInput.js @@ -5,14 +5,14 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/*jslint bitwise: true */ +/* jslint bitwise: true */ /* global Script, Controller, LaserPointers, RayPick, RIGHT_HAND, LEFT_HAND, Mat4, MyAvatar, Vec3, Camera, Quat, getGrabPointSphereOffset, getEnabledModuleByName, makeRunningValues, Entities, NULL_UUID, enableDispatcherModule, disableDispatcherModule, entityIsDistanceGrabbable, 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, + AVATAR_SELF_ID, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC */ @@ -20,7 +20,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); (function() { - var halfPath = { + var halfPath = { type: "line3d", color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, visible: true, @@ -75,13 +75,17 @@ Script.include("/~/system/libraries/controllers.js"); parentID: AVATAR_SELF_ID }; - var renderStates = [{name: "half", path: halfPath, end: halfEnd}, - {name: "full", path: fullPath, end: fullEnd}, - {name: "hold", path: holdPath}]; + 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}]; + 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 @@ -143,7 +147,7 @@ Script.include("/~/system/libraries/controllers.js"); } - function sendTouchStartEventToLaserTarget(hand, laserTarget) { + function sendTouchStartEventToLaserTarget(hand, laserTarget) { var pointerEvent = { type: "Press", id: hand + 1, // 0 is reserved for hardware mouse @@ -208,16 +212,18 @@ Script.include("/~/system/libraries/controllers.js"); Vec3.multiplyQbyV(props.rotation, {x: 0, y: 1, z: 0}); var distance = Vec3.dot(Vec3.subtract(intersection, props.position), normal); var position = Vec3.subtract(intersection, Vec3.multiply(normal, distance)); - + // generate normalized coordinates var invRot = Quat.inverse(props.rotation); var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, props.position)); var invDimensions = { x: 1 / props.dimensions.x, y: 1 / props.dimensions.y, z: 1 / props.dimensions.z }; var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), props.registrationPoint); - + // 2D position on entity plane in meters, relative to the bounding box upper-left hand corner. - var position2D = { x: normalizedPosition.x * props.dimensions.x, - y: (1 - normalizedPosition.y) * props.dimensions.y }; // flip y-axis + var position2D = { + x: normalizedPosition.x * props.dimensions.x, + y: (1 - normalizedPosition.y) * props.dimensions.y // flip y-axis + }; return { entityID: props.id, @@ -258,7 +264,7 @@ Script.include("/~/system/libraries/controllers.js"); this.getOtherModule = function() { return (this.hand === RIGHT_HAND) ? leftWebEntityLaserInput : rightWebEntityLaserInput; }; - + this.parameters = makeDispatcherModuleParameters( 550, this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], @@ -288,7 +294,7 @@ Script.include("/~/system/libraries/controllers.js"); }; this.processControllerTriggers = function(controllerData) { - if (controllerData.triggerClicks[this.hand]) { + if (controllerData.triggerClicks[this.hand]) { this.mode = "full"; this.laserPressingTarget = true; this.hover = false; @@ -316,7 +322,7 @@ Script.include("/~/system/libraries/controllers.js"); this.laserPressEnter = function () { sendTouchStartEventToLaserTarget(this.hand, this.laserTarget); Controller.triggerHapticPulse(HAPTIC_STYLUS_STRENGTH, HAPTIC_STYLUS_DURATION, this.hand); - + this.touchingEnterTimer = 0; this.pressEnterLaserTarget = this.laserTarget; this.deadspotExpired = false; @@ -340,12 +346,12 @@ Script.include("/~/system/libraries/controllers.js"); this.laserPressing = function (controllerData, dt) { this.touchingEnterTimer += dt; - + if (this.laserTarget) { var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || distance2D(this.laserTarget.position2D, - this.pressEnterLaserTarget.position2D) > this.deadspotRadius) { + this.pressEnterLaserTarget.position2D) > this.deadspotRadius) { sendTouchMoveEventToLaserTarget(this.hand, this.laserTarget); this.deadspotExpired = true; } @@ -353,19 +359,19 @@ Script.include("/~/system/libraries/controllers.js"); this.laserPressingTarget = false; } }; - + this.releaseTouchEvent = function() { if (this.pressEnterLaserTarget === null) { return; } sendTouchEndEventToLaserTarget(this.hand, this.pressEnterLaserTarget); - } + }; this.updateLaserPointer = function(controllerData) { var RADIUS = 0.005; var dim = { x: RADIUS, y: RADIUS, z: RADIUS }; - + if (this.mode === "full") { fullEnd.dimensions = dim; LaserPointers.editRenderState(this.laserPointer, this.mode, {path: fullPath, end: fullEnd}); @@ -373,7 +379,7 @@ Script.include("/~/system/libraries/controllers.js"); halfEnd.dimensions = dim; LaserPointers.editRenderState(this.laserPointer, this.mode, {path: halfPath, end: halfEnd}); } - + LaserPointers.enableLaserPointer(this.laserPointer); LaserPointers.setRenderState(this.laserPointer, this.mode); }; @@ -386,7 +392,7 @@ Script.include("/~/system/libraries/controllers.js"); if ((intersection.type === RayPick.INTERSECTED_ENTITY && entityType === "Web")) { return true; } - return false + return false; }; this.exitModule = function() { @@ -396,7 +402,7 @@ Script.include("/~/system/libraries/controllers.js"); this.updateLaserPointer(); LaserPointers.disableLaserPointer(this.laserPointer); }; - + this.reset = function() { this.pressEnterLaserTarget = null; this.laserTarget = null; @@ -412,7 +418,7 @@ Script.include("/~/system/libraries/controllers.js"); if (this.isPointingAtWebEntity(controllerData) && !otherModule.active) { return makeRunningValues(true, [], []); } - + return makeRunningValues(false, [], []); }; @@ -433,7 +439,7 @@ Script.include("/~/system/libraries/controllers.js"); this.laserPressExit(); } this.previousLaserClickedTarget = this.laserPressingTarget; - + if (this.laserPressingTarget) { this.laserPressing(controllerData, deltaTime); } @@ -447,7 +453,7 @@ Script.include("/~/system/libraries/controllers.js"); this.cleanup = function() { LaserPointers.disableLaserPointer(this.laserPointer); LaserPointers.removeLaserPointer(this.laserPointer); - } + }; this.laserPointer = LaserPointers.createLaserPointer({ joint: (this.hand === RIGHT_HAND) ? "_CONTROLLER_RIGHTHAND" : "_CONTROLLER_LEFTHAND", @@ -458,13 +464,13 @@ Script.include("/~/system/libraries/controllers.js"); faceAvatar: true, defaultRenderStates: defaultRenderStates }); - }; + } var leftWebEntityLaserInput = new WebEntityLaserInput(LEFT_HAND); var rightWebEntityLaserInput = new WebEntityLaserInput(RIGHT_HAND); - enableDispatcherModule("LeftWebEntityLaserInput", leftWebEntityLaserInput); + enableDispatcherModule("LeftWebEntityLaserInput", leftWebEntityLaserInput); enableDispatcherModule("RightWebEntityLaserInput", rightWebEntityLaserInput); this.cleanup = function() { @@ -474,5 +480,5 @@ Script.include("/~/system/libraries/controllers.js"); disableDispatcherModule("RightWebEntityLaserInput"); }; Script.scriptEnding.connect(this.cleanup); - + }()); From 29480619047eba2a7d1e1bd4ab00b808723a077d Mon Sep 17 00:00:00 2001 From: druiz17 Date: Thu, 31 Aug 2017 22:30:30 -0700 Subject: [PATCH 40/59] code cleanup --- interface/src/Application.cpp | 1 - .../controllerModules/equipEntity.js | 2 +- .../controllerModules/nearParentGrabEntity.js | 21 ++++++++++--------- .../controllerModules/nearTrigger.js | 2 +- .../controllerModules/overlayLaserInput.js | 3 ++- scripts/system/controllers/grab.js | 8 ------- .../controllers/handControllerPointer.js | 1 - 7 files changed, 15 insertions(+), 23 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 533b5a4bee..027b7efe37 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1891,7 +1891,6 @@ QString Application::getUserAgent() { void Application::toggleTabletUI(bool shouldOpen) const { auto tabletScriptingInterface = DependencyManager::get(); auto hmd = DependencyManager::get(); - qDebug() << "Application::toggleTabletUI" << shouldOpen << hmd->getShouldShowTablet(); if (!(shouldOpen && hmd->getShouldShowTablet())) { auto HMD = DependencyManager::get(); HMD->toggleShouldShowTablet(); diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index 5ad7fc9e16..5b73204c8e 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -10,7 +10,7 @@ getControllerJointIndex, NULL_UUID, enableDispatcherModule, disableDispatcherModule, Messages, makeDispatcherModuleParameters, makeRunningValues, Settings, entityHasActions, Vec3, Overlays, flatten, Xform, getControllerWorldLocation, ensureDynamic, entityIsCloneable, - cloneEntity + cloneEntity, DISPATCHER_PROPERTIES */ Script.include("/~/system/libraries/Xform.js"); diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index f05eab348d..00fba45ae6 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -10,7 +10,7 @@ getControllerJointIndex, NULL_UUID, enableDispatcherModule, disableDispatcherModule, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS, - findGroupParent, Vec3 + findGroupParent, Vec3, cloneEntity, entityIsCloneable */ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); @@ -46,19 +46,20 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); } var handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); - if (props.parentJointIndex == handJointIndex) { + if (props.parentJointIndex === handJointIndex) { return true; } var controllerJointIndex = this.controllerJointIndex; - if (props.parentJointIndex == controllerJointIndex) { + if (props.parentJointIndex === controllerJointIndex) { return true; } var controllerCRJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? - "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : - "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); - if (props.parentJointIndex == controllerCRJointIndex) { + "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : + "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); + + if (props.parentJointIndex === controllerCRJointIndex) { return true; } @@ -172,7 +173,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.run = function (controllerData, deltaTime) { if (this.grabbing) { - if (controllerData.triggerClicks[this.hand] == 0) { + if (controllerData.triggerClicks[this.hand] === 0) { this.endNearParentingGrabEntity(); return makeRunningValues(false, [], []); } @@ -185,7 +186,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); if (!readiness.active) { return readiness; } - if (controllerData.triggerClicks[this.hand] == 1) { + if (controllerData.triggerClicks[this.hand] === 1) { // switch to grab var targetProps = this.getTargetProps(controllerData); var targetCloneable = entityIsCloneable(targetProps); @@ -196,9 +197,9 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); var cloneProps = Entities.getEntityProperties(cloneID); this.grabbing = true; - this.targetEntityID = cloneID + this.targetEntityID = cloneID; this.startNearParentingGrabEntity(controllerData, cloneProps); - + } else if (targetProps) { this.grabbing = true; this.startNearParentingGrabEntity(controllerData, targetProps); diff --git a/scripts/system/controllers/controllerModules/nearTrigger.js b/scripts/system/controllers/controllerModules/nearTrigger.js index fd24aeefa2..239778040c 100644 --- a/scripts/system/controllers/controllerModules/nearTrigger.js +++ b/scripts/system/controllers/controllerModules/nearTrigger.js @@ -84,7 +84,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); }; this.run = function (controllerData) { - if (controllerData.triggerClicks[this.hand] == 0) { + if (controllerData.triggerClicks[this.hand] === 0) { this.endNearTrigger(controllerData); return makeRunningValues(false, [], []); } diff --git a/scripts/system/controllers/controllerModules/overlayLaserInput.js b/scripts/system/controllers/controllerModules/overlayLaserInput.js index ec2aa7750a..86623dbc72 100644 --- a/scripts/system/controllers/controllerModules/overlayLaserInput.js +++ b/scripts/system/controllers/controllerModules/overlayLaserInput.js @@ -10,7 +10,8 @@ Messages, Quat, Vec3, getControllerWorldLocation, makeDispatcherModuleParameters, Overlays, ZERO_VEC, AVATAR_SELF_ID, HMD, INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, Settings, getGrabPointSphereOffset, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, - DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_ON_VALUE, TRIGGER_OFF_VALUE, getEnabledModuleByName, PICK_MAX_DISTANCE + DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_ON_VALUE, TRIGGER_OFF_VALUE, getEnabledModuleByName, PICK_MAX_DISTANCE, + DISPATCHER_PROPERTIES */ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); diff --git a/scripts/system/controllers/grab.js b/scripts/system/controllers/grab.js index 50f9a5c5f0..2844940d2b 100644 --- a/scripts/system/controllers/grab.js +++ b/scripts/system/controllers/grab.js @@ -21,8 +21,6 @@ (function() { // BEGIN LOCAL_SCOPE Script.include("/~/system/libraries/utils.js"); -Script.include("/~/system/controllers/controllerDispatcherUtils.js"); -Script.include("/~/system/libraries/controllers.js"); var MAX_SOLID_ANGLE = 0.01; // objects that appear smaller than this can't be grabbed var DELAY_FOR_30HZ = 33; // milliseconds @@ -253,12 +251,6 @@ function Grabber() { z: 0 }; - this.paramters = makeDispatcherModuleParameters( - 300, - "mouse", - [], - 100); - this.targetPosition = null; this.targetRotation = null; diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index fbbb708a92..538fe0b1e4 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -639,7 +639,6 @@ function update() { // If there's a HUD element at the (newly moved) reticle, just make it visible and bail. if (isPointingAtOverlay(hudPoint2d) && isPointerEnabled) { - //print("--------> pointing at HUD <--------"); if (HMD.active) { Reticle.depth = hudReticleDistance(); From 3b357ad61ed22cd9400ddbe6abc925e1bae7faeb Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 1 Sep 2017 17:07:26 -0700 Subject: [PATCH 41/59] finshed handControllerGrab refactoring --- .../controllers/controllerDispatcher.js | 86 ++++++--------- .../controllers/controllerDispatcherUtils.js | 101 +++++++++--------- .../controllerModules/disableOtherModule.js | 85 +++++++++++++++ .../controllerModules/equipEntity.js | 11 +- .../controllerModules/farActionGrabEntity.js | 13 +++ .../controllerModules/inEditMode.js | 2 +- .../controllerModules/nearActionGrabEntity.js | 21 ++-- .../controllerModules/nearParentGrabEntity.js | 32 +++++- .../nearParentGrabOverlay.js | 62 +++++++---- .../controllerModules/nearTrigger.js | 2 +- .../system/controllers/controllerScripts.js | 1 + scripts/system/controllers/teleport.js | 7 +- scripts/system/libraries/cloneEntityUtils.js | 29 +++-- 13 files changed, 302 insertions(+), 150 deletions(-) create mode 100644 scripts/system/controllers/controllerModules/disableOtherModule.js diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 9469b59a95..3826222666 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -5,11 +5,12 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/*jslint bitwise: true */ +/* jslint bitwise: true */ /* global Script, Entities, Overlays, Controller, Vec3, Quat, getControllerWorldLocation, RayPick, - controllerDispatcherPlugins, controllerDispatcherPluginsNeedSort, entityIsGrabbable, - LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS, DEFAULT_SEARCH_SPHERE_DISTANCE, DISPATCHER_PROPERTIES + controllerDispatcherPlugins:true, controllerDispatcherPluginsNeedSort:true, entityIsGrabbable:true, + LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS, DEFAULT_SEARCH_SPHERE_DISTANCE, DISPATCHER_PROPERTIES, + getGrabPointSphereOffset */ controllerDispatcherPlugins = {}; @@ -21,18 +22,16 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); (function() { var _this = this; - - var NEAR_MIN_RADIUS = 0.1; var NEAR_MAX_RADIUS = 1.0; var TARGET_UPDATE_HZ = 60; // 50hz good enough, but we're using update var BASIC_TIMER_INTERVAL_MS = 1000 / TARGET_UPDATE_HZ; - var lastInterval = Date.now(); - var intervalCount = 0; - var totalDelta = 0; - var totalVariance = 0; - var highVarianceCount = 0; - var veryhighVarianceCount = 0; + this.lastInterval = Date.now(); + this.intervalCount = 0; + this.totalDelta = 0; + this.totalVariance = 0; + this.highVarianceCount = 0; + this.veryhighVarianceCount = 0; this.tabletID = null; this.blacklist = []; @@ -63,7 +62,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); this.unmarkSlotsForPluginName = function (runningPluginName) { // this is used to free activity-slots when a plugin is deactivated while it's running. for (var activitySlot in _this.activitySlots) { - if (activitySlot.hasOwnProperty(activitySlot) && _this.activitySlots[activitySlot] == runningPluginName) { + if (activitySlot.hasOwnProperty(activitySlot) && _this.activitySlots[activitySlot] === runningPluginName) { _this.activitySlots[activitySlot] = false; } } @@ -106,23 +105,23 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); }; this.updateTimings = function () { - intervalCount++; + _this.intervalCount++; var thisInterval = Date.now(); - var deltaTimeMsec = thisInterval - lastInterval; + var deltaTimeMsec = thisInterval - _this.lastInterval; var deltaTime = deltaTimeMsec / 1000; - lastInterval = thisInterval; + _this.lastInterval = thisInterval; - totalDelta += deltaTimeMsec; + _this.totalDelta += deltaTimeMsec; var variance = Math.abs(deltaTimeMsec - BASIC_TIMER_INTERVAL_MS); - totalVariance += variance; + _this.totalVariance += variance; if (variance > 1) { - highVarianceCount++; + _this.highVarianceCount++; } if (variance > 5) { - veryhighVarianceCount++; + _this.veryhighVarianceCount++; } return deltaTime; @@ -132,13 +131,12 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); if (HMD.tabletID !== _this.tabletID) { RayPick.setIgnoreOverlays(_this.leftControllerRayPick, [HMD.tabletID]); RayPick.setIgnoreOverlays(_this.rightControllerRayPick, [HMD.tabletID]); - tabletIgnored = true } - } + }; this.update = function () { - var deltaTime = this.updateTimings(); - this.setIgnoreTablet() + var deltaTime = this.updateTimings(); + this.setIgnoreTablet(); if (controllerDispatcherPluginsNeedSort) { this.orderedPluginNames = []; @@ -167,8 +165,10 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); controllerDispatcherPluginsNeedSort = false; } - var controllerLocations = [_this.dataGatherers.leftControllerLocation(), - _this.dataGatherers.rightControllerLocation()]; + var controllerLocations = [ + _this.dataGatherers.leftControllerLocation(), + _this.dataGatherers.rightControllerLocation() + ]; // find 3d overlays near each hand var nearbyOverlayIDs = []; @@ -221,7 +221,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); length: 1000 }; - if (rayPicks[h].type == RayPick.INTERSECTED_ENTITY) { + if (rayPicks[h].type === RayPick.INTERSECTED_ENTITY) { // XXX check to make sure this one isn't already in nearbyEntityProperties? if (rayPicks[h].distance < NEAR_GRAB_PICK_RADIUS) { var nearEntityID = rayPicks[h].objectID; @@ -259,7 +259,6 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); var candidatePlugin = controllerDispatcherPlugins[orderedPluginName]; if (_this.slotsAreAvailableForPlugin(candidatePlugin)) { - //print(orderedPluginName); var readiness = candidatePlugin.isReady(controllerData, deltaTime); if (readiness.active) { // this plugin will start. add it to the list of running plugins and mark the @@ -297,7 +296,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); this.setBlacklist = function() { RayPick.setIgnoreEntities(_this.leftControllerRayPick, this.blacklist); RayPick.setIgnoreEntities(_this.rightControllerRayPick, this.blacklist); - + }; var MAPPING_NAME = "com.highfidelity.controllerDispatcher"; @@ -314,19 +313,6 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); Controller.enableMapping(MAPPING_NAME); - - this.mouseRayPick = RayPick.createRayPick({ - joint: "Mouse", - filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, - enabled: true - }); - - this.mouseHudRayPick = RayPick.createRayPick({ - joint: "Mouse", - filter: RayPick.PICK_HUD, - enabled: true - }); - this.leftControllerRayPick = RayPick.createRayPick({ joint: "_CONTROLLER_LEFTHAND", filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, @@ -357,7 +343,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); }); this.handleHandMessage = function(channel, message, sender) { - var data + var data; if (sender === MyAvatar.sessionUUID) { try { if (channel === 'Hifi-Hand-RayPick-Blacklist') { @@ -365,33 +351,29 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); var action = data.action; var id = data.id; var index = this.blacklis.indexOf(id); - + if (action === 'add' && index === -1) { this.blacklist.push(id); - //this.setBlacklist(); + this.setBlacklist(); } - + if (action === 'remove') { if (index > -1) { - blacklist.splice(index, 1); - //this.setBlacklist(); + this.blacklist.splice(index, 1); + this.setBlacklist(); } } } - + } catch (e) { print("WARNING: handControllerGrab.js -- error parsing Hifi-Hand-RayPick-Blacklist message: " + message); } } }; - - - this.cleanup = function () { Script.update.disconnect(_this.update); Controller.disableMapping(MAPPING_NAME); - // RayPick.removeRayPick(_this.mouseRayPick); RayPick.removeRayPick(_this.leftControllerRayPick); RayPick.removeRayPick(_this.rightControllerRayPick); RayPick.removeRayPick(_this.rightControllerHudRayPick); diff --git a/scripts/system/controllers/controllerDispatcherUtils.js b/scripts/system/controllers/controllerDispatcherUtils.js index 808623fc79..773f79754d 100644 --- a/scripts/system/controllers/controllerDispatcherUtils.js +++ b/scripts/system/controllers/controllerDispatcherUtils.js @@ -6,37 +6,37 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Camera, HMD, MyAvatar, controllerDispatcherPlugins, Quat, Vec3, Overlays, - MSECS_PER_SEC, LEFT_HAND, RIGHT_HAND, NULL_UUID, AVATAR_SELF_ID, FORBIDDEN_GRAB_TYPES, - HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, ZERO_VEC, ONE_VEC, DEFAULT_REGISTRATION_POINT, INCHES_TO_METERS, - TRIGGER_OFF_VALUE, - TRIGGER_ON_VALUE, - PICK_MAX_DISTANCE, - DEFAULT_SEARCH_SPHERE_DISTANCE, - NEAR_GRAB_PICK_RADIUS, - COLORS_GRAB_SEARCHING_HALF_SQUEEZE, - COLORS_GRAB_SEARCHING_FULL_SQUEEZE, - COLORS_GRAB_DISTANCE_HOLD, - NEAR_GRAB_RADIUS, - DISPATCHER_PROPERTIES, +/* global Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, + MSECS_PER_SEC:true , LEFT_HAND:true, RIGHT_HAND:true, NULL_UUID:true, AVATAR_SELF_ID:true, FORBIDDEN_GRAB_TYPES:true, + HAPTIC_PULSE_STRENGTH:true, HAPTIC_PULSE_DURATION:true, ZERO_VEC:true, ONE_VEC:true, DEFAULT_REGISTRATION_POINT:true, INCHES_TO_METERS:true, + TRIGGER_OFF_VALUE:true, + TRIGGER_ON_VALUE:true, + PICK_MAX_DISTANCE:true, + DEFAULT_SEARCH_SPHERE_DISTANCE:true, + NEAR_GRAB_PICK_RADIUS:true, + COLORS_GRAB_SEARCHING_HALF_SQUEEZE:true, + COLORS_GRAB_SEARCHING_FULL_SQUEEZE:true, + COLORS_GRAB_DISTANCE_HOLD:true, + NEAR_GRAB_RADIUS:true, + DISPATCHER_PROPERTIES:true, Entities, - makeDispatcherModuleParameters, - makeRunningValues, - enableDispatcherModule, - disableDispatcherModule, - getEnabledModuleByName, - getGrabbableData, - entityIsGrabbable, - entityIsDistanceGrabbable, - getControllerJointIndex, - propsArePhysical, - controllerDispatcherPluginsNeedSort, - projectOntoXYPlane, - projectOntoEntityXYPlane, - projectOntoOverlayXYPlane, - entityHasActions, - ensureDynamic, - findGroupParent + makeDispatcherModuleParameters:true, + makeRunningValues:true, + enableDispatcherModule:true, + disableDispatcherModule:true, + getEnabledModuleByName:true, + getGrabbableData:true, + entityIsGrabbable:true, + entityIsDistanceGrabbable:true, + getControllerJointIndex:true, + propsArePhysical:true, + controllerDispatcherPluginsNeedSort:true, + projectOntoXYPlane:true, + projectOntoEntityXYPlane:true, + projectOntoOverlayXYPlane:true, + entityHasActions:true, + ensureDynamic:true, + findGroupParent:true */ MSECS_PER_SEC = 1000.0; @@ -69,11 +69,8 @@ COLORS_GRAB_SEARCHING_HALF_SQUEEZE = { red: 10, green: 10, blue: 255 }; COLORS_GRAB_SEARCHING_FULL_SQUEEZE = { red: 250, green: 10, blue: 10 }; COLORS_GRAB_DISTANCE_HOLD = { red: 238, green: 75, blue: 214 }; - NEAR_GRAB_RADIUS = 0.1; - - DISPATCHER_PROPERTIES = [ "position", "registrationPoint", @@ -92,9 +89,6 @@ DISPATCHER_PROPERTIES = [ "userData" ]; - - - // priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step // activitySlots -- indicates which "slots" must not yet be in use for this module to start // requiredDataForReady -- which "situation" parts this module looks at to decide if it will start @@ -211,12 +205,12 @@ getControllerJointIndex = function (hand) { var controllerJointIndex = -1; if (Camera.mode === "first person") { controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? - "_CONTROLLER_RIGHTHAND" : - "_CONTROLLER_LEFTHAND"); + "_CONTROLLER_RIGHTHAND" : + "_CONTROLLER_LEFTHAND"); } else if (Camera.mode === "third person") { controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? - "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : - "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); + "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : + "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); } return controllerJointIndex; @@ -229,19 +223,24 @@ propsArePhysical = function (props) { if (!props.dynamic) { return false; } - var isPhysical = (props.shapeType && props.shapeType != 'none'); + var isPhysical = (props.shapeType && props.shapeType !== 'none'); return isPhysical; }; projectOntoXYPlane = function (worldPos, position, rotation, dimensions, registrationPoint) { var invRot = Quat.inverse(rotation); var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(worldPos, position)); - var invDimensions = { x: 1 / dimensions.x, - y: 1 / dimensions.y, - z: 1 / dimensions.z }; + var invDimensions = { + x: 1 / dimensions.x, + y: 1 / dimensions.y, + z: 1 / dimensions.z + }; + var normalizedPos = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), registrationPoint); - return { x: normalizedPos.x * dimensions.x, - y: (1 - normalizedPos.y) * dimensions.y }; // flip y-axis + return { + x: normalizedPos.x * dimensions.x, + y: (1 - normalizedPos.y) * dimensions.y // flip y-axis + }; }; projectOntoEntityXYPlane = function (entityID, worldPos, props) { @@ -257,14 +256,14 @@ projectOntoOverlayXYPlane = function projectOntoOverlayXYPlane(overlayID, worldP if (dpi) { // Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property is used as a scale. var resolution = Overlays.getProperty(overlayID, "resolution"); - resolution.z = 1; // Circumvent divide-by-zero. + resolution.z = 1; // Circumvent divide-by-zero. var scale = Overlays.getProperty(overlayID, "dimensions"); - scale.z = 0.01; // overlay dimensions are 2D, not 3D. + scale.z = 0.01; // overlay dimensions are 2D, not 3D. dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale); } else { dimensions = Overlays.getProperty(overlayID, "dimensions"); if (dimensions.z) { - dimensions.z = 0.01; // overlay dimensions are 2D, not 3D. + dimensions.z = 0.01; // overlay dimensions are 2D, not 3D. } } @@ -279,7 +278,7 @@ ensureDynamic = function (entityID) { // if we distance hold something and keep it very still before releasing it, it ends up // non-dynamic in bullet. If it's too still, give it a little bounce so it will fall. var props = Entities.getEntityProperties(entityID, ["velocity", "dynamic", "parentID"]); - if (props.dynamic && props.parentID == NULL_UUID) { + if (props.dynamic && props.parentID === NULL_UUID) { var velocity = props.velocity; if (Vec3.length(velocity) < 0.05) { // see EntityMotionState.cpp DYNAMIC_LINEAR_VELOCITY_THRESHOLD velocity = { x: 0.0, y: 0.2, z: 0.0 }; @@ -289,7 +288,7 @@ ensureDynamic = function (entityID) { }; findGroupParent = function (controllerData, targetProps) { - while (targetProps.parentID && targetProps.parentID != NULL_UUID) { + while (targetProps.parentID && targetProps.parentID !== NULL_UUID) { // XXX use controllerData.nearbyEntityPropertiesByID ? var parentProps = Entities.getEntityProperties(targetProps.parentID, DISPATCHER_PROPERTIES); if (!parentProps) { diff --git a/scripts/system/controllers/controllerModules/disableOtherModule.js b/scripts/system/controllers/controllerModules/disableOtherModule.js new file mode 100644 index 0000000000..65732dd08d --- /dev/null +++ b/scripts/system/controllers/controllerModules/disableOtherModule.js @@ -0,0 +1,85 @@ +"use strict"; + +// nearTrigger.js +// +// 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, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, + enableDispatcherModule, disableDispatcherModule, getGrabbableData, Vec3, + TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, makeRunningValues, NEAR_GRAB_RADIUS, + getEnabledModuleByName +*/ + +Script.include("/~/system/controllers/controllerDispatcherUtils.js"); + +(function() { + function DisableModules(hand) { + this.hand = hand; + this.disableModules = false; + this.parameters = makeDispatcherModuleParameters( + 90, + this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip", "rightHandTrigger"] : ["leftHand", "leftHandEquip", "leftHandTrigger"], + 100); + + this.isReady = function(controllerData) { + if (this.disableModules) { + return makeRunningValues(true, [], []); + } + return false; + }; + + this.run = function(controllerData) { + var teleportModuleName = this.hand === RIGHT_HAND ? "RightTeleporter" : "LeftTeleporter"; + var teleportModule = getEnabledModuleByName(teleportModuleName); + + if (teleportModule) { + var ready = teleportModule.isReady(controllerData); + if (ready) { + return makeRunningValues(false, [], []); + } + } + if (!this.disablemodules) { + return makeRunningValues(false, [], []); + } + return makeRunningValues(true, [], []); + }; + } + + var leftDisableModules = new DisableModules(LEFT_HAND); + var rightDisableModules = new DisableModules(RIGHT_HAND); + + enableDispatcherModule("LeftDisableModules", leftDisableModules); + enableDispatcherModule("RightDisableModules", rightDisableModules); + this.handleMessage = function(channel, message, sender) { + if (sender === MyAvatar.sessionUUID) { + if (channel === 'Hifi-Hand-Disabler') { + if (message === 'left') { + leftDisableModules.disableModules = true; + } + if (message === 'right') { + rightDisableModules.disableModules = true; + + } + if (message === 'both' || message === 'none') { + if (message === 'both') { + leftDisableModules.disableModules = true; + rightDisableModules.disableModules = true; + } else if (message === 'none') { + leftDisableModules.disableModules = false; + rightDisableModules.disableModules = false; + } + } + } + } + }; + + Messages.subscribe('Hifi-Hand-Disabler'); + this.cleanup = function() { + disableDispatcherModule("LeftDisableModules"); + disableDispatcherModule("RightDisableModules"); + }; + Messages.messageReceived.connect(this.handleMessage); + Script.scriptEnding.connect(this.cleanup); +}()); diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index 5b73204c8e..a9e62de4df 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -187,6 +187,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa wearable = props.userDataParsed.wearable ? props.userDataParsed.wearable : {}; } catch (err) { + // don't want to spam the logs } return wearable; } @@ -199,6 +200,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa equipHotspots = props.userDataParsed.equipHotspots ? props.userDataParsed.equipHotspots : []; } catch (err) { + // don't want to spam the logs } return equipHotspots; } @@ -255,7 +257,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.parameters = makeDispatcherModuleParameters( 300, - this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip"] : ["leftHand", "rightHandEquip"], [], 100); @@ -556,8 +558,8 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var controllerLocation = getControllerWorldLocation(this.handToController(), true); var worldHandPosition = controllerLocation.position; var candidateEntityProps = controllerData.nearbyEntityProperties[this.hand]; - - + + var potentialEquipHotspot = null; if (this.messageGrabEntity) { var hotspots = this.collectEquipHotspots(this.grabEntityProps); @@ -567,7 +569,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa } else { potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntityProps, controllerData); } - + if (!this.waitForTriggerRelease) { this.updateEquipHaptics(potentialEquipHotspot, worldHandPosition); } @@ -675,7 +677,6 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var handleMessage = function(channel, message, sender) { var data; - print(channel); if (sender === MyAvatar.sessionUUID) { if (channel === 'Hifi-Hand-Grab') { try { diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 2b748e60d6..7883222c73 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -527,6 +527,19 @@ Script.include("/~/system/libraries/controllers.js"); this.distanceRotate(otherFarGrabModule); } } + return this.exitIfDisabled(); + }; + + this.exitIfDisabled = function() { + var moduleName = this.hand === RIGHT_HAND ? "RightDisableModules" : "LeftDisableModules"; + var disableModule = getEnabledModuleByName(moduleName); + if (disableModule) { + if (disableModule.disableModules) { + this.laserPointerOff(); + this.endNearGrabAction(); + return makeRunningValues(false, [], []); + } + } return makeRunningValues(true, [], []); }; diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index fc7e0b526e..c6ac0c01bc 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -93,7 +93,7 @@ Script.include("/~/system/libraries/utils.js"); this.parameters = makeDispatcherModuleParameters( 160, - this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip", "rightHandTrigger"] : ["leftHand", "leftHandEquip", "leftHandTrigger"], 100); this.nearTablet = function(overlays) { diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index a2673f8bca..2b3a2405fb 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -9,11 +9,12 @@ getControllerJointIndex, getGrabbableData, NULL_UUID, enableDispatcherModule, disableDispatcherModule, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, entityIsGrabbable, Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation, makeDispatcherModuleParameters, makeRunningValues, - TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, findGroupParent + TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, findGroupParent, entityIsCloneable, propsAreCloneDynamic, cloneEntity */ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); +Script.include("/~/system/libraries/cloneEntityUtils.js"); (function() { @@ -172,7 +173,7 @@ Script.include("/~/system/libraries/controllers.js"); var targetProps = this.getTargetProps(controllerData); if (targetProps) { - if (!propsArePhysical(targetProps)) { + if (!propsArePhysical(targetProps) && !propsAreCloneDynamic) { return makeRunningValues(false, [], []); // let nearParentGrabEntity handle it } else { this.targetEntityID = targetProps.id; @@ -185,13 +186,12 @@ Script.include("/~/system/libraries/controllers.js"); this.run = function (controllerData) { if (this.actionID) { - if (controllerData.triggerClicks[this.hand] == 0) { + if (controllerData.triggerClicks[this.hand] === 0) { this.endNearGrabAction(); return makeRunningValues(false, [], []); } this.refreshNearGrabAction(controllerData); - var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.targetEntityID, "continueNearGrab", args); } else { @@ -204,9 +204,18 @@ Script.include("/~/system/libraries/controllers.js"); var targetProps = this.getTargetProps(controllerData); if (targetProps) { - if (controllerData.triggerClicks[this.hand] == 1) { + if (controllerData.triggerClicks[this.hand] === 1) { // switch to grabbing - this.startNearGrabAction(controllerData, targetProps); + var targetCloneable = entityIsCloneable(targetProps); + if (targetCloneable) { + var worldEntityProps = controllerData.nearbyEntityProperties[this.hand]; + var cloneID = cloneEntity(targetProps, worldEntityProps); + var cloneProps = Entities.getEntityProperties(cloneID); + this.targetEntityID = cloneID; + this.startNearGrabAction(controllerData, cloneProps); + } else { + this.startNearGrabAction(controllerData, targetProps); + } } } } diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index 00fba45ae6..4a59fd727f 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -10,7 +10,7 @@ getControllerJointIndex, NULL_UUID, enableDispatcherModule, disableDispatcherModule, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS, - findGroupParent, Vec3, cloneEntity, entityIsCloneable + findGroupParent, Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic */ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); @@ -40,6 +40,14 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); this.controllerJointIndex = getControllerJointIndex(this.hand); + this.getOtherModule = function() { + return (this.hand === RIGHT_HAND) ? leftNearParentingGrabEntity : rightNearParentingGrabEntity; + }; + + this.otherHandIsParent = function(props) { + return this.getOtherModule().thisHandIsParent(props); + }; + this.thisHandIsParent = function(props) { if (props.parentID !== MyAvatar.sessionUUID && props.parentID !== AVATAR_SELF_ID) { return false; @@ -67,7 +75,6 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); }; this.startNearParentingGrabEntity = function (controllerData, targetProps) { - Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); var handJointIndex; @@ -92,10 +99,18 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); // this should never happen, but if it does, don't set previous parent to be this hand. // this.previousParentID[targetProps.id] = NULL; // this.previousParentJointIndex[targetProps.id] = -1; + } else if (this.otherHandIsParent(targetProps)) { + // the other hand is parent. Steal the object and information + var otherModule = this.getOtherModule(); + this.previousParentID[targetProps.id] = otherModule.previousParentID[targetProps.id]; + this.previousParentJointIndex[targetProps.id] = otherModule.previousParentJointIndex[targetProps.id]; + otherModule.endNearParentingGrabEntity(); } else { this.previousParentID[targetProps.id] = targetProps.parentID; this.previousParentJointIndex[targetProps.id] = targetProps.parentJointIndex; } + + this.targetEntityID = targetProps.id; Entities.editEntity(targetProps.id, reparentProps); Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ @@ -103,10 +118,11 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); grabbedEntity: targetProps.id, joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" })); + this.grabbing = true; }; this.endNearParentingGrabEntity = function () { - if (this.previousParentID[this.targetEntityID] === NULL_UUID) { + if (this.previousParentID[this.targetEntityID] === NULL_UUID || this.previousParentID === undefined) { Entities.editEntity(this.targetEntityID, { parentID: this.previousParentID[this.targetEntityID], parentJointIndex: this.previousParentJointIndex[this.targetEntityID] @@ -123,7 +139,6 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.targetEntityID, "releaseGrab", args); - this.grabbing = false; this.targetEntityID = null; }; @@ -160,7 +175,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); var targetProps = this.getTargetProps(controllerData); if (targetProps) { - if (propsArePhysical(targetProps)) { + if (propsArePhysical(targetProps) || propsAreCloneDynamic(targetProps)) { return makeRunningValues(false, [], []); // let nearActionGrabEntity handle it } else { this.targetEntityID = targetProps.id; @@ -178,6 +193,13 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); return makeRunningValues(false, [], []); } + var props = Entities.getEntityProperties(this.targetEntityID); + if (!this.thisHandIsParent(props)) { + this.grabbing = false; + this.targetEntityID = null; + return makeRunningValues(false, [], []); + } + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.targetEntityID, "continueNearGrab", args); } else { diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js index f0a5b9f07d..46ed8df271 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -38,31 +38,52 @@ var GRAB_RADIUS = 0.35; this.handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); this.controllerJointIndex = getControllerJointIndex(this.hand); + this.getOtherModule = function() { + return (this.hand === RIGHT_HAND) ? leftNearParentingGrabOverlay : rightNearParentingGrabOverlay; + }; + + this.otherHandIsParent = function(props) { + return this.getOtherModule().thisHandIsParent(props); + }; + this.thisHandIsParent = function(props) { if (props.parentID !== MyAvatar.sessionUUID && props.parentID !== AVATAR_SELF_ID) { return false; } var handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); - if (props.parentJointIndex == handJointIndex) { + if (props.parentJointIndex === handJointIndex) { return true; } var controllerJointIndex = this.controllerJointIndex; - if (props.parentJointIndex == controllerJointIndex) { + if (props.parentJointIndex === controllerJointIndex) { return true; } var controllerCRJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? - "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : - "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); - if (props.parentJointIndex == controllerCRJointIndex) { + "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : + "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); + + if (props.parentJointIndex === controllerCRJointIndex) { return true; } return false; }; + this.getGrabbedProperties = function() { + return { + position: Overlays.getProperty(this.grabbedThingID, "position"), + rotation: Overlays.getProperty(this.grabbedThingID, "rotation"), + parentID: Overlays.getProperty(this.grabbedThingID, "parentID"), + parentJointIndex: Overlays.getProperty(this.grabbedThingID, "parentJointIndex"), + dynamic: false, + shapeType: "none" + }; + }; + + this.startNearParentingGrabOverlay = function (controllerData) { Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); @@ -74,15 +95,8 @@ var GRAB_RADIUS = 0.35; // } handJointIndex = this.controllerJointIndex; - var grabbedProperties = { - position: Overlays.getProperty(this.grabbedThingID, "position"), - rotation: Overlays.getProperty(this.grabbedThingID, "rotation"), - parentID: Overlays.getProperty(this.grabbedThingID, "parentID"), - parentJointIndex: Overlays.getProperty(this.grabbedThingID, "parentJointIndex"), - dynamic: false, - shapeType: "none" - }; - + var grabbedProperties = this.getGrabbedProperties(); + var reparentProps = { parentID: AVATAR_SELF_ID, parentJointIndex: handJointIndex, @@ -94,6 +108,12 @@ var GRAB_RADIUS = 0.35; // this should never happen, but if it does, don't set previous parent to be this hand. // this.previousParentID[this.grabbedThingID] = NULL; // this.previousParentJointIndex[this.grabbedThingID] = -1; + } else if (this.otherHandIsParent(grabbedProperties)) { + // the other hand is parent. Steal the object and information + var otherModule = this.getOtherModule(); + this.previousParentID[this.grabbedThingID] = otherModule.previousParentID[this.garbbedThingID]; + this.previousParentJointIndex[this.grabbedThingID] = otherModule.previousParentJointIndex[this.grabbedThingID]; + } else { this.previousParentID[this.grabbedThingID] = grabbedProperties.parentID; this.previousParentJointIndex[this.grabbedThingID] = grabbedProperties.parentJointIndex; @@ -117,7 +137,7 @@ var GRAB_RADIUS = 0.35; // before we grabbed it, overlay was a child of something; put it back. Overlays.editOverlay(this.grabbedThingID, { parentID: this.previousParentID[this.grabbedThingID], - parentJointIndex: this.previousParentJointIndex[this.grabbedThingID], + parentJointIndex: this.previousParentJointIndex[this.grabbedThingID] }); } @@ -135,10 +155,10 @@ var GRAB_RADIUS = 0.35; } return null; }; - + this.isReady = function (controllerData) { - if (controllerData.triggerClicks[this.hand] == 0) { + if (controllerData.triggerClicks[this.hand] === 0) { return makeRunningValues(false, [], []); } @@ -160,10 +180,16 @@ var GRAB_RADIUS = 0.35; }; this.run = function (controllerData) { - if (controllerData.triggerClicks[this.hand] == 0) { + if (controllerData.triggerClicks[this.hand] === 0) { this.endNearParentingGrabOverlay(); return makeRunningValues(false, [], []); } else { + // check if someone stole the target from us + var grabbedProperties = this.getGrabbedProperties(); + if (!this.thisHandIsParent(grabbedProperties)) { + return makeRunningValues(false, [], []); + } + return makeRunningValues(true, [this.grabbedThingID], []); } }; diff --git a/scripts/system/controllers/controllerModules/nearTrigger.js b/scripts/system/controllers/controllerModules/nearTrigger.js index 239778040c..4d31a8a22b 100644 --- a/scripts/system/controllers/controllerModules/nearTrigger.js +++ b/scripts/system/controllers/controllerModules/nearTrigger.js @@ -30,7 +30,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); this.parameters = makeDispatcherModuleParameters( 520, - this.hand === RIGHT_HAND ? ["rightHandTrigger"] : ["leftHandTrigger"], + this.hand === RIGHT_HAND ? ["rightHandTrigger", "rightHand"] : ["leftHandTrigger", "leftHand"], [], 100); diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index cd572b901a..60b820f06b 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -26,6 +26,7 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/overlayLaserInput.js", "controllerModules/webEntityLaserInput.js", "controllerModules/inEditMode.js", + "controllerModules/disableOtherModule.js", "teleport.js" ]; diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 6246c0f03a..17ac36d955 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -14,6 +14,7 @@ Messages, makeDispatcherModuleParameters, makeRunningValues, Settings, entityHasActions, Vec3, Overlays, flatten, Xform, getControllerWorldLocation, ensureDynamic */ +/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ Script.include("/~/system/libraries/Xform.js"); Script.include("/~/system/controllers/controllerDispatcherUtils.js"); @@ -225,7 +226,7 @@ function Teleporter(hand) { } this.parameters = makeDispatcherModuleParameters( - 300, + 80, this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], [], 100); @@ -338,12 +339,12 @@ function Teleporter(hand) { } }; } - + // related to repositioning the avatar after you teleport var FOOT_JOINT_NAMES = ["RightToe_End", "RightToeBase", "RightFoot"]; var DEFAULT_ROOT_TO_FOOT_OFFSET = 0.5; function getAvatarFootOffset() { - + // find a valid foot jointIndex var footJointIndex = -1; var i, l = FOOT_JOINT_NAMES.length; diff --git a/scripts/system/libraries/cloneEntityUtils.js b/scripts/system/libraries/cloneEntityUtils.js index 69c91fc398..a8e8bc227d 100644 --- a/scripts/system/libraries/cloneEntityUtils.js +++ b/scripts/system/libraries/cloneEntityUtils.js @@ -5,11 +5,13 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +/* global entityIsCloneable:true, getGrabbableData:true, cloneEntity:true propsAreCloneDynamic:true */ + Script.include("/~/system/controllers/controllerDispatcherUtils.js"); // Object assign polyfill -if (typeof Object.assign != 'function') { +if (typeof Object.assign !== 'function') { Object.assign = function(target, varArgs) { if (target === null) { throw new TypeError('Cannot convert undefined or null to object'); @@ -36,7 +38,18 @@ entityIsCloneable = function(props) { } return false; -} +}; + +propsAreCloneDynamic = function(props) { + var cloneable = entityIsCloneable(props); + if (cloneable) { + var grabInfo = getGrabbableData(props); + if (grabInfo.cloneDynamic) { + return true; + } + } + return false; +}; cloneEntity = function(props, worldEntityProps) { @@ -49,26 +62,26 @@ cloneEntity = function(props, worldEntityProps) { count++; } }); - + var grabInfo = getGrabbableData(cloneableProps); var limit = grabInfo.cloneLimit ? grabInfo.cloneLimit : 0; if (count >= limit && limit !== 0) { return null; } - + cloneableProps.name = cloneableProps.name + '-clone-' + cloneableProps.id; var lifetime = grabInfo.cloneLifetime ? grabInfo.cloneLifetime : 300; var dynamic = grabInfo.cloneDynamic ? grabInfo.cloneDynamic : false; var cUserData = Object.assign({}, JSON.parse(cloneableProps.userData)); var cProperties = Object.assign({}, cloneableProps); - + delete cUserData.grabbableKey.cloneLifetime; delete cUserData.grabbableKey.cloneable; delete cUserData.grabbableKey.cloneDynamic; delete cUserData.grabbableKey.cloneLimit; delete cProperties.id; - + cProperties.dynamic = dynamic; cProperties.locked = false; @@ -76,7 +89,7 @@ cloneEntity = function(props, worldEntityProps) { cUserData.grabbableKey.grabbable = true; cProperties.lifetime = lifetime; cProperties.userData = JSON.stringify(cUserData); - + var cloneID = Entities.addEntity(cProperties); return cloneID; -} +}; From 28694dcf8a6e76918d1f1130d61abc28c2576d73 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 5 Sep 2017 11:46:06 -0700 Subject: [PATCH 42/59] small change to contextOVerlays in farActionGrab --- .../controllers/controllerModules/farActionGrabEntity.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 7883222c73..6e265742a6 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -434,11 +434,6 @@ Script.include("/~/system/libraries/controllers.js"); return makeRunningValues(false, [], []); } - var targetEntity = controllerData.rayPicks[this.hand].objectID; - if (targetEntity !== this.entityWithContextOverlay) { - this.destroyContextOverlay(); - } - var otherModuleName =this.hand === RIGHT_HAND ? "LeftFarActionGrabEntity" : "RightFarActionGrabEntity"; var otherFarGrabModule = getEnabledModuleByName(otherModuleName); From cefa65de5e5069020d4b27fad840c8a6b1830536 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 5 Sep 2017 12:06:32 -0700 Subject: [PATCH 43/59] some more small changes --- .../controllers/controllerModules/farActionGrabEntity.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 6e265742a6..822ad55f88 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -483,6 +483,10 @@ Script.include("/~/system/libraries/controllers.js"); "userData", "locked", "type" ]); + if (entityID !== this.entityWithContextOverlay) { + this.destroyContextOverlay(); + } + if (entityIsDistanceGrabbable(targetProps)) { if (!this.distanceRotating) { this.grabbedThingID = entityID; From 0d66bfca0570aa480d4e891ea78e3a2de3c5f630 Mon Sep 17 00:00:00 2001 From: druiz17 Date: Wed, 6 Sep 2017 14:58:04 -0700 Subject: [PATCH 44/59] added far trigger --- .../controllerModules/equipEntity.js | 21 +- .../controllerModules/farActionGrabEntity.js | 1 + .../controllerModules/farTrigger.js | 234 ++++++++++++++++++ .../system/controllers/controllerScripts.js | 1 + 4 files changed, 254 insertions(+), 3 deletions(-) create mode 100644 scripts/system/controllers/controllerModules/farTrigger.js diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index a9e62de4df..211989d661 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -257,7 +257,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.parameters = makeDispatcherModuleParameters( 300, - this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip"] : ["leftHand", "rightHandEquip"], + this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip"] : ["leftHand", "leftHandEquip"], [], 100); @@ -539,6 +539,8 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa ensureDynamic(this.targetEntityID); this.targetEntityID = null; + this.messageGrabEntity = false; + this.grabEntityProps = null; }; this.updateInputs = function (controllerData) { @@ -594,11 +596,18 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa } return makeRunningValues(true, [potentialEquipHotspot.entityID], []); } else { - this.messageGrabEnity = false; return makeRunningValues(false, [], []); } }; + this.isTargetIDValid = function() { + var entityProperties = Entities.getEntityProperties(this.targetEntityID); + for (var propertry in entityProperties) { + return true; + } + return false; + }; + this.isReady = function (controllerData, deltaTime) { var timestamp = Date.now(); this.updateInputs(controllerData); @@ -609,6 +618,11 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var timestamp = Date.now(); this.updateInputs(controllerData); + if (!this.isTargetIDValid()) { + this.endEquipEntity(); + return makeRunningValues(false, [], []); + } + if (!this.targetEntityID) { return this.checkNearbyHotspots(controllerData, deltaTime, timestamp); } @@ -681,7 +695,8 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa if (channel === 'Hifi-Hand-Grab') { try { data = JSON.parse(message); - var equipModule = (data.hand === 'left') ? leftEquipEntity : rightEquipEntity; + print(JSON.stringify(data)); + var equipModule = (data.hand === "left") ? leftEquipEntity : rightEquipEntity; var entityProperties = Entities.getEntityProperties(data.entityID, DISPATCHER_PROPERTIES); entityProperties.id = data.entityID; equipModule.setMessageGrabData(entityProperties); diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 822ad55f88..44360c2e39 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -439,6 +439,7 @@ Script.include("/~/system/libraries/controllers.js"); // gather up the readiness of the near-grab modules var nearGrabNames = [ + this.hand === RIGHT_HAND ? "RightFarTriggerEntity" : "LeftFarTriggerEntity", this.hand === RIGHT_HAND ? "RightNearActionGrabEntity" : "LeftNearActionGrabEntity", this.hand === RIGHT_HAND ? "RightNearParentingGrabEntity" : "LeftNearParentingGrabEntity", this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay" diff --git a/scripts/system/controllers/controllerModules/farTrigger.js b/scripts/system/controllers/controllerModules/farTrigger.js new file mode 100644 index 0000000000..671fc58c7d --- /dev/null +++ b/scripts/system/controllers/controllerModules/farTrigger.js @@ -0,0 +1,234 @@ +"use strict"; + +// farTrigger.js +// +// 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, Controller, LaserPointers, RayPick, RIGHT_HAND, LEFT_HAND, Mat4, MyAvatar, Vec3, Camera, Quat, + getGrabPointSphereOffset, getEnabledModuleByName, makeRunningValues, Entities, NULL_UUID, + enableDispatcherModule, disableDispatcherModule, entityIsDistanceGrabbable, + 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, getGrabbableData + +*/ + +Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllers.js"); + +(function() { + 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 + drawInFront: 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, + drawInFront: 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 + drawInFront: 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, + drawInFront: 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 + drawInFront: 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} + ]; + + function entityWantsNearTrigger(props) { + var grabbableData = getGrabbableData(props); + return grabbableData.triggerable || grabbableData.wantsTrigger; + } + + function FarTriggerEntity(hand) { + this.hand = hand; + this.targetEntityID = null; + this.grabbing = false; + this.previousParentID = {}; + this.previousParentJointIndex = {}; + this.previouslyUnhooked = {}; + + this.parameters = makeDispatcherModuleParameters( + 520, + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100); + + this.handToController = function() { + return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + }; + + this.updateLaserPointer = function(controllerData) { + var SEARCH_SPHERE_SIZE = 0.011; + var MIN_SPHERE_SIZE = 0.0005; + var radius = Math.max(1.2 * SEARCH_SPHERE_SIZE * this.intersectionDistance, MIN_SPHERE_SIZE); + var dim = {x: radius, y: radius, z: radius}; + var mode = "none"; + if (controllerData.triggerClicks[this.hand]) { + mode = "full"; + } else { + mode = "half"; + } + + var laserPointerID = this.laserPointer; + if (mode === "full") { + var fullEndToEdit = this.fullEnd; + fullEndToEdit.dimensions = dim; + LaserPointers.editRenderState(laserPointerID, mode, {path: fullPath, end: fullEndToEdit}); + } else if (mode === "half") { + var halfEndToEdit = this.halfEnd; + halfEndToEdit.dimensions = dim; + LaserPointers.editRenderState(laserPointerID, mode, {path: halfPath, end: halfEndToEdit}); + } + LaserPointers.enableLaserPointer(laserPointerID); + LaserPointers.setRenderState(laserPointerID, mode); + }; + + this.laserPointerOff = function() { + LaserPointers.disableLaserPointer(this.laserPointer); + }; + + this.getTargetProps = function (controllerData) { + // nearbyEntityProperties is already sorted by length from controller + var targetEntity = controllerData.rayPicks[this.hand].objectID; + if (targetEntity) { + var targetProperties = Entities.getEntityProperties(targetEntity); + if (entityWantsNearTrigger(targetProperties)) { + return targetProperties; + } + } + return null; + }; + + this.startFarTrigger = function (controllerData) { + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.targetEntityID, "startFarTrigger", args); + }; + + this.continueFarTrigger = function (controllerData) { + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.targetEntityID, "continueFarTrigger", args); + }; + + this.endFarTrigger = function (controllerData) { + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.targetEntityID, "stopFarTrigger", args); + this.laserPointerOff(); + }; + + this.isReady = function (controllerData) { + this.targetEntityID = null; + if (controllerData.triggerClicks[this.hand] === 0) { + return makeRunningValues(false, [], []); + } + + var targetProps = this.getTargetProps(controllerData); + if (targetProps) { + this.targetEntityID = targetProps.id; + this.startFarTrigger(controllerData); + return makeRunningValues(true, [this.targetEntityID], []); + } else { + return makeRunningValues(false, [], []); + } + }; + + this.run = function (controllerData) { + var targetEntity = controllerData.rayPicks[this.hand].objectID; + if (controllerData.triggerClicks[this.hand] === 0 || this.targetEntityID !== targetEntity) { + this.endFarTrigger(controllerData); + return makeRunningValues(false, [], []); + } + + this.updateLaserPointer(controllerData); + this.continueFarTrigger(controllerData); + return makeRunningValues(true, [this.targetEntityID], []); + }; + + this.halfEnd = halfEnd; + this.fullEnd = fullEnd; + this.laserPointer = LaserPointers.createLaserPointer({ + joint: (this.hand === RIGHT_HAND) ? "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", + filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, + maxDistance: PICK_MAX_DISTANCE, + posOffset: getGrabPointSphereOffset(this.handToController()), + renderStates: renderStates, + faceAvatar: true, + defaultRenderStates: defaultRenderStates + }); + + this.cleanup = function () { + if (this.targetEntityID) { + this.endFarTrigger(); + } + + LaserPointers.disableLaserPointer(this.laserPointer); + LaserPointers.removeLaserPointer(this.laserPointer); + }; + } + + var leftFarTriggerEntity = new FarTriggerEntity(LEFT_HAND); + var rightFarTriggerEntity = new FarTriggerEntity(RIGHT_HAND); + + enableDispatcherModule("LeftFarTriggerEntity", leftFarTriggerEntity); + enableDispatcherModule("RightFarTriggerEntity", rightFarTriggerEntity); + + this.cleanup = function () { + leftFarTriggerEntity.cleanup(); + rightFarTriggerEntity.cleanup(); + disableDispatcherModule("LeftFarTriggerEntity"); + disableDispatcherModule("RightFarTriggerEntity"); + }; + Script.scriptEnding.connect(this.cleanup); +}()); diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 60b820f06b..569fad4855 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -27,6 +27,7 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/webEntityLaserInput.js", "controllerModules/inEditMode.js", "controllerModules/disableOtherModule.js", + "controllerModules/farTrigger.js", "teleport.js" ]; From cc7a22237454c760ccd1ddb87f4b54414c13979a Mon Sep 17 00:00:00 2001 From: druiz17 Date: Wed, 6 Sep 2017 17:15:05 -0700 Subject: [PATCH 45/59] fixing Hifi-Hand-Drop for equipEntity --- .../controllerModules/equipEntity.js | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index 211989d661..40e0e19cf3 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -492,6 +492,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa localPosition: this.offsetPosition, localRotation: this.offsetRotation }; + var isClone = false; if (entityIsCloneable(grabbedProperties)) { var cloneID = this.cloneHotspot(grabbedProperties, controllerData); @@ -622,7 +623,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.endEquipEntity(); return makeRunningValues(false, [], []); } - + if (!this.targetEntityID) { return this.checkNearbyHotspots(controllerData, deltaTime, timestamp); } @@ -695,7 +696,6 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa if (channel === 'Hifi-Hand-Grab') { try { data = JSON.parse(message); - print(JSON.stringify(data)); var equipModule = (data.hand === "left") ? leftEquipEntity : rightEquipEntity; var entityProperties = Entities.getEntityProperties(data.entityID, DISPATCHER_PROPERTIES); entityProperties.id = data.entityID; @@ -704,19 +704,17 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa } catch (e) { print("WARNING: equipEntity.js -- error parsing Hifi-Hand-Grab message: " + message); } - } - } else if (channel === 'Hifi-Hand-Drop') { - data = JSON.parse(message); - if (data.hand === 'left') { - leftEquipEntity.endEquipEntity(); - } else if (data.hand === 'right') { - rightEquipEntity.endEquipEntity(); - } else if (data.hand === 'both') { - leftEquipEntity.endEquipEntity(); - rightEquipEntity.endEquipEntity(); + } else if (channel === 'Hifi-Hand-Drop') { + if (message === "left") { + leftEquipEntity.endEquipEntity(); + } else if (message === "right") { + rightEquipEntity.endEquipEntity(); + } else if (message === "both") { + leftEquipEntity.endEquipEntity(); + rightEquipEntity.endEquipEntity(); + } } } - }; Messages.subscribe('Hifi-Hand-Grab'); From ef050a09f9caf64daef0263cf54e90308d5b5587 Mon Sep 17 00:00:00 2001 From: druiz17 Date: Thu, 7 Sep 2017 14:42:46 -0700 Subject: [PATCH 46/59] made requested changes and also allow entities/overlays to be grabbed by the grip button --- .../controllers/controllerDispatcher.js | 682 +++++++++--------- .../controllerModules/disableOtherModule.js | 3 +- .../controllerModules/equipEntity.js | 2 +- .../controllerModules/farActionGrabEntity.js | 2 +- .../controllerModules/farTrigger.js | 2 +- .../controllerModules/inEditMode.js | 3 +- .../controllerModules/nearActionGrabEntity.js | 2 +- .../controllerModules/nearParentGrabEntity.js | 8 +- .../nearParentGrabOverlay.js | 6 +- .../controllerModules/nearTrigger.js | 2 +- .../controllerModules/overlayLaserInput.js | 2 +- .../controllerModules/tabletStylusInput.js | 2 +- .../{ => controllerModules}/teleport.js | 8 +- .../controllerModules/webEntityLaserInput.js | 2 +- .../system/controllers/controllerScripts.js | 2 +- .../controllerDispatcherUtils.js | 0 16 files changed, 371 insertions(+), 357 deletions(-) rename scripts/system/controllers/{ => controllerModules}/teleport.js (98%) rename scripts/system/{controllers => libraries}/controllerDispatcherUtils.js (100%) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 3826222666..43ad2ccf51 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -21,366 +21,378 @@ Script.include("/~/system/libraries/controllers.js"); Script.include("/~/system/controllers/controllerDispatcherUtils.js"); (function() { - var _this = this; var NEAR_MAX_RADIUS = 1.0; var TARGET_UPDATE_HZ = 60; // 50hz good enough, but we're using update var BASIC_TIMER_INTERVAL_MS = 1000 / TARGET_UPDATE_HZ; - this.lastInterval = Date.now(); - this.intervalCount = 0; - this.totalDelta = 0; - this.totalVariance = 0; - this.highVarianceCount = 0; - this.veryhighVarianceCount = 0; - this.tabletID = null; - this.blacklist = []; - // a module can occupy one or more "activity" slots while it's running. If all the required slots for a module are - // not set to false (not in use), a module cannot start. When a module is using a slot, that module's name - // is stored as the value, rather than false. - this.activitySlots = { - leftHand: false, - rightHand: false, - mouse: false - }; + function ControllerDispatcher() { + var _this = this + this.lastInterval = Date.now(); + this.intervalCount = 0; + this.totalDelta = 0; + this.totalVariance = 0; + this.highVarianceCount = 0; + this.veryhighVarianceCount = 0; + this.tabletID = null; + this.blacklist = []; - this.slotsAreAvailableForPlugin = function (plugin) { - for (var i = 0; i < plugin.parameters.activitySlots.length; i++) { - if (_this.activitySlots[plugin.parameters.activitySlots[i]]) { - return false; // something is already using a slot which _this plugin requires - } - } - return true; - }; - - this.markSlots = function (plugin, used) { - for (var i = 0; i < plugin.parameters.activitySlots.length; i++) { - _this.activitySlots[plugin.parameters.activitySlots[i]] = used; - } - }; - - this.unmarkSlotsForPluginName = function (runningPluginName) { - // this is used to free activity-slots when a plugin is deactivated while it's running. - for (var activitySlot in _this.activitySlots) { - if (activitySlot.hasOwnProperty(activitySlot) && _this.activitySlots[activitySlot] === runningPluginName) { - _this.activitySlots[activitySlot] = false; - } - } - }; - - this.runningPluginNames = {}; - this.leftTriggerValue = 0; - this.leftTriggerClicked = 0; - this.rightTriggerValue = 0; - this.rightTriggerClicked = 0; - this.leftSecondaryValue = 0; - this.rightSecondaryValue = 0; - - this.leftTriggerPress = function (value) { - _this.leftTriggerValue = value; - }; - this.leftTriggerClick = function (value) { - _this.leftTriggerClicked = value; - }; - this.rightTriggerPress = function (value) { - _this.rightTriggerValue = value; - }; - this.rightTriggerClick = function (value) { - _this.rightTriggerClicked = value; - }; - this.leftSecondaryPress = function (value) { - _this.leftSecondaryValue = value; - }; - this.rightSecondaryPress = function (value) { - _this.rightSecondaryValue = value; - }; - - - this.dataGatherers = {}; - this.dataGatherers.leftControllerLocation = function () { - return getControllerWorldLocation(Controller.Standard.LeftHand, true); - }; - this.dataGatherers.rightControllerLocation = function () { - return getControllerWorldLocation(Controller.Standard.RightHand, true); - }; - - this.updateTimings = function () { - _this.intervalCount++; - var thisInterval = Date.now(); - var deltaTimeMsec = thisInterval - _this.lastInterval; - var deltaTime = deltaTimeMsec / 1000; - _this.lastInterval = thisInterval; - - _this.totalDelta += deltaTimeMsec; - - var variance = Math.abs(deltaTimeMsec - BASIC_TIMER_INTERVAL_MS); - _this.totalVariance += variance; - - if (variance > 1) { - _this.highVarianceCount++; - } - - if (variance > 5) { - _this.veryhighVarianceCount++; - } - - return deltaTime; - }; - - this.setIgnoreTablet = function() { - if (HMD.tabletID !== _this.tabletID) { - RayPick.setIgnoreOverlays(_this.leftControllerRayPick, [HMD.tabletID]); - RayPick.setIgnoreOverlays(_this.rightControllerRayPick, [HMD.tabletID]); - } - }; - - this.update = function () { - var deltaTime = this.updateTimings(); - this.setIgnoreTablet(); - - if (controllerDispatcherPluginsNeedSort) { - this.orderedPluginNames = []; - for (var pluginName in controllerDispatcherPlugins) { - if (controllerDispatcherPlugins.hasOwnProperty(pluginName)) { - this.orderedPluginNames.push(pluginName); - } - } - this.orderedPluginNames.sort(function (a, b) { - return controllerDispatcherPlugins[a].parameters.priority - - controllerDispatcherPlugins[b].parameters.priority; - }); - - // print("controllerDispatcher -- new plugin order: " + JSON.stringify(this.orderedPluginNames)); - var output = "controllerDispatcher -- new plugin order: "; - for (var k = 0; k < this.orderedPluginNames.length; k++) { - var dbgPluginName = this.orderedPluginNames[k]; - var priority = controllerDispatcherPlugins[dbgPluginName].parameters.priority; - output += dbgPluginName + ":" + priority; - if (k + 1 < this.orderedPluginNames.length) { - output += ", "; - } - } - print(output); - - controllerDispatcherPluginsNeedSort = false; - } - - var controllerLocations = [ - _this.dataGatherers.leftControllerLocation(), - _this.dataGatherers.rightControllerLocation() - ]; - - // find 3d overlays near each hand - var nearbyOverlayIDs = []; - var h; - for (h = LEFT_HAND; h <= RIGHT_HAND; h++) { - // todo: check controllerLocations[h].valid - var nearbyOverlays = Overlays.findOverlays(controllerLocations[h].position, NEAR_MAX_RADIUS); - nearbyOverlays.sort(function (a, b) { - var aPosition = Overlays.getProperty(a, "position"); - var aDistance = Vec3.distance(aPosition, controllerLocations[h].position); - var bPosition = Overlays.getProperty(b, "position"); - var bDistance = Vec3.distance(bPosition, controllerLocations[h].position); - return aDistance - bDistance; - }); - nearbyOverlayIDs.push(nearbyOverlays); - } - - // find entities near each hand - var nearbyEntityProperties = [[], []]; - var nearbyEntityPropertiesByID = {}; - for (h = LEFT_HAND; h <= RIGHT_HAND; h++) { - // todo: check controllerLocations[h].valid - var controllerPosition = controllerLocations[h].position; - var nearbyEntityIDs = Entities.findEntities(controllerPosition, NEAR_MAX_RADIUS); - for (var j = 0; j < nearbyEntityIDs.length; j++) { - var entityID = nearbyEntityIDs[j]; - var props = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); - props.id = entityID; - nearbyEntityPropertiesByID[entityID] = props; - nearbyEntityProperties[h].push(props); - } - } - - // raypick for each controller - var rayPicks = [ - RayPick.getPrevRayPickResult(_this.leftControllerRayPick), - RayPick.getPrevRayPickResult(_this.rightControllerRayPick) - ]; - var hudRayPicks = [ - RayPick.getPrevRayPickResult(_this.leftControllerHudRayPick), - RayPick.getPrevRayPickResult(_this.rightControllerHudRayPick) - ]; - // if the pickray hit something very nearby, put it into the nearby entities list - for (h = LEFT_HAND; h <= RIGHT_HAND; h++) { - - // XXX find a way to extract searchRay from samuel's stuff - rayPicks[h].searchRay = { - origin: controllerLocations[h].position, - direction: Quat.getUp(controllerLocations[h].orientation), - length: 1000 - }; - - if (rayPicks[h].type === RayPick.INTERSECTED_ENTITY) { - // XXX check to make sure this one isn't already in nearbyEntityProperties? - if (rayPicks[h].distance < NEAR_GRAB_PICK_RADIUS) { - var nearEntityID = rayPicks[h].objectID; - var nearbyProps = Entities.getEntityProperties(nearEntityID, DISPATCHER_PROPERTIES); - nearbyProps.id = nearEntityID; - nearbyEntityPropertiesByID[nearEntityID] = nearbyProps; - nearbyEntityProperties[h].push(nearbyProps); - } - } - - // sort by distance from each hand - nearbyEntityProperties[h].sort(function (a, b) { - var aDistance = Vec3.distance(a.position, controllerLocations[h].position); - var bDistance = Vec3.distance(b.position, controllerLocations[h].position); - return aDistance - bDistance; - }); - } - - // bundle up all the data about the current situation - var controllerData = { - triggerValues: [_this.leftTriggerValue, _this.rightTriggerValue], - triggerClicks: [_this.leftTriggerClicked, _this.rightTriggerClicked], - secondaryValues: [_this.leftSecondaryValue, _this.rightSecondaryValue], - controllerLocations: controllerLocations, - nearbyEntityProperties: nearbyEntityProperties, - nearbyEntityPropertiesByID: nearbyEntityPropertiesByID, - nearbyOverlayIDs: nearbyOverlayIDs, - rayPicks: rayPicks, - hudRayPicks: hudRayPicks + // a module can occupy one or more "activity" slots while it's running. If all the required slots for a module are + // not set to false (not in use), a module cannot start. When a module is using a slot, that module's name + // is stored as the value, rather than false. + this.activitySlots = { + leftHand: false, + rightHand: false, + rightHandTrigger: false, + leftHandTrigger: false, + rightHandEquip: false, + leftHandEquip: false, + mouse: false }; - // check for plugins that would like to start. ask in order of increasing priority value - for (var pluginIndex = 0; pluginIndex < this.orderedPluginNames.length; pluginIndex++) { - var orderedPluginName = this.orderedPluginNames[pluginIndex]; - var candidatePlugin = controllerDispatcherPlugins[orderedPluginName]; - - if (_this.slotsAreAvailableForPlugin(candidatePlugin)) { - var readiness = candidatePlugin.isReady(controllerData, deltaTime); - if (readiness.active) { - // this plugin will start. add it to the list of running plugins and mark the - // activity-slots which this plugin consumes as "in use" - _this.runningPluginNames[orderedPluginName] = true; - _this.markSlots(candidatePlugin, orderedPluginName); + this.slotsAreAvailableForPlugin = function (plugin) { + for (var i = 0; i < plugin.parameters.activitySlots.length; i++) { + if (_this.activitySlots[plugin.parameters.activitySlots[i]]) { + return false; // something is already using a slot which _this plugin requires } } - } + return true; + }; - // print("QQQ running plugins: " + JSON.stringify(_this.runningPluginNames)); + this.markSlots = function (plugin, pluginName) { + for (var i = 0; i < plugin.parameters.activitySlots.length; i++) { + _this.activitySlots[plugin.parameters.activitySlots[i]] = pluginName; + } + }; - // give time to running plugins - for (var runningPluginName in _this.runningPluginNames) { - if (_this.runningPluginNames.hasOwnProperty(runningPluginName)) { - var plugin = controllerDispatcherPlugins[runningPluginName]; - if (!plugin) { - // plugin was deactivated while running. find the activity-slots it was using and make - // them available. - delete _this.runningPluginNames[runningPluginName]; - _this.unmarkSlotsForPluginName(runningPluginName); + this.unmarkSlotsForPluginName = function (runningPluginName) { + // this is used to free activity-slots when a plugin is deactivated while it's running. + for (var activitySlot in _this.activitySlots) { + if (activitySlot.hasOwnProperty(activitySlot) && _this.activitySlots[activitySlot] === runningPluginName) { + _this.activitySlots[activitySlot] = false; + } + } + }; + + this.runningPluginNames = {}; + this.leftTriggerValue = 0; + this.leftTriggerClicked = 0; + this.rightTriggerValue = 0; + this.rightTriggerClicked = 0; + this.leftSecondaryValue = 0; + this.rightSecondaryValue = 0; + + this.leftTriggerPress = function (value) { + _this.leftTriggerValue = value; + }; + this.leftTriggerClick = function (value) { + _this.leftTriggerClicked = value; + }; + this.rightTriggerPress = function (value) { + _this.rightTriggerValue = value; + }; + this.rightTriggerClick = function (value) { + _this.rightTriggerClicked = value; + }; + this.leftSecondaryPress = function (value) { + _this.leftSecondaryValue = value; + }; + this.rightSecondaryPress = function (value) { + _this.rightSecondaryValue = value; + }; + + + this.dataGatherers = {}; + this.dataGatherers.leftControllerLocation = function () { + return getControllerWorldLocation(Controller.Standard.LeftHand, true); + }; + this.dataGatherers.rightControllerLocation = function () { + return getControllerWorldLocation(Controller.Standard.RightHand, true); + }; + + this.updateTimings = function () { + _this.intervalCount++; + var thisInterval = Date.now(); + var deltaTimeMsec = thisInterval - _this.lastInterval; + var deltaTime = deltaTimeMsec / 1000; + _this.lastInterval = thisInterval; + + _this.totalDelta += deltaTimeMsec; + + var variance = Math.abs(deltaTimeMsec - BASIC_TIMER_INTERVAL_MS); + _this.totalVariance += variance; + + if (variance > 1) { + _this.highVarianceCount++; + } + + if (variance > 5) { + _this.veryhighVarianceCount++; + } + + return deltaTime; + }; + + this.setIgnoreTablet = function() { + if (HMD.tabletID !== _this.tabletID) { + RayPick.setIgnoreOverlays(_this.leftControllerRayPick, [HMD.tabletID]); + RayPick.setIgnoreOverlays(_this.rightControllerRayPick, [HMD.tabletID]); + } + }; + + this.update = function () { + var deltaTime = _this.updateTimings(); + _this.setIgnoreTablet(); + + if (controllerDispatcherPluginsNeedSort) { + _this.orderedPluginNames = []; + for (var pluginName in controllerDispatcherPlugins) { + if (controllerDispatcherPlugins.hasOwnProperty(pluginName)) { + _this.orderedPluginNames.push(pluginName); + } + } + _this.orderedPluginNames.sort(function (a, b) { + return controllerDispatcherPlugins[a].parameters.priority - + controllerDispatcherPlugins[b].parameters.priority; + }); + + // print("controllerDispatcher -- new plugin order: " + JSON.stringify(this.orderedPluginNames)); + var output = "controllerDispatcher -- new plugin order: "; + for (var k = 0; k < _this.orderedPluginNames.length; k++) { + var dbgPluginName = _this.orderedPluginNames[k]; + var priority = controllerDispatcherPlugins[dbgPluginName].parameters.priority; + output += dbgPluginName + ":" + priority; + if (k + 1 < _this.orderedPluginNames.length) { + output += ", "; + } + } + + controllerDispatcherPluginsNeedSort = false; + } + + var controllerLocations = [ + _this.dataGatherers.leftControllerLocation(), + _this.dataGatherers.rightControllerLocation() + ]; + + // find 3d overlays near each hand + var nearbyOverlayIDs = []; + var h; + for (h = LEFT_HAND; h <= RIGHT_HAND; h++) { + if (controllerLocations[h].valid) { + var nearbyOverlays = Overlays.findOverlays(controllerLocations[h].position, NEAR_MAX_RADIUS); + nearbyOverlays.sort(function (a, b) { + var aPosition = Overlays.getProperty(a, "position"); + var aDistance = Vec3.distance(aPosition, controllerLocations[h].position); + var bPosition = Overlays.getProperty(b, "position"); + var bDistance = Vec3.distance(bPosition, controllerLocations[h].position); + return aDistance - bDistance; + }); + nearbyOverlayIDs.push(nearbyOverlays); } else { - var runningness = plugin.run(controllerData, deltaTime); - if (!runningness.active) { - // 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); + nearbyOverlayIDs.push([]); + } + } + + // find entities near each hand + var nearbyEntityProperties = [[], []]; + var nearbyEntityPropertiesByID = {}; + for (h = LEFT_HAND; h <= RIGHT_HAND; h++) { + if (controllerLocations[h].valid) { + var controllerPosition = controllerLocations[h].position; + var nearbyEntityIDs = Entities.findEntities(controllerPosition, NEAR_MAX_RADIUS); + for (var j = 0; j < nearbyEntityIDs.length; j++) { + var entityID = nearbyEntityIDs[j]; + var props = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); + props.id = entityID; + nearbyEntityPropertiesByID[entityID] = props; + nearbyEntityProperties[h].push(props); } } } - } - }; - this.setBlacklist = function() { - RayPick.setIgnoreEntities(_this.leftControllerRayPick, this.blacklist); - RayPick.setIgnoreEntities(_this.rightControllerRayPick, this.blacklist); + // raypick for each controller + var rayPicks = [ + RayPick.getPrevRayPickResult(_this.leftControllerRayPick), + RayPick.getPrevRayPickResult(_this.rightControllerRayPick) + ]; + var hudRayPicks = [ + RayPick.getPrevRayPickResult(_this.leftControllerHudRayPick), + RayPick.getPrevRayPickResult(_this.rightControllerHudRayPick) + ]; + // if the pickray hit something very nearby, put it into the nearby entities list + for (h = LEFT_HAND; h <= RIGHT_HAND; h++) { - }; + // XXX find a way to extract searchRay from samuel's stuff + rayPicks[h].searchRay = { + origin: controllerLocations[h].position, + direction: Quat.getUp(controllerLocations[h].orientation), + length: 1000 + }; - var MAPPING_NAME = "com.highfidelity.controllerDispatcher"; - var mapping = Controller.newMapping(MAPPING_NAME); - mapping.from([Controller.Standard.RT]).peek().to(_this.rightTriggerPress); - mapping.from([Controller.Standard.RTClick]).peek().to(_this.rightTriggerClick); - mapping.from([Controller.Standard.LT]).peek().to(_this.leftTriggerPress); - mapping.from([Controller.Standard.LTClick]).peek().to(_this.leftTriggerClick); - - mapping.from([Controller.Standard.RB]).peek().to(_this.rightSecondaryPress); - mapping.from([Controller.Standard.LB]).peek().to(_this.leftSecondaryPress); - mapping.from([Controller.Standard.LeftGrip]).peek().to(_this.leftSecondaryPress); - mapping.from([Controller.Standard.RightGrip]).peek().to(_this.rightSecondaryPress); - - Controller.enableMapping(MAPPING_NAME); - - this.leftControllerRayPick = RayPick.createRayPick({ - joint: "_CONTROLLER_LEFTHAND", - filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, - enabled: true, - maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE, - posOffset: getGrabPointSphereOffset(Controller.Standard.LeftHand) - }); - this.leftControllerHudRayPick = RayPick.createRayPick({ - joint: "_CONTROLLER_LEFTHAND", - filter: RayPick.PICK_HUD, - enabled: true, - maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE, - posOffset: getGrabPointSphereOffset(Controller.Standard.LeftHand) - }); - this.rightControllerRayPick = RayPick.createRayPick({ - joint: "_CONTROLLER_RIGHTHAND", - filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, - enabled: true, - maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE, - posOffset: getGrabPointSphereOffset(Controller.Standard.RightHand) - }); - this.rightControllerHudRayPick = RayPick.createRayPick({ - joint: "_CONTROLLER_RIGHTHAND", - filter: RayPick.PICK_HUD, - enabled: true, - maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE, - posOffset: getGrabPointSphereOffset(Controller.Standard.RightHand) - }); - - this.handleHandMessage = function(channel, message, sender) { - var data; - if (sender === MyAvatar.sessionUUID) { - try { - if (channel === 'Hifi-Hand-RayPick-Blacklist') { - data = JSON.parse(message); - var action = data.action; - var id = data.id; - var index = this.blacklis.indexOf(id); - - if (action === 'add' && index === -1) { - this.blacklist.push(id); - this.setBlacklist(); + if (rayPicks[h].type === RayPick.INTERSECTED_ENTITY) { + // XXX check to make sure this one isn't already in nearbyEntityProperties? + if (rayPicks[h].distance < NEAR_GRAB_PICK_RADIUS) { + var nearEntityID = rayPicks[h].objectID; + var nearbyProps = Entities.getEntityProperties(nearEntityID, DISPATCHER_PROPERTIES); + nearbyProps.id = nearEntityID; + nearbyEntityPropertiesByID[nearEntityID] = nearbyProps; + nearbyEntityProperties[h].push(nearbyProps); } + } - if (action === 'remove') { - if (index > -1) { - this.blacklist.splice(index, 1); - this.setBlacklist(); + // sort by distance from each hand + nearbyEntityProperties[h].sort(function (a, b) { + var aDistance = Vec3.distance(a.position, controllerLocations[h].position); + var bDistance = Vec3.distance(b.position, controllerLocations[h].position); + return aDistance - bDistance; + }); + } + + // bundle up all the data about the current situation + var controllerData = { + triggerValues: [_this.leftTriggerValue, _this.rightTriggerValue], + triggerClicks: [_this.leftTriggerClicked, _this.rightTriggerClicked], + secondaryValues: [_this.leftSecondaryValue, _this.rightSecondaryValue], + controllerLocations: controllerLocations, + nearbyEntityProperties: nearbyEntityProperties, + nearbyEntityPropertiesByID: nearbyEntityPropertiesByID, + nearbyOverlayIDs: nearbyOverlayIDs, + rayPicks: rayPicks, + hudRayPicks: hudRayPicks + }; + + // check for plugins that would like to start. ask in order of increasing priority value + for (var pluginIndex = 0; pluginIndex < _this.orderedPluginNames.length; pluginIndex++) { + var orderedPluginName = _this.orderedPluginNames[pluginIndex]; + var candidatePlugin = controllerDispatcherPlugins[orderedPluginName]; + + if (_this.slotsAreAvailableForPlugin(candidatePlugin)) { + var readiness = candidatePlugin.isReady(controllerData, deltaTime); + if (readiness.active) { + // this plugin will start. add it to the list of running plugins and mark the + // activity-slots which this plugin consumes as "in use" + _this.runningPluginNames[orderedPluginName] = true; + _this.markSlots(candidatePlugin, orderedPluginName); + } + } + } + + // print("QQQ running plugins: " + JSON.stringify(_this.runningPluginNames)); + + // give time to running plugins + for (var runningPluginName in _this.runningPluginNames) { + if (_this.runningPluginNames.hasOwnProperty(runningPluginName)) { + var plugin = controllerDispatcherPlugins[runningPluginName]; + if (!plugin) { + // plugin was deactivated while running. find the activity-slots it was using and make + // them available. + delete _this.runningPluginNames[runningPluginName]; + _this.unmarkSlotsForPluginName(runningPluginName); + } else { + var runningness = plugin.run(controllerData, deltaTime); + if (!runningness.active) { + // 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); } } } - - } catch (e) { - print("WARNING: handControllerGrab.js -- error parsing Hifi-Hand-RayPick-Blacklist message: " + message); } - } - }; + }; - this.cleanup = function () { - Script.update.disconnect(_this.update); - Controller.disableMapping(MAPPING_NAME); - RayPick.removeRayPick(_this.leftControllerRayPick); - RayPick.removeRayPick(_this.rightControllerRayPick); - RayPick.removeRayPick(_this.rightControllerHudRayPick); - RayPick.removeRayPick(_this.leftControllerHudRayPick); - }; + this.setBlacklist = function() { + RayPick.setIgnoreEntities(_this.leftControllerRayPick, this.blacklist); + RayPick.setIgnoreEntities(_this.rightControllerRayPick, this.blacklist); + + }; + + var MAPPING_NAME = "com.highfidelity.controllerDispatcher"; + var mapping = Controller.newMapping(MAPPING_NAME); + mapping.from([Controller.Standard.RT]).peek().to(_this.rightTriggerPress); + mapping.from([Controller.Standard.RTClick]).peek().to(_this.rightTriggerClick); + mapping.from([Controller.Standard.LT]).peek().to(_this.leftTriggerPress); + mapping.from([Controller.Standard.LTClick]).peek().to(_this.leftTriggerClick); + + mapping.from([Controller.Standard.RB]).peek().to(_this.rightSecondaryPress); + mapping.from([Controller.Standard.LB]).peek().to(_this.leftSecondaryPress); + mapping.from([Controller.Standard.LeftGrip]).peek().to(_this.leftSecondaryPress); + mapping.from([Controller.Standard.RightGrip]).peek().to(_this.rightSecondaryPress); + + Controller.enableMapping(MAPPING_NAME); + + this.leftControllerRayPick = RayPick.createRayPick({ + joint: "_CONTROLLER_LEFTHAND", + filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, + enabled: true, + maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE, + posOffset: getGrabPointSphereOffset(Controller.Standard.LeftHand) + }); + this.leftControllerHudRayPick = RayPick.createRayPick({ + joint: "_CONTROLLER_LEFTHAND", + filter: RayPick.PICK_HUD, + enabled: true, + maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE, + posOffset: getGrabPointSphereOffset(Controller.Standard.LeftHand) + }); + this.rightControllerRayPick = RayPick.createRayPick({ + joint: "_CONTROLLER_RIGHTHAND", + filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, + enabled: true, + maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE, + posOffset: getGrabPointSphereOffset(Controller.Standard.RightHand) + }); + this.rightControllerHudRayPick = RayPick.createRayPick({ + joint: "_CONTROLLER_RIGHTHAND", + filter: RayPick.PICK_HUD, + enabled: true, + maxDistance: DEFAULT_SEARCH_SPHERE_DISTANCE, + posOffset: getGrabPointSphereOffset(Controller.Standard.RightHand) + }); + + this.handleHandMessage = function(channel, message, sender) { + var data; + if (sender === MyAvatar.sessionUUID) { + try { + if (channel === 'Hifi-Hand-RayPick-Blacklist') { + data = JSON.parse(message); + var action = data.action; + var id = data.id; + var index = _this.blacklis.indexOf(id); + + if (action === 'add' && index === -1) { + _this.blacklist.push(id); + _this.setBlacklist(); + } + + if (action === 'remove') { + if (index > -1) { + _this.blacklist.splice(index, 1); + _this.setBlacklist(); + } + } + } + + } catch (e) { + print("WARNING: handControllerGrab.js -- error parsing Hifi-Hand-RayPick-Blacklist message: " + message); + } + } + }; + + this.cleanup = function () { + Script.update.disconnect(_this.update); + Controller.disableMapping(MAPPING_NAME); + RayPick.removeRayPick(_this.leftControllerRayPick); + RayPick.removeRayPick(_this.rightControllerRayPick); + RayPick.removeRayPick(_this.rightControllerHudRayPick); + RayPick.removeRayPick(_this.leftControllerHudRayPick); + }; + } + + var controllerDispatcher = new ControllerDispatcher(); Messages.subscribe('Hifi-Hand-RayPick-Blacklist'); - Messages.messageReceived.connect(this.handleHandMessage); - Script.scriptEnding.connect(this.cleanup); - Script.update.connect(this.update); + Messages.messageReceived.connect(controllerDispatcher.handleHandMessage); + Script.scriptEnding.connect(controllerDispatcher.cleanup); + Script.update.connect(controllerDispatcher.update); }()); diff --git a/scripts/system/controllers/controllerModules/disableOtherModule.js b/scripts/system/controllers/controllerModules/disableOtherModule.js index 65732dd08d..d6079ffafb 100644 --- a/scripts/system/controllers/controllerModules/disableOtherModule.js +++ b/scripts/system/controllers/controllerModules/disableOtherModule.js @@ -12,7 +12,7 @@ getEnabledModuleByName */ -Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllerDispatcherUtils.js"); (function() { function DisableModules(hand) { @@ -21,6 +21,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); this.parameters = makeDispatcherModuleParameters( 90, this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip", "rightHandTrigger"] : ["leftHand", "leftHandEquip", "leftHandTrigger"], + [], 100); this.isReady = function(controllerData) { diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index 40e0e19cf3..fa1321b168 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -14,7 +14,7 @@ */ Script.include("/~/system/libraries/Xform.js"); -Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); Script.include("/~/system/libraries/cloneEntityUtils.js"); diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 44360c2e39..56513ab766 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -17,7 +17,7 @@ */ -Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); (function() { diff --git a/scripts/system/controllers/controllerModules/farTrigger.js b/scripts/system/controllers/controllerModules/farTrigger.js index 671fc58c7d..38152aac91 100644 --- a/scripts/system/controllers/controllerModules/farTrigger.js +++ b/scripts/system/controllers/controllerModules/farTrigger.js @@ -16,7 +16,7 @@ */ -Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); (function() { diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index c6ac0c01bc..cf82de1820 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -14,7 +14,7 @@ isInEditMode */ -Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); Script.include("/~/system/libraries/utils.js"); @@ -94,6 +94,7 @@ Script.include("/~/system/libraries/utils.js"); this.parameters = makeDispatcherModuleParameters( 160, this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip", "rightHandTrigger"] : ["leftHand", "leftHandEquip", "leftHandTrigger"], + [], 100); this.nearTablet = function(overlays) { diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index 2b3a2405fb..7c856535ab 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -12,7 +12,7 @@ TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, findGroupParent, entityIsCloneable, propsAreCloneDynamic, cloneEntity */ -Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); Script.include("/~/system/libraries/cloneEntityUtils.js"); diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index 4a59fd727f..47f80932bb 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -13,7 +13,7 @@ findGroupParent, Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic */ -Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/cloneEntityUtils.js"); (function() { @@ -169,7 +169,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.targetEntityID = null; this.grabbing = false; - if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE) { + if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE && controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) { return makeRunningValues(false, [], []); } @@ -188,7 +188,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.run = function (controllerData, deltaTime) { if (this.grabbing) { - if (controllerData.triggerClicks[this.hand] === 0) { + if (controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0) { this.endNearParentingGrabEntity(); return makeRunningValues(false, [], []); } @@ -208,7 +208,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); if (!readiness.active) { return readiness; } - if (controllerData.triggerClicks[this.hand] === 1) { + if (controllerData.triggerClicks[this.hand] === 1 || controllerData.secondaryValues[this.hand] === 1) { // switch to grab var targetProps = this.getTargetProps(controllerData); var targetCloneable = entityIsCloneable(targetProps); diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js index 46ed8df271..9c8e6265ed 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -12,7 +12,7 @@ makeDispatcherModuleParameters, Overlays, makeRunningValues */ -Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllerDispatcherUtils.js"); var GRAB_RADIUS = 0.35; (function() { @@ -158,7 +158,7 @@ var GRAB_RADIUS = 0.35; this.isReady = function (controllerData) { - if (controllerData.triggerClicks[this.hand] === 0) { + if (controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0) { return makeRunningValues(false, [], []); } @@ -180,7 +180,7 @@ var GRAB_RADIUS = 0.35; }; this.run = function (controllerData) { - if (controllerData.triggerClicks[this.hand] === 0) { + if (controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0) { this.endNearParentingGrabOverlay(); return makeRunningValues(false, [], []); } else { diff --git a/scripts/system/controllers/controllerModules/nearTrigger.js b/scripts/system/controllers/controllerModules/nearTrigger.js index 4d31a8a22b..03fc7f8f64 100644 --- a/scripts/system/controllers/controllerModules/nearTrigger.js +++ b/scripts/system/controllers/controllerModules/nearTrigger.js @@ -11,7 +11,7 @@ TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, makeRunningValues, NEAR_GRAB_RADIUS */ -Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllerDispatcherUtils.js"); (function() { diff --git a/scripts/system/controllers/controllerModules/overlayLaserInput.js b/scripts/system/controllers/controllerModules/overlayLaserInput.js index 86623dbc72..4b17f90524 100644 --- a/scripts/system/controllers/controllerModules/overlayLaserInput.js +++ b/scripts/system/controllers/controllerModules/overlayLaserInput.js @@ -14,7 +14,7 @@ DISPATCHER_PROPERTIES */ -Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); (function() { diff --git a/scripts/system/controllers/controllerModules/tabletStylusInput.js b/scripts/system/controllers/controllerModules/tabletStylusInput.js index 9720bc8022..9e6062e3c3 100644 --- a/scripts/system/controllers/controllerModules/tabletStylusInput.js +++ b/scripts/system/controllers/controllerModules/tabletStylusInput.js @@ -11,7 +11,7 @@ AVATAR_SELF_ID, HMD, INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, Settings, getGrabPointSphereOffset */ -Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); (function() { diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/controllerModules/teleport.js similarity index 98% rename from scripts/system/controllers/teleport.js rename to scripts/system/controllers/controllerModules/teleport.js index 17ac36d955..fcf5ea0ed2 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/controllerModules/teleport.js @@ -17,7 +17,7 @@ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ Script.include("/~/system/libraries/Xform.js"); -Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); (function() { // BEGIN LOCAL_SCOPE @@ -27,9 +27,9 @@ var inTeleportMode = false; var SMOOTH_ARRIVAL_SPACING = 33; var NUMBER_OF_STEPS = 6; -var TARGET_MODEL_URL = Script.resolvePath("../assets/models/teleport-destination.fbx"); -var TOO_CLOSE_MODEL_URL = Script.resolvePath("../assets/models/teleport-cancel.fbx"); -var SEAT_MODEL_URL = Script.resolvePath("../assets/models/teleport-seat.fbx"); +var TARGET_MODEL_URL = Script.resolvePath("../../assets/models/teleport-destination.fbx"); +var TOO_CLOSE_MODEL_URL = Script.resolvePath("../../assets/models/teleport-cancel.fbx"); +var SEAT_MODEL_URL = Script.resolvePath("../../assets/models/teleport-seat.fbx"); var TARGET_MODEL_DIMENSIONS = { x: 1.15, diff --git a/scripts/system/controllers/controllerModules/webEntityLaserInput.js b/scripts/system/controllers/controllerModules/webEntityLaserInput.js index 747e1bae44..1e954d5917 100644 --- a/scripts/system/controllers/controllerModules/webEntityLaserInput.js +++ b/scripts/system/controllers/controllerModules/webEntityLaserInput.js @@ -16,7 +16,7 @@ */ -Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); (function() { diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 569fad4855..e3375a5a27 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -28,7 +28,7 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/inEditMode.js", "controllerModules/disableOtherModule.js", "controllerModules/farTrigger.js", - "teleport.js" + "controllerModules/teleport.js" ]; var DEBUG_MENU_ITEM = "Debug defaultScripts.js"; diff --git a/scripts/system/controllers/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js similarity index 100% rename from scripts/system/controllers/controllerDispatcherUtils.js rename to scripts/system/libraries/controllerDispatcherUtils.js From f0c12e50dd36588de651ff4e34a5500201d23c1e Mon Sep 17 00:00:00 2001 From: druiz17 Date: Fri, 8 Sep 2017 11:50:19 -0700 Subject: [PATCH 47/59] some small bug fixes --- .../controllers/controllerModules/nearActionGrabEntity.js | 6 +++--- .../controllers/controllerModules/nearParentGrabOverlay.js | 3 ++- .../controllers/controllerModules/overlayLaserInput.js | 5 ++++- scripts/system/libraries/cloneEntityUtils.js | 3 ++- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index 7c856535ab..932cbda179 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -167,7 +167,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.isReady = function (controllerData) { this.targetEntityID = null; - if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE) { + if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE && controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) { return makeRunningValues(false, [], []); } @@ -186,7 +186,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.run = function (controllerData) { if (this.actionID) { - if (controllerData.triggerClicks[this.hand] === 0) { + if (controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0) { this.endNearGrabAction(); return makeRunningValues(false, [], []); } @@ -204,7 +204,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); var targetProps = this.getTargetProps(controllerData); if (targetProps) { - if (controllerData.triggerClicks[this.hand] === 1) { + if (controllerData.triggerClicks[this.hand] === 1 || controllerData.secondaryValues[this.hand] === 1) { // switch to grabbing var targetCloneable = entityIsCloneable(targetProps); if (targetCloneable) { diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js index 9c8e6265ed..54257ea6af 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -128,7 +128,8 @@ var GRAB_RADIUS = 0.35; }; this.endNearParentingGrabOverlay = function () { - if (this.previousParentID[this.grabbedThingID] === NULL_UUID) { + var previousParentID = this.previousParentID[this.grabbedThingID]; + if (previousParentID === NULL_UUID || previousParentID === null || previousParentID === undefined) { Overlays.editOverlay(this.grabbedThingID, { parentID: NULL_UUID, parentJointIndex: -1 diff --git a/scripts/system/controllers/controllerModules/overlayLaserInput.js b/scripts/system/controllers/controllerModules/overlayLaserInput.js index 4b17f90524..71fc31a500 100644 --- a/scripts/system/controllers/controllerModules/overlayLaserInput.js +++ b/scripts/system/controllers/controllerModules/overlayLaserInput.js @@ -429,8 +429,11 @@ Script.include("/~/system/libraries/controllers.js"); this.shouldExit = function(controllerData) { var intersection = controllerData.rayPicks[this.hand]; + var nearGrabName = this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay"; + var nearGrabModule = getEnabledModuleByName(nearGrabName); + var status = nearGrabModule ? nearGrabModule.isReady(controllerData) : makeRunningValues(false, [], []); var offOverlay = (intersection.type !== RayPick.INTERSECTED_OVERLAY); - return offOverlay; + return offOverlay || status.active; }; this.exitModule = function() { diff --git a/scripts/system/libraries/cloneEntityUtils.js b/scripts/system/libraries/cloneEntityUtils.js index a8e8bc227d..26d8051f89 100644 --- a/scripts/system/libraries/cloneEntityUtils.js +++ b/scripts/system/libraries/cloneEntityUtils.js @@ -72,6 +72,7 @@ cloneEntity = function(props, worldEntityProps) { cloneableProps.name = cloneableProps.name + '-clone-' + cloneableProps.id; var lifetime = grabInfo.cloneLifetime ? grabInfo.cloneLifetime : 300; var dynamic = grabInfo.cloneDynamic ? grabInfo.cloneDynamic : false; + var triggerable = grabInfo.triggerable ? grabInfo.triggerable : false; var cUserData = Object.assign({}, JSON.parse(cloneableProps.userData)); var cProperties = Object.assign({}, cloneableProps); @@ -85,7 +86,7 @@ cloneEntity = function(props, worldEntityProps) { cProperties.dynamic = dynamic; cProperties.locked = false; - cUserData.grabbableKey.triggerable = true; + cUserData.grabbableKey.triggerable = triggerable; cUserData.grabbableKey.grabbable = true; cProperties.lifetime = lifetime; cProperties.userData = JSON.stringify(cUserData); From e33012e522fa83dafa052e7eacbc9d32bfae487d Mon Sep 17 00:00:00 2001 From: druiz17 Date: Fri, 8 Sep 2017 13:30:30 -0700 Subject: [PATCH 48/59] laser on tablet update --- .../system/controllers/controllerModules/overlayLaserInput.js | 3 ++- .../system/controllers/controllerModules/tabletStylusInput.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/controllerModules/overlayLaserInput.js b/scripts/system/controllers/controllerModules/overlayLaserInput.js index 71fc31a500..b1ffc77afe 100644 --- a/scripts/system/controllers/controllerModules/overlayLaserInput.js +++ b/scripts/system/controllers/controllerModules/overlayLaserInput.js @@ -433,7 +433,8 @@ Script.include("/~/system/libraries/controllers.js"); var nearGrabModule = getEnabledModuleByName(nearGrabName); var status = nearGrabModule ? nearGrabModule.isReady(controllerData) : makeRunningValues(false, [], []); var offOverlay = (intersection.type !== RayPick.INTERSECTED_OVERLAY); - return offOverlay || status.active; + var triggerOff = (controllerData.triggerValues[this.hand] === 0); + return offOverlay || status.active || triggerOff; }; this.exitModule = function() { diff --git a/scripts/system/controllers/controllerModules/tabletStylusInput.js b/scripts/system/controllers/controllerModules/tabletStylusInput.js index 9e6062e3c3..a70b9867d8 100644 --- a/scripts/system/controllers/controllerModules/tabletStylusInput.js +++ b/scripts/system/controllers/controllerModules/tabletStylusInput.js @@ -20,7 +20,7 @@ Script.include("/~/system/libraries/controllers.js"); var HAPTIC_STYLUS_STRENGTH = 1.0; var HAPTIC_STYLUS_DURATION = 20.0; - var WEB_DISPLAY_STYLUS_DISTANCE = 0.5; + var WEB_DISPLAY_STYLUS_DISTANCE = 0.2; var WEB_STYLUS_LENGTH = 0.2; var WEB_TOUCH_Y_OFFSET = 0.05; // how far forward (or back with a negative number) to slide stylus in hand From 5d1a5eea4c48872379afda0b7f08f961598a460f Mon Sep 17 00:00:00 2001 From: druiz17 Date: Fri, 8 Sep 2017 13:45:45 -0700 Subject: [PATCH 49/59] added test file --- .../developer/tests/controllerTableTest.js | 278 ++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 scripts/developer/tests/controllerTableTest.js diff --git a/scripts/developer/tests/controllerTableTest.js b/scripts/developer/tests/controllerTableTest.js new file mode 100644 index 0000000000..239c7fd0ce --- /dev/null +++ b/scripts/developer/tests/controllerTableTest.js @@ -0,0 +1,278 @@ +"use strict"; + +/* jslint bitwise: true */ +/* global Script, Entities, MyAvatar, Vec3, Quat, Mat4 */ + +(function() { // BEGIN LOCAL_SCOPE + + // var lifetime = -1; + var lifetime = 600; + var tableSections = 32; + var tableRadius = 9; + + var sectionRelativeRotation = 0; + var sectionRotation = 0; + var sectionRelativeCenterA = 0; + var sectionRelativeCenterB = 0; + var sectionRelativeCenterSign = 0; + var sectionCenterA = 0; + var sectionCenterB = 0; + var sectionCenterSign = 0; + var yFlip = 0; + + var objects = []; + var overlays = []; + + var testNames = [ + "FarActionGrab", + "NearParentGrabEntity", + "NearParentGrabOverlay", + "Clone Entity (dynamic)", + "Clone Entity (non-dynamic" + ]; + + function createCloneDynamicEntity(index) { + createPropsCube(index, false, false, true, true); + createPropsModel(index, false, false, true, true); + createSign(index, "Clone Dynamic Entity"); + }; + + function createCloneEntity(index) { + createPropsCube(index, false, false, true, false); + createPropsModel(index, false, false, true, false); + createSign(index, "Clone Non-Dynamic Entity"); + }; + + function createNearGrabOverlay(index) { + createPropsCubeOverlay(index, false, false, true, true); + createPropsModelOverlay(index, false, false, true, true); + createSign(index, "Near Grab Overlay"); + }; + + function createNearGrabEntity(index) { + createPropsCube(index, false, false, false, false); + createPropsModel(index, false, false, false, false); + createSign(index, "Near Grab Entity"); + }; + + function createFarGrabEntity(index) { + createPropsCube(index, true, false, false, false); + createPropsModel(index, true, false, false, false); + createSign(index, "Far Grab Entity"); + }; + + function createPropsModel(i, dynamic, collisionless, clone, cloneDynamic) { + var propsModel = { + name: "controller-tests model object " + i, + type: "Model", + modelURL: "http://headache.hungry.com/~seth/hifi/controller-tests/color-cube.obj", + + position: sectionCenterA, + rotation: sectionRotation, + + gravity: (dynamic && !collisionless) ? { x: 0, y: -1, z: 0 } : { x: 0, y: 0, z: 0 }, + dimensions: { x: 0.2, y: 0.2, z: 0.2 }, + userData: JSON.stringify({ + grabbableKey: { + grabbable: true, + cloneLimit: 10, + cloneable: clone, + cloneDynamic: cloneDynamic + }, + controllerTestEntity: true + }), + lifetime: lifetime, + shapeType: "box", + dynamic: dynamic, + collisionless: collisionless + }; + objects.push(Entities.addEntity(propsModel)); + } + + function createPropsModelOverlay(i, dynamic, collisionless, clone, cloneDynamic) { + var propsModel = { + name: "controller-tests model object " + i, + type: "Model", + modelURL: "http://headache.hungry.com/~seth/hifi/controller-tests/color-cube.obj", + url: "http://headache.hungry.com/~seth/hifi/controller-tests/color-cube.obj", + grabbable: true, + position: sectionCenterA, + rotation: sectionRotation, + dimensions: { x: 0.2, y: 0.2, z: 0.2 }, + userData: JSON.stringify({ + grabbableKey: { + grabbable: true, + }, + controllerTestEntity: true + }), + lifetime: lifetime, + visible: true, + }; + overlays.push(Overlays.addOverlay("model", propsModel)); + } + + + function createPropsCubeOverlay(i, dynamic, collisionless, clone, cloneDynamic) { + var propsCube = { + name: "controller-tests cube object " + i, + type: "Box", + color: { "blue": 200, "green": 10, "red": 20 }, + position: sectionCenterB, + rotation: sectionRotation, + grabbable: true, + dimensions: { x: 0.2, y: 0.2, z: 0.2 }, + userData: JSON.stringify({ + grabbableKey: { + grabbable: true, + }, + controllerTestEntity: true + }), + lifetime: lifetime, + solid: true, + visible: true, + }; + overlays.push(Overlays.addOverlay("cube", propsCube)); + } + + function createPropsCube(i, dynamic, collisionless, clone, cloneDynamic) { + var propsCube = { + name: "controller-tests cube object " + i, + type: "Box", + shape: "Cube", + color: { "blue": 200, "green": 10, "red": 20 }, + position: sectionCenterB, + rotation: sectionRotation, + gravity: dynamic ? { x: 0, y: -1, z: 0 } : { x: 0, y: 0, z: 0 }, + dimensions: { x: 0.2, y: 0.2, z: 0.2 }, + userData: JSON.stringify({ + grabbableKey: { + grabbable: true, + cloneLimit: 10, + cloneable: clone, + cloneDynamic: cloneDynamic + }, + controllerTestEntity: true + }), + lifetime: lifetime, + shapeType: "box", + dynamic: dynamic, + collisionless: collisionless + }; + objects.push(Entities.addEntity(propsCube)); + } + + function createSign(i, signText) { + var propsLabel = { + name: "controller-tests sign " + i, + type: "Text", + lineHeight: 0.125, + position: sectionCenterSign, + rotation: Quat.multiply(sectionRotation, yFlip), + text: signText, + dimensions: { x: 1, y: 1, z: 0.01 }, + lifetime: lifetime, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false, + }, + controllerTestEntity: true + }) + }; + objects.push(Entities.addEntity(propsLabel)); + } + + function chooseType(index) { + switch (index) { + case 0: + createFarGrabEntity(index); + break; + case 1: + createNearGrabEntity(index); + break; + case 2: + createNearGrabOverlay(index); + break; + case 3: + createCloneDynamicEntity(); + break; + case 4: + createCloneEntity(index); + break; + } + } + + function setupControllerTests(testBaseTransform) { + // var tableID = + objects.push(Entities.addEntity({ + name: "controller-tests table", + type: "Model", + modelURL: "http://headache.hungry.com/~seth/hifi/controller-tests/controller-tests-table.obj.gz", + position: Mat4.transformPoint(testBaseTransform, { x: 0, y: 1, z: 0 }), + rotation: Mat4.extractRotation(testBaseTransform), + userData: JSON.stringify({ + grabbableKey: { grabbable: false }, + soundKey: { + url: "http://headache.hungry.com/~seth/hifi/sound/clock-ticking-3.wav", + volume: 0.4, + loop: true, + playbackGap: 0, + playbackGapRange: 0 + }, + controllerTestEntity: true + }), + shapeType: "static-mesh", + lifetime: lifetime + })); + + var Xdynamic = 1; + var Xcollisionless = 2; + var Xkinematic = 4; + var XignoreIK = 8; + + yFlip = Quat.fromPitchYawRollDegrees(0, 180, 0); + + for (var i = 0; i < 16; i++) { + sectionRelativeRotation = Quat.fromPitchYawRollDegrees(0, -360 * i / tableSections, 0); + sectionRotation = Quat.multiply(Mat4.extractRotation(testBaseTransform), sectionRelativeRotation); + sectionRelativeCenterA = Vec3.multiplyQbyV(sectionRotation, { x: -0.2, y: 1.25, z: tableRadius - 0.8 }); + sectionRelativeCenterB = Vec3.multiplyQbyV(sectionRotation, { x: 0.2, y: 1.25, z: tableRadius - 0.8 }); + sectionRelativeCenterSign = Vec3.multiplyQbyV(sectionRotation, { x: 0, y: 1.5, z: tableRadius + 1.0 }); + sectionCenterA = Mat4.transformPoint(testBaseTransform, sectionRelativeCenterA); + sectionCenterB = Mat4.transformPoint(testBaseTransform, sectionRelativeCenterB); + sectionCenterSign = Mat4.transformPoint(testBaseTransform, sectionRelativeCenterSign); + + var dynamic = (i & Xdynamic) ? true : false; + var collisionless = (i & Xcollisionless) ? true : false; + var kinematic = (i & Xkinematic) ? true : false; + var ignoreIK = (i & XignoreIK) ? true : false; + + chooseType(i); + } + } + + // This assumes the avatar is standing on a flat floor with plenty of space. + // Find the floor: + var pickRay = { + origin: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 2, z: -1 })), + direction: { x: 0, y: -1, z: 0 }, + length: 20 + }; + var intersection = Entities.findRayIntersection(pickRay, true, [], [], true); + + if (intersection.intersects) { + var testBaseTransform = Mat4.createFromRotAndTrans(MyAvatar.rotation, intersection.intersection); + setupControllerTests(testBaseTransform); + } + + Script.scriptEnding.connect(function () { + for (var i = 0; i < objects.length; i++) { + var nearbyID = objects[i]; + Entities.deleteEntity(nearbyID); + } + + for (var i = 0; i < overlays.length; i++) { + var overlayID = overlays[i]; + Overlays.deleteOverlay(overlayID); + } + }); +}()); // END LOCAL_SCOPE From 42ef397b5e4d6f120fa32c6751eac23022b806f8 Mon Sep 17 00:00:00 2001 From: druiz17 Date: Fri, 8 Sep 2017 15:13:19 -0700 Subject: [PATCH 50/59] fixed include path --- scripts/system/controllers/controllerDispatcher.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 43ad2ccf51..759d9c8f31 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -18,7 +18,7 @@ controllerDispatcherPluginsNeedSort = false; Script.include("/~/system/libraries/utils.js"); Script.include("/~/system/libraries/controllers.js"); -Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllerDispatcherUtils.js"); (function() { var NEAR_MAX_RADIUS = 1.0; From a1ffc7abd01cc59a848310aa450e8634a9d5983c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 8 Sep 2017 17:04:09 -0700 Subject: [PATCH 51/59] distance from controller to object is sometimes decided by pickray --- scripts/system/controllers/controllerDispatcher.js | 6 +++--- .../controllers/controllerModules/nearActionGrabEntity.js | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 759d9c8f31..c52b3d56df 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -205,6 +205,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); var entityID = nearbyEntityIDs[j]; var props = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); props.id = entityID; + props.distance = Vec3.distance(props.position, controllerLocations[h].position) nearbyEntityPropertiesByID[entityID] = props; nearbyEntityProperties[h].push(props); } @@ -236,6 +237,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); var nearEntityID = rayPicks[h].objectID; var nearbyProps = Entities.getEntityProperties(nearEntityID, DISPATCHER_PROPERTIES); nearbyProps.id = nearEntityID; + nearbyProps.distance = rayPicks[h].distance; nearbyEntityPropertiesByID[nearEntityID] = nearbyProps; nearbyEntityProperties[h].push(nearbyProps); } @@ -243,9 +245,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); // sort by distance from each hand nearbyEntityProperties[h].sort(function (a, b) { - var aDistance = Vec3.distance(a.position, controllerLocations[h].position); - var bDistance = Vec3.distance(b.position, controllerLocations[h].position); - return aDistance - bDistance; + return a.distance - b.distance; }); } diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index 932cbda179..d76de2b428 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -148,8 +148,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); for (var i = 0; i < nearbyEntityProperties.length; i++) { var props = nearbyEntityProperties[i]; var handPosition = controllerData.controllerLocations[this.hand].position; - var distance = Vec3.distance(props.position, handPosition); - if (distance > NEAR_GRAB_RADIUS) { + if (props.distance > NEAR_GRAB_RADIUS) { break; } if (entityIsGrabbable(props)) { From 405d8c3afc335028d9ef1043b6a86620808a1f7b Mon Sep 17 00:00:00 2001 From: druiz17 Date: Fri, 8 Sep 2017 17:07:27 -0700 Subject: [PATCH 52/59] fixing cloning and laser --- .../controllers/controllerModules/farActionGrabEntity.js | 6 +++--- .../controllers/controllerModules/nearActionGrabEntity.js | 4 ++-- .../controllers/controllerModules/tabletStylusInput.js | 2 +- scripts/system/libraries/cloneEntityUtils.js | 1 - 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 56513ab766..3355901e6e 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -137,7 +137,7 @@ Script.include("/~/system/libraries/controllers.js"); var dim = {x: radius, y: radius, z: radius}; var mode = "hold"; if (!this.distanceHolding && !this.distanceRotating) { - if (controllerData.triggerClicks[this.hand]) { + if (controllerData.triggerValues[this.hand] === 1) { mode = "full"; } else { mode = "half"; @@ -367,8 +367,6 @@ Script.include("/~/system/libraries/controllers.js"); otherFarGrabModule.offsetPosition = Vec3.multiplyQbyV(controllerRotationDelta, otherFarGrabModule.offsetPosition); - this.updateLaserPointer(); - this.previousWorldControllerRotation = worldControllerRotation; }; @@ -434,6 +432,8 @@ Script.include("/~/system/libraries/controllers.js"); return makeRunningValues(false, [], []); } + this.updateLaserPointer(controllerData); + var otherModuleName =this.hand === RIGHT_HAND ? "LeftFarActionGrabEntity" : "RightFarActionGrabEntity"; var otherFarGrabModule = getEnabledModuleByName(otherModuleName); diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index 932cbda179..802540af17 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -152,7 +152,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); if (distance > NEAR_GRAB_RADIUS) { break; } - if (entityIsGrabbable(props)) { + if (entityIsGrabbable(props) || entityIsCloneable(props)) { // if we've attempted to grab a child, roll up to the root of the tree var groupRootProps = findGroupParent(controllerData, props); if (entityIsGrabbable(groupRootProps)) { @@ -173,7 +173,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); var targetProps = this.getTargetProps(controllerData); if (targetProps) { - if (!propsArePhysical(targetProps) && !propsAreCloneDynamic) { + if (!propsArePhysical(targetProps) && !propsAreCloneDynamic(targetProps)) { return makeRunningValues(false, [], []); // let nearParentGrabEntity handle it } else { this.targetEntityID = targetProps.id; diff --git a/scripts/system/controllers/controllerModules/tabletStylusInput.js b/scripts/system/controllers/controllerModules/tabletStylusInput.js index a70b9867d8..8a1262bac0 100644 --- a/scripts/system/controllers/controllerModules/tabletStylusInput.js +++ b/scripts/system/controllers/controllerModules/tabletStylusInput.js @@ -20,7 +20,7 @@ Script.include("/~/system/libraries/controllers.js"); var HAPTIC_STYLUS_STRENGTH = 1.0; var HAPTIC_STYLUS_DURATION = 20.0; - var WEB_DISPLAY_STYLUS_DISTANCE = 0.2; + var WEB_DISPLAY_STYLUS_DISTANCE = 0.1; var WEB_STYLUS_LENGTH = 0.2; var WEB_TOUCH_Y_OFFSET = 0.05; // how far forward (or back with a negative number) to slide stylus in hand diff --git a/scripts/system/libraries/cloneEntityUtils.js b/scripts/system/libraries/cloneEntityUtils.js index 26d8051f89..500c6a0696 100644 --- a/scripts/system/libraries/cloneEntityUtils.js +++ b/scripts/system/libraries/cloneEntityUtils.js @@ -36,7 +36,6 @@ entityIsCloneable = function(props) { var grabbableData = getGrabbableData(props); return grabbableData.cloneable; } - return false; }; From a46e41607cc658ac510e452aa4af4c549b0b8b23 Mon Sep 17 00:00:00 2001 From: druiz17 Date: Mon, 11 Sep 2017 15:07:14 -0700 Subject: [PATCH 53/59] fixed teleport, added avatar scaling, allow laser to be used on tablet when stylus is shown --- .../controllerModules/farActionGrabEntity.js | 1 + .../controllerModules/inEditMode.js | 11 +++ .../controllerModules/scaleAvatar.js | 84 +++++++++++++++++++ .../controllerModules/tabletStylusInput.js | 12 ++- .../controllers/controllerModules/teleport.js | 29 ++++++- .../system/controllers/controllerScripts.js | 3 +- .../libraries/controllerDispatcherUtils.js | 11 +++ 7 files changed, 145 insertions(+), 6 deletions(-) create mode 100644 scripts/system/controllers/controllerModules/scaleAvatar.js diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 3355901e6e..9a6f39c490 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -439,6 +439,7 @@ Script.include("/~/system/libraries/controllers.js"); // gather up the readiness of the near-grab modules var nearGrabNames = [ + this.hand === RIGHT_HAND ? "RightScaleAvatar" : "LeftScaleAvatar", this.hand === RIGHT_HAND ? "RightFarTriggerEntity" : "LeftFarTriggerEntity", this.hand === RIGHT_HAND ? "RightNearActionGrabEntity" : "LeftNearActionGrabEntity", this.hand === RIGHT_HAND ? "RightNearParentingGrabEntity" : "LeftNearParentingGrabEntity", diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index cf82de1820..916487277a 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -209,6 +209,15 @@ Script.include("/~/system/libraries/utils.js"); return this.exitModule(); } } + + var teleport = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightTeleporter" : "LeftTeleporter"); + if (teleport) { + var teleportReady = teleport.isReady(controllerData); + if (teleportReady.active) { + return this.exitModule(); + } + } + this.processControllerTriggers(controllerData); this.updateLaserPointer(controllerData); this.sendPickData(controllerData); @@ -251,4 +260,6 @@ Script.include("/~/system/libraries/utils.js"); disableDispatcherModule("LeftHandInEditMode"); disableDispatcherModule("RightHandInEditMode"); }; + + Script.scriptEnding.connect(this.cleanup); }()); diff --git a/scripts/system/controllers/controllerModules/scaleAvatar.js b/scripts/system/controllers/controllerModules/scaleAvatar.js new file mode 100644 index 0000000000..2c85b75ea9 --- /dev/null +++ b/scripts/system/controllers/controllerModules/scaleAvatar.js @@ -0,0 +1,84 @@ +// handControllerGrab.js +// +// Created by Dante Ruiz on 9/11/17 +// +// Grabs physically moveable entities with hydra-like controllers; it works for either near or far objects. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +/* global getEntityCustomData, flatten, Xform, Script, Quat, Vec3, MyAvatar, Entities, Overlays, Settings, + Reticle, Controller, Camera, Messages, Mat4, getControllerWorldLocation, getGrabPointSphereOffset, + setGrabCommunications, Menu, HMD, isInEditMode, AvatarList */ +/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ + +//Script.include("/~/system/libraries/controllerDispatcherUtils.js"); +(function () { + var dispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); + + function ScaleAvatar(hand) { + this.hand = hand; + this.scalingStartAvatarScale = 0; + this.scalingStartDistance = 0; + + this.parameters = dispatcherUtils.makeDispatcherModuleParameters( + 120, + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100 + ); + + this.otherHand = function() { + return this.hand === dispatcherUtils.RIGHT_HAND ? dispatcherUtils.LEFT_HAND : dispatcherUtils.RIGHT_HAND; + }; + + this.getOtherModule = function() { + var otherModule = this.hand === dispatcherUtils.RIGHT_HAND ? leftScaleAvatar : rightScaleAvatar; + return otherModule; + }; + + this.triggersPressed = function(controllerData) { + if (controllerData.triggerValues[this.hand] === 1 && controllerData.secondaryValues[this.hand] === 1) { + return true; + } + return false; + }; + + this.isReady = function(controllerData) { + var otherModule = this.getOtherModule(); + if (this.triggersPressed(controllerData) && otherModule.triggersPressed(controllerData)) { + this.scalingStartAvatarScale = MyAvatar.scale; + this.scalingStartDistance = Vec3.length(Vec3.subtract(controllerData.controllerLocations[this.hand].position, + controllerData.controllerLocations[this.otherHand()].position)); + return dispatcherUtils.makeRunningValues(true, [], []); + } + return dispatcherUtils.makeRunningValues(false, [], []); + }; + + this.run = function(controllerData) { + var otherModule = this.getOtherModule(); + if (this.triggersPressed(controllerData) && otherModule.triggersPressed(controllerData)) { + if (this.hand === dispatcherUtils.RIGHT_HAND) { + var scalingCurrentDistance = Vec3.length(Vec3.subtract(controllerData.controllerLocations[this.hand].position, + controllerData.controllerLocations[this.otherHand()].position)); + + var newAvatarScale = (scalingCurrentDistance / this.scalingStartDistance) * this.scalingStartAvatarScale; + MyAvatar.scale = newAvatarScale; + } + return dispatcherUtils.makeRunningValues(true, [], []); + } + return dispatcherUtils.makeRunningValues(false, [], []); + }; + } + + var leftScaleAvatar = new ScaleAvatar(dispatcherUtils.LEFT_HAND); + var rightScaleAvatar = new ScaleAvatar(dispatcherUtils.RIGHT_HAND); + + dispatcherUtils.enableDispatcherModule("LeftScaleAvatar", leftScaleAvatar); + dispatcherUtils.enableDispatcherModule("RightScaleAvatar", rightScaleAvatar); + + this.cleanup = function() { + dispatcherUtils.disableDispatcherModule("LeftScaleAvatar"); + dispatcherUtils.disableDispatcherModule("RightScaleAvatar"); + }; +})(); diff --git a/scripts/system/controllers/controllerModules/tabletStylusInput.js b/scripts/system/controllers/controllerModules/tabletStylusInput.js index 8a1262bac0..0d4f4ff78a 100644 --- a/scripts/system/controllers/controllerModules/tabletStylusInput.js +++ b/scripts/system/controllers/controllerModules/tabletStylusInput.js @@ -20,7 +20,7 @@ Script.include("/~/system/libraries/controllers.js"); var HAPTIC_STYLUS_STRENGTH = 1.0; var HAPTIC_STYLUS_DURATION = 20.0; - var WEB_DISPLAY_STYLUS_DISTANCE = 0.1; + var WEB_DISPLAY_STYLUS_DISTANCE = 0.5; var WEB_STYLUS_LENGTH = 0.2; var WEB_TOUCH_Y_OFFSET = 0.05; // how far forward (or back with a negative number) to slide stylus in hand @@ -474,7 +474,7 @@ Script.include("/~/system/libraries/controllers.js"); this.processStylus = function(controllerData) { this.updateStylusTip(); - if (!this.stylusTip.valid) { + if (!this.stylusTip.valid || this.overlayLaserActive(controllerData)) { this.pointFinger(false); this.hideStylus(); return false; @@ -646,6 +646,14 @@ Script.include("/~/system/libraries/controllers.js"); } }; + this.overlayLaserActive = function(controllerData) { + var overlayLaserModule = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightOverlayLaserInput" : "LeftOverlayLaserInput"); + if (overlayLaserModule) { + return overlayLaserModule.isReady(controllerData).active; + } + return false; + }; + this.isReady = function (controllerData) { if (this.processStylus(controllerData)) { return makeRunningValues(true, [], []); diff --git a/scripts/system/controllers/controllerModules/teleport.js b/scripts/system/controllers/controllerModules/teleport.js index fcf5ea0ed2..9bb3bcb2f9 100644 --- a/scripts/system/controllers/controllerModules/teleport.js +++ b/scripts/system/controllers/controllerModules/teleport.js @@ -170,6 +170,11 @@ function Teleporter(hand) { this.currentTarget = TARGET.INVALID; this.currentResult = null; + this.getOtherModule = function() { + var otherModule = this.hand === RIGHT_HAND ? leftTeleporter : rightTeleporter; + return otherModule; + }; + this.teleportRayHandVisible = LaserPointers.createLaserPointer({ joint: (_this.hand === RIGHT_HAND) ? "RightHand" : "LeftHand", filter: RayPick.PICK_ENTITIES, @@ -231,15 +236,32 @@ function Teleporter(hand) { [], 100); + this.enterTeleport = function() { + if (coolInTimeout !== null) { + Script.clearTimeout(coolInTimeout); + } + + this.state = TELEPORTER_STATES.COOL_IN; + coolInTimeout = Script.setTimeout(function() { + if (_this.state === TELEPORTER_STATES.COOL_IN) { + _this.state = TELEPORTER_STATES.TARGETTING; + } + }, COOL_IN_DURATION); + }; + + this.isReady = function(controllerData, deltaTime) { - if (_this.buttonValue !== 0) { + var otherModule = this.getOtherModule(); + if (_this.buttonValue !== 0 && !otherModule.active) { + this.active = true; + this.enterTeleport(); return makeRunningValues(true, [], []); } return makeRunningValues(false, [], []); }; this.run = function(controllerData, deltaTime) { - _this.state = TELEPORTER_STATES.TARGETTING; + //_this.state = TELEPORTER_STATES.TARGETTING; // Get current hand pose information to see if the pose is valid var pose = Controller.getPoseValue(handInfo[(_this.hand === RIGHT_HAND) ? 'right' : 'left'].controllerInput); @@ -306,7 +328,7 @@ function Teleporter(hand) { return makeRunningValues(true, [], []); } - if (target === TARGET.NONE || target === TARGET.INVALID) { + if (target === TARGET.NONE || target === TARGET.INVALID || this.state === TELEPORTER_STATES.COOL_IN) { // Do nothing } else if (target === TARGET.SEAT) { Entities.callEntityMethod(result.objectID, 'sit'); @@ -319,6 +341,7 @@ function Teleporter(hand) { } this.disableLasers(); + this.active = false; return makeRunningValues(false, [], []); }; diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index e3375a5a27..3fcc158893 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -28,7 +28,8 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/inEditMode.js", "controllerModules/disableOtherModule.js", "controllerModules/farTrigger.js", - "controllerModules/teleport.js" + "controllerModules/teleport.js", + "controllerModules/scaleAvatar.js" ]; var DEBUG_MENU_ITEM = "Debug defaultScripts.js"; diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index 773f79754d..bc2aa1a9c8 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -301,3 +301,14 @@ findGroupParent = function (controllerData, targetProps) { return targetProps; }; + +if (typeof module !== 'undefined') { + module.exports = { + makeDispatcherModuleParameters: makeDispatcherModuleParameters, + enableDispatcherModule: enableDispatcherModule, + disableDispatcherModule: disableDispatcherModule, + makeRunningValues: makeRunningValues, + LEFT_HAND: LEFT_HAND, + RIGHT_HAND: RIGHT_HAND + }; +} From 2850029f2620fe393f8b08b90563f09c6e9c3a12 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 12 Sep 2017 10:00:32 -0700 Subject: [PATCH 54/59] add profiling to controller-dispatcher --- .../controllers/controllerDispatcher.js | 51 ++++++++++++++++--- .../libraries/controllerDispatcherUtils.js | 3 +- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index c52b3d56df..f4567bd295 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -8,9 +8,9 @@ /* jslint bitwise: true */ /* global Script, Entities, Overlays, Controller, Vec3, Quat, getControllerWorldLocation, RayPick, - controllerDispatcherPlugins:true, controllerDispatcherPluginsNeedSort:true, entityIsGrabbable:true, + controllerDispatcherPlugins:true, controllerDispatcherPluginsNeedSort:true, LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS, DEFAULT_SEARCH_SPHERE_DISTANCE, DISPATCHER_PROPERTIES, - getGrabPointSphereOffset + getGrabPointSphereOffset, HMD, MyAvatar, Messages */ controllerDispatcherPlugins = {}; @@ -26,8 +26,10 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); var TARGET_UPDATE_HZ = 60; // 50hz good enough, but we're using update var BASIC_TIMER_INTERVAL_MS = 1000 / TARGET_UPDATE_HZ; + var PROFILE = true; + function ControllerDispatcher() { - var _this = this + var _this = this; this.lastInterval = Date.now(); this.intervalCount = 0; this.totalDelta = 0; @@ -141,6 +143,9 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); }; this.update = function () { + if (PROFILE) { + Script.beginProfileRange("dispatch.pre"); + } var deltaTime = _this.updateTimings(); _this.setIgnoreTablet(); @@ -156,7 +161,6 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); controllerDispatcherPlugins[b].parameters.priority; }); - // print("controllerDispatcher -- new plugin order: " + JSON.stringify(this.orderedPluginNames)); var output = "controllerDispatcher -- new plugin order: "; for (var k = 0; k < _this.orderedPluginNames.length; k++) { var dbgPluginName = _this.orderedPluginNames[k]; @@ -170,6 +174,14 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); controllerDispatcherPluginsNeedSort = false; } + if (PROFILE) { + Script.endProfileRange("dispatch.pre"); + } + + if (PROFILE) { + Script.beginProfileRange("dispatch.gather"); + } + var controllerLocations = [ _this.dataGatherers.leftControllerLocation(), _this.dataGatherers.rightControllerLocation() @@ -205,7 +217,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); var entityID = nearbyEntityIDs[j]; var props = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); props.id = entityID; - props.distance = Vec3.distance(props.position, controllerLocations[h].position) + props.distance = Vec3.distance(props.position, controllerLocations[h].position); nearbyEntityPropertiesByID[entityID] = props; nearbyEntityProperties[h].push(props); } @@ -261,13 +273,22 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); rayPicks: rayPicks, hudRayPicks: hudRayPicks }; + if (PROFILE) { + Script.endProfileRange("dispatch.gather"); + } + if (PROFILE) { + Script.beginProfileRange("dispatch.isReady"); + } // check for plugins that would like to start. ask in order of increasing priority value for (var pluginIndex = 0; pluginIndex < _this.orderedPluginNames.length; pluginIndex++) { var orderedPluginName = _this.orderedPluginNames[pluginIndex]; var candidatePlugin = controllerDispatcherPlugins[orderedPluginName]; if (_this.slotsAreAvailableForPlugin(candidatePlugin)) { + if (PROFILE) { + Script.beginProfileRange("dispatch.isReady." + orderedPluginName); + } var readiness = candidatePlugin.isReady(controllerData, deltaTime); if (readiness.active) { // this plugin will start. add it to the list of running plugins and mark the @@ -275,11 +296,18 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); _this.runningPluginNames[orderedPluginName] = true; _this.markSlots(candidatePlugin, orderedPluginName); } + if (PROFILE) { + Script.endProfileRange("dispatch.isReady." + orderedPluginName); + } } } + if (PROFILE) { + Script.endProfileRange("dispatch.isReady"); + } - // print("QQQ running plugins: " + JSON.stringify(_this.runningPluginNames)); - + if (PROFILE) { + Script.beginProfileRange("dispatch.run"); + } // give time to running plugins for (var runningPluginName in _this.runningPluginNames) { if (_this.runningPluginNames.hasOwnProperty(runningPluginName)) { @@ -290,6 +318,9 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); delete _this.runningPluginNames[runningPluginName]; _this.unmarkSlotsForPluginName(runningPluginName); } else { + if (PROFILE) { + Script.beginProfileRange("dispatch.run." + runningPluginName); + } var runningness = plugin.run(controllerData, deltaTime); if (!runningness.active) { // plugin is finished running, for now. remove it from the list @@ -297,9 +328,15 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); delete _this.runningPluginNames[runningPluginName]; _this.markSlots(plugin, false); } + if (PROFILE) { + Script.endProfileRange("dispatch.run." + runningPluginName); + } } } } + if (PROFILE) { + Script.endProfileRange("dispatch.run"); + } }; this.setBlacklist = function() { diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index 773f79754d..bb39b1a7f8 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -8,7 +8,8 @@ /* global Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, MSECS_PER_SEC:true , LEFT_HAND:true, RIGHT_HAND:true, NULL_UUID:true, AVATAR_SELF_ID:true, FORBIDDEN_GRAB_TYPES:true, - HAPTIC_PULSE_STRENGTH:true, HAPTIC_PULSE_DURATION:true, ZERO_VEC:true, ONE_VEC:true, DEFAULT_REGISTRATION_POINT:true, INCHES_TO_METERS:true, + HAPTIC_PULSE_STRENGTH:true, HAPTIC_PULSE_DURATION:true, ZERO_VEC:true, ONE_VEC:true, + DEFAULT_REGISTRATION_POINT:true, INCHES_TO_METERS:true, TRIGGER_OFF_VALUE:true, TRIGGER_ON_VALUE:true, PICK_MAX_DISTANCE:true, From 315f874824c2df6bc73662059aff76b9f0020673 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 12 Sep 2017 10:44:09 -0700 Subject: [PATCH 55/59] add profiling to controller-dispatcher --- scripts/system/controllers/controllerScripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index e3375a5a27..4e63d023fc 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -15,7 +15,7 @@ var CONTOLLER_SCRIPTS = [ "handControllerPointer.js", "grab.js", "toggleAdvancedMovementForHandControllers.js", - "ControllerDispatcher.js", + "controllerDispatcher.js", "controllerModules/nearParentGrabEntity.js", "controllerModules/nearParentGrabOverlay.js", "controllerModules/nearActionGrabEntity.js", From e9918ca59964aed6f6ffd935115da06db001a246 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 12 Sep 2017 10:52:52 -0700 Subject: [PATCH 56/59] add profiling to controller-dispatcher --- scripts/system/controllers/controllerDispatcher.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index f4567bd295..5b72e63054 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -26,7 +26,11 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); var TARGET_UPDATE_HZ = 60; // 50hz good enough, but we're using update var BASIC_TIMER_INTERVAL_MS = 1000 / TARGET_UPDATE_HZ; - var PROFILE = true; + var PROFILE = false; + + if (typeof Test !== "undefined") { + PROFILE = true; + } function ControllerDispatcher() { var _this = this; From b634292261a49d26d17bee5360a2e5b03c955be5 Mon Sep 17 00:00:00 2001 From: druiz17 Date: Tue, 12 Sep 2017 10:53:04 -0700 Subject: [PATCH 57/59] fixing some small issues --- .../controllerModules/farActionGrabEntity.js | 2 +- .../controllerModules/nearActionGrabEntity.js | 12 ++++++++++-- .../controllerModules/nearParentGrabEntity.js | 13 +++++++++++-- .../controllerModules/overlayLaserInput.js | 2 +- .../controllers/controllerModules/scaleAvatar.js | 4 ++-- .../system/libraries/controllerDispatcherUtils.js | 5 +++++ 6 files changed, 30 insertions(+), 8 deletions(-) diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 9a6f39c490..80718bc68d 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -137,7 +137,7 @@ Script.include("/~/system/libraries/controllers.js"); var dim = {x: radius, y: radius, z: radius}; var mode = "hold"; if (!this.distanceHolding && !this.distanceRotating) { - if (controllerData.triggerValues[this.hand] === 1) { + if (controllerData.triggerClicks[this.hand]) { mode = "full"; } else { mode = "half"; diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index 98a3b9395d..ee44fb4907 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -9,7 +9,8 @@ getControllerJointIndex, getGrabbableData, NULL_UUID, enableDispatcherModule, disableDispatcherModule, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, entityIsGrabbable, Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation, makeDispatcherModuleParameters, makeRunningValues, - TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, findGroupParent, entityIsCloneable, propsAreCloneDynamic, cloneEntity + TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, findGroupParent, entityIsCloneable, propsAreCloneDynamic, cloneEntity, + HAPTIC_PULSE_STRENGTH, HAPTIC_STYLUS_DURATION */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -22,6 +23,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.hand = hand; this.targetEntityID = null; this.actionID = null; // action this script created... + this.hapticTargetID = null; this.parameters = makeDispatcherModuleParameters( 500, @@ -152,6 +154,10 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); break; } if (entityIsGrabbable(props) || entityIsCloneable(props)) { + if (props.id !== this.hapticTargetID) { + Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); + this.hapticTargetID = props.id; + } // if we've attempted to grab a child, roll up to the root of the tree var groupRootProps = findGroupParent(controllerData, props); if (entityIsGrabbable(groupRootProps)) { @@ -166,11 +172,11 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.isReady = function (controllerData) { this.targetEntityID = null; + var targetProps = this.getTargetProps(controllerData); if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE && controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) { return makeRunningValues(false, [], []); } - var targetProps = this.getTargetProps(controllerData); if (targetProps) { if (!propsArePhysical(targetProps) && !propsAreCloneDynamic(targetProps)) { return makeRunningValues(false, [], []); // let nearParentGrabEntity handle it @@ -179,6 +185,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); return makeRunningValues(true, [this.targetEntityID], []); } } else { + this.hapticTargetID = null; return makeRunningValues(false, [], []); } }; @@ -187,6 +194,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); if (this.actionID) { if (controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0) { this.endNearGrabAction(); + this.hapticTargetID = null; return makeRunningValues(false, [], []); } diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index 47f80932bb..30cce1b315 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -10,7 +10,7 @@ getControllerJointIndex, NULL_UUID, enableDispatcherModule, disableDispatcherModule, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS, - findGroupParent, Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic + findGroupParent, Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic, HAPTIC_PULSE_STRENGTH, HAPTIC_STYLUS_DURATION */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -28,6 +28,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.previousParentID = {}; this.previousParentJointIndex = {}; this.previouslyUnhooked = {}; + this.hapticTargetID = null; this.parameters = makeDispatcherModuleParameters( 500, @@ -154,6 +155,11 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); continue; } if (entityIsGrabbable(props)) { + // give haptic feedback + if (props.id !== this.hapticTargetID) { + Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); + this.hapticTargetID = props.id; + } // if we've attempted to grab a child, roll up to the root of the tree var groupRootProps = findGroupParent(controllerData, props); if (entityIsGrabbable(groupRootProps)) { @@ -169,11 +175,11 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.targetEntityID = null; this.grabbing = false; + var targetProps = this.getTargetProps(controllerData); if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE && controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) { return makeRunningValues(false, [], []); } - var targetProps = this.getTargetProps(controllerData); if (targetProps) { if (propsArePhysical(targetProps) || propsAreCloneDynamic(targetProps)) { return makeRunningValues(false, [], []); // let nearActionGrabEntity handle it @@ -182,6 +188,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); return makeRunningValues(true, [this.targetEntityID], []); } } else { + this.hapticTargetID = null; return makeRunningValues(false, [], []); } }; @@ -190,6 +197,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); if (this.grabbing) { if (controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0) { this.endNearParentingGrabEntity(); + this.hapticTargetID = null; return makeRunningValues(false, [], []); } @@ -197,6 +205,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); if (!this.thisHandIsParent(props)) { this.grabbing = false; this.targetEntityID = null; + this.hapticTargetID = null; return makeRunningValues(false, [], []); } diff --git a/scripts/system/controllers/controllerModules/overlayLaserInput.js b/scripts/system/controllers/controllerModules/overlayLaserInput.js index b1ffc77afe..2c950fd4df 100644 --- a/scripts/system/controllers/controllerModules/overlayLaserInput.js +++ b/scripts/system/controllers/controllerModules/overlayLaserInput.js @@ -433,7 +433,7 @@ Script.include("/~/system/libraries/controllers.js"); var nearGrabModule = getEnabledModuleByName(nearGrabName); var status = nearGrabModule ? nearGrabModule.isReady(controllerData) : makeRunningValues(false, [], []); var offOverlay = (intersection.type !== RayPick.INTERSECTED_OVERLAY); - var triggerOff = (controllerData.triggerValues[this.hand] === 0); + var triggerOff = (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE); return offOverlay || status.active || triggerOff; }; diff --git a/scripts/system/controllers/controllerModules/scaleAvatar.js b/scripts/system/controllers/controllerModules/scaleAvatar.js index 2c85b75ea9..04c400f2bc 100644 --- a/scripts/system/controllers/controllerModules/scaleAvatar.js +++ b/scripts/system/controllers/controllerModules/scaleAvatar.js @@ -15,7 +15,7 @@ //Script.include("/~/system/libraries/controllerDispatcherUtils.js"); (function () { var dispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); - + var BUMPER_ON_VALUE = 0.5; function ScaleAvatar(hand) { this.hand = hand; this.scalingStartAvatarScale = 0; @@ -38,7 +38,7 @@ }; this.triggersPressed = function(controllerData) { - if (controllerData.triggerValues[this.hand] === 1 && controllerData.secondaryValues[this.hand] === 1) { + if (controllerData.triggerClicks[this.hand] && controllerData.secondaryValues[this.hand] > BUMPER_ON_VALUE) { return true; } return false; diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index bc2aa1a9c8..dcdbed6fb2 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -19,6 +19,8 @@ COLORS_GRAB_DISTANCE_HOLD:true, NEAR_GRAB_RADIUS:true, DISPATCHER_PROPERTIES:true, + HAPTIC_PULSE_STRENGTH:true, + HAPTIC_PULSE_DURATION:true, Entities, makeDispatcherModuleParameters:true, makeRunningValues:true, @@ -42,6 +44,9 @@ MSECS_PER_SEC = 1000.0; INCHES_TO_METERS = 1.0 / 39.3701; +HAPTIC_PULSE_STRENGTH = 1.0; +HAPTIC_PULSE_DURATION = 13.0; + ZERO_VEC = { x: 0, y: 0, z: 0 }; ONE_VEC = { x: 1, y: 1, z: 1 }; From b3ff5c86f77a2a9cbfcb8f75b98d74237d3a08af Mon Sep 17 00:00:00 2001 From: druiz17 Date: Tue, 12 Sep 2017 14:43:06 -0700 Subject: [PATCH 58/59] fixed secondary triggers and chest lid --- .../controllers/controllerModules/nearActionGrabEntity.js | 8 ++++---- .../controllers/controllerModules/nearParentGrabEntity.js | 6 +++--- .../system/controllers/controllerModules/scaleAvatar.js | 5 ++--- scripts/system/libraries/controllerDispatcherUtils.js | 8 +++++--- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index ee44fb4907..399388d614 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -10,7 +10,7 @@ propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, entityIsGrabbable, Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation, makeDispatcherModuleParameters, makeRunningValues, TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, findGroupParent, entityIsCloneable, propsAreCloneDynamic, cloneEntity, - HAPTIC_PULSE_STRENGTH, HAPTIC_STYLUS_DURATION + HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -63,7 +63,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); var grabbableData = getGrabbableData(targetProps); this.ignoreIK = grabbableData.ignoreIK; - this.kinematicGrab = grabbableData.kinematicGrab; + this.kinematicGrab = grabbableData.kinematic; var handRotation; var handPosition; @@ -192,7 +192,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.run = function (controllerData) { if (this.actionID) { - if (controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0) { + if (controllerData.triggerClicks[this.hand] < TRIGGER_OFF_VALUE && controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) { this.endNearGrabAction(); this.hapticTargetID = null; return makeRunningValues(false, [], []); @@ -211,7 +211,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); var targetProps = this.getTargetProps(controllerData); if (targetProps) { - if (controllerData.triggerClicks[this.hand] === 1 || controllerData.secondaryValues[this.hand] === 1) { + if (controllerData.triggerClicks[this.hand] || controllerData.secondaryValues[this.hand] > BUMPER_ON_VALUE) { // switch to grabbing var targetCloneable = entityIsCloneable(targetProps); if (targetCloneable) { diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index 30cce1b315..6cd52fef07 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -10,7 +10,7 @@ getControllerJointIndex, NULL_UUID, enableDispatcherModule, disableDispatcherModule, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS, - findGroupParent, Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic, HAPTIC_PULSE_STRENGTH, HAPTIC_STYLUS_DURATION + findGroupParent, Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -195,7 +195,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.run = function (controllerData, deltaTime) { if (this.grabbing) { - if (controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0) { + if (controllerData.triggerClicks[this.hand] < TRIGGER_OFF_VALUE && controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) { this.endNearParentingGrabEntity(); this.hapticTargetID = null; return makeRunningValues(false, [], []); @@ -217,7 +217,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); if (!readiness.active) { return readiness; } - if (controllerData.triggerClicks[this.hand] === 1 || controllerData.secondaryValues[this.hand] === 1) { + if (controllerData.triggerClicks[this.hand] || controllerData.secondaryValues[this.hand] > BUMPER_ON_VALUE) { // switch to grab var targetProps = this.getTargetProps(controllerData); var targetCloneable = entityIsCloneable(targetProps); diff --git a/scripts/system/controllers/controllerModules/scaleAvatar.js b/scripts/system/controllers/controllerModules/scaleAvatar.js index 04c400f2bc..b98e26b1da 100644 --- a/scripts/system/controllers/controllerModules/scaleAvatar.js +++ b/scripts/system/controllers/controllerModules/scaleAvatar.js @@ -12,10 +12,9 @@ setGrabCommunications, Menu, HMD, isInEditMode, AvatarList */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ -//Script.include("/~/system/libraries/controllerDispatcherUtils.js"); (function () { var dispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); - var BUMPER_ON_VALUE = 0.5; + function ScaleAvatar(hand) { this.hand = hand; this.scalingStartAvatarScale = 0; @@ -38,7 +37,7 @@ }; this.triggersPressed = function(controllerData) { - if (controllerData.triggerClicks[this.hand] && controllerData.secondaryValues[this.hand] > BUMPER_ON_VALUE) { + if (controllerData.triggerClicks[this.hand] && controllerData.secondaryValues[this.hand] > dispatcherUtils.BUMPER_ON_VALUE) { return true; } return false; diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index 1781c0dff0..7bed6413da 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -66,6 +66,7 @@ DEFAULT_REGISTRATION_POINT = { x: 0.5, y: 0.5, z: 0.5 }; TRIGGER_OFF_VALUE = 0.1; TRIGGER_ON_VALUE = TRIGGER_OFF_VALUE + 0.05; // Squeezed just enough to activate search or near grab +BUMPER_ON_VALUE = 0.5; PICK_MAX_DISTANCE = 500; // max length of pick-ray DEFAULT_SEARCH_SPHERE_DISTANCE = 1000; // how far from camera to search intersection? @@ -157,8 +158,8 @@ getGrabbableData = function (props) { if (!grabbableData.hasOwnProperty("ignoreIK")) { grabbableData.ignoreIK = true; } - if (!grabbableData.hasOwnProperty("kinematicGrab")) { - grabbableData.kinematicGrab = true; + if (!grabbableData.hasOwnProperty("kinematic")) { + grabbableData.kinematic = true; } if (!grabbableData.hasOwnProperty("wantsTrigger")) { grabbableData.wantsTrigger = false; @@ -315,6 +316,7 @@ if (typeof module !== 'undefined') { disableDispatcherModule: disableDispatcherModule, makeRunningValues: makeRunningValues, LEFT_HAND: LEFT_HAND, - RIGHT_HAND: RIGHT_HAND + RIGHT_HAND: RIGHT_HAND, + BUMPER_ON_VALUE: BUMPER_ON_VALUE }; } From 1b288ef860e4a40a1dbb3d8c1db16bcf4ba6affa Mon Sep 17 00:00:00 2001 From: druiz17 Date: Wed, 13 Sep 2017 16:15:36 -0700 Subject: [PATCH 59/59] make garbbing radius the same as master --- scripts/system/controllers/controllerDispatcher.js | 2 +- scripts/system/libraries/controllerDispatcherUtils.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 5b72e63054..990f156ba8 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -21,7 +21,7 @@ Script.include("/~/system/libraries/controllers.js"); Script.include("/~/system/libraries/controllerDispatcherUtils.js"); (function() { - var NEAR_MAX_RADIUS = 1.0; + var NEAR_MAX_RADIUS = 0.1; var TARGET_UPDATE_HZ = 60; // 50hz good enough, but we're using update var BASIC_TIMER_INTERVAL_MS = 1000 / TARGET_UPDATE_HZ; diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index 7bed6413da..715d520501 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -76,7 +76,7 @@ COLORS_GRAB_SEARCHING_HALF_SQUEEZE = { red: 10, green: 10, blue: 255 }; COLORS_GRAB_SEARCHING_FULL_SQUEEZE = { red: 250, green: 10, blue: 10 }; COLORS_GRAB_DISTANCE_HOLD = { red: 238, green: 75, blue: 214 }; -NEAR_GRAB_RADIUS = 0.1; +NEAR_GRAB_RADIUS = 1.0; DISPATCHER_PROPERTIES = [ "position",