From 7a8cee4cc3001650e02fc41a9254257551aa1a99 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 18 Sep 2015 14:13:08 -0700 Subject: [PATCH 1/4] fix release velocity so throwing things works reliably --- examples/controllers/handControllerGrab.js | 26 +++++++++++++--------- examples/libraries/utils.js | 7 ++++-- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index ed69d1844d..df90de1b67 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -18,7 +18,7 @@ Script.include("../libraries/utils.js"); // these tune time-averaging and "on" value for analog trigger // -var TRIGGER_SMOOTH_RATIO = 0.7; +var TRIGGER_SMOOTH_RATIO = 0.0; // 0.0 disables smoothing of trigger value var TRIGGER_ON_VALUE = 0.2; ///////////////////////////////////////////////////////////////// @@ -42,9 +42,9 @@ var LINE_LENGTH = 500; var GRAB_RADIUS = 0.3; // if the ray misses but an object is this close, it will still be selected var CLOSE_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position -var CLOSE_GRABBING_VELOCITY_SMOOTH_RATIO = 0.9; // adjust time-averaging of held object's velocity +var CLOSE_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. var CLOSE_PICK_MAX_DISTANCE = 0.6; // max length of pick-ray for close grabbing to be selected - +var RELEASE_VELOCITY_MULTIPLIER = 2.0; // affects throwing things ///////////////////////////////////////////////////////////////// // @@ -278,9 +278,8 @@ function controller(hand, triggerAction) { var objectRotation = grabbedProperties.rotation; var offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); - this.currentObjectPosition = grabbedProperties.position; - this.currentObjectTime = Date.now(); - var offset = Vec3.subtract(this.currentObjectPosition, handPosition); + currentObjectPosition = grabbedProperties.position; + var offset = Vec3.subtract(currentObjectPosition, handPosition); var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset); this.actionID = Entities.addAction("hold", this.grabbedEntity, { @@ -294,6 +293,9 @@ function controller(hand, triggerAction) { } else { this.state = STATE_CONTINUE_CLOSE_GRABBING; } + + this.currentHandControllerPosition = Controller.getSpatialControlPosition(this.palm); + this.currentObjectTime = Date.now(); } @@ -304,13 +306,13 @@ function controller(hand, triggerAction) { } // keep track of the measured velocity of the held object - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity); + var handControllerPosition = Controller.getSpatialControlPosition(this.palm); var now = Date.now(); - var deltaPosition = Vec3.subtract(grabbedProperties.position, this.currentObjectPosition); // meters + var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerPosition); // meters var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds - if (deltaTime > 0.0) { + if (deltaTime > 0.0 && !vec3equal(this.currentHandControllerPosition, handControllerPosition)) { var grabbedVelocity = Vec3.multiply(deltaPosition, 1.0 / deltaTime); // don't update grabbedVelocity if the trigger is off. the smoothing of the trigger // value would otherwise give the held object time to slow down. @@ -322,7 +324,7 @@ function controller(hand, triggerAction) { } } - this.currentObjectPosition = grabbedProperties.position; + this.currentHandControllerPosition = handControllerPosition; this.currentObjectTime = now; } @@ -336,7 +338,9 @@ function controller(hand, triggerAction) { // the action will tend to quickly bring an object's velocity to zero. now that // the action is gone, set the objects velocity to something the holder might expect. - Entities.editEntity(this.grabbedEntity, {velocity: this.grabbedVelocity}); + Entities.editEntity(this.grabbedEntity, + {velocity: Vec3.multiply(this.grabbedVelocity, RELEASE_VELOCITY_MULTIPLIER)} + ); this.grabbedVelocity = ZERO_VEC; this.grabbedEntity = null; diff --git a/examples/libraries/utils.js b/examples/libraries/utils.js index 1275975fd8..865a5a55b1 100644 --- a/examples/libraries/utils.js +++ b/examples/libraries/utils.js @@ -6,11 +6,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -vec3toStr = function (v, digits) { +vec3toStr = function(v, digits) { if (!digits) { digits = 3; } return "{ " + v.x.toFixed(digits) + ", " + v.y.toFixed(digits) + ", " + v.z.toFixed(digits)+ " }"; } +vec3equal = function(v0, v1) { + return (v0.x == v1.x) && (v0.y == v1.y) && (v0.z == v1.z); +} colorMix = function(colorA, colorB, mix) { var result = {}; @@ -175,4 +178,4 @@ pointInExtents = function(point, minPoint, maxPoint) { return (point.x >= minPoint.x && point.x <= maxPoint.x) && (point.y >= minPoint.y && point.y <= maxPoint.y) && (point.z >= minPoint.z && point.z <= maxPoint.z); -} \ No newline at end of file +} From d1a7aca7f091c38266b1924df4f05286e323b11c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 18 Sep 2015 14:45:49 -0700 Subject: [PATCH 2/4] add continue-distance-holding state. don't call callEntityMethod unless action creation works. increase distance-holding multiplier. --- examples/controllers/handControllerGrab.js | 105 +++++++++++---------- 1 file changed, 56 insertions(+), 49 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 554d5117ea..fc82e0c065 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -26,7 +26,7 @@ var TRIGGER_ON_VALUE = 0.2; // distant manipulation // -var DISTANCE_HOLDING_RADIUS_FACTOR = 4; // multiplied by distance between hand and object +var DISTANCE_HOLDING_RADIUS_FACTOR = 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_ROTATION_EXAGGERATION_FACTOR = 2.0; // object rotates this much more than hand did var NO_INTERSECT_COLOR = {red: 10, green: 10, blue: 255}; // line color when pick misses @@ -65,9 +65,10 @@ var LIFETIME = 10; // states for the state machine var STATE_SEARCHING = 0; var STATE_DISTANCE_HOLDING = 1; -var STATE_CLOSE_GRABBING = 2; -var STATE_CONTINUE_CLOSE_GRABBING = 3; -var STATE_RELEASE = 4; +var STATE_CONTINUE_DISTANCE_HOLDING = 2; +var STATE_CLOSE_GRABBING = 3; +var STATE_CONTINUE_CLOSE_GRABBING = 4; +var STATE_RELEASE = 5; var GRAB_USER_DATA_KEY = "grabKey"; @@ -90,7 +91,6 @@ function controller(hand, triggerAction) { this.state = 0; // 0 = searching, 1 = distanceHolding, 2 = closeGrabbing this.pointer = null; // entity-id of line object this.triggerValue = 0; // rolling average of trigger value - this.alreadyDistanceHolding = false; // FIXME - I'll leave it to Seth to potentially make this another state this.update = function() { switch(this.state) { @@ -100,6 +100,9 @@ function controller(hand, triggerAction) { case STATE_DISTANCE_HOLDING: this.distanceHolding(); break; + case STATE_CONTINUE_DISTANCE_HOLDING: + this.continueDistanceHolding(); + break; case STATE_CLOSE_GRABBING: this.closeGrabbing(); break; @@ -210,64 +213,68 @@ function controller(hand, triggerAction) { this.distanceHolding = function() { - if (!this.triggerSmoothedSqueezed()) { - this.state = STATE_RELEASE; - this.alreadyDistanceHolding = false; - return; + var handControllerPosition = Controller.getSpatialControlPosition(this.palm); + var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm)); + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position","rotation"]); + + // add the action and initialize some variables + this.currentObjectPosition = grabbedProperties.position; + this.currentObjectRotation = grabbedProperties.rotation; + this.handPreviousPosition = handControllerPosition; + this.handPreviousRotation = handRotation; + + this.actionID = Entities.addAction("spring", this.grabbedEntity, { + targetPosition: this.currentObjectPosition, + linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + targetRotation: this.currentObjectRotation, + angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME + }); + if (this.actionID == NULL_ACTION_ID) { + this.actionID = null; } - if (!this.alreadyDistanceHolding) { + if (this.actionID != null) { + this.state = STATE_CONTINUE_DISTANCE_HOLDING; this.activateEntity(this.grabbedEntity); - this.alreadyDistanceHolding = true; + Entities.callEntityMethod(this.grabbedEntity, "distanceHolding"); + } + } + + + this.continueDistanceHolding = function() { + if (!this.triggerSmoothedSqueezed()) { + this.state = STATE_RELEASE; + return; } var handPosition = this.getHandPosition(); var handControllerPosition = Controller.getSpatialControlPosition(this.palm); var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm)); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position","rotation"]); - Entities.callEntityMethod(this.grabbedEntity, "distanceHolding"); this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); - if (this.actionID === null) { - // first time here since trigger pulled -- add the action and initialize some variables - this.currentObjectPosition = grabbedProperties.position; - this.currentObjectRotation = grabbedProperties.rotation; - this.handPreviousPosition = handControllerPosition; - this.handPreviousRotation = handRotation; + // the action was set up on a previous call. update the targets. + var radius = Math.max(Vec3.distance(this.currentObjectPosition, + handControllerPosition) * DISTANCE_HOLDING_RADIUS_FACTOR, + DISTANCE_HOLDING_RADIUS_FACTOR); - this.actionID = Entities.addAction("spring", this.grabbedEntity, { - targetPosition: this.currentObjectPosition, - linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, - targetRotation: this.currentObjectRotation, - angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME - }); - if (this.actionID == NULL_ACTION_ID) { - this.actionID = null; - } - } else { - // the action was set up on a previous call. update the targets. - var radius = Math.max(Vec3.distance(this.currentObjectPosition, - handControllerPosition) * DISTANCE_HOLDING_RADIUS_FACTOR, - DISTANCE_HOLDING_RADIUS_FACTOR); + var handMoved = Vec3.subtract(handControllerPosition, this.handPreviousPosition); + this.handPreviousPosition = handControllerPosition; + var superHandMoved = Vec3.multiply(handMoved, radius); + this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, superHandMoved); - var handMoved = Vec3.subtract(handControllerPosition, this.handPreviousPosition); - this.handPreviousPosition = handControllerPosition; - var superHandMoved = Vec3.multiply(handMoved, radius); - this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, superHandMoved); + // this doubles hand rotation + var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, handRotation, + DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), + Quat.inverse(this.handPreviousRotation)); + this.handPreviousRotation = handRotation; + this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); - // this doubles hand rotation - var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, handRotation, - DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), - Quat.inverse(this.handPreviousRotation)); - this.handPreviousRotation = handRotation; - this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); - - Entities.updateAction(this.grabbedEntity, this.actionID, { - targetPosition: this.currentObjectPosition, linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, - targetRotation: this.currentObjectRotation, angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME - }); - } + Entities.updateAction(this.grabbedEntity, this.actionID, { + targetPosition: this.currentObjectPosition, linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + targetRotation: this.currentObjectRotation, angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME + }); } @@ -303,11 +310,11 @@ function controller(hand, triggerAction) { this.actionID = null; } else { this.state = STATE_CONTINUE_CLOSE_GRABBING; + Entities.callEntityMethod(this.grabbedEntity, "closeGrabbing"); } this.currentHandControllerPosition = Controller.getSpatialControlPosition(this.palm); this.currentObjectTime = Date.now(); - Entities.callEntityMethod(this.grabbedEntity, "closeGrabbing"); } From 612e906a443a7fc0f317252792210f78b28fcadb Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 18 Sep 2015 15:09:05 -0700 Subject: [PATCH 3/4] change the names of entityMethods which the grab script will call. adjust the release velocity multiplier --- examples/controllers/handControllerGrab.js | 63 +++++++++++++--------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index fc82e0c065..20b0003cc7 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -37,14 +37,14 @@ var LINE_LENGTH = 500; ///////////////////////////////////////////////////////////////// // -// close grabbing +// near grabbing // var GRAB_RADIUS = 0.3; // if the ray misses but an object is this close, it will still be selected -var CLOSE_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position -var CLOSE_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. -var CLOSE_PICK_MAX_DISTANCE = 0.6; // max length of pick-ray for close grabbing to be selected -var RELEASE_VELOCITY_MULTIPLIER = 2.0; // affects throwing things +var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position +var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. +var NEAR_PICK_MAX_DISTANCE = 0.6; // max length of pick-ray for close grabbing to be selected +var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things ///////////////////////////////////////////////////////////////// // @@ -66,8 +66,8 @@ var LIFETIME = 10; var STATE_SEARCHING = 0; var STATE_DISTANCE_HOLDING = 1; var STATE_CONTINUE_DISTANCE_HOLDING = 2; -var STATE_CLOSE_GRABBING = 3; -var STATE_CONTINUE_CLOSE_GRABBING = 4; +var STATE_NEAR_GRABBING = 3; +var STATE_CONTINUE_NEAR_GRABBING = 4; var STATE_RELEASE = 5; var GRAB_USER_DATA_KEY = "grabKey"; @@ -88,7 +88,7 @@ function controller(hand, triggerAction) { this.actionID = null; // action this script created... this.grabbedEntity = null; // on this entity. this.grabbedVelocity = ZERO_VEC; // rolling average of held object's velocity - this.state = 0; // 0 = searching, 1 = distanceHolding, 2 = closeGrabbing + this.state = 0; this.pointer = null; // entity-id of line object this.triggerValue = 0; // rolling average of trigger value @@ -103,11 +103,11 @@ function controller(hand, triggerAction) { case STATE_CONTINUE_DISTANCE_HOLDING: this.continueDistanceHolding(); break; - case STATE_CLOSE_GRABBING: - this.closeGrabbing(); + case STATE_NEAR_GRABBING: + this.nearGrabbing(); break; - case STATE_CONTINUE_CLOSE_GRABBING: - this.continueCloseGrabbing(); + case STATE_CONTINUE_NEAR_GRABBING: + this.continueNearGrabbing(); break; case STATE_RELEASE: this.release(); @@ -180,9 +180,9 @@ function controller(hand, triggerAction) { var handControllerPosition = Controller.getSpatialControlPosition(this.palm); var intersectionDistance = Vec3.distance(handControllerPosition, intersection.intersection); this.grabbedEntity = intersection.entityID; - if (intersectionDistance < CLOSE_PICK_MAX_DISTANCE) { + if (intersectionDistance < NEAR_PICK_MAX_DISTANCE) { // the hand is very close to the intersected object. go into close-grabbing mode. - this.state = STATE_CLOSE_GRABBING; + this.state = STATE_NEAR_GRABBING; } else { // the hand is far from the intersected object. go into distance-holding mode this.state = STATE_DISTANCE_HOLDING; @@ -206,7 +206,7 @@ function controller(hand, triggerAction) { if (this.grabbedEntity === null) { this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); } else { - this.state = STATE_CLOSE_GRABBING; + this.state = STATE_NEAR_GRABBING; } } } @@ -236,7 +236,13 @@ function controller(hand, triggerAction) { if (this.actionID != null) { this.state = STATE_CONTINUE_DISTANCE_HOLDING; this.activateEntity(this.grabbedEntity); - Entities.callEntityMethod(this.grabbedEntity, "distanceHolding"); + Entities.callEntityMethod(this.grabbedEntity, "startDistantGrab"); + + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } } } @@ -271,6 +277,8 @@ function controller(hand, triggerAction) { this.handPreviousRotation = handRotation; this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); + Entities.callEntityMethod(this.grabbedEntity, "continueDistantGrab"); + Entities.updateAction(this.grabbedEntity, this.actionID, { targetPosition: this.currentObjectPosition, linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, targetRotation: this.currentObjectRotation, angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME @@ -278,7 +286,7 @@ function controller(hand, triggerAction) { } - this.closeGrabbing = function() { + this.nearGrabbing = function() { if (!this.triggerSmoothedSqueezed()) { this.state = STATE_RELEASE; return; @@ -302,15 +310,20 @@ function controller(hand, triggerAction) { this.actionID = Entities.addAction("hold", this.grabbedEntity, { hand: this.hand == RIGHT_HAND ? "right" : "left", - timeScale: CLOSE_GRABBING_ACTION_TIMEFRAME, + timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, relativePosition: offsetPosition, relativeRotation: offsetRotation }); if (this.actionID == NULL_ACTION_ID) { this.actionID = null; } else { - this.state = STATE_CONTINUE_CLOSE_GRABBING; - Entities.callEntityMethod(this.grabbedEntity, "closeGrabbing"); + this.state = STATE_CONTINUE_NEAR_GRABBING; + Entities.callEntityMethod(this.grabbedEntity, "startNearGrab"); + if (this.hand === RIGHT_HAND) { + Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); + } else { + Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); + } } this.currentHandControllerPosition = Controller.getSpatialControlPosition(this.palm); @@ -318,7 +331,7 @@ function controller(hand, triggerAction) { } - this.continueCloseGrabbing = function() { + this.continueNearGrabbing = function() { if (!this.triggerSmoothedSqueezed()) { this.state = STATE_RELEASE; return; @@ -338,14 +351,14 @@ function controller(hand, triggerAction) { if (this.triggerSqueezed()) { this.grabbedVelocity = Vec3.sum(Vec3.multiply(this.grabbedVelocity, - (1.0 - CLOSE_GRABBING_VELOCITY_SMOOTH_RATIO)), - Vec3.multiply(grabbedVelocity, CLOSE_GRABBING_VELOCITY_SMOOTH_RATIO)); + (1.0 - NEAR_GRABBING_VELOCITY_SMOOTH_RATIO)), + Vec3.multiply(grabbedVelocity, NEAR_GRABBING_VELOCITY_SMOOTH_RATIO)); } } this.currentHandControllerPosition = handControllerPosition; this.currentObjectTime = now; - Entities.callEntityMethod(this.grabbedEntity, "continueCloseGrabbing"); + Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab"); } @@ -361,7 +374,7 @@ function controller(hand, triggerAction) { Entities.editEntity(this.grabbedEntity, {velocity: Vec3.multiply(this.grabbedVelocity, RELEASE_VELOCITY_MULTIPLIER)} ); - Entities.callEntityMethod(this.grabbedEntity, "release"); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); this.deactivateEntity(this.grabbedEntity); this.grabbedVelocity = ZERO_VEC; From 3d18edd9d10f646d08ae954a367a2e2b2b1272cf Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 18 Sep 2015 15:13:09 -0700 Subject: [PATCH 4/4] update detectGrabExample.js --- examples/entityScripts/detectGrabExample.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/examples/entityScripts/detectGrabExample.js b/examples/entityScripts/detectGrabExample.js index c84d3250cc..7e97572159 100644 --- a/examples/entityScripts/detectGrabExample.js +++ b/examples/entityScripts/detectGrabExample.js @@ -23,16 +23,27 @@ DetectGrabbed.prototype = { - distanceHolding: function () { - print("I am being distance held... entity:" + this.entityID); + setRightHand: function () { + print("I am being held in a right hand... entity:" + this.entityID); + }, + setLeftHand: function () { + print("I am being held in a left hand... entity:" + this.entityID); }, - closeGrabbing: function () { + startDistantGrab: function () { + print("I am being distance held... entity:" + this.entityID); + }, + continueDistantGrab: function () { + print("I continue to be distance held... entity:" + this.entityID); + }, + + startNearGrab: function () { print("I was just grabbed... entity:" + this.entityID); }, - continueCloseGrabbing: function () { + continueNearGrab: function () { print("I am still being grabbed... entity:" + this.entityID); }, + release: function () { print("I was released... entity:" + this.entityID); },