From 848d5a89753df1972ccdc730650d236cb94faa61 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 13 Jul 2016 13:57:43 -0700 Subject: [PATCH 01/11] Reduce calls to Entity.getEntityProperties(). By using a single EntityProprtiesCache instance instead of one per controller. --- .../system/controllers/handControllerGrab.js | 95 +++++++++++-------- 1 file changed, 58 insertions(+), 37 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 94a10798fe..2f82dd9e17 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -286,20 +286,32 @@ function restore2DMode() { // EntityPropertiesCache is a helper class that contains a cache of entity properties. // the hope is to prevent excess calls to Entity.getEntityProperties() +// +// usage: +// call EntityPropertiesCache.addEntities with all the entities that you are interested in. +// This will fetch their properties. Then call EntityPropertiesCache.getProps to receive an object +// containing a cache of all the properties previously fetched. function EntityPropertiesCache() { this.cache = {}; } EntityPropertiesCache.prototype.clear = function () { this.cache = {}; }; -EntityPropertiesCache.prototype.findEntities = function (position, radius) { - var entities = Entities.findEntities(position, radius); +EntityPropertiesCache.prototype.addEntity = function (entityID) { + var cacheEntry = this.cache[entityID]; + if (cacheEntry && cacheEntry.refCount) { + cacheEntry.refCount += 1; + } else { + this._updateCacheEntry(entityID); + } +}; +EntityPropertiesCache.prototype.addEntities = function (entities) { var _this = this; - entities.forEach(function (x) { - _this.updateEntity(x); + entities.forEach(function (entityID) { + _this.addEntity(entityID); }); }; -EntityPropertiesCache.prototype.updateEntity = function (entityID) { +EntityPropertiesCache.prototype._updateCacheEntry = function (entityID) { var props = Entities.getEntityProperties(entityID, GRABBABLE_PROPERTIES); // convert props.userData from a string to an object. @@ -312,11 +324,21 @@ EntityPropertiesCache.prototype.updateEntity = function (entityID) { } } props.userData = userData; + props.refCount = 1; this.cache[entityID] = props; }; -EntityPropertiesCache.prototype.getEntities = function () { - return Object.keys(this.cache); +EntityPropertiesCache.prototype.update = function () { + // delete any cacheEntries with zero refCounts. + var entities = Object.keys(this.cache); + for (var i = 0; i < entities.length; i++) { + var props = this.cache[entities[i]]; + if (props.refCount === 0) { + delete this.cache[entities[i]]; + } else { + props.refCount = 0; + } + } }; EntityPropertiesCache.prototype.getProps = function (entityID) { var obj = this.cache[entityID]; @@ -355,6 +377,9 @@ EntityPropertiesCache.prototype.getEquipHotspotsProps = function (entityID) { } }; +// global cache +var entityPropertiesCache = new EntityPropertiesCache(); + function MyController(hand) { this.hand = hand; if (this.hand === RIGHT_HAND) { @@ -404,8 +429,6 @@ function MyController(hand) { this.lastPickTime = 0; this.lastUnequipCheckTime = 0; - this.entityPropertyCache = new EntityPropertiesCache(); - this.equipOverlayInfoSetMap = {}; var _this = this; @@ -900,10 +923,8 @@ function MyController(hand) { } } - this.entityPropertyCache.clear(); - this.entityPropertyCache.findEntities(this.getHandPosition(), EQUIP_HOTSPOT_RENDER_RADIUS); - var candidateEntities = this.entityPropertyCache.getEntities(); - + var candidateEntities = Entities.findEntities(this.getHandPosition(), EQUIP_HOTSPOT_RENDER_RADIUS); + entityPropertiesCache.addEntities(candidateEntities); var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities); if (!this.waitForTriggerRelease) { this.updateEquipHaptics(potentialEquipHotspot); @@ -971,7 +992,7 @@ function MyController(hand) { diameter = diameter * EQUIP_RADIUS_EMBIGGEN_FACTOR; } - var props = _this.entityPropertyCache.getProps(overlayInfoSet.entityID); + var props = entityPropertiesCache.getProps(overlayInfoSet.entityID); var entityXform = new Xform(props.rotation, props.position); var position = entityXform.xformPoint(overlayInfoSet.localPosition); @@ -1072,7 +1093,7 @@ function MyController(hand) { }; this.entityWantsTrigger = function (entityID) { - var grabbableProps = this.entityPropertyCache.getGrabbableProps(entityID); + var grabbableProps = entityPropertiesCache.getGrabbableProps(entityID); return grabbableProps && grabbableProps.wantsTrigger; }; @@ -1088,9 +1109,9 @@ function MyController(hand) { // offset position {Vec3} and offset rotation {Quat}, both are in the coordinate system of the joint. this.collectEquipHotspots = function (entityID) { var result = []; - var props = this.entityPropertyCache.getProps(entityID); + var props = entityPropertiesCache.getProps(entityID); var entityXform = new Xform(props.rotation, props.position); - var equipHotspotsProps = this.entityPropertyCache.getEquipHotspotsProps(entityID); + var equipHotspotsProps = entityPropertiesCache.getEquipHotspotsProps(entityID); if (equipHotspotsProps && equipHotspotsProps.length > 0) { var i, length = equipHotspotsProps.length; for (i = 0; i < length; i++) { @@ -1107,7 +1128,7 @@ function MyController(hand) { } } } else { - var wearableProps = this.entityPropertyCache.getWearableProps(entityID); + var wearableProps = entityPropertiesCache.getWearableProps(entityID); if (wearableProps && wearableProps.joints) { result.push({ key: entityID.toString() + "0", @@ -1123,8 +1144,8 @@ function MyController(hand) { }; this.hotspotIsEquippable = function (hotspot) { - var props = this.entityPropertyCache.getProps(hotspot.entityID); - var grabProps = this.entityPropertyCache.getGrabProps(hotspot.entityID); + 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; @@ -1142,9 +1163,9 @@ function MyController(hand) { }; this.entityIsGrabbable = function (entityID) { - var grabbableProps = this.entityPropertyCache.getGrabbableProps(entityID); - var grabProps = this.entityPropertyCache.getGrabProps(entityID); - var props = this.entityPropertyCache.getProps(entityID); + var grabbableProps = entityPropertiesCache.getGrabbableProps(entityID); + var grabProps = entityPropertiesCache.getGrabProps(entityID); + var props = entityPropertiesCache.getProps(entityID); var physical = propsArePhysical(props); var grabbable = false; var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME); @@ -1198,7 +1219,7 @@ function MyController(hand) { return false; } - var props = this.entityPropertyCache.getProps(entityID); + var props = entityPropertiesCache.getProps(entityID); var distance = Vec3.distance(props.position, handPosition); var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME); @@ -1236,7 +1257,7 @@ function MyController(hand) { return false; } - var props = this.entityPropertyCache.getProps(entityID); + var props = entityPropertiesCache.getProps(entityID); var distance = Vec3.distance(props.position, handPosition); var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME); @@ -1294,16 +1315,15 @@ function MyController(hand) { var handPosition = this.getHandPosition(); - this.entityPropertyCache.clear(); - this.entityPropertyCache.findEntities(handPosition, NEAR_GRAB_RADIUS); - var candidateEntities = this.entityPropertyCache.getEntities(); + var candidateEntities = Entities.findEntities(handPosition, NEAR_GRAB_RADIUS); + entityPropertiesCache.addEntities(candidateEntities); var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities); if (potentialEquipHotspot) { if (this.triggerSmoothedGrab()) { this.grabbedHotspot = potentialEquipHotspot; this.grabbedEntity = potentialEquipHotspot.entityID; - this.setState(STATE_HOLD, "eqipping '" + this.entityPropertyCache.getProps(this.grabbedEntity).name + "'"); + this.setState(STATE_HOLD, "eqipping '" + entityPropertiesCache.getProps(this.grabbedEntity).name + "'"); return; } } @@ -1315,7 +1335,7 @@ function MyController(hand) { var rayPickInfo = this.calcRayPickInfo(this.hand); if (rayPickInfo.entityID) { this.intersectionDistance = rayPickInfo.distance; - this.entityPropertyCache.updateEntity(rayPickInfo.entityID); + entityPropertiesCache.addEntity(rayPickInfo.entityID); if (this.entityIsGrabbable(rayPickInfo.entityID) && rayPickInfo.distance < NEAR_GRAB_PICK_RADIUS) { grabbableEntities.push(rayPickInfo.entityID); } @@ -1329,12 +1349,12 @@ function MyController(hand) { if (grabbableEntities.length > 0) { // sort by distance grabbableEntities.sort(function (a, b) { - var aDistance = Vec3.distance(_this.entityPropertyCache.getProps(a).position, handPosition); - var bDistance = Vec3.distance(_this.entityPropertyCache.getProps(b).position, handPosition); + var aDistance = Vec3.distance(entityPropertiesCache.getProps(a).position, handPosition); + var bDistance = Vec3.distance(entityPropertiesCache.getProps(b).position, handPosition); return aDistance - bDistance; }); entity = grabbableEntities[0]; - name = this.entityPropertyCache.getProps(entity).name; + name = entityPropertiesCache.getProps(entity).name; this.grabbedEntity = entity; if (this.entityWantsTrigger(entity)) { if (this.triggerSmoothedGrab()) { @@ -1345,8 +1365,8 @@ function MyController(hand) { } } else { if (this.triggerSmoothedGrab()) { - var props = this.entityPropertyCache.getProps(entity); - var grabProps = this.entityPropertyCache.getGrabProps(entity); + 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 @@ -1366,7 +1386,7 @@ function MyController(hand) { if (rayPickInfo.entityID) { entity = rayPickInfo.entityID; - name = this.entityPropertyCache.getProps(entity).name; + name = entityPropertiesCache.getProps(entity).name; if (this.entityWantsTrigger(entity)) { if (this.triggerSmoothedGrab()) { this.grabbedEntity = entity; @@ -1989,7 +2009,7 @@ function MyController(hand) { // and rotation of the held thing to help content creators set the userData. var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, {}); if (grabData.refCount > 1) { - grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "localRotation"]); + 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 + @@ -2248,6 +2268,7 @@ function update(deltaTime) { if (handToDisable !== RIGHT_HAND && handToDisable !== 'both') { rightController.update(deltaTime); } + entityPropertiesCache.update(); } Messages.subscribe('Hifi-Hand-Disabler'); From 0ce970408c7059a93880ae58a9a4fe0000672c45 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 13 Jul 2016 14:44:15 -0700 Subject: [PATCH 02/11] Fix for double rendering of equip hotspots --- .../system/controllers/handControllerGrab.js | 222 +++++++++--------- 1 file changed, 117 insertions(+), 105 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 2f82dd9e17..47fb689c33 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -380,6 +380,106 @@ EntityPropertiesCache.prototype.getEquipHotspotsProps = function (entityID) { // global cache var entityPropertiesCache = new EntityPropertiesCache(); +// Each overlayInfoSet describes a single equip hotspot. +// It is an object with the following keys: +// timestamp - last time this object was updated, used to delete stale hotspot overlays. +// entityID - entity assosicated with this hotspot +// localPosition - position relative to the entity +// hotspot - hotspot object +// overlays - array of overlay objects created by Overlay.addOverlay() +function EquipHotspotBuddy() { + // holds map from {string} hotspot.key to {object} overlayInfoSet. + this.map = {}; + + // array of all hotspots that are highlighed. + this.highlightedHotspots = []; +} +EquipHotspotBuddy.prototype.clear = function () { + var keys = Object.keys(this.map); + for (var i = 0; i < keys.length; i++) { + var overlayInfoSet = this.map[keys[i]]; + this.deleteOverlayInfoSet(overlayInfoSet); + } + this.map = {}; + this.highlightedHotspots = []; +}; +EquipHotspotBuddy.prototype.highlightHotspot = function (hotspot) { + this.highlightedHotspots.push(hotspot); +}; +EquipHotspotBuddy.prototype.updateHotspot = function (hotspot, timestamp) { + var overlayInfoSet = this.map[hotspot.key]; + if (!overlayInfoSet) { + // create a new overlayInfoSet + overlayInfoSet = { + timestamp: timestamp, + entityID: hotspot.entityID, + localPosition: hotspot.localPosition, + hotspot: hotspot, + overlays: [] + }; + + var diameter = hotspot.radius * 2; + + // TODO: check for custom hotspot model urls. + + // default sphere + overlayInfoSet.overlays.push(Overlays.addOverlay("sphere", { + position: hotspot.worldPosition, + rotation: {x: 0, y: 0, z: 0, w: 1}, + dimensions: diameter * EQUIP_SPHERE_SCALE_FACTOR, + color: EQUIP_SPHERE_COLOR, + alpha: EQUIP_SPHERE_ALPHA, + solid: true, + visible: true, + ignoreRayIntersection: true, + drawInFront: false + })); + + this.map[hotspot.key] = overlayInfoSet; + } else { + overlayInfoSet.timestamp = timestamp; + } +}; +EquipHotspotBuddy.prototype.updateHotspots = function (hotspots, timestamp) { + var _this = this; + hotspots.forEach(function (hotspot) { + _this.updateHotspot(hotspot, timestamp); + }); + this.highlightedHotspots = []; +}; +EquipHotspotBuddy.prototype.update = function (deltaTime, timestamp) { + var keys = Object.keys(this.map); + for (var i = 0; i < keys.length; i++) { + var overlayInfoSet = this.map[keys[i]]; + + // TODO: figure out how to do animation.... fade in, fade out, highlight, un-highlight + + if (overlayInfoSet.timestamp != timestamp) { + // this is an old stale overlay, delete it! + overlayInfoSet.overlays.forEach(function (overlay) { + Overlays.deleteOverlay(overlay); + }); + delete this.map[keys[i]]; + } else { + // update overlay position, rotation to follow the object it's attached to. + + var props = entityPropertiesCache.getProps(overlayInfoSet.entityID); + var entityXform = new Xform(props.rotation, props.position); + var position = entityXform.xformPoint(overlayInfoSet.localPosition); + + overlayInfoSet.overlays.forEach(function (overlay) { + Overlays.editOverlay(overlay, { + position: position, + rotation: props.rotation + }); + }); + } + } +}; + +// global EquipHotspotBuddy instance +var equipHotspotBuddy = new EquipHotspotBuddy(); + function MyController(hand) { this.hand = hand; if (this.hand === RIGHT_HAND) { @@ -440,7 +540,7 @@ function MyController(hand) { return (-1 !== suppressedIn2D.indexOf(this.state)) && isIn2DMode(); }; - this.update = function (deltaTime) { + this.update = function (deltaTime, timestamp) { this.updateSmoothedTrigger(); @@ -453,7 +553,7 @@ function MyController(hand) { var updateMethodName = CONTROLLER_STATE_MACHINE[this.state].updateMethod; var updateMethod = this[updateMethodName]; if (updateMethod) { - updateMethod.call(this, deltaTime); + updateMethod.call(this, deltaTime, timestamp); } else { print("WARNING: could not find updateMethod for state " + stateToName(this.state)); } @@ -909,7 +1009,7 @@ function MyController(hand) { return _this.rawThumbValue < THUMB_ON_VALUE; }; - this.off = function () { + this.off = function (deltaTime, timestamp) { if (this.triggerSmoothedReleased()) { this.waitForTriggerRelease = false; } @@ -931,7 +1031,8 @@ function MyController(hand) { } var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS); - this.updateEquipHotspotRendering(nearEquipHotspots, potentialEquipHotspot); + equipHotspotBuddy.updateHotspots(nearEquipHotspots, timestamp); + equipHotspotBuddy.highlightHotspot(potentialEquipHotspot); }; this.clearEquipHaptics = function () { @@ -946,95 +1047,6 @@ function MyController(hand) { this.prevPotentialEquipHotspot = potentialEquipHotspot; }; - this.clearEquipHotspotRendering = function () { - var keys = Object.keys(this.equipOverlayInfoSetMap); - for (var i = 0; i < keys.length; i++) { - var overlayInfoSet = this.equipOverlayInfoSetMap[keys[i]]; - this.deleteOverlayInfoSet(overlayInfoSet); - } - this.equipOverlayInfoSetMap = {}; - }; - - this.createOverlayInfoSet = function (hotspot, timestamp) { - var overlayInfoSet = { - timestamp: timestamp, - entityID: hotspot.entityID, - localPosition: hotspot.localPosition, - hotspot: hotspot, - overlays: [] - }; - - var diameter = hotspot.radius * 2; - - overlayInfoSet.overlays.push(Overlays.addOverlay("sphere", { - position: hotspot.worldPosition, - rotation: {x: 0, y: 0, z: 0, w: 1}, - dimensions: diameter * EQUIP_SPHERE_SCALE_FACTOR, - color: EQUIP_SPHERE_COLOR, - alpha: EQUIP_SPHERE_ALPHA, - solid: true, - visible: true, - ignoreRayIntersection: true, - drawInFront: false - })); - - return overlayInfoSet; - }; - - this.updateOverlayInfoSet = function (overlayInfoSet, timestamp, potentialEquipHotspot) { - overlayInfoSet.timestamp = timestamp; - - var diameter = overlayInfoSet.hotspot.radius * 2; - - // embiggen the overlays if it maches the potentialEquipHotspot - if (potentialEquipHotspot && overlayInfoSet.entityID == potentialEquipHotspot.entityID && - Vec3.equal(overlayInfoSet.localPosition, potentialEquipHotspot.localPosition)) { - diameter = diameter * EQUIP_RADIUS_EMBIGGEN_FACTOR; - } - - var props = entityPropertiesCache.getProps(overlayInfoSet.entityID); - var entityXform = new Xform(props.rotation, props.position); - var position = entityXform.xformPoint(overlayInfoSet.localPosition); - - overlayInfoSet.overlays.forEach(function (overlay) { - Overlays.editOverlay(overlay, { - position: position, - rotation: props.rotation, - dimensions: diameter * EQUIP_SPHERE_SCALE_FACTOR - }); - }); - }; - - this.deleteOverlayInfoSet = function (overlayInfoSet) { - overlayInfoSet.overlays.forEach(function (overlay) { - Overlays.deleteOverlay(overlay); - }); - }; - - this.updateEquipHotspotRendering = function (hotspots, potentialEquipHotspot) { - var now = Date.now(); - var _this = this; - - hotspots.forEach(function (hotspot) { - var overlayInfoSet = _this.equipOverlayInfoSetMap[hotspot.key]; - if (overlayInfoSet) { - _this.updateOverlayInfoSet(overlayInfoSet, now, potentialEquipHotspot); - } else { - _this.equipOverlayInfoSetMap[hotspot.key] = _this.createOverlayInfoSet(hotspot, now); - } - }); - - // delete sets with old timestamps. - var keys = Object.keys(this.equipOverlayInfoSetMap); - for (var i = 0; i < keys.length; i++) { - var overlayInfoSet = this.equipOverlayInfoSetMap[keys[i]]; - if (overlayInfoSet.timestamp !== now) { - this.deleteOverlayInfoSet(overlayInfoSet); - delete this.equipOverlayInfoSetMap[keys[i]]; - } - } - }; - // 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 @@ -1298,7 +1310,7 @@ function MyController(hand) { } }; - this.search = function () { + this.search = function (deltaTime, timestamp) { var _this = this; var name; @@ -1409,7 +1421,8 @@ function MyController(hand) { this.updateEquipHaptics(potentialEquipHotspot); var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS); - this.updateEquipHotspotRendering(nearEquipHotspots, potentialEquipHotspot); + equipHotspotBuddy.updateHotspots(nearEquipHotspots, timestamp); + equipHotspotBuddy.highlightHotspot(potentialEquipHotspot); // search line visualizations if (USE_ENTITY_LINES_FOR_SEARCHING === true) { @@ -1438,7 +1451,6 @@ function MyController(hand) { this.distanceHoldingEnter = function () { - this.clearEquipHotspotRendering(); this.clearEquipHaptics(); // controller pose is in avatar frame @@ -1498,7 +1510,7 @@ function MyController(hand) { this.previousControllerRotation = controllerRotation; }; - this.distanceHolding = function () { + this.distanceHolding = function (deltaTime, timestamp) { if (this.triggerSmoothedReleased()) { this.callEntityMethodOnGrabbed("releaseGrab"); this.setState(STATE_OFF, "trigger released"); @@ -1717,7 +1729,6 @@ function MyController(hand) { this.dropGestureReset(); this.clearEquipHaptics(); - this.clearEquipHotspotRendering(); Controller.triggerShortHapticPulse(1.0, this.hand); @@ -1819,7 +1830,7 @@ function MyController(hand) { this.currentAngularVelocity = ZERO_VEC; }; - this.nearGrabbing = function (deltaTime) { + this.nearGrabbing = function (deltaTime, timestamp) { var dropDetected = this.dropGestureProcess(deltaTime); @@ -1938,7 +1949,6 @@ function MyController(hand) { this.nearTriggerEnter = function () { - this.clearEquipHotspotRendering(); this.clearEquipHaptics(); Controller.triggerShortHapticPulse(1.0, this.hand); @@ -1946,13 +1956,12 @@ function MyController(hand) { }; this.farTriggerEnter = function () { - this.clearEquipHotspotRendering(); this.clearEquipHaptics(); this.callEntityMethodOnGrabbed("startFarTrigger"); }; - this.nearTrigger = function () { + this.nearTrigger = function (deltaTime, timestamp) { if (this.triggerSmoothedReleased()) { this.callEntityMethodOnGrabbed("stopNearTrigger"); this.setState(STATE_OFF, "trigger released"); @@ -1961,7 +1970,7 @@ function MyController(hand) { this.callEntityMethodOnGrabbed("continueNearTrigger"); }; - this.farTrigger = function () { + this.farTrigger = function (deltaTime, timestamp) { if (this.triggerSmoothedReleased()) { this.callEntityMethodOnGrabbed("stopFarTrigger"); this.setState(STATE_OFF, "trigger released"); @@ -2262,12 +2271,15 @@ Controller.enableMapping(MAPPING_NAME); var handToDisable = 'none'; function update(deltaTime) { + var timestamp = Date.now(); + if (handToDisable !== LEFT_HAND && handToDisable !== 'both') { - leftController.update(deltaTime); + leftController.update(deltaTime, timestamp); } if (handToDisable !== RIGHT_HAND && handToDisable !== 'both') { - rightController.update(deltaTime); + rightController.update(deltaTime, timestamp); } + equipHotspotBuddy.update(deltaTime, timestamp); entityPropertiesCache.update(); } From f3ef2801b4d5275dd27ff3070120a91e8781585d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 13 Jul 2016 15:07:09 -0700 Subject: [PATCH 03/11] Support for model overrides for equip hotspots rendering Bug fix for modelScale on hotspots --- .../system/controllers/handControllerGrab.js | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 47fb689c33..01a4e2588a 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -420,20 +420,30 @@ EquipHotspotBuddy.prototype.updateHotspot = function (hotspot, timestamp) { var diameter = hotspot.radius * 2; - // TODO: check for custom hotspot model urls. - - // default sphere - overlayInfoSet.overlays.push(Overlays.addOverlay("sphere", { - position: hotspot.worldPosition, - rotation: {x: 0, y: 0, z: 0, w: 1}, - dimensions: diameter * EQUIP_SPHERE_SCALE_FACTOR, - color: EQUIP_SPHERE_COLOR, - alpha: EQUIP_SPHERE_ALPHA, - solid: true, - visible: true, - ignoreRayIntersection: true, - drawInFront: false - })); + if (hotspot.modelURL) { + // override default sphere with a user specified model + overlayInfoSet.overlays.push(Overlays.addOverlay("model", { + url: hotspot.modelURL, + position: hotspot.worldPosition, + rotation: {x: 0, y: 0, z: 0, w: 1}, + dimensions: diameter * EQUIP_SPHERE_SCALE_FACTOR, + scale: hotspot.modelScale, + ignoreRayIntersection: true + })); + } else { + // default sphere overlay + overlayInfoSet.overlays.push(Overlays.addOverlay("sphere", { + position: hotspot.worldPosition, + rotation: {x: 0, y: 0, z: 0, w: 1}, + dimensions: diameter * EQUIP_SPHERE_SCALE_FACTOR, + color: EQUIP_SPHERE_COLOR, + alpha: EQUIP_SPHERE_ALPHA, + solid: true, + visible: true, + ignoreRayIntersection: true, + drawInFront: false + })); + } this.map[hotspot.key] = overlayInfoSet; } else { @@ -1119,6 +1129,8 @@ function MyController(hand) { // * radius {number} radius of equip hotspot // * joints {Object} keys are joint names values are arrays of two elements: // offset position {Vec3} and offset rotation {Quat}, both are in the coordinate system of the joint. + // * modelURL {string} url for model to use instead of default sphere. + // * modelScale {Vec3} scale factor for model this.collectEquipHotspots = function (entityID) { var result = []; var props = entityPropertiesCache.getProps(entityID); @@ -1135,7 +1147,9 @@ function MyController(hand) { localPosition: hotspot.position, worldPosition: entityXform.xformPoint(hotspot.position), radius: hotspot.radius, - joints: hotspot.joints + joints: hotspot.joints, + modelURL: hotspot.modelURL, + modelScale: hotspot.modelScale }); } } @@ -1148,7 +1162,9 @@ function MyController(hand) { localPosition: {x: 0, y: 0, z: 0}, worldPosition: entityXform.pos, radius: EQUIP_RADIUS, - joints: wearableProps.joints + joints: wearableProps.joints, + modelURL: null, + modelScale: null }); } } From 66830a05387ad12b32c9687461e6f626e93509c2 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 13 Jul 2016 19:44:51 -0700 Subject: [PATCH 04/11] First pass at equip sphere animation --- .../system/controllers/handControllerGrab.js | 46 +++++++++++++++---- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 01a4e2588a..e446162278 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -78,8 +78,6 @@ var LINE_ENTITY_DIMENSIONS = { var LINE_LENGTH = 500; var PICK_MAX_DISTANCE = 500; // max length of pick-ray -var EQUIP_RADIUS_EMBIGGEN_FACTOR = 1.1; - // // near grabbing // @@ -404,7 +402,7 @@ EquipHotspotBuddy.prototype.clear = function () { this.highlightedHotspots = []; }; EquipHotspotBuddy.prototype.highlightHotspot = function (hotspot) { - this.highlightedHotspots.push(hotspot); + this.highlightedHotspots.push(hotspot.key); }; EquipHotspotBuddy.prototype.updateHotspot = function (hotspot, timestamp) { var overlayInfoSet = this.map[hotspot.key]; @@ -415,6 +413,8 @@ EquipHotspotBuddy.prototype.updateHotspot = function (hotspot, timestamp) { entityID: hotspot.entityID, localPosition: hotspot.localPosition, hotspot: hotspot, + currentSize: 0, + targetSize: 1, overlays: [] }; @@ -458,14 +458,38 @@ EquipHotspotBuddy.prototype.updateHotspots = function (hotspots, timestamp) { this.highlightedHotspots = []; }; EquipHotspotBuddy.prototype.update = function (deltaTime, timestamp) { + + var HIGHLIGHT_SIZE = 1.1; + var NORMAL_SIZE = 1.0; + var keys = Object.keys(this.map); for (var i = 0; i < keys.length; i++) { var overlayInfoSet = this.map[keys[i]]; - // TODO: figure out how to do animation.... fade in, fade out, highlight, un-highlight + // this overlayInfo is highlighted. + if (this.highlightedHotspots.indexOf(keys[i]) != -1) { + overlayInfoSet.targetSize = HIGHLIGHT_SIZE; + } else { + overlayInfoSet.targetSize = NORMAL_SIZE; + } + // start to fade out this hotspot. if (overlayInfoSet.timestamp != timestamp) { - // this is an old stale overlay, delete it! + // because this item timestamp has expired, it might not be in the cache anymore.... + entityPropertiesCache.addEntity(overlayInfoSet.entityID); + overlayInfoSet.targetSize = 0; + } + + // animate the size. + var SIZE_TIMESCALE = 0.1; + var tau = deltaTime / SIZE_TIMESCALE; + if (tau > 1.0) { + tau = 1.0; + } + overlayInfoSet.currentSize += (overlayInfoSet.targetSize - overlayInfoSet.currentSize) * tau; + + if (overlayInfoSet.timestamp != timestamp && overlayInfoSet.currentSize <= 0.05) { + // this is an old overlay, that has finished fading out, delete it! overlayInfoSet.overlays.forEach(function (overlay) { Overlays.deleteOverlay(overlay); }); @@ -476,11 +500,13 @@ EquipHotspotBuddy.prototype.update = function (deltaTime, timestamp) { var props = entityPropertiesCache.getProps(overlayInfoSet.entityID); var entityXform = new Xform(props.rotation, props.position); var position = entityXform.xformPoint(overlayInfoSet.localPosition); + var alpha = overlayInfoSet.currentSize / HIGHLIGHT_SIZE; // AJT: TEMPORARY overlayInfoSet.overlays.forEach(function (overlay) { Overlays.editOverlay(overlay, { position: position, - rotation: props.rotation + rotation: props.rotation, + alpha: alpha // AJT: TEMPORARY }); }); } @@ -1042,7 +1068,9 @@ function MyController(hand) { var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS); equipHotspotBuddy.updateHotspots(nearEquipHotspots, timestamp); - equipHotspotBuddy.highlightHotspot(potentialEquipHotspot); + if (potentialEquipHotspot) { + equipHotspotBuddy.highlightHotspot(potentialEquipHotspot); + } }; this.clearEquipHaptics = function () { @@ -1438,7 +1466,9 @@ function MyController(hand) { var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS); equipHotspotBuddy.updateHotspots(nearEquipHotspots, timestamp); - equipHotspotBuddy.highlightHotspot(potentialEquipHotspot); + if (potentialEquipHotspot) { + equipHotspotBuddy.highlightHotspot(potentialEquipHotspot); + } // search line visualizations if (USE_ENTITY_LINES_FOR_SEARCHING === true) { From 23ea85541cbcae3f94b2b12e85c7806d7a7879ab Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 14 Jul 2016 10:24:08 -0700 Subject: [PATCH 05/11] Animate equip hotspots scale instead of alpha --- scripts/system/controllers/handControllerGrab.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index e446162278..c3177c5338 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -385,6 +385,9 @@ var entityPropertiesCache = new EntityPropertiesCache(); // localPosition - position relative to the entity // hotspot - hotspot object // overlays - array of overlay objects created by Overlay.addOverlay() +// currentSize - current animated scale value +// targetSize - the target of our scale animations +// type - "sphere" or "model". function EquipHotspotBuddy() { // holds map from {string} hotspot.key to {object} overlayInfoSet. this.map = {}; @@ -430,6 +433,7 @@ EquipHotspotBuddy.prototype.updateHotspot = function (hotspot, timestamp) { scale: hotspot.modelScale, ignoreRayIntersection: true })); + overlayInfoSet.type = "model"; } else { // default sphere overlay overlayInfoSet.overlays.push(Overlays.addOverlay("sphere", { @@ -443,6 +447,7 @@ EquipHotspotBuddy.prototype.updateHotspot = function (hotspot, timestamp) { ignoreRayIntersection: true, drawInFront: false })); + overlayInfoSet.type = "sphere"; } this.map[hotspot.key] = overlayInfoSet; @@ -500,13 +505,19 @@ EquipHotspotBuddy.prototype.update = function (deltaTime, timestamp) { var props = entityPropertiesCache.getProps(overlayInfoSet.entityID); var entityXform = new Xform(props.rotation, props.position); var position = entityXform.xformPoint(overlayInfoSet.localPosition); - var alpha = overlayInfoSet.currentSize / HIGHLIGHT_SIZE; // AJT: TEMPORARY + + var dimensions; + if (overlayInfoSet.type == "sphere") { + dimensions = overlayInfoSet.hotspot.radius * 2 * overlayInfoSet.currentSize * EQUIP_SPHERE_SCALE_FACTOR; + } else { + dimensions = overlayInfoSet.hotspot.radius * 2 * overlayInfoSet.currentSize; + } overlayInfoSet.overlays.forEach(function (overlay) { Overlays.editOverlay(overlay, { position: position, rotation: props.rotation, - alpha: alpha // AJT: TEMPORARY + dimensions: dimensions }); }); } From 51cce939b30e09f041028d5e5c5dcf3cfc4605af Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 14 Jul 2016 10:32:02 -0700 Subject: [PATCH 06/11] removed shake to drop --- .../system/controllers/handControllerGrab.js | 51 +------------------ 1 file changed, 1 insertion(+), 50 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index c3177c5338..3b3015563a 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -41,10 +41,6 @@ var HAND_HEAD_MIX_RATIO = 0.0; // 0 = only use hands for search/move. 1 = only var PICK_WITH_HAND_RAY = true; -var DRAW_GRAB_BOXES = false; -var DRAW_HAND_SPHERES = false; -var DROP_WITHOUT_SHAKE = false; - var EQUIP_SPHERE_COLOR = { red: 179, green: 120, blue: 211 }; var EQUIP_SPHERE_ALPHA = 0.15; var EQUIP_SPHERE_SCALE_FACTOR = 0.65; @@ -1743,22 +1739,8 @@ function MyController(hand) { this.dropGestureProcess = function (deltaTime) { var standardControllerValue = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; var pose = Controller.getPoseValue(standardControllerValue); - var worldHandVelocity = Vec3.multiplyQbyV(MyAvatar.orientation, pose.velocity); var worldHandRotation = Quat.multiply(MyAvatar.orientation, pose.rotation); - if (this.fastHandMoveDetected) { - this.fastHandMoveTimer -= deltaTime; - } - if (this.fastHandMoveTimer < 0) { - this.fastHandMoveDetected = false; - } - var FAST_HAND_SPEED_REST_TIME = 1; // sec - var FAST_HAND_SPEED_THRESHOLD = 0.4; // m/sec - if (Vec3.length(worldHandVelocity) > FAST_HAND_SPEED_THRESHOLD) { - this.fastHandMoveDetected = true; - this.fastHandMoveTimer = FAST_HAND_SPEED_REST_TIME; - } - var localHandUpAxis = this.hand === RIGHT_HAND ? {x: 1, y: 0, z: 0} : {x: -1, y: 0, z: 0}; var worldHandUpAxis = Vec3.multiplyQbyV(worldHandRotation, localHandUpAxis); var DOWN = {x: 0, y: -1, z: 0}; @@ -1776,7 +1758,7 @@ function MyController(hand) { print("handMove = " + this.fastHandMoveDetected + ", handIsUpsideDown = " + handIsUpsideDown); } - return (DROP_WITHOUT_SHAKE || this.fastHandMoveDetected) && handIsUpsideDown; + return handIsUpsideDown; }; this.nearGrabbingEnter = function () { @@ -2407,38 +2389,7 @@ function cleanup() { leftController.cleanup(); Controller.disableMapping(MAPPING_NAME); Reticle.setVisible(true); - Menu.removeMenuItem("Developer > Hands", "Drop Without Shake"); } Script.scriptEnding.connect(cleanup); Script.update.connect(update); - -if (!Menu.menuExists("Developer > Grab Script")) { - Menu.addMenu("Developer > Grab Script"); -} - -Menu.addMenuItem({ - menuName: "Developer > Grab Script", - menuItemName: "Drop Without Shake", - isCheckable: true, - isChecked: DROP_WITHOUT_SHAKE -}); - -Menu.addMenuItem({ - menuName: "Developer > Grab Script", - menuItemName: "Draw Grab Boxes", - isCheckable: true, - isChecked: DRAW_GRAB_BOXES -}); - -function handleMenuItemEvent(menuItem) { - if (menuItem === "Drop Without Shake") { - DROP_WITHOUT_SHAKE = Menu.isOptionChecked("Drop Without Shake"); - } - if (menuItem === "Draw Grab Boxes") { - DRAW_GRAB_BOXES = Menu.isOptionChecked("Draw Grab Boxes"); - DRAW_HAND_SPHERES = DRAW_GRAB_BOXES; - } -} - -Menu.menuItemEvent.connect(handleMenuItemEvent); From e62d34136d24ea7c4102fdd75f8a6d04a6c00b0c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 14 Jul 2016 10:53:47 -0700 Subject: [PATCH 07/11] Added haptics and hysteresis to drop gesture --- .../system/controllers/handControllerGrab.js | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 3b3015563a..0ba0b1be7a 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1732,8 +1732,7 @@ function MyController(hand) { }; this.dropGestureReset = function () { - this.fastHandMoveDetected = false; - this.fastHandMoveTimer = 0; + this.prevHandIsUpsideDown = false; }; this.dropGestureProcess = function (deltaTime) { @@ -1744,18 +1743,21 @@ function MyController(hand) { var localHandUpAxis = this.hand === RIGHT_HAND ? {x: 1, y: 0, z: 0} : {x: -1, y: 0, z: 0}; var worldHandUpAxis = Vec3.multiplyQbyV(worldHandRotation, localHandUpAxis); var DOWN = {x: 0, y: -1, z: 0}; - var ROTATION_THRESHOLD = Math.cos(Math.PI / 8); + + var DROP_ANGLE = Math.PI / 7; + var HYSTERESIS_FACTOR = 1.1; + var ROTATION_ENTER_THRESHOLD = Math.cos(DROP_ANGLE); + var ROTATION_EXIT_THRESHOLD = Math.cos(DROP_ANGLE * HYSTERESIS_FACTOR); + var rotationThreshold = this.prevHandIsUpsideDown ? ROTATION_EXIT_THRESHOLD : ROTATION_ENTER_THRESHOLD; var handIsUpsideDown = false; - if (Vec3.dot(worldHandUpAxis, DOWN) > ROTATION_THRESHOLD) { + if (Vec3.dot(worldHandUpAxis, DOWN) > rotationThreshold) { handIsUpsideDown = true; } - var WANT_DEBUG = false; - if (WANT_DEBUG) { - print("zAxis = " + worldHandUpAxis.x + ", " + worldHandUpAxis.y + ", " + worldHandUpAxis.z); - print("dot = " + Vec3.dot(worldHandUpAxis, DOWN) + ", ROTATION_THRESHOLD = " + ROTATION_THRESHOLD); - print("handMove = " + this.fastHandMoveDetected + ", handIsUpsideDown = " + handIsUpsideDown); + if (handIsUpsideDown != this.prevHandIsUpsideDown) { + this.prevHandIsUpsideDown = handIsUpsideDown; + Controller.triggerShortHapticPulse(0.5, this.hand); } return handIsUpsideDown; From eb5107d9a510dcf6da5d117198bb13869cb6c267 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 14 Jul 2016 12:07:07 -0700 Subject: [PATCH 08/11] highlight the grabbed hotspot when the drop gesture is detected --- scripts/system/controllers/handControllerGrab.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 0ba0b1be7a..c78cf4dbbb 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1882,6 +1882,14 @@ function MyController(hand) { } if (this.state == STATE_HOLD) { + + // highlight the grabbed hotspot when the dropGesture is detected. + if (dropDetected) { + entityPropertiesCache.addEntity(this.grabbedHotspot.entityID); + equipHotspotBuddy.updateHotspot(this.grabbedHotspot, timestamp); + equipHotspotBuddy.highlightHotspot(this.grabbedHotspot); + } + if (dropDetected && this.triggerSmoothedGrab()) { this.callEntityMethodOnGrabbed("releaseEquip"); this.setState(STATE_OFF, "drop gesture detected"); From 6094fb3de3eb90e40d8e137bf20e0e0cacb14f1e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 14 Jul 2016 15:51:05 -0700 Subject: [PATCH 09/11] Remember object attach point offsets in user settings --- .../system/controllers/handControllerGrab.js | 76 +++++++++++++++++-- 1 file changed, 71 insertions(+), 5 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index c78cf4dbbb..fef58d28b4 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -197,7 +197,8 @@ CONTROLLER_STATE_MACHINE[STATE_NEAR_GRABBING] = { CONTROLLER_STATE_MACHINE[STATE_HOLD] = { name: "hold", enterMethod: "nearGrabbingEnter", - updateMethod: "nearGrabbing" + updateMethod: "nearGrabbing", + exitMethod: "holdExit" }; CONTROLLER_STATE_MACHINE[STATE_NEAR_TRIGGER] = { name: "trigger", @@ -257,6 +258,48 @@ function propsArePhysical(props) { return isPhysical; } +var ATTACH_POINT_SETTINGS = "io.highfidelity.attachPoints"; +function getAttachPointSettings() { + try { + var str = Settings.getValue(ATTACH_POINT_SETTINGS); + print("getAttachPointSettings = " + str); + if (str === "false") { + return {}; + } else { + return JSON.parse(str); + } + } catch (err) { + print("Error parsing attachPointSettings: " + err); + return {}; + } +} +function setAttachPointSettings(attachPointSettings) { + var str = JSON.stringify(attachPointSettings); + print("setAttachPointSettings = " + str); + Settings.setValue(ATTACH_POINT_SETTINGS, str); +} +function getAttachPointForHotspotFromSettings(hotspot, hand) { + var attachPointSettings = getAttachPointSettings(); + var jointName = (hand === RIGHT_HAND) ? "RightHand" : "LeftHand"; + var joints = attachPointSettings[hotspot.key]; + if (joints) { + return joints[jointName]; + } else { + return undefined; + } +} +function storeAttachPointForHotspotInSettings(hotspot, hand, offsetPosition, offsetRotation) { + var attachPointSettings = getAttachPointSettings(); + var jointName = (hand === RIGHT_HAND) ? "RightHand" : "LeftHand"; + var joints = attachPointSettings[hotspot.key]; + if (!joints) { + joints = {}; + attachPointSettings[hotspot.key] = joints; + } + joints[jointName] = [offsetPosition, offsetRotation]; + setAttachPointSettings(attachPointSettings); +} + // 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; @@ -1798,11 +1841,18 @@ function MyController(hand) { // if an object is "equipped" and has a predefined offset, use it. this.ignoreIK = grabbableData.ignoreIK ? grabbableData.ignoreIK : false; - var handJointName = this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"; - if (this.grabbedHotspot.joints[handJointName]) { - this.offsetPosition = this.grabbedHotspot.joints[handJointName][0]; - this.offsetRotation = this.grabbedHotspot.joints[handJointName][1]; + var offsets = getAttachPointForHotspotFromSettings(this.grabbedHotspot, this.hand); + if (offsets) { + this.offsetPosition = offsets[0]; + this.offsetRotation = offsets[1]; hasPresetPosition = true; + } else { + var handJointName = this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"; + if (this.grabbedHotspot.joints[handJointName]) { + this.offsetPosition = this.grabbedHotspot.joints[handJointName][0]; + this.offsetRotation = this.grabbedHotspot.joints[handJointName][1]; + hasPresetPosition = true; + } } } else { this.ignoreIK = false; @@ -1996,6 +2046,22 @@ function MyController(hand) { } }; + this.holdExit = function () { + // store the offset attach points into preferences. + if (this.grabbedHotspot) { + entityPropertiesCache.addEntity(this.grabbedEntity); + var props = entityPropertiesCache.getProps(this.grabbedEntity); + var entityXform = new Xform(props.rotation, props.position); + var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position); + var handRot = (this.hand === RIGHT_HAND) ? MyAvatar.getRightPalmRotation() : MyAvatar.getLeftPalmRotation(); + var avatarHandPos = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; + var palmXform = new Xform(handRot, avatarXform.xformPoint(avatarHandPos)); + var offsetXform = Xform.mul(palmXform.inv(), entityXform); + + storeAttachPointForHotspotInSettings(this.grabbedHotspot, this.hand, offsetXform.pos, offsetXform.rot); + } + }; + this.nearTriggerEnter = function () { this.clearEquipHaptics(); From 37d60c6c8575b3423c74755c320782ebc4fd9e38 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 14 Jul 2016 15:58:13 -0700 Subject: [PATCH 10/11] eslint clean --- scripts/system/controllers/handControllerGrab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 93c8dbde76..d6bf5cdd5f 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2354,7 +2354,7 @@ function MyController(hand) { delayedDeactivateTimeout = null; _this.delayedDeactivateEntity(delayedEntityID, delayedCollidesWith); return delayedCollidesWith; - } + }; delayedDeactivateTimeout = Script.setTimeout(delayedDeactivateFunc, COLLIDE_WITH_AV_AFTER_RELEASE_DELAY * MSECS_PER_SEC); delayedDeactivateEntityID = entityID; From e4794450c202e38b13b837131bbb4f85de89843f Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Fri, 15 Jul 2016 15:36:20 -0700 Subject: [PATCH 11/11] Disable attach point saving --- scripts/system/controllers/handControllerGrab.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index d6bf5cdd5f..1e83fca024 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -264,6 +264,9 @@ function propsArePhysical(props) { return isPhysical; } +// currently disabled. +var USE_ATTACH_POINT_SETTINGS = false; + var ATTACH_POINT_SETTINGS = "io.highfidelity.attachPoints"; function getAttachPointSettings() { try { @@ -1858,7 +1861,7 @@ function MyController(hand) { // if an object is "equipped" and has a predefined offset, use it. this.ignoreIK = grabbableData.ignoreIK ? grabbableData.ignoreIK : false; - var offsets = getAttachPointForHotspotFromSettings(this.grabbedHotspot, this.hand); + var offsets = USE_ATTACH_POINT_SETTINGS && getAttachPointForHotspotFromSettings(this.grabbedHotspot, this.hand); if (offsets) { this.offsetPosition = offsets[0]; this.offsetRotation = offsets[1]; @@ -2065,7 +2068,7 @@ function MyController(hand) { this.holdExit = function () { // store the offset attach points into preferences. - if (this.grabbedHotspot) { + if (USE_ATTACH_POINT_SETTINGS && this.grabbedHotspot && this.grabbedEntity) { entityPropertiesCache.addEntity(this.grabbedEntity); var props = entityPropertiesCache.getProps(this.grabbedEntity); var entityXform = new Xform(props.rotation, props.position);