Merge pull request #6948 from sethalves/equip-via-parenting

equip via parenting rather than action
This commit is contained in:
James B. Pollack 2016-02-01 13:22:09 -08:00
commit cf441861cc
13 changed files with 700 additions and 315 deletions

View file

@ -0,0 +1,275 @@
//
// attachedEntitiesManager.js
//
// Created by Seth Alves on 2016-1-20
// Copyright 2016 High Fidelity, Inc.
//
// This script handles messages from the grab script related to wearables, and interacts with a doppelganger.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("libraries/utils.js");
var NULL_UUID = "{00000000-0000-0000-0000-000000000000}";
var DEFAULT_WEARABLE_DATA = {
joints: {}
};
var MINIMUM_DROP_DISTANCE_FROM_JOINT = 0.4;
var ATTACHED_ENTITY_SEARCH_DISTANCE = 10.0;
var ATTACHED_ENTITIES_SETTINGS_KEY = "ATTACHED_ENTITIES";
var DRESSING_ROOM_DISTANCE = 2.0;
// tool bar
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var BUTTON_SIZE = 32;
var PADDING = 3;
Script.include(["libraries/toolBars.js"]);
var toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.attachedEntities.toolbar", function(screenSize) {
return {
x: (BUTTON_SIZE + PADDING),
y: (screenSize.y / 2 - BUTTON_SIZE * 2 + PADDING)
};
});
var saveButton = toolBar.addOverlay("image", {
width: BUTTON_SIZE,
height: BUTTON_SIZE,
imageURL: "http://headache.hungry.com/~seth/hifi/save.png",
color: {
red: 255,
green: 255,
blue: 255
},
alpha: 1
});
var loadButton = toolBar.addOverlay("image", {
width: BUTTON_SIZE,
height: BUTTON_SIZE,
imageURL: "http://headache.hungry.com/~seth/hifi/load.png",
color: {
red: 255,
green: 255,
blue: 255
},
alpha: 1
});
function mousePressEvent(event) {
var clickedOverlay = Overlays.getOverlayAtPoint({
x: event.x,
y: event.y
});
if (clickedOverlay == saveButton) {
manager.saveAttachedEntities();
} else if (clickedOverlay == loadButton) {
manager.loadAttachedEntities();
}
}
function scriptEnding() {
toolBar.cleanup();
}
Controller.mousePressEvent.connect(mousePressEvent);
Script.scriptEnding.connect(scriptEnding);
// attached entites
function AttachedEntitiesManager() {
this.subscribeToMessages = function() {
Messages.subscribe('Hifi-Object-Manipulation');
Messages.messageReceived.connect(this.handleWearableMessages);
}
this.handleWearableMessages = function(channel, message, sender) {
if (channel !== 'Hifi-Object-Manipulation') {
return;
}
// if (sender !== MyAvatar.sessionUUID) {
// print('wearablesManager got message from wrong sender');
// return;
// }
var parsedMessage = null;
try {
parsedMessage = JSON.parse(message);
} catch (e) {
print('error parsing wearable message');
return;
}
if (parsedMessage.action === 'update' ||
parsedMessage.action === 'loaded') {
// ignore
} else if (parsedMessage.action === 'release') {
manager.checkIfWearable(parsedMessage.grabbedEntity, parsedMessage.joint)
// manager.saveAttachedEntities();
} else if (parsedMessage.action === 'shared-release') {
// manager.saveAttachedEntities();
} else if (parsedMessage.action === 'equip') {
// manager.saveAttachedEntities();
} else {
print('attachedEntitiesManager -- unknown actions: ' + parsedMessage.action);
}
}
this.avatarIsInDressingRoom = function() {
// return true if MyAvatar is near the dressing room
var possibleDressingRoom = Entities.findEntities(MyAvatar.position, DRESSING_ROOM_DISTANCE);
for (i = 0; i < possibleDressingRoom.length; i++) {
var entityID = possibleDressingRoom[i];
var props = Entities.getEntityProperties(entityID);
if (props.name == 'Hifi-Dressing-Room-Base') {
return true;
}
}
return false;
}
this.checkIfWearable = function(grabbedEntity, releasedFromJoint) {
var allowedJoints = getEntityCustomData('wearable', grabbedEntity, DEFAULT_WEARABLE_DATA).joints;
var props = Entities.getEntityProperties(grabbedEntity, ["position", "parentID"]);
if (props.parentID === NULL_UUID || props.parentID === MyAvatar.sessionUUID) {
var bestJointName = "";
var bestJointIndex = -1;
var bestJointDistance = 0;
var bestJointOffset = null;
for (var jointName in allowedJoints) {
if ((releasedFromJoint == "LeftHand" || releasedFromJoint == "RightHand") &&
(jointName == "LeftHand" || jointName == "RightHand")) {
// don't auto-attach to a hand if a hand just dropped something
continue;
}
var jointIndex = MyAvatar.getJointIndex(jointName);
if (jointIndex > 0) {
var jointPosition = MyAvatar.getJointPosition(jointIndex);
var distanceFromJoint = Vec3.distance(jointPosition, props.position);
if (distanceFromJoint <= MINIMUM_DROP_DISTANCE_FROM_JOINT) {
if (bestJointIndex == -1 || distanceFromJoint < bestJointDistance) {
bestJointName = jointName;
bestJointIndex = jointIndex;
bestJointDistance = distanceFromJoint;
bestJointOffset = allowedJoints[jointName];
}
}
}
}
if (bestJointIndex != -1) {
var wearProps = {
parentID: MyAvatar.sessionUUID,
parentJointIndex: bestJointIndex
};
if (!this.avatarIsInDressingRoom() &&
bestJointOffset && bestJointOffset.constructor === Array && bestJointOffset.length > 1) {
// don't snap the entity to the preferred position if the avatar is in the dressing room.
wearProps.localPosition = bestJointOffset[0];
wearProps.localRotation = bestJointOffset[1];
}
Entities.editEntity(grabbedEntity, wearProps);
} else if (props.parentID != NULL_UUID) {
// drop the entity with no parent (not on the avatar)
Entities.editEntity(grabbedEntity, {
parentID: NULL_UUID
});
}
}
}
this.updateRelativeOffsets = function(entityID, props) {
// save the preferred (current) relative position and rotation into the user-data of the entity
var wearableData = getEntityCustomData('wearable', entityID, DEFAULT_WEARABLE_DATA);
var currentJointName = MyAvatar.getJointNames()[props.parentJointIndex];
wearableData.joints[currentJointName] = [props.localPosition, props.localRotation];
setEntityCustomData('wearable', entityID, wearableData);
}
this.saveAttachedEntities = function() {
print("--- saving attached entities ---");
saveData = [];
var nearbyEntities = Entities.findEntities(MyAvatar.position, ATTACHED_ENTITY_SEARCH_DISTANCE);
for (i = 0; i < nearbyEntities.length; i++) {
var entityID = nearbyEntities[i];
var props = Entities.getEntityProperties(entityID);
if (props.parentID == MyAvatar.sessionUUID) {
grabData = getEntityCustomData('grabKey', entityID, {});
grabbableData = getEntityCustomData('grabbableKey', entityID, {});
this.updateRelativeOffsets(entityID, props);
props = Entities.getEntityProperties(entityID); // refresh, because updateRelativeOffsets changed them
this.scrubProperties(props);
saveData.push(props);
}
}
Settings.setValue(ATTACHED_ENTITIES_SETTINGS_KEY, JSON.stringify(saveData));
}
this.scrubProperties = function(props) {
var toScrub = ["queryAACube", "position", "rotation",
"created", "ageAsText", "naturalDimensions",
"naturalPosition", "velocity", "acceleration",
"angularVelocity", "boundingBox"];
toScrub.forEach(function(propertyName) {
delete props[propertyName];
});
// if the userData has a grabKey, clear old state
if ("userData" in props) {
try {
parsedUserData = JSON.parse(props.userData);
if ("grabKey" in parsedUserData) {
parsedUserData.grabKey.refCount = 0;
delete parsedUserData.grabKey["avatarId"];
props["userData"] = JSON.stringify(parsedUserData);
}
} catch (e) {
}
}
}
this.loadAttachedEntities = function(grabbedEntity) {
print("--- loading attached entities ---");
jsonAttachmentData = Settings.getValue(ATTACHED_ENTITIES_SETTINGS_KEY);
var loadData = [];
try {
loadData = JSON.parse(jsonAttachmentData);
} catch (e) {
print('error parsing saved attachment data');
return;
}
for (i = 0; i < loadData.length; i ++) {
var savedProps = loadData[ i ];
var currentProps = Entities.getEntityProperties(savedProps.id);
if (currentProps.id == savedProps.id &&
// TODO -- also check that parentJointIndex matches?
currentProps.parentID == MyAvatar.sessionUUID) {
// entity is already in-world. TODO -- patch it up?
continue;
}
this.scrubProperties(savedProps);
delete savedProps["id"];
savedProps.parentID = MyAvatar.sessionUUID; // this will change between sessions
var loadedEntityID = Entities.addEntity(savedProps);
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
action: 'loaded',
grabbedEntity: loadedEntityID
}));
}
}
}
var manager = new AttachedEntitiesManager();
manager.subscribeToMessages();

