From 25266b986c07cfdcfa4e5d48315641932ae09d16 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 7 Aug 2015 21:43:14 +0200 Subject: [PATCH 01/42] added basic implementation of WebSocket class ( https://developer.mozilla.org/en-US/docs/Web/API/WebSocket ) using QWebSocket --- interface/CMakeLists.txt | 2 +- libraries/script-engine/CMakeLists.txt | 2 +- libraries/script-engine/src/ScriptEngine.cpp | 4 + .../script-engine/src/WebSocketClass.cpp | 82 ++++++++++++ libraries/script-engine/src/WebSocketClass.h | 118 ++++++++++++++++++ 5 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 libraries/script-engine/src/WebSocketClass.cpp create mode 100644 libraries/script-engine/src/WebSocketClass.h diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index f1ef38ade9..f1bcbf4c47 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -60,7 +60,7 @@ else () list(REMOVE_ITEM INTERFACE_SRCS ${SPEECHRECOGNIZER_CPP}) endif () -find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Qml Quick Script Svg WebKitWidgets) +find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Qml Quick Script Svg WebKitWidgets WebSockets) # grab the ui files in resources/ui file (GLOB_RECURSE QT_UI_FILES ui/*.ui) diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 99d9149c3a..139b99e426 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -1,7 +1,7 @@ set(TARGET_NAME script-engine) # use setup_hifi_library macro to setup our project and link appropriate Qt modules -setup_hifi_library(Gui Network Script Widgets) +setup_hifi_library(Gui Network Script WebSockets Widgets) add_dependency_external_projects(glm) find_package(GLM REQUIRED) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index d5e727657c..afbf3eaf38 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -39,6 +39,7 @@ #include "ScriptEngine.h" #include "TypedArrays.h" #include "XMLHttpRequestClass.h" +#include "WebSocketClass.h" #include "SceneScriptingInterface.h" @@ -344,6 +345,9 @@ void ScriptEngine::init() { QScriptValue xmlHttpRequestConstructorValue = newFunction(XMLHttpRequestClass::constructor); globalObject().setProperty("XMLHttpRequest", xmlHttpRequestConstructorValue); + QScriptValue webSocketConstructorValue = newFunction(WebSocketClass::constructor); + globalObject().setProperty("WebSocket", webSocketConstructorValue); + QScriptValue printConstructorValue = newFunction(debugPrint); globalObject().setProperty("print", printConstructorValue); diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp new file mode 100644 index 0000000000..1b8d305b16 --- /dev/null +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -0,0 +1,82 @@ +// +// 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) +{ + connect(&_webSocket, &QWebSocket::disconnected, this, &WebSocketClass::handleOnClose); + connect(&_webSocket, SIGNAL(error()), this, SLOT(handleOnError())); + connect(&_webSocket, &QWebSocket::textMessageReceived, this, &WebSocketClass::handleOnMessage); + connect(&_webSocket, &QWebSocket::connected, this, &WebSocketClass::handleOnOpen); + _webSocket.open(url); +} + +QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine) { + QString url; + QScriptValue callee = context->callee(); + if (context->argumentCount() == 1) { + url = context->argument(0).toString(); + } + return engine->newQObject(new WebSocketClass(engine, url)); +} + +WebSocketClass::~WebSocketClass() { + +} + +void WebSocketClass::send(QScriptValue message) { + _webSocket.sendTextMessage(message.toString()); +} + +void WebSocketClass::close(QWebSocketProtocol::CloseCode closeCode, QString reason) { + _webSocket.close(closeCode, reason); +} + +void WebSocketClass::handleOnClose() { + if (_onCloseEvent.isFunction()) { + //QScriptValueList args; + //args << ("received: " + message); + _onCloseEvent.call();//(QScriptValue(), args); + } +} + +void WebSocketClass::handleOnError(QAbstractSocket::SocketError error) { + if (_onErrorEvent.isFunction()) { + // QScriptValueList args; + //args << ("received: " + message); + _onErrorEvent.call();/// QScriptValue(), args); + } +} + +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()) { + //QScriptValueList args; + //args << ("received: " + message); + _onOpenEvent.call();// QScriptValue(), args); + } +} \ No newline at end of file diff --git a/libraries/script-engine/src/WebSocketClass.h b/libraries/script-engine/src/WebSocketClass.h new file mode 100644 index 0000000000..6f0e3729c0 --- /dev/null +++ b/libraries/script-engine/src/WebSocketClass.h @@ -0,0 +1,118 @@ +// +// 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 +#include +#include + +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(uint readyState READ getReadyState) + Q_PROPERTY(QString url READ getURL) + + Q_PROPERTY(int CONNECTING READ getConnecting CONSTANT) + Q_PROPERTY(int OPEN READ getOpen CONSTANT) + Q_PROPERTY(int CLOSING READ getClosing CONSTANT) + Q_PROPERTY(int CLOSED READ getClosed CONSTANT) + +public: + WebSocketClass(QScriptEngine* engine, QString url); + ~WebSocketClass(); + + static QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine); + + enum ReadyState { + CONNECTING = 0, + OPEN, + CLOSING, + CLOSED + }; + + int getConnecting() const { return CONNECTING; }; + int getOpen() const { return OPEN; }; + int getClosing() const { return CLOSING; }; + int getClosed() const { return CLOSED; }; + + void setBinaryType(QString binaryType) { _binaryType = binaryType; } + QString getBinaryType() { return _binaryType; } + + // extensions is a empty string until supported in QT + QString getExtensions() { return QString(); } + + // protocol is a empty string until supported in QT + QString getProtocol() { return QString(); } + + //TODO: find buffered amount + ulong getBufferedAmount() { return 0; } + + QString getURL() { return _webSocket.requestUrl().toDisplayString(); } + + uint 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(QWebSocketProtocol::CloseCode closeCode, QString reason); + +private: + QWebSocket _webSocket; + QScriptEngine* _engine; + + QScriptValue _onCloseEvent; + QScriptValue _onErrorEvent; + QScriptValue _onMessageEvent; + QScriptValue _onOpenEvent; + + QString _binaryType; + +private slots: + void handleOnClose(); + void handleOnError(QAbstractSocket::SocketError error); + void handleOnMessage(const QString& message); + void handleOnOpen(); + +}; + +#endif // hifi_WebSocketClass_h From 7c77e0e941f3fe9b3ceda0a079a31b8d63ace6fa Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 13 Aug 2015 18:21:50 +0200 Subject: [PATCH 02/42] initial WebSocketServer --- libraries/script-engine/src/ScriptEngine.cpp | 4 ++ .../script-engine/src/WebSocketClass.cpp | 1 - .../src/WebSocketServerClass.cpp | 44 +++++++++++++++++++ .../script-engine/src/WebSocketServerClass.h | 39 ++++++++++++++++ 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 libraries/script-engine/src/WebSocketServerClass.cpp create mode 100644 libraries/script-engine/src/WebSocketServerClass.h diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 861034a6dc..bde0643c51 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -39,6 +39,7 @@ #include "TypedArrays.h" #include "XMLHttpRequestClass.h" #include "WebSocketClass.h" +#include "WebSocketServerClass.h" #include "SceneScriptingInterface.h" @@ -347,6 +348,9 @@ void ScriptEngine::init() { QScriptValue webSocketConstructorValue = newFunction(WebSocketClass::constructor); globalObject().setProperty("WebSocket", webSocketConstructorValue); + QScriptValue webSocketServerConstructorValue = newFunction(WebSocketServerClass::constructor); + globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue); + QScriptValue printConstructorValue = newFunction(debugPrint); globalObject().setProperty("print", printConstructorValue); diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 1b8d305b16..f116c3a0e4 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -64,7 +64,6 @@ void WebSocketClass::handleOnError(QAbstractSocket::SocketError error) { void WebSocketClass::handleOnMessage(const QString& message) { if (_onMessageEvent.isFunction()) { - QScriptValueList args; QScriptValue arg = _engine->newObject(); arg.setProperty("data", message); diff --git a/libraries/script-engine/src/WebSocketServerClass.cpp b/libraries/script-engine/src/WebSocketServerClass.cpp new file mode 100644 index 0000000000..86f6506375 --- /dev/null +++ b/libraries/script-engine/src/WebSocketServerClass.cpp @@ -0,0 +1,44 @@ +// +// 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, quint16 port) : + _engine(engine), + _webSocketServer(serverName, QWebSocketServer::SslMode::NonSecureMode) +{ + _webSocketServer.listen(QHostAddress::Any, port); +} + +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(); + } + } + return engine->newQObject(new WebSocketServerClass(engine, serverName, port)); +} + +WebSocketServerClass::~WebSocketServerClass() { + if (_webSocketServer.isListening()) { + _webSocketServer.close(); + } +} diff --git a/libraries/script-engine/src/WebSocketServerClass.h b/libraries/script-engine/src/WebSocketServerClass.h new file mode 100644 index 0000000000..18fcdc693e --- /dev/null +++ b/libraries/script-engine/src/WebSocketServerClass.h @@ -0,0 +1,39 @@ +// +// 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 +#include +#include + +class WebSocketServerClass : public QObject { + Q_OBJECT + +public: + WebSocketServerClass(QScriptEngine* engine, const QString& serverName, quint16 port); + ~WebSocketServerClass(); + + static QScriptValue WebSocketServerClass::constructor(QScriptContext* context, QScriptEngine* engine); + +private: + QWebSocketServer _webSocketServer; + QScriptEngine* _engine; + +signals: + void newConnection(); + + + +}; + +#endif // hifi_WebSocketServerClass_h From 708190bfd6cefc0f746a3746c703fe756fc675ea Mon Sep 17 00:00:00 2001 From: Christopher Root Date: Sat, 15 Aug 2015 14:10:25 -0700 Subject: [PATCH 03/42] horiz amb occl performance upgrades --- .../src/AmbientOcclusionEffect.cpp | 53 ++++++-- .../render-utils/src/AmbientOcclusionEffect.h | 11 +- .../render-utils/src/ambient_occlusion.slf | 126 +++++++++++++----- 3 files changed, 142 insertions(+), 48 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 5f0afd37d1..b94bd09538 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -51,8 +51,13 @@ const gpu::PipelinePointer& AmbientOcclusion::getOcclusionPipeline() { _gBiasLoc = program->getUniforms().findLocation("g_bias"); _gSampleRadiusLoc = program->getUniforms().findLocation("g_sample_rad"); _gIntensityLoc = program->getUniforms().findLocation("g_intensity"); - _bufferWidthLoc = program->getUniforms().findLocation("bufferWidth"); - _bufferHeightLoc = program->getUniforms().findLocation("bufferHeight"); + + _nearLoc = program->getUniforms().findLocation("near"); + _depthScaleLoc = program->getUniforms().findLocation("depthScale"); + _depthTexCoordOffsetLoc = program->getUniforms().findLocation("depthTexCoordOffset"); + _depthTexCoordScaleLoc = program->getUniforms().findLocation("depthTexCoordScale"); + _renderTargetResLoc = program->getUniforms().findLocation("renderTargetRes"); + _renderTargetResInvLoc = program->getUniforms().findLocation("renderTargetResInv"); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); @@ -172,9 +177,19 @@ const gpu::PipelinePointer& AmbientOcclusion::getBlendPipeline() { void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) { assert(renderContext->args); assert(renderContext->args->_viewFrustum); - RenderArgs* args = renderContext->args; gpu::Batch batch; + RenderArgs* args = renderContext->args; + + auto framebufferCache = DependencyManager::get(); + QSize framebufferSize = framebufferCache->getFrameBufferSize(); + float fbWidth = framebufferSize.width(); + float fbHeight = framebufferSize.height(); + float sMin = args->_viewport.x / fbWidth; + float sWidth = args->_viewport.z / fbWidth; + float tMin = args->_viewport.y / fbHeight; + float tHeight = args->_viewport.w / fbHeight; + glm::mat4 projMat; Transform viewMat; @@ -186,8 +201,8 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons // Occlusion step getOcclusionPipeline(); - batch.setResourceTexture(0, DependencyManager::get()->getPrimaryDepthTexture()); - batch.setResourceTexture(1, DependencyManager::get()->getPrimaryNormalTexture()); + batch.setResourceTexture(0, framebufferCache->getPrimaryDepthTexture()); + batch.setResourceTexture(1, framebufferCache->getPrimaryNormalTexture()); _occlusionBuffer->setRenderBuffer(0, _occlusionTexture); batch.setFramebuffer(_occlusionBuffer); @@ -203,8 +218,28 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons batch._glUniform1f(_gBiasLoc, g_bias); batch._glUniform1f(_gSampleRadiusLoc, g_sample_rad); batch._glUniform1f(_gIntensityLoc, g_intensity); - batch._glUniform1f(_bufferWidthLoc, DependencyManager::get()->getFrameBufferSize().width()); - batch._glUniform1f(_bufferHeightLoc, DependencyManager::get()->getFrameBufferSize().height()); + + // setup uniforms for extracting depth from the depth buffer and + // converting that depth to a camera-space position, same as DeferredLightingEffect.cpp + float left, right, bottom, top, nearVal, farVal; + glm::vec4 nearClipPlane, farClipPlane; + args->_viewFrustum->computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); + + batch._glUniform1f(_nearLoc, nearVal); + + float depthScale = (farVal - nearVal) / farVal; + batch._glUniform1f(_depthScaleLoc, depthScale); + + 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; + batch._glUniform2f(_depthTexCoordOffsetLoc, depthTexCoordOffsetS, depthTexCoordOffsetT); + batch._glUniform2f(_depthTexCoordScaleLoc, depthTexCoordScaleS, depthTexCoordScaleT); + + batch._glUniform2f(_renderTargetResLoc, fbWidth, fbHeight); + batch._glUniform2f(_renderTargetResInvLoc, 1.0/fbWidth, 1.0/fbHeight); glm::vec4 color(0.0f, 0.0f, 0.0f, 1.0f); glm::vec2 bottomLeft(-1.0f, -1.0f); @@ -238,13 +273,13 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons // Blend step getBlendPipeline(); batch.setResourceTexture(0, _hBlurTexture); - batch.setFramebuffer(DependencyManager::get()->getPrimaryFramebuffer()); + batch.setFramebuffer(framebufferCache->getPrimaryFramebuffer()); // Bind the fourth gpu::Pipeline we need - for blending the primary color buffer with blurred occlusion texture batch.setPipeline(getBlendPipeline()); DependencyManager::get()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color); - + // Ready to render args->_context->syncCache(); args->_context->render((batch)); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 0b695dd2ad..6153795ea6 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -36,8 +36,15 @@ private: gpu::int32 _gBiasLoc; gpu::int32 _gSampleRadiusLoc; gpu::int32 _gIntensityLoc; - gpu::int32 _bufferWidthLoc; - gpu::int32 _bufferHeightLoc; + + gpu::int32 _nearLoc; + gpu::int32 _depthScaleLoc; + gpu::int32 _depthTexCoordOffsetLoc; + gpu::int32 _depthTexCoordScaleLoc; + gpu::int32 _renderTargetResLoc; + gpu::int32 _renderTargetResInvLoc; + + float g_scale; float g_bias; float g_sample_rad; diff --git a/libraries/render-utils/src/ambient_occlusion.slf b/libraries/render-utils/src/ambient_occlusion.slf index 649fb16c56..73bed26a9c 100644 --- a/libraries/render-utils/src/ambient_occlusion.slf +++ b/libraries/render-utils/src/ambient_occlusion.slf @@ -30,25 +30,49 @@ uniform float g_scale; uniform float g_bias; uniform float g_sample_rad; uniform float g_intensity; -uniform float bufferWidth; -uniform float bufferHeight; + +// the distance to the near clip plane +uniform float near; + +// scale factor for depth: (far - near) / far +uniform float depthScale; + +// offset for depth texture coordinates +uniform vec2 depthTexCoordOffset; + +// scale for depth texture coordinates +uniform vec2 depthTexCoordScale; + +// the resolution of the occlusion buffer +// and its inverse +uniform vec2 renderTargetRes; +uniform vec2 renderTargetResInv; + + const float PI = 3.14159265; -const vec2 FocalLen = vec2(1.0, 1.0); +// const vec2 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 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 vec2 AORes = vec2(1024.0, 768.0); +// const vec2 InvAORes = vec2(1.0/1024.0, 1.0/768.0); +// const vec2 NoiseScale = vec2(1024.0, 768.0) / 4.0; const float AOStrength = 1.9; -const float R = 0.3; -const float R2 = 0.3*0.3; -const float NegInvR2 = - 1.0 / (0.3*0.3); + +// const float R = 0.3; +// const float R2 = 0.3*0.3; +// const float NegInvR2 = - 1.0 / (0.3*0.3); + +const float R = 0.01; +const float R2 = 0.01*0.01; +const float NegInvR2 = - 1.0 / (0.01*0.01); + + + // can't use tan to initialize a const value -const float TanBias = 0.57735027; // tan(30.0 * PI / 180.0); +const float TanBias = 0.57735027; // tan(30.0 * PI / 180.0); const float MaxRadiusPixels = 50.0; const int NumDirections = 6; @@ -56,30 +80,43 @@ const int NumSamples = 4; out vec4 outFragColor; -float ViewSpaceZFromDepth(float d){ - // [0,1] -> [-1,1] clip space - d = d * 2.0 - 1.0; +// float ViewSpaceZFromDepth(float d){ +// // [0,1] -> [-1,1] clip space +// d = d * 2.0 - 1.0; - // Get view space Z - return -1.0 / (LinMAD.x * d + LinMAD.y); +// // Get view space Z +// return -1.0 / (LinMAD.x * d + LinMAD.y); +// } + +// vec3 UVToViewSpace(vec2 uv, float z){ +// //uv = UVToViewA * uv + UVToViewB; +// return vec3(uv * z, z); +// } + +// vec3 GetViewPos(vec2 uv){ +// float z = ViewSpaceZFromDepth(texture(depthTexture, uv).r); +// return UVToViewSpace(uv, z); +// } + +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); +} + +float ViewSpaceZFromDepth(float d){ + return near / (d * depthScale - 1.0); } vec3 UVToViewSpace(vec2 uv, float z){ - //uv = UVToViewA * uv + UVToViewB; - return vec3(uv * z, z); + return vec3((depthTexCoordOffset + varTexcoord * depthTexCoordScale) * z, z); } vec3 GetViewPos(vec2 uv){ - float z = ViewSpaceZFromDepth(texture(depthTexture, uv).r); - return UVToViewSpace(uv, z); + float z = ViewSpaceZFromDepth(texture(depthTexture, uv).r); + return UVToViewSpace(uv, z); } -vec3 GetViewPosPoint(ivec2 uv){ - vec2 coord = vec2(gl_FragCoord.xy) + uv; - //float z = texelFetch(texture0, coord, 0).r; - float z = texture(depthTexture, uv).r; - return UVToViewSpace(uv, z); -} float TanToSin(float x){ return x * inversesqrt(x*x + 1.0); @@ -112,7 +149,8 @@ vec3 MinDiff(vec3 P, vec3 Pr, vec3 Pl){ } vec2 SnapUVOffset(vec2 uv){ - return round(uv * AORes) * InvAORes; + // return round(uv * AORes) * InvAORes; + return round(uv * renderTargetRes) * renderTargetResInv; } float Falloff(float d2){ @@ -180,7 +218,8 @@ void ComputeSteps(inout vec2 stepSizeUv, inout float numSteps, float rayRadiusPi } // Step size in uv space - stepSizeUv = stepSizePix * InvAORes; + // stepSizeUv = stepSizePix * InvAORes; + stepSizeUv = stepSizePix * renderTargetResInv; } float getRandom(vec2 uv){ @@ -188,27 +227,39 @@ float getRandom(vec2 uv){ } void main(void){ + mat4 projMatrix = getTransformCamera()._projection; + float numDirections = NumDirections; vec3 P, Pr, Pl, Pt, Pb; P = GetViewPos(varTexcoord); // Sample neighboring pixels - Pr = GetViewPos(varTexcoord + vec2( InvAORes.x, 0)); - Pl = GetViewPos(varTexcoord + vec2(-InvAORes.x, 0)); - Pt = GetViewPos(varTexcoord + vec2( 0, InvAORes.y)); - Pb = GetViewPos(varTexcoord + vec2( 0,-InvAORes.y)); + // Pr = GetViewPos(varTexcoord + vec2( InvAORes.x, 0)); + // Pl = GetViewPos(varTexcoord + vec2(-InvAORes.x, 0)); + // Pt = GetViewPos(varTexcoord + vec2( 0, InvAORes.y)); + // Pb = GetViewPos(varTexcoord + vec2( 0,-InvAORes.y)); + Pr = GetViewPos(varTexcoord + vec2( renderTargetResInv.x, 0)); + Pl = GetViewPos(varTexcoord + vec2(-renderTargetResInv.x, 0)); + Pt = GetViewPos(varTexcoord + vec2( 0, renderTargetResInv.y)); + Pb = GetViewPos(varTexcoord + vec2( 0,-renderTargetResInv.y)); // Calculate tangent basis vectors using the minimum difference vec3 dPdu = MinDiff(P, Pr, Pl); - vec3 dPdv = MinDiff(P, Pt, Pb) * (AORes.y * InvAORes.x); + // vec3 dPdv = MinDiff(P, Pt, Pb) * (AORes.y * InvAORes.x); + vec3 dPdv = MinDiff(P, Pt, Pb) * (renderTargetRes.y * renderTargetResInv.x); // Get the random samples from the noise function vec3 random = vec3(getRandom(varTexcoord.xy), getRandom(varTexcoord.yx), getRandom(varTexcoord.xx)); // Calculate the projected size of the hemisphere - vec2 rayRadiusUV = 0.5 * R * FocalLen / -P.z; - float rayRadiusPix = rayRadiusUV.x * AORes.x; + // vec2 rayRadiusUV = 0.5 * R * FocalLen / -P.z; + // float rayRadiusPix = rayRadiusUV.x * AORes.x; + + // project the radius of the hemisphere into screen space + float w = P.z * projMatrix[2][3] + projMatrix[3][3]; + vec2 rayRadiusUV = (0.5 * R * vec2(projMatrix[0][0], projMatrix[1][1]) / w); // [-1,1] -> [0,1] uv + float rayRadiusPix = rayRadiusUV.x * renderTargetRes.x; float ao = 1.0; @@ -244,5 +295,6 @@ void main(void){ ao = 1.0 - ao / numDirections * AOStrength; } + outFragColor = vec4(vec3(ao), 1.0); -} \ No newline at end of file +} From 81ea8e88743bfd6f2ec6710da649dddf8018508a Mon Sep 17 00:00:00 2001 From: Christopher Root Date: Sat, 15 Aug 2015 14:27:08 -0700 Subject: [PATCH 04/42] Updating some comments and removing unnecessary code --- .../src/AmbientOcclusionEffect.cpp | 16 ++++--- .../render-utils/src/ambient_occlusion.slf | 47 +++++++------------ 2 files changed, 28 insertions(+), 35 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index b94bd09538..962b57a22c 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -219,22 +219,26 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons batch._glUniform1f(_gSampleRadiusLoc, g_sample_rad); batch._glUniform1f(_gIntensityLoc, g_intensity); - // setup uniforms for extracting depth from the depth buffer and - // converting that depth to a camera-space position, same as DeferredLightingEffect.cpp + // 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); - batch._glUniform1f(_nearLoc, nearVal); - float depthScale = (farVal - nearVal) / farVal; - batch._glUniform1f(_depthScaleLoc, depthScale); - 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); diff --git a/libraries/render-utils/src/ambient_occlusion.slf b/libraries/render-utils/src/ambient_occlusion.slf index 73bed26a9c..44ff79a179 100644 --- a/libraries/render-utils/src/ambient_occlusion.slf +++ b/libraries/render-utils/src/ambient_occlusion.slf @@ -52,19 +52,8 @@ uniform vec2 renderTargetResInv; const float PI = 3.14159265; -// const vec2 FocalLen = vec2(1.0, 1.0); -// const vec2 LinMAD = vec2(0.1-10.0, 0.1+10.0) / (2.0*0.1*10.0); - -// const vec2 AORes = vec2(1024.0, 768.0); -// const vec2 InvAORes = vec2(1.0/1024.0, 1.0/768.0); -// const vec2 NoiseScale = vec2(1024.0, 768.0) / 4.0; - const float AOStrength = 1.9; -// const float R = 0.3; -// const float R2 = 0.3*0.3; -// const float NegInvR2 = - 1.0 / (0.3*0.3); - const float R = 0.01; const float R2 = 0.01*0.01; const float NegInvR2 = - 1.0 / (0.01*0.01); @@ -80,38 +69,38 @@ const int NumSamples = 4; out vec4 outFragColor; -// float ViewSpaceZFromDepth(float d){ -// // [0,1] -> [-1,1] clip space -// d = d * 2.0 - 1.0; - -// // Get view space Z -// return -1.0 / (LinMAD.x * d + LinMAD.y); -// } - -// vec3 UVToViewSpace(vec2 uv, float z){ -// //uv = UVToViewA * uv + UVToViewB; -// return vec3(uv * z, z); -// } - -// vec3 GetViewPos(vec2 uv){ -// float z = ViewSpaceZFromDepth(texture(depthTexture, uv).r); -// return UVToViewSpace(uv, z); -// } - +/** + * 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){ return near / (d * depthScale - 1.0); } +/** + * Converts a uv coordinate and depth value into a 3D view space coordinate. + * uv: the uv coordinates to convert + * z: the view space depth of the uv coordinate. + */ vec3 UVToViewSpace(vec2 uv, float z){ return vec3((depthTexCoordOffset + varTexcoord * depthTexCoordScale) * z, z); } +/** + * Converts a uv coordinate into a 3D view space coordinate. + * The depth of the uv coord is determined from the depth texture. + * uv: the uv coordinates to convert + */ vec3 GetViewPos(vec2 uv){ float z = ViewSpaceZFromDepth(texture(depthTexture, uv).r); return UVToViewSpace(uv, z); From 8ea6048ec75d8c21c3f6d32090ad5fda8f736432 Mon Sep 17 00:00:00 2001 From: Christopher Root Date: Sat, 15 Aug 2015 14:31:03 -0700 Subject: [PATCH 05/42] Removing commented out code --- libraries/render-utils/src/ambient_occlusion.slf | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/libraries/render-utils/src/ambient_occlusion.slf b/libraries/render-utils/src/ambient_occlusion.slf index 44ff79a179..f304644604 100644 --- a/libraries/render-utils/src/ambient_occlusion.slf +++ b/libraries/render-utils/src/ambient_occlusion.slf @@ -54,6 +54,8 @@ const float PI = 3.14159265; const float AOStrength = 1.9; + +// 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); @@ -224,10 +226,6 @@ void main(void){ P = GetViewPos(varTexcoord); // Sample neighboring pixels - // Pr = GetViewPos(varTexcoord + vec2( InvAORes.x, 0)); - // Pl = GetViewPos(varTexcoord + vec2(-InvAORes.x, 0)); - // Pt = GetViewPos(varTexcoord + vec2( 0, InvAORes.y)); - // Pb = GetViewPos(varTexcoord + vec2( 0,-InvAORes.y)); Pr = GetViewPos(varTexcoord + vec2( renderTargetResInv.x, 0)); Pl = GetViewPos(varTexcoord + vec2(-renderTargetResInv.x, 0)); Pt = GetViewPos(varTexcoord + vec2( 0, renderTargetResInv.y)); @@ -235,17 +233,12 @@ void main(void){ // Calculate tangent basis vectors using the minimum difference vec3 dPdu = MinDiff(P, Pr, Pl); - // vec3 dPdv = MinDiff(P, Pt, Pb) * (AORes.y * InvAORes.x); vec3 dPdv = MinDiff(P, Pt, Pb) * (renderTargetRes.y * renderTargetResInv.x); // Get the random samples from the noise function vec3 random = vec3(getRandom(varTexcoord.xy), getRandom(varTexcoord.yx), getRandom(varTexcoord.xx)); // Calculate the projected size of the hemisphere - // vec2 rayRadiusUV = 0.5 * R * FocalLen / -P.z; - // float rayRadiusPix = rayRadiusUV.x * AORes.x; - - // project the radius of the hemisphere into screen space float w = P.z * projMatrix[2][3] + projMatrix[3][3]; vec2 rayRadiusUV = (0.5 * R * vec2(projMatrix[0][0], projMatrix[1][1]) / w); // [-1,1] -> [0,1] uv float rayRadiusPix = rayRadiusUV.x * renderTargetRes.x; From 21dc58eb85fcf795f98fa8e7be7d24ccc1274e10 Mon Sep 17 00:00:00 2001 From: Christopher Root Date: Sat, 15 Aug 2015 14:40:34 -0700 Subject: [PATCH 06/42] Improving readability in shader --- .../render-utils/src/ambient_occlusion.slf | 167 +++++++++--------- 1 file changed, 82 insertions(+), 85 deletions(-) diff --git a/libraries/render-utils/src/ambient_occlusion.slf b/libraries/render-utils/src/ambient_occlusion.slf index f304644604..8ab78891b0 100644 --- a/libraries/render-utils/src/ambient_occlusion.slf +++ b/libraries/render-utils/src/ambient_occlusion.slf @@ -103,95 +103,94 @@ vec3 UVToViewSpace(vec2 uv, float z){ * The depth of the uv coord is determined from the depth texture. * uv: the uv coordinates to convert */ -vec3 GetViewPos(vec2 uv){ +vec3 GetViewPos(vec2 uv) { float z = ViewSpaceZFromDepth(texture(depthTexture, uv).r); return UVToViewSpace(uv, z); } -float TanToSin(float x){ - return x * inversesqrt(x*x + 1.0); +float TanToSin(float x) { + return x * inversesqrt(x*x + 1.0); } -float InvLength(vec2 V){ - return inversesqrt(dot(V,V)); +float InvLength(vec2 V) { + return inversesqrt(dot(V, V)); } -float Tangent(vec3 V){ - return V.z * InvLength(V.xy); +float Tangent(vec3 V) { + return V.z * InvLength(V.xy); } -float BiasedTangent(vec3 V){ - return V.z * InvLength(V.xy) + TanBias; +float BiasedTangent(vec3 V) { + return V.z * InvLength(V.xy) + TanBias; } -float Tangent(vec3 P, vec3 S){ +float Tangent(vec3 P, vec3 S) { return -(P.z - S.z) * InvLength(S.xy - P.xy); } -float Length2(vec3 V){ - return dot(V,V); +float Length2(vec3 V) { + return dot(V, V); } -vec3 MinDiff(vec3 P, vec3 Pr, vec3 Pl){ +vec3 MinDiff(vec3 P, vec3 Pr, vec3 Pl) { vec3 V1 = Pr - P; vec3 V2 = P - Pl; return (Length2(V1) < Length2(V2)) ? V1 : V2; } -vec2 SnapUVOffset(vec2 uv){ - // return round(uv * AORes) * InvAORes; +vec2 SnapUVOffset(vec2 uv) { return round(uv * renderTargetRes) * renderTargetResInv; } -float Falloff(float d2){ - return d2 * NegInvR2 + 1.0f; +float Falloff(float d2) { + return d2 * NegInvR2 + 1.0f; } -float HorizonOcclusion( vec2 deltaUV, vec3 P, vec3 dPdu, vec3 dPdv, float randstep, float numSamples){ - float ao = 0; +float HorizonOcclusion(vec2 deltaUV, vec3 P, vec3 dPdu, vec3 dPdv, float randstep, float numSamples) { + float ao = 0; - // Offset the first coord with some noise - vec2 uv = varTexcoord + SnapUVOffset(randstep*deltaUV); - deltaUV = SnapUVOffset( deltaUV ); + // Offset the first coord with some noise + vec2 uv = varTexcoord + SnapUVOffset(randstep*deltaUV); + deltaUV = SnapUVOffset(deltaUV); - // Calculate the tangent vector - vec3 T = deltaUV.x * dPdu + deltaUV.y * dPdv; + // Calculate the tangent vector + vec3 T = deltaUV.x * dPdu + deltaUV.y * dPdv; - // Get the angle of the tangent vector from the viewspace axis - float tanH = BiasedTangent(T); - float sinH = TanToSin(tanH); + // Get the angle of the tangent vector from the viewspace axis + float tanH = BiasedTangent(T); + float sinH = TanToSin(tanH); - float tanS; - float d2; - vec3 S; + float tanS; + float d2; + vec3 S; - // Sample to find the maximum angle - for(float s = 1; s <= numSamples; ++s){ - uv += deltaUV; - S = GetViewPos(uv); - tanS = Tangent(P, S); - d2 = Length2(S - P); + // Sample to find the maximum angle + for (float s = 1; s <= numSamples; ++s) { + uv += deltaUV; + S = GetViewPos(uv); + tanS = Tangent(P, S); + d2 = Length2(S - P); - // Is the sample within the radius and the angle greater? - if(d2 < R2 && tanS > tanH) - { - float sinS = TanToSin(tanS); - // Apply falloff based on the distance - ao += Falloff(d2) * (sinS - sinH); + // Is the sample within the radius and the angle greater? + if (d2 < R2 && tanS > tanH) { + float sinS = TanToSin(tanS); + // Apply falloff based on the distance + ao += Falloff(d2) * (sinS - sinH); - tanH = tanS; - sinH = sinS; - } - } - return ao; + tanH = tanS; + sinH = sinS; + } + } + return ao; } -vec2 RotateDirections(vec2 Dir, vec2 CosSin){ - return vec2(Dir.x*CosSin.x - Dir.y*CosSin.y, Dir.x*CosSin.y + Dir.y*CosSin.x); +vec2 RotateDirections(vec2 Dir, vec2 CosSin) { + return vec2(Dir.x*CosSin.x - Dir.y*CosSin.y, + Dir.x*CosSin.y + Dir.y*CosSin.x); } -void ComputeSteps(inout vec2 stepSizeUv, inout float numSteps, float rayRadiusPix, float rand){ +void ComputeSteps(inout vec2 stepSizeUv, inout float numSteps, float rayRadiusPix, float rand) { // Avoid oversampling if numSteps is greater than the kernel radius in pixels numSteps = min(NumSamples, rayRadiusPix); @@ -200,8 +199,7 @@ void ComputeSteps(inout vec2 stepSizeUv, inout float numSteps, float rayRadiusPi // Clamp numSteps if it is greater than the max kernel footprint float maxNumSteps = MaxRadiusPixels / stepSizePix; - if (maxNumSteps < numSteps) - { + if (maxNumSteps < numSteps) { // Use dithering to avoid AO discontinuities numSteps = floor(maxNumSteps + rand); numSteps = max(numSteps, 1); @@ -209,23 +207,22 @@ void ComputeSteps(inout vec2 stepSizeUv, inout float numSteps, float rayRadiusPi } // Step size in uv space - // stepSizeUv = stepSizePix * InvAORes; stepSizeUv = stepSizePix * renderTargetResInv; } -float getRandom(vec2 uv){ +float getRandom(vec2 uv) { return fract(sin(dot(uv.xy ,vec2(12.9898,78.233))) * 43758.5453); } -void main(void){ +void main(void) { mat4 projMatrix = getTransformCamera()._projection; - float numDirections = NumDirections; + float numDirections = NumDirections; - vec3 P, Pr, Pl, Pt, Pb; - P = GetViewPos(varTexcoord); + vec3 P, Pr, Pl, Pt, Pb; + P = GetViewPos(varTexcoord); - // Sample neighboring pixels + // Sample neighboring pixels Pr = GetViewPos(varTexcoord + vec2( renderTargetResInv.x, 0)); Pl = GetViewPos(varTexcoord + vec2(-renderTargetResInv.x, 0)); Pt = GetViewPos(varTexcoord + vec2( 0, renderTargetResInv.y)); @@ -236,9 +233,9 @@ void main(void){ vec3 dPdv = MinDiff(P, Pt, Pb) * (renderTargetRes.y * renderTargetResInv.x); // Get the random samples from the noise function - vec3 random = vec3(getRandom(varTexcoord.xy), getRandom(varTexcoord.yx), getRandom(varTexcoord.xx)); + vec3 random = vec3(getRandom(varTexcoord.xy), getRandom(varTexcoord.yx), getRandom(varTexcoord.xx)); - // Calculate the projected size of the hemisphere + // Calculate the projected size of the hemisphere float w = P.z * projMatrix[2][3] + projMatrix[3][3]; vec2 rayRadiusUV = (0.5 * R * vec2(projMatrix[0][0], projMatrix[1][1]) / w); // [-1,1] -> [0,1] uv float rayRadiusPix = rayRadiusUV.x * renderTargetRes.x; @@ -246,36 +243,36 @@ void main(void){ float ao = 1.0; // Make sure the radius of the evaluated hemisphere is more than a pixel - if(rayRadiusPix > 1.0){ - ao = 0.0; - float numSteps; - vec2 stepSizeUV; + if(rayRadiusPix > 1.0) { + ao = 0.0; + float numSteps; + vec2 stepSizeUV; - // Compute the number of steps - ComputeSteps(stepSizeUV, numSteps, rayRadiusPix, random.z); + // Compute the number of steps + ComputeSteps(stepSizeUV, numSteps, rayRadiusPix, random.z); - float alpha = 2.0 * PI / numDirections; + float alpha = 2.0 * PI / numDirections; - // Calculate the horizon occlusion of each direction - for(float d = 0; d < numDirections; ++d){ - float theta = alpha * d; + // Calculate the horizon occlusion of each direction + for(float d = 0; d < numDirections; ++d) { + float theta = alpha * d; - // Apply noise to the direction - vec2 dir = RotateDirections(vec2(cos(theta), sin(theta)), random.xy); - vec2 deltaUV = dir * stepSizeUV; + // Apply noise to the direction + vec2 dir = RotateDirections(vec2(cos(theta), sin(theta)), random.xy); + vec2 deltaUV = dir * stepSizeUV; - // Sample the pixels along the direction - ao += HorizonOcclusion( deltaUV, - P, - dPdu, - dPdv, - random.z, - numSteps); - } + // Sample the pixels along the direction + ao += HorizonOcclusion( deltaUV, + P, + dPdu, + dPdv, + random.z, + numSteps); + } - // Average the results and produce the final AO - ao = 1.0 - ao / numDirections * AOStrength; - } + // Average the results and produce the final AO + ao = 1.0 - ao / numDirections * AOStrength; + } outFragColor = vec4(vec3(ao), 1.0); From e43dc3f9fed195c4ee99c1ccc3db81e92a9e8351 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 18 Aug 2015 03:21:48 +0200 Subject: [PATCH 07/42] Completed implementation of WebSocket and WebSocketServer --- libraries/script-engine/src/ScriptEngine.cpp | 2 + .../script-engine/src/WebSocketClass.cpp | 83 ++++++++++++++----- libraries/script-engine/src/WebSocketClass.h | 28 +++++-- .../src/WebSocketServerClass.cpp | 27 +++++- .../script-engine/src/WebSocketServerClass.h | 21 ++++- 5 files changed, 124 insertions(+), 37 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index bde0643c51..c629529382 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -361,6 +361,8 @@ void ScriptEngine::init() { qScriptRegisterMetaType(this, inputControllerToScriptValue, inputControllerFromScriptValue); qScriptRegisterMetaType(this, avatarDataToScriptValue, avatarDataFromScriptValue); qScriptRegisterMetaType(this, animationDetailsToScriptValue, animationDetailsFromScriptValue); + qScriptRegisterMetaType(this, webSocketToScriptValue, webSocketFromScriptValue); + qScriptRegisterMetaType(this, qWSCloseCodeToScriptValue, qWSCloseCodeFromScriptValue); registerGlobalObject("Script", this); registerGlobalObject("Audio", &AudioScriptingInterface::getInstance()); diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index f116c3a0e4..654b4746a8 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -16,13 +16,25 @@ #include "WebSocketClass.h" WebSocketClass::WebSocketClass(QScriptEngine* engine, QString url) : - _engine(engine) + _engine(engine), + _webSocket(new QWebSocket()) { - connect(&_webSocket, &QWebSocket::disconnected, this, &WebSocketClass::handleOnClose); - connect(&_webSocket, SIGNAL(error()), this, SLOT(handleOnError())); - connect(&_webSocket, &QWebSocket::textMessageReceived, this, &WebSocketClass::handleOnMessage); - connect(&_webSocket, &QWebSocket::connected, this, &WebSocketClass::handleOnOpen); - _webSocket.open(url); + 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); + _binaryType = "blob"; } QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine) { @@ -39,26 +51,37 @@ WebSocketClass::~WebSocketClass() { } void WebSocketClass::send(QScriptValue message) { - _webSocket.sendTextMessage(message.toString()); + _webSocket->sendTextMessage(message.toString()); +} + +void WebSocketClass::close() { + this->close(QWebSocketProtocol::CloseCode::CloseCodeNormal); +} + +void WebSocketClass::close(QWebSocketProtocol::CloseCode closeCode) { + this->close(closeCode, QStringLiteral("")); } void WebSocketClass::close(QWebSocketProtocol::CloseCode closeCode, QString reason) { - _webSocket.close(closeCode, reason); + _webSocket->close(closeCode, reason); } void WebSocketClass::handleOnClose() { - if (_onCloseEvent.isFunction()) { - //QScriptValueList args; - //args << ("received: " + message); - _onCloseEvent.call();//(QScriptValue(), args); + bool hasError = false; + if (_webSocket->error() != QAbstractSocket::SocketError::UnknownSocketError) { + hasError = true; + if (_onErrorEvent.isFunction()) { + _onErrorEvent.call(); + } } -} - -void WebSocketClass::handleOnError(QAbstractSocket::SocketError error) { - if (_onErrorEvent.isFunction()) { - // QScriptValueList args; - //args << ("received: " + message); - _onErrorEvent.call();/// QScriptValue(), args); + if (_onCloseEvent.isFunction()) { + QScriptValueList args; + QScriptValue arg = _engine->newObject(); + arg.setProperty("code", hasError ? QWebSocketProtocol::CloseCode::CloseCodeAbnormalDisconnection : _webSocket->closeCode()); + arg.setProperty("reason", _webSocket->closeReason()); + arg.setProperty("wasClean", !hasError); + args << arg; + _onCloseEvent.call(QScriptValue(), args); } } @@ -74,8 +97,22 @@ void WebSocketClass::handleOnMessage(const QString& message) { void WebSocketClass::handleOnOpen() { if (_onOpenEvent.isFunction()) { - //QScriptValueList args; - //args << ("received: " + message); - _onOpenEvent.call();// QScriptValue(), args); + _onOpenEvent.call(); } -} \ No newline at end of file +} + +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); +} + +void webSocketFromScriptValue(const QScriptValue &object, WebSocketClass* &out) { + out = qobject_cast(object.toQObject()); +} diff --git a/libraries/script-engine/src/WebSocketClass.h b/libraries/script-engine/src/WebSocketClass.h index 6f0e3729c0..fc70a72883 100644 --- a/libraries/script-engine/src/WebSocketClass.h +++ b/libraries/script-engine/src/WebSocketClass.h @@ -38,6 +38,7 @@ class WebSocketClass : public QObject { public: WebSocketClass(QScriptEngine* engine, QString url); + WebSocketClass(QScriptEngine* engine, QWebSocket* qWebSocket); ~WebSocketClass(); static QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine); @@ -57,19 +58,18 @@ public: void setBinaryType(QString binaryType) { _binaryType = binaryType; } QString getBinaryType() { return _binaryType; } - // extensions is a empty string until supported in QT + // extensions is a empty string until supported in QT WebSockets QString getExtensions() { return QString(); } - // protocol is a empty string until supported in QT + // protocol is a empty string until supported in QT WebSockets QString getProtocol() { return QString(); } - //TODO: find buffered amount ulong getBufferedAmount() { return 0; } - QString getURL() { return _webSocket.requestUrl().toDisplayString(); } + QString getURL() { return _webSocket->requestUrl().toDisplayString(); } uint getReadyState() { - switch (_webSocket.state()) { + switch (_webSocket->state()) { case QAbstractSocket::SocketState::HostLookupState: case QAbstractSocket::SocketState::ConnectingState: return CONNECTING; @@ -85,19 +85,25 @@ public: 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; + QWebSocket* _webSocket; QScriptEngine* _engine; QScriptValue _onCloseEvent; @@ -107,12 +113,20 @@ private: 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); +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); + #endif // hifi_WebSocketClass_h diff --git a/libraries/script-engine/src/WebSocketServerClass.cpp b/libraries/script-engine/src/WebSocketServerClass.cpp index 86f6506375..7981d67de3 100644 --- a/libraries/script-engine/src/WebSocketServerClass.cpp +++ b/libraries/script-engine/src/WebSocketServerClass.cpp @@ -14,11 +14,13 @@ #include "ScriptEngine.h" #include "WebSocketServerClass.h" -WebSocketServerClass::WebSocketServerClass(QScriptEngine* engine, const QString& serverName, quint16 port) : +WebSocketServerClass::WebSocketServerClass(QScriptEngine* engine, const QString& serverName, const quint16 port) : _engine(engine), _webSocketServer(serverName, QWebSocketServer::SslMode::NonSecureMode) { - _webSocketServer.listen(QHostAddress::Any, port); + if (_webSocketServer.listen(QHostAddress::Any, port)) { + connect(&_webSocketServer, &QWebSocketServer::newConnection, this, &WebSocketServerClass::onNewConnection); + } } QScriptValue WebSocketServerClass::constructor(QScriptContext* context, QScriptEngine* engine) { @@ -33,12 +35,31 @@ QScriptValue WebSocketServerClass::constructor(QScriptContext* context, QScriptE 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)); } WebSocketServerClass::~WebSocketServerClass() { if (_webSocketServer.isListening()) { - _webSocketServer.close(); + close(); } } + +void WebSocketServerClass::onNewConnection() { + WebSocketClass* newClient = new WebSocketClass(_engine, _webSocketServer.nextPendingConnection()); + _clients << 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(); +} diff --git a/libraries/script-engine/src/WebSocketServerClass.h b/libraries/script-engine/src/WebSocketServerClass.h index 18fcdc693e..cad993a508 100644 --- a/libraries/script-engine/src/WebSocketServerClass.h +++ b/libraries/script-engine/src/WebSocketServerClass.h @@ -15,24 +15,37 @@ #include #include #include +#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, quint16 port); + 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 WebSocketServerClass::constructor(QScriptContext* context, QScriptEngine* engine); +public slots: + void close(); + private: QWebSocketServer _webSocketServer; QScriptEngine* _engine; + QList _clients; + +private slots: + void onNewConnection(); signals: - void newConnection(); - - + void newConnection(WebSocketClass* client); }; From 29c857f1fa1d95913e7b3a0fc949c8e7fe5a21e6 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 18 Aug 2015 03:24:26 +0200 Subject: [PATCH 08/42] - unitTests for WebSockets + WebSocketServer (added some asynchronous functionality to the unitTest.js library , previous functionality intact) --- examples/libraries/unitTest.js | 107 +++++++++++++++--- .../utilities/diagnostics/testWebSocket.js | 100 ++++++++++++++++ 2 files changed, 192 insertions(+), 15 deletions(-) create mode 100644 examples/utilities/diagnostics/testWebSocket.js diff --git a/examples/libraries/unitTest.js b/examples/libraries/unitTest.js index beb3387898..d65cd780bd 100644 --- a/examples/libraries/unitTest.js +++ b/examples/libraries/unitTest.js @@ -11,17 +11,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var test = function(name, func) { +test = function(name, func, timeout) { print("Running test: " + name); - - var unitTest = new UnitTest(name, func); - - try { - unitTest.run(); + var unitTest = new UnitTest(name, func, timeout); + unitTest.run(function(unitTest) { print(" Success: " + unitTest.numAssertions + " assertions passed"); - } catch (error) { + }, function(unitTest, error) { print(" Failure: " + error.name + " " + error.message); - } + }); }; AssertionException = function(expected, actual, message) { @@ -36,13 +33,86 @@ UnthrownException = function(message) { this.name = 'UnthrownException'; }; -UnitTest = function(name, func) { - this.numAssertions = 0; - this.func = func; +TimeoutException = function() { + print("Creating exception"); + this.message = "UnitTest timed out\n"; + this.name = 'TimeoutException'; }; -UnitTest.prototype.run = function() { - this.func(); +SequentialUnitTester = function() { + this.tests = []; + this.testIndex = -1; +}; + +SequentialUnitTester.prototype.addTest = function(name, func, timeout) { + var _this = this; + this.tests.push(function() { + print("Running test: " + name); + var unitTest = new UnitTest(name, func, timeout); + unitTest.run(function(unitTest) { + print(" Success: " + unitTest.numAssertions + " assertions passed"); + _this._nextTest(); + }, function(unitTest, error) { + print(" Failure: " + error.name + " " + error.message); + _this._nextTest(); + }); + }); +}; + +SequentialUnitTester.prototype._nextTest = function() { + this.testIndex++; + if (this.testIndex < this.tests.length) { + this.tests[this.testIndex](); + return; + } + print("Completed all UnitTests"); +}; + +SequentialUnitTester.prototype.run = function() { + this._nextTest(); +}; + +UnitTest = function(name, func, timeout) { + this.numAssertions = 0; + this.func = func; + this.timeout = timeout; +}; + +UnitTest.prototype.run = function(successCallback, failureCallback) { + var _this = this; + this.successCallback = successCallback; + this.failureCallback = failureCallback; + if (this.timeout !== undefined) { + this.timeoutTimer = Script.setTimeout(function() { + _this.failureCallback(this, new TimeoutException()); + }, this.timeout); + } + try { + this.func(); + if (this.timeout === undefined) { + successCallback(this); + } + } catch (exception) { + this.handleException(exception); + } +}; + +UnitTest.prototype.registerCallbackFunction = function(func) { + var _this = this; + return function(one, two, three, four, five, six) { + try { + func(one, two, three, four, five, six); + } catch (exception) { + _this.handleException(exception); + } + }; +}; + +UnitTest.prototype.handleException = function(exception) { + if (this.timeout !== undefined) { + Script.clearTimeout(this.timeoutTimer); + } + this.failureCallback(this, exception); }; UnitTest.prototype.assertNotEquals = function(expected, actual, message) { @@ -83,7 +153,7 @@ UnitTest.prototype.assertNull = function(value, message) { UnitTest.prototype.arrayEqual = function(array1, array2, message) { this.numAssertions++; if (array1.length !== array2.length) { - throw new AssertionException(array1.length , array2.length , message); + throw new AssertionException(array1.length, array2.length , message); } for (var i = 0; i < array1.length; ++i) { if (array1[i] !== array2[i]) { @@ -101,4 +171,11 @@ UnitTest.prototype.raises = function(func, message) { } throw new UnthrownException(message); -} \ No newline at end of file +} + +UnitTest.prototype.ready = function() { + if (this.timeout !== undefined) { + Script.clearTimeout(this.timeoutTimer); + this.successCallback(this); + } +} diff --git a/examples/utilities/diagnostics/testWebSocket.js b/examples/utilities/diagnostics/testWebSocket.js new file mode 100644 index 0000000000..03b9b6b405 --- /dev/null +++ b/examples/utilities/diagnostics/testWebSocket.js @@ -0,0 +1,100 @@ +// +// 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.ready(); + }); + 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; + }); + webSocket.onclose = this.registerCallbackFunction(function(event) { + _this.assertEquals(true, hadError, "hadError should be true"); + _this.assertEquals(webSocket.CLOSED, webSocket.readyState, "readyState should be CLOSED"); + _this.ready(); + }); + 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); + + +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.ready(); + } + }); + 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(); From 1b6538cc59fa154fa9302247cb5770b003a71072 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 19 Aug 2015 00:31:40 +0200 Subject: [PATCH 09/42] fixes extra qualification errors Linux --- libraries/script-engine/src/WebSocketClass.cpp | 2 +- libraries/script-engine/src/WebSocketServerClass.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 654b4746a8..7283f2c47f 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -37,7 +37,7 @@ void WebSocketClass::initialize() { _binaryType = "blob"; } -QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine) { +QScriptValue constructor(QScriptContext* context, QScriptEngine* engine) { QString url; QScriptValue callee = context->callee(); if (context->argumentCount() == 1) { diff --git a/libraries/script-engine/src/WebSocketServerClass.h b/libraries/script-engine/src/WebSocketServerClass.h index cad993a508..972bf9c032 100644 --- a/libraries/script-engine/src/WebSocketServerClass.h +++ b/libraries/script-engine/src/WebSocketServerClass.h @@ -31,7 +31,7 @@ public: quint16 getPort() { return _webSocketServer.serverPort(); } bool isListening() { return _webSocketServer.isListening(); } - static QScriptValue WebSocketServerClass::constructor(QScriptContext* context, QScriptEngine* engine); + static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); public slots: void close(); From 7f8506acc0c4851a090dc74986d994c79f3415d1 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 19 Aug 2015 00:54:00 +0200 Subject: [PATCH 10/42] fix. switchup. --- libraries/script-engine/src/WebSocketClass.cpp | 2 +- libraries/script-engine/src/WebSocketClass.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 7283f2c47f..654b4746a8 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -37,7 +37,7 @@ void WebSocketClass::initialize() { _binaryType = "blob"; } -QScriptValue constructor(QScriptContext* context, QScriptEngine* engine) { +QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine) { QString url; QScriptValue callee = context->callee(); if (context->argumentCount() == 1) { diff --git a/libraries/script-engine/src/WebSocketClass.h b/libraries/script-engine/src/WebSocketClass.h index fc70a72883..e053257e3d 100644 --- a/libraries/script-engine/src/WebSocketClass.h +++ b/libraries/script-engine/src/WebSocketClass.h @@ -41,7 +41,7 @@ public: WebSocketClass(QScriptEngine* engine, QWebSocket* qWebSocket); ~WebSocketClass(); - static QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine); + static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); enum ReadyState { CONNECTING = 0, From 07ae00f20715b8a5c0ab814c31c50c9b49d5edca Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 19 Aug 2015 19:33:48 +0200 Subject: [PATCH 11/42] Close WebSocket properly after ScriptEngine finished running --- libraries/script-engine/src/WebSocketServerClass.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/script-engine/src/WebSocketServerClass.cpp b/libraries/script-engine/src/WebSocketServerClass.cpp index 7981d67de3..9fc060e61c 100644 --- a/libraries/script-engine/src/WebSocketServerClass.cpp +++ b/libraries/script-engine/src/WebSocketServerClass.cpp @@ -40,7 +40,9 @@ QScriptValue WebSocketServerClass::constructor(QScriptContext* context, QScriptE serverName = serverNameOption.toString(); } } - return engine->newQObject(new WebSocketServerClass(engine, serverName, port)); + auto webSocketServerClass = new WebSocketServerClass(engine, serverName, port); + connect(engine, SIGNAL(finished(const QString&)), webSocketServerClass, SLOT(deleteLater())); + return engine->newQObject(webSocketServerClass); } WebSocketServerClass::~WebSocketServerClass() { From ecd368fac35172a5de2c52259366c4934e4bdde5 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 19 Aug 2015 20:00:06 +0200 Subject: [PATCH 12/42] new style qt connectors --- libraries/script-engine/src/WebSocketServerClass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/script-engine/src/WebSocketServerClass.cpp b/libraries/script-engine/src/WebSocketServerClass.cpp index 9fc060e61c..dbe92510e4 100644 --- a/libraries/script-engine/src/WebSocketServerClass.cpp +++ b/libraries/script-engine/src/WebSocketServerClass.cpp @@ -41,7 +41,7 @@ QScriptValue WebSocketServerClass::constructor(QScriptContext* context, QScriptE } } auto webSocketServerClass = new WebSocketServerClass(engine, serverName, port); - connect(engine, SIGNAL(finished(const QString&)), webSocketServerClass, SLOT(deleteLater())); + connect(static_cast(engine), &ScriptEngine::finished, webSocketServerClass, &QObject::deleteLater); return engine->newQObject(webSocketServerClass); } From 76f236adf61f4103298cb3d37beeff688161efbb Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 12 Aug 2015 22:44:16 -0700 Subject: [PATCH 13/42] New stereo rendering implementation --- interface/src/Application.cpp | 111 ++++++---------- interface/src/Stars.cpp | 4 +- interface/src/ui/ApplicationCompositor.cpp | 2 - interface/src/ui/ApplicationOverlay.cpp | 1 - .../src/display-plugins/DisplayPlugin.cpp | 2 +- .../src/display-plugins/DisplayPlugin.h | 4 + .../oculus/OculusBaseDisplayPlugin.cpp | 8 +- .../oculus/OculusBaseDisplayPlugin.h | 4 +- .../stereo/SideBySideStereoDisplayPlugin.cpp | 2 +- .../stereo/StereoDisplayPlugin.cpp | 12 +- .../stereo/StereoDisplayPlugin.h | 3 + libraries/gpu/src/gpu/Batch.cpp | 15 +++ libraries/gpu/src/gpu/Batch.h | 18 ++- libraries/gpu/src/gpu/Context.cpp | 35 +++++ libraries/gpu/src/gpu/Context.h | 44 ++++++- libraries/gpu/src/gpu/Forward.h | 77 +++++++++++ libraries/gpu/src/gpu/GLBackend.cpp | 80 ++++++------ libraries/gpu/src/gpu/GLBackend.h | 27 ++-- libraries/gpu/src/gpu/GLBackendOutput.cpp | 3 + libraries/gpu/src/gpu/GLBackendState.cpp | 6 + libraries/gpu/src/gpu/GLBackendTransform.cpp | 121 +++++++++++------- .../input-plugins/ViveControllerManager.cpp | 1 - .../src/AmbientOcclusionEffect.cpp | 1 - .../src/DeferredLightingEffect.cpp | 5 + .../render-utils/src/RenderDeferredTask.cpp | 65 ++++++---- libraries/render/src/render/DrawStatus.cpp | 5 +- libraries/render/src/render/DrawTask.cpp | 6 +- 27 files changed, 452 insertions(+), 210 deletions(-) create mode 100644 libraries/gpu/src/gpu/Forward.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b9bafbab1a..68b2322394 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1058,29 +1058,18 @@ void Application::paintGL() { // Using the latter will cause the camera to wobble with idle animations, // or with changes from the face tracker renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; - - if (!getActiveDisplayPlugin()->isHmd()) { - _myCamera.setPosition(_myAvatar->getDefaultEyePosition()); - _myCamera.setRotation(_myAvatar->getHead()->getCameraOrientation()); - } else { - mat4 camMat = _myAvatar->getSensorToWorldMatrix() * _myAvatar->getHMDSensorMatrix(); - _myCamera.setPosition(extractTranslation(camMat)); - _myCamera.setRotation(glm::quat_cast(camMat)); - } + _myCamera.setPosition(_myAvatar->getDefaultEyePosition()); + _myCamera.setRotation(_myAvatar->getOrientation()); } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { - if (isHMDMode()) { - _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation()); - } else { - _myCamera.setRotation(_myAvatar->getHead()->getOrientation()); - } - if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { - _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + - _myCamera.getRotation() * glm::vec3(0.0f, 0.0f, 1.0f) * _myAvatar->getBoomLength() * _myAvatar->getScale()); - } else { - _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + - _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, 1.0f) * _myAvatar->getBoomLength() * _myAvatar->getScale()); - } + _myCamera.setRotation(_myAvatar->getOrientation()); + // https://www.youtube.com/watch?v=pFriRcIwqNU + vec3 boomStick = glm::vec3(0.0f, 0.0f, 1.0f) * _myAvatar->getBoomLength() * _myAvatar->getScale(); + quat boomRotation = _myAvatar->getOrientation(); + if (!isHMDMode() && Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { + boomRotation = _myCamera.getRotation(); + } + _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + boomRotation * boomStick); } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + @@ -1089,7 +1078,6 @@ void Application::paintGL() { glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; } - // Update camera position if (!isHMDMode()) { _myCamera.update(1.0f / _fps); @@ -1105,57 +1093,37 @@ void Application::paintGL() { QSize size = DependencyManager::get()->getFrameBufferSize(); renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height()); - { - PROFILE_RANGE(__FUNCTION__ "/clear"); - doInBatch(&renderArgs, [&](gpu::Batch& batch) { - auto primaryFbo = DependencyManager::get()->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); - }); - } + doInBatch(&renderArgs, [&](gpu::Batch& batch) { + auto primaryFbo = DependencyManager::get()->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); + }); + renderArgs._viewport = gpu::Vec4i(0, 0, size.width(), size.height()); if (displayPlugin->isStereo()) { - PROFILE_RANGE(__FUNCTION__ "/stereoRender"); - QRect currentViewport(QPoint(0, 0), QSize(size.width() / 2, size.height())); - glEnable(GL_SCISSOR_TEST); - for_each_eye([&](Eye eye){ - // Load the view frustum, used by meshes - Camera eyeCamera; - if (qApp->isHMDMode()) { - // Allow the displayPlugin to compose the final eye transform, based on the most up-to-date head motion. - eyeCamera.setTransform(displayPlugin->getModelview(eye, _myAvatar->getSensorToWorldMatrix())); - } else { - eyeCamera.setTransform(displayPlugin->getModelview(eye, _myCamera.getTransform())); - } - eyeCamera.setProjection(displayPlugin->getProjection(eye, _myCamera.getProjection())); - renderArgs._viewport = toGlm(currentViewport); - doInBatch(&renderArgs, [&](gpu::Batch& batch) { - batch.setViewportTransform(renderArgs._viewport); - batch.setStateScissorRect(renderArgs._viewport); - }); - displaySide(&renderArgs, eyeCamera); - }, [&] { - currentViewport.moveLeft(currentViewport.width()); + //_myCamera.setProjection(displayPlugin->getProjection(Mono, _myCamera.getProjection())); + renderArgs._context->enableStereo(true); + mat4 eyeViews[2]; + mat4 eyeProjections[2]; + auto baseProjection = renderArgs._viewFrustum->getProjection(); + // FIXME we don't need to set these every frame, + // only when the display plugin changes + for_each_eye([&](Eye eye) { + eyeViews[eye] = displayPlugin->getModelview(eye, mat4()); + eyeProjections[eye] = displayPlugin->getProjection(eye, baseProjection); }); - glDisable(GL_SCISSOR_TEST); - } else { - 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); + renderArgs._context->setStereoProjections(eyeProjections); + renderArgs._context->setStereoViews(eyeViews); } - - doInBatch(&renderArgs, [](gpu::Batch& batch){ + displaySide(&renderArgs, _myCamera); + renderArgs._context->enableStereo(false); + doInBatch(&renderArgs, [](gpu::Batch& batch) { batch.setFramebuffer(nullptr); }); } @@ -4997,6 +4965,11 @@ mat4 Application::getHMDSensorPose() const { return mat4(); } +// FIXME there is a bug in the fullscreen setting, where leaving +// fullscreen does not restore the window frame, making it difficult +// or impossible to move or size the window. +// Additionally, setting fullscreen isn't hiding the menu on windows +// make it useless for stereoscopic modes. void Application::setFullscreen(const QScreen* target) { if (!_window->isFullScreen()) { _savedGeometry = _window->geometry(); diff --git a/interface/src/Stars.cpp b/interface/src/Stars.cpp index 42b1a3f2e2..119b9ed1a2 100644 --- a/interface/src/Stars.cpp +++ b/interface/src/Stars.cpp @@ -191,7 +191,7 @@ void Stars::render(RenderArgs* renderArgs, float alpha) { auto geometryCache = DependencyManager::get(); auto textureCache = DependencyManager::get(); - gpu::Batch batch; + gpu::Batch& batch = *renderArgs->_batch; batch.setViewTransform(Transform()); batch.setProjectionTransform(renderArgs->_viewFrustum->getProjection()); 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(COLOR_SLOT, colView); batch.draw(gpu::Primitive::POINTS, STARFIELD_NUM_STARS); - - renderArgs->_context->render(batch); } diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index abc1e49101..98634d7aed 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -211,7 +211,6 @@ void ApplicationCompositor::displayOverlayTexture(RenderArgs* renderArgs) { //Handle fading and deactivation/activation of UI gpu::Batch batch; - renderArgs->_context->syncCache(); auto geometryCache = DependencyManager::get(); geometryCache->useSimpleDrawPipeline(batch); @@ -279,7 +278,6 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int vec2 canvasSize = qApp->getCanvasSize(); _textureAspectRatio = aspect(canvasSize); - renderArgs->_context->syncCache(); auto geometryCache = DependencyManager::get(); gpu::Batch batch; diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 6f18cac127..7254295c2f 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -92,7 +92,6 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope renderStatsAndLogs(renderArgs); // currently renders nothing - renderArgs->_context->syncCache(); renderArgs->_context->render(batch); renderArgs->_batch = nullptr; // so future users of renderArgs don't try to use our batch diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp index 2316ff70c4..8bfe8c20da 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp @@ -28,7 +28,7 @@ DisplayPluginList getDisplayPlugins() { // Stereo modes // FIXME fix stereo display plugins - //new SideBySideStereoDisplayPlugin(), + new SideBySideStereoDisplayPlugin(), //new InterleavedStereoDisplayPlugin(), // HMDs diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h index 45a5923a1f..da3bc98135 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h @@ -107,6 +107,10 @@ public: static const glm::mat4 pose; return pose; } + virtual glm::vec3 getEyeOffset(Eye eye) const { + static const glm::vec3 offset; return offset; + } + virtual glm::mat4 getHeadPose() const { static const glm::mat4 pose; return pose; } diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp index 7d0fb705df..0490e82106 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp @@ -14,6 +14,9 @@ using namespace Oculus; +OculusBaseDisplayPlugin::OculusBaseDisplayPlugin() : _ipd(OVR_DEFAULT_IPD) { +} + void OculusBaseDisplayPlugin::activate() { glm::uvec2 eyeSizes[2]; ovr_for_each_eye([&](ovrEyeType eye) { @@ -27,9 +30,12 @@ void OculusBaseDisplayPlugin::activate() { ovrMatrix4f_Projection(erd.Fov, 0.001f, 10.0f, ovrProjection_RightHanded); _compositeEyeProjections[eye] = toGlm(ovrPerspectiveProjection); - _eyeOffsets[eye] = erd.HmdToEyeViewOffset; + // We handle the eye offsets slightly differently, using an _ipd in the base class + // _eyeOffsets[eye] = erd.HmdToEyeViewOffset; + _eyeOffsets[eye] = { 0, 0, 0 }; eyeSizes[eye] = toGlm(ovrHmd_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f)); }); + _ipd = ovrHmd_GetFloat(_hmd, OVR_KEY_IPD, _ipd); _desiredFramebufferSize = uvec2( eyeSizes[0].x + eyeSizes[1].x, std::max(eyeSizes[0].y, eyeSizes[1].y)); diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h index e376ae12ba..401ee6579a 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h @@ -11,6 +11,7 @@ class OculusBaseDisplayPlugin : public MainWindowOpenGLDisplayPlugin { public: + OculusBaseDisplayPlugin(); // Stereo specific methods virtual bool isHmd() const override { return true; } virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override; @@ -22,5 +23,6 @@ public: virtual void resetSensors() override; virtual glm::mat4 getEyePose(Eye eye) const override; virtual glm::mat4 getHeadPose() const override; - +protected: + float _ipd; }; diff --git a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp index e348143250..72ef5249a8 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp @@ -17,7 +17,7 @@ #include -const QString SideBySideStereoDisplayPlugin::NAME("SBS Stereo Display"); +const QString SideBySideStereoDisplayPlugin::NAME("Debug Stereo Display"); const QString & SideBySideStereoDisplayPlugin::getName() const { return NAME; diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index c741967328..3d5f73bfa8 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -52,6 +52,16 @@ glm::mat4 StereoDisplayPlugin::getModelview(Eye eye, const glm::mat4& baseModelv void StereoDisplayPlugin::activate() { WindowOpenGLDisplayPlugin::activate(); - CONTAINER->setFullscreen(qApp->primaryScreen()); + // FIXME there is a bug in the fullscreen setting, see + // Application::setFullscreen + //CONTAINER->setFullscreen(qApp->primaryScreen()); // FIXME Add menu items } + +glm::vec3 StereoDisplayPlugin::getEyeOffset(Eye eye) const { + glm::vec3 result(_ipd / 2.0f, 0, 0); + if (eye == Eye::Right) { + result *= -1.0f; + } + return result; +} diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h index bb1a4fd42c..77b1141aed 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h @@ -20,5 +20,8 @@ public: 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::vec3 getEyeOffset(Eye eye) const override; +protected: + float _ipd{ 0.064f }; }; diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index 3ecbc3b2f3..fb6618e953 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -288,3 +288,18 @@ void Batch::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; +} \ No newline at end of file diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index d48ffef209..ca74032c5e 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -26,7 +26,7 @@ ProfileRange(const char *name); ~ProfileRange(); }; - #define PROFILE_RANGE(name) ProfileRange profileRangeThis(name); +#define PROFILE_RANGE(name) ProfileRange profileRangeThis(name); #else #define PROFILE_RANGE(name) #endif @@ -47,6 +47,19 @@ public: ~Batch(); 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 void draw(Primitive primitiveType, uint32 numVertices, uint32 startVertex = 0); @@ -276,6 +289,9 @@ public: FramebufferCaches _framebuffers; QueryCaches _queries; + bool _enableStereo{ true }; + bool _enableSkybox{ false }; + protected: }; diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index 239c460c77..561a51b477 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -40,6 +40,18 @@ void Context::render(Batch& 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() { PROFILE_RANGE(__FUNCTION__); _backend->syncCache(); @@ -49,3 +61,26 @@ void Context::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, cons _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; +} diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 7158bd1a6d..9aca83e577 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -14,6 +14,8 @@ #include #include +#include + #include "Batch.h" #include "Resource.h" @@ -25,28 +27,58 @@ class QImage; 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 { public: virtual~ Backend() {}; 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 downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0; + // UBO class... layout MUST match the layout in TransformCamera.slh class TransformObject { public: Mat4 _model; Mat4 _modelInverse; }; + // UBO class... layout MUST match the layout in TransformCamera.slh class TransformCamera { public: Mat4 _view; - Mat4 _viewInverse; - Mat4 _projectionViewUntranslated; + mutable Mat4 _viewInverse; + mutable Mat4 _projectionViewUntranslated; 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. + + const Backend::TransformCamera& recomputeDerived() const; + TransformCamera getEyeCamera(int eye, const StereoState& stereo) const; }; template< typename T > @@ -113,7 +145,7 @@ public: } protected: - + StereoState _stereo; }; class Context { @@ -136,7 +168,9 @@ public: ~Context(); void render(Batch& batch); - + void enableStereo(bool enable = true); + void setStereoProjections(const mat4 eyeProjections[2]); + void setStereoViews(const mat4 eyeViews[2]); void syncCache(); // Downloading the Framebuffer is a synchronous action that is not efficient. diff --git a/libraries/gpu/src/gpu/Forward.h b/libraries/gpu/src/gpu/Forward.h new file mode 100644 index 0000000000..0fa315ef1c --- /dev/null +++ b/libraries/gpu/src/gpu/Forward.h @@ -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 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 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 SwapchainPointer; + class Framebuffer; + typedef std::shared_ptr FramebufferPointer; + class Pipeline; + typedef std::shared_ptr PipelinePointer; + typedef std::vector Pipelines; + class Query; + typedef std::shared_ptr QueryPointer; + typedef std::vector Queries; + class Resource; + class Buffer; + typedef std::shared_ptr BufferPointer; + typedef std::vector Buffers; + class BufferView; + class Shader; + typedef Shader::Pointer ShaderPointer; + typedef std::vector Shaders; + class State; + typedef std::shared_ptr StatePointer; + typedef std::vector States; + class Stream; + class BufferStream; + typedef std::shared_ptr BufferStreamPointer; + class Texture; + class SphericalHarmonics; + typedef std::shared_ptr SHPointer; + class Sampler; + class Texture; + typedef std::shared_ptr TexturePointer; + typedef std::vector Textures; + class TextureView; + typedef std::vector TextureViews; +} + +#endif diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 4afd2ba940..2270c0dce7 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -127,39 +127,29 @@ void GLBackend::renderPassTransfer(Batch& batch) { const Batch::Commands::value_type* command = batch.getCommands().data(); const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data(); - _transform._cameraTransforms.resize(0); - _transform._cameraTransforms.push_back(TransformCamera()); + // Reset the transform buffers + _transform._cameras.resize(0); _transform._cameraOffsets.clear(); - _transform._cameraOffsets.push_back(TransformStageState::Pair(0, 0)); - - _transform._objectTransforms.push_back(TransformObject()); - _transform._objectOffsets.push_back(TransformStageState::Pair(0, 0)); + _transform._objects.resize(0); _transform._objectOffsets.clear(); - _transform._objectTransforms.resize(0); - - _commandIndex = 0; - preUpdateTransform(); - int drawCount = 0; for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) { switch (*command) { case Batch::COMMAND_draw: case Batch::COMMAND_drawIndexed: case Batch::COMMAND_drawInstanced: case Batch::COMMAND_drawIndexedInstanced: - preUpdateTransform(); - ++drawCount; + _transform.preUpdate(_commandIndex, _stereo); break; case Batch::COMMAND_setModelTransform: case Batch::COMMAND_setViewportTransform: case Batch::COMMAND_setViewTransform: - case Batch::COMMAND_setProjectionTransform: - { + case Batch::COMMAND_setProjectionTransform: { CommandCall call = _commandCalls[(*command)]; (this->*(call))(batch, *offset); + break; } - break; default: break; @@ -167,44 +157,31 @@ void GLBackend::renderPassTransfer(Batch& batch) { command++; offset++; } - - - 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(); + _transform.transfer(); } void GLBackend::renderPassDraw(Batch& batch) { + _transform._objectsItr = _transform._objectOffsets.begin(); + _transform._camerasItr = _transform._cameraOffsets.begin(); const size_t numCommands = batch.getCommands().size(); const Batch::Commands::value_type* command = batch.getCommands().data(); const Batch::CommandOffsets::value_type* offset = batch.getCommandOffsets().data(); for (_commandIndex = 0; _commandIndex < numCommands; ++_commandIndex) { switch (*command) { // 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_setViewportTransform: case Batch::COMMAND_setViewTransform: case Batch::COMMAND_setProjectionTransform: break; - default: - { + default: { CommandCall call = _commandCalls[(*command)]; (this->*(call))(batch, *offset); + break; } - break; } command++; @@ -213,8 +190,33 @@ void GLBackend::renderPassDraw(Batch& batch) { } void GLBackend::render(Batch& batch) { - renderPassTransfer(batch); - renderPassDraw(batch); + _stereo._skybox = batch.isSkyboxEnabled(); + // 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) { diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 455240deb8..c97ea4e615 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -310,19 +310,22 @@ protected: void killTransform(); // Synchronize the state cache of this Backend with the actual real state of the GL Context void syncTransformStateCache(); - void updateTransform(); - void preUpdateTransform(); + void updateTransform() const; void resetTransformStage(); - struct TransformStageState { - TransformObject _transformObject; - TransformCamera _transformCamera; - std::vector _objectTransforms; - std::vector _cameraTransforms; + struct TransformStageState { + using TransformObjects = std::vector; + using TransformCameras = std::vector; + + TransformObject _object; + TransformCamera _camera; + TransformObjects _objects; + TransformCameras _cameras; + size_t _cameraUboSize{ 0 }; size_t _objectUboSize{ 0 }; - GLuint _transformObjectBuffer{ 0 }; - GLuint _transformCameraBuffer{ 0 }; + GLuint _objectBuffer{ 0 }; + GLuint _cameraBuffer{ 0 }; Transform _model; Transform _view; Mat4 _projection; @@ -336,6 +339,12 @@ protected: using List = std::list; List _cameraOffsets; 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; int32_t _uboAlignment{ 0 }; diff --git a/libraries/gpu/src/gpu/GLBackendOutput.cpp b/libraries/gpu/src/gpu/GLBackendOutput.cpp index d75d0cf521..33ae1dd0a3 100755 --- a/libraries/gpu/src/gpu/GLBackendOutput.cpp +++ b/libraries/gpu/src/gpu/GLBackendOutput.cpp @@ -198,6 +198,9 @@ void GLBackend::do_setFramebuffer(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; Vec4 color; diff --git a/libraries/gpu/src/gpu/GLBackendState.cpp b/libraries/gpu/src/gpu/GLBackendState.cpp index c5cc987fd1..9fdcbc0870 100644 --- a/libraries/gpu/src/gpu/GLBackendState.cpp +++ b/libraries/gpu/src/gpu/GLBackendState.cpp @@ -768,6 +768,12 @@ void GLBackend::do_setStateScissorRect(Batch& batch, uint32 paramOffset) { Vec4i rect; 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); (void) CHECK_GL_ERROR(); } diff --git a/libraries/gpu/src/gpu/GLBackendTransform.cpp b/libraries/gpu/src/gpu/GLBackendTransform.cpp index 23b3dba14e..e33a8f8cde 100755 --- a/libraries/gpu/src/gpu/GLBackendTransform.cpp +++ b/libraries/gpu/src/gpu/GLBackendTransform.cpp @@ -33,16 +33,26 @@ void GLBackend::do_setProjectionTransform(Batch& batch, uint32 paramOffset) { void GLBackend::do_setViewportTransform(Batch& batch, uint32 paramOffset) { memcpy(&_transform._viewport, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i)); + ivec4& vp = _transform._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 _transform._invalidViewport = true; } void GLBackend::initTransform() { - glGenBuffers(1, &_transform._transformObjectBuffer); - glGenBuffers(1, &_transform._transformCameraBuffer); + glGenBuffers(1, &_transform._objectBuffer); + glGenBuffers(1, &_transform._cameraBuffer); size_t cameraSize = sizeof(TransformCamera); while (_transform._cameraUboSize < cameraSize) { _transform._cameraUboSize += _uboAlignment; @@ -54,8 +64,8 @@ void GLBackend::initTransform() { } void GLBackend::killTransform() { - glDeleteBuffers(1, &_transform._transformObjectBuffer); - glDeleteBuffers(1, &_transform._transformCameraBuffer); + glDeleteBuffers(1, &_transform._objectBuffer); + glDeleteBuffers(1, &_transform._cameraBuffer); } void GLBackend::syncTransformStateCache() { @@ -72,73 +82,98 @@ void GLBackend::syncTransformStateCache() { _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 - if (_transform._invalidViewport) { - _transform._transformCamera._viewport = glm::vec4(_transform._viewport); + if (_invalidViewport) { + _camera._viewport = glm::vec4(_viewport); } - if (_transform._invalidProj) { - _transform._transformCamera._projection = _transform._projection; - _transform._transformCamera._projectionInverse = glm::inverse(_transform._projection); + if (_invalidProj) { + _camera._projection = _projection; } - if (_transform._invalidView) { - _transform._view.getInverseMatrix(_transform._transformCamera._view); - _transform._view.getMatrix(_transform._transformCamera._viewInverse); + if (_invalidView) { + _view.getInverseMatrix(_camera._view); } - if (_transform._invalidModel) { - _transform._model.getMatrix(_transform._transformObject._model); - _transform._model.getInverseMatrix(_transform._transformObject._modelInverse); + if (_invalidModel) { + _model.getMatrix(_object._model); + _model.getInverseMatrix(_object._modelInverse); } - if (_transform._invalidView || _transform._invalidProj) { - Mat4 viewUntranslated = _transform._transformCamera._view; - viewUntranslated[3] = Vec4(0.0f, 0.0f, 0.0f, 1.0f); - _transform._transformCamera._projectionViewUntranslated = _transform._transformCamera._projection * viewUntranslated; + if (_invalidView || _invalidProj || _invalidViewport) { + size_t offset = _cameraUboSize * _cameras.size(); + if (stereo._enable) { + _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) { - _transform._cameraOffsets.push_back(TransformStageState::Pair(_commandIndex, _transform._cameraUboSize * _transform._cameraTransforms.size())); - _transform._cameraTransforms.push_back(_transform._transformCamera); - } - - if (_transform._invalidModel) { - _transform._objectOffsets.push_back(TransformStageState::Pair(_commandIndex, _transform._objectUboSize * _transform._objectTransforms.size())); - _transform._objectTransforms.push_back(_transform._transformObject); + if (_invalidModel) { + size_t offset = _objectUboSize * _objects.size(); + _objectOffsets.push_back(TransformStageState::Pair(commandIndex, offset)); + _objects.push_back(_object); } // 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; - while (!_transform._objectOffsets.empty() && _commandIndex >= _transform._objectOffsets.front().first) { - offset = _transform._objectOffsets.front().second; - _transform._objectOffsets.pop_front(); + while ((_objectsItr != _objectOffsets.end()) && (commandIndex >= (*_objectsItr).first)) { + offset = (*_objectsItr).second; + ++_objectsItr; } if (offset >= 0) { - glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_OBJECT_SLOT, - _transform._transformObjectBuffer, - offset, sizeof(Backend::TransformObject)); + glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_OBJECT_SLOT, + _objectBuffer, offset, sizeof(Backend::TransformObject)); } offset = -1; - while (!_transform._cameraOffsets.empty() && _commandIndex >= _transform._cameraOffsets.front().first) { - offset = _transform._cameraOffsets.front().second; - _transform._cameraOffsets.pop_front(); + while ((_camerasItr != _cameraOffsets.end()) && (commandIndex >= (*_camerasItr).first)) { + offset = (*_camerasItr).second; + ++_camerasItr; } if (offset >= 0) { - glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, - _transform._transformCameraBuffer, - offset, sizeof(Backend::TransformCamera)); + // We include both camera offsets for stereo + if (stereo._enable && stereo._pass) { + offset += _cameraUboSize; + } + glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, + _cameraBuffer, offset, sizeof(Backend::TransformCamera)); } (void)CHECK_GL_ERROR(); } +void GLBackend::updateTransform() const { + _transform.update(_commandIndex, _stereo); +} + void GLBackend::resetTransformStage() { } diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index fc53670fd0..e76983cce9 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -189,7 +189,6 @@ void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePoint renderHand(rightHand, batch, RIGHT_HAND); } - args->_context->syncCache(); args->_context->render(batch); } } diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 5f0afd37d1..ecf7c34dfe 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -246,6 +246,5 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons DependencyManager::get()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color); // Ready to render - args->_context->syncCache(); args->_context->render((batch)); } diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 6776fa6c79..a785a5d2ec 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -225,6 +225,7 @@ void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radiu void DeferredLightingEffect::prepare(RenderArgs* args) { gpu::Batch batch; + batch.enableStereo(false); batch.setStateScissorRect(args->_viewport); @@ -244,6 +245,9 @@ gpu::FramebufferPointer _copyFBO; void DeferredLightingEffect::render(RenderArgs* args) { gpu::Batch batch; + // Framebuffer copy operations cannot function as multipass stereo operations. + batch.enableStereo(false); + // perform deferred lighting, rendering to free fbo auto framebufferCache = DependencyManager::get(); @@ -555,6 +559,7 @@ void DeferredLightingEffect::render(RenderArgs* args) { void DeferredLightingEffect::copyBack(RenderArgs* args) { gpu::Batch batch; + batch.enableStereo(false); auto framebufferCache = DependencyManager::get(); QSize framebufferSize = framebufferCache->getFrameBufferSize(); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index efef423623..ca3f87f53f 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -35,6 +35,7 @@ void SetupDeferred::run(const SceneContextPointer& sceneContext, const RenderCon auto primaryFbo = DependencyManager::get()->getPrimaryFramebufferDepthColor(); gpu::Batch batch; + batch.enableStereo(false); batch.setFramebuffer(nullptr); batch.setFramebuffer(primaryFbo); @@ -159,6 +160,8 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend RenderArgs* args = renderContext->args; gpu::Batch batch; + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); args->_batch = &batch; renderContext->_numDrawnOpaqueItems = inItems.size(); @@ -188,6 +191,8 @@ void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const RenderArgs* args = renderContext->args; gpu::Batch batch; + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); args->_batch = &batch; renderContext->_numDrawnTransparentItems = inItems.size(); @@ -247,30 +252,42 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon renderContext->_numFeedOverlay3DItems = inItems.size(); renderContext->_numDrawnOverlay3DItems = inItems.size(); - RenderArgs* args = renderContext->args; - gpu::Batch batch; - args->_batch = &batch; - args->_whiteTexture = DependencyManager::get()->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()) { - batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, true); - renderItems(sceneContext, renderContext, inItems, renderContext->_maxDrawnOverlay3DItems); - } + RenderArgs* args = renderContext->args; - args->_context->render((*args->_batch)); - args->_batch = nullptr; - args->_whiteTexture.reset(); + // Clear the framebuffer without stereo + // Needs to be distinct from the other batch because using the clear call + // 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()->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(); + } + } } diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index 0c1b92b559..28b4344bf1 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -161,8 +161,5 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex batch.draw(gpu::TRIANGLES, 24, 0); } - // Before rendering the batch make sure we re in sync with gl state - args->_context->syncCache(); - renderContext->args->_context->syncCache(); - args->_context->render((batch)); + args->_context->render(batch); } diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index ff2cf6ff41..0754d81bde 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -258,6 +258,9 @@ void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderCo } RenderArgs* args = renderContext->args; gpu::Batch batch; + batch.enableSkybox(true); + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); args->_batch = &batch; glm::mat4 projMat; @@ -271,9 +274,6 @@ void DrawBackground::run(const SceneContextPointer& sceneContext, const RenderCo renderItems(sceneContext, renderContext, inItems); args->_context->render((*args->_batch)); args->_batch = nullptr; - - // Force the context sync - args->_context->syncCache(); } void ItemMaterialBucketMap::insert(const ItemID& id, const model::MaterialKey& key) { From 7fb491e48c2507db85391a2ed3d7cf282fa31b7f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 19 Aug 2015 19:09:21 -0700 Subject: [PATCH 14/42] Cleanup plugin interface, break up oculus plugins --- cmake/externals/LibOVR/CMakeLists.txt | 15 ++ interface/src/Application.cpp | 37 ++- .../Basic2DWindowOpenGLDisplayPlugin.cpp | 4 +- .../Basic2DWindowOpenGLDisplayPlugin.h | 4 +- .../src/display-plugins/DisplayPlugin.cpp | 12 +- .../src/display-plugins/DisplayPlugin.h | 15 +- .../MainWindowOpenGLDisplayPlugin.cpp | 9 - .../MainWindowOpenGLDisplayPlugin.h | 13 - .../oculus/OculusBaseDisplayPlugin.cpp | 82 ------ .../oculus/OculusBaseDisplayPlugin.h | 28 --- ...playPlugin.cpp => OculusDisplayPlugin.cpp} | 238 +++++++++++++----- .../oculus/OculusDisplayPlugin.h | 78 ++++++ .../display-plugins/oculus/OculusHelpers.h | 13 +- ...ugin.cpp => OculusLegacyDisplayPlugin.cpp} | 121 ++++++--- .../oculus/OculusLegacyDisplayPlugin.h | 63 +++++ .../oculus/Oculus_0_5_DisplayPlugin.h | 37 --- .../oculus/Oculus_0_6_DisplayPlugin.h | 41 --- .../openvr/OpenVrDisplayPlugin.cpp | 12 +- .../openvr/OpenVrDisplayPlugin.h | 5 +- .../stereo/StereoDisplayPlugin.cpp | 12 +- .../stereo/StereoDisplayPlugin.h | 7 +- 21 files changed, 465 insertions(+), 381 deletions(-) delete mode 100644 libraries/display-plugins/src/display-plugins/MainWindowOpenGLDisplayPlugin.cpp delete mode 100644 libraries/display-plugins/src/display-plugins/MainWindowOpenGLDisplayPlugin.h delete mode 100644 libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp delete mode 100644 libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h rename libraries/display-plugins/src/display-plugins/oculus/{Oculus_0_6_DisplayPlugin.cpp => OculusDisplayPlugin.cpp} (59%) create mode 100644 libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h rename libraries/display-plugins/src/display-plugins/oculus/{Oculus_0_5_DisplayPlugin.cpp => OculusLegacyDisplayPlugin.cpp} (55%) create mode 100644 libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h delete mode 100644 libraries/display-plugins/src/display-plugins/oculus/Oculus_0_5_DisplayPlugin.h delete mode 100644 libraries/display-plugins/src/display-plugins/oculus/Oculus_0_6_DisplayPlugin.h diff --git a/cmake/externals/LibOVR/CMakeLists.txt b/cmake/externals/LibOVR/CMakeLists.txt index b0bf34a594..e03a3af484 100644 --- a/cmake/externals/LibOVR/CMakeLists.txt +++ b/cmake/externals/LibOVR/CMakeLists.txt @@ -5,6 +5,21 @@ set(EXTERNAL_NAME LibOVR) 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) ExternalProject_Add( diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 68b2322394..374963e9b6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1058,18 +1058,29 @@ void Application::paintGL() { // Using the latter will cause the camera to wobble with idle animations, // or with changes from the face tracker renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; - _myCamera.setPosition(_myAvatar->getDefaultEyePosition()); - _myCamera.setRotation(_myAvatar->getOrientation()); - } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { - _myCamera.setRotation(_myAvatar->getOrientation()); - // https://www.youtube.com/watch?v=pFriRcIwqNU - vec3 boomStick = glm::vec3(0.0f, 0.0f, 1.0f) * _myAvatar->getBoomLength() * _myAvatar->getScale(); - quat boomRotation = _myAvatar->getOrientation(); - if (!isHMDMode() && Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { - boomRotation = _myCamera.getRotation(); - } - _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + boomRotation * boomStick); + if (!getActiveDisplayPlugin()->isHmd()) { + _myCamera.setPosition(_myAvatar->getDefaultEyePosition()); + _myCamera.setRotation(_myAvatar->getHead()->getCameraOrientation()); + } else { + mat4 camMat = _myAvatar->getSensorToWorldMatrix() * _myAvatar->getHMDSensorMatrix(); + _myCamera.setPosition(extractTranslation(camMat)); + _myCamera.setRotation(glm::quat_cast(camMat)); + } + } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { + if (isHMDMode()) { + _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation()); + } else { + _myCamera.setRotation(_myAvatar->getHead()->getOrientation()); + } + if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { + _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + + _myCamera.getRotation() * glm::vec3(0.0f, 0.0f, 1.0f) * _myAvatar->getBoomLength() * _myAvatar->getScale()); + } else { + _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + + _myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, 1.0f) * _myAvatar->getBoomLength() * _myAvatar->getScale()); + } + } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { _myCamera.setRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); _myCamera.setPosition(_myAvatar->getDefaultEyePosition() + @@ -1115,7 +1126,7 @@ void Application::paintGL() { // FIXME we don't need to set these every frame, // only when the display plugin changes for_each_eye([&](Eye eye) { - eyeViews[eye] = displayPlugin->getModelview(eye, mat4()); + eyeViews[eye] = displayPlugin->getView(eye, mat4()); eyeProjections[eye] = displayPlugin->getProjection(eye, baseProjection); }); renderArgs._context->setStereoProjections(eyeProjections); @@ -4952,7 +4963,7 @@ mat4 Application::getEyePose(int eye) const { mat4 Application::getEyeOffset(int eye) const { if (isHMDMode()) { mat4 identity; - return getActiveDisplayPlugin()->getModelview((Eye)eye, identity); + return getActiveDisplayPlugin()->getView((Eye)eye, identity); } return mat4(); diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index 7667fa1a29..14ba868275 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -30,11 +30,11 @@ void Basic2DWindowOpenGLDisplayPlugin::activate() { CONTAINER->unsetFullscreen(); } }, true, false); - MainWindowOpenGLDisplayPlugin::activate(); + WindowOpenGLDisplayPlugin::activate(); } void Basic2DWindowOpenGLDisplayPlugin::deactivate() { - MainWindowOpenGLDisplayPlugin::deactivate(); + WindowOpenGLDisplayPlugin::deactivate(); } int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval(bool isThrottled) const { diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h index 477e214f4e..64edfe3600 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h @@ -7,10 +7,10 @@ // #pragma once -#include "MainWindowOpenGLDisplayPlugin.h" +#include "WindowOpenGLDisplayPlugin.h" class QScreen; -class Basic2DWindowOpenGLDisplayPlugin : public MainWindowOpenGLDisplayPlugin { +class Basic2DWindowOpenGLDisplayPlugin : public WindowOpenGLDisplayPlugin { Q_OBJECT public: diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp index 8bfe8c20da..cd620d85a4 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp @@ -15,8 +15,8 @@ #include "Basic2DWindowOpenGLDisplayPlugin.h" #include "openvr/OpenVrDisplayPlugin.h" -#include "oculus/Oculus_0_5_DisplayPlugin.h" -#include "oculus/Oculus_0_6_DisplayPlugin.h" +#include "oculus/OculusDisplayPlugin.h" +#include "oculus/OculusLegacyDisplayPlugin.h" // TODO migrate to a DLL model where plugins are discovered and loaded at runtime by the PluginManager class DisplayPluginList getDisplayPlugins() { @@ -32,9 +32,13 @@ DisplayPluginList getDisplayPlugins() { //new InterleavedStereoDisplayPlugin(), // 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 + // SteamVR SDK new OpenVrDisplayPlugin(), #endif nullptr diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h index da3bc98135..1a4166c0fa 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h @@ -97,20 +97,16 @@ public: return baseProjection; } - virtual glm::mat4 getModelview(Eye eye, const glm::mat4& baseModelview) const { - return glm::inverse(getEyePose(eye)) * baseModelview; + virtual glm::mat4 getView(Eye eye, const glm::mat4& baseView) const { + return glm::inverse(getEyePose(eye)) * baseView; } // HMD specific methods - // TODO move these into another class + // TODO move these into another class? virtual glm::mat4 getEyePose(Eye eye) const { static const glm::mat4 pose; return pose; } - virtual glm::vec3 getEyeOffset(Eye eye) const { - static const glm::vec3 offset; return offset; - } - virtual glm::mat4 getHeadPose() const { static const glm::mat4 pose; return pose; } @@ -119,11 +115,6 @@ public: virtual void resetSensors() {} virtual float devicePixelRatio() { return 1.0; } - //// The window for the surface, used for event interception. May be null. - //virtual QWindow* getWindow() const = 0; - - //virtual void installEventFilter(QObject* filter) {} - //virtual void removeEventFilter(QObject* filter) {} signals: void recommendedFramebufferSizeChanged(const QSize & size); diff --git a/libraries/display-plugins/src/display-plugins/MainWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/MainWindowOpenGLDisplayPlugin.cpp deleted file mode 100644 index 68fe92c943..0000000000 --- a/libraries/display-plugins/src/display-plugins/MainWindowOpenGLDisplayPlugin.cpp +++ /dev/null @@ -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" diff --git a/libraries/display-plugins/src/display-plugins/MainWindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/MainWindowOpenGLDisplayPlugin.h deleted file mode 100644 index 5b28ec7c21..0000000000 --- a/libraries/display-plugins/src/display-plugins/MainWindowOpenGLDisplayPlugin.h +++ /dev/null @@ -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 { -}; diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp deleted file mode 100644 index 0490e82106..0000000000 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp +++ /dev/null @@ -1,82 +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 - -#include "OculusHelpers.h" - - -using namespace Oculus; - -OculusBaseDisplayPlugin::OculusBaseDisplayPlugin() : _ipd(OVR_DEFAULT_IPD) { -} - -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); - - // We handle the eye offsets slightly differently, using an _ipd in the base class - // _eyeOffsets[eye] = erd.HmdToEyeViewOffset; - _eyeOffsets[eye] = { 0, 0, 0 }; - eyeSizes[eye] = toGlm(ovrHmd_GetFovTextureSize(_hmd, eye, erd.Fov, 1.0f)); - }); - _ipd = ovrHmd_GetFloat(_hmd, OVR_KEY_IPD, _ipd); - _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); -} diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h deleted file mode 100644 index 401ee6579a..0000000000 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h +++ /dev/null @@ -1,28 +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: - OculusBaseDisplayPlugin(); - // 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; -protected: - float _ipd; -}; diff --git a/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_6_DisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp similarity index 59% rename from libraries/display-plugins/src/display-plugins/oculus/Oculus_0_6_DisplayPlugin.cpp rename to libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp index 6ed8977f47..84d67f038a 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_6_DisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp @@ -5,7 +5,7 @@ // Distributed under the Apache License, Version 2.0. // 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 @@ -15,9 +15,7 @@ #include #include #include - -#include - +#include #include #include @@ -27,18 +25,34 @@ #include #include +#include #include "OculusHelpers.h" -using namespace Oculus; -#if (OVR_MAJOR_VERSION == 6) -SwapFboPtr _sceneFbo; -MirrorFboPtr _mirrorFbo; -ovrLayerEyeFov _sceneLayer; +#if (OVR_MAJOR_VERSION == 6) +#define ovr_Create ovrHmd_Create +#define ovr_CreateSwapTextureSetGL ovrHmd_CreateSwapTextureSetGL +#define ovr_CreateMirrorTextureGL ovrHmd_CreateMirrorTextureGL +#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 -// API to manage textures via ovrHmd_CreateSwapTextureSetGL, -// ovrHmd_CreateMirrorTextureGL, etc +// API to manage textures via ovr_CreateSwapTextureSetGL, +// ovr_CreateMirrorTextureGL, etc template struct RiftFramebufferWrapper : public FramebufferWrapper { ovrHmd hmd; @@ -73,7 +87,7 @@ struct SwapFramebufferWrapper : public RiftFramebufferWrapper { virtual ~MirrorFramebufferWrapper() { if (color) { - ovrHmd_DestroyMirrorTexture(hmd, (ovrTexture*)color); + ovr_DestroyMirrorTexture(hmd, (ovrTexture*)color); color = nullptr; } } @@ -135,10 +149,10 @@ struct MirrorFramebufferWrapper : public RiftFramebufferWrapper { private: void initColor() override { if (color) { - ovrHmd_DestroyMirrorTexture(hmd, (ovrTexture*)color); + ovr_DestroyMirrorTexture(hmd, (ovrTexture*)color); 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)); } @@ -149,52 +163,128 @@ private: } }; + #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; } -bool Oculus_0_6_DisplayPlugin::isSupported() const { -#if (OVR_MAJOR_VERSION == 6) - if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { - return false; - } - bool result = false; - if (ovrHmd_Detect() > 0) { - result = true; - } - ovr_Shutdown(); - return result; +bool OculusDisplayPlugin::isSupported() const { +#if (OVR_MAJOR_VERSION >= 6) + return true; #else return false; #endif } +void OculusDisplayPlugin::init() { + if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { + qFatal("Could not init OVR"); + } +} -#if (OVR_MAJOR_VERSION == 6) -ovrLayerEyeFov& getSceneLayer() { +void OculusDisplayPlugin::deinit() { + ovr_Shutdown(); +} + +#if (OVR_MAJOR_VERSION >= 6) +ovrLayerEyeFov& OculusDisplayPlugin::getSceneLayer() { return _sceneLayer; } #endif //static gpu::TexturePointer _texture; -void Oculus_0_6_DisplayPlugin::activate() { -#if (OVR_MAJOR_VERSION == 6) +void OculusDisplayPlugin::activate() { +#if (OVR_MAJOR_VERSION >= 6) if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { Q_ASSERT(false); 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); 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. ovrLayerEyeFov& sceneLayer = getSceneLayer(); @@ -203,7 +293,7 @@ void Oculus_0_6_DisplayPlugin::activate() { sceneLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft; ovr_for_each_eye([&](ovrEyeType eye) { 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 }; }); // We're rendering both eyes to the same texture, so only one of the @@ -214,17 +304,16 @@ void Oculus_0_6_DisplayPlugin::activate() { PerformanceTimer::setActive(true); - if (!OVR_SUCCESS(ovrHmd_ConfigureTracking(_hmd, + if (!OVR_SUCCESS(ovr_ConfigureTracking(_hmd, ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) { qFatal("Could not attach to sensor device"); } #endif } -void Oculus_0_6_DisplayPlugin::customizeContext() { -#if (OVR_MAJOR_VERSION == 6) - OculusBaseDisplayPlugin::customizeContext(); - +void OculusDisplayPlugin::customizeContext() { + WindowOpenGLDisplayPlugin::customizeContext(); +#if (OVR_MAJOR_VERSION >= 6) //_texture = DependencyManager::get()-> // getImageTexture(PathUtils::resourcesPath() + "/images/cube_texture.png"); uvec2 mirrorSize = toGlm(_window->geometry().size()); @@ -236,24 +325,29 @@ void Oculus_0_6_DisplayPlugin::customizeContext() { #endif } -void Oculus_0_6_DisplayPlugin::deactivate() { -#if (OVR_MAJOR_VERSION == 6) +void OculusDisplayPlugin::deactivate() { +#if (OVR_MAJOR_VERSION >= 6) makeCurrent(); _sceneFbo.reset(); _mirrorFbo.reset(); doneCurrent(); PerformanceTimer::setActive(false); - OculusBaseDisplayPlugin::deactivate(); + WindowOpenGLDisplayPlugin::deactivate(); - ovrHmd_Destroy(_hmd); + ovr_Destroy(_hmd); _hmd = nullptr; ovr_Shutdown(); #endif } -void Oculus_0_6_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { -#if (OVR_MAJOR_VERSION == 6) +void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { + static bool inDisplay = false; + if (inDisplay) { + return; + } + inDisplay = true; +#if (OVR_MAJOR_VERSION >= 6) using namespace oglplus; // Need to make sure only the display plugin is responsible for // controlling vsync @@ -263,6 +357,7 @@ void Oculus_0_6_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sc auto size = _sceneFbo->size; Context::Viewport(size.x, size.y); glBindTexture(GL_TEXTURE_2D, finalTexture); + GLenum err = glGetError(); drawUnitQuad(); }); @@ -280,17 +375,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 _sceneFbo->Increment or we're be using the wrong texture */ - //_sceneFbo->Bound(GL_READ_FRAMEBUFFER, [&] { - // glBlitFramebuffer( - // 0, 0, _sceneFbo->size.x, _sceneFbo->size.y, - // 0, 0, windowSize.x, _mirrorFbo.y, - // GL_COLOR_BUFFER_BIT, GL_NEAREST); - //}); + _sceneFbo->Bound(Framebuffer::Target::Read, [&] { + glBlitFramebuffer( + 0, 0, _sceneFbo->size.x, _sceneFbo->size.y, + 0, 0, windowSize.x, windowSize.y, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + }); { PerformanceTimer("OculusSubmit"); + ovrViewScaleDesc viewScaleDesc; + viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f; + viewScaleDesc.HmdToEyeViewOffset[0] = _eyeOffsets[0]; + viewScaleDesc.HmdToEyeViewOffset[1] = _eyeOffsets[1]; + 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(); @@ -299,21 +402,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 we send. */ - auto mirrorSize = _mirrorFbo->size; - _mirrorFbo->Bound(Framebuffer::Target::Read, [&] { - Context::BlitFramebuffer( - 0, mirrorSize.y, mirrorSize.x, 0, - 0, 0, windowSize.x, windowSize.y, - BufferSelectBit::ColorBuffer, BlitFilter::Nearest); - }); + //auto mirrorSize = _mirrorFbo->size; + //_mirrorFbo->Bound(Framebuffer::Target::Read, [&] { + // Context::BlitFramebuffer( + // 0, mirrorSize.y, mirrorSize.x, 0, + // 0, 0, windowSize.x, windowSize.y, + // BufferSelectBit::ColorBuffer, BlitFilter::Nearest); + //}); ++_frameIndex; #endif + inDisplay = false; } // Pass input events on to the application -bool Oculus_0_6_DisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { -#if (OVR_MAJOR_VERSION == 6) +bool OculusDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { +#if (OVR_MAJOR_VERSION >= 6) if (event->type() == QEvent::Resize) { QResizeEvent* resizeEvent = static_cast(event); qDebug() << resizeEvent->size().width() << " x " << resizeEvent->size().height(); @@ -323,7 +427,7 @@ bool Oculus_0_6_DisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { doneCurrent(); } #endif - return OculusBaseDisplayPlugin::eventFilter(receiver, event); + return WindowOpenGLDisplayPlugin::eventFilter(receiver, event); } /* @@ -331,8 +435,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, otherwise the swapbuffer delay will interefere with the framerate of the headset */ -void Oculus_0_6_DisplayPlugin::finishFrame() { - swapBuffers(); +void OculusDisplayPlugin::finishFrame() { + //swapBuffers(); doneCurrent(); }; diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h new file mode 100644 index 0000000000..75173fd2bd --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h @@ -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 + +#include + +class OffscreenGlCanvas; +struct SwapFramebufferWrapper; +struct MirrorFramebufferWrapper; + +using SwapFboPtr = QSharedPointer; +using MirrorFboPtr = QSharedPointer; + +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 +}; + diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusHelpers.h b/libraries/display-plugins/src/display-plugins/oculus/OculusHelpers.h index 31b7c246af..df0a6c5228 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusHelpers.h +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusHelpers.h @@ -7,7 +7,7 @@ // #pragma once -#include +#include #include #include #include @@ -79,14 +79,3 @@ inline ovrQuatf ovrFromGlm(const glm::quat & q) { 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; -} diff --git a/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_5_DisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp similarity index 55% rename from libraries/display-plugins/src/display-plugins/oculus/Oculus_0_5_DisplayPlugin.cpp rename to libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp index 86ee3b41f2..2c5af1a52b 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_5_DisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp @@ -5,7 +5,7 @@ // Distributed under the Apache License, Version 2.0. // 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 @@ -19,34 +19,66 @@ #include #include - -#include - #include #include +#include #include "plugins/PluginContainer.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; -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; } +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_Initialize(nullptr)) { return false; @@ -77,7 +109,7 @@ bool Oculus_0_5_DisplayPlugin::isSupported() const { #endif } -void Oculus_0_5_DisplayPlugin::activate() { +void OculusLegacyDisplayPlugin::activate() { #if (OVR_MAJOR_VERSION == 5) if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { Q_ASSERT(false); @@ -89,7 +121,34 @@ void Oculus_0_5_DisplayPlugin::activate() { 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)); + }); + _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(); if (screen != -1) { CONTAINER->setFullscreen(qApp->screens()[screen]); @@ -118,17 +177,16 @@ void Oculus_0_5_DisplayPlugin::activate() { } }); - ovrEyeRenderDesc _eyeRenderDescs[ovrEye_Count]; ovrBool result = ovrHmd_ConfigureRendering(_hmd, &config.Config, distortionCaps, _eyeFovs, _eyeRenderDescs); Q_ASSERT(result); #endif } -void Oculus_0_5_DisplayPlugin::deactivate() { +void OculusLegacyDisplayPlugin::deactivate() { #if (OVR_MAJOR_VERSION == 5) _window->removeEventFilter(this); - OculusBaseDisplayPlugin::deactivate(); + WindowOpenGLDisplayPlugin::deactivate(); QScreen* riftScreen = nullptr; if (_hmdScreen >= 0) { @@ -142,18 +200,11 @@ void Oculus_0_5_DisplayPlugin::deactivate() { #endif } -void Oculus_0_5_DisplayPlugin::preRender() { -#if (OVR_MAJOR_VERSION == 5) - OculusBaseDisplayPlugin::preRender(); - ovrHmd_BeginFrame(_hmd, _frameIndex); -#endif -} - -void Oculus_0_5_DisplayPlugin::preDisplay() { +void OculusLegacyDisplayPlugin::preDisplay() { _window->makeCurrent(); } -void Oculus_0_5_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { +void OculusLegacyDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { ++_frameIndex; #if (OVR_MAJOR_VERSION == 5) ovr_for_each_eye([&](ovrEyeType eye) { @@ -164,7 +215,7 @@ void Oculus_0_5_DisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sc } // 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 (!_hswDismissed && (event->type() == QEvent::KeyPress)) { static ovrHSWDisplayState hswState; @@ -176,17 +227,19 @@ bool Oculus_0_5_DisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { } } #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 // 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 // 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(); }; -int Oculus_0_5_DisplayPlugin::getHmdScreen() const { +int OculusLegacyDisplayPlugin::getHmdScreen() const { return _hmdScreen; } diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h new file mode 100644 index 0000000000..ce91289cb0 --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h @@ -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 + +#include + +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[2]; + mat4 _compositeEyeProjections[2]; + uvec2 _desiredFramebufferSize; + ovrTexture _eyeTextures[2]; + mutable int _hmdScreen{ -1 }; + bool _hswDismissed{ false }; +}; + + diff --git a/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_5_DisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_5_DisplayPlugin.h deleted file mode 100644 index b539d07fb0..0000000000 --- a/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_5_DisplayPlugin.h +++ /dev/null @@ -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 - -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; -}; - - diff --git a/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_6_DisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_6_DisplayPlugin.h deleted file mode 100644 index 0fde5e76b3..0000000000 --- a/libraries/display-plugins/src/display-plugins/oculus/Oculus_0_6_DisplayPlugin.h +++ /dev/null @@ -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 - -class OffscreenGlCanvas; -struct SwapFramebufferWrapper; -struct MirrorFramebufferWrapper; - -using SwapFboPtr = QSharedPointer; -using MirrorFboPtr = QSharedPointer; - -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; -}; - diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp index 181546d428..1a5aa2f437 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp @@ -128,7 +128,7 @@ void OpenVrDisplayPlugin::activate() { delete[] buffer; } Q_ASSERT(unSize <= 1); - MainWindowOpenGLDisplayPlugin::activate(); + WindowOpenGLDisplayPlugin::activate(); } void OpenVrDisplayPlugin::deactivate() { @@ -141,7 +141,7 @@ void OpenVrDisplayPlugin::deactivate() { _hmd = nullptr; } _compositor = nullptr; - MainWindowOpenGLDisplayPlugin::deactivate(); + WindowOpenGLDisplayPlugin::deactivate(); } uvec2 OpenVrDisplayPlugin::getRecommendedRenderSize() const { @@ -152,16 +152,12 @@ mat4 OpenVrDisplayPlugin::getProjection(Eye eye, const mat4& baseProjection) con return _eyesData[eye]._projectionMatrix; } -glm::mat4 OpenVrDisplayPlugin::getModelview(Eye eye, const mat4& baseModelview) const { - return baseModelview * getEyePose(eye); -} - void OpenVrDisplayPlugin::resetSensors() { _sensorResetMat = glm::inverse(cancelOutRollAndPitch(_trackedDevicePoseMat4[0])); } glm::mat4 OpenVrDisplayPlugin::getEyePose(Eye eye) const { - return getHeadPose() * _eyesData[eye]._eyeOffset; + return _eyesData[eye]._eyeOffset * getHeadPose(); } glm::mat4 OpenVrDisplayPlugin::getHeadPose() const { @@ -169,7 +165,7 @@ glm::mat4 OpenVrDisplayPlugin::getHeadPose() const { } void OpenVrDisplayPlugin::customizeContext() { - MainWindowOpenGLDisplayPlugin::customizeContext(); + WindowOpenGLDisplayPlugin::customizeContext(); } void OpenVrDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h index 608a869341..afe024e72b 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h @@ -11,9 +11,9 @@ #if defined(Q_OS_WIN) -#include "../MainWindowOpenGLDisplayPlugin.h" +#include "../WindowOpenGLDisplayPlugin.h" -class OpenVrDisplayPlugin : public MainWindowOpenGLDisplayPlugin { +class OpenVrDisplayPlugin : public WindowOpenGLDisplayPlugin { public: virtual bool isSupported() const override; virtual const QString & getName() const override; @@ -27,7 +27,6 @@ public: // Stereo specific methods 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 glm::mat4 getEyePose(Eye eye) const override; diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index 3d5f73bfa8..104d74097c 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -42,12 +42,12 @@ glm::mat4 StereoDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProje 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; if (eye == Left) { modelviewShift = -modelviewShift; } - return baseModelview * glm::translate(mat4(), vec3(modelviewShift, 0, 0)); + return glm::translate(mat4(), vec3(modelviewShift, 0, 0)); } void StereoDisplayPlugin::activate() { @@ -57,11 +57,3 @@ void StereoDisplayPlugin::activate() { //CONTAINER->setFullscreen(qApp->primaryScreen()); // FIXME Add menu items } - -glm::vec3 StereoDisplayPlugin::getEyeOffset(Eye eye) const { - glm::vec3 result(_ipd / 2.0f, 0, 0); - if (eye == Eye::Right) { - result *= -1.0f; - } - return result; -} diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h index 77b1141aed..b0f0414de2 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h @@ -7,9 +7,9 @@ // #pragma once -#include "../MainWindowOpenGLDisplayPlugin.h" +#include "../WindowOpenGLDisplayPlugin.h" -class StereoDisplayPlugin : public MainWindowOpenGLDisplayPlugin { +class StereoDisplayPlugin : public WindowOpenGLDisplayPlugin { Q_OBJECT public: StereoDisplayPlugin(); @@ -19,8 +19,7 @@ public: virtual void activate() 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::vec3 getEyeOffset(Eye eye) const override; + virtual glm::mat4 getEyePose(Eye eye) const override; protected: float _ipd{ 0.064f }; From 62e8ec3fdfc47dea34dd9adb62c0307570d6a591 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 20 Aug 2015 15:09:52 -0700 Subject: [PATCH 15/42] add fullscreen menu item and rename side by side stereo plugin --- .../Basic2DWindowOpenGLDisplayPlugin.cpp | 4 ++-- .../stereo/SideBySideStereoDisplayPlugin.cpp | 24 ++++++++++++++++++- .../stereo/SideBySideStereoDisplayPlugin.h | 6 ++++- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index 14ba868275..018a09ff7e 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -13,8 +13,8 @@ const QString Basic2DWindowOpenGLDisplayPlugin::NAME("2D Display"); -const QString MENU_PATH = "Display"; -const QString FULLSCREEN = "Fullscreen"; +static const QString MENU_PATH = "Display"; +static const QString FULLSCREEN = "Fullscreen"; const QString& Basic2DWindowOpenGLDisplayPlugin::getName() const { return NAME; diff --git a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp index 72ef5249a8..829385b209 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp @@ -10,14 +10,19 @@ #include #include +#include #include #include #include #include +#include -const QString SideBySideStereoDisplayPlugin::NAME("Debug Stereo Display"); +const QString SideBySideStereoDisplayPlugin::NAME("3D TV - Side by Side Stereo"); + +static const QString MENU_PATH = "Display"; +static const QString FULLSCREEN = "Fullscreen"; const QString & SideBySideStereoDisplayPlugin::getName() const { return NAME; @@ -26,3 +31,20 @@ const QString & SideBySideStereoDisplayPlugin::getName() const { SideBySideStereoDisplayPlugin::SideBySideStereoDisplayPlugin() { } +void SideBySideStereoDisplayPlugin::activate() { + CONTAINER->addMenu(MENU_PATH); + CONTAINER->addMenuItem(MENU_PATH, FULLSCREEN, + [this](bool clicked) { + if (clicked) { + CONTAINER->setFullscreen(getFullscreenTarget()); + } else { + CONTAINER->unsetFullscreen(); + } + }, true, false); + StereoDisplayPlugin::activate(); +} + +// FIXME target the screen the window is currently on +QScreen* SideBySideStereoDisplayPlugin::getFullscreenTarget() { + return qApp->primaryScreen(); +} diff --git a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h index ead9ea7dc4..3a764d9f4e 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h @@ -9,11 +9,15 @@ #include "StereoDisplayPlugin.h" +class QScreen; + class SideBySideStereoDisplayPlugin : public StereoDisplayPlugin { Q_OBJECT public: SideBySideStereoDisplayPlugin(); - virtual const QString & getName() const override; + virtual const QString& getName() const override; + virtual void activate() override; private: + QScreen* getFullscreenTarget(); static const QString NAME; }; From 476e5edb3210bb76d43e9498526f468317a9459f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 20 Aug 2015 15:47:31 -0700 Subject: [PATCH 16/42] Fixing HMD wobble --- interface/src/Application.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 374963e9b6..c3773c3b73 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1123,10 +1123,18 @@ void Application::paintGL() { mat4 eyeViews[2]; mat4 eyeProjections[2]; auto baseProjection = renderArgs._viewFrustum->getProjection(); - // FIXME we don't need to set these every frame, - // only when the display plugin changes + // FIXME we probably don't need to set the projection matrix every frame, + // only when the display plugin changes (or in non-HMD modes when the user + // changes the FOV manually, which right now I don't think they can. for_each_eye([&](Eye eye) { - eyeViews[eye] = displayPlugin->getView(eye, mat4()); + // 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 + // 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); }); renderArgs._context->setStereoProjections(eyeProjections); From cc037c8828f2a6b84e0bf90e0e24ca280d2ef5a6 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 20 Aug 2015 16:20:34 -0700 Subject: [PATCH 17/42] Fix culling calculations in HMD with new stereo mechanism --- interface/src/Application.cpp | 11 ++++++++++- .../oculus/OculusLegacyDisplayPlugin.cpp | 7 +++++++ .../oculus/OculusLegacyDisplayPlugin.h | 2 +- .../display-plugins/openvr/OpenVrDisplayPlugin.cpp | 4 ++++ .../display-plugins/stereo/StereoDisplayPlugin.cpp | 2 ++ 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c3773c3b73..0c3bbd8d34 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1118,7 +1118,16 @@ void Application::paintGL() { renderArgs._viewport = gpu::Vec4i(0, 0, size.width(), size.height()); if (displayPlugin->isStereo()) { - //_myCamera.setProjection(displayPlugin->getProjection(Mono, _myCamera.getProjection())); + // Stereo modes will typically have a larger projection matrix overall, + // so we ask for the 'mono' projection matrix, which for stereo and HMD + // plugins will imply the combined projection for both eyes. + // + // This is properly implemented for the Oculus plugins, but for OpenVR + // and Stereo displays I'm not sure how to get / calculate it, so we're + // just relying on the left FOV in each case and hoping that the + // overall culling margin of error doesn't cause popping in the + // right eye. There are FIXMEs in the relevant plugins + _myCamera.setProjection(displayPlugin->getProjection(Mono, _myCamera.getProjection())); renderArgs._context->enableStereo(true); mat4 eyeViews[2]; mat4 eyeProjections[2]; diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp index 2c5af1a52b..e823f456ce 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp @@ -136,6 +136,13 @@ void OculusLegacyDisplayPlugin::activate() { _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)); diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h index ce91289cb0..219b6c54b3 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h @@ -52,7 +52,7 @@ private: ovrPosef _eyePoses[2]; ovrVector3f _eyeOffsets[2]; ovrFovPort _eyeFovs[2]; - mat4 _eyeProjections[2]; + mat4 _eyeProjections[3]; mat4 _compositeEyeProjections[2]; uvec2 _desiredFramebufferSize; ovrTexture _eyeTextures[2]; diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp index 1a5aa2f437..1e3e7699f4 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp @@ -149,6 +149,10 @@ uvec2 OpenVrDisplayPlugin::getRecommendedRenderSize() 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; } diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index 104d74097c..20613d6a2c 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -29,6 +29,8 @@ const float DEFAULT_IPD = 0.064f; const float HALF_DEFAULT_IPD = DEFAULT_IPD / 2.0f; glm::mat4 StereoDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const { + // FIXME check for mono eye and provide a combined matrix, needed for proper + // culling // Refer to http://www.nvidia.com/content/gtc-2010/pdfs/2010_gtc2010.pdf on creating // stereo projection matrices. Do NOT use "toe-in", use translation. From 75aff2f281053e3bc0e30a5ee6fc6090739cdf31 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 20 Aug 2015 16:39:23 -0700 Subject: [PATCH 18/42] fix atmosphere rendering --- libraries/render-utils/src/Environment.cpp | 24 +++++++++++++++------- libraries/render-utils/src/Environment.h | 4 ++-- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/libraries/render-utils/src/Environment.cpp b/libraries/render-utils/src/Environment.cpp index 9cce508946..605f67f957 100644 --- a/libraries/render-utils/src/Environment.cpp +++ b/libraries/render-utils/src/Environment.cpp @@ -92,17 +92,17 @@ void Environment::resetToDefault() { _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 QMutexLocker locker(&_mutex); if (_environmentIsOverridden) { - renderAtmosphere(batch, camera, _overrideData); + renderAtmosphere(batch, viewFrustum, _overrideData); } else { foreach (const ServerData& serverData, _data) { // TODO: do something about EnvironmentData 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; } -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(); + // transform the model transform to the center of our atmosphere Transform transform; transform.setTranslation(center); 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); // 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()) { batch.setPipeline(_skyFromAtmosphereProgram); locations = _skyFromAtmosphereUniformLocations; - } else { batch.setPipeline(_skyFromSpaceProgram); locations = _skyFromSpaceUniformLocations; } + // the constants here are from Sean O'Neil's GPU Gems entry // (http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter16.html), GameEngine.cpp diff --git a/libraries/render-utils/src/Environment.h b/libraries/render-utils/src/Environment.h index 5b10f2f1a9..af5a3c3df5 100644 --- a/libraries/render-utils/src/Environment.h +++ b/libraries/render-utils/src/Environment.h @@ -29,7 +29,7 @@ public: void init(); 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 endOverride() { _environmentIsOverridden = false; } @@ -41,7 +41,7 @@ private: bool findCapsulePenetration(const glm::vec3& start, 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; From 1e074ab81c20cd2ef3bf60cb271988a238a9e6aa Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 21 Aug 2015 00:28:43 -0700 Subject: [PATCH 19/42] Remove superfluous clear --- interface/src/Application.cpp | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0c3bbd8d34..301362aca0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1097,26 +1097,11 @@ void Application::paintGL() { // Primary rendering pass auto framebufferCache = DependencyManager::get(); - QSize size = framebufferCache->getFrameBufferSize(); + const QSize size = framebufferCache->getFrameBufferSize(); { PROFILE_RANGE(__FUNCTION__ "/mainRender"); // Viewport is assigned to the size of the framebuffer - QSize size = DependencyManager::get()->getFrameBufferSize(); - renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height()); - - doInBatch(&renderArgs, [&](gpu::Batch& batch) { - auto primaryFbo = DependencyManager::get()->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); - }); - - renderArgs._viewport = gpu::Vec4i(0, 0, size.width(), size.height()); + renderArgs._viewport = ivec4(0, 0, size.width(), size.height()); if (displayPlugin->isStereo()) { // Stereo modes will typically have a larger projection matrix overall, // so we ask for the 'mono' projection matrix, which for stereo and HMD @@ -1185,7 +1170,6 @@ void Application::paintGL() { PROFILE_RANGE(__FUNCTION__ "/pluginOutput"); auto primaryFbo = framebufferCache->getPrimaryFramebuffer(); GLuint finalTexture = gpu::GLBackend::getTextureID(primaryFbo->getRenderBuffer(0)); - uvec2 finalSize = toGlm(size); // Ensure the rendering context commands are completed when rendering GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); // Ensure the sync object is flushed to the driver thread before releasing the context @@ -1201,7 +1185,7 @@ void Application::paintGL() { { PROFILE_RANGE(__FUNCTION__ "/pluginDisplay"); - displayPlugin->display(finalTexture, finalSize); + displayPlugin->display(finalTexture, toGlm(size)); } { From 270463fc4e5ac72648869ec45cca793421d02d91 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 21 Aug 2015 09:29:25 -0700 Subject: [PATCH 20/42] restore new oculus detect code from master --- .../src/display-plugins/oculus/OculusDisplayPlugin.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp index 84d67f038a..ff218987ec 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp @@ -204,7 +204,15 @@ const QString & OculusDisplayPlugin::getName() const { bool OculusDisplayPlugin::isSupported() const { #if (OVR_MAJOR_VERSION >= 6) - return true; + if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { + return false; + } + bool result = false; + if (ovrHmd_Detect() > 0) { + result = true; + } + ovr_Shutdown(); + return result; #else return false; #endif From b8b0917d9db1c38d58ae4a5be6829f9c1755ae49 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 21 Aug 2015 10:01:11 -0700 Subject: [PATCH 21/42] Attempting to fix judder issues --- interface/src/Application.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 301362aca0..d3b1ce2318 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -993,6 +993,8 @@ void Application::paintGL() { auto displayPlugin = getActiveDisplayPlugin(); displayPlugin->preRender(); _offscreenContext->makeCurrent(); + // update the avatar with a fresh HMD pose + _myAvatar->updateFromHMDSensorMatrix(getHMDSensorPose()); auto lodManager = DependencyManager::get(); @@ -2683,9 +2685,6 @@ void Application::update(float deltaTime) { updateLOD(); 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"); DeviceTracker::updateAll(); From 55fd847636cbc99a438ecaef7cb5b0d4ee69ed0e Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 21 Aug 2015 19:07:33 +0200 Subject: [PATCH 22/42] removed unused code, renamed UnitTest.ready() to UnitTest.done() --- examples/libraries/unitTest.js | 2 +- examples/utilities/diagnostics/testWebSocket.js | 6 +++--- libraries/script-engine/src/WebSocketClass.cpp | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/examples/libraries/unitTest.js b/examples/libraries/unitTest.js index d65cd780bd..7d5234933f 100644 --- a/examples/libraries/unitTest.js +++ b/examples/libraries/unitTest.js @@ -173,7 +173,7 @@ UnitTest.prototype.raises = function(func, message) { throw new UnthrownException(message); } -UnitTest.prototype.ready = function() { +UnitTest.prototype.done = function() { if (this.timeout !== undefined) { Script.clearTimeout(this.timeoutTimer); this.successCallback(this); diff --git a/examples/utilities/diagnostics/testWebSocket.js b/examples/utilities/diagnostics/testWebSocket.js index 03b9b6b405..af915c3790 100644 --- a/examples/utilities/diagnostics/testWebSocket.js +++ b/examples/utilities/diagnostics/testWebSocket.js @@ -35,7 +35,7 @@ unitTests.addTest("Test default WebSocket values", function(finished) { }); webSocket.onclose = this.registerCallbackFunction(function(event) { _this.assertEquals(webSocket.CLOSED, webSocket.readyState, "readyState should be CLOSED"); - _this.ready(); + _this.done(); }); this.assertEquals(webSocket.CONNECTING, webSocket.readyState, "readyState should be CONNECTING"); this.assertEquals("blob", webSocket.binaryType, "binaryType should be 'blob'"); @@ -55,7 +55,7 @@ unitTests.addTest("Test WebSocket invalid URL", function(finished) { webSocket.onclose = this.registerCallbackFunction(function(event) { _this.assertEquals(true, hadError, "hadError should be true"); _this.assertEquals(webSocket.CLOSED, webSocket.readyState, "readyState should be CLOSED"); - _this.ready(); + _this.done(); }); this.assertEquals(webSocket.CONNECTING, webSocket.readyState, "readyState should be CONNECTING"); this.assertEquals(WEBSOCKET_INVALID_URL, webSocket.url, "url should be '" + WEBSOCKET_INVALID_URL + "'"); @@ -78,7 +78,7 @@ unitTests.addTest("Test WebSocketServer with three clients", function(finished) if (respondedClients === NUMBER_OF_CLIENTS) { webSocketServer.close(); _this.assertEquals(false, webSocketServer.listening, "listening should be false"); - _this.ready(); + _this.done(); } }); newClient.send(JSON.stringify({message: TEST_MESSAGE, client: connectedClients})); diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 654b4746a8..0245da538d 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -39,7 +39,6 @@ void WebSocketClass::initialize() { QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine) { QString url; - QScriptValue callee = context->callee(); if (context->argumentCount() == 1) { url = context->argument(0).toString(); } From e581a57320495e3a5fc6b04ab5671428eafda9cc Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 21 Aug 2015 19:13:57 +0200 Subject: [PATCH 23/42] WebSocket constructor could allow more then one parameter now, only the first one is used though. --- libraries/script-engine/src/WebSocketClass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 0245da538d..27cb436998 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -39,7 +39,7 @@ void WebSocketClass::initialize() { QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine) { QString url; - if (context->argumentCount() == 1) { + if (context->argumentCount() > 0) { url = context->argument(0).toString(); } return engine->newQObject(new WebSocketClass(engine, url)); From c7346209fb40aba7902d76c73e4bc503e47aa69b Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 21 Aug 2015 19:44:43 +0200 Subject: [PATCH 24/42] fix memory leak --- libraries/script-engine/src/WebSocketClass.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 27cb436998..52059d1290 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -34,7 +34,7 @@ void WebSocketClass::initialize() { connect(_webSocket, &QWebSocket::disconnected, this, &WebSocketClass::handleOnClose); connect(_webSocket, &QWebSocket::textMessageReceived, this, &WebSocketClass::handleOnMessage); connect(_webSocket, &QWebSocket::connected, this, &WebSocketClass::handleOnOpen); - _binaryType = "blob"; + _binaryType = QStringLiteral("blob"); } QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* engine) { @@ -46,7 +46,7 @@ QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* } WebSocketClass::~WebSocketClass() { - + _webSocket->deleteLater(); } void WebSocketClass::send(QScriptValue message) { From d3a68331339f89ed6c939a6b0aec49204b429cae Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 21 Aug 2015 20:20:50 +0200 Subject: [PATCH 25/42] delete WebSockets on ScriptEngine finish --- libraries/script-engine/src/WebSocketClass.cpp | 10 ++++++---- libraries/script-engine/src/WebSocketServerClass.cpp | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 52059d1290..7c7670fcce 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -42,7 +42,9 @@ QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* if (context->argumentCount() > 0) { url = context->argument(0).toString(); } - return engine->newQObject(new WebSocketClass(engine, url)); + auto webSocketClass = new WebSocketClass(engine, url); + connect(static_cast(engine), &ScriptEngine::finished, webSocketClass, &QObject::deleteLater); + return engine->newQObject(webSocketClass); } WebSocketClass::~WebSocketClass() { @@ -54,7 +56,7 @@ void WebSocketClass::send(QScriptValue message) { } void WebSocketClass::close() { - this->close(QWebSocketProtocol::CloseCode::CloseCodeNormal); + this->close(QWebSocketProtocol::CloseCodeNormal); } void WebSocketClass::close(QWebSocketProtocol::CloseCode closeCode) { @@ -67,7 +69,7 @@ void WebSocketClass::close(QWebSocketProtocol::CloseCode closeCode, QString reas void WebSocketClass::handleOnClose() { bool hasError = false; - if (_webSocket->error() != QAbstractSocket::SocketError::UnknownSocketError) { + if (_webSocket->error() != QAbstractSocket::UnknownSocketError) { hasError = true; if (_onErrorEvent.isFunction()) { _onErrorEvent.call(); @@ -76,7 +78,7 @@ void WebSocketClass::handleOnClose() { if (_onCloseEvent.isFunction()) { QScriptValueList args; QScriptValue arg = _engine->newObject(); - arg.setProperty("code", hasError ? QWebSocketProtocol::CloseCode::CloseCodeAbnormalDisconnection : _webSocket->closeCode()); + arg.setProperty("code", hasError ? QWebSocketProtocol::CloseCodeAbnormalDisconnection : _webSocket->closeCode()); arg.setProperty("reason", _webSocket->closeReason()); arg.setProperty("wasClean", !hasError); args << arg; diff --git a/libraries/script-engine/src/WebSocketServerClass.cpp b/libraries/script-engine/src/WebSocketServerClass.cpp index dbe92510e4..045b39d800 100644 --- a/libraries/script-engine/src/WebSocketServerClass.cpp +++ b/libraries/script-engine/src/WebSocketServerClass.cpp @@ -49,6 +49,7 @@ WebSocketServerClass::~WebSocketServerClass() { if (_webSocketServer.isListening()) { close(); } + _clients.empty(); } void WebSocketServerClass::onNewConnection() { From 6ee22055fa6b5c3e1b40e4aee451522a7334b9a1 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 21 Aug 2015 12:48:08 -0700 Subject: [PATCH 26/42] More temporary logging to track down cause of assert --- interface/src/avatar/Avatar.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 19f84018f8..6d6120f3ba 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -748,11 +748,12 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, floa qDebug() << "ASSERT because isinf(scale)"; } qDebug() << "textPosition =" << textPosition; + qDebug() << "projMat =" << projMat; + qDebug() << "viewMat =" << viewMat; + qDebug() << "viewProj =" << viewProj; qDebug() << "windowSizeY =" << windowSizeY; - qDebug() << "p1.y =" << p1.y; - qDebug() << "p1.w =" << p1.w; - qDebug() << "p0.y =" << p0.y; - qDebug() << "p0.w =" << p0.w; + qDebug() << "p1 =" << p1; + qDebug() << "p0 =" << p0; qDebug() << "qApp->getDevicePixelRatio() =" << qApp->getDevicePixelRatio(); qDebug() << "fontSize =" << fontSize; qDebug() << "pixelHeight =" << pixelHeight; From d89ecc1e420cd01b0bf501bbb38d22b6b0686f3c Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 21 Aug 2015 22:43:49 +0200 Subject: [PATCH 27/42] Move WebSocketServer to be accessable from Assignment-Client:Agent only. --- assignment-client/src/Agent.cpp | 4 + .../utilities/diagnostics/testWebSocket.js | 73 ++++++++++--------- libraries/script-engine/src/ScriptEngine.cpp | 4 - 3 files changed, 42 insertions(+), 39 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index d656464c10..4c3958c878 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -24,6 +24,7 @@ #include #include +#include #include // TODO: consider moving to scriptengine.h #include "avatars/ScriptableAvatar.h" @@ -184,6 +185,9 @@ void Agent::run() { _scriptEngine.registerGlobalObject("SoundCache", DependencyManager::get().data()); + QScriptValue webSocketServerConstructorValue = _scriptEngine.newFunction(WebSocketServerClass::constructor); + _scriptEngine.globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue); + auto entityScriptingInterface = DependencyManager::get(); _scriptEngine.registerGlobalObject("EntityViewer", &_entityViewer); diff --git a/examples/utilities/diagnostics/testWebSocket.js b/examples/utilities/diagnostics/testWebSocket.js index af915c3790..4cf6c3682b 100644 --- a/examples/utilities/diagnostics/testWebSocket.js +++ b/examples/utilities/diagnostics/testWebSocket.js @@ -61,40 +61,43 @@ unitTests.addTest("Test WebSocket invalid URL", function(finished) { this.assertEquals(WEBSOCKET_INVALID_URL, webSocket.url, "url should be '" + WEBSOCKET_INVALID_URL + "'"); }, UNITTEST_TIMEOUT); - -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); +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(); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index c629529382..32a2a5b6f0 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -39,7 +39,6 @@ #include "TypedArrays.h" #include "XMLHttpRequestClass.h" #include "WebSocketClass.h" -#include "WebSocketServerClass.h" #include "SceneScriptingInterface.h" @@ -348,9 +347,6 @@ void ScriptEngine::init() { QScriptValue webSocketConstructorValue = newFunction(WebSocketClass::constructor); globalObject().setProperty("WebSocket", webSocketConstructorValue); - QScriptValue webSocketServerConstructorValue = newFunction(WebSocketServerClass::constructor); - globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue); - QScriptValue printConstructorValue = newFunction(debugPrint); globalObject().setProperty("print", printConstructorValue); From 176d7372adf72fbc3c7d61af52534ee52e9bc297 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 21 Aug 2015 23:03:26 +0200 Subject: [PATCH 28/42] add QWebSockets to assignment-client project (cmake) --- assignment-client/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 315eeb6b83..dc20763953 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME assignment-client) -setup_hifi_project(Core Gui Network Script Widgets) +setup_hifi_project(Core Gui Network Script Widgets WebSockets) add_dependency_external_projects(glm) find_package(GLM REQUIRED) From 220ad189a04128e1926182b6170e1f46d1cbd6bf Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 21 Aug 2015 14:16:34 -0700 Subject: [PATCH 29/42] force the HTTPManager to listen on IPv4 --- .../embedded-webserver/src/HTTPManager.cpp | 27 ++++++------------- .../embedded-webserver/src/HTTPManager.h | 7 ----- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index 72436fc55e..946da00a34 100644 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -29,10 +29,14 @@ HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestH _requestHandler(requestHandler), _port(port) { - bindSocket(); - _isListeningTimer = new QTimer(this); - connect(_isListeningTimer, &QTimer::timeout, this, &HTTPManager::isTcpServerListening); - _isListeningTimer->start(SOCKET_CHECK_INTERVAL_IN_MS); + qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port); + + if (listen(QHostAddress::AnyIPv4, _port)) { + qCDebug(embeddedwebserver) << "TCP socket is listening on" << serverAddress() << "and port" << serverPort(); + } else { + qCritical() << "Failed to open HTTP server socket:" << errorString() << " - forcing exit."; + QCoreApplication::exit(SOCKET_ERROR_EXIT_CODE); + } } void HTTPManager::incomingConnection(qintptr socketDescriptor) { @@ -162,18 +166,3 @@ bool HTTPManager::requestHandledByRequestHandler(HTTPConnection* connection, con return _requestHandler && _requestHandler->handleHTTPRequest(connection, url); } -void HTTPManager::isTcpServerListening() { - if (!isListening()) { - qCWarning(embeddedwebserver) << "Socket on port " << QString::number(_port) << " is no longer listening"; - bindSocket(); - } -} - -bool HTTPManager::bindSocket() { - qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port); - if (!listen(QHostAddress::Any, _port)) { - qCritical() << "Failed to open HTTP server socket:" << errorString() << " can't continue"; - QCoreApplication::exit(SOCKET_ERROR_EXIT_CODE); - } - return true; -} \ No newline at end of file diff --git a/libraries/embedded-webserver/src/HTTPManager.h b/libraries/embedded-webserver/src/HTTPManager.h index 6375b10205..8e1780a631 100644 --- a/libraries/embedded-webserver/src/HTTPManager.h +++ b/libraries/embedded-webserver/src/HTTPManager.h @@ -37,12 +37,6 @@ public: bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false); -private slots: - void isTcpServerListening(); - -private: - bool bindSocket(); - protected: /// Accepts all pending connections virtual void incomingConnection(qintptr socketDescriptor); @@ -50,7 +44,6 @@ protected: QString _documentRoot; HTTPRequestHandler* _requestHandler; - QTimer* _isListeningTimer; const quint16 _port; }; From ad0df3a47c8b1801be37c1aa12f768459694289a Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 21 Aug 2015 14:21:49 -0700 Subject: [PATCH 30/42] Make sixense work in dev builds (if you put the library in the documented place). --- libraries/input-plugins/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/input-plugins/CMakeLists.txt b/libraries/input-plugins/CMakeLists.txt index 4428327deb..5d6dae7ad1 100644 --- a/libraries/input-plugins/CMakeLists.txt +++ b/libraries/input-plugins/CMakeLists.txt @@ -6,7 +6,7 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE) if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR) string(TOLOWER ${EXTERNAL} ${EXTERNAL}_LOWERCASE) - set(${${EXTERNAL}_UPPERCASE}_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/${${EXTERNAL}_LOWERCASE}") + set(${${EXTERNAL}_UPPERCASE}_ROOT_DIR "${CMAKE_SOURCE_DIR}/interface/external/${${EXTERNAL}_LOWERCASE}") endif () endforeach() From 34b3fb1c0c7e2230b55f8f9afee889ecf9be5512 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 21 Aug 2015 14:27:17 -0700 Subject: [PATCH 31/42] Revert "force the HTTPManager to listen on IPv4" This reverts commit 220ad189a04128e1926182b6170e1f46d1cbd6bf. --- .../embedded-webserver/src/HTTPManager.cpp | 27 +++++++++++++------ .../embedded-webserver/src/HTTPManager.h | 7 +++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index 946da00a34..72436fc55e 100644 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -29,14 +29,10 @@ HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestH _requestHandler(requestHandler), _port(port) { - qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port); - - if (listen(QHostAddress::AnyIPv4, _port)) { - qCDebug(embeddedwebserver) << "TCP socket is listening on" << serverAddress() << "and port" << serverPort(); - } else { - qCritical() << "Failed to open HTTP server socket:" << errorString() << " - forcing exit."; - QCoreApplication::exit(SOCKET_ERROR_EXIT_CODE); - } + bindSocket(); + _isListeningTimer = new QTimer(this); + connect(_isListeningTimer, &QTimer::timeout, this, &HTTPManager::isTcpServerListening); + _isListeningTimer->start(SOCKET_CHECK_INTERVAL_IN_MS); } void HTTPManager::incomingConnection(qintptr socketDescriptor) { @@ -166,3 +162,18 @@ bool HTTPManager::requestHandledByRequestHandler(HTTPConnection* connection, con return _requestHandler && _requestHandler->handleHTTPRequest(connection, url); } +void HTTPManager::isTcpServerListening() { + if (!isListening()) { + qCWarning(embeddedwebserver) << "Socket on port " << QString::number(_port) << " is no longer listening"; + bindSocket(); + } +} + +bool HTTPManager::bindSocket() { + qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port); + if (!listen(QHostAddress::Any, _port)) { + qCritical() << "Failed to open HTTP server socket:" << errorString() << " can't continue"; + QCoreApplication::exit(SOCKET_ERROR_EXIT_CODE); + } + return true; +} \ No newline at end of file diff --git a/libraries/embedded-webserver/src/HTTPManager.h b/libraries/embedded-webserver/src/HTTPManager.h index 8e1780a631..6375b10205 100644 --- a/libraries/embedded-webserver/src/HTTPManager.h +++ b/libraries/embedded-webserver/src/HTTPManager.h @@ -37,6 +37,12 @@ public: bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false); +private slots: + void isTcpServerListening(); + +private: + bool bindSocket(); + protected: /// Accepts all pending connections virtual void incomingConnection(qintptr socketDescriptor); @@ -44,6 +50,7 @@ protected: QString _documentRoot; HTTPRequestHandler* _requestHandler; + QTimer* _isListeningTimer; const quint16 _port; }; From 3e4a05a57bf7284537065288605f8ad597f6b226 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 21 Aug 2015 14:30:41 -0700 Subject: [PATCH 32/42] leave the listen check but queue Application quit --- .../embedded-webserver/src/HTTPManager.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index 72436fc55e..4498d619cd 100644 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -20,7 +20,6 @@ #include "EmbeddedWebserverLogging.h" #include "HTTPManager.h" -const int SOCKET_ERROR_EXIT_CODE = 2; const int SOCKET_CHECK_INTERVAL_IN_MS = 30000; HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler, QObject* parent) : @@ -30,6 +29,7 @@ HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestH _port(port) { bindSocket(); + _isListeningTimer = new QTimer(this); connect(_isListeningTimer, &QTimer::timeout, this, &HTTPManager::isTcpServerListening); _isListeningTimer->start(SOCKET_CHECK_INTERVAL_IN_MS); @@ -171,9 +171,16 @@ void HTTPManager::isTcpServerListening() { bool HTTPManager::bindSocket() { qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port); - if (!listen(QHostAddress::Any, _port)) { + + if (listen(QHostAddress::AnyIPv4, _port)) { + qCDebug(embeddedwebserver) << "TCP socket is listening on" << serverAddress() << "and port" << serverPort(); + + return true; + } else { qCritical() << "Failed to open HTTP server socket:" << errorString() << " can't continue"; - QCoreApplication::exit(SOCKET_ERROR_EXIT_CODE); + QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection); + + return false; } - return true; -} \ No newline at end of file + +} From 0fbbfb77d1d66df91e5130613e5fec1b6606129b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 21 Aug 2015 14:42:03 -0700 Subject: [PATCH 33/42] exit with a code to indicate error --- libraries/embedded-webserver/src/HTTPManager.cpp | 10 +++++++--- libraries/embedded-webserver/src/HTTPManager.h | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index 4498d619cd..525caf8f3c 100644 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -20,6 +20,7 @@ #include "EmbeddedWebserverLogging.h" #include "HTTPManager.h" +const int SOCKET_ERROR_EXIT_CODE = 2; const int SOCKET_CHECK_INTERVAL_IN_MS = 30000; HTTPManager::HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler, QObject* parent) : @@ -172,15 +173,18 @@ void HTTPManager::isTcpServerListening() { bool HTTPManager::bindSocket() { qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port); - if (listen(QHostAddress::AnyIPv4, _port)) { + if (false) { 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"; - QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection); + QMetaObject::invokeMethod(this, "queuedExit", Qt::QueuedConnection); return false; } - +} + +void HTTPManager::queuedExit() { + QCoreApplication::exit(SOCKET_ERROR_EXIT_CODE); } diff --git a/libraries/embedded-webserver/src/HTTPManager.h b/libraries/embedded-webserver/src/HTTPManager.h index 6375b10205..03498fbe8d 100644 --- a/libraries/embedded-webserver/src/HTTPManager.h +++ b/libraries/embedded-webserver/src/HTTPManager.h @@ -39,6 +39,7 @@ public: private slots: void isTcpServerListening(); + void queuedExit(); private: bool bindSocket(); From 4b95d54955f0264b826cf6a3ca4cc9c7dc29ad84 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 21 Aug 2015 14:55:24 -0700 Subject: [PATCH 34/42] remove the explicit failure test --- libraries/embedded-webserver/src/HTTPManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index 525caf8f3c..1f1dfa1735 100644 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -173,7 +173,7 @@ void HTTPManager::isTcpServerListening() { bool HTTPManager::bindSocket() { qCDebug(embeddedwebserver) << "Attempting to bind TCP socket on port " << QString::number(_port); - if (false) { + if (listen(QHostAddress::AnyIPv4, _port)) { qCDebug(embeddedwebserver) << "TCP socket is listening on" << serverAddress() << "and port" << serverPort(); return true; From 518cf3be1504234eb0dc22906876e292b2186f57 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Fri, 21 Aug 2015 16:28:38 -0700 Subject: [PATCH 35/42] Improve the light attenuation formulae --- libraries/model/src/model/Light.cpp | 11 +++++++++-- libraries/model/src/model/Light.h | 2 ++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/libraries/model/src/model/Light.cpp b/libraries/model/src/model/Light.cpp index 525f68e640..823be727a4 100755 --- a/libraries/model/src/model/Light.cpp +++ b/libraries/model/src/model/Light.cpp @@ -58,10 +58,12 @@ const Vec3& Light::getDirection() const { void Light::setColor(const Color& color) { editSchema()._color = color; + updateLightRadius(); } void Light::setIntensity(float intensity) { editSchema()._intensity = intensity; + updateLightRadius(); } void Light::setAmbientIntensity(float intensity) { @@ -72,9 +74,14 @@ void Light::setMaximumRadius(float radius) { if (radius <= 0.f) { radius = 1.0f; } + editSchema()._attenuation.w = radius; + updateLightRadius(); +} + +void Light::updateLightRadius() { float CutOffIntensityRatio = 0.05f; - float surfaceRadius = radius / (sqrtf(1.0f / CutOffIntensityRatio) - 1.0f); - editSchema()._attenuation = Vec4(surfaceRadius, 1.0f/surfaceRadius, CutOffIntensityRatio, radius); + float surfaceRadius = getMaximumRadius() / (sqrtf((getIntensity() * std::max(std::max(getColor().x, getColor().y), getColor().z)) / CutOffIntensityRatio) - 1.0f); + editSchema()._attenuation = Vec4(surfaceRadius, 1.0f/surfaceRadius, CutOffIntensityRatio, getMaximumRadius()); } #include diff --git a/libraries/model/src/model/Light.h b/libraries/model/src/model/Light.h index 1ed07a942c..3fbaba75bf 100755 --- a/libraries/model/src/model/Light.h +++ b/libraries/model/src/model/Light.h @@ -127,6 +127,8 @@ protected: const Schema& getSchema() const { return _schemaBuffer.get(); } Schema& editSchema() { return _schemaBuffer.edit(); } + + void updateLightRadius(); }; typedef std::shared_ptr< Light > LightPointer; From f512205f6f22b26eb7a864e2eeb27c1fee4d7468 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 22 Aug 2015 01:30:07 +0200 Subject: [PATCH 36/42] add ability to include scripts from a relative path in assignment-client (for persistent scripts only) --- assignment-client/src/Agent.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 4c3958c878..300976f81c 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -181,6 +181,10 @@ void Agent::run() { // register ourselves to the script engine _scriptEngine.registerGlobalObject("Agent", this); + if (!_payload.isEmpty()) { + _scriptEngine.setParentURL(_payload); + } + _scriptEngine.init(); // must be done before we set up the viewers _scriptEngine.registerGlobalObject("SoundCache", DependencyManager::get().data()); From 9edba451eb989f68d9190887566dc610a0524027 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 22 Aug 2015 01:33:22 +0200 Subject: [PATCH 37/42] - remove webSockets from List after disconnect. - leave it up to the QtScript to delete the WebSocket / WebSocketServer objects --- libraries/script-engine/src/WebSocketClass.cpp | 6 ++---- libraries/script-engine/src/WebSocketClass.h | 2 ++ libraries/script-engine/src/WebSocketServerClass.cpp | 7 ++++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 7c7670fcce..8341d65d81 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -42,9 +42,7 @@ QScriptValue WebSocketClass::constructor(QScriptContext* context, QScriptEngine* if (context->argumentCount() > 0) { url = context->argument(0).toString(); } - auto webSocketClass = new WebSocketClass(engine, url); - connect(static_cast(engine), &ScriptEngine::finished, webSocketClass, &QObject::deleteLater); - return engine->newQObject(webSocketClass); + return engine->newQObject(new WebSocketClass(engine, url), QScriptEngine::ScriptOwnership); } WebSocketClass::~WebSocketClass() { @@ -111,7 +109,7 @@ void qWSCloseCodeFromScriptValue(const QScriptValue &object, QWebSocketProtocol: } QScriptValue webSocketToScriptValue(QScriptEngine* engine, WebSocketClass* const &in) { - return engine->newQObject(in); + return engine->newQObject(in, QScriptEngine::ScriptOwnership); } void webSocketFromScriptValue(const QScriptValue &object, WebSocketClass* &out) { diff --git a/libraries/script-engine/src/WebSocketClass.h b/libraries/script-engine/src/WebSocketClass.h index e053257e3d..b87d578843 100644 --- a/libraries/script-engine/src/WebSocketClass.h +++ b/libraries/script-engine/src/WebSocketClass.h @@ -50,6 +50,8 @@ public: CLOSED }; + QWebSocket* getWebSocket() { return _webSocket; } + int getConnecting() const { return CONNECTING; }; int getOpen() const { return OPEN; }; int getClosing() const { return CLOSING; }; diff --git a/libraries/script-engine/src/WebSocketServerClass.cpp b/libraries/script-engine/src/WebSocketServerClass.cpp index 045b39d800..3b3a02a7c9 100644 --- a/libraries/script-engine/src/WebSocketServerClass.cpp +++ b/libraries/script-engine/src/WebSocketServerClass.cpp @@ -40,9 +40,7 @@ QScriptValue WebSocketServerClass::constructor(QScriptContext* context, QScriptE serverName = serverNameOption.toString(); } } - auto webSocketServerClass = new WebSocketServerClass(engine, serverName, port); - connect(static_cast(engine), &ScriptEngine::finished, webSocketServerClass, &QObject::deleteLater); - return engine->newQObject(webSocketServerClass); + return engine->newQObject(new WebSocketServerClass(engine, serverName, port), QScriptEngine::ScriptOwnership); } WebSocketServerClass::~WebSocketServerClass() { @@ -55,6 +53,9 @@ WebSocketServerClass::~WebSocketServerClass() { 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); } From 0e255f4f3ee785b6c35799423e0dad447e2291a1 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 22 Aug 2015 01:59:44 +0200 Subject: [PATCH 38/42] properly exposed WebSocketClass::ReadyState to the ScriptEngine --- libraries/script-engine/src/ScriptEngine.cpp | 1 + .../script-engine/src/WebSocketClass.cpp | 8 ++++++ libraries/script-engine/src/WebSocketClass.h | 25 +++++++++++-------- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 32a2a5b6f0..b42daa710a 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -359,6 +359,7 @@ void ScriptEngine::init() { qScriptRegisterMetaType(this, animationDetailsToScriptValue, animationDetailsFromScriptValue); qScriptRegisterMetaType(this, webSocketToScriptValue, webSocketFromScriptValue); qScriptRegisterMetaType(this, qWSCloseCodeToScriptValue, qWSCloseCodeFromScriptValue); + qScriptRegisterMetaType(this, wscReadyStateToScriptValue, wscReadyStateFromScriptValue); registerGlobalObject("Script", this); registerGlobalObject("Audio", &AudioScriptingInterface::getInstance()); diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 8341d65d81..fd923965cf 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -115,3 +115,11 @@ QScriptValue webSocketToScriptValue(QScriptEngine* engine, WebSocketClass* const void webSocketFromScriptValue(const QScriptValue &object, WebSocketClass* &out) { out = qobject_cast(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(); +} diff --git a/libraries/script-engine/src/WebSocketClass.h b/libraries/script-engine/src/WebSocketClass.h index b87d578843..9f14b44ba0 100644 --- a/libraries/script-engine/src/WebSocketClass.h +++ b/libraries/script-engine/src/WebSocketClass.h @@ -28,13 +28,13 @@ class WebSocketClass : public QObject { Q_PROPERTY(QScriptValue onopen READ getOnOpen WRITE setOnOpen) Q_PROPERTY(QString protocol READ getProtocol) - Q_PROPERTY(uint readyState READ getReadyState) + Q_PROPERTY(WebSocketClass::ReadyState readyState READ getReadyState) Q_PROPERTY(QString url READ getURL) - Q_PROPERTY(int CONNECTING READ getConnecting CONSTANT) - Q_PROPERTY(int OPEN READ getOpen CONSTANT) - Q_PROPERTY(int CLOSING READ getClosing CONSTANT) - Q_PROPERTY(int CLOSED READ getClosed CONSTANT) + 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); @@ -52,10 +52,10 @@ public: QWebSocket* getWebSocket() { return _webSocket; } - int getConnecting() const { return CONNECTING; }; - int getOpen() const { return OPEN; }; - int getClosing() const { return CLOSING; }; - int getClosed() const { return CLOSED; }; + 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; } @@ -70,7 +70,7 @@ public: QString getURL() { return _webSocket->requestUrl().toDisplayString(); } - uint getReadyState() { + ReadyState getReadyState() { switch (_webSocket->state()) { case QAbstractSocket::SocketState::HostLookupState: case QAbstractSocket::SocketState::ConnectingState: @@ -125,10 +125,15 @@ private slots: }; 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 From d84cf1859c25e9e94d2613da9367f0d53ffd38a8 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 22 Aug 2015 02:18:21 +0200 Subject: [PATCH 39/42] hook up the WebSocket error event directly --- libraries/script-engine/src/WebSocketClass.cpp | 16 +++++++++------- libraries/script-engine/src/WebSocketClass.h | 1 + 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index fd923965cf..c844dc3582 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -34,6 +34,8 @@ 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(&QWebSocket::error), this, + &WebSocketClass::handleOnError); _binaryType = QStringLiteral("blob"); } @@ -66,13 +68,7 @@ void WebSocketClass::close(QWebSocketProtocol::CloseCode closeCode, QString reas } void WebSocketClass::handleOnClose() { - bool hasError = false; - if (_webSocket->error() != QAbstractSocket::UnknownSocketError) { - hasError = true; - if (_onErrorEvent.isFunction()) { - _onErrorEvent.call(); - } - } + bool hasError = (_webSocket->error() != QAbstractSocket::UnknownSocketError); if (_onCloseEvent.isFunction()) { QScriptValueList args; QScriptValue arg = _engine->newObject(); @@ -84,6 +80,12 @@ void WebSocketClass::handleOnClose() { } } +void WebSocketClass::handleOnError(QAbstractSocket::SocketError error) { + if (_onErrorEvent.isFunction()) { + _onErrorEvent.call(); + } +} + void WebSocketClass::handleOnMessage(const QString& message) { if (_onMessageEvent.isFunction()) { QScriptValueList args; diff --git a/libraries/script-engine/src/WebSocketClass.h b/libraries/script-engine/src/WebSocketClass.h index 9f14b44ba0..8ba8ecf362 100644 --- a/libraries/script-engine/src/WebSocketClass.h +++ b/libraries/script-engine/src/WebSocketClass.h @@ -119,6 +119,7 @@ private: private slots: void handleOnClose(); + void handleOnError(QAbstractSocket::SocketError error); void handleOnMessage(const QString& message); void handleOnOpen(); From 67e945b8847a833f8ad9ac0800984ecafc40ee98 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 22 Aug 2015 03:32:53 +0200 Subject: [PATCH 40/42] fix unit test --- examples/utilities/diagnostics/testWebSocket.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/utilities/diagnostics/testWebSocket.js b/examples/utilities/diagnostics/testWebSocket.js index 4cf6c3682b..c356fd99a8 100644 --- a/examples/utilities/diagnostics/testWebSocket.js +++ b/examples/utilities/diagnostics/testWebSocket.js @@ -51,11 +51,10 @@ unitTests.addTest("Test WebSocket invalid URL", function(finished) { var hadError = false; webSocket.onerror = this.registerCallbackFunction(function() { hadError = true; + _this.done(); }); webSocket.onclose = this.registerCallbackFunction(function(event) { - _this.assertEquals(true, hadError, "hadError should be true"); _this.assertEquals(webSocket.CLOSED, webSocket.readyState, "readyState should be CLOSED"); - _this.done(); }); this.assertEquals(webSocket.CONNECTING, webSocket.readyState, "readyState should be CONNECTING"); this.assertEquals(WEBSOCKET_INVALID_URL, webSocket.url, "url should be '" + WEBSOCKET_INVALID_URL + "'"); From 04e7084743a1c50fac9b1040c6b3878b91aa9601 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 21 Aug 2015 21:43:21 -0700 Subject: [PATCH 41/42] Resource fix for textures in FBX files. The baseTexturePath url for textures in FBXGeometry should default to the same base url as the fbx file itself. This error was introduced in my recent refactoring. Textures embedded in FBXGeometries should be un-affected by this change. --- libraries/render-utils/src/GeometryCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index f48ceb9b62..c778376e92 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1723,7 +1723,7 @@ void GeometryReader::run() { NetworkGeometry::NetworkGeometry(const QUrl& url, bool delayLoad, const QVariantHash& mapping, const QUrl& textureBaseUrl) : _url(url), _mapping(mapping), - _textureBaseUrl(textureBaseUrl) { + _textureBaseUrl(textureBaseUrl.isValid() ? textureBaseUrl : url) { if (delayLoad) { _state = DelayState; From d3135dcfa6167052e6a34520e0f5e64db7a4ba0f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 20 Aug 2015 17:04:53 -0700 Subject: [PATCH 42/42] Stereo rendering work --- interface/src/Application.cpp | 156 +++-------------- interface/src/Application.h | 26 +-- interface/src/Menu.cpp | 4 +- interface/src/PluginContainerProxy.cpp | 161 ++++++++++++++++++ interface/src/PluginContainerProxy.h | 30 ++++ .../Basic2DWindowOpenGLDisplayPlugin.cpp | 1 - .../src/display-plugins/DisplayPlugin.cpp | 8 +- .../src/display-plugins/DisplayPlugin.h | 2 +- .../display-plugins/OpenGLDisplayPlugin.cpp | 2 +- .../src/display-plugins/OpenGLDisplayPlugin.h | 1 + .../WindowOpenGLDisplayPlugin.cpp | 5 + .../WindowOpenGLDisplayPlugin.h | 1 + .../stereo/InterleavedStereoDisplayPlugin.cpp | 53 +++++- .../stereo/InterleavedStereoDisplayPlugin.h | 3 + .../stereo/SideBySideStereoDisplayPlugin.cpp | 23 +-- .../stereo/SideBySideStereoDisplayPlugin.h | 3 +- .../stereo/StereoDisplayPlugin.cpp | 44 ++++- .../stereo/StereoDisplayPlugin.h | 2 + libraries/plugins/src/plugins/Forward.h | 23 +++ libraries/plugins/src/plugins/Plugin.cpp | 7 + libraries/plugins/src/plugins/Plugin.h | 9 +- .../plugins/src/plugins/PluginContainer.h | 7 +- .../plugins/src/plugins/PluginManager.cpp | 8 +- libraries/plugins/src/plugins/PluginManager.h | 20 +-- 24 files changed, 397 insertions(+), 202 deletions(-) create mode 100644 interface/src/PluginContainerProxy.cpp create mode 100644 interface/src/PluginContainerProxy.h create mode 100644 libraries/plugins/src/plugins/Forward.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 50d60b4cb3..0f3282582f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -58,6 +58,8 @@ #include #include #include +#include +#include #include #include @@ -148,6 +150,8 @@ #include "ui/overlays/Cube3DOverlay.h" +#include "PluginContainerProxy.h" + // ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU // FIXME seems to be broken. #if defined(Q_OS_WIN) @@ -301,7 +305,7 @@ bool setupEssentials(int& argc, char** argv) { // continuing to overburden Application.cpp Cube3DOverlay* _keyboardFocusHighlight{ nullptr }; int _keyboardFocusHighlightID{ -1 }; - +PluginContainer* _pluginContainer; Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : QApplication(argc, argv), @@ -351,7 +355,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _applicationOverlay() { setInstance(this); - Plugin::setContainer(this); + + _pluginContainer = new PluginContainerProxy(); + Plugin::setContainer(_pluginContainer); #ifdef Q_OS_WIN installNativeEventFilter(&MyNativeEventFilter::getInstance()); #endif @@ -1262,6 +1268,7 @@ void Application::resizeGL() { // Possible change in aspect ratio loadViewFrustum(_myCamera, _viewFrustum); float fov = glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES); + // FIXME the aspect ratio for stereo displays is incorrect based on this. float aspectRatio = aspect(_renderResolution); _myCamera.setProjection(glm::perspective(fov, aspectRatio, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP)); } @@ -1421,7 +1428,15 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_Enter: case Qt::Key_Return: - Menu::getInstance()->triggerOption(MenuOption::AddressBar); + if (isOption) { + if (_window->isFullScreen()) { + _pluginContainer->unsetFullscreen(); + } else { + _pluginContainer->setFullscreen(nullptr); + } + } else { + Menu::getInstance()->triggerOption(MenuOption::AddressBar); + } break; case Qt::Key_B: @@ -4617,10 +4632,6 @@ void Application::checkSkeleton() { } } -bool Application::isForeground() { - return _isForeground && !getWindow()->isMinimized(); -} - void Application::activeChanged(Qt::ApplicationState state) { switch (state) { case Qt::ApplicationActive: @@ -4726,6 +4737,10 @@ const DisplayPlugin * Application::getActiveDisplayPlugin() const { return ((Application*)this)->getActiveDisplayPlugin(); } +bool _activatingDisplayPlugin{ false }; +QVector> _currentDisplayPluginActions; +QVector> _currentInputPluginActions; + static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool active = false) { auto menu = Menu::getInstance(); @@ -4747,9 +4762,6 @@ static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool acti Q_ASSERT(menu->menuItemExists(MenuOption::OutputMenu, name)); } -static QVector> _currentDisplayPluginActions; -static bool _activatingDisplayPlugin{false}; - void Application::updateDisplayMode() { auto menu = Menu::getInstance(); auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins(); @@ -4816,7 +4828,7 @@ void Application::updateDisplayMode() { // Only show the hmd tools after the correct plugin has // been activated so that it's UI is setup correctly if (newPluginWantsHMDTools) { - showDisplayPluginsTools(); + _pluginContainer->showDisplayPluginsTools(); } if (oldDisplayPlugin) { @@ -4834,9 +4846,6 @@ void Application::updateDisplayMode() { Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin"); } -static QVector> _currentInputPluginActions; - - static void addInputPluginToMenu(InputPluginPointer inputPlugin, bool active = false) { auto menu = Menu::getInstance(); QString name = inputPlugin->getName(); @@ -4910,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 onClicked, bool checkable, bool checked, const QString& groupName) { - auto menu = Menu::getInstance(); - MenuWrapper* parentItem = menu->getMenu(path); - QAction* action = menu->addActionToQMenuAndActionHash(parentItem, name); - connect(action, &QAction::triggered, [=] { - onClicked(action->isChecked()); - }); - action->setCheckable(checkable); - action->setChecked(checked); - if (_activatingDisplayPlugin) { - _currentDisplayPluginActions.push_back({ path, name }); - } else { - _currentInputPluginActions.push_back({ path, name }); - } -} - -void Application::removeMenuItem(const QString& menuName, const QString& menuItem) { - Menu::getInstance()->removeMenuItem(menuName, menuItem); -} - -bool Application::isOptionChecked(const QString& name) { - return Menu::getInstance()->isOptionChecked(name); -} - -void Application::setIsOptionChecked(const QString& path, bool checked) { - Menu::getInstance()->setIsOptionChecked(path, checked); -} - mat4 Application::getEyeProjection(int eye) const { if (isHMDMode()) { return getActiveDisplayPlugin()->getProjection((Eye)eye, _viewFrustum.getProjection()); @@ -4978,89 +4951,6 @@ mat4 Application::getHMDSensorPose() const { return mat4(); } -// FIXME there is a bug in the fullscreen setting, where leaving -// fullscreen does not restore the window frame, making it difficult -// or impossible to move or size the window. -// Additionally, setting fullscreen isn't hiding the menu on windows -// make it useless for stereoscopic modes. -void Application::setFullscreen(const QScreen* target) { - if (!_window->isFullScreen()) { - _savedGeometry = _window->geometry(); - } -#ifdef Q_OS_MAC - _window->setGeometry(target->availableGeometry()); -#endif - _window->windowHandle()->setScreen((QScreen*)target); - _window->showFullScreen(); - -#ifndef Q_OS_MAC - // also hide the QMainWindow's menuBar - QMenuBar* menuBar = _window->menuBar(); - if (menuBar) { - menuBar->setVisible(false); - } -#endif -} - -void Application::unsetFullscreen(const QScreen* avoid) { - _window->showNormal(); - - QRect targetGeometry = _savedGeometry; - if (avoid != nullptr) { - QRect avoidGeometry = avoid->geometry(); - if (avoidGeometry.contains(targetGeometry.topLeft())) { - QScreen* newTarget = primaryScreen(); - if (newTarget == avoid) { - foreach(auto screen, screens()) { - if (screen != avoid) { - newTarget = screen; - break; - } - } - } - targetGeometry = newTarget->availableGeometry(); - } - } -#ifdef Q_OS_MAC - QTimer* timer = new QTimer(); - timer->singleShot(2000, [=] { - _window->setGeometry(targetGeometry); - timer->deleteLater(); - }); -#else - _window->setGeometry(targetGeometry); -#endif - -#ifndef Q_OS_MAC - // also show the QMainWindow's menuBar - QMenuBar* menuBar = _window->menuBar(); - if (menuBar) { - menuBar->setVisible(true); - } -#endif -} - - -void Application::showDisplayPluginsTools() { - DependencyManager::get()->hmdTools(true); -} - -QGLWidget* Application::getPrimarySurface() { - return _glWidget; -} - -void Application::setActiveDisplayPlugin(const QString& pluginName) { - auto menu = Menu::getInstance(); - foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { - QString name = displayPlugin->getName(); - QAction* action = menu->getActionForOption(name); - if (pluginName == name) { - action->setChecked(true); - } - } - updateDisplayMode(); -} - void Application::setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float deltaTime, int index) { PalmData* palm; bool foundHand = false; diff --git a/interface/src/Application.h b/interface/src/Application.h index a1765109ce..6394aa12d0 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -26,18 +26,18 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include #include #include -#include -#include #include #include "AudioClient.h" @@ -50,7 +50,6 @@ #include "Stars.h" #include "avatar/Avatar.h" #include "avatar/MyAvatar.h" -#include #include "scripting/ControllerScriptingInterface.h" #include "scripting/DialogsManagerScriptingInterface.h" #include "scripting/WebWindowClass.h" @@ -132,7 +131,7 @@ class Application; typedef bool (Application::* AcceptURLMethod)(const QString &); -class Application : public QApplication, public AbstractViewStateInterface, public AbstractScriptingServicesInterface, PluginContainer { +class Application : public QApplication, public AbstractViewStateInterface, public AbstractScriptingServicesInterface { Q_OBJECT friend class OctreePacketProcessor; @@ -280,23 +279,10 @@ public: virtual void endOverrideEnvironmentData() { _environment.endOverride(); } virtual qreal getDevicePixelRatio(); - // Plugin container support - virtual void addMenu(const QString& menuName); - virtual void removeMenu(const QString& menuName); - virtual void addMenuItem(const QString& path, const QString& name, std::function onClicked, bool checkable, bool checked, const QString& groupName); - virtual void removeMenuItem(const QString& menuName, const QString& menuItem); - virtual bool isOptionChecked(const QString& name); - virtual void setIsOptionChecked(const QString& path, bool checked); - virtual void setFullscreen(const QScreen* target) override; - virtual void unsetFullscreen(const QScreen* avoid) override; - virtual void showDisplayPluginsTools() override; - virtual QGLWidget* getPrimarySurface() override; - virtual bool isForeground() override; - void setActiveDisplayPlugin(const QString& pluginName); - DisplayPlugin * getActiveDisplayPlugin(); - const DisplayPlugin * getActiveDisplayPlugin() const; + DisplayPlugin* getActiveDisplayPlugin(); + const DisplayPlugin* getActiveDisplayPlugin() const; public: @@ -691,6 +677,8 @@ private: int _simsPerSecondReport = 0; quint64 _lastSimsPerSecondUpdate = 0; bool _isForeground = true; // starts out assumed to be in foreground + + friend class PluginContainerProxy; }; #endif // hifi_Application_h diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 11bc38c85e..f99b3ba579 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -16,11 +16,13 @@ #include #include #include +#include #include #include #include #include + #include "Application.h" #include "AccountManager.h" #include "audio/AudioScope.h" @@ -220,7 +222,7 @@ Menu::Menu() { addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0, qApp, SLOT(packageModel())); - MenuWrapper* displayMenu = addMenu("Display"); + MenuWrapper* displayMenu = addMenu(DisplayPlugin::MENU_PATH); { MenuWrapper* displayModeMenu = addMenu(MenuOption::OutputMenu); QActionGroup* displayModeGroup = new QActionGroup(displayModeMenu); diff --git a/interface/src/PluginContainerProxy.cpp b/interface/src/PluginContainerProxy.cpp new file mode 100644 index 0000000000..e172dbbd9e --- /dev/null +++ b/interface/src/PluginContainerProxy.cpp @@ -0,0 +1,161 @@ +#include "PluginContainerProxy.h" + +#include +#include + +#include +#include +#include + +#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> _currentDisplayPluginActions; +extern QVector> _currentInputPluginActions; +std::map _exclusiveGroups; + +QAction* PluginContainerProxy::addMenuItem(const QString& path, const QString& name, std::function 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()->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(); +} diff --git a/interface/src/PluginContainerProxy.h b/interface/src/PluginContainerProxy.h new file mode 100644 index 0000000000..e01cabc3b8 --- /dev/null +++ b/interface/src/PluginContainerProxy.h @@ -0,0 +1,30 @@ +#pragma once +#ifndef hifi_PluginContainerProxy_h +#define hifi_PluginContainerProxy_h + +#include +#include + +#include +#include + +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 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 \ No newline at end of file diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index 018a09ff7e..da8deefa74 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -13,7 +13,6 @@ const QString Basic2DWindowOpenGLDisplayPlugin::NAME("2D Display"); -static const QString MENU_PATH = "Display"; static const QString FULLSCREEN = "Fullscreen"; const QString& Basic2DWindowOpenGLDisplayPlugin::getName() const { diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp index cd620d85a4..5840b3cbba 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp @@ -18,6 +18,8 @@ #include "oculus/OculusDisplayPlugin.h" #include "oculus/OculusLegacyDisplayPlugin.h" +const QString DisplayPlugin::MENU_PATH{ "Display" }; + // TODO migrate to a DLL model where plugins are discovered and loaded at runtime by the PluginManager class DisplayPluginList getDisplayPlugins() { DisplayPlugin* PLUGIN_POOL[] = { @@ -27,9 +29,11 @@ DisplayPluginList getDisplayPlugins() { #endif // Stereo modes - // FIXME fix stereo display plugins + + // SBS left/right new SideBySideStereoDisplayPlugin(), - //new InterleavedStereoDisplayPlugin(), + // Interleaved left/right + new InterleavedStereoDisplayPlugin(), // HMDs diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h index 1a4166c0fa..8ee2d698a6 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h @@ -115,7 +115,7 @@ public: virtual void resetSensors() {} virtual float devicePixelRatio() { return 1.0; } - + static const QString MENU_PATH; signals: void recommendedFramebufferSizeChanged(const QSize & size); void requestRender(); diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 7269c9410c..0409899739 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -105,7 +105,7 @@ bool OpenGLDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { void OpenGLDisplayPlugin::display( GLuint finalTexture, const glm::uvec2& sceneSize) { using namespace oglplus; - uvec2 size = getRecommendedRenderSize(); + uvec2 size = getSurfaceSize(); Context::Viewport(size.x, size.y); glBindTexture(GL_TEXTURE_2D, finalTexture); drawUnitQuad(); diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 9b6b92d8d4..3152500232 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -33,6 +33,7 @@ public: protected: virtual void customizeContext(); virtual void drawUnitQuad(); + virtual glm::uvec2 getSurfaceSize() const = 0; virtual void makeCurrent() = 0; virtual void doneCurrent() = 0; virtual void swapBuffers() = 0; diff --git a/libraries/display-plugins/src/display-plugins/WindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/WindowOpenGLDisplayPlugin.cpp index 658aa2c767..ffea6605af 100644 --- a/libraries/display-plugins/src/display-plugins/WindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/WindowOpenGLDisplayPlugin.cpp @@ -16,6 +16,10 @@ WindowOpenGLDisplayPlugin::WindowOpenGLDisplayPlugin() { } glm::uvec2 WindowOpenGLDisplayPlugin::getRecommendedRenderSize() const { + return getSurfaceSize(); +} + +glm::uvec2 WindowOpenGLDisplayPlugin::getSurfaceSize() const { uvec2 result; if (_window) { result = toGlm(_window->geometry().size() * _window->devicePixelRatio()); @@ -23,6 +27,7 @@ glm::uvec2 WindowOpenGLDisplayPlugin::getRecommendedRenderSize() const { return result; } + glm::uvec2 WindowOpenGLDisplayPlugin::getRecommendedUiSize() const { uvec2 result; if (_window) { diff --git a/libraries/display-plugins/src/display-plugins/WindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/WindowOpenGLDisplayPlugin.h index c75cf1484c..fc7691fc56 100644 --- a/libraries/display-plugins/src/display-plugins/WindowOpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/WindowOpenGLDisplayPlugin.h @@ -21,6 +21,7 @@ public: virtual void deactivate() override; protected: + virtual glm::uvec2 getSurfaceSize() const override final; virtual void makeCurrent() override; virtual void doneCurrent() override; virtual void swapBuffers() override; diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp index 7d017f714d..23bc176d65 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp @@ -17,6 +17,42 @@ #include +static const char * INTERLEAVED_TEXTURED_VS = R"VS(#version 410 core +#pragma line __LINE__ + +in vec3 Position; +in vec2 TexCoord; + +out vec2 vTexCoord; + +void main() { + gl_Position = vec4(Position, 1); + vTexCoord = TexCoord; +} + +)VS"; + +static const char * INTERLEAVED_TEXTURED_FS = R"FS(#version 410 core +#pragma line __LINE__ + +uniform sampler2D sampler; +uniform ivec2 textureSize; + +in vec2 vTexCoord; +out vec4 FragColor; + +void main() { + ivec2 texCoord = ivec2(floor(vTexCoord * textureSize)); + texCoord.x /= 2; + int row = int(floor(gl_FragCoord.y)); + if (row % 2 > 0) { + texCoord.x += (textureSize.x / 2); + } + FragColor = texelFetch(sampler, texCoord, 0); //texture(sampler, texCoord); +} + +)FS"; + const QString InterleavedStereoDisplayPlugin::NAME("Interleaved Stereo Display"); const QString & InterleavedStereoDisplayPlugin::getName() const { @@ -29,5 +65,20 @@ InterleavedStereoDisplayPlugin::InterleavedStereoDisplayPlugin() { void InterleavedStereoDisplayPlugin::customizeContext() { StereoDisplayPlugin::customizeContext(); // Set up the stencil buffers? Or use a custom shader? - + compileProgram(_program, INTERLEAVED_TEXTURED_VS, INTERLEAVED_TEXTURED_FS); } + +glm::uvec2 InterleavedStereoDisplayPlugin::getRecommendedRenderSize() const { + uvec2 result = WindowOpenGLDisplayPlugin::getRecommendedRenderSize(); + result.x *= 2; + result.y /= 2; + return result; +} + +void InterleavedStereoDisplayPlugin::display( + GLuint finalTexture, const glm::uvec2& sceneSize) { + using namespace oglplus; + _program->Bind(); + Uniform(*_program, "textureSize").SetValue(sceneSize); + WindowOpenGLDisplayPlugin::display(finalTexture, sceneSize); +} \ No newline at end of file diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h index 1dd38efed5..3044d91247 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h @@ -18,6 +18,9 @@ public: // initialize OpenGL context settings needed by the plugin virtual void customizeContext() override; + virtual glm::uvec2 getRecommendedRenderSize() const override; + void display(GLuint finalTexture, const glm::uvec2& sceneSize) override; + private: static const QString NAME; }; diff --git a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp index 829385b209..1bdb02fd26 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp @@ -21,9 +21,6 @@ const QString SideBySideStereoDisplayPlugin::NAME("3D TV - Side by Side Stereo"); -static const QString MENU_PATH = "Display"; -static const QString FULLSCREEN = "Fullscreen"; - const QString & SideBySideStereoDisplayPlugin::getName() const { return NAME; } @@ -31,20 +28,10 @@ const QString & SideBySideStereoDisplayPlugin::getName() const { SideBySideStereoDisplayPlugin::SideBySideStereoDisplayPlugin() { } -void SideBySideStereoDisplayPlugin::activate() { - CONTAINER->addMenu(MENU_PATH); - CONTAINER->addMenuItem(MENU_PATH, FULLSCREEN, - [this](bool clicked) { - if (clicked) { - CONTAINER->setFullscreen(getFullscreenTarget()); - } else { - CONTAINER->unsetFullscreen(); - } - }, true, false); - StereoDisplayPlugin::activate(); +glm::uvec2 SideBySideStereoDisplayPlugin::getRecommendedRenderSize() const { + uvec2 result = WindowOpenGLDisplayPlugin::getRecommendedRenderSize(); + result.x *= 2; + return result; } -// FIXME target the screen the window is currently on -QScreen* SideBySideStereoDisplayPlugin::getFullscreenTarget() { - return qApp->primaryScreen(); -} + diff --git a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h index 3a764d9f4e..9f8440227f 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h @@ -16,8 +16,7 @@ class SideBySideStereoDisplayPlugin : public StereoDisplayPlugin { public: SideBySideStereoDisplayPlugin(); virtual const QString& getName() const override; - virtual void activate() override; + virtual glm::uvec2 getRecommendedRenderSize() const override; private: - QScreen* getFullscreenTarget(); static const QString NAME; }; diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index 20613d6a2c..43c3b727c4 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -10,11 +10,14 @@ #include #include +#include #include #include #include #include +#include +#include StereoDisplayPlugin::StereoDisplayPlugin() { } @@ -29,11 +32,14 @@ const float DEFAULT_IPD = 0.064f; const float HALF_DEFAULT_IPD = DEFAULT_IPD / 2.0f; glm::mat4 StereoDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProjection) const { - // FIXME check for mono eye and provide a combined matrix, needed for proper - // culling // Refer to http://www.nvidia.com/content/gtc-2010/pdfs/2010_gtc2010.pdf on creating // stereo projection matrices. Do NOT use "toe-in", use translation. + if (eye == Mono) { + // FIXME provide a combined matrix, needed for proper culling + return baseProjection; + } + float nearZ = DEFAULT_NEAR_CLIP; // near clipping plane float screenZ = 0.25f; // screen projection plane // FIXME verify this is the right calculation @@ -52,10 +58,36 @@ glm::mat4 StereoDisplayPlugin::getEyePose(Eye eye) const { return glm::translate(mat4(), vec3(modelviewShift, 0, 0)); } +std::vector _screenActions; void StereoDisplayPlugin::activate() { + auto screens = qApp->screens(); + _screenActions.resize(screens.size()); + for (int i = 0; i < screens.size(); ++i) { + auto screen = screens.at(i); + QString name = QString("Screen %1: %2").arg(i + 1).arg(screen->name()); + bool checked = false; + if (screen == qApp->primaryScreen()) { + checked = true; + } + auto action = CONTAINER->addMenuItem(MENU_PATH, name, + [this](bool clicked) { updateScreen(); }, true, checked, "Screens"); + _screenActions[i] = action; + } + CONTAINER->setFullscreen(qApp->primaryScreen()); WindowOpenGLDisplayPlugin::activate(); - // FIXME there is a bug in the fullscreen setting, see - // Application::setFullscreen - //CONTAINER->setFullscreen(qApp->primaryScreen()); - // FIXME Add menu items +} + +void StereoDisplayPlugin::updateScreen() { + for (int i = 0; i < _screenActions.size(); ++i) { + if (_screenActions[i]->isChecked()) { + CONTAINER->setFullscreen(qApp->screens().at(i)); + break; + } + } +} + +void StereoDisplayPlugin::deactivate() { + _screenActions.clear(); + CONTAINER->unsetFullscreen(); + WindowOpenGLDisplayPlugin::deactivate(); } diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h index b0f0414de2..2009c32e1c 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h @@ -17,10 +17,12 @@ public: virtual bool isSupported() const override final; virtual void activate() override; + virtual void deactivate() override; virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const override; virtual glm::mat4 getEyePose(Eye eye) const override; protected: + void updateScreen(); float _ipd{ 0.064f }; }; diff --git a/libraries/plugins/src/plugins/Forward.h b/libraries/plugins/src/plugins/Forward.h new file mode 100644 index 0000000000..1a9298d13c --- /dev/null +++ b/libraries/plugins/src/plugins/Forward.h @@ -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 +#include +#include + +class DisplayPlugin; +class InputPlugin; +class Plugin; +class PluginContainer; +class PluginManager; + +using DisplayPluginPointer = QSharedPointer; +using DisplayPluginList = QVector; +using InputPluginPointer = QSharedPointer; +using InputPluginList = QVector; diff --git a/libraries/plugins/src/plugins/Plugin.cpp b/libraries/plugins/src/plugins/Plugin.cpp index e0cacda474..ffcc682ebd 100644 --- a/libraries/plugins/src/plugins/Plugin.cpp +++ b/libraries/plugins/src/plugins/Plugin.cpp @@ -1,3 +1,10 @@ +// +// Created by Bradley Austin Davis on 2015/08/08 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// #include "Plugin.h" PluginContainer* Plugin::CONTAINER{ nullptr }; diff --git a/libraries/plugins/src/plugins/Plugin.h b/libraries/plugins/src/plugins/Plugin.h index a2edbc8236..eac355b47d 100644 --- a/libraries/plugins/src/plugins/Plugin.h +++ b/libraries/plugins/src/plugins/Plugin.h @@ -1,9 +1,16 @@ +// +// Created by Bradley Austin Davis on 2015/08/08 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// #pragma once #include #include -class PluginContainer; +#include "Forward.h" class Plugin : public QObject { public: diff --git a/libraries/plugins/src/plugins/PluginContainer.h b/libraries/plugins/src/plugins/PluginContainer.h index 7f6346a181..c2cba23392 100644 --- a/libraries/plugins/src/plugins/PluginContainer.h +++ b/libraries/plugins/src/plugins/PluginContainer.h @@ -7,9 +7,10 @@ // #pragma once -#include #include +#include +class QAction; class QGLWidget; class QScreen; @@ -18,11 +19,11 @@ public: PluginContainer(); virtual void addMenu(const QString& menuName) = 0; virtual void removeMenu(const QString& menuName) = 0; - virtual void addMenuItem(const QString& path, const QString& name, std::function onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") = 0; + virtual QAction* addMenuItem(const QString& path, const QString& name, std::function onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") = 0; virtual void removeMenuItem(const QString& menuName, const QString& menuItem) = 0; virtual bool isOptionChecked(const QString& name) = 0; virtual void setIsOptionChecked(const QString& path, bool checked) = 0; - virtual void setFullscreen(const QScreen* targetScreen) = 0; + virtual void setFullscreen(const QScreen* targetScreen, bool hideMenu = false) = 0; virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) = 0; virtual void showDisplayPluginsTools() = 0; virtual QGLWidget* getPrimarySurface() = 0; diff --git a/libraries/plugins/src/plugins/PluginManager.cpp b/libraries/plugins/src/plugins/PluginManager.cpp index ffee9905a0..3a71700c9e 100644 --- a/libraries/plugins/src/plugins/PluginManager.cpp +++ b/libraries/plugins/src/plugins/PluginManager.cpp @@ -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* PluginManager::getInstance() { static PluginManager _manager; return &_manager; diff --git a/libraries/plugins/src/plugins/PluginManager.h b/libraries/plugins/src/plugins/PluginManager.h index 88dba3366e..b7ec453814 100644 --- a/libraries/plugins/src/plugins/PluginManager.h +++ b/libraries/plugins/src/plugins/PluginManager.h @@ -1,17 +1,13 @@ +// +// Created by Bradley Austin Davis on 2015/08/08 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// #pragma once -#include "Plugin.h" -#include -#include -#include - -class DisplayPlugin; -class InputPlugin; - -using DisplayPluginPointer = QSharedPointer; -using DisplayPluginList = QVector; -using InputPluginPointer = QSharedPointer; -using InputPluginList = QVector; +#include "Forward.h" class PluginManager : public QObject { public: