mirror of
https://github.com/overte-org/overte.git
synced 2025-04-23 15:13:41 +02:00
Merge pull request #11430 from sethalves/fix-parent-grab
Fix parent grab
This commit is contained in:
commit
a871a6db1d
5 changed files with 226 additions and 65 deletions
scripts/system
|
@ -10,7 +10,7 @@
|
|||
/* global Script, Entities, Overlays, Controller, Vec3, Quat, getControllerWorldLocation, RayPick,
|
||||
controllerDispatcherPlugins:true, controllerDispatcherPluginsNeedSort:true,
|
||||
LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS, DEFAULT_SEARCH_SPHERE_DISTANCE, DISPATCHER_PROPERTIES,
|
||||
getGrabPointSphereOffset, HMD, MyAvatar, Messages
|
||||
getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities
|
||||
*/
|
||||
|
||||
controllerDispatcherPlugins = {};
|
||||
|
@ -27,6 +27,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
var BASIC_TIMER_INTERVAL_MS = 1000 / TARGET_UPDATE_HZ;
|
||||
|
||||
var PROFILE = false;
|
||||
var DEBUG = true;
|
||||
|
||||
if (typeof Test !== "undefined") {
|
||||
PROFILE = true;
|
||||
|
@ -195,7 +196,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
var h;
|
||||
for (h = LEFT_HAND; h <= RIGHT_HAND; h++) {
|
||||
if (controllerLocations[h].valid) {
|
||||
var nearbyOverlays = Overlays.findOverlays(controllerLocations[h].position, NEAR_MAX_RADIUS * sensorScaleFactor);
|
||||
var nearbyOverlays =
|
||||
Overlays.findOverlays(controllerLocations[h].position, NEAR_MAX_RADIUS * sensorScaleFactor);
|
||||
nearbyOverlays.sort(function (a, b) {
|
||||
var aPosition = Overlays.getProperty(a, "position");
|
||||
var aDistance = Vec3.distance(aPosition, controllerLocations[h].position);
|
||||
|
@ -265,6 +267,20 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
});
|
||||
}
|
||||
|
||||
// sometimes, during a HMD snap-turn, an equipped or held item wont be near
|
||||
// the hand when the findEntities is done. Gather up any hand-children here.
|
||||
for (h = LEFT_HAND; h <= RIGHT_HAND; h++) {
|
||||
var handChildrenIDs = findHandChildEntities(h);
|
||||
handChildrenIDs.forEach(function (handChildID) {
|
||||
if (handChildID in nearbyEntityPropertiesByID) {
|
||||
return;
|
||||
}
|
||||
var props = Entities.getEntityProperties(handChildID, DISPATCHER_PROPERTIES);
|
||||
props.id = handChildID;
|
||||
nearbyEntityPropertiesByID[handChildID] = props;
|
||||
});
|
||||
}
|
||||
|
||||
// bundle up all the data about the current situation
|
||||
var controllerData = {
|
||||
triggerValues: [_this.leftTriggerValue, _this.rightTriggerValue],
|
||||
|
@ -300,6 +316,9 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
// activity-slots which this plugin consumes as "in use"
|
||||
_this.runningPluginNames[orderedPluginName] = true;
|
||||
_this.markSlots(candidatePlugin, orderedPluginName);
|
||||
if (DEBUG) {
|
||||
print("controllerDispatcher running " + orderedPluginName);
|
||||
}
|
||||
}
|
||||
if (PROFILE) {
|
||||
Script.endProfileRange("dispatch.isReady." + orderedPluginName);
|
||||
|
@ -332,6 +351,9 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
|||
// of running plugins and mark its activity-slots as "not in use"
|
||||
delete _this.runningPluginNames[runningPluginName];
|
||||
_this.markSlots(plugin, false);
|
||||
if (DEBUG) {
|
||||
print("controllerDispatcher stopping " + runningPluginName);
|
||||
}
|
||||
}
|
||||
if (PROFILE) {
|
||||
Script.endProfileRange("dispatch.run." + runningPluginName);
|
||||
|
|
|
@ -254,6 +254,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
|
|||
this.triggerValue = 0;
|
||||
this.messageGrabEntity = false;
|
||||
this.grabEntityProps = null;
|
||||
this.shouldSendStart = false;
|
||||
|
||||
this.parameters = makeDispatcherModuleParameters(
|
||||
300,
|
||||
|
@ -498,6 +499,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
|
|||
var cloneID = this.cloneHotspot(grabbedProperties, controllerData);
|
||||
this.targetEntityID = cloneID;
|
||||
Entities.editEntity(this.targetEntityID, reparentProps);
|
||||
controllerData.nearbyEntityPropertiesByID[this.targetEntityID] = grabbedProperties;
|
||||
isClone = true;
|
||||
} else if (!grabbedProperties.locked) {
|
||||
Entities.editEntity(this.targetEntityID, reparentProps);
|
||||
|
@ -507,8 +509,9 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
|
|||
return;
|
||||
}
|
||||
|
||||
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
|
||||
Entities.callEntityMethod(this.targetEntityID, "startEquip", args);
|
||||
// we don't want to send startEquip message until the trigger is released. otherwise,
|
||||
// guns etc will fire right as they are equipped.
|
||||
this.shouldSendStart = true;
|
||||
|
||||
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
|
||||
action: 'equip',
|
||||
|
@ -588,22 +591,21 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
|
|||
// if the potentialHotspot is cloneable, clone it and return it
|
||||
// if the potentialHotspot os not cloneable and locked return null
|
||||
|
||||
if (potentialEquipHotspot) {
|
||||
if ((this.triggerSmoothedSqueezed() && !this.waitForTriggerRelease) || this.messageGrabEntity) {
|
||||
this.grabbedHotspot = potentialEquipHotspot;
|
||||
this.targetEntityID = this.grabbedHotspot.entityID;
|
||||
this.startEquipEntity(controllerData);
|
||||
this.messageGrabEnity = false;
|
||||
}
|
||||
if (potentialEquipHotspot &&
|
||||
((this.triggerSmoothedSqueezed() && !this.waitForTriggerRelease) || this.messageGrabEntity)) {
|
||||
this.grabbedHotspot = potentialEquipHotspot;
|
||||
this.targetEntityID = this.grabbedHotspot.entityID;
|
||||
this.startEquipEntity(controllerData);
|
||||
this.messageGrabEnity = false;
|
||||
return makeRunningValues(true, [potentialEquipHotspot.entityID], []);
|
||||
} else {
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
};
|
||||
|
||||
this.isTargetIDValid = function() {
|
||||
var entityProperties = Entities.getEntityProperties(this.targetEntityID, ["type"]);
|
||||
return "type" in entityProperties;
|
||||
this.isTargetIDValid = function(controllerData) {
|
||||
var entityProperties = controllerData.nearbyEntityPropertiesByID[this.targetEntityID];
|
||||
return entityProperties && "type" in entityProperties;
|
||||
};
|
||||
|
||||
this.isReady = function (controllerData, deltaTime) {
|
||||
|
@ -616,7 +618,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
|
|||
var timestamp = Date.now();
|
||||
this.updateInputs(controllerData);
|
||||
|
||||
if (!this.isTargetIDValid()) {
|
||||
if (!this.isTargetIDValid(controllerData)) {
|
||||
this.endEquipEntity();
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
|
@ -643,6 +645,13 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
|
|||
var dropDetected = this.dropGestureProcess(deltaTime);
|
||||
|
||||
if (this.triggerSmoothedReleased()) {
|
||||
if (this.shouldSendStart) {
|
||||
// we don't want to send startEquip message until the trigger is released. otherwise,
|
||||
// guns etc will fire right as they are equipped.
|
||||
var startArgs = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
|
||||
Entities.callEntityMethod(this.targetEntityID, "startEquip", startArgs);
|
||||
this.shouldSendStart = false;
|
||||
}
|
||||
this.waitForTriggerRelease = false;
|
||||
}
|
||||
|
||||
|
@ -674,8 +683,10 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
|
|||
|
||||
equipHotspotBuddy.update(deltaTime, timestamp, controllerData);
|
||||
|
||||
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
|
||||
Entities.callEntityMethod(this.targetEntityID, "continueEquip", args);
|
||||
if (!this.shouldSendStart) {
|
||||
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
|
||||
Entities.callEntityMethod(this.targetEntityID, "continueEquip", args);
|
||||
}
|
||||
|
||||
return makeRunningValues(true, [this.targetEntityID], []);
|
||||
};
|
||||
|
|
|
@ -182,7 +182,8 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
|
|||
}
|
||||
|
||||
if (targetProps) {
|
||||
if (!propsArePhysical(targetProps) && !propsAreCloneDynamic(targetProps)) {
|
||||
if ((!propsArePhysical(targetProps) && !propsAreCloneDynamic(targetProps)) ||
|
||||
targetProps.parentID != NULL_UUID) {
|
||||
return makeRunningValues(false, [], []); // let nearParentGrabEntity handle it
|
||||
} else {
|
||||
this.targetEntityID = targetProps.id;
|
||||
|
@ -216,7 +217,8 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
|
|||
|
||||
var targetProps = this.getTargetProps(controllerData);
|
||||
if (targetProps) {
|
||||
if (controllerData.triggerClicks[this.hand] || controllerData.secondaryValues[this.hand] > BUMPER_ON_VALUE) {
|
||||
if (controllerData.triggerClicks[this.hand] ||
|
||||
controllerData.secondaryValues[this.hand] > BUMPER_ON_VALUE) {
|
||||
// switch to grabbing
|
||||
var targetCloneable = entityIsCloneable(targetProps);
|
||||
if (targetCloneable) {
|
||||
|
|
|
@ -8,8 +8,10 @@
|
|||
|
||||
/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, getControllerJointIndex, NULL_UUID,
|
||||
enableDispatcherModule, disableDispatcherModule, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION,
|
||||
TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS, findGroupParent,
|
||||
Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE
|
||||
TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS,
|
||||
findGroupParent, Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic, HAPTIC_PULSE_STRENGTH,
|
||||
HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, findHandChildEntities, TEAR_AWAY_DISTANCE, MSECS_PER_SEC, TEAR_AWAY_CHECK_TIME,
|
||||
TEAR_AWAY_COUNT, distanceBetweenPointAndEntityBoundingBox
|
||||
*/
|
||||
|
||||
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
|
||||
|
@ -28,6 +30,9 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
|
|||
this.previousParentJointIndex = {};
|
||||
this.previouslyUnhooked = {};
|
||||
this.hapticTargetID = null;
|
||||
this.lastUnequipCheckTime = 0;
|
||||
this.autoUnequipCounter = 0;
|
||||
this.lastUnexpectedChildrenCheckTime = 0;
|
||||
|
||||
this.parameters = makeDispatcherModuleParameters(
|
||||
500,
|
||||
|
@ -40,11 +45,11 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
|
|||
this.handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand");
|
||||
this.controllerJointIndex = getControllerJointIndex(this.hand);
|
||||
|
||||
this.getOtherModule = function() {
|
||||
return (this.hand === RIGHT_HAND) ? leftNearParentingGrabEntity : rightNearParentingGrabEntity;
|
||||
};
|
||||
|
||||
this.thisHandIsParent = function(props) {
|
||||
if (!props) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (props.parentID !== MyAvatar.sessionUUID && props.parentID !== AVATAR_SELF_ID) {
|
||||
return false;
|
||||
}
|
||||
|
@ -93,8 +98,8 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
|
|||
|
||||
if (this.thisHandIsParent(targetProps)) {
|
||||
// this should never happen, but if it does, don't set previous parent to be this hand.
|
||||
// this.previousParentID[targetProps.id] = NULL;
|
||||
// this.previousParentJointIndex[targetProps.id] = -1;
|
||||
this.previousParentID[targetProps.id] = null;
|
||||
this.previousParentJointIndex[targetProps.id] = -1;
|
||||
} else {
|
||||
this.previousParentID[targetProps.id] = targetProps.parentID;
|
||||
this.previousParentJointIndex[targetProps.id] = targetProps.parentJointIndex;
|
||||
|
@ -111,20 +116,24 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
|
|||
this.grabbing = true;
|
||||
};
|
||||
|
||||
this.endNearParentingGrabEntity = function () {
|
||||
if (this.previousParentID[this.targetEntityID] === NULL_UUID || this.previousParentID === undefined) {
|
||||
Entities.editEntity(this.targetEntityID, {
|
||||
parentID: this.previousParentID[this.targetEntityID],
|
||||
parentJointIndex: this.previousParentJointIndex[this.targetEntityID]
|
||||
});
|
||||
} else {
|
||||
// we're putting this back as a child of some other parent, so zero its velocity
|
||||
Entities.editEntity(this.targetEntityID, {
|
||||
parentID: this.previousParentID[this.targetEntityID],
|
||||
parentJointIndex: this.previousParentJointIndex[this.targetEntityID],
|
||||
localVelocity: {x: 0, y: 0, z: 0},
|
||||
localAngularVelocity: {x: 0, y: 0, z: 0}
|
||||
});
|
||||
this.endNearParentingGrabEntity = function (controllerData) {
|
||||
this.hapticTargetID = null;
|
||||
var props = controllerData.nearbyEntityPropertiesByID[this.targetEntityID];
|
||||
if (this.thisHandIsParent(props)) {
|
||||
if (this.previousParentID[this.targetEntityID] === NULL_UUID || this.previousParentID === undefined) {
|
||||
Entities.editEntity(this.targetEntityID, {
|
||||
parentID: this.previousParentID[this.targetEntityID],
|
||||
parentJointIndex: this.previousParentJointIndex[this.targetEntityID]
|
||||
});
|
||||
} else {
|
||||
// we're putting this back as a child of some other parent, so zero its velocity
|
||||
Entities.editEntity(this.targetEntityID, {
|
||||
parentID: this.previousParentID[this.targetEntityID],
|
||||
parentJointIndex: this.previousParentJointIndex[this.targetEntityID],
|
||||
localVelocity: {x: 0, y: 0, z: 0},
|
||||
localAngularVelocity: {x: 0, y: 0, z: 0}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
|
||||
|
@ -133,6 +142,71 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
|
|||
this.targetEntityID = null;
|
||||
};
|
||||
|
||||
this.checkForChildTooFarAway = function (controllerData) {
|
||||
var props = controllerData.nearbyEntityPropertiesByID[this.targetEntityID];
|
||||
var now = Date.now();
|
||||
if (now - this.lastUnequipCheckTime > MSECS_PER_SEC * TEAR_AWAY_CHECK_TIME) {
|
||||
this.lastUnequipCheckTime = now;
|
||||
if (props.parentID == AVATAR_SELF_ID) {
|
||||
var handPosition = controllerData.controllerLocations[this.hand].position;
|
||||
var dist = distanceBetweenPointAndEntityBoundingBox(handPosition, props);
|
||||
if (dist > TEAR_AWAY_DISTANCE) {
|
||||
this.autoUnequipCounter++;
|
||||
} else {
|
||||
this.autoUnequipCounter = 0;
|
||||
}
|
||||
if (this.autoUnequipCounter >= TEAR_AWAY_COUNT) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
this.checkForUnexpectedChildren = function (controllerData) {
|
||||
// sometimes things can get parented to a hand and this script is unaware. Search for such entities and
|
||||
// unhook them.
|
||||
|
||||
var now = Date.now();
|
||||
var UNEXPECTED_CHILDREN_CHECK_TIME = 0.1; // seconds
|
||||
if (now - this.lastUnexpectedChildrenCheckTime > MSECS_PER_SEC * UNEXPECTED_CHILDREN_CHECK_TIME) {
|
||||
this.lastUnexpectedChildrenCheckTime = now;
|
||||
|
||||
var children = findHandChildEntities(this.hand);
|
||||
var _this = this;
|
||||
|
||||
children.forEach(function(childID) {
|
||||
// we appear to be holding something and this script isn't in a state that would be holding something.
|
||||
// unhook it. if we previously took note of this entity's parent, put it back where it was. This
|
||||
// works around some problems that happen when more than one hand or avatar is passing something around.
|
||||
if (_this.previousParentID[childID]) {
|
||||
var previousParentID = _this.previousParentID[childID];
|
||||
var previousParentJointIndex = _this.previousParentJointIndex[childID];
|
||||
|
||||
// The main flaw with keeping track of previous parantage in individual scripts is:
|
||||
// (1) A grabs something (2) B takes it from A (3) A takes it from B (4) A releases it
|
||||
// now A and B will take turns passing it back to the other. Detect this and stop the loop here...
|
||||
var UNHOOK_LOOP_DETECT_MS = 200;
|
||||
if (_this.previouslyUnhooked[childID]) {
|
||||
if (now - _this.previouslyUnhooked[childID] < UNHOOK_LOOP_DETECT_MS) {
|
||||
previousParentID = NULL_UUID;
|
||||
previousParentJointIndex = -1;
|
||||
}
|
||||
}
|
||||
_this.previouslyUnhooked[childID] = now;
|
||||
|
||||
Entities.editEntity(childID, {
|
||||
parentID: previousParentID,
|
||||
parentJointIndex: previousParentJointIndex
|
||||
});
|
||||
} else {
|
||||
Entities.editEntity(childID, { parentID: NULL_UUID });
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.getTargetProps = function (controllerData) {
|
||||
// nearbyEntityProperties is already sorted by length from controller
|
||||
var nearbyEntityProperties = controllerData.nearbyEntityProperties[this.hand];
|
||||
|
@ -168,11 +242,13 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
|
|||
var targetProps = this.getTargetProps(controllerData);
|
||||
if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE &&
|
||||
controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) {
|
||||
this.checkForUnexpectedChildren(controllerData);
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
|
||||
if (targetProps) {
|
||||
if (propsArePhysical(targetProps) || propsAreCloneDynamic(targetProps)) {
|
||||
if ((propsArePhysical(targetProps) || propsAreCloneDynamic(targetProps)) &&
|
||||
targetProps.parentID == NULL_UUID) {
|
||||
return makeRunningValues(false, [], []); // let nearActionGrabEntity handle it
|
||||
} else {
|
||||
this.targetEntityID = targetProps.id;
|
||||
|
@ -188,16 +264,23 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
|
|||
if (this.grabbing) {
|
||||
if (controllerData.triggerClicks[this.hand] < TRIGGER_OFF_VALUE &&
|
||||
controllerData.secondaryValues[this.hand] < TRIGGER_OFF_VALUE) {
|
||||
this.endNearParentingGrabEntity();
|
||||
this.endNearParentingGrabEntity(controllerData);
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
|
||||
var props = controllerData.nearbyEntityPropertiesByID[this.targetEntityID];
|
||||
if (!props) {
|
||||
// entity was deleted
|
||||
this.grabbing = false;
|
||||
this.targetEntityID = null;
|
||||
this.hapticTargetID = null;
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
|
||||
var props = Entities.getEntityProperties(this.targetEntityID);
|
||||
if (!this.thisHandIsParent(props)) {
|
||||
this.grabbing = false;
|
||||
this.targetEntityID = null;
|
||||
this.hapticTargetID = null;
|
||||
if (this.checkForChildTooFarAway(controllerData)) {
|
||||
// if the held entity moves too far from the hand, release it
|
||||
print("nearParentGrabEntity -- autoreleasing held item because it is far from hand");
|
||||
this.endNearParentingGrabEntity(controllerData);
|
||||
return makeRunningValues(false, [], []);
|
||||
}
|
||||
|
||||
|
@ -205,7 +288,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
|
|||
Entities.callEntityMethod(this.targetEntityID, "continueNearGrab", args);
|
||||
} else {
|
||||
// still searching / highlighting
|
||||
var readiness = this.isReady (controllerData);
|
||||
var readiness = this.isReady(controllerData);
|
||||
if (!readiness.active) {
|
||||
return readiness;
|
||||
}
|
||||
|
@ -218,7 +301,6 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
|
|||
var worldEntityProps = controllerData.nearbyEntityProperties[this.hand];
|
||||
var cloneID = cloneEntity(targetProps, worldEntityProps);
|
||||
var cloneProps = Entities.getEntityProperties(cloneID);
|
||||
|
||||
this.grabbing = true;
|
||||
this.targetEntityID = cloneID;
|
||||
this.startNearParentingGrabEntity(controllerData, cloneProps);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
|
||||
/* global Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays,
|
||||
/* global module, Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, Xform,
|
||||
MSECS_PER_SEC:true , LEFT_HAND:true, RIGHT_HAND:true, NULL_UUID:true, AVATAR_SELF_ID:true, FORBIDDEN_GRAB_TYPES:true,
|
||||
HAPTIC_PULSE_STRENGTH:true, HAPTIC_PULSE_DURATION:true, ZERO_VEC:true, ONE_VEC:true,
|
||||
DEFAULT_REGISTRATION_POINT:true, INCHES_TO_METERS:true,
|
||||
|
@ -40,7 +40,12 @@
|
|||
entityHasActions:true,
|
||||
ensureDynamic:true,
|
||||
findGroupParent:true,
|
||||
BUMPER_ON_VALUE:true
|
||||
BUMPER_ON_VALUE:true,
|
||||
findHandChildEntities:true,
|
||||
TEAR_AWAY_DISTANCE:true,
|
||||
TEAR_AWAY_COUNT:true,
|
||||
TEAR_AWAY_CHECK_TIME:true,
|
||||
distanceBetweenPointAndEntityBoundingBox:true
|
||||
*/
|
||||
|
||||
MSECS_PER_SEC = 1000.0;
|
||||
|
@ -79,6 +84,10 @@ COLORS_GRAB_DISTANCE_HOLD = { red: 238, green: 75, blue: 214 };
|
|||
|
||||
NEAR_GRAB_RADIUS = 1.0;
|
||||
|
||||
TEAR_AWAY_DISTANCE = 0.1; // ungrab an entity if its bounding-box moves this far from the hand
|
||||
TEAR_AWAY_COUNT = 2; // multiply by TEAR_AWAY_CHECK_TIME to know how long the item must be away
|
||||
TEAR_AWAY_CHECK_TIME = 0.15; // seconds, duration between checks
|
||||
|
||||
DISPATCHER_PROPERTIES = [
|
||||
"position",
|
||||
"registrationPoint",
|
||||
|
@ -193,17 +202,6 @@ entityIsDistanceGrabbable = function(props) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// XXX
|
||||
// var distance = Vec3.distance(props.position, handPosition);
|
||||
// this.otherGrabbingUUID = entityIsGrabbedByOther(entityID);
|
||||
// if (this.otherGrabbingUUID !== null) {
|
||||
// // don't distance grab something that is already grabbed.
|
||||
// if (debug) {
|
||||
// print("distance grab is skipping '" + props.name + "': already grabbed by another.");
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
@ -296,8 +294,9 @@ ensureDynamic = function (entityID) {
|
|||
};
|
||||
|
||||
findGroupParent = function (controllerData, targetProps) {
|
||||
while (targetProps.parentID && targetProps.parentID !== NULL_UUID) {
|
||||
// XXX use controllerData.nearbyEntityPropertiesByID ?
|
||||
while (targetProps.parentID &&
|
||||
targetProps.parentID !== NULL_UUID &&
|
||||
Entities.getNestableType(targetProps.parentID) == "entity") {
|
||||
var parentProps = Entities.getEntityProperties(targetProps.parentID, DISPATCHER_PROPERTIES);
|
||||
if (!parentProps) {
|
||||
break;
|
||||
|
@ -310,6 +309,50 @@ findGroupParent = function (controllerData, targetProps) {
|
|||
return targetProps;
|
||||
};
|
||||
|
||||
|
||||
findHandChildEntities = function(hand) {
|
||||
// find children of avatar's hand joint
|
||||
var handJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? "RightHand" : "LeftHand");
|
||||
var children = Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, handJointIndex);
|
||||
children = children.concat(Entities.getChildrenIDsOfJoint(AVATAR_SELF_ID, handJointIndex));
|
||||
|
||||
// find children of faux controller joint
|
||||
var controllerJointIndex = getControllerJointIndex(hand);
|
||||
children = children.concat(Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, controllerJointIndex));
|
||||
children = children.concat(Entities.getChildrenIDsOfJoint(AVATAR_SELF_ID, controllerJointIndex));
|
||||
|
||||
// find children of faux camera-relative controller joint
|
||||
var controllerCRJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ?
|
||||
"_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" :
|
||||
"_CAMERA_RELATIVE_CONTROLLER_LEFTHAND");
|
||||
children = children.concat(Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, controllerCRJointIndex));
|
||||
children = children.concat(Entities.getChildrenIDsOfJoint(AVATAR_SELF_ID, controllerCRJointIndex));
|
||||
|
||||
return children.filter(function (childID) {
|
||||
var childType = Entities.getNestableType(childID);
|
||||
return childType == "entity";
|
||||
});
|
||||
};
|
||||
|
||||
distanceBetweenPointAndEntityBoundingBox = function(point, entityProps) {
|
||||
var entityXform = new Xform(entityProps.rotation, entityProps.position);
|
||||
var localPoint = entityXform.inv().xformPoint(point);
|
||||
var minOffset = Vec3.multiplyVbyV(entityProps.registrationPoint, entityProps.dimensions);
|
||||
var maxOffset = Vec3.multiplyVbyV(Vec3.subtract(ONE_VEC, entityProps.registrationPoint), entityProps.dimensions);
|
||||
var localMin = Vec3.subtract(entityXform.trans, minOffset);
|
||||
var localMax = Vec3.sum(entityXform.trans, maxOffset);
|
||||
|
||||
var v = {x: localPoint.x, y: localPoint.y, z: localPoint.z};
|
||||
v.x = Math.max(v.x, localMin.x);
|
||||
v.x = Math.min(v.x, localMax.x);
|
||||
v.y = Math.max(v.y, localMin.y);
|
||||
v.y = Math.min(v.y, localMax.y);
|
||||
v.z = Math.max(v.z, localMin.z);
|
||||
v.z = Math.min(v.z, localMax.z);
|
||||
|
||||
return Vec3.distance(v, localPoint);
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = {
|
||||
makeDispatcherModuleParameters: makeDispatcherModuleParameters,
|
||||
|
@ -319,6 +362,7 @@ if (typeof module !== 'undefined') {
|
|||
LEFT_HAND: LEFT_HAND,
|
||||
RIGHT_HAND: RIGHT_HAND,
|
||||
BUMPER_ON_VALUE: BUMPER_ON_VALUE,
|
||||
TEAR_AWAY_DISTANCE: TEAR_AWAY_DISTANCE,
|
||||
propsArePhysical: propsArePhysical,
|
||||
entityIsGrabbable: entityIsGrabbable,
|
||||
NEAR_GRAB_RADIUS: NEAR_GRAB_RADIUS,
|
||||
|
|
Loading…
Reference in a new issue