diff --git a/BUILD_OSX.md b/BUILD_OSX.md index 3365627b8c..586f81def3 100644 --- a/BUILD_OSX.md +++ b/BUILD_OSX.md @@ -4,7 +4,6 @@ Please read the [general build guide](BUILD.md) for information on dependencies [Homebrew](https://brew.sh/) is an excellent package manager for OS X. It makes install of some High Fidelity dependencies very simple. - brew tap homebrew/versions brew install cmake openssl ### OpenSSL diff --git a/domain-server/resources/web/assignment/placeholder.js b/domain-server/resources/web/assignment/placeholder.js index 2c1d8253aa..bf64539bea 100644 --- a/domain-server/resources/web/assignment/placeholder.js +++ b/domain-server/resources/web/assignment/placeholder.js @@ -1,3 +1,3 @@ // Here you can put a script that will be run by an assignment-client (AC) -// For examples, please go to http://public.highfidelity.io/scripts +// For examples, please go to https://github.com/highfidelity/hifi/tree/master/script-archive/acScripts // The directory named acScripts contains assignment-client specific scripts you can try. diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml index 2e7ff39ed6..e21e8b7354 100644 --- a/interface/resources/qml/LoginDialog.qml +++ b/interface/resources/qml/LoginDialog.qml @@ -27,6 +27,8 @@ ModalWindow { destroyOnHidden: true visible: true + readonly property bool isTablet: false + property string iconText: "" property int iconSize: 50 @@ -35,6 +37,10 @@ ModalWindow { keyboardOverride: true // Disable ModalWindow's keyboard. + function tryDestroy() { + root.destroy() + } + LoginDialog { id: loginDialog diff --git a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml index e06ce239ab..fe4c511f1d 100644 --- a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml +++ b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml @@ -29,11 +29,12 @@ Item { readonly property int maxHeight: 720 function resize() { - var targetWidth = Math.max(titleWidth, Math.max(additionalTextContainer.contentWidth, - termsContainer.contentWidth)) + if (root.isTablet === false) { + var targetWidth = Math.max(titleWidth, Math.max(additionalTextContainer.contentWidth, + termsContainer.contentWidth)) + parent.width = root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)) + } var targetHeight = 5 * hifi.dimensions.contentSpacing.y + buttons.height + additionalTextContainer.height + termsContainer.height - - parent.width = root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)) parent.height = root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) } } @@ -61,11 +62,8 @@ Item { Button { anchors.verticalCenter: parent.verticalCenter - text: qsTr("Cancel") - - - onClicked: root.destroy() + onClicked: root.tryDestroy() } } @@ -96,17 +94,19 @@ Item { id: termsContainer anchors { top: additionalTextContainer.bottom - left: parent.left margins: 0 topMargin: 2 * hifi.dimensions.contentSpacing.y + horizontalCenter: parent.horizontalCenter } + width: parent.width text: qsTr("By creating this user profile, you agree to High Fidelity's Terms of Service") wrapMode: Text.WordWrap color: hifi.colors.baseGrayHighlight lineHeight: 1 lineHeightMode: Text.ProportionalHeight - horizontalAlignment: Text.AlignHCenter + fontSizeMode: Text.HorizontalFit + horizontalAlignment: Text.AlignVCenter onLinkActivated: loginDialog.openUrl(link) } @@ -128,8 +128,10 @@ Item { console.log("Create Failed: " + error) bodyLoader.source = "UsernameCollisionBody.qml" - bodyLoader.item.width = root.pane.width - bodyLoader.item.height = root.pane.height + if (!root.isTablet) { + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } } onHandleLoginCompleted: { console.log("Login Succeeded") diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index e27635dbbd..c73aab08c3 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -9,7 +9,7 @@ // import Hifi 1.0 -import QtQuick 2.4 +import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 as OriginalStyles @@ -45,8 +45,7 @@ Item { function resize() { var targetWidth = Math.max(titleWidth, form.contentWidth); var targetHeight = hifi.dimensions.contentSpacing.y + mainTextContainer.height + - 4 * hifi.dimensions.contentSpacing.y + form.height + - hifi.dimensions.contentSpacing.y + buttons.height; + 4 * hifi.dimensions.contentSpacing.y + form.height; if (additionalInformation.visible) { targetWidth = Math.max(targetWidth, additionalInformation.width); @@ -66,9 +65,6 @@ Item { if (loginDialog.isSteamRunning()) { additionalInformation.visible = !isLoading } - - leftButton.visible = !isLoading - buttons.visible = !isLoading } BusyIndicator { @@ -108,30 +104,27 @@ Item { Column { id: form + width: parent.width + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + anchors { top: mainTextContainer.bottom - left: parent.left - margins: 0 topMargin: 2 * hifi.dimensions.contentSpacing.y } spacing: 2 * hifi.dimensions.contentSpacing.y - Row { - spacing: hifi.dimensions.contentSpacing.x - TextField { - id: usernameField - anchors { - verticalCenter: parent.verticalCenter - } - width: 350 - - label: "Username or Email" - } + TextField { + id: usernameField + width: parent.width + focus: true + label: "Username or Email" ShortcutText { anchors { - verticalCenter: parent.verticalCenter + verticalCenter: usernameField.textFieldLabel.verticalCenter + left: usernameField.textFieldLabel.right + leftMargin: 10 } text: "Forgot Username?" @@ -143,23 +136,19 @@ Item { onLinkActivated: loginDialog.openUrl(link) } } - Row { - spacing: hifi.dimensions.contentSpacing.x - TextField { - id: passwordField - anchors { - verticalCenter: parent.verticalCenter - } - width: 350 + TextField { + id: passwordField + width: parent.width - label: "Password" - echoMode: TextInput.Password - } + label: "Password" + echoMode: showPassword.checked ? TextInput.Normal : TextInput.Password ShortcutText { anchors { - verticalCenter: parent.verticalCenter + verticalCenter: passwordField.textFieldLabel.verticalCenter + left: passwordField.textFieldLabel.right + leftMargin: 10 } text: "Forgot Password?" @@ -172,25 +161,76 @@ Item { } } - } - - InfoItem { - id: additionalInformation - anchors { - top: form.bottom - left: parent.left - margins: 0 - topMargin: hifi.dimensions.contentSpacing.y + CheckBoxQQC2 { + id: showPassword + text: "Show password" } - visible: loginDialog.isSteamRunning() + InfoItem { + id: additionalInformation - text: qsTr("Your steam account informations will not be exposed to other users.") - wrapMode: Text.WordWrap - color: hifi.colors.baseGrayHighlight - lineHeight: 1 - lineHeightMode: Text.ProportionalHeight - horizontalAlignment: Text.AlignHCenter + visible: loginDialog.isSteamRunning() + + text: qsTr("Your steam account informations will not be exposed to other users.") + wrapMode: Text.WordWrap + color: hifi.colors.baseGrayHighlight + lineHeight: 1 + lineHeightMode: Text.ProportionalHeight + horizontalAlignment: Text.AlignHCenter + } + + Row { + id: buttons + spacing: hifi.dimensions.contentSpacing.y*2 + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + anchors.horizontalCenter: parent.horizontalCenter + + Button { + id: linkAccountButton + anchors.verticalCenter: parent.verticalCenter + width: 200 + + text: qsTr(loginDialog.isSteamRunning() ? "Link Account" : "Login") + color: hifi.buttons.blue + + onClicked: linkAccountBody.login() + } + + Button { + anchors.verticalCenter: parent.verticalCenter + text: qsTr("Cancel") + onClicked: root.tryDestroy() + } + } + + Row { + id: leftButton + + anchors.horizontalCenter: parent.horizontalCenter + spacing: hifi.dimensions.contentSpacing.y*2 + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + + RalewaySemiBold { + size: hifi.fontSizes.inputLabel + anchors.verticalCenter: parent.verticalCenter + text: qsTr("Don't have an account?") + } + + Button { + anchors.verticalCenter: parent.verticalCenter + + text: qsTr("Sign Up") + visible: !loginDialog.isSteamRunning() + + onClicked: { + bodyLoader.setSource("SignUpBody.qml") + if (!root.isTablet) { + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + } + } + } } // Override ScrollingWindow's keyboard that would be at very bottom of dialog. @@ -200,70 +240,22 @@ Item { anchors { left: parent.left right: parent.right - bottom: buttons.top + bottom: parent.bottom bottomMargin: keyboardRaised ? 2 * hifi.dimensions.contentSpacing.y : 0 } } - Row { - id: leftButton - anchors { - left: parent.left - bottom: parent.bottom - bottomMargin: hifi.dimensions.contentSpacing.y - } - - spacing: hifi.dimensions.contentSpacing.x - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - - Button { - anchors.verticalCenter: parent.verticalCenter - - text: qsTr("Sign Up") - visible: !loginDialog.isSteamRunning() - - onClicked: { - bodyLoader.setSource("SignUpBody.qml") - bodyLoader.item.width = root.pane.width - bodyLoader.item.height = root.pane.height - } - } - } - - Row { - id: buttons - anchors { - right: parent.right - bottom: parent.bottom - bottomMargin: hifi.dimensions.contentSpacing.y - } - spacing: hifi.dimensions.contentSpacing.x - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - - Button { - id: linkAccountButton - anchors.verticalCenter: parent.verticalCenter - width: 200 - - text: qsTr(loginDialog.isSteamRunning() ? "Link Account" : "Login") - color: hifi.buttons.blue - - onClicked: linkAccountBody.login() - } - - Button { - anchors.verticalCenter: parent.verticalCenter - - text: qsTr("Cancel") - - onClicked: root.destroy() - } - } - Component.onCompleted: { root.title = qsTr("Sign Into High Fidelity") root.iconText = "<" - keyboardEnabled = HMD.active; + + //dont rise local keyboard + keyboardEnabled = !root.isTablet && HMD.active; + //but rise Tablet's one instead for Tablet interface + if (root.isTablet) { + root.keyboardEnabled = HMD.active; + root.keyboardRaised = Qt.binding( function() { return keyboardRaised; }) + } d.resize(); if (failAfterSignUp) { @@ -311,11 +303,11 @@ Item { } switch (event.key) { - case Qt.Key_Enter: - case Qt.Key_Return: - event.accepted = true - linkAccountBody.login() - break + case Qt.Key_Enter: + case Qt.Key_Return: + event.accepted = true + linkAccountBody.login() + break } } } diff --git a/interface/resources/qml/LoginDialog/SignInBody.qml b/interface/resources/qml/LoginDialog/SignInBody.qml index 167ed1640a..c4b6c2aee1 100644 --- a/interface/resources/qml/LoginDialog/SignInBody.qml +++ b/interface/resources/qml/LoginDialog/SignInBody.qml @@ -9,7 +9,7 @@ // import Hifi 1.0 -import QtQuick 2.4 +import QtQuick 2.7 import QtQuick.Controls.Styles 1.4 as OriginalStyles import "../controls-uit" @@ -18,8 +18,8 @@ import "../styles-uit" Item { id: signInBody clip: true - width: root.pane.width height: root.pane.height + width: root.pane.width property bool required: false @@ -29,7 +29,7 @@ Item { } function cancel() { - root.destroy() + root.tryDestroy() } QtObject { @@ -121,8 +121,10 @@ Item { console.log("Login Failed") bodyLoader.source = "CompleteProfileBody.qml" - bodyLoader.item.width = root.pane.width - bodyLoader.item.height = root.pane.height + if (!root.isTablet) { + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } } } } diff --git a/interface/resources/qml/LoginDialog/SignUpBody.qml b/interface/resources/qml/LoginDialog/SignUpBody.qml index c0ff2d77cb..f6cf40db8e 100644 --- a/interface/resources/qml/LoginDialog/SignUpBody.qml +++ b/interface/resources/qml/LoginDialog/SignUpBody.qml @@ -9,7 +9,7 @@ // import Hifi 1.0 -import QtQuick 2.4 +import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 as OriginalStyles @@ -44,8 +44,7 @@ Item { function resize() { var targetWidth = Math.max(titleWidth, form.contentWidth); var targetHeight = hifi.dimensions.contentSpacing.y + mainTextContainer.height + - 4 * hifi.dimensions.contentSpacing.y + form.height + - hifi.dimensions.contentSpacing.y + buttons.height; + 4 * hifi.dimensions.contentSpacing.y + form.height; parent.width = root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)); parent.height = root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) @@ -96,44 +95,31 @@ Item { Column { id: form + width: parent.width + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + anchors { top: mainTextContainer.bottom - left: parent.left - margins: 0 topMargin: 2 * hifi.dimensions.contentSpacing.y } spacing: 2 * hifi.dimensions.contentSpacing.y - Row { - spacing: hifi.dimensions.contentSpacing.x - - TextField { - id: emailField - anchors { - verticalCenter: parent.verticalCenter - } - width: 350 - - label: "Email" - } + TextField { + id: emailField + width: parent.width + label: "Email" } - Row { - spacing: hifi.dimensions.contentSpacing.x - - TextField { - id: usernameField - anchors { - verticalCenter: parent.verticalCenter - } - width: 350 - - label: "Username" - } + TextField { + id: usernameField + width: parent.width + label: "Username" ShortcutText { anchors { - verticalCenter: parent.verticalCenter + verticalCenter: parent.textFieldLabel.verticalCenter + left: parent.textFieldLabel.right + leftMargin: 10 } text: qsTr("No spaces / special chars.") @@ -145,23 +131,17 @@ Item { } } - Row { - spacing: hifi.dimensions.contentSpacing.x - - TextField { - id: passwordField - anchors { - verticalCenter: parent.verticalCenter - } - width: 350 - - label: "Password" - echoMode: TextInput.Password - } + TextField { + id: passwordField + width: parent.width + label: "Password" + echoMode: TextInput.Password ShortcutText { anchors { - verticalCenter: parent.verticalCenter + verticalCenter: parent.textFieldLabel.verticalCenter + left: parent.textFieldLabel.right + leftMargin: 10 } text: qsTr("At least 6 characters") @@ -173,6 +153,53 @@ Item { } } + Row { + id: leftButton + anchors.horizontalCenter: parent.horizontalCenter + + spacing: hifi.dimensions.contentSpacing.x + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + + Button { + anchors.verticalCenter: parent.verticalCenter + + text: qsTr("Existing User") + + onClicked: { + bodyLoader.setSource("LinkAccountBody.qml") + if (!root.isTablet) { + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + } + } + } + + Row { + id: buttons + anchors.horizontalCenter: parent.horizontalCenter + spacing: hifi.dimensions.contentSpacing.x + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + + Button { + id: linkAccountButton + anchors.verticalCenter: parent.verticalCenter + width: 200 + + text: qsTr("Sign Up") + color: hifi.buttons.blue + + onClicked: signupBody.signup() + } + + Button { + anchors.verticalCenter: parent.verticalCenter + + text: qsTr("Cancel") + + onClicked: root.tryDestroy() + } + } } // Override ScrollingWindow's keyboard that would be at very bottom of dialog. @@ -182,69 +209,21 @@ Item { anchors { left: parent.left right: parent.right - bottom: buttons.top + bottom: parent.bottom bottomMargin: keyboardRaised ? 2 * hifi.dimensions.contentSpacing.y : 0 } } - Row { - id: leftButton - anchors { - left: parent.left - bottom: parent.bottom - bottomMargin: hifi.dimensions.contentSpacing.y - } - - spacing: hifi.dimensions.contentSpacing.x - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - - Button { - anchors.verticalCenter: parent.verticalCenter - - text: qsTr("Existing User") - - onClicked: { - bodyLoader.setSource("LinkAccountBody.qml") - bodyLoader.item.width = root.pane.width - bodyLoader.item.height = root.pane.height - } - } - } - - Row { - id: buttons - anchors { - right: parent.right - bottom: parent.bottom - bottomMargin: hifi.dimensions.contentSpacing.y - } - spacing: hifi.dimensions.contentSpacing.x - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - - Button { - id: linkAccountButton - anchors.verticalCenter: parent.verticalCenter - width: 200 - - text: qsTr("Sign Up") - color: hifi.buttons.blue - - onClicked: signupBody.signup() - } - - Button { - anchors.verticalCenter: parent.verticalCenter - - text: qsTr("Cancel") - - onClicked: root.destroy() - } - } - Component.onCompleted: { root.title = qsTr("Create an Account") root.iconText = "<" - keyboardEnabled = HMD.active; + //dont rise local keyboard + keyboardEnabled = !root.isTablet && HMD.active; + //but rise Tablet's one instead for Tablet interface + if (root.isTablet) { + root.keyboardEnabled = HMD.active; + root.keyboardRaised = Qt.binding( function() { return keyboardRaised; }) + } d.resize(); emailField.forceActiveFocus(); @@ -275,8 +254,10 @@ Item { onHandleLoginFailed: { // we failed to login, show the LoginDialog so the user will try again bodyLoader.setSource("LinkAccountBody.qml", { "failAfterSignUp": true }) - bodyLoader.item.width = root.pane.width - bodyLoader.item.height = root.pane.height + if (!root.isTablet) { + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } } } diff --git a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml index 18c831b3a9..5c212578b8 100644 --- a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml +++ b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml @@ -79,7 +79,7 @@ Item { margins: 0 topMargin: hifi.dimensions.contentSpacing.y } - width: 250 + width: parent.width placeholderText: "Choose your own" } @@ -102,7 +102,7 @@ Item { bottom: parent.bottom right: parent.right margins: 0 - topMargin: hifi.dimensions.contentSpacing.y + bottomMargin: hifi.dimensions.contentSpacing.y } spacing: hifi.dimensions.contentSpacing.x onHeightChanged: d.resize(); onWidthChanged: d.resize(); @@ -122,14 +122,21 @@ Item { text: qsTr("Cancel") - onClicked: root.destroy() + onClicked: root.tryDestroy() } } Component.onCompleted: { root.title = qsTr("Complete Your Profile") root.iconText = "<" - keyboardEnabled = HMD.active; + //dont rise local keyboard + keyboardEnabled = !root.isTablet && HMD.active; + //but rise Tablet's one instead for Tablet interface + if (root.isTablet) { + root.keyboardEnabled = HMD.active; + root.keyboardRaised = Qt.binding( function() { return keyboardRaised; }) + } + d.resize(); } diff --git a/interface/resources/qml/LoginDialog/WelcomeBody.qml b/interface/resources/qml/LoginDialog/WelcomeBody.qml index eb91956532..551ec263b7 100644 --- a/interface/resources/qml/LoginDialog/WelcomeBody.qml +++ b/interface/resources/qml/LoginDialog/WelcomeBody.qml @@ -77,7 +77,7 @@ Item { text: qsTr("Close"); - onClicked: root.destroy() + onClicked: root.tryDestroy() } } diff --git a/interface/resources/qml/TabletLoginDialog/CompleteProfileBody.qml b/interface/resources/qml/TabletLoginDialog/CompleteProfileBody.qml deleted file mode 100644 index 6024563bcf..0000000000 --- a/interface/resources/qml/TabletLoginDialog/CompleteProfileBody.qml +++ /dev/null @@ -1,124 +0,0 @@ -// -// CompleteProfileBody.qml -// -// Created by Clement on 7/18/16 -// Copyright 2015 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 Hifi 1.0 -import QtQuick 2.4 -import QtQuick.Controls.Styles 1.4 as OriginalStyles - -import "../controls-uit" -import "../styles-uit" - -Item { - id: completeProfileBody - clip: true - - QtObject { - id: d - function resize() {} - } - - Row { - id: buttons - anchors { - top: parent.top - horizontalCenter: parent.horizontalCenter - margins: 0 - topMargin: 2 * hifi.dimensions.contentSpacing.y - } - spacing: hifi.dimensions.contentSpacing.x - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - - Button { - anchors.verticalCenter: parent.verticalCenter - width: 200 - - text: qsTr("Create your profile") - color: hifi.buttons.blue - - onClicked: loginDialog.createAccountFromStream() - } - - Button { - anchors.verticalCenter: parent.verticalCenter - - text: qsTr("Cancel") - - onClicked: bodyLoader.popup() - } - } - - ShortcutText { - id: additionalTextContainer - anchors { - top: buttons.bottom - horizontalCenter: parent.horizontalCenter - margins: 0 - topMargin: hifi.dimensions.contentSpacing.y - } - - text: "Already have a High Fidelity profile? Link to an existing profile here." - - wrapMode: Text.WordWrap - lineHeight: 2 - lineHeightMode: Text.ProportionalHeight - horizontalAlignment: Text.AlignHCenter - - onLinkActivated: { - bodyLoader.setSource("LinkAccountBody.qml") - } - } - - InfoItem { - id: termsContainer - anchors { - top: additionalTextContainer.bottom - left: parent.left - margins: 0 - topMargin: 2 * hifi.dimensions.contentSpacing.y - } - - text: qsTr("By creating this user profile, you agree to High Fidelity's Terms of Service") - wrapMode: Text.WordWrap - color: hifi.colors.baseGrayHighlight - lineHeight: 1 - lineHeightMode: Text.ProportionalHeight - horizontalAlignment: Text.AlignHCenter - - onLinkActivated: loginDialog.openUrl(link) - } - - Component.onCompleted: { - loginDialogRoot.title = qsTr("Complete Your Profile") - loginDialogRoot.iconText = "<" - d.resize(); - } - - Connections { - target: loginDialog - onHandleCreateCompleted: { - console.log("Create Succeeded") - - loginDialog.loginThroughSteam() - } - onHandleCreateFailed: { - console.log("Create Failed: " + error) - - bodyLoadersetSource("UsernameCollisionBody.qml") - } - onHandleLoginCompleted: { - console.log("Login Succeeded") - - bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : false }) - } - onHandleLoginFailed: { - console.log("Login Failed") - } - } -} diff --git a/interface/resources/qml/TabletLoginDialog/LinkAccountBody.qml b/interface/resources/qml/TabletLoginDialog/LinkAccountBody.qml deleted file mode 100644 index 8010a34250..0000000000 --- a/interface/resources/qml/TabletLoginDialog/LinkAccountBody.qml +++ /dev/null @@ -1,296 +0,0 @@ -// -// LinkAccountBody.qml -// -// Created by Clement on 7/18/16 -// Copyright 2015 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 Hifi 1.0 -import QtQuick 2.4 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 as OriginalStyles - -import "../controls-uit" -import "../styles-uit" - -Item { - id: linkAccountBody - clip: true - height: parent.height - width: parent.width - property bool failAfterSignUp: false - - function login() { - mainTextContainer.visible = false - toggleLoading(true) - loginDialog.login(usernameField.text, passwordField.text) - } - - property bool keyboardEnabled: false - property bool keyboardRaised: false - property bool punctuationMode: false - - onKeyboardRaisedChanged: d.resize(); - - QtObject { - id: d - function resize() {} - } - - function toggleLoading(isLoading) { - linkAccountSpinner.visible = isLoading - form.visible = !isLoading - - if (loginDialog.isSteamRunning()) { - additionalInformation.visible = !isLoading - } - - leftButton.visible = !isLoading - buttons.visible = !isLoading - } - - BusyIndicator { - id: linkAccountSpinner - - anchors { - top: parent.top - horizontalCenter: parent.horizontalCenter - topMargin: hifi.dimensions.contentSpacing.y - } - - visible: false - running: true - - width: 48 - height: 48 - } - - ShortcutText { - id: mainTextContainer - anchors { - top: parent.top - left: parent.left - margins: 0 - topMargin: hifi.dimensions.contentSpacing.y - } - - visible: false - - text: qsTr("Username or password incorrect.") - wrapMode: Text.WordWrap - color: hifi.colors.redAccent - lineHeight: 1 - lineHeightMode: Text.ProportionalHeight - horizontalAlignment: Text.AlignHCenter - } - - Column { - id: form - anchors { - top: mainTextContainer.bottom - left: parent.left - margins: 0 - topMargin: 2 * hifi.dimensions.contentSpacing.y - } - spacing: 2 * hifi.dimensions.contentSpacing.y - - Row { - spacing: hifi.dimensions.contentSpacing.x - - TextField { - id: usernameField - anchors { - verticalCenter: parent.verticalCenter - } - width: 350 - - label: "Username or Email" - } - - ShortcutText { - anchors { - verticalCenter: parent.verticalCenter - } - - text: "Forgot Username?" - - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - linkColor: hifi.colors.blueAccent - - onLinkActivated: loginDialog.openUrl(link) - } - } - Row { - spacing: hifi.dimensions.contentSpacing.x - - TextField { - id: passwordField - anchors { - verticalCenter: parent.verticalCenter - } - width: 350 - - label: "Password" - echoMode: TextInput.Password - } - - ShortcutText { - anchors { - verticalCenter: parent.verticalCenter - } - - text: "Forgot Password?" - - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - linkColor: hifi.colors.blueAccent - - onLinkActivated: loginDialog.openUrl(link) - } - } - - } - - InfoItem { - id: additionalInformation - anchors { - top: form.bottom - left: parent.left - margins: 0 - topMargin: hifi.dimensions.contentSpacing.y - } - - visible: loginDialog.isSteamRunning() - - text: qsTr("Your steam account informations will not be exposed to other users.") - wrapMode: Text.WordWrap - color: hifi.colors.baseGrayHighlight - lineHeight: 1 - lineHeightMode: Text.ProportionalHeight - horizontalAlignment: Text.AlignHCenter - } - - // Override ScrollingWindow's keyboard that would be at very bottom of dialog. - Keyboard { - raised: keyboardEnabled && keyboardRaised - numeric: punctuationMode - anchors { - left: parent.left - right: parent.right - bottom: buttons.top - bottomMargin: keyboardRaised ? 2 * hifi.dimensions.contentSpacing.y : 0 - } - } - - Row { - id: leftButton - anchors { - left: parent.left - bottom: parent.bottom - bottomMargin: hifi.dimensions.contentSpacing.y - } - - spacing: hifi.dimensions.contentSpacing.x - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - - Button { - anchors.verticalCenter: parent.verticalCenter - - text: qsTr("Sign Up") - visible: !loginDialog.isSteamRunning() - - onClicked: { - bodyLoader.setSource("SignUpBody.qml") - } - } - } - - Row { - id: buttons - anchors { - right: parent.right - bottom: parent.bottom - bottomMargin: hifi.dimensions.contentSpacing.y - } - spacing: hifi.dimensions.contentSpacing.x - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - - Button { - id: linkAccountButton - anchors.verticalCenter: parent.verticalCenter - width: 200 - - text: qsTr(loginDialog.isSteamRunning() ? "Link Account" : "Login") - color: hifi.buttons.blue - - onClicked: linkAccountBody.login() - } - - Button { - anchors.verticalCenter: parent.verticalCenter - text: qsTr("Cancel") - onClicked: { - bodyLoader.popup() - } - } - } - - Component.onCompleted: { - loginDialogRoot.title = qsTr("Sign Into High Fidelity") - loginDialogRoot.iconText = "<" - keyboardEnabled = HMD.active; - d.resize(); - - if (failAfterSignUp) { - mainTextContainer.text = "Account created successfully." - mainTextContainer.visible = true - } - - usernameField.forceActiveFocus(); - } - - Connections { - target: loginDialog - onHandleLoginCompleted: { - console.log("Login Succeeded, linking steam account") - - if (loginDialog.isSteamRunning()) { - loginDialog.linkSteam() - } else { - bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true }) - } - } - onHandleLoginFailed: { - console.log("Login Failed") - mainTextContainer.visible = true - toggleLoading(false) - } - onHandleLinkCompleted: { - console.log("Link Succeeded") - - bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true }) - } - onHandleLinkFailed: { - console.log("Link Failed") - toggleLoading(false) - } - } - - Keys.onPressed: { - if (!visible) { - return - } - - switch (event.key) { - case Qt.Key_Enter: - case Qt.Key_Return: - event.accepted = true - linkAccountBody.login() - break - } - } -} diff --git a/interface/resources/qml/TabletLoginDialog/SignInBody.qml b/interface/resources/qml/TabletLoginDialog/SignInBody.qml deleted file mode 100644 index 9cdf69c7bc..0000000000 --- a/interface/resources/qml/TabletLoginDialog/SignInBody.qml +++ /dev/null @@ -1,109 +0,0 @@ -// -// SignInBody.qml -// -// Created by Clement on 7/18/16 -// Copyright 2015 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 Hifi 1.0 -import QtQuick 2.4 -import QtQuick.Controls.Styles 1.4 as OriginalStyles - -import "../controls-uit" -import "../styles-uit" - -Item { - id: signInBody - clip: true - - property bool required: false - - function login() { - console.log("Trying to log in") - loginDialog.loginThroughSteam() - } - - function cancel() { - bodyLoader.popup() - } - - QtObject { - id: d - function resize() {} - } - - InfoItem { - id: mainTextContainer - anchors { - top: parent.top - horizontalCenter: parent.horizontalCenter - margins: 0 - topMargin: hifi.dimensions.contentSpacing.y - } - - text: required ? qsTr("This domain's owner requires that you sign in:") - : qsTr("Sign in to access your user account:") - wrapMode: Text.WordWrap - color: hifi.colors.baseGrayHighlight - lineHeight: 2 - lineHeightMode: Text.ProportionalHeight - horizontalAlignment: Text.AlignHCenter - } - - Row { - id: buttons - anchors { - top: mainTextContainer.bottom - horizontalCenter: parent.horizontalCenter - margins: 0 - topMargin: 2 * hifi.dimensions.contentSpacing.y - } - spacing: hifi.dimensions.contentSpacing.x - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - - Button { - anchors.verticalCenter: parent.verticalCenter - - width: undefined // invalidate so that the image's size sets the width - height: undefined // invalidate so that the image's size sets the height - focus: true - - style: OriginalStyles.ButtonStyle { - background: Image { - id: buttonImage - source: "../../images/steam-sign-in.png" - } - } - onClicked: signInBody.login() - } - Button { - anchors.verticalCenter: parent.verticalCenter - - text: qsTr("Cancel"); - - onClicked: signInBody.cancel() - } - } - - Component.onCompleted: { - loginDialogRoot.title = required ? qsTr("Sign In Required") - : qsTr("Sign In") - loginDialogRoot.iconText = "" - d.resize(); - } - - Connections { - target: loginDialog - onHandleLoginCompleted: { - console.log("Login Succeeded") - bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true }) - } - onHandleLoginFailed: { - console.log("Login Failed") - bodyLoader.setSource("CompleteProfileBody.qml") - } - } -} diff --git a/interface/resources/qml/TabletLoginDialog/SignUpBody.qml b/interface/resources/qml/TabletLoginDialog/SignUpBody.qml deleted file mode 100644 index 2cfc0e736a..0000000000 --- a/interface/resources/qml/TabletLoginDialog/SignUpBody.qml +++ /dev/null @@ -1,276 +0,0 @@ -// -// SignUpBody.qml -// -// Created by Stephen Birarda on 7 Dec 2016 -// Copyright 2016 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 Hifi 1.0 -import QtQuick 2.4 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 as OriginalStyles - -import "../controls-uit" -import "../styles-uit" - -Item { - id: signupBody - clip: true -// height: parent.height -// width: parent.width - - function signup() { - mainTextContainer.visible = false - toggleLoading(true) - loginDialog.signup(emailField.text, usernameField.text, passwordField.text) - } - - property bool keyboardEnabled: false - property bool keyboardRaised: false - property bool punctuationMode: false - - onKeyboardRaisedChanged: d.resize(); - - QtObject { - id: d - function resize() {} - } - - function toggleLoading(isLoading) { - linkAccountSpinner.visible = isLoading - form.visible = !isLoading - - leftButton.visible = !isLoading - buttons.visible = !isLoading - } - - BusyIndicator { - id: linkAccountSpinner - - anchors { - top: parent.top - horizontalCenter: parent.horizontalCenter - topMargin: hifi.dimensions.contentSpacing.y - } - - visible: false - running: true - - width: 48 - height: 48 - } - - ShortcutText { - id: mainTextContainer - anchors { - top: parent.top - left: parent.left - margins: 0 - topMargin: hifi.dimensions.contentSpacing.y - } - - visible: false - - text: qsTr("There was an unknown error while creating your account.") - wrapMode: Text.WordWrap - color: hifi.colors.redAccent - horizontalAlignment: Text.AlignLeft - } - - Column { - id: form - anchors { - top: mainTextContainer.bottom - left: parent.left - margins: 0 - topMargin: 2 * hifi.dimensions.contentSpacing.y - } - spacing: 2 * hifi.dimensions.contentSpacing.y - - Row { - spacing: hifi.dimensions.contentSpacing.x - - TextField { - id: emailField - anchors { - verticalCenter: parent.verticalCenter - } - width: 300 - - label: "Email" - } - } - - Row { - spacing: hifi.dimensions.contentSpacing.x - - TextField { - id: usernameField - anchors { - verticalCenter: parent.verticalCenter - } - width: 300 - - label: "Username" - } - - ShortcutText { - anchors { - verticalCenter: parent.verticalCenter - } - - text: qsTr("No spaces / special chars.") - - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - - color: hifi.colors.blueAccent - } - } - - Row { - spacing: hifi.dimensions.contentSpacing.x - - TextField { - id: passwordField - anchors { - verticalCenter: parent.verticalCenter - } - width: 300 - - label: "Password" - echoMode: TextInput.Password - } - - ShortcutText { - anchors { - verticalCenter: parent.verticalCenter - } - - text: qsTr("At least 6 characters") - - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - - color: hifi.colors.blueAccent - } - } - - } - - // Override ScrollingWindow's keyboard that would be at very bottom of dialog. - Keyboard { - raised: keyboardEnabled && keyboardRaised - numeric: punctuationMode - anchors { - left: parent.left - right: parent.right - bottom: buttons.top - bottomMargin: keyboardRaised ? 2 * hifi.dimensions.contentSpacing.y : 0 - } - } - - Row { - id: leftButton - anchors { - left: parent.left - bottom: parent.bottom - bottomMargin: hifi.dimensions.contentSpacing.y - } - - spacing: hifi.dimensions.contentSpacing.x - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - - Button { - anchors.verticalCenter: parent.verticalCenter - - text: qsTr("Existing User") - - onClicked: { - bodyLoader.setSource("LinkAccountBody.qml") - } - } - } - - Row { - id: buttons - anchors { - right: parent.right - bottom: parent.bottom - bottomMargin: hifi.dimensions.contentSpacing.y - } - spacing: hifi.dimensions.contentSpacing.x - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - - Button { - id: linkAccountButton - anchors.verticalCenter: parent.verticalCenter - width: 200 - - text: qsTr("Sign Up") - color: hifi.buttons.blue - - onClicked: signupBody.signup() - } - - Button { - anchors.verticalCenter: parent.verticalCenter - - text: qsTr("Cancel") - - onClicked: bodyLoader.popup() - } - } - - Component.onCompleted: { - loginDialogRoot.title = qsTr("Create an Account") - loginDialogRoot.iconText = "<" - keyboardEnabled = HMD.active; - d.resize(); - - emailField.forceActiveFocus(); - } - - Connections { - target: loginDialog - onHandleSignupCompleted: { - console.log("Sign Up Succeeded"); - - // now that we have an account, login with that username and password - loginDialog.login(usernameField.text, passwordField.text) - } - onHandleSignupFailed: { - console.log("Sign Up Failed") - toggleLoading(false) - - mainTextContainer.text = errorString - mainTextContainer.visible = true - - d.resize(); - } - onHandleLoginCompleted: { - bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack": false }) - } - onHandleLoginFailed: { - // we failed to login, show the LoginDialog so the user will try again - bodyLoader.setSource("LinkAccountBody.qml", { "failAfterSignUp": true }) - } - } - - Keys.onPressed: { - if (!visible) { - return - } - - switch (event.key) { - case Qt.Key_Enter: - case Qt.Key_Return: - event.accepted = true - signupBody.signup() - break - } - } -} diff --git a/interface/resources/qml/TabletLoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/TabletLoginDialog/UsernameCollisionBody.qml deleted file mode 100644 index 9e5b01d339..0000000000 --- a/interface/resources/qml/TabletLoginDialog/UsernameCollisionBody.qml +++ /dev/null @@ -1,157 +0,0 @@ -// -// UsernameCollisionBody.qml -// -// Created by Clement on 7/18/16 -// Copyright 2015 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 Hifi 1.0 -import QtQuick 2.4 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 as OriginalStyles - -import "../controls-uit" -import "../styles-uit" - -Item { - id: usernameCollisionBody - clip: true - width: parent.width - height: parent.height - - function create() { - mainTextContainer.visible = false - loginDialog.createAccountFromStream(textField.text) - } - - - property bool keyboardEnabled: false - property bool keyboardRaised: false - property bool punctuationMode: false - - onKeyboardRaisedChanged: d.resize(); - - QtObject { - id: d - function resize() {} - } - - ShortcutText { - id: mainTextContainer - anchors { - top: parent.top - left: parent.left - margins: 0 - topMargin: hifi.dimensions.contentSpacing.y - } - - text: qsTr("Your Steam username is not available.") - wrapMode: Text.WordWrap - color: hifi.colors.redAccent - lineHeight: 1 - lineHeightMode: Text.ProportionalHeight - horizontalAlignment: Text.AlignHCenter - } - - - TextField { - id: textField - anchors { - top: mainTextContainer.bottom - left: parent.left - margins: 0 - topMargin: hifi.dimensions.contentSpacing.y - } - width: 250 - - placeholderText: "Choose your own" - } - - // Override ScrollingWindow's keyboard that would be at very bottom of dialog. - Keyboard { - raised: keyboardEnabled && keyboardRaised - numeric: punctuationMode - anchors { - left: parent.left - right: parent.right - bottom: buttons.top - bottomMargin: keyboardRaised ? 2 * hifi.dimensions.contentSpacing.y : 0 - } - } - - Row { - id: buttons - anchors { - bottom: parent.bottom - right: parent.right - margins: 0 - topMargin: hifi.dimensions.contentSpacing.y - } - spacing: hifi.dimensions.contentSpacing.x - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - - Button { - anchors.verticalCenter: parent.verticalCenter - width: 200 - - text: qsTr("Create your profile") - color: hifi.buttons.blue - - onClicked: usernameCollisionBody.create() - } - - Button { - anchors.verticalCenter: parent.verticalCenter - - text: qsTr("Cancel") - onClicked: bodyLoader.popup() - } - } - - Component.onCompleted: { - loginDialogRoot.title = qsTr("Complete Your Profile") - loginDialogRoot.iconText = "<" - keyboardEnabled = HMD.active; - d.resize(); - } - - Connections { - target: loginDialog - onHandleCreateCompleted: { - console.log("Create Succeeded") - - loginDialog.loginThroughSteam() - } - onHandleCreateFailed: { - console.log("Create Failed: " + error) - - mainTextContainer.visible = true - mainTextContainer.text = "\"" + textField.text + qsTr("\" is invalid or already taken.") - } - onHandleLoginCompleted: { - console.log("Login Succeeded") - - bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : false }) - } - onHandleLoginFailed: { - console.log("Login Failed") - } - } - - Keys.onPressed: { - if (!visible) { - return - } - - switch (event.key) { - case Qt.Key_Enter: - case Qt.Key_Return: - event.accepted = true - usernameCollisionBody.create() - break - } - } -} diff --git a/interface/resources/qml/TabletLoginDialog/WelcomeBody.qml b/interface/resources/qml/TabletLoginDialog/WelcomeBody.qml deleted file mode 100644 index 5ec259ca96..0000000000 --- a/interface/resources/qml/TabletLoginDialog/WelcomeBody.qml +++ /dev/null @@ -1,79 +0,0 @@ -// -// WelcomeBody.qml -// -// Created by Clement on 7/18/16 -// Copyright 2015 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 Hifi 1.0 -import QtQuick 2.4 - -import "../controls-uit" -import "../styles-uit" - -Item { - id: welcomeBody - clip: true - - property bool welcomeBack: false - - function setTitle() { - loginDialogRoot.title = (welcomeBack ? qsTr("Welcome back ") : qsTr("Welcome ")) + Account.username + qsTr("!") - loginDialogRoot.iconText = "" - d.resize(); - } - - QtObject { - id: d - function resize() {} - } - - InfoItem { - id: mainTextContainer - anchors { - top: parent.top - horizontalCenter: parent.horizontalCenter - margins: 0 - topMargin: hifi.dimensions.contentSpacing.y - } - - text: qsTr("You are now signed into High Fidelity") - wrapMode: Text.WordWrap - color: hifi.colors.baseGrayHighlight - lineHeight: 2 - lineHeightMode: Text.ProportionalHeight - horizontalAlignment: Text.AlignHCenter - } - - Row { - id: buttons - anchors { - top: mainTextContainer.bottom - horizontalCenter: parent.horizontalCenter - margins: 0 - topMargin: 2 * hifi.dimensions.contentSpacing.y - } - spacing: hifi.dimensions.contentSpacing.x - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - - Button { - anchors.verticalCenter: parent.verticalCenter - - text: qsTr("Close"); - - onClicked: bodyLoader.popup() - } - } - - Component.onCompleted: { - welcomeBody.setTitle() - } - - Connections { - target: Account - onUsernameChanged: welcomeBody.setTitle() - } -} diff --git a/interface/resources/qml/controls-uit/TextField.qml b/interface/resources/qml/controls-uit/TextField.qml index a8b2ed45a5..1cc4d0ecc1 100644 --- a/interface/resources/qml/controls-uit/TextField.qml +++ b/interface/resources/qml/controls-uit/TextField.qml @@ -35,6 +35,7 @@ TextField { font.pixelSize: hifi.fontSizes.textFieldInput font.italic: textField.text == "" height: implicitHeight + 3 // Make surrounding box higher so that highlight is vertically centered. + property alias textFieldLabel: textFieldLabel y: textFieldLabel.visible ? textFieldLabel.height + textFieldLabel.anchors.bottomMargin : 0 diff --git a/interface/resources/qml/dialogs/TabletLoginDialog.qml b/interface/resources/qml/dialogs/TabletLoginDialog.qml index 36ca480b24..9722f31144 100644 --- a/interface/resources/qml/dialogs/TabletLoginDialog.qml +++ b/interface/resources/qml/dialogs/TabletLoginDialog.qml @@ -16,48 +16,66 @@ import "../controls-uit" import "../styles-uit" import "../windows" +import "../LoginDialog" + TabletModalWindow { - id: loginDialogRoot + id: realRoot objectName: "LoginDialog" signal sendToScript(var message); property bool isHMD: false property bool gotoPreviousApp: false; color: hifi.colors.baseGray + title: qsTr("Sign in to High Fidelity") + property alias titleWidth: root.titleWidth + property alias punctuationMode: root.punctuationMode + + //fake root for shared components expecting root here + property var root: QtObject { + id: root + + property bool keyboardEnabled: false + property bool keyboardRaised: false + property bool punctuationMode: false + + readonly property bool isTablet: true + + property alias title: realRoot.title + property real width: realRoot.width + property real height: realRoot.height + + property int titleWidth: 0 + property string iconText: hifi.glyphs.avatar + property int iconSize: 35 + + property var pane: QtObject { + property real width: root.width + property real height: root.height + } + + function tryDestroy() { + canceled() + } + } + + //property int colorScheme: hifi.colorSchemes.dark - property int colorScheme: hifi.colorSchemes.dark - property int titleWidth: 0 - property string iconText: "" - property int icon: hifi.icons.none - property int iconSize: 35 MouseArea { - width: parent.width - height: parent.height + width: realRoot.width + height: realRoot.height } property bool keyboardOverride: true - onIconChanged: updateIcon(); property var items; property string label: "" - onTitleWidthChanged: d.resize(); + //onTitleWidthChanged: d.resize(); - property bool keyboardEnabled: false - property bool keyboardRaised: false - property bool punctuationMode: false - - onKeyboardRaisedChanged: d.resize(); + //onKeyboardRaisedChanged: d.resize(); signal canceled(); - function updateIcon() { - if (!root) { - return; - } - iconText = hifi.glyphForIcon(root.icon); - } - property alias bodyLoader: bodyLoader property alias loginDialog: loginDialog property alias hifi: hifi @@ -65,9 +83,10 @@ TabletModalWindow { HifiConstants { id: hifi } onCanceled: { - if (loginDialogRoot.Stack.view) { - loginDialogRoot.Stack.view.pop(); - } else if (gotoPreviousApp) { + if (bodyLoader.active === true) { + //bodyLoader.active = false + } + if (gotoPreviousApp) { var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); tablet.returnToPreviousApp(); } else { @@ -75,45 +94,81 @@ TabletModalWindow { } } - LoginDialog { - id: loginDialog - width: parent.width - height: parent.height - StackView { - id: bodyLoader - property var item: currentItem - property var props - property string source: "" + TabletModalFrame { + id: mfRoot - onCurrentItemChanged: { - //cleanup source for future usage - source = "" + width: root.width + height: root.height + frameMarginTop + hifi.dimensions.contentMargin.x + + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: parent.verticalCenter + verticalCenterOffset: -loginKeyboard.height / 2 + } + + LoginDialog { + id: loginDialog + + anchors { + fill: parent + topMargin: parent.frameMarginTop + leftMargin: hifi.dimensions.contentMargin.x + rightMargin: hifi.dimensions.contentMargin.x + horizontalCenter: parent.horizontalCenter } - function setSource(src, props) { - source = "../TabletLoginDialog/" + src - bodyLoader.props = props - } - function popup() { - bodyLoader.pop() - - //check if last screen, if yes, dialog is popped out - if (depth === 1) - loginDialogRoot.canceled() - } - - anchors.fill: parent - anchors.margins: 10 - onSourceChanged: { - if (source !== "") { - bodyLoader.push(Qt.resolvedUrl(source), props) - } - } - Component.onCompleted: { - setSource(loginDialog.isSteamRunning() ? - "SignInBody.qml" : - "LinkAccountBody.qml") + Loader { + id: bodyLoader + anchors.fill: parent + anchors.horizontalCenter: parent.horizontalCenter + source: loginDialog.isSteamRunning() ? "../LoginDialog/SignInBody.qml" : "../LoginDialog/LinkAccountBody.qml" } } } + + Keyboard { + id: loginKeyboard + raised: root.keyboardEnabled && root.keyboardRaised + numeric: root.punctuationMode + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + } + + Keys.onPressed: { + if (!visible) { + return + } + + if (event.modifiers === Qt.ControlModifier) + switch (event.key) { + case Qt.Key_A: + event.accepted = true + detailedText.selectAll() + break + case Qt.Key_C: + event.accepted = true + detailedText.copy() + break + case Qt.Key_Period: + if (Qt.platform.os === "osx") { + event.accepted = true + content.reject() + } + break + } else switch (event.key) { + case Qt.Key_Escape: + case Qt.Key_Back: + event.accepted = true + destroy() + break + + case Qt.Key_Enter: + case Qt.Key_Return: + event.accepted = true + break + } + } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 669f942f30..b7cf6fb236 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -189,7 +189,6 @@ #include "InterfaceParentFinder.h" #include "ui/OctreeStatsProvider.h" -#include "FrameTimingsScriptingInterface.h" #include #include #include @@ -2023,7 +2022,8 @@ void Application::cleanupBeforeQuit() { // The cleanup process enqueues the transactions but does not process them. Calling this here will force the actual // removal of the items. // See https://highfidelity.fogbugz.com/f/cases/5328 - _main3DScene->processTransactionQueue(); + _main3DScene->enqueueFrame(); // flush all the transactions + _main3DScene->processTransactionQueue(); // process and apply deletions // first stop all timers directly or by invokeMethod // depending on what thread they run in @@ -2221,8 +2221,6 @@ void Application::initializeGL() { update(0); } -FrameTimingsScriptingInterface _frameTimingsScriptingInterface; - extern void setupPreferences(); void Application::initializeUi() { @@ -2383,301 +2381,105 @@ void Application::initializeUi() { offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2); } -void Application::paintGL() { - // Some plugins process message events, allowing paintGL to be called reentrantly. - if (_aboutToQuit || _window->isMinimized()) { - return; - } - - _renderFrameCount++; - _lastTimeRendered.start(); - - auto lastPaintBegin = usecTimestampNow(); - PROFILE_RANGE_EX(render, __FUNCTION__, 0xff0000ff, (uint64_t)_renderFrameCount); - PerformanceTimer perfTimer("paintGL"); - - if (nullptr == _displayPlugin) { - return; - } - - DisplayPluginPointer displayPlugin; - { - PROFILE_RANGE(render, "/getActiveDisplayPlugin"); - displayPlugin = getActiveDisplayPlugin(); - } - - { - PROFILE_RANGE(render, "/pluginBeginFrameRender"); - // If a display plugin loses it's underlying support, it - // needs to be able to signal us to not use it - if (!displayPlugin->beginFrameRender(_renderFrameCount)) { - updateDisplayMode(); - return; - } - } - - // update the avatar with a fresh HMD pose - { - PROFILE_RANGE(render, "/updateAvatar"); - getMyAvatar()->updateFromHMDSensorMatrix(getHMDSensorPose()); - } - - auto lodManager = DependencyManager::get(); - - RenderArgs renderArgs; - - float sensorToWorldScale = getMyAvatar()->getSensorToWorldScale(); - { - PROFILE_RANGE(render, "/buildFrustrumAndArgs"); - { - QMutexLocker viewLocker(&_viewMutex); - // adjust near clip plane to account for sensor scaling. - auto adjustedProjection = glm::perspective(_viewFrustum.getFieldOfView(), - _viewFrustum.getAspectRatio(), - DEFAULT_NEAR_CLIP * sensorToWorldScale, - _viewFrustum.getFarClip()); - _viewFrustum.setProjection(adjustedProjection); - _viewFrustum.calculate(); - } - renderArgs = RenderArgs(_gpuContext, lodManager->getOctreeSizeScale(), - lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE, - RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); - { - QMutexLocker viewLocker(&_viewMutex); - renderArgs.setViewFrustum(_viewFrustum); - } - } - - { - PROFILE_RANGE(render, "/resizeGL"); - PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings)); - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::paintGL()"); - resizeGL(); - } - - { - PROFILE_RANGE(render, "/gpuContextReset"); - _gpuContext->beginFrame(getHMDSensorPose()); - // Reset the gpu::Context Stages - // Back to the default framebuffer; - gpu::doInBatch(_gpuContext, [&](gpu::Batch& batch) { - batch.resetStages(); - }); - } - - - { - PROFILE_RANGE(render, "/renderOverlay"); - PerformanceTimer perfTimer("renderOverlay"); - // NOTE: There is no batch associated with this renderArgs - // the ApplicationOverlay class assumes it's viewport is setup to be the device size - QSize size = getDeviceSize(); - renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height()); - _applicationOverlay.renderOverlay(&renderArgs); - } +void Application::updateCamera(RenderArgs& renderArgs) { + PROFILE_RANGE(render, "/updateCamera"); + PerformanceTimer perfTimer("CameraUpdates"); glm::vec3 boomOffset; - { - PROFILE_RANGE(render, "/updateCamera"); - { - PerformanceTimer perfTimer("CameraUpdates"); + auto myAvatar = getMyAvatar(); + boomOffset = myAvatar->getModelScale() * myAvatar->getBoomLength() * -IDENTITY_FORWARD; - auto myAvatar = getMyAvatar(); - boomOffset = myAvatar->getModelScale() * myAvatar->getBoomLength() * -IDENTITY_FORWARD; + // The render mode is default or mirror if the camera is in mirror mode, assigned further below + renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; - // The render mode is default or mirror if the camera is in mirror mode, assigned further below - renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; - - // Always use the default eye position, not the actual head eye position. - // Using the latter will cause the camera to wobble with idle animations, - // or with changes from the face tracker - if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { - if (isHMDMode()) { - mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); - _myCamera.setPosition(extractTranslation(camMat)); - _myCamera.setOrientation(glmExtractRotation(camMat)); - } else { - _myCamera.setPosition(myAvatar->getDefaultEyePosition()); - _myCamera.setOrientation(myAvatar->getMyHead()->getHeadOrientation()); - } - } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { - if (isHMDMode()) { - auto hmdWorldMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); - _myCamera.setOrientation(glm::normalize(glmExtractRotation(hmdWorldMat))); - _myCamera.setPosition(extractTranslation(hmdWorldMat) + - myAvatar->getOrientation() * boomOffset); - } else { - _myCamera.setOrientation(myAvatar->getHead()->getOrientation()); - if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { - _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + _myCamera.getOrientation() * boomOffset); - } else { - _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + myAvatar->getOrientation() * boomOffset); - } - } - } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { - if (isHMDMode()) { - auto mirrorBodyOrientation = myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)); - - glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); - // Mirror HMD yaw and roll - glm::vec3 mirrorHmdEulers = glm::eulerAngles(hmdRotation); - mirrorHmdEulers.y = -mirrorHmdEulers.y; - mirrorHmdEulers.z = -mirrorHmdEulers.z; - glm::quat mirrorHmdRotation = glm::quat(mirrorHmdEulers); - - glm::quat worldMirrorRotation = mirrorBodyOrientation * mirrorHmdRotation; - - _myCamera.setOrientation(worldMirrorRotation); - - glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); - // Mirror HMD lateral offsets - hmdOffset.x = -hmdOffset.x; - - _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + glm::vec3(0, _raiseMirror * myAvatar->getModelScale(), 0) - + mirrorBodyOrientation * glm::vec3(0.0f, 0.0f, 1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror - + mirrorBodyOrientation * hmdOffset); - } else { - _myCamera.setOrientation(myAvatar->getOrientation() - * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); - _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + glm::vec3(0, _raiseMirror * myAvatar->getModelScale(), 0) - + (myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * - glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); - } - renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; - } else if (_myCamera.getMode() == CAMERA_MODE_ENTITY) { - EntityItemPointer cameraEntity = _myCamera.getCameraEntityPointer(); - if (cameraEntity != nullptr) { - if (isHMDMode()) { - glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); - _myCamera.setOrientation(cameraEntity->getRotation() * hmdRotation); - glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); - _myCamera.setPosition(cameraEntity->getPosition() + (hmdRotation * hmdOffset)); - } else { - _myCamera.setOrientation(cameraEntity->getRotation()); - _myCamera.setPosition(cameraEntity->getPosition()); - } - } + // Always use the default eye position, not the actual head eye position. + // Using the latter will cause the camera to wobble with idle animations, + // or with changes from the face tracker + if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { + if (isHMDMode()) { + mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); + _myCamera.setPosition(extractTranslation(camMat)); + _myCamera.setOrientation(glmExtractRotation(camMat)); + } + else { + _myCamera.setPosition(myAvatar->getDefaultEyePosition()); + _myCamera.setOrientation(myAvatar->getMyHead()->getHeadOrientation()); + } + } + else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { + if (isHMDMode()) { + auto hmdWorldMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); + _myCamera.setOrientation(glm::normalize(glmExtractRotation(hmdWorldMat))); + _myCamera.setPosition(extractTranslation(hmdWorldMat) + + myAvatar->getOrientation() * boomOffset); + } + else { + _myCamera.setOrientation(myAvatar->getHead()->getOrientation()); + if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + + _myCamera.getOrientation() * boomOffset); } - // Update camera position - if (!isHMDMode()) { - _myCamera.update(); + else { + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + + myAvatar->getOrientation() * boomOffset); } } } + else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { + if (isHMDMode()) { + auto mirrorBodyOrientation = myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)); - { - PROFILE_RANGE(render, "/updateCompositor"); - getApplicationCompositor().setFrameInfo(_renderFrameCount, _myCamera.getTransform(), getMyAvatar()->getSensorToWorldMatrix()); - } + glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); + // Mirror HMD yaw and roll + glm::vec3 mirrorHmdEulers = glm::eulerAngles(hmdRotation); + mirrorHmdEulers.y = -mirrorHmdEulers.y; + mirrorHmdEulers.z = -mirrorHmdEulers.z; + glm::quat mirrorHmdRotation = glm::quat(mirrorHmdEulers); - gpu::FramebufferPointer finalFramebuffer; - QSize finalFramebufferSize; - { - PROFILE_RANGE(render, "/getOutputFramebuffer"); - // Primary rendering pass - auto framebufferCache = DependencyManager::get(); - finalFramebufferSize = framebufferCache->getFrameBufferSize(); - // Final framebuffer that will be handled to the display-plugin - finalFramebuffer = framebufferCache->getFramebuffer(); - } + glm::quat worldMirrorRotation = mirrorBodyOrientation * mirrorHmdRotation; - auto hmdInterface = DependencyManager::get(); - float ipdScale = hmdInterface->getIPDScale(); + _myCamera.setOrientation(worldMirrorRotation); - // scale IPD by sensorToWorldScale, to make the world seem larger or smaller accordingly. - ipdScale *= sensorToWorldScale; + glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); + // Mirror HMD lateral offsets + hmdOffset.x = -hmdOffset.x; - { - PROFILE_RANGE(render, "/mainRender"); - PerformanceTimer perfTimer("mainRender"); - renderArgs._boomOffset = boomOffset; - // FIXME is this ever going to be different from the size previously set in the render args - // in the overlay render? - // Viewport is assigned to the size of the framebuffer - renderArgs._viewport = ivec4(0, 0, finalFramebufferSize.width(), finalFramebufferSize.height()); - auto baseProjection = renderArgs.getViewFrustum().getProjection(); - if (displayPlugin->isStereo()) { - // Stereo modes will typically have a larger projection matrix overall, - // so we ask for the 'mono' projection matrix, which for stereo and HMD - // plugins will imply the combined projection for both eyes. - // - // This is properly implemented for the Oculus plugins, but for OpenVR - // and Stereo displays I'm not sure how to get / calculate it, so we're - // just relying on the left FOV in each case and hoping that the - // overall culling margin of error doesn't cause popping in the - // right eye. There are FIXMEs in the relevant plugins - _myCamera.setProjection(displayPlugin->getCullingProjection(baseProjection)); - renderArgs._context->enableStereo(true); - mat4 eyeOffsets[2]; - mat4 eyeProjections[2]; - - // FIXME we probably don't need to set the projection matrix every frame, - // only when the display plugin changes (or in non-HMD modes when the user - // changes the FOV manually, which right now I don't think they can. - for_each_eye([&](Eye eye) { - // For providing the stereo eye views, the HMD head pose has already been - // applied to the avatar, so we need to get the difference between the head - // pose applied to the avatar and the per eye pose, and use THAT as - // the per-eye stereo matrix adjustment. - mat4 eyeToHead = displayPlugin->getEyeToHeadTransform(eye); - // Grab the translation - vec3 eyeOffset = glm::vec3(eyeToHead[3]); - // Apply IPD scaling - mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * ipdScale); - eyeOffsets[eye] = eyeOffsetTransform; - eyeProjections[eye] = displayPlugin->getEyeProjection(eye, baseProjection); - }); - renderArgs._context->setStereoProjections(eyeProjections); - renderArgs._context->setStereoViews(eyeOffsets); - - // Configure the type of display / stereo - renderArgs._displayMode = (isHMDMode() ? RenderArgs::STEREO_HMD : RenderArgs::STEREO_MONITOR); + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + + glm::vec3(0, _raiseMirror * myAvatar->getModelScale(), 0) + + mirrorBodyOrientation * glm::vec3(0.0f, 0.0f, 1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror + + mirrorBodyOrientation * hmdOffset); } - renderArgs._blitFramebuffer = finalFramebuffer; - displaySide(&renderArgs, _myCamera); + else { + _myCamera.setOrientation(myAvatar->getOrientation() + * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); + _myCamera.setPosition(myAvatar->getDefaultEyePosition() + + glm::vec3(0, _raiseMirror * myAvatar->getModelScale(), 0) + + (myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) * + glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); + } + renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; + } + else if (_myCamera.getMode() == CAMERA_MODE_ENTITY) { + EntityItemPointer cameraEntity = _myCamera.getCameraEntityPointer(); + if (cameraEntity != nullptr) { + if (isHMDMode()) { + glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); + _myCamera.setOrientation(cameraEntity->getRotation() * hmdRotation); + glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); + _myCamera.setPosition(cameraEntity->getPosition() + (hmdRotation * hmdOffset)); + } + else { + _myCamera.setOrientation(cameraEntity->getRotation()); + _myCamera.setPosition(cameraEntity->getPosition()); + } + } + } + // Update camera position + if (!isHMDMode()) { + _myCamera.update(); } - gpu::Batch postCompositeBatch; - { - PROFILE_RANGE(render, "/postComposite"); - PerformanceTimer perfTimer("postComposite"); - renderArgs._batch = &postCompositeBatch; - renderArgs._batch->setViewportTransform(ivec4(0, 0, finalFramebufferSize.width(), finalFramebufferSize.height())); - renderArgs._batch->setViewTransform(renderArgs.getViewFrustum().getView()); - _overlays.render3DHUDOverlays(&renderArgs); - } - - auto frame = _gpuContext->endFrame(); - frame->frameIndex = _renderFrameCount; - frame->framebuffer = finalFramebuffer; - frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer){ - DependencyManager::get()->releaseFramebuffer(framebuffer); - }; - frame->overlay = _applicationOverlay.getOverlayTexture(); - frame->postCompositeBatch = postCompositeBatch; - // deliver final scene rendering commands to the display plugin - { - PROFILE_RANGE(render, "/pluginOutput"); - PerformanceTimer perfTimer("pluginOutput"); - _renderLoopCounter.increment(); - displayPlugin->submitFrame(frame); - } - - // Reset the framebuffer and stereo state - renderArgs._blitFramebuffer.reset(); - renderArgs._context->enableStereo(false); - - { - Stats::getInstance()->setRenderDetails(renderArgs._details); - } - - uint64_t lastPaintDuration = usecTimestampNow() - lastPaintBegin; - _frameTimingsScriptingInterface.addValue(lastPaintDuration); + renderArgs._cameraMode = (int8_t)_myCamera.getMode(); } void Application::runTests() { @@ -5225,6 +5027,7 @@ void Application::update(float deltaTime) { avatarManager->postUpdate(deltaTime, getMain3DScene()); + { PROFILE_RANGE_EX(app, "PreRenderLambdas", 0xffff0000, (uint64_t)0); @@ -5235,9 +5038,123 @@ void Application::update(float deltaTime) { _postUpdateLambdas.clear(); } + editRenderArgs([this](AppRenderArgs& appRenderArgs) { + appRenderArgs._headPose= getHMDSensorPose(); + + auto myAvatar = getMyAvatar(); + + // update the avatar with a fresh HMD pose + { + PROFILE_RANGE(render, "/updateAvatar"); + myAvatar->updateFromHMDSensorMatrix(appRenderArgs._headPose); + } + + auto lodManager = DependencyManager::get(); + + float sensorToWorldScale = getMyAvatar()->getSensorToWorldScale(); + appRenderArgs._sensorToWorldScale = sensorToWorldScale; + { + PROFILE_RANGE(render, "/buildFrustrumAndArgs"); + { + QMutexLocker viewLocker(&_viewMutex); + // adjust near clip plane to account for sensor scaling. + auto adjustedProjection = glm::perspective(_viewFrustum.getFieldOfView(), + _viewFrustum.getAspectRatio(), + DEFAULT_NEAR_CLIP * sensorToWorldScale, + _viewFrustum.getFarClip()); + _viewFrustum.setProjection(adjustedProjection); + _viewFrustum.calculate(); + } + appRenderArgs._renderArgs = RenderArgs(_gpuContext, lodManager->getOctreeSizeScale(), + lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE, + RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); + appRenderArgs._renderArgs._scene = getMain3DScene(); + + { + QMutexLocker viewLocker(&_viewMutex); + appRenderArgs._renderArgs.setViewFrustum(_viewFrustum); + } + } + { + PROFILE_RANGE(render, "/resizeGL"); + PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings)); + bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(showWarnings, "Application::paintGL()"); + resizeGL(); + } + + this->updateCamera(appRenderArgs._renderArgs); + appRenderArgs._isStereo = false; + + { + auto hmdInterface = DependencyManager::get(); + float ipdScale = hmdInterface->getIPDScale(); + + // scale IPD by sensorToWorldScale, to make the world seem larger or smaller accordingly. + ipdScale *= sensorToWorldScale; + + auto baseProjection = appRenderArgs._renderArgs.getViewFrustum().getProjection(); + if (getActiveDisplayPlugin()->isStereo()) { + // Stereo modes will typically have a larger projection matrix overall, + // so we ask for the 'mono' projection matrix, which for stereo and HMD + // plugins will imply the combined projection for both eyes. + // + // This is properly implemented for the Oculus plugins, but for OpenVR + // and Stereo displays I'm not sure how to get / calculate it, so we're + // just relying on the left FOV in each case and hoping that the + // overall culling margin of error doesn't cause popping in the + // right eye. There are FIXMEs in the relevant plugins + _myCamera.setProjection(getActiveDisplayPlugin()->getCullingProjection(baseProjection)); + appRenderArgs._isStereo = true; + + auto& eyeOffsets = appRenderArgs._eyeOffsets; + auto& eyeProjections = appRenderArgs._eyeProjections; + + // FIXME we probably don't need to set the projection matrix every frame, + // only when the display plugin changes (or in non-HMD modes when the user + // changes the FOV manually, which right now I don't think they can. + for_each_eye([&](Eye eye) { + // For providing the stereo eye views, the HMD head pose has already been + // applied to the avatar, so we need to get the difference between the head + // pose applied to the avatar and the per eye pose, and use THAT as + // the per-eye stereo matrix adjustment. + mat4 eyeToHead = getActiveDisplayPlugin()->getEyeToHeadTransform(eye); + // Grab the translation + vec3 eyeOffset = glm::vec3(eyeToHead[3]); + // Apply IPD scaling + mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * ipdScale); + eyeOffsets[eye] = eyeOffsetTransform; + eyeProjections[eye] = getActiveDisplayPlugin()->getEyeProjection(eye, baseProjection); + }); + + // Configure the type of display / stereo + appRenderArgs._renderArgs._displayMode = (isHMDMode() ? RenderArgs::STEREO_HMD : RenderArgs::STEREO_MONITOR); + } + } + + // HACK + // load the view frustum + // FIXME: This preDisplayRender call is temporary until we create a separate render::scene for the mirror rendering. + // Then we can move this logic into the Avatar::simulate call. + myAvatar->preDisplaySide(&appRenderArgs._renderArgs); + + { + QMutexLocker viewLocker(&_viewMutex); + _myCamera.loadViewFrustum(_displayViewFrustum); + } + + { + QMutexLocker viewLocker(&_viewMutex); + appRenderArgs._renderArgs.setViewFrustum(_displayViewFrustum); + } + }); + AnimDebugDraw::getInstance().update(); DependencyManager::get()->update(); + + // Game loop is done, mark the end of the frame for the scene transactions and the render loop to take over + getMain3DScene()->enqueueFrame(); } void Application::sendAvatarViewFrustum() { @@ -5296,7 +5213,6 @@ int Application::sendNackPackets() { } }); - return packetsSent; } @@ -5527,116 +5443,6 @@ void Application::copyDisplayViewFrustum(ViewFrustum& viewOut) const { viewOut = _displayViewFrustum; } -void Application::copyShadowViewFrustum(ViewFrustum& viewOut) const { - QMutexLocker viewLocker(&_viewMutex); - viewOut = _shadowViewFrustum; -} - -// WorldBox Render Data & rendering functions - -class WorldBoxRenderData { -public: - typedef render::Payload Payload; - typedef Payload::DataPointer Pointer; - - int _val = 0; - static render::ItemID _item; // unique WorldBoxRenderData -}; - -render::ItemID WorldBoxRenderData::_item { render::Item::INVALID_ITEM_ID }; - -namespace render { - template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape(); } - template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff) { return Item::Bound(); } - template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) { - if (Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) { - PerformanceTimer perfTimer("worldBox"); - - auto& batch = *args->_batch; - DependencyManager::get()->bindSimpleProgram(batch); - renderWorldBox(args, batch); - } - } -} - -void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool selfAvatarOnly) { - - // FIXME: This preDisplayRender call is temporary until we create a separate render::scene for the mirror rendering. - // Then we can move this logic into the Avatar::simulate call. - auto myAvatar = getMyAvatar(); - myAvatar->preDisplaySide(renderArgs); - - PROFILE_RANGE(render, __FUNCTION__); - PerformanceTimer perfTimer("display"); - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()"); - - // load the view frustum - { - QMutexLocker viewLocker(&_viewMutex); - theCamera.loadViewFrustum(_displayViewFrustum); - } - - // TODO fix shadows and make them use the GPU library - - // The pending changes collecting the changes here - render::Transaction transaction; - - // Assuming nothing gets rendered through that - if (!selfAvatarOnly) { - if (DependencyManager::get()->shouldRenderEntities()) { - // render models... - PerformanceTimer perfTimer("entities"); - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::displaySide() ... entities..."); - - RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE; - - if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) { - renderDebugFlags = static_cast(renderDebugFlags | - static_cast(RenderArgs::RENDER_DEBUG_HULLS)); - } - renderArgs->_debugFlags = renderDebugFlags; - //ViveControllerManager::getInstance().updateRendering(renderArgs, _main3DScene, transaction); - } - } - - // FIXME: Move this out of here!, WorldBox should be driven by the entity content just like the other entities - // Make sure the WorldBox is in the scene - if (!render::Item::isValidID(WorldBoxRenderData::_item)) { - auto worldBoxRenderData = make_shared(); - auto worldBoxRenderPayload = make_shared(worldBoxRenderData); - - WorldBoxRenderData::_item = _main3DScene->allocateID(); - - transaction.resetItem(WorldBoxRenderData::_item, worldBoxRenderPayload); - } else { - transaction.updateItem(WorldBoxRenderData::_item, - [](WorldBoxRenderData& payload) { - payload._val++; - }); - } - - { - _main3DScene->enqueueTransaction(transaction); - } - - // For now every frame pass the renderContext - { - PerformanceTimer perfTimer("EngineRun"); - - { - QMutexLocker viewLocker(&_viewMutex); - renderArgs->setViewFrustum(_displayViewFrustum); - } - renderArgs->_cameraMode = (int8_t)theCamera.getMode(); // HACK - renderArgs->_scene = getMain3DScene(); - _renderEngine->getRenderContext()->args = renderArgs; - - // Before the deferred pass, let's try to use the render engine - _renderEngine->run(); - } -} - void Application::resetSensors(bool andReload) { DependencyManager::get()->reset(); DependencyManager::get()->reset(); @@ -7677,5 +7483,4 @@ void Application::setAvatarOverrideUrl(const QUrl& url, bool save) { _avatarOverrideUrl = url; _saveAvatarOverrideUrl = save; } - #include "Application.moc" diff --git a/interface/src/Application.h b/interface/src/Application.h index f3c7f82b4b..478642e2da 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -76,6 +76,7 @@ #include #include #include +#include "FrameTimingsScriptingInterface.h" #include "Sound.h" @@ -147,6 +148,8 @@ public: void initializeGL(); void initializeUi(); + + void updateCamera(RenderArgs& renderArgs); void paintGL(); void resizeGL(); @@ -173,7 +176,6 @@ public: // which might be different from the viewFrustum, i.e. shadowmap // passes, mirror window passes, etc void copyDisplayViewFrustum(ViewFrustum& viewOut) const; - void copyShadowViewFrustum(ViewFrustum& viewOut) const override; const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; } QSharedPointer getEntities() const { return DependencyManager::get(); } QUndoStack* getUndoStack() { return &_undoStack; } @@ -467,8 +469,6 @@ private: void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions); - void renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool isZoomed); - int sendNackPackets(); void sendAvatarViewFrustum(); @@ -478,7 +478,7 @@ private: void initializeAcceptedFiles(); - void displaySide(RenderArgs* renderArgs, Camera& whichCamera, bool selfAvatarOnly = false); + void runRenderFrame(RenderArgs* renderArgs/*, Camera& whichCamera, bool selfAvatarOnly = false*/); bool importJSONFromURL(const QString& urlString); bool importSVOFromURL(const QString& urlString); @@ -535,6 +535,8 @@ private: RateCounter<500> _renderLoopCounter; RateCounter<500> _gameLoopCounter; + FrameTimingsScriptingInterface _frameTimingsScriptingInterface; + QTimer _minimizedWindowTimer; QElapsedTimer _timerStart; QElapsedTimer _lastTimeUpdated; @@ -550,7 +552,6 @@ private: ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc. ViewFrustum _lastQueriedViewFrustum; /// last view frustum used to query octree servers (voxels) ViewFrustum _displayViewFrustum; - ViewFrustum _shadowViewFrustum; quint64 _lastQueriedTime; OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers @@ -621,6 +622,24 @@ private: render::EnginePointer _renderEngine{ new render::Engine() }; gpu::ContextPointer _gpuContext; // initialized during window creation + mutable QMutex _renderArgsMutex{ QMutex::Recursive }; + struct AppRenderArgs { + render::Args _renderArgs; + glm::mat4 _eyeToWorld; + glm::mat4 _eyeOffsets[2]; + glm::mat4 _eyeProjections[2]; + glm::mat4 _headPose; + glm::mat4 _sensorToWorld; + float _sensorToWorldScale { 1.0f }; + bool _isStereo{ false }; + }; + AppRenderArgs _appRenderArgs; + + + using RenderArgsEditor = std::function ; + void editRenderArgs(RenderArgsEditor editor); + + Overlays _overlays; ApplicationOverlay _applicationOverlay; OverlayConductor _overlayConductor; diff --git a/interface/src/Application_render.cpp b/interface/src/Application_render.cpp new file mode 100644 index 0000000000..bbcd8266d7 --- /dev/null +++ b/interface/src/Application_render.cpp @@ -0,0 +1,240 @@ +// +// Application_render.cpp +// interface/src +// +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Application.h" +#include + +#include +#include +#include "ui/Stats.h" +#include +#include "Util.h" + + +// Statically provided display and input plugins +extern DisplayPluginList getDisplayPlugins(); + +void Application::editRenderArgs(RenderArgsEditor editor) { + QMutexLocker renderLocker(&_renderArgsMutex); + editor(_appRenderArgs); + +} + +void Application::paintGL() { + // Some plugins process message events, allowing paintGL to be called reentrantly. + if (_aboutToQuit || _window->isMinimized()) { + return; + } + + _renderFrameCount++; + _lastTimeRendered.start(); + + auto lastPaintBegin = usecTimestampNow(); + PROFILE_RANGE_EX(render, __FUNCTION__, 0xff0000ff, (uint64_t)_renderFrameCount); + PerformanceTimer perfTimer("paintGL"); + + if (nullptr == _displayPlugin) { + return; + } + + DisplayPluginPointer displayPlugin; + { + PROFILE_RANGE(render, "/getActiveDisplayPlugin"); + displayPlugin = getActiveDisplayPlugin(); + } + + { + PROFILE_RANGE(render, "/pluginBeginFrameRender"); + // If a display plugin loses it's underlying support, it + // needs to be able to signal us to not use it + if (!displayPlugin->beginFrameRender(_renderFrameCount)) { + updateDisplayMode(); + return; + } + } + + RenderArgs renderArgs; + glm::mat4 HMDSensorPose; + glm::mat4 eyeToWorld; + glm::mat4 sensorToWorld; + + bool isStereo; + glm::mat4 stereoEyeOffsets[2]; + glm::mat4 stereoEyeProjections[2]; + + { + QMutexLocker viewLocker(&_renderArgsMutex); + renderArgs = _appRenderArgs._renderArgs; + HMDSensorPose = _appRenderArgs._headPose; + eyeToWorld = _appRenderArgs._eyeToWorld; + sensorToWorld = _appRenderArgs._sensorToWorld; + isStereo = _appRenderArgs._isStereo; + for_each_eye([&](Eye eye) { + stereoEyeOffsets[eye] = _appRenderArgs._eyeOffsets[eye]; + stereoEyeProjections[eye] = _appRenderArgs._eyeProjections[eye]; + }); + } + + { + PROFILE_RANGE(render, "/gpuContextReset"); + _gpuContext->beginFrame(HMDSensorPose); + // Reset the gpu::Context Stages + // Back to the default framebuffer; + gpu::doInBatch(_gpuContext, [&](gpu::Batch& batch) { + batch.resetStages(); + }); + } + + + { + PROFILE_RANGE(render, "/renderOverlay"); + PerformanceTimer perfTimer("renderOverlay"); + // NOTE: There is no batch associated with this renderArgs + // the ApplicationOverlay class assumes it's viewport is setup to be the device size + QSize size = getDeviceSize(); + renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height()); + _applicationOverlay.renderOverlay(&renderArgs); + } + + { + PROFILE_RANGE(render, "/updateCompositor"); + getApplicationCompositor().setFrameInfo(_renderFrameCount, eyeToWorld, sensorToWorld); + } + + gpu::FramebufferPointer finalFramebuffer; + QSize finalFramebufferSize; + { + PROFILE_RANGE(render, "/getOutputFramebuffer"); + // Primary rendering pass + auto framebufferCache = DependencyManager::get(); + finalFramebufferSize = framebufferCache->getFrameBufferSize(); + // Final framebuffer that will be handled to the display-plugin + finalFramebuffer = framebufferCache->getFramebuffer(); + } + + { + if (isStereo) { + renderArgs._context->enableStereo(true); + renderArgs._context->setStereoProjections(stereoEyeProjections); + renderArgs._context->setStereoViews(stereoEyeOffsets); + } + + renderArgs._blitFramebuffer = finalFramebuffer; + runRenderFrame(&renderArgs); + } + + gpu::Batch postCompositeBatch; + { + PROFILE_RANGE(render, "/postComposite"); + PerformanceTimer perfTimer("postComposite"); + renderArgs._batch = &postCompositeBatch; + renderArgs._batch->setViewportTransform(ivec4(0, 0, finalFramebufferSize.width(), finalFramebufferSize.height())); + renderArgs._batch->setViewTransform(renderArgs.getViewFrustum().getView()); + _overlays.render3DHUDOverlays(&renderArgs); + } + + auto frame = _gpuContext->endFrame(); + frame->frameIndex = _renderFrameCount; + frame->framebuffer = finalFramebuffer; + frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer) { + DependencyManager::get()->releaseFramebuffer(framebuffer); + }; + frame->overlay = _applicationOverlay.getOverlayTexture(); + frame->postCompositeBatch = postCompositeBatch; + // deliver final scene rendering commands to the display plugin + { + PROFILE_RANGE(render, "/pluginOutput"); + PerformanceTimer perfTimer("pluginOutput"); + _renderLoopCounter.increment(); + displayPlugin->submitFrame(frame); + } + + // Reset the framebuffer and stereo state + renderArgs._blitFramebuffer.reset(); + renderArgs._context->enableStereo(false); + + { + Stats::getInstance()->setRenderDetails(renderArgs._details); + } + + uint64_t lastPaintDuration = usecTimestampNow() - lastPaintBegin; + _frameTimingsScriptingInterface.addValue(lastPaintDuration); +} + + +// WorldBox Render Data & rendering functions + +class WorldBoxRenderData { +public: + typedef render::Payload Payload; + typedef Payload::DataPointer Pointer; + + int _val = 0; + static render::ItemID _item; // unique WorldBoxRenderData +}; + +render::ItemID WorldBoxRenderData::_item{ render::Item::INVALID_ITEM_ID }; + +namespace render { + template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape(); } + template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff) { return Item::Bound(); } + template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) { + if (Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) { + PerformanceTimer perfTimer("worldBox"); + + auto& batch = *args->_batch; + DependencyManager::get()->bindSimpleProgram(batch); + renderWorldBox(args, batch); + } + } +} + +void Application::runRenderFrame(RenderArgs* renderArgs) { + PROFILE_RANGE(render, __FUNCTION__); + PerformanceTimer perfTimer("display"); + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::runRenderFrame()"); + + // The pending changes collecting the changes here + render::Transaction transaction; + + if (DependencyManager::get()->shouldRenderEntities()) { + // render models... + PerformanceTimer perfTimer("entities"); + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + "Application::runRenderFrame() ... entities..."); + + RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE; + + if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) { + renderDebugFlags = static_cast(renderDebugFlags | + static_cast(RenderArgs::RENDER_DEBUG_HULLS)); + } + renderArgs->_debugFlags = renderDebugFlags; + } + + // Make sure the WorldBox is in the scene + // For the record, this one RenderItem is the first one we created and added to the scene. + // We could meoee that code elsewhere but you know... + if (!render::Item::isValidID(WorldBoxRenderData::_item)) { + auto worldBoxRenderData = std::make_shared(); + auto worldBoxRenderPayload = std::make_shared(worldBoxRenderData); + + WorldBoxRenderData::_item = _main3DScene->allocateID(); + + transaction.resetItem(WorldBoxRenderData::_item, worldBoxRenderPayload); + _main3DScene->enqueueTransaction(transaction); + } + + { + PerformanceTimer perfTimer("EngineRun"); + _renderEngine->getRenderContext()->args = renderArgs; + _renderEngine->run(); + } +} diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index 714723e48e..4210e76097 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -265,7 +265,6 @@ void Base3DOverlay::parentDeleted() { } void Base3DOverlay::update(float duration) { - // In Base3DOverlay, if its location or bound changed, the renderTrasnformDirty flag is true. // then the correct transform used for rendering is computed in the update transaction and assigned. if (_renderTransformDirty) { diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 8ce6e7f1f3..4f5ea2042f 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -64,7 +64,6 @@ void ModelOverlay::update(float deltatime) { } _isLoaded = _model->isActive(); - if (isAnimatingSomething()) { if (!jointsMapped()) { mapAnimationJoints(_model->getJointNames()); diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index ba1ffa86c1..93deebac43 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -78,6 +78,8 @@ private: bool _scaleToFit = { false }; float _loadPriority { 0.0f }; + bool _visibleDirty { false }; + bool _drawInFrontDirty { false }; AnimationPointer _animation; QUrl _animationURL; @@ -95,10 +97,6 @@ private: QUrl _jointMappingURL; bool _jointMappingCompleted { false }; QVector _jointMapping; // domain is index into model-joints, range is index into animation-joints - - bool _visibleDirty { false }; - bool _drawInFrontDirty { false }; - }; #endif // hifi_ModelOverlay_h diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 712c728dcb..0897c26a12 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1620,13 +1620,14 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo } void Rig::initAnimGraph(const QUrl& url) { - if (_animGraphURL != url || !_animNode) { + if (_animGraphURL != url || (!_animNode && !_animLoading)) { _animGraphURL = url; _animNode.reset(); // load the anim graph _animLoader.reset(new AnimNodeLoader(url)); + _animLoading = true; connect(_animLoader.get(), &AnimNodeLoader::success, [this](AnimNode::Pointer nodeIn) { _animNode = nodeIn; _animNode->setSkeleton(_animSkeleton); @@ -1637,6 +1638,7 @@ void Rig::initAnimGraph(const QUrl& url) { _userAnimState = { UserAnimState::None, "", 30.0f, false, 0.0f, 0.0f }; overrideAnimation(origState.url, origState.fps, origState.loop, origState.firstFrame, origState.lastFrame); } + _animLoading = false; emit onLoadComplete(); }); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index eabc62ab75..18d49c5f1e 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -303,6 +303,7 @@ protected: std::shared_ptr _animNode; std::shared_ptr _animSkeleton; std::unique_ptr _animLoader; + bool _animLoading { false }; AnimVariantMap _animVars; enum class RigRole { Idle = 0, diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 0cb25a2e2f..67fcc5cc69 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -260,12 +260,24 @@ void EntityTreeRenderer::update(bool simulate) { } } - auto scene = _viewState->getMain3DScene(); - if (scene) { - render::Transaction transaction; - addPendingEntities(scene, transaction); - updateChangedEntities(scene, transaction); - scene->enqueueTransaction(transaction); + { + PerformanceTimer sceneTimer("scene"); + auto scene = _viewState->getMain3DScene(); + if (scene) { + render::Transaction transaction; + { + PerformanceTimer pt("add"); + addPendingEntities(scene, transaction); + } + { + PerformanceTimer pt("change"); + updateChangedEntities(scene, transaction); + } + { + PerformanceTimer pt("enqueue"); + scene->enqueueTransaction(transaction); + } + } } } } @@ -1078,4 +1090,4 @@ void EntityTreeRenderer::onEntityChanged(const EntityItemID& id) { _changedEntitiesGuard.withWriteLock([&] { _changedEntities.insert(id); }); -} \ No newline at end of file +} diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index a1b7ff54de..58b8dd22bf 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1433,7 +1433,7 @@ void EntityItem::recordCreationTime() { const Transform EntityItem::getTransformToCenter(bool& success) const { Transform result = getTransform(success); if (getRegistrationPoint() != ENTITY_ITEM_HALF_VEC3) { // If it is not already centered, translate to center - result.postTranslate(ENTITY_ITEM_HALF_VEC3 - getRegistrationPoint()); // Position to center + result.postTranslate((ENTITY_ITEM_HALF_VEC3 - getRegistrationPoint()) * getDimensions()); // Position to center } return result; } diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp index 1d8cb50a4b..3ade5879c5 100644 --- a/libraries/entities/src/TextEntityItem.cpp +++ b/libraries/entities/src/TextEntityItem.cpp @@ -136,7 +136,7 @@ bool TextEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec2 xyDimensions(dimensions.x, dimensions.y); glm::quat rotation = getRotation(); glm::vec3 position = getPosition() + rotation * - (dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT)); + (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); // FIXME - should set face and surfaceNormal return findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance); diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp index 61c6c8d80e..9595f2959c 100644 --- a/libraries/entities/src/WebEntityItem.cpp +++ b/libraries/entities/src/WebEntityItem.cpp @@ -112,7 +112,7 @@ bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const g glm::vec3 dimensions = getDimensions(); glm::vec2 xyDimensions(dimensions.x, dimensions.y); glm::quat rotation = getRotation(); - glm::vec3 position = getPosition() + rotation * (dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT)); + glm::vec3 position = getPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); if (findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance)) { surfaceNormal = rotation * Vectors::UNIT_Z; diff --git a/libraries/render-utils/src/AbstractViewStateInterface.h b/libraries/render-utils/src/AbstractViewStateInterface.h index 4570ead9e1..96e9f4d222 100644 --- a/libraries/render-utils/src/AbstractViewStateInterface.h +++ b/libraries/render-utils/src/AbstractViewStateInterface.h @@ -31,9 +31,6 @@ public: /// copies the current view frustum for rendering the view state virtual void copyCurrentViewFrustum(ViewFrustum& viewOut) const = 0; - /// copies the shadow view frustum for rendering the view state - virtual void copyShadowViewFrustum(ViewFrustum& viewOut) const = 0; - virtual QThread* getMainThread() = 0; virtual PickRay computePickRay(float x, float y) const = 0; diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 4f7f9ef5c4..c22e99cbbc 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -144,6 +144,7 @@ void AnimDebugDraw::shutdown() { if (scene && _itemID) { render::Transaction transaction; transaction.removeItem(_itemID); + render::Item::clearID(_itemID); scene->enqueueTransaction(transaction); } } @@ -316,7 +317,9 @@ void AnimDebugDraw::update() { if (!scene) { return; } - + if (!render::Item::isValidID(_itemID)) { + return; + } render::Transaction transaction; transaction.updateItem(_itemID, [&](AnimDebugDrawData& data) { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index f25cad8a6e..36f12fb1bf 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -241,7 +241,7 @@ void Model::updateRenderItems() { if (model && model->isLoaded()) { // Ensure the model geometry was not reset between frames if (deleteGeometryCounter == model->_deleteGeometryCounter) { - + const Model::MeshState& state = model->getMeshState(data._meshIndex); Transform renderTransform = modelTransform; if (state.clusterMatrices.size() == 1) { diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index d2d3ad6de6..714be2a8c6 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -15,9 +15,6 @@ #include "Logging.h" #include "TransitionStage.h" -// Comment this to disable transitions (fades) -#define SCENE_ENABLE_TRANSITIONS - using namespace render; void Transaction::resetItem(ItemID id, const PayloadPointer& payload) { @@ -101,16 +98,46 @@ void consolidateTransaction(TransactionQueue& queue, Transaction& singleBatch) { queue.pop(); }; } - -void Scene::processTransactionQueue() { + +uint32_t Scene::enqueueFrame() { PROFILE_RANGE(render, __FUNCTION__); Transaction consolidatedTransaction; - { std::unique_lock lock(_transactionQueueMutex); consolidateTransaction(_transactionQueue, consolidatedTransaction); } - + + uint32_t frameNumber = 0; + { + std::unique_lock lock(_transactionFramesMutex); + _transactionFrames.push_back(consolidatedTransaction); + _transactionFrameNumber++; + frameNumber = _transactionFrameNumber; + } + + return frameNumber; +} + + +void Scene::processTransactionQueue() { + PROFILE_RANGE(render, __FUNCTION__); + + TransactionFrames queuedFrames; + { + // capture the queued frames and clear the queue + std::unique_lock lock(_transactionFramesMutex); + queuedFrames = _transactionFrames; + _transactionFrames.clear(); + } + + // go through the queue of frames and process them + for (auto& frame : queuedFrames) { + processTransactionFrame(frame); + } +} + +void Scene::processTransactionFrame(const Transaction& transaction) { + PROFILE_RANGE(render, __FUNCTION__); { std::unique_lock lock(_itemsMutex); // Here we should be able to check the value of last ItemID allocated @@ -123,32 +150,31 @@ void Scene::processTransactionQueue() { // capture anything coming from the transaction // resets and potential NEW items - resetItems(consolidatedTransaction._resetItems); + resetItems(transaction._resetItems); // Update the numItemsAtomic counter AFTER the reset changes went through _numAllocatedItems.exchange(maxID); // updates - updateItems(consolidatedTransaction._updatedItems); + updateItems(transaction._updatedItems); // removes - removeItems(consolidatedTransaction._removedItems); + removeItems(transaction._removedItems); -#ifdef SCENE_ENABLE_TRANSITIONS // add transitions - transitionItems(consolidatedTransaction._addedTransitions); - reApplyTransitions(consolidatedTransaction._reAppliedTransitions); - queryTransitionItems(consolidatedTransaction._queriedTransitions); -#endif + transitionItems(transaction._addedTransitions); + reApplyTransitions(transaction._reAppliedTransitions); + queryTransitionItems(transaction._queriedTransitions); + // Update the numItemsAtomic counter AFTER the pending changes went through _numAllocatedItems.exchange(maxID); } - if (consolidatedTransaction.touchTransactions()) { + if (transaction.touchTransactions()) { std::unique_lock lock(_selectionsMutex); // resets and potential NEW items - resetSelections(consolidatedTransaction._resetSelections); + resetSelections(transaction._resetSelections); } } diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 3b61a20f24..fef2077897 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -117,6 +117,9 @@ public: // Enqueue transaction to the scene void enqueueTransaction(const Transaction& transaction); + // Enqueue end of frame transactions boundary + uint32_t enqueueFrame(); + // Process the pending transactions queued void processTransactionQueue(); @@ -162,6 +165,15 @@ protected: std::mutex _transactionQueueMutex; TransactionQueue _transactionQueue; + + std::mutex _transactionFramesMutex; + using TransactionFrames = std::list; + TransactionFrames _transactionFrames; + uint32_t _transactionFrameNumber{ 0 }; + + // Process one transaction frame + void processTransactionFrame(const Transaction& transaction); + // The actual database // database of items is protected for editing by a mutex std::mutex _itemsMutex; diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index 9323f651a2..70d91bf1ec 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -223,7 +223,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); (distance > NEAR_GRAB_RADIUS * sensorScaleFactor)) { continue; } - if (entityIsGrabbable(props)) { + if (entityIsGrabbable(props) || entityIsCloneable(props)) { // give haptic feedback if (props.id !== this.hapticTargetID) { Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 58eb4d16f9..c70a74cd7f 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -443,10 +443,6 @@ protected: viewOut = _viewFrustum; } - void copyShadowViewFrustum(ViewFrustum& viewOut) const override { - viewOut = _shadowViewFrustum; - } - QThread* getMainThread() override { return QThread::currentThread(); } @@ -1118,7 +1114,6 @@ private: RenderThread _renderThread; QWindowCamera _camera; ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc. - ViewFrustum _shadowViewFrustum; // current state of view frustum, perspective, orientation, etc. model::SunSkyStage _sunSkyStage; model::LightPointer _globalLight { std::make_shared() }; bool _ready { false };