Merge branch 'master' of https://github.com/highfidelity/hifi into hdr

This commit is contained in:
sam 2016-08-13 11:30:54 -07:00
commit 87d61abe5a
62 changed files with 2107 additions and 453 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View file

@ -10,296 +10,70 @@
import Hifi 1.0
import QtQuick 2.4
import "controls"
import "styles"
import "controls-uit"
import "styles-uit"
import "windows"
ScrollingWindow {
import "LoginDialog"
ModalWindow {
id: root
HifiConstants { id: hifi }
objectName: "LoginDialog"
height: loginDialog.implicitHeight
width: loginDialog.implicitWidth
// FIXME make movable
anchors.centerIn: parent
destroyOnHidden: false
hideBackground: true
shown: false
implicitWidth: 520
implicitHeight: 320
destroyOnCloseButton: true
destroyOnHidden: true
visible: true
property string iconText: ""
property int iconSize: 50
property string title: ""
property int titleWidth: 0
LoginDialog {
id: loginDialog
implicitWidth: backgroundRectangle.width
implicitHeight: backgroundRectangle.height
readonly property int inputWidth: 500
readonly property int inputHeight: 60
readonly property int borderWidth: 30
readonly property int closeMargin: 16
readonly property real tan30: 0.577 // tan(30°)
readonly property int inputSpacing: 16
Rectangle {
id: backgroundRectangle
width: loginDialog.inputWidth + loginDialog.borderWidth * 2
height: loginDialog.inputHeight * 6 + loginDialog.closeMargin * 2
radius: loginDialog.closeMargin * 2
color: "#2c86b1"
opacity: 0.85
}
Column {
id: mainContent
width: loginDialog.inputWidth
spacing: loginDialog.inputSpacing
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
Item {
// Offset content down a little
width: loginDialog.inputWidth
height: loginDialog.closeMargin
}
Rectangle {
width: loginDialog.inputWidth
height: loginDialog.inputHeight
radius: height / 2
color: "#ebebeb"
Image {
source: "../images/login-username.svg"
width: loginDialog.inputHeight * 0.65
height: width
sourceSize: Qt.size(width, height);
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
leftMargin: loginDialog.inputHeight / 4
}
}
TextInput {
id: username
anchors {
fill: parent
leftMargin: loginDialog.inputHeight
rightMargin: loginDialog.inputHeight / 2
}
helperText: "username or email"
color: hifi.colors.text
KeyNavigation.tab: password
KeyNavigation.backtab: password
}
}
Rectangle {
width: loginDialog.inputWidth
height: loginDialog.inputHeight
radius: height / 2
color: "#ebebeb"
Image {
source: "../images/login-password.svg"
width: loginDialog.inputHeight * 0.65
height: width
sourceSize: Qt.size(width, height);
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
leftMargin: loginDialog.inputHeight / 4
}
}
TextInput {
id: password
anchors {
fill: parent
leftMargin: loginDialog.inputHeight
rightMargin: loginDialog.inputHeight / 2
}
helperText: "password"
echoMode: TextInput.Password
color: hifi.colors.text
KeyNavigation.tab: username
KeyNavigation.backtab: username
onFocusChanged: {
if (password.focus) {
password.selectAll()
}
}
}
}
Item {
width: loginDialog.inputWidth
height: loginDialog.inputHeight / 2
Text {
id: messageText
visible: loginDialog.statusText != "" && loginDialog.statusText != "Logging in..."
width: loginDialog.inputWidth
height: loginDialog.inputHeight / 2
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: loginDialog.statusText
color: "white"
}
Row {
id: messageSpinner
visible: loginDialog.statusText == "Logging in..."
onVisibleChanged: visible ? messageSpinnerAnimation.restart() : messageSpinnerAnimation.stop()
spacing: 24
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
Rectangle {
id: spinner1
width: 10
height: 10
color: "#ebebeb"
opacity: 0.05
}
Rectangle {
id: spinner2
width: 10
height: 10
color: "#ebebeb"
opacity: 0.05
}
Rectangle {
id: spinner3
width: 10
height: 10
color: "#ebebeb"
opacity: 0.05
}
SequentialAnimation {
id: messageSpinnerAnimation
running: messageSpinner.visible
loops: Animation.Infinite
NumberAnimation { target: spinner1; property: "opacity"; to: 1.0; duration: 1000 }
NumberAnimation { target: spinner2; property: "opacity"; to: 1.0; duration: 1000 }
NumberAnimation { target: spinner3; property: "opacity"; to: 1.0; duration: 1000 }
NumberAnimation { target: spinner1; property: "opacity"; to: 0.05; duration: 0 }
NumberAnimation { target: spinner2; property: "opacity"; to: 0.05; duration: 0 }
NumberAnimation { target: spinner3; property: "opacity"; to: 0.05; duration: 0 }
}
}
}
Rectangle {
width: loginDialog.inputWidth
height: loginDialog.inputHeight
radius: height / 2
color: "#353535"
TextInput {
Loader {
id: bodyLoader
anchors.fill: parent
text: "Login"
color: "white"
horizontalAlignment: Text.AlignHCenter
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
loginDialog.login(username.text, password.text)
}
}
}
Item {
anchors { left: parent.left; right: parent.right; }
height: loginDialog.inputHeight
Image {
id: hifiIcon
source: "../images/hifi-logo-blackish.svg"
width: loginDialog.inputHeight
height: width
sourceSize: Qt.size(width, height);
anchors { verticalCenter: parent.verticalCenter; horizontalCenter: parent.horizontalCenter }
}
Text {
anchors { verticalCenter: parent.verticalCenter; right: hifiIcon.left; margins: loginDialog.inputSpacing }
text: "Password?"
scale: 0.8
font.underline: true
color: "#e0e0e0"
MouseArea {
anchors { fill: parent; margins: -loginDialog.inputSpacing / 2 }
cursorShape: Qt.PointingHandCursor
onClicked: loginDialog.openUrl(loginDialog.rootUrl + "/users/password/new")
}
}
Text {
anchors { verticalCenter: parent.verticalCenter; left: hifiIcon.right; margins: loginDialog.inputSpacing }
text: "Register"
scale: 0.8
font.underline: true
color: "#e0e0e0"
MouseArea {
anchors { fill: parent; margins: -loginDialog.inputSpacing / 2 }
cursorShape: Qt.PointingHandCursor
onClicked: loginDialog.openUrl(loginDialog.rootUrl + "/signup")
}
}
}
}
}
onShownChanged: {
if (!shown) {
username.text = ""
password.text = ""
loginDialog.statusText = ""
} else {
username.forceActiveFocus()
source: loginDialog.isSteamRunning() ? "LoginDialog/SignInBody.qml" : "LoginDialog/LinkAccountBody.qml"
}
}
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:
root.shown = false;
event.accepted = true;
break;
event.accepted = true
destroy()
break
case Qt.Key_Enter:
case Qt.Key_Return:
if (username.activeFocus) {
event.accepted = true
password.forceActiveFocus()
} else if (password.activeFocus) {
event.accepted = true
if (username.text == "") {
username.forceActiveFocus()
} else {
loginDialog.login(username.text, password.text)
}
}
break
}
}

View file

@ -0,0 +1,125 @@
//
// 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
width: pane.width
height: pane.height
QtObject {
id: d
readonly property int minWidth: 480
readonly property int maxWidth: 1280
readonly property int minHeight: 120
readonly property int maxHeight: 720
function resize() {
var targetWidth = Math.max(titleWidth, additionalTextContainer.contentWidth)
var targetHeight = 4 * hifi.dimensions.contentSpacing.y + buttons.height + additionalTextContainer.height
root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
}
}
Row {
id: buttons
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
margins: 0
topMargin: 3 * 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: root.destroy()
}
}
ShortcutText {
id: additionalTextContainer
anchors {
top: buttons.bottom
horizontalCenter: parent.horizontalCenter
margins: 0
topMargin: hifi.dimensions.contentSpacing.y
}
text: "<a href='https://fake.link'>Already have a High Fidelity profile? Link to an existing profile here.</a>"
wrapMode: Text.WordWrap
lineHeight: 2
lineHeightMode: Text.ProportionalHeight
horizontalAlignment: Text.AlignHCenter
onLinkActivated: {
bodyLoader.source = "LinkAccountBody.qml"
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
}
Component.onCompleted: {
root.title = qsTr("Complete Your Profile")
root.iconText = "<"
d.resize();
}
Connections {
target: loginDialog
onHandleCreateCompleted: {
console.log("Create Succeeded")
loginDialog.loginThroughSteam()
}
onHandleCreateFailed: {
console.log("Create Failed: " + error)
bodyLoader.source = "UsernameCollisionBody.qml"
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
onHandleLoginCompleted: {
console.log("Login Succeeded")
bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : false })
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
onHandleLoginFailed: {
console.log("Login Failed")
}
}
}

View file

@ -0,0 +1,215 @@
//
// 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
width: root.pane.width
height: root.pane.height
function login() {
mainTextContainer.visible = false
loginDialog.login(usernameField.text, passwordField.text)
}
QtObject {
id: d
readonly property int minWidth: 480
readonly property int maxWidth: 1280
readonly property int minHeight: 120
readonly property int maxHeight: 720
function resize() {
var targetWidth = Math.max(titleWidth, form.contentWidth)
var targetHeight = hifi.dimensions.contentSpacing.y + mainTextContainer.height +
4 * hifi.dimensions.contentSpacing.y + form.height +
4 * hifi.dimensions.contentSpacing.y + buttons.height
root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
}
}
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: "User Name or Email"
}
ShortcutText {
anchors {
verticalCenter: parent.verticalCenter
}
text: "<a href='https://highfidelity.com/users/password/new'>Forgot Username?</a>"
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
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: "<a href='https://highfidelity.com/users/password/new'>Forgot Password?</a>"
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
onLinkActivated: loginDialog.openUrl(link)
}
}
}
Row {
id: buttons
anchors {
top: form.bottom
right: parent.right
margins: 0
topMargin: 3 * 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 = "<"
d.resize();
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 })
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
}
onHandleLoginFailed: {
console.log("Login Failed")
mainTextContainer.visible = true
}
onHandleLinkCompleted: {
console.log("Link Succeeded")
bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true })
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
onHandleLinkFailed: {
console.log("Link Failed")
}
}
Keys.onPressed: {
if (!visible) {
return
}
switch (event.key) {
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true
linkAccountBody.login()
break
}
}
}

View file

@ -0,0 +1,128 @@
//
// 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
width: pane.width
height: pane.height
property bool required: false
function login() {
console.log("Trying to log in")
loginDialog.loginThroughSteam()
}
function cancel() {
root.destroy()
}
QtObject {
id: d
readonly property int minWidth: 480
readonly property int maxWidth: 1280
readonly property int minHeight: 120
readonly property int maxHeight: 720
function resize() {
var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth)
var targetHeight = mainTextContainer.height + 3 * hifi.dimensions.contentSpacing.y + buttons.height
root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
}
}
MenuItem {
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: {
root.title = required ? qsTr("Sign In Required")
: qsTr("Sign In")
root.iconText = ""
d.resize();
}
Connections {
target: loginDialog
onHandleLoginCompleted: {
console.log("Login Succeeded")
bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true })
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
onHandleLoginFailed: {
console.log("Login Failed")
bodyLoader.source = "CompleteProfileBody.qml"
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
}
}

View file

