diff --git a/examples/grab.js b/examples/grab.js
index 7ed69e9664..593c671506 100644
--- a/examples/grab.js
+++ b/examples/grab.js
@@ -13,6 +13,7 @@
var isGrabbing = false;
var grabbedEntity = null;
+var actionID = null;
var prevMouse = {};
var deltaMouse = {
z: 0
@@ -87,7 +88,6 @@ function mousePressEvent(event) {
Entities.editEntity(grabbedEntity, {
gravity: {x: 0, y: 0, z: 0}
});
-
}
}
@@ -110,7 +110,10 @@ function updateDropLine(position) {
function mouseReleaseEvent() {
if (isGrabbing) {
+ // Controller.mouseMoveEvent.disconnect(mouseMoveEvent);
isGrabbing = false;
+ Entities.deleteAction(grabbedEntity, actionID);
+ actionID = null;
// only restore the original gravity if it's not zero. This is to avoid...
// 1. interface A grabs an entity and locally saves off its gravity
@@ -228,23 +231,28 @@ function update(deltaTime) {
}
if (shouldRotate) {
angularVelocity = Vec3.subtract(angularVelocity, Vec3.multiply(angularVelocity, ANGULAR_DAMPING_RATE));
+ Entities.editEntity(grabbedEntity, {
+ rotation: currentRotation,
+ angularVelocity: angularVelocity
+ });
} else {
angularVelocity = entityProps.angularVelocity;
}
- Entities.editEntity(grabbedEntity, {
- position: currentPosition,
- rotation: currentRotation,
- velocity: newVelocity,
- angularVelocity: angularVelocity
- });
+ var newSpeed = Vec3.length(newVelocity);
+ if (!actionID) {
+ actionID = Entities.addAction("pull-to-point", grabbedEntity, {target: targetPosition, speed: newSpeed});
+ } else {
+ Entities.updateAction(grabbedEntity, actionID, {target: targetPosition, speed: newSpeed});
+ }
+
updateDropLine(targetPosition);
}
}
-Controller.mouseMoveEvent.connect(mouseMoveEvent);
Controller.mousePressEvent.connect(mousePressEvent);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
+Controller.mouseMoveEvent.connect(mouseMoveEvent);
Controller.keyPressEvent.connect(keyPressEvent);
Controller.keyReleaseEvent.connect(keyReleaseEvent);
Script.update.connect(update);
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/src/Application.cpp b/interface/src/Application.cpp
index c58b10880c..ec521ea7d8 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -482,7 +482,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
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
@@ -2477,8 +2477,9 @@ void Application::update(float deltaTime) {
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
-
- DependencyManager::get()->updateOtherAvatars(deltaTime); //loop through all the other avatars and simulate them...
+
+ //loop through all the other avatars and simulate them...
+ DependencyManager::get()->updateOtherAvatars(deltaTime);
updateCamera(deltaTime); // handle various camera tweaks like off axis projection
updateDialogs(deltaTime); // update various stats dialogs if present
@@ -2492,6 +2493,7 @@ void Application::update(float deltaTime) {
_physicsEngine.deleteObjects(_entitySimulation.getObjectsToDelete());
_physicsEngine.addObjects(_entitySimulation.getObjectsToAdd());
_physicsEngine.changeObjects(_entitySimulation.getObjectsToChange());
+ _entitySimulation.applyActionChanges();
_entitySimulation.unlock();
AvatarManager* avatarManager = DependencyManager::get().data();
@@ -2514,7 +2516,8 @@ void Application::update(float deltaTime) {
if (!_aboutToQuit) {
PerformanceTimer perfTimer("entities");
- // Collision events (and their scripts) must not be handled when we're locked, above. (That would risk deadlock.)
+ // Collision events (and their scripts) must not be handled when we're locked, above. (That would risk
+ // deadlock.)
_entitySimulation.handleCollisionEvents(collisionEvents);
// NOTE: the _entities.update() call below will wait for lock
// and will simulate entity motion (the EntityTree has been given an EntitySimulation).
@@ -2527,11 +2530,12 @@ void Application::update(float deltaTime) {
PerformanceTimer perfTimer("overlays");
_overlays.update(deltaTime);
}
-
+
{
PerformanceTimer perfTimer("myAvatar");
updateMyAvatarLookAtPosition();
- DependencyManager::get()->updateMyAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes
+ // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes
+ DependencyManager::get()->updateMyAvatar(deltaTime);
}
{
@@ -3760,7 +3764,7 @@ 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();
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/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp
new file mode 100644
index 0000000000..f26dd006ff
--- /dev/null
+++ b/libraries/entities/src/EntityActionInterface.cpp
@@ -0,0 +1,101 @@
+//
+// EntityActionInterface.cpp
+// libraries/entities/src
+//
+// Created by Seth Alves on 2015-6-4
+// 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
+//
+
+#include "EntityItem.h"
+
+#include "EntityActionInterface.h"
+
+
+EntityActionType EntityActionInterface::actionTypeFromString(QString actionTypeString) {
+ QString normalizedActionTypeString = actionTypeString.toLower().remove('-').remove('_');
+ if (normalizedActionTypeString == "none") {
+ return ACTION_TYPE_NONE;
+ }
+ if (normalizedActionTypeString == "pulltopoint") {
+ return ACTION_TYPE_PULL_TO_POINT;
+ }
+
+ qDebug() << "Warning -- EntityActionInterface::actionTypeFromString got unknown action-type name" << actionTypeString;
+ return ACTION_TYPE_NONE;
+}
+
+QString EntityActionInterface::actionTypeToString(EntityActionType actionType) {
+ switch(actionType) {
+ case ACTION_TYPE_NONE:
+ return "none";
+ case ACTION_TYPE_PULL_TO_POINT:
+ return "pullToPoint";
+ }
+ assert(false);
+ return "none";
+}
+
+glm::vec3 EntityActionInterface::extractVec3Argument(QString objectName, QVariantMap arguments,
+ QString argumentName, bool& ok) {
+ if (!arguments.contains(argumentName)) {
+ qDebug() << objectName << "requires argument:" << argumentName;
+ ok = false;
+ return vec3();
+ }
+
+ QVariant resultV = arguments[argumentName];
+ if (resultV.type() != (QVariant::Type) QMetaType::QVariantMap) {
+ qDebug() << objectName << "argument" << argumentName << "must be a map";
+ ok = false;
+ return vec3();
+ }
+
+ QVariantMap resultVM = resultV.toMap();
+ if (!resultVM.contains("x") || !resultVM.contains("y") || !resultVM.contains("z")) {
+ qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z";
+ ok = false;
+ return vec3();
+ }
+
+ QVariant xV = resultVM["x"];
+ QVariant yV = resultVM["y"];
+ QVariant zV = resultVM["z"];
+
+ bool xOk = true;
+ bool yOk = true;
+ bool zOk = true;
+ float x = xV.toFloat(&xOk);
+ float y = yV.toFloat(&yOk);
+ float z = zV.toFloat(&zOk);
+ if (!xOk || !yOk || !zOk) {
+ qDebug() << objectName << "argument" << argumentName << "must be a map with keys of x, y, z and values of type float.";
+ ok = false;
+ return vec3();
+ }
+
+ return vec3(x, y, z);
+}
+
+
+float EntityActionInterface::extractFloatArgument(QString objectName, QVariantMap arguments,
+ QString argumentName, bool& ok) {
+ if (!arguments.contains(argumentName)) {
+ qDebug() << objectName << "requires argument:" << argumentName;
+ ok = false;
+ return 0.0f;
+ }
+
+ QVariant vV = arguments[argumentName];
+ bool vOk = true;
+ float v = vV.toFloat(&vOk);
+
+ if (!vOk) {
+ ok = false;
+ return 0.0f;
+ }
+
+ return v;
+}
diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h
new file mode 100644
index 0000000000..74efae3239
--- /dev/null
+++ b/libraries/entities/src/EntityActionInterface.h
@@ -0,0 +1,49 @@
+//
+// EntityActionInterface.h
+// libraries/entities/src
+//
+// Created by Seth Alves on 2015-6-2
+// 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
+//
+
+#ifndef hifi_EntityActionInterface_h
+#define hifi_EntityActionInterface_h
+
+#include
+
+class EntitySimulation;
+
+enum EntityActionType {
+ // keep these synchronized with actionTypeFromString and actionTypeToString
+ ACTION_TYPE_NONE,
+ ACTION_TYPE_PULL_TO_POINT
+};
+
+
+class EntityActionInterface {
+public:
+ EntityActionInterface() { }
+ virtual ~EntityActionInterface() { }
+ virtual const QUuid& getID() const = 0;
+ virtual void removeFromSimulation(EntitySimulation* simulation) const = 0;
+ virtual const EntityItemPointer& getOwnerEntity() const = 0;
+ virtual void setOwnerEntity(const EntityItemPointer ownerEntity) = 0;
+ virtual bool updateArguments(QVariantMap arguments) = 0;
+ // virtual QByteArray serialize() = 0;
+ // static EntityActionPointer deserialize(EntityItemPointer ownerEntity, QByteArray data);
+
+ static EntityActionType actionTypeFromString(QString actionTypeString);
+ static QString actionTypeToString(EntityActionType actionType);
+
+protected:
+
+ static glm::vec3 extractVec3Argument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok);
+ static float extractFloatArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok);
+};
+
+typedef std::shared_ptr EntityActionPointer;
+
+#endif // hifi_EntityActionInterface_h
diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp
index 6b8e1833d7..4a10aad95d 100644
--- a/libraries/entities/src/EntityItem.cpp
+++ b/libraries/entities/src/EntityItem.cpp
@@ -25,6 +25,7 @@
#include "EntityItem.h"
#include "EntitiesLogging.h"
#include "EntityTree.h"
+#include "EntitySimulation.h"
bool EntityItem::_sendPhysicsUpdates = true;
@@ -1334,3 +1335,46 @@ void EntityItem::updateSimulatorID(const QUuid& value) {
_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID;
}
}
+
+bool EntityItem::addAction(EntitySimulation* simulation, EntityActionPointer action) {
+ assert(action);
+ const QUuid& actionID = action->getID();
+ assert(!_objectActions.contains(actionID) || _objectActions[actionID] == action);
+ _objectActions[actionID] = action;
+
+ assert(action->getOwnerEntity().get() == this);
+
+ simulation->addAction(action);
+
+ return false;
+}
+
+bool EntityItem::updateAction(EntitySimulation* simulation, const QUuid& actionID, const QVariantMap& arguments) {
+ if (!_objectActions.contains(actionID)) {
+ return false;
+ }
+ EntityActionPointer action = _objectActions[actionID];
+ return action->updateArguments(arguments);
+}
+
+bool EntityItem::removeAction(EntitySimulation* simulation, const QUuid& actionID) {
+ if (_objectActions.contains(actionID)) {
+ EntityActionPointer action = _objectActions[actionID];
+ _objectActions.remove(actionID);
+ action->setOwnerEntity(nullptr);
+ action->removeFromSimulation(simulation);
+ return true;
+ }
+ return false;
+}
+
+void EntityItem::clearActions(EntitySimulation* simulation) {
+ QHash::iterator i = _objectActions.begin();
+ while (i != _objectActions.end()) {
+ const QUuid id = i.key();
+ EntityActionPointer action = _objectActions[id];
+ i = _objectActions.erase(i);
+ action->setOwnerEntity(nullptr);
+ action->removeFromSimulation(simulation);
+ }
+}
diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h
index 638d9bf1d7..77a6627853 100644
--- a/libraries/entities/src/EntityItem.h
+++ b/libraries/entities/src/EntityItem.h
@@ -25,9 +25,10 @@
#include
#include
-#include "EntityItemID.h"
-#include "EntityItemProperties.h"
-#include "EntityItemPropertiesDefaults.h"
+#include "EntityItemID.h"
+#include "EntityItemProperties.h"
+#include "EntityItemPropertiesDefaults.h"
+#include "EntityActionInterface.h"
#include "EntityTypes.h"
class EntitySimulation;
@@ -95,7 +96,7 @@ public:
};
DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly
-
+
EntityItem(const EntityItemID& entityItemID);
EntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
virtual ~EntityItem();
@@ -107,7 +108,7 @@ public:
// methods for getting/setting all properties of an entity
virtual EntityItemProperties getProperties() const;
-
+
/// returns true if something changed
virtual bool setProperties(const EntityItemProperties& properties);
@@ -121,7 +122,7 @@ public:
/// Last edited time of this entity universal usecs
quint64 getLastEdited() const { return _lastEdited; }
- void setLastEdited(quint64 lastEdited)
+ void setLastEdited(quint64 lastEdited)
{ _lastEdited = _lastUpdated = lastEdited; _changedOnServer = glm::max(lastEdited, _changedOnServer); }
float getEditedAgo() const /// Elapsed seconds since this entity was last edited
{ return (float)(usecTimestampNow() - getLastEdited()) / (float)USECS_PER_SECOND; }
@@ -135,26 +136,26 @@ public:
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
-
+
virtual OctreeElement::AppendState appendEntityData(OctreePacketData* packetData, EncodeBitstreamParams& params,
EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData) const;
- virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
+ virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData,
EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags,
EntityPropertyFlags& propertiesDidntFit,
- int& propertyCount,
+ int& propertyCount,
OctreeElement::AppendState& appendState) const { /* do nothing*/ };
- static EntityItemID readEntityItemIDFromBuffer(const unsigned char* data, int bytesLeftToRead,
+ static EntityItemID readEntityItemIDFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args);
virtual int readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args);
- virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
+ virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args,
- EntityPropertyFlags& propertyFlags, bool overwriteLocalData)
+ EntityPropertyFlags& propertyFlags, bool overwriteLocalData)
{ return 0; }
virtual bool addToScene(EntityItemPointer self, std::shared_ptr scene,
@@ -170,7 +171,7 @@ public:
// perform update
virtual void update(const quint64& now) { _lastUpdated = now; }
quint64 getLastUpdated() const { return _lastUpdated; }
-
+
// perform linear extrapolation for SimpleEntitySimulation
void simulate(const quint64& now);
void simulateKinematicMotion(float timeElapsed, bool setFlags=true);
@@ -178,10 +179,10 @@ public:
virtual bool needsToCallUpdate() const { return false; }
virtual void debugDump() const;
-
+
virtual bool supportsDetailedRayIntersection() const { return false; }
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
- bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
+ bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
void** intersectedObject, bool precisionPicking) const { return true; }
// attributes applicable to all entity types
@@ -207,7 +208,6 @@ public:
inline const glm::vec3& getDimensions() const { return _transform.getScale(); }
virtual void setDimensions(const glm::vec3& value);
-
float getGlowLevel() const { return _glowLevel; }
void setGlowLevel(float glowLevel) { _glowLevel = glowLevel; }
@@ -231,7 +231,7 @@ public:
const glm::vec3& getAcceleration() const { return _acceleration; } /// get acceleration in meters/second/second
void setAcceleration(const glm::vec3& value) { _acceleration = value; } /// acceleration in meters/second/second
bool hasAcceleration() const { return _acceleration != ENTITY_ITEM_ZERO_VEC3; }
-
+
float getDamping() const { return _damping; }
void setDamping(float value) { _damping = value; }
@@ -253,7 +253,7 @@ public:
/// is this entity mortal, in that it has a lifetime set, and will automatically be deleted when that lifetime expires
bool isMortal() const { return _lifetime != ENTITY_ITEM_IMMORTAL_LIFETIME; }
-
+
/// age of this entity in seconds
float getAge() const { return (float)(usecTimestampNow() - _created) / (float)USECS_PER_SECOND; }
bool lifetimeHasExpired() const;
@@ -272,7 +272,7 @@ public:
const glm::vec3& getRegistrationPoint() const { return _registrationPoint; } /// registration point as ratio of entity
/// registration point as ratio of entity
- void setRegistrationPoint(const glm::vec3& value)
+ void setRegistrationPoint(const glm::vec3& value)
{ _registrationPoint = glm::clamp(value, 0.0f, 1.0f); }
const glm::vec3& getAngularVelocity() const { return _angularVelocity; }
@@ -300,7 +300,7 @@ public:
bool getLocked() const { return _locked; }
void setLocked(bool value) { _locked = value; }
-
+
const QString& getUserData() const { return _userData; }
void setUserData(const QString& value) { _userData = value; }
@@ -308,13 +308,13 @@ public:
void setSimulatorID(const QUuid& value);
void updateSimulatorID(const QUuid& value);
quint64 getSimulatorIDChangedTime() const { return _simulatorIDChangedTime; }
-
+
const QString& getMarketplaceID() const { return _marketplaceID; }
void setMarketplaceID(const QString& value) { _marketplaceID = value; }
-
- // TODO: get rid of users of getRadius()...
+
+ // TODO: get rid of users of getRadius()...
float getRadius() const;
-
+
virtual bool contains(const glm::vec3& point) const;
virtual bool isReadyToComputeShape() { return true; }
@@ -345,11 +345,11 @@ public:
uint32_t getDirtyFlags() const { return _dirtyFlags; }
void clearDirtyFlags(uint32_t mask = 0xffff) { _dirtyFlags &= ~mask; }
-
+
bool isMoving() const;
void* getPhysicsInfo() const { return _physicsInfo; }
-
+
void setPhysicsInfo(void* data) { _physicsInfo = data; }
EntityTreeElement* getElement() const { return _element; }
@@ -365,12 +365,18 @@ public:
void getAllTerseUpdateProperties(EntityItemProperties& properties) const;
+ bool addAction(EntitySimulation* simulation, EntityActionPointer action);
+ bool updateAction(EntitySimulation* simulation, const QUuid& actionID, const QVariantMap& arguments);
+ bool removeAction(EntitySimulation* simulation, const QUuid& actionID);
+ void clearActions(EntitySimulation* simulation);
+
protected:
static bool _sendPhysicsUpdates;
EntityTypes::EntityType _type;
QUuid _id;
- quint64 _lastSimulated; // last time this entity called simulate(), this includes velocity, angular velocity, and physics changes
+ quint64 _lastSimulated; // last time this entity called simulate(), this includes velocity, angular velocity,
+ // and physics changes
quint64 _lastUpdated; // last time this entity called update(), this includes animations and non-physics changes
quint64 _lastEdited; // last official local or remote edit time
quint64 _lastBroadcast; // the last time we sent an edit packet about this entity
@@ -419,12 +425,12 @@ protected:
//
// damping = 1 - exp(-1 / timescale)
//
-
- // NOTE: Radius support is obsolete, but these private helper functions are available for this class to
+
+ // NOTE: Radius support is obsolete, but these private helper functions are available for this class to
// parse old data streams
-
+
/// set radius in domain scale units (0.0 - 1.0) this will also reset dimensions to be equal for each axis
- void setRadius(float value);
+ void setRadius(float value);
// DirtyFlags are set whenever a property changes that the EntitySimulation needs to know about.
uint32_t _dirtyFlags; // things that have changed from EXTERNAL changes (via script or packet) but NOT from simulation
@@ -433,6 +439,8 @@ protected:
EntityTreeElement* _element = nullptr; // set by EntityTreeElement
void* _physicsInfo = nullptr; // set by EntitySimulation
bool _simulated; // set by EntitySimulation
+
+ QHash _objectActions;
};
#endif // hifi_EntityItem_h
diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp
index 5a897f7fe7..836e7f5225 100644
--- a/libraries/entities/src/EntityScriptingInterface.cpp
+++ b/libraries/entities/src/EntityScriptingInterface.cpp
@@ -11,13 +11,14 @@
#include
-#include "EntityScriptingInterface.h"
#include "EntityTree.h"
#include "LightEntityItem.h"
#include "ModelEntityItem.h"
#include "ZoneEntityItem.h"
#include "EntitiesLogging.h"
+#include "EntitySimulation.h"
+#include "EntityScriptingInterface.h"
EntityScriptingInterface::EntityScriptingInterface() :
_entityTree(NULL)
@@ -83,7 +84,8 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
entity->setLastBroadcast(usecTimestampNow());
// This Node is creating a new object. If it's in motion, set this Node as the simulator.
bidForSimulationOwnership(propertiesWithSimID);
- entity->setSimulatorID(propertiesWithSimID.getSimulatorID()); // and make note of it now, so we can act on it right away.
+ // and make note of it now, so we can act on it right away.
+ entity->setSimulatorID(propertiesWithSimID.getSimulatorID());
} else {
qCDebug(entities) << "script failed to add new Entity to local Octree";
success = false;
@@ -105,7 +107,7 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit
_entityTree->lockForRead();
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(identity));
-
+
if (entity) {
results = entity->getProperties();
@@ -124,7 +126,7 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit
}
_entityTree->unlock();
}
-
+
return results;
}
@@ -228,7 +230,7 @@ QVector EntityScriptingInterface::findEntities(const glm::vec3& center, f
QVector entities;
_entityTree->findEntities(center, radius, entities);
_entityTree->unlock();
-
+
foreach (EntityItemPointer entity, entities) {
result << entity->getEntityItemID();
}
@@ -244,7 +246,7 @@ QVector EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corn
QVector entities;
_entityTree->findEntities(box, entities);
_entityTree->unlock();
-
+
foreach (EntityItemPointer entity, entities) {
result << entity->getEntityItemID();
}
@@ -401,7 +403,6 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra
}
}
-
bool EntityScriptingInterface::setVoxels(QUuid entityID,
std::function actor) {
if (!_entityTree) {
@@ -439,23 +440,83 @@ bool EntityScriptingInterface::setVoxels(QUuid entityID,
return true;
}
-
bool EntityScriptingInterface::setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value) {
return setVoxels(entityID, [center, radius, value](PolyVoxEntityItem& polyVoxEntity) {
polyVoxEntity.setSphere(center, radius, value);
});
}
-
bool EntityScriptingInterface::setVoxel(QUuid entityID, const glm::vec3& position, int value) {
return setVoxels(entityID, [position, value](PolyVoxEntityItem& polyVoxEntity) {
polyVoxEntity.setVoxelInVolume(position, value);
});
}
-
bool EntityScriptingInterface::setAllVoxels(QUuid entityID, int value) {
return setVoxels(entityID, [value](PolyVoxEntityItem& polyVoxEntity) {
polyVoxEntity.setAll(value);
});
}
+
+
+bool EntityScriptingInterface::actionWorker(const QUuid& entityID,
+ std::function actor) {
+ if (!_entityTree) {
+ return false;
+ }
+
+ _entityTree->lockForWrite();
+
+ EntitySimulation* simulation = _entityTree->getSimulation();
+ EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
+ if (!entity) {
+ qDebug() << "actionWorker -- unknown entity" << entityID;
+ _entityTree->unlock();
+ return false;
+ }
+
+ if (!simulation) {
+ qDebug() << "actionWorker -- no simulation" << entityID;
+ _entityTree->unlock();
+ return false;
+ }
+
+ bool success = actor(simulation, entity);
+ _entityTree->unlock();
+ return success;
+}
+
+
+QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
+ const QUuid& entityID,
+ const QVariantMap& arguments) {
+ QUuid actionID = QUuid::createUuid();
+ bool success = actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
+ EntityActionType actionType = EntityActionInterface::actionTypeFromString(actionTypeString);
+ if (actionType == ACTION_TYPE_NONE) {
+ return false;
+ }
+ if (simulation->actionFactory(actionType, actionID, entity, arguments)) {
+ return true;
+ }
+ return false;
+ });
+ if (success) {
+ return actionID;
+ }
+ return QUuid();
+}
+
+
+bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid& actionID, const QVariantMap& arguments) {
+ return actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
+ return entity->updateAction(simulation, actionID, arguments);
+ });
+}
+
+
+bool EntityScriptingInterface::deleteAction(const QUuid& entityID, const QUuid& actionID) {
+ return actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
+ return entity->removeAction(simulation, actionID);
+ });
+}
diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h
index 738a011b15..c6bc43c8c6 100644
--- a/libraries/entities/src/EntityScriptingInterface.h
+++ b/libraries/entities/src/EntityScriptingInterface.h
@@ -54,14 +54,14 @@ class EntityScriptingInterface : public OctreeScriptingInterface, public Depende
Q_OBJECT
public:
EntityScriptingInterface();
-
+
EntityEditPacketSender* getEntityPacketSender() const { return (EntityEditPacketSender*)getPacketSender(); }
virtual NodeType_t getServerNodeType() const { return NodeType::EntityServer; }
virtual OctreeEditPacketSender* createPacketSender() { return new EntityEditPacketSender(); }
void setEntityTree(EntityTree* modelTree);
EntityTree* getEntityTree(EntityTree*) { return _entityTree; }
-
+
public slots:
// returns true if the DomainServer will allow this Node/Avatar to make changes
@@ -88,11 +88,11 @@ public slots:
/// will return a EntityItemID.isKnownID = false if no models are in the radius
/// this function will not find any models in script engine contexts which don't have access to models
Q_INVOKABLE QUuid findClosestEntity(const glm::vec3& center, float radius) const;
-
+
/// finds models within the search sphere specified by the center point and radius
/// this function will not find any models in script engine contexts which don't have access to models
Q_INVOKABLE QVector findEntities(const glm::vec3& center, float radius) const;
-
+
/// finds models within the search sphere specified by the center point and radius
/// this function will not find any models in script engine contexts which don't have access to models
Q_INVOKABLE QVector findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const;
@@ -118,13 +118,16 @@ public slots:
Q_INVOKABLE void setSendPhysicsUpdates(bool value);
Q_INVOKABLE bool getSendPhysicsUpdates() const;
- bool setVoxels(QUuid entityID, std::function actor);
Q_INVOKABLE bool setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value);
Q_INVOKABLE bool setVoxel(QUuid entityID, const glm::vec3& position, int value);
Q_INVOKABLE bool setAllVoxels(QUuid entityID, int value);
Q_INVOKABLE void dumpTree() const;
+ Q_INVOKABLE QUuid addAction(const QString& actionTypeString, const QUuid& entityID, const QVariantMap& arguments);
+ Q_INVOKABLE bool updateAction(const QUuid& entityID, const QUuid& actionID, const QVariantMap& arguments);
+ Q_INVOKABLE bool deleteAction(const QUuid& entityID, const QUuid& actionID);
+
signals:
void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
@@ -152,6 +155,8 @@ signals:
void clearingEntities();
private:
+ bool actionWorker(const QUuid& entityID, std::function actor);
+ bool setVoxels(QUuid entityID, std::function actor);
void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties);
/// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode
diff --git a/libraries/entities/src/EntitySimulation.h b/libraries/entities/src/EntitySimulation.h
index bce16ba1c2..0c9b3efee6 100644
--- a/libraries/entities/src/EntitySimulation.h
+++ b/libraries/entities/src/EntitySimulation.h
@@ -24,9 +24,9 @@
typedef QSet SetOfEntities;
typedef QVector VectorOfEntities;
-// the EntitySimulation needs to know when these things change on an entity,
+// the EntitySimulation needs to know when these things change on an entity,
// so it can sort EntityItem or relay its state to the PhysicsEngine.
-const int DIRTY_SIMULATION_FLAGS =
+const int DIRTY_SIMULATION_FLAGS =
EntityItem::DIRTY_POSITION |
EntityItem::DIRTY_ROTATION |
EntityItem::DIRTY_LINEAR_VELOCITY |
@@ -56,6 +56,15 @@ public:
friend class EntityTree;
+ virtual EntityActionPointer actionFactory(EntityActionType type,
+ QUuid id,
+ EntityItemPointer ownerEntity,
+ QVariantMap arguments) { return nullptr; }
+ virtual void addAction(EntityActionPointer action) { _actionsToAdd += action; }
+ virtual void removeAction(const QUuid actionID) { _actionsToRemove += actionID; }
+ virtual void removeActions(QList actionIDsToRemove) { _actionsToRemove += actionIDsToRemove; }
+ virtual void applyActionChanges() { _actionsToAdd.clear(); _actionsToRemove.clear(); }
+
protected: // these only called by the EntityTree?
/// \param entity pointer to EntityItem to be added
/// \sideeffect sets relevant backpointers in entity, but maybe later when appropriate data structures are locked
@@ -112,8 +121,12 @@ protected:
SetOfEntities _entitiesToDelete; // entities simulation decided needed to be deleted (EntityTree will actually delete)
SetOfEntities _simpleKinematicEntities; // entities undergoing non-colliding kinematic motion
-private:
+ private:
void moveSimpleKinematics();
+
+ protected:
+ QList _actionsToAdd;
+ QList _actionsToRemove;
};
#endif // hifi_EntitySimulation_h
diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp
index 47eb845daa..363f3c56d7 100644
--- a/libraries/entities/src/EntityTree.cpp
+++ b/libraries/entities/src/EntityTree.cpp
@@ -279,7 +279,7 @@ void EntityTree::setSimulation(EntitySimulation* simulation) {
if (simulation) {
// assert that the simulation's backpointer has already been properly connected
assert(simulation->getEntityTree() == this);
- }
+ }
if (_simulation && _simulation != simulation) {
// It's important to clearEntities() on the simulation since taht will update each
// EntityItem::_simulationState correctly so as to not confuse the next _simulation.
@@ -381,6 +381,7 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator)
}
if (_simulation) {
+ theEntity->clearActions(_simulation);
_simulation->removeEntity(theEntity);
}
}
diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h
index 5897d3e9a8..1ad3ef6b1c 100644
--- a/libraries/entities/src/EntityTree.h
+++ b/libraries/entities/src/EntityTree.h
@@ -31,7 +31,7 @@ public:
class EntityItemFBXService {
public:
virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) = 0;
- virtual const Model* getModelForEntityItem(EntityItemPointer entityItem) = 0;
+ virtual const Model* getModelForEntityItem(EntityItemPointer entityItem) = 0;
virtual const FBXGeometry* getCollisionGeometryForEntity(EntityItemPointer entityItem) = 0;
};
@@ -63,23 +63,23 @@ public:
// own definition. Implement these to allow your octree based server to support editing
virtual bool getWantSVOfileVersions() const { return true; }
virtual PacketType expectedDataPacketType() const { return PacketTypeEntityData; }
- virtual bool canProcessVersion(PacketVersion thisVersion) const
+ virtual bool canProcessVersion(PacketVersion thisVersion) const
{ return thisVersion >= VERSION_ENTITIES_USE_METERS_AND_RADIANS; }
virtual bool handlesEditPacketType(PacketType packetType) const;
virtual int processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength,
const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode);
virtual bool rootElementHasData() const { return true; }
-
+
// the root at least needs to store the number of entities in the packet/buffer
virtual int minimumRequiredRootDataBytes() const { return sizeof(uint16_t); }
virtual bool suppressEmptySubtrees() const { return false; }
virtual void releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const;
virtual bool mustIncludeAllChildData() const { return false; }
- virtual bool versionHasSVOfileBreaks(PacketVersion thisVersion) const
+ virtual bool versionHasSVOfileBreaks(PacketVersion thisVersion) const
{ return thisVersion >= VERSION_ENTITIES_HAS_FILE_BREAKS; }
-
+
virtual void update();
// The newer API...
@@ -111,13 +111,13 @@ public:
/// \param foundEntities[out] vector of EntityItemPointer
/// \remark Side effect: any initial contents in foundEntities will be lost
void findEntities(const glm::vec3& center, float radius, QVector& foundEntities);
-
+
/// finds all entities that touch a cube
/// \param cube the query cube in world-frame (meters)
/// \param foundEntities[out] vector of non-EntityItemPointer
/// \remark Side effect: any initial contents in entities will be lost
void findEntities(const AACube& cube, QVector& foundEntities);
-
+
/// finds all entities that touch a box
/// \param box the query box in world-frame (meters)
/// \param foundEntities[out] vector of non-EntityItemPointer
@@ -129,13 +129,13 @@ public:
bool hasAnyDeletedEntities() const { return _recentlyDeletedEntityItemIDs.size() > 0; }
bool hasEntitiesDeletedSince(quint64 sinceTime);
- bool encodeEntitiesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime,
+ bool encodeEntitiesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime,
unsigned char* packetData, size_t maxLength, size_t& outputLength);
void forgetEntitiesDeletedBefore(quint64 sinceTime);
int processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
int processEraseMessageDetails(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
-
+
EntityItemFBXService* getFBXService() const { return _fbxService; }
void setFBXService(EntityItemFBXService* service) { _fbxService = service; }
const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) {
@@ -144,7 +144,7 @@ public:
const Model* getModelForEntityItem(EntityItemPointer entityItem) {
return _fbxService ? _fbxService->getModelForEntityItem(entityItem) : NULL;
}
-
+
EntityTreeElement* getContainingElement(const EntityItemID& entityItemID) /*const*/;
void setContainingElement(const EntityItemID& entityItemID, EntityTreeElement* element);
void debugDumpMap();
@@ -158,13 +158,14 @@ public:
void emitEntityScriptChanging(const EntityItemID& entityItemID);
void setSimulation(EntitySimulation* simulation);
-
+ EntitySimulation* getSimulation() const { return _simulation; }
+
bool wantEditLogging() const { return _wantEditLogging; }
void setWantEditLogging(bool value) { _wantEditLogging = value; }
bool writeToMap(QVariantMap& entityDescription, OctreeElement* element, bool skipDefaultValues);
bool readFromMap(QVariantMap& entityDescription);
-
+
float getContentsLargestDimension();
signals:
@@ -177,7 +178,7 @@ signals:
private:
void processRemovedEntities(const DeleteEntityOperator& theOperator);
- bool updateEntityWithElement(EntityItemPointer entity, const EntityItemProperties& properties,
+ bool updateEntityWithElement(EntityItemPointer entity, const EntityItemProperties& properties,
EntityTreeElement* containingElement,
const SharedNodePointer& senderNode = SharedNodePointer(nullptr));
static bool findNearPointOperation(OctreeElement* element, void* extraData);
@@ -198,7 +199,7 @@ private:
QHash _entityToElementMap;
EntitySimulation* _simulation;
-
+
bool _wantEditLogging = false;
void maybeNotifyNewCollisionSoundURL(const QString& oldCollisionSoundURL, const QString& newCollisionSoundURL);
};
diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h
index 90fb035d7b..05d1904384 100644
--- a/libraries/entities/src/EntityTreeElement.h
+++ b/libraries/entities/src/EntityTreeElement.h
@@ -33,7 +33,7 @@ public:
_totalItems(0),
_movingItems(0)
{ }
-
+
QList _movingEntities;
int _totalElements;
int _totalItems;
@@ -42,8 +42,8 @@ public:
class EntityTreeElementExtraEncodeData {
public:
- EntityTreeElementExtraEncodeData() :
- elementCompleted(false),
+ EntityTreeElementExtraEncodeData() :
+ elementCompleted(false),
subtreeCompleted(false),
entities() {
memset(childCompleted, 0, sizeof(childCompleted));
@@ -140,7 +140,7 @@ public:
virtual bool canRayIntersect() const { return hasEntities(); }
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
- bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
+ bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
void** intersectedObject, bool precisionPicking, float distanceToElementCube);
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
@@ -148,10 +148,11 @@ public:
const EntityItems& getEntities() const { return *_entityItems; }
EntityItems& getEntities() { return *_entityItems; }
-
+
bool hasEntities() const { return _entityItems ? _entityItems->size() > 0 : false; }
void setTree(EntityTree* tree) { _myTree = tree; }
+ EntityTree* getTree() const { return _myTree; }
bool updateEntity(const EntityItem& entity);
void addEntityItem(EntityItemPointer entity);
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/physics/src/DynamicCharacterController.cpp b/libraries/physics/src/DynamicCharacterController.cpp
index 1fca236f63..ebdba5f754 100644
--- a/libraries/physics/src/DynamicCharacterController.cpp
+++ b/libraries/physics/src/DynamicCharacterController.cpp
@@ -156,7 +156,7 @@ void DynamicCharacterController::playerStep(btCollisionWorld* dynaWorld, btScala
velocityCorrection -= velocityCorrection.dot(_currentUp) * _currentUp;
}
_rigidBody->setLinearVelocity(actualVelocity + tau * velocityCorrection);
- }
+ }
}
}
diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp
new file mode 100644
index 0000000000..6ff4098ba8
--- /dev/null
+++ b/libraries/physics/src/ObjectAction.cpp
@@ -0,0 +1,35 @@
+//
+// ObjectAction.cpp
+// libraries/physcis/src
+//
+// Created by Seth Alves 2015-6-2
+// 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
+//
+
+#include "EntitySimulation.h"
+
+#include "ObjectAction.h"
+
+ObjectAction::ObjectAction(QUuid id, EntityItemPointer ownerEntity) :
+ btActionInterface(),
+ _id(id),
+ _active(false),
+ _ownerEntity(ownerEntity) {
+}
+
+ObjectAction::~ObjectAction() {
+}
+
+void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) {
+ qDebug() << "ObjectAction::updateAction called";
+}
+
+void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) {
+}
+
+void ObjectAction::removeFromSimulation(EntitySimulation* simulation) const {
+ simulation->removeAction(_id);
+}
diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h
new file mode 100644
index 0000000000..10b086c07d
--- /dev/null
+++ b/libraries/physics/src/ObjectAction.h
@@ -0,0 +1,50 @@
+//
+// ObjectAction.h
+// libraries/physcis/src
+//
+// Created by Seth Alves 2015-6-2
+// 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
+//
+// http://bulletphysics.org/Bullet/BulletFull/classbtActionInterface.html
+
+#ifndef hifi_ObjectAction_h
+#define hifi_ObjectAction_h
+
+#include
+
+#include
+
+#include
+
+class ObjectAction : public btActionInterface, public EntityActionInterface {
+public:
+ ObjectAction(QUuid id, EntityItemPointer ownerEntity);
+ virtual ~ObjectAction();
+
+ const QUuid& getID() const { return _id; }
+ virtual void removeFromSimulation(EntitySimulation* simulation) const;
+ virtual const EntityItemPointer& getOwnerEntity() const { return _ownerEntity; }
+ virtual void setOwnerEntity(const EntityItemPointer ownerEntity) { _ownerEntity = ownerEntity; }
+ virtual bool updateArguments(QVariantMap arguments) { return false; }
+
+ // these are from btActionInterface
+ virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep);
+ virtual void debugDraw(btIDebugDraw* debugDrawer);
+
+private:
+ QUuid _id;
+ QReadWriteLock _lock;
+
+protected:
+ bool tryLockForRead() { return _lock.tryLockForRead(); }
+ void lockForWrite() { _lock.lockForWrite(); }
+ void unlock() { _lock.unlock(); }
+
+ bool _active;
+ EntityItemPointer _ownerEntity;
+};
+
+#endif // hifi_ObjectAction_h
diff --git a/libraries/physics/src/ObjectActionPullToPoint.cpp b/libraries/physics/src/ObjectActionPullToPoint.cpp
new file mode 100644
index 0000000000..78f202a24f
--- /dev/null
+++ b/libraries/physics/src/ObjectActionPullToPoint.cpp
@@ -0,0 +1,69 @@
+//
+// ObjectActionPullToPoint.cpp
+// libraries/physics/src
+//
+// Created by Seth Alves 2015-6-2
+// 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
+//
+
+#include "ObjectMotionState.h"
+#include "BulletUtil.h"
+
+#include "ObjectActionPullToPoint.h"
+
+ObjectActionPullToPoint::ObjectActionPullToPoint(QUuid id, EntityItemPointer ownerEntity) :
+ ObjectAction(id, ownerEntity) {
+ #if WANT_DEBUG
+ qDebug() << "ObjectActionPullToPoint::ObjectActionPullToPoint";
+ #endif
+}
+
+ObjectActionPullToPoint::~ObjectActionPullToPoint() {
+ #if WANT_DEBUG
+ qDebug() << "ObjectActionPullToPoint::~ObjectActionPullToPoint";
+ #endif
+}
+
+void ObjectActionPullToPoint::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) {
+ if (!tryLockForRead()) {
+ // don't risk hanging the thread running the physics simulation
+ return;
+ }
+ void* physicsInfo = _ownerEntity->getPhysicsInfo();
+
+ if (_active && physicsInfo) {
+ ObjectMotionState* motionState = static_cast(physicsInfo);
+ btRigidBody* rigidBody = motionState->getRigidBody();
+ if (rigidBody) {
+ glm::vec3 offset = _target - bulletToGLM(rigidBody->getCenterOfMassPosition());
+ float offsetLength = glm::length(offset);
+ if (offsetLength > IGNORE_POSITION_DELTA) {
+ glm::vec3 newVelocity = glm::normalize(offset) * _speed;
+ rigidBody->setLinearVelocity(glmToBullet(newVelocity));
+ rigidBody->activate();
+ } else {
+ rigidBody->setLinearVelocity(glmToBullet(glm::vec3()));
+ }
+ }
+ }
+ unlock();
+}
+
+
+bool ObjectActionPullToPoint::updateArguments(QVariantMap arguments) {
+ bool ok = true;
+ glm::vec3 target = EntityActionInterface::extractVec3Argument("pull-to-point action", arguments, "target", ok);
+ float speed = EntityActionInterface::extractFloatArgument("pull-to-point action", arguments, "speed", ok);
+ if (ok) {
+ lockForWrite();
+ _target = target;
+ _speed = speed;
+ _active = true;
+ unlock();
+ return true;
+ }
+ return false;
+}
diff --git a/libraries/physics/src/ObjectActionPullToPoint.h b/libraries/physics/src/ObjectActionPullToPoint.h
new file mode 100644
index 0000000000..3aca70d640
--- /dev/null
+++ b/libraries/physics/src/ObjectActionPullToPoint.h
@@ -0,0 +1,34 @@
+//
+// ObjectActionPullToPoint.h
+// libraries/physics/src
+//
+// Created by Seth Alves 2015-6-3
+// 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
+//
+
+#ifndef hifi_ObjectActionPullToPoint_h
+#define hifi_ObjectActionPullToPoint_h
+
+#include
+
+#include
+#include "ObjectAction.h"
+
+class ObjectActionPullToPoint : public ObjectAction {
+public:
+ ObjectActionPullToPoint(QUuid id, EntityItemPointer ownerEntity);
+ virtual ~ObjectActionPullToPoint();
+
+ virtual bool updateArguments(QVariantMap arguments);
+ virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep);
+
+private:
+
+ glm::vec3 _target;
+ float _speed;
+};
+
+#endif // hifi_ObjectActionPullToPoint_h
diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp
index 70853fb5f6..711c5e49da 100644
--- a/libraries/physics/src/PhysicalEntitySimulation.cpp
+++ b/libraries/physics/src/PhysicalEntitySimulation.cpp
@@ -9,10 +9,12 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-#include "PhysicalEntitySimulation.h"
#include "PhysicsHelpers.h"
#include "PhysicsLogging.h"
#include "ShapeManager.h"
+#include "ObjectActionPullToPoint.h"
+
+#include "PhysicalEntitySimulation.h"
PhysicalEntitySimulation::PhysicalEntitySimulation() {
}
@@ -232,3 +234,37 @@ void PhysicalEntitySimulation::handleCollisionEvents(CollisionEvents& collisionE
}
}
+EntityActionPointer PhysicalEntitySimulation::actionFactory(EntityActionType type,
+ QUuid id,
+ EntityItemPointer ownerEntity,
+ QVariantMap arguments) {
+ EntityActionPointer action = nullptr;
+ switch (type) {
+ case ACTION_TYPE_NONE:
+ return nullptr;
+ case ACTION_TYPE_PULL_TO_POINT:
+ action = (EntityActionPointer) new ObjectActionPullToPoint(id, ownerEntity);
+ break;
+ }
+
+ bool ok = action->updateArguments(arguments);
+ if (ok) {
+ ownerEntity->addAction(this, action);
+ return action;
+ }
+
+ action = nullptr;
+ return action;
+}
+
+void PhysicalEntitySimulation::applyActionChanges() {
+ if (_physicsEngine) {
+ foreach (EntityActionPointer actionToAdd, _actionsToAdd) {
+ _physicsEngine->addAction(actionToAdd);
+ }
+ foreach (QUuid actionToRemove, _actionsToRemove) {
+ _physicsEngine->removeAction(actionToRemove);
+ }
+ }
+ EntitySimulation::applyActionChanges();
+}
diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h
index 1aae27962a..82b0f8ad51 100644
--- a/libraries/physics/src/PhysicalEntitySimulation.h
+++ b/libraries/physics/src/PhysicalEntitySimulation.h
@@ -32,6 +32,12 @@ public:
void init(EntityTree* tree, PhysicsEngine* engine, EntityEditPacketSender* packetSender);
+ virtual EntityActionPointer actionFactory(EntityActionType type,
+ QUuid id,
+ EntityItemPointer ownerEntity,
+ QVariantMap arguments);
+ virtual void applyActionChanges();
+
protected: // only called by EntitySimulation
// overrides for EntitySimulation
virtual void updateEntitiesInternal(const quint64& now);
diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp
index 54b05b63d6..e5c974dfbc 100644
--- a/libraries/physics/src/PhysicsEngine.cpp
+++ b/libraries/physics/src/PhysicsEngine.cpp
@@ -436,3 +436,28 @@ int16_t PhysicsEngine::getCollisionMask(int16_t group) const {
const int16_t* mask = _collisionMasks.find(btHashInt((int)group));
return mask ? *mask : COLLISION_MASK_DEFAULT;
}
+
+void PhysicsEngine::addAction(EntityActionPointer action) {
+ assert(action);
+ const QUuid& actionID = action->getID();
+ if (_objectActions.contains(actionID)) {
+ assert(_objectActions[actionID] == action);
+ return;
+ }
+
+ _objectActions[actionID] = action;
+
+ // bullet needs a pointer to the action, but it doesn't use shared pointers.
+ // is there a way to bump the reference count?
+ ObjectAction* objectAction = static_cast(action.get());
+ _dynamicsWorld->addAction(objectAction);
+}
+
+void PhysicsEngine::removeAction(const QUuid actionID) {
+ if (_objectActions.contains(actionID)) {
+ EntityActionPointer action = _objectActions[actionID];
+ ObjectAction* objectAction = static_cast(action.get());
+ _dynamicsWorld->removeAction(objectAction);
+ _objectActions.remove(actionID);
+ }
+}
diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h
index db00a2ba01..6bb6ef8305 100644
--- a/libraries/physics/src/PhysicsEngine.h
+++ b/libraries/physics/src/PhysicsEngine.h
@@ -24,6 +24,7 @@
#include "DynamicCharacterController.h"
#include "ObjectMotionState.h"
#include "ThreadSafeDynamicsWorld.h"
+#include "ObjectAction.h"
const float HALF_SIMULATION_EXTENT = 512.0f; // meters
@@ -93,6 +94,9 @@ public:
int16_t getCollisionMask(int16_t group) const;
+ void addAction(EntityActionPointer action);
+ void removeAction(const QUuid actionID);
+
private:
void removeContacts(ObjectMotionState* motionState);
@@ -120,6 +124,9 @@ private:
QUuid _sessionID;
CollisionEvents _collisionEvents;
+
+ QHash _objectActions;
+
btHashMap _collisionMasks;
};
diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h
index 175eff059f..58ecf4adb2 100644
--- a/libraries/script-engine/src/ScriptEngine.h
+++ b/libraries/script-engine/src/ScriptEngine.h
@@ -54,7 +54,6 @@ public:
bool setScriptContents(const QString& scriptContents, const QString& fileNameString = QString(""));
const QString& getScriptName() const { return _scriptName; }
- void cleanupMenuItems();
QScriptValue registerGlobalObject(const QString& name, QObject* object); /// registers a global object by name
void registerGetterSetter(const QString& name, QScriptEngine::FunctionSignature getter,