mirror of
https://github.com/lubosz/overte.git
synced 2025-04-23 11:33:44 +02:00
Use trigger to both grab and equip objects
* Removed entry into HOLD state via secondary aka grip buttons. * Changed equip logic to prioritize equip over near over far grabs. * Added drop gesture, upside down controller + slight shake + trigger press * Bug fix for near grab snapping to an incorrect offset after a far grab (this bug is present in master)
This commit is contained in:
parent
4aa5e87a26
commit
1759132377
1 changed files with 466 additions and 277 deletions
|
@ -14,12 +14,11 @@
|
|||
|
||||
Script.include("/~/system/libraries/utils.js");
|
||||
|
||||
|
||||
//
|
||||
// add lines where the hand ray picking is happening
|
||||
//
|
||||
var WANT_DEBUG = false;
|
||||
var WANT_DEBUG_STATE = false;
|
||||
var WANT_DEBUG_STATE = true;
|
||||
var WANT_DEBUG_SEARCH_NAME = null;
|
||||
|
||||
//
|
||||
|
@ -126,8 +125,6 @@ var DEFAULT_GRABBABLE_DATA = {
|
|||
disableReleaseVelocity: false
|
||||
};
|
||||
|
||||
|
||||
|
||||
// sometimes we want to exclude objects from being picked
|
||||
var USE_BLACKLIST = true;
|
||||
var blacklist = [];
|
||||
|
@ -140,19 +137,20 @@ var USE_ENTITY_LINES_FOR_MOVING = false;
|
|||
var USE_OVERLAY_LINES_FOR_MOVING = false;
|
||||
var USE_PARTICLE_BEAM_FOR_MOVING = true;
|
||||
|
||||
|
||||
var USE_SPOTLIGHT = false;
|
||||
var USE_POINTLIGHT = false;
|
||||
|
||||
var FORBIDDEN_GRAB_NAMES = ["Grab Debug Entity", "grab pointer"];
|
||||
var FORBIDDEN_GRAB_TYPES = ['Unknown', 'Light', 'PolyLine', 'Zone'];
|
||||
|
||||
// states for the state machine
|
||||
var STATE_OFF = 0;
|
||||
var STATE_SEARCHING = 1;
|
||||
var STATE_HOLD_SEARCHING = 2;
|
||||
var STATE_DISTANCE_HOLDING = 3;
|
||||
var STATE_NEAR_GRABBING = 4;
|
||||
var STATE_NEAR_TRIGGER = 5;
|
||||
var STATE_FAR_TRIGGER = 6;
|
||||
var STATE_HOLD = 7;
|
||||
var STATE_DISTANCE_HOLDING = 2;
|
||||
var STATE_NEAR_GRABBING = 3;
|
||||
var STATE_NEAR_TRIGGER = 4;
|
||||
var STATE_FAR_TRIGGER = 5;
|
||||
var STATE_HOLD = 6;
|
||||
|
||||
// "collidesWith" is specified by comma-separated list of group names
|
||||
// the possible group names are: static, dynamic, kinematic, myAvatar, otherAvatar
|
||||
|
@ -175,10 +173,6 @@ CONTROLLER_STATE_MACHINE[STATE_SEARCHING] = {
|
|||
enterMethod: "searchEnter",
|
||||
exitMethod: "searchExit"
|
||||
};
|
||||
CONTROLLER_STATE_MACHINE[STATE_HOLD_SEARCHING] = {
|
||||
name: "hold_searching",
|
||||
updateMethod: "search"
|
||||
};
|
||||
CONTROLLER_STATE_MACHINE[STATE_DISTANCE_HOLDING] = {
|
||||
name: "distance_holding",
|
||||
enterMethod: "distanceHoldingEnter",
|
||||
|
@ -259,6 +253,58 @@ function restore2DMode() {
|
|||
}
|
||||
}
|
||||
|
||||
function filter(array, predicate) {
|
||||
var i, l = array.length;
|
||||
var result = [];
|
||||
for (i = 0; i < l; i++) {
|
||||
if (predicate(array[i])) {
|
||||
result.push(array[i]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// constructor
|
||||
function EntityPropertiesCache() {
|
||||
this.cache = {};
|
||||
}
|
||||
EntityPropertiesCache.prototype.clear = function() {
|
||||
this.cache = {};
|
||||
};
|
||||
EntityPropertiesCache.prototype.findEntities = function(position, radius) {
|
||||
var entities = Entities.findEntities(position, radius);
|
||||
var i, l = entities.length;
|
||||
for (i = 0; i < l; i++) {
|
||||
this.addEntity(entities[i]);
|
||||
}
|
||||
};
|
||||
EntityPropertiesCache.prototype.addEntity = function(entityID) {
|
||||
var props = Entities.getEntityProperties(entityID, GRABBABLE_PROPERTIES);
|
||||
var grabbableProps = getEntityCustomData(GRABBABLE_DATA_KEY, entityID, DEFAULT_GRABBABLE_DATA);
|
||||
var grabProps = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {});
|
||||
var wearableProps = getEntityCustomData("wearable", entityID, {});
|
||||
this.cache[entityID] = { props: props, grabbableProps: grabbableProps, grabProps: grabProps, wearableProps: wearableProps };
|
||||
};
|
||||
EntityPropertiesCache.prototype.getEntities = function() {
|
||||
return Object.keys(this.cache);
|
||||
}
|
||||
EntityPropertiesCache.prototype.getProps = function(entityID) {
|
||||
var obj = this.cache[entityID]
|
||||
return obj ? obj.props : undefined;
|
||||
};
|
||||
EntityPropertiesCache.prototype.getGrabbableProps = function(entityID) {
|
||||
var obj = this.cache[entityID]
|
||||
return obj ? obj.grabbableProps : undefined;
|
||||
};
|
||||
EntityPropertiesCache.prototype.getGrabProps = function(entityID) {
|
||||
var obj = this.cache[entityID]
|
||||
return obj ? obj.grabProps : undefined;
|
||||
};
|
||||
EntityPropertiesCache.prototype.getWearableProps = function(entityID) {
|
||||
var obj = this.cache[entityID]
|
||||
return obj ? obj.wearableProps : undefined;
|
||||
};
|
||||
|
||||
function MyController(hand) {
|
||||
this.hand = hand;
|
||||
if (this.hand === RIGHT_HAND) {
|
||||
|
@ -295,6 +341,8 @@ function MyController(hand) {
|
|||
this.overlayLine = null;
|
||||
this.searchSphere = null;
|
||||
|
||||
this.waitForTriggerRelease = false;
|
||||
|
||||
// how far from camera to search intersection?
|
||||
var DEFAULT_SEARCH_SPHERE_DISTANCE = 1000;
|
||||
this.intersectionDistance = 0.0;
|
||||
|
@ -307,16 +355,18 @@ function MyController(hand) {
|
|||
this.lastPickTime = 0;
|
||||
this.lastUnequipCheckTime = 0;
|
||||
|
||||
this.entityPropertyCache = new EntityPropertiesCache();
|
||||
|
||||
var _this = this;
|
||||
|
||||
var suppressedIn2D = [STATE_OFF, STATE_SEARCHING, STATE_HOLD_SEARCHING];
|
||||
var suppressedIn2D = [STATE_OFF, STATE_SEARCHING];
|
||||
this.ignoreInput = function () {
|
||||
// We've made the decision to use 'this' for new code, even though it is fragile,
|
||||
// in order to keep/ the code uniform without making any no-op line changes.
|
||||
return (-1 !== suppressedIn2D.indexOf(this.state)) && isIn2DMode();
|
||||
};
|
||||
|
||||
this.update = function() {
|
||||
this.update = function(deltaTime) {
|
||||
|
||||
this.updateSmoothedTrigger();
|
||||
|
||||
|
@ -329,7 +379,7 @@ function MyController(hand) {
|
|||
var updateMethodName = CONTROLLER_STATE_MACHINE[this.state].updateMethod;
|
||||
var updateMethod = this[updateMethodName];
|
||||
if (updateMethod) {
|
||||
updateMethod.call(this);
|
||||
updateMethod.call(this, deltaTime);
|
||||
} else {
|
||||
print("WARNING: could not find updateMethod for state " + stateToName(this.state));
|
||||
}
|
||||
|
@ -343,12 +393,12 @@ function MyController(hand) {
|
|||
Entities.callEntityMethod(this.grabbedEntity, entityMethodName, args);
|
||||
}
|
||||
|
||||
this.setState = function(newState) {
|
||||
this.setState = function(newState, reason) {
|
||||
this.grabSphereOff();
|
||||
if (WANT_DEBUG || WANT_DEBUG_STATE) {
|
||||
var oldStateName = stateToName(this.state);
|
||||
var newStateName = stateToName(newState);
|
||||
print("STATE (" + this.hand + "): " + newStateName + " <-- " + oldStateName);
|
||||
print("STATE (" + this.hand + "): " + newStateName + " <-- " + oldStateName + ", reason = " + reason);
|
||||
}
|
||||
|
||||
// exit the old state
|
||||
|
@ -819,14 +869,16 @@ function MyController(hand) {
|
|||
};
|
||||
|
||||
this.off = function() {
|
||||
if (this.triggerSmoothedSqueezed() || this.secondarySqueezed()) {
|
||||
|
||||
if (this.triggerSmoothedReleased()) {
|
||||
this.waitForTriggerRelease = false;
|
||||
}
|
||||
if (!this.waitForTriggerRelease && this.triggerSmoothedSqueezed()) {
|
||||
this.lastPickTime = 0;
|
||||
var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
|
||||
this.startingHandRotation = Controller.getPoseValue(controllerHandInput).rotation;
|
||||
if (this.triggerSmoothedSqueezed()) {
|
||||
this.setState(STATE_SEARCHING);
|
||||
} else if (this.secondarySqueezed()) {
|
||||
this.setState(STATE_HOLD_SEARCHING);
|
||||
this.setState(STATE_SEARCHING, "trigger squeeze detected");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -875,265 +927,345 @@ function MyController(hand) {
|
|||
this.equipHotspotOverlays = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
this.calcRayPickInfo = function(hand) {
|
||||
|
||||
var pose = Controller.getPoseValue((hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand);
|
||||
var worldHandPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position);
|
||||
var worldHandRotation = Quat.multiply(MyAvatar.orientation, pose.rotation);
|
||||
|
||||
var pickRay = {
|
||||
origin: PICK_WITH_HAND_RAY ? worldHandPosition : Camera.position,
|
||||
direction: PICK_WITH_HAND_RAY ? Quat.getUp(worldHandRotation) : Vec3.mix(Quat.getUp(worldHandRotation),
|
||||
Quat.getFront(Camera.orientation),
|
||||
HAND_HEAD_MIX_RATIO),
|
||||
length: PICK_MAX_DISTANCE
|
||||
};
|
||||
|
||||
var result = { entityID: null,
|
||||
searchRay: pickRay,
|
||||
distance: PICK_MAX_DISTANCE };
|
||||
|
||||
var now = Date.now();
|
||||
if (now - this.lastPickTime < MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) {
|
||||
return result;
|
||||
}
|
||||
this.lastPickTime = now;
|
||||
|
||||
var directionNormalized = Vec3.normalize(pickRay.direction);
|
||||
var directionBacked = Vec3.multiply(directionNormalized, PICK_BACKOFF_DISTANCE);
|
||||
var pickRayBacked = {
|
||||
origin: Vec3.subtract(pickRay.origin, directionBacked),
|
||||
direction: pickRay.direction
|
||||
};
|
||||
|
||||
var intersection;
|
||||
if (USE_BLACKLIST === true && blacklist.length !== 0) {
|
||||
intersection = Entities.findRayIntersection(pickRayBacked, true, [], blacklist);
|
||||
} else {
|
||||
intersection = Entities.findRayIntersection(pickRayBacked, true);
|
||||
}
|
||||
|
||||
var overlayIntersection = Overlays.findRayIntersection(pickRayBacked);
|
||||
if (!intersection.intersects || (overlayIntersection.intersects && (intersection.distance > overlayIntersection.distance))) {
|
||||
intersection = overlayIntersection;
|
||||
}
|
||||
|
||||
if (intersection.intersects) {
|
||||
return { entityID: intersection.entityID,
|
||||
searchRay: pickRay,
|
||||
distance: Vec3.distance(pickRay.origin, intersection.intersection) }
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
this.entityWantsTrigger = function (entityID) {
|
||||
var grabbableProps = this.entityPropertyCache.getGrabbableProps(entityID);
|
||||
return grabbableProps && grabbableProps.wantsTrigger;
|
||||
};
|
||||
|
||||
this.entityIsEquippable = function (entityID, handPosition) {
|
||||
var props = this.entityPropertyCache.getProps(entityID);
|
||||
var distance = Vec3.distance(props.position, handPosition);
|
||||
var grabProps = this.entityPropertyCache.getGrabProps(entityID);
|
||||
var debug = true;//(WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME);
|
||||
|
||||
var refCount = ("refCount" in grabProps) ? grabProps.refCount : 0;
|
||||
if (refCount > 0) {
|
||||
if (debug) {
|
||||
print("equip is skipping '" + props.name + "': it is already grabbed");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (distance > NEAR_PICK_MAX_DISTANCE) {
|
||||
if (debug) {
|
||||
print("equip is skipping '" + props.name + "': too far away.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var wearableProps = this.entityPropertyCache.getWearableProps(entityID);
|
||||
if (!wearableProps || !wearableProps.joints) {
|
||||
if (debug) {
|
||||
print("equip is skipping '" + props.name + "': no wearable attach-point");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var handJointName = this.hand === RIGHT_HAND ? "RightHand" : "LeftHand";
|
||||
if (!wearableProps.joints[handJointName]) {
|
||||
if (debug) {
|
||||
print("equip is skipping '" + props.name + "': no wearable joint for " + handJointName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
this.entityIsGrabbable = function (entityID) {
|
||||
var grabbableProps = this.entityPropertyCache.getGrabbableProps(entityID);
|
||||
var grabProps = this.entityPropertyCache.getGrabProps(entityID);
|
||||
var props = this.entityPropertyCache.getProps(entityID);
|
||||
var physical = propsArePhysical(props);
|
||||
var grabbable = false;
|
||||
var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME);
|
||||
|
||||
if (physical) {
|
||||
// physical things default to grabbable
|
||||
grabbable = true;
|
||||
} else {
|
||||
// non-physical things default to non-grabbable unless they are already grabbed
|
||||
if ("refCount" in grabProps && grabProps.refCount > 0) {
|
||||
grabbable = true;
|
||||
} else {
|
||||
grabbable = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (grabbableProps.hasOwnProperty("grabbable")) {
|
||||
grabbable = grabbableProps.grabbable;
|
||||
}
|
||||
|
||||
if (!grabbable && !grabbableProps.wantsTrigger) {
|
||||
if (debug) {
|
||||
print("grab is skipping '" + props.name + "': not grabbable.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (FORBIDDEN_GRAB_TYPES.indexOf(props.type) >= 0) {
|
||||
if (debug) {
|
||||
print("grab is skipping '" + props.name + "': forbidden entity type.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (props.locked && !grabbableProps.wantsTrigger) {
|
||||
if (debug) {
|
||||
print("grab is skipping '" + props.name + "': locked and not triggerable.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (FORBIDDEN_GRAB_NAMES.indexOf(props.name) >= 0) {
|
||||
if (debug) {
|
||||
print("grab is skipping '" + props.name + "': forbidden name.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
this.entityIsDistanceGrabbable = function(entityID, handPosition) {
|
||||
if (!this.entityIsGrabbable(entityID)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var props = this.entityPropertyCache.getProps(entityID);
|
||||
var distance = Vec3.distance(props.position, handPosition);
|
||||
var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME);
|
||||
|
||||
// we can't distance-grab non-physical
|
||||
var isPhysical = propsArePhysical(props);
|
||||
if (!isPhysical) {
|
||||
if (debug) {
|
||||
print("distance grab is skipping '" + props.name + "': not physical");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (distance > PICK_MAX_DISTANCE) {
|
||||
// too far away, don't grab
|
||||
if (debug) {
|
||||
print("distance grab is skipping '" + props.name + "': too far away.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entityIsGrabbedByOther(entityID)) {
|
||||
// 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;
|
||||
};
|
||||
|
||||
this.entityIsNearGrabbable = function(entityID, handPosition) {
|
||||
|
||||
if (!this.entityIsGrabbable(entityID)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var props = this.entityPropertyCache.getProps(entityID);
|
||||
var distance = Vec3.distance(props.position, handPosition);
|
||||
var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME);
|
||||
|
||||
if (distance > NEAR_PICK_MAX_DISTANCE) {
|
||||
// too far away, don't grab
|
||||
if (debug) {
|
||||
print(" grab is skipping '" + props.name + "': too far away.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
this.search = function() {
|
||||
var _this = this;
|
||||
var name;
|
||||
|
||||
this.grabbedEntity = null;
|
||||
this.isInitialGrab = false;
|
||||
this.shouldResetParentOnRelease = false;
|
||||
|
||||
this.checkForStrayChildren();
|
||||
|
||||
if (this.state == STATE_SEARCHING && this.triggerSmoothedReleased()) {
|
||||
this.setState(STATE_OFF);
|
||||
if (this.triggerSmoothedReleased()) {
|
||||
this.setState(STATE_OFF, "trigger released");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.state == STATE_HOLD_SEARCHING && this.secondaryReleased()) {
|
||||
this.setState(STATE_OFF);
|
||||
return;
|
||||
}
|
||||
|
||||
// the trigger is being pressed, so do a ray test to see what we are hitting
|
||||
var handPosition = this.getHandPosition();
|
||||
|
||||
if (SHOW_GRAB_SPHERE) {
|
||||
this.grabSphereOn();
|
||||
}
|
||||
|
||||
var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
|
||||
var currentControllerPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation,
|
||||
Controller.getPoseValue(controllerHandInput).translation),
|
||||
MyAvatar.position);
|
||||
var avatarControllerPose = Controller.getPoseValue((this.hand === RIGHT_HAND) ?
|
||||
Controller.Standard.RightHand : Controller.Standard.LeftHand);
|
||||
var controllerRotation = Quat.multiply(MyAvatar.orientation, avatarControllerPose.rotation);
|
||||
this.entityPropertyCache.clear();
|
||||
this.entityPropertyCache.findEntities(handPosition, GRAB_RADIUS);
|
||||
var candidateEntities = this.entityPropertyCache.getEntities();
|
||||
|
||||
var distantPickRay = {
|
||||
origin: PICK_WITH_HAND_RAY ? currentControllerPosition : Camera.position,
|
||||
direction: PICK_WITH_HAND_RAY ? Quat.getUp(controllerRotation) : Vec3.mix(Quat.getUp(controllerRotation),
|
||||
Quat.getFront(Camera.orientation),
|
||||
HAND_HEAD_MIX_RATIO),
|
||||
length: PICK_MAX_DISTANCE
|
||||
};
|
||||
var equippableEntities = filter(candidateEntities, function (entity) {
|
||||
return _this.entityIsEquippable(entity, handPosition);
|
||||
});
|
||||
|
||||
// Pick at some maximum rate, not always
|
||||
var pickRays = [];
|
||||
var now = Date.now();
|
||||
if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) {
|
||||
pickRays = [distantPickRay];
|
||||
this.lastPickTime = now;
|
||||
}
|
||||
|
||||
var rayPickedCandidateEntities = []; // the list of candidates to consider grabbing
|
||||
|
||||
this.intersectionDistance = 0.0;
|
||||
for (var index = 0; index < pickRays.length; ++index) {
|
||||
var pickRay = pickRays[index];
|
||||
var directionNormalized = Vec3.normalize(pickRay.direction);
|
||||
var directionBacked = Vec3.multiply(directionNormalized, PICK_BACKOFF_DISTANCE);
|
||||
var pickRayBacked = {
|
||||
origin: Vec3.subtract(pickRay.origin, directionBacked),
|
||||
direction: pickRay.direction
|
||||
};
|
||||
|
||||
var intersection;
|
||||
|
||||
if (USE_BLACKLIST === true && blacklist.length !== 0) {
|
||||
intersection = Entities.findRayIntersection(pickRayBacked, true, [], blacklist);
|
||||
} else {
|
||||
intersection = Entities.findRayIntersection(pickRayBacked, true);
|
||||
}
|
||||
var overlayIntersection = Overlays.findRayIntersection(pickRayBacked);
|
||||
if (!intersection.intersects || (overlayIntersection.intersects && (intersection.distance > overlayIntersection.distance))) {
|
||||
intersection = overlayIntersection;
|
||||
}
|
||||
// If we want to share results with other scripts, this is where we would do it.
|
||||
|
||||
if (intersection.intersects) {
|
||||
if (intersection.entityID) {
|
||||
rayPickedCandidateEntities.push(intersection.entityID);
|
||||
}
|
||||
this.intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection);
|
||||
}
|
||||
}
|
||||
|
||||
var nearPickedCandidateEntities = Entities.findEntities(handPosition, GRAB_RADIUS);
|
||||
var candidateEntities = rayPickedCandidateEntities.concat(nearPickedCandidateEntities);
|
||||
|
||||
var forbiddenNames = ["Grab Debug Entity", "grab pointer"];
|
||||
var forbiddenTypes = ['Unknown', 'Light', 'PolyLine', 'Zone'];
|
||||
|
||||
var minDistance = PICK_MAX_DISTANCE;
|
||||
var i, props, distance, grabbableData;
|
||||
this.grabbedEntity = null;
|
||||
for (i = 0; i < candidateEntities.length; i++) {
|
||||
var grabbableDataForCandidate =
|
||||
getEntityCustomData(GRABBABLE_DATA_KEY, candidateEntities[i], DEFAULT_GRABBABLE_DATA);
|
||||
var grabDataForCandidate = getEntityCustomData(GRAB_USER_DATA_KEY, candidateEntities[i], {});
|
||||
var propsForCandidate = Entities.getEntityProperties(candidateEntities[i], GRABBABLE_PROPERTIES);
|
||||
var near = (nearPickedCandidateEntities.indexOf(candidateEntities[i]) >= 0);
|
||||
|
||||
var physical = propsArePhysical(propsForCandidate);
|
||||
var grabbable;
|
||||
if (physical) {
|
||||
// physical things default to grabbable
|
||||
grabbable = true;
|
||||
} else {
|
||||
// non-physical things default to non-grabbable unless they are already grabbed
|
||||
if ("refCount" in grabDataForCandidate && grabDataForCandidate.refCount > 0) {
|
||||
grabbable = true;
|
||||
} else {
|
||||
grabbable = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ("grabbable" in grabbableDataForCandidate) {
|
||||
// if userData indicates that this is grabbable or not, override the default.
|
||||
grabbable = grabbableDataForCandidate.grabbable;
|
||||
}
|
||||
|
||||
if (!grabbable && !grabbableDataForCandidate.wantsTrigger) {
|
||||
if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) {
|
||||
print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': not grabbable.");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (forbiddenTypes.indexOf(propsForCandidate.type) >= 0) {
|
||||
if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) {
|
||||
print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': forbidden entity type.");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (propsForCandidate.locked && !grabbableDataForCandidate.wantsTrigger) {
|
||||
if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) {
|
||||
print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': locked and not triggerable.");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (forbiddenNames.indexOf(propsForCandidate.name) >= 0) {
|
||||
if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) {
|
||||
print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': forbidden name.");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
distance = Vec3.distance(propsForCandidate.position, handPosition);
|
||||
if (distance > PICK_MAX_DISTANCE) {
|
||||
// too far away, don't grab
|
||||
if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) {
|
||||
print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': too far away.");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (propsForCandidate.parentID != NULL_UUID && this.state == STATE_HOLD_SEARCHING) {
|
||||
// don't allow a double-equip
|
||||
if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) {
|
||||
print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': it's a child");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.state == STATE_SEARCHING &&
|
||||
!physical && distance > NEAR_PICK_MAX_DISTANCE && !near && !grabbableDataForCandidate.wantsTrigger) {
|
||||
// we can't distance-grab non-physical
|
||||
if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) {
|
||||
print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': not physical and too far for near-grab");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (distance < minDistance) {
|
||||
this.grabbedEntity = candidateEntities[i];
|
||||
minDistance = distance;
|
||||
props = propsForCandidate;
|
||||
grabbableData = grabbableDataForCandidate;
|
||||
}
|
||||
}
|
||||
if ((this.grabbedEntity !== null) && (this.triggerSmoothedGrab() || this.secondarySqueezed())) {
|
||||
// We are squeezing enough to grab, and we've found an entity that we'll try to do something with.
|
||||
var isNear = (nearPickedCandidateEntities.indexOf(this.grabbedEntity) >= 0) || minDistance <= NEAR_PICK_MAX_DISTANCE;
|
||||
var isPhysical = propsArePhysical(props);
|
||||
|
||||
// near or far trigger
|
||||
if (grabbableData.wantsTrigger) {
|
||||
this.setState(isNear ? STATE_NEAR_TRIGGER : STATE_FAR_TRIGGER);
|
||||
var entity;
|
||||
if (equippableEntities.length > 0) {
|
||||
// sort by distance
|
||||
equippableEntities.sort(function (a, b) {
|
||||
var aDistance = Vec3.distance(_this.entityPropertyCache.getProps(a).position, handPosition);
|
||||
var bDistance = Vec3.distance(_this.entityPropertyCache.getProps(b).position, handPosition);
|
||||
return aDistance - bDistance;
|
||||
});
|
||||
entity = equippableEntities[0];
|
||||
if (this.triggerSmoothedGrab()) {
|
||||
this.grabbedEntity = entity;
|
||||
this.setState(STATE_HOLD, "eqipping '" + this.entityPropertyCache.getProps(entity).name + "'");
|
||||
return;
|
||||
} else {
|
||||
// TODO: highlight the equippable object?
|
||||
}
|
||||
// near grab with action or equip
|
||||
var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, {});
|
||||
var refCount = ("refCount" in grabData) ? grabData.refCount : 0;
|
||||
if (isNear && (refCount < 1 || entityHasActions(this.grabbedEntity))) {
|
||||
if (this.state == STATE_SEARCHING) {
|
||||
this.setState(STATE_NEAR_GRABBING);
|
||||
} else { // (this.state == STATE_HOLD_SEARCHING)
|
||||
// if there was already an action, we'll need to set the parent back to null once we release
|
||||
this.shouldResetParentOnRelease = true;
|
||||
this.previousParentID = props.parentID;
|
||||
this.previousParentJointIndex = props.parentJointIndex;
|
||||
this.setState(STATE_HOLD);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// far grab
|
||||
if (isPhysical && !isNear) {
|
||||
if (entityIsGrabbedByOther(this.grabbedEntity)) {
|
||||
// don't distance grab something that is already grabbed.
|
||||
if (WANT_DEBUG_SEARCH_NAME && props.name == WANT_DEBUG_SEARCH_NAME) {
|
||||
print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': already grabbed by another.");
|
||||
}
|
||||
}
|
||||
|
||||
var rayPickInfo = this.calcRayPickInfo(this.hand);
|
||||
this.intersectionDistance = rayPickInfo.distance;
|
||||
if (rayPickInfo.entityID) {
|
||||
candidateEntities.push(rayPickInfo.entityID);
|
||||
this.entityPropertyCache.addEntity(rayPickInfo.entityID);
|
||||
}
|
||||
|
||||
var grabbableEntities = filter(candidateEntities, function (entity) {
|
||||
return _this.entityIsNearGrabbable(entity, handPosition);
|
||||
});
|
||||
|
||||
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);
|
||||
return aDistance - bDistance;
|
||||
});
|
||||
entity = grabbableEntities[0];
|
||||
name = this.entityPropertyCache.getProps(entity).name;
|
||||
this.grabbedEntity = entity;
|
||||
if (this.entityWantsTrigger(entity)) {
|
||||
if (this.triggerSmoothedGrab()) {
|
||||
this.setState(STATE_NEAR_TRIGGER, "near trigger '" + name + "'");
|
||||
return;
|
||||
} else {
|
||||
// TODO: highlight the near-triggerable object?
|
||||
}
|
||||
this.temporaryPositionOffset = null;
|
||||
if (!this.hasPresetOffsets()) {
|
||||
// We want to give a temporary position offset to this object so it is pulled close to hand
|
||||
var intersectionPointToCenterDistance = Vec3.length(Vec3.subtract(intersection.intersection,
|
||||
intersection.properties.position));
|
||||
var handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand");
|
||||
var handJointPosition = MyAvatar.getJointPosition(handJointIndex);
|
||||
this.temporaryPositionOffset =
|
||||
Vec3.normalize(Vec3.subtract(intersection.properties.position, handJointPosition));
|
||||
this.temporaryPositionOffset = Vec3.multiply(this.temporaryPositionOffset,
|
||||
intersectionPointToCenterDistance *
|
||||
FAR_TO_NEAR_GRAB_PADDING_FACTOR);
|
||||
}
|
||||
this.setState(STATE_DISTANCE_HOLDING);
|
||||
|
||||
this.searchSphereOff();
|
||||
return;
|
||||
}
|
||||
|
||||
// else this thing isn't physical. grab it by reparenting it (but not if we've already
|
||||
// grabbed it).
|
||||
if (refCount < 1) {
|
||||
if (this.state == STATE_SEARCHING) {
|
||||
this.setState(STATE_NEAR_GRABBING);
|
||||
} else { // this.state == STATE_HOLD_SEARCHING)
|
||||
this.setState(STATE_HOLD);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
// it's not physical and it's already held via parenting. go ahead and grab it, but
|
||||
// save off the current parent and joint. this wont always be right if there are more than
|
||||
// two grabs and the order of release isn't opposite of the order of grabs.
|
||||
this.shouldResetParentOnRelease = true;
|
||||
this.previousParentID = props.parentID;
|
||||
this.previousParentJointIndex = props.parentJointIndex;
|
||||
if (this.state == STATE_SEARCHING) {
|
||||
this.setState(STATE_NEAR_GRABBING);
|
||||
} else { // (this.state == STATE_HOLD_SEARCHING)
|
||||
this.setState(STATE_HOLD);
|
||||
if (this.triggerSmoothedGrab()) {
|
||||
|
||||
var props = this.entityPropertyCache.getProps(entity);
|
||||
var grabProps = this.entityPropertyCache.getGrabProps(entity);
|
||||
var refCount = grabProps.refCount ? grabProps.refCount : 0;
|
||||
if (refCount >= 1) {
|
||||
// if another person is holding the object, remember to restore the
|
||||
// parent info, when we are finished grabbing it.
|
||||
this.shouldResetParentOnRelease = true;
|
||||
this.previousParentID = props.parentID;
|
||||
this.previousParentJointIndex = props.parentJointIndex;
|
||||
}
|
||||
|
||||
this.setState(STATE_NEAR_GRABBING, "near grab '" + name + "'");
|
||||
return;
|
||||
} else {
|
||||
// TODO: highlight the grabbable object?
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (rayPickInfo.entityID) {
|
||||
entity = rayPickInfo.entityID;
|
||||
name = this.entityPropertyCache.getProps(entity).name;
|
||||
if (this.entityWantsTrigger(entity)) {
|
||||
if (this.triggerSmoothedGrab()) {
|
||||
this.grabbedEntity = entity;
|
||||
this.setState(STATE_FAR_TRIGGER, "far trigger '" + name + "'");
|
||||
return;
|
||||
} else {
|
||||
// TODO: highlight the far-triggerable object?
|
||||
}
|
||||
} else if (this.entityIsDistanceGrabbable(rayPickInfo.entityID, handPosition)) {
|
||||
if (this.triggerSmoothedGrab()) {
|
||||
this.grabbedEntity = entity;
|
||||
this.setState(STATE_DISTANCE_HOLDING, "distance hold '" + name + "'");
|
||||
return;
|
||||
} else {
|
||||
// TODO: highlight the far-grabbable object?
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//search line visualizations
|
||||
if (USE_ENTITY_LINES_FOR_SEARCHING === true) {
|
||||
this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR);
|
||||
this.lineOn(rayPickInfo.searchRay.origin, Vec3.multiply(rayPickInfo.searchRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR);
|
||||
}
|
||||
|
||||
this.searchIndicatorOn(distantPickRay);
|
||||
this.searchIndicatorOn(rayPickInfo.searchRay);
|
||||
Reticle.setVisible(false);
|
||||
|
||||
};
|
||||
|
||||
this.distanceGrabTimescale = function(mass, distance) {
|
||||
|
@ -1210,8 +1342,8 @@ function MyController(hand) {
|
|||
};
|
||||
|
||||
this.distanceHolding = function() {
|
||||
if (this.triggerSmoothedReleased() && this.secondaryReleased()) {
|
||||
this.setState(STATE_OFF);
|
||||
if (this.triggerSmoothedReleased()) {
|
||||
this.setState(STATE_OFF, "trigger released");
|
||||
this.callEntityMethodOnGrabbed("releaseGrab");
|
||||
return;
|
||||
}
|
||||
|
@ -1230,7 +1362,7 @@ function MyController(hand) {
|
|||
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
|
||||
|
||||
var now = Date.now();
|
||||
var deltaTime = (now - this.currentObjectTime) / MSECS_PER_SEC; // convert to seconds
|
||||
var deltaObjectTime = (now - this.currentObjectTime) / MSECS_PER_SEC; // convert to seconds
|
||||
this.currentObjectTime = now;
|
||||
|
||||
// the action was set up when this.distanceHolding was called. update the targets.
|
||||
|
@ -1267,17 +1399,17 @@ function MyController(hand) {
|
|||
|
||||
// Update radialVelocity
|
||||
var lastVelocity = Vec3.subtract(controllerPositionVSAvatar, this.previousControllerPositionVSAvatar);
|
||||
lastVelocity = Vec3.multiply(lastVelocity, 1.0 / deltaTime);
|
||||
lastVelocity = Vec3.multiply(lastVelocity, 1.0 / deltaObjectTime);
|
||||
var newRadialVelocity = Vec3.dot(lastVelocity,
|
||||
Vec3.normalize(Vec3.subtract(grabbedProperties.position, controllerPosition)));
|
||||
|
||||
var VELOCITY_AVERAGING_TIME = 0.016;
|
||||
this.grabRadialVelocity = (deltaTime / VELOCITY_AVERAGING_TIME) * newRadialVelocity +
|
||||
(1.0 - (deltaTime / VELOCITY_AVERAGING_TIME)) * this.grabRadialVelocity;
|
||||
this.grabRadialVelocity = (deltaObjectTime / VELOCITY_AVERAGING_TIME) * newRadialVelocity +
|
||||
(1.0 - (deltaObjectTime / VELOCITY_AVERAGING_TIME)) * this.grabRadialVelocity;
|
||||
|
||||
var RADIAL_GRAB_AMPLIFIER = 10.0;
|
||||
if (Math.abs(this.grabRadialVelocity) > 0.0) {
|
||||
this.grabRadius = this.grabRadius + (this.grabRadialVelocity * deltaTime * this.grabRadius * RADIAL_GRAB_AMPLIFIER);
|
||||
this.grabRadius = this.grabRadius + (this.grabRadialVelocity * deltaObjectTime * this.grabRadius * RADIAL_GRAB_AMPLIFIER);
|
||||
}
|
||||
|
||||
var newTargetPosition = Vec3.multiply(this.grabRadius, Quat.getUp(controllerRotation));
|
||||
|
@ -1431,11 +1563,56 @@ function MyController(hand) {
|
|||
}
|
||||
}
|
||||
|
||||
this.dropGestureReset = function() {
|
||||
this.fastHandMoveDetected = false;
|
||||
this.fastHandMoveTimer = 0;
|
||||
};
|
||||
|
||||
this.dropGestureProcess = function(deltaTime) {
|
||||
var pose = Controller.getPoseValue((this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand);
|
||||
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};
|
||||
var ROTATION_THRESHOLD = Math.cos(Math.PI / 8);
|
||||
|
||||
var handIsUpsideDown = false;
|
||||
if (Vec3.dot(worldHandUpAxis, DOWN) > ROTATION_THRESHOLD) {
|
||||
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);
|
||||
}
|
||||
|
||||
return this.fastHandMoveDetected && handIsUpsideDown;
|
||||
};
|
||||
|
||||
this.nearGrabbingEnter = function() {
|
||||
|
||||
this.lineOff();
|
||||
this.overlayLineOff();
|
||||
|
||||
this.dropGestureReset();
|
||||
|
||||
if (this.entityActivated) {
|
||||
var saveGrabbedID = this.grabbedEntity;
|
||||
this.release();
|
||||
|
@ -1524,14 +1701,18 @@ function MyController(hand) {
|
|||
this.currentAngularVelocity = ZERO_VEC;
|
||||
};
|
||||
|
||||
this.nearGrabbing = function() {
|
||||
this.nearGrabbing = function(deltaTime) {
|
||||
|
||||
var dropDetected = this.dropGestureProcess(deltaTime);
|
||||
|
||||
if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) {
|
||||
this.setState(STATE_OFF);
|
||||
this.setState(STATE_OFF, "trigger released");
|
||||
this.callEntityMethodOnGrabbed("releaseGrab");
|
||||
return;
|
||||
}
|
||||
if (this.state == STATE_HOLD && this.secondaryReleased()) {
|
||||
this.setState(STATE_OFF);
|
||||
|
||||
if (this.state == STATE_HOLD && dropDetected && this.triggerSmoothedGrab()) {
|
||||
this.setState(STATE_OFF, "drop detected");
|
||||
this.callEntityMethodOnGrabbed("releaseEquip");
|
||||
return;
|
||||
}
|
||||
|
@ -1541,7 +1722,7 @@ function MyController(hand) {
|
|||
var props = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "parentID", "position", "rotation"]);
|
||||
if (!props.position) {
|
||||
// server may have reset, taking our equipped entity with it. move back to "off" stte
|
||||
this.setState(STATE_OFF);
|
||||
this.setState(STATE_OFF, "entity has no position property");
|
||||
this.callEntityMethodOnGrabbed("releaseGrab");
|
||||
return;
|
||||
}
|
||||
|
@ -1561,7 +1742,7 @@ function MyController(hand) {
|
|||
// for whatever reason, the held/equipped entity has been pulled away. ungrab or unequip.
|
||||
print("handControllerGrab -- autoreleasing held or equipped item because it is far from hand." +
|
||||
props.parentID + " " + vec3toStr(props.position));
|
||||
this.setState(STATE_OFF);
|
||||
this.setState(STATE_OFF, "held object too far away");
|
||||
if (this.state == STATE_NEAR_GRABBING) {
|
||||
this.callEntityMethodOnGrabbed("releaseGrab");
|
||||
} else { // this.state == STATE_HOLD
|
||||
|
@ -1581,17 +1762,17 @@ function MyController(hand) {
|
|||
|
||||
var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition;
|
||||
|
||||
var deltaTime = (now - this.currentObjectTime) / MSECS_PER_SEC; // convert to seconds
|
||||
var deltaObjectTime = (now - this.currentObjectTime) / MSECS_PER_SEC; // convert to seconds
|
||||
|
||||
if (deltaTime > 0.0) {
|
||||
if (deltaObjectTime > 0.0) {
|
||||
var worldDeltaPosition = Vec3.subtract(props.position, this.currentObjectPosition);
|
||||
|
||||
var previousEulers = Quat.safeEulerAngles(this.currentObjectRotation);
|
||||
var newEulers = Quat.safeEulerAngles(props.rotation);
|
||||
var worldDeltaRotation = Vec3.subtract(newEulers, previousEulers);
|
||||
|
||||
this.currentVelocity = Vec3.multiply(worldDeltaPosition, 1.0 / deltaTime);
|
||||
this.currentAngularVelocity = Vec3.multiply(worldDeltaRotation, Math.PI / (deltaTime * 180.0));
|
||||
this.currentVelocity = Vec3.multiply(worldDeltaPosition, 1.0 / deltaObjectTime);
|
||||
this.currentAngularVelocity = Vec3.multiply(worldDeltaRotation, Math.PI / (deltaObjectTime * 180.0));
|
||||
|
||||
this.currentObjectPosition = props.position;
|
||||
this.currentObjectRotation = props.rotation;
|
||||
|
@ -1638,8 +1819,8 @@ function MyController(hand) {
|
|||
};
|
||||
|
||||
this.nearTrigger = function() {
|
||||
if (this.triggerSmoothedReleased() && this.secondaryReleased()) {
|
||||
this.setState(STATE_OFF);
|
||||
if (this.triggerSmoothedReleased()) {
|
||||
this.setState(STATE_OFF, "trigger released");
|
||||
this.callEntityMethodOnGrabbed("stopNearTrigger");
|
||||
return;
|
||||
}
|
||||
|
@ -1647,8 +1828,8 @@ function MyController(hand) {
|
|||
};
|
||||
|
||||
this.farTrigger = function() {
|
||||
if (this.triggerSmoothedReleased() && this.secondaryReleased()) {
|
||||
this.setState(STATE_OFF);
|
||||
if (this.triggerSmoothedReleased()) {
|
||||
this.setState(STATE_OFF, "trigger released");
|
||||
this.callEntityMethodOnGrabbed("stopFarTrigger");
|
||||
return;
|
||||
}
|
||||
|
@ -1665,7 +1846,7 @@ function MyController(hand) {
|
|||
if (intersection.accurate) {
|
||||
this.lastPickTime = now;
|
||||
if (intersection.entityID != this.grabbedEntity) {
|
||||
this.setState(STATE_OFF);
|
||||
this.setState(STATE_OFF, "laser moved off of entity");
|
||||
this.callEntityMethodOnGrabbed("stopFarTrigger");
|
||||
return;
|
||||
}
|
||||
|
@ -1680,6 +1861,10 @@ function MyController(hand) {
|
|||
};
|
||||
|
||||
this.offEnter = function() {
|
||||
this.release();
|
||||
};
|
||||
|
||||
this.release = function() {
|
||||
this.turnLightsOff();
|
||||
this.turnOffVisualizations();
|
||||
|
||||
|
@ -1710,6 +1895,10 @@ function MyController(hand) {
|
|||
}));
|
||||
|
||||
this.grabbedEntity = null;
|
||||
|
||||
if (this.triggerSmoothedGrab()) {
|
||||
this.waitForTriggerRelease = true;
|
||||
}
|
||||
};
|
||||
|
||||
this.cleanup = function() {
|
||||
|
@ -1918,12 +2107,12 @@ Controller.enableMapping(MAPPING_NAME);
|
|||
//the section below allows the grab script to listen for messages that disable either one or both hands. useful for two handed items
|
||||
var handToDisable = 'none';
|
||||
|
||||
function update() {
|
||||
function update(deltaTime) {
|
||||
if (handToDisable !== LEFT_HAND && handToDisable !== 'both') {
|
||||
leftController.update();
|
||||
leftController.update(deltaTime);
|
||||
}
|
||||
if (handToDisable !== RIGHT_HAND && handToDisable !== 'both') {
|
||||
rightController.update();
|
||||
rightController.update(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1950,7 +2139,7 @@ var handleHandMessages = function(channel, message, sender) {
|
|||
data = JSON.parse(message);
|
||||
var selectedController = (data.hand === 'left') ? leftController : rightController;
|
||||
selectedController.release();
|
||||
selectedController.setState(STATE_HOLD);
|
||||
selectedController.setState(STATE_HOLD, "Hifi-Hand-Grab msg received");
|
||||
selectedController.grabbedEntity = data.entityID;
|
||||
|
||||
} catch (e) {
|
||||
|
|
Loading…
Reference in a new issue