Adding the reverse transformation

This commit is contained in:
samcake 2016-09-09 19:00:54 -07:00
commit 108910052a
46 changed files with 1820 additions and 208 deletions

View file

@ -368,11 +368,11 @@ void DomainServer::setupNodeListAndAssignments() {
const QString CUSTOM_LOCAL_PORT_OPTION = "metaverse.local_port";
QVariant localPortValue = _settingsManager.valueOrDefaultValueForKeyPath(CUSTOM_LOCAL_PORT_OPTION);
unsigned short domainServerPort = (unsigned short) localPortValue.toUInt();
int domainServerPort = localPortValue.toInt();
QVariantMap& settingsMap = _settingsManager.getSettingsMap();
unsigned short domainServerDTLSPort = 0;
int domainServerDTLSPort = INVALID_PORT;
if (_isUsingDTLS) {
domainServerDTLSPort = DEFAULT_DOMAIN_SERVER_DTLS_PORT;

View 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);
});
});
})();

View 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);
})();

View file

@ -1,6 +1,6 @@
import QtQuick 2.5
import QtQuick.Controls 1.2
import QtWebEngine 1.1
import QtWebEngine 1.2
import "controls-uit"
import "styles" as HifiStyles
@ -223,6 +223,9 @@ ScrollingWindow {
var newWindow = component.createObject(desktop);
request.openIn(newWindow.webView)
}
onWindowCloseRequested: {
root.destroy();
}
Component.onCompleted: {
desktop.initWebviewProfileHandlers(webview.profile)

View file

@ -15,7 +15,7 @@ WebEngineView {
id: root
property var newUrl;
profile.httpUserAgent: "Mozilla/5.0 Chrome/38.0 (HighFidelityInterface)"
profile: desktop.browserProfile
Component.onCompleted: {
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
}

View 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
}
}
]
}

View 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
}
}

View 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
}
}

View file

