change grab script to equip with parenting rather than action. various other changes to messaging. added initial version of attachedEntitiesManager.js. updated toybox scripts to track messaging changes

This commit is contained in:
Seth Alves 2016-01-29 15:17:46 -08:00
parent f554e25e36
commit 9ca89172f6
13 changed files with 610 additions and 225 deletions

View file

@ -0,0 +1,303 @@
//
// 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
// if an entity is currently being held or equipped, its original properties are saved in
// the userData under "grabKey". Save with these original properties rather than the
// ones currently on the entity.
if (grabData.refCount > 0) {
if ("gravity" in grabData) {
props.gravity = grabData.gravity;
}
if ("collidesWith" in grabData) {
props.collidesWith = grabData.collidesWith;
}
if ("dynamic" in grabData) {
props.dynamic = grabData.dynamic;
}
if ("collisionless" in grabData) {
if ("invertSolidWhileHeld" in grabbableData && grabbableData.invertSolidWhileHeld) {
props.collisionless = !grabData.collisionless;
} else {
props.collisionless = grabData.collisionless;
}
}
// if ("parentID" in grabData) {
// props.parentID = grabData.parentID;
// }
// if ("parentJointIndex" in grabData) {
// props.parentJointIndex = grabData.parentJointIndex;
// }
}
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, strip it out
if ("userData" in props) {
try {
parsedUserData = JSON.parse(props.userData);
if ("grabKey" in parsedUserData) {
delete parsedUserData["grabKey"];
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

@ -18,7 +18,7 @@ Script.include("../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;
//
// these tune time-averaging and "on" value for analog trigger
@ -109,7 +109,8 @@ var GRABBABLE_PROPERTIES = [
"rotation",
"gravity",
"collidesWith",
"collisionsWillMove",
"dynamic",
"collisionless",
"locked",
"name",
"shapeType",
@ -228,48 +229,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) {
@ -280,9 +239,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;
@ -363,6 +319,15 @@ 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 (" + this.hand + "): " + stateToName(this.state) + " --> " +
@ -381,7 +346,7 @@ function MyController(hand) {
linePoints: [ZERO_VEC, farPoint],
color: color,
lifetime: 0.1,
collisionsWillMove: false,
dynamic: false,
ignoreForCollisions: true,
userData: JSON.stringify({
grabbableKey: {
@ -403,7 +368,7 @@ function MyController(hand) {
linePoints: [ZERO_VEC, farPoint],
color: color,
lifetime: LIFETIME,
collisionsWillMove: false,
dynamic: false,
ignoreForCollisions: true,
userData: JSON.stringify({
grabbableKey: {
@ -767,6 +732,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);
@ -813,7 +779,7 @@ function MyController(hand) {
direction: pickRay.direction
};
Messages.sendMessage('Hifi-Light-Overlay-Ray-Check', JSON.stringify(pickRayBacked));
// Messages.sendMessage('Hifi-Light-Overlay-Ray-Check', JSON.stringify(pickRayBacked));
var intersection;
@ -861,6 +827,11 @@ 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 (distance < minDistance) {
this.grabbedEntity = candidateEntities[i];
minDistance = distance;
@ -870,7 +841,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
@ -890,11 +861,14 @@ function MyController(hand) {
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);
@ -919,13 +893,17 @@ function MyController(hand) {
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);
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);
}
};
@ -966,14 +944,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;
@ -985,7 +957,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;
}
@ -997,7 +971,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);
@ -1067,7 +1041,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
@ -1167,40 +1141,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;
}
@ -1208,10 +1211,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
});
}
@ -1220,11 +1223,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;
@ -1236,6 +1241,7 @@ 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;
}
}
@ -1247,42 +1253,44 @@ function MyController(hand) {
}
} 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,
localPosition: this.offsetPosition,
localRotation: this.offsetRotation
});
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()) {
@ -1291,11 +1299,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;
}
@ -1314,16 +1326,23 @@ 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");
}
}
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
action: 'update',
grabbedEntity: this.grabbedEntity
}))
// Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
// action: 'update',
// grabbedEntity: this.grabbedEntity
// }))
if (this.actionID && this.actionTimeout - now < ACTION_TTL_REFRESH * MSEC_PER_SEC) {
// if less than a 5 seconds left, refresh the actions ttl
@ -1350,60 +1369,48 @@ function MyController(hand) {
this.waitingForBumperRelease = function() {
if (this.bumperReleased()) {
this.setState(STATE_RELEASE);
Entities.callEntityMethod(this.grabbedEntity, "releaseGrab");
Entities.callEntityMethod(this.grabbedEntity, "unequip");
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", []);
}
}
};
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;
}
@ -1419,7 +1426,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;
}
}
@ -1427,8 +1434,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 = {};
@ -1488,15 +1494,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() {
@ -1506,13 +1513,10 @@ function MyController(hand) {
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,
@ -1524,25 +1528,28 @@ function MyController(hand) {
y: 0,
z: 0
}
})
Entities.deleteAction(this.grabbedEntity, this.actionID);
} else {
//don't make adjustments
Entities.deleteAction(this.grabbedEntity, this.actionID);
});
}
}
}
this.deactivateEntity(this.grabbedEntity);
this.actionID = null;
this.setState(STATE_OFF);
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
action: 'release',
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;
};
@ -1554,19 +1561,30 @@ 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;
if (wasLoaded) {
data["refCount"] = 1;
} 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["collisionsWillMove"] = grabbedProperties.collisionsWillMove;
data["parentID"] = grabbedProperties.parentID;
data["collisionless"] = grabbedProperties.collisionless;
data["dynamic"] = grabbedProperties.dynamic;
data["parentID"] = wasLoaded ? NULL_UUID : grabbedProperties.parentID;
data["parentJointIndex"] = grabbedProperties.parentJointIndex;
if ("invertSolidWhileHeld" in grabbableData && grabbableData.invertSolidWhileHeld) {
data["collisionless"] = !data["collisionless"];
}
var whileHeldProperties = {
gravity: {
x: 0,
@ -1580,6 +1598,7 @@ function MyController(hand) {
};
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
@ -1600,8 +1619,8 @@ function MyController(hand) {
Entities.editEntity(entityID, {
gravity: data["gravity"],
collidesWith: data["collidesWith"],
collisionsWillMove: data["collisionsWillMove"],
ignoreForCollisions: data["ignoreForCollisions"],
collisionless: data["collisionless"],
dynamic: data["dynamic"],
parentID: data["parentID"],
parentJointIndex: data["parentJointIndex"]
});
@ -1617,7 +1636,7 @@ function MyController(hand) {
if (this.state == STATE_OFF ||
this.state == STATE_SEARCHING ||
this.state == STATE_EQUIP_SEARCHING) {
var loadedProps = Entities.getEntityProperties(loadedEntityID, ["parentID", "parentJointIndex"]);
var loadedProps = Entities.getEntityProperties(loadedEntityID);
if (loadedProps.parentID != MyAvatar.sessionUUID) {
return;
}
@ -1625,9 +1644,13 @@ function MyController(hand) {
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.setState(STATE_EQUIP);
this.activateEntity(this.grabbedEntity, loadedProps, true);
this.isInitialGrab = true;
this.callSetupEntityMethods("startEquip");
this.setState(STATE_CONTINUE_EQUIP);
}
}
};

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() {