finshed handControllerGrab refactoring

This commit is contained in:
Dante Ruiz 2017-09-01 17:07:26 -07:00
parent 9bd368c0a9
commit 3b357ad61e
13 changed files with 302 additions and 150 deletions

View file

@ -5,11 +5,12 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/*jslint bitwise: true */
/* jslint bitwise: true */
/* global Script, Entities, Overlays, Controller, Vec3, Quat, getControllerWorldLocation, RayPick,
controllerDispatcherPlugins, controllerDispatcherPluginsNeedSort, entityIsGrabbable,
LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS, DEFAULT_SEARCH_SPHERE_DISTANCE, DISPATCHER_PROPERTIES
controllerDispatcherPlugins:true, controllerDispatcherPluginsNeedSort:true, entityIsGrabbable:true,
LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS, DEFAULT_SEARCH_SPHERE_DISTANCE, DISPATCHER_PROPERTIES,
getGrabPointSphereOffset
*/
controllerDispatcherPlugins = {};
@ -21,18 +22,16 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js");
(function() {
var _this = this;
var NEAR_MIN_RADIUS = 0.1;
var NEAR_MAX_RADIUS = 1.0;
var TARGET_UPDATE_HZ = 60; // 50hz good enough, but we're using update
var BASIC_TIMER_INTERVAL_MS = 1000 / TARGET_UPDATE_HZ;
var lastInterval = Date.now();
var intervalCount = 0;
var totalDelta = 0;
var totalVariance = 0;
var highVarianceCount = 0;
var veryhighVarianceCount = 0;
this.lastInterval = Date.now();
this.intervalCount = 0;
this.totalDelta = 0;
this.totalVariance = 0;
this.highVarianceCount = 0;
this.veryhighVarianceCount = 0;
this.tabletID = null;
this.blacklist = [];
@ -63,7 +62,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js");
this.unmarkSlotsForPluginName = function (runningPluginName) {
// this is used to free activity-slots when a plugin is deactivated while it's running.
for (var activitySlot in _this.activitySlots) {
if (activitySlot.hasOwnProperty(activitySlot) && _this.activitySlots[activitySlot] == runningPluginName) {
if (activitySlot.hasOwnProperty(activitySlot) && _this.activitySlots[activitySlot] === runningPluginName) {
_this.activitySlots[activitySlot] = false;
}
}
@ -106,23 +105,23 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js");
};
this.updateTimings = function () {
intervalCount++;
_this.intervalCount++;
var thisInterval = Date.now();
var deltaTimeMsec = thisInterval - lastInterval;
var deltaTimeMsec = thisInterval - _this.lastInterval;
var deltaTime = deltaTimeMsec / 1000;
lastInterval = thisInterval;
_this.lastInterval = thisInterval;
totalDelta += deltaTimeMsec;
_this.totalDelta += deltaTimeMsec;
var variance = Math.abs(deltaTimeMsec - BASIC_TIMER_INTERVAL_MS);
totalVariance += variance;
_this.totalVariance += variance;
if (variance > 1) {
highVarianceCount++;
_this.highVarianceCount++;
}
if (variance > 5) {
veryhighVarianceCount++;
_this.veryhighVarianceCount++;
}
return deltaTime;
@ -132,13 +131,12 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js");
if (HMD.tabletID !== _this.tabletID) {
RayPick.setIgnoreOverlays(_this.leftControllerRayPick, [HMD.tabletID]);
RayPick.setIgnoreOverlays(_this.rightControllerRayPick, [HMD.tabletID]);
tabletIgnored = true
}
}
};
this.update = function () {
var deltaTime = this.updateTimings();
this.setIgnoreTablet()
var deltaTime = this.updateTimings();
this.setIgnoreTablet();
if (controllerDispatcherPluginsNeedSort) {
this.orderedPluginNames = [];
@ -167,8 +165,10 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js");
controllerDispatcherPluginsNeedSort = false;
}
var controllerLocations = [_this.dataGatherers.leftControllerLocation(),
_this.dataGatherers.rightControllerLocation()];
var controllerLocations = [
_this.dataGatherers.leftControllerLocation(),
_this.dataGatherers.rightControllerLocation()
];
// find 3d overlays near each hand
var nearbyOverlayIDs = [];
@ -221,7 +221,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js");
length: 1000
};
if (rayPicks[h].type == RayPick.INTERSECTED_ENTITY) {
if (rayPicks[h].type === RayPick.INTERSECTED_ENTITY) {
// XXX check to make sure this one isn't already in nearbyEntityProperties?
if (rayPicks[h].distance < NEAR_GRAB_PICK_RADIUS) {
var nearEntityID = rayPicks[h].objectID;
@ -259,7 +259,6 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js");
var candidatePlugin = controllerDispatcherPlugins[orderedPluginName];
if (_this.slotsAreAvailableForPlugin(candidatePlugin)) {
//print(orderedPluginName);
var readiness = candidatePlugin.isReady(controllerData, deltaTime);
if (readiness.active) {
// this plugin will start. add it to the list of running plugins and mark the
@ -297,7 +296,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js");
this.setBlacklist = function() {
RayPick.setIgnoreEntities(_this.leftControllerRayPick, this.blacklist);
RayPick.setIgnoreEntities(_this.rightControllerRayPick, this.blacklist);
};
var MAPPING_NAME = "com.highfidelity.controllerDispatcher";
@ -314,19 +313,6 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js");
Controller.enableMapping(MAPPING_NAME);
this.mouseRayPick = RayPick.createRayPick({
joint: "Mouse",
filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS,
enabled: true
});
this.mouseHudRayPick = RayPick.createRayPick({
joint: "Mouse",
filter: RayPick.PICK_HUD,
enabled: true
});
this.leftControllerRayPick = RayPick.createRayPick({
joint: "_CONTROLLER_LEFTHAND",
filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS,
@ -357,7 +343,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js");
});
this.handleHandMessage = function(channel, message, sender) {
var data
var data;
if (sender === MyAvatar.sessionUUID) {
try {
if (channel === 'Hifi-Hand-RayPick-Blacklist') {
@ -365,33 +351,29 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js");
var action = data.action;
var id = data.id;
var index = this.blacklis.indexOf(id);
if (action === 'add' && index === -1) {
this.blacklist.push(id);
//this.setBlacklist();
this.setBlacklist();
}
if (action === 'remove') {
if (index > -1) {
blacklist.splice(index, 1);
//this.setBlacklist();
this.blacklist.splice(index, 1);
this.setBlacklist();
}
}
}
} catch (e) {
print("WARNING: handControllerGrab.js -- error parsing Hifi-Hand-RayPick-Blacklist message: " + message);
}
}
};
this.cleanup = function () {
Script.update.disconnect(_this.update);
Controller.disableMapping(MAPPING_NAME);
// RayPick.removeRayPick(_this.mouseRayPick);
RayPick.removeRayPick(_this.leftControllerRayPick);
RayPick.removeRayPick(_this.rightControllerRayPick);
RayPick.removeRayPick(_this.rightControllerHudRayPick);

View file

@ -6,37 +6,37 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/* global Camera, HMD, MyAvatar, controllerDispatcherPlugins, Quat, Vec3, Overlays,
MSECS_PER_SEC, LEFT_HAND, RIGHT_HAND, NULL_UUID, AVATAR_SELF_ID, FORBIDDEN_GRAB_TYPES,
HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, ZERO_VEC, ONE_VEC, DEFAULT_REGISTRATION_POINT, INCHES_TO_METERS,
TRIGGER_OFF_VALUE,
TRIGGER_ON_VALUE,
PICK_MAX_DISTANCE,
DEFAULT_SEARCH_SPHERE_DISTANCE,
NEAR_GRAB_PICK_RADIUS,
COLORS_GRAB_SEARCHING_HALF_SQUEEZE,
COLORS_GRAB_SEARCHING_FULL_SQUEEZE,
COLORS_GRAB_DISTANCE_HOLD,
NEAR_GRAB_RADIUS,
DISPATCHER_PROPERTIES,
/* global Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays,
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,
TRIGGER_OFF_VALUE:true,
TRIGGER_ON_VALUE:true,
PICK_MAX_DISTANCE:true,
DEFAULT_SEARCH_SPHERE_DISTANCE:true,
NEAR_GRAB_PICK_RADIUS:true,
COLORS_GRAB_SEARCHING_HALF_SQUEEZE:true,
COLORS_GRAB_SEARCHING_FULL_SQUEEZE:true,
COLORS_GRAB_DISTANCE_HOLD:true,
NEAR_GRAB_RADIUS:true,
DISPATCHER_PROPERTIES:true,
Entities,
makeDispatcherModuleParameters,
makeRunningValues,
enableDispatcherModule,
disableDispatcherModule,
getEnabledModuleByName,
getGrabbableData,
entityIsGrabbable,
entityIsDistanceGrabbable,
getControllerJointIndex,
propsArePhysical,
controllerDispatcherPluginsNeedSort,
projectOntoXYPlane,
projectOntoEntityXYPlane,
projectOntoOverlayXYPlane,
entityHasActions,
ensureDynamic,
findGroupParent
makeDispatcherModuleParameters:true,
makeRunningValues:true,
enableDispatcherModule:true,
disableDispatcherModule:true,
getEnabledModuleByName:true,
getGrabbableData:true,
entityIsGrabbable:true,
entityIsDistanceGrabbable:true,
getControllerJointIndex:true,
propsArePhysical:true,
controllerDispatcherPluginsNeedSort:true,
projectOntoXYPlane:true,
projectOntoEntityXYPlane:true,
projectOntoOverlayXYPlane:true,
entityHasActions:true,
ensureDynamic:true,
findGroupParent:true
*/
MSECS_PER_SEC = 1000.0;
@ -69,11 +69,8 @@ COLORS_GRAB_SEARCHING_HALF_SQUEEZE = { red: 10, green: 10, blue: 255 };
COLORS_GRAB_SEARCHING_FULL_SQUEEZE = { red: 250, green: 10, blue: 10 };
COLORS_GRAB_DISTANCE_HOLD = { red: 238, green: 75, blue: 214 };
NEAR_GRAB_RADIUS = 0.1;
DISPATCHER_PROPERTIES = [
"position",
"registrationPoint",
@ -92,9 +89,6 @@ DISPATCHER_PROPERTIES = [
"userData"
];
// priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step
// activitySlots -- indicates which "slots" must not yet be in use for this module to start
// requiredDataForReady -- which "situation" parts this module looks at to decide if it will start
@ -211,12 +205,12 @@ getControllerJointIndex = function (hand) {
var controllerJointIndex = -1;
if (Camera.mode === "first person") {
controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ?
"_CONTROLLER_RIGHTHAND" :
"_CONTROLLER_LEFTHAND");
"_CONTROLLER_RIGHTHAND" :
"_CONTROLLER_LEFTHAND");
} else if (Camera.mode === "third person") {
controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ?
"_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" :
"_CAMERA_RELATIVE_CONTROLLER_LEFTHAND");
"_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" :
"_CAMERA_RELATIVE_CONTROLLER_LEFTHAND");
}
return controllerJointIndex;
@ -229,19 +223,24 @@ propsArePhysical = function (props) {
if (!props.dynamic) {
return false;
}
var isPhysical = (props.shapeType && props.shapeType != 'none');
var isPhysical = (props.shapeType && props.shapeType !== 'none');
return isPhysical;
};
projectOntoXYPlane = function (worldPos, position, rotation, dimensions, registrationPoint) {
var invRot = Quat.inverse(rotation);
var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(worldPos, position));
var invDimensions = { x: 1 / dimensions.x,
y: 1 / dimensions.y,
z: 1 / dimensions.z };
var invDimensions = {
x: 1 / dimensions.x,
y: 1 / dimensions.y,
z: 1 / dimensions.z
};
var normalizedPos = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), registrationPoint);
return { x: normalizedPos.x * dimensions.x,
y: (1 - normalizedPos.y) * dimensions.y }; // flip y-axis
return {
x: normalizedPos.x * dimensions.x,
y: (1 - normalizedPos.y) * dimensions.y // flip y-axis
};
};
projectOntoEntityXYPlane = function (entityID, worldPos, props) {
@ -257,14 +256,14 @@ projectOntoOverlayXYPlane = function projectOntoOverlayXYPlane(overlayID, worldP
if (dpi) {
// Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property is used as a scale.
var resolution = Overlays.getProperty(overlayID, "resolution");
resolution.z = 1; // Circumvent divide-by-zero.
resolution.z = 1; // Circumvent divide-by-zero.
var scale = Overlays.getProperty(overlayID, "dimensions");
scale.z = 0.01; // overlay dimensions are 2D, not 3D.
scale.z = 0.01; // overlay dimensions are 2D, not 3D.
dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale);
} else {
dimensions = Overlays.getProperty(overlayID, "dimensions");
if (dimensions.z) {
dimensions.z = 0.01; // overlay dimensions are 2D, not 3D.
dimensions.z = 0.01; // overlay dimensions are 2D, not 3D.
}
}
@ -279,7 +278,7 @@ ensureDynamic = function (entityID) {
// if we distance hold something and keep it very still before releasing it, it ends up
// non-dynamic in bullet. If it's too still, give it a little bounce so it will fall.
var props = Entities.getEntityProperties(entityID, ["velocity", "dynamic", "parentID"]);
if (props.dynamic && props.parentID == NULL_UUID) {
if (props.dynamic && props.parentID === NULL_UUID) {
var velocity = props.velocity;
if (Vec3.length(velocity) < 0.05) { // see EntityMotionState.cpp DYNAMIC_LINEAR_VELOCITY_THRESHOLD
velocity = { x: 0.0, y: 0.2, z: 0.0 };
@ -289,7 +288,7 @@ ensureDynamic = function (entityID) {
};
findGroupParent = function (controllerData, targetProps) {
while (targetProps.parentID && targetProps.parentID != NULL_UUID) {
while (targetProps.parentID && targetProps.parentID !== NULL_UUID) {
// XXX use controllerData.nearbyEntityPropertiesByID ?
var parentProps = Entities.getEntityProperties(targetProps.parentID, DISPATCHER_PROPERTIES);
if (!parentProps) {

View file

@ -0,0 +1,85 @@
"use strict";
// nearTrigger.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, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND,
enableDispatcherModule, disableDispatcherModule, getGrabbableData, Vec3,
TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, makeRunningValues, NEAR_GRAB_RADIUS,
getEnabledModuleByName
*/
Script.include("/~/system/controllers/controllerDispatcherUtils.js");
(function() {
function DisableModules(hand) {
this.hand = hand;
this.disableModules = false;
this.parameters = makeDispatcherModuleParameters(
90,
this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip", "rightHandTrigger"] : ["leftHand", "leftHandEquip", "leftHandTrigger"],
100);
this.isReady = function(controllerData) {
if (this.disableModules) {
return makeRunningValues(true, [], []);
}
return false;
};
this.run = function(controllerData) {
var teleportModuleName = this.hand === RIGHT_HAND ? "RightTeleporter" : "LeftTeleporter";
var teleportModule = getEnabledModuleByName(teleportModuleName);
if (teleportModule) {
var ready = teleportModule.isReady(controllerData);
if (ready) {
return makeRunningValues(false, [], []);
}
}
if (!this.disablemodules) {
return makeRunningValues(false, [], []);
}
return makeRunningValues(true, [], []);
};
}
var leftDisableModules = new DisableModules(LEFT_HAND);
var rightDisableModules = new DisableModules(RIGHT_HAND);
enableDispatcherModule("LeftDisableModules", leftDisableModules);
enableDispatcherModule("RightDisableModules", rightDisableModules);
this.handleMessage = function(channel, message, sender) {
if (sender === MyAvatar.sessionUUID) {
if (channel === 'Hifi-Hand-Disabler') {
if (message === 'left') {
leftDisableModules.disableModules = true;
}
if (message === 'right') {
rightDisableModules.disableModules = true;
}
if (message === 'both' || message === 'none') {
if (message === 'both') {
leftDisableModules.disableModules = true;
rightDisableModules.disableModules = true;
} else if (message === 'none') {
leftDisableModules.disableModules = false;
rightDisableModules.disableModules = false;
}
}
}
}
};
Messages.subscribe('Hifi-Hand-Disabler');
this.cleanup = function() {
disableDispatcherModule("LeftDisableModules");
disableDispatcherModule("RightDisableModules");
};
Messages.messageReceived.connect(this.handleMessage);
Script.scriptEnding.connect(this.cleanup);
}());

View file

@ -187,6 +187,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
wearable = props.userDataParsed.wearable ? props.userDataParsed.wearable : {};
} catch (err) {
// don't want to spam the logs
}
return wearable;
}
@ -199,6 +200,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
equipHotspots = props.userDataParsed.equipHotspots ? props.userDataParsed.equipHotspots : [];
} catch (err) {
// don't want to spam the logs
}
return equipHotspots;
}
@ -255,7 +257,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
this.parameters = makeDispatcherModuleParameters(
300,
this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"],
this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip"] : ["leftHand", "rightHandEquip"],
[],
100);
@ -556,8 +558,8 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
var controllerLocation = getControllerWorldLocation(this.handToController(), true);
var worldHandPosition = controllerLocation.position;
var candidateEntityProps = controllerData.nearbyEntityProperties[this.hand];
var potentialEquipHotspot = null;
if (this.messageGrabEntity) {
var hotspots = this.collectEquipHotspots(this.grabEntityProps);
@ -567,7 +569,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
} else {
potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntityProps, controllerData);
}
if (!this.waitForTriggerRelease) {
this.updateEquipHaptics(potentialEquipHotspot, worldHandPosition);
}
@ -675,7 +677,6 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa
var handleMessage = function(channel, message, sender) {
var data;
print(channel);
if (sender === MyAvatar.sessionUUID) {
if (channel === 'Hifi-Hand-Grab') {
try {

View file

@ -527,6 +527,19 @@ Script.include("/~/system/libraries/controllers.js");
this.distanceRotate(otherFarGrabModule);
}
}
return this.exitIfDisabled();
};
this.exitIfDisabled = function() {
var moduleName = this.hand === RIGHT_HAND ? "RightDisableModules" : "LeftDisableModules";
var disableModule = getEnabledModuleByName(moduleName);
if (disableModule) {
if (disableModule.disableModules) {
this.laserPointerOff();
this.endNearGrabAction();
return makeRunningValues(false, [], []);
}
}
return makeRunningValues(true, [], []);
};

View file

@ -93,7 +93,7 @@ Script.include("/~/system/libraries/utils.js");
this.parameters = makeDispatcherModuleParameters(
160,
this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"],
this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip", "rightHandTrigger"] : ["leftHand", "leftHandEquip", "leftHandTrigger"],
100);
this.nearTablet = function(overlays) {

View file

@ -9,11 +9,12 @@
getControllerJointIndex, getGrabbableData, NULL_UUID, enableDispatcherModule, disableDispatcherModule,
propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, entityIsGrabbable,
Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation, makeDispatcherModuleParameters, makeRunningValues,
TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, findGroupParent
TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, findGroupParent, entityIsCloneable, propsAreCloneDynamic, cloneEntity
*/
Script.include("/~/system/controllers/controllerDispatcherUtils.js");
Script.include("/~/system/libraries/controllers.js");
Script.include("/~/system/libraries/cloneEntityUtils.js");
(function() {
@ -172,7 +173,7 @@ Script.include("/~/system/libraries/controllers.js");
var targetProps = this.getTargetProps(controllerData);
if (targetProps) {
if (!propsArePhysical(targetProps)) {
if (!propsArePhysical(targetProps) && !propsAreCloneDynamic) {
return makeRunningValues(false, [], []); // let nearParentGrabEntity handle it
} else {
this.targetEntityID = targetProps.id;
@ -185,13 +186,12 @@ Script.include("/~/system/libraries/controllers.js");
this.run = function (controllerData) {
if (this.actionID) {
if (controllerData.triggerClicks[this.hand] == 0) {
if (controllerData.triggerClicks[this.hand] === 0) {
this.endNearGrabAction();
return makeRunningValues(false, [], []);
}
this.refreshNearGrabAction(controllerData);
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
Entities.callEntityMethod(this.targetEntityID, "continueNearGrab", args);
} else {
@ -204,9 +204,18 @@ Script.include("/~/system/libraries/controllers.js");
var targetProps = this.getTargetProps(controllerData);
if (targetProps) {
if (controllerData.triggerClicks[this.hand] == 1) {
if (controllerData.triggerClicks[this.hand] === 1) {
// switch to grabbing
this.startNearGrabAction(controllerData, targetProps);
var targetCloneable = entityIsCloneable(targetProps);
if (targetCloneable) {
var worldEntityProps = controllerData.nearbyEntityProperties[this.hand];
var cloneID = cloneEntity(targetProps, worldEntityProps);
var cloneProps = Entities.getEntityProperties(cloneID);
this.targetEntityID = cloneID;
this.startNearGrabAction(controllerData, cloneProps);
} else {
this.startNearGrabAction(controllerData, targetProps);
}
}
}
}

View file

@ -10,7 +10,7 @@
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
findGroupParent, Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic
*/
Script.include("/~/system/controllers/controllerDispatcherUtils.js");
@ -40,6 +40,14 @@ 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.otherHandIsParent = function(props) {
return this.getOtherModule().thisHandIsParent(props);
};
this.thisHandIsParent = function(props) {
if (props.parentID !== MyAvatar.sessionUUID && props.parentID !== AVATAR_SELF_ID) {
return false;
@ -67,7 +75,6 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
};
this.startNearParentingGrabEntity = function (controllerData, targetProps) {
Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand);
var handJointIndex;
@ -92,10 +99,18 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
// 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;
} else if (this.otherHandIsParent(targetProps)) {
// the other hand is parent. Steal the object and information
var otherModule = this.getOtherModule();
this.previousParentID[targetProps.id] = otherModule.previousParentID[targetProps.id];
this.previousParentJointIndex[targetProps.id] = otherModule.previousParentJointIndex[targetProps.id];
otherModule.endNearParentingGrabEntity();
} else {
this.previousParentID[targetProps.id] = targetProps.parentID;
this.previousParentJointIndex[targetProps.id] = targetProps.parentJointIndex;
}
this.targetEntityID = targetProps.id;
Entities.editEntity(targetProps.id, reparentProps);
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
@ -103,10 +118,11 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
grabbedEntity: targetProps.id,
joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"
}));
this.grabbing = true;
};
this.endNearParentingGrabEntity = function () {
if (this.previousParentID[this.targetEntityID] === NULL_UUID) {
if (this.previousParentID[this.targetEntityID] === NULL_UUID || this.previousParentID === undefined) {
Entities.editEntity(this.targetEntityID, {
parentID: this.previousParentID[this.targetEntityID],
parentJointIndex: this.previousParentJointIndex[this.targetEntityID]
@ -123,7 +139,6 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
Entities.callEntityMethod(this.targetEntityID, "releaseGrab", args);
this.grabbing = false;
this.targetEntityID = null;
};
@ -160,7 +175,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
var targetProps = this.getTargetProps(controllerData);
if (targetProps) {
if (propsArePhysical(targetProps)) {
if (propsArePhysical(targetProps) || propsAreCloneDynamic(targetProps)) {
return makeRunningValues(false, [], []); // let nearActionGrabEntity handle it
} else {
this.targetEntityID = targetProps.id;
@ -178,6 +193,13 @@ Script.include("/~/system/libraries/cloneEntityUtils.js");
return makeRunningValues(false, [], []);
}
var props = Entities.getEntityProperties(this.targetEntityID);
if (!this.thisHandIsParent(props)) {
this.grabbing = false;
this.targetEntityID = null;
return makeRunningValues(false, [], []);
}
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
Entities.callEntityMethod(this.targetEntityID, "continueNearGrab", args);
} else {

View file

@ -38,31 +38,52 @@ var GRAB_RADIUS = 0.35;
this.handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand");
this.controllerJointIndex = getControllerJointIndex(this.hand);
this.getOtherModule = function() {
return (this.hand === RIGHT_HAND) ? leftNearParentingGrabOverlay : rightNearParentingGrabOverlay;
};
this.otherHandIsParent = function(props) {
return this.getOtherModule().thisHandIsParent(props);
};
this.thisHandIsParent = function(props) {
if (props.parentID !== MyAvatar.sessionUUID && props.parentID !== AVATAR_SELF_ID) {
return false;
}
var handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand");
if (props.parentJointIndex == handJointIndex) {
if (props.parentJointIndex === handJointIndex) {
return true;
}
var controllerJointIndex = this.controllerJointIndex;
if (props.parentJointIndex == 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) {
"_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" :
"_CAMERA_RELATIVE_CONTROLLER_LEFTHAND");
if (props.parentJointIndex === controllerCRJointIndex) {
return true;
}
return false;
};
this.getGrabbedProperties = function() {
return {
position: Overlays.getProperty(this.grabbedThingID, "position"),
rotation: Overlays.getProperty(this.grabbedThingID, "rotation"),
parentID: Overlays.getProperty(this.grabbedThingID, "parentID"),
parentJointIndex: Overlays.getProperty(this.grabbedThingID, "parentJointIndex"),
dynamic: false,
shapeType: "none"
};
};
this.startNearParentingGrabOverlay = function (controllerData) {
Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand);
@ -74,15 +95,8 @@ var GRAB_RADIUS = 0.35;
// }
handJointIndex = this.controllerJointIndex;
var grabbedProperties = {
position: Overlays.getProperty(this.grabbedThingID, "position"),
rotation: Overlays.getProperty(this.grabbedThingID, "rotation"),
parentID: Overlays.getProperty(this.grabbedThingID, "parentID"),
parentJointIndex: Overlays.getProperty(this.grabbedThingID, "parentJointIndex"),
dynamic: false,
shapeType: "none"
};
var grabbedProperties = this.getGrabbedProperties();
var reparentProps = {
parentID: AVATAR_SELF_ID,
parentJointIndex: handJointIndex,
@ -94,6 +108,12 @@ var GRAB_RADIUS = 0.35;
// 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.garbbedThingID];
this.previousParentJointIndex[this.grabbedThingID] = otherModule.previousParentJointIndex[this.grabbedThingID];
} else {
this.previousParentID[this.grabbedThingID] = grabbedProperties.parentID;
this.previousParentJointIndex[this.grabbedThingID] = grabbedProperties.parentJointIndex;
@ -117,7 +137,7 @@ var GRAB_RADIUS = 0.35;
// before we grabbed it, overlay was a child of something; put it back.
Overlays.editOverlay(this.grabbedThingID, {
parentID: this.previousParentID[this.grabbedThingID],
parentJointIndex: this.previousParentJointIndex[this.grabbedThingID],
parentJointIndex: this.previousParentJointIndex[this.grabbedThingID]
});
}
@ -135,10 +155,10 @@ var GRAB_RADIUS = 0.35;
}
return null;
};
this.isReady = function (controllerData) {
if (controllerData.triggerClicks[this.hand] == 0) {
if (controllerData.triggerClicks[this.hand] === 0) {
return makeRunningValues(false, [], []);
}
@ -160,10 +180,16 @@ var GRAB_RADIUS = 0.35;
};
this.run = function (controllerData) {
if (controllerData.triggerClicks[this.hand] == 0) {
if (controllerData.triggerClicks[this.hand] === 0) {
this.endNearParentingGrabOverlay();
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], []);
}
};

View file

@ -30,7 +30,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js");
this.parameters = makeDispatcherModuleParameters(
520,
this.hand === RIGHT_HAND ? ["rightHandTrigger"] : ["leftHandTrigger"],
this.hand === RIGHT_HAND ? ["rightHandTrigger", "rightHand"] : ["leftHandTrigger", "leftHand"],
[],
100);

View file

@ -26,6 +26,7 @@ var CONTOLLER_SCRIPTS = [
"controllerModules/overlayLaserInput.js",
"controllerModules/webEntityLaserInput.js",
"controllerModules/inEditMode.js",
"controllerModules/disableOtherModule.js",
"teleport.js"
];

View file

@ -14,6 +14,7 @@
Messages, makeDispatcherModuleParameters, makeRunningValues, Settings, entityHasActions,
Vec3, Overlays, flatten, Xform, getControllerWorldLocation, ensureDynamic
*/
/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */
Script.include("/~/system/libraries/Xform.js");
Script.include("/~/system/controllers/controllerDispatcherUtils.js");
@ -225,7 +226,7 @@ function Teleporter(hand) {
}
this.parameters = makeDispatcherModuleParameters(
300,
80,
this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"],
[],
100);
@ -338,12 +339,12 @@ function Teleporter(hand) {
}
};
}
// related to repositioning the avatar after you teleport
var FOOT_JOINT_NAMES = ["RightToe_End", "RightToeBase", "RightFoot"];
var DEFAULT_ROOT_TO_FOOT_OFFSET = 0.5;
function getAvatarFootOffset() {
// find a valid foot jointIndex
var footJointIndex = -1;
var i, l = FOOT_JOINT_NAMES.length;

View file

@ -5,11 +5,13 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/* global entityIsCloneable:true, getGrabbableData:true, cloneEntity:true propsAreCloneDynamic:true */
Script.include("/~/system/controllers/controllerDispatcherUtils.js");
// Object assign polyfill
if (typeof Object.assign != 'function') {
if (typeof Object.assign !== 'function') {
Object.assign = function(target, varArgs) {
if (target === null) {
throw new TypeError('Cannot convert undefined or null to object');
@ -36,7 +38,18 @@ entityIsCloneable = function(props) {
}
return false;
}
};
propsAreCloneDynamic = function(props) {
var cloneable = entityIsCloneable(props);
if (cloneable) {
var grabInfo = getGrabbableData(props);
if (grabInfo.cloneDynamic) {
return true;
}
}
return false;
};
cloneEntity = function(props, worldEntityProps) {
@ -49,26 +62,26 @@ cloneEntity = function(props, worldEntityProps) {
count++;
}
});
var grabInfo = getGrabbableData(cloneableProps);
var limit = grabInfo.cloneLimit ? grabInfo.cloneLimit : 0;
if (count >= limit && limit !== 0) {
return null;
}
cloneableProps.name = cloneableProps.name + '-clone-' + cloneableProps.id;
var lifetime = grabInfo.cloneLifetime ? grabInfo.cloneLifetime : 300;
var dynamic = grabInfo.cloneDynamic ? grabInfo.cloneDynamic : false;
var cUserData = Object.assign({}, JSON.parse(cloneableProps.userData));
var cProperties = Object.assign({}, cloneableProps);
delete cUserData.grabbableKey.cloneLifetime;
delete cUserData.grabbableKey.cloneable;
delete cUserData.grabbableKey.cloneDynamic;
delete cUserData.grabbableKey.cloneLimit;
delete cProperties.id;
cProperties.dynamic = dynamic;
cProperties.locked = false;
@ -76,7 +89,7 @@ cloneEntity = function(props, worldEntityProps) {
cUserData.grabbableKey.grabbable = true;
cProperties.lifetime = lifetime;
cProperties.userData = JSON.stringify(cUserData);
var cloneID = Entities.addEntity(cProperties);
return cloneID;
}
};