From 0e028fbac80cdd51121399f9aca1cc66d9dd3d0a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 23 Feb 2016 16:21:41 -0800 Subject: [PATCH] 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