Merge pull request #8273 from hyperlogic/bug-fix/far-grab-zoom

far-grabbed objects no longer fly away when the avatar body shifts
This commit is contained in:
Brad Hefta-Gaub 2016-07-19 10:40:48 -07:00 committed by GitHub
commit 866ba2f185
2 changed files with 29 additions and 167 deletions

View file

@ -1587,13 +1587,16 @@ function MyController(hand) {
this.clearEquipHaptics();
// controller pose is in avatar frame
var avatarControllerPose =
Controller.getPoseValue((this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand);
var device = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
var avatarControllerPose = Controller.getPoseValue(device);
// transform it into world frame
var controllerPositionVSAvatar = Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation);
var controllerPosition = Vec3.sum(MyAvatar.position, controllerPositionVSAvatar);
var controllerRotation = Quat.multiply(MyAvatar.orientation, avatarControllerPose.rotation);
var worldControllerPosition = Vec3.sum(MyAvatar.position,
Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation));
// also transform the position into room space
var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix());
var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition);
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
var now = Date.now();
@ -1604,7 +1607,7 @@ function MyController(hand) {
this.currentObjectTime = now;
this.currentCameraOrientation = Camera.orientation;
this.grabRadius = Vec3.distance(this.currentObjectPosition, controllerPosition);
this.grabRadius = Vec3.distance(this.currentObjectPosition, worldControllerPosition);
this.grabRadialVelocity = 0.0;
// compute a constant based on the initial conditions which we use below to exagerate hand motion onto the held object
@ -1639,8 +1642,7 @@ function MyController(hand) {
this.turnOffVisualizations();
this.previousControllerPositionVSAvatar = controllerPositionVSAvatar;
this.previousControllerRotation = controllerRotation;
this.previousRoomControllerPosition = roomControllerPosition;
};
this.distanceHolding = function (deltaTime, timestamp) {
@ -1653,13 +1655,17 @@ function MyController(hand) {
this.heartBeat(this.grabbedEntity);
// controller pose is in avatar frame
var avatarControllerPose = Controller.getPoseValue((this.hand === RIGHT_HAND) ?
Controller.Standard.RightHand : Controller.Standard.LeftHand);
var device = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
var avatarControllerPose = Controller.getPoseValue(device);
// transform it into world frame
var controllerPositionVSAvatar = Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation);
var controllerPosition = Vec3.sum(MyAvatar.position, controllerPositionVSAvatar);
var controllerRotation = Quat.multiply(MyAvatar.orientation, avatarControllerPose.rotation);
var worldControllerPosition = Vec3.sum(MyAvatar.position,
Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation));
var worldControllerRotation = Quat.multiply(MyAvatar.orientation, avatarControllerPose.rotation);
// also transform the position into room space
var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix());
var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition);
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
@ -1668,26 +1674,16 @@ function MyController(hand) {
this.currentObjectTime = now;
// the action was set up when this.distanceHolding was called. update the targets.
var radius = Vec3.distance(this.currentObjectPosition, controllerPosition) *
var radius = Vec3.distance(this.currentObjectPosition, worldControllerPosition) *
this.radiusScalar * DISTANCE_HOLDING_RADIUS_FACTOR;
if (radius < 1.0) {
radius = 1.0;
}
// scale delta controller hand movement by radius.
var handMoved = Vec3.multiply(Vec3.subtract(controllerPositionVSAvatar, this.previousControllerPositionVSAvatar),
radius);
/// double delta controller rotation
// var DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR = 2.0; // object rotates this much more than hand did
// var handChange = Quat.multiply(Quat.slerp(this.previousControllerRotation,
// controllerRotation,
// DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR),
// Quat.inverse(this.previousControllerRotation));
// update the currentObject position and rotation.
var roomHandDelta = Vec3.subtract(roomControllerPosition, this.previousRoomControllerPosition);
var worldHandDelta = Mat4.transformVector(MyAvatar.getSensorToWorldMatrix(), roomHandDelta);
var handMoved = Vec3.multiply(worldHandDelta, radius);
this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, handMoved);
// this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation);
this.callEntityMethodOnGrabbed("continueDistantGrab");
@ -1698,10 +1694,9 @@ function MyController(hand) {
var handControllerData = getEntityCustomData('handControllerKey', this.grabbedEntity, defaultMoveWithHeadData);
// Update radialVelocity
var lastVelocity = Vec3.subtract(controllerPositionVSAvatar, this.previousControllerPositionVSAvatar);
lastVelocity = Vec3.multiply(lastVelocity, 1.0 / deltaObjectTime);
var newRadialVelocity = Vec3.dot(lastVelocity,
Vec3.normalize(Vec3.subtract(grabbedProperties.position, controllerPosition)));
var lastVelocity = Vec3.multiply(worldHandDelta, 1.0 / deltaObjectTime);
var delta = Vec3.normalize(Vec3.subtract(grabbedProperties.position, worldControllerPosition));
var newRadialVelocity = Vec3.dot(lastVelocity, delta);
var VELOCITY_AVERAGING_TIME = 0.016;
this.grabRadialVelocity = (deltaObjectTime / VELOCITY_AVERAGING_TIME) * newRadialVelocity +
@ -1713,9 +1708,8 @@ function MyController(hand) {
this.grabRadius * RADIAL_GRAB_AMPLIFIER);
}
var newTargetPosition = Vec3.multiply(this.grabRadius, Quat.getUp(controllerRotation));
newTargetPosition = Vec3.sum(newTargetPosition, controllerPosition);
var newTargetPosition = Vec3.multiply(this.grabRadius, Quat.getUp(worldControllerRotation));
newTargetPosition = Vec3.sum(newTargetPosition, worldControllerPosition);
var objectToAvatar = Vec3.subtract(this.currentObjectPosition, MyAvatar.position);
if (handControllerData.disableMoveWithHead !== true) {
@ -1771,8 +1765,7 @@ function MyController(hand) {
print("continueDistanceHolding -- updateAction failed");
}
this.previousControllerPositionVSAvatar = controllerPositionVSAvatar;
this.previousControllerRotation = controllerRotation;
this.previousRoomControllerPosition = roomControllerPosition;
};
this.setupHoldAction = function () {

View file

@ -1,131 +0,0 @@
//
// handControllerMouse.js
// examples/controllers
//
// Created by Brad Hefta-Gaub on 2015/12/15
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var DEBUGGING = false;
var angularVelocityTrailingAverage = 0.0; // Global trailing average used to decide whether to move reticle at all
var lastX = 0;
var lastY = 0;
Math.clamp=function(a,b,c) {
return Math.max(b,Math.min(c,a));
}
function length(posA, posB) {
var dx = posA.x - posB.x;
var dy = posA.y - posB.y;
var length = Math.sqrt((dx*dx) + (dy*dy))
return length;
}
function moveReticleAbsolute(x, y) {
var globalPos = Reticle.getPosition();
globalPos.x = x;
globalPos.y = y;
Reticle.setPosition(globalPos);
}
var MAPPING_NAME = "com.highfidelity.testing.reticleWithHandRotation";
var mapping = Controller.newMapping(MAPPING_NAME);
if (Controller.Hardware.Hydra !== undefined) {
mapping.from(Controller.Hardware.Hydra.L3).peek().to(Controller.Actions.ReticleClick);
mapping.from(Controller.Hardware.Hydra.R4).peek().to(Controller.Actions.ReticleClick);
}
if (Controller.Hardware.Vive !== undefined) {
mapping.from(Controller.Hardware.Vive.LeftPrimaryThumb).peek().to(Controller.Actions.ReticleClick);
mapping.from(Controller.Hardware.Vive.RightPrimaryThumb).peek().to(Controller.Actions.ReticleClick);
}
mapping.enable();
function debugPrint(message) {
if (DEBUGGING) {
print(message);
}
}
var leftRightBias = 0.0;
var filteredRotatedLeft = Vec3.UNIT_NEG_Y;
var filteredRotatedRight = Vec3.UNIT_NEG_Y;
var lastAlpha = 0;
Script.update.connect(function(deltaTime) {
// avatar frame
var poseRight = Controller.getPoseValue(Controller.Standard.RightHand);
var poseLeft = Controller.getPoseValue(Controller.Standard.LeftHand);
// NOTE: hack for now
var screenSize = Reticle.maximumPosition;
var screenSizeX = screenSize.x;
var screenSizeY = screenSize.y;
// transform hand facing vectors from avatar frame into sensor frame.
var worldToSensorMatrix = Mat4.inverse(MyAvatar.sensorToWorldMatrix);
var rotatedRight = Mat4.transformVector(worldToSensorMatrix, Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.multiplyQbyV(poseRight.rotation, Vec3.UNIT_NEG_Y)));
var rotatedLeft = Mat4.transformVector(worldToSensorMatrix, Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.multiplyQbyV(poseLeft.rotation, Vec3.UNIT_NEG_Y)));
lastRotatedRight = rotatedRight;
// Decide which hand should be controlling the pointer
// by comparing which one is moving more, and by
// tending to stay with the one moving more.
if (deltaTime > 0.001) {
// leftRightBias is a running average of the difference in angular hand speed.
// a positive leftRightBias indicates the right hand is spinning faster then the left hand.
// a negative leftRightBias indicates the left hand is spnning faster.
var BIAS_ADJUST_PERIOD = 1.0;
var tau = Math.clamp(deltaTime / BIAS_ADJUST_PERIOD, 0, 1);
newLeftRightBias = Vec3.length(poseRight.angularVelocity) - Vec3.length(poseLeft.angularVelocity);
leftRightBias = (1 - tau) * leftRightBias + tau * newLeftRightBias;
}
// add a bit of hysteresis to prevent control flopping back and forth
// between hands when they are both mostly stationary.
var alpha;
var HYSTERESIS_OFFSET = 0.25;
if (lastAlpha > 0.5) {
// prefer right hand over left
alpha = leftRightBias > -HYSTERESIS_OFFSET ? 1 : 0;
} else {
alpha = leftRightBias > HYSTERESIS_OFFSET ? 1 : 0;
}
lastAlpha = alpha;
// Velocity filter the hand rotation used to position reticle so that it is easier to target small things with the hand controllers
var VELOCITY_FILTER_GAIN = 0.5;
filteredRotatedLeft = Vec3.mix(filteredRotatedLeft, rotatedLeft, Math.clamp(Vec3.length(poseLeft.angularVelocity) * VELOCITY_FILTER_GAIN, 0.0, 1.0));
filteredRotatedRight = Vec3.mix(filteredRotatedRight, rotatedRight, Math.clamp(Vec3.length(poseRight.angularVelocity) * VELOCITY_FILTER_GAIN, 0.0, 1.0));
var rotated = Vec3.mix(filteredRotatedLeft, filteredRotatedRight, alpha);
var absolutePitch = rotated.y; // from 1 down to -1 up ... but note: if you rotate down "too far" it starts to go up again...
var absoluteYaw = -rotated.x; // from -1 left to 1 right
var x = Math.clamp(screenSizeX * (absoluteYaw + 0.5), 0, screenSizeX);
var y = Math.clamp(screenSizeX * absolutePitch, 0, screenSizeY);
// don't move the reticle with the hand controllers unless the controllers are actually being moved
// take a time average of angular velocity, and don't move mouse at all if it's below threshold
var AVERAGING_INTERVAL = 0.95;
var MINIMUM_CONTROLLER_ANGULAR_VELOCITY = 0.03;
var angularVelocityMagnitude = Vec3.length(poseLeft.angularVelocity) * (1.0 - alpha) + Vec3.length(poseRight.angularVelocity) * alpha;
angularVelocityTrailingAverage = angularVelocityTrailingAverage * AVERAGING_INTERVAL + angularVelocityMagnitude * (1.0 - AVERAGING_INTERVAL);
if ((angularVelocityTrailingAverage > MINIMUM_CONTROLLER_ANGULAR_VELOCITY) && ((x != lastX) || (y != lastY))) {
moveReticleAbsolute(x, y);
lastX = x;
lastY = y;
}
});
Script.scriptEnding.connect(function(){
mapping.disable();
});