diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 46ca51219d..32c944f5b8 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -448,13 +448,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // or that somehow we haven't sent if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) { ++numAvatarsHeldBack; - - // BUGZ-781 verbose debugging: - auto usecLastTimeSent = destinationNodeData->getLastOtherAvatarEncodeTime(sourceAvatarNodeData->getNodeLocalID()); - if (usecLastTimeSent != 0 && startIgnoreCalculation - usecLastTimeSent > 10 * USECS_PER_SECOND) { - qCDebug(avatars) << "Not sent avatar" << *sourceAvatarNode << "to Node" << *destinationNode << "in > 10 s"; - } - sendAvatar = false; } else if (lastSeqFromSender == 0) { // We have have not yet received any data about this avatar. Ignore it for now diff --git a/cmake/externals/wasapi/CMakeLists.txt b/cmake/externals/wasapi/CMakeLists.txt index 9e22e28f58..18d93bde40 100644 --- a/cmake/externals/wasapi/CMakeLists.txt +++ b/cmake/externals/wasapi/CMakeLists.txt @@ -6,8 +6,8 @@ if (WIN32) include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL https://public.highfidelity.com/dependencies/qtaudio_wasapi11.zip - URL_MD5 d0eb8489455e7f79d59155535a2c8861 + URL https://public.highfidelity.com/dependencies/qtaudio_wasapi12.zip + URL_MD5 9e2eef41165f85344808f754b48bf08d CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" diff --git a/cmake/ports/bullet3/bullet-git-fix-build-clang-8.patch b/cmake/ports/bullet3/bullet-git-fix-build-clang-8.patch new file mode 100644 index 0000000000..bdf106b38e --- /dev/null +++ b/cmake/ports/bullet3/bullet-git-fix-build-clang-8.patch @@ -0,0 +1,36 @@ +From 7638b7c5a659dceb4e580ae87d4d60b00847ef94 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Emil=20Nord=C3=A9n?= +Date: Sat, 4 May 2019 08:38:53 +0200 +Subject: [PATCH] fixed build on latest version of clang + +--- + src/Bullet3Common/b3Vector3.h | 2 +- + src/LinearMath/btVector3.h | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/Bullet3Common/b3Vector3.h b/src/Bullet3Common/b3Vector3.h +index 56e6c13311..a70d68d6e1 100644 +--- a/src/Bullet3Common/b3Vector3.h ++++ b/src/Bullet3Common/b3Vector3.h +@@ -36,7 +36,7 @@ subject to the following restrictions: + #pragma warning(disable : 4556) // value of intrinsic immediate argument '4294967239' is out of range '0 - 255' + #endif + +-#define B3_SHUFFLE(x, y, z, w) ((w) << 6 | (z) << 4 | (y) << 2 | (x)) ++#define B3_SHUFFLE(x, y, z, w) (((w) << 6 | (z) << 4 | (y) << 2 | (x)) & 0xff) + //#define b3_pshufd_ps( _a, _mask ) (__m128) _mm_shuffle_epi32((__m128i)(_a), (_mask) ) + #define b3_pshufd_ps(_a, _mask) _mm_shuffle_ps((_a), (_a), (_mask)) + #define b3_splat3_ps(_a, _i) b3_pshufd_ps((_a), B3_SHUFFLE(_i, _i, _i, 3)) +diff --git a/src/LinearMath/btVector3.h b/src/LinearMath/btVector3.h +index 61fd8d1e46..d65ed9808d 100644 +--- a/src/LinearMath/btVector3.h ++++ b/src/LinearMath/btVector3.h +@@ -36,7 +36,7 @@ subject to the following restrictions: + #pragma warning(disable : 4556) // value of intrinsic immediate argument '4294967239' is out of range '0 - 255' + #endif + +-#define BT_SHUFFLE(x, y, z, w) ((w) << 6 | (z) << 4 | (y) << 2 | (x)) ++#define BT_SHUFFLE(x, y, z, w) (((w) << 6 | (z) << 4 | (y) << 2 | (x)) & 0xff) + //#define bt_pshufd_ps( _a, _mask ) (__m128) _mm_shuffle_epi32((__m128i)(_a), (_mask) ) + #define bt_pshufd_ps(_a, _mask) _mm_shuffle_ps((_a), (_a), (_mask)) + #define bt_splat3_ps(_a, _i) bt_pshufd_ps((_a), BT_SHUFFLE(_i, _i, _i, 3)) diff --git a/cmake/ports/bullet3/portfile.cmake b/cmake/ports/bullet3/portfile.cmake index 32713f5a46..cda39ed349 100644 --- a/cmake/ports/bullet3/portfile.cmake +++ b/cmake/ports/bullet3/portfile.cmake @@ -27,6 +27,7 @@ vcpkg_from_github( REF ab8f16961e19a86ee20c6a1d61f662392524cc77 SHA512 927742db29867517283d45e475f0c534a9a57e165cae221f26e08e88057253a1682ac9919b2dc547b9cf388ba0b931b175623461d44f28c9184796ba90b1ed55 HEAD_REF master + PATCHES "bullet-git-fix-build-clang-8.patch" ) vcpkg_configure_cmake( diff --git a/hifi_vcpkg.py b/hifi_vcpkg.py index 821d9ae0b7..7bb261faa0 100644 --- a/hifi_vcpkg.py +++ b/hifi_vcpkg.py @@ -134,7 +134,7 @@ endif() downloadVcpkg = True if not downloadVcpkg and not os.path.isfile(self.exe): - print("Missing executable, boostrapping") + print("Missing executable, boot-strapping") downloadVcpkg = True # Make sure we have a vcpkg executable diff --git a/interface/resources/avatar/avatar-animation.json b/interface/resources/avatar/avatar-animation.json index 1c3a603eb5..b1e3b0e978 100644 --- a/interface/resources/avatar/avatar-animation.json +++ b/interface/resources/avatar/avatar-animation.json @@ -2478,8 +2478,8 @@ { "easingType": "easeInOutQuad", "id": "talk", - "interpDuration": 1, - "interpTarget": 1, + "interpDuration": 20, + "interpTarget": 20, "interpType": "evaluateBoth", "priority": 0.33, "resume": true, @@ -2489,8 +2489,8 @@ { "easingType": "easeInOutQuad", "id": "talk02", - "interpDuration": 1, - "interpTarget": 1, + "interpDuration": 20, + "interpTarget": 20, "interpType": "evaluateBoth", "priority": 0.33, "resume": true, @@ -2500,8 +2500,8 @@ { "easingType": "easeInOutQuad", "id": "talk03", - "interpDuration": 1, - "interpTarget": 1, + "interpDuration": 20, + "interpTarget": 20, "interpType": "evaluateBoth", "priority": 0.33, "resume": true, @@ -2511,8 +2511,8 @@ { "easingType": "easeInOutQuad", "id": "talk04", - "interpDuration": 1, - "interpTarget": 1, + "interpDuration": 20, + "interpTarget": 20, "interpType": "evaluateBoth", "priority": 0.33, "resume": true, @@ -2522,8 +2522,8 @@ { "easingType": "easeInOutQuad", "id": "talk_armsdown", - "interpDuration": 1, - "interpTarget": 1, + "interpDuration": 20, + "interpTarget": 20, "interpType": "evaluateBoth", "priority": 0.33, "resume": true, @@ -2533,8 +2533,8 @@ { "easingType": "easeInOutQuad", "id": "talk_lefthand", - "interpDuration": 1, - "interpTarget": 1, + "interpDuration": 20, + "interpTarget": 20, "interpType": "evaluateBoth", "priority": 0.33, "resume": true, @@ -2544,8 +2544,8 @@ { "easingType": "easeInOutQuad", "id": "talk_righthand", - "interpDuration": 1, - "interpTarget": 1, + "interpDuration": 20, + "interpTarget": 20, "interpType": "evaluateBoth", "priority": 0.33, "resume": true, @@ -5242,62 +5242,6 @@ "interpTarget": 6, "interpType": "evaluateBoth", "transitions": [ - { - "state": "idle", - "var": "isNotInput" - }, - { - "state": "WALKFWD", - "var": "isInputForward" - }, - { - "state": "WALKBWD", - "var": "isInputBackward" - }, - { - "state": "STRAFERIGHT", - "var": "isInputRight" - }, - { - "state": "STRAFELEFT", - "var": "isInputLeft" - }, - { - "state": "turnRight", - "var": "isTurningRight" - }, - { - "state": "turnLeft", - "var": "isTurningLeft" - }, - { - "state": "fly", - "var": "isFlying" - }, - { - "state": "takeoffStand", - "var": "isTakeoffStand" - }, - { - "state": "TAKEOFFRUN", - "var": "isTakeoffRun" - }, - { - "state": "inAirStand", - "var": "isInAirStand" - }, - { - "state": "INAIRRUN", - "var": "isInAirRun" - }, - { - "state": "strafeRightHmd", - "var": "isMovingRightHmd" - }, - { - "state": "strafeLeftHmd", - "var": "isMovingLeftHmd" - }, { "state": "idle", "var": "isNotSeated" @@ -5307,7 +5251,7 @@ { "easingType": "easeInOutQuad", "id": "idle", - "interpDuration": 20, + "interpDuration": 15, "interpTarget": 20, "interpType": "evaluateBoth", "transitions": [ @@ -5439,8 +5383,8 @@ { "easingType": "easeInOutQuad", "id": "idleSettle", - "interpDuration": 15, - "interpTarget": 15, + "interpDuration": 13, + "interpTarget": 14, "interpType": "snapshotPrev", "transitions": [ { @@ -5533,7 +5477,7 @@ "transitions": [ { "state": "idleSettle", - "var": "isNotInput" + "var": "isNotInputSlow" }, { "state": "WALKBWD", @@ -5597,7 +5541,7 @@ "transitions": [ { "state": "idleSettle", - "var": "isNotInput" + "var": "isNotInputSlow" }, { "state": "WALKFWD", @@ -5661,7 +5605,7 @@ "transitions": [ { "state": "idleSettle", - "var": "isNotInput" + "var": "isNotInputSlow" }, { "state": "WALKFWD", @@ -5725,7 +5669,7 @@ "transitions": [ { "state": "idleSettle", - "var": "isNotInput" + "var": "isNotInputSlow" }, { "state": "WALKFWD", diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index eb07c9a6dd..d6ecc540c2 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -78,6 +78,15 @@ "to": "Actions.Yaw" }, + { "from": { "makeAxis" : [ + ["Keyboard.Left"], + ["Keyboard.Right"] + ] + }, + "when": ["Application.CameraFirstPersonLookat", "!Keyboard.Shift"], + "to": "Actions.Yaw" + }, + { "from": { "makeAxis" : [ ["Keyboard.Left"], ["Keyboard.Right"] @@ -113,7 +122,16 @@ "when": ["Application.CameraFirstPerson", "!Keyboard.Control"], "to": "Actions.Yaw" }, - + + { "from": { "makeAxis" : [ + ["Keyboard.A"], + ["Keyboard.D"] + ] + }, + "when": ["Application.CameraFirstPersonLookat", "!Keyboard.Control"], + "to": "Actions.Yaw" + }, + { "from": { "makeAxis" : [ ["Keyboard.A"], ["Keyboard.D"] @@ -149,6 +167,15 @@ "when": "Application.CameraFirstPerson", "to": "Actions.Yaw" }, + + { "from": { "makeAxis" : [ + ["Keyboard.TouchpadLeft"], + ["Keyboard.TouchpadRight"] + ] + }, + "when": "Application.CameraFirstPersonLookat", + "to": "Actions.Yaw" + }, { "from": { "makeAxis" : [ ["Keyboard.TouchpadLeft"], @@ -222,10 +249,12 @@ { "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, { "from": "Keyboard.Up", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_FORWARD" }, + { "from": "Keyboard.Up", "when": "Application.CameraFirstPersonLookat", "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.Up", "when": "Application.CameraThirdPerson", "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.Up", "when": "Application.CameraLookAt", "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.Up", "when": "Application.CameraSelfie", "to": "Actions.LONGITUDINAL_BACKWARD" }, { "from": "Keyboard.Down", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_BACKWARD" }, + { "from": "Keyboard.Down", "when": "Application.CameraFirstPersonLookat", "to": "Actions.LONGITUDINAL_BACKWARD" }, { "from": "Keyboard.Down", "when": "Application.CameraThirdPerson", "to": "Actions.LONGITUDINAL_BACKWARD" }, { "from": "Keyboard.Down", "when": "Application.CameraLookAt", "to": "Actions.LONGITUDINAL_BACKWARD" }, { "from": "Keyboard.Down", "when": "Application.CameraSelfie", "to": "Actions.LONGITUDINAL_FORWARD" }, diff --git a/interface/resources/qml/Web3DSurface.qml b/interface/resources/qml/Web3DSurface.qml index ff574ceaa5..3340226761 100644 --- a/interface/resources/qml/Web3DSurface.qml +++ b/interface/resources/qml/Web3DSurface.qml @@ -33,6 +33,12 @@ Item { property var item: null function load(url, scriptUrl) { + // Ensure we reset any existing item to "about:blank" to ensure web audio stops: DEV-2375 + if (root.item != null) { + root.item.url = "about:blank" + root.item.destroy() + root.item = null + } QmlSurface.load("./controls/WebView.qml", root, function(newItem) { root.item = newItem root.item.url = url diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 03fbbb178e..85e5211649 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -580,8 +580,9 @@ Rectangle { sendToScript(msg); } else if (msg.method === "showInvalidatedLightbox") { lightboxPopup.titleText = "Item Invalidated"; - lightboxPopup.bodyText = 'Your item is marked "invalidated" because this item has been suspended ' + - "from the Marketplace due to a claim against its author."; + lightboxPopup.bodyText = 'This item has been invalidated and is no longer available.
' + + 'If you have questions, please contact marketplace@highfidelity.com.
' + + 'Thank you!'; lightboxPopup.button1text = "CLOSE"; lightboxPopup.button1method = function() { lightboxPopup.visible = false; diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/AvatarApp.qml b/interface/resources/qml/hifi/simplifiedUI/avatarApp/AvatarApp.qml index d52dd3f3d7..7dbadc59f4 100644 --- a/interface/resources/qml/hifi/simplifiedUI/avatarApp/AvatarApp.qml +++ b/interface/resources/qml/hifi/simplifiedUI/avatarApp/AvatarApp.qml @@ -9,6 +9,7 @@ // import QtQuick 2.10 +import QtQuick.Layouts 1.3 import "../simplifiedConstants" as SimplifiedConstants import "../simplifiedControls" as SimplifiedControls import "./components" as AvatarAppComponents @@ -79,7 +80,11 @@ Rectangle { errorText.text = "There was a problem while retrieving your inventory. " + "Please try closing and re-opening the Avatar app.\n\nInventory status: " + result.status + "\nMessage: " + result.message; } else if (result.data && result.data.assets && result.data.assets.length === 0 && avatarAppInventoryModel.count === 0) { - errorText.text = "You have not created any avatars yet! Create an avatar with the Avatar Creator, then close and re-open the Avatar App." + emptyInventoryContainer.visible = true; + } + + if (Settings.getValue("simplifiedUI/debugFTUE", 0) === 4) { + emptyInventoryContainer.visible = true; } avatarAppInventoryModel.handlePage(result.status !== "success" && result.message, result); @@ -140,8 +145,95 @@ Rectangle { anchors.rightMargin: 24 } + + Item { + id: emptyInventoryContainer + visible: false + anchors.top: displayNameHeader.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + Flickable { + id: emptyInventoryFlickable + anchors.fill: parent + contentWidth: parent.width + contentHeight: emptyInventoryLayout.height + clip: true + + ColumnLayout { + id: emptyInventoryLayout + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: 26 + anchors.right: parent.right + anchors.rightMargin: 26 + spacing: 0 + + HifiStylesUit.GraphikSemiBold { + text: "Stand out from the crowd!" + Layout.preferredWidth: parent.width + Layout.preferredHeight: paintedHeight + Layout.topMargin: 16 + size: 28 + color: simplifiedUI.colors.text.white + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + } + + HifiStylesUit.GraphikRegular { + text: "Create your custom avatar." + Layout.preferredWidth: parent.width + Layout.preferredHeight: paintedHeight + Layout.topMargin: 2 + size: 18 + wrapMode: Text.Wrap + color: simplifiedUI.colors.text.white + horizontalAlignment: Text.AlignHCenter + } + + Image { + id: avatarImage; + source: "images/avatarProfilePic.png" + Layout.preferredWidth: parent.width + Layout.preferredHeight: 450 + Layout.alignment: Qt.AlignHCenter + mipmap: true + fillMode: Image.PreserveAspectFit + } + + Image { + source: "images/qrCode.jpg" + Layout.preferredWidth: 190 + Layout.preferredHeight: 190 + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: -160 + mipmap: true + fillMode: Image.PreserveAspectFit + } + + HifiStylesUit.GraphikSemiBold { + text: "Scan for Mobile App" + Layout.preferredWidth: parent.width + Layout.preferredHeight: paintedHeight + Layout.topMargin: 12 + size: 28 + color: simplifiedUI.colors.text.white + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + } + } + } + + SimplifiedControls.VerticalScrollBar { + parent: emptyInventoryFlickable + } + } + + Item { id: avatarInfoTextContainer + visible: !emptyInventoryContainer.visible width: parent.implicitWidth height: childrenRect.height anchors.top: displayNameHeader.bottom @@ -164,7 +256,7 @@ Rectangle { id: yourAvatarsSubtitle text: "These are the avatars that you've created and uploaded via the Avatar Creator." width: parent.width - wrapMode: Text.WordWrap + wrapMode: Text.Wrap anchors.top: yourAvatarsTitle.bottom anchors.topMargin: 6 verticalAlignment: TextInput.AlignVCenter @@ -208,9 +300,10 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom + visible: !emptyInventoryContainer.visible AnimatedImage { - visible: !inventoryContentsList.visible && !errorText.visible + visible: !(inventoryContentsList.visible || errorText.visible) anchors.centerIn: parent width: 72 height: width @@ -271,6 +364,8 @@ Rectangle { return; } } + + root.avatarPreviewUrl = "../../images/defaultAvatar.svg"; } function fromScript(message) { diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/avatarProfilePic.png b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/avatarProfilePic.png new file mode 100644 index 0000000000..b59ebb3085 Binary files /dev/null and b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/avatarProfilePic.png differ diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/hero.png b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/hero.png new file mode 100644 index 0000000000..15c358e024 Binary files /dev/null and b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/hero.png differ diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/qrCode.jpg b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/qrCode.jpg new file mode 100644 index 0000000000..0674781c45 Binary files /dev/null and b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/qrCode.jpg differ diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Blue.png b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Blue.png new file mode 100644 index 0000000000..210d37acb6 Binary files /dev/null and b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Blue.png differ diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Cyan.png b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Cyan.png new file mode 100644 index 0000000000..726e1c8a69 Binary files /dev/null and b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Cyan.png differ diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Green.png b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Green.png new file mode 100644 index 0000000000..6db55816dc Binary files /dev/null and b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Green.png differ diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Magenta.png b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Magenta.png new file mode 100644 index 0000000000..f01622dcd0 Binary files /dev/null and b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Magenta.png differ diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Red.png b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Red.png new file mode 100644 index 0000000000..1e308d7f1f Binary files /dev/null and b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Red.png differ diff --git a/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Yellow.png b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Yellow.png new file mode 100644 index 0000000000..b123f358c0 Binary files /dev/null and b/interface/resources/qml/hifi/simplifiedUI/avatarApp/images/simplifiedAvatar_Yellow.png differ diff --git a/interface/resources/qml/hifi/simplifiedUI/settingsApp/general/General.qml b/interface/resources/qml/hifi/simplifiedUI/settingsApp/general/General.qml index 7a98849b95..6a59816af8 100644 --- a/interface/resources/qml/hifi/simplifiedUI/settingsApp/general/General.qml +++ b/interface/resources/qml/hifi/simplifiedUI/settingsApp/general/General.qml @@ -225,9 +225,9 @@ Flickable { SimplifiedControls.RadioButton { id: firstPerson text: "First Person View" - checked: Camera.mode === "first person" + checked: Camera.mode === "first person look at" onClicked: { - Camera.mode = "first person" + Camera.mode = "first person look at" } } @@ -254,7 +254,7 @@ Flickable { target: Camera onModeUpdated: { - if (Camera.mode === "first person") { + if (Camera.mode === "first person look at") { firstPerson.checked = true } else if (Camera.mode === "look at") { thirdPerson.checked = true diff --git a/interface/resources/qml/hifi/simplifiedUI/topBar/SimplifiedTopBar.qml b/interface/resources/qml/hifi/simplifiedUI/topBar/SimplifiedTopBar.qml index c92da7e6e5..65a5eb0c80 100644 --- a/interface/resources/qml/hifi/simplifiedUI/topBar/SimplifiedTopBar.qml +++ b/interface/resources/qml/hifi/simplifiedUI/topBar/SimplifiedTopBar.qml @@ -54,8 +54,8 @@ Rectangle { if ((MyAvatar.skeletonModelURL.indexOf("defaultAvatar") > -1 || MyAvatar.skeletonModelURL.indexOf("fst") === -1) && topBarInventoryModel.count > 0) { - Settings.setValue("simplifiedUI/alreadyAutoSelectedAvatar", true); - MyAvatar.useFullAvatarURL = topBarInventoryModel.get(0).download_url; + Settings.setValue("simplifiedUI/alreadyAutoSelectedAvatarFromInventory", true); + MyAvatar.useFullAvatarURL(topBarInventoryModel.get(0).download_url); } } } @@ -71,7 +71,7 @@ Rectangle { if (isLoggedIn) { Commerce.getWalletStatus(); } else { - // Show some error to the user + // Show some error to the user in the UI? } } @@ -113,12 +113,68 @@ Rectangle { topBarInventoryModel.getNextPage(); } else { inventoryFullyReceived = true; + var scriptExecutionCount = Settings.getValue("simplifiedUI/SUIScriptExecutionCount"); + var currentAvatarURL = MyAvatar.skeletonModelURL; + var currentAvatarURLContainsDefaultAvatar = currentAvatarURL.indexOf("defaultAvatar") > -1; + var currentAvatarURLContainsFST = currentAvatarURL.indexOf("fst") > -1; + var currentAvatarURLContainsSimplifiedAvatar = currentAvatarURL.indexOf("simplifiedAvatar") > -1; + var alreadyAutoSelectedAvatarFromInventory = Settings.getValue("simplifiedUI/alreadyAutoSelectedAvatarFromInventory", false); + var userHasValidAvatarInInventory = topBarInventoryModel.count > 0 && + topBarInventoryModel.get(0).download_url.indexOf(".fst") > -1; + var simplifiedAvatarPrefix = "https://content.highfidelity.com/Experiences/Releases/simplifiedUI/simplifiedFTUE/avatars/simplifiedAvatar_"; + var simplifiedAvatarColors = ["Blue", "Cyan", "Green", "Magenta", "Red"]; + var simplifiedAvatarSuffix = "/avatar.fst"; - // If we have an avatar in our inventory AND we haven't already auto-selected an avatar... - if ((!Settings.getValue("simplifiedUI/alreadyAutoSelectedAvatar", false) || - MyAvatar.skeletonModelURL.indexOf("defaultAvatar") > -1 || MyAvatar.skeletonModelURL.indexOf("fst") === -1) && topBarInventoryModel.count > 0) { - Settings.setValue("simplifiedUI/alreadyAutoSelectedAvatar", true); - MyAvatar.skeletonModelURL = topBarInventoryModel.get(0).download_url; + // Use `Settings.setValue("simplifiedUI/debugFTUE", 0);` to turn off FTUE Debug Mode. + // Use `Settings.setValue("simplifiedUI/debugFTUE", 1);` to debug FTUE Screen 1. + // Use `Settings.setValue("simplifiedUI/debugFTUE", 2);` to debug FTUE Screen 2. + // Use `Settings.setValue("simplifiedUI/debugFTUE", 3);` to debug FTUE Screen 3. + // Use `Settings.setValue("simplifiedUI/debugFTUE", 4);` to force the UI to show what would happen if the user had an empty Inventory. + + var debugFTUE = Settings.getValue("simplifiedUI/debugFTUE", 0); + if (debugFTUE === 1 || debugFTUE === 2) { + scriptExecutionCount = 1; + currentAvatarURLContainsDefaultAvatar = true; + if (debugFTUE === 1) { + userHasValidAvatarInInventory = false; + currentAvatarURLContainsSimplifiedAvatar = false; + } + } else if (debugFTUE === 3) { + scriptExecutionCount = 2; + currentAvatarURLContainsDefaultAvatar = false; + currentAvatarURLContainsSimplifiedAvatar = true; + } + + // If we have never auto-selected and the user is still using a default avatar or if the current avatar is not valid (fst), or if + // the current avatar is the old default (Woody), use top avatar from inventory or one of the new defaults. + + // If the current avatar URL is invalid, OR the user is using the "default avatar" (Woody)... + if (!currentAvatarURLContainsFST || currentAvatarURLContainsDefaultAvatar) { + // If the user has a valid avatar in their inventory... + if (userHasValidAvatarInInventory) { + // ...use the first avatar in the user's inventory. + MyAvatar.useFullAvatarURL(topBarInventoryModel.get(0).download_url); + Settings.setValue("simplifiedUI/alreadyAutoSelectedAvatarFromInventory", true); + // Else if the user isn't wearing a "Simplified Avatar" + } else if (!currentAvatarURLContainsSimplifiedAvatar) { + // ...assign to the user a new "Simplified Avatar" (i.e. a simple avatar of random color) + var avatarColor = simplifiedAvatarColors[Math.floor(Math.random() * simplifiedAvatarColors.length)]; + var simplifiedAvatarModelURL = simplifiedAvatarPrefix + avatarColor + simplifiedAvatarSuffix; + MyAvatar.useFullAvatarURL(simplifiedAvatarModelURL); + currentAvatarURLContainsSimplifiedAvatar = true; + } + } + + if (scriptExecutionCount === 1) { + sendToScript({ + "source": "SimplifiedTopBar.qml", + "method": "displayInitialLaunchWindow" + }); + } else if (scriptExecutionCount === 2 && currentAvatarURLContainsSimplifiedAvatar) { + sendToScript({ + "source": "SimplifiedTopBar.qml", + "method": "displaySecondLaunchWindow" + }); } } } @@ -384,6 +440,7 @@ Rectangle { placeholderTextColor: "#8E8E8E" font.pixelSize: 14 placeholderText: width - leftPadding - rightPadding < goToTextFieldMetrics.width ? shortPlaceholderText : longPlaceholderText + blankPlaceholderTextOnFocus: false clip: true selectByMouse: true autoScroll: true @@ -555,7 +612,7 @@ Rectangle { } - function updatePreviewUrl() { + function updatePreviewUrl() { var previewUrl = ""; var downloadUrl = ""; for (var i = 0; i < topBarInventoryModel.count; ++i) { @@ -569,6 +626,8 @@ Rectangle { return; } } + + avatarButtonImage.source = "../images/defaultAvatar.svg"; } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 421741b0a2..994c9c7fd3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -718,6 +718,7 @@ private: static const QString STATE_IN_HMD = "InHMD"; static const QString STATE_CAMERA_FULL_SCREEN_MIRROR = "CameraFSM"; static const QString STATE_CAMERA_FIRST_PERSON = "CameraFirstPerson"; +static const QString STATE_CAMERA_FIRST_PERSON_LOOK_AT = "CameraFirstPersonLookat"; static const QString STATE_CAMERA_THIRD_PERSON = "CameraThirdPerson"; static const QString STATE_CAMERA_ENTITY = "CameraEntity"; static const QString STATE_CAMERA_INDEPENDENT = "CameraIndependent"; @@ -939,7 +940,8 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR, - STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_THIRD_PERSON, STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, STATE_CAMERA_LOOK_AT, STATE_CAMERA_SELFIE, + STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_FIRST_PERSON_LOOK_AT, STATE_CAMERA_THIRD_PERSON, + STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, STATE_CAMERA_LOOK_AT, STATE_CAMERA_SELFIE, STATE_SNAP_TURN, STATE_ADVANCED_MOVEMENT_CONTROLS, STATE_GROUNDED, STATE_NAV_FOCUSED, STATE_PLATFORM_WINDOWS, STATE_PLATFORM_MAC, STATE_PLATFORM_ANDROID, STATE_LEFT_HAND_DOMINANT, STATE_RIGHT_HAND_DOMINANT, STATE_STRAFE_ENABLED } }); DependencyManager::set(); @@ -974,6 +976,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { QObject::connect(PlatformHelper::instance(), &PlatformHelper::systemWillWake, [] { QMetaObject::invokeMethod(DependencyManager::get().data(), "noteAwakening", Qt::QueuedConnection); + QMetaObject::invokeMethod(DependencyManager::get().data(), "noteAwakening", Qt::QueuedConnection); }); @@ -1886,6 +1889,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _applicationStateDevice->setInputVariant(STATE_CAMERA_FIRST_PERSON, []() -> float { return qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON ? 1 : 0; }); + _applicationStateDevice->setInputVariant(STATE_CAMERA_FIRST_PERSON_LOOK_AT, []() -> float { + return qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON_LOOK_AT ? 1 : 0; + }); _applicationStateDevice->setInputVariant(STATE_CAMERA_THIRD_PERSON, []() -> float { return qApp->getCamera().getMode() == CAMERA_MODE_THIRD_PERSON ? 1 : 0; }); @@ -1995,7 +2001,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo settingsTimer->start(); }, QThread::LowestPriority); - if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) { + if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPersonLookAt)) { getMyAvatar()->setBoomLength(MyAvatar::ZOOM_MIN); // So that camera doesn't auto-switch to third person. } @@ -2958,13 +2964,29 @@ Application::~Application() { qInstallMessageHandler(LogHandler::verboseMessageHandler); #ifdef Q_OS_MAC + // 10/16/2019 - Disabling this call. This causes known crashes (A), and it is not + // fully understood whether it might cause other unknown crashes (B). + // + // (A) Although we try to shutdown the ScriptEngine threads in onAboutToQuit, there is + // currently no guarantee that they have stopped. Waiting on them to stop has so far appeared to + // never return on Mac, causing the application to hang on shutdown. Because ScriptEngines + // may still be running, they may end up receiving events that are triggered from this processEvents call, + // and then try to access resources that are no longer available at this point in time. + // If the ScriptEngine threads were fully destroyed before getting here, this would + // not be an issue. + // + // (B) It seems likely that a bunch of potential event handlers are dependent on Application + // and other common dependencies to be available and not destroyed or in the middle of being + // destroyed. + + // Clear the event queue before application is totally destructed. // This will drain the messasge queue of pending "deleteLaters" queued up // during shutdown of the script engines. // We do this here because there is a possiblty that [NSApplication terminate:] // will be called during processEvents which will invoke all static destructors. // We want to postpone this utill the last possible moment. - QCoreApplication::processEvents(); + //QCoreApplication::processEvents(); #endif } @@ -3610,14 +3632,17 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { // Using the latter will cause the camera to wobble with idle animations, // or with changes from the face tracker CameraMode mode = _myCamera.getMode(); - if (mode == CAMERA_MODE_FIRST_PERSON) { + if (mode == CAMERA_MODE_FIRST_PERSON || mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT) { _thirdPersonHMDCameraBoomValid= false; if (isHMDMode()) { mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); _myCamera.setPosition(extractTranslation(camMat)); _myCamera.setOrientation(glmExtractRotation(camMat)); + } else if (mode == CAMERA_MODE_FIRST_PERSON) { + _myCamera.setPosition(myAvatar->getDefaultEyePosition()); + _myCamera.setOrientation(myAvatar->getMyHead()->getHeadOrientation()); } else { - _myCamera.setPosition(myAvatar->getLookAtPivotPoint()); + _myCamera.setPosition(myAvatar->getCameraEyesPosition(deltaTime)); _myCamera.setOrientation(myAvatar->getLookAtRotation()); } } else if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) { @@ -4406,7 +4431,7 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_1: { Menu* menu = Menu::getInstance(); - menu->triggerOption(MenuOption::FirstPerson); + menu->triggerOption(MenuOption::FirstPersonLookAt); break; } case Qt::Key_2: { @@ -5491,7 +5516,7 @@ void Application::loadSettings() { isFirstPerson = menu->isOptionChecked(MenuOption::FirstPersonHMD); } else { // if HMD is not active, only use first person if the menu option is checked - isFirstPerson = menu->isOptionChecked(MenuOption::FirstPerson); + isFirstPerson = menu->isOptionChecked(MenuOption::FirstPersonLookAt); } } } @@ -5506,9 +5531,9 @@ void Application::loadSettings() { // finish initializing the camera, based on everything we checked above. Third person camera will be used if no settings // dictated that we should be in first person - Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, isFirstPerson); + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPersonLookAt, isFirstPerson); Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !isFirstPerson); - _myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_LOOK_AT); + _myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON_LOOK_AT : CAMERA_MODE_LOOK_AT); cameraMenuChanged(); auto inputs = pluginManager->getInputPlugins(); @@ -5672,7 +5697,7 @@ void Application::pauseUntilLoginDetermined() { menu->getMenu("Developer")->setVisible(false); } _previousCameraMode = _myCamera.getMode(); - _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); + _myCamera.setMode(CAMERA_MODE_FIRST_PERSON_LOOK_AT); cameraModeChanged(); // disconnect domain handler. @@ -5861,11 +5886,11 @@ void Application::cycleCamera() { if (menu->isOptionChecked(MenuOption::FullscreenMirror)) { menu->setIsOptionChecked(MenuOption::FullscreenMirror, false); - menu->setIsOptionChecked(MenuOption::FirstPerson, true); + menu->setIsOptionChecked(MenuOption::FirstPersonLookAt, true); - } else if (menu->isOptionChecked(MenuOption::FirstPerson)) { + } else if (menu->isOptionChecked(MenuOption::FirstPersonLookAt)) { - menu->setIsOptionChecked(MenuOption::FirstPerson, false); + menu->setIsOptionChecked(MenuOption::FirstPersonLookAt, false); menu->setIsOptionChecked(MenuOption::LookAtCamera, true); } else if (menu->isOptionChecked(MenuOption::LookAtCamera)) { @@ -5884,8 +5909,8 @@ void Application::cycleCamera() { void Application::cameraModeChanged() { switch (_myCamera.getMode()) { - case CAMERA_MODE_FIRST_PERSON: - Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true); + case CAMERA_MODE_FIRST_PERSON_LOOK_AT: + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPersonLookAt, true); break; case CAMERA_MODE_LOOK_AT: Menu::getInstance()->setIsOptionChecked(MenuOption::LookAtCamera, true); @@ -5905,12 +5930,12 @@ void Application::changeViewAsNeeded(float boomLength) { // This is called when the boom length has changed bool boomLengthGreaterThanMinimum = (boomLength > MyAvatar::ZOOM_MIN); - if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON && boomLengthGreaterThanMinimum) { - Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, false); + if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON_LOOK_AT && boomLengthGreaterThanMinimum) { + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPersonLookAt, false); Menu::getInstance()->setIsOptionChecked(MenuOption::LookAtCamera, true); cameraMenuChanged(); } else if (_myCamera.getMode() == CAMERA_MODE_LOOK_AT && !boomLengthGreaterThanMinimum) { - Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true); + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPersonLookAt, true); Menu::getInstance()->setIsOptionChecked(MenuOption::LookAtCamera, false); cameraMenuChanged(); } @@ -5918,9 +5943,9 @@ void Application::changeViewAsNeeded(float boomLength) { void Application::cameraMenuChanged() { auto menu = Menu::getInstance(); - if (menu->isOptionChecked(MenuOption::FirstPerson)) { - if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) { - _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); + if (menu->isOptionChecked(MenuOption::FirstPersonLookAt)) { + if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON_LOOK_AT) { + _myCamera.setMode(CAMERA_MODE_FIRST_PERSON_LOOK_AT); getMyAvatar()->setBoomLength(MyAvatar::ZOOM_MIN); } } else if (menu->isOptionChecked(MenuOption::LookAtCamera)) { @@ -9021,7 +9046,7 @@ void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) { } if (isHmd && menu->isOptionChecked(MenuOption::FirstPersonHMD)) { - menu->setIsOptionChecked(MenuOption::FirstPerson, true); + menu->setIsOptionChecked(MenuOption::FirstPersonLookAt, true); cameraMenuChanged(); } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index b66bc9c1c4..5a3bb36ca2 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -172,7 +172,7 @@ Menu::Menu() { // View > First Person auto firstPersonAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash( - viewMenu, MenuOption::FirstPerson, 0, + viewMenu, MenuOption::FirstPersonLookAt, 0, true, qApp, SLOT(cameraMenuChanged()))); firstPersonAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup)); @@ -640,11 +640,6 @@ Menu::Menu() { } #endif - - // Developer >> Tests >>> - MenuWrapper* testMenu = developerMenu->addMenu("Tests"); - addActionToQMenuAndActionHash(testMenu, MenuOption::RunClientScriptTests, 0, dialogsManager.data(), SLOT(showTestingResults())); - // Developer > Timing >>> MenuWrapper* timingMenu = developerMenu->addMenu("Timing"); MenuWrapper* perfTimerMenu = timingMenu->addMenu("Performance Timer"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 0ba1159052..4a2a97b168 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -111,7 +111,8 @@ namespace MenuOption { const QString ExpandSimulationTiming = "Expand /simulation"; const QString ExpandPhysicsTiming = "Expand /physics"; const QString ExpandUpdateTiming = "Expand /update"; - const QString FirstPerson = "First Person"; + const QString FirstPerson = "First Person Legacy"; + const QString FirstPersonLookAt = "First Person"; const QString FirstPersonHMD = "Enter First Person Mode in HMD"; const QString FivePointCalibration = "5 Point Calibration"; const QString FixGaze = "Fix Gaze (no saccade)"; @@ -178,7 +179,6 @@ namespace MenuOption { const QString ResetAvatarSize = "Reset Avatar Size"; const QString ResetSensors = "Reset Sensors"; const QString RunningScripts = "Running Scripts..."; - const QString RunClientScriptTests = "Run Client Script Tests"; const QString RunTimingTests = "Run Timing Tests"; const QString ScriptedMotorControl = "Enable Scripted Motor Control"; const QString ShowTrackedObjects = "Show Tracked Objects"; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6c9b62cfcf..6e0bfab69b 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -152,7 +152,7 @@ static int triggerReactionNameToIndex(const QString& reactionName) { } static int beginEndReactionNameToIndex(const QString& reactionName) { - assert(NUM_AVATAR_BEGIN_END_REACTIONS == TRIGGER_REACTION_NAMES.size()); + assert(NUM_AVATAR_BEGIN_END_REACTIONS == BEGIN_END_REACTION_NAMES.size()); return BEGIN_END_REACTION_NAMES.indexOf(reactionName); } @@ -958,7 +958,8 @@ void MyAvatar::simulate(float deltaTime, bool inView) { head->setScale(getModelScale()); head->simulate(deltaTime); CameraMode mode = qApp->getCamera().getMode(); - if (_scriptControlsHeadLookAt || mode == CAMERA_MODE_FIRST_PERSON || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) { + if (_scriptControlsHeadLookAt || mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT || mode == CAMERA_MODE_FIRST_PERSON || + mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) { if (!_pointAtActive || !_isPointTargetValid) { updateHeadLookAt(deltaTime); } else { @@ -2178,7 +2179,7 @@ void MyAvatar::computeMyLookAtTarget(const AvatarHash& hash) { glm::vec3 myForward = _lookAtYaw * IDENTITY_FORWARD; glm::vec3 myPosition = getHead()->getEyePosition(); CameraMode mode = qApp->getCamera().getMode(); - if (mode == CAMERA_MODE_FIRST_PERSON) { + if (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT || mode == CAMERA_MODE_FIRST_PERSON) { myPosition = qApp->getCamera().getPosition(); } @@ -2719,7 +2720,7 @@ void MyAvatar::updateMotors() { if (_characterController.getState() == CharacterController::State::Hover || _characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) { CameraMode mode = qApp->getCamera().getMode(); - if (mode == CAMERA_MODE_FIRST_PERSON || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) { + if (!qApp->isHMDMode() && (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE)) { motorRotation = getLookAtRotation(); } else { motorRotation = getMyHead()->getHeadOrientation(); @@ -3399,7 +3400,8 @@ bool MyAvatar::cameraInsideHead(const glm::vec3& cameraPosition) const { bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { bool defaultMode = renderArgs->_renderMode == RenderArgs::DEFAULT_RENDER_MODE; - bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON; + bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON_LOOK_AT || + qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON; bool overrideAnim = _skeletonModel ? _skeletonModel->getRig().isPlayingOverrideAnimation() : false; bool insideHead = cameraInsideHead(renderArgs->getViewFrustum().getPosition()); return !defaultMode || (!firstPerson && !insideHead) || (overrideAnim && !insideHead); @@ -3444,8 +3446,8 @@ void MyAvatar::updateOrientation(float deltaTime) { float targetSpeed = getDriveKey(YAW) * _yawSpeed; CameraMode mode = qApp->getCamera().getMode(); bool computeLookAt = isReadyForPhysics() && !qApp->isHMDMode() && - (mode == CAMERA_MODE_FIRST_PERSON || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE); - bool smoothCameraYaw = computeLookAt && mode != CAMERA_MODE_FIRST_PERSON; + (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE); + bool smoothCameraYaw = computeLookAt && mode != CAMERA_MODE_FIRST_PERSON_LOOK_AT; if (smoothCameraYaw) { // For "Look At" and "Selfie" camera modes we also smooth the yaw rotation from right-click mouse movement. float speedFromDeltaYaw = deltaTime > FLT_EPSILON ? getDriveKey(DELTA_YAW) / deltaTime : 0.0f; @@ -3569,11 +3571,11 @@ void MyAvatar::updateOrientation(float deltaTime) { if (isMovingFwdBwd) { if (isMovingSideways) { // Reorient avatar to face camera diagonal - blend = mode == CAMERA_MODE_FIRST_PERSON ? 1.0f : DIAGONAL_TURN_BLEND; + blend = mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT ? 1.0f : DIAGONAL_TURN_BLEND; float turnSign = getDriveKey(TRANSLATE_Z) < 0.0f ? -1.0f : 1.0f; turnSign = getDriveKey(TRANSLATE_X) > 0.0f ? -turnSign : turnSign; faceRotation = _lookAtYaw * glm::angleAxis(turnSign * 0.25f * PI, Vectors::UP); - } else if (mode == CAMERA_MODE_FIRST_PERSON) { + } else if (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT) { blend = 1.0f; } } @@ -3644,11 +3646,11 @@ void MyAvatar::updateOrientation(float deltaTime) { glm::vec3 ajustedYawVector = cameraYawVector; float limitAngle = 0.0f; float triggerAngle = -glm::sin(glm::radians(TRIGGER_REORIENT_ANGLE)); - if (mode == CAMERA_MODE_FIRST_PERSON) { + if (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT) { limitAngle = glm::sin(glm::radians(90.0f - FIRST_PERSON_TRIGGER_REORIENT_ANGLE)); triggerAngle = limitAngle; } - float reorientAngle = mode == CAMERA_MODE_FIRST_PERSON ? FIRST_PERSON_REORIENT_ANGLE : DEFAULT_REORIENT_ANGLE; + float reorientAngle = mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT ? FIRST_PERSON_REORIENT_ANGLE : DEFAULT_REORIENT_ANGLE; if (frontBackDot < limitAngle) { if (frontBackDot < 0.0f) { ajustedYawVector = (leftRightDot < 0.0f ? -avatarVectorRight : avatarVectorRight); @@ -3684,7 +3686,7 @@ void MyAvatar::updateOrientation(float deltaTime) { } _headLookAtActive = true; const float FIRST_PERSON_RECENTER_SECONDS = 15.0f; - if (mode == CAMERA_MODE_FIRST_PERSON) { + if (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT) { if (getDriveKey(YAW) + getDriveKey(STEP_YAW) + getDriveKey(DELTA_YAW) == 0.0f) { if (_firstPersonSteadyHeadTimer < FIRST_PERSON_RECENTER_SECONDS) { if (_firstPersonSteadyHeadTimer > 0.0f) { @@ -3772,7 +3774,7 @@ glm::vec3 MyAvatar::scaleMotorSpeed(const glm::vec3 forward, const glm::vec3 rig // Desktop mode. direction = (zSpeed * forward) + (xSpeed * right); CameraMode mode = qApp->getCamera().getMode(); - if ((mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_FIRST_PERSON || mode == CAMERA_MODE_SELFIE) && + if ((mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT || mode == CAMERA_MODE_SELFIE) && zSpeed != 0.0f && xSpeed != 0.0f && !isFlying()){ direction = (zSpeed * forward); } @@ -5437,7 +5439,7 @@ glm::quat MyAvatar::getOrientationForAudio() { case AudioListenerMode::FROM_HEAD: { // Using the camera's orientation instead, when the current mode is controlling the avatar's head. CameraMode mode = qApp->getCamera().getMode(); - bool headFollowsCamera = mode == CAMERA_MODE_FIRST_PERSON || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE; + bool headFollowsCamera = mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE; result = headFollowsCamera ? qApp->getCamera().getOrientation() : getHead()->getFinalOrientationInWorldFrame(); break; } @@ -6812,6 +6814,66 @@ glm::vec3 MyAvatar::getLookAtPivotPoint() { return yAxisEyePosition; } +glm::vec3 MyAvatar::getCameraEyesPosition(float deltaTime) { + glm::vec3 defaultEyesPosition = getLookAtPivotPoint(); + if (isFlying()) { + return defaultEyesPosition; + } + glm::vec3 avatarFrontVector = getWorldOrientation() * Vectors::FRONT; + glm::vec3 avatarUpVector = getWorldOrientation() * Vectors::UP; + // Compute the offset between the default and real eye positions. + glm::vec3 defaultEyesToEyesVector = getHead()->getEyePosition() - defaultEyesPosition; + float FRONT_OFFSET_IDLE_MULTIPLIER = 2.5f; + float FRONT_OFFSET_JUMP_MULTIPLIER = 1.5f; + float frontOffset = FRONT_OFFSET_IDLE_MULTIPLIER * glm::length(defaultEyesPosition - getDefaultEyePosition()); + + // Looking down will aproximate move the camera forward to meet the real eye position + float mixAlpha = glm::dot(_lookAtPitch * Vectors::FRONT, -avatarUpVector); + bool isLanding = false; + // When jumping the camera should follow the real eye on the Y coordenate + float upOffset = 0.0f; + if (isJumping() || _characterController.getState() == CharacterController::State::Takeoff) { + upOffset = glm::dot(defaultEyesToEyesVector, avatarUpVector); + frontOffset = glm::dot(defaultEyesToEyesVector, avatarFrontVector) * FRONT_OFFSET_JUMP_MULTIPLIER; + mixAlpha = 1.0f; + _landingAfterJumpTime = 0.0f; + } else { + // Limit the range effect from 45 to 0 degrees + // between the front camera and the down vectors + const float HEAD_OFFSET_RANGE_IN_DEGREES = 45.0f; + const float HEAD_OFFSET_RANGE_OUT_DEGREES = 0.0f; + float rangeIn = glm::cos(glm::radians(HEAD_OFFSET_RANGE_IN_DEGREES)); + float rangeOut = glm::cos(glm::radians(HEAD_OFFSET_RANGE_OUT_DEGREES)); + mixAlpha = mixAlpha < rangeIn ? 0.0f : (mixAlpha - rangeIn) / (rangeOut - rangeIn); + const float WAIT_TO_LAND_TIME = 1.0f; + if (_landingAfterJumpTime < WAIT_TO_LAND_TIME) { + _landingAfterJumpTime += deltaTime; + isLanding = true; + } + } + const float FPS = 60.0f; + float timeScale = deltaTime * FPS; + frontOffset = frontOffset < 0.0f ? 0.0f : mixAlpha * frontOffset; + glm::vec3 cameraOffset = upOffset * Vectors::UP + frontOffset * Vectors::FRONT; + const float JUMPING_TAU = 0.1f; + const float NO_JUMP_TAU = 0.3f; + const float LANDING_TAU = 0.05f; + float tau = NO_JUMP_TAU; + if (isJumping()) { + tau = JUMPING_TAU; + } else if (isLanding) { + tau = LANDING_TAU; + } + _cameraEyesOffset = _cameraEyesOffset + (cameraOffset - _cameraEyesOffset) * min(1.0f, tau * timeScale); + glm::vec3 estimatedCameraPosition = defaultEyesPosition + getWorldOrientation() * _cameraEyesOffset; + return estimatedCameraPosition; +} + +bool MyAvatar::isJumping() { + return (_characterController.getState() == CharacterController::State::InAir || + _characterController.getState() == CharacterController::State::Takeoff) && !isFlying(); +} + bool MyAvatar::setPointAt(const glm::vec3& pointAtTarget) { if (QThread::currentThread() != thread()) { bool result = false; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 0f139ddbff..081fd00d5b 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1919,6 +1919,8 @@ public: bool getIsJointOverridden(int jointIndex) const; glm::vec3 getLookAtPivotPoint(); + glm::vec3 getCameraEyesPosition(float deltaTime); + bool isJumping(); public slots: @@ -2973,6 +2975,9 @@ private: // used to prevent character from jumping after endSit is called. bool _endSitKeyPressComplete { false }; + + glm::vec3 _cameraEyesOffset; + float _landingAfterJumpTime { 0.0f }; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/interface/src/scripting/DesktopScriptingInterface.cpp b/interface/src/scripting/DesktopScriptingInterface.cpp index 95d3bae332..ae4af48cd6 100644 --- a/interface/src/scripting/DesktopScriptingInterface.cpp +++ b/interface/src/scripting/DesktopScriptingInterface.cpp @@ -52,6 +52,23 @@ static const QVariantMap DOCK_AREA { { "RIGHT", DockArea::RIGHT } }; +/**jsdoc + * The possible "relative position anchors" of an InteractiveWindow. Used when defining the `relativePosition` property of an `InteractiveWindow`. + * @typedef {object} InteractiveWindow.RelativePositionAnchors + * @property {InteractiveWindow.RelativePositionAnchor} NO_ANCHOR - Specifies that the position of the `InteractiveWindow` will not be relative to any part of the Interface window. + * @property {InteractiveWindow.RelativePositionAnchor} TOP_LEFT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the top left of the Interface window. + * @property {InteractiveWindow.RelativePositionAnchor} TOP_RIGHT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the top right of the Interface window. + * @property {InteractiveWindow.RelativePositionAnchor} BOTTOM_RIGHT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the bottom right of the Interface window. + * @property {InteractiveWindow.RelativePositionAnchor} BOTTOM_LEFT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the bottom left of the Interface window. + */ +static const QVariantMap RELATIVE_POSITION_ANCHOR { + { "NO_ANCHOR", RelativePositionAnchor::NO_ANCHOR }, + { "TOP_LEFT", RelativePositionAnchor::TOP_LEFT }, + { "TOP_RIGHT", RelativePositionAnchor::TOP_RIGHT }, + { "BOTTOM_RIGHT", RelativePositionAnchor::BOTTOM_RIGHT }, + { "BOTTOM_LEFT", RelativePositionAnchor::BOTTOM_LEFT } +}; + DesktopScriptingInterface::DesktopScriptingInterface(QObject* parent, bool restricted) : QObject(parent), _restricted(restricted) { } @@ -99,6 +116,10 @@ QVariantMap DesktopScriptingInterface::getDockArea() { return DOCK_AREA; } +QVariantMap DesktopScriptingInterface::getRelativePositionAnchor() { + return RELATIVE_POSITION_ANCHOR; +} + void DesktopScriptingInterface::setHUDAlpha(float alpha) { qApp->getApplicationCompositor().setAlpha(alpha); } diff --git a/interface/src/scripting/DesktopScriptingInterface.h b/interface/src/scripting/DesktopScriptingInterface.h index e562a32543..c25f382891 100644 --- a/interface/src/scripting/DesktopScriptingInterface.h +++ b/interface/src/scripting/DesktopScriptingInterface.h @@ -42,6 +42,9 @@ * @property {InteractiveWindow.DockAreas} DockArea - The possible docking locations of an {@link InteractiveWindow}: top, * bottom, left, or right of the Interface window. * Read-only. + * @property {InteractiveWindow.RelativePositionAnchors} RelativePositionAnchor - The possible "relative position anchors" for an {@link InteractiveWindow}: top left, + * top right, bottom right, or bottom left of the Interface window. + * Read-only. */ class DesktopScriptingInterface : public QObject, public Dependency { Q_OBJECT @@ -50,6 +53,7 @@ class DesktopScriptingInterface : public QObject, public Dependency { Q_PROPERTY(QVariantMap PresentationMode READ getPresentationMode CONSTANT FINAL) Q_PROPERTY(QVariantMap DockArea READ getDockArea CONSTANT FINAL) + Q_PROPERTY(QVariantMap RelativePositionAnchor READ getRelativePositionAnchor CONSTANT FINAL) Q_PROPERTY(int ALWAYS_ON_TOP READ flagAlwaysOnTop CONSTANT FINAL) Q_PROPERTY(int CLOSE_BUTTON_HIDES READ flagCloseButtonHides CONSTANT FINAL) @@ -106,7 +110,7 @@ private: Q_INVOKABLE InteractiveWindowPointer createWindowOnThread(const QString& sourceUrl, const QVariantMap& properties, QThread* targetThread); static QVariantMap getDockArea(); - + static QVariantMap getRelativePositionAnchor(); Q_INVOKABLE static QVariantMap getPresentationMode(); const bool _restricted; }; diff --git a/interface/src/scripting/RenderScriptingInterface.cpp b/interface/src/scripting/RenderScriptingInterface.cpp index f23dc598a9..56e9a93377 100644 --- a/interface/src/scripting/RenderScriptingInterface.cpp +++ b/interface/src/scripting/RenderScriptingInterface.cpp @@ -82,11 +82,18 @@ void RenderScriptingInterface::forceShadowsEnabled(bool enabled) { _shadowsEnabled = (enabled); _shadowsEnabledSetting.set(enabled); - auto lightingModelConfig = qApp->getRenderEngine()->getConfiguration()->getConfig("RenderMainView.LightingModel"); + auto renderConfig = qApp->getRenderEngine()->getConfiguration(); + assert(renderConfig); + auto lightingModelConfig = renderConfig->getConfig("RenderMainView.LightingModel"); if (lightingModelConfig) { Menu::getInstance()->setIsOptionChecked(MenuOption::Shadows, enabled); lightingModelConfig->setShadow(enabled); } + auto secondaryLightingModelConfig = renderConfig->getConfig("RenderSecondView.LightingModel"); + if (secondaryLightingModelConfig) { + Menu::getInstance()->setIsOptionChecked(MenuOption::Shadows, enabled); + secondaryLightingModelConfig->setShadow(enabled); + } }); } diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index e34b82e0a1..0a655de5e5 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -186,15 +186,6 @@ void DialogsManager::setAddressBarVisible(bool addressBarVisible) { emit addressBarShown(_addressBarVisible); } -void DialogsManager::showTestingResults() { - if (!_testingDialog) { - _testingDialog = new TestingDialog(qApp->getWindow()); - connect(_testingDialog, SIGNAL(closed()), _testingDialog, SLOT(deleteLater())); - } - _testingDialog->show(); - _testingDialog->raise(); -} - void DialogsManager::showDomainConnectionDialog() { // if the dialog already exists we delete it so the connection data is refreshed if (_domainConnectionDialog) { diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index b11264444b..949c86c240 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -53,7 +53,6 @@ public slots: void lodTools(); void hmdTools(bool showTools); void showDomainConnectionDialog(); - void showTestingResults(); void toggleAddressBar(); // Application Update diff --git a/interface/src/ui/InteractiveWindow.cpp b/interface/src/ui/InteractiveWindow.cpp index e63c392a47..6cc26e2409 100644 --- a/interface/src/ui/InteractiveWindow.cpp +++ b/interface/src/ui/InteractiveWindow.cpp @@ -39,6 +39,9 @@ static const char* const ADDITIONAL_FLAGS_PROPERTY = "additionalFlags"; static const char* const OVERRIDE_FLAGS_PROPERTY = "overrideFlags"; static const char* const SOURCE_PROPERTY = "source"; static const char* const TITLE_PROPERTY = "title"; +static const char* const RELATIVE_POSITION_ANCHOR_PROPERTY = "relativePositionAnchor"; +static const char* const RELATIVE_POSITION_PROPERTY = "relativePosition"; +static const char* const IS_FULL_SCREEN_WINDOW = "isFullScreenWindow"; static const char* const POSITION_PROPERTY = "position"; static const char* const INTERACTIVE_WINDOW_POSITION_PROPERTY = "interactiveWindowPosition"; static const char* const SIZE_PROPERTY = "size"; @@ -112,6 +115,15 @@ void InteractiveWindow::forwardKeyReleaseEvent(int key, int modifiers) { QCoreApplication::postEvent(QCoreApplication::instance(), event); } +void InteractiveWindow::onMainWindowGeometryChanged(QRect geometry) { + // This handler is only connected `if (_isFullScreenWindow || _relativePositionAnchor != RelativePositionAnchor::NONE)`. + if (_isFullScreenWindow) { + repositionAndResizeFullScreenWindow(); + } else if (_relativePositionAnchor != RelativePositionAnchor::NO_ANCHOR) { + setPositionUsingRelativePositionAndAnchor(geometry); + } +} + void InteractiveWindow::emitMainWindowResizeEvent() { emit qApp->getWindow()->windowGeometryChanged(qApp->getWindow()->geometry()); } @@ -184,22 +196,32 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap */ if (nativeWindowInfo.contains(DOCK_AREA_PROPERTY)) { DockArea dockedArea = (DockArea) nativeWindowInfo[DOCK_AREA_PROPERTY].toInt(); + int tempWidth = 0; + int tempHeight = 0; switch (dockedArea) { case DockArea::TOP: dockArea = Qt::TopDockWidgetArea; - _dockWidget->setFixedHeight(windowSize.height()); + tempHeight = windowSize.height(); + _dockWidget->setFixedHeight(tempHeight); + qApp->getWindow()->setDockedWidgetRelativePositionOffset(QSize(0, -tempHeight)); break; case DockArea::BOTTOM: dockArea = Qt::BottomDockWidgetArea; - _dockWidget->setFixedHeight(windowSize.height()); + tempHeight = windowSize.height(); + _dockWidget->setFixedHeight(tempHeight); + qApp->getWindow()->setDockedWidgetRelativePositionOffset(QSize(0, tempHeight)); break; case DockArea::LEFT: dockArea = Qt::LeftDockWidgetArea; - _dockWidget->setFixedWidth(windowSize.width()); + tempWidth = windowSize.width(); + _dockWidget->setFixedWidth(tempWidth); + qApp->getWindow()->setDockedWidgetRelativePositionOffset(QSize(-tempWidth, 0)); break; case DockArea::RIGHT: dockArea = Qt::RightDockWidgetArea; - _dockWidget->setFixedWidth(windowSize.width()); + tempWidth = windowSize.width(); + _dockWidget->setFixedWidth(tempWidth); + qApp->getWindow()->setDockedWidgetRelativePositionOffset(QSize(tempWidth, 0)); break; default: @@ -255,6 +277,9 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap if (properties.contains(TITLE_PROPERTY)) { object->setProperty(TITLE_PROPERTY, properties[TITLE_PROPERTY].toString()); } + if (properties.contains(VISIBLE_PROPERTY)) { + object->setProperty(VISIBLE_PROPERTY, properties[INTERACTIVE_WINDOW_VISIBLE_PROPERTY].toBool()); + } if (properties.contains(SIZE_PROPERTY)) { const auto size = vec2FromVariant(properties[SIZE_PROPERTY]); object->setProperty(INTERACTIVE_WINDOW_SIZE_PROPERTY, QSize(size.x, size.y)); @@ -263,8 +288,21 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap const auto position = vec2FromVariant(properties[POSITION_PROPERTY]); object->setProperty(INTERACTIVE_WINDOW_POSITION_PROPERTY, QPointF(position.x, position.y)); } - if (properties.contains(VISIBLE_PROPERTY)) { - object->setProperty(VISIBLE_PROPERTY, properties[INTERACTIVE_WINDOW_VISIBLE_PROPERTY].toBool()); + if (properties.contains(RELATIVE_POSITION_ANCHOR_PROPERTY)) { + _relativePositionAnchor = static_cast(properties[RELATIVE_POSITION_ANCHOR_PROPERTY].toInt()); + } + if (properties.contains(RELATIVE_POSITION_PROPERTY)) { + _relativePosition = vec2FromVariant(properties[RELATIVE_POSITION_PROPERTY]); + setPositionUsingRelativePositionAndAnchor(qApp->getWindow()->geometry()); + } + if (properties.contains(IS_FULL_SCREEN_WINDOW)) { + _isFullScreenWindow = properties[IS_FULL_SCREEN_WINDOW].toBool(); + } + + if (_isFullScreenWindow) { + QRect geo = qApp->getWindow()->geometry(); + object->setProperty(INTERACTIVE_WINDOW_POSITION_PROPERTY, QPointF(geo.x(), geo.y())); + object->setProperty(INTERACTIVE_WINDOW_SIZE_PROPERTY, QSize(geo.width(), geo.height())); } // The qmlToScript method handles the thread-safety of this call. Because the QVariant argument @@ -288,6 +326,10 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap connect(object, SIGNAL(interactiveWindowVisibleChanged()), this, SLOT(parentNativeWindowToMainWindow()), Qt::QueuedConnection); connect(object, SIGNAL(presentationModeChanged()), this, SLOT(parentNativeWindowToMainWindow()), Qt::QueuedConnection); #endif + + if (_isFullScreenWindow || _relativePositionAnchor != RelativePositionAnchor::NO_ANCHOR) { + connect(qApp->getWindow(), &MainWindow::windowGeometryChanged, this, &InteractiveWindow::onMainWindowGeometryChanged, Qt::QueuedConnection); + } QUrl sourceURL{ sourceUrl }; // If the passed URL doesn't correspond to a known scheme, assume it's a local file path @@ -414,6 +456,71 @@ void InteractiveWindow::setPosition(const glm::vec2& position) { } } +RelativePositionAnchor InteractiveWindow::getRelativePositionAnchor() const { + return _relativePositionAnchor; +} + +void InteractiveWindow::setRelativePositionAnchor(const RelativePositionAnchor& relativePositionAnchor) { + _relativePositionAnchor = relativePositionAnchor; + setPositionUsingRelativePositionAndAnchor(qApp->getWindow()->geometry()); +} + +glm::vec2 InteractiveWindow::getRelativePosition() const { + return _relativePosition; +} + +void InteractiveWindow::setRelativePosition(const glm::vec2& relativePosition) { + _relativePosition = relativePosition; + setPositionUsingRelativePositionAndAnchor(qApp->getWindow()->geometry()); +} + +void InteractiveWindow::setPositionUsingRelativePositionAndAnchor(const QRect& mainWindowGeometry) { + RelativePositionAnchor relativePositionAnchor = getRelativePositionAnchor(); + glm::vec2 relativePosition = getRelativePosition(); + + glm::vec2 newPosition; + + switch (relativePositionAnchor) { + case RelativePositionAnchor::TOP_LEFT: + newPosition.x = mainWindowGeometry.x() + relativePosition.x; + newPosition.y = mainWindowGeometry.y() + relativePosition.y; + break; + case RelativePositionAnchor::TOP_RIGHT: + newPosition.x = mainWindowGeometry.x() + mainWindowGeometry.width() - relativePosition.x; + newPosition.y = mainWindowGeometry.y() + relativePosition.y; + break; + case RelativePositionAnchor::BOTTOM_RIGHT: + newPosition.x = mainWindowGeometry.x() + mainWindowGeometry.width() - relativePosition.x; + newPosition.y = mainWindowGeometry.y() + mainWindowGeometry.height() - relativePosition.y; + break; + case RelativePositionAnchor::BOTTOM_LEFT: + newPosition.x = mainWindowGeometry.x() + relativePosition.x; + newPosition.y = mainWindowGeometry.y() + mainWindowGeometry.height() - relativePosition.y; + break; + case RelativePositionAnchor::NO_ANCHOR: + // No-op. + break; + } + + // Make sure we include the dimensions of the docked widget! + QSize dockedWidgetRelativePositionOffset = qApp->getWindow()->getDockedWidgetRelativePositionOffset(); + newPosition.x = newPosition.x + dockedWidgetRelativePositionOffset.width(); + newPosition.y = newPosition.y + dockedWidgetRelativePositionOffset.height(); + + if (_qmlWindowProxy) { + QMetaObject::invokeMethod(_qmlWindowProxy.get(), "writeProperty", Q_ARG(QString, INTERACTIVE_WINDOW_POSITION_PROPERTY), + Q_ARG(QVariant, QPointF(newPosition.x, newPosition.y))); + } + setPosition(newPosition); +} + +void InteractiveWindow::repositionAndResizeFullScreenWindow() { + QRect windowGeometry = qApp->getWindow()->geometry(); + + setPosition(glm::vec2(windowGeometry.x(), windowGeometry.y())); + setSize(glm::vec2(windowGeometry.width(), windowGeometry.height())); +} + glm::vec2 InteractiveWindow::getSize() const { if (!_qmlWindowProxy) { return {}; diff --git a/interface/src/ui/InteractiveWindow.h b/interface/src/ui/InteractiveWindow.h index ba53684173..fb10aac444 100644 --- a/interface/src/ui/InteractiveWindow.h +++ b/interface/src/ui/InteractiveWindow.h @@ -89,6 +89,15 @@ namespace InteractiveWindowEnums { RIGHT }; Q_ENUM_NS(DockArea); + + enum RelativePositionAnchor { + NO_ANCHOR, + TOP_LEFT, + TOP_RIGHT, + BOTTOM_RIGHT, + BOTTOM_LEFT + }; + Q_ENUM_NS(RelativePositionAnchor); } using namespace InteractiveWindowEnums; @@ -121,6 +130,8 @@ class InteractiveWindow : public QObject { Q_PROPERTY(QString title READ getTitle WRITE setTitle) Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition) + Q_PROPERTY(RelativePositionAnchor relativePositionAnchor READ getRelativePositionAnchor WRITE setRelativePositionAnchor) + Q_PROPERTY(glm::vec2 relativePosition READ getRelativePosition WRITE setRelativePosition) Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize) Q_PROPERTY(bool visible READ isVisible WRITE setVisible) Q_PROPERTY(int presentationMode READ getPresentationMode WRITE setPresentationMode) @@ -136,6 +147,21 @@ private: Q_INVOKABLE glm::vec2 getPosition() const; Q_INVOKABLE void setPosition(const glm::vec2& position); + + RelativePositionAnchor _relativePositionAnchor{ RelativePositionAnchor::NO_ANCHOR }; + Q_INVOKABLE RelativePositionAnchor getRelativePositionAnchor() const; + Q_INVOKABLE void setRelativePositionAnchor(const RelativePositionAnchor& position); + + // This "relative position" is relative to the "relative position anchor" and excludes the window frame. + // This position will ALWAYS include the geometry of a docked widget, if one is present. + glm::vec2 _relativePosition{ 0.0f, 0.0f }; + Q_INVOKABLE glm::vec2 getRelativePosition() const; + Q_INVOKABLE void setRelativePosition(const glm::vec2& position); + + Q_INVOKABLE void setPositionUsingRelativePositionAndAnchor(const QRect& mainWindowGeometry); + + bool _isFullScreenWindow{ false }; + Q_INVOKABLE void repositionAndResizeFullScreenWindow(); Q_INVOKABLE glm::vec2 getSize() const; Q_INVOKABLE void setSize(const glm::vec2& size); @@ -320,6 +346,7 @@ protected slots: void forwardKeyPressEvent(int key, int modifiers); void forwardKeyReleaseEvent(int key, int modifiers); void emitMainWindowResizeEvent(); + void onMainWindowGeometryChanged(QRect geometry); private: std::shared_ptr _qmlWindowProxy; diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 24acdcf20a..7f201f94e7 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -55,6 +55,8 @@ public: float getFrame() const { return _frame; } void loadURL(const QString& url); + AnimBlendType getBlendType() const { return _blendType; }; + protected: virtual void setCurrentFrameInternal(float frame) override; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 0be05f843e..fac4e04ce9 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -545,7 +545,8 @@ QStringList Rig::getAnimationRoles() const { auto clipNode = std::dynamic_pointer_cast(node); if (clipNode) { // filter out the userAnims, they are for internal use only. - if (!clipNode->getID().startsWith("userAnim")) { + // also don't return additive blend node clips as valid roles. + if (!clipNode->getID().startsWith("userAnim") && clipNode->getBlendType() == AnimBlendType_Normal) { list.append(node->getID()); } } @@ -1432,6 +1433,69 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos } _lastEnableInverseKinematics = _enableInverseKinematics; + + //stategraph vars based on input + const float INPUT_DEADZONE_THRESHOLD = 0.05f; + const float SLOW_SPEED_THRESHOLD = 1.5f; + + if (fabsf(_previousControllerParameters.inputX) <= INPUT_DEADZONE_THRESHOLD && + fabsf(_previousControllerParameters.inputZ) <= INPUT_DEADZONE_THRESHOLD) { + // no WASD input + if (fabsf(forwardSpeed) <= SLOW_SPEED_THRESHOLD && fabsf(lateralSpeed) <= SLOW_SPEED_THRESHOLD) { + _animVars.set("isInputForward", false); + _animVars.set("isInputBackward", false); + _animVars.set("isInputRight", false); + _animVars.set("isInputLeft", false); + _animVars.set("isNotInput", true); + _animVars.set("isNotInputSlow", true); + + } else { + _animVars.set("isInputForward", false); + _animVars.set("isInputBackward", false); + _animVars.set("isInputRight", false); + _animVars.set("isInputLeft", false); + _animVars.set("isNotInput", true); + _animVars.set("isNotInputSlow", false); + } + } else if (fabsf(_previousControllerParameters.inputZ) >= fabsf(_previousControllerParameters.inputX)) { + if (_previousControllerParameters.inputZ > 0.0f) { + // forward + _animVars.set("isInputForward", true); + _animVars.set("isInputBackward", false); + _animVars.set("isInputRight", false); + _animVars.set("isInputLeft", false); + _animVars.set("isNotInput", false); + _animVars.set("isNotInputSlow", false); + } else { + // backward + _animVars.set("isInputForward", false); + _animVars.set("isInputBackward", true); + _animVars.set("isInputRight", false); + _animVars.set("isInputLeft", false); + _animVars.set("isNotInput", false); + _animVars.set("isNotInputSlow", false); + } + } else { + if (_previousControllerParameters.inputX > 0.0f) { + // right + _animVars.set("isInputForward", false); + _animVars.set("isInputBackward", false); + _animVars.set("isInputRight", true); + _animVars.set("isInputLeft", false); + _animVars.set("isNotInput", false); + _animVars.set("isNotInputSlow", false); + } else { + // left + _animVars.set("isInputForward", false); + _animVars.set("isInputBackward", false); + _animVars.set("isInputRight", false); + _animVars.set("isInputLeft", true); + _animVars.set("isNotInput", false); + _animVars.set("isNotInputSlow", false); + } + } + + } _lastForward = forward; _lastPosition = worldPosition; @@ -2160,50 +2224,6 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo } } - //deadzone constant - const float INPUT_DEADZONE_THRESHOLD = 0.05f; - - if (fabsf(params.inputX) <= INPUT_DEADZONE_THRESHOLD && fabsf(params.inputZ) <= INPUT_DEADZONE_THRESHOLD) { - // no WASD input - _animVars.set("isInputForward", false); - _animVars.set("isInputBackward", false); - _animVars.set("isInputRight", false); - _animVars.set("isInputLeft", false); - _animVars.set("isNotInput", true); - } else if (fabsf(params.inputZ) >= fabsf(params.inputX)) { - if (params.inputZ > 0.0f) { - // forward - _animVars.set("isInputForward", true); - _animVars.set("isInputBackward", false); - _animVars.set("isInputRight", false); - _animVars.set("isInputLeft", false); - _animVars.set("isNotInput", false); - } else { - // backward - _animVars.set("isInputForward", false); - _animVars.set("isInputBackward", true); - _animVars.set("isInputRight", false); - _animVars.set("isInputLeft", false); - _animVars.set("isNotInput", false); - } - } else { - if (params.inputX > 0.0f) { - // right - _animVars.set("isInputForward", false); - _animVars.set("isInputBackward", false); - _animVars.set("isInputRight", true); - _animVars.set("isInputLeft", false); - _animVars.set("isNotInput", false); - } else { - // left - _animVars.set("isInputForward", false); - _animVars.set("isInputBackward", false); - _animVars.set("isInputRight", false); - _animVars.set("isInputLeft", true); - _animVars.set("isNotInput", false); - } - } - _headEnabled = params.primaryControllerFlags[PrimaryControllerType_Head] & (uint8_t)ControllerFlags::Enabled; bool leftHandEnabled = params.primaryControllerFlags[PrimaryControllerType_LeftHand] & (uint8_t)ControllerFlags::Enabled; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 8f5eddac00..8570ae4441 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -88,8 +88,8 @@ public: AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space uint8_t secondaryControllerFlags[NumSecondaryControllerTypes]; bool isTalking; - float inputX; - float inputZ; + float inputX = 0.0f; + float inputZ = 0.0f; bool reactionEnabledFlags[NUM_AVATAR_BEGIN_END_REACTIONS]; bool reactionTriggers[NUM_AVATAR_TRIGGER_REACTIONS]; HFMJointShapeInfo hipsShapeInfo; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 5e1f285a6c..d29045c99b 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1998,6 +1998,12 @@ void AudioClient::outputNotify() { } } +void AudioClient::noteAwakening() { + qCDebug(audioclient) << "Restarting the audio devices."; + switchInputToAudioDevice(_inputDeviceInfo); + switchOutputToAudioDevice(_outputDeviceInfo); +} + bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest) { Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread"); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 28a4f23968..b4ddb1018e 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -255,6 +255,7 @@ public slots: void setOutputGain(float gain) { _outputGain = gain; }; void outputNotify(); + void noteAwakening(); void loadSettings(); void saveSettings(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 7a2ea5321f..bea9f979b8 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -854,7 +854,8 @@ void Avatar::render(RenderArgs* renderArgs) { float distanceToTarget = glm::length(toTarget); const float DISPLAYNAME_DISTANCE = 20.0f; updateDisplayNameAlpha(distanceToTarget < DISPLAYNAME_DISTANCE); - if (!isMyAvatar() || renderArgs->_cameraMode != (int8_t)CAMERA_MODE_FIRST_PERSON) { + if (!isMyAvatar() || !(renderArgs->_cameraMode == (int8_t)CAMERA_MODE_FIRST_PERSON_LOOK_AT + || renderArgs->_cameraMode == (int8_t)CAMERA_MODE_FIRST_PERSON)) { auto& frustum = renderArgs->getViewFrustum(); auto textPosition = getDisplayNamePosition(); if (frustum.pointIntersectsFrustum(textPosition)) { diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index 6be0a5a800..b6b96216b5 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -335,8 +335,8 @@ namespace controller { makeAxisPair(Action::STEP_PITCH, "StepPitch"), makeAxisPair(Action::STEP_ROLL, "StepRoll"), makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateX"), - makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateY"), - makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateZ"), + makeAxisPair(Action::STEP_TRANSLATE_Y, "StepTranslateY"), + makeAxisPair(Action::STEP_TRANSLATE_Z, "StepTranslateZ"), makePosePair(Action::LEFT_HAND, "LeftHand"), makePosePair(Action::RIGHT_HAND, "RightHand"), diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 5edbc5261d..5473f1a010 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -808,6 +808,8 @@ void AccountManager::requestAccountSettings() { return; } + qCDebug(networking) << "Requesting the Account Settings from the Metaverse API"; + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QUrl lockerURL = _authURL; @@ -834,6 +836,9 @@ void AccountManager::requestAccountSettingsFinished() { if (rootObject.contains("status") && rootObject["status"].toString() == "success") { if (rootObject.contains("data") && rootObject["data"].isObject()) { _settings.unpack(rootObject["data"].toObject()); + _lastSuccessfulSyncTimestamp = _settings.lastChangeTimestamp(); + + qCDebug(networking) << "Received the Account Settings from the Metaverse API"; emit accountSettingsLoaded(); } else { @@ -874,6 +879,8 @@ void AccountManager::postAccountSettings() { return; } + qCDebug(networking) << "Account Settings have changed, pushing them to the Metaverse API"; + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QUrl lockerURL = _authURL; diff --git a/libraries/script-engine/src/RecordingScriptingInterface.cpp b/libraries/script-engine/src/RecordingScriptingInterface.cpp index f95edb482b..cbcf94662e 100644 --- a/libraries/script-engine/src/RecordingScriptingInterface.cpp +++ b/libraries/script-engine/src/RecordingScriptingInterface.cpp @@ -64,6 +64,11 @@ void RecordingScriptingInterface::playClip(NetworkClipLoaderPointer clipLoader, } void RecordingScriptingInterface::loadRecording(const QString& url, QScriptValue callback) { + if (QThread::currentThread() != thread()) { + BLOCKING_INVOKE_METHOD(this, "loadRecording", Q_ARG(const QString&, url), Q_ARG(QScriptValue, callback)); + return; + } + auto clipLoader = DependencyManager::get()->getClipLoader(url); if (clipLoader->isLoaded()) { @@ -117,6 +122,11 @@ void RecordingScriptingInterface::startPlaying() { } void RecordingScriptingInterface::setPlayerVolume(float volume) { + if (QThread::currentThread() != thread()) { + BLOCKING_INVOKE_METHOD(this, "setPlayerVolume", Q_ARG(float, volume)); + return; + } + _player->setVolume(std::min(std::max(volume, 0.0f), 1.0f)); } @@ -137,6 +147,11 @@ void RecordingScriptingInterface::setPlayFromCurrentLocation(bool playFromCurren } void RecordingScriptingInterface::setPlayerLoop(bool loop) { + if (QThread::currentThread() != thread()) { + BLOCKING_INVOKE_METHOD(this, "setPlayerLoop", Q_ARG(bool, loop)); + return; + } + _player->loop(loop); } @@ -195,6 +210,16 @@ void RecordingScriptingInterface::startRecording() { } void RecordingScriptingInterface::stopRecording() { + if (!_recorder->isRecording()) { + qCWarning(scriptengine) << "Recorder is not running"; + return; + } + + if (QThread::currentThread() != thread()) { + BLOCKING_INVOKE_METHOD(this, "stopRecording"); + return; + } + _recorder->stop(); _lastClip = _recorder->getClip(); _lastClip->seek(0); diff --git a/libraries/shared/src/shared/Camera.cpp b/libraries/shared/src/shared/Camera.cpp index b39b8b9169..6727f89d29 100644 --- a/libraries/shared/src/shared/Camera.cpp +++ b/libraries/shared/src/shared/Camera.cpp @@ -25,8 +25,14 @@ * * First Person * "first person" - * The camera is positioned such that you have the same view as your avatar. The camera moves and rotates with your - * avatar. + * Legacy first person camera mode. The camera is positioned such that you have the same view as your avatar. + * The camera moves and rotates with your avatar. + * + * + * First Person Look At + * "first person look at" + * Default first person camera mode. The camera is positioned such that you have the same view as your avatar. + * The camera moves and rotates with your avatar's head. * * * Third Person @@ -73,6 +79,8 @@ CameraMode stringToMode(const QString& mode) { return CAMERA_MODE_THIRD_PERSON; } else if (mode == "first person") { return CAMERA_MODE_FIRST_PERSON; + } else if (mode == "first person look at") { + return CAMERA_MODE_FIRST_PERSON_LOOK_AT; } else if (mode == "mirror") { return CAMERA_MODE_MIRROR; } else if (mode == "independent") { @@ -92,6 +100,8 @@ QString modeToString(CameraMode mode) { return "third person"; } else if (mode == CAMERA_MODE_FIRST_PERSON) { return "first person"; + } else if (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT) { + return "first person look at"; } else if (mode == CAMERA_MODE_MIRROR) { return "mirror"; } else if (mode == CAMERA_MODE_INDEPENDENT) { diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h index eecad34838..48cd814d86 100644 --- a/libraries/shared/src/shared/Camera.h +++ b/libraries/shared/src/shared/Camera.h @@ -19,6 +19,7 @@ enum CameraMode { CAMERA_MODE_NULL = -1, CAMERA_MODE_THIRD_PERSON, + CAMERA_MODE_FIRST_PERSON_LOOK_AT, CAMERA_MODE_FIRST_PERSON, CAMERA_MODE_MIRROR, CAMERA_MODE_INDEPENDENT, diff --git a/libraries/ui/src/MainWindow.h b/libraries/ui/src/MainWindow.h index 543f8ce9af..b37ac2ec1b 100644 --- a/libraries/ui/src/MainWindow.h +++ b/libraries/ui/src/MainWindow.h @@ -24,6 +24,10 @@ public: ~MainWindow(); static QWindow* findMainWindow(); + + // This offset is used for positioning children window relative to the main window. + void setDockedWidgetRelativePositionOffset(const QSize& newOffset) { _dockedWidgetRelativePositionOffset.setWidth(newOffset.width()); _dockedWidgetRelativePositionOffset.setHeight(newOffset.height()); } + QSize getDockedWidgetRelativePositionOffset() { return _dockedWidgetRelativePositionOffset; } public slots: void restoreGeometry(); void saveGeometry(); @@ -46,6 +50,7 @@ protected: private: Setting::Handle _windowGeometry; Setting::Handle _windowState; + QSize _dockedWidgetRelativePositionOffset{ 0, 0 }; }; #endif /* defined(__hifi__MainWindow__) */ diff --git a/libraries/ui/src/ui/ToolbarScriptingInterface.h b/libraries/ui/src/ui/ToolbarScriptingInterface.h index 952d3cce95..3d38aa296b 100644 --- a/libraries/ui/src/ui/ToolbarScriptingInterface.h +++ b/libraries/ui/src/ui/ToolbarScriptingInterface.h @@ -92,15 +92,19 @@ public: ToolbarProxy(QObject* qmlObject, QObject* parent = nullptr); /**jsdoc + * Currently doesn't work. * @function ToolbarProxy#addButton - * @param {object} properties - * @returns {ToolbarButtonProxy} + * @param {object} properties - Button properties + * @returns {object} The button added. + * @deprecated This method is deprecated and will be removed. */ Q_INVOKABLE ToolbarButtonProxy* addButton(const QVariant& properties); /**jsdoc + * Currently doesn't work. * @function ToolbarProxy#removeButton - * @param {string} name + * @param {string} name - Button name. + * @deprecated This method is deprecated and will be removed. */ Q_INVOKABLE void removeButton(const QVariant& name); diff --git a/scripts/developer/tests/toolbarTest.js b/scripts/developer/tests/toolbarTest.js index 89609e610d..9e82f814ac 100644 --- a/scripts/developer/tests/toolbarTest.js +++ b/scripts/developer/tests/toolbarTest.js @@ -1,117 +1,38 @@ -var isActive = false; +(function () { -var toolBar = (function() { - var that = {}, - toolBar, - activeButton, - newModelButton, - newShapeButton, - newLightButton, - newTextButton, - newWebButton, - newZoneButton, - newParticleButton, - newMaterialButton - - var toolIconUrl = Script.resolvePath("../../system/assets/images/tools/"); - - function initialize() { - print("Toolbars: " + Toolbars); - toolBar = Toolbars.getToolbar("highfidelity.edit.toolbar"); - print("Toolbar: " + toolBar); - activeButton = toolBar.addButton({ - objectName: "activeButton", - imageURL: toolIconUrl + "edit-01.svg", - visible: true, - alpha: 0.9, - }); - - print("Button " + activeButton); - print("Button signal " + activeButton.clicked); - activeButton.clicked.connect(function(){ - print("Clicked on button " + isActive); - that.setActive(!isActive); - }); - - newModelButton = toolBar.addButton({ - objectName: "newModelButton", - imageURL: toolIconUrl + "model-01.svg", - alpha: 0.9, - visible: false - }); - - newShapeButton = toolBar.addButton({ - objectName: "newShapeButton", - imageURL: toolIconUrl + "cube-01.svg", - alpha: 0.9, - visible: false - }); - - newLightButton = toolBar.addButton({ - objectName: "newLightButton", - imageURL: toolIconUrl + "light-01.svg", - alpha: 0.9, - visible: false - }); - - newTextButton = toolBar.addButton({ - objectName: "newTextButton", - imageURL: toolIconUrl + "text-01.svg", - alpha: 0.9, - visible: false - }); - - newWebButton = toolBar.addButton({ - objectName: "newWebButton", - imageURL: toolIconUrl + "web-01.svg", - alpha: 0.9, - visible: false - }); - - newZoneButton = toolBar.addButton({ - objectName: "newZoneButton", - imageURL: toolIconUrl + "zone-01.svg", - alpha: 0.9, - visible: false - }); - - newParticleButton = toolBar.addButton({ - objectName: "newParticleButton", - imageURL: toolIconUrl + "particle-01.svg", - alpha: 0.9, - visible: false - }); - - newMaterialButton = toolBar.addButton({ - objectName: "newMaterialButton", - imageURL: toolIconUrl + "material-01.svg", - alpha: 0.9, - visible: false - }); - - that.setActive(false); - newModelButton.clicked(); + // Get the system toolbar. + var toolbar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system"); + if (!toolbar) { + print("ERROR: Couldn't get system toolbar."); + return; } - that.setActive = function(active) { - if (active != isActive) { - isActive = active; - that.showTools(isActive); - } - }; + Script.setTimeout(function () { + // Report the system toolbar visibility. + var isToolbarVisible = toolbar.readProperty("visible"); + print("Toolbar visible: " + isToolbarVisible); - // Sets visibility of tool buttons, excluding the power button - that.showTools = function(doShow) { - newModelButton.writeProperty('visible', doShow); - newShapeButton.writeProperty('visible', doShow); - newLightButton.writeProperty('visible', doShow); - newTextButton.writeProperty('visible', doShow); - newWebButton.writeProperty('visible', doShow); - newZoneButton.writeProperty('visible', doShow); - newParticleButton.writeProperty('visible', doShow); - newMaterialButton.writeProperty('visible', doShow); - }; + // Briefly toggle the system toolbar visibility. + print("Toggle toolbar"); + toolbar.writeProperty("visible", !isToolbarVisible); + Script.setTimeout(function () { + print("Toggle toolbar"); + toolbar.writeProperty("visible", isToolbarVisible); + }, 2000); + }, 2000); + + Script.setTimeout(function () { + // Report the system toolbar visibility alternative method. + isToolbarVisible = toolbar.readProperties(["visible"]).visible; + print("Toolbar visible: " + isToolbarVisible); + + // Briefly toggle the system toolbar visibility. + print("Toggle toolbar"); + toolbar.writeProperties({ visible: !isToolbarVisible }); + Script.setTimeout(function () { + print("Toggle toolbar"); + toolbar.writeProperties({ visible: isToolbarVisible }); + }, 2000); + }, 6000); - initialize(); - return that; }()); diff --git a/scripts/simplifiedUI/simplifiedEmote/simplifiedEmote.js b/scripts/simplifiedUI/simplifiedEmote/simplifiedEmote.js index 8633fe8870..d7d6279e10 100644 --- a/scripts/simplifiedUI/simplifiedEmote/simplifiedEmote.js +++ b/scripts/simplifiedUI/simplifiedEmote/simplifiedEmote.js @@ -444,17 +444,9 @@ function updateEmoteIndicatorIcon(iconURL) { } -function onGeometryChanged(rect) { - updateEmoteAppBarPosition(); -} - - function onWindowMinimizedChanged(isMinimized) { - if (isMinimized) { - handleEmoteIndicatorVisibleChanged(false); - } else if (!HMD.active) { - handleEmoteIndicatorVisibleChanged(true); - } + isWindowMinimized = isMinimized; + maybeChangeEmoteIndicatorVisibility(!isMinimized); } @@ -539,10 +531,11 @@ function showEmoteAppBar() { x: EMOTE_APP_BAR_WIDTH_PX, y: EMOTE_APP_BAR_HEIGHT_PX }, - position: { - x: Window.x + EMOTE_APP_BAR_LEFT_MARGIN, - y: Window.y + Window.innerHeight - EMOTE_APP_BAR_BOTTOM_MARGIN + relativePosition: { + x: EMOTE_APP_BAR_LEFT_MARGIN, + y: EMOTE_APP_BAR_BOTTOM_MARGIN }, + relativePositionAnchor: Desktop.RelativePositionAnchor.BOTTOM_LEFT, overrideFlags: EMOTE_APP_BAR_WINDOW_FLAGS }); @@ -550,10 +543,18 @@ function showEmoteAppBar() { } -function handleEmoteIndicatorVisibleChanged(shouldBeVisible) { - if (shouldBeVisible && !emoteAppBarWindow) { +// There is currently no property in the Window Scripting Interface to determine +// whether the Interface window is currently minimized. This feels like an oversight. +// We should add that functionality to the Window Scripting Interface, and remove `isWindowMinimized` below. +var isWindowMinimized = false; +function maybeChangeEmoteIndicatorVisibility(desiredVisibility) { + if (isWindowMinimized || HMD.active) { + desiredVisibility = false; + } + + if (desiredVisibility && !emoteAppBarWindow) { showEmoteAppBar(); - } else if (emoteAppBarWindow) { + } else if (!desiredVisibility && emoteAppBarWindow) { emoteAppBarWindow.fromQml.disconnect(onMessageFromEmoteAppBar); emoteAppBarWindow.close(); emoteAppBarWindow = false; @@ -561,23 +562,25 @@ function handleEmoteIndicatorVisibleChanged(shouldBeVisible) { } +function handleFTUEScreensVisibilityChanged(ftueScreenVisible) { + maybeChangeEmoteIndicatorVisibility(!ftueScreenVisible); +} + + function onDisplayModeChanged(isHMDMode) { reactionsBegun.forEach(function(react) { endReactionWrapper(react); }); - if (isHMDMode) { - handleEmoteIndicatorVisibleChanged(false); - } else { - handleEmoteIndicatorVisibleChanged(true); - } + maybeChangeEmoteIndicatorVisibility(!isHMDMode); } -var emojiAPI = Script.require("./emojiApp/simplifiedEmoji.js?" + Date.now()); +var emojiAPI = Script.require("./emojiApp/simplifiedEmoji.js"); var keyPressSignalsConnected = false; var emojiCodeMap; var customEmojiCodeMap; +var _this; function setup() { deleteOldReticles(); @@ -605,16 +608,25 @@ function setup() { }, {}); Window.minimizedChanged.connect(onWindowMinimizedChanged); - Window.geometryChanged.connect(onGeometryChanged); HMD.displayModeChanged.connect(onDisplayModeChanged); getSounds(); - handleEmoteIndicatorVisibleChanged(true); + maybeChangeEmoteIndicatorVisibility(true); Controller.keyPressEvent.connect(keyPressHandler); Controller.keyReleaseEvent.connect(keyReleaseHandler); keyPressSignalsConnected = true; Script.scriptEnding.connect(unload); + + function Emote() { + _this = this; + } + + Emote.prototype = { + handleFTUEScreensVisibilityChanged: handleFTUEScreensVisibilityChanged + }; + + return new Emote(); } @@ -638,7 +650,6 @@ function unload() { maybeDeleteRemoteIndicatorTimeout(); Window.minimizedChanged.disconnect(onWindowMinimizedChanged); - Window.geometryChanged.disconnect(onGeometryChanged); HMD.displayModeChanged.disconnect(onDisplayModeChanged); if (keyPressSignalsConnected) { @@ -671,7 +682,6 @@ function unload() { // #region EMOJI_UTILITY -var EMOJI_52_BASE_URL = "../../resources/images/emojis/52px/"; function selectedEmoji(code) { emojiAPI.addEmoji(code); // this URL needs to be relative to SimplifiedEmoteIndicator.qml @@ -786,4 +796,6 @@ function toggleEmojiApp() { // END EMOJI // ************************************* -setup(); \ No newline at end of file +var emote = setup(); + +module.exports = emote; \ No newline at end of file diff --git a/scripts/simplifiedUI/simplifiedEmote/ui/qml/images/emote_Icon.svg b/scripts/simplifiedUI/simplifiedEmote/ui/qml/images/emote_Icon.svg index 99c88cc20c..967ced7ae6 100644 --- a/scripts/simplifiedUI/simplifiedEmote/ui/qml/images/emote_Icon.svg +++ b/scripts/simplifiedUI/simplifiedEmote/ui/qml/images/emote_Icon.svg @@ -1,15 +1,40 @@ - - - + + + + + + + + + +]> + + + + + + + + + + + + + + diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/InitialLaunchWindow.qml b/scripts/simplifiedUI/ui/simplifiedFTUE/InitialLaunchWindow.qml new file mode 100644 index 0000000000..1938586edb --- /dev/null +++ b/scripts/simplifiedUI/ui/simplifiedFTUE/InitialLaunchWindow.qml @@ -0,0 +1,350 @@ +// +// InitialLaunchWindow.qml +// +// Copyright 2019 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.10 +import QtQuick.Controls 2.3 +import QtGraphicalEffects 1.0 +import QtQuick.Layouts 1.3 +import stylesUit 1.0 as HifiStylesUit +import TabletScriptingInterface 1.0 +import hifi.simplifiedUI.simplifiedConstants 1.0 as SimplifiedConstants +import hifi.simplifiedUI.simplifiedControls 1.0 as SimplifiedControls + +Rectangle { + id: root + color: simplifiedUI.colors.white + anchors.fill: parent + property bool landscapeOrientation: root.width > root.height + + SimplifiedConstants.SimplifiedConstants { + id: simplifiedUI + } + + Component.onCompleted: { + var debugFTUE = Settings.getValue("simplifiedUI/debugFTUE", 0); + + if ((debugFTUE !== 1 && + (Settings.getValue("simplifiedUI/alreadyAutoSelectedAvatarFromInventory", false) || + Settings.getValue("simplifiedUI/closedAvatarPageOfInitialLaunchWindow", false))) || + debugFTUE === 2) { + tempAvatarPageContainer.visible = false; + controlsContainer.visible = true; + } + } + + Item { + id: tempAvatarPageContainer + anchors.fill: parent + + Item { + id: contentContainer + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: firstPageBottomBarContainer.top + + Image { + id: avatarImage + anchors.verticalCenter: parent.verticalCenter + height: Math.max(parent.height - 48, 350) + anchors.left: parent.left + anchors.leftMargin: 12 + source: resourceDirectoryUrl + "qml/hifi/simplifiedUI/avatarApp/images/" + + MyAvatar.skeletonModelURL.substring(MyAvatar.skeletonModelURL.indexOf("simplifiedAvatar"), MyAvatar.skeletonModelURL.lastIndexOf("/")) + ".png" + mipmap: true + fillMode: Image.PreserveAspectFit + } + + Flickable { + id: textContainer + clip: true + anchors.top: parent.top + anchors.topMargin: 128 + anchors.bottom: qrAndInstructionsContainer.top + anchors.bottomMargin: 32 + anchors.left: avatarImage.right + anchors.leftMargin: 48 + anchors.right: parent.right + contentWidth: width + contentHeight: contentItem.childrenRect.height + interactive: contentHeight > height + + HifiStylesUit.RalewayBold { + id: headerText + text: "We know this isn't you..." + color: simplifiedUI.colors.text.black + size: 48 + height: paintedHeight + wrapMode: Text.Wrap + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: 16 + } + + HifiStylesUit.RalewayRegular { + id: descriptionText + anchors.top: headerText.bottom + anchors.topMargin: 10 + anchors.left: parent.left + width: Math.min(700, parent.width) - headerText.anchors.rightMargin + height: paintedHeight + text: "...but we've given you this temporary avatar to use " + + "for today. If you see this avatar in-world, walk up and " + + "say hello to other new users!

