From fa803aebe9327c3e454edf4c821cd9af2abe9358 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 9 Jan 2017 10:21:07 -0800 Subject: [PATCH 1/8] use /~/ style to reference includes --- scripts/system/controllers/handControllerGrab.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 2475314873..942418780e 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -19,9 +19,9 @@ (function() { // BEGIN LOCAL_SCOPE -Script.include("../libraries/utils.js"); -Script.include("../libraries/Xform.js"); -Script.include("../libraries/controllers.js"); +Script.include("/~/system/libraries/utils.js"); +Script.include("/~/system/libraries/Xform.js"); +Script.include("/~/system/libraries/controllers.js"); // // add lines where the hand ray picking is happening From 61f0c56a3672b4c6fe64b8308e8a97947b3d27d9 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 9 Jan 2017 14:20:03 -0800 Subject: [PATCH 2/8] remove code that remembers previous state in userData --- .../system/controllers/handControllerGrab.js | 376 ++---------------- 1 file changed, 28 insertions(+), 348 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 942418780e..9a7262dbe6 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -12,7 +12,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global setEntityCustomData, getEntityCustomData, flatten, Xform, Script, Quat, Vec3, MyAvatar, Entities, Overlays, Settings, +/* global getEntityCustomData, flatten, Xform, Script, Quat, Vec3, MyAvatar, Entities, Overlays, Settings, Reticle, Controller, Camera, Messages, Mat4, getControllerWorldLocation, getGrabPointSphereOffset, setGrabCommunications, Menu */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ @@ -27,7 +27,7 @@ Script.include("/~/system/libraries/controllers.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; var FORCE_IGNORE_IK = false; @@ -41,8 +41,6 @@ var TRIGGER_SMOOTH_RATIO = 0.1; // Time averaging of trigger - 0.0 disables smo var TRIGGER_OFF_VALUE = 0.1; var TRIGGER_ON_VALUE = TRIGGER_OFF_VALUE + 0.05; // Squeezed just enough to activate search or near grab -var COLLIDE_WITH_AV_AFTER_RELEASE_DELAY = 0.25; // seconds - var BUMPER_ON_VALUE = 0.5; var THUMB_ON_VALUE = 0.5; @@ -175,7 +173,6 @@ var GRABBABLE_PROPERTIES = [ ]; var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js -var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js var DEFAULT_GRABBABLE_DATA = { disableReleaseVelocity: false @@ -188,6 +185,15 @@ var blacklist = []; var FORBIDDEN_GRAB_NAMES = ["Grab Debug Entity", "grab pointer"]; var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; +var holdEnabled = true; +var nearGrabEnabled = true; +var farGrabEnabled = true; +var myAvatarScalingEnabled = true; +var objectScalingEnabled = true; +var mostRecentSearchingHand = RIGHT_HAND; +var DEFAULT_SPHERE_MODEL_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/equip-Fresnel-3.fbx"; +var HARDWARE_MOUSE_ID = 0; // Value reserved for hardware mouse. + // states for the state machine var STATE_OFF = 0; var STATE_SEARCHING = 1; @@ -201,31 +207,8 @@ var STATE_ENTITY_LASER_TOUCHING = 8; var STATE_OVERLAY_STYLUS_TOUCHING = 9; var STATE_OVERLAY_LASER_TOUCHING = 10; -var holdEnabled = true; -var nearGrabEnabled = true; -var farGrabEnabled = true; -var myAvatarScalingEnabled = true; -var objectScalingEnabled = true; - -// "collidesWith" is specified by comma-separated list of group names -// the possible group names are: static, dynamic, kinematic, myAvatar, otherAvatar -var COLLIDES_WITH_WHILE_GRABBED = "dynamic,otherAvatar"; - -var HEART_BEAT_INTERVAL = 5 * MSECS_PER_SEC; -var HEART_BEAT_TIMEOUT = 15 * MSECS_PER_SEC; - -var delayedDeactivateFunc; -var delayedDeactivateTimeout; -var delayedDeactivateEntityID; - var CONTROLLER_STATE_MACHINE = {}; -var mostRecentSearchingHand = RIGHT_HAND; - -var DEFAULT_SPHERE_MODEL_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/equip-Fresnel-3.fbx"; - -var HARDWARE_MOUSE_ID = 0; // Value reserved for hardware mouse. - CONTROLLER_STATE_MACHINE[STATE_OFF] = { name: "off", enterMethod: "offEnter", @@ -491,17 +474,6 @@ function storeAttachPointForHotspotInSettings(hotspot, hand, offsetPosition, off setAttachPointSettings(attachPointSettings); } -function removeMyAvatarFromCollidesWith(origCollidesWith) { - var collidesWithSplit = origCollidesWith.split(","); - // remove myAvatar from the array - for (var i = collidesWithSplit.length - 1; i >= 0; i--) { - if (collidesWithSplit[i] === "myAvatar") { - collidesWithSplit.splice(i, 1); - } - } - return collidesWithSplit.join(); -} - // If another script is managing the reticle (as is done by HandControllerPointer), we should not be setting it here, // and we should not be showing lasers when someone else is using the Reticle to indicate a 2D minor mode. var EXTERNALLY_MANAGED_2D_MINOR_MODE = true; @@ -1271,11 +1243,6 @@ function MyController(hand) { this.prevPotentialEquipHotspot = potentialEquipHotspot; }; - this.heartBeatIsStale = function(data) { - var now = Date.now(); - return data.heartBeat === undefined || now - data.heartBeat > HEART_BEAT_TIMEOUT; - }; - // Performs ray pick test from the hand controller into the world // @param {number} which hand to use, RIGHT_HAND or LEFT_HAND // @returns {object} returns object with two keys entityID and distance @@ -1391,14 +1358,13 @@ function MyController(hand) { this.hotspotIsEquippable = function(hotspot) { var props = entityPropertiesCache.getProps(hotspot.entityID); - var grabProps = entityPropertiesCache.getGrabProps(hotspot.entityID); var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME); - var refCount = ("refCount" in grabProps) ? grabProps.refCount : 0; var okToEquipFromOtherHand = ((this.getOtherHandController().state == STATE_NEAR_GRABBING || - this.getOtherHandController().state == STATE_DISTANCE_HOLDING) && - this.getOtherHandController().grabbedEntity == hotspot.entityID); - if (refCount > 0 && !this.heartBeatIsStale(grabProps) && !okToEquipFromOtherHand) { + this.getOtherHandController().state == STATE_DISTANCE_HOLDING) && + this.getOtherHandController().grabbedEntity == hotspot.entityID); + var hasParent = !(props.parentID === NULL_UUID); + if ((hasParent || entityHasActions(hotspot.entityID)) && !okToEquipFromOtherHand) { if (debug) { print("equip is skipping '" + props.name + "': grabbed by someone else"); } @@ -1410,29 +1376,13 @@ function MyController(hand) { this.entityIsGrabbable = function(entityID) { var grabbableProps = entityPropertiesCache.getGrabbableProps(entityID); - var grabProps = entityPropertiesCache.getGrabProps(entityID); var props = entityPropertiesCache.getProps(entityID); if (!props) { return false; } - var physical = propsArePhysical(props); - var grabbable = false; var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME); - var refCount = ("refCount" in grabProps) ? grabProps.refCount : 0; - - if (physical) { - // physical things default to grabbable - grabbable = true; - } else { - // non-physical things default to non-grabbable unless they are already grabbed - if (refCount > 0) { - grabbable = true; - } else { - grabbable = false; - } - } - - if (grabbableProps.hasOwnProperty("grabbable") && refCount === 0) { + var grabbable = propsArePhysical(props); + if (grabbableProps.hasOwnProperty("grabbable")) { grabbable = grabbableProps.grabbable; } @@ -1640,17 +1590,6 @@ function MyController(hand) { } else { // If near something grabbable, grab it! if ((this.triggerSmoothedGrab() || this.secondarySqueezed()) && nearGrabEnabled) { - var props = entityPropertiesCache.getProps(entity); - var grabProps = entityPropertiesCache.getGrabProps(entity); - var refCount = grabProps.refCount ? grabProps.refCount : 0; - if (refCount >= 1) { - // if another person is holding the object, remember to restore the - // parent info, when we are finished grabbing it. - this.shouldResetParentOnRelease = true; - this.previousParentID = props.parentID; - this.previousParentJointIndex = props.parentJointIndex; - } - this.setState(STATE_NEAR_GRABBING, "near grab '" + name + "'"); return; } else { @@ -2039,7 +1978,6 @@ function MyController(hand) { this.actionTimeout = now + (ACTION_TTL * MSECS_PER_SEC); if (this.actionID !== null) { - this.activateEntity(this.grabbedEntity, grabbedProperties, false, true); this.callEntityMethodOnGrabbed("startDistanceGrab"); } @@ -2063,12 +2001,11 @@ function MyController(hand) { Entities.editEntity(this.grabbedEntity, { velocity: velocity }); } + this.release(); this.setState(STATE_OFF, "trigger released"); return; } - this.heartBeat(this.grabbedEntity); - var controllerLocation = getControllerWorldLocation(this.handToController(), true); var worldControllerPosition = controllerLocation.position; var worldControllerRotation = controllerLocation.orientation; @@ -2101,8 +2038,6 @@ function MyController(hand) { disableMoveWithHead: false }; - var handControllerData = getEntityCustomData('handControllerKey', this.grabbedEntity, defaultMoveWithHeadData); - // Update radialVelocity var lastVelocity = Vec3.multiply(worldHandDelta, 1.0 / deltaObjectTime); var delta = Vec3.normalize(Vec3.subtract(grabbedProperties.position, worldControllerPosition)); @@ -2133,6 +2068,7 @@ function MyController(hand) { newTargetPosition = Vec3.sum(newTargetPosition, worldControllerPosition); var objectToAvatar = Vec3.subtract(this.currentObjectPosition, MyAvatar.position); + var handControllerData = getEntityCustomData('handControllerKey', this.grabbedEntity, defaultMoveWithHeadData); if (handControllerData.disableMoveWithHead !== true) { // mix in head motion if (MOVE_WITH_HEAD) { @@ -2280,12 +2216,10 @@ function MyController(hand) { } var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); - this.activateEntity(this.grabbedEntity, grabbedProperties, false, false); - - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); if (FORCE_IGNORE_IK) { this.ignoreIK = true; } else { + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); this.ignoreIK = (grabbableData.ignoreIK !== undefined) ? grabbableData.ignoreIK : true; } @@ -2372,17 +2306,9 @@ function MyController(hand) { } Entities.editEntity(this.grabbedEntity, { - velocity: { - x: 0, - y: 0, - z: 0 - }, - angularVelocity: { - x: 0, - y: 0, - z: 0 - }, - dynamic: false + velocity: { x: 0, y: 0, z: 0 }, + angularVelocity: { x: 0, y: 0, z: 0 }, + // dynamic: false }); if (this.state == STATE_NEAR_GRABBING) { @@ -2408,6 +2334,7 @@ function MyController(hand) { if (this.state == STATE_NEAR_GRABBING && (!this.triggerClicked && this.secondaryReleased())) { this.callEntityMethodOnGrabbed("releaseGrab"); + this.release(); this.setState(STATE_OFF, "trigger released"); return; } @@ -2425,8 +2352,8 @@ function MyController(hand) { // we have an equipped object and the secondary trigger was released // short-circuit the other checks and release it this.preparingHoldRelease = false; - this.release(); + this.setState(STATE_OFF, "equipping ended via secondary press"); return; } @@ -2466,14 +2393,13 @@ function MyController(hand) { this.prevDropDetected = dropDetected; } - this.heartBeat(this.grabbedEntity); - var props = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "parentID", "position", "rotation", "dimensions", "registrationPoint"]); if (!props.position) { // server may have reset, taking our equipped entity with it. move back to "off" state this.callEntityMethodOnGrabbed("releaseGrab"); + this.release(); this.setState(STATE_OFF, "entity has no position property"); return; } @@ -2508,6 +2434,7 @@ function MyController(hand) { } else { // this.state == STATE_HOLD this.callEntityMethodOnGrabbed("releaseEquip"); } + this.release(); this.setState(STATE_OFF, "held object too far away"); return; } @@ -2902,7 +2829,6 @@ function MyController(hand) { this.release = function() { this.turnOffVisualizations(); - var noVelocity = false; if (this.grabbedEntity !== null) { if (this.state === STATE_HOLD) { this.callEntityMethodOnGrabbed("releaseEquip"); @@ -2910,26 +2836,11 @@ function MyController(hand) { // Make a small release haptic pulse if we really were holding something Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); - - // If this looks like the release after adjusting something still held in the other hand, print the position - // and rotation of the held thing to help content creators set the userData. - var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, {}); - this.printNewOffsets = (grabData.refCount > 1); - if (this.actionID !== null) { Entities.deleteAction(this.grabbedEntity, this.actionID); - // sometimes we want things to stay right where they are when we let go. - var releaseVelocityData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - if (releaseVelocityData.disableReleaseVelocity === true || - // 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 - grabData.refCount > 1) { - noVelocity = true; - } } - this.deactivateEntity(this.grabbedEntity, noVelocity); + Entities.editEntity(this.grabbedEntity, { parentID: NULL_UUID }); // XXX Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ action: 'release', @@ -2953,96 +2864,6 @@ function MyController(hand) { this.grabPointSphereOff(); }; - 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, collideWithStatic) { - this.autoUnequipCounter = 0; - - if (this.entityActivated) { - return; - } - this.entityActivated = true; - - if (delayedDeactivateTimeout && delayedDeactivateEntityID == entityID) { - // we have a timeout waiting to set collisions with myAvatar back on (so that when something - // is thrown it doesn't collide with the avatar's capsule the moment it's released). We've - // regrabbed the entity before the timeout fired, so cancel the timeout, run the function now - // and adjust the grabbedProperties. This will make the saved set of properties (the ones that - // get re-instated after all the grabs have been released) be correct. - Script.clearTimeout(delayedDeactivateTimeout); - delayedDeactivateTimeout = null; - grabbedProperties.collidesWith = delayedDeactivateFunc(); - } - - var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); - var now = Date.now(); - - if (wasLoaded) { - data.refCount = 1; - } 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; - data.collisionless = grabbedProperties.collisionless; - data.dynamic = grabbedProperties.dynamic; - data.parentID = wasLoaded ? NULL_UUID : grabbedProperties.parentID; - data.parentJointIndex = grabbedProperties.parentJointIndex; - - var whileHeldProperties = { - gravity: { x: 0, y: 0, z: 0 }, - "collidesWith": collideWithStatic ? - COLLIDES_WITH_WHILE_GRABBED + ",static" : - COLLIDES_WITH_WHILE_GRABBED - }; - Entities.editEntity(entityID, whileHeldProperties); - } else if (data.refCount > 1) { - if (this.heartBeatIsStale(data)) { - // 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, false); - } - - 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 - // people are holding something and one of them will be able (if the other releases at the right time) to - // bootstrap themselves with the held object. This happens because the meaning of "otherAvatar" in - // the collision mask hinges on who the physics simulation owner is. - Entities.editEntity(entityID, { - // "collidesWith": removeAvatarsFromCollidesWith(grabbedProperties.collidesWith) - collisionless: true - }); - } - } - setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); - return data; - }; - this.checkForStrayChildren = function() { // sometimes things can get parented to a hand and this script is unaware. Search for such entities and // unhook them. @@ -3060,147 +2881,6 @@ function MyController(hand) { }); }; - this.delayedDeactivateEntity = function(entityID, collidesWith) { - // If, before the grab started, the held entity collided with myAvatar, we do the deactivation in - // two parts. Most of it is done in deactivateEntity(), but the final collidesWith and refcount - // are delayed a bit. This keeps thrown things from colliding with the avatar's capsule so often. - // The refcount is handled in this delayed fashion so things don't get confused if someone else - // grabs the entity before the timeout fires. - Entities.editEntity(entityID, { - collidesWith: collidesWith - }); - var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); - if (data && data.refCount) { - data.refCount = data.refCount - 1; - if (data.refCount < 1) { - data = null; - } - } else { - data = null; - } - - setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); - }; - - this.deactivateEntity = function(entityID, noVelocity, delayed) { - var deactiveProps; - - if (!this.entityActivated) { - return; - } - this.entityActivated = false; - - var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {}); - var doDelayedDeactivate = false; - if (data && data.refCount) { - data.refCount = data.refCount - 1; - if (data.refCount < 1) { - deactiveProps = { - gravity: data.gravity, - // don't set collidesWith myAvatar back right away, because thrown things tend to bounce off the - // avatar's capsule. - collidesWith: removeMyAvatarFromCollidesWith(data.collidesWith), - collisionless: data.collisionless, - dynamic: data.dynamic, - parentID: data.parentID, - parentJointIndex: data.parentJointIndex - }; - - doDelayedDeactivate = (data.collidesWith.indexOf("myAvatar") >= 0); - - if (doDelayedDeactivate) { - var delayedCollidesWith = data.collidesWith; - var delayedEntityID = entityID; - delayedDeactivateFunc = function() { - // set collidesWith back to original value a bit later than the rest - delayedDeactivateTimeout = null; - _this.delayedDeactivateEntity(delayedEntityID, delayedCollidesWith); - return delayedCollidesWith; - }; - delayedDeactivateTimeout = - Script.setTimeout(delayedDeactivateFunc, COLLIDE_WITH_AV_AFTER_RELEASE_DELAY * MSECS_PER_SEC); - delayedDeactivateEntityID = entityID; - } - - // 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 props = Entities.getEntityProperties(entityID, ["parentID", "velocity", "dynamic", "shapeType"]); - var parentID = props.parentID; - - if (!noVelocity && - parentID == MyAvatar.sessionUUID && - Vec3.length(data.gravity) > 0.0 && - data.dynamic && - data.parentID == NULL_UUID && - !data.collisionless) { - deactiveProps.velocity = this.currentVelocity; - } - if (noVelocity) { - deactiveProps.velocity = { - x: 0.0, - y: 0.0, - z: 0.0 - }; - deactiveProps.angularVelocity = { - x: 0.0, - y: 0.0, - z: 0.0 - }; - } - - Entities.editEntity(entityID, deactiveProps); - data = null; - } else if (this.shouldResetParentOnRelease) { - // we parent-grabbed this from another parent grab. try to put it back where we found it. - deactiveProps = { - parentID: this.previousParentID, - parentJointIndex: this.previousParentJointIndex, - velocity: { - x: 0.0, - y: 0.0, - z: 0.0 - }, - angularVelocity: { - x: 0.0, - y: 0.0, - z: 0.0 - } - }; - Entities.editEntity(entityID, deactiveProps); - - if (this.printNewOffsets) { - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "localRotation"]); - if (grabbedProperties && grabbedProperties.localPosition && grabbedProperties.localRotation) { - print((this.hand === RIGHT_HAND ? '"LeftHand"' : '"RightHand"') + ":" + - '[{"x":' + grabbedProperties.localPosition.x + ', "y":' + grabbedProperties.localPosition.y + - ', "z":' + grabbedProperties.localPosition.z + '}, {"x":' + grabbedProperties.localRotation.x + - ', "y":' + grabbedProperties.localRotation.y + ', "z":' + grabbedProperties.localRotation.z + - ', "w":' + grabbedProperties.localRotation.w + '}]'); - } - } - } else if (noVelocity) { - Entities.editEntity(entityID, { - velocity: { - x: 0.0, - y: 0.0, - z: 0.0 - }, - angularVelocity: { - x: 0.0, - y: 0.0, - z: 0.0 - }, - dynamic: data.dynamic - }); - } - } else { - data = null; - } - if (!doDelayedDeactivate) { - setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); - } - }; - this.getOtherHandController = function() { return (this.hand === RIGHT_HAND) ? leftController : rightController; }; From 01ff3f14b6fd3d7ece2fe6d440a8e6a4d9ac9f15 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 9 Jan 2017 15:28:05 -0800 Subject: [PATCH 3/8] re-evaluate an entities in-bullet status when parentID changes --- libraries/entities/src/EntityItem.cpp | 9 ++++++++- libraries/entities/src/EntityItem.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 00c9ab2dde..d6792d3871 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1308,7 +1308,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(href, setHref); SET_ENTITY_PROPERTY_FROM_PROPERTIES(description, setDescription); SET_ENTITY_PROPERTY_FROM_PROPERTIES(actionData, setActionData); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentID, setParentID); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentID, updateParentID); SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentJointIndex, setParentJointIndex); SET_ENTITY_PROPERTY_FROM_PROPERTIES(queryAACube, setQueryAACube); @@ -1565,6 +1565,13 @@ void EntityItem::updatePosition(const glm::vec3& value) { } } +void EntityItem::updateParentID(const QUuid& value) { + if (_parentID != value) { + setParentID(value); + _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; // children are forced to be kinematic + } +} + void EntityItem::updatePositionFromNetwork(const glm::vec3& value) { if (shouldSuppressLocationEdits()) { return; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index bd5fb44fc8..7be81391f3 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -339,6 +339,7 @@ public: // updateFoo() methods to be used when changes need to be accumulated in the _dirtyFlags virtual void updateRegistrationPoint(const glm::vec3& value); void updatePosition(const glm::vec3& value); + void updateParentID(const QUuid& value); void updatePositionFromNetwork(const glm::vec3& value); void updateDimensions(const glm::vec3& value); void updateRotation(const glm::quat& rotation); From 45a26d599fd74dfa7abba925c60c96ffd03a1738 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 9 Jan 2017 16:40:28 -0800 Subject: [PATCH 4/8] if something is a child of the local avatar, never let it collide with MY_AVATAR --- libraries/entities/src/EntityItem.cpp | 7 +++++-- libraries/shared/src/SpatiallyNestable.cpp | 2 +- libraries/shared/src/SpatiallyNestable.h | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index d6792d3871..8cf5265fae 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1780,7 +1780,6 @@ void EntityItem::updateCreated(uint64_t value) { } void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask) const { - // TODO: detect attachment status and adopt group of wearer if (_collisionless) { group = BULLET_COLLISION_GROUP_COLLISIONLESS; mask = 0; @@ -1794,10 +1793,14 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask } uint8_t userMask = getCollisionMask(); + if (hasAncestorOfType(NestableType::Avatar)) { + userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; + } + if ((bool)(userMask & USER_COLLISION_GROUP_MY_AVATAR) != (bool)(userMask & USER_COLLISION_GROUP_OTHER_AVATAR)) { // asymmetric avatar collision mask bits - if (!getSimulatorID().isNull() && (!getSimulatorID().isNull()) && getSimulatorID() != Physics::getSessionUUID()) { + if (!getSimulatorID().isNull() && getSimulatorID() != Physics::getSessionUUID()) { // someone else owns the simulation, so we toggle the avatar bits (swap interpretation) userMask ^= USER_COLLISION_MASK_AVATARS | ~userMask; } diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index cbe982b959..4876277d67 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -1015,7 +1015,7 @@ AACube SpatiallyNestable::getQueryAACube() const { return result; } -bool SpatiallyNestable::hasAncestorOfType(NestableType nestableType) { +bool SpatiallyNestable::hasAncestorOfType(NestableType nestableType) const { bool success; SpatiallyNestablePointer parent = getParentPointer(success); if (!success || !parent) { diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index f58e2c906c..25a6254689 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -163,7 +163,7 @@ public: bool isParentIDValid() const { bool success = false; getParentPointer(success); return success; } virtual SpatialParentTree* getParentTree() const { return nullptr; } - bool hasAncestorOfType(NestableType nestableType); + bool hasAncestorOfType(NestableType nestableType) const; SpatiallyNestablePointer getParentPointer(bool& success) const; static SpatiallyNestablePointer findByID(QUuid id, bool& success); From ad45bbf5ba00a412f79383d0f2b89839da295495 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 9 Jan 2017 16:45:54 -0800 Subject: [PATCH 5/8] set DIRTY_COLLISION_GROUP flag when parentID changes --- libraries/entities/src/EntityItem.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 8cf5265fae..e869225dd0 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1569,6 +1569,7 @@ void EntityItem::updateParentID(const QUuid& value) { if (_parentID != value) { setParentID(value); _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; // children are forced to be kinematic + _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar } } From 35577f49d092416b2101549ba981df196ba73dca Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 9 Jan 2017 17:00:39 -0800 Subject: [PATCH 6/8] change the test for when MY_AVATAR collisions are short-circuited --- libraries/entities/src/EntityItem.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index e869225dd0..b073f30b03 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1794,7 +1794,8 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask } uint8_t userMask = getCollisionMask(); - if (hasAncestorOfType(NestableType::Avatar)) { + QUuid parentID = getParentID(); + if (!parentID.isNull() && parentID == Physics::getSessionUUID()) { userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; } From 4a29cce58464a7ae5af23abcab32c153fe9f02ce Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 Jan 2017 06:17:40 -0800 Subject: [PATCH 7/8] adjust when USER_COLLISION_GROUP_MY_AVATAR is auto-disabled --- libraries/entities/src/EntityItem.cpp | 7 +++++-- libraries/shared/src/SpatiallyNestable.cpp | 14 ++++++++++++++ libraries/shared/src/SpatiallyNestable.h | 1 + 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index b073f30b03..27b3507f31 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1794,8 +1794,11 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask } uint8_t userMask = getCollisionMask(); - QUuid parentID = getParentID(); - if (!parentID.isNull() && parentID == Physics::getSessionUUID()) { + // if this entity is a descendant of MyAvatar, don't collide with MyAvatar. This avoids the + // "bootstrapping" problem where you can shoot yourself across the room by grabbing something + // and holding it against your own avatar. + QUuid ancestorID = findAncestorOfType(NestableType::Avatar); + if (!ancestorID.isNull() && ancestorID == Physics::getSessionUUID()) { userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; } diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 4876277d67..de998799b3 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -1029,6 +1029,20 @@ bool SpatiallyNestable::hasAncestorOfType(NestableType nestableType) const { return parent->hasAncestorOfType(nestableType); } +const QUuid SpatiallyNestable::findAncestorOfType(NestableType nestableType) const { + bool success; + SpatiallyNestablePointer parent = getParentPointer(success); + if (!success || !parent) { + return QUuid(); + } + + if (parent->_nestableType == nestableType) { + return parent->getID(); + } + + return parent->findAncestorOfType(nestableType); +} + void SpatiallyNestable::getLocalTransformAndVelocities( Transform& transform, glm::vec3& velocity, diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 25a6254689..bf60b2966d 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -164,6 +164,7 @@ public: virtual SpatialParentTree* getParentTree() const { return nullptr; } bool hasAncestorOfType(NestableType nestableType) const; + const QUuid findAncestorOfType(NestableType nestableType) const; SpatiallyNestablePointer getParentPointer(bool& success) const; static SpatiallyNestablePointer findByID(QUuid id, bool& success); From bff9e9be3813d389d6499afacc7aec6bce76dd43 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 Jan 2017 09:22:36 -0800 Subject: [PATCH 8/8] put back code that replaces previous parentID on release --- scripts/system/controllers/handControllerGrab.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 9a7262dbe6..ebafb167e0 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1363,7 +1363,10 @@ function MyController(hand) { var okToEquipFromOtherHand = ((this.getOtherHandController().state == STATE_NEAR_GRABBING || this.getOtherHandController().state == STATE_DISTANCE_HOLDING) && this.getOtherHandController().grabbedEntity == hotspot.entityID); - var hasParent = !(props.parentID === NULL_UUID); + var hasParent = true; + if (props.parentID === NULL_UUID) { + hasParent = false; + } if ((hasParent || entityHasActions(hotspot.entityID)) && !okToEquipFromOtherHand) { if (debug) { print("equip is skipping '" + props.name + "': grabbed by someone else"); @@ -1591,6 +1594,10 @@ function MyController(hand) { // If near something grabbable, grab it! if ((this.triggerSmoothedGrab() || this.secondarySqueezed()) && nearGrabEnabled) { this.setState(STATE_NEAR_GRABBING, "near grab '" + name + "'"); + var props = entityPropertiesCache.getProps(entity); + this.shouldResetParentOnRelease = true; + this.previousParentID = props.parentID; + this.previousParentJointIndex = props.parentJointIndex; return; } else { // potentialNearGrabEntity = entity; @@ -2840,7 +2847,12 @@ function MyController(hand) { Entities.deleteAction(this.grabbedEntity, this.actionID); } - Entities.editEntity(this.grabbedEntity, { parentID: NULL_UUID }); // XXX + if (this.shouldResetParentOnRelease) { + Entities.editEntity(this.grabbedEntity, { + parentID: this.previousParentID, + parentJointIndex: this.previousParentJointIndex + }); + } Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ action: 'release',