View file

@ -111,7 +111,8 @@ var GRABBABLE_PROPERTIES = [
"rotation",
"gravity",
"collidesWith",
"collisionsWillMove",
"dynamic",
"collisionless",
"locked",
"name",
"shapeType",
@ -125,7 +126,6 @@ var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js
var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js
var DEFAULT_GRABBABLE_DATA = {
grabbable: true,
disableReleaseVelocity: false
};
@ -164,7 +164,6 @@ var STATE_EQUIP = 12
var STATE_CONTINUE_EQUIP_BD = 13; // equip while bumper is still held down
var STATE_CONTINUE_EQUIP = 14;
var STATE_WAITING_FOR_BUMPER_RELEASE = 15;
var STATE_EQUIP_SPRING = 16;
// "collidesWith" is specified by comma-separated list of group names
// the possible group names are: static, dynamic, kinematic, myAvatar, otherAvatar
@ -205,8 +204,6 @@ function stateToName(state) {
return "continue_equip";
case STATE_WAITING_FOR_BUMPER_RELEASE:
return "waiting_for_bumper_release";
case STATE_EQUIP_SPRING:
return "state_equip_spring";
}
return "unknown";
@ -235,48 +232,6 @@ function entityIsGrabbedByOther(entityID) {
return false;
}
function getSpatialOffsetPosition(hand, spatialKey) {
var position = Vec3.ZERO;
if (hand !== RIGHT_HAND && spatialKey.leftRelativePosition) {
position = spatialKey.leftRelativePosition;
}
if (hand === RIGHT_HAND && spatialKey.rightRelativePosition) {
position = spatialKey.rightRelativePosition;
}
if (spatialKey.relativePosition) {
position = spatialKey.relativePosition;
}
// add the relative hand center offset
var handSizeRatio = calculateHandSizeRatio();
position = Vec3.multiply(position, handSizeRatio);
return position;
}
var yFlip = Quat.angleAxis(180, Vec3.UNIT_Y);
function getSpatialOffsetRotation(hand, spatialKey) {
var rotation = Quat.IDENTITY;
if (hand !== RIGHT_HAND && spatialKey.leftRelativeRotation) {
rotation = spatialKey.leftRelativeRotation;
}
if (hand === RIGHT_HAND && spatialKey.rightRelativeRotation) {
rotation = spatialKey.rightRelativeRotation;
}
if (spatialKey.relativeRotation) {
rotation = spatialKey.relativeRotation;
}
// Flip left hand
if (hand !== RIGHT_HAND) {
rotation = Quat.multiply(yFlip, rotation);
}
return rotation;
}
function MyController(hand) {
this.hand = hand;
if (this.hand === RIGHT_HAND) {
@ -287,9 +242,6 @@ function MyController(hand) {
this.getHandRotation = MyAvatar.getLeftPalmRotation;
}
var SPATIAL_CONTROLLERS_PER_PALM = 2;
var TIP_CONTROLLER_OFFSET = 1;
this.actionID = null; // action this script created...
this.grabbedEntity = null; // on this entity.
this.state = STATE_OFF;
@ -347,9 +299,6 @@ function MyController(hand) {
case STATE_WAITING_FOR_BUMPER_RELEASE:
this.waitingForBumperRelease();
break;
case STATE_EQUIP_SPRING:
this.pullTowardEquipPosition()
break;
case STATE_CONTINUE_NEAR_GRABBING:
case STATE_CONTINUE_EQUIP_BD:
case STATE_CONTINUE_EQUIP:
@ -373,9 +322,19 @@ function MyController(hand) {
}
};
this.callEntityMethodOnGrabbed = function(entityMethodName, args) {
// print("Entity Method: " + entityMethodName + ", hand: " + this.hand);
if (args.length > 0) {
Entities.callEntityMethod(this.grabbedEntity, entityMethodName, args);
} else {
Entities.callEntityMethod(this.grabbedEntity, entityMethodName);
}
}
this.setState = function(newState) {
if (WANT_DEBUG || WANT_DEBUG_STATE) {
print("STATE: " + stateToName(this.state) + " --> " + stateToName(newState) + ", hand: " + this.hand);
print("STATE (" + this.hand + "): " + stateToName(this.state) + " --> " +
stateToName(newState) + ", hand: " + this.hand);
}
this.state = newState;
};
@ -390,7 +349,7 @@ function MyController(hand) {
linePoints: [ZERO_VEC, farPoint],
color: color,
lifetime: 0.1,
collisionsWillMove: false,
dynamic: false,
ignoreForCollisions: true,
userData: JSON.stringify({
grabbableKey: {
@ -412,7 +371,7 @@ function MyController(hand) {
linePoints: [ZERO_VEC, farPoint],
color: color,
lifetime: LIFETIME,
collisionsWillMove: false,
dynamic: false,
ignoreForCollisions: true,
userData: JSON.stringify({
grabbableKey: {
@ -704,6 +663,10 @@ function MyController(hand) {
};
this.propsArePhysical = function(props) {
if (!props.dynamic && props.parentID != MyAvatar.sessionUUID) {
// if we have parented something, don't do this check on dynamic.
return false;
}
var isPhysical = (props.shapeType && props.shapeType != 'none');
return isPhysical;
}
@ -773,6 +736,7 @@ function MyController(hand) {
this.search = function() {
this.grabbedEntity = null;
this.isInitialGrab = false;
if (this.state == STATE_SEARCHING ? this.triggerSmoothedReleased() : this.bumperReleased()) {
this.setState(STATE_RELEASE);
@ -787,7 +751,9 @@ function MyController(hand) {
var distantPickRay = {
origin: PICK_WITH_HAND_RAY ? handPosition : Camera.position,
direction: PICK_WITH_HAND_RAY ? Quat.getUp(this.getHandRotation()) : Vec3.mix(Quat.getUp(this.getHandRotation()), Quat.getFront(Camera.orientation), HAND_HEAD_MIX_RATIO),
direction: PICK_WITH_HAND_RAY ? Quat.getUp(this.getHandRotation()) : Vec3.mix(Quat.getUp(this.getHandRotation()),
Quat.getFront(Camera.orientation),
HAND_HEAD_MIX_RATIO),
length: PICK_MAX_DISTANCE
};
@ -835,7 +801,7 @@ function MyController(hand) {
candidateEntities = rayPickedCandidateEntities.concat(nearPickedCandidateEntities);
var forbiddenNames = ["Grab Debug Entity", "grab pointer"];
var forbiddenTyes = ['Unknown', 'Light', 'ParticleEffect', 'PolyLine', 'Zone'];
var forbiddenTypes = ['Unknown', 'Light', 'ParticleEffect', 'PolyLine', 'Zone'];
var minDistance = PICK_MAX_DISTANCE;
var i, props, distance, grabbableData;
@ -843,12 +809,31 @@ function MyController(hand) {
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 grabbable = (typeof grabbableDataForCandidate.grabbable === 'undefined' || grabbableDataForCandidate.grabbable);
var isPhysical = this.propsArePhysical(propsForCandidate);
var grabbable;
if (isPhysical) {
// 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) {
continue;
}
if (forbiddenTyes.indexOf(propsForCandidate.type) >= 0) {
if (forbiddenTypes.indexOf(propsForCandidate.type) >= 0) {
continue;
}
if (propsForCandidate.locked && !grabbableDataForCandidate.wantsTrigger) {
@ -863,6 +848,15 @@ function MyController(hand) {
// too far away, don't grab
continue;
}
if (propsForCandidate.parentID != NULL_UUID && this.state == STATE_EQUIP_SEARCHING) {
// don't allow a double-equip
continue;
}
if (this.state == STATE_SEARCHING && !isPhysical && distance > NEAR_PICK_MAX_DISTANCE) {
// we can't distance-grab non-physical
continue;
}
if (distance < minDistance) {
this.grabbedEntity = candidateEntities[i];
@ -873,7 +867,7 @@ function MyController(hand) {
}
if ((this.grabbedEntity !== null) && (this.triggerSmoothedGrab() || this.bumperSqueezed())) {
// We are squeezing enough to grab, and we've found an entity that we'll try to do something with.
var near = (nearPickedCandidateEntities.indexOf(this.grabbedEntity) >= 0);
var near = (nearPickedCandidateEntities.indexOf(this.grabbedEntity) >= 0) || minDistance <= NEAR_PICK_MAX_DISTANCE;
var isPhysical = this.propsArePhysical(props);
// near or far trigger
@ -882,22 +876,25 @@ function MyController(hand) {
return;
}
// near grab or equip with action
if (isPhysical && near) {
if (near) {
this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP);
return;
}
// far grab or equip with action
if (isPhysical && !near) {
if ((isPhysical || this.state == STATE_EQUIP_SEARCHING) && !near) {
if (entityIsGrabbedByOther(intersection.entityID)) {
// don't distance grab something that is already grabbed.
return;
}
this.temporaryPositionOffset = null;
if (typeof grabbableData.spatialKey === 'undefined') {
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));
this.temporaryPositionOffset = Vec3.normalize(Vec3.subtract(intersection.properties.position, handPosition));
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);
@ -907,9 +904,12 @@ function MyController(hand) {
return;
}
// else this thing isn't physical. grab it by reparenting it.
this.setState(STATE_NEAR_GRABBING);
return;
// else this thing isn't physical. grab it by reparenting it (but not if we've already
// grabbed it).
if (grabbableData.refCount < 1) {
this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP);
return;
}
}
//search line visualizations
@ -921,14 +921,18 @@ function MyController(hand) {
var SEARCH_SPHERE_FOLLOW_RATE = 0.50;
if (this.intersectionDistance > 0) {
// If we hit something with our pick ray, move the search sphere toward that distance
this.searchSphereDistance = this.searchSphereDistance * SEARCH_SPHERE_FOLLOW_RATE + this.intersectionDistance * (1.0 - SEARCH_SPHERE_FOLLOW_RATE);
// If we hit something with our pick ray, move the search sphere toward that distance
this.searchSphereDistance = this.searchSphereDistance * SEARCH_SPHERE_FOLLOW_RATE +
this.intersectionDistance * (1.0 - SEARCH_SPHERE_FOLLOW_RATE);
}
var searchSphereLocation = Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, this.searchSphereDistance));
this.searchSphereOn(searchSphereLocation, SEARCH_SPHERE_SIZE * this.searchSphereDistance, (this.triggerSmoothedGrab() || this.bumperSqueezed()) ? INTERSECT_COLOR : NO_INTERSECT_COLOR);
var searchSphereLocation = Vec3.sum(distantPickRay.origin,
Vec3.multiply(distantPickRay.direction, this.searchSphereDistance));
this.searchSphereOn(searchSphereLocation, SEARCH_SPHERE_SIZE * this.searchSphereDistance,
(this.triggerSmoothedGrab() || this.bumperSqueezed()) ? INTERSECT_COLOR : NO_INTERSECT_COLOR);
if ((USE_OVERLAY_LINES_FOR_SEARCHING === true) && PICK_WITH_HAND_RAY) {
this.overlayLineOn(handPosition, searchSphereLocation, (this.triggerSmoothedGrab() || this.bumperSqueezed()) ? INTERSECT_COLOR : NO_INTERSECT_COLOR);
this.overlayLineOn(handPosition, searchSphereLocation,
(this.triggerSmoothedGrab() || this.bumperSqueezed()) ? INTERSECT_COLOR : NO_INTERSECT_COLOR);
}
};
@ -986,14 +990,8 @@ function MyController(hand) {
if (this.actionID !== null) {
this.setState(STATE_CONTINUE_DISTANCE_HOLDING);
this.activateEntity(this.grabbedEntity, grabbedProperties);
if (this.hand === RIGHT_HAND) {
Entities.callEntityMethod(this.grabbedEntity, "setRightHand");
} else {
Entities.callEntityMethod(this.grabbedEntity, "setLeftHand");
}
Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]);
Entities.callEntityMethod(this.grabbedEntity, "startDistantGrab");
this.activateEntity(this.grabbedEntity, grabbedProperties, false);
this.callSetupEntityMethods("startDistanceGrab");
}
this.currentAvatarPosition = MyAvatar.position;
@ -1005,7 +1003,9 @@ function MyController(hand) {
this.continueDistanceHolding = function() {
if (this.triggerSmoothedReleased()) {
this.setState(STATE_RELEASE);
Entities.callEntityMethod(this.grabbedEntity, "releaseGrab");
if (this.isInitialGrab) {
this.callEntityMethodOnGrabbed("releaseGrab", []);
}
return;
}
@ -1017,7 +1017,7 @@ function MyController(hand) {
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
if (this.state == STATE_CONTINUE_DISTANCE_HOLDING && this.bumperSqueezed() &&
typeof grabbableData.spatialKey !== 'undefined') {
this.hasPresetOffsets()) {
var saveGrabbedID = this.grabbedEntity;
this.release();
this.setState(STATE_EQUIP);
@ -1087,7 +1087,7 @@ function MyController(hand) {
this.handPreviousRotation = handRotation;
this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation);
Entities.callEntityMethod(this.grabbedEntity, "continueDistantGrab");
this.callEntityMethodOnGrabbed("continueDistantGrab", []);
var defaultMoveWithHeadData = {
disableMoveWithHead: false
@ -1188,40 +1188,69 @@ function MyController(hand) {
};
this.projectVectorAlongAxis = function(position, axisStart, axisEnd) {
var aPrime = Vec3.subtract(position, axisStart);
var bPrime = Vec3.subtract(axisEnd, axisStart);
var bPrimeMagnitude = Vec3.length(bPrime);
var dotProduct = Vec3.dot(aPrime, bPrime);
var scalar = dotProduct / bPrimeMagnitude;
if (scalar < 0) {
scalar = 0;
}
if (scalar > 1) {
scalar = 1;
}
var projection = Vec3.sum(axisStart, Vec3.multiply(scalar, Vec3.normalize(bPrime)));
return projection
};
this.callSetupEntityMethods = function(entityMethodName) {
if (this.isInitialGrab) {
if (this.hand === RIGHT_HAND) {
this.callEntityMethodOnGrabbed("setRightHand", []);
} else {
this.callEntityMethodOnGrabbed("setLeftHand", []);
}
this.callEntityMethodOnGrabbed("setHand", [this.hand]);
this.callEntityMethodOnGrabbed(entityMethodName, [JSON.stringify(this.hand)]);
}
}
this.hasPresetOffsets = function() {
var wearableData = getEntityCustomData('wearable', this.grabbedEntity, {joints: {}});
var allowedJoints = wearableData.joints;
var handJointName = this.hand === RIGHT_HAND ? "RightHand" : "LeftHand";
if (handJointName in allowedJoints) {
return true;
}
}
this.getPresetPosition = function() {
var wearableData = getEntityCustomData('wearable', this.grabbedEntity, {joints: {}});
var allowedJoints = wearableData.joints;
var handJointName = this.hand === RIGHT_HAND ? "RightHand" : "LeftHand";
if (handJointName in allowedJoints) {
return allowedJoints[handJointName][0];
}
}
this.getPresetRotation = function() {
var wearableData = getEntityCustomData('wearable', this.grabbedEntity, {joints: {}});
var allowedJoints = wearableData.joints;
var handJointName = this.hand === RIGHT_HAND ? "RightHand" : "LeftHand";
if (handJointName in allowedJoints) {
return allowedJoints[handJointName][1];
}
}
this.nearGrabbing = function() {
var now = Date.now();
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) {
this.setState(STATE_RELEASE);
Entities.callEntityMethod(this.grabbedEntity, "releaseGrab");
if (this.isInitialGrab) {
this.callEntityMethodOnGrabbed("releaseGrab", []);
}
return;
}
@ -1229,10 +1258,10 @@ function MyController(hand) {
this.overlayLineOff();
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
this.activateEntity(this.grabbedEntity, grabbedProperties);
if (grabbedProperties.collisionsWillMove && NEAR_GRABBING_KINEMATIC) {
this.activateEntity(this.grabbedEntity, grabbedProperties, false);
if (grabbedProperties.dynamic && NEAR_GRABBING_KINEMATIC) {
Entities.editEntity(this.grabbedEntity, {
collisionsWillMove: false
dynamic: false
});
}
@ -1241,11 +1270,13 @@ function MyController(hand) {
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) {
// if an object is "equipped" and has a spatialKey, use it.
this.ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false;
this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey);
this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey);
var hasPresetPosition = false;
if (this.state != STATE_NEAR_GRABBING && this.hasPresetOffsets()) {
// if an object is "equipped" and has a predefined offset, use it.
this.ignoreIK = grabbableData.ignoreIK ? grabbableData.ignoreIK : false;
this.offsetPosition = this.getPresetPosition();
this.offsetRotation = this.getPresetRotation();
hasPresetPosition = true;
} else {
this.ignoreIK = false;
@ -1257,51 +1288,56 @@ function MyController(hand) {
this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset);
if (this.temporaryPositionOffset && this.state != STATE_NEAR_GRABBING) {
this.offsetPosition = this.temporaryPositionOffset;
hasPresetPosition = true;
}
}
var isPhysical = this.propsArePhysical(grabbedProperties);
if (isPhysical) {
if (isPhysical && this.state == STATE_NEAR_GRABBING) {
// grab entity via action
if (!this.setupHoldAction()) {
return;
}
} else {
// grab entity via parenting
this.actionID = null;
var handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand");
Entities.editEntity(this.grabbedEntity, {
reparentProps = {
parentID: MyAvatar.sessionUUID,
parentJointIndex: handJointIndex
});
}
if (hasPresetPosition) {
reparentProps["localPosition"] = this.offsetPosition;
reparentProps["localRotation"] = this.offsetRotation;
}
Entities.editEntity(this.grabbedEntity, reparentProps);
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
action: 'equip',
grabbedEntity: this.grabbedEntity
}));
}
this.callSetupEntityMethods(this.state == STATE_NEAR_GRABBING ? "startNearGrab" : "startEquip");
if (this.state == STATE_NEAR_GRABBING) {
// near grabbing
this.setState(STATE_CONTINUE_NEAR_GRABBING);
} else {
// equipping
Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]);
this.setState(STATE_CONTINUE_EQUIP_BD);
}
if (this.hand === RIGHT_HAND) {
Entities.callEntityMethod(this.grabbedEntity, "setRightHand");
} else {
Entities.callEntityMethod(this.grabbedEntity, "setLeftHand");
}
Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]);
Entities.callEntityMethod(this.grabbedEntity, "startNearGrab");
this.currentHandControllerTipPosition =
(this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition;
this.currentObjectTime = Date.now();
};
this.continueNearGrabbing = function() {
if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.triggerSmoothedReleased()) {
this.setState(STATE_RELEASE);
Entities.callEntityMethod(this.grabbedEntity, "releaseGrab");
if (this.isInitialGrab) {
this.callEntityMethodOnGrabbed("releaseGrab", []);
}
return;
}
if (this.state == STATE_CONTINUE_EQUIP_BD && this.bumperReleased()) {
@ -1310,11 +1346,15 @@ function MyController(hand) {
}
if (this.state == STATE_CONTINUE_EQUIP && this.bumperSqueezed()) {
this.setState(STATE_WAITING_FOR_BUMPER_RELEASE);
this.callEntityMethodOnGrabbed("releaseEquip", [JSON.stringify(this.hand)]);
return;
}
if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.bumperSqueezed()) {
this.setState(STATE_CONTINUE_EQUIP_BD);
Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]);
if (this.isInitialGrab) {
this.callEntityMethodOnGrabbed("releaseGrab", [JSON.stringify(this.hand)]);
this.callEntityMethodOnGrabbed("startEquip", [JSON.stringify(this.hand)]);
}
return;
}
@ -1333,10 +1373,17 @@ function MyController(hand) {
this.currentHandControllerTipPosition = handControllerPosition;
this.currentObjectTime = now;
Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab");
if (this.state === STATE_CONTINUE_EQUIP_BD) {
Entities.callEntityMethod(this.grabbedEntity, "continueEquip");
var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, {});
if (this.isInitialGrab) {
if (this.state === STATE_CONTINUE_EQUIP) {
// this.callEntityMethodOnGrabbed("continueEquip", []);
Entities.callEntityMethod(this.grabbedEntity, "continueEquip");
}
if (this.state == STATE_CONTINUE_NEAR_GRABBING) {
// this.callEntityMethodOnGrabbed("continueNearGrab", []);
Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab");
}
}
if (this.actionID && this.actionTimeout - now < ACTION_TTL_REFRESH * MSEC_PER_SEC) {
@ -1364,114 +1411,48 @@ function MyController(hand) {
this.waitingForBumperRelease = function() {
if (this.bumperReleased()) {
this.setState(STATE_RELEASE);
Entities.callEntityMethod(this.grabbedEntity, "releaseGrab");
Entities.callEntityMethod(this.grabbedEntity, "unequip");
}
};
this.pullTowardEquipPosition = function() {
this.turnOffVisualizations();
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
// use a spring to pull the object to where it will be when equipped
var relativeRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey);
var relativePosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey);
var ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false;
var handRotation = this.getHandRotation();
var handPosition = this.getHandPosition();
var targetRotation = Quat.multiply(handRotation, relativeRotation);
var offset = Vec3.multiplyQbyV(targetRotation, relativePosition);
var targetPosition = Vec3.sum(handPosition, offset);
if (typeof this.equipSpringID === 'undefined' ||
this.equipSpringID === null ||
this.equipSpringID === NULL_UUID) {
this.equipSpringID = Entities.addAction("spring", this.grabbedEntity, {
targetPosition: targetPosition,
linearTimeScale: EQUIP_SPRING_TIMEFRAME,
targetRotation: targetRotation,
angularTimeScale: EQUIP_SPRING_TIMEFRAME,
ttl: ACTION_TTL,
ignoreIK: ignoreIK
});
if (this.equipSpringID === NULL_UUID) {
this.equipSpringID = null;
this.setState(STATE_OFF);
return;
var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, {});
if (this.isInitialGrab) {
// TODO -- only one of these should be sent
this.callEntityMethodOnGrabbed("releaseGrab", []);
this.callEntityMethodOnGrabbed("unequip", []);
}
} else {
var success = Entities.updateAction(this.grabbedEntity, this.equipSpringID, {
targetPosition: targetPosition,
linearTimeScale: EQUIP_SPRING_TIMEFRAME,
targetRotation: targetRotation,
angularTimeScale: EQUIP_SPRING_TIMEFRAME,
ttl: ACTION_TTL,
ignoreIK: ignoreIK
});
if (!success) {
print("pullTowardEquipPosition -- updateActionfailed");
}
}
if (Vec3.distance(grabbedProperties.position, targetPosition) < EQUIP_SPRING_SHUTOFF_DISTANCE) {
Entities.deleteAction(this.grabbedEntity, this.equipSpringID);
this.equipSpringID = null;
this.setState(STATE_EQUIP);
}
};
this.nearTrigger = function() {
if (this.triggerSmoothedReleased()) {
this.setState(STATE_RELEASE);
Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger");
this.callEntityMethodOnGrabbed("stopNearTrigger", []);
return;
}
if (this.hand === RIGHT_HAND) {
Entities.callEntityMethod(this.grabbedEntity, "setRightHand");
} else {
Entities.callEntityMethod(this.grabbedEntity, "setLeftHand");
}
Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]);
Entities.callEntityMethod(this.grabbedEntity, "startNearTrigger");
this.callSetupEntityMethods("startNearTrigger");
this.setState(STATE_CONTINUE_NEAR_TRIGGER);
};
this.farTrigger = function() {
if (this.triggerSmoothedReleased()) {
this.setState(STATE_RELEASE);
Entities.callEntityMethod(this.grabbedEntity, "stopFarTrigger");
this.callEntityMethodOnGrabbed("stopFarTrigger", []);
return;
}
if (this.hand === RIGHT_HAND) {
Entities.callEntityMethod(this.grabbedEntity, "setRightHand");
} else {
Entities.callEntityMethod(this.grabbedEntity, "setLeftHand");
}
Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]);
Entities.callEntityMethod(this.grabbedEntity, "startFarTrigger");
this.callSetupEntityMethods("startFarTrigger");
this.setState(STATE_CONTINUE_FAR_TRIGGER);
};
this.continueNearTrigger = function() {
if (this.triggerSmoothedReleased()) {
this.setState(STATE_RELEASE);
Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger");
this.callEntityMethodOnGrabbed("stopNearTrigger", []);
return;
}
Entities.callEntityMethod(this.grabbedEntity, "continueNearTrigger");
this.callEntityMethodOnGrabbed("continueNearTrigger", []);
};
this.continueFarTrigger = function() {
if (this.triggerSmoothedReleased()) {
this.setState(STATE_RELEASE);
Entities.callEntityMethod(this.grabbedEntity, "stopFarTrigger");
this.callEntityMethodOnGrabbed("stopFarTrigger", []);
return;
}
@ -1487,7 +1468,7 @@ function MyController(hand) {
this.lastPickTime = now;
if (intersection.entityID != this.grabbedEntity) {
this.setState(STATE_RELEASE);
Entities.callEntityMethod(this.grabbedEntity, "stopFarTrigger");
this.callEntityMethodOnGrabbed("stopFarTrigger", []);
return;
}
}
@ -1495,8 +1476,7 @@ function MyController(hand) {
if (USE_ENTITY_LINES_FOR_MOVING === true) {
this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR);
}
Entities.callEntityMethod(this.grabbedEntity, "continueFarTrigger");
this.callEntityMethodOnGrabbed("continueFarTrigger", []);
};
_this.allTouchedIDs = {};
@ -1556,15 +1536,16 @@ function MyController(hand) {
};
this.startTouch = function(entityID) {
Entities.callEntityMethod(entityID, "startTouch");
this.callEntityMethodOnGrabbed("startTouch", []);
};
this.continueTouch = function(entityID) {
Entities.callEntityMethod(entityID, "continueTouch");
// this.callEntityMethodOnGrabbed("continueTouch", []);
Entities.callEntityMethod(this.grabbedEntity, "continueTouch");
};
this.stopTouch = function(entityID) {
Entities.callEntityMethod(entityID, "stopTouch");
this.callEntityMethodOnGrabbed("stopTouch", []);
};
this.release = function() {
@ -1572,15 +1553,13 @@ function MyController(hand) {
this.turnLightsOff();
this.turnOffVisualizations();
var noVelocity = false;
if (this.grabbedEntity !== null) {
if (this.actionID !== null) {
Entities.deleteAction(this.grabbedEntity, this.actionID);
//sometimes we want things to stay right where they are when we let go.
var releaseVelocityData = getEntityCustomData(GRABBABLE_DATA_KEY,
this.grabbedEntity,
DEFAULT_GRABBABLE_DATA);
if (releaseVelocityData.disableReleaseVelocity === true) {
Entities.deleteAction(this.grabbedEntity, this.actionID);
var releaseVelocityData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
if (releaseVelocityData.disableReleaseVelocity === true || !this.isInitialGrab) {
Entities.editEntity(this.grabbedEntity, {
velocity: {
x: 0,
@ -1592,25 +1571,29 @@ function MyController(hand) {
y: 0,
z: 0
}
})
Entities.deleteAction(this.grabbedEntity, this.actionID);
} else {
//don't make adjustments
Entities.deleteAction(this.grabbedEntity, this.actionID);
});
noVelocity = true;
}
}
}
this.deactivateEntity(this.grabbedEntity);
this.deactivateEntity(this.grabbedEntity, noVelocity);
this.actionID = null;
this.setState(STATE_OFF);
Messages.sendMessage('Hifi-Wearables-Manager', JSON.stringify({
action: 'checkIfWearable',
grabbedEntity: this.grabbedEntity
}))
if (this.isInitialGrab) {
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
action: 'release',
grabbedEntity: this.grabbedEntity,
joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"
}));
} else {
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
action: 'shared-release',
grabbedEntity: this.grabbedEntity,
joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"
}));
}
this.grabbedEntity = null;
};
@ -1622,57 +1605,85 @@ function MyController(hand) {
Entities.deleteEntity(this.pointLight);
};
this.activateEntity = function(entityID, grabbedProperties) {
this.activateEntity = function(entityID, grabbedProperties, wasLoaded) {
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, entityID, DEFAULT_GRABBABLE_DATA);
var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {});
data["activated"] = true;
data["avatarId"] = MyAvatar.sessionUUID;
data["refCount"] = data["refCount"] ? data["refCount"] + 1 : 1;
// zero gravity and set ignoreForCollisions in a way that lets us put them back, after all grabs are done
if (data["refCount"] == 1) {
data["gravity"] = grabbedProperties.gravity;
data["collidesWith"] = grabbedProperties.collidesWith;
data["collisionsWillMove"] = grabbedProperties.collisionsWillMove;
data["parentID"] = grabbedProperties.parentID;
data["parentJointIndex"] = grabbedProperties.parentJointIndex;
var whileHeldProperties = {
gravity: {
x: 0,
y: 0,
z: 0
},
// bummer, it isn't easy to do bitwise collisionMask operations like this:
//"collisionMask": COLLISION_MASK_WHILE_GRABBED | grabbedProperties.collisionMask
// when using string values
"collidesWith": COLLIDES_WITH_WHILE_GRABBED
};
Entities.editEntity(entityID, whileHeldProperties);
} else if (data["refCount"] > 1) {
// if an object is being grabbed by more than one person (or the same person twice, but nevermind), switch
// the collision groups so that it wont collide with "other" avatars. This avoids a situation where two
// people are holding something and one of them will be able (if the other releases at the right time) to
// bootstrap themselves with the held object. This happens because the meaning of "otherAvatar" in
// the collision mask hinges on who the physics simulation owner is.
Entities.editEntity(entityID, {"collidesWith": COLLIDES_WITH_WHILE_MULTI_GRABBED});
}
if (wasLoaded) {
data["refCount"] = 1;
data["avatarId"] = MyAvatar.sessionUUID;
} else {
data["refCount"] = data["refCount"] ? data["refCount"] + 1 : 1;
// zero gravity and set ignoreForCollisions in a way that lets us put them back, after all grabs are done
if (data["refCount"] == 1) {
this.isInitialGrab = true;
data["gravity"] = grabbedProperties.gravity;
data["collidesWith"] = grabbedProperties.collidesWith;
data["collisionless"] = grabbedProperties.collisionless;
data["dynamic"] = grabbedProperties.dynamic;
data["parentID"] = wasLoaded ? NULL_UUID : grabbedProperties.parentID;
data["parentJointIndex"] = grabbedProperties.parentJointIndex;
var whileHeldProperties = {
gravity: {
x: 0,
y: 0,
z: 0
},
// bummer, it isn't easy to do bitwise collisionMask operations like this:
//"collisionMask": COLLISION_MASK_WHILE_GRABBED | grabbedProperties.collisionMask
// when using string values
"collidesWith": COLLIDES_WITH_WHILE_GRABBED
};
Entities.editEntity(entityID, whileHeldProperties);
} else if (data["refCount"] > 1) {
this.isInitialGrab = false;
// if an object is being grabbed by more than one person (or the same person twice, but nevermind), switch
// the collision groups so that it wont collide with "other" avatars. This avoids a situation where two
// people are holding something and one of them will be able (if the other releases at the right time) to
// bootstrap themselves with the held object. This happens because the meaning of "otherAvatar" in
// the collision mask hinges on who the physics simulation owner is.
Entities.editEntity(entityID, {"collidesWith": COLLIDES_WITH_WHILE_MULTI_GRABBED});
}
}
setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data);
return data;
};
this.deactivateEntity = function(entityID) {
this.deactivateEntity = function(entityID, noVelocity) {
var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {});
if (data && data["refCount"]) {
data["refCount"] = data["refCount"] - 1;
if (data["refCount"] < 1) {
Entities.editEntity(entityID, {
var deactiveProps = {
gravity: data["gravity"],
collidesWith: data["collidesWith"],
collisionsWillMove: data["collisionsWillMove"],
ignoreForCollisions: data["ignoreForCollisions"],
collisionless: data["collisionless"],
dynamic: data["dynamic"],
parentID: data["parentID"],
parentJointIndex: data["parentJointIndex"]
});
};
// things that are held by parenting and dropped with no velocity will end up as "static" in bullet. If
// it looks like the dropped thing should fall, give it a little velocity.
var parentID = Entities.getEntityProperties(entityID, ["parentID"]).parentID;
var forceVelocity = false;
if (!noVelocity &&
parentID == MyAvatar.sessionUUID &&
Vec3.length(data["gravity"]) > 0.0 &&
data["dynamic"] &&
data["parentID"] == NULL_UUID &&
!data["collisionless"]) {
forceVelocity = true;
}
Entities.editEntity(entityID, deactiveProps);
if (forceVelocity) {
Entities.editEntity(entityID, {velocity:{x:0, y:0.1, z:0}});
}
data = null;
}
} else {
@ -1680,6 +1691,28 @@ function MyController(hand) {
}
setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data);
};
this.checkNewlyLoaded = function(loadedEntityID) {
if (this.state == STATE_OFF ||
this.state == STATE_SEARCHING ||
this.state == STATE_EQUIP_SEARCHING) {
var loadedProps = Entities.getEntityProperties(loadedEntityID);
if (loadedProps.parentID != MyAvatar.sessionUUID) {
return;
}
var handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand");
if (loadedProps.parentJointIndex != handJointIndex) {
return;
}
print("--- handControllerGrab found loaded entity ---");
// an entity has been loaded and it's where this script would have equipped something, so switch states.
this.grabbedEntity = loadedEntityID;
this.activateEntity(this.grabbedEntity, loadedProps, true);
this.isInitialGrab = true;
this.callSetupEntityMethods("startEquip");
this.setState(STATE_CONTINUE_EQUIP);
}
}
};
var rightController = new MyController(RIGHT_HAND);
@ -1711,6 +1744,7 @@ function update() {
Messages.subscribe('Hifi-Hand-Disabler');
Messages.subscribe('Hifi-Hand-Grab');
Messages.subscribe('Hifi-Hand-RayPick-Blacklist');
Messages.subscribe('Hifi-Object-Manipulation');
handleHandMessages = function(channel, message, sender) {
if (sender === MyAvatar.sessionUUID) {
@ -1751,6 +1785,23 @@ handleHandMessages = function(channel, message, sender) {
}
} catch (e) {}
} else if (channel === 'Hifi-Object-Manipulation') {
if (sender !== MyAvatar.sessionUUID) {
return;
}
var parsedMessage = null;
try {
parsedMessage = JSON.parse(message);
} catch (e) {
print('error parsing Hifi-Object-Manipulation message');
return;
}
if (parsedMessage.action === 'loaded') {
rightController.checkNewlyLoaded(parsedMessage['grabbedEntity']);
leftController.checkNewlyLoaded(parsedMessage['grabbedEntity']);
}
}
}
}

View file

@ -139,7 +139,7 @@
this.hand = 'right';
},
startNearGrab: function() {
startEquip: function() {
print('START BOW GRAB')
if (this.isGrabbed === true) {
@ -159,7 +159,7 @@
setEntityCustomData('grabbableKey', this.entityID, data);
},
continueNearGrab: function() {
continueEquip: function() {
this.deltaTime = checkInterval();
//debounce during debugging -- maybe we're updating too fast?
@ -552,4 +552,4 @@
};
return new Bow();
});
});

