overte/unpublishedScripts/marketplace/shapes/modules/hand.js

250 lines
7.8 KiB
JavaScript

//
// hand.js
//
// Created by David Rowe on 21 Jul 2017.
// Copyright 2017 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 Hand:true */
Hand = function (side) {
"use strict";
// Hand controller input.
var handController,
controllerTrigger,
controllerTriggerClicked,
controllerGrip,
isGripClicked = false,
isGripClickedHandled = false,
GRIP_ON_VALUE = 0.99,
GRIP_OFF_VALUE = 0.95,
isTriggerPressed,
isTriggerClicked,
TRIGGER_ON_VALUE = 0.15, // Per controllerDispatcherUtils.js.
TRIGGER_OFF_VALUE = 0.1, // Per controllerDispatcherUtils.js.
TRIGGER_CLICKED_VALUE = 1.0,
NEAR_GRAB_RADIUS = 0.05, // Different from controllerDispatcherUtils.js.
NEAR_HOVER_RADIUS = 0.025,
LEFT_HAND = 0,
HALF_TREE_SCALE = 16384,
handPose,
handPosition,
handOrientation,
palmPosition,
handleOverlayIDs = [],
intersection = {};
if (!(this instanceof Hand)) {
return new Hand(side);
}
if (side === LEFT_HAND) {
handController = Controller.Standard.LeftHand;
controllerTrigger = Controller.Standard.LT;
controllerTriggerClicked = Controller.Standard.LTClick;
controllerGrip = Controller.Standard.LeftGrip;
} else {
handController = Controller.Standard.RightHand;
controllerTrigger = Controller.Standard.RT;
controllerTriggerClicked = Controller.Standard.RTClick;
controllerGrip = Controller.Standard.RightGrip;
}
function setHandleOverlays(overlayIDs) {
handleOverlayIDs = overlayIDs;
}
function valid() {
return handPose.valid;
}
function position() {
return handPosition;
}
function orientation() {
return handOrientation;
}
function getPalmPosition() {
return palmPosition;
}
function triggerPressed() {
return isTriggerPressed;
}
function triggerClicked() {
return isTriggerClicked;
}
function gripClicked() {
return isGripClicked;
}
function setGripClickedHandled() {
isGripClicked = false;
isGripClickedHandled = true;
}
function getIntersection() {
return intersection;
}
function getNearGrabRadius() {
return NEAR_GRAB_RADIUS;
}
function update() {
var gripValue,
overlayID,
overlayIDs,
overlayDistance,
intersectionPosition,
distance,
entityID,
entityIDs,
entitySize,
size,
i,
length;
// Hand pose.
handPose = Controller.getPoseValue(handController);
if (!handPose.valid) {
intersection = {};
return;
}
handPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, handPose.translation), MyAvatar.position);
handOrientation = Quat.multiply(MyAvatar.orientation, handPose.rotation);
// Controller trigger.
isTriggerPressed = Controller.getValue(controllerTrigger) > (isTriggerPressed
? TRIGGER_OFF_VALUE : TRIGGER_ON_VALUE);
isTriggerClicked = Controller.getValue(controllerTriggerClicked) === TRIGGER_CLICKED_VALUE;
// Controller grip.
gripValue = Controller.getValue(controllerGrip);
if (isGripClicked) {
isGripClicked = gripValue > GRIP_OFF_VALUE;
} else {
isGripClicked = gripValue > GRIP_ON_VALUE;
}
// Grip clicked may be being handled by UI.
if (isGripClicked) {
isGripClicked = !isGripClickedHandled;
} else {
isGripClickedHandled = false;
}
// Hand-overlay intersection, if any handle overlays.
overlayID = null;
palmPosition = side === LEFT_HAND ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition();
if (handleOverlayIDs.length > 0) {
overlayIDs = Overlays.findOverlays(palmPosition, NEAR_HOVER_RADIUS);
if (overlayIDs.length > 0) {
// Typically, there will be only one overlay; optimize for that case.
overlayID = overlayIDs[0];
if (overlayIDs.length > 1) {
// Find closest overlay.
overlayDistance = Vec3.length(Vec3.subtract(Overlays.getProperty(overlayID, "position"), palmPosition));
for (i = 1, length = overlayIDs.length; i < length; i++) {
distance =
Vec3.length(Vec3.subtract(Overlays.getProperty(overlayIDs[i], "position"), palmPosition));
if (distance > overlayDistance) {
overlayID = overlayIDs[i];
overlayDistance = distance;
}
}
}
}
if (overlayID && handleOverlayIDs.indexOf(overlayID) === -1) {
overlayID = null;
}
if (overlayID) {
intersectionPosition = Overlays.getProperty(overlayID, "position");
}
}
// Hand-entity intersection, if any editable, if overlay not intersected.
entityID = null;
if (overlayID === null) {
// palmPosition is set above.
entityIDs = Entities.findEntities(palmPosition, NEAR_GRAB_RADIUS);
if (entityIDs.length > 0) {
// Typically, there will be only one entity; optimize for that case.
if (Entities.hasEditableRoot(entityIDs[0])) {
entityID = entityIDs[0];
}
if (entityIDs.length > 1) {
// Find smallest, editable entity.
entitySize = HALF_TREE_SCALE;
for (i = 0, length = entityIDs.length; i < length; i++) {
if (Entities.hasEditableRoot(entityIDs[i])) {
size = Vec3.length(Entities.getEntityProperties(entityIDs[i], "dimensions").dimensions);
if (size < entitySize) {
entityID = entityIDs[i];
entitySize = size;
}
}
}
}
}
if (entityID) {
intersectionPosition = Entities.getEntityProperties(entityID, "position").position;
if (Vec3.distance(palmPosition, intersectionPosition) > NEAR_GRAB_RADIUS) {
intersectionPosition = Vec3.sum(palmPosition,
Vec3.multiply(NEAR_GRAB_RADIUS, Vec3.normalize(Vec3.subtract(intersectionPosition, palmPosition))));
}
}
}
intersection = {
intersects: overlayID !== null || entityID !== null,
overlayID: overlayID,
entityID: entityID,
handIntersected: overlayID !== null || entityID !== null,
editableEntity: entityID !== null,
intersection: intersectionPosition
};
}
function clear() {
// Nothing to do.
}
function destroy() {
// Nothing to do.
}
return {
setHandleOverlays: setHandleOverlays,
valid: valid,
position: position,
orientation: orientation,
palmPosition: getPalmPosition,
triggerPressed: triggerPressed,
triggerClicked: triggerClicked,
gripClicked: gripClicked,
setGripClickedHandled: setGripClickedHandled,
intersection: getIntersection,
getNearGrabRadius: getNearGrabRadius,
update: update,
clear: clear,
destroy: destroy
};
};
Hand.prototype = {};