mirror of
https://github.com/overte-org/overte.git
synced 2025-07-23 10:44:31 +02:00
Merge branch '20660' of github.com:thoys/hifi into 20660
This commit is contained in:
commit
8d8a9535a0
81 changed files with 2174 additions and 885 deletions
|
@ -1,6 +1,6 @@
|
||||||
set(TARGET_NAME assignment-client)
|
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)
|
add_dependency_external_projects(glm)
|
||||||
find_package(GLM REQUIRED)
|
find_package(GLM REQUIRED)
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <SoundCache.h>
|
#include <SoundCache.h>
|
||||||
#include <UUID.h>
|
#include <UUID.h>
|
||||||
|
|
||||||
|
#include <WebSocketServerClass.h>
|
||||||
#include <EntityScriptingInterface.h> // TODO: consider moving to scriptengine.h
|
#include <EntityScriptingInterface.h> // TODO: consider moving to scriptengine.h
|
||||||
|
|
||||||
#include "avatars/ScriptableAvatar.h"
|
#include "avatars/ScriptableAvatar.h"
|
||||||
|
@ -180,10 +181,17 @@ void Agent::run() {
|
||||||
// register ourselves to the script engine
|
// register ourselves to the script engine
|
||||||
_scriptEngine.registerGlobalObject("Agent", this);
|
_scriptEngine.registerGlobalObject("Agent", this);
|
||||||
|
|
||||||
|
if (!_payload.isEmpty()) {
|
||||||
|
_scriptEngine.setParentURL(_payload);
|
||||||
|
}
|
||||||
|
|
||||||
_scriptEngine.init(); // must be done before we set up the viewers
|
_scriptEngine.init(); // must be done before we set up the viewers
|
||||||
|
|
||||||
_scriptEngine.registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
|
_scriptEngine.registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||||
|
|
||||||
|
QScriptValue webSocketServerConstructorValue = _scriptEngine.newFunction(WebSocketServerClass::constructor);
|
||||||
|
_scriptEngine.globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue);
|
||||||
|
|
||||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||||
|
|
||||||
_scriptEngine.registerGlobalObject("EntityViewer", &_entityViewer);
|
_scriptEngine.registerGlobalObject("EntityViewer", &_entityViewer);
|
||||||
|
|
15
cmake/externals/LibOVR/CMakeLists.txt
vendored
15
cmake/externals/LibOVR/CMakeLists.txt
vendored
|
@ -5,6 +5,21 @@ set(EXTERNAL_NAME LibOVR)
|
||||||
|
|
||||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||||
|
|
||||||
|
# These are all provided in order to allow easier testing of both
|
||||||
|
# the legacy display plugin and the new windows only plugin on
|
||||||
|
# various versions of the SDK, all on windows
|
||||||
|
#
|
||||||
|
# 0.5 public
|
||||||
|
# URL http://static.oculus.com/sdk-downloads/ovr_sdk_win_0.5.0.1.zip
|
||||||
|
# URL_MD5 d3fc4c02db9be5ff08af4ef4c97b32f9
|
||||||
|
# 0.6 public
|
||||||
|
# URL http://static.oculus.com/sdk-downloads/0.6.0.1/Public/1435190862/ovr_sdk_win_0.6.0.1.zip
|
||||||
|
# URL_MD5 4b3ef825f9a1d6d3035c9f6820687da9
|
||||||
|
# 0.7 alpha
|
||||||
|
# URL https://s3.amazonaws.com/static.oculus.com/sdk-downloads/0.7.0.0/Public/Alpha/ovr_sdk_win_0.7.0.0_RC1.zip
|
||||||
|
# URL_MD5 a562bb9d117087b2cf9d86653ea70fd8
|
||||||
|
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
|
|
||||||
ExternalProject_Add(
|
ExternalProject_Add(
|
||||||
|
|
|
@ -11,17 +11,14 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// 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);
|
print("Running test: " + name);
|
||||||
|
var unitTest = new UnitTest(name, func, timeout);
|
||||||
var unitTest = new UnitTest(name, func);
|
unitTest.run(function(unitTest) {
|
||||||
|
|
||||||
try {
|
|
||||||
unitTest.run();
|
|
||||||
print(" Success: " + unitTest.numAssertions + " assertions passed");
|
print(" Success: " + unitTest.numAssertions + " assertions passed");
|
||||||
} catch (error) {
|
}, function(unitTest, error) {
|
||||||
print(" Failure: " + error.name + " " + error.message);
|
print(" Failure: " + error.name + " " + error.message);
|
||||||
}
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
AssertionException = function(expected, actual, message) {
|
AssertionException = function(expected, actual, message) {
|
||||||
|
@ -36,13 +33,86 @@ UnthrownException = function(message) {
|
||||||
this.name = 'UnthrownException';
|
this.name = 'UnthrownException';
|
||||||
};
|
};
|
||||||
|
|
||||||
UnitTest = function(name, func) {
|
TimeoutException = function() {
|
||||||
this.numAssertions = 0;
|
print("Creating exception");
|
||||||
this.func = func;
|
this.message = "UnitTest timed out\n";
|
||||||
|
this.name = 'TimeoutException';
|
||||||
};
|
};
|
||||||
|
|
||||||
UnitTest.prototype.run = function() {
|
SequentialUnitTester = function() {
|
||||||
this.func();
|
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) {
|
UnitTest.prototype.assertNotEquals = function(expected, actual, message) {
|
||||||
|
@ -83,7 +153,7 @@ UnitTest.prototype.assertNull = function(value, message) {
|
||||||
UnitTest.prototype.arrayEqual = function(array1, array2, message) {
|
UnitTest.prototype.arrayEqual = function(array1, array2, message) {
|
||||||
this.numAssertions++;
|
this.numAssertions++;
|
||||||
if (array1.length !== array2.length) {
|
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) {
|
for (var i = 0; i < array1.length; ++i) {
|
||||||
if (array1[i] !== array2[i]) {
|
if (array1[i] !== array2[i]) {
|
||||||
|
@ -101,4 +171,11 @@ UnitTest.prototype.raises = function(func, message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new UnthrownException(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})
|
list(REMOVE_ITEM INTERFACE_SRCS ${SPEECHRECOGNIZER_CPP})
|
||||||
endif ()
|
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
|
# grab the ui files in resources/ui
|
||||||
file (GLOB_RECURSE QT_UI_FILES ui/*.ui)
|
file (GLOB_RECURSE QT_UI_FILES ui/*.ui)
|
||||||
|
|
|
@ -58,6 +58,8 @@
|
||||||
#include <AutoUpdater.h>
|
#include <AutoUpdater.h>
|
||||||
#include <DeferredLightingEffect.h>
|
#include <DeferredLightingEffect.h>
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
|
#include <plugins/PluginContainer.h>
|
||||||
|
#include <plugins/PluginManager.h>
|
||||||
#include <display-plugins/DisplayPlugin.h>
|
#include <display-plugins/DisplayPlugin.h>
|
||||||
|
|
||||||
#include <EntityScriptingInterface.h>
|
#include <EntityScriptingInterface.h>
|
||||||
|
@ -148,6 +150,8 @@
|
||||||
|
|
||||||
#include "ui/overlays/Cube3DOverlay.h"
|
#include "ui/overlays/Cube3DOverlay.h"
|
||||||
|
|
||||||
|
#include "PluginContainerProxy.h"
|
||||||
|
|
||||||
// ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
// ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
||||||
// FIXME seems to be broken.
|
// FIXME seems to be broken.
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
|
@ -301,7 +305,7 @@ bool setupEssentials(int& argc, char** argv) {
|
||||||
// continuing to overburden Application.cpp
|
// continuing to overburden Application.cpp
|
||||||
Cube3DOverlay* _keyboardFocusHighlight{ nullptr };
|
Cube3DOverlay* _keyboardFocusHighlight{ nullptr };
|
||||||
int _keyboardFocusHighlightID{ -1 };
|
int _keyboardFocusHighlightID{ -1 };
|
||||||
|
PluginContainer* _pluginContainer;
|
||||||
|
|
||||||
Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||||
QApplication(argc, argv),
|
QApplication(argc, argv),
|
||||||
|
@ -351,7 +355,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||||
_applicationOverlay()
|
_applicationOverlay()
|
||||||
{
|
{
|
||||||
setInstance(this);
|
setInstance(this);
|
||||||
Plugin::setContainer(this);
|
|
||||||
|
_pluginContainer = new PluginContainerProxy();
|
||||||
|
Plugin::setContainer(_pluginContainer);
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
installNativeEventFilter(&MyNativeEventFilter::getInstance());
|
installNativeEventFilter(&MyNativeEventFilter::getInstance());
|
||||||
#endif
|
#endif
|
||||||
|
@ -995,6 +1001,8 @@ void Application::paintGL() {
|
||||||
auto displayPlugin = getActiveDisplayPlugin();
|
auto displayPlugin = getActiveDisplayPlugin();
|
||||||
displayPlugin->preRender();
|
displayPlugin->preRender();
|
||||||
_offscreenContext->makeCurrent();
|
_offscreenContext->makeCurrent();
|
||||||
|
// update the avatar with a fresh HMD pose
|
||||||
|
_myAvatar->updateFromHMDSensorMatrix(getHMDSensorPose());
|
||||||
|
|
||||||
auto lodManager = DependencyManager::get<LODManager>();
|
auto lodManager = DependencyManager::get<LODManager>();
|
||||||
|
|
||||||
|
@ -1091,7 +1099,6 @@ void Application::paintGL() {
|
||||||
glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror);
|
glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror);
|
||||||
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
|
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update camera position
|
// Update camera position
|
||||||
if (!isHMDMode()) {
|
if (!isHMDMode()) {
|
||||||
_myCamera.update(1.0f / _fps);
|
_myCamera.update(1.0f / _fps);
|
||||||
|
@ -1100,64 +1107,46 @@ void Application::paintGL() {
|
||||||
|
|
||||||
// Primary rendering pass
|
// Primary rendering pass
|
||||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||||
QSize size = framebufferCache->getFrameBufferSize();
|
const QSize size = framebufferCache->getFrameBufferSize();
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(__FUNCTION__ "/mainRender");
|
PROFILE_RANGE(__FUNCTION__ "/mainRender");
|
||||||
// Viewport is assigned to the size of the framebuffer
|
// Viewport is assigned to the size of the framebuffer
|
||||||
QSize size = DependencyManager::get<FramebufferCache>()->getFrameBufferSize();
|
renderArgs._viewport = ivec4(0, 0, size.width(), size.height());
|
||||||
renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height());
|
|
||||||
|
|
||||||
{
|
|
||||||
PROFILE_RANGE(__FUNCTION__ "/clear");
|
|
||||||
doInBatch(&renderArgs, [&](gpu::Batch& batch) {
|
|
||||||
auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebuffer();
|
|
||||||
batch.setFramebuffer(primaryFbo);
|
|
||||||
// clear the normal and specular buffers
|
|
||||||
batch.clearFramebuffer(
|
|
||||||
gpu::Framebuffer::BUFFER_COLOR0 |
|
|
||||||
gpu::Framebuffer::BUFFER_COLOR1 |
|
|
||||||
gpu::Framebuffer::BUFFER_COLOR2 |
|
|
||||||
gpu::Framebuffer::BUFFER_DEPTH,
|
|
||||||
vec4(vec3(0), 1), 1.0, 0.0);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (displayPlugin->isStereo()) {
|
if (displayPlugin->isStereo()) {
|
||||||
PROFILE_RANGE(__FUNCTION__ "/stereoRender");
|
// Stereo modes will typically have a larger projection matrix overall,
|
||||||
QRect currentViewport(QPoint(0, 0), QSize(size.width() / 2, size.height()));
|
// so we ask for the 'mono' projection matrix, which for stereo and HMD
|
||||||
glEnable(GL_SCISSOR_TEST);
|
// plugins will imply the combined projection for both eyes.
|
||||||
for_each_eye([&](Eye eye){
|
//
|
||||||
// Load the view frustum, used by meshes
|
// This is properly implemented for the Oculus plugins, but for OpenVR
|
||||||
Camera eyeCamera;
|
// and Stereo displays I'm not sure how to get / calculate it, so we're
|
||||||
if (qApp->isHMDMode()) {
|
// just relying on the left FOV in each case and hoping that the
|
||||||
// Allow the displayPlugin to compose the final eye transform, based on the most up-to-date head motion.
|
// overall culling margin of error doesn't cause popping in the
|
||||||
eyeCamera.setTransform(displayPlugin->getModelview(eye, _myAvatar->getSensorToWorldMatrix()));
|
// right eye. There are FIXMEs in the relevant plugins
|
||||||
} else {
|
_myCamera.setProjection(displayPlugin->getProjection(Mono, _myCamera.getProjection()));
|
||||||
eyeCamera.setTransform(displayPlugin->getModelview(eye, _myCamera.getTransform()));
|
renderArgs._context->enableStereo(true);
|
||||||
}
|
mat4 eyeViews[2];
|
||||||
eyeCamera.setProjection(displayPlugin->getProjection(eye, _myCamera.getProjection()));
|
mat4 eyeProjections[2];
|
||||||
renderArgs._viewport = toGlm(currentViewport);
|
auto baseProjection = renderArgs._viewFrustum->getProjection();
|
||||||
doInBatch(&renderArgs, [&](gpu::Batch& batch) {
|
// FIXME we probably don't need to set the projection matrix every frame,
|
||||||
batch.setViewportTransform(renderArgs._viewport);
|
// only when the display plugin changes (or in non-HMD modes when the user
|
||||||
batch.setStateScissorRect(renderArgs._viewport);
|
// changes the FOV manually, which right now I don't think they can.
|
||||||
});
|
for_each_eye([&](Eye eye) {
|
||||||
displaySide(&renderArgs, eyeCamera);
|
// For providing the stereo eye views, the HMD head pose has already been
|
||||||
}, [&] {
|
// applied to the avatar, so we need to get the difference between the head
|
||||||
currentViewport.moveLeft(currentViewport.width());
|
// pose applied to the avatar and the per eye pose, and use THAT as
|
||||||
|
// the per-eye stereo matrix adjustment.
|
||||||
|
mat4 eyePose = displayPlugin->getEyePose(eye);
|
||||||
|
mat4 headPose = displayPlugin->getHeadPose();
|
||||||
|
mat4 eyeView = glm::inverse(eyePose) * headPose;
|
||||||
|
eyeViews[eye] = eyeView;
|
||||||
|
eyeProjections[eye] = displayPlugin->getProjection(eye, baseProjection);
|
||||||
});
|
});
|
||||||
glDisable(GL_SCISSOR_TEST);
|
renderArgs._context->setStereoProjections(eyeProjections);
|
||||||
} else {
|
renderArgs._context->setStereoViews(eyeViews);
|
||||||
PROFILE_RANGE(__FUNCTION__ "/monoRender");
|
|
||||||
renderArgs._viewport = gpu::Vec4i(0, 0, size.width(), size.height());
|
|
||||||
// Viewport is assigned to the size of the framebuffer
|
|
||||||
doInBatch(&renderArgs, [&](gpu::Batch& batch) {
|
|
||||||
batch.setViewportTransform(renderArgs._viewport);
|
|
||||||
batch.setStateScissorRect(renderArgs._viewport);
|
|
||||||
});
|
|
||||||
displaySide(&renderArgs, _myCamera);
|
|
||||||
}
|
}
|
||||||
|
displaySide(&renderArgs, _myCamera);
|
||||||
doInBatch(&renderArgs, [](gpu::Batch& batch){
|
renderArgs._context->enableStereo(false);
|
||||||
|
doInBatch(&renderArgs, [](gpu::Batch& batch) {
|
||||||
batch.setFramebuffer(nullptr);
|
batch.setFramebuffer(nullptr);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1191,7 +1180,6 @@ void Application::paintGL() {
|
||||||
PROFILE_RANGE(__FUNCTION__ "/pluginOutput");
|
PROFILE_RANGE(__FUNCTION__ "/pluginOutput");
|
||||||
auto primaryFbo = framebufferCache->getPrimaryFramebuffer();
|
auto primaryFbo = framebufferCache->getPrimaryFramebuffer();
|
||||||
GLuint finalTexture = gpu::GLBackend::getTextureID(primaryFbo->getRenderBuffer(0));
|
GLuint finalTexture = gpu::GLBackend::getTextureID(primaryFbo->getRenderBuffer(0));
|
||||||
uvec2 finalSize = toGlm(size);
|
|
||||||
// Ensure the rendering context commands are completed when rendering
|
// Ensure the rendering context commands are completed when rendering
|
||||||
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||||
// Ensure the sync object is flushed to the driver thread before releasing the context
|
// Ensure the sync object is flushed to the driver thread before releasing the context
|
||||||
|
@ -1207,7 +1195,7 @@ void Application::paintGL() {
|
||||||
|
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(__FUNCTION__ "/pluginDisplay");
|
PROFILE_RANGE(__FUNCTION__ "/pluginDisplay");
|
||||||
displayPlugin->display(finalTexture, finalSize);
|
displayPlugin->display(finalTexture, toGlm(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -1280,6 +1268,7 @@ void Application::resizeGL() {
|
||||||
// Possible change in aspect ratio
|
// Possible change in aspect ratio
|
||||||
loadViewFrustum(_myCamera, _viewFrustum);
|
loadViewFrustum(_myCamera, _viewFrustum);
|
||||||
float fov = glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES);
|
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);
|
float aspectRatio = aspect(_renderResolution);
|
||||||
_myCamera.setProjection(glm::perspective(fov, aspectRatio, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP));
|
_myCamera.setProjection(glm::perspective(fov, aspectRatio, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP));
|
||||||
}
|
}
|
||||||
|
@ -1439,7 +1428,15 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
||||||
break;
|
break;
|
||||||
case Qt::Key_Enter:
|
case Qt::Key_Enter:
|
||||||
case Qt::Key_Return:
|
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;
|
break;
|
||||||
|
|
||||||
case Qt::Key_B:
|
case Qt::Key_B:
|
||||||
|
@ -2705,9 +2702,6 @@ void Application::update(float deltaTime) {
|
||||||
updateLOD();
|
updateLOD();
|
||||||
updateMouseRay(); // check what's under the mouse and update the mouse voxel
|
updateMouseRay(); // check what's under the mouse and update the mouse voxel
|
||||||
|
|
||||||
// update the avatar with a fresh HMD pose
|
|
||||||
_myAvatar->updateFromHMDSensorMatrix(getHMDSensorPose());
|
|
||||||
|
|
||||||
{
|
{
|
||||||
PerformanceTimer perfTimer("devices");
|
PerformanceTimer perfTimer("devices");
|
||||||
DeviceTracker::updateAll();
|
DeviceTracker::updateAll();
|
||||||
|
@ -4638,10 +4632,6 @@ void Application::checkSkeleton() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Application::isForeground() {
|
|
||||||
return _isForeground && !getWindow()->isMinimized();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::activeChanged(Qt::ApplicationState state) {
|
void Application::activeChanged(Qt::ApplicationState state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case Qt::ApplicationActive:
|
case Qt::ApplicationActive:
|
||||||
|
@ -4747,6 +4737,10 @@ const DisplayPlugin * Application::getActiveDisplayPlugin() const {
|
||||||
return ((Application*)this)->getActiveDisplayPlugin();
|
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) {
|
static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool active = false) {
|
||||||
auto menu = Menu::getInstance();
|
auto menu = Menu::getInstance();
|
||||||
|
@ -4768,9 +4762,6 @@ static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool acti
|
||||||
Q_ASSERT(menu->menuItemExists(MenuOption::OutputMenu, name));
|
Q_ASSERT(menu->menuItemExists(MenuOption::OutputMenu, name));
|
||||||
}
|
}
|
||||||
|
|
||||||
static QVector<QPair<QString, QString>> _currentDisplayPluginActions;
|
|
||||||
static bool _activatingDisplayPlugin{false};
|
|
||||||
|
|
||||||
void Application::updateDisplayMode() {
|
void Application::updateDisplayMode() {
|
||||||
auto menu = Menu::getInstance();
|
auto menu = Menu::getInstance();
|
||||||
auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins();
|
auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins();
|
||||||
|
@ -4837,7 +4828,7 @@ void Application::updateDisplayMode() {
|
||||||
// Only show the hmd tools after the correct plugin has
|
// Only show the hmd tools after the correct plugin has
|
||||||
// been activated so that it's UI is setup correctly
|
// been activated so that it's UI is setup correctly
|
||||||
if (newPluginWantsHMDTools) {
|
if (newPluginWantsHMDTools) {
|
||||||
showDisplayPluginsTools();
|
_pluginContainer->showDisplayPluginsTools();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldDisplayPlugin) {
|
if (oldDisplayPlugin) {
|
||||||
|
@ -4855,9 +4846,6 @@ void Application::updateDisplayMode() {
|
||||||
Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin");
|
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) {
|
static void addInputPluginToMenu(InputPluginPointer inputPlugin, bool active = false) {
|
||||||
auto menu = Menu::getInstance();
|
auto menu = Menu::getInstance();
|
||||||
QString name = inputPlugin->getName();
|
QString name = inputPlugin->getName();
|
||||||
|
@ -4931,42 +4919,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 {
|
mat4 Application::getEyeProjection(int eye) const {
|
||||||
if (isHMDMode()) {
|
if (isHMDMode()) {
|
||||||
return getActiveDisplayPlugin()->getProjection((Eye)eye, _viewFrustum.getProjection());
|
return getActiveDisplayPlugin()->getProjection((Eye)eye, _viewFrustum.getProjection());
|
||||||
|
@ -4986,7 +4938,7 @@ mat4 Application::getEyePose(int eye) const {
|
||||||
mat4 Application::getEyeOffset(int eye) const {
|
mat4 Application::getEyeOffset(int eye) const {
|
||||||
if (isHMDMode()) {
|
if (isHMDMode()) {
|
||||||
mat4 identity;
|
mat4 identity;
|
||||||
return getActiveDisplayPlugin()->getModelview((Eye)eye, identity);
|
return getActiveDisplayPlugin()->getView((Eye)eye, identity);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mat4();
|
return mat4();
|
||||||
|
@ -4999,84 +4951,6 @@ mat4 Application::getHMDSensorPose() const {
|
||||||
return mat4();
|
return mat4();
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
void Application::setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float deltaTime, int index) {
|
||||||
PalmData* palm;
|
PalmData* palm;
|
||||||
bool foundHand = false;
|
bool foundHand = false;
|
||||||
|
|
|
@ -26,18 +26,18 @@
|
||||||
#include <EntityEditPacketSender.h>
|
#include <EntityEditPacketSender.h>
|
||||||
#include <EntityTreeRenderer.h>
|
#include <EntityTreeRenderer.h>
|
||||||
#include <GeometryCache.h>
|
#include <GeometryCache.h>
|
||||||
|
#include <input-plugins/KeyboardMouseDevice.h>
|
||||||
#include <NodeList.h>
|
#include <NodeList.h>
|
||||||
#include <OctreeQuery.h>
|
#include <OctreeQuery.h>
|
||||||
#include <OffscreenUi.h>
|
#include <OffscreenUi.h>
|
||||||
#include <PhysicalEntitySimulation.h>
|
#include <PhysicalEntitySimulation.h>
|
||||||
#include <PhysicsEngine.h>
|
#include <PhysicsEngine.h>
|
||||||
|
#include <plugins/Forward.h>
|
||||||
#include <ScriptEngine.h>
|
#include <ScriptEngine.h>
|
||||||
#include <ShapeManager.h>
|
#include <ShapeManager.h>
|
||||||
#include <StDev.h>
|
#include <StDev.h>
|
||||||
#include <udt/PacketHeaders.h>
|
#include <udt/PacketHeaders.h>
|
||||||
#include <ViewFrustum.h>
|
#include <ViewFrustum.h>
|
||||||
#include <plugins/PluginContainer.h>
|
|
||||||
#include <plugins/PluginManager.h>
|
|
||||||
#include <SimpleMovingAverage.h>
|
#include <SimpleMovingAverage.h>
|
||||||
|
|
||||||
#include "AudioClient.h"
|
#include "AudioClient.h"
|
||||||
|
@ -50,7 +50,6 @@
|
||||||
#include "Stars.h"
|
#include "Stars.h"
|
||||||
#include "avatar/Avatar.h"
|
#include "avatar/Avatar.h"
|
||||||
#include "avatar/MyAvatar.h"
|
#include "avatar/MyAvatar.h"
|
||||||
#include <input-plugins/KeyboardMouseDevice.h>
|
|
||||||
#include "scripting/ControllerScriptingInterface.h"
|
#include "scripting/ControllerScriptingInterface.h"
|
||||||
#include "scripting/DialogsManagerScriptingInterface.h"
|
#include "scripting/DialogsManagerScriptingInterface.h"
|
||||||
#include "scripting/WebWindowClass.h"
|
#include "scripting/WebWindowClass.h"
|
||||||
|
@ -132,7 +131,7 @@ class Application;
|
||||||
|
|
||||||
typedef bool (Application::* AcceptURLMethod)(const QString &);
|
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
|
Q_OBJECT
|
||||||
|
|
||||||
friend class OctreePacketProcessor;
|
friend class OctreePacketProcessor;
|
||||||
|
@ -280,23 +279,10 @@ public:
|
||||||
virtual void endOverrideEnvironmentData() { _environment.endOverride(); }
|
virtual void endOverrideEnvironmentData() { _environment.endOverride(); }
|
||||||
virtual qreal getDevicePixelRatio();
|
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);
|
void setActiveDisplayPlugin(const QString& pluginName);
|
||||||
|
|
||||||
DisplayPlugin * getActiveDisplayPlugin();
|
DisplayPlugin* getActiveDisplayPlugin();
|
||||||
const DisplayPlugin * getActiveDisplayPlugin() const;
|
const DisplayPlugin* getActiveDisplayPlugin() const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -691,6 +677,8 @@ private:
|
||||||
int _simsPerSecondReport = 0;
|
int _simsPerSecondReport = 0;
|
||||||
quint64 _lastSimsPerSecondUpdate = 0;
|
quint64 _lastSimsPerSecondUpdate = 0;
|
||||||
bool _isForeground = true; // starts out assumed to be in foreground
|
bool _isForeground = true; // starts out assumed to be in foreground
|
||||||
|
|
||||||
|
friend class PluginContainerProxy;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_Application_h
|
#endif // hifi_Application_h
|
||||||
|
|
|
@ -16,11 +16,13 @@
|
||||||
#include <AddressManager.h>
|
#include <AddressManager.h>
|
||||||
#include <AudioClient.h>
|
#include <AudioClient.h>
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
|
#include <display-plugins/DisplayPlugin.h>
|
||||||
#include <PathUtils.h>
|
#include <PathUtils.h>
|
||||||
#include <SettingHandle.h>
|
#include <SettingHandle.h>
|
||||||
#include <UserActivityLogger.h>
|
#include <UserActivityLogger.h>
|
||||||
#include <VrMenu.h>
|
#include <VrMenu.h>
|
||||||
|
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "AccountManager.h"
|
#include "AccountManager.h"
|
||||||
#include "audio/AudioScope.h"
|
#include "audio/AudioScope.h"
|
||||||
|
@ -220,7 +222,7 @@ Menu::Menu() {
|
||||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0,
|
addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0,
|
||||||
qApp, SLOT(packageModel()));
|
qApp, SLOT(packageModel()));
|
||||||
|
|
||||||
MenuWrapper* displayMenu = addMenu("Display");
|
MenuWrapper* displayMenu = addMenu(DisplayPlugin::MENU_PATH);
|
||||||
{
|
{
|
||||||
MenuWrapper* displayModeMenu = addMenu(MenuOption::OutputMenu);
|
MenuWrapper* displayModeMenu = addMenu(MenuOption::OutputMenu);
|
||||||
QActionGroup* displayModeGroup = new QActionGroup(displayModeMenu);
|
QActionGroup* displayModeGroup = new QActionGroup(displayModeMenu);
|
||||||
|
|
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
|
|
@ -191,7 +191,7 @@ void Stars::render(RenderArgs* renderArgs, float alpha) {
|
||||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||||
auto textureCache = DependencyManager::get<TextureCache>();
|
auto textureCache = DependencyManager::get<TextureCache>();
|
||||||
|
|
||||||
gpu::Batch batch;
|
gpu::Batch& batch = *renderArgs->_batch;
|
||||||
batch.setViewTransform(Transform());
|
batch.setViewTransform(Transform());
|
||||||
batch.setProjectionTransform(renderArgs->_viewFrustum->getProjection());
|
batch.setProjectionTransform(renderArgs->_viewFrustum->getProjection());
|
||||||
batch.setModelTransform(Transform().setRotation(glm::inverse(renderArgs->_viewFrustum->getOrientation()) *
|
batch.setModelTransform(Transform().setRotation(glm::inverse(renderArgs->_viewFrustum->getOrientation()) *
|
||||||
|
@ -219,6 +219,4 @@ void Stars::render(RenderArgs* renderArgs, float alpha) {
|
||||||
batch.setInputBuffer(VERTICES_SLOT, posView);
|
batch.setInputBuffer(VERTICES_SLOT, posView);
|
||||||
batch.setInputBuffer(COLOR_SLOT, colView);
|
batch.setInputBuffer(COLOR_SLOT, colView);
|
||||||
batch.draw(gpu::Primitive::POINTS, STARFIELD_NUM_STARS);
|
batch.draw(gpu::Primitive::POINTS, STARFIELD_NUM_STARS);
|
||||||
|
|
||||||
renderArgs->_context->render(batch);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ void renderWorldBox(gpu::Batch& batch) {
|
||||||
static const glm::vec3 GREEN(0.0f, 1.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 BLUE(0.0f, 0.0f, 1.0f);
|
||||||
static const glm::vec3 GREY(0.5f, 0.5f, 0.5f);
|
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_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_GREEN(0.0f, 1.0f, 0.0f, 1.0f);
|
||||||
|
@ -62,8 +63,37 @@ void renderWorldBox(gpu::Batch& batch) {
|
||||||
geometryCache->renderDashedLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, -HALF_TREE_SCALE), DASHED_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);
|
DASH_LENGTH, GAP_LENGTH);
|
||||||
|
|
||||||
geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, HALF_TREE_SCALE), glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY);
|
// X center boundaries
|
||||||
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(-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
|
// Draw meter markers along the 3 axis to help with measuring things
|
||||||
const float MARKER_DISTANCE = 1.0f;
|
const float MARKER_DISTANCE = 1.0f;
|
||||||
|
|
|
@ -748,11 +748,12 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, floa
|
||||||
qDebug() << "ASSERT because isinf(scale)";
|
qDebug() << "ASSERT because isinf(scale)";
|
||||||
}
|
}
|
||||||
qDebug() << "textPosition =" << textPosition;
|
qDebug() << "textPosition =" << textPosition;
|
||||||
|
qDebug() << "projMat =" << projMat;
|
||||||
|
qDebug() << "viewMat =" << viewMat;
|
||||||
|
qDebug() << "viewProj =" << viewProj;
|
||||||
qDebug() << "windowSizeY =" << windowSizeY;
|
qDebug() << "windowSizeY =" << windowSizeY;
|
||||||
qDebug() << "p1.y =" << p1.y;
|
qDebug() << "p1 =" << p1;
|
||||||
qDebug() << "p1.w =" << p1.w;
|
qDebug() << "p0 =" << p0;
|
||||||
qDebug() << "p0.y =" << p0.y;
|
|
||||||
qDebug() << "p0.w =" << p0.w;
|
|
||||||
qDebug() << "qApp->getDevicePixelRatio() =" << qApp->getDevicePixelRatio();
|
qDebug() << "qApp->getDevicePixelRatio() =" << qApp->getDevicePixelRatio();
|
||||||
qDebug() << "fontSize =" << fontSize;
|
qDebug() << "fontSize =" << fontSize;
|
||||||
qDebug() << "pixelHeight =" << pixelHeight;
|
qDebug() << "pixelHeight =" << pixelHeight;
|
||||||
|
|
|
@ -211,7 +211,6 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) {
|
||||||
//Handle fading and deactivation/activation of UI
|
//Handle fading and deactivation/activation of UI
|
||||||
gpu::Batch batch;
|
gpu::Batch batch;
|
||||||
|
|
||||||
renderArgs->_context->syncCache();
|
|
||||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||||
|
|
||||||
geometryCache->useSimpleDrawPipeline(batch);
|
geometryCache->useSimpleDrawPipeline(batch);
|
||||||
|
@ -279,7 +278,6 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
|
||||||
vec2 canvasSize = qApp->getCanvasSize();
|
vec2 canvasSize = qApp->getCanvasSize();
|
||||||
_textureAspectRatio = aspect(canvasSize);
|
_textureAspectRatio = aspect(canvasSize);
|
||||||
|
|
||||||
renderArgs->_context->syncCache();
|
|
||||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||||
|
|
||||||
gpu::Batch batch;
|
gpu::Batch batch;
|
||||||
|
|
|
@ -92,7 +92,6 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
|
||||||
renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope
|
renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope
|
||||||
renderStatsAndLogs(renderArgs); // currently renders nothing
|
renderStatsAndLogs(renderArgs); // currently renders nothing
|
||||||
|
|
||||||
renderArgs->_context->syncCache();
|
|
||||||
renderArgs->_context->render(batch);
|
renderArgs->_context->render(batch);
|
||||||
|
|
||||||
renderArgs->_batch = nullptr; // so future users of renderArgs don't try to use our batch
|
renderArgs->_batch = nullptr; // so future users of renderArgs don't try to use our batch
|
||||||
|
|
|
@ -13,8 +13,7 @@
|
||||||
|
|
||||||
const QString Basic2DWindowOpenGLDisplayPlugin::NAME("2D Display");
|
const QString Basic2DWindowOpenGLDisplayPlugin::NAME("2D Display");
|
||||||
|
|
||||||
const QString MENU_PATH = "Display";
|
static const QString FULLSCREEN = "Fullscreen";
|
||||||
const QString FULLSCREEN = "Fullscreen";
|
|
||||||
|
|
||||||
const QString& Basic2DWindowOpenGLDisplayPlugin::getName() const {
|
const QString& Basic2DWindowOpenGLDisplayPlugin::getName() const {
|
||||||
return NAME;
|
return NAME;
|
||||||
|
@ -30,11 +29,11 @@ void Basic2DWindowOpenGLDisplayPlugin::activate() {
|
||||||
CONTAINER->unsetFullscreen();
|
CONTAINER->unsetFullscreen();
|
||||||
}
|
}
|
||||||
}, true, false);
|
}, true, false);
|
||||||
MainWindowOpenGLDisplayPlugin::activate();
|
WindowOpenGLDisplayPlugin::activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Basic2DWindowOpenGLDisplayPlugin::deactivate() {
|
void Basic2DWindowOpenGLDisplayPlugin::deactivate() {
|
||||||
MainWindowOpenGLDisplayPlugin::deactivate();
|
WindowOpenGLDisplayPlugin::deactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval(bool isThrottled) const {
|
int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval(bool isThrottled) const {
|
||||||
|
|
|
@ -7,10 +7,10 @@
|
||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "MainWindowOpenGLDisplayPlugin.h"
|
#include "WindowOpenGLDisplayPlugin.h"
|
||||||
|
|
||||||
class QScreen;
|
class QScreen;
|
||||||
class Basic2DWindowOpenGLDisplayPlugin : public MainWindowOpenGLDisplayPlugin {
|
class Basic2DWindowOpenGLDisplayPlugin : public WindowOpenGLDisplayPlugin {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -15,8 +15,10 @@
|
||||||
#include "Basic2DWindowOpenGLDisplayPlugin.h"
|
#include "Basic2DWindowOpenGLDisplayPlugin.h"
|
||||||
|
|
||||||
#include "openvr/OpenVrDisplayPlugin.h"
|
#include "openvr/OpenVrDisplayPlugin.h"
|
||||||
#include "oculus/Oculus_0_5_DisplayPlugin.h"
|
#include "oculus/OculusDisplayPlugin.h"
|
||||||
#include "oculus/Oculus_0_6_DisplayPlugin.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
|
// TODO migrate to a DLL model where plugins are discovered and loaded at runtime by the PluginManager class
|
||||||
DisplayPluginList getDisplayPlugins() {
|
DisplayPluginList getDisplayPlugins() {
|
||||||
|
@ -27,14 +29,20 @@ DisplayPluginList getDisplayPlugins() {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Stereo modes
|
// Stereo modes
|
||||||
// FIXME fix stereo display plugins
|
|
||||||
//new SideBySideStereoDisplayPlugin(),
|
// SBS left/right
|
||||||
//new InterleavedStereoDisplayPlugin(),
|
new SideBySideStereoDisplayPlugin(),
|
||||||
|
// Interleaved left/right
|
||||||
|
new InterleavedStereoDisplayPlugin(),
|
||||||
|
|
||||||
// HMDs
|
// HMDs
|
||||||
new Oculus_0_5_DisplayPlugin(),
|
|
||||||
new Oculus_0_6_DisplayPlugin(),
|
// Windows Oculus SDK
|
||||||
|
new OculusDisplayPlugin(),
|
||||||
|
// Mac/Linux Oculus SDK (0.5)
|
||||||
|
new OculusLegacyDisplayPlugin(),
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
|
// SteamVR SDK
|
||||||
new OpenVrDisplayPlugin(),
|
new OpenVrDisplayPlugin(),
|
||||||
#endif
|
#endif
|
||||||
nullptr
|
nullptr
|
||||||
|
|
|
@ -97,12 +97,12 @@ public:
|
||||||
return baseProjection;
|
return baseProjection;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual glm::mat4 getModelview(Eye eye, const glm::mat4& baseModelview) const {
|
virtual glm::mat4 getView(Eye eye, const glm::mat4& baseView) const {
|
||||||
return glm::inverse(getEyePose(eye)) * baseModelview;
|
return glm::inverse(getEyePose(eye)) * baseView;
|
||||||
}
|
}
|
||||||
|
|
||||||
// HMD specific methods
|
// HMD specific methods
|
||||||
// TODO move these into another class
|
// TODO move these into another class?
|
||||||
virtual glm::mat4 getEyePose(Eye eye) const {
|
virtual glm::mat4 getEyePose(Eye eye) const {
|
||||||
static const glm::mat4 pose; return pose;
|
static const glm::mat4 pose; return pose;
|
||||||
}
|
}
|
||||||
|
@ -115,12 +115,7 @@ public:
|
||||||
virtual void resetSensors() {}
|
virtual void resetSensors() {}
|
||||||
virtual float devicePixelRatio() { return 1.0; }
|
virtual float devicePixelRatio() { return 1.0; }
|
||||||
|
|
||||||
//// The window for the surface, used for event interception. May be null.
|
static const QString MENU_PATH;
|
||||||
//virtual QWindow* getWindow() const = 0;
|
|
||||||
|
|
||||||
//virtual void installEventFilter(QObject* filter) {}
|
|
||||||
//virtual void removeEventFilter(QObject* filter) {}
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void recommendedFramebufferSizeChanged(const QSize & size);
|
void recommendedFramebufferSizeChanged(const QSize & size);
|
||||||
void requestRender();
|
void requestRender();
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
|
|
||||||
//
|
|
||||||
// Created by Bradley Austin Davis on 2015/05/29
|
|
||||||
// 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 "MainWindowOpenGLDisplayPlugin.h"
|
|
|
@ -1,13 +0,0 @@
|
||||||
//
|
|
||||||
// Created by Bradley Austin Davis on 2015/05/29
|
|
||||||
// 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 "WindowOpenGLDisplayPlugin.h"
|
|
||||||
|
|
||||||
class MainWindowOpenGLDisplayPlugin : public WindowOpenGLDisplayPlugin {
|
|
||||||
};
|
|
|
@ -105,7 +105,7 @@ bool OpenGLDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
|
||||||
void OpenGLDisplayPlugin::display(
|
void OpenGLDisplayPlugin::display(
|
||||||
GLuint finalTexture, const glm::uvec2& sceneSize) {
|
GLuint finalTexture, const glm::uvec2& sceneSize) {
|
||||||
using namespace oglplus;
|
using namespace oglplus;
|
||||||
uvec2 size = getRecommendedRenderSize();
|
uvec2 size = getSurfaceSize();
|
||||||
Context::Viewport(size.x, size.y);
|
Context::Viewport(size.x, size.y);
|
||||||
glBindTexture(GL_TEXTURE_2D, finalTexture);
|
glBindTexture(GL_TEXTURE_2D, finalTexture);
|
||||||
drawUnitQuad();
|
drawUnitQuad();
|
||||||
|
|
|
@ -33,6 +33,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
virtual void customizeContext();
|
virtual void customizeContext();
|
||||||
virtual void drawUnitQuad();
|
virtual void drawUnitQuad();
|
||||||
|
virtual glm::uvec2 getSurfaceSize() const = 0;
|
||||||
virtual void makeCurrent() = 0;
|
virtual void makeCurrent() = 0;
|
||||||
virtual void doneCurrent() = 0;
|
virtual void doneCurrent() = 0;
|
||||||
virtual void swapBuffers() = 0;
|
virtual void swapBuffers() = 0;
|
||||||
|
|
|
@ -16,6 +16,10 @@ WindowOpenGLDisplayPlugin::WindowOpenGLDisplayPlugin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::uvec2 WindowOpenGLDisplayPlugin::getRecommendedRenderSize() const {
|
glm::uvec2 WindowOpenGLDisplayPlugin::getRecommendedRenderSize() const {
|
||||||
|
return getSurfaceSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::uvec2 WindowOpenGLDisplayPlugin::getSurfaceSize() const {
|
||||||
uvec2 result;
|
uvec2 result;
|
||||||
if (_window) {
|
if (_window) {
|
||||||
result = toGlm(_window->geometry().size() * _window->devicePixelRatio());
|
result = toGlm(_window->geometry().size() * _window->devicePixelRatio());
|
||||||
|
@ -23,6 +27,7 @@ glm::uvec2 WindowOpenGLDisplayPlugin::getRecommendedRenderSize() const {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
glm::uvec2 WindowOpenGLDisplayPlugin::getRecommendedUiSize() const {
|
glm::uvec2 WindowOpenGLDisplayPlugin::getRecommendedUiSize() const {
|
||||||
uvec2 result;
|
uvec2 result;
|
||||||
if (_window) {
|
if (_window) {
|
||||||
|
|
|
@ -21,6 +21,7 @@ public:
|
||||||
virtual void deactivate() override;
|
virtual void deactivate() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
virtual glm::uvec2 getSurfaceSize() const override final;
|
||||||
virtual void makeCurrent() override;
|
virtual void makeCurrent() override;
|
||||||
virtual void doneCurrent() override;
|
virtual void doneCurrent() override;
|
||||||
virtual void swapBuffers() override;
|
virtual void swapBuffers() override;
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
//
|
|
||||||
// Created by Bradley Austin Davis on 2015/05/29
|
|
||||||
// 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 "OculusBaseDisplayPlugin.h"
|
|
||||||
|
|
||||||
#include <ViewFrustum.h>
|
|
||||||
|
|
||||||
#include "OculusHelpers.h"
|
|
||||||
|
|
||||||
|
|
||||||
using namespace Oculus;
|
|
||||||
|
|
||||||
void OculusBaseDisplayPlugin::activate() {
|
|
||||||
glm::uvec2 eyeSizes[2];
|
|
||||||
ovr_for_each_eye([&](ovrEyeType eye) {
|
|
||||||
_eyeFovs[eye] = _hmd->MaxEyeFov[eye];
|
|
||||||
ovrEyeRenderDesc& erd = _eyeRenderDescs[eye] = ovrHmd_GetRenderDesc(_hmd, eye, _eyeFovs[eye]);
|
|
||||||
ovrMatrix4f ovrPerspectiveProjection =
|
|
||||||
ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded);
|
|
||||||
_eyeProjections[eye] = toGlm(ovrPerspectiveProjection);
|
|
||||||
|
|
||||||
ovrPerspectiveProjection =
|
|
||||||
ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded);
|
|
||||||
_compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection);
|
|
||||||
|
|
||||||
_eyeOffsets[eye] = erd.HmdToEyeViewOffset;
|
|
||||||
eyeSizes[eye] = toGlm(ovrHmd_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f));
|
|
||||||
});
|
|
||||||
_desiredFramebufferSize = uvec2(
|
|
||||||
eyeSizes[0].x + eyeSizes[1].x,
|
|
||||||
std::max(eyeSizes[0].y, eyeSizes[1].y));
|
|
||||||
|
|
||||||
_frameIndex = 0;
|
|
||||||
|
|
||||||
if (!OVR_SUCCESS(ovrHmd_ConfigureTracking(_hmd,
|
|
||||||
ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) {
|
|
||||||
qFatal("Could not attach to sensor device");
|
|
||||||
}
|
|
||||||
|
|
||||||
MainWindowOpenGLDisplayPlugin::activate();
|
|
||||||
}
|
|
||||||
|
|
||||||
uvec2 OculusBaseDisplayPlugin::getRecommendedRenderSize() const {
|
|
||||||
return _desiredFramebufferSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OculusBaseDisplayPlugin::preRender() {
|
|
||||||
ovrHmd_GetEyePoses(_hmd, _frameIndex, _eyeOffsets, _eyePoses, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::mat4 OculusBaseDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const {
|
|
||||||
return _eyeProjections[eye];
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::mat4 OculusBaseDisplayPlugin::getModelview(Eye eye, const glm::mat4& baseModelview) const {
|
|
||||||
return baseModelview * toGlm(_eyePoses[eye]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OculusBaseDisplayPlugin::resetSensors() {
|
|
||||||
ovrHmd_RecenterPose(_hmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::mat4 OculusBaseDisplayPlugin::getEyePose(Eye eye) const {
|
|
||||||
return toGlm(_eyePoses[eye]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should NOT be used for rendering as this will mess up timewarp. Use the getModelview() method above for
|
|
||||||
// any use of head poses for rendering, ensuring you use the correct eye
|
|
||||||
glm::mat4 OculusBaseDisplayPlugin::getHeadPose() const {
|
|
||||||
ovrTrackingState state = ovrHmd_GetTrackingState(_hmd, 0.0f);
|
|
||||||
return toGlm(state.HeadPose.ThePose);
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
//
|
|
||||||
// Created by Bradley Austin Davis on 2015/05/29
|
|
||||||
// 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 "../MainWindowOpenGLDisplayPlugin.h"
|
|
||||||
|
|
||||||
class OculusBaseDisplayPlugin : public MainWindowOpenGLDisplayPlugin {
|
|
||||||
public:
|
|
||||||
// Stereo specific methods
|
|
||||||
virtual bool isHmd() const override { return true; }
|
|
||||||
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
|
|
||||||
virtual glm::mat4 getModelview(Eye eye, const glm::mat4& baseModelview) const override;
|
|
||||||
virtual void activate() override;
|
|
||||||
virtual void preRender() override;
|
|
||||||
virtual glm::uvec2 getRecommendedRenderSize() const override;
|
|
||||||
virtual glm::uvec2 getRecommendedUiSize() const override { return uvec2(1920, 1080); }
|
|
||||||
virtual void resetSensors() override;
|
|
||||||
virtual glm::mat4 getEyePose(Eye eye) const override;
|
|
||||||
virtual glm::mat4 getHeadPose() const override;
|
|
||||||
|
|
||||||
};
|
|
|
@ -5,7 +5,7 @@
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
#include "Oculus_0_6_DisplayPlugin.h"
|
#include "OculusDisplayPlugin.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
@ -15,9 +15,7 @@
|
||||||
#include <GlWindow.h>
|
#include <GlWindow.h>
|
||||||
#include <QEvent>
|
#include <QEvent>
|
||||||
#include <QResizeEvent>
|
#include <QResizeEvent>
|
||||||
|
#include <QThread>
|
||||||
#include <OVR_CAPI_GL.h>
|
|
||||||
|
|
||||||
|
|
||||||
#include <OglplusHelpers.h>
|
#include <OglplusHelpers.h>
|
||||||
#include <oglplus/opt/list_init.hpp>
|
#include <oglplus/opt/list_init.hpp>
|
||||||
|
@ -27,18 +25,34 @@
|
||||||
|
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
#include <plugins/PluginContainer.h>
|
#include <plugins/PluginContainer.h>
|
||||||
|
#include <ViewFrustum.h>
|
||||||
|
|
||||||
#include "OculusHelpers.h"
|
#include "OculusHelpers.h"
|
||||||
|
|
||||||
using namespace Oculus;
|
#if (OVR_MAJOR_VERSION == 6)
|
||||||
#if (OVR_MAJOR_VERSION == 6)
|
#define ovr_Create ovrHmd_Create
|
||||||
SwapFboPtr _sceneFbo;
|
#define ovr_CreateSwapTextureSetGL ovrHmd_CreateSwapTextureSetGL
|
||||||
MirrorFboPtr _mirrorFbo;
|
#define ovr_CreateMirrorTextureGL ovrHmd_CreateMirrorTextureGL
|
||||||
ovrLayerEyeFov _sceneLayer;
|
#define ovr_Destroy ovrHmd_Destroy
|
||||||
|
#define ovr_DestroySwapTextureSet ovrHmd_DestroySwapTextureSet
|
||||||
|
#define ovr_DestroyMirrorTexture ovrHmd_DestroyMirrorTexture
|
||||||
|
#define ovr_GetFloat ovrHmd_GetFloat
|
||||||
|
#define ovr_GetFovTextureSize ovrHmd_GetFovTextureSize
|
||||||
|
#define ovr_GetFrameTiming ovrHmd_GetFrameTiming
|
||||||
|
#define ovr_GetTrackingState ovrHmd_GetTrackingState
|
||||||
|
#define ovr_GetRenderDesc ovrHmd_GetRenderDesc
|
||||||
|
#define ovr_RecenterPose ovrHmd_RecenterPose
|
||||||
|
#define ovr_SubmitFrame ovrHmd_SubmitFrame
|
||||||
|
#define ovr_ConfigureTracking ovrHmd_ConfigureTracking
|
||||||
|
|
||||||
|
#define ovr_GetHmdDesc(X) *X
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (OVR_MAJOR_VERSION >= 6)
|
||||||
|
|
||||||
// A base class for FBO wrappers that need to use the Oculus C
|
// A base class for FBO wrappers that need to use the Oculus C
|
||||||
// API to manage textures via ovrHmd_CreateSwapTextureSetGL,
|
// API to manage textures via ovr_CreateSwapTextureSetGL,
|
||||||
// ovrHmd_CreateMirrorTextureGL, etc
|
// ovr_CreateMirrorTextureGL, etc
|
||||||
template <typename C>
|
template <typename C>
|
||||||
struct RiftFramebufferWrapper : public FramebufferWrapper<C, char> {
|
struct RiftFramebufferWrapper : public FramebufferWrapper<C, char> {
|
||||||
ovrHmd hmd;
|
ovrHmd hmd;
|
||||||
|
@ -73,7 +87,7 @@ struct SwapFramebufferWrapper : public RiftFramebufferWrapper<ovrSwapTextureSet*
|
||||||
|
|
||||||
~SwapFramebufferWrapper() {
|
~SwapFramebufferWrapper() {
|
||||||
if (color) {
|
if (color) {
|
||||||
ovrHmd_DestroySwapTextureSet(hmd, color);
|
ovr_DestroySwapTextureSet(hmd, color);
|
||||||
color = nullptr;
|
color = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,11 +100,11 @@ struct SwapFramebufferWrapper : public RiftFramebufferWrapper<ovrSwapTextureSet*
|
||||||
protected:
|
protected:
|
||||||
virtual void initColor() override {
|
virtual void initColor() override {
|
||||||
if (color) {
|
if (color) {
|
||||||
ovrHmd_DestroySwapTextureSet(hmd, color);
|
ovr_DestroySwapTextureSet(hmd, color);
|
||||||
color = nullptr;
|
color = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!OVR_SUCCESS(ovrHmd_CreateSwapTextureSetGL(hmd, GL_RGBA, size.x, size.y, &color))) {
|
if (!OVR_SUCCESS(ovr_CreateSwapTextureSetGL(hmd, GL_RGBA, size.x, size.y, &color))) {
|
||||||
qFatal("Unable to create swap textures");
|
qFatal("Unable to create swap textures");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +141,7 @@ struct MirrorFramebufferWrapper : public RiftFramebufferWrapper<ovrGLTexture*> {
|
||||||
|
|
||||||
virtual ~MirrorFramebufferWrapper() {
|
virtual ~MirrorFramebufferWrapper() {
|
||||||
if (color) {
|
if (color) {
|
||||||
ovrHmd_DestroyMirrorTexture(hmd, (ovrTexture*)color);
|
ovr_DestroyMirrorTexture(hmd, (ovrTexture*)color);
|
||||||
color = nullptr;
|
color = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,10 +149,10 @@ struct MirrorFramebufferWrapper : public RiftFramebufferWrapper<ovrGLTexture*> {
|
||||||
private:
|
private:
|
||||||
void initColor() override {
|
void initColor() override {
|
||||||
if (color) {
|
if (color) {
|
||||||
ovrHmd_DestroyMirrorTexture(hmd, (ovrTexture*)color);
|
ovr_DestroyMirrorTexture(hmd, (ovrTexture*)color);
|
||||||
color = nullptr;
|
color = nullptr;
|
||||||
}
|
}
|
||||||
ovrResult result = ovrHmd_CreateMirrorTextureGL(hmd, GL_RGBA, size.x, size.y, (ovrTexture**)&color);
|
ovrResult result = ovr_CreateMirrorTextureGL(hmd, GL_RGBA, size.x, size.y, (ovrTexture**)&color);
|
||||||
Q_ASSERT(OVR_SUCCESS(result));
|
Q_ASSERT(OVR_SUCCESS(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,17 +163,47 @@ private:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
const QString OculusDisplayPlugin::NAME("Oculus Rift");
|
||||||
|
|
||||||
const QString Oculus_0_6_DisplayPlugin::NAME("Oculus Rift");
|
uvec2 OculusDisplayPlugin::getRecommendedRenderSize() const {
|
||||||
|
return _desiredFramebufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
const QString & Oculus_0_6_DisplayPlugin::getName() const {
|
void OculusDisplayPlugin::preRender() {
|
||||||
|
#if (OVR_MAJOR_VERSION >= 6)
|
||||||
|
ovrFrameTiming ftiming = ovr_GetFrameTiming(_hmd, _frameIndex);
|
||||||
|
_trackingState = ovr_GetTrackingState(_hmd, ftiming.DisplayMidpointSeconds);
|
||||||
|
ovr_CalcEyePoses(_trackingState.HeadPose.ThePose, _eyeOffsets, _eyePoses);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::mat4 OculusDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const {
|
||||||
|
return _eyeProjections[eye];
|
||||||
|
}
|
||||||
|
|
||||||
|
void OculusDisplayPlugin::resetSensors() {
|
||||||
|
#if (OVR_MAJOR_VERSION >= 6)
|
||||||
|
ovr_RecenterPose(_hmd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::mat4 OculusDisplayPlugin::getEyePose(Eye eye) const {
|
||||||
|
return toGlm(_eyePoses[eye]);
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::mat4 OculusDisplayPlugin::getHeadPose() const {
|
||||||
|
return toGlm(_trackingState.HeadPose.ThePose);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString & OculusDisplayPlugin::getName() const {
|
||||||
return NAME;
|
return NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Oculus_0_6_DisplayPlugin::isSupported() const {
|
bool OculusDisplayPlugin::isSupported() const {
|
||||||
#if (OVR_MAJOR_VERSION == 6)
|
#if (OVR_MAJOR_VERSION >= 6)
|
||||||
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
|
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -174,27 +218,81 @@ bool Oculus_0_6_DisplayPlugin::isSupported() const {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OculusDisplayPlugin::init() {
|
||||||
|
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
|
||||||
|
qFatal("Could not init OVR");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if (OVR_MAJOR_VERSION == 6)
|
void OculusDisplayPlugin::deinit() {
|
||||||
ovrLayerEyeFov& getSceneLayer() {
|
ovr_Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (OVR_MAJOR_VERSION >= 6)
|
||||||
|
ovrLayerEyeFov& OculusDisplayPlugin::getSceneLayer() {
|
||||||
return _sceneLayer;
|
return _sceneLayer;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//static gpu::TexturePointer _texture;
|
//static gpu::TexturePointer _texture;
|
||||||
|
|
||||||
void Oculus_0_6_DisplayPlugin::activate() {
|
void OculusDisplayPlugin::activate() {
|
||||||
#if (OVR_MAJOR_VERSION == 6)
|
#if (OVR_MAJOR_VERSION >= 6)
|
||||||
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
|
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
|
||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
qFatal("Failed to Initialize SDK");
|
qFatal("Failed to Initialize SDK");
|
||||||
}
|
}
|
||||||
if (!OVR_SUCCESS(ovrHmd_Create(0, &_hmd))) {
|
|
||||||
|
// CONTAINER->getPrimarySurface()->makeCurrent();
|
||||||
|
#if (OVR_MAJOR_VERSION == 6)
|
||||||
|
if (!OVR_SUCCESS(ovr_Create(0, &_hmd))) {
|
||||||
|
#elif (OVR_MAJOR_VERSION == 7)
|
||||||
|
if (!OVR_SUCCESS(ovr_Create(&_hmd, &_luid))) {
|
||||||
|
#endif
|
||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
qFatal("Failed to acquire HMD");
|
qFatal("Failed to acquire HMD");
|
||||||
}
|
}
|
||||||
|
|
||||||
OculusBaseDisplayPlugin::activate();
|
_hmdDesc = ovr_GetHmdDesc(_hmd);
|
||||||
|
|
||||||
|
_ipd = ovr_GetFloat(_hmd, OVR_KEY_IPD, _ipd);
|
||||||
|
|
||||||
|
glm::uvec2 eyeSizes[2];
|
||||||
|
ovr_for_each_eye([&](ovrEyeType eye) {
|
||||||
|
_eyeFovs[eye] = _hmdDesc.DefaultEyeFov[eye];
|
||||||
|
ovrEyeRenderDesc& erd = _eyeRenderDescs[eye] = ovr_GetRenderDesc(_hmd, eye, _eyeFovs[eye]);
|
||||||
|
ovrMatrix4f ovrPerspectiveProjection =
|
||||||
|
ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded);
|
||||||
|
_eyeProjections[eye] = toGlm(ovrPerspectiveProjection);
|
||||||
|
|
||||||
|
ovrPerspectiveProjection =
|
||||||
|
ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded);
|
||||||
|
_compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection);
|
||||||
|
|
||||||
|
_eyeOffsets[eye] = erd.HmdToEyeViewOffset;
|
||||||
|
eyeSizes[eye] = toGlm(ovr_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f));
|
||||||
|
});
|
||||||
|
ovrFovPort combined = _eyeFovs[Left];
|
||||||
|
combined.LeftTan = std::max(_eyeFovs[Left].LeftTan, _eyeFovs[Right].LeftTan);
|
||||||
|
combined.RightTan = std::max(_eyeFovs[Left].RightTan, _eyeFovs[Right].RightTan);
|
||||||
|
ovrMatrix4f ovrPerspectiveProjection =
|
||||||
|
ovrMatrix4f_Projection(combined, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded);
|
||||||
|
_eyeProjections[Mono] = toGlm(ovrPerspectiveProjection);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_desiredFramebufferSize = uvec2(
|
||||||
|
eyeSizes[0].x + eyeSizes[1].x,
|
||||||
|
std::max(eyeSizes[0].y, eyeSizes[1].y));
|
||||||
|
|
||||||
|
_frameIndex = 0;
|
||||||
|
|
||||||
|
if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd,
|
||||||
|
ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) {
|
||||||
|
qFatal("Could not attach to sensor device");
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowOpenGLDisplayPlugin::activate();
|
||||||
|
|
||||||
// Parent class relies on our _hmd intialization, so it must come after that.
|
// Parent class relies on our _hmd intialization, so it must come after that.
|
||||||
ovrLayerEyeFov& sceneLayer = getSceneLayer();
|
ovrLayerEyeFov& sceneLayer = getSceneLayer();
|
||||||
|
@ -203,7 +301,7 @@ void Oculus_0_6_DisplayPlugin::activate() {
|
||||||
sceneLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft;
|
sceneLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft;
|
||||||
ovr_for_each_eye([&](ovrEyeType eye) {
|
ovr_for_each_eye([&](ovrEyeType eye) {
|
||||||
ovrFovPort & fov = sceneLayer.Fov[eye] = _eyeRenderDescs[eye].Fov;
|
ovrFovPort & fov = sceneLayer.Fov[eye] = _eyeRenderDescs[eye].Fov;
|
||||||
ovrSizei & size = sceneLayer.Viewport[eye].Size = ovrHmd_GetFovTextureSize(_hmd, eye, fov, 1.0f);
|
ovrSizei & size = sceneLayer.Viewport[eye].Size = ovr_GetFovTextureSize(_hmd, eye, fov, 1.0f);
|
||||||
sceneLayer.Viewport[eye].Pos = { eye == ovrEye_Left ? 0 : size.w, 0 };
|
sceneLayer.Viewport[eye].Pos = { eye == ovrEye_Left ? 0 : size.w, 0 };
|
||||||
});
|
});
|
||||||
// We're rendering both eyes to the same texture, so only one of the
|
// We're rendering both eyes to the same texture, so only one of the
|
||||||
|
@ -214,17 +312,16 @@ void Oculus_0_6_DisplayPlugin::activate() {
|
||||||
|
|
||||||
PerformanceTimer::setActive(true);
|
PerformanceTimer::setActive(true);
|
||||||
|
|
||||||
if (!OVR_SUCCESS(ovrHmd_ConfigureTracking(_hmd,
|
if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd,
|
||||||
ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) {
|
ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) {
|
||||||
qFatal("Could not attach to sensor device");
|
qFatal("Could not attach to sensor device");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Oculus_0_6_DisplayPlugin::customizeContext() {
|
void OculusDisplayPlugin::customizeContext() {
|
||||||
#if (OVR_MAJOR_VERSION == 6)
|
WindowOpenGLDisplayPlugin::customizeContext();
|
||||||
OculusBaseDisplayPlugin::customizeContext();
|
#if (OVR_MAJOR_VERSION >= 6)
|
||||||
|
|
||||||
//_texture = DependencyManager::get<TextureCache>()->
|
//_texture = DependencyManager::get<TextureCache>()->
|
||||||
// getImageTexture(PathUtils::resourcesPath() + "/images/cube_texture.png");
|
// getImageTexture(PathUtils::resourcesPath() + "/images/cube_texture.png");
|
||||||
uvec2 mirrorSize = toGlm(_window->geometry().size());
|
uvec2 mirrorSize = toGlm(_window->geometry().size());
|
||||||
|
@ -236,24 +333,29 @@ void Oculus_0_6_DisplayPlugin::customizeContext() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Oculus_0_6_DisplayPlugin::deactivate() {
|
void OculusDisplayPlugin::deactivate() {
|
||||||
#if (OVR_MAJOR_VERSION == 6)
|
#if (OVR_MAJOR_VERSION >= 6)
|
||||||
makeCurrent();
|
makeCurrent();
|
||||||
_sceneFbo.reset();
|
_sceneFbo.reset();
|
||||||
_mirrorFbo.reset();
|
_mirrorFbo.reset();
|
||||||
doneCurrent();
|
doneCurrent();
|
||||||
PerformanceTimer::setActive(false);
|
PerformanceTimer::setActive(false);
|
||||||
|
|
||||||
OculusBaseDisplayPlugin::deactivate();
|
WindowOpenGLDisplayPlugin::deactivate();
|
||||||
|
|
||||||
ovrHmd_Destroy(_hmd);
|
ovr_Destroy(_hmd);
|
||||||
_hmd = nullptr;
|
_hmd = nullptr;
|
||||||
ovr_Shutdown();
|
ovr_Shutdown();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Oculus_0_6_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) {
|
void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) {
|
||||||
#if (OVR_MAJOR_VERSION == 6)
|
static bool inDisplay = false;
|
||||||
|
if (inDisplay) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
inDisplay = true;
|
||||||
|
#if (OVR_MAJOR_VERSION >= 6)
|
||||||
using namespace oglplus;
|
using namespace oglplus;
|
||||||
// Need to make sure only the display plugin is responsible for
|
// Need to make sure only the display plugin is responsible for
|
||||||
// controlling vsync
|
// controlling vsync
|
||||||
|
@ -263,6 +365,7 @@ void Oculus_0_6_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sc
|
||||||
auto size = _sceneFbo->size;
|
auto size = _sceneFbo->size;
|
||||||
Context::Viewport(size.x, size.y);
|
Context::Viewport(size.x, size.y);
|
||||||
glBindTexture(GL_TEXTURE_2D, finalTexture);
|
glBindTexture(GL_TEXTURE_2D, finalTexture);
|
||||||
|
GLenum err = glGetError();
|
||||||
drawUnitQuad();
|
drawUnitQuad();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -280,17 +383,25 @@ void Oculus_0_6_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sc
|
||||||
the UI visible in the output window (unlikely). This should be done before
|
the UI visible in the output window (unlikely). This should be done before
|
||||||
_sceneFbo->Increment or we're be using the wrong texture
|
_sceneFbo->Increment or we're be using the wrong texture
|
||||||
*/
|
*/
|
||||||
//_sceneFbo->Bound(GL_READ_FRAMEBUFFER, [&] {
|
_sceneFbo->Bound(Framebuffer::Target::Read, [&] {
|
||||||
// glBlitFramebuffer(
|
glBlitFramebuffer(
|
||||||
// 0, 0, _sceneFbo->size.x, _sceneFbo->size.y,
|
0, 0, _sceneFbo->size.x, _sceneFbo->size.y,
|
||||||
// 0, 0, windowSize.x, _mirrorFbo.y,
|
0, 0, windowSize.x, windowSize.y,
|
||||||
// GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||||
//});
|
});
|
||||||
|
|
||||||
{
|
{
|
||||||
PerformanceTimer("OculusSubmit");
|
PerformanceTimer("OculusSubmit");
|
||||||
|
ovrViewScaleDesc viewScaleDesc;
|
||||||
|
viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f;
|
||||||
|
viewScaleDesc.HmdToEyeViewOffset[0] = _eyeOffsets[0];
|
||||||
|
viewScaleDesc.HmdToEyeViewOffset[1] = _eyeOffsets[1];
|
||||||
|
|
||||||
ovrLayerHeader* layers = &sceneLayer.Header;
|
ovrLayerHeader* layers = &sceneLayer.Header;
|
||||||
ovrResult result = ovrHmd_SubmitFrame(_hmd, _frameIndex, nullptr, &layers, 1);
|
ovrResult result = ovr_SubmitFrame(_hmd, 0, &viewScaleDesc, &layers, 1);
|
||||||
|
if (!OVR_SUCCESS(result)) {
|
||||||
|
qDebug() << result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_sceneFbo->Increment();
|
_sceneFbo->Increment();
|
||||||
|
|
||||||
|
@ -299,21 +410,22 @@ void Oculus_0_6_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sc
|
||||||
will contain the post-distorted and fully composited scene regardless of how many layers
|
will contain the post-distorted and fully composited scene regardless of how many layers
|
||||||
we send.
|
we send.
|
||||||
*/
|
*/
|
||||||
auto mirrorSize = _mirrorFbo->size;
|
//auto mirrorSize = _mirrorFbo->size;
|
||||||
_mirrorFbo->Bound(Framebuffer::Target::Read, [&] {
|
//_mirrorFbo->Bound(Framebuffer::Target::Read, [&] {
|
||||||
Context::BlitFramebuffer(
|
// Context::BlitFramebuffer(
|
||||||
0, mirrorSize.y, mirrorSize.x, 0,
|
// 0, mirrorSize.y, mirrorSize.x, 0,
|
||||||
0, 0, windowSize.x, windowSize.y,
|
// 0, 0, windowSize.x, windowSize.y,
|
||||||
BufferSelectBit::ColorBuffer, BlitFilter::Nearest);
|
// BufferSelectBit::ColorBuffer, BlitFilter::Nearest);
|
||||||
});
|
//});
|
||||||
|
|
||||||
++_frameIndex;
|
++_frameIndex;
|
||||||
#endif
|
#endif
|
||||||
|
inDisplay = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass input events on to the application
|
// Pass input events on to the application
|
||||||
bool Oculus_0_6_DisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
|
bool OculusDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
|
||||||
#if (OVR_MAJOR_VERSION == 6)
|
#if (OVR_MAJOR_VERSION >= 6)
|
||||||
if (event->type() == QEvent::Resize) {
|
if (event->type() == QEvent::Resize) {
|
||||||
QResizeEvent* resizeEvent = static_cast<QResizeEvent*>(event);
|
QResizeEvent* resizeEvent = static_cast<QResizeEvent*>(event);
|
||||||
qDebug() << resizeEvent->size().width() << " x " << resizeEvent->size().height();
|
qDebug() << resizeEvent->size().width() << " x " << resizeEvent->size().height();
|
||||||
|
@ -323,7 +435,7 @@ bool Oculus_0_6_DisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
|
||||||
doneCurrent();
|
doneCurrent();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return OculusBaseDisplayPlugin::eventFilter(receiver, event);
|
return WindowOpenGLDisplayPlugin::eventFilter(receiver, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -331,8 +443,8 @@ bool Oculus_0_6_DisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
|
||||||
However, it should only be done if we can reliably disable v-sync on the mirror surface,
|
However, it should only be done if we can reliably disable v-sync on the mirror surface,
|
||||||
otherwise the swapbuffer delay will interefere with the framerate of the headset
|
otherwise the swapbuffer delay will interefere with the framerate of the headset
|
||||||
*/
|
*/
|
||||||
void Oculus_0_6_DisplayPlugin::finishFrame() {
|
void OculusDisplayPlugin::finishFrame() {
|
||||||
swapBuffers();
|
//swapBuffers();
|
||||||
doneCurrent();
|
doneCurrent();
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2015/05/29
|
||||||
|
// 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 "../WindowOpenGLDisplayPlugin.h"
|
||||||
|
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include <OVR_CAPI.h>
|
||||||
|
|
||||||
|
class OffscreenGlCanvas;
|
||||||
|
struct SwapFramebufferWrapper;
|
||||||
|
struct MirrorFramebufferWrapper;
|
||||||
|
|
||||||
|
using SwapFboPtr = QSharedPointer<SwapFramebufferWrapper>;
|
||||||
|
using MirrorFboPtr = QSharedPointer<MirrorFramebufferWrapper>;
|
||||||
|
|
||||||
|
class OculusDisplayPlugin : public WindowOpenGLDisplayPlugin {
|
||||||
|
public:
|
||||||
|
virtual bool isSupported() const override;
|
||||||
|
virtual const QString & getName() const override;
|
||||||
|
|
||||||
|
virtual void init() override;
|
||||||
|
virtual void deinit() override;
|
||||||
|
|
||||||
|
virtual void activate() override;
|
||||||
|
virtual void deactivate() override;
|
||||||
|
|
||||||
|
virtual bool eventFilter(QObject* receiver, QEvent* event) override;
|
||||||
|
|
||||||
|
// Stereo specific methods
|
||||||
|
virtual bool isHmd() const override { return true; }
|
||||||
|
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
|
||||||
|
virtual glm::uvec2 getRecommendedRenderSize() const override;
|
||||||
|
virtual glm::uvec2 getRecommendedUiSize() const override { return uvec2(1920, 1080); }
|
||||||
|
virtual void resetSensors() override;
|
||||||
|
virtual glm::mat4 getEyePose(Eye eye) const override;
|
||||||
|
virtual glm::mat4 getHeadPose() const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void preRender() override;
|
||||||
|
virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override;
|
||||||
|
virtual void customizeContext() override;
|
||||||
|
// Do not perform swap in finish
|
||||||
|
virtual void finishFrame() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const QString NAME;
|
||||||
|
|
||||||
|
ovrHmd _hmd;
|
||||||
|
float _ipd{ OVR_DEFAULT_IPD };
|
||||||
|
unsigned int _frameIndex;
|
||||||
|
ovrEyeRenderDesc _eyeRenderDescs[2];
|
||||||
|
ovrPosef _eyePoses[2];
|
||||||
|
ovrVector3f _eyeOffsets[2];
|
||||||
|
ovrFovPort _eyeFovs[2];
|
||||||
|
mat4 _eyeProjections[3];
|
||||||
|
mat4 _compositeEyeProjections[2];
|
||||||
|
uvec2 _desiredFramebufferSize;
|
||||||
|
ovrTrackingState _trackingState;
|
||||||
|
|
||||||
|
#if (OVR_MAJOR_VERSION >= 6)
|
||||||
|
ovrLayerEyeFov& getSceneLayer();
|
||||||
|
ovrHmdDesc _hmdDesc;
|
||||||
|
SwapFboPtr _sceneFbo;
|
||||||
|
MirrorFboPtr _mirrorFbo;
|
||||||
|
ovrLayerEyeFov _sceneLayer;
|
||||||
|
#endif
|
||||||
|
#if (OVR_MAJOR_VERSION == 7)
|
||||||
|
ovrGraphicsLuid _luid;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <OVR_CAPI.h>
|
#include <OVR_CAPI_GL.h>
|
||||||
#include <GLMHelpers.h>
|
#include <GLMHelpers.h>
|
||||||
#include <glm/gtc/type_ptr.hpp>
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
@ -79,14 +79,3 @@ inline ovrQuatf ovrFromGlm(const glm::quat & q) {
|
||||||
return{ q.x, q.y, q.z, q.w };
|
return{ q.x, q.y, q.z, q.w };
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Oculus {
|
|
||||||
extern ovrHmd _hmd;
|
|
||||||
extern unsigned int _frameIndex;
|
|
||||||
extern ovrEyeRenderDesc _eyeRenderDescs[2];
|
|
||||||
extern ovrPosef _eyePoses[2];
|
|
||||||
extern ovrVector3f _eyeOffsets[2];
|
|
||||||
extern ovrFovPort _eyeFovs[2];
|
|
||||||
extern mat4 _eyeProjections[2];
|
|
||||||
extern mat4 _compositeEyeProjections[2];
|
|
||||||
extern uvec2 _desiredFramebufferSize;
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
#include "Oculus_0_5_DisplayPlugin.h"
|
#include "OculusLegacyDisplayPlugin.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
@ -19,34 +19,66 @@
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <QScreen>
|
#include <QScreen>
|
||||||
|
|
||||||
|
|
||||||
#include <OVR_CAPI_GL.h>
|
|
||||||
|
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
#include <OglplusHelpers.h>
|
#include <OglplusHelpers.h>
|
||||||
|
#include <ViewFrustum.h>
|
||||||
|
|
||||||
#include "plugins/PluginContainer.h"
|
#include "plugins/PluginContainer.h"
|
||||||
#include "OculusHelpers.h"
|
#include "OculusHelpers.h"
|
||||||
|
|
||||||
using namespace Oculus;
|
|
||||||
ovrTexture _eyeTextures[2];
|
|
||||||
int _hmdScreen{ -1 };
|
|
||||||
bool _hswDismissed{ false };
|
|
||||||
|
|
||||||
DisplayPlugin* makeOculusDisplayPlugin() {
|
|
||||||
return new Oculus_0_5_DisplayPlugin();
|
|
||||||
}
|
|
||||||
|
|
||||||
using namespace oglplus;
|
using namespace oglplus;
|
||||||
|
|
||||||
const QString Oculus_0_5_DisplayPlugin::NAME("Oculus Rift (0.5)");
|
const QString OculusLegacyDisplayPlugin::NAME("Oculus Rift (0.5)");
|
||||||
|
|
||||||
const QString & Oculus_0_5_DisplayPlugin::getName() const {
|
const QString & OculusLegacyDisplayPlugin::getName() const {
|
||||||
return NAME;
|
return NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OculusLegacyDisplayPlugin::OculusLegacyDisplayPlugin() : _ipd(OVR_DEFAULT_IPD) {
|
||||||
|
}
|
||||||
|
|
||||||
bool Oculus_0_5_DisplayPlugin::isSupported() const {
|
uvec2 OculusLegacyDisplayPlugin::getRecommendedRenderSize() const {
|
||||||
|
return _desiredFramebufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OculusLegacyDisplayPlugin::preRender() {
|
||||||
|
#if (OVR_MAJOR_VERSION == 5)
|
||||||
|
ovrHmd_GetEyePoses(_hmd, _frameIndex, _eyeOffsets, _eyePoses, &_trackingState);
|
||||||
|
ovrHmd_BeginFrame(_hmd, _frameIndex);
|
||||||
|
#endif
|
||||||
|
WindowOpenGLDisplayPlugin::preRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::mat4 OculusLegacyDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const {
|
||||||
|
return _eyeProjections[eye];
|
||||||
|
}
|
||||||
|
|
||||||
|
void OculusLegacyDisplayPlugin::resetSensors() {
|
||||||
|
#if (OVR_MAJOR_VERSION == 5)
|
||||||
|
ovrHmd_RecenterPose(_hmd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::mat4 OculusLegacyDisplayPlugin::getEyePose(Eye eye) const {
|
||||||
|
#if (OVR_MAJOR_VERSION == 5)
|
||||||
|
return toGlm(_eyePoses[eye]);
|
||||||
|
#else
|
||||||
|
return WindowOpenGLDisplayPlugin::getEyePose(eye);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should NOT be used for rendering as this will mess up timewarp. Use the getModelview() method above for
|
||||||
|
// any use of head poses for rendering, ensuring you use the correct eye
|
||||||
|
glm::mat4 OculusLegacyDisplayPlugin::getHeadPose() const {
|
||||||
|
#if (OVR_MAJOR_VERSION == 5)
|
||||||
|
return toGlm(_trackingState.HeadPose.ThePose);
|
||||||
|
#else
|
||||||
|
return WindowOpenGLDisplayPlugin::getHeadPose();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool OculusLegacyDisplayPlugin::isSupported() const {
|
||||||
#if (OVR_MAJOR_VERSION == 5)
|
#if (OVR_MAJOR_VERSION == 5)
|
||||||
if (!ovr_Initialize(nullptr)) {
|
if (!ovr_Initialize(nullptr)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -77,7 +109,7 @@ bool Oculus_0_5_DisplayPlugin::isSupported() const {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Oculus_0_5_DisplayPlugin::activate() {
|
void OculusLegacyDisplayPlugin::activate() {
|
||||||
#if (OVR_MAJOR_VERSION == 5)
|
#if (OVR_MAJOR_VERSION == 5)
|
||||||
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
|
if (!OVR_SUCCESS(ovr_Initialize(nullptr))) {
|
||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
|
@ -89,7 +121,41 @@ void Oculus_0_5_DisplayPlugin::activate() {
|
||||||
qFatal("Failed to acquire HMD");
|
qFatal("Failed to acquire HMD");
|
||||||
}
|
}
|
||||||
|
|
||||||
OculusBaseDisplayPlugin::activate();
|
glm::uvec2 eyeSizes[2];
|
||||||
|
ovr_for_each_eye([&](ovrEyeType eye) {
|
||||||
|
_eyeFovs[eye] = _hmd->MaxEyeFov[eye];
|
||||||
|
ovrEyeRenderDesc erd = _eyeRenderDescs[eye] = ovrHmd_GetRenderDesc(_hmd, eye, _eyeFovs[eye]);
|
||||||
|
ovrMatrix4f ovrPerspectiveProjection =
|
||||||
|
ovrMatrix4f_Projection(erd.Fov, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded);
|
||||||
|
_eyeProjections[eye] = toGlm(ovrPerspectiveProjection);
|
||||||
|
|
||||||
|
ovrPerspectiveProjection =
|
||||||
|
ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded);
|
||||||
|
_compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection);
|
||||||
|
|
||||||
|
_eyeOffsets[eye] = erd.HmdToEyeViewOffset;
|
||||||
|
eyeSizes[eye] = toGlm(ovrHmd_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f));
|
||||||
|
});
|
||||||
|
ovrFovPort combined = _eyeFovs[Left];
|
||||||
|
combined.LeftTan = std::max(_eyeFovs[Left].LeftTan, _eyeFovs[Right].LeftTan);
|
||||||
|
combined.RightTan = std::max(_eyeFovs[Left].RightTan, _eyeFovs[Right].RightTan);
|
||||||
|
ovrMatrix4f ovrPerspectiveProjection =
|
||||||
|
ovrMatrix4f_Projection(combined, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded);
|
||||||
|
_eyeProjections[Mono] = toGlm(ovrPerspectiveProjection);
|
||||||
|
|
||||||
|
_desiredFramebufferSize = uvec2(
|
||||||
|
eyeSizes[0].x + eyeSizes[1].x,
|
||||||
|
std::max(eyeSizes[0].y, eyeSizes[1].y));
|
||||||
|
|
||||||
|
_frameIndex = 0;
|
||||||
|
|
||||||
|
if (!OVR_SUCCESS(ovrHmd_ConfigureTracking(_hmd,
|
||||||
|
ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) {
|
||||||
|
qFatal("Could not attach to sensor device");
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowOpenGLDisplayPlugin::activate();
|
||||||
|
|
||||||
int screen = getHmdScreen();
|
int screen = getHmdScreen();
|
||||||
if (screen != -1) {
|
if (screen != -1) {
|
||||||
CONTAINER->setFullscreen(qApp->screens()[screen]);
|
CONTAINER->setFullscreen(qApp->screens()[screen]);
|
||||||
|
@ -118,17 +184,16 @@ void Oculus_0_5_DisplayPlugin::activate() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ovrEyeRenderDesc _eyeRenderDescs[ovrEye_Count];
|
|
||||||
ovrBool result = ovrHmd_ConfigureRendering(_hmd, &config.Config, distortionCaps, _eyeFovs, _eyeRenderDescs);
|
ovrBool result = ovrHmd_ConfigureRendering(_hmd, &config.Config, distortionCaps, _eyeFovs, _eyeRenderDescs);
|
||||||
Q_ASSERT(result);
|
Q_ASSERT(result);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Oculus_0_5_DisplayPlugin::deactivate() {
|
void OculusLegacyDisplayPlugin::deactivate() {
|
||||||
#if (OVR_MAJOR_VERSION == 5)
|
#if (OVR_MAJOR_VERSION == 5)
|
||||||
_window->removeEventFilter(this);
|
_window->removeEventFilter(this);
|
||||||
|
|
||||||
OculusBaseDisplayPlugin::deactivate();
|
WindowOpenGLDisplayPlugin::deactivate();
|
||||||
|
|
||||||
QScreen* riftScreen = nullptr;
|
QScreen* riftScreen = nullptr;
|
||||||
if (_hmdScreen >= 0) {
|
if (_hmdScreen >= 0) {
|
||||||
|
@ -142,18 +207,11 @@ void Oculus_0_5_DisplayPlugin::deactivate() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Oculus_0_5_DisplayPlugin::preRender() {
|
void OculusLegacyDisplayPlugin::preDisplay() {
|
||||||
#if (OVR_MAJOR_VERSION == 5)
|
|
||||||
OculusBaseDisplayPlugin::preRender();
|
|
||||||
ovrHmd_BeginFrame(_hmd, _frameIndex);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void Oculus_0_5_DisplayPlugin::preDisplay() {
|
|
||||||
_window->makeCurrent();
|
_window->makeCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Oculus_0_5_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) {
|
void OculusLegacyDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) {
|
||||||
++_frameIndex;
|
++_frameIndex;
|
||||||
#if (OVR_MAJOR_VERSION == 5)
|
#if (OVR_MAJOR_VERSION == 5)
|
||||||
ovr_for_each_eye([&](ovrEyeType eye) {
|
ovr_for_each_eye([&](ovrEyeType eye) {
|
||||||
|
@ -164,7 +222,7 @@ void Oculus_0_5_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass input events on to the application
|
// Pass input events on to the application
|
||||||
bool Oculus_0_5_DisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
|
bool OculusLegacyDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
|
||||||
#if (OVR_MAJOR_VERSION == 5)
|
#if (OVR_MAJOR_VERSION == 5)
|
||||||
if (!_hswDismissed && (event->type() == QEvent::KeyPress)) {
|
if (!_hswDismissed && (event->type() == QEvent::KeyPress)) {
|
||||||
static ovrHSWDisplayState hswState;
|
static ovrHSWDisplayState hswState;
|
||||||
|
@ -176,17 +234,19 @@ bool Oculus_0_5_DisplayPlugin::eventFilter(QObject* receiver, QEvent* event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return OculusBaseDisplayPlugin::eventFilter(receiver, event);
|
return WindowOpenGLDisplayPlugin::eventFilter(receiver, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME mirroring tot he main window is diffucult on OSX because it requires that we
|
// FIXME mirroring tot he main window is diffucult on OSX because it requires that we
|
||||||
// trigger a swap, which causes the client to wait for the v-sync of the main screen running
|
// trigger a swap, which causes the client to wait for the v-sync of the main screen running
|
||||||
// at 60 Hz. This would introduce judder. Perhaps we can push mirroring to a separate
|
// at 60 Hz. This would introduce judder. Perhaps we can push mirroring to a separate
|
||||||
// thread
|
// thread
|
||||||
void Oculus_0_5_DisplayPlugin::finishFrame() {
|
// FIXME If we move to the 'batch rendering on a different thread' we can possibly do this.
|
||||||
|
// however, we need to make sure it doesn't block the event handling.
|
||||||
|
void OculusLegacyDisplayPlugin::finishFrame() {
|
||||||
_window->doneCurrent();
|
_window->doneCurrent();
|
||||||
};
|
};
|
||||||
|
|
||||||
int Oculus_0_5_DisplayPlugin::getHmdScreen() const {
|
int OculusLegacyDisplayPlugin::getHmdScreen() const {
|
||||||
return _hmdScreen;
|
return _hmdScreen;
|
||||||
}
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2015/05/29
|
||||||
|
// 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 "../WindowOpenGLDisplayPlugin.h"
|
||||||
|
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include <OVR_CAPI.h>
|
||||||
|
|
||||||
|
class OculusLegacyDisplayPlugin : public WindowOpenGLDisplayPlugin {
|
||||||
|
public:
|
||||||
|
OculusLegacyDisplayPlugin();
|
||||||
|
virtual bool isSupported() const override;
|
||||||
|
virtual const QString & getName() const override;
|
||||||
|
|
||||||
|
virtual void activate() override;
|
||||||
|
virtual void deactivate() override;
|
||||||
|
|
||||||
|
virtual bool eventFilter(QObject* receiver, QEvent* event) override;
|
||||||
|
virtual int getHmdScreen() const override;
|
||||||
|
|
||||||
|
// Stereo specific methods
|
||||||
|
virtual bool isHmd() const override { return true; }
|
||||||
|
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
|
||||||
|
virtual glm::uvec2 getRecommendedRenderSize() const override;
|
||||||
|
virtual glm::uvec2 getRecommendedUiSize() const override { return uvec2(1920, 1080); }
|
||||||
|
virtual void resetSensors() override;
|
||||||
|
virtual glm::mat4 getEyePose(Eye eye) const override;
|
||||||
|
virtual glm::mat4 getHeadPose() const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void preRender() override;
|
||||||
|
virtual void preDisplay() override;
|
||||||
|
virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override;
|
||||||
|
// Do not perform swap in finish
|
||||||
|
virtual void finishFrame() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const QString NAME;
|
||||||
|
|
||||||
|
float _ipd{ OVR_DEFAULT_IPD };
|
||||||
|
ovrHmd _hmd;
|
||||||
|
unsigned int _frameIndex;
|
||||||
|
ovrTrackingState _trackingState;
|
||||||
|
ovrEyeRenderDesc _eyeRenderDescs[2];
|
||||||
|
ovrPosef _eyePoses[2];
|
||||||
|
ovrVector3f _eyeOffsets[2];
|
||||||
|
ovrFovPort _eyeFovs[2];
|
||||||
|
mat4 _eyeProjections[3];
|
||||||
|
mat4 _compositeEyeProjections[2];
|
||||||
|
uvec2 _desiredFramebufferSize;
|
||||||
|
ovrTexture _eyeTextures[2];
|
||||||
|
mutable int _hmdScreen{ -1 };
|
||||||
|
bool _hswDismissed{ false };
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
//
|
|
||||||
// Created by Bradley Austin Davis on 2015/05/29
|
|
||||||
// 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 "OculusBaseDisplayPlugin.h"
|
|
||||||
|
|
||||||
#include <QTimer>
|
|
||||||
|
|
||||||
class Oculus_0_5_DisplayPlugin : public OculusBaseDisplayPlugin {
|
|
||||||
public:
|
|
||||||
virtual bool isSupported() const override;
|
|
||||||
virtual const QString & getName() const override;
|
|
||||||
|
|
||||||
virtual void activate() override;
|
|
||||||
virtual void deactivate() override;
|
|
||||||
|
|
||||||
virtual bool eventFilter(QObject* receiver, QEvent* event) override;
|
|
||||||
|
|
||||||
virtual int getHmdScreen() const override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void preRender() override;
|
|
||||||
virtual void preDisplay() override;
|
|
||||||
virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override;
|
|
||||||
// Do not perform swap in finish
|
|
||||||
virtual void finishFrame() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
static const QString NAME;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
//
|
|
||||||
// Created by Bradley Austin Davis on 2015/05/29
|
|
||||||
// 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 "OculusBaseDisplayPlugin.h"
|
|
||||||
|
|
||||||
#include <QTimer>
|
|
||||||
|
|
||||||
class OffscreenGlCanvas;
|
|
||||||
struct SwapFramebufferWrapper;
|
|
||||||
struct MirrorFramebufferWrapper;
|
|
||||||
|
|
||||||
using SwapFboPtr = QSharedPointer<SwapFramebufferWrapper>;
|
|
||||||
using MirrorFboPtr = QSharedPointer<MirrorFramebufferWrapper>;
|
|
||||||
|
|
||||||
class Oculus_0_6_DisplayPlugin : public OculusBaseDisplayPlugin {
|
|
||||||
public:
|
|
||||||
virtual bool isSupported() const override;
|
|
||||||
virtual const QString & getName() const override;
|
|
||||||
|
|
||||||
virtual void activate() override;
|
|
||||||
virtual void deactivate() override;
|
|
||||||
|
|
||||||
|
|
||||||
virtual bool eventFilter(QObject* receiver, QEvent* event) override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override;
|
|
||||||
virtual void customizeContext() override;
|
|
||||||
// Do not perform swap in finish
|
|
||||||
virtual void finishFrame() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
static const QString NAME;
|
|
||||||
};
|
|
||||||
|
|
|
@ -128,7 +128,7 @@ void OpenVrDisplayPlugin::activate() {
|
||||||
delete[] buffer;
|
delete[] buffer;
|
||||||
}
|
}
|
||||||
Q_ASSERT(unSize <= 1);
|
Q_ASSERT(unSize <= 1);
|
||||||
MainWindowOpenGLDisplayPlugin::activate();
|
WindowOpenGLDisplayPlugin::activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenVrDisplayPlugin::deactivate() {
|
void OpenVrDisplayPlugin::deactivate() {
|
||||||
|
@ -141,7 +141,7 @@ void OpenVrDisplayPlugin::deactivate() {
|
||||||
_hmd = nullptr;
|
_hmd = nullptr;
|
||||||
}
|
}
|
||||||
_compositor = nullptr;
|
_compositor = nullptr;
|
||||||
MainWindowOpenGLDisplayPlugin::deactivate();
|
WindowOpenGLDisplayPlugin::deactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
uvec2 OpenVrDisplayPlugin::getRecommendedRenderSize() const {
|
uvec2 OpenVrDisplayPlugin::getRecommendedRenderSize() const {
|
||||||
|
@ -149,19 +149,19 @@ uvec2 OpenVrDisplayPlugin::getRecommendedRenderSize() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
mat4 OpenVrDisplayPlugin::getProjection(Eye eye, const mat4& baseProjection) const {
|
mat4 OpenVrDisplayPlugin::getProjection(Eye eye, const mat4& baseProjection) const {
|
||||||
|
// FIXME hack to ensure that we don't crash trying to get the combined matrix
|
||||||
|
if (eye == Mono) {
|
||||||
|
eye = Left;
|
||||||
|
}
|
||||||
return _eyesData[eye]._projectionMatrix;
|
return _eyesData[eye]._projectionMatrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::mat4 OpenVrDisplayPlugin::getModelview(Eye eye, const mat4& baseModelview) const {
|
|
||||||
return baseModelview * getEyePose(eye);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpenVrDisplayPlugin::resetSensors() {
|
void OpenVrDisplayPlugin::resetSensors() {
|
||||||
_sensorResetMat = glm::inverse(cancelOutRollAndPitch(_trackedDevicePoseMat4[0]));
|
_sensorResetMat = glm::inverse(cancelOutRollAndPitch(_trackedDevicePoseMat4[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::mat4 OpenVrDisplayPlugin::getEyePose(Eye eye) const {
|
glm::mat4 OpenVrDisplayPlugin::getEyePose(Eye eye) const {
|
||||||
return getHeadPose() * _eyesData[eye]._eyeOffset;
|
return _eyesData[eye]._eyeOffset * getHeadPose();
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::mat4 OpenVrDisplayPlugin::getHeadPose() const {
|
glm::mat4 OpenVrDisplayPlugin::getHeadPose() const {
|
||||||
|
@ -169,7 +169,7 @@ glm::mat4 OpenVrDisplayPlugin::getHeadPose() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenVrDisplayPlugin::customizeContext() {
|
void OpenVrDisplayPlugin::customizeContext() {
|
||||||
MainWindowOpenGLDisplayPlugin::customizeContext();
|
WindowOpenGLDisplayPlugin::customizeContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenVrDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) {
|
void OpenVrDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) {
|
||||||
|
|
|
@ -11,9 +11,9 @@
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
|
|
||||||
#include "../MainWindowOpenGLDisplayPlugin.h"
|
#include "../WindowOpenGLDisplayPlugin.h"
|
||||||
|
|
||||||
class OpenVrDisplayPlugin : public MainWindowOpenGLDisplayPlugin {
|
class OpenVrDisplayPlugin : public WindowOpenGLDisplayPlugin {
|
||||||
public:
|
public:
|
||||||
virtual bool isSupported() const override;
|
virtual bool isSupported() const override;
|
||||||
virtual const QString & getName() const override;
|
virtual const QString & getName() const override;
|
||||||
|
@ -27,7 +27,6 @@ public:
|
||||||
|
|
||||||
// Stereo specific methods
|
// Stereo specific methods
|
||||||
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
|
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
|
||||||
virtual glm::mat4 getModelview(Eye eye, const glm::mat4& baseModelview) const override;
|
|
||||||
virtual void resetSensors() override;
|
virtual void resetSensors() override;
|
||||||
|
|
||||||
virtual glm::mat4 getEyePose(Eye eye) const override;
|
virtual glm::mat4 getEyePose(Eye eye) const override;
|
||||||
|
|
|
@ -17,6 +17,42 @@
|
||||||
|
|
||||||
#include <gpu/GLBackend.h>
|
#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::NAME("Interleaved Stereo Display");
|
||||||
|
|
||||||
const QString & InterleavedStereoDisplayPlugin::getName() const {
|
const QString & InterleavedStereoDisplayPlugin::getName() const {
|
||||||
|
@ -29,5 +65,20 @@ InterleavedStereoDisplayPlugin::InterleavedStereoDisplayPlugin() {
|
||||||
void InterleavedStereoDisplayPlugin::customizeContext() {
|
void InterleavedStereoDisplayPlugin::customizeContext() {
|
||||||
StereoDisplayPlugin::customizeContext();
|
StereoDisplayPlugin::customizeContext();
|
||||||
// Set up the stencil buffers? Or use a custom shader?
|
// 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
|
// initialize OpenGL context settings needed by the plugin
|
||||||
virtual void customizeContext() override;
|
virtual void customizeContext() override;
|
||||||
|
|
||||||
|
virtual glm::uvec2 getRecommendedRenderSize() const override;
|
||||||
|
void display(GLuint finalTexture, const glm::uvec2& sceneSize) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const QString NAME;
|
static const QString NAME;
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,14 +10,16 @@
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDesktopWidget>
|
#include <QDesktopWidget>
|
||||||
|
#include <QScreen>
|
||||||
|
|
||||||
#include <GlWindow.h>
|
#include <GlWindow.h>
|
||||||
#include <ViewFrustum.h>
|
#include <ViewFrustum.h>
|
||||||
#include <MatrixStack.h>
|
#include <MatrixStack.h>
|
||||||
|
|
||||||
#include <gpu/GLBackend.h>
|
#include <gpu/GLBackend.h>
|
||||||
|
#include <plugins/PluginContainer.h>
|
||||||
|
|
||||||
const QString SideBySideStereoDisplayPlugin::NAME("SBS Stereo Display");
|
const QString SideBySideStereoDisplayPlugin::NAME("3D TV - Side by Side Stereo");
|
||||||
|
|
||||||
const QString & SideBySideStereoDisplayPlugin::getName() const {
|
const QString & SideBySideStereoDisplayPlugin::getName() const {
|
||||||
return NAME;
|
return NAME;
|
||||||
|
@ -26,3 +28,10 @@ const QString & SideBySideStereoDisplayPlugin::getName() const {
|
||||||
SideBySideStereoDisplayPlugin::SideBySideStereoDisplayPlugin() {
|
SideBySideStereoDisplayPlugin::SideBySideStereoDisplayPlugin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glm::uvec2 SideBySideStereoDisplayPlugin::getRecommendedRenderSize() const {
|
||||||
|
uvec2 result = WindowOpenGLDisplayPlugin::getRecommendedRenderSize();
|
||||||
|
result.x *= 2;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,14 @@
|
||||||
|
|
||||||
#include "StereoDisplayPlugin.h"
|
#include "StereoDisplayPlugin.h"
|
||||||
|
|
||||||
|
class QScreen;
|
||||||
|
|
||||||
class SideBySideStereoDisplayPlugin : public StereoDisplayPlugin {
|
class SideBySideStereoDisplayPlugin : public StereoDisplayPlugin {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
SideBySideStereoDisplayPlugin();
|
SideBySideStereoDisplayPlugin();
|
||||||
virtual const QString & getName() const override;
|
virtual const QString& getName() const override;
|
||||||
|
virtual glm::uvec2 getRecommendedRenderSize() const override;
|
||||||
private:
|
private:
|
||||||
static const QString NAME;
|
static const QString NAME;
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,11 +10,14 @@
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDesktopWidget>
|
#include <QDesktopWidget>
|
||||||
|
#include <QAction>
|
||||||
|
|
||||||
#include <gpu/GLBackend.h>
|
#include <gpu/GLBackend.h>
|
||||||
#include <ViewFrustum.h>
|
#include <ViewFrustum.h>
|
||||||
#include <MatrixStack.h>
|
#include <MatrixStack.h>
|
||||||
#include <plugins/PluginContainer.h>
|
#include <plugins/PluginContainer.h>
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#include <QScreen>
|
||||||
|
|
||||||
StereoDisplayPlugin::StereoDisplayPlugin() {
|
StereoDisplayPlugin::StereoDisplayPlugin() {
|
||||||
}
|
}
|
||||||
|
@ -32,6 +35,11 @@ glm::mat4 StereoDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProje
|
||||||
// Refer to http://www.nvidia.com/content/gtc-2010/pdfs/2010_gtc2010.pdf on creating
|
// 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.
|
// 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 nearZ = DEFAULT_NEAR_CLIP; // near clipping plane
|
||||||
float screenZ = 0.25f; // screen projection plane
|
float screenZ = 0.25f; // screen projection plane
|
||||||
// FIXME verify this is the right calculation
|
// FIXME verify this is the right calculation
|
||||||
|
@ -42,16 +50,44 @@ glm::mat4 StereoDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProje
|
||||||
return glm::translate(baseProjection, vec3(frustumshift, 0, 0));
|
return glm::translate(baseProjection, vec3(frustumshift, 0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::mat4 StereoDisplayPlugin::getModelview(Eye eye, const glm::mat4& baseModelview) const {
|
glm::mat4 StereoDisplayPlugin::getEyePose(Eye eye) const {
|
||||||
float modelviewShift = HALF_DEFAULT_IPD;
|
float modelviewShift = HALF_DEFAULT_IPD;
|
||||||
if (eye == Left) {
|
if (eye == Left) {
|
||||||
modelviewShift = -modelviewShift;
|
modelviewShift = -modelviewShift;
|
||||||
}
|
}
|
||||||
return baseModelview * glm::translate(mat4(), vec3(modelviewShift, 0, 0));
|
return glm::translate(mat4(), vec3(modelviewShift, 0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<QAction*> _screenActions;
|
||||||
void StereoDisplayPlugin::activate() {
|
void StereoDisplayPlugin::activate() {
|
||||||
WindowOpenGLDisplayPlugin::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());
|
CONTAINER->setFullscreen(qApp->primaryScreen());
|
||||||
// FIXME Add menu items
|
WindowOpenGLDisplayPlugin::activate();
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,9 @@
|
||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../MainWindowOpenGLDisplayPlugin.h"
|
#include "../WindowOpenGLDisplayPlugin.h"
|
||||||
|
|
||||||
class StereoDisplayPlugin : public MainWindowOpenGLDisplayPlugin {
|
class StereoDisplayPlugin : public WindowOpenGLDisplayPlugin {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
StereoDisplayPlugin();
|
StereoDisplayPlugin();
|
||||||
|
@ -17,8 +17,12 @@ public:
|
||||||
virtual bool isSupported() const override final;
|
virtual bool isSupported() const override final;
|
||||||
|
|
||||||
virtual void activate() override;
|
virtual void activate() override;
|
||||||
|
virtual void deactivate() override;
|
||||||
|
|
||||||
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
|
virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override;
|
||||||
virtual glm::mat4 getModelview(Eye eye, const glm::mat4& baseModelview) 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)
|
_port(port)
|
||||||
{
|
{
|
||||||
bindSocket();
|
bindSocket();
|
||||||
|
|
||||||
_isListeningTimer = new QTimer(this);
|
_isListeningTimer = new QTimer(this);
|
||||||
connect(_isListeningTimer, &QTimer::timeout, this, &HTTPManager::isTcpServerListening);
|
connect(_isListeningTimer, &QTimer::timeout, this, &HTTPManager::isTcpServerListening);
|
||||||
_isListeningTimer->start(SOCKET_CHECK_INTERVAL_IN_MS);
|
_isListeningTimer->start(SOCKET_CHECK_INTERVAL_IN_MS);
|
||||||
|
@ -171,9 +172,19 @@ void HTTPManager::isTcpServerListening() {
|
||||||
|
|
||||||
bool HTTPManager::bindSocket() {
|
bool HTTPManager::bindSocket() {
|
||||||
qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port);
|
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";
|
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:
|
private slots:
|
||||||
void isTcpServerListening();
|
void isTcpServerListening();
|
||||||
|
void queuedExit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool bindSocket();
|
bool bindSocket();
|
||||||
|
|
|
@ -288,3 +288,18 @@ void Batch::resetStages() {
|
||||||
ADD_COMMAND(resetStages);
|
ADD_COMMAND(resetStages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Batch::enableStereo(bool enable) {
|
||||||
|
_enableStereo = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Batch::isStereoEnabled() const {
|
||||||
|
return _enableStereo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Batch::enableSkybox(bool enable) {
|
||||||
|
_enableSkybox = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Batch::isSkyboxEnabled() const {
|
||||||
|
return _enableSkybox;
|
||||||
|
}
|
|
@ -26,7 +26,7 @@
|
||||||
ProfileRange(const char *name);
|
ProfileRange(const char *name);
|
||||||
~ProfileRange();
|
~ProfileRange();
|
||||||
};
|
};
|
||||||
#define PROFILE_RANGE(name) ProfileRange profileRangeThis(name);
|
#define PROFILE_RANGE(name) ProfileRange profileRangeThis(name);
|
||||||
#else
|
#else
|
||||||
#define PROFILE_RANGE(name)
|
#define PROFILE_RANGE(name)
|
||||||
#endif
|
#endif
|
||||||
|
@ -47,6 +47,19 @@ public:
|
||||||
~Batch();
|
~Batch();
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
// Batches may need to override the context level stereo settings
|
||||||
|
// if they're performing framebuffer copy operations, like the
|
||||||
|
// deferred lighting resolution mechanism
|
||||||
|
void enableStereo(bool enable = true);
|
||||||
|
bool isStereoEnabled() const;
|
||||||
|
|
||||||
|
// Stereo batches will pre-translate the view matrix, but this isn't
|
||||||
|
// appropriate for skyboxes or other things intended to be drawn at
|
||||||
|
// infinite distance, so provide a mechanism to render in stereo
|
||||||
|
// without the pre-translation of the view.
|
||||||
|
void enableSkybox(bool enable = true);
|
||||||
|
bool isSkyboxEnabled() const;
|
||||||
|
|
||||||
// Drawcalls
|
// Drawcalls
|
||||||
void draw(Primitive primitiveType, uint32 numVertices, uint32 startVertex = 0);
|
void draw(Primitive primitiveType, uint32 numVertices, uint32 startVertex = 0);
|
||||||
|
@ -276,6 +289,9 @@ public:
|
||||||
FramebufferCaches _framebuffers;
|
FramebufferCaches _framebuffers;
|
||||||
QueryCaches _queries;
|
QueryCaches _queries;
|
||||||
|
|
||||||
|
bool _enableStereo{ true };
|
||||||
|
bool _enableSkybox{ false };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,18 @@ void Context::render(Batch& batch) {
|
||||||
_backend->render(batch);
|
_backend->render(batch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Context::enableStereo(bool enable) {
|
||||||
|
_backend->enableStereo(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Context::setStereoProjections(const mat4 eyeProjections[2]) {
|
||||||
|
_backend->setStereoProjections(eyeProjections);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Context::setStereoViews(const mat4 eyeViews[2]) {
|
||||||
|
_backend->setStereoViews(eyeViews);
|
||||||
|
}
|
||||||
|
|
||||||
void Context::syncCache() {
|
void Context::syncCache() {
|
||||||
PROFILE_RANGE(__FUNCTION__);
|
PROFILE_RANGE(__FUNCTION__);
|
||||||
_backend->syncCache();
|
_backend->syncCache();
|
||||||
|
@ -49,3 +61,26 @@ void Context::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, cons
|
||||||
_backend->downloadFramebuffer(srcFramebuffer, region, destImage);
|
_backend->downloadFramebuffer(srcFramebuffer, region, destImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Backend::TransformCamera& Backend::TransformCamera::recomputeDerived() const {
|
||||||
|
_projectionInverse = glm::inverse(_projection);
|
||||||
|
_viewInverse = glm::inverse(_view);
|
||||||
|
|
||||||
|
Mat4 viewUntranslated = _view;
|
||||||
|
viewUntranslated[3] = Vec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
_projectionViewUntranslated = _projection * viewUntranslated;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Backend::TransformCamera Backend::TransformCamera::getEyeCamera(int eye, const StereoState& _stereo) const {
|
||||||
|
TransformCamera result = *this;
|
||||||
|
if (!_stereo._skybox) {
|
||||||
|
result._view = _stereo._eyeViews[eye] * result._view;
|
||||||
|
} else {
|
||||||
|
glm::mat4 skyboxView = _stereo._eyeViews[eye];
|
||||||
|
skyboxView[3] = vec4(0, 0, 0, 1);
|
||||||
|
result._view = skyboxView * result._view;
|
||||||
|
}
|
||||||
|
result._projection = _stereo._eyeProjections[eye];
|
||||||
|
result.recomputeDerived();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <GLMHelpers.h>
|
||||||
|
|
||||||
#include "Batch.h"
|
#include "Batch.h"
|
||||||
|
|
||||||
#include "Resource.h"
|
#include "Resource.h"
|
||||||
|
@ -25,28 +27,58 @@ class QImage;
|
||||||
|
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
|
||||||
|
struct StereoState {
|
||||||
|
bool _enable{ false };
|
||||||
|
bool _skybox{ false };
|
||||||
|
// 0 for left eye, 1 for right eye
|
||||||
|
uint8_t _pass{ 0 };
|
||||||
|
mat4 _eyeViews[2];
|
||||||
|
mat4 _eyeProjections[2];
|
||||||
|
};
|
||||||
|
|
||||||
class Backend {
|
class Backend {
|
||||||
public:
|
public:
|
||||||
virtual~ Backend() {};
|
virtual~ Backend() {};
|
||||||
|
|
||||||
virtual void render(Batch& batch) = 0;
|
virtual void render(Batch& batch) = 0;
|
||||||
|
virtual void enableStereo(bool enable) {
|
||||||
|
_stereo._enable = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setStereoProjections(const mat4 eyeProjections[2]) {
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
_stereo._eyeProjections[i] = eyeProjections[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setStereoViews(const mat4 views[2]) {
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
_stereo._eyeViews[i] = views[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
virtual void syncCache() = 0;
|
virtual void syncCache() = 0;
|
||||||
virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0;
|
virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0;
|
||||||
|
|
||||||
|
// UBO class... layout MUST match the layout in TransformCamera.slh
|
||||||
class TransformObject {
|
class TransformObject {
|
||||||
public:
|
public:
|
||||||
Mat4 _model;
|
Mat4 _model;
|
||||||
Mat4 _modelInverse;
|
Mat4 _modelInverse;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// UBO class... layout MUST match the layout in TransformCamera.slh
|
||||||
class TransformCamera {
|
class TransformCamera {
|
||||||
public:
|
public:
|
||||||
Mat4 _view;
|
Mat4 _view;
|
||||||
Mat4 _viewInverse;
|
mutable Mat4 _viewInverse;
|
||||||
Mat4 _projectionViewUntranslated;
|
mutable Mat4 _projectionViewUntranslated;
|
||||||
Mat4 _projection;
|
Mat4 _projection;
|
||||||
Mat4 _projectionInverse;
|
mutable Mat4 _projectionInverse;
|
||||||
Vec4 _viewport; // Public value is int but float in the shader to stay in floats for all the transform computations.
|
Vec4 _viewport; // Public value is int but float in the shader to stay in floats for all the transform computations.
|
||||||
|
|
||||||
|
const Backend::TransformCamera& recomputeDerived() const;
|
||||||
|
TransformCamera getEyeCamera(int eye, const StereoState& stereo) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
template< typename T >
|
template< typename T >
|
||||||
|
@ -113,7 +145,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
StereoState _stereo;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Context {
|
class Context {
|
||||||
|
@ -136,7 +168,9 @@ public:
|
||||||
~Context();
|
~Context();
|
||||||
|
|
||||||
void render(Batch& batch);
|
void render(Batch& batch);
|
||||||
|
void enableStereo(bool enable = true);
|
||||||
|
void setStereoProjections(const mat4 eyeProjections[2]);
|
||||||
|
void setStereoViews(const mat4 eyeViews[2]);
|
||||||
void syncCache();
|
void syncCache();
|
||||||
|
|
||||||
// Downloading the Framebuffer is a synchronous action that is not efficient.
|
// Downloading the Framebuffer is a synchronous action that is not efficient.
|
||||||
|
|
77
libraries/gpu/src/gpu/Forward.h
Normal file
77
libraries/gpu/src/gpu/Forward.h
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2015/08/15
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
#ifndef hifi_gpu_Forward_h
|
||||||
|
#define hifi_gpu_Forward_h
|
||||||
|
|
||||||
|
namespace gpu {
|
||||||
|
class Batch;
|
||||||
|
class Backend;
|
||||||
|
class Context;
|
||||||
|
typedef std::shared_ptr<Context> ContextPointer;
|
||||||
|
class GPUObject;
|
||||||
|
|
||||||
|
typedef int Stamp;
|
||||||
|
typedef uint32_t uint32;
|
||||||
|
typedef int32_t int32;
|
||||||
|
typedef uint16_t uint16;
|
||||||
|
typedef int16_t int16;
|
||||||
|
typedef uint8_t uint8;
|
||||||
|
typedef int8_t int8;
|
||||||
|
|
||||||
|
typedef uint8 Byte;
|
||||||
|
typedef uint32 Offset;
|
||||||
|
typedef std::vector<Offset> Offsets;
|
||||||
|
|
||||||
|
typedef glm::mat4 Mat4;
|
||||||
|
typedef glm::mat3 Mat3;
|
||||||
|
typedef glm::vec4 Vec4;
|
||||||
|
typedef glm::ivec4 Vec4i;
|
||||||
|
typedef glm::vec3 Vec3;
|
||||||
|
typedef glm::vec2 Vec2;
|
||||||
|
typedef glm::ivec2 Vec2i;
|
||||||
|
typedef glm::uvec2 Vec2u;
|
||||||
|
|
||||||
|
class Element;
|
||||||
|
typedef Element Format;
|
||||||
|
class Swapchain;
|
||||||
|
typedef std::shared_ptr<Swapchain> SwapchainPointer;
|
||||||
|
class Framebuffer;
|
||||||
|
typedef std::shared_ptr<Framebuffer> FramebufferPointer;
|
||||||
|
class Pipeline;
|
||||||
|
typedef std::shared_ptr<Pipeline> PipelinePointer;
|
||||||
|
typedef std::vector<PipelinePointer> Pipelines;
|
||||||
|
class Query;
|
||||||
|
typedef std::shared_ptr<Query> QueryPointer;
|
||||||
|
typedef std::vector<QueryPointer> Queries;
|
||||||
|
class Resource;
|
||||||
|
class Buffer;
|
||||||
|
typedef std::shared_ptr<Buffer> BufferPointer;
|
||||||
|
typedef std::vector<BufferPointer> Buffers;
|
||||||
|
class BufferView;
|
||||||
|
class Shader;
|
||||||
|
typedef Shader::Pointer ShaderPointer;
|
||||||
|
typedef std::vector<ShaderPointer> Shaders;
|
||||||
|
class State;
|
||||||
|
typedef std::shared_ptr<State> StatePointer;
|
||||||
|
typedef std::vector<StatePointer> States;
|
||||||
|
class Stream;
|
||||||
|
class BufferStream;
|
||||||
|
typedef std::shared_ptr<BufferStream> BufferStreamPointer;
|
||||||
|
class Texture;
|
||||||
|
class SphericalHarmonics;
|
||||||
|
typedef std::shared_ptr<SphericalHarmonics> SHPointer;
|
||||||
|
class Sampler;
|
||||||
|
class Texture;
|
||||||
|
typedef std::shared_ptr<Texture> TexturePointer;
|
||||||
|
typedef std::vector<TexturePointer> Textures;
|
||||||
|
class TextureView;
|
||||||
|
typedef std::vector<TextureView> TextureViews;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -127,39 +127,29 @@ void GLBackend::renderPassTransfer(Batch& batch) {
|
||||||
const Batch::Commands::value_type* command = batch.getCommands().data();
|
const Batch::Commands::value_type* command = batch.getCommands().data();
|
||||||
const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data();
|
const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data();
|
||||||
|
|
||||||
_transform._cameraTransforms.resize(0);
|
// Reset the transform buffers
|
||||||
_transform._cameraTransforms.push_back(TransformCamera());
|
_transform._cameras.resize(0);
|
||||||
_transform._cameraOffsets.clear();
|
_transform._cameraOffsets.clear();
|
||||||
_transform._cameraOffsets.push_back(TransformStageState::Pair(0, 0));
|
_transform._objects.resize(0);
|
||||||
|
|
||||||
_transform._objectTransforms.push_back(TransformObject());
|
|
||||||
_transform._objectOffsets.push_back(TransformStageState::Pair(0, 0));
|
|
||||||
_transform._objectOffsets.clear();
|
_transform._objectOffsets.clear();
|
||||||
_transform._objectTransforms.resize(0);
|
|
||||||
|
|
||||||
_commandIndex = 0;
|
|
||||||
preUpdateTransform();
|
|
||||||
|
|
||||||
int drawCount = 0;
|
|
||||||
for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) {
|
for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) {
|
||||||
switch (*command) {
|
switch (*command) {
|
||||||
case Batch::COMMAND_draw:
|
case Batch::COMMAND_draw:
|
||||||
case Batch::COMMAND_drawIndexed:
|
case Batch::COMMAND_drawIndexed:
|
||||||
case Batch::COMMAND_drawInstanced:
|
case Batch::COMMAND_drawInstanced:
|
||||||
case Batch::COMMAND_drawIndexedInstanced:
|
case Batch::COMMAND_drawIndexedInstanced:
|
||||||
preUpdateTransform();
|
_transform.preUpdate(_commandIndex, _stereo);
|
||||||
++drawCount;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Batch::COMMAND_setModelTransform:
|
case Batch::COMMAND_setModelTransform:
|
||||||
case Batch::COMMAND_setViewportTransform:
|
case Batch::COMMAND_setViewportTransform:
|
||||||
case Batch::COMMAND_setViewTransform:
|
case Batch::COMMAND_setViewTransform:
|
||||||
case Batch::COMMAND_setProjectionTransform:
|
case Batch::COMMAND_setProjectionTransform: {
|
||||||
{
|
|
||||||
CommandCall call = _commandCalls[(*command)];
|
CommandCall call = _commandCalls[(*command)];
|
||||||
(this->*(call))(batch, *offset);
|
(this->*(call))(batch, *offset);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -167,44 +157,31 @@ void GLBackend::renderPassTransfer(Batch& batch) {
|
||||||
command++;
|
command++;
|
||||||
offset++;
|
offset++;
|
||||||
}
|
}
|
||||||
|
_transform.transfer();
|
||||||
|
|
||||||
static QByteArray bufferData;
|
|
||||||
glBindBuffer(GL_UNIFORM_BUFFER, _transform._transformCameraBuffer);
|
|
||||||
bufferData.resize(_transform._cameraUboSize * _transform._cameraTransforms.size());
|
|
||||||
for (size_t i = 0; i < _transform._cameraTransforms.size(); ++i) {
|
|
||||||
memcpy(bufferData.data() + (_transform._cameraUboSize * i), &_transform._cameraTransforms[i], sizeof(TransformCamera));
|
|
||||||
}
|
|
||||||
glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW);
|
|
||||||
glBindBuffer(GL_UNIFORM_BUFFER, _transform._transformObjectBuffer);
|
|
||||||
bufferData.resize(_transform._objectUboSize * _transform._objectTransforms.size());
|
|
||||||
for (size_t i = 0; i < _transform._objectTransforms.size(); ++i) {
|
|
||||||
memcpy(bufferData.data() + (_transform._objectUboSize * i), &_transform._objectTransforms[i], sizeof(TransformObject));
|
|
||||||
}
|
|
||||||
glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW);
|
|
||||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
|
||||||
CHECK_GL_ERROR();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::renderPassDraw(Batch& batch) {
|
void GLBackend::renderPassDraw(Batch& batch) {
|
||||||
|
_transform._objectsItr = _transform._objectOffsets.begin();
|
||||||
|
_transform._camerasItr = _transform._cameraOffsets.begin();
|
||||||
const size_t numCommands = batch.getCommands().size();
|
const size_t numCommands = batch.getCommands().size();
|
||||||
const Batch::Commands::value_type* command = batch.getCommands().data();
|
const Batch::Commands::value_type* command = batch.getCommands().data();
|
||||||
const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data();
|
const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data();
|
||||||
for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) {
|
for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) {
|
||||||
switch (*command) {
|
switch (*command) {
|
||||||
// Ignore these commands on this pass, taken care of in the transfer pass
|
// Ignore these commands on this pass, taken care of in the transfer pass
|
||||||
|
// Note we allow COMMAND_setViewportTransform to occur in both passes
|
||||||
|
// as it both updates the transform object (and thus the uniforms in the
|
||||||
|
// UBO) as well as executes the actual viewport call
|
||||||
case Batch::COMMAND_setModelTransform:
|
case Batch::COMMAND_setModelTransform:
|
||||||
case Batch::COMMAND_setViewportTransform:
|
|
||||||
case Batch::COMMAND_setViewTransform:
|
case Batch::COMMAND_setViewTransform:
|
||||||
case Batch::COMMAND_setProjectionTransform:
|
case Batch::COMMAND_setProjectionTransform:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default: {
|
||||||
{
|
|
||||||
CommandCall call = _commandCalls[(*command)];
|
CommandCall call = _commandCalls[(*command)];
|
||||||
(this->*(call))(batch, *offset);
|
(this->*(call))(batch, *offset);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
command++;
|
command++;
|
||||||
|
@ -213,8 +190,33 @@ void GLBackend::renderPassDraw(Batch& batch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::render(Batch& batch) {
|
void GLBackend::render(Batch& batch) {
|
||||||
renderPassTransfer(batch);
|
_stereo._skybox = batch.isSkyboxEnabled();
|
||||||
renderPassDraw(batch);
|
// Allow the batch to override the rendering stereo settings
|
||||||
|
// for things like full framebuffer copy operations (deferred lighting passes)
|
||||||
|
bool savedStereo = _stereo._enable;
|
||||||
|
if (!batch.isStereoEnabled()) {
|
||||||
|
_stereo._enable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
PROFILE_RANGE("Transfer");
|
||||||
|
renderPassTransfer(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
PROFILE_RANGE(_stereo._enable ? "LeftRender" : "Render");
|
||||||
|
renderPassDraw(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_stereo._enable) {
|
||||||
|
PROFILE_RANGE("RightRender");
|
||||||
|
_stereo._pass = 1;
|
||||||
|
renderPassDraw(batch);
|
||||||
|
_stereo._pass = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore the saved stereo state for the next batch
|
||||||
|
_stereo._enable = savedStereo;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLBackend::checkGLError(const char* name) {
|
bool GLBackend::checkGLError(const char* name) {
|
||||||
|
|
|
@ -310,19 +310,22 @@ protected:
|
||||||
void killTransform();
|
void killTransform();
|
||||||
// Synchronize the state cache of this Backend with the actual real state of the GL Context
|
// Synchronize the state cache of this Backend with the actual real state of the GL Context
|
||||||
void syncTransformStateCache();
|
void syncTransformStateCache();
|
||||||
void updateTransform();
|
void updateTransform() const;
|
||||||
void preUpdateTransform();
|
|
||||||
void resetTransformStage();
|
void resetTransformStage();
|
||||||
struct TransformStageState {
|
|
||||||
TransformObject _transformObject;
|
|
||||||
TransformCamera _transformCamera;
|
|
||||||
|
|
||||||
std::vector<TransformObject> _objectTransforms;
|
struct TransformStageState {
|
||||||
std::vector<TransformCamera> _cameraTransforms;
|
using TransformObjects = std::vector<TransformObject>;
|
||||||
|
using TransformCameras = std::vector<TransformCamera>;
|
||||||
|
|
||||||
|
TransformObject _object;
|
||||||
|
TransformCamera _camera;
|
||||||
|
TransformObjects _objects;
|
||||||
|
TransformCameras _cameras;
|
||||||
|
|
||||||
size_t _cameraUboSize{ 0 };
|
size_t _cameraUboSize{ 0 };
|
||||||
size_t _objectUboSize{ 0 };
|
size_t _objectUboSize{ 0 };
|
||||||
GLuint _transformObjectBuffer{ 0 };
|
GLuint _objectBuffer{ 0 };
|
||||||
GLuint _transformCameraBuffer{ 0 };
|
GLuint _cameraBuffer{ 0 };
|
||||||
Transform _model;
|
Transform _model;
|
||||||
Transform _view;
|
Transform _view;
|
||||||
Mat4 _projection;
|
Mat4 _projection;
|
||||||
|
@ -336,6 +339,12 @@ protected:
|
||||||
using List = std::list<Pair>;
|
using List = std::list<Pair>;
|
||||||
List _cameraOffsets;
|
List _cameraOffsets;
|
||||||
List _objectOffsets;
|
List _objectOffsets;
|
||||||
|
mutable List::const_iterator _objectsItr;
|
||||||
|
mutable List::const_iterator _camerasItr;
|
||||||
|
|
||||||
|
void preUpdate(size_t commandIndex, const StereoState& stereo);
|
||||||
|
void update(size_t commandIndex, const StereoState& stereo) const;
|
||||||
|
void transfer() const;
|
||||||
} _transform;
|
} _transform;
|
||||||
|
|
||||||
int32_t _uboAlignment{ 0 };
|
int32_t _uboAlignment{ 0 };
|
||||||
|
|
|
@ -198,6 +198,9 @@ void GLBackend::do_setFramebuffer(Batch& batch, uint32 paramOffset) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::do_clearFramebuffer(Batch& batch, uint32 paramOffset) {
|
void GLBackend::do_clearFramebuffer(Batch& batch, uint32 paramOffset) {
|
||||||
|
if (_stereo._enable && !_pipeline._stateCache.scissorEnable) {
|
||||||
|
qWarning("Clear without scissor in stereo mode");
|
||||||
|
}
|
||||||
|
|
||||||
uint32 masks = batch._params[paramOffset + 7]._uint;
|
uint32 masks = batch._params[paramOffset + 7]._uint;
|
||||||
Vec4 color;
|
Vec4 color;
|
||||||
|
|
|
@ -768,6 +768,12 @@ void GLBackend::do_setStateScissorRect(Batch& batch, uint32 paramOffset) {
|
||||||
Vec4i rect;
|
Vec4i rect;
|
||||||
memcpy(&rect, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i));
|
memcpy(&rect, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i));
|
||||||
|
|
||||||
|
if (_stereo._enable) {
|
||||||
|
rect.z /= 2;
|
||||||
|
if (_stereo._pass) {
|
||||||
|
rect.x += rect.z;
|
||||||
|
}
|
||||||
|
}
|
||||||
glScissor(rect.x, rect.y, rect.z, rect.w);
|
glScissor(rect.x, rect.y, rect.z, rect.w);
|
||||||
(void) CHECK_GL_ERROR();
|
(void) CHECK_GL_ERROR();
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,16 +33,26 @@ void GLBackend::do_setProjectionTransform(Batch& batch, uint32 paramOffset) {
|
||||||
void GLBackend::do_setViewportTransform(Batch& batch, uint32 paramOffset) {
|
void GLBackend::do_setViewportTransform(Batch& batch, uint32 paramOffset) {
|
||||||
memcpy(&_transform._viewport, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i));
|
memcpy(&_transform._viewport, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i));
|
||||||
|
|
||||||
|
ivec4& vp = _transform._viewport;
|
||||||
|
|
||||||
// Where we assign the GL viewport
|
// Where we assign the GL viewport
|
||||||
glViewport(_transform._viewport.x, _transform._viewport.y, _transform._viewport.z, _transform._viewport.w);
|
if (_stereo._enable) {
|
||||||
|
vp.z /= 2;
|
||||||
|
if (_stereo._pass) {
|
||||||
|
vp.x += vp.z;
|
||||||
|
}
|
||||||
|
int i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
glViewport(vp.x, vp.y, vp.z, vp.w);
|
||||||
|
|
||||||
// The Viewport is tagged invalid because the CameraTransformUBO is not up to date and willl need update on next drawcall
|
// The Viewport is tagged invalid because the CameraTransformUBO is not up to date and willl need update on next drawcall
|
||||||
_transform._invalidViewport = true;
|
_transform._invalidViewport = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::initTransform() {
|
void GLBackend::initTransform() {
|
||||||
glGenBuffers(1, &_transform._transformObjectBuffer);
|
glGenBuffers(1, &_transform._objectBuffer);
|
||||||
glGenBuffers(1, &_transform._transformCameraBuffer);
|
glGenBuffers(1, &_transform._cameraBuffer);
|
||||||
size_t cameraSize = sizeof(TransformCamera);
|
size_t cameraSize = sizeof(TransformCamera);
|
||||||
while (_transform._cameraUboSize < cameraSize) {
|
while (_transform._cameraUboSize < cameraSize) {
|
||||||
_transform._cameraUboSize += _uboAlignment;
|
_transform._cameraUboSize += _uboAlignment;
|
||||||
|
@ -54,8 +64,8 @@ void GLBackend::initTransform() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::killTransform() {
|
void GLBackend::killTransform() {
|
||||||
glDeleteBuffers(1, &_transform._transformObjectBuffer);
|
glDeleteBuffers(1, &_transform._objectBuffer);
|
||||||
glDeleteBuffers(1, &_transform._transformCameraBuffer);
|
glDeleteBuffers(1, &_transform._cameraBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::syncTransformStateCache() {
|
void GLBackend::syncTransformStateCache() {
|
||||||
|
@ -72,73 +82,98 @@ void GLBackend::syncTransformStateCache() {
|
||||||
_transform._model.setIdentity();
|
_transform._model.setIdentity();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::preUpdateTransform() {
|
void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const StereoState& stereo) {
|
||||||
// Check all the dirty flags and update the state accordingly
|
// Check all the dirty flags and update the state accordingly
|
||||||
if (_transform._invalidViewport) {
|
if (_invalidViewport) {
|
||||||
_transform._transformCamera._viewport = glm::vec4(_transform._viewport);
|
_camera._viewport = glm::vec4(_viewport);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_transform._invalidProj) {
|
if (_invalidProj) {
|
||||||
_transform._transformCamera._projection = _transform._projection;
|
_camera._projection = _projection;
|
||||||
_transform._transformCamera._projectionInverse = glm::inverse(_transform._projection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_transform._invalidView) {
|
if (_invalidView) {
|
||||||
_transform._view.getInverseMatrix(_transform._transformCamera._view);
|
_view.getInverseMatrix(_camera._view);
|
||||||
_transform._view.getMatrix(_transform._transformCamera._viewInverse);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_transform._invalidModel) {
|
if (_invalidModel) {
|
||||||
_transform._model.getMatrix(_transform._transformObject._model);
|
_model.getMatrix(_object._model);
|
||||||
_transform._model.getInverseMatrix(_transform._transformObject._modelInverse);
|
_model.getInverseMatrix(_object._modelInverse);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_transform._invalidView || _transform._invalidProj) {
|
if (_invalidView || _invalidProj || _invalidViewport) {
|
||||||
Mat4 viewUntranslated = _transform._transformCamera._view;
|
size_t offset = _cameraUboSize * _cameras.size();
|
||||||
viewUntranslated[3] = Vec4(0.0f, 0.0f, 0.0f, 1.0f);
|
if (stereo._enable) {
|
||||||
_transform._transformCamera._projectionViewUntranslated = _transform._transformCamera._projection * viewUntranslated;
|
_cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset));
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
_cameras.push_back(_camera.getEyeCamera(i, stereo));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset));
|
||||||
|
_cameras.push_back(_camera.recomputeDerived());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_transform._invalidView || _transform._invalidProj || _transform._invalidViewport) {
|
if (_invalidModel) {
|
||||||
_transform._cameraOffsets.push_back(TransformStageState::Pair(_commandIndex, _transform._cameraUboSize * _transform._cameraTransforms.size()));
|
size_t offset = _objectUboSize * _objects.size();
|
||||||
_transform._cameraTransforms.push_back(_transform._transformCamera);
|
_objectOffsets.push_back(TransformStageState::Pair(commandIndex, offset));
|
||||||
}
|
_objects.push_back(_object);
|
||||||
|
|
||||||
if (_transform._invalidModel) {
|
|
||||||
_transform._objectOffsets.push_back(TransformStageState::Pair(_commandIndex, _transform._objectUboSize * _transform._objectTransforms.size()));
|
|
||||||
_transform._objectTransforms.push_back(_transform._transformObject);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flags are clean
|
// Flags are clean
|
||||||
_transform._invalidView = _transform._invalidProj = _transform._invalidModel = _transform._invalidViewport = false;
|
_invalidView = _invalidProj = _invalidModel = _invalidViewport = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLBackend::updateTransform() {
|
void GLBackend::TransformStageState::transfer() const {
|
||||||
|
static QByteArray bufferData;
|
||||||
|
glBindBuffer(GL_UNIFORM_BUFFER, _cameraBuffer);
|
||||||
|
bufferData.resize(_cameraUboSize * _cameras.size());
|
||||||
|
for (size_t i = 0; i < _cameras.size(); ++i) {
|
||||||
|
memcpy(bufferData.data() + (_cameraUboSize * i), &_cameras[i], sizeof(TransformCamera));
|
||||||
|
}
|
||||||
|
glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW);
|
||||||
|
glBindBuffer(GL_UNIFORM_BUFFER, _objectBuffer);
|
||||||
|
bufferData.resize(_objectUboSize * _objects.size());
|
||||||
|
for (size_t i = 0; i < _objects.size(); ++i) {
|
||||||
|
memcpy(bufferData.data() + (_objectUboSize * i), &_objects[i], sizeof(TransformObject));
|
||||||
|
}
|
||||||
|
glBufferData(GL_UNIFORM_BUFFER, bufferData.size(), bufferData.data(), GL_DYNAMIC_DRAW);
|
||||||
|
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||||
|
CHECK_GL_ERROR();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLBackend::TransformStageState::update(size_t commandIndex, const StereoState& stereo) const {
|
||||||
int offset = -1;
|
int offset = -1;
|
||||||
while (!_transform._objectOffsets.empty() && _commandIndex >= _transform._objectOffsets.front().first) {
|
while ((_objectsItr != _objectOffsets.end()) && (commandIndex >= (*_objectsItr).first)) {
|
||||||
offset = _transform._objectOffsets.front().second;
|
offset = (*_objectsItr).second;
|
||||||
_transform._objectOffsets.pop_front();
|
++_objectsItr;
|
||||||
}
|
}
|
||||||
if (offset >= 0) {
|
if (offset >= 0) {
|
||||||
glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_OBJECT_SLOT,
|
glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_OBJECT_SLOT,
|
||||||
_transform._transformObjectBuffer,
|
_objectBuffer, offset, sizeof(Backend::TransformObject));
|
||||||
offset, sizeof(Backend::TransformObject));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = -1;
|
offset = -1;
|
||||||
while (!_transform._cameraOffsets.empty() && _commandIndex >= _transform._cameraOffsets.front().first) {
|
while ((_camerasItr != _cameraOffsets.end()) && (commandIndex >= (*_camerasItr).first)) {
|
||||||
offset = _transform._cameraOffsets.front().second;
|
offset = (*_camerasItr).second;
|
||||||
_transform._cameraOffsets.pop_front();
|
++_camerasItr;
|
||||||
}
|
}
|
||||||
if (offset >= 0) {
|
if (offset >= 0) {
|
||||||
glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT,
|
// We include both camera offsets for stereo
|
||||||
_transform._transformCameraBuffer,
|
if (stereo._enable && stereo._pass) {
|
||||||
offset, sizeof(Backend::TransformCamera));
|
offset += _cameraUboSize;
|
||||||
|
}
|
||||||
|
glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT,
|
||||||
|
_cameraBuffer, offset, sizeof(Backend::TransformCamera));
|
||||||
}
|
}
|
||||||
|
|
||||||
(void)CHECK_GL_ERROR();
|
(void)CHECK_GL_ERROR();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GLBackend::updateTransform() const {
|
||||||
|
_transform.update(_commandIndex, _stereo);
|
||||||
|
}
|
||||||
|
|
||||||
void GLBackend::resetTransformStage() {
|
void GLBackend::resetTransformStage() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
|
||||||
string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE)
|
string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE)
|
||||||
if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR)
|
if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR)
|
||||||
string(TOLOWER ${EXTERNAL} ${EXTERNAL}_LOWERCASE)
|
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 ()
|
endif ()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
|
|
@ -189,7 +189,6 @@ void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePoint
|
||||||
renderHand(rightHand, batch, RIGHT_HAND);
|
renderHand(rightHand, batch, RIGHT_HAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
args->_context->syncCache();
|
|
||||||
args->_context->render(batch);
|
args->_context->render(batch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,10 +58,12 @@ const Vec3& Light::getDirection() const {
|
||||||
|
|
||||||
void Light::setColor(const Color& color) {
|
void Light::setColor(const Color& color) {
|
||||||
editSchema()._color = color;
|
editSchema()._color = color;
|
||||||
|
updateLightRadius();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Light::setIntensity(float intensity) {
|
void Light::setIntensity(float intensity) {
|
||||||
editSchema()._intensity = intensity;
|
editSchema()._intensity = intensity;
|
||||||
|
updateLightRadius();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Light::setAmbientIntensity(float intensity) {
|
void Light::setAmbientIntensity(float intensity) {
|
||||||
|
@ -72,9 +74,14 @@ void Light::setMaximumRadius(float radius) {
|
||||||
if (radius <= 0.f) {
|
if (radius <= 0.f) {
|
||||||
radius = 1.0f;
|
radius = 1.0f;
|
||||||
}
|
}
|
||||||
|
editSchema()._attenuation.w = radius;
|
||||||
|
updateLightRadius();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Light::updateLightRadius() {
|
||||||
float CutOffIntensityRatio = 0.05f;
|
float CutOffIntensityRatio = 0.05f;
|
||||||
float surfaceRadius = radius / (sqrtf(1.0f / CutOffIntensityRatio) - 1.0f);
|
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, radius);
|
editSchema()._attenuation = Vec4(surfaceRadius, 1.0f/surfaceRadius, CutOffIntensityRatio, getMaximumRadius());
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
|
@ -127,6 +127,8 @@ protected:
|
||||||
|
|
||||||
const Schema& getSchema() const { return _schemaBuffer.get<Schema>(); }
|
const Schema& getSchema() const { return _schemaBuffer.get<Schema>(); }
|
||||||
Schema& editSchema() { return _schemaBuffer.edit<Schema>(); }
|
Schema& editSchema() { return _schemaBuffer.edit<Schema>(); }
|
||||||
|
|
||||||
|
void updateLightRadius();
|
||||||
};
|
};
|
||||||
typedef std::shared_ptr< Light > LightPointer;
|
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"
|
#include "Plugin.h"
|
||||||
|
|
||||||
PluginContainer* Plugin::CONTAINER{ nullptr };
|
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
|
#pragma once
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
class PluginContainer;
|
#include "Forward.h"
|
||||||
|
|
||||||
class Plugin : public QObject {
|
class Plugin : public QObject {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -7,9 +7,10 @@
|
||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
class QAction;
|
||||||
class QGLWidget;
|
class QGLWidget;
|
||||||
class QScreen;
|
class QScreen;
|
||||||
|
|
||||||
|
@ -18,11 +19,11 @@ public:
|
||||||
PluginContainer();
|
PluginContainer();
|
||||||
virtual void addMenu(const QString& menuName) = 0;
|
virtual void addMenu(const QString& menuName) = 0;
|
||||||
virtual void removeMenu(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 void removeMenuItem(const QString& menuName, const QString& menuItem) = 0;
|
||||||
virtual bool isOptionChecked(const QString& name) = 0;
|
virtual bool isOptionChecked(const QString& name) = 0;
|
||||||
virtual void setIsOptionChecked(const QString& path, bool checked) = 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 unsetFullscreen(const QScreen* avoidScreen = nullptr) = 0;
|
||||||
virtual void showDisplayPluginsTools() = 0;
|
virtual void showDisplayPluginsTools() = 0;
|
||||||
virtual QGLWidget* getPrimarySurface() = 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 "PluginManager.h"
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
|
||||||
PluginManager* PluginManager::getInstance() {
|
PluginManager* PluginManager::getInstance() {
|
||||||
static PluginManager _manager;
|
static PluginManager _manager;
|
||||||
return &_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
|
#pragma once
|
||||||
|
|
||||||
#include "Plugin.h"
|
#include "Forward.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>;
|
|
||||||
|
|
||||||
class PluginManager : public QObject {
|
class PluginManager : public QObject {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -51,8 +51,13 @@ const gpu::PipelinePointer& AmbientOcclusion::getOcclusionPipeline() {
|
||||||
_gBiasLoc = program->getUniforms().findLocation("g_bias");
|
_gBiasLoc = program->getUniforms().findLocation("g_bias");
|
||||||
_gSampleRadiusLoc = program->getUniforms().findLocation("g_sample_rad");
|
_gSampleRadiusLoc = program->getUniforms().findLocation("g_sample_rad");
|
||||||
_gIntensityLoc = program->getUniforms().findLocation("g_intensity");
|
_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());
|
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) {
|
void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) {
|
||||||
assert(renderContext->args);
|
assert(renderContext->args);
|
||||||
assert(renderContext->args->_viewFrustum);
|
assert(renderContext->args->_viewFrustum);
|
||||||
RenderArgs* args = renderContext->args;
|
|
||||||
|
|
||||||
gpu::Batch batch;
|
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;
|
glm::mat4 projMat;
|
||||||
Transform viewMat;
|
Transform viewMat;
|
||||||
|
@ -186,8 +201,8 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons
|
||||||
|
|
||||||
// Occlusion step
|
// Occlusion step
|
||||||
getOcclusionPipeline();
|
getOcclusionPipeline();
|
||||||
batch.setResourceTexture(0, DependencyManager::get<FramebufferCache>()->getPrimaryDepthTexture());
|
batch.setResourceTexture(0, framebufferCache->getPrimaryDepthTexture());
|
||||||
batch.setResourceTexture(1, DependencyManager::get<FramebufferCache>()->getPrimaryNormalTexture());
|
batch.setResourceTexture(1, framebufferCache->getPrimaryNormalTexture());
|
||||||
_occlusionBuffer->setRenderBuffer(0, _occlusionTexture);
|
_occlusionBuffer->setRenderBuffer(0, _occlusionTexture);
|
||||||
batch.setFramebuffer(_occlusionBuffer);
|
batch.setFramebuffer(_occlusionBuffer);
|
||||||
|
|
||||||
|
@ -203,8 +218,32 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons
|
||||||
batch._glUniform1f(_gBiasLoc, g_bias);
|
batch._glUniform1f(_gBiasLoc, g_bias);
|
||||||
batch._glUniform1f(_gSampleRadiusLoc, g_sample_rad);
|
batch._glUniform1f(_gSampleRadiusLoc, g_sample_rad);
|
||||||
batch._glUniform1f(_gIntensityLoc, g_intensity);
|
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::vec4 color(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
glm::vec2 bottomLeft(-1.0f, -1.0f);
|
glm::vec2 bottomLeft(-1.0f, -1.0f);
|
||||||
|
@ -238,14 +277,13 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons
|
||||||
// Blend step
|
// Blend step
|
||||||
getBlendPipeline();
|
getBlendPipeline();
|
||||||
batch.setResourceTexture(0, _hBlurTexture);
|
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
|
// Bind the fourth gpu::Pipeline we need - for blending the primary color buffer with blurred occlusion texture
|
||||||
batch.setPipeline(getBlendPipeline());
|
batch.setPipeline(getBlendPipeline());
|
||||||
|
|
||||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color);
|
DependencyManager::get<GeometryCache>()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color);
|
||||||
|
|
||||||
// Ready to render
|
// Ready to render
|
||||||
args->_context->syncCache();
|
|
||||||
args->_context->render((batch));
|
args->_context->render((batch));
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,15 @@ private:
|
||||||
gpu::int32 _gBiasLoc;
|
gpu::int32 _gBiasLoc;
|
||||||
gpu::int32 _gSampleRadiusLoc;
|
gpu::int32 _gSampleRadiusLoc;
|
||||||
gpu::int32 _gIntensityLoc;
|
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_scale;
|
||||||
float g_bias;
|
float g_bias;
|
||||||
float g_sample_rad;
|
float g_sample_rad;
|
||||||
|
|
|
@ -225,6 +225,7 @@ void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radiu
|
||||||
|
|
||||||
void DeferredLightingEffect::prepare(RenderArgs* args) {
|
void DeferredLightingEffect::prepare(RenderArgs* args) {
|
||||||
gpu::Batch batch;
|
gpu::Batch batch;
|
||||||
|
batch.enableStereo(false);
|
||||||
|
|
||||||
batch.setStateScissorRect(args->_viewport);
|
batch.setStateScissorRect(args->_viewport);
|
||||||
|
|
||||||
|
@ -244,6 +245,9 @@ gpu::FramebufferPointer _copyFBO;
|
||||||
void DeferredLightingEffect::render(RenderArgs* args) {
|
void DeferredLightingEffect::render(RenderArgs* args) {
|
||||||
gpu::Batch batch;
|
gpu::Batch batch;
|
||||||
|
|
||||||
|
// Framebuffer copy operations cannot function as multipass stereo operations.
|
||||||
|
batch.enableStereo(false);
|
||||||
|
|
||||||
// perform deferred lighting, rendering to free fbo
|
// perform deferred lighting, rendering to free fbo
|
||||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||||
|
|
||||||
|
@ -555,6 +559,7 @@ void DeferredLightingEffect::render(RenderArgs* args) {
|
||||||
|
|
||||||
void DeferredLightingEffect::copyBack(RenderArgs* args) {
|
void DeferredLightingEffect::copyBack(RenderArgs* args) {
|
||||||
gpu::Batch batch;
|
gpu::Batch batch;
|
||||||
|
batch.enableStereo(false);
|
||||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||||
QSize framebufferSize = framebufferCache->getFrameBufferSize();
|
QSize framebufferSize = framebufferCache->getFrameBufferSize();
|
||||||
|
|
||||||
|
|
|
@ -92,17 +92,17 @@ void Environment::resetToDefault() {
|
||||||
_data[QUuid()][0];
|
_data[QUuid()][0];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Environment::renderAtmospheres(gpu::Batch& batch, ViewFrustum& camera) {
|
void Environment::renderAtmospheres(gpu::Batch& batch, ViewFrustum& viewFrustum) {
|
||||||
// get the lock for the duration of the call
|
// get the lock for the duration of the call
|
||||||
QMutexLocker locker(&_mutex);
|
QMutexLocker locker(&_mutex);
|
||||||
|
|
||||||
if (_environmentIsOverridden) {
|
if (_environmentIsOverridden) {
|
||||||
renderAtmosphere(batch, camera, _overrideData);
|
renderAtmosphere(batch, viewFrustum, _overrideData);
|
||||||
} else {
|
} else {
|
||||||
foreach (const ServerData& serverData, _data) {
|
foreach (const ServerData& serverData, _data) {
|
||||||
// TODO: do something about EnvironmentData
|
// TODO: do something about EnvironmentData
|
||||||
foreach (const EnvironmentData& environmentData, serverData) {
|
foreach (const EnvironmentData& environmentData, serverData) {
|
||||||
renderAtmosphere(batch, camera, environmentData);
|
renderAtmosphere(batch, viewFrustum, environmentData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,15 +196,25 @@ bool Environment::findCapsulePenetration(const glm::vec3& start, const glm::vec3
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Environment::renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const EnvironmentData& data) {
|
void Environment::renderAtmosphere(gpu::Batch& batch, ViewFrustum& viewFrustum, const EnvironmentData& data) {
|
||||||
|
|
||||||
glm::vec3 center = data.getAtmosphereCenter();
|
glm::vec3 center = data.getAtmosphereCenter();
|
||||||
|
|
||||||
|
// transform the model transform to the center of our atmosphere
|
||||||
Transform transform;
|
Transform transform;
|
||||||
transform.setTranslation(center);
|
transform.setTranslation(center);
|
||||||
batch.setModelTransform(transform);
|
batch.setModelTransform(transform);
|
||||||
|
|
||||||
glm::vec3 relativeCameraPos = camera.getPosition() - center;
|
// Make sure our view and projection transforms are correct for our viewFrustum
|
||||||
|
Transform viewTransform;
|
||||||
|
viewFrustum.evalViewTransform(viewTransform);
|
||||||
|
batch.setViewTransform(viewTransform);
|
||||||
|
|
||||||
|
glm::mat4 projMat;
|
||||||
|
viewFrustum.evalProjectionMatrix(projMat);
|
||||||
|
batch.setProjectionTransform(projMat);
|
||||||
|
|
||||||
|
glm::vec3 relativeCameraPos = viewFrustum.getPosition() - center;
|
||||||
float height = glm::length(relativeCameraPos);
|
float height = glm::length(relativeCameraPos);
|
||||||
|
|
||||||
// use the appropriate shader depending on whether we're inside or outside
|
// use the appropriate shader depending on whether we're inside or outside
|
||||||
|
@ -212,11 +222,11 @@ void Environment::renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const
|
||||||
if (height < data.getAtmosphereOuterRadius()) {
|
if (height < data.getAtmosphereOuterRadius()) {
|
||||||
batch.setPipeline(_skyFromAtmosphereProgram);
|
batch.setPipeline(_skyFromAtmosphereProgram);
|
||||||
locations = _skyFromAtmosphereUniformLocations;
|
locations = _skyFromAtmosphereUniformLocations;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
batch.setPipeline(_skyFromSpaceProgram);
|
batch.setPipeline(_skyFromSpaceProgram);
|
||||||
locations = _skyFromSpaceUniformLocations;
|
locations = _skyFromSpaceUniformLocations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// the constants here are from Sean O'Neil's GPU Gems entry
|
// the constants here are from Sean O'Neil's GPU Gems entry
|
||||||
// (http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter16.html), GameEngine.cpp
|
// (http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter16.html), GameEngine.cpp
|
||||||
|
|
|
@ -29,7 +29,7 @@ public:
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
void resetToDefault();
|
void resetToDefault();
|
||||||
void renderAtmospheres(gpu::Batch& batch, ViewFrustum& camera);
|
void renderAtmospheres(gpu::Batch& batch, ViewFrustum& viewFrustum);
|
||||||
|
|
||||||
void override(const EnvironmentData& overrideData) { _overrideData = overrideData; _environmentIsOverridden = true; }
|
void override(const EnvironmentData& overrideData) { _overrideData = overrideData; _environmentIsOverridden = true; }
|
||||||
void endOverride() { _environmentIsOverridden = false; }
|
void endOverride() { _environmentIsOverridden = false; }
|
||||||
|
@ -41,7 +41,7 @@ private:
|
||||||
bool findCapsulePenetration(const glm::vec3& start,
|
bool findCapsulePenetration(const glm::vec3& start,
|
||||||
const glm::vec3& end, float radius, glm::vec3& penetration); // NOTE: Deprecated
|
const glm::vec3& end, float radius, glm::vec3& penetration); // NOTE: Deprecated
|
||||||
|
|
||||||
void renderAtmosphere(gpu::Batch& batch, ViewFrustum& camera, const EnvironmentData& data);
|
void renderAtmosphere(gpu::Batch& batch, ViewFrustum& viewFrustum, const EnvironmentData& data);
|
||||||
|
|
||||||
bool _initialized;
|
bool _initialized;
|
||||||
|
|
||||||
|
|
|
@ -1723,7 +1723,7 @@ void GeometryReader::run() {
|
||||||
NetworkGeometry::NetworkGeometry(const QUrl& url, bool delayLoad, const QVariantHash& mapping, const QUrl& textureBaseUrl) :
|
NetworkGeometry::NetworkGeometry(const QUrl& url, bool delayLoad, const QVariantHash& mapping, const QUrl& textureBaseUrl) :
|
||||||
_url(url),
|
_url(url),
|
||||||
_mapping(mapping),
|
_mapping(mapping),
|
||||||
_textureBaseUrl(textureBaseUrl) {
|
_textureBaseUrl(textureBaseUrl.isValid() ? textureBaseUrl : url) {
|
||||||
|
|
||||||
if (delayLoad) {
|
if (delayLoad) {
|
||||||
_state = DelayState;
|
_state = DelayState;
|
||||||
|
|
|
@ -35,6 +35,7 @@ void SetupDeferred::run(const SceneContextPointer& sceneContext, const RenderCon
|
||||||
auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebufferDepthColor();
|
auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebufferDepthColor();
|
||||||
|
|
||||||
gpu::Batch batch;
|
gpu::Batch batch;
|
||||||
|
batch.enableStereo(false);
|
||||||
batch.setFramebuffer(nullptr);
|
batch.setFramebuffer(nullptr);
|
||||||
batch.setFramebuffer(primaryFbo);
|
batch.setFramebuffer(primaryFbo);
|
||||||
|
|
||||||
|
@ -159,6 +160,8 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend
|
||||||
|
|
||||||
RenderArgs* args = renderContext->args;
|
RenderArgs* args = renderContext->args;
|
||||||
gpu::Batch batch;
|
gpu::Batch batch;
|
||||||
|
batch.setViewportTransform(args->_viewport);
|
||||||
|
batch.setStateScissorRect(args->_viewport);
|
||||||
args->_batch = &batch;
|
args->_batch = &batch;
|
||||||
|
|
||||||
renderContext->_numDrawnOpaqueItems = inItems.size();
|
renderContext->_numDrawnOpaqueItems = inItems.size();
|
||||||
|
@ -188,6 +191,8 @@ void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const
|
||||||
|
|
||||||
RenderArgs* args = renderContext->args;
|
RenderArgs* args = renderContext->args;
|
||||||
gpu::Batch batch;
|
gpu::Batch batch;
|
||||||
|
batch.setViewportTransform(args->_viewport);
|
||||||
|
batch.setStateScissorRect(args->_viewport);
|
||||||
args->_batch = &batch;
|
args->_batch = &batch;
|
||||||
|
|
||||||
renderContext->_numDrawnTransparentItems = inItems.size();
|
renderContext->_numDrawnTransparentItems = inItems.size();
|
||||||
|
@ -247,30 +252,42 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon
|
||||||
renderContext->_numFeedOverlay3DItems = inItems.size();
|
renderContext->_numFeedOverlay3DItems = inItems.size();
|
||||||
renderContext->_numDrawnOverlay3DItems = inItems.size();
|
renderContext->_numDrawnOverlay3DItems = inItems.size();
|
||||||
|
|
||||||
RenderArgs* args = renderContext->args;
|
|
||||||
gpu::Batch batch;
|
|
||||||
args->_batch = &batch;
|
|
||||||
args->_whiteTexture = DependencyManager::get<TextureCache>()->getWhiteTexture();
|
|
||||||
|
|
||||||
glm::mat4 projMat;
|
|
||||||
Transform viewMat;
|
|
||||||
args->_viewFrustum->evalProjectionMatrix(projMat);
|
|
||||||
args->_viewFrustum->evalViewTransform(viewMat);
|
|
||||||
|
|
||||||
batch.setProjectionTransform(projMat);
|
|
||||||
batch.setViewTransform(viewMat);
|
|
||||||
batch.setViewportTransform(args->_viewport);
|
|
||||||
batch.setStateScissorRect(args->_viewport);
|
|
||||||
|
|
||||||
batch.setPipeline(getOpaquePipeline());
|
|
||||||
batch.setResourceTexture(0, args->_whiteTexture);
|
|
||||||
|
|
||||||
if (!inItems.empty()) {
|
if (!inItems.empty()) {
|
||||||
batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, true);
|
RenderArgs* args = renderContext->args;
|
||||||
renderItems(sceneContext, renderContext, inItems, renderContext->_maxDrawnOverlay3DItems);
|
|
||||||
}
|
|
||||||
|
|
||||||
args->_context->render((*args->_batch));
|
// Clear the framebuffer without stereo
|
||||||
args->_batch = nullptr;
|
// Needs to be distinct from the other batch because using the clear call
|
||||||
args->_whiteTexture.reset();
|
// while stereo is enabled triggers a warning
|
||||||
|
{
|
||||||
|
gpu::Batch batch;
|
||||||
|
batch.enableStereo(false);
|
||||||
|
batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, true);
|
||||||
|
args->_context->render(batch);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the items
|
||||||
|
{
|
||||||
|
gpu::Batch batch;
|
||||||
|
args->_batch = &batch;
|
||||||
|
args->_whiteTexture = DependencyManager::get<TextureCache>()->getWhiteTexture();
|
||||||
|
|
||||||
|
glm::mat4 projMat;
|
||||||
|
Transform viewMat;
|
||||||
|
args->_viewFrustum->evalProjectionMatrix(projMat);
|
||||||
|
args->_viewFrustum->evalViewTransform(viewMat);
|
||||||
|
|
||||||
|
batch.setProjectionTransform(projMat);
|
||||||
|
batch.setViewTransform(viewMat);
|
||||||
|
batch.setViewportTransform(args->_viewport);
|
||||||
|
batch.setStateScissorRect(args->_viewport);
|
||||||
|
|
||||||
|
batch.setPipeline(getOpaquePipeline());
|
||||||
|
batch.setResourceTexture(0, args->_whiteTexture);
|
||||||
|
renderItems(sceneContext, renderContext, inItems, renderContext->_maxDrawnOverlay3DItems);
|
||||||
|
|
||||||
|
args->_context->render((*args->_batch));
|
||||||
|
args->_batch = nullptr;
|
||||||
|
args->_whiteTexture.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,25 +30,40 @@ uniform float g_scale;
|
||||||
uniform float g_bias;
|
uniform float g_bias;
|
||||||
uniform float g_sample_rad;
|
uniform float g_sample_rad;
|
||||||
uniform float g_intensity;
|
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 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 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
|
// 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 float MaxRadiusPixels = 50.0;
|
||||||
|
|
||||||
const int NumDirections = 6;
|
const int NumDirections = 6;
|
||||||
|
@ -56,113 +71,126 @@ const int NumSamples = 4;
|
||||||
|
|
||||||
out vec4 outFragColor;
|
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){
|
float ViewSpaceZFromDepth(float d){
|
||||||
// [0,1] -> [-1,1] clip space
|
return near / (d * depthScale - 1.0);
|
||||||
d = d * 2.0 - 1.0;
|
|
||||||
|
|
||||||
// Get view space Z
|
|
||||||
return -1.0 / (LinMAD.x * d + LinMAD.y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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){
|
vec3 UVToViewSpace(vec2 uv, float z){
|
||||||
//uv = UVToViewA * uv + UVToViewB;
|
return vec3((depthTexCoordOffset + varTexcoord * depthTexCoordScale) * z, z);
|
||||||
return vec3(uv * z, z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 GetViewPos(vec2 uv){
|
/**
|
||||||
float z = ViewSpaceZFromDepth(texture(depthTexture, uv).r);
|
* Converts a uv coordinate into a 3D view space coordinate.
|
||||||
return UVToViewSpace(uv, z);
|
* 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 TanToSin(float x) {
|
||||||
//float z = texelFetch(texture0, coord, 0).r;
|
return x * inversesqrt(x*x + 1.0);
|
||||||
float z = texture(depthTexture, uv).r;
|
|
||||||
return UVToViewSpace(uv, z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float TanToSin(float x){
|
float InvLength(vec2 V) {
|
||||||
return x * inversesqrt(x*x + 1.0);
|
return inversesqrt(dot(V, V));
|
||||||
}
|
}
|
||||||
|
|
||||||
float InvLength(vec2 V){
|
float Tangent(vec3 V) {
|
||||||
return inversesqrt(dot(V,V));
|
return V.z * InvLength(V.xy);
|
||||||
}
|
}
|
||||||
|
|
||||||
float Tangent(vec3 V){
|
float BiasedTangent(vec3 V) {
|
||||||
return V.z * InvLength(V.xy);
|
return V.z * InvLength(V.xy) + TanBias;
|
||||||
}
|
}
|
||||||
|
|
||||||
float BiasedTangent(vec3 V){
|
float Tangent(vec3 P, vec3 S) {
|
||||||
return V.z * InvLength(V.xy) + TanBias;
|
|
||||||
}
|
|
||||||
|
|
||||||
float Tangent(vec3 P, vec3 S){
|
|
||||||
return -(P.z - S.z) * InvLength(S.xy - P.xy);
|
return -(P.z - S.z) * InvLength(S.xy - P.xy);
|
||||||
}
|
}
|
||||||
|
|
||||||
float Length2(vec3 V){
|
float Length2(vec3 V) {
|
||||||
return dot(V,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 V1 = Pr - P;
|
||||||
vec3 V2 = P - Pl;
|
vec3 V2 = P - Pl;
|
||||||
return (Length2(V1) < Length2(V2)) ? V1 : V2;
|
return (Length2(V1) < Length2(V2)) ? V1 : V2;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec2 SnapUVOffset(vec2 uv){
|
vec2 SnapUVOffset(vec2 uv) {
|
||||||
return round(uv * AORes) * InvAORes;
|
return round(uv * renderTargetRes) * renderTargetResInv;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Falloff(float d2){
|
float Falloff(float d2) {
|
||||||
return d2 * NegInvR2 + 1.0f;
|
return d2 * NegInvR2 + 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float HorizonOcclusion( vec2 deltaUV, vec3 P, vec3 dPdu, vec3 dPdv, float randstep, float numSamples){
|
float HorizonOcclusion(vec2 deltaUV, vec3 P, vec3 dPdu, vec3 dPdv, float randstep, float numSamples) {
|
||||||
float ao = 0;
|
float ao = 0;
|
||||||
|
|
||||||
// Offset the first coord with some noise
|
// Offset the first coord with some noise
|
||||||
vec2 uv = varTexcoord + SnapUVOffset(randstep*deltaUV);
|
vec2 uv = varTexcoord + SnapUVOffset(randstep*deltaUV);
|
||||||
deltaUV = SnapUVOffset( deltaUV );
|
deltaUV = SnapUVOffset(deltaUV);
|
||||||
|
|
||||||
// Calculate the tangent vector
|
// Calculate the tangent vector
|
||||||
vec3 T = deltaUV.x * dPdu + deltaUV.y * dPdv;
|
vec3 T = deltaUV.x * dPdu + deltaUV.y * dPdv;
|
||||||
|
|
||||||
// Get the angle of the tangent vector from the viewspace axis
|
// Get the angle of the tangent vector from the viewspace axis
|
||||||
float tanH = BiasedTangent(T);
|
float tanH = BiasedTangent(T);
|
||||||
float sinH = TanToSin(tanH);
|
float sinH = TanToSin(tanH);
|
||||||
|
|
||||||
float tanS;
|
float tanS;
|
||||||
float d2;
|
float d2;
|
||||||
vec3 S;
|
vec3 S;
|
||||||
|
|
||||||
// Sample to find the maximum angle
|
// Sample to find the maximum angle
|
||||||
for(float s = 1; s <= numSamples; ++s){
|
for (float s = 1; s <= numSamples; ++s) {
|
||||||
uv += deltaUV;
|
uv += deltaUV;
|
||||||
S = GetViewPos(uv);
|
S = GetViewPos(uv);
|
||||||
tanS = Tangent(P, S);
|
tanS = Tangent(P, S);
|
||||||
d2 = Length2(S - P);
|
d2 = Length2(S - P);
|
||||||
|
|
||||||
// Is the sample within the radius and the angle greater?
|
// Is the sample within the radius and the angle greater?
|
||||||
if(d2 < R2 && tanS > tanH)
|
if (d2 < R2 && tanS > tanH) {
|
||||||
{
|
float sinS = TanToSin(tanS);
|
||||||
float sinS = TanToSin(tanS);
|
// Apply falloff based on the distance
|
||||||
// Apply falloff based on the distance
|
ao += Falloff(d2) * (sinS - sinH);
|
||||||
ao += Falloff(d2) * (sinS - sinH);
|
|
||||||
|
|
||||||
tanH = tanS;
|
tanH = tanS;
|
||||||
sinH = sinS;
|
sinH = sinS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ao;
|
return ao;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec2 RotateDirections(vec2 Dir, vec2 CosSin){
|
vec2 RotateDirections(vec2 Dir, vec2 CosSin) {
|
||||||
return vec2(Dir.x*CosSin.x - Dir.y*CosSin.y, Dir.x*CosSin.y + Dir.y*CosSin.x);
|
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
|
// Avoid oversampling if numSteps is greater than the kernel radius in pixels
|
||||||
numSteps = min(NumSamples, rayRadiusPix);
|
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
|
// Clamp numSteps if it is greater than the max kernel footprint
|
||||||
float maxNumSteps = MaxRadiusPixels / stepSizePix;
|
float maxNumSteps = MaxRadiusPixels / stepSizePix;
|
||||||
if (maxNumSteps < numSteps)
|
if (maxNumSteps < numSteps) {
|
||||||
{
|
|
||||||
// Use dithering to avoid AO discontinuities
|
// Use dithering to avoid AO discontinuities
|
||||||
numSteps = floor(maxNumSteps + rand);
|
numSteps = floor(maxNumSteps + rand);
|
||||||
numSteps = max(numSteps, 1);
|
numSteps = max(numSteps, 1);
|
||||||
|
@ -180,69 +207,73 @@ void ComputeSteps(inout vec2 stepSizeUv, inout float numSteps, float rayRadiusPi
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step size in uv space
|
// 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);
|
return fract(sin(dot(uv.xy ,vec2(12.9898,78.233))) * 43758.5453);
|
||||||
}
|
}
|
||||||
|
|
||||||
void main(void){
|
void main(void) {
|
||||||
float numDirections = NumDirections;
|
mat4 projMatrix = getTransformCamera()._projection;
|
||||||
|
|
||||||
vec3 P, Pr, Pl, Pt, Pb;
|
float numDirections = NumDirections;
|
||||||
P = GetViewPos(varTexcoord);
|
|
||||||
|
|
||||||
// Sample neighboring pixels
|
vec3 P, Pr, Pl, Pt, Pb;
|
||||||
Pr = GetViewPos(varTexcoord + vec2( InvAORes.x, 0));
|
P = GetViewPos(varTexcoord);
|
||||||
Pl = GetViewPos(varTexcoord + vec2(-InvAORes.x, 0));
|
|
||||||
Pt = GetViewPos(varTexcoord + vec2( 0, InvAORes.y));
|
// Sample neighboring pixels
|
||||||
Pb = GetViewPos(varTexcoord + vec2( 0,-InvAORes.y));
|
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
|
// Calculate tangent basis vectors using the minimum difference
|
||||||
vec3 dPdu = MinDiff(P, Pr, Pl);
|
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
|
// 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
|
// Calculate the projected size of the hemisphere
|
||||||
vec2 rayRadiusUV = 0.5 * R * FocalLen / -P.z;
|
float w = P.z * projMatrix[2][3] + projMatrix[3][3];
|
||||||
float rayRadiusPix = rayRadiusUV.x * AORes.x;
|
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;
|
float ao = 1.0;
|
||||||
|
|
||||||
// Make sure the radius of the evaluated hemisphere is more than a pixel
|
// Make sure the radius of the evaluated hemisphere is more than a pixel
|
||||||
if(rayRadiusPix > 1.0){
|
if(rayRadiusPix > 1.0) {
|
||||||
ao = 0.0;
|
ao = 0.0;
|
||||||
float numSteps;
|
float numSteps;
|
||||||
vec2 stepSizeUV;
|
vec2 stepSizeUV;
|
||||||
|
|
||||||
// Compute the number of steps
|
// Compute the number of steps
|
||||||
ComputeSteps(stepSizeUV, numSteps, rayRadiusPix, random.z);
|
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
|
// Calculate the horizon occlusion of each direction
|
||||||
for(float d = 0; d < numDirections; ++d){
|
for(float d = 0; d < numDirections; ++d) {
|
||||||
float theta = alpha * d;
|
float theta = alpha * d;
|
||||||
|
|
||||||
// Apply noise to the direction
|
// Apply noise to the direction
|
||||||
vec2 dir = RotateDirections(vec2(cos(theta), sin(theta)), random.xy);
|
vec2 dir = RotateDirections(vec2(cos(theta), sin(theta)), random.xy);
|
||||||
vec2 deltaUV = dir * stepSizeUV;
|
vec2 deltaUV = dir * stepSizeUV;
|
||||||
|
|
||||||
// Sample the pixels along the direction
|
// Sample the pixels along the direction
|
||||||
ao += HorizonOcclusion( deltaUV,
|
ao += HorizonOcclusion( deltaUV,
|
||||||
P,
|
P,
|
||||||
dPdu,
|
dPdu,
|
||||||
dPdv,
|
dPdv,
|
||||||
random.z,
|
random.z,
|
||||||
numSteps);
|
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);
|
outFragColor = vec4(vec3(ao), 1.0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,8 +161,5 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex
|
||||||
batch.draw(gpu::TRIANGLES, 24, 0);
|
batch.draw(gpu::TRIANGLES, 24, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Before rendering the batch make sure we re in sync with gl state
|
args->_context->render(batch);
|
||||||
args->_context->syncCache();
|
|
||||||
renderContext->args->_context->syncCache();
|
|
||||||
args->_context->render((batch));
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -258,6 +258,9 @@ void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderCo
|
||||||
}
|
}
|
||||||
RenderArgs* args = renderContext->args;
|
RenderArgs* args = renderContext->args;
|
||||||
gpu::Batch batch;
|
gpu::Batch batch;
|
||||||
|
batch.enableSkybox(true);
|
||||||
|
batch.setViewportTransform(args->_viewport);
|
||||||
|
batch.setStateScissorRect(args->_viewport);
|
||||||
args->_batch = &batch;
|
args->_batch = &batch;
|
||||||
|
|
||||||
glm::mat4 projMat;
|
glm::mat4 projMat;
|
||||||
|
@ -271,9 +274,6 @@ void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderCo
|
||||||
renderItems(sceneContext, renderContext, inItems);
|
renderItems(sceneContext, renderContext, inItems);
|
||||||
args->_context->render((*args->_batch));
|
args->_context->render((*args->_batch));
|
||||||
args->_batch = nullptr;
|
args->_batch = nullptr;
|
||||||
|
|
||||||
// Force the context sync
|
|
||||||
args->_context->syncCache();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ItemMaterialBucketMap::insert(const ItemID& id, const model::MaterialKey& key) {
|
void ItemMaterialBucketMap::insert(const ItemID& id, const model::MaterialKey& key) {
|
||||||
|
|
|
@ -3,7 +3,7 @@ set(TARGET_NAME script-engine)
|
||||||
setup_memory_debugger()
|
setup_memory_debugger()
|
||||||
|
|
||||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
# 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)
|
add_dependency_external_projects(glm)
|
||||||
find_package(GLM REQUIRED)
|
find_package(GLM REQUIRED)
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include "ScriptEngine.h"
|
#include "ScriptEngine.h"
|
||||||
#include "TypedArrays.h"
|
#include "TypedArrays.h"
|
||||||
#include "XMLHttpRequestClass.h"
|
#include "XMLHttpRequestClass.h"
|
||||||
|
#include "WebSocketClass.h"
|
||||||
|
|
||||||
#include "SceneScriptingInterface.h"
|
#include "SceneScriptingInterface.h"
|
||||||
|
|
||||||
|
@ -343,6 +344,9 @@ void ScriptEngine::init() {
|
||||||
QScriptValue xmlHttpRequestConstructorValue = newFunction(XMLHttpRequestClass::constructor);
|
QScriptValue xmlHttpRequestConstructorValue = newFunction(XMLHttpRequestClass::constructor);
|
||||||
globalObject().setProperty("XMLHttpRequest", xmlHttpRequestConstructorValue);
|
globalObject().setProperty("XMLHttpRequest", xmlHttpRequestConstructorValue);
|
||||||
|
|
||||||
|
QScriptValue webSocketConstructorValue = newFunction(WebSocketClass::constructor);
|
||||||
|
globalObject().setProperty("WebSocket", webSocketConstructorValue);
|
||||||
|
|
||||||
QScriptValue printConstructorValue = newFunction(debugPrint);
|
QScriptValue printConstructorValue = newFunction(debugPrint);
|
||||||
globalObject().setProperty("print", printConstructorValue);
|
globalObject().setProperty("print", printConstructorValue);
|
||||||
|
|
||||||
|
@ -353,6 +357,9 @@ void ScriptEngine::init() {
|
||||||
qScriptRegisterMetaType(this, inputControllerToScriptValue, inputControllerFromScriptValue);
|
qScriptRegisterMetaType(this, inputControllerToScriptValue, inputControllerFromScriptValue);
|
||||||
qScriptRegisterMetaType(this, avatarDataToScriptValue, avatarDataFromScriptValue);
|
qScriptRegisterMetaType(this, avatarDataToScriptValue, avatarDataFromScriptValue);
|
||||||
qScriptRegisterMetaType(this, animationDetailsToScriptValue, animationDetailsFromScriptValue);
|
qScriptRegisterMetaType(this, animationDetailsToScriptValue, animationDetailsFromScriptValue);
|
||||||
|
qScriptRegisterMetaType(this, webSocketToScriptValue, webSocketFromScriptValue);
|
||||||
|
qScriptRegisterMetaType(this, qWSCloseCodeToScriptValue, qWSCloseCodeFromScriptValue);
|
||||||
|
qScriptRegisterMetaType(this, wscReadyStateToScriptValue, wscReadyStateFromScriptValue);
|
||||||
|
|
||||||
registerGlobalObject("Script", this);
|
registerGlobalObject("Script", this);
|
||||||
registerGlobalObject("Audio", &AudioScriptingInterface::getInstance());
|
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