@ -1,63 +1,136 @@
import QtQuick 2.5
import QtWebEngine 1.1
import QtWebChannel 1.0
WebEngineView {
id: root
property var newUrl;
Item {
property alias url: root.url
property alias eventBridge: eventBridgeWrapper.eventBridge
property bool keyboardRaised: false
property bool punctuationMode: false
profile: desktop.browserProfile
Component.onCompleted: {
console.log("Connecting JS messaging to Hifi Logging")
// Ensure the JS from the web-engine makes it to our logging
root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message);
});
QtObject {
id: eventBridgeWrapper
WebChannel.id: "eventBridgeWrapper"
property var eventBridge;
}
// FIXME hack to get the URL with the auth token included. Remove when we move to Qt 5.6
Timer {
id: urlReplacementTimer
running: false
repeat: false
interval: 50
onTriggered: url = newUrl;
}
WebEngineView {
id: root
x: 0
y: 0
width: parent.width
height: keyboardRaised ? parent.height - keyboard1.height : parent.height
onUrlChanged: {
var originalUrl = url.toString();
newUrl = urlHandler.fixupUrl(originalUrl).toString();
if (newUrl !== originalUrl) {
root.stop();
if (urlReplacementTimer.running) {
console.warn("Replacement timer already running");
return;
}
urlReplacementTimer.start();
// creates a global EventBridge object.
WebEngineScript {
id: createGlobalEventBridge
sourceCode: eventBridgeJavaScriptToInject
injectionPoint: WebEngineScript.DocumentCreation
worldId: WebEngineScript.MainWorld
}
}
onFeaturePermissionRequested: {
grantFeaturePermission(securityOrigin, feature, true);
}
// detects when to raise and lower virtual keyboard
WebEngineScript {
id: raiseAndLowerKeyboard
injectionPoint: WebEngineScript.Deferred
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
worldId: WebEngineScript.MainWorld
}
onLoadingChanged: {
// Required to support clicking on "hifi://" links
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
var url = loadRequest.url.toString();
if (urlHandler.canHandleUrl(url)) {
if (urlHandler.handleUrl(url)) {
root.stop();
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
property string newUrl: ""
webChannel.registeredObjects: [eventBridgeWrapper]
Component.onCompleted: {
console.log("Connecting JS messaging to Hifi Logging");
// Ensure the JS from the web-engine makes it to our logging
root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
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
Timer {
id: urlReplacementTimer
running: false
repeat: false
interval: 50
onTriggered: url = root.newUrl;
}
onUrlChanged: {
var originalUrl = url.toString();
root.newUrl = urlHandler.fixupUrl(originalUrl).toString();
if (root.newUrl !== originalUrl) {
root.stop();
if (urlReplacementTimer.running) {
console.warn("Replacement timer already running");
return;
}
urlReplacementTimer.start();
}
}
onFeaturePermissionRequested: {
grantFeaturePermission(securityOrigin, feature, true);
}
onLoadingChanged: {
keyboardRaised = false;
punctuationMode = false;
keyboard1.resetShiftMode(false);
// Required to support clicking on "hifi://" links
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
var url = loadRequest.url.toString();
if (urlHandler.canHandleUrl(url)) {
if (urlHandler.handleUrl(url)) {
root.stop();
}
}
}
}
}
onNewViewRequested:{
if (desktop) {
var component = Qt.createComponent("../Browser.qml");
var newWindow = component.createObject(desktop);
request.openIn(newWindow.webView);
onNewViewRequested:{
// desktop is not defined for web-entities
if (desktop) {
var component = Qt.createComponent("../Browser.qml");
var newWindow = component.createObject(desktop);
request.openIn(newWindow.webView);
}
}
}
// 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
}
}

View file

@ -46,8 +46,20 @@ OriginalDesktop.Desktop {
}
}
property var toolbars: ({})
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: {
WebEngine.settings.javascriptCanOpenWindows = true;
@ -55,7 +67,6 @@ OriginalDesktop.Desktop {
WebEngine.settings.spatialNavigationEnabled = false;
WebEngine.settings.localContentCanAccessRemoteUrls = true;
var sysToolbar = desktop.getToolbar("com.highfidelity.interface.toolbar.system");
var toggleHudButton = sysToolbar.addButton({
objectName: "hudToggle",
imageURL: "../../../icons/hud.svg",

View file

@ -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
}
}

View file

@ -400,12 +400,10 @@ static const QString STATE_GROUNDED = "Grounded";
static const QString STATE_NAV_FOCUSED = "NavigationFocused";
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* portStr = getCmdOption(argc, constArgv, "--listenPort");
if (portStr) {
listenPort = atoi(portStr);
}
const int listenPort = portStr ? atoi(portStr) : INVALID_PORT;
// Set build version
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);

View file

@ -223,7 +223,7 @@ public:
// 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
// TODO: carry that information on the Camera as a setting
bool isHMDMode() const;
virtual bool isHMDMode() const override;
glm::mat4 getHMDSensorPose() const;
glm::mat4 getEyeOffset(int eye) const;
glm::mat4 getEyeProjection(int eye) const;

View file

@ -522,6 +522,11 @@ Menu::Menu() {
// Developer > 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()));
addCheckableActionToQMenuAndActionHash(networkMenu,
MenuOption::DisableActivityLogger,

View file

@ -129,6 +129,7 @@ namespace MenuOption {
const QString MuteEnvironment = "Mute Environment";
const QString MuteFaceTracking = "Mute Face Tracking";
const QString NamesAboveHeads = "Names Above Heads";
const QString Networking = "Networking...";
const QString NoFaceTracking = "None";
const QString OctreeStats = "Entity Statistics";
const QString OnePointCalibration = "1 Point Calibration";

View file

@ -332,4 +332,19 @@ void setupPreferences() {
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);
}
}
}

View file

@ -512,7 +512,7 @@ void OpenGLDisplayPlugin::compositeOverlay() {
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
} else {
batch.setViewportTransform(ivec4(uvec2(0), _currentFrame->framebuffer->getSize()));
batch.setViewportTransform(ivec4(uvec2(0), _compositeFramebuffer->getSize()));
batch.draw(gpu::TRIANGLE_STRIP, 4);
}
});
@ -536,7 +536,7 @@ void OpenGLDisplayPlugin::compositePointer() {
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
} else {
batch.setViewportTransform(ivec4(uvec2(0), _currentFrame->framebuffer->getSize()));
batch.setViewportTransform(ivec4(uvec2(0), _compositeFramebuffer->getSize()));
batch.draw(gpu::TRIANGLE_STRIP, 4);
}
});
@ -726,7 +726,7 @@ bool OpenGLDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
}
ivec4 OpenGLDisplayPlugin::eyeViewport(Eye eye) const {
uvec2 vpSize = _currentFrame->framebuffer->getSize();
uvec2 vpSize = _compositeFramebuffer->getSize();
vpSize.x /= 2;
uvec2 vpPos;
if (eye == Eye::Right) {

View file

@ -26,6 +26,7 @@
#include <gpu/Context.h>
#include "EntityTreeRenderer.h"
#include "EntitiesRendererLogging.h"
const float METERS_TO_INCHES = 39.3701f;
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 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 entity{ new RenderableWebEntityItem(entityID) };
entity->setProperties(properties);
@ -46,9 +76,26 @@ EntityItemPointer RenderableWebEntityItem::factory(const EntityItemID& entityID,
RenderableWebEntityItem::RenderableWebEntityItem(const EntityItemID& entityItemID) :
WebEntityItem(entityItemID) {
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() {
_webEntityAPIHelper->setRenderableWebEntityItem(nullptr);
_webEntityAPIHelper->deleteLater();
destroyWebSurface();
qDebug() << "Destroyed web entity " << getID();
}
@ -60,6 +107,20 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) {
}
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;
// Save the original GL context, because creating a QML surface will create a new context
QOpenGLContext * currentContext = QOpenGLContext::currentContext();
@ -67,10 +128,14 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) {
_webSurface = new OffscreenQmlSurface();
_webSurface->create(currentContext);
_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->getRootItem()->setProperty("eventBridge", QVariant::fromValue(_webEntityAPIHelper));
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
_webSurface->getRootContext()->setContextProperty("desktop", QVariant());
_webSurface->getRootContext()->setContextProperty("webEntity", _webEntityAPIHelper);
_connection = QObject::connect(_webSurface, &OffscreenQmlSurface::textureUpdated, [&](GLuint textureId) {
_texture = textureId;
});
@ -93,10 +158,14 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) {
point.setState(Qt::TouchPointReleased);
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi);
QPointF windowPoint(windowPos.x, windowPos.y);
point.setScenePos(windowPoint);
point.setPos(windowPoint);
QList<QTouchEvent::TouchPoint> touchPoints;
touchPoints.push_back(point);
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);
}
});
@ -210,7 +279,6 @@ void RenderableWebEntityItem::handlePointerEvent(const PointerEvent& event) {
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi);
QPointF windowPoint(windowPos.x, windowPos.y);
if (event.getType() == PointerEvent::Move) {
// Forward a mouse move event to webSurface
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);
QTouchEvent* touchEvent = new QTouchEvent(type);
touchEvent->setWindow(nullptr);
touchEvent->setDevice(nullptr);
touchEvent->setTarget(nullptr);
touchEvent->setWindow(_webSurface->getWindow());
touchEvent->setDevice(&_touchDevice);
touchEvent->setTarget(_webSurface->getRootItem());
touchEvent->setTouchPoints(touchPoints);
touchEvent->setTouchPointStates(touchPointState);
@ -303,3 +371,63 @@ bool RenderableWebEntityItem::isTransparent() {
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
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));
}