View file

@ -64,21 +64,22 @@ function makeBow() {
script: SCRIPT_URL,
userData: JSON.stringify({
grabbableKey: {
invertSolidWhileHeld: true,
spatialKey: {
leftRelativePosition: {
x: -0.02,
y: 0.08,
z: 0.09
},
relativePosition: {
x: 0.02,
y: 0.08,
z: 0.09
},
relativeRotation: Quat.fromPitchYawRollDegrees(0, 90, -90)
}
}
invertSolidWhileHeld: true
},
wearable:{joints:{RightHand:[{x:0.03960523009300232,
y:0.01979270577430725,
z:0.03294898942112923},
{x:-0.7257906794548035,
y:-0.4611682891845703,
z:0.4436084032058716,
w:-0.25251442193984985}],
LeftHand:[{x:0.0055799782276153564,
y:0.04354757443070412,
z:0.05119767785072327},
{x:-0.14914104342460632,
y:0.6448180079460144,
z:-0.2888556718826294,
w:-0.6917579770088196}]}}
})
};
@ -147,4 +148,4 @@ function cleanup() {
Entities.deleteEntity(preNotchString);
}
Script.scriptEnding.connect(cleanup);
Script.scriptEnding.connect(cleanup);

View file

@ -46,16 +46,22 @@ var wand = Entities.addEntity({
script: WAND_SCRIPT_URL,
userData: JSON.stringify({
grabbableKey: {
invertSolidWhileHeld: true,
spatialKey: {
relativePosition: {
x: 0,
y: 0.1,
z: 0
},
relativeRotation: Quat.fromPitchYawRollDegrees(0, 0, -90)
}
}
invertSolidWhileHeld: true
},
"wearable":{"joints":{"RightHand":[{"x":0.11421211808919907,
"y":0.06508062779903412,
"z":0.06317152827978134},
{"x":-0.7886992692947388,
"y":-0.6108893156051636,
"z":-0.05003821849822998,
"w":0.047579944133758545}],
"LeftHand":[{"x":0.03530977666378021,
"y":0.11278322339057922,
"z":0.049768272787332535},
{"x":-0.050609711557626724,
"y":-0.11595471203327179,
"z":0.3554558753967285,
"w":0.9260908961296082}]}}
})
});

