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