diff --git a/examples/dialTone.js b/examples/dialTone.js
new file mode 100644
index 0000000000..0748d0ba94
--- /dev/null
+++ b/examples/dialTone.js
@@ -0,0 +1,23 @@
+//
+// dialTone.js
+// examples
+//
+// Created by Stephen Birarda on 06/08/15.
+// 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
+//
+
+// setup the local sound we're going to use
+var connectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/short1.wav");
+
+// setup the options needed for that sound
+var connectSoundOptions = {
+ localOnly: true
+}
+
+// play the sound locally once we get the first audio packet from a mixer
+Audio.receivedFirstPacket.connect(function(){
+ Audio.playSound(connectSound, connectSoundOptions);
+});
diff --git a/examples/example/dynamicLandscape.js b/examples/example/dynamicLandscape.js
new file mode 100644
index 0000000000..ad247963fd
--- /dev/null
+++ b/examples/example/dynamicLandscape.js
@@ -0,0 +1,89 @@
+
+// dynamicLandscape.js
+// examples
+//
+// Created by Eric Levin on June 8
+// Copyright 2015 High Fidelity, Inc.
+//
+// Meditative ocean landscape
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+
+HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
+Script.include(HIFI_PUBLIC_BUCKET + 'scripts/utilities.js')
+
+var NUM_ROWS = 10;
+var CUBE_SIZE = 1;
+var cubes = [];
+var cubesSettings = [];
+var time = 0;
+
+var OMEGA = 2.0 * Math.PI/8;
+var RANGE = CUBE_SIZE/2;
+
+var center = Vec3.sum(MyAvatar.position, Vec3.multiply(CUBE_SIZE* 10, Quat.getFront(Camera.getOrientation())));
+
+
+for (var x = 0, rowIndex = 0; x < NUM_ROWS * CUBE_SIZE; x += CUBE_SIZE, rowIndex++) {
+ for (var z = 0, columnIndex = 0; z < NUM_ROWS * CUBE_SIZE; z += CUBE_SIZE, columnIndex++) {
+
+ var baseHeight = map( columnIndex + 1, 1, NUM_ROWS, -CUBE_SIZE * 2, -CUBE_SIZE);
+ var relativePosition = {
+ x: x,
+ y: baseHeight,
+ z: z
+ };
+ var position = Vec3.sum(center, relativePosition);
+ cubes.push(Entities.addEntity({
+ type: 'Box',
+ position: position,
+ dimensions: {
+ x: CUBE_SIZE,
+ y: CUBE_SIZE,
+ z: CUBE_SIZE
+ }
+ }));
+
+ var phase = map( (columnIndex + 1) * (rowIndex + 1), 2, NUM_ROWS * NUM_ROWS, Math.PI * 2, Math.PI * 4);
+ cubesSettings.push({
+ baseHeight: center.y + baseHeight,
+ phase: phase
+ })
+ }
+}
+
+function update(deleteTime) {
+ time += deleteTime;
+ for (var i = 0; i < cubes.length; i++) {
+ var phase = cubesSettings[i].phase;
+ var props = Entities.getEntityProperties(cubes[i]);
+ var newHeight = Math.sin(time * OMEGA + phase) / 2.0;
+ var hue = map(newHeight, -.5, .5, 0.5, 0.7);
+ var light = map(newHeight, -.5, .5, 0.4, 0.6)
+ newHeight = cubesSettings[i].baseHeight + (newHeight * RANGE);
+ var newVelocityY = Math.cos(time * OMEGA + phase) / 2.0 * RANGE * OMEGA;
+
+ var newPosition = props.position;
+ var newVelocity = props.velocity;
+
+ newPosition.y = newHeight;
+ newVelocity = newVelocityY;
+ Entities.editEntity( cubes[i], {
+ position: newPosition,
+ velocity: props.velocity,
+ color: hslToRgb({hue: hue, sat: 0.7, light: light})
+ });
+ }
+}
+
+function cleanup() {
+ cubes.forEach(function(cube) {
+ Entities.deleteEntity(cube);
+ })
+}
+
+Script.update.connect(update);
+Script.scriptEnding.connect(cleanup)
+
+
diff --git a/examples/utilities.js b/examples/utilities.js
new file mode 100644
index 0000000000..3844e23e14
--- /dev/null
+++ b/examples/utilities.js
@@ -0,0 +1,56 @@
+// utilities.js
+// examples
+//
+// Created by Eric Levin on June 8
+// Copyright 2015 High Fidelity, Inc.
+//
+// Common utilities
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+
+function hslToRgb(hslColor) {
+ var h = hslColor.hue;
+ var s = hslColor.sat;
+ var l = hslColor.light;
+ var r, g, b;
+
+ if (s == 0) {
+ r = g = b = l; // achromatic
+ } else {
+ var hue2rgb = function hue2rgb(p, q, t) {
+ if (t < 0) t += 1;
+ if (t > 1) t -= 1;
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
+ if (t < 1 / 2) return q;
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
+ return p;
+ }
+
+ var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ var p = 2 * l - q;
+ r = hue2rgb(p, q, h + 1 / 3);
+ g = hue2rgb(p, q, h);
+ b = hue2rgb(p, q, h - 1 / 3);
+ }
+
+ return {
+ red: Math.round(r * 255),
+ green: Math.round(g * 255),
+ blue: Math.round(b * 255)
+ };
+
+}
+
+function map(value, min1, max1, min2, max2) {
+ return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
+}
+
+function randFloat(low, high) {
+ return low + Math.random() * (high - low);
+}
+
+
+function randInt(low, high) {
+ return Math.floor(randFloat(low, high));
+}
\ No newline at end of file
diff --git a/interface/resources/images/hifi-logo-blackish.svg b/interface/resources/images/hifi-logo-blackish.svg
new file mode 100644
index 0000000000..60bfb3d418
--- /dev/null
+++ b/interface/resources/images/hifi-logo-blackish.svg
@@ -0,0 +1,123 @@
+
+
+
+
\ No newline at end of file
diff --git a/interface/resources/images/login-close.svg b/interface/resources/images/login-close.svg
new file mode 100644
index 0000000000..2fb10c241b
--- /dev/null
+++ b/interface/resources/images/login-close.svg
@@ -0,0 +1,55 @@
+
+
\ No newline at end of file
diff --git a/interface/resources/images/login-password.svg b/interface/resources/images/login-password.svg
new file mode 100644
index 0000000000..98d86f6d8a
--- /dev/null
+++ b/interface/resources/images/login-password.svg
@@ -0,0 +1,58 @@
+
+
\ No newline at end of file
diff --git a/interface/resources/images/login-username.svg b/interface/resources/images/login-username.svg
new file mode 100644
index 0000000000..a40dd91cf7
--- /dev/null
+++ b/interface/resources/images/login-username.svg
@@ -0,0 +1,51 @@
+
+
\ No newline at end of file
diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml
index 91e05d020d..3377b20d87 100644
--- a/interface/resources/qml/AddressBarDialog.qml
+++ b/interface/resources/qml/AddressBarDialog.qml
@@ -9,18 +9,16 @@
//
import Hifi 1.0
-import QtQuick 2.3
-import QtQuick.Controls 1.2
+import QtQuick 2.4
import "controls"
import "styles"
-Item {
+DialogContainer {
id: root
HifiConstants { id: hifi }
objectName: "AddressBarDialog"
- property int animationDuration: hifi.effects.fadeInDuration
property bool destroyOnInvisible: false
property real scale: 1.25 // Make this dialog a little larger than normal
@@ -101,47 +99,16 @@ Item {
}
}
- // The UI enables an object, rather than manipulating its visibility, so that we can do animations in both directions.
- // Because visibility and enabled are booleans, they cannot be animated. So when enabled is changed, we modify a property
- // that can be animated, like scale or opacity, and then when the target animation value is reached, we can modify the
- // visibility.
- enabled: false
- opacity: 0.0
-
onEnabledChanged: {
- opacity = enabled ? 1.0 : 0.0
if (enabled) {
- addressLine.forceActiveFocus();
+ addressLine.forceActiveFocus()
}
}
- Behavior on opacity {
- // Animate opacity.
- NumberAnimation {
- duration: animationDuration
- easing.type: Easing.OutCubic
- }
- }
-
- onOpacityChanged: {
- // Once we're transparent, disable the dialog's visibility.
- visible = (opacity != 0.0)
- }
-
onVisibleChanged: {
if (!visible) {
- reset()
-
- // Some dialogs should be destroyed when they become invisible.
- if (destroyOnInvisible) {
- destroy()
- }
+ addressLine.text = ""
}
-
- }
-
- function reset() {
- addressLine.text = ""
}
function toggleOrGo() {
@@ -152,21 +119,18 @@ Item {
}
}
- Keys.onEscapePressed: {
- enabled = false
- }
-
Keys.onPressed: {
- switch(event.key) {
- case Qt.Key_W:
- if (event.modifiers == Qt.ControlModifier) {
- event.accepted = true
- enabled = false
- }
+ switch (event.key) {
+ case Qt.Key_Escape:
+ case Qt.Key_Back:
+ enabled = false
+ event.accepted = true
+ break
+ case Qt.Key_Enter:
+ case Qt.Key_Return:
+ toggleOrGo()
+ event.accepted = true
break
}
}
-
- Keys.onReturnPressed: toggleOrGo()
- Keys.onEnterPressed: toggleOrGo()
}
diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml
index 55a0a6a461..947bf739fc 100644
--- a/interface/resources/qml/Browser.qml
+++ b/interface/resources/qml/Browser.qml
@@ -4,7 +4,7 @@ import QtWebKit 3.0
import "controls"
import "styles"
-Dialog {
+VrDialog {
id: root
HifiConstants { id: hifi }
title: "Browser"
diff --git a/interface/resources/qml/ErrorDialog.qml b/interface/resources/qml/ErrorDialog.qml
index c0f8132f14..76d9111d97 100644
--- a/interface/resources/qml/ErrorDialog.qml
+++ b/interface/resources/qml/ErrorDialog.qml
@@ -8,27 +8,22 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-import Hifi 1.0 as Hifi
-import QtQuick 2.3
-import QtQuick.Controls 1.2
-import QtQuick.Dialogs 1.2
+import Hifi 1.0
+import QtQuick 2.4
import "controls"
import "styles"
-Item {
+DialogContainer {
id: root
HifiConstants { id: hifi }
- property int animationDuration: hifi.effects.fadeInDuration
- property bool destroyOnInvisible: true
-
Component.onCompleted: {
enabled = true
}
onParentChanged: {
if (visible && enabled) {
- forceActiveFocus();
+ forceActiveFocus()
}
}
@@ -38,7 +33,7 @@ Item {
x: parent ? parent.width / 2 - width / 2 : 0
y: parent ? parent.height / 2 - height / 2 : 0
- Hifi.ErrorDialog {
+ ErrorDialog {
id: content
implicitWidth: box.width
@@ -69,7 +64,7 @@ Item {
Text {
id: messageText
- font.pointSize: 10
+ font.pixelSize: hifi.fonts.pixelSize * 0.6
font.weight: Font.Bold
anchors {
@@ -91,55 +86,17 @@ Item {
}
MouseArea {
anchors.fill: parent
+ cursorShape: "PointingHandCursor"
onClicked: {
- content.accept();
+ content.accept()
}
}
}
}
}
- // The UI enables an object, rather than manipulating its visibility, so that we can do animations in both directions.
- // Because visibility and enabled are booleans, they cannot be animated. So when enabled is changed, we modify a property
- // that can be animated, like scale or opacity, and then when the target animation value is reached, we can modify the
- // visibility.
- enabled: false
- opacity: 0.0
-
- onEnabledChanged: {
- opacity = enabled ? 1.0 : 0.0
- }
-
- Behavior on opacity {
- // Animate opacity.
- NumberAnimation {
- duration: animationDuration
- easing.type: Easing.OutCubic
- }
- }
-
- onOpacityChanged: {
- // Once we're transparent, disable the dialog's visibility.
- visible = (opacity != 0.0)
- }
-
- onVisibleChanged: {
- if (!visible) {
- // Some dialogs should be destroyed when they become invisible.
- if (destroyOnInvisible) {
- destroy()
- }
- }
- }
-
Keys.onPressed: {
- if (event.modifiers === Qt.ControlModifier)
- switch (event.key) {
- case Qt.Key_W:
- event.accepted = true
- content.accept()
- break
- } else switch (event.key) {
+ switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back:
case Qt.Key_Enter:
diff --git a/interface/resources/qml/InfoView.qml b/interface/resources/qml/InfoView.qml
index 6b49e6f0c7..012f04f1fd 100644
--- a/interface/resources/qml/InfoView.qml
+++ b/interface/resources/qml/InfoView.qml
@@ -5,7 +5,7 @@ import QtQuick.Controls.Styles 1.3
import QtWebKit 3.0
import "controls"
-Dialog {
+VrDialog {
id: root
width: 800
height: 800
diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml
index 5653dfc7a1..8d5267f7f8 100644
--- a/interface/resources/qml/LoginDialog.qml
+++ b/interface/resources/qml/LoginDialog.qml
@@ -1,78 +1,173 @@
+//
+// LoginDialog.qml
+//
+// Created by David Rowe on 3 Jun 2015
+// 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.3
-import QtQuick.Controls.Styles 1.3
+import QtQuick 2.4
import "controls"
import "styles"
-Dialog {
+DialogContainer {
+ id: root
HifiConstants { id: hifi }
- title: "Login"
+
objectName: "LoginDialog"
- height: 512
- width: 384
- onVisibleChanged: {
- if (!visible) {
- reset()
- }
- }
+ property bool destroyOnInvisible: false
- onEnabledChanged: {
- if (enabled) {
- username.forceActiveFocus();
- }
- }
+ implicitWidth: loginDialog.implicitWidth
+ implicitHeight: loginDialog.implicitHeight
- function reset() {
- username.text = ""
- password.text = ""
- loginDialog.statusText = ""
- }
+ x: parent ? parent.width / 2 - width / 2 : 0
+ y: parent ? parent.height / 2 - height / 2 : 0
+ property int maximumX: parent ? parent.width - width : 0
+ property int maximumY: parent ? parent.height - height : 0
LoginDialog {
id: loginDialog
- anchors.fill: parent
- anchors.margins: parent.margins
- anchors.topMargin: parent.topMargin
- Column {
- anchors.topMargin: 8
- anchors.right: parent.right
- anchors.rightMargin: 0
- anchors.left: parent.left
- anchors.top: parent.top
- spacing: 8
- Image {
- height: 64
- anchors.horizontalCenter: parent.horizontalCenter
- width: 64
- source: "../images/hifi-logo.svg"
+ 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
+ property int maximumX: parent ? parent.width - width : 0
+ property int maximumY: parent ? parent.height - height : 0
+
+ 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
+
+ MouseArea {
+ width: parent.width
+ height: parent.height
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ verticalCenter: parent.verticalCenter
+ }
+ drag {
+ target: root
+ minimumX: 0
+ minimumY: 0
+ maximumX: root.parent ? root.maximumX : 0
+ maximumY: root.parent ? root.maximumY : 0
+ }
+ }
+ }
+
+ Image {
+ id: closeIcon
+ source: "../images/login-close.svg"
+ width: 20
+ height: 20
+ anchors {
+ top: backgroundRectangle.top
+ right: backgroundRectangle.right
+ topMargin: loginDialog.closeMargin
+ rightMargin: loginDialog.closeMargin
}
- Border {
- width: 304
- height: 64
- anchors.horizontalCenter: parent.horizontalCenter
+ MouseArea {
+ anchors.fill: parent
+ cursorShape: "PointingHandCursor"
+ onClicked: {
+ root.enabled = false
+ }
+ }
+ }
+
+ 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
+ anchors {
+ verticalCenter: parent.verticalCenter
+ left: parent.left
+ leftMargin: loginDialog.inputHeight / 4
+ }
+ }
+
TextInput {
id: username
- anchors.fill: parent
- helperText: "Username or Email"
- anchors.margins: 8
+ anchors {
+ fill: parent
+ leftMargin: loginDialog.inputHeight
+ rightMargin: loginDialog.inputHeight / 2
+ }
+
+ helperText: "username or email"
+ color: hifi.colors.text
+
KeyNavigation.tab: password
KeyNavigation.backtab: password
}
}
- Border {
- width: 304
- height: 64
- anchors.horizontalCenter: parent.horizontalCenter
+ 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
+ anchors {
+ verticalCenter: parent.verticalCenter
+ left: parent.left
+ leftMargin: loginDialog.inputHeight / 4
+ }
+ }
+
TextInput {
id: password
- anchors.fill: parent
+ anchors {
+ fill: parent
+ leftMargin: loginDialog.inputHeight
+ rightMargin: loginDialog.inputHeight / 2
+ }
+
+ helperText: "password"
echoMode: TextInput.Password
- helperText: "Password"
- anchors.margins: 8
+ color: hifi.colors.text
+
KeyNavigation.tab: username
KeyNavigation.backtab: username
onFocusChanged: {
@@ -83,102 +178,176 @@ Dialog {
}
}
- Text {
- anchors.horizontalCenter: parent.horizontalCenter
- textFormat: Text.StyledText
- width: parent.width
- height: 96
- wrapMode: Text.WordWrap
- verticalAlignment: Text.AlignVCenter
- horizontalAlignment: Text.AlignHCenter
- text: loginDialog.statusText
- }
- }
+ Item {
+ width: loginDialog.inputWidth
+ height: loginDialog.inputHeight / 2
- Column {
- anchors.bottomMargin: 5
- anchors.right: parent.right
- anchors.rightMargin: 0
- anchors.left: parent.left
- anchors.bottom: parent.bottom
+ 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: 192
- height: 64
- anchors.horizontalCenter: parent.horizontalCenter
- color: hifi.colors.hifiBlue
- border.width: 0
- radius: 10
+ width: loginDialog.inputWidth
+ height: loginDialog.inputHeight
+ radius: height / 2
+ color: "#353535"
+
+ TextInput {
+ anchors.fill: parent
+ text: "Login"
+ color: "white"
+ horizontalAlignment: Text.AlignHCenter
+ }
MouseArea {
- anchors.bottom: parent.bottom
- anchors.bottomMargin: 0
- anchors.top: parent.top
- anchors.right: parent.right
- anchors.left: parent.left
+ anchors.fill: parent
+ cursorShape: "PointingHandCursor"
onClicked: {
loginDialog.login(username.text, password.text)
}
}
+ }
- Row {
- anchors.centerIn: parent
+ Row {
+ anchors.horizontalCenter: parent.horizontalCenter
+
+ Text {
+ text: "Password?"
+ font.pixelSize: hifi.fonts.pixelSize * 0.8
+ font.underline: true
+ color: "#e0e0e0"
+ width: loginDialog.inputHeight * 4
+ horizontalAlignment: Text.AlignRight
anchors.verticalCenter: parent.verticalCenter
- spacing: 8
+
+ MouseArea {
+ anchors.fill: parent
+ cursorShape: "PointingHandCursor"
+ onClicked: {
+ loginDialog.openUrl(loginDialog.rootUrl + "/users/password/new")
+ }
+ }
+ }
+
+ Item {
+ width: loginDialog.inputHeight + loginDialog.inputSpacing * 2
+ height: loginDialog.inputHeight
+
Image {
- id: loginIcon
- height: 32
- width: 32
- source: "../images/login.svg"
- }
- Text {
- text: "Login"
- color: "white"
- width: 64
- height: parent.height
+ id: hifiIcon
+ source: "../images/hifi-logo-blackish.svg"
+ width: loginDialog.inputHeight
+ height: width
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ verticalCenter: parent.verticalCenter
+ }
}
}
- }
+ Text {
+ text: "Register"
+ font.pixelSize: hifi.fonts.pixelSize * 0.8
+ font.underline: true
+ color: "#e0e0e0"
+ width: loginDialog.inputHeight * 4
+ horizontalAlignment: Text.AlignLeft
+ anchors.verticalCenter: parent.verticalCenter
- Text {
- width: parent.width
- height: 24
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- text:"Create Account"
- font.pointSize: 12
- font.bold: true
- color: hifi.colors.hifiBlue
-
- MouseArea {
- anchors.fill: parent
- onClicked: {
- loginDialog.openUrl(loginDialog.rootUrl + "/signup")
- }
- }
- }
-
- Text {
- width: parent.width
- height: 24
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- font.pointSize: 12
- text: "Recover Password"
- color: hifi.colors.hifiBlue
-
- MouseArea {
- anchors.fill: parent
- onClicked: {
- loginDialog.openUrl(loginDialog.rootUrl + "/users/password/new")
+ MouseArea {
+ anchors.fill: parent
+ cursorShape: "PointingHandCursor"
+ onClicked: {
+ loginDialog.openUrl(loginDialog.rootUrl + "/signup")
+ }
}
}
}
}
}
+
+ onOpacityChanged: {
+ // Set focus once animation is completed so that focus is set at start-up when not logged in
+ if (opacity == 1.0) {
+ username.forceActiveFocus()
+ }
+ }
+
+ onVisibleChanged: {
+ if (!visible) {
+ username.text = ""
+ password.text = ""
+ loginDialog.statusText = ""
+ }
+ }
+
Keys.onPressed: {
- switch(event.key) {
+ switch (event.key) {
+ case Qt.Key_Escape:
+ case Qt.Key_Back:
+ enabled = false
+ event.accepted = true
+ break
case Qt.Key_Enter:
case Qt.Key_Return:
if (username.activeFocus) {
@@ -192,7 +361,7 @@ Dialog {
loginDialog.login(username.text, password.text)
}
}
- break;
+ break
}
}
}
diff --git a/interface/resources/qml/MarketplaceDialog.qml b/interface/resources/qml/MarketplaceDialog.qml
index 58bb3e6183..946f32e84a 100644
--- a/interface/resources/qml/MarketplaceDialog.qml
+++ b/interface/resources/qml/MarketplaceDialog.qml
@@ -5,7 +5,7 @@ import QtQuick.Controls.Styles 1.3
import QtWebKit 3.0
import "controls"
-Dialog {
+VrDialog {
title: "Test Dlg"
id: testDialog
objectName: "Browser"
diff --git a/interface/resources/qml/MessageDialog.qml b/interface/resources/qml/MessageDialog.qml
index dd410b8070..e8b01df9d0 100644
--- a/interface/resources/qml/MessageDialog.qml
+++ b/interface/resources/qml/MessageDialog.qml
@@ -5,7 +5,7 @@ import QtQuick.Dialogs 1.2
import "controls"
import "styles"
-Dialog {
+VrDialog {
id: root
HifiConstants { id: hifi }
property real spacing: hifi.layout.spacing
diff --git a/interface/resources/qml/TestDialog.qml b/interface/resources/qml/TestDialog.qml
index 15bd790c22..e6675b7282 100644
--- a/interface/resources/qml/TestDialog.qml
+++ b/interface/resources/qml/TestDialog.qml
@@ -3,7 +3,7 @@ import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.3
import "controls"
-Dialog {
+VrDialog {
title: "Test Dialog"
id: testDialog
objectName: "TestDialog"
diff --git a/interface/resources/qml/controls/DialogContainer.qml b/interface/resources/qml/controls/DialogContainer.qml
new file mode 100644
index 0000000000..4aa4e45d67
--- /dev/null
+++ b/interface/resources/qml/controls/DialogContainer.qml
@@ -0,0 +1,65 @@
+//
+// DialogCommon.qml
+//
+// Created by David Rowe on 3 Jun 2015
+// 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 "../styles"
+
+Item {
+ id: root
+
+ property bool destroyOnInvisible: true
+
+
+ // The UI enables an object, rather than manipulating its visibility, so that we can do animations in both directions.
+ // Because visibility and enabled are booleans, they cannot be animated. So when enabled is changed, we modify a property
+ // that can be animated, like scale or opacity, and then when the target animation value is reached, we can modify the
+ // visibility.
+ enabled: false
+ opacity: 0.0
+
+ onEnabledChanged: {
+ opacity = enabled ? 1.0 : 0.0
+ }
+
+ Behavior on opacity {
+ // Animate opacity.
+ NumberAnimation {
+ duration: hifi.effects.fadeInDuration
+ easing.type: Easing.OutCubic
+ }
+ }
+
+ onOpacityChanged: {
+ // Once we're transparent, disable the dialog's visibility.
+ visible = (opacity != 0.0)
+ }
+
+ onVisibleChanged: {
+ if (!visible) {
+ // Some dialogs should be destroyed when they become invisible.
+ if (destroyOnInvisible) {
+ destroy()
+ }
+ }
+ }
+
+
+ Keys.onPressed: {
+ switch(event.key) {
+ case Qt.Key_W:
+ if (event.modifiers == Qt.ControlModifier) {
+ enabled = false
+ event.accepted = true
+ }
+ break
+ }
+ }
+}
diff --git a/interface/resources/qml/controls/Dialog.qml b/interface/resources/qml/controls/VrDialog.qml
similarity index 100%
rename from interface/resources/qml/controls/Dialog.qml
rename to interface/resources/qml/controls/VrDialog.qml
diff --git a/interface/resources/qml/styles/HifiConstants.qml b/interface/resources/qml/styles/HifiConstants.qml
index fa556f2083..c232b993d1 100644
--- a/interface/resources/qml/styles/HifiConstants.qml
+++ b/interface/resources/qml/styles/HifiConstants.qml
@@ -13,10 +13,9 @@ Item {
readonly property color hifiBlue: "#0e7077"
readonly property color window: sysPalette.window
readonly property color dialogBackground: sysPalette.window
- //readonly property color dialogBackground: "#00000000"
readonly property color inputBackground: "white"
readonly property color background: sysPalette.dark
- readonly property color text: sysPalette.text
+ readonly property color text: "#202020"
readonly property color disabledText: "gray"
readonly property color hintText: "gray" // A bit darker than sysPalette.dark so that it is visible on the DK2
readonly property color light: sysPalette.light
diff --git a/interface/resources/sounds/short1.wav b/interface/resources/sounds/short1.wav
new file mode 100644
index 0000000000..fb03f5dd49
Binary files /dev/null and b/interface/resources/sounds/short1.wav differ
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 614522d618..64136627d7 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -250,12 +250,12 @@ bool setupEssentials(int& argc, char** argv) {
}
// Set build version
QCoreApplication::setApplicationVersion(BUILD_VERSION);
-
+
DependencyManager::registerInheritance();
DependencyManager::registerInheritance();
Setting::init();
-
+
// Set dependencies
auto addressManager = DependencyManager::set();
auto nodeList = DependencyManager::set(NodeType::Agent, listenPort);
@@ -289,6 +289,7 @@ bool setupEssentials(int& argc, char** argv) {
auto discoverabilityManager = DependencyManager::set();
auto sceneScriptingInterface = DependencyManager::set();
auto offscreenUi = DependencyManager::set();
+ auto pathUtils = DependencyManager::set();
return true;
}
@@ -355,9 +356,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "styles/Inconsolata.otf");
_window->setWindowTitle("Interface");
-
+
Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us
-
+
auto nodeList = DependencyManager::get();
_myAvatar = DependencyManager::get()->getMyAvatar();
@@ -369,7 +370,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_bookmarks = new Bookmarks(); // Before setting up the menu
_runningScriptsWidget = new RunningScriptsWidget(_window);
-
+
// start the nodeThread so its event loop is running
QThread* nodeThread = new QThread(this);
nodeThread->setObjectName("Datagram Processor Thread");
@@ -377,14 +378,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
// make sure the node thread is given highest priority
nodeThread->setPriority(QThread::TimeCriticalPriority);
-
+
_datagramProcessor = new DatagramProcessor(nodeList.data());
-
+
// have the NodeList use deleteLater from DM customDeleter
nodeList->setCustomDeleter([](Dependency* dependency) {
static_cast(dependency)->deleteLater();
});
-
+
// put the NodeList and datagram processing on the node thread
nodeList->moveToThread(nodeThread);
@@ -401,20 +402,22 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
// put the audio processing on a separate thread
QThread* audioThread = new QThread();
audioThread->setObjectName("Audio Thread");
-
+
auto audioIO = DependencyManager::get();
-
+
audioIO->setPositionGetter(getPositionForAudio);
audioIO->setOrientationGetter(getOrientationForAudio);
-
+
audioIO->moveToThread(audioThread);
connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start);
connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit);
connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater);
connect(audioIO.data(), &AudioClient::muteToggled, this, &Application::audioMuteToggled);
+ connect(audioIO.data(), &AudioClient::receivedFirstPacket,
+ &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::receivedFirstPacket);
audioThread->start();
-
+
const DomainHandler& domainHandler = nodeList->getDomainHandler();
connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
@@ -433,7 +436,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
auto discoverabilityManager = DependencyManager::get();
connect(locationUpdateTimer, &QTimer::timeout, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);
locationUpdateTimer->start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS);
-
+
// if we get a domain change, immediately attempt update location in metaverse server
connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain,
discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);
@@ -467,14 +470,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
// once the event loop has started, check and signal for an access token
QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection);
-
+
auto addressManager = DependencyManager::get();
-
+
// use our MyAvatar position and quat for address manager path
addressManager->setPositionGetter(getPositionForPath);
addressManager->setOrientationGetter(getOrientationForPath);
-
- connect(addressManager.data(), &AddressManager::rootPlaceNameChanged, this, &Application::updateWindowTitle);
+
+ connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle);
connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress);
#ifdef _WIN32
@@ -505,7 +508,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE);
cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache");
networkAccessManager.setCache(cache);
-
+
ResourceCache::setRequestLimit(3);
_window->setCentralWidget(_glWidget);
@@ -556,11 +559,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
connect(nodeList.data(), SIGNAL(dataReceived(const quint8, const int)),
bandwidthRecorder.data(), SLOT(updateInboundData(const quint8, const int)));
- connect(&_myAvatar->getSkeletonModel(), &SkeletonModel::skeletonLoaded,
+ connect(&_myAvatar->getSkeletonModel(), &SkeletonModel::skeletonLoaded,
this, &Application::checkSkeleton, Qt::QueuedConnection);
// Setup the userInputMapper with the actions
- // Setup the keyboardMouseDevice and the user input mapper with the default bindings
+ // Setup the keyboardMouseDevice and the user input mapper with the default bindings
_keyboardMouseDevice.registerToUserInputMapper(_userInputMapper);
_keyboardMouseDevice.assignDefaultInputMapping(_userInputMapper);
@@ -576,7 +579,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
// do this as late as possible so that all required subsystems are initialized
loadScripts();
}
-
+
loadSettings();
int SAVE_SETTINGS_INTERVAL = 10 * MSECS_PER_SECOND; // Let's save every seconds for now
connect(&_settingsTimer, &QTimer::timeout, this, &Application::saveSettings);
@@ -586,12 +589,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_settingsTimer.setSingleShot(false);
_settingsTimer.setInterval(SAVE_SETTINGS_INTERVAL);
_settingsThread.start();
-
+
_trayIcon->show();
-
+
// set the local loopback interface for local sounds from audio scripts
AudioScriptingInterface::getInstance().setLocalAudioInterface(audioIO.data());
-
+
#ifdef HAVE_RTMIDI
// setup the MIDIManager
MIDIManager& midiManagerInstance = MIDIManager::getInstance();
@@ -599,7 +602,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
#endif
this->installEventFilter(this);
- // The offscreen UI needs to intercept the mouse and keyboard
+ // The offscreen UI needs to intercept the mouse and keyboard
// events coming from the onscreen window
_glWidget->installEventFilter(DependencyManager::get().data());
@@ -617,7 +620,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
void Application::aboutToQuit() {
emit beforeAboutToQuit();
-
+
_aboutToQuit = true;
cleanupBeforeQuit();
}
@@ -629,7 +632,7 @@ void Application::cleanupBeforeQuit() {
_datagramProcessor->shutdown(); // tell the datagram processor we're shutting down, so it can short circuit
_entities.shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
ScriptEngine::stopAllScripts(this); // stop all currently running global scripts
-
+
// first stop all timers directly or by invokeMethod
// depending on what thread they run in
locationUpdateTimer->stop();
@@ -656,11 +659,11 @@ void Application::cleanupBeforeQuit() {
// let the avatar mixer know we're out
MyAvatar::sendKillAvatar();
-
+
// stop the AudioClient
QMetaObject::invokeMethod(DependencyManager::get().data(),
"stop", Qt::BlockingQueuedConnection);
-
+
// destroy the AudioClient so it and its thread have a chance to go down safely
DependencyManager::destroy();
@@ -669,12 +672,12 @@ void Application::cleanupBeforeQuit() {
#endif
}
-Application::~Application() {
+Application::~Application() {
EntityTree* tree = _entities.getTree();
tree->lockForWrite();
_entities.getTree()->setSimulation(NULL);
tree->unlock();
-
+
_octreeProcessor.terminate();
_entityEditSender.terminate();
@@ -684,7 +687,7 @@ Application::~Application() {
_myAvatar = NULL;
ModelEntityItem::cleanupLoadedAnimations();
-
+
// stop the glWidget frame timer so it doesn't call paintGL
_glWidget->stopFrameTimer();
@@ -699,10 +702,10 @@ Application::~Application() {
DependencyManager::destroy();
DependencyManager::destroy();
DependencyManager::destroy();
-
+
QThread* nodeThread = DependencyManager::get()->thread();
DependencyManager::destroy();
-
+
// ask the node thread to quit and wait until it is done
nodeThread->quit();
nodeThread->wait();
@@ -756,8 +759,8 @@ void Application::initializeGL() {
initDisplay();
qCDebug(interfaceapp, "Initialized Display.");
- // The UI can't be created until the primary OpenGL
- // context is created, because it needs to share
+ // The UI can't be created until the primary OpenGL
+ // context is created, because it needs to share
// texture resources
initializeUi();
qCDebug(interfaceapp, "Initialized Offscreen UI.");
@@ -909,7 +912,7 @@ void Application::paintGL() {
OculusManager::display(_glWidget, _myAvatar->getWorldAlignedOrientation(), _myAvatar->getDefaultEyePosition(), _myCamera);
}
} else if (TV3DManager::isConnected()) {
-
+
TV3DManager::display(_myCamera);
} else {
@@ -928,7 +931,7 @@ void Application::paintGL() {
if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
_rearMirrorTools->render(true, _glWidget->mapFromGlobal(QCursor::pos()));
} else if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
- renderRearViewMirror(_mirrorViewRect);
+ renderRearViewMirror(_mirrorViewRect);
}
auto finalFbo = DependencyManager::get()->render();
@@ -945,7 +948,7 @@ void Application::paintGL() {
if (!OculusManager::isConnected() || OculusManager::allowSwap()) {
_glWidget->swapBuffers();
- }
+ }
if (OculusManager::isConnected()) {
OculusManager::endFrameTiming();
@@ -1035,7 +1038,7 @@ void Application::updateProjectionMatrix(Camera& camera, bool updateViewFrustum)
// Tell our viewFrustum about this change, using the application camera
if (updateViewFrustum) {
loadViewFrustum(camera, _viewFrustum);
- }
+ }
glMatrixMode(GL_MODELVIEW);
}
@@ -1103,11 +1106,11 @@ bool Application::event(QEvent* event) {
// handle custom URL
if (event->type() == QEvent::FileOpen) {
-
+
QFileOpenEvent* fileEvent = static_cast(event);
QUrl url = fileEvent->url();
-
+
if (!url.isEmpty()) {
QString urlString = url.toString();
if (canAcceptURL(urlString)) {
@@ -1116,7 +1119,7 @@ bool Application::event(QEvent* event) {
}
return false;
}
-
+
if (HFActionEvent::types().contains(event->type())) {
_controllerScriptingInterface.handleMetaEvent(static_cast(event));
}
@@ -1182,7 +1185,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
Menu::getInstance()->triggerOption(MenuOption::AddressBar);
} else if (isShifted) {
Menu::getInstance()->triggerOption(MenuOption::LodTools);
- }
+ }
break;
case Qt::Key_F: {
@@ -1223,7 +1226,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
case Qt::Key_Backslash:
Menu::getInstance()->triggerOption(MenuOption::Chat);
break;
-
+
case Qt::Key_Up:
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
if (!isShifted) {
@@ -1352,18 +1355,18 @@ void Application::keyPressEvent(QKeyEvent* event) {
computePickRay(getTrueMouseX(), getTrueMouseY()));
sendEvent(this, &startActionEvent);
}
-
+
break;
}
case Qt::Key_Escape: {
OculusManager::abandonCalibration();
-
+
if (!event->isAutoRepeat()) {
// this starts the HFCancelEvent
HFBackEvent startBackEvent(HFBackEvent::startType());
sendEvent(this, &startBackEvent);
}
-
+
break;
}
@@ -1425,7 +1428,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
void Application::focusOutEvent(QFocusEvent* event) {
_keyboardMouseDevice.focusOutEvent(event);
-
+
// synthesize events for keys currently pressed, since we may not get their release events
foreach (int key, _keysPressed) {
QKeyEvent event(QEvent::KeyRelease, key, Qt::NoModifier);
@@ -1440,12 +1443,12 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
if (!_lastMouseMoveWasSimulated) {
_lastMouseMove = usecTimestampNow();
}
-
+
if (_aboutToQuit) {
return;
}
-
- if (Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen)
+
+ if (Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen)
&& !Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) {
// Show/hide menu bar in fullscreen
if (event->globalY() > _menuBarHeight) {
@@ -1458,7 +1461,7 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
}
_entities.mouseMoveEvent(event, deviceID);
-
+
_controllerScriptingInterface.emitMouseMoveEvent(event, deviceID); // send events to any registered scripts
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isMouseCaptured()) {
@@ -1466,7 +1469,7 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
}
_keyboardMouseDevice.mouseMoveEvent(event, deviceID);
-
+
}
void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
@@ -1492,7 +1495,7 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
_mouseDragStartedX = getTrueMouseX();
_mouseDragStartedY = getTrueMouseY();
_mousePressed = true;
-
+
if (mouseOnScreen()) {
if (DependencyManager::get()->mousePressEvent(getMouseX(), getMouseY())) {
// stop propagation
@@ -1509,7 +1512,7 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
return;
}
}
-
+
// nobody handled this - make it an action event on the _window object
HFActionEvent actionEvent(HFActionEvent::startType(),
computePickRay(event->x(), event->y()));
@@ -1557,14 +1560,14 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
if (event->button() == Qt::LeftButton) {
_mousePressed = false;
-
+
if (Menu::getInstance()->isOptionChecked(MenuOption::Stats) && mouseOnScreen()) {
// let's set horizontal offset to give stats some margin to mirror
int horizontalOffset = MIRROR_VIEW_WIDTH;
Stats::getInstance()->checkClick(getMouseX(), getMouseY(),
getMouseDragStartedX(), getMouseDragStartedY(), horizontalOffset);
}
-
+
// fire an action end event
HFActionEvent actionEvent(HFActionEvent::endType(),
computePickRay(event->x(), event->y()));
@@ -1673,7 +1676,7 @@ void Application::dropEvent(QDropEvent *event) {
}
}
}
-
+
if (atLeastOneFileAccepted) {
event->acceptProposedAction();
}
@@ -1693,7 +1696,7 @@ void Application::dragEnterEvent(QDragEnterEvent* event) {
bool Application::acceptSnapshot(const QString& urlString) {
QUrl url(urlString);
QString snapshotPath = url.toLocalFile();
-
+
SnapshotMetaData* snapshotData = Snapshot::parseSnapshotData(snapshotPath);
if (snapshotData) {
if (!snapshotData->getURL().toString().isEmpty()) {
@@ -1703,7 +1706,7 @@ bool Application::acceptSnapshot(const QString& urlString) {
QMessageBox msgBox;
msgBox.setText("No location details were found in the file "
+ snapshotPath + ", try dragging in an authentic Hifi snapshot.");
-
+
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.exec();
}
@@ -1736,7 +1739,7 @@ void Application::checkFPS() {
void Application::idle() {
PerformanceTimer perfTimer("idle");
-
+
if (_aboutToQuit) {
return; // bail early, nothing to do here.
}
@@ -1865,15 +1868,15 @@ void Application::setEnableVRMode(bool enableVRMode) {
OculusManager::recalibrate();
} else {
OculusManager::abandonCalibration();
-
+
_mirrorCamera.setHmdPosition(glm::vec3());
_mirrorCamera.setHmdRotation(glm::quat());
_myCamera.setHmdPosition(glm::vec3());
_myCamera.setHmdRotation(glm::quat());
}
-
+
resizeGL();
-
+
updateCursorVisibility();
}
@@ -1926,7 +1929,7 @@ int Application::getMouseDragStartedY() const {
FaceTracker* Application::getActiveFaceTracker() {
auto faceshift = DependencyManager::get();
auto dde = DependencyManager::get();
-
+
return (dde->isActive() ? static_cast(dde.data()) :
(faceshift->isActive() ? static_cast(faceshift.data()) : NULL));
}
@@ -2059,14 +2062,14 @@ void Application::saveSettings() {
bool Application::importEntities(const QString& urlOrFilename) {
_entityClipboard.eraseAllOctreeElements();
-
+
QUrl url(urlOrFilename);
-
+
// if the URL appears to be invalid or relative, then it is probably a local file
if (!url.isValid() || url.isRelative()) {
url = QUrl::fromLocalFile(urlOrFilename);
}
-
+
bool success = _entityClipboard.readFromURL(url.toString());
if (success) {
_entityClipboard.reaverageOctreeElements();
@@ -2090,7 +2093,7 @@ void Application::initDisplay() {
void Application::init() {
// Make sure Login state is up to date
DependencyManager::get()->toggleLoginDialog();
-
+
_environment.init();
DependencyManager::get()->init(this);
@@ -2118,7 +2121,7 @@ void Application::init() {
_timerStart.start();
_lastTimeUpdated.start();
-
+
// when --url in command line, teleport to location
const QString HIFI_URL_COMMAND_LINE_KEY = "--url";
int urlIndex = arguments().indexOf(HIFI_URL_COMMAND_LINE_KEY);
@@ -2126,11 +2129,11 @@ void Application::init() {
if (urlIndex != -1) {
addressLookupString = arguments().value(urlIndex + 1);
}
-
+
DependencyManager::get()->loadSettings(addressLookupString);
-
+
qCDebug(interfaceapp) << "Loaded settings";
-
+
#ifdef __APPLE__
if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseEnabled)) {
// on OS X we only setup sixense if the user wants it on - this allows running without the hid_init crash
@@ -2238,7 +2241,7 @@ void Application::updateMouseRay() {
PickRay pickRay = computePickRay(getTrueMouseX(), getTrueMouseY());
_mouseRayOrigin = pickRay.origin;
_mouseRayDirection = pickRay.direction;
-
+
// adjust for mirroring
if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
glm::vec3 mouseRayOffset = _mouseRayOrigin - _viewFrustum.getPosition();
@@ -2270,11 +2273,11 @@ void Application::updateMyAvatarLookAtPosition() {
lookAtSpot = OculusManager::getRightEyePosition();
}
}
-
+
} else {
AvatarSharedPointer lookingAt = _myAvatar->getLookAtTargetAvatar().toStrongRef();
if (lookingAt && _myAvatar != lookingAt.data()) {
-
+
isLookingAtSomeone = true;
// If I am looking at someone else, look directly at one of their eyes
if (tracker && !tracker->isMuted()) {
@@ -2361,7 +2364,7 @@ void Application::updateDialogs(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateDialogs()");
auto dialogsManager = DependencyManager::get();
-
+
// Update bandwidth dialog, if any
BandwidthDialog* bandwidthDialog = dialogsManager->getBandwidthDialog();
if (bandwidthDialog) {
@@ -2567,7 +2570,7 @@ void Application::update(float deltaTime) {
}
}
- // send packet containing downstream audio stats to the AudioMixer
+ // send packet containing downstream audio stats to the AudioMixer
{
quint64 sinceLastNack = now - _lastSendDownstreamAudioStats;
if (sinceLastNack > TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS) {
@@ -2589,64 +2592,64 @@ int Application::sendNackPackets() {
// iterates thru all nodes in NodeList
auto nodeList = DependencyManager::get();
-
+
nodeList->eachNode([&](const SharedNodePointer& node){
-
+
if (node->getActiveSocket() && node->getType() == NodeType::EntityServer) {
-
+
QUuid nodeUUID = node->getUUID();
-
+
// if there are octree packets from this node that are waiting to be processed,
// don't send a NACK since the missing packets may be among those waiting packets.
if (_octreeProcessor.hasPacketsToProcessFrom(nodeUUID)) {
return;
}
-
+
_octreeSceneStatsLock.lockForRead();
-
+
// retreive octree scene stats of this node
if (_octreeServerSceneStats.find(nodeUUID) == _octreeServerSceneStats.end()) {
_octreeSceneStatsLock.unlock();
return;
}
-
+
// get sequence number stats of node, prune its missing set, and make a copy of the missing set
SequenceNumberStats& sequenceNumberStats = _octreeServerSceneStats[nodeUUID].getIncomingOctreeSequenceNumberStats();
sequenceNumberStats.pruneMissingSet();
const QSet missingSequenceNumbers = sequenceNumberStats.getMissingSet();
-
+
_octreeSceneStatsLock.unlock();
-
+
// construct nack packet(s) for this node
int numSequenceNumbersAvailable = missingSequenceNumbers.size();
QSet::const_iterator missingSequenceNumbersIterator = missingSequenceNumbers.constBegin();
while (numSequenceNumbersAvailable > 0) {
-
+
char* dataAt = packet;
int bytesRemaining = MAX_PACKET_SIZE;
-
+
// pack header
int numBytesPacketHeader = nodeList->populatePacketHeader(packet, PacketTypeOctreeDataNack);
dataAt += numBytesPacketHeader;
bytesRemaining -= numBytesPacketHeader;
-
+
// calculate and pack the number of sequence numbers
int numSequenceNumbersRoomFor = (bytesRemaining - sizeof(uint16_t)) / sizeof(OCTREE_PACKET_SEQUENCE);
uint16_t numSequenceNumbers = min(numSequenceNumbersAvailable, numSequenceNumbersRoomFor);
uint16_t* numSequenceNumbersAt = (uint16_t*)dataAt;
*numSequenceNumbersAt = numSequenceNumbers;
dataAt += sizeof(uint16_t);
-
+
// pack sequence numbers
for (int i = 0; i < numSequenceNumbers; i++) {
OCTREE_PACKET_SEQUENCE* sequenceNumberAt = (OCTREE_PACKET_SEQUENCE*)dataAt;
*sequenceNumberAt = *missingSequenceNumbersIterator;
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
-
+
missingSequenceNumbersIterator++;
}
numSequenceNumbersAvailable -= numSequenceNumbers;
-
+
// send it
nodeList->writeUnverifiedDatagram(packet, dataAt - packet, node);
packetsSent++;
@@ -2688,7 +2691,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
int unknownJurisdictionServers = 0;
auto nodeList = DependencyManager::get();
-
+
nodeList->eachNode([&](const SharedNodePointer& node) {
// only send to the NodeTypes that are serverType
if (node->getActiveSocket() && node->getType() == serverType) {
@@ -2748,17 +2751,17 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
if (wantExtraDebugging) {
qCDebug(interfaceapp, "perServerPPS: %d perUnknownServer: %d", perServerPPS, perUnknownServer);
}
-
+
nodeList->eachNode([&](const SharedNodePointer& node){
// only send to the NodeTypes that are serverType
if (node->getActiveSocket() && node->getType() == serverType) {
-
+
// get the server bounds for this server
QUuid nodeUUID = node->getUUID();
-
+
bool inView = false;
bool unknownView = false;
-
+
// if we haven't heard from this voxel server, go ahead and send it a query, so we
// can get the jurisdiction...
if (jurisdictions.find(nodeUUID) == jurisdictions.end()) {
@@ -2768,9 +2771,9 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
}
} else {
const JurisdictionMap& map = (jurisdictions)[nodeUUID];
-
+
unsigned char* rootCode = map.getRootOctalCode();
-
+
if (rootCode) {
VoxelPositionSize rootDetails;
voxelDetailsForCode(rootCode, rootDetails);
@@ -2780,7 +2783,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
rootDetails.s * TREE_SCALE);
-
+
ViewFrustum::location serverFrustumLocation = _viewFrustum.cubeInFrustum(serverBounds);
if (serverFrustumLocation != ViewFrustum::OUTSIDE) {
inView = true;
@@ -2793,7 +2796,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
}
}
}
-
+
if (inView) {
_octreeQuery.setMaxQueryPacketsPerSecond(perServerPPS);
} else if (unknownView) {
@@ -2801,7 +2804,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
qCDebug(interfaceapp) << "no known jurisdiction for node " << *node << ", give it budget of "
<< perUnknownServer << " to send us jurisdiction.";
}
-
+
// set the query's position/orientation to be degenerate in a manner that will get the scene quickly
// If there's only one server, then don't do this, and just let the normal voxel query pass through
// as expected... this way, we will actually get a valid scene if there is one to be seen
@@ -2828,12 +2831,12 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
// insert packet type/version and node UUID
endOfQueryPacket += nodeList->populatePacketHeader(reinterpret_cast(endOfQueryPacket), packetType);
-
+
// encode the query data...
endOfQueryPacket += _octreeQuery.getBroadcastData(endOfQueryPacket);
-
+
int packetLength = endOfQueryPacket - queryPacket;
-
+
// make sure we still have an active socket
nodeList->writeUnverifiedDatagram(reinterpret_cast(queryPacket), packetLength, node);
}
@@ -2850,7 +2853,7 @@ bool Application::isHMDMode() const {
QRect Application::getDesirableApplicationGeometry() {
QRect applicationGeometry = getWindow()->geometry();
-
+
// If our parent window is on the HMD, then don't use its geometry, instead use
// the "main screen" geometry.
HMDToolsDialog* hmdTools = DependencyManager::get()->getHMDToolsDialog();
@@ -2911,14 +2914,14 @@ void Application::updateShadowMap() {
glm::vec3 lightDirection = getSunDirection();
glm::quat rotation = rotationBetween(IDENTITY_FRONT, lightDirection);
glm::quat inverseRotation = glm::inverse(rotation);
-
+
const float SHADOW_MATRIX_DISTANCES[] = { 0.0f, 2.0f, 6.0f, 14.0f, 30.0f };
const glm::vec2 MAP_COORDS[] = { glm::vec2(0.0f, 0.0f), glm::vec2(0.5f, 0.0f),
glm::vec2(0.0f, 0.5f), glm::vec2(0.5f, 0.5f) };
-
+
float frustumScale = 1.0f / (_viewFrustum.getFarClip() - _viewFrustum.getNearClip());
loadViewFrustum(_myCamera, _viewFrustum);
-
+
int matrixCount = 1;
//int targetSize = fbo->width();
int sourceSize = shadowFramebuffer->getWidth();
@@ -2960,12 +2963,12 @@ void Application::updateShadowMap() {
_shadowDistances[i] = -glm::distance(_viewFrustum.getPosition(), center) - radius * RADIUS_SCALE;
}
center = inverseRotation * center;
-
+
// to reduce texture "shimmer," move in texel increments
float texelSize = (2.0f * radius) / targetSize;
center = glm::vec3(roundf(center.x / texelSize) * texelSize, roundf(center.y / texelSize) * texelSize,
roundf(center.z / texelSize) * texelSize);
-
+
glm::vec3 minima(center.x - radius, center.y - radius, center.z - radius);
glm::vec3 maxima(center.x + radius, center.y + radius, center.z + radius);
@@ -3004,7 +3007,7 @@ void Application::updateShadowMap() {
// store view matrix without translation, which we'll use for precision-sensitive objects
updateUntranslatedViewMatrix();
-
+
// Equivalent to what is happening with _untranslatedViewMatrix and the _viewMatrixTranslation
// the viewTransofmr object is updatded with the correct values and saved,
// this is what is used for rendering the Entities and avatars
@@ -3047,9 +3050,9 @@ void Application::updateShadowMap() {
glMatrixMode(GL_MODELVIEW);
}
-
+
// fbo->release();
-
+
glViewport(0, 0, _glWidget->getDeviceWidth(), _glWidget->getDeviceHeight());
activeRenderingThread = nullptr;
}
@@ -3081,11 +3084,11 @@ bool Application::shouldRenderMesh(float largestDimension, float distanceToCamer
return DependencyManager::get()->shouldRenderMesh(largestDimension, distanceToCamera);
}
-float Application::getSizeScale() const {
+float Application::getSizeScale() const {
return DependencyManager::get()->getOctreeSizeScale();
}
-int Application::getBoundaryLevelAdjust() const {
+int Application::getBoundaryLevelAdjust() const {
return DependencyManager::get()->getBoundaryLevelAdjust();
}
@@ -3109,27 +3112,27 @@ PickRay Application::computePickRay(float x, float y) const {
QImage Application::renderAvatarBillboard() {
auto primaryFramebuffer = DependencyManager::get()->getPrimaryFramebuffer();
glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFramebuffer));
-
+
// clear the alpha channel so the background is transparent
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
-
+
// the "glow" here causes an alpha of one
Glower glower;
-
+
const int BILLBOARD_SIZE = 64;
renderRearViewMirror(QRect(0, _glWidget->getDeviceHeight() - BILLBOARD_SIZE,
BILLBOARD_SIZE, BILLBOARD_SIZE),
true);
-
+
QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32);
glReadPixels(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE, GL_BGRA, GL_UNSIGNED_BYTE, image.bits());
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
-
+
glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
+
return image;
}
@@ -3214,11 +3217,11 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billb
}
if (renderSide != RenderArgs::MONO) {
glm::mat4 invView = glm::inverse(_untranslatedViewMatrix);
-
+
viewTransform.evalFromRawMatrix(invView);
viewTransform.preTranslate(_viewMatrixTranslation);
}
-
+
setViewTransform(viewTransform);
glTranslatef(_viewMatrixTranslation.x, _viewMatrixTranslation.y, _viewMatrixTranslation.z);
@@ -3269,7 +3272,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billb
const float APPROXIMATE_DISTANCE_FROM_HORIZON = 0.1f;
const float DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON = 0.2f;
- glm::vec3 sunDirection = (getAvatarPosition() - closestData.getSunLocation())
+ glm::vec3 sunDirection = (getAvatarPosition() - closestData.getSunLocation())
/ closestData.getAtmosphereOuterRadius();
float height = glm::distance(theCamera.getPosition(), closestData.getAtmosphereCenter());
if (height < closestData.getAtmosphereInnerRadius()) {
@@ -3277,20 +3280,20 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billb
alpha = 0.0f;
if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) {
- float directionY = glm::clamp(sunDirection.y,
- -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
+ float directionY = glm::clamp(sunDirection.y,
+ -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
+ APPROXIMATE_DISTANCE_FROM_HORIZON;
alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON);
}
-
+
} else if (height < closestData.getAtmosphereOuterRadius()) {
alpha = (height - closestData.getAtmosphereInnerRadius()) /
(closestData.getAtmosphereOuterRadius() - closestData.getAtmosphereInnerRadius());
if (sunDirection.y > -APPROXIMATE_DISTANCE_FROM_HORIZON) {
- float directionY = glm::clamp(sunDirection.y,
- -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
+ float directionY = glm::clamp(sunDirection.y,
+ -APPROXIMATE_DISTANCE_FROM_HORIZON, APPROXIMATE_DISTANCE_FROM_HORIZON)
+ APPROXIMATE_DISTANCE_FROM_HORIZON;
alpha = (directionY / DOUBLE_APPROXIMATE_DISTANCE_FROM_HORIZON);
}
@@ -3331,14 +3334,14 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billb
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
-
+
DependencyManager::get()->prepare();
if (!selfAvatarOnly) {
// draw a red sphere
float originSphereRadius = 0.05f;
DependencyManager::get()->renderSphere(originSphereRadius, 15, 15, glm::vec4(1.0f, 0.0f, 0.0f, 1.0f));
-
+
// render models...
if (DependencyManager::get()->shouldRenderEntities()) {
PerformanceTimer perfTimer("entities");
@@ -3359,7 +3362,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billb
renderMode = RenderArgs::MIRROR_RENDER_MODE;
}
_entities.render(renderMode, renderSide, renderDebugFlags);
-
+
if (!Menu::getInstance()->isOptionChecked(MenuOption::Wireframe)) {
// Restaure polygon mode
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
@@ -3380,13 +3383,13 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billb
DependencyManager::get()->render();
}
}
-
+
bool mirrorMode = (theCamera.getMode() == CAMERA_MODE_MIRROR);
-
+
{
PerformanceTimer perfTimer("avatars");
DependencyManager::get()->renderAvatars(mirrorMode ? RenderArgs::MIRROR_RENDER_MODE : RenderArgs::NORMAL_RENDER_MODE,
- false, selfAvatarOnly);
+ false, selfAvatarOnly);
}
if (!billboard) {
@@ -3396,7 +3399,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billb
DependencyManager::get()->setGlobalAtmosphere(skyStage->getAtmosphere());
DependencyManager::get()->setGlobalSkybox(skybox);
- PROFILE_RANGE("DeferredLighting");
+ PROFILE_RANGE("DeferredLighting");
PerformanceTimer perfTimer("lighting");
DependencyManager::get()->render();
}
@@ -3404,9 +3407,9 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billb
{
PerformanceTimer perfTimer("avatarsPostLighting");
DependencyManager::get()->renderAvatars(mirrorMode ? RenderArgs::MIRROR_RENDER_MODE : RenderArgs::NORMAL_RENDER_MODE,
- true, selfAvatarOnly);
+ true, selfAvatarOnly);
}
-
+
//Render the sixense lasers
if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseLasers)) {
_myAvatar->renderLaserPointers();
@@ -3414,7 +3417,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billb
if (!selfAvatarOnly) {
_nodeBoundsDisplay.draw();
-
+
// Render the world box
if (theCamera.getMode() != CAMERA_MODE_MIRROR && Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
PerformanceTimer perfTimer("worldBox");
@@ -3491,9 +3494,9 @@ void Application::computeOffAxisFrustum(float& left, float& right, float& bottom
_displayViewFrustum.computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
if (OculusManager::isConnected()) {
OculusManager::overrideOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
-
+
} else if (TV3DManager::isConnected()) {
- TV3DManager::overrideOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
+ TV3DManager::overrideOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
}
}
@@ -3503,8 +3506,8 @@ bool Application::getShadowsEnabled() {
menubar->isOptionChecked(MenuOption::CascadedShadows);
}
-bool Application::getCascadeShadowsEnabled() {
- return Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows);
+bool Application::getCascadeShadowsEnabled() {
+ return Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows);
}
glm::vec2 Application::getScaledScreenPoint(glm::vec2 projectedPoint) {
@@ -3551,22 +3554,22 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
_myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * _myAvatar->getScale());
} else { // HEAD zoom level
- // FIXME note that the positioing of the camera relative to the avatar can suffer limited
- // precision as the user's position moves further away from the origin. Thus at
- // /1e7,1e7,1e7 (well outside the buildable volume) the mirror camera veers and sways
- // wildly as you rotate your avatar because the floating point values are becoming
- // larger, squeezing out the available digits of precision you have available at the
- // human scale for camera positioning.
+ // FIXME note that the positioing of the camera relative to the avatar can suffer limited
+ // precision as the user's position moves further away from the origin. Thus at
+ // /1e7,1e7,1e7 (well outside the buildable volume) the mirror camera veers and sways
+ // wildly as you rotate your avatar because the floating point values are becoming
+ // larger, squeezing out the available digits of precision you have available at the
+ // human scale for camera positioning.
- // Previously there was a hack to correct this using the mechanism of repositioning
- // the avatar at the origin of the world for the purposes of rendering the mirror,
- // but it resulted in failing to render the avatar's head model in the mirror view
- // when in first person mode. Presumably this was because of some missed culling logic
- // that was not accounted for in the hack.
+ // Previously there was a hack to correct this using the mechanism of repositioning
+ // the avatar at the origin of the world for the purposes of rendering the mirror,
+ // but it resulted in failing to render the avatar's head model in the mirror view
+ // when in first person mode. Presumably this was because of some missed culling logic
+ // that was not accounted for in the hack.
- // This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further
+ // This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further
// investigated in order to adapt the technique while fixing the head rendering issue,
- // but the complexity of the hack suggests that a better approach
+ // but the complexity of the hack suggests that a better approach
_mirrorCamera.setPosition(_myAvatar->getHead()->getEyePosition() +
_myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * _myAvatar->getScale());
}
@@ -3577,7 +3580,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
if (billboard) {
QSize size = DependencyManager::get()->getFrameBufferSize();
glViewport(region.x(), size.height() - region.y() - region.height(), region.width(), region.height());
- glScissor(region.x(), size.height() - region.y() - region.height(), region.width(), region.height());
+ glScissor(region.x(), size.height() - region.y() - region.height(), region.width(), region.height());
} else {
// if not rendering the billboard, the region is in device independent coordinates; must convert to device
QSize size = DependencyManager::get()->getFrameBufferSize();
@@ -3618,7 +3621,7 @@ void Application::resetSensors() {
QWindow* mainWindow = _window->windowHandle();
QPoint windowCenter = mainWindow->geometry().center();
_glWidget->cursor().setPos(currentScreen, windowCenter);
-
+
_myAvatar->reset();
QMetaObject::invokeMethod(DependencyManager::get().data(), "reset", Qt::QueuedConnection);
@@ -3650,12 +3653,12 @@ void Application::updateWindowTitle(){
QString connectionStatus = nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED) ";
QString username = AccountManager::getInstance().getAccountInfo().getUsername();
- QString currentPlaceName = DependencyManager::get()->getRootPlaceName();
-
+ QString currentPlaceName = DependencyManager::get()->getHost();
+
if (currentPlaceName.isEmpty()) {
currentPlaceName = nodeList->getDomainHandler().getHostname();
}
-
+
QString title = QString() + (!username.isEmpty() ? username + " @ " : QString())
+ currentPlaceName + connectionStatus + buildVersion;
@@ -3701,7 +3704,7 @@ void Application::domainConnectionDenied(const QString& reason) {
void Application::connectedToDomain(const QString& hostname) {
AccountManager& accountManager = AccountManager::getInstance();
const QUuid& domainID = DependencyManager::get()->getDomainHandler().getUUID();
-
+
if (accountManager.isLoggedIn() && !domainID.isNull()) {
_notifiedPacketVersionMismatchThisDomain = false;
}
@@ -3885,7 +3888,7 @@ void Application::saveScripts() {
Settings settings;
settings.beginWriteArray(SETTINGS_KEY);
settings.remove("");
-
+
QStringList runningScripts = getRunningScripts();
int i = 0;
for (auto it = runningScripts.begin(); it != runningScripts.end(); ++it) {
@@ -3936,7 +3939,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("Overlays", &_overlays);
qScriptRegisterMetaType(scriptEngine, OverlayPropertyResultToScriptValue, OverlayPropertyResultFromScriptValue);
- qScriptRegisterMetaType(scriptEngine, RayToOverlayIntersectionResultToScriptValue,
+ qScriptRegisterMetaType(scriptEngine, RayToOverlayIntersectionResultToScriptValue,
RayToOverlayIntersectionResultFromScriptValue);
QScriptValue windowValue = scriptEngine->registerGlobalObject("Window", DependencyManager::get().data());
@@ -3947,7 +3950,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
LocationScriptingInterface::locationSetter);
scriptEngine->registerFunction("WebWindow", WebWindowClass::constructor, 1);
-
+
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance());
@@ -3959,7 +3962,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
qScriptRegisterMetaType(scriptEngine, DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue);
scriptEngine->registerGlobalObject("AvatarManager", DependencyManager::get().data());
-
+
scriptEngine->registerGlobalObject("Joysticks", &JoystickScriptingInterface::getInstance());
qScriptRegisterMetaType(scriptEngine, joystickToScriptValue, joystickFromScriptValue);
@@ -3967,6 +3970,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("LODManager", DependencyManager::get().data());
+ scriptEngine->registerGlobalObject("Paths", DependencyManager::get().data());
+
QScriptValue hmdInterface = scriptEngine->registerGlobalObject("HMD", &HMDScriptingInterface::getInstance());
scriptEngine->registerFunction(hmdInterface, "getHUDLookAtPosition2D", HMDScriptingInterface::getHUDLookAtPosition2D, 0);
scriptEngine->registerFunction(hmdInterface, "getHUDLookAtPosition3D", HMDScriptingInterface::getHUDLookAtPosition3D, 0);
@@ -4014,7 +4019,7 @@ void Application::initializeAcceptedFiles() {
bool Application::canAcceptURL(const QString& urlString) {
initializeAcceptedFiles();
-
+
QUrl url(urlString);
if (urlString.startsWith(HIFI_URL_SCHEME)) {
return true;
@@ -4032,7 +4037,7 @@ bool Application::canAcceptURL(const QString& urlString) {
bool Application::acceptURL(const QString& urlString) {
initializeAcceptedFiles();
-
+
if (urlString.startsWith(HIFI_URL_SCHEME)) {
// this is a hifi URL - have the AddressManager handle it
QMetaObject::invokeMethod(DependencyManager::get().data(), "handleLookupString",
@@ -4070,19 +4075,19 @@ bool Application::askToSetAvatarUrl(const QString& url) {
msgBox.exec();
return false;
}
-
+
// Download the FST file, to attempt to determine its model type
QVariantHash fstMapping = FSTReader::downloadMapping(url);
-
+
FSTReader::ModelType modelType = FSTReader::predictModelType(fstMapping);
-
+
QMessageBox msgBox;
msgBox.setIcon(QMessageBox::Question);
msgBox.setWindowTitle("Set Avatar");
QPushButton* headButton = NULL;
QPushButton* bodyButton = NULL;
QPushButton* bodyAndHeadButton = NULL;
-
+
QString modelName = fstMapping["name"].toString();
QString message;
QString typeInfo;
@@ -4101,7 +4106,7 @@ bool Application::askToSetAvatarUrl(const QString& url) {
message = QString("Would you like to use '") + modelName + QString("' for your avatar?");
bodyAndHeadButton = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole);
break;
-
+
default:
message = QString("Would you like to use '") + modelName + QString("' for some part of your avatar head?");
headButton = msgBox.addButton(tr("Use for Head"), QMessageBox::ActionRole);
@@ -4127,7 +4132,7 @@ bool Application::askToSetAvatarUrl(const QString& url) {
} else {
qCDebug(interfaceapp) << "Declined to use the avatar: " << url;
}
-
+
return true;
}
@@ -4152,7 +4157,7 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser
if (isAboutToQuit()) {
return NULL;
}
-
+
QUrl scriptUrl(scriptFilename);
const QString& scriptURLString = scriptUrl.toString();
if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor
@@ -4163,18 +4168,18 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser
ScriptEngine* scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface);
scriptEngine->setUserLoaded(isUserLoaded);
-
+
if (scriptFilename.isNull()) {
// this had better be the script editor (we should de-couple so somebody who thinks they are loading a script
// doesn't just get an empty script engine)
-
+
// we can complete setup now since there isn't a script we have to load
registerScriptEngineWithApplicationServices(scriptEngine);
} else {
// connect to the appropriate signals of this script engine
connect(scriptEngine, &ScriptEngine::scriptLoaded, this, &Application::handleScriptEngineLoaded);
connect(scriptEngine, &ScriptEngine::errorLoadingScript, this, &Application::handleScriptLoadError);
-
+
// get the script engine object to load the script at the designated script URL
scriptEngine->loadURL(scriptUrl);
}
@@ -4189,11 +4194,11 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser
void Application::handleScriptEngineLoaded(const QString& scriptFilename) {
ScriptEngine* scriptEngine = qobject_cast(sender());
-
+
_scriptEnginesHash.insertMulti(scriptFilename, scriptEngine);
_runningScriptsWidget->setRunningScripts(getRunningScripts());
UserActivityLogger::getInstance().loadedScript(scriptFilename);
-
+
// register our application services and set it off on its own thread
registerScriptEngineWithApplicationServices(scriptEngine);
}
@@ -4303,7 +4308,7 @@ void Application::updateMyAvatarTransform() {
glm::vec3 newOriginOffset = avatarPosition;
int halfExtent = (int)HALF_SIMULATION_EXTENT;
for (int i = 0; i < 3; ++i) {
- newOriginOffset[i] = (float)(glm::max(halfExtent,
+ newOriginOffset[i] = (float)(glm::max(halfExtent,
((int)(avatarPosition[i] / SIMULATION_OFFSET_QUANTIZATION)) * (int)SIMULATION_OFFSET_QUANTIZATION));
}
// TODO: Andrew to replace this with method that actually moves existing object positions in PhysicsEngine
@@ -4317,23 +4322,23 @@ void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject
const QString PER_VOXEL_COST_KEY = "per-voxel-credits";
const QString PER_METER_CUBED_COST_KEY = "per-meter-cubed-credits";
const QString VOXEL_WALLET_UUID = "voxel-wallet";
-
+
const QJsonObject& voxelObject = domainSettingsObject[VOXEL_SETTINGS_KEY].toObject();
-
+
qint64 satoshisPerVoxel = 0;
qint64 satoshisPerMeterCubed = 0;
QUuid voxelWalletUUID;
-
+
if (!domainSettingsObject.isEmpty()) {
float perVoxelCredits = (float) voxelObject[PER_VOXEL_COST_KEY].toDouble();
float perMeterCubedCredits = (float) voxelObject[PER_METER_CUBED_COST_KEY].toDouble();
-
+
satoshisPerVoxel = (qint64) floorf(perVoxelCredits * SATOSHIS_PER_CREDIT);
satoshisPerMeterCubed = (qint64) floorf(perMeterCubedCredits * SATOSHIS_PER_CREDIT);
-
+
voxelWalletUUID = QUuid(voxelObject[VOXEL_WALLET_UUID].toString());
}
-
+
qCDebug(interfaceapp) << "Octree edits costs are" << satoshisPerVoxel << "per octree cell and" << satoshisPerMeterCubed << "per meter cubed";
qCDebug(interfaceapp) << "Destination wallet UUID for edit payments is" << voxelWalletUUID;
}
@@ -4536,7 +4541,7 @@ bool Application::isVSyncOn() const {
} else {
return true;
}
- */
+ */
#endif
return true;
}
@@ -4634,14 +4639,14 @@ void Application::notifyPacketVersionMismatch() {
void Application::checkSkeleton() {
if (_myAvatar->getSkeletonModel().isActive() && !_myAvatar->getSkeletonModel().hasSkeleton()) {
qCDebug(interfaceapp) << "MyAvatar model has no skeleton";
-
+
QString message = "Your selected avatar body has no skeleton.\n\nThe default body will be loaded...";
QMessageBox msgBox;
msgBox.setText(message);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setIcon(QMessageBox::Warning);
msgBox.exec();
-
+
_myAvatar->useBodyURL(DEFAULT_BODY_MODEL_URL, "Default");
} else {
_physicsEngine.setCharacterController(_myAvatar->getCharacterController());
@@ -4654,7 +4659,7 @@ void Application::showFriendsWindow() {
const int FRIENDS_WINDOW_WIDTH = 290;
const int FRIENDS_WINDOW_HEIGHT = 500;
if (!_friendsWindow) {
- _friendsWindow = new WebWindowClass(FRIENDS_WINDOW_TITLE, FRIENDS_WINDOW_URL, FRIENDS_WINDOW_WIDTH,
+ _friendsWindow = new WebWindowClass(FRIENDS_WINDOW_TITLE, FRIENDS_WINDOW_URL, FRIENDS_WINDOW_WIDTH,
FRIENDS_WINDOW_HEIGHT, false);
connect(_friendsWindow, &WebWindowClass::closed, this, &Application::friendsWindowClosed);
}
@@ -4695,16 +4700,16 @@ QSize Application::getDeviceSize() const {
return _glWidget->getDeviceSize();
}
-int Application::getTrueMouseX() const {
- return _glWidget->mapFromGlobal(QCursor::pos()).x();
+int Application::getTrueMouseX() const {
+ return _glWidget->mapFromGlobal(QCursor::pos()).x();
}
-int Application::getTrueMouseY() const {
- return _glWidget->mapFromGlobal(QCursor::pos()).y();
+int Application::getTrueMouseY() const {
+ return _glWidget->mapFromGlobal(QCursor::pos()).y();
}
-bool Application::isThrottleRendering() const {
- return _glWidget->isThrottleRendering();
+bool Application::isThrottleRendering() const {
+ return _glWidget->isThrottleRendering();
}
PickRay Application::computePickRay() const {
diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp
index b452f153f0..196ba5d29e 100644
--- a/interface/src/ui/LoginDialog.cpp
+++ b/interface/src/ui/LoginDialog.cpp
@@ -1,6 +1,6 @@
//
-//
// LoginDialog.cpp
+// interface/src/ui
//
// Created by Bradley Austin Davis on 2015/04/14
// Copyright 2015 High Fidelity, Inc.
@@ -8,16 +8,22 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+
#include "LoginDialog.h"
-#include "DependencyManager.h"
-#include "AccountManager.h"
-#include "Menu.h"
+#include
+
#include
+#include "AccountManager.h"
+#include "DependencyManager.h"
+#include "Menu.h"
+
HIFI_QML_DEF(LoginDialog)
-LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent), _rootUrl(NetworkingConstants::METAVERSE_SERVER_URL.toString()) {
+LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent),
+ _rootUrl(NetworkingConstants::METAVERSE_SERVER_URL.toString())
+{
connect(&AccountManager::getInstance(), &AccountManager::loginComplete,
this, &LoginDialog::handleLoginCompleted);
connect(&AccountManager::getInstance(), &AccountManager::loginFailed,
@@ -48,7 +54,7 @@ void LoginDialog::handleLoginCompleted(const QUrl&) {
}
void LoginDialog::handleLoginFailed() {
- setStatusText("Invalid username or password.< / font>");
+ setStatusText("Invalid username or password");
}
void LoginDialog::setStatusText(const QString& statusText) {
@@ -68,10 +74,11 @@ QString LoginDialog::rootUrl() const {
void LoginDialog::login(const QString& username, const QString& password) {
qDebug() << "Attempting to login " << username;
- setStatusText("Authenticating...");
+ setStatusText("Logging in...");
AccountManager::getInstance().requestAccessToken(username, password);
}
void LoginDialog::openUrl(const QString& url) {
qDebug() << url;
+ QDesktopServices::openUrl(url);
}
diff --git a/interface/src/ui/LoginDialog.h b/interface/src/ui/LoginDialog.h
index e9ae0a1c16..50c820aa07 100644
--- a/interface/src/ui/LoginDialog.h
+++ b/interface/src/ui/LoginDialog.h
@@ -1,5 +1,6 @@
//
// LoginDialog.h
+// interface/src/ui
//
// Created by Bradley Austin Davis on 2015/04/14
// Copyright 2015 High Fidelity, Inc.
@@ -9,6 +10,7 @@
//
#pragma once
+
#ifndef hifi_LoginDialog_h
#define hifi_LoginDialog_h
diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp
index 41c8cb9537..340ca9374d 100644
--- a/libraries/audio-client/src/AudioClient.cpp
+++ b/libraries/audio-client/src/AudioClient.cpp
@@ -153,6 +153,7 @@ void AudioClient::reset() {
}
void AudioClient::audioMixerKilled() {
+ _hasReceivedFirstPacket = false;
_outgoingAvatarAudioSequenceNumber = 0;
_stats.reset();
}
@@ -481,6 +482,7 @@ void AudioClient::start() {
qCDebug(audioclient) << "Unable to set up audio input because of a problem with input format.";
qCDebug(audioclient) << "The closest format available is" << inputDeviceInfo.nearestFormat(_desiredInputFormat);
}
+
if (!outputFormatSupported) {
qCDebug(audioclient) << "Unable to set up audio output because of a problem with output format.";
qCDebug(audioclient) << "The closest format available is" << outputDeviceInfo.nearestFormat(_desiredOutputFormat);
@@ -489,6 +491,7 @@ void AudioClient::start() {
if (_audioInput) {
_inputFrameBuffer.initialize( _inputFormat.channelCount(), _audioInput->bufferSize() * 8 );
}
+
_inputGain.initialize();
_sourceGain.initialize();
_noiseSource.initialize();
@@ -926,6 +929,14 @@ void AudioClient::addReceivedAudioToStream(const QByteArray& audioByteArray) {
DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveFirstAudioPacket);
if (_audioOutput) {
+
+ if (!_hasReceivedFirstPacket) {
+ _hasReceivedFirstPacket = true;
+
+ // have the audio scripting interface emit a signal to say we just connected to mixer
+ emit receivedFirstPacket();
+ }
+
// Audio output must exist and be correctly set up if we're going to process received audio
_receivedAudioStream.parseData(audioByteArray);
}
diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h
index f9392c6a10..3b2c1c1ae6 100644
--- a/libraries/audio-client/src/AudioClient.h
+++ b/libraries/audio-client/src/AudioClient.h
@@ -37,6 +37,7 @@
#include
#include
#include
+#include
#include
#include "AudioIOStats.h"
@@ -57,7 +58,7 @@ static const int NUM_AUDIO_CHANNELS = 2;
static const int DEFAULT_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 3;
static const int MIN_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 1;
static const int MAX_AUDIO_OUTPUT_BUFFER_SIZE_FRAMES = 20;
-#if defined(Q_OS_ANDROID) || defined(Q_OS_WIN)
+#if defined(Q_OS_ANDROID) || defined(Q_OS_WIN)
static const int DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_ENABLED = false;
#else
static const int DEFAULT_AUDIO_OUTPUT_STARVE_DETECTION_ENABLED = true;
@@ -88,14 +89,14 @@ public:
void stop() { close(); }
qint64 readData(char * data, qint64 maxSize);
qint64 writeData(const char * data, qint64 maxSize) { return 0; }
-
+
int getRecentUnfulfilledReads() { int unfulfilledReads = _unfulfilledReads; _unfulfilledReads = 0; return unfulfilledReads; }
private:
MixedProcessedAudioStream& _receivedAudioStream;
AudioClient* _audio;
int _unfulfilledReads;
};
-
+
const MixedProcessedAudioStream& getReceivedAudioStream() const { return _receivedAudioStream; }
MixedProcessedAudioStream& getReceivedAudioStream() { return _receivedAudioStream; }
@@ -105,30 +106,30 @@ public:
float getAudioAverageInputLoudness() const { return _lastInputLoudness; }
int getDesiredJitterBufferFrames() const { return _receivedAudioStream.getDesiredJitterBufferFrames(); }
-
+
bool isMuted() { return _muted; }
-
+
const AudioIOStats& getStats() const { return _stats; }
float getInputRingBufferMsecsAvailable() const;
float getAudioOutputMsecsUnplayed() const;
int getOutputBufferSize() { return _outputBufferSizeFrames.get(); }
-
+
bool getOutputStarveDetectionEnabled() { return _outputStarveDetectionEnabled.get(); }
void setOutputStarveDetectionEnabled(bool enabled) { _outputStarveDetectionEnabled.set(enabled); }
int getOutputStarveDetectionPeriod() { return _outputStarveDetectionPeriodMsec.get(); }
void setOutputStarveDetectionPeriod(int msecs) { _outputStarveDetectionPeriodMsec.set(msecs); }
-
+
int getOutputStarveDetectionThreshold() { return _outputStarveDetectionThreshold.get(); }
void setOutputStarveDetectionThreshold(int threshold) { _outputStarveDetectionThreshold.set(threshold); }
-
+
void setPositionGetter(AudioPositionGetter positionGetter) { _positionGetter = positionGetter; }
void setOrientationGetter(AudioOrientationGetter orientationGetter) { _orientationGetter = orientationGetter; }
static const float CALLBACK_ACCELERATOR_RATIO;
-
+
public slots:
void start();
void stop();
@@ -140,7 +141,7 @@ public slots:
void reset();
void audioMixerKilled();
void toggleMute();
-
+
virtual void enableAudioSourceInject(bool enable);
virtual void selectAudioSourcePinkNoise();
virtual void selectAudioSourceSine440();
@@ -148,10 +149,10 @@ public slots:
virtual void setIsStereoInput(bool stereo);
void toggleAudioNoiseReduction() { _isNoiseGateEnabled = !_isNoiseGateEnabled; }
-
+
void toggleLocalEcho() { _shouldEchoLocally = !_shouldEchoLocally; }
void toggleServerEcho() { _shouldEchoToServer = !_shouldEchoToServer; }
-
+
void processReceivedSamples(const QByteArray& inputBuffer, QByteArray& outputBuffer);
void sendMuteEnvironmentPacket();
@@ -172,10 +173,10 @@ public slots:
void setReverbOptions(const AudioEffectOptions* options);
void outputNotify();
-
+
void loadSettings();
void saveSettings();
-
+
signals:
bool muteToggled();
void inputReceived(const QByteArray& inputSamples);
@@ -184,14 +185,16 @@ signals:
void deviceChanged();
+ void receivedFirstPacket();
+
protected:
AudioClient();
~AudioClient();
-
+
virtual void customDeleter() {
deleteLater();
}
-
+
private:
void outputFormatChanged();
@@ -216,35 +219,35 @@ private:
QString _inputAudioDeviceName;
QString _outputAudioDeviceName;
-
+
quint64 _outputStarveDetectionStartTimeMsec;
int _outputStarveDetectionCount;
-
+
Setting::Handle _outputBufferSizeFrames;
Setting::Handle _outputStarveDetectionEnabled;
Setting::Handle _outputStarveDetectionPeriodMsec;
// Maximum number of starves per _outputStarveDetectionPeriod before increasing buffer size
Setting::Handle _outputStarveDetectionThreshold;
-
+
StDev _stdev;
QElapsedTimer _timeSinceLastReceived;
float _averagedLatency;
float _lastInputLoudness;
float _timeSinceLastClip;
int _totalInputAudioSamples;
-
+
bool _muted;
bool _shouldEchoLocally;
bool _shouldEchoToServer;
bool _isNoiseGateEnabled;
bool _audioSourceInjectEnabled;
-
+
bool _reverb;
AudioEffectOptions _scriptReverbOptions;
AudioEffectOptions _zoneReverbOptions;
AudioEffectOptions* _reverbOptions;
ty_gverb* _gverb;
-
+
// possible soxr streams needed for resample
soxr* _inputToNetworkResampler;
soxr* _networkToOutputResampler;
@@ -268,17 +271,17 @@ private:
// Input framebuffer
AudioBufferFloat32 _inputFrameBuffer;
-
+
// Input gain
AudioGain _inputGain;
-
+
// Post tone/pink noise generator gain
AudioGain _sourceGain;
// Pink noise source
bool _noiseSourceEnabled;
AudioSourcePinkNoise _noiseSource;
-
+
// Tone source
bool _toneSourceEnabled;
AudioSourceTone _toneSource;
@@ -286,17 +289,19 @@ private:
quint16 _outgoingAvatarAudioSequenceNumber;
AudioOutputIODevice _audioOutputIODevice;
-
+
AudioIOStats _stats;
-
+
AudioNoiseGate _inputGate;
-
+
AudioPositionGetter _positionGetter;
AudioOrientationGetter _orientationGetter;
-
+
QVector _inputDevices;
QVector _outputDevices;
void checkDevices();
+
+ bool _hasReceivedFirstPacket = false;
};
diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp
index ae397ba97e..912351e21a 100644
--- a/libraries/audio/src/AudioInjector.cpp
+++ b/libraries/audio/src/AudioInjector.cpp
@@ -26,7 +26,7 @@
AudioInjector::AudioInjector(QObject* parent) :
QObject(parent)
{
-
+
}
AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions) :
@@ -39,24 +39,25 @@ AudioInjector::AudioInjector(const QByteArray& audioData, const AudioInjectorOpt
_audioData(audioData),
_options(injectorOptions)
{
-
+
}
void AudioInjector::setIsFinished(bool isFinished) {
_isFinished = isFinished;
-
+ // In all paths, regardless of isFinished argument. restart() passes false to prepare for new play, and injectToMixer() needs _shouldStop reset.
+ _shouldStop = false;
+
if (_isFinished) {
emit finished();
-
+
if (_localBuffer) {
_localBuffer->stop();
_localBuffer->deleteLater();
_localBuffer = NULL;
}
-
+
_isStarted = false;
- _shouldStop = false;
-
+
if (_shouldDeleteAfterFinish) {
// we've been asked to delete after finishing, trigger a queued deleteLater here
qCDebug(audio) << "AudioInjector triggering delete from setIsFinished";
@@ -67,18 +68,19 @@ void AudioInjector::setIsFinished(bool isFinished) {
void AudioInjector::injectAudio() {
if (!_isStarted) {
+ _isStarted = true;
// check if we need to offset the sound by some number of seconds
if (_options.secondOffset > 0.0f) {
-
+
// convert the offset into a number of bytes
int byteOffset = (int) floorf(AudioConstants::SAMPLE_RATE * _options.secondOffset * (_options.stereo ? 2.0f : 1.0f));
byteOffset *= sizeof(int16_t);
-
+
_currentSendPosition = byteOffset;
} else {
_currentSendPosition = 0;
}
-
+
if (_options.localOnly) {
injectLocally();
} else {
@@ -86,12 +88,20 @@ void AudioInjector::injectAudio() {
}
} else {
qCDebug(audio) << "AudioInjector::injectAudio called but already started.";
- }
+ }
}
void AudioInjector::restart() {
qCDebug(audio) << "Restarting an AudioInjector by stopping and starting over.";
- stop();
+ connect(this, &AudioInjector::finished, this, &AudioInjector::restartPortionAfterFinished);
+ if (!_isStarted || _isFinished) {
+ emit finished();
+ } else {
+ stop();
+ }
+}
+void AudioInjector::restartPortionAfterFinished() {
+ disconnect(this, &AudioInjector::finished, this, &AudioInjector::restartPortionAfterFinished);
setIsFinished(false);
QMetaObject::invokeMethod(this, "injectAudio", Qt::QueuedConnection);
}
@@ -100,37 +110,37 @@ void AudioInjector::injectLocally() {
bool success = false;
if (_localAudioInterface) {
if (_audioData.size() > 0) {
-
+
_localBuffer = new AudioInjectorLocalBuffer(_audioData, this);
-
+
_localBuffer->open(QIODevice::ReadOnly);
_localBuffer->setShouldLoop(_options.loop);
_localBuffer->setVolume(_options.volume);
-
+
// give our current send position to the local buffer
_localBuffer->setCurrentOffset(_currentSendPosition);
-
+
success = _localAudioInterface->outputLocalInjector(_options.stereo, this);
-
+
// if we're not looping and the buffer tells us it is empty then emit finished
connect(_localBuffer, &AudioInjectorLocalBuffer::bufferEmpty, this, &AudioInjector::stop);
-
+
if (!success) {
qCDebug(audio) << "AudioInjector::injectLocally could not output locally via _localAudioInterface";
}
} else {
qCDebug(audio) << "AudioInjector::injectLocally called without any data in Sound QByteArray";
}
-
+
} else {
qCDebug(audio) << "AudioInjector::injectLocally cannot inject locally with no local audio interface present.";
}
-
+
if (!success) {
// we never started so we are finished, call our stop method
stop();
}
-
+
}
const uchar MAX_INJECTOR_VOLUME = 0xFF;
@@ -140,65 +150,65 @@ void AudioInjector::injectToMixer() {
_currentSendPosition >= _audioData.size()) {
_currentSendPosition = 0;
}
-
+
auto nodeList = DependencyManager::get();
-
+
// make sure we actually have samples downloaded to inject
if (_audioData.size()) {
-
+
// setup the packet for injected audio
QByteArray injectAudioPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeInjectAudio);
QDataStream packetStream(&injectAudioPacket, QIODevice::Append);
-
+
// pack some placeholder sequence number for now
int numPreSequenceNumberBytes = injectAudioPacket.size();
packetStream << (quint16)0;
-
+
// pack stream identifier (a generated UUID)
packetStream << QUuid::createUuid();
-
+
// pack the stereo/mono type of the stream
packetStream << _options.stereo;
-
+
// pack the flag for loopback
uchar loopbackFlag = (uchar) true;
packetStream << loopbackFlag;
-
+
// pack the position for injected audio
int positionOptionOffset = injectAudioPacket.size();
packetStream.writeRawData(reinterpret_cast(&_options.position),
sizeof(_options.position));
-
+
// pack our orientation for injected audio
int orientationOptionOffset = injectAudioPacket.size();
packetStream.writeRawData(reinterpret_cast(&_options.orientation),
sizeof(_options.orientation));
-
+
// pack zero for radius
float radius = 0;
packetStream << radius;
-
+
// pack 255 for attenuation byte
int volumeOptionOffset = injectAudioPacket.size();
quint8 volume = MAX_INJECTOR_VOLUME * _options.volume;
packetStream << volume;
-
+
packetStream << _options.ignorePenumbra;
-
+
QElapsedTimer timer;
timer.start();
int nextFrame = 0;
-
+
int numPreAudioDataBytes = injectAudioPacket.size();
bool shouldLoop = _options.loop;
-
+
// loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks
quint16 outgoingInjectedAudioSequenceNumber = 0;
while (_currentSendPosition < _audioData.size() && !_shouldStop) {
-
+
int bytesToCopy = std::min(((_options.stereo) ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL,
_audioData.size() - _currentSendPosition);
-
+
// Measure the loudness of this frame
_loudness = 0.0f;
for (int i = 0; i < bytesToCopy; i += sizeof(int16_t)) {
@@ -215,45 +225,45 @@ void AudioInjector::injectToMixer() {
sizeof(_options.orientation));
volume = MAX_INJECTOR_VOLUME * _options.volume;
memcpy(injectAudioPacket.data() + volumeOptionOffset, &volume, sizeof(volume));
-
+
// resize the QByteArray to the right size
injectAudioPacket.resize(numPreAudioDataBytes + bytesToCopy);
// pack the sequence number
memcpy(injectAudioPacket.data() + numPreSequenceNumberBytes,
&outgoingInjectedAudioSequenceNumber, sizeof(quint16));
-
+
// copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet
memcpy(injectAudioPacket.data() + numPreAudioDataBytes,
_audioData.data() + _currentSendPosition, bytesToCopy);
-
+
// grab our audio mixer from the NodeList, if it exists
SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);
-
+
// send off this audio packet
nodeList->writeDatagram(injectAudioPacket, audioMixer);
outgoingInjectedAudioSequenceNumber++;
-
+
_currentSendPosition += bytesToCopy;
-
+
// send two packets before the first sleep so the mixer can start playback right away
-
+
if (_currentSendPosition != bytesToCopy && _currentSendPosition < _audioData.size()) {
-
+
// process events in case we have been told to stop and be deleted
QCoreApplication::processEvents();
-
+
if (_shouldStop) {
break;
}
-
+
// not the first packet and not done
// sleep for the appropriate time
int usecToSleep = (++nextFrame * AudioConstants::NETWORK_FRAME_USECS) - timer.nsecsElapsed() / 1000;
-
+
if (usecToSleep > 0) {
usleep(usecToSleep);
- }
+ }
}
if (shouldLoop && _currentSendPosition >= _audioData.size()) {
@@ -261,13 +271,13 @@ void AudioInjector::injectToMixer() {
}
}
}
-
+
setIsFinished(true);
}
void AudioInjector::stop() {
_shouldStop = true;
-
+
if (_options.localOnly) {
// we're only a local injector, so we can say we are finished right away too
setIsFinished(true);
diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h
index ca39dcbdc4..0513b70bd8 100644
--- a/libraries/audio/src/AudioInjector.h
+++ b/libraries/audio/src/AudioInjector.h
@@ -59,6 +59,7 @@ public slots:
void setCurrentSendPosition(int currentSendPosition) { _currentSendPosition = currentSendPosition; }
float getLoudness() const { return _loudness; }
bool isPlaying() const { return !_isFinished; }
+ void restartPortionAfterFinished();
signals:
void finished();
diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp
index 577328ee18..7dc6010f8f 100644
--- a/libraries/audio/src/Sound.cpp
+++ b/libraries/audio/src/Sound.cpp
@@ -53,21 +53,25 @@ Sound::Sound(const QUrl& url, bool isStereo) :
_isStereo(isStereo),
_isReady(false)
{
-
+
}
void Sound::downloadFinished(QNetworkReply* reply) {
// replace our byte array with the downloaded data
QByteArray rawAudioByteArray = reply->readAll();
+ QString fileName = reply->url().fileName();
- if (reply->hasRawHeader("Content-Type")) {
+ const QString WAV_EXTENSION = ".wav";
+
+ if (reply->hasRawHeader("Content-Type") || fileName.endsWith(WAV_EXTENSION)) {
QByteArray headerContentType = reply->rawHeader("Content-Type");
// WAV audio file encountered
if (headerContentType == "audio/x-wav"
|| headerContentType == "audio/wav"
- || headerContentType == "audio/wave") {
+ || headerContentType == "audio/wave"
+ || fileName.endsWith(WAV_EXTENSION)) {
QByteArray outputAudioByteArray;
@@ -80,7 +84,7 @@ void Sound::downloadFinished(QNetworkReply* reply) {
_isStereo = true;
qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << reply->url() << "as stereo audio file.";
}
-
+
// Process as RAW file
downSample(rawAudioByteArray);
}
@@ -88,7 +92,7 @@ void Sound::downloadFinished(QNetworkReply* reply) {
} else {
qCDebug(audio) << "Network reply without 'Content-Type'.";
}
-
+
_isReady = true;
reply->deleteLater();
}
@@ -99,16 +103,16 @@ void Sound::downSample(const QByteArray& rawAudioByteArray) {
// we want to convert it to the format that the audio-mixer wants
// which is signed, 16-bit, 24Khz
-
+
int numSourceSamples = rawAudioByteArray.size() / sizeof(AudioConstants::AudioSample);
-
+
int numDestinationBytes = rawAudioByteArray.size() / sizeof(AudioConstants::AudioSample);
if (_isStereo && numSourceSamples % 2 != 0) {
numDestinationBytes += sizeof(AudioConstants::AudioSample);
}
-
+
_byteArray.resize(numDestinationBytes);
-
+
int16_t* sourceSamples = (int16_t*) rawAudioByteArray.data();
int16_t* destinationSamples = (int16_t*) _byteArray.data();
@@ -134,22 +138,22 @@ void Sound::downSample(const QByteArray& rawAudioByteArray) {
}
void Sound::trimFrames() {
-
+
const uint32_t inputFrameCount = _byteArray.size() / sizeof(int16_t);
const uint32_t trimCount = 1024; // number of leading and trailing frames to trim
-
+
if (inputFrameCount <= (2 * trimCount)) {
return;
}
-
+
int16_t* inputFrameData = (int16_t*)_byteArray.data();
AudioEditBufferFloat32 editBuffer(1, inputFrameCount);
editBuffer.copyFrames(1, inputFrameCount, inputFrameData, false /*copy in*/);
-
+
editBuffer.linearFade(0, trimCount, true);
editBuffer.linearFade(inputFrameCount - trimCount, inputFrameCount, false);
-
+
editBuffer.copyFrames(1, inputFrameCount, inputFrameData, true /*copy out*/);
}
@@ -238,7 +242,7 @@ void Sound::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& ou
} else if (qFromLittleEndian(fileHeader.wave.numChannels) > 2) {
qCDebug(audio) << "Currently not support audio files with more than 2 channels.";
}
-
+
if (qFromLittleEndian(fileHeader.wave.bitsPerSample) != 16) {
qCDebug(audio) << "Currently not supporting non 16bit audio files.";
return;
diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp
index 3c6b7bd3f5..a86ce78655 100644
--- a/libraries/networking/src/AddressManager.cpp
+++ b/libraries/networking/src/AddressManager.cpp
@@ -30,7 +30,7 @@ const QString SETTINGS_CURRENT_ADDRESS_KEY = "address";
Setting::Handle currentAddressHandle(QStringList() << ADDRESS_MANAGER_SETTINGS_GROUP << "address", DEFAULT_HIFI_ADDRESS);
AddressManager::AddressManager() :
- _rootPlaceName(),
+ _host(),
_rootPlaceID(),
_positionGetter(NULL),
_orientationGetter(NULL)
@@ -45,7 +45,7 @@ const QUrl AddressManager::currentAddress() const {
QUrl hifiURL;
hifiURL.setScheme(HIFI_URL_SCHEME);
- hifiURL.setHost(_rootPlaceName);
+ hifiURL.setHost(_host);
hifiURL.setPath(currentPath());
return hifiURL;
@@ -123,6 +123,10 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) {
+ (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())))) {
// we may have a path that defines a relative viewpoint - if so we should jump to that now
handlePath(lookupUrl.path());
+ } else if (handleDomainID(lookupUrl.host())){
+ // no place name - this is probably a domain ID
+ // try to look up the domain ID on the metaverse API
+ attemptDomainIDLookup(lookupUrl.host(), lookupUrl.path());
} else {
// wasn't an address - lookup the place name
// we may have a path that defines a relative viewpoint - pass that through the lookup so we can go to it after
@@ -161,11 +165,18 @@ void AddressManager::handleLookupString(const QString& lookupString) {
}
}
+const QString DATA_OBJECT_DOMAIN_KEY = "domain";
+
+
void AddressManager::handleAPIResponse(QNetworkReply& requestReply) {
QJsonObject responseObject = QJsonDocument::fromJson(requestReply.readAll()).object();
QJsonObject dataObject = responseObject["data"].toObject();
- goToAddressFromObject(dataObject.toVariantMap(), requestReply);
+ if (!dataObject.isEmpty()) {
+ goToAddressFromObject(dataObject.toVariantMap(), requestReply);
+ } else if (responseObject.contains(DATA_OBJECT_DOMAIN_KEY)) {
+ goToAddressFromObject(responseObject.toVariantMap(), requestReply);
+ }
emit lookupResultsFinished();
}
@@ -180,6 +191,8 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
QVariantMap locationMap;
if (dataObject.contains(DATA_OBJECT_PLACE_KEY)) {
locationMap = dataObject[DATA_OBJECT_PLACE_KEY].toMap();
+ } else if (dataObject.contains(DATA_OBJECT_DOMAIN_KEY)) {
+ locationMap = dataObject;
} else {
locationMap = dataObject[DATA_OBJECT_USER_LOCATION_KEY].toMap();
}
@@ -206,6 +219,10 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress);
+ const QString DOMAIN_ID_KEY = "id";
+ QString domainIDString = domainObject[DOMAIN_ID_KEY].toString();
+ QUuid domainID(domainIDString);
+
if (domainObject.contains(DOMAIN_NETWORK_ADDRESS_KEY)) {
QString domainHostname = domainObject[DOMAIN_NETWORK_ADDRESS_KEY].toString();
@@ -219,10 +236,6 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
} else {
QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString();
- const QString DOMAIN_ID_KEY = "id";
- QString domainIDString = domainObject[DOMAIN_ID_KEY].toString();
- QUuid domainID(domainIDString);
-
qCDebug(networking) << "Possible domain change required to connect to domain with ID" << domainID
<< "via ice-server at" << iceServerAddress;
@@ -235,8 +248,12 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
// set our current root place name to the name that came back
const QString PLACE_NAME_KEY = "name";
- QString newRootPlaceName = rootMap[PLACE_NAME_KEY].toString();
- setRootPlaceName(newRootPlaceName);
+ QString placeName = rootMap[PLACE_NAME_KEY].toString();
+ if (!placeName.isEmpty()) {
+ setHost(placeName);
+ } else {
+ setHost(domainIDString);
+ }
// check if we had a path to override the path returned
QString overridePath = reply.property(OVERRIDE_PATH_KEY).toString();
@@ -304,6 +321,24 @@ void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const Q
QByteArray(), NULL, requestParams);
}
+const QString GET_DOMAIN_ID = "/api/v1/domains/%1";
+
+void AddressManager::attemptDomainIDLookup(const QString& lookupString, const QString& overridePath) {
+ // assume this is a domain ID and see if we can get any info on it
+ QString domainID = QUrl::toPercentEncoding(lookupString);
+
+ QVariantMap requestParams;
+ if (!overridePath.isEmpty()) {
+ requestParams.insert(OVERRIDE_PATH_KEY, overridePath);
+ }
+
+ AccountManager::getInstance().sendRequest(GET_DOMAIN_ID.arg(domainID),
+ AccountManagerAuth::None,
+ QNetworkAccessManager::GetOperation,
+ apiCallbackParameters(),
+ QByteArray(), NULL, requestParams);
+}
+
bool AddressManager::handleNetworkAddress(const QString& lookupString) {
const QString IP_ADDRESS_REGEX_STRING = "^((?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}"
"(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))(?::(\\d{1,5}))?$";
@@ -335,7 +370,7 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString) {
quint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT;
if (!hostnameRegex.cap(2).isEmpty()) {
- domainPort = (qint16) hostnameRegex.cap(2).toInt();
+ domainPort = (qint16)hostnameRegex.cap(2).toInt();
}
emit lookupResultsFinished();
@@ -347,6 +382,14 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString) {
return false;
}
+bool AddressManager::handleDomainID(const QString& host) {
+ const QString UUID_REGEX_STRING = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
+
+ QRegExp domainIDRegex(UUID_REGEX_STRING, Qt::CaseInsensitive);
+
+ return (domainIDRegex.indexIn(host) != -1);
+}
+
void AddressManager::handlePath(const QString& path) {
if (!handleViewpoint(path)) {
qCDebug(networking) << "User entered path could not be handled as a viewpoint - " << path <<
@@ -422,16 +465,16 @@ bool AddressManager::handleUsername(const QString& lookupString) {
return false;
}
-void AddressManager::setRootPlaceName(const QString& rootPlaceName) {
- if (rootPlaceName != _rootPlaceName) {
- _rootPlaceName = rootPlaceName;
- emit rootPlaceNameChanged(_rootPlaceName);
+void AddressManager::setHost(const QString& host) {
+ if (host != _host) {
+ _host = host;
+ emit hostChanged(_host);
}
}
void AddressManager::setDomainInfo(const QString& hostname, quint16 port) {
- _rootPlaceName = hostname;
+ _host = hostname;
_rootPlaceID = QUuid();
qCDebug(networking) << "Possible domain change required to connect to domain at" << hostname << "on" << port;
diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h
index 5831d62603..2b587a9bd7 100644
--- a/libraries/networking/src/AddressManager.h
+++ b/libraries/networking/src/AddressManager.h
@@ -35,7 +35,7 @@ class AddressManager : public QObject, public Dependency {
Q_PROPERTY(bool isConnected READ isConnected)
Q_PROPERTY(QUrl href READ currentAddress)
Q_PROPERTY(QString protocol READ getProtocol)
- Q_PROPERTY(QString hostname READ getRootPlaceName)
+ Q_PROPERTY(QString hostname READ getHost)
Q_PROPERTY(QString pathname READ currentPath)
public:
bool isConnected();
@@ -46,10 +46,11 @@ public:
const QUuid& getRootPlaceID() const { return _rootPlaceID; }
- const QString& getRootPlaceName() const { return _rootPlaceName; }
- void setRootPlaceName(const QString& rootPlaceName);
+ const QString& getHost() const { return _host; }
+ void setHost(const QString& host);
void attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath = QString());
+ void attemptDomainIDLookup(const QString& lookupString, const QString& overridePath = QString());
void setPositionGetter(PositionGetter positionGetter) { _positionGetter = positionGetter; }
void setOrientationGetter(OrientationGetter orientationGetter) { _orientationGetter = orientationGetter; }
@@ -78,7 +79,7 @@ signals:
bool hasOrientationChange, const glm::quat& newOrientation,
bool shouldFaceLocation);
void pathChangeRequired(const QString& newPath);
- void rootPlaceNameChanged(const QString& newRootPlaceName);
+ void hostChanged(const QString& newHost);
protected:
AddressManager();
private slots:
@@ -95,8 +96,9 @@ private:
void handlePath(const QString& path);
bool handleViewpoint(const QString& viewpointString, bool shouldFace = false);
bool handleUsername(const QString& lookupString);
+ bool handleDomainID(const QString& host);
- QString _rootPlaceName;
+ QString _host;
QUuid _rootPlaceID;
PositionGetter _positionGetter;
OrientationGetter _orientationGetter;
diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp
index e210ee6f6e..9e3e924933 100644
--- a/libraries/script-engine/src/AudioScriptingInterface.cpp
+++ b/libraries/script-engine/src/AudioScriptingInterface.cpp
@@ -28,19 +28,19 @@ AudioScriptingInterface& AudioScriptingInterface::getInstance() {
AudioScriptingInterface::AudioScriptingInterface() :
_localAudioInterface(NULL)
{
-
+
}
ScriptAudioInjector* AudioScriptingInterface::playSound(Sound* sound, const AudioInjectorOptions& injectorOptions) {
if (QThread::currentThread() != thread()) {
ScriptAudioInjector* injector = NULL;
-
+
QMetaObject::invokeMethod(this, "playSound", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(ScriptAudioInjector*, injector),
Q_ARG(Sound*, sound), Q_ARG(const AudioInjectorOptions&, injectorOptions));
return injector;
}
-
+
if (sound) {
// stereo option isn't set from script, this comes from sound metadata or filename
AudioInjectorOptions optionsCopy = injectorOptions;
diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h
index bbc9a57db8..d74e1ed1e0 100644
--- a/libraries/script-engine/src/AudioScriptingInterface.h
+++ b/libraries/script-engine/src/AudioScriptingInterface.h
@@ -22,22 +22,23 @@ class AudioScriptingInterface : public QObject {
Q_OBJECT
public:
static AudioScriptingInterface& getInstance();
-
+
void setLocalAudioInterface(AbstractAudioInterface* audioInterface) { _localAudioInterface = audioInterface; }
-
+
protected:
// this method is protected to stop C++ callers from calling, but invokable from script
Q_INVOKABLE ScriptAudioInjector* playSound(Sound* sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions());
-
+
Q_INVOKABLE void injectGeneratedNoise(bool inject);
Q_INVOKABLE void selectPinkNoise();
Q_INVOKABLE void selectSine440();
Q_INVOKABLE void setStereoInput(bool stereo);
-
+
signals:
void mutedByMixer();
void environmentMuted();
+ void receivedFirstPacket();
private:
AudioScriptingInterface();
diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp
index 84c8ae4939..79e83e9b40 100644
--- a/libraries/shared/src/PathUtils.cpp
+++ b/libraries/shared/src/PathUtils.cpp
@@ -18,7 +18,7 @@
#include "PathUtils.h"
-QString& PathUtils::resourcesPath() {
+const QString& PathUtils::resourcesPath() {
#ifdef Q_OS_MAC
static QString staticResourcePath = QCoreApplication::applicationDirPath() + "/../Resources/";
#else
diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h
index 6b6893574b..12b1d57641 100644
--- a/libraries/shared/src/PathUtils.h
+++ b/libraries/shared/src/PathUtils.h
@@ -12,12 +12,17 @@
#ifndef hifi_PathUtils_h
#define hifi_PathUtils_h
+#include
-#include
+#include "DependencyManager.h"
-namespace PathUtils {
- QString& resourcesPath();
-}
+class PathUtils : public QObject, public Dependency {
+ Q_OBJECT
+ SINGLETON_DEPENDENCY
+ Q_PROPERTY(QString resources READ resourcesPath)
+public:
+ static const QString& resourcesPath();
+};
QString fileNameWithoutExtension(const QString& fileName, const QVector possibleExtensions);
QString findMostRecentFileExtension(const QString& originalFileName, QVector possibleExtensions);