View file

@ -174,6 +174,9 @@
this.createBubbleAtTipOfWand();
}
},
startEquip: function(id, params) {
this.startNearGrab(id, params);
},
continueNearGrab: function() {
var deltaTime = checkInterval();
//only get the properties that we need
@ -188,11 +191,17 @@
this.growBubbleWithWandVelocity(properties, deltaTime);
},
continueEquip: function() {
this.continueNearGrab();
},
releaseGrab: function() {
//delete the current buble and reset state when the wand is released
Entities.deleteEntity(this.currentBubble);
this.currentBubble = null;
},
releaseEquip: function() {
this.releaseGrab();
},
};

View file

@ -49,6 +49,9 @@
this.isGrabbed = true;
this.initialHand = this.hand;
},
startEquip: function(id, params) {
this.startNearGrab(id, params);
},
continueNearGrab: function() {
var props = Entities.getEntityProperties(this.entityID, ["position"]);
@ -57,6 +60,9 @@
};
this.audioInjector.options = audioOptions;
},
continueEquip: function() {
this.continueNearGrab();
},
releaseGrab: function() {
if (this.isGrabbed === true && this.hand === this.initialHand) {
@ -73,6 +79,9 @@
this.isGrabbed = false;
}
},
releaseEquip: function() {
this.releaseGrab();
},
preload: function(entityID) {
this.entityID = entityID;

View file

@ -39,6 +39,20 @@ var flashlight = Entities.addEntity({
userData: JSON.stringify({
grabbableKey: {
invertSolidWhileHeld: true
}
},
wearable:{joints:{RightHand:[{x:0.0717092975974083,
y:0.1166968047618866,
z:0.07085515558719635},
{x:-0.7195770740509033,
y:0.175227552652359,
z:0.5953742265701294,
w:0.31150275468826294}],
LeftHand:[{x:0.0806504637002945,
y:0.09710478782653809,
z:0.08610185235738754},
{x:0.5630447864532471,
y:-0.2545935809612274,
z:0.7855332493782043,
w:0.033170729875564575}]}}
})
});

View file

@ -86,6 +86,7 @@
},
startNearGrab: function(entityID) {
print("FLASHLIGHT startNearGrab");
if (!this.hasSpotlight) {
var modelProperties = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
@ -144,6 +145,9 @@
}
},
startEquip: function(id, params) {
this.startNearGrab(id, params);
},
setWhichHand: function() {
this.whichHand = this.hand;
@ -157,6 +161,9 @@
this.changeLightWithTriggerPressure(this.whichHand);
}
},
continueEquip: function() {
this.continueNearGrab();
},
releaseGrab: function() {
//delete the lights and reset state
@ -170,6 +177,9 @@
this.lightOn = false;
}
},
releaseEquip: function() {
this.releaseGrab();
},
changeLightWithTriggerPressure: function(flashLightHand) {
@ -258,4 +268,4 @@
// entity scripts always need to return a newly constructed object of our type
return new Flashlight();
});
});

View file

@ -38,16 +38,22 @@ var pingPongGun = Entities.addEntity({
collisionSoundURL: COLLISION_SOUND_URL,
userData: JSON.stringify({
grabbableKey: {
spatialKey: {
relativePosition: {
x: -0.05,
y: 0,
z: 0.0
},
relativeRotation: Quat.fromPitchYawRollDegrees(0, -90, -90)
},
invertSolidWhileHeld: true
}
},
wearable:{joints:{RightHand:[{x:0.1177130937576294,
y:0.12922893464565277,
z:0.08307232707738876},
{x:0.4934672713279724,
y:0.3605862259864807,
z:0.6394805908203125,
w:-0.4664038419723511}],
LeftHand:[{x:0.09151676297187805,
y:0.13639454543590546,
z:0.09354984760284424},
{x:-0.19628101587295532,
y:0.6418180465698242,
z:0.2830369472503662,
w:0.6851521730422974}]}}
})
});