View file

@ -22,6 +22,27 @@ class OffscreenQmlSurface;
class QWindow;
class QObject;
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 {
public:
@ -42,10 +63,16 @@ public:
void update(const quint64& now) override;
bool needsToCallUpdate() const override { return _webSurface != nullptr; }
virtual void emitScriptEvent(const QVariant& message) override;
void setKeyboardRaised(bool raised);
SIMPLE_RENDERABLE();
virtual bool isTransparent() override;
public:
void synthesizeKeyPress(QString key);
private:
bool buildWebSurface(EntityTreeRenderer* renderer);
void destroyWebSurface();
@ -58,6 +85,8 @@ private:
bool _pressed{ false };
QTouchEvent _lastTouchEvent { QEvent::TouchUpdate };
uint64_t _lastRenderTime{ 0 };
QTouchDevice _touchDevice;
WebEntityAPIHelper* _webEntityAPIHelper;
QMetaObject::Connection _mousePressConnection;
QMetaObject::Connection _mouseReleaseConnection;
@ -65,5 +94,4 @@ private:
QMetaObject::Connection _hoverLeaveConnection;
};
#endif // hifi_RenderableWebEntityItem_h

View file

@ -456,6 +456,8 @@ public:
bool isFading() const { return _isFading; }
float getFadingRatio() const { return (isFading() ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f); }
virtual void emitScriptEvent(const QVariant& message) {}
protected:
void setSimulated(bool simulated) { _simulated = simulated; }

View file

@ -1289,6 +1289,17 @@ bool EntityScriptingInterface::wantsHandControllerPointerEvents(QUuid id) {
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) {
return std::abs(mass * (newVelocity - oldVelocity));
}
@ -1305,3 +1316,4 @@ float EntityScriptingInterface::getCostMultiplier() {
void EntityScriptingInterface::setCostMultiplier(float value) {
costMultiplier = value;
}

View file

@ -205,6 +205,8 @@ public slots:
Q_INVOKABLE bool wantsHandControllerPointerEvents(QUuid id);
Q_INVOKABLE void emitScriptEvent(const EntityItemID& entityID, const QVariant& message);
signals:
void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
@ -232,6 +234,8 @@ signals:
void clearingEntities();
void debitEnergySource(float value);
void webEventReceived(const EntityItemID& entityItemID, const QVariant& message);
private:
bool actionWorker(const QUuid& entityID, std::function<bool(EntitySimulationPointer, EntityItemPointer)> actor);
bool setVoxels(QUuid entityID, std::function<bool(PolyVoxEntityItem&)> actor);

View file

@ -479,6 +479,7 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
auto rootContext = getRootContext();
rootContext->setContextProperty("urlHandler", new UrlHandler());
rootContext->setContextProperty("resourceDirectoryUrl", QUrl::fromLocalFile(PathUtils::resourcesPath()));
}
void OffscreenQmlSurface::resize(const QSize& newSize_, bool forceResize) {

View file

@ -18,6 +18,7 @@
#include <QtCore/QDataStream>
#include <QtCore/QDebug>
#include <QtCore/QJsonDocument>
#include <QtCore/QThread>
#include <QtCore/QUrl>
#include <QtNetwork/QHostInfo>
@ -25,6 +26,7 @@
#include <LogHandler.h>
#include <NumericalConstants.h>
#include <SettingHandle.h>
#include <SharedUtil.h>
#include <UUID.h>
@ -34,12 +36,14 @@
#include "NetworkLogging.h"
#include "udt/Packet.h"
static Setting::Handle<quint16> LIMITED_NODELIST_LOCAL_PORT("LimitedNodeList.LocalPort", 0);
const std::set<NodeType_t> SOLO_NODE_TYPES = {
NodeType::AvatarMixer,
NodeType::AudioMixer
};
LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short dtlsListenPort) :
LimitedNodeList::LimitedNodeList(int socketListenPort, int dtlsListenPort) :
_sessionUUID(),
_nodeHash(),
_nodeMutex(QReadWriteLock::Recursive),
@ -62,11 +66,11 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short
}
qRegisterMetaType<ConnectionStep>("ConnectionStep");
_nodeSocket.bind(QHostAddress::AnyIPv4, socketListenPort);
auto port = (socketListenPort != INVALID_PORT) ? socketListenPort : LIMITED_NODELIST_LOCAL_PORT.get();
_nodeSocket.bind(QHostAddress::AnyIPv4, port);
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
_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() {
if (!_dtlsSocket) {
// 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;
}
}

View file

@ -45,6 +45,8 @@
#include "udt/Socket.h"
#include "UUIDHasher.h"
const int INVALID_PORT = -1;
const quint64 NODE_SILENCE_THRESHOLD_MSECS = 5 * 1000;
extern const std::set<NodeType_t> SOLO_NODE_TYPES;
@ -113,6 +115,8 @@ public:
bool getThisNodeCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); }
quint16 getSocketLocalPort() const { return _nodeSocket.localPort(); }
Q_INVOKABLE void setSocketLocalPort(quint16 socketLocalPort);
QUdpSocket& getDTLSSocket();
PacketReceiver& getPacketReceiver() { return *_packetReceiver; }
@ -250,6 +254,7 @@ signals:
void uuidChanged(const QUuid& ownerUUID, const QUuid& oldUUID);
void nodeAdded(SharedNodePointer);
void nodeSocketUpdated(SharedNodePointer);
void nodeKilled(SharedNodePointer);
void nodeActivated(SharedNodePointer);
@ -267,9 +272,9 @@ protected slots:
void errorTestingLocalSocket();
protected:
LimitedNodeList(unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0);
LimitedNodeList(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton
void operator=(LimitedNodeList const&); // Don't implement, needed to avoid copies of singleton
LimitedNodeList(int socketListenPort = INVALID_PORT, int dtlsListenPort = INVALID_PORT);
LimitedNodeList(LimitedNodeList const&) = delete; // 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,
const HifiSockAddr& overridenSockAddr);

View file

@ -63,6 +63,7 @@ void NetworkPeer::setPublicSocket(const HifiSockAddr& publicSocket) {
if (!wasOldSocketNull) {
qCDebug(networking) << "Public socket change for node" << *this;
emit socketUpdated();
}
}
}
@ -82,6 +83,7 @@ void NetworkPeer::setLocalSocket(const HifiSockAddr& localSocket) {
if (!wasOldSocketNull) {
qCDebug(networking) << "Local socket change for node" << *this;
emit socketUpdated();
}
}
}
@ -101,6 +103,7 @@ void NetworkPeer::setSymmetricSocket(const HifiSockAddr& symmetricSocket) {
if (!wasOldSocketNull) {
qCDebug(networking) << "Symmetric socket change for node" << *this;
emit socketUpdated();
}
}
}

View file

@ -81,9 +81,12 @@ public:
public slots:
void startPingTimer();
void stopPingTimer();
signals:
void pingTimerTimeout();
void socketActivated(const HifiSockAddr& sockAddr);
void socketUpdated();
protected:
void setActiveSocket(HifiSockAddr* discoveredSocket);

View file

@ -33,7 +33,7 @@
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),
_ownerType(newOwnerType),
_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
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
connect(this, &LimitedNodeList::nodeActivated, this, &NodeList::maybeSendIgnoreSetToNode);

View file

@ -116,10 +116,10 @@ private slots:
void maybeSendIgnoreSetToNode(SharedNodePointer node);
private:
NodeList() : LimitedNodeList(0, 0) { assert(false); } // Not implemented, needed for DependencyManager templates compile
NodeList(char ownerType, unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0);
NodeList(NodeList const&); // Don't implement, needed to avoid copies of singleton
void operator=(NodeList const&); // Don't implement, needed to avoid copies of singleton
NodeList() : LimitedNodeList(INVALID_PORT, INVALID_PORT) { assert(false); } // Not implemented, needed for DependencyManager templates compile
NodeList(char ownerType, int socketListenPort = INVALID_PORT, int dtlsListenPort = INVALID_PORT);
NodeList(NodeList const&) = delete; // 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 requestAuthForDomainServer();

View file

