From 381a24951e68ccff1b4ce52bdd350cd20be70dea Mon Sep 17 00:00:00 2001 From: James Pollack Date: Wed, 23 Sep 2015 16:56:46 -0700 Subject: [PATCH] Update grab script to handle touching, better naming for non colliding grab functions --- examples/controllers/handControllerGrab.js | 266 ++++++++++++------- examples/entityScripts/changeColorOnTouch.js | 71 +++++ 2 files changed, 243 insertions(+), 94 deletions(-) create mode 100644 examples/entityScripts/changeColorOnTouch.js diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index bb806e14f1..5818815bb2 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -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) \ No newline at end of file +Script.update.connect(update); \ No newline at end of file diff --git a/examples/entityScripts/changeColorOnTouch.js b/examples/entityScripts/changeColorOnTouch.js new file mode 100644 index 0000000000..b3082fa9d5 --- /dev/null +++ b/examples/entityScripts/changeColorOnTouch.js @@ -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(); +}) \ No newline at end of file