View file

@ -66,7 +66,7 @@
this.hand = 0;
},
startNearGrab: function() {
startEquip: function() {
this.setWhichHand();
},
@ -74,7 +74,7 @@
this.whichHand = this.hand;
},
continueNearGrab: function() {
continueEquip: function() {
if (this.whichHand === null) {
//only set the active hand once -- if we always read the current hand, our 'holding' hand will get overwritten
this.setWhichHand();
@ -86,7 +86,7 @@
}
},
releaseGrab: function() {
releaseEquip: function() {
var _this = this;
if (this.whichHand === this.hand) {

View file

@ -30,16 +30,22 @@ var pistol = Entities.addEntity({
collisionSoundURL: "http://hifi-content.s3.amazonaws.com/james/pistol/sounds/drop.wav",
userData: JSON.stringify({
grabbableKey: {
spatialKey: {
relativePosition: {
x: 0,
y: 0.05,
z: -0.08
},
relativeRotation: Quat.fromPitchYawRollDegrees(90, 90, 0)
},
invertSolidWhileHeld: true
}
},
wearable:{joints:{RightHand:[{x:0.07079616189002991,
y:0.20177987217903137,
z:0.06374628841876984},
{x:-0.5863648653030396,
y:-0.46007341146469116,
z:0.46949487924575806,
w:-0.4733745753765106}],
LeftHand:[{x:0.1802254319190979,
y:0.13442856073379517,
z:0.08504903316497803},
{x:0.2198076844215393,
y:-0.7377811074256897,
z:0.2780133783817291,
w:0.574519157409668}]}}
})
});

View file

@ -52,7 +52,7 @@
this.hand = JSON.parse(params[0]);
},
continueNearGrab: function() {
continueEquip: function() {
if (!this.equipped) {
return;
}
@ -61,8 +61,6 @@
this.updateLaser();
}
this.toggleWithTriggerPressure();
},
updateProps: function() {