mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 23:55:12 +02:00
Merge pull request #9696 from ctrlaltdavid/21168
Add finger painting script
This commit is contained in:
commit
fa0749b494
5 changed files with 583 additions and 2 deletions
66
interface/resources/icons/tablet-icons/finger-paint-a.svg
Normal file
66
interface/resources/icons/tablet-icons/finger-paint-a.svg
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.1"
|
||||||
|
id="Layer_1"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
viewBox="0 0 50 50"
|
||||||
|
style="enable-background:new 0 0 50 50;"
|
||||||
|
xml:space="preserve"
|
||||||
|
sodipodi:docname="finger-paint-a.svg"
|
||||||
|
inkscape:version="0.92.0 r15299"><metadata
|
||||||
|
id="metadata28"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs26" /><sodipodi:namedview
|
||||||
|
pagecolor="#ff0000"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1054"
|
||||||
|
inkscape:window-height="851"
|
||||||
|
id="namedview24"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="8.88"
|
||||||
|
inkscape:cx="6.7004505"
|
||||||
|
inkscape:cy="25"
|
||||||
|
inkscape:window-x="120"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:current-layer="Layer_1" /><style
|
||||||
|
type="text/css"
|
||||||
|
id="style10">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
</style><g
|
||||||
|
id="Layer_2" /><g
|
||||||
|
id="g15"
|
||||||
|
style="fill:#000000;fill-opacity:1"><path
|
||||||
|
class="st0"
|
||||||
|
d="M18.3,19.6c0.7,0.1,1.2,0.5,1.4,1.1c0.4,1.3,2,5.6,2.4,6.9c2.2,0,7.9,0.1,9.9,0.1c0.2,0,0.6,0,0.8,0.1 c0.5,0.1,0.8,0.4,1.1,0.9c0.1,0.2,0.2,0.4,0.2,0.6c0.8,2.9,0.7,2,1.5,4.9c0.8,3,0.1,5.7-2.3,7.7c-1.9,1.6-3.6,2.3-5.7,1.9 c-0.6-0.1-1.2-0.3-1.8-0.5c-3.6-1.5-7.3-2.9-10.9-4.3c-0.9-0.4-1.4-1.2-1.3-2c0.1-0.9,0.9-1.5,1.8-1.6c0.1,0,0.3,0,0.4,0 c0.2,0,0.4,0.1,0.7,0.2c1.6,0.6,2.6,1.4,4.2,2c0,0,0,0,0,0c0,0,0.1,0,0.2,0c-0.1-0.2-0.1-0.4-0.2-0.6c-1.4-4.9-2.9-9.8-4.3-14.7 c-0.2-0.7-0.3-1.4,0.2-2c0.4-0.6,1-0.8,1.7-0.8C18.2,19.5,18.2,19.5,18.3,19.6 M18.9,16.3c-0.1,0-0.3,0-0.4-0.1 c-1.9-0.2-3.6,0.6-4.7,2.1c-1.5,2-0.9,4.1-0.7,4.8c0.9,3,1.8,6,2.6,9c-0.1,0-0.3,0-0.4,0c-2.5,0-4.6,1.9-5,4.3 c-0.2,1.2,0.1,2.4,0.7,3.4c0.6,1,1.5,1.7,2.7,2.2c1.2,0.5,2.4,0.9,3.6,1.4c2.4,0.9,4.9,1.9,7.3,2.9c0.9,0.4,1.7,0.6,2.5,0.7 c3.9,0.7,6.7-1.2,8.5-2.7c3.4-2.8,4.6-6.7,3.4-11.1c-0.5-1.7-0.6-2.1-0.8-2.7c-0.1-0.4-0.3-1-0.7-2.2c-0.1-0.4-0.2-0.7-0.4-1.1 c-0.7-1.5-1.8-2.5-3.4-2.7c-0.6-0.1-1.2-0.1-1.8-0.1c-1.2,0-3.4,0-5.4,0c-0.6,0-1.3-0.1-1.9-0.1c-0.2-0.6-0.5-1.3-0.7-2 c-0.4-1.1-0.7-2.1-0.9-2.6C22.3,17.9,20.8,16.7,18.9,16.3L18.9,16.3z"
|
||||||
|
id="path13"
|
||||||
|
style="fill:#000000;fill-opacity:1" /></g><path
|
||||||
|
class="st0"
|
||||||
|
d="M35.1,4.3c0.5,0,1,0,1.5,0c0.6,0,1.3-0.4,1.3-1.1c0-0.6-0.5-1.2-1.2-1.2c-0.5,0-1,0-1.5,0 c-0.6,0-1.3,0.4-1.3,1.1C33.9,3.6,34.4,4.3,35.1,4.3L35.1,4.3z"
|
||||||
|
id="path17"
|
||||||
|
style="fill:#000000;fill-opacity:1" /><path
|
||||||
|
class="st0"
|
||||||
|
d="M32,7.7c-1.2-0.8-0.9-2.1,0.1-3c1.2-1-0.6-2.6-1.7-1.6c-1,0.8-1.6,2-1.6,3.2c0,1.4,0.8,2.5,2,3.2 c0.5,0.3,1.4,0.1,1.7-0.4C32.8,8.7,32.5,8.1,32,7.7L32,7.7z"
|
||||||
|
id="path19"
|
||||||
|
style="fill:#000000;fill-opacity:1" /><path
|
||||||
|
class="st0"
|
||||||
|
d="M35.4,9.6c-1.4-0.7-2.6,1.3-1.2,1.9c1,0.5,1.8,1.2,2,2.1c0,0,0,0.2,0,0.2c0,0.1,0,0.2,0,0.3c0,0,0,0.2-0.1,0.2 c0,0,0,0,0,0.1c0,0-0.1,0.1-0.1,0.1c-0.2,0.3-0.6,0.6-1,0.8c-1.3,0.5-2.8,0.2-4.1-0.2c-1.4-0.5-2.7-1.1-4.1-1.7 c-3.2-1.3-6.6-2.6-10.1-2.5c-2.8,0.1-6.1,1.1-7.4,3.5c-1.1,2.1-0.4,4.5,1.4,5.8c0-0.8,0.4-1.7,0.9-2.3c-0.7-0.9-0.7-2.2,0.3-3.1 c1.9-1.9,5.3-1.9,7.8-1.4c3.1,0.6,5.9,2,8.8,3.2c2.8,1.1,6.5,2.1,9-0.2C39.9,14.2,38,10.8,35.4,9.6z"
|
||||||
|
id="path21"
|
||||||
|
style="fill:#000000;fill-opacity:1" /></svg>
|
After Width: | Height: | Size: 3.9 KiB |
30
interface/resources/icons/tablet-icons/finger-paint-i.svg
Normal file
30
interface/resources/icons/tablet-icons/finger-paint-i.svg
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<g id="Layer_2">
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M18.3,19.6c0.7,0.1,1.2,0.5,1.4,1.1c0.4,1.3,2,5.6,2.4,6.9c2.2,0,7.9,0.1,9.9,0.1c0.2,0,0.6,0,0.8,0.1
|
||||||
|
c0.5,0.1,0.8,0.4,1.1,0.9c0.1,0.2,0.2,0.4,0.2,0.6c0.8,2.9,0.7,2,1.5,4.9c0.8,3,0.1,5.7-2.3,7.7c-1.9,1.6-3.6,2.3-5.7,1.9
|
||||||
|
c-0.6-0.1-1.2-0.3-1.8-0.5c-3.6-1.5-7.3-2.9-10.9-4.3c-0.9-0.4-1.4-1.2-1.3-2c0.1-0.9,0.9-1.5,1.8-1.6c0.1,0,0.3,0,0.4,0
|
||||||
|
c0.2,0,0.4,0.1,0.7,0.2c1.6,0.6,2.6,1.4,4.2,2c0,0,0,0,0,0c0,0,0.1,0,0.2,0c-0.1-0.2-0.1-0.4-0.2-0.6c-1.4-4.9-2.9-9.8-4.3-14.7
|
||||||
|
c-0.2-0.7-0.3-1.4,0.2-2c0.4-0.6,1-0.8,1.7-0.8C18.2,19.5,18.2,19.5,18.3,19.6 M18.9,16.3c-0.1,0-0.3,0-0.4-0.1
|
||||||
|
c-1.9-0.2-3.6,0.6-4.7,2.1c-1.5,2-0.9,4.1-0.7,4.8c0.9,3,1.8,6,2.6,9c-0.1,0-0.3,0-0.4,0c-2.5,0-4.6,1.9-5,4.3
|
||||||
|
c-0.2,1.2,0.1,2.4,0.7,3.4c0.6,1,1.5,1.7,2.7,2.2c1.2,0.5,2.4,0.9,3.6,1.4c2.4,0.9,4.9,1.9,7.3,2.9c0.9,0.4,1.7,0.6,2.5,0.7
|
||||||
|
c3.9,0.7,6.7-1.2,8.5-2.7c3.4-2.8,4.6-6.7,3.4-11.1c-0.5-1.7-0.6-2.1-0.8-2.7c-0.1-0.4-0.3-1-0.7-2.2c-0.1-0.4-0.2-0.7-0.4-1.1
|
||||||
|
c-0.7-1.5-1.8-2.5-3.4-2.7c-0.6-0.1-1.2-0.1-1.8-0.1c-1.2,0-3.4,0-5.4,0c-0.6,0-1.3-0.1-1.9-0.1c-0.2-0.6-0.5-1.3-0.7-2
|
||||||
|
c-0.4-1.1-0.7-2.1-0.9-2.6C22.3,17.9,20.8,16.7,18.9,16.3L18.9,16.3z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st0" d="M35.1,4.3c0.5,0,1,0,1.5,0c0.6,0,1.3-0.4,1.3-1.1c0-0.6-0.5-1.2-1.2-1.2c-0.5,0-1,0-1.5,0
|
||||||
|
c-0.6,0-1.3,0.4-1.3,1.1C33.9,3.6,34.4,4.3,35.1,4.3L35.1,4.3z"/>
|
||||||
|
<path class="st0" d="M32,7.7c-1.2-0.8-0.9-2.1,0.1-3c1.2-1-0.6-2.6-1.7-1.6c-1,0.8-1.6,2-1.6,3.2c0,1.4,0.8,2.5,2,3.2
|
||||||
|
c0.5,0.3,1.4,0.1,1.7-0.4C32.8,8.7,32.5,8.1,32,7.7L32,7.7z"/>
|
||||||
|
<path class="st0" d="M35.4,9.6c-1.4-0.7-2.6,1.3-1.2,1.9c1,0.5,1.8,1.2,2,2.1c0,0,0,0.2,0,0.2c0,0.1,0,0.2,0,0.3c0,0,0,0.2-0.1,0.2
|
||||||
|
c0,0,0,0,0,0.1c0,0-0.1,0.1-0.1,0.1c-0.2,0.3-0.6,0.6-1,0.8c-1.3,0.5-2.8,0.2-4.1-0.2c-1.4-0.5-2.7-1.1-4.1-1.7
|
||||||
|
c-3.2-1.3-6.6-2.6-10.1-2.5c-2.8,0.1-6.1,1.1-7.4,3.5c-1.1,2.1-0.4,4.5,1.4,5.8c0-0.8,0.4-1.7,0.9-2.3c-0.7-0.9-0.7-2.2,0.3-3.1
|
||||||
|
c1.9-1.9,5.3-1.9,7.8-1.4c3.1,0.6,5.9,2,8.8,3.2c2.8,1.1,6.5,2.1,9-0.2C39.9,14.2,38,10.8,35.4,9.6z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
|
@ -480,6 +480,10 @@ var LASER_SEARCH_COLOR_XYZW = {x: 10 / 255, y: 10 / 255, z: 255 / 255, w: LASER_
|
||||||
var LASER_TRIGGER_COLOR_XYZW = {x: 250 / 255, y: 10 / 255, z: 10 / 255, w: LASER_ALPHA};
|
var LASER_TRIGGER_COLOR_XYZW = {x: 250 / 255, y: 10 / 255, z: 10 / 255, w: LASER_ALPHA};
|
||||||
var SYSTEM_LASER_DIRECTION = {x: 0, y: 0, z: -1};
|
var SYSTEM_LASER_DIRECTION = {x: 0, y: 0, z: -1};
|
||||||
var systemLaserOn = false;
|
var systemLaserOn = false;
|
||||||
|
|
||||||
|
var HIFI_POINTER_DISABLE_MESSAGE_CHANNEL = "Hifi-Pointer-Disable";
|
||||||
|
var isPointerEnabled = true;
|
||||||
|
|
||||||
function clearSystemLaser() {
|
function clearSystemLaser() {
|
||||||
if (!systemLaserOn) {
|
if (!systemLaserOn) {
|
||||||
return;
|
return;
|
||||||
|
@ -542,9 +546,8 @@ function update() {
|
||||||
return off();
|
return off();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// If there's a HUD element at the (newly moved) reticle, just make it visible and bail.
|
// If there's a HUD element at the (newly moved) reticle, just make it visible and bail.
|
||||||
if (isPointingAtOverlay(hudPoint2d)) {
|
if (isPointingAtOverlay(hudPoint2d) && isPointerEnabled) {
|
||||||
if (HMD.active) {
|
if (HMD.active) {
|
||||||
Reticle.depth = hudReticleDistance();
|
Reticle.depth = hudReticleDistance();
|
||||||
|
|
||||||
|
@ -579,9 +582,25 @@ function checkSettings() {
|
||||||
}
|
}
|
||||||
checkSettings();
|
checkSettings();
|
||||||
|
|
||||||
|
// Enable/disable pointer.
|
||||||
|
function handleMessages(channel, message, sender) {
|
||||||
|
if (sender === MyAvatar.sessionUUID && channel === HIFI_POINTER_DISABLE_MESSAGE_CHANNEL) {
|
||||||
|
var data = JSON.parse(message);
|
||||||
|
if (data.pointerEnabled !== undefined) {
|
||||||
|
print("pointerEnabled: " + data.pointerEnabled);
|
||||||
|
isPointerEnabled = data.pointerEnabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Messages.subscribe(HIFI_POINTER_DISABLE_MESSAGE_CHANNEL);
|
||||||
|
Messages.messageReceived.connect(handleMessages);
|
||||||
|
|
||||||
var settingsChecker = Script.setInterval(checkSettings, SETTINGS_CHANGE_RECHECK_INTERVAL);
|
var settingsChecker = Script.setInterval(checkSettings, SETTINGS_CHANGE_RECHECK_INTERVAL);
|
||||||
Script.update.connect(update);
|
Script.update.connect(update);
|
||||||
Script.scriptEnding.connect(function () {
|
Script.scriptEnding.connect(function () {
|
||||||
|
Messages.unsubscribe(HIFI_POINTER_DISABLE_MESSAGE_CHANNEL);
|
||||||
|
Messages.messageReceived.disconnect(handleMessages);
|
||||||
Script.clearInterval(settingsChecker);
|
Script.clearInterval(settingsChecker);
|
||||||
Script.update.disconnect(update);
|
Script.update.disconnect(update);
|
||||||
OffscreenFlags.navigationFocusDisabled = false;
|
OffscreenFlags.navigationFocusDisabled = false;
|
||||||
|
|
|
@ -25,6 +25,11 @@ var OVERLAY_RAMP_RATE = 8.0;
|
||||||
|
|
||||||
var animStateHandlerID;
|
var animStateHandlerID;
|
||||||
|
|
||||||
|
var isPointingIndex = false;
|
||||||
|
var HIFI_POINT_INDEX_MESSAGE_CHANNEL = "Hifi-Point-Index";
|
||||||
|
|
||||||
|
var indexfingerJointNames = ["LeftHandIndex1", "LeftHandIndex2", "LeftHandIndex3", "RightHandIndex1", "RightHandIndex2", "RightHandIndex3"];
|
||||||
|
|
||||||
function clamp(val, min, max) {
|
function clamp(val, min, max) {
|
||||||
return Math.min(Math.max(val, min), max);
|
return Math.min(Math.max(val, min), max);
|
||||||
}
|
}
|
||||||
|
@ -43,6 +48,8 @@ function init() {
|
||||||
animStateHandler,
|
animStateHandler,
|
||||||
["leftHandOverlayAlpha", "rightHandOverlayAlpha", "leftHandGraspAlpha", "rightHandGraspAlpha"]
|
["leftHandOverlayAlpha", "rightHandOverlayAlpha", "leftHandGraspAlpha", "rightHandGraspAlpha"]
|
||||||
);
|
);
|
||||||
|
Messages.subscribe(HIFI_POINT_INDEX_MESSAGE_CHANNEL);
|
||||||
|
Messages.messageReceived.connect(handleMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
function animStateHandler(props) {
|
function animStateHandler(props) {
|
||||||
|
@ -76,11 +83,37 @@ function update(dt) {
|
||||||
} else {
|
} else {
|
||||||
rightHandOverlayAlpha = clamp(rightHandOverlayAlpha - OVERLAY_RAMP_RATE * dt, 0, 1);
|
rightHandOverlayAlpha = clamp(rightHandOverlayAlpha - OVERLAY_RAMP_RATE * dt, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Point index finger.
|
||||||
|
if (isPointingIndex) {
|
||||||
|
var zeroRotation = { x: 0, y: 0, z: 0, w: 1 };
|
||||||
|
for (var i = 0; i < indexfingerJointNames.length; i++) {
|
||||||
|
MyAvatar.setJointRotation(indexfingerJointNames[i], zeroRotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMessages(channel, message, sender) {
|
||||||
|
if (sender === MyAvatar.sessionUUID && channel === HIFI_POINT_INDEX_MESSAGE_CHANNEL) {
|
||||||
|
var data = JSON.parse(message);
|
||||||
|
if (data.pointIndex !== undefined) {
|
||||||
|
print("pointIndex: " + data.pointIndex);
|
||||||
|
isPointingIndex = data.pointIndex;
|
||||||
|
|
||||||
|
if (!isPointingIndex) {
|
||||||
|
for (var i = 0; i < indexfingerJointNames.length; i++) {
|
||||||
|
MyAvatar.clearJointData(indexfingerJointNames[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function shutdown() {
|
function shutdown() {
|
||||||
Script.update.disconnect(update);
|
Script.update.disconnect(update);
|
||||||
MyAvatar.removeAnimationStateHandler(animStateHandlerID);
|
MyAvatar.removeAnimationStateHandler(animStateHandlerID);
|
||||||
|
Messages.unsubscribe(HIFI_POINT_INDEX_MESSAGE_CHANNEL);
|
||||||
|
Messages.messageReceived.disconnect(handleMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
Script.scriptEnding.connect(shutdown);
|
Script.scriptEnding.connect(shutdown);
|
||||||
|
|
433
scripts/system/fingerPaint.js
Normal file
433
scripts/system/fingerPaint.js
Normal file
|
@ -0,0 +1,433 @@
|
||||||
|
//
|
||||||
|
// fingerPaint.js
|
||||||
|
//
|
||||||
|
// Created by David Rowe on 15 Feb 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
|
||||||
|
//
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
var tablet,
|
||||||
|
button,
|
||||||
|
BUTTON_NAME = "PAINT",
|
||||||
|
isFingerPainting = false,
|
||||||
|
leftHand = null,
|
||||||
|
rightHand = null,
|
||||||
|
leftBrush = null,
|
||||||
|
rightBrush = null,
|
||||||
|
CONTROLLER_MAPPING_NAME = "com.highfidelity.fingerPaint",
|
||||||
|
isTabletDisplayed = false,
|
||||||
|
HIFI_POINT_INDEX_MESSAGE_CHANNEL = "Hifi-Point-Index",
|
||||||
|
HIFI_GRAB_DISABLE_MESSAGE_CHANNEL = "Hifi-Grab-Disable",
|
||||||
|
HIFI_POINTER_DISABLE_MESSAGE_CHANNEL = "Hifi-Pointer-Disable";
|
||||||
|
|
||||||
|
function paintBrush(name) {
|
||||||
|
// Paints in 3D.
|
||||||
|
var brushName = name,
|
||||||
|
STROKE_COLOR = { red: 250, green: 0, blue: 0 },
|
||||||
|
ERASE_SEARCH_RADIUS = 0.1, // m
|
||||||
|
isDrawingLine = false,
|
||||||
|
entityID,
|
||||||
|
basePosition,
|
||||||
|
strokePoints,
|
||||||
|
strokeNormals,
|
||||||
|
strokeWidths,
|
||||||
|
timeOfLastPoint,
|
||||||
|
MIN_STROKE_LENGTH = 0.005, // m
|
||||||
|
MIN_STROKE_INTERVAL = 66, // ms
|
||||||
|
MAX_POINTS_PER_LINE = 70; // Hard-coded limit in PolyLineEntityItem.h.
|
||||||
|
|
||||||
|
function strokeNormal() {
|
||||||
|
return Vec3.multiplyQbyV(Camera.getOrientation(), Vec3.UNIT_NEG_Z);
|
||||||
|
}
|
||||||
|
|
||||||
|
function startLine(position, width) {
|
||||||
|
// Start drawing a polyline.
|
||||||
|
|
||||||
|
if (isDrawingLine) {
|
||||||
|
print("ERROR: startLine() called when already drawing line");
|
||||||
|
// Nevertheless, continue on and start a new line.
|
||||||
|
}
|
||||||
|
|
||||||
|
basePosition = position;
|
||||||
|
|
||||||
|
strokePoints = [Vec3.ZERO];
|
||||||
|
strokeNormals = [strokeNormal()];
|
||||||
|
strokeWidths = [width];
|
||||||
|
timeOfLastPoint = Date.now();
|
||||||
|
|
||||||
|
entityID = Entities.addEntity({
|
||||||
|
type: "PolyLine",
|
||||||
|
name: "fingerPainting",
|
||||||
|
color: STROKE_COLOR,
|
||||||
|
position: position,
|
||||||
|
linePoints: strokePoints,
|
||||||
|
normals: strokeNormals,
|
||||||
|
strokeWidths: strokeWidths,
|
||||||
|
dimensions: { x: 10, y: 10, z: 10 }
|
||||||
|
});
|
||||||
|
|
||||||
|
isDrawingLine = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawLine(position, width) {
|
||||||
|
// Add a stroke to the polyline if stroke is a sufficient length.
|
||||||
|
var localPosition,
|
||||||
|
distanceToPrevious,
|
||||||
|
MAX_DISTANCE_TO_PREVIOUS = 1.0;
|
||||||
|
|
||||||
|
if (!isDrawingLine) {
|
||||||
|
print("ERROR: drawLine() called when not drawing line");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
localPosition = Vec3.subtract(position, basePosition);
|
||||||
|
distanceToPrevious = Vec3.distance(localPosition, strokePoints[strokePoints.length - 1]);
|
||||||
|
|
||||||
|
if (distanceToPrevious > MAX_DISTANCE_TO_PREVIOUS) {
|
||||||
|
// Ignore occasional spurious finger tip positions.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (distanceToPrevious >= MIN_STROKE_LENGTH
|
||||||
|
&& (Date.now() - timeOfLastPoint) >= MIN_STROKE_INTERVAL
|
||||||
|
&& strokePoints.length < MAX_POINTS_PER_LINE) {
|
||||||
|
strokePoints.push(localPosition);
|
||||||
|
strokeNormals.push(strokeNormal());
|
||||||
|
strokeWidths.push(width);
|
||||||
|
timeOfLastPoint = Date.now();
|
||||||
|
|
||||||
|
Entities.editEntity(entityID, {
|
||||||
|
linePoints: strokePoints,
|
||||||
|
normals: strokeNormals,
|
||||||
|
strokeWidths: strokeWidths
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishLine(position, width) {
|
||||||
|
// Finish drawing polyline; delete if it has only 1 point.
|
||||||
|
|
||||||
|
if (!isDrawingLine) {
|
||||||
|
print("ERROR: finishLine() called when not drawing line");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strokePoints.length === 1) {
|
||||||
|
// Delete "empty" line.
|
||||||
|
Entities.deleteEntity(entityID);
|
||||||
|
}
|
||||||
|
|
||||||
|
isDrawingLine = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelLine() {
|
||||||
|
// Cancel any line being drawn.
|
||||||
|
if (isDrawingLine) {
|
||||||
|
Entities.deleteEntity(entityID);
|
||||||
|
isDrawingLine = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function eraseClosestLine(position) {
|
||||||
|
// Erase closest line that is within search radius of finger tip.
|
||||||
|
var entities,
|
||||||
|
entitiesLength,
|
||||||
|
properties,
|
||||||
|
i,
|
||||||
|
pointsLength,
|
||||||
|
j,
|
||||||
|
distance,
|
||||||
|
found = false,
|
||||||
|
foundID,
|
||||||
|
foundDistance = ERASE_SEARCH_RADIUS;
|
||||||
|
|
||||||
|
// Find entities with bounding box within search radius.
|
||||||
|
entities = Entities.findEntities(position, ERASE_SEARCH_RADIUS);
|
||||||
|
|
||||||
|
// Fine polyline entity with closest point within search radius.
|
||||||
|
for (i = 0, entitiesLength = entities.length; i < entitiesLength; i += 1) {
|
||||||
|
properties = Entities.getEntityProperties(entities[i], ["type", "position", "linePoints"]);
|
||||||
|
if (properties.type === "PolyLine") {
|
||||||
|
basePosition = properties.position;
|
||||||
|
for (j = 0, pointsLength = properties.linePoints.length; j < pointsLength; j += 1) {
|
||||||
|
distance = Vec3.distance(position, Vec3.sum(basePosition, properties.linePoints[j]));
|
||||||
|
if (distance <= foundDistance) {
|
||||||
|
found = true;
|
||||||
|
foundID = entities[i];
|
||||||
|
foundDistance = distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete found entity.
|
||||||
|
if (found) {
|
||||||
|
Entities.deleteEntity(foundID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tearDown() {
|
||||||
|
cancelLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
startLine: startLine,
|
||||||
|
drawLine: drawLine,
|
||||||
|
finishLine: finishLine,
|
||||||
|
cancelLine: cancelLine,
|
||||||
|
eraseClosestLine: eraseClosestLine,
|
||||||
|
tearDown: tearDown
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function handController(name) {
|
||||||
|
// Translates controller data into application events.
|
||||||
|
var handName = name,
|
||||||
|
|
||||||
|
triggerPressedCallback,
|
||||||
|
triggerPressingCallback,
|
||||||
|
triggerReleasedCallback,
|
||||||
|
gripPressedCallback,
|
||||||
|
|
||||||
|
rawTriggerValue = 0.0,
|
||||||
|
triggerValue = 0.0,
|
||||||
|
isTriggerPressed = false,
|
||||||
|
TRIGGER_SMOOTH_RATIO = 0.1,
|
||||||
|
TRIGGER_OFF = 0.05,
|
||||||
|
TRIGGER_ON = 0.1,
|
||||||
|
TRIGGER_START_WIDTH_RAMP = 0.15,
|
||||||
|
TRIGGER_FINISH_WIDTH_RAMP = 1.0,
|
||||||
|
TRIGGER_RAMP_WIDTH = TRIGGER_FINISH_WIDTH_RAMP - TRIGGER_START_WIDTH_RAMP,
|
||||||
|
MIN_LINE_WIDTH = 0.005,
|
||||||
|
MAX_LINE_WIDTH = 0.03,
|
||||||
|
RAMP_LINE_WIDTH = MAX_LINE_WIDTH - MIN_LINE_WIDTH,
|
||||||
|
|
||||||
|
rawGripValue = 0.0,
|
||||||
|
gripValue = 0.0,
|
||||||
|
isGripPressed = false,
|
||||||
|
GRIP_SMOOTH_RATIO = 0.1,
|
||||||
|
GRIP_OFF = 0.05,
|
||||||
|
GRIP_ON = 0.1;
|
||||||
|
|
||||||
|
function onTriggerPress(value) {
|
||||||
|
// Controller values are only updated when they change so store latest for use in update.
|
||||||
|
rawTriggerValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTriggerPress(value) {
|
||||||
|
var wasTriggerPressed,
|
||||||
|
fingerTipPosition,
|
||||||
|
lineWidth;
|
||||||
|
|
||||||
|
triggerValue = triggerValue * TRIGGER_SMOOTH_RATIO + rawTriggerValue * (1.0 - TRIGGER_SMOOTH_RATIO);
|
||||||
|
|
||||||
|
wasTriggerPressed = isTriggerPressed;
|
||||||
|
if (isTriggerPressed) {
|
||||||
|
isTriggerPressed = triggerValue > TRIGGER_OFF;
|
||||||
|
} else {
|
||||||
|
isTriggerPressed = triggerValue > TRIGGER_ON;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wasTriggerPressed || isTriggerPressed) {
|
||||||
|
fingerTipPosition = MyAvatar.getJointPosition(handName === "left" ? "LeftHandIndex4" : "RightHandIndex4");
|
||||||
|
if (triggerValue < TRIGGER_START_WIDTH_RAMP) {
|
||||||
|
lineWidth = MIN_LINE_WIDTH;
|
||||||
|
} else {
|
||||||
|
lineWidth = MIN_LINE_WIDTH
|
||||||
|
+ (triggerValue - TRIGGER_START_WIDTH_RAMP) / TRIGGER_RAMP_WIDTH * RAMP_LINE_WIDTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wasTriggerPressed && isTriggerPressed) {
|
||||||
|
triggerPressedCallback(fingerTipPosition, lineWidth);
|
||||||
|
} else if (wasTriggerPressed && isTriggerPressed) {
|
||||||
|
triggerPressingCallback(fingerTipPosition, lineWidth);
|
||||||
|
} else {
|
||||||
|
triggerReleasedCallback(fingerTipPosition, lineWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onGripPress(value) {
|
||||||
|
// Controller values are only updated when they change so store latest for use in update.
|
||||||
|
rawGripValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateGripPress() {
|
||||||
|
var fingerTipPosition;
|
||||||
|
|
||||||
|
gripValue = gripValue * GRIP_SMOOTH_RATIO + rawGripValue * (1.0 - GRIP_SMOOTH_RATIO);
|
||||||
|
|
||||||
|
if (isGripPressed) {
|
||||||
|
isGripPressed = gripValue > GRIP_OFF;
|
||||||
|
} else {
|
||||||
|
isGripPressed = gripValue > GRIP_ON;
|
||||||
|
if (isGripPressed) {
|
||||||
|
fingerTipPosition = MyAvatar.getJointPosition(handName === "left" ? "LeftHandIndex4" : "RightHandIndex4");
|
||||||
|
gripPressedCallback(fingerTipPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
updateTriggerPress();
|
||||||
|
updateGripPress();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setUp(onTriggerPressed, onTriggerPressing, onTriggerReleased, onGripPressed) {
|
||||||
|
triggerPressedCallback = onTriggerPressed;
|
||||||
|
triggerPressingCallback = onTriggerPressing;
|
||||||
|
triggerReleasedCallback = onTriggerReleased;
|
||||||
|
gripPressedCallback = onGripPressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tearDown() {
|
||||||
|
// Nothing to do.
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
onTriggerPress: onTriggerPress,
|
||||||
|
onGripPress: onGripPress,
|
||||||
|
onUpdate: onUpdate,
|
||||||
|
setUp: setUp,
|
||||||
|
tearDown: tearDown
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateHandFunctions() {
|
||||||
|
// Update other scripts' hand functions.
|
||||||
|
var enabled = !isFingerPainting || isTabletDisplayed;
|
||||||
|
|
||||||
|
Messages.sendMessage(HIFI_GRAB_DISABLE_MESSAGE_CHANNEL, JSON.stringify({
|
||||||
|
holdEnabled: enabled,
|
||||||
|
nearGrabEnabled: enabled,
|
||||||
|
farGrabEnabled: enabled
|
||||||
|
}), true);
|
||||||
|
Messages.sendMessage(HIFI_POINTER_DISABLE_MESSAGE_CHANNEL, JSON.stringify({
|
||||||
|
pointerEnabled: enabled
|
||||||
|
}), true);
|
||||||
|
Messages.sendMessage(HIFI_POINT_INDEX_MESSAGE_CHANNEL, JSON.stringify({
|
||||||
|
pointIndex: !enabled
|
||||||
|
}), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableProcessing() {
|
||||||
|
// Connect controller API to handController objects.
|
||||||
|
leftHand = handController("left");
|
||||||
|
rightHand = handController("right");
|
||||||
|
var controllerMapping = Controller.newMapping(CONTROLLER_MAPPING_NAME);
|
||||||
|
controllerMapping.from(Controller.Standard.LT).to(leftHand.onTriggerPress);
|
||||||
|
controllerMapping.from(Controller.Standard.LeftGrip).to(leftHand.onGripPress);
|
||||||
|
controllerMapping.from(Controller.Standard.RT).to(rightHand.onTriggerPress);
|
||||||
|
controllerMapping.from(Controller.Standard.RightGrip).to(rightHand.onGripPress);
|
||||||
|
Controller.enableMapping(CONTROLLER_MAPPING_NAME);
|
||||||
|
|
||||||
|
// Connect handController outputs to paintBrush objects.
|
||||||
|
leftBrush = paintBrush("left");
|
||||||
|
leftHand.setUp(leftBrush.startLine, leftBrush.drawLine, leftBrush.finishLine, leftBrush.eraseClosestLine);
|
||||||
|
rightBrush = paintBrush("right");
|
||||||
|
rightHand.setUp(rightBrush.startLine, rightBrush.drawLine, rightBrush.finishLine, rightBrush.eraseClosestLine);
|
||||||
|
|
||||||
|
// Messages channels for enabling/disabling other scripts' functions.
|
||||||
|
Messages.subscribe(HIFI_POINT_INDEX_MESSAGE_CHANNEL);
|
||||||
|
Messages.subscribe(HIFI_GRAB_DISABLE_MESSAGE_CHANNEL);
|
||||||
|
Messages.subscribe(HIFI_POINTER_DISABLE_MESSAGE_CHANNEL);
|
||||||
|
|
||||||
|
// Update hand controls.
|
||||||
|
Script.update.connect(leftHand.onUpdate);
|
||||||
|
Script.update.connect(rightHand.onUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableProcessing() {
|
||||||
|
Script.update.disconnect(leftHand.onUpdate);
|
||||||
|
Script.update.disconnect(rightHand.onUpdate);
|
||||||
|
|
||||||
|
Controller.disableMapping(CONTROLLER_MAPPING_NAME);
|
||||||
|
|
||||||
|
leftBrush.tearDown();
|
||||||
|
leftBrush = null;
|
||||||
|
leftHand.tearDown();
|
||||||
|
leftHand = null;
|
||||||
|
|
||||||
|
rightBrush.tearDown();
|
||||||
|
rightBrush = null;
|
||||||
|
rightHand.tearDown();
|
||||||
|
rightHand = null;
|
||||||
|
|
||||||
|
Messages.unsubscribe(HIFI_POINT_INDEX_MESSAGE_CHANNEL);
|
||||||
|
Messages.unsubscribe(HIFI_GRAB_DISABLE_MESSAGE_CHANNEL);
|
||||||
|
Messages.unsubscribe(HIFI_POINTER_DISABLE_MESSAGE_CHANNEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onButtonClicked() {
|
||||||
|
var wasFingerPainting = isFingerPainting;
|
||||||
|
|
||||||
|
isFingerPainting = !isFingerPainting;
|
||||||
|
button.editProperties({ isActive: isFingerPainting });
|
||||||
|
|
||||||
|
print("Finger painting: " + isFingerPainting ? "on" : "off");
|
||||||
|
|
||||||
|
if (wasFingerPainting) {
|
||||||
|
leftBrush.cancelLine();
|
||||||
|
rightBrush.cancelLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFingerPainting) {
|
||||||
|
enableProcessing();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHandFunctions();
|
||||||
|
|
||||||
|
if (!isFingerPainting) {
|
||||||
|
disableProcessing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onTabletScreenChanged(type, url) {
|
||||||
|
var TABLET_SCREEN_CLOSED = "Closed";
|
||||||
|
|
||||||
|
isTabletDisplayed = type !== TABLET_SCREEN_CLOSED;
|
||||||
|
updateHandFunctions();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setUp() {
|
||||||
|
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||||
|
if (!tablet) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tablet button.
|
||||||
|
button = tablet.addButton({
|
||||||
|
icon: "icons/tablet-icons/finger-paint-i.svg",
|
||||||
|
activeIcon: "icons/tablet-icons/finger-paint-a.svg",
|
||||||
|
text: BUTTON_NAME,
|
||||||
|
isActive: isFingerPainting
|
||||||
|
});
|
||||||
|
button.clicked.connect(onButtonClicked);
|
||||||
|
|
||||||
|
// Track whether tablet is displayed or not.
|
||||||
|
tablet.screenChanged.connect(onTabletScreenChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
function tearDown() {
|
||||||
|
if (!tablet) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFingerPainting) {
|
||||||
|
isFingerPainting = false;
|
||||||
|
updateHandFunctions();
|
||||||
|
disableProcessing();
|
||||||
|
}
|
||||||
|
|
||||||
|
tablet.screenChanged.disconnect(onTabletScreenChanged);
|
||||||
|
|
||||||
|
button.clicked.disconnect(onButtonClicked);
|
||||||
|
tablet.removeButton(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
setUp();
|
||||||
|
Script.scriptEnding.connect(tearDown);
|
||||||
|
}());
|
Loading…
Reference in a new issue