@ -63,10 +63,12 @@ void Socket::bind(const QHostAddress& address, quint16 port) {
}
void Socket::rebind() {
quint16 oldPort = _udpSocket.localPort();
rebind(_udpSocket.localPort());
}
void Socket::rebind(quint16 localPort) {
_udpSocket.close();
bind(QHostAddress::AnyIPv4, oldPort);
bind(QHostAddress::AnyIPv4, localPort);
}
void Socket::setSystemBufferSizes() {

View file

@ -61,8 +61,9 @@ public:
qint64 writeDatagram(const QByteArray& datagram, const HifiSockAddr& sockAddr);
void bind(const QHostAddress& address, quint16 port = 0);
void rebind(quint16 port);
void rebind();
void setPacketFilterOperator(PacketFilterOperator filterOperator) { _packetFilterOperator = filterOperator; }
void setPacketHandler(PacketHandler handler) { _packetHandler = handler; }
void setMessageHandler(MessageHandler handler) { _messageHandler = handler; }

View file

@ -48,6 +48,8 @@ public:
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
static AbstractViewStateInterface* instance();
static void setInstance(AbstractViewStateInterface* instance);

View file

@ -705,7 +705,7 @@ void RenderDeferredLocals::run(const render::SceneContextPointer& sceneContext,
static int frame = 0;
frame++;
if (frame % 1000 == 0) {
if (frame % 2000 == 0) {
lightClusters->updateFrustum(viewFrustum);
lightClusters->updateVisibleLights(lightIndices);

View 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@>

View 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

View file

@ -12,11 +12,25 @@
#include <gpu/Context.h>
#include <gpu/StandardShaderLib.h>
#include "lightClusters_drawGrid_vert.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 {
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"
@ -27,7 +41,7 @@ LightClusters::LightClusters() {
void LightClusters::updateFrustum(const ViewFrustum& frustum) {
_frustum = frustum;
_frustrumGridBuffer.edit().updateFrustrum(frustum);
_frustumGridBuffer.edit().updateFrustum(frustum);
}
void LightClusters::updateLightStage(const LightStagePointer& lightStage) {
@ -57,7 +71,7 @@ const gpu::PipelinePointer DebugLightClusters::getDrawClusterGridPipeline() {
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
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);
@ -75,26 +89,49 @@ const gpu::PipelinePointer DebugLightClusters::getDrawClusterGridPipeline() {
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 lightClusters = deferredLightingEffect->getLightClusters();
/* auto deferredTransform = inputs.get0();
auto deferredTransform = inputs.get0();
auto deferredFramebuffer = inputs.get1();
auto lightingModel = inputs.get2();
auto surfaceGeometryFramebuffer = inputs.get3();
auto ssaoFramebuffer = inputs.get4();
auto subsurfaceScatteringResource = inputs.get5();
*/
auto args = renderContext->args;
auto drawPipeline = getDrawClusterGridPipeline();
gpu::Batch batch;
// Assign the camera transform
batch.setViewportTransform(args->_viewport);
glm::mat4 projMat;
@ -108,15 +145,34 @@ void DebugLightClusters::run(const render::SceneContextPointer& sceneContext, co
// Then the actual ClusterGrid attributes
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) {
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);

View file

@ -20,65 +20,33 @@
class FrustumGrid {
public:
float _near { 0.1f };
float _nearPrime { 1.0f };
float _farPrime { 400.0f };
float rangeNear { 1.0f };
float rangeFar { 100.0f };
float _far { 10000.0f };
glm::uvec3 _dims { 16, 16, 16 };
glm::ivec3 dims { 8, 8, 8 };
float spare;
glm::mat4 _eyeToGridProj;
glm::mat4 _eyeToGridProjInv;
glm::mat4 eyeToGridProj;
glm::mat4 _worldToEyeMat;
glm::mat4 _eyeToWorldMat;
glm::mat4 eyeToWorldMat;
float viewToLinearDepth(float depth) const {
float nDepth = -depth;
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);
void updateFrustum(const ViewFrustum& frustum) {
eyeToGridProj = frustum.evalProjectionMatrixRange(rangeNear, rangeFar);
Transform view;
frustum.evalViewTransform(view);
_eyeToWorldMat = view.getMatrix();
eyeToWorldMat = view.getMatrix();
_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 {
@ -93,15 +61,13 @@ public:
void updateVisibleLights(const LightStage::LightIndices& visibleLights);
// FrustumGrid _grid;
ViewFrustum _frustum;
LightStagePointer _lightStage;
gpu::StructBuffer<FrustumGrid> _frustrumGridBuffer;
gpu::StructBuffer<FrustumGrid> _frustumGridBuffer;
gpu::BufferPointer _lightIndicesBuffer;
@ -131,22 +97,30 @@ protected:
int numDrawn { 0 };
};
#include "DeferredFrameTransform.h"
#include "DeferredFramebuffer.h"
#include "LightingModel.h"
#include "SurfaceGeometryPass.h"
class DebugLightClusters {
public:
// using Inputs = render::VaryingSet6 < DeferredFrameTransformPointer, DeferredFramebufferPointer, LightingModelPointer, SurfaceGeometryFramebufferPointer, AmbientOcclusionFramebufferPointer, SubsurfaceScatteringResourcePointer>;
using Inputs = render::VaryingSet4 < DeferredFrameTransformPointer, DeferredFramebufferPointer, LightingModelPointer, SurfaceGeometryFramebufferPointer>;
using Config = DebugLightClustersConfig;
using JobModel = render::Job::Model<DebugLightClusters, Config>;
using JobModel = render::Job::ModelI<DebugLightClusters, Inputs, Config>;
DebugLightClusters();
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:
gpu::BufferPointer _gridBuffer;
gpu::PipelinePointer _drawClusterGrid;
gpu::PipelinePointer _drawClusterFromDepth;
const gpu::PipelinePointer getDrawClusterGridPipeline();
const gpu::PipelinePointer getDrawClusterFromDepthPipeline();
};
#endif

View file

@ -192,7 +192,8 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) {
// 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

View file

@ -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);
}

View file

@ -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);
}

View file

@ -13,39 +13,15 @@
//
<@include gpu/Transform.slh@>
<$declareStandardTransform()$>
<@include LightClusterGrid.slh@>
<@include gpu/Color.slh@>
<$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;
@ -77,35 +53,22 @@ void main(void) {
);
vec4 pos = UNIT_BOX[UNIT_BOX_LINE_INDICES[gl_VertexID]];
// pos.z -= 10.0f;
vec3 cpos = vec3(1, 0, 1);
ivec3 dims = frustumGrid.dims.xyz;
// float z = frustrumGrid.nearFarRange.y * (1 - pos.z) + frustrumGrid.nearFarRange.z * (pos.z);
vec3 gridScale = vec3(0.5, 0.5, 1.0) / vec3(frustrumGrid.dims.xyz);
ivec3 summedDims = ivec3(dims.x * dims.y, dims.x, 1);
vec3 cvpos = cpos + pos.xyz;
vec3 clusterStart = vec3(-1.0 + cvpos.x * gridScale.x, -1.0 + cvpos.y * gridScale.y, cvpos.z * gridScale.z);
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);
int layer = gl_InstanceID / summedDims.x;
int offsetInLayer = gl_InstanceID % summedDims.x;
ivec3 clusterPos = ivec3(offsetInLayer % summedDims.y, offsetInLayer / summedDims.y, layer);
/*
int cellIsEmpty = sign(inClusterLocation.w);
ivec4 cellLocation = ivec4(inClusterLocation.xyz, (inClusterLocation.w < 0 ? -inClusterLocation.w : inClusterLocation.w));
vec4 cellBound = evalBound(cellLocation);
vec3 eyePos = frustumGrid_clusterPosToEye(clusterPos, vec3(0.05) + 0.9 * pos.xyz);
vec4 worldPos = frustumGrid_eyeToWorld(vec4(eyePos.xyz, 1.0));
pos.xyz = cellBound.xyz + vec3(cellBound.w) * pos.xyz;
*/
// standard transform
TransformCamera cam = getTransformCamera();
<$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);
}

View file

@ -189,6 +189,38 @@ protected:
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> {
Q_OBJECT
Q_PROPERTY(QString value READ getValue WRITE setValue NOTIFY valueChanged)
@ -222,6 +254,15 @@ public:
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 {
Q_OBJECT
Q_PROPERTY(QString placeholderText READ getPlaceholderText CONSTANT)

View file

@ -2271,6 +2271,7 @@ function MyController(hand) {
};
} else {
pointerEvent = this.touchingEnterPointerEvent;
pointerEvent.type = "Release";
pointerEvent.button = "Primary";
pointerEvent.isPrimaryHeld = false;
}

View file

@ -11,7 +11,7 @@
var RAD_TO_DEG = 180 / Math.PI;
var X_AXIS = {x: 1, y: 0, 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 TABLET_URL = "https://s3.amazonaws.com/hifi-public/tony/tablet.fbx";

View file

@ -497,13 +497,17 @@ protected:
_postUpdateLambdas[key] = func;
}
bool isHMDMode() const override {
return false;
}
public:
//"/-17.2049,-8.08629,-19.4153/0,0.881994,0,-0.47126"
static void setup() {
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
DependencyManager::registerInheritance<SpatialParentFinder, ParentFinder>();
DependencyManager::set<AddressManager>();
DependencyManager::set<NodeList>(NodeType::Agent, 0);
DependencyManager::set<NodeList>(NodeType::Agent);
DependencyManager::set<DeferredLightingEffect>();
DependencyManager::set<ResourceCacheSharedItems>();
DependencyManager::set<TextureCache>();

View file

@ -295,7 +295,7 @@ public:
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
//DependencyManager::registerInheritance<SpatialParentFinder, ParentFinder>();
DependencyManager::set<AddressManager>();
DependencyManager::set<NodeList>(NodeType::Agent, 0);
DependencyManager::set<NodeList>(NodeType::Agent);
DependencyManager::set<DeferredLightingEffect>();
DependencyManager::set<ResourceCacheSharedItems>();
DependencyManager::set<TextureCache>();