From 7e5a1e73e5d32d167a3c639b79ffba21020da6df Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 14 Mar 2016 15:25:59 -0700 Subject: [PATCH 01/16] equip/de-equip with thumb button rather than bumper. trigger and bumper do the same thing --- examples/controllers/handControllerGrab.js | 172 +++++++++++++-------- 1 file changed, 105 insertions(+), 67 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 43c18da72d..e6314a5183 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -20,7 +20,7 @@ Script.include("../libraries/utils.js"); // add lines where the hand ray picking is happening // var WANT_DEBUG = false; -var WANT_DEBUG_STATE = false; +var WANT_DEBUG_STATE = true; var WANT_DEBUG_SEARCH_NAME = null; // @@ -34,6 +34,8 @@ var TRIGGER_OFF_VALUE = 0.15; var BUMPER_ON_VALUE = 0.5; +var THUMB_ON_VALUE = 0.5; + var HAND_HEAD_MIX_RATIO = 0.0; // 0 = only use hands for search/move. 1 = only use head for search/move. var PICK_WITH_HAND_RAY = true; @@ -161,11 +163,11 @@ var STATE_CONTINUE_NEAR_TRIGGER = 7; var STATE_FAR_TRIGGER = 8; var STATE_CONTINUE_FAR_TRIGGER = 9; var STATE_RELEASE = 10; -var STATE_EQUIP_SEARCHING = 11; +// var STATE_EQUIP_SEARCHING = 11; var STATE_EQUIP = 12 -var STATE_CONTINUE_EQUIP_BD = 13; // equip while bumper is still held down +// var STATE_CONTINUE_EQUIP_BD = 13; // equip while bumper is still held down var STATE_CONTINUE_EQUIP = 14; -var STATE_WAITING_FOR_BUMPER_RELEASE = 15; +var STATE_WAITING_FOR_THUMB_RELEASE = 15; // "collidesWith" is specified by comma-separated list of group names // the possible group names are: static, dynamic, kinematic, myAvatar, otherAvatar @@ -199,16 +201,16 @@ function stateToName(state) { return "continue_far_trigger"; case STATE_RELEASE: return "release"; - case STATE_EQUIP_SEARCHING: - return "equip_searching"; + // case STATE_EQUIP_SEARCHING: + // return "equip_searching"; case STATE_EQUIP: return "equip"; - case STATE_CONTINUE_EQUIP_BD: - return "continue_equip_bd"; + // case STATE_CONTINUE_EQUIP_BD: + // return "continue_equip_bd"; case STATE_CONTINUE_EQUIP: return "continue_equip"; - case STATE_WAITING_FOR_BUMPER_RELEASE: - return "waiting_for_bumper_release"; + case STATE_WAITING_FOR_THUMB_RELEASE: + return "waiting_for_thumb_release"; } return "unknown"; @@ -262,6 +264,7 @@ function MyController(hand) { this.triggerValue = 0; // rolling average of trigger value this.rawTriggerValue = 0; this.rawBumperValue = 0; + this.rawThumbValue = 0; //for visualizations this.overlayLine = null; this.particleBeamObject = null; @@ -297,9 +300,9 @@ function MyController(hand) { case STATE_SEARCHING: this.search(); break; - case STATE_EQUIP_SEARCHING: - this.search(); - break; + // case STATE_EQUIP_SEARCHING: + // this.search(); + // break; case STATE_DISTANCE_HOLDING: this.distanceHolding(); break; @@ -310,11 +313,11 @@ function MyController(hand) { case STATE_EQUIP: this.nearGrabbing(); break; - case STATE_WAITING_FOR_BUMPER_RELEASE: - this.waitingForBumperRelease(); + case STATE_WAITING_FOR_THUMB_RELEASE: + this.waitingForThumbRelease(); break; case STATE_CONTINUE_NEAR_GRABBING: - case STATE_CONTINUE_EQUIP_BD: + // case STATE_CONTINUE_EQUIP_BD: case STATE_CONTINUE_EQUIP: this.continueNearGrabbing(); break; @@ -784,16 +787,36 @@ function MyController(hand) { return _this.rawBumperValue < BUMPER_ON_VALUE; }; + // this.triggerOrBumperSqueezed = function() { + // return triggerSmoothedSqueezed() || bumperSqueezed(); + // } + + // this.triggerAndBumperReleased = function() { + // return triggerSmoothedReleased() && bumperReleased(); + // } + + this.thumbPress = function(value) { + _this.rawThumbValue = value; + } + + this.thumbPressed = function() { + return _this.rawThumbValue > THUMB_ON_VALUE; + }; + + this.thumbReleased = function() { + return _this.rawThumbValue < THUMB_ON_VALUE; + }; + this.off = function() { if (this.triggerSmoothedSqueezed() || this.bumperSqueezed()) { this.lastPickTime = 0; var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; this.startingHandRotation = Controller.getPoseValue(controllerHandInput).rotation; - if (this.triggerSmoothedSqueezed()) { + if (this.triggerSmoothedSqueezed() || this.bumperSqueezed()) { this.setState(STATE_SEARCHING); - } else { - this.setState(STATE_EQUIP_SEARCHING); - } + } // else { + // this.setState(STATE_EQUIP_SEARCHING); + // } } }; @@ -804,7 +827,7 @@ function MyController(hand) { this.checkForStrayChildren(); - if (this.state == STATE_SEARCHING ? this.triggerSmoothedReleased() : this.bumperReleased()) { + if (this.state == STATE_SEARCHING && this.triggerSmoothedReleased() && this.bumperReleased()) { this.setState(STATE_RELEASE); return; } @@ -936,13 +959,13 @@ function MyController(hand) { } continue; } - if (propsForCandidate.parentID != NULL_UUID && this.state == STATE_EQUIP_SEARCHING) { - // don't allow a double-equip - if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) { - print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': it's a child"); - } - continue; - } + // if (propsForCandidate.parentID != NULL_UUID && this.state == STATE_EQUIP_SEARCHING) { + // // don't allow a double-equip + // if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) { + // print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': it's a child"); + // } + // continue; + // } if (this.state == STATE_SEARCHING && !isPhysical && distance > NEAR_PICK_MAX_DISTANCE && !near && !grabbableDataForCandidate.wantsTrigger) { @@ -978,7 +1001,8 @@ function MyController(hand) { return; } // far grab or equip with action - if ((isPhysical || this.state == STATE_EQUIP_SEARCHING) && !near) { + // if ((isPhysical || this.state == STATE_EQUIP_SEARCHING) && !near) { + if (isPhysical && !near) { if (entityIsGrabbedByOther(this.grabbedEntity)) { // don't distance grab something that is already grabbed. if (WANT_DEBUG_SEARCH_NAME && props.name == WANT_DEBUG_SEARCH_NAME) { @@ -999,7 +1023,8 @@ function MyController(hand) { intersectionPointToCenterDistance * FAR_TO_NEAR_GRAB_PADDING_FACTOR); } - this.setState(this.state == STATE_SEARCHING ? STATE_DISTANCE_HOLDING : STATE_EQUIP); + // this.setState(this.state == STATE_SEARCHING ? STATE_DISTANCE_HOLDING : STATE_EQUIP); + this.setState(STATE_DISTANCE_HOLDING); this.searchSphereOff(); return; } @@ -1104,7 +1129,7 @@ function MyController(hand) { }; this.continueDistanceHolding = function() { - if (this.triggerSmoothedReleased()) { + if (this.triggerSmoothedReleased() && this.bumperReleased()) { this.setState(STATE_RELEASE); this.callEntityMethodOnGrabbed("releaseGrab"); return; @@ -1121,14 +1146,15 @@ function MyController(hand) { var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - if (this.state == STATE_CONTINUE_DISTANCE_HOLDING && this.bumperSqueezed() && - this.hasPresetOffsets()) { - var saveGrabbedID = this.grabbedEntity; - this.release(); - this.setState(STATE_EQUIP); - this.grabbedEntity = saveGrabbedID; - return; - } + // // switch from far grab to near equip + // if (this.state == STATE_CONTINUE_DISTANCE_HOLDING && this.bumperSqueezed() && + // this.hasPresetOffsets()) { + // var saveGrabbedID = this.grabbedEntity; + // this.release(); + // this.setState(STATE_EQUIP); + // this.grabbedEntity = saveGrabbedID; + // return; + // } var now = Date.now(); this.currentObjectTime = now; @@ -1307,7 +1333,7 @@ function MyController(hand) { this.nearGrabbing = function() { var now = Date.now(); - if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { + if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased() && this.bumperReleased()) { this.setState(STATE_RELEASE); this.callEntityMethodOnGrabbed("releaseGrab"); return; @@ -1329,7 +1355,7 @@ function MyController(hand) { var handPosition = this.getHandPosition(); var hasPresetPosition = false; - if (this.state != STATE_NEAR_GRABBING && this.hasPresetOffsets()) { + if (this.state == STATE_EQUIP && this.hasPresetOffsets()) { var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); // if an object is "equipped" and has a predefined offset, use it. this.ignoreIK = grabbableData.ignoreIK ? grabbableData.ignoreIK : false; @@ -1351,7 +1377,7 @@ function MyController(hand) { } } - var isPhysical = this.propsArePhysical(grabbedProperties) || entityHasActions(this.grabbedEntity); + var isPhysical = this.propsArePhysical(grabbedProperties); // || entityHasActions(this.grabbedEntity); if (isPhysical && this.state == STATE_NEAR_GRABBING) { // grab entity via action if (!this.setupHoldAction()) { @@ -1359,6 +1385,11 @@ function MyController(hand) { } } else { // grab entity via parenting + if (this.actionID) { + var saveGrabbedID = this.grabbedEntity; + this.release(); + this.grabbedEntity = saveGrabbedID; + } this.actionID = null; var handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); reparentProps = { @@ -1378,13 +1409,15 @@ function MyController(hand) { this.callEntityMethodOnGrabbed(this.state == STATE_NEAR_GRABBING ? "startNearGrab" : "startEquip"); - if (this.state == STATE_NEAR_GRABBING) { - // near grabbing - this.setState(STATE_CONTINUE_NEAR_GRABBING); - } else { - // equipping - this.setState(STATE_CONTINUE_EQUIP_BD); - } + // if (this.state == STATE_NEAR_GRABBING) { + // // near grabbing + // this.setState(STATE_CONTINUE_NEAR_GRABBING); + // } else { + // // equipping + // this.setState(STATE_CONTINUE_EQUIP_BD); + // } + + this.setState(this.state == STATE_NEAR_GRABBING ? STATE_CONTINUE_NEAR_GRABBING : STATE_CONTINUE_EQUIP); this.currentHandControllerTipPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition; @@ -1392,22 +1425,23 @@ function MyController(hand) { }; this.continueNearGrabbing = function() { - if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.triggerSmoothedReleased()) { + if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.triggerSmoothedReleased() && this.bumperReleased()) { this.setState(STATE_RELEASE); this.callEntityMethodOnGrabbed("releaseGrab"); return; } - if (this.state == STATE_CONTINUE_EQUIP_BD && this.bumperReleased()) { - this.setState(STATE_CONTINUE_EQUIP); - return; - } - if (this.state == STATE_CONTINUE_EQUIP && this.bumperSqueezed()) { - this.setState(STATE_WAITING_FOR_BUMPER_RELEASE); + // if (this.state == STATE_CONTINUE_EQUIP_BD && this.thumbReleased()) { + // this.setState(STATE_EQUIP); + // return; + // } + if (this.state == STATE_CONTINUE_EQUIP && this.thumbPressed()) { + // this.setState(STATE_WAITING_FOR_THUMB_RELEASE); + this.setState(STATE_RELEASE); this.callEntityMethodOnGrabbed("releaseEquip"); return; } - if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.bumperSqueezed()) { - this.setState(STATE_CONTINUE_EQUIP_BD); + if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.thumbPressed()) { + this.setState(STATE_WAITING_FOR_THUMB_RELEASE); this.callEntityMethodOnGrabbed("releaseGrab"); this.callEntityMethodOnGrabbed("startEquip"); return; @@ -1479,14 +1513,14 @@ function MyController(hand) { } }; - this.waitingForBumperRelease = function() { - if (this.bumperReleased()) { - this.setState(STATE_RELEASE); + this.waitingForThumbRelease = function() { + if (this.thumbReleased() && this.triggerSmoothedReleased()) { + this.setState(STATE_EQUIP); } }; this.nearTrigger = function() { - if (this.triggerSmoothedReleased()) { + if (this.triggerSmoothedReleased() && this.bumperReleased()) { this.setState(STATE_RELEASE); this.callEntityMethodOnGrabbed("stopNearTrigger"); return; @@ -1496,7 +1530,7 @@ function MyController(hand) { }; this.farTrigger = function() { - if (this.triggerSmoothedReleased()) { + if (this.triggerSmoothedReleased() && this.bumperReleased()) { this.setState(STATE_RELEASE); this.callEntityMethodOnGrabbed("stopFarTrigger"); return; @@ -1506,7 +1540,7 @@ function MyController(hand) { }; this.continueNearTrigger = function() { - if (this.triggerSmoothedReleased()) { + if (this.triggerSmoothedReleased() && this.bumperReleased()) { this.setState(STATE_RELEASE); this.callEntityMethodOnGrabbed("stopNearTrigger"); return; @@ -1515,7 +1549,7 @@ function MyController(hand) { }; this.continueFarTrigger = function() { - if (this.triggerSmoothedReleased()) { + if (this.triggerSmoothedReleased() && this.bumperReleased()) { this.setState(STATE_RELEASE); this.callEntityMethodOnGrabbed("stopFarTrigger"); return; @@ -1794,8 +1828,9 @@ function MyController(hand) { this.checkNewlyLoaded = function(loadedEntityID) { if (this.state == STATE_OFF || - this.state == STATE_SEARCHING || - this.state == STATE_EQUIP_SEARCHING) { + this.state == STATE_SEARCHING // || + // this.state == STATE_EQUIP_SEARCHING + ) { var loadedProps = Entities.getEntityProperties(loadedEntityID); if (loadedProps.parentID != MyAvatar.sessionUUID) { return; @@ -1827,6 +1862,9 @@ mapping.from([Controller.Standard.LT]).peek().to(leftController.triggerPress); mapping.from([Controller.Standard.RB]).peek().to(rightController.bumperPress); mapping.from([Controller.Standard.LB]).peek().to(leftController.bumperPress); +mapping.from([Controller.Standard.LeftPrimaryThumb]).peek().to(leftController.thumbPress); +mapping.from([Controller.Standard.RightPrimaryThumb]).peek().to(rightController.thumbPress); + Controller.enableMapping(MAPPING_NAME); //the section below allows the grab script to listen for messages that disable either one or both hands. useful for two handed items From 8b5b6f5a207a5f2aab682a7fe2a2d5bdcf7bca71 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 14 Mar 2016 16:39:09 -0700 Subject: [PATCH 02/16] added HOLD state which uses presets but isn't persistent when button is released --- examples/controllers/handControllerGrab.js | 225 ++++++++++++++------- 1 file changed, 148 insertions(+), 77 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index e6314a5183..a13bd62231 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -154,20 +154,22 @@ var USE_POINTLIGHT = false; // states for the state machine var STATE_OFF = 0; var STATE_SEARCHING = 1; -var STATE_DISTANCE_HOLDING = 2; -var STATE_CONTINUE_DISTANCE_HOLDING = 3; -var STATE_NEAR_GRABBING = 4; -var STATE_CONTINUE_NEAR_GRABBING = 5; -var STATE_NEAR_TRIGGER = 6; -var STATE_CONTINUE_NEAR_TRIGGER = 7; -var STATE_FAR_TRIGGER = 8; -var STATE_CONTINUE_FAR_TRIGGER = 9; -var STATE_RELEASE = 10; -// var STATE_EQUIP_SEARCHING = 11; -var STATE_EQUIP = 12 -// var STATE_CONTINUE_EQUIP_BD = 13; // equip while bumper is still held down -var STATE_CONTINUE_EQUIP = 14; -var STATE_WAITING_FOR_THUMB_RELEASE = 15; +var STATE_HOLD_SEARCHING = 2; +var STATE_DISTANCE_HOLDING = 3; +var STATE_CONTINUE_DISTANCE_HOLDING = 4; +var STATE_NEAR_GRABBING = 5; +var STATE_CONTINUE_NEAR_GRABBING = 6; +var STATE_NEAR_TRIGGER = 7; +var STATE_CONTINUE_NEAR_TRIGGER = 8; +var STATE_FAR_TRIGGER = 9; +var STATE_CONTINUE_FAR_TRIGGER = 10; +var STATE_RELEASE = 11; +var STATE_EQUIP = 12; +var STATE_HOLD = 13; +var STATE_CONTINUE_HOLD = 14; +var STATE_CONTINUE_EQUIP = 15; +var STATE_WAITING_FOR_RELEASE_THUMB_RELEASE = 16; +var STATE_WAITING_FOR_EQUIP_THUMB_RELEASE = 17; // "collidesWith" is specified by comma-separated list of group names // the possible group names are: static, dynamic, kinematic, myAvatar, otherAvatar @@ -183,6 +185,8 @@ function stateToName(state) { return "off"; case STATE_SEARCHING: return "searching"; + case STATE_HOLD_SEARCHING: + return "hold_searching"; case STATE_DISTANCE_HOLDING: return "distance_holding"; case STATE_CONTINUE_DISTANCE_HOLDING: @@ -201,16 +205,18 @@ function stateToName(state) { return "continue_far_trigger"; case STATE_RELEASE: return "release"; - // case STATE_EQUIP_SEARCHING: - // return "equip_searching"; case STATE_EQUIP: return "equip"; - // case STATE_CONTINUE_EQUIP_BD: - // return "continue_equip_bd"; + case STATE_HOLD: + return "hold"; + case STATE_CONTINUE_HOLD: + return "continue_hold"; case STATE_CONTINUE_EQUIP: return "continue_equip"; - case STATE_WAITING_FOR_THUMB_RELEASE: - return "waiting_for_thumb_release"; + case STATE_WAITING_FOR_EQUIP_THUMB_RELEASE: + return "waiting_for_equip_thumb_release"; + case STATE_WAITING_FOR_RELEASE_THUMB_RELEASE: + return "waiting_for_release_thumb_release"; } return "unknown"; @@ -261,10 +267,13 @@ function MyController(hand) { this.grabbedEntity = null; // on this entity. this.state = STATE_OFF; this.pointer = null; // entity-id of line object + this.entityActivated = false; + this.triggerValue = 0; // rolling average of trigger value this.rawTriggerValue = 0; this.rawBumperValue = 0; this.rawThumbValue = 0; + //for visualizations this.overlayLine = null; this.particleBeamObject = null; @@ -298,11 +307,9 @@ function MyController(hand) { this.touchTest(); break; case STATE_SEARCHING: + case STATE_HOLD_SEARCHING: this.search(); break; - // case STATE_EQUIP_SEARCHING: - // this.search(); - // break; case STATE_DISTANCE_HOLDING: this.distanceHolding(); break; @@ -311,13 +318,17 @@ function MyController(hand) { break; case STATE_NEAR_GRABBING: case STATE_EQUIP: + case STATE_HOLD: this.nearGrabbing(); break; - case STATE_WAITING_FOR_THUMB_RELEASE: - this.waitingForThumbRelease(); + case STATE_WAITING_FOR_EQUIP_THUMB_RELEASE: + this.waitingForEquipThumbRelease(); + break; + case STATE_WAITING_FOR_RELEASE_THUMB_RELEASE: + this.waitingForReleaseThumbRelease(); break; case STATE_CONTINUE_NEAR_GRABBING: - // case STATE_CONTINUE_EQUIP_BD: + case STATE_CONTINUE_HOLD: case STATE_CONTINUE_EQUIP: this.continueNearGrabbing(); break; @@ -812,11 +823,11 @@ function MyController(hand) { this.lastPickTime = 0; var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; this.startingHandRotation = Controller.getPoseValue(controllerHandInput).rotation; - if (this.triggerSmoothedSqueezed() || this.bumperSqueezed()) { + if (this.triggerSmoothedSqueezed()) { this.setState(STATE_SEARCHING); - } // else { - // this.setState(STATE_EQUIP_SEARCHING); - // } + } else if (this.bumperSqueezed()) { + this.setState(STATE_HOLD_SEARCHING); + } } }; @@ -827,7 +838,11 @@ function MyController(hand) { this.checkForStrayChildren(); - if (this.state == STATE_SEARCHING && this.triggerSmoothedReleased() && this.bumperReleased()) { + if (this.state == STATE_SEARCHING && this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + return; + } + if (this.state == STATE_HOLD_SEARCHING && this.bumperReleased()) { this.setState(STATE_RELEASE); return; } @@ -959,13 +974,13 @@ function MyController(hand) { } continue; } - // if (propsForCandidate.parentID != NULL_UUID && this.state == STATE_EQUIP_SEARCHING) { - // // don't allow a double-equip - // if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) { - // print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': it's a child"); - // } - // continue; - // } + if (propsForCandidate.parentID != NULL_UUID && this.state == STATE_HOLD_SEARCHING) { + // don't allow a double-equip + if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) { + print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': it's a child"); + } + continue; + } if (this.state == STATE_SEARCHING && !isPhysical && distance > NEAR_PICK_MAX_DISTANCE && !near && !grabbableDataForCandidate.wantsTrigger) { @@ -997,11 +1012,14 @@ function MyController(hand) { var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, {}); var refCount = ("refCount" in grabData) ? grabData.refCount : 0; if (near && (refCount < 1 || entityHasActions(this.grabbedEntity))) { - this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP); + if (this.state == STATE_SEARCHING) { + this.setState(STATE_NEAR_GRABBING); + } else { // (this.state == STATE_HOLD_SEARCHING) + this.setState(STATE_HOLD); + } return; } // far grab or equip with action - // if ((isPhysical || this.state == STATE_EQUIP_SEARCHING) && !near) { if (isPhysical && !near) { if (entityIsGrabbedByOther(this.grabbedEntity)) { // don't distance grab something that is already grabbed. @@ -1024,7 +1042,12 @@ function MyController(hand) { FAR_TO_NEAR_GRAB_PADDING_FACTOR); } // this.setState(this.state == STATE_SEARCHING ? STATE_DISTANCE_HOLDING : STATE_EQUIP); - this.setState(STATE_DISTANCE_HOLDING); + if (this.state == STATE_SEARCHING) { + this.setState(STATE_DISTANCE_HOLDING); + } else { // (this.state == STATE_HOLD_SEARCHING) + this.setState(STATE_HOLD); + } + this.searchSphereOff(); return; } @@ -1032,7 +1055,11 @@ function MyController(hand) { // else this thing isn't physical. grab it by reparenting it (but not if we've already // grabbed it). if (refCount < 1) { - this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP); + if (this.state == STATE_SEARCHING) { + this.setState(STATE_NEAR_GRABBING); + } else { // this.state == STATE_HOLD_SEARCHING) + this.setState(STATE_HOLD); + } return; } else { // it's not physical and it's already held via parenting. go ahead and grab it, but @@ -1041,7 +1068,11 @@ function MyController(hand) { this.doubleParentGrab = true; this.previousParentID = props.parentID; this.previousParentJointIndex = props.parentJointIndex; - this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP); + if (this.state == STATE_SEARCHING) { + this.setState(STATE_NEAR_GRABBING); + } else { // (this.state == STATE_HOLD_SEARCHING) + this.setState(STATE_HOLD); + } return; } if (WANT_DEBUG_SEARCH_NAME && props.name == WANT_DEBUG_SEARCH_NAME) { @@ -1138,10 +1169,12 @@ function MyController(hand) { this.heartBeat(this.grabbedEntity); // controller pose is in avatar frame - var avatarControllerPose = Controller.getPoseValue((this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand); + var avatarControllerPose = Controller.getPoseValue((this.hand === RIGHT_HAND) ? + Controller.Standard.RightHand : Controller.Standard.LeftHand); // transform it into world frame - var controllerPosition = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation)); + var controllerPosition = Vec3.sum(MyAvatar.position, + Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation)); var controllerRotation = Quat.multiply(MyAvatar.orientation, avatarControllerPose.rotation); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); @@ -1333,7 +1366,12 @@ function MyController(hand) { this.nearGrabbing = function() { var now = Date.now(); - if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased() && this.bumperReleased()) { + if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + this.callEntityMethodOnGrabbed("releaseGrab"); + return; + } + if (this.state == STATE_HOLD && this.bumperReleased()) { this.setState(STATE_RELEASE); this.callEntityMethodOnGrabbed("releaseGrab"); return; @@ -1342,6 +1380,12 @@ function MyController(hand) { this.lineOff(); this.overlayLineOff(); + if (this.entityActivated) { + var saveGrabbedID = this.grabbedEntity; + this.release(); + this.grabbedEntity = saveGrabbedID; + } + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); this.activateEntity(this.grabbedEntity, grabbedProperties, false); if (grabbedProperties.dynamic && NEAR_GRABBING_KINEMATIC) { @@ -1355,7 +1399,7 @@ function MyController(hand) { var handPosition = this.getHandPosition(); var hasPresetPosition = false; - if (this.state == STATE_EQUIP && this.hasPresetOffsets()) { + if ((this.state == STATE_EQUIP || this.state == STATE_HOLD) && this.hasPresetOffsets()) { var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); // if an object is "equipped" and has a predefined offset, use it. this.ignoreIK = grabbableData.ignoreIK ? grabbableData.ignoreIK : false; @@ -1371,7 +1415,7 @@ function MyController(hand) { var currentObjectPosition = grabbedProperties.position; var offset = Vec3.subtract(currentObjectPosition, handPosition); this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); - if (this.temporaryPositionOffset && this.state != STATE_NEAR_GRABBING) { + if (this.temporaryPositionOffset && (this.state == STATE_EQUIP || this.state == STATE_HOLD)) { this.offsetPosition = this.temporaryPositionOffset; hasPresetPosition = true; } @@ -1385,11 +1429,11 @@ function MyController(hand) { } } else { // grab entity via parenting - if (this.actionID) { - var saveGrabbedID = this.grabbedEntity; - this.release(); - this.grabbedEntity = saveGrabbedID; - } + // if (this.actionID) { + // var saveGrabbedID = this.grabbedEntity; + // this.release(); + // this.grabbedEntity = saveGrabbedID; + // } this.actionID = null; var handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); reparentProps = { @@ -1407,17 +1451,22 @@ function MyController(hand) { })); } - this.callEntityMethodOnGrabbed(this.state == STATE_NEAR_GRABBING ? "startNearGrab" : "startEquip"); + if (this.state == STATE_NEAR_GRABBING) { + this.callEntityMethodOnGrabbed("startNearGrab"); + } else { // this.state == STATE_EQUIP || this.state == STATE_HOLD + this.callEntityMethodOnGrabbed("startEquip"); + } - // if (this.state == STATE_NEAR_GRABBING) { - // // near grabbing - // this.setState(STATE_CONTINUE_NEAR_GRABBING); - // } else { - // // equipping - // this.setState(STATE_CONTINUE_EQUIP_BD); - // } - - this.setState(this.state == STATE_NEAR_GRABBING ? STATE_CONTINUE_NEAR_GRABBING : STATE_CONTINUE_EQUIP); + if (this.state == STATE_NEAR_GRABBING) { + // near grabbing + this.setState(STATE_CONTINUE_NEAR_GRABBING); + } else if (this.state == STATE_HOLD) { + // holding + this.setState(STATE_CONTINUE_HOLD); + } else { // (this.state == STATE_EQUIP) + // equipping + this.setState(STATE_CONTINUE_EQUIP); + } this.currentHandControllerTipPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition; @@ -1425,27 +1474,31 @@ function MyController(hand) { }; this.continueNearGrabbing = function() { - if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.triggerSmoothedReleased() && this.bumperReleased()) { + if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.triggerSmoothedReleased()) { this.setState(STATE_RELEASE); this.callEntityMethodOnGrabbed("releaseGrab"); return; } - // if (this.state == STATE_CONTINUE_EQUIP_BD && this.thumbReleased()) { - // this.setState(STATE_EQUIP); - // return; - // } - if (this.state == STATE_CONTINUE_EQUIP && this.thumbPressed()) { - // this.setState(STATE_WAITING_FOR_THUMB_RELEASE); + if (this.state == STATE_CONTINUE_HOLD && this.bumperReleased()) { this.setState(STATE_RELEASE); this.callEntityMethodOnGrabbed("releaseEquip"); return; } + if (this.state == STATE_CONTINUE_EQUIP && this.thumbPressed()) { + this.setState(STATE_WAITING_FOR_RELEASE_THUMB_RELEASE); + this.callEntityMethodOnGrabbed("releaseEquip"); + return; + } if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.thumbPressed()) { - this.setState(STATE_WAITING_FOR_THUMB_RELEASE); + this.setState(STATE_WAITING_FOR_EQUIP_THUMB_RELEASE); this.callEntityMethodOnGrabbed("releaseGrab"); this.callEntityMethodOnGrabbed("startEquip"); return; } + if (this.state == STATE_CONTINUE_HOLD && this.thumbPressed()) { + this.setState(STATE_WAITING_FOR_EQUIP_THUMB_RELEASE); + return; + } this.heartBeat(this.grabbedEntity); @@ -1463,7 +1516,11 @@ function MyController(hand) { print("handControllerGrab -- autoreleasing held or equipped item because it is far from hand." + props.parentID + " " + vec3toStr(props.position)); this.setState(STATE_RELEASE); - this.callEntityMethodOnGrabbed(this.state == STATE_NEAR_GRABBING ? "releaseGrab" : "releaseEquip"); + if (this.state == STATE_CONTINUE_NEAR_GRABBING) { + this.callEntityMethodOnGrabbed("releaseGrab"); + } else { // (this.state == STATE_CONTINUE_EQUIP || this.state == STATE_CONTINUE_HOLD) + this.callEntityMethodOnGrabbed("releaseEquip"); + } return; } @@ -1484,7 +1541,7 @@ function MyController(hand) { this.currentObjectTime = now; var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, {}); - if (this.state === STATE_CONTINUE_EQUIP) { + if (this.state === STATE_CONTINUE_EQUIP || this.state === STATE_CONTINUE_HOLD) { this.callEntityMethodOnGrabbed("continueEquip"); } if (this.state == STATE_CONTINUE_NEAR_GRABBING) { @@ -1513,11 +1570,16 @@ function MyController(hand) { } }; - this.waitingForThumbRelease = function() { + this.waitingForEquipThumbRelease = function() { if (this.thumbReleased() && this.triggerSmoothedReleased()) { this.setState(STATE_EQUIP); } }; + this.waitingForReleaseThumbRelease = function() { + if (this.thumbReleased() && this.triggerSmoothedReleased()) { + this.setState(STATE_RELEASE); + } + }; this.nearTrigger = function() { if (this.triggerSmoothedReleased() && this.bumperReleased()) { @@ -1707,6 +1769,11 @@ function MyController(hand) { }; this.activateEntity = function(entityID, grabbedProperties, wasLoaded) { + if (this.entityActivated) { + return; + } + this.entityActivated = true; + var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); var now = Date.now(); @@ -1775,6 +1842,11 @@ function MyController(hand) { } this.deactivateEntity = function(entityID, noVelocity) { + if (!this.entityActivated) { + return; + } + this.entityActivated = false; + var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); if (data && data["refCount"]) { data["refCount"] = data["refCount"] - 1; @@ -1828,9 +1900,8 @@ function MyController(hand) { this.checkNewlyLoaded = function(loadedEntityID) { if (this.state == STATE_OFF || - this.state == STATE_SEARCHING // || - // this.state == STATE_EQUIP_SEARCHING - ) { + this.state == STATE_SEARCHING || + this.state == STATE_HOLD_SEARCHING) { var loadedProps = Entities.getEntityProperties(loadedEntityID); if (loadedProps.parentID != MyAvatar.sessionUUID) { return; From 9df8c4e969461c615a3403ac5e4bf95943237517 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 15 Mar 2016 09:00:01 -0700 Subject: [PATCH 03/16] add velocity argument to release --- examples/controllers/handControllerGrab.js | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index a13bd62231..60dc4080ea 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -345,7 +345,7 @@ function MyController(hand) { this.continueFarTrigger(); break; case STATE_RELEASE: - this.release(); + this.release(false); break; } }; @@ -1382,7 +1382,7 @@ function MyController(hand) { if (this.entityActivated) { var saveGrabbedID = this.grabbedEntity; - this.release(); + this.release(true); this.grabbedEntity = saveGrabbedID; } @@ -1429,11 +1429,6 @@ function MyController(hand) { } } else { // grab entity via parenting - // if (this.actionID) { - // var saveGrabbedID = this.grabbedEntity; - // this.release(); - // this.grabbedEntity = saveGrabbedID; - // } this.actionID = null; var handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); reparentProps = { @@ -1708,12 +1703,10 @@ function MyController(hand) { Entities.callEntityMethod(entityID, "stopTouch", args); }; - this.release = function() { - + this.release = function(noVelocity) { this.turnLightsOff(); this.turnOffVisualizations(); - var noVelocity = false; if (this.grabbedEntity !== null) { if (this.actionID !== null) { Entities.deleteAction(this.grabbedEntity, this.actionID); @@ -1744,7 +1737,7 @@ function MyController(hand) { }; this.cleanup = function() { - this.release(); + this.release(false); Entities.deleteEntity(this.particleBeamObject); Entities.deleteEntity(this.spotLight); Entities.deleteEntity(this.pointLight); From 2b73b2c6d8f8846a97aed48fed71dcfee6907476 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 15 Mar 2016 13:50:23 -0700 Subject: [PATCH 04/16] debugging --- examples/controllers/handControllerGrab.js | 32 +++++++++++++++------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 60dc4080ea..b2766100e3 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -345,7 +345,7 @@ function MyController(hand) { this.continueFarTrigger(); break; case STATE_RELEASE: - this.release(false); + this.release(); break; } }; @@ -1013,6 +1013,7 @@ function MyController(hand) { var refCount = ("refCount" in grabData) ? grabData.refCount : 0; if (near && (refCount < 1 || entityHasActions(this.grabbedEntity))) { if (this.state == STATE_SEARCHING) { + print("HERE: " + (this.hand === RIGHT_HAND ? "RightHand" : "LeftHand")); this.setState(STATE_NEAR_GRABBING); } else { // (this.state == STATE_HOLD_SEARCHING) this.setState(STATE_HOLD); @@ -1381,18 +1382,20 @@ function MyController(hand) { this.overlayLineOff(); if (this.entityActivated) { + print("HERE: release before grab " + (this.hand === RIGHT_HAND ? "RightHand" : "LeftHand")); var saveGrabbedID = this.grabbedEntity; - this.release(true); + this.release(); this.grabbedEntity = saveGrabbedID; } var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); this.activateEntity(this.grabbedEntity, grabbedProperties, false); - if (grabbedProperties.dynamic && NEAR_GRABBING_KINEMATIC) { - Entities.editEntity(this.grabbedEntity, { - dynamic: false - }); - } + // if (grabbedProperties.dynamic && NEAR_GRABBING_KINEMATIC) { + // Entities.editEntity(this.grabbedEntity, { + // velocity: {x: 0, y: 0, z: 0}, + // dynamic: false + // }); + // } // var handRotation = this.getHandRotation(); var handRotation = (this.hand === RIGHT_HAND) ? MyAvatar.getRightPalmRotation() : MyAvatar.getLeftPalmRotation(); @@ -1421,13 +1424,15 @@ function MyController(hand) { } } - var isPhysical = this.propsArePhysical(grabbedProperties); // || entityHasActions(this.grabbedEntity); + var isPhysical = this.propsArePhysical(grabbedProperties) || entityHasActions(this.grabbedEntity); if (isPhysical && this.state == STATE_NEAR_GRABBING) { + print("HERE: setting up hold action " + (this.hand === RIGHT_HAND ? "RightHand" : "LeftHand")); // grab entity via action if (!this.setupHoldAction()) { return; } } else { + print("HERE: parenting " + (this.hand === RIGHT_HAND ? "RightHand" : "LeftHand")); // grab entity via parenting this.actionID = null; var handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); @@ -1446,6 +1451,12 @@ function MyController(hand) { })); } + Entities.editEntity(this.grabbedEntity, { + velocity: {x: 0, y: 0, z: 0}, + angularVelocity: {x: 0, y: 0, z: 0}, + dynamic: false + }); + if (this.state == STATE_NEAR_GRABBING) { this.callEntityMethodOnGrabbed("startNearGrab"); } else { // this.state == STATE_EQUIP || this.state == STATE_HOLD @@ -1703,10 +1714,11 @@ function MyController(hand) { Entities.callEntityMethod(entityID, "stopTouch", args); }; - this.release = function(noVelocity) { + this.release = function() { this.turnLightsOff(); this.turnOffVisualizations(); + var noVelocity = false; if (this.grabbedEntity !== null) { if (this.actionID !== null) { Entities.deleteAction(this.grabbedEntity, this.actionID); @@ -1737,7 +1749,7 @@ function MyController(hand) { }; this.cleanup = function() { - this.release(false); + this.release(); Entities.deleteEntity(this.particleBeamObject); Entities.deleteEntity(this.spotLight); Entities.deleteEntity(this.pointLight); From 40f30b9141cfafdc30c25f296a89349e717047b3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 30 Mar 2016 17:25:53 -0700 Subject: [PATCH 05/16] get rid of distance equip --- examples/controllers/handControllerGrab.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index b2766100e3..f66926447c 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1008,7 +1008,7 @@ function MyController(hand) { this.setState(near ? STATE_NEAR_TRIGGER : STATE_FAR_TRIGGER); return; } - // near grab or equip with action + // near grab with action or equip var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, {}); var refCount = ("refCount" in grabData) ? grabData.refCount : 0; if (near && (refCount < 1 || entityHasActions(this.grabbedEntity))) { @@ -1020,7 +1020,7 @@ function MyController(hand) { } return; } - // far grab or equip with action + // far grab if (isPhysical && !near) { if (entityIsGrabbedByOther(this.grabbedEntity)) { // don't distance grab something that is already grabbed. @@ -1042,12 +1042,7 @@ function MyController(hand) { intersectionPointToCenterDistance * FAR_TO_NEAR_GRAB_PADDING_FACTOR); } - // this.setState(this.state == STATE_SEARCHING ? STATE_DISTANCE_HOLDING : STATE_EQUIP); - if (this.state == STATE_SEARCHING) { - this.setState(STATE_DISTANCE_HOLDING); - } else { // (this.state == STATE_HOLD_SEARCHING) - this.setState(STATE_HOLD); - } + this.setState(STATE_DISTANCE_HOLDING); this.searchSphereOff(); return; From a89735cb76bc431f71c8b0d285a4c5f81cafdc50 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 31 Mar 2016 13:29:31 -0700 Subject: [PATCH 06/16] integrate Philip's changes to distance grabbing --- examples/controllers/handControllerGrab.js | 43 +++++++++++++++------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index f66926447c..a2a44e6b41 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -858,9 +858,13 @@ function MyController(hand) { var currentHandRotation = Controller.getPoseValue(controllerHandInput).rotation; var handDeltaRotation = Quat.multiply(currentHandRotation, Quat.inverse(this.startingHandRotation)); + var avatarControllerPose = Controller.getPoseValue((this.hand === RIGHT_HAND) ? + Controller.Standard.RightHand : Controller.Standard.LeftHand); + var controllerRotation = Quat.multiply(MyAvatar.orientation, avatarControllerPose.rotation); + var distantPickRay = { origin: PICK_WITH_HAND_RAY ? handPosition : Camera.position, - direction: PICK_WITH_HAND_RAY ? Quat.getUp(this.getHandRotation()) : Vec3.mix(Quat.getUp(this.getHandRotation()), + direction: PICK_WITH_HAND_RAY ? Quat.getUp(controllerRotation) : Vec3.mix(Quat.getUp(controllerRotation), Quat.getFront(Camera.orientation), HAND_HEAD_MIX_RATIO), length: PICK_MAX_DISTANCE @@ -1118,6 +1122,9 @@ function MyController(hand) { this.currentObjectTime = now; this.currentCameraOrientation = Camera.orientation; + this.grabRadius = Vec3.distance(this.currentObjectPosition, controllerPosition); + this.grabRadialVelocity = 0.0; + // compute a constant based on the initial conditions which we use below to exagerate hand motion onto the held object this.radiusScalar = Math.log(Vec3.distance(this.currentObjectPosition, controllerPosition) + 1.0); if (this.radiusScalar < 1.0) { @@ -1175,17 +1182,8 @@ function MyController(hand) { var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - // // switch from far grab to near equip - // if (this.state == STATE_CONTINUE_DISTANCE_HOLDING && this.bumperSqueezed() && - // this.hasPresetOffsets()) { - // var saveGrabbedID = this.grabbedEntity; - // this.release(); - // this.setState(STATE_EQUIP); - // this.grabbedEntity = saveGrabbedID; - // return; - // } - var now = Date.now(); + var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds this.currentObjectTime = now; // the action was set up on a previous call. update the targets. @@ -1206,7 +1204,7 @@ function MyController(hand) { // update the currentObject position and rotation. this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, handMoved); - this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); + // this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); this.callEntityMethodOnGrabbed("continueDistantGrab"); @@ -1216,6 +1214,25 @@ function MyController(hand) { var handControllerData = getEntityCustomData('handControllerKey', this.grabbedEntity, defaultMoveWithHeadData); + // Update radialVelocity + var lastVelocity = Vec3.subtract(controllerPosition, this.previousControllerPosition); + lastVelocity = Vec3.multiply(lastVelocity, 1.0 / deltaTime); + var newRadialVelocity = Vec3.dot(lastVelocity, + Vec3.normalize(Vec3.subtract(grabbedProperties.position, controllerPosition))); + + var VELOCITY_AVERAGING_TIME = 0.016; + this.grabRadialVelocity = (deltaTime / VELOCITY_AVERAGING_TIME) * newRadialVelocity + + (1.0 - (deltaTime / VELOCITY_AVERAGING_TIME)) * this.grabRadialVelocity; + + var RADIAL_GRAB_AMPLIFIER = 10.0; + if (Math.abs(this.grabRadialVelocity) > 0.0) { + this.grabRadius = this.grabRadius + (this.grabRadialVelocity * deltaTime * this.grabRadius * RADIAL_GRAB_AMPLIFIER); + } + + var newTargetPosition = Vec3.multiply(this.grabRadius, Quat.getUp(controllerRotation)); + newTargetPosition = Vec3.sum(newTargetPosition, controllerPosition); + + var objectToAvatar = Vec3.subtract(this.currentObjectPosition, MyAvatar.position); if (handControllerData.disableMoveWithHead !== true) { // mix in head motion @@ -1277,7 +1294,7 @@ function MyController(hand) { var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, this.currentObjectPosition)); var success = Entities.updateAction(this.grabbedEntity, this.actionID, { - targetPosition: targetPosition, + targetPosition: newTargetPosition, linearTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject), targetRotation: this.currentObjectRotation, angularTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject), From a8b4d1a379a47589b17536bc45f7cd9496d67015 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 31 Mar 2016 13:46:00 -0700 Subject: [PATCH 07/16] ray follows controller --- examples/controllers/handControllerGrab.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index a2a44e6b41..f1cf1877e6 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -856,6 +856,7 @@ function MyController(hand) { var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; var currentHandRotation = Controller.getPoseValue(controllerHandInput).rotation; + var currentControllerPosition = Controller.getPoseValue(controllerHandInput).position; var handDeltaRotation = Quat.multiply(currentHandRotation, Quat.inverse(this.startingHandRotation)); var avatarControllerPose = Controller.getPoseValue((this.hand === RIGHT_HAND) ? @@ -871,7 +872,7 @@ function MyController(hand) { }; var searchVisualizationPickRay = { - origin: handPosition, + origin: currentControllerPosition, direction: Quat.getUp(this.getHandRotation()), length: PICK_MAX_DISTANCE }; From 0e643a1555bbe9a6ba2214b33892e2db5825dd58 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 31 Mar 2016 16:26:54 -0700 Subject: [PATCH 08/16] debugging prints --- examples/controllers/handControllerGrab.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index f1cf1877e6..f0abae39d8 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -123,7 +123,8 @@ var GRABBABLE_PROPERTIES = [ "parentID", "parentJointIndex", "density", - "dimensions" + "dimensions", + "userData" ]; var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js @@ -1108,10 +1109,12 @@ function MyController(hand) { this.distanceHolding = function() { // controller pose is in avatar frame - var avatarControllerPose = Controller.getPoseValue((this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand); + var avatarControllerPose = + Controller.getPoseValue((this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand); // transform it into world frame - var controllerPosition = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation)); + var controllerPosition = Vec3.sum(MyAvatar.position, + Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation)); var controllerRotation = Quat.multiply(MyAvatar.orientation, avatarControllerPose.rotation); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); @@ -1127,7 +1130,7 @@ function MyController(hand) { this.grabRadialVelocity = 0.0; // compute a constant based on the initial conditions which we use below to exagerate hand motion onto the held object - this.radiusScalar = Math.log(Vec3.distance(this.currentObjectPosition, controllerPosition) + 1.0); + this.radiusScalar = Math.log(grabRadius + 1.0); if (this.radiusScalar < 1.0) { this.radiusScalar = 1.0; } @@ -1187,7 +1190,7 @@ function MyController(hand) { var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds this.currentObjectTime = now; - // the action was set up on a previous call. update the targets. + // the action was set up when this.distanceHolding was called. update the targets. var radius = Vec3.distance(this.currentObjectPosition, controllerPosition) * this.radiusScalar * DISTANCE_HOLDING_RADIUS_FACTOR; if (radius < 1.0) { @@ -1395,13 +1398,13 @@ function MyController(hand) { this.overlayLineOff(); if (this.entityActivated) { - print("HERE: release before grab " + (this.hand === RIGHT_HAND ? "RightHand" : "LeftHand")); var saveGrabbedID = this.grabbedEntity; this.release(); this.grabbedEntity = saveGrabbedID; } var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + print("USERDATA FOR " + this.grabbedEntity + " IS " + grabbedProperties.userData); this.activateEntity(this.grabbedEntity, grabbedProperties, false); // if (grabbedProperties.dynamic && NEAR_GRABBING_KINEMATIC) { // Entities.editEntity(this.grabbedEntity, { @@ -1416,6 +1419,7 @@ function MyController(hand) { var hasPresetPosition = false; if ((this.state == STATE_EQUIP || this.state == STATE_HOLD) && this.hasPresetOffsets()) { + print("HAS PRESET"); var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); // if an object is "equipped" and has a predefined offset, use it. this.ignoreIK = grabbableData.ignoreIK ? grabbableData.ignoreIK : false; @@ -1423,6 +1427,8 @@ function MyController(hand) { this.offsetRotation = this.getPresetRotation(); hasPresetPosition = true; } else { + print("NO PRESET"); + this.ignoreIK = false; var objectRotation = grabbedProperties.rotation; From e8d58483aa1276b63b67f6dcb66e3a2ecedf0dcb Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 31 Mar 2016 16:37:08 -0700 Subject: [PATCH 09/16] fix typo --- examples/controllers/handControllerGrab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index f0abae39d8..6a15c57232 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1130,7 +1130,7 @@ function MyController(hand) { this.grabRadialVelocity = 0.0; // compute a constant based on the initial conditions which we use below to exagerate hand motion onto the held object - this.radiusScalar = Math.log(grabRadius + 1.0); + this.radiusScalar = Math.log(this.grabRadius + 1.0); if (this.radiusScalar < 1.0) { this.radiusScalar = 1.0; } From 7c8050790b3060207b0dc52ac3cc33e65d1bf38f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 1 Apr 2016 14:20:36 -0700 Subject: [PATCH 10/16] set velocity when something is un-parent-grabbed --- examples/controllers/handControllerGrab.js | 18 +++++++++++++++++- examples/defaultScripts.js | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 6a15c57232..d2260012f3 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1562,6 +1562,10 @@ function MyController(hand) { var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds + if (deltaTime > 0.0) { + this.currentVelocity = Vec3.multiply(deltaPosition, 1.0 / deltaTime); + } + this.currentHandControllerTipPosition = handControllerPosition; this.currentObjectTime = now; @@ -1886,9 +1890,21 @@ function MyController(hand) { // things that are held by parenting and dropped with no velocity will end up as "static" in bullet. If // it looks like the dropped thing should fall, give it a little velocity. - var parentID = Entities.getEntityProperties(entityID, ["parentID"]).parentID; + var props = Entities.getEntityProperties(entityID, ["parentID", "velocity"]) + var parentID = props.parentID; var forceVelocity = false; + + deactiveProps["velocity"] = props.velocity; + if (parentID != NULL_UUID) { + // TODO: EntityScriptingInterface::convertLocationToScriptSemantics should be setting up + // props.velocity to be a world-frame velocity and localVelocity to be vs parent. Until that + // is done, we use a measured velocity here so that things held via a bumper-grab / parenting-grab + // can be thrown. + deactiveProps["velocity"] = this.currentVelocity; + } + if (!noVelocity && + vec3equal(deactiveProps.velocity, ZERO_VEC) && parentID == MyAvatar.sessionUUID && Vec3.length(data["gravity"]) > 0.0 && data["dynamic"] && diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index ab3ff956d4..65c4fb315b 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -20,5 +20,5 @@ Script.load("controllers/squeezeHands.js"); Script.load("grab.js"); Script.load("directory.js"); Script.load("dialTone.js"); -Script.load("attachedEntitiesManager.js"); +// Script.load("attachedEntitiesManager.js"); Script.load("depthReticle.js"); From eb22b5e4a7ba405e5162cdb075a30ba0b9052adc Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 1 Apr 2016 14:50:42 -0700 Subject: [PATCH 11/16] velocity is now set when a parenting grab ends, though not angular velocity --- examples/controllers/handControllerGrab.js | 33 ++++++++++++++++++---- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index d2260012f3..5f462a1716 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1496,6 +1496,10 @@ function MyController(hand) { this.currentHandControllerTipPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition; this.currentObjectTime = Date.now(); + + this.currentObjectPosition = grabbedProperties.position; + this.currentObjectRotation = grabbedProperties.rotation; + this.currentVelocity = ZERO_VEC; }; this.continueNearGrabbing = function() { @@ -1563,7 +1567,9 @@ function MyController(hand) { var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds if (deltaTime > 0.0) { - this.currentVelocity = Vec3.multiply(deltaPosition, 1.0 / deltaTime); + var worldDeltaPosition = Vec3.subtract(props.position, this.currentObjectPosition); + this.currentVelocity = Vec3.multiply(worldDeltaPosition, 1.0 / deltaTime); + this.currentObjectPosition = props.position; } this.currentHandControllerTipPosition = handControllerPosition; @@ -1894,30 +1900,47 @@ function MyController(hand) { var parentID = props.parentID; var forceVelocity = false; - deactiveProps["velocity"] = props.velocity; - if (parentID != NULL_UUID) { + var doSetVelocity = false; + var setVelocityTo = ZERO_VEC; + + if (parentID != NULL_UUID && deactiveProps.parentID == NULL_UUID) { // TODO: EntityScriptingInterface::convertLocationToScriptSemantics should be setting up // props.velocity to be a world-frame velocity and localVelocity to be vs parent. Until that // is done, we use a measured velocity here so that things held via a bumper-grab / parenting-grab // can be thrown. - deactiveProps["velocity"] = this.currentVelocity; + print("SETTING doSetVelocity"); + doSetVelocity = true; + setVelocityTo = this.currentVelocity;; } if (!noVelocity && - vec3equal(deactiveProps.velocity, ZERO_VEC) && + !doSetVelocity && parentID == MyAvatar.sessionUUID && Vec3.length(data["gravity"]) > 0.0 && data["dynamic"] && data["parentID"] == NULL_UUID && !data["collisionless"]) { deactiveProps["velocity"] = {x: 0.0, y: 0.1, z: 0.0}; + doSetVelocity = false; } if (noVelocity) { deactiveProps["velocity"] = {x: 0.0, y: 0.0, z: 0.0}; deactiveProps["angularVelocity"] = {x: 0.0, y: 0.0, z: 0.0}; + doSetVelocity = false; } Entities.editEntity(entityID, deactiveProps); + + print("doSetVelocity = " + doSetVelocity); + if (doSetVelocity) { + // this is a continuation of the TODO above -- we shouldn't need to set this here. + // do this after the parent has been reset. setting this at the same time as + // the parent causes it to go off in the wrong direction. This is a bug that should + // be fixed. + print("SETTING VEL TO" + vec3toStr(setVelocityTo)); + Entities.editEntity(entityID, { "velocity": setVelocityTo }); + } + data = null; } else if (this.doubleParentGrab) { // we parent-grabbed this from another parent grab. try to put it back where we found it. From bfa2d8f40518c9df025cd1ec45bc64e95c0f0a8a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 1 Apr 2016 15:11:01 -0700 Subject: [PATCH 12/16] set angular velocity on parenting-grab release --- examples/controllers/handControllerGrab.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 5f462a1716..b7493ff8aa 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1500,6 +1500,7 @@ function MyController(hand) { this.currentObjectPosition = grabbedProperties.position; this.currentObjectRotation = grabbedProperties.rotation; this.currentVelocity = ZERO_VEC; + this.currentAngularVelocity = ZERO_VEC; }; this.continueNearGrabbing = function() { @@ -1531,7 +1532,7 @@ function MyController(hand) { this.heartBeat(this.grabbedEntity); - var props = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "parentID", "position"]); + var props = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "parentID", "position", "rotation"]); if (!props.position) { // server may have reset, taking our equipped entity with it. move back to "off" stte this.setState(STATE_RELEASE); @@ -1568,8 +1569,16 @@ function MyController(hand) { if (deltaTime > 0.0) { var worldDeltaPosition = Vec3.subtract(props.position, this.currentObjectPosition); + + var previousEulers = Quat.safeEulerAngles(this.currentObjectRotation); + var newEulers = Quat.safeEulerAngles(props.rotation); + var worldDeltaRotation = Vec3.subtract(newEulers, previousEulers); + this.currentVelocity = Vec3.multiply(worldDeltaPosition, 1.0 / deltaTime); + this.currentAngularVelocity = Vec3.multiply(worldDeltaRotation, Math.PI / (deltaTime * 180.0)); + this.currentObjectPosition = props.position; + this.currentObjectRotation = props.rotation; } this.currentHandControllerTipPosition = handControllerPosition; @@ -1901,16 +1910,12 @@ function MyController(hand) { var forceVelocity = false; var doSetVelocity = false; - var setVelocityTo = ZERO_VEC; - if (parentID != NULL_UUID && deactiveProps.parentID == NULL_UUID) { // TODO: EntityScriptingInterface::convertLocationToScriptSemantics should be setting up // props.velocity to be a world-frame velocity and localVelocity to be vs parent. Until that // is done, we use a measured velocity here so that things held via a bumper-grab / parenting-grab // can be thrown. - print("SETTING doSetVelocity"); doSetVelocity = true; - setVelocityTo = this.currentVelocity;; } if (!noVelocity && @@ -1931,14 +1936,15 @@ function MyController(hand) { Entities.editEntity(entityID, deactiveProps); - print("doSetVelocity = " + doSetVelocity); if (doSetVelocity) { // this is a continuation of the TODO above -- we shouldn't need to set this here. // do this after the parent has been reset. setting this at the same time as // the parent causes it to go off in the wrong direction. This is a bug that should // be fixed. - print("SETTING VEL TO" + vec3toStr(setVelocityTo)); - Entities.editEntity(entityID, { "velocity": setVelocityTo }); + Entities.editEntity(entityID, { + velocity: this.currentVelocity, + angularVelocity: this.currentAngularVelocity + }); } data = null; From f05f283b044a1f3ba1eeffc86edb86a93b2568f9 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 1 Apr 2016 16:58:17 -0700 Subject: [PATCH 13/16] disable setting of angular velocity upon release of a parenting grab --- examples/controllers/handControllerGrab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index b7493ff8aa..20e6eeb3fa 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1943,7 +1943,7 @@ function MyController(hand) { // be fixed. Entities.editEntity(entityID, { velocity: this.currentVelocity, - angularVelocity: this.currentAngularVelocity + // angularVelocity: this.currentAngularVelocity }); } From d9f5a0ccaf7ab19227b46d0c106f01dbf5512019 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 1 Apr 2016 17:17:21 -0700 Subject: [PATCH 14/16] experiment --- examples/controllers/handControllerGrab.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 20e6eeb3fa..59280af446 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1439,7 +1439,7 @@ function MyController(hand) { this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); if (this.temporaryPositionOffset && (this.state == STATE_EQUIP || this.state == STATE_HOLD)) { this.offsetPosition = this.temporaryPositionOffset; - hasPresetPosition = true; + // hasPresetPosition = true; } } @@ -1464,6 +1464,7 @@ function MyController(hand) { reparentProps["localRotation"] = this.offsetRotation; } Entities.editEntity(this.grabbedEntity, reparentProps); + Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ action: 'equip', grabbedEntity: this.grabbedEntity From 9942a5edd40dcd70628f2ceba2cd858c02006b15 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 1 Apr 2016 17:22:20 -0700 Subject: [PATCH 15/16] don't reset local offset during a hold --- examples/controllers/handControllerGrab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 59280af446..eb5c5bac6d 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1437,7 +1437,7 @@ function MyController(hand) { var currentObjectPosition = grabbedProperties.position; var offset = Vec3.subtract(currentObjectPosition, handPosition); this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); - if (this.temporaryPositionOffset && (this.state == STATE_EQUIP || this.state == STATE_HOLD)) { + if (this.temporaryPositionOffset && (this.state == STATE_EQUIP)) { this.offsetPosition = this.temporaryPositionOffset; // hasPresetPosition = true; } From 3f3efc425035c3fffe9b8de9fda17791f5373ff8 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 1 Apr 2016 17:55:12 -0700 Subject: [PATCH 16/16] remove debug prints --- examples/controllers/handControllerGrab.js | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index eb5c5bac6d..a3bed0a256 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -20,7 +20,7 @@ Script.include("../libraries/utils.js"); // add lines where the hand ray picking is happening // var WANT_DEBUG = false; -var WANT_DEBUG_STATE = true; +var WANT_DEBUG_STATE = false; var WANT_DEBUG_SEARCH_NAME = null; // @@ -1019,7 +1019,6 @@ function MyController(hand) { var refCount = ("refCount" in grabData) ? grabData.refCount : 0; if (near && (refCount < 1 || entityHasActions(this.grabbedEntity))) { if (this.state == STATE_SEARCHING) { - print("HERE: " + (this.hand === RIGHT_HAND ? "RightHand" : "LeftHand")); this.setState(STATE_NEAR_GRABBING); } else { // (this.state == STATE_HOLD_SEARCHING) this.setState(STATE_HOLD); @@ -1404,14 +1403,7 @@ function MyController(hand) { } var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - print("USERDATA FOR " + this.grabbedEntity + " IS " + grabbedProperties.userData); this.activateEntity(this.grabbedEntity, grabbedProperties, false); - // if (grabbedProperties.dynamic && NEAR_GRABBING_KINEMATIC) { - // Entities.editEntity(this.grabbedEntity, { - // velocity: {x: 0, y: 0, z: 0}, - // dynamic: false - // }); - // } // var handRotation = this.getHandRotation(); var handRotation = (this.hand === RIGHT_HAND) ? MyAvatar.getRightPalmRotation() : MyAvatar.getLeftPalmRotation(); @@ -1419,7 +1411,6 @@ function MyController(hand) { var hasPresetPosition = false; if ((this.state == STATE_EQUIP || this.state == STATE_HOLD) && this.hasPresetOffsets()) { - print("HAS PRESET"); var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); // if an object is "equipped" and has a predefined offset, use it. this.ignoreIK = grabbableData.ignoreIK ? grabbableData.ignoreIK : false; @@ -1427,8 +1418,6 @@ function MyController(hand) { this.offsetRotation = this.getPresetRotation(); hasPresetPosition = true; } else { - print("NO PRESET"); - this.ignoreIK = false; var objectRotation = grabbedProperties.rotation; @@ -1445,13 +1434,11 @@ function MyController(hand) { var isPhysical = this.propsArePhysical(grabbedProperties) || entityHasActions(this.grabbedEntity); if (isPhysical && this.state == STATE_NEAR_GRABBING) { - print("HERE: setting up hold action " + (this.hand === RIGHT_HAND ? "RightHand" : "LeftHand")); // grab entity via action if (!this.setupHoldAction()) { return; } } else { - print("HERE: parenting " + (this.hand === RIGHT_HAND ? "RightHand" : "LeftHand")); // grab entity via parenting this.actionID = null; var handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand");