diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 7d4380d764..82fd847f25 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -4,7 +4,7 @@ // Created by Eric Levin on 9/2/15 // Copyright 2015 High Fidelity, Inc. // -// Grab's physically moveable entities with the hydra- works for either near or far objects. User can also grab a far away object and drag it towards them by pressing the "4" button on either the left or ride controller. +// Grabs physically moveable entities with hydra-like controllers; it works for either near or far objects. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -13,6 +13,8 @@ Script.include("../libraries/utils.js"); +var RADIUS_FACTOR = 4; + var RIGHT_HAND_CLICK = Controller.findAction("RIGHT_HAND_CLICK"); var rightTriggerAction = RIGHT_HAND_CLICK; @@ -45,7 +47,7 @@ var INTERSECT_COLOR = { blue: 10 }; -var GRAB_RADIUS = 1.5; +var GRAB_RADIUS = 0.3; var GRAB_COLOR = { red: 250, @@ -58,12 +60,11 @@ var DISTANCE_HOLD_THRESHOLD = 0.8; var right4Action = 18; var left4Action = 17; -var TRACTOR_BEAM_VELOCITY_THRESHOLD = 0.5; - var RIGHT = 1; var LEFT = 0; var rightController = new controller(RIGHT, rightTriggerAction, right4Action, "right"); var leftController = new controller(LEFT, leftTriggerAction, left4Action, "left"); +var startTime = Date.now(); //Need to wait before calling these methods for some reason... @@ -78,56 +79,67 @@ function controller(side, triggerAction, pullAction, hand) { this.getHandPosition = MyAvatar.getRightPalmPosition; this.getHandRotation = MyAvatar.getRightPalmRotation; } else { - this.getHandPosition = MyAvatar.getLeftPalmPosition; this.getHandRotation = MyAvatar.getLeftPalmRotation; } this.triggerAction = triggerAction; this.pullAction = pullAction; this.actionID = null; - this.tractorBeamActive = false; this.distanceHolding = false; this.closeGrabbing = false; this.triggerValue = 0; this.prevTriggerValue = 0; this.palm = 2 * side; this.tip = 2 * side + 1; - this.pointer = Entities.addEntity({ - type: "Line", - name: "pointer", - color: NO_INTERSECT_COLOR, - dimensions: { - x: 1000, - y: 1000, - z: 1000 - }, - visible: false, - lifetime: LIFETIME - }); - + this.pointer = null; } controller.prototype.updateLine = function() { - var handPosition = Controller.getSpatialControlPosition(this.palm); - var direction = Controller.getSpatialControlNormal(this.tip); + if (this.pointer != null) { + if (Entities.getEntityProperties(this.pointer).id != this.poitner) { + this.pointer == null; + } + } - Entities.editEntity(this.pointer, { - position: handPosition, - linePoints: [ - ZERO_VEC, - Vec3.multiply(direction, LINE_LENGTH) - ] - }); + if (this.pointer == null) { + this.lineCreationTime = Date.now(); + this.pointer = Entities.addEntity({ + type: "Line", + name: "pointer", + color: NO_INTERSECT_COLOR, + dimensions: { + x: 1000, + y: 1000, + z: 1000 + }, + visible: true, + lifetime: LIFETIME + }); + } + + + var handPosition = this.getHandPosition(); + var direction = Quat.getUp(this.getHandRotation()); //only check if we havent already grabbed an object if (this.distanceHolding) { + Entities.editEntity(this.pointer, { + position: handPosition, + linePoints: [ ZERO_VEC, Vec3.subtract(Entities.getEntityProperties(this.grabbedEntity).position, handPosition) ], + lifetime: (Date.now() - startTime) / 1000.0 + LIFETIME + }); + return; } - //move origin a bit away from hand so nothing gets in way - var origin = Vec3.sum(handPosition, direction); - if (this.checkForIntersections(origin, direction)) { + Entities.editEntity(this.pointer, { + position: handPosition, + linePoints: [ ZERO_VEC, Vec3.multiply(direction, LINE_LENGTH) ], + lifetime: (Date.now() - startTime) / 1000.0 + LIFETIME + }); + + if (this.checkForIntersections(handPosition, direction)) { Entities.editEntity(this.pointer, { color: INTERSECT_COLOR, }); @@ -144,7 +156,7 @@ controller.prototype.checkPointer = function() { Script.setTimeout(function() { var props = Entities.getEntityProperties(self.pointer); Entities.editEntity(self.pointer, { - lifetime: props.age + EXTRA_TIME + LIFETIME + lifetime: (Date.now() - startTime) / 1000.0 + LIFETIME }); self.checkPointer(); }, POINTER_CHECK_TIME); @@ -172,28 +184,54 @@ controller.prototype.checkForIntersections = function(origin, direction) { return false; } + controller.prototype.attemptMove = function() { - if (this.tractorBeamActive) { - return; - } if (this.grabbedEntity || this.distanceHolding) { var handPosition = Controller.getSpatialControlPosition(this.palm); - var direction = Controller.getSpatialControlNormal(this.tip); + var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm)); - var newPosition = Vec3.sum(handPosition, Vec3.multiply(direction, this.distanceToEntity)) this.distanceHolding = true; if (this.actionID === null) { + this.currentObjectPosition = Entities.getEntityProperties(this.grabbedEntity).position; + this.currentObjectRotation = Entities.getEntityProperties(this.grabbedEntity).rotation; + + this.handPreviousPosition = handPosition; + this.handPreviousRotation = handRotation; + this.actionID = Entities.addAction("spring", this.grabbedEntity, { - targetPosition: newPosition, - linearTimeScale: .1 + targetPosition: this.currentObjectPosition, + linearTimeScale: .1, + targetRotation: this.currentObjectRotation, + angularTimeScale: .1 }); } else { + var radius = Math.max(Vec3.distance(this.currentObjectPosition, handPosition) * RADIUS_FACTOR, 1.0); + + var handMoved = Vec3.subtract(handPosition, this.handPreviousPosition); + this.handPreviousPosition = handPosition; + var superHandMoved = Vec3.multiply(handMoved, radius); + this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, superHandMoved); + + // ---------------- this tracks hand rotation + // var handChange = Quat.multiply(handRotation, Quat.inverse(this.handPreviousRotation)); + // this.handPreviousRotation = handRotation; + // this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); + // ---------------- + + // ---------------- this doubles hand rotation + var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, handRotation, 2.0), + Quat.inverse(this.handPreviousRotation)); + this.handPreviousRotation = handRotation; + this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); + // ---------------- + + Entities.updateAction(this.grabbedEntity, this.actionID, { - targetPosition: newPosition + targetPosition: this.currentObjectPosition, linearTimeScale: .1, + targetRotation: this.currentObjectRotation, angularTimeScale: .1 }); } } - } controller.prototype.showPointer = function() { @@ -218,19 +256,10 @@ controller.prototype.letGo = function() { this.grabbedEntity = null; this.actionID = null; this.distanceHolding = false; - this.tractorBeamActive = false; - this.checkForEntityArrival = false; this.closeGrabbing = false; } controller.prototype.update = function() { - if (this.tractorBeamActive && this.checkForEntityArrival) { - var entityVelocity = Entities.getEntityProperties(this.grabbedEntity).velocity - if (Vec3.length(entityVelocity) < TRACTOR_BEAM_VELOCITY_THRESHOLD) { - this.letGo(); - } - return; - } this.triggerValue = Controller.getActionValue(this.triggerAction); if (this.triggerValue > SHOW_LINE_THRESHOLD && this.prevTriggerValue < SHOW_LINE_THRESHOLD) { //First check if an object is within close range and then run the close grabbing logic @@ -253,7 +282,6 @@ controller.prototype.update = function() { this.attemptMove(); } - this.prevTriggerValue = this.triggerValue; } @@ -331,27 +359,6 @@ controller.prototype.deactivateEntity = function(entity) { setEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, data); } -controller.prototype.onActionEvent = function(action, state) { - if (this.pullAction === action && state === 1) { - if (this.actionID !== null) { - var self = this; - this.tractorBeamActive = true; - //We need to wait a bit before checking for entity arrival at target destination (meaning checking for velocity being close to some - //low threshold) because otherwise we'll think the entity has arrived before its even really gotten moving! - Script.setTimeout(function() { - self.checkForEntityArrival = true; - }, 500); - var handPosition = Controller.getSpatialControlPosition(this.palm); - var direction = Vec3.normalize(Controller.getSpatialControlNormal(this.tip)); - //move final destination along line a bit, so it doesnt hit avatar hand - Entities.updateAction(this.grabbedEntity, this.actionID, { - targetPosition: Vec3.sum(handPosition, Vec3.multiply(3, direction)) - }); - } - } - -} - controller.prototype.cleanup = function() { Entities.deleteEntity(this.pointer); if (this.grabbedEntity) { @@ -364,13 +371,6 @@ function update() { leftController.update(); } -function onActionEvent(action, state) { - rightController.onActionEvent(action, state); - leftController.onActionEvent(action, state); - -} - - function cleanup() { rightController.cleanup(); leftController.cleanup(); @@ -378,4 +378,3 @@ function cleanup() { Script.scriptEnding.connect(cleanup); Script.update.connect(update) -Controller.actionEvent.connect(onActionEvent); \ No newline at end of file