"use strict"; // nearParentGrabOverlay.js//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html /* global Script, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, getControllerJointIndex, enableDispatcherModule, disableDispatcherModule, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, makeDispatcherModuleParameters, Overlays, makeRunningValues, Vec3, resizeTablet, getTabletWidthFromSettings, NEAR_GRAB_RADIUS, HMD, Uuid, getEnabledModuleByName*/ Script.include("/~/system/libraries/controllerDispatcherUtils.js");Script.include("/~/system/libraries/utils.js"); (function() { // XXX this.ignoreIK = (grabbableData.ignoreIK !== undefined) ? grabbableData.ignoreIK : true; // XXX this.kinematicGrab = (grabbableData.kinematic !== undefined) ? grabbableData.kinematic : NEAR_GRABBING_KINEMATIC; function NearParentingGrabOverlay(hand) { this.hand = hand; this.grabbedThingID = null; this.previousParentID = {}; this.previousParentJointIndex = {}; this.previouslyUnhooked = {}; this.robbed = false; this.parameters = makeDispatcherModuleParameters( 90, this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], [], 100); // XXX does handJointIndex change if the avatar changes? this.handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); this.getOtherModule = function() { return (this.hand === RIGHT_HAND) ? leftNearParentingGrabOverlay : rightNearParentingGrabOverlay; }; this.otherHandIsParent = function(props) { return this.getOtherModule().thisHandIsParent(props); }; this.isGrabbedThingVisible = function() { return Entities.getEntityProperties(this.grabbedThingID, ["visible"]).visible; }; this.thisHandIsParent = function(props) { if (props.parentID !== MyAvatar.sessionUUID && props.parentID !== MyAvatar.SELF_ID) { return false; } var handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); if (props.parentJointIndex === handJointIndex) { return true; } var controllerJointIndex = this.controllerJointIndex; if (props.parentJointIndex === controllerJointIndex) { return true; } var controllerCRJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); if (props.parentJointIndex === controllerCRJointIndex) { return true; } return false; }; this.getGrabbedProperties = function() { return { position: Entities.getEntityProperties(this.grabbedThingID, ["position"]).position, rotation: Entities.getEntityProperties(this.grabbedThingID, ["rotation"]).rotation, parentID: Entities.getEntityProperties(this.grabbedThingID, ["parentID"]).parentID, parentJointIndex: Entities.getEntityProperties(this.grabbedThingID, ["parentJointIndex"]).parentJointIndex, dynamic: false, shapeType: "none" }; }; this.startNearParentingGrabOverlay = function (controllerData) { Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); this.controllerJointIndex = getControllerJointIndex(this.hand); var handJointIndex = this.controllerJointIndex; var grabbedProperties = this.getGrabbedProperties(); var reparentProps = { parentID: MyAvatar.SELF_ID, parentJointIndex: handJointIndex, velocity: {x: 0, y: 0, z: 0}, angularVelocity: {x: 0, y: 0, z: 0} }; if (this.thisHandIsParent(grabbedProperties)) { // this should never happen, but if it does, don't set previous parent to be this hand. // this.previousParentID[this.grabbedThingID] = NULL; // this.previousParentJointIndex[this.grabbedThingID] = -1; } else if (this.otherHandIsParent(grabbedProperties)) { // the other hand is parent. Steal the object and information var otherModule = this.getOtherModule(); this.previousParentID[this.grabbedThingID] = otherModule.previousParentID[this.grabbedThingID]; this.previousParentJointIndex[this.grabbedThingID] = otherModule.previousParentJointIndex[this.grabbedThingID]; otherModule.robbed = true; } else { this.previousParentID[this.grabbedThingID] = grabbedProperties.parentID; this.previousParentJointIndex[this.grabbedThingID] = grabbedProperties.parentJointIndex; } // resizeTablet to counter adjust offsets to account for change of scale from sensorToWorldMatrix if (HMD.tabletID && this.grabbedThingID === HMD.tabletID) { reparentAndScaleTablet(getTabletWidthFromSettings(), reparentProps); } else { Entities.editEntity(this.grabbedThingID, reparentProps); } Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ action: 'grab', grabbedEntity: this.grabbedThingID, joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" })); }; this.endNearParentingGrabOverlay = function () { var previousParentID = this.previousParentID[this.grabbedThingID]; if ((previousParentID === Uuid.NONE || previousParentID === null) && !this.robbed) { Entities.editEntity(this.grabbedThingID, { parentID: Uuid.NONE, parentJointIndex: -1 }); } else if (!this.robbed){ // before we grabbed it, overlay was a child of something; put it back. Entities.editEntity(this.grabbedThingID, { parentID: this.previousParentID[this.grabbedThingID], parentJointIndex: this.previousParentJointIndex[this.grabbedThingID] }); // resizeTablet to counter adjust offsets to account for change of scale from sensorToWorldMatrix if (HMD.tabletID && this.grabbedThingID === HMD.tabletID) { resizeTablet(getTabletWidthFromSettings(), this.previousParentJointIndex[this.grabbedThingID]); } } Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ action: 'release', grabbedEntity: this.grabbedThingID, joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" })); this.grabbedThingID = null; }; this.getTargetID = function(overlays, controllerData) { var sensorScaleFactor = MyAvatar.sensorToWorldScale; for (var i = 0; i < overlays.length; i++) { var overlayPosition = Entities.getEntityProperties(overlays[i], ["position"]).position; var handPosition = controllerData.controllerLocations[this.hand].position; var distance = Vec3.distance(overlayPosition, handPosition); if (distance <= NEAR_GRAB_RADIUS * sensorScaleFactor) { if (overlays[i] !== HMD.miniTabletID || controllerData.secondaryValues[this.hand] === 0) { // Don't grab mini tablet with grip. return overlays[i]; } } } return null; }; this.isEditing = function () { var inEditModeModule = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightHandInEditMode" : "LeftHandInEditMode"); if (inEditModeModule && inEditModeModule.isEditing) { return true; } var inVREditModeModule = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightHandInVREditMode" : "LeftHandInVREditMode"); if (inVREditModeModule && inVREditModeModule.isEditing) { return true; } return false; }; this.isReady = function (controllerData) { if ((controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0) || this.isEditing()) { this.robbed = false; return makeRunningValues(false, [], []); } this.grabbedThingID = null; var candidateOverlays = controllerData.nearbyOverlayIDs[this.hand]; var grabbableOverlays = candidateOverlays.filter(function(overlayID) { // V8TODO: check if this works return Entities.getEntityProperties(overlayID, ["grab.grabbable"]).grab.grabbable; }); var targetID = this.getTargetID(grabbableOverlays, controllerData); if (targetID && !this.robbed) { this.grabbedThingID = targetID; this.startNearParentingGrabOverlay(controllerData); return makeRunningValues(true, [this.grabbedThingID], []); } else { return makeRunningValues(false, [], []); } }; this.run = function (controllerData) { if ((controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0) || this.isEditing() || !this.isGrabbedThingVisible()) { this.endNearParentingGrabOverlay(); this.robbed = false; return makeRunningValues(false, [], []); } else { // check if someone stole the target from us var grabbedProperties = this.getGrabbedProperties(); if (!this.thisHandIsParent(grabbedProperties)) { return makeRunningValues(false, [], []); } return makeRunningValues(true, [this.grabbedThingID], []); } }; this.cleanup = function () { if (this.grabbedThingID) { this.endNearParentingGrabOverlay(); } }; } var leftNearParentingGrabOverlay = new NearParentingGrabOverlay(LEFT_HAND); var rightNearParentingGrabOverlay = new NearParentingGrabOverlay(RIGHT_HAND); enableDispatcherModule("LeftNearParentingGrabOverlay", leftNearParentingGrabOverlay); enableDispatcherModule("RightNearParentingGrabOverlay", rightNearParentingGrabOverlay); function cleanup() { leftNearParentingGrabOverlay.cleanup(); rightNearParentingGrabOverlay.cleanup(); disableDispatcherModule("LeftNearParentingGrabOverlay"); disableDispatcherModule("RightNearParentingGrabOverlay"); } Script.scriptEnding.connect(cleanup);}());
"use strict";
// nearParentGrabOverlay.js
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/* global Script, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, getControllerJointIndex,
enableDispatcherModule, disableDispatcherModule, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION,
makeDispatcherModuleParameters, Overlays, makeRunningValues, Vec3, resizeTablet, getTabletWidthFromSettings,
NEAR_GRAB_RADIUS, HMD, Uuid, getEnabledModuleByName
*/
Script.include("/~/system/libraries/controllerDispatcherUtils.js");
Script.include("/~/system/libraries/utils.js");
(function() {
// XXX this.ignoreIK = (grabbableData.ignoreIK !== undefined) ? grabbableData.ignoreIK : true;
// XXX this.kinematicGrab = (grabbableData.kinematic !== undefined) ? grabbableData.kinematic : NEAR_GRABBING_KINEMATIC;
function NearParentingGrabOverlay(hand) {
this.hand = hand;
this.grabbedThingID = null;
this.previousParentID = {};
this.previousParentJointIndex = {};
this.previouslyUnhooked = {};
this.robbed = false;
this.parameters = makeDispatcherModuleParameters(
90,
this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"],
[],
100);
// XXX does handJointIndex change if the avatar changes?
this.handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand");
this.getOtherModule = function() {
return (this.hand === RIGHT_HAND) ? leftNearParentingGrabOverlay : rightNearParentingGrabOverlay;
};
this.otherHandIsParent = function(props) {
return this.getOtherModule().thisHandIsParent(props);
};
this.isGrabbedThingVisible = function() {
return Entities.getEntityProperties(this.grabbedThingID, ["visible"]).visible;
};
this.thisHandIsParent = function(props) {
if (props.parentID !== MyAvatar.sessionUUID && props.parentID !== MyAvatar.SELF_ID) {
return false;
}
var handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand");
if (props.parentJointIndex === handJointIndex) {
return true;
}
var controllerJointIndex = this.controllerJointIndex;
if (props.parentJointIndex === controllerJointIndex) {
return true;
}
var controllerCRJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ?
"_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" :
"_CAMERA_RELATIVE_CONTROLLER_LEFTHAND");
if (props.parentJointIndex === controllerCRJointIndex) {
return true;
}
return false;
};
this.getGrabbedProperties = function() {
return {
position: Entities.getEntityProperties(this.grabbedThingID, ["position"]).position,
rotation: Entities.getEntityProperties(this.grabbedThingID, ["rotation"]).rotation,
parentID: Entities.getEntityProperties(this.grabbedThingID, ["parentID"]).parentID,
parentJointIndex: Entities.getEntityProperties(this.grabbedThingID, ["parentJointIndex"]).parentJointIndex,
dynamic: false,
shapeType: "none"
};
};
this.startNearParentingGrabOverlay = function (controllerData) {
Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand);
this.controllerJointIndex = getControllerJointIndex(this.hand);
var handJointIndex = this.controllerJointIndex;
var grabbedProperties = this.getGrabbedProperties();
var reparentProps = {
parentID: MyAvatar.SELF_ID,
parentJointIndex: handJointIndex,
velocity: {x: 0, y: 0, z: 0},
angularVelocity: {x: 0, y: 0, z: 0}
};
if (this.thisHandIsParent(grabbedProperties)) {
// this should never happen, but if it does, don't set previous parent to be this hand.
// this.previousParentID[this.grabbedThingID] = NULL;
// this.previousParentJointIndex[this.grabbedThingID] = -1;
} else if (this.otherHandIsParent(grabbedProperties)) {
// the other hand is parent. Steal the object and information
var otherModule = this.getOtherModule();
this.previousParentID[this.grabbedThingID] = otherModule.previousParentID[this.grabbedThingID];
this.previousParentJointIndex[this.grabbedThingID] = otherModule.previousParentJointIndex[this.grabbedThingID];
otherModule.robbed = true;
} else {
this.previousParentID[this.grabbedThingID] = grabbedProperties.parentID;
this.previousParentJointIndex[this.grabbedThingID] = grabbedProperties.parentJointIndex;
}
// resizeTablet to counter adjust offsets to account for change of scale from sensorToWorldMatrix
if (HMD.tabletID && this.grabbedThingID === HMD.tabletID) {
reparentAndScaleTablet(getTabletWidthFromSettings(), reparentProps);
} else {
Entities.editEntity(this.grabbedThingID, reparentProps);
}
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
action: 'grab',
grabbedEntity: this.grabbedThingID,
joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"
}));
};
this.endNearParentingGrabOverlay = function () {
var previousParentID = this.previousParentID[this.grabbedThingID];
if ((previousParentID === Uuid.NONE || previousParentID === null) && !this.robbed) {
Entities.editEntity(this.grabbedThingID, {
parentID: Uuid.NONE,
parentJointIndex: -1
});
} else if (!this.robbed){
// before we grabbed it, overlay was a child of something; put it back.
Entities.editEntity(this.grabbedThingID, {
parentID: this.previousParentID[this.grabbedThingID],
parentJointIndex: this.previousParentJointIndex[this.grabbedThingID]
});
// resizeTablet to counter adjust offsets to account for change of scale from sensorToWorldMatrix
if (HMD.tabletID && this.grabbedThingID === HMD.tabletID) {
resizeTablet(getTabletWidthFromSettings(), this.previousParentJointIndex[this.grabbedThingID]);
}
}
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
action: 'release',
grabbedEntity: this.grabbedThingID,
joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"
}));
this.grabbedThingID = null;
};
this.getTargetID = function(overlays, controllerData) {
var sensorScaleFactor = MyAvatar.sensorToWorldScale;
for (var i = 0; i < overlays.length; i++) {
var overlayPosition = Entities.getEntityProperties(overlays[i], ["position"]).position;
var handPosition = controllerData.controllerLocations[this.hand].position;
var distance = Vec3.distance(overlayPosition, handPosition);
if (distance <= NEAR_GRAB_RADIUS * sensorScaleFactor) {
if (overlays[i] !== HMD.miniTabletID || controllerData.secondaryValues[this.hand] === 0) {
// Don't grab mini tablet with grip.
return overlays[i];
}
}
}
return null;
};
this.isEditing = function () {
var inEditModeModule = getEnabledModuleByName(this.hand === RIGHT_HAND
? "RightHandInEditMode" : "LeftHandInEditMode");
if (inEditModeModule && inEditModeModule.isEditing) {
return true;
}
var inVREditModeModule = getEnabledModuleByName(this.hand === RIGHT_HAND
? "RightHandInVREditMode" : "LeftHandInVREditMode");
if (inVREditModeModule && inVREditModeModule.isEditing) {
return true;
}
return false;
};
this.isReady = function (controllerData) {
if ((controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0)
|| this.isEditing()) {
this.robbed = false;
return makeRunningValues(false, [], []);
}
this.grabbedThingID = null;
var candidateOverlays = controllerData.nearbyOverlayIDs[this.hand];
var grabbableOverlays = candidateOverlays.filter(function(overlayID) {
// V8TODO: check if this works
return Entities.getEntityProperties(overlayID, ["grab.grabbable"]).grab.grabbable;
});
var targetID = this.getTargetID(grabbableOverlays, controllerData);
if (targetID && !this.robbed) {
this.grabbedThingID = targetID;
this.startNearParentingGrabOverlay(controllerData);
return makeRunningValues(true, [this.grabbedThingID], []);
} else {
return makeRunningValues(false, [], []);
}
};
this.run = function (controllerData) {
if ((controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0)
|| this.isEditing() || !this.isGrabbedThingVisible()) {
this.endNearParentingGrabOverlay();
this.robbed = false;
return makeRunningValues(false, [], []);
} else {
// check if someone stole the target from us
var grabbedProperties = this.getGrabbedProperties();
if (!this.thisHandIsParent(grabbedProperties)) {
return makeRunningValues(false, [], []);
}
return makeRunningValues(true, [this.grabbedThingID], []);
}
};
this.cleanup = function () {
if (this.grabbedThingID) {
this.endNearParentingGrabOverlay();
}
};
}
var leftNearParentingGrabOverlay = new NearParentingGrabOverlay(LEFT_HAND);
var rightNearParentingGrabOverlay = new NearParentingGrabOverlay(RIGHT_HAND);
enableDispatcherModule("LeftNearParentingGrabOverlay", leftNearParentingGrabOverlay);
enableDispatcherModule("RightNearParentingGrabOverlay", rightNearParentingGrabOverlay);
function cleanup() {
leftNearParentingGrabOverlay.cleanup();
rightNearParentingGrabOverlay.cleanup();
disableDispatcherModule("LeftNearParentingGrabOverlay");
disableDispatcherModule("RightNearParentingGrabOverlay");
}
Script.scriptEnding.connect(cleanup);
}());