@ -0,0 +1,173 @@
//
// 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: root.pane.width
height: root.pane.height
function create() {
mainTextContainer.visible = false
loginDialog.createAccountFromStream(textField.text)
}
QtObject {
id: d
readonly property int minWidth: 480
readonly property int maxWidth: 1280
readonly property int minHeight: 120
readonly property int maxHeight: 720
function resize() {
var targetWidth = Math.max(titleWidth, Math.max(mainTextContainer.contentWidth,
termsContainer.contentWidth))
var targetHeight = mainTextContainer.height +
2 * hifi.dimensions.contentSpacing.y + textField.height +
5 * hifi.dimensions.contentSpacing.y + termsContainer.height +
1 * hifi.dimensions.contentSpacing.y + buttons.height
root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
}
}
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: 2 * hifi.dimensions.contentSpacing.y
}
width: 250
placeholderText: "Choose your own"
}
MenuItem {
id: termsContainer
anchors {
top: textField.bottom
left: parent.left
margins: 0
topMargin: 3 * hifi.dimensions.contentSpacing.y
}
text: qsTr("By creating this user profile, you agree to <a href='https://highfidelity.com/terms'>High Fidelity's Terms of Service</a>")
wrapMode: Text.WordWrap
color: hifi.colors.baseGrayHighlight
lineHeight: 1
lineHeightMode: Text.ProportionalHeight
horizontalAlignment: Text.AlignHCenter
onLinkActivated: loginDialog.openUrl(link)
}
Row {
id: buttons
anchors {
top: termsContainer.bottom
right: parent.right
margins: 0
topMargin: 1 * 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: root.destroy()
}
}
Component.onCompleted: {
root.title = qsTr("Complete Your Profile")
root.iconText = "<"
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 })
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
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
}
}
}

View file

@ -0,0 +1,90 @@
//
// 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
width: pane.width
height: pane.height
property bool welcomeBack: false
function setTitle() {
root.title = (welcomeBack ? qsTr("Welcome back <b>") : qsTr("Welcome <b>")) + Account.username + qsTr("</b>!")
root.iconText = ""
d.resize();
}
QtObject {
id: d
readonly property int minWidth: 480
readonly property int maxWidth: 1280
readonly property int minHeight: 120
readonly property int maxHeight: 720
function resize() {
var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth)
var targetHeight = mainTextContainer.height + 3 * hifi.dimensions.contentSpacing.y + buttons.height
root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
}
}
MenuItem {
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: root.destroy()
}
}
Component.onCompleted: welcomeBody.setTitle()
Connections {
target: Account
onUsernameChanged: welcomeBody.setTitle()
}
}

View file

@ -0,0 +1,20 @@
//
// HorizontalRule.qml
//
// Created by Clement on 7/18/16
// 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 QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
height: 1
color: hifi.colors.lightGray
}

View file

@ -0,0 +1,21 @@
//
// HorizontalSpacer.qml
//
// Created by Clement on 7/18/16
// 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 QtQuick 2.5
import "../styles-uit"
Item {
id: root
property alias size: root.width
width: hifi.dimensions.controlInterlineHeight
height: 1 // Must be non-zero
}

View file

@ -13,6 +13,9 @@ import QtQuick 2.5
import "../styles-uit"
Item {
id: root
property alias size: root.height
width: 1 // Must be non-zero
height: hifi.dimensions.controlInterlineHeight
}

View file

@ -21,8 +21,6 @@ import "messageDialog"
ModalWindow {
id: root
HifiConstants { id: hifi }
implicitWidth: 640
implicitHeight: 320
destroyOnCloseButton: true
destroyOnHidden: true
visible: true
@ -70,7 +68,7 @@ ModalWindow {
QtObject {
id: d
readonly property int minWidth: 480
readonly property int maxWdith: 1280
readonly property int maxWidth: 1280
readonly property int minHeight: 120
readonly property int maxHeight: 720
@ -80,7 +78,7 @@ ModalWindow {
+ (informativeTextContainer.text != "" ? informativeTextContainer.contentHeight + 3 * hifi.dimensions.contentSpacing.y : 0)
+ buttons.height
+ (content.state === "expanded" ? details.implicitHeight + hifi.dimensions.contentSpacing.y : 0)
root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth)
root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWidth) ? d.maxWidth : targetWidth)
root.height = (targetHeight < d.minHeight) ? d.minHeight: ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight)
}
}

View file

@ -0,0 +1,18 @@
//
// ButtonLabel.qml
//
// Created by Clement on 7/18/16
// 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 QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
RalewayBold {
font.pixelSize: hifi.fontSizes.buttonLabel
}

View file

@ -0,0 +1,20 @@
//
// IconButton.qml
//
// Created by Clement on 7/18/16
// 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 QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
RalewayRegular {
font.pixelSize: hifi.fontSizes.iconButton
font.capitalization: Font.AllUppercase
font.letterSpacing: 1.5
}

View file

@ -0,0 +1,18 @@
//
// InputLabel.qml
//
// Created by Clement on 7/18/16
// 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 QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
RalewaySemiBold {
font.pixelSize: hifi.fontSizes.inputLabel
}

View file

@ -0,0 +1,18 @@
//
// ListItem.qml
//
// Created by Clement on 7/18/16
// 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 QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
RalewayRegular {
font.pixelSize: hifi.fontSizes.listItem
}

View file

@ -0,0 +1,18 @@
//
// Logs.qml
//
// Created by Clement on 7/18/16
// 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 QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
AnonymousProRegular {
font.pixelSize: hifi.fontSizes.logs
}

View file

@ -0,0 +1,19 @@
//
// MenuItem.qml
//
// Created by Clement on 7/18/16
// 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 QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
RalewaySemiBold {
lineHeight: 2
font.pixelSize: hifi.fontSizes.menuItem
}

View file

@ -0,0 +1,18 @@
//
// OverlayTitle.qml
//
// Created by Clement on 7/18/16
// 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 QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
RalewayRegular {
font.pixelSize: hifi.fontSizes.overlayTitle
}

View file

@ -0,0 +1,19 @@
//
// SectionName.qml
//
// Created by Clement on 7/18/16
// 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 QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
RalewayRegular {
font.pixelSize: hifi.fontSizes.sectionName
font.capitalization: Font.AllUppercase
}

View file

@ -0,0 +1,18 @@
//
// ShortcutText.qml
//
// Created by Clement on 7/18/16
// 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 QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
RalewayLight {
font.pixelSize: hifi.fontSizes.shortcutText
}

View file

@ -0,0 +1,19 @@
//
// TabName.qml
//
// Created by Clement on 7/18/16
// 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 QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
RalewayRegular {
font.pixelSize: hifi.fontSizes.tabName
font.capitalization: Font.AllUppercase
}

View file

@ -0,0 +1,18 @@
//
// TextFieldInput.qml
//
// Created by Clement on 7/18/16
// 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 QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "."
FiraSansSemiBold {
font.pixelSize: hifi.fontSizes.textFieldInput
}

View file

