Update grab script to handle touching, better naming for non colliding grab functions

This commit is contained in:
James Pollack 2015-09-23 16:56:46 -07:00
parent ba44390f79
commit 381a24951e
2 changed files with 243 additions and 94 deletions

View file

@ -8,11 +8,10 @@
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */
Script.include("../libraries/utils.js");
/////////////////////////////////////////////////////////////////
//
// these tune time-averaging and "on" value for analog trigger
@ -110,40 +109,40 @@ function controller(hand, triggerAction) {
this.state = 0;
this.pointer = null; // entity-id of line object
this.triggerValue = 0; // rolling average of trigger value
this.update = function() {
var _this = this;
this.update = function () {
switch (this.state) {
case STATE_SEARCHING:
this.search();
break;
case STATE_DISTANCE_HOLDING:
this.distanceHolding();
break;
case STATE_CONTINUE_DISTANCE_HOLDING:
this.continueDistanceHolding();
break;
case STATE_NEAR_GRABBING:
this.nearGrabbing();
break;
case STATE_CONTINUE_NEAR_GRABBING:
this.continueNearGrabbing();
break;
case STATE_NEAR_GRABBING_NON_COLLIDING:
this.nearGrabbingNonColliding();
break;
case STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING:
this.continueNearGrabbingNonColliding();
break;
case STATE_RELEASE:
this.release();
break;
case STATE_SEARCHING:
this.search();
this.touchTest();
break;
case STATE_DISTANCE_HOLDING:
this.distanceHolding();
break;
case STATE_CONTINUE_DISTANCE_HOLDING:
this.continueDistanceHolding();
break;
case STATE_NEAR_GRABBING:
this.nearGrabbing();
break;
case STATE_CONTINUE_NEAR_GRABBING:
this.continueNearGrabbing();
break;
case STATE_NEAR_GRABBING_NON_COLLIDING:
this.nearGrabbingNonColliding();
break;
case STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING:
this.continueNearGrabbingNonColliding();
break;
case STATE_RELEASE:
this.release();
break;
}
}
this.lineOn = function(closePoint, farPoint, color) {
};
_this.pointerIDs = [];
this.lineOn = function (closePoint, farPoint, color) {
// draw a line
if (this.pointer == null) {
if (this.pointer === null) {
this.pointer = Entities.addEntity({
type: "Line",
name: "pointer",
@ -154,6 +153,7 @@ function controller(hand, triggerAction) {
color: color,
lifetime: LIFETIME
});
_this.pointerIDs.push(this.pointer);
} else {
Entities.editEntity(this.pointer, {
position: closePoint,
@ -162,33 +162,40 @@ function controller(hand, triggerAction) {
lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME
});
}
}
};
this.lineOff = function() {
if (this.pointer != null) {
this.lineOff = function () {
if (this.pointer !== null) {
Entities.deleteEntity(this.pointer);
}
var index = _this.pointerIDs.indexOf(this.pointer);
if (index > -1) {
_this.pointerIDs.splice(index, 1);
}
this.pointer = null;
}
};
this.triggerSmoothedSqueezed = function() {
this.triggerSmoothedSqueezed = function () {
var triggerValue = Controller.getActionValue(this.triggerAction);
// smooth out trigger value
this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) +
(triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO));
return this.triggerValue > TRIGGER_ON_VALUE;
}
};
this.triggerSqueezed = function() {
this.triggerSqueezed = function () {
var triggerValue = Controller.getActionValue(this.triggerAction);
return triggerValue > TRIGGER_ON_VALUE;
}
};
this.search = function () {
this.search = function() {
if (!this.triggerSmoothedSqueezed()) {
this.state = STATE_RELEASE;
return;
@ -220,10 +227,10 @@ function controller(hand, triggerAction) {
// forward ray test failed, try sphere test.
var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS);
var minDistance = GRAB_RADIUS;
var grabbedEntity = null;
for (var i = 0; i < nearbyEntities.length; i++) {
var props = Entities.getEntityProperties(nearbyEntities[i], ["position", "name", "collisionsWillMove", "locked"]);
var distance = Vec3.distance(props.position, handPosition);
var i, props, distance;
for (i = 0; i < nearbyEntities.length; i++) {
props = Entities.getEntityProperties(nearbyEntities[i], ["position", "name", "collisionsWillMove", "locked"]);
distance = Vec3.distance(props.position, handPosition);
if (distance < minDistance && props.name !== "pointer") {
this.grabbedEntity = nearbyEntities[i];
minDistance = distance;
@ -238,10 +245,11 @@ function controller(hand, triggerAction) {
this.state = STATE_NEAR_GRABBING_NON_COLLIDING;
}
}
}
};
this.distanceHolding = function() {
this.distanceHolding = function () {
var handControllerPosition = Controller.getSpatialControlPosition(this.palm);
var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm));
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation"]);
@ -259,25 +267,26 @@ function controller(hand, triggerAction) {
targetRotation: this.currentObjectRotation,
angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME
});
if (this.actionID == NULL_ACTION_ID) {
if (this.actionID === NULL_ACTION_ID) {
this.actionID = null;
}
if (this.actionID != null) {
if (this.actionID !== null) {
this.state = STATE_CONTINUE_DISTANCE_HOLDING;
this.activateEntity(this.grabbedEntity);
Entities.callEntityMethod(this.grabbedEntity, "startDistantGrab");
if (this.hand === RIGHT_HAND) {
Entities.callEntityMethod(this.grabbedEntity, "setRightHand");
} else {
Entities.callEntityMethod(this.grabbedEntity, "setLeftHand");
}
}
}
Entities.callEntityMethod(this.grabbedEntity, "startDistantGrab");
this.continueDistanceHolding = function() {
};
this.continueDistanceHolding = function () {
if (!this.triggerSmoothedSqueezed()) {
this.state = STATE_RELEASE;
return;
@ -323,10 +332,10 @@ function controller(hand, triggerAction) {
targetRotation: this.currentObjectRotation,
angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME
});
}
};
this.nearGrabbing = function() {
this.nearGrabbing = function () {
if (!this.triggerSmoothedSqueezed()) {
this.state = STATE_RELEASE;
return;
@ -344,51 +353,34 @@ function controller(hand, triggerAction) {
var objectRotation = grabbedProperties.rotation;
var offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation);
currentObjectPosition = grabbedProperties.position;
var currentObjectPosition = grabbedProperties.position;
var offset = Vec3.subtract(currentObjectPosition, handPosition);
var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset);
this.actionID = Entities.addAction("hold", this.grabbedEntity, {
hand: this.hand == RIGHT_HAND ? "right" : "left",
hand: this.hand === RIGHT_HAND ? "right" : "left",
timeScale: NEAR_GRABBING_ACTION_TIMEFRAME,
relativePosition: offsetPosition,
relativeRotation: offsetRotation
});
if (this.actionID == NULL_ACTION_ID) {
if (this.actionID === NULL_ACTION_ID) {
this.actionID = null;
} else {
this.state = STATE_CONTINUE_NEAR_GRABBING;
Entities.callEntityMethod(this.grabbedEntity, "startNearGrab");
if (this.hand === RIGHT_HAND) {
Entities.callEntityMethod(this.grabbedEntity, "setRightHand");
} else {
Entities.callEntityMethod(this.grabbedEntity, "setLeftHand");
}
Entities.callEntityMethod(this.grabbedEntity, "startNearGrab");
}
this.currentHandControllerPosition = Controller.getSpatialControlPosition(this.palm);
this.currentObjectTime = Date.now();
}
};
this.nearGrabbingNonColliding = function() {
if (!this.triggerSmoothedSqueezed()) {
this.state = STATE_RELEASE;
return;
}
Entities.callEntityMethod(this.grabbedEntity, "startNearGrabNonColliding")
this.state = STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING;
}
this.continueNearGrabNonColliding = function() {
if (!this.triggerSmoothedSqueezed()) {
this.state = STATE_RELEASE;
return;
}
Entities.callEntityMethod(this.grabbedEntity, "continueNearGrabNonColliding");
}
this.continueNearGrabbing = function() {
this.continueNearGrabbing = function () {
if (!this.triggerSmoothedSqueezed()) {
this.state = STATE_RELEASE;
return;
@ -405,10 +397,96 @@ function controller(hand, triggerAction) {
this.currentHandControllerPosition = handControllerPosition;
this.currentObjectTime = now;
Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab");
}
};
this.nearGrabbingNonColliding = function () {
if (!this.triggerSmoothedSqueezed()) {
this.state = STATE_RELEASE;
return;
}
Entities.callEntityMethod(this.grabbedEntity, "startNearGrabNonColliding");
this.state = STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING;
};
this.computeReleaseVelocity = function(deltaPosition, deltaTime, useMultiplier) {
this.continueNearGrabbingNonColliding = function () {
if (!this.triggerSmoothedSqueezed()) {
this.state = STATE_RELEASE;
return;
}
Entities.callEntityMethod(this.grabbedEntity, "continueNearGrabbingNonColliding");
};
_this.allTouchedIDs = {};
this.touchTest = function () {
//print('touch test');
var maxDistance = 0.05;
var leftHandPosition = MyAvatar.getLeftPalmPosition();
var rightHandPosition = MyAvatar.getRightPalmPosition();
var leftEntities = Entities.findEntities(leftHandPosition, maxDistance);
var rightEntities = Entities.findEntities(rightHandPosition, maxDistance);
var ids = [];
if (leftEntities.length !== 0) {
leftEntities.forEach(function (entity) {
ids.push(entity);
});
}
if (rightEntities.length !== 0) {
rightEntities.forEach(function (entity) {
ids.push(entity);
});
}
ids.forEach(function (id) {
var props = Entities.getEntityProperties(id, ["boundingBox", "name"]);
if (props.name === 'pointer') {
return;
} else {
var entityMinPoint = props.boundingBox.brn;
var entityMaxPoint = props.boundingBox.tfl;
var leftIsTouching = pointInExtents(leftHandPosition, entityMinPoint, entityMaxPoint);
var rightIsTouching = pointInExtents(rightHandPosition, entityMinPoint, entityMaxPoint);
if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id] === undefined) {
// we haven't been touched before, but either right or left is touching us now
_this.allTouchedIDs[id] = true;
_this.startTouch(id);
} else if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id] === true) {
// we have been touched before and are still being touched
// continue touch
_this.continueTouch(id);
} else if (_this.allTouchedIDs[id] === true) {
delete _this.allTouchedIDs[id];
_this.stopTouch(id);
} else {
//we are in another state
return;
}
}
});
};
this.startTouch = function (entityID) {
// print('START TOUCH' + entityID);
Entities.callEntityMethod(entityID, "startTouch");
};
this.continueTouch = function (entityID) {
// print('CONTINUE TOUCH' + entityID);
Entities.callEntityMethod(entityID, "continueTouch");
};
this.stopTouch = function (entityID) {
// print('STOP TOUCH' + entityID);
Entities.callEntityMethod(entityID, "stopTouch");
};
this.computeReleaseVelocity = function (deltaPosition, deltaTime, useMultiplier) {
if (deltaTime > 0.0 && !vec3equal(deltaPosition, ZERO_VEC)) {
var grabbedVelocity = Vec3.multiply(deltaPosition, 1.0 / deltaTime);
// don't update grabbedVelocity if the trigger is off. the smoothing of the trigger
@ -423,13 +501,13 @@ function controller(hand, triggerAction) {
this.grabbedVelocity = Vec3.multiply(this.grabbedVelocity, RELEASE_VELOCITY_MULTIPLIER);
}
}
}
};
this.release = function() {
this.release = function () {
this.lineOff();
if (this.grabbedEntity != null && this.actionID != null) {
if (this.grabbedEntity !== null && this.actionID !== null) {
Entities.deleteAction(this.grabbedEntity, this.actionID);
Entities.callEntityMethod(this.grabbedEntity, "releaseGrab");
}
@ -445,28 +523,28 @@ function controller(hand, triggerAction) {
this.grabbedEntity = null;
this.actionID = null;
this.state = STATE_SEARCHING;
}
};
this.cleanup = function() {
release();
}
this.cleanup = function () {
this.release();
};
this.activateEntity = function(entity) {
this.activateEntity = function () {
var data = {
activated: true,
avatarId: MyAvatar.sessionUUID
};
setEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, data);
}
};
this.deactivateEntity = function(entity) {
this.deactivateEntity = function () {
var data = {
activated: false,
avatarId: null
};
setEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, data);
}
};
}
@ -487,4 +565,4 @@ function cleanup() {
Script.scriptEnding.connect(cleanup);
Script.update.connect(update)
Script.update.connect(update);

View file

@ -0,0 +1,71 @@
//
// changeColorOnTouch.js
// examples/entityScripts
//
// Created by Brad Hefta-Gaub on 11/1/14.
// Additions by James B. Pollack @imgntn on 9/23/2015
// Copyright 2014 High Fidelity, Inc.
//
// ATTENTION: Requires you to run handControllerGrab.js
// This is an example of an entity script which when assigned to a non-model entity like a box or sphere, will
// change the color of the entity when you touch it.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function () {
ChangeColorOnTouch = function () {
this.oldColor = {};
this.oldColorKnown = false;
};
ChangeColorOnTouch.prototype = {
storeOldColor: function (entityID) {
var oldProperties = Entities.getEntityProperties(entityID);
this.oldColor = oldProperties.color;
this.oldColorKnown = true;
print("storing old color... this.oldColor=" + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue);
},
preload: function (entityID) {
print("preload");
this.entityID = entityID;
this.storeOldColor(entityID);
},
startTouch: function () {
print("startTouch");
if (!this.oldColorKnown) {
this.storeOldColor(this.entityID);
}
Entities.editEntity(this.entityID, {
color: {
red: 0,
green: 255,
blue: 255
}
});
},
continueTouch: function () {
//unused here
return;
},
stopTouch: function () {
print("stopTouch");
if (this.oldColorKnown) {
print("leave restoring old color... this.oldColor=" + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue);
Entities.editEntity(this.entityID, {
color: this.oldColor
});
}
}
};
return new ChangeColorOnTouch();
})