From 521797b32f0e75a053fcda8d891b915eabff1379 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 9 Aug 2018 20:44:01 +1200 Subject: [PATCH 001/259] Script and state machine basics --- scripts/system/tabletRezzer.js | 237 +++++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 scripts/system/tabletRezzer.js diff --git a/scripts/system/tabletRezzer.js b/scripts/system/tabletRezzer.js new file mode 100644 index 0000000000..ebc2c2cbea --- /dev/null +++ b/scripts/system/tabletRezzer.js @@ -0,0 +1,237 @@ +// +// tabletRezzer.js +// +// Created by David Rowe on 9 Aug 2018. +// Copyright 2018 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 */ + +(function () { + + "use strict"; + + var // Overlay + proxyOverlay, + + // State machine + PROXY_HIDDEN = 0, + PROXY_VISIBLE = 1, + PROXY_GRABBED = 2, + PROXY_EXPANDING = 3, + TABLET_OPEN = 4, + STATE_STRINGS = ["PROXY_HIDDEN", "PROXY_VISIBLE", "PROXY_GRABBED", "PROXY_EXPANDING", "TABLET_OPEN"], + STATE_MACHINE, + rezzerState = PROXY_HIDDEN, + proxyHand, + + // Events + MIN_HAND_CAMERA_ANGLE = 30, + DEGREES_180 = 180, + MIN_HAND_CAMERA_ANGLE_COS = Math.cos(Math.PI * MIN_HAND_CAMERA_ANGLE / DEGREES_180), + updateTimer = null, + UPDATE_INTERVAL = 250, + + LEFT_HAND = 0, + RIGHT_HAND = 1, + DEBUG = true; + + // #region Utilities ======================================================================================================= + + function debug(message) { + if (!DEBUG) { + return; + } + print("DEBUG: " + message); + } + + function error(message) { + print("ERROR: " + message); + } + + function handJointName(hand) { + var jointName; + if (hand === LEFT_HAND) { + if (Camera.mode === "first person") { + jointName = "_CONTROLLER_LEFTHAND"; + } else if (Camera.mode === "third person") { + jointName = "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"; + } else { + jointName = "LeftHand"; + } + } else { + if (Camera.mode === "first person") { + jointName = "_CONTROLLER_RIGHTHAND"; + } else if (Camera.mode === "third person") { + jointName = "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND"; + } else { + jointName = "RightHand"; + } + } + return jointName; + } + + function handJointIndex(hand) { + return MyAvatar.getJointIndex(handJointName(hand)); + } + + // #endregion + + // #region State Machine =================================================================================================== + + function enterProxyHidden() { + } + + function enterProxyVisible(hand) { + proxyHand = hand; + } + + STATE_MACHINE = { + PROXY_HIDDEN: { // Tablet proxy could be shown but isn't because hand is oriented to show it or aren't in HMD mode. + enter: enterProxyHidden, + update: null, + exit: null + }, + PROXY_VISIBLE: { // Tablet proxy is visible and attached to hand. + enter: enterProxyVisible, + update: null, + exit: null + }, + PROXY_GRABBED: { // Other hand has grabbed and is holding the tablet proxy. + enter: null, + update: null, + exit: null + }, + PROXY_EXPANDING: { // Tablet proxy has been released from grab and is expanding before showing tablet proper. + enter: null, + update: null, + exit: null + }, + TABLET_OPEN: { // Tablet proper is being displayed. + enter: null, + update: null, + exit: null + } + }; + + function setState(state, data) { + if (state !== rezzerState) { + debug("State transition from " + STATE_STRINGS[rezzerState] + " to " + STATE_STRINGS[state]); + if (STATE_MACHINE[STATE_STRINGS[rezzerState]].exit) { + STATE_MACHINE[STATE_STRINGS[rezzerState]].exit(data); + } + if (STATE_MACHINE[STATE_STRINGS[state]].enter) { + STATE_MACHINE[STATE_STRINGS[state]].enter(data); + } + rezzerState = state; + } else { + error("Null state transition: " + state + "!"); + } + } + + function updateState() { + STATE_MACHINE[STATE_STRINGS[rezzerState]].update(); + } + + // #endregion + + // #region Events ========================================================================================================== + + function shouldShowProxy(hand) { + // Should show tablet proxy if hand is oriented towards the camera. + var pose, + jointIndex, + handPosition, + handOrientation; + + pose = Controller.getPoseValue(hand === LEFT_HAND ? Controller.Standard.LeftHand : Controller.Standard.RightHand); + if (!pose.valid) { + return false; + } + + jointIndex = handJointIndex(hand); + handPosition = Vec3.sum(MyAvatar.position, + Vec3.multiplyQbyV(MyAvatar.orientation, MyAvatar.getAbsoluteJointTranslationInObjectFrame(jointIndex))); + handOrientation = Quat.multiply(MyAvatar.orientation, MyAvatar.getAbsoluteJointRotationInObjectFrame(jointIndex)); + + return Vec3.dot(Vec3.normalize(Vec3.subtract(handPosition, Camera.position)), Quat.getForward(handOrientation)) + > MIN_HAND_CAMERA_ANGLE_COS; + } + + function update() { + // Assumes that is HMD.mounted. + switch (rezzerState) { + case PROXY_HIDDEN: + // Compare palm directions of hands with vectors from palms to camera. + if (shouldShowProxy(LEFT_HAND)) { + setState(PROXY_VISIBLE, LEFT_HAND); + break; + } else if (shouldShowProxy(RIGHT_HAND)) { + setState(PROXY_VISIBLE, RIGHT_HAND); + break; + } + break; + case PROXY_VISIBLE: + // Check that palm direction of proxy hand still less than maximum angle. + if (!shouldShowProxy(proxyHand)) { + setState(PROXY_HIDDEN); + break; + } + break; + default: + error("Missing case: " + rezzerState); + } + + updateTimer = Script.setTimeout(update, UPDATE_INTERVAL); + } + + + function onMountedChanged() { + // Tablet proxy only available when HMD is mounted. + + if (HMD.mounted) { + if (updateTimer === null) { + update(); + } + return; + } + + if (updateTimer !== null) { + Script.clearTimeout(updateTimer); + updateTimer = null; + } + if (rezzerState !== PROXY_HIDDEN) { + setState(PROXY_HIDDEN); + } + } + + // #endregion + + // #region Start-up and tear-down ========================================================================================== + + function setUp() { + HMD.mountedChanged.connect(onMountedChanged); + HMD.displayModeChanged.connect(onMountedChanged); // For the case that the HMD is already worn when the script starts. + if (HMD.mounted) { + update(); + } + } + + function tearDown() { + HMD.displayModeChanged.disconnect(onMountedChanged); + HMD.mountedChanged.disconnect(onMountedChanged); + if (updateTimer !== null) { + Script.clearTimeout(updateTimer); + updateTimer = null; + } + } + + setUp(); + Script.scriptEnding.connect(tearDown); + + //#endregion + +}()); From 5bada416bb6570467a0343ad94493e732d3e3b5d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 10 Aug 2018 09:41:25 +1200 Subject: [PATCH 002/259] Show/hide/expand tablet proxy --- scripts/system/tabletRezzer.js | 136 +++++++++++++++++++++++++++++++-- 1 file changed, 129 insertions(+), 7 deletions(-) diff --git a/scripts/system/tabletRezzer.js b/scripts/system/tabletRezzer.js index ebc2c2cbea..a62b0741bc 100644 --- a/scripts/system/tabletRezzer.js +++ b/scripts/system/tabletRezzer.js @@ -8,14 +8,29 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global */ +/* global getTabletWidthFromSettings */ (function () { "use strict"; + Script.include("./libraries/utils.js"); + var // Overlay - proxyOverlay, + proxyOverlay = null, + TABLET_PROXY_DIMENSIONS = { x: 0.0638, y: 0.0965, z: 0.0045 }, + TABLET_PROXY_POSITION_LEFT_HAND = { + x: 0, + y: 0.07, // Distance from joint. + z: 0.07 // Distance above palm. + }, + TABLET_PROXY_POSITION_RIGHT_HAND = { + x: 0, + y: 0.07, // Distance from joint. + z: 0.07 // Distance above palm. + }, + TABLET_PROXY_ROTATION_LEFT_HAND = Quat.fromVec3Degrees({ x: 0, y: 180, z: 90 }), + TABLET_PROXY_ROTATION_RIGHT_HAND = Quat.fromVec3Degrees({ x: 0, y: 180, z: -90 }), // State machine PROXY_HIDDEN = 0, @@ -27,6 +42,12 @@ STATE_MACHINE, rezzerState = PROXY_HIDDEN, proxyHand, + PROXY_EXPAND_DURATION = 500, + PROXY_EXPAND_TIMEOUT = 25, + proxyExpandTimer = null, + proxyExpandStart, + proxyInitialWidth, + proxyTargetWidth, // Events MIN_HAND_CAMERA_ANGLE = 30, @@ -34,10 +55,11 @@ MIN_HAND_CAMERA_ANGLE_COS = Math.cos(Math.PI * MIN_HAND_CAMERA_ANGLE / DEGREES_180), updateTimer = null, UPDATE_INTERVAL = 250, + HIFI_OBJECT_MANIPULATION_CHANNEL = "Hifi-Object-Manipulation", LEFT_HAND = 0, RIGHT_HAND = 1, - DEBUG = true; + DEBUG = false; // #region Utilities ======================================================================================================= @@ -83,10 +105,73 @@ // #region State Machine =================================================================================================== function enterProxyHidden() { + Overlays.deleteOverlay(proxyOverlay); + proxyOverlay = null; } function enterProxyVisible(hand) { proxyHand = hand; + proxyOverlay = Overlays.addOverlay("cube", { + parentID: MyAvatar.SELF_ID, + parentJointIndex: handJointIndex(proxyHand), + localPosition: proxyHand === LEFT_HAND ? TABLET_PROXY_POSITION_LEFT_HAND : TABLET_PROXY_POSITION_RIGHT_HAND, + localRotation: proxyHand === LEFT_HAND ? TABLET_PROXY_ROTATION_LEFT_HAND : TABLET_PROXY_ROTATION_RIGHT_HAND, + dimensions: TABLET_PROXY_DIMENSIONS, + solid: true, + grabbable: true, + displayInFront: true, + visible: true + }); + } + + function expandProxy() { + var scaleFactor = (Date.now() - proxyExpandStart) / PROXY_EXPAND_DURATION; + if (scaleFactor < 1) { + Overlays.editOverlay(proxyOverlay, { + dimensions: Vec3.multiply( + 1 + scaleFactor * (proxyTargetWidth - proxyInitialWidth) / proxyInitialWidth, + TABLET_PROXY_DIMENSIONS) + }); + proxyExpandTimer = Script.setTimeout(expandProxy, PROXY_EXPAND_TIMEOUT); + return; + } + + proxyExpandTimer = null; + setState(TABLET_OPEN); + } + + function enterProxyExpanding() { + // Detach from hand. + Overlays.editOverlay(proxyOverlay, { + parentID: Uuid.NULL, + parentJointIndex: -1 + }); + + // Start expanding. + proxyInitialWidth = TABLET_PROXY_DIMENSIONS.x; + proxyTargetWidth = getTabletWidthFromSettings(); + proxyExpandStart = Date.now(); + proxyExpandTimer = Script.setTimeout(expandProxy, PROXY_EXPAND_TIMEOUT); + } + + function exitProxyExpanding() { + if (proxyExpandTimer !== null) { + Script.clearTimeout(proxyExpandTimer); + proxyExpandTimer = null; + } + } + + function enterTabletOpen() { + var proxyOverlayProperties = Overlays.getProperties(proxyOverlay, ["position", "orientation"]); + + Overlays.deleteOverlay(proxyOverlay); + proxyOverlay = null; + + Overlays.editOverlay(HMD.tabletID, { + position: proxyOverlayProperties.position, + orientation: proxyOverlayProperties.orientation + }); + HMD.openTablet(true); } STATE_MACHINE = { @@ -106,12 +191,12 @@ exit: null }, PROXY_EXPANDING: { // Tablet proxy has been released from grab and is expanding before showing tablet proper. - enter: null, + enter: enterProxyExpanding, update: null, - exit: null + exit: exitProxyExpanding }, TABLET_OPEN: { // Tablet proper is being displayed. - enter: null, + enter: enterTabletOpen, update: null, exit: null } @@ -181,6 +266,14 @@ break; } break; + case PROXY_GRABBED: + case PROXY_EXPANDING: + break; + case TABLET_OPEN: + if (!HMD.showTablet) { + setState(PROXY_HIDDEN); + } + break; default: error("Missing case: " + rezzerState); } @@ -188,6 +281,24 @@ updateTimer = Script.setTimeout(update, UPDATE_INTERVAL); } + function onMessageReceived(channel, data, senderID, localOnly) { + var message; + + if (channel !== HIFI_OBJECT_MANIPULATION_CHANNEL) { + return; + } + + message = JSON.parse(data); + if (message.grabbedEntity !== proxyOverlay) { + return; + } + + if (message.action === "grab" && rezzerState === PROXY_VISIBLE) { + setState(PROXY_GRABBED); + } else if (message.action === "release" && rezzerState === PROXY_GRABBED) { + setState(PROXY_EXPANDING); + } + } function onMountedChanged() { // Tablet proxy only available when HMD is mounted. @@ -213,6 +324,9 @@ // #region Start-up and tear-down ========================================================================================== function setUp() { + Messages.subscribe(HIFI_OBJECT_MANIPULATION_CHANNEL); + Messages.messageReceived.connect(onMessageReceived); + HMD.mountedChanged.connect(onMountedChanged); HMD.displayModeChanged.connect(onMountedChanged); // For the case that the HMD is already worn when the script starts. if (HMD.mounted) { @@ -221,17 +335,25 @@ } function tearDown() { + setState(PROXY_HIDDEN); // Or just tear right down? Perhaps so. + + Messages.messageReceived.disconnect(onMessageReceived); + Messages.unsubscribe(HIFI_OBJECT_MANIPULATION_CHANNEL); + HMD.displayModeChanged.disconnect(onMountedChanged); HMD.mountedChanged.disconnect(onMountedChanged); if (updateTimer !== null) { Script.clearTimeout(updateTimer); updateTimer = null; } + if (proxyOverlay !== null) { + Overlays.deleteOverlay(proxyOverlay); + } } setUp(); Script.scriptEnding.connect(tearDown); - //#endregion + // #endregion }()); From 0aa42d70d65ac47cbcef5973d55801bc546cd047 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 10 Aug 2018 12:37:21 +1200 Subject: [PATCH 003/259] Don't let the tablet proxy hand do the initial grab --- scripts/system/tabletRezzer.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/system/tabletRezzer.js b/scripts/system/tabletRezzer.js index a62b0741bc..e338e767bb 100644 --- a/scripts/system/tabletRezzer.js +++ b/scripts/system/tabletRezzer.js @@ -59,6 +59,7 @@ LEFT_HAND = 0, RIGHT_HAND = 1, + HAND_NAMES = ["LeftHand", "RightHand"], DEBUG = false; // #region Utilities ======================================================================================================= @@ -293,7 +294,9 @@ return; } - if (message.action === "grab" && rezzerState === PROXY_VISIBLE) { + // Don't transition into PROXY_GRABBED unless the tablet proxy is grabbed with "other" hand. However, once it has been + // grabbed then the original hand can grab it back and grow it from there. + if (message.action === "grab" && message.joint !== HAND_NAMES[proxyHand] && rezzerState === PROXY_VISIBLE) { setState(PROXY_GRABBED); } else if (message.action === "release" && rezzerState === PROXY_GRABBED) { setState(PROXY_EXPANDING); From b240a4248dfe7476fa869c25559ef323ea6eec2e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 10 Aug 2018 12:51:06 +1200 Subject: [PATCH 004/259] Size and position according to avatar scale --- scripts/system/tabletRezzer.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/scripts/system/tabletRezzer.js b/scripts/system/tabletRezzer.js index e338e767bb..a34f79da11 100644 --- a/scripts/system/tabletRezzer.js +++ b/scripts/system/tabletRezzer.js @@ -56,6 +56,7 @@ updateTimer = null, UPDATE_INTERVAL = 250, HIFI_OBJECT_MANIPULATION_CHANNEL = "Hifi-Object-Manipulation", + avatarScale = 1, LEFT_HAND = 0, RIGHT_HAND = 1, @@ -115,9 +116,10 @@ proxyOverlay = Overlays.addOverlay("cube", { parentID: MyAvatar.SELF_ID, parentJointIndex: handJointIndex(proxyHand), - localPosition: proxyHand === LEFT_HAND ? TABLET_PROXY_POSITION_LEFT_HAND : TABLET_PROXY_POSITION_RIGHT_HAND, + localPosition: Vec3.multiply(avatarScale, + proxyHand === LEFT_HAND ? TABLET_PROXY_POSITION_LEFT_HAND : TABLET_PROXY_POSITION_RIGHT_HAND), localRotation: proxyHand === LEFT_HAND ? TABLET_PROXY_ROTATION_LEFT_HAND : TABLET_PROXY_ROTATION_RIGHT_HAND, - dimensions: TABLET_PROXY_DIMENSIONS, + dimensions: Vec3.multiply(avatarScale, TABLET_PROXY_DIMENSIONS), solid: true, grabbable: true, displayInFront: true, @@ -130,7 +132,7 @@ if (scaleFactor < 1) { Overlays.editOverlay(proxyOverlay, { dimensions: Vec3.multiply( - 1 + scaleFactor * (proxyTargetWidth - proxyInitialWidth) / proxyInitialWidth, + avatarScale * (1 + scaleFactor * (proxyTargetWidth - proxyInitialWidth) / proxyInitialWidth), TABLET_PROXY_DIMENSIONS) }); proxyExpandTimer = Script.setTimeout(expandProxy, PROXY_EXPAND_TIMEOUT); @@ -282,6 +284,12 @@ updateTimer = Script.setTimeout(update, UPDATE_INTERVAL); } + function onScaleChanged() { + avatarScale = MyAvatar.scale; + // Clamp scale in order to work around M17434. + avatarScale = Math.max(MyAvatar.getDomainMinScale(), Math.min(MyAvatar.getDomainMaxScale(), avatarScale)); + } + function onMessageReceived(channel, data, senderID, localOnly) { var message; @@ -327,6 +335,8 @@ // #region Start-up and tear-down ========================================================================================== function setUp() { + MyAvatar.scaleChanged.connect(onScaleChanged); + Messages.subscribe(HIFI_OBJECT_MANIPULATION_CHANNEL); Messages.messageReceived.connect(onMessageReceived); @@ -343,6 +353,8 @@ Messages.messageReceived.disconnect(onMessageReceived); Messages.unsubscribe(HIFI_OBJECT_MANIPULATION_CHANNEL); + MyAvatar.scaleChanged.disconnect(onScaleChanged); + HMD.displayModeChanged.disconnect(onMountedChanged); HMD.mountedChanged.disconnect(onMountedChanged); if (updateTimer !== null) { From 557f18f370e9ac2bef5715beb881af7680b830d2 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 10 Aug 2018 13:10:24 +1200 Subject: [PATCH 005/259] Don't show proxy if tablet has been opened by other means --- scripts/system/tabletRezzer.js | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/scripts/system/tabletRezzer.js b/scripts/system/tabletRezzer.js index a34f79da11..6efaa33ed6 100644 --- a/scripts/system/tabletRezzer.js +++ b/scripts/system/tabletRezzer.js @@ -107,8 +107,10 @@ // #region State Machine =================================================================================================== function enterProxyHidden() { - Overlays.deleteOverlay(proxyOverlay); - proxyOverlay = null; + if (proxyOverlay) { + Overlays.deleteOverlay(proxyOverlay); + proxyOverlay = null; + } } function enterProxyVisible(hand) { @@ -253,30 +255,39 @@ // Assumes that is HMD.mounted. switch (rezzerState) { case PROXY_HIDDEN: + // Don't show proxy is tablet is already displayed. + if (HMD.showTablet) { + break; + } // Compare palm directions of hands with vectors from palms to camera. if (shouldShowProxy(LEFT_HAND)) { setState(PROXY_VISIBLE, LEFT_HAND); - break; } else if (shouldShowProxy(RIGHT_HAND)) { setState(PROXY_VISIBLE, RIGHT_HAND); - break; } break; case PROXY_VISIBLE: + // Hide proxy if tablet has been displayed by other means. + if (HMD.showTablet) { + setState(PROXY_HIDDEN); + break; + } // Check that palm direction of proxy hand still less than maximum angle. if (!shouldShowProxy(proxyHand)) { setState(PROXY_HIDDEN); - break; } break; case PROXY_GRABBED: case PROXY_EXPANDING: - break; - case TABLET_OPEN: - if (!HMD.showTablet) { + // Hide proxy if tablet has been displayed by other means. + if (HMD.showTablet) { setState(PROXY_HIDDEN); } break; + case TABLET_OPEN: + // Immediately transition back to PROXY_HIDDEN. + setState(PROXY_HIDDEN); + break; default: error("Missing case: " + rezzerState); } From bc456ae5c8c010b6784b9e058454fd8ed0e5b14f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 10 Aug 2018 13:18:21 +1200 Subject: [PATCH 006/259] Don't show proxy if in toolbar mode --- scripts/system/tabletRezzer.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/system/tabletRezzer.js b/scripts/system/tabletRezzer.js index 6efaa33ed6..390eaadded 100644 --- a/scripts/system/tabletRezzer.js +++ b/scripts/system/tabletRezzer.js @@ -48,6 +48,7 @@ proxyExpandStart, proxyInitialWidth, proxyTargetWidth, + tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"), // Events MIN_HAND_CAMERA_ANGLE = 30, @@ -255,8 +256,8 @@ // Assumes that is HMD.mounted. switch (rezzerState) { case PROXY_HIDDEN: - // Don't show proxy is tablet is already displayed. - if (HMD.showTablet) { + // Don't show proxy is tablet is already displayed or are in toolbar mode. + if (HMD.showTablet || tablet.toolbarMode) { break; } // Compare palm directions of hands with vectors from palms to camera. From 3732de51a37f98c844527959d8a28d4c630f1c9d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 10 Aug 2018 13:25:41 +1200 Subject: [PATCH 007/259] Tidying --- scripts/system/tabletRezzer.js | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/scripts/system/tabletRezzer.js b/scripts/system/tabletRezzer.js index 390eaadded..a84c53ad9e 100644 --- a/scripts/system/tabletRezzer.js +++ b/scripts/system/tabletRezzer.js @@ -55,7 +55,7 @@ DEGREES_180 = 180, MIN_HAND_CAMERA_ANGLE_COS = Math.cos(Math.PI * MIN_HAND_CAMERA_ANGLE / DEGREES_180), updateTimer = null, - UPDATE_INTERVAL = 250, + UPDATE_INTERVAL = 300, HIFI_OBJECT_MANIPULATION_CHANNEL = "Hifi-Object-Manipulation", avatarScale = 1, @@ -223,10 +223,6 @@ } } - function updateState() { - STATE_MACHINE[STATE_STRINGS[rezzerState]].update(); - } - // #endregion // #region Events ========================================================================================================== @@ -360,22 +356,20 @@ } function tearDown() { - setState(PROXY_HIDDEN); // Or just tear right down? Perhaps so. + if (updateTimer !== null) { + Script.clearTimeout(updateTimer); + updateTimer = null; + } + + setState(PROXY_HIDDEN); + + HMD.displayModeChanged.disconnect(onMountedChanged); + HMD.mountedChanged.disconnect(onMountedChanged); Messages.messageReceived.disconnect(onMessageReceived); Messages.unsubscribe(HIFI_OBJECT_MANIPULATION_CHANNEL); MyAvatar.scaleChanged.disconnect(onScaleChanged); - - HMD.displayModeChanged.disconnect(onMountedChanged); - HMD.mountedChanged.disconnect(onMountedChanged); - if (updateTimer !== null) { - Script.clearTimeout(updateTimer); - updateTimer = null; - } - if (proxyOverlay !== null) { - Overlays.deleteOverlay(proxyOverlay); - } } setUp(); From c89955b416a446ea0746bd7e135a85b133e5b10a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 10 Aug 2018 13:53:53 +1200 Subject: [PATCH 008/259] Use FBX for tablet proxy --- scripts/system/assets/models/tinyTablet.fbx | Bin 0 -> 42864 bytes scripts/system/tabletRezzer.js | 6 ++++-- 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 scripts/system/assets/models/tinyTablet.fbx diff --git a/scripts/system/assets/models/tinyTablet.fbx b/scripts/system/assets/models/tinyTablet.fbx new file mode 100644 index 0000000000000000000000000000000000000000..104d2df8cdb57bdaf23310a3a82baa26f9e82d16 GIT binary patch literal 42864 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 literal 0 HcmV?d00001 diff --git a/scripts/system/tabletRezzer.js b/scripts/system/tabletRezzer.js index a84c53ad9e..792deec164 100644 --- a/scripts/system/tabletRezzer.js +++ b/scripts/system/tabletRezzer.js @@ -18,7 +18,8 @@ var // Overlay proxyOverlay = null, - TABLET_PROXY_DIMENSIONS = { x: 0.0638, y: 0.0965, z: 0.0045 }, + TABLET_PROXY_MODEL = Script.resolvePath("./assets/models/tinyTablet.fbx"), + TABLET_PROXY_DIMENSIONS = { x: 0.0637, y: 0.0965, z: 0.0046 }, // Proportional to tablet proper. TABLET_PROXY_POSITION_LEFT_HAND = { x: 0, y: 0.07, // Distance from joint. @@ -116,7 +117,8 @@ function enterProxyVisible(hand) { proxyHand = hand; - proxyOverlay = Overlays.addOverlay("cube", { + proxyOverlay = Overlays.addOverlay("model", { + url: TABLET_PROXY_MODEL, parentID: MyAvatar.SELF_ID, parentJointIndex: handJointIndex(proxyHand), localPosition: Vec3.multiply(avatarScale, From 7a7fca3982573f1bba648244e4dfe3982614e49e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 11 Aug 2018 05:34:01 +1200 Subject: [PATCH 009/259] Update tiny tablet model --- scripts/system/assets/models/tinyTablet.fbx | Bin 42864 -> 411712 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/scripts/system/assets/models/tinyTablet.fbx b/scripts/system/assets/models/tinyTablet.fbx index 104d2df8cdb57bdaf23310a3a82baa26f9e82d16..cdcdf8af3461946f9050f07f897312fb77f5ef38 100644 GIT binary patch literal 411712 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

T1vQwdFfn)XEo30mE8|8ko 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 delta 3350 zcmbtWYfxLq6+Wxgl^`HMAP5iw!j5Ct5Ddl`8!!?hWFaIZYzV325KBfEq97z!5`MH3 zB*)`Sr_+as+HRbQi@<0SNyXMQ~DSX;oQyRe5E}=Cbf( z%rz8G2v$l*(~4dznf_zRpCf0+UtfD_)n^OA8R^5RrJI#ymAqor=CbwS6$$@gBzvCu zS%pj#&P!=UlKTK)JT-Vyo*NFN${7ib@ei3nl=V>ff}#{5QTR^g$C(I)KPo!PAQXPC z>~%3hVf97{gL{S{sIM-TG{3vALM93#bwTjO>Vv*{EdULt5C9T%tgDChx(^s5D|gn( zL;x++BcMfkix>c!SkF`CTlfTDqFQaN+iUM&J+^EiD-mHXAVu_7{brU{O-Z?@##MAa zdzqsv*WFt1afCIR^K_0pkyk_o{hC_~@$|`Y!qF{XGi2HIE6z~lui2af|24%;s^>rY zHb=MQSA0d+YSSB^(An~AfU~21b4lLEDX5>x;uxWIyn>%9oDyPe2Y~cum*YT}%Sl6J z_v@WDcE4RAMA=L;B>dC-d=ZUC>5^tu0svn|qfwC{Lkn^~8jVsOrAh4o@ZV@ON)@TF zR50+B;Cu_MVy&%<_1KdIAEm-feNr3L7FDOVsfh#tpxc%N08}Rh019e>gz`{6{!k6% zTplW1|3q!9MWk)4+hcb*^(+9m;}~L-wTEq?{!Y{aDQ!X<@by|Ch6m9jzW3C;ff)oK zHM!h9R)@z{BVe246Jk04$^ngB_zmyyjh}LcBFD#gL(2hO#_^IJ-`5N+ z7n$<-k<7C+#l08XX{7!;(VH}a|CgS*Xec%gX@IC=CIldQw9U3gu&qXV;g9cUGf)&qIVh91t_znrL93q)l@t8uqh5S>^~^mkeyb1W*vS9#I$U_dU*`E3J# zxE0aEt;uC$9Y*Eb_id4hh_e$tgiTw4%=Mrwk)t7KRo#!rwgL(CigYBU3#H&J9T4?i z(N=zC)&a2|LL2d69gwMhR4#f?7qk|r$Q372U}Xc4{s7Y8T@659Rbf4TmQwsE1CKNS znR*dDl9O)W!4${9q+Q^^6mGIFa-8#zEb^@7uWz2J0OA9GsVC^+>u0@*e! zs(kQYmDTAt$gfrP-}o78Nx( zT73>{&u-T3-2iB@9JnK$HN;aKya~vl5!djUJ=_H3=1y$J?@+QGn{lQIh<`h-Cf8Y< zyXk`gKC3HfR@>}0)@2NwoX_H*)4QwF?qDkbaUAaPp>f#v^qGKsa0uTo(wR9}pP7JI zpBC@HRc0W6dRn{~VJe zZ2(^TWw$u3dDFlU4~Ra8q^<0JZ?Bs*?l~$zqP;CZ5_nuDt3p7|&M+%+O9+U3j>*D( zAs`DgOn!0&EK0Txa$v8AfYg1;sIYtx$muzz0_z5WgeDj@-Zuzj;Zr6%X))q6gFvo- z#8f3t4c-fV`Vf%QpED&`H3Vepb7n2J4*?ncH?tZ)KLq5;4W=B+hk-b6GdYq##V(~x zM1FFcDG-T=gL5xri;e;L%Wb9_dyfH;%rjM@-yeHkzA0(@lV{xY6u5m1$l-bByVx=e zWOAO#ky!e~8)YIAooA{Z_{T`>q)5`ZoKCjGYj-(4uM7i0UosgAzGVT7i*kP-1|meeGQqLm$u8e z)CIJZ>g}DKy&hKMa=6_75g=t!i6TR&wI!9~yr&Gs;saP{a)8StK+cIJ3ZKwfON_fl zWHh#9TCLrzCuNl1I(juoHN9>(>-4tJCmQ7{OH0ebaROpWyob}_@=HFTI0|0VLC+Ytm>Zne$ From b4ec1f43104b78512d2be55fa0f6cd9984546fd2 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 11 Aug 2018 06:18:26 +1200 Subject: [PATCH 010/259] Reorient tablet proxy --- scripts/system/tabletRezzer.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/system/tabletRezzer.js b/scripts/system/tabletRezzer.js index 792deec164..2b6e738a26 100644 --- a/scripts/system/tabletRezzer.js +++ b/scripts/system/tabletRezzer.js @@ -30,8 +30,14 @@ y: 0.07, // Distance from joint. z: 0.07 // Distance above palm. }, + /* + // Aligned cross-palm. TABLET_PROXY_ROTATION_LEFT_HAND = Quat.fromVec3Degrees({ x: 0, y: 180, z: 90 }), TABLET_PROXY_ROTATION_RIGHT_HAND = Quat.fromVec3Degrees({ x: 0, y: 180, z: -90 }), + */ + // Aligned with palm. + TABLET_PROXY_ROTATION_LEFT_HAND = Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }), + TABLET_PROXY_ROTATION_RIGHT_HAND = Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }), // State machine PROXY_HIDDEN = 0, From 38d8a9a2da11eff836bdc1d184e3b56a4b5a6bbb Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 11 Aug 2018 07:00:50 +1200 Subject: [PATCH 011/259] Send messages locally if not connected to a domain --- libraries/networking/src/MessagesClient.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/networking/src/MessagesClient.cpp b/libraries/networking/src/MessagesClient.cpp index 2302c22a48..8f252ac69b 100644 --- a/libraries/networking/src/MessagesClient.cpp +++ b/libraries/networking/src/MessagesClient.cpp @@ -116,16 +116,16 @@ void MessagesClient::handleMessagesPacket(QSharedPointer receiv void MessagesClient::sendMessage(QString channel, QString message, bool localOnly) { auto nodeList = DependencyManager::get(); + QUuid senderID = nodeList->getSessionUUID(); if (localOnly) { - QUuid senderID = nodeList->getSessionUUID(); emit messageReceived(channel, message, senderID, true); } else { SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer); - if (messagesMixer) { - QUuid senderID = nodeList->getSessionUUID(); auto packetList = encodeMessagesPacket(channel, message, senderID); nodeList->sendPacketList(std::move(packetList), *messagesMixer); + } else { + emit messageReceived(channel, message, senderID, true); } } } From 2a177e04e165631145ae432775193ba08024c452 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 11 Aug 2018 10:11:56 +1200 Subject: [PATCH 012/259] Misc. tweaks --- libraries/networking/src/MessagesClient.cpp | 5 +++-- scripts/system/tabletRezzer.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/networking/src/MessagesClient.cpp b/libraries/networking/src/MessagesClient.cpp index 8f252ac69b..d6f9d041ea 100644 --- a/libraries/networking/src/MessagesClient.cpp +++ b/libraries/networking/src/MessagesClient.cpp @@ -132,16 +132,17 @@ void MessagesClient::sendMessage(QString channel, QString message, bool localOnl void MessagesClient::sendData(QString channel, QByteArray data, bool localOnly) { auto nodeList = DependencyManager::get(); + QUuid senderID = nodeList->getSessionUUID(); if (localOnly) { - QUuid senderID = nodeList->getSessionUUID(); emit dataReceived(channel, data, senderID, true); } else { SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer); - if (messagesMixer) { QUuid senderID = nodeList->getSessionUUID(); auto packetList = encodeMessagesDataPacket(channel, data, senderID); nodeList->sendPacketList(std::move(packetList), *messagesMixer); + } else { + emit dataReceived(channel, data, senderID, true); } } } diff --git a/scripts/system/tabletRezzer.js b/scripts/system/tabletRezzer.js index 2b6e738a26..b5f94a7c87 100644 --- a/scripts/system/tabletRezzer.js +++ b/scripts/system/tabletRezzer.js @@ -49,7 +49,7 @@ STATE_MACHINE, rezzerState = PROXY_HIDDEN, proxyHand, - PROXY_EXPAND_DURATION = 500, + PROXY_EXPAND_DURATION = 250, PROXY_EXPAND_TIMEOUT = 25, proxyExpandTimer = null, proxyExpandStart, From cd70c97e90f19101968bcae9b77df5aec9d1d1e1 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 16 Aug 2018 10:58:14 +1200 Subject: [PATCH 013/259] Add tablet rezzer script to default scripts --- scripts/defaultScripts.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index b275660c0f..0e6aa08ba6 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -31,7 +31,8 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/dialTone.js", "system/firstPersonHMD.js", "system/tablet-ui/tabletUI.js", - "system/emote.js" + "system/emote.js", + "system/tabletRezzer.js" ]; var DEFAULT_SCRIPTS_SEPARATE = [ "system/controllers/controllerScripts.js", From cb9b7f567a1bdae3338668469f546cf0c92cb147 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 16 Aug 2018 16:08:36 +1200 Subject: [PATCH 014/259] Move update functions into state machine --- scripts/system/tabletRezzer.js | 98 +++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 44 deletions(-) diff --git a/scripts/system/tabletRezzer.js b/scripts/system/tabletRezzer.js index b5f94a7c87..3033177b7e 100644 --- a/scripts/system/tabletRezzer.js +++ b/scripts/system/tabletRezzer.js @@ -121,6 +121,19 @@ } } + function updateProxyHidden() { + // Don't show proxy is tablet is already displayed or are in toolbar mode. + if (HMD.showTablet || tablet.toolbarMode) { + return; + } + // Compare palm directions of hands with vectors from palms to camera. + if (shouldShowProxy(LEFT_HAND)) { + setState(PROXY_VISIBLE, LEFT_HAND); + } else if (shouldShowProxy(RIGHT_HAND)) { + setState(PROXY_VISIBLE, RIGHT_HAND); + } + } + function enterProxyVisible(hand) { proxyHand = hand; proxyOverlay = Overlays.addOverlay("model", { @@ -138,6 +151,25 @@ }); } + function updateProxyVisible() { + // Hide proxy if tablet has been displayed by other means. + if (HMD.showTablet) { + setState(PROXY_HIDDEN); + return; + } + // Check that palm direction of proxy hand still less than maximum angle. + if (!shouldShowProxy(proxyHand)) { + setState(PROXY_HIDDEN); + } + } + + function updateProxyGrabbed() { + // Hide proxy if tablet has been displayed by other means. + if (HMD.showTablet) { + setState(PROXY_HIDDEN); + } + } + function expandProxy() { var scaleFactor = (Date.now() - proxyExpandStart) / PROXY_EXPAND_DURATION; if (scaleFactor < 1) { @@ -168,6 +200,13 @@ proxyExpandTimer = Script.setTimeout(expandProxy, PROXY_EXPAND_TIMEOUT); } + function updateProxyExanding() { + // Hide proxy if tablet has been displayed by other means. + if (HMD.showTablet) { + setState(PROXY_HIDDEN); + } + } + function exitProxyExpanding() { if (proxyExpandTimer !== null) { Script.clearTimeout(proxyExpandTimer); @@ -188,30 +227,35 @@ HMD.openTablet(true); } + function updateTabletOpen() { + // Immediately transition back to PROXY_HIDDEN. + setState(PROXY_HIDDEN); + } + STATE_MACHINE = { PROXY_HIDDEN: { // Tablet proxy could be shown but isn't because hand is oriented to show it or aren't in HMD mode. enter: enterProxyHidden, - update: null, + update: updateProxyHidden, exit: null }, PROXY_VISIBLE: { // Tablet proxy is visible and attached to hand. enter: enterProxyVisible, - update: null, + update: updateProxyVisible, exit: null }, PROXY_GRABBED: { // Other hand has grabbed and is holding the tablet proxy. enter: null, - update: null, + update: updateProxyGrabbed, exit: null }, PROXY_EXPANDING: { // Tablet proxy has been released from grab and is expanding before showing tablet proper. enter: enterProxyExpanding, - update: null, + update: updateProxyExanding, exit: exitProxyExpanding }, TABLET_OPEN: { // Tablet proper is being displayed. enter: enterTabletOpen, - update: null, + update: updateTabletOpen, exit: null } }; @@ -231,6 +275,10 @@ } } + function updateState() { + STATE_MACHINE[STATE_STRINGS[rezzerState]].update(); + } + // #endregion // #region Events ========================================================================================================== @@ -258,45 +306,7 @@ function update() { // Assumes that is HMD.mounted. - switch (rezzerState) { - case PROXY_HIDDEN: - // Don't show proxy is tablet is already displayed or are in toolbar mode. - if (HMD.showTablet || tablet.toolbarMode) { - break; - } - // Compare palm directions of hands with vectors from palms to camera. - if (shouldShowProxy(LEFT_HAND)) { - setState(PROXY_VISIBLE, LEFT_HAND); - } else if (shouldShowProxy(RIGHT_HAND)) { - setState(PROXY_VISIBLE, RIGHT_HAND); - } - break; - case PROXY_VISIBLE: - // Hide proxy if tablet has been displayed by other means. - if (HMD.showTablet) { - setState(PROXY_HIDDEN); - break; - } - // Check that palm direction of proxy hand still less than maximum angle. - if (!shouldShowProxy(proxyHand)) { - setState(PROXY_HIDDEN); - } - break; - case PROXY_GRABBED: - case PROXY_EXPANDING: - // Hide proxy if tablet has been displayed by other means. - if (HMD.showTablet) { - setState(PROXY_HIDDEN); - } - break; - case TABLET_OPEN: - // Immediately transition back to PROXY_HIDDEN. - setState(PROXY_HIDDEN); - break; - default: - error("Missing case: " + rezzerState); - } - + updateState(); updateTimer = Script.setTimeout(update, UPDATE_INTERVAL); } From 7a1a86f1824e57b2d44340be5f5b05e1b7db1a10 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 16 Aug 2018 16:20:28 +1200 Subject: [PATCH 015/259] Expand proxy tablet immediately it is grabbed; grab with either hand --- scripts/system/tabletRezzer.js | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/scripts/system/tabletRezzer.js b/scripts/system/tabletRezzer.js index 3033177b7e..c261ebb059 100644 --- a/scripts/system/tabletRezzer.js +++ b/scripts/system/tabletRezzer.js @@ -42,10 +42,9 @@ // State machine PROXY_HIDDEN = 0, PROXY_VISIBLE = 1, - PROXY_GRABBED = 2, - PROXY_EXPANDING = 3, - TABLET_OPEN = 4, - STATE_STRINGS = ["PROXY_HIDDEN", "PROXY_VISIBLE", "PROXY_GRABBED", "PROXY_EXPANDING", "TABLET_OPEN"], + PROXY_EXPANDING = 2, + TABLET_OPEN = 3, + STATE_STRINGS = ["PROXY_HIDDEN", "PROXY_VISIBLE", "PROXY_EXPANDING", "TABLET_OPEN"], STATE_MACHINE, rezzerState = PROXY_HIDDEN, proxyHand, @@ -163,13 +162,6 @@ } } - function updateProxyGrabbed() { - // Hide proxy if tablet has been displayed by other means. - if (HMD.showTablet) { - setState(PROXY_HIDDEN); - } - } - function expandProxy() { var scaleFactor = (Date.now() - proxyExpandStart) / PROXY_EXPAND_DURATION; if (scaleFactor < 1) { @@ -243,12 +235,7 @@ update: updateProxyVisible, exit: null }, - PROXY_GRABBED: { // Other hand has grabbed and is holding the tablet proxy. - enter: null, - update: updateProxyGrabbed, - exit: null - }, - PROXY_EXPANDING: { // Tablet proxy has been released from grab and is expanding before showing tablet proper. + PROXY_EXPANDING: { // Tablet proxy has been grabbed and is expanding before showing tablet proper. enter: enterProxyExpanding, update: updateProxyExanding, exit: exitProxyExpanding @@ -328,11 +315,7 @@ return; } - // Don't transition into PROXY_GRABBED unless the tablet proxy is grabbed with "other" hand. However, once it has been - // grabbed then the original hand can grab it back and grow it from there. - if (message.action === "grab" && message.joint !== HAND_NAMES[proxyHand] && rezzerState === PROXY_VISIBLE) { - setState(PROXY_GRABBED); - } else if (message.action === "release" && rezzerState === PROXY_GRABBED) { + if (message.action === "grab" && rezzerState === PROXY_VISIBLE) { setState(PROXY_EXPANDING); } } From 40f7914aae987394397956ce86d95d1e10875b09 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 16 Aug 2018 19:11:03 +1200 Subject: [PATCH 016/259] Expand from lower left/right of proxy tablet --- scripts/system/tabletRezzer.js | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/scripts/system/tabletRezzer.js b/scripts/system/tabletRezzer.js index c261ebb059..1779defb28 100644 --- a/scripts/system/tabletRezzer.js +++ b/scripts/system/tabletRezzer.js @@ -48,6 +48,13 @@ STATE_MACHINE, rezzerState = PROXY_HIDDEN, proxyHand, + PROXY_GRAB_HANDLES = [ + { x: 0.5, y: -0.4, z: 0 }, + { x: -0.5, y: -0.4, z: 0 } + ], + proxyGrabHand, + proxyGrabHandleLocalPosition, + proxyGrabLocalRotation = Quat.IDENTITY, PROXY_EXPAND_DURATION = 250, PROXY_EXPAND_TIMEOUT = 25, proxyExpandTimer = null, @@ -109,6 +116,10 @@ return MyAvatar.getJointIndex(handJointName(hand)); } + function otherHand(hand) { + return hand === LEFT_HAND ? RIGHT_HAND : LEFT_HAND; + } + // #endregion // #region State Machine =================================================================================================== @@ -164,11 +175,16 @@ function expandProxy() { var scaleFactor = (Date.now() - proxyExpandStart) / PROXY_EXPAND_DURATION; + var tabletScaleFactor = avatarScale * (1 + scaleFactor * (proxyTargetWidth - proxyInitialWidth) / proxyInitialWidth); if (scaleFactor < 1) { Overlays.editOverlay(proxyOverlay, { - dimensions: Vec3.multiply( - avatarScale * (1 + scaleFactor * (proxyTargetWidth - proxyInitialWidth) / proxyInitialWidth), - TABLET_PROXY_DIMENSIONS) + dimensions: Vec3.multiply(tabletScaleFactor, TABLET_PROXY_DIMENSIONS), + localPosition: + Vec3.sum(proxyGrabHandleLocalPosition, + Vec3.multiplyQbyV(proxyGrabLocalRotation, + Vec3.multiply(-tabletScaleFactor, + Vec3.multiplyVbyV(PROXY_GRAB_HANDLES[proxyGrabHand], TABLET_PROXY_DIMENSIONS))) + ) }); proxyExpandTimer = Script.setTimeout(expandProxy, PROXY_EXPAND_TIMEOUT); return; @@ -179,11 +195,12 @@ } function enterProxyExpanding() { - // Detach from hand. - Overlays.editOverlay(proxyOverlay, { - parentID: Uuid.NULL, - parentJointIndex: -1 - }); + // Grab details. + var properties = Overlays.getProperties(proxyOverlay, ["localPosition", "localRotation"]); + proxyGrabLocalRotation = properties.localRotation; + proxyGrabHandleLocalPosition = Vec3.sum(properties.localPosition, + Vec3.multiplyQbyV(proxyGrabLocalRotation, + Vec3.multiplyVbyV(PROXY_GRAB_HANDLES[proxyGrabHand], TABLET_PROXY_DIMENSIONS))); // Start expanding. proxyInitialWidth = TABLET_PROXY_DIMENSIONS.x; @@ -316,6 +333,7 @@ } if (message.action === "grab" && rezzerState === PROXY_VISIBLE) { + proxyGrabHand = message.joint === HAND_NAMES[proxyHand] ? proxyHand : otherHand(proxyHand); setState(PROXY_EXPANDING); } } From 2c389af751d4710c1556eeb7e1183783b4f0b156 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 17 Aug 2018 16:18:22 +1200 Subject: [PATCH 017/259] Display mini tablet only if camera is also looking at the hand --- scripts/system/tabletRezzer.js | 44 ++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/scripts/system/tabletRezzer.js b/scripts/system/tabletRezzer.js index 1779defb28..8fe206e9e0 100644 --- a/scripts/system/tabletRezzer.js +++ b/scripts/system/tabletRezzer.js @@ -124,6 +124,29 @@ // #region State Machine =================================================================================================== + function shouldShowProxy(hand) { + // Should show tablet proxy if hand is oriented toward the camera and the camera is oriented toward the proxy tablet. + var pose, + jointIndex, + handPosition, + handOrientation, + cameraToHandDirection; + + pose = Controller.getPoseValue(hand === LEFT_HAND ? Controller.Standard.LeftHand : Controller.Standard.RightHand); + if (!pose.valid) { + return false; + } + + jointIndex = handJointIndex(hand); + handPosition = Vec3.sum(MyAvatar.position, + Vec3.multiplyQbyV(MyAvatar.orientation, MyAvatar.getAbsoluteJointTranslationInObjectFrame(jointIndex))); + handOrientation = Quat.multiply(MyAvatar.orientation, MyAvatar.getAbsoluteJointRotationInObjectFrame(jointIndex)); + cameraToHandDirection = Vec3.normalize(Vec3.subtract(handPosition, Camera.position)); + + return Vec3.dot(cameraToHandDirection, Quat.getForward(handOrientation)) > MIN_HAND_CAMERA_ANGLE_COS + && Vec3.dot(cameraToHandDirection, Quat.getForward(Camera.orientation)) > MIN_HAND_CAMERA_ANGLE_COS; + } + function enterProxyHidden() { if (proxyOverlay) { Overlays.deleteOverlay(proxyOverlay); @@ -287,27 +310,6 @@ // #region Events ========================================================================================================== - function shouldShowProxy(hand) { - // Should show tablet proxy if hand is oriented towards the camera. - var pose, - jointIndex, - handPosition, - handOrientation; - - pose = Controller.getPoseValue(hand === LEFT_HAND ? Controller.Standard.LeftHand : Controller.Standard.RightHand); - if (!pose.valid) { - return false; - } - - jointIndex = handJointIndex(hand); - handPosition = Vec3.sum(MyAvatar.position, - Vec3.multiplyQbyV(MyAvatar.orientation, MyAvatar.getAbsoluteJointTranslationInObjectFrame(jointIndex))); - handOrientation = Quat.multiply(MyAvatar.orientation, MyAvatar.getAbsoluteJointRotationInObjectFrame(jointIndex)); - - return Vec3.dot(Vec3.normalize(Vec3.subtract(handPosition, Camera.position)), Quat.getForward(handOrientation)) - > MIN_HAND_CAMERA_ANGLE_COS; - } - function update() { // Assumes that is HMD.mounted. updateState(); From 94041291d5c7e66b10ffac93d7b7a0cec39988b5 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 20 Aug 2018 10:58:01 +1200 Subject: [PATCH 018/259] Add web3d overlay for UI surface --- scripts/system/html/tabletRezzer.html | 21 +++++++++ scripts/system/tabletRezzer.js | 66 ++++++++++++++++++++------- 2 files changed, 70 insertions(+), 17 deletions(-) create mode 100644 scripts/system/html/tabletRezzer.html diff --git a/scripts/system/html/tabletRezzer.html b/scripts/system/html/tabletRezzer.html new file mode 100644 index 0000000000..6a0e0d0448 --- /dev/null +++ b/scripts/system/html/tabletRezzer.html @@ -0,0 +1,21 @@ + + + + + + + + + + + Hello world! + + diff --git a/scripts/system/tabletRezzer.js b/scripts/system/tabletRezzer.js index 8fe206e9e0..fe4bc6477b 100644 --- a/scripts/system/tabletRezzer.js +++ b/scripts/system/tabletRezzer.js @@ -16,28 +16,39 @@ Script.include("./libraries/utils.js"); - var // Overlay + var // Base overlay proxyOverlay = null, - TABLET_PROXY_MODEL = Script.resolvePath("./assets/models/tinyTablet.fbx"), - TABLET_PROXY_DIMENSIONS = { x: 0.0637, y: 0.0965, z: 0.0046 }, // Proportional to tablet proper. - TABLET_PROXY_POSITION_LEFT_HAND = { + PROXY_MODEL = Script.resolvePath("./assets/models/tinyTablet.fbx"), + PROXY_DIMENSIONS = { x: 0.0637, y: 0.0965, z: 0.0046 }, // Proportional to tablet proper. + PROXY_POSITION_LEFT_HAND = { x: 0, y: 0.07, // Distance from joint. z: 0.07 // Distance above palm. }, - TABLET_PROXY_POSITION_RIGHT_HAND = { + PROXY_POSITION_RIGHT_HAND = { x: 0, y: 0.07, // Distance from joint. z: 0.07 // Distance above palm. }, /* // Aligned cross-palm. - TABLET_PROXY_ROTATION_LEFT_HAND = Quat.fromVec3Degrees({ x: 0, y: 180, z: 90 }), - TABLET_PROXY_ROTATION_RIGHT_HAND = Quat.fromVec3Degrees({ x: 0, y: 180, z: -90 }), + PROXY_ROTATION_LEFT_HAND = Quat.fromVec3Degrees({ x: 0, y: 180, z: 90 }), + PROXY_ROTATION_RIGHT_HAND = Quat.fromVec3Degrees({ x: 0, y: 180, z: -90 }), */ // Aligned with palm. - TABLET_PROXY_ROTATION_LEFT_HAND = Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }), - TABLET_PROXY_ROTATION_RIGHT_HAND = Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }), + PROXY_ROTATION_LEFT_HAND = Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }), + PROXY_ROTATION_RIGHT_HAND = Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }), + + // UI overlay. + proxyUIOverlay = null, + PROXY_UI_HTML = Script.resolvePath("./html/tabletRezzer.html"), + PROXY_UI_DIMENSIONS = { x: 0.0577, y: 0.0905 }, + PROXY_UI_WIDTH_PIXELS = 150, + METERS_TO_INCHES = 39.3701, + PROXY_UI_DPI = PROXY_UI_WIDTH_PIXELS / (PROXY_UI_DIMENSIONS.x * METERS_TO_INCHES), + PROXY_UI_OFFSET = 0.001, // Above model surface. + PROXY_UI_LOCAL_POSITION = { x: 0, y: 0, z: -(PROXY_DIMENSIONS.z / 2 + PROXY_UI_OFFSET) }, + PROXY_UI_LOCAL_ROTATION = Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }), // State machine PROXY_HIDDEN = 0, @@ -149,7 +160,9 @@ function enterProxyHidden() { if (proxyOverlay) { + Overlays.deleteOverlay(proxyUIOverlay); Overlays.deleteOverlay(proxyOverlay); + proxyUIOverlay = null; proxyOverlay = null; } } @@ -170,18 +183,30 @@ function enterProxyVisible(hand) { proxyHand = hand; proxyOverlay = Overlays.addOverlay("model", { - url: TABLET_PROXY_MODEL, + url: PROXY_MODEL, parentID: MyAvatar.SELF_ID, parentJointIndex: handJointIndex(proxyHand), localPosition: Vec3.multiply(avatarScale, - proxyHand === LEFT_HAND ? TABLET_PROXY_POSITION_LEFT_HAND : TABLET_PROXY_POSITION_RIGHT_HAND), - localRotation: proxyHand === LEFT_HAND ? TABLET_PROXY_ROTATION_LEFT_HAND : TABLET_PROXY_ROTATION_RIGHT_HAND, - dimensions: Vec3.multiply(avatarScale, TABLET_PROXY_DIMENSIONS), + proxyHand === LEFT_HAND ? PROXY_POSITION_LEFT_HAND : PROXY_POSITION_RIGHT_HAND), + localRotation: proxyHand === LEFT_HAND ? PROXY_ROTATION_LEFT_HAND : PROXY_ROTATION_RIGHT_HAND, + dimensions: Vec3.multiply(avatarScale, PROXY_DIMENSIONS), solid: true, grabbable: true, displayInFront: true, visible: true }); + proxyUIOverlay = Overlays.addOverlay("web3d", { + url: PROXY_UI_HTML, + parentID: proxyOverlay, + localPosition: PROXY_UI_LOCAL_POSITION, + localRotation: PROXY_UI_LOCAL_ROTATION, + dimensions: PROXY_UI_DIMENSIONS, + dpi: PROXY_UI_DPI, + alpha: 1.0, + grabbable: false, + displayInFront: true, + visible: true + }); } function updateProxyVisible() { @@ -201,14 +226,19 @@ var tabletScaleFactor = avatarScale * (1 + scaleFactor * (proxyTargetWidth - proxyInitialWidth) / proxyInitialWidth); if (scaleFactor < 1) { Overlays.editOverlay(proxyOverlay, { - dimensions: Vec3.multiply(tabletScaleFactor, TABLET_PROXY_DIMENSIONS), + dimensions: Vec3.multiply(tabletScaleFactor, PROXY_DIMENSIONS), localPosition: Vec3.sum(proxyGrabHandleLocalPosition, Vec3.multiplyQbyV(proxyGrabLocalRotation, Vec3.multiply(-tabletScaleFactor, - Vec3.multiplyVbyV(PROXY_GRAB_HANDLES[proxyGrabHand], TABLET_PROXY_DIMENSIONS))) + Vec3.multiplyVbyV(PROXY_GRAB_HANDLES[proxyGrabHand], PROXY_DIMENSIONS))) ) }); + Overlays.editOverlay(proxyUIOverlay, { + dimensions: Vec3.multiply(tabletScaleFactor, PROXY_UI_DIMENSIONS), + localPosition: Vec3.multiply(tabletScaleFactor, PROXY_UI_LOCAL_POSITION), + dpi: PROXY_UI_DPI / tabletScaleFactor + }); proxyExpandTimer = Script.setTimeout(expandProxy, PROXY_EXPAND_TIMEOUT); return; } @@ -223,10 +253,10 @@ proxyGrabLocalRotation = properties.localRotation; proxyGrabHandleLocalPosition = Vec3.sum(properties.localPosition, Vec3.multiplyQbyV(proxyGrabLocalRotation, - Vec3.multiplyVbyV(PROXY_GRAB_HANDLES[proxyGrabHand], TABLET_PROXY_DIMENSIONS))); + Vec3.multiplyVbyV(PROXY_GRAB_HANDLES[proxyGrabHand], PROXY_DIMENSIONS))); // Start expanding. - proxyInitialWidth = TABLET_PROXY_DIMENSIONS.x; + proxyInitialWidth = PROXY_DIMENSIONS.x; proxyTargetWidth = getTabletWidthFromSettings(); proxyExpandStart = Date.now(); proxyExpandTimer = Script.setTimeout(expandProxy, PROXY_EXPAND_TIMEOUT); @@ -249,6 +279,8 @@ function enterTabletOpen() { var proxyOverlayProperties = Overlays.getProperties(proxyOverlay, ["position", "orientation"]); + Overlays.deleteOverlay(proxyUIOverlay); + proxyUIOverlay = null; Overlays.deleteOverlay(proxyOverlay); proxyOverlay = null; From aca343330fb203d861a30e8b5c1a943df58b3dd4 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 20 Aug 2018 16:00:02 +1200 Subject: [PATCH 019/259] Base UI elements --- scripts/defaultScripts.js | 2 +- scripts/system/html/css/miniTablet.css | 102 ++++++++++++++++++ scripts/system/html/miniTablet.html | 31 ++++++ scripts/system/html/tabletRezzer.html | 21 ---- .../system/{tabletRezzer.js => miniTablet.js} | 4 +- 5 files changed, 136 insertions(+), 24 deletions(-) create mode 100644 scripts/system/html/css/miniTablet.css create mode 100644 scripts/system/html/miniTablet.html delete mode 100644 scripts/system/html/tabletRezzer.html rename scripts/system/{tabletRezzer.js => miniTablet.js} (99%) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 0e6aa08ba6..2aee26f71e 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -32,7 +32,7 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/firstPersonHMD.js", "system/tablet-ui/tabletUI.js", "system/emote.js", - "system/tabletRezzer.js" + "system/miniTablet.js" ]; var DEFAULT_SCRIPTS_SEPARATE = [ "system/controllers/controllerScripts.js", diff --git a/scripts/system/html/css/miniTablet.css b/scripts/system/html/css/miniTablet.css new file mode 100644 index 0000000000..d0585e489a --- /dev/null +++ b/scripts/system/html/css/miniTablet.css @@ -0,0 +1,102 @@ +/* +miniTablet.css + +Created by David Rowe on 20 Aug 2018. +Copyright 2018 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 +*/ + +* { + box-sizing: border-box; + padding: 0; + margin: 0; + user-select: none; +} + +html { + background-color: #121212; +} + +body { + height: 100%; +} + +section { + background-color: #404040; + position: relative; + padding: 15px; +} + +section .button { + width: 100%; + height: 88px; + background-color: #252525; + margin-top: 10px; + text-align: center; + border-radius: 8px; +} + + section .button.off { + border: 2px solid #6a6a6a; + background-color: #303030; + } + + section .button.off:hover { + border: 2px solid #1fc6a6; + } + + section .button.on { + border: 2px solid #1fc6a6; + background-color: #1fc6a6; + } + + section .button.on:hover { + border: 2px solid #e3e3e3; + } + + section .button:first-child { + margin-top: 0; + } + +img { + width: 60px; +} + +#mute { + padding-top: 12px; +} + +#bubble { + padding-top: 14px; +} + +footer { + position: absolute; + bottom: 0; + width: 100%; + height: 30px; + text-align: center; + color: #e3e3e3; + font-weight: bold; +} + + footer div { + display: inline-block; + height: 12px; + width: 24px; + position: relative; + top: 16px; + } + + footer div p { + position: relative; + top: -8px; + } + + footer .button:hover { + border: 1px solid #1fc6a6; + border-radius: 2px; + margin: -1px; + } \ No newline at end of file diff --git a/scripts/system/html/miniTablet.html b/scripts/system/html/miniTablet.html new file mode 100644 index 0000000000..f8216e5515 --- /dev/null +++ b/scripts/system/html/miniTablet.html @@ -0,0 +1,31 @@ + + + + + + + + + + +

+
+ +
+
+ +
+
+
+

...

+
+ + diff --git a/scripts/system/html/tabletRezzer.html b/scripts/system/html/tabletRezzer.html deleted file mode 100644 index 6a0e0d0448..0000000000 --- a/scripts/system/html/tabletRezzer.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - Hello world! - - diff --git a/scripts/system/tabletRezzer.js b/scripts/system/miniTablet.js similarity index 99% rename from scripts/system/tabletRezzer.js rename to scripts/system/miniTablet.js index fe4bc6477b..aa8b96665b 100644 --- a/scripts/system/tabletRezzer.js +++ b/scripts/system/miniTablet.js @@ -1,5 +1,5 @@ // -// tabletRezzer.js +// miniTablet.js // // Created by David Rowe on 9 Aug 2018. // Copyright 2018 High Fidelity, Inc. @@ -41,7 +41,7 @@ // UI overlay. proxyUIOverlay = null, - PROXY_UI_HTML = Script.resolvePath("./html/tabletRezzer.html"), + PROXY_UI_HTML = Script.resolvePath("./html/miniTablet.html"), PROXY_UI_DIMENSIONS = { x: 0.0577, y: 0.0905 }, PROXY_UI_WIDTH_PIXELS = 150, METERS_TO_INCHES = 39.3701, From ff54516790fc6946d51810a0888207d15eb881ca Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 20 Aug 2018 17:46:07 +1200 Subject: [PATCH 020/259] Maintain overlays ready for display in HMD mode --- scripts/system/miniTablet.js | 225 ++++++++++++++++++++++------------- 1 file changed, 140 insertions(+), 85 deletions(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index aa8b96665b..f0741582c3 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -49,15 +49,18 @@ PROXY_UI_OFFSET = 0.001, // Above model surface. PROXY_UI_LOCAL_POSITION = { x: 0, y: 0, z: -(PROXY_DIMENSIONS.z / 2 + PROXY_UI_OFFSET) }, PROXY_UI_LOCAL_ROTATION = Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }), + proxyUIOverlayEnabled = false, + PROXY_UI_OVERLAY_ENABLED_DELAY = 500, // State machine - PROXY_HIDDEN = 0, - PROXY_VISIBLE = 1, - PROXY_EXPANDING = 2, - TABLET_OPEN = 3, - STATE_STRINGS = ["PROXY_HIDDEN", "PROXY_VISIBLE", "PROXY_EXPANDING", "TABLET_OPEN"], + PROXY_DISABLED = 0, + PROXY_HIDDEN = 1, + PROXY_VISIBLE = 2, + PROXY_EXPANDING = 3, + TABLET_OPEN = 4, + STATE_STRINGS = ["PROXY_DISABLED", "PROXY_HIDDEN", "PROXY_VISIBLE", "PROXY_EXPANDING", "TABLET_OPEN"], STATE_MACHINE, - rezzerState = PROXY_HIDDEN, + rezzerState = PROXY_DISABLED, proxyHand, PROXY_GRAB_HANDLES = [ { x: 0.5, y: -0.4, z: 0 }, @@ -86,7 +89,7 @@ LEFT_HAND = 0, RIGHT_HAND = 1, HAND_NAMES = ["LeftHand", "RightHand"], - DEBUG = false; + DEBUG = true; // #region Utilities ======================================================================================================= @@ -133,8 +136,117 @@ // #endregion + // #region UI ============================================================================================================== + + function createUI() { + proxyOverlay = Overlays.addOverlay("model", { + url: PROXY_MODEL, + dimensions: Vec3.multiply(avatarScale, PROXY_DIMENSIONS), + solid: true, + grabbable: true, + displayInFront: true, + visible: false + }); + proxyUIOverlay = Overlays.addOverlay("web3d", { + url: PROXY_UI_HTML, + parentID: proxyOverlay, + localPosition: Vec3.multiply(avatarScale, PROXY_UI_LOCAL_POSITION), + localRotation: PROXY_UI_LOCAL_ROTATION, + dimensions: Vec3.multiply(avatarScale, PROXY_UI_DIMENSIONS), + dpi: PROXY_UI_DPI / avatarScale, + alpha: 0, // Hide overlay while its content is being created. + grabbable: false, + displayInFront: true, + visible: false + }); + + proxyUIOverlayEnabled = false; // This and alpha = 0 hides overlay while its content is being created. + } + + function showUI(hand) { + Overlays.editOverlay(proxyOverlay, { + parentID: MyAvatar.SELF_ID, + parentJointIndex: handJointIndex(proxyHand), + localPosition: Vec3.multiply(avatarScale, + proxyHand === LEFT_HAND ? PROXY_POSITION_LEFT_HAND : PROXY_POSITION_RIGHT_HAND), + localRotation: proxyHand === LEFT_HAND ? PROXY_ROTATION_LEFT_HAND : PROXY_ROTATION_RIGHT_HAND, + dimensions: Vec3.multiply(avatarScale, PROXY_DIMENSIONS), + visible: true + }); + Overlays.editOverlay(proxyUIOverlay, { + localPosition: Vec3.multiply(avatarScale, PROXY_UI_LOCAL_POSITION), + dimensions: Vec3.multiply(avatarScale, PROXY_UI_DIMENSIONS), + dpi: PROXY_UI_DPI / avatarScale, + visible: true + }); + + if (!proxyUIOverlayEnabled) { + // Overlay content is created the first time it is visible to the user. The initial creation displays artefacts. + // Delay showing UI overlay until after giving it time for its content to be created. + Script.setTimeout(function () { + Overlays.editOverlay(proxyUIOverlay, { alpha: 1.0 }); + }, PROXY_UI_OVERLAY_ENABLED_DELAY); + } + } + + function sizeUI(scaleFactor) { + Overlays.editOverlay(proxyOverlay, { + localPosition: + Vec3.sum(proxyGrabHandleLocalPosition, + Vec3.multiplyQbyV(proxyGrabLocalRotation, + Vec3.multiply(-scaleFactor, + Vec3.multiplyVbyV(PROXY_GRAB_HANDLES[proxyGrabHand], PROXY_DIMENSIONS))) + ), + dimensions: Vec3.multiply(scaleFactor, PROXY_DIMENSIONS) + }); + Overlays.editOverlay(proxyUIOverlay, { + localPosition: Vec3.multiply(scaleFactor, PROXY_UI_LOCAL_POSITION), + dimensions: Vec3.multiply(scaleFactor, PROXY_UI_DIMENSIONS), + dpi: PROXY_UI_DPI / scaleFactor + }); + } + + function hideUI() { + Overlays.editOverlay(proxyOverlay, { + visible: false + }); + Overlays.editOverlay(proxyUIOverlay, { + visible: false + }); + } + + function destroyUI() { + if (proxyOverlay) { + Overlays.deleteOverlay(proxyUIOverlay); + Overlays.deleteOverlay(proxyOverlay); + proxyUIOverlay = null; + proxyOverlay = null; + } + } + + // #endregion + // #region State Machine =================================================================================================== + function enterProxyDisabled() { + // Stop updates. + if (updateTimer !== null) { + Script.clearTimeout(updateTimer); + updateTimer = null; + } + + // Don't keep overlays prepared if in desktop mode. + destroyUI(); + } + + function exitProxyDisabled() { + // Create UI so that it's ready to be displayed without seeing artefacts from creating the UI. + createUI(); + + // Start updates. + updateTimer = Script.setTimeout(updateState, UPDATE_INTERVAL); + } + function shouldShowProxy(hand) { // Should show tablet proxy if hand is oriented toward the camera and the camera is oriented toward the proxy tablet. var pose, @@ -159,16 +271,11 @@ } function enterProxyHidden() { - if (proxyOverlay) { - Overlays.deleteOverlay(proxyUIOverlay); - Overlays.deleteOverlay(proxyOverlay); - proxyUIOverlay = null; - proxyOverlay = null; - } + hideUI(); } function updateProxyHidden() { - // Don't show proxy is tablet is already displayed or are in toolbar mode. + // Don't show proxy if tablet is already displayed or in toolbar mode. if (HMD.showTablet || tablet.toolbarMode) { return; } @@ -182,31 +289,7 @@ function enterProxyVisible(hand) { proxyHand = hand; - proxyOverlay = Overlays.addOverlay("model", { - url: PROXY_MODEL, - parentID: MyAvatar.SELF_ID, - parentJointIndex: handJointIndex(proxyHand), - localPosition: Vec3.multiply(avatarScale, - proxyHand === LEFT_HAND ? PROXY_POSITION_LEFT_HAND : PROXY_POSITION_RIGHT_HAND), - localRotation: proxyHand === LEFT_HAND ? PROXY_ROTATION_LEFT_HAND : PROXY_ROTATION_RIGHT_HAND, - dimensions: Vec3.multiply(avatarScale, PROXY_DIMENSIONS), - solid: true, - grabbable: true, - displayInFront: true, - visible: true - }); - proxyUIOverlay = Overlays.addOverlay("web3d", { - url: PROXY_UI_HTML, - parentID: proxyOverlay, - localPosition: PROXY_UI_LOCAL_POSITION, - localRotation: PROXY_UI_LOCAL_ROTATION, - dimensions: PROXY_UI_DIMENSIONS, - dpi: PROXY_UI_DPI, - alpha: 1.0, - grabbable: false, - displayInFront: true, - visible: true - }); + showUI(proxyHand); } function updateProxyVisible() { @@ -225,20 +308,7 @@ var scaleFactor = (Date.now() - proxyExpandStart) / PROXY_EXPAND_DURATION; var tabletScaleFactor = avatarScale * (1 + scaleFactor * (proxyTargetWidth - proxyInitialWidth) / proxyInitialWidth); if (scaleFactor < 1) { - Overlays.editOverlay(proxyOverlay, { - dimensions: Vec3.multiply(tabletScaleFactor, PROXY_DIMENSIONS), - localPosition: - Vec3.sum(proxyGrabHandleLocalPosition, - Vec3.multiplyQbyV(proxyGrabLocalRotation, - Vec3.multiply(-tabletScaleFactor, - Vec3.multiplyVbyV(PROXY_GRAB_HANDLES[proxyGrabHand], PROXY_DIMENSIONS))) - ) - }); - Overlays.editOverlay(proxyUIOverlay, { - dimensions: Vec3.multiply(tabletScaleFactor, PROXY_UI_DIMENSIONS), - localPosition: Vec3.multiply(tabletScaleFactor, PROXY_UI_LOCAL_POSITION), - dpi: PROXY_UI_DPI / tabletScaleFactor - }); + sizeUI(tabletScaleFactor); proxyExpandTimer = Script.setTimeout(expandProxy, PROXY_EXPAND_TIMEOUT); return; } @@ -279,10 +349,7 @@ function enterTabletOpen() { var proxyOverlayProperties = Overlays.getProperties(proxyOverlay, ["position", "orientation"]); - Overlays.deleteOverlay(proxyUIOverlay); - proxyUIOverlay = null; - Overlays.deleteOverlay(proxyOverlay); - proxyOverlay = null; + hideUI(); Overlays.editOverlay(HMD.tabletID, { position: proxyOverlayProperties.position, @@ -297,6 +364,11 @@ } STATE_MACHINE = { + PROXY_DISABLED: { // Tablet proxy cannot be shown because in desktop mode. + enter: enterProxyDisabled, + update: null, + exit: exitProxyDisabled + }, PROXY_HIDDEN: { // Tablet proxy could be shown but isn't because hand is oriented to show it or aren't in HMD mode. enter: enterProxyHidden, update: updateProxyHidden, @@ -336,18 +408,13 @@ function updateState() { STATE_MACHINE[STATE_STRINGS[rezzerState]].update(); + updateTimer = Script.setTimeout(updateState, UPDATE_INTERVAL); } // #endregion // #region Events ========================================================================================================== - function update() { - // Assumes that is HMD.mounted. - updateState(); - updateTimer = Script.setTimeout(update, UPDATE_INTERVAL); - } - function onScaleChanged() { avatarScale = MyAvatar.scale; // Clamp scale in order to work around M17434. @@ -372,22 +439,12 @@ } } - function onMountedChanged() { - // Tablet proxy only available when HMD is mounted. - - if (HMD.mounted) { - if (updateTimer === null) { - update(); - } - return; - } - - if (updateTimer !== null) { - Script.clearTimeout(updateTimer); - updateTimer = null; - } - if (rezzerState !== PROXY_HIDDEN) { + function onDisplayModeChanged() { + // Tablet proxy only available when HMD is active. + if (HMD.active) { setState(PROXY_HIDDEN); + } else { + setState(PROXY_DISABLED); } } @@ -401,10 +458,9 @@ Messages.subscribe(HIFI_OBJECT_MANIPULATION_CHANNEL); Messages.messageReceived.connect(onMessageReceived); - HMD.mountedChanged.connect(onMountedChanged); - HMD.displayModeChanged.connect(onMountedChanged); // For the case that the HMD is already worn when the script starts. - if (HMD.mounted) { - update(); + HMD.displayModeChanged.connect(onDisplayModeChanged); + if (HMD.active) { + setState(PROXY_HIDDEN); } } @@ -414,10 +470,9 @@ updateTimer = null; } - setState(PROXY_HIDDEN); + setState(PROXY_DISABLED); - HMD.displayModeChanged.disconnect(onMountedChanged); - HMD.mountedChanged.disconnect(onMountedChanged); + HMD.displayModeChanged.disconnect(onDisplayModeChanged); Messages.messageReceived.disconnect(onMessageReceived); Messages.unsubscribe(HIFI_OBJECT_MANIPULATION_CHANNEL); From 4e23caf46deebd3a7f28fcd3179f735a47ec6cbb Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 20 Aug 2018 17:57:59 +1200 Subject: [PATCH 021/259] Fix tablet proper not being grabbed once mini tablet has expanded --- scripts/system/miniTablet.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index f0741582c3..08e7103ba4 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -89,7 +89,7 @@ LEFT_HAND = 0, RIGHT_HAND = 1, HAND_NAMES = ["LeftHand", "RightHand"], - DEBUG = true; + DEBUG = false; // #region Utilities ======================================================================================================= @@ -208,6 +208,7 @@ function hideUI() { Overlays.editOverlay(proxyOverlay, { + parentID: Uuid.NULL, // Release hold so that hand can grab tablet proper. visible: false }); Overlays.editOverlay(proxyUIOverlay, { From 6791d3f1b6c6939bb9858c123a29e344aaa785dc Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 21 Aug 2018 08:36:04 +1200 Subject: [PATCH 022/259] Mini tablet button icons and states --- scripts/system/html/css/miniTablet.css | 4 +- scripts/system/html/js/miniTablet.js | 88 ++++++++++++++++++++++++++ scripts/system/html/miniTablet.html | 1 + scripts/system/miniTablet.js | 67 +++++++++++++++++++- 4 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 scripts/system/html/js/miniTablet.js diff --git a/scripts/system/html/css/miniTablet.css b/scripts/system/html/css/miniTablet.css index d0585e489a..5ff9944a2c 100644 --- a/scripts/system/html/css/miniTablet.css +++ b/scripts/system/html/css/miniTablet.css @@ -96,7 +96,7 @@ footer { } footer .button:hover { - border: 1px solid #1fc6a6; + border: 1px solid #e3e3e3; border-radius: 2px; margin: -1px; - } \ No newline at end of file + } diff --git a/scripts/system/html/js/miniTablet.js b/scripts/system/html/js/miniTablet.js new file mode 100644 index 0000000000..450a87ff8d --- /dev/null +++ b/scripts/system/html/js/miniTablet.js @@ -0,0 +1,88 @@ +// +// miniTablet.js +// +// Created by David Rowe on 20 Aug 2018. +// Copyright 2018 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 EventBridge */ +/* eslint-env browser */ + +(function () { + + "use strict"; + + var // EventBridge + READY_MESSAGE = "ready", // Engine <== Dialog + MUTE_MESSAGE = "mute", // Engine <=> Dialog + BUBBLE_MESSAGE = "bubble", // Engine <=> Dialog + + muteButton, + muteImage, + bubbleButton, + bubbleImage; + + // #region Communications ================================================================================================== + + function onScriptEventReceived(data) { + var message; + + try { + message = JSON.parse(data); + } catch (e) { + console.error("EventBridge message error"); + return; + } + + switch (message.type) { + case MUTE_MESSAGE: + muteImage.src = message.icon; + break; + case BUBBLE_MESSAGE: + bubbleButton.classList.remove(message.on ? "off" : "on"); + bubbleButton.classList.add(message.on ? "on" : "off"); + bubbleImage.src = message.icon; + break; + } + } + + function connectEventBridge() { + EventBridge.scriptEventReceived.connect(onScriptEventReceived); + EventBridge.emitWebEvent(JSON.stringify({ + type: READY_MESSAGE + })); + } + + function disconnectEventBridge() { + EventBridge.scriptEventReceived.disconnect(onScriptEventReceived); + } + + // #endregion + + // #region Set-up and tear-down ============================================================================================ + + function onUnload() { + disconnectEventBridge(); + } + + function onLoad() { + muteButton = document.getElementById("mute"); + muteImage = document.getElementById("mute-img"); + bubbleButton = document.getElementById("bubble"); + bubbleImage = document.getElementById("bubble-img"); + + connectEventBridge(); + + document.body.onunload = function () { + onUnload(); + }; + } + + onLoad(); + + // #endregion + +}()); diff --git a/scripts/system/html/miniTablet.html b/scripts/system/html/miniTablet.html index f8216e5515..3ba8d66196 100644 --- a/scripts/system/html/miniTablet.html +++ b/scripts/system/html/miniTablet.html @@ -27,5 +27,6 @@ See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.

...

+ diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 08e7103ba4..044500979c 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -51,6 +51,12 @@ PROXY_UI_LOCAL_ROTATION = Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }), proxyUIOverlayEnabled = false, PROXY_UI_OVERLAY_ENABLED_DELAY = 500, + proxyOverlayObject = null, + + MUTE_ON_ICON = Script.resourcesPath() + "icons/tablet-icons/mic-mute-a.svg", + MUTE_OFF_ICON = Script.resourcesPath() + "icons/tablet-icons/mic-unmute-i.svg", + BUBBLE_ON_ICON = Script.resourcesPath() + "icons/tablet-icons/bubble-a.svg", + BUBBLE_OFF_ICON = Script.resourcesPath() + "icons/tablet-icons/bubble-i.svg", // State machine PROXY_DISABLED = 0, @@ -77,6 +83,11 @@ proxyTargetWidth, tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"), + // EventBridge + READY_MESSAGE = "ready", // Engine <== Dialog + MUTE_MESSAGE = "mute", // Engine <=> Dialog + BUBBLE_MESSAGE = "bubble", // Engine <=> Dialog + // Events MIN_HAND_CAMERA_ANGLE = 30, DEGREES_180 = 180, @@ -136,6 +147,47 @@ // #endregion + // #region Communications ================================================================================================== + + function updateMutedStatus() { + var isMuted = Audio.muted; + proxyOverlayObject.emitScriptEvent(JSON.stringify({ + type: MUTE_MESSAGE, + on: isMuted, + icon: isMuted ? MUTE_ON_ICON : MUTE_OFF_ICON + })); + } + + function updateBubbleStatus() { + var isBubbleOn = Users.getIgnoreRadiusEnabled(); + proxyOverlayObject.emitScriptEvent(JSON.stringify({ + type: BUBBLE_MESSAGE, + on: isBubbleOn, + icon: isBubbleOn ? BUBBLE_ON_ICON : BUBBLE_OFF_ICON + })); + } + + function onWebEventReceived(data) { + var message; + + try { + message = JSON.parse(data); + } catch (e) { + console.error("EventBridge message error"); + return; + } + + switch (message.type) { + case READY_MESSAGE: + // Send initial button statuses. + updateMutedStatus(); + updateBubbleStatus(); + break; + } + } + + // #endregion + // #region UI ============================================================================================================== function createUI() { @@ -161,6 +213,9 @@ }); proxyUIOverlayEnabled = false; // This and alpha = 0 hides overlay while its content is being created. + + proxyOverlayObject = Overlays.getOverlayObject(proxyUIOverlay); + proxyOverlayObject.webEventReceived.connect(onWebEventReceived); } function showUI(hand) { @@ -217,9 +272,11 @@ } function destroyUI() { - if (proxyOverlay) { + if (proxyOverlayObject) { + proxyOverlayObject.webEventReceived.disconnect(onWebEventReceived); Overlays.deleteOverlay(proxyUIOverlay); Overlays.deleteOverlay(proxyOverlay); + proxyOverlayObject = null; proxyUIOverlay = null; proxyOverlay = null; } @@ -236,6 +293,10 @@ updateTimer = null; } + // Stop monitoring mute and bubble changes. + Audio.mutedChanged.disconnect(updateMutedStatus); + Users.ignoreRadiusEnabledChanged.disconnect(updateBubbleStatus); + // Don't keep overlays prepared if in desktop mode. destroyUI(); } @@ -244,6 +305,10 @@ // Create UI so that it's ready to be displayed without seeing artefacts from creating the UI. createUI(); + // Start monitoring mute and bubble changes. + Audio.mutedChanged.connect(updateMutedStatus); + Users.ignoreRadiusEnabledChanged.connect(updateBubbleStatus); + // Start updates. updateTimer = Script.setTimeout(updateState, UPDATE_INTERVAL); } From 4268297769387e2e7b37a0e71416ce9368b424cb Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 21 Aug 2018 09:17:53 +1200 Subject: [PATCH 023/259] Mini tablet button actions --- scripts/system/html/js/miniTablet.js | 27 ++++++++++++++- scripts/system/miniTablet.js | 51 +++++++++++++++++++--------- 2 files changed, 61 insertions(+), 17 deletions(-) diff --git a/scripts/system/html/js/miniTablet.js b/scripts/system/html/js/miniTablet.js index 450a87ff8d..1260813448 100644 --- a/scripts/system/html/js/miniTablet.js +++ b/scripts/system/html/js/miniTablet.js @@ -19,11 +19,13 @@ READY_MESSAGE = "ready", // Engine <== Dialog MUTE_MESSAGE = "mute", // Engine <=> Dialog BUBBLE_MESSAGE = "bubble", // Engine <=> Dialog + EXPAND_MESSAGE = "expand", // Engine <== Dialog muteButton, muteImage, bubbleButton, - bubbleImage; + bubbleImage, + expandButton; // #region Communications ================================================================================================== @@ -49,6 +51,24 @@ } } + function onMuteButtonClick() { + EventBridge.emitWebEvent(JSON.stringify({ + type: MUTE_MESSAGE + })); + } + + function onBubbleButtonClick() { + EventBridge.emitWebEvent(JSON.stringify({ + type: BUBBLE_MESSAGE + })); + } + + function onExpandButtonClick() { + EventBridge.emitWebEvent(JSON.stringify({ + type: EXPAND_MESSAGE + })); + } + function connectEventBridge() { EventBridge.scriptEventReceived.connect(onScriptEventReceived); EventBridge.emitWebEvent(JSON.stringify({ @@ -73,9 +93,14 @@ muteImage = document.getElementById("mute-img"); bubbleButton = document.getElementById("bubble"); bubbleImage = document.getElementById("bubble-img"); + expandButton = document.getElementById("expand"); connectEventBridge(); + muteButton.addEventListener("click", onMuteButtonClick, true); + bubbleButton.addEventListener("click", onBubbleButtonClick, true); + expandButton.addEventListener("click", onExpandButtonClick, true); + document.body.onunload = function () { onUnload(); }; diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 044500979c..6f56f4ab93 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -68,13 +68,14 @@ STATE_MACHINE, rezzerState = PROXY_DISABLED, proxyHand, - PROXY_GRAB_HANDLES = [ + PROXY_EXPAND_HANDLES = [ { x: 0.5, y: -0.4, z: 0 }, - { x: -0.5, y: -0.4, z: 0 } + { x: -0.5, y: -0.4, z: 0 }, + { x: 0, y: -0.4, z: 0 } ], - proxyGrabHand, - proxyGrabHandleLocalPosition, - proxyGrabLocalRotation = Quat.IDENTITY, + proxyExpandHand, + proxyExpandLocalPosition, + proxyExpandLocalRotation = Quat.IDENTITY, PROXY_EXPAND_DURATION = 250, PROXY_EXPAND_TIMEOUT = 25, proxyExpandTimer = null, @@ -87,6 +88,7 @@ READY_MESSAGE = "ready", // Engine <== Dialog MUTE_MESSAGE = "mute", // Engine <=> Dialog BUBBLE_MESSAGE = "bubble", // Engine <=> Dialog + EXPAND_MESSAGE = "expand", // Engine <== Dialog // Events MIN_HAND_CAMERA_ANGLE = 30, @@ -97,8 +99,10 @@ HIFI_OBJECT_MANIPULATION_CHANNEL = "Hifi-Object-Manipulation", avatarScale = 1, + LEFT_HAND = 0, RIGHT_HAND = 1, + NO_HAND = 2, HAND_NAMES = ["LeftHand", "RightHand"], DEBUG = false; @@ -183,6 +187,18 @@ updateMutedStatus(); updateBubbleStatus(); break; + case MUTE_MESSAGE: + // Toggle mute. + Audio.muted = !Audio.muted; + break; + case BUBBLE_MESSAGE: + // Toggle bubble. + Users.toggleIgnoreRadius(); + break; + case EXPAND_MESSAGE: + // Expand tablet; + setState(PROXY_EXPANDING, NO_HAND); + break; } } @@ -196,6 +212,7 @@ dimensions: Vec3.multiply(avatarScale, PROXY_DIMENSIONS), solid: true, grabbable: true, + showKeyboardFocusHighlight: false, displayInFront: true, visible: false }); @@ -208,6 +225,7 @@ dpi: PROXY_UI_DPI / avatarScale, alpha: 0, // Hide overlay while its content is being created. grabbable: false, + showKeyboardFocusHighlight: false, displayInFront: true, visible: false }); @@ -247,10 +265,10 @@ function sizeUI(scaleFactor) { Overlays.editOverlay(proxyOverlay, { localPosition: - Vec3.sum(proxyGrabHandleLocalPosition, - Vec3.multiplyQbyV(proxyGrabLocalRotation, + Vec3.sum(proxyExpandLocalPosition, + Vec3.multiplyQbyV(proxyExpandLocalRotation, Vec3.multiply(-scaleFactor, - Vec3.multiplyVbyV(PROXY_GRAB_HANDLES[proxyGrabHand], PROXY_DIMENSIONS))) + Vec3.multiplyVbyV(PROXY_EXPAND_HANDLES[proxyExpandHand], PROXY_DIMENSIONS))) ), dimensions: Vec3.multiply(scaleFactor, PROXY_DIMENSIONS) }); @@ -383,13 +401,14 @@ setState(TABLET_OPEN); } - function enterProxyExpanding() { + function enterProxyExpanding(hand) { // Grab details. var properties = Overlays.getProperties(proxyOverlay, ["localPosition", "localRotation"]); - proxyGrabLocalRotation = properties.localRotation; - proxyGrabHandleLocalPosition = Vec3.sum(properties.localPosition, - Vec3.multiplyQbyV(proxyGrabLocalRotation, - Vec3.multiplyVbyV(PROXY_GRAB_HANDLES[proxyGrabHand], PROXY_DIMENSIONS))); + proxyExpandHand = hand; + proxyExpandLocalRotation = properties.localRotation; + proxyExpandLocalPosition = Vec3.sum(properties.localPosition, + Vec3.multiplyQbyV(proxyExpandLocalRotation, + Vec3.multiplyVbyV(PROXY_EXPAND_HANDLES[proxyExpandHand], PROXY_DIMENSIONS))); // Start expanding. proxyInitialWidth = PROXY_DIMENSIONS.x; @@ -488,7 +507,7 @@ } function onMessageReceived(channel, data, senderID, localOnly) { - var message; + var message, hand; if (channel !== HIFI_OBJECT_MANIPULATION_CHANNEL) { return; @@ -500,8 +519,8 @@ } if (message.action === "grab" && rezzerState === PROXY_VISIBLE) { - proxyGrabHand = message.joint === HAND_NAMES[proxyHand] ? proxyHand : otherHand(proxyHand); - setState(PROXY_EXPANDING); + hand = message.joint === HAND_NAMES[proxyHand] ? proxyHand : otherHand(proxyHand); + setState(PROXY_EXPANDING, hand); } } From f4a8dc49e963df2354526b0f109ba94e16fbd2f4 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 21 Aug 2018 10:28:23 +1200 Subject: [PATCH 024/259] Add button sounds --- scripts/system/assets/sounds/button-click.wav | Bin 0 -> 26898 bytes scripts/system/assets/sounds/button-hover.wav | Bin 0 -> 22432 bytes scripts/system/html/js/miniTablet.js | 10 +++++++ scripts/system/miniTablet.js | 25 ++++++++++++++++++ 4 files changed, 35 insertions(+) create mode 100644 scripts/system/assets/sounds/button-click.wav create mode 100644 scripts/system/assets/sounds/button-hover.wav diff --git a/scripts/system/assets/sounds/button-click.wav b/scripts/system/assets/sounds/button-click.wav new file mode 100644 index 0000000000000000000000000000000000000000..30a097ce456369195b2791ebe83fe4d20d0a0c57 GIT binary patch literal 26898 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 literal 0 HcmV?d00001 diff --git a/scripts/system/assets/sounds/button-hover.wav b/scripts/system/assets/sounds/button-hover.wav new file mode 100644 index 0000000000000000000000000000000000000000..cd76d0174cf7bfd5589dc26ba7f58793778a5456 GIT binary patch literal 22432 zcmeHPU5wkr79MZd{;RY_;-ad8G!)bd9NUQ#C%IYorkkW2X|uaZ0$W`KE61L6P4m~p zyPKAm`-p_Z11jRe11~%SLPAJ@P~Sj8RDhs@M-UYVK`)e_{w>K|9Xpda+4N^GLbrnX zX0^8G%=yllbI#ZkXSKOZI{oi?gys_ur%Dyey9*)2p)vHYw-G}B8AG?B9S9*qv$TK6 zh=GBDfq{X6fq{X6fq{X6;g$j+gmhdDKpo_|Z48asXB5Lav>!lq=y4csehS;@5U30Q z_GtrNH$bgT+V%_pZW;9fj>B^RQ28)`XE-L5`!-keAdjsIV}B#Rk=1U4YxnqZG>$f) z4QM0U*wX<}J!%W)0sWv0eQy8U=imdi4css{(19c13US(x zOClo~y^G#OF)qfXxit4adLPZ888nM#(LLxMw3TcnZ?)fQC)>$(r`>7qC;Q29a-4XH zmxNIm?eXmKyg2^i_#5MIjJG^3&jZ{8+y!(2?cjEBiB6((?#j6<$1Wec{K?f%u8wua zIv;dC=)BN=q5VYXiH_y5JWp?Wdea@7@7Vmv_#@-banEsgad&b1$M%n%A!o?Os~fML zyLj$m;!5JmW9`SO`c7sH=o{oV8ek8XSg%mo9Imx7z>Q; z<@Rz(Z`EFJFH7@`3h&wnQXyXXnn&eeL_&pOMeV-Q#zUzq0X_jf>-puV1_Zs)S=XuXg&rZ)G zx5zz%ox}D1+Dx%NP=jdbfF?tj| zitb1Equpq?owJ|G&*VMw9(k5LOWq;x*fj%rhnzvKz93(aO=uJHBR|@M_Sm`JiFTrW zXdmJckM^Ryc3oT`7kat=mV8UTA>WX%$X5jVp;k7d&1fsyingQe_VIRfyL}Gz0yPJ< z1@#HG;2iuqPtKE{$WP=~@+-MSF4+|YH4gQ*4Q)eP&=&i+tJ{DZ^kEV(j@C9*BIsWv z7wsdQLp{O~#?ZA3zJL$FO*vt%(7<=7_p9WpJ*OYK4dV1Y`M&2T*n;{8AHhHHiTVoH z;2+qa>xBaR1#Vcsa84V&rfU)a@qw7qc)^;6_yRv&-!KN|MduDNggMg~Tqc+8dBZsH z74)Eif6$;0<_mH+AYR}TjTv0i<_74%9H9XZyh?2sK5nc)vBrzt*yr{%P z5uqY(S^sqY?Bg$ugi$MEuk$;L4O8=`s(Q`94e#WnHy}zk3Qj-1c6eh!k!lQn2mjYY zGLxU%X8*MmLWhd^_`LxnUS z@f}pR#1GAy)nqy@4EFQ~*v5jQD2jq4ijr%fEwKmY3yJJp z*Sq{eJ~K0!TF7RSGnvCvg@x%^Rdlp&H0;J)Q<_pFDL<3XR)~eJi*wVbMO1avMwI+NT>>Og% zW(>LopVuB@9h>ku_thiWIqzhxfxRJrnAas)^eR!m6c*)>^dM|36hRCLVwe}Dm>7u# zBpP#{y2b1D#v4W{mP@9)<{Cz6!nbHy^_U>6tgQG~g8o|Ls31k7Q9%p{fdJnyN__J~ z)zVsgwRx{YLV1$7sW;5JW!9=*;MIz?C2PXx14BJ@=T)g^vprMQrr(~WU$0e!mR1)e zzbI5H!YIwAm0Gs0uh}e|sAD0Ao3*8ej#JCHYTY}UDu?^?9qGg%RSuU%616Jz>w7fH zdaF`TUMmC@Cj`k8Rjp#`_I`0BhoSfE)|VP(J6VP<;4-e@s?`)Eza;c>1ZlG49?Mjl zmR8kqCaJb6b-!sEv62|pgIdVoMLjI?(Ws31NLa+Yq!@ZAV#om{q{W3b>iyJqsgpH* z32bMQsuD63T$C{%j)X(J7zrzUKvys?1{4jpIHD*ADWywo4draZJZe_8vTF=@ZJ0(Z zge66k!~iep5rY?tvcX4`V2Q^vjs^mX7z!z2YGMS<8dFnd)2cO2xCFGe^Z58+FBOzc zyVmPkqluyJCVaH;+=1^QZr5*2(_xjUO>G$)abcjZ?^F+6C9|KlQaG%XqH=^+aG=B& zHEi%$Dn)r&)36g;MWnngwQF;)EaIwbi{*TXiv>mJa~!3zM_rqwSu0s9S_3DJ;;MDS zeV^i&y!+?Ps!>~M9-tNmDXwiwU(~8cv7ri7k#-EL#09t&mCdyRr~m(vJw-Q)VljmI zkRH@VS81ksvZ-yPLise_M6`siKzSf#rK+%T81k#5u~ zH+dh9H(}G}&cw~$Oo!A4o)cYGqrZ8PBU%8n(yP%QCMF%;%YB{{$srDBv1l#0P(QPV|3Q>c{@G<|C{(yoBIrL9Um zmhGlDsag1XPx>`uT~=&a%`i(RZi=Npa)@KHfi(+{ z-50o?%*K|r@)A~qq7twp*6ZohcX!FjY^+LESfwqlMSghCQe%g)>!)T69s{!qM83i(0jc%l3%8 zg&Vjwpzjv1A!u4!!z!2+3~#h=b|H7eVODUu)~INf8i+=fWTRH6MAW`(NZ)clO(1^Q zPOB*Xo&LCW9bsRU$_Bi4%N@^p;=El4_FLICs*cRXJ*;bIG;f~7s`Enk+VV9dTKRXx zbX^e{4Ceom4jLGj&+Z8sddxDdvb&FdeoB-r*GLC+Yu>7qV`U9q)wg&9m$ap_ Dialog BUBBLE_MESSAGE = "bubble", // Engine <=> Dialog EXPAND_MESSAGE = "expand", // Engine <== Dialog @@ -57,6 +58,12 @@ })); } + function onButtonHover() { + EventBridge.emitWebEvent(JSON.stringify({ + type: HOVER_MESSAGE + })); + } + function onBubbleButtonClick() { EventBridge.emitWebEvent(JSON.stringify({ type: BUBBLE_MESSAGE @@ -97,6 +104,9 @@ connectEventBridge(); + muteButton.addEventListener("mouseenter", onButtonHover, false); + bubbleButton.addEventListener("mouseenter", onButtonHover, false); + expandButton.addEventListener("mouseenter", onButtonHover, false); muteButton.addEventListener("click", onMuteButtonClick, true); bubbleButton.addEventListener("click", onBubbleButtonClick, true); expandButton.addEventListener("click", onExpandButtonClick, true); diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 6f56f4ab93..a16ae50b29 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -86,6 +86,7 @@ // EventBridge READY_MESSAGE = "ready", // Engine <== Dialog + HOVER_MESSAGE = "hover", // Engine <== Dialog MUTE_MESSAGE = "mute", // Engine <=> Dialog BUBBLE_MESSAGE = "bubble", // Engine <=> Dialog EXPAND_MESSAGE = "expand", // Engine <== Dialog @@ -99,11 +100,20 @@ HIFI_OBJECT_MANIPULATION_CHANNEL = "Hifi-Object-Manipulation", avatarScale = 1, + // Sounds + HOVER_SOUND = "./assets/sounds/button-hover.wav", + HOVER_VOLUME = 0.5, + CLICK_SOUND = "./assets/sounds/button-click.wav", + CLICK_VOLUME = 0.8, + hoverSound = SoundCache.getSound(Script.resolvePath(HOVER_SOUND)), + clickSound = SoundCache.getSound(Script.resolvePath(CLICK_SOUND)), + // Hands LEFT_HAND = 0, RIGHT_HAND = 1, NO_HAND = 2, HAND_NAMES = ["LeftHand", "RightHand"], + DEBUG = false; // #region Utilities ======================================================================================================= @@ -149,6 +159,14 @@ return hand === LEFT_HAND ? RIGHT_HAND : LEFT_HAND; } + function playSound(sound, volume) { + Audio.playSound(sound, { + position: proxyHand === LEFT_HAND ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(), + volume: volume, + localOnly: true + }); + } + // #endregion // #region Communications ================================================================================================== @@ -187,16 +205,23 @@ updateMutedStatus(); updateBubbleStatus(); break; + case HOVER_MESSAGE: + // Audio feedback. + playSound(hoverSound, HOVER_VOLUME); + break; case MUTE_MESSAGE: // Toggle mute. + playSound(clickSound, CLICK_VOLUME); Audio.muted = !Audio.muted; break; case BUBBLE_MESSAGE: // Toggle bubble. + playSound(clickSound, CLICK_VOLUME); Users.toggleIgnoreRadius(); break; case EXPAND_MESSAGE: // Expand tablet; + playSound(clickSound, CLICK_VOLUME); setState(PROXY_EXPANDING, NO_HAND); break; } From dc86230e40bda530ec7ceda38c9a432f5b661448 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 21 Aug 2018 10:33:13 +1200 Subject: [PATCH 025/259] Make "..." expand tablet per display hand rather than centered --- scripts/system/miniTablet.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index a16ae50b29..f7a262b9c4 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -70,8 +70,7 @@ proxyHand, PROXY_EXPAND_HANDLES = [ { x: 0.5, y: -0.4, z: 0 }, - { x: -0.5, y: -0.4, z: 0 }, - { x: 0, y: -0.4, z: 0 } + { x: -0.5, y: -0.4, z: 0 } ], proxyExpandHand, proxyExpandLocalPosition, @@ -111,7 +110,6 @@ // Hands LEFT_HAND = 0, RIGHT_HAND = 1, - NO_HAND = 2, HAND_NAMES = ["LeftHand", "RightHand"], DEBUG = false; @@ -222,7 +220,7 @@ case EXPAND_MESSAGE: // Expand tablet; playSound(clickSound, CLICK_VOLUME); - setState(PROXY_EXPANDING, NO_HAND); + setState(PROXY_EXPANDING, proxyHand); break; } } From 69e5eb9b1c8f3a90cc9058d2a3d8d277ecbe8539 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 21 Aug 2018 10:46:48 +1200 Subject: [PATCH 026/259] Move tablet towards fingertips as it expands --- scripts/system/miniTablet.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index f7a262b9c4..6d6180a964 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -68,9 +68,9 @@ STATE_MACHINE, rezzerState = PROXY_DISABLED, proxyHand, - PROXY_EXPAND_HANDLES = [ - { x: 0.5, y: -0.4, z: 0 }, - { x: -0.5, y: -0.4, z: 0 } + PROXY_EXPAND_HANDLES = [ // Normalized coordinates in range [-0.5, 0.5] about center of mini tablet. + { x: 0.5, y: -0.75, z: 0 }, + { x: -0.5, y: -0.75, z: 0 } ], proxyExpandHand, proxyExpandLocalPosition, From 8a8bf6dc046f179371c94c1f6f8e8e90ab0b3d67 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 21 Aug 2018 10:49:19 +1200 Subject: [PATCH 027/259] Reduce default size of tablet in HMD mode --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 45ef336333..14a9af09d2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -971,7 +971,7 @@ Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context); Setting::Handle sessionRunTime{ "sessionRunTime", 0 }; -const float DEFAULT_HMD_TABLET_SCALE_PERCENT = 70.0f; +const float DEFAULT_HMD_TABLET_SCALE_PERCENT = 60.0f; const float DEFAULT_DESKTOP_TABLET_SCALE_PERCENT = 75.0f; const bool DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR = true; const bool DEFAULT_HMD_TABLET_BECOMES_TOOLBAR = false; From 5e7f68d4b7c7d080a4ee7316b0e09f111b84c8d3 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 21 Aug 2018 14:00:20 +1200 Subject: [PATCH 028/259] Use a smaller near grab radius for tablet and mini-tablet --- .../controllers/controllerDispatcher.js | 23 +++++++++++++++++++ scripts/system/miniTablet.js | 8 +++++++ 2 files changed, 31 insertions(+) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 7a916392b9..f71af03e26 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -25,7 +25,9 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); (function() { Script.include("/~/system/libraries/pointersUtils.js"); + var NEAR_MAX_RADIUS = 0.1; + var NEAR_TABLET_MAX_RADIUS = 0.05; var TARGET_UPDATE_HZ = 60; // 50hz good enough, but we're using update var BASIC_TIMER_INTERVAL_MS = 1000 / TARGET_UPDATE_HZ; @@ -49,6 +51,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.tabletID = null; this.blacklist = []; this.pointerManager = new PointerManager(); + this.miniTabletID = null; // a module can occupy one or more "activity" slots while it's running. If all the required slots for a module are // not set to false (not in use), a module cannot start. When a module is using a slot, that module's name @@ -211,6 +214,22 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); if (controllerLocations[h].valid) { var nearbyOverlays = Overlays.findOverlays(controllerLocations[h].position, NEAR_MAX_RADIUS * sensorScaleFactor); + + // Tablet and mini-tablet must be within NEAR_TABLET_MAX_RADIUS in order to be grabbed. + var tabletIndex = nearbyOverlays.indexOf(HMD.tabletID); + var miniTabletIndex = nearbyOverlays.indexOf(_this.miniTabletID); + if (tabletIndex !== -1 || miniTabletIndex !== -1) { + var closebyOverlays = + Overlays.findOverlays(controllerLocations[h].position, NEAR_TABLET_MAX_RADIUS * sensorScaleFactor); + // Assumes that the tablet and mini-tablet are not displayed at the same time. + if (tabletIndex !== -1 && closebyOverlays.indexOf(HMD.tabletID) === -1) { + nearbyOverlays.splice(tabletIndex, 1); + } + if (miniTabletIndex !== -1 && closebyOverlays.indexOf(_this.miniTabletID) === -1) { + nearbyOverlays.splice(miniTabletIndex, 1); + } + } + nearbyOverlays.sort(function (a, b) { var aPosition = Overlays.getProperty(a, "position"); var aDistance = Vec3.distance(aPosition, controllerLocations[h].position); @@ -218,6 +237,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); var bDistance = Vec3.distance(bPosition, controllerLocations[h].position); return aDistance - bDistance; }); + nearbyOverlayIDs.push(nearbyOverlays); } else { nearbyOverlayIDs.push([]); @@ -470,6 +490,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); _this.setBlacklist(); } } + } else if (channel === 'Hifi-MiniTablet-ID') { + _this.miniTabletID = message; } } catch (e) { @@ -508,6 +530,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Entities.mousePressOnEntity.connect(mousePress); var controllerDispatcher = new ControllerDispatcher(); Messages.subscribe('Hifi-Hand-RayPick-Blacklist'); + Messages.subscribe('Hifi-MiniTablet-ID'); Messages.messageReceived.connect(controllerDispatcher.handleHandMessage); Script.scriptEnding.connect(controllerDispatcher.cleanup); Script.setTimeout(controllerDispatcher.update, BASIC_TIMER_INTERVAL_MS); diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 6d6180a964..fdfa92d98a 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -169,6 +169,11 @@ // #region Communications ================================================================================================== + function updateMiniTabletID() { + // Send mini-tablet overlay ID to controllerDispatcher so that it can use a smaller near grab distance. + Messages.sendLocalMessage("Hifi-MiniTablet-ID", proxyOverlay); + } + function updateMutedStatus() { var isMuted = Audio.muted; proxyOverlayObject.emitScriptEvent(JSON.stringify({ @@ -257,6 +262,8 @@ proxyOverlayObject = Overlays.getOverlayObject(proxyUIOverlay); proxyOverlayObject.webEventReceived.connect(onWebEventReceived); + + updateMiniTabletID(); } function showUI(hand) { @@ -320,6 +327,7 @@ proxyOverlayObject = null; proxyUIOverlay = null; proxyOverlay = null; + updateMiniTabletID(); } } From f6a607efcc3b4f563cc5d75cbb6336004179ad4c Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 21 Aug 2018 15:43:37 +1200 Subject: [PATCH 029/259] Expand / shrink mini-tablet when showing / hiding --- scripts/system/miniTablet.js | 130 +++++++++++++++++++++++++++++------ 1 file changed, 110 insertions(+), 20 deletions(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index fdfa92d98a..dcbaa34ad5 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -61,13 +61,20 @@ // State machine PROXY_DISABLED = 0, PROXY_HIDDEN = 1, - PROXY_VISIBLE = 2, - PROXY_EXPANDING = 3, - TABLET_OPEN = 4, - STATE_STRINGS = ["PROXY_DISABLED", "PROXY_HIDDEN", "PROXY_VISIBLE", "PROXY_EXPANDING", "TABLET_OPEN"], + PROXY_HIDING = 2, + PROXY_SHOWING = 3, + PROXY_VISIBLE = 4, + PROXY_EXPANDING = 5, + TABLET_OPEN = 6, + STATE_STRINGS = ["PROXY_DISABLED", "PROXY_HIDDEN", "PROXY_HIDING", "PROXY_SHOWING", "PROXY_VISIBLE", "PROXY_EXPANDING", + "TABLET_OPEN"], STATE_MACHINE, rezzerState = PROXY_DISABLED, proxyHand, + PROXY_SCALE_DURATION = 150, + PROXY_SCALE_TIMEOUT = 20, + proxyScaleTimer = null, + proxyScaleStart, PROXY_EXPAND_HANDLES = [ // Normalized coordinates in range [-0.5, 0.5] about center of mini tablet. { x: 0.5, y: -0.75, z: 0 }, { x: -0.5, y: -0.75, z: 0 } @@ -76,12 +83,11 @@ proxyExpandLocalPosition, proxyExpandLocalRotation = Quat.IDENTITY, PROXY_EXPAND_DURATION = 250, - PROXY_EXPAND_TIMEOUT = 25, + PROXY_EXPAND_TIMEOUT = 20, proxyExpandTimer = null, proxyExpandStart, proxyInitialWidth, proxyTargetWidth, - tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"), // EventBridge READY_MESSAGE = "ready", // Engine <== Dialog @@ -112,6 +118,8 @@ RIGHT_HAND = 1, HAND_NAMES = ["LeftHand", "RightHand"], + // Miscellaneous. + tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"), DEBUG = false; // #region Utilities ======================================================================================================= @@ -266,20 +274,22 @@ updateMiniTabletID(); } - function showUI(hand) { + function showUI() { + var initialScale = 0.01; // Start very small. + Overlays.editOverlay(proxyOverlay, { parentID: MyAvatar.SELF_ID, parentJointIndex: handJointIndex(proxyHand), localPosition: Vec3.multiply(avatarScale, proxyHand === LEFT_HAND ? PROXY_POSITION_LEFT_HAND : PROXY_POSITION_RIGHT_HAND), localRotation: proxyHand === LEFT_HAND ? PROXY_ROTATION_LEFT_HAND : PROXY_ROTATION_RIGHT_HAND, - dimensions: Vec3.multiply(avatarScale, PROXY_DIMENSIONS), + dimensions: Vec3.multiply(initialScale, PROXY_DIMENSIONS), visible: true }); Overlays.editOverlay(proxyUIOverlay, { localPosition: Vec3.multiply(avatarScale, PROXY_UI_LOCAL_POSITION), - dimensions: Vec3.multiply(avatarScale, PROXY_UI_DIMENSIONS), - dpi: PROXY_UI_DPI / avatarScale, + dimensions: Vec3.multiply(initialScale, PROXY_UI_DIMENSIONS), + dpi: PROXY_UI_DPI / initialScale, visible: true }); @@ -293,6 +303,19 @@ } function sizeUI(scaleFactor) { + // Scale UI in place. + Overlays.editOverlay(proxyOverlay, { + dimensions: Vec3.multiply(scaleFactor, PROXY_DIMENSIONS) + }); + Overlays.editOverlay(proxyUIOverlay, { + localPosition: Vec3.multiply(scaleFactor, PROXY_UI_LOCAL_POSITION), + dimensions: Vec3.multiply(scaleFactor, PROXY_UI_DIMENSIONS), + dpi: PROXY_UI_DPI / scaleFactor + }); + } + + function sizeUIAboutHandles(scaleFactor) { + // Scale UI and move per handles. Overlays.editOverlay(proxyOverlay, { localPosition: Vec3.sum(proxyExpandLocalPosition, @@ -396,15 +419,71 @@ } // Compare palm directions of hands with vectors from palms to camera. if (shouldShowProxy(LEFT_HAND)) { - setState(PROXY_VISIBLE, LEFT_HAND); + setState(PROXY_SHOWING, LEFT_HAND); } else if (shouldShowProxy(RIGHT_HAND)) { - setState(PROXY_VISIBLE, RIGHT_HAND); + setState(PROXY_SHOWING, RIGHT_HAND); } } - function enterProxyVisible(hand) { + function scaleProxyDown() { + var scaleFactor = (Date.now() - proxyScaleStart) / PROXY_SCALE_DURATION; + if (scaleFactor < 1) { + sizeUI((1 - scaleFactor) * avatarScale); + proxyScaleTimer = Script.setTimeout(scaleProxyDown, PROXY_SCALE_TIMEOUT); + return; + } + proxyScaleTimer = null; + setState(PROXY_HIDDEN); + } + + function enterProxyHiding() { + proxyScaleStart = Date.now(); + proxyScaleTimer = Script.setTimeout(scaleProxyDown, PROXY_SCALE_TIMEOUT); + } + + function updateProxyHiding() { + if (HMD.showTablet) { + setState(PROXY_HIDDEN); + } + } + + function exitProxyHiding() { + if (proxyScaleTimer) { + Script.clearTimeout(proxyScaleTimer); + proxyScaleTimer = null; + } + } + + function scaleProxyUp() { + var scaleFactor = (Date.now() - proxyScaleStart) / PROXY_SCALE_DURATION; + if (scaleFactor < 1) { + sizeUI(scaleFactor * avatarScale); + proxyScaleTimer = Script.setTimeout(scaleProxyUp, PROXY_SCALE_TIMEOUT); + return; + } + proxyScaleTimer = null; + sizeUI(avatarScale); + setState(PROXY_VISIBLE); + } + + function enterProxyShowing(hand) { proxyHand = hand; - showUI(proxyHand); + showUI(); + proxyScaleStart = Date.now(); + proxyScaleTimer = Script.setTimeout(scaleProxyUp, PROXY_SCALE_TIMEOUT); + } + + function updateProxyShowing() { + if (HMD.showTablet) { + setState(PROXY_HIDDEN); + } + } + + function exitProxyShowing() { + if (proxyScaleTimer) { + Script.clearTimeout(proxyScaleTimer); + proxyScaleTimer = null; + } } function updateProxyVisible() { @@ -415,7 +494,7 @@ } // Check that palm direction of proxy hand still less than maximum angle. if (!shouldShowProxy(proxyHand)) { - setState(PROXY_HIDDEN); + setState(PROXY_HIDING); } } @@ -423,11 +502,10 @@ var scaleFactor = (Date.now() - proxyExpandStart) / PROXY_EXPAND_DURATION; var tabletScaleFactor = avatarScale * (1 + scaleFactor * (proxyTargetWidth - proxyInitialWidth) / proxyInitialWidth); if (scaleFactor < 1) { - sizeUI(tabletScaleFactor); + sizeUIAboutHandles(tabletScaleFactor); proxyExpandTimer = Script.setTimeout(expandProxy, PROXY_EXPAND_TIMEOUT); return; } - proxyExpandTimer = null; setState(TABLET_OPEN); } @@ -449,7 +527,7 @@ } function updateProxyExanding() { - // Hide proxy if tablet has been displayed by other means. + // Hide proxy immediately if tablet has been displayed by other means. if (HMD.showTablet) { setState(PROXY_HIDDEN); } @@ -490,8 +568,18 @@ update: updateProxyHidden, exit: null }, + PROXY_HIDING: { // Tablet proxy is reducing from PROXY_VISIBLE to PROXY_HIDDEN. + enter: enterProxyHiding, + update: updateProxyHiding, + exit: exitProxyHiding + }, + PROXY_SHOWING: { // Tablet proxy is expanding from PROXY_HIDDN to PROXY_VISIBLE. + enter: enterProxyShowing, + update: updateProxyShowing, + exit: exitProxyShowing + }, PROXY_VISIBLE: { // Tablet proxy is visible and attached to hand. - enter: enterProxyVisible, + enter: null, update: updateProxyVisible, exit: null }, @@ -523,7 +611,9 @@ } function updateState() { - STATE_MACHINE[STATE_STRINGS[rezzerState]].update(); + if (STATE_MACHINE[STATE_STRINGS[rezzerState]].update) { + STATE_MACHINE[STATE_STRINGS[rezzerState]].update(); + } updateTimer = Script.setTimeout(updateState, UPDATE_INTERVAL); } From e25b2f6e0f3e02fc569a126262edfad3737444a2 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 21 Aug 2018 16:47:40 +1200 Subject: [PATCH 030/259] Support stylus input on the mini tablet --- .../controllerModules/stylusInput.js | 27 +++++++++++++++++++ scripts/system/miniTablet.js | 6 ++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerModules/stylusInput.js b/scripts/system/controllers/controllerModules/stylusInput.js index a512fd89db..77615e361f 100644 --- a/scripts/system/controllers/controllerModules/stylusInput.js +++ b/scripts/system/controllers/controllerModules/stylusInput.js @@ -58,6 +58,12 @@ Script.include("/~/system/libraries/controllers.js"); enabled: true }); + this.miniTabletID = null; + + this.setMiniTabletID = function (id) { + this.miniTabletID = id; + }; + this.disable = false; this.otherModuleNeedsToRun = function(controllerData) { @@ -123,6 +129,14 @@ Script.include("/~/system/libraries/controllers.js"); } } + // Add the mini tablet. + if (this.miniTabletID && Overlays.getProperty(this.miniTabletID, "visible")) { + stylusTarget = getOverlayDistance(controllerPosition, this.miniTabletID); + if (stylusTarget) { + stylusTargets.push(stylusTarget); + } + } + var WEB_DISPLAY_STYLUS_DISTANCE = 0.5; var nearStylusTarget = isNearStylusTarget(stylusTargets, WEB_DISPLAY_STYLUS_DISTANCE * sensorScaleFactor); @@ -191,6 +205,15 @@ Script.include("/~/system/libraries/controllers.js"); } } + function onMessageReceived(channel, message, sender) { + if (sender === MyAvatar.sessionUUID) { + if (channel === 'Hifi-MiniTablet-UI-ID') { + leftTabletStylusInput.setMiniTabletID(message); + rightTabletStylusInput.setMiniTabletID(message); + } + } + } + var leftTabletStylusInput = new StylusInput(LEFT_HAND); var rightTabletStylusInput = new StylusInput(RIGHT_HAND); @@ -201,7 +224,11 @@ Script.include("/~/system/libraries/controllers.js"); Overlays.hoverLeaveOverlay.connect(mouseHoverLeave); Overlays.mousePressOnOverlay.connect(mousePress); + Messages.subscribe('Hifi-MiniTablet-UI-ID'); + Messages.messageReceived.connect(onMessageReceived); + this.cleanup = function () { + Messages.messageReceived.disconnect(onMessageReceived); leftTabletStylusInput.cleanup(); rightTabletStylusInput.cleanup(); disableDispatcherModule("LeftTabletStylusInput"); diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index dcbaa34ad5..30594392e2 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -180,6 +180,8 @@ function updateMiniTabletID() { // Send mini-tablet overlay ID to controllerDispatcher so that it can use a smaller near grab distance. Messages.sendLocalMessage("Hifi-MiniTablet-ID", proxyOverlay); + // Send mini-tablet UI overlay ID to stylusInput so that it styluses can be used on it. + Messages.sendLocalMessage("Hifi-MiniTablet-UI-ID", proxyUIOverlay); } function updateMutedStatus() { @@ -271,7 +273,7 @@ proxyOverlayObject = Overlays.getOverlayObject(proxyUIOverlay); proxyOverlayObject.webEventReceived.connect(onWebEventReceived); - updateMiniTabletID(); + // updateMiniTabletID(); Other scripts relying on this may not be ready yet so do this in showUI(). } function showUI() { @@ -293,6 +295,8 @@ visible: true }); + updateMiniTabletID(); + if (!proxyUIOverlayEnabled) { // Overlay content is created the first time it is visible to the user. The initial creation displays artefacts. // Delay showing UI overlay until after giving it time for its content to be created. From 536db64123c76d5854787f85cc50cbdcfae6d644 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 21 Aug 2018 16:48:35 +1200 Subject: [PATCH 031/259] Mitigate against UI overlay possibly getting rotated --- scripts/system/miniTablet.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 30594392e2..5c950cd594 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -290,6 +290,7 @@ }); Overlays.editOverlay(proxyUIOverlay, { localPosition: Vec3.multiply(avatarScale, PROXY_UI_LOCAL_POSITION), + localRotation: PROXY_UI_LOCAL_ROTATION, dimensions: Vec3.multiply(initialScale, PROXY_UI_DIMENSIONS), dpi: PROXY_UI_DPI / initialScale, visible: true From 6dcce0e953038e6f248ee9d9ee7b40f1b16c5343 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 23 Aug 2018 10:59:23 +1200 Subject: [PATCH 032/259] Revert display test to depending just on hand to camera angle for now --- scripts/system/miniTablet.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 5c950cd594..17b1587604 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -409,8 +409,7 @@ handOrientation = Quat.multiply(MyAvatar.orientation, MyAvatar.getAbsoluteJointRotationInObjectFrame(jointIndex)); cameraToHandDirection = Vec3.normalize(Vec3.subtract(handPosition, Camera.position)); - return Vec3.dot(cameraToHandDirection, Quat.getForward(handOrientation)) > MIN_HAND_CAMERA_ANGLE_COS - && Vec3.dot(cameraToHandDirection, Quat.getForward(Camera.orientation)) > MIN_HAND_CAMERA_ANGLE_COS; + return Vec3.dot(cameraToHandDirection, Quat.getForward(handOrientation)) > MIN_HAND_CAMERA_ANGLE_COS; } function enterProxyHidden() { From 335eb98bb77364d6c86736185adeb900f2d106a6 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 23 Aug 2018 11:14:21 +1200 Subject: [PATCH 033/259] Increase size of the "..." button --- scripts/system/html/css/miniTablet.css | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/scripts/system/html/css/miniTablet.css b/scripts/system/html/css/miniTablet.css index 5ff9944a2c..75da2eaf82 100644 --- a/scripts/system/html/css/miniTablet.css +++ b/scripts/system/html/css/miniTablet.css @@ -80,23 +80,24 @@ footer { text-align: center; color: #e3e3e3; font-weight: bold; + font-size: 26px; } footer div { display: inline-block; - height: 12px; - width: 24px; + height: 14px; + width: 30px; position: relative; - top: 16px; + top: 14px; } footer div p { position: relative; - top: -8px; + top: -15px; } footer .button:hover { border: 1px solid #e3e3e3; - border-radius: 2px; - margin: -1px; + border-radius: 3px; + margin: -8px; } From b6e4a22db7938359792cd6d8853d1bd5b794213e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 23 Aug 2018 13:06:28 +1200 Subject: [PATCH 034/259] Rotate the mini tablet as it expands --- scripts/system/miniTablet.js | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 17b1587604..ca665b888a 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -79,6 +79,7 @@ { x: 0.5, y: -0.75, z: 0 }, { x: -0.5, y: -0.75, z: 0 } ], + PROXY_EXPAND_DELTA_ROTATION = Quat.fromVec3Degrees({ x: -45, y: 0, z: 0 }), proxyExpandHand, proxyExpandLocalPosition, proxyExpandLocalRotation = Quat.IDENTITY, @@ -88,6 +89,7 @@ proxyExpandStart, proxyInitialWidth, proxyTargetWidth, + proxyTargetLocalRotation, // EventBridge READY_MESSAGE = "ready", // Engine <== Dialog @@ -321,19 +323,28 @@ function sizeUIAboutHandles(scaleFactor) { // Scale UI and move per handles. + var tabletScaleFactor = avatarScale * (1 + scaleFactor * (proxyTargetWidth - proxyInitialWidth) / proxyInitialWidth); + var dimensions = Vec3.multiply(tabletScaleFactor, PROXY_DIMENSIONS); + var localRotation = Quat.mix(proxyExpandLocalRotation, proxyTargetLocalRotation, scaleFactor); + var localPosition = + Vec3.sum(proxyExpandLocalPosition, + Vec3.multiplyQbyV(proxyExpandLocalRotation, + Vec3.multiply(-tabletScaleFactor, + Vec3.multiplyVbyV(PROXY_EXPAND_HANDLES[proxyExpandHand], PROXY_DIMENSIONS))) + ); + localPosition = Vec3.sum(localPosition, + Vec3.multiplyQbyV(proxyExpandLocalRotation, { x: 0, y: 0.5 * -dimensions.y, z: 0 })); + localPosition = Vec3.sum(localPosition, + Vec3.multiplyQbyV(localRotation, { x: 0, y: 0.5 * dimensions.y, z: 0 })); Overlays.editOverlay(proxyOverlay, { - localPosition: - Vec3.sum(proxyExpandLocalPosition, - Vec3.multiplyQbyV(proxyExpandLocalRotation, - Vec3.multiply(-scaleFactor, - Vec3.multiplyVbyV(PROXY_EXPAND_HANDLES[proxyExpandHand], PROXY_DIMENSIONS))) - ), - dimensions: Vec3.multiply(scaleFactor, PROXY_DIMENSIONS) + localPosition: localPosition, + localRotation: localRotation, + dimensions: dimensions }); Overlays.editOverlay(proxyUIOverlay, { - localPosition: Vec3.multiply(scaleFactor, PROXY_UI_LOCAL_POSITION), - dimensions: Vec3.multiply(scaleFactor, PROXY_UI_DIMENSIONS), - dpi: PROXY_UI_DPI / scaleFactor + localPosition: Vec3.multiply(tabletScaleFactor, PROXY_UI_LOCAL_POSITION), + dimensions: Vec3.multiply(tabletScaleFactor, PROXY_UI_DIMENSIONS), + dpi: PROXY_UI_DPI / tabletScaleFactor }); } @@ -504,9 +515,8 @@ function expandProxy() { var scaleFactor = (Date.now() - proxyExpandStart) / PROXY_EXPAND_DURATION; - var tabletScaleFactor = avatarScale * (1 + scaleFactor * (proxyTargetWidth - proxyInitialWidth) / proxyInitialWidth); if (scaleFactor < 1) { - sizeUIAboutHandles(tabletScaleFactor); + sizeUIAboutHandles(scaleFactor); proxyExpandTimer = Script.setTimeout(expandProxy, PROXY_EXPAND_TIMEOUT); return; } @@ -526,6 +536,7 @@ // Start expanding. proxyInitialWidth = PROXY_DIMENSIONS.x; proxyTargetWidth = getTabletWidthFromSettings(); + proxyTargetLocalRotation = Quat.multiply(proxyExpandLocalRotation, PROXY_EXPAND_DELTA_ROTATION); proxyExpandStart = Date.now(); proxyExpandTimer = Script.setTimeout(expandProxy, PROXY_EXPAND_TIMEOUT); } From 49f5d5f2b7dd7fccb1a7bfc73733c12daf28b431 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 29 Aug 2018 08:55:46 +1200 Subject: [PATCH 035/259] Display mini tablet rotated in palm --- scripts/system/miniTablet.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index ca665b888a..25a3389539 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -22,12 +22,12 @@ PROXY_DIMENSIONS = { x: 0.0637, y: 0.0965, z: 0.0046 }, // Proportional to tablet proper. PROXY_POSITION_LEFT_HAND = { x: 0, - y: 0.07, // Distance from joint. + y: 0.1, // Distance from joint. z: 0.07 // Distance above palm. }, PROXY_POSITION_RIGHT_HAND = { x: 0, - y: 0.07, // Distance from joint. + y: 0.1, // Distance from joint. z: 0.07 // Distance above palm. }, /* @@ -36,8 +36,8 @@ PROXY_ROTATION_RIGHT_HAND = Quat.fromVec3Degrees({ x: 0, y: 180, z: -90 }), */ // Aligned with palm. - PROXY_ROTATION_LEFT_HAND = Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }), - PROXY_ROTATION_RIGHT_HAND = Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }), + PROXY_ROTATION_LEFT_HAND = Quat.fromVec3Degrees({ x: -40, y: 180, z: 0 }), + PROXY_ROTATION_RIGHT_HAND = Quat.fromVec3Degrees({ x: -40, y: 180, z: 0 }), // UI overlay. proxyUIOverlay = null, @@ -79,7 +79,7 @@ { x: 0.5, y: -0.75, z: 0 }, { x: -0.5, y: -0.75, z: 0 } ], - PROXY_EXPAND_DELTA_ROTATION = Quat.fromVec3Degrees({ x: -45, y: 0, z: 0 }), + PROXY_EXPAND_DELTA_ROTATION = Quat.fromVec3Degrees({ x: -5, y: 0, z: 0 }), proxyExpandHand, proxyExpandLocalPosition, proxyExpandLocalRotation = Quat.IDENTITY, From f5c49026abebf14d166a7c6ef2bc5f0e545141f4 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 29 Aug 2018 09:49:42 +1200 Subject: [PATCH 036/259] Expand tablet after it's been released from grab by other hand --- scripts/system/miniTablet.js | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 25a3389539..c7f04cca11 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -64,10 +64,11 @@ PROXY_HIDING = 2, PROXY_SHOWING = 3, PROXY_VISIBLE = 4, - PROXY_EXPANDING = 5, - TABLET_OPEN = 6, - STATE_STRINGS = ["PROXY_DISABLED", "PROXY_HIDDEN", "PROXY_HIDING", "PROXY_SHOWING", "PROXY_VISIBLE", "PROXY_EXPANDING", - "TABLET_OPEN"], + PROXY_GRABBED = 5, + PROXY_EXPANDING = 6, + TABLET_OPEN = 7, + STATE_STRINGS = ["PROXY_DISABLED", "PROXY_HIDDEN", "PROXY_HIDING", "PROXY_SHOWING", "PROXY_VISIBLE", "PROXY_GRABBED", + "PROXY_EXPANDING", "TABLET_OPEN"], STATE_MACHINE, rezzerState = PROXY_DISABLED, proxyHand, @@ -513,6 +514,13 @@ } } + function updateProxyGrabbed() { + // Hide proxy if tablet has been displayed by other means. + if (HMD.showTablet) { + setState(PROXY_HIDDEN); + } + } + function expandProxy() { var scaleFactor = (Date.now() - proxyExpandStart) / PROXY_EXPAND_DURATION; if (scaleFactor < 1) { @@ -598,7 +606,12 @@ update: updateProxyVisible, exit: null }, - PROXY_EXPANDING: { // Tablet proxy has been grabbed and is expanding before showing tablet proper. + PROXY_GRABBED: { // Tablet proxy is grabbed by other hand. + enter: null, + update: updateProxyGrabbed, + exit: null + }, + PROXY_EXPANDING: { // Tablet proxy is expanding before showing tablet proper. enter: enterProxyExpanding, update: updateProxyExanding, exit: exitProxyExpanding @@ -655,6 +668,13 @@ } if (message.action === "grab" && rezzerState === PROXY_VISIBLE) { + hand = message.joint === HAND_NAMES[proxyHand] ? proxyHand : otherHand(proxyHand); + if (hand === proxyHand) { + setState(PROXY_EXPANDING, hand); + } else { + setState(PROXY_GRABBED); + } + } else if (message.action === "release" && rezzerState === PROXY_GRABBED) { hand = message.joint === HAND_NAMES[proxyHand] ? proxyHand : otherHand(proxyHand); setState(PROXY_EXPANDING, hand); } From 91c1e1b27679f44b118c350a3f5d6be4465a3714 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 29 Aug 2018 09:59:04 +1200 Subject: [PATCH 037/259] Different expansion points and delta rotations if grabbed by other hand --- scripts/system/miniTablet.js | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index c7f04cca11..efe33303a8 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -81,7 +81,14 @@ { x: -0.5, y: -0.75, z: 0 } ], PROXY_EXPAND_DELTA_ROTATION = Quat.fromVec3Degrees({ x: -5, y: 0, z: 0 }), + PROXY_EXPAND_HANDLES_OTHER = [ // Different handles when expanding after being grabbed by other hand, + { x: 0.5, y: -0.4, z: 0 }, + { x: -0.5, y: -0.4, z: 0 } + ], + PROXY_EXPAND_DELTA_ROTATION_OTHER = Quat.IDENTITY, proxyExpandHand, + proxyExpandHandles = PROXY_EXPAND_HANDLES, + proxyExpandDeltaRotation = PROXY_EXPAND_HANDLES_OTHER, proxyExpandLocalPosition, proxyExpandLocalRotation = Quat.IDENTITY, PROXY_EXPAND_DURATION = 250, @@ -331,7 +338,7 @@ Vec3.sum(proxyExpandLocalPosition, Vec3.multiplyQbyV(proxyExpandLocalRotation, Vec3.multiply(-tabletScaleFactor, - Vec3.multiplyVbyV(PROXY_EXPAND_HANDLES[proxyExpandHand], PROXY_DIMENSIONS))) + Vec3.multiplyVbyV(proxyExpandHandles[proxyExpandHand], PROXY_DIMENSIONS))) ); localPosition = Vec3.sum(localPosition, Vec3.multiplyQbyV(proxyExpandLocalRotation, { x: 0, y: 0.5 * -dimensions.y, z: 0 })); @@ -533,18 +540,27 @@ } function enterProxyExpanding(hand) { + // Expansion details. + if (hand === proxyHand) { + proxyExpandHandles = PROXY_EXPAND_HANDLES; + proxyExpandDeltaRotation = PROXY_EXPAND_DELTA_ROTATION; + } else { + proxyExpandHandles = PROXY_EXPAND_HANDLES_OTHER; + proxyExpandDeltaRotation = PROXY_EXPAND_DELTA_ROTATION_OTHER; + } + // Grab details. var properties = Overlays.getProperties(proxyOverlay, ["localPosition", "localRotation"]); proxyExpandHand = hand; proxyExpandLocalRotation = properties.localRotation; proxyExpandLocalPosition = Vec3.sum(properties.localPosition, Vec3.multiplyQbyV(proxyExpandLocalRotation, - Vec3.multiplyVbyV(PROXY_EXPAND_HANDLES[proxyExpandHand], PROXY_DIMENSIONS))); + Vec3.multiplyVbyV(proxyExpandHandles[proxyExpandHand], PROXY_DIMENSIONS))); // Start expanding. proxyInitialWidth = PROXY_DIMENSIONS.x; proxyTargetWidth = getTabletWidthFromSettings(); - proxyTargetLocalRotation = Quat.multiply(proxyExpandLocalRotation, PROXY_EXPAND_DELTA_ROTATION); + proxyTargetLocalRotation = Quat.multiply(proxyExpandLocalRotation, proxyExpandDeltaRotation); proxyExpandStart = Date.now(); proxyExpandTimer = Script.setTimeout(expandProxy, PROXY_EXPAND_TIMEOUT); } From b338c9f6fa42f354c30294b6f499d87bfaa992b2 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 29 Aug 2018 22:10:24 +1200 Subject: [PATCH 038/259] Updated tiny table model and layout --- scripts/system/assets/models/tinyTablet.fbx | Bin 411712 -> 476000 bytes scripts/system/html/css/miniTablet.css | 54 ++++++++++---------- scripts/system/html/miniTablet.html | 4 +- scripts/system/miniTablet.js | 6 +-- 4 files changed, 30 insertions(+), 34 deletions(-) diff --git a/scripts/system/assets/models/tinyTablet.fbx b/scripts/system/assets/models/tinyTablet.fbx index cdcdf8af3461946f9050f07f897312fb77f5ef38..4ad5d8760fcb2828ff0011eb2925066e9cd9d6ae 100644 GIT binary patch literal 476000 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%0DpIuU8KG@@UbK& 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.|9_K=*k&m*FjF(-ie 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

~y{jVIB$JG{1IMNo@mcB`e&CPw3 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 delta 151033 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ξhFML9FrF(}WHqzm`gwz7sl(6**E{vM=cJ4F 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 S{ixaLH{}lrH^yt5hyEXxI +

`
-
-

...

-
diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 25a3389539..403511365b 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -19,7 +19,7 @@ var // Base overlay proxyOverlay = null, PROXY_MODEL = Script.resolvePath("./assets/models/tinyTablet.fbx"), - PROXY_DIMENSIONS = { x: 0.0637, y: 0.0965, z: 0.0046 }, // Proportional to tablet proper. + PROXY_DIMENSIONS = { x: 0.0637, y: 0.0965, z: 0.0045 }, // Proportional to tablet proper. PROXY_POSITION_LEFT_HAND = { x: 0, y: 0.1, // Distance from joint. @@ -42,12 +42,12 @@ // UI overlay. proxyUIOverlay = null, PROXY_UI_HTML = Script.resolvePath("./html/miniTablet.html"), - PROXY_UI_DIMENSIONS = { x: 0.0577, y: 0.0905 }, + PROXY_UI_DIMENSIONS = { x: 0.059, y: 0.0865 }, PROXY_UI_WIDTH_PIXELS = 150, METERS_TO_INCHES = 39.3701, PROXY_UI_DPI = PROXY_UI_WIDTH_PIXELS / (PROXY_UI_DIMENSIONS.x * METERS_TO_INCHES), PROXY_UI_OFFSET = 0.001, // Above model surface. - PROXY_UI_LOCAL_POSITION = { x: 0, y: 0, z: -(PROXY_DIMENSIONS.z / 2 + PROXY_UI_OFFSET) }, + PROXY_UI_LOCAL_POSITION = { x: 0.0002, y: 0.0024, z: -(PROXY_DIMENSIONS.z / 2 + PROXY_UI_OFFSET) }, PROXY_UI_LOCAL_ROTATION = Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }), proxyUIOverlayEnabled = false, PROXY_UI_OVERLAY_ENABLED_DELAY = 500, From 15fcafa2f5babf9ec6ac53302c4cdc8e4c2af5ae Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 29 Aug 2018 22:13:09 +1200 Subject: [PATCH 039/259] Use SVGs for buttons --- .../system/html/css/img/mt-bubble-a-hover.svg | 5 ++ .../html/css/img/mt-bubble-a-normal.svg | 5 ++ .../system/html/css/img/mt-bubble-i-hover.svg | 5 ++ .../html/css/img/mt-bubble-i-normal.svg | 5 ++ .../system/html/css/img/mt-expand-hover.svg | 31 +++++++ .../system/html/css/img/mt-expand-normal.svg | 31 +++++++ scripts/system/html/css/img/mt-mute-hover.svg | 5 ++ .../system/html/css/img/mt-mute-normal.svg | 5 ++ scripts/system/html/css/miniTablet.css | 82 +++++++++---------- scripts/system/html/miniTablet.html | 2 +- 10 files changed, 134 insertions(+), 42 deletions(-) create mode 100644 scripts/system/html/css/img/mt-bubble-a-hover.svg create mode 100644 scripts/system/html/css/img/mt-bubble-a-normal.svg create mode 100644 scripts/system/html/css/img/mt-bubble-i-hover.svg create mode 100644 scripts/system/html/css/img/mt-bubble-i-normal.svg create mode 100644 scripts/system/html/css/img/mt-expand-hover.svg create mode 100644 scripts/system/html/css/img/mt-expand-normal.svg create mode 100644 scripts/system/html/css/img/mt-mute-hover.svg create mode 100644 scripts/system/html/css/img/mt-mute-normal.svg diff --git a/scripts/system/html/css/img/mt-bubble-a-hover.svg b/scripts/system/html/css/img/mt-bubble-a-hover.svg new file mode 100644 index 0000000000..36abbf5c34 --- /dev/null +++ b/scripts/system/html/css/img/mt-bubble-a-hover.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/scripts/system/html/css/img/mt-bubble-a-normal.svg b/scripts/system/html/css/img/mt-bubble-a-normal.svg new file mode 100644 index 0000000000..e6e8021bf5 --- /dev/null +++ b/scripts/system/html/css/img/mt-bubble-a-normal.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/scripts/system/html/css/img/mt-bubble-i-hover.svg b/scripts/system/html/css/img/mt-bubble-i-hover.svg new file mode 100644 index 0000000000..e39dff3888 --- /dev/null +++ b/scripts/system/html/css/img/mt-bubble-i-hover.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/scripts/system/html/css/img/mt-bubble-i-normal.svg b/scripts/system/html/css/img/mt-bubble-i-normal.svg new file mode 100644 index 0000000000..ef2591ba16 --- /dev/null +++ b/scripts/system/html/css/img/mt-bubble-i-normal.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/scripts/system/html/css/img/mt-expand-hover.svg b/scripts/system/html/css/img/mt-expand-hover.svg new file mode 100644 index 0000000000..eea3487bd8 --- /dev/null +++ b/scripts/system/html/css/img/mt-expand-hover.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/system/html/css/img/mt-expand-normal.svg b/scripts/system/html/css/img/mt-expand-normal.svg new file mode 100644 index 0000000000..7b5b59a7c9 --- /dev/null +++ b/scripts/system/html/css/img/mt-expand-normal.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/system/html/css/img/mt-mute-hover.svg b/scripts/system/html/css/img/mt-mute-hover.svg new file mode 100644 index 0000000000..59c38ca5a0 --- /dev/null +++ b/scripts/system/html/css/img/mt-mute-hover.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/scripts/system/html/css/img/mt-mute-normal.svg b/scripts/system/html/css/img/mt-mute-normal.svg new file mode 100644 index 0000000000..1f25bce9c6 --- /dev/null +++ b/scripts/system/html/css/img/mt-mute-normal.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/scripts/system/html/css/miniTablet.css b/scripts/system/html/css/miniTablet.css index 35a8354750..e633f93664 100644 --- a/scripts/system/html/css/miniTablet.css +++ b/scripts/system/html/css/miniTablet.css @@ -8,14 +8,6 @@ Distributed under the Apache License, Version 2.0. See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html */ -@font-face { - font-family: HiFi-Glyphs; - src: url(../../../../resources/fonts/hifi-glyphs.ttf), - url(../../../../fonts/hifi-glyphs.ttf), - url(../../../../interface/resources/fonts/hifi-glyphs.ttf), - url(../fonts/hifi-glyphs.ttf); -} - * { box-sizing: border-box; padding: 0; @@ -37,34 +29,14 @@ section { padding: 13px; } -section .button { +.button { width: 100%; height: 90px; - background-color: #252525; margin-top: 10px; text-align: center; - border-radius: 8px; } - section .button.off { - border: 2px solid #6a6a6a; - background-color: #303030; - } - - section .button.off:hover { - border: 2px solid #1fc6a6; - } - - section .button.on { - border: 2px solid #1fc6a6; - background-color: #1fc6a6; - } - - section .button.on:hover { - border: 2px solid #e3e3e3; - } - - section .button:first-child { + .button:first-child { margin-top: 0; } @@ -74,28 +46,56 @@ img { #mute { padding-top: 19px; + background-size: 100% 100%; } + #mute.off { + background-image: url("./img/mt-mute-normal.svg"); + } + + #mute.off:hover { + background-image: url("./img/mt-mute-hover.svg"); + } + #bubble { padding-top: 19px; + background-size: 100% 100%; } + #bubble.off { + background-image: url("./img/mt-bubble-i-normal.svg"); + } + + #bubble.off:hover { + background-image: url("./img/mt-bubble-i-hover.svg"); + } + + #bubble.on { + background-image: url("./img/mt-bubble-a-normal.svg"); + } + + #bubble.on:hover { + background-image: url("./img/mt-bubble-a-hover.svg"); + } + + #bubble img { + position: relative; + left: -2px; + } + #expand { position: absolute; - right: 13px; + right: 14px; bottom: 13px; width: 40px; height: 40px; - border-radius: 20px; + background-size: 100% 100%; } - #expand span { - display: inline-block; - transform: rotate(45deg); - position: relative; - left: 0; - top: 5px; - font-family: hifi-glyphs; - font-size: 24px; - color: #ffffff; + #expand.off { + background-image: url("./img/mt-expand-normal.svg"); } + + #expand.off:hover { + background-image: url("./img/mt-expand-hover.svg"); + } diff --git a/scripts/system/html/miniTablet.html b/scripts/system/html/miniTablet.html index 1f18228987..4b3129fca9 100644 --- a/scripts/system/html/miniTablet.html +++ b/scripts/system/html/miniTablet.html @@ -23,7 +23,7 @@ See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.
-
`
+
From a5c3450bfcad201b95d044cae6cb0aeff90f63f1 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 30 Aug 2018 13:43:31 +1200 Subject: [PATCH 040/259] Tweak mini tablet UI layout --- .../system/html/css/img/mt-bubble-a-hover.svg | 4 ++-- .../html/css/img/mt-bubble-a-normal.svg | 4 ++-- .../system/html/css/img/mt-bubble-i-hover.svg | 4 ++-- .../html/css/img/mt-bubble-i-normal.svg | 4 ++-- .../system/html/css/img/mt-expand-hover.svg | 24 +++++++++---------- .../system/html/css/img/mt-expand-normal.svg | 24 +++++++++---------- scripts/system/html/css/img/mt-mute-hover.svg | 4 ++-- .../system/html/css/img/mt-mute-normal.svg | 4 ++-- scripts/system/html/css/miniTablet.css | 13 ++++------ 9 files changed, 40 insertions(+), 45 deletions(-) diff --git a/scripts/system/html/css/img/mt-bubble-a-hover.svg b/scripts/system/html/css/img/mt-bubble-a-hover.svg index 36abbf5c34..88ddfff808 100644 --- a/scripts/system/html/css/img/mt-bubble-a-hover.svg +++ b/scripts/system/html/css/img/mt-bubble-a-hover.svg @@ -1,5 +1,5 @@  - - + + diff --git a/scripts/system/html/css/img/mt-bubble-a-normal.svg b/scripts/system/html/css/img/mt-bubble-a-normal.svg index e6e8021bf5..3d9d0a1286 100644 --- a/scripts/system/html/css/img/mt-bubble-a-normal.svg +++ b/scripts/system/html/css/img/mt-bubble-a-normal.svg @@ -1,5 +1,5 @@  - - + + diff --git a/scripts/system/html/css/img/mt-bubble-i-hover.svg b/scripts/system/html/css/img/mt-bubble-i-hover.svg index e39dff3888..c8c407139b 100644 --- a/scripts/system/html/css/img/mt-bubble-i-hover.svg +++ b/scripts/system/html/css/img/mt-bubble-i-hover.svg @@ -1,5 +1,5 @@  - - + + diff --git a/scripts/system/html/css/img/mt-bubble-i-normal.svg b/scripts/system/html/css/img/mt-bubble-i-normal.svg index ef2591ba16..6be11c464e 100644 --- a/scripts/system/html/css/img/mt-bubble-i-normal.svg +++ b/scripts/system/html/css/img/mt-bubble-i-normal.svg @@ -1,5 +1,5 @@  - - + + diff --git a/scripts/system/html/css/img/mt-expand-hover.svg b/scripts/system/html/css/img/mt-expand-hover.svg index eea3487bd8..97737f42c0 100644 --- a/scripts/system/html/css/img/mt-expand-hover.svg +++ b/scripts/system/html/css/img/mt-expand-hover.svg @@ -5,26 +5,26 @@ - - - + + + - - - + + + - - - + + + - - - + + + diff --git a/scripts/system/html/css/img/mt-expand-normal.svg b/scripts/system/html/css/img/mt-expand-normal.svg index 7b5b59a7c9..8b849ecc70 100644 --- a/scripts/system/html/css/img/mt-expand-normal.svg +++ b/scripts/system/html/css/img/mt-expand-normal.svg @@ -5,26 +5,26 @@ - - - + + + - - - + + + - - - + + + - - - + + + diff --git a/scripts/system/html/css/img/mt-mute-hover.svg b/scripts/system/html/css/img/mt-mute-hover.svg index 59c38ca5a0..909ecb0272 100644 --- a/scripts/system/html/css/img/mt-mute-hover.svg +++ b/scripts/system/html/css/img/mt-mute-hover.svg @@ -1,5 +1,5 @@  - - + + diff --git a/scripts/system/html/css/img/mt-mute-normal.svg b/scripts/system/html/css/img/mt-mute-normal.svg index 1f25bce9c6..506756216f 100644 --- a/scripts/system/html/css/img/mt-mute-normal.svg +++ b/scripts/system/html/css/img/mt-mute-normal.svg @@ -1,5 +1,5 @@  - - + + diff --git a/scripts/system/html/css/miniTablet.css b/scripts/system/html/css/miniTablet.css index e633f93664..3b01a45613 100644 --- a/scripts/system/html/css/miniTablet.css +++ b/scripts/system/html/css/miniTablet.css @@ -26,11 +26,11 @@ body { section { background-color: #404040; position: relative; - padding: 13px; + padding: 13px 9px; } .button { - width: 100%; + width: 128px; height: 90px; margin-top: 10px; text-align: center; @@ -41,7 +41,7 @@ section { } img { - width: 44px; + width: 46px; } #mute { @@ -78,14 +78,9 @@ img { background-image: url("./img/mt-bubble-a-hover.svg"); } - #bubble img { - position: relative; - left: -2px; - } - #expand { position: absolute; - right: 14px; + right: 12px; bottom: 13px; width: 40px; height: 40px; From ec20fb7edb21cbb0c9af41163c70eff93217826f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 30 Aug 2018 16:16:49 +1200 Subject: [PATCH 041/259] Reorient and reposition mini tablet --- scripts/system/miniTablet.js | 59 ++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 403511365b..b91b016caf 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -20,24 +20,22 @@ proxyOverlay = null, PROXY_MODEL = Script.resolvePath("./assets/models/tinyTablet.fbx"), PROXY_DIMENSIONS = { x: 0.0637, y: 0.0965, z: 0.0045 }, // Proportional to tablet proper. - PROXY_POSITION_LEFT_HAND = { - x: 0, - y: 0.1, // Distance from joint. - z: 0.07 // Distance above palm. - }, - PROXY_POSITION_RIGHT_HAND = { - x: 0, - y: 0.1, // Distance from joint. - z: 0.07 // Distance above palm. - }, - /* - // Aligned cross-palm. - PROXY_ROTATION_LEFT_HAND = Quat.fromVec3Degrees({ x: 0, y: 180, z: 90 }), - PROXY_ROTATION_RIGHT_HAND = Quat.fromVec3Degrees({ x: 0, y: 180, z: -90 }), - */ - // Aligned with palm. - PROXY_ROTATION_LEFT_HAND = Quat.fromVec3Degrees({ x: -40, y: 180, z: 0 }), - PROXY_ROTATION_RIGHT_HAND = Quat.fromVec3Degrees({ x: -40, y: 180, z: 0 }), + PROXY_POSITIONS = [ + { + x: -0.03, // Distance across hand. + y: 0.08, // Distance from joint. + z: 0.06 // Distance above palm. + }, + { + x: 0.03, // Distance across hand. + y: 0.08, // Distance from joint. + z: 0.06 // Distance above palm. + } + ], + PROXY_ROTATIONS = [ + Quat.fromVec3Degrees({ x: 0, y: 180 - 40, z: 90 }), + Quat.fromVec3Degrees({ x: 0, y: 180 + 40, z: -90 }), + ], // UI overlay. proxyUIOverlay = null, @@ -76,10 +74,10 @@ proxyScaleTimer = null, proxyScaleStart, PROXY_EXPAND_HANDLES = [ // Normalized coordinates in range [-0.5, 0.5] about center of mini tablet. - { x: 0.5, y: -0.75, z: 0 }, - { x: -0.5, y: -0.75, z: 0 } + { x: 0.5, y: -0.65, z: 0 }, + { x: -0.5, y: -0.65, z: 0 } ], - PROXY_EXPAND_DELTA_ROTATION = Quat.fromVec3Degrees({ x: -5, y: 0, z: 0 }), + PROXY_EXPAND_DELTA_ROTATION = Quat.fromVec3Degrees({ x: 0, y: 0, z: 0 }), proxyExpandHand, proxyExpandLocalPosition, proxyExpandLocalRotation = Quat.IDENTITY, @@ -284,9 +282,8 @@ Overlays.editOverlay(proxyOverlay, { parentID: MyAvatar.SELF_ID, parentJointIndex: handJointIndex(proxyHand), - localPosition: Vec3.multiply(avatarScale, - proxyHand === LEFT_HAND ? PROXY_POSITION_LEFT_HAND : PROXY_POSITION_RIGHT_HAND), - localRotation: proxyHand === LEFT_HAND ? PROXY_ROTATION_LEFT_HAND : PROXY_ROTATION_RIGHT_HAND, + localPosition: Vec3.multiply(avatarScale, PROXY_POSITIONS[proxyHand]), + localRotation: PROXY_ROTATIONS[proxyHand], dimensions: Vec3.multiply(initialScale, PROXY_DIMENSIONS), visible: true }); @@ -402,12 +399,14 @@ } function shouldShowProxy(hand) { - // Should show tablet proxy if hand is oriented toward the camera and the camera is oriented toward the proxy tablet. + // Should show proxy if it would be oriented toward the camera. var pose, jointIndex, handPosition, handOrientation, - cameraToHandDirection; + proxyPosition, + proxyOrientation, + proxyToCameraDirection; pose = Controller.getPoseValue(hand === LEFT_HAND ? Controller.Standard.LeftHand : Controller.Standard.RightHand); if (!pose.valid) { @@ -418,9 +417,11 @@ handPosition = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, MyAvatar.getAbsoluteJointTranslationInObjectFrame(jointIndex))); handOrientation = Quat.multiply(MyAvatar.orientation, MyAvatar.getAbsoluteJointRotationInObjectFrame(jointIndex)); - cameraToHandDirection = Vec3.normalize(Vec3.subtract(handPosition, Camera.position)); - - return Vec3.dot(cameraToHandDirection, Quat.getForward(handOrientation)) > MIN_HAND_CAMERA_ANGLE_COS; + proxyPosition = Vec3.sum(handPosition, Vec3.multiply(avatarScale, + Vec3.multiplyQbyV(handOrientation, PROXY_POSITIONS[hand]))); + proxyOrientation = Quat.multiply(handOrientation, PROXY_ROTATIONS[hand]); + proxyToCameraDirection = Vec3.normalize(Vec3.subtract(Camera.position, proxyPosition)); + return Vec3.dot(proxyToCameraDirection, Quat.getForward(proxyOrientation)) > MIN_HAND_CAMERA_ANGLE_COS; } function enterProxyHidden() { From ec0b0ab464fcb12b0e01b86cb837470259be2069 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 4 Sep 2018 12:59:53 -0700 Subject: [PATCH 042/259] fix acceleration spread --- .../src/RenderableParticleEffectEntityItem.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 18c4921836..1a263fba79 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -187,12 +187,10 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa particle.basePosition = baseTransform.getTranslation(); // Position, velocity, and acceleration + glm::vec3 emitDirection; if (polarStart == 0.0f && polarFinish == 0.0f && emitDimensions.z == 0.0f) { // Emit along z-axis from position - - particle.velocity = (emitSpeed + randFloatInRange(-1.0f, 1.0f) * speedSpread) * (emitOrientation * Vectors::UNIT_Z); - particle.acceleration = emitAcceleration + randFloatInRange(-1.0f, 1.0f) * accelerationSpread; - + emitDirection = Vectors::UNIT_Z; } else { // Emit around point or from ellipsoid // - Distribute directions evenly around point @@ -210,7 +208,6 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa azimuth = azimuthStart + (TWO_PI + azimuthFinish - azimuthStart) * randFloat(); } - glm::vec3 emitDirection; if (emitDimensions == Vectors::ZERO) { // Point emitDirection = glm::quat(glm::vec3(PI_OVER_TWO - elevation, 0.0f, azimuth)) * Vectors::UNIT_Z; @@ -235,10 +232,10 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa )); particle.relativePosition += emitOrientation * emitPosition; } - - particle.velocity = (emitSpeed + randFloatInRange(-1.0f, 1.0f) * speedSpread) * (emitOrientation * emitDirection); - particle.acceleration = emitAcceleration + randFloatInRange(-1.0f, 1.0f) * accelerationSpread; } + particle.velocity = (emitSpeed + randFloatInRange(-1.0f, 1.0f) * speedSpread) * (emitOrientation * emitDirection); + particle.acceleration = emitAcceleration + + glm::vec3(randFloatInRange(-1.0f, 1.0f), randFloatInRange(-1.0f, 1.0f), randFloatInRange(-1.0f, 1.0f)) * accelerationSpread; return particle; } From 2d2a4804f7790f41a8b9af373c5274ff73e7c84c Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Thu, 6 Sep 2018 17:49:52 -0300 Subject: [PATCH 043/259] Add android settings screen. Add AEC setting --- android/app/src/main/AndroidManifest.xml | 10 +++ android/app/src/main/cpp/native.cpp | 46 ++++++++++++++ .../hifiinterface/InterfaceActivity.java | 7 +++ .../hifiinterface/MainActivity.java | 23 +++++++ .../fragment/SettingsFragment.java | 63 +++++++++++++++++++ .../receiver/HeadsetStateReceiver.java | 20 ++++++ .../app/src/main/res/menu/menu_navigation.xml | 5 ++ android/app/src/main/res/values/strings.xml | 5 ++ android/app/src/main/res/xml/settings.xml | 11 ++++ interface/src/AndroidHelper.cpp | 17 +++++ interface/src/AndroidHelper.h | 1 + libraries/audio-client/src/AudioClient.cpp | 6 +- libraries/audio-client/src/AudioClient.h | 8 +++ 13 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 android/app/src/main/java/io/highfidelity/hifiinterface/fragment/SettingsFragment.java create mode 100644 android/app/src/main/java/io/highfidelity/hifiinterface/receiver/HeadsetStateReceiver.java create mode 100644 android/app/src/main/res/xml/settings.xml diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 7255e1f295..b216819ed0 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -9,6 +9,7 @@ + @@ -75,6 +76,15 @@ android:enabled="true" android:exported="false" android:process=":breakpad_uploader"/> + + + + + + diff --git a/android/app/src/main/cpp/native.cpp b/android/app/src/main/cpp/native.cpp index ce5af01f29..6b44b2dc7a 100644 --- a/android/app/src/main/cpp/native.cpp +++ b/android/app/src/main/cpp/native.cpp @@ -355,5 +355,51 @@ JNIEXPORT void Java_io_highfidelity_hifiinterface_WebViewActivity_nativeProcessU AndroidHelper::instance().processURL(QString::fromUtf8(nativeString)); } +JNIEXPORT void JNICALL +Java_io_highfidelity_hifiinterface_fragment_SettingsFragment_updateHifiSetting(JNIEnv *env, + jobject instance, + jstring group_, + jstring key_, + jboolean value_) { + const char *c_group = env->GetStringUTFChars(group_, 0); + const char *c_key = env->GetStringUTFChars(key_, 0); + + const QString group = QString::fromUtf8(c_group); + const QString key = QString::fromUtf8(c_key); + + env->ReleaseStringUTFChars(group_, c_group); + env->ReleaseStringUTFChars(key_, c_key); + + bool value = value_; + + Setting::Handle setting { QStringList() << group << key , !value }; + setting.set(value); +} + +JNIEXPORT jboolean JNICALL +Java_io_highfidelity_hifiinterface_fragment_SettingsFragment_getHifiSettingBoolean(JNIEnv *env, + jobject instance, + jstring group_, + jstring key_, + jboolean defaultValue) { + const char *c_group = env->GetStringUTFChars(group_, 0); + const char *c_key = env->GetStringUTFChars(key_, 0); + + const QString group = QString::fromUtf8(c_group); + const QString key = QString::fromUtf8(c_key); + + env->ReleaseStringUTFChars(group_, c_group); + env->ReleaseStringUTFChars(key_, c_key); + + Setting::Handle setting { QStringList() << group << key , defaultValue}; + return setting.get(); +} + +JNIEXPORT void JNICALL +Java_io_highfidelity_hifiinterface_receiver_HeadsetStateReceiver_notifyHeadsetOn(JNIEnv *env, + jobject instance, + jboolean pluggedIn) { + AndroidHelper::instance().notifyHeadsetOn(pluggedIn); +} } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java index f161783d6a..08e66a2f42 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java @@ -13,6 +13,7 @@ package io.highfidelity.hifiinterface; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -40,6 +41,7 @@ import java.util.HashMap; import java.util.List; import io.highfidelity.hifiinterface.fragment.WebViewFragment; +import io.highfidelity.hifiinterface.receiver.HeadsetStateReceiver; /*import com.google.vr.cardboard.DisplaySynchronizer; import com.google.vr.cardboard.DisplayUtils; @@ -55,6 +57,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW private static final int NORMAL_DPI = 160; private Vibrator mVibrator; + private HeadsetStateReceiver headsetStateReceiver; //public static native void handleHifiURL(String hifiURLString); private native long nativeOnCreate(InterfaceActivity instance, AssetManager assetManager); @@ -151,6 +154,8 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW layoutParams.resolveLayoutDirection(View.LAYOUT_DIRECTION_RTL); qtLayout.addView(webSlidingDrawer, layoutParams); webSlidingDrawer.setVisibility(View.GONE); + + headsetStateReceiver = new HeadsetStateReceiver(); } @Override @@ -161,6 +166,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW } else { nativeEnterBackground(); } + unregisterReceiver(headsetStateReceiver); //gvrApi.pauseTracking(); } @@ -183,6 +189,7 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW nativeEnterForeground(); surfacesWorkaround(); keepInterfaceRunning = false; + registerReceiver(headsetStateReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG)); //gvrApi.resumeTracking(); } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java index db6f0fca24..4c6d05a3e8 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java @@ -33,6 +33,7 @@ import io.highfidelity.hifiinterface.fragment.FriendsFragment; import io.highfidelity.hifiinterface.fragment.HomeFragment; import io.highfidelity.hifiinterface.fragment.LoginFragment; import io.highfidelity.hifiinterface.fragment.PolicyFragment; +import io.highfidelity.hifiinterface.fragment.SettingsFragment; import io.highfidelity.hifiinterface.task.DownloadProfileImageTask; public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, @@ -80,6 +81,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On mPeopleMenuItem = mNavigationView.getMenu().findItem(R.id.action_people); + updateDebugMenu(mNavigationView.getMenu()); + Toolbar toolbar = findViewById(R.id.toolbar); toolbar.setTitleTextAppearance(this, R.style.HomeActionBarTitleStyle); setSupportActionBar(toolbar); @@ -108,6 +111,16 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On } } + private void updateDebugMenu(Menu menu) { + if (BuildConfig.DEBUG) { + for (int i=0; i < menu.size(); i++) { + if (menu.getItem(i).getItemId() == R.id.action_debug_settings) { + menu.getItem(i).setVisible(true); + } + } + } + } + private void loadFragment(String fragment) { switch (fragment) { case "Login": @@ -151,6 +164,13 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On loadFragment(fragment, getString(R.string.people), getString(R.string.tagFragmentPeople), true); } + private void loadSettingsFragment() { + SettingsFragment fragment = SettingsFragment.newInstance(); + + loadFragment(fragment, getString(R.string.settings), getString(R.string.tagSettings), true); + } + + private void loadFragment(Fragment fragment, String title, String tag, boolean addToBackStack) { FragmentManager fragmentManager = getFragmentManager(); @@ -241,6 +261,9 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On case R.id.action_people: loadPeopleFragment(); return true; + case R.id.action_debug_settings: + loadSettingsFragment(); + return true; } return false; } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/SettingsFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/SettingsFragment.java new file mode 100644 index 0000000000..cc23665e72 --- /dev/null +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/SettingsFragment.java @@ -0,0 +1,63 @@ +package io.highfidelity.hifiinterface.fragment; + +import android.content.SharedPreferences; +import android.media.audiofx.AcousticEchoCanceler; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.PreferenceFragment; +import android.support.annotation.Nullable; + +import io.highfidelity.hifiinterface.R; + +public class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { + + public native void updateHifiSetting(String group, String key, boolean value); + public native boolean getHifiSettingBoolean(String group, String key, boolean defaultValue); + + private final String HIFI_SETTINGS_ANDROID_GROUP = "Android"; + private final String HIFI_SETTINGS_AEC_KEY = "aec"; + private final String PREFERENCE_KEY_AEC = "aec"; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.settings); + + if (!AcousticEchoCanceler.isAvailable()) { + getPreferenceScreen().getPreferenceManager().findPreference("aec").setEnabled(false); + } + + getPreferenceScreen().getSharedPreferences().edit().putBoolean(PREFERENCE_KEY_AEC, + getHifiSettingBoolean(HIFI_SETTINGS_ANDROID_GROUP, HIFI_SETTINGS_AEC_KEY, false)); + } + + public static SettingsFragment newInstance() { + SettingsFragment fragment = new SettingsFragment(); + return fragment; + } + + @Override + public void onResume() { + super.onResume(); + getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); + } + + @Override + public void onPause() { + super.onPause(); + getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); + + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + Preference pref = findPreference(key); + switch (key) { + case "aec": + updateHifiSetting(HIFI_SETTINGS_ANDROID_GROUP, HIFI_SETTINGS_AEC_KEY, sharedPreferences.getBoolean(key, false)); + break; + default: + break; + } + } +} diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/receiver/HeadsetStateReceiver.java b/android/app/src/main/java/io/highfidelity/hifiinterface/receiver/HeadsetStateReceiver.java new file mode 100644 index 0000000000..29bc1c49f2 --- /dev/null +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/receiver/HeadsetStateReceiver.java @@ -0,0 +1,20 @@ +package io.highfidelity.hifiinterface.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.media.AudioManager; +import android.util.Log; + +public class HeadsetStateReceiver extends BroadcastReceiver { + + private native void notifyHeadsetOn(boolean pluggedIn); + + @Override + public void onReceive(Context context, Intent intent) { + AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + + Log.d("[HEADSET] " , "BR - Wired headset on:" + audioManager.isWiredHeadsetOn()); + notifyHeadsetOn(audioManager.isWiredHeadsetOn()); + } +} diff --git a/android/app/src/main/res/menu/menu_navigation.xml b/android/app/src/main/res/menu/menu_navigation.xml index 3cce64f9f5..142af5d146 100644 --- a/android/app/src/main/res/menu/menu_navigation.xml +++ b/android/app/src/main/res/menu/menu_navigation.xml @@ -9,4 +9,9 @@ android:id="@+id/action_people" android:title="@string/people" /> + diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index b158aba59d..abde15f484 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -29,4 +29,9 @@ tagFragmentLogin tagFragmentPolicy tagFragmentPeople + tagSettings + Settings + AEC + Acoustic Echo Cancellation + Developer diff --git a/android/app/src/main/res/xml/settings.xml b/android/app/src/main/res/xml/settings.xml new file mode 100644 index 0000000000..5ec47b1aff --- /dev/null +++ b/android/app/src/main/res/xml/settings.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/interface/src/AndroidHelper.cpp b/interface/src/AndroidHelper.cpp index 419382f2cb..35bf094591 100644 --- a/interface/src/AndroidHelper.cpp +++ b/interface/src/AndroidHelper.cpp @@ -10,6 +10,7 @@ // #include "AndroidHelper.h" #include +#include #include "Application.h" #if defined(qApp) @@ -18,6 +19,7 @@ #define qApp (static_cast(QCoreApplication::instance())) AndroidHelper::AndroidHelper() { + qRegisterMetaType("QAudio::Mode"); } AndroidHelper::~AndroidHelper() { @@ -56,3 +58,18 @@ void AndroidHelper::processURL(const QString &url) { qApp->acceptURL(url); } } + +void AndroidHelper::notifyHeadsetOn(bool pluggedIn) { +#if defined (Q_OS_ANDROID) + auto audioClient = DependencyManager::get(); + if (audioClient) { + QAudioDeviceInfo activeDev = audioClient->getActiveAudioDevice(QAudio::AudioInput); + Setting::Handle enableAEC(QStringList() << ANDROID_SETTINGS_GROUP << SETTING_AEC_KEY, false); + if ((pluggedIn || !enableAEC.get()) && !activeDev.isNull() && activeDev.deviceName() != VOICE_RECOGNITION) { + QMetaObject::invokeMethod(audioClient.get(), "switchAudioDevice", Q_ARG(QAudio::Mode, QAudio::AudioInput), Q_ARG(QString, VOICE_RECOGNITION)); + } else if ( (!pluggedIn && enableAEC.get()) && !activeDev.isNull() && activeDev.deviceName() != VOICE_COMMUNICATION) { + QMetaObject::invokeMethod(audioClient.get(), "switchAudioDevice", Q_ARG(QAudio::Mode, QAudio::AudioInput), Q_ARG(QString, VOICE_COMMUNICATION)); + } + } +#endif +} diff --git a/interface/src/AndroidHelper.h b/interface/src/AndroidHelper.h index 03d92f91d9..11b84e4025 100644 --- a/interface/src/AndroidHelper.h +++ b/interface/src/AndroidHelper.h @@ -29,6 +29,7 @@ public: void performHapticFeedback(int duration); void processURL(const QString &url); + void notifyHeadsetOn(bool pluggedIn); AndroidHelper(AndroidHelper const&) = delete; void operator=(AndroidHelper const&) = delete; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 6a9363f309..e23b8ac3cd 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -53,7 +53,6 @@ #include "AudioHelpers.h" #if defined(Q_OS_ANDROID) -#define VOICE_RECOGNITION "voicerecognition" #include #endif @@ -451,9 +450,12 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { #if defined (Q_OS_ANDROID) if (mode == QAudio::AudioInput) { + Setting::Handle enableAEC(QStringList() << ANDROID_SETTINGS_GROUP << SETTING_AEC_KEY, false); + bool aecEnabled = enableAEC.get(); auto inputDevices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); for (auto inputDevice : inputDevices) { - if (inputDevice.deviceName() == VOICE_RECOGNITION) { + if ((aecEnabled && inputDevice.deviceName() == VOICE_COMMUNICATION) || + (!aecEnabled && inputDevice.deviceName() == VOICE_RECOGNITION)) { return inputDevice; } } diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 8599c990a3..fa7ac40a16 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -64,6 +64,14 @@ #pragma warning( pop ) #endif +#if defined (Q_OS_ANDROID) +#define VOICE_RECOGNITION "voicerecognition" +#define VOICE_COMMUNICATION "voicecommunication" + +#define ANDROID_SETTINGS_GROUP "Android" +#define SETTING_AEC_KEY "aec" +#endif + class QAudioInput; class QAudioOutput; class QIODevice; From d0e73b00f783bb66a5083a146c8ec427378630a5 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 7 Sep 2018 15:41:30 +1200 Subject: [PATCH 044/259] Restructure mini tablet code --- scripts/system/miniTablet.js | 1272 +++++++++++++++++++--------------- 1 file changed, 696 insertions(+), 576 deletions(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 52ca5c947a..c53fab88a5 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -16,118 +16,20 @@ Script.include("./libraries/utils.js"); - var // Base overlay - proxyOverlay = null, - PROXY_MODEL = Script.resolvePath("./assets/models/tinyTablet.fbx"), - PROXY_DIMENSIONS = { x: 0.0637, y: 0.0965, z: 0.0045 }, // Proportional to tablet proper. - PROXY_POSITIONS = [ - { - x: -0.03, // Distance across hand. - y: 0.08, // Distance from joint. - z: 0.06 // Distance above palm. - }, - { - x: 0.03, // Distance across hand. - y: 0.08, // Distance from joint. - z: 0.06 // Distance above palm. - } - ], - PROXY_ROTATIONS = [ - Quat.fromVec3Degrees({ x: 0, y: 180 - 40, z: 90 }), - Quat.fromVec3Degrees({ x: 0, y: 180 + 40, z: -90 }), - ], + var UI, + ui = null, + State, + miniState = null, - // UI overlay. - proxyUIOverlay = null, - PROXY_UI_HTML = Script.resolvePath("./html/miniTablet.html"), - PROXY_UI_DIMENSIONS = { x: 0.059, y: 0.0865 }, - PROXY_UI_WIDTH_PIXELS = 150, - METERS_TO_INCHES = 39.3701, - PROXY_UI_DPI = PROXY_UI_WIDTH_PIXELS / (PROXY_UI_DIMENSIONS.x * METERS_TO_INCHES), - PROXY_UI_OFFSET = 0.001, // Above model surface. - PROXY_UI_LOCAL_POSITION = { x: 0.0002, y: 0.0024, z: -(PROXY_DIMENSIONS.z / 2 + PROXY_UI_OFFSET) }, - PROXY_UI_LOCAL_ROTATION = Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }), - proxyUIOverlayEnabled = false, - PROXY_UI_OVERLAY_ENABLED_DELAY = 500, - proxyOverlayObject = null, - - MUTE_ON_ICON = Script.resourcesPath() + "icons/tablet-icons/mic-mute-a.svg", - MUTE_OFF_ICON = Script.resourcesPath() + "icons/tablet-icons/mic-unmute-i.svg", - BUBBLE_ON_ICON = Script.resourcesPath() + "icons/tablet-icons/bubble-a.svg", - BUBBLE_OFF_ICON = Script.resourcesPath() + "icons/tablet-icons/bubble-i.svg", - - // State machine - PROXY_DISABLED = 0, - PROXY_HIDDEN = 1, - PROXY_HIDING = 2, - PROXY_SHOWING = 3, - PROXY_VISIBLE = 4, - PROXY_GRABBED = 5, - PROXY_EXPANDING = 6, - TABLET_OPEN = 7, - STATE_STRINGS = ["PROXY_DISABLED", "PROXY_HIDDEN", "PROXY_HIDING", "PROXY_SHOWING", "PROXY_VISIBLE", "PROXY_GRABBED", - "PROXY_EXPANDING", "TABLET_OPEN"], - STATE_MACHINE, - rezzerState = PROXY_DISABLED, - proxyHand, - PROXY_SCALE_DURATION = 150, - PROXY_SCALE_TIMEOUT = 20, - proxyScaleTimer = null, - proxyScaleStart, - PROXY_EXPAND_HANDLES = [ // Normalized coordinates in range [-0.5, 0.5] about center of mini tablet. - { x: 0.5, y: -0.65, z: 0 }, - { x: -0.5, y: -0.65, z: 0 } - ], - PROXY_EXPAND_DELTA_ROTATION = Quat.fromVec3Degrees({ x: -5, y: 0, z: 0 }), - PROXY_EXPAND_HANDLES_OTHER = [ // Different handles when expanding after being grabbed by other hand, - { x: 0.5, y: -0.4, z: 0 }, - { x: -0.5, y: -0.4, z: 0 } - ], - PROXY_EXPAND_DELTA_ROTATION_OTHER = Quat.IDENTITY, - proxyExpandHand, - proxyExpandHandles = PROXY_EXPAND_HANDLES, - proxyExpandDeltaRotation = PROXY_EXPAND_HANDLES_OTHER, - proxyExpandLocalPosition, - proxyExpandLocalRotation = Quat.IDENTITY, - PROXY_EXPAND_DURATION = 250, - PROXY_EXPAND_TIMEOUT = 20, - proxyExpandTimer = null, - proxyExpandStart, - proxyInitialWidth, - proxyTargetWidth, - proxyTargetLocalRotation, - - // EventBridge - READY_MESSAGE = "ready", // Engine <== Dialog - HOVER_MESSAGE = "hover", // Engine <== Dialog - MUTE_MESSAGE = "mute", // Engine <=> Dialog - BUBBLE_MESSAGE = "bubble", // Engine <=> Dialog - EXPAND_MESSAGE = "expand", // Engine <== Dialog - - // Events - MIN_HAND_CAMERA_ANGLE = 30, - DEGREES_180 = 180, - MIN_HAND_CAMERA_ANGLE_COS = Math.cos(Math.PI * MIN_HAND_CAMERA_ANGLE / DEGREES_180), - updateTimer = null, - UPDATE_INTERVAL = 300, - HIFI_OBJECT_MANIPULATION_CHANNEL = "Hifi-Object-Manipulation", - avatarScale = 1, - - // Sounds - HOVER_SOUND = "./assets/sounds/button-hover.wav", - HOVER_VOLUME = 0.5, - CLICK_SOUND = "./assets/sounds/button-click.wav", - CLICK_VOLUME = 0.8, - hoverSound = SoundCache.getSound(Script.resolvePath(HOVER_SOUND)), - clickSound = SoundCache.getSound(Script.resolvePath(CLICK_SOUND)), - - // Hands + // Hands. LEFT_HAND = 0, RIGHT_HAND = 1, HAND_NAMES = ["LeftHand", "RightHand"], // Miscellaneous. + HIFI_OBJECT_MANIPULATION_CHANNEL = "Hifi-Object-Manipulation", tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"), + avatarScale = 1, // Sanitized MyAvatar.scale. DEBUG = false; // #region Utilities ======================================================================================================= @@ -173,498 +75,713 @@ return hand === LEFT_HAND ? RIGHT_HAND : LEFT_HAND; } - function playSound(sound, volume) { - Audio.playSound(sound, { - position: proxyHand === LEFT_HAND ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(), - volume: volume, - localOnly: true - }); - } - - // #endregion - - // #region Communications ================================================================================================== - - function updateMiniTabletID() { - // Send mini-tablet overlay ID to controllerDispatcher so that it can use a smaller near grab distance. - Messages.sendLocalMessage("Hifi-MiniTablet-ID", proxyOverlay); - // Send mini-tablet UI overlay ID to stylusInput so that it styluses can be used on it. - Messages.sendLocalMessage("Hifi-MiniTablet-UI-ID", proxyUIOverlay); - } - - function updateMutedStatus() { - var isMuted = Audio.muted; - proxyOverlayObject.emitScriptEvent(JSON.stringify({ - type: MUTE_MESSAGE, - on: isMuted, - icon: isMuted ? MUTE_ON_ICON : MUTE_OFF_ICON - })); - } - - function updateBubbleStatus() { - var isBubbleOn = Users.getIgnoreRadiusEnabled(); - proxyOverlayObject.emitScriptEvent(JSON.stringify({ - type: BUBBLE_MESSAGE, - on: isBubbleOn, - icon: isBubbleOn ? BUBBLE_ON_ICON : BUBBLE_OFF_ICON - })); - } - - function onWebEventReceived(data) { - var message; - - try { - message = JSON.parse(data); - } catch (e) { - console.error("EventBridge message error"); - return; - } - - switch (message.type) { - case READY_MESSAGE: - // Send initial button statuses. - updateMutedStatus(); - updateBubbleStatus(); - break; - case HOVER_MESSAGE: - // Audio feedback. - playSound(hoverSound, HOVER_VOLUME); - break; - case MUTE_MESSAGE: - // Toggle mute. - playSound(clickSound, CLICK_VOLUME); - Audio.muted = !Audio.muted; - break; - case BUBBLE_MESSAGE: - // Toggle bubble. - playSound(clickSound, CLICK_VOLUME); - Users.toggleIgnoreRadius(); - break; - case EXPAND_MESSAGE: - // Expand tablet; - playSound(clickSound, CLICK_VOLUME); - setState(PROXY_EXPANDING, proxyHand); - break; - } - } - // #endregion // #region UI ============================================================================================================== - function createUI() { - proxyOverlay = Overlays.addOverlay("model", { - url: PROXY_MODEL, - dimensions: Vec3.multiply(avatarScale, PROXY_DIMENSIONS), - solid: true, - grabbable: true, - showKeyboardFocusHighlight: false, - displayInFront: true, - visible: false - }); - proxyUIOverlay = Overlays.addOverlay("web3d", { - url: PROXY_UI_HTML, - parentID: proxyOverlay, - localPosition: Vec3.multiply(avatarScale, PROXY_UI_LOCAL_POSITION), - localRotation: PROXY_UI_LOCAL_ROTATION, - dimensions: Vec3.multiply(avatarScale, PROXY_UI_DIMENSIONS), - dpi: PROXY_UI_DPI / avatarScale, - alpha: 0, // Hide overlay while its content is being created. - grabbable: false, - showKeyboardFocusHighlight: false, - displayInFront: true, - visible: false - }); + UI = function () { - proxyUIOverlayEnabled = false; // This and alpha = 0 hides overlay while its content is being created. - - proxyOverlayObject = Overlays.getOverlayObject(proxyUIOverlay); - proxyOverlayObject.webEventReceived.connect(onWebEventReceived); - - // updateMiniTabletID(); Other scripts relying on this may not be ready yet so do this in showUI(). - } - - function showUI() { - var initialScale = 0.01; // Start very small. - - Overlays.editOverlay(proxyOverlay, { - parentID: MyAvatar.SELF_ID, - parentJointIndex: handJointIndex(proxyHand), - localPosition: Vec3.multiply(avatarScale, PROXY_POSITIONS[proxyHand]), - localRotation: PROXY_ROTATIONS[proxyHand], - dimensions: Vec3.multiply(initialScale, PROXY_DIMENSIONS), - visible: true - }); - Overlays.editOverlay(proxyUIOverlay, { - localPosition: Vec3.multiply(avatarScale, PROXY_UI_LOCAL_POSITION), - localRotation: PROXY_UI_LOCAL_ROTATION, - dimensions: Vec3.multiply(initialScale, PROXY_UI_DIMENSIONS), - dpi: PROXY_UI_DPI / initialScale, - visible: true - }); - - updateMiniTabletID(); - - if (!proxyUIOverlayEnabled) { - // Overlay content is created the first time it is visible to the user. The initial creation displays artefacts. - // Delay showing UI overlay until after giving it time for its content to be created. - Script.setTimeout(function () { - Overlays.editOverlay(proxyUIOverlay, { alpha: 1.0 }); - }, PROXY_UI_OVERLAY_ENABLED_DELAY); + if (!(this instanceof UI)) { + return new UI(); } - } - function sizeUI(scaleFactor) { - // Scale UI in place. - Overlays.editOverlay(proxyOverlay, { - dimensions: Vec3.multiply(scaleFactor, PROXY_DIMENSIONS) - }); - Overlays.editOverlay(proxyUIOverlay, { - localPosition: Vec3.multiply(scaleFactor, PROXY_UI_LOCAL_POSITION), - dimensions: Vec3.multiply(scaleFactor, PROXY_UI_DIMENSIONS), - dpi: PROXY_UI_DPI / scaleFactor - }); - } + var // Base overlay + miniOverlay = null, + MINI_MODEL = Script.resolvePath("./assets/models/tinyTablet.fbx"), + MINI_DIMENSIONS = { x: 0.0637, y: 0.0965, z: 0.0045 }, // Proportional to tablet proper. + MINI_POSITIONS = [ + { + x: -0.03, // Distance across hand. + y: 0.08, // Distance from joint. + z: 0.06 // Distance above palm. + }, + { + x: 0.03, // Distance across hand. + y: 0.08, // Distance from joint. + z: 0.06 // Distance above palm. + } + ], + DEGREES_180 = 180, + MINI_PICTH = 40, + MINI_ROTATIONS = [ + Quat.fromVec3Degrees({ x: 0, y: DEGREES_180 - MINI_PICTH, z: 90 }), + Quat.fromVec3Degrees({ x: 0, y: DEGREES_180 + MINI_PICTH, z: -90 }) + ], - function sizeUIAboutHandles(scaleFactor) { - // Scale UI and move per handles. - var tabletScaleFactor = avatarScale * (1 + scaleFactor * (proxyTargetWidth - proxyInitialWidth) / proxyInitialWidth); - var dimensions = Vec3.multiply(tabletScaleFactor, PROXY_DIMENSIONS); - var localRotation = Quat.mix(proxyExpandLocalRotation, proxyTargetLocalRotation, scaleFactor); - var localPosition = - Vec3.sum(proxyExpandLocalPosition, - Vec3.multiplyQbyV(proxyExpandLocalRotation, - Vec3.multiply(-tabletScaleFactor, - Vec3.multiplyVbyV(proxyExpandHandles[proxyExpandHand], PROXY_DIMENSIONS))) - ); - localPosition = Vec3.sum(localPosition, - Vec3.multiplyQbyV(proxyExpandLocalRotation, { x: 0, y: 0.5 * -dimensions.y, z: 0 })); - localPosition = Vec3.sum(localPosition, - Vec3.multiplyQbyV(localRotation, { x: 0, y: 0.5 * dimensions.y, z: 0 })); - Overlays.editOverlay(proxyOverlay, { - localPosition: localPosition, - localRotation: localRotation, - dimensions: dimensions - }); - Overlays.editOverlay(proxyUIOverlay, { - localPosition: Vec3.multiply(tabletScaleFactor, PROXY_UI_LOCAL_POSITION), - dimensions: Vec3.multiply(tabletScaleFactor, PROXY_UI_DIMENSIONS), - dpi: PROXY_UI_DPI / tabletScaleFactor - }); - } + // UI overlay. + uiHand = LEFT_HAND, + miniUIOverlay = null, + MINI_UI_HTML = Script.resolvePath("./html/miniTablet.html"), + MINI_UI_DIMENSIONS = { x: 0.059, y: 0.0865 }, + MINI_UI_WIDTH_PIXELS = 150, + METERS_TO_INCHES = 39.3701, + MINI_UI_DPI = MINI_UI_WIDTH_PIXELS / (MINI_UI_DIMENSIONS.x * METERS_TO_INCHES), + MINI_UI_OFFSET = 0.001, // Above model surface. + MINI_UI_LOCAL_POSITION = { x: 0.0002, y: 0.0024, z: -(MINI_DIMENSIONS.z / 2 + MINI_UI_OFFSET) }, + MINI_UI_LOCAL_ROTATION = Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 }), + miniUIOverlayEnabled = false, + MINI_UI_OVERLAY_ENABLED_DELAY = 500, + miniOverlayObject = null, - function hideUI() { - Overlays.editOverlay(proxyOverlay, { - parentID: Uuid.NULL, // Release hold so that hand can grab tablet proper. - visible: false - }); - Overlays.editOverlay(proxyUIOverlay, { - visible: false - }); - } + // Button icons. + MUTE_ON_ICON = Script.resourcesPath() + "icons/tablet-icons/mic-mute-a.svg", + MUTE_OFF_ICON = Script.resourcesPath() + "icons/tablet-icons/mic-unmute-i.svg", + BUBBLE_ON_ICON = Script.resourcesPath() + "icons/tablet-icons/bubble-a.svg", + BUBBLE_OFF_ICON = Script.resourcesPath() + "icons/tablet-icons/bubble-i.svg", + + // Expansion to tablet. + MINI_EXPAND_HANDLES = [ // Normalized coordinates in range [-0.5, 0.5] about center of mini tablet. + { x: 0.5, y: -0.65, z: 0 }, + { x: -0.5, y: -0.65, z: 0 } + ], + MINI_EXPAND_DELTA_ROTATION = Quat.fromVec3Degrees({ x: -5, y: 0, z: 0 }), + MINI_EXPAND_HANDLES_OTHER = [ // Different handles when expanding after being grabbed by other hand, + { x: 0.5, y: -0.4, z: 0 }, + { x: -0.5, y: -0.4, z: 0 } + ], + MINI_EXPAND_DELTA_ROTATION_OTHER = Quat.IDENTITY, + miniExpandHand, + miniExpandHandles = MINI_EXPAND_HANDLES, + miniExpandDeltaRotation = MINI_EXPAND_HANDLES_OTHER, + miniExpandLocalPosition, + miniExpandLocalRotation = Quat.IDENTITY, + miniInitialWidth, + miniTargetWidth, + miniTargetLocalRotation, + + // EventBridge + READY_MESSAGE = "ready", // Engine <== Dialog + HOVER_MESSAGE = "hover", // Engine <== Dialog + MUTE_MESSAGE = "mute", // Engine <=> Dialog + BUBBLE_MESSAGE = "bubble", // Engine <=> Dialog + EXPAND_MESSAGE = "expand", // Engine <== Dialog + + // Sounds + HOVER_SOUND = "./assets/sounds/button-hover.wav", + HOVER_VOLUME = 0.5, + CLICK_SOUND = "./assets/sounds/button-click.wav", + CLICK_VOLUME = 0.8, + hoverSound = SoundCache.getSound(Script.resolvePath(HOVER_SOUND)), + clickSound = SoundCache.getSound(Script.resolvePath(CLICK_SOUND)); + + + function updateMutedStatus() { + var isMuted = Audio.muted; + miniOverlayObject.emitScriptEvent(JSON.stringify({ + type: MUTE_MESSAGE, + on: isMuted, + icon: isMuted ? MUTE_ON_ICON : MUTE_OFF_ICON + })); + } + + function updateBubbleStatus() { + var isBubbleOn = Users.getIgnoreRadiusEnabled(); + miniOverlayObject.emitScriptEvent(JSON.stringify({ + type: BUBBLE_MESSAGE, + on: isBubbleOn, + icon: isBubbleOn ? BUBBLE_ON_ICON : BUBBLE_OFF_ICON + })); + } + + function onWebEventReceived(data) { + var message; + + try { + message = JSON.parse(data); + } catch (e) { + console.error("EventBridge message error"); + return; + } + + switch (message.type) { + case READY_MESSAGE: + // Send initial button statuses. + updateMutedStatus(); + updateBubbleStatus(); + break; + case HOVER_MESSAGE: + // Audio feedback. + playSound(hoverSound, HOVER_VOLUME); + break; + case MUTE_MESSAGE: + // Toggle mute. + playSound(clickSound, CLICK_VOLUME); + Audio.muted = !Audio.muted; + break; + case BUBBLE_MESSAGE: + // Toggle bubble. + playSound(clickSound, CLICK_VOLUME); + Users.toggleIgnoreRadius(); + break; + case EXPAND_MESSAGE: + // Expand tablet; + playSound(clickSound, CLICK_VOLUME); + miniState.setState(miniState.MINI_EXPANDING, uiHand); + break; + } + } + + + function updateMiniTabletID() { + // Send mini-tablet overlay ID to controllerDispatcher so that it can use a smaller near grab distance. + Messages.sendLocalMessage("Hifi-MiniTablet-ID", miniOverlay); + // Send mini-tablet UI overlay ID to stylusInput so that it styluses can be used on it. + Messages.sendLocalMessage("Hifi-MiniTablet-UI-ID", miniUIOverlay); + } + + function playSound(sound, volume) { + Audio.playSound(sound, { + position: uiHand === LEFT_HAND ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(), + volume: volume, + localOnly: true + }); + } + + + function getUIPositionAndRotation(hand) { + return { + position: MINI_POSITIONS[hand], + rotation: MINI_ROTATIONS[hand] + }; + } + + function getMiniTabletID() { + return miniOverlay; + } + + function getMiniTabletProperties() { + var properties = Overlays.getProperties(miniOverlay, ["position", "orientation"]); + return { + position: properties.position, + orientation: properties.orientation + }; + } + + + function show(hand) { + var initialScale = 0.01; // Start very small. + + uiHand = hand; + + Overlays.editOverlay(miniOverlay, { + parentID: MyAvatar.SELF_ID, + parentJointIndex: handJointIndex(hand), + localPosition: Vec3.multiply(avatarScale, MINI_POSITIONS[hand]), + localRotation: MINI_ROTATIONS[hand], + dimensions: Vec3.multiply(initialScale, MINI_DIMENSIONS), + visible: true + }); + Overlays.editOverlay(miniUIOverlay, { + localPosition: Vec3.multiply(avatarScale, MINI_UI_LOCAL_POSITION), + localRotation: MINI_UI_LOCAL_ROTATION, + dimensions: Vec3.multiply(initialScale, MINI_UI_DIMENSIONS), + dpi: MINI_UI_DPI / initialScale, + visible: true + }); - function destroyUI() { - if (proxyOverlayObject) { - proxyOverlayObject.webEventReceived.disconnect(onWebEventReceived); - Overlays.deleteOverlay(proxyUIOverlay); - Overlays.deleteOverlay(proxyOverlay); - proxyOverlayObject = null; - proxyUIOverlay = null; - proxyOverlay = null; updateMiniTabletID(); + + if (!miniUIOverlayEnabled) { + // Overlay content is created the first time it is visible to the user. The initial creation displays artefacts. + // Delay showing UI overlay until after giving it time for its content to be created. + Script.setTimeout(function () { + Overlays.editOverlay(miniUIOverlay, { alpha: 1.0 }); + }, MINI_UI_OVERLAY_ENABLED_DELAY); + } } - } + + function size(scaleFactor) { + // Scale UI in place. + Overlays.editOverlay(miniOverlay, { + dimensions: Vec3.multiply(scaleFactor, MINI_DIMENSIONS) + }); + Overlays.editOverlay(miniUIOverlay, { + localPosition: Vec3.multiply(scaleFactor, MINI_UI_LOCAL_POSITION), + dimensions: Vec3.multiply(scaleFactor, MINI_UI_DIMENSIONS), + dpi: MINI_UI_DPI / scaleFactor + }); + } + + function startExpandingTablet(hand) { + // Expansion details. + if (hand === uiHand) { + miniExpandHandles = MINI_EXPAND_HANDLES; + miniExpandDeltaRotation = MINI_EXPAND_DELTA_ROTATION; + } else { + miniExpandHandles = MINI_EXPAND_HANDLES_OTHER; + miniExpandDeltaRotation = MINI_EXPAND_DELTA_ROTATION_OTHER; + } + + // Grab details. + var properties = Overlays.getProperties(miniOverlay, ["localPosition", "localRotation"]); + miniExpandHand = hand; + miniExpandLocalRotation = properties.localRotation; + miniExpandLocalPosition = Vec3.sum(properties.localPosition, + Vec3.multiplyQbyV(miniExpandLocalRotation, + Vec3.multiplyVbyV(miniExpandHandles[miniExpandHand], MINI_DIMENSIONS))); + + // Start expanding. + miniInitialWidth = MINI_DIMENSIONS.x; + miniTargetWidth = getTabletWidthFromSettings(); + miniTargetLocalRotation = Quat.multiply(miniExpandLocalRotation, miniExpandDeltaRotation); + } + + function sizeAboutHandles(scaleFactor) { + // Scale UI and move per handles. + var tabletScaleFactor, + dimensions, + localRotation, + localPosition; + + tabletScaleFactor = avatarScale * (1 + scaleFactor * (miniTargetWidth - miniInitialWidth) / miniInitialWidth); + dimensions = Vec3.multiply(tabletScaleFactor, MINI_DIMENSIONS); + localRotation = Quat.mix(miniExpandLocalRotation, miniTargetLocalRotation, scaleFactor); + localPosition = + Vec3.sum(miniExpandLocalPosition, + Vec3.multiplyQbyV(miniExpandLocalRotation, + Vec3.multiply(-tabletScaleFactor, + Vec3.multiplyVbyV(miniExpandHandles[miniExpandHand], MINI_DIMENSIONS))) + ); + localPosition = Vec3.sum(localPosition, + Vec3.multiplyQbyV(miniExpandLocalRotation, { x: 0, y: 0.5 * -dimensions.y, z: 0 })); + localPosition = Vec3.sum(localPosition, + Vec3.multiplyQbyV(localRotation, { x: 0, y: 0.5 * dimensions.y, z: 0 })); + Overlays.editOverlay(miniOverlay, { + localPosition: localPosition, + localRotation: localRotation, + dimensions: dimensions + }); + Overlays.editOverlay(miniUIOverlay, { + localPosition: Vec3.multiply(tabletScaleFactor, MINI_UI_LOCAL_POSITION), + dimensions: Vec3.multiply(tabletScaleFactor, MINI_UI_DIMENSIONS), + dpi: MINI_UI_DPI / tabletScaleFactor + }); + } + + function hide() { + Overlays.editOverlay(miniOverlay, { + parentID: Uuid.NULL, // Release hold so that hand can grab tablet proper. + visible: false + }); + Overlays.editOverlay(miniUIOverlay, { + visible: false + }); + } + + function create() { + miniOverlay = Overlays.addOverlay("model", { + url: MINI_MODEL, + dimensions: Vec3.multiply(avatarScale, MINI_DIMENSIONS), + solid: true, + grabbable: true, + showKeyboardFocusHighlight: false, + displayInFront: true, + visible: false + }); + miniUIOverlay = Overlays.addOverlay("web3d", { + url: MINI_UI_HTML, + parentID: miniOverlay, + localPosition: Vec3.multiply(avatarScale, MINI_UI_LOCAL_POSITION), + localRotation: MINI_UI_LOCAL_ROTATION, + dimensions: Vec3.multiply(avatarScale, MINI_UI_DIMENSIONS), + dpi: MINI_UI_DPI / avatarScale, + alpha: 0, // Hide overlay while its content is being created. + grabbable: false, + showKeyboardFocusHighlight: false, + displayInFront: true, + visible: false + }); + + miniUIOverlayEnabled = false; // This and alpha = 0 hides overlay while its content is being created. + + miniOverlayObject = Overlays.getOverlayObject(miniUIOverlay); + miniOverlayObject.webEventReceived.connect(onWebEventReceived); + + // updateMiniTabletID(); Other scripts relying on this may not be ready yet so do this in showUI(). + } + + function destroy() { + if (miniOverlayObject) { + miniOverlayObject.webEventReceived.disconnect(onWebEventReceived); + Overlays.deleteOverlay(miniUIOverlay); + Overlays.deleteOverlay(miniOverlay); + miniOverlayObject = null; + miniUIOverlay = null; + miniOverlay = null; + updateMiniTabletID(); + } + } + + create(); + + return { + getUIPositionAndRotation: getUIPositionAndRotation, + getMiniTabletID: getMiniTabletID, + getMiniTabletProperties: getMiniTabletProperties, + updateMutedStatus: updateMutedStatus, + updateBubbleStatus: updateBubbleStatus, + show: show, + size: size, + startExpandingTablet: startExpandingTablet, + sizeAboutHandles: sizeAboutHandles, + hide: hide, + destroy: destroy + }; + + }; // #endregion // #region State Machine =================================================================================================== - function enterProxyDisabled() { - // Stop updates. - if (updateTimer !== null) { - Script.clearTimeout(updateTimer); - updateTimer = null; + State = function () { + + if (!(this instanceof State)) { + return new State(); } - // Stop monitoring mute and bubble changes. - Audio.mutedChanged.disconnect(updateMutedStatus); - Users.ignoreRadiusEnabledChanged.disconnect(updateBubbleStatus); + var + // States. + MINI_DISABLED = 0, + MINI_HIDDEN = 1, + MINI_HIDING = 2, + MINI_SHOWING = 3, + MINI_VISIBLE = 4, + MINI_GRABBED = 5, + MINI_EXPANDING = 6, + TABLET_OPEN = 7, + STATE_STRINGS = ["MINI_DISABLED", "MINI_HIDDEN", "MINI_HIDING", "MINI_SHOWING", "MINI_VISIBLE", "MINI_GRABBED", + "MINI_EXPANDING", "TABLET_OPEN"], + STATE_MACHINE, + miniState = MINI_DISABLED, + miniHand, + updateTimer = null, + UPDATE_INTERVAL = 300, - // Don't keep overlays prepared if in desktop mode. - destroyUI(); - } + // Mini tablet scaling. + MINI_SCALE_DURATION = 150, + MINI_SCALE_TIMEOUT = 20, + miniScaleTimer = null, + miniScaleStart, - function exitProxyDisabled() { - // Create UI so that it's ready to be displayed without seeing artefacts from creating the UI. - createUI(); + // Expansion to tablet. + MINI_EXPAND_DURATION = 250, + MINI_EXPAND_TIMEOUT = 20, + miniExpandTimer = null, + miniExpandStart, - // Start monitoring mute and bubble changes. - Audio.mutedChanged.connect(updateMutedStatus); - Users.ignoreRadiusEnabledChanged.connect(updateBubbleStatus); + // Visibility. + MIN_HAND_CAMERA_ANGLE = 30, + DEGREES_180 = 180, + MIN_HAND_CAMERA_ANGLE_COS = Math.cos(Math.PI * MIN_HAND_CAMERA_ANGLE / DEGREES_180); - // Start updates. - updateTimer = Script.setTimeout(updateState, UPDATE_INTERVAL); - } - function shouldShowProxy(hand) { - // Should show proxy if it would be oriented toward the camera. - var pose, - jointIndex, - handPosition, - handOrientation, - proxyPosition, - proxyOrientation, - proxyToCameraDirection; + function enterMiniDisabled() { + // Stop updates. + if (updateTimer !== null) { + Script.clearTimeout(updateTimer); + updateTimer = null; + } - pose = Controller.getPoseValue(hand === LEFT_HAND ? Controller.Standard.LeftHand : Controller.Standard.RightHand); - if (!pose.valid) { - return false; + // Stop monitoring mute and bubble changes. + Audio.mutedChanged.disconnect(ui.updateMutedStatus); + Users.ignoreRadiusEnabledChanged.disconnect(ui.updateBubbleStatus); + + // Don't keep overlays prepared if in desktop mode. + ui.destroy(); + ui = null; } - jointIndex = handJointIndex(hand); - handPosition = Vec3.sum(MyAvatar.position, - Vec3.multiplyQbyV(MyAvatar.orientation, MyAvatar.getAbsoluteJointTranslationInObjectFrame(jointIndex))); - handOrientation = Quat.multiply(MyAvatar.orientation, MyAvatar.getAbsoluteJointRotationInObjectFrame(jointIndex)); - proxyPosition = Vec3.sum(handPosition, Vec3.multiply(avatarScale, - Vec3.multiplyQbyV(handOrientation, PROXY_POSITIONS[hand]))); - proxyOrientation = Quat.multiply(handOrientation, PROXY_ROTATIONS[hand]); - proxyToCameraDirection = Vec3.normalize(Vec3.subtract(Camera.position, proxyPosition)); - return Vec3.dot(proxyToCameraDirection, Quat.getForward(proxyOrientation)) > MIN_HAND_CAMERA_ANGLE_COS; - } + function exitMiniDisabled() { + // Create UI so that it's ready to be displayed without seeing artefacts from creating the UI. + ui = new UI(); - function enterProxyHidden() { - hideUI(); - } + // Start monitoring mute and bubble changes. + Audio.mutedChanged.connect(ui.updateMutedStatus); + Users.ignoreRadiusEnabledChanged.connect(ui.updateBubbleStatus); - function updateProxyHidden() { - // Don't show proxy if tablet is already displayed or in toolbar mode. - if (HMD.showTablet || tablet.toolbarMode) { - return; - } - // Compare palm directions of hands with vectors from palms to camera. - if (shouldShowProxy(LEFT_HAND)) { - setState(PROXY_SHOWING, LEFT_HAND); - } else if (shouldShowProxy(RIGHT_HAND)) { - setState(PROXY_SHOWING, RIGHT_HAND); - } - } - - function scaleProxyDown() { - var scaleFactor = (Date.now() - proxyScaleStart) / PROXY_SCALE_DURATION; - if (scaleFactor < 1) { - sizeUI((1 - scaleFactor) * avatarScale); - proxyScaleTimer = Script.setTimeout(scaleProxyDown, PROXY_SCALE_TIMEOUT); - return; - } - proxyScaleTimer = null; - setState(PROXY_HIDDEN); - } - - function enterProxyHiding() { - proxyScaleStart = Date.now(); - proxyScaleTimer = Script.setTimeout(scaleProxyDown, PROXY_SCALE_TIMEOUT); - } - - function updateProxyHiding() { - if (HMD.showTablet) { - setState(PROXY_HIDDEN); - } - } - - function exitProxyHiding() { - if (proxyScaleTimer) { - Script.clearTimeout(proxyScaleTimer); - proxyScaleTimer = null; - } - } - - function scaleProxyUp() { - var scaleFactor = (Date.now() - proxyScaleStart) / PROXY_SCALE_DURATION; - if (scaleFactor < 1) { - sizeUI(scaleFactor * avatarScale); - proxyScaleTimer = Script.setTimeout(scaleProxyUp, PROXY_SCALE_TIMEOUT); - return; - } - proxyScaleTimer = null; - sizeUI(avatarScale); - setState(PROXY_VISIBLE); - } - - function enterProxyShowing(hand) { - proxyHand = hand; - showUI(); - proxyScaleStart = Date.now(); - proxyScaleTimer = Script.setTimeout(scaleProxyUp, PROXY_SCALE_TIMEOUT); - } - - function updateProxyShowing() { - if (HMD.showTablet) { - setState(PROXY_HIDDEN); - } - } - - function exitProxyShowing() { - if (proxyScaleTimer) { - Script.clearTimeout(proxyScaleTimer); - proxyScaleTimer = null; - } - } - - function updateProxyVisible() { - // Hide proxy if tablet has been displayed by other means. - if (HMD.showTablet) { - setState(PROXY_HIDDEN); - return; - } - // Check that palm direction of proxy hand still less than maximum angle. - if (!shouldShowProxy(proxyHand)) { - setState(PROXY_HIDING); - } - } - - function updateProxyGrabbed() { - // Hide proxy if tablet has been displayed by other means. - if (HMD.showTablet) { - setState(PROXY_HIDDEN); - } - } - - function expandProxy() { - var scaleFactor = (Date.now() - proxyExpandStart) / PROXY_EXPAND_DURATION; - if (scaleFactor < 1) { - sizeUIAboutHandles(scaleFactor); - proxyExpandTimer = Script.setTimeout(expandProxy, PROXY_EXPAND_TIMEOUT); - return; - } - proxyExpandTimer = null; - setState(TABLET_OPEN); - } - - function enterProxyExpanding(hand) { - // Expansion details. - if (hand === proxyHand) { - proxyExpandHandles = PROXY_EXPAND_HANDLES; - proxyExpandDeltaRotation = PROXY_EXPAND_DELTA_ROTATION; - } else { - proxyExpandHandles = PROXY_EXPAND_HANDLES_OTHER; - proxyExpandDeltaRotation = PROXY_EXPAND_DELTA_ROTATION_OTHER; + // Start updates. + updateTimer = Script.setTimeout(updateState, UPDATE_INTERVAL); } - // Grab details. - var properties = Overlays.getProperties(proxyOverlay, ["localPosition", "localRotation"]); - proxyExpandHand = hand; - proxyExpandLocalRotation = properties.localRotation; - proxyExpandLocalPosition = Vec3.sum(properties.localPosition, - Vec3.multiplyQbyV(proxyExpandLocalRotation, - Vec3.multiplyVbyV(proxyExpandHandles[proxyExpandHand], PROXY_DIMENSIONS))); + function shouldShowMini(hand) { + // Should show mini tablet if it would be oriented toward the camera. + var pose, + jointIndex, + handPosition, + handOrientation, + uiPositionAndOrientation, + miniPosition, + miniOrientation, + miniToCameraDirection; - // Start expanding. - proxyInitialWidth = PROXY_DIMENSIONS.x; - proxyTargetWidth = getTabletWidthFromSettings(); - proxyTargetLocalRotation = Quat.multiply(proxyExpandLocalRotation, proxyExpandDeltaRotation); - proxyExpandStart = Date.now(); - proxyExpandTimer = Script.setTimeout(expandProxy, PROXY_EXPAND_TIMEOUT); - } + pose = Controller.getPoseValue(hand === LEFT_HAND ? Controller.Standard.LeftHand : Controller.Standard.RightHand); + if (!pose.valid) { + return false; + } - function updateProxyExanding() { - // Hide proxy immediately if tablet has been displayed by other means. - if (HMD.showTablet) { - setState(PROXY_HIDDEN); + jointIndex = handJointIndex(hand); + handPosition = Vec3.sum(MyAvatar.position, + Vec3.multiplyQbyV(MyAvatar.orientation, MyAvatar.getAbsoluteJointTranslationInObjectFrame(jointIndex))); + handOrientation = Quat.multiply(MyAvatar.orientation, MyAvatar.getAbsoluteJointRotationInObjectFrame(jointIndex)); + uiPositionAndOrientation = ui.getUIPositionAndRotation(hand); + miniPosition = Vec3.sum(handPosition, Vec3.multiply(avatarScale, + Vec3.multiplyQbyV(handOrientation, uiPositionAndOrientation.position))); + miniOrientation = Quat.multiply(handOrientation, uiPositionAndOrientation.rotation); + miniToCameraDirection = Vec3.normalize(Vec3.subtract(Camera.position, miniPosition)); + return Vec3.dot(miniToCameraDirection, Quat.getForward(miniOrientation)) > MIN_HAND_CAMERA_ANGLE_COS; } - } - function exitProxyExpanding() { - if (proxyExpandTimer !== null) { - Script.clearTimeout(proxyExpandTimer); - proxyExpandTimer = null; + function enterMiniHidden() { + ui.hide(); } - } - function enterTabletOpen() { - var proxyOverlayProperties = Overlays.getProperties(proxyOverlay, ["position", "orientation"]); - - hideUI(); - - Overlays.editOverlay(HMD.tabletID, { - position: proxyOverlayProperties.position, - orientation: proxyOverlayProperties.orientation - }); - HMD.openTablet(true); - } - - function updateTabletOpen() { - // Immediately transition back to PROXY_HIDDEN. - setState(PROXY_HIDDEN); - } - - STATE_MACHINE = { - PROXY_DISABLED: { // Tablet proxy cannot be shown because in desktop mode. - enter: enterProxyDisabled, - update: null, - exit: exitProxyDisabled - }, - PROXY_HIDDEN: { // Tablet proxy could be shown but isn't because hand is oriented to show it or aren't in HMD mode. - enter: enterProxyHidden, - update: updateProxyHidden, - exit: null - }, - PROXY_HIDING: { // Tablet proxy is reducing from PROXY_VISIBLE to PROXY_HIDDEN. - enter: enterProxyHiding, - update: updateProxyHiding, - exit: exitProxyHiding - }, - PROXY_SHOWING: { // Tablet proxy is expanding from PROXY_HIDDN to PROXY_VISIBLE. - enter: enterProxyShowing, - update: updateProxyShowing, - exit: exitProxyShowing - }, - PROXY_VISIBLE: { // Tablet proxy is visible and attached to hand. - enter: null, - update: updateProxyVisible, - exit: null - }, - PROXY_GRABBED: { // Tablet proxy is grabbed by other hand. - enter: null, - update: updateProxyGrabbed, - exit: null - }, - PROXY_EXPANDING: { // Tablet proxy is expanding before showing tablet proper. - enter: enterProxyExpanding, - update: updateProxyExanding, - exit: exitProxyExpanding - }, - TABLET_OPEN: { // Tablet proper is being displayed. - enter: enterTabletOpen, - update: updateTabletOpen, - exit: null + function updateMiniHidden() { + // Don't show mini tablet if tablet proper is already displayed or in toolbar mode. + if (HMD.showTablet || tablet.toolbarMode) { + return; + } + // Compare palm directions of hands with vectors from palms to camera. + if (shouldShowMini(LEFT_HAND)) { + setState(MINI_SHOWING, LEFT_HAND); + } else if (shouldShowMini(RIGHT_HAND)) { + setState(MINI_SHOWING, RIGHT_HAND); + } } + + function scaleMiniDown() { + var scaleFactor = (Date.now() - miniScaleStart) / MINI_SCALE_DURATION; + if (scaleFactor < 1) { + ui.size((1 - scaleFactor) * avatarScale); + miniScaleTimer = Script.setTimeout(scaleMiniDown, MINI_SCALE_TIMEOUT); + return; + } + miniScaleTimer = null; + setState(MINI_HIDDEN); + } + + function enterMiniHiding() { + miniScaleStart = Date.now(); + miniScaleTimer = Script.setTimeout(scaleMiniDown, MINI_SCALE_TIMEOUT); + } + + function updateMiniHiding() { + if (HMD.showTablet) { + setState(MINI_HIDDEN); + } + } + + function exitMiniHiding() { + if (miniScaleTimer) { + Script.clearTimeout(miniScaleTimer); + miniScaleTimer = null; + } + } + + function scaleMiniUp() { + var scaleFactor = (Date.now() - miniScaleStart) / MINI_SCALE_DURATION; + if (scaleFactor < 1) { + ui.size(scaleFactor * avatarScale); + miniScaleTimer = Script.setTimeout(scaleMiniUp, MINI_SCALE_TIMEOUT); + return; + } + miniScaleTimer = null; + ui.size(avatarScale); + setState(MINI_VISIBLE); + } + + function enterMiniShowing(hand) { + miniHand = hand; + ui.show(miniHand); + miniScaleStart = Date.now(); + miniScaleTimer = Script.setTimeout(scaleMiniUp, MINI_SCALE_TIMEOUT); + } + + function updateMiniShowing() { + if (HMD.showTablet) { + setState(MINI_HIDDEN); + } + } + + function exitMiniShowing() { + if (miniScaleTimer) { + Script.clearTimeout(miniScaleTimer); + miniScaleTimer = null; + } + } + + function updateMiniVisible() { + // Hide mini tablet if tablet proper has been displayed by other means. + if (HMD.showTablet) { + setState(MINI_HIDDEN); + return; + } + // Check that palm direction of mini tablet hand still less than maximum angle. + if (!shouldShowMini(miniHand)) { + setState(MINI_HIDING); + } + } + + function updateMiniGrabbed() { + // Hide mini tablet if tablet proper has been displayed by other means. + if (HMD.showTablet) { + setState(MINI_HIDDEN); + } + } + + function expandMini() { + var scaleFactor = (Date.now() - miniExpandStart) / MINI_EXPAND_DURATION; + if (scaleFactor < 1) { + ui.sizeAboutHandles(scaleFactor); + miniExpandTimer = Script.setTimeout(expandMini, MINI_EXPAND_TIMEOUT); + return; + } + miniExpandTimer = null; + setState(TABLET_OPEN); + } + + function enterMiniExpanding(hand) { + ui.startExpandingTablet(hand); + miniExpandStart = Date.now(); + miniExpandTimer = Script.setTimeout(expandMini, MINI_EXPAND_TIMEOUT); + } + + function updateMiniExanding() { + // Hide mini tablet immediately if tablet proper has been displayed by other means. + if (HMD.showTablet) { + setState(MINI_HIDDEN); + } + } + + function exitMiniExpanding() { + if (miniExpandTimer !== null) { + Script.clearTimeout(miniExpandTimer); + miniExpandTimer = null; + } + } + + function enterTabletOpen() { + var miniTabletProperties = ui.getMiniTabletProperties(); + + ui.hide(); + + Overlays.editOverlay(HMD.tabletID, { + position: miniTabletProperties.position, + orientation: miniTabletProperties.orientation + }); + HMD.openTablet(true); + } + + function updateTabletOpen() { + // Immediately transition back to MINI_HIDDEN. + setState(MINI_HIDDEN); + } + + STATE_MACHINE = { + MINI_DISABLED: { // Mini tablet cannot be shown because in desktop mode. + enter: enterMiniDisabled, + update: null, + exit: exitMiniDisabled + }, + MINI_HIDDEN: { // Mini tablet could be shown but isn't because hand is oriented to show it or aren't in HMD mode. + enter: enterMiniHidden, + update: updateMiniHidden, + exit: null + }, + MINI_HIDING: { // Mini tablet is reducing from MINI_VISIBLE to MINI_HIDDEN. + enter: enterMiniHiding, + update: updateMiniHiding, + exit: exitMiniHiding + }, + MINI_SHOWING: { // Mini tablet is expanding from MINI_HIDDN to MINI_VISIBLE. + enter: enterMiniShowing, + update: updateMiniShowing, + exit: exitMiniShowing + }, + MINI_VISIBLE: { // Mini tablet is visible and attached to hand. + enter: null, + update: updateMiniVisible, + exit: null + }, + MINI_GRABBED: { // Mini tablet is grabbed by other hand. + enter: null, + update: updateMiniGrabbed, + exit: null + }, + MINI_EXPANDING: { // Mini tablet is expanding before showing tablet proper. + enter: enterMiniExpanding, + update: updateMiniExanding, + exit: exitMiniExpanding + }, + TABLET_OPEN: { // Tablet proper is being displayed. + enter: enterTabletOpen, + update: updateTabletOpen, + exit: null + } + }; + + function setState(state, data) { + if (state !== miniState) { + debug("State transition from " + STATE_STRINGS[miniState] + " to " + STATE_STRINGS[state]); + if (STATE_MACHINE[STATE_STRINGS[miniState]].exit) { + STATE_MACHINE[STATE_STRINGS[miniState]].exit(data); + } + if (STATE_MACHINE[STATE_STRINGS[state]].enter) { + STATE_MACHINE[STATE_STRINGS[state]].enter(data); + } + miniState = state; + } else { + error("Null state transition: " + state + "!"); + } + } + + function getState() { + return miniState; + } + + function getHand() { + return miniHand; + } + + function updateState() { + if (STATE_MACHINE[STATE_STRINGS[miniState]].update) { + STATE_MACHINE[STATE_STRINGS[miniState]].update(); + } + updateTimer = Script.setTimeout(updateState, UPDATE_INTERVAL); + } + + function create() { + // Nothing to do. + } + + function destroy() { + if (miniState !== MINI_DISABLED) { + setState(MINI_DISABLED); + } + } + + create(); + + return { + MINI_DISABLED: MINI_DISABLED, + MINI_HIDDEN: MINI_HIDDEN, + MINI_HIDING: MINI_HIDING, + MINI_SHOWING: MINI_SHOWING, + MINI_VISIBLE: MINI_VISIBLE, + MINI_GRABBED: MINI_GRABBED, + MINI_EXPANDING: MINI_EXPANDING, + TABLET_OPEN: TABLET_OPEN, + updateState: updateState, + setState: setState, + getState: getState, + getHand: getHand, + destroy: destroy + }; }; - function setState(state, data) { - if (state !== rezzerState) { - debug("State transition from " + STATE_STRINGS[rezzerState] + " to " + STATE_STRINGS[state]); - if (STATE_MACHINE[STATE_STRINGS[rezzerState]].exit) { - STATE_MACHINE[STATE_STRINGS[rezzerState]].exit(data); - } - if (STATE_MACHINE[STATE_STRINGS[state]].enter) { - STATE_MACHINE[STATE_STRINGS[state]].enter(data); - } - rezzerState = state; - } else { - error("Null state transition: " + state + "!"); - } - } - - function updateState() { - if (STATE_MACHINE[STATE_STRINGS[rezzerState]].update) { - STATE_MACHINE[STATE_STRINGS[rezzerState]].update(); - } - updateTimer = Script.setTimeout(updateState, UPDATE_INTERVAL); - } - // #endregion - // #region Events ========================================================================================================== + // #region External Events ================================================================================================= function onScaleChanged() { avatarScale = MyAvatar.scale; @@ -673,44 +790,49 @@ } function onMessageReceived(channel, data, senderID, localOnly) { - var message, hand; + var message, + miniHand, + hand; if (channel !== HIFI_OBJECT_MANIPULATION_CHANNEL) { return; } message = JSON.parse(data); - if (message.grabbedEntity !== proxyOverlay) { + if (message.grabbedEntity !== ui.getMiniTabletID()) { return; } - if (message.action === "grab" && rezzerState === PROXY_VISIBLE) { - hand = message.joint === HAND_NAMES[proxyHand] ? proxyHand : otherHand(proxyHand); - if (hand === proxyHand) { - setState(PROXY_EXPANDING, hand); + miniHand = miniState.getHand(); + if (message.action === "grab" && miniState.getState() === miniState.MINI_VISIBLE) { + hand = message.joint === HAND_NAMES[miniHand] ? miniHand : otherHand(miniHand); + if (hand === miniHand) { + miniState.setState(miniState.MINI_EXPANDING, hand); } else { - setState(PROXY_GRABBED); + miniState.setState(miniState.MINI_GRABBED); } - } else if (message.action === "release" && rezzerState === PROXY_GRABBED) { - hand = message.joint === HAND_NAMES[proxyHand] ? proxyHand : otherHand(proxyHand); - setState(PROXY_EXPANDING, hand); + } else if (message.action === "release" && miniState.getState() === miniState.MINI_GRABBED) { + hand = message.joint === HAND_NAMES[miniHand] ? miniHand : otherHand(miniHand); + miniState.setState(miniState.MINI_EXPANDING, hand); } } function onDisplayModeChanged() { - // Tablet proxy only available when HMD is active. + // Mini tablet only available when HMD is active. if (HMD.active) { - setState(PROXY_HIDDEN); + miniState.setState(miniState.MINI_HIDDEN); } else { - setState(PROXY_DISABLED); + miniState.setState(miniState.MINI_DISABLED); } } // #endregion - // #region Start-up and tear-down ========================================================================================== + // #region Set-up and tear-down ============================================================================================ function setUp() { + miniState = new State(); + MyAvatar.scaleChanged.connect(onScaleChanged); Messages.subscribe(HIFI_OBJECT_MANIPULATION_CHANNEL); @@ -718,17 +840,12 @@ HMD.displayModeChanged.connect(onDisplayModeChanged); if (HMD.active) { - setState(PROXY_HIDDEN); + miniState.setState(miniState.MINI_HIDDEN); } } function tearDown() { - if (updateTimer !== null) { - Script.clearTimeout(updateTimer); - updateTimer = null; - } - - setState(PROXY_DISABLED); + miniState.setState(miniState.MINI_DISABLED); HMD.displayModeChanged.disconnect(onDisplayModeChanged); @@ -736,6 +853,9 @@ Messages.unsubscribe(HIFI_OBJECT_MANIPULATION_CHANNEL); MyAvatar.scaleChanged.disconnect(onScaleChanged); + + miniState.destroy(); + miniState = null; } setUp(); From f3cbca6cee95ebb24de5309ff73bc8a2d3687c5a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 7 Sep 2018 16:43:21 +1200 Subject: [PATCH 045/259] Change "bubble" button to "goto" --- .../system/html/css/img/mt-bubble-a-hover.svg | 5 -- .../html/css/img/mt-bubble-a-normal.svg | 5 -- ...t-bubble-i-hover.svg => mt-goto-hover.svg} | 0 ...bubble-i-normal.svg => mt-goto-normal.svg} | 0 scripts/system/html/css/miniTablet.css | 18 ++----- scripts/system/html/js/miniTablet.js | 24 ++++----- scripts/system/html/miniTablet.html | 4 +- scripts/system/miniTablet.js | 54 ++++++++++--------- 8 files changed, 48 insertions(+), 62 deletions(-) delete mode 100644 scripts/system/html/css/img/mt-bubble-a-hover.svg delete mode 100644 scripts/system/html/css/img/mt-bubble-a-normal.svg rename scripts/system/html/css/img/{mt-bubble-i-hover.svg => mt-goto-hover.svg} (100%) rename scripts/system/html/css/img/{mt-bubble-i-normal.svg => mt-goto-normal.svg} (100%) diff --git a/scripts/system/html/css/img/mt-bubble-a-hover.svg b/scripts/system/html/css/img/mt-bubble-a-hover.svg deleted file mode 100644 index 88ddfff808..0000000000 --- a/scripts/system/html/css/img/mt-bubble-a-hover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/scripts/system/html/css/img/mt-bubble-a-normal.svg b/scripts/system/html/css/img/mt-bubble-a-normal.svg deleted file mode 100644 index 3d9d0a1286..0000000000 --- a/scripts/system/html/css/img/mt-bubble-a-normal.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/scripts/system/html/css/img/mt-bubble-i-hover.svg b/scripts/system/html/css/img/mt-goto-hover.svg similarity index 100% rename from scripts/system/html/css/img/mt-bubble-i-hover.svg rename to scripts/system/html/css/img/mt-goto-hover.svg diff --git a/scripts/system/html/css/img/mt-bubble-i-normal.svg b/scripts/system/html/css/img/mt-goto-normal.svg similarity index 100% rename from scripts/system/html/css/img/mt-bubble-i-normal.svg rename to scripts/system/html/css/img/mt-goto-normal.svg diff --git a/scripts/system/html/css/miniTablet.css b/scripts/system/html/css/miniTablet.css index 3b01a45613..d5a12aae9c 100644 --- a/scripts/system/html/css/miniTablet.css +++ b/scripts/system/html/css/miniTablet.css @@ -57,25 +57,17 @@ img { background-image: url("./img/mt-mute-hover.svg"); } -#bubble { +#goto { padding-top: 19px; background-size: 100% 100%; } - #bubble.off { - background-image: url("./img/mt-bubble-i-normal.svg"); + #goto.off { + background-image: url("./img/mt-goto-normal.svg"); } - #bubble.off:hover { - background-image: url("./img/mt-bubble-i-hover.svg"); - } - - #bubble.on { - background-image: url("./img/mt-bubble-a-normal.svg"); - } - - #bubble.on:hover { - background-image: url("./img/mt-bubble-a-hover.svg"); + #goto.off:hover { + background-image: url("./img/mt-goto-hover.svg"); } #expand { diff --git a/scripts/system/html/js/miniTablet.js b/scripts/system/html/js/miniTablet.js index ab1790cf00..f21dc7a6e6 100644 --- a/scripts/system/html/js/miniTablet.js +++ b/scripts/system/html/js/miniTablet.js @@ -19,13 +19,13 @@ READY_MESSAGE = "ready", // Engine <== Dialog HOVER_MESSAGE = "hover", // Engine <== Dialog MUTE_MESSAGE = "mute", // Engine <=> Dialog - BUBBLE_MESSAGE = "bubble", // Engine <=> Dialog + GOTO_MESSAGE = "goto", // Engine <=> Dialog EXPAND_MESSAGE = "expand", // Engine <== Dialog muteButton, muteImage, - bubbleButton, - bubbleImage, + gotoButton, + gotoImage, expandButton; // #region Communications ================================================================================================== @@ -44,10 +44,8 @@ case MUTE_MESSAGE: muteImage.src = message.icon; break; - case BUBBLE_MESSAGE: - bubbleButton.classList.remove(message.on ? "off" : "on"); - bubbleButton.classList.add(message.on ? "on" : "off"); - bubbleImage.src = message.icon; + case GOTO_MESSAGE: + gotoImage.src = message.icon; break; } } @@ -64,9 +62,9 @@ })); } - function onBubbleButtonClick() { + function onGotoButtonClick() { EventBridge.emitWebEvent(JSON.stringify({ - type: BUBBLE_MESSAGE + type: GOTO_MESSAGE })); } @@ -98,17 +96,17 @@ function onLoad() { muteButton = document.getElementById("mute"); muteImage = document.getElementById("mute-img"); - bubbleButton = document.getElementById("bubble"); - bubbleImage = document.getElementById("bubble-img"); + gotoButton = document.getElementById("goto"); + gotoImage = document.getElementById("goto-img"); expandButton = document.getElementById("expand"); connectEventBridge(); muteButton.addEventListener("mouseenter", onButtonHover, false); - bubbleButton.addEventListener("mouseenter", onButtonHover, false); + gotoButton.addEventListener("mouseenter", onButtonHover, false); expandButton.addEventListener("mouseenter", onButtonHover, false); muteButton.addEventListener("click", onMuteButtonClick, true); - bubbleButton.addEventListener("click", onBubbleButtonClick, true); + gotoButton.addEventListener("click", onGotoButtonClick, true); expandButton.addEventListener("click", onExpandButtonClick, true); document.body.onunload = function () { diff --git a/scripts/system/html/miniTablet.html b/scripts/system/html/miniTablet.html index 4b3129fca9..eca21c0d68 100644 --- a/scripts/system/html/miniTablet.html +++ b/scripts/system/html/miniTablet.html @@ -20,8 +20,8 @@ See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.
-
- +
+
diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index c53fab88a5..3e88c2fd20 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -126,8 +126,7 @@ // Button icons. MUTE_ON_ICON = Script.resourcesPath() + "icons/tablet-icons/mic-mute-a.svg", MUTE_OFF_ICON = Script.resourcesPath() + "icons/tablet-icons/mic-unmute-i.svg", - BUBBLE_ON_ICON = Script.resourcesPath() + "icons/tablet-icons/bubble-a.svg", - BUBBLE_OFF_ICON = Script.resourcesPath() + "icons/tablet-icons/bubble-i.svg", + GOTO_ICON = Script.resourcesPath() + "icons/tablet-icons/goto-i.svg", // Expansion to tablet. MINI_EXPAND_HANDLES = [ // Normalized coordinates in range [-0.5, 0.5] about center of mini tablet. @@ -149,14 +148,14 @@ miniTargetWidth, miniTargetLocalRotation, - // EventBridge + // EventBridge. READY_MESSAGE = "ready", // Engine <== Dialog HOVER_MESSAGE = "hover", // Engine <== Dialog MUTE_MESSAGE = "mute", // Engine <=> Dialog - BUBBLE_MESSAGE = "bubble", // Engine <=> Dialog + GOTO_MESSAGE = "goto", // Engine <=> Dialog EXPAND_MESSAGE = "expand", // Engine <== Dialog - // Sounds + // Sounds. HOVER_SOUND = "./assets/sounds/button-hover.wav", HOVER_VOLUME = 0.5, CLICK_SOUND = "./assets/sounds/button-click.wav", @@ -174,12 +173,10 @@ })); } - function updateBubbleStatus() { - var isBubbleOn = Users.getIgnoreRadiusEnabled(); + function setGotoIcon() { miniOverlayObject.emitScriptEvent(JSON.stringify({ - type: BUBBLE_MESSAGE, - on: isBubbleOn, - icon: isBubbleOn ? BUBBLE_ON_ICON : BUBBLE_OFF_ICON + type: GOTO_MESSAGE, + icon: GOTO_ICON })); } @@ -197,7 +194,7 @@ case READY_MESSAGE: // Send initial button statuses. updateMutedStatus(); - updateBubbleStatus(); + setGotoIcon(); break; case HOVER_MESSAGE: // Audio feedback. @@ -208,15 +205,15 @@ playSound(clickSound, CLICK_VOLUME); Audio.muted = !Audio.muted; break; - case BUBBLE_MESSAGE: - // Toggle bubble. + case GOTO_MESSAGE: + // Goto. playSound(clickSound, CLICK_VOLUME); - Users.toggleIgnoreRadius(); + miniState.setState(miniState.MINI_EXPANDING, { hand: uiHand, goto: true }); break; case EXPAND_MESSAGE: // Expand tablet; playSound(clickSound, CLICK_VOLUME); - miniState.setState(miniState.MINI_EXPANDING, uiHand); + miniState.setState(miniState.MINI_EXPANDING, { hand: uiHand, goto: false }); break; } } @@ -419,7 +416,6 @@ getMiniTabletID: getMiniTabletID, getMiniTabletProperties: getMiniTabletProperties, updateMutedStatus: updateMutedStatus, - updateBubbleStatus: updateBubbleStatus, show: show, size: size, startExpandingTablet: startExpandingTablet, @@ -470,6 +466,10 @@ miniExpandTimer = null, miniExpandStart, + // Tablet targets. + isGoto = false, + TABLET_ADDRESS_DIALOG = "hifi/tablet/TabletAddressDialog.qml", + // Visibility. MIN_HAND_CAMERA_ANGLE = 30, DEGREES_180 = 180, @@ -483,9 +483,8 @@ updateTimer = null; } - // Stop monitoring mute and bubble changes. + // Stop monitoring mute changes. Audio.mutedChanged.disconnect(ui.updateMutedStatus); - Users.ignoreRadiusEnabledChanged.disconnect(ui.updateBubbleStatus); // Don't keep overlays prepared if in desktop mode. ui.destroy(); @@ -496,9 +495,8 @@ // Create UI so that it's ready to be displayed without seeing artefacts from creating the UI. ui = new UI(); - // Start monitoring mute and bubble changes. + // Start monitoring mute changes. Audio.mutedChanged.connect(ui.updateMutedStatus); - Users.ignoreRadiusEnabledChanged.connect(ui.updateBubbleStatus); // Start updates. updateTimer = Script.setTimeout(updateState, UPDATE_INTERVAL); @@ -640,8 +638,9 @@ setState(TABLET_OPEN); } - function enterMiniExpanding(hand) { - ui.startExpandingTablet(hand); + function enterMiniExpanding(data) { + isGoto = data.goto; + ui.startExpandingTablet(data.hand); miniExpandStart = Date.now(); miniExpandTimer = Script.setTimeout(expandMini, MINI_EXPAND_TIMEOUT); } @@ -665,10 +664,17 @@ ui.hide(); + if (isGoto) { + tablet.loadQMLSource(TABLET_ADDRESS_DIALOG); + } else { + tablet.gotoHomeScreen(); + } + Overlays.editOverlay(HMD.tabletID, { position: miniTabletProperties.position, orientation: miniTabletProperties.orientation }); + HMD.openTablet(true); } @@ -807,13 +813,13 @@ if (message.action === "grab" && miniState.getState() === miniState.MINI_VISIBLE) { hand = message.joint === HAND_NAMES[miniHand] ? miniHand : otherHand(miniHand); if (hand === miniHand) { - miniState.setState(miniState.MINI_EXPANDING, hand); + miniState.setState(miniState.MINI_EXPANDING, { hand: hand, goto: false }); } else { miniState.setState(miniState.MINI_GRABBED); } } else if (message.action === "release" && miniState.getState() === miniState.MINI_GRABBED) { hand = message.joint === HAND_NAMES[miniHand] ? miniHand : otherHand(miniHand); - miniState.setState(miniState.MINI_EXPANDING, hand); + miniState.setState(miniState.MINI_EXPANDING, { hand: hand, goto: false }); } } From 12454d7ec74d4489c69d34ffedf5a57cc78a39b2 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 7 Sep 2018 16:48:36 +1200 Subject: [PATCH 046/259] Increase mini tablet grow/shrink time --- scripts/system/miniTablet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 3e88c2fd20..226dbc7d9a 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -455,7 +455,7 @@ UPDATE_INTERVAL = 300, // Mini tablet scaling. - MINI_SCALE_DURATION = 150, + MINI_SCALE_DURATION = 250, MINI_SCALE_TIMEOUT = 20, miniScaleTimer = null, miniScaleStart, From f206a8f158e1a938a435326aaf0df5d5702d6ba0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 7 Sep 2018 19:43:51 +1200 Subject: [PATCH 047/259] Display mini tablet on hand being gazed at if there is a choice --- scripts/system/miniTablet.js | 56 +++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 226dbc7d9a..fa3047c92e 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -527,7 +527,10 @@ Vec3.multiplyQbyV(handOrientation, uiPositionAndOrientation.position))); miniOrientation = Quat.multiply(handOrientation, uiPositionAndOrientation.rotation); miniToCameraDirection = Vec3.normalize(Vec3.subtract(Camera.position, miniPosition)); - return Vec3.dot(miniToCameraDirection, Quat.getForward(miniOrientation)) > MIN_HAND_CAMERA_ANGLE_COS; + return { + show: Vec3.dot(miniToCameraDirection, Quat.getForward(miniOrientation)) > MIN_HAND_CAMERA_ANGLE_COS, + cameraToHand: -Vec3.dot(miniToCameraDirection, Quat.getForward(Camera.orientation)) + }; } function enterMiniHidden() { @@ -535,14 +538,27 @@ } function updateMiniHidden() { + var showLeft, + showRight; + // Don't show mini tablet if tablet proper is already displayed or in toolbar mode. if (HMD.showTablet || tablet.toolbarMode) { return; } - // Compare palm directions of hands with vectors from palms to camera. - if (shouldShowMini(LEFT_HAND)) { + + // Show mini tablet if it would be pointing at the camera. + showLeft = shouldShowMini(LEFT_HAND); + showRight = shouldShowMini(RIGHT_HAND); + if (showLeft.show && showRight.show) { + // Both hands would be pointing at camera; show the one the camera is gazing at. + if (showLeft.cameraToHand > showRight.cameraToHand) { + setState(MINI_SHOWING, LEFT_HAND); + } else { + setState(MINI_SHOWING, RIGHT_HAND); + } + } else if (showLeft.show) { setState(MINI_SHOWING, LEFT_HAND); - } else if (shouldShowMini(RIGHT_HAND)) { + } else if (showRight.show) { setState(MINI_SHOWING, RIGHT_HAND); } } @@ -609,13 +625,38 @@ } function updateMiniVisible() { + var showLeft, + showRight; + // Hide mini tablet if tablet proper has been displayed by other means. if (HMD.showTablet) { setState(MINI_HIDDEN); return; } - // Check that palm direction of mini tablet hand still less than maximum angle. - if (!shouldShowMini(miniHand)) { + + // Check that the mini tablet should still be visible and if so then ensure it's on the hand that the camera is + // gazing at. + showLeft = shouldShowMini(LEFT_HAND); + showRight = shouldShowMini(RIGHT_HAND); + if (showLeft.show && showRight.show) { + if (showLeft.cameraToHand > showRight.cameraToHand) { + if (miniHand !== LEFT_HAND) { + setState(MINI_HIDING); + } + } else { + if (miniHand !== RIGHT_HAND) { + setState(MINI_HIDING); + } + } + } else if (showLeft.show) { + if (miniHand !== LEFT_HAND) { + setState(MINI_HIDING); + } + } else if (showRight.show) { + if (miniHand !== RIGHT_HAND) { + setState(MINI_HIDING); + } + } else { setState(MINI_HIDING); } } @@ -728,7 +769,8 @@ function setState(state, data) { if (state !== miniState) { - debug("State transition from " + STATE_STRINGS[miniState] + " to " + STATE_STRINGS[state]); + debug("State transition from " + STATE_STRINGS[miniState] + " to " + STATE_STRINGS[state] + + ( data ? " " + JSON.stringify(data) : "")); if (STATE_MACHINE[STATE_STRINGS[miniState]].exit) { STATE_MACHINE[STATE_STRINGS[miniState]].exit(data); } From c0a153482a63689543821d1fbf142d207c99de8e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 8 Sep 2018 11:55:47 +1200 Subject: [PATCH 048/259] Update mini tablet model --- .../system/assets/models/miniTabletBlank.fbx | Bin 0 -> 202832 bytes scripts/system/assets/models/tinyTablet.fbx | Bin 476000 -> 0 bytes scripts/system/html/css/miniTablet.css | 2 +- scripts/system/miniTablet.js | 2 +- 4 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 scripts/system/assets/models/miniTabletBlank.fbx delete mode 100644 scripts/system/assets/models/tinyTablet.fbx diff --git a/scripts/system/assets/models/miniTabletBlank.fbx b/scripts/system/assets/models/miniTabletBlank.fbx new file mode 100644 index 0000000000000000000000000000000000000000..a2faa2a80acd99d0a3bbc8e3d0e672b4352d29ad GIT binary patch literal 202832 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
o3*Qd?75aPl?xK4N^#huZ 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

~y{jVIB$JG{1IMNo@mcB`e&CPw3 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 diff --git a/scripts/system/html/css/miniTablet.css b/scripts/system/html/css/miniTablet.css index d5a12aae9c..c134f87f49 100644 --- a/scripts/system/html/css/miniTablet.css +++ b/scripts/system/html/css/miniTablet.css @@ -16,7 +16,7 @@ See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0. } html { - background-color: #121212; + background-color: #404040; } body { diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index fa3047c92e..23610fb3a4 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -87,7 +87,7 @@ var // Base overlay miniOverlay = null, - MINI_MODEL = Script.resolvePath("./assets/models/tinyTablet.fbx"), + MINI_MODEL = Script.resolvePath("./assets/models/miniTabletBlank.fbx"), MINI_DIMENSIONS = { x: 0.0637, y: 0.0965, z: 0.0045 }, // Proportional to tablet proper. MINI_POSITIONS = [ { From 8eeed772eb6864fc096b920018c5bd0983f880de Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 8 Sep 2018 12:40:36 +1200 Subject: [PATCH 049/259] Update button layout --- scripts/system/assets/images/expand.svg | 85 +++++++++++++++++++ .../system/html/css/img/mt-expand-hover.svg | 30 +------ .../system/html/css/img/mt-expand-normal.svg | 30 +------ scripts/system/html/css/img/mt-goto-hover.svg | 4 +- .../system/html/css/img/mt-goto-normal.svg | 4 +- scripts/system/html/css/img/mt-mute-hover.svg | 4 +- .../system/html/css/img/mt-mute-normal.svg | 4 +- scripts/system/html/css/miniTablet.css | 23 +++-- scripts/system/html/miniTablet.html | 4 +- 9 files changed, 114 insertions(+), 74 deletions(-) create mode 100644 scripts/system/assets/images/expand.svg diff --git a/scripts/system/assets/images/expand.svg b/scripts/system/assets/images/expand.svg new file mode 100644 index 0000000000..f57e624374 --- /dev/null +++ b/scripts/system/assets/images/expand.svg @@ -0,0 +1,85 @@ + + + +image/svg+xml + + + + + + + + + \ No newline at end of file diff --git a/scripts/system/html/css/img/mt-expand-hover.svg b/scripts/system/html/css/img/mt-expand-hover.svg index 97737f42c0..a8e84c42ad 100644 --- a/scripts/system/html/css/img/mt-expand-hover.svg +++ b/scripts/system/html/css/img/mt-expand-hover.svg @@ -1,31 +1,5 @@  - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/scripts/system/html/css/img/mt-expand-normal.svg b/scripts/system/html/css/img/mt-expand-normal.svg index 8b849ecc70..aac349ebda 100644 --- a/scripts/system/html/css/img/mt-expand-normal.svg +++ b/scripts/system/html/css/img/mt-expand-normal.svg @@ -1,31 +1,5 @@  - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/scripts/system/html/css/img/mt-goto-hover.svg b/scripts/system/html/css/img/mt-goto-hover.svg index c8c407139b..4cad54331a 100644 --- a/scripts/system/html/css/img/mt-goto-hover.svg +++ b/scripts/system/html/css/img/mt-goto-hover.svg @@ -1,5 +1,5 @@  - - + + diff --git a/scripts/system/html/css/img/mt-goto-normal.svg b/scripts/system/html/css/img/mt-goto-normal.svg index 6be11c464e..ead63329fb 100644 --- a/scripts/system/html/css/img/mt-goto-normal.svg +++ b/scripts/system/html/css/img/mt-goto-normal.svg @@ -1,5 +1,5 @@  - - + + diff --git a/scripts/system/html/css/img/mt-mute-hover.svg b/scripts/system/html/css/img/mt-mute-hover.svg index 909ecb0272..9a18ccd933 100644 --- a/scripts/system/html/css/img/mt-mute-hover.svg +++ b/scripts/system/html/css/img/mt-mute-hover.svg @@ -1,5 +1,5 @@  - - + + diff --git a/scripts/system/html/css/img/mt-mute-normal.svg b/scripts/system/html/css/img/mt-mute-normal.svg index 506756216f..472f03f138 100644 --- a/scripts/system/html/css/img/mt-mute-normal.svg +++ b/scripts/system/html/css/img/mt-mute-normal.svg @@ -1,5 +1,5 @@  - - + + diff --git a/scripts/system/html/css/miniTablet.css b/scripts/system/html/css/miniTablet.css index c134f87f49..55abba43a6 100644 --- a/scripts/system/html/css/miniTablet.css +++ b/scripts/system/html/css/miniTablet.css @@ -26,13 +26,13 @@ body { section { background-color: #404040; position: relative; - padding: 13px 9px; + padding: 16px 16px; } .button { - width: 128px; - height: 90px; - margin-top: 10px; + width: 116px; + height: 84px; + margin-top: 16px; text-align: center; } @@ -41,7 +41,7 @@ section { } img { - width: 46px; + width: 40px; } #mute { @@ -72,10 +72,10 @@ img { #expand { position: absolute; - right: 12px; - bottom: 13px; - width: 40px; - height: 40px; + right: 1px; + bottom: -1px; + width: 50px; + height: 50px; background-size: 100% 100%; } @@ -86,3 +86,8 @@ img { #expand.off:hover { background-image: url("./img/mt-expand-hover.svg"); } + + #expand img { + width:34px; + margin-top: 7px; + } diff --git a/scripts/system/html/miniTablet.html b/scripts/system/html/miniTablet.html index eca21c0d68..92eac02734 100644 --- a/scripts/system/html/miniTablet.html +++ b/scripts/system/html/miniTablet.html @@ -23,7 +23,9 @@ See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.

-
+
+ +
From 5fcb69c72427ff05d5b980bfd4a82218d86bf948 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 8 Sep 2018 16:13:36 +1200 Subject: [PATCH 050/259] Remove extraneous CSS --- scripts/system/html/css/miniTablet.css | 27 +++++++++----------------- scripts/system/html/miniTablet.html | 6 +++--- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/scripts/system/html/css/miniTablet.css b/scripts/system/html/css/miniTablet.css index 55abba43a6..a87add36a1 100644 --- a/scripts/system/html/css/miniTablet.css +++ b/scripts/system/html/css/miniTablet.css @@ -47,29 +47,23 @@ img { #mute { padding-top: 19px; background-size: 100% 100%; + background-image: url("./img/mt-mute-normal.svg"); } - #mute.off { - background-image: url("./img/mt-mute-normal.svg"); + #mute:hover { + background-image: url("./img/mt-mute-hover.svg"); } - #mute.off:hover { - background-image: url("./img/mt-mute-hover.svg"); - } - #goto { padding-top: 19px; background-size: 100% 100%; + background-image: url("./img/mt-goto-normal.svg"); } - #goto.off { - background-image: url("./img/mt-goto-normal.svg"); + #goto:hover { + background-image: url("./img/mt-goto-hover.svg"); } - #goto.off:hover { - background-image: url("./img/mt-goto-hover.svg"); - } - #expand { position: absolute; right: 1px; @@ -77,16 +71,13 @@ img { width: 50px; height: 50px; background-size: 100% 100%; + background-image: url("./img/mt-expand-normal.svg"); } - #expand.off { - background-image: url("./img/mt-expand-normal.svg"); + #expand:hover { + background-image: url("./img/mt-expand-hover.svg"); } - #expand.off:hover { - background-image: url("./img/mt-expand-hover.svg"); - } - #expand img { width:34px; margin-top: 7px; diff --git a/scripts/system/html/miniTablet.html b/scripts/system/html/miniTablet.html index 92eac02734..f0ef7cd609 100644 --- a/scripts/system/html/miniTablet.html +++ b/scripts/system/html/miniTablet.html @@ -17,13 +17,13 @@ See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.
-
+
-
+
-
+
From 66f75e856e5f03a4ef449cbcfa06d6851953389c Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 8 Sep 2018 16:41:36 +1200 Subject: [PATCH 051/259] Fix goto and expand displaying as hovered when redisplay mini tablet --- scripts/system/html/css/miniTablet.css | 8 ++++++ scripts/system/html/js/miniTablet.js | 40 +++++++++++++++++++++----- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/scripts/system/html/css/miniTablet.css b/scripts/system/html/css/miniTablet.css index a87add36a1..7598332d28 100644 --- a/scripts/system/html/css/miniTablet.css +++ b/scripts/system/html/css/miniTablet.css @@ -64,6 +64,10 @@ img { background-image: url("./img/mt-goto-hover.svg"); } + #goto:hover.unhover { + background-image: url("./img/mt-goto-normal.svg"); + } + #expand { position: absolute; right: 1px; @@ -78,6 +82,10 @@ img { background-image: url("./img/mt-expand-hover.svg"); } + #expand:hover.unhover { + background-image: url("./img/mt-expand-normal.svg"); + } + #expand img { width:34px; margin-top: 7px; diff --git a/scripts/system/html/js/miniTablet.js b/scripts/system/html/js/miniTablet.js index f21dc7a6e6..a966cbb139 100644 --- a/scripts/system/html/js/miniTablet.js +++ b/scripts/system/html/js/miniTablet.js @@ -26,7 +26,30 @@ muteImage, gotoButton, gotoImage, - expandButton; + expandButton, + + // Work around buttons staying hovered when mini tablet is replaced by tablet proper then subsequently redisplayed. + isUnhover = true; + + // #region Utilites ======================================================================================================== + + function setUnhover() { + if (!isUnhover) { + gotoButton.classList.add("unhover"); + expandButton.classList.add("unhover"); + isUnhover = true; + } + } + + function clearUnhover() { + if (isUnhover) { + gotoButton.classList.remove("unhover"); + expandButton.classList.remove("unhover"); + isUnhover = false; + } + } + + // #endregion // #region Communications ================================================================================================== @@ -50,25 +73,28 @@ } } + function onButtonHover() { + EventBridge.emitWebEvent(JSON.stringify({ + type: HOVER_MESSAGE + })); + clearUnhover(); + } + function onMuteButtonClick() { EventBridge.emitWebEvent(JSON.stringify({ type: MUTE_MESSAGE })); } - function onButtonHover() { - EventBridge.emitWebEvent(JSON.stringify({ - type: HOVER_MESSAGE - })); - } - function onGotoButtonClick() { + setUnhover(); EventBridge.emitWebEvent(JSON.stringify({ type: GOTO_MESSAGE })); } function onExpandButtonClick() { + setUnhover(); EventBridge.emitWebEvent(JSON.stringify({ type: EXPAND_MESSAGE })); From 5fd3f563d6d6eab98696abf4e65169823a7a5fd9 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 9 Sep 2018 10:55:05 +1200 Subject: [PATCH 052/259] Move mini tablet down in the palm a little. --- scripts/system/miniTablet.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 23610fb3a4..1a753ee65a 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -91,12 +91,12 @@ MINI_DIMENSIONS = { x: 0.0637, y: 0.0965, z: 0.0045 }, // Proportional to tablet proper. MINI_POSITIONS = [ { - x: -0.03, // Distance across hand. + x: -0.01, // Distance across hand. y: 0.08, // Distance from joint. z: 0.06 // Distance above palm. }, { - x: 0.03, // Distance across hand. + x: 0.01, // Distance across hand. y: 0.08, // Distance from joint. z: 0.06 // Distance above palm. } From baf1e8044539752bb6b6f96cd2aa7613872e3c4b Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 9 Sep 2018 11:37:05 +1200 Subject: [PATCH 053/259] Code tidying --- scripts/system/miniTablet.js | 42 ++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 1a753ee65a..8c35109528 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -29,7 +29,6 @@ // Miscellaneous. HIFI_OBJECT_MANIPULATION_CHANNEL = "Hifi-Object-Manipulation", tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"), - avatarScale = 1, // Sanitized MyAvatar.scale. DEBUG = false; // #region Utilities ======================================================================================================= @@ -263,13 +262,13 @@ Overlays.editOverlay(miniOverlay, { parentID: MyAvatar.SELF_ID, parentJointIndex: handJointIndex(hand), - localPosition: Vec3.multiply(avatarScale, MINI_POSITIONS[hand]), + localPosition: Vec3.multiply(MyAvatar.scale, MINI_POSITIONS[hand]), localRotation: MINI_ROTATIONS[hand], dimensions: Vec3.multiply(initialScale, MINI_DIMENSIONS), visible: true }); Overlays.editOverlay(miniUIOverlay, { - localPosition: Vec3.multiply(avatarScale, MINI_UI_LOCAL_POSITION), + localPosition: Vec3.multiply(MyAvatar.scale, MINI_UI_LOCAL_POSITION), localRotation: MINI_UI_LOCAL_ROTATION, dimensions: Vec3.multiply(initialScale, MINI_UI_DIMENSIONS), dpi: MINI_UI_DPI / initialScale, @@ -330,7 +329,7 @@ localRotation, localPosition; - tabletScaleFactor = avatarScale * (1 + scaleFactor * (miniTargetWidth - miniInitialWidth) / miniInitialWidth); + tabletScaleFactor = MyAvatar.scale * (1 + scaleFactor * (miniTargetWidth - miniInitialWidth) / miniInitialWidth); dimensions = Vec3.multiply(tabletScaleFactor, MINI_DIMENSIONS); localRotation = Quat.mix(miniExpandLocalRotation, miniTargetLocalRotation, scaleFactor); localPosition = @@ -368,7 +367,7 @@ function create() { miniOverlay = Overlays.addOverlay("model", { url: MINI_MODEL, - dimensions: Vec3.multiply(avatarScale, MINI_DIMENSIONS), + dimensions: Vec3.multiply(MyAvatar.scale, MINI_DIMENSIONS), solid: true, grabbable: true, showKeyboardFocusHighlight: false, @@ -378,10 +377,10 @@ miniUIOverlay = Overlays.addOverlay("web3d", { url: MINI_UI_HTML, parentID: miniOverlay, - localPosition: Vec3.multiply(avatarScale, MINI_UI_LOCAL_POSITION), + localPosition: Vec3.multiply(MyAvatar.scale, MINI_UI_LOCAL_POSITION), localRotation: MINI_UI_LOCAL_ROTATION, - dimensions: Vec3.multiply(avatarScale, MINI_UI_DIMENSIONS), - dpi: MINI_UI_DPI / avatarScale, + dimensions: Vec3.multiply(MyAvatar.scale, MINI_UI_DIMENSIONS), + dpi: MINI_UI_DPI / MyAvatar.scale, alpha: 0, // Hide overlay while its content is being created. grabbable: false, showKeyboardFocusHighlight: false, @@ -394,7 +393,7 @@ miniOverlayObject = Overlays.getOverlayObject(miniUIOverlay); miniOverlayObject.webEventReceived.connect(onWebEventReceived); - // updateMiniTabletID(); Other scripts relying on this may not be ready yet so do this in showUI(). + // updateMiniTabletID(); Other scripts relying on this may not be ready yet so do this in show(). } function destroy() { @@ -523,7 +522,7 @@ Vec3.multiplyQbyV(MyAvatar.orientation, MyAvatar.getAbsoluteJointTranslationInObjectFrame(jointIndex))); handOrientation = Quat.multiply(MyAvatar.orientation, MyAvatar.getAbsoluteJointRotationInObjectFrame(jointIndex)); uiPositionAndOrientation = ui.getUIPositionAndRotation(hand); - miniPosition = Vec3.sum(handPosition, Vec3.multiply(avatarScale, + miniPosition = Vec3.sum(handPosition, Vec3.multiply(MyAvatar.scale, Vec3.multiplyQbyV(handOrientation, uiPositionAndOrientation.position))); miniOrientation = Quat.multiply(handOrientation, uiPositionAndOrientation.rotation); miniToCameraDirection = Vec3.normalize(Vec3.subtract(Camera.position, miniPosition)); @@ -566,7 +565,7 @@ function scaleMiniDown() { var scaleFactor = (Date.now() - miniScaleStart) / MINI_SCALE_DURATION; if (scaleFactor < 1) { - ui.size((1 - scaleFactor) * avatarScale); + ui.size((1 - scaleFactor) * MyAvatar.scale); miniScaleTimer = Script.setTimeout(scaleMiniDown, MINI_SCALE_TIMEOUT); return; } @@ -595,12 +594,12 @@ function scaleMiniUp() { var scaleFactor = (Date.now() - miniScaleStart) / MINI_SCALE_DURATION; if (scaleFactor < 1) { - ui.size(scaleFactor * avatarScale); + ui.size(scaleFactor * MyAvatar.scale); miniScaleTimer = Script.setTimeout(scaleMiniUp, MINI_SCALE_TIMEOUT); return; } miniScaleTimer = null; - ui.size(avatarScale); + ui.size(MyAvatar.scale); setState(MINI_VISIBLE); } @@ -831,12 +830,6 @@ // #region External Events ================================================================================================= - function onScaleChanged() { - avatarScale = MyAvatar.scale; - // Clamp scale in order to work around M17434. - avatarScale = Math.max(MyAvatar.getDomainMinScale(), Math.min(MyAvatar.getDomainMaxScale(), avatarScale)); - } - function onMessageReceived(channel, data, senderID, localOnly) { var message, miniHand, @@ -846,7 +839,12 @@ return; } - message = JSON.parse(data); + try { + message = JSON.parse(data); + } catch (e) { + return; + } + if (message.grabbedEntity !== ui.getMiniTabletID()) { return; } @@ -881,8 +879,6 @@ function setUp() { miniState = new State(); - MyAvatar.scaleChanged.connect(onScaleChanged); - Messages.subscribe(HIFI_OBJECT_MANIPULATION_CHANNEL); Messages.messageReceived.connect(onMessageReceived); @@ -900,8 +896,6 @@ Messages.messageReceived.disconnect(onMessageReceived); Messages.unsubscribe(HIFI_OBJECT_MANIPULATION_CHANNEL); - MyAvatar.scaleChanged.disconnect(onScaleChanged); - miniState.destroy(); miniState = null; } From 1faef9db8c89c0269aa303890fb5d1b21c90fa49 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 9 Sep 2018 12:07:33 +1200 Subject: [PATCH 054/259] Fix tablet ending upside down if it expands into other grabbing hand --- scripts/system/miniTablet.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 8c35109528..df428c9c52 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -265,6 +265,7 @@ localPosition: Vec3.multiply(MyAvatar.scale, MINI_POSITIONS[hand]), localRotation: MINI_ROTATIONS[hand], dimensions: Vec3.multiply(initialScale, MINI_DIMENSIONS), + grabbable: true, visible: true }); Overlays.editOverlay(miniUIOverlay, { @@ -320,6 +321,11 @@ miniInitialWidth = MINI_DIMENSIONS.x; miniTargetWidth = getTabletWidthFromSettings(); miniTargetLocalRotation = Quat.multiply(miniExpandLocalRotation, miniExpandDeltaRotation); + + // Don't let other hand grab while expanding. + Overlays.editOverlay(miniOverlay, { + grabbable: false + }); } function sizeAboutHandles(scaleFactor) { From 0e079e78e61c7c0814c8439de9499e1babe712a6 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 10 Sep 2018 16:16:04 -0700 Subject: [PATCH 055/259] correct transparent parabola shader --- interface/src/raypick/ParabolaPointer.cpp | 4 ++-- .../render-utils/src/parabola_translucent.slf | 18 ++++++++++++++++++ .../src/render-utils/parabola_translucent.slp | 1 + 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 libraries/render-utils/src/parabola_translucent.slf create mode 100644 libraries/render-utils/src/render-utils/parabola_translucent.slp diff --git a/interface/src/raypick/ParabolaPointer.cpp b/interface/src/raypick/ParabolaPointer.cpp index 888b3ddbe8..ad698c409b 100644 --- a/interface/src/raypick/ParabolaPointer.cpp +++ b/interface/src/raypick/ParabolaPointer.cpp @@ -382,9 +382,8 @@ void ParabolaPointer::RenderState::ParabolaRenderItem::updateBounds() { const gpu::PipelinePointer ParabolaPointer::RenderState::ParabolaRenderItem::getParabolaPipeline() { if (!_parabolaPipeline || !_transparentParabolaPipeline) { - gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::parabola); - { + gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::parabola); auto state = std::make_shared(); state->setDepthTest(true, true, gpu::LESS_EQUAL); state->setBlendFunction(false, @@ -396,6 +395,7 @@ const gpu::PipelinePointer ParabolaPointer::RenderState::ParabolaRenderItem::get } { + gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::parabola_translucent); auto state = std::make_shared(); state->setDepthTest(true, true, gpu::LESS_EQUAL); state->setBlendFunction(true, diff --git a/libraries/render-utils/src/parabola_translucent.slf b/libraries/render-utils/src/parabola_translucent.slf new file mode 100644 index 0000000000..62b5b15193 --- /dev/null +++ b/libraries/render-utils/src/parabola_translucent.slf @@ -0,0 +1,18 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// Created by Sam Gondelman on 9/10/2018 +// Copyright 2018 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 +// + +<@include DeferredBufferWrite.slh@> + +layout(location=0) in vec4 _color; + +void main(void) { + packDeferredFragmentTranslucent(vec3(1.0, 0.0, 0.0), _color.a, _color.rgb, DEFAULT_FRESNEL, DEFAULT_ROUGHNESS); +} diff --git a/libraries/render-utils/src/render-utils/parabola_translucent.slp b/libraries/render-utils/src/render-utils/parabola_translucent.slp new file mode 100644 index 0000000000..ab3f1d4126 --- /dev/null +++ b/libraries/render-utils/src/render-utils/parabola_translucent.slp @@ -0,0 +1 @@ +VERTEX parabola From 1aaa90675c1f4aaa137e088228f3e6221d135d9f Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 11 Sep 2018 12:04:39 -0700 Subject: [PATCH 056/259] Snapshot.js now uses AppUi --- scripts/modules/appUi.js | 17 ++++- scripts/system/snapshot.js | 141 ++++++++++++------------------------- 2 files changed, 61 insertions(+), 97 deletions(-) diff --git a/scripts/modules/appUi.js b/scripts/modules/appUi.js index 6d2986768a..76a06424db 100644 --- a/scripts/modules/appUi.js +++ b/scripts/modules/appUi.js @@ -54,6 +54,17 @@ function AppUi(properties) { that.tablet.gotoWebScreen(url, that.inject); } }; + that.openOnTop = function openOnTop(url, optionalInject) { // Opens some app on top of the current app + if (that.isQML(url)) { + that.tablet.loadQMLOnTop(url); + } else { + if (optionalInject) { + that.tablet.loadWebScreenOnTop(url, optionalInject); + } else { + that.tablet.loadWebScreenOnTop(url); + } + } + } that.close = function close() { // How to close the app. that.currentUrl = ""; // for toolbar-mode: go back to home screen, this will close the window. @@ -69,7 +80,11 @@ function AppUi(properties) { activeIcon: isWaiting ? that.activeMessagesButton : that.activeButton }); }; - that.isQML = function isQML() { // We set type property in onClick. + that.isQML = function isQML(optionalUrl) { // We set type property in onClick. + if (optionalUrl) { + var type = /.qml$/.test(optionalUrl) ? 'QML' : 'Web'; + return type === 'QML'; + } return that.type === 'QML'; }; that.eventSignal = function eventSignal() { // What signal to hook onMessage to. diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 37270f896e..907dcd75c1 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -12,23 +12,13 @@ (function () { // BEGIN LOCAL_SCOPE Script.include("/~/system/libraries/accountUtils.js"); +var AppUi = Script.require('appUi'); var SNAPSHOT_DELAY = 500; // 500ms var FINISH_SOUND_DELAY = 350; var resetOverlays; var reticleVisible; -var buttonName = "SNAP"; -var buttonConnected = false; - -var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); -var button = tablet.addButton({ - icon: "icons/tablet-icons/snap-i.svg", - activeIcon: "icons/tablet-icons/snap-a.svg", - text: buttonName, - sortOrder: 5 -}); - var snapshotOptions = {}; var imageData = []; var storyIDsToMaybeDelete = []; @@ -52,8 +42,6 @@ try { print('Failed to resolve request api, error: ' + err); } - - function removeFromStoryIDsToMaybeDelete(story_id) { storyIDsToMaybeDelete.splice(storyIDsToMaybeDelete.indexOf(story_id), 1); print('storyIDsToMaybeDelete[] now:', JSON.stringify(storyIDsToMaybeDelete)); @@ -73,33 +61,32 @@ function onMessage(message) { // 2. Although we currently use a single image, we would like to take snapshot, a selfie, a 360 etc. all at the // same time, show the user all of them, and have the user deselect any that they do not want to share. // So we'll ultimately be receiving a set of objects, perhaps with different post processing for each. - message = JSON.parse(message); if (message.type !== "snapshot") { return; } switch (message.action) { case 'ready': // DOM is ready and page has loaded - tablet.emitScriptEvent(JSON.stringify({ + ui.sendMessage({ type: "snapshot", action: "captureSettings", setting: Settings.getValue("alsoTakeAnimatedSnapshot", true) - })); + }); if (Snapshot.getSnapshotsLocation() !== "") { isDomainOpen(Settings.getValue("previousSnapshotDomainID"), function (canShare) { - tablet.emitScriptEvent(JSON.stringify({ + ui.sendMessage({ type: "snapshot", action: "showPreviousImages", options: snapshotOptions, image_data: imageData, canShare: canShare - })); + }); }); } else { - tablet.emitScriptEvent(JSON.stringify({ + ui.sendMessage({ type: "snapshot", action: "showSetupInstructions" - })); + }); Settings.setValue("previousStillSnapPath", ""); Settings.setValue("previousStillSnapStoryID", ""); Settings.setValue("previousStillSnapBlastingDisabled", false); @@ -124,7 +111,7 @@ function onMessage(message) { || (!HMD.active && Settings.getValue("desktopTabletBecomesToolbar", true))) { Desktop.show("hifi/dialogs/GeneralPreferencesDialog.qml", "GeneralPreferencesDialog"); } else { - tablet.loadQMLOnTop("hifi/tablet/TabletGeneralPreferences.qml"); + ui.openOnTop("hifi/tablet/TabletGeneralPreferences.qml"); } break; case 'captureStillAndGif': @@ -284,7 +271,6 @@ var POLAROID_RATE_LIMIT_MS = 1000; var polaroidPrintingIsRateLimited = false; function printToPolaroid(image_url) { - // Rate-limit printing if (polaroidPrintingIsRateLimited) { return; @@ -376,19 +362,6 @@ function fillImageDataFromPrevious() { } } -var SNAPSHOT_REVIEW_URL = Script.resolvePath("html/SnapshotReview.html"); -var isInSnapshotReview = false; -function onButtonClicked() { - if (isInSnapshotReview){ - // for toolbar-mode: go back to home screen, this will close the window. - tablet.gotoHomeScreen(); - } else { - fillImageDataFromPrevious(); - tablet.gotoWebScreen(SNAPSHOT_REVIEW_URL); - HMD.openTablet(); - } -} - function snapshotUploaded(isError, reply) { if (!isError) { var replyJson = JSON.parse(reply), @@ -409,12 +382,12 @@ function snapshotUploaded(isError, reply) { } if ((isGif && !ignoreGifSnapshotData) || (!isGif && !ignoreStillSnapshotData)) { print('SUCCESS: Snapshot uploaded! Story with audience:for_url created! ID:', storyID); - tablet.emitScriptEvent(JSON.stringify({ + ui.sendMessage({ type: "snapshot", action: "snapshotUploadComplete", story_id: storyID, image_url: imageURL, - })); + }); if (isGif) { Settings.setValue("previousAnimatedSnapStoryID", storyID); } else { @@ -429,10 +402,10 @@ function snapshotUploaded(isError, reply) { } var href, snapshotDomainID; function takeSnapshot() { - tablet.emitScriptEvent(JSON.stringify({ + ui.sendMessage({ type: "snapshot", action: "clearPreviousImages" - })); + }); Settings.setValue("previousStillSnapPath", ""); Settings.setValue("previousStillSnapStoryID", ""); Settings.setValue("previousStillSnapBlastingDisabled", false); @@ -471,10 +444,6 @@ function takeSnapshot() { } else { Window.stillSnapshotTaken.connect(stillSnapshotTaken); } - if (buttonConnected) { - button.clicked.disconnect(onButtonClicked); - buttonConnected = false; - } // hide overlays if they are on if (resetOverlays) { @@ -538,10 +507,6 @@ function stillSnapshotTaken(pathStillSnapshot, notify) { Menu.setIsOptionChecked("Show Overlays", true); } Window.stillSnapshotTaken.disconnect(stillSnapshotTaken); - if (!buttonConnected) { - button.clicked.connect(onButtonClicked); - buttonConnected = true; - } // A Snapshot Review dialog might be left open indefinitely after taking the picture, // during which time the user may have moved. So stash that info in the dialog so that @@ -559,12 +524,12 @@ function stillSnapshotTaken(pathStillSnapshot, notify) { isLoggedIn: isLoggedIn }; imageData = [{ localPath: pathStillSnapshot, href: href }]; - tablet.emitScriptEvent(JSON.stringify({ + ui.sendMessage({ type: "snapshot", action: "addImages", options: snapshotOptions, image_data: imageData - })); + }); }); } @@ -572,10 +537,10 @@ function snapshotDirChanged(snapshotPath) { Window.browseDirChanged.disconnect(snapshotDirChanged); if (snapshotPath !== "") { // not cancelled Snapshot.setSnapshotsLocation(snapshotPath); - tablet.emitScriptEvent(JSON.stringify({ + ui.sendMessage({ type: "snapshot", action: "snapshotLocationChosen" - })); + }); } } @@ -603,22 +568,18 @@ function processingGifStarted(pathStillSnapshot) { isLoggedIn: isLoggedIn }; imageData = [{ localPath: pathStillSnapshot, href: href }]; - tablet.emitScriptEvent(JSON.stringify({ + ui.sendMessage({ type: "snapshot", action: "addImages", options: snapshotOptions, image_data: imageData - })); + }); }); } function processingGifCompleted(pathAnimatedSnapshot) { isLoggedIn = Account.isLoggedIn(); Window.processingGifCompleted.disconnect(processingGifCompleted); - if (!buttonConnected) { - button.clicked.connect(onButtonClicked); - buttonConnected = true; - } Settings.setValue("previousAnimatedSnapPath", pathAnimatedSnapshot); @@ -631,12 +592,12 @@ function processingGifCompleted(pathAnimatedSnapshot) { canBlast: location.domainID === Settings.getValue("previousSnapshotDomainID"), }; imageData = [{ localPath: pathAnimatedSnapshot, href: href }]; - tablet.emitScriptEvent(JSON.stringify({ + ui.sendMessage({ type: "snapshot", action: "addImages", options: snapshotOptions, image_data: imageData - })); + }); }); } function maybeDeleteSnapshotStories() { @@ -655,28 +616,16 @@ function maybeDeleteSnapshotStories() { }); storyIDsToMaybeDelete = []; } -function onTabletScreenChanged(type, url) { - var wasInSnapshotReview = isInSnapshotReview; - isInSnapshotReview = (type === "Web" && url === SNAPSHOT_REVIEW_URL); - button.editProperties({ isActive: isInSnapshotReview }); - if (isInSnapshotReview !== wasInSnapshotReview) { - if (isInSnapshotReview) { - tablet.webEventReceived.connect(onMessage); - } else { - tablet.webEventReceived.disconnect(onMessage); - } - } -} function onUsernameChanged() { fillImageDataFromPrevious(); isDomainOpen(Settings.getValue("previousSnapshotDomainID"), function (canShare) { - tablet.emitScriptEvent(JSON.stringify({ + ui.sendMessage({ type: "snapshot", action: "showPreviousImages", options: snapshotOptions, image_data: imageData, canShare: canShare - })); + }); }); if (isLoggedIn) { if (shareAfterLogin) { @@ -705,10 +654,10 @@ function onUsernameChanged() { function snapshotLocationSet(location) { if (location !== "") { - tablet.emitScriptEvent(JSON.stringify({ + ui.sendMessage({ type: "snapshot", action: "snapshotLocationChosen" - })); + }); } } @@ -733,36 +682,36 @@ function processRezPermissionChange(canRez) { action = 'setPrintButtonDisabled'; } - tablet.emitScriptEvent(JSON.stringify({ + ui.sendMessage({ type: "snapshot", action : action - })); + }); } -button.clicked.connect(onButtonClicked); -buttonConnected = true; +function startup() { + ui = new AppUi({ + buttonName: "SNAP", + sortOrder: 5, + home: Script.resolvePath("html/SnapshotReview.html"), + onOpened: fillImageDataFromPrevious, + onMessage: onMessage + }); -Window.snapshotShared.connect(snapshotUploaded); -tablet.screenChanged.connect(onTabletScreenChanged); -GlobalServices.myUsernameChanged.connect(onUsernameChanged); -Snapshot.snapshotLocationSet.connect(snapshotLocationSet); + Entities.canRezChanged.connect(updatePrintPermissions); + Entities.canRezTmpChanged.connect(updatePrintPermissions); + GlobalServices.myUsernameChanged.connect(onUsernameChanged); + Snapshot.snapshotLocationSet.connect(snapshotLocationSet); + Window.snapshotShared.connect(snapshotUploaded); +} +startup(); -Entities.canRezChanged.connect(updatePrintPermissions); -Entities.canRezTmpChanged.connect(updatePrintPermissions); - -Script.scriptEnding.connect(function () { - if (buttonConnected) { - button.clicked.disconnect(onButtonClicked); - buttonConnected = false; - } - if (tablet) { - tablet.removeButton(button); - tablet.screenChanged.disconnect(onTabletScreenChanged); - } +function shutdown() { Window.snapshotShared.disconnect(snapshotUploaded); Snapshot.snapshotLocationSet.disconnect(snapshotLocationSet); + GlobalServices.myUsernameChanged.disconnect(onUsernameChanged); Entities.canRezChanged.disconnect(updatePrintPermissions); Entities.canRezTmpChanged.disconnect(updatePrintPermissions); -}); +} +Script.scriptEnding.connect(shutdown); }()); // END LOCAL_SCOPE From 3dabd392ef5e0bba77bee657f73b9f9acb44f9dc Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 11 Sep 2018 12:21:02 -0700 Subject: [PATCH 057/259] HELP app now uses AppUi --- scripts/system/help.js | 55 +++++++++--------------------------------- 1 file changed, 11 insertions(+), 44 deletions(-) diff --git a/scripts/system/help.js b/scripts/system/help.js index aaeb82721c..325a2c243b 100644 --- a/scripts/system/help.js +++ b/scripts/system/help.js @@ -12,50 +12,17 @@ // /* globals Tablet, Script, HMD, Controller, Menu */ -(function() { // BEGIN LOCAL_SCOPE - - var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; +(function () { // BEGIN LOCAL_SCOPE + var AppUi = Script.require('appUi'); + var HELP_URL = Script.resourcesPath() + "html/tabletHelp.html"; - var buttonName = "HELP"; - var onHelpScreen = false; - var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - var button = tablet.addButton({ - icon: "icons/tablet-icons/help-i.svg", - activeIcon: "icons/tablet-icons/help-a.svg", - text: buttonName, - sortOrder: 6 - }); - - var enabled = false; - function onClicked() { - if (onHelpScreen) { - tablet.gotoHomeScreen(); - } else { - if (HMD.tabletID) { - Entities.editEntity(HMD.tabletID, {textures: JSON.stringify({"tex.close" : HOME_BUTTON_TEXTURE})}); - } - Menu.triggerOption('Help...'); - onHelpScreen = true; - } + var HELP_BUTTON_NAME = "HELP"; + function startup() { + ui = new AppUi({ + buttonName: HELP_BUTTON_NAME, + sortOrder: 6, + home: HELP_URL + }); } - - function onScreenChanged(type, url) { - onHelpScreen = type === "Web" && (url.indexOf(HELP_URL) === 0); - button.editProperties({ isActive: onHelpScreen }); - } - - button.clicked.connect(onClicked); - tablet.screenChanged.connect(onScreenChanged); - - Script.scriptEnding.connect(function () { - if (onHelpScreen) { - tablet.gotoHomeScreen(); - } - button.clicked.disconnect(onClicked); - tablet.screenChanged.disconnect(onScreenChanged); - if (tablet) { - tablet.removeButton(button); - } - }); - + startup(); }()); // END LOCAL_SCOPE From ea04ee6d359d8bfbb68ab80bec93549635b78fb8 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 11 Sep 2018 12:37:53 -0700 Subject: [PATCH 058/259] Wallet now uses AppUi --- .../qml/hifi/commerce/wallet/Wallet.qml | 4 +- scripts/modules/appUi.js | 13 +- scripts/system/commerce/wallet.js | 169 ++++-------------- scripts/system/snapshot.js | 2 +- 4 files changed, 49 insertions(+), 139 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 65d98af234..47b9d354d0 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -408,9 +408,7 @@ Rectangle { Connections { onSendSignalToWallet: { - if (msg.method === 'walletReset' || msg.method === 'passphraseReset') { - sendToScript(msg); - } else if (msg.method === 'walletSecurity_changeSecurityImage') { + if (msg.method === 'walletSecurity_changeSecurityImage') { securityImageChange.initModel(); root.activeView = "securityImageChange"; } diff --git a/scripts/modules/appUi.js b/scripts/modules/appUi.js index 76a06424db..fd13075eb6 100644 --- a/scripts/modules/appUi.js +++ b/scripts/modules/appUi.js @@ -54,7 +54,18 @@ function AppUi(properties) { that.tablet.gotoWebScreen(url, that.inject); } }; - that.openOnTop = function openOnTop(url, optionalInject) { // Opens some app on top of the current app + that.openNewApp = function openNewApp(url, optionalInject) { // Opens some app and replaces the current app + if (that.isQML(url)) { + that.tablet.pushOntoStack(url); + } else { + if (optionalInject) { + that.tablet.gotoWebScreen(url, optionalInject); + } else { + that.tablet.gotoWebScreen(url); + } + } + } + that.openNewAppOnTop = function openNewAppOnTop(url, optionalInject) { // Opens some app on top of the current app (on desktop, opens new window) if (that.isQML(url)) { that.tablet.loadQMLOnTop(url); } else { diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 5939b36438..d3109e3d66 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -16,6 +16,7 @@ (function () { // BEGIN LOCAL_SCOPE Script.include("/~/system/libraries/accountUtils.js"); Script.include("/~/system/libraries/connectionUtils.js"); + var AppUi = Script.require('appUi'); var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace"; @@ -190,7 +191,7 @@ method: 'updateSelectedRecipientUsername', userName: username === "" ? "unknown username" : username }; - sendToQml(message); + ui.sendMessage(message); } } function handleClick(pickRay) { @@ -208,7 +209,7 @@ displayName: '"' + AvatarList.getAvatar(avatarId).sessionDisplayName + '"', userName: '' }; - sendToQml(message); + ui.sendMessage(message); ExtendedOverlay.some(function (overlay) { var id = overlay.key; @@ -299,39 +300,6 @@ triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand)); // END AVATAR SELECTOR LOGIC - // Function Name: onButtonClicked() - // - // Description: - // -Fired when the app button is pressed. - // - // Relevant Variables: - // -WALLET_QML_SOURCE: The path to the Wallet QML - // -onWalletScreen: true/false depending on whether we're looking at the app. - var WALLET_QML_SOURCE = "hifi/commerce/wallet/Wallet.qml"; - var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml"; - var onWalletScreen = false; - function onButtonClicked() { - if (!tablet) { - print("Warning in buttonClicked(): 'tablet' undefined!"); - return; - } - if (onWalletScreen) { - // for toolbar-mode: go back to home screen, this will close the window. - tablet.gotoHomeScreen(); - } else { - tablet.loadQMLSource(WALLET_QML_SOURCE); - } - } - - // Function Name: sendToQml() - // - // Description: - // -Use this function to send a message to the QML (i.e. to change appearances). The "message" argument is what is sent to - // the QML in the format "{method, params}", like json-rpc. See also fromQml(). - function sendToQml(message) { - tablet.sendToQml(message); - } - var sendMoneyRecipient; var sendMoneyParticleEffectUpdateTimer; var particleEffectTimestamp; @@ -419,28 +387,28 @@ // -Called when a message is received from SpectatorCamera.qml. The "message" argument is what is sent from the QML // in the format "{method, params}", like json-rpc. See also sendToQml(). var isHmdPreviewDisabled = true; + var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml"; var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); - function fromQml(message) { switch (message.method) { case 'passphrasePopup_cancelClicked': case 'needsLogIn_cancelClicked': - tablet.gotoHomeScreen(); + ui.close(); break; case 'walletSetup_cancelClicked': switch (message.referrer) { case '': // User clicked "Wallet" app case undefined: case null: - tablet.gotoHomeScreen(); + ui.close(); break; case 'purchases': case 'marketplace cta': case 'mainPage': - tablet.gotoWebScreen(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL); + ui.openNewApp(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL); break; default: // User needs to return to an individual marketplace item URL - tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.referrer, MARKETPLACES_INJECT_SCRIPT_URL); + ui.openNewApp(MARKETPLACE_URL + '/items/' + message.referrer, MARKETPLACES_INJECT_SCRIPT_URL); break; } break; @@ -451,27 +419,18 @@ break; // do nothing here, handled in marketplaces.js case 'maybeEnableHmdPreview': break; // do nothing here, handled in marketplaces.js - case 'passphraseReset': - onButtonClicked(); - onButtonClicked(); - break; - case 'walletReset': - Settings.setValue("isFirstUseOfPurchases", true); - onButtonClicked(); - onButtonClicked(); - break; case 'transactionHistory_linkClicked': - tablet.gotoWebScreen(message.marketplaceLink, MARKETPLACES_INJECT_SCRIPT_URL); + ui.openNewApp(message.marketplaceLink, MARKETPLACES_INJECT_SCRIPT_URL); break; case 'goToPurchases_fromWalletHome': case 'goToPurchases': - tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH); + ui.openNewApp(MARKETPLACE_PURCHASES_QML_PATH); break; case 'goToMarketplaceMainPage': - tablet.gotoWebScreen(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL); + ui.openNewApp(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL); break; case 'goToMarketplaceItemPage': - tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL); + ui.openNewApp(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL); break; case 'refreshConnections': print('Refreshing Connections...'); @@ -526,93 +485,43 @@ } } - // Function Name: wireEventBridge() - // - // Description: - // -Used to connect/disconnect the script's response to the tablet's "fromQml" signal. Set the "on" argument to enable or - // disable to event bridge. - // - // Relevant Variables: - // -hasEventBridge: true/false depending on whether we've already connected the event bridge. - var hasEventBridge = false; - function wireEventBridge(on) { - if (!tablet) { - print("Warning in wireEventBridge(): 'tablet' undefined!"); - return; - } - if (on) { - if (!hasEventBridge) { - tablet.fromQml.connect(fromQml); - hasEventBridge = true; - } - } else { - if (hasEventBridge) { - tablet.fromQml.disconnect(fromQml); - hasEventBridge = false; - } - } + function walletOpened() { + Users.usernameFromIDReply.connect(usernameFromIDReply); + Controller.mousePressEvent.connect(handleMouseEvent); + Controller.mouseMoveEvent.connect(handleMouseMoveEvent); + triggerMapping.enable(); + triggerPressMapping.enable(); } - // Function Name: onTabletScreenChanged() - // - // Description: - // -Called when the TabletScriptingInterface::screenChanged() signal is emitted. The "type" argument can be either the string - // value of "Home", "Web", "Menu", "QML", or "Closed". The "url" argument is only valid for Web and QML. - function onTabletScreenChanged(type, url) { - onWalletScreen = (type === "QML" && url === WALLET_QML_SOURCE); - wireEventBridge(onWalletScreen); - // Change button to active when window is first openend, false otherwise. - if (button) { - button.editProperties({ isActive: onWalletScreen }); - } - - if (onWalletScreen) { - if (!isWired) { - Users.usernameFromIDReply.connect(usernameFromIDReply); - Controller.mousePressEvent.connect(handleMouseEvent); - Controller.mouseMoveEvent.connect(handleMouseMoveEvent); - triggerMapping.enable(); - triggerPressMapping.enable(); - } - isWired = true; - } else { - off(); - } + function walletClosed() { + off(); } // // Manage the connection between the button and the window. // - var button; - var buttonName = "WALLET"; - var tablet = null; + var BUTTON_NAME = "WALLET"; + var WALLET_QML_SOURCE = "hifi/commerce/wallet/Wallet.qml"; var walletEnabled = Settings.getValue("commerce", true); function startup() { + ui = new AppUi({ + buttonName: BUTTON_NAME, + sortOrder: 10, + home: WALLET_QML_SOURCE, + onOpened: walletOpened, + onClosed: walletClosed, + onMessage: fromQml + }); GlobalServices.myUsernameChanged.connect(onUsernameChanged); - if (walletEnabled) { - tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - button = tablet.addButton({ - text: buttonName, - icon: "icons/tablet-icons/wallet-i.svg", - activeIcon: "icons/tablet-icons/wallet-a.svg", - sortOrder: 10 - }); - button.clicked.connect(onButtonClicked); - tablet.screenChanged.connect(onTabletScreenChanged); - } } - var isWired = false; var isUpdateOverlaysWired = false; function off() { - if (isWired) { - Users.usernameFromIDReply.disconnect(usernameFromIDReply); - Controller.mousePressEvent.disconnect(handleMouseEvent); - Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); - triggerMapping.disable(); - triggerPressMapping.disable(); + Users.usernameFromIDReply.disconnect(usernameFromIDReply); + Controller.mousePressEvent.disconnect(handleMouseEvent); + Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); + triggerMapping.disable(); + triggerPressMapping.disable(); - isWired = false; - } if (isUpdateOverlaysWired) { Script.update.disconnect(updateOverlays); isUpdateOverlaysWired = false; @@ -621,15 +530,7 @@ } function shutdown() { GlobalServices.myUsernameChanged.disconnect(onUsernameChanged); - button.clicked.disconnect(onButtonClicked); - tablet.removeButton(button); deleteSendMoneyParticleEffect(); - if (tablet) { - tablet.screenChanged.disconnect(onTabletScreenChanged); - if (onWalletScreen) { - tablet.gotoHomeScreen(); - } - } off(); } diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 907dcd75c1..fe952ab1a7 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -111,7 +111,7 @@ function onMessage(message) { || (!HMD.active && Settings.getValue("desktopTabletBecomesToolbar", true))) { Desktop.show("hifi/dialogs/GeneralPreferencesDialog.qml", "GeneralPreferencesDialog"); } else { - ui.openOnTop("hifi/tablet/TabletGeneralPreferences.qml"); + ui.openNewAppOnTop("hifi/tablet/TabletGeneralPreferences.qml"); } break; case 'captureStillAndGif': From a1c49bd2b61199de3ce6143079be9c67db434a50 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 12 Sep 2018 11:18:26 +1200 Subject: [PATCH 059/259] Hide mini tablet if lasering or grabbing something else --- scripts/system/html/js/miniTablet.js | 21 +++++++- scripts/system/miniTablet.js | 74 +++++++++++++++++++++------- 2 files changed, 76 insertions(+), 19 deletions(-) diff --git a/scripts/system/html/js/miniTablet.js b/scripts/system/html/js/miniTablet.js index a966cbb139..e44a454a80 100644 --- a/scripts/system/html/js/miniTablet.js +++ b/scripts/system/html/js/miniTablet.js @@ -18,6 +18,7 @@ var // EventBridge READY_MESSAGE = "ready", // Engine <== Dialog HOVER_MESSAGE = "hover", // Engine <== Dialog + UNHOVER_MESSAGE = "unhover", // Engine <== Dialog MUTE_MESSAGE = "mute", // Engine <=> Dialog GOTO_MESSAGE = "goto", // Engine <=> Dialog EXPAND_MESSAGE = "expand", // Engine <== Dialog @@ -73,9 +74,24 @@ } } + function onBodyHover() { + EventBridge.emitWebEvent(JSON.stringify({ + type: HOVER_MESSAGE, + target: "body" + })); + } + + function onBodyUnhover() { + EventBridge.emitWebEvent(JSON.stringify({ + type: UNHOVER_MESSAGE, + target: "body" + })); + } + function onButtonHover() { EventBridge.emitWebEvent(JSON.stringify({ - type: HOVER_MESSAGE + type: HOVER_MESSAGE, + target: "button" })); clearUnhover(); } @@ -128,6 +144,9 @@ connectEventBridge(); + document.body.addEventListener("mouseenter", onBodyHover, false); + document.body.addEventListener("mouseleave", onBodyUnhover, false); + muteButton.addEventListener("mouseenter", onButtonHover, false); gotoButton.addEventListener("mouseenter", onButtonHover, false); expandButton.addEventListener("mouseenter", onButtonHover, false); diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index df428c9c52..2482efec8d 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -8,13 +8,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global getTabletWidthFromSettings */ +/* global getTabletWidthFromSettings, TRIGGER_OFF_VALUE */ (function () { "use strict"; Script.include("./libraries/utils.js"); + Script.include("./libraries/controllerDispatcherUtils.js"); var UI, ui = null, @@ -147,9 +148,13 @@ miniTargetWidth, miniTargetLocalRotation, + // Laser pointing at. + isBodyHovered = false, + // EventBridge. READY_MESSAGE = "ready", // Engine <== Dialog HOVER_MESSAGE = "hover", // Engine <== Dialog + UNHOVER_MESSAGE = "unhover", // Engine <== Dialog MUTE_MESSAGE = "mute", // Engine <=> Dialog GOTO_MESSAGE = "goto", // Engine <=> Dialog EXPAND_MESSAGE = "expand", // Engine <== Dialog @@ -196,8 +201,19 @@ setGotoIcon(); break; case HOVER_MESSAGE: - // Audio feedback. - playSound(hoverSound, HOVER_VOLUME); + if (message.target === "body") { + // Laser status. + isBodyHovered = true; + } else if (message.target === "button") { + // Audio feedback. + playSound(hoverSound, HOVER_VOLUME); + } + break; + case UNHOVER_MESSAGE: + if (message.target === "body") { + // Laser status. + isBodyHovered = false; + } break; case MUTE_MESSAGE: // Toggle mute. @@ -253,6 +269,10 @@ }; } + function isLaserPointingAt() { + return isBodyHovered; + } + function show(hand) { var initialScale = 0.01; // Start very small. @@ -420,6 +440,7 @@ getUIPositionAndRotation: getUIPositionAndRotation, getMiniTabletID: getMiniTabletID, getMiniTabletProperties: getMiniTabletProperties, + isLaserPointingAt: isLaserPointingAt, updateMutedStatus: updateMutedStatus, show: show, size: size, @@ -509,32 +530,49 @@ function shouldShowMini(hand) { // Should show mini tablet if it would be oriented toward the camera. - var pose, + var show, + pose, jointIndex, handPosition, handOrientation, uiPositionAndOrientation, miniPosition, miniOrientation, - miniToCameraDirection; + miniToCameraDirection, + cameraToHand; + // Shouldn't show mini tablet if hand isn't being controlled. pose = Controller.getPoseValue(hand === LEFT_HAND ? Controller.Standard.LeftHand : Controller.Standard.RightHand); - if (!pose.valid) { - return false; + show = pose.valid; + + // Shouldn't show mini tablet on hand if that hand's trigger is pressed (i.e., laser is searching or grabbing + // something) or the other hand's trigger is pressed unless it is pointing at the mini tablet. + if (show) { + show = Controller.getValue(hand === LEFT_HAND ? Controller.Standard.LT : Controller.Standard.RT) < + TRIGGER_OFF_VALUE + && (Controller.getValue(hand === LEFT_HAND ? Controller.Standard.RT : Controller.Standard.LT) < + TRIGGER_OFF_VALUE || ui.isLaserPointingAt()); + } + + // Should show mini tablet if it would be oriented toward the camera. + if (show) { + jointIndex = handJointIndex(hand); + handPosition = Vec3.sum(MyAvatar.position, + Vec3.multiplyQbyV(MyAvatar.orientation, MyAvatar.getAbsoluteJointTranslationInObjectFrame(jointIndex))); + handOrientation = + Quat.multiply(MyAvatar.orientation, MyAvatar.getAbsoluteJointRotationInObjectFrame(jointIndex)); + uiPositionAndOrientation = ui.getUIPositionAndRotation(hand); + miniPosition = Vec3.sum(handPosition, Vec3.multiply(MyAvatar.scale, + Vec3.multiplyQbyV(handOrientation, uiPositionAndOrientation.position))); + miniOrientation = Quat.multiply(handOrientation, uiPositionAndOrientation.rotation); + miniToCameraDirection = Vec3.normalize(Vec3.subtract(Camera.position, miniPosition)); + show = Vec3.dot(miniToCameraDirection, Quat.getForward(miniOrientation)) > MIN_HAND_CAMERA_ANGLE_COS; + cameraToHand = -Vec3.dot(miniToCameraDirection, Quat.getForward(Camera.orientation)); } - jointIndex = handJointIndex(hand); - handPosition = Vec3.sum(MyAvatar.position, - Vec3.multiplyQbyV(MyAvatar.orientation, MyAvatar.getAbsoluteJointTranslationInObjectFrame(jointIndex))); - handOrientation = Quat.multiply(MyAvatar.orientation, MyAvatar.getAbsoluteJointRotationInObjectFrame(jointIndex)); - uiPositionAndOrientation = ui.getUIPositionAndRotation(hand); - miniPosition = Vec3.sum(handPosition, Vec3.multiply(MyAvatar.scale, - Vec3.multiplyQbyV(handOrientation, uiPositionAndOrientation.position))); - miniOrientation = Quat.multiply(handOrientation, uiPositionAndOrientation.rotation); - miniToCameraDirection = Vec3.normalize(Vec3.subtract(Camera.position, miniPosition)); return { - show: Vec3.dot(miniToCameraDirection, Quat.getForward(miniOrientation)) > MIN_HAND_CAMERA_ANGLE_COS, - cameraToHand: -Vec3.dot(miniToCameraDirection, Quat.getForward(Camera.orientation)) + show: show, + cameraToHand: cameraToHand }; } From 6d345e506ba64d2ca2ff0e12acac0352c1d73600 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 11 Sep 2018 19:42:12 -0700 Subject: [PATCH 060/259] Marketplaces.js WIP --- scripts/system/html/js/marketplacesInject.js | 1 + scripts/system/marketplaces/marketplaces.js | 231 +++++-------------- 2 files changed, 61 insertions(+), 171 deletions(-) diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 9b91d06d41..a68556d771 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -708,6 +708,7 @@ function onLoad() { EventBridge.scriptEventReceived.connect(function (message) { + message = JSON.stringify(message); if (message.slice(0, CAN_WRITE_ASSETS.length) === CAN_WRITE_ASSETS) { canWriteAssets = message.slice(-4) === "true"; } else if (message.slice(0, CLARA_IO_CANCEL_DOWNLOAD.length) === CLARA_IO_CANCEL_DOWNLOAD) { diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 7b4f05193f..66ddd0f09f 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -16,14 +16,11 @@ var selectionDisplay = null; // for gridTool.js to ignore (function () { // BEGIN LOCAL_SCOPE - - Script.include("/~/system/libraries/WebTablet.js"); + var AppUi = Script.require('appUi'); Script.include("/~/system/libraries/gridTool.js"); Script.include("/~/system/libraries/connectionUtils.js"); var METAVERSE_SERVER_URL = Account.metaverseServerURL; - var MARKETPLACE_URL = METAVERSE_SERVER_URL + "/marketplace"; - var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); var MARKETPLACE_CHECKOUT_QML_PATH = "hifi/commerce/checkout/Checkout.qml"; @@ -32,9 +29,6 @@ var selectionDisplay = null; // for gridTool.js to ignore var MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH = "commerce/inspectionCertificate/InspectionCertificate.qml"; var REZZING_SOUND = SoundCache.getSound(Script.resolvePath("../assets/sounds/rezzing.wav")); - var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; - // var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; - // Event bridge messages. var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD"; var CLARA_IO_STATUS = "CLARA.IO STATUS"; @@ -58,45 +52,13 @@ var selectionDisplay = null; // for gridTool.js to ignore if (id === messageBox && button === CANCEL_BUTTON) { isDownloadBeingCancelled = true; messageBox = null; - tablet.emitScriptEvent(CLARA_IO_CANCEL_DOWNLOAD); - } - } - - var onMarketplaceScreen = false; - var onCommerceScreen = false; - - var debugCheckout = false; - var debugError = false; - function showMarketplace() { - if (!debugCheckout) { - UserActivityLogger.openedMarketplace(); - tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); - } else { - tablet.pushOntoStack(MARKETPLACE_CHECKOUT_QML_PATH); - sendToQml({ - method: 'updateCheckoutQML', params: { - itemId: '424611a2-73d0-4c03-9087-26a6a279257b', - itemName: '2018-02-15 Finnegon', - itemPrice: (debugError ? 10 : 3), - itemHref: 'http://devmpassets.highfidelity.com/424611a2-73d0-4c03-9087-26a6a279257b-v1/finnigon.fst', - categories: ["Miscellaneous"] - } - }); - } - } - - function messagesWaiting(isWaiting) { - if (marketplaceButton) { - marketplaceButton.editProperties({ - icon: (isWaiting ? WAITING_ICON : NORMAL_ICON), - activeIcon: (isWaiting ? WAITING_ACTIVE : NORMAL_ACTIVE) - }); + ui.sendToHtml(CLARA_IO_CANCEL_DOWNLOAD); } } function onCanWriteAssetsChanged() { var message = CAN_WRITE_ASSETS + " " + Entities.canWriteAssets(); - tablet.emitScriptEvent(message); + ui.sendToHtml(message); } @@ -119,13 +81,13 @@ var selectionDisplay = null; // for gridTool.js to ignore } function openWallet() { - tablet.pushOntoStack(MARKETPLACE_WALLET_QML_PATH); + ui.openNewApp(MARKETPLACE_WALLET_QML_PATH); } function setCertificateInfo(currentEntityWithContextOverlay, itemCertificateId) { - wireEventBridge(true); + ui.wireEventBridge(true); var certificateId = itemCertificateId || (Entities.getEntityProperties(currentEntityWithContextOverlay, ['certificateID']).certificateID); - sendToQml({ + ui.sendMessage({ method: 'inspectionCertificate_setCertificateId', entityId: currentEntityWithContextOverlay, certificateId: certificateId @@ -134,13 +96,13 @@ var selectionDisplay = null; // for gridTool.js to ignore function onUsernameChanged() { if (onMarketplaceScreen) { - tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); + ui.openNewApp(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); } } var userHasUpdates = false; function sendCommerceSettings() { - tablet.emitScriptEvent(JSON.stringify({ + ui.sendToHtml({ type: "marketplaces", action: "commerceSetting", data: { @@ -150,7 +112,7 @@ var selectionDisplay = null; // for gridTool.js to ignore metaverseServerURL: Account.metaverseServerURL, messagesWaiting: userHasUpdates } - })); + }); } // BEGIN AVATAR SELECTOR LOGIC @@ -323,7 +285,7 @@ var selectionDisplay = null; // for gridTool.js to ignore method: 'updateSelectedRecipientUsername', userName: username === "" ? "unknown username" : username }; - sendToQml(message); + ui.sendMessage(message); } } function handleClick(pickRay) { @@ -341,7 +303,7 @@ var selectionDisplay = null; // for gridTool.js to ignore displayName: '"' + AvatarList.getAvatar(avatarId).sessionDisplayName + '"', userName: '' }; - sendToQml(message); + ui.sendMessage(message); ExtendedOverlay.some(function (overlay) { var id = overlay.key; @@ -627,9 +589,9 @@ var selectionDisplay = null; // for gridTool.js to ignore var filterText; // Used for updating Purchases QML function onMessage(message) { if (message === GOTO_DIRECTORY) { - tablet.gotoWebScreen(MARKETPLACES_URL, MARKETPLACES_INJECT_SCRIPT_URL); + ui.openNewApp(MARKETPLACES_URL, MARKETPLACES_INJECT_SCRIPT_URL); } else if (message === QUERY_CAN_WRITE_ASSETS) { - tablet.emitScriptEvent(CAN_WRITE_ASSETS + " " + Entities.canWriteAssets()); + ui.sendToHtml(CAN_WRITE_ASSETS + " " + Entities.canWriteAssets()); } else if (message === WARN_USER_NO_PERMISSIONS) { Window.alert(NO_PERMISSIONS_ERROR_MESSAGE); } else if (message.slice(0, CLARA_IO_STATUS.length) === CLARA_IO_STATUS) { @@ -655,9 +617,9 @@ var selectionDisplay = null; // for gridTool.js to ignore } else { var parsedJsonMessage = JSON.parse(message); if (parsedJsonMessage.type === "CHECKOUT") { - wireEventBridge(true); - tablet.pushOntoStack(MARKETPLACE_CHECKOUT_QML_PATH); - sendToQml({ + ui.wireEventBridge(true); + ui.openNewApp(MARKETPLACE_CHECKOUT_QML_PATH); + ui.sendMessage({ method: 'updateCheckoutQML', params: parsedJsonMessage }); @@ -666,12 +628,12 @@ var selectionDisplay = null; // for gridTool.js to ignore } else if (parsedJsonMessage.type === "PURCHASES") { referrerURL = parsedJsonMessage.referrerURL; filterText = ""; - tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH); + ui.openNewApp(MARKETPLACE_PURCHASES_QML_PATH); } else if (parsedJsonMessage.type === "LOGIN") { openLoginWindow(); } else if (parsedJsonMessage.type === "WALLET_SETUP") { - wireEventBridge(true); - sendToQml({ + ui.wireEventBridge(true); + ui.sendMessage({ method: 'updateWalletReferrer', referrer: "marketplace cta" }); @@ -679,40 +641,14 @@ var selectionDisplay = null; // for gridTool.js to ignore } else if (parsedJsonMessage.type === "MY_ITEMS") { referrerURL = MARKETPLACE_URL_INITIAL; filterText = ""; - tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH); - wireEventBridge(true); - sendToQml({ + ui.openNewApp(MARKETPLACE_PURCHASES_QML_PATH); + ui.wireEventBridge(true); + ui.sendMessage({ method: 'purchases_showMyItems' }); } } } - - function onButtonClicked() { - if (!tablet) { - print("Warning in buttonClicked(): 'tablet' undefined!"); - return; - } - if (onMarketplaceScreen || onCommerceScreen) { - // for toolbar-mode: go back to home screen, this will close the window. - tablet.gotoHomeScreen(); - } else { - if (HMD.tabletID) { - Entities.editEntity(HMD.tabletID, { textures: JSON.stringify({ "tex.close": HOME_BUTTON_TEXTURE }) }); - } - showMarketplace(); - } - } - - // Function Name: sendToQml() - // - // Description: - // -Use this function to send a message to the QML (i.e. to change appearances). The "message" argument is what is sent to - // the QML in the format "{method, params}", like json-rpc. See also fromQml(). - function sendToQml(message) { - tablet.sendToQml(message); - } - var sendAssetRecipient; var sendAssetParticleEffectUpdateTimer; var particleEffectTimestamp; @@ -813,44 +749,40 @@ var selectionDisplay = null; // for gridTool.js to ignore openWallet(); break; case 'purchases_walletNotSetUp': - wireEventBridge(true); - sendToQml({ + ui.wireEventBridge(true); + ui.sendMessage({ method: 'updateWalletReferrer', referrer: "purchases" }); openWallet(); break; case 'checkout_walletNotSetUp': - wireEventBridge(true); - sendToQml({ + ui.wireEventBridge(true); + ui.sendMessage({ method: 'updateWalletReferrer', referrer: message.referrer === "itemPage" ? message.itemId : message.referrer }); openWallet(); break; case 'checkout_cancelClicked': - tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.params, MARKETPLACES_INJECT_SCRIPT_URL); - // TODO: Make Marketplace a QML app that's a WebView wrapper so we can use the app stack. - // I don't think this is trivial to do since we also want to inject some JS into the DOM. - //tablet.popFromStack(); + ui.openNewApp(MARKETPLACE_URL + '/items/' + message.params, MARKETPLACES_INJECT_SCRIPT_URL); break; case 'header_goToPurchases': case 'checkout_goToPurchases': referrerURL = MARKETPLACE_URL_INITIAL; filterText = message.filterText; - tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH); + ui.openNewApp(MARKETPLACE_PURCHASES_QML_PATH); break; case 'checkout_itemLinkClicked': - tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL); + ui.openNewApp(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL); break; case 'checkout_continueShopping': - tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); - //tablet.popFromStack(); + ui.openNewApp(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); break; case 'purchases_itemInfoClicked': var itemId = message.itemId; if (itemId && itemId !== "") { - tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + itemId, MARKETPLACES_INJECT_SCRIPT_URL); + ui.openNewApp(MARKETPLACE_URL + '/items/' + itemId, MARKETPLACES_INJECT_SCRIPT_URL); } break; case 'checkout_rezClicked': @@ -859,13 +791,13 @@ var selectionDisplay = null; // for gridTool.js to ignore break; case 'header_marketplaceImageClicked': case 'purchases_backClicked': - tablet.gotoWebScreen(message.referrerURL, MARKETPLACES_INJECT_SCRIPT_URL); + ui.openNewApp(message.referrerURL, MARKETPLACES_INJECT_SCRIPT_URL); break; case 'purchases_goToMarketplaceClicked': - tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); + ui.openNewApp(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); break; case 'updateItemClicked': - tablet.gotoWebScreen(message.upgradeUrl + "?edition=" + message.itemEdition, + ui.openNewApp(message.upgradeUrl + "?edition=" + message.itemEdition, MARKETPLACES_INJECT_SCRIPT_URL); break; case 'giftAsset': @@ -873,7 +805,7 @@ var selectionDisplay = null; // for gridTool.js to ignore break; case 'passphrasePopup_cancelClicked': case 'needsLogIn_cancelClicked': - tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); + ui.openNewApp(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); break; case 'needsLogIn_loginClicked': openLoginWindow(); @@ -894,26 +826,26 @@ var selectionDisplay = null; // for gridTool.js to ignore maybeEnableHMDPreview(); break; case 'purchases_openGoTo': - tablet.loadQMLSource("hifi/tablet/TabletAddressDialog.qml"); + ui.openNewApp("hifi/tablet/TabletAddressDialog.qml"); break; case 'purchases_itemCertificateClicked': setCertificateInfo("", message.itemCertificateId); break; case 'inspectionCertificate_closeClicked': - tablet.gotoHomeScreen(); + ui.close(); break; case 'inspectionCertificate_requestOwnershipVerification': ContextOverlay.requestOwnershipVerification(message.entity); break; case 'inspectionCertificate_showInMarketplaceClicked': - tablet.gotoWebScreen(message.marketplaceUrl, MARKETPLACES_INJECT_SCRIPT_URL); + ui.openNewApp(message.marketplaceUrl, MARKETPLACES_INJECT_SCRIPT_URL); break; case 'header_myItemsClicked': referrerURL = MARKETPLACE_URL_INITIAL; filterText = ""; - tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH); - wireEventBridge(true); - sendToQml({ + ui.openNewApp(MARKETPLACE_PURCHASES_QML_PATH); + ui.wireEventBridge(true); + ui.sendMessage({ method: 'purchases_showMyItems' }); break; @@ -949,7 +881,7 @@ var selectionDisplay = null; // for gridTool.js to ignore case 'wallet_availableUpdatesReceived': case 'purchases_availableUpdatesReceived': userHasUpdates = message.numUpdates > 0; - messagesWaiting(userHasUpdates); + ui.messagesWaiting(userHasUpdates); break; case 'purchases_updateWearables': var currentlyWornWearables = []; @@ -968,7 +900,7 @@ var selectionDisplay = null; // for gridTool.js to ignore } } - sendToQml({ method: 'updateWearables', wornWearables: currentlyWornWearables }); + ui.sendMessage({ method: 'updateWearables', wornWearables: currentlyWornWearables }); break; case 'sendAsset_sendPublicly': if (message.assetName !== "") { @@ -998,38 +930,12 @@ var selectionDisplay = null; // for gridTool.js to ignore } } - // Function Name: wireEventBridge() - // - // Description: - // -Used to connect/disconnect the script's response to the tablet's "fromQml" signal. Set the "on" argument to enable or - // disable to event bridge. - // - // Relevant Variables: - // -hasEventBridge: true/false depending on whether we've already connected the event bridge. - var hasEventBridge = false; - function wireEventBridge(on) { - if (!tablet) { - print("Warning in wireEventBridge(): 'tablet' undefined!"); - return; - } - if (on) { - if (!hasEventBridge) { - tablet.fromQml.connect(fromQml); - hasEventBridge = true; - } - } else { - if (hasEventBridge) { - tablet.fromQml.disconnect(fromQml); - hasEventBridge = false; - } - } - } - // Function Name: onTabletScreenChanged() // // Description: // -Called when the TabletScriptingInterface::screenChanged() signal is emitted. The "type" argument can be either the string // value of "Home", "Web", "Menu", "QML", or "Closed". The "url" argument is only valid for Web and QML. + var onMarketplaceScreen = false; var onWalletScreen = false; var onCommerceScreen = false; function onTabletScreenChanged(type, url) { @@ -1044,20 +950,18 @@ var selectionDisplay = null; // for gridTool.js to ignore onCommerceScreen = onCommerceScreenNow; onWalletScreen = onWalletScreenNow; - wireEventBridge(onMarketplaceScreen || onCommerceScreen || onWalletScreen); + ui.wireEventBridge(onMarketplaceScreen || onCommerceScreen || onWalletScreen); if (url === MARKETPLACE_PURCHASES_QML_PATH) { - sendToQml({ + ui.sendMessage({ method: 'updatePurchases', referrerURL: referrerURL, filterText: filterText }); } - // for toolbar mode: change button to active when window is first openend, false otherwise. - if (marketplaceButton) { - marketplaceButton.editProperties({ isActive: (onMarketplaceScreen || onCommerceScreen) && !onWalletScreen }); - } + ui.buttonActive((onMarketplaceScreen || onCommerceScreen) && !onWalletScreen); + if (type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1) { ContextOverlay.isInMarketplaceInspectionMode = true; } else { @@ -1075,38 +979,32 @@ var selectionDisplay = null; // for gridTool.js to ignore isWired = true; Wallet.refreshWalletStatus(); } else { - off(); - sendToQml({ + ui.sendMessage({ method: 'inspectionCertificate_resetCert' }); + off(); } } // // Manage the connection between the button and the window. // - var marketplaceButton; - var buttonName = "MARKET"; - var tablet = null; - var NORMAL_ICON = "icons/tablet-icons/market-i.svg"; - var NORMAL_ACTIVE = "icons/tablet-icons/market-a.svg"; - var WAITING_ICON = "icons/tablet-icons/market-i-msg.svg"; - var WAITING_ACTIVE = "icons/tablet-icons/market-a-msg.svg"; + var BUTTON_NAME = "MARKET"; + var MARKETPLACE_URL = METAVERSE_SERVER_URL + "/marketplace"; + var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. function startup() { - tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - marketplaceButton = tablet.addButton({ - icon: NORMAL_ICON, - activeIcon: NORMAL_ACTIVE, - text: buttonName, - sortOrder: 9 + ui = new AppUi({ + buttonName: BUTTON_NAME, + sortOrder: 9, + inject: MARKETPLACES_INJECT_SCRIPT_URL, + home: MARKETPLACE_URL_INITIAL, + onMessage: fromQml }); ContextOverlay.contextOverlayClicked.connect(setCertificateInfo); Entities.canWriteAssetsChanged.connect(onCanWriteAssetsChanged); GlobalServices.myUsernameChanged.connect(onUsernameChanged); marketplaceButton.clicked.connect(onButtonClicked); - tablet.screenChanged.connect(onTabletScreenChanged); - tablet.webEventReceived.connect(onMessage); Wallet.walletStatusChanged.connect(sendCommerceSettings); Window.messageBoxClosed.connect(onMessageBoxClosed); @@ -1124,6 +1022,7 @@ var selectionDisplay = null; // for gridTool.js to ignore isWired = false; } + if (isUpdateOverlaysWired) { Script.update.disconnect(updateOverlays); isUpdateOverlaysWired = false; @@ -1138,18 +1037,8 @@ var selectionDisplay = null; // for gridTool.js to ignore Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged); GlobalServices.myUsernameChanged.disconnect(onUsernameChanged); marketplaceButton.clicked.disconnect(onButtonClicked); - tablet.removeButton(marketplaceButton); - tablet.webEventReceived.disconnect(onMessage); Wallet.walletStatusChanged.disconnect(sendCommerceSettings); Window.messageBoxClosed.disconnect(onMessageBoxClosed); - - if (tablet) { - tablet.screenChanged.disconnect(onTabletScreenChanged); - if (onMarketplaceScreen) { - tablet.gotoHomeScreen(); - } - } - off(); } From 4d381b9d04ec5d93a58842ac5dbd0a8cb817487e Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 12 Sep 2018 10:01:41 -0700 Subject: [PATCH 061/259] Smooth other avatars positions --- interface/src/avatar/AvatarManager.cpp | 20 ++++++++++ interface/src/avatar/AvatarManager.h | 18 +++++++++ .../src/avatars-renderer/Avatar.cpp | 38 +++++++++++++++++++ .../src/avatars-renderer/Avatar.h | 19 ++++++++++ 4 files changed, 95 insertions(+) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index e9486b9def..26b3119c2f 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -78,6 +78,12 @@ AvatarManager::AvatarManager(QObject* parent) : removeAvatar(nodeID, KillAvatarReason::AvatarIgnored); } }); + + const float AVATAR_TRANSIT_MAX_DISTANCE = 1.0f; + const int AVATAR_TRANSIT_FRAME_COUNT = 20; + + _avatarTransitMaxDistance = AVATAR_TRANSIT_MAX_DISTANCE; + _avatarTransitFrameCount = AVATAR_TRANSIT_FRAME_COUNT; } AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { @@ -250,6 +256,20 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { if (inView && avatar->hasNewJointData()) { numAvatarsUpdated++; } + // smooth other avatars positions + { + float oneFrameDistance = glm::length(avatar->_globalPosition - avatar->_lastPosition); + if (oneFrameDistance > _avatarTransitMaxDistance) { + avatar->_transit.start(avatar->_lastPosition, avatar->_globalPosition, _avatarTransitFrameCount, _avatarTransitFramePerMeter, _avatarTransitDistanceBased); + } + if (avatar->_transit.isTransiting()) { + glm::vec3 nextPosition; + if (avatar->_transit.getNextPosition(nextPosition)) { + avatar->setWorldPosition(nextPosition); + } + } + } + avatar->simulate(deltaTime, inView); avatar->updateRenderItem(renderTransaction); avatar->updateSpaceProxy(workloadTransaction); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index bcdfc064bd..dd2c431cec 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -178,6 +178,15 @@ public: */ Q_INVOKABLE QVariantMap getPalData(const QList specificAvatarIdentifiers = QList()); + Q_INVOKABLE void setAvatarTransitDistanceBased(bool isDistanceBased) { _avatarTransitDistanceBased = isDistanceBased; } + Q_INVOKABLE void setAvatarTransitMaxDistance(float maxDistance) { _avatarTransitMaxDistance = maxDistance; } + Q_INVOKABLE void setAvatarTransitFrameCount(int frameCount) { _avatarTransitFrameCount = frameCount; } + Q_INVOKABLE void setAvatarTransitFramePerMeter(int frameCount) { _avatarTransitFramePerMeter = frameCount; } + Q_INVOKABLE bool getAvatarTransitDistanceBased() { return _avatarTransitDistanceBased; } + Q_INVOKABLE float getAvatarTransitMaxDistance() { return _avatarTransitMaxDistance; } + Q_INVOKABLE int getAvatarTransitFrameCount() { return _avatarTransitFrameCount; } + Q_INVOKABLE int getAvatarTransitFramePerMeter() { return _avatarTransitFramePerMeter; } + float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); } int getIdentityRequestsSent() const { return _identityRequestsSent; } @@ -205,6 +214,7 @@ private: void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override; QVector _avatarsToFade; + QVector _avatarsToCopy; using SetOfOtherAvatars = std::set; SetOfOtherAvatars _avatarsToChangeInPhysics; @@ -224,6 +234,14 @@ private: mutable std::mutex _spaceLock; workload::SpacePointer _space; std::vector _spaceProxiesToDelete; + + // Other avatars smooth transit global configuration + + bool _avatarTransitDistanceBased { false }; + float _avatarTransitMaxDistance; + int _avatarTransitFrameCount; + int _avatarTransitFramePerMeter; + }; #endif // hifi_AvatarManager_h diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 4ffccefe61..f0e89fcd7c 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -113,6 +113,43 @@ void Avatar::setShowNamesAboveHeads(bool show) { showNamesAboveHeads = show; } +void AvatarTransit::start(const glm::vec3& startPosition, const glm::vec3& endPosition, int totalFrames, int framesPerMeter, bool isDistanceBased) { + _startPosition = startPosition; + _endPosition = endPosition; + _step = 0; + if (!isDistanceBased) { + calculateSteps(totalFrames); + } else { + float distance = glm::length(_endPosition - _startPosition); + calculateSteps(framesPerMeter * distance); + } + + _isTransiting = true; +} + +void AvatarTransit::calculateSteps(int stepCount) { + glm::vec3 startPosition = _isTransiting ? _transitSteps[_step] : _startPosition; + _transitSteps.clear(); + glm::vec3 transitLine = _endPosition - _startPosition; + glm::vec3 direction = glm::normalize(transitLine); + glm::vec3 stepVector = (glm::length(transitLine) / stepCount) * direction; + for (auto i = 0; i < stepCount; i++) { + glm::vec3 localStep = _transitSteps.size() > 0 ? _transitSteps[i-1] + stepVector : _startPosition + stepVector; + _transitSteps.push_back(localStep); + } +} + +bool AvatarTransit::getNextPosition(glm::vec3& nextPosition) { + int lastIdx = _transitSteps.size() - 1; + _isTransiting = _step < lastIdx; + if (_isTransiting) { + _step++; + nextPosition = _transitSteps[_step]; + _currentPosition = nextPosition; + } + return _isTransiting; +} + Avatar::Avatar(QThread* thread) : _voiceSphereID(GeometryCache::UNKNOWN_ID) { @@ -480,6 +517,7 @@ void Avatar::simulate(float deltaTime, bool inView) { _skeletonModel->simulate(deltaTime, false); } _skeletonModelSimulationRate.increment(); + _lastPosition = _globalPosition; } // update animation for display name fade in/out diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 37c3d08c6c..2d940b8fd9 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -50,6 +50,22 @@ enum ScreenTintLayer { class Texture; +class AvatarTransit { +public: + AvatarTransit() {}; + void start(const glm::vec3& startPosition, const glm::vec3& endPosition, int totalFrames, int framesPerMeter, bool isDistanceBased); + bool getNextPosition(glm::vec3& nextPosition); + bool isTransiting() { return _isTransiting; }; +private: + void calculateSteps(int stepCount); + bool _isTransiting{ false }; + glm::vec3 _startPosition; + glm::vec3 _endPosition; + glm::vec3 _currentPosition; + std::vector _transitSteps; + int _step{ 0 }; +}; + class Avatar : public AvatarData, public scriptable::ModelProvider { Q_OBJECT @@ -518,6 +534,9 @@ protected: bool _isFading { false }; bool _reconstructSoftEntitiesJointMap { false }; float _modelScale { 1.0f }; + glm::vec3 _lastPosition; + AvatarTransit _transit; + static int _jointConesID; From b9fbcce40314aa882dac6096f0d23117b76d8bfb Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 12 Sep 2018 10:15:00 -0700 Subject: [PATCH 062/259] init frames per meter --- interface/src/avatar/AvatarManager.cpp | 4 +++- interface/src/avatar/AvatarManager.h | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 26b3119c2f..334cdc9608 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -81,9 +81,11 @@ AvatarManager::AvatarManager(QObject* parent) : const float AVATAR_TRANSIT_MAX_DISTANCE = 1.0f; const int AVATAR_TRANSIT_FRAME_COUNT = 20; + const int AVATAR_TRANSIT_FRAMES_PER_METER = 5; _avatarTransitMaxDistance = AVATAR_TRANSIT_MAX_DISTANCE; _avatarTransitFrameCount = AVATAR_TRANSIT_FRAME_COUNT; + _avatarTransitFramesPerMeter = AVATAR_TRANSIT_FRAMES_PER_METER; } AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { @@ -260,7 +262,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { { float oneFrameDistance = glm::length(avatar->_globalPosition - avatar->_lastPosition); if (oneFrameDistance > _avatarTransitMaxDistance) { - avatar->_transit.start(avatar->_lastPosition, avatar->_globalPosition, _avatarTransitFrameCount, _avatarTransitFramePerMeter, _avatarTransitDistanceBased); + avatar->_transit.start(avatar->_lastPosition, avatar->_globalPosition, _avatarTransitFrameCount, _avatarTransitFramesPerMeter, _avatarTransitDistanceBased); } if (avatar->_transit.isTransiting()) { glm::vec3 nextPosition; diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index dd2c431cec..867c49063f 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -181,11 +181,11 @@ public: Q_INVOKABLE void setAvatarTransitDistanceBased(bool isDistanceBased) { _avatarTransitDistanceBased = isDistanceBased; } Q_INVOKABLE void setAvatarTransitMaxDistance(float maxDistance) { _avatarTransitMaxDistance = maxDistance; } Q_INVOKABLE void setAvatarTransitFrameCount(int frameCount) { _avatarTransitFrameCount = frameCount; } - Q_INVOKABLE void setAvatarTransitFramePerMeter(int frameCount) { _avatarTransitFramePerMeter = frameCount; } + Q_INVOKABLE void setAvatarTransitFramesPerMeter(int frameCount) { _avatarTransitFramesPerMeter = frameCount; } Q_INVOKABLE bool getAvatarTransitDistanceBased() { return _avatarTransitDistanceBased; } Q_INVOKABLE float getAvatarTransitMaxDistance() { return _avatarTransitMaxDistance; } Q_INVOKABLE int getAvatarTransitFrameCount() { return _avatarTransitFrameCount; } - Q_INVOKABLE int getAvatarTransitFramePerMeter() { return _avatarTransitFramePerMeter; } + Q_INVOKABLE int getAvatarTransitFramesPerMeter() { return _avatarTransitFramesPerMeter; } float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); } int getIdentityRequestsSent() const { return _identityRequestsSent; } @@ -240,7 +240,7 @@ private: bool _avatarTransitDistanceBased { false }; float _avatarTransitMaxDistance; int _avatarTransitFrameCount; - int _avatarTransitFramePerMeter; + int _avatarTransitFramesPerMeter; }; From 889e033048819ce2cf4d7f49fcdb4a512c9377fd Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 12 Sep 2018 11:20:23 -0700 Subject: [PATCH 063/259] fix warnings --- libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index f0e89fcd7c..3dae9171a8 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -130,7 +130,7 @@ void AvatarTransit::start(const glm::vec3& startPosition, const glm::vec3& endPo void AvatarTransit::calculateSteps(int stepCount) { glm::vec3 startPosition = _isTransiting ? _transitSteps[_step] : _startPosition; _transitSteps.clear(); - glm::vec3 transitLine = _endPosition - _startPosition; + glm::vec3 transitLine = _endPosition - startPosition; glm::vec3 direction = glm::normalize(transitLine); glm::vec3 stepVector = (glm::length(transitLine) / stepCount) * direction; for (auto i = 0; i < stepCount; i++) { @@ -140,7 +140,7 @@ void AvatarTransit::calculateSteps(int stepCount) { } bool AvatarTransit::getNextPosition(glm::vec3& nextPosition) { - int lastIdx = _transitSteps.size() - 1; + int lastIdx = (int)_transitSteps.size() - 1; _isTransiting = _step < lastIdx; if (_isTransiting) { _step++; From 2943502c9b5d0d9630c97b76793ffa5b3c205561 Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Wed, 12 Sep 2018 16:36:04 -0300 Subject: [PATCH 064/259] Headset plug/unplug detection --- .../receiver/HeadsetStateReceiver.java | 2 -- interface/src/AndroidHelper.cpp | 8 +---- libraries/audio-client/src/AudioClient.cpp | 32 +++++++++++++++++-- libraries/audio-client/src/AudioClient.h | 11 +++++-- 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/receiver/HeadsetStateReceiver.java b/android/app/src/main/java/io/highfidelity/hifiinterface/receiver/HeadsetStateReceiver.java index 29bc1c49f2..5645912d73 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/receiver/HeadsetStateReceiver.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/receiver/HeadsetStateReceiver.java @@ -13,8 +13,6 @@ public class HeadsetStateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - - Log.d("[HEADSET] " , "BR - Wired headset on:" + audioManager.isWiredHeadsetOn()); notifyHeadsetOn(audioManager.isWiredHeadsetOn()); } } diff --git a/interface/src/AndroidHelper.cpp b/interface/src/AndroidHelper.cpp index 35bf094591..400085a62a 100644 --- a/interface/src/AndroidHelper.cpp +++ b/interface/src/AndroidHelper.cpp @@ -63,13 +63,7 @@ void AndroidHelper::notifyHeadsetOn(bool pluggedIn) { #if defined (Q_OS_ANDROID) auto audioClient = DependencyManager::get(); if (audioClient) { - QAudioDeviceInfo activeDev = audioClient->getActiveAudioDevice(QAudio::AudioInput); - Setting::Handle enableAEC(QStringList() << ANDROID_SETTINGS_GROUP << SETTING_AEC_KEY, false); - if ((pluggedIn || !enableAEC.get()) && !activeDev.isNull() && activeDev.deviceName() != VOICE_RECOGNITION) { - QMetaObject::invokeMethod(audioClient.get(), "switchAudioDevice", Q_ARG(QAudio::Mode, QAudio::AudioInput), Q_ARG(QString, VOICE_RECOGNITION)); - } else if ( (!pluggedIn && enableAEC.get()) && !activeDev.isNull() && activeDev.deviceName() != VOICE_COMMUNICATION) { - QMetaObject::invokeMethod(audioClient.get(), "switchAudioDevice", Q_ARG(QAudio::Mode, QAudio::AudioInput), Q_ARG(QString, VOICE_COMMUNICATION)); - } + QMetaObject::invokeMethod(audioClient.data(), "setHeadsetPluggedIn", Q_ARG(bool, pluggedIn)); } #endif } diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index e23b8ac3cd..f0ba0307cc 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -209,6 +209,7 @@ AudioClient::AudioClient() : _positionGetter(DEFAULT_POSITION_GETTER), #if defined(Q_OS_ANDROID) _checkInputTimer(this), + _isHeadsetPluggedIn(false), #endif _orientationGetter(DEFAULT_ORIENTATION_GETTER) { // avoid putting a lock in the device callback @@ -450,12 +451,14 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { #if defined (Q_OS_ANDROID) if (mode == QAudio::AudioInput) { - Setting::Handle enableAEC(QStringList() << ANDROID_SETTINGS_GROUP << SETTING_AEC_KEY, false); + Setting::Handle enableAEC(SETTING_AEC_KEY, false); bool aecEnabled = enableAEC.get(); + auto audioClient = DependencyManager::get(); + bool headsetOn = audioClient? audioClient->isHeadsetPluggedIn() : false ; auto inputDevices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); for (auto inputDevice : inputDevices) { - if ((aecEnabled && inputDevice.deviceName() == VOICE_COMMUNICATION) || - (!aecEnabled && inputDevice.deviceName() == VOICE_RECOGNITION)) { + if (((headsetOn || !aecEnabled) && inputDevice.deviceName() == VOICE_RECOGNITION) || + ((!headsetOn && aecEnabled) && inputDevice.deviceName() == VOICE_COMMUNICATION)) { return inputDevice; } } @@ -1632,6 +1635,29 @@ void AudioClient::checkInputTimeout() { #endif } +void AudioClient::setHeadsetPluggedIn(bool pluggedIn) { +#if defined(Q_OS_ANDROID) + if (pluggedIn == !_isHeadsetPluggedIn && !_inputDeviceInfo.isNull()) { + QAndroidJniObject brand = QAndroidJniObject::getStaticObjectField("android/os/Build", "BRAND"); + // some samsung phones needs more time to shutdown the previous input device + if (brand.toString().contains("samsung", Qt::CaseInsensitive)) { + switchInputToAudioDevice(QAudioDeviceInfo(), true); + QThread::msleep(200); + } + + Setting::Handle enableAEC(SETTING_AEC_KEY, false); + bool aecEnabled = enableAEC.get(); + + if ((pluggedIn || !aecEnabled) && _inputDeviceInfo.deviceName() != VOICE_RECOGNITION) { + switchAudioDevice(QAudio::AudioInput, VOICE_RECOGNITION); + } else if (!pluggedIn && aecEnabled && _inputDeviceInfo.deviceName() != VOICE_COMMUNICATION) { + switchAudioDevice(QAudio::AudioInput, VOICE_COMMUNICATION); + } + } + _isHeadsetPluggedIn = pluggedIn; +#endif +} + void AudioClient::outputNotify() { int recentUnfulfilled = _audioOutputIODevice.getRecentUnfulfilledReads(); if (recentUnfulfilled > 0) { diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index fa7ac40a16..b1ccb496b6 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -68,8 +68,7 @@ #define VOICE_RECOGNITION "voicerecognition" #define VOICE_COMMUNICATION "voicecommunication" -#define ANDROID_SETTINGS_GROUP "Android" -#define SETTING_AEC_KEY "aec" +#define SETTING_AEC_KEY "Android/aec" #endif class QAudioInput; @@ -176,6 +175,10 @@ public: static QString getWinDeviceName(wchar_t* guid); #endif +#if defined(Q_OS_ANDROID) + bool isHeadsetPluggedIn() { return _isHeadsetPluggedIn; } +#endif + public slots: void start(); void stop(); @@ -224,6 +227,9 @@ public slots: bool switchAudioDevice(QAudio::Mode mode, const QAudioDeviceInfo& deviceInfo = QAudioDeviceInfo()); bool switchAudioDevice(QAudio::Mode mode, const QString& deviceName); + // Qt opensles plugin is not able to detect when the headset is plugged in + void setHeadsetPluggedIn(bool pluggedIn); + float getInputVolume() const { return (_audioInput) ? (float)_audioInput->volume() : 0.0f; } void setInputVolume(float volume, bool emitSignal = true); void setReverb(bool reverb); @@ -285,6 +291,7 @@ private: #ifdef Q_OS_ANDROID QTimer _checkInputTimer; long _inputReadsSinceLastCheck = 0l; + bool _isHeadsetPluggedIn; #endif class Gate { From 01073ca18d378a8e04f67cabb1d786f373a74aa6 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 12 Sep 2018 15:32:51 -0700 Subject: [PATCH 065/259] Lots of changes; marketplaces.js working now --- .../ui/overlays/ContextOverlayInterface.cpp | 34 - .../src/ui/overlays/ContextOverlayInterface.h | 2 - scripts/modules/appUi.js | 175 +- scripts/system/commerce/wallet.js | 961 ++++---- scripts/system/html/js/marketplacesInject.js | 290 +-- scripts/system/marketplaces/marketplaces.js | 1994 +++++++++-------- scripts/system/pal.js | 2 +- 7 files changed, 1747 insertions(+), 1711 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index ba9a1f9fc9..5de8bb1a2a 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -252,12 +252,6 @@ bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt void ContextOverlayInterface::contextOverlays_mousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event) { if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) { qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID; - Setting::Handle _settingSwitch{ "commerce", true }; - if (_settingSwitch.get()) { - openInspectionCertificate(); - } else { - openMarketplace(); - } emit contextOverlayClicked(_currentEntityWithContextOverlay); _contextOverlayJustClicked = true; } @@ -390,34 +384,6 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID } } -static const QString INSPECTION_CERTIFICATE_QML_PATH = "hifi/commerce/inspectionCertificate/InspectionCertificate.qml"; -void ContextOverlayInterface::openInspectionCertificate() { - // lets open the tablet to the inspection certificate QML - if (!_currentEntityWithContextOverlay.isNull() && _entityMarketplaceID.length() > 0) { - setLastInspectedEntity(_currentEntityWithContextOverlay); - auto tablet = dynamic_cast(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); - tablet->loadQMLSource(INSPECTION_CERTIFICATE_QML_PATH); - _hmdScriptingInterface->openTablet(); - } -} - -static const QString MARKETPLACE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace/items/"; - -void ContextOverlayInterface::openMarketplace() { - // lets open the tablet and go to the current item in - // the marketplace (if the current entity has a - // marketplaceID) - if (!_currentEntityWithContextOverlay.isNull() && _entityMarketplaceID.length() > 0) { - auto tablet = dynamic_cast(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); - // construct the url to the marketplace item - QString url = MARKETPLACE_BASE_URL + _entityMarketplaceID; - QString MARKETPLACES_INJECT_SCRIPT_PATH = "file:///" + qApp->applicationDirPath() + "/scripts/system/html/js/marketplacesInject.js"; - tablet->gotoWebScreen(url, MARKETPLACES_INJECT_SCRIPT_PATH); - _hmdScriptingInterface->openTablet(); - _isInMarketplaceInspectionMode = true; - } -} - void ContextOverlayInterface::enableEntityHighlight(const EntityItemID& entityItemID) { _selectionScriptingInterface->addToSelectedItemsList("contextOverlayHighlightList", "entity", entityItemID); } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 808c3a4ee3..48b14e1a91 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -94,8 +94,6 @@ private: bool _isInMarketplaceInspectionMode { false }; - void openInspectionCertificate(); - void openMarketplace(); void enableEntityHighlight(const EntityItemID& entityItemID); void disableEntityHighlight(const EntityItemID& entityItemID); diff --git a/scripts/modules/appUi.js b/scripts/modules/appUi.js index fd13075eb6..3a70a69d4d 100644 --- a/scripts/modules/appUi.js +++ b/scripts/modules/appUi.js @@ -1,5 +1,5 @@ "use strict"; -/*global Tablet, Script*/ +/* global Tablet, Script */ // // libraries/appUi.js // @@ -31,53 +31,49 @@ function AppUi(properties) { var that = this; function defaultButton(name, suffix) { var base = that[name] || (that.buttonPrefix + suffix); - that[name] = (base.indexOf('/') >= 0) ? base : (that.graphicsDirectory + base); //poor man's merge + that[name] = (base.indexOf('/') >= 0) ? base : (that.graphicsDirectory + base); // poor man's merge } // Defaults: that.tabletName = "com.highfidelity.interface.tablet.system"; that.inject = ""; that.graphicsDirectory = "icons/tablet-icons/"; // Where to look for button svgs. See below. + that.additionalAppScreens = []; that.checkIsOpen = function checkIsOpen(type, tabletUrl) { // Are we active? Value used to set isOpen. - return (type === that.type) && that.currentUrl && (tabletUrl.indexOf(that.currentUrl) >= 0); // Actual url may have prefix or suffix. + // Actual url may have prefix or suffix. + return (type === that.currentVisibleScreenType) && + that.currentVisibleUrl && + ((that.home.indexOf(that.currentVisibleUrl) > -1) || + (that.additionalAppScreens.indexOf(that.currentVisibleUrl) > -1)); }; - that.setCurrentData = function setCurrentData(url) { - that.currentUrl = url; - that.type = /.qml$/.test(url) ? 'QML' : 'Web'; - } - that.open = function open(optionalUrl) { // How to open the app. + that.setCurrentVisibleScreenMetadata = function setCurrentVisibleScreenMetadata(type, url) { + that.currentVisibleScreenType = type; + that.currentVisibleUrl = url; + }; + that.open = function open(optionalUrl, optionalInject) { // How to open the app. var url = optionalUrl || that.home; - that.setCurrentData(url); - if (that.isQML()) { + var inject = that.inject; + if (optionalUrl && optionalInject) { + inject = optionalInject; + } + + if (that.isQMLUrl(url)) { that.tablet.loadQMLSource(url); } else { - that.tablet.gotoWebScreen(url, that.inject); + that.tablet.gotoWebScreen(url, inject); } }; - that.openNewApp = function openNewApp(url, optionalInject) { // Opens some app and replaces the current app - if (that.isQML(url)) { - that.tablet.pushOntoStack(url); - } else { - if (optionalInject) { - that.tablet.gotoWebScreen(url, optionalInject); - } else { - that.tablet.gotoWebScreen(url); - } - } - } - that.openNewAppOnTop = function openNewAppOnTop(url, optionalInject) { // Opens some app on top of the current app (on desktop, opens new window) - if (that.isQML(url)) { + // Opens some app on top of the current app (on desktop, opens new window) + that.openNewAppOnTop = function openNewAppOnTop(url, optionalInject) { + var inject = optionalInject || ""; + if (that.isQMLUrl(url)) { that.tablet.loadQMLOnTop(url); } else { - if (optionalInject) { - that.tablet.loadWebScreenOnTop(url, optionalInject); - } else { - that.tablet.loadWebScreenOnTop(url); - } + that.tablet.loadWebScreenOnTop(url, inject); } - } + }; that.close = function close() { // How to close the app. - that.currentUrl = ""; + that.currentVisibleUrl = ""; // for toolbar-mode: go back to home screen, this will close the window. that.tablet.gotoHomeScreen(); }; @@ -91,15 +87,40 @@ function AppUi(properties) { activeIcon: isWaiting ? that.activeMessagesButton : that.activeButton }); }; - that.isQML = function isQML(optionalUrl) { // We set type property in onClick. - if (optionalUrl) { - var type = /.qml$/.test(optionalUrl) ? 'QML' : 'Web'; - return type === 'QML'; - } - return that.type === 'QML'; + that.isQMLUrl = function isQMLUrl(url) { + var type = /.qml$/.test(url) ? 'QML' : 'Web'; + return type === 'QML'; }; - that.eventSignal = function eventSignal() { // What signal to hook onMessage to. - return that.isQML() ? that.tablet.fromQml : that.tablet.webEventReceived; + that.isCurrentlyOnQMLScreen = function isCurrentlyOnQMLScreen() { + return that.currentVisibleScreenType === 'QML'; + }; + + // Handlers + that.onScreenChanged = function onScreenChanged(type, url) { + // Set isOpen, wireEventBridge, set buttonActive as appropriate, + // and finally call onOpened() or onClosed() IFF defined. + that.setCurrentVisibleScreenMetadata(type, url); + if (that.checkIsOpen(type, url)) { + that.wireEventBridge(true); + if (!that.isOpen) { + that.buttonActive(true); + if (that.onOpened) { + that.onOpened(); + } + that.isOpen = true; + } + } else { // Not us. Should we do something for type Home, Menu, and particularly Closed (meaning tablet hidden? + that.wireEventBridge(false); + if (that.isOpen) { + that.buttonActive(false); + if (that.onClosed) { + that.onClosed(); + } + that.isOpen = false; + } + } + console.debug(that.buttonName + " app reports: Tablet screen changed.\nNew screen type: " + type + + "\nNew screen URL: " + url + "\nCurrent app open status: " + that.isOpen + "\n"); }; // Overwrite with the given properties: @@ -126,65 +147,55 @@ function AppUi(properties) { } that.button = that.tablet.addButton(buttonOptions); that.ignore = function ignore() { }; - - // Handlers - that.onScreenChanged = function onScreenChanged(type, url) { - // Set isOpen, wireEventBridge, set buttonActive as appropriate, - // and finally call onOpened() or onClosed() IFF defined. - console.debug(that.buttonName, 'onScreenChanged', type, url, that.isOpen); - if (that.checkIsOpen(type, url)) { - if (!that.isOpen) { - that.wireEventBridge(true); - that.buttonActive(true); - if (that.onOpened) { - that.onOpened(); - } - that.isOpen = true; - } - - } else { // Not us. Should we do something for type Home, Menu, and particularly Closed (meaning tablet hidden? - if (that.isOpen) { - that.wireEventBridge(false); - that.buttonActive(false); - if (that.onClosed) { - that.onClosed(); - } - that.isOpen = false; - } - } - }; - that.hasEventBridge = false; + that.hasQmlEventBridge = false; + that.hasHtmlEventBridge = false; // HTML event bridge uses strings, not objects. Here we abstract over that. // (Although injected javascript still has to use JSON.stringify/JSON.parse.) - that.sendToHtml = function (messageObject) { that.tablet.emitScriptEvent(JSON.stringify(messageObject)); }; - that.fromHtml = function (messageString) { that.onMessage(JSON.parse(messageString)); }; + that.sendToHtml = function (messageObject) { + that.tablet.emitScriptEvent(JSON.stringify(messageObject)); + }; + that.fromHtml = function (messageString) { + var parsedMessage = JSON.parse(messageString); + parsedMessage.messageSrc = "HTML"; + that.onMessage(parsedMessage); + }; that.sendMessage = that.ignore; that.wireEventBridge = function wireEventBridge(on) { // Uniquivocally sets that.sendMessage(messageObject) to do the right thing. - // Sets hasEventBridge and wires onMessage to eventSignal as appropriate, IFF onMessage defined. - var handler, isQml = that.isQML(); + // Sets has*EventBridge and wires onMessage to the proper event bridge as appropriate, IFF onMessage defined. + var isCurrentlyOnQMLScreen = that.isCurrentlyOnQMLScreen(); // Outbound (always, regardless of whether there is an inbound handler). if (on) { - that.sendMessage = isQml ? that.tablet.sendToQml : that.sendToHtml; + that.sendMessage = isCurrentlyOnQMLScreen ? that.tablet.sendToQml : that.sendToHtml; } else { that.sendMessage = that.ignore; } - if (!that.onMessage) { return; } + if (!that.onMessage) { + return; + } // Inbound - handler = isQml ? that.onMessage : that.fromHtml; if (on) { - if (!that.hasEventBridge) { - console.debug(that.buttonName, 'connecting', that.eventSignal()); - that.eventSignal().connect(handler); - that.hasEventBridge = true; + if (isCurrentlyOnQMLScreen && !that.hasQmlEventBridge) { + console.debug(that.buttonName, 'connecting', that.tablet.fromQml); + that.tablet.fromQml.connect(that.onMessage); + that.hasQmlEventBridge = true; + } else if (!isCurrentlyOnQMLScreen && !that.hasHtmlEventBridge) { + console.debug(that.buttonName, 'connecting', that.tablet.webEventReceived); + that.tablet.webEventReceived.connect(that.fromHtml); + that.hasHtmlEventBridge = true; } } else { - if (that.hasEventBridge) { - console.debug(that.buttonName, 'disconnecting', that.eventSignal()); - that.eventSignal().disconnect(handler); - that.hasEventBridge = false; + if (that.hasQmlEventBridge) { + console.debug(that.buttonName, 'disconnecting', that.tablet.fromQml); + that.tablet.fromQml.disconnect(that.onMessage); + that.hasQmlEventBridge = false; + } + if (that.hasHtmlEventBridge) { + console.debug(that.buttonName, 'disconnecting', that.tablet.webEventReceived); + that.tablet.webEventReceived.disconnect(that.fromHtml); + that.hasHtmlEventBridge = false; } } }; diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index d3109e3d66..213e008bdc 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -14,530 +14,529 @@ /* global getConnectionData */ (function () { // BEGIN LOCAL_SCOPE - Script.include("/~/system/libraries/accountUtils.js"); - Script.include("/~/system/libraries/connectionUtils.js"); - var AppUi = Script.require('appUi'); +Script.include("/~/system/libraries/accountUtils.js"); +Script.include("/~/system/libraries/connectionUtils.js"); +var AppUi = Script.require('appUi'); - var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace"; +var MARKETPLACE_URL = Account.metaverseServerURL + "/marketplace"; - // BEGIN AVATAR SELECTOR LOGIC - var UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6 }; - var SELECTED_COLOR = { red: 0xF3, green: 0x91, blue: 0x29 }; - var HOVER_COLOR = { red: 0xD0, green: 0xD0, blue: 0xD0 }; +// BEGIN AVATAR SELECTOR LOGIC +var UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6 }; +var SELECTED_COLOR = { red: 0xF3, green: 0x91, blue: 0x29 }; +var HOVER_COLOR = { red: 0xD0, green: 0xD0, blue: 0xD0 }; - var overlays = {}; // Keeps track of all our extended overlay data objects, keyed by target identifier. +var overlays = {}; // Keeps track of all our extended overlay data objects, keyed by target identifier. - function ExtendedOverlay(key, type, properties) { // A wrapper around overlays to store the key it is associated with. - overlays[key] = this; - this.key = key; - this.selected = false; - this.hovering = false; - this.activeOverlay = Overlays.addOverlay(type, properties); // We could use different overlays for (un)selected... +function ExtendedOverlay(key, type, properties) { // A wrapper around overlays to store the key it is associated with. + overlays[key] = this; + this.key = key; + this.selected = false; + this.hovering = false; + this.activeOverlay = Overlays.addOverlay(type, properties); // We could use different overlays for (un)selected... +} +// Instance methods: +ExtendedOverlay.prototype.deleteOverlay = function () { // remove display and data of this overlay + Overlays.deleteOverlay(this.activeOverlay); + delete overlays[this.key]; +}; + +ExtendedOverlay.prototype.editOverlay = function (properties) { // change display of this overlay + Overlays.editOverlay(this.activeOverlay, properties); +}; + +function color(selected, hovering) { + var base = hovering ? HOVER_COLOR : selected ? SELECTED_COLOR : UNSELECTED_COLOR; + function scale(component) { + var delta = 0xFF - component; + return component; } - // Instance methods: - ExtendedOverlay.prototype.deleteOverlay = function () { // remove display and data of this overlay - Overlays.deleteOverlay(this.activeOverlay); - delete overlays[this.key]; - }; - - ExtendedOverlay.prototype.editOverlay = function (properties) { // change display of this overlay - Overlays.editOverlay(this.activeOverlay, properties); - }; - - function color(selected, hovering) { - var base = hovering ? HOVER_COLOR : selected ? SELECTED_COLOR : UNSELECTED_COLOR; - function scale(component) { - var delta = 0xFF - component; - return component; - } - return { red: scale(base.red), green: scale(base.green), blue: scale(base.blue) }; - } - // so we don't have to traverse the overlays to get the last one - var lastHoveringId = 0; - ExtendedOverlay.prototype.hover = function (hovering) { - this.hovering = hovering; - if (this.key === lastHoveringId) { - if (hovering) { - return; - } - lastHoveringId = 0; - } - this.editOverlay({ color: color(this.selected, hovering) }); + return { red: scale(base.red), green: scale(base.green), blue: scale(base.blue) }; +} +// so we don't have to traverse the overlays to get the last one +var lastHoveringId = 0; +ExtendedOverlay.prototype.hover = function (hovering) { + this.hovering = hovering; + if (this.key === lastHoveringId) { if (hovering) { - // un-hover the last hovering overlay - if (lastHoveringId && lastHoveringId !== this.key) { - ExtendedOverlay.get(lastHoveringId).hover(false); - } - lastHoveringId = this.key; - } - }; - ExtendedOverlay.prototype.select = function (selected) { - if (this.selected === selected) { return; } - - this.editOverlay({ color: color(selected, this.hovering) }); - this.selected = selected; - }; - // Class methods: - var selectedId = false; - ExtendedOverlay.isSelected = function (id) { - return selectedId === id; - }; - ExtendedOverlay.get = function (key) { // answer the extended overlay data object associated with the given avatar identifier - return overlays[key]; - }; - ExtendedOverlay.some = function (iterator) { // Bails early as soon as iterator returns truthy. - var key; - for (key in overlays) { - if (iterator(ExtendedOverlay.get(key))) { - return; - } - } - }; - ExtendedOverlay.unHover = function () { // calls hover(false) on lastHoveringId (if any) - if (lastHoveringId) { + lastHoveringId = 0; + } + this.editOverlay({ color: color(this.selected, hovering) }); + if (hovering) { + // un-hover the last hovering overlay + if (lastHoveringId && lastHoveringId !== this.key) { ExtendedOverlay.get(lastHoveringId).hover(false); } - }; + lastHoveringId = this.key; + } +}; +ExtendedOverlay.prototype.select = function (selected) { + if (this.selected === selected) { + return; + } - // hit(overlay) on the one overlay intersected by pickRay, if any. - // noHit() if no ExtendedOverlay was intersected (helps with hover) - ExtendedOverlay.applyPickRay = function (pickRay, hit, noHit) { - var pickedOverlay = Overlays.findRayIntersection(pickRay); // Depends on nearer coverOverlays to extend closer to us than farther ones. - if (!pickedOverlay.intersects) { - if (noHit) { - return noHit(); - } + this.editOverlay({ color: color(selected, this.hovering) }); + this.selected = selected; +}; +// Class methods: +var selectedId = false; +ExtendedOverlay.isSelected = function (id) { + return selectedId === id; +}; +ExtendedOverlay.get = function (key) { // answer the extended overlay data object associated with the given avatar identifier + return overlays[key]; +}; +ExtendedOverlay.some = function (iterator) { // Bails early as soon as iterator returns truthy. + var key; + for (key in overlays) { + if (iterator(ExtendedOverlay.get(key))) { return; } - ExtendedOverlay.some(function (overlay) { // See if pickedOverlay is one of ours. - if ((overlay.activeOverlay) === pickedOverlay.overlayID) { - hit(overlay); - return true; - } - }); - }; - - function addAvatarNode(id) { - return new ExtendedOverlay(id, "sphere", { - drawInFront: true, - solid: true, - alpha: 0.8, - color: color(false, false), - ignoreRayIntersection: false - }); } - - var pingPong = true; - function updateOverlays() { - var eye = Camera.position; - AvatarList.getAvatarIdentifiers().forEach(function (id) { - if (!id) { - return; // don't update ourself, or avatars we're not interested in - } - var avatar = AvatarList.getAvatar(id); - if (!avatar) { - return; // will be deleted below if there had been an overlay. - } - var overlay = ExtendedOverlay.get(id); - if (!overlay) { // For now, we're treating this as a temporary loss, as from the personal space bubble. Add it back. - overlay = addAvatarNode(id); - } - var target = avatar.position; - var distance = Vec3.distance(target, eye); - var offset = 0.2; - var diff = Vec3.subtract(target, eye); // get diff between target and eye (a vector pointing to the eye from avatar position) - var headIndex = avatar.getJointIndex("Head"); // base offset on 1/2 distance from hips to head if we can - if (headIndex > 0) { - offset = avatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y / 2; - } - - // move a bit in front, towards the camera - target = Vec3.subtract(target, Vec3.multiply(Vec3.normalize(diff), offset)); - - // now bump it up a bit - target.y = target.y + offset; - - overlay.ping = pingPong; - overlay.editOverlay({ - color: color(ExtendedOverlay.isSelected(id), overlay.hovering), - position: target, - dimensions: 0.032 * distance - }); - }); - pingPong = !pingPong; - ExtendedOverlay.some(function (overlay) { // Remove any that weren't updated. (User is gone.) - if (overlay.ping === pingPong) { - overlay.deleteOverlay(); - } - }); - } - function removeOverlays() { - selectedId = false; - lastHoveringId = 0; - ExtendedOverlay.some(function (overlay) { - overlay.deleteOverlay(); - }); +}; +ExtendedOverlay.unHover = function () { // calls hover(false) on lastHoveringId (if any) + if (lastHoveringId) { + ExtendedOverlay.get(lastHoveringId).hover(false); } +}; - // - // Clicks. - // - function usernameFromIDReply(id, username, machineFingerprint, isAdmin) { - if (selectedId === id) { - var message = { - method: 'updateSelectedRecipientUsername', - userName: username === "" ? "unknown username" : username - }; - ui.sendMessage(message); +// hit(overlay) on the one overlay intersected by pickRay, if any. +// noHit() if no ExtendedOverlay was intersected (helps with hover) +ExtendedOverlay.applyPickRay = function (pickRay, hit, noHit) { + var pickedOverlay = Overlays.findRayIntersection(pickRay); // Depends on nearer coverOverlays to extend closer to us than farther ones. + if (!pickedOverlay.intersects) { + if (noHit) { + return noHit(); } + return; } - function handleClick(pickRay) { - ExtendedOverlay.applyPickRay(pickRay, function (overlay) { - var nextSelectedStatus = !overlay.selected; - var avatarId = overlay.key; - selectedId = nextSelectedStatus ? avatarId : false; - if (nextSelectedStatus) { - Users.requestUsernameFromID(avatarId); - } - var message = { - method: 'selectRecipient', - id: avatarId, - isSelected: nextSelectedStatus, - displayName: '"' + AvatarList.getAvatar(avatarId).sessionDisplayName + '"', - userName: '' - }; - ui.sendMessage(message); - - ExtendedOverlay.some(function (overlay) { - var id = overlay.key; - var selected = ExtendedOverlay.isSelected(id); - overlay.select(selected); - }); - + ExtendedOverlay.some(function (overlay) { // See if pickedOverlay is one of ours. + if ((overlay.activeOverlay) === pickedOverlay.overlayID) { + hit(overlay); return true; - }); - } - function handleMouseEvent(mousePressEvent) { // handleClick if we get one. - if (!mousePressEvent.isLeftButton) { - return; } - handleClick(Camera.computePickRay(mousePressEvent.x, mousePressEvent.y)); - } - function handleMouseMove(pickRay) { // given the pickRay, just do the hover logic - ExtendedOverlay.applyPickRay(pickRay, function (overlay) { - overlay.hover(true); - }, function () { - ExtendedOverlay.unHover(); + }); +}; + +function addAvatarNode(id) { + return new ExtendedOverlay(id, "sphere", { + drawInFront: true, + solid: true, + alpha: 0.8, + color: color(false, false), + ignoreRayIntersection: false + }); +} + +var pingPong = true; +function updateOverlays() { + var eye = Camera.position; + AvatarList.getAvatarIdentifiers().forEach(function (id) { + if (!id) { + return; // don't update ourself, or avatars we're not interested in + } + var avatar = AvatarList.getAvatar(id); + if (!avatar) { + return; // will be deleted below if there had been an overlay. + } + var overlay = ExtendedOverlay.get(id); + if (!overlay) { // For now, we're treating this as a temporary loss, as from the personal space bubble. Add it back. + overlay = addAvatarNode(id); + } + var target = avatar.position; + var distance = Vec3.distance(target, eye); + var offset = 0.2; + var diff = Vec3.subtract(target, eye); // get diff between target and eye (a vector pointing to the eye from avatar position) + var headIndex = avatar.getJointIndex("Head"); // base offset on 1/2 distance from hips to head if we can + if (headIndex > 0) { + offset = avatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y / 2; + } + + // move a bit in front, towards the camera + target = Vec3.subtract(target, Vec3.multiply(Vec3.normalize(diff), offset)); + + // now bump it up a bit + target.y = target.y + offset; + + overlay.ping = pingPong; + overlay.editOverlay({ + color: color(ExtendedOverlay.isSelected(id), overlay.hovering), + position: target, + dimensions: 0.032 * distance }); + }); + pingPong = !pingPong; + ExtendedOverlay.some(function (overlay) { // Remove any that weren't updated. (User is gone.) + if (overlay.ping === pingPong) { + overlay.deleteOverlay(); + } + }); +} +function removeOverlays() { + selectedId = false; + lastHoveringId = 0; + ExtendedOverlay.some(function (overlay) { + overlay.deleteOverlay(); + }); +} + +// +// Clicks. +// +function usernameFromIDReply(id, username, machineFingerprint, isAdmin) { + if (selectedId === id) { + var message = { + method: 'updateSelectedRecipientUsername', + userName: username === "" ? "unknown username" : username + }; + ui.sendMessage(message); } +} +function handleClick(pickRay) { + ExtendedOverlay.applyPickRay(pickRay, function (overlay) { + var nextSelectedStatus = !overlay.selected; + var avatarId = overlay.key; + selectedId = nextSelectedStatus ? avatarId : false; + if (nextSelectedStatus) { + Users.requestUsernameFromID(avatarId); + } + var message = { + method: 'selectRecipient', + id: avatarId, + isSelected: nextSelectedStatus, + displayName: '"' + AvatarList.getAvatar(avatarId).sessionDisplayName + '"', + userName: '' + }; + ui.sendMessage(message); - // handy global to keep track of which hand is the mouse (if any) - var currentHandPressed = 0; - var TRIGGER_CLICK_THRESHOLD = 0.85; - var TRIGGER_PRESS_THRESHOLD = 0.05; + ExtendedOverlay.some(function (overlay) { + var id = overlay.key; + var selected = ExtendedOverlay.isSelected(id); + overlay.select(selected); + }); - function handleMouseMoveEvent(event) { // find out which overlay (if any) is over the mouse position - var pickRay; - if (HMD.active) { - if (currentHandPressed !== 0) { - pickRay = controllerComputePickRay(currentHandPressed); - } else { - // nothing should hover, so - ExtendedOverlay.unHover(); - return; - } + return true; + }); +} +function handleMouseEvent(mousePressEvent) { // handleClick if we get one. + if (!mousePressEvent.isLeftButton) { + return; + } + handleClick(Camera.computePickRay(mousePressEvent.x, mousePressEvent.y)); +} +function handleMouseMove(pickRay) { // given the pickRay, just do the hover logic + ExtendedOverlay.applyPickRay(pickRay, function (overlay) { + overlay.hover(true); + }, function () { + ExtendedOverlay.unHover(); + }); +} + +// handy global to keep track of which hand is the mouse (if any) +var currentHandPressed = 0; +var TRIGGER_CLICK_THRESHOLD = 0.85; +var TRIGGER_PRESS_THRESHOLD = 0.05; + +function handleMouseMoveEvent(event) { // find out which overlay (if any) is over the mouse position + var pickRay; + if (HMD.active) { + if (currentHandPressed !== 0) { + pickRay = controllerComputePickRay(currentHandPressed); } else { - pickRay = Camera.computePickRay(event.x, event.y); - } - handleMouseMove(pickRay); - } - function handleTriggerPressed(hand, value) { - // The idea is if you press one trigger, it is the one - // we will consider the mouse. Even if the other is pressed, - // we ignore it until this one is no longer pressed. - var isPressed = value > TRIGGER_PRESS_THRESHOLD; - if (currentHandPressed === 0) { - currentHandPressed = isPressed ? hand : 0; + // nothing should hover, so + ExtendedOverlay.unHover(); return; } - if (currentHandPressed === hand) { - currentHandPressed = isPressed ? hand : 0; - return; - } - // otherwise, the other hand is still triggered - // so do nothing. + } else { + pickRay = Camera.computePickRay(event.x, event.y); } + handleMouseMove(pickRay); +} +function handleTriggerPressed(hand, value) { + // The idea is if you press one trigger, it is the one + // we will consider the mouse. Even if the other is pressed, + // we ignore it until this one is no longer pressed. + var isPressed = value > TRIGGER_PRESS_THRESHOLD; + if (currentHandPressed === 0) { + currentHandPressed = isPressed ? hand : 0; + return; + } + if (currentHandPressed === hand) { + currentHandPressed = isPressed ? hand : 0; + return; + } + // otherwise, the other hand is still triggered + // so do nothing. +} - // We get mouseMoveEvents from the handControllers, via handControllerPointer. - // But we don't get mousePressEvents. - var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click'); - var triggerPressMapping = Controller.newMapping(Script.resolvePath('') + '-press'); - function controllerComputePickRay(hand) { - var controllerPose = getControllerWorldLocation(hand, true); - if (controllerPose.valid) { - return { origin: controllerPose.position, direction: Quat.getUp(controllerPose.orientation) }; +// We get mouseMoveEvents from the handControllers, via handControllerPointer. +// But we don't get mousePressEvents. +var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click'); +var triggerPressMapping = Controller.newMapping(Script.resolvePath('') + '-press'); +function controllerComputePickRay(hand) { + var controllerPose = getControllerWorldLocation(hand, true); + if (controllerPose.valid) { + return { origin: controllerPose.position, direction: Quat.getUp(controllerPose.orientation) }; + } +} +function makeClickHandler(hand) { + return function (clicked) { + if (clicked > TRIGGER_CLICK_THRESHOLD) { + var pickRay = controllerComputePickRay(hand); + handleClick(pickRay); } - } - function makeClickHandler(hand) { - return function (clicked) { - if (clicked > TRIGGER_CLICK_THRESHOLD) { - var pickRay = controllerComputePickRay(hand); - handleClick(pickRay); - } - }; - } - function makePressHandler(hand) { - return function (value) { - handleTriggerPressed(hand, value); - }; - } - triggerMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand)); - triggerMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand)); - triggerPressMapping.from(Controller.Standard.RT).peek().to(makePressHandler(Controller.Standard.RightHand)); - triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand)); - // END AVATAR SELECTOR LOGIC - - var sendMoneyRecipient; - var sendMoneyParticleEffectUpdateTimer; - var particleEffectTimestamp; - var sendMoneyParticleEffect; - var SEND_MONEY_PARTICLE_TIMER_UPDATE = 250; - var SEND_MONEY_PARTICLE_EMITTING_DURATION = 3000; - var SEND_MONEY_PARTICLE_LIFETIME_SECONDS = 8; - var SEND_MONEY_PARTICLE_PROPERTIES = { - accelerationSpread: { x: 0, y: 0, z: 0 }, - alpha: 1, - alphaFinish: 1, - alphaSpread: 0, - alphaStart: 1, - azimuthFinish: 0, - azimuthStart: -6, - color: { red: 143, green: 5, blue: 255 }, - colorFinish: { red: 255, green: 0, blue: 204 }, - colorSpread: { red: 0, green: 0, blue: 0 }, - colorStart: { red: 0, green: 136, blue: 255 }, - emitAcceleration: { x: 0, y: 0, z: 0 }, // Immediately gets updated to be accurate - emitDimensions: { x: 0, y: 0, z: 0 }, - emitOrientation: { x: 0, y: 0, z: 0 }, - emitRate: 4, - emitSpeed: 2.1, - emitterShouldTrail: true, - isEmitting: 1, - lifespan: SEND_MONEY_PARTICLE_LIFETIME_SECONDS + 1, // Immediately gets updated to be accurate - lifetime: SEND_MONEY_PARTICLE_LIFETIME_SECONDS + 1, - maxParticles: 20, - name: 'hfc-particles', - particleRadius: 0.2, - polarFinish: 0, - polarStart: 0, - radiusFinish: 0.05, - radiusSpread: 0, - radiusStart: 0.2, - speedSpread: 0, - textures: "http://hifi-content.s3.amazonaws.com/alan/dev/Particles/Bokeh-Particle-HFC.png", - type: 'ParticleEffect' }; +} +function makePressHandler(hand) { + return function (value) { + handleTriggerPressed(hand, value); + }; +} +triggerMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand)); +triggerMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand)); +triggerPressMapping.from(Controller.Standard.RT).peek().to(makePressHandler(Controller.Standard.RightHand)); +triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand)); +// END AVATAR SELECTOR LOGIC - function updateSendMoneyParticleEffect() { - var timestampNow = Date.now(); - if ((timestampNow - particleEffectTimestamp) > (SEND_MONEY_PARTICLE_LIFETIME_SECONDS * 1000)) { - deleteSendMoneyParticleEffect(); - return; - } else if ((timestampNow - particleEffectTimestamp) > SEND_MONEY_PARTICLE_EMITTING_DURATION) { - Entities.editEntity(sendMoneyParticleEffect, { - isEmitting: 0 - }); - } else if (sendMoneyParticleEffect) { - var recipientPosition = AvatarList.getAvatar(sendMoneyRecipient).position; - var distance = Vec3.distance(recipientPosition, MyAvatar.position); - var accel = Vec3.subtract(recipientPosition, MyAvatar.position); - accel.y -= 3.0; - var life = Math.sqrt(2 * distance / Vec3.length(accel)); - Entities.editEntity(sendMoneyParticleEffect, { - emitAcceleration: accel, - lifespan: life - }); - } - } +var sendMoneyRecipient; +var sendMoneyParticleEffectUpdateTimer; +var particleEffectTimestamp; +var sendMoneyParticleEffect; +var SEND_MONEY_PARTICLE_TIMER_UPDATE = 250; +var SEND_MONEY_PARTICLE_EMITTING_DURATION = 3000; +var SEND_MONEY_PARTICLE_LIFETIME_SECONDS = 8; +var SEND_MONEY_PARTICLE_PROPERTIES = { + accelerationSpread: { x: 0, y: 0, z: 0 }, + alpha: 1, + alphaFinish: 1, + alphaSpread: 0, + alphaStart: 1, + azimuthFinish: 0, + azimuthStart: -6, + color: { red: 143, green: 5, blue: 255 }, + colorFinish: { red: 255, green: 0, blue: 204 }, + colorSpread: { red: 0, green: 0, blue: 0 }, + colorStart: { red: 0, green: 136, blue: 255 }, + emitAcceleration: { x: 0, y: 0, z: 0 }, // Immediately gets updated to be accurate + emitDimensions: { x: 0, y: 0, z: 0 }, + emitOrientation: { x: 0, y: 0, z: 0 }, + emitRate: 4, + emitSpeed: 2.1, + emitterShouldTrail: true, + isEmitting: 1, + lifespan: SEND_MONEY_PARTICLE_LIFETIME_SECONDS + 1, // Immediately gets updated to be accurate + lifetime: SEND_MONEY_PARTICLE_LIFETIME_SECONDS + 1, + maxParticles: 20, + name: 'hfc-particles', + particleRadius: 0.2, + polarFinish: 0, + polarStart: 0, + radiusFinish: 0.05, + radiusSpread: 0, + radiusStart: 0.2, + speedSpread: 0, + textures: "http://hifi-content.s3.amazonaws.com/alan/dev/Particles/Bokeh-Particle-HFC.png", + type: 'ParticleEffect' +}; - function deleteSendMoneyParticleEffect() { - if (sendMoneyParticleEffectUpdateTimer) { - Script.clearInterval(sendMoneyParticleEffectUpdateTimer); - sendMoneyParticleEffectUpdateTimer = null; - } - if (sendMoneyParticleEffect) { - sendMoneyParticleEffect = Entities.deleteEntity(sendMoneyParticleEffect); - } - sendMoneyRecipient = null; - } - - function onUsernameChanged() { - if (Account.username !== Settings.getValue("wallet/savedUsername")) { - Settings.setValue("wallet/autoLogout", false); - Settings.setValue("wallet/savedUsername", ""); - } - } - - // Function Name: fromQml() - // - // Description: - // -Called when a message is received from SpectatorCamera.qml. The "message" argument is what is sent from the QML - // in the format "{method, params}", like json-rpc. See also sendToQml(). - var isHmdPreviewDisabled = true; - var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml"; - var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); - function fromQml(message) { - switch (message.method) { - case 'passphrasePopup_cancelClicked': - case 'needsLogIn_cancelClicked': - ui.close(); - break; - case 'walletSetup_cancelClicked': - switch (message.referrer) { - case '': // User clicked "Wallet" app - case undefined: - case null: - ui.close(); - break; - case 'purchases': - case 'marketplace cta': - case 'mainPage': - ui.openNewApp(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL); - break; - default: // User needs to return to an individual marketplace item URL - ui.openNewApp(MARKETPLACE_URL + '/items/' + message.referrer, MARKETPLACES_INJECT_SCRIPT_URL); - break; - } - break; - case 'needsLogIn_loginClicked': - openLoginWindow(); - break; - case 'disableHmdPreview': - break; // do nothing here, handled in marketplaces.js - case 'maybeEnableHmdPreview': - break; // do nothing here, handled in marketplaces.js - case 'transactionHistory_linkClicked': - ui.openNewApp(message.marketplaceLink, MARKETPLACES_INJECT_SCRIPT_URL); - break; - case 'goToPurchases_fromWalletHome': - case 'goToPurchases': - ui.openNewApp(MARKETPLACE_PURCHASES_QML_PATH); - break; - case 'goToMarketplaceMainPage': - ui.openNewApp(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL); - break; - case 'goToMarketplaceItemPage': - ui.openNewApp(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL); - break; - case 'refreshConnections': - print('Refreshing Connections...'); - getConnectionData(false); - break; - case 'enable_ChooseRecipientNearbyMode': - if (!isUpdateOverlaysWired) { - Script.update.connect(updateOverlays); - isUpdateOverlaysWired = true; - } - break; - case 'disable_ChooseRecipientNearbyMode': - if (isUpdateOverlaysWired) { - Script.update.disconnect(updateOverlays); - isUpdateOverlaysWired = false; - } - removeOverlays(); - break; - case 'sendAsset_sendPublicly': - if (message.assetName === "") { - deleteSendMoneyParticleEffect(); - sendMoneyRecipient = message.recipient; - var amount = message.amount; - var props = SEND_MONEY_PARTICLE_PROPERTIES; - props.parentID = MyAvatar.sessionUUID; - props.position = MyAvatar.position; - props.position.y += 0.2; - if (message.effectImage) { - props.textures = message.effectImage; - } - sendMoneyParticleEffect = Entities.addEntity(props, true); - particleEffectTimestamp = Date.now(); - updateSendMoneyParticleEffect(); - sendMoneyParticleEffectUpdateTimer = Script.setInterval(updateSendMoneyParticleEffect, SEND_MONEY_PARTICLE_TIMER_UPDATE); - } - break; - case 'transactionHistory_goToBank': - if (Account.metaverseServerURL.indexOf("staging") >= 0) { - Window.location = "hifi://hifiqa-master-metaverse-staging"; // So that we can test in staging. - } else { - Window.location = "hifi://BankOfHighFidelity"; - } - break; - case 'wallet_availableUpdatesReceived': - // NOP - break; - case 'http.request': - // Handled elsewhere, don't log. - break; - default: - print('Unrecognized message from QML:', JSON.stringify(message)); - } - } - - function walletOpened() { - Users.usernameFromIDReply.connect(usernameFromIDReply); - Controller.mousePressEvent.connect(handleMouseEvent); - Controller.mouseMoveEvent.connect(handleMouseMoveEvent); - triggerMapping.enable(); - triggerPressMapping.enable(); - } - - function walletClosed() { - off(); - } - - // - // Manage the connection between the button and the window. - // - var BUTTON_NAME = "WALLET"; - var WALLET_QML_SOURCE = "hifi/commerce/wallet/Wallet.qml"; - var walletEnabled = Settings.getValue("commerce", true); - function startup() { - ui = new AppUi({ - buttonName: BUTTON_NAME, - sortOrder: 10, - home: WALLET_QML_SOURCE, - onOpened: walletOpened, - onClosed: walletClosed, - onMessage: fromQml +var MS_PER_SEC = 1000; +function updateSendMoneyParticleEffect() { + var timestampNow = Date.now(); + if ((timestampNow - particleEffectTimestamp) > (SEND_MONEY_PARTICLE_LIFETIME_SECONDS * MS_PER_SEC)) { + deleteSendMoneyParticleEffect(); + return; + } else if ((timestampNow - particleEffectTimestamp) > SEND_MONEY_PARTICLE_EMITTING_DURATION) { + Entities.editEntity(sendMoneyParticleEffect, { + isEmitting: 0 + }); + } else if (sendMoneyParticleEffect) { + var recipientPosition = AvatarList.getAvatar(sendMoneyRecipient).position; + var distance = Vec3.distance(recipientPosition, MyAvatar.position); + var accel = Vec3.subtract(recipientPosition, MyAvatar.position); + accel.y -= 3.0; + var life = Math.sqrt(2 * distance / Vec3.length(accel)); + Entities.editEntity(sendMoneyParticleEffect, { + emitAcceleration: accel, + lifespan: life }); - GlobalServices.myUsernameChanged.connect(onUsernameChanged); } - var isUpdateOverlaysWired = false; - function off() { - Users.usernameFromIDReply.disconnect(usernameFromIDReply); - Controller.mousePressEvent.disconnect(handleMouseEvent); - Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); - triggerMapping.disable(); - triggerPressMapping.disable(); +} +function deleteSendMoneyParticleEffect() { + if (sendMoneyParticleEffectUpdateTimer) { + Script.clearInterval(sendMoneyParticleEffectUpdateTimer); + sendMoneyParticleEffectUpdateTimer = null; + } + if (sendMoneyParticleEffect) { + sendMoneyParticleEffect = Entities.deleteEntity(sendMoneyParticleEffect); + } + sendMoneyRecipient = null; +} + +function onUsernameChanged() { + if (Account.username !== Settings.getValue("wallet/savedUsername")) { + Settings.setValue("wallet/autoLogout", false); + Settings.setValue("wallet/savedUsername", ""); + } +} + +// Function Name: fromQml() +// +// Description: +// -Called when a message is received from SpectatorCamera.qml. The "message" argument is what is sent from the QML +// in the format "{method, params}", like json-rpc. See also sendToQml(). +var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml"; +var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); +function fromQml(message) { + switch (message.method) { + case 'passphrasePopup_cancelClicked': + case 'needsLogIn_cancelClicked': + ui.close(); + break; + case 'walletSetup_cancelClicked': + switch (message.referrer) { + case '': // User clicked "Wallet" app + case undefined: + case null: + ui.close(); + break; + case 'purchases': + case 'marketplace cta': + case 'mainPage': + ui.open(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL); + break; + default: // User needs to return to an individual marketplace item URL + ui.open(MARKETPLACE_URL + '/items/' + message.referrer, MARKETPLACES_INJECT_SCRIPT_URL); + break; + } + break; + case 'needsLogIn_loginClicked': + openLoginWindow(); + break; + case 'disableHmdPreview': + break; // do nothing here, handled in marketplaces.js + case 'maybeEnableHmdPreview': + break; // do nothing here, handled in marketplaces.js + case 'transactionHistory_linkClicked': + ui.open(message.marketplaceLink, MARKETPLACES_INJECT_SCRIPT_URL); + break; + case 'goToPurchases_fromWalletHome': + case 'goToPurchases': + ui.open(MARKETPLACE_PURCHASES_QML_PATH); + break; + case 'goToMarketplaceMainPage': + ui.open(MARKETPLACE_URL, MARKETPLACES_INJECT_SCRIPT_URL); + break; + case 'goToMarketplaceItemPage': + ui.open(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL); + break; + case 'refreshConnections': + print('Refreshing Connections...'); + getConnectionData(false); + break; + case 'enable_ChooseRecipientNearbyMode': + if (!isUpdateOverlaysWired) { + Script.update.connect(updateOverlays); + isUpdateOverlaysWired = true; + } + break; + case 'disable_ChooseRecipientNearbyMode': if (isUpdateOverlaysWired) { Script.update.disconnect(updateOverlays); isUpdateOverlaysWired = false; } removeOverlays(); + break; + case 'sendAsset_sendPublicly': + if (message.assetName === "") { + deleteSendMoneyParticleEffect(); + sendMoneyRecipient = message.recipient; + var amount = message.amount; + var props = SEND_MONEY_PARTICLE_PROPERTIES; + props.parentID = MyAvatar.sessionUUID; + props.position = MyAvatar.position; + props.position.y += 0.2; + if (message.effectImage) { + props.textures = message.effectImage; + } + sendMoneyParticleEffect = Entities.addEntity(props, true); + particleEffectTimestamp = Date.now(); + updateSendMoneyParticleEffect(); + sendMoneyParticleEffectUpdateTimer = Script.setInterval(updateSendMoneyParticleEffect, SEND_MONEY_PARTICLE_TIMER_UPDATE); + } + break; + case 'transactionHistory_goToBank': + if (Account.metaverseServerURL.indexOf("staging") >= 0) { + Window.location = "hifi://hifiqa-master-metaverse-staging"; // So that we can test in staging. + } else { + Window.location = "hifi://BankOfHighFidelity"; + } + break; + case 'wallet_availableUpdatesReceived': + // NOP + break; + case 'http.request': + // Handled elsewhere, don't log. + break; + default: + print('Unrecognized message from QML:', JSON.stringify(message)); } - function shutdown() { - GlobalServices.myUsernameChanged.disconnect(onUsernameChanged); - deleteSendMoneyParticleEffect(); - off(); +} + +function walletOpened() { + Users.usernameFromIDReply.connect(usernameFromIDReply); + Controller.mousePressEvent.connect(handleMouseEvent); + Controller.mouseMoveEvent.connect(handleMouseMoveEvent); + triggerMapping.enable(); + triggerPressMapping.enable(); +} + +function walletClosed() { + off(); +} + +// +// Manage the connection between the button and the window. +// +var BUTTON_NAME = "WALLET"; +var WALLET_QML_SOURCE = "hifi/commerce/wallet/Wallet.qml"; +var ui; +function startup() { + ui = new AppUi({ + buttonName: BUTTON_NAME, + sortOrder: 10, + home: WALLET_QML_SOURCE, + onOpened: walletOpened, + onClosed: walletClosed, + onMessage: fromQml + }); + GlobalServices.myUsernameChanged.connect(onUsernameChanged); +} +var isUpdateOverlaysWired = false; +function off() { + Users.usernameFromIDReply.disconnect(usernameFromIDReply); + Controller.mousePressEvent.disconnect(handleMouseEvent); + Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); + triggerMapping.disable(); + triggerPressMapping.disable(); + + if (isUpdateOverlaysWired) { + Script.update.disconnect(updateOverlays); + isUpdateOverlaysWired = false; } + removeOverlays(); +} +function shutdown() { + GlobalServices.myUsernameChanged.disconnect(onUsernameChanged); + deleteSendMoneyParticleEffect(); + off(); +} - // - // Run the functions. - // - startup(); - Script.scriptEnding.connect(shutdown); - +// +// Run the functions. +// +startup(); +Script.scriptEnding.connect(shutdown); }()); // END LOCAL_SCOPE diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index a68556d771..7821edee33 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -1,3 +1,5 @@ +/* global $, window, MutationObserver */ + // // marketplacesInject.js // @@ -11,7 +13,6 @@ // (function () { - // Event bridge messages. var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD"; var CLARA_IO_STATUS = "CLARA.IO STATUS"; @@ -24,7 +25,7 @@ var canWriteAssets = false; var xmlHttpRequest = null; - var isPreparing = false; // Explicitly track download request status. + var isPreparing = false; // Explicitly track download request status. var commerceMode = false; var userIsLoggedIn = false; @@ -33,7 +34,6 @@ var messagesWaiting = false; function injectCommonCode(isDirectoryPage) { - // Supporting styles from marketplaces.css. // Glyph font family, size, and spacing adjusted because HiFi-Glyphs cannot be used cross-domain. $("head").append( @@ -74,7 +74,9 @@ (document.referrer !== "") ? window.history.back() : window.location = (marketplaceBaseURL + "/marketplace?"); }); $("#all-markets").on("click", function () { - EventBridge.emitWebEvent(GOTO_DIRECTORY); + EventBridge.emitWebEvent(JSON.stringify({ + type: GOTO_DIRECTORY + })); }); } @@ -94,11 +96,11 @@ }); } - emitWalletSetupEvent = function() { + var emitWalletSetupEvent = function () { EventBridge.emitWebEvent(JSON.stringify({ type: "WALLET_SETUP" })); - } + }; function maybeAddSetupWalletButton() { if (!$('body').hasClass("walletsetup-injected") && userIsLoggedIn && walletNeedsSetup) { @@ -285,7 +287,7 @@ $(this).closest('.col-xs-3').prev().attr("class", 'col-xs-6'); $(this).closest('.col-xs-3').attr("class", 'col-xs-6'); - var priceElement = $(this).find('.price') + var priceElement = $(this).find('.price'); priceElement.css({ "padding": "3px 5px", "height": "40px", @@ -355,12 +357,12 @@ function injectAddScrollbarToCategories() { $('#categories-dropdown').on('show.bs.dropdown', function () { $('body > div.container').css('display', 'none') - $('#categories-dropdown > ul.dropdown-menu').css({ 'overflow': 'auto', 'height': 'calc(100vh - 110px)' }) + $('#categories-dropdown > ul.dropdown-menu').css({ 'overflow': 'auto', 'height': 'calc(100vh - 110px)' }); }); $('#categories-dropdown').on('hide.bs.dropdown', function () { - $('body > div.container').css('display', '') - $('#categories-dropdown > ul.dropdown-menu').css({ 'overflow': '', 'height': '' }) + $('body > div.container').css('display', ''); + $('#categories-dropdown > ul.dropdown-menu').css({ 'overflow': '', 'height': '' }); }); } @@ -382,7 +384,6 @@ mutations.forEach(function (mutation) { injectBuyButtonOnMainPage(); }); - //observer.disconnect(); }); var config = { attributes: true, childList: true, characterData: true }; observer.observe(target, config); @@ -451,8 +452,8 @@ "itemPage", urlParams.get('edition'), type); - } - }); + } + }); maybeAddPurchasesButton(); } } @@ -503,127 +504,142 @@ $(".top-title .col-sm-4").append(downloadContainer); downloadContainer.append(downloadFBX); } + } + } - // Automatic download to High Fidelity. - function startAutoDownload() { + // Automatic download to High Fidelity. + function startAutoDownload() { + // One file request at a time. + if (isPreparing) { + console.log("WARNING: Clara.io FBX: Prepare only one download at a time"); + return; + } - // One file request at a time. - if (isPreparing) { - console.log("WARNING: Clara.io FBX: Prepare only one download at a time"); - return; - } + // User must be able to write to Asset Server. + if (!canWriteAssets) { + console.log("ERROR: Clara.io FBX: File download cancelled because no permissions to write to Asset Server"); + EventBridge.emitWebEvent(JSON.stringify({ + type: WARN_USER_NO_PERMISSIONS + })); + return; + } - // User must be able to write to Asset Server. - if (!canWriteAssets) { - console.log("ERROR: Clara.io FBX: File download cancelled because no permissions to write to Asset Server"); - EventBridge.emitWebEvent(WARN_USER_NO_PERMISSIONS); - return; - } + // User must be logged in. + var loginButton = $("#topnav a[href='/signup']"); + if (loginButton.length > 0) { + loginButton[0].click(); + return; + } - // User must be logged in. - var loginButton = $("#topnav a[href='/signup']"); - if (loginButton.length > 0) { - loginButton[0].click(); - return; - } + // Obtain zip file to download for requested asset. + // Reference: https://clara.io/learn/sdk/api/export - // Obtain zip file to download for requested asset. - // Reference: https://clara.io/learn/sdk/api/export + //var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?zip=true¢erScene=true&alignSceneGround=true&fbxUnit=Meter&fbxVersion=7&fbxEmbedTextures=true&imageFormat=WebGL"; + // 13 Jan 2017: Specify FBX version 5 and remove some options in order to make Clara.io site more likely to + // be successful in generating zip files. + var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?fbxUnit=Meter&fbxVersion=5&fbxEmbedTextures=true&imageFormat=WebGL"; - //var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?zip=true¢erScene=true&alignSceneGround=true&fbxUnit=Meter&fbxVersion=7&fbxEmbedTextures=true&imageFormat=WebGL"; - // 13 Jan 2017: Specify FBX version 5 and remove some options in order to make Clara.io site more likely to - // be successful in generating zip files. - var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?fbxUnit=Meter&fbxVersion=5&fbxEmbedTextures=true&imageFormat=WebGL"; + var uuid = location.href.match(/\/view\/([a-z0-9\-]*)/)[1]; + var url = XMLHTTPREQUEST_URL.replace("{uuid}", uuid); - var uuid = location.href.match(/\/view\/([a-z0-9\-]*)/)[1]; - var url = XMLHTTPREQUEST_URL.replace("{uuid}", uuid); + xmlHttpRequest = new XMLHttpRequest(); + var responseTextIndex = 0; + var zipFileURL = ""; - xmlHttpRequest = new XMLHttpRequest(); - var responseTextIndex = 0; - var zipFileURL = ""; + xmlHttpRequest.onreadystatechange = function () { + // Messages are appended to responseText; process the new ones. + var message = this.responseText.slice(responseTextIndex); + var statusMessage = ""; - xmlHttpRequest.onreadystatechange = function () { - // Messages are appended to responseText; process the new ones. - var message = this.responseText.slice(responseTextIndex); - var statusMessage = ""; + if (isPreparing) { // Ignore messages in flight after finished/cancelled. + var lines = message.split(/[\n\r]+/); - if (isPreparing) { // Ignore messages in flight after finished/cancelled. - var lines = message.split(/[\n\r]+/); + for (var i = 0, length = lines.length; i < length; i++) { + if (lines[i].slice(0, 5) === "data:") { + // Parse line. + var data; + try { + data = JSON.parse(lines[i].slice(5)); + } + catch (e) { + data = {}; + } - for (var i = 0, length = lines.length; i < length; i++) { - if (lines[i].slice(0, 5) === "data:") { - // Parse line. - var data; - try { - data = JSON.parse(lines[i].slice(5)); - } - catch (e) { - data = {}; - } + // Extract status message. + if (data.hasOwnProperty("message") && data.message !== null) { + statusMessage = data.message; + console.log("Clara.io FBX: " + statusMessage); + } - // Extract status message. - if (data.hasOwnProperty("message") && data.message !== null) { - statusMessage = data.message; - console.log("Clara.io FBX: " + statusMessage); - } - - // Extract zip file URL. - if (data.hasOwnProperty("files") && data.files.length > 0) { - zipFileURL = data.files[0].url; - if (zipFileURL.slice(-4) !== ".zip") { - console.log(JSON.stringify(data)); // Data for debugging. - } - } + // Extract zip file URL. + if (data.hasOwnProperty("files") && data.files.length > 0) { + zipFileURL = data.files[0].url; + if (zipFileURL.slice(-4) !== ".zip") { + console.log(JSON.stringify(data)); // Data for debugging. } } - - if (statusMessage !== "") { - // Update the UI with the most recent status message. - EventBridge.emitWebEvent(CLARA_IO_STATUS + " " + statusMessage); - } } - - responseTextIndex = this.responseText.length; - }; - - // Note: onprogress doesn't have computable total length so can't use it to determine % complete. - - xmlHttpRequest.onload = function () { - var statusMessage = ""; - - if (!isPreparing) { - return; - } - - isPreparing = false; - - var HTTP_OK = 200; - if (this.status !== HTTP_OK) { - statusMessage = "Zip file request terminated with " + this.status + " " + this.statusText; - console.log("ERROR: Clara.io FBX: " + statusMessage); - EventBridge.emitWebEvent(CLARA_IO_STATUS + " " + statusMessage); - } else if (zipFileURL.slice(-4) !== ".zip") { - statusMessage = "Error creating zip file for download."; - console.log("ERROR: Clara.io FBX: " + statusMessage + ": " + zipFileURL); - EventBridge.emitWebEvent(CLARA_IO_STATUS + " " + statusMessage); - } else { - EventBridge.emitWebEvent(CLARA_IO_DOWNLOAD + " " + zipFileURL); - console.log("Clara.io FBX: File download initiated for " + zipFileURL); - } - - xmlHttpRequest = null; } - isPreparing = true; - - console.log("Clara.io FBX: Request zip file for " + uuid); - EventBridge.emitWebEvent(CLARA_IO_STATUS + " Initiating download"); - - xmlHttpRequest.open("POST", url, true); - xmlHttpRequest.setRequestHeader("Accept", "text/event-stream"); - xmlHttpRequest.send(); + if (statusMessage !== "") { + // Update the UI with the most recent status message. + EventBridge.emitWebEvent(JSON.stringify({ + type: CLARA_IO_STATUS, + status: statusMessage + })); + } } + + responseTextIndex = this.responseText.length; + }; + + // Note: onprogress doesn't have computable total length so can't use it to determine % complete. + + xmlHttpRequest.onload = function () { + var statusMessage = ""; + + if (!isPreparing) { + return; + } + + isPreparing = false; + + var HTTP_OK = 200; + if (this.status !== HTTP_OK) { + statusMessage = "Zip file request terminated with " + this.status + " " + this.statusText; + console.log("ERROR: Clara.io FBX: " + statusMessage); + EventBridge.emitWebEvent(JSON.stringify({ + type: CLARA_IO_STATUS, + status: statusMessage + })); + } else if (zipFileURL.slice(-4) !== ".zip") { + statusMessage = "Error creating zip file for download."; + console.log("ERROR: Clara.io FBX: " + statusMessage + ": " + zipFileURL); + EventBridge.emitWebEvent(JSON.stringify({ + type: CLARA_IO_STATUS, + status: (statusMessage + ": " + zipFileURL) + })); + } else { + EventBridge.emitWebEvent(JSON.stringify({ + type: CLARA_IO_DOWNLOAD + })); + console.log("Clara.io FBX: File download initiated for " + zipFileURL); + } + + xmlHttpRequest = null; } + + isPreparing = true; + + console.log("Clara.io FBX: Request zip file for " + uuid); + EventBridge.emitWebEvent(JSON.stringify({ + type: CLARA_IO_STATUS, + status: "Initiating download" + })); + + xmlHttpRequest.open("POST", url, true); + xmlHttpRequest.setRequestHeader("Accept", "text/event-stream"); + xmlHttpRequest.send(); } function injectClaraCode() { @@ -663,7 +679,9 @@ updateClaraCodeInterval = undefined; }); - EventBridge.emitWebEvent(QUERY_CAN_WRITE_ASSETS); + EventBridge.emitWebEvent(JSON.stringify({ + type: QUERY_CAN_WRITE_ASSETS + })); } function cancelClaraDownload() { @@ -673,7 +691,9 @@ xmlHttpRequest.abort(); xmlHttpRequest = null; console.log("Clara.io FBX: File download cancelled"); - EventBridge.emitWebEvent(CLARA_IO_CANCELLED_DOWNLOAD); + EventBridge.emitWebEvent(JSON.stringify({ + type: CLARA_IO_CANCELLED_DOWNLOAD + })); } } @@ -708,26 +728,22 @@ function onLoad() { EventBridge.scriptEventReceived.connect(function (message) { - message = JSON.stringify(message); - if (message.slice(0, CAN_WRITE_ASSETS.length) === CAN_WRITE_ASSETS) { - canWriteAssets = message.slice(-4) === "true"; - } else if (message.slice(0, CLARA_IO_CANCEL_DOWNLOAD.length) === CLARA_IO_CANCEL_DOWNLOAD) { + message = JSON.parse(message); + if (message.type === CAN_WRITE_ASSETS) { + canWriteAssets = message.canWriteAssets; + } else if (message.type === CLARA_IO_CANCEL_DOWNLOAD) { cancelClaraDownload(); - } else { - var parsedJsonMessage = JSON.parse(message); - - if (parsedJsonMessage.type === "marketplaces") { - if (parsedJsonMessage.action === "commerceSetting") { - commerceMode = !!parsedJsonMessage.data.commerceMode; - userIsLoggedIn = !!parsedJsonMessage.data.userIsLoggedIn; - walletNeedsSetup = !!parsedJsonMessage.data.walletNeedsSetup; - marketplaceBaseURL = parsedJsonMessage.data.metaverseServerURL; - if (marketplaceBaseURL.indexOf('metaverse.') !== -1) { - marketplaceBaseURL = marketplaceBaseURL.replace('metaverse.', ''); - } - messagesWaiting = parsedJsonMessage.data.messagesWaiting; - injectCode(); + } else if (message.type === "marketplaces") { + if (message.action === "commerceSetting") { + commerceMode = !!message.data.commerceMode; + userIsLoggedIn = !!message.data.userIsLoggedIn; + walletNeedsSetup = !!message.data.walletNeedsSetup; + marketplaceBaseURL = message.data.metaverseServerURL; + if (marketplaceBaseURL.indexOf('metaverse.') !== -1) { + marketplaceBaseURL = marketplaceBaseURL.replace('metaverse.', ''); } + messagesWaiting = message.data.messagesWaiting; + injectCode(); } } }); @@ -740,6 +756,6 @@ } // Load / unload. - window.addEventListener("load", onLoad); // More robust to Web site issues than using $(document).ready(). - window.addEventListener("page:change", onLoad); // Triggered after Marketplace HTML is changed + window.addEventListener("load", onLoad); // More robust to Web site issues than using $(document).ready(). + window.addEventListener("page:change", onLoad); // Triggered after Marketplace HTML is changed }()); diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 66ddd0f09f..13ad1f6b69 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -16,1036 +16,1082 @@ var selectionDisplay = null; // for gridTool.js to ignore (function () { // BEGIN LOCAL_SCOPE - var AppUi = Script.require('appUi'); - Script.include("/~/system/libraries/gridTool.js"); - Script.include("/~/system/libraries/connectionUtils.js"); +var AppUi = Script.require('appUi'); +Script.include("/~/system/libraries/gridTool.js"); +Script.include("/~/system/libraries/connectionUtils.js"); - var METAVERSE_SERVER_URL = Account.metaverseServerURL; - var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); - var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); - var MARKETPLACE_CHECKOUT_QML_PATH = "hifi/commerce/checkout/Checkout.qml"; - var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml"; - var MARKETPLACE_WALLET_QML_PATH = "hifi/commerce/wallet/Wallet.qml"; - var MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH = "commerce/inspectionCertificate/InspectionCertificate.qml"; - var REZZING_SOUND = SoundCache.getSound(Script.resolvePath("../assets/sounds/rezzing.wav")); +var METAVERSE_SERVER_URL = Account.metaverseServerURL; +var MARKETPLACES_URL = Script.resolvePath("../html/marketplaces.html"); +var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js"); +var MARKETPLACE_CHECKOUT_QML_PATH = "hifi/commerce/checkout/Checkout.qml"; +var MARKETPLACE_PURCHASES_QML_PATH = "hifi/commerce/purchases/Purchases.qml"; +var MARKETPLACE_WALLET_QML_PATH = "hifi/commerce/wallet/Wallet.qml"; +var MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH = "hifi/commerce/inspectionCertificate/InspectionCertificate.qml"; +var REZZING_SOUND = SoundCache.getSound(Script.resolvePath("../assets/sounds/rezzing.wav")); - // Event bridge messages. - var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD"; - var CLARA_IO_STATUS = "CLARA.IO STATUS"; - var CLARA_IO_CANCEL_DOWNLOAD = "CLARA.IO CANCEL DOWNLOAD"; - var CLARA_IO_CANCELLED_DOWNLOAD = "CLARA.IO CANCELLED DOWNLOAD"; - var GOTO_DIRECTORY = "GOTO_DIRECTORY"; - var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS"; - var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS"; - var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS"; +// Event bridge messages. +var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD"; +var CLARA_IO_STATUS = "CLARA.IO STATUS"; +var CLARA_IO_CANCEL_DOWNLOAD = "CLARA.IO CANCEL DOWNLOAD"; +var CLARA_IO_CANCELLED_DOWNLOAD = "CLARA.IO CANCELLED DOWNLOAD"; +var GOTO_DIRECTORY = "GOTO_DIRECTORY"; +var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS"; +var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS"; +var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS"; - var CLARA_DOWNLOAD_TITLE = "Preparing Download"; - var messageBox = null; - var isDownloadBeingCancelled = false; +var CLARA_DOWNLOAD_TITLE = "Preparing Download"; +var messageBox = null; +var isDownloadBeingCancelled = false; - var CANCEL_BUTTON = 4194304; // QMessageBox::Cancel - var NO_BUTTON = 0; // QMessageBox::NoButton +var CANCEL_BUTTON = 4194304; // QMessageBox::Cancel +var NO_BUTTON = 0; // QMessageBox::NoButton - var NO_PERMISSIONS_ERROR_MESSAGE = "Cannot download model because you can't write to \nthe domain's Asset Server."; +var NO_PERMISSIONS_ERROR_MESSAGE = "Cannot download model because you can't write to \nthe domain's Asset Server."; - function onMessageBoxClosed(id, button) { - if (id === messageBox && button === CANCEL_BUTTON) { - isDownloadBeingCancelled = true; - messageBox = null; - ui.sendToHtml(CLARA_IO_CANCEL_DOWNLOAD); - } - } - - function onCanWriteAssetsChanged() { - var message = CAN_WRITE_ASSETS + " " + Entities.canWriteAssets(); - ui.sendToHtml(message); - } - - - var tabletShouldBeVisibleInSecondaryCamera = false; - function setTabletVisibleInSecondaryCamera(visibleInSecondaryCam) { - if (visibleInSecondaryCam) { - // if we're potentially showing the tablet, only do so if it was visible before - if (!tabletShouldBeVisibleInSecondaryCamera) { - return; - } - } else { - // if we're hiding the tablet, check to see if it was visible in the first place - tabletShouldBeVisibleInSecondaryCamera = Overlays.getProperty(HMD.tabletID, "isVisibleInSecondaryCamera"); - } - - Overlays.editOverlay(HMD.tabletID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); - Overlays.editOverlay(HMD.homeButtonID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); - Overlays.editOverlay(HMD.homeButtonHighlightID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); - Overlays.editOverlay(HMD.tabletScreenID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); - } - - function openWallet() { - ui.openNewApp(MARKETPLACE_WALLET_QML_PATH); - } - - function setCertificateInfo(currentEntityWithContextOverlay, itemCertificateId) { - ui.wireEventBridge(true); - var certificateId = itemCertificateId || (Entities.getEntityProperties(currentEntityWithContextOverlay, ['certificateID']).certificateID); - ui.sendMessage({ - method: 'inspectionCertificate_setCertificateId', - entityId: currentEntityWithContextOverlay, - certificateId: certificateId - }); - } - - function onUsernameChanged() { - if (onMarketplaceScreen) { - ui.openNewApp(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); - } - } - - var userHasUpdates = false; - function sendCommerceSettings() { +function onMessageBoxClosed(id, button) { + if (id === messageBox && button === CANCEL_BUTTON) { + isDownloadBeingCancelled = true; + messageBox = null; ui.sendToHtml({ - type: "marketplaces", - action: "commerceSetting", - data: { - commerceMode: Settings.getValue("commerce", true), - userIsLoggedIn: Account.loggedIn, - walletNeedsSetup: Wallet.walletStatus === 1, - metaverseServerURL: Account.metaverseServerURL, - messagesWaiting: userHasUpdates - } + type: CLARA_IO_CANCEL_DOWNLOAD }); } +} - // BEGIN AVATAR SELECTOR LOGIC - var UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6 }; - var SELECTED_COLOR = { red: 0xF3, green: 0x91, blue: 0x29 }; - var HOVER_COLOR = { red: 0xD0, green: 0xD0, blue: 0xD0 }; +function onCanWriteAssetsChanged() { + ui.sendToHtml({ + type: CAN_WRITE_ASSETS, + canWriteAssets: Entities.canWriteAssets() + }); +} - var overlays = {}; // Keeps track of all our extended overlay data objects, keyed by target identifier. - function ExtendedOverlay(key, type, properties) { // A wrapper around overlays to store the key it is associated with. - overlays[key] = this; - this.key = key; - this.selected = false; - this.hovering = false; - this.activeOverlay = Overlays.addOverlay(type, properties); // We could use different overlays for (un)selected... - } - // Instance methods: - ExtendedOverlay.prototype.deleteOverlay = function () { // remove display and data of this overlay - Overlays.deleteOverlay(this.activeOverlay); - delete overlays[this.key]; - }; - - ExtendedOverlay.prototype.editOverlay = function (properties) { // change display of this overlay - Overlays.editOverlay(this.activeOverlay, properties); - }; - - function color(selected, hovering) { - var base = hovering ? HOVER_COLOR : selected ? SELECTED_COLOR : UNSELECTED_COLOR; - function scale(component) { - var delta = 0xFF - component; - return component; - } - return { red: scale(base.red), green: scale(base.green), blue: scale(base.blue) }; - } - // so we don't have to traverse the overlays to get the last one - var lastHoveringId = 0; - ExtendedOverlay.prototype.hover = function (hovering) { - this.hovering = hovering; - if (this.key === lastHoveringId) { - if (hovering) { - return; - } - lastHoveringId = 0; - } - this.editOverlay({ color: color(this.selected, hovering) }); - if (hovering) { - // un-hover the last hovering overlay - if (lastHoveringId && lastHoveringId !== this.key) { - ExtendedOverlay.get(lastHoveringId).hover(false); - } - lastHoveringId = this.key; - } - }; - ExtendedOverlay.prototype.select = function (selected) { - if (this.selected === selected) { +var tabletShouldBeVisibleInSecondaryCamera = false; +function setTabletVisibleInSecondaryCamera(visibleInSecondaryCam) { + if (visibleInSecondaryCam) { + // if we're potentially showing the tablet, only do so if it was visible before + if (!tabletShouldBeVisibleInSecondaryCamera) { return; } + } else { + // if we're hiding the tablet, check to see if it was visible in the first place + tabletShouldBeVisibleInSecondaryCamera = Overlays.getProperty(HMD.tabletID, "isVisibleInSecondaryCamera"); + } - this.editOverlay({ color: color(selected, this.hovering) }); - this.selected = selected; - }; - // Class methods: - var selectedId = false; - ExtendedOverlay.isSelected = function (id) { - return selectedId === id; - }; - ExtendedOverlay.get = function (key) { // answer the extended overlay data object associated with the given avatar identifier - return overlays[key]; - }; - ExtendedOverlay.some = function (iterator) { // Bails early as soon as iterator returns truthy. - var key; - for (key in overlays) { - if (iterator(ExtendedOverlay.get(key))) { - return; - } + Overlays.editOverlay(HMD.tabletID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); + Overlays.editOverlay(HMD.homeButtonID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); + Overlays.editOverlay(HMD.homeButtonHighlightID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); + Overlays.editOverlay(HMD.tabletScreenID, { isVisibleInSecondaryCamera : visibleInSecondaryCam }); +} + +function openWallet() { + ui.open(MARKETPLACE_WALLET_QML_PATH); +} + +// Function Name: wireQmlEventBridge() +// +// Description: +// -Used to connect/disconnect the script's response to the tablet's "fromQml" signal. Set the "on" argument to enable or +// disable to event bridge. +// +// Relevant Variables: +// -hasEventBridge: true/false depending on whether we've already connected the event bridge. +var hasEventBridge = false; +function wireQmlEventBridge(on) { + if (!ui.tablet) { + print("Warning in wireQmlEventBridge(): 'tablet' undefined!"); + return; + } + if (on) { + if (!hasEventBridge) { + ui.tablet.fromQml.connect(onQmlMessageReceived); + hasEventBridge = true; } - }; - ExtendedOverlay.unHover = function () { // calls hover(false) on lastHoveringId (if any) - if (lastHoveringId) { + } else { + if (hasEventBridge) { + ui.tablet.fromQml.disconnect(onQmlMessageReceived); + hasEventBridge = false; + } + } +} + +var contextOverlayEntity = ""; +function openInspectionCertificateQML(currentEntityWithContextOverlay) { + ui.open(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH); + contextOverlayEntity = currentEntityWithContextOverlay; +} + +function setCertificateInfo(currentEntityWithContextOverlay, itemCertificateId) { + var certificateId = itemCertificateId || + (Entities.getEntityProperties(currentEntityWithContextOverlay, ['certificateID']).certificateID); + ui.tablet.sendToQml({ + method: 'inspectionCertificate_setCertificateId', + entityId: currentEntityWithContextOverlay, + certificateId: certificateId + }); +} + +function onUsernameChanged() { + if (onMarketplaceScreen) { + ui.open(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); + } +} + +var userHasUpdates = false; +function sendCommerceSettings() { + ui.sendToHtml({ + type: "marketplaces", + action: "commerceSetting", + data: { + commerceMode: Settings.getValue("commerce", true), + userIsLoggedIn: Account.loggedIn, + walletNeedsSetup: Wallet.walletStatus === 1, + metaverseServerURL: Account.metaverseServerURL, + messagesWaiting: userHasUpdates + } + }); +} + +// BEGIN AVATAR SELECTOR LOGIC +var UNSELECTED_COLOR = { red: 0x1F, green: 0xC6, blue: 0xA6 }; +var SELECTED_COLOR = { red: 0xF3, green: 0x91, blue: 0x29 }; +var HOVER_COLOR = { red: 0xD0, green: 0xD0, blue: 0xD0 }; + +var overlays = {}; // Keeps track of all our extended overlay data objects, keyed by target identifier. + +function ExtendedOverlay(key, type, properties) { // A wrapper around overlays to store the key it is associated with. + overlays[key] = this; + this.key = key; + this.selected = false; + this.hovering = false; + this.activeOverlay = Overlays.addOverlay(type, properties); // We could use different overlays for (un)selected... +} +// Instance methods: +ExtendedOverlay.prototype.deleteOverlay = function () { // remove display and data of this overlay + Overlays.deleteOverlay(this.activeOverlay); + delete overlays[this.key]; +}; + +ExtendedOverlay.prototype.editOverlay = function (properties) { // change display of this overlay + Overlays.editOverlay(this.activeOverlay, properties); +}; + +function color(selected, hovering) { + var base = hovering ? HOVER_COLOR : selected ? SELECTED_COLOR : UNSELECTED_COLOR; + function scale(component) { + return component; + } + return { red: scale(base.red), green: scale(base.green), blue: scale(base.blue) }; +} +// so we don't have to traverse the overlays to get the last one +var lastHoveringId = 0; +ExtendedOverlay.prototype.hover = function (hovering) { + this.hovering = hovering; + if (this.key === lastHoveringId) { + if (hovering) { + return; + } + lastHoveringId = 0; + } + this.editOverlay({ color: color(this.selected, hovering) }); + if (hovering) { + // un-hover the last hovering overlay + if (lastHoveringId && lastHoveringId !== this.key) { ExtendedOverlay.get(lastHoveringId).hover(false); } - }; + lastHoveringId = this.key; + } +}; +ExtendedOverlay.prototype.select = function (selected) { + if (this.selected === selected) { + return; + } - // hit(overlay) on the one overlay intersected by pickRay, if any. - // noHit() if no ExtendedOverlay was intersected (helps with hover) - ExtendedOverlay.applyPickRay = function (pickRay, hit, noHit) { - var pickedOverlay = Overlays.findRayIntersection(pickRay); // Depends on nearer coverOverlays to extend closer to us than farther ones. - if (!pickedOverlay.intersects) { - if (noHit) { - return noHit(); - } + this.editOverlay({ color: color(selected, this.hovering) }); + this.selected = selected; +}; +// Class methods: +var selectedId = false; +ExtendedOverlay.isSelected = function (id) { + return selectedId === id; +}; +ExtendedOverlay.get = function (key) { // answer the extended overlay data object associated with the given avatar identifier + return overlays[key]; +}; +ExtendedOverlay.some = function (iterator) { // Bails early as soon as iterator returns truthy. + var key; + for (key in overlays) { + if (iterator(ExtendedOverlay.get(key))) { return; } - ExtendedOverlay.some(function (overlay) { // See if pickedOverlay is one of ours. - if ((overlay.activeOverlay) === pickedOverlay.overlayID) { - hit(overlay); - return true; - } - }); - }; - - function addAvatarNode(id) { - return new ExtendedOverlay(id, "sphere", { - drawInFront: true, - solid: true, - alpha: 0.8, - color: color(false, false), - ignoreRayIntersection: false - }); } - - var pingPong = true; - function updateOverlays() { - var eye = Camera.position; - AvatarList.getAvatarIdentifiers().forEach(function (id) { - if (!id) { - return; // don't update ourself, or avatars we're not interested in - } - var avatar = AvatarList.getAvatar(id); - if (!avatar) { - return; // will be deleted below if there had been an overlay. - } - var overlay = ExtendedOverlay.get(id); - if (!overlay) { // For now, we're treating this as a temporary loss, as from the personal space bubble. Add it back. - overlay = addAvatarNode(id); - } - var target = avatar.position; - var distance = Vec3.distance(target, eye); - var offset = 0.2; - var diff = Vec3.subtract(target, eye); // get diff between target and eye (a vector pointing to the eye from avatar position) - var headIndex = avatar.getJointIndex("Head"); // base offset on 1/2 distance from hips to head if we can - if (headIndex > 0) { - offset = avatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y / 2; - } - - // move a bit in front, towards the camera - target = Vec3.subtract(target, Vec3.multiply(Vec3.normalize(diff), offset)); - - // now bump it up a bit - target.y = target.y + offset; - - overlay.ping = pingPong; - overlay.editOverlay({ - color: color(ExtendedOverlay.isSelected(id), overlay.hovering), - position: target, - dimensions: 0.032 * distance - }); - }); - pingPong = !pingPong; - ExtendedOverlay.some(function (overlay) { // Remove any that weren't updated. (User is gone.) - if (overlay.ping === pingPong) { - overlay.deleteOverlay(); - } - }); - } - function removeOverlays() { - selectedId = false; - lastHoveringId = 0; - ExtendedOverlay.some(function (overlay) { - overlay.deleteOverlay(); - }); +}; +ExtendedOverlay.unHover = function () { // calls hover(false) on lastHoveringId (if any) + if (lastHoveringId) { + ExtendedOverlay.get(lastHoveringId).hover(false); } +}; - // - // Clicks. - // - function usernameFromIDReply(id, username, machineFingerprint, isAdmin) { - if (selectedId === id) { - var message = { - method: 'updateSelectedRecipientUsername', - userName: username === "" ? "unknown username" : username - }; - ui.sendMessage(message); +// hit(overlay) on the one overlay intersected by pickRay, if any. +// noHit() if no ExtendedOverlay was intersected (helps with hover) +ExtendedOverlay.applyPickRay = function (pickRay, hit, noHit) { + var pickedOverlay = Overlays.findRayIntersection(pickRay); // Depends on nearer coverOverlays to extend closer to us than farther ones. + if (!pickedOverlay.intersects) { + if (noHit) { + return noHit(); } + return; } - function handleClick(pickRay) { - ExtendedOverlay.applyPickRay(pickRay, function (overlay) { - var nextSelectedStatus = !overlay.selected; - var avatarId = overlay.key; - selectedId = nextSelectedStatus ? avatarId : false; - if (nextSelectedStatus) { - Users.requestUsernameFromID(avatarId); - } - var message = { - method: 'selectRecipient', - id: avatarId, - isSelected: nextSelectedStatus, - displayName: '"' + AvatarList.getAvatar(avatarId).sessionDisplayName + '"', - userName: '' - }; - ui.sendMessage(message); - - ExtendedOverlay.some(function (overlay) { - var id = overlay.key; - var selected = ExtendedOverlay.isSelected(id); - overlay.select(selected); - }); - + ExtendedOverlay.some(function (overlay) { // See if pickedOverlay is one of ours. + if ((overlay.activeOverlay) === pickedOverlay.overlayID) { + hit(overlay); return true; - }); - } - function handleMouseEvent(mousePressEvent) { // handleClick if we get one. - if (!mousePressEvent.isLeftButton) { - return; } - handleClick(Camera.computePickRay(mousePressEvent.x, mousePressEvent.y)); + }); +}; + +function addAvatarNode(id) { + return new ExtendedOverlay(id, "sphere", { + drawInFront: true, + solid: true, + alpha: 0.8, + color: color(false, false), + ignoreRayIntersection: false + }); +} + +var pingPong = true; +function updateOverlays() { + var eye = Camera.position; + AvatarList.getAvatarIdentifiers().forEach(function (id) { + if (!id) { + return; // don't update ourself, or avatars we're not interested in + } + var avatar = AvatarList.getAvatar(id); + if (!avatar) { + return; // will be deleted below if there had been an overlay. + } + var overlay = ExtendedOverlay.get(id); + if (!overlay) { // For now, we're treating this as a temporary loss, as from the personal space bubble. Add it back. + overlay = addAvatarNode(id); + } + var target = avatar.position; + var distance = Vec3.distance(target, eye); + var offset = 0.2; + var diff = Vec3.subtract(target, eye); // get diff between target and eye (a vector pointing to the eye from avatar position) + var headIndex = avatar.getJointIndex("Head"); // base offset on 1/2 distance from hips to head if we can + if (headIndex > 0) { + offset = avatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y / 2; + } + + // move a bit in front, towards the camera + target = Vec3.subtract(target, Vec3.multiply(Vec3.normalize(diff), offset)); + + // now bump it up a bit + target.y = target.y + offset; + + overlay.ping = pingPong; + overlay.editOverlay({ + color: color(ExtendedOverlay.isSelected(id), overlay.hovering), + position: target, + dimensions: 0.032 * distance + }); + }); + pingPong = !pingPong; + ExtendedOverlay.some(function (overlay) { // Remove any that weren't updated. (User is gone.) + if (overlay.ping === pingPong) { + overlay.deleteOverlay(); + } + }); +} +function removeOverlays() { + selectedId = false; + lastHoveringId = 0; + ExtendedOverlay.some(function (overlay) { + overlay.deleteOverlay(); + }); +} + +// +// Clicks. +// +function usernameFromIDReply(id, username, machineFingerprint, isAdmin) { + if (selectedId === id) { + var message = { + method: 'updateSelectedRecipientUsername', + userName: username === "" ? "unknown username" : username + }; + ui.tablet.sendToQml(message); } - function handleMouseMove(pickRay) { // given the pickRay, just do the hover logic - ExtendedOverlay.applyPickRay(pickRay, function (overlay) { - overlay.hover(true); - }, function () { +} +function handleClick(pickRay) { + ExtendedOverlay.applyPickRay(pickRay, function (overlay) { + var nextSelectedStatus = !overlay.selected; + var avatarId = overlay.key; + selectedId = nextSelectedStatus ? avatarId : false; + if (nextSelectedStatus) { + Users.requestUsernameFromID(avatarId); + } + var message = { + method: 'selectRecipient', + id: avatarId, + isSelected: nextSelectedStatus, + displayName: '"' + AvatarList.getAvatar(avatarId).sessionDisplayName + '"', + userName: '' + }; + ui.tablet.sendToQml(message); + + ExtendedOverlay.some(function (overlay) { + var id = overlay.key; + var selected = ExtendedOverlay.isSelected(id); + overlay.select(selected); + }); + + return true; + }); +} +function handleMouseEvent(mousePressEvent) { // handleClick if we get one. + if (!mousePressEvent.isLeftButton) { + return; + } + handleClick(Camera.computePickRay(mousePressEvent.x, mousePressEvent.y)); +} +function handleMouseMove(pickRay) { // given the pickRay, just do the hover logic + ExtendedOverlay.applyPickRay(pickRay, function (overlay) { + overlay.hover(true); + }, function () { + ExtendedOverlay.unHover(); + }); +} + +// handy global to keep track of which hand is the mouse (if any) +var currentHandPressed = 0; +var TRIGGER_CLICK_THRESHOLD = 0.85; +var TRIGGER_PRESS_THRESHOLD = 0.05; + +function handleMouseMoveEvent(event) { // find out which overlay (if any) is over the mouse position + var pickRay; + if (HMD.active) { + if (currentHandPressed !== 0) { + pickRay = controllerComputePickRay(currentHandPressed); + } else { + // nothing should hover, so ExtendedOverlay.unHover(); - }); - } - - // handy global to keep track of which hand is the mouse (if any) - var currentHandPressed = 0; - var TRIGGER_CLICK_THRESHOLD = 0.85; - var TRIGGER_PRESS_THRESHOLD = 0.05; - - function handleMouseMoveEvent(event) { // find out which overlay (if any) is over the mouse position - var pickRay; - if (HMD.active) { - if (currentHandPressed !== 0) { - pickRay = controllerComputePickRay(currentHandPressed); - } else { - // nothing should hover, so - ExtendedOverlay.unHover(); - return; - } - } else { - pickRay = Camera.computePickRay(event.x, event.y); - } - handleMouseMove(pickRay); - } - function handleTriggerPressed(hand, value) { - // The idea is if you press one trigger, it is the one - // we will consider the mouse. Even if the other is pressed, - // we ignore it until this one is no longer pressed. - var isPressed = value > TRIGGER_PRESS_THRESHOLD; - if (currentHandPressed === 0) { - currentHandPressed = isPressed ? hand : 0; return; } - if (currentHandPressed === hand) { - currentHandPressed = isPressed ? hand : 0; - return; - } - // otherwise, the other hand is still triggered - // so do nothing. + } else { + pickRay = Camera.computePickRay(event.x, event.y); } - - // We get mouseMoveEvents from the handControllers, via handControllerPointer. - // But we don't get mousePressEvents. - var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click'); - var triggerPressMapping = Controller.newMapping(Script.resolvePath('') + '-press'); - function controllerComputePickRay(hand) { - var controllerPose = getControllerWorldLocation(hand, true); - if (controllerPose.valid) { - return { origin: controllerPose.position, direction: Quat.getUp(controllerPose.orientation) }; - } + handleMouseMove(pickRay); +} +function handleTriggerPressed(hand, value) { + // The idea is if you press one trigger, it is the one + // we will consider the mouse. Even if the other is pressed, + // we ignore it until this one is no longer pressed. + var isPressed = value > TRIGGER_PRESS_THRESHOLD; + if (currentHandPressed === 0) { + currentHandPressed = isPressed ? hand : 0; + return; } - function makeClickHandler(hand) { - return function (clicked) { - if (clicked > TRIGGER_CLICK_THRESHOLD) { - var pickRay = controllerComputePickRay(hand); - handleClick(pickRay); - } - }; + if (currentHandPressed === hand) { + currentHandPressed = isPressed ? hand : 0; + return; } - function makePressHandler(hand) { - return function (value) { - handleTriggerPressed(hand, value); - }; + // otherwise, the other hand is still triggered + // so do nothing. +} + +// We get mouseMoveEvents from the handControllers, via handControllerPointer. +// But we don't get mousePressEvents. +var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click'); +var triggerPressMapping = Controller.newMapping(Script.resolvePath('') + '-press'); +function controllerComputePickRay(hand) { + var controllerPose = getControllerWorldLocation(hand, true); + if (controllerPose.valid) { + return { origin: controllerPose.position, direction: Quat.getUp(controllerPose.orientation) }; } - triggerMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand)); - triggerMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand)); - triggerPressMapping.from(Controller.Standard.RT).peek().to(makePressHandler(Controller.Standard.RightHand)); - triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand)); - // END AVATAR SELECTOR LOGIC - - var grid = new Grid(); - function adjustPositionPerBoundingBox(position, direction, registration, dimensions, orientation) { - // Adjust the position such that the bounding box (registration, dimenions, and orientation) lies behind the original - // position in the given direction. - var CORNERS = [ - { x: 0, y: 0, z: 0 }, - { x: 0, y: 0, z: 1 }, - { x: 0, y: 1, z: 0 }, - { x: 0, y: 1, z: 1 }, - { x: 1, y: 0, z: 0 }, - { x: 1, y: 0, z: 1 }, - { x: 1, y: 1, z: 0 }, - { x: 1, y: 1, z: 1 }, - ]; - - // Go through all corners and find least (most negative) distance in front of position. - var distance = 0; - for (var i = 0, length = CORNERS.length; i < length; i++) { - var cornerVector = - Vec3.multiplyQbyV(orientation, Vec3.multiplyVbyV(Vec3.subtract(CORNERS[i], registration), dimensions)); - var cornerDistance = Vec3.dot(cornerVector, direction); - distance = Math.min(cornerDistance, distance); +} +function makeClickHandler(hand) { + return function (clicked) { + if (clicked > TRIGGER_CLICK_THRESHOLD) { + var pickRay = controllerComputePickRay(hand); + handleClick(pickRay); } - position = Vec3.sum(Vec3.multiply(distance, direction), position); - return position; - } - - var HALF_TREE_SCALE = 16384; - function getPositionToCreateEntity(extra) { - var CREATE_DISTANCE = 2; - var position; - var delta = extra !== undefined ? extra : 0; - if (Camera.mode === "entity" || Camera.mode === "independent") { - position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getForward(Camera.orientation), CREATE_DISTANCE + delta)); - } else { - position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getForward(MyAvatar.orientation), CREATE_DISTANCE + delta)); - position.y += 0.5; - } - - if (position.x > HALF_TREE_SCALE || position.y > HALF_TREE_SCALE || position.z > HALF_TREE_SCALE) { - return null; - } - return position; - } - - function rezEntity(itemHref, itemType) { - var isWearable = itemType === "wearable"; - var success = Clipboard.importEntities(itemHref); - var wearableLocalPosition = null; - var wearableLocalRotation = null; - var wearableLocalDimensions = null; - var wearableDimensions = null; - - if (itemType === "contentSet") { - console.log("Item is a content set; codepath shouldn't go here."); - return; - } - - if (isWearable) { - var wearableTransforms = Settings.getValue("io.highfidelity.avatarStore.checkOut.transforms"); - if (!wearableTransforms) { - // TODO delete this clause - wearableTransforms = Settings.getValue("io.highfidelity.avatarStore.checkOut.tranforms"); - } - var certPos = itemHref.search("certificate_id="); // TODO how do I parse a URL from here? - if (certPos >= 0) { - certPos += 15; // length of "certificate_id=" - var certURLEncoded = itemHref.substring(certPos); - var certB64Encoded = decodeURIComponent(certURLEncoded); - for (var key in wearableTransforms) { - if (wearableTransforms.hasOwnProperty(key)) { - var certificateTransforms = wearableTransforms[key].certificateTransforms; - if (certificateTransforms) { - for (var certID in certificateTransforms) { - if (certificateTransforms.hasOwnProperty(certID) && - certID == certB64Encoded) { - var certificateTransform = certificateTransforms[certID]; - wearableLocalPosition = certificateTransform.localPosition; - wearableLocalRotation = certificateTransform.localRotation; - wearableLocalDimensions = certificateTransform.localDimensions; - wearableDimensions = certificateTransform.dimensions; - } - } - } - } - } - } - } - - if (success) { - var VERY_LARGE = 10000; - var isLargeImport = Clipboard.getClipboardContentsLargestDimension() >= VERY_LARGE; - var position = Vec3.ZERO; - if (!isLargeImport) { - position = getPositionToCreateEntity(Clipboard.getClipboardContentsLargestDimension() / 2); - } - if (position !== null && position !== undefined) { - var pastedEntityIDs = Clipboard.pasteEntities(position); - if (!isLargeImport) { - // The first entity in Clipboard gets the specified position with the rest being relative to it. Therefore, move - // entities after they're imported so that they're all the correct distance in front of and with geometric mean - // centered on the avatar/camera direction. - var deltaPosition = Vec3.ZERO; - var entityPositions = []; - var entityParentIDs = []; - - var propType = Entities.getEntityProperties(pastedEntityIDs[0], ["type"]).type; - var NO_ADJUST_ENTITY_TYPES = ["Zone", "Light", "ParticleEffect"]; - if (NO_ADJUST_ENTITY_TYPES.indexOf(propType) === -1) { - var targetDirection; - if (Camera.mode === "entity" || Camera.mode === "independent") { - targetDirection = Camera.orientation; - } else { - targetDirection = MyAvatar.orientation; - } - targetDirection = Vec3.multiplyQbyV(targetDirection, Vec3.UNIT_Z); - - var targetPosition = getPositionToCreateEntity(); - var deltaParallel = HALF_TREE_SCALE; // Distance to move entities parallel to targetDirection. - var deltaPerpendicular = Vec3.ZERO; // Distance to move entities perpendicular to targetDirection. - for (var i = 0, length = pastedEntityIDs.length; i < length; i++) { - var curLoopEntityProps = Entities.getEntityProperties(pastedEntityIDs[i], ["position", "dimensions", - "registrationPoint", "rotation", "parentID"]); - var adjustedPosition = adjustPositionPerBoundingBox(targetPosition, targetDirection, - curLoopEntityProps.registrationPoint, curLoopEntityProps.dimensions, curLoopEntityProps.rotation); - var delta = Vec3.subtract(adjustedPosition, curLoopEntityProps.position); - var distance = Vec3.dot(delta, targetDirection); - deltaParallel = Math.min(distance, deltaParallel); - deltaPerpendicular = Vec3.sum(Vec3.subtract(delta, Vec3.multiply(distance, targetDirection)), - deltaPerpendicular); - entityPositions[i] = curLoopEntityProps.position; - entityParentIDs[i] = curLoopEntityProps.parentID; - } - deltaPerpendicular = Vec3.multiply(1 / pastedEntityIDs.length, deltaPerpendicular); - deltaPosition = Vec3.sum(Vec3.multiply(deltaParallel, targetDirection), deltaPerpendicular); - } - - if (grid.getSnapToGrid()) { - var firstEntityProps = Entities.getEntityProperties(pastedEntityIDs[0], ["position", "dimensions", - "registrationPoint"]); - var positionPreSnap = Vec3.sum(deltaPosition, firstEntityProps.position); - position = grid.snapToSurface(grid.snapToGrid(positionPreSnap, false, firstEntityProps.dimensions, - firstEntityProps.registrationPoint), firstEntityProps.dimensions, firstEntityProps.registrationPoint); - deltaPosition = Vec3.subtract(position, firstEntityProps.position); - } - - if (!Vec3.equal(deltaPosition, Vec3.ZERO)) { - for (var editEntityIndex = 0, numEntities = pastedEntityIDs.length; editEntityIndex < numEntities; editEntityIndex++) { - if (Uuid.isNull(entityParentIDs[editEntityIndex])) { - Entities.editEntity(pastedEntityIDs[editEntityIndex], { - position: Vec3.sum(deltaPosition, entityPositions[editEntityIndex]) - }); - } - } - } - } - - if (isWearable) { - // apply the relative offsets saved during checkout - var offsets = {}; - if (wearableLocalPosition) { - offsets.localPosition = wearableLocalPosition; - } - if (wearableLocalRotation) { - offsets.localRotation = wearableLocalRotation; - } - if (wearableLocalDimensions) { - offsets.localDimensions = wearableLocalDimensions; - } else if (wearableDimensions) { - offsets.dimensions = wearableDimensions; - } - // we currently assume a wearable is a single entity - Entities.editEntity(pastedEntityIDs[0], offsets); - } - - var rezPosition = Entities.getEntityProperties(pastedEntityIDs[0], "position").position; - - Audio.playSound(REZZING_SOUND, { - volume: 1.0, - position: rezPosition, - localOnly: true - }); - - } else { - Window.notifyEditError("Can't import entities: entities would be out of bounds."); - } - } else { - Window.notifyEditError("There was an error importing the entity file."); - } - } - - var referrerURL; // Used for updating Purchases QML - var filterText; // Used for updating Purchases QML - function onMessage(message) { - if (message === GOTO_DIRECTORY) { - ui.openNewApp(MARKETPLACES_URL, MARKETPLACES_INJECT_SCRIPT_URL); - } else if (message === QUERY_CAN_WRITE_ASSETS) { - ui.sendToHtml(CAN_WRITE_ASSETS + " " + Entities.canWriteAssets()); - } else if (message === WARN_USER_NO_PERMISSIONS) { - Window.alert(NO_PERMISSIONS_ERROR_MESSAGE); - } else if (message.slice(0, CLARA_IO_STATUS.length) === CLARA_IO_STATUS) { - if (isDownloadBeingCancelled) { - return; - } - - var text = message.slice(CLARA_IO_STATUS.length); - if (messageBox === null) { - messageBox = Window.openMessageBox(CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); - } else { - Window.updateMessageBox(messageBox, CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); - } - return; - } else if (message.slice(0, CLARA_IO_DOWNLOAD.length) === CLARA_IO_DOWNLOAD) { - if (messageBox !== null) { - Window.closeMessageBox(messageBox); - messageBox = null; - } - return; - } else if (message === CLARA_IO_CANCELLED_DOWNLOAD) { - isDownloadBeingCancelled = false; - } else { - var parsedJsonMessage = JSON.parse(message); - if (parsedJsonMessage.type === "CHECKOUT") { - ui.wireEventBridge(true); - ui.openNewApp(MARKETPLACE_CHECKOUT_QML_PATH); - ui.sendMessage({ - method: 'updateCheckoutQML', - params: parsedJsonMessage - }); - } else if (parsedJsonMessage.type === "REQUEST_SETTING") { - sendCommerceSettings(); - } else if (parsedJsonMessage.type === "PURCHASES") { - referrerURL = parsedJsonMessage.referrerURL; - filterText = ""; - ui.openNewApp(MARKETPLACE_PURCHASES_QML_PATH); - } else if (parsedJsonMessage.type === "LOGIN") { - openLoginWindow(); - } else if (parsedJsonMessage.type === "WALLET_SETUP") { - ui.wireEventBridge(true); - ui.sendMessage({ - method: 'updateWalletReferrer', - referrer: "marketplace cta" - }); - openWallet(); - } else if (parsedJsonMessage.type === "MY_ITEMS") { - referrerURL = MARKETPLACE_URL_INITIAL; - filterText = ""; - ui.openNewApp(MARKETPLACE_PURCHASES_QML_PATH); - ui.wireEventBridge(true); - ui.sendMessage({ - method: 'purchases_showMyItems' - }); - } - } - } - var sendAssetRecipient; - var sendAssetParticleEffectUpdateTimer; - var particleEffectTimestamp; - var sendAssetParticleEffect; - var SEND_ASSET_PARTICLE_TIMER_UPDATE = 250; - var SEND_ASSET_PARTICLE_EMITTING_DURATION = 3000; - var SEND_ASSET_PARTICLE_LIFETIME_SECONDS = 8; - var SEND_ASSET_PARTICLE_PROPERTIES = { - accelerationSpread: { x: 0, y: 0, z: 0 }, - alpha: 1, - alphaFinish: 1, - alphaSpread: 0, - alphaStart: 1, - azimuthFinish: 0, - azimuthStart: -6, - color: { red: 255, green: 222, blue: 255 }, - colorFinish: { red: 255, green: 229, blue: 225 }, - colorSpread: { red: 0, green: 0, blue: 0 }, - colorStart: { red: 243, green: 255, blue: 255 }, - emitAcceleration: { x: 0, y: 0, z: 0 }, // Immediately gets updated to be accurate - emitDimensions: { x: 0, y: 0, z: 0 }, - emitOrientation: { x: 0, y: 0, z: 0 }, - emitRate: 4, - emitSpeed: 2.1, - emitterShouldTrail: true, - isEmitting: 1, - lifespan: SEND_ASSET_PARTICLE_LIFETIME_SECONDS + 1, // Immediately gets updated to be accurate - lifetime: SEND_ASSET_PARTICLE_LIFETIME_SECONDS + 1, - maxParticles: 20, - name: 'asset-particles', - particleRadius: 0.2, - polarFinish: 0, - polarStart: 0, - radiusFinish: 0.05, - radiusSpread: 0, - radiusStart: 0.2, - speedSpread: 0, - textures: "http://hifi-content.s3.amazonaws.com/alan/dev/Particles/Bokeh-Particle-HFC.png", - type: 'ParticleEffect' }; +} +function makePressHandler(hand) { + return function (value) { + handleTriggerPressed(hand, value); + }; +} +triggerMapping.from(Controller.Standard.RTClick).peek().to(makeClickHandler(Controller.Standard.RightHand)); +triggerMapping.from(Controller.Standard.LTClick).peek().to(makeClickHandler(Controller.Standard.LeftHand)); +triggerPressMapping.from(Controller.Standard.RT).peek().to(makePressHandler(Controller.Standard.RightHand)); +triggerPressMapping.from(Controller.Standard.LT).peek().to(makePressHandler(Controller.Standard.LeftHand)); +// END AVATAR SELECTOR LOGIC - function updateSendAssetParticleEffect() { - var timestampNow = Date.now(); - if ((timestampNow - particleEffectTimestamp) > (SEND_ASSET_PARTICLE_LIFETIME_SECONDS * 1000)) { - deleteSendAssetParticleEffect(); - return; - } else if ((timestampNow - particleEffectTimestamp) > SEND_ASSET_PARTICLE_EMITTING_DURATION) { - Entities.editEntity(sendAssetParticleEffect, { - isEmitting: 0 - }); - } else if (sendAssetParticleEffect) { - var recipientPosition = AvatarList.getAvatar(sendAssetRecipient).position; - var distance = Vec3.distance(recipientPosition, MyAvatar.position); - var accel = Vec3.subtract(recipientPosition, MyAvatar.position); - accel.y -= 3.0; - var life = Math.sqrt(2 * distance / Vec3.length(accel)); - Entities.editEntity(sendAssetParticleEffect, { - emitAcceleration: accel, - lifespan: life - }); - } +var grid = new Grid(); +function adjustPositionPerBoundingBox(position, direction, registration, dimensions, orientation) { + // Adjust the position such that the bounding box (registration, dimenions, and orientation) lies behind the original + // position in the given direction. + var CORNERS = [ + { x: 0, y: 0, z: 0 }, + { x: 0, y: 0, z: 1 }, + { x: 0, y: 1, z: 0 }, + { x: 0, y: 1, z: 1 }, + { x: 1, y: 0, z: 0 }, + { x: 1, y: 0, z: 1 }, + { x: 1, y: 1, z: 0 }, + { x: 1, y: 1, z: 1 } + ]; + + // Go through all corners and find least (most negative) distance in front of position. + var distance = 0; + for (var i = 0, length = CORNERS.length; i < length; i++) { + var cornerVector = + Vec3.multiplyQbyV(orientation, Vec3.multiplyVbyV(Vec3.subtract(CORNERS[i], registration), dimensions)); + var cornerDistance = Vec3.dot(cornerVector, direction); + distance = Math.min(cornerDistance, distance); + } + position = Vec3.sum(Vec3.multiply(distance, direction), position); + return position; +} + +var HALF_TREE_SCALE = 16384; +function getPositionToCreateEntity(extra) { + var CREATE_DISTANCE = 2; + var position; + var delta = extra !== undefined ? extra : 0; + if (Camera.mode === "entity" || Camera.mode === "independent") { + position = Vec3.sum(Camera.position, Vec3.multiply(Quat.getForward(Camera.orientation), CREATE_DISTANCE + delta)); + } else { + position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getForward(MyAvatar.orientation), CREATE_DISTANCE + delta)); + position.y += 0.5; } - function deleteSendAssetParticleEffect() { - if (sendAssetParticleEffectUpdateTimer) { - Script.clearInterval(sendAssetParticleEffectUpdateTimer); - sendAssetParticleEffectUpdateTimer = null; - } - if (sendAssetParticleEffect) { - sendAssetParticleEffect = Entities.deleteEntity(sendAssetParticleEffect); - } - sendAssetRecipient = null; + if (position.x > HALF_TREE_SCALE || position.y > HALF_TREE_SCALE || position.z > HALF_TREE_SCALE) { + return null; } - - var savedDisablePreviewOptionLocked = false; - var savedDisablePreviewOption = Menu.isOptionChecked("Disable Preview");; - function maybeEnableHMDPreview() { - // Set a small timeout to prevent sensitive data from being shown during - // UI fade - Script.setTimeout(function () { - setTabletVisibleInSecondaryCamera(true); - DesktopPreviewProvider.setPreviewDisabledReason("USER"); - Menu.setIsOptionChecked("Disable Preview", savedDisablePreviewOption); - savedDisablePreviewOptionLocked = false; - }, 150); + return position; +} + +function rezEntity(itemHref, itemType) { + var isWearable = itemType === "wearable"; + var success = Clipboard.importEntities(itemHref); + var wearableLocalPosition = null; + var wearableLocalRotation = null; + var wearableLocalDimensions = null; + var wearableDimensions = null; + + if (itemType === "contentSet") { + console.log("Item is a content set; codepath shouldn't go here."); + return; } - // Function Name: fromQml() - // - // Description: - // -Called when a message is received from Checkout.qml. The "message" argument is what is sent from the Checkout QML - // in the format "{method, params}", like json-rpc. - function fromQml(message) { - switch (message.method) { - case 'purchases_openWallet': - case 'checkout_openWallet': - case 'checkout_setUpClicked': - openWallet(); - break; - case 'purchases_walletNotSetUp': - ui.wireEventBridge(true); - ui.sendMessage({ - method: 'updateWalletReferrer', - referrer: "purchases" - }); - openWallet(); - break; - case 'checkout_walletNotSetUp': - ui.wireEventBridge(true); - ui.sendMessage({ - method: 'updateWalletReferrer', - referrer: message.referrer === "itemPage" ? message.itemId : message.referrer - }); - openWallet(); - break; - case 'checkout_cancelClicked': - ui.openNewApp(MARKETPLACE_URL + '/items/' + message.params, MARKETPLACES_INJECT_SCRIPT_URL); - break; - case 'header_goToPurchases': - case 'checkout_goToPurchases': - referrerURL = MARKETPLACE_URL_INITIAL; - filterText = message.filterText; - ui.openNewApp(MARKETPLACE_PURCHASES_QML_PATH); - break; - case 'checkout_itemLinkClicked': - ui.openNewApp(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL); - break; - case 'checkout_continueShopping': - ui.openNewApp(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); - break; - case 'purchases_itemInfoClicked': - var itemId = message.itemId; - if (itemId && itemId !== "") { - ui.openNewApp(MARKETPLACE_URL + '/items/' + itemId, MARKETPLACES_INJECT_SCRIPT_URL); - } - break; - case 'checkout_rezClicked': - case 'purchases_rezClicked': - rezEntity(message.itemHref, message.itemType); - break; - case 'header_marketplaceImageClicked': - case 'purchases_backClicked': - ui.openNewApp(message.referrerURL, MARKETPLACES_INJECT_SCRIPT_URL); - break; - case 'purchases_goToMarketplaceClicked': - ui.openNewApp(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); - break; - case 'updateItemClicked': - ui.openNewApp(message.upgradeUrl + "?edition=" + message.itemEdition, - MARKETPLACES_INJECT_SCRIPT_URL); - break; - case 'giftAsset': - - break; - case 'passphrasePopup_cancelClicked': - case 'needsLogIn_cancelClicked': - ui.openNewApp(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); - break; - case 'needsLogIn_loginClicked': - openLoginWindow(); - break; - case 'disableHmdPreview': - if (!savedDisablePreviewOption) { - savedDisablePreviewOption = Menu.isOptionChecked("Disable Preview"); - savedDisablePreviewOptionLocked = true; - } - - if (!savedDisablePreviewOption) { - DesktopPreviewProvider.setPreviewDisabledReason("SECURE_SCREEN"); - Menu.setIsOptionChecked("Disable Preview", true); - setTabletVisibleInSecondaryCamera(false); - } - break; - case 'maybeEnableHmdPreview': - maybeEnableHMDPreview(); - break; - case 'purchases_openGoTo': - ui.openNewApp("hifi/tablet/TabletAddressDialog.qml"); - break; - case 'purchases_itemCertificateClicked': - setCertificateInfo("", message.itemCertificateId); - break; - case 'inspectionCertificate_closeClicked': - ui.close(); - break; - case 'inspectionCertificate_requestOwnershipVerification': - ContextOverlay.requestOwnershipVerification(message.entity); - break; - case 'inspectionCertificate_showInMarketplaceClicked': - ui.openNewApp(message.marketplaceUrl, MARKETPLACES_INJECT_SCRIPT_URL); - break; - case 'header_myItemsClicked': - referrerURL = MARKETPLACE_URL_INITIAL; - filterText = ""; - ui.openNewApp(MARKETPLACE_PURCHASES_QML_PATH); - ui.wireEventBridge(true); - ui.sendMessage({ - method: 'purchases_showMyItems' - }); - break; - case 'refreshConnections': - // Guard to prevent this code from being executed while sending money -- - // we only want to execute this while sending non-HFC gifts - if (!onWalletScreen) { - print('Refreshing Connections...'); - getConnectionData(false); - } - break; - case 'enable_ChooseRecipientNearbyMode': - // Guard to prevent this code from being executed while sending money -- - // we only want to execute this while sending non-HFC gifts - if (!onWalletScreen) { - if (!isUpdateOverlaysWired) { - Script.update.connect(updateOverlays); - isUpdateOverlaysWired = true; + if (isWearable) { + var wearableTransforms = Settings.getValue("io.highfidelity.avatarStore.checkOut.transforms"); + if (!wearableTransforms) { + // TODO delete this clause + wearableTransforms = Settings.getValue("io.highfidelity.avatarStore.checkOut.tranforms"); + } + var certPos = itemHref.search("certificate_id="); // TODO how do I parse a URL from here? + if (certPos >= 0) { + certPos += 15; // length of "certificate_id=" + var certURLEncoded = itemHref.substring(certPos); + var certB64Encoded = decodeURIComponent(certURLEncoded); + for (var key in wearableTransforms) { + if (wearableTransforms.hasOwnProperty(key)) { + var certificateTransforms = wearableTransforms[key].certificateTransforms; + if (certificateTransforms) { + for (var certID in certificateTransforms) { + if (certificateTransforms.hasOwnProperty(certID) && + certID == certB64Encoded) { + var certificateTransform = certificateTransforms[certID]; + wearableLocalPosition = certificateTransform.localPosition; + wearableLocalRotation = certificateTransform.localRotation; + wearableLocalDimensions = certificateTransform.localDimensions; + wearableDimensions = certificateTransform.dimensions; + } + } } } - break; - case 'disable_ChooseRecipientNearbyMode': - // Guard to prevent this code from being executed while sending money -- - // we only want to execute this while sending non-HFC gifts - if (!onWalletScreen) { - if (isUpdateOverlaysWired) { - Script.update.disconnect(updateOverlays); - isUpdateOverlaysWired = false; - } - removeOverlays(); - } - break; - case 'wallet_availableUpdatesReceived': - case 'purchases_availableUpdatesReceived': - userHasUpdates = message.numUpdates > 0; - ui.messagesWaiting(userHasUpdates); - break; - case 'purchases_updateWearables': - var currentlyWornWearables = []; - var ATTACHMENT_SEARCH_RADIUS = 100; // meters (just in case) - - var nearbyEntities = Entities.findEntitiesByType('Model', MyAvatar.position, ATTACHMENT_SEARCH_RADIUS); - - for (var i = 0; i < nearbyEntities.length; i++) { - var currentProperties = Entities.getEntityProperties(nearbyEntities[i], ['certificateID', 'editionNumber', 'parentID']); - if (currentProperties.parentID === MyAvatar.sessionUUID) { - currentlyWornWearables.push({ - entityID: nearbyEntities[i], - entityCertID: currentProperties.certificateID, - entityEdition: currentProperties.editionNumber - }); - } - } - - ui.sendMessage({ method: 'updateWearables', wornWearables: currentlyWornWearables }); - break; - case 'sendAsset_sendPublicly': - if (message.assetName !== "") { - deleteSendAssetParticleEffect(); - sendAssetRecipient = message.recipient; - var amount = message.amount; - var props = SEND_ASSET_PARTICLE_PROPERTIES; - props.parentID = MyAvatar.sessionUUID; - props.position = MyAvatar.position; - props.position.y += 0.2; - if (message.effectImage) { - props.textures = message.effectImage; - } - sendAssetParticleEffect = Entities.addEntity(props, true); - particleEffectTimestamp = Date.now(); - updateSendAssetParticleEffect(); - sendAssetParticleEffectUpdateTimer = Script.setInterval(updateSendAssetParticleEffect, SEND_ASSET_PARTICLE_TIMER_UPDATE); - } - break; - case 'http.request': - // Handled elsewhere, don't log. - break; - case 'goToPurchases_fromWalletHome': // HRS FIXME What's this about? - break; - default: - print('Unrecognized message from Checkout.qml or Purchases.qml: ' + JSON.stringify(message)); - } - } - - // Function Name: onTabletScreenChanged() - // - // Description: - // -Called when the TabletScriptingInterface::screenChanged() signal is emitted. The "type" argument can be either the string - // value of "Home", "Web", "Menu", "QML", or "Closed". The "url" argument is only valid for Web and QML. - var onMarketplaceScreen = false; - var onWalletScreen = false; - var onCommerceScreen = false; - function onTabletScreenChanged(type, url) { - onMarketplaceScreen = type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1; - var onWalletScreenNow = url.indexOf(MARKETPLACE_WALLET_QML_PATH) !== -1; - var onCommerceScreenNow = type === "QML" && (url.indexOf(MARKETPLACE_CHECKOUT_QML_PATH) !== -1 || url === MARKETPLACE_PURCHASES_QML_PATH - || url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1); - - if ((!onWalletScreenNow && onWalletScreen) || (!onCommerceScreenNow && onCommerceScreen)) { // exiting wallet or commerce screen - maybeEnableHMDPreview(); - } - - onCommerceScreen = onCommerceScreenNow; - onWalletScreen = onWalletScreenNow; - ui.wireEventBridge(onMarketplaceScreen || onCommerceScreen || onWalletScreen); - - if (url === MARKETPLACE_PURCHASES_QML_PATH) { - ui.sendMessage({ - method: 'updatePurchases', - referrerURL: referrerURL, - filterText: filterText - }); - } - - ui.buttonActive((onMarketplaceScreen || onCommerceScreen) && !onWalletScreen); - - if (type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1) { - ContextOverlay.isInMarketplaceInspectionMode = true; - } else { - ContextOverlay.isInMarketplaceInspectionMode = false; - } - - if (onCommerceScreen) { - if (!isWired) { - Users.usernameFromIDReply.connect(usernameFromIDReply); - Controller.mousePressEvent.connect(handleMouseEvent); - Controller.mouseMoveEvent.connect(handleMouseMoveEvent); - triggerMapping.enable(); - triggerPressMapping.enable(); } - isWired = true; - Wallet.refreshWalletStatus(); - } else { - ui.sendMessage({ - method: 'inspectionCertificate_resetCert' + } + } + + if (success) { + var VERY_LARGE = 10000; + var isLargeImport = Clipboard.getClipboardContentsLargestDimension() >= VERY_LARGE; + var position = Vec3.ZERO; + if (!isLargeImport) { + position = getPositionToCreateEntity(Clipboard.getClipboardContentsLargestDimension() / 2); + } + if (position !== null && position !== undefined) { + var pastedEntityIDs = Clipboard.pasteEntities(position); + if (!isLargeImport) { + // The first entity in Clipboard gets the specified position with the rest being relative to it. Therefore, move + // entities after they're imported so that they're all the correct distance in front of and with geometric mean + // centered on the avatar/camera direction. + var deltaPosition = Vec3.ZERO; + var entityPositions = []; + var entityParentIDs = []; + + var propType = Entities.getEntityProperties(pastedEntityIDs[0], ["type"]).type; + var NO_ADJUST_ENTITY_TYPES = ["Zone", "Light", "ParticleEffect"]; + if (NO_ADJUST_ENTITY_TYPES.indexOf(propType) === -1) { + var targetDirection; + if (Camera.mode === "entity" || Camera.mode === "independent") { + targetDirection = Camera.orientation; + } else { + targetDirection = MyAvatar.orientation; + } + targetDirection = Vec3.multiplyQbyV(targetDirection, Vec3.UNIT_Z); + + var targetPosition = getPositionToCreateEntity(); + var deltaParallel = HALF_TREE_SCALE; // Distance to move entities parallel to targetDirection. + var deltaPerpendicular = Vec3.ZERO; // Distance to move entities perpendicular to targetDirection. + for (var i = 0, length = pastedEntityIDs.length; i < length; i++) { + var curLoopEntityProps = Entities.getEntityProperties(pastedEntityIDs[i], ["position", "dimensions", + "registrationPoint", "rotation", "parentID"]); + var adjustedPosition = adjustPositionPerBoundingBox(targetPosition, targetDirection, + curLoopEntityProps.registrationPoint, curLoopEntityProps.dimensions, curLoopEntityProps.rotation); + var delta = Vec3.subtract(adjustedPosition, curLoopEntityProps.position); + var distance = Vec3.dot(delta, targetDirection); + deltaParallel = Math.min(distance, deltaParallel); + deltaPerpendicular = Vec3.sum(Vec3.subtract(delta, Vec3.multiply(distance, targetDirection)), + deltaPerpendicular); + entityPositions[i] = curLoopEntityProps.position; + entityParentIDs[i] = curLoopEntityProps.parentID; + } + deltaPerpendicular = Vec3.multiply(1 / pastedEntityIDs.length, deltaPerpendicular); + deltaPosition = Vec3.sum(Vec3.multiply(deltaParallel, targetDirection), deltaPerpendicular); + } + + if (grid.getSnapToGrid()) { + var firstEntityProps = Entities.getEntityProperties(pastedEntityIDs[0], ["position", "dimensions", + "registrationPoint"]); + var positionPreSnap = Vec3.sum(deltaPosition, firstEntityProps.position); + position = grid.snapToSurface(grid.snapToGrid(positionPreSnap, false, firstEntityProps.dimensions, + firstEntityProps.registrationPoint), firstEntityProps.dimensions, firstEntityProps.registrationPoint); + deltaPosition = Vec3.subtract(position, firstEntityProps.position); + } + + if (!Vec3.equal(deltaPosition, Vec3.ZERO)) { + for (var editEntityIndex = 0, numEntities = pastedEntityIDs.length; editEntityIndex < numEntities; editEntityIndex++) { + if (Uuid.isNull(entityParentIDs[editEntityIndex])) { + Entities.editEntity(pastedEntityIDs[editEntityIndex], { + position: Vec3.sum(deltaPosition, entityPositions[editEntityIndex]) + }); + } + } + } + } + + if (isWearable) { + // apply the relative offsets saved during checkout + var offsets = {}; + if (wearableLocalPosition) { + offsets.localPosition = wearableLocalPosition; + } + if (wearableLocalRotation) { + offsets.localRotation = wearableLocalRotation; + } + if (wearableLocalDimensions) { + offsets.localDimensions = wearableLocalDimensions; + } else if (wearableDimensions) { + offsets.dimensions = wearableDimensions; + } + // we currently assume a wearable is a single entity + Entities.editEntity(pastedEntityIDs[0], offsets); + } + + var rezPosition = Entities.getEntityProperties(pastedEntityIDs[0], "position").position; + + Audio.playSound(REZZING_SOUND, { + volume: 1.0, + position: rezPosition, + localOnly: true }); - off(); - } - } - // - // Manage the connection between the button and the window. - // - var BUTTON_NAME = "MARKET"; - var MARKETPLACE_URL = METAVERSE_SERVER_URL + "/marketplace"; - var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. - function startup() { - ui = new AppUi({ - buttonName: BUTTON_NAME, - sortOrder: 9, - inject: MARKETPLACES_INJECT_SCRIPT_URL, - home: MARKETPLACE_URL_INITIAL, - onMessage: fromQml + } else { + Window.notifyEditError("Can't import entities: entities would be out of bounds."); + } + } else { + Window.notifyEditError("There was an error importing the entity file."); + } +} + +var referrerURL; // Used for updating Purchases QML +var filterText; // Used for updating Purchases QML +function onWebEventReceived(message) { + message = JSON.parse(message); + if (message.type === GOTO_DIRECTORY) { + ui.open(MARKETPLACES_URL, MARKETPLACES_INJECT_SCRIPT_URL); + } else if (message.type === QUERY_CAN_WRITE_ASSETS) { + ui.sendToHtml(CAN_WRITE_ASSETS + " " + Entities.canWriteAssets()); + } else if (message.type === WARN_USER_NO_PERMISSIONS) { + Window.alert(NO_PERMISSIONS_ERROR_MESSAGE); + } else if (message.type === CLARA_IO_STATUS) { + if (isDownloadBeingCancelled) { + return; + } + + var text = message.status; + if (messageBox === null) { + messageBox = Window.openMessageBox(CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); + } else { + Window.updateMessageBox(messageBox, CLARA_DOWNLOAD_TITLE, text, CANCEL_BUTTON, NO_BUTTON); + } + return; + } else if (message.type === CLARA_IO_DOWNLOAD) { + if (messageBox !== null) { + Window.closeMessageBox(messageBox); + messageBox = null; + } + return; + } else if (message.type === CLARA_IO_CANCELLED_DOWNLOAD) { + isDownloadBeingCancelled = false; + } else if (message.type === "CHECKOUT") { + wireQmlEventBridge(true); + ui.open(MARKETPLACE_CHECKOUT_QML_PATH); + ui.tablet.sendToQml({ + method: 'updateCheckoutQML', + params: message + }); + } else if (message.type === "REQUEST_SETTING") { + sendCommerceSettings(); + } else if (message.type === "PURCHASES") { + referrerURL = message.referrerURL; + filterText = ""; + ui.open(MARKETPLACE_PURCHASES_QML_PATH); + } else if (message.type === "LOGIN") { + openLoginWindow(); + } else if (message.type === "WALLET_SETUP") { + wireQmlEventBridge(true); + ui.tablet.sendToQml({ + method: 'updateWalletReferrer', + referrer: "marketplace cta" + }); + openWallet(); + } else if (message.type === "MY_ITEMS") { + referrerURL = MARKETPLACE_URL_INITIAL; + filterText = ""; + ui.open(MARKETPLACE_PURCHASES_QML_PATH); + wireQmlEventBridge(true); + ui.tablet.sendToQml({ + method: 'purchases_showMyItems' }); - - ContextOverlay.contextOverlayClicked.connect(setCertificateInfo); - Entities.canWriteAssetsChanged.connect(onCanWriteAssetsChanged); - GlobalServices.myUsernameChanged.connect(onUsernameChanged); - marketplaceButton.clicked.connect(onButtonClicked); - Wallet.walletStatusChanged.connect(sendCommerceSettings); - Window.messageBoxClosed.connect(onMessageBoxClosed); - - Wallet.refreshWalletStatus(); } - var isWired = false; - var isUpdateOverlaysWired = false; - function off() { - if (isWired) { - Users.usernameFromIDReply.disconnect(usernameFromIDReply); - Controller.mousePressEvent.disconnect(handleMouseEvent); - Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); - triggerMapping.disable(); - triggerPressMapping.disable(); +} +var sendAssetRecipient; +var sendAssetParticleEffectUpdateTimer; +var particleEffectTimestamp; +var sendAssetParticleEffect; +var SEND_ASSET_PARTICLE_TIMER_UPDATE = 250; +var SEND_ASSET_PARTICLE_EMITTING_DURATION = 3000; +var SEND_ASSET_PARTICLE_LIFETIME_SECONDS = 8; +var SEND_ASSET_PARTICLE_PROPERTIES = { + accelerationSpread: { x: 0, y: 0, z: 0 }, + alpha: 1, + alphaFinish: 1, + alphaSpread: 0, + alphaStart: 1, + azimuthFinish: 0, + azimuthStart: -6, + color: { red: 255, green: 222, blue: 255 }, + colorFinish: { red: 255, green: 229, blue: 225 }, + colorSpread: { red: 0, green: 0, blue: 0 }, + colorStart: { red: 243, green: 255, blue: 255 }, + emitAcceleration: { x: 0, y: 0, z: 0 }, // Immediately gets updated to be accurate + emitDimensions: { x: 0, y: 0, z: 0 }, + emitOrientation: { x: 0, y: 0, z: 0 }, + emitRate: 4, + emitSpeed: 2.1, + emitterShouldTrail: true, + isEmitting: 1, + lifespan: SEND_ASSET_PARTICLE_LIFETIME_SECONDS + 1, // Immediately gets updated to be accurate + lifetime: SEND_ASSET_PARTICLE_LIFETIME_SECONDS + 1, + maxParticles: 20, + name: 'asset-particles', + particleRadius: 0.2, + polarFinish: 0, + polarStart: 0, + radiusFinish: 0.05, + radiusSpread: 0, + radiusStart: 0.2, + speedSpread: 0, + textures: "http://hifi-content.s3.amazonaws.com/alan/dev/Particles/Bokeh-Particle-HFC.png", + type: 'ParticleEffect' +}; - isWired = false; - } - - if (isUpdateOverlaysWired) { - Script.update.disconnect(updateOverlays); - isUpdateOverlaysWired = false; - } - removeOverlays(); - } - function shutdown() { - maybeEnableHMDPreview(); +function updateSendAssetParticleEffect() { + var timestampNow = Date.now(); + if ((timestampNow - particleEffectTimestamp) > (SEND_ASSET_PARTICLE_LIFETIME_SECONDS * 1000)) { deleteSendAssetParticleEffect(); + return; + } else if ((timestampNow - particleEffectTimestamp) > SEND_ASSET_PARTICLE_EMITTING_DURATION) { + Entities.editEntity(sendAssetParticleEffect, { + isEmitting: 0 + }); + } else if (sendAssetParticleEffect) { + var recipientPosition = AvatarList.getAvatar(sendAssetRecipient).position; + var distance = Vec3.distance(recipientPosition, MyAvatar.position); + var accel = Vec3.subtract(recipientPosition, MyAvatar.position); + accel.y -= 3.0; + var life = Math.sqrt(2 * distance / Vec3.length(accel)); + Entities.editEntity(sendAssetParticleEffect, { + emitAcceleration: accel, + lifespan: life + }); + } +} - ContextOverlay.contextOverlayClicked.disconnect(setCertificateInfo); - Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged); - GlobalServices.myUsernameChanged.disconnect(onUsernameChanged); - marketplaceButton.clicked.disconnect(onButtonClicked); - Wallet.walletStatusChanged.disconnect(sendCommerceSettings); - Window.messageBoxClosed.disconnect(onMessageBoxClosed); +function deleteSendAssetParticleEffect() { + if (sendAssetParticleEffectUpdateTimer) { + Script.clearInterval(sendAssetParticleEffectUpdateTimer); + sendAssetParticleEffectUpdateTimer = null; + } + if (sendAssetParticleEffect) { + sendAssetParticleEffect = Entities.deleteEntity(sendAssetParticleEffect); + } + sendAssetRecipient = null; +} + +var savedDisablePreviewOption = Menu.isOptionChecked("Disable Preview"); +var UI_FADE_TIMEOUT_MS = 150; +function maybeEnableHMDPreview() { + // Set a small timeout to prevent sensitive data from being shown during UI fade + Script.setTimeout(function () { + setTabletVisibleInSecondaryCamera(true); + DesktopPreviewProvider.setPreviewDisabledReason("USER"); + Menu.setIsOptionChecked("Disable Preview", savedDisablePreviewOption); + }, UI_FADE_TIMEOUT_MS); +} + +var onQmlMessageReceived = function onQmlMessageReceived(message) { + if (message.messageSrc === "HTML") { + return; + } + switch (message.method) { + case 'purchases_openWallet': + case 'checkout_openWallet': + case 'checkout_setUpClicked': + openWallet(); + break; + case 'purchases_walletNotSetUp': + wireQmlEventBridge(true); + ui.tablet.sendToQml({ + method: 'updateWalletReferrer', + referrer: "purchases" + }); + openWallet(); + break; + case 'checkout_walletNotSetUp': + wireQmlEventBridge(true); + ui.tablet.sendToQml({ + method: 'updateWalletReferrer', + referrer: message.referrer === "itemPage" ? message.itemId : message.referrer + }); + openWallet(); + break; + case 'checkout_cancelClicked': + ui.open(MARKETPLACE_URL + '/items/' + message.params, MARKETPLACES_INJECT_SCRIPT_URL); + break; + case 'header_goToPurchases': + case 'checkout_goToPurchases': + referrerURL = MARKETPLACE_URL_INITIAL; + filterText = message.filterText; + ui.open(MARKETPLACE_PURCHASES_QML_PATH); + break; + case 'checkout_itemLinkClicked': + ui.open(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL); + break; + case 'checkout_continueShopping': + ui.open(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); + break; + case 'purchases_itemInfoClicked': + var itemId = message.itemId; + if (itemId && itemId !== "") { + ui.open(MARKETPLACE_URL + '/items/' + itemId, MARKETPLACES_INJECT_SCRIPT_URL); + } + break; + case 'checkout_rezClicked': + case 'purchases_rezClicked': + rezEntity(message.itemHref, message.itemType); + break; + case 'header_marketplaceImageClicked': + case 'purchases_backClicked': + ui.open(message.referrerURL, MARKETPLACES_INJECT_SCRIPT_URL); + break; + case 'purchases_goToMarketplaceClicked': + ui.open(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); + break; + case 'updateItemClicked': + ui.open(message.upgradeUrl + "?edition=" + message.itemEdition, + MARKETPLACES_INJECT_SCRIPT_URL); + break; + case 'giftAsset': + + break; + case 'passphrasePopup_cancelClicked': + case 'needsLogIn_cancelClicked': + ui.open(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); + break; + case 'needsLogIn_loginClicked': + openLoginWindow(); + break; + case 'disableHmdPreview': + if (!savedDisablePreviewOption) { + savedDisablePreviewOption = Menu.isOptionChecked("Disable Preview"); + } + + if (!savedDisablePreviewOption) { + DesktopPreviewProvider.setPreviewDisabledReason("SECURE_SCREEN"); + Menu.setIsOptionChecked("Disable Preview", true); + setTabletVisibleInSecondaryCamera(false); + } + break; + case 'maybeEnableHmdPreview': + maybeEnableHMDPreview(); + break; + case 'purchases_openGoTo': + ui.open("hifi/tablet/TabletAddressDialog.qml"); + break; + case 'purchases_itemCertificateClicked': + contextOverlayEntity = ""; + setCertificateInfo(contextOverlayEntity, message.itemCertificateId); + break; + case 'inspectionCertificate_closeClicked': + ui.close(); + break; + case 'inspectionCertificate_requestOwnershipVerification': + ContextOverlay.requestOwnershipVerification(message.entity); + break; + case 'inspectionCertificate_showInMarketplaceClicked': + ui.open(message.marketplaceUrl, MARKETPLACES_INJECT_SCRIPT_URL); + break; + case 'header_myItemsClicked': + referrerURL = MARKETPLACE_URL_INITIAL; + filterText = ""; + ui.open(MARKETPLACE_PURCHASES_QML_PATH); + wireQmlEventBridge(true); + ui.tablet.sendToQml({ + method: 'purchases_showMyItems' + }); + break; + case 'refreshConnections': + // Guard to prevent this code from being executed while sending money -- + // we only want to execute this while sending non-HFC gifts + if (!onWalletScreen) { + print('Refreshing Connections...'); + getConnectionData(false); + } + break; + case 'enable_ChooseRecipientNearbyMode': + // Guard to prevent this code from being executed while sending money -- + // we only want to execute this while sending non-HFC gifts + if (!onWalletScreen) { + if (!isUpdateOverlaysWired) { + Script.update.connect(updateOverlays); + isUpdateOverlaysWired = true; + } + } + break; + case 'disable_ChooseRecipientNearbyMode': + // Guard to prevent this code from being executed while sending money -- + // we only want to execute this while sending non-HFC gifts + if (!onWalletScreen) { + if (isUpdateOverlaysWired) { + Script.update.disconnect(updateOverlays); + isUpdateOverlaysWired = false; + } + removeOverlays(); + } + break; + case 'wallet_availableUpdatesReceived': + case 'purchases_availableUpdatesReceived': + userHasUpdates = message.numUpdates > 0; + ui.messagesWaiting(userHasUpdates); + break; + case 'purchases_updateWearables': + var currentlyWornWearables = []; + var ATTACHMENT_SEARCH_RADIUS = 100; // meters (just in case) + + var nearbyEntities = Entities.findEntitiesByType('Model', MyAvatar.position, ATTACHMENT_SEARCH_RADIUS); + + for (var i = 0; i < nearbyEntities.length; i++) { + var currentProperties = Entities.getEntityProperties( + nearbyEntities[i], ['certificateID', 'editionNumber', 'parentID'] + ); + if (currentProperties.parentID === MyAvatar.sessionUUID) { + currentlyWornWearables.push({ + entityID: nearbyEntities[i], + entityCertID: currentProperties.certificateID, + entityEdition: currentProperties.editionNumber + }); + } + } + + ui.tablet.sendToQml({ method: 'updateWearables', wornWearables: currentlyWornWearables }); + break; + case 'sendAsset_sendPublicly': + if (message.assetName !== "") { + deleteSendAssetParticleEffect(); + sendAssetRecipient = message.recipient; + var props = SEND_ASSET_PARTICLE_PROPERTIES; + props.parentID = MyAvatar.sessionUUID; + props.position = MyAvatar.position; + props.position.y += 0.2; + if (message.effectImage) { + props.textures = message.effectImage; + } + sendAssetParticleEffect = Entities.addEntity(props, true); + particleEffectTimestamp = Date.now(); + updateSendAssetParticleEffect(); + sendAssetParticleEffectUpdateTimer = Script.setInterval(updateSendAssetParticleEffect, + SEND_ASSET_PARTICLE_TIMER_UPDATE); + } + break; + case 'http.request': + // Handled elsewhere, don't log. + break; + case 'goToPurchases_fromWalletHome': // HRS FIXME What's this about? + break; + default: + print('Unrecognized message from Checkout.qml or Purchases.qml: ' + JSON.stringify(message)); + } +}; + +// Function Name: onTabletScreenChanged() +// +// Description: +// -Called when the TabletScriptingInterface::screenChanged() signal is emitted. The "type" argument can be either the string +// value of "Home", "Web", "Menu", "QML", or "Closed". The "url" argument is only valid for Web and QML. +var onMarketplaceScreen = false; +var onWalletScreen = false; +var onCommerceScreen = false; +var onInspectionCertificateScreen = false; +var onTabletScreenChanged = function onTabletScreenChanged(type, url) { + ui.setCurrentVisibleScreenMetadata(type, url); + onMarketplaceScreen = type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1; + onInspectionCertificateScreen = type === "QML" && url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1; + var onWalletScreenNow = url.indexOf(MARKETPLACE_WALLET_QML_PATH) !== -1; + var onCommerceScreenNow = type === "QML" && + (url.indexOf(MARKETPLACE_CHECKOUT_QML_PATH) !== -1 || url === MARKETPLACE_PURCHASES_QML_PATH || + onInspectionCertificateScreen); + + // exiting wallet or commerce screen + if ((!onWalletScreenNow && onWalletScreen) || (!onCommerceScreenNow && onCommerceScreen)) { + maybeEnableHMDPreview(); + } + + onCommerceScreen = onCommerceScreenNow; + onWalletScreen = onWalletScreenNow; + wireQmlEventBridge(onMarketplaceScreen || onCommerceScreen || onWalletScreen); + + if (url === MARKETPLACE_PURCHASES_QML_PATH) { + ui.tablet.sendToQml({ + method: 'updatePurchases', + referrerURL: referrerURL, + filterText: filterText + }); + } + + ui.isOpen = (onMarketplaceScreen || onCommerceScreen) && !onWalletScreen; + ui.buttonActive(ui.isOpen); + + if (type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1) { + ContextOverlay.isInMarketplaceInspectionMode = true; + } else { + ContextOverlay.isInMarketplaceInspectionMode = false; + } + + if (onInspectionCertificateScreen) { + setCertificateInfo(contextOverlayEntity); + } + + if (onCommerceScreen) { + if (!isWired) { + Users.usernameFromIDReply.connect(usernameFromIDReply); + Controller.mousePressEvent.connect(handleMouseEvent); + Controller.mouseMoveEvent.connect(handleMouseMoveEvent); + triggerMapping.enable(); + triggerPressMapping.enable(); + } + isWired = true; + Wallet.refreshWalletStatus(); + } else { + ui.tablet.sendToQml({ + method: 'inspectionCertificate_resetCert' + }); off(); } + console.debug(ui.buttonName + " app reports: Tablet screen changed.\nNew screen type: " + type + + "\nNew screen URL: " + url + "\nCurrent app open status: " + ui.isOpen + "\n"); +}; - // - // Run the functions. - // - startup(); - Script.scriptEnding.connect(shutdown); +// +// Manage the connection between the button and the window. +// +var BUTTON_NAME = "MARKET"; +var MARKETPLACE_URL = METAVERSE_SERVER_URL + "/marketplace"; +var MARKETPLACE_URL_INITIAL = MARKETPLACE_URL + "?"; // Append "?" to signal injected script that it's the initial page. +var ui; +function startup() { + ui = new AppUi({ + buttonName: BUTTON_NAME, + sortOrder: 9, + inject: MARKETPLACES_INJECT_SCRIPT_URL, + home: MARKETPLACE_URL_INITIAL, + onScreenChanged: onTabletScreenChanged, + onMessage: onQmlMessageReceived + }); + ContextOverlay.contextOverlayClicked.connect(openInspectionCertificateQML); + Entities.canWriteAssetsChanged.connect(onCanWriteAssetsChanged); + GlobalServices.myUsernameChanged.connect(onUsernameChanged); + ui.tablet.webEventReceived.connect(onWebEventReceived); + Wallet.walletStatusChanged.connect(sendCommerceSettings); + Window.messageBoxClosed.connect(onMessageBoxClosed); + Wallet.refreshWalletStatus(); +} + +var isWired = false; +var isUpdateOverlaysWired = false; +function off() { + if (isWired) { + Users.usernameFromIDReply.disconnect(usernameFromIDReply); + Controller.mousePressEvent.disconnect(handleMouseEvent); + Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); + triggerMapping.disable(); + triggerPressMapping.disable(); + + isWired = false; + } + + if (isUpdateOverlaysWired) { + Script.update.disconnect(updateOverlays); + isUpdateOverlaysWired = false; + } + removeOverlays(); +} +function shutdown() { + maybeEnableHMDPreview(); + deleteSendAssetParticleEffect(); + + Window.messageBoxClosed.disconnect(onMessageBoxClosed); + Wallet.walletStatusChanged.disconnect(sendCommerceSettings); + ui.tablet.webEventReceived.disconnect(onWebEventReceived); + GlobalServices.myUsernameChanged.disconnect(onUsernameChanged); + Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged); + ContextOverlay.contextOverlayClicked.disconnect(openInspectionCertificateQML); + + off(); +} + +// +// Run the functions. +// +startup(); +Script.scriptEnding.connect(shutdown); }()); // END LOCAL_SCOPE diff --git a/scripts/system/pal.js b/scripts/system/pal.js index ebb45130e5..4593c8c2de 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -713,7 +713,7 @@ function tabletVisibilityChanged() { if (!ui.tablet.tabletShown && ui.isOpen) { ui.close(); } - } +} var UPDATE_INTERVAL_MS = 100; var updateInterval; From 5a1d074e2fdcf3cd12bcf9f95627ba5171e88063 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 12 Sep 2018 16:04:37 -0700 Subject: [PATCH 066/259] Goto now uses AppUi; lint --- scripts/system/commerce/wallet.js | 2 +- scripts/system/help.js | 25 +-- scripts/system/pal.js | 9 +- scripts/system/snapshot.js | 3 +- scripts/system/tablet-goto.js | 247 +++++++++++++----------------- 5 files changed, 131 insertions(+), 155 deletions(-) diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 213e008bdc..993ea30c2e 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -1,5 +1,5 @@ "use strict"; -/*jslint vars:true, plusplus:true, forin:true*/ +/* jslint vars:true, plusplus:true, forin:true */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ // // wallet.js diff --git a/scripts/system/help.js b/scripts/system/help.js index 325a2c243b..40bbf6dbe2 100644 --- a/scripts/system/help.js +++ b/scripts/system/help.js @@ -1,5 +1,5 @@ "use strict"; - +/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ // // help.js // scripts/system/ @@ -13,16 +13,17 @@ /* globals Tablet, Script, HMD, Controller, Menu */ (function () { // BEGIN LOCAL_SCOPE - var AppUi = Script.require('appUi'); +var AppUi = Script.require('appUi'); - var HELP_URL = Script.resourcesPath() + "html/tabletHelp.html"; - var HELP_BUTTON_NAME = "HELP"; - function startup() { - ui = new AppUi({ - buttonName: HELP_BUTTON_NAME, - sortOrder: 6, - home: HELP_URL - }); - } - startup(); +var HELP_URL = Script.resourcesPath() + "html/tabletHelp.html"; +var HELP_BUTTON_NAME = "HELP"; +var ui; +function startup() { + ui = new AppUi({ + buttonName: HELP_BUTTON_NAME, + sortOrder: 6, + home: HELP_URL + }); +} +startup(); }()); // END LOCAL_SCOPE diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 4593c8c2de..5e38624b35 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -1,6 +1,9 @@ "use strict"; -/*jslint vars:true, plusplus:true, forin:true*/ -/*global Tablet, Settings, Script, AvatarList, Users, Entities, MyAvatar, Camera, Overlays, Vec3, Quat, HMD, Controller, Account, UserActivityLogger, Messages, Window, XMLHttpRequest, print, location, getControllerWorldLocation*/ +/* jslint vars:true, plusplus:true, forin:true */ +/* global Tablet, Settings, Script, AvatarList, Users, Entities, + MyAvatar, Camera, Overlays, Vec3, Quat, HMD, Controller, Account, + UserActivityLogger, Messages, Window, XMLHttpRequest, print, location, getControllerWorldLocation +*/ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ // // pal.js @@ -20,7 +23,7 @@ var AppUi = Script.require('appUi'); var populateNearbyUserList, color, textures, removeOverlays, controllerComputePickRay, off, receiveMessage, avatarDisconnected, clearLocalQMLDataAndClosePAL, - CHANNEL, getConnectionData, findableByChanged, + CHANNEL, getConnectionData, avatarAdded, avatarRemoved, avatarSessionChanged; // forward references; // hardcoding these as it appears we cannot traverse the originalTextures in overlays??? Maybe I've missed diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index fe952ab1a7..3d744b3bd2 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -7,7 +7,8 @@ // Distributed under the Apache License, Version 2.0 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* globals Tablet, Script, HMD, Settings, DialogsManager, Menu, Reticle, OverlayWebWindow, Desktop, Account, MyAvatar, Snapshot */ +/* globals Tablet, Script, HMD, Settings, DialogsManager, Menu, Reticle, + OverlayWebWindow, Desktop, Account, MyAvatar, Snapshot */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ (function () { // BEGIN LOCAL_SCOPE diff --git a/scripts/system/tablet-goto.js b/scripts/system/tablet-goto.js index 8fafac7685..804f838d04 100644 --- a/scripts/system/tablet-goto.js +++ b/scripts/system/tablet-goto.js @@ -1,9 +1,10 @@ "use strict"; -/*jslint vars:true, plusplus:true, forin:true*/ -/*global Window, Script, Tablet, HMD, Controller, Account, XMLHttpRequest, location, print*/ +/* jslint vars:true, plusplus:true, forin:true */ +/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ +/* global Window, Script, Tablet, HMD, Controller, Account, XMLHttpRequest, location, print */ // -// goto.js +// tablet-goto.js // scripts/system/ // // Created by Dante Ruiz on 8 February 2017 @@ -14,148 +15,118 @@ // (function () { // BEGIN LOCAL_SCOPE +var request = Script.require('request').request; +var AppUi = Script.require('appUi'); +var DEBUG = false; +function debug() { + if (!DEBUG) { + return; + } + print('tablet-goto.js:', [].map.call(arguments, JSON.stringify)); +} - var request = Script.require('request').request; - var DEBUG = false; - function debug() { - if (!DEBUG) { +var stories = {}, pingPong = false; +function expire(id) { + var options = { + uri: Account.metaverseServerURL + '/api/v1/user_stories/' + id, + method: 'PUT', + json: true, + body: {expire: "true"} + }; + request(options, function (error, response) { + debug('expired story', options, 'error:', error, 'response:', response); + if (error || (response.status !== 'success')) { + print("ERROR expiring story: ", error || response.status); + } + }); +} +var PER_PAGE_DEBUG = 10; +var PER_PAGE_NORMAL = 100; +function pollForAnnouncements() { + // We could bail now if !Account.isLoggedIn(), but what if we someday have system-wide announcments? + var actions = 'announcement'; + var count = DEBUG ? PER_PAGE_DEBUG : PER_PAGE_NORMAL; + var options = [ + 'now=' + new Date().toISOString(), + 'include_actions=' + actions, + 'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'), + 'require_online=true', + 'protocol=' + encodeURIComponent(Window.protocolSignature()), + 'per_page=' + count + ]; + var url = Account.metaverseServerURL + '/api/v1/user_stories?' + options.join('&'); + request({ + uri: url + }, function (error, data) { + debug(url, error, data); + if (error || (data.status !== 'success')) { + print("Error: unable to get", url, error || data.status); return; } - print('tablet-goto.js:', [].map.call(arguments, JSON.stringify)); - } - - var gotoQmlSource = "hifi/tablet/TabletAddressDialog.qml"; - var buttonName = "GOTO"; - var onGotoScreen = false; - var shouldActivateButton = false; - function ignore() { } - - var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - var NORMAL_ICON = "icons/tablet-icons/goto-i.svg"; - var NORMAL_ACTIVE = "icons/tablet-icons/goto-a.svg"; - var WAITING_ICON = "icons/tablet-icons/goto-msg.svg"; - var button = tablet.addButton({ - icon: NORMAL_ICON, - activeIcon: NORMAL_ACTIVE, - text: buttonName, - sortOrder: 8 - }); - - function messagesWaiting(isWaiting) { - button.editProperties({ - icon: isWaiting ? WAITING_ICON : NORMAL_ICON - // No need for a different activeIcon, because we issue messagesWaiting(false) when the button goes active anyway. - }); - } - - function onClicked() { - if (onGotoScreen) { - // for toolbar-mode: go back to home screen, this will close the window. - tablet.gotoHomeScreen(); - } else { - shouldActivateButton = true; - tablet.loadQMLSource(gotoQmlSource); - onGotoScreen = true; - } - } - - function onScreenChanged(type, url) { - ignore(type); - if (url === gotoQmlSource) { - onGotoScreen = true; - shouldActivateButton = true; - button.editProperties({isActive: shouldActivateButton}); - messagesWaiting(false); - } else { - shouldActivateButton = false; - onGotoScreen = false; - button.editProperties({isActive: shouldActivateButton}); - } - } - button.clicked.connect(onClicked); - tablet.screenChanged.connect(onScreenChanged); - - var stories = {}, pingPong = false; - function expire(id) { - var options = { - uri: Account.metaverseServerURL + '/api/v1/user_stories/' + id, - method: 'PUT', - json: true, - body: {expire: "true"} - }; - request(options, function (error, response) { - debug('expired story', options, 'error:', error, 'response:', response); - if (error || (response.status !== 'success')) { - print("ERROR expiring story: ", error || response.status); + var didNotify = false, key; + pingPong = !pingPong; + data.user_stories.forEach(function (story) { + var stored = stories[story.id], storedOrNew = stored || story; + debug('story exists:', !!stored, storedOrNew); + if ((storedOrNew.username === Account.username) && (storedOrNew.place_name !== location.placename)) { + if (storedOrNew.audience === 'for_connections') { // Only expire if we haven't already done so. + expire(story.id); + } + return; // before marking } - }); - } - function pollForAnnouncements() { - // We could bail now if !Account.isLoggedIn(), but what if we someday have system-wide announcments? - var actions = 'announcement'; - var count = DEBUG ? 10 : 100; - var options = [ - 'now=' + new Date().toISOString(), - 'include_actions=' + actions, - 'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'), - 'require_online=true', - 'protocol=' + encodeURIComponent(Window.protocolSignature()), - 'per_page=' + count - ]; - var url = Account.metaverseServerURL + '/api/v1/user_stories?' + options.join('&'); - request({ - uri: url - }, function (error, data) { - debug(url, error, data); - if (error || (data.status !== 'success')) { - print("Error: unable to get", url, error || data.status); + storedOrNew.pingPong = pingPong; + if (stored) { // already seen return; } - var didNotify = false, key; - pingPong = !pingPong; - data.user_stories.forEach(function (story) { - var stored = stories[story.id], storedOrNew = stored || story; - debug('story exists:', !!stored, storedOrNew); - if ((storedOrNew.username === Account.username) && (storedOrNew.place_name !== location.placename)) { - if (storedOrNew.audience == 'for_connections') { // Only expire if we haven't already done so. - expire(story.id); - } - return; // before marking - } - storedOrNew.pingPong = pingPong; - if (stored) { // already seen - return; - } - stories[story.id] = story; - var message = story.username + " " + story.action_string + " in " + story.place_name + ". Open GOTO to join them."; - Window.displayAnnouncement(message); - didNotify = true; - }); - for (key in stories) { // Any story we were tracking that was not marked, has expired. - if (stories[key].pingPong !== pingPong) { - debug('removing story', key); - delete stories[key]; - } - } - if (didNotify) { - messagesWaiting(true); - if (HMD.isHandControllerAvailable()) { - var STRENGTH = 1.0, DURATION_MS = 60, HAND = 2; // both hands - Controller.triggerHapticPulse(STRENGTH, DURATION_MS, HAND); - } - } else if (!Object.keys(stories).length) { // If there's nothing being tracked, then any messageWaiting has expired. - messagesWaiting(false); - } + stories[story.id] = story; + var message = story.username + " " + story.action_string + " in " + + story.place_name + ". Open GOTO to join them."; + Window.displayAnnouncement(message); + didNotify = true; }); - } - var ANNOUNCEMENTS_POLL_TIME_MS = (DEBUG ? 10 : 60) * 1000; - var pollTimer = Script.setInterval(pollForAnnouncements, ANNOUNCEMENTS_POLL_TIME_MS); - - Script.scriptEnding.connect(function () { - Script.clearInterval(pollTimer); - button.clicked.disconnect(onClicked); - tablet.removeButton(button); - tablet.screenChanged.disconnect(onScreenChanged); + for (key in stories) { // Any story we were tracking that was not marked, has expired. + if (stories[key].pingPong !== pingPong) { + debug('removing story', key); + delete stories[key]; + } + } + if (didNotify) { + ui.messagesWaiting(true); + if (HMD.isHandControllerAvailable()) { + var STRENGTH = 1.0, DURATION_MS = 60, HAND = 2; // both hands + Controller.triggerHapticPulse(STRENGTH, DURATION_MS, HAND); + } + } else if (!Object.keys(stories).length) { // If there's nothing being tracked, then any messageWaiting has expired. + ui.messagesWaiting(false); + } }); +} +var MS_PER_SEC = 1000; +var DEBUG_POLL_TIME_SEC = 10; +var NORMAL_POLL_TIME_SEC = 60; +var ANNOUNCEMENTS_POLL_TIME_MS = (DEBUG ? DEBUG_POLL_TIME_SEC : NORMAL_POLL_TIME_SEC) * MS_PER_SEC; +var pollTimer = Script.setInterval(pollForAnnouncements, ANNOUNCEMENTS_POLL_TIME_MS); +function gotoOpened() { + ui.messagesWaiting(false); +} + +var ui; +var GOTO_QML_SOURCE = "hifi/tablet/TabletAddressDialog.qml"; +var BUTTON_NAME = "GOTO"; +function startup() { + ui = new AppUi({ + buttonName: BUTTON_NAME, + sortOrder: 8, + onOpened: gotoOpened, + home: GOTO_QML_SOURCE + }); +} + +function shutdown() { + Script.clearInterval(pollTimer); +} + +startup(); +Script.scriptEnding.connect(shutdown); }()); // END LOCAL_SCOPE From 54dd868b3c8ed42cc183bf4ebfc4fdc609b766d4 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 13 Sep 2018 11:54:59 +1200 Subject: [PATCH 067/259] Rotate mini tablet to keep it "vertical" --- scripts/system/miniTablet.js | 54 +++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 2482efec8d..8c2c8f8dc8 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -317,6 +317,7 @@ dimensions: Vec3.multiply(scaleFactor, MINI_UI_DIMENSIONS), dpi: MINI_UI_DPI / scaleFactor }); + updateRotation(); } function startExpandingTablet(hand) { @@ -380,6 +381,51 @@ }); } + function updateRotation() { + // Update the rotation of the tablet about its face normal so that its base is horizontal. + var COS_5_DEGREES = 0.996, + RADIANS_TO_DEGREES = DEGREES_180 / Math.PI, + defaultLocalRotation, + handOrientation, + defaultOrientation, + faceNormal, + desiredOrientation, + defaultYAxis, + desiredYAxis, + cross, + dot, + deltaAngle, + deltaRotation, + localRotation; + + defaultLocalRotation = MINI_ROTATIONS[uiHand]; + handOrientation = + Quat.multiply(MyAvatar.orientation, MyAvatar.getAbsoluteJointRotationInObjectFrame(handJointIndex(uiHand))); + defaultOrientation = Quat.multiply(handOrientation, defaultLocalRotation); + faceNormal = Vec3.multiplyQbyV(defaultOrientation, Vec3.UNIT_Z); + + if (Math.abs(Vec3.dot(faceNormal, Vec3.UNIT_Y)) > COS_5_DEGREES) { + // Don't rotate mini tablet if almost flat in the x-z plane. + return; + } else { + // Rotate the tablet so that its base is parallel with the x-z plane. + desiredOrientation = Quat.lookAt(Vec3.ZERO, Vec3.multiplyQbyV(defaultOrientation, Vec3.UNIT_Z), Vec3.UNIT_Y); + defaultYAxis = Vec3.multiplyQbyV(defaultOrientation, Vec3.UNIT_Y); + desiredYAxis = Vec3.multiplyQbyV(desiredOrientation, Vec3.UNIT_Y); + cross = Vec3.cross(defaultYAxis, desiredYAxis); + dot = Vec3.dot(defaultYAxis, desiredYAxis); + deltaAngle = Math.atan2(Vec3.length(cross), dot) * RADIANS_TO_DEGREES; + if (Vec3.dot(cross, Vec3.multiplyQbyV(desiredOrientation, Vec3.UNIT_Z)) > 0) { + deltaAngle = -deltaAngle; + } + deltaRotation = Quat.angleAxis(deltaAngle, Vec3.multiplyQbyV(defaultLocalRotation, Vec3.UNIT_Z)); + localRotation = Quat.multiply(deltaRotation, defaultLocalRotation); + } + Overlays.editOverlay(miniOverlay, { + localRotation: localRotation + }); + } + function hide() { Overlays.editOverlay(miniOverlay, { parentID: Uuid.NULL, // Release hold so that hand can grab tablet proper. @@ -446,6 +492,7 @@ size: size, startExpandingTablet: startExpandingTablet, sizeAboutHandles: sizeAboutHandles, + updateRotation: updateRotation, hide: hide, destroy: destroy }; @@ -478,7 +525,7 @@ miniState = MINI_DISABLED, miniHand, updateTimer = null, - UPDATE_INTERVAL = 300, + UPDATE_INTERVAL = 25, // Mini tablet scaling. MINI_SCALE_DURATION = 250, @@ -702,6 +749,11 @@ } else { setState(MINI_HIDING); } + + // If state hasn't changed, update mini tablet rotation. + if (miniState === MINI_VISIBLE) { + ui.updateRotation(); + } } function updateMiniGrabbed() { From 0437c50c0676c09bfcf1857f98379faa88deb448 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 13 Sep 2018 12:20:00 +1200 Subject: [PATCH 068/259] Fix mini tablet not reliably being grabbed --- scripts/system/miniTablet.js | 39 +++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 8c2c8f8dc8..3873782a19 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -543,6 +543,11 @@ isGoto = false, TABLET_ADDRESS_DIALOG = "hifi/tablet/TabletAddressDialog.qml", + // Trigger values. + leftTriggerOn = 0, + rightTriggerOn = 0, + MAX_TRIGGER_ON_TIME = 100, + // Visibility. MIN_HAND_CAMERA_ANGLE = 30, DEGREES_180 = 180, @@ -579,6 +584,10 @@ // Should show mini tablet if it would be oriented toward the camera. var show, pose, + isLeftTriggerOff, + isRightTriggerOff, + wasLeftTriggerOff = true, + wasRightTriggerOff = true, jointIndex, handPosition, handOrientation, @@ -593,12 +602,32 @@ show = pose.valid; // Shouldn't show mini tablet on hand if that hand's trigger is pressed (i.e., laser is searching or grabbing - // something) or the other hand's trigger is pressed unless it is pointing at the mini tablet. + // something) or the other hand's trigger is pressed unless it is pointing at the mini tablet. Allow the triggers + // to be pressed briefly to allow for the grabbing process. if (show) { - show = Controller.getValue(hand === LEFT_HAND ? Controller.Standard.LT : Controller.Standard.RT) < - TRIGGER_OFF_VALUE - && (Controller.getValue(hand === LEFT_HAND ? Controller.Standard.RT : Controller.Standard.LT) < - TRIGGER_OFF_VALUE || ui.isLaserPointingAt()); + isLeftTriggerOff = Controller.getValue(Controller.Standard.LT) < TRIGGER_OFF_VALUE; + if (!isLeftTriggerOff) { + if (leftTriggerOn === 0) { + leftTriggerOn = Date.now(); + } else { + wasLeftTriggerOff = Date.now() - leftTriggerOn < MAX_TRIGGER_ON_TIME; + } + } else { + leftTriggerOn = 0; + } + isRightTriggerOff = Controller.getValue(Controller.Standard.RT) < TRIGGER_OFF_VALUE; + if (!isRightTriggerOff) { + if (rightTriggerOn === 0) { + rightTriggerOn = Date.now(); + } else { + wasRightTriggerOff = Date.now() - rightTriggerOn < MAX_TRIGGER_ON_TIME; + } + } else { + rightTriggerOn = 0; + } + + show = (hand === LEFT_HAND ? wasLeftTriggerOff : wasRightTriggerOff) + && ((hand === LEFT_HAND ? wasRightTriggerOff : wasLeftTriggerOff) || ui.isLaserPointingAt()); } // Should show mini tablet if it would be oriented toward the camera. From 4978f80f43758e861944c37797cd4d8b99e9f530 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 13 Sep 2018 12:27:30 +1200 Subject: [PATCH 069/259] Don't turn on laser when mini tablet is expanding --- scripts/system/miniTablet.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 3873782a19..a35e5fc1af 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -342,11 +342,6 @@ miniInitialWidth = MINI_DIMENSIONS.x; miniTargetWidth = getTabletWidthFromSettings(); miniTargetLocalRotation = Quat.multiply(miniExpandLocalRotation, miniExpandDeltaRotation); - - // Don't let other hand grab while expanding. - Overlays.editOverlay(miniOverlay, { - grabbable: false - }); } function sizeAboutHandles(scaleFactor) { From 27c7592d986af1424c4e85a7df5d44e2a50b2d7f Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Thu, 13 Sep 2018 14:30:42 -0300 Subject: [PATCH 070/259] Patch qt 5.11.1 to support voice_communication opensl es preset --- android/build.gradle | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index a6de0d469c..a5d40b9b43 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -73,16 +73,16 @@ def baseUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/android/' def breakpadDumpSymsDir = new File("${appDir}/build/tmp/breakpadDumpSyms") def qtFile='qt-5.11.1_linux_armv8-libcpp_openssl.tgz' -def qtChecksum='f312c47cd8b8dbca824c32af4eec5e66' -def qtVersionId='nyCGcb91S4QbYeJhUkawO5x1lrLdSNB_' +def qtChecksum='bf9e734d9c4e77c4807a38c93515e25c' +def qtVersionId='Qt3rvI0O7l5gLkacia2vA6KAchTgFkFf' if (Os.isFamily(Os.FAMILY_MAC)) { - qtFile = 'qt-5.11.1_osx_armv8-libcpp_openssl.tgz' - qtChecksum='a0c8b394aec5b0fcd46714ca3a53278a' - qtVersionId='QNa.lwNJaPc0eGuIL.xZ8ebeTuLL7rh8' + qtFile = 'qt-5.11.1_osx_armv8-libcpp_openssl_patched.tgz' + qtChecksum='5f12fd4fb25efe648c2fd161fd44998a' + qtVersionId='XXJ7ii1.xYzgFdWPnU.8mXnCj.q6y0Q5' } else if (Os.isFamily(Os.FAMILY_WINDOWS)) { - qtFile = 'qt-5.11.1_win_armv8-libcpp_openssl.tgz' - qtChecksum='d80aed4233ce9e222aae8376e7a94bf9' - qtVersionId='iDVXu0i3WEXRFIxQCtzcJ2XuKrE8RIqB' + qtFile = 'qt-5.11.1_win_armv8-libcpp_openssl_patched.tgz' + qtChecksum='0582191cc55431aa4f660848a542883e' + qtVersionId='JfWM0P_Mz5Qp0LwpzhrsRwN3fqlLSFeT' } def packages = [ From ec2c0226a5aadcf0ab762d463b10a30bc10c05ca Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Thu, 13 Sep 2018 16:42:12 -0300 Subject: [PATCH 071/259] Replace qt build version (osx) --- android/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index a5d40b9b43..8a3f4d75b9 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -77,8 +77,8 @@ def qtChecksum='bf9e734d9c4e77c4807a38c93515e25c' def qtVersionId='Qt3rvI0O7l5gLkacia2vA6KAchTgFkFf' if (Os.isFamily(Os.FAMILY_MAC)) { qtFile = 'qt-5.11.1_osx_armv8-libcpp_openssl_patched.tgz' - qtChecksum='5f12fd4fb25efe648c2fd161fd44998a' - qtVersionId='XXJ7ii1.xYzgFdWPnU.8mXnCj.q6y0Q5' + qtChecksum='c83cc477c08a892e00c71764dca051a0' + qtVersionId='QNa.lwNJaPc0eGuIL.xZ8ebeTuLL7rh8' } else if (Os.isFamily(Os.FAMILY_WINDOWS)) { qtFile = 'qt-5.11.1_win_armv8-libcpp_openssl_patched.tgz' qtChecksum='0582191cc55431aa4f660848a542883e' From 74ea1548a929f4936684cc1d9ab59adfc553a85a Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 13 Sep 2018 14:24:49 -0700 Subject: [PATCH 072/259] Use chrono::system_clock for usecTimestampNow() on all platforms --- libraries/shared/src/SharedUtil.cpp | 81 ++--------------------------- 1 file changed, 5 insertions(+), 76 deletions(-) diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index bb22a1e753..886445824b 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -19,8 +19,8 @@ #include #include #include -#include #include +#include #include @@ -122,87 +122,16 @@ void setGlobalInstance(const char* propertyName, const QVariant& variant) { } } +// Do we still need this? static qint64 usecTimestampNowAdjust = 0; // in usec void usecTimestampNowForceClockSkew(qint64 clockSkew) { ::usecTimestampNowAdjust = clockSkew; } -static std::atomic TIME_REFERENCE { 0 }; // in usec -static std::once_flag usecTimestampNowIsInitialized; -static QElapsedTimer timestampTimer; - quint64 usecTimestampNow(bool wantDebug) { - std::call_once(usecTimestampNowIsInitialized, [&] { - TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * USECS_PER_MSEC; // ms to usec - timestampTimer.start(); - }); - - quint64 now; - quint64 nsecsElapsed = timestampTimer.nsecsElapsed(); - quint64 usecsElapsed = nsecsElapsed / NSECS_PER_USEC; // nsec to usec - - // QElapsedTimer may not advance if the CPU has gone to sleep. In which case it - // will begin to deviate from real time. We detect that here, and reset if necessary - quint64 msecsCurrentTime = QDateTime::currentMSecsSinceEpoch(); - quint64 msecsEstimate = (TIME_REFERENCE + usecsElapsed) / USECS_PER_MSEC; // usecs to msecs - int possibleSkew = msecsEstimate - msecsCurrentTime; - const int TOLERANCE = 10 * MSECS_PER_SECOND; // up to 10 seconds of skew is tolerated - if (abs(possibleSkew) > TOLERANCE) { - // reset our TIME_REFERENCE and timer - TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * USECS_PER_MSEC; // ms to usec - timestampTimer.restart(); - now = TIME_REFERENCE + ::usecTimestampNowAdjust; - - if (wantDebug) { - qCDebug(shared) << "usecTimestampNow() - resetting QElapsedTimer. "; - qCDebug(shared) << " msecsCurrentTime:" << msecsCurrentTime; - qCDebug(shared) << " msecsEstimate:" << msecsEstimate; - qCDebug(shared) << " possibleSkew:" << possibleSkew; - qCDebug(shared) << " TOLERANCE:" << TOLERANCE; - - qCDebug(shared) << " nsecsElapsed:" << nsecsElapsed; - qCDebug(shared) << " usecsElapsed:" << usecsElapsed; - - QDateTime currentLocalTime = QDateTime::currentDateTime(); - - quint64 msecsNow = now / 1000; // usecs to msecs - QDateTime nowAsString; - nowAsString.setMSecsSinceEpoch(msecsNow); - - qCDebug(shared) << " now:" << now; - qCDebug(shared) << " msecsNow:" << msecsNow; - - qCDebug(shared) << " nowAsString:" << nowAsString.toString("yyyy-MM-dd hh:mm:ss.zzz"); - qCDebug(shared) << " currentLocalTime:" << currentLocalTime.toString("yyyy-MM-dd hh:mm:ss.zzz"); - } - } else { - now = TIME_REFERENCE + usecsElapsed + ::usecTimestampNowAdjust; - } - - if (wantDebug) { - QDateTime currentLocalTime = QDateTime::currentDateTime(); - - quint64 msecsNow = now / 1000; // usecs to msecs - QDateTime nowAsString; - nowAsString.setMSecsSinceEpoch(msecsNow); - - quint64 msecsTimeReference = TIME_REFERENCE / 1000; // usecs to msecs - QDateTime timeReferenceAsString; - timeReferenceAsString.setMSecsSinceEpoch(msecsTimeReference); - - qCDebug(shared) << "usecTimestampNow() - details... "; - qCDebug(shared) << " TIME_REFERENCE:" << TIME_REFERENCE; - qCDebug(shared) << " timeReferenceAsString:" << timeReferenceAsString.toString("yyyy-MM-dd hh:mm:ss.zzz"); - qCDebug(shared) << " usecTimestampNowAdjust:" << usecTimestampNowAdjust; - qCDebug(shared) << " nsecsElapsed:" << nsecsElapsed; - qCDebug(shared) << " usecsElapsed:" << usecsElapsed; - qCDebug(shared) << " now:" << now; - qCDebug(shared) << " msecsNow:" << msecsNow; - qCDebug(shared) << " nowAsString:" << nowAsString.toString("yyyy-MM-dd hh:mm:ss.zzz"); - qCDebug(shared) << " currentLocalTime:" << currentLocalTime.toString("yyyy-MM-dd hh:mm:ss.zzz"); - } - - return now; + using namespace std::chrono; + static const auto unixEpoch = system_clock::from_time_t(0); + return duration_cast(system_clock::now() - unixEpoch).count() + usecTimestampNowAdjust; } float secTimestampNow() { From 1215ddbe88a6669173c7a635be0f0b8514e36fce Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Thu, 13 Sep 2018 18:50:46 -0300 Subject: [PATCH 073/259] Remove debug files from linux qt build. Remove extra space --- android/build.gradle | 6 +++--- libraries/audio-client/src/AudioClient.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 8a3f4d75b9..14f0779e29 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -72,9 +72,9 @@ def jniFolder = new File(appDir, 'src/main/jniLibs/arm64-v8a') def baseUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/android/' def breakpadDumpSymsDir = new File("${appDir}/build/tmp/breakpadDumpSyms") -def qtFile='qt-5.11.1_linux_armv8-libcpp_openssl.tgz' -def qtChecksum='bf9e734d9c4e77c4807a38c93515e25c' -def qtVersionId='Qt3rvI0O7l5gLkacia2vA6KAchTgFkFf' +def qtFile='qt-5.11.1_linux_armv8-libcpp_openssl_patched.tgz' +def qtChecksum='aa449d4bfa963f3bc9a9dfe558ba29df' +def qtVersionId='3S97HBM5G5Xw9EfE52sikmgdN3t6C2MN' if (Os.isFamily(Os.FAMILY_MAC)) { qtFile = 'qt-5.11.1_osx_armv8-libcpp_openssl_patched.tgz' qtChecksum='c83cc477c08a892e00c71764dca051a0' diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index f0ba0307cc..814cbf87e4 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -454,7 +454,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { Setting::Handle enableAEC(SETTING_AEC_KEY, false); bool aecEnabled = enableAEC.get(); auto audioClient = DependencyManager::get(); - bool headsetOn = audioClient? audioClient->isHeadsetPluggedIn() : false ; + bool headsetOn = audioClient? audioClient->isHeadsetPluggedIn() : false; auto inputDevices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); for (auto inputDevice : inputDevices) { if (((headsetOn || !aecEnabled) && inputDevice.deviceName() == VOICE_RECOGNITION) || From 0fc1cc9af802a5b411fb3b5ae4dfdf75aa5db125 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 13 Sep 2018 15:39:18 -0700 Subject: [PATCH 074/259] Revert "adding fix for getting DomainHandler into error state" This reverts commit 49f26d968a4349d3665b40eaa552943b6ed4237a. --- libraries/networking/src/DomainHandler.cpp | 15 +++++---------- libraries/networking/src/DomainHandler.h | 5 ++--- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index a432b41a0d..59e3de922f 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -55,9 +55,6 @@ DomainHandler::DomainHandler(QObject* parent) : // stop the refresh timer if we connect to a domain connect(this, &DomainHandler::connectedToDomain, &_apiRefreshTimer, &QTimer::stop); - - // stop the refresh timer if we connect to a domain - connect(this, &DomainHandler::redirectToErrorDomainURL, &_apiRefreshTimer, &QTimer::stop); } void DomainHandler::disconnect() { @@ -102,6 +99,7 @@ void DomainHandler::softReset() { clearSettings(); + _isInErrorState = false; _connectionDenialsSinceKeypairRegen = 0; _checkInPacketsSinceLastReply = 0; @@ -109,14 +107,11 @@ void DomainHandler::softReset() { QMetaObject::invokeMethod(&_settingsTimer, "stop"); // restart the API refresh timer in case we fail to connect and need to refresh information - if (!_isInErrorState) - QMetaObject::invokeMethod(&_apiRefreshTimer, "start"); - _isInErrorState = false; + QMetaObject::invokeMethod(&_apiRefreshTimer, "start"); } void DomainHandler::hardReset() { - if (!_isInErrorState) - emit resetting(); + emit resetting(); softReset(); @@ -343,7 +338,6 @@ void DomainHandler::loadedErrorDomain(std::map namedPaths) { void DomainHandler::setRedirectErrorState(QUrl errorUrl, int reasonCode) { _errorDomainURL = errorUrl; _lastDomainConnectionError = reasonCode; - _isInErrorState = true; emit redirectToErrorDomainURL(_errorDomainURL); } @@ -486,8 +480,9 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer namedPaths); void loadedErrorDomain(std::map namedPaths); + // sets domain handler in error state. + void setRedirectErrorState(QUrl errorUrl, int reasonCode); QString getViewPointFromNamedPath(QString namedPath); @@ -170,9 +172,6 @@ public slots: void processICEResponsePacket(QSharedPointer icePacket); void processDomainServerConnectionDeniedPacket(QSharedPointer message); - // sets domain handler in error state. - void setRedirectErrorState(QUrl errorUrl, int reasonCode); - private slots: void completedHostnameLookup(const QHostInfo& hostInfo); void completedIceServerHostnameLookup(); From 5795ef3c6323c9dbfd5782b486d3687d3016e322 Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Thu, 13 Sep 2018 19:43:28 -0300 Subject: [PATCH 075/259] Fix qt build (osx) file version --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index 14f0779e29..aa7aa399b2 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -78,7 +78,7 @@ def qtVersionId='3S97HBM5G5Xw9EfE52sikmgdN3t6C2MN' if (Os.isFamily(Os.FAMILY_MAC)) { qtFile = 'qt-5.11.1_osx_armv8-libcpp_openssl_patched.tgz' qtChecksum='c83cc477c08a892e00c71764dca051a0' - qtVersionId='QNa.lwNJaPc0eGuIL.xZ8ebeTuLL7rh8' + qtVersionId='OxBD7iKINv1HbyOXmAmDrBb8AF3N.Kup' } else if (Os.isFamily(Os.FAMILY_WINDOWS)) { qtFile = 'qt-5.11.1_win_armv8-libcpp_openssl_patched.tgz' qtChecksum='0582191cc55431aa4f660848a542883e' From c27bb0459683b5221c16361631b620359c0dcfbf Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 14 Sep 2018 11:10:26 +1200 Subject: [PATCH 076/259] Display mini tablet when it or palm is facing the camera --- scripts/system/miniTablet.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index a35e5fc1af..735cae4d3b 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -638,6 +638,7 @@ miniOrientation = Quat.multiply(handOrientation, uiPositionAndOrientation.rotation); miniToCameraDirection = Vec3.normalize(Vec3.subtract(Camera.position, miniPosition)); show = Vec3.dot(miniToCameraDirection, Quat.getForward(miniOrientation)) > MIN_HAND_CAMERA_ANGLE_COS; + show = show || (-Vec3.dot(miniToCameraDirection, Quat.getForward(handOrientation)) > MIN_HAND_CAMERA_ANGLE_COS); cameraToHand = -Vec3.dot(miniToCameraDirection, Quat.getForward(Camera.orientation)); } From fe8c237d646875c894b3a42422f634dea1c13d81 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 14 Sep 2018 11:22:31 +1200 Subject: [PATCH 077/259] Don't display mini tablet in peripheral vision --- scripts/system/miniTablet.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 735cae4d3b..f8315ae3c2 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -545,8 +545,10 @@ // Visibility. MIN_HAND_CAMERA_ANGLE = 30, + MAX_CAMERA_HAND_ANGLE = 30, DEGREES_180 = 180, - MIN_HAND_CAMERA_ANGLE_COS = Math.cos(Math.PI * MIN_HAND_CAMERA_ANGLE / DEGREES_180); + MIN_HAND_CAMERA_ANGLE_COS = Math.cos(Math.PI * MIN_HAND_CAMERA_ANGLE / DEGREES_180), + MAX_CAMERA_HAND_ANGLE_COS = Math.cos(Math.PI * MAX_CAMERA_HAND_ANGLE / DEGREES_180); function enterMiniDisabled() { @@ -640,6 +642,7 @@ show = Vec3.dot(miniToCameraDirection, Quat.getForward(miniOrientation)) > MIN_HAND_CAMERA_ANGLE_COS; show = show || (-Vec3.dot(miniToCameraDirection, Quat.getForward(handOrientation)) > MIN_HAND_CAMERA_ANGLE_COS); cameraToHand = -Vec3.dot(miniToCameraDirection, Quat.getForward(Camera.orientation)); + show = show && (cameraToHand > MAX_CAMERA_HAND_ANGLE_COS); } return { From 727aea20251af8bbe3c2b77d4ec35b9feda02662 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 13 Sep 2018 18:21:16 -0700 Subject: [PATCH 078/259] Remove comment --- libraries/shared/src/SharedUtil.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 886445824b..012e7aa1f5 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -122,7 +122,6 @@ void setGlobalInstance(const char* propertyName, const QVariant& variant) { } } -// Do we still need this? static qint64 usecTimestampNowAdjust = 0; // in usec void usecTimestampNowForceClockSkew(qint64 clockSkew) { ::usecTimestampNowAdjust = clockSkew; From f01ebe57ea5a96776d0ed9ee7878db1cda1f6e99 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 14 Sep 2018 18:55:29 +1200 Subject: [PATCH 079/259] Trial hiding the UI when it's expanding to become the tablet proper --- scripts/system/miniTablet.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index f8315ae3c2..d29b4b0318 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -369,10 +369,14 @@ localRotation: localRotation, dimensions: dimensions }); + // FIXME: Temporary code change to try not displaying UI when mini tablet is expanding to become the tablet proper. Overlays.editOverlay(miniUIOverlay, { + /* localPosition: Vec3.multiply(tabletScaleFactor, MINI_UI_LOCAL_POSITION), dimensions: Vec3.multiply(tabletScaleFactor, MINI_UI_DIMENSIONS), dpi: MINI_UI_DPI / tabletScaleFactor + */ + visible: false }); } From ba564576632e66ae32327b131cf6fcfb4c76377d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 14 Sep 2018 20:14:54 +1200 Subject: [PATCH 080/259] Smooth the transition between expanded mini tablet and tablet proper --- scripts/system/miniTablet.js | 38 +++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index d29b4b0318..0df8b9505c 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -425,9 +425,15 @@ }); } - function hide() { + function release() { Overlays.editOverlay(miniOverlay, { parentID: Uuid.NULL, // Release hold so that hand can grab tablet proper. + grabbable: false + }); + } + + function hide() { + Overlays.editOverlay(miniOverlay, { visible: false }); Overlays.editOverlay(miniUIOverlay, { @@ -492,6 +498,7 @@ startExpandingTablet: startExpandingTablet, sizeAboutHandles: sizeAboutHandles, updateRotation: updateRotation, + release: release, hide: hide, destroy: destroy }; @@ -537,6 +544,7 @@ MINI_EXPAND_TIMEOUT = 20, miniExpandTimer = null, miniExpandStart, + miniExpandedStartTime, // Tablet targets. isGoto = false, @@ -656,6 +664,7 @@ } function enterMiniHidden() { + ui.release(); ui.hide(); } @@ -828,9 +837,7 @@ } function enterTabletOpen() { - var miniTabletProperties = ui.getMiniTabletProperties(); - - ui.hide(); + var miniTabletProperties; if (isGoto) { tablet.loadQMLSource(TABLET_ADDRESS_DIALOG); @@ -838,17 +845,26 @@ tablet.gotoHomeScreen(); } + ui.release(); + miniTabletProperties = ui.getMiniTabletProperties(); + Overlays.editOverlay(HMD.tabletID, { position: miniTabletProperties.position, orientation: miniTabletProperties.orientation }); HMD.openTablet(true); + + miniExpandedStartTime = Date.now(); } function updateTabletOpen() { - // Immediately transition back to MINI_HIDDEN. - setState(MINI_HIDDEN); + // Give the tablet proper time to rez before hiding expanded mini tablet. + // The mini tablet is also hidden elsewhere if the tablet proper is grabbed. + var TABLET_OPENING_DELAY = 500; + if (Date.now() >= miniExpandedStartTime + TABLET_OPENING_DELAY) { + setState(MINI_HIDDEN); + } } STATE_MACHINE = { @@ -973,12 +989,15 @@ return; } - if (message.grabbedEntity !== ui.getMiniTabletID()) { + if (message.grabbedEntity !== HMD.tabletID && message.grabbedEntity !== ui.getMiniTabletID()) { return; } - miniHand = miniState.getHand(); - if (message.action === "grab" && miniState.getState() === miniState.MINI_VISIBLE) { + if (message.action === "grab" && message.grabbedEntity === HMD.tabletID) { + // Tablet may have been grabbed after it replaced expanded mini tablet. + miniState.setState(miniState.MINI_HIDDEN); + } else if (message.action === "grab" && miniState.getState() === miniState.MINI_VISIBLE) { + miniHand = miniState.getHand(); hand = message.joint === HAND_NAMES[miniHand] ? miniHand : otherHand(miniHand); if (hand === miniHand) { miniState.setState(miniState.MINI_EXPANDING, { hand: hand, goto: false }); @@ -986,6 +1005,7 @@ miniState.setState(miniState.MINI_GRABBED); } } else if (message.action === "release" && miniState.getState() === miniState.MINI_GRABBED) { + miniHand = miniState.getHand(); hand = message.joint === HAND_NAMES[miniHand] ? miniHand : otherHand(miniHand); miniState.setState(miniState.MINI_EXPANDING, { hand: hand, goto: false }); } From 80aa74600f19cc639b1b2af3e5b5ecc4bf7d70e7 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 14 Sep 2018 20:15:08 +1200 Subject: [PATCH 081/259] Fix mini tablet sometimes remaining parented when it shouldn't be --- scripts/system/miniTablet.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 0df8b9505c..c894f92ed3 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -804,6 +804,11 @@ } } + function exitMiniGrabbed() { + // Explicitly unparent mini tablet in case controller grabbing code has reparented it. + ui.release(); + } + function expandMini() { var scaleFactor = (Date.now() - miniExpandStart) / MINI_EXPAND_DURATION; if (scaleFactor < 1) { @@ -896,7 +901,7 @@ MINI_GRABBED: { // Mini tablet is grabbed by other hand. enter: null, update: updateMiniGrabbed, - exit: null + exit: exitMiniGrabbed }, MINI_EXPANDING: { // Mini tablet is expanding before showing tablet proper. enter: enterMiniExpanding, From 45281960aff9194447a7fc6cf51243f1cb82e0ff Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 14 Sep 2018 20:30:17 +1200 Subject: [PATCH 082/259] Don't display mini tablet if user is away --- scripts/system/miniTablet.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index c894f92ed3..6a12edefaf 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -672,8 +672,8 @@ var showLeft, showRight; - // Don't show mini tablet if tablet proper is already displayed or in toolbar mode. - if (HMD.showTablet || tablet.toolbarMode) { + // Don't show mini tablet if tablet proper is already displayed, in toolbar mode, or away. + if (HMD.showTablet || tablet.toolbarMode || MyAvatar.isAway) { return; } @@ -1016,6 +1016,11 @@ } } + function onWentAway() { + // Mini tablet only available when user is not away. + miniState.setState(miniState.MINI_HIDDEN); + } + function onDisplayModeChanged() { // Mini tablet only available when HMD is active. if (HMD.active) { @@ -1035,6 +1040,7 @@ Messages.subscribe(HIFI_OBJECT_MANIPULATION_CHANNEL); Messages.messageReceived.connect(onMessageReceived); + MyAvatar.wentAway.connect(onWentAway); HMD.displayModeChanged.connect(onDisplayModeChanged); if (HMD.active) { miniState.setState(miniState.MINI_HIDDEN); @@ -1045,6 +1051,7 @@ miniState.setState(miniState.MINI_DISABLED); HMD.displayModeChanged.disconnect(onDisplayModeChanged); + MyAvatar.wentAway.disconnect(onWentAway); Messages.messageReceived.disconnect(onMessageReceived); Messages.unsubscribe(HIFI_OBJECT_MANIPULATION_CHANNEL); From cb496356272b2077821516ef780b68cb92e21d73 Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Fri, 14 Sep 2018 13:34:04 -0300 Subject: [PATCH 083/259] Fix switch from Top View to My View --- scripts/system/+android/clickWeb.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/system/+android/clickWeb.js b/scripts/system/+android/clickWeb.js index dc75a58327..229b2b8b82 100644 --- a/scripts/system/+android/clickWeb.js +++ b/scripts/system/+android/clickWeb.js @@ -103,4 +103,6 @@ module.exports = { ending: ending } +init(); + }()); // END LOCAL_SCOPE From 0afea6fbd190721d147e53c17183834239953f9d Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Fri, 14 Sep 2018 11:08:53 -0700 Subject: [PATCH 084/259] removing double login fail signal --- libraries/networking/src/AccountManager.cpp | 7 ------- libraries/networking/src/AccountManager.h | 1 - 2 files changed, 8 deletions(-) diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 5b3196a2bf..d99c0020da 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -538,7 +538,6 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas QNetworkReply* requestReply = networkAccessManager.post(request, postData); connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished); - connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError))); } void AccountManager::requestAccessTokenWithSteam(QByteArray authSessionTicket) { @@ -633,12 +632,6 @@ void AccountManager::requestAccessTokenFinished() { } } -void AccountManager::requestAccessTokenError(QNetworkReply::NetworkError error) { - // TODO: error handling - qCDebug(networking) << "AccountManager: failed to fetch access token - " << error; - emit loginFailed(); -} - void AccountManager::refreshAccessTokenFinished() { QNetworkReply* requestReply = reinterpret_cast(sender()); diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index b122115dd0..f3b81cf1c9 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -106,7 +106,6 @@ public slots: void requestAccessTokenFinished(); void refreshAccessTokenFinished(); void requestProfileFinished(); - void requestAccessTokenError(QNetworkReply::NetworkError error); void refreshAccessTokenError(QNetworkReply::NetworkError error); void requestProfileError(QNetworkReply::NetworkError error); void logout(); From 5edb76ef347e9e5da1e0cb4f0f769e23b4c6cb24 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Fri, 14 Sep 2018 11:35:20 -0700 Subject: [PATCH 085/259] Checkpoint Tray Notifier --- .../resources/tray-menu-notification.png | Bin 0 -> 319 bytes server-console/src/main.js | 229 +++++++++------ server-console/src/modules/hf-app.js | 71 +++++ .../src/modules/hf-notifications.js | 263 ++++++++++++++++++ server-console/src/modules/hf-process.js | 18 ++ 5 files changed, 493 insertions(+), 88 deletions(-) create mode 100644 server-console/resources/tray-menu-notification.png create mode 100644 server-console/src/modules/hf-app.js create mode 100644 server-console/src/modules/hf-notifications.js diff --git a/server-console/resources/tray-menu-notification.png b/server-console/resources/tray-menu-notification.png new file mode 100644 index 0000000000000000000000000000000000000000..569ee95d7ed77761062251173dda4a02f2d40e75 GIT binary patch literal 319 zcmV-F0l@x=P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0MtoDK~yMHElxXb z0x=MMV=b^qp`Zb}h%0abD3YFn2Bm?9bJ5UofK)jG1!+?tBsPgiw%}`QuP0ASMtYv{ zypJvTFP%&k`(F-RHJgydyyVJ5heO5b3KS6tgtApDsJy+3=*w8~{c|Tu1lkM^H0GTa z#6Lf2a9k3`2NZN$5gaJmCkfXtfg4eS}R@*jg3nv`Z2I{s5>wVTi4c RNGSjS002ovPDHLkV1gtFc~bxY literal 0 HcmV?d00001 diff --git a/server-console/src/main.js b/server-console/src/main.js index 92ebdbf36c..0fd2659fe9 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -29,58 +29,31 @@ const updater = require('./modules/hf-updater.js'); const Config = require('./modules/config').Config; const hfprocess = require('./modules/hf-process.js'); + +global.log = require('electron-log'); + const Process = hfprocess.Process; const ACMonitorProcess = hfprocess.ACMonitorProcess; const ProcessStates = hfprocess.ProcessStates; const ProcessGroup = hfprocess.ProcessGroup; const ProcessGroupStates = hfprocess.ProcessGroupStates; +const hfApp = require('./modules/hf-app.js'); +const GetBuildInfo = hfApp.getBuildInfo; +const StartInterface = hfApp.startInterface; + const osType = os.type(); const appIcon = path.join(__dirname, '../resources/console.png'); +const menuNotificationIcon = path.join(__dirname, '../resources/tray-menu-notification.png'); + const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Days const LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/; const HOME_CONTENT_URL = "http://cdn.highfidelity.com/content-sets/home-tutorial-RC40.tar.gz"; -function getBuildInfo() { - var buildInfoPath = null; - - if (osType == 'Windows_NT') { - buildInfoPath = path.join(path.dirname(process.execPath), 'build-info.json'); - } else if (osType == 'Darwin') { - var contentPath = ".app/Contents/"; - var contentEndIndex = __dirname.indexOf(contentPath); - - if (contentEndIndex != -1) { - // this is an app bundle - var appPath = __dirname.substring(0, contentEndIndex) + ".app"; - buildInfoPath = path.join(appPath, "/Contents/Resources/build-info.json"); - } - } - - const DEFAULT_BUILD_INFO = { - releaseType: "", - buildIdentifier: "dev", - buildNumber: "0", - stableBuild: "0", - organization: "High Fidelity - dev", - appUserModelId: "com.highfidelity.sandbox-dev" - }; - var buildInfo = DEFAULT_BUILD_INFO; - - if (buildInfoPath) { - try { - buildInfo = JSON.parse(fs.readFileSync(buildInfoPath)); - } catch (e) { - buildInfo = DEFAULT_BUILD_INFO; - } - } - - return buildInfo; -} -const buildInfo = getBuildInfo(); +const buildInfo = GetBuildInfo(); function getRootHifiDataDirectory(local) { var organization = buildInfo.organization; @@ -114,7 +87,6 @@ const UPDATER_LOCK_FILENAME = ".updating"; const UPDATER_LOCK_FULL_PATH = getRootHifiDataDirectory() + "/" + UPDATER_LOCK_FILENAME; // Configure log -global.log = require('electron-log'); const oldLogFile = path.join(getApplicationDataDirectory(), '/log.txt'); const logFile = path.join(getApplicationDataDirectory(true), '/log.txt'); if (oldLogFile != logFile && fs.existsSync(oldLogFile)) { @@ -149,15 +121,23 @@ const configPath = path.join(getApplicationDataDirectory(), 'config.json'); var userConfig = new Config(); userConfig.load(configPath); - const ipcMain = electron.ipcMain; + +function isServerInstalled() { + return interfacePath && userConfig.get("serverInstalled", true); +} + +function isInterfaceInstalled() { + return dsPath && acPath && userConfig.get("interfaceInstalled", true); +} + var isShuttingDown = false; function shutdown() { log.debug("Normal shutdown (isShuttingDown: " + isShuttingDown + ")"); if (!isShuttingDown) { // if the home server is running, show a prompt before quit to ask if the user is sure - if (homeServer.state == ProcessGroupStates.STARTED) { + if (isServerInstalled() && homeServer.state == ProcessGroupStates.STARTED) { log.debug("Showing shutdown dialog."); dialog.showMessageBox({ type: 'question', @@ -184,6 +164,9 @@ function shutdownCallback(idx) { if (idx == 0 && !isShuttingDown) { isShuttingDown = true; + log.debug("Stop tray polling."); + trayNotifications.stopPolling(); + log.debug("Saving user config"); userConfig.save(configPath); @@ -191,31 +174,37 @@ function shutdownCallback(idx) { log.debug("Closing log window"); logWindow.close(); } - if (homeServer) { - log.debug("Stoping home server"); - homeServer.stop(); - } - updateTrayMenu(homeServer.state); + if(isServerInstalled()) { + if (homeServer) { + log.debug("Stoping home server"); + homeServer.stop(); - if (homeServer.state == ProcessGroupStates.STOPPED) { - // if the home server is already down, take down the server console now - log.debug("Quitting."); - app.exit(0); - } else { - // if the home server is still running, wait until we get a state change or timeout - // before quitting the app - log.debug("Server still shutting down. Waiting"); - var timeoutID = setTimeout(function() { - app.exit(0); - }, 5000); - homeServer.on('state-update', function(processGroup) { - if (processGroup.state == ProcessGroupStates.STOPPED) { - clearTimeout(timeoutID); + updateTrayMenu(homeServer.state); + + if (homeServer.state == ProcessGroupStates.STOPPED) { + // if the home server is already down, take down the server console now log.debug("Quitting."); app.exit(0); + } else { + // if the home server is still running, wait until we get a state change or timeout + // before quitting the app + log.debug("Server still shutting down. Waiting"); + var timeoutID = setTimeout(function() { + app.exit(0); + }, 5000); + homeServer.on('state-update', function(processGroup) { + if (processGroup.state == ProcessGroupStates.STOPPED) { + clearTimeout(timeoutID); + log.debug("Quitting."); + app.exit(0); + } + }); } - }); + } + } + else { + app.exit(0); } } } @@ -351,20 +340,6 @@ function openLogDirectory() { app.on('window-all-closed', function() { }); -function startInterface(url) { - var argArray = []; - - // check if we have a url parameter to include - if (url) { - argArray = ["--url", url]; - } - - // create a new Interface instance - Interface makes sure only one is running at a time - var pInterface = new Process('interface', interfacePath, argArray); - pInterface.detached = true; - pInterface.start(); -} - var tray = null; global.homeServer = null; global.domainServer = null; @@ -372,6 +347,18 @@ global.acMonitor = null; global.userConfig = userConfig; global.openLogDirectory = openLogDirectory; +const hfNotifications = require('./modules/hf-notifications.js'); +const HifiNotifications = hfNotifications.HifiNotifications; +const HifiNotificationType = hfNotifications.NotificationType; + +var pendingNotifications = {} +function notificationCallback(notificationType) { + pendingNotifications[notificationType] = true; + updateTrayMenu(homeServer ? homeServer.state : ProcessGroupStates.STOPPED); +} + +var trayNotifications = new HifiNotifications(userConfig, notificationCallback); + var LogWindow = function(ac, ds) { this.ac = ac; this.ds = ds; @@ -407,7 +394,7 @@ LogWindow.prototype = { function visitSandboxClicked() { if (interfacePath) { - startInterface('hifi://localhost'); + StartInterface('hifi://localhost'); } else { // show an error to say that we can't go home without an interface instance dialog.showErrorBox("Client Not Found", binaryMissingMessage("High Fidelity client", "Interface", false)); @@ -425,6 +412,47 @@ var labels = { label: 'Version - ' + buildInfo.buildIdentifier, enabled: false }, + enableNotifications: { + label: 'Enable Notifications', + type: 'checkbox', + checked: true, + click: function() { + trayNotifications.enable(!trayNotifications.enabled(), notificationCallback); + updateTrayMenu(homeServer ? homeServer.state : ProcessGroupStates.STOPPED); + } + }, + goto: { + label: 'Goto', + click: function() { + StartInterface(""); + pendingNotifications[HifiNotificationType.GOTO] = false; + updateTrayMenu(homeServer ? homeServer.state : ProcessGroupStates.STOPPED); + } + }, + people: { + label: 'People', + click: function() { + StartInterface(""); + pendingNotifications[HifiNotificationType.PEOPLE] = false; + updateTrayMenu(homeServer ? homeServer.state : ProcessGroupStates.STOPPED); + } + }, + wallet: { + label: 'Wallet', + click: function() { + StartInterface(""); + pendingNotifications[HifiNotificationType.WALLET] = false; + updateTrayMenu(homeServer ? homeServer.state : ProcessGroupStates.STOPPED); + } + }, + marketplace: { + label: 'Marketplace', + click: function() { + StartInterface(""); + pendingNotifications[HifiNotificationType.MARKETPLACE] = false; + updateTrayMenu(homeServer ? homeServer.state : ProcessGroupStates.STOPPED); + } + }, restart: { label: 'Start Server', click: function() { @@ -489,16 +517,30 @@ function buildMenuArray(serverState) { if (isShuttingDown) { menuArray.push(labels.shuttingDown); } else { - menuArray.push(labels.serverState); - menuArray.push(labels.version); - menuArray.push(separator); - menuArray.push(labels.goHome); - menuArray.push(separator); - menuArray.push(labels.restart); - menuArray.push(labels.stopServer); - menuArray.push(labels.settings); - menuArray.push(labels.viewLogs); - menuArray.push(separator); + if(isServerInstalled()) { + menuArray.push(labels.serverState); + menuArray.push(labels.version); + menuArray.push(separator); + } + if(isInterfaceInstalled()) { + menuArray.push(labels.enableNotifications); + menuArray.push(labels.goto); + menuArray.push(labels.people); + menuArray.push(labels.wallet); + menuArray.push(labels.marketplace); + menuArray.push(separator); + } + if(isServerInstalled() && isInterfaceInstalled()) { + menuArray.push(labels.goHome); + menuArray.push(separator); + } + if(isServerInstalled()) { + menuArray.push(labels.restart); + menuArray.push(labels.stopServer); + menuArray.push(labels.settings); + menuArray.push(labels.viewLogs); + menuArray.push(separator); + } menuArray.push(labels.share); menuArray.push(separator); menuArray.push(labels.quit); @@ -528,6 +570,17 @@ function updateLabels(serverState) { labels.restart.label = "Restart Server"; labels.restart.enabled = false; } + + labels.enableNotifications.checked = trayNotifications.enabled(); + labels.people.visible = trayNotifications.enabled(); + labels.goto.visible = trayNotifications.enabled(); + labels.wallet.visible = trayNotifications.enabled(); + labels.marketplace.visible = trayNotifications.enabled(); + labels.goto.icon = pendingNotifications[HifiNotificationType.GOTO] ? menuNotificationIcon : null; + labels.people.icon = pendingNotifications[HifiNotificationType.PEOPLE] ? menuNotificationIcon : null; + labels.wallet.icon = pendingNotifications[HifiNotificationType.WALLET] ? menuNotificationIcon : null; + labels.marketplace.icon = pendingNotifications[HifiNotificationType.MARKETPLACE] ? menuNotificationIcon : null; + } function updateTrayMenu(serverState) { @@ -807,7 +860,7 @@ function onContentLoaded() { deleteOldFiles(logPath, DELETE_LOG_FILES_OLDER_THAN_X_SECONDS, LOG_FILE_REGEX); - if (dsPath && acPath) { + if (isServerInstalled()) { var dsArguments = ['--get-temp-name', '--parent-pid', process.pid]; domainServer = new Process('domain-server', dsPath, dsArguments, logPath); @@ -838,7 +891,7 @@ function onContentLoaded() { // If we were launched with the launchInterface option, then we need to launch interface now if (argv.launchInterface) { log.debug("Interface launch requested... argv.launchInterface:", argv.launchInterface); - startInterface(); + StartInterface(); } // If we were launched with the shutdownWith option, then we need to shutdown when that process (pid) @@ -869,7 +922,7 @@ app.on('ready', function() { // Create tray icon tray = new Tray(trayIcons[ProcessGroupStates.STOPPED]); - tray.setToolTip('High Fidelity Sandbox'); + tray.setToolTip('High Fidelity'); tray.on('click', function() { tray.popUpContextMenu(tray.menu); diff --git a/server-console/src/modules/hf-app.js b/server-console/src/modules/hf-app.js new file mode 100644 index 0000000000..28b97f582a --- /dev/null +++ b/server-console/src/modules/hf-app.js @@ -0,0 +1,71 @@ +const fs = require('fs'); +const extend = require('extend'); +const Config = require('./config').Config +const os = require('os'); +const pathFinder = require('./path-finder'); +const path = require('path'); +const argv = require('yargs').argv; +const hfprocess = require('./hf-process'); +const Process = hfprocess.Process; + +const binaryType = argv.binaryType; +const osType = os.type(); + +exports.getBuildInfo = function() { + var buildInfoPath = null; + + if (osType == 'Windows_NT') { + buildInfoPath = path.join(path.dirname(process.execPath), 'build-info.json'); + } else if (osType == 'Darwin') { + var contentPath = ".app/Contents/"; + var contentEndIndex = __dirname.indexOf(contentPath); + + if (contentEndIndex != -1) { + // this is an app bundle + var appPath = __dirname.substring(0, contentEndIndex) + ".app"; + buildInfoPath = path.join(appPath, "/Contents/Resources/build-info.json"); + } + } + + const DEFAULT_BUILD_INFO = { + releaseType: "", + buildIdentifier: "dev", + buildNumber: "0", + stableBuild: "0", + organization: "High Fidelity - dev", + appUserModelId: "com.highfidelity.sandbox-dev" + }; + var buildInfo = DEFAULT_BUILD_INFO; + + if (buildInfoPath) { + try { + buildInfo = JSON.parse(fs.readFileSync(buildInfoPath)); + } catch (e) { + buildInfo = DEFAULT_BUILD_INFO; + } + } + + return buildInfo; +} + +const buildInfo = exports.getBuildInfo(); +const interfacePath = pathFinder.discoveredPath("Interface", binaryType, buildInfo.releaseType); + +exports.startInterface = function(url) { + var argArray = []; + + // check if we have a url parameter to include + if (url) { + argArray = ["--url", url]; + } + console.log("Starting with " + url); + // create a new Interface instance - Interface makes sure only one is running at a time + var pInterface = new Process('Interface', interfacePath, argArray); + pInterface.detached = true; + pInterface.start(); +} + +exports.isInterfaceRunning = function(done) { + var pInterface = new Process('interface', 'interface.exe'); + return pInterface.isRunning(done); +} diff --git a/server-console/src/modules/hf-notifications.js b/server-console/src/modules/hf-notifications.js new file mode 100644 index 0000000000..06be365208 --- /dev/null +++ b/server-console/src/modules/hf-notifications.js @@ -0,0 +1,263 @@ +const request = require('request'); +const notifier = require('node-notifier'); +const os = require('os'); +const process = require('process'); +const hfApp = require('./hf-app'); +const path = require('path'); + +const notificationIcon = path.join(__dirname, '../../resources/console-notification.png'); +const NOTIFICATION_POLL_TIME_MS = 15 * 1000; +const METAVERSE_SERVER_URL= process.env.HIFI_METAVERSE_URL ? process.env.HIFI_METAVERSE_URL : 'https://highfidelity.com' +const STORIES_URL= '/api/v1/user_stories'; +const USERS_URL= '/api/v1/users'; +const ECONOMIC_ACTIVITY_URL= '/api/v1/commerce/history'; +const UPDATES_URL= '/api/v1/commerce/available_updates'; + + +const StartInterface=hfApp.startInterface; +const IsInterfaceRunning=hfApp.isInterfaceRunning; + +const NotificationType = { + GOTO: 'goto', + PEOPLE: 'people', + WALLET: 'wallet', + MARKETPLACE: 'marketplace' +}; + +function HifiNotification(notificationType, notificationData) { + this.type = notificationType; + this.data = notificationData; +} + +HifiNotification.prototype = { + show: function() { + switch(this.type) { + case NotificationType.GOTO: + var text = this.data.username + " " + this.data.action_string + " in " + this.data.place_name; + notifier.notify({ + notificationType: this.type, + icon: notificationIcon, + title: text, + message: "Click to goto " + this.data.place_name, + wait: true, + url: "hifi://" + this.data.place_name + this.data.path + }); + break; + + case NotificationType.PEOPLE: + var text = this.data.username + " has logged in."; + notifier.notify({ + notificationType: this.type, + icon: notificationIcon, + title: text, + message: "Click to join them in " + this.data.location.root.name, + wait: true, + url: "hifi://" + this.data.location.root.name + this.data.location.path + }); + break; + + case NotificationType.WALLET: + var text = "Economic activity."; + notifier.notify({ + notificationType: this.type, + icon: notificationIcon, + title: text, + message: "Click to open your wallet", + wait: true, + app: "Wallet" + }); + break; + + case NotificationType.MARKETPLACE: + var text = "One of your marketplace items has an update."; + notifier.notify({ + notificationType: this.type, + icon: notificationIcon, + title: text, + message: "Click to start the marketplace app", + wait: true, + app: "Marketplace" + }); + break; + } + } +} + +function HifiNotifications(config, callback) { + this.config = config; + this.callback = callback; + this.since = new Date(this.config.get("notifySince", "1970-01-01T00:00:00.000Z")); + this.enable(this.enabled()); + notifier.on('click', function(notifierObject, options) { + console.log(options); + StartInterface(options.url); + }); +} + +HifiNotifications.prototype = { + enable: function(enabled) { + this.config.set("enableTrayNotifications", enabled); + if(enabled) { + var _this = this; + this.pollTimer = setInterval(function() { + var _since = _this.since; + _this.since = new Date(); + IsInterfaceRunning(function(running) { + if(running) { + return; + } + _this.pollForStories(_since, _this.callback); + _this.pollForConnections(_since, _this.callback); + _this.pollForEconomicActivity(_since, _this.callback); + _this.pollForMarketplaceUpdates(_since, _this.callback); + }); + }, + NOTIFICATION_POLL_TIME_MS); + } + else if(this.pollTimer) { + clearInterval(this.pollTimer); + } + }, + enabled: function() { + return this.config.get("enableTrayNotifications", true); + }, + stopPolling: function() { + this.config.set("notifySince", this.since.toISOString()); + if(this.pollTimer) { + clearInterval(this.pollTimer); + } + }, + pollForStories: function(since, callback) { + var _this = this; + var actions = 'announcement'; + var options = [ + 'now=' + new Date().toISOString(), + 'since=' + since.toISOString(), + 'include_actions=announcement', + 'restriction=open,hifi', + 'require_online=true' + ]; + console.log("Polling for stories"); + var url = METAVERSE_SERVER_URL + STORIES_URL + '?' + options.join('&'); + request({ + uri: url + }, function (error, data) { + if (error || !data.body) { + console.log("Error: unable to get " + url); + return; + } + var content = JSON.parse(data.body); + if(!content || content.status != 'success') { + console.log("Error: unable to get " + url); + return; + } + content.user_stories.forEach(function(story) { + var updated_at = new Date(story.updated_at); + if (updated_at < since) { + return; + } + callback(NotificationType.GOTO); + var notification = new HifiNotification(NotificationType.GOTO, story); + notification.show(); + }); + }); + }, + pollForConnections: function(since, callback) { + var _this = this; + var options = [ + 'filter=connections', + 'since=' + since.toISOString(), + 'status=online' + ]; + console.log("Polling for connections"); + var url = METAVERSE_SERVER_URL + USERS_URL + '?' + options.join('&'); + request({ + uri: url + }, function (error, data) { + if (error || !data.body) { + console.log("Error: unable to get " + url); + return; + } + var content = JSON.parse(data.body); + if(!content || content.status != 'success') { + console.log("Error: unable to get " + url); + return; + } + console.log(content.data); + content.data.users.forEach(function(user) { + if(user.online) { + callback(NotificationType.PEOPLE); + var notification = new HifiNotification(NotificationType.PEOPLE, user); + notification.show(); + } + }); + }); + }, + pollForEconomicActivity: function(since, callback) { + var _this = this; + var options = [ + 'filter=connections', + 'since=' + since.toISOString(), + 'status=online' + ]; + console.log("Polling for economic activity"); + var url = METAVERSE_SERVER_URL + ECONOMIC_ACTIVITY_URL + '?' + options.join('&'); + request.post({ + uri: url + }, function (error, data) { + if (error || !data.body) { + console.log("Error " + error + ": unable to post " + url); + console.log(data); + return; + } + var content = JSON.parse(data.body); + if(!content || content.status != 'success') { + console.log(data.body); + console.log("Error " + content.status + ": unable to post " + url); + return; + } + console.log(content.data); + content.data.users.forEach(function(user) { + if(user.online) { + callback(NotificationType.PEOPLE); + var notification = new HifiNotification(NotificationType.PEOPLE, user); + notification.show(); + } + }); + }); + }, + pollForMarketplaceUpdates: function(since, callback) { + var _this = this; + var options = [ + 'filter=connections', + 'since=' + since.toISOString(), + 'status=online' + ]; + console.log("Polling for marketplace update"); + var url = METAVERSE_SERVER_URL + UPDATES_URL + '?' + options.join('&'); + request.put({ + uri: url + }, function (error, data) { + if (error || !data.body) { + console.log("Error " + error + ": unable to put " + url); + return; + } + var content = JSON.parse(data.body); + if(!content || content.status != 'success') { + console.log(data.body); + console.log("Error " + content.status + ": unable to put " + url); + return; + } + content.data.users.forEach(function(user) { + if(user.online) { + callback(NotificationType.PEOPLE); + var notification = new HifiNotification(NotificationType.PEOPLE, user); + notification.show(); + } + }); + }); + } +}; + +exports.HifiNotifications = HifiNotifications; +exports.NotificationType = NotificationType; \ No newline at end of file diff --git a/server-console/src/modules/hf-process.js b/server-console/src/modules/hf-process.js index 797ee38a0d..7fbc9a894e 100644 --- a/server-console/src/modules/hf-process.js +++ b/server-console/src/modules/hf-process.js @@ -259,6 +259,24 @@ Process.prototype = extend(Process.prototype, { }; return logs; }, + isRunning: function(done) { + var _command = this.command; + if (os.type == 'Windows_NT') { + childProcess.exec('tasklist /FO CSV', function(err, stdout, stderr) { + var running = false; + stdout.split("\n").forEach(function(line) { + var exeData = line.split(","); + var executable = exeData[0].replace(/\"/g, "").toLowerCase(); + if(executable == _command) { + running = true; + } + }); + done(running); + }); + } else if (os.type == 'Darwin') { + console.log("TODO IsRunning Darwin"); + } + }, // Events onChildStartError: function(error) { From 555432dd49cf70b31c11f3a0791e0cde2b715a8b Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Fri, 14 Sep 2018 14:13:26 -0700 Subject: [PATCH 086/259] Blank js file to force build (and prep for appinfo parsing) --- server-console/src/modules/hf-appinfo.js | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 server-console/src/modules/hf-appinfo.js diff --git a/server-console/src/modules/hf-appinfo.js b/server-console/src/modules/hf-appinfo.js new file mode 100644 index 0000000000..9fac0ca61c --- /dev/null +++ b/server-console/src/modules/hf-appinfo.js @@ -0,0 +1,10 @@ +'use strict' + +const request = require('request'); +const extend = require('extend'); +const util = require('util'); +const events = require('events'); +const childProcess = require('child_process'); +const fs = require('fs-extra'); +const os = require('os'); +const path = require('path'); From 9c5544af393ac8a45f9a527af51ffc51dc15fa88 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 14 Sep 2018 14:37:04 -0700 Subject: [PATCH 087/259] Merge in big work from appui_notifications branch --- .../icons/tablet-icons/people-a-msg.svg | 83 +++++++++++ .../icons/tablet-icons/people-i-msg.svg | 24 ++++ interface/resources/qml/hifi/Pal.qml | 23 ++- scripts/modules/appUi.js | 132 +++++++++++++++--- scripts/system/pal.js | 62 +++++++- 5 files changed, 299 insertions(+), 25 deletions(-) create mode 100644 interface/resources/icons/tablet-icons/people-a-msg.svg create mode 100644 interface/resources/icons/tablet-icons/people-i-msg.svg diff --git a/interface/resources/icons/tablet-icons/people-a-msg.svg b/interface/resources/icons/tablet-icons/people-a-msg.svg new file mode 100644 index 0000000000..862ce936ce --- /dev/null +++ b/interface/resources/icons/tablet-icons/people-a-msg.svg @@ -0,0 +1,83 @@ + + + +image/svg+xml + + \ No newline at end of file diff --git a/interface/resources/icons/tablet-icons/people-i-msg.svg b/interface/resources/icons/tablet-icons/people-i-msg.svg new file mode 100644 index 0000000000..635a01be4b --- /dev/null +++ b/interface/resources/icons/tablet-icons/people-i-msg.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 35a0078d32..e8fc41da63 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -271,6 +271,8 @@ Rectangle { connectionsUserModel.getFirstPage(); } activeTab = "connectionsTab"; + connectionsOnlineDot.visible = false; + pal.sendToScript({method: 'hideNotificationDot'}); connectionsHelpText.color = hifi.colors.blueAccent; } } @@ -298,6 +300,16 @@ Rectangle { } } } + Rectangle { + id: connectionsOnlineDot; + visible: false; + width: 10; + height: width; + radius: width; + color: "#EF3B4E" + anchors.left: parent.left; + anchors.verticalCenter: parent.verticalCenter; + } // "CONNECTIONS" text RalewaySemiBold { id: connectionsTabSelectorText; @@ -305,7 +317,11 @@ Rectangle { // Text size size: hifi.fontSizes.tabularData; // Anchors - anchors.fill: parent; + anchors.left: connectionsOnlineDot.visible ? connectionsOnlineDot.right : parent.left; + anchors.leftMargin: connectionsOnlineDot.visible ? 4 : 0; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + anchors.right: parent.right; // Style font.capitalization: Font.AllUppercase; color: activeTab === "connectionsTab" ? hifi.colors.blueAccent : hifi.colors.baseGray; @@ -326,7 +342,7 @@ Rectangle { anchors.left: connectionsTabSelectorTextContainer.left; anchors.top: connectionsTabSelectorTextContainer.top; anchors.topMargin: 1; - anchors.leftMargin: connectionsTabSelectorTextMetrics.width + 42; + anchors.leftMargin: connectionsTabSelectorTextMetrics.width + 42 + connectionsOnlineDot.width + connectionsTabSelectorText.anchors.leftMargin; RalewayRegular { id: connectionsHelpText; text: "[?]"; @@ -1267,6 +1283,9 @@ Rectangle { case 'http.response': http.handleHttpResponse(message); break; + case 'changeConnectionsDotStatus': + connectionsOnlineDot.visible = message.shouldShowDot; + break; default: console.log('Unrecognized message:', JSON.stringify(message)); } diff --git a/scripts/modules/appUi.js b/scripts/modules/appUi.js index 3a70a69d4d..02beb41674 100644 --- a/scripts/modules/appUi.js +++ b/scripts/modules/appUi.js @@ -11,6 +11,7 @@ // function AppUi(properties) { + var request = Script.require('request').request; /* Example development order: 1. var AppUi = Script.require('appUi'); 2. Put appname-i.svg, appname-a.svg in graphicsDirectory (where non-default graphicsDirectory can be added in #3). @@ -80,13 +81,6 @@ function AppUi(properties) { that.buttonActive = function buttonActive(isActive) { // How to make the button active (white). that.button.editProperties({isActive: isActive}); }; - that.messagesWaiting = function messagesWaiting(isWaiting) { // How to indicate a message light on button. - // Note that waitingButton doesn't have to exist unless someone explicitly calls this with isWaiting true. - that.button.editProperties({ - icon: isWaiting ? that.normalMessagesButton : that.normalButton, - activeIcon: isWaiting ? that.activeMessagesButton : that.activeButton - }); - }; that.isQMLUrl = function isQMLUrl(url) { var type = /.qml$/.test(url) ? 'QML' : 'Web'; return type === 'QML'; @@ -95,6 +89,32 @@ function AppUi(properties) { return that.currentVisibleScreenType === 'QML'; }; + // + // START Notification Handling Defaults + // + that.messagesWaiting = function messagesWaiting(isWaiting) { // How to indicate a message light on button. + // Note that waitingButton doesn't have to exist unless someone explicitly calls this with isWaiting true. + that.button.editProperties({ + icon: isWaiting ? that.normalMessagesButton : that.normalButton, + activeIcon: isWaiting ? that.activeMessagesButton : that.activeButton + }); + }; + that.notificationPollTimeout = false; + that.notificationPollTimeoutMs = 60000; + that.notificationPollEndpoint = false; + that.notificationPollStopPaginatingConditionMet = false; + that.notificationDataProcessPage = function (data) { + return data; + }; + that.notificationPollCallback = that.ignore; + that.notificationPollCaresAboutSince = false; + that.notificationDisplayBanner = function (message) { + Window.displayAnnouncement(message); + }; + // + // END Notification Handling Defaults + // + // Handlers that.onScreenChanged = function onScreenChanged(type, url) { // Set isOpen, wireEventBridge, set buttonActive as appropriate, @@ -126,6 +146,75 @@ function AppUi(properties) { // Overwrite with the given properties: Object.keys(properties).forEach(function (key) { that[key] = properties[key]; }); + // + // START Notification Handling + // + var METAVERSE_BASE = Account.metaverseServerURL; + var currentDataPageToRetrieve = 1; + var concatenatedServerResponse = new Array(); + that.notificationPoll = function () { + if (!that.notificationPollEndpoint) { + return; + } + + // User is "appearing offline" + if (GlobalServices.findableBy === "none") { + that.notificationPollTimeout = Script.setTimeout(that.notificationPoll, that.notificationPollTimeoutMs); + return; + } + + var url = METAVERSE_BASE + that.notificationPollEndpoint; + + if (that.notificationPollCaresAboutSince) { + url = url + "&since=" + (new Date().getTime()); + } + + console.debug(that.buttonName, 'polling for notifications at endpoint', url); + + function requestCallback(error, response) { + if (error || (response.status !== 'success')) { + print("Error: unable to get", url, error || response.status); + that.notificationPollTimeout = Script.setTimeout(that.notificationPoll, that.notificationPollTimeoutMs); + return; + } + + if (!that.notificationPollStopPaginatingConditionMet || that.notificationPollStopPaginatingConditionMet(response)) { + that.notificationPollTimeout = Script.setTimeout(that.notificationPoll, that.notificationPollTimeoutMs); + + var notificationData; + if (concatenatedServerResponse.length) { + notificationData = concatenatedServerResponse; + } else { + notificationData = that.notificationDataProcessPage(response); + } + console.debug(that.buttonName, 'notification data for processing:', JSON.stringify(notificationData)); + that.notificationPollCallback(notificationData); + currentDataPageToRetrieve = 1; + concatenatedServerResponse = new Array(); + } else { + concatenatedServerResponse = concatenatedServerResponse.concat(that.notificationDataProcessPage(response)); + currentDataPageToRetrieve++; + request({ uri: (url + "&page=" + currentDataPageToRetrieve) }, requestCallback); + } + } + + request({ uri: url }, requestCallback); + }; + + // This won't do anything if there isn't a notification endpoint set + that.notificationPoll(); + + function availabilityChanged() { + if (that.notificationPollTimeout) { + Script.clearTimeout(that.notificationPollTimeout); + that.notificationPollTimeout = false; + } + that.notificationPoll(); + } + // + // END Notification Handling + // + // Properties: that.tablet = Tablet.getTablet(that.tabletName); // Must be after we gather properties. @@ -147,8 +236,9 @@ function AppUi(properties) { } that.button = that.tablet.addButton(buttonOptions); that.ignore = function ignore() { }; - that.hasQmlEventBridge = false; - that.hasHtmlEventBridge = false; + that.hasOutboundEventBridge = false; + that.hasInboundQmlEventBridge = false; + that.hasInboundHtmlEventBridge = false; // HTML event bridge uses strings, not objects. Here we abstract over that. // (Although injected javascript still has to use JSON.stringify/JSON.parse.) that.sendToHtml = function (messageObject) { @@ -167,8 +257,10 @@ function AppUi(properties) { // Outbound (always, regardless of whether there is an inbound handler). if (on) { that.sendMessage = isCurrentlyOnQMLScreen ? that.tablet.sendToQml : that.sendToHtml; + that.hasOutboundEventBridge = true; } else { that.sendMessage = that.ignore; + that.hasOutboundEventBridge = false; } if (!that.onMessage) { @@ -177,25 +269,25 @@ function AppUi(properties) { // Inbound if (on) { - if (isCurrentlyOnQMLScreen && !that.hasQmlEventBridge) { + if (isCurrentlyOnQMLScreen && !that.hasInboundQmlEventBridge) { console.debug(that.buttonName, 'connecting', that.tablet.fromQml); that.tablet.fromQml.connect(that.onMessage); - that.hasQmlEventBridge = true; - } else if (!isCurrentlyOnQMLScreen && !that.hasHtmlEventBridge) { + that.hasInboundQmlEventBridge = true; + } else if (!isCurrentlyOnQMLScreen && !that.hasInboundHtmlEventBridge) { console.debug(that.buttonName, 'connecting', that.tablet.webEventReceived); that.tablet.webEventReceived.connect(that.fromHtml); - that.hasHtmlEventBridge = true; + that.hasInboundHtmlEventBridge = true; } } else { - if (that.hasQmlEventBridge) { + if (that.hasInboundQmlEventBridge) { console.debug(that.buttonName, 'disconnecting', that.tablet.fromQml); that.tablet.fromQml.disconnect(that.onMessage); - that.hasQmlEventBridge = false; + that.hasInboundQmlEventBridge = false; } - if (that.hasHtmlEventBridge) { + if (that.hasInboundHtmlEventBridge) { console.debug(that.buttonName, 'disconnecting', that.tablet.webEventReceived); that.tablet.webEventReceived.disconnect(that.fromHtml); - that.hasHtmlEventBridge = false; + that.hasInboundHtmlEventBridge = false; } } }; @@ -212,6 +304,7 @@ function AppUi(properties) { } : that.ignore; that.onScriptEnding = function onScriptEnding() { // Close if necessary, clean up any remaining handlers, and remove the button. + GlobalServices.findableByChanged.disconnect(availabilityChanged); if (that.isOpen) { that.close(); } @@ -222,10 +315,15 @@ function AppUi(properties) { } that.tablet.removeButton(that.button); } + if (that.notificationPollTimeout) { + Script.clearInterval(that.notificationPollTimeout); + that.notificationPollTimeout = false; + } }; // Set up the handlers. that.tablet.screenChanged.connect(that.onScreenChanged); that.button.clicked.connect(that.onClicked); Script.scriptEnding.connect(that.onScriptEnding); + GlobalServices.findableByChanged.connect(availabilityChanged); } module.exports = AppUi; diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 5e38624b35..1abac53f50 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -321,6 +321,10 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See break; case 'http.request': break; // Handled by request-service. + case 'hideNotificationDot': + shouldShowDot = false; + ui.messagesWaiting(shouldShowDot); + break; default: print('Unrecognized message from Pal.qml:', JSON.stringify(message)); } @@ -364,8 +368,8 @@ function getProfilePicture(username, callback) { // callback(url) if successfull }); } var SAFETY_LIMIT = 400; -function getAvailableConnections(domain, callback) { // callback([{usename, location}...]) if successfull. (Logs otherwise) - var url = METAVERSE_BASE + '/api/v1/users?per_page=' + SAFETY_LIMIT + '&'; +function getAvailableConnections(domain, callback, numResultsPerPage) { // callback([{usename, location}...]) if successfull. (Logs otherwise) + var url = METAVERSE_BASE + '/api/v1/users?per_page=' + (numResultsPerPage || SAFETY_LIMIT) + '&'; if (domain) { url += 'status=' + domain.slice(1, -1); // without curly braces } else { @@ -728,10 +732,14 @@ function createUpdateInterval() { var previousContextOverlay = ContextOverlay.enabled; var previousRequestsDomainListData = Users.requestsDomainListData; -function on() { +function palOpened() { + ui.sendMessage({ + method: 'changeConnectionsDotStatus', + shouldShowDot: shouldShowDot + }); previousContextOverlay = ContextOverlay.enabled; - previousRequestsDomainListData = Users.requestsDomainListData + previousRequestsDomainListData = Users.requestsDomainListData; ContextOverlay.enabled = false; Users.requestsDomainListData = true; @@ -810,14 +818,56 @@ function avatarSessionChanged(avatarID) { sendToQml({ method: 'palIsStale', params: [avatarID, 'avatarSessionChanged'] }); } +function notificationDataProcessPage(data) { + return data.data.users; +} + +var shouldShowDot = false; +var firstBannerNotificationShown = false; +function notificationPollCallback(onlineUsersArray) { + shouldShowDot = onlineUsersArray.length > 0; + + if (!ui.isOpen) { + ui.messagesWaiting(shouldShowDot); + ui.sendMessage({ + method: 'changeConnectionsDotStatus', + shouldShowDot: shouldShowDot + }); + + var message; + if (!firstBannerNotificationShown) { + message = onlineUsersArray.length + " of your connections are online. Open PEOPLE to join them!"; + ui.notificationDisplayBanner(message); + firstBannerNotificationShown = true; + } else { + for (var i = 0; i < onlineUsersArray.length; i++) { + message = onlineUsersArray[i].username + " is available in " + + onlineUsersArray[i].location.root.name + ". Open PEOPLE to join them!"; + ui.notificationDisplayBanner(message); + } + } + } +} + +function isReturnedDataEmpty(data) { + var usersArray = data.data.users; + return usersArray.length === 0; +} + function startup() { ui = new AppUi({ buttonName: "PEOPLE", sortOrder: 7, home: "hifi/Pal.qml", - onOpened: on, + onOpened: palOpened, onClosed: off, - onMessage: fromQml + onMessage: fromQml, + notificationPollEndpoint: "/api/v1/users?filter=connections&status=online&per_page=10", + notificationPollTimeoutMs: 60000, + notificationDataProcessPage: notificationDataProcessPage, + notificationPollCallback: notificationPollCallback, + notificationPollStopPaginatingConditionMet: isReturnedDataEmpty, + notificationPollCaresAboutSince: true }); Window.domainChanged.connect(clearLocalQMLDataAndClosePAL); Window.domainConnectionRefused.connect(clearLocalQMLDataAndClosePAL); From b0cde2afcc40c94e10ef1b683af320fec6994947 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Fri, 14 Sep 2018 15:05:17 -0700 Subject: [PATCH 088/259] Small addition to Anim Stats * During a state machine interp the previous state now shows up in AlphaValues panel, with parentheses around the name. * Added 3 new fields * Position - in world coordinate frame. * Heading - the facing angle in the world corrdinate frame. * Local Vel - the velocity of the character in the local coordinate frame. (left, forward, up). --- interface/resources/qml/AnimStats.qml | 11 ++++++++-- interface/src/ui/AnimStats.cpp | 23 ++++++++++++++++++++ interface/src/ui/AnimStats.h | 20 ++++++++++++++--- libraries/animation/src/AnimStateMachine.cpp | 4 ++++ libraries/animation/src/AnimVariant.cpp | 9 ++++++++ 5 files changed, 62 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/AnimStats.qml b/interface/resources/qml/AnimStats.qml index 35ed3799a6..d000f3d9be 100644 --- a/interface/resources/qml/AnimStats.qml +++ b/interface/resources/qml/AnimStats.qml @@ -47,6 +47,9 @@ Item { id: firstCol spacing: 4; x: 4; y: 4; + StatText { + text: root.positionText + } StatText { text: "State Machines:---------------------------------------------------------------------------" } @@ -73,10 +76,12 @@ Item { id: secondCol spacing: 4; x: 4; y: 4; + StatText { + text: root.rotationText + } StatText { text: "Anim Vars:--------------------------------------------------------------------------------" } - ListView { width: secondCol.width height: root.animVars.length * 15 @@ -113,10 +118,12 @@ Item { id: thirdCol spacing: 4; x: 4; y: 4; + StatText { + text: root.velocityText + } StatText { text: "Alpha Values:--------------------------------------------------------------------------" } - ListView { width: thirdCol.width height: root.animAlphaValues.length * 15 diff --git a/interface/src/ui/AnimStats.cpp b/interface/src/ui/AnimStats.cpp index 7b4778e365..e3579fa2dc 100644 --- a/interface/src/ui/AnimStats.cpp +++ b/interface/src/ui/AnimStats.cpp @@ -42,6 +42,29 @@ void AnimStats::updateStats(bool force) { auto myAvatar = avatarManager->getMyAvatar(); auto debugAlphaMap = myAvatar->getSkeletonModel()->getRig().getDebugAlphaMap(); + glm::vec3 position = myAvatar->getWorldPosition(); + glm::quat rotation = myAvatar->getWorldOrientation(); + glm::vec3 velocity = myAvatar->getWorldVelocity(); + + _positionText = QString("Position: (%1, %2, %3)"). + arg(QString::number(position.x, 'f', 2)). + arg(QString::number(position.y, 'f', 2)). + arg(QString::number(position.z, 'f', 2)); + emit positionTextChanged(); + + glm::vec3 eulerRotation = safeEulerAngles(rotation); + _rotationText = QString("Heading: %1"). + arg(QString::number(glm::degrees(eulerRotation.y), 'f', 2)); + emit rotationTextChanged(); + + // transform velocity into rig coordinate frame. z forward. + glm::vec3 localVelocity = Quaternions::Y_180 * glm::inverse(rotation) * velocity; + _velocityText = QString("Local Vel: (%1, %2, %3)"). + arg(QString::number(localVelocity.x, 'f', 2)). + arg(QString::number(localVelocity.y, 'f', 2)). + arg(QString::number(localVelocity.z, 'f', 2)); + emit velocityTextChanged(); + // update animation debug alpha values QStringList newAnimAlphaValues; qint64 now = usecTimestampNow(); diff --git a/interface/src/ui/AnimStats.h b/interface/src/ui/AnimStats.h index 1a6795b498..7b6aaf7b54 100644 --- a/interface/src/ui/AnimStats.h +++ b/interface/src/ui/AnimStats.h @@ -19,6 +19,9 @@ class AnimStats : public QQuickItem { Q_PROPERTY(QStringList animAlphaValues READ animAlphaValues NOTIFY animAlphaValuesChanged) Q_PROPERTY(QStringList animVars READ animVars NOTIFY animVarsChanged) Q_PROPERTY(QStringList animStateMachines READ animStateMachines NOTIFY animStateMachinesChanged) + Q_PROPERTY(QString positionText READ positionText NOTIFY positionTextChanged) + Q_PROPERTY(QString rotationText READ rotationText NOTIFY rotationTextChanged) + Q_PROPERTY(QString velocityText READ velocityText NOTIFY velocityTextChanged) public: static AnimStats* getInstance(); @@ -27,9 +30,13 @@ public: void updateStats(bool force = false); - QStringList animAlphaValues() { return _animAlphaValues; } - QStringList animVars() { return _animVarsList; } - QStringList animStateMachines() { return _animStateMachines; } + QStringList animAlphaValues() const { return _animAlphaValues; } + QStringList animVars() const { return _animVarsList; } + QStringList animStateMachines() const { return _animStateMachines; } + + QString positionText() const { return _positionText; } + QString rotationText() const { return _rotationText; } + QString velocityText() const { return _velocityText; } public slots: void forceUpdateStats() { updateStats(true); } @@ -39,6 +46,9 @@ signals: void animAlphaValuesChanged(); void animVarsChanged(); void animStateMachinesChanged(); + void positionTextChanged(); + void rotationTextChanged(); + void velocityTextChanged(); private: QStringList _animAlphaValues; @@ -50,6 +60,10 @@ private: std::map _animVarChangedTimers; // last time animVar value has changed. QStringList _animStateMachines; + + QString _positionText; + QString _rotationText; + QString _velocityText; }; #endif // hifi_AnimStats_h diff --git a/libraries/animation/src/AnimStateMachine.cpp b/libraries/animation/src/AnimStateMachine.cpp index 7f46cd614a..fb13b8e71c 100644 --- a/libraries/animation/src/AnimStateMachine.cpp +++ b/libraries/animation/src/AnimStateMachine.cpp @@ -88,6 +88,10 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co processOutputJoints(triggersOut); context.addStateMachineInfo(_id, _currentState->getID(), _previousState->getID(), _duringInterp, _alpha); + if (_duringInterp) { + // hack: add previoius state to debug alpha map, with parens around it's name. + context.setDebugAlpha(QString("(%1)").arg(_previousState->getID()), 1.0f - _alpha, AnimNodeType::Clip); + } return _poses; } diff --git a/libraries/animation/src/AnimVariant.cpp b/libraries/animation/src/AnimVariant.cpp index 509462984a..21fe234f7b 100644 --- a/libraries/animation/src/AnimVariant.cpp +++ b/libraries/animation/src/AnimVariant.cpp @@ -140,14 +140,19 @@ std::map AnimVariantMap::toDebugMap() const { result[pair.first] = QString::number(pair.second.getFloat(), 'f', 3); break; case AnimVariant::Type::Vec3: { + // To prevent filling up debug stats, don't show vec3 values + /* glm::vec3 value = pair.second.getVec3(); result[pair.first] = QString("(%1, %2, %3)"). arg(QString::number(value.x, 'f', 3)). arg(QString::number(value.y, 'f', 3)). arg(QString::number(value.z, 'f', 3)); + */ break; } case AnimVariant::Type::Quat: { + // To prevent filling up the anim stats, don't show quat values + /* glm::quat value = pair.second.getQuat(); result[pair.first] = QString("(%1, %2, %3, %4)"). arg(QString::number(value.x, 'f', 3)). @@ -155,10 +160,14 @@ std::map AnimVariantMap::toDebugMap() const { arg(QString::number(value.z, 'f', 3)). arg(QString::number(value.w, 'f', 3)); break; + */ } case AnimVariant::Type::String: + // To prevent filling up anim stats, don't show string values + /* result[pair.first] = pair.second.getString(); break; + */ default: assert(("invalid AnimVariant::Type", false)); } From 9d051b71bb59ef79a654bef67baef276462333f7 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 15 Sep 2018 10:14:22 +1200 Subject: [PATCH 089/259] Fix mini tablet sometimes flipping when grabbed by other hand --- scripts/system/miniTablet.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 6a12edefaf..bda5909491 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -397,6 +397,11 @@ deltaRotation, localRotation; + if (Overlays.getProperty(miniOverlay, "parentJointIndex") !== handJointIndex(uiHand)) { + // Overlay has been grabbed by other hand but this script hasn't received notification yet. + return; + } + defaultLocalRotation = MINI_ROTATIONS[uiHand]; handOrientation = Quat.multiply(MyAvatar.orientation, MyAvatar.getAbsoluteJointRotationInObjectFrame(handJointIndex(uiHand))); From 68e6142f8bdce15960da98370373ed5b5a1b1ae2 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 14 Sep 2018 15:34:00 -0700 Subject: [PATCH 090/259] Follow wallet setup referrer when autogenerating new wallet --- interface/resources/qml/hifi/commerce/wallet/Wallet.qml | 1 + interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 65d98af234..2cfa5094b9 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -831,6 +831,7 @@ Rectangle { Commerce.getWalletAuthenticatedStatus(); // before writing security image, ensures that salt/account password is set. Commerce.chooseSecurityImage(securityImagePath); Commerce.generateKeyPair(); + followReferrer({ referrer: walletSetup.referrer }); } function addLeadingZero(n) { diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml index eeecff9ad0..dc6ce45a74 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml @@ -28,7 +28,7 @@ Item { property string activeView: "step_1"; property string lastPage; property bool hasShownSecurityImageTip: false; - property string referrer; + property string referrer: ''; property string keyFilePath; property date startingTimestamp; property string setupAttemptID; From 1a44054c494af587aa617e93eeb06d7e2e3bb343 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 15 Sep 2018 09:15:43 -0700 Subject: [PATCH 091/259] attempt fix for fb-17131 --- libraries/entities/src/EntityScriptingInterface.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index d9924cb9fd..8f0fde5c9a 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -132,8 +132,8 @@ EntityItemProperties convertPropertiesToScriptSemantics(const EntityItemProperti EntityItemProperties scriptSideProperties = entitySideProperties; scriptSideProperties.setLocalPosition(entitySideProperties.getPosition()); scriptSideProperties.setLocalRotation(entitySideProperties.getRotation()); - scriptSideProperties.setLocalVelocity(entitySideProperties.getLocalVelocity()); - scriptSideProperties.setLocalAngularVelocity(entitySideProperties.getLocalAngularVelocity()); + scriptSideProperties.setLocalVelocity(entitySideProperties.getVelocity()); + scriptSideProperties.setLocalAngularVelocity(entitySideProperties.getAngularVelocity()); scriptSideProperties.setLocalDimensions(entitySideProperties.getDimensions()); bool success; @@ -181,8 +181,6 @@ EntityItemProperties convertPropertiesFromScriptSemantics(const EntityItemProper EntityItemProperties entitySideProperties = scriptSideProperties; bool success; - // TODO -- handle velocity and angularVelocity - if (scriptSideProperties.localPositionChanged()) { entitySideProperties.setPosition(scriptSideProperties.getLocalPosition()); } else if (scriptSideProperties.positionChanged()) { From f86075b2dda4303f706b65ebc9fb5f467615c302 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 15 Sep 2018 09:21:12 -0700 Subject: [PATCH 092/259] Revert "Fix for angularVelocity zeroing out on duplication or undo" This reverts commit 9c96a10f6cf0c60eb39cab3ea3e7460d0dcda0a5. --- scripts/system/libraries/entitySelectionTool.js | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 5bca58b1ac..1fc0f0611c 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -29,17 +29,6 @@ Script.include([ SelectionManager = (function() { var that = {}; - /** - * @description Removes known to be broken properties from a properties object - * @param properties - * @return properties - */ - var fixRemoveBrokenProperties = function (properties) { - // Reason: Entity property is always set to 0,0,0 which causes it to override angularVelocity (see MS17131) - delete properties.localAngularVelocity; - return properties; - } - // FUNCTION: SUBSCRIBE TO UPDATE MESSAGES function subscribeToUpdateMessages() { Messages.subscribe("entityToolUpdates"); @@ -130,7 +119,7 @@ SelectionManager = (function() { that.savedProperties = {}; for (var i = 0; i < that.selections.length; i++) { var entityID = that.selections[i]; - that.savedProperties[entityID] = fixRemoveBrokenProperties(Entities.getEntityProperties(entityID)); + that.savedProperties[entityID] = Entities.getEntityProperties(entityID); } }; @@ -258,7 +247,7 @@ SelectionManager = (function() { var originalEntityID = entitiesToDuplicate[i]; var properties = that.savedProperties[originalEntityID]; if (properties === undefined) { - properties = fixRemoveBrokenProperties(Entities.getEntityProperties(originalEntityID)); + properties = Entities.getEntityProperties(originalEntityID); } if (!properties.locked && (!properties.clientOnly || properties.owningAvatarID === MyAvatar.sessionUUID)) { if (nonDynamicEntityIsBeingGrabbedByAvatar(properties)) { From 5b3d92e8167e7bcab5a3ed697993288eef8b5938 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sun, 16 Sep 2018 08:09:26 +1200 Subject: [PATCH 093/259] Persist showing mini tablet for a while after it would otherwise hide --- scripts/system/miniTablet.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index bda5909491..2e5a5d86a2 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -565,7 +565,9 @@ MAX_CAMERA_HAND_ANGLE = 30, DEGREES_180 = 180, MIN_HAND_CAMERA_ANGLE_COS = Math.cos(Math.PI * MIN_HAND_CAMERA_ANGLE / DEGREES_180), - MAX_CAMERA_HAND_ANGLE_COS = Math.cos(Math.PI * MAX_CAMERA_HAND_ANGLE / DEGREES_180); + MAX_CAMERA_HAND_ANGLE_COS = Math.cos(Math.PI * MAX_CAMERA_HAND_ANGLE / DEGREES_180), + HIDING_DELAY = 1000, // ms + lastVisible = [0, 0]; function enterMiniDisabled() { @@ -646,6 +648,7 @@ // Should show mini tablet if it would be oriented toward the camera. if (show) { + // Calculate current visibility of mini tablet. jointIndex = handJointIndex(hand); handPosition = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, MyAvatar.getAbsoluteJointTranslationInObjectFrame(jointIndex))); @@ -660,6 +663,13 @@ show = show || (-Vec3.dot(miniToCameraDirection, Quat.getForward(handOrientation)) > MIN_HAND_CAMERA_ANGLE_COS); cameraToHand = -Vec3.dot(miniToCameraDirection, Quat.getForward(Camera.orientation)); show = show && (cameraToHand > MAX_CAMERA_HAND_ANGLE_COS); + + // Persist showing for a while after it would otherwise be hidden. + if (show) { + lastVisible[hand] = Date.now(); + } else { + show = Date.now() - lastVisible[hand] <= HIDING_DELAY; + } } return { From 1e8ae1a2940a53e867fc8d4bbadf8daca9b7f0da Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 15 Sep 2018 16:36:55 -0700 Subject: [PATCH 094/259] fix scaling of worn shape entities --- libraries/entities-renderer/src/RenderableShapeEntityItem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 71e3a0ff27..5003e36e86 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -97,10 +97,10 @@ void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce withWriteLock([&] { auto entity = getEntity(); _position = entity->getWorldPosition(); - _dimensions = entity->getScaledDimensions(); + _dimensions = entity->getUnscaledDimensions(); // get unscaled to avoid scaling twice _orientation = entity->getWorldOrientation(); updateModelTransformAndBound(); - _renderTransform = getModelTransform(); + _renderTransform = getModelTransform(); // contains parent scale, if this entity scales with its parent if (_shape == entity::Sphere) { _renderTransform.postScale(SPHERE_ENTITY_SCALE); } From ff319ebd950778c85b60078e13130e155ff675d9 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 17 Sep 2018 09:47:22 -0700 Subject: [PATCH 095/259] Bypass position updates from mixer --- interface/src/avatar/AvatarManager.cpp | 33 +++++++++++-------- interface/src/avatar/AvatarManager.h | 2 +- interface/src/avatar/MyAvatar.cpp | 11 +++---- .../src/avatars-renderer/Avatar.cpp | 26 +++++++++++++-- .../src/avatars-renderer/Avatar.h | 11 +++++-- libraries/avatars/src/AvatarHashMap.cpp | 3 +- 6 files changed, 61 insertions(+), 25 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 334cdc9608..aa651e9e11 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -81,7 +81,7 @@ AvatarManager::AvatarManager(QObject* parent) : const float AVATAR_TRANSIT_MAX_DISTANCE = 1.0f; const int AVATAR_TRANSIT_FRAME_COUNT = 20; - const int AVATAR_TRANSIT_FRAMES_PER_METER = 5; + const int AVATAR_TRANSIT_FRAMES_PER_METER = 15; _avatarTransitMaxDistance = AVATAR_TRANSIT_MAX_DISTANCE; _avatarTransitFrameCount = AVATAR_TRANSIT_FRAME_COUNT; @@ -136,7 +136,23 @@ void AvatarManager::setSpace(workload::SpacePointer& space ) { void AvatarManager::updateMyAvatar(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "AvatarManager::updateMyAvatar()"); - + /* + std::shared_ptr transit = _myAvatar->getTransit(); + bool initTransit = false; + if (!transit->isTransiting()) { + initTransit = transit->update(_myAvatar->getWorldPosition(), _avatarTransitFrameCount, _avatarTransitFramesPerMeter, _avatarTransitDistanceBased, _avatarTransitMaxDistance); + if (initTransit) { + _myAvatar->getSkeletonModel()->getRig().restoreAnimation(); + _myAvatar->getSkeletonModel()->getRig().overrideAnimation("https://hifi-content.s3.amazonaws.com/luis/test_scripts/transit_app/animations/teleport01_warp.fbx", 30, false, 0, 49); + } + } + if (transit->isTransiting()){ + glm::vec3 nextPosition; + if (!transit->getNextPosition(nextPosition)) { + _myAvatar->getSkeletonModel()->getRig().restoreAnimation(); + } + } + */ _myAvatar->update(deltaTime); render::Transaction transaction; _myAvatar->updateRenderItem(transaction); @@ -259,17 +275,8 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { numAvatarsUpdated++; } // smooth other avatars positions - { - float oneFrameDistance = glm::length(avatar->_globalPosition - avatar->_lastPosition); - if (oneFrameDistance > _avatarTransitMaxDistance) { - avatar->_transit.start(avatar->_lastPosition, avatar->_globalPosition, _avatarTransitFrameCount, _avatarTransitFramesPerMeter, _avatarTransitDistanceBased); - } - if (avatar->_transit.isTransiting()) { - glm::vec3 nextPosition; - if (avatar->_transit.getNextPosition(nextPosition)) { - avatar->setWorldPosition(nextPosition); - } - } + { + avatar->_transit.update(avatar->_globalPosition, _avatarTransitFrameCount, _avatarTransitFramesPerMeter, _avatarTransitDistanceBased, _avatarTransitMaxDistance); } avatar->simulate(deltaTime, inView); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 867c49063f..3fc675f9b8 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -237,7 +237,7 @@ private: // Other avatars smooth transit global configuration - bool _avatarTransitDistanceBased { false }; + bool _avatarTransitDistanceBased { true }; float _avatarTransitMaxDistance; int _avatarTransitFrameCount; int _avatarTransitFramesPerMeter; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c8af792d8f..a6e77f76e9 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -610,7 +610,6 @@ void MyAvatar::updateChildCauterization(SpatiallyNestablePointer object, bool ca void MyAvatar::simulate(float deltaTime) { PerformanceTimer perfTimer("simulate"); - animateScaleChanges(deltaTime); setFlyingEnabled(getFlyingEnabled()); @@ -1012,7 +1011,7 @@ glm::vec3 MyAvatar::worldToJointPoint(const glm::vec3& position, const int joint if (_skeletonModel->getJointPositionInWorldFrame(jointIndex, jointPos)) { _skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot); } else { - qWarning() << "Invalid joint index specified: " << jointIndex; + //qWarning() << "Invalid joint index specified: " << jointIndex; } } glm::vec3 modelOffset = position - jointPos; @@ -1034,7 +1033,7 @@ glm::vec3 MyAvatar::worldToJointDirection(const glm::vec3& worldDir, const int j glm::quat MyAvatar::worldToJointRotation(const glm::quat& worldRot, const int jointIndex) const { glm::quat jointRot = getWorldOrientation();//default value if no or invalid joint specified if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) { - qWarning() << "Invalid joint index specified: " << jointIndex; + //qWarning() << "Invalid joint index specified: " << jointIndex; } glm::quat jointSpaceRot = glm::inverse(jointRot) * worldRot; return jointSpaceRot; @@ -1048,7 +1047,7 @@ glm::vec3 MyAvatar::jointToWorldPoint(const glm::vec3& jointSpacePos, const int if (_skeletonModel->getJointPositionInWorldFrame(jointIndex, jointPos)) { _skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot); } else { - qWarning() << "Invalid joint index specified: " << jointIndex; + //qWarning() << "Invalid joint index specified: " << jointIndex; } } @@ -1061,7 +1060,7 @@ glm::vec3 MyAvatar::jointToWorldPoint(const glm::vec3& jointSpacePos, const int glm::vec3 MyAvatar::jointToWorldDirection(const glm::vec3& jointSpaceDir, const int jointIndex) const { glm::quat jointRot = getWorldOrientation();//default value if no or invalid joint specified if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) { - qWarning() << "Invalid joint index specified: " << jointIndex; + //qWarning() << "Invalid joint index specified: " << jointIndex; } glm::vec3 worldDir = jointRot * jointSpaceDir; return worldDir; @@ -1070,7 +1069,7 @@ glm::vec3 MyAvatar::jointToWorldDirection(const glm::vec3& jointSpaceDir, const glm::quat MyAvatar::jointToWorldRotation(const glm::quat& jointSpaceRot, const int jointIndex) const { glm::quat jointRot = getWorldOrientation();//default value if no or invalid joint specified if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) { - qWarning() << "Invalid joint index specified: " << jointIndex; + //qWarning() << "Invalid joint index specified: " << jointIndex; } glm::quat worldRot = jointRot * jointSpaceRot; return worldRot; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 3dae9171a8..9feb9a5983 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -113,6 +113,16 @@ void Avatar::setShowNamesAboveHeads(bool show) { showNamesAboveHeads = show; } +bool AvatarTransit::update(const glm::vec3& avatarPosition, int totalFrames, int framesPerMeter, bool isDistanceBased, float maxDistance) { + glm::vec3 currentPosition = _isTransiting ? _currentPosition : avatarPosition; + float oneFrameDistance = glm::length(currentPosition - _lastPosition); + if (oneFrameDistance > maxDistance && !_isTransiting) { + start(_lastPosition, currentPosition, totalFrames, framesPerMeter, isDistanceBased); + return true; + } + return false; +} + void AvatarTransit::start(const glm::vec3& startPosition, const glm::vec3& endPosition, int totalFrames, int framesPerMeter, bool isDistanceBased) { _startPosition = startPosition; _endPosition = endPosition; @@ -140,6 +150,7 @@ void AvatarTransit::calculateSteps(int stepCount) { } bool AvatarTransit::getNextPosition(glm::vec3& nextPosition) { + _lastPosition = _currentPosition; int lastIdx = (int)_transitSteps.size() - 1; _isTransiting = _step < lastIdx; if (_isTransiting) { @@ -482,6 +493,18 @@ void Avatar::relayJointDataToChildren() { void Avatar::simulate(float deltaTime, bool inView) { PROFILE_RANGE(simulation, "simulate"); + if (_transit.isTransiting()) { + glm::vec3 nextPosition; + if (_transit.getNextPosition(nextPosition)) { + // setWorldPosition(nextPosition); + _globalPosition = nextPosition; + _globalPositionChanged = usecTimestampNow(); + if (!hasParent()) { + setLocalPosition(nextPosition); + } + } + } + _simulationRate.increment(); if (inView) { _simulationInViewRate.increment(); @@ -492,7 +515,7 @@ void Avatar::simulate(float deltaTime, bool inView) { PROFILE_RANGE(simulation, "updateJoints"); if (inView) { Head* head = getHead(); - if (_hasNewJointData) { + if (true) { _skeletonModel->getRig().copyJointsFromJointData(_jointData); glm::mat4 rootTransform = glm::scale(_skeletonModel->getScale()) * glm::translate(_skeletonModel->getOffset()); _skeletonModel->getRig().computeExternalPoses(rootTransform); @@ -517,7 +540,6 @@ void Avatar::simulate(float deltaTime, bool inView) { _skeletonModel->simulate(deltaTime, false); } _skeletonModelSimulationRate.increment(); - _lastPosition = _globalPosition; } // update animation for display name fade in/out diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 2d940b8fd9..20cac1fcc4 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -53,9 +53,13 @@ class Texture; class AvatarTransit { public: AvatarTransit() {}; + bool update(const glm::vec3& avatarPosition, int totalFrames, int framesPerMeter, bool isDistanceBased, float maxDistance); void start(const glm::vec3& startPosition, const glm::vec3& endPosition, int totalFrames, int framesPerMeter, bool isDistanceBased); bool getNextPosition(glm::vec3& nextPosition); bool isTransiting() { return _isTransiting; }; + glm::vec3 getCurrentPosition() { return _currentPosition; }; + int getCurrentStep() { return _step; }; + private: void calculateSteps(int stepCount); bool _isTransiting{ false }; @@ -63,7 +67,8 @@ private: glm::vec3 _endPosition; glm::vec3 _currentPosition; std::vector _transitSteps; - int _step{ 0 }; + glm::vec3 _lastPosition; + int _step { 0 }; }; class Avatar : public AvatarData, public scriptable::ModelProvider { @@ -377,6 +382,8 @@ public: virtual scriptable::ScriptableModelBase getScriptableModel() override; + std::shared_ptr getTransit() { return std::make_shared(_transit); }; + signals: void targetScaleChanged(float targetScale); @@ -534,7 +541,7 @@ protected: bool _isFading { false }; bool _reconstructSoftEntitiesJointMap { false }; float _modelScale { 1.0f }; - glm::vec3 _lastPosition; + AvatarTransit _transit; diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 1383939b8b..424fef0168 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -247,6 +247,7 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointerparseDataFromBuffer(byteArray); message->seek(positionBeforeRead + bytesRead); @@ -297,7 +298,6 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer // In this case, the "sendingNode" is the Avatar Mixer. avatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); _replicas.processAvatarIdentity(identityUUID, message->getMessage(), identityChanged, displayNameChanged); - } } @@ -310,6 +310,7 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess // grab the avatar so we can ask it to process trait data bool isNewAvatar; auto avatar = newOrExistingAvatar(avatarID, sendingNode, isNewAvatar); + // read the first trait type for this avatar AvatarTraits::TraitType traitType; message->readPrimitive(&traitType); From 11fe657c0588943a4d275f66923cf78c6237fbd2 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Mon, 17 Sep 2018 10:33:04 -0700 Subject: [PATCH 096/259] Swaped AnimVar and StatMachine panels --- interface/resources/qml/AnimStats.qml | 59 ++++++++++++++------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/interface/resources/qml/AnimStats.qml b/interface/resources/qml/AnimStats.qml index d000f3d9be..91ed0eb69b 100644 --- a/interface/resources/qml/AnimStats.qml +++ b/interface/resources/qml/AnimStats.qml @@ -50,35 +50,6 @@ Item { StatText { text: root.positionText } - StatText { - text: "State Machines:---------------------------------------------------------------------------" - } - ListView { - width: firstCol.width - height: root.animStateMachines.length * 15 - visible: root.animStateMchines.length > 0; - model: root.animStateMachines - delegate: StatText { - text: { - return modelData; - } - } - } - } - } - - Rectangle { - width: secondCol.width + 8 - height: secondCol.height + 8 - color: root.bgColor; - - Column { - id: secondCol - spacing: 4; x: 4; y: 4; - - StatText { - text: root.rotationText - } StatText { text: "Anim Vars:--------------------------------------------------------------------------------" } @@ -109,6 +80,36 @@ Item { } } + Rectangle { + width: secondCol.width + 8 + height: secondCol.height + 8 + color: root.bgColor; + + Column { + id: secondCol + spacing: 4; x: 4; y: 4; + + StatText { + text: root.rotationText + } + StatText { + text: "State Machines:---------------------------------------------------------------------------" + } + ListView { + width: firstCol.width + height: root.animStateMachines.length * 15 + visible: root.animStateMchines.length > 0; + model: root.animStateMachines + delegate: StatText { + text: { + return modelData; + } + } + } + + } + } + Rectangle { width: thirdCol.width + 8 height: thirdCol.height + 8 From 143a059b16cb5c429a352fb58c0d8aa352986b0a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 17 Sep 2018 11:11:36 -0700 Subject: [PATCH 097/259] Small changes to AppUI and PAL --- scripts/modules/appUi.js | 7 +++---- scripts/system/pal.js | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/scripts/modules/appUi.js b/scripts/modules/appUi.js index 02beb41674..dab377911b 100644 --- a/scripts/modules/appUi.js +++ b/scripts/modules/appUi.js @@ -53,10 +53,7 @@ function AppUi(properties) { }; that.open = function open(optionalUrl, optionalInject) { // How to open the app. var url = optionalUrl || that.home; - var inject = that.inject; - if (optionalUrl && optionalInject) { - inject = optionalInject; - } + var inject = optionalInject || that.inject; if (that.isQMLUrl(url)) { that.tablet.loadQMLSource(url); @@ -108,6 +105,7 @@ function AppUi(properties) { }; that.notificationPollCallback = that.ignore; that.notificationPollCaresAboutSince = false; + that.notificationInitialCallbackMade = false; that.notificationDisplayBanner = function (message) { Window.displayAnnouncement(message); }; @@ -189,6 +187,7 @@ function AppUi(properties) { } console.debug(that.buttonName, 'notification data for processing:', JSON.stringify(notificationData)); that.notificationPollCallback(notificationData); + that.notificationInitialCallbackMade = true; currentDataPageToRetrieve = 1; concatenatedServerResponse = new Array(); } else { diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 1abac53f50..d9e99272ba 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -823,7 +823,6 @@ function notificationDataProcessPage(data) { } var shouldShowDot = false; -var firstBannerNotificationShown = false; function notificationPollCallback(onlineUsersArray) { shouldShowDot = onlineUsersArray.length > 0; @@ -835,10 +834,10 @@ function notificationPollCallback(onlineUsersArray) { }); var message; - if (!firstBannerNotificationShown) { - message = onlineUsersArray.length + " of your connections are online. Open PEOPLE to join them!"; + if (!ui.notificationInitialCallbackMade) { + message = onlineUsersArray.length + " of your connections " + + (onlineUsersArray.length === 1 ? "is" : "are") + " online. Open PEOPLE to join them!"; ui.notificationDisplayBanner(message); - firstBannerNotificationShown = true; } else { for (var i = 0; i < onlineUsersArray.length; i++) { message = onlineUsersArray[i].username + " is available in " + From f339bbdd7f5343a4571296ff147a4830476df156 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 17 Sep 2018 13:29:04 -0700 Subject: [PATCH 098/259] Fix bug while walking --- interface/src/avatar/AvatarManager.cpp | 4 ++-- .../src/avatars-renderer/Avatar.cpp | 22 ++++++++++++------- .../src/avatars-renderer/Avatar.h | 1 + 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index aa651e9e11..cea0ff0f4d 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -80,8 +80,8 @@ AvatarManager::AvatarManager(QObject* parent) : }); const float AVATAR_TRANSIT_MAX_DISTANCE = 1.0f; - const int AVATAR_TRANSIT_FRAME_COUNT = 20; - const int AVATAR_TRANSIT_FRAMES_PER_METER = 15; + const int AVATAR_TRANSIT_FRAME_COUNT = 11; // Based on testing + const int AVATAR_TRANSIT_FRAMES_PER_METER = 3; // Based on testing _avatarTransitMaxDistance = AVATAR_TRANSIT_MAX_DISTANCE; _avatarTransitFrameCount = AVATAR_TRANSIT_FRAME_COUNT; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 9feb9a5983..0fc04febaa 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -119,7 +119,8 @@ bool AvatarTransit::update(const glm::vec3& avatarPosition, int totalFrames, int if (oneFrameDistance > maxDistance && !_isTransiting) { start(_lastPosition, currentPosition, totalFrames, framesPerMeter, isDistanceBased); return true; - } + } + updatePosition(avatarPosition); return false; } @@ -149,15 +150,20 @@ void AvatarTransit::calculateSteps(int stepCount) { } } -bool AvatarTransit::getNextPosition(glm::vec3& nextPosition) { - _lastPosition = _currentPosition; - int lastIdx = (int)_transitSteps.size() - 1; - _isTransiting = _step < lastIdx; +void AvatarTransit::updatePosition(const glm::vec3& avatarPosition) { + _lastPosition = _isTransiting ? _currentPosition : avatarPosition; if (_isTransiting) { - _step++; - nextPosition = _transitSteps[_step]; - _currentPosition = nextPosition; + int lastIdx = (int)_transitSteps.size() - 1; + _isTransiting = _step < lastIdx; + if (_isTransiting) { + _step++; + _currentPosition = _transitSteps[_step]; + } } +} + +bool AvatarTransit::getNextPosition(glm::vec3& nextPosition) { + nextPosition = _currentPosition; return _isTransiting; } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 20cac1fcc4..22163635ff 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -62,6 +62,7 @@ public: private: void calculateSteps(int stepCount); + void updatePosition(const glm::vec3& avatarPosition); bool _isTransiting{ false }; glm::vec3 _startPosition; glm::vec3 _endPosition; From b39391ca7a5a248394c2955eafd424115d6b6fd8 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 17 Sep 2018 13:53:57 -0700 Subject: [PATCH 099/259] From first frame --- libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 0fc04febaa..05d49a9ec0 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -114,14 +114,15 @@ void Avatar::setShowNamesAboveHeads(bool show) { } bool AvatarTransit::update(const glm::vec3& avatarPosition, int totalFrames, int framesPerMeter, bool isDistanceBased, float maxDistance) { + bool starting = false; glm::vec3 currentPosition = _isTransiting ? _currentPosition : avatarPosition; float oneFrameDistance = glm::length(currentPosition - _lastPosition); if (oneFrameDistance > maxDistance && !_isTransiting) { start(_lastPosition, currentPosition, totalFrames, framesPerMeter, isDistanceBased); - return true; + starting = true; } updatePosition(avatarPosition); - return false; + return starting; } void AvatarTransit::start(const glm::vec3& startPosition, const glm::vec3& endPosition, int totalFrames, int framesPerMeter, bool isDistanceBased) { From c453594eb741f93cd3b146dfed961887d88e6641 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 17 Sep 2018 14:41:18 -0700 Subject: [PATCH 100/259] Overhaul notification logic for PAL --- scripts/system/pal.js | 59 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index d9e99272ba..1be5b44786 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -823,8 +823,49 @@ function notificationDataProcessPage(data) { } var shouldShowDot = false; -function notificationPollCallback(onlineUsersArray) { - shouldShowDot = onlineUsersArray.length > 0; +var storedOnlineUsersArray = []; +function notificationPollCallback(connectionsArray) { + // + // START logic for handling online/offline user changes + // + var i, j; + var newlyOnlineConnectionsArray = []; + for (i = 0; i < connectionsArray.length; i++) { + var currentUser = connectionsArray[i]; + + if (connectionsArray[i].online) { + var indexOfStoredOnlineUser = -1; + for (j = 0; j < storedOnlineUsersArray.length; j++) { + if (currentUser.username === storedOnlineUsersArray[j].username) { + indexOfStoredOnlineUser = j; + break; + } + } + // If the user record isn't already presesnt inside `storedOnlineUsersArray`... + if (indexOfStoredOnlineUser < 0) { + storedOnlineUsersArray.push(currentUser); + newlyOnlineConnectionsArray.push(currentUser); + } + } else { + var indexOfOfflineUser = -1; + for (j = 0; j < storedOnlineUsersArray.length; j++) { + if (currentUser.username === storedOnlineUsersArray[j].username) { + indexOfOfflineUser = j; + break; + } + } + if (indexOfOfflineUser >= 0) { + storedOnlineUsersArray.splice(indexOfOfflineUser); + } + } + } + // If there's new data, the light should turn on. + // If the light is already on and you have connections online, the light should stay on. + // In all other cases, the light should turn off or stay off. + shouldShowDot = newlyOnlineConnectionsArray.length > 0 || (storedOnlineUsersArray.length > 0 && shouldShowDot); + // + // END logic for handling online/offline user changes + // if (!ui.isOpen) { ui.messagesWaiting(shouldShowDot); @@ -835,13 +876,13 @@ function notificationPollCallback(onlineUsersArray) { var message; if (!ui.notificationInitialCallbackMade) { - message = onlineUsersArray.length + " of your connections " + - (onlineUsersArray.length === 1 ? "is" : "are") + " online. Open PEOPLE to join them!"; + message = newlyOnlineConnectionsArray.length + " of your connections " + + (newlyOnlineConnectionsArray.length === 1 ? "is" : "are") + " online. Open PEOPLE to join them!"; ui.notificationDisplayBanner(message); } else { - for (var i = 0; i < onlineUsersArray.length; i++) { - message = onlineUsersArray[i].username + " is available in " + - onlineUsersArray[i].location.root.name + ". Open PEOPLE to join them!"; + for (i = 0; i < newlyOnlineConnectionsArray.length; i++) { + message = newlyOnlineConnectionsArray[i].username + " is available in " + + newlyOnlineConnectionsArray[i].location.root.name + ". Open PEOPLE to join them!"; ui.notificationDisplayBanner(message); } } @@ -861,12 +902,12 @@ function startup() { onOpened: palOpened, onClosed: off, onMessage: fromQml, - notificationPollEndpoint: "/api/v1/users?filter=connections&status=online&per_page=10", + notificationPollEndpoint: "/api/v1/users?filter=connections&per_page=10", notificationPollTimeoutMs: 60000, notificationDataProcessPage: notificationDataProcessPage, notificationPollCallback: notificationPollCallback, notificationPollStopPaginatingConditionMet: isReturnedDataEmpty, - notificationPollCaresAboutSince: true + notificationPollCaresAboutSince: false }); Window.domainChanged.connect(clearLocalQMLDataAndClosePAL); Window.domainConnectionRefused.connect(clearLocalQMLDataAndClosePAL); From 78b41390394b378bf11ef8d65922cde3824bf2b9 Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Mon, 17 Sep 2018 19:16:04 -0300 Subject: [PATCH 101/259] Fix android login infinite loading (case 18459) --- android/app/src/main/cpp/native.cpp | 22 +++++++++++++--- .../hifiinterface/fragment/LoginFragment.java | 26 ++++++++++++++++++- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/android/app/src/main/cpp/native.cpp b/android/app/src/main/cpp/native.cpp index ce5af01f29..ba956ba322 100644 --- a/android/app/src/main/cpp/native.cpp +++ b/android/app/src/main/cpp/native.cpp @@ -255,6 +255,16 @@ JNIEXPORT jstring JNICALL Java_io_highfidelity_hifiinterface_fragment_HomeFragme return env->NewStringUTF(lastLocation.toString().toLatin1().data()); } +JNIEXPORT void JNICALL +Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeCancelLogin(JNIEnv *env, jobject instance) { + + auto accountManager = DependencyManager::get(); + + QObject::disconnect(accountManager.data(), &AccountManager::loginComplete, nullptr, nullptr); + QObject::disconnect(accountManager.data(), &AccountManager::loginFailed, nullptr, nullptr); + +} + JNIEXPORT void JNICALL Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeLogin(JNIEnv *env, jobject instance, jstring username_, jstring password_, @@ -273,17 +283,23 @@ Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeLogin(JNIEnv *en QObject::connect(accountManager.data(), &AccountManager::loginComplete, [](const QUrl& authURL) { jboolean jSuccess = (jboolean) true; - __loginCompletedListener.callMethod("handleLoginCompleted", "(Z)V", jSuccess); + if (__loginCompletedListener.isValid()) { + __loginCompletedListener.callMethod("handleLoginCompleted", "(Z)V", jSuccess); + } }); QObject::connect(accountManager.data(), &AccountManager::loginFailed, []() { jboolean jSuccess = (jboolean) false; - __loginCompletedListener.callMethod("handleLoginCompleted", "(Z)V", jSuccess); + if (__loginCompletedListener.isValid()) { + __loginCompletedListener.callMethod("handleLoginCompleted", "(Z)V", jSuccess); + } }); QObject::connect(accountManager.data(), &AccountManager::usernameChanged, [](const QString& username) { QAndroidJniObject string = QAndroidJniObject::fromString(username); - __usernameChangedListener.callMethod("handleUsernameChanged", "(Ljava/lang/String;)V", string.object()); + if (__usernameChangedListener.isValid()) { + __usernameChangedListener.callMethod("handleUsernameChanged", "(Ljava/lang/String;)V", string.object()); + } }); QMetaObject::invokeMethod(accountManager.data(), "requestAccessToken", diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/LoginFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/LoginFragment.java index f29c237ed7..1a030baabf 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/LoginFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/LoginFragment.java @@ -4,6 +4,7 @@ import android.app.Activity; import android.app.Fragment; import android.app.ProgressDialog; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; @@ -19,8 +20,13 @@ import android.widget.Button; import android.widget.EditText; import android.widget.TextView; +import org.qtproject.qt5.android.QtNative; + import io.highfidelity.hifiinterface.R; +import static org.qtproject.qt5.android.QtActivityDelegate.ApplicationActive; +import static org.qtproject.qt5.android.QtActivityDelegate.ApplicationInactive; + public class LoginFragment extends Fragment { private EditText mUsername; @@ -32,6 +38,7 @@ public class LoginFragment extends Fragment { private ProgressDialog mDialog; public native void nativeLogin(String username, String password, Activity usernameChangedListener); + public native void nativeCancelLogin(); private LoginFragment.OnLoginInteractionListener mListener; @@ -125,10 +132,19 @@ public class LoginFragment extends Fragment { mListener = null; } + @Override + public void onResume() { + super.onResume(); + // This hack intends to keep Qt threads running even after the app comes from background + QtNative.setApplicationState(ApplicationActive); + } + @Override public void onStop() { super.onStop(); cancelActivityIndicator(); + // Leave the Qt app paused + QtNative.setApplicationState(ApplicationInactive); hideKeyboard(); } @@ -164,7 +180,15 @@ public class LoginFragment extends Fragment { mDialog = new ProgressDialog(getContext()); } mDialog.setMessage(getString(R.string.logging_in)); - mDialog.setCancelable(false); + mDialog.setCancelable(true); + mDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialogInterface) { + nativeCancelLogin(); + cancelActivityIndicator(); + mLoginButton.setEnabled(true); + } + }); mDialog.show(); } From 8229c14c4486c57e963f470dd9f0c54a56950d41 Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Mon, 17 Sep 2018 15:17:43 -0700 Subject: [PATCH 102/259] Fix for type in AnimStats.qml --- interface/resources/qml/AnimStats.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/AnimStats.qml b/interface/resources/qml/AnimStats.qml index 91ed0eb69b..b1900cf0a7 100644 --- a/interface/resources/qml/AnimStats.qml +++ b/interface/resources/qml/AnimStats.qml @@ -98,7 +98,7 @@ Item { ListView { width: firstCol.width height: root.animStateMachines.length * 15 - visible: root.animStateMchines.length > 0; + visible: root.animStateMachines.length > 0; model: root.animStateMachines delegate: StatText { text: { From f2a046da9160edafec7b94d838f7ed19983a46ce Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Thu, 13 Sep 2018 12:15:18 -0700 Subject: [PATCH 103/259] Install marketplace item tester --- .../MarketplaceItemTester.qml | 157 ++++++++++++++++++ interface/src/Application.cpp | 9 +- interface/src/commerce/QmlCommerce.cpp | 1 + scripts/system/commerce/wallet.js | 23 +++ 4 files changed, 186 insertions(+), 4 deletions(-) create mode 100644 interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml new file mode 100644 index 0000000000..80cf82678f --- /dev/null +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -0,0 +1,157 @@ +// +// marketplaceItemTester +// qml/hifi/commerce/marketplaceItemTester +// +// Load items not in the marketplace for testing purposes +// +// Created by Zach Fox on 2018-09-05 +// Copyright 2018 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 +// + +import QtQuick 2.5 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Dialogs 1.0 +import QtQuick.Layouts 1.1 +import Hifi 1.0 as Hifi +import "../../../styles-uit" as HifiStylesUit +import "../../../controls-uit" as HifiControlsUit + + + +Rectangle { + id:root + HifiStylesUit.HifiConstants { id: hifi } + color: hifi.colors.white + ListModel { id: listModel } + ListView { + anchors.fill: parent + anchors.leftMargin: 12 + anchors.bottomMargin: 40 + model: listModel + spacing: 5 + delegate: RowLayout { + anchors.left: parent.left + width: parent.width + spacing: 5 + Text { + text: { + var match = resource.match(/\/([^/]*)$/); + return match ? match[1] : resource; + } + font.pointSize: 12 + Layout.preferredWidth: root.width * .6 + horizontalAlignment: Text.AlignBottom + } + Text { + text: assetType + font.pointSize: 10 + Layout.preferredWidth: root.width * .2 + horizontalAlignment: Text.AlignBottom + } + property var actions: { + "forward": function(resource, assetType){ + if ("application" == assetType) { + Commerce.installApp(resource); + Commerce.openApp(resource); + } + // XXX support other resource types here. + }, + "trash": function(){ + if ("application" == assetType) { + Commerce.uninstallApp(resource); + } + // XXX support other resource types here. + listModel.remove(index); + } + } + Repeater { + model: [ + { "name": "forward", "glyph": hifi.glyphs.forward, "size": 30 }, + { "name": "trash", "glyph": hifi.glyphs.trash, "size": 22} + ] + HifiStylesUit.HiFiGlyphs { + text: modelData.glyph + size: modelData.size + color: hifi.colors.black + horizontalAlignment: Text.AlignHCenter + MouseArea { + anchors.fill: parent + onClicked: { + actions[modelData.name](resource, assetType); + } + } + } + } + } + headerPositioning: ListView.OverlayHeader + header: HifiStylesUit.RalewayRegular { + id: rootHeader + text: "Marketplace Item Tester" + height: 80 + width: paintedWidth + size: 22 + color: hifi.colors.black + anchors.left: parent.left + anchors.leftMargin: 12 + } + footerPositioning: ListView.OverlayFooter + footer: Row { + id: rootActions + spacing: 20 + anchors.horizontalCenter: parent.horizontalCenter + property string currentAction + function assetType(resource) { + return (resource.match(/\.app\.json$/) ? "application" : + resource.match(/\.(?:fbx|fst)$/) ? "avatar" : + resource.match(/\.json\.gz$/) ? "content set" : + resource.match(/\.json$/) ? "entity or wearable" : + "unknown") + } + function onResourceSelected(resource) { + // It is possible that we received the present signal + // from something other than our browserAsync window. + // Alas, there is nothing we can do about that so charge + // ahead as though we are sure the present signal is one + // we expect. + if ("load file" == currentAction) { + print("disconnecting load file"); + Window.browseChanged.disconnect(onResourceSelected); + } else if ("load url" == currentAction) { + print("disconnecting load url"); + Window.promptTextChanged.disconnect(onResourceSelected); + } + if (resource) { + listModel.append( { + "resource": resource.trim(), + "assetType": assetType(resource.trim()) } ); + } + } + property var actions: { + "Load File": function(){ + rootActions.currentAction = "load file"; + Window.browseChanged.connect(onResourceSelected); + Window.browseAsync("Please select a file", "", "Assets (*.app.json *.json *.fbx *.json.gz)"); + }, + "Load URL": function(){ + rootActions.currentAction = "load url"; + Window.promptTextChanged.connect(onResourceSelected); + Window.promptAsync("Please enter a URL", ""); + } + } + Repeater { + model: [ "Load File", "Load URL" ] + HifiControlsUit.Button { + color: hifi.buttons.blue + fontSize: 20 + text: modelData + width: root.width / 3 + height: 40 + onClicked: actions[text]() + } + } + } + } +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 826f6a87a9..0415475397 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1691,21 +1691,21 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo return DependencyManager::get()->navigationFocused() ? 1 : 0; }); _applicationStateDevice->setInputVariant(STATE_PLATFORM_WINDOWS, []() -> float { -#if defined(Q_OS_WIN) +#if defined(Q_OS_WIN) return 1; #else return 0; #endif }); _applicationStateDevice->setInputVariant(STATE_PLATFORM_MAC, []() -> float { -#if defined(Q_OS_MAC) +#if defined(Q_OS_MAC) return 1; #else return 0; #endif }); _applicationStateDevice->setInputVariant(STATE_PLATFORM_ANDROID, []() -> float { -#if defined(Q_OS_ANDROID) +#if defined(Q_OS_ANDROID) return 1; #else return 0; @@ -2881,9 +2881,10 @@ void Application::initializeUi() { QUrl{ "hifi/commerce/common/CommerceLightbox.qml" }, QUrl{ "hifi/commerce/common/EmulatedMarketplaceHeader.qml" }, QUrl{ "hifi/commerce/common/FirstUseTutorial.qml" }, - QUrl{ "hifi/commerce/common/SortableListModel.qml" }, QUrl{ "hifi/commerce/common/sendAsset/SendAsset.qml" }, + QUrl{ "hifi/commerce/common/SortableListModel.qml" }, QUrl{ "hifi/commerce/inspectionCertificate/InspectionCertificate.qml" }, + QUrl{ "hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml"}, QUrl{ "hifi/commerce/purchases/PurchasedItem.qml" }, QUrl{ "hifi/commerce/purchases/Purchases.qml" }, QUrl{ "hifi/commerce/wallet/Help.qml" }, diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 06da18148f..10be228310 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -228,6 +228,7 @@ QString QmlCommerce::getInstalledApps(const QString& justInstalledAppID) { // Thus, we protect against deleting the .app.json from the user's disk (below) // by skipping that check for the app we just installed. if ((justInstalledAppID != "") && ((justInstalledAppID + ".app.json") == appFileName)) { + installedAppsFromMarketplace += appFileName + ","; continue; } diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 5939b36438..12ec7dce6b 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -583,6 +583,27 @@ // // Manage the connection between the button and the window. // + var DEVELOPER_MENU = "Developer"; + var MARKETPLACE_ITEM_TESTER_LABEL = "Marktplace Item Tester"; + var MARKETPLACE_ITEM_TESTER_QML_SOURCE = "hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml"; + function installMarketplaceItemTester() { + if (!Menu.menuExists(DEVELOPER_MENU)) { + Menu.addMenu(DEVELOPER_MENU); + } + + if (!Menu.menuItemExists(DEVELOPER_MENU, MARKETPLACE_ITEM_TESTER_LABEL)) { + Menu.addMenuItem({ menuName: DEVELOPER_MENU, + menuItemName: MARKETPLACE_ITEM_TESTER_LABEL, + isCheckable: false }) + } + + Menu.menuItemEvent.connect(function (menuItem) { + if (menuItem === MARKETPLACE_ITEM_TESTER_LABEL) { + tablet.loadQMLSource(MARKETPLACE_ITEM_TESTER_QML_SOURCE); + } + }); + } + var button; var buttonName = "WALLET"; var tablet = null; @@ -600,7 +621,9 @@ button.clicked.connect(onButtonClicked); tablet.screenChanged.connect(onTabletScreenChanged); } + installMarketplaceItemTester(); } + var isWired = false; var isUpdateOverlaysWired = false; function off() { From 4807b5af7b49b722807a730e83714c21dd985484 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 18 Sep 2018 09:57:26 -0700 Subject: [PATCH 104/259] fixing display of user/pw incorrect --- interface/resources/qml/LoginDialog/LinkAccountBody.qml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 57293cb5e3..a6f962a677 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -43,7 +43,7 @@ Item { function resize() { var targetWidth = Math.max(titleWidth, form.contentWidth); - var targetHeight = hifi.dimensions.contentSpacing.y + mainTextContainer.height + + var targetHeight = hifi.dimensions.contentSpacing.y + flavorText.height + mainTextContainer.height + 4 * hifi.dimensions.contentSpacing.y + form.height; if (additionalInformation.visible) { @@ -106,14 +106,15 @@ Item { ShortcutText { id: mainTextContainer anchors { - top: parent.top + top: flavorText.bottom left: parent.left margins: 0 - topMargin: hifi.dimensions.contentSpacing.y + topMargin: 1.5 * hifi.dimensions.contentSpacing.y } visible: false text: qsTr("Username or password incorrect.") + height: flavorText.height - 20 wrapMode: Text.WordWrap color: hifi.colors.redAccent lineHeight: 1 @@ -128,7 +129,7 @@ Item { anchors { top: mainTextContainer.bottom - topMargin: 2 * hifi.dimensions.contentSpacing.y + topMargin: 1.5 * hifi.dimensions.contentSpacing.y } spacing: 2 * hifi.dimensions.contentSpacing.y From 8f5923d5624026d492ef5067dc915ccdfe56d4dc Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Tue, 18 Sep 2018 11:23:32 -0700 Subject: [PATCH 105/259] Show installed apps on start of marketplace item tester Also, refactor a bit. --- .../MarketplaceItemTester.qml | 120 ++++++++++++------ 1 file changed, 79 insertions(+), 41 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index 80cf82678f..b36deb8b70 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -22,39 +22,61 @@ import "../../../controls-uit" as HifiControlsUit Rectangle { - id:root + id: root + property string installedApps HifiStylesUit.HifiConstants { id: hifi } + ListModel { id: resourceListModel } + color: hifi.colors.white - ListModel { id: listModel } + + function buildResourceObj(resource) { + resource = resource.trim(); + var assetType = (resource.match(/\.app\.json$/) ? "application" : + resource.match(/\.(?:fbx|fst)$/) ? "avatar" : + resource.match(/\.json\.gz$/) ? "content set" : + resource.match(/\.json$/) ? "entity or wearable" : + "unknown"); + return { "resource": resource, "assetType": assetType }; + } + + function installResourceObj(resourceObj) { + if ("application" == resourceObj["assetType"]) { + Commerce.installApp(resourceObj["resource"]); + } + // XXX support other asset types here + } + + function addAllInstalledAppsToList() { + var i, apps = Commerce.getInstalledApps().split(","), len = apps.length; + for(i = 0; i < len - 1; ++i) { + if (i in apps) { + resourceListModel.append(buildResourceObj(apps[i])); + } + } + } + + Component.onCompleted: { + // On startup, list includes all tester-installed assets. + addAllInstalledAppsToList(); + // XXX support other asset types here + } + ListView { anchors.fill: parent anchors.leftMargin: 12 anchors.bottomMargin: 40 - model: listModel + anchors.rightMargin: 12 + model: resourceListModel spacing: 5 + delegate: RowLayout { anchors.left: parent.left width: parent.width spacing: 5 - Text { - text: { - var match = resource.match(/\/([^/]*)$/); - return match ? match[1] : resource; - } - font.pointSize: 12 - Layout.preferredWidth: root.width * .6 - horizontalAlignment: Text.AlignBottom - } - Text { - text: assetType - font.pointSize: 10 - Layout.preferredWidth: root.width * .2 - horizontalAlignment: Text.AlignBottom - } + property var actions: { "forward": function(resource, assetType){ if ("application" == assetType) { - Commerce.installApp(resource); Commerce.openApp(resource); } // XXX support other resource types here. @@ -64,9 +86,27 @@ Rectangle { Commerce.uninstallApp(resource); } // XXX support other resource types here. - listModel.remove(index); + resourceListModel.remove(index); } } + + Text { + text: { + var match = resource.match(/\/([^/]*)$/); + return match ? match[1] : resource; + } + font.pointSize: 12 + Layout.preferredWidth: root.width * .6 + horizontalAlignment: Text.AlignBottom + } + + Text { + text: assetType + font.pointSize: 10 + Layout.preferredWidth: root.width * .2 + horizontalAlignment: Text.AlignBottom + } + Repeater { model: [ { "name": "forward", "glyph": hifi.glyphs.forward, "size": 30 }, @@ -86,6 +126,7 @@ Rectangle { } } } + headerPositioning: ListView.OverlayHeader header: HifiStylesUit.RalewayRegular { id: rootHeader @@ -97,19 +138,27 @@ Rectangle { anchors.left: parent.left anchors.leftMargin: 12 } + footerPositioning: ListView.OverlayFooter footer: Row { id: rootActions spacing: 20 anchors.horizontalCenter: parent.horizontalCenter + property string currentAction - function assetType(resource) { - return (resource.match(/\.app\.json$/) ? "application" : - resource.match(/\.(?:fbx|fst)$/) ? "avatar" : - resource.match(/\.json\.gz$/) ? "content set" : - resource.match(/\.json$/) ? "entity or wearable" : - "unknown") + property var actions: { + "Load File": function(){ + rootActions.currentAction = "load file"; + Window.browseChanged.connect(onResourceSelected); + Window.browseAsync("Please select a file", "", "Assets (*.app.json *.json *.fbx *.json.gz)"); + }, + "Load URL": function(){ + rootActions.currentAction = "load url"; + Window.promptTextChanged.connect(onResourceSelected); + Window.promptAsync("Please enter a URL", ""); + } } + function onResourceSelected(resource) { // It is possible that we received the present signal // from something other than our browserAsync window. @@ -124,23 +173,12 @@ Rectangle { Window.promptTextChanged.disconnect(onResourceSelected); } if (resource) { - listModel.append( { - "resource": resource.trim(), - "assetType": assetType(resource.trim()) } ); - } - } - property var actions: { - "Load File": function(){ - rootActions.currentAction = "load file"; - Window.browseChanged.connect(onResourceSelected); - Window.browseAsync("Please select a file", "", "Assets (*.app.json *.json *.fbx *.json.gz)"); - }, - "Load URL": function(){ - rootActions.currentAction = "load url"; - Window.promptTextChanged.connect(onResourceSelected); - Window.promptAsync("Please enter a URL", ""); + var resourceObj = buildResourceObj(resource); + installResourceObj(resourceObj); + resourceListModel.append(resourceObj); } } + Repeater { model: [ "Load File", "Load URL" ] HifiControlsUit.Button { From d48791465627801813b4cc67ca71bbdaef769b23 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Sep 2018 11:28:59 -0700 Subject: [PATCH 106/259] Prevent text notification from appearing when not necessary --- scripts/system/pal.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 1be5b44786..85898c28fb 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -874,16 +874,18 @@ function notificationPollCallback(connectionsArray) { shouldShowDot: shouldShowDot }); - var message; - if (!ui.notificationInitialCallbackMade) { - message = newlyOnlineConnectionsArray.length + " of your connections " + - (newlyOnlineConnectionsArray.length === 1 ? "is" : "are") + " online. Open PEOPLE to join them!"; - ui.notificationDisplayBanner(message); - } else { - for (i = 0; i < newlyOnlineConnectionsArray.length; i++) { - message = newlyOnlineConnectionsArray[i].username + " is available in " + - newlyOnlineConnectionsArray[i].location.root.name + ". Open PEOPLE to join them!"; + if (newlyOnlineConnectionsArray.length > 0) { + var message; + if (!ui.notificationInitialCallbackMade) { + message = newlyOnlineConnectionsArray.length + " of your connections " + + (newlyOnlineConnectionsArray.length === 1 ? "is" : "are") + " online. Open PEOPLE to join them!"; ui.notificationDisplayBanner(message); + } else { + for (i = 0; i < newlyOnlineConnectionsArray.length; i++) { + message = newlyOnlineConnectionsArray[i].username + " is available in " + + newlyOnlineConnectionsArray[i].location.root.name + ". Open PEOPLE to join them!"; + ui.notificationDisplayBanner(message); + } } } } From a97f9eb79aeccf423517c099d12f3c4daf319cb8 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Sep 2018 12:28:03 -0700 Subject: [PATCH 107/259] Logic for Wallet notifications; some bugfixes --- scripts/modules/appUi.js | 18 +++-- scripts/modules/request.js | 4 +- scripts/system/commerce/wallet.js | 106 +++++++++++++++++++++++++++++- scripts/system/pal.js | 22 ++++--- 4 files changed, 131 insertions(+), 19 deletions(-) diff --git a/scripts/modules/appUi.js b/scripts/modules/appUi.js index dab377911b..98f6dad1f9 100644 --- a/scripts/modules/appUi.js +++ b/scripts/modules/appUi.js @@ -155,8 +155,8 @@ function AppUi(properties) { return; } - // User is "appearing offline" - if (GlobalServices.findableBy === "none") { + // User is "appearing offline", or is offline, or the app is open + if (GlobalServices.findableBy === "none" || Account.username === "" || that.isOpen) { that.notificationPollTimeout = Script.setTimeout(that.notificationPoll, that.notificationPollTimeoutMs); return; } @@ -164,7 +164,10 @@ function AppUi(properties) { var url = METAVERSE_BASE + that.notificationPollEndpoint; if (that.notificationPollCaresAboutSince) { - url = url + "&since=" + (new Date().getTime()); + var settingsKey = "notifications/" + that.buttonName + "/lastSince"; + var timestamp = Settings.getValue(settingsKey, new Date().getTime()); + url = url + "&since=" + timestamp; + Settings.setValue(settingsKey, timestamp); } console.debug(that.buttonName, 'polling for notifications at endpoint', url); @@ -203,7 +206,8 @@ function AppUi(properties) { // This won't do anything if there isn't a notification endpoint set that.notificationPoll(); - function availabilityChanged() { + function restartNotificationPoll() { + that.notificationInitialCallbackMade = false; if (that.notificationPollTimeout) { Script.clearTimeout(that.notificationPollTimeout); that.notificationPollTimeout = false; @@ -303,7 +307,8 @@ function AppUi(properties) { } : that.ignore; that.onScriptEnding = function onScriptEnding() { // Close if necessary, clean up any remaining handlers, and remove the button. - GlobalServices.findableByChanged.disconnect(availabilityChanged); + GlobalServices.myUsernameChanged.disconnect(restartNotificationPoll); + GlobalServices.findableByChanged.disconnect(restartNotificationPoll); if (that.isOpen) { that.close(); } @@ -323,6 +328,7 @@ function AppUi(properties) { that.tablet.screenChanged.connect(that.onScreenChanged); that.button.clicked.connect(that.onClicked); Script.scriptEnding.connect(that.onScriptEnding); - GlobalServices.findableByChanged.connect(availabilityChanged); + GlobalServices.findableByChanged.connect(restartNotificationPoll); + GlobalServices.myUsernameChanged.connect(restartNotificationPoll); } module.exports = AppUi; diff --git a/scripts/modules/request.js b/scripts/modules/request.js index 3516554567..d0037f9b43 100644 --- a/scripts/modules/request.js +++ b/scripts/modules/request.js @@ -19,7 +19,7 @@ module.exports = { // ------------------------------------------------------------------ request: function (options, callback) { // cb(error, responseOfCorrectContentType) of url. A subset of npm request. - var httpRequest = new XMLHttpRequest(), key; + var httpRequest = new XMLHttpRequest(), key; // QT bug: apparently doesn't handle onload. Workaround using readyState. httpRequest.onreadystatechange = function () { var READY_STATE_DONE = 4; @@ -72,7 +72,7 @@ module.exports = { } httpRequest.open(options.method, options.uri, true); httpRequest.send(options.body || null); - } + } }; // =========================================================================================== diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 993ea30c2e..26b8f95bd5 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -491,12 +491,110 @@ function walletOpened() { Controller.mouseMoveEvent.connect(handleMouseMoveEvent); triggerMapping.enable(); triggerPressMapping.enable(); + ui.messagesWaiting(false); } function walletClosed() { off(); } +function notificationDataProcessPage(data) { + return data.data.history; +} + +var shouldShowDot = false; +function notificationPollCallback(historyArray) { + var i; + var someoneElsePurchasedArray = []; + var proofIssuedArray = []; + var moneyReceivedArray = []; + var giftReceivedArray = []; + for (i = 0; i < historyArray.length; i++) { + var currentHistoryTxn = historyArray[i]; + + if (currentHistoryTxn.sent_certs <= 0 && + currentHistoryTxn.received_certs <= 0) { + // This is an HFC transfer. + if (currentHistoryTxn.received_money > 0) { + if (currentHistoryTxn.sender_name === "marketplace") { + someoneElsePurchasedArray.push(currentHistoryTxn); + } else { + moneyReceivedArray.push(currentHistoryTxn); + } + } + } else if (currentHistoryTxn.sent_money <= 0 && + currentHistoryTxn.received_money <= 0 && + currentHistoryTxn.received_certs > 0) { + // This is a non-HFC asset transfer. + if (currentHistoryTxn.sender_name === "marketplace") { + proofIssuedArray.push(currentHistoryTxn); + } else { + giftReceivedArray.push(currentHistoryTxn); + } + } + } + + if (!ui.isOpen) { + shouldShowDot = shouldShowDot || + (someoneElsePurchasedArray.length > 0) || + (proofIssuedArray.length > 0) || + (moneyReceivedArray.length > 0) || + (giftReceivedArray.length > 0); + ui.messagesWaiting(shouldShowDot); + + var notificationCount = someoneElsePurchasedArray.length + + proofIssuedArray.length + + moneyReceivedArray.length + + giftReceivedArray.length; + + if (notificationCount > 0) { + var message; + if (!ui.notificationInitialCallbackMade) { + message = "You have " + notificationCount + " unread wallet " + + "notification" + (notificationCount === 1 ? "" : "s") + "! Open WALLET to see all activity."; + ui.notificationDisplayBanner(message); + } else { + var currentItemName, senderName; + for (i = 0; i < someoneElsePurchasedArray.length; i++) { + currentItemName = (someoneElsePurchasedArray[i].message).match('
(.*)')[1]; + message = "Someone purchased your item \"" + currentItemName + "\" from the Marketplace! " + + "Open WALLET to see all activity."; + ui.notificationDisplayBanner(message); + } + for (i = 0; i < proofIssuedArray.length; i++) { + currentItemName = (proofIssuedArray[i].message).match('
(.*)')[1]; + message = "You have been issued a proof for your Marketplace item \"" + currentItemName + "\"! " + + "Open WALLET to see all activity."; + ui.notificationDisplayBanner(message); + } + for (i = 0; i < moneyReceivedArray.length; i++) { + senderName = moneyReceivedArray[i].sender_name; + if (senderName === "") { + senderName = "Someone"; + } + message = senderName + " sent you " + moneyReceivedArray[i].received_money + " HFC! " + + "Open WALLET to see all activity."; + ui.notificationDisplayBanner(message); + } + for (i = 0; i < giftReceivedArray.length; i++) { + senderName = giftReceivedArray[i].sender_name; + if (senderName === "") { + senderName = "Someone"; + } + message = senderName + " sent you a gift! " + + "Open WALLET to see all activity."; + ui.notificationDisplayBanner(message); + } + } + } + } +} + +function isReturnedDataEmpty(data) { + var historyArray = data.data.history; + return historyArray.length === 0; +} + // // Manage the connection between the button and the window. // @@ -510,7 +608,13 @@ function startup() { home: WALLET_QML_SOURCE, onOpened: walletOpened, onClosed: walletClosed, - onMessage: fromQml + onMessage: fromQml, + notificationPollEndpoint: "/api/v1/notifications?source=commerce-history&per_page=10", + notificationPollTimeoutMs: 60000, + notificationDataProcessPage: notificationDataProcessPage, + notificationPollCallback: notificationPollCallback, + notificationPollStopPaginatingConditionMet: isReturnedDataEmpty, + notificationPollCaresAboutSince: true }); GlobalServices.myUsernameChanged.connect(onUsernameChanged); } diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 1be5b44786..38359fbab9 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -874,16 +874,18 @@ function notificationPollCallback(connectionsArray) { shouldShowDot: shouldShowDot }); - var message; - if (!ui.notificationInitialCallbackMade) { - message = newlyOnlineConnectionsArray.length + " of your connections " + - (newlyOnlineConnectionsArray.length === 1 ? "is" : "are") + " online. Open PEOPLE to join them!"; - ui.notificationDisplayBanner(message); - } else { - for (i = 0; i < newlyOnlineConnectionsArray.length; i++) { - message = newlyOnlineConnectionsArray[i].username + " is available in " + - newlyOnlineConnectionsArray[i].location.root.name + ". Open PEOPLE to join them!"; + if (newlyOnlineConnectionsArray.length > 0) { + var message; + if (!ui.notificationInitialCallbackMade) { + message = newlyOnlineConnectionsArray.length + " of your connections " + + (newlyOnlineConnectionsArray.length === 1 ? "is" : "are") + " online! Open PEOPLE to join them."; ui.notificationDisplayBanner(message); + } else { + for (i = 0; i < newlyOnlineConnectionsArray.length; i++) { + message = newlyOnlineConnectionsArray[i].username + " is available in " + + newlyOnlineConnectionsArray[i].location.root.name + "! Open PEOPLE to join them."; + ui.notificationDisplayBanner(message); + } } } } @@ -902,7 +904,7 @@ function startup() { onOpened: palOpened, onClosed: off, onMessage: fromQml, - notificationPollEndpoint: "/api/v1/users?filter=connections&per_page=10", + notificationPollEndpoint: "/api/v1/notifications?source=users&filter=connections&per_page=10", notificationPollTimeoutMs: 60000, notificationDataProcessPage: notificationDataProcessPage, notificationPollCallback: notificationPollCallback, From 645ad2bb7a3493a73f5aace63a49b8d03b605759 Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Tue, 18 Sep 2018 12:36:03 -0700 Subject: [PATCH 108/259] Enable marketplace item test for avatar url --- .../marketplaceItemTester/MarketplaceItemTester.qml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index b36deb8b70..fed8480239 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -76,8 +76,12 @@ Rectangle { property var actions: { "forward": function(resource, assetType){ - if ("application" == assetType) { - Commerce.openApp(resource); + switch(assetType) { + case "application": + Commerce.openApp(resource); + break + case "avatar": + MyAvatar.useFullAvatarURL(resource); } // XXX support other resource types here. }, From 04b44d05942d3247ccf5f6eaf523432ec64e7e29 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 18 Sep 2018 12:38:33 -0700 Subject: [PATCH 109/259] Fix small since bug --- scripts/modules/appUi.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/modules/appUi.js b/scripts/modules/appUi.js index 98f6dad1f9..b8e5cc45f9 100644 --- a/scripts/modules/appUi.js +++ b/scripts/modules/appUi.js @@ -165,9 +165,10 @@ function AppUi(properties) { if (that.notificationPollCaresAboutSince) { var settingsKey = "notifications/" + that.buttonName + "/lastSince"; - var timestamp = Settings.getValue(settingsKey, new Date().getTime()); - url = url + "&since=" + timestamp; - Settings.setValue(settingsKey, timestamp); + var currentTimestamp = new Date().getTime(); + var settingsTimestamp = Settings.getValue(settingsKey, currentTimestamp); + url = url + "&since=" + settingsTimestamp; + Settings.setValue(settingsKey, currentTimestamp); } console.debug(that.buttonName, 'polling for notifications at endpoint', url); From 96e8b060eb60d55de2f79ae79e89ae878b59f0cb Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Tue, 18 Sep 2018 12:42:37 -0700 Subject: [PATCH 110/259] Enable transit animations --- interface/src/avatar/AvatarManager.cpp | 88 ++++++++++++++----- interface/src/avatar/AvatarManager.h | 17 +--- .../src/avatars-renderer/Avatar.cpp | 57 ++++++++---- .../src/avatars-renderer/Avatar.h | 46 ++++++++-- 4 files changed, 152 insertions(+), 56 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index cea0ff0f4d..edf9469670 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -79,13 +79,21 @@ AvatarManager::AvatarManager(QObject* parent) : } }); - const float AVATAR_TRANSIT_MAX_DISTANCE = 1.0f; + const float AVATAR_TRANSIT_TRIGGER_DISTANCE = 1.0f; const int AVATAR_TRANSIT_FRAME_COUNT = 11; // Based on testing const int AVATAR_TRANSIT_FRAMES_PER_METER = 3; // Based on testing - _avatarTransitMaxDistance = AVATAR_TRANSIT_MAX_DISTANCE; - _avatarTransitFrameCount = AVATAR_TRANSIT_FRAME_COUNT; - _avatarTransitFramesPerMeter = AVATAR_TRANSIT_FRAMES_PER_METER; + const QString START_ANIMATION_URL = "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx"; + const QString MIDDLE_ANIMATION_URL = "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx"; + const QString END_ANIMATION_URL = "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx"; + + _transitConfig._totalFrames = AVATAR_TRANSIT_FRAME_COUNT; + _transitConfig._triggerDistance = AVATAR_TRANSIT_TRIGGER_DISTANCE; + _transitConfig._framesPerMeter = AVATAR_TRANSIT_FRAMES_PER_METER; + _transitConfig._isDistanceBased = true; + _transitConfig._startTransitAnimation = AvatarTransit::TransitAnimation(START_ANIMATION_URL, 30, 0, 10); + _transitConfig._middleTransitAnimation = AvatarTransit::TransitAnimation(MIDDLE_ANIMATION_URL, 30, 11, 0); + _transitConfig._endTransitAnimation = AvatarTransit::TransitAnimation(END_ANIMATION_URL, 30, 12, 38); } AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { @@ -133,26 +141,37 @@ void AvatarManager::setSpace(workload::SpacePointer& space ) { _space = space; } +void AvatarManager::playTransitAnimations(AvatarTransit::Status status) { + auto startAnimation = _transitConfig._startTransitAnimation; + auto middleAnimation = _transitConfig._middleTransitAnimation; + auto endAnimation = _transitConfig._endTransitAnimation; + + switch (status) { + case AvatarTransit::Status::START_FRAME: + _myAvatar->overrideAnimation(startAnimation._animationUrl, startAnimation._fps, false, startAnimation._firstFrame, startAnimation._firstFrame + startAnimation._frameCount); + break; + case AvatarTransit::Status::START_TRANSIT: + _myAvatar->overrideAnimation(middleAnimation._animationUrl, middleAnimation._fps, false, middleAnimation._firstFrame, middleAnimation._firstFrame + middleAnimation._frameCount); + break; + case AvatarTransit::Status::END_TRANSIT: + _myAvatar->overrideAnimation(endAnimation._animationUrl, endAnimation._fps, false, endAnimation._firstFrame, endAnimation._firstFrame + endAnimation._frameCount); + break; + case AvatarTransit::Status::END_FRAME: + _myAvatar->restoreAnimation(); + break; + } +} + void AvatarManager::updateMyAvatar(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "AvatarManager::updateMyAvatar()"); - /* - std::shared_ptr transit = _myAvatar->getTransit(); - bool initTransit = false; - if (!transit->isTransiting()) { - initTransit = transit->update(_myAvatar->getWorldPosition(), _avatarTransitFrameCount, _avatarTransitFramesPerMeter, _avatarTransitDistanceBased, _avatarTransitMaxDistance); - if (initTransit) { - _myAvatar->getSkeletonModel()->getRig().restoreAnimation(); - _myAvatar->getSkeletonModel()->getRig().overrideAnimation("https://hifi-content.s3.amazonaws.com/luis/test_scripts/transit_app/animations/teleport01_warp.fbx", 30, false, 0, 49); - } - } - if (transit->isTransiting()){ - glm::vec3 nextPosition; - if (!transit->getNextPosition(nextPosition)) { - _myAvatar->getSkeletonModel()->getRig().restoreAnimation(); - } + + AvatarTransit::Status status = _myAvatar->updateTransit(_myAvatar->getWorldPosition(), _transitConfig); + + if (_transitConfig._playAnimation) { + playTransitAnimations(status); } - */ + _myAvatar->update(deltaTime); render::Transaction transaction; _myAvatar->updateRenderItem(transaction); @@ -276,7 +295,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { } // smooth other avatars positions { - avatar->_transit.update(avatar->_globalPosition, _avatarTransitFrameCount, _avatarTransitFramesPerMeter, _avatarTransitDistanceBased, _avatarTransitMaxDistance); + avatar->_transit.update(avatar->_globalPosition, _transitConfig); } avatar->simulate(deltaTime, inView); @@ -866,3 +885,30 @@ void AvatarManager::setAvatarSortCoefficient(const QString& name, const QScriptV doc.insert("data", palData); return doc.toVariantMap(); } + + QVariantMap AvatarManager::getAvatarTransitData() { + QVariantMap result; + result["frameCount"] = _transitConfig._totalFrames; + result["framesPerMeter"] = _transitConfig._framesPerMeter; + result["isDistanceBased"] = _transitConfig._isDistanceBased; + result["triggerDistance"] = _transitConfig._triggerDistance; + result["playAnimation"] = _transitConfig._playAnimation; + return result; +} + void AvatarManager::setAvatarTransitData(const QVariantMap& data) { + if (data.contains("frameCount")) { + _transitConfig._totalFrames = data["frameCount"].toInt(); + } + if (data.contains("framesPerMeter")) { + _transitConfig._framesPerMeter = data["framesPerMeter"].toInt(); + } + if (data.contains("isDistanceBased")) { + _transitConfig._isDistanceBased = data["isDistanceBased"].toBool(); + } + if (data.contains("triggerDistance")) { + _transitConfig._triggerDistance = data["triggerDistance"].toDouble(); + } + if (data.contains("playAnimation")) { + _transitConfig._playAnimation = data["playAnimation"].toBool(); + } +} \ No newline at end of file diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 3fc675f9b8..e5936154a4 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -178,14 +178,8 @@ public: */ Q_INVOKABLE QVariantMap getPalData(const QList specificAvatarIdentifiers = QList()); - Q_INVOKABLE void setAvatarTransitDistanceBased(bool isDistanceBased) { _avatarTransitDistanceBased = isDistanceBased; } - Q_INVOKABLE void setAvatarTransitMaxDistance(float maxDistance) { _avatarTransitMaxDistance = maxDistance; } - Q_INVOKABLE void setAvatarTransitFrameCount(int frameCount) { _avatarTransitFrameCount = frameCount; } - Q_INVOKABLE void setAvatarTransitFramesPerMeter(int frameCount) { _avatarTransitFramesPerMeter = frameCount; } - Q_INVOKABLE bool getAvatarTransitDistanceBased() { return _avatarTransitDistanceBased; } - Q_INVOKABLE float getAvatarTransitMaxDistance() { return _avatarTransitMaxDistance; } - Q_INVOKABLE int getAvatarTransitFrameCount() { return _avatarTransitFrameCount; } - Q_INVOKABLE int getAvatarTransitFramesPerMeter() { return _avatarTransitFramesPerMeter; } + Q_INVOKABLE QVariantMap getAvatarTransitData(); + Q_INVOKABLE void setAvatarTransitData(const QVariantMap& data); float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); } int getIdentityRequestsSent() const { return _identityRequestsSent; } @@ -212,6 +206,7 @@ private: AvatarSharedPointer newSharedAvatar() override; void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override; + void playTransitAnimations(AvatarTransit::Status status); QVector _avatarsToFade; QVector _avatarsToCopy; @@ -237,11 +232,7 @@ private: // Other avatars smooth transit global configuration - bool _avatarTransitDistanceBased { true }; - float _avatarTransitMaxDistance; - int _avatarTransitFrameCount; - int _avatarTransitFramesPerMeter; - + AvatarTransit::TransitConfig _transitConfig; }; #endif // hifi_AvatarManager_h diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 05d49a9ec0..a3c3286793 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -113,29 +113,27 @@ void Avatar::setShowNamesAboveHeads(bool show) { showNamesAboveHeads = show; } -bool AvatarTransit::update(const glm::vec3& avatarPosition, int totalFrames, int framesPerMeter, bool isDistanceBased, float maxDistance) { - bool starting = false; +AvatarTransit::Status AvatarTransit::update(const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config) { glm::vec3 currentPosition = _isTransiting ? _currentPosition : avatarPosition; float oneFrameDistance = glm::length(currentPosition - _lastPosition); - if (oneFrameDistance > maxDistance && !_isTransiting) { - start(_lastPosition, currentPosition, totalFrames, framesPerMeter, isDistanceBased); - starting = true; - } - updatePosition(avatarPosition); - return starting; + if (oneFrameDistance > config._triggerDistance && !_isTransiting) { + start(_lastPosition, currentPosition, config); + } + return updatePosition(avatarPosition); } -void AvatarTransit::start(const glm::vec3& startPosition, const glm::vec3& endPosition, int totalFrames, int framesPerMeter, bool isDistanceBased) { +void AvatarTransit::start(const glm::vec3& startPosition, const glm::vec3& endPosition, const AvatarTransit::TransitConfig& config) { _startPosition = startPosition; _endPosition = endPosition; + _framesBefore = config._startTransitAnimation._frameCount; + _framesAfter = config._endTransitAnimation._frameCount; _step = 0; - if (!isDistanceBased) { - calculateSteps(totalFrames); + if (!config._isDistanceBased) { + calculateSteps(config._totalFrames); } else { float distance = glm::length(_endPosition - _startPosition); - calculateSteps(framesPerMeter * distance); + calculateSteps(config._framesPerMeter * distance); } - _isTransiting = true; } @@ -145,22 +143,42 @@ void AvatarTransit::calculateSteps(int stepCount) { glm::vec3 transitLine = _endPosition - startPosition; glm::vec3 direction = glm::normalize(transitLine); glm::vec3 stepVector = (glm::length(transitLine) / stepCount) * direction; - for (auto i = 0; i < stepCount; i++) { - glm::vec3 localStep = _transitSteps.size() > 0 ? _transitSteps[i-1] + stepVector : _startPosition + stepVector; - _transitSteps.push_back(localStep); + int totalSteps = stepCount + _framesBefore + _framesAfter; + for (auto i = 0; i < totalSteps; i++) { + if (i < _framesBefore) { + _transitSteps.push_back(_startPosition); + } else if (i >= stepCount + _framesBefore) { + _transitSteps.push_back(_endPosition); + } else { + glm::vec3 localStep = _transitSteps.size() > _framesBefore ? _transitSteps[i - 1] + stepVector : _startPosition + stepVector; + _transitSteps.push_back(localStep); + } } } -void AvatarTransit::updatePosition(const glm::vec3& avatarPosition) { +AvatarTransit::Status AvatarTransit::updatePosition(const glm::vec3& avatarPosition) { + Status status = Status::IDLE; _lastPosition = _isTransiting ? _currentPosition : avatarPosition; if (_isTransiting) { int lastIdx = (int)_transitSteps.size() - 1; _isTransiting = _step < lastIdx; if (_isTransiting) { + if (_step == 0) { + status = Status::START_FRAME; + qDebug() << "Transit starting"; + } else if (_step == _framesBefore - 1) { + status = Status::START_TRANSIT; + } else if (_step == (int)_transitSteps.size() - _framesAfter) { + status = Status::END_TRANSIT; + } _step++; _currentPosition = _transitSteps[_step]; + } else { + status = Status::END_FRAME; + qDebug() << "Transit ending"; } } + return status; } bool AvatarTransit::getNextPosition(glm::vec3& nextPosition) { @@ -1936,6 +1954,11 @@ float Avatar::getUnscaledEyeHeightFromSkeleton() const { } } +AvatarTransit::Status Avatar::updateTransit(const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config) { + std::lock_guard lock(_transitLock); + return _transit.update(avatarPosition, config); +} + void Avatar::addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) { std::lock_guard lock(_materialsLock); _materials[parentMaterialName].push(material); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 22163635ff..b955706861 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -52,21 +52,55 @@ class Texture; class AvatarTransit { public: + enum Status { + IDLE = 0, + START_FRAME, + START_TRANSIT, + TRANSITING, + END_TRANSIT, + END_FRAME + }; + + struct TransitAnimation { + TransitAnimation() {}; + TransitAnimation(const QString& animationUrl, int fps, int firstFrame, int frameCount) : + _firstFrame(firstFrame), _frameCount(frameCount), _animationUrl(animationUrl), _fps(fps) {}; + int _firstFrame; + int _frameCount; + QString _animationUrl; + int _fps; + }; + + struct TransitConfig { + TransitConfig() {}; + int _totalFrames { 0 }; + int _framesPerMeter { 0 }; + bool _isDistanceBased { false }; + float _triggerDistance { 0 }; + bool _playAnimation { false }; + TransitAnimation _startTransitAnimation; + TransitAnimation _middleTransitAnimation; + TransitAnimation _endTransitAnimation; + }; + + AvatarTransit() {}; - bool update(const glm::vec3& avatarPosition, int totalFrames, int framesPerMeter, bool isDistanceBased, float maxDistance); - void start(const glm::vec3& startPosition, const glm::vec3& endPosition, int totalFrames, int framesPerMeter, bool isDistanceBased); - bool getNextPosition(glm::vec3& nextPosition); + Status update(const glm::vec3& avatarPosition, const TransitConfig& config); bool isTransiting() { return _isTransiting; }; glm::vec3 getCurrentPosition() { return _currentPosition; }; + bool getNextPosition(glm::vec3& nextPosition); int getCurrentStep() { return _step; }; private: void calculateSteps(int stepCount); - void updatePosition(const glm::vec3& avatarPosition); + Status updatePosition(const glm::vec3& avatarPosition); + void start(const glm::vec3& startPosition, const glm::vec3& endPosition, const TransitConfig& config); bool _isTransiting{ false }; glm::vec3 _startPosition; glm::vec3 _endPosition; glm::vec3 _currentPosition; + int _framesBefore { 0 }; + int _framesAfter { 0 }; std::vector _transitSteps; glm::vec3 _lastPosition; int _step { 0 }; @@ -385,6 +419,7 @@ public: std::shared_ptr getTransit() { return std::make_shared(_transit); }; + AvatarTransit::Status updateTransit(const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config); signals: void targetScaleChanged(float targetScale); @@ -520,6 +555,7 @@ protected: RateCounter<> _skeletonModelSimulationRate; RateCounter<> _jointDataSimulationRate; + protected: class AvatarEntityDataHash { public: @@ -544,7 +580,7 @@ protected: float _modelScale { 1.0f }; AvatarTransit _transit; - + std::mutex _transitLock; static int _jointConesID; From a592565b7b0ee6c941cb6e6de561d0bbd1bbe160 Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Tue, 18 Sep 2018 13:25:01 -0700 Subject: [PATCH 111/259] Enable marketplace item test for avatar file --- .../commerce/marketplaceItemTester/MarketplaceItemTester.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index fed8480239..4ef6109752 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -32,7 +32,7 @@ Rectangle { function buildResourceObj(resource) { resource = resource.trim(); var assetType = (resource.match(/\.app\.json$/) ? "application" : - resource.match(/\.(?:fbx|fst)$/) ? "avatar" : + resource.match(/\.fst$/) ? "avatar" : resource.match(/\.json\.gz$/) ? "content set" : resource.match(/\.json$/) ? "entity or wearable" : "unknown"); @@ -154,7 +154,7 @@ Rectangle { "Load File": function(){ rootActions.currentAction = "load file"; Window.browseChanged.connect(onResourceSelected); - Window.browseAsync("Please select a file", "", "Assets (*.app.json *.json *.fbx *.json.gz)"); + Window.browseAsync("Please select a file (*.app.json *.json *.fst *.json.gz)", "", "Assets (*.app.json *.json *.fst *.json.gz)"); }, "Load URL": function(){ rootActions.currentAction = "load url"; From cfa9d185f02dfd764abb7cf2a7473ebaa097f5c0 Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Tue, 18 Sep 2018 14:02:16 -0700 Subject: [PATCH 112/259] Add full resource paths to list in marketplace item tester --- .../MarketplaceItemTester.qml | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index 4ef6109752..8b0ecf3436 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -94,21 +94,32 @@ Rectangle { } } - Text { - text: { - var match = resource.match(/\/([^/]*)$/); - return match ? match[1] : resource; - } - font.pointSize: 12 + Column { Layout.preferredWidth: root.width * .6 - horizontalAlignment: Text.AlignBottom + spacing: 5 + Text { + text: { + var match = resource.match(/\/([^/]*)$/); + return match ? match[1] : resource; + } + font.pointSize: 12 + horizontalAlignment: Text.AlignBottom + } + Text { + text: resource + font.pointSize: 8 + width: root.width * .6 + horizontalAlignment: Text.AlignBottom + wrapMode: Text.WrapAnywhere + } } Text { text: assetType font.pointSize: 10 Layout.preferredWidth: root.width * .2 - horizontalAlignment: Text.AlignBottom + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Test.AlignVCenter } Repeater { From 32da2cbef6cdf1a12bb462cb2f27a44cda60eefd Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Tue, 18 Sep 2018 18:11:11 -0300 Subject: [PATCH 113/259] Show password in android login --- android/app/build.gradle | 4 ++ .../hifiinterface/fragment/LoginFragment.java | 42 ------------------- .../src/main/res/drawable/ic_eye_noshow.xml | 27 ++++++++++++ .../app/src/main/res/drawable/ic_eye_show.xml | 15 +++++++ .../res/drawable/selector_show_password.xml | 5 +++ .../src/main/res/layout/fragment_login.xml | 41 +++++++++++------- android/app/src/main/res/values/colors.xml | 1 + android/app/src/main/res/values/strings.xml | 4 +- 8 files changed, 81 insertions(+), 58 deletions(-) create mode 100644 android/app/src/main/res/drawable/ic_eye_noshow.xml create mode 100644 android/app/src/main/res/drawable/ic_eye_show.xml create mode 100644 android/app/src/main/res/drawable/selector_show_password.xml diff --git a/android/app/build.gradle b/android/app/build.gradle index d3463411b8..24c067b176 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -133,6 +133,10 @@ dependencies { implementation 'com.android.support.constraint:constraint-layout:1.0.2' implementation 'com.android.support:design:26.1.0' + compile 'com.android.support:support-v4:26.1.0' + compile 'com.android.support:appcompat-v7:26.1.0' + compile 'com.android.support:support-vector-drawable:26.1.0' + implementation 'com.android.support:appcompat-v7:26.1.0' compile 'com.android.support:recyclerview-v7:26.1.0' compile 'com.android.support:cardview-v7:26.1.0' diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/LoginFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/LoginFragment.java index 1a030baabf..92cdec19a1 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/LoginFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/LoginFragment.java @@ -8,9 +8,6 @@ import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.text.Editable; -import android.text.TextWatcher; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -62,44 +59,6 @@ public class LoginFragment extends Fragment { mLoginButton = rootView.findViewById(R.id.loginButton); mForgotPassword = rootView.findViewById(R.id.forgotPassword); - mUsername.addTextChangedListener(new TextWatcher() { - boolean ignoreNextChange = false; - boolean hadBlankSpace = false; - @Override - public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) { - hadBlankSpace = charSequence.length() > 0 && charSequence.charAt(charSequence.length()-1) == ' '; - } - - @Override - public void onTextChanged(CharSequence charSequence, int start, int count, int after) { - - } - - @Override - public void afterTextChanged(Editable editable) { - if (!ignoreNextChange) { - ignoreNextChange = true; - boolean spaceFound = false; - for (int i = 0; i < editable.length(); i++) { - if (editable.charAt(i) == ' ') { - spaceFound=true; - editable.delete(i, i + 1); - i--; - } - } - - if (hadBlankSpace && !spaceFound && editable.length() > 0) { - editable.delete(editable.length()-1, editable.length()); - } - - editable.append(' '); - ignoreNextChange = false; - } - - } - }); - - mLoginButton.setOnClickListener(view -> login()); mForgotPassword.setOnClickListener(view -> forgotPassword()); @@ -208,7 +167,6 @@ public class LoginFragment extends Fragment { } public void handleLoginCompleted(boolean success) { - Log.d("[LOGIN]", "handleLoginCompleted " + success); getActivity().runOnUiThread(() -> { mLoginButton.setEnabled(true); cancelActivityIndicator(); diff --git a/android/app/src/main/res/drawable/ic_eye_noshow.xml b/android/app/src/main/res/drawable/ic_eye_noshow.xml new file mode 100644 index 0000000000..1d5304afac --- /dev/null +++ b/android/app/src/main/res/drawable/ic_eye_noshow.xml @@ -0,0 +1,27 @@ + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/drawable/ic_eye_show.xml b/android/app/src/main/res/drawable/ic_eye_show.xml new file mode 100644 index 0000000000..273ecc8339 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_eye_show.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/android/app/src/main/res/drawable/selector_show_password.xml b/android/app/src/main/res/drawable/selector_show_password.xml new file mode 100644 index 0000000000..a44092aceb --- /dev/null +++ b/android/app/src/main/res/drawable/selector_show_password.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/layout/fragment_login.xml b/android/app/src/main/res/layout/fragment_login.xml index c50e6c1380..46ed783898 100644 --- a/android/app/src/main/res/layout/fragment_login.xml +++ b/android/app/src/main/res/layout/fragment_login.xml @@ -41,38 +41,51 @@ android:paddingTop="14dp" android:ems="10" android:fontFamily="@font/raleway" - android:textSize="14sp" + android:textSize="17sp" android:inputType="textEmailAddress" android:textStyle="italic" android:textColor="@color/editTextColor" android:textColorHint="@color/editTextColor" - android:gravity="right|center_vertical" + android:gravity="left|center_vertical" app:layout_constraintTop_toBottomOf="@id/header" android:layout_marginTop="70dp" android:hint="@string/username_or_email" /> - + + android:inputType="textPassword" /> +

97!MJYQl?Y=q*4YsHhGAd^V8V3f5 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%0DpIuU8KG@@UbK& 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.|9_K=*k&m*FjF(-ie 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

- +
diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 8436e36087..0893108109 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -32,7 +32,6 @@ tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"), DEBUG = false; - // #region Utilities ======================================================================================================= function debug(message) { if (!DEBUG) { @@ -75,9 +74,6 @@ return hand === LEFT_HAND ? RIGHT_HAND : LEFT_HAND; } - // #endregion - - // #region UI ============================================================================================================== UI = function () { @@ -511,9 +507,6 @@ }; - // #endregion - - // #region State Machine =================================================================================================== State = function () { @@ -535,8 +528,6 @@ STATE_MACHINE, miniState = MINI_DISABLED, miniHand, - updateTimer = null, - UPDATE_INTERVAL = 25, // Mini tablet scaling. MINI_SCALE_DURATION = 250, @@ -572,10 +563,7 @@ function enterMiniDisabled() { // Stop updates. - if (updateTimer !== null) { - Script.clearTimeout(updateTimer); - updateTimer = null; - } + Script.update.disconnect(updateState); // Stop monitoring mute changes. Audio.mutedChanged.disconnect(ui.updateMutedStatus); @@ -593,7 +581,7 @@ Audio.mutedChanged.connect(ui.updateMutedStatus); // Start updates. - updateTimer = Script.setTimeout(updateState, UPDATE_INTERVAL); + Script.update.connect(updateState); } function shouldShowMini(hand) { @@ -943,7 +931,6 @@ if (STATE_MACHINE[STATE_STRINGS[miniState]].update) { STATE_MACHINE[STATE_STRINGS[miniState]].update(); } - updateTimer = Script.setTimeout(updateState, UPDATE_INTERVAL); } function create() { @@ -966,7 +953,6 @@ MINI_VISIBLE: MINI_VISIBLE, MINI_EXPANDING: MINI_EXPANDING, TABLET_OPEN: TABLET_OPEN, - updateState: updateState, setState: setState, getState: getState, getHand: getHand, @@ -974,9 +960,6 @@ }; }; - // #endregion - - // #region External Events ================================================================================================= function onMessageReceived(channel, data, senderID, localOnly) { var message, @@ -1021,9 +1004,6 @@ } } - // #endregion - - // #region Set-up and tear-down ============================================================================================ function setUp() { miniState = new State(); @@ -1054,6 +1034,4 @@ setUp(); Script.scriptEnding.connect(tearDown); - // #endregion - }()); From 1088a0f9ae26437af880b702b0b14a93b2985fa5 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 28 Sep 2018 10:19:17 +1200 Subject: [PATCH 233/259] Fix invalid state transition in desktop mode --- scripts/system/miniTablet.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 0893108109..395b2cb71f 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -980,7 +980,7 @@ return; } - if (message.action === "grab" && message.grabbedEntity === HMD.tabletID) { + if (message.action === "grab" && message.grabbedEntity === HMD.tabletID && HMD.active) { // Tablet may have been grabbed after it replaced expanded mini tablet. miniState.setState(miniState.MINI_HIDDEN); } else if (message.action === "grab" && miniState.getState() === miniState.MINI_VISIBLE) { @@ -992,7 +992,9 @@ function onWentAway() { // Mini tablet only available when user is not away. - miniState.setState(miniState.MINI_HIDDEN); + if (HMD.active) { + miniState.setState(miniState.MINI_HIDDEN); + } } function onDisplayModeChanged() { From 4cf5b51124808edfc73d42c3e29010abbd991bb5 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 27 Sep 2018 15:34:16 -0700 Subject: [PATCH 234/259] works, using additional handler from wallet.js --- scripts/system/marketplaces/marketplaces.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 6bba3ce8bc..4cbc8c489a 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -93,10 +93,10 @@ function setupWallet(cta) { openWallet(); var ALLOWANCE_FOR_EVENT_BRIDGE_SETUP = 0; Script.setTimeout(function () { - ui.tablet.sendToQml({ - method: 'updateWalletReferrer', - referrer: cta - }); + ui.tablet.sendToQml({ + method: 'updateWalletReferrer', + referrer: cta + }); }, ALLOWANCE_FOR_EVENT_BRIDGE_SETUP); } @@ -126,7 +126,6 @@ function openMarketplace(optionalItemOrUrl) { // Does that ever happen without the wallet already setup? cta = ''; } - onMarketplaceOpen(cta); ui.open(url, MARKETPLACES_INJECT_SCRIPT_URL); } @@ -714,7 +713,7 @@ function onWebEventReceived(message) { } else if (message.type === "LOGIN") { openLoginWindow(); } else if (message.type === "WALLET_SETUP") { - setupWallet('marketplace cta'); + setupWallet('marketplace cta'); } else if (message.type === "MY_ITEMS") { referrerURL = MARKETPLACE_URL_INITIAL; filterText = ""; @@ -1017,6 +1016,9 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { SEND_ASSET_PARTICLE_TIMER_UPDATE); } break; + case 'goToMarketplaceMainPage': + openMarketplace(); + break; case 'http.request': // Handled elsewhere, don't log. break; @@ -1112,7 +1114,9 @@ var onTabletScreenChanged = function onTabletScreenChanged(type, url) { isWired = true; Wallet.refreshWalletStatus(); } else { - onMarketplaceOpen('marketplace cta'); + if (onMarketplaceScreen) { + onMarketplaceOpen('marketplace cta'); + } ui.tablet.sendToQml({ method: 'inspectionCertificate_resetCert' }); From ad676b14a17334c5424284038be3291197b64d58 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 27 Sep 2018 16:06:55 -0700 Subject: [PATCH 235/259] doh - return with a newline (and no semicolon) does not continue. Don't need extra handler in marketplace any more. --- scripts/modules/appUi.js | 3 +-- scripts/system/marketplaces/marketplaces.js | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/scripts/modules/appUi.js b/scripts/modules/appUi.js index 19208ba7ab..d5ab3f32ba 100644 --- a/scripts/modules/appUi.js +++ b/scripts/modules/appUi.js @@ -42,8 +42,7 @@ function AppUi(properties) { that.additionalAppScreens = []; that.checkIsOpen = function checkIsOpen(type, tabletUrl) { // Are we active? Value used to set isOpen. // Actual url may have prefix or suffix. - return - that.currentVisibleUrl && + return that.currentVisibleUrl && ((that.home.indexOf(that.currentVisibleUrl) > -1) || (that.additionalAppScreens.indexOf(that.currentVisibleUrl) > -1)); }; diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 4cbc8c489a..f36679a4b6 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -1016,9 +1016,6 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { SEND_ASSET_PARTICLE_TIMER_UPDATE); } break; - case 'goToMarketplaceMainPage': - openMarketplace(); - break; case 'http.request': // Handled elsewhere, don't log. break; From d98e7cea4ef0d097d00d7a75eb162566be5083e1 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 28 Sep 2018 11:09:16 +1200 Subject: [PATCH 236/259] Fix not being able to grab tablet with other hand --- scripts/system/controllers/controllerDispatcher.js | 2 +- .../controllers/controllerModules/nearParentGrabOverlay.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 1ce695c87f..d3f37ac62b 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -228,7 +228,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); nearbyOverlays.splice(tabletIndex, 1); } if (miniTabletIndex !== -1 - && (closebyOverlays.indexOf(_this.miniTabletID) === -1) || h !== _this.miniTabletHand) { + && ((closebyOverlays.indexOf(_this.miniTabletID) === -1) || h !== _this.miniTabletHand)) { nearbyOverlays.splice(miniTabletIndex, 1); } } diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js index 2c881aceca..fc5c2f9f79 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -45,7 +45,7 @@ Script.include("/~/system/libraries/utils.js"); this.setMiniTabletID = function (id) { this.miniTabletID = id; - } + }; this.otherHandIsParent = function(props) { return this.getOtherModule().thisHandIsParent(props); From 75de305ec393b87159f6aefa83ca973006416612 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 27 Sep 2018 16:37:07 -0700 Subject: [PATCH 237/259] remove dead code --- interface/resources/qml/hifi/commerce/wallet/Wallet.qml | 3 --- 1 file changed, 3 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index d659d7abdb..588b80c435 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -776,9 +776,6 @@ Rectangle { case 'updateWalletReferrer': walletSetup.referrer = message.referrer; walletChoice.referrer = message.referrer; - if (Commerce.walletStatus !== 1) { // Already set up. Do it now! - followReferrer({ referrer: walletSetup.referrer }); - } // Otherwise we'll followReferrer later. break; case 'inspectionCertificate_resetCert': // NOP From f96c7f4db472ca27c5e137497226231a4e12a156 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 27 Sep 2018 17:05:18 -0700 Subject: [PATCH 238/259] minimal time handshake --- scripts/system/makeUserConnection.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 5dee36d147..d205d368dd 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -32,7 +32,7 @@ var WAITING_INTERVAL = 100; // ms var CONNECTING_INTERVAL = 100; // ms var MAKING_CONNECTION_TIMEOUT = 800; // ms - var CONNECTING_TIME = 1600; // ms + var CONNECTING_TIME = 100; // ms One interval. var PARTICLE_RADIUS = 0.15; // m var PARTICLE_ANGLE_INCREMENT = 360 / 45; // 1hz var HANDSHAKE_SOUND_URL = "https://s3-us-west-1.amazonaws.com/hifi-content/davidkelly/production/audio/4beat_sweep.wav"; From 97d5e8f6145661d8399d482581d7e8df21ea17ce Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 27 Sep 2018 17:11:10 -0700 Subject: [PATCH 239/259] fix for row re-ordering causing no change alone --- .../resources/web/js/base-settings.js | 45 ++++++++++++++----- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/domain-server/resources/web/js/base-settings.js b/domain-server/resources/web/js/base-settings.js index 7a860098d2..fd404aff20 100644 --- a/domain-server/resources/web/js/base-settings.js +++ b/domain-server/resources/web/js/base-settings.js @@ -1110,7 +1110,36 @@ function moveTableRow(row, move_up) { } // we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated - badgeForDifferences($(table)) + badgeForDifferences($(table)); + + // figure out which group this row is in + var panelParentID = row.closest('.panel').attr('id'); + + // get the short name for the setting from the table + var tableShortName = row.closest('table').data('short-name'); + + var changed = tableHasChanged(panelParentID, tableShortName); + $(table).find('.' + Settings.DATA_ROW_CLASS).each(function(){ + var hiddenInput = $(this).find('td.' + Settings.DATA_COL_CLASS + ' input'); + if (changed) { + hiddenInput.attr('data-changed', true); + } else { + hiddenInput.removeAttr('data-changed'); + } + }); + +} + +function tableHasChanged(panelParentID, tableShortName) { + // get a JSON representation of that section + var panelSettingJSON = form2js(panelParentID, ".", false, cleanupFormValues, true)[panelParentID][tableShortName] + if (Settings.initialValues[panelParentID]) { + var initialPanelSettingJSON = Settings.initialValues[panelParentID][tableShortName] + } else { + var initialPanelSettingJSON = {}; + } + + return !_.isEqual(panelSettingJSON, initialPanelSettingJSON); } function updateDataChangedForSiblingRows(row, forceTrue) { @@ -1123,16 +1152,8 @@ function updateDataChangedForSiblingRows(row, forceTrue) { // get the short name for the setting from the table var tableShortName = row.closest('table').data('short-name') - // get a JSON representation of that section - var panelSettingJSON = form2js(panelParentID, ".", false, cleanupFormValues, true)[panelParentID][tableShortName] - if (Settings.initialValues[panelParentID]) { - var initialPanelSettingJSON = Settings.initialValues[panelParentID][tableShortName] - } else { - var initialPanelSettingJSON = {}; - } - // if they are equal, we don't need data-changed - isTrue = !_.isEqual(panelSettingJSON, initialPanelSettingJSON) + isTrue = tableHasChanged(panelParentID, tableShortName); } else { isTrue = true } @@ -1140,9 +1161,9 @@ function updateDataChangedForSiblingRows(row, forceTrue) { row.siblings('.' + Settings.DATA_ROW_CLASS).each(function(){ var hiddenInput = $(this).find('td.' + Settings.DATA_COL_CLASS + ' input') if (isTrue) { - hiddenInput.attr('data-changed', isTrue) + hiddenInput.attr('data-changed', isTrue); } else { - hiddenInput.removeAttr('data-changed') + hiddenInput.removeAttr('data-changed'); } }) } From 195df9e0ecc55676a857eae04357217c369d2613 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 27 Sep 2018 17:18:43 -0700 Subject: [PATCH 240/259] compute feet position offset in situ --- interface/src/avatar/MyAvatar.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index f2e6b68a0f..f9d549d911 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -529,7 +529,8 @@ void MyAvatar::update(float deltaTime) { } if (_goToFeetAjustment && _skeletonModelLoaded) { auto feetAjustment = getWorldPosition() - getWorldFeetPosition(); - goToLocation(getWorldPosition() + feetAjustment); + _goToPosition = getWorldPosition() + feetAjustment; + setWorldPosition(_goToPosition); _goToFeetAjustment = false; } if (_physicsSafetyPending && qApp->isPhysicsEnabled() && _characterController.isEnabledAndReady()) { From a6ef25f662643184818d036d66e744fe6840dad1 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 27 Sep 2018 18:14:16 -0700 Subject: [PATCH 241/259] just whitespace and varname changes --- scripts/system/marketplaces/marketplaces.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index b92a427c1f..dbb838664a 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -88,22 +88,22 @@ function setTabletVisibleInSecondaryCamera(visibleInSecondaryCam) { function openWallet() { ui.open(MARKETPLACE_WALLET_QML_PATH); } -function setupWallet(cta) { +function setupWallet(referrer) { // Needs to be done within the QML page in order to get access to QmlCommerce openWallet(); var ALLOWANCE_FOR_EVENT_BRIDGE_SETUP = 0; Script.setTimeout(function () { ui.tablet.sendToQml({ method: 'updateWalletReferrer', - referrer: cta + referrer: referrer }); }, ALLOWANCE_FOR_EVENT_BRIDGE_SETUP); } -function onMarketplaceOpen(cta) { +function onMarketplaceOpen(referrer) { if (Account.loggedIn && walletNeedsSetup()) { - if (cta) { - setupWallet(cta); + if (referrer) { + setupWallet(referrer); } else { print("WARNING: opening marketplace to", url, "without wallet setup."); } @@ -670,8 +670,8 @@ var filterText; // Used for updating Purchases QML function onWebEventReceived(message) { message = JSON.parse(message); if (message.type === GOTO_DIRECTORY) { - // This is the chooser between marketplaces. Only OUR markteplace - // requires/makes-use-of wallet, so doesn't go through openMarketplace bottleneck. + // This is the chooser between marketplaces. Only OUR markteplace + // requires/makes-use-of wallet, so doesn't go through openMarketplace bottleneck. ui.open(MARKETPLACES_URL, MARKETPLACES_INJECT_SCRIPT_URL); } else if (message.type === QUERY_CAN_WRITE_ASSETS) { ui.sendToHtml(CAN_WRITE_ASSETS + " " + Entities.canWriteAssets()); @@ -822,7 +822,7 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { } switch (message.method) { case 'gotoBank': - ui.close(); + ui.close(); if (Account.metaverseServerURL.indexOf("staging") >= 0) { Window.location = "hifi://hifiqa-master-metaverse-staging"; // So that we can test in staging. } else { From 3dea4fd10a5a2961a037bff6db58e94fbbdab5f8 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Thu, 27 Sep 2018 19:21:50 -0700 Subject: [PATCH 242/259] Fix some Mac rendering issues --- interface/resources/qml/hifi/avatarapp/TransparencyMask.qml | 1 + libraries/qml/src/qml/impl/SharedObject.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/interface/resources/qml/hifi/avatarapp/TransparencyMask.qml b/interface/resources/qml/hifi/avatarapp/TransparencyMask.qml index 6c50a6093a..db9b4f06ae 100644 --- a/interface/resources/qml/hifi/avatarapp/TransparencyMask.qml +++ b/interface/resources/qml/hifi/avatarapp/TransparencyMask.qml @@ -24,6 +24,7 @@ Item { fragmentShader: { " +#version 150 core varying highp vec2 qt_TexCoord0; uniform lowp sampler2D source; uniform lowp sampler2D mask; diff --git a/libraries/qml/src/qml/impl/SharedObject.cpp b/libraries/qml/src/qml/impl/SharedObject.cpp index 3b8d0bb743..259defdb48 100644 --- a/libraries/qml/src/qml/impl/SharedObject.cpp +++ b/libraries/qml/src/qml/impl/SharedObject.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "../OffscreenSurface.h" #include "../Logging.h" @@ -67,6 +68,7 @@ SharedObject::SharedObject() { // so we wait until after its ctor to move object/context to this thread. QQuickWindow::setDefaultAlphaBuffer(true); _quickWindow = new QQuickWindow(_renderControl); + _quickWindow->setFormat(getDefaultOpenGLSurfaceFormat()); _quickWindow->setColor(QColor(255, 255, 255, 0)); _quickWindow->setClearBeforeRendering(true); From 45993ba475f44fe12779e4da472d77e8c3ea5fca Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 28 Sep 2018 14:23:08 +1200 Subject: [PATCH 243/259] Provide mini tablet details via API instead of messages --- .../src/scripting/HMDScriptingInterface.h | 19 ++++++++++++++ .../controllers/controllerDispatcher.js | 12 ++------- .../nearParentGrabOverlay.js | 26 +------------------ .../controllerModules/stylusInput.js | 23 ++-------------- scripts/system/miniTablet.js | 10 +++---- 5 files changed, 27 insertions(+), 63 deletions(-) diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index a8fec839eb..21a9253327 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -57,6 +57,10 @@ class QScriptEngine; * @property {Uuid} tabletScreenID - The UUID of the tablet's screen overlay. * @property {Uuid} homeButtonID - The UUID of the tablet's "home" button overlay. * @property {Uuid} homeButtonHighlightID - The UUID of the tablet's "home" button highlight overlay. + * @property {Uuid} miniTabletID - The UUID of the mini tablet's body model overlay. null if not in HMD mode. + * @property {Uuid} miniTabletScreenID - The UUID of the mini tablet's screen overlay. null if not in HMD mode. + * @property {number} miniTabletHand - The hand that the mini tablet is displayed on: 0 for left hand, + * 1 for right hand, -1 if not in HMD mode. */ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Dependency { Q_OBJECT @@ -69,6 +73,9 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen Q_PROPERTY(QUuid homeButtonID READ getCurrentHomeButtonID WRITE setCurrentHomeButtonID) Q_PROPERTY(QUuid tabletScreenID READ getCurrentTabletScreenID WRITE setCurrentTabletScreenID) Q_PROPERTY(QUuid homeButtonHighlightID READ getCurrentHomeButtonHighlightID WRITE setCurrentHomeButtonHighlightID) + Q_PROPERTY(QUuid miniTabletID READ getCurrentMiniTabletID WRITE setCurrentMiniTabletID) + Q_PROPERTY(QUuid miniTabletScreenID READ getCurrentMiniTabletScreenID WRITE setCurrentMiniTabletScreenID) + Q_PROPERTY(int miniTabletHand READ getCurrentMiniTabletHand WRITE setCurrentMiniTabletHand) public: @@ -369,6 +376,15 @@ public: void setCurrentTabletScreenID(QUuid tabletID) { _tabletScreenID = tabletID; } QUuid getCurrentTabletScreenID() const { return _tabletScreenID; } + void setCurrentMiniTabletID(QUuid miniTabletID) { _miniTabletID = miniTabletID; } + QUuid getCurrentMiniTabletID() const { return _miniTabletID; } + + void setCurrentMiniTabletScreenID(QUuid miniTabletScreenID) { _miniTabletScreenID = miniTabletScreenID; } + QUuid getCurrentMiniTabletScreenID() const { return _miniTabletScreenID; } + + void setCurrentMiniTabletHand(int miniTabletHand) { _miniTabletHand = miniTabletHand; } + int getCurrentMiniTabletHand() const { return _miniTabletHand; } + private: bool _showTablet { false }; bool _tabletContextualMode { false }; @@ -377,6 +393,9 @@ private: QUuid _homeButtonID; QUuid _tabletEntityID; QUuid _homeButtonHighlightID; + QUuid _miniTabletID; + QUuid _miniTabletScreenID; + int _miniTabletHand { -1 }; // Get the position of the HMD glm::vec3 getPosition() const; diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index d3f37ac62b..acc1b8a15d 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -51,8 +51,6 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.tabletID = null; this.blacklist = []; this.pointerManager = new PointerManager(); - this.miniTabletID = null; - this.niniTabletHand = LEFT_HAND; // a module can occupy one or more "activity" slots while it's running. If all the required slots for a module are // not set to false (not in use), a module cannot start. When a module is using a slot, that module's name @@ -219,7 +217,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); // Tablet and mini-tablet must be within NEAR_TABLET_MAX_RADIUS in order to be grabbed. // Mini tablet can only be grabbed the hand it's displayed on. var tabletIndex = nearbyOverlays.indexOf(HMD.tabletID); - var miniTabletIndex = nearbyOverlays.indexOf(_this.miniTabletID); + var miniTabletIndex = nearbyOverlays.indexOf(HMD.miniTabletID); if (tabletIndex !== -1 || miniTabletIndex !== -1) { var closebyOverlays = Overlays.findOverlays(controllerLocations[h].position, NEAR_TABLET_MAX_RADIUS * sensorScaleFactor); @@ -228,7 +226,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); nearbyOverlays.splice(tabletIndex, 1); } if (miniTabletIndex !== -1 - && ((closebyOverlays.indexOf(_this.miniTabletID) === -1) || h !== _this.miniTabletHand)) { + && ((closebyOverlays.indexOf(HMD.miniTabletID) === -1) || h !== HMD.miniTabletHand)) { nearbyOverlays.splice(miniTabletIndex, 1); } } @@ -493,12 +491,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); _this.setBlacklist(); } } - } else if (channel === 'Hifi-MiniTablet-Details') { - message = JSON.parse(data); - _this.miniTabletID = message.overlay; - _this.miniTabletHand = message.hand; } - } catch (e) { print("WARNING: handControllerGrab.js -- error parsing message: " + data); } @@ -535,7 +528,6 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Entities.mousePressOnEntity.connect(mousePress); var controllerDispatcher = new ControllerDispatcher(); Messages.subscribe('Hifi-Hand-RayPick-Blacklist'); - Messages.subscribe('Hifi-MiniTablet-Details'); Messages.messageReceived.connect(controllerDispatcher.handleHandMessage); Script.scriptEnding.connect(controllerDispatcher.cleanup); Script.setTimeout(controllerDispatcher.update, BASIC_TIMER_INTERVAL_MS); diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js index fc5c2f9f79..763a0a0a27 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -27,7 +27,6 @@ Script.include("/~/system/libraries/utils.js"); this.previousParentJointIndex = {}; this.previouslyUnhooked = {}; this.robbed = false; - this.miniTabletID = null; this.parameters = makeDispatcherModuleParameters( 90, @@ -43,10 +42,6 @@ Script.include("/~/system/libraries/utils.js"); return (this.hand === RIGHT_HAND) ? leftNearParentingGrabOverlay : rightNearParentingGrabOverlay; }; - this.setMiniTabletID = function (id) { - this.miniTabletID = id; - }; - this.otherHandIsParent = function(props) { return this.getOtherModule().thisHandIsParent(props); }; @@ -168,7 +163,7 @@ Script.include("/~/system/libraries/utils.js"); var handPosition = controllerData.controllerLocations[this.hand].position; var distance = Vec3.distance(overlayPosition, handPosition); if (distance <= NEAR_GRAB_RADIUS * sensorScaleFactor) { - if (overlays[i] !== this.miniTabletID || controllerData.secondaryValues[this.hand] === 0) { + if (overlays[i] !== HMD.miniTabletID || controllerData.secondaryValues[this.hand] === 0) { // Don't grab mini tablet with grip. return overlays[i]; } @@ -225,25 +220,6 @@ Script.include("/~/system/libraries/utils.js"); }; } - function handleMessage(channel, data, sender) { - if (sender !== MyAvatar.sessionUUID) { - return; - } - - if (channel === 'Hifi-MiniTablet-Details') { - try { - var message = JSON.parse(data); - leftNearParentingGrabOverlay.setMiniTabletID(message.overlay); - rightNearParentingGrabOverlay.setMiniTabletID(message.overlay); - } catch (e) { - print("WARNING: nearParentGrabOverlay.js -- error parsing message: " + data); - } - } - } - - Messages.subscribe('Hifi-MiniTablet-Details'); - Messages.messageReceived.connect(handleMessage); - var leftNearParentingGrabOverlay = new NearParentingGrabOverlay(LEFT_HAND); var rightNearParentingGrabOverlay = new NearParentingGrabOverlay(RIGHT_HAND); diff --git a/scripts/system/controllers/controllerModules/stylusInput.js b/scripts/system/controllers/controllerModules/stylusInput.js index 77615e361f..6242e47992 100644 --- a/scripts/system/controllers/controllerModules/stylusInput.js +++ b/scripts/system/controllers/controllerModules/stylusInput.js @@ -58,12 +58,6 @@ Script.include("/~/system/libraries/controllers.js"); enabled: true }); - this.miniTabletID = null; - - this.setMiniTabletID = function (id) { - this.miniTabletID = id; - }; - this.disable = false; this.otherModuleNeedsToRun = function(controllerData) { @@ -130,8 +124,8 @@ Script.include("/~/system/libraries/controllers.js"); } // Add the mini tablet. - if (this.miniTabletID && Overlays.getProperty(this.miniTabletID, "visible")) { - stylusTarget = getOverlayDistance(controllerPosition, this.miniTabletID); + if (HMD.miniTabletScreenID && Overlays.getProperty(HMD.miniTabletScreenID, "visible")) { + stylusTarget = getOverlayDistance(controllerPosition, HMD.miniTabletScreenID); if (stylusTarget) { stylusTargets.push(stylusTarget); } @@ -205,15 +199,6 @@ Script.include("/~/system/libraries/controllers.js"); } } - function onMessageReceived(channel, message, sender) { - if (sender === MyAvatar.sessionUUID) { - if (channel === 'Hifi-MiniTablet-UI-ID') { - leftTabletStylusInput.setMiniTabletID(message); - rightTabletStylusInput.setMiniTabletID(message); - } - } - } - var leftTabletStylusInput = new StylusInput(LEFT_HAND); var rightTabletStylusInput = new StylusInput(RIGHT_HAND); @@ -224,11 +209,7 @@ Script.include("/~/system/libraries/controllers.js"); Overlays.hoverLeaveOverlay.connect(mouseHoverLeave); Overlays.mousePressOnOverlay.connect(mousePress); - Messages.subscribe('Hifi-MiniTablet-UI-ID'); - Messages.messageReceived.connect(onMessageReceived); - this.cleanup = function () { - Messages.messageReceived.disconnect(onMessageReceived); leftTabletStylusInput.cleanup(); rightTabletStylusInput.cleanup(); disableDispatcherModule("LeftTabletStylusInput"); diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 395b2cb71f..4ece210012 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -231,13 +231,9 @@ function updateMiniTabletID() { - // Send mini-tablet overlay ID to controllerDispatcher so that it can use a smaller near grab distance. - Messages.sendLocalMessage("Hifi-MiniTablet-Details", JSON.stringify({ - overlay: miniOverlay, - hand: uiHand - })); - // Send mini-tablet UI overlay ID to stylusInput so that styluses can be used on it. - Messages.sendLocalMessage("Hifi-MiniTablet-UI-ID", miniUIOverlay); + HMD.miniTabletID = miniOverlay; + HMD.miniTabletScreenID = miniUIOverlay; + HMD.miniTabletHand = miniOverlay ? uiHand : -1; } function playSound(sound, volume) { From 7b7447739ad5cb50086f5e3464ab784e59f2d9b0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 28 Sep 2018 14:23:20 +1200 Subject: [PATCH 244/259] Remove unused code noticed in passing --- .../system/controllers/controllerModules/stylusInput.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/scripts/system/controllers/controllerModules/stylusInput.js b/scripts/system/controllers/controllerModules/stylusInput.js index 6242e47992..0ca6cd1b04 100644 --- a/scripts/system/controllers/controllerModules/stylusInput.js +++ b/scripts/system/controllers/controllerModules/stylusInput.js @@ -35,13 +35,6 @@ Script.include("/~/system/libraries/controllers.js"); }; } - function getEntityDistance(controllerPosition, entityProps) { - return { - id: entityProps.id, - distance: Vec3.distance(entityProps.position, controllerPosition) - }; - } - function StylusInput(hand) { this.hand = hand; From b45677cd02836167b76e31085b5768711c36d1d3 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 27 Sep 2018 19:42:01 -0700 Subject: [PATCH 245/259] additionally handle item urls (marketplace links from cert inspections) --- scripts/system/marketplaces/marketplaces.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index dbb838664a..03ce986821 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -118,13 +118,16 @@ function openMarketplace(optionalItemOrUrl) { // AND... if call onMarketplaceOpen to setupWallet if we need to. var url = optionalItemOrUrl || MARKETPLACE_URL_INITIAL; var cta = 'marketplace cta'; + var match; + // If optionalItemOrUrl contains the metaverse base, then it's a url, not an item id. if (optionalItemOrUrl && optionalItemOrUrl.indexOf(METAVERSE_SERVER_URL) === -1) { cta = optionalItemOrUrl; // item id url = MARKETPLACE_URL + '/items/' + optionalItemOrUrl; } else if (optionalItemOrUrl) { - // A specific url. Don't attempt to set up wallet because we don't have a way to come back. - // Does that ever happen without the wallet already setup? - cta = ''; + // A specific url. + match = optionalItemOrUrl.match(/\/item\/(\w+)$/); + // Don't attempt to set up wallet for a non-item url because we don't have a way to come back. + cta = (match && match[1]) || ''; } ui.open(url, MARKETPLACES_INJECT_SCRIPT_URL); } From 440e2062f5db2a3a2bb4128a8351f5ca7a73c420 Mon Sep 17 00:00:00 2001 From: Kerry Ivan Kurian Date: Thu, 27 Sep 2018 22:14:02 -0700 Subject: [PATCH 246/259] Fix application uninstall in marketplace item tester --- .../commerce/marketplaceItemTester/MarketplaceItemTester.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml index 8f391f24c0..c3d87ca2f5 100644 --- a/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml +++ b/interface/resources/qml/hifi/commerce/marketplaceItemTester/MarketplaceItemTester.qml @@ -131,7 +131,7 @@ Rectangle { print("Marketplace item tester unsupported assetType " + assetType); } }, - "trash": function(){ + "trash": function(resource, assetType){ if ("application" === assetType) { Commerce.uninstallApp(resource); } From 053f22182a1a3d759cfa017ab9fff266da4a36f7 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 28 Sep 2018 11:57:36 -0700 Subject: [PATCH 247/259] fix inspection case --- scripts/system/marketplaces/marketplaces.js | 22 ++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 03ce986821..cca535a064 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -101,11 +101,19 @@ function setupWallet(referrer) { } function onMarketplaceOpen(referrer) { + var cta = referrer, match; if (Account.loggedIn && walletNeedsSetup()) { - if (referrer) { - setupWallet(referrer); + if (referrer === MARKETPLACE_URL_INITIAL) { + setupWallet('marketplace cta'); } else { - print("WARNING: opening marketplace to", url, "without wallet setup."); + match = referrer.match(/\/item\/(\w+)$/); + if (match && match[1]) { + setupWallet(match[1]); + } else if (referrer.indexOf(METAVERSE_SERVER_URL) === -1) { // not a url + setupWallet(referrer); + } else { + print("WARNING: opening marketplace to", referrer, "without wallet setup."); + } } } } @@ -117,17 +125,9 @@ function openMarketplace(optionalItemOrUrl) { // Otherwise, use home and 'marketplace cta'. // AND... if call onMarketplaceOpen to setupWallet if we need to. var url = optionalItemOrUrl || MARKETPLACE_URL_INITIAL; - var cta = 'marketplace cta'; - var match; // If optionalItemOrUrl contains the metaverse base, then it's a url, not an item id. if (optionalItemOrUrl && optionalItemOrUrl.indexOf(METAVERSE_SERVER_URL) === -1) { - cta = optionalItemOrUrl; // item id url = MARKETPLACE_URL + '/items/' + optionalItemOrUrl; - } else if (optionalItemOrUrl) { - // A specific url. - match = optionalItemOrUrl.match(/\/item\/(\w+)$/); - // Don't attempt to set up wallet for a non-item url because we don't have a way to come back. - cta = (match && match[1]) || ''; } ui.open(url, MARKETPLACES_INJECT_SCRIPT_URL); } From b69d0f39975791fd2ea52f5a1d03b98a3320ec7e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 28 Sep 2018 13:46:51 -0700 Subject: [PATCH 248/259] get interface building and working with clang-7 on ubuntu 18 --- domain-server/src/DomainServer.cpp | 2 +- interface/src/Application.cpp | 22 ++++++++++++++----- interface/src/Application_render.cpp | 5 ++++- interface/src/raypick/PathPointer.h | 1 + .../src/scripting/HMDScriptingInterface.h | 3 +-- interface/src/ui/OverlayConductor.cpp | 9 +++++++- interface/src/ui/overlays/Text3DOverlay.cpp | 3 +-- .../controllers/src/controllers/InputDevice.h | 1 + .../impl/conditionals/AndConditional.h | 2 ++ .../impl/conditionals/EndpointConditional.h | 1 + .../impl/conditionals/NotConditional.h | 1 + .../controllers/impl/filters/ClampFilter.h | 1 + .../impl/filters/ConstrainToIntegerFilter.h | 1 + .../ConstrainToPositiveIntegerFilter.h | 1 + .../controllers/impl/filters/DeadZoneFilter.h | 1 + .../impl/filters/ExponentialSmoothingFilter.h | 1 + .../impl/filters/HysteresisFilter.h | 1 + .../controllers/impl/filters/InvertFilter.h | 1 + .../impl/filters/LowVelocityFilter.h | 1 + .../src/controllers/impl/filters/NotFilter.h | 1 + .../impl/filters/PostTransformFilter.h | 1 + .../controllers/impl/filters/PulseFilter.h | 1 + .../controllers/impl/filters/RotateFilter.h | 1 + .../controllers/impl/filters/ScaleFilter.h | 1 + .../impl/filters/TransformFilter.h | 1 + .../impl/filters/TranslateFilter.h | 1 + .../AbstractHMDScriptingInterface.h | 2 ++ libraries/fbx/src/FBXReader.cpp | 1 - .../gpu-gl-common/src/gpu/gl/GLTexture.h | 1 + .../src/gpu/gl41/GL41BackendTexture.cpp | 1 - .../gpu/gl45/GL45BackendVariableTexture.cpp | 1 - libraries/gpu/src/gpu/State.h | 1 + .../src/input-plugins/KeyboardMouseDevice.h | 1 + .../src/model-networking/ModelCache.h | 1 + libraries/plugins/src/plugins/InputPlugin.h | 1 + libraries/pointers/src/Pick.h | 2 ++ libraries/render-utils/src/MeshPartPayload.h | 1 + .../render/src/render/DrawSceneOctree.cpp | 2 +- libraries/render/src/render/Item.h | 1 + libraries/shared/src/PointerEvent.cpp | 7 ------ libraries/shared/src/PrioritySortUtil.h | 1 + libraries/shared/src/TransformNode.h | 3 ++- libraries/task/src/task/Task.h | 3 ++- libraries/ui/src/OffscreenUi.cpp | 10 ++++++--- libraries/ui/src/OffscreenUi.h | 2 ++ libraries/workload/src/workload/Transaction.h | 2 +- tests-manual/gpu-textures/src/main.cpp | 2 -- tests-manual/gpu/src/main.cpp | 2 -- 48 files changed, 80 insertions(+), 33 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 57c4692794..8136bf3b53 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -2906,7 +2906,7 @@ void DomainServer::updateReplicationNodes(ReplicationServerDirection direction) // collect them in a vector to separately remove them with handleKillNode (since eachNode has a read lock and // we cannot recursively take the write lock required by handleKillNode) std::vector nodesToKill; - nodeList->eachNode([this, direction, replicationNodesInSettings, replicationDirection, &nodesToKill](const SharedNodePointer& otherNode) { + nodeList->eachNode([direction, replicationNodesInSettings, replicationDirection, &nodesToKill](const SharedNodePointer& otherNode) { if ((direction == Upstream && NodeType::isUpstream(otherNode->getType())) || (direction == Downstream && NodeType::isDownstream(otherNode->getType()))) { bool nodeInSettings = find(replicationNodesInSettings.cbegin(), replicationNodesInSettings.cend(), diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f23410bff9..16869dd3d7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -269,7 +269,7 @@ class RenderEventHandler : public QObject { public: RenderEventHandler() { // Transfer to a new thread - moveToNewNamedThread(this, "RenderThread", [this](QThread* renderThread) { + moveToNewNamedThread(this, "RenderThread", [](QThread* renderThread) { hifi::qt::addBlockingForbiddenThread("Render", renderThread); qApp->_lastTimeRendered.start(); }, std::bind(&RenderEventHandler::initialize, this), QThread::HighestPriority); @@ -1755,7 +1755,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // we can unlock the desktop repositioning code, since all the positions will be // relative to the desktop size for this plugin auto offscreenUi = DependencyManager::get(); - offscreenUi->getDesktop()->setProperty("repositionLocked", false); + connect(offscreenUi.data(), &OffscreenUi::desktopReady, []() { + auto offscreenUi = DependencyManager::get(); + auto desktop = offscreenUi->getDesktop(); + if (desktop) { + desktop->setProperty("repositionLocked", false); + } + }); // Make sure we don't time out during slow operations at startup updateHeartbeat(); @@ -2309,7 +2315,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo QTimer* checkLoginTimer = new QTimer(this); checkLoginTimer->setInterval(CHECK_LOGIN_TIMER); checkLoginTimer->setSingleShot(true); - connect(checkLoginTimer, &QTimer::timeout, this, [this]() { + connect(checkLoginTimer, &QTimer::timeout, this, []() { auto accountManager = DependencyManager::get(); auto dialogsManager = DependencyManager::get(); if (!accountManager->isLoggedIn()) { @@ -4673,8 +4679,14 @@ void Application::idle() { checkChangeCursor(); - Stats::getInstance()->updateStats(); - AnimStats::getInstance()->updateStats(); + auto stats = Stats::getInstance(); + if (stats) { + stats->updateStats(); + } + auto animStats = AnimStats::getInstance(); + if (animStats) { + animStats->updateStats(); + } // Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing // details if we're in ExtraDebugging mode. However, the ::update() and its subcomponents will show their timing diff --git a/interface/src/Application_render.cpp b/interface/src/Application_render.cpp index 6648fa2eb7..a87caeb9a8 100644 --- a/interface/src/Application_render.cpp +++ b/interface/src/Application_render.cpp @@ -157,7 +157,10 @@ void Application::paintGL() { renderArgs._context->enableStereo(false); { - Stats::getInstance()->setRenderDetails(renderArgs._details); + auto stats = Stats::getInstance(); + if (stats) { + stats->setRenderDetails(renderArgs._details); + } } uint64_t lastPaintDuration = usecTimestampNow() - lastPaintBegin; diff --git a/interface/src/raypick/PathPointer.h b/interface/src/raypick/PathPointer.h index b3638d1f7d..6d5abdeec0 100644 --- a/interface/src/raypick/PathPointer.h +++ b/interface/src/raypick/PathPointer.h @@ -27,6 +27,7 @@ class StartEndRenderState { public: StartEndRenderState() {} StartEndRenderState(const OverlayID& startID, const OverlayID& endID); + virtual ~StartEndRenderState() {} const OverlayID& getStartID() const { return _startID; } const OverlayID& getEndID() const { return _endID; } diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index a8fec839eb..0b18947f76 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -62,7 +62,6 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen Q_OBJECT Q_PROPERTY(glm::vec3 position READ getPosition) Q_PROPERTY(glm::quat orientation READ getOrientation) - Q_PROPERTY(bool mounted READ isMounted NOTIFY mountedChanged) Q_PROPERTY(bool showTablet READ getShouldShowTablet) Q_PROPERTY(bool tabletContextualMode READ getTabletContextualMode) Q_PROPERTY(QUuid tabletID READ getCurrentTabletFrameID WRITE setCurrentTabletFrameID) @@ -350,7 +349,7 @@ public: static QScriptValue getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine); static QScriptValue getHUDLookAtPosition3D(QScriptContext* context, QScriptEngine* engine); - bool isMounted() const; + bool isMounted() const override; void toggleShouldShowTablet(); void setShouldShowTablet(bool value); diff --git a/interface/src/ui/OverlayConductor.cpp b/interface/src/ui/OverlayConductor.cpp index e27001567f..da4fdaba44 100644 --- a/interface/src/ui/OverlayConductor.cpp +++ b/interface/src/ui/OverlayConductor.cpp @@ -75,7 +75,14 @@ void OverlayConductor::centerUI() { void OverlayConductor::update(float dt) { auto offscreenUi = DependencyManager::get(); - bool currentVisible = !offscreenUi->getDesktop()->property("pinned").toBool(); + if (!offscreenUi) { + return; + } + auto desktop = offscreenUi->getDesktop(); + if (!desktop) { + return; + } + bool currentVisible = !desktop->property("pinned").toBool(); auto myAvatar = DependencyManager::get()->getMyAvatar(); // centerUI when hmd mode is first enabled and mounted diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index fc4b8b9010..40e49ccefd 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -114,7 +114,6 @@ void Text3DOverlay::render(RenderArgs* args) { float scaleFactor = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight; - glm::vec2 clipMinimum(0.0f, 0.0f); glm::vec2 clipDimensions((dimensions.x - (_leftMargin + _rightMargin)) / scaleFactor, (dimensions.y - (_topMargin + _bottomMargin)) / scaleFactor); @@ -296,4 +295,4 @@ QSizeF Text3DOverlay::textSize(const QString& text) const { float pointToWorldScale = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight; return QSizeF(extents.x, extents.y) * pointToWorldScale; -} \ No newline at end of file +} diff --git a/libraries/controllers/src/controllers/InputDevice.h b/libraries/controllers/src/controllers/InputDevice.h index 1e626e6a3c..6c5cc3a065 100644 --- a/libraries/controllers/src/controllers/InputDevice.h +++ b/libraries/controllers/src/controllers/InputDevice.h @@ -98,6 +98,7 @@ enum Hand { class InputDevice { public: InputDevice(const QString& name) : _name(name) {} + virtual ~InputDevice() {} using Pointer = std::shared_ptr; diff --git a/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h b/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h index 2299843a24..7d8f08ca1c 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h +++ b/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h @@ -24,6 +24,8 @@ public: AndConditional(Conditional::Pointer& first, Conditional::Pointer& second) : _children({ first, second }) {} + virtual ~AndConditional() {} + virtual bool satisfied() override; private: diff --git a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h index 0ba1347087..a6bd7d468d 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h +++ b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h @@ -18,6 +18,7 @@ namespace controller { class EndpointConditional : public Conditional { public: EndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) {} + virtual ~EndpointConditional() {} virtual bool satisfied() override { return _endpoint && _endpoint->peek() != 0.0f; } private: Endpoint::Pointer _endpoint; diff --git a/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h b/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h index 6b19cf9505..3fcd5f49fc 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h +++ b/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h @@ -19,6 +19,7 @@ namespace controller { using Pointer = std::shared_ptr; NotConditional(Conditional::Pointer operand) : _operand(operand) { } + virtual ~NotConditional() {} virtual bool satisfied() override; diff --git a/libraries/controllers/src/controllers/impl/filters/ClampFilter.h b/libraries/controllers/src/controllers/impl/filters/ClampFilter.h index b06a43515f..6eca00fbe2 100644 --- a/libraries/controllers/src/controllers/impl/filters/ClampFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/ClampFilter.h @@ -18,6 +18,7 @@ class ClampFilter : public Filter { REGISTER_FILTER_CLASS(ClampFilter); public: ClampFilter(float min = 0.0, float max = 1.0) : _min(min), _max(max) {}; + virtual ~ClampFilter() {} virtual float apply(float value) const override { return glm::clamp(value, _min, _max); } diff --git a/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.h b/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.h index c9a25fde72..bc90121ab0 100644 --- a/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.h @@ -18,6 +18,7 @@ class ConstrainToIntegerFilter : public Filter { REGISTER_FILTER_CLASS(ConstrainToIntegerFilter); public: ConstrainToIntegerFilter() {}; + virtual ~ConstrainToIntegerFilter() {} virtual float apply(float value) const override { return glm::sign(value); diff --git a/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.h b/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.h index e3f4ee8929..accebef851 100644 --- a/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.h @@ -18,6 +18,7 @@ class ConstrainToPositiveIntegerFilter : public Filter { REGISTER_FILTER_CLASS(ConstrainToPositiveIntegerFilter); public: ConstrainToPositiveIntegerFilter() {}; + virtual ~ConstrainToPositiveIntegerFilter() {}; virtual float apply(float value) const override { return (value <= 0.0f) ? 0.0f : 1.0f; diff --git a/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.h b/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.h index d898647126..96c60198e2 100644 --- a/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.h @@ -18,6 +18,7 @@ class DeadZoneFilter : public Filter { REGISTER_FILTER_CLASS(DeadZoneFilter); public: DeadZoneFilter(float min = 0.0) : _min(min) {}; + virtual ~DeadZoneFilter() {} virtual float apply(float value) const override; diff --git a/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.h b/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.h index 134f57243e..5b29b6681a 100644 --- a/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/ExponentialSmoothingFilter.h @@ -20,6 +20,7 @@ namespace controller { ExponentialSmoothingFilter() {} ExponentialSmoothingFilter(float rotationConstant, float translationConstant) : _translationConstant(translationConstant), _rotationConstant(rotationConstant) {} + virtual ~ExponentialSmoothingFilter() {} float apply(float value) const override { return value; } Pose apply(Pose value) const override; diff --git a/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h b/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h index 4eb563754f..4a607d0d5f 100644 --- a/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h @@ -18,6 +18,7 @@ class HysteresisFilter : public Filter { REGISTER_FILTER_CLASS(HysteresisFilter); public: HysteresisFilter(float min = 0.25, float max = 0.75); + virtual ~HysteresisFilter() {} virtual float apply(float value) const override; virtual Pose apply(Pose value) const override { return value; } diff --git a/libraries/controllers/src/controllers/impl/filters/InvertFilter.h b/libraries/controllers/src/controllers/impl/filters/InvertFilter.h index 8acc9d56d6..03b6e9fcb0 100644 --- a/libraries/controllers/src/controllers/impl/filters/InvertFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/InvertFilter.h @@ -19,6 +19,7 @@ class InvertFilter : public ScaleFilter { public: using ScaleFilter::parseParameters; InvertFilter() : ScaleFilter(-1.0f) {} + virtual ~InvertFilter() {} virtual bool parseParameters(const QJsonArray& parameters) { return true; } diff --git a/libraries/controllers/src/controllers/impl/filters/LowVelocityFilter.h b/libraries/controllers/src/controllers/impl/filters/LowVelocityFilter.h index b1c6be1f58..fa75473edf 100644 --- a/libraries/controllers/src/controllers/impl/filters/LowVelocityFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/LowVelocityFilter.h @@ -20,6 +20,7 @@ namespace controller { LowVelocityFilter() {} LowVelocityFilter(float rotationConstant, float translationConstant) : _translationConstant(translationConstant), _rotationConstant(rotationConstant) {} + virtual ~LowVelocityFilter() {} float apply(float value) const override { return value; } Pose apply(Pose newPose) const override; diff --git a/libraries/controllers/src/controllers/impl/filters/NotFilter.h b/libraries/controllers/src/controllers/impl/filters/NotFilter.h index ceb7d29de3..fa52b8e212 100644 --- a/libraries/controllers/src/controllers/impl/filters/NotFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/NotFilter.h @@ -10,6 +10,7 @@ class NotFilter : public Filter { REGISTER_FILTER_CLASS(NotFilter); public: NotFilter(); + virtual ~NotFilter() {} virtual float apply(float value) const override; virtual Pose apply(Pose value) const override { return value; } diff --git a/libraries/controllers/src/controllers/impl/filters/PostTransformFilter.h b/libraries/controllers/src/controllers/impl/filters/PostTransformFilter.h index 656a146ff2..1cb9c0a1bd 100644 --- a/libraries/controllers/src/controllers/impl/filters/PostTransformFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/PostTransformFilter.h @@ -21,6 +21,7 @@ class PostTransformFilter : public Filter { public: PostTransformFilter() { } PostTransformFilter(glm::mat4 transform) : _transform(transform) {} + virtual ~PostTransformFilter() {} virtual float apply(float value) const override { return value; } virtual Pose apply(Pose value) const override { return value.postTransform(_transform); } virtual bool parseParameters(const QJsonValue& parameters) override { return parseMat4Parameter(parameters, _transform); } diff --git a/libraries/controllers/src/controllers/impl/filters/PulseFilter.h b/libraries/controllers/src/controllers/impl/filters/PulseFilter.h index a8c7cbf9e6..37cfe34b86 100644 --- a/libraries/controllers/src/controllers/impl/filters/PulseFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/PulseFilter.h @@ -20,6 +20,7 @@ class PulseFilter : public Filter { public: PulseFilter() {} PulseFilter(float interval) : _interval(interval) {} + virtual ~PulseFilter() {} virtual float apply(float value) const override; diff --git a/libraries/controllers/src/controllers/impl/filters/RotateFilter.h b/libraries/controllers/src/controllers/impl/filters/RotateFilter.h index ee2e081393..aecf4f7b7c 100644 --- a/libraries/controllers/src/controllers/impl/filters/RotateFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/RotateFilter.h @@ -21,6 +21,7 @@ class RotateFilter : public Filter { public: RotateFilter() { } RotateFilter(glm::quat rotation) : _rotation(rotation) {} + virtual ~RotateFilter() {} virtual float apply(float value) const override { return value; } diff --git a/libraries/controllers/src/controllers/impl/filters/ScaleFilter.h b/libraries/controllers/src/controllers/impl/filters/ScaleFilter.h index 7b03e2ce48..84f7cb7e47 100644 --- a/libraries/controllers/src/controllers/impl/filters/ScaleFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/ScaleFilter.h @@ -21,6 +21,7 @@ class ScaleFilter : public Filter { public: ScaleFilter() {} ScaleFilter(float scale) : _scale(scale) {} + virtual ~ScaleFilter() {} virtual float apply(float value) const override { return value * _scale; diff --git a/libraries/controllers/src/controllers/impl/filters/TransformFilter.h b/libraries/controllers/src/controllers/impl/filters/TransformFilter.h index 263b70c9b4..ccfa9c6c25 100644 --- a/libraries/controllers/src/controllers/impl/filters/TransformFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/TransformFilter.h @@ -21,6 +21,7 @@ class TransformFilter : public Filter { public: TransformFilter() { } TransformFilter(glm::mat4 transform) : _transform(transform) {} + virtual ~TransformFilter() {} virtual float apply(float value) const override { return value; } virtual Pose apply(Pose value) const override { return value.transform(_transform); } diff --git a/libraries/controllers/src/controllers/impl/filters/TranslateFilter.h b/libraries/controllers/src/controllers/impl/filters/TranslateFilter.h index eda2912a8a..a66e1eb4a4 100644 --- a/libraries/controllers/src/controllers/impl/filters/TranslateFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/TranslateFilter.h @@ -21,6 +21,7 @@ class TranslateFilter : public Filter { public: TranslateFilter() { } TranslateFilter(glm::vec3 translate) : _translate(translate) {} + virtual ~TranslateFilter() {} virtual float apply(float value) const override { return value; } virtual Pose apply(Pose value) const override { return value.transform(glm::translate(_translate)); } diff --git a/libraries/display-plugins/src/display-plugins/AbstractHMDScriptingInterface.h b/libraries/display-plugins/src/display-plugins/AbstractHMDScriptingInterface.h index 7fe58618bc..4234a8731b 100644 --- a/libraries/display-plugins/src/display-plugins/AbstractHMDScriptingInterface.h +++ b/libraries/display-plugins/src/display-plugins/AbstractHMDScriptingInterface.h @@ -20,6 +20,7 @@ class AbstractHMDScriptingInterface : public QObject { Q_PROPERTY(float eyeHeight READ getEyeHeight) Q_PROPERTY(float playerHeight READ getPlayerHeight) Q_PROPERTY(float ipdScale READ getIPDScale WRITE setIPDScale NOTIFY IPDScaleChanged) + Q_PROPERTY(bool mounted READ isMounted NOTIFY mountedChanged) public: AbstractHMDScriptingInterface(); @@ -29,6 +30,7 @@ public: float getIPDScale() const; void setIPDScale(float ipdScale); bool isHMDMode() const; + virtual bool isMounted() const = 0; signals: /**jsdoc diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index d0efed3df4..dd766f002c 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1923,7 +1923,6 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS }; // now that all joints have been scanned compute a k-Dop bounding volume of mesh - glm::vec3 defaultCapsuleAxis(0.0f, 1.0f, 0.0f); for (int i = 0; i < geometry.joints.size(); ++i) { FBXJoint& joint = geometry.joints[i]; diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLTexture.h b/libraries/gpu-gl-common/src/gpu/gl/GLTexture.h index a6a38fbcd6..e9a55ad8e2 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLTexture.h @@ -25,6 +25,7 @@ struct GLFilterMode { class GLTextureTransferEngine { public: + virtual ~GLTextureTransferEngine() {} using Pointer = std::shared_ptr; /// Called once per frame to perform any require memory management or transfer work virtual void manageMemory() = 0; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 97b1a96a1d..d1b5969793 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -295,7 +295,6 @@ GL41VariableAllocationTexture::GL41VariableAllocationTexture(const std::weak_ptr _maxAllocatedMip = _populatedMip = mipLevels; _minAllocatedMip = texture.minAvailableMipLevel(); - uvec3 mipDimensions; for (uint16_t mip = _minAllocatedMip; mip < mipLevels; ++mip) { if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS))) { _maxAllocatedMip = _populatedMip = mip; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 4689163a47..f53eff637d 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -85,7 +85,6 @@ GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr& backend _maxAllocatedMip = _populatedMip = mipLevels; _minAllocatedMip = texture.minAvailableMipLevel(); - uvec3 mipDimensions; for (uint16_t mip = _minAllocatedMip; mip < mipLevels; ++mip) { if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS))) { _maxAllocatedMip = _populatedMip = mip; diff --git a/libraries/gpu/src/gpu/State.h b/libraries/gpu/src/gpu/State.h index 385edec277..6e29900c03 100755 --- a/libraries/gpu/src/gpu/State.h +++ b/libraries/gpu/src/gpu/State.h @@ -118,6 +118,7 @@ public: uint8 _function = LESS; uint8 _writeMask = true; uint8 _enabled = false; + __attribute__((unused)) // _spare is here to to affect alignment uint8 _spare = 0; public: DepthTest(bool enabled = false, bool writeMask = true, ComparisonFunction func = LESS) : diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index b94acb8b71..ce2bea0853 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -96,6 +96,7 @@ protected: class InputDevice : public controller::InputDevice { public: InputDevice() : controller::InputDevice("Keyboard") {} + virtual ~InputDevice() {} private: // Device functions virtual controller::Input::NamedVector getAvailableInputs() const override; diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index eea6c93786..dfe26788f2 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -36,6 +36,7 @@ public: Geometry() = default; Geometry(const Geometry& geometry); + virtual ~Geometry() {} // Immutable over lifetime using GeometryMeshes = std::vector>; diff --git a/libraries/plugins/src/plugins/InputPlugin.h b/libraries/plugins/src/plugins/InputPlugin.h index 23fbb6cac6..344f07d64c 100644 --- a/libraries/plugins/src/plugins/InputPlugin.h +++ b/libraries/plugins/src/plugins/InputPlugin.h @@ -19,6 +19,7 @@ namespace controller { class InputPlugin : public Plugin { public: + virtual ~InputPlugin() {} virtual void pluginFocusOutEvent() = 0; virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) = 0; diff --git a/libraries/pointers/src/Pick.h b/libraries/pointers/src/Pick.h index 099a791407..1703f6598f 100644 --- a/libraries/pointers/src/Pick.h +++ b/libraries/pointers/src/Pick.h @@ -109,6 +109,7 @@ class PickResult { public: PickResult() {} PickResult(const QVariantMap& pickVariant) : pickVariant(pickVariant) {} + virtual ~PickResult() {} virtual QVariantMap toVariantMap() const { return pickVariant; @@ -233,6 +234,7 @@ template class Pick : public PickQuery { public: Pick(const PickFilter& filter, const float maxDistance, const bool enabled) : PickQuery(filter, maxDistance, enabled) {} + virtual ~Pick() {} virtual T getMathematicalPick() const = 0; virtual PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const = 0; diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 5c7177e890..0cbe082cb5 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -28,6 +28,7 @@ class MeshPartPayload { public: MeshPartPayload() {} MeshPartPayload(const std::shared_ptr& mesh, int partIndex, graphics::MaterialPointer material); + virtual ~MeshPartPayload() {} typedef render::Payload Payload; typedef Payload::DataPointer Pointer; diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index 6c87d2f56b..b10c3fef8d 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -88,7 +88,7 @@ void DrawSceneOctree::run(const RenderContextPointer& renderContext, const ItemS batch.setInputFormat(_cellBoundsFormat); std::vector cellBounds; - auto drawCellBounds = [this, &cellBounds, &scene, &batch](const std::vector& cells) { + auto drawCellBounds = [this, &cellBounds, &scene](const std::vector& cells) { cellBounds.reserve(cellBounds.size() + cells.size()); for (const auto& cellID : cells) { auto cell = scene->getSpatialTree().getConcreteCell(cellID); diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 5ecfba2da8..88e85c604a 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -531,6 +531,7 @@ public: typedef UpdateFunctor Updater; Payload(const DataPointer& data) : _data(data) {} + virtual ~Payload() {} // Payload general interface virtual const ItemKey getKey() const override { return payloadGetKey(_data); } diff --git a/libraries/shared/src/PointerEvent.cpp b/libraries/shared/src/PointerEvent.cpp index fb855922c2..422f2dfb20 100644 --- a/libraries/shared/src/PointerEvent.cpp +++ b/libraries/shared/src/PointerEvent.cpp @@ -229,16 +229,9 @@ void PointerEvent::fromScriptValue(const QScriptValue& object, PointerEvent& eve QScriptValue id = object.property("id"); event._id = id.isNumber() ? (uint32_t)id.toNumber() : 0; - glm::vec2 pos2D; vec2FromScriptValue(object.property("pos2D"), event._pos2D); - - glm::vec3 pos3D; vec3FromScriptValue(object.property("pos3D"), event._pos3D); - - glm::vec3 normal; vec3FromScriptValue(object.property("normal"), event._normal); - - glm::vec3 direction; vec3FromScriptValue(object.property("direction"), event._direction); QScriptValue button = object.property("button"); diff --git a/libraries/shared/src/PrioritySortUtil.h b/libraries/shared/src/PrioritySortUtil.h index 27f6b193ba..4a74c5d212 100644 --- a/libraries/shared/src/PrioritySortUtil.h +++ b/libraries/shared/src/PrioritySortUtil.h @@ -29,6 +29,7 @@ namespace PrioritySortUtil { class Sortable { public: + virtual ~Sortable() {} virtual glm::vec3 getPosition() const = 0; virtual float getRadius() const = 0; virtual uint64_t getTimestamp() const = 0; diff --git a/libraries/shared/src/TransformNode.h b/libraries/shared/src/TransformNode.h index 73223a8cee..1c10bed1c0 100644 --- a/libraries/shared/src/TransformNode.h +++ b/libraries/shared/src/TransformNode.h @@ -12,7 +12,8 @@ class TransformNode { public: + virtual ~TransformNode() {} virtual Transform getTransform() = 0; }; -#endif // hifi_TransformNode_h \ No newline at end of file +#endif // hifi_TransformNode_h diff --git a/libraries/task/src/task/Task.h b/libraries/task/src/task/Task.h index 60ccd26b70..3878c5719b 100644 --- a/libraries/task/src/task/Task.h +++ b/libraries/task/src/task/Task.h @@ -174,7 +174,8 @@ public: template using ModelO = Model; template using ModelIO = Model; - Job(ConceptPointer concept) : _concept(concept) {} + Job(const ConceptPointer& concept) : _concept(concept) {} + virtual ~Job() {} const std::string& getName() const { return _concept->getName(); } const Varying getInput() const { return _concept->getInput(); } diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index d82cfbbf3f..7579de256c 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -205,9 +205,12 @@ bool OffscreenUi::isPointOnDesktopWindow(QVariant point) { } void OffscreenUi::hide(const QString& name) { - QQuickItem* item = getRootItem()->findChild(name); - if (item) { - QQmlProperty(item, OFFSCREEN_VISIBILITY_PROPERTY).write(false); + auto rootItem = getRootItem(); + if (rootItem) { + QQuickItem* item = getRootItem()->findChild(name); + if (item) { + QQmlProperty(item, OFFSCREEN_VISIBILITY_PROPERTY).write(false); + } } } @@ -672,6 +675,7 @@ void OffscreenUi::createDesktop(const QUrl& url) { new KeyboardFocusHack(); connect(_desktop, SIGNAL(showDesktop()), this, SIGNAL(showDesktop())); + emit desktopReady(); }); } diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 26f9f58dd5..fbf7068b1c 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -243,6 +243,8 @@ signals: // void fileDialogResponse(QString response); // void assetDialogResponse(QString response); // void inputDialogResponse(QVariant response); + void desktopReady(); + public slots: void removeModalDialog(QObject* modal); diff --git a/libraries/workload/src/workload/Transaction.h b/libraries/workload/src/workload/Transaction.h index 22328cf4b1..2bad911e4b 100644 --- a/libraries/workload/src/workload/Transaction.h +++ b/libraries/workload/src/workload/Transaction.h @@ -135,7 +135,7 @@ typedef std::vector TransactionQueue; class Collection { public: Collection(); - ~Collection(); + virtual ~Collection(); virtual void clear(); diff --git a/tests-manual/gpu-textures/src/main.cpp b/tests-manual/gpu-textures/src/main.cpp index 3d0051dc1d..70846b13b1 100644 --- a/tests-manual/gpu-textures/src/main.cpp +++ b/tests-manual/gpu-textures/src/main.cpp @@ -85,14 +85,12 @@ class MyTestWindow : public TestWindow { void updateCamera() { float t = _time.elapsed() * 1e-3f; - glm::vec3 unitscale{ 1.0f }; glm::vec3 up{ 0.0f, 1.0f, 0.0f }; float distance = 3.0f; glm::vec3 camera_position{ distance * sinf(t), 0.5f, distance * cosf(t) }; static const vec3 camera_focus(0); - static const vec3 camera_up(0, 1, 0); _camera = glm::inverse(glm::lookAt(camera_position, camera_focus, up)); ViewFrustum frustum; diff --git a/tests-manual/gpu/src/main.cpp b/tests-manual/gpu/src/main.cpp index 41d84ca026..f36e6a9abd 100644 --- a/tests-manual/gpu/src/main.cpp +++ b/tests-manual/gpu/src/main.cpp @@ -102,14 +102,12 @@ class MyTestWindow : public TestWindow { #ifdef INTERACTIVE t = _time.elapsed() * 1e-3f; #endif - glm::vec3 unitscale { 1.0f }; glm::vec3 up { 0.0f, 1.0f, 0.0f }; float distance = 3.0f; glm::vec3 camera_position { distance * sinf(t), 0.5f, distance * cosf(t) }; static const vec3 camera_focus(0); - static const vec3 camera_up(0, 1, 0); _camera = glm::inverse(glm::lookAt(camera_position, camera_focus, up)); ViewFrustum frustum; From cb04894e335d2e1ffef1de24a208432fcfef469d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 28 Sep 2018 14:53:05 -0700 Subject: [PATCH 249/259] try another way of fixing _spare warning --- libraries/gpu/src/gpu/State.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/gpu/src/gpu/State.h b/libraries/gpu/src/gpu/State.h index 6e29900c03..9bcd69dcdb 100755 --- a/libraries/gpu/src/gpu/State.h +++ b/libraries/gpu/src/gpu/State.h @@ -118,11 +118,12 @@ public: uint8 _function = LESS; uint8 _writeMask = true; uint8 _enabled = false; - __attribute__((unused)) // _spare is here to to affect alignment - uint8 _spare = 0; + uint8 _spare = 0; // _spare is here to to affect alignment public: DepthTest(bool enabled = false, bool writeMask = true, ComparisonFunction func = LESS) : - _function(func), _writeMask(writeMask), _enabled(enabled) {} + _function(func), _writeMask(writeMask), _enabled(enabled) { + (void)_spare; // to avoid unusued variable warning + } bool isEnabled() const { return _enabled != 0; } ComparisonFunction getFunction() const { return ComparisonFunction(_function); } From bbe9b5c26f77aa5929b7f139e9e92839841937bc Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 28 Sep 2018 16:04:40 -0700 Subject: [PATCH 250/259] code review, remove some unneeded automoc includes --- .../src/graphics-scripting/GraphicsScriptingInterface.cpp | 2 -- .../src/graphics-scripting/ScriptableMesh.cpp | 3 --- .../src/graphics-scripting/ScriptableModel.cpp | 2 -- libraries/ui/src/OffscreenUi.cpp | 2 +- tests-manual/entities/src/main.cpp | 2 -- tests-manual/gpu-textures/src/main.cpp | 2 +- tests-manual/gpu/src/main.cpp | 2 +- tests-manual/render-perf/src/main.cpp | 2 -- 8 files changed, 3 insertions(+), 14 deletions(-) diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index 6fd0017ae2..f2afc1e7d4 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -445,5 +445,3 @@ void GraphicsScriptingInterface::registerMetaTypes(QScriptEngine* engine) { Q_UNUSED(metaTypeIds); } - -#include "GraphicsScriptingInterface.moc" diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp index 585a719638..72d2adb48f 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp @@ -401,6 +401,3 @@ scriptable::ScriptableMesh::~ScriptableMesh() { #endif strongMesh.reset(); } - -#include "ScriptableMesh.moc" - diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp index 7aaa182163..51085561c3 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp @@ -240,5 +240,3 @@ glm::uint32 scriptable::ScriptableModel::forEachVertexAttribute(QScriptValue cal return result; } #endif - -#include "ScriptableModel.moc" diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 7579de256c..0aad297587 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -207,7 +207,7 @@ bool OffscreenUi::isPointOnDesktopWindow(QVariant point) { void OffscreenUi::hide(const QString& name) { auto rootItem = getRootItem(); if (rootItem) { - QQuickItem* item = getRootItem()->findChild(name); + QQuickItem* item = rootItem->findChild(name); if (item) { QQmlProperty(item, OFFSCREEN_VISIBILITY_PROPERTY).write(false); } diff --git a/tests-manual/entities/src/main.cpp b/tests-manual/entities/src/main.cpp index 43a5d2e48d..e38298ba2c 100644 --- a/tests-manual/entities/src/main.cpp +++ b/tests-manual/entities/src/main.cpp @@ -169,5 +169,3 @@ int main(int argc, char** argv) { qDebug() << (duration / 1000.0f); return 0; } - -#include "main.moc" diff --git a/tests-manual/gpu-textures/src/main.cpp b/tests-manual/gpu-textures/src/main.cpp index 70846b13b1..d8454c6c22 100644 --- a/tests-manual/gpu-textures/src/main.cpp +++ b/tests-manual/gpu-textures/src/main.cpp @@ -85,7 +85,7 @@ class MyTestWindow : public TestWindow { void updateCamera() { float t = _time.elapsed() * 1e-3f; - glm::vec3 up{ 0.0f, 1.0f, 0.0f }; + static const glm::vec3 up{ 0.0f, 1.0f, 0.0f }; float distance = 3.0f; glm::vec3 camera_position{ distance * sinf(t), 0.5f, distance * cosf(t) }; diff --git a/tests-manual/gpu/src/main.cpp b/tests-manual/gpu/src/main.cpp index f36e6a9abd..7cf941daea 100644 --- a/tests-manual/gpu/src/main.cpp +++ b/tests-manual/gpu/src/main.cpp @@ -102,7 +102,7 @@ class MyTestWindow : public TestWindow { #ifdef INTERACTIVE t = _time.elapsed() * 1e-3f; #endif - glm::vec3 up { 0.0f, 1.0f, 0.0f }; + static const glm::vec3 up { 0.0f, 1.0f, 0.0f }; float distance = 3.0f; glm::vec3 camera_position { distance * sinf(t), 0.5f, distance * cosf(t) }; diff --git a/tests-manual/render-perf/src/main.cpp b/tests-manual/render-perf/src/main.cpp index c9724ea352..357d40cfb4 100644 --- a/tests-manual/render-perf/src/main.cpp +++ b/tests-manual/render-perf/src/main.cpp @@ -1103,5 +1103,3 @@ int main(int argc, char** argv) { app.exec(); return 0; } - -#include "main.moc" From feb230b3bd3001a707ac67d41a2c7ac519208bbc Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 28 Sep 2018 16:17:52 -0700 Subject: [PATCH 251/259] Remove old comment in entitySelectionTool.js --- scripts/system/libraries/entitySelectionTool.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 930f33f8d9..47228c3fae 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -2637,8 +2637,6 @@ SelectionDisplay = (function() { startAt: startAtCurrent, endAt: endAtCurrent }); - - // not sure why but this seems to be needed to fix an reverse rotation for yaw ring only } if (wantDebug) { From 8464a70c9e6f319a1b3c9c4761b32b33ffcdc8c1 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 29 Sep 2018 12:42:41 +1200 Subject: [PATCH 252/259] Fix initial tablet scale --- scripts/system/libraries/utils.js | 9 +++++---- scripts/system/tablet-ui/tabletUI.js | 13 +++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/scripts/system/libraries/utils.js b/scripts/system/libraries/utils.js index f7b5f6db8d..f8928dd74a 100644 --- a/scripts/system/libraries/utils.js +++ b/scripts/system/libraries/utils.js @@ -356,13 +356,14 @@ getTabletWidthFromSettings = function () { var DEFAULT_TABLET_WIDTH = 0.4375; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var toolbarMode = tablet.toolbarMode; - var DEFAULT_TABLET_SCALE = 70; - var tabletScalePercentage = DEFAULT_TABLET_SCALE; + var DEFAULT_DESKTOP_TABLET_SCALE = 75; + var DEFAULT_HMD_TABLET_SCALE = 60; + var tabletScalePercentage = DEFAULT_HMD_TABLET_SCALE; if (!toolbarMode) { if (HMD.active) { - tabletScalePercentage = Settings.getValue("hmdTabletScale") || DEFAULT_TABLET_SCALE; + tabletScalePercentage = Settings.getValue("hmdTabletScale") || DEFAULT_HMD_TABLET_SCALE; } else { - tabletScalePercentage = Settings.getValue("desktopTabletScale") || DEFAULT_TABLET_SCALE; + tabletScalePercentage = Settings.getValue("desktopTabletScale") || DEFAULT_DESKTOP_TABLET_SCALE; } } return DEFAULT_TABLET_WIDTH * (tabletScalePercentage / 100); diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index 80ddbeca8b..9a22014895 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -19,11 +19,12 @@ var tabletRezzed = false; var activeHand = null; var DEFAULT_WIDTH = 0.4375; - var DEFAULT_TABLET_SCALE = 70; + var DEFAULT_DESKTOP_TABLET_SCALE = 75; + var DEFAULT_HMD_TABLET_SCALE = 60; var preMakeTime = Date.now(); var validCheckTime = Date.now(); var debugTablet = false; - var tabletScalePercentage = 70.0; + var tabletScalePercentage = DEFAULT_HMD_TABLET_SCALE; var UIWebTablet = null; var MSECS_PER_SEC = 1000.0; var MUTE_MICROPHONE_MENU_ITEM = "Mute Microphone"; @@ -66,14 +67,14 @@ } function getTabletScalePercentageFromSettings() { - checkTablet() + checkTablet(); var toolbarMode = gTablet.toolbarMode; - var tabletScalePercentage = DEFAULT_TABLET_SCALE; + var tabletScalePercentage = DEFAULT_HMD_TABLET_SCALE; if (!toolbarMode) { if (HMD.active) { - tabletScalePercentage = Settings.getValue("hmdTabletScale") || DEFAULT_TABLET_SCALE; + tabletScalePercentage = Settings.getValue("hmdTabletScale") || DEFAULT_HMD_TABLET_SCALE; } else { - tabletScalePercentage = Settings.getValue("desktopTabletScale") || DEFAULT_TABLET_SCALE; + tabletScalePercentage = Settings.getValue("desktopTabletScale") || DEFAULT_DESKTOP_TABLET_SCALE; } } return tabletScalePercentage; From e266e2ebbfb4e48d4c1874fe0262ec787283cda8 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 29 Sep 2018 02:44:11 +0200 Subject: [PATCH 253/259] filter getEntity(Multiple)Properties entities unitTest --- .../tests/unit_tests/entityUnitTests.js | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/scripts/developer/tests/unit_tests/entityUnitTests.js b/scripts/developer/tests/unit_tests/entityUnitTests.js index f2c4b4871f..578f2fb75a 100644 --- a/scripts/developer/tests/unit_tests/entityUnitTests.js +++ b/scripts/developer/tests/unit_tests/entityUnitTests.js @@ -72,4 +72,49 @@ describe('Entity', function() { props = Entities.getEntityProperties(boxEntity); expect(props.lastEdited).toBeGreaterThan(prevLastEdited); }); + + it("filtering getEntityProperties seems to work correctly", function() { + var properties = Entities.getEntityProperties(boxEntity, 'position'); + expect(properties.id).toBe(boxEntity); + expect(properties.type).toBe(boxProps.type); + expect(properties.position).toBeDefined(); + }); + + it("filtering getMultipleEntityProperties seems to work correctly", function () { + var sphereProps = { + type: 'Sphere', + color: { + red: 255, + green: 255, + blue: 255, + }, + position: center, + dimensions: { + x: 1, + y: 1, + z: 1, + }, + }; + var sphereEntity = Entities.addEntity(sphereProps); + + var entityIDs = [boxEntity, sphereEntity]; + var filterTypes = 'type'; + var propertySets = Entities.getMultipleEntityProperties(entityIDs, filterTypes); + expect(propertySets.length).toBe(entityIDs.length); + expect(propertySets[0].type).toBe(boxProps.type); + expect(Object.keys(propertySets[0]).length).toBe(1); + expect(propertySets[1].type).toBe(sphereProps.type); + expect(Object.keys(propertySets[1]).length).toBe(1); + + filterTypes = ['id', 'type']; + propertySets = Entities.getMultipleEntityProperties(entityIDs, filterTypes); + expect(propertySets.length).toBe(entityIDs.length); + expect(Object.keys(propertySets[0]).length).toBe(filterTypes.length); + expect(propertySets[0].type).toBe(boxProps.type); + expect(Object.keys(propertySets[1]).length).toBe(filterTypes.length); + expect(propertySets[1].type).toBe(sphereProps.type); + + Entities.deleteEntity(sphereEntity); + }); + }); From 2449868ea9d56138b43293a216c80804b5f6c5af Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 29 Sep 2018 13:51:00 +1200 Subject: [PATCH 254/259] Fix mini tablet sizing for different sizes of avatar --- scripts/system/miniTablet.js | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 4ece210012..fdc00c1ebf 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -277,14 +277,14 @@ Overlays.editOverlay(miniOverlay, { parentID: MyAvatar.SELF_ID, parentJointIndex: handJointIndex(hand), - localPosition: Vec3.multiply(MyAvatar.scale, MINI_POSITIONS[hand]), + localPosition: Vec3.multiply(MyAvatar.sensorToWorldScale, MINI_POSITIONS[hand]), localRotation: MINI_ROTATIONS[hand], dimensions: Vec3.multiply(initialScale, MINI_DIMENSIONS), grabbable: true, visible: true }); Overlays.editOverlay(miniUIOverlay, { - localPosition: Vec3.multiply(MyAvatar.scale, MINI_UI_LOCAL_POSITION), + localPosition: Vec3.multiply(MyAvatar.sensorToWorldScale, MINI_UI_LOCAL_POSITION), localRotation: MINI_UI_LOCAL_ROTATION, dimensions: Vec3.multiply(initialScale, MINI_UI_DIMENSIONS), dpi: MINI_UI_DPI / initialScale, @@ -346,7 +346,8 @@ localRotation, localPosition; - tabletScaleFactor = MyAvatar.scale * (1 + scaleFactor * (miniTargetWidth - miniInitialWidth) / miniInitialWidth); + tabletScaleFactor = MyAvatar.sensorToWorldScale + * (1 + scaleFactor * (miniTargetWidth - miniInitialWidth) / miniInitialWidth); dimensions = Vec3.multiply(tabletScaleFactor, MINI_DIMENSIONS); localRotation = Quat.mix(miniExpandLocalRotation, miniTargetLocalRotation, scaleFactor); localPosition = @@ -444,7 +445,7 @@ function create() { miniOverlay = Overlays.addOverlay("model", { url: MINI_MODEL, - dimensions: Vec3.multiply(MyAvatar.scale, MINI_DIMENSIONS), + dimensions: Vec3.multiply(MyAvatar.sensorToWorldScale, MINI_DIMENSIONS), solid: true, grabbable: true, showKeyboardFocusHighlight: false, @@ -454,10 +455,10 @@ miniUIOverlay = Overlays.addOverlay("web3d", { url: MINI_UI_HTML, parentID: miniOverlay, - localPosition: Vec3.multiply(MyAvatar.scale, MINI_UI_LOCAL_POSITION), + localPosition: Vec3.multiply(MyAvatar.sensorToWorldScale, MINI_UI_LOCAL_POSITION), localRotation: MINI_UI_LOCAL_ROTATION, - dimensions: Vec3.multiply(MyAvatar.scale, MINI_UI_DIMENSIONS), - dpi: MINI_UI_DPI / MyAvatar.scale, + dimensions: Vec3.multiply(MyAvatar.sensorToWorldScale, MINI_UI_DIMENSIONS), + dpi: MINI_UI_DPI / MyAvatar.sensorToWorldScale, alpha: 0, // Hide overlay while its content is being created. grabbable: false, showKeyboardFocusHighlight: false, @@ -641,7 +642,7 @@ handOrientation = Quat.multiply(MyAvatar.orientation, MyAvatar.getAbsoluteJointRotationInObjectFrame(jointIndex)); uiPositionAndOrientation = ui.getUIPositionAndRotation(hand); - miniPosition = Vec3.sum(handPosition, Vec3.multiply(MyAvatar.scale, + miniPosition = Vec3.sum(handPosition, Vec3.multiply(MyAvatar.sensorToWorldScale, Vec3.multiplyQbyV(handOrientation, uiPositionAndOrientation.position))); miniOrientation = Quat.multiply(handOrientation, uiPositionAndOrientation.rotation); miniToCameraDirection = Vec3.normalize(Vec3.subtract(Camera.position, miniPosition)); @@ -698,7 +699,7 @@ function scaleMiniDown() { var scaleFactor = (Date.now() - miniScaleStart) / MINI_SCALE_DURATION; if (scaleFactor < 1) { - ui.size((1 - scaleFactor) * MyAvatar.scale); + ui.size((1 - scaleFactor) * MyAvatar.sensorToWorldScale); miniScaleTimer = Script.setTimeout(scaleMiniDown, MINI_SCALE_TIMEOUT); return; } @@ -727,12 +728,12 @@ function scaleMiniUp() { var scaleFactor = (Date.now() - miniScaleStart) / MINI_SCALE_DURATION; if (scaleFactor < 1) { - ui.size(scaleFactor * MyAvatar.scale); + ui.size(scaleFactor * MyAvatar.sensorToWorldScale); miniScaleTimer = Script.setTimeout(scaleMiniUp, MINI_SCALE_TIMEOUT); return; } miniScaleTimer = null; - ui.size(MyAvatar.scale); + ui.size(MyAvatar.sensorToWorldScale); setState(MINI_VISIBLE); } From 749b02cfb80527188ff2bbe38df72d63b51a2270 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 30 Sep 2018 15:12:49 -0700 Subject: [PATCH 255/259] get asan to not complain about invalid downcast of CauterizedMeshPartPayload UpdateFunctor --- libraries/render-utils/src/CauterizedModel.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index ffb652f923..2f1ad445fd 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -226,8 +226,9 @@ void CauterizedModel::updateRenderItems() { bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex); bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning(); - transaction.updateItem(itemID, [modelTransform, meshState, useDualQuaternionSkinning, cauterizedMeshState, invalidatePayloadShapeKey, - isWireframe, renderItemKeyGlobalFlags, enableCauterization](CauterizedMeshPartPayload& data) { + transaction.updateItem(itemID, [modelTransform, meshState, useDualQuaternionSkinning, cauterizedMeshState, invalidatePayloadShapeKey, + isWireframe, renderItemKeyGlobalFlags, enableCauterization](ModelMeshPartPayload& mmppData) { + CauterizedMeshPartPayload& data = dynamic_cast(mmppData); if (useDualQuaternionSkinning) { data.updateClusterBuffer(meshState.clusterDualQuaternions, cauterizedMeshState.clusterDualQuaternions); From f784b33eed24cba47afcc7bc7b9e6ad09d6a293b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 30 Sep 2018 15:25:21 -0700 Subject: [PATCH 256/259] get asan to not complain about invalid downcast of EntityRenderer UpdateFunctor --- libraries/entities-renderer/src/RenderableEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index a183101fff..aa335bb7d5 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -305,7 +305,7 @@ void EntityRenderer::updateInScene(const ScenePointer& scene, Transaction& trans } doRenderUpdateSynchronous(scene, transaction, _entity); - transaction.updateItem(_renderItemID, [this](EntityRenderer& self) { + transaction.updateItem(_renderItemID, [this](PayloadProxyInterface& self) { if (!isValidRenderItem()) { return; } From e30497595ca2fec35841d6be293cd87faa827da7 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 30 Sep 2018 15:49:42 -0700 Subject: [PATCH 257/259] static_cast can be used here rather than dynamic --- libraries/render-utils/src/CauterizedModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 2f1ad445fd..353942ed49 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -228,7 +228,7 @@ void CauterizedModel::updateRenderItems() { transaction.updateItem(itemID, [modelTransform, meshState, useDualQuaternionSkinning, cauterizedMeshState, invalidatePayloadShapeKey, isWireframe, renderItemKeyGlobalFlags, enableCauterization](ModelMeshPartPayload& mmppData) { - CauterizedMeshPartPayload& data = dynamic_cast(mmppData); + CauterizedMeshPartPayload& data = static_cast(mmppData); if (useDualQuaternionSkinning) { data.updateClusterBuffer(meshState.clusterDualQuaternions, cauterizedMeshState.clusterDualQuaternions); From 90d9af35b59f596e5dd05eb15151dc3b1b0efb67 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 30 Sep 2018 16:05:08 -0700 Subject: [PATCH 258/259] minimize diff --- libraries/render-utils/src/CauterizedModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 353942ed49..d52b7854ea 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -227,7 +227,7 @@ void CauterizedModel::updateRenderItems() { bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning(); transaction.updateItem(itemID, [modelTransform, meshState, useDualQuaternionSkinning, cauterizedMeshState, invalidatePayloadShapeKey, - isWireframe, renderItemKeyGlobalFlags, enableCauterization](ModelMeshPartPayload& mmppData) { + isWireframe, renderItemKeyGlobalFlags, enableCauterization](ModelMeshPartPayload& mmppData) { CauterizedMeshPartPayload& data = static_cast(mmppData); if (useDualQuaternionSkinning) { data.updateClusterBuffer(meshState.clusterDualQuaternions, From eeda7400c99b1c61472e06d9642ae93b1878e361 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 1 Oct 2018 19:36:53 +0200 Subject: [PATCH 259/259] check if id matches in filtered getMultipleEntityProperties --- scripts/developer/tests/unit_tests/entityUnitTests.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/developer/tests/unit_tests/entityUnitTests.js b/scripts/developer/tests/unit_tests/entityUnitTests.js index 578f2fb75a..004cf48fe5 100644 --- a/scripts/developer/tests/unit_tests/entityUnitTests.js +++ b/scripts/developer/tests/unit_tests/entityUnitTests.js @@ -110,8 +110,10 @@ describe('Entity', function() { propertySets = Entities.getMultipleEntityProperties(entityIDs, filterTypes); expect(propertySets.length).toBe(entityIDs.length); expect(Object.keys(propertySets[0]).length).toBe(filterTypes.length); + expect(propertySets[0].id).toBe(boxEntity); expect(propertySets[0].type).toBe(boxProps.type); expect(Object.keys(propertySets[1]).length).toBe(filterTypes.length); + expect(propertySets[1].id).toBe(sphereEntity); expect(propertySets[1].type).toBe(sphereProps.type); Entities.deleteEntity(sphereEntity);