mirror of
https://github.com/overte-org/overte.git
synced 2025-08-10 09:08:37 +02:00
Finger now points when near tablet + deadspot added when touching the tablet
This commit is contained in:
parent
836c701cb3
commit
1acc5ea760
5 changed files with 281 additions and 161 deletions
|
@ -27,10 +27,10 @@ var DEFAULT_SCRIPTS = [
|
||||||
"system/tablet-users.js",
|
"system/tablet-users.js",
|
||||||
"system/selectAudioDevice.js",
|
"system/selectAudioDevice.js",
|
||||||
"system/notifications.js",
|
"system/notifications.js",
|
||||||
|
"system/controllers/squeezeHands.js",
|
||||||
"system/controllers/controllerDisplayManager.js",
|
"system/controllers/controllerDisplayManager.js",
|
||||||
"system/controllers/handControllerGrab.js",
|
"system/controllers/handControllerGrab.js",
|
||||||
"system/controllers/handControllerPointer.js",
|
"system/controllers/handControllerPointer.js",
|
||||||
"system/controllers/squeezeHands.js",
|
|
||||||
"system/controllers/grab.js",
|
"system/controllers/grab.js",
|
||||||
"system/controllers/teleport.js",
|
"system/controllers/teleport.js",
|
||||||
"system/controllers/toggleAdvancedMovementForHandControllers.js",
|
"system/controllers/toggleAdvancedMovementForHandControllers.js",
|
||||||
|
|
|
@ -272,6 +272,12 @@ CONTROLLER_STATE_MACHINE[STATE_STYLUS_TOUCHING] = {
|
||||||
updateMethod: "stylusTouching"
|
updateMethod: "stylusTouching"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function distance2D(a, b) {
|
||||||
|
var dx = (a.x - b.x);
|
||||||
|
var dy = (a.y - b.y);
|
||||||
|
return Math.sqrt(dx * dx + dy * dy);
|
||||||
|
}
|
||||||
|
|
||||||
function getFingerWorldLocation(hand) {
|
function getFingerWorldLocation(hand) {
|
||||||
var fingerJointName = (hand === RIGHT_HAND) ? "RightHandIndex4" : "LeftHandIndex4";
|
var fingerJointName = (hand === RIGHT_HAND) ? "RightHandIndex4" : "LeftHandIndex4";
|
||||||
|
|
||||||
|
@ -664,6 +670,125 @@ function sendTouchMoveEventToStylusTarget(hand, stylusTarget) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function calculateStylusTargetFromEntity(stylusTip, entityID) {
|
||||||
|
var props = entityPropertiesCache.getProps(entityID);
|
||||||
|
|
||||||
|
// entity could have been deleted.
|
||||||
|
if (props === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// project stylus tip onto entity plane.
|
||||||
|
var normal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1});
|
||||||
|
Vec3.multiplyQbyV(props.rotation, {x: 0, y: 1, z: 0});
|
||||||
|
var distance = Vec3.dot(Vec3.subtract(stylusTip.position, props.position), normal);
|
||||||
|
var position = Vec3.subtract(stylusTip.position, Vec3.multiply(normal, distance));
|
||||||
|
|
||||||
|
// generate normalized coordinates
|
||||||
|
var invRot = Quat.inverse(props.rotation);
|
||||||
|
var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, props.position));
|
||||||
|
var invDimensions = { x: 1 / props.dimensions.x, y: 1 / props.dimensions.y, z: 1 / props.dimensions.z };
|
||||||
|
var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), props.registrationPoint);
|
||||||
|
|
||||||
|
// 2D position on entity plane in meters, relative to the bounding box upper-left hand corner.
|
||||||
|
var position2D = { x: normalizedPosition.x * props.dimensions.x, y: (1 - normalizedPosition.y) * props.dimensions.y }; // flip y-axis
|
||||||
|
|
||||||
|
return {
|
||||||
|
entityID: entityID,
|
||||||
|
overlayID: null,
|
||||||
|
distance: distance,
|
||||||
|
position: position,
|
||||||
|
position2D: position2D,
|
||||||
|
normal: normal,
|
||||||
|
normalizedPosition: normalizedPosition,
|
||||||
|
dimensions: props.dimensions,
|
||||||
|
valid: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateStylusTargetFromOverlay(stylusTip, overlayID) {
|
||||||
|
var overlayPosition = Overlays.getProperty(overlayID, "position");
|
||||||
|
|
||||||
|
// overlay could have been deleted.
|
||||||
|
if (overlayPosition === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// project stylusTip onto overlay plane.
|
||||||
|
var overlayRotation = Overlays.getProperty(overlayID, "rotation");
|
||||||
|
var normal = Vec3.multiplyQbyV(overlayRotation, {x: 0, y: 0, z: 1});
|
||||||
|
var distance = Vec3.dot(Vec3.subtract(stylusTip.position, overlayPosition), normal);
|
||||||
|
var position = Vec3.subtract(stylusTip.position, Vec3.multiply(normal, distance));
|
||||||
|
|
||||||
|
// calclulate normalized position
|
||||||
|
var invRot = Quat.inverse(overlayRotation);
|
||||||
|
var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, overlayPosition));
|
||||||
|
var dpi = Overlays.getProperty(overlayID, "dpi");
|
||||||
|
var dimensions;
|
||||||
|
if (dpi) {
|
||||||
|
// Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property is used as a scale.
|
||||||
|
var resolution = Overlays.getProperty(overlayID, "resolution");
|
||||||
|
resolution.z = 1; // Circumvent divide-by-zero.
|
||||||
|
var scale = Overlays.getProperty(overlayID, "dimensions");
|
||||||
|
scale.z = 0.01; // overlay dimensions are 2D, not 3D.
|
||||||
|
dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale);
|
||||||
|
} else {
|
||||||
|
dimensions = Overlays.getProperty(overlayID, "dimensions");
|
||||||
|
if (!dimensions.z) {
|
||||||
|
dimensions.z = 0.01; // sometimes overlay dimensions are 2D, not 3D.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var invDimensions = { x: 1 / dimensions.x, y: 1 / dimensions.y, z: 1 / dimensions.z };
|
||||||
|
var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), DEFAULT_REGISTRATION_POINT);
|
||||||
|
|
||||||
|
// 2D position on overlay plane in meters, relative to the bounding box upper-left hand corner.
|
||||||
|
var position2D = { x: normalizedPosition.x * dimensions.x, y: (1 - normalizedPosition.y) * dimensions.y }; // flip y-axis
|
||||||
|
|
||||||
|
return {
|
||||||
|
entityID: null,
|
||||||
|
overlayID: overlayID,
|
||||||
|
distance: distance,
|
||||||
|
position: position,
|
||||||
|
position2D: position2D,
|
||||||
|
normal: normal,
|
||||||
|
normalizedPosition: normalizedPosition,
|
||||||
|
dimensions: dimensions,
|
||||||
|
valid: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNearStylusTarget(stylusTargets, edgeBorder, minNormalDistance, maxNormalDistance) {
|
||||||
|
for (var i = 0; i < stylusTargets.length; i++) {
|
||||||
|
var stylusTarget = stylusTargets[i];
|
||||||
|
|
||||||
|
// check to see if the projected stylusTip is within within the 2d border
|
||||||
|
var borderMin = {x: -edgeBorder, y: -edgeBorder};
|
||||||
|
var borderMax = {x: stylusTarget.dimensions.x + edgeBorder, y: stylusTarget.dimensions.y + edgeBorder};
|
||||||
|
if (stylusTarget.distance >= minNormalDistance && stylusTarget.distance <= maxNormalDistance &&
|
||||||
|
stylusTarget.position2D.x >= borderMin.x && stylusTarget.position2D.y >= borderMin.y &&
|
||||||
|
stylusTarget.position2D.x <= borderMax.x && stylusTarget.position2D.y <= borderMax.y) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateNearestStylusTarget(stylusTargets) {
|
||||||
|
var nearestStylusTarget;
|
||||||
|
|
||||||
|
for (var i = 0; i < stylusTargets.length; i++) {
|
||||||
|
var stylusTarget = stylusTargets[i];
|
||||||
|
|
||||||
|
if ((!nearestStylusTarget || stylusTarget.distance < nearestStylusTarget.distance) &&
|
||||||
|
stylusTarget.normalizedPosition.x >= 0 && stylusTarget.normalizedPosition.y >= 0 &&
|
||||||
|
stylusTarget.normalizedPosition.x <= 1 && stylusTarget.normalizedPosition.y <= 1) {
|
||||||
|
nearestStylusTarget = stylusTarget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nearestStylusTarget;
|
||||||
|
};
|
||||||
|
|
||||||
// EntityPropertiesCache is a helper class that contains a cache of entity properties.
|
// EntityPropertiesCache is a helper class that contains a cache of entity properties.
|
||||||
// the hope is to prevent excess calls to Entity.getEntityProperties()
|
// the hope is to prevent excess calls to Entity.getEntityProperties()
|
||||||
//
|
//
|
||||||
|
@ -984,6 +1109,7 @@ function MyController(hand) {
|
||||||
this.tabletStabbedPos3D = null;
|
this.tabletStabbedPos3D = null;
|
||||||
|
|
||||||
this.useFingerInsteadOfStylus = false;
|
this.useFingerInsteadOfStylus = false;
|
||||||
|
this.fingerPointing = false;
|
||||||
|
|
||||||
// initialize stylus tip
|
// initialize stylus tip
|
||||||
var DEFAULT_STYLUS_TIP = {
|
var DEFAULT_STYLUS_TIP = {
|
||||||
|
@ -1417,123 +1543,6 @@ function MyController(hand) {
|
||||||
return _this.rawThumbValue < THUMB_ON_VALUE;
|
return _this.rawThumbValue < THUMB_ON_VALUE;
|
||||||
};
|
};
|
||||||
|
|
||||||
// returns object with the following fields
|
|
||||||
// * entityID - if non null, this entityID is the closest to the stylusTip.
|
|
||||||
// * overlayID - if non null, this overlayID is the closest to the stylusTip.
|
|
||||||
// * distance - distance in meters from the stylus to the surface of the stylusTarget.
|
|
||||||
// * position - position on the surface of the stylusTarget that is nearest to the stylusTip. (world space)
|
|
||||||
// * position2D - postion on surface of the stylusTarget ready for use for pointerEvent.pos2D
|
|
||||||
// * normal - normal vector of the surface. (world space)
|
|
||||||
// * valid - if false, all other fields are invalid.
|
|
||||||
this.calculateNearestStylusTargetFromCandidates = function (candidates, cullSide) {
|
|
||||||
|
|
||||||
// now attempt to find the nearest stylusTarget.
|
|
||||||
|
|
||||||
var nearestStylusTarget = {
|
|
||||||
entityID: null,
|
|
||||||
overlayID: null,
|
|
||||||
distance: WEB_DISPLAY_STYLUS_DISTANCE,
|
|
||||||
position: {x: 0, y: 0, z: 0},
|
|
||||||
normalizedPosition: {x: 0, y: 0, z: 0},
|
|
||||||
normal: {x: 0, y: 0, z: 1},
|
|
||||||
valid: false
|
|
||||||
};
|
|
||||||
|
|
||||||
if (candidates.entities.length > 0 || candidates.overlays.length > 0) {
|
|
||||||
var i, props, entityID, normal, distance, position, invRot, localPos, invDimensions, normalizedPos, position2D;
|
|
||||||
for (i = 0; i < candidates.entities.length; i++) {
|
|
||||||
entityID = candidates.entities[i];
|
|
||||||
props = entityPropertiesCache.getProps(entityID);
|
|
||||||
|
|
||||||
// entity could have been deleted.
|
|
||||||
if (props === undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
normal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1});
|
|
||||||
Vec3.multiplyQbyV(props.rotation, {x: 0, y: 1, z: 0});
|
|
||||||
distance = Vec3.dot(Vec3.subtract(this.stylusTip.position, props.position), normal);
|
|
||||||
position = Vec3.subtract(this.stylusTip.position, Vec3.multiply(normal, distance));
|
|
||||||
|
|
||||||
if (distance < nearestStylusTarget.distance) {
|
|
||||||
|
|
||||||
invRot = Quat.inverse(props.rotation);
|
|
||||||
localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, props.position));
|
|
||||||
invDimensions = { x: 1 / props.dimensions.x, y: 1 / props.dimensions.y, z: 1 / props.dimensions.z };
|
|
||||||
normalizedPos = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), props.registrationPoint);
|
|
||||||
|
|
||||||
if (normalizedPos.x >= 0 && normalizedPos.y >= 0 && normalizedPos.x <= 1 && normalizedPos.y <= 1) {
|
|
||||||
position2D = { x: normalizedPos.x * props.dimensions.x, y: (1 - normalizedPos.y) * props.dimensions.y }; // flip y-axis
|
|
||||||
nearestStylusTarget = {
|
|
||||||
entityID: entityID,
|
|
||||||
overlayID: null,
|
|
||||||
distance: distance,
|
|
||||||
position: position,
|
|
||||||
position2D: position2D,
|
|
||||||
normal: normal,
|
|
||||||
valid: true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < candidates.overlays.length; i++) {
|
|
||||||
var overlayID = candidates.overlays[i];
|
|
||||||
var overlayPosition = Overlays.getProperty(overlayID, "position");
|
|
||||||
|
|
||||||
// overlay could have been deleted.
|
|
||||||
if (overlayPosition === undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var overlayRotation = Overlays.getProperty(overlayID, "rotation");
|
|
||||||
normal = Vec3.multiplyQbyV(overlayRotation, {x: 0, y: 0, z: 1});
|
|
||||||
distance = Vec3.dot(Vec3.subtract(this.stylusTip.position, overlayPosition), normal);
|
|
||||||
position = Vec3.subtract(this.stylusTip.position, Vec3.multiply(normal, distance));
|
|
||||||
|
|
||||||
if (distance < nearestStylusTarget.distance) {
|
|
||||||
|
|
||||||
invRot = Quat.inverse(overlayRotation);
|
|
||||||
localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, overlayPosition));
|
|
||||||
|
|
||||||
var dpi = Overlays.getProperty(overlayID, "dpi");
|
|
||||||
var dimensions;
|
|
||||||
if (dpi) {
|
|
||||||
// Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property is used as a scale.
|
|
||||||
var resolution = Overlays.getProperty(overlayID, "resolution");
|
|
||||||
resolution.z = 1; // Circumvent divide-by-zero.
|
|
||||||
var scale = Overlays.getProperty(overlayID, "dimensions");
|
|
||||||
scale.z = 0.01; // overlay dimensions are 2D, not 3D.
|
|
||||||
dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale);
|
|
||||||
} else {
|
|
||||||
dimensions = Overlays.getProperty(overlayID, "dimensions");
|
|
||||||
if (!dimensions.z) {
|
|
||||||
dimensions.z = 0.01; // sometimes overlay dimensions are 2D, not 3D.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
invDimensions = { x: 1 / dimensions.x, y: 1 / dimensions.y, z: 1 / dimensions.z };
|
|
||||||
normalizedPos = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), DEFAULT_REGISTRATION_POINT);
|
|
||||||
|
|
||||||
if (!cullSide || (normalizedPos.x >= 0 && normalizedPos.y >= 0 && normalizedPos.x <= 1 && normalizedPos.y <= 1)) {
|
|
||||||
position2D = { x: normalizedPos.x * dimensions.x, y: (1 - normalizedPos.y) * dimensions.y }; // flip y-axis
|
|
||||||
nearestStylusTarget = {
|
|
||||||
entityID: null,
|
|
||||||
overlayID: overlayID,
|
|
||||||
distance: distance,
|
|
||||||
position: position,
|
|
||||||
position2D: position2D,
|
|
||||||
normal: normal,
|
|
||||||
valid: true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nearestStylusTarget;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.stealTouchFocus = function(stylusTarget) {
|
this.stealTouchFocus = function(stylusTarget) {
|
||||||
// send hover events to target
|
// send hover events to target
|
||||||
// record the entity or overlay we are hovering over.
|
// record the entity or overlay we are hovering over.
|
||||||
|
@ -1577,8 +1586,23 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.pointFinger = function(value) {
|
||||||
|
var HIFI_POINT_INDEX_MESSAGE_CHANNEL = "Hifi-Point-Index";
|
||||||
|
if (this.fingerPointing !== value) {
|
||||||
|
var message;
|
||||||
|
if (this.hand === RIGHT_HAND) {
|
||||||
|
message = { pointRightIndex: value };
|
||||||
|
} else {
|
||||||
|
message = { pointLeftIndex: value };
|
||||||
|
}
|
||||||
|
Messages.sendMessage(HIFI_POINT_INDEX_MESSAGE_CHANNEL, JSON.stringify(message), true);
|
||||||
|
this.fingerPointing = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
this.processStylus = function() {
|
this.processStylus = function() {
|
||||||
if (!this.stylusTip.valid) {
|
if (!this.stylusTip.valid) {
|
||||||
|
this.pointFinger(false);
|
||||||
this.hideStylus();
|
this.hideStylus();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1615,20 +1639,53 @@ function MyController(hand) {
|
||||||
candidates.overlays.push(HMD.homeButtonID);
|
candidates.overlays.push(HMD.homeButtonID);
|
||||||
}
|
}
|
||||||
|
|
||||||
var nearestStylusTarget = this.calculateNearestStylusTargetFromCandidates(candidates, true);
|
// build list of stylus targets
|
||||||
|
var stylusTargets = [];
|
||||||
|
if (candidates.entities.length > 0 || candidates.overlays.length > 0) {
|
||||||
|
var stylusTarget;
|
||||||
|
for (i = 0; i < candidates.entities.length; i++) {
|
||||||
|
stylusTarget = calculateStylusTargetFromEntity(this.stylusTip, candidates.entities[i]);
|
||||||
|
if (stylusTarget) {
|
||||||
|
stylusTargets.push(stylusTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.useFingerInsteadOfStylus && nearestStylusTarget.valid) {
|
for (i = 0; i < candidates.overlays.length; i++) {
|
||||||
this.showStylus();
|
stylusTarget = calculateStylusTargetFromOverlay(this.stylusTip, candidates.overlays[i]);
|
||||||
} else {
|
if (stylusTarget) {
|
||||||
this.hideStylus();
|
stylusTargets.push(stylusTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var TABLET_MIN_HOVER_DISTANCE = 0.01;
|
var TABLET_MIN_HOVER_DISTANCE = 0.01;
|
||||||
var TABLET_MAX_HOVER_DISTANCE = 0.1;
|
var TABLET_MAX_HOVER_DISTANCE = 0.1;
|
||||||
var TABLET_MIN_TOUCH_DISTANCE = -0.05;
|
var TABLET_MIN_TOUCH_DISTANCE = -0.05;
|
||||||
var TABLET_MAX_TOUCH_DISTANCE = TABLET_MIN_HOVER_DISTANCE;
|
var TABLET_MAX_TOUCH_DISTANCE = TABLET_MIN_HOVER_DISTANCE;
|
||||||
|
var EDGE_BORDER = 0.075;
|
||||||
|
|
||||||
if (nearestStylusTarget.valid && nearestStylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE &&
|
var hysteresisOffset = 0.0;
|
||||||
|
if (this.isNearStylusTarget) {
|
||||||
|
hysteresisOffset = 0.05;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isNearStylusTarget = isNearStylusTarget(stylusTargets, EDGE_BORDER + hysteresisOffset,
|
||||||
|
TABLET_MIN_TOUCH_DISTANCE - hysteresisOffset, WEB_DISPLAY_STYLUS_DISTANCE + hysteresisOffset);
|
||||||
|
|
||||||
|
if (this.isNearStylusTarget) {
|
||||||
|
if (!this.useFingerInsteadOfStylus) {
|
||||||
|
this.showStylus();
|
||||||
|
} else {
|
||||||
|
this.pointFinger(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.hideStylus();
|
||||||
|
this.pointFinger(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
var nearestStylusTarget = calculateNearestStylusTarget(stylusTargets);
|
||||||
|
|
||||||
|
if (nearestStylusTarget && nearestStylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE &&
|
||||||
nearestStylusTarget.distance < TABLET_MAX_HOVER_DISTANCE) {
|
nearestStylusTarget.distance < TABLET_MAX_HOVER_DISTANCE) {
|
||||||
|
|
||||||
this.requestTouchFocus(nearestStylusTarget);
|
this.requestTouchFocus(nearestStylusTarget);
|
||||||
|
@ -1642,8 +1699,11 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter out presses when tip is moving away from tablet.
|
// filter out presses when tip is moving away from tablet.
|
||||||
|
// ensure that stylus is within bounding box by checking normalizedPosition
|
||||||
if (nearestStylusTarget.valid && nearestStylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE &&
|
if (nearestStylusTarget.valid && nearestStylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE &&
|
||||||
nearestStylusTarget.distance < TABLET_MAX_TOUCH_DISTANCE && Vec3.dot(this.stylusTip.velocity, nearestStylusTarget.normal) < 0) {
|
nearestStylusTarget.distance < TABLET_MAX_TOUCH_DISTANCE && Vec3.dot(this.stylusTip.velocity, nearestStylusTarget.normal) < 0 &&
|
||||||
|
nearestStylusTarget.normalizedPosition.x >= 0 && nearestStylusTarget.normalizedPosition.x <= 1 &&
|
||||||
|
nearestStylusTarget.normalizedPosition.y >= 0 && nearestStylusTarget.normalizedPosition.y <= 1) {
|
||||||
|
|
||||||
var name;
|
var name;
|
||||||
if (nearestStylusTarget.entityID) {
|
if (nearestStylusTarget.entityID) {
|
||||||
|
@ -3505,8 +3565,8 @@ function MyController(hand) {
|
||||||
this.touchingEnterStylusTarget = this.stylusTarget;
|
this.touchingEnterStylusTarget = this.stylusTarget;
|
||||||
this.deadspotExpired = false;
|
this.deadspotExpired = false;
|
||||||
|
|
||||||
var TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.025;
|
var TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.0381;
|
||||||
this.deadspotRadius = TOUCH_PRESS_TO_MOVE_DEADSPOT; // meters
|
this.deadspotRadius = TOUCH_PRESS_TO_MOVE_DEADSPOT;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.stylusTouchingExit = function () {
|
this.stylusTouchingExit = function () {
|
||||||
|
@ -3528,26 +3588,29 @@ function MyController(hand) {
|
||||||
|
|
||||||
this.touchingEnterTimer += dt;
|
this.touchingEnterTimer += dt;
|
||||||
|
|
||||||
var candidates = { entities: [], overlays: [] };
|
if (this.stylusTarget.entityID) {
|
||||||
if (this.stylusTarget.entityID && this.stylusTarget.entityID !== NULL_UUID) {
|
entityPropertiesCache.addEntity(this.stylusTarget.entityID);
|
||||||
candidates.entities.push(this.stylusTarget.entityID);
|
this.stylusTarget = calcualteStylusTargetFromEntity(this.stylusTip, this.stylusTarget.entityID);
|
||||||
} else if (this.stylusTarget.overlayID && this.stylusTarget.overlayID !== NULL_UUID) {
|
} else if (this.stylusTarget.overlayID) {
|
||||||
candidates.overlays.push(this.stylusTarget.overlayID);
|
this.stylusTarget = calculateStylusTargetFromOverlay(this.stylusTip, this.stylusTarget.overlayID);
|
||||||
}
|
}
|
||||||
this.stylusTarget = this.calculateNearestStylusTargetFromCandidates(candidates, false);
|
|
||||||
|
|
||||||
var TABLET_MIN_TOUCH_DISTANCE = -0.1;
|
var TABLET_MIN_TOUCH_DISTANCE = -0.1;
|
||||||
var TABLET_MAX_TOUCH_DISTANCE = 0.01;
|
var TABLET_MAX_TOUCH_DISTANCE = 0.01;
|
||||||
|
|
||||||
if (this.stylusTarget.valid && this.stylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE && this.stylusTarget.distance < TABLET_MAX_TOUCH_DISTANCE) {
|
if (this.stylusTarget) {
|
||||||
|
if (this.stylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE && this.stylusTarget.distance < TABLET_MAX_TOUCH_DISTANCE) {
|
||||||
var POINTER_PRESS_TO_MOVE_DELAY = 0.25; // seconds
|
var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds
|
||||||
if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY ||
|
if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY ||
|
||||||
Vec3.distance(this.stylusTarget.position, this.touchingEnterPointerEvent.pos3D) > this.deadspotRadius) {
|
distance2D(this.stylusTarget.position2D, this.touchingEnterStylusTarget.position2D) > this.deadspotRadius) {
|
||||||
sendTouchMoveEventToStylusTarget(this.hand, this.stylusTarget);
|
sendTouchMoveEventToStylusTarget(this.hand, this.stylusTarget);
|
||||||
|
this.deadspotExpired = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.setState(STATE_OFF, "hand moved away from touch surface");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.setState(STATE_OFF, "hand moved away from tablet");
|
this.setState(STATE_OFF, "touch surface was destroyed");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
// 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
|
||||||
//
|
//
|
||||||
|
/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */
|
||||||
|
|
||||||
(function() { // BEGIN LOCAL_SCOPE
|
(function() { // BEGIN LOCAL_SCOPE
|
||||||
|
|
||||||
|
@ -25,7 +26,11 @@ var OVERLAY_RAMP_RATE = 8.0;
|
||||||
|
|
||||||
var animStateHandlerID;
|
var animStateHandlerID;
|
||||||
|
|
||||||
var isBothIndexesPointing = false;
|
var leftIndexPointingOverride = 0;
|
||||||
|
var rightIndexPointingOverride = 0;
|
||||||
|
var leftThumbRaisedOverride = 0;
|
||||||
|
var rightThumbRaisedOverride = 0;
|
||||||
|
|
||||||
var HIFI_POINT_INDEX_MESSAGE_CHANNEL = "Hifi-Point-Index";
|
var HIFI_POINT_INDEX_MESSAGE_CHANNEL = "Hifi-Point-Index";
|
||||||
|
|
||||||
var isLeftIndexPointing = false;
|
var isLeftIndexPointing = false;
|
||||||
|
@ -53,7 +58,7 @@ function init() {
|
||||||
"leftHandOverlayAlpha", "leftHandGraspAlpha",
|
"leftHandOverlayAlpha", "leftHandGraspAlpha",
|
||||||
"rightHandOverlayAlpha", "rightHandGraspAlpha",
|
"rightHandOverlayAlpha", "rightHandGraspAlpha",
|
||||||
"isLeftHandGrasp", "isLeftIndexPoint", "isLeftThumbRaise", "isLeftIndexPointAndThumbRaise",
|
"isLeftHandGrasp", "isLeftIndexPoint", "isLeftThumbRaise", "isLeftIndexPointAndThumbRaise",
|
||||||
"isRightHandGrasp", "isRightIndexPoint", "isRightThumbRaise", "isRightIndexPointAndThumbRaise",
|
"isRightHandGrasp", "isRightIndexPoint", "isRightThumbRaise", "isRightIndexPointAndThumbRaise"
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
Messages.subscribe(HIFI_POINT_INDEX_MESSAGE_CHANNEL);
|
Messages.subscribe(HIFI_POINT_INDEX_MESSAGE_CHANNEL);
|
||||||
|
@ -66,21 +71,23 @@ function animStateHandler(props) {
|
||||||
leftHandGraspAlpha: lastLeftTrigger,
|
leftHandGraspAlpha: lastLeftTrigger,
|
||||||
rightHandOverlayAlpha: rightHandOverlayAlpha,
|
rightHandOverlayAlpha: rightHandOverlayAlpha,
|
||||||
rightHandGraspAlpha: lastRightTrigger,
|
rightHandGraspAlpha: lastRightTrigger,
|
||||||
isLeftHandGrasp: !isBothIndexesPointing && !isLeftIndexPointing && !isLeftThumbRaised,
|
|
||||||
isLeftIndexPoint: (isBothIndexesPointing || isLeftIndexPointing) && !isLeftThumbRaised,
|
isLeftHandGrasp: !isLeftIndexPointing && !isLeftThumbRaised,
|
||||||
isLeftThumbRaise: !isBothIndexesPointing && !isLeftIndexPointing && isLeftThumbRaised,
|
isLeftIndexPoint: isLeftIndexPointing && !isLeftThumbRaised,
|
||||||
isLeftIndexPointAndThumbRaise: (isBothIndexesPointing || isLeftIndexPointing) && isLeftThumbRaised,
|
isLeftThumbRaise: !isLeftIndexPointing && isLeftThumbRaised,
|
||||||
isRightHandGrasp: !isBothIndexesPointing && !isRightIndexPointing && !isRightThumbRaised,
|
isLeftIndexPointAndThumbRaise: isLeftIndexPointing && isLeftThumbRaised,
|
||||||
isRightIndexPoint: (isBothIndexesPointing || isRightIndexPointing) && !isRightThumbRaised,
|
|
||||||
isRightThumbRaise: !isBothIndexesPointing && !isRightIndexPointing && isRightThumbRaised,
|
isRightHandGrasp: !isRightIndexPointing && !isRightThumbRaised,
|
||||||
isRightIndexPointAndThumbRaise: (isBothIndexesPointing || isRightIndexPointing) && isRightThumbRaised
|
isRightIndexPoint: isRightIndexPointing && !isRightThumbRaised,
|
||||||
|
isRightThumbRaise: !isRightIndexPointing && isRightThumbRaised,
|
||||||
|
isRightIndexPointAndThumbRaise: isRightIndexPointing && isRightThumbRaised
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function update(dt) {
|
function update(dt) {
|
||||||
var leftTrigger = clamp(Controller.getValue(Controller.Standard.LT) + Controller.getValue(Controller.Standard.LeftGrip), 0, 1);
|
var leftTrigger = clamp(Controller.getValue(Controller.Standard.LT) + Controller.getValue(Controller.Standard.LeftGrip), 0, 1);
|
||||||
var rightTrigger = clamp(Controller.getValue(Controller.Standard.RT) + Controller.getValue(Controller.Standard.RightGrip), 0, 1);
|
var rightTrigger = clamp(Controller.getValue(Controller.Standard.RT) + Controller.getValue(Controller.Standard.RightGrip), 0, 1);
|
||||||
|
|
||||||
// Average last few trigger values together for a bit of smoothing
|
// Average last few trigger values together for a bit of smoothing
|
||||||
var tau = clamp(dt / TRIGGER_SMOOTH_TIMESCALE, 0, 1);
|
var tau = clamp(dt / TRIGGER_SMOOTH_TIMESCALE, 0, 1);
|
||||||
lastLeftTrigger = lerp(leftTrigger, lastLeftTrigger, tau);
|
lastLeftTrigger = lerp(leftTrigger, lastLeftTrigger, tau);
|
||||||
|
@ -103,18 +110,61 @@ function update(dt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pointing index fingers and raising thumbs
|
// Pointing index fingers and raising thumbs
|
||||||
isLeftIndexPointing = leftHandPose.valid && Controller.getValue(Controller.Standard.LeftIndexPoint) === 1;
|
isLeftIndexPointing = (leftIndexPointingOverride > 0) || (leftHandPose.valid && Controller.getValue(Controller.Standard.LeftIndexPoint) === 1);
|
||||||
isRightIndexPointing = rightHandPose.valid && Controller.getValue(Controller.Standard.RightIndexPoint) === 1;
|
isRightIndexPointing = (rightIndexPointingOverride > 0) || (rightHandPose.valid && Controller.getValue(Controller.Standard.RightIndexPoint) === 1);
|
||||||
isLeftThumbRaised = leftHandPose.valid && Controller.getValue(Controller.Standard.LeftThumbUp) === 1;
|
isLeftThumbRaised = (leftThumbRaisedOverride > 0) || (leftHandPose.valid && Controller.getValue(Controller.Standard.LeftThumbUp) === 1);
|
||||||
isRightThumbRaised = rightHandPose.valid && Controller.getValue(Controller.Standard.RightThumbUp) === 1;
|
isRightThumbRaised = (rightThumbRaisedOverride > 0) || (rightHandPose.valid && Controller.getValue(Controller.Standard.RightThumbUp) === 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMessages(channel, message, sender) {
|
function handleMessages(channel, message, sender) {
|
||||||
if (sender === MyAvatar.sessionUUID && channel === HIFI_POINT_INDEX_MESSAGE_CHANNEL) {
|
if (sender === MyAvatar.sessionUUID && channel === HIFI_POINT_INDEX_MESSAGE_CHANNEL) {
|
||||||
var data = JSON.parse(message);
|
var data = JSON.parse(message);
|
||||||
|
|
||||||
if (data.pointIndex !== undefined) {
|
if (data.pointIndex !== undefined) {
|
||||||
print("pointIndex: " + data.pointIndex);
|
if (data.pointIndex) {
|
||||||
isBothIndexesPointing = data.pointIndex;
|
leftIndexPointingOverride++;
|
||||||
|
rightIndexPointingOverride++;
|
||||||
|
} else {
|
||||||
|
leftIndexPointingOverride--;
|
||||||
|
rightIndexPointingOverride--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.pointLeftIndex !== undefined) {
|
||||||
|
if (data.pointLeftIndex) {
|
||||||
|
leftIndexPointingOverride++;
|
||||||
|
} else {
|
||||||
|
leftIndexPointingOverride--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.pointRightIndex !== undefined) {
|
||||||
|
if (data.pointRightIndex) {
|
||||||
|
rightIndexPointingOverride++;
|
||||||
|
} else {
|
||||||
|
rightIndexPointingOverride--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.raiseThumbs !== undefined) {
|
||||||
|
if (data.raiseThumbs) {
|
||||||
|
leftThumbRaisedOverride++;
|
||||||
|
rightThumbRaisedOverride++;
|
||||||
|
} else {
|
||||||
|
leftThumbRaisedOverride--;
|
||||||
|
rightThumbRaisedOverride--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.raiseLeftThumb !== undefined) {
|
||||||
|
if (data.raiseLeftThumb) {
|
||||||
|
leftThumbRaisedOverride++;
|
||||||
|
} else {
|
||||||
|
leftThumbRaisedOverride--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.raiseRightThumb !== undefined) {
|
||||||
|
if (data.raiseRightThumb) {
|
||||||
|
rightThumbRaisedOverride++;
|
||||||
|
} else {
|
||||||
|
rightThumbRaisedOverride--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
button,
|
button,
|
||||||
BUTTON_NAME = "PAINT",
|
BUTTON_NAME = "PAINT",
|
||||||
isFingerPainting = false,
|
isFingerPainting = false,
|
||||||
|
shouldPointFingers = false,
|
||||||
leftHand = null,
|
leftHand = null,
|
||||||
rightHand = null,
|
rightHand = null,
|
||||||
leftBrush = null,
|
leftBrush = null,
|
||||||
|
@ -308,9 +309,14 @@
|
||||||
Messages.sendMessage(HIFI_POINTER_DISABLE_MESSAGE_CHANNEL, JSON.stringify({
|
Messages.sendMessage(HIFI_POINTER_DISABLE_MESSAGE_CHANNEL, JSON.stringify({
|
||||||
pointerEnabled: enabled
|
pointerEnabled: enabled
|
||||||
}), true);
|
}), true);
|
||||||
Messages.sendMessage(HIFI_POINT_INDEX_MESSAGE_CHANNEL, JSON.stringify({
|
|
||||||
pointIndex: !enabled
|
var newShouldPointFingers = !enabled;
|
||||||
}), true);
|
if (newShouldPointFingers !== shouldPointFingers) {
|
||||||
|
Messages.sendMessage(HIFI_POINT_INDEX_MESSAGE_CHANNEL, JSON.stringify({
|
||||||
|
pointIndex: newShouldPointFingers
|
||||||
|
}), true);
|
||||||
|
shouldPointFingers = newShouldPointFingers;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableProcessing() {
|
function enableProcessing() {
|
||||||
|
@ -430,4 +436,4 @@
|
||||||
|
|
||||||
setUp();
|
setUp();
|
||||||
Script.scriptEnding.connect(tearDown);
|
Script.scriptEnding.connect(tearDown);
|
||||||
}());
|
}());
|
||||||
|
|
|
@ -171,6 +171,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly) {
|
||||||
this.homeButtonID = Overlays.addOverlay("sphere", {
|
this.homeButtonID = Overlays.addOverlay("sphere", {
|
||||||
name: "homeButton",
|
name: "homeButton",
|
||||||
localPosition: {x: -0.001, y: -HOME_BUTTON_Y_OFFSET, z: 0.0},
|
localPosition: {x: -0.001, y: -HOME_BUTTON_Y_OFFSET, z: 0.0},
|
||||||
|
localRotation: {x: 0, y: 1, z: 0, w: 0},
|
||||||
dimensions: { x: 4 * tabletScaleFactor, y: 4 * tabletScaleFactor, z: 4 * tabletScaleFactor},
|
dimensions: { x: 4 * tabletScaleFactor, y: 4 * tabletScaleFactor, z: 4 * tabletScaleFactor},
|
||||||
alpha: 0.0,
|
alpha: 0.0,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
|
Loading…
Reference in a new issue