mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-26 02:35:16 +02:00
286 lines
9.2 KiB
JavaScript
286 lines
9.2 KiB
JavaScript
//
|
|
// laser.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 Laser:true */
|
|
|
|
Laser = function (side) {
|
|
// Draws hand lasers.
|
|
// May intersect with overlays or entities, or bounding box of other hand's selection.
|
|
// Laser dot is always drawn on UI entities.
|
|
|
|
"use strict";
|
|
|
|
var isLaserEnabled = true,
|
|
isLaserOn = false,
|
|
|
|
laserLine = null,
|
|
laserSphere = null,
|
|
|
|
searchDistance = 0.0,
|
|
|
|
SEARCH_SPHERE_SIZE = 0.013, // Per farActionGrabEntity.js multiplied by 1.2 per farActionGrabEntity.js.
|
|
MINUMUM_SEARCH_SPHERE_SIZE = 0.006,
|
|
SEARCH_SPHERE_FOLLOW_RATE = 0.5,
|
|
COLORS_GRAB_SEARCHING_HALF_SQUEEZE = { red: 10, green: 10, blue: 255 }, // Per controllerDispatcherUtils.js.
|
|
COLORS_GRAB_SEARCHING_FULL_SQUEEZE = { red: 250, green: 10, blue: 10 }, // Per controllerDispatcherUtils.js.
|
|
COLORS_GRAB_SEARCHING_HALF_SQUEEZE_BRIGHT,
|
|
COLORS_GRAB_SEARCHING_FULL_SQUEEZE_BRIGHT,
|
|
BRIGHT_POW = 0.06, // Per old handControllerGrab.js.
|
|
|
|
GRAB_POINT_SPHERE_OFFSET = { x: 0.04, y: 0.13, z: 0.039 }, // Per HmdDisplayPlugin.cpp and controllers.js.
|
|
|
|
PICK_MAX_DISTANCE = 500, // Per controllerDispatcherUtils.js.
|
|
PRECISION_PICKING = true,
|
|
NO_INCLUDE_IDS = [],
|
|
NO_EXCLUDE_IDS = [],
|
|
VISIBLE_ONLY = true,
|
|
|
|
laserLength,
|
|
specifiedLaserLength = null,
|
|
laserSphereSize = 0,
|
|
|
|
LEFT_HAND = 0,
|
|
|
|
uiOverlayIDs = [],
|
|
|
|
intersection;
|
|
|
|
if (!(this instanceof Laser)) {
|
|
return new Laser(side);
|
|
}
|
|
|
|
function colorPow(color, power) { // Per old handControllerGrab.js.
|
|
return {
|
|
red: Math.pow(color.red / 255, power) * 255,
|
|
green: Math.pow(color.green / 255, power) * 255,
|
|
blue: Math.pow(color.blue / 255, power) * 255
|
|
};
|
|
}
|
|
|
|
COLORS_GRAB_SEARCHING_HALF_SQUEEZE_BRIGHT = colorPow(COLORS_GRAB_SEARCHING_HALF_SQUEEZE, BRIGHT_POW);
|
|
COLORS_GRAB_SEARCHING_FULL_SQUEEZE_BRIGHT = colorPow(COLORS_GRAB_SEARCHING_FULL_SQUEEZE, BRIGHT_POW);
|
|
|
|
if (side === LEFT_HAND) {
|
|
GRAB_POINT_SPHERE_OFFSET.x = -GRAB_POINT_SPHERE_OFFSET.x;
|
|
}
|
|
|
|
laserLine = Overlays.addOverlay("line3d", {
|
|
alpha: 1.0,
|
|
glow: 1.0,
|
|
ignoreRayIntersection: true,
|
|
drawInFront: true,
|
|
parentID: Uuid.SELF,
|
|
parentJointIndex: MyAvatar.getJointIndex(side === LEFT_HAND
|
|
? "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"
|
|
: "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND"),
|
|
visible: false
|
|
});
|
|
laserSphere = Overlays.addOverlay("circle3d", {
|
|
innerAlpha: 1.0,
|
|
outerAlpha: 0.0,
|
|
solid: true,
|
|
ignoreRayIntersection: true,
|
|
drawInFront: true,
|
|
visible: false
|
|
});
|
|
|
|
function updateLine(start, end, color) {
|
|
Overlays.editOverlay(laserLine, {
|
|
start: start,
|
|
end: end,
|
|
color: color,
|
|
visible: true
|
|
});
|
|
}
|
|
|
|
function updateSphere(location, size, color, brightColor) {
|
|
var rotation;
|
|
|
|
rotation = Quat.lookAt(location, Camera.getPosition(), Vec3.UP);
|
|
|
|
Overlays.editOverlay(laserSphere, {
|
|
position: location,
|
|
rotation: rotation,
|
|
innerColor: brightColor,
|
|
outerColor: color,
|
|
outerRadius: size,
|
|
visible: true
|
|
});
|
|
}
|
|
|
|
function display(origin, direction, distance, isPressed, isClicked) {
|
|
var searchTarget,
|
|
sphereSize,
|
|
color,
|
|
brightColor;
|
|
|
|
searchDistance = SEARCH_SPHERE_FOLLOW_RATE * searchDistance + (1.0 - SEARCH_SPHERE_FOLLOW_RATE) * distance;
|
|
searchTarget = Vec3.sum(origin, Vec3.multiply(searchDistance, direction));
|
|
sphereSize = Math.max(SEARCH_SPHERE_SIZE * searchDistance, MINUMUM_SEARCH_SPHERE_SIZE);
|
|
color = isClicked ? COLORS_GRAB_SEARCHING_FULL_SQUEEZE : COLORS_GRAB_SEARCHING_HALF_SQUEEZE;
|
|
brightColor = isClicked ? COLORS_GRAB_SEARCHING_FULL_SQUEEZE_BRIGHT : COLORS_GRAB_SEARCHING_HALF_SQUEEZE_BRIGHT;
|
|
|
|
if (isPressed) {
|
|
updateLine(origin, searchTarget, color);
|
|
} else {
|
|
Overlays.editOverlay(laserLine, { visible: false });
|
|
}
|
|
// Avoid flash from large laser sphere when turn on or suddenly increase distance. Rendering seems to update overlay
|
|
// position one frame behind so use sphere size from preceding frame.
|
|
updateSphere(searchTarget, laserSphereSize, color, brightColor);
|
|
laserSphereSize = sphereSize;
|
|
}
|
|
|
|
function hide() {
|
|
Overlays.editOverlay(laserLine, { visible: false });
|
|
Overlays.editOverlay(laserSphere, { visible: false });
|
|
laserSphereSize = 0;
|
|
}
|
|
|
|
function setUIOverlays(overlayIDs) {
|
|
uiOverlayIDs = overlayIDs;
|
|
}
|
|
|
|
function update(hand) {
|
|
var handPosition,
|
|
handOrientation,
|
|
deltaOrigin,
|
|
pickRay;
|
|
|
|
if (!isLaserEnabled) {
|
|
intersection = {};
|
|
return;
|
|
}
|
|
|
|
handPosition = hand.position();
|
|
handOrientation = hand.orientation();
|
|
deltaOrigin = Vec3.multiplyQbyV(handOrientation, GRAB_POINT_SPHERE_OFFSET);
|
|
pickRay = {
|
|
origin: Vec3.sum(handPosition, deltaOrigin),
|
|
direction: Quat.getUp(handOrientation),
|
|
length: PICK_MAX_DISTANCE
|
|
};
|
|
|
|
if (hand.triggerPressed()) {
|
|
|
|
// Normal laser operation with trigger.
|
|
intersection = Overlays.findRayIntersection(pickRay, PRECISION_PICKING, NO_INCLUDE_IDS, NO_EXCLUDE_IDS,
|
|
VISIBLE_ONLY);
|
|
if (Reticle.pointingAtSystemOverlay || (intersection.overlayID
|
|
&& [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID].indexOf(intersection.overlayID) !== -1)) {
|
|
// No laser if pointing at HUD overlay or tablet; system provides lasers for these cases.
|
|
if (isLaserOn) {
|
|
isLaserOn = false;
|
|
hide();
|
|
}
|
|
} else {
|
|
if (!intersection.intersects) {
|
|
intersection = Entities.findRayIntersection(pickRay, PRECISION_PICKING, NO_INCLUDE_IDS, NO_EXCLUDE_IDS,
|
|
VISIBLE_ONLY);
|
|
intersection.editableEntity = intersection.intersects && Entities.hasEditableRoot(intersection.entityID);
|
|
intersection.overlayID = null;
|
|
}
|
|
intersection.laserIntersected = intersection.intersects;
|
|
laserLength = (specifiedLaserLength !== null)
|
|
? specifiedLaserLength
|
|
: (intersection.intersects ? intersection.distance : PICK_MAX_DISTANCE);
|
|
isLaserOn = true;
|
|
display(pickRay.origin, pickRay.direction, laserLength, true, hand.triggerClicked());
|
|
}
|
|
|
|
} else if (uiOverlayIDs.length > 0) {
|
|
|
|
// Special UI cursor.
|
|
intersection = Overlays.findRayIntersection(pickRay, PRECISION_PICKING, uiOverlayIDs, NO_EXCLUDE_IDS,
|
|
VISIBLE_ONLY);
|
|
if (intersection.intersects) {
|
|
intersection.laserIntersected = true;
|
|
laserLength = (specifiedLaserLength !== null)
|
|
? specifiedLaserLength
|
|
: (intersection.intersects ? intersection.distance : PICK_MAX_DISTANCE);
|
|
if (!isLaserOn) {
|
|
// Start laser dot at UI distance.
|
|
searchDistance = laserLength;
|
|
}
|
|
isLaserOn = true;
|
|
display(pickRay.origin, pickRay.direction, laserLength, false, false);
|
|
} else if (isLaserOn) {
|
|
isLaserOn = false;
|
|
hide();
|
|
}
|
|
|
|
} else {
|
|
intersection = { intersects: false };
|
|
if (isLaserOn) {
|
|
isLaserOn = false;
|
|
hide();
|
|
}
|
|
}
|
|
}
|
|
|
|
function getIntersection() {
|
|
return intersection;
|
|
}
|
|
|
|
function setLength(length) {
|
|
specifiedLaserLength = length;
|
|
laserLength = length;
|
|
}
|
|
|
|
function clearLength() {
|
|
specifiedLaserLength = null;
|
|
}
|
|
|
|
function getLength() {
|
|
return laserLength;
|
|
}
|
|
|
|
function handOffset() {
|
|
return GRAB_POINT_SPHERE_OFFSET;
|
|
}
|
|
|
|
function clear() {
|
|
isLaserOn = false;
|
|
hide();
|
|
}
|
|
|
|
function enable() {
|
|
isLaserEnabled = true;
|
|
}
|
|
|
|
function disable() {
|
|
isLaserEnabled = false;
|
|
if (isLaserOn) {
|
|
hide();
|
|
}
|
|
isLaserOn = false;
|
|
}
|
|
|
|
function destroy() {
|
|
Overlays.deleteOverlay(laserLine);
|
|
Overlays.deleteOverlay(laserSphere);
|
|
}
|
|
|
|
return {
|
|
setUIOverlays: setUIOverlays,
|
|
update: update,
|
|
intersection: getIntersection,
|
|
setLength: setLength,
|
|
clearLength: clearLength,
|
|
length: getLength,
|
|
enable: enable,
|
|
disable: disable,
|
|
handOffset: handOffset,
|
|
clear: clear,
|
|
destroy: destroy
|
|
};
|
|
};
|
|
|
|
Laser.prototype = {};
|