" + + "We want you to be you so we've built " + + 'Virtual You, an Avatar Creator ' + + "App. Creating an avatar is as easy as taking a selfie and picking your " + + "outfits! Available now on iOS and Android." + color: simplifiedUI.colors.text.black + size: 22 + wrapMode: Text.Wrap + + onLinkActivated: { + Qt.openUrlExternally(link); + } + } + } + + Item { + id: qrAndInstructionsContainer + anchors.left: avatarImage.right + anchors.leftMargin: 48 + anchors.right: parent.right + anchors.rightMargin: 16 + anchors.bottom: parent.bottom + height: 130 + + Image { + id: avatarAppQRCodeImage + source: resourceDirectoryUrl + "qml/hifi/simplifiedUI/avatarApp/images/qrCode.jpg" + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + width: 130 + mipmap: true + fillMode: Image.PreserveAspectFit + } + + HifiStylesUit.RalewayBold { + id: instructionText + anchors.top: avatarAppQRCodeImage.top + anchors.bottom: avatarAppQRCodeImage.bottom + anchors.left: avatarAppQRCodeImage.right + anchors.leftMargin: 30 + anchors.right: parent.right + text: "Use your mobile phone to scan this QR code." + color: simplifiedUI.colors.text.black + size: 22 + wrapMode: Text.Wrap + } + } + + SimplifiedControls.VerticalScrollBar { + parent: textContainer + visible: parent.contentHeight > parent.height + size: parent.height / parent.contentHeight + } + } + + Item { + id: firstPageBottomBarContainer + anchors.left: parent.left + anchors.leftMargin: 32 + anchors.right: parent.right + anchors.rightMargin: 32 + anchors.bottom: parent.bottom + height: continueLink.height + 48 + + HifiStylesUit.RalewayBold { + id: continueLink + anchors.centerIn: parent + text: "Continue >" + width: parent.width + height: paintedHeight + color: simplifiedUI.colors.text.lightBlue + opacity: continueMouseArea.containsMouse ? 1.0 : 0.7 + size: 36 + wrapMode: Text.Wrap + horizontalAlignment: Text.AlignHCenter + + MouseArea { + id: continueMouseArea + hoverEnabled: true + anchors.fill: parent + + onClicked: { + Tablet.playSound(TabletEnums.ButtonClick); + tempAvatarPageContainer.visible = false; + Settings.setValue("simplifiedUI/closedAvatarPageOfInitialLaunchWindow", true); + controlsContainer.visible = true; + } + } + } + } + } + + Item { + id: controlsContainer + visible: false + anchors.fill: parent + + HifiStylesUit.RalewayRegular { + id: controlsDescriptionText + text: "Use these avatar controls to
interact with and move around in your new HQ." + anchors.top: parent.top + anchors.topMargin: 48 + anchors.left: parent.left + anchors.leftMargin: 32 + anchors.right: parent.right + anchors.rightMargin: 32 + horizontalAlignment: Text.AlignHCenter + height: paintedHeight + color: simplifiedUI.colors.text.black + size: 36 + wrapMode: Text.Wrap + } + + Item { + anchors.top: controlsDescriptionText.bottom + anchors.topMargin: 16 + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: bottomBarContainer.top + + GridView { + id: controlsGrid + property int maxColumns: 2 + property int idealCellWidth: 361 + anchors.fill: parent + clip: true + cellWidth: width / Math.min(Math.floor(width / idealCellWidth), maxColumns) + cellHeight: 225 + model: ListModel { + ListElement { + imageHeight: 198 + imageSource: "images/walkingControls.png" + } + ListElement { + imageHeight: 193 + imageSource: "images/mouseControls.png" + } + ListElement { + imageHeight: 146 + imageSource: "images/runJumpControls.png" + } + ListElement { + imageHeight: 96 + imageSource: "images/cameraControls.png" + } + } + delegate: Rectangle { + height: GridView.view.cellHeight + width: GridView.view.cellWidth + Image { + anchors.centerIn: parent + width: parent.GridView.view.idealCellWidth + height: model.imageHeight + source: model.imageSource + fillMode: Image.PreserveAspectFit + } + } + } + + SimplifiedControls.VerticalScrollBar { + parent: controlsGrid + anchors.topMargin: 96 + anchors.bottomMargin: anchors.topMargin + } + } + + Item { + id: bottomBarContainer + anchors.left: parent.left + anchors.leftMargin: 32 + anchors.right: parent.right + anchors.rightMargin: 32 + anchors.bottom: parent.bottom + height: iHaveAGoodGrip.height + learnMoreLink.height + 48 + + HifiStylesUit.RalewayBold { + id: iHaveAGoodGrip + anchors.centerIn: parent + text: "I've got a good grip on the controls." + width: parent.width + height: paintedHeight + color: simplifiedUI.colors.text.lightBlue + opacity: goodGripMouseArea.containsMouse ? 1.0 : 0.7 + size: 36 + wrapMode: Text.Wrap + horizontalAlignment: Text.AlignHCenter + + MouseArea { + id: goodGripMouseArea + hoverEnabled: true + anchors.fill: parent + + onClicked: { + Tablet.playSound(TabletEnums.ButtonClick); + sendToScript({ + "source": "InitialLaunchWindow.qml", + "method": "closeInitialLaunchWindow" + }); + } + } + } + + HifiStylesUit.RalewayBold { + id: learnMoreLink + anchors.left: parent.left + anchors.leftMargin: 16 + anchors.top: iHaveAGoodGrip.bottom + anchors.topMargin: 8 + text: "Learn more about our controls." + width: paintedWidth + height: paintedHeight + color: simplifiedUI.colors.text.lightBlue + opacity: learnMoreAboutControlsMouseArea.containsMouse ? 1.0 : 0.7 + size: 14 + wrapMode: Text.Wrap + + MouseArea { + id: learnMoreAboutControlsMouseArea + hoverEnabled: true + anchors.fill: parent + + onClicked: { + Tablet.playSound(TabletEnums.ButtonClick); + Qt.openUrlExternally("https://www.highfidelity.com/knowledge/get-around"); + } + } + } + } + } + + Image { + id: topLeftAccentImage + width: 400 + height: 180 + anchors.left: parent.left + anchors.top: parent.top + source: "images/defaultTopLeft.png" + } + + Image { + id: bottomRightAccentImage + width: 80 + height: 250 + anchors.right: parent.right + anchors.bottom: parent.bottom + source: "images/defaultBottomRight.png" + } + + signal sendToScript(var message); +} diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/SecondLaunchWindow.qml b/scripts/simplifiedUI/ui/simplifiedFTUE/SecondLaunchWindow.qml new file mode 100644 index 0000000000..2a796465ae --- /dev/null +++ b/scripts/simplifiedUI/ui/simplifiedFTUE/SecondLaunchWindow.qml @@ -0,0 +1,186 @@ +// +// SecondLaunchWindow.qml +// +// Copyright 2019 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.10 +import QtQuick.Controls 2.3 +import QtGraphicalEffects 1.0 +import QtQuick.Layouts 1.3 +import stylesUit 1.0 as HifiStylesUit +import TabletScriptingInterface 1.0 +import hifi.simplifiedUI.simplifiedConstants 1.0 as SimplifiedConstants +import hifi.simplifiedUI.simplifiedControls 1.0 as SimplifiedControls + +Rectangle { + id: root + color: simplifiedUI.colors.white + anchors.fill: parent + + SimplifiedConstants.SimplifiedConstants { + id: simplifiedUI + } + + Item { + id: contentContainer + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: continueLink.top + + Image { + id: avatarImage + anchors.verticalCenter: parent.verticalCenter + height: Math.max(parent.height - 48, 350) + anchors.left: parent.left + anchors.leftMargin: 12 + source: resourceDirectoryUrl + "qml/hifi/simplifiedUI/avatarApp/images/hero.png" + mipmap: true + fillMode: Image.PreserveAspectFit + } + + Item { + anchors.top: parent.top + anchors.topMargin: 196 + anchors.bottom: parent.bottom + anchors.bottomMargin: 32 + anchors.left: avatarImage.right + anchors.leftMargin: 48 + anchors.right: parent.right + + Flickable { + id: textContainer + clip: true + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + width: Math.min(700, parent.width) + contentWidth: width + contentHeight: contentItem.childrenRect.height + interactive: contentHeight > height + + HifiStylesUit.RalewayBold { + id: headerText + text: "Stand out from the crowd!" + color: simplifiedUI.colors.text.black + size: 48 + height: paintedHeight + wrapMode: Text.Wrap + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: 16 + } + + HifiStylesUit.RalewayRegular { + id: descriptionText + anchors.top: headerText.bottom + anchors.topMargin: 10 + anchors.left: parent.left + width: parent.width - headerText.anchors.rightMargin + height: paintedHeight + text: "You can create and upload custom avatars from our Avatar Creator App. " + + "It's as easy as taking a selfie.
Available now on iOS and Android Platforms." + color: simplifiedUI.colors.text.black + size: 22 + wrapMode: Text.Wrap + } + + Item { + id: qrAndInstructionsContainer + anchors.top: descriptionText.bottom + anchors.topMargin: 24 + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: 16 + height: avatarAppQRCodeImage.height + + Image { + id: avatarAppQRCodeImage + source: resourceDirectoryUrl + "qml/hifi/simplifiedUI/avatarApp/images/qrCode.jpg" + anchors.top: parent.top + anchors.left: parent.left + width: 130 + height: width + mipmap: true + fillMode: Image.PreserveAspectFit + } + + HifiStylesUit.RalewayBold { + id: instructionText + anchors.top: avatarAppQRCodeImage.top + anchors.bottom: avatarAppQRCodeImage.bottom + anchors.left: avatarAppQRCodeImage.right + anchors.leftMargin: 30 + anchors.right: parent.right + text: "Use your mobile phone to scan this QR code." + color: simplifiedUI.colors.text.black + size: 22 + wrapMode: Text.Wrap + } + } + } + } + + SimplifiedControls.VerticalScrollBar { + parent: textContainer + visible: parent.contentHeight > parent.height + size: parent.height / parent.contentHeight + } + } + + + HifiStylesUit.RalewayBold { + id: continueLink + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.leftMargin: 16 + anchors.right: parent.right + anchors.rightMargin: 16 + height: 96 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: "No thanks, I'll keep using my default avatar." + color: simplifiedUI.colors.text.lightBlue + opacity: continueMouseArea.containsMouse ? 1.0 : 0.7 + size: 24 + + MouseArea { + id: continueMouseArea + hoverEnabled: true + anchors.fill: parent + + onClicked: { + Tablet.playSound(TabletEnums.ButtonClick); + sendToScript({ + "source": "SecondLaunchWindow.qml", + "method": "closeSecondLaunchWindow" + }); + } + } + } + + Image { + id: topLeftAccentImage + width: 130 + height: 320 + anchors.left: parent.left + anchors.top: parent.top + source: "images/standOutTopLeft.png" + } + + Image { + id: bottomRightAccentImage + width: 250 + height: 80 + anchors.right: parent.right + anchors.bottom: parent.bottom + source: "images/standOutBottomRight.png" + } + + signal sendToScript(var message); +} diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/images/cameraControls.png b/scripts/simplifiedUI/ui/simplifiedFTUE/images/cameraControls.png new file mode 100644 index 0000000000..e54e26a3ba Binary files /dev/null and b/scripts/simplifiedUI/ui/simplifiedFTUE/images/cameraControls.png differ diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/images/defaultBottomRight.png b/scripts/simplifiedUI/ui/simplifiedFTUE/images/defaultBottomRight.png new file mode 100644 index 0000000000..1668347aa4 Binary files /dev/null and b/scripts/simplifiedUI/ui/simplifiedFTUE/images/defaultBottomRight.png differ diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/images/defaultTopLeft.png b/scripts/simplifiedUI/ui/simplifiedFTUE/images/defaultTopLeft.png new file mode 100644 index 0000000000..c863569d7f Binary files /dev/null and b/scripts/simplifiedUI/ui/simplifiedFTUE/images/defaultTopLeft.png differ diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/images/mouseControls.png b/scripts/simplifiedUI/ui/simplifiedFTUE/images/mouseControls.png new file mode 100644 index 0000000000..6354b1aeae Binary files /dev/null and b/scripts/simplifiedUI/ui/simplifiedFTUE/images/mouseControls.png differ diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/images/runJumpControls.png b/scripts/simplifiedUI/ui/simplifiedFTUE/images/runJumpControls.png new file mode 100644 index 0000000000..af0492475d Binary files /dev/null and b/scripts/simplifiedUI/ui/simplifiedFTUE/images/runJumpControls.png differ diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/images/standOutBottomRight.png b/scripts/simplifiedUI/ui/simplifiedFTUE/images/standOutBottomRight.png new file mode 100644 index 0000000000..8b9983bb88 Binary files /dev/null and b/scripts/simplifiedUI/ui/simplifiedFTUE/images/standOutBottomRight.png differ diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/images/standOutTopLeft.png b/scripts/simplifiedUI/ui/simplifiedFTUE/images/standOutTopLeft.png new file mode 100644 index 0000000000..30cb623f42 Binary files /dev/null and b/scripts/simplifiedUI/ui/simplifiedFTUE/images/standOutTopLeft.png differ diff --git a/scripts/simplifiedUI/ui/simplifiedFTUE/images/walkingControls.png b/scripts/simplifiedUI/ui/simplifiedFTUE/images/walkingControls.png new file mode 100644 index 0000000000..bedc6991bb Binary files /dev/null and b/scripts/simplifiedUI/ui/simplifiedFTUE/images/walkingControls.png differ diff --git a/scripts/simplifiedUI/ui/simplifiedUI.js b/scripts/simplifiedUI/ui/simplifiedUI.js index 1154e386ea..3025b938cb 100644 --- a/scripts/simplifiedUI/ui/simplifiedUI.js +++ b/scripts/simplifiedUI/ui/simplifiedUI.js @@ -14,7 +14,6 @@ // START CONFIG OPTIONS -var DOCKED_QML_SUPPORTED = true; var TOOLBAR_NAME = "com.highfidelity.interface.toolbar.system"; var DEFAULT_SCRIPTS_PATH_PREFIX = ScriptDiscoveryService.defaultScriptsPath + "/"; // END CONFIG OPTIONS @@ -160,6 +159,7 @@ var SETTINGS_APP_WINDOW_FLAGS = 0x00000001 | // Qt::Window 0x08000000 | // Qt::WindowCloseButtonHint 0x00008000 | // Qt::WindowMaximizeButtonHint 0x00004000; // Qt::WindowMinimizeButtonHint +var SETTINGS_APP_RIGHT_MARGIN = 48; var settingsAppWindow = false; function toggleSettingsApp() { if (settingsAppWindow) { @@ -179,7 +179,7 @@ function toggleSettingsApp() { y: SETTINGS_APP_HEIGHT_PX }, position: { - x: Math.max(Window.x + POPOUT_SAFE_MARGIN_X, Window.x + Window.innerWidth / 2 - SETTINGS_APP_WIDTH_PX / 2), + x: Window.x + Window.innerWidth - SETTINGS_APP_WIDTH_PX - SETTINGS_APP_RIGHT_MARGIN, y: Math.max(Window.y + POPOUT_SAFE_MARGIN_Y, Window.y + Window.innerHeight / 2 - SETTINGS_APP_HEIGHT_PX / 2) }, overrideFlags: SETTINGS_APP_WINDOW_FLAGS @@ -283,28 +283,21 @@ function maybeDeleteOutputDeviceMutedOverlay() { var outputDeviceMutedOverlay = false; var OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_DIMS_PX = 300; -var OUTPUT_DEVICE_MUTED_MARGIN_BOTTOM_PX = 20; -var OUTPUT_DEVICE_MUTED_MARGIN_LEFT_RIGHT_PX = 20; +var OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_MARGINS_PX = 20; +var OUTPUT_DEVICE_MUTED_DIMS_RATIO_TO_WINDOW_SIZE = 0.8; function updateOutputDeviceMutedOverlay(isMuted) { if (isMuted) { var props = { imageURL: Script.resolvePath("images/outputDeviceMuted.svg"), alpha: 0.5 }; + var overlayDims = OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_DIMS_PX; - props.x = Window.innerWidth / 2 - overlayDims / 2; - props.y = Window.innerHeight / 2 - overlayDims / 2; + var overlayWithMarginsDims = overlayDims + 2 * OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_MARGINS_PX; - var outputDeviceMutedOverlayBottomY = props.y + overlayDims; - var inputDeviceMutedOverlayTopY = INPUT_DEVICE_MUTED_MARGIN_TOP_PX; - if (outputDeviceMutedOverlayBottomY + OUTPUT_DEVICE_MUTED_MARGIN_BOTTOM_PX > inputDeviceMutedOverlayTopY) { - overlayDims = 2 * (inputDeviceMutedOverlayTopY - Window.innerHeight / 2 - OUTPUT_DEVICE_MUTED_MARGIN_BOTTOM_PX); - } - - if (overlayDims + OUTPUT_DEVICE_MUTED_MARGIN_LEFT_RIGHT_PX > Window.innerWidth) { - overlayDims = Math.min(Window.innerWidth - OUTPUT_DEVICE_MUTED_MARGIN_LEFT_RIGHT_PX, overlayDims); - } else { - overlayDims = Math.min(OUTPUT_DEVICE_MUTED_OVERLAY_DEFAULT_DIMS_PX, overlayDims); + if (overlayWithMarginsDims > Window.innerHeight || overlayWithMarginsDims > Window.innerWidth) { + var minWindowDims = Math.min(Window.innerHeight, Window.innerWidth); + overlayDims = Math.round(minWindowDims * OUTPUT_DEVICE_MUTED_DIMS_RATIO_TO_WINDOW_SIZE); } props.width = overlayDims; @@ -358,6 +351,124 @@ function setOutputMuted(outputMuted) { } } +var TOP_BAR_HEIGHT_PX = 48; +var INITIAL_LAUNCH_QML_PATH = Script.resolvePath("./simplifiedFTUE/InitialLaunchWindow.qml"); +var INITIAL_LAUNCH_WINDOW_TITLE = "Initial Launch"; +var INITIAL_LAUNCH_PRESENTATION_MODE = Desktop.PresentationMode.NATIVE; +var INITIAL_WINDOW_FLAGS = 0x00000001 | // Qt::Window +0x00000008 | // Qt::Popup +0x00000002 | // Qt::Tool +0x00000800 | // Qt::FramelessWindowHint +0x40000000; // Qt::NoDropShadowWindowHint +var initialLaunchWindow = false; +function displayInitialLaunchWindow() { + if (initialLaunchWindow) { + return; + } + + simplifiedEmote.handleFTUEScreensVisibilityChanged(true); + + initialLaunchWindow = Desktop.createWindow(INITIAL_LAUNCH_QML_PATH, { + title: INITIAL_LAUNCH_WINDOW_TITLE, + presentationMode: INITIAL_LAUNCH_PRESENTATION_MODE, + isFullScreenWindow: true, + overrideFlags: INITIAL_WINDOW_FLAGS + }); + + initialLaunchWindow.fromQml.connect(onMessageFromInitialLaunchWindow); + + Window.location = "file:///~/serverless/tutorial.json"; +} + +var SECOND_LAUNCH_QML_PATH = Script.resolvePath("simplifiedFTUE/SecondLaunchWindow.qml"); +var SECOND_LAUNCH_WINDOW_TITLE = "Second Launch"; +var SECOND_LAUNCH_PRESENTATION_MODE = Desktop.PresentationMode.NATIVE; +var SECOND_WINDOW_FLAGS = 0x00000001 | // Qt::Window +0x00000008 | // Qt::Popup +0x00000002 | // Qt::Tool +0x00000800 | // Qt::FramelessWindowHint +0x40000000; // Qt::NoDropShadowWindowHint +var secondLaunchWindow = false; +function displaySecondLaunchWindow() { + if (secondLaunchWindow) { + return; + } + + simplifiedEmote.handleFTUEScreensVisibilityChanged(true); + + secondLaunchWindow = Desktop.createWindow(SECOND_LAUNCH_QML_PATH, { + title: SECOND_LAUNCH_WINDOW_TITLE, + presentationMode: SECOND_LAUNCH_PRESENTATION_MODE, + isFullScreenWindow: true, + overrideFlags: SECOND_WINDOW_FLAGS + }); + + secondLaunchWindow.fromQml.connect(onMessageFromSecondLaunchWindow); + + Window.location = "file:///~/serverless/tutorial.json"; +} + +function closeInitialLaunchWindow() { + if (initialLaunchWindow) { + initialLaunchWindow.fromQml.disconnect(onMessageFromInitialLaunchWindow); + initialLaunchWindow.close(); + initialLaunchWindow = null; + } + + simplifiedEmote.handleFTUEScreensVisibilityChanged(false); +} + +function closeSecondLaunchWindow() { + if (secondLaunchWindow) { + secondLaunchWindow.fromQml.disconnect(onMessageFromSecondLaunchWindow); + secondLaunchWindow.close(); + secondLaunchWindow = null; + } + + simplifiedEmote.handleFTUEScreensVisibilityChanged(false); +} + +var INITIAL_LAUNCH_WINDOW_MESSAGE_SOURCE = "InitialLaunchWindow.qml"; +function onMessageFromInitialLaunchWindow(message) { + if (message.source !== INITIAL_LAUNCH_WINDOW_MESSAGE_SOURCE) { + return; + } + + switch (message.method) { + case "closeInitialLaunchWindow": + closeInitialLaunchWindow(); + var homeLocation = LocationBookmarks.getAddress("hqhome"); + if (homeLocation) { + Window.location = homeLocation; + } + break; + + default: + console.log("Unrecognized message from " + INITIAL_LAUNCH_WINDOW_MESSAGE_SOURCE + ": " + JSON.stringify(message)); + break; + } +} + +var SECOND_LAUNCH_WINDOW_MESSAGE_SOURCE = "SecondLaunchWindow.qml"; +function onMessageFromSecondLaunchWindow(message) { + if (message.source !== SECOND_LAUNCH_WINDOW_MESSAGE_SOURCE) { + return; + } + + switch (message.method) { + case "closeSecondLaunchWindow": + closeSecondLaunchWindow(); + var homeLocation = LocationBookmarks.getAddress("hqhome"); + if (homeLocation) { + Window.location = homeLocation; + } + break; + + default: + console.log("Unrecognized message from " + SECOND_LAUNCH_WINDOW_MESSAGE_SOURCE + ": " + JSON.stringify(message)); + break; + } +} var WAIT_FOR_TOP_BAR_MS = 1000; function sendLocalStatusToQml() { @@ -403,6 +514,14 @@ function onMessageFromTopBar(message) { si.toggleStatus(); break; + case "displayInitialLaunchWindow": + displayInitialLaunchWindow(); + break; + + case "displaySecondLaunchWindow": + displaySecondLaunchWindow(); + break; + default: console.log("Unrecognized message from " + TOP_BAR_MESSAGE_SOURCE + ": " + JSON.stringify(message)); break; @@ -431,7 +550,6 @@ var TOP_BAR_QML_PATH = Script.resourcesPath() + "qml/hifi/simplifiedUI/topBar/Si var TOP_BAR_WINDOW_TITLE = "Simplified Top Bar"; var TOP_BAR_PRESENTATION_MODE = Desktop.PresentationMode.NATIVE; var TOP_BAR_WIDTH_PX = Window.innerWidth; -var TOP_BAR_HEIGHT_PX = 48; var topBarWindow = false; function loadSimplifiedTopBar() { var windowProps = { @@ -442,16 +560,9 @@ function loadSimplifiedTopBar() { y: TOP_BAR_HEIGHT_PX } }; - if (DOCKED_QML_SUPPORTED) { - windowProps.presentationWindowInfo = { - dockArea: Desktop.DockArea.TOP - }; - } else { - windowProps.position = { - x: Window.x, - y: Window.y - }; - } + windowProps.presentationWindowInfo = { + dockArea: Desktop.DockArea.TOP + }; topBarWindow = Desktop.createWindow(TOP_BAR_QML_PATH, windowProps); topBarWindow.fromQml.connect(onMessageFromTopBar); @@ -516,32 +627,53 @@ function onHMDInputDeviceMutedChanged(isMuted) { function onGeometryChanged(rect) { updateInputDeviceMutedOverlay(Audio.muted); updateOutputDeviceMutedOverlay(isOutputMuted()); - if (topBarWindow && !DOCKED_QML_SUPPORTED) { - topBarWindow.size = { - "x": rect.width, - "y": TOP_BAR_HEIGHT_PX - }; - topBarWindow.position = { - "x": rect.x, - "y": rect.y - }; +} + +var initialLaunchWindowIsMinimized = false; +var secondLaunchWindowIsMinimized = false; +function onWindowMinimizedChanged(isMinimized) { + if (isMinimized) { + handleInitialLaunchWindowVisibleChanged(false); + handleSecondLaunchWindowVisibleChanged(false); + } else if (!HMD.active) { + handleInitialLaunchWindowVisibleChanged(true); + handleSecondLaunchWindowVisibleChanged(true); } } -function onWindowMinimizedChanged() { - // prerequisite placeholder for Reduce Friction of Customer Acquisition sub task: https://highfidelity.atlassian.net/browse/DEV-585 - print("WINDOW MINIMIZED CHANGED SIGNAL"); +function handleInitialLaunchWindowVisibleChanged(shouldBeVisible) { + if (shouldBeVisible && !initialLaunchWindow && initialLaunchWindowIsMinimized) { + displayInitialLaunchWindow(); + initialLaunchWindowIsMinimized = false; + } else if (!shouldBeVisible && initialLaunchWindow) { + closeInitialLaunchWindow(); + initialLaunchWindowIsMinimized = true; + } +} + +function handleSecondLaunchWindowVisibleChanged(shouldBeVisible) { + if (shouldBeVisible && !secondLaunchWindow && secondLaunchWindowIsMinimized) { + displaySecondLaunchWindow(); + secondLaunchWindowIsMinimized = false; + } else if (!shouldBeVisible && secondLaunchWindow) { + closeSecondLaunchWindow(); + secondLaunchWindowIsMinimized = true; + } } function onDisplayModeChanged(isHMDMode) { if (isHMDMode) { - Camera.setModeString("first person"); + Camera.setModeString("first person look at"); } if (isHMDMode) { onHMDInputDeviceMutedChanged(Audio.mutedHMD); + handleInitialLaunchWindowVisibleChanged(false); + handleSecondLaunchWindowVisibleChanged(false); } else { onDesktopInputDeviceMutedChanged(Audio.mutedDesktop); + handleInitialLaunchWindowVisibleChanged(true); + handleSecondLaunchWindowVisibleChanged(true); } } @@ -584,9 +716,9 @@ function restoreLODSettings() { } -var nametag = Script.require("./simplifiedNametag/simplifiedNametag.js?" + Date.now()); -var si = Script.require("./simplifiedStatusIndicator/simplifiedStatusIndicator.js?" + Date.now()); -var emote = Script.require("../simplifiedEmote/simplifiedEmote.js?" + Date.now()); +var nametag = Script.require("./simplifiedNametag/simplifiedNametag.js"); +var si = Script.require("./simplifiedStatusIndicator/simplifiedStatusIndicator.js"); +var simplifiedEmote = Script.require("../simplifiedEmote/simplifiedEmote.js"); var oldShowAudioTools; var oldShowBubbleTools; var keepExistingUIAndScriptsSetting = Settings.getValue("simplifiedUI/keepExistingUIAndScripts", false); @@ -653,6 +785,14 @@ function shutdown() { settingsAppWindow.close(); } + if (initialLaunchWindow) { + closeInitialLaunchWindow(); + } + + if (secondLaunchWindow) { + closeSecondLaunchWindow(); + } + maybeDeleteInputDeviceMutedOverlay(); maybeDeleteOutputDeviceMutedOverlay(); diff --git a/scripts/system/away.js b/scripts/system/away.js index 6293c0c452..da24e0c3f8 100644 --- a/scripts/system/away.js +++ b/scripts/system/away.js @@ -50,11 +50,11 @@ var OVERLAY_DATA_HMD = { }; var AWAY_INTRO = { - url: "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/kneel.fbx", + url: "https://hifi-content.s3.amazonaws.com/doug/animation/fbx/afk_texting.fbx", playbackRate: 30.0, - loopFlag: false, - startFrame: 0.0, - endFrame: 83.0 + loopFlag: true, + startFrame: 1.0, + endFrame: 489.0 }; // MAIN CONTROL diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index cda6fc9be0..24f443f901 100644 --- a/scripts/system/create/assets/data/createAppTooltips.json +++ b/scripts/system/create/assets/data/createAppTooltips.json @@ -76,7 +76,7 @@ "tooltip": "The angle in deg at which light emits. Starts in the entity's -z direction, and rotates around its x axis." }, "keyLight.castShadows": { - "tooltip": "If enabled, shadows are cast. The entity or avatar casting the shadow must also have Cast Shadows enabled." + "tooltip": "If enabled, shadows are cast. The entity or avatar casting the shadow must also have Cast Shadows enabled. Note: Shadows are rendered only on high-profiled computers. This setting will have no effect on computers profiled to medium or low graphics." }, "keyLight.shadowBias": { "tooltip": "The bias of the shadows cast by the light. Use this to fine-tune your shadows to your scene to prevent shadow acne and peter panning." @@ -500,7 +500,7 @@ "tooltip": "If enabled, grabbed entities will follow the movements of your hand controller instead of your avatar's hand." }, "canCastShadow": { - "tooltip": "If enabled, this geometry of this entity casts shadows when a shadow-casting light source shines on it." + "tooltip": "If enabled, the geometry of this entity casts shadows when a shadow-casting light source shines on it. Note: Shadows are rendered only on high-profiled computers. This setting will have no effect on computers profiled to medium or low graphics.." }, "ignorePickIntersection": { "tooltip": "If enabled, this entity will not be considered for ray picks, and will also not occlude other entities when picking." diff --git a/scripts/system/firstPersonHMD.js b/scripts/system/firstPersonHMD.js index 5fdee1b7b5..5e81d53f6b 100644 --- a/scripts/system/firstPersonHMD.js +++ b/scripts/system/firstPersonHMD.js @@ -16,7 +16,7 @@ // Automatically enter first person mode when entering HMD mode HMD.displayModeChanged.connect(function(isHMDMode) { if (isHMDMode) { - Camera.setModeString("first person"); + Camera.setModeString("first person look at"); } }); diff --git a/scripts/system/interstitialPage.js b/scripts/system/interstitialPage.js index 8ecc982dab..2d225fd2a6 100644 --- a/scripts/system/interstitialPage.js +++ b/scripts/system/interstitialPage.js @@ -272,7 +272,7 @@ currentProgress = 0.0; connectionToDomainFailed = false; previousCameraMode = Camera.mode; - Camera.mode = "first person"; + Camera.mode = "first person look at"; updateProgressBar(0.0); scaleInterstitialPage(MyAvatar.sensorToWorldScale); timer = Script.setTimeout(update, 2000); diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 0ee5259ffa..a92fbf1065 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -52,8 +52,8 @@ function calcSpawnInfo(hand, landscape) { var LEFT_HAND = Controller.Standard.LeftHand; var sensorToWorldScale = MyAvatar.sensorToWorldScale; - var headPos = (HMD.active && Camera.mode === "first person") ? HMD.position : Camera.position; - var headRot = Quat.cancelOutRollAndPitch((HMD.active && Camera.mode === "first person") ? + var headPos = (HMD.active && (Camera.mode === "first person" || Camera.mode === "first person look at")) ? HMD.position : Camera.position; + var headRot = Quat.cancelOutRollAndPitch((HMD.active && (Camera.mode === "first person" || Camera.mode === "first person look at")) ? HMD.orientation : Camera.orientation); var right = Quat.getRight(headRot); diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 91c8b1edcf..f5b5ecf0a1 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -53,7 +53,7 @@ function handJointName(hand) { var jointName; if (hand === LEFT_HAND) { - if (Camera.mode === "first person") { + if (Camera.mode === "first person" || Camera.mode === "first person look at") { jointName = "_CONTROLLER_LEFTHAND"; } else if (Camera.mode === "third person") { jointName = "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"; @@ -61,7 +61,7 @@ jointName = "LeftHand"; } } else { - if (Camera.mode === "first person") { + if (Camera.mode === "first person" || Camera.mode === "first person look at") { jointName = "_CONTROLLER_RIGHTHAND"; } else if (Camera.mode === "third person") { jointName = "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND";