From 0e028fbac80cdd51121399f9aca1cc66d9dd3d0a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 23 Feb 2016 16:21:41 -0800 Subject: [PATCH 1/4] keep a heart-beat timestamp in userData of grabbed entities. if grabbing something with an out-of-date heartbeat, reset it before grabbing --- examples/controllers/handControllerGrab.js | 47 +++++++++++++++++++--- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 1fd6045fe0..ad5f1c3775 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -73,9 +73,7 @@ var PICK_MAX_DISTANCE = 500; // max length of pick-ray var GRAB_RADIUS = 0.06; // if the ray misses but an object is this close, it will still be selected 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.3; // max length of pick-ray for close grabbing to be selected -var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things 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 var SHOW_GRAB_SPHERE = false; // draw a green sphere to show the grab search position and size @@ -173,6 +171,10 @@ var STATE_WAITING_FOR_BUMPER_RELEASE = 15; var COLLIDES_WITH_WHILE_GRABBED = "dynamic,otherAvatar"; var COLLIDES_WITH_WHILE_MULTI_GRABBED = "dynamic"; +var HEART_BEAT_INTERVAL = 5; // seconds +var HEART_BEAT_TIMEOUT = 15; + + function stateToName(state) { switch (state) { case STATE_OFF: @@ -1103,6 +1105,8 @@ function MyController(hand) { return; } + this.heartBeat(this.grabbedEntity); + var handPosition = this.getHandPosition(); var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; @@ -1438,6 +1442,8 @@ function MyController(hand) { return; } + this.heartBeat(this.grabbedEntity); + var props = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "parentID", "position"]); if (props.parentID == MyAvatar.sessionUUID && Vec3.length(props.localPosition) > NEAR_PICK_MAX_DISTANCE * 2.0) { @@ -1670,19 +1676,40 @@ function MyController(hand) { Entities.deleteEntity(this.pointLight); }; + this.heartBeat = function(entityID) { + var now = Date.now(); + if (now - this.lastHeartBeat > HEART_BEAT_INTERVAL) { + var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); + data["heartBeat"] = now; + setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); + this.lastHeartBeat = now; + } + }; + + this.resetAbandonedGrab = function(entityID) { + print("cleaning up abandoned grab on " + entityID); + var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); + data["refCount"] = 1; + setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); + this.deactivateEntity(entityID, false); + }; + this.activateEntity = function(entityID, grabbedProperties, wasLoaded) { + print("activating: " + entityID + " " + (this.hand === RIGHT_HAND ? "right" : "left")); var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, entityID, DEFAULT_GRABBABLE_DATA); var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); - data["activated"] = true; - data["avatarId"] = MyAvatar.sessionUUID; + var now = Date.now(); + if (wasLoaded) { data["refCount"] = 1; - data["avatarId"] = MyAvatar.sessionUUID; } else { data["refCount"] = data["refCount"] ? data["refCount"] + 1 : 1; // zero gravity and set ignoreForCollisions in a way that lets us put them back, after all grabs are done if (data["refCount"] == 1) { + data["heartBeat"] = now; + this.lastHeartBeat = now; + this.isInitialGrab = true; data["gravity"] = grabbedProperties.gravity; data["collidesWith"] = grabbedProperties.collidesWith; @@ -1698,12 +1725,20 @@ function MyController(hand) { z: 0 }, // bummer, it isn't easy to do bitwise collisionMask operations like this: - //"collisionMask": COLLISION_MASK_WHILE_GRABBED | grabbedProperties.collisionMask + // "collisionMask": COLLISION_MASK_WHILE_GRABBED | grabbedProperties.collisionMask // when using string values "collidesWith": COLLIDES_WITH_WHILE_GRABBED }; Entities.editEntity(entityID, whileHeldProperties); } else if (data["refCount"] > 1) { + if (now - data["heartBeat"] > HEART_BEAT_TIMEOUT) { + // this entity has userData suggesting it is grabbed, but nobody is updating the hearbeat. + // deactivate it before grabbing. + this.resetAbandonedGrab(entityID); + grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + return this.activateEntity(entityID, grabbedProperties, wasLoaded); + } + this.isInitialGrab = false; // if an object is being grabbed by more than one person (or the same person twice, but nevermind), switch // the collision groups so that it wont collide with "other" avatars. This avoids a situation where two From acd2caf26c279111043ea554606d07cb795b705f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 23 Feb 2016 16:35:20 -0800 Subject: [PATCH 2/4] handle case where user grab data didn't have a heartbeat --- 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 ad5f1c3775..7db4a0ee8b 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1731,7 +1731,8 @@ function MyController(hand) { }; Entities.editEntity(entityID, whileHeldProperties); } else if (data["refCount"] > 1) { - if (now - data["heartBeat"] > HEART_BEAT_TIMEOUT) { + if (data["heartBeat"] === undefined || + now - data["heartBeat"] > HEART_BEAT_TIMEOUT) { // this entity has userData suggesting it is grabbed, but nobody is updating the hearbeat. // deactivate it before grabbing. this.resetAbandonedGrab(entityID); From efca6ecb1d7fffb9e8c94cf1a84998f017cf3ecc Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 23 Feb 2016 16:37:08 -0800 Subject: [PATCH 3/4] remove debug print --- examples/controllers/handControllerGrab.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 7db4a0ee8b..b298ce81b2 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1695,7 +1695,6 @@ function MyController(hand) { }; this.activateEntity = function(entityID, grabbedProperties, wasLoaded) { - print("activating: " + entityID + " " + (this.hand === RIGHT_HAND ? "right" : "left")); var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, entityID, DEFAULT_GRABBABLE_DATA); var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); var now = Date.now(); From 7fb67e487e6de90592e8301499d62e7ccf86d155 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 23 Feb 2016 16:47:05 -0800 Subject: [PATCH 4/4] when releasing a near-grab, zero velocity if there is another grab, regardless of the isInitialGrab flag --- 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 b298ce81b2..f2acfb9b47 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1650,7 +1650,7 @@ function MyController(hand) { // this next line allowed both: // (1) far-grab, pull to self, near grab, then throw // (2) equip something physical and adjust it with a other-hand grab without the thing drifting - (!this.isInitialGrab && grabData.refCount > 1)) { + grabData.refCount > 1) { noVelocity = true; } }