mirror of
https://github.com/overte-org/overte.git
synced 2025-08-07 20:13:40 +02:00
Merge pull request #8571 from sethalves/grab-point-adjustments
grab-point adjustments
This commit is contained in:
commit
46e57f7773
7 changed files with 180 additions and 90 deletions
|
@ -244,14 +244,19 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) {
|
||||||
// 3 -- ignore i of 0 1 2
|
// 3 -- ignore i of 0 1 2
|
||||||
// 4 -- ignore i of 1 2 3
|
// 4 -- ignore i of 1 2 3
|
||||||
// 5 -- ignore i of 2 3 4
|
// 5 -- ignore i of 2 3 4
|
||||||
if ((i + 1) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex ||
|
|
||||||
(i + 2) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex ||
|
// This code is now disabled, but I'm leaving it commented-out because I suspect it will come back.
|
||||||
(i + 3) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex) {
|
// if ((i + 1) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex ||
|
||||||
continue;
|
// (i + 2) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex ||
|
||||||
}
|
// (i + 3) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
measuredLinearVelocity += _measuredLinearVelocities[i];
|
measuredLinearVelocity += _measuredLinearVelocities[i];
|
||||||
}
|
}
|
||||||
measuredLinearVelocity /= (float)(AvatarActionHold::velocitySmoothFrames - 3); // 3 because of the 3 we skipped, above
|
measuredLinearVelocity /= (float)(AvatarActionHold::velocitySmoothFrames
|
||||||
|
// - 3 // 3 because of the 3 we skipped, above
|
||||||
|
);
|
||||||
|
|
||||||
if (_kinematicSetVelocity) {
|
if (_kinematicSetVelocity) {
|
||||||
rigidBody->setLinearVelocity(glmToBullet(measuredLinearVelocity));
|
rigidBody->setLinearVelocity(glmToBullet(measuredLinearVelocity));
|
||||||
|
|
|
@ -277,27 +277,37 @@ void HmdDisplayPlugin::updateFrameData() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& laserDirection = handLaser.direction;
|
const vec3& laserDirection = handLaser.direction;
|
||||||
auto model = _presentHandPoses[i];
|
mat4 model = _presentHandPoses[i];
|
||||||
auto castDirection = glm::quat_cast(model) * laserDirection;
|
vec3 castStart = vec3(model[3]);
|
||||||
|
vec3 castDirection = glm::quat_cast(model) * laserDirection;
|
||||||
if (glm::abs(glm::length2(castDirection) - 1.0f) > EPSILON) {
|
if (glm::abs(glm::length2(castDirection) - 1.0f) > EPSILON) {
|
||||||
castDirection = glm::normalize(castDirection);
|
castDirection = glm::normalize(castDirection);
|
||||||
castDirection = glm::inverse(_presentUiModelTransform.getRotation()) * castDirection;
|
castDirection = glm::inverse(_presentUiModelTransform.getRotation()) * castDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this offset needs to match GRAB_POINT_SPHERE_OFFSET in scripts/system/libraries/controllers.js
|
||||||
|
static const vec3 GRAB_POINT_SPHERE_OFFSET = vec3(0.1f, 0.04f, -0.32f);
|
||||||
|
vec3 grabPointOffset = GRAB_POINT_SPHERE_OFFSET;
|
||||||
|
if (i == 0) {
|
||||||
|
grabPointOffset.x *= -1.0f; // this changes between left and right hands
|
||||||
|
}
|
||||||
|
castStart += glm::quat_cast(model) * grabPointOffset;
|
||||||
|
|
||||||
// FIXME fetch the actual UI radius from... somewhere?
|
// FIXME fetch the actual UI radius from... somewhere?
|
||||||
float uiRadius = 1.0f;
|
float uiRadius = 1.0f;
|
||||||
|
|
||||||
// Find the intersection of the laser with he UI and use it to scale the model matrix
|
// Find the intersection of the laser with he UI and use it to scale the model matrix
|
||||||
float distance;
|
float distance;
|
||||||
if (!glm::intersectRaySphere(vec3(_presentHandPoses[i][3]), castDirection, _presentUiModelTransform.getTranslation(), uiRadius * uiRadius, distance)) {
|
if (!glm::intersectRaySphere(castStart, castDirection,
|
||||||
|
_presentUiModelTransform.getTranslation(), uiRadius * uiRadius, distance)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_presentHandLaserPoints[i].first = vec3(_presentHandPoses[i][3]);
|
_presentHandLaserPoints[i].first = castStart;
|
||||||
_presentHandLaserPoints[i].second = _presentHandLaserPoints[i].first + (castDirection * distance);
|
_presentHandLaserPoints[i].second = _presentHandLaserPoints[i].first + (castDirection * distance);
|
||||||
|
|
||||||
vec3 intersectionPosition = vec3(_presentHandPoses[i][3]) + (castDirection * distance) - _presentUiModelTransform.getTranslation();
|
vec3 intersectionPosition = castStart + (castDirection * distance) - _presentUiModelTransform.getTranslation();
|
||||||
intersectionPosition = glm::inverse(_presentUiModelTransform.getRotation()) * intersectionPosition;
|
intersectionPosition = glm::inverse(_presentUiModelTransform.getRotation()) * intersectionPosition;
|
||||||
|
|
||||||
// Take the interesection normal and convert it to a texture coordinate
|
// Take the interesection normal and convert it to a texture coordinate
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <EntityItemProperties.h>
|
#include <EntityItemProperties.h>
|
||||||
#include <EntityEditPacketSender.h>
|
#include <EntityEditPacketSender.h>
|
||||||
#include <PhysicsCollisionGroups.h>
|
#include <PhysicsCollisionGroups.h>
|
||||||
|
#include <LogHandler.h>
|
||||||
|
|
||||||
#include "BulletUtil.h"
|
#include "BulletUtil.h"
|
||||||
#include "EntityMotionState.h"
|
#include "EntityMotionState.h"
|
||||||
|
@ -230,11 +231,17 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
||||||
bool positionSuccess;
|
bool positionSuccess;
|
||||||
_entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(), positionSuccess, false);
|
_entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(), positionSuccess, false);
|
||||||
if (!positionSuccess) {
|
if (!positionSuccess) {
|
||||||
|
static QString repeatedMessage =
|
||||||
|
LogHandler::getInstance().addRepeatedMessageRegex("EntityMotionState::setWorldTransform "
|
||||||
|
"setPosition failed.*");
|
||||||
qDebug() << "EntityMotionState::setWorldTransform setPosition failed" << _entity->getID();
|
qDebug() << "EntityMotionState::setWorldTransform setPosition failed" << _entity->getID();
|
||||||
}
|
}
|
||||||
bool orientationSuccess;
|
bool orientationSuccess;
|
||||||
_entity->setOrientation(bulletToGLM(worldTrans.getRotation()), orientationSuccess, false);
|
_entity->setOrientation(bulletToGLM(worldTrans.getRotation()), orientationSuccess, false);
|
||||||
if (!orientationSuccess) {
|
if (!orientationSuccess) {
|
||||||
|
static QString repeatedMessage =
|
||||||
|
LogHandler::getInstance().addRepeatedMessageRegex("EntityMotionState::setWorldTransform "
|
||||||
|
"setOrientation failed.*");
|
||||||
qDebug() << "EntityMotionState::setWorldTransform setOrientation failed" << _entity->getID();
|
qDebug() << "EntityMotionState::setWorldTransform setOrientation failed" << _entity->getID();
|
||||||
}
|
}
|
||||||
_entity->setVelocity(getBodyLinearVelocity());
|
_entity->setVelocity(getBodyLinearVelocity());
|
||||||
|
|
|
@ -11,12 +11,13 @@
|
||||||
//
|
//
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
/* global setEntityCustomData, getEntityCustomData, flatten, Xform, Script, Quat, Vec3, MyAvatar, Entities, Overlays, Settings, Reticle, Controller, Camera, Messages, Mat4 */
|
/* global setEntityCustomData, getEntityCustomData, flatten, Xform, Script, Quat, Vec3, MyAvatar, Entities, Overlays, Settings, Reticle, Controller, Camera, Messages, Mat4, getControllerWorldLocation, getGrabPointSphereOffset */
|
||||||
|
|
||||||
(function() { // BEGIN LOCAL_SCOPE
|
(function() { // BEGIN LOCAL_SCOPE
|
||||||
|
|
||||||
Script.include("/~/system/libraries/utils.js");
|
Script.include("/~/system/libraries/utils.js");
|
||||||
Script.include("/~/system/libraries/Xform.js");
|
Script.include("/~/system/libraries/Xform.js");
|
||||||
|
Script.include("/~/system/libraries/controllers.js");
|
||||||
|
|
||||||
//
|
//
|
||||||
// add lines where the hand ray picking is happening
|
// add lines where the hand ray picking is happening
|
||||||
|
@ -106,19 +107,16 @@ var MAX_EQUIP_HOTSPOT_RADIUS = 1.0;
|
||||||
|
|
||||||
var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position
|
var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position
|
||||||
|
|
||||||
var NEAR_GRAB_RADIUS = 0.07; // radius used for palm vs object for near grabbing.
|
var NEAR_GRAB_RADIUS = 0.04; // radius used for palm vs object for near grabbing.
|
||||||
var NEAR_GRAB_MAX_DISTANCE = 1.0; // you cannot grab objects that are this far away from your hand
|
var NEAR_GRAB_MAX_DISTANCE = 1.0; // you cannot grab objects that are this far away from your hand
|
||||||
|
|
||||||
var NEAR_GRAB_PICK_RADIUS = 0.25; // radius used for search ray vs object for near grabbing.
|
var NEAR_GRAB_PICK_RADIUS = 0.25; // radius used for search ray vs object for near grabbing.
|
||||||
|
|
||||||
var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object
|
|
||||||
var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed
|
var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed
|
||||||
|
|
||||||
// if an equipped item is "adjusted" to be too far from the hand it's in, it will be unequipped.
|
// if an equipped item is "adjusted" to be too far from the hand it's in, it will be unequipped.
|
||||||
var CHECK_TOO_FAR_UNEQUIP_TIME = 0.3; // seconds, duration between checks
|
var CHECK_TOO_FAR_UNEQUIP_TIME = 0.3; // seconds, duration between checks
|
||||||
|
|
||||||
|
|
||||||
var GRAB_POINT_SPHERE_OFFSET = { x: 0.0, y: 0.2, z: 0.0 };
|
|
||||||
var GRAB_POINT_SPHERE_RADIUS = NEAR_GRAB_RADIUS;
|
var GRAB_POINT_SPHERE_RADIUS = NEAR_GRAB_RADIUS;
|
||||||
var GRAB_POINT_SPHERE_COLOR = { red: 20, green: 90, blue: 238 };
|
var GRAB_POINT_SPHERE_COLOR = { red: 20, green: 90, blue: 238 };
|
||||||
var GRAB_POINT_SPHERE_ALPHA = 0.85;
|
var GRAB_POINT_SPHERE_ALPHA = 0.85;
|
||||||
|
@ -722,6 +720,7 @@ var equipHotspotBuddy = new EquipHotspotBuddy();
|
||||||
function MyController(hand) {
|
function MyController(hand) {
|
||||||
this.hand = hand;
|
this.hand = hand;
|
||||||
this.autoUnequipCounter = 0;
|
this.autoUnequipCounter = 0;
|
||||||
|
this.grabPointIntersectsEntity = false;
|
||||||
|
|
||||||
// handPosition is where the avatar's hand appears to be, in-world.
|
// handPosition is where the avatar's hand appears to be, in-world.
|
||||||
this.getHandPosition = function () {
|
this.getHandPosition = function () {
|
||||||
|
@ -738,19 +737,9 @@ function MyController(hand) {
|
||||||
return MyAvatar.getLeftPalmRotation();
|
return MyAvatar.getLeftPalmRotation();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// controllerLocation is where the controller would be, in-world.
|
|
||||||
this.getControllerLocation = function (doOffset) {
|
|
||||||
var standardControllerValue = (hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
|
|
||||||
var pose = Controller.getPoseValue(standardControllerValue);
|
|
||||||
|
|
||||||
var orientation = Quat.multiply(MyAvatar.orientation, pose.rotation);
|
this.handToController = function() {
|
||||||
var position = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position);
|
return (hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
|
||||||
// add to the real position so the grab-point is out in front of the hand, a bit
|
|
||||||
if (doOffset) {
|
|
||||||
position = Vec3.sum(position, Vec3.multiplyQbyV(orientation, GRAB_POINT_SPHERE_OFFSET));
|
|
||||||
}
|
|
||||||
|
|
||||||
return {position: position, orientation: orientation};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.actionID = null; // action this script created...
|
this.actionID = null; // action this script created...
|
||||||
|
@ -866,7 +855,7 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
if (!this.grabPointSphere) {
|
if (!this.grabPointSphere) {
|
||||||
this.grabPointSphere = Overlays.addOverlay("sphere", {
|
this.grabPointSphere = Overlays.addOverlay("sphere", {
|
||||||
localPosition: GRAB_POINT_SPHERE_OFFSET,
|
localPosition: getGrabPointSphereOffset(this.handToController()),
|
||||||
localRotation: { x: 0, y: 0, z: 0, w: 1 },
|
localRotation: { x: 0, y: 0, z: 0, w: 1 },
|
||||||
dimensions: GRAB_POINT_SPHERE_RADIUS,
|
dimensions: GRAB_POINT_SPHERE_RADIUS,
|
||||||
color: GRAB_POINT_SPHERE_COLOR,
|
color: GRAB_POINT_SPHERE_COLOR,
|
||||||
|
@ -1094,20 +1083,28 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
if (!this.waitForTriggerRelease && this.triggerSmoothedSqueezed()) {
|
if (!this.waitForTriggerRelease && this.triggerSmoothedSqueezed()) {
|
||||||
this.lastPickTime = 0;
|
this.lastPickTime = 0;
|
||||||
this.startingHandRotation = this.getControllerLocation(true).orientation;
|
this.startingHandRotation = getControllerWorldLocation(this.handToController(), true).orientation;
|
||||||
if (this.triggerSmoothedSqueezed()) {
|
if (this.triggerSmoothedSqueezed()) {
|
||||||
this.setState(STATE_SEARCHING, "trigger squeeze detected");
|
this.setState(STATE_SEARCHING, "trigger squeeze detected");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.grabPointSphereOn();
|
|
||||||
|
|
||||||
var candidateEntities = Entities.findEntities(this.getControllerLocation(true).position, MAX_EQUIP_HOTSPOT_RADIUS);
|
var controllerLocation = getControllerWorldLocation(this.handToController(), true);
|
||||||
|
var worldHandPosition = controllerLocation.position;
|
||||||
|
|
||||||
|
if (controllerLocation.valid) {
|
||||||
|
this.grabPointSphereOn();
|
||||||
|
} else {
|
||||||
|
this.grabPointSphereOff();
|
||||||
|
}
|
||||||
|
|
||||||
|
var candidateEntities = Entities.findEntities(worldHandPosition, MAX_EQUIP_HOTSPOT_RADIUS);
|
||||||
entityPropertiesCache.addEntities(candidateEntities);
|
entityPropertiesCache.addEntities(candidateEntities);
|
||||||
var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities);
|
var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities);
|
||||||
if (!this.waitForTriggerRelease) {
|
if (!this.waitForTriggerRelease) {
|
||||||
this.updateEquipHaptics(potentialEquipHotspot, this.getControllerLocation(true).position);
|
this.updateEquipHaptics(potentialEquipHotspot, worldHandPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS);
|
var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS);
|
||||||
|
@ -1115,6 +1112,20 @@ function MyController(hand) {
|
||||||
if (potentialEquipHotspot) {
|
if (potentialEquipHotspot) {
|
||||||
equipHotspotBuddy.highlightHotspot(potentialEquipHotspot);
|
equipHotspotBuddy.highlightHotspot(potentialEquipHotspot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// when the grab-point enters a grabable entity, give a haptic pulse
|
||||||
|
candidateEntities = Entities.findEntities(worldHandPosition, NEAR_GRAB_RADIUS);
|
||||||
|
var grabbableEntities = candidateEntities.filter(function(entity) {
|
||||||
|
return _this.entityIsNearGrabbable(entity, worldHandPosition, NEAR_GRAB_MAX_DISTANCE);
|
||||||
|
});
|
||||||
|
if (grabbableEntities.length > 0) {
|
||||||
|
if (!this.grabPointIntersectsEntity) {
|
||||||
|
Controller.triggerHapticPulse(1, 20, this.hand);
|
||||||
|
this.grabPointIntersectsEntity = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.grabPointIntersectsEntity = false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.clearEquipHaptics = function() {
|
this.clearEquipHaptics = function() {
|
||||||
|
@ -1144,7 +1155,7 @@ function MyController(hand) {
|
||||||
// @returns {object} returns object with two keys entityID and distance
|
// @returns {object} returns object with two keys entityID and distance
|
||||||
//
|
//
|
||||||
this.calcRayPickInfo = function(hand) {
|
this.calcRayPickInfo = function(hand) {
|
||||||
var controllerLocation = this.getControllerLocation(true);
|
var controllerLocation = getControllerWorldLocation(this.handToController(), true);
|
||||||
var worldHandPosition = controllerLocation.position;
|
var worldHandPosition = controllerLocation.position;
|
||||||
var worldHandRotation = controllerLocation.orientation;
|
var worldHandRotation = controllerLocation.orientation;
|
||||||
|
|
||||||
|
@ -1168,18 +1179,11 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
this.lastPickTime = now;
|
this.lastPickTime = now;
|
||||||
|
|
||||||
var directionNormalized = Vec3.normalize(pickRay.direction);
|
|
||||||
var directionBacked = Vec3.multiply(directionNormalized, PICK_BACKOFF_DISTANCE);
|
|
||||||
var pickRayBacked = {
|
|
||||||
origin: Vec3.subtract(pickRay.origin, directionBacked),
|
|
||||||
direction: pickRay.direction
|
|
||||||
};
|
|
||||||
|
|
||||||
var intersection;
|
var intersection;
|
||||||
if (USE_BLACKLIST === true && blacklist.length !== 0) {
|
if (USE_BLACKLIST === true && blacklist.length !== 0) {
|
||||||
intersection = findRayIntersection(pickRayBacked, true, [], blacklist);
|
intersection = findRayIntersection(pickRay, true, [], blacklist);
|
||||||
} else {
|
} else {
|
||||||
intersection = findRayIntersection(pickRayBacked, true);
|
intersection = findRayIntersection(pickRay, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (intersection.intersects) {
|
if (intersection.intersects) {
|
||||||
|
@ -1392,7 +1396,8 @@ function MyController(hand) {
|
||||||
return _this.collectEquipHotspots(entityID);
|
return _this.collectEquipHotspots(entityID);
|
||||||
})).filter(function(hotspot) {
|
})).filter(function(hotspot) {
|
||||||
return (_this.hotspotIsEquippable(hotspot) &&
|
return (_this.hotspotIsEquippable(hotspot) &&
|
||||||
Vec3.distance(hotspot.worldPosition, _this.getControllerLocation(true).position) < hotspot.radius + distance);
|
Vec3.distance(hotspot.worldPosition, getControllerWorldLocation(_this.handToController(), true).position) <
|
||||||
|
hotspot.radius + distance);
|
||||||
});
|
});
|
||||||
return equippableHotspots;
|
return equippableHotspots;
|
||||||
};
|
};
|
||||||
|
@ -1403,8 +1408,9 @@ function MyController(hand) {
|
||||||
if (equippableHotspots.length > 0) {
|
if (equippableHotspots.length > 0) {
|
||||||
// sort by distance
|
// sort by distance
|
||||||
equippableHotspots.sort(function(a, b) {
|
equippableHotspots.sort(function(a, b) {
|
||||||
var aDistance = Vec3.distance(a.worldPosition, this.getControllerLocation(true).position);
|
var handControllerLocation = getControllerWorldLocation(this.handToController(), true);
|
||||||
var bDistance = Vec3.distance(b.worldPosition, this.getControllerLocation(true).position);
|
var aDistance = Vec3.distance(a.worldPosition, handControllerLocation.position);
|
||||||
|
var bDistance = Vec3.distance(b.worldPosition, handControllerLocation.position);
|
||||||
return aDistance - bDistance;
|
return aDistance - bDistance;
|
||||||
});
|
});
|
||||||
return equippableHotspots[0];
|
return equippableHotspots[0];
|
||||||
|
@ -1430,8 +1436,6 @@ function MyController(hand) {
|
||||||
this.isInitialGrab = false;
|
this.isInitialGrab = false;
|
||||||
this.shouldResetParentOnRelease = false;
|
this.shouldResetParentOnRelease = false;
|
||||||
|
|
||||||
this.grabPointSphereOn();
|
|
||||||
|
|
||||||
this.checkForStrayChildren();
|
this.checkForStrayChildren();
|
||||||
|
|
||||||
if (this.triggerSmoothedReleased()) {
|
if (this.triggerSmoothedReleased()) {
|
||||||
|
@ -1439,7 +1443,14 @@ function MyController(hand) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var handPosition = this.getControllerLocation(true).position;
|
var controllerLocation = getControllerWorldLocation(this.handToController(), true);
|
||||||
|
var handPosition = controllerLocation.position;
|
||||||
|
|
||||||
|
if (controllerLocation.valid) {
|
||||||
|
this.grabPointSphereOn();
|
||||||
|
} else {
|
||||||
|
this.grabPointSphereOff();
|
||||||
|
}
|
||||||
|
|
||||||
var rayPickInfo = this.calcRayPickInfo(this.hand);
|
var rayPickInfo = this.calcRayPickInfo(this.hand);
|
||||||
|
|
||||||
|
@ -1624,7 +1635,7 @@ function MyController(hand) {
|
||||||
this.clearEquipHaptics();
|
this.clearEquipHaptics();
|
||||||
this.grabPointSphereOff();
|
this.grabPointSphereOff();
|
||||||
|
|
||||||
var worldControllerPosition = this.getControllerLocation(true).position;
|
var worldControllerPosition = getControllerWorldLocation(this.handToController(), true).position;
|
||||||
|
|
||||||
// transform the position into room space
|
// transform the position into room space
|
||||||
var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix());
|
var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix());
|
||||||
|
@ -1642,7 +1653,8 @@ function MyController(hand) {
|
||||||
this.grabRadius = Vec3.distance(this.currentObjectPosition, worldControllerPosition);
|
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
|
||||||
this.radiusScalar = Math.log(this.grabRadius + 1.0);
|
this.radiusScalar = Math.log(this.grabRadius + 1.0);
|
||||||
if (this.radiusScalar < 1.0) {
|
if (this.radiusScalar < 1.0) {
|
||||||
this.radiusScalar = 1.0;
|
this.radiusScalar = 1.0;
|
||||||
|
@ -1668,7 +1680,7 @@ function MyController(hand) {
|
||||||
this.actionTimeout = now + (ACTION_TTL * MSECS_PER_SEC);
|
this.actionTimeout = now + (ACTION_TTL * MSECS_PER_SEC);
|
||||||
|
|
||||||
if (this.actionID !== null) {
|
if (this.actionID !== null) {
|
||||||
this.activateEntity(this.grabbedEntity, grabbedProperties, false);
|
this.activateEntity(this.grabbedEntity, grabbedProperties, false, true);
|
||||||
this.callEntityMethodOnGrabbed("startDistanceGrab");
|
this.callEntityMethodOnGrabbed("startDistanceGrab");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1683,14 +1695,22 @@ function MyController(hand) {
|
||||||
|
|
||||||
if (!this.triggerClicked) {
|
if (!this.triggerClicked) {
|
||||||
this.callEntityMethodOnGrabbed("releaseGrab");
|
this.callEntityMethodOnGrabbed("releaseGrab");
|
||||||
|
|
||||||
|
// if we distance hold something and keep it very still before releasing it, it ends up
|
||||||
|
// non-dynamic in bullet. If it's too still, give it a little bounce so it will fall.
|
||||||
|
var velocity = Entities.getEntityProperties(this.grabbedEntity, ["velocity"]).velocity;
|
||||||
|
if (Vec3.length(velocity) < 0.05) { // see EntityMotionState.cpp DYNAMIC_LINEAR_VELOCITY_THRESHOLD
|
||||||
|
velocity = { x: 0.0, y: 0.2, z:0.0 };
|
||||||
|
Entities.editEntity(this.grabbedEntity, { velocity: velocity });
|
||||||
|
}
|
||||||
|
|
||||||
this.setState(STATE_OFF, "trigger released");
|
this.setState(STATE_OFF, "trigger released");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.heartBeat(this.grabbedEntity);
|
this.heartBeat(this.grabbedEntity);
|
||||||
|
|
||||||
|
var controllerLocation = getControllerWorldLocation(this.handToController(), true);
|
||||||
var controllerLocation = this.getControllerLocation(true);
|
|
||||||
var worldControllerPosition = controllerLocation.position;
|
var worldControllerPosition = controllerLocation.position;
|
||||||
var worldControllerRotation = controllerLocation.orientation;
|
var worldControllerRotation = controllerLocation.orientation;
|
||||||
|
|
||||||
|
@ -1736,7 +1756,13 @@ function MyController(hand) {
|
||||||
var RADIAL_GRAB_AMPLIFIER = 10.0;
|
var RADIAL_GRAB_AMPLIFIER = 10.0;
|
||||||
if (Math.abs(this.grabRadialVelocity) > 0.0) {
|
if (Math.abs(this.grabRadialVelocity) > 0.0) {
|
||||||
this.grabRadius = this.grabRadius + (this.grabRadialVelocity * deltaObjectTime *
|
this.grabRadius = this.grabRadius + (this.grabRadialVelocity * deltaObjectTime *
|
||||||
this.grabRadius * RADIAL_GRAB_AMPLIFIER);
|
this.grabRadius * RADIAL_GRAB_AMPLIFIER);
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't let grabRadius go all the way to zero, because it can't come back from that
|
||||||
|
var MINIMUM_GRAB_RADIUS = 0.1;
|
||||||
|
if (this.grabRadius < MINIMUM_GRAB_RADIUS) {
|
||||||
|
this.grabRadius = MINIMUM_GRAB_RADIUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
var newTargetPosition = Vec3.multiply(this.grabRadius, Quat.getUp(worldControllerRotation));
|
var newTargetPosition = Vec3.multiply(this.grabRadius, Quat.getUp(worldControllerRotation));
|
||||||
|
@ -1827,7 +1853,7 @@ function MyController(hand) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.dropGestureProcess = function(deltaTime) {
|
this.dropGestureProcess = function(deltaTime) {
|
||||||
var worldHandRotation = this.getControllerLocation(true).orientation;
|
var worldHandRotation = getControllerWorldLocation(this.handToController(), true).orientation;
|
||||||
var localHandUpAxis = this.hand === RIGHT_HAND ? {
|
var localHandUpAxis = this.hand === RIGHT_HAND ? {
|
||||||
x: 1,
|
x: 1,
|
||||||
y: 0,
|
y: 0,
|
||||||
|
@ -1893,7 +1919,7 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
|
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
|
||||||
this.activateEntity(this.grabbedEntity, grabbedProperties, false);
|
this.activateEntity(this.grabbedEntity, grabbedProperties, false, false);
|
||||||
|
|
||||||
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
|
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
|
||||||
if (FORCE_IGNORE_IK) {
|
if (FORCE_IGNORE_IK) {
|
||||||
|
@ -1905,7 +1931,7 @@ function MyController(hand) {
|
||||||
var handRotation;
|
var handRotation;
|
||||||
var handPosition;
|
var handPosition;
|
||||||
if (this.ignoreIK) {
|
if (this.ignoreIK) {
|
||||||
var controllerLocation = this.getControllerLocation(false);
|
var controllerLocation = getControllerWorldLocation(this.handToController(), false);
|
||||||
handRotation = controllerLocation.orientation;
|
handRotation = controllerLocation.orientation;
|
||||||
handPosition = controllerLocation.position;
|
handPosition = controllerLocation.position;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2085,7 +2111,7 @@ function MyController(hand) {
|
||||||
if (props.parentID == MyAvatar.sessionUUID) {
|
if (props.parentID == MyAvatar.sessionUUID) {
|
||||||
var handPosition;
|
var handPosition;
|
||||||
if (this.ignoreIK) {
|
if (this.ignoreIK) {
|
||||||
handPosition = this.getControllerLocation(false).position;
|
handPosition = getControllerWorldLocation(this.handToController(), false).position;
|
||||||
} else {
|
} else {
|
||||||
handPosition = this.getHandPosition();
|
handPosition = this.getHandPosition();
|
||||||
}
|
}
|
||||||
|
@ -2201,8 +2227,8 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var pickRay = {
|
var pickRay = {
|
||||||
origin: this.getControllerLocation().position,
|
origin: getControllerWorldLocation(this.handToController(), false).position,
|
||||||
direction: Quat.getUp(this.getControllerLocation().orientation)
|
direction: Quat.getUp(getControllerWorldLocation(this.handToController(), false).orientation)
|
||||||
};
|
};
|
||||||
|
|
||||||
var now = Date.now();
|
var now = Date.now();
|
||||||
|
@ -2231,7 +2257,8 @@ function MyController(hand) {
|
||||||
|
|
||||||
this.entityTouchingEnter = function() {
|
this.entityTouchingEnter = function() {
|
||||||
// test for intersection between controller laser and web entity plane.
|
// test for intersection between controller laser and web entity plane.
|
||||||
var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.getControllerLocation(true));
|
var intersectInfo = handLaserIntersectEntity(this.grabbedEntity,
|
||||||
|
getControllerWorldLocation(this.handToController(), true));
|
||||||
if (intersectInfo) {
|
if (intersectInfo) {
|
||||||
var pointerEvent = {
|
var pointerEvent = {
|
||||||
type: "Press",
|
type: "Press",
|
||||||
|
@ -2256,7 +2283,8 @@ function MyController(hand) {
|
||||||
|
|
||||||
this.entityTouchingExit = function() {
|
this.entityTouchingExit = function() {
|
||||||
// test for intersection between controller laser and web entity plane.
|
// test for intersection between controller laser and web entity plane.
|
||||||
var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.getControllerLocation(true));
|
var intersectInfo = handLaserIntersectEntity(this.grabbedEntity,
|
||||||
|
getControllerWorldLocation(this.handToController(), true));
|
||||||
if (intersectInfo) {
|
if (intersectInfo) {
|
||||||
var pointerEvent;
|
var pointerEvent;
|
||||||
if (this.deadspotExpired) {
|
if (this.deadspotExpired) {
|
||||||
|
@ -2295,7 +2323,8 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// test for intersection between controller laser and web entity plane.
|
// test for intersection between controller laser and web entity plane.
|
||||||
var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.getControllerLocation(true));
|
var intersectInfo = handLaserIntersectEntity(this.grabbedEntity,
|
||||||
|
getControllerWorldLocation(this.handToController(), true));
|
||||||
if (intersectInfo) {
|
if (intersectInfo) {
|
||||||
|
|
||||||
if (Entities.keyboardFocusEntity != this.grabbedEntity) {
|
if (Entities.keyboardFocusEntity != this.grabbedEntity) {
|
||||||
|
@ -2400,7 +2429,7 @@ function MyController(hand) {
|
||||||
this.deactivateEntity(entityID, false);
|
this.deactivateEntity(entityID, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.activateEntity = function(entityID, grabbedProperties, wasLoaded) {
|
this.activateEntity = function(entityID, grabbedProperties, wasLoaded, collideWithStatic) {
|
||||||
this.autoUnequipCounter = 0;
|
this.autoUnequipCounter = 0;
|
||||||
|
|
||||||
if (this.entityActivated) {
|
if (this.entityActivated) {
|
||||||
|
@ -2441,15 +2470,10 @@ function MyController(hand) {
|
||||||
data.parentJointIndex = grabbedProperties.parentJointIndex;
|
data.parentJointIndex = grabbedProperties.parentJointIndex;
|
||||||
|
|
||||||
var whileHeldProperties = {
|
var whileHeldProperties = {
|
||||||
gravity: {
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
x: 0,
|
"collidesWith": collideWithStatic ?
|
||||||
y: 0,
|
COLLIDES_WITH_WHILE_GRABBED + ",static" :
|
||||||
z: 0
|
COLLIDES_WITH_WHILE_GRABBED
|
||||||
},
|
|
||||||
// bummer, it isn't easy to do bitwise collisionMask operations like this:
|
|
||||||
// "collisionMask": COLLISION_MASK_WHILE_GRABBED | grabbedProperties.collisionMask
|
|
||||||
// when using string values
|
|
||||||
"collidesWith": COLLIDES_WITH_WHILE_GRABBED
|
|
||||||
};
|
};
|
||||||
Entities.editEntity(entityID, whileHeldProperties);
|
Entities.editEntity(entityID, whileHeldProperties);
|
||||||
} else if (data.refCount > 1) {
|
} else if (data.refCount > 1) {
|
||||||
|
@ -2458,7 +2482,7 @@ function MyController(hand) {
|
||||||
// deactivate it before grabbing.
|
// deactivate it before grabbing.
|
||||||
this.resetAbandonedGrab(entityID);
|
this.resetAbandonedGrab(entityID);
|
||||||
grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
|
grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
|
||||||
return this.activateEntity(entityID, grabbedProperties, wasLoaded);
|
return this.activateEntity(entityID, grabbedProperties, wasLoaded, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isInitialGrab = false;
|
this.isInitialGrab = false;
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
// When partially squeezing over a HUD element, a laser or the reticle is shown where the active hand
|
// When partially squeezing over a HUD element, a laser or the reticle is shown where the active hand
|
||||||
// controller beam intersects the HUD.
|
// controller beam intersects the HUD.
|
||||||
|
|
||||||
|
Script.include("/~/system/libraries/controllers.js");
|
||||||
|
|
||||||
// UTILITIES -------------
|
// UTILITIES -------------
|
||||||
//
|
//
|
||||||
|
@ -203,16 +204,13 @@ function overlayFromWorldPoint(point) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function activeHudPoint2d(activeHand) { // if controller is valid, update reticle position and answer 2d point. Otherwise falsey.
|
function activeHudPoint2d(activeHand) { // if controller is valid, update reticle position and answer 2d point. Otherwise falsey.
|
||||||
var controllerPose = Controller.getPoseValue(activeHand);
|
var controllerPose = getControllerWorldLocation(activeHand, true);
|
||||||
// Valid if any plugged-in hand controller is "on". (uncradled Hydra, green-lighted Vive...)
|
|
||||||
if (!controllerPose.valid) {
|
if (!controllerPose.valid) {
|
||||||
return; // Controller is cradled.
|
return; // Controller is cradled.
|
||||||
}
|
}
|
||||||
var controllerPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, controllerPose.translation),
|
var controllerPosition = controllerPose.position;
|
||||||
MyAvatar.position);
|
var controllerDirection = Quat.getUp(controllerPose.rotation);
|
||||||
// This gets point direction right, but if you want general quaternion it would be more complicated:
|
|
||||||
var controllerDirection = Quat.getUp(Quat.multiply(MyAvatar.orientation, controllerPose.rotation));
|
|
||||||
|
|
||||||
var hudPoint3d = calculateRayUICollisionPoint(controllerPosition, controllerDirection);
|
var hudPoint3d = calculateRayUICollisionPoint(controllerPosition, controllerDirection);
|
||||||
if (!hudPoint3d) {
|
if (!hudPoint3d) {
|
||||||
if (Menu.isOptionChecked("Overlays")) { // With our hud resetting strategy, hudPoint3d should be valid here
|
if (Menu.isOptionChecked("Overlays")) { // With our hud resetting strategy, hudPoint3d should be valid here
|
||||||
|
|
46
scripts/system/libraries/controllers.js
Normal file
46
scripts/system/libraries/controllers.js
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
// handControllerGrab.js
|
||||||
|
//
|
||||||
|
// Created by Seth Alves on 2016-9-7
|
||||||
|
// Copyright 2016 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
|
||||||
|
/* global MyAvatar, Vec3, Controller, Quat */
|
||||||
|
|
||||||
|
|
||||||
|
// var GRAB_POINT_SPHERE_OFFSET = { x: 0, y: 0.2, z: 0 };
|
||||||
|
// var GRAB_POINT_SPHERE_OFFSET = { x: 0.1, y: 0.175, z: 0.04 };
|
||||||
|
|
||||||
|
// this offset needs to match the one in libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp
|
||||||
|
var GRAB_POINT_SPHERE_OFFSET = { x: 0.1, y: 0.32, z: 0.04 };
|
||||||
|
|
||||||
|
getGrabPointSphereOffset = function(handController) {
|
||||||
|
if (handController === Controller.Standard.RightHand) {
|
||||||
|
return GRAB_POINT_SPHERE_OFFSET;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
x: GRAB_POINT_SPHERE_OFFSET.x * -1,
|
||||||
|
y: GRAB_POINT_SPHERE_OFFSET.y,
|
||||||
|
z: GRAB_POINT_SPHERE_OFFSET.z
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// controllerWorldLocation is where the controller would be, in-world, with an added offset
|
||||||
|
getControllerWorldLocation = function (handController, doOffset) {
|
||||||
|
var orientation;
|
||||||
|
var position;
|
||||||
|
var pose = Controller.getPoseValue(handController);
|
||||||
|
if (pose.valid) {
|
||||||
|
orientation = Quat.multiply(MyAvatar.orientation, pose.rotation);
|
||||||
|
position = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position);
|
||||||
|
// add to the real position so the grab-point is out in front of the hand, a bit
|
||||||
|
if (doOffset) {
|
||||||
|
position = Vec3.sum(position, Vec3.multiplyQbyV(orientation, getGrabPointSphereOffset(handController)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {position: position,
|
||||||
|
translation: position,
|
||||||
|
orientation: orientation,
|
||||||
|
rotation: orientation,
|
||||||
|
valid: pose.valid};
|
||||||
|
};
|
|
@ -10,9 +10,13 @@
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
/* global Toolbars, Script, Users, Overlays, AvatarList, Controller, Camera, getControllerWorldLocation */
|
||||||
|
|
||||||
|
|
||||||
(function() { // BEGIN LOCAL_SCOPE
|
(function() { // BEGIN LOCAL_SCOPE
|
||||||
|
|
||||||
|
Script.include("/~/system/libraries/controllers.js");
|
||||||
|
|
||||||
// grab the toolbar
|
// grab the toolbar
|
||||||
var toolbar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system");
|
var toolbar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system");
|
||||||
|
|
||||||
|
@ -144,7 +148,7 @@ AvatarList.avatarRemovedEvent.connect(function(avatarID){
|
||||||
function handleSelectedOverlay(clickedOverlay) {
|
function handleSelectedOverlay(clickedOverlay) {
|
||||||
// see this is one of our mod overlays
|
// see this is one of our mod overlays
|
||||||
|
|
||||||
var modOverlayKeys = Object.keys(modOverlays)
|
var modOverlayKeys = Object.keys(modOverlays);
|
||||||
for (var i = 0; i < modOverlayKeys.length; ++i) {
|
for (var i = 0; i < modOverlayKeys.length; ++i) {
|
||||||
var avatarID = modOverlayKeys[i];
|
var avatarID = modOverlayKeys[i];
|
||||||
var modOverlay = modOverlays[avatarID];
|
var modOverlay = modOverlays[avatarID];
|
||||||
|
@ -187,13 +191,9 @@ Controller.mousePressEvent.connect(function(event){
|
||||||
var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click');
|
var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click');
|
||||||
|
|
||||||
function controllerComputePickRay(hand) {
|
function controllerComputePickRay(hand) {
|
||||||
var controllerPose = Controller.getPoseValue(hand);
|
var controllerPose = getControllerWorldLocation(hand, true);
|
||||||
if (controllerPose.valid) {
|
if (controllerPose.valid) {
|
||||||
var controllerPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, controllerPose.translation),
|
return { origin: controllerPose.position, direction: controllerPose.orientation };
|
||||||
MyAvatar.position);
|
|
||||||
// This gets point direction right, but if you want general quaternion it would be more complicated:
|
|
||||||
var controllerDirection = Quat.getUp(Quat.multiply(MyAvatar.orientation, controllerPose.rotation));
|
|
||||||
return { origin: controllerPosition, direction: controllerDirection };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue