diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 01f607b0ed..28b19ee9e0 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -277,27 +277,37 @@ void HmdDisplayPlugin::updateFrameData() { continue; } - const auto& laserDirection = handLaser.direction; - auto model = _presentHandPoses[i]; - auto castDirection = glm::quat_cast(model) * laserDirection; + const vec3& laserDirection = handLaser.direction; + mat4 model = _presentHandPoses[i]; + vec3 castStart = vec3(model[3]); + vec3 castDirection = glm::quat_cast(model) * laserDirection; if (glm::abs(glm::length2(castDirection) - 1.0f) > EPSILON) { castDirection = glm::normalize(castDirection); castDirection = glm::inverse(_presentUiModelTransform.getRotation()) * castDirection; } + // this offset needs to match GRAB_POINT_SPHERE_OFFSET in scripts/system/libraries/controllers.js + static const vec3 GRAB_POINT_SPHERE_OFFSET = vec3(0.1f, 0.04f, -0.32f); + vec3 grabPointOffset = GRAB_POINT_SPHERE_OFFSET; + if (i == 0) { + grabPointOffset.x *= -1.0f; // this changes between left and right hands + } + castStart += glm::quat_cast(model) * grabPointOffset; + // FIXME fetch the actual UI radius from... somewhere? float uiRadius = 1.0f; // Find the intersection of the laser with he UI and use it to scale the model matrix float distance; - if (!glm::intersectRaySphere(vec3(_presentHandPoses[i][3]), castDirection, _presentUiModelTransform.getTranslation(), uiRadius * uiRadius, distance)) { + if (!glm::intersectRaySphere(castStart, castDirection, + _presentUiModelTransform.getTranslation(), uiRadius * uiRadius, distance)) { continue; } - _presentHandLaserPoints[i].first = vec3(_presentHandPoses[i][3]); + _presentHandLaserPoints[i].first = castStart; _presentHandLaserPoints[i].second = _presentHandLaserPoints[i].first + (castDirection * distance); - vec3 intersectionPosition = vec3(_presentHandPoses[i][3]) + (castDirection * distance) - _presentUiModelTransform.getTranslation(); + vec3 intersectionPosition = castStart + (castDirection * distance) - _presentUiModelTransform.getTranslation(); intersectionPosition = glm::inverse(_presentUiModelTransform.getRotation()) * intersectionPosition; // Take the interesection normal and convert it to a texture coordinate diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 6f34ec4294..01be03c14a 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -11,12 +11,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 setEntityCustomData, getEntityCustomData, flatten, Xform, Script, Quat, Vec3, MyAvatar, Entities, Overlays, Settings, Reticle, Controller, Camera, Messages, Mat4 */ +/* global setEntityCustomData, getEntityCustomData, flatten, Xform, Script, Quat, Vec3, MyAvatar, Entities, Overlays, Settings, Reticle, Controller, Camera, Messages, Mat4, getControllerWorldLocation */ (function() { // BEGIN LOCAL_SCOPE Script.include("/~/system/libraries/utils.js"); Script.include("/~/system/libraries/Xform.js"); +Script.include("/~/system/libraries/controllers.js"); // // add lines where the hand ray picking is happening @@ -110,17 +111,12 @@ var NEAR_GRAB_RADIUS = 0.04; // radius used for palm vs object for near grabbing var NEAR_GRAB_MAX_DISTANCE = 1.0; // you cannot grab objects that are this far away from your hand var NEAR_GRAB_PICK_RADIUS = 0.25; // radius used for search ray vs object for near grabbing. - -var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed // if an equipped item is "adjusted" to be too far from the hand it's in, it will be unequipped. var CHECK_TOO_FAR_UNEQUIP_TIME = 0.3; // seconds, duration between checks -// var GRAB_POINT_SPHERE_OFFSET = { x: 0, y: 0.2, z: 0 }; -// var GRAB_POINT_SPHERE_OFFSET = { x: 0.1, y: 0.175, z: 0.04 }; -var GRAB_POINT_SPHERE_OFFSET = { x: 0.1, y: 0.32, z: 0.04 }; var GRAB_POINT_SPHERE_RADIUS = NEAR_GRAB_RADIUS; var GRAB_POINT_SPHERE_COLOR = { red: 20, green: 90, blue: 238 }; var GRAB_POINT_SPHERE_ALPHA = 0.85; @@ -742,30 +738,8 @@ function MyController(hand) { } }; - this.getGrabPointSphereOffset = function() { - if (hand === RIGHT_HAND) { - return GRAB_POINT_SPHERE_OFFSET; - } - return { - x: GRAB_POINT_SPHERE_OFFSET.x * -1, - y: GRAB_POINT_SPHERE_OFFSET.y, - z: GRAB_POINT_SPHERE_OFFSET.z - }; - }; - - // controllerLocation is where the controller would be, in-world. - this.getControllerLocation = function (doOffset) { - var standardControllerValue = (hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; - var pose = Controller.getPoseValue(standardControllerValue); - - var orientation = Quat.multiply(MyAvatar.orientation, pose.rotation); - var position = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position); - // add to the real position so the grab-point is out in front of the hand, a bit - if (doOffset) { - position = Vec3.sum(position, Vec3.multiplyQbyV(orientation, this.getGrabPointSphereOffset())); - } - - return {position: position, orientation: orientation}; + this.handToController = function() { + return (hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; }; this.actionID = null; // action this script created... @@ -881,7 +855,7 @@ function MyController(hand) { } if (!this.grabPointSphere) { this.grabPointSphere = Overlays.addOverlay("sphere", { - localPosition: this.getGrabPointSphereOffset(), + localPosition: getGrabPointSphereOffset(this.handToController()), localRotation: { x: 0, y: 0, z: 0, w: 1 }, dimensions: GRAB_POINT_SPHERE_RADIUS, color: GRAB_POINT_SPHERE_COLOR, @@ -1109,7 +1083,7 @@ function MyController(hand) { } if (!this.waitForTriggerRelease && this.triggerSmoothedSqueezed()) { this.lastPickTime = 0; - this.startingHandRotation = this.getControllerLocation(true).orientation; + this.startingHandRotation = getControllerWorldLocation(this.handToController(), true).orientation; if (this.triggerSmoothedSqueezed()) { this.setState(STATE_SEARCHING, "trigger squeeze detected"); return; @@ -1118,7 +1092,7 @@ function MyController(hand) { this.grabPointSphereOn(); - var controllerLocation = this.getControllerLocation(true); + var controllerLocation = getControllerWorldLocation(this.handToController(), true); var worldHandPosition = controllerLocation.position; var candidateEntities = Entities.findEntities(worldHandPosition, MAX_EQUIP_HOTSPOT_RADIUS); @@ -1176,7 +1150,7 @@ function MyController(hand) { // @returns {object} returns object with two keys entityID and distance // this.calcRayPickInfo = function(hand) { - var controllerLocation = this.getControllerLocation(true); + var controllerLocation = getControllerWorldLocation(this.handToController(), true); var worldHandPosition = controllerLocation.position; var worldHandRotation = controllerLocation.orientation; @@ -1200,9 +1174,6 @@ function MyController(hand) { } this.lastPickTime = now; - var directionNormalized = Vec3.normalize(pickRay.direction); - var directionBacked = Vec3.multiply(directionNormalized, PICK_BACKOFF_DISTANCE); - var intersection; if (USE_BLACKLIST === true && blacklist.length !== 0) { intersection = findRayIntersection(pickRay, true, [], blacklist); @@ -1420,7 +1391,8 @@ function MyController(hand) { return _this.collectEquipHotspots(entityID); })).filter(function(hotspot) { return (_this.hotspotIsEquippable(hotspot) && - Vec3.distance(hotspot.worldPosition, _this.getControllerLocation(true).position) < hotspot.radius + distance); + Vec3.distance(hotspot.worldPosition, getControllerWorldLocation(_this.handToController(), true).position) < + hotspot.radius + distance); }); return equippableHotspots; }; @@ -1431,8 +1403,9 @@ function MyController(hand) { if (equippableHotspots.length > 0) { // sort by distance equippableHotspots.sort(function(a, b) { - var aDistance = Vec3.distance(a.worldPosition, this.getControllerLocation(true).position); - var bDistance = Vec3.distance(b.worldPosition, this.getControllerLocation(true).position); + var handControllerLocation = getControllerWorldLocation(this.handToController(), true); + var aDistance = Vec3.distance(a.worldPosition, handControllerLocation.position); + var bDistance = Vec3.distance(b.worldPosition, handControllerLocation.position); return aDistance - bDistance; }); return equippableHotspots[0]; @@ -1467,7 +1440,7 @@ function MyController(hand) { return; } - var handPosition = this.getControllerLocation(true).position; + var handPosition = getControllerWorldLocation(this.handToController(), true).position; var rayPickInfo = this.calcRayPickInfo(this.hand); @@ -1652,7 +1625,7 @@ function MyController(hand) { this.clearEquipHaptics(); this.grabPointSphereOff(); - var worldControllerPosition = this.getControllerLocation(true).position; + var worldControllerPosition = getControllerWorldLocation(this.handToController(), true).position; // transform the position into room space var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix()); @@ -1727,7 +1700,7 @@ function MyController(hand) { this.heartBeat(this.grabbedEntity); - var controllerLocation = this.getControllerLocation(true); + var controllerLocation = getControllerWorldLocation(this.handToController(), true); var worldControllerPosition = controllerLocation.position; var worldControllerRotation = controllerLocation.orientation; @@ -1864,7 +1837,7 @@ function MyController(hand) { }; this.dropGestureProcess = function(deltaTime) { - var worldHandRotation = this.getControllerLocation(true).orientation; + var worldHandRotation = getControllerWorldLocation(this.handToController(), true).orientation; var localHandUpAxis = this.hand === RIGHT_HAND ? { x: 1, y: 0, @@ -1942,7 +1915,7 @@ function MyController(hand) { var handRotation; var handPosition; if (this.ignoreIK) { - var controllerLocation = this.getControllerLocation(false); + var controllerLocation = getControllerWorldLocation(this.handToController(), false); handRotation = controllerLocation.orientation; handPosition = controllerLocation.position; } else { @@ -2122,7 +2095,7 @@ function MyController(hand) { if (props.parentID == MyAvatar.sessionUUID) { var handPosition; if (this.ignoreIK) { - handPosition = this.getControllerLocation(false).position; + handPosition = getControllerWorldLocation(this.handToController(), false).position; } else { handPosition = this.getHandPosition(); } @@ -2238,8 +2211,8 @@ function MyController(hand) { } var pickRay = { - origin: this.getControllerLocation().position, - direction: Quat.getUp(this.getControllerLocation().orientation) + origin: getControllerWorldLocation(this.handToController(), false).position, + direction: Quat.getUp(getControllerWorldLocation(this.handToController(), false).orientation) }; var now = Date.now(); @@ -2268,7 +2241,8 @@ function MyController(hand) { this.entityTouchingEnter = function() { // test for intersection between controller laser and web entity plane. - var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.getControllerLocation(true)); + var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, + getControllerWorldLocation(this.handToController(), true)); if (intersectInfo) { var pointerEvent = { type: "Press", @@ -2293,7 +2267,8 @@ function MyController(hand) { this.entityTouchingExit = function() { // test for intersection between controller laser and web entity plane. - var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.getControllerLocation(true)); + var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, + getControllerWorldLocation(this.handToController(), true)); if (intersectInfo) { var pointerEvent; if (this.deadspotExpired) { @@ -2331,7 +2306,8 @@ function MyController(hand) { } // test for intersection between controller laser and web entity plane. - var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.getControllerLocation(true)); + var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, + getControllerWorldLocation(this.handToController(), true)); if (intersectInfo) { if (Entities.keyboardFocusEntity != this.grabbedEntity) { diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 6b62674be5..ce98ed6d8e 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -20,6 +20,7 @@ // When partially squeezing over a HUD element, a laser or the reticle is shown where the active hand // controller beam intersects the HUD. +Script.include("/~/system/libraries/controllers.js"); // UTILITIES ------------- // @@ -203,16 +204,13 @@ function overlayFromWorldPoint(point) { } function activeHudPoint2d(activeHand) { // if controller is valid, update reticle position and answer 2d point. Otherwise falsey. - var controllerPose = Controller.getPoseValue(activeHand); - // Valid if any plugged-in hand controller is "on". (uncradled Hydra, green-lighted Vive...) + var controllerPose = getControllerWorldLocation(activeHand, true); if (!controllerPose.valid) { return; // Controller is cradled. } - var controllerPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, controllerPose.translation), - MyAvatar.position); - // This gets point direction right, but if you want general quaternion it would be more complicated: - var controllerDirection = Quat.getUp(Quat.multiply(MyAvatar.orientation, controllerPose.rotation)); - + var controllerPosition = controllerPose.position; + var controllerDirection = Quat.getUp(controllerPose.rotation); + var hudPoint3d = calculateRayUICollisionPoint(controllerPosition, controllerDirection); if (!hudPoint3d) { if (Menu.isOptionChecked("Overlays")) { // With our hud resetting strategy, hudPoint3d should be valid here diff --git a/scripts/system/libraries/controllers.js b/scripts/system/libraries/controllers.js new file mode 100644 index 0000000000..a1318ca47c --- /dev/null +++ b/scripts/system/libraries/controllers.js @@ -0,0 +1,46 @@ +// handControllerGrab.js +// +// Created by Seth Alves on 2016-9-7 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +/* global MyAvatar, Vec3, Controller, Quat */ + + +// var GRAB_POINT_SPHERE_OFFSET = { x: 0, y: 0.2, z: 0 }; +// var GRAB_POINT_SPHERE_OFFSET = { x: 0.1, y: 0.175, z: 0.04 }; + +// this offset needs to match the one in libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +var GRAB_POINT_SPHERE_OFFSET = { x: 0.1, y: 0.32, z: 0.04 }; + +getGrabPointSphereOffset = function(handController) { + if (handController === Controller.Standard.RightHand) { + return GRAB_POINT_SPHERE_OFFSET; + } + return { + x: GRAB_POINT_SPHERE_OFFSET.x * -1, + y: GRAB_POINT_SPHERE_OFFSET.y, + z: GRAB_POINT_SPHERE_OFFSET.z + }; +}; + +// controllerLocation is where the controller would be, in-world. +getControllerWorldLocation = function (handController, doOffset) { + var orientation; + var position; + var pose = Controller.getPoseValue(handController); + if (pose.valid) { + orientation = Quat.multiply(MyAvatar.orientation, pose.rotation); + position = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position); + // add to the real position so the grab-point is out in front of the hand, a bit + if (doOffset) { + position = Vec3.sum(position, Vec3.multiplyQbyV(orientation, getGrabPointSphereOffset(handController))); + } + } + return {position: position, + translation: position, + orientation: orientation, + rotation: orientation, + valid: pose.valid}; +}; diff --git a/scripts/system/mod.js b/scripts/system/mod.js index b2c9785539..4db1576168 100644 --- a/scripts/system/mod.js +++ b/scripts/system/mod.js @@ -10,6 +10,8 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/* global Toolbars, Script, Users, Overlays, AvatarList, Controller, Camera, getControllerWorldLocation */ + (function() { // BEGIN LOCAL_SCOPE @@ -144,7 +146,7 @@ AvatarList.avatarRemovedEvent.connect(function(avatarID){ function handleSelectedOverlay(clickedOverlay) { // see this is one of our mod overlays - var modOverlayKeys = Object.keys(modOverlays) + var modOverlayKeys = Object.keys(modOverlays); for (var i = 0; i < modOverlayKeys.length; ++i) { var avatarID = modOverlayKeys[i]; var modOverlay = modOverlays[avatarID]; @@ -187,13 +189,9 @@ Controller.mousePressEvent.connect(function(event){ var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click'); function controllerComputePickRay(hand) { - var controllerPose = Controller.getPoseValue(hand); + var controllerPose = getControllerWorldLocation(hand, true); if (controllerPose.valid) { - var controllerPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, controllerPose.translation), - MyAvatar.position); - // This gets point direction right, but if you want general quaternion it would be more complicated: - var controllerDirection = Quat.getUp(Quat.multiply(MyAvatar.orientation, controllerPose.rotation)); - return { origin: controllerPosition, direction: controllerDirection }; + return { origin: controllerPose.position, direction: controllerPose.orientation }; } }