mirror of
https://github.com/overte-org/overte.git
synced 2025-07-23 21:43:58 +02:00
Adding the reverse transformation
This commit is contained in:
commit
108910052a
46 changed files with 1820 additions and 208 deletions
|
@ -368,11 +368,11 @@ void DomainServer::setupNodeListAndAssignments() {
|
||||||
const QString CUSTOM_LOCAL_PORT_OPTION = "metaverse.local_port";
|
const QString CUSTOM_LOCAL_PORT_OPTION = "metaverse.local_port";
|
||||||
|
|
||||||
QVariant localPortValue = _settingsManager.valueOrDefaultValueForKeyPath(CUSTOM_LOCAL_PORT_OPTION);
|
QVariant localPortValue = _settingsManager.valueOrDefaultValueForKeyPath(CUSTOM_LOCAL_PORT_OPTION);
|
||||||
unsigned short domainServerPort = (unsigned short) localPortValue.toUInt();
|
int domainServerPort = localPortValue.toInt();
|
||||||
|
|
||||||
QVariantMap& settingsMap = _settingsManager.getSettingsMap();
|
QVariantMap& settingsMap = _settingsManager.getSettingsMap();
|
||||||
|
|
||||||
unsigned short domainServerDTLSPort = 0;
|
int domainServerDTLSPort = INVALID_PORT;
|
||||||
|
|
||||||
if (_isUsingDTLS) {
|
if (_isUsingDTLS) {
|
||||||
domainServerDTLSPort = DEFAULT_DOMAIN_SERVER_DTLS_PORT;
|
domainServerDTLSPort = DEFAULT_DOMAIN_SERVER_DTLS_PORT;
|
||||||
|
|
43
interface/resources/html/createGlobalEventBridge.js
Normal file
43
interface/resources/html/createGlobalEventBridge.js
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
//
|
||||||
|
// createGlobalEventBridge.js
|
||||||
|
//
|
||||||
|
// Created by Anthony J. Thibault on 9/7/2016
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
// Stick a EventBridge object in the global namespace.
|
||||||
|
var EventBridge;
|
||||||
|
(function () {
|
||||||
|
// the TempEventBridge class queues up emitWebEvent messages and executes them when the real EventBridge is ready.
|
||||||
|
// Similarly, it holds all scriptEventReceived callbacks, and hooks them up to the real EventBridge.
|
||||||
|
function TempEventBridge() {
|
||||||
|
var self = this;
|
||||||
|
this._callbacks = [];
|
||||||
|
this._messages = [];
|
||||||
|
this.scriptEventReceived = {
|
||||||
|
connect: function (callback) {
|
||||||
|
self._callbacks.push(callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.emitWebEvent = function (message) {
|
||||||
|
self._messages.push(message);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
EventBridge = new TempEventBridge();
|
||||||
|
|
||||||
|
var webChannel = new QWebChannel(qt.webChannelTransport, function (channel) {
|
||||||
|
// replace the TempEventBridge with the real one.
|
||||||
|
var tempEventBridge = EventBridge;
|
||||||
|
EventBridge = channel.objects.eventBridgeWrapper.eventBridge;
|
||||||
|
tempEventBridge._callbacks.forEach(function (callback) {
|
||||||
|
EventBridge.scriptEventReceived.connect(callback);
|
||||||
|
});
|
||||||
|
tempEventBridge._messages.forEach(function (message) {
|
||||||
|
EventBridge.emitWebEvent(message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
41
interface/resources/html/raiseAndLowerKeyboard.js
Normal file
41
interface/resources/html/raiseAndLowerKeyboard.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
//
|
||||||
|
// Created by Anthony Thibault on 2016-09-02
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
// Sends messages over the EventBridge when text input is required.
|
||||||
|
//
|
||||||
|
(function () {
|
||||||
|
var POLL_FREQUENCY = 500; // ms
|
||||||
|
var MAX_WARNINGS = 3;
|
||||||
|
var numWarnings = 0;
|
||||||
|
|
||||||
|
function shouldRaiseKeyboard() {
|
||||||
|
if (document.activeElement.nodeName == "INPUT" || document.activeElement.nodeName == "TEXTAREA") {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// check for contenteditable attribute
|
||||||
|
for (var i = 0; i < document.activeElement.attributes.length; i++) {
|
||||||
|
if (document.activeElement.attributes[i].name === "contenteditable" &&
|
||||||
|
document.activeElement.attributes[i].value === "true") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setInterval(function () {
|
||||||
|
var event = shouldRaiseKeyboard() ? "_RAISE_KEYBOARD" : "_LOWER_KEYBOARD";
|
||||||
|
if (typeof EventBridge != "undefined") {
|
||||||
|
EventBridge.emitWebEvent(event);
|
||||||
|
} else {
|
||||||
|
if (numWarnings < MAX_WARNINGS) {
|
||||||
|
console.log("WARNING: no global EventBridge object found");
|
||||||
|
numWarnings++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, POLL_FREQUENCY);
|
||||||
|
})();
|
|
@ -1,6 +1,6 @@
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtQuick.Controls 1.2
|
import QtQuick.Controls 1.2
|
||||||
import QtWebEngine 1.1
|
import QtWebEngine 1.2
|
||||||
|
|
||||||
import "controls-uit"
|
import "controls-uit"
|
||||||
import "styles" as HifiStyles
|
import "styles" as HifiStyles
|
||||||
|
@ -223,6 +223,9 @@ ScrollingWindow {
|
||||||
var newWindow = component.createObject(desktop);
|
var newWindow = component.createObject(desktop);
|
||||||
request.openIn(newWindow.webView)
|
request.openIn(newWindow.webView)
|
||||||
}
|
}
|
||||||
|
onWindowCloseRequested: {
|
||||||
|
root.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
desktop.initWebviewProfileHandlers(webview.profile)
|
desktop.initWebviewProfileHandlers(webview.profile)
|
||||||
|
|
|
@ -15,7 +15,7 @@ WebEngineView {
|
||||||
id: root
|
id: root
|
||||||
property var newUrl;
|
property var newUrl;
|
||||||
|
|
||||||
profile.httpUserAgent: "Mozilla/5.0 Chrome/38.0 (HighFidelityInterface)"
|
profile: desktop.browserProfile
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
console.log("Connecting JS messaging to Hifi Logging")
|
console.log("Connecting JS messaging to Hifi Logging")
|
||||||
|
@ -60,9 +60,4 @@ WebEngineView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// This breaks the webchannel used for passing messages. Fixed in Qt 5.6
|
|
||||||
// See https://bugreports.qt.io/browse/QTBUG-49521
|
|
||||||
//profile: desktop.browserProfile
|
|
||||||
}
|
}
|
||||||
|
|
164
interface/resources/qml/controls/Key.qml
Normal file
164
interface/resources/qml/controls/Key.qml
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: keyItem
|
||||||
|
width: 45
|
||||||
|
height: 50
|
||||||
|
property string glyph: "a"
|
||||||
|
property bool toggle: false // does this button have the toggle behaivor?
|
||||||
|
property bool toggled: false // is this button currently toggled?
|
||||||
|
property alias mouseArea: mouseArea1
|
||||||
|
|
||||||
|
function resetToggledMode(mode) {
|
||||||
|
toggled = mode;
|
||||||
|
if (toggled) {
|
||||||
|
state = "mouseDepressed";
|
||||||
|
} else {
|
||||||
|
state = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea1
|
||||||
|
width: 36
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
onCanceled: {
|
||||||
|
if (toggled) {
|
||||||
|
keyItem.state = "mouseDepressed";
|
||||||
|
} else {
|
||||||
|
keyItem.state = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
mouse.accepted = true;
|
||||||
|
webEntity.synthesizeKeyPress(glyph);
|
||||||
|
if (toggle) {
|
||||||
|
toggled = !toggled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDoubleClicked: {
|
||||||
|
mouse.accepted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onEntered: {
|
||||||
|
keyItem.state = "mouseOver";
|
||||||
|
}
|
||||||
|
|
||||||
|
onExited: {
|
||||||
|
if (toggled) {
|
||||||
|
keyItem.state = "mouseDepressed";
|
||||||
|
} else {
|
||||||
|
keyItem.state = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onPressed: {
|
||||||
|
keyItem.state = "mouseClicked";
|
||||||
|
mouse.accepted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onReleased: {
|
||||||
|
if (containsMouse) {
|
||||||
|
keyItem.state = "mouseOver";
|
||||||
|
} else {
|
||||||
|
if (toggled) {
|
||||||
|
keyItem.state = "mouseDepressed";
|
||||||
|
} else {
|
||||||
|
keyItem.state = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mouse.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: roundedRect
|
||||||
|
width: 30
|
||||||
|
color: "#121212"
|
||||||
|
radius: 2
|
||||||
|
border.color: "#00000000"
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 4
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 4
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 4
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: letter
|
||||||
|
y: 6
|
||||||
|
width: 50
|
||||||
|
color: "#ffffff"
|
||||||
|
text: glyph
|
||||||
|
style: Text.Normal
|
||||||
|
font.family: "Tahoma"
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 8
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
font.pixelSize: 28
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "mouseOver"
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: roundedRect
|
||||||
|
color: "#121212"
|
||||||
|
radius: 3
|
||||||
|
border.width: 2
|
||||||
|
border.color: "#00b4ef"
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: letter
|
||||||
|
color: "#00b4ef"
|
||||||
|
style: Text.Normal
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "mouseClicked"
|
||||||
|
PropertyChanges {
|
||||||
|
target: roundedRect
|
||||||
|
color: "#1080b8"
|
||||||
|
border.width: 2
|
||||||
|
border.color: "#00b4ef"
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: letter
|
||||||
|
color: "#121212"
|
||||||
|
styleColor: "#00000000"
|
||||||
|
style: Text.Normal
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "mouseDepressed"
|
||||||
|
PropertyChanges {
|
||||||
|
target: roundedRect
|
||||||
|
color: "#0578b1"
|
||||||
|
border.width: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: letter
|
||||||
|
color: "#121212"
|
||||||
|
styleColor: "#00000000"
|
||||||
|
style: Text.Normal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
390
interface/resources/qml/controls/Keyboard.qml
Normal file
390
interface/resources/qml/controls/Keyboard.qml
Normal file
|
@ -0,0 +1,390 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: keyboardBase
|
||||||
|
height: 200
|
||||||
|
property alias shiftKey: key27
|
||||||
|
property bool shiftMode: false
|
||||||
|
|
||||||
|
function resetShiftMode(mode) {
|
||||||
|
shiftMode = mode;
|
||||||
|
shiftKey.resetToggledMode(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toUpper(str) {
|
||||||
|
if (str === ",") {
|
||||||
|
return "<";
|
||||||
|
} else if (str === ".") {
|
||||||
|
return ">";
|
||||||
|
} else if (str === "/") {
|
||||||
|
return "?";
|
||||||
|
} else {
|
||||||
|
return str.toUpperCase(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toLower(str) {
|
||||||
|
if (str === "<") {
|
||||||
|
return ",";
|
||||||
|
} else if (str === ">") {
|
||||||
|
return ".";
|
||||||
|
} else if (str === "?") {
|
||||||
|
return "/";
|
||||||
|
} else {
|
||||||
|
return str.toLowerCase(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function forEachKey(func) {
|
||||||
|
var i, j;
|
||||||
|
for (i = 0; i < column1.children.length; i++) {
|
||||||
|
var row = column1.children[i];
|
||||||
|
for (j = 0; j < row.children.length; j++) {
|
||||||
|
var key = row.children[j];
|
||||||
|
func(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onShiftModeChanged: {
|
||||||
|
forEachKey(function (key) {
|
||||||
|
if (shiftMode) {
|
||||||
|
key.glyph = keyboardBase.toUpper(key.glyph);
|
||||||
|
} else {
|
||||||
|
key.glyph = keyboardBase.toLower(key.glyph);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function alphaKeyClickedHandler(mouseArea) {
|
||||||
|
// reset shift mode to false after first keypress
|
||||||
|
if (shiftMode) {
|
||||||
|
resetShiftMode(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
// hook up callbacks to every ascii key
|
||||||
|
forEachKey(function (key) {
|
||||||
|
if (/^[a-z]+$/i.test(key.glyph)) {
|
||||||
|
key.mouseArea.onClicked.connect(alphaKeyClickedHandler);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: leftRect
|
||||||
|
y: 0
|
||||||
|
height: 200
|
||||||
|
color: "#252525"
|
||||||
|
anchors.right: keyboardRect.left
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: keyboardRect
|
||||||
|
x: 206
|
||||||
|
y: 0
|
||||||
|
width: 480
|
||||||
|
height: 200
|
||||||
|
color: "#252525"
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: column1
|
||||||
|
width: 480
|
||||||
|
height: 200
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: row1
|
||||||
|
width: 480
|
||||||
|
height: 50
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key1
|
||||||
|
width: 44
|
||||||
|
glyph: "q"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key2
|
||||||
|
width: 44
|
||||||
|
glyph: "w"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key3
|
||||||
|
width: 44
|
||||||
|
glyph: "e"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key4
|
||||||
|
width: 43
|
||||||
|
glyph: "r"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key5
|
||||||
|
width: 43
|
||||||
|
glyph: "t"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key6
|
||||||
|
width: 44
|
||||||
|
glyph: "y"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key7
|
||||||
|
width: 44
|
||||||
|
glyph: "u"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key8
|
||||||
|
width: 43
|
||||||
|
glyph: "i"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key9
|
||||||
|
width: 42
|
||||||
|
glyph: "o"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key10
|
||||||
|
width: 44
|
||||||
|
glyph: "p"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key28
|
||||||
|
width: 45
|
||||||
|
glyph: "←"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: row2
|
||||||
|
width: 480
|
||||||
|
height: 50
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 18
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key11
|
||||||
|
width: 43
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key12
|
||||||
|
width: 43
|
||||||
|
glyph: "s"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key13
|
||||||
|
width: 43
|
||||||
|
glyph: "d"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key14
|
||||||
|
width: 43
|
||||||
|
glyph: "f"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key15
|
||||||
|
width: 43
|
||||||
|
glyph: "g"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key16
|
||||||
|
width: 43
|
||||||
|
glyph: "h"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key17
|
||||||
|
width: 43
|
||||||
|
glyph: "j"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key18
|
||||||
|
width: 43
|
||||||
|
glyph: "k"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key19
|
||||||
|
width: 43
|
||||||
|
glyph: "l"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key32
|
||||||
|
width: 75
|
||||||
|
glyph: "⏎"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: row3
|
||||||
|
width: 480
|
||||||
|
height: 50
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key27
|
||||||
|
width: 46
|
||||||
|
glyph: "⇪"
|
||||||
|
toggle: true
|
||||||
|
onToggledChanged: {
|
||||||
|
shiftMode = toggled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key20
|
||||||
|
width: 43
|
||||||
|
glyph: "z"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key21
|
||||||
|
width: 43
|
||||||
|
glyph: "x"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key22
|
||||||
|
width: 43
|
||||||
|
glyph: "c"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key23
|
||||||
|
width: 43
|
||||||
|
glyph: "v"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key24
|
||||||
|
width: 43
|
||||||
|
glyph: "b"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key25
|
||||||
|
width: 43
|
||||||
|
glyph: "n"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key26
|
||||||
|
width: 44
|
||||||
|
glyph: "m"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key31
|
||||||
|
width: 43
|
||||||
|
glyph: ","
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key33
|
||||||
|
width: 43
|
||||||
|
glyph: "."
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key36
|
||||||
|
width: 46
|
||||||
|
glyph: "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: row4
|
||||||
|
width: 480
|
||||||
|
height: 50
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 19
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key30
|
||||||
|
width: 89
|
||||||
|
glyph: "&123"
|
||||||
|
mouseArea.onClicked: {
|
||||||
|
keyboardBase.parent.punctuationMode = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key29
|
||||||
|
width: 285
|
||||||
|
glyph: " "
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key34
|
||||||
|
width: 43
|
||||||
|
glyph: "⇦"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key35
|
||||||
|
x: 343
|
||||||
|
width: 43
|
||||||
|
glyph: "⇨"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: rightRect
|
||||||
|
y: 280
|
||||||
|
height: 200
|
||||||
|
color: "#252525"
|
||||||
|
border.width: 0
|
||||||
|
anchors.left: keyboardRect.right
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: rectangle1
|
||||||
|
color: "#ffffff"
|
||||||
|
anchors.bottom: keyboardRect.top
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
324
interface/resources/qml/controls/KeyboardPunctuation.qml
Normal file
324
interface/resources/qml/controls/KeyboardPunctuation.qml
Normal file
|
@ -0,0 +1,324 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: keyboardBase
|
||||||
|
height: 200
|
||||||
|
Rectangle {
|
||||||
|
id: leftRect
|
||||||
|
y: 0
|
||||||
|
height: 200
|
||||||
|
color: "#252525"
|
||||||
|
anchors.right: keyboardRect.left
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: keyboardRect
|
||||||
|
x: 206
|
||||||
|
y: 0
|
||||||
|
width: 480
|
||||||
|
height: 200
|
||||||
|
color: "#252525"
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: column1
|
||||||
|
width: 480
|
||||||
|
height: 200
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: row1
|
||||||
|
width: 480
|
||||||
|
height: 50
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key1
|
||||||
|
width: 43
|
||||||
|
glyph: "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key2
|
||||||
|
width: 43
|
||||||
|
glyph: "2"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key3
|
||||||
|
width: 43
|
||||||
|
glyph: "3"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key4
|
||||||
|
width: 43
|
||||||
|
glyph: "4"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key5
|
||||||
|
width: 43
|
||||||
|
glyph: "5"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key6
|
||||||
|
width: 43
|
||||||
|
glyph: "6"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key7
|
||||||
|
width: 43
|
||||||
|
glyph: "7"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key8
|
||||||
|
width: 43
|
||||||
|
glyph: "8"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key9
|
||||||
|
width: 43
|
||||||
|
glyph: "9"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key10
|
||||||
|
width: 43
|
||||||
|
glyph: "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key28
|
||||||
|
width: 50
|
||||||
|
glyph: "←"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: row2
|
||||||
|
width: 480
|
||||||
|
height: 50
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key11
|
||||||
|
width: 43
|
||||||
|
glyph: "!"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key12
|
||||||
|
width: 43
|
||||||
|
glyph: "@"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key13
|
||||||
|
width: 43
|
||||||
|
glyph: "#"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key14
|
||||||
|
width: 43
|
||||||
|
glyph: "$"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key15
|
||||||
|
width: 43
|
||||||
|
glyph: "%"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key16
|
||||||
|
width: 43
|
||||||
|
glyph: "^"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key17
|
||||||
|
width: 43
|
||||||
|
glyph: "&"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key18
|
||||||
|
width: 43
|
||||||
|
glyph: "*"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key19
|
||||||
|
width: 43
|
||||||
|
glyph: "("
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key32
|
||||||
|
width: 43
|
||||||
|
glyph: ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key37
|
||||||
|
width: 50
|
||||||
|
glyph: "⏎"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: row3
|
||||||
|
width: 480
|
||||||
|
height: 50
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 4
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key27
|
||||||
|
width: 43
|
||||||
|
glyph: "="
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key20
|
||||||
|
width: 43
|
||||||
|
glyph: "+"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key21
|
||||||
|
width: 43
|
||||||
|
glyph: "-"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key22
|
||||||
|
width: 43
|
||||||
|
glyph: "_"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key23
|
||||||
|
width: 43
|
||||||
|
glyph: ";"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key24
|
||||||
|
width: 43
|
||||||
|
glyph: ":"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key25
|
||||||
|
width: 43
|
||||||
|
glyph: "'"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key26
|
||||||
|
width: 43
|
||||||
|
glyph: "\""
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key31
|
||||||
|
width: 43
|
||||||
|
glyph: "<"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key33
|
||||||
|
width: 43
|
||||||
|
glyph: ">"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key36
|
||||||
|
width: 43
|
||||||
|
glyph: "?"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: row4
|
||||||
|
width: 480
|
||||||
|
height: 50
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 19
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key30
|
||||||
|
width: 65
|
||||||
|
glyph: "abc"
|
||||||
|
mouseArea.onClicked: {
|
||||||
|
keyboardBase.parent.punctuationMode = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key29
|
||||||
|
width: 285
|
||||||
|
glyph: " "
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key34
|
||||||
|
width: 43
|
||||||
|
glyph: "⇦"
|
||||||
|
}
|
||||||
|
|
||||||
|
Key {
|
||||||
|
id: key35
|
||||||
|
x: 343
|
||||||
|
width: 43
|
||||||
|
glyph: "⇨"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: rightRect
|
||||||
|
y: 280
|
||||||
|
height: 200
|
||||||
|
color: "#252525"
|
||||||
|
border.width: 0
|
||||||
|
anchors.left: keyboardRect.right
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: rectangle1
|
||||||
|
color: "#ffffff"
|
||||||
|
anchors.bottom: keyboardRect.top
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,18 +1,56 @@
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtWebEngine 1.1
|
import QtWebEngine 1.1
|
||||||
|
import QtWebChannel 1.0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
property alias url: root.url
|
||||||
|
property alias eventBridge: eventBridgeWrapper.eventBridge
|
||||||
|
property bool keyboardRaised: false
|
||||||
|
property bool punctuationMode: false
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: eventBridgeWrapper
|
||||||
|
WebChannel.id: "eventBridgeWrapper"
|
||||||
|
property var eventBridge;
|
||||||
|
}
|
||||||
|
|
||||||
WebEngineView {
|
WebEngineView {
|
||||||
id: root
|
id: root
|
||||||
property var newUrl;
|
x: 0
|
||||||
|
y: 0
|
||||||
|
width: parent.width
|
||||||
|
height: keyboardRaised ? parent.height - keyboard1.height : parent.height
|
||||||
|
|
||||||
profile: desktop.browserProfile
|
// creates a global EventBridge object.
|
||||||
|
WebEngineScript {
|
||||||
|
id: createGlobalEventBridge
|
||||||
|
sourceCode: eventBridgeJavaScriptToInject
|
||||||
|
injectionPoint: WebEngineScript.DocumentCreation
|
||||||
|
worldId: WebEngineScript.MainWorld
|
||||||
|
}
|
||||||
|
|
||||||
|
// detects when to raise and lower virtual keyboard
|
||||||
|
WebEngineScript {
|
||||||
|
id: raiseAndLowerKeyboard
|
||||||
|
injectionPoint: WebEngineScript.Deferred
|
||||||
|
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
|
||||||
|
worldId: WebEngineScript.MainWorld
|
||||||
|
}
|
||||||
|
|
||||||
|
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
|
||||||
|
|
||||||
|
property string newUrl: ""
|
||||||
|
|
||||||
|
webChannel.registeredObjects: [eventBridgeWrapper]
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
console.log("Connecting JS messaging to Hifi Logging")
|
console.log("Connecting JS messaging to Hifi Logging");
|
||||||
// Ensure the JS from the web-engine makes it to our logging
|
// Ensure the JS from the web-engine makes it to our logging
|
||||||
root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||||
console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message);
|
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
root.profile.httpUserAgent = "Mozilla/5.0 Chrome (HighFidelityInterface)";
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME hack to get the URL with the auth token included. Remove when we move to Qt 5.6
|
// FIXME hack to get the URL with the auth token included. Remove when we move to Qt 5.6
|
||||||
|
@ -21,13 +59,13 @@ WebEngineView {
|
||||||
running: false
|
running: false
|
||||||
repeat: false
|
repeat: false
|
||||||
interval: 50
|
interval: 50
|
||||||
onTriggered: url = newUrl;
|
onTriggered: url = root.newUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
onUrlChanged: {
|
onUrlChanged: {
|
||||||
var originalUrl = url.toString();
|
var originalUrl = url.toString();
|
||||||
newUrl = urlHandler.fixupUrl(originalUrl).toString();
|
root.newUrl = urlHandler.fixupUrl(originalUrl).toString();
|
||||||
if (newUrl !== originalUrl) {
|
if (root.newUrl !== originalUrl) {
|
||||||
root.stop();
|
root.stop();
|
||||||
if (urlReplacementTimer.running) {
|
if (urlReplacementTimer.running) {
|
||||||
console.warn("Replacement timer already running");
|
console.warn("Replacement timer already running");
|
||||||
|
@ -42,6 +80,10 @@ WebEngineView {
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoadingChanged: {
|
onLoadingChanged: {
|
||||||
|
keyboardRaised = false;
|
||||||
|
punctuationMode = false;
|
||||||
|
keyboard1.resetShiftMode(false);
|
||||||
|
|
||||||
// Required to support clicking on "hifi://" links
|
// Required to support clicking on "hifi://" links
|
||||||
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
|
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
|
||||||
var url = loadRequest.url.toString();
|
var url = loadRequest.url.toString();
|
||||||
|
@ -54,6 +96,7 @@ WebEngineView {
|
||||||
}
|
}
|
||||||
|
|
||||||
onNewViewRequested:{
|
onNewViewRequested:{
|
||||||
|
// desktop is not defined for web-entities
|
||||||
if (desktop) {
|
if (desktop) {
|
||||||
var component = Qt.createComponent("../Browser.qml");
|
var component = Qt.createComponent("../Browser.qml");
|
||||||
var newWindow = component.createObject(desktop);
|
var newWindow = component.createObject(desktop);
|
||||||
|
@ -61,3 +104,33 @@ WebEngineView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// virtual keyboard, letters
|
||||||
|
Keyboard {
|
||||||
|
id: keyboard1
|
||||||
|
y: keyboardRaised ? parent.height : 0
|
||||||
|
height: keyboardRaised ? 200 : 0
|
||||||
|
visible: keyboardRaised && !punctuationMode
|
||||||
|
enabled: keyboardRaised && !punctuationMode
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyboardPunctuation {
|
||||||
|
id: keyboard2
|
||||||
|
y: keyboardRaised ? parent.height : 0
|
||||||
|
height: keyboardRaised ? 200 : 0
|
||||||
|
visible: keyboardRaised && punctuationMode
|
||||||
|
enabled: keyboardRaised && punctuationMode
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 0
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -46,8 +46,20 @@ OriginalDesktop.Desktop {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property var toolbars: ({})
|
|
||||||
Component { id: toolbarBuilder; Toolbar { } }
|
Component { id: toolbarBuilder; Toolbar { } }
|
||||||
|
// This used to create sysToolbar dynamically with a call to getToolbar() within onCompleted.
|
||||||
|
// Beginning with QT 5.6, this stopped working, as anything added to toolbars too early got
|
||||||
|
// wiped during startup.
|
||||||
|
Toolbar {
|
||||||
|
id: sysToolbar;
|
||||||
|
objectName: "com.highfidelity.interface.toolbar.system";
|
||||||
|
// Magic: sysToolbar.x and y come from settings, and are bound before the properties specified here are applied.
|
||||||
|
x: sysToolbar.x;
|
||||||
|
y: sysToolbar.y;
|
||||||
|
}
|
||||||
|
property var toolbars: (function (map) { // answer dictionary preloaded with sysToolbar
|
||||||
|
map[sysToolbar.objectName] = sysToolbar;
|
||||||
|
return map; })({});
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
WebEngine.settings.javascriptCanOpenWindows = true;
|
WebEngine.settings.javascriptCanOpenWindows = true;
|
||||||
|
@ -55,7 +67,6 @@ OriginalDesktop.Desktop {
|
||||||
WebEngine.settings.spatialNavigationEnabled = false;
|
WebEngine.settings.spatialNavigationEnabled = false;
|
||||||
WebEngine.settings.localContentCanAccessRemoteUrls = true;
|
WebEngine.settings.localContentCanAccessRemoteUrls = true;
|
||||||
|
|
||||||
var sysToolbar = desktop.getToolbar("com.highfidelity.interface.toolbar.system");
|
|
||||||
var toggleHudButton = sysToolbar.addButton({
|
var toggleHudButton = sysToolbar.addButton({
|
||||||
objectName: "hudToggle",
|
objectName: "hudToggle",
|
||||||
imageURL: "../../../icons/hud.svg",
|
imageURL: "../../../icons/hud.svg",
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
import QtQuick 2.5
|
||||||
|
import Qt.labs.settings 1.0
|
||||||
|
|
||||||
|
import "../../dialogs"
|
||||||
|
|
||||||
|
PreferencesDialog {
|
||||||
|
id: root
|
||||||
|
objectName: "NetworkingPreferencesDialog"
|
||||||
|
title: "Networking Settings"
|
||||||
|
showCategories: ["Networking"]
|
||||||
|
property var settings: Settings {
|
||||||
|
category: root.objectName
|
||||||
|
property alias x: root.x
|
||||||
|
property alias y: root.y
|
||||||
|
property alias width: root.width
|
||||||
|
property alias height: root.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -400,12 +400,10 @@ static const QString STATE_GROUNDED = "Grounded";
|
||||||
static const QString STATE_NAV_FOCUSED = "NavigationFocused";
|
static const QString STATE_NAV_FOCUSED = "NavigationFocused";
|
||||||
|
|
||||||
bool setupEssentials(int& argc, char** argv) {
|
bool setupEssentials(int& argc, char** argv) {
|
||||||
unsigned int listenPort = 0; // bind to an ephemeral port by default
|
|
||||||
const char** constArgv = const_cast<const char**>(argv);
|
const char** constArgv = const_cast<const char**>(argv);
|
||||||
const char* portStr = getCmdOption(argc, constArgv, "--listenPort");
|
const char* portStr = getCmdOption(argc, constArgv, "--listenPort");
|
||||||
if (portStr) {
|
const int listenPort = portStr ? atoi(portStr) : INVALID_PORT;
|
||||||
listenPort = atoi(portStr);
|
|
||||||
}
|
|
||||||
// Set build version
|
// Set build version
|
||||||
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
|
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
|
||||||
|
|
||||||
|
|
|
@ -223,7 +223,7 @@ public:
|
||||||
// the isHMDMode is true whenever we use the interface from an HMD and not a standard flat display
|
// the isHMDMode is true whenever we use the interface from an HMD and not a standard flat display
|
||||||
// rendering of several elements depend on that
|
// rendering of several elements depend on that
|
||||||
// TODO: carry that information on the Camera as a setting
|
// TODO: carry that information on the Camera as a setting
|
||||||
bool isHMDMode() const;
|
virtual bool isHMDMode() const override;
|
||||||
glm::mat4 getHMDSensorPose() const;
|
glm::mat4 getHMDSensorPose() const;
|
||||||
glm::mat4 getEyeOffset(int eye) const;
|
glm::mat4 getEyeOffset(int eye) const;
|
||||||
glm::mat4 getEyeProjection(int eye) const;
|
glm::mat4 getEyeProjection(int eye) const;
|
||||||
|
|
|
@ -522,6 +522,11 @@ Menu::Menu() {
|
||||||
|
|
||||||
// Developer > Network >>>
|
// Developer > Network >>>
|
||||||
MenuWrapper* networkMenu = developerMenu->addMenu("Network");
|
MenuWrapper* networkMenu = developerMenu->addMenu("Network");
|
||||||
|
action = addActionToQMenuAndActionHash(networkMenu, MenuOption::Networking);
|
||||||
|
connect(action, &QAction::triggered, [] {
|
||||||
|
DependencyManager::get<OffscreenUi>()->toggle(QUrl("hifi/dialogs/NetworkingPreferencesDialog.qml"),
|
||||||
|
"NetworkingPreferencesDialog");
|
||||||
|
});
|
||||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()));
|
addActionToQMenuAndActionHash(networkMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()));
|
||||||
addCheckableActionToQMenuAndActionHash(networkMenu,
|
addCheckableActionToQMenuAndActionHash(networkMenu,
|
||||||
MenuOption::DisableActivityLogger,
|
MenuOption::DisableActivityLogger,
|
||||||
|
|
|
@ -129,6 +129,7 @@ namespace MenuOption {
|
||||||
const QString MuteEnvironment = "Mute Environment";
|
const QString MuteEnvironment = "Mute Environment";
|
||||||
const QString MuteFaceTracking = "Mute Face Tracking";
|
const QString MuteFaceTracking = "Mute Face Tracking";
|
||||||
const QString NamesAboveHeads = "Names Above Heads";
|
const QString NamesAboveHeads = "Names Above Heads";
|
||||||
|
const QString Networking = "Networking...";
|
||||||
const QString NoFaceTracking = "None";
|
const QString NoFaceTracking = "None";
|
||||||
const QString OctreeStats = "Entity Statistics";
|
const QString OctreeStats = "Entity Statistics";
|
||||||
const QString OnePointCalibration = "1 Point Calibration";
|
const QString OnePointCalibration = "1 Point Calibration";
|
||||||
|
|
|
@ -332,4 +332,19 @@ void setupPreferences() {
|
||||||
preferences->addPreference(preference);
|
preferences->addPreference(preference);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
static const QString RENDER("Networking");
|
||||||
|
|
||||||
|
auto nodelist = DependencyManager::get<NodeList>();
|
||||||
|
{
|
||||||
|
static const int MIN_PORT_NUMBER { 0 };
|
||||||
|
static const int MAX_PORT_NUMBER { 65535 };
|
||||||
|
auto getter = [nodelist] { return static_cast<int>(nodelist->getSocketLocalPort()); };
|
||||||
|
auto setter = [nodelist](int preset) { nodelist->setSocketLocalPort(static_cast<quint16>(preset)); };
|
||||||
|
auto preference = new IntSpinnerPreference(RENDER, "Listening Port", getter, setter);
|
||||||
|
preference->setMin(MIN_PORT_NUMBER);
|
||||||
|
preference->setMax(MAX_PORT_NUMBER);
|
||||||
|
preferences->addPreference(preference);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -512,7 +512,7 @@ void OpenGLDisplayPlugin::compositeOverlay() {
|
||||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
batch.setViewportTransform(ivec4(uvec2(0), _currentFrame->framebuffer->getSize()));
|
batch.setViewportTransform(ivec4(uvec2(0), _compositeFramebuffer->getSize()));
|
||||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -536,7 +536,7 @@ void OpenGLDisplayPlugin::compositePointer() {
|
||||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
batch.setViewportTransform(ivec4(uvec2(0), _currentFrame->framebuffer->getSize()));
|
batch.setViewportTransform(ivec4(uvec2(0), _compositeFramebuffer->getSize()));
|
||||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -726,7 +726,7 @@ bool OpenGLDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ivec4 OpenGLDisplayPlugin::eyeViewport(Eye eye) const {
|
ivec4 OpenGLDisplayPlugin::eyeViewport(Eye eye) const {
|
||||||
uvec2 vpSize = _currentFrame->framebuffer->getSize();
|
uvec2 vpSize = _compositeFramebuffer->getSize();
|
||||||
vpSize.x /= 2;
|
vpSize.x /= 2;
|
||||||
uvec2 vpPos;
|
uvec2 vpPos;
|
||||||
if (eye == Eye::Right) {
|
if (eye == Eye::Right) {
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <gpu/Context.h>
|
#include <gpu/Context.h>
|
||||||
|
|
||||||
#include "EntityTreeRenderer.h"
|
#include "EntityTreeRenderer.h"
|
||||||
|
#include "EntitiesRendererLogging.h"
|
||||||
|
|
||||||
const float METERS_TO_INCHES = 39.3701f;
|
const float METERS_TO_INCHES = 39.3701f;
|
||||||
static uint32_t _currentWebCount { 0 };
|
static uint32_t _currentWebCount { 0 };
|
||||||
|
@ -37,6 +38,35 @@ static uint64_t MAX_NO_RENDER_INTERVAL = 30 * USECS_PER_SECOND;
|
||||||
static int MAX_WINDOW_SIZE = 4096;
|
static int MAX_WINDOW_SIZE = 4096;
|
||||||
static float OPAQUE_ALPHA_THRESHOLD = 0.99f;
|
static float OPAQUE_ALPHA_THRESHOLD = 0.99f;
|
||||||
|
|
||||||
|
void WebEntityAPIHelper::synthesizeKeyPress(QString key) {
|
||||||
|
if (_renderableWebEntityItem) {
|
||||||
|
_renderableWebEntityItem->synthesizeKeyPress(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebEntityAPIHelper::emitScriptEvent(const QVariant& message) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "emitScriptEvent", Qt::QueuedConnection, Q_ARG(QVariant, message));
|
||||||
|
} else {
|
||||||
|
emit scriptEventReceived(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebEntityAPIHelper::emitWebEvent(const QVariant& message) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "emitWebEvent", Qt::QueuedConnection, Q_ARG(QVariant, message));
|
||||||
|
} else {
|
||||||
|
// special case to handle raising and lowering the virtual keyboard
|
||||||
|
if (message.type() == QVariant::String && message.toString() == "_RAISE_KEYBOARD" && _renderableWebEntityItem) {
|
||||||
|
_renderableWebEntityItem->setKeyboardRaised(true);
|
||||||
|
} else if (message.type() == QVariant::String && message.toString() == "_LOWER_KEYBOARD" && _renderableWebEntityItem) {
|
||||||
|
_renderableWebEntityItem->setKeyboardRaised(false);
|
||||||
|
} else {
|
||||||
|
emit webEventReceived(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EntityItemPointer RenderableWebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
EntityItemPointer RenderableWebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||||
EntityItemPointer entity{ new RenderableWebEntityItem(entityID) };
|
EntityItemPointer entity{ new RenderableWebEntityItem(entityID) };
|
||||||
entity->setProperties(properties);
|
entity->setProperties(properties);
|
||||||
|
@ -46,9 +76,26 @@ EntityItemPointer RenderableWebEntityItem::factory(const EntityItemID& entityID,
|
||||||
RenderableWebEntityItem::RenderableWebEntityItem(const EntityItemID& entityItemID) :
|
RenderableWebEntityItem::RenderableWebEntityItem(const EntityItemID& entityItemID) :
|
||||||
WebEntityItem(entityItemID) {
|
WebEntityItem(entityItemID) {
|
||||||
qDebug() << "Created web entity " << getID();
|
qDebug() << "Created web entity " << getID();
|
||||||
|
|
||||||
|
_touchDevice.setCapabilities(QTouchDevice::Position);
|
||||||
|
_touchDevice.setType(QTouchDevice::TouchScreen);
|
||||||
|
_touchDevice.setName("RenderableWebEntityItemTouchDevice");
|
||||||
|
_touchDevice.setMaximumTouchPoints(4);
|
||||||
|
|
||||||
|
_webEntityAPIHelper = new WebEntityAPIHelper;
|
||||||
|
_webEntityAPIHelper->setRenderableWebEntityItem(this);
|
||||||
|
_webEntityAPIHelper->moveToThread(qApp->thread());
|
||||||
|
|
||||||
|
// forward web events to EntityScriptingInterface
|
||||||
|
auto entities = DependencyManager::get<EntityScriptingInterface>();
|
||||||
|
QObject::connect(_webEntityAPIHelper, &WebEntityAPIHelper::webEventReceived, [=](const QVariant& message) {
|
||||||
|
emit entities->webEventReceived(entityItemID, message);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderableWebEntityItem::~RenderableWebEntityItem() {
|
RenderableWebEntityItem::~RenderableWebEntityItem() {
|
||||||
|
_webEntityAPIHelper->setRenderableWebEntityItem(nullptr);
|
||||||
|
_webEntityAPIHelper->deleteLater();
|
||||||
destroyWebSurface();
|
destroyWebSurface();
|
||||||
qDebug() << "Destroyed web entity " << getID();
|
qDebug() << "Destroyed web entity " << getID();
|
||||||
}
|
}
|
||||||
|
@ -60,6 +107,20 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) {
|
||||||
}
|
}
|
||||||
qDebug() << "Building web surface";
|
qDebug() << "Building web surface";
|
||||||
|
|
||||||
|
QString javaScriptToInject;
|
||||||
|
QFile webChannelFile(":qtwebchannel/qwebchannel.js");
|
||||||
|
QFile createGlobalEventBridgeFile(PathUtils::resourcesPath() + "/html/createGlobalEventBridge.js");
|
||||||
|
if (webChannelFile.open(QFile::ReadOnly | QFile::Text) &&
|
||||||
|
createGlobalEventBridgeFile.open(QFile::ReadOnly | QFile::Text)) {
|
||||||
|
QString webChannelStr = QTextStream(&webChannelFile).readAll();
|
||||||
|
QString createGlobalEventBridgeStr = QTextStream(&createGlobalEventBridgeFile).readAll();
|
||||||
|
|
||||||
|
// concatenate these js files
|
||||||
|
javaScriptToInject = webChannelStr + createGlobalEventBridgeStr;
|
||||||
|
} else {
|
||||||
|
qCWarning(entitiesrenderer) << "unable to find qwebchannel.js or createGlobalEventBridge.js";
|
||||||
|
}
|
||||||
|
|
||||||
++_currentWebCount;
|
++_currentWebCount;
|
||||||
// Save the original GL context, because creating a QML surface will create a new context
|
// Save the original GL context, because creating a QML surface will create a new context
|
||||||
QOpenGLContext * currentContext = QOpenGLContext::currentContext();
|
QOpenGLContext * currentContext = QOpenGLContext::currentContext();
|
||||||
|
@ -67,10 +128,14 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) {
|
||||||
_webSurface = new OffscreenQmlSurface();
|
_webSurface = new OffscreenQmlSurface();
|
||||||
_webSurface->create(currentContext);
|
_webSurface->create(currentContext);
|
||||||
_webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/controls/"));
|
_webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/controls/"));
|
||||||
_webSurface->load("WebView.qml");
|
_webSurface->load("WebView.qml", [&](QQmlContext* context, QObject* obj) {
|
||||||
|
context->setContextProperty("eventBridgeJavaScriptToInject", QVariant(javaScriptToInject));
|
||||||
|
});
|
||||||
_webSurface->resume();
|
_webSurface->resume();
|
||||||
|
_webSurface->getRootItem()->setProperty("eventBridge", QVariant::fromValue(_webEntityAPIHelper));
|
||||||
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
|
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
|
||||||
_webSurface->getRootContext()->setContextProperty("desktop", QVariant());
|
_webSurface->getRootContext()->setContextProperty("desktop", QVariant());
|
||||||
|
_webSurface->getRootContext()->setContextProperty("webEntity", _webEntityAPIHelper);
|
||||||
_connection = QObject::connect(_webSurface, &OffscreenQmlSurface::textureUpdated, [&](GLuint textureId) {
|
_connection = QObject::connect(_webSurface, &OffscreenQmlSurface::textureUpdated, [&](GLuint textureId) {
|
||||||
_texture = textureId;
|
_texture = textureId;
|
||||||
});
|
});
|
||||||
|
@ -93,10 +158,14 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) {
|
||||||
point.setState(Qt::TouchPointReleased);
|
point.setState(Qt::TouchPointReleased);
|
||||||
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi);
|
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi);
|
||||||
QPointF windowPoint(windowPos.x, windowPos.y);
|
QPointF windowPoint(windowPos.x, windowPos.y);
|
||||||
|
point.setScenePos(windowPoint);
|
||||||
point.setPos(windowPoint);
|
point.setPos(windowPoint);
|
||||||
QList<QTouchEvent::TouchPoint> touchPoints;
|
QList<QTouchEvent::TouchPoint> touchPoints;
|
||||||
touchPoints.push_back(point);
|
touchPoints.push_back(point);
|
||||||
QTouchEvent* touchEvent = new QTouchEvent(QEvent::TouchEnd, nullptr, Qt::NoModifier, Qt::TouchPointReleased, touchPoints);
|
QTouchEvent* touchEvent = new QTouchEvent(QEvent::TouchEnd, nullptr, Qt::NoModifier, Qt::TouchPointReleased, touchPoints);
|
||||||
|
touchEvent->setWindow(_webSurface->getWindow());
|
||||||
|
touchEvent->setDevice(&_touchDevice);
|
||||||
|
touchEvent->setTarget(_webSurface->getRootItem());
|
||||||
QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent);
|
QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -210,7 +279,6 @@ void RenderableWebEntityItem::handlePointerEvent(const PointerEvent& event) {
|
||||||
|
|
||||||
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi);
|
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi);
|
||||||
QPointF windowPoint(windowPos.x, windowPos.y);
|
QPointF windowPoint(windowPos.x, windowPos.y);
|
||||||
|
|
||||||
if (event.getType() == PointerEvent::Move) {
|
if (event.getType() == PointerEvent::Move) {
|
||||||
// Forward a mouse move event to webSurface
|
// Forward a mouse move event to webSurface
|
||||||
QMouseEvent* mouseEvent = new QMouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, Qt::NoButton, Qt::NoButton, Qt::NoModifier);
|
QMouseEvent* mouseEvent = new QMouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, Qt::NoButton, Qt::NoButton, Qt::NoModifier);
|
||||||
|
@ -252,9 +320,9 @@ void RenderableWebEntityItem::handlePointerEvent(const PointerEvent& event) {
|
||||||
touchPoints.push_back(point);
|
touchPoints.push_back(point);
|
||||||
|
|
||||||
QTouchEvent* touchEvent = new QTouchEvent(type);
|
QTouchEvent* touchEvent = new QTouchEvent(type);
|
||||||
touchEvent->setWindow(nullptr);
|
touchEvent->setWindow(_webSurface->getWindow());
|
||||||
touchEvent->setDevice(nullptr);
|
touchEvent->setDevice(&_touchDevice);
|
||||||
touchEvent->setTarget(nullptr);
|
touchEvent->setTarget(_webSurface->getRootItem());
|
||||||
touchEvent->setTouchPoints(touchPoints);
|
touchEvent->setTouchPoints(touchPoints);
|
||||||
touchEvent->setTouchPointStates(touchPointState);
|
touchEvent->setTouchPointStates(touchPointState);
|
||||||
|
|
||||||
|
@ -303,3 +371,63 @@ bool RenderableWebEntityItem::isTransparent() {
|
||||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||||
return fadeRatio < OPAQUE_ALPHA_THRESHOLD;
|
return fadeRatio < OPAQUE_ALPHA_THRESHOLD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UTF-8 encoded symbols
|
||||||
|
static const uint8_t UPWARDS_WHITE_ARROW_FROM_BAR[] = { 0xE2, 0x87, 0xAA, 0x00 }; // shift
|
||||||
|
static const uint8_t LEFT_ARROW[] = { 0xE2, 0x86, 0x90, 0x00 }; // backspace
|
||||||
|
static const uint8_t LEFTWARD_WHITE_ARROW[] = { 0xE2, 0x87, 0xA6, 0x00 }; // left arrow
|
||||||
|
static const uint8_t RIGHTWARD_WHITE_ARROW[] = { 0xE2, 0x87, 0xA8, 0x00 }; // right arrow
|
||||||
|
static const uint8_t ASTERISIM[] = { 0xE2, 0x81, 0x82, 0x00 }; // symbols
|
||||||
|
static const uint8_t RETURN_SYMBOL[] = { 0xE2, 0x8F, 0x8E, 0x00 }; // return
|
||||||
|
static const char PUNCTUATION_STRING[] = "&123";
|
||||||
|
static const char ALPHABET_STRING[] = "abc";
|
||||||
|
|
||||||
|
static bool equals(const QByteArray& byteArray, const uint8_t* ptr) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < byteArray.size(); i++) {
|
||||||
|
if ((char)ptr[i] != byteArray[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ptr[i] == 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderableWebEntityItem::synthesizeKeyPress(QString key) {
|
||||||
|
auto utf8Key = key.toUtf8();
|
||||||
|
|
||||||
|
int scanCode = (int)utf8Key[0];
|
||||||
|
QString keyString = key;
|
||||||
|
if (equals(utf8Key, UPWARDS_WHITE_ARROW_FROM_BAR) || equals(utf8Key, ASTERISIM) ||
|
||||||
|
equals(utf8Key, (uint8_t*)PUNCTUATION_STRING) || equals(utf8Key, (uint8_t*)ALPHABET_STRING)) {
|
||||||
|
return; // ignore
|
||||||
|
} else if (equals(utf8Key, LEFT_ARROW)) {
|
||||||
|
scanCode = Qt::Key_Backspace;
|
||||||
|
keyString = "\x08";
|
||||||
|
} else if (equals(utf8Key, RETURN_SYMBOL)) {
|
||||||
|
scanCode = Qt::Key_Return;
|
||||||
|
keyString = "\x0d";
|
||||||
|
} else if (equals(utf8Key, LEFTWARD_WHITE_ARROW)) {
|
||||||
|
scanCode = Qt::Key_Left;
|
||||||
|
keyString = "";
|
||||||
|
} else if (equals(utf8Key, RIGHTWARD_WHITE_ARROW)) {
|
||||||
|
scanCode = Qt::Key_Right;
|
||||||
|
keyString = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
QKeyEvent* pressEvent = new QKeyEvent(QEvent::KeyPress, scanCode, Qt::NoModifier, keyString);
|
||||||
|
QKeyEvent* releaseEvent = new QKeyEvent(QEvent::KeyRelease, scanCode, Qt::NoModifier, keyString);
|
||||||
|
QCoreApplication::postEvent(getEventHandler(), pressEvent);
|
||||||
|
QCoreApplication::postEvent(getEventHandler(), releaseEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderableWebEntityItem::emitScriptEvent(const QVariant& message) {
|
||||||
|
_webEntityAPIHelper->emitScriptEvent(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderableWebEntityItem::setKeyboardRaised(bool raised) {
|
||||||
|
|
||||||
|
// raise the keyboard only while in HMD mode and it's being requested.
|
||||||
|
bool value = AbstractViewStateInterface::instance()->isHMDMode() && raised;
|
||||||
|
|
||||||
|
_webSurface->getRootItem()->setProperty("keyboardRaised", QVariant(value));
|
||||||
|
}
|
||||||
|
|
|
@ -22,6 +22,27 @@ class OffscreenQmlSurface;
|
||||||
class QWindow;
|
class QWindow;
|
||||||
class QObject;
|
class QObject;
|
||||||
class EntityTreeRenderer;
|
class EntityTreeRenderer;
|
||||||
|
class RenderableWebEntityItem;
|
||||||
|
|
||||||
|
class WebEntityAPIHelper : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
void setRenderableWebEntityItem(RenderableWebEntityItem* renderableWebEntityItem) {
|
||||||
|
_renderableWebEntityItem = renderableWebEntityItem;
|
||||||
|
}
|
||||||
|
Q_INVOKABLE void synthesizeKeyPress(QString key);
|
||||||
|
|
||||||
|
// event bridge
|
||||||
|
public slots:
|
||||||
|
void emitScriptEvent(const QVariant& scriptMessage);
|
||||||
|
void emitWebEvent(const QVariant& webMessage);
|
||||||
|
signals:
|
||||||
|
void scriptEventReceived(const QVariant& message);
|
||||||
|
void webEventReceived(const QVariant& message);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
RenderableWebEntityItem* _renderableWebEntityItem{ nullptr };
|
||||||
|
};
|
||||||
|
|
||||||
class RenderableWebEntityItem : public WebEntityItem {
|
class RenderableWebEntityItem : public WebEntityItem {
|
||||||
public:
|
public:
|
||||||
|
@ -42,10 +63,16 @@ public:
|
||||||
void update(const quint64& now) override;
|
void update(const quint64& now) override;
|
||||||
bool needsToCallUpdate() const override { return _webSurface != nullptr; }
|
bool needsToCallUpdate() const override { return _webSurface != nullptr; }
|
||||||
|
|
||||||
|
virtual void emitScriptEvent(const QVariant& message) override;
|
||||||
|
void setKeyboardRaised(bool raised);
|
||||||
|
|
||||||
SIMPLE_RENDERABLE();
|
SIMPLE_RENDERABLE();
|
||||||
|
|
||||||
virtual bool isTransparent() override;
|
virtual bool isTransparent() override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void synthesizeKeyPress(QString key);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool buildWebSurface(EntityTreeRenderer* renderer);
|
bool buildWebSurface(EntityTreeRenderer* renderer);
|
||||||
void destroyWebSurface();
|
void destroyWebSurface();
|
||||||
|
@ -58,6 +85,8 @@ private:
|
||||||
bool _pressed{ false };
|
bool _pressed{ false };
|
||||||
QTouchEvent _lastTouchEvent { QEvent::TouchUpdate };
|
QTouchEvent _lastTouchEvent { QEvent::TouchUpdate };
|
||||||
uint64_t _lastRenderTime{ 0 };
|
uint64_t _lastRenderTime{ 0 };
|
||||||
|
QTouchDevice _touchDevice;
|
||||||
|
WebEntityAPIHelper* _webEntityAPIHelper;
|
||||||
|
|
||||||
QMetaObject::Connection _mousePressConnection;
|
QMetaObject::Connection _mousePressConnection;
|
||||||
QMetaObject::Connection _mouseReleaseConnection;
|
QMetaObject::Connection _mouseReleaseConnection;
|
||||||
|
@ -65,5 +94,4 @@ private:
|
||||||
QMetaObject::Connection _hoverLeaveConnection;
|
QMetaObject::Connection _hoverLeaveConnection;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif // hifi_RenderableWebEntityItem_h
|
#endif // hifi_RenderableWebEntityItem_h
|
||||||
|
|
|
@ -456,6 +456,8 @@ public:
|
||||||
bool isFading() const { return _isFading; }
|
bool isFading() const { return _isFading; }
|
||||||
float getFadingRatio() const { return (isFading() ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f); }
|
float getFadingRatio() const { return (isFading() ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f); }
|
||||||
|
|
||||||
|
virtual void emitScriptEvent(const QVariant& message) {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
void setSimulated(bool simulated) { _simulated = simulated; }
|
void setSimulated(bool simulated) { _simulated = simulated; }
|
||||||
|
|
|
@ -1289,6 +1289,17 @@ bool EntityScriptingInterface::wantsHandControllerPointerEvents(QUuid id) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntityScriptingInterface::emitScriptEvent(const EntityItemID& entityID, const QVariant& message) {
|
||||||
|
if (_entityTree) {
|
||||||
|
_entityTree->withReadLock([&] {
|
||||||
|
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(entityID));
|
||||||
|
if (entity) {
|
||||||
|
entity->emitScriptEvent(message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
float EntityScriptingInterface::calculateCost(float mass, float oldVelocity, float newVelocity) {
|
float EntityScriptingInterface::calculateCost(float mass, float oldVelocity, float newVelocity) {
|
||||||
return std::abs(mass * (newVelocity - oldVelocity));
|
return std::abs(mass * (newVelocity - oldVelocity));
|
||||||
}
|
}
|
||||||
|
@ -1305,3 +1316,4 @@ float EntityScriptingInterface::getCostMultiplier() {
|
||||||
void EntityScriptingInterface::setCostMultiplier(float value) {
|
void EntityScriptingInterface::setCostMultiplier(float value) {
|
||||||
costMultiplier = value;
|
costMultiplier = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -205,6 +205,8 @@ public slots:
|
||||||
|
|
||||||
Q_INVOKABLE bool wantsHandControllerPointerEvents(QUuid id);
|
Q_INVOKABLE bool wantsHandControllerPointerEvents(QUuid id);
|
||||||
|
|
||||||
|
Q_INVOKABLE void emitScriptEvent(const EntityItemID& entityID, const QVariant& message);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
|
void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
|
||||||
|
|
||||||
|
@ -232,6 +234,8 @@ signals:
|
||||||
void clearingEntities();
|
void clearingEntities();
|
||||||
void debitEnergySource(float value);
|
void debitEnergySource(float value);
|
||||||
|
|
||||||
|
void webEventReceived(const EntityItemID& entityItemID, const QVariant& message);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool actionWorker(const QUuid& entityID, std::function<bool(EntitySimulationPointer, EntityItemPointer)> actor);
|
bool actionWorker(const QUuid& entityID, std::function<bool(EntitySimulationPointer, EntityItemPointer)> actor);
|
||||||
bool setVoxels(QUuid entityID, std::function<bool(PolyVoxEntityItem&)> actor);
|
bool setVoxels(QUuid entityID, std::function<bool(PolyVoxEntityItem&)> actor);
|
||||||
|
|
|
@ -479,6 +479,7 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
|
||||||
|
|
||||||
auto rootContext = getRootContext();
|
auto rootContext = getRootContext();
|
||||||
rootContext->setContextProperty("urlHandler", new UrlHandler());
|
rootContext->setContextProperty("urlHandler", new UrlHandler());
|
||||||
|
rootContext->setContextProperty("resourceDirectoryUrl", QUrl::fromLocalFile(PathUtils::resourcesPath()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void OffscreenQmlSurface::resize(const QSize& newSize_, bool forceResize) {
|
void OffscreenQmlSurface::resize(const QSize& newSize_, bool forceResize) {
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <QtCore/QDataStream>
|
#include <QtCore/QDataStream>
|
||||||
#include <QtCore/QDebug>
|
#include <QtCore/QDebug>
|
||||||
#include <QtCore/QJsonDocument>
|
#include <QtCore/QJsonDocument>
|
||||||
|
#include <QtCore/QThread>
|
||||||
#include <QtCore/QUrl>
|
#include <QtCore/QUrl>
|
||||||
#include <QtNetwork/QHostInfo>
|
#include <QtNetwork/QHostInfo>
|
||||||
|
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
|
|
||||||
#include <LogHandler.h>
|
#include <LogHandler.h>
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
|
#include <SettingHandle.h>
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
#include <UUID.h>
|
#include <UUID.h>
|
||||||
|
|
||||||
|
@ -34,12 +36,14 @@
|
||||||
#include "NetworkLogging.h"
|
#include "NetworkLogging.h"
|
||||||
#include "udt/Packet.h"
|
#include "udt/Packet.h"
|
||||||
|
|
||||||
|
static Setting::Handle<quint16> LIMITED_NODELIST_LOCAL_PORT("LimitedNodeList.LocalPort", 0);
|
||||||
|
|
||||||
const std::set<NodeType_t> SOLO_NODE_TYPES = {
|
const std::set<NodeType_t> SOLO_NODE_TYPES = {
|
||||||
NodeType::AvatarMixer,
|
NodeType::AvatarMixer,
|
||||||
NodeType::AudioMixer
|
NodeType::AudioMixer
|
||||||
};
|
};
|
||||||
|
|
||||||
LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short dtlsListenPort) :
|
LimitedNodeList::LimitedNodeList(int socketListenPort, int dtlsListenPort) :
|
||||||
_sessionUUID(),
|
_sessionUUID(),
|
||||||
_nodeHash(),
|
_nodeHash(),
|
||||||
_nodeMutex(QReadWriteLock::Recursive),
|
_nodeMutex(QReadWriteLock::Recursive),
|
||||||
|
@ -62,11 +66,11 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short
|
||||||
}
|
}
|
||||||
|
|
||||||
qRegisterMetaType<ConnectionStep>("ConnectionStep");
|
qRegisterMetaType<ConnectionStep>("ConnectionStep");
|
||||||
|
auto port = (socketListenPort != INVALID_PORT) ? socketListenPort : LIMITED_NODELIST_LOCAL_PORT.get();
|
||||||
_nodeSocket.bind(QHostAddress::AnyIPv4, socketListenPort);
|
_nodeSocket.bind(QHostAddress::AnyIPv4, port);
|
||||||
qCDebug(networking) << "NodeList socket is listening on" << _nodeSocket.localPort();
|
qCDebug(networking) << "NodeList socket is listening on" << _nodeSocket.localPort();
|
||||||
|
|
||||||
if (dtlsListenPort > 0) {
|
if (dtlsListenPort != INVALID_PORT) {
|
||||||
// only create the DTLS socket during constructor if a custom port is passed
|
// only create the DTLS socket during constructor if a custom port is passed
|
||||||
_dtlsSocket = new QUdpSocket(this);
|
_dtlsSocket = new QUdpSocket(this);
|
||||||
|
|
||||||
|
@ -157,6 +161,18 @@ void LimitedNodeList::setPermissions(const NodePermissions& newPermissions) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LimitedNodeList::setSocketLocalPort(quint16 socketLocalPort) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "setSocketLocalPort", Qt::QueuedConnection,
|
||||||
|
Q_ARG(quint16, socketLocalPort));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_nodeSocket.localPort() != socketLocalPort) {
|
||||||
|
_nodeSocket.rebind(socketLocalPort);
|
||||||
|
LIMITED_NODELIST_LOCAL_PORT.set(socketLocalPort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QUdpSocket& LimitedNodeList::getDTLSSocket() {
|
QUdpSocket& LimitedNodeList::getDTLSSocket() {
|
||||||
if (!_dtlsSocket) {
|
if (!_dtlsSocket) {
|
||||||
// DTLS socket getter called but no DTLS socket exists, create it now
|
// DTLS socket getter called but no DTLS socket exists, create it now
|
||||||
|
@ -606,6 +622,12 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Signal when a socket changes, so we can start the hole punch over.
|
||||||
|
auto weakPtr = newNodePointer.toWeakRef(); // We don't want the lambda to hold a strong ref
|
||||||
|
connect(newNodePointer.data(), &NetworkPeer::socketUpdated, this, [=] {
|
||||||
|
emit nodeSocketUpdated(weakPtr);
|
||||||
|
});
|
||||||
|
|
||||||
return newNodePointer;
|
return newNodePointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,8 @@
|
||||||
#include "udt/Socket.h"
|
#include "udt/Socket.h"
|
||||||
#include "UUIDHasher.h"
|
#include "UUIDHasher.h"
|
||||||
|
|
||||||
|
const int INVALID_PORT = -1;
|
||||||
|
|
||||||
const quint64 NODE_SILENCE_THRESHOLD_MSECS = 5 * 1000;
|
const quint64 NODE_SILENCE_THRESHOLD_MSECS = 5 * 1000;
|
||||||
|
|
||||||
extern const std::set<NodeType_t> SOLO_NODE_TYPES;
|
extern const std::set<NodeType_t> SOLO_NODE_TYPES;
|
||||||
|
@ -113,6 +115,8 @@ public:
|
||||||
bool getThisNodeCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); }
|
bool getThisNodeCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); }
|
||||||
|
|
||||||
quint16 getSocketLocalPort() const { return _nodeSocket.localPort(); }
|
quint16 getSocketLocalPort() const { return _nodeSocket.localPort(); }
|
||||||
|
Q_INVOKABLE void setSocketLocalPort(quint16 socketLocalPort);
|
||||||
|
|
||||||
QUdpSocket& getDTLSSocket();
|
QUdpSocket& getDTLSSocket();
|
||||||
|
|
||||||
PacketReceiver& getPacketReceiver() { return *_packetReceiver; }
|
PacketReceiver& getPacketReceiver() { return *_packetReceiver; }
|
||||||
|
@ -250,6 +254,7 @@ signals:
|
||||||
|
|
||||||
void uuidChanged(const QUuid& ownerUUID, const QUuid& oldUUID);
|
void uuidChanged(const QUuid& ownerUUID, const QUuid& oldUUID);
|
||||||
void nodeAdded(SharedNodePointer);
|
void nodeAdded(SharedNodePointer);
|
||||||
|
void nodeSocketUpdated(SharedNodePointer);
|
||||||
void nodeKilled(SharedNodePointer);
|
void nodeKilled(SharedNodePointer);
|
||||||
void nodeActivated(SharedNodePointer);
|
void nodeActivated(SharedNodePointer);
|
||||||
|
|
||||||
|
@ -267,9 +272,9 @@ protected slots:
|
||||||
void errorTestingLocalSocket();
|
void errorTestingLocalSocket();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
LimitedNodeList(unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0);
|
LimitedNodeList(int socketListenPort = INVALID_PORT, int dtlsListenPort = INVALID_PORT);
|
||||||
LimitedNodeList(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton
|
LimitedNodeList(LimitedNodeList const&) = delete; // Don't implement, needed to avoid copies of singleton
|
||||||
void operator=(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton
|
void operator=(LimitedNodeList const&) = delete; // Don't implement, needed to avoid copies of singleton
|
||||||
|
|
||||||
qint64 sendPacket(std::unique_ptr<NLPacket> packet, const Node& destinationNode,
|
qint64 sendPacket(std::unique_ptr<NLPacket> packet, const Node& destinationNode,
|
||||||
const HifiSockAddr& overridenSockAddr);
|
const HifiSockAddr& overridenSockAddr);
|
||||||
|
|
|
@ -63,6 +63,7 @@ void NetworkPeer::setPublicSocket(const HifiSockAddr& publicSocket) {
|
||||||
|
|
||||||
if (!wasOldSocketNull) {
|
if (!wasOldSocketNull) {
|
||||||
qCDebug(networking) << "Public socket change for node" << *this;
|
qCDebug(networking) << "Public socket change for node" << *this;
|
||||||
|
emit socketUpdated();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,6 +83,7 @@ void NetworkPeer::setLocalSocket(const HifiSockAddr& localSocket) {
|
||||||
|
|
||||||
if (!wasOldSocketNull) {
|
if (!wasOldSocketNull) {
|
||||||
qCDebug(networking) << "Local socket change for node" << *this;
|
qCDebug(networking) << "Local socket change for node" << *this;
|
||||||
|
emit socketUpdated();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,6 +103,7 @@ void NetworkPeer::setSymmetricSocket(const HifiSockAddr& symmetricSocket) {
|
||||||
|
|
||||||
if (!wasOldSocketNull) {
|
if (!wasOldSocketNull) {
|
||||||
qCDebug(networking) << "Symmetric socket change for node" << *this;
|
qCDebug(networking) << "Symmetric socket change for node" << *this;
|
||||||
|
emit socketUpdated();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,9 +81,12 @@ public:
|
||||||
public slots:
|
public slots:
|
||||||
void startPingTimer();
|
void startPingTimer();
|
||||||
void stopPingTimer();
|
void stopPingTimer();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void pingTimerTimeout();
|
void pingTimerTimeout();
|
||||||
void socketActivated(const HifiSockAddr& sockAddr);
|
void socketActivated(const HifiSockAddr& sockAddr);
|
||||||
|
void socketUpdated();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void setActiveSocket(HifiSockAddr* discoveredSocket);
|
void setActiveSocket(HifiSockAddr* discoveredSocket);
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
|
|
||||||
const int KEEPALIVE_PING_INTERVAL_MS = 1000;
|
const int KEEPALIVE_PING_INTERVAL_MS = 1000;
|
||||||
|
|
||||||
NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned short dtlsListenPort) :
|
NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort) :
|
||||||
LimitedNodeList(socketListenPort, dtlsListenPort),
|
LimitedNodeList(socketListenPort, dtlsListenPort),
|
||||||
_ownerType(newOwnerType),
|
_ownerType(newOwnerType),
|
||||||
_nodeTypesOfInterest(),
|
_nodeTypesOfInterest(),
|
||||||
|
@ -93,6 +93,7 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned
|
||||||
|
|
||||||
// anytime we get a new node we will want to attempt to punch to it
|
// anytime we get a new node we will want to attempt to punch to it
|
||||||
connect(this, &LimitedNodeList::nodeAdded, this, &NodeList::startNodeHolePunch);
|
connect(this, &LimitedNodeList::nodeAdded, this, &NodeList::startNodeHolePunch);
|
||||||
|
connect(this, &LimitedNodeList::nodeSocketUpdated, this, &NodeList::startNodeHolePunch);
|
||||||
|
|
||||||
// anytime we get a new node we may need to re-send our set of ignored node IDs to it
|
// anytime we get a new node we may need to re-send our set of ignored node IDs to it
|
||||||
connect(this, &LimitedNodeList::nodeActivated, this, &NodeList::maybeSendIgnoreSetToNode);
|
connect(this, &LimitedNodeList::nodeActivated, this, &NodeList::maybeSendIgnoreSetToNode);
|
||||||
|
|
|
@ -116,10 +116,10 @@ private slots:
|
||||||
void maybeSendIgnoreSetToNode(SharedNodePointer node);
|
void maybeSendIgnoreSetToNode(SharedNodePointer node);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NodeList() : LimitedNodeList(0, 0) { assert(false); } // Not implemented, needed for DependencyManager templates compile
|
NodeList() : LimitedNodeList(INVALID_PORT, INVALID_PORT) { assert(false); } // Not implemented, needed for DependencyManager templates compile
|
||||||
NodeList(char ownerType, unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0);
|
NodeList(char ownerType, int socketListenPort = INVALID_PORT, int dtlsListenPort = INVALID_PORT);
|
||||||
NodeList(NodeList const&); // Don't implement, needed to avoid copies of singleton
|
NodeList(NodeList const&) = delete; // Don't implement, needed to avoid copies of singleton
|
||||||
void operator=(NodeList const&); // Don't implement, needed to avoid copies of singleton
|
void operator=(NodeList const&) = delete; // Don't implement, needed to avoid copies of singleton
|
||||||
|
|
||||||
void processDomainServerAuthRequest(const QByteArray& packet);
|
void processDomainServerAuthRequest(const QByteArray& packet);
|
||||||
void requestAuthForDomainServer();
|
void requestAuthForDomainServer();
|
||||||
|
|
|
@ -63,10 +63,12 @@ void Socket::bind(const QHostAddress& address, quint16 port) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Socket::rebind() {
|
void Socket::rebind() {
|
||||||
quint16 oldPort = _udpSocket.localPort();
|
rebind(_udpSocket.localPort());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Socket::rebind(quint16 localPort) {
|
||||||
_udpSocket.close();
|
_udpSocket.close();
|
||||||
bind(QHostAddress::AnyIPv4, oldPort);
|
bind(QHostAddress::AnyIPv4, localPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Socket::setSystemBufferSizes() {
|
void Socket::setSystemBufferSizes() {
|
||||||
|
|
|
@ -61,6 +61,7 @@ public:
|
||||||
qint64 writeDatagram(const QByteArray& datagram, const HifiSockAddr& sockAddr);
|
qint64 writeDatagram(const QByteArray& datagram, const HifiSockAddr& sockAddr);
|
||||||
|
|
||||||
void bind(const QHostAddress& address, quint16 port = 0);
|
void bind(const QHostAddress& address, quint16 port = 0);
|
||||||
|
void rebind(quint16 port);
|
||||||
void rebind();
|
void rebind();
|
||||||
|
|
||||||
void setPacketFilterOperator(PacketFilterOperator filterOperator) { _packetFilterOperator = filterOperator; }
|
void setPacketFilterOperator(PacketFilterOperator filterOperator) { _packetFilterOperator = filterOperator; }
|
||||||
|
|
|
@ -48,6 +48,8 @@ public:
|
||||||
|
|
||||||
virtual void pushPostUpdateLambda(void* key, std::function<void()> func) = 0;
|
virtual void pushPostUpdateLambda(void* key, std::function<void()> func) = 0;
|
||||||
|
|
||||||
|
virtual bool isHMDMode() const = 0;
|
||||||
|
|
||||||
// FIXME - we shouldn't assume that there's a single instance of an AbstractViewStateInterface
|
// FIXME - we shouldn't assume that there's a single instance of an AbstractViewStateInterface
|
||||||
static AbstractViewStateInterface* instance();
|
static AbstractViewStateInterface* instance();
|
||||||
static void setInstance(AbstractViewStateInterface* instance);
|
static void setInstance(AbstractViewStateInterface* instance);
|
||||||
|
|
|
@ -705,7 +705,7 @@ void RenderDeferredLocals::run(const render::SceneContextPointer& sceneContext,
|
||||||
static int frame = 0;
|
static int frame = 0;
|
||||||
frame++;
|
frame++;
|
||||||
|
|
||||||
if (frame % 1000 == 0) {
|
if (frame % 2000 == 0) {
|
||||||
lightClusters->updateFrustum(viewFrustum);
|
lightClusters->updateFrustum(viewFrustum);
|
||||||
|
|
||||||
lightClusters->updateVisibleLights(lightIndices);
|
lightClusters->updateVisibleLights(lightIndices);
|
||||||
|
|
49
libraries/render-utils/src/LightClusterGrid.slh
Normal file
49
libraries/render-utils/src/LightClusterGrid.slh
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<!
|
||||||
|
// LightCluserGrid.slh
|
||||||
|
//
|
||||||
|
// Created by Sam Gateau on 9/8/16.
|
||||||
|
// Copyright 2013 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
!>
|
||||||
|
<@if not RENDER_LIGHT_CLUSTER_GRID_SLH@>
|
||||||
|
<@def RENDER_LIGHT_CLUSTER_GRID_SLH@>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct FrustumGrid {
|
||||||
|
float frustumNear;
|
||||||
|
float rangeNear;
|
||||||
|
float rangeFar;
|
||||||
|
float frustumFar;
|
||||||
|
ivec3 dims;
|
||||||
|
float spare;
|
||||||
|
mat4 eyeToGridProj;
|
||||||
|
mat4 worldToEyeMat;
|
||||||
|
mat4 eyeToWorldMat;
|
||||||
|
};
|
||||||
|
|
||||||
|
uniform frustumGridBuffer {
|
||||||
|
FrustumGrid frustumGrid;
|
||||||
|
};
|
||||||
|
|
||||||
|
float projection_getNear(mat4 projection) {
|
||||||
|
float planeC = projection[2][3] + projection[2][2];
|
||||||
|
float planeD = projection[3][2];
|
||||||
|
return planeD / planeC;
|
||||||
|
}
|
||||||
|
float projection_getFar(mat4 projection) {
|
||||||
|
//float planeA = projection[0][3] - projection[0][2]; All Zeros
|
||||||
|
//float planeB = projection[1][3] - projection[1][2]; All Zeros
|
||||||
|
float planeC = projection[2][3] - projection[2][2];
|
||||||
|
float planeD = /*projection[3][3]*/ -projection[3][2];
|
||||||
|
return planeD / planeC;
|
||||||
|
}
|
||||||
|
|
||||||
|
// glsl / C++ compatible source as interface for FrustrumGrid
|
||||||
|
<@include LightClusterGrid_shared.slh@>
|
||||||
|
|
||||||
|
// end of hybrid include
|
||||||
|
|
||||||
|
<@endif@>
|
92
libraries/render-utils/src/LightClusterGrid_shared.slh
Normal file
92
libraries/render-utils/src/LightClusterGrid_shared.slh
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
// glsl / C++ compatible source as interface for FrustrumGrid
|
||||||
|
|
||||||
|
int frustumGrid_numClusters() {
|
||||||
|
return frustumGrid.dims.x * frustumGrid.dims.y * frustumGrid.dims.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float frustumGrid_depthRamp(float linear) {
|
||||||
|
// return linear;
|
||||||
|
return linear * linear;
|
||||||
|
}
|
||||||
|
float frustumGrid_depthRampInverse(float volume) {
|
||||||
|
// return volume;
|
||||||
|
return sqrt(volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 frustumGrid_gridToVolume(vec3 pos, ivec3 dims) {
|
||||||
|
vec3 gridScale = vec3(1.0, 1.0, 1.0) / vec3(dims);
|
||||||
|
vec3 volumePos = pos * gridScale;
|
||||||
|
volumePos.z = frustumGrid_depthRamp(volumePos.z);
|
||||||
|
return volumePos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vec3 frustumGrid_volumeToGrid(vec3 vpos, ivec3 dims) {
|
||||||
|
vec3 gridPos = vec3(vpos.x, vpos.y, frustumGrid_depthRampInverse(vpos.z)) * vec3(dims);
|
||||||
|
return gridPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 frustumGrid_volumeToClip(vec3 vpos, float rangeNear, float rangeFar) {
|
||||||
|
vec3 ndcPos = vec3(-1.0 + 2.0 * vpos.x, -1.0 + 2.0 * vpos.y, vpos.z);
|
||||||
|
float depth = rangeNear * (1 - ndcPos.z) + rangeFar * (ndcPos.z);
|
||||||
|
vec4 clipPos = vec4(ndcPos.x * depth, ndcPos.y * depth, 1.0, depth);
|
||||||
|
return clipPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 frustumGrid_clipToEye(vec4 clipPos, mat4 projection) {
|
||||||
|
return vec3(
|
||||||
|
(clipPos.x + projection[2][0] * clipPos.w) / projection[0][0],
|
||||||
|
(clipPos.y + projection[2][1] * clipPos.w) / projection[1][1],
|
||||||
|
-clipPos.w
|
||||||
|
//, (clipPos.z - projection[3][3] * clipPos.w) / projection[3][2]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 frustumGrid_volumeToEye(vec3 vpos, mat4 projection, float rangeNear, float rangeFar) {
|
||||||
|
return frustumGrid_clipToEye(frustumGrid_volumeToClip(vpos, rangeNear, rangeFar), projection);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vec3 frustumGrid_eyeToVolume(vec3 epos, mat4 projection, float rangeNear, float rangeFar) {
|
||||||
|
vec4 clipPos = vec4(epos.x * projection[0][0] + epos.z * projection[2][0],
|
||||||
|
epos.y * projection[1][1] + epos.z * projection[2][1],
|
||||||
|
epos.z * projection[2][2] + projection[2][3],
|
||||||
|
-epos.z);
|
||||||
|
vec4 ndcPos = clipPos / clipPos.w;
|
||||||
|
|
||||||
|
vec3 volumePos = vec3(0.5 * (ndcPos.x + 1.0), 0.5 * (ndcPos.y + 1.0), (clipPos.w - rangeNear) / (rangeFar - rangeNear));
|
||||||
|
return volumePos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vec3 frustumGrid_clusterPosToEye(ivec3 clusterPos, vec3 offset = vec3(0.5)) {
|
||||||
|
|
||||||
|
vec3 cvpos = vec3(clusterPos) + offset;
|
||||||
|
|
||||||
|
|
||||||
|
vec3 volumePos = frustumGrid_gridToVolume(cvpos, frustumGrid.dims);
|
||||||
|
|
||||||
|
vec3 eyePos = frustumGrid_volumeToEye(volumePos, frustumGrid.eyeToGridProj, frustumGrid.rangeNear, frustumGrid.rangeFar);
|
||||||
|
|
||||||
|
return eyePos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ivec3 frustumGrid_eyeToClusterPos(vec3 eyePos) {
|
||||||
|
|
||||||
|
vec3 volumePos = frustumGrid_eyeToVolume(eyePos, frustumGrid.eyeToGridProj, frustumGrid.rangeNear, frustumGrid.rangeFar);
|
||||||
|
|
||||||
|
vec3 gridPos = frustumGrid_volumeToGrid(volumePos, frustumGrid.dims);
|
||||||
|
|
||||||
|
|
||||||
|
return ivec3(gridPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 frustumGrid_eyeToWorld(vec4 eyePos) {
|
||||||
|
return frustumGrid.eyeToWorldMat * eyePos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// <@if 1@>
|
||||||
|
// Trigger Scribe include
|
||||||
|
// <@endif@> <!def that !> End C++ compatible
|
|
@ -12,11 +12,25 @@
|
||||||
|
|
||||||
|
|
||||||
#include <gpu/Context.h>
|
#include <gpu/Context.h>
|
||||||
|
|
||||||
|
#include <gpu/StandardShaderLib.h>
|
||||||
|
|
||||||
#include "lightClusters_drawGrid_vert.h"
|
#include "lightClusters_drawGrid_vert.h"
|
||||||
#include "lightClusters_drawGrid_frag.h"
|
#include "lightClusters_drawGrid_frag.h"
|
||||||
|
|
||||||
|
//#include "lightClusters_drawClusterFromDepth_vert.h"
|
||||||
|
#include "lightClusters_drawClusterFromDepth_frag.h"
|
||||||
|
|
||||||
|
enum LightClusterGridShader_MapSlot {
|
||||||
|
DEFERRED_BUFFER_LINEAR_DEPTH_UNIT = 7,
|
||||||
|
};
|
||||||
|
|
||||||
enum LightClusterGridShader_BufferSlot {
|
enum LightClusterGridShader_BufferSlot {
|
||||||
LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT = 0,
|
LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT = 0,
|
||||||
|
DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT,
|
||||||
|
CAMERA_CORRECTION_BUFFER_SLOT,
|
||||||
|
LIGHT_GPU_SLOT = render::ShapePipeline::Slot::LIGHT,
|
||||||
|
LIGHT_INDEX_GPU_SLOT,
|
||||||
};
|
};
|
||||||
|
|
||||||
#include "DeferredLightingEffect.h"
|
#include "DeferredLightingEffect.h"
|
||||||
|
@ -27,7 +41,7 @@ LightClusters::LightClusters() {
|
||||||
void LightClusters::updateFrustum(const ViewFrustum& frustum) {
|
void LightClusters::updateFrustum(const ViewFrustum& frustum) {
|
||||||
_frustum = frustum;
|
_frustum = frustum;
|
||||||
|
|
||||||
_frustrumGridBuffer.edit().updateFrustrum(frustum);
|
_frustumGridBuffer.edit().updateFrustum(frustum);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LightClusters::updateLightStage(const LightStagePointer& lightStage) {
|
void LightClusters::updateLightStage(const LightStagePointer& lightStage) {
|
||||||
|
@ -57,7 +71,7 @@ const gpu::PipelinePointer DebugLightClusters::getDrawClusterGridPipeline() {
|
||||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||||
|
|
||||||
gpu::Shader::BindingSet slotBindings;
|
gpu::Shader::BindingSet slotBindings;
|
||||||
slotBindings.insert(gpu::Shader::Binding(std::string("frustrumGridBuffer"), LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT));
|
slotBindings.insert(gpu::Shader::Binding(std::string("frustumGridBuffer"), LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT));
|
||||||
|
|
||||||
gpu::Shader::makeProgram(*program, slotBindings);
|
gpu::Shader::makeProgram(*program, slotBindings);
|
||||||
|
|
||||||
|
@ -75,26 +89,49 @@ const gpu::PipelinePointer DebugLightClusters::getDrawClusterGridPipeline() {
|
||||||
return _drawClusterGrid;
|
return _drawClusterGrid;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebugLightClusters::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) {
|
const gpu::PipelinePointer DebugLightClusters::getDrawClusterFromDepthPipeline() {
|
||||||
|
if (!_drawClusterFromDepth) {
|
||||||
|
// auto vs = gpu::Shader::createVertex(std::string(lightClusters_drawGrid_vert));
|
||||||
|
auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS();
|
||||||
|
auto ps = gpu::Shader::createPixel(std::string(lightClusters_drawClusterFromDepth_frag));
|
||||||
|
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||||
|
|
||||||
|
gpu::Shader::BindingSet slotBindings;
|
||||||
|
slotBindings.insert(gpu::Shader::Binding(std::string("frustumGridBuffer"), LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT));
|
||||||
|
slotBindings.insert(gpu::Shader::Binding(std::string("linearZeyeMap"), DEFERRED_BUFFER_LINEAR_DEPTH_UNIT));
|
||||||
|
slotBindings.insert(gpu::Shader::Binding(std::string("cameraCorrectionBuffer"), CAMERA_CORRECTION_BUFFER_SLOT));
|
||||||
|
slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT));
|
||||||
|
|
||||||
|
gpu::Shader::makeProgram(*program, slotBindings);
|
||||||
|
|
||||||
|
|
||||||
|
auto state = std::make_shared<gpu::State>();
|
||||||
|
|
||||||
|
// state->setDepthTest(true, false, gpu::LESS_EQUAL);
|
||||||
|
|
||||||
|
// Blend on transparent
|
||||||
|
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||||
|
|
||||||
|
// Good to go add the brand new pipeline
|
||||||
|
_drawClusterFromDepth = gpu::Pipeline::create(program, state);
|
||||||
|
}
|
||||||
|
return _drawClusterFromDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugLightClusters::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& inputs) {
|
||||||
auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();
|
auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();
|
||||||
auto lightClusters = deferredLightingEffect->getLightClusters();
|
auto lightClusters = deferredLightingEffect->getLightClusters();
|
||||||
|
|
||||||
|
|
||||||
/* auto deferredTransform = inputs.get0();
|
auto deferredTransform = inputs.get0();
|
||||||
auto deferredFramebuffer = inputs.get1();
|
auto deferredFramebuffer = inputs.get1();
|
||||||
auto lightingModel = inputs.get2();
|
auto lightingModel = inputs.get2();
|
||||||
auto surfaceGeometryFramebuffer = inputs.get3();
|
auto surfaceGeometryFramebuffer = inputs.get3();
|
||||||
auto ssaoFramebuffer = inputs.get4();
|
|
||||||
auto subsurfaceScatteringResource = inputs.get5();
|
|
||||||
*/
|
|
||||||
auto args = renderContext->args;
|
auto args = renderContext->args;
|
||||||
|
|
||||||
|
|
||||||
auto drawPipeline = getDrawClusterGridPipeline();
|
|
||||||
|
|
||||||
gpu::Batch batch;
|
gpu::Batch batch;
|
||||||
|
|
||||||
|
|
||||||
// Assign the camera transform
|
// Assign the camera transform
|
||||||
batch.setViewportTransform(args->_viewport);
|
batch.setViewportTransform(args->_viewport);
|
||||||
glm::mat4 projMat;
|
glm::mat4 projMat;
|
||||||
|
@ -108,15 +145,34 @@ void DebugLightClusters::run(const render::SceneContextPointer& sceneContext, co
|
||||||
// Then the actual ClusterGrid attributes
|
// Then the actual ClusterGrid attributes
|
||||||
batch.setModelTransform(Transform());
|
batch.setModelTransform(Transform());
|
||||||
|
|
||||||
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT, lightClusters->_frustrumGridBuffer);
|
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT, lightClusters->_frustumGridBuffer);
|
||||||
|
|
||||||
|
|
||||||
// bind the one gpu::Pipeline we need
|
|
||||||
batch.setPipeline(drawPipeline);
|
|
||||||
|
|
||||||
if (true) {
|
if (true) {
|
||||||
batch.draw(gpu::LINES, 24, 0);
|
// bind the one gpu::Pipeline we need
|
||||||
|
batch.setPipeline(getDrawClusterGridPipeline());
|
||||||
|
|
||||||
|
auto dims = lightClusters->_frustumGridBuffer->dims;
|
||||||
|
glm::ivec3 summedDims(dims.x*dims.y * dims.z, dims.x*dims.y, dims.x);
|
||||||
|
batch.drawInstanced(summedDims.x, gpu::LINES, 24, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
batch.setPipeline(getDrawClusterFromDepthPipeline());
|
||||||
|
batch.setUniformBuffer(DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT, deferredTransform->getFrameTransformBuffer());
|
||||||
|
|
||||||
|
if (surfaceGeometryFramebuffer) {
|
||||||
|
batch.setResourceTexture(DEFERRED_BUFFER_LINEAR_DEPTH_UNIT, surfaceGeometryFramebuffer->getLinearDepthTexture());
|
||||||
|
}
|
||||||
|
|
||||||
|
batch.draw(gpu::TRIANGLE_STRIP, 4, 0);
|
||||||
|
|
||||||
|
// Probably not necessary in the long run because the gpu layer would unbound this texture if used as render target
|
||||||
|
|
||||||
|
batch.setResourceTexture(DEFERRED_BUFFER_LINEAR_DEPTH_UNIT, nullptr);
|
||||||
|
batch.setUniformBuffer(DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
args->_context->appendFrameBatch(batch);
|
args->_context->appendFrameBatch(batch);
|
||||||
|
|
||||||
|
|
|
@ -20,65 +20,33 @@
|
||||||
class FrustumGrid {
|
class FrustumGrid {
|
||||||
public:
|
public:
|
||||||
float _near { 0.1f };
|
float _near { 0.1f };
|
||||||
float _nearPrime { 1.0f };
|
float rangeNear { 1.0f };
|
||||||
float _farPrime { 400.0f };
|
float rangeFar { 100.0f };
|
||||||
float _far { 10000.0f };
|
float _far { 10000.0f };
|
||||||
|
|
||||||
glm::uvec3 _dims { 16, 16, 16 };
|
glm::ivec3 dims { 8, 8, 8 };
|
||||||
float spare;
|
float spare;
|
||||||
|
|
||||||
glm::mat4 _eyeToGridProj;
|
glm::mat4 eyeToGridProj;
|
||||||
glm::mat4 _eyeToGridProjInv;
|
|
||||||
glm::mat4 _worldToEyeMat;
|
glm::mat4 _worldToEyeMat;
|
||||||
glm::mat4 _eyeToWorldMat;
|
glm::mat4 eyeToWorldMat;
|
||||||
|
|
||||||
float viewToLinearDepth(float depth) const {
|
void updateFrustum(const ViewFrustum& frustum) {
|
||||||
float nDepth = -depth;
|
eyeToGridProj = frustum.evalProjectionMatrixRange(rangeNear, rangeFar);
|
||||||
float ldepth = (nDepth - _nearPrime) / (_farPrime - _nearPrime);
|
|
||||||
|
|
||||||
if (ldepth < 0.0f) {
|
|
||||||
return (nDepth - _near) / (_nearPrime - _near) - 1.0f;
|
|
||||||
}
|
|
||||||
if (ldepth > 1.0f) {
|
|
||||||
return (nDepth - _farPrime) / (_far - _farPrime) + 1.0f;
|
|
||||||
}
|
|
||||||
return ldepth;
|
|
||||||
}
|
|
||||||
|
|
||||||
float linearToGridDepth(float depth) const {
|
|
||||||
return depth / (float) _dims.z;
|
|
||||||
}
|
|
||||||
|
|
||||||
int gridDepthToLayer(float gridDepth) const {
|
|
||||||
return (int) gridDepth;
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec2 ndcToGridXY(const glm::vec3& ncpos) const {
|
|
||||||
return 0.5f * glm::vec2((ncpos.x + 1.0f) / (float)_dims.x, (ncpos.y + 1.0f) / (float)_dims.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::ivec3 viewToGridPos(const glm::vec3& pos) const {
|
|
||||||
float z = linearToGridDepth(viewToLinearDepth(pos.z));
|
|
||||||
|
|
||||||
auto cpos = _eyeToGridProj * glm::vec4(pos, 1.0f);
|
|
||||||
|
|
||||||
glm::vec3 ncpos(cpos);
|
|
||||||
ncpos /= cpos.w;
|
|
||||||
|
|
||||||
|
|
||||||
return glm::ivec3(ndcToGridXY(ncpos), (int) linearToGridDepth(z));
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateFrustrum(const ViewFrustum& frustum) {
|
|
||||||
_eyeToGridProj = frustum.evalProjectionMatrixRange(_nearPrime, _farPrime);
|
|
||||||
_eyeToGridProjInv = glm::inverse(_eyeToGridProj);
|
|
||||||
|
|
||||||
Transform view;
|
Transform view;
|
||||||
frustum.evalViewTransform(view);
|
frustum.evalViewTransform(view);
|
||||||
_eyeToWorldMat = view.getMatrix();
|
eyeToWorldMat = view.getMatrix();
|
||||||
_worldToEyeMat = view.getInverseMatrix();
|
_worldToEyeMat = view.getInverseMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy paste of the slh functions
|
||||||
|
using vec3 = glm::vec3;
|
||||||
|
using ivec3 = glm::ivec3;
|
||||||
|
using mat4 = glm::mat4;
|
||||||
|
#define frustumGrid (*this)
|
||||||
|
#include "LightClusterGrid_shared.slh"
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class LightClusters {
|
class LightClusters {
|
||||||
|
@ -93,15 +61,13 @@ public:
|
||||||
void updateVisibleLights(const LightStage::LightIndices& visibleLights);
|
void updateVisibleLights(const LightStage::LightIndices& visibleLights);
|
||||||
|
|
||||||
|
|
||||||
// FrustumGrid _grid;
|
|
||||||
|
|
||||||
ViewFrustum _frustum;
|
ViewFrustum _frustum;
|
||||||
|
|
||||||
LightStagePointer _lightStage;
|
LightStagePointer _lightStage;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
gpu::StructBuffer<FrustumGrid> _frustrumGridBuffer;
|
gpu::StructBuffer<FrustumGrid> _frustumGridBuffer;
|
||||||
|
|
||||||
|
|
||||||
gpu::BufferPointer _lightIndicesBuffer;
|
gpu::BufferPointer _lightIndicesBuffer;
|
||||||
|
@ -131,22 +97,30 @@ protected:
|
||||||
int numDrawn { 0 };
|
int numDrawn { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#include "DeferredFrameTransform.h"
|
||||||
|
#include "DeferredFramebuffer.h"
|
||||||
|
#include "LightingModel.h"
|
||||||
|
#include "SurfaceGeometryPass.h"
|
||||||
|
|
||||||
class DebugLightClusters {
|
class DebugLightClusters {
|
||||||
public:
|
public:
|
||||||
// using Inputs = render::VaryingSet6 < DeferredFrameTransformPointer, DeferredFramebufferPointer, LightingModelPointer, SurfaceGeometryFramebufferPointer, AmbientOcclusionFramebufferPointer, SubsurfaceScatteringResourcePointer>;
|
using Inputs = render::VaryingSet4 < DeferredFrameTransformPointer, DeferredFramebufferPointer, LightingModelPointer, SurfaceGeometryFramebufferPointer>;
|
||||||
using Config = DebugLightClustersConfig;
|
using Config = DebugLightClustersConfig;
|
||||||
using JobModel = render::Job::Model<DebugLightClusters, Config>;
|
using JobModel = render::Job::ModelI<DebugLightClusters, Inputs, Config>;
|
||||||
|
|
||||||
DebugLightClusters();
|
DebugLightClusters();
|
||||||
|
|
||||||
void configure(const Config& config);
|
void configure(const Config& config);
|
||||||
|
|
||||||
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
|
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& inputs);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
gpu::BufferPointer _gridBuffer;
|
gpu::BufferPointer _gridBuffer;
|
||||||
gpu::PipelinePointer _drawClusterGrid;
|
gpu::PipelinePointer _drawClusterGrid;
|
||||||
|
gpu::PipelinePointer _drawClusterFromDepth;
|
||||||
const gpu::PipelinePointer getDrawClusterGridPipeline();
|
const gpu::PipelinePointer getDrawClusterGridPipeline();
|
||||||
|
const gpu::PipelinePointer getDrawClusterFromDepthPipeline();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -192,7 +192,8 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) {
|
||||||
|
|
||||||
// LIght Cluster Grid Debuging job
|
// LIght Cluster Grid Debuging job
|
||||||
{
|
{
|
||||||
addJob<DebugLightClusters>("DebugLightClusters");
|
const auto debugLightClustersInputs = DebugLightClusters::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel, linearDepthTarget).hasVarying();
|
||||||
|
addJob<DebugLightClusters>("DebugLightClusters", debugLightClustersInputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status icon rendering job
|
// Status icon rendering job
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
<@include gpu/Config.slh@>
|
||||||
|
<$VERSION_HEADER$>
|
||||||
|
// Generated on <$_SCRIBE_DATE$>
|
||||||
|
// lightClusters_drawClusterFro Depth.slf
|
||||||
|
//
|
||||||
|
// Created by Sam Gateau on 9/8/2016.
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
// Everything about deferred buffer
|
||||||
|
<@include DeferredBufferRead.slh@>
|
||||||
|
|
||||||
|
|
||||||
|
<@include LightClusterGrid.slh@>
|
||||||
|
|
||||||
|
<@include gpu/Color.slh@>
|
||||||
|
<$declareColorWheel()$>
|
||||||
|
|
||||||
|
|
||||||
|
in vec4 varTexCoord0;
|
||||||
|
out vec4 _fragColor;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
|
||||||
|
// Grab the fragment data from the uv
|
||||||
|
vec2 texCoord = varTexCoord0.st;
|
||||||
|
|
||||||
|
float Zeye = texture(linearZeyeMap, texCoord).x;
|
||||||
|
_fragColor = vec4(vec3(0.1 * (10 - Zeye)), 1.0);
|
||||||
|
// return;
|
||||||
|
|
||||||
|
vec4 fragPosition = unpackDeferredPositionFromZeye(texCoord);
|
||||||
|
|
||||||
|
|
||||||
|
// return;
|
||||||
|
|
||||||
|
ivec3 dims = frustumGrid.dims.xyz;
|
||||||
|
|
||||||
|
ivec3 summedDims = ivec3(dims.x * dims.y, dims.x, 1);
|
||||||
|
|
||||||
|
|
||||||
|
vec3 eyePos = fragPosition.xyz;
|
||||||
|
|
||||||
|
// vec4 worldPos = frustumGrid_eyeToWorld(vec4(eyePos.xyz, 1.0));
|
||||||
|
|
||||||
|
ivec3 clusterPos = frustumGrid_eyeToClusterPos(eyePos);
|
||||||
|
|
||||||
|
|
||||||
|
// standard transform
|
||||||
|
// TransformCamera cam = getTransformCamera();
|
||||||
|
// <$transformWorldToClipPos(cam, worldPos, gl_Position)$>
|
||||||
|
|
||||||
|
_fragColor = vec4(colorWheel(fract(float(clusterPos.z * summedDims.x + clusterPos.y * summedDims.y + clusterPos.x) / float(frustumGrid_numClusters()))), 0.9);
|
||||||
|
_fragColor = vec4(abs(fract(eyePos * 0.5)), 0.9);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
<@include gpu/Config.slh@>
|
||||||
|
<$VERSION_HEADER$>
|
||||||
|
// Generated on <$_SCRIBE_DATE$>
|
||||||
|
//
|
||||||
|
// lightClusters_drawClusterFrom Depth.slv
|
||||||
|
// Vertex shader
|
||||||
|
//
|
||||||
|
// Created by Sam Gateau on 9/8/2016
|
||||||
|
// 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 gpu/Transform.slh@>
|
||||||
|
<$declareStandardTransform()$>
|
||||||
|
|
||||||
|
<@include LightClusterGrid.slh@>
|
||||||
|
|
||||||
|
<@include gpu/Color.slh@>
|
||||||
|
<$declareColorWheel()$>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
out vec4 varColor;
|
||||||
|
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
const vec4 UNIT_BOX[8] = vec4[8](
|
||||||
|
vec4(0.0, 0.0, 0.0, 1.0),
|
||||||
|
vec4(1.0, 0.0, 0.0, 1.0),
|
||||||
|
vec4(0.0, 1.0, 0.0, 1.0),
|
||||||
|
vec4(1.0, 1.0, 0.0, 1.0),
|
||||||
|
vec4(0.0, 0.0, 1.0, 1.0),
|
||||||
|
vec4(1.0, 0.0, 1.0, 1.0),
|
||||||
|
vec4(0.0, 1.0, 1.0, 1.0),
|
||||||
|
vec4(1.0, 1.0, 1.0, 1.0)
|
||||||
|
);
|
||||||
|
const int UNIT_BOX_LINE_INDICES[24] = int[24](
|
||||||
|
0, 1,
|
||||||
|
1, 3,
|
||||||
|
3, 2,
|
||||||
|
2, 0,
|
||||||
|
4, 5,
|
||||||
|
5, 7,
|
||||||
|
7, 6,
|
||||||
|
6, 4,
|
||||||
|
2, 6,
|
||||||
|
3, 7,
|
||||||
|
0, 4,
|
||||||
|
1, 5
|
||||||
|
);
|
||||||
|
vec4 pos = UNIT_BOX[UNIT_BOX_LINE_INDICES[gl_VertexID]];
|
||||||
|
|
||||||
|
ivec3 dims = frustumGrid.dims.xyz;
|
||||||
|
|
||||||
|
ivec3 summedDims = ivec3(dims.x * dims.y, dims.x, 1);
|
||||||
|
|
||||||
|
int layer = gl_InstanceID / summedDims.x;
|
||||||
|
int offsetInLayer = gl_InstanceID % summedDims.x;
|
||||||
|
ivec3 clusterPos = ivec3(offsetInLayer % summedDims.y, offsetInLayer / summedDims.y, layer);
|
||||||
|
|
||||||
|
|
||||||
|
vec3 eyePos = frustumGrid_clusterPosToEye(clusterPos, vec3(0.05) + 0.9 * pos.xyz);
|
||||||
|
vec4 worldPos = frustumGrid_eyeToWorld(vec4(eyePos.xyz, 1.0));
|
||||||
|
|
||||||
|
|
||||||
|
// standard transform
|
||||||
|
TransformCamera cam = getTransformCamera();
|
||||||
|
<$transformWorldToClipPos(cam, worldPos, gl_Position)$>
|
||||||
|
|
||||||
|
varColor = vec4(colorWheel(fract(float(gl_InstanceID) / float(frustumGrid_numClusters()))), 0.9);
|
||||||
|
}
|
|
@ -13,39 +13,15 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
<@include gpu/Transform.slh@>
|
<@include gpu/Transform.slh@>
|
||||||
|
|
||||||
<$declareStandardTransform()$>
|
<$declareStandardTransform()$>
|
||||||
|
|
||||||
|
<@include LightClusterGrid.slh@>
|
||||||
|
|
||||||
<@include gpu/Color.slh@>
|
<@include gpu/Color.slh@>
|
||||||
<$declareColorWheel()$>
|
<$declareColorWheel()$>
|
||||||
|
|
||||||
|
|
||||||
struct FrustrumGrid {
|
|
||||||
vec4 nearFarRange;
|
|
||||||
ivec4 dims;
|
|
||||||
mat4 eyeToGridProj;
|
|
||||||
mat4 eyeToGridProjInv;
|
|
||||||
mat4 worldToEyeMat;
|
|
||||||
mat4 eyeToWorldMat;
|
|
||||||
};
|
|
||||||
|
|
||||||
uniform frustrumGridBuffer {
|
|
||||||
FrustrumGrid frustrumGrid;
|
|
||||||
};
|
|
||||||
|
|
||||||
float getProjectionNear(mat4 projection) {
|
|
||||||
float planeC = projection[2][3] + projection[2][2];
|
|
||||||
float planeD = projection[3][2];
|
|
||||||
return planeD / planeC;
|
|
||||||
}
|
|
||||||
float getProjectionFar(mat4 projection) {
|
|
||||||
//float planeA = projection[0][3] - projection[0][2];
|
|
||||||
//float planeB = projection[1][3] - projection[1][2];
|
|
||||||
float planeC = projection[2][3] - projection[2][2];
|
|
||||||
float planeD = /*projection[3][3]*/ - projection[3][2];
|
|
||||||
return planeD / planeC;
|
|
||||||
}
|
|
||||||
//uniform ivec4 inClusterLocation;
|
|
||||||
|
|
||||||
out vec4 varColor;
|
out vec4 varColor;
|
||||||
|
|
||||||
|
@ -77,35 +53,22 @@ void main(void) {
|
||||||
);
|
);
|
||||||
vec4 pos = UNIT_BOX[UNIT_BOX_LINE_INDICES[gl_VertexID]];
|
vec4 pos = UNIT_BOX[UNIT_BOX_LINE_INDICES[gl_VertexID]];
|
||||||
|
|
||||||
// pos.z -= 10.0f;
|
ivec3 dims = frustumGrid.dims.xyz;
|
||||||
vec3 cpos = vec3(1, 0, 1);
|
|
||||||
|
|
||||||
// float z = frustrumGrid.nearFarRange.y * (1 - pos.z) + frustrumGrid.nearFarRange.z * (pos.z);
|
ivec3 summedDims = ivec3(dims.x * dims.y, dims.x, 1);
|
||||||
vec3 gridScale = vec3(0.5, 0.5, 1.0) / vec3(frustrumGrid.dims.xyz);
|
|
||||||
|
|
||||||
vec3 cvpos = cpos + pos.xyz;
|
int layer = gl_InstanceID / summedDims.x;
|
||||||
|
int offsetInLayer = gl_InstanceID % summedDims.x;
|
||||||
vec3 clusterStart = vec3(-1.0 + cvpos.x * gridScale.x, -1.0 + cvpos.y * gridScale.y, cvpos.z * gridScale.z);
|
ivec3 clusterPos = ivec3(offsetInLayer % summedDims.y, offsetInLayer / summedDims.y, layer);
|
||||||
|
|
||||||
float z = getProjectionNear(frustrumGrid.eyeToGridProj) * (1 - clusterStart.z) + getProjectionFar(frustrumGrid.eyeToGridProj) * (clusterStart.z);
|
|
||||||
|
|
||||||
vec4 eyePos = frustrumGrid.eyeToGridProjInv * (vec4(clusterStart.xy, 1.0, 1.0) * z);
|
|
||||||
|
|
||||||
eyePos.xyz *= eyePos.w;
|
|
||||||
|
|
||||||
vec4 worldPos = frustrumGrid.eyeToWorldMat * vec4(eyePos.xyz, 1.0);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
vec3 eyePos = frustumGrid_clusterPosToEye(clusterPos, vec3(0.05) + 0.9 * pos.xyz);
|
||||||
int cellIsEmpty = sign(inClusterLocation.w);
|
vec4 worldPos = frustumGrid_eyeToWorld(vec4(eyePos.xyz, 1.0));
|
||||||
ivec4 cellLocation = ivec4(inClusterLocation.xyz, (inClusterLocation.w < 0 ? -inClusterLocation.w : inClusterLocation.w));
|
|
||||||
vec4 cellBound = evalBound(cellLocation);
|
|
||||||
|
|
||||||
pos.xyz = cellBound.xyz + vec3(cellBound.w) * pos.xyz;
|
|
||||||
*/
|
|
||||||
// standard transform
|
// standard transform
|
||||||
TransformCamera cam = getTransformCamera();
|
TransformCamera cam = getTransformCamera();
|
||||||
<$transformWorldToClipPos(cam, worldPos, gl_Position)$>
|
<$transformWorldToClipPos(cam, worldPos, gl_Position)$>
|
||||||
|
|
||||||
varColor = vec4(colorWheel(fract(float(gl_VertexID) / 24.0)), 0.9);
|
varColor = vec4(colorWheel(fract(float(gl_InstanceID) / float(frustumGrid_numClusters()))), 0.9);
|
||||||
}
|
}
|
|
@ -189,6 +189,38 @@ protected:
|
||||||
float _step { 0.1f };
|
float _step { 0.1f };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class IntPreference : public TypedPreference<int> {
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(float value READ getValue WRITE setValue NOTIFY valueChanged)
|
||||||
|
Q_PROPERTY(float min READ getMin CONSTANT)
|
||||||
|
Q_PROPERTY(float max READ getMax CONSTANT)
|
||||||
|
Q_PROPERTY(float step READ getStep CONSTANT)
|
||||||
|
|
||||||
|
public:
|
||||||
|
IntPreference(const QString& category, const QString& name, Getter getter, Setter setter)
|
||||||
|
: TypedPreference(category, name, getter, setter) { }
|
||||||
|
|
||||||
|
float getMin() const { return _min; }
|
||||||
|
void setMin(float min) { _min = min; };
|
||||||
|
|
||||||
|
float getMax() const { return _max; }
|
||||||
|
void setMax(float max) { _max = max; };
|
||||||
|
|
||||||
|
float getStep() const { return _step; }
|
||||||
|
void setStep(float step) { _step = step; };
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void valueChanged();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void emitValueChanged() override { emit valueChanged(); }
|
||||||
|
|
||||||
|
int _min { std::numeric_limits<int>::min() };
|
||||||
|
int _max { std::numeric_limits<int>::max() };
|
||||||
|
int _step { 1 };
|
||||||
|
};
|
||||||
|
|
||||||
class StringPreference : public TypedPreference<QString> {
|
class StringPreference : public TypedPreference<QString> {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(QString value READ getValue WRITE setValue NOTIFY valueChanged)
|
Q_PROPERTY(QString value READ getValue WRITE setValue NOTIFY valueChanged)
|
||||||
|
@ -222,6 +254,15 @@ public:
|
||||||
Type getType() override { return Spinner; }
|
Type getType() override { return Spinner; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class IntSpinnerPreference : public IntPreference {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
IntSpinnerPreference(const QString& category, const QString& name, Getter getter, Setter setter)
|
||||||
|
: IntPreference(category, name, getter, setter) { }
|
||||||
|
|
||||||
|
Type getType() override { return Spinner; }
|
||||||
|
};
|
||||||
|
|
||||||
class EditPreference : public StringPreference {
|
class EditPreference : public StringPreference {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(QString placeholderText READ getPlaceholderText CONSTANT)
|
Q_PROPERTY(QString placeholderText READ getPlaceholderText CONSTANT)
|
||||||
|
|
|
@ -2271,6 +2271,7 @@ function MyController(hand) {
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
pointerEvent = this.touchingEnterPointerEvent;
|
pointerEvent = this.touchingEnterPointerEvent;
|
||||||
|
pointerEvent.type = "Release";
|
||||||
pointerEvent.button = "Primary";
|
pointerEvent.button = "Primary";
|
||||||
pointerEvent.isPrimaryHeld = false;
|
pointerEvent.isPrimaryHeld = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
var RAD_TO_DEG = 180 / Math.PI;
|
var RAD_TO_DEG = 180 / Math.PI;
|
||||||
var X_AXIS = {x: 1, y: 0, z: 0};
|
var X_AXIS = {x: 1, y: 0, z: 0};
|
||||||
var Y_AXIS = {x: 0, y: 1, z: 0};
|
var Y_AXIS = {x: 0, y: 1, z: 0};
|
||||||
var DEFAULT_DPI = 30;
|
var DEFAULT_DPI = 32;
|
||||||
var DEFAULT_WIDTH = 0.5;
|
var DEFAULT_WIDTH = 0.5;
|
||||||
|
|
||||||
var TABLET_URL = "https://s3.amazonaws.com/hifi-public/tony/tablet.fbx";
|
var TABLET_URL = "https://s3.amazonaws.com/hifi-public/tony/tablet.fbx";
|
||||||
|
|
|
@ -497,13 +497,17 @@ protected:
|
||||||
_postUpdateLambdas[key] = func;
|
_postUpdateLambdas[key] = func;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isHMDMode() const override {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//"/-17.2049,-8.08629,-19.4153/0,0.881994,0,-0.47126"
|
//"/-17.2049,-8.08629,-19.4153/0,0.881994,0,-0.47126"
|
||||||
static void setup() {
|
static void setup() {
|
||||||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||||
DependencyManager::registerInheritance<SpatialParentFinder, ParentFinder>();
|
DependencyManager::registerInheritance<SpatialParentFinder, ParentFinder>();
|
||||||
DependencyManager::set<AddressManager>();
|
DependencyManager::set<AddressManager>();
|
||||||
DependencyManager::set<NodeList>(NodeType::Agent, 0);
|
DependencyManager::set<NodeList>(NodeType::Agent);
|
||||||
DependencyManager::set<DeferredLightingEffect>();
|
DependencyManager::set<DeferredLightingEffect>();
|
||||||
DependencyManager::set<ResourceCacheSharedItems>();
|
DependencyManager::set<ResourceCacheSharedItems>();
|
||||||
DependencyManager::set<TextureCache>();
|
DependencyManager::set<TextureCache>();
|
||||||
|
|
|
@ -295,7 +295,7 @@ public:
|
||||||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||||
//DependencyManager::registerInheritance<SpatialParentFinder, ParentFinder>();
|
//DependencyManager::registerInheritance<SpatialParentFinder, ParentFinder>();
|
||||||
DependencyManager::set<AddressManager>();
|
DependencyManager::set<AddressManager>();
|
||||||
DependencyManager::set<NodeList>(NodeType::Agent, 0);
|
DependencyManager::set<NodeList>(NodeType::Agent);
|
||||||
DependencyManager::set<DeferredLightingEffect>();
|
DependencyManager::set<DeferredLightingEffect>();
|
||||||
DependencyManager::set<ResourceCacheSharedItems>();
|
DependencyManager::set<ResourceCacheSharedItems>();
|
||||||
DependencyManager::set<TextureCache>();
|
DependencyManager::set<TextureCache>();
|
||||||
|
|
Loading…
Reference in a new issue