From ca4b6d2ce2b52aaaae496f16ded8752b311ae6c8 Mon Sep 17 00:00:00 2001 From: Joakim Kilby Date: Wed, 17 May 2023 22:02:47 +0200 Subject: [PATCH] Fix viewport & window issues (#2630) * Add support for multiple windows & viewports With correct rendering of both Scene and Overlays/GUI. GUI is restricted to either the first window or any other windows tagged with "GUI", overlays are drawn on all windows/viewports. Closes #2542 and #1645 --- apps/OpenSpace/main.cpp | 94 +++++++++++++++++----- include/openspace/engine/openspaceengine.h | 1 + include/openspace/engine/windowdelegate.h | 11 +++ modules/cefwebgui/cefwebguimodule.cpp | 7 +- modules/webbrowser/src/browserinstance.cpp | 2 +- modules/webbrowser/src/eventhandler.cpp | 2 + src/engine/openspaceengine.cpp | 26 +++--- src/rendering/loadingscreen.cpp | 2 +- src/rendering/renderengine.cpp | 4 +- 9 files changed, 112 insertions(+), 37 deletions(-) diff --git a/apps/OpenSpace/main.cpp b/apps/OpenSpace/main.cpp index 3cc1e87ab5..634eaba8d5 100644 --- a/apps/OpenSpace/main.cpp +++ b/apps/OpenSpace/main.cpp @@ -264,12 +264,12 @@ void checkJoystickStatus() { } bool isGuiWindow(sgct::Window* window) { - if (Engine::instance().windows().size() == 1) { - // If we only have one window, assume it's also the GUI window. - // It might not have been given the 'GUI' tag - return true; + if (global::windowDelegate->hasGuiWindow()) { + return window->hasTag("GUI"); } - return window->hasTag("GUI"); + + const sgct::Window* first = Engine::instance().windows().front().get(); + return window->id() == first->id(); } // @@ -742,13 +742,6 @@ void setSgctDelegateFunctions() { sgctDelegate.currentSubwindowSize = []() { ZoneScoped; - if (currentWindow->viewports().size() > 1) { - const Viewport& viewport = *currentWindow->viewports().front(); - return glm::ivec2( - currentWindow->resolution().x * viewport.size().x, - currentWindow->resolution().y * viewport.size().y - ); - } switch (currentWindow->stereoMode()) { case Window::StereoMode::SideBySide: case Window::StereoMode::SideBySideInverted: @@ -764,26 +757,20 @@ void setSgctDelegateFunctions() { ); default: return glm::ivec2( - currentWindow->resolution().x, - currentWindow->resolution().y + currentWindow->resolution().x * currentViewport->size().x, + currentWindow->resolution().y * currentViewport->size().y ); } }; sgctDelegate.currentDrawBufferResolution = []() { ZoneScoped; - Viewport* viewport = currentWindow->viewports().front().get(); + const Viewport* viewport = dynamic_cast(currentViewport); if (viewport != nullptr) { if (viewport->hasSubViewports() && viewport->nonLinearProjection()) { ivec2 dim = viewport->nonLinearProjection()->cubemapResolution(); return glm::ivec2(dim.x, dim.y); } - else if (currentWindow->viewports().size() > 1) { - // @TODO (abock, 2020-04-09) This should probably be based on the current - // viewport? - ivec2 dim = currentWindow->finalFBODimensions(); - return glm::ivec2(dim.x * viewport->size().x, dim.y * viewport->size().y); - } else { ivec2 dim = currentWindow->finalFBODimensions(); return glm::ivec2(dim.x, dim.y); @@ -800,12 +787,43 @@ void setSgctDelegateFunctions() { } return glm::ivec2(-1, -1); }; + sgctDelegate.currentViewportResolution = []() { + ZoneScoped; + + if (currentViewport != nullptr) { + ivec2 res = currentWindow->resolution(); + vec2 size = currentViewport->size(); + return glm::ivec2(size.x * res.x, size.y * res.y); + } + return glm::ivec2(-1, -1); + }; sgctDelegate.dpiScaling = []() { ZoneScoped; vec2 scale = currentWindow->scale(); return glm::vec2(scale.x, scale.y); }; + sgctDelegate.firstWindowResolution = []() { + ZoneScoped; + sgct::Window* window = Engine::instance().windows().front().get(); + return glm::ivec2(window->resolution().x, window->resolution().y); + }; + sgctDelegate.guiWindowResolution = []() { + ZoneScoped; + const Window* guiWindow = nullptr; + for (const std::unique_ptr& window : Engine::instance().windows()) { + if (window->hasTag("GUI")) { + guiWindow = window.get(); + break; + } + } + + if (!guiWindow) { + guiWindow = Engine::instance().windows().front().get(); + } + + return glm::ivec2(guiWindow->resolution().x, guiWindow->resolution().y); + }; sgctDelegate.osDpiScaling = []() { ZoneScoped; @@ -901,6 +919,11 @@ void setSgctDelegateFunctions() { return currentWindow->id(); }; + sgctDelegate.firstWindowId = []() { + ZoneScoped; + + return Engine::instance().windows().front()->id(); + }; sgctDelegate.openGLProcedureAddress = [](const char* func) { ZoneScoped; @@ -959,6 +982,35 @@ void setSgctDelegateFunctions() { sgctDelegate.currentNode = []() { return ClusterManager::instance().thisNodeId(); }; + sgctDelegate.mousePositionViewportRelative = [](glm::vec2 mousePosition) { + for (const std::unique_ptr& window : Engine::instance().windows()) { + if (isGuiWindow(window.get())) { + sgct::ivec2 res = window->resolution(); + for (const std::unique_ptr& viewport : window->viewports()) { + sgct::vec2 pos = viewport->position(); + sgct::vec2 size = viewport->size(); + glm::vec4 bounds = glm::vec4( + pos.x * res.x, + (1.0 - pos.y - size.y) * res.y, + (pos.x + size.x) * res.x, + (1.0 - pos.y) * res.y + ); + + if ( + (mousePosition.x >= bounds.x && mousePosition.x <= bounds.z) && + (mousePosition.y >= bounds.y && mousePosition.y <= bounds.w) + ) { + return glm::vec2( + res.x * (mousePosition.x - bounds.x) / (bounds.z - bounds.x), + res.y * (mousePosition.y - bounds.y) / (bounds.w - bounds.y) + ); + } + } + } + } + + return mousePosition; + }; } void checkCommandLineForSettings(int& argc, char** argv, bool& hasSGCT, bool& hasProfile, diff --git a/include/openspace/engine/openspaceengine.h b/include/openspace/engine/openspaceengine.h index 16c5fa23ac..b21c04cba3 100644 --- a/include/openspace/engine/openspaceengine.h +++ b/include/openspace/engine/openspaceengine.h @@ -89,6 +89,7 @@ public: void deinitializeGL(); void preSynchronization(); void postSynchronizationPreDraw(); + void viewportChanged(); void render(const glm::mat4& sceneMatrix, const glm::mat4& viewMatrix, const glm::mat4& projectionMatrix); void drawOverlays(); diff --git a/include/openspace/engine/windowdelegate.h b/include/openspace/engine/windowdelegate.h index af290a293e..13458e6e8c 100644 --- a/include/openspace/engine/windowdelegate.h +++ b/include/openspace/engine/windowdelegate.h @@ -62,8 +62,14 @@ struct WindowDelegate { glm::ivec2 (*currentViewportSize)() = []() { return glm::ivec2(0); }; + glm::ivec2(*currentViewportResolution)() = []() { return glm::ivec2(0); }; + glm::vec2 (*dpiScaling)() = []() { return glm::vec2(1.f); }; + glm::ivec2(*firstWindowResolution)() = []() { return glm::ivec2(0); }; + + glm::ivec2(*guiWindowResolution)() = []() { return glm::ivec2(0); }; + float (*osDpiScaling)() = []() { return 1.f; }; bool (*hasGuiWindow)() = []() { return false; }; @@ -89,6 +95,8 @@ struct WindowDelegate { int (*currentWindowId)() = []() { return 0; }; + int (*firstWindowId)() = []() { return 0; }; + double (*getHorizFieldOfView)() = []() { return 0.0; }; void (*setHorizFieldOfView)(float hFovDeg) = [](float) { }; @@ -113,6 +121,9 @@ struct WindowDelegate { int (*numberOfNodes)() = []() { return 0; }; int (*currentNode)() = []() { return 0; }; + + glm::vec2 (*mousePositionViewportRelative)(glm::vec2 mousePosition) = + [](glm::vec2) { return glm::vec2(0); }; }; } // namespace openspace diff --git a/modules/cefwebgui/cefwebguimodule.cpp b/modules/cefwebgui/cefwebguimodule.cpp index 9d10c38408..193a12a242 100644 --- a/modules/cefwebgui/cefwebguimodule.cpp +++ b/modules/cefwebgui/cefwebguimodule.cpp @@ -111,7 +111,7 @@ void CefWebGuiModule::startOrStopGui() { ); _instance->initialize(); _instance->reshape(static_cast( - static_cast(global::windowDelegate->currentSubwindowSize()) * + static_cast(global::windowDelegate->guiWindowResolution()) * global::windowDelegate->dpiScaling() )); if (!_url.value().empty()) { @@ -226,12 +226,13 @@ void CefWebGuiModule::internalInitialize(const ghoul::Dictionary& configuration) const bool isGuiWindow = global::windowDelegate->hasGuiWindow() ? global::windowDelegate->isGuiWindow() : - true; + global::windowDelegate->currentWindowId() + == global::windowDelegate->firstWindowId(); const bool isMaster = global::windowDelegate->isMaster(); if (isGuiWindow && isMaster && _instance) { if (global::windowDelegate->windowHasResized() || _instance->_shouldReshape) { - glm::ivec2 csws = global::windowDelegate->currentSubwindowSize(); + glm::ivec2 csws = global::windowDelegate->guiWindowResolution(); _instance->reshape(static_cast( static_cast(csws) * global::windowDelegate->dpiScaling() )); diff --git a/modules/webbrowser/src/browserinstance.cpp b/modules/webbrowser/src/browserinstance.cpp index c52d7877ce..45e2d4575e 100644 --- a/modules/webbrowser/src/browserinstance.cpp +++ b/modules/webbrowser/src/browserinstance.cpp @@ -79,7 +79,7 @@ BrowserInstance::~BrowserInstance() { void BrowserInstance::initialize() { reshape(static_cast( - static_cast(global::windowDelegate->currentSubwindowSize()) * + static_cast(global::windowDelegate->guiWindowResolution()) * global::windowDelegate->dpiScaling() )); _isInitialized = true; diff --git a/modules/webbrowser/src/eventhandler.cpp b/modules/webbrowser/src/eventhandler.cpp index 8e64179863..e6ca44a97e 100644 --- a/modules/webbrowser/src/eventhandler.cpp +++ b/modules/webbrowser/src/eventhandler.cpp @@ -383,6 +383,8 @@ bool EventHandler::mousePositionCallback(double x, double y) { const glm::vec2 dpiScaling = global::windowDelegate->dpiScaling(); _mousePosition.x = floor(static_cast(x) * dpiScaling.x); _mousePosition.y = floor(static_cast(y) * dpiScaling.y); + _mousePosition = + global::windowDelegate->mousePositionViewportRelative(_mousePosition); _browserInstance->sendMouseMoveEvent(mouseEvent()); global::interactionMonitor->markInteraction(); diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 4321b72ecd..4c52cba524 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -938,6 +938,8 @@ void OpenSpaceEngine::deinitializeGL() { LTRACE("OpenSpaceEngine::deinitializeGL(begin)"); + viewportChanged(); + // We want to render an image informing the user that we are shutting down global::renderEngine->renderEndscreen(); global::windowDelegate->swapBuffer(); @@ -1171,15 +1173,6 @@ void OpenSpaceEngine::postSynchronizationPreDraw() { bool master = global::windowDelegate->isMaster(); global::syncEngine->postSynchronization(SyncEngine::IsMaster(master)); - // This probably doesn't have to be done here every frame, but doing it earlier gives - // weird results when using side_by_side stereo --- abock - using FR = ghoul::fontrendering::FontRenderer; - FR::defaultRenderer().setFramebufferSize(global::renderEngine->fontResolution()); - - FR::defaultProjectionRenderer().setFramebufferSize( - global::renderEngine->renderingResolution() - ); - if (_shutdown.inShutdown) { if (_shutdown.timer <= 0.f) { global::eventEngine->publishEvent( @@ -1231,6 +1224,17 @@ void OpenSpaceEngine::postSynchronizationPreDraw() { LTRACE("OpenSpaceEngine::postSynchronizationPreDraw(end)"); } +void OpenSpaceEngine::viewportChanged() { + // Needs to be updated since each render call potentially targets a different + // window and/or viewport + using FR = ghoul::fontrendering::FontRenderer; + FR::defaultRenderer().setFramebufferSize(global::renderEngine->fontResolution()); + + FR::defaultProjectionRenderer().setFramebufferSize( + global::renderEngine->renderingResolution() + ); +} + void OpenSpaceEngine::render(const glm::mat4& sceneMatrix, const glm::mat4& viewMatrix, const glm::mat4& projectionMatrix) { @@ -1238,6 +1242,8 @@ void OpenSpaceEngine::render(const glm::mat4& sceneMatrix, const glm::mat4& view TracyGpuZone("Render"); LTRACE("OpenSpaceEngine::render(begin)"); + viewportChanged(); + global::renderEngine->render(sceneMatrix, viewMatrix, projectionMatrix); for (const std::function& func : *global::callback::render) { @@ -1254,6 +1260,8 @@ void OpenSpaceEngine::drawOverlays() { TracyGpuZone("Draw2D"); LTRACE("OpenSpaceEngine::drawOverlays(begin)"); + viewportChanged(); + const bool isGuiWindow = global::windowDelegate->hasGuiWindow() ? global::windowDelegate->isGuiWindow() : diff --git a/src/rendering/loadingscreen.cpp b/src/rendering/loadingscreen.cpp index 73ceb3841c..ba8672113f 100644 --- a/src/rendering/loadingscreen.cpp +++ b/src/rendering/loadingscreen.cpp @@ -165,7 +165,7 @@ void LoadingScreen::render() { const glm::vec2 dpiScaling = global::windowDelegate->dpiScaling(); const glm::ivec2 res = - glm::vec2(global::windowDelegate->currentSubwindowSize()) * dpiScaling; + glm::vec2(global::windowDelegate->firstWindowResolution()) * dpiScaling; float screenAspectRatio = static_cast(res.x) / static_cast(res.y); diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index d0dacbc6aa..b119b65ea1 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -615,7 +615,7 @@ glm::ivec2 RenderEngine::fontResolution() const { return global::windowDelegate->currentViewportSize(); } else { - return global::windowDelegate->currentSubwindowSize(); + return global::windowDelegate->currentViewportResolution(); } } @@ -835,7 +835,7 @@ void RenderEngine::renderEndscreen() { const glm::vec2 dpiScaling = global::windowDelegate->dpiScaling(); const glm::ivec2 res = - glm::vec2(global::windowDelegate->currentSubwindowSize()) / dpiScaling; + glm::vec2(global::windowDelegate->firstWindowResolution()) / dpiScaling; glViewport(0, 0, res.x, res.y); constexpr std::string_view Text = "Shutting down";