mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-11 09:04:24 +02:00
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:
commit
866ba2f185
2 changed files with 29 additions and 167 deletions
|
@ -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 () {
|
||||
|
|
|
@ -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();
|
||||
});
|
Loading…
Reference in a new issue