mirror of
https://github.com/overte-org/overte.git
synced 2025-08-04 22:07:03 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into decouple-avatar-updates
This commit is contained in:
commit
828509e8eb
55 changed files with 1710 additions and 452 deletions
|
@ -1,6 +1,6 @@
|
|||
set(TARGET_NAME assignment-client)
|
||||
|
||||
setup_hifi_project(Core Gui Network Script Widgets)
|
||||
setup_hifi_project(Core Gui Network Script Widgets WebSockets)
|
||||
|
||||
add_dependency_external_projects(glm)
|
||||
find_package(GLM REQUIRED)
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <SoundCache.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include <WebSocketServerClass.h>
|
||||
#include <EntityScriptingInterface.h> // TODO: consider moving to scriptengine.h
|
||||
|
||||
#include "avatars/ScriptableAvatar.h"
|
||||
|
@ -180,10 +181,17 @@ void Agent::run() {
|
|||
// register ourselves to the script engine
|
||||
_scriptEngine.registerGlobalObject("Agent", this);
|
||||
|
||||
if (!_payload.isEmpty()) {
|
||||
_scriptEngine.setParentURL(_payload);
|
||||
}
|
||||
|
||||
_scriptEngine.init(); // must be done before we set up the viewers
|
||||
|
||||
_scriptEngine.registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
|
||||
QScriptValue webSocketServerConstructorValue = _scriptEngine.newFunction(WebSocketServerClass::constructor);
|
||||
_scriptEngine.globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue);
|
||||
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
|
||||
_scriptEngine.registerGlobalObject("EntityViewer", &_entityViewer);
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
var toolIconUrl = HIFI_PUBLIC_BUCKET + "images/tools/";
|
||||
|
||||
progressDialog = (function () {
|
||||
var that = {},
|
||||
|
@ -142,4 +144,4 @@ progressDialog = (function () {
|
|||
that.cleanup = cleanup;
|
||||
|
||||
return that;
|
||||
}());
|
||||
}());
|
||||
|
|
|
@ -11,17 +11,14 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var test = function(name, func) {
|
||||
test = function(name, func, timeout) {
|
||||
print("Running test: " + name);
|
||||
|
||||
var unitTest = new UnitTest(name, func);
|
||||
|
||||
try {
|
||||
unitTest.run();
|
||||
var unitTest = new UnitTest(name, func, timeout);
|
||||
unitTest.run(function(unitTest) {
|
||||
print(" Success: " + unitTest.numAssertions + " assertions passed");
|
||||
} catch (error) {
|
||||
}, function(unitTest, error) {
|
||||
print(" Failure: " + error.name + " " + error.message);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
AssertionException = function(expected, actual, message) {
|
||||
|
@ -36,13 +33,86 @@ UnthrownException = function(message) {
|
|||
this.name = 'UnthrownException';
|
||||
};
|
||||
|
||||
UnitTest = function(name, func) {
|
||||
this.numAssertions = 0;
|
||||
this.func = func;
|
||||
TimeoutException = function() {
|
||||
print("Creating exception");
|
||||
this.message = "UnitTest timed out\n";
|
||||
this.name = 'TimeoutException';
|
||||
};
|
||||
|
||||
UnitTest.prototype.run = function() {
|
||||
this.func();
|
||||
SequentialUnitTester = function() {
|
||||
this.tests = [];
|
||||
this.testIndex = -1;
|
||||
};
|
||||
|
||||
SequentialUnitTester.prototype.addTest = function(name, func, timeout) {
|
||||
var _this = this;
|
||||
this.tests.push(function() {
|
||||
print("Running test: " + name);
|
||||
var unitTest = new UnitTest(name, func, timeout);
|
||||
unitTest.run(function(unitTest) {
|
||||
print(" Success: " + unitTest.numAssertions + " assertions passed");
|
||||
_this._nextTest();
|
||||
}, function(unitTest, error) {
|
||||
print(" Failure: " + error.name + " " + error.message);
|
||||
_this._nextTest();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
SequentialUnitTester.prototype._nextTest = function() {
|
||||
this.testIndex++;
|
||||
if (this.testIndex < this.tests.length) {
|
||||
this.tests[this.testIndex]();
|
||||
return;
|
||||
}
|
||||
print("Completed all UnitTests");
|
||||
};
|
||||
|
||||
SequentialUnitTester.prototype.run = function() {
|
||||
this._nextTest();
|
||||
};
|
||||
|
||||
UnitTest = function(name, func, timeout) {
|
||||
this.numAssertions = 0;
|
||||
this.func = func;
|
||||
this.timeout = timeout;
|
||||
};
|
||||
|
||||
UnitTest.prototype.run = function(successCallback, failureCallback) {
|
||||
var _this = this;
|
||||
this.successCallback = successCallback;
|
||||
this.failureCallback = failureCallback;
|
||||
if (this.timeout !== undefined) {
|
||||
this.timeoutTimer = Script.setTimeout(function() {
|
||||
_this.failureCallback(this, new TimeoutException());
|
||||
}, this.timeout);
|
||||
}
|
||||
try {
|
||||
this.func();
|
||||
if (this.timeout === undefined) {
|
||||
successCallback(this);
|
||||
}
|
||||
} catch (exception) {
|
||||
this.handleException(exception);
|
||||
}
|
||||
};
|
||||
|
||||
UnitTest.prototype.registerCallbackFunction = function(func) {
|
||||
var _this = this;
|
||||
return function(one, two, three, four, five, six) {
|
||||
try {
|
||||
func(one, two, three, four, five, six);
|
||||
} catch (exception) {
|
||||
_this.handleException(exception);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
UnitTest.prototype.handleException = function(exception) {
|
||||
if (this.timeout !== undefined) {
|
||||
Script.clearTimeout(this.timeoutTimer);
|
||||
}
|
||||
this.failureCallback(this, exception);
|
||||
};
|
||||
|
||||
UnitTest.prototype.assertNotEquals = function(expected, actual, message) {
|
||||
|
@ -83,7 +153,7 @@ UnitTest.prototype.assertNull = function(value, message) {
|
|||
UnitTest.prototype.arrayEqual = function(array1, array2, message) {
|
||||
this.numAssertions++;
|
||||
if (array1.length !== array2.length) {
|
||||
throw new AssertionException(array1.length , array2.length , message);
|
||||
throw new AssertionException(array1.length, array2.length , message);
|
||||
}
|
||||
for (var i = 0; i < array1.length; ++i) {
|
||||
if (array1[i] !== array2[i]) {
|
||||
|
@ -101,4 +171,11 @@ UnitTest.prototype.raises = function(func, message) {
|
|||
}
|
||||
|
||||
throw new UnthrownException(message);
|
||||
}
|
||||
}
|
||||
|
||||
UnitTest.prototype.done = function() {
|
||||
if (this.timeout !== undefined) {
|
||||
Script.clearTimeout(this.timeoutTimer);
|
||||
this.successCallback(this);
|
||||
}
|
||||
}
|
||||
|
|
102
examples/utilities/diagnostics/testWebSocket.js
Normal file
102
examples/utilities/diagnostics/testWebSocket.js
Normal file
|
@ -0,0 +1,102 @@
|
|||
//
|
||||
// testWebSocket.js
|
||||
// examples
|
||||
//
|
||||
// Created by Thijs Wenker on 8/18/15
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// WebSocket and WebSocketServer Tests
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
Script.include("../../libraries/unitTest.js");
|
||||
|
||||
// We set the unit testing timeout to 1000 milliseconds by default. Please increase if the test fails due to a slow connection.
|
||||
const UNITTEST_TIMEOUT = 1000;
|
||||
const WEBSOCKET_PING_URL = "ws://echo.websocket.org";
|
||||
// Please do not register the following domain + gTLD:
|
||||
const WEBSOCKET_INVALID_URL = "ws://thisisnotavaliddomainname.invalid";
|
||||
const TEST_MESSAGE = "This is a test message.";
|
||||
|
||||
var unitTests = new SequentialUnitTester();
|
||||
|
||||
unitTests.addTest("Test default WebSocket values", function(finished) {
|
||||
var _this = this;
|
||||
var webSocket = new WebSocket(WEBSOCKET_PING_URL);
|
||||
|
||||
webSocket.onmessage = this.registerCallbackFunction(function(event) {
|
||||
_this.assertEquals(TEST_MESSAGE, event.data, "event.data should be '" + TEST_MESSAGE + "'");
|
||||
webSocket.close();
|
||||
});
|
||||
webSocket.onopen = this.registerCallbackFunction(function(event) {
|
||||
_this.assertEquals(webSocket.OPEN, webSocket.readyState, "readyState should be OPEN");
|
||||
webSocket.send(TEST_MESSAGE);
|
||||
});
|
||||
webSocket.onclose = this.registerCallbackFunction(function(event) {
|
||||
_this.assertEquals(webSocket.CLOSED, webSocket.readyState, "readyState should be CLOSED");
|
||||
_this.done();
|
||||
});
|
||||
this.assertEquals(webSocket.CONNECTING, webSocket.readyState, "readyState should be CONNECTING");
|
||||
this.assertEquals("blob", webSocket.binaryType, "binaryType should be 'blob'");
|
||||
this.assertEquals(0, webSocket.bufferedAmount, "bufferedAmount should be 0");
|
||||
this.assertEquals("", webSocket.extensions, "extensions should be an empty string by default");
|
||||
this.assertEquals("", webSocket.protocol, "protocol should be an empty string by default");
|
||||
this.assertEquals(WEBSOCKET_PING_URL, webSocket.url, "url should be '" + WEBSOCKET_PING_URL + "'");
|
||||
}, UNITTEST_TIMEOUT);
|
||||
|
||||
unitTests.addTest("Test WebSocket invalid URL", function(finished) {
|
||||
var _this = this;
|
||||
var webSocket = new WebSocket(WEBSOCKET_INVALID_URL);
|
||||
var hadError = false;
|
||||
webSocket.onerror = this.registerCallbackFunction(function() {
|
||||
hadError = true;
|
||||
_this.done();
|
||||
});
|
||||
webSocket.onclose = this.registerCallbackFunction(function(event) {
|
||||
_this.assertEquals(webSocket.CLOSED, webSocket.readyState, "readyState should be CLOSED");
|
||||
});
|
||||
this.assertEquals(webSocket.CONNECTING, webSocket.readyState, "readyState should be CONNECTING");
|
||||
this.assertEquals(WEBSOCKET_INVALID_URL, webSocket.url, "url should be '" + WEBSOCKET_INVALID_URL + "'");
|
||||
}, UNITTEST_TIMEOUT);
|
||||
|
||||
if (this.WebSocketServer === undefined) {
|
||||
print("Skipping WebSocketServer tests.");
|
||||
} else {
|
||||
unitTests.addTest("Test WebSocketServer with three clients", function(finished) {
|
||||
var _this = this;
|
||||
const NUMBER_OF_CLIENTS = 3;
|
||||
var connectedClients = 0;
|
||||
var respondedClients = 0;
|
||||
var webSocketServer = new WebSocketServer();
|
||||
_this.assertEquals(true, webSocketServer.listening, "listening should be true");
|
||||
webSocketServer.newConnection.connect(this.registerCallbackFunction(function(newClient) {
|
||||
connectedClients++;
|
||||
newClient.onmessage = _this.registerCallbackFunction(function(event) {
|
||||
var data = JSON.parse(event.data);
|
||||
_this.assertEquals(TEST_MESSAGE, data.message, "data.message should be '" + TEST_MESSAGE + "'");
|
||||
respondedClients++;
|
||||
if (respondedClients === NUMBER_OF_CLIENTS) {
|
||||
webSocketServer.close();
|
||||
_this.assertEquals(false, webSocketServer.listening, "listening should be false");
|
||||
_this.done();
|
||||
}
|
||||
});
|
||||
newClient.send(JSON.stringify({message: TEST_MESSAGE, client: connectedClients}));
|
||||
}));
|
||||
var newSocket1 = new WebSocket(webSocketServer.url);
|
||||
newSocket1.onmessage = this.registerCallbackFunction(function(event) {
|
||||
newSocket1.send(event.data);
|
||||
});
|
||||
var newSocket2 = new WebSocket(webSocketServer.url);
|
||||
newSocket2.onmessage = this.registerCallbackFunction(function(event) {
|
||||
newSocket2.send(event.data);
|
||||
});
|
||||
var newSocket3 = new WebSocket(webSocketServer.url);
|
||||
newSocket3.onmessage = this.registerCallbackFunction(function(event) {
|
||||
newSocket3.send(event.data);
|
||||
});
|
||||
}, UNITTEST_TIMEOUT);
|
||||
}
|
||||
|
||||
unitTests.run();
|
|
@ -40,7 +40,7 @@ else ()
|
|||
list(REMOVE_ITEM INTERFACE_SRCS ${SPEECHRECOGNIZER_CPP})
|
||||
endif ()
|
||||
|
||||
find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Qml Quick Script Svg WebKitWidgets)
|
||||
find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Qml Quick Script Svg WebKitWidgets WebSockets)
|
||||
|
||||
# grab the ui files in resources/ui
|
||||
file (GLOB_RECURSE QT_UI_FILES ui/*.ui)
|
||||
|
|
300
interface/interface_da.ts
Normal file
300
interface/interface_da.ts
Normal file
|
@ -0,0 +1,300 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="da">
|
||||
<context>
|
||||
<name>Application</name>
|
||||
<message>
|
||||
<location filename="src/Application.cpp" line="1482"/>
|
||||
<source>Sparse Voxel Octree Files (*.svo)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Application.cpp" line="3465"/>
|
||||
<source>Open Script</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Application.cpp" line="3466"/>
|
||||
<source>JavaScript Files (*.js)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ChatWindow</name>
|
||||
<message>
|
||||
<location filename="ui/chatWindow.ui" line="20"/>
|
||||
<location filename="../build/interface/ui_chatWindow.h" line="143"/>
|
||||
<source>Chat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/chatWindow.ui" line="50"/>
|
||||
<location filename="../build/interface/ui_chatWindow.h" line="144"/>
|
||||
<source>Connecting to XMPP...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/chatWindow.ui" line="71"/>
|
||||
<location filename="../build/interface/ui_chatWindow.h" line="145"/>
|
||||
<source> online now:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="src/ui/ChatWindow.cpp" line="128"/>
|
||||
<source>day</source>
|
||||
<translation>
|
||||
<numerusform>%n dag</numerusform>
|
||||
<numerusform>%n dage</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="src/ui/ChatWindow.cpp" line="128"/>
|
||||
<source>hour</source>
|
||||
<translation>
|
||||
<numerusform>%n time</numerusform>
|
||||
<numerusform>%n timer</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="src/ui/ChatWindow.cpp" line="128"/>
|
||||
<source>minute</source>
|
||||
<translation>
|
||||
<numerusform>%n minut</numerusform>
|
||||
<numerusform>%n minutter</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<source>second</source>
|
||||
<translation type="vanished">
|
||||
<numerusform>%n second</numerusform>
|
||||
<numerusform>%n seconds</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/ChatWindow.cpp" line="183"/>
|
||||
<source>%1 online now:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Dialog</name>
|
||||
<message>
|
||||
<location filename="ui/updateDialog.ui" line="20"/>
|
||||
<location filename="ui/updateDialog.ui" line="73"/>
|
||||
<location filename="../build/interface/ui_updateDialog.h" line="137"/>
|
||||
<location filename="../build/interface/ui_updateDialog.h" line="138"/>
|
||||
<source>Update Required</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/updateDialog.ui" line="129"/>
|
||||
<location filename="../build/interface/ui_updateDialog.h" line="140"/>
|
||||
<source>Download</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/updateDialog.ui" line="151"/>
|
||||
<location filename="../build/interface/ui_updateDialog.h" line="141"/>
|
||||
<source>Skip Version</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/updateDialog.ui" line="173"/>
|
||||
<location filename="../build/interface/ui_updateDialog.h" line="142"/>
|
||||
<source>Close</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Menu</name>
|
||||
<message>
|
||||
<location filename="src/Menu.cpp" line="554"/>
|
||||
<source>Open .ini config file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Menu.cpp" line="556"/>
|
||||
<location filename="src/Menu.cpp" line="568"/>
|
||||
<source>Text files (*.ini)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Menu.cpp" line="566"/>
|
||||
<source>Save .ini config file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PreferencesDialog</name>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="90"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="618"/>
|
||||
<source>Cancel</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="125"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="619"/>
|
||||
<source>Save all changes</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="196"/>
|
||||
<location filename="ui/preferencesDialog.ui" line="573"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="620"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="629"/>
|
||||
<source>Avatar</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="230"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="621"/>
|
||||
<source><html><head/><body><p>Avatar display name <span style=" color:#909090;">(optional)</span></p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="266"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="622"/>
|
||||
<source>Not showing a name</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="294"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="623"/>
|
||||
<source>Head</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="395"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="625"/>
|
||||
<source>Body</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="506"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="627"/>
|
||||
<source>Advanced Tuning</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="537"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="628"/>
|
||||
<source>It's not recomended that you play with these settings unless you've looked into exactly what they do.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="605"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="630"/>
|
||||
<source>Vertical field of view</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="708"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="631"/>
|
||||
<source>Lean scale (applies to Faceshift users)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="793"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="632"/>
|
||||
<source>Avatar scale <span style=" color:#909090;">(default is 1.0)</span></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="875"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="633"/>
|
||||
<source>Pupil dillation</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="954"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="634"/>
|
||||
<source>Audio Jitter Buffer Samples (0 for automatic)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="1045"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="635"/>
|
||||
<source>Faceshift eye detection</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="1125"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="636"/>
|
||||
<source>Octree</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/preferencesDialog.ui" line="1236"/>
|
||||
<location filename="../build/interface/ui_preferencesDialog.h" line="638"/>
|
||||
<source>Max packets sent each second</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QObject</name>
|
||||
<message>
|
||||
<location filename="src/ui/VoxelImportDialog.cpp" line="24"/>
|
||||
<source>Loading ...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/VoxelImportDialog.cpp" line="27"/>
|
||||
<source>Cancel</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>RunningScriptsWidget</name>
|
||||
<message>
|
||||
<location filename="ui/runningScriptsWidget.ui" line="14"/>
|
||||
<location filename="../build/interface/ui_runningScriptsWidget.h" line="140"/>
|
||||
<source>Form</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/runningScriptsWidget.ui" line="39"/>
|
||||
<location filename="../build/interface/ui_runningScriptsWidget.h" line="141"/>
|
||||
<source><html><head/><body><p><span style=" font-size:18pt;">Running Scripts</span></p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/runningScriptsWidget.ui" line="63"/>
|
||||
<location filename="../build/interface/ui_runningScriptsWidget.h" line="142"/>
|
||||
<source><html><head/><body><p><span style=" font-weight:600;">Currently running</span></p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/runningScriptsWidget.ui" line="89"/>
|
||||
<location filename="../build/interface/ui_runningScriptsWidget.h" line="143"/>
|
||||
<source>Reload all</source>
|
||||
<oldsource>Reload All</oldsource>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/runningScriptsWidget.ui" line="116"/>
|
||||
<location filename="../build/interface/ui_runningScriptsWidget.h" line="144"/>
|
||||
<source>Stop all</source>
|
||||
<oldsource>Stop All</oldsource>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/runningScriptsWidget.ui" line="137"/>
|
||||
<location filename="../build/interface/ui_runningScriptsWidget.h" line="145"/>
|
||||
<source><html><head/><body><p><span style=" font-weight:600;">Recently loaded</span></p></body></html></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/runningScriptsWidget.ui" line="154"/>
|
||||
<location filename="../build/interface/ui_runningScriptsWidget.h" line="146"/>
|
||||
<source>(click a script or use the 1-9 keys to load and run it)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="ui/runningScriptsWidget.ui" line="202"/>
|
||||
<location filename="../build/interface/ui_runningScriptsWidget.h" line="148"/>
|
||||
<source>There are no scripts currently running.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
|
@ -58,6 +58,8 @@
|
|||
#include <AutoUpdater.h>
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <plugins/PluginContainer.h>
|
||||
#include <plugins/PluginManager.h>
|
||||
#include <display-plugins/DisplayPlugin.h>
|
||||
|
||||
#include <EntityScriptingInterface.h>
|
||||
|
@ -148,6 +150,8 @@
|
|||
|
||||
#include "ui/overlays/Cube3DOverlay.h"
|
||||
|
||||
#include "PluginContainerProxy.h"
|
||||
|
||||
// ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
||||
// FIXME seems to be broken.
|
||||
#if defined(Q_OS_WIN)
|
||||
|
@ -301,7 +305,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
// continuing to overburden Application.cpp
|
||||
Cube3DOverlay* _keyboardFocusHighlight{ nullptr };
|
||||
int _keyboardFocusHighlightID{ -1 };
|
||||
|
||||
PluginContainer* _pluginContainer;
|
||||
|
||||
Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||
QApplication(argc, argv),
|
||||
|
@ -351,7 +355,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
_applicationOverlay()
|
||||
{
|
||||
setInstance(this);
|
||||
Plugin::setContainer(this);
|
||||
|
||||
_pluginContainer = new PluginContainerProxy();
|
||||
Plugin::setContainer(_pluginContainer);
|
||||
#ifdef Q_OS_WIN
|
||||
installNativeEventFilter(&MyNativeEventFilter::getInstance());
|
||||
#endif
|
||||
|
@ -1263,6 +1269,7 @@ void Application::resizeGL() {
|
|||
// Possible change in aspect ratio
|
||||
loadViewFrustum(_myCamera, _viewFrustum);
|
||||
float fov = glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES);
|
||||
// FIXME the aspect ratio for stereo displays is incorrect based on this.
|
||||
float aspectRatio = aspect(_renderResolution);
|
||||
_myCamera.setProjection(glm::perspective(fov, aspectRatio, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP));
|
||||
}
|
||||
|
@ -1422,7 +1429,15 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
break;
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
Menu::getInstance()->triggerOption(MenuOption::AddressBar);
|
||||
if (isOption) {
|
||||
if (_window->isFullScreen()) {
|
||||
_pluginContainer->unsetFullscreen();
|
||||
} else {
|
||||
_pluginContainer->setFullscreen(nullptr);
|
||||
}
|
||||
} else {
|
||||
Menu::getInstance()->triggerOption(MenuOption::AddressBar);
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_B:
|
||||
|
@ -3327,7 +3342,7 @@ namespace render {
|
|||
template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape(); }
|
||||
template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff) { return Item::Bound(); }
|
||||
template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) {
|
||||
if (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
|
||||
if (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) {
|
||||
PerformanceTimer perfTimer("worldBox");
|
||||
|
||||
auto& batch = *args->_batch;
|
||||
|
@ -4625,10 +4640,6 @@ void Application::checkSkeleton() {
|
|||
}
|
||||
}
|
||||
|
||||
bool Application::isForeground() {
|
||||
return _isForeground && !getWindow()->isMinimized();
|
||||
}
|
||||
|
||||
void Application::activeChanged(Qt::ApplicationState state) {
|
||||
switch (state) {
|
||||
case Qt::ApplicationActive:
|
||||
|
@ -4734,6 +4745,10 @@ const DisplayPlugin * Application::getActiveDisplayPlugin() const {
|
|||
return ((Application*)this)->getActiveDisplayPlugin();
|
||||
}
|
||||
|
||||
bool _activatingDisplayPlugin{ false };
|
||||
QVector<QPair<QString, QString>> _currentDisplayPluginActions;
|
||||
QVector<QPair<QString, QString>> _currentInputPluginActions;
|
||||
|
||||
|
||||
static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool active = false) {
|
||||
auto menu = Menu::getInstance();
|
||||
|
@ -4755,9 +4770,6 @@ static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool acti
|
|||
Q_ASSERT(menu->menuItemExists(MenuOption::OutputMenu, name));
|
||||
}
|
||||
|
||||
static QVector<QPair<QString, QString>> _currentDisplayPluginActions;
|
||||
static bool _activatingDisplayPlugin{false};
|
||||
|
||||
void Application::updateDisplayMode() {
|
||||
auto menu = Menu::getInstance();
|
||||
auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins();
|
||||
|
@ -4824,7 +4836,7 @@ void Application::updateDisplayMode() {
|
|||
// Only show the hmd tools after the correct plugin has
|
||||
// been activated so that it's UI is setup correctly
|
||||
if (newPluginWantsHMDTools) {
|
||||
showDisplayPluginsTools();
|
||||
_pluginContainer->showDisplayPluginsTools();
|
||||
}
|
||||
|
||||
if (oldDisplayPlugin) {
|
||||
|
@ -4842,9 +4854,6 @@ void Application::updateDisplayMode() {
|
|||
Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin");
|
||||
}
|
||||
|
||||
static QVector<QPair<QString, QString>> _currentInputPluginActions;
|
||||
|
||||
|
||||
static void addInputPluginToMenu(InputPluginPointer inputPlugin, bool active = false) {
|
||||
auto menu = Menu::getInstance();
|
||||
QString name = inputPlugin->getName();
|
||||
|
@ -4918,42 +4927,6 @@ void Application::updateInputModes() {
|
|||
//}
|
||||
}
|
||||
|
||||
void Application::addMenu(const QString& menuName) {
|
||||
Menu::getInstance()->addMenu(menuName);
|
||||
}
|
||||
|
||||
void Application::removeMenu(const QString& menuName) {
|
||||
Menu::getInstance()->removeMenu(menuName);
|
||||
}
|
||||
|
||||
void Application::addMenuItem(const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable, bool checked, const QString& groupName) {
|
||||
auto menu = Menu::getInstance();
|
||||
MenuWrapper* parentItem = menu->getMenu(path);
|
||||
QAction* action = menu->addActionToQMenuAndActionHash(parentItem, name);
|
||||
connect(action, &QAction::triggered, [=] {
|
||||
onClicked(action->isChecked());
|
||||
});
|
||||
action->setCheckable(checkable);
|
||||
action->setChecked(checked);
|
||||
if (_activatingDisplayPlugin) {
|
||||
_currentDisplayPluginActions.push_back({ path, name });
|
||||
} else {
|
||||
_currentInputPluginActions.push_back({ path, name });
|
||||
}
|
||||
}
|
||||
|
||||
void Application::removeMenuItem(const QString& menuName, const QString& menuItem) {
|
||||
Menu::getInstance()->removeMenuItem(menuName, menuItem);
|
||||
}
|
||||
|
||||
bool Application::isOptionChecked(const QString& name) {
|
||||
return Menu::getInstance()->isOptionChecked(name);
|
||||
}
|
||||
|
||||
void Application::setIsOptionChecked(const QString& path, bool checked) {
|
||||
Menu::getInstance()->setIsOptionChecked(path, checked);
|
||||
}
|
||||
|
||||
mat4 Application::getEyeProjection(int eye) const {
|
||||
if (isHMDMode()) {
|
||||
return getActiveDisplayPlugin()->getProjection((Eye)eye, _viewFrustum.getProjection());
|
||||
|
@ -4986,89 +4959,6 @@ mat4 Application::getHMDSensorPose() const {
|
|||
return mat4();
|
||||
}
|
||||
|
||||
// FIXME there is a bug in the fullscreen setting, where leaving
|
||||
// fullscreen does not restore the window frame, making it difficult
|
||||
// or impossible to move or size the window.
|
||||
// Additionally, setting fullscreen isn't hiding the menu on windows
|
||||
// make it useless for stereoscopic modes.
|
||||
void Application::setFullscreen(const QScreen* target) {
|
||||
if (!_window->isFullScreen()) {
|
||||
_savedGeometry = _window->geometry();
|
||||
}
|
||||
#ifdef Q_OS_MAC
|
||||
_window->setGeometry(target->availableGeometry());
|
||||
#endif
|
||||
_window->windowHandle()->setScreen((QScreen*)target);
|
||||
_window->showFullScreen();
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
// also hide the QMainWindow's menuBar
|
||||
QMenuBar* menuBar = _window->menuBar();
|
||||
if (menuBar) {
|
||||
menuBar->setVisible(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Application::unsetFullscreen(const QScreen* avoid) {
|
||||
_window->showNormal();
|
||||
|
||||
QRect targetGeometry = _savedGeometry;
|
||||
if (avoid != nullptr) {
|
||||
QRect avoidGeometry = avoid->geometry();
|
||||
if (avoidGeometry.contains(targetGeometry.topLeft())) {
|
||||
QScreen* newTarget = primaryScreen();
|
||||
if (newTarget == avoid) {
|
||||
foreach(auto screen, screens()) {
|
||||
if (screen != avoid) {
|
||||
newTarget = screen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
targetGeometry = newTarget->availableGeometry();
|
||||
}
|
||||
}
|
||||
#ifdef Q_OS_MAC
|
||||
QTimer* timer = new QTimer();
|
||||
timer->singleShot(2000, [=] {
|
||||
_window->setGeometry(targetGeometry);
|
||||
timer->deleteLater();
|
||||
});
|
||||
#else
|
||||
_window->setGeometry(targetGeometry);
|
||||
#endif
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
// also show the QMainWindow's menuBar
|
||||
QMenuBar* menuBar = _window->menuBar();
|
||||
if (menuBar) {
|
||||
menuBar->setVisible(true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Application::showDisplayPluginsTools() {
|
||||
DependencyManager::get<DialogsManager>()->hmdTools(true);
|
||||
}
|
||||
|
||||
QGLWidget* Application::getPrimarySurface() {
|
||||
return _glWidget;
|
||||
}
|
||||
|
||||
void Application::setActiveDisplayPlugin(const QString& pluginName) {
|
||||
auto menu = Menu::getInstance();
|
||||
foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) {
|
||||
QString name = displayPlugin->getName();
|
||||
QAction* action = menu->getActionForOption(name);
|
||||
if (pluginName == name) {
|
||||
action->setChecked(true);
|
||||
}
|
||||
}
|
||||
updateDisplayMode();
|
||||
}
|
||||
|
||||
void Application::setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float deltaTime, int index) {
|
||||
PalmData* palm;
|
||||
bool foundHand = false;
|
||||
|
|
|
@ -26,18 +26,18 @@
|
|||
#include <EntityEditPacketSender.h>
|
||||
#include <EntityTreeRenderer.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <input-plugins/KeyboardMouseDevice.h>
|
||||
#include <NodeList.h>
|
||||
#include <OctreeQuery.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <PhysicalEntitySimulation.h>
|
||||
#include <PhysicsEngine.h>
|
||||
#include <plugins/Forward.h>
|
||||
#include <ScriptEngine.h>
|
||||
#include <ShapeManager.h>
|
||||
#include <StDev.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
#include <ViewFrustum.h>
|
||||
#include <plugins/PluginContainer.h>
|
||||
#include <plugins/PluginManager.h>
|
||||
#include <SimpleMovingAverage.h>
|
||||
|
||||
#include "AudioClient.h"
|
||||
|
@ -51,7 +51,6 @@
|
|||
#include "avatar/AvatarUpdate.h"
|
||||
#include "avatar/Avatar.h"
|
||||
#include "avatar/MyAvatar.h"
|
||||
#include <input-plugins/KeyboardMouseDevice.h>
|
||||
#include "scripting/ControllerScriptingInterface.h"
|
||||
#include "scripting/DialogsManagerScriptingInterface.h"
|
||||
#include "scripting/WebWindowClass.h"
|
||||
|
@ -133,7 +132,7 @@ class Application;
|
|||
|
||||
typedef bool (Application::* AcceptURLMethod)(const QString &);
|
||||
|
||||
class Application : public QApplication, public AbstractViewStateInterface, public AbstractScriptingServicesInterface, PluginContainer {
|
||||
class Application : public QApplication, public AbstractViewStateInterface, public AbstractScriptingServicesInterface {
|
||||
Q_OBJECT
|
||||
|
||||
friend class OctreePacketProcessor;
|
||||
|
@ -281,23 +280,10 @@ public:
|
|||
virtual void endOverrideEnvironmentData() { _environment.endOverride(); }
|
||||
virtual qreal getDevicePixelRatio();
|
||||
|
||||
// Plugin container support
|
||||
virtual void addMenu(const QString& menuName);
|
||||
virtual void removeMenu(const QString& menuName);
|
||||
virtual void addMenuItem(const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable, bool checked, const QString& groupName);
|
||||
virtual void removeMenuItem(const QString& menuName, const QString& menuItem);
|
||||
virtual bool isOptionChecked(const QString& name);
|
||||
virtual void setIsOptionChecked(const QString& path, bool checked);
|
||||
virtual void setFullscreen(const QScreen* target) override;
|
||||
virtual void unsetFullscreen(const QScreen* avoid) override;
|
||||
virtual void showDisplayPluginsTools() override;
|
||||
virtual QGLWidget* getPrimarySurface() override;
|
||||
virtual bool isForeground() override;
|
||||
|
||||
void setActiveDisplayPlugin(const QString& pluginName);
|
||||
|
||||
DisplayPlugin * getActiveDisplayPlugin();
|
||||
const DisplayPlugin * getActiveDisplayPlugin() const;
|
||||
DisplayPlugin* getActiveDisplayPlugin();
|
||||
const DisplayPlugin* getActiveDisplayPlugin() const;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -701,6 +687,8 @@ private:
|
|||
int _simsPerSecondReport = 0;
|
||||
quint64 _lastSimsPerSecondUpdate = 0;
|
||||
bool _isForeground = true; // starts out assumed to be in foreground
|
||||
|
||||
friend class PluginContainerProxy;
|
||||
};
|
||||
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -16,11 +16,13 @@
|
|||
#include <AddressManager.h>
|
||||
#include <AudioClient.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <display-plugins/DisplayPlugin.h>
|
||||
#include <PathUtils.h>
|
||||
#include <SettingHandle.h>
|
||||
#include <UserActivityLogger.h>
|
||||
#include <VrMenu.h>
|
||||
|
||||
|
||||
#include "Application.h"
|
||||
#include "AccountManager.h"
|
||||
#include "audio/AudioScope.h"
|
||||
|
@ -220,7 +222,7 @@ Menu::Menu() {
|
|||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0,
|
||||
qApp, SLOT(packageModel()));
|
||||
|
||||
MenuWrapper* displayMenu = addMenu("Display");
|
||||
MenuWrapper* displayMenu = addMenu(DisplayPlugin::MENU_PATH);
|
||||
{
|
||||
MenuWrapper* displayModeMenu = addMenu(MenuOption::OutputMenu);
|
||||
QActionGroup* displayModeGroup = new QActionGroup(displayModeMenu);
|
||||
|
@ -294,6 +296,8 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::StandingHMDSensorMode, 0, false,
|
||||
avatar, SLOT(updateStandingHMDModeFromMenu()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::WorldAxes);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats);
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::Log,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_L,
|
||||
|
|
|
@ -294,6 +294,7 @@ namespace MenuOption {
|
|||
const QString VisibleToEveryone = "Everyone";
|
||||
const QString VisibleToFriends = "Friends";
|
||||
const QString VisibleToNoOne = "No one";
|
||||
const QString WorldAxes = "World Axes";
|
||||
}
|
||||
|
||||
#endif // hifi_Menu_h
|
||||
|
|
161
interface/src/PluginContainerProxy.cpp
Normal file
161
interface/src/PluginContainerProxy.cpp
Normal file
|
@ -0,0 +1,161 @@
|
|||
#include "PluginContainerProxy.h"
|
||||
|
||||
#include <QScreen>
|
||||
#include <QWindow>
|
||||
|
||||
#include <plugins/Plugin.h>
|
||||
#include <plugins/PluginManager.h>
|
||||
#include <display-plugins/DisplayPlugin.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "MainWindow.h"
|
||||
#include "GLCanvas.h"
|
||||
#include "ui/DialogsManager.h"
|
||||
|
||||
PluginContainerProxy::PluginContainerProxy() {
|
||||
Plugin::setContainer(this);
|
||||
}
|
||||
|
||||
bool PluginContainerProxy::isForeground() {
|
||||
return qApp->_isForeground && !qApp->getWindow()->isMinimized();
|
||||
}
|
||||
|
||||
void PluginContainerProxy::addMenu(const QString& menuName) {
|
||||
Menu::getInstance()->addMenu(menuName);
|
||||
}
|
||||
|
||||
void PluginContainerProxy::removeMenu(const QString& menuName) {
|
||||
Menu::getInstance()->removeMenu(menuName);
|
||||
}
|
||||
|
||||
extern bool _activatingDisplayPlugin;
|
||||
extern QVector<QPair<QString, QString>> _currentDisplayPluginActions;
|
||||
extern QVector<QPair<QString, QString>> _currentInputPluginActions;
|
||||
std::map<QString, QActionGroup*> _exclusiveGroups;
|
||||
|
||||
QAction* PluginContainerProxy::addMenuItem(const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable, bool checked, const QString& groupName) {
|
||||
auto menu = Menu::getInstance();
|
||||
MenuWrapper* parentItem = menu->getMenu(path);
|
||||
QAction* action = menu->addActionToQMenuAndActionHash(parentItem, name);
|
||||
if (!groupName.isEmpty()) {
|
||||
QActionGroup* group{ nullptr };
|
||||
if (!_exclusiveGroups.count(groupName)) {
|
||||
group = _exclusiveGroups[groupName] = new QActionGroup(menu);
|
||||
group->setExclusive(true);
|
||||
} else {
|
||||
group = _exclusiveGroups[groupName];
|
||||
}
|
||||
group->addAction(action);
|
||||
}
|
||||
connect(action, &QAction::triggered, [=] {
|
||||
onClicked(action->isChecked());
|
||||
});
|
||||
action->setCheckable(checkable);
|
||||
action->setChecked(checked);
|
||||
if (_activatingDisplayPlugin) {
|
||||
_currentDisplayPluginActions.push_back({ path, name });
|
||||
} else {
|
||||
_currentInputPluginActions.push_back({ path, name });
|
||||
}
|
||||
return action;
|
||||
}
|
||||
|
||||
void PluginContainerProxy::removeMenuItem(const QString& menuName, const QString& menuItem) {
|
||||
Menu::getInstance()->removeMenuItem(menuName, menuItem);
|
||||
}
|
||||
|
||||
bool PluginContainerProxy::isOptionChecked(const QString& name) {
|
||||
return Menu::getInstance()->isOptionChecked(name);
|
||||
}
|
||||
|
||||
void PluginContainerProxy::setIsOptionChecked(const QString& path, bool checked) {
|
||||
Menu::getInstance()->setIsOptionChecked(path, checked);
|
||||
}
|
||||
|
||||
// FIXME there is a bug in the fullscreen setting, where leaving
|
||||
// fullscreen does not restore the window frame, making it difficult
|
||||
// or impossible to move or size the window.
|
||||
// Additionally, setting fullscreen isn't hiding the menu on windows
|
||||
// make it useless for stereoscopic modes.
|
||||
void PluginContainerProxy::setFullscreen(const QScreen* target, bool hideMenu) {
|
||||
auto _window = qApp->_window;
|
||||
if (!_window->isFullScreen()) {
|
||||
_savedGeometry = _window->geometry();
|
||||
}
|
||||
if (nullptr == target) {
|
||||
// FIXME target the screen where the window currently is
|
||||
target = qApp->primaryScreen();
|
||||
}
|
||||
|
||||
_window->setGeometry(target->availableGeometry());
|
||||
_window->windowHandle()->setScreen((QScreen*)target);
|
||||
_window->showFullScreen();
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
// also hide the QMainWindow's menuBar
|
||||
QMenuBar* menuBar = _window->menuBar();
|
||||
if (menuBar && hideMenu) {
|
||||
menuBar->setVisible(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void PluginContainerProxy::unsetFullscreen(const QScreen* avoid) {
|
||||
auto _window = qApp->_window;
|
||||
_window->showNormal();
|
||||
|
||||
QRect targetGeometry = _savedGeometry;
|
||||
if (avoid != nullptr) {
|
||||
QRect avoidGeometry = avoid->geometry();
|
||||
if (avoidGeometry.contains(targetGeometry.topLeft())) {
|
||||
QScreen* newTarget = qApp->primaryScreen();
|
||||
if (newTarget == avoid) {
|
||||
foreach(auto screen, qApp->screens()) {
|
||||
if (screen != avoid) {
|
||||
newTarget = screen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
targetGeometry = newTarget->availableGeometry();
|
||||
}
|
||||
}
|
||||
#ifdef Q_OS_MAC
|
||||
QTimer* timer = new QTimer();
|
||||
timer->singleShot(2000, [=] {
|
||||
_window->setGeometry(targetGeometry);
|
||||
timer->deleteLater();
|
||||
});
|
||||
#else
|
||||
_window->setGeometry(targetGeometry);
|
||||
#endif
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
// also show the QMainWindow's menuBar
|
||||
QMenuBar* menuBar = _window->menuBar();
|
||||
if (menuBar) {
|
||||
menuBar->setVisible(true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void PluginContainerProxy::showDisplayPluginsTools() {
|
||||
DependencyManager::get<DialogsManager>()->hmdTools(true);
|
||||
}
|
||||
|
||||
QGLWidget* PluginContainerProxy::getPrimarySurface() {
|
||||
return qApp->_glWidget;
|
||||
}
|
||||
|
||||
void Application::setActiveDisplayPlugin(const QString& pluginName) {
|
||||
auto menu = Menu::getInstance();
|
||||
foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) {
|
||||
QString name = displayPlugin->getName();
|
||||
QAction* action = menu->getActionForOption(name);
|
||||
if (pluginName == name) {
|
||||
action->setChecked(true);
|
||||
}
|
||||
}
|
||||
updateDisplayMode();
|
||||
}
|
30
interface/src/PluginContainerProxy.h
Normal file
30
interface/src/PluginContainerProxy.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
#ifndef hifi_PluginContainerProxy_h
|
||||
#define hifi_PluginContainerProxy_h
|
||||
|
||||
#include <QObject>
|
||||
#include <QRect>
|
||||
|
||||
#include <plugins/Forward.h>
|
||||
#include <plugins/PluginContainer.h>
|
||||
|
||||
class PluginContainerProxy : public QObject, PluginContainer {
|
||||
Q_OBJECT
|
||||
PluginContainerProxy();
|
||||
virtual void addMenu(const QString& menuName) override;
|
||||
virtual void removeMenu(const QString& menuName) override;
|
||||
virtual QAction* addMenuItem(const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") override;
|
||||
virtual void removeMenuItem(const QString& menuName, const QString& menuItem) override;
|
||||
virtual bool isOptionChecked(const QString& name) override;
|
||||
virtual void setIsOptionChecked(const QString& path, bool checked);
|
||||
virtual void setFullscreen(const QScreen* targetScreen, bool hideMenu = true) override;
|
||||
virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) override;
|
||||
virtual void showDisplayPluginsTools() override;
|
||||
virtual QGLWidget* getPrimarySurface() override;
|
||||
virtual bool isForeground() override;
|
||||
QRect _savedGeometry{ 10, 120, 800, 600 };
|
||||
|
||||
friend class Application;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -35,43 +35,87 @@ using namespace std;
|
|||
void renderWorldBox(gpu::Batch& batch) {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
// Show edge of world
|
||||
static const glm::vec3 red(1.0f, 0.0f, 0.0f);
|
||||
static const glm::vec3 green(0.0f, 1.0f, 0.0f);
|
||||
static const glm::vec3 blue(0.0f, 0.0f, 1.0f);
|
||||
static const glm::vec3 grey(0.5f, 0.5f, 0.5f);
|
||||
// Show center of world
|
||||
static const glm::vec3 RED(1.0f, 0.0f, 0.0f);
|
||||
static const glm::vec3 GREEN(0.0f, 1.0f, 0.0f);
|
||||
static const glm::vec3 BLUE(0.0f, 0.0f, 1.0f);
|
||||
static const glm::vec3 GREY(0.5f, 0.5f, 0.5f);
|
||||
static const glm::vec4 GREY4(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
|
||||
static const glm::vec4 DASHED_RED(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
static const glm::vec4 DASHED_GREEN(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
static const glm::vec4 DASHED_BLUE(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
static const float DASH_LENGTH = 1.0f;
|
||||
static const float GAP_LENGTH = 1.0f;
|
||||
auto transform = Transform{};
|
||||
|
||||
batch.setModelTransform(transform);
|
||||
|
||||
// TODO - consider alternate rendering for negative build-able space in the domain
|
||||
geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(HALF_TREE_SCALE, 0.0f, 0.0f), red);
|
||||
geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, HALF_TREE_SCALE, 0.0f), green);
|
||||
geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, HALF_TREE_SCALE), blue);
|
||||
geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, HALF_TREE_SCALE), glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), grey);
|
||||
geometryCache->renderLine(batch, glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), glm::vec3(HALF_TREE_SCALE, 0.0f, 0.0f), grey);
|
||||
|
||||
geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(HALF_TREE_SCALE, 0.0f, 0.0f), RED);
|
||||
geometryCache->renderDashedLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(-HALF_TREE_SCALE, 0.0f, 0.0f), DASHED_RED,
|
||||
DASH_LENGTH, GAP_LENGTH);
|
||||
|
||||
geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, HALF_TREE_SCALE, 0.0f), GREEN);
|
||||
geometryCache->renderDashedLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, -HALF_TREE_SCALE, 0.0f), DASHED_GREEN,
|
||||
DASH_LENGTH, GAP_LENGTH);
|
||||
|
||||
geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, HALF_TREE_SCALE), BLUE);
|
||||
geometryCache->renderDashedLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, -HALF_TREE_SCALE), DASHED_BLUE,
|
||||
DASH_LENGTH, GAP_LENGTH);
|
||||
|
||||
// X center boundaries
|
||||
geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f),
|
||||
glm::vec3(HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f), GREY);
|
||||
geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f),
|
||||
glm::vec3(-HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), GREY);
|
||||
geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f),
|
||||
glm::vec3(HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), GREY);
|
||||
geometryCache->renderLine(batch, glm::vec3(HALF_TREE_SCALE, -HALF_TREE_SCALE, 0.0f),
|
||||
glm::vec3(HALF_TREE_SCALE, HALF_TREE_SCALE, 0.0f), GREY);
|
||||
|
||||
// Z center boundaries
|
||||
geometryCache->renderLine(batch, glm::vec3(0.0f, -HALF_TREE_SCALE, -HALF_TREE_SCALE),
|
||||
glm::vec3(0.0f, -HALF_TREE_SCALE, HALF_TREE_SCALE), GREY);
|
||||
geometryCache->renderLine(batch, glm::vec3(0.0f, -HALF_TREE_SCALE, -HALF_TREE_SCALE),
|
||||
glm::vec3(0.0f, HALF_TREE_SCALE, -HALF_TREE_SCALE), GREY);
|
||||
geometryCache->renderLine(batch, glm::vec3(0.0f, HALF_TREE_SCALE, -HALF_TREE_SCALE),
|
||||
glm::vec3(0.0f, HALF_TREE_SCALE, HALF_TREE_SCALE), GREY);
|
||||
geometryCache->renderLine(batch, glm::vec3(0.0f, -HALF_TREE_SCALE, HALF_TREE_SCALE),
|
||||
glm::vec3(0.0f, HALF_TREE_SCALE, HALF_TREE_SCALE), GREY);
|
||||
|
||||
// Center boundaries
|
||||
geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE),
|
||||
glm::vec3(-HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY);
|
||||
geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE),
|
||||
glm::vec3(HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE), GREY);
|
||||
geometryCache->renderLine(batch, glm::vec3(HALF_TREE_SCALE, 0.0f, -HALF_TREE_SCALE),
|
||||
glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY);
|
||||
geometryCache->renderLine(batch, glm::vec3(-HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE),
|
||||
glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY);
|
||||
|
||||
geometryCache->renderWireCube(batch, TREE_SCALE, GREY4);
|
||||
|
||||
// Draw meter markers along the 3 axis to help with measuring things
|
||||
const float MARKER_DISTANCE = 1.0f;
|
||||
const float MARKER_RADIUS = 0.05f;
|
||||
|
||||
geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, red);
|
||||
geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, RED);
|
||||
|
||||
transform.setTranslation(glm::vec3(MARKER_DISTANCE, 0.0f, 0.0f));
|
||||
batch.setModelTransform(transform);
|
||||
geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, red);
|
||||
geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, RED);
|
||||
|
||||
transform.setTranslation(glm::vec3(0.0f, MARKER_DISTANCE, 0.0f));
|
||||
batch.setModelTransform(transform);
|
||||
geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, green);
|
||||
geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, GREEN);
|
||||
|
||||
transform.setTranslation(glm::vec3(0.0f, 0.0f, MARKER_DISTANCE));
|
||||
batch.setModelTransform(transform);
|
||||
geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, blue);
|
||||
geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, BLUE);
|
||||
|
||||
transform.setTranslation(glm::vec3(MARKER_DISTANCE, 0.0f, MARKER_DISTANCE));
|
||||
batch.setModelTransform(transform);
|
||||
geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, grey);
|
||||
geometryCache->renderSphere(batch, MARKER_RADIUS, 10, 10, GREY);
|
||||
}
|
||||
|
||||
// Return a random vector of average length 1
|
||||
|
|
|
@ -469,8 +469,8 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
* (1.0f - ((float)(now - getHead()->getLookingAtMeStarted()))
|
||||
/ (LOOKING_AT_ME_DURATION * (float)USECS_PER_SECOND));
|
||||
if (alpha > 0.0f) {
|
||||
QSharedPointer<NetworkGeometry> geometry = getHead()->getFaceModel().getGeometry();
|
||||
if (geometry) {
|
||||
QSharedPointer<NetworkGeometry> geometry = _skeletonModel.getGeometry();
|
||||
if (geometry && geometry->isLoaded()) {
|
||||
const float DEFAULT_EYE_DIAMETER = 0.048f; // Typical human eye
|
||||
const float RADIUS_INCREMENT = 0.005f;
|
||||
Transform transform;
|
||||
|
@ -597,13 +597,12 @@ void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, floa
|
|||
if (_shouldRenderBillboard || !(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) {
|
||||
// render the billboard until both models are loaded
|
||||
renderBillboard(renderArgs);
|
||||
return;
|
||||
} else {
|
||||
getHead()->render(renderArgs, 1.0f, renderFrustum);
|
||||
}
|
||||
|
||||
getHand()->render(renderArgs, false);
|
||||
}
|
||||
|
||||
getHead()->render(renderArgs, 1.0f, renderFrustum);
|
||||
getHead()->renderLookAts(renderArgs);
|
||||
}
|
||||
|
||||
|
@ -748,11 +747,12 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, floa
|
|||
qDebug() << "ASSERT because isinf(scale)";
|
||||
}
|
||||
qDebug() << "textPosition =" << textPosition;
|
||||
qDebug() << "projMat =" << projMat;
|
||||
qDebug() << "viewMat =" << viewMat;
|
||||
qDebug() << "viewProj =" << viewProj;
|
||||
qDebug() << "windowSizeY =" << windowSizeY;
|
||||
qDebug() << "p1.y =" << p1.y;
|
||||
qDebug() << "p1.w =" << p1.w;
|
||||
qDebug() << "p0.y =" << p0.y;
|
||||
qDebug() << "p0.w =" << p0.w;
|
||||
qDebug() << "p1 =" << p1;
|
||||
qDebug() << "p0 =" << p0;
|
||||
qDebug() << "qApp->getDevicePixelRatio() =" << qApp->getDevicePixelRatio();
|
||||
qDebug() << "fontSize =" << fontSize;
|
||||
qDebug() << "pixelHeight =" << pixelHeight;
|
||||
|
|
|
@ -1141,7 +1141,7 @@ void MyAvatar::attach(const QString& modelURL, const QString& jointName, const g
|
|||
|
||||
void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel) {
|
||||
|
||||
if (!(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) {
|
||||
if (!_skeletonModel.isRenderable()) {
|
||||
return; // wait until all models are loaded
|
||||
}
|
||||
|
||||
|
|
|
@ -185,7 +185,8 @@ void ConnexionClient::destroy() {
|
|||
ConnexionData& connexiondata = ConnexionData::getInstance();
|
||||
int deviceid = connexiondata.getDeviceID();
|
||||
connexiondata.setDeviceID(0);
|
||||
Application::getUserInputMapper()->removeDevice(deviceid);
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
userInputMapper->removeDevice(deviceid);
|
||||
}
|
||||
|
||||
#define LOGITECH_VENDOR_ID 0x46d
|
||||
|
@ -289,14 +290,15 @@ unsigned short HidToVirtualKey(unsigned long pid, unsigned short hidKeyCode) {
|
|||
|
||||
bool ConnexionClient::RawInputEventFilter(void* msg, long* result) {
|
||||
ConnexionData& connexiondata = ConnexionData::getInstance();
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) {
|
||||
connexiondata.registerToUserInputMapper(*Application::getUserInputMapper());
|
||||
connexiondata.assignDefaultInputMapping(*Application::getUserInputMapper());
|
||||
connexiondata.registerToUserInputMapper(*userInputMapper);
|
||||
connexiondata.assignDefaultInputMapping(*userInputMapper);
|
||||
UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion");
|
||||
} else if (!Is3dmouseAttached() && connexiondata.getDeviceID() != 0) {
|
||||
int deviceid = connexiondata.getDeviceID();
|
||||
connexiondata.setDeviceID(0);
|
||||
Application::getUserInputMapper()->removeDevice(deviceid);
|
||||
userInputMapper->removeDevice(deviceid);
|
||||
}
|
||||
|
||||
if (!Is3dmouseAttached()) {
|
||||
|
|
|
@ -39,35 +39,43 @@ QSharedPointer<Resource> AnimationCache::createResource(const QUrl& url, const Q
|
|||
return QSharedPointer<Resource>(new Animation(url), &Resource::allReferencesCleared);
|
||||
}
|
||||
|
||||
Animation::Animation(const QUrl& url) : Resource(url) {}
|
||||
|
||||
class AnimationReader : public QRunnable {
|
||||
public:
|
||||
|
||||
AnimationReader(const QWeakPointer<Resource>& animation, QNetworkReply* reply);
|
||||
|
||||
virtual void run();
|
||||
|
||||
private:
|
||||
|
||||
QWeakPointer<Resource> _animation;
|
||||
QNetworkReply* _reply;
|
||||
};
|
||||
|
||||
AnimationReader::AnimationReader(const QWeakPointer<Resource>& animation, QNetworkReply* reply) :
|
||||
_animation(animation),
|
||||
AnimationReader::AnimationReader(const QUrl& url, QNetworkReply* reply) :
|
||||
_url(url),
|
||||
_reply(reply) {
|
||||
}
|
||||
|
||||
void AnimationReader::run() {
|
||||
QSharedPointer<Resource> animation = _animation.toStrongRef();
|
||||
if (!animation.isNull()) {
|
||||
QMetaObject::invokeMethod(animation.data(), "setGeometry",
|
||||
Q_ARG(FBXGeometry*, readFBX(_reply->readAll(), QVariantHash(), _reply->property("url").toString())));
|
||||
try {
|
||||
if (!_reply) {
|
||||
throw QString("Reply is NULL ?!");
|
||||
}
|
||||
QString urlname = _url.path().toLower();
|
||||
bool urlValid = true;
|
||||
urlValid &= !urlname.isEmpty();
|
||||
urlValid &= !_url.path().isEmpty();
|
||||
|
||||
if (urlValid) {
|
||||
// Parse the FBX directly from the QNetworkReply
|
||||
FBXGeometry* fbxgeo = nullptr;
|
||||
if (_url.path().toLower().endsWith(".fbx")) {
|
||||
fbxgeo = readFBX(_reply, QVariantHash(), _url.path());
|
||||
} else {
|
||||
QString errorStr("usupported format");
|
||||
emit onError(299, errorStr);
|
||||
}
|
||||
emit onSuccess(fbxgeo);
|
||||
} else {
|
||||
throw QString("url is invalid");
|
||||
}
|
||||
|
||||
} catch (const QString& error) {
|
||||
emit onError(299, error);
|
||||
}
|
||||
_reply->deleteLater();
|
||||
}
|
||||
|
||||
Animation::Animation(const QUrl& url) : Resource(url) {}
|
||||
|
||||
bool Animation::isLoaded() const {
|
||||
return _loaded && _geometry;
|
||||
}
|
||||
|
@ -100,17 +108,27 @@ const QVector<FBXAnimationFrame>& Animation::getFramesReference() const {
|
|||
return _geometry->animationFrames;
|
||||
}
|
||||
|
||||
void Animation::setGeometry(FBXGeometry* geometry) {
|
||||
void Animation::downloadFinished(QNetworkReply* reply) {
|
||||
// parse the animation/fbx file on a background thread.
|
||||
AnimationReader* animationReader = new AnimationReader(reply->url(), reply);
|
||||
connect(animationReader, SIGNAL(onSuccess(FBXGeometry*)), SLOT(animationParseSuccess(FBXGeometry*)));
|
||||
connect(animationReader, SIGNAL(onError(int, QString)), SLOT(animationParseError(int, QString)));
|
||||
QThreadPool::globalInstance()->start(animationReader);
|
||||
}
|
||||
|
||||
void Animation::animationParseSuccess(FBXGeometry* geometry) {
|
||||
|
||||
qCDebug(animation) << "Animation parse success" << _url.toDisplayString();
|
||||
|
||||
_geometry.reset(geometry);
|
||||
finishedLoading(true);
|
||||
}
|
||||
|
||||
void Animation::downloadFinished(QNetworkReply* reply) {
|
||||
// send the reader off to the thread pool
|
||||
QThreadPool::globalInstance()->start(new AnimationReader(_self, reply));
|
||||
void Animation::animationParseError(int error, QString str) {
|
||||
qCCritical(animation) << "Animation failure parsing " << _url.toDisplayString() << "code =" << error << str;
|
||||
emit failed(QNetworkReply::UnknownContentError);
|
||||
}
|
||||
|
||||
|
||||
AnimationDetails::AnimationDetails() :
|
||||
role(), url(), fps(0.0f), priority(0.0f), loop(false), hold(false),
|
||||
startAutomatically(false), firstFrame(0.0f), lastFrame(0.0f), running(false), frameIndex(0.0f)
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#ifndef hifi_AnimationCache_h
|
||||
#define hifi_AnimationCache_h
|
||||
|
||||
#include <QRunnable>
|
||||
#include <QScriptEngine>
|
||||
#include <QScriptValue>
|
||||
|
||||
|
@ -64,16 +65,33 @@ public:
|
|||
const QVector<FBXAnimationFrame>& getFramesReference() const;
|
||||
|
||||
protected:
|
||||
|
||||
Q_INVOKABLE void setGeometry(FBXGeometry* geometry);
|
||||
|
||||
virtual void downloadFinished(QNetworkReply* reply);
|
||||
|
||||
protected slots:
|
||||
void animationParseSuccess(FBXGeometry* geometry);
|
||||
void animationParseError(int error, QString str);
|
||||
|
||||
private:
|
||||
|
||||
std::unique_ptr<FBXGeometry> _geometry;
|
||||
};
|
||||
|
||||
/// Reads geometry in a worker thread.
|
||||
class AnimationReader : public QObject, public QRunnable {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AnimationReader(const QUrl& url, QNetworkReply* reply);
|
||||
virtual void run();
|
||||
|
||||
signals:
|
||||
void onSuccess(FBXGeometry* geometry);
|
||||
void onError(int error, QString str);
|
||||
|
||||
private:
|
||||
QUrl _url;
|
||||
QNetworkReply* _reply;
|
||||
};
|
||||
|
||||
class AnimationDetails {
|
||||
public:
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
void AnimationHandle::setURL(const QUrl& url) {
|
||||
if (_url != url) {
|
||||
_animation = DependencyManager::get<AnimationCache>()->getAnimation(_url = url);
|
||||
_animation->ensureLoading();
|
||||
QObject::connect(_animation.data(), &Resource::onRefresh, this, &AnimationHandle::clearJoints);
|
||||
_jointMappings.clear();
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
const QString Basic2DWindowOpenGLDisplayPlugin::NAME("2D Display");
|
||||
|
||||
static const QString MENU_PATH = "Display";
|
||||
static const QString FULLSCREEN = "Fullscreen";
|
||||
|
||||
const QString& Basic2DWindowOpenGLDisplayPlugin::getName() const {
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include "oculus/OculusDisplayPlugin.h"
|
||||
#include "oculus/OculusLegacyDisplayPlugin.h"
|
||||
|
||||
const QString DisplayPlugin::MENU_PATH{ "Display" };
|
||||
|
||||
// TODO migrate to a DLL model where plugins are discovered and loaded at runtime by the PluginManager class
|
||||
DisplayPluginList getDisplayPlugins() {
|
||||
DisplayPlugin* PLUGIN_POOL[] = {
|
||||
|
@ -27,9 +29,11 @@ DisplayPluginList getDisplayPlugins() {
|
|||
#endif
|
||||
|
||||
// Stereo modes
|
||||
// FIXME fix stereo display plugins
|
||||
|
||||
// SBS left/right
|
||||
new SideBySideStereoDisplayPlugin(),
|
||||
//new InterleavedStereoDisplayPlugin(),
|
||||
// Interleaved left/right
|
||||
new InterleavedStereoDisplayPlugin(),
|
||||
|
||||
// HMDs
|
||||
|
||||
|
|
|
@ -115,7 +115,7 @@ public:
|
|||
virtual void resetSensors() {}
|
||||
virtual float devicePixelRatio() { return 1.0; }
|
||||
|
||||
|
||||
static const QString MENU_PATH;
|
||||
signals:
|
||||
void recommendedFramebufferSizeChanged(const QSize & size);
|
||||
void requestRender();
|
||||
|
|
|
@ -105,7 +105,7 @@ bool OpenGLDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
|
|||
void OpenGLDisplayPlugin::display(
|
||||
GLuint finalTexture, const glm::uvec2& sceneSize) {
|
||||
using namespace oglplus;
|
||||
uvec2 size = getRecommendedRenderSize();
|
||||
uvec2 size = getSurfaceSize();
|
||||
Context::Viewport(size.x, size.y);
|
||||
glBindTexture(GL_TEXTURE_2D, finalTexture);
|
||||
drawUnitQuad();
|
||||
|
|
|
@ -33,6 +33,7 @@ public:
|
|||
protected:
|
||||
virtual void customizeContext();
|
||||
virtual void drawUnitQuad();
|
||||
virtual glm::uvec2 getSurfaceSize() const = 0;
|
||||
virtual void makeCurrent() = 0;
|
||||
virtual void doneCurrent() = 0;
|
||||
virtual void swapBuffers() = 0;
|
||||
|
|
|
@ -16,6 +16,10 @@ WindowOpenGLDisplayPlugin::WindowOpenGLDisplayPlugin() {
|
|||
}
|
||||
|
||||
glm::uvec2 WindowOpenGLDisplayPlugin::getRecommendedRenderSize() const {
|
||||
return getSurfaceSize();
|
||||
}
|
||||
|
||||
glm::uvec2 WindowOpenGLDisplayPlugin::getSurfaceSize() const {
|
||||
uvec2 result;
|
||||
if (_window) {
|
||||
result = toGlm(_window->geometry().size() * _window->devicePixelRatio());
|
||||
|
@ -23,6 +27,7 @@ glm::uvec2 WindowOpenGLDisplayPlugin::getRecommendedRenderSize() const {
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
glm::uvec2 WindowOpenGLDisplayPlugin::getRecommendedUiSize() const {
|
||||
uvec2 result;
|
||||
if (_window) {
|
||||
|
|
|
@ -21,6 +21,7 @@ public:
|
|||
virtual void deactivate() override;
|
||||
|
||||
protected:
|
||||
virtual glm::uvec2 getSurfaceSize() const override final;
|
||||
virtual void makeCurrent() override;
|
||||
virtual void doneCurrent() override;
|
||||
virtual void swapBuffers() override;
|
||||
|
|
|
@ -17,6 +17,42 @@
|
|||
|
||||
#include <gpu/GLBackend.h>
|
||||
|
||||
static const char * INTERLEAVED_TEXTURED_VS = R"VS(#version 410 core
|
||||
#pragma line __LINE__
|
||||
|
||||
in vec3 Position;
|
||||
in vec2 TexCoord;
|
||||
|
||||
out vec2 vTexCoord;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(Position, 1);
|
||||
vTexCoord = TexCoord;
|
||||
}
|
||||
|
||||
)VS";
|
||||
|
||||
static const char * INTERLEAVED_TEXTURED_FS = R"FS(#version 410 core
|
||||
#pragma line __LINE__
|
||||
|
||||
uniform sampler2D sampler;
|
||||
uniform ivec2 textureSize;
|
||||
|
||||
in vec2 vTexCoord;
|
||||
out vec4 FragColor;
|
||||
|
||||
void main() {
|
||||
ivec2 texCoord = ivec2(floor(vTexCoord * textureSize));
|
||||
texCoord.x /= 2;
|
||||
int row = int(floor(gl_FragCoord.y));
|
||||
if (row % 2 > 0) {
|
||||
texCoord.x += (textureSize.x / 2);
|
||||
}
|
||||
FragColor = texelFetch(sampler, texCoord, 0); //texture(sampler, texCoord);
|
||||
}
|
||||
|
||||
)FS";
|
||||
|
||||
const QString InterleavedStereoDisplayPlugin::NAME("Interleaved Stereo Display");
|
||||
|
||||
const QString & InterleavedStereoDisplayPlugin::getName() const {
|
||||
|
@ -29,5 +65,20 @@ InterleavedStereoDisplayPlugin::InterleavedStereoDisplayPlugin() {
|
|||
void InterleavedStereoDisplayPlugin::customizeContext() {
|
||||
StereoDisplayPlugin::customizeContext();
|
||||
// Set up the stencil buffers? Or use a custom shader?
|
||||
|
||||
compileProgram(_program, INTERLEAVED_TEXTURED_VS, INTERLEAVED_TEXTURED_FS);
|
||||
}
|
||||
|
||||
glm::uvec2 InterleavedStereoDisplayPlugin::getRecommendedRenderSize() const {
|
||||
uvec2 result = WindowOpenGLDisplayPlugin::getRecommendedRenderSize();
|
||||
result.x *= 2;
|
||||
result.y /= 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
void InterleavedStereoDisplayPlugin::display(
|
||||
GLuint finalTexture, const glm::uvec2& sceneSize) {
|
||||
using namespace oglplus;
|
||||
_program->Bind();
|
||||
Uniform<ivec2>(*_program, "textureSize").SetValue(sceneSize);
|
||||
WindowOpenGLDisplayPlugin::display(finalTexture, sceneSize);
|
||||
}
|
|
@ -18,6 +18,9 @@ public:
|
|||
// initialize OpenGL context settings needed by the plugin
|
||||
virtual void customizeContext() override;
|
||||
|
||||
virtual glm::uvec2 getRecommendedRenderSize() const override;
|
||||
void display(GLuint finalTexture, const glm::uvec2& sceneSize) override;
|
||||
|
||||
private:
|
||||
static const QString NAME;
|
||||
};
|
||||
|
|
|
@ -21,9 +21,6 @@
|
|||
|
||||
const QString SideBySideStereoDisplayPlugin::NAME("3D TV - Side by Side Stereo");
|
||||
|
||||
static const QString MENU_PATH = "Display";
|
||||
static const QString FULLSCREEN = "Fullscreen";
|
||||
|
||||
const QString & SideBySideStereoDisplayPlugin::getName() const {
|
||||
return NAME;
|
||||
}
|
||||
|
@ -31,20 +28,10 @@ const QString & SideBySideStereoDisplayPlugin::getName() const {
|
|||
SideBySideStereoDisplayPlugin::SideBySideStereoDisplayPlugin() {
|
||||
}
|
||||
|
||||
void SideBySideStereoDisplayPlugin::activate() {
|
||||
CONTAINER->addMenu(MENU_PATH);
|
||||
CONTAINER->addMenuItem(MENU_PATH, FULLSCREEN,
|
||||
[this](bool clicked) {
|
||||
if (clicked) {
|
||||
CONTAINER->setFullscreen(getFullscreenTarget());
|
||||
} else {
|
||||
CONTAINER->unsetFullscreen();
|
||||
}
|
||||
}, true, false);
|
||||
StereoDisplayPlugin::activate();
|
||||
glm::uvec2 SideBySideStereoDisplayPlugin::getRecommendedRenderSize() const {
|
||||
uvec2 result = WindowOpenGLDisplayPlugin::getRecommendedRenderSize();
|
||||
result.x *= 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
// FIXME target the screen the window is currently on
|
||||
QScreen* SideBySideStereoDisplayPlugin::getFullscreenTarget() {
|
||||
return qApp->primaryScreen();
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,7 @@ class SideBySideStereoDisplayPlugin : public StereoDisplayPlugin {
|
|||
public:
|
||||
SideBySideStereoDisplayPlugin();
|
||||
virtual const QString& getName() const override;
|
||||
virtual void activate() override;
|
||||
virtual glm::uvec2 getRecommendedRenderSize() const override;
|
||||
private:
|
||||
QScreen* getFullscreenTarget();
|
||||
static const QString NAME;
|
||||
};
|
||||
|
|
|
@ -10,11 +10,14 @@
|
|||
|
||||
#include <QApplication>
|
||||
#include <QDesktopWidget>
|
||||
#include <QAction>
|
||||
|
||||
#include <gpu/GLBackend.h>
|
||||
#include <ViewFrustum.h>
|
||||
#include <MatrixStack.h>
|
||||
#include <plugins/PluginContainer.h>
|
||||
#include <QGuiApplication>
|
||||
#include <QScreen>
|
||||
|
||||
StereoDisplayPlugin::StereoDisplayPlugin() {
|
||||
}
|
||||
|
@ -29,11 +32,14 @@ const float DEFAULT_IPD = 0.064f;
|
|||
const float HALF_DEFAULT_IPD = DEFAULT_IPD / 2.0f;
|
||||
|
||||
glm::mat4 StereoDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const {
|
||||
// FIXME check for mono eye and provide a combined matrix, needed for proper
|
||||
// culling
|
||||
// Refer to http://www.nvidia.com/content/gtc-2010/pdfs/2010_gtc2010.pdf on creating
|
||||
// stereo projection matrices. Do NOT use "toe-in", use translation.
|
||||
|
||||
if (eye == Mono) {
|
||||
// FIXME provide a combined matrix, needed for proper culling
|
||||
return baseProjection;
|
||||
}
|
||||
|
||||
float nearZ = DEFAULT_NEAR_CLIP; // near clipping plane
|
||||
float screenZ = 0.25f; // screen projection plane
|
||||
// FIXME verify this is the right calculation
|
||||
|
@ -52,10 +58,36 @@ glm::mat4 StereoDisplayPlugin::getEyePose(Eye eye) const {
|
|||
return glm::translate(mat4(), vec3(modelviewShift, 0, 0));
|
||||
}
|
||||
|
||||
std::vector<QAction*> _screenActions;
|
||||
void StereoDisplayPlugin::activate() {
|
||||
auto screens = qApp->screens();
|
||||
_screenActions.resize(screens.size());
|
||||
for (int i = 0; i < screens.size(); ++i) {
|
||||
auto screen = screens.at(i);
|
||||
QString name = QString("Screen %1: %2").arg(i + 1).arg(screen->name());
|
||||
bool checked = false;
|
||||
if (screen == qApp->primaryScreen()) {
|
||||
checked = true;
|
||||
}
|
||||
auto action = CONTAINER->addMenuItem(MENU_PATH, name,
|
||||
[this](bool clicked) { updateScreen(); }, true, checked, "Screens");
|
||||
_screenActions[i] = action;
|
||||
}
|
||||
CONTAINER->setFullscreen(qApp->primaryScreen());
|
||||
WindowOpenGLDisplayPlugin::activate();
|
||||
// FIXME there is a bug in the fullscreen setting, see
|
||||
// Application::setFullscreen
|
||||
//CONTAINER->setFullscreen(qApp->primaryScreen());
|
||||
// FIXME Add menu items
|
||||
}
|
||||
|
||||
void StereoDisplayPlugin::updateScreen() {
|
||||
for (int i = 0; i < _screenActions.size(); ++i) {
|
||||
if (_screenActions[i]->isChecked()) {
|
||||
CONTAINER->setFullscreen(qApp->screens().at(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StereoDisplayPlugin::deactivate() {
|
||||
_screenActions.clear();
|
||||
CONTAINER->unsetFullscreen();
|
||||
WindowOpenGLDisplayPlugin::deactivate();
|
||||
}
|
||||
|
|
|
@ -17,10 +17,12 @@ public:
|
|||
virtual bool isSupported() const override final;
|
||||
|
||||
virtual void activate() override;
|
||||
virtual void deactivate() override;
|
||||
|
||||
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
|
||||
virtual glm::mat4 getEyePose(Eye eye) const override;
|
||||
|
||||
protected:
|
||||
void updateScreen();
|
||||
float _ipd{ 0.064f };
|
||||
};
|
||||
|
|
|
@ -30,6 +30,7 @@ HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestH
|
|||
_port(port)
|
||||
{
|
||||
bindSocket();
|
||||
|
||||
_isListeningTimer = new QTimer(this);
|
||||
connect(_isListeningTimer, &QTimer::timeout, this, &HTTPManager::isTcpServerListening);
|
||||
_isListeningTimer->start(SOCKET_CHECK_INTERVAL_IN_MS);
|
||||
|
@ -171,9 +172,19 @@ void HTTPManager::isTcpServerListening() {
|
|||
|
||||
bool HTTPManager::bindSocket() {
|
||||
qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port);
|
||||
if (!listen(QHostAddress::Any, _port)) {
|
||||
|
||||
if (listen(QHostAddress::AnyIPv4, _port)) {
|
||||
qCDebug(embeddedwebserver) << "TCP socket is listening on" << serverAddress() << "and port" << serverPort();
|
||||
|
||||
return true;
|
||||
} else {
|
||||
qCritical() << "Failed to open HTTP server socket:" << errorString() << " can't continue";
|
||||
QCoreApplication::exit(SOCKET_ERROR_EXIT_CODE);
|
||||
QMetaObject::invokeMethod(this, "queuedExit", Qt::QueuedConnection);
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPManager::queuedExit() {
|
||||
QCoreApplication::exit(SOCKET_ERROR_EXIT_CODE);
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ public:
|
|||
|
||||
private slots:
|
||||
void isTcpServerListening();
|
||||
void queuedExit();
|
||||
|
||||
private:
|
||||
bool bindSocket();
|
||||
|
|
|
@ -6,7 +6,7 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
|
|||
string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE)
|
||||
if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR)
|
||||
string(TOLOWER ${EXTERNAL} ${EXTERNAL}_LOWERCASE)
|
||||
set(${${EXTERNAL}_UPPERCASE}_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/${${EXTERNAL}_LOWERCASE}")
|
||||
set(${${EXTERNAL}_UPPERCASE}_ROOT_DIR "${CMAKE_SOURCE_DIR}/interface/external/${${EXTERNAL}_LOWERCASE}")
|
||||
endif ()
|
||||
endforeach()
|
||||
|
||||
|
|
|
@ -58,10 +58,12 @@ const Vec3& Light::getDirection() const {
|
|||
|
||||
void Light::setColor(const Color& color) {
|
||||
editSchema()._color = color;
|
||||
updateLightRadius();
|
||||
}
|
||||
|
||||
void Light::setIntensity(float intensity) {
|
||||
editSchema()._intensity = intensity;
|
||||
updateLightRadius();
|
||||
}
|
||||
|
||||
void Light::setAmbientIntensity(float intensity) {
|
||||
|
@ -72,9 +74,14 @@ void Light::setMaximumRadius(float radius) {
|
|||
if (radius <= 0.f) {
|
||||
radius = 1.0f;
|
||||
}
|
||||
editSchema()._attenuation.w = radius;
|
||||
updateLightRadius();
|
||||
}
|
||||
|
||||
void Light::updateLightRadius() {
|
||||
float CutOffIntensityRatio = 0.05f;
|
||||
float surfaceRadius = radius / (sqrtf(1.0f / CutOffIntensityRatio) - 1.0f);
|
||||
editSchema()._attenuation = Vec4(surfaceRadius, 1.0f/surfaceRadius, CutOffIntensityRatio, radius);
|
||||
float surfaceRadius = getMaximumRadius() / (sqrtf((getIntensity() * std::max(std::max(getColor().x, getColor().y), getColor().z)) / CutOffIntensityRatio) - 1.0f);
|
||||
editSchema()._attenuation = Vec4(surfaceRadius, 1.0f/surfaceRadius, CutOffIntensityRatio, getMaximumRadius());
|
||||
}
|
||||
|
||||
#include <math.h>
|
||||
|
|
|
@ -127,6 +127,8 @@ protected:
|
|||
|
||||
const Schema& getSchema() const { return _schemaBuffer.get<Schema>(); }
|
||||
Schema& editSchema() { return _schemaBuffer.edit<Schema>(); }
|
||||
|
||||
void updateLightRadius();
|
||||
};
|
||||
typedef std::shared_ptr< Light > LightPointer;
|
||||
|
||||
|
|
23
libraries/plugins/src/plugins/Forward.h
Normal file
23
libraries/plugins/src/plugins/Forward.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/08/08
|
||||
// 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
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <QList>
|
||||
#include <QVector>
|
||||
#include <QSharedPointer>
|
||||
|
||||
class DisplayPlugin;
|
||||
class InputPlugin;
|
||||
class Plugin;
|
||||
class PluginContainer;
|
||||
class PluginManager;
|
||||
|
||||
using DisplayPluginPointer = QSharedPointer<DisplayPlugin>;
|
||||
using DisplayPluginList = QVector<DisplayPluginPointer>;
|
||||
using InputPluginPointer = QSharedPointer<InputPlugin>;
|
||||
using InputPluginList = QVector<InputPluginPointer>;
|
|
@ -1,3 +1,10 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/08/08
|
||||
// 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 "Plugin.h"
|
||||
|
||||
PluginContainer* Plugin::CONTAINER{ nullptr };
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/08/08
|
||||
// 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
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QObject>
|
||||
|
||||
class PluginContainer;
|
||||
#include "Forward.h"
|
||||
|
||||
class Plugin : public QObject {
|
||||
public:
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
//
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <functional>
|
||||
#include <QString>
|
||||
|
||||
class QAction;
|
||||
class QGLWidget;
|
||||
class QScreen;
|
||||
|
||||
|
@ -18,11 +19,11 @@ public:
|
|||
PluginContainer();
|
||||
virtual void addMenu(const QString& menuName) = 0;
|
||||
virtual void removeMenu(const QString& menuName) = 0;
|
||||
virtual void addMenuItem(const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") = 0;
|
||||
virtual QAction* addMenuItem(const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") = 0;
|
||||
virtual void removeMenuItem(const QString& menuName, const QString& menuItem) = 0;
|
||||
virtual bool isOptionChecked(const QString& name) = 0;
|
||||
virtual void setIsOptionChecked(const QString& path, bool checked) = 0;
|
||||
virtual void setFullscreen(const QScreen* targetScreen) = 0;
|
||||
virtual void setFullscreen(const QScreen* targetScreen, bool hideMenu = false) = 0;
|
||||
virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) = 0;
|
||||
virtual void showDisplayPluginsTools() = 0;
|
||||
virtual QGLWidget* getPrimarySurface() = 0;
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/08/08
|
||||
// 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 "PluginManager.h"
|
||||
#include <mutex>
|
||||
|
||||
|
||||
PluginManager* PluginManager::getInstance() {
|
||||
static PluginManager _manager;
|
||||
return &_manager;
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/08/08
|
||||
// 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
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "Plugin.h"
|
||||
#include <QList>
|
||||
#include <QVector>
|
||||
#include <QSharedPointer>
|
||||
|
||||
class DisplayPlugin;
|
||||
class InputPlugin;
|
||||
|
||||
using DisplayPluginPointer = QSharedPointer<DisplayPlugin>;
|
||||
using DisplayPluginList = QVector<DisplayPluginPointer>;
|
||||
using InputPluginPointer = QSharedPointer<InputPlugin>;
|
||||
using InputPluginList = QVector<InputPluginPointer>;
|
||||
#include "Forward.h"
|
||||
|
||||
class PluginManager : public QObject {
|
||||
public:
|
||||
|
|
|
@ -51,8 +51,13 @@ const gpu::PipelinePointer& AmbientOcclusion::getOcclusionPipeline() {
|
|||
_gBiasLoc = program->getUniforms().findLocation("g_bias");
|
||||
_gSampleRadiusLoc = program->getUniforms().findLocation("g_sample_rad");
|
||||
_gIntensityLoc = program->getUniforms().findLocation("g_intensity");
|
||||
_bufferWidthLoc = program->getUniforms().findLocation("bufferWidth");
|
||||
_bufferHeightLoc = program->getUniforms().findLocation("bufferHeight");
|
||||
|
||||
_nearLoc = program->getUniforms().findLocation("near");
|
||||
_depthScaleLoc = program->getUniforms().findLocation("depthScale");
|
||||
_depthTexCoordOffsetLoc = program->getUniforms().findLocation("depthTexCoordOffset");
|
||||
_depthTexCoordScaleLoc = program->getUniforms().findLocation("depthTexCoordScale");
|
||||
_renderTargetResLoc = program->getUniforms().findLocation("renderTargetRes");
|
||||
_renderTargetResInvLoc = program->getUniforms().findLocation("renderTargetResInv");
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
|
||||
|
@ -172,9 +177,19 @@ const gpu::PipelinePointer& AmbientOcclusion::getBlendPipeline() {
|
|||
void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->_viewFrustum);
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
gpu::Batch batch;
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||
QSize framebufferSize = framebufferCache->getFrameBufferSize();
|
||||
float fbWidth = framebufferSize.width();
|
||||
float fbHeight = framebufferSize.height();
|
||||
float sMin = args->_viewport.x / fbWidth;
|
||||
float sWidth = args->_viewport.z / fbWidth;
|
||||
float tMin = args->_viewport.y / fbHeight;
|
||||
float tHeight = args->_viewport.w / fbHeight;
|
||||
|
||||
|
||||
glm::mat4 projMat;
|
||||
Transform viewMat;
|
||||
|
@ -186,8 +201,8 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons
|
|||
|
||||
// Occlusion step
|
||||
getOcclusionPipeline();
|
||||
batch.setResourceTexture(0, DependencyManager::get<FramebufferCache>()->getPrimaryDepthTexture());
|
||||
batch.setResourceTexture(1, DependencyManager::get<FramebufferCache>()->getPrimaryNormalTexture());
|
||||
batch.setResourceTexture(0, framebufferCache->getPrimaryDepthTexture());
|
||||
batch.setResourceTexture(1, framebufferCache->getPrimaryNormalTexture());
|
||||
_occlusionBuffer->setRenderBuffer(0, _occlusionTexture);
|
||||
batch.setFramebuffer(_occlusionBuffer);
|
||||
|
||||
|
@ -203,8 +218,32 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons
|
|||
batch._glUniform1f(_gBiasLoc, g_bias);
|
||||
batch._glUniform1f(_gSampleRadiusLoc, g_sample_rad);
|
||||
batch._glUniform1f(_gIntensityLoc, g_intensity);
|
||||
batch._glUniform1f(_bufferWidthLoc, DependencyManager::get<FramebufferCache>()->getFrameBufferSize().width());
|
||||
batch._glUniform1f(_bufferHeightLoc, DependencyManager::get<FramebufferCache>()->getFrameBufferSize().height());
|
||||
|
||||
// setup uniforms for unpacking a view-space position from the depth buffer
|
||||
// This is code taken from DeferredLightEffect.render() method in DeferredLightingEffect.cpp.
|
||||
// DeferredBuffer.slh shows how the unpacking is done and what variables are needed.
|
||||
|
||||
// initialize the view-space unpacking uniforms using frustum data
|
||||
float left, right, bottom, top, nearVal, farVal;
|
||||
glm::vec4 nearClipPlane, farClipPlane;
|
||||
|
||||
args->_viewFrustum->computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane);
|
||||
|
||||
float depthScale = (farVal - nearVal) / farVal;
|
||||
float nearScale = -1.0f / nearVal;
|
||||
float depthTexCoordScaleS = (right - left) * nearScale / sWidth;
|
||||
float depthTexCoordScaleT = (top - bottom) * nearScale / tHeight;
|
||||
float depthTexCoordOffsetS = left * nearScale - sMin * depthTexCoordScaleS;
|
||||
float depthTexCoordOffsetT = bottom * nearScale - tMin * depthTexCoordScaleT;
|
||||
|
||||
// now set the position-unpacking unforms
|
||||
batch._glUniform1f(_nearLoc, nearVal);
|
||||
batch._glUniform1f(_depthScaleLoc, depthScale);
|
||||
batch._glUniform2f(_depthTexCoordOffsetLoc, depthTexCoordOffsetS, depthTexCoordOffsetT);
|
||||
batch._glUniform2f(_depthTexCoordScaleLoc, depthTexCoordScaleS, depthTexCoordScaleT);
|
||||
|
||||
batch._glUniform2f(_renderTargetResLoc, fbWidth, fbHeight);
|
||||
batch._glUniform2f(_renderTargetResInvLoc, 1.0/fbWidth, 1.0/fbHeight);
|
||||
|
||||
glm::vec4 color(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glm::vec2 bottomLeft(-1.0f, -1.0f);
|
||||
|
@ -238,13 +277,13 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons
|
|||
// Blend step
|
||||
getBlendPipeline();
|
||||
batch.setResourceTexture(0, _hBlurTexture);
|
||||
batch.setFramebuffer(DependencyManager::get<FramebufferCache>()->getPrimaryFramebuffer());
|
||||
batch.setFramebuffer(framebufferCache->getPrimaryFramebuffer());
|
||||
|
||||
// Bind the fourth gpu::Pipeline we need - for blending the primary color buffer with blurred occlusion texture
|
||||
batch.setPipeline(getBlendPipeline());
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color);
|
||||
|
||||
|
||||
// Ready to render
|
||||
args->_context->render((batch));
|
||||
}
|
||||
|
|
|
@ -36,8 +36,15 @@ private:
|
|||
gpu::int32 _gBiasLoc;
|
||||
gpu::int32 _gSampleRadiusLoc;
|
||||
gpu::int32 _gIntensityLoc;
|
||||
gpu::int32 _bufferWidthLoc;
|
||||
gpu::int32 _bufferHeightLoc;
|
||||
|
||||
gpu::int32 _nearLoc;
|
||||
gpu::int32 _depthScaleLoc;
|
||||
gpu::int32 _depthTexCoordOffsetLoc;
|
||||
gpu::int32 _depthTexCoordScaleLoc;
|
||||
gpu::int32 _renderTargetResLoc;
|
||||
gpu::int32 _renderTargetResInvLoc;
|
||||
|
||||
|
||||
float g_scale;
|
||||
float g_bias;
|
||||
float g_sample_rad;
|
||||
|
|
|
@ -70,7 +70,7 @@ void GeometryCache::renderSphere(gpu::Batch& batch, float radius, int slices, in
|
|||
|
||||
int vertices = slices * (stacks - 1) + 2;
|
||||
int indices = slices * (stacks - 1) * NUM_VERTICES_PER_TRIANGULATED_QUAD;
|
||||
|
||||
|
||||
if ((registered && (!_registeredSphereVertices.contains(id) || _lastRegisteredSphereVertices[id] != radiusKey))
|
||||
|| (!registered && !_sphereVertices.contains(radiusKey))) {
|
||||
|
||||
|
@ -80,7 +80,7 @@ void GeometryCache::renderSphere(gpu::Batch& batch, float radius, int slices, in
|
|||
qCDebug(renderutils) << "renderSphere()... RELEASING REGISTERED VERTICES BUFFER";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
auto verticesBuffer = std::make_shared<gpu::Buffer>();
|
||||
if (registered) {
|
||||
_registeredSphereVertices[id] = verticesBuffer;
|
||||
|
@ -134,7 +134,7 @@ void GeometryCache::renderSphere(gpu::Batch& batch, float radius, int slices, in
|
|||
qCDebug(renderutils) << "renderSphere()... REUSING PREVIOUSLY REGISTERED VERTICES BUFFER";
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if ((registered && (!_registeredSphereIndices.contains(id) || _lastRegisteredSphereIndices[id] != slicesStacksKey))
|
||||
|| (!registered && !_sphereIndices.contains(slicesStacksKey))) {
|
||||
|
||||
|
@ -1327,7 +1327,9 @@ void GeometryCache::renderQuad(gpu::Batch& batch, const glm::vec3& topLeft, cons
|
|||
batch.draw(gpu::TRIANGLE_STRIP, 4, 0);
|
||||
}
|
||||
|
||||
void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, int id) {
|
||||
void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color,
|
||||
const float dash_length, const float gap_length, int id) {
|
||||
|
||||
bool registered = (id != UNKNOWN_ID);
|
||||
Vec3PairVec2Pair key(Vec3Pair(start, end), Vec2Pair(glm::vec2(color.x, color.y), glm::vec2(color.z, color.w)));
|
||||
BatchItemDetails& details = registered ? _registeredDashedLines[id] : _dashedLines[key];
|
||||
|
@ -1351,16 +1353,14 @@ void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start,
|
|||
((int(color.w * 255.0f) & 0xFF) << 24);
|
||||
|
||||
// draw each line segment with appropriate gaps
|
||||
const float DASH_LENGTH = 0.05f;
|
||||
const float GAP_LENGTH = 0.025f;
|
||||
const float SEGMENT_LENGTH = DASH_LENGTH + GAP_LENGTH;
|
||||
const float SEGMENT_LENGTH = dash_length + gap_length;
|
||||
float length = glm::distance(start, end);
|
||||
float segmentCount = length / SEGMENT_LENGTH;
|
||||
int segmentCountFloor = (int)glm::floor(segmentCount);
|
||||
|
||||
glm::vec3 segmentVector = (end - start) / segmentCount;
|
||||
glm::vec3 dashVector = segmentVector / SEGMENT_LENGTH * DASH_LENGTH;
|
||||
glm::vec3 gapVector = segmentVector / SEGMENT_LENGTH * GAP_LENGTH;
|
||||
glm::vec3 dashVector = segmentVector / SEGMENT_LENGTH * dash_length;
|
||||
glm::vec3 gapVector = segmentVector / SEGMENT_LENGTH * gap_length;
|
||||
|
||||
const int FLOATS_PER_VERTEX = 3;
|
||||
details.vertices = (segmentCountFloor + 1) * 2;
|
||||
|
@ -1394,7 +1394,7 @@ void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start,
|
|||
*(vertex++) = point.y;
|
||||
*(vertex++) = point.z;
|
||||
*(colorDataAt++) = compactColor;
|
||||
|
||||
|
||||
for (int i = 0; i < segmentCountFloor; i++) {
|
||||
point += dashVector;
|
||||
*(vertex++) = point.x;
|
||||
|
@ -1723,7 +1723,7 @@ void GeometryReader::run() {
|
|||
NetworkGeometry::NetworkGeometry(const QUrl& url, bool delayLoad, const QVariantHash& mapping, const QUrl& textureBaseUrl) :
|
||||
_url(url),
|
||||
_mapping(mapping),
|
||||
_textureBaseUrl(textureBaseUrl) {
|
||||
_textureBaseUrl(textureBaseUrl.isValid() ? textureBaseUrl : url) {
|
||||
|
||||
if (delayLoad) {
|
||||
_state = DelayState;
|
||||
|
|
|
@ -21,8 +21,6 @@
|
|||
#include "FBXReader.h"
|
||||
#include "OBJReader.h"
|
||||
|
||||
#include <AnimationCache.h>
|
||||
|
||||
#include <gpu/Batch.h>
|
||||
#include <gpu/Stream.h>
|
||||
|
||||
|
@ -182,8 +180,13 @@ public:
|
|||
|
||||
void renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2,
|
||||
const glm::vec4& color1, const glm::vec4& color2, int id = UNKNOWN_ID);
|
||||
|
||||
void renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color, int id = UNKNOWN_ID);
|
||||
|
||||
void renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color,
|
||||
int id = UNKNOWN_ID)
|
||||
{ renderDashedLine(batch, start, end, color, 0.05f, 0.025f, id); }
|
||||
|
||||
void renderDashedLine(gpu::Batch& batch, const glm::vec3& start, const glm::vec3& end, const glm::vec4& color,
|
||||
const float dash_length, const float gap_length, int id = UNKNOWN_ID);
|
||||
|
||||
void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, const glm::vec3& color, int id = UNKNOWN_ID)
|
||||
{ renderLine(batch, p1, p2, glm::vec4(color, 1.0f), id); }
|
||||
|
@ -191,12 +194,11 @@ public:
|
|||
void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2, const glm::vec4& color, int id = UNKNOWN_ID)
|
||||
{ renderLine(batch, p1, p2, color, color, id); }
|
||||
|
||||
|
||||
void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2,
|
||||
const glm::vec3& color1, const glm::vec3& color2, int id = UNKNOWN_ID)
|
||||
void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2,
|
||||
const glm::vec3& color1, const glm::vec3& color2, int id = UNKNOWN_ID)
|
||||
{ renderLine(batch, p1, p2, glm::vec4(color1, 1.0f), glm::vec4(color2, 1.0f), id); }
|
||||
|
||||
void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2,
|
||||
|
||||
void renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm::vec2& p2,
|
||||
const glm::vec4& color1, const glm::vec4& color2, int id = UNKNOWN_ID);
|
||||
|
||||
void updateVertices(int id, const QVector<glm::vec2>& points, const glm::vec4& color);
|
||||
|
@ -215,7 +217,7 @@ public:
|
|||
private:
|
||||
GeometryCache();
|
||||
virtual ~GeometryCache();
|
||||
|
||||
|
||||
typedef QPair<int, int> IntPair;
|
||||
typedef QPair<unsigned int, unsigned int> VerticesIndices;
|
||||
|
||||
|
@ -246,7 +248,7 @@ private:
|
|||
~BatchItemDetails();
|
||||
void clear();
|
||||
};
|
||||
|
||||
|
||||
QHash<IntPair, VerticesIndices> _coneVBOs;
|
||||
|
||||
int _nextID;
|
||||
|
@ -383,20 +385,13 @@ protected:
|
|||
/// Reads geometry in a worker thread.
|
||||
class GeometryReader : public QObject, public QRunnable {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
GeometryReader(const QUrl& url, QNetworkReply* reply, const QVariantHash& mapping);
|
||||
|
||||
virtual void run();
|
||||
|
||||
signals:
|
||||
void onSuccess(FBXGeometry* geometry);
|
||||
void onError(int error, QString str);
|
||||
|
||||
private:
|
||||
|
||||
QWeakPointer<Resource> _geometry;
|
||||
QUrl _url;
|
||||
QNetworkReply* _reply;
|
||||
QVariantHash _mapping;
|
||||
|
|
|
@ -30,25 +30,40 @@ uniform float g_scale;
|
|||
uniform float g_bias;
|
||||
uniform float g_sample_rad;
|
||||
uniform float g_intensity;
|
||||
uniform float bufferWidth;
|
||||
uniform float bufferHeight;
|
||||
|
||||
// the distance to the near clip plane
|
||||
uniform float near;
|
||||
|
||||
// scale factor for depth: (far - near) / far
|
||||
uniform float depthScale;
|
||||
|
||||
// offset for depth texture coordinates
|
||||
uniform vec2 depthTexCoordOffset;
|
||||
|
||||
// scale for depth texture coordinates
|
||||
uniform vec2 depthTexCoordScale;
|
||||
|
||||
// the resolution of the occlusion buffer
|
||||
// and its inverse
|
||||
uniform vec2 renderTargetRes;
|
||||
uniform vec2 renderTargetResInv;
|
||||
|
||||
|
||||
|
||||
const float PI = 3.14159265;
|
||||
|
||||
const vec2 FocalLen = vec2(1.0, 1.0);
|
||||
|
||||
const vec2 LinMAD = vec2(0.1-10.0, 0.1+10.0) / (2.0*0.1*10.0);
|
||||
|
||||
const vec2 AORes = vec2(1024.0, 768.0);
|
||||
const vec2 InvAORes = vec2(1.0/1024.0, 1.0/768.0);
|
||||
const vec2 NoiseScale = vec2(1024.0, 768.0) / 4.0;
|
||||
|
||||
const float AOStrength = 1.9;
|
||||
const float R = 0.3;
|
||||
const float R2 = 0.3*0.3;
|
||||
const float NegInvR2 = - 1.0 / (0.3*0.3);
|
||||
|
||||
|
||||
// TODO: R (radius) should be exposed as a uniform parameter
|
||||
const float R = 0.01;
|
||||
const float R2 = 0.01*0.01;
|
||||
const float NegInvR2 = - 1.0 / (0.01*0.01);
|
||||
|
||||
|
||||
|
||||
// can't use tan to initialize a const value
|
||||
const float TanBias = 0.57735027; // tan(30.0 * PI / 180.0);
|
||||
const float TanBias = 0.57735027; // tan(30.0 * PI / 180.0);
|
||||
const float MaxRadiusPixels = 50.0;
|
||||
|
||||
const int NumDirections = 6;
|
||||
|
@ -56,113 +71,126 @@ const int NumSamples = 4;
|
|||
|
||||
out vec4 outFragColor;
|
||||
|
||||
/**
|
||||
* Gets the normal in view space from a normal texture.
|
||||
* uv: the uv texture coordinates to look up in the texture at.
|
||||
*/
|
||||
vec3 GetViewNormalFromTexture(vec2 uv) {
|
||||
// convert [0,1] -> [-1,1], note: since we're normalizing
|
||||
// we don't need to do v*2 - 1.0, we can just do a v-0.5
|
||||
return normalize(texture(normalTexture, uv).xyz - 0.5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the linearized depth in view space.
|
||||
* d: the depth value [0-1], usually from a depth texture to convert.
|
||||
*/
|
||||
float ViewSpaceZFromDepth(float d){
|
||||
// [0,1] -> [-1,1] clip space
|
||||
d = d * 2.0 - 1.0;
|
||||
|
||||
// Get view space Z
|
||||
return -1.0 / (LinMAD.x * d + LinMAD.y);
|
||||
return near / (d * depthScale - 1.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a uv coordinate and depth value into a 3D view space coordinate.
|
||||
* uv: the uv coordinates to convert
|
||||
* z: the view space depth of the uv coordinate.
|
||||
*/
|
||||
vec3 UVToViewSpace(vec2 uv, float z){
|
||||
//uv = UVToViewA * uv + UVToViewB;
|
||||
return vec3(uv * z, z);
|
||||
return vec3((depthTexCoordOffset + varTexcoord * depthTexCoordScale) * z, z);
|
||||
}
|
||||
|
||||
vec3 GetViewPos(vec2 uv){
|
||||
float z = ViewSpaceZFromDepth(texture(depthTexture, uv).r);
|
||||
return UVToViewSpace(uv, z);
|
||||
/**
|
||||
* Converts a uv coordinate into a 3D view space coordinate.
|
||||
* The depth of the uv coord is determined from the depth texture.
|
||||
* uv: the uv coordinates to convert
|
||||
*/
|
||||
vec3 GetViewPos(vec2 uv) {
|
||||
float z = ViewSpaceZFromDepth(texture(depthTexture, uv).r);
|
||||
return UVToViewSpace(uv, z);
|
||||
}
|
||||
|
||||
vec3 GetViewPosPoint(ivec2 uv){
|
||||
vec2 coord = vec2(gl_FragCoord.xy) + uv;
|
||||
//float z = texelFetch(texture0, coord, 0).r;
|
||||
float z = texture(depthTexture, uv).r;
|
||||
return UVToViewSpace(uv, z);
|
||||
|
||||
float TanToSin(float x) {
|
||||
return x * inversesqrt(x*x + 1.0);
|
||||
}
|
||||
|
||||
float TanToSin(float x){
|
||||
return x * inversesqrt(x*x + 1.0);
|
||||
float InvLength(vec2 V) {
|
||||
return inversesqrt(dot(V, V));
|
||||
}
|
||||
|
||||
float InvLength(vec2 V){
|
||||
return inversesqrt(dot(V,V));
|
||||
float Tangent(vec3 V) {
|
||||
return V.z * InvLength(V.xy);
|
||||
}
|
||||
|
||||
float Tangent(vec3 V){
|
||||
return V.z * InvLength(V.xy);
|
||||
float BiasedTangent(vec3 V) {
|
||||
return V.z * InvLength(V.xy) + TanBias;
|
||||
}
|
||||
|
||||
float BiasedTangent(vec3 V){
|
||||
return V.z * InvLength(V.xy) + TanBias;
|
||||
}
|
||||
|
||||
float Tangent(vec3 P, vec3 S){
|
||||
float Tangent(vec3 P, vec3 S) {
|
||||
return -(P.z - S.z) * InvLength(S.xy - P.xy);
|
||||
}
|
||||
|
||||
float Length2(vec3 V){
|
||||
return dot(V,V);
|
||||
float Length2(vec3 V) {
|
||||
return dot(V, V);
|
||||
}
|
||||
|
||||
vec3 MinDiff(vec3 P, vec3 Pr, vec3 Pl){
|
||||
vec3 MinDiff(vec3 P, vec3 Pr, vec3 Pl) {
|
||||
vec3 V1 = Pr - P;
|
||||
vec3 V2 = P - Pl;
|
||||
return (Length2(V1) < Length2(V2)) ? V1 : V2;
|
||||
}
|
||||
|
||||
vec2 SnapUVOffset(vec2 uv){
|
||||
return round(uv * AORes) * InvAORes;
|
||||
vec2 SnapUVOffset(vec2 uv) {
|
||||
return round(uv * renderTargetRes) * renderTargetResInv;
|
||||
}
|
||||
|
||||
float Falloff(float d2){
|
||||
return d2 * NegInvR2 + 1.0f;
|
||||
float Falloff(float d2) {
|
||||
return d2 * NegInvR2 + 1.0f;
|
||||
}
|
||||
|
||||
float HorizonOcclusion( vec2 deltaUV, vec3 P, vec3 dPdu, vec3 dPdv, float randstep, float numSamples){
|
||||
float ao = 0;
|
||||
float HorizonOcclusion(vec2 deltaUV, vec3 P, vec3 dPdu, vec3 dPdv, float randstep, float numSamples) {
|
||||
float ao = 0;
|
||||
|
||||
// Offset the first coord with some noise
|
||||
vec2 uv = varTexcoord + SnapUVOffset(randstep*deltaUV);
|
||||
deltaUV = SnapUVOffset( deltaUV );
|
||||
// Offset the first coord with some noise
|
||||
vec2 uv = varTexcoord + SnapUVOffset(randstep*deltaUV);
|
||||
deltaUV = SnapUVOffset(deltaUV);
|
||||
|
||||
// Calculate the tangent vector
|
||||
vec3 T = deltaUV.x * dPdu + deltaUV.y * dPdv;
|
||||
// Calculate the tangent vector
|
||||
vec3 T = deltaUV.x * dPdu + deltaUV.y * dPdv;
|
||||
|
||||
// Get the angle of the tangent vector from the viewspace axis
|
||||
float tanH = BiasedTangent(T);
|
||||
float sinH = TanToSin(tanH);
|
||||
// Get the angle of the tangent vector from the viewspace axis
|
||||
float tanH = BiasedTangent(T);
|
||||
float sinH = TanToSin(tanH);
|
||||
|
||||
float tanS;
|
||||
float d2;
|
||||
vec3 S;
|
||||
float tanS;
|
||||
float d2;
|
||||
vec3 S;
|
||||
|
||||
// Sample to find the maximum angle
|
||||
for(float s = 1; s <= numSamples; ++s){
|
||||
uv += deltaUV;
|
||||
S = GetViewPos(uv);
|
||||
tanS = Tangent(P, S);
|
||||
d2 = Length2(S - P);
|
||||
// Sample to find the maximum angle
|
||||
for (float s = 1; s <= numSamples; ++s) {
|
||||
uv += deltaUV;
|
||||
S = GetViewPos(uv);
|
||||
tanS = Tangent(P, S);
|
||||
d2 = Length2(S - P);
|
||||
|
||||
// Is the sample within the radius and the angle greater?
|
||||
if(d2 < R2 && tanS > tanH)
|
||||
{
|
||||
float sinS = TanToSin(tanS);
|
||||
// Apply falloff based on the distance
|
||||
ao += Falloff(d2) * (sinS - sinH);
|
||||
// Is the sample within the radius and the angle greater?
|
||||
if (d2 < R2 && tanS > tanH) {
|
||||
float sinS = TanToSin(tanS);
|
||||
// Apply falloff based on the distance
|
||||
ao += Falloff(d2) * (sinS - sinH);
|
||||
|
||||
tanH = tanS;
|
||||
sinH = sinS;
|
||||
}
|
||||
}
|
||||
return ao;
|
||||
tanH = tanS;
|
||||
sinH = sinS;
|
||||
}
|
||||
}
|
||||
return ao;
|
||||
}
|
||||
|
||||
vec2 RotateDirections(vec2 Dir, vec2 CosSin){
|
||||
return vec2(Dir.x*CosSin.x - Dir.y*CosSin.y, Dir.x*CosSin.y + Dir.y*CosSin.x);
|
||||
vec2 RotateDirections(vec2 Dir, vec2 CosSin) {
|
||||
return vec2(Dir.x*CosSin.x - Dir.y*CosSin.y,
|
||||
Dir.x*CosSin.y + Dir.y*CosSin.x);
|
||||
}
|
||||
|
||||
void ComputeSteps(inout vec2 stepSizeUv, inout float numSteps, float rayRadiusPix, float rand){
|
||||
void ComputeSteps(inout vec2 stepSizeUv, inout float numSteps, float rayRadiusPix, float rand) {
|
||||
// Avoid oversampling if numSteps is greater than the kernel radius in pixels
|
||||
numSteps = min(NumSamples, rayRadiusPix);
|
||||
|
||||
|
@ -171,8 +199,7 @@ void ComputeSteps(inout vec2 stepSizeUv, inout float numSteps, float rayRadiusPi
|
|||
|
||||
// Clamp numSteps if it is greater than the max kernel footprint
|
||||
float maxNumSteps = MaxRadiusPixels / stepSizePix;
|
||||
if (maxNumSteps < numSteps)
|
||||
{
|
||||
if (maxNumSteps < numSteps) {
|
||||
// Use dithering to avoid AO discontinuities
|
||||
numSteps = floor(maxNumSteps + rand);
|
||||
numSteps = max(numSteps, 1);
|
||||
|
@ -180,69 +207,73 @@ void ComputeSteps(inout vec2 stepSizeUv, inout float numSteps, float rayRadiusPi
|
|||
}
|
||||
|
||||
// Step size in uv space
|
||||
stepSizeUv = stepSizePix * InvAORes;
|
||||
stepSizeUv = stepSizePix * renderTargetResInv;
|
||||
}
|
||||
|
||||
float getRandom(vec2 uv){
|
||||
float getRandom(vec2 uv) {
|
||||
return fract(sin(dot(uv.xy ,vec2(12.9898,78.233))) * 43758.5453);
|
||||
}
|
||||
|
||||
void main(void){
|
||||
float numDirections = NumDirections;
|
||||
void main(void) {
|
||||
mat4 projMatrix = getTransformCamera()._projection;
|
||||
|
||||
vec3 P, Pr, Pl, Pt, Pb;
|
||||
P = GetViewPos(varTexcoord);
|
||||
float numDirections = NumDirections;
|
||||
|
||||
// Sample neighboring pixels
|
||||
Pr = GetViewPos(varTexcoord + vec2( InvAORes.x, 0));
|
||||
Pl = GetViewPos(varTexcoord + vec2(-InvAORes.x, 0));
|
||||
Pt = GetViewPos(varTexcoord + vec2( 0, InvAORes.y));
|
||||
Pb = GetViewPos(varTexcoord + vec2( 0,-InvAORes.y));
|
||||
vec3 P, Pr, Pl, Pt, Pb;
|
||||
P = GetViewPos(varTexcoord);
|
||||
|
||||
// Sample neighboring pixels
|
||||
Pr = GetViewPos(varTexcoord + vec2( renderTargetResInv.x, 0));
|
||||
Pl = GetViewPos(varTexcoord + vec2(-renderTargetResInv.x, 0));
|
||||
Pt = GetViewPos(varTexcoord + vec2( 0, renderTargetResInv.y));
|
||||
Pb = GetViewPos(varTexcoord + vec2( 0,-renderTargetResInv.y));
|
||||
|
||||
// Calculate tangent basis vectors using the minimum difference
|
||||
vec3 dPdu = MinDiff(P, Pr, Pl);
|
||||
vec3 dPdv = MinDiff(P, Pt, Pb) * (AORes.y * InvAORes.x);
|
||||
vec3 dPdv = MinDiff(P, Pt, Pb) * (renderTargetRes.y * renderTargetResInv.x);
|
||||
|
||||
// Get the random samples from the noise function
|
||||
vec3 random = vec3(getRandom(varTexcoord.xy), getRandom(varTexcoord.yx), getRandom(varTexcoord.xx));
|
||||
vec3 random = vec3(getRandom(varTexcoord.xy), getRandom(varTexcoord.yx), getRandom(varTexcoord.xx));
|
||||
|
||||
// Calculate the projected size of the hemisphere
|
||||
vec2 rayRadiusUV = 0.5 * R * FocalLen / -P.z;
|
||||
float rayRadiusPix = rayRadiusUV.x * AORes.x;
|
||||
// Calculate the projected size of the hemisphere
|
||||
float w = P.z * projMatrix[2][3] + projMatrix[3][3];
|
||||
vec2 rayRadiusUV = (0.5 * R * vec2(projMatrix[0][0], projMatrix[1][1]) / w); // [-1,1] -> [0,1] uv
|
||||
float rayRadiusPix = rayRadiusUV.x * renderTargetRes.x;
|
||||
|
||||
float ao = 1.0;
|
||||
|
||||
// Make sure the radius of the evaluated hemisphere is more than a pixel
|
||||
if(rayRadiusPix > 1.0){
|
||||
ao = 0.0;
|
||||
float numSteps;
|
||||
vec2 stepSizeUV;
|
||||
if(rayRadiusPix > 1.0) {
|
||||
ao = 0.0;
|
||||
float numSteps;
|
||||
vec2 stepSizeUV;
|
||||
|
||||
// Compute the number of steps
|
||||
ComputeSteps(stepSizeUV, numSteps, rayRadiusPix, random.z);
|
||||
// Compute the number of steps
|
||||
ComputeSteps(stepSizeUV, numSteps, rayRadiusPix, random.z);
|
||||
|
||||
float alpha = 2.0 * PI / numDirections;
|
||||
float alpha = 2.0 * PI / numDirections;
|
||||
|
||||
// Calculate the horizon occlusion of each direction
|
||||
for(float d = 0; d < numDirections; ++d){
|
||||
float theta = alpha * d;
|
||||
// Calculate the horizon occlusion of each direction
|
||||
for(float d = 0; d < numDirections; ++d) {
|
||||
float theta = alpha * d;
|
||||
|
||||
// Apply noise to the direction
|
||||
vec2 dir = RotateDirections(vec2(cos(theta), sin(theta)), random.xy);
|
||||
vec2 deltaUV = dir * stepSizeUV;
|
||||
// Apply noise to the direction
|
||||
vec2 dir = RotateDirections(vec2(cos(theta), sin(theta)), random.xy);
|
||||
vec2 deltaUV = dir * stepSizeUV;
|
||||
|
||||
// Sample the pixels along the direction
|
||||
ao += HorizonOcclusion( deltaUV,
|
||||
P,
|
||||
dPdu,
|
||||
dPdv,
|
||||
random.z,
|
||||
numSteps);
|
||||
}
|
||||
// Sample the pixels along the direction
|
||||
ao += HorizonOcclusion( deltaUV,
|
||||
P,
|
||||
dPdu,
|
||||
dPdv,
|
||||
random.z,
|
||||
numSteps);
|
||||
}
|
||||
|
||||
// Average the results and produce the final AO
|
||||
ao = 1.0 - ao / numDirections * AOStrength;
|
||||
}
|
||||
|
||||
// Average the results and produce the final AO
|
||||
ao = 1.0 - ao / numDirections * AOStrength;
|
||||
}
|
||||
|
||||
outFragColor = vec4(vec3(ao), 1.0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ set(TARGET_NAME script-engine)
|
|||
setup_memory_debugger()
|
||||
|
||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||
setup_hifi_library(Gui Network Script Widgets)
|
||||
setup_hifi_library(Gui Network Script WebSockets Widgets)
|
||||
|
||||
add_dependency_external_projects(glm)
|
||||
find_package(GLM REQUIRED)
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "ScriptEngine.h"
|
||||
#include "TypedArrays.h"
|
||||
#include "XMLHttpRequestClass.h"
|
||||
#include "WebSocketClass.h"
|
||||
|
||||
#include "SceneScriptingInterface.h"
|
||||
|
||||
|
@ -343,6 +344,9 @@ void ScriptEngine::init() {
|
|||
QScriptValue xmlHttpRequestConstructorValue = newFunction(XMLHttpRequestClass::constructor);
|
||||
globalObject().setProperty("XMLHttpRequest", xmlHttpRequestConstructorValue);
|
||||
|
||||
QScriptValue webSocketConstructorValue = newFunction(WebSocketClass::constructor);
|
||||
globalObject().setProperty("WebSocket", webSocketConstructorValue);
|
||||
|
||||
QScriptValue printConstructorValue = newFunction(debugPrint);
|
||||
globalObject().setProperty("print", printConstructorValue);
|
||||
|
||||
|
@ -353,6 +357,9 @@ void ScriptEngine::init() {
|
|||
qScriptRegisterMetaType(this, inputControllerToScriptValue, inputControllerFromScriptValue);
|
||||
qScriptRegisterMetaType(this, avatarDataToScriptValue, avatarDataFromScriptValue);
|
||||
qScriptRegisterMetaType(this, animationDetailsToScriptValue, animationDetailsFromScriptValue);
|
||||
qScriptRegisterMetaType(this, webSocketToScriptValue, webSocketFromScriptValue);
|
||||
qScriptRegisterMetaType(this, qWSCloseCodeToScriptValue, qWSCloseCodeFromScriptValue);
|
||||
qScriptRegisterMetaType(this, wscReadyStateToScriptValue, wscReadyStateFromScriptValue);
|
||||
|
||||
registerGlobalObject("Script", this);
|
||||
registerGlobalObject("Audio", &AudioScriptingInterface::getInstance());
|
||||
|
|
127
libraries/script-engine/src/WebSocketClass.cpp
Normal file
127
libraries/script-engine/src/WebSocketClass.cpp
Normal file
|
@ -0,0 +1,127 @@
|
|||
//
|
||||
// WebSocketClass.cpp
|
||||
// libraries/script-engine/src/
|
||||
//
|
||||
// Created by Thijs Wenker on 8/4/15.
|
||||
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// This class is an implementation of the WebSocket object for scripting use. It provides a near-complete implementation
|
||||
// of the class described in the Mozilla docs: https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ScriptEngine.h"
|
||||
#include "WebSocketClass.h"
|
||||
|
||||
WebSocketClass::WebSocketClass(QScriptEngine* engine, QString url) :
|
||||
_engine(engine),
|
||||
_webSocket(new QWebSocket())
|
||||
{
|
||||
initialize();
|
||||
_webSocket->open(url);
|
||||
}
|
||||
|
||||
WebSocketClass::WebSocketClass(QScriptEngine* engine, QWebSocket* qWebSocket) :
|
||||
_engine(engine),
|
||||
_webSocket(qWebSocket)
|
||||
{
|
||||
initialize();
|
||||
}
|
||||
|
||||
void WebSocketClass::initialize() {
|
||||
connect(_webSocket, &QWebSocket::disconnected, this, &WebSocketClass::handleOnClose);
|
||||
connect(_webSocket, &QWebSocket::textMessageReceived, this, &WebSocketClass::handleOnMessage);
|
||||
connect(_webSocket, &QWebSocket::connected, this, &WebSocketClass::handleOnOpen);
|
||||
connect(_webSocket, static_cast<void(QWebSocket::*)(QAbstractSocket::SocketError)>(&QWebSocket::error), this,
|
||||
&WebSocketClass::handleOnError);
|
||||
_binaryType = QStringLiteral("blob");
|
||||
}
|
||||
|
||||
QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine) {
|
||||
QString url;
|
||||
if (context->argumentCount() > 0) {
|
||||
url = context->argument(0).toString();
|
||||
}
|
||||
return engine->newQObject(new WebSocketClass(engine, url), QScriptEngine::ScriptOwnership);
|
||||
}
|
||||
|
||||
WebSocketClass::~WebSocketClass() {
|
||||
_webSocket->deleteLater();
|
||||
}
|
||||
|
||||
void WebSocketClass::send(QScriptValue message) {
|
||||
_webSocket->sendTextMessage(message.toString());
|
||||
}
|
||||
|
||||
void WebSocketClass::close() {
|
||||
this->close(QWebSocketProtocol::CloseCodeNormal);
|
||||
}
|
||||
|
||||
void WebSocketClass::close(QWebSocketProtocol::CloseCode closeCode) {
|
||||
this->close(closeCode, QStringLiteral(""));
|
||||
}
|
||||
|
||||
void WebSocketClass::close(QWebSocketProtocol::CloseCode closeCode, QString reason) {
|
||||
_webSocket->close(closeCode, reason);
|
||||
}
|
||||
|
||||
void WebSocketClass::handleOnClose() {
|
||||
bool hasError = (_webSocket->error() != QAbstractSocket::UnknownSocketError);
|
||||
if (_onCloseEvent.isFunction()) {
|
||||
QScriptValueList args;
|
||||
QScriptValue arg = _engine->newObject();
|
||||
arg.setProperty("code", hasError ? QWebSocketProtocol::CloseCodeAbnormalDisconnection : _webSocket->closeCode());
|
||||
arg.setProperty("reason", _webSocket->closeReason());
|
||||
arg.setProperty("wasClean", !hasError);
|
||||
args << arg;
|
||||
_onCloseEvent.call(QScriptValue(), args);
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketClass::handleOnError(QAbstractSocket::SocketError error) {
|
||||
if (_onErrorEvent.isFunction()) {
|
||||
_onErrorEvent.call();
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketClass::handleOnMessage(const QString& message) {
|
||||
if (_onMessageEvent.isFunction()) {
|
||||
QScriptValueList args;
|
||||
QScriptValue arg = _engine->newObject();
|
||||
arg.setProperty("data", message);
|
||||
args << arg;
|
||||
_onMessageEvent.call(QScriptValue(), args);
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketClass::handleOnOpen() {
|
||||
if (_onOpenEvent.isFunction()) {
|
||||
_onOpenEvent.call();
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue qWSCloseCodeToScriptValue(QScriptEngine* engine, const QWebSocketProtocol::CloseCode &closeCode) {
|
||||
return closeCode;
|
||||
}
|
||||
|
||||
void qWSCloseCodeFromScriptValue(const QScriptValue &object, QWebSocketProtocol::CloseCode &closeCode) {
|
||||
closeCode = (QWebSocketProtocol::CloseCode)object.toUInt16();
|
||||
}
|
||||
|
||||
QScriptValue webSocketToScriptValue(QScriptEngine* engine, WebSocketClass* const &in) {
|
||||
return engine->newQObject(in, QScriptEngine::ScriptOwnership);
|
||||
}
|
||||
|
||||
void webSocketFromScriptValue(const QScriptValue &object, WebSocketClass* &out) {
|
||||
out = qobject_cast<WebSocketClass*>(object.toQObject());
|
||||
}
|
||||
|
||||
QScriptValue wscReadyStateToScriptValue(QScriptEngine* engine, const WebSocketClass::ReadyState& readyState) {
|
||||
return readyState;
|
||||
}
|
||||
|
||||
void wscReadyStateFromScriptValue(const QScriptValue& object, WebSocketClass::ReadyState& readyState) {
|
||||
readyState = (WebSocketClass::ReadyState)object.toUInt16();
|
||||
}
|
140
libraries/script-engine/src/WebSocketClass.h
Normal file
140
libraries/script-engine/src/WebSocketClass.h
Normal file
|
@ -0,0 +1,140 @@
|
|||
//
|
||||
// WebSocketClass.h
|
||||
// libraries/script-engine/src/
|
||||
//
|
||||
// Created by Thijs Wenker on 8/4/15.
|
||||
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_WebSocketClass_h
|
||||
#define hifi_WebSocketClass_h
|
||||
|
||||
#include <QObject>
|
||||
#include <QScriptEngine>
|
||||
#include <QWebSocket>
|
||||
|
||||
class WebSocketClass : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString binaryType READ getBinaryType WRITE setBinaryType)
|
||||
Q_PROPERTY(ulong bufferedAmount READ getBufferedAmount)
|
||||
Q_PROPERTY(QString extensions READ getExtensions)
|
||||
|
||||
Q_PROPERTY(QScriptValue onclose READ getOnClose WRITE setOnClose)
|
||||
Q_PROPERTY(QScriptValue onerror READ getOnError WRITE setOnError)
|
||||
Q_PROPERTY(QScriptValue onmessage READ getOnMessage WRITE setOnMessage)
|
||||
Q_PROPERTY(QScriptValue onopen READ getOnOpen WRITE setOnOpen)
|
||||
|
||||
Q_PROPERTY(QString protocol READ getProtocol)
|
||||
Q_PROPERTY(WebSocketClass::ReadyState readyState READ getReadyState)
|
||||
Q_PROPERTY(QString url READ getURL)
|
||||
|
||||
Q_PROPERTY(WebSocketClass::ReadyState CONNECTING READ getConnecting CONSTANT)
|
||||
Q_PROPERTY(WebSocketClass::ReadyState OPEN READ getOpen CONSTANT)
|
||||
Q_PROPERTY(WebSocketClass::ReadyState CLOSING READ getClosing CONSTANT)
|
||||
Q_PROPERTY(WebSocketClass::ReadyState CLOSED READ getClosed CONSTANT)
|
||||
|
||||
public:
|
||||
WebSocketClass(QScriptEngine* engine, QString url);
|
||||
WebSocketClass(QScriptEngine* engine, QWebSocket* qWebSocket);
|
||||
~WebSocketClass();
|
||||
|
||||
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
enum ReadyState {
|
||||
CONNECTING = 0,
|
||||
OPEN,
|
||||
CLOSING,
|
||||
CLOSED
|
||||
};
|
||||
|
||||
QWebSocket* getWebSocket() { return _webSocket; }
|
||||
|
||||
ReadyState getConnecting() const { return CONNECTING; };
|
||||
ReadyState getOpen() const { return OPEN; };
|
||||
ReadyState getClosing() const { return CLOSING; };
|
||||
ReadyState getClosed() const { return CLOSED; };
|
||||
|
||||
void setBinaryType(QString binaryType) { _binaryType = binaryType; }
|
||||
QString getBinaryType() { return _binaryType; }
|
||||
|
||||
// extensions is a empty string until supported in QT WebSockets
|
||||
QString getExtensions() { return QString(); }
|
||||
|
||||
// protocol is a empty string until supported in QT WebSockets
|
||||
QString getProtocol() { return QString(); }
|
||||
|
||||
ulong getBufferedAmount() { return 0; }
|
||||
|
||||
QString getURL() { return _webSocket->requestUrl().toDisplayString(); }
|
||||
|
||||
ReadyState getReadyState() {
|
||||
switch (_webSocket->state()) {
|
||||
case QAbstractSocket::SocketState::HostLookupState:
|
||||
case QAbstractSocket::SocketState::ConnectingState:
|
||||
return CONNECTING;
|
||||
case QAbstractSocket::SocketState::ConnectedState:
|
||||
case QAbstractSocket::SocketState::BoundState:
|
||||
case QAbstractSocket::SocketState::ListeningState:
|
||||
return OPEN;
|
||||
case QAbstractSocket::SocketState::ClosingState:
|
||||
return CLOSING;
|
||||
}
|
||||
return CLOSED;
|
||||
}
|
||||
|
||||
void setOnClose(QScriptValue eventFunction) { _onCloseEvent = eventFunction; }
|
||||
QScriptValue getOnClose() { return _onCloseEvent; }
|
||||
|
||||
void setOnError(QScriptValue eventFunction) { _onErrorEvent = eventFunction; }
|
||||
QScriptValue getOnError() { return _onErrorEvent; }
|
||||
|
||||
void setOnMessage(QScriptValue eventFunction) { _onMessageEvent = eventFunction; }
|
||||
QScriptValue getOnMessage() { return _onMessageEvent; }
|
||||
|
||||
void setOnOpen(QScriptValue eventFunction) { _onOpenEvent = eventFunction; }
|
||||
QScriptValue getOnOpen() { return _onOpenEvent; }
|
||||
|
||||
public slots:
|
||||
void send(QScriptValue message);
|
||||
|
||||
void close();
|
||||
void close(QWebSocketProtocol::CloseCode closeCode);
|
||||
void close(QWebSocketProtocol::CloseCode closeCode, QString reason);
|
||||
|
||||
private:
|
||||
QWebSocket* _webSocket;
|
||||
QScriptEngine* _engine;
|
||||
|
||||
QScriptValue _onCloseEvent;
|
||||
QScriptValue _onErrorEvent;
|
||||
QScriptValue _onMessageEvent;
|
||||
QScriptValue _onOpenEvent;
|
||||
|
||||
QString _binaryType;
|
||||
|
||||
void initialize();
|
||||
|
||||
private slots:
|
||||
void handleOnClose();
|
||||
void handleOnError(QAbstractSocket::SocketError error);
|
||||
void handleOnMessage(const QString& message);
|
||||
void handleOnOpen();
|
||||
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(QWebSocketProtocol::CloseCode);
|
||||
Q_DECLARE_METATYPE(WebSocketClass::ReadyState);
|
||||
|
||||
QScriptValue qWSCloseCodeToScriptValue(QScriptEngine* engine, const QWebSocketProtocol::CloseCode& closeCode);
|
||||
void qWSCloseCodeFromScriptValue(const QScriptValue& object, QWebSocketProtocol::CloseCode& closeCode);
|
||||
|
||||
QScriptValue webSocketToScriptValue(QScriptEngine* engine, WebSocketClass* const &in);
|
||||
void webSocketFromScriptValue(const QScriptValue &object, WebSocketClass* &out);
|
||||
|
||||
QScriptValue wscReadyStateToScriptValue(QScriptEngine* engine, const WebSocketClass::ReadyState& readyState);
|
||||
void wscReadyStateFromScriptValue(const QScriptValue& object, WebSocketClass::ReadyState& readyState);
|
||||
|
||||
#endif // hifi_WebSocketClass_h
|
69
libraries/script-engine/src/WebSocketServerClass.cpp
Normal file
69
libraries/script-engine/src/WebSocketServerClass.cpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
//
|
||||
// WebSocketServerClass.cpp
|
||||
// libraries/script-engine/src/
|
||||
//
|
||||
// Created by Thijs Wenker on 8/10/15.
|
||||
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Making WebSocketServer accessible through scripting.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ScriptEngine.h"
|
||||
#include "WebSocketServerClass.h"
|
||||
|
||||
WebSocketServerClass::WebSocketServerClass(QScriptEngine* engine, const QString& serverName, const quint16 port) :
|
||||
_engine(engine),
|
||||
_webSocketServer(serverName, QWebSocketServer::SslMode::NonSecureMode)
|
||||
{
|
||||
if (_webSocketServer.listen(QHostAddress::Any, port)) {
|
||||
connect(&_webSocketServer, &QWebSocketServer::newConnection, this, &WebSocketServerClass::onNewConnection);
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue WebSocketServerClass::constructor(QScriptContext* context, QScriptEngine* engine) {
|
||||
// the serverName is used in handshakes
|
||||
QString serverName = QStringLiteral("HighFidelity - Scripted WebSocket Listener");
|
||||
// port 0 will auto-assign a free port
|
||||
quint16 port = 0;
|
||||
QScriptValue callee = context->callee();
|
||||
if (context->argumentCount() > 0) {
|
||||
QScriptValue options = context->argument(0);
|
||||
QScriptValue portOption = options.property(QStringLiteral("port"));
|
||||
if (portOption.isValid() && portOption.isNumber()) {
|
||||
port = portOption.toNumber();
|
||||
}
|
||||
QScriptValue serverNameOption = options.property(QStringLiteral("serverName"));
|
||||
if (serverNameOption.isValid() && serverNameOption.isString()) {
|
||||
serverName = serverNameOption.toString();
|
||||
}
|
||||
}
|
||||
return engine->newQObject(new WebSocketServerClass(engine, serverName, port), QScriptEngine::ScriptOwnership);
|
||||
}
|
||||
|
||||
WebSocketServerClass::~WebSocketServerClass() {
|
||||
if (_webSocketServer.isListening()) {
|
||||
close();
|
||||
}
|
||||
_clients.empty();
|
||||
}
|
||||
|
||||
void WebSocketServerClass::onNewConnection() {
|
||||
WebSocketClass* newClient = new WebSocketClass(_engine, _webSocketServer.nextPendingConnection());
|
||||
_clients << newClient;
|
||||
connect(newClient->getWebSocket(), &QWebSocket::disconnected, [newClient, this]() {
|
||||
_clients.removeOne(newClient);
|
||||
});
|
||||
emit newConnection(newClient);
|
||||
}
|
||||
|
||||
void WebSocketServerClass::close() {
|
||||
foreach(WebSocketClass* client, _clients) {
|
||||
if (client->getReadyState() != WebSocketClass::ReadyState::CLOSED) {
|
||||
client->close(QWebSocketProtocol::CloseCode::CloseCodeGoingAway, "Server closing.");
|
||||
}
|
||||
}
|
||||
_webSocketServer.close();
|
||||
}
|
52
libraries/script-engine/src/WebSocketServerClass.h
Normal file
52
libraries/script-engine/src/WebSocketServerClass.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// WebSocketServerClass.h
|
||||
// libraries/script-engine/src/
|
||||
//
|
||||
// Created by Thijs Wenker on 8/10/15.
|
||||
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_WebSocketServerClass_h
|
||||
#define hifi_WebSocketServerClass_h
|
||||
|
||||
#include <QObject>
|
||||
#include <QScriptEngine>
|
||||
#include <QWebSocketServer>
|
||||
#include "WebSocketClass.h"
|
||||
|
||||
class WebSocketServerClass : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString url READ getURL)
|
||||
Q_PROPERTY(quint16 port READ getPort)
|
||||
Q_PROPERTY(bool listening READ isListening)
|
||||
|
||||
public:
|
||||
WebSocketServerClass(QScriptEngine* engine, const QString& serverName, const quint16 port);
|
||||
~WebSocketServerClass();
|
||||
|
||||
QString getURL() { return _webSocketServer.serverUrl().toDisplayString(); }
|
||||
quint16 getPort() { return _webSocketServer.serverPort(); }
|
||||
bool isListening() { return _webSocketServer.isListening(); }
|
||||
|
||||
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
public slots:
|
||||
void close();
|
||||
|
||||
private:
|
||||
QWebSocketServer _webSocketServer;
|
||||
QScriptEngine* _engine;
|
||||
QList<WebSocketClass*> _clients;
|
||||
|
||||
private slots:
|
||||
void onNewConnection();
|
||||
|
||||
signals:
|
||||
void newConnection(WebSocketClass* client);
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_WebSocketServerClass_h
|
Loading…
Reference in a new issue