@ -86,15 +86,16 @@
#include <PhysicsHelpers.h>
#include <plugins/PluginManager.h>
#include <plugins/CodecPlugin.h>
#include <RecordingScriptingInterface.h>
#include <RenderableWebEntityItem.h>
#include <RenderShadowTask.h>
#include <RenderDeferredTask.h>
#include <ResourceCache.h>
#include <SceneScriptingInterface.h>
#include <RecordingScriptingInterface.h>
#include <ScriptEngines.h>
#include <ScriptCache.h>
#include <SoundCache.h>
#include <ScriptEngines.h>
#include <steamworks-wrapper/SteamClient.h>
#include <Tooltip.h>
#include <udt/PacketHeaders.h>
#include <UserActivityLogger.h>
@ -1632,6 +1633,8 @@ void Application::initializeUi() {
rootContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor());
rootContext->setContextProperty("Steam", new SteamScriptingInterface(engine));
_glWidget->installEventFilter(offscreenUi.data());
offscreenUi->setMouseTranslator([=](const QPointF& pt) {
@ -2943,6 +2946,8 @@ void Application::idle(float nsecsElapsed) {
PROFILE_RANGE(__FUNCTION__);
SteamClient::runCallbacks();
float secondsSinceLastUpdate = nsecsElapsed / NSECS_PER_MSEC / MSECS_PER_SECOND;
// If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus.
@ -3251,6 +3256,14 @@ void Application::init() {
addressLookupString = arguments().value(urlIndex + 1);
}
// when +connect_lobby in command line, join steam lobby
const QString STEAM_LOBBY_COMMAND_LINE_KEY = "+connect_lobby";
int lobbyIndex = arguments().indexOf(STEAM_LOBBY_COMMAND_LINE_KEY);
if (lobbyIndex != -1) {
QString lobbyId = arguments().value(lobbyIndex + 1);
SteamClient::joinLobby(lobbyId);
}
Setting::Handle<bool> firstRun { Settings::firstRun, true };
if (addressLookupString.isEmpty() && firstRun.get()) {
qDebug() << "First run and no URL passed... attempting to go to Home or Entry...";
@ -4837,6 +4850,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());
scriptEngine->registerGlobalObject("Users", DependencyManager::get<UsersScriptingInterface>().data());
scriptEngine->registerGlobalObject("Steam", new SteamScriptingInterface(scriptEngine));
}
bool Application::canAcceptURL(const QString& urlString) const {
@ -5114,8 +5129,10 @@ void Application::setPreviousScriptLocation(const QString& location) {
}
void Application::loadScriptURLDialog() const {
auto newScript = OffscreenUi::getText(nullptr, "Open and Run Script", "Script URL");
if (!newScript.isEmpty()) {
auto newScript = OffscreenUi::getText(OffscreenUi::ICON_NONE, "Open and Run Script", "Script URL");
if (QUrl(newScript).scheme() == "atp") {
OffscreenUi::warning("Error Loading Script", "Cannot load client script over ATP");
} else if (!newScript.isEmpty()) {
DependencyManager::get<ScriptEngines>()->loadScript(newScript);
}
}

View file

@ -15,6 +15,7 @@
#include <AddressManager.h>
#include <DomainHandler.h>
#include <NodeList.h>
#include <steamworks-wrapper/SteamClient.h>
#include <UserActivityLogger.h>
#include <UUID.h>
@ -36,11 +37,11 @@ const QString SESSION_ID_KEY = "session_id";
void DiscoverabilityManager::updateLocation() {
auto accountManager = DependencyManager::get<AccountManager>();
auto addressManager = DependencyManager::get<AddressManager>();
auto& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
if (_mode.get() != Discoverability::None && accountManager->isLoggedIn()) {
auto addressManager = DependencyManager::get<AddressManager>();
DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
// construct a QJsonObject given the user's current address information
QJsonObject rootObject;
@ -48,8 +49,6 @@ void DiscoverabilityManager::updateLocation() {
QString pathString = addressManager->currentPath();
const QString LOCATION_KEY_IN_ROOT = "location";
const QString PATH_KEY_IN_LOCATION = "path";
locationObject.insert(PATH_KEY_IN_LOCATION, pathString);
@ -90,6 +89,7 @@ void DiscoverabilityManager::updateLocation() {
// we have a changed location, send it now
_lastLocationObject = locationObject;
const QString LOCATION_KEY_IN_ROOT = "location";
rootObject.insert(LOCATION_KEY_IN_ROOT, locationObject);
apiPath = API_USER_LOCATION_PATH;
@ -109,6 +109,9 @@ void DiscoverabilityManager::updateLocation() {
accountManager->sendRequest(API_USER_HEARTBEAT_PATH, AccountManagerAuth::Optional,
QNetworkAccessManager::PutOperation, callbackParameters);
}
// Update Steam
SteamClient::updateLocation(domainHandler.getHostname(), addressManager->currentFacingAddress());
}
void DiscoverabilityManager::handleHeartbeatResponse(QNetworkReply& requestReply) {

View file

@ -122,11 +122,13 @@ MyAvatar::MyAvatar(RigPointer rig) :
_driveKeys[i] = 0.0f;
}
// Necessary to select the correct slot
using SlotType = void(MyAvatar::*)(const glm::vec3&, bool, const glm::quat&, bool);
// connect to AddressManager signal for location jumps
connect(DependencyManager::get<AddressManager>().data(), &AddressManager::locationChangeRequired,
[=](const glm::vec3& newPosition, bool hasOrientation, const glm::quat& newOrientation, bool shouldFaceLocation){
goToLocation(newPosition, hasOrientation, newOrientation, shouldFaceLocation);
});
this, static_cast<SlotType>(&MyAvatar::goToLocation));
_characterController.setEnabled(true);
@ -1859,7 +1861,7 @@ void MyAvatar::goToLocation(const glm::vec3& newPosition,
glm::quat quatOrientation = cancelOutRollAndPitch(newOrientation);
if (shouldFaceLocation) {
quatOrientation = newOrientation * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
quatOrientation = newOrientation * glm::angleAxis(PI, Vectors::UP);
// move the user a couple units away
const float DISTANCE_TO_USER = 2.0f;

View file

@ -8,6 +8,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <thread>
#include <QCommandLineParser>
#include <QDebug>
#include <QDir>
@ -20,12 +22,13 @@
#include <gl/OpenGLVersionChecker.h>
#include <SharedUtil.h>
#include <steamworks-wrapper/SteamClient.h>
#include "AddressManager.h"
#include "Application.h"
#include "InterfaceLogging.h"
#include "UserActivityLogger.h"
#include "MainWindow.h"
#include <thread>
#ifdef HAS_BUGSPLAT
#include <BuildInfo.h>
@ -137,6 +140,8 @@ int main(int argc, const char* argv[]) {
// or in the main window ctor, before GL startup.
Application::initPlugins(arguments);
SteamClient::init();
int exitCode;
{
QSettings::setDefaultFormat(QSettings::IniFormat);
@ -202,6 +207,8 @@ int main(int argc, const char* argv[]) {
Application::shutdownPlugins();
SteamClient::shutdown();
qCDebug(interfaceapp, "Normal exit.");
#if !defined(DEBUG) && !defined(Q_OS_LINUX)
// HACK: exit immediately (don't handle shutdown callbacks) for Release build

View file

@ -15,6 +15,9 @@
AccountScriptingInterface* AccountScriptingInterface::getInstance() {
static AccountScriptingInterface sharedInstance;
auto accountManager = DependencyManager::get<AccountManager>();
QObject::connect(accountManager.data(), &AccountManager::profileChanged,
&sharedInstance, &AccountScriptingInterface::usernameChanged);
return &sharedInstance;
}

View file

@ -17,6 +17,11 @@
class AccountScriptingInterface : public QObject {
Q_OBJECT
Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged)
signals:
void usernameChanged();
public slots:
static AccountScriptingInterface* getInstance();
QString getUsername();

View file

@ -12,8 +12,11 @@
#include "LoginDialog.h"
#include <QDesktopServices>
#include <QJsonDocument>
#include <QNetworkReply>
#include <NetworkingConstants.h>
#include <steamworks-wrapper/SteamClient.h>
#include "AccountManager.h"
#include "DependencyManager.h"
@ -21,9 +24,7 @@
HIFI_QML_DEF(LoginDialog)
LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent),
_rootUrl(NetworkingConstants::METAVERSE_SERVER_URL.toString())
{
LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent) {
auto accountManager = DependencyManager::get<AccountManager>();
connect(accountManager.data(), &AccountManager::loginComplete,
this, &LoginDialog::handleLoginCompleted);
@ -53,36 +54,102 @@ void LoginDialog::toggleAction() {
}
}
void LoginDialog::handleLoginCompleted(const QUrl&) {
hide();
bool LoginDialog::isSteamRunning() const {
return SteamClient::isRunning();
}
void LoginDialog::handleLoginFailed() {
setStatusText("Invalid username or password");
}
void LoginDialog::setStatusText(const QString& statusText) {
if (statusText != _statusText) {
_statusText = statusText;
emit statusTextChanged();
}
}
QString LoginDialog::statusText() const {
return _statusText;
}
QString LoginDialog::rootUrl() const {
return _rootUrl;
}
void LoginDialog::login(const QString& username, const QString& password) {
void LoginDialog::login(const QString& username, const QString& password) const {
qDebug() << "Attempting to login " << username;
setStatusText("Logging in...");
DependencyManager::get<AccountManager>()->requestAccessToken(username, password);
}
void LoginDialog::openUrl(const QString& url) {
qDebug() << url;
QDesktopServices::openUrl(url);
void LoginDialog::loginThroughSteam() {
qDebug() << "Attempting to login through Steam";
SteamClient::requestTicket([this](Ticket ticket) {
if (ticket.isNull()) {
emit handleLoginFailed();
return;
}
DependencyManager::get<AccountManager>()->requestAccessTokenWithSteam(ticket);
});
}
void LoginDialog::linkSteam() {
qDebug() << "Attempting to link Steam account";
SteamClient::requestTicket([this](Ticket ticket) {
if (ticket.isNull()) {
emit handleLoginFailed();
return;
}
JSONCallbackParameters callbackParams;
callbackParams.jsonCallbackReceiver = this;
callbackParams.jsonCallbackMethod = "linkCompleted";
callbackParams.errorCallbackReceiver = this;
callbackParams.errorCallbackMethod = "linkFailed";
const QString LINK_STEAM_PATH = "api/v1/user/steam/link";
QJsonObject payload;
payload.insert("steam_auth_ticket", QJsonValue::fromVariant(QVariant(ticket)));
auto accountManager = DependencyManager::get<AccountManager>();
accountManager->sendRequest(LINK_STEAM_PATH, AccountManagerAuth::Required,
QNetworkAccessManager::PostOperation, callbackParams,
QJsonDocument(payload).toJson());
});
}
void LoginDialog::createAccountFromStream(QString username) {
qDebug() << "Attempting to create account from Steam info";
SteamClient::requestTicket([this, username](Ticket ticket) {
if (ticket.isNull()) {
emit handleLoginFailed();
return;
}
JSONCallbackParameters callbackParams;
callbackParams.jsonCallbackReceiver = this;
callbackParams.jsonCallbackMethod = "createCompleted";
callbackParams.errorCallbackReceiver = this;
callbackParams.errorCallbackMethod = "createFailed";
const QString CREATE_ACCOUNT_FROM_STEAM_PATH = "api/v1/user/steam/create";
QJsonObject payload;
payload.insert("steam_auth_ticket", QJsonValue::fromVariant(QVariant(ticket)));
if (!username.isEmpty()) {
payload.insert("username", QJsonValue::fromVariant(QVariant(username)));
}
auto accountManager = DependencyManager::get<AccountManager>();
accountManager->sendRequest(CREATE_ACCOUNT_FROM_STEAM_PATH, AccountManagerAuth::None,
QNetworkAccessManager::PostOperation, callbackParams,
QJsonDocument(payload).toJson());
});
}
void LoginDialog::openUrl(const QString& url) const {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
auto browser = offscreenUi->load("Browser.qml");
browser->setProperty("url", url);
}
void LoginDialog::linkCompleted(QNetworkReply& reply) {
emit handleLinkCompleted();
}
void LoginDialog::linkFailed(QNetworkReply& reply) {
emit handleLinkFailed(reply.errorString());
}
void LoginDialog::createCompleted(QNetworkReply& reply) {
emit handleCreateCompleted();
}
void LoginDialog::createFailed(QNetworkReply& reply) {
emit handleCreateFailed(reply.errorString());
}

View file

@ -16,35 +16,44 @@
#include <OffscreenQmlDialog.h>
class QNetworkReply;
class LoginDialog : public OffscreenQmlDialog {
Q_OBJECT
HIFI_QML_DECL
Q_PROPERTY(QString statusText READ statusText WRITE setStatusText NOTIFY statusTextChanged)
Q_PROPERTY(QString rootUrl READ rootUrl)
public:
static void toggleAction();
LoginDialog(QQuickItem* parent = nullptr);
void setStatusText(const QString& statusText);
QString statusText() const;
QString rootUrl() const;
signals:
void statusTextChanged();
protected:
void handleLoginCompleted(const QUrl& authURL);
void handleLoginCompleted();
void handleLoginFailed();
Q_INVOKABLE void login(const QString& username, const QString& password);
Q_INVOKABLE void openUrl(const QString& url);
private:
QString _statusText;
const QString _rootUrl;
void handleLinkCompleted();
void handleLinkFailed(QString error);
void handleCreateCompleted();
void handleCreateFailed(QString error);
public slots:
void linkCompleted(QNetworkReply& reply);
void linkFailed(QNetworkReply& reply);
void createCompleted(QNetworkReply& reply);
void createFailed(QNetworkReply& reply);
protected slots:
Q_INVOKABLE bool isSteamRunning() const;
Q_INVOKABLE void login(const QString& username, const QString& password) const;
Q_INVOKABLE void loginThroughSteam();
Q_INVOKABLE void linkSteam();
Q_INVOKABLE void createAccountFromStream(QString username = QString());
Q_INVOKABLE void openUrl(const QString& url) const;
};
#endif // hifi_LoginDialog_h

View file

@ -21,6 +21,7 @@ const bool DEFAULT_IS_SOLID = false;
const bool DEFAULT_IS_DASHED_LINE = false;
Base3DOverlay::Base3DOverlay() :
SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()),
_lineWidth(DEFAULT_LINE_WIDTH),
_isSolid(DEFAULT_IS_SOLID),
_isDashedLine(DEFAULT_IS_DASHED_LINE),
@ -31,15 +32,85 @@ Base3DOverlay::Base3DOverlay() :
Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
Overlay(base3DOverlay),
_transform(base3DOverlay->_transform),
SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()),
_lineWidth(base3DOverlay->_lineWidth),
_isSolid(base3DOverlay->_isSolid),
_isDashedLine(base3DOverlay->_isDashedLine),
_ignoreRayIntersection(base3DOverlay->_ignoreRayIntersection),
_drawInFront(base3DOverlay->_drawInFront)
{
setTransform(base3DOverlay->getTransform());
}
void Base3DOverlay::setProperties(const QVariantMap& properties) {
QVariantMap convertOverlayLocationFromScriptSemantics(const QVariantMap& properties) {
// the position and rotation in _transform are relative to the parent (aka local). The versions coming from
// scripts are in world-frame, unless localPosition or localRotation are used. Patch up the properties
// so that "position" and "rotation" are relative-to-parent values.
QVariantMap result = properties;
QUuid parentID = result["parentID"].isValid() ? QUuid(result["parentID"].toString()) : QUuid();
int parentJointIndex = result["parentJointIndex"].isValid() ? result["parentJointIndex"].toInt() : -1;
bool success;
// make "position" and "orientation" be relative-to-parent
if (result["localPosition"].isValid()) {
result["position"] = result["localPosition"];
} else if (result["position"].isValid()) {
glm::vec3 localPosition = SpatiallyNestable::worldToLocal(vec3FromVariant(result["position"]),
parentID, parentJointIndex, success);
result["position"] = vec3toVariant(localPosition);
}
if (result["localOrientation"].isValid()) {
result["orientation"] = result["localOrientation"];
} else if (result["orientation"].isValid()) {
glm::quat localOrientation = SpatiallyNestable::worldToLocal(quatFromVariant(result["orientation"]),
parentID, parentJointIndex, success);
result["orientation"] = quatToVariant(localOrientation);
}
return result;
}
void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
QVariantMap properties = originalProperties;
// carry over some legacy keys
if (!properties["position"].isValid() && !properties["localPosition"].isValid()) {
if (properties["p1"].isValid()) {
properties["position"] = properties["p1"];
} else if (properties["point"].isValid()) {
properties["position"] = properties["point"];
} else if (properties["start"].isValid()) {
properties["position"] = properties["start"];
}
}
if (!properties["orientation"].isValid() && properties["rotation"].isValid()) {
properties["orientation"] = properties["rotation"];
}
if (!properties["localOrientation"].isValid() && properties["localRotation"].isValid()) {
properties["localOrientation"] = properties["localRotation"];
}
// All of parentID, parentJointIndex, position, orientation are needed to make sense of any of them.
// If any of these changed, pull any missing properties from the overlay.
if (properties["parentID"].isValid() || properties["parentJointIndex"].isValid() ||
properties["position"].isValid() || properties["localPosition"].isValid() ||
properties["orientation"].isValid() || properties["localOrientation"].isValid()) {
if (!properties["parentID"].isValid()) {
properties["parentID"] = getParentID();
}
if (!properties["parentJointIndex"].isValid()) {
properties["parentJointIndex"] = getParentJointIndex();
}
if (!properties["position"].isValid() && !properties["localPosition"].isValid()) {
properties["position"] = vec3toVariant(getPosition());
}
if (!properties["orientation"].isValid() && !properties["localOrientation"].isValid()) {
properties["orientation"] = quatToVariant(getOrientation());
}
}
properties = convertOverlayLocationFromScriptSemantics(properties);
Overlay::setProperties(properties);
bool needRenderItemUpdate = false;
@ -52,17 +123,12 @@ void Base3DOverlay::setProperties(const QVariantMap& properties) {
needRenderItemUpdate = true;
}
auto position = properties["position"];
// if "position" property was not there, check to see if they included aliases: point, p1
if (!position.isValid()) {
position = properties["p1"];
if (!position.isValid()) {
position = properties["point"];
if (properties["position"].isValid()) {
setLocalPosition(vec3FromVariant(properties["position"]));
needRenderItemUpdate = true;
}
}
if (position.isValid()) {
setPosition(vec3FromVariant(position));
if (properties["orientation"].isValid()) {
setLocalOrientation(quatFromVariant(properties["orientation"]));
needRenderItemUpdate = true;
}
@ -71,13 +137,6 @@ void Base3DOverlay::setProperties(const QVariantMap& properties) {
needRenderItemUpdate = true;
}
auto rotation = properties["rotation"];
if (rotation.isValid()) {
setRotation(quatFromVariant(rotation));
needRenderItemUpdate = true;
}
if (properties["isSolid"].isValid()) {
setIsSolid(properties["isSolid"].toBool());
}
@ -107,6 +166,15 @@ void Base3DOverlay::setProperties(const QVariantMap& properties) {
setIgnoreRayIntersection(properties["ignoreRayIntersection"].toBool());
}
if (properties["parentID"].isValid()) {
setParentID(QUuid(properties["parentID"].toString()));
needRenderItemUpdate = true;
}
if (properties["parentJointIndex"].isValid()) {
setParentJointIndex(properties["parentJointIndex"].toInt());
needRenderItemUpdate = true;
}
// Communicate changes to the renderItem if needed
if (needRenderItemUpdate) {
auto itemID = getRenderItemID();
@ -123,12 +191,18 @@ QVariant Base3DOverlay::getProperty(const QString& property) {
if (property == "position" || property == "start" || property == "p1" || property == "point") {
return vec3toVariant(getPosition());
}
if (property == "localPosition") {
return vec3toVariant(getLocalPosition());
}
if (property == "rotation" || property == "orientation") {
return quatToVariant(getOrientation());
}
if (property == "localRotation" || property == "localOrientation") {
return quatToVariant(getLocalOrientation());
}
if (property == "lineWidth") {
return _lineWidth;
}
if (property == "rotation") {
return quatToVariant(getRotation());
}
if (property == "isSolid" || property == "isFilled" || property == "solid" || property == "filed") {
return _isSolid;
}
@ -144,6 +218,12 @@ QVariant Base3DOverlay::getProperty(const QString& property) {
if (property == "drawInFront") {
return _drawInFront;
}
if (property == "parentID") {
return getParentID();
}
if (property == "parentJointIndex") {
return getParentJointIndex();
}
return Overlay::getProperty(property);
}
@ -152,3 +232,19 @@ bool Base3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3
float& distance, BoxFace& face, glm::vec3& surfaceNormal) {
return false;
}
void Base3DOverlay::locationChanged(bool tellPhysics) {
auto itemID = getRenderItemID();
if (render::Item::isValidID(itemID)) {
render::ScenePointer scene = qApp->getMain3DScene();
render::PendingChanges pendingChanges;
pendingChanges.updateItem(itemID);
scene->enqueuePendingChanges(pendingChanges);
}
// Overlays can't currently have children.
// SpatiallyNestable::locationChanged(tellPhysics); // tell all the children, also
}
void Base3DOverlay::parentDeleted() {
qApp->getOverlays().deleteOverlay(getOverlayID());
}

View file

@ -12,10 +12,11 @@
#define hifi_Base3DOverlay_h
#include <Transform.h>
#include <SpatiallyNestable.h>
#include "Overlay.h"
class Base3DOverlay : public Overlay {
class Base3DOverlay : public Overlay, public SpatiallyNestable {
Q_OBJECT
public:
@ -24,12 +25,9 @@ public:
// getters
virtual bool is3D() const override { return true; }
const glm::vec3& getPosition() const { return _transform.getTranslation(); }
const glm::quat& getRotation() const { return _transform.getRotation(); }
const glm::vec3& getScale() const { return _transform.getScale(); }
// TODO: consider implementing registration points in this class
const glm::vec3& getCenter() const { return getPosition(); }
glm::vec3 getCenter() const { return getPosition(); }
float getLineWidth() const { return _lineWidth; }
bool getIsSolid() const { return _isSolid; }
@ -38,12 +36,6 @@ public:
bool getIgnoreRayIntersection() const { return _ignoreRayIntersection; }
bool getDrawInFront() const { return _drawInFront; }
// setters
void setPosition(const glm::vec3& value) { _transform.setTranslation(value); }
void setRotation(const glm::quat& value) { _transform.setRotation(value); }
void setScale(float value) { _transform.setScale(value); }
void setScale(const glm::vec3& value) { _transform.setScale(value); }
void setLineWidth(float lineWidth) { _lineWidth = lineWidth; }
void setIsSolid(bool isSolid) { _isSolid = isSolid; }
void setIsDashedLine(bool isDashedLine) { _isDashedLine = isDashedLine; }
@ -64,7 +56,8 @@ public:
}
protected:
Transform _transform;
virtual void locationChanged(bool tellPhysics = true) override;
virtual void parentDeleted() override;
float _lineWidth;
bool _isSolid;

View file

@ -69,7 +69,7 @@ void Circle3DOverlay::render(RenderArgs* args) {
// FIXME: THe line width of _lineWidth is not supported anymore, we ll need a workaround
auto transform = _transform;
auto transform = getTransform();
transform.postScale(glm::vec3(getDimensions(), 1.0f));
batch.setModelTransform(transform);

View file

@ -37,7 +37,11 @@ Image3DOverlay::Image3DOverlay(const Image3DOverlay* image3DOverlay) :
}
void Image3DOverlay::update(float deltatime) {
applyTransformTo(_transform);
if (usecTimestampNow() > _transformExpiry) {
Transform transform = getTransform();
applyTransformTo(transform);
setTransform(transform);
}
}
void Image3DOverlay::render(RenderArgs* args) {
@ -86,8 +90,9 @@ void Image3DOverlay::render(RenderArgs* args) {
xColor color = getColor();
float alpha = getAlpha();
applyTransformTo(_transform, true);
Transform transform = _transform;
Transform transform = getTransform();
applyTransformTo(transform, true);
setTransform(transform);
transform.postScale(glm::vec3(getDimensions(), 1.0f));
batch->setModelTransform(transform);
@ -187,7 +192,10 @@ bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec
float& distance, BoxFace& face, glm::vec3& surfaceNormal) {
if (_texture && _texture->isLoaded()) {
// Make sure position and rotation is updated.
applyTransformTo(_transform, true);
Transform transform = getTransform();
// XXX this code runs too often for this...
// applyTransformTo(transform, true);
// setTransform(transform);
// Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale.
bool isNull = _fromImage.isNull();
@ -197,7 +205,10 @@ bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec
glm::vec2 dimensions = _dimensions * glm::vec2(width / maxSize, height / maxSize);
// FIXME - face and surfaceNormal not being set
return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), dimensions, distance);
return findRayRectangleIntersection(origin, direction,
transform.getRotation(),
transform.getTranslation(),
dimensions, distance);
}
return false;

View file

@ -35,7 +35,7 @@ AABox Line3DOverlay::getBounds() const {
auto extents = Extents{};
extents.addPoint(_start);
extents.addPoint(_end);
extents.transform(_transform);
extents.transform(getTransform());
return AABox(extents);
}
@ -52,7 +52,7 @@ void Line3DOverlay::render(RenderArgs* args) {
auto batch = args->_batch;
if (batch) {
batch->setModelTransform(_transform);
batch->setModelTransform(getTransform());
auto geometryCache = DependencyManager::get<GeometryCache>();
if (getIsDashedLine()) {

View file

@ -178,3 +178,12 @@ bool ModelOverlay::findRayIntersectionExtraInfo(const glm::vec3& origin, const g
ModelOverlay* ModelOverlay::createClone() const {
return new ModelOverlay(this);
}
void ModelOverlay::locationChanged(bool tellPhysics) {
Base3DOverlay::locationChanged(tellPhysics);
if (_model && _model->isActive()) {
_model->setRotation(getRotation());
_model->setTranslation(getPosition());
}
}

View file

@ -39,6 +39,8 @@ public:
virtual bool addToScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) override;
virtual void removeFromScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) override;
void locationChanged(bool tellPhysics) override;
private:
ModelPointer _model;

View file

@ -31,6 +31,10 @@ public:
Overlay();
Overlay(const Overlay* overlay);
~Overlay();
unsigned int getOverlayID() { return _overlayID; }
void setOverlayID(unsigned int overlayID) { _overlayID = overlayID; }
virtual void update(float deltatime) {}
virtual void render(RenderArgs* args) = 0;
@ -85,6 +89,8 @@ protected:
render::ItemID _renderItemID{ render::Item::INVALID_ITEM_ID };
unsigned int _overlayID; // what Overlays.cpp knows this instance as
bool _isLoaded;
float _alpha;

View file

@ -192,6 +192,7 @@ unsigned int Overlays::addOverlay(const QString& type, const QVariant& propertie
unsigned int Overlays::addOverlay(Overlay::Pointer overlay) {
QWriteLocker lock(&_lock);
unsigned int thisID = _nextOverlayID;
overlay->setOverlayID(thisID);
_nextOverlayID++;
if (overlay->is3D()) {
_overlaysWorld[thisID] = overlay;

View file

@ -30,7 +30,7 @@ AABox Planar3DOverlay::getBounds() const {
auto halfDimensions = glm::vec3{_dimensions / 2.0f, 0.01f};
auto extents = Extents{-halfDimensions, halfDimensions};
extents.transform(_transform);
extents.transform(getTransform());
return AABox(extents);
}

View file

@ -39,7 +39,7 @@ void Sphere3DOverlay::render(RenderArgs* args) {
auto batch = args->_batch;
if (batch) {
Transform transform = _transform;
Transform transform = getTransform();
transform.postScale(getDimensions() * SPHERE_OVERLAY_SCALE);
batch->setModelTransform(transform);

View file

@ -65,7 +65,11 @@ xColor Text3DOverlay::getBackgroundColor() {
}
void Text3DOverlay::update(float deltatime) {
applyTransformTo(_transform);
if (usecTimestampNow() > _transformExpiry) {
Transform transform = getTransform();
applyTransformTo(transform);
setTransform(transform);
}
}
void Text3DOverlay::render(RenderArgs* args) {
@ -76,8 +80,10 @@ void Text3DOverlay::render(RenderArgs* args) {
Q_ASSERT(args->_batch);
auto& batch = *args->_batch;
applyTransformTo(_transform, true);
batch.setModelTransform(_transform);
Transform transform = getTransform();
applyTransformTo(transform, true);
setTransform(transform);
batch.setModelTransform(transform);
const float MAX_COLOR = 255.0f;
xColor backgroundColor = getBackgroundColor();
@ -102,7 +108,6 @@ void Text3DOverlay::render(RenderArgs* args) {
glm::vec2 clipDimensions((dimensions.x - (_leftMargin + _rightMargin)) / scaleFactor,
(dimensions.y - (_topMargin + _bottomMargin)) / scaleFactor);
Transform transform = _transform;
transform.postTranslate(glm::vec3(-(halfDimensions.x - _leftMargin),
halfDimensions.y - _topMargin, 0.001f));
transform.setScale(scaleFactor);
@ -222,6 +227,8 @@ QSizeF Text3DOverlay::textSize(const QString& text) const {
bool Text3DOverlay::findRayIntersection(const glm::vec3 &origin, const glm::vec3 &direction, float &distance,
BoxFace &face, glm::vec3& surfaceNormal) {
applyTransformTo(_transform, true);
Transform transform = getTransform();
applyTransformTo(transform, true);
setTransform(transform);
return Billboard3DOverlay::findRayIntersection(origin, direction, distance, face, surfaceNormal);
}

View file

@ -56,7 +56,7 @@ bool Volume3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::ve
float& distance, BoxFace& face, glm::vec3& surfaceNormal) {
// extents is the entity relative, scaled, centered extents of the entity
glm::mat4 worldToEntityMatrix;
_transform.getInverseMatrix(worldToEntityMatrix);
getTransform().getInverseMatrix(worldToEntityMatrix);
glm::vec3 overlayFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f));
glm::vec3 overlayFrameDirection = glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f));

View file

@ -57,7 +57,11 @@ Web3DOverlay::~Web3DOverlay() {
}
void Web3DOverlay::update(float deltatime) {
applyTransformTo(_transform);
if (usecTimestampNow() > _transformExpiry) {
Transform transform = getTransform();
applyTransformTo(transform);
setTransform(transform);
}
}
void Web3DOverlay::render(RenderArgs* args) {
@ -85,8 +89,9 @@ void Web3DOverlay::render(RenderArgs* args) {
vec2 halfSize = size / 2.0f;
vec4 color(toGlm(getColor()), getAlpha());
applyTransformTo(_transform, true);
Transform transform = _transform;
Transform transform = getTransform();
applyTransformTo(transform, true);
setTransform(transform);
if (glm::length2(getDimensions()) != 1.0f) {
transform.postScale(vec3(getDimensions(), 1.0f));
}
@ -165,7 +170,10 @@ bool Web3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3&
// FIXME - face and surfaceNormal not being returned
// Make sure position and rotation is updated.
applyTransformTo(_transform, true);
Transform transform;
applyTransformTo(transform, true);
setTransform(transform);
vec2 size = _resolution / _dpi * INCHES_TO_METERS * vec2(getDimensions());
// Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale.
return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), size, distance);

View file

@ -322,10 +322,6 @@ public:
/// return preferred shape type (actual physical shape may differ)
virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; }
// these are only needed because the names don't match
virtual const glm::quat getRotation() const { return getOrientation(); }
virtual void setRotation(glm::quat orientation) { setOrientation(orientation); }
// updateFoo() methods to be used when changes need to be accumulated in the _dirtyFlags
virtual void updateRegistrationPoint(const glm::vec3& value);
void updatePosition(const glm::vec3& value);

View file

@ -505,6 +505,29 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas
connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError)));
}
void AccountManager::requestAccessTokenWithSteam(QByteArray authSessionTicket) {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest request;
request.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter());
QUrl grantURL = _authURL;
grantURL.setPath("/oauth/token");
const QString ACCOUNT_MANAGER_REQUESTED_SCOPE = "owner";
QByteArray postData;
postData.append("grant_type=password&");
postData.append("steam_auth_ticket=" + QUrl::toPercentEncoding(authSessionTicket) + "&");
postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE);
request.setUrl(grantURL);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QNetworkReply* requestReply = networkAccessManager.post(request, postData);
connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished);
connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError)));
}
void AccountManager::requestAccessTokenFinished() {
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
@ -545,6 +568,7 @@ void AccountManager::requestAccessTokenFinished() {
void AccountManager::requestAccessTokenError(QNetworkReply::NetworkError error) {
// TODO: error handling
qCDebug(networking) << "AccountManager requestError - " << error;
emit loginFailed();
}
void AccountManager::requestProfile() {

View file

@ -96,6 +96,7 @@ public:
public slots:
void requestAccessToken(const QString& login, const QString& password);
void requestAccessTokenWithSteam(QByteArray authSessionTicket);
void requestAccessTokenFinished();
void requestProfileFinished();

View file

@ -17,6 +17,7 @@
#include <QStringList>
#include <GLMHelpers.h>
#include <NumericalConstants.h>
#include <SettingHandle.h>
#include <UUID.h>
@ -46,7 +47,7 @@ bool AddressManager::isConnected() {
return DependencyManager::get<NodeList>()->getDomainHandler().isConnected();
}
const QUrl AddressManager::currentAddress() const {
QUrl AddressManager::currentAddress() const {
QUrl hifiURL;
hifiURL.setScheme(HIFI_URL_SCHEME);
@ -61,6 +62,21 @@ const QUrl AddressManager::currentAddress() const {
return hifiURL;
}
QUrl AddressManager::currentFacingAddress() const {
QUrl hifiURL;
hifiURL.setScheme(HIFI_URL_SCHEME);
hifiURL.setHost(_host);
if (_port != 0 && _port != DEFAULT_DOMAIN_SERVER_PORT) {
hifiURL.setPort(_port);
}
hifiURL.setPath(currentFacingPath());
return hifiURL;
}
void AddressManager::loadSettings(const QString& lookupString) {
if (lookupString.isEmpty()) {
handleUrl(currentAddressHandle.get().toString(), LookupTrigger::StartupFromSettings);
@ -97,7 +113,7 @@ void AddressManager::storeCurrentAddress() {
currentAddressHandle.set(currentAddress());
}
const QString AddressManager::currentPath(bool withOrientation) const {
QString AddressManager::currentPath(bool withOrientation) const {
if (_positionGetter) {
QString pathString = "/" + createByteArray(_positionGetter());
@ -121,6 +137,25 @@ const QString AddressManager::currentPath(bool withOrientation) const {
}
}
QString AddressManager::currentFacingPath() const {
if (_positionGetter && _orientationGetter) {
auto position = _positionGetter();
auto orientation = _orientationGetter();
// move the user a couple units away
const float DISTANCE_TO_USER = 2.0f;
position += orientation * Vectors::FRONT * DISTANCE_TO_USER;
// rotate the user by 180 degrees
orientation = orientation * glm::angleAxis(PI, Vectors::UP);
return "/" + createByteArray(position) + "/" + createByteArray(orientation);
} else {
qCDebug(networking) << "Cannot create address path without a getter for position/orientation.";
return QString();
}
}
const JSONCallbackParameters& AddressManager::apiCallbackParameters() {
static bool hasSetupParameters = false;
static JSONCallbackParameters callbackParams;

View file

@ -57,8 +57,10 @@ public:
bool isConnected();
const QString& getProtocol() { return HIFI_URL_SCHEME; };
const QUrl currentAddress() const;
const QString currentPath(bool withOrientation = true) const;
QUrl currentAddress() const;
QUrl currentFacingAddress() const;
QString currentPath(bool withOrientation = true) const;
QString currentFacingPath() const;
const QUuid& getRootPlaceID() const { return _rootPlaceID; }
const QString& getPlaceName() const { return _placeName; }

View file

@ -26,6 +26,12 @@ SpatiallyNestable::SpatiallyNestable(NestableType nestableType, QUuid id) :
_transform.setRotation(glm::quat());
}
SpatiallyNestable::~SpatiallyNestable() {
forEachChild([&](SpatiallyNestablePointer object) {
object->parentDeleted();
});
}
const QUuid SpatiallyNestable::getID() const {
QUuid result;
_idLock.withReadLock([&] {
@ -35,6 +41,10 @@ const QUuid SpatiallyNestable::getID() const {
}
void SpatiallyNestable::setID(const QUuid& id) {
// adjust the parentID of any children
forEachChild([&](SpatiallyNestablePointer object) {
object->setParentID(id);
});
_idLock.withWriteLock([&] {
_id = id;
});
@ -313,17 +323,20 @@ void SpatiallyNestable::setPosition(const glm::vec3& position, bool& success, bo
success = false;
return;
}
bool changed = false;
Transform parentTransform = getParentTransform(success);
Transform myWorldTransform;
_transformLock.withWriteLock([&] {
Transform::mult(myWorldTransform, parentTransform, _transform);
if (myWorldTransform.getTranslation() != position) {
changed = true;
myWorldTransform.setTranslation(position);
Transform::inverseMult(_transform, parentTransform, myWorldTransform);
}
});
if (success) {
if (success && changed) {
locationChanged(tellPhysics);
} else {
qDebug() << "setPosition failed for" << getID();
}
}
@ -363,14 +376,18 @@ void SpatiallyNestable::setOrientation(const glm::quat& orientation, bool& succe
return;
}
bool changed = false;
Transform parentTransform = getParentTransform(success);
Transform myWorldTransform;
_transformLock.withWriteLock([&] {
Transform::mult(myWorldTransform, parentTransform, _transform);
if (myWorldTransform.getRotation() != orientation) {
changed = true;
myWorldTransform.setRotation(orientation);
Transform::inverseMult(_transform, parentTransform, myWorldTransform);
}
});
if (success) {
if (success && changed) {
locationChanged(tellPhysics);
}
}
@ -513,6 +530,15 @@ const Transform SpatiallyNestable::getTransform(bool& success, int depth) const
return result;
}
const Transform SpatiallyNestable::getTransform() const {
bool success;
Transform result = getTransform(success);
if (!success) {
qDebug() << "getTransform failed for" << getID();
}
return result;
}
const Transform SpatiallyNestable::getTransform(int jointIndex, bool& success, int depth) const {
// this returns the world-space transform for this object. It finds its parent's transform (which may
// cause this object's parent to query its parent, etc) and multiplies this object's local transform onto it.
@ -549,15 +575,27 @@ void SpatiallyNestable::setTransform(const Transform& transform, bool& success)
success = false;
return;
}
bool changed = false;
Transform parentTransform = getParentTransform(success);
_transformLock.withWriteLock([&] {
Transform beforeTransform = _transform;
Transform::inverseMult(_transform, parentTransform, transform);
if (_transform != beforeTransform) {
changed = true;
}
});
if (success) {
if (success && changed) {
locationChanged();
}
}
bool SpatiallyNestable::setTransform(const Transform& transform) {
bool success;
setTransform(transform, success);
return success;
}
glm::vec3 SpatiallyNestable::getScale() const {
// TODO: scale
glm::vec3 result;
@ -575,14 +613,43 @@ glm::vec3 SpatiallyNestable::getScale(int jointIndex) const {
void SpatiallyNestable::setScale(const glm::vec3& scale) {
// guard against introducing NaN into the transform
if (isNaN(scale)) {
qDebug() << "SpatiallyNestable::setLocalScale -- scale contains NaN";
qDebug() << "SpatiallyNestable::setScale -- scale contains NaN";
return;
}
bool changed = false;
// TODO: scale
_transformLock.withWriteLock([&] {
if (_transform.getScale() != scale) {
_transform.setScale(scale);
changed = true;
}
});
if (changed) {
dimensionsChanged();
}
}
void SpatiallyNestable::setScale(float value) {
// guard against introducing NaN into the transform
if (value <= 0.0f) {
qDebug() << "SpatiallyNestable::setScale -- scale is zero or negative value";
return;
}
bool changed = false;
// TODO: scale
_transformLock.withWriteLock([&] {
glm::vec3 beforeScale = _transform.getScale();
_transform.setScale(value);
if (_transform.getScale() != beforeScale) {
changed = true;
}
});
if (changed) {
dimensionsChanged();
}
}
const Transform SpatiallyNestable::getLocalTransform() const {
@ -599,10 +666,18 @@ void SpatiallyNestable::setLocalTransform(const Transform& transform) {
qDebug() << "SpatiallyNestable::setLocalTransform -- transform contains NaN";
return;
}
bool changed = false;
_transformLock.withWriteLock([&] {
if (_transform != transform) {
_transform = transform;
changed = true;
}
});
if (changed) {
locationChanged();
}
}
glm::vec3 SpatiallyNestable::getLocalPosition() const {
@ -619,10 +694,16 @@ void SpatiallyNestable::setLocalPosition(const glm::vec3& position, bool tellPhy
qDebug() << "SpatiallyNestable::setLocalPosition -- position contains NaN";
return;
}
bool changed = false;
_transformLock.withWriteLock([&] {
if (_transform.getTranslation() != position) {
_transform.setTranslation(position);
changed = true;
}
});
if (changed) {
locationChanged(tellPhysics);
}
}
glm::quat SpatiallyNestable::getLocalOrientation() const {
@ -639,10 +720,16 @@ void SpatiallyNestable::setLocalOrientation(const glm::quat& orientation) {
qDebug() << "SpatiallyNestable::setLocalOrientation -- orientation contains NaN";
return;
}
bool changed = false;
_transformLock.withWriteLock([&] {
if (_transform.getRotation() != orientation) {
_transform.setRotation(orientation);
changed = true;
}
});
if (changed) {
locationChanged();
}
}
glm::vec3 SpatiallyNestable::getLocalVelocity() const {
@ -688,9 +775,14 @@ void SpatiallyNestable::setLocalScale(const glm::vec3& scale) {
qDebug() << "SpatiallyNestable::setLocalScale -- scale contains NaN";
return;
}
bool changed = false;
// TODO: scale
_transformLock.withWriteLock([&] {
if (_transform.getScale() != scale) {
_transform.setScale(scale);
changed = true;
}
});
dimensionsChanged();
}
@ -889,9 +981,15 @@ void SpatiallyNestable::setLocalTransformAndVelocities(
const Transform& localTransform,
const glm::vec3& localVelocity,
const glm::vec3& localAngularVelocity) {
bool changed = false;
// transform
_transformLock.withWriteLock([&] {
if (_transform != localTransform) {
_transform = localTransform;
changed = true;
}
});
// linear velocity
_velocityLock.withWriteLock([&] {
@ -901,5 +999,8 @@ void SpatiallyNestable::setLocalTransformAndVelocities(
_angularVelocityLock.withWriteLock([&] {
_angularVelocity = localAngularVelocity;
});
if (changed) {
locationChanged(false);
}
}

View file

@ -28,13 +28,14 @@ using SpatiallyNestableConstPointer = std::shared_ptr<const SpatiallyNestable>;
enum class NestableType {
Entity,
Avatar
Avatar,
Overlay
};
class SpatiallyNestable : public std::enable_shared_from_this<SpatiallyNestable> {
public:
SpatiallyNestable(NestableType nestableType, QUuid id);
virtual ~SpatiallyNestable() { }
virtual ~SpatiallyNestable();
virtual const QUuid getID() const;
virtual void setID(const QUuid& id);
@ -53,7 +54,9 @@ public:
// world frame
virtual const Transform getTransform(bool& success, int depth = 0) const;
virtual const Transform getTransform() const;
virtual void setTransform(const Transform& transform, bool& success);
virtual bool setTransform(const Transform& transform);
virtual Transform getParentTransform(bool& success, int depth = 0) const;
@ -68,6 +71,10 @@ public:
virtual void setOrientation(const glm::quat& orientation, bool& success, bool tellPhysics = true);
virtual void setOrientation(const glm::quat& orientation);
// these are here because some older code uses rotation rather than orientation
virtual const glm::quat getRotation() const { return getOrientation(); }
virtual void setRotation(glm::quat orientation) { setOrientation(orientation); }
virtual glm::vec3 getVelocity(bool& success) const;
virtual glm::vec3 getVelocity() const;
virtual void setVelocity(const glm::vec3& velocity, bool& success);
@ -91,6 +98,7 @@ public:
virtual glm::vec3 getScale() const;
virtual void setScale(const glm::vec3& scale);
virtual void setScale(float value);
// get world-frame values for a specific joint
virtual const Transform getTransform(int jointIndex, bool& success, int depth = 0) const;
@ -123,10 +131,10 @@ public:
// this object's frame
virtual const Transform getAbsoluteJointTransformInObjectFrame(int jointIndex) const;
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const = 0;
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const = 0;
virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) = 0;
virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) = 0;
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const { return glm::quat(); }
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const { return glm::vec3(); }
virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) { return false; }
virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) {return false; }
SpatiallyNestablePointer getThisPointer() const;
@ -170,6 +178,7 @@ protected:
virtual void locationChanged(bool tellPhysics = true); // called when a this object's location has changed
virtual void dimensionsChanged() { } // called when a this object's dimensions have changed
virtual void parentDeleted() { } // called on children of a deleted parent
// _queryAACube is used to decide where something lives in the octree
mutable AACube _queryAACube;

View file

@ -89,6 +89,10 @@ public:
return _rotation == other._rotation && _scale == other._scale && _translation == other._translation;
}
bool operator!=(const Transform& other) const {
return _rotation != other._rotation || _scale != other._scale || _translation != other._translation;
}
Transform& setIdentity();
const Vec3& getTranslation() const;

View file

@ -11,9 +11,303 @@
#include "SteamClient.h"
#include <atomic>
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QMimeData>
#include <QtCore/QString>
#include <QtCore/QUrl>
#include <QtGui/qevent.h>
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Woverloaded-virtual"
#endif
#include <steam/steam_api.h>
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
static const Ticket INVALID_TICKET = Ticket();
class SteamTicketRequests {
public:
SteamTicketRequests();
~SteamTicketRequests();
HAuthTicket startRequest(TicketRequestCallback callback);
void stopRequest(HAuthTicket authTicket);
void stopAll();
STEAM_CALLBACK(SteamTicketRequests, onGetAuthSessionTicketResponse,
GetAuthSessionTicketResponse_t, _getAuthSessionTicketResponse);
private:
struct PendingTicket {
HAuthTicket authTicket;
Ticket ticket;
TicketRequestCallback callback;
};
std::vector<PendingTicket> _pendingTickets;
};
SteamTicketRequests::SteamTicketRequests() :
_getAuthSessionTicketResponse(this, &SteamTicketRequests::onGetAuthSessionTicketResponse)
{
}
SteamTicketRequests::~SteamTicketRequests() {
stopAll();
}
HAuthTicket SteamTicketRequests::startRequest(TicketRequestCallback callback) {
static const uint32 MAX_TICKET_SIZE { 1024 };
uint32 ticketSize { 0 };
char ticket[MAX_TICKET_SIZE];
auto authTicket = SteamUser()->GetAuthSessionTicket(ticket, MAX_TICKET_SIZE, &ticketSize);
qDebug() << "Got Steam auth session ticket:" << authTicket;
if (authTicket == k_HAuthTicketInvalid) {
qWarning() << "Auth session ticket is invalid.";
callback(INVALID_TICKET);
} else {
PendingTicket pendingTicket{ authTicket, QByteArray(ticket, ticketSize).toHex(), callback };
_pendingTickets.push_back(pendingTicket);
}
return authTicket;
}
void SteamTicketRequests::stopRequest(HAuthTicket authTicket) {
auto it = std::find_if(_pendingTickets.begin(), _pendingTickets.end(), [&authTicket](const PendingTicket& pendingTicket) {
return pendingTicket.authTicket == authTicket;
});
if (it != _pendingTickets.end()) {
SteamUser()->CancelAuthTicket(it->authTicket);
it->callback(INVALID_TICKET);
_pendingTickets.erase(it);
}
}
void SteamTicketRequests::stopAll() {
auto steamUser = SteamUser();
if (steamUser) {
for (const auto& pendingTicket : _pendingTickets) {
steamUser->CancelAuthTicket(pendingTicket.authTicket);
pendingTicket.callback(INVALID_TICKET);
}
}
_pendingTickets.clear();
}
void SteamTicketRequests::onGetAuthSessionTicketResponse(GetAuthSessionTicketResponse_t* pCallback) {
auto authTicket = pCallback->m_hAuthTicket;
auto it = std::find_if(_pendingTickets.begin(), _pendingTickets.end(), [&authTicket](const PendingTicket& pendingTicket) {
return pendingTicket.authTicket == authTicket;
});
if (it != _pendingTickets.end()) {
if (pCallback->m_eResult == k_EResultOK) {
qDebug() << "Got steam callback, auth session ticket is valid. Send it." << authTicket;
it->callback(it->ticket);
} else {
qWarning() << "Steam auth session ticket callback encountered an error:" << pCallback->m_eResult;
it->callback(INVALID_TICKET);
}
_pendingTickets.erase(it);
} else {
qWarning() << "Could not find steam auth session ticket in list of pending tickets:" << authTicket;
}
}
const QString CONNECT_PREFIX = "--url \"";
const QString CONNECT_SUFFIX = "\"";
class SteamCallbackManager {
public:
SteamCallbackManager();
STEAM_CALLBACK(SteamCallbackManager, onGameRichPresenceJoinRequested,
GameRichPresenceJoinRequested_t, _gameRichPresenceJoinRequestedResponse);
STEAM_CALLBACK(SteamCallbackManager, onLobbyCreated,
LobbyCreated_t, _lobbyCreatedResponse);
STEAM_CALLBACK(SteamCallbackManager, onGameLobbyJoinRequested,
GameLobbyJoinRequested_t, _gameLobbyJoinRequestedResponse);
STEAM_CALLBACK(SteamCallbackManager, onLobbyEnter,
LobbyEnter_t, _lobbyEnterResponse);
SteamTicketRequests& getTicketRequests() { return _steamTicketRequests; }
private:
SteamTicketRequests _steamTicketRequests;
};
SteamCallbackManager::SteamCallbackManager() :
_gameRichPresenceJoinRequestedResponse(this, &SteamCallbackManager::onGameRichPresenceJoinRequested),
_lobbyCreatedResponse(this, &SteamCallbackManager::onLobbyCreated),
_gameLobbyJoinRequestedResponse(this, &SteamCallbackManager::onGameLobbyJoinRequested),
_lobbyEnterResponse(this, &SteamCallbackManager::onLobbyEnter)
{
}
void parseUrlAndGo(QString url) {
if (url.startsWith(CONNECT_PREFIX) && url.endsWith(CONNECT_SUFFIX)) {
url.remove(0, CONNECT_PREFIX.size());
url.remove(-CONNECT_SUFFIX.size(), CONNECT_SUFFIX.size());
}
qDebug() << "Joining Steam Friend at:" << url;
auto mimeData = new QMimeData();
mimeData->setUrls(QList<QUrl>() << QUrl(url));
auto event = new QDropEvent(QPointF(0, 0), Qt::MoveAction, mimeData, Qt::LeftButton, Qt::NoModifier);
QCoreApplication::postEvent(qApp, event);
}
void SteamCallbackManager::onGameRichPresenceJoinRequested(GameRichPresenceJoinRequested_t* pCallback) {
auto url = QString::fromLocal8Bit(pCallback->m_rgchConnect);
parseUrlAndGo(url);
}
void SteamCallbackManager::onLobbyCreated(LobbyCreated_t* pCallback) {
if (pCallback->m_eResult == k_EResultOK) {
qDebug() << "Inviting steam friends" << pCallback->m_ulSteamIDLobby;
auto url = SteamFriends()->GetFriendRichPresence(SteamUser()->GetSteamID(), "connect");
SteamMatchmaking()->SetLobbyData(pCallback->m_ulSteamIDLobby, "connect", url);
SteamFriends()->ActivateGameOverlayInviteDialog(pCallback->m_ulSteamIDLobby);
}
}
void SteamCallbackManager::onGameLobbyJoinRequested(GameLobbyJoinRequested_t* pCallback) {
qDebug() << "Joining Steam lobby" << pCallback->m_steamIDLobby.ConvertToUint64();
SteamMatchmaking()->JoinLobby(pCallback->m_steamIDLobby);
}
void SteamCallbackManager::onLobbyEnter(LobbyEnter_t* pCallback) {
if (pCallback->m_EChatRoomEnterResponse != k_EChatRoomEnterResponseSuccess) {
qWarning() << "An error occured while joing the Steam lobby:" << pCallback->m_EChatRoomEnterResponse;
return;
}
qDebug() << "Entered Steam lobby" << pCallback->m_ulSteamIDLobby;
if (SteamMatchmaking()->GetLobbyOwner(pCallback->m_ulSteamIDLobby) != SteamUser()->GetSteamID()) {
auto url = SteamMatchmaking()->GetLobbyData(pCallback->m_ulSteamIDLobby, "connect");
qDebug() << "Jumping to" << url;
parseUrlAndGo(url);
SteamMatchmaking()->LeaveLobby(pCallback->m_ulSteamIDLobby);
}
}
static std::atomic_bool initialized { false };
static SteamCallbackManager steamCallbackManager;
bool SteamClient::isRunning() {
return initialized;
}
bool SteamClient::init() {
if (SteamAPI_IsSteamRunning() && !initialized) {
initialized = SteamAPI_Init();
}
return initialized;
}
void SteamClient::shutdown() {
if (initialized) {
SteamAPI_Shutdown();
}
steamCallbackManager.getTicketRequests().stopAll();
}
void SteamClient::runCallbacks() {
if (!initialized) {
return;
}
auto steamPipe = SteamAPI_GetHSteamPipe();
if (!steamPipe) {
qDebug() << "Could not get SteamPipe";
return;
}
Steam_RunCallbacks(steamPipe, false);
}
void SteamClient::requestTicket(TicketRequestCallback callback) {
if (!initialized) {
if (SteamAPI_IsSteamRunning()) {
init();
} else {
qWarning() << "Steam is not running";
callback(INVALID_TICKET);
return;
}
}
if (!initialized) {
qDebug() << "Steam not initialized";
return;
}
steamCallbackManager.getTicketRequests().startRequest(callback);
}
void SteamClient::updateLocation(QString status, QUrl locationUrl) {
if (!initialized) {
return;
}
auto connectStr = locationUrl.isEmpty() ? "" : CONNECT_PREFIX + locationUrl.toString() + CONNECT_SUFFIX;
SteamFriends()->SetRichPresence("status", status.toLocal8Bit().data());
SteamFriends()->SetRichPresence("connect", connectStr.toLocal8Bit().data());
}
void SteamClient::openInviteOverlay() {
if (!initialized) {
return;
}
qDebug() << "Creating Steam lobby";
static const int MAX_LOBBY_SIZE = 20;
SteamMatchmaking()->CreateLobby(k_ELobbyTypePrivate, MAX_LOBBY_SIZE);
}
void SteamClient::joinLobby(QString lobbyIdStr) {
if (!initialized) {
if (SteamAPI_IsSteamRunning()) {
init();
} else {
qWarning() << "Steam is not running";
return;
}
}
qDebug() << "Trying to join Steam lobby:" << lobbyIdStr;
CSteamID lobbyId(lobbyIdStr.toULongLong());
SteamMatchmaking()->JoinLobby(lobbyId);
}

View file

@ -13,8 +13,43 @@
#ifndef hifi_SteamClient_h
#define hifi_SteamClient_h
class SteamClient {
#include <functional>
#include <QtCore/QObject>
#include <QtCore/QByteArray>
using Ticket = QByteArray;
using TicketRequestCallback = std::function<void(Ticket)>;
class QUrl;
class SteamClient {
public:
static bool isRunning();
static bool init();
static void shutdown();
static void runCallbacks();
static void requestTicket(TicketRequestCallback callback);
static void updateLocation(QString status, QUrl locationUrl);
static void openInviteOverlay();
static void joinLobby(QString lobbyId);
};
class SteamScriptingInterface : public QObject {
Q_OBJECT
Q_PROPERTY(bool isRunning READ isRunning)
public:
SteamScriptingInterface(QObject* parent) : QObject(parent) {}
public slots:
bool isRunning() const { return SteamClient::isRunning(); }
void openInviteOverlay() const { SteamClient::openInviteOverlay(); }
};

View file

@ -161,7 +161,10 @@ static bool isBadPose(vr::HmdMatrix34_t* mat) {
bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
handleOpenVrEvents();
if (openVrQuitRequested()) {
QMetaObject::invokeMethod(qApp, "quit");
return false;
}
double displayFrequency = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_DisplayFrequency_Float);
double frameDuration = 1.f / displayFrequency;
double vsyncToPhotons = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float);

View file

@ -34,6 +34,11 @@ using Lock = std::unique_lock<Mutex>;
static int refCount { 0 };
static Mutex mutex;
static vr::IVRSystem* activeHmd { nullptr };
static bool _openVrQuitRequested { false };
bool openVrQuitRequested() {
return _openVrQuitRequested;
}
static const uint32_t RELEASE_OPENVR_HMD_DELAY_MS = 5000;
@ -99,6 +104,7 @@ void releaseOpenVrSystem() {
qCDebug(displayplugins) << "OpenVR: zero refcount, deallocate VR system";
#endif
vr::VR_Shutdown();
_openVrQuitRequested = false;
activeHmd = nullptr;
}
}
@ -257,8 +263,8 @@ void handleOpenVrEvents() {
while (activeHmd->PollNextEvent(&event, sizeof(event))) {
switch (event.eventType) {
case vr::VREvent_Quit:
_openVrQuitRequested = true;
activeHmd->AcknowledgeQuit_Exiting();
QMetaObject::invokeMethod(qApp, "quit");
break;
case vr::VREvent_KeyboardDone:

View file

@ -214,6 +214,10 @@ void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch&
void ViveControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
handleOpenVrEvents();
if (openVrQuitRequested()) {
deactivate();
return;
}
// because update mutates the internal state we need to lock
userInputMapper->withLock([&, this]() {

View file

@ -15,6 +15,7 @@ Script.load("system/users.js");
Script.load("system/mute.js");
Script.load("system/goto.js");
Script.load("system/hmd.js");
Script.load("system/steam.js");
Script.load("system/marketplace.js");
Script.load("system/edit.js");
Script.load("system/mod.js");

View file

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 200.1" style="enable-background:new 0 0 50 200.1;" xml:space="preserve">
<style type="text/css">
.st0{fill:#414042;}
.st1{fill:#FFFFFF;}
.st2{fill:#1E1E1E;}
.st3{fill:#333333;}
</style>
<g id="Layer_2">
<g>
<g>
<path class="st0" d="M50.1,146.1c0,2.2-1.8,4-4,4h-42c-2.2,0-4-1.8-4-4v-42c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V146.1z"/>
</g>
</g>
<g>
<g>
<path class="st0" d="M50,196.1c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4v-42c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V196.1z"/>
</g>
</g>
<g>
<g>
<path class="st1" d="M50,46c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4V4c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V46z"/>
</g>
</g>
<g>
<path class="st2" d="M50,96.1c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4v-42c0-2.2,1.8-4,4-4h42c2.2,0,4,1.8,4,4V96.1z"/>
</g>
</g>
<path class="st3" d="M14.32,21.31c0.16,0.05,0.25,0.08,0.33,0.11c1.27,0.53,2.55,1.07,3.82,1.59c0.19,0.08,0.28,0.19,0.34,0.38
c0.49,1.6,1.94,2.57,3.55,2.37c1.59-0.19,2.8-1.51,2.89-3.16c0.01-0.22,0.08-0.35,0.25-0.47c1.17-0.83,2.33-1.67,3.5-2.51
c0.11-0.08,0.27-0.14,0.41-0.14c2.31-0.06,4.14-1.88,4.23-4.29c0.09-2.34-1.64-4.19-3.73-4.49c-2.59-0.36-4.9,1.56-5,4.17
c-0.01,0.14-0.05,0.3-0.13,0.41c-0.82,1.21-1.66,2.42-2.48,3.64c-0.11,0.16-0.22,0.22-0.41,0.23c-0.56,0.02-1.1,0.17-1.59,0.45
c-0.1,0.06-0.27,0.08-0.37,0.03c-1.91-0.79-3.81-1.59-5.72-2.37c-0.2-0.08-0.27-0.18-0.25-0.4c0.22-2.12,0.95-4.04,2.22-5.74
c1.96-2.62,4.57-4.26,7.83-4.6c5.01-0.53,8.91,1.43,11.48,5.76c3.52,5.94,0.91,13.76-5.42,16.52c-6.07,2.65-13.21-0.43-15.45-6.66
C14.51,21.89,14.43,21.64,14.32,21.31z"/>
<path class="st3" d="M29.28,18c-1.6,0-2.92-1.33-2.91-2.93c0.01-1.61,1.33-2.92,2.93-2.92c1.59,0.01,2.88,1.31,2.88,2.92
C32.18,16.69,30.88,18,29.28,18z M29.27,17.28c1.21,0,2.21-0.99,2.21-2.2c0.01-1.22-0.98-2.22-2.18-2.23c-1.21,0-2.2,0.98-2.21,2.2
C27.08,16.27,28.07,17.28,29.27,17.28z"/>
<path class="st3" d="M19.8,23.57c0.47,0.19,0.89,0.37,1.3,0.54c1.01,0.41,2.06,0,2.45-0.97c0.39-0.95-0.05-1.99-1.04-2.41
c-0.44-0.19-0.88-0.37-1.35-0.57c1.08-0.49,2.47,0.1,3.02,1.26c0.57,1.2,0.05,2.68-1.14,3.26C21.84,25.27,20.4,24.8,19.8,23.57z"/>
<g>
<path class="st3" d="M10.49,42.3v-6.38h1.24v6.38H10.49z"/>
<path class="st3" d="M14.53,38.2v4.1h-1.24V35.9h0.96l3.33,4.19v-4.19h1.24v6.38h-1.01L14.53,38.2z"/>
<path class="st3" d="M21.02,35.9l1.73,4.83l1.71-4.83h1.3l-2.49,6.39h-1.04l-2.51-6.39H21.02z"/>
<path class="st3" d="M26.67,42.3v-6.38h1.24v6.38H26.67z"/>
<path class="st3" d="M34.25,36.99h-2.03v5.3h-1.24v-5.3h-2.04V35.9h5.32V36.99z"/>
<path class="st3" d="M39.71,41.21v1.09h-4.44V35.9h4.36v1.09h-3.11v1.54h2.69v1.01h-2.69v1.67H39.71z"/>
</g>
<path class="st1" d="M14.32,71.31c0.16,0.05,0.25,0.08,0.33,0.11c1.27,0.53,2.55,1.07,3.82,1.59c0.19,0.08,0.28,0.19,0.34,0.38
c0.49,1.6,1.94,2.57,3.55,2.37c1.59-0.19,2.8-1.51,2.89-3.16c0.01-0.22,0.08-0.35,0.25-0.47c1.17-0.83,2.33-1.67,3.5-2.51
c0.11-0.08,0.27-0.14,0.41-0.14c2.31-0.06,4.14-1.88,4.23-4.29c0.09-2.34-1.64-4.19-3.73-4.49c-2.59-0.36-4.9,1.56-5,4.17
c-0.01,0.14-0.05,0.3-0.13,0.41c-0.82,1.21-1.66,2.42-2.48,3.64c-0.11,0.16-0.22,0.22-0.41,0.23c-0.56,0.02-1.1,0.17-1.59,0.45
c-0.1,0.06-0.27,0.08-0.37,0.03c-1.91-0.79-3.81-1.59-5.72-2.37c-0.2-0.08-0.27-0.18-0.25-0.4c0.22-2.12,0.95-4.04,2.22-5.74
c1.96-2.62,4.57-4.26,7.83-4.6c5.01-0.53,8.91,1.43,11.48,5.76c3.52,5.94,0.91,13.76-5.42,16.52c-6.07,2.65-13.21-0.43-15.45-6.66
C14.51,71.9,14.43,71.65,14.32,71.31z"/>
<path class="st1" d="M29.28,68c-1.6,0-2.92-1.33-2.91-2.93c0.01-1.61,1.33-2.92,2.93-2.92c1.59,0.01,2.88,1.31,2.88,2.92
C32.18,66.69,30.88,68,29.28,68z M29.27,67.29c1.21,0,2.21-0.99,2.21-2.2c0.01-1.22-0.98-2.22-2.18-2.23c-1.21,0-2.2,0.98-2.21,2.2
C27.08,66.28,28.07,67.28,29.27,67.29z"/>
<path class="st1" d="M19.8,73.58c0.47,0.19,0.89,0.37,1.3,0.54c1.01,0.41,2.06,0,2.45-0.97c0.39-0.95-0.05-1.99-1.04-2.41
c-0.44-0.19-0.88-0.37-1.35-0.57c1.08-0.49,2.47,0.1,3.02,1.26c0.57,1.2,0.05,2.68-1.14,3.26C21.84,75.27,20.4,74.8,19.8,73.58z"/>
<g>
<path class="st1" d="M10.49,92.3v-6.38h1.24v6.38H10.49z"/>
<path class="st1" d="M14.53,88.2v4.1h-1.24v-6.39h0.96l3.33,4.19v-4.19h1.24v6.38h-1.01L14.53,88.2z"/>
<path class="st1" d="M21.02,85.91l1.73,4.83l1.71-4.83h1.3l-2.49,6.39h-1.04l-2.51-6.39H21.02z"/>
<path class="st1" d="M26.67,92.3v-6.38h1.24v6.38H26.67z"/>
<path class="st1" d="M34.25,87h-2.03v5.3h-1.24V87h-2.04v-1.09h5.32V87z"/>
<path class="st1" d="M39.71,91.21v1.09h-4.44v-6.39h4.36V87h-3.11v1.54h2.69v1.01h-2.69v1.67H39.71z"/>
</g>
<path class="st1" d="M14.32,121.41c0.16,0.05,0.25,0.08,0.33,0.11c1.27,0.53,2.55,1.07,3.82,1.59c0.19,0.08,0.28,0.19,0.34,0.38
c0.49,1.6,1.94,2.57,3.55,2.37c1.59-0.19,2.8-1.51,2.89-3.16c0.01-0.22,0.08-0.35,0.25-0.47c1.17-0.83,2.33-1.67,3.5-2.51
c0.11-0.08,0.27-0.14,0.41-0.14c2.31-0.06,4.14-1.88,4.23-4.29c0.09-2.34-1.64-4.19-3.73-4.49c-2.59-0.36-4.9,1.56-5,4.17
c-0.01,0.14-0.05,0.3-0.13,0.41c-0.82,1.21-1.66,2.42-2.48,3.64c-0.11,0.16-0.22,0.22-0.41,0.23c-0.56,0.02-1.1,0.17-1.59,0.45
c-0.1,0.06-0.27,0.08-0.37,0.03c-1.91-0.79-3.81-1.59-5.72-2.37c-0.2-0.08-0.27-0.18-0.25-0.4c0.22-2.12,0.95-4.04,2.22-5.74
c1.96-2.62,4.57-4.26,7.83-4.6c5.01-0.53,8.91,1.43,11.48,5.76c3.52,5.94,0.91,13.76-5.42,16.52c-6.07,2.65-13.21-0.43-15.45-6.66
C14.51,122,14.43,121.75,14.32,121.41z"/>
<path class="st1" d="M29.28,118.1c-1.6,0-2.92-1.33-2.91-2.93c0.01-1.61,1.33-2.92,2.93-2.92c1.59,0.01,2.88,1.31,2.88,2.92
C32.18,116.79,30.88,118.1,29.28,118.1z M29.27,117.39c1.21,0,2.21-0.99,2.21-2.2c0.01-1.22-0.98-2.22-2.18-2.23
c-1.21,0-2.2,0.98-2.21,2.2C27.08,116.38,28.07,117.38,29.27,117.39z"/>
<path class="st1" d="M19.8,123.68c0.47,0.19,0.89,0.37,1.3,0.54c1.01,0.41,2.06,0,2.45-0.97c0.39-0.95-0.05-1.99-1.04-2.41
c-0.44-0.19-0.88-0.37-1.35-0.57c1.08-0.49,2.47,0.1,3.02,1.26c0.57,1.2,0.05,2.68-1.14,3.26C21.84,125.37,20.4,124.9,19.8,123.68z"
/>
<g>
<path class="st1" d="M10.49,142.4v-6.38h1.24v6.38H10.49z"/>
<path class="st1" d="M14.53,138.3v4.1h-1.24v-6.39h0.96l3.33,4.19v-4.19h1.24v6.38h-1.01L14.53,138.3z"/>
<path class="st1" d="M21.02,136.01l1.73,4.83l1.71-4.83h1.3l-2.49,6.39h-1.04l-2.51-6.39H21.02z"/>
<path class="st1" d="M26.67,142.4v-6.38h1.24v6.38H26.67z"/>
<path class="st1" d="M34.25,137.1h-2.03v5.3h-1.24v-5.3h-2.04v-1.09h5.32V137.1z"/>
<path class="st1" d="M39.71,141.31v1.09h-4.44v-6.39h4.36v1.09h-3.11v1.54h2.69v1.01h-2.69v1.67H39.71z"/>
</g>
<path class="st1" d="M14.32,171.51c0.16,0.05,0.25,0.08,0.33,0.11c1.27,0.53,2.55,1.07,3.82,1.59c0.19,0.08,0.28,0.19,0.34,0.38
c0.49,1.6,1.94,2.57,3.55,2.37c1.59-0.19,2.8-1.51,2.89-3.16c0.01-0.22,0.08-0.35,0.25-0.47c1.17-0.83,2.33-1.67,3.5-2.51
c0.11-0.08,0.27-0.14,0.41-0.14c2.31-0.06,4.14-1.88,4.23-4.29c0.09-2.34-1.64-4.19-3.73-4.49c-2.59-0.36-4.9,1.56-5,4.17
c-0.01,0.14-0.05,0.3-0.13,0.41c-0.82,1.21-1.66,2.42-2.48,3.64c-0.11,0.16-0.22,0.22-0.41,0.23c-0.56,0.02-1.1,0.17-1.59,0.45
c-0.1,0.06-0.27,0.08-0.37,0.03c-1.91-0.79-3.81-1.59-5.72-2.37c-0.2-0.08-0.27-0.18-0.25-0.4c0.22-2.12,0.95-4.04,2.22-5.74
c1.96-2.62,4.57-4.26,7.83-4.6c5.01-0.53,8.91,1.43,11.48,5.76c3.52,5.94,0.91,13.76-5.42,16.52c-6.07,2.65-13.21-0.43-15.45-6.66
C14.51,172.1,14.43,171.85,14.32,171.51z"/>
<path class="st1" d="M29.28,168.2c-1.6,0-2.92-1.33-2.91-2.93c0.01-1.61,1.33-2.92,2.93-2.92c1.59,0.01,2.88,1.31,2.88,2.92
C32.18,166.89,30.88,168.2,29.28,168.2z M29.27,167.49c1.21,0,2.21-0.99,2.21-2.2c0.01-1.22-0.98-2.22-2.18-2.23
c-1.21,0-2.2,0.98-2.21,2.2C27.08,166.48,28.07,167.48,29.27,167.49z"/>
<path class="st1" d="M19.8,173.78c0.47,0.19,0.89,0.37,1.3,0.54c1.01,0.41,2.06,0,2.45-0.97c0.39-0.95-0.05-1.99-1.04-2.41
c-0.44-0.19-0.88-0.37-1.35-0.57c1.08-0.49,2.47,0.1,3.02,1.26c0.57,1.2,0.05,2.68-1.14,3.26C21.84,175.47,20.4,175,19.8,173.78z"/>
<g>
<path class="st1" d="M10.49,192.5v-6.38h1.24v6.38H10.49z"/>
<path class="st1" d="M14.53,188.4v4.1h-1.24v-6.39h0.96l3.33,4.19v-4.19h1.24v6.38h-1.01L14.53,188.4z"/>
<path class="st1" d="M21.02,186.11l1.73,4.83l1.71-4.83h1.3l-2.49,6.39h-1.04l-2.51-6.39H21.02z"/>
<path class="st1" d="M26.67,192.5v-6.38h1.24v6.38H26.67z"/>
<path class="st1" d="M34.25,187.2h-2.03v5.3h-1.24v-5.3h-2.04v-1.09h5.32V187.2z"/>
<path class="st1" d="M39.71,191.41v1.09h-4.44v-6.39h4.36v1.09h-3.11v1.54h2.69v1.01h-2.69v1.67H39.71z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.2 KiB

View file

@ -22,7 +22,7 @@ var button = toolbar.addButton({
imageURL: buttonImageURL(),
visible: true,
buttonState: 1,
defaultState: 2,
defaultState: 1,
hoverState: 3,
alpha: 0.9
});

31
scripts/system/steam.js Normal file
View file

@ -0,0 +1,31 @@
//
// steam.js
// scripts/system/
//
// Created by Clement on 7/28/16
// 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
//
var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system");
var steamInviteButton = toolBar.addButton({
objectName: "steamInvite",
imageURL: Script.resolvePath("assets/images/tools/steam-invite.svg"),
visible: Steam.isRunning,
buttonState: 1,
defaultState: 1,
hoverState: 3,
alpha: 0.9
});
steamInviteButton.clicked.connect(function(){
Steam.openInviteOverlay();
});
Script.scriptEnding.connect(function () {
toolBar.removeButton("steamInvite");
});