From 0f34b055124e01eafcad915e0a8b65d8990dffbf Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Sat, 8 Jul 2017 12:40:52 -0400 Subject: [PATCH] Increasing warning level --- Jenkinsfile | 160 +- apps/OpenSpace/main.cpp | 1316 ++++---- ext/ghoul | 2 +- modules/globebrowsing/other/pixelbuffer.cpp | 168 +- modules/globebrowsing/other/pixelbuffer.h | 280 +- .../other/pixelbuffercontainer.h | 230 +- .../other/pixelbuffercontainer.inl | 260 +- .../gdalrawtiledatareader.cpp | 678 ++-- .../tile/rawtiledatareader/tiledatatype.cpp | 1228 +++---- .../tile/rawtiledatareader/tiledatatype.h | 122 +- modules/globebrowsing/tile/textureformat.h | 86 +- .../tile/tiletextureinitdata.cpp | 304 +- .../globebrowsing/tile/tiletextureinitdata.h | 182 +- .../newhorizons/util/projectioncomponent.cpp | 1900 +++++------ modules/space/rendering/renderableplanet.cpp | 1126 +++---- modules/volume/rawvolumewriter.h | 114 +- src/engine/openspaceengine.cpp | 2892 ++++++++--------- src/engine/wrapper/sgctwindowwrapper.cpp | 496 +-- src/rendering/framebufferrenderer.cpp | 1056 +++--- src/util/syncbuffer.cpp | 124 +- support/cmake/support_macros.cmake | 1098 ++++--- 21 files changed, 6926 insertions(+), 6896 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 1985a6a989..8454bd82fc 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,80 +1,80 @@ -def modules = [ - "base", - "debugging", - "fieldlines", - "galaxy", - "globebrowsing", - "iswa", - "kameleon", - "kameleonvolume", - "multiresvolume", - "newhorizons", - "onscreengui", - "space", - "toyvolume", - "volume" -]; - -def flags = "-DGHOUL_USE_DEVIL=OFF " - -for (module in modules) { - flags += "-DOPENSPACE_OPENSPACE_MODULE_" + module.toUpperCase() + "=ON " -} - -echo flags - -stage('Build') { - parallel linux: { - node('linux') { - timeout(time: 30, unit: 'MINUTES') { - checkout scm - sh 'git submodule update --init --recursive' - sh ''' - mkdir -p build - cd build - cmake .. ''' + - flags + ''' .. - make - ''' - } - } - }, - windows: { - node('windows') { - timeout(time: 30, unit: 'MINUTES') { - checkout scm - bat ''' - git submodule update --init --recursive - if not exist "build" mkdir "build" - cd build - cmake -G "Visual Studio 15 2017 Win64" .. ''' + - flags + ''' .. - msbuild.exe OpenSpace.sln /nologo /verbosity:minimal /m:2 /p:Configuration=Debug - ''' - } - } - }, - osx: { - node('osx') { - timeout(time: 30, unit: 'MINUTES') { - checkout scm - sh 'git submodule update --init --recursive' - sh ''' - export PATH=${PATH}:/usr/local/bin:/Applications/CMake.app/Contents/bin - export CMAKE_BUILD_TOOL=/Applications/CMake.app/Contents/bin/CMake - srcDir=$PWD - if [ ! -d ${srcDir} ]; then - mkdir ${srcDir} - fi - if [ ! -d ${srcDir}/build ]; then - mkdir ${srcDir}/build - fi - cd ${srcDir}/build - /Applications/CMake.app/Contents/bin/cmake -G Xcode -D NASM=/usr/local/bin/nasm ${srcDir} .. ''' + - flags + ''' - xcodebuild -quiet - ''' - } - } - } -} +def modules = [ + "base", + "debugging", + "fieldlines", + "galaxy", + "globebrowsing", + "iswa", + "kameleon", + "kameleonvolume", + "multiresvolume", + "newhorizons", + "onscreengui", + "space", + "toyvolume", + "volume" +]; + +def flags = "-DGHOUL_USE_DEVIL=OFF " + +for (module in modules) { + flags += "-DOPENSPACE_OPENSPACE_MODULE_" + module.toUpperCase() + "=ON " +} + +echo flags + +stage('Build') { + parallel linux: { + node('linux') { + timeout(time: 30, unit: 'MINUTES') { + checkout scm + sh 'git submodule update --init --recursive' + sh ''' + mkdir -p build + cd build + cmake .. ''' + + flags + ''' .. + make + ''' + } + } + }, + windows: { + node('windows') { + timeout(time: 30, unit: 'MINUTES') { + checkout scm + bat ''' + git submodule update --init --recursive + if not exist "build" mkdir "build" + cd build + cmake -G "Visual Studio 15 2017 Win64" .. ''' + + flags + ''' .. + msbuild.exe OpenSpace.sln /nologo /verbosity:minimal /m:2 /p:Configuration=Debug + ''' + } + } + }, + osx: { + node('osx') { + timeout(time: 30, unit: 'MINUTES') { + checkout scm + sh 'git submodule update --init --recursive' + sh ''' + export PATH=${PATH}:/usr/local/bin:/Applications/CMake.app/Contents/bin + export CMAKE_BUILD_TOOL=/Applications/CMake.app/Contents/bin/CMake + srcDir=$PWD + if [ ! -d ${srcDir} ]; then + mkdir ${srcDir} + fi + if [ ! -d ${srcDir}/build ]; then + mkdir ${srcDir}/build + fi + cd ${srcDir}/build + /Applications/CMake.app/Contents/bin/cmake -G Xcode -D NASM=/usr/local/bin/nasm ${srcDir} .. ''' + + flags + ''' + xcodebuild -quiet + ''' + } + } + } +} diff --git a/apps/OpenSpace/main.cpp b/apps/OpenSpace/main.cpp index 6ee55bb774..c280bef68e 100644 --- a/apps/OpenSpace/main.cpp +++ b/apps/OpenSpace/main.cpp @@ -1,658 +1,658 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2017 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#include -#include -#include - -#include -#include -//#include - -#include - -#ifdef WIN32 - -#include - -#include -#include - -#include - -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning (push) -#pragma warning (disable : 4091) -#include -#pragma warning (pop) -#endif // _MSC_VER - -#endif // WIN32 - -#ifdef OPENVR_SUPPORT -#include -#endif // OPENVR_SUPPORT - -#ifdef OPENSPACE_HAS_SPOUT -#include "SpoutLibrary.h" -#endif // OPENSPACE_HAS_SPOUT - - -#define DEVELOPER_MODE - -namespace { - -const char* _loggerCat = "main"; -sgct::Engine* SgctEngine; - -const char* OpenVRTag = "OpenVR"; -const char* SpoutTag = "Spout"; - -#ifdef WIN32 - -LONG WINAPI generateMiniDump(EXCEPTION_POINTERS* exceptionPointers) { - SYSTEMTIME stLocalTime; - GetLocalTime(&stLocalTime); - - - LFATAL("Printing Stack Trace that lead to the crash:"); - std::vector stackTrace = ghoul::stackTrace(); - for (const std::string& s : stackTrace) { - LINFO(s); - } - - std::string dumpFile = fmt::format( - "OpenSpace_{}_{}_{}-{}-{}-{}-{}-{}-{}--{}--{}.dmp", - openspace::OPENSPACE_VERSION_MAJOR, - openspace::OPENSPACE_VERSION_MINOR, - openspace::OPENSPACE_VERSION_PATCH, - stLocalTime.wYear, - stLocalTime.wMonth, - stLocalTime.wDay, - stLocalTime.wHour, - stLocalTime.wMinute, - stLocalTime.wSecond, - GetCurrentProcessId(), - GetCurrentThreadId() - ); - - LINFO("Creating dump file: " << dumpFile); - - HANDLE hDumpFile = CreateFile( - dumpFile.c_str(), - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_WRITE | FILE_SHARE_READ, - 0, - CREATE_ALWAYS, - 0, - 0 - ); - - MINIDUMP_EXCEPTION_INFORMATION exceptionParameter; - exceptionParameter.ThreadId = GetCurrentThreadId(); - exceptionParameter.ExceptionPointers = exceptionPointers; - exceptionParameter.ClientPointers = TRUE; - - BOOL success = MiniDumpWriteDump( - GetCurrentProcess(), - GetCurrentProcessId(), - hDumpFile, - MiniDumpWithDataSegs, - &exceptionParameter, - nullptr, - nullptr - ); - - if (success) { - LINFO("Created successfully"); - } - else { - LERROR("Dumpfile created unsuccessfully"); - } - - return EXCEPTION_EXECUTE_HANDLER; -} - -#endif // WIN32 - -#ifdef OPENVR_SUPPORT -sgct::SGCTWindow* FirstOpenVRWindow = nullptr; -#endif - -#ifdef OPENSPACE_HAS_SPOUT -/** - * This struct stores all information about a single render window. Depending on the - * frame setup, each window can be mono or stereo, the information of which is stored in - * the \c leftOrMain and \c right members respectively. - */ -struct SpoutWindow { - struct SpoutData { - SPOUTHANDLE handle = nullptr; - bool initialized = false; - }; - - /// The left framebuffer (or main, if there is no stereo rendering) - SpoutData leftOrMain; - - /// The right framebuffer - SpoutData right; - - /// The window ID of this windows - size_t windowId = size_t(-1); -}; - -/// The list of all windows with spout senders -std::vector SpoutWindows; - -#endif // OPENSPACE_HAS_SPOUT - - - -std::pair supportedOpenGLVersion() { - // Just create a window in order to retrieve the available OpenGL version before we - // create the real window - glfwInit(); - - // On OS X we need to explicitly set the version and specify that we are using CORE - // profile to be able to use glGetIntegerv(GL_MAJOR_VERSION, &major) and - // glGetIntegerv(GL_MINOR_VERSION, &minor) explicitly setting to OGL 3.3 CORE works - // since all Mac's now support at least 3.3 -#ifdef __APPLE__ - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); -#endif - - glfwWindowHint(GLFW_VISIBLE, GL_FALSE); - - // By creating an offscreen window, the user will not know that we created this window - GLFWwindow* offscreen = glfwCreateWindow(128, 128, "", nullptr, nullptr); - glfwMakeContextCurrent(offscreen); - - // Get the OpenGL version - int major, minor; - glGetIntegerv(GL_MAJOR_VERSION, &major); - glGetIntegerv(GL_MINOR_VERSION, &minor); - - // And get rid of the window again - glfwDestroyWindow(offscreen); - glfwWindowHint(GLFW_VISIBLE, GL_TRUE); - - return { major, minor }; -} - -void mainInitFunc() { - LTRACE("main::mainInitFunc(begin)"); - - LDEBUG("Initializing OpenSpace Engine started"); - OsEng.initialize(); - LDEBUG("Initializing OpenSpace Engine finished"); - - LDEBUG("Initializing OpenGL in OpenSpace Engine started"); - OsEng.initializeGL(); - LDEBUG("Initializing OpenGL in OpenSpace Engine finished"); - - // Find if we have at least one OpenVR window - // Save reference to first OpenVR window, which is the one we will copy to the HMD. - for (size_t i = 0; i < SgctEngine->getNumberOfWindows(); ++i) { - if (SgctEngine->getWindowPtr(i)->checkIfTagExists(OpenVRTag)) { -#ifdef OPENVR_SUPPORT - FirstOpenVRWindow = SgctEngine->getWindowPtr(i); - - // If we have an OpenVRWindow, initialize OpenVR. - sgct::SGCTOpenVR::initialize( - SgctEngine->getNearClippingPlane(), SgctEngine->getFarClippingPlane() - ); -#else - LWARNING( - "OpenVR was requested, but OpenSpace was compiled without VR support." - ); -#endif - - break; - } - } - - // Set the clear color for all non-linear projection viewports - // @CLEANUP: Why is this necessary? We can set the clear color in the configuration - // files --- abock - const size_t nWindows = SgctEngine->getNumberOfWindows(); - for (size_t i = 0; i < nWindows; ++i) { - sgct::SGCTWindow* w = SgctEngine->getWindowPtr(i); - const size_t nViewports = w->getNumberOfViewports(); - for (size_t j = 0; j < nViewports; ++j) { - sgct_core::Viewport* v = w->getViewport(j); - ghoul_assert(v != nullptr, "Number of reported viewports was incorrect"); - sgct_core::NonLinearProjection* p = v->getNonLinearProjectionPtr(); - if (p) { - p->setClearColor(glm::vec4(0.f, 0.f, 0.f, 1.f)); - } - } - } - - for (size_t i = 0; i < nWindows; ++i) { - const sgct::SGCTWindow* windowPtr = SgctEngine->getWindowPtr(i); - - if (!windowPtr->checkIfTagExists(SpoutTag)) { - continue; - } - -#ifdef OPENSPACE_HAS_SPOUT - SpoutWindow w; - - w.windowId = i; - - const sgct::SGCTWindow::StereoMode sm = windowPtr->getStereoMode(); - const bool hasStereo = - (sm != sgct::SGCTWindow::No_Stereo) && - (sm < sgct::SGCTWindow::Side_By_Side_Stereo); - - if (hasStereo) { - SpoutWindow::SpoutData& left = w.leftOrMain; - left.handle = GetSpout(); - left.initialized = left.handle->CreateSender( - (windowPtr->getName() + "_left").c_str(), - windowPtr->getXFramebufferResolution(), - windowPtr->getYFramebufferResolution() - ); - - SpoutWindow::SpoutData& right = w.right; - right.handle = GetSpout(); - right.initialized = right.handle->CreateSender( - (windowPtr->getName() + "_right").c_str(), - windowPtr->getXFramebufferResolution(), - windowPtr->getYFramebufferResolution() - ); - } - else { - SpoutWindow::SpoutData& main = w.leftOrMain; - main.handle = GetSpout(); - main.initialized = main.handle->CreateSender( - windowPtr->getName().c_str(), - windowPtr->getXFramebufferResolution(), - windowPtr->getYFramebufferResolution() - ); - } - - SpoutWindows.push_back(std::move(w)); -#else - LWARNING( - "Spout was requested, but OpenSpace was compiled without Spout support." - ); - -#endif // OPENSPACE_HAS_SPOUT - } - LTRACE("main::mainInitFunc(end)"); -} - -void mainPreSyncFunc() { - LTRACE("main::mainPreSyncFunc(begin)"); - OsEng.setRunTime(sgct::Engine::getTime()); - OsEng.preSynchronization(); - LTRACE("main::mainPreSyncFunc(end)"); -} - -void mainPostSyncPreDrawFunc() { - LTRACE("main::postSynchronizationPreDraw(begin)"); - OsEng.postSynchronizationPreDraw(); - -#ifdef OPENVR_SUPPORT - if (FirstOpenVRWindow) { - // Update pose matrices for all tracked OpenVR devices once per frame - sgct::SGCTOpenVR::updatePoses(); - } -#endif // OPENVR_SUPPORT - - LTRACE("main::postSynchronizationPreDraw(end)"); -} - -void mainRenderFunc() { - LTRACE("main::mainRenderFunc(begin)"); - - glm::mat4 viewMatrix = - SgctEngine->getCurrentViewMatrix() * - // User matrix - glm::translate( - glm::mat4(1.f), - SgctEngine->getDefaultUserPtr()->getPos() - ) - ; - - glm::mat4 projectionMatrix = SgctEngine->getCurrentProjectionMatrix(); -#ifdef OPENVR_SUPPORT - bool currentWindowIsHMD = FirstOpenVRWindow == SgctEngine->getCurrentWindowPtr(); - if (sgct::SGCTOpenVR::isHMDActive() && currentWindowIsHMD) { - projectionMatrix = sgct::SGCTOpenVR::getHMDCurrentViewProjectionMatrix( - SgctEngine->getCurrentFrustumMode() - ); - } -#endif - - OsEng.render( - SgctEngine->getModelMatrix(), - viewMatrix, - projectionMatrix - ); - LTRACE("main::mainRenderFunc(end)"); -} - -void mainPostDrawFunc() { - LTRACE("main::mainPostDrawFunc(begin)"); - -#ifdef OPENVR_SUPPORT - if (FirstOpenVRWindow) { - // Copy the first OpenVR window to the HMD - sgct::SGCTOpenVR::copyWindowToHMD(FirstOpenVRWindow); - } -#endif // OPENVR_SUPPORT - - OsEng.postDraw(); - -#ifdef OPENSPACE_HAS_SPOUT - for (const SpoutWindow& w : SpoutWindows) { - sgct::SGCTWindow* window = SgctEngine->getWindowPtr(w.windowId); - if (w.leftOrMain.initialized) { - GLuint texId = window->getFrameBufferTexture(sgct::Engine::LeftEye); - glBindTexture(GL_TEXTURE_2D, texId); - w.leftOrMain.handle->SendTexture( - texId, - GL_TEXTURE_2D, - window->getXFramebufferResolution(), - window->getYFramebufferResolution() - ); - } - - if (w.right.initialized) { - GLuint texId = window->getFrameBufferTexture(sgct::Engine::RightEye); - glBindTexture(GL_TEXTURE_2D, texId); - w.right.handle->SendTexture( - texId, - GL_TEXTURE_2D, - window->getXFramebufferResolution(), - window->getYFramebufferResolution() - ); - } - } - glBindTexture(GL_TEXTURE_2D, 0); -#endif // OPENSPACE_HAS_SPOUT - - LTRACE("main::mainPostDrawFunc(end)"); -} - -void mainExternalControlCallback(const char* receivedChars, int size) { - LTRACE("main::mainExternalControlCallback(begin)"); - if (SgctEngine->isMaster()) { - OsEng.externalControlCallback(receivedChars, size, 0); - } - LTRACE("main::mainExternalControlCallback(end)"); -} - -void mainKeyboardCallback(int key, int, int action, int mods) { - LTRACE("main::mainKeyboardCallback(begin)"); - if (SgctEngine->isMaster()) { - OsEng.keyboardCallback( - openspace::Key(key), - openspace::KeyModifier(mods), - openspace::KeyAction(action) - ); - } - LTRACE("main::mainKeyboardCallback(begin)"); -} - -void mainMouseButtonCallback(int key, int action) { - LTRACE("main::mainMouseButtonCallback(begin)"); - if (SgctEngine->isMaster()) { - OsEng.mouseButtonCallback( - openspace::MouseButton(key), - openspace::MouseAction(action) - ); - } - LTRACE("main::mainMouseButtonCallback(end)"); -} - -void mainMousePosCallback(double x, double y) { - if (SgctEngine->isMaster()) { - OsEng.mousePositionCallback(x, y); - } -} - -void mainMouseScrollCallback(double, double posY) { - LTRACE("main::mainMouseScrollCallback(begin"); - if (SgctEngine->isMaster()) { - OsEng.mouseScrollWheelCallback(posY); - } - LTRACE("main::mainMouseScrollCallback(end)"); -} - -void mainCharCallback(unsigned int codepoint, int mods) { - if (SgctEngine->isMaster()) { - OsEng.charCallback(codepoint, openspace::KeyModifier(mods)); - } -} - -void mainEncodeFun() { - LTRACE("main::mainEncodeFun(begin)"); - OsEng.encode(); - LTRACE("main::mainEncodeFun(end)"); -} - -void mainDecodeFun() { - LTRACE("main::mainDecodeFun(begin)"); - OsEng.decode(); - LTRACE("main::mainDecodeFun(end)"); -} - -void mainLogCallback(const char* msg) { - std::string message = msg; - if (message.empty() || message == ".") { - // We don't want the empty '.' message that SGCT sends while it is waiting for - // connections from other network nodes - return; - } - // Remove the trailing \n that is passed along - LINFOC("SGCT", message.substr(0, message.size() - 1)); -} - -int main_main(int argc, char** argv) { - std::pair glVersion = supportedOpenGLVersion(); - - // Create the OpenSpace engine and get arguments for the SGCT engine - // @CLEANUP: Replace the return valua with throwing an exception --abock - std::vector sgctArguments; - bool requestQuit = false; - openspace::OpenSpaceEngine::create( - argc, argv, - std::make_unique(), - sgctArguments, - requestQuit - ); - - if (requestQuit) { - return EXIT_SUCCESS; - } - - LINFO("Detected OpenGL version: " << glVersion.first << "." << glVersion.second); - - // Create sgct engine c arguments - int newArgc = static_cast(sgctArguments.size()); - - char** newArgv = new char*[newArgc]; - for (int i = 0; i < newArgc; ++i) { - newArgv[i] = const_cast(sgctArguments.at(i).c_str()); - } - - // Need to set this before the creation of the sgct::Engine - sgct::MessageHandler::instance()->setLogToConsole(false); - sgct::MessageHandler::instance()->setShowTime(false); - sgct::MessageHandler::instance()->setLogToCallback(true); - sgct::MessageHandler::instance()->setLogCallback(mainLogCallback); - -#ifdef __APPLE__ - glfwWindowHint(GLFW_STENCIL_BITS, 8); -#endif - - LDEBUG("Creating SGCT Engine"); - SgctEngine = new sgct::Engine(newArgc, newArgv); - - // Deallocate sgct c arguments - delete[] newArgv; - - // Bind functions - SgctEngine->setInitOGLFunction(mainInitFunc); - SgctEngine->setPreSyncFunction(mainPreSyncFunc); - SgctEngine->setPostSyncPreDrawFunction(mainPostSyncPreDrawFunc); - SgctEngine->setDrawFunction(mainRenderFunc); - SgctEngine->setPostDrawFunction(mainPostDrawFunc); - SgctEngine->setKeyboardCallbackFunction(mainKeyboardCallback); - SgctEngine->setMouseButtonCallbackFunction(mainMouseButtonCallback); - SgctEngine->setMousePosCallbackFunction(mainMousePosCallback); - SgctEngine->setMouseScrollCallbackFunction(mainMouseScrollCallback); - SgctEngine->setExternalControlCallback(mainExternalControlCallback); - SgctEngine->setCharCallbackFunction(mainCharCallback); - - // Disable the immediate exit of the application when the ESC key is pressed - SgctEngine->setExitKey(SGCT_KEY_UNKNOWN); - - sgct::MessageHandler::instance()->setNotifyLevel(sgct::MessageHandler::NOTIFY_ALL); - - // Set encode and decode functions - // NOTE: starts synchronizing before init functions - sgct::SharedData::instance()->setEncodeFunction(mainEncodeFun); - sgct::SharedData::instance()->setDecodeFunction(mainDecodeFun); - - // Try to open a window - LDEBUG("Initialize SGCT Engine"); - std::map, sgct::Engine::RunMode> versionMapping = { - { { 3, 3 }, sgct::Engine::RunMode::OpenGL_3_3_Core_Profile }, - { { 4, 0 }, sgct::Engine::RunMode::OpenGL_4_0_Core_Profile }, - { { 4, 1 }, sgct::Engine::RunMode::OpenGL_4_1_Core_Profile }, - { { 4, 2 }, sgct::Engine::RunMode::OpenGL_4_2_Core_Profile }, - { { 4, 3 }, sgct::Engine::RunMode::OpenGL_4_3_Core_Profile }, - { { 4, 4 }, sgct::Engine::RunMode::OpenGL_4_4_Core_Profile }, - { { 4, 5 }, sgct::Engine::RunMode::OpenGL_4_5_Core_Profile } - }; - ghoul_assert( - versionMapping.find(glVersion) != versionMapping.end(), - "Unknown OpenGL version. Missing statement in version mapping map" - ); - - auto cleanup = [&](){ - OsEng.deinitialize(); - - // Clear function bindings to avoid crash after destroying the OpenSpace Engine - sgct::MessageHandler::instance()->setLogToCallback(false); - sgct::MessageHandler::instance()->setLogCallback(nullptr); - - LDEBUG("Destroying OpenSpaceEngine"); - openspace::OpenSpaceEngine::destroy(); - - LDEBUG("Destroying SGCT Engine"); - delete SgctEngine; - - -#ifdef OPENVR_SUPPORT - // Clean up OpenVR - sgct::SGCTOpenVR::shutdown(); -#endif - -#ifdef OPENSPACE_HAS_SPOUT - for (SpoutWindow& w : SpoutWindows) { - if (w.leftOrMain.handle) { - w.leftOrMain.handle->ReleaseReceiver(); - w.leftOrMain.handle->Release(); - } - if (w.right.handle) { - w.right.handle->ReleaseReceiver(); - w.right.handle->Release(); - } - } -#endif // OPENSPACE_HAS_SPOUT - }; - - bool initSuccess = SgctEngine->init(versionMapping[glVersion]); - - if (!initSuccess) { - LFATAL("Initializing failed"); - cleanup(); - return EXIT_FAILURE; - } - - // Main loop - LDEBUG("Starting rendering loop"); - SgctEngine->render(); - LDEBUG("Ending rendering loop"); - - cleanup(); - - // Exit program - exit(EXIT_SUCCESS); -} - -} // namespace - -int main(int argc, char** argv) { -#ifdef WIN32 - SetUnhandledExceptionFilter(generateMiniDump); -#endif // WIN32 - - // If we are working as a developer, we don't want to catch the exceptions in order to - // find the locations where the exceptions are raised. - // If we are not in developer mode, we want to catch and at least log the error before - // dying -#ifdef DEVELOPER_MODE - return main_main(argc, argv); -#else - // We wrap the actual main function in a try catch block so that we can get and print - // some additional information in case an exception is raised - try { - return main_main(argc, argv); - } - catch (const ghoul::RuntimeError& e) { - // Write out all of the information about the exception and flush the logs - LFATALC(e.component, e.message); - LogMgr.flushLogs(); - return EXIT_FAILURE; - } - catch (const ghoul::AssertionException& e) { - // We don't want to catch the assertion exception as we won't be able to add a - // breakpoint for debugging - LFATALC("Assertion failed", e.what()); - throw; - } catch (const std::exception& e) { - LFATALC("Exception", e.what()); - LogMgr.flushLogs(); - return EXIT_FAILURE; - } - catch (...) { - LFATALC("Exception", "Unknown exception"); - LogMgr.flushLogs(); - return EXIT_FAILURE; - } -#endif // DEVELOPER_MODE -} +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include +#include +#include + +#include +#include +//#include + +#include + +#ifdef WIN32 + +#include + +#include +#include + +#include + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable : 4091) +#include +#pragma warning (pop) +#endif // _MSC_VER + +#endif // WIN32 + +#ifdef OPENVR_SUPPORT +#include +#endif // OPENVR_SUPPORT + +#ifdef OPENSPACE_HAS_SPOUT +#include "SpoutLibrary.h" +#endif // OPENSPACE_HAS_SPOUT + + +#define DEVELOPER_MODE + +namespace { + +const char* _loggerCat = "main"; +sgct::Engine* SgctEngine; + +const char* OpenVRTag = "OpenVR"; +const char* SpoutTag = "Spout"; + +#ifdef WIN32 + +LONG WINAPI generateMiniDump(EXCEPTION_POINTERS* exceptionPointers) { + SYSTEMTIME stLocalTime; + GetLocalTime(&stLocalTime); + + + LFATAL("Printing Stack Trace that lead to the crash:"); + std::vector stackTrace = ghoul::stackTrace(); + for (const std::string& s : stackTrace) { + LINFO(s); + } + + std::string dumpFile = fmt::format( + "OpenSpace_{}_{}_{}-{}-{}-{}-{}-{}-{}--{}--{}.dmp", + openspace::OPENSPACE_VERSION_MAJOR, + openspace::OPENSPACE_VERSION_MINOR, + openspace::OPENSPACE_VERSION_PATCH, + stLocalTime.wYear, + stLocalTime.wMonth, + stLocalTime.wDay, + stLocalTime.wHour, + stLocalTime.wMinute, + stLocalTime.wSecond, + GetCurrentProcessId(), + GetCurrentThreadId() + ); + + LINFO("Creating dump file: " << dumpFile); + + HANDLE hDumpFile = CreateFile( + dumpFile.c_str(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_WRITE | FILE_SHARE_READ, + 0, + CREATE_ALWAYS, + 0, + 0 + ); + + MINIDUMP_EXCEPTION_INFORMATION exceptionParameter; + exceptionParameter.ThreadId = GetCurrentThreadId(); + exceptionParameter.ExceptionPointers = exceptionPointers; + exceptionParameter.ClientPointers = TRUE; + + BOOL success = MiniDumpWriteDump( + GetCurrentProcess(), + GetCurrentProcessId(), + hDumpFile, + MiniDumpWithDataSegs, + &exceptionParameter, + nullptr, + nullptr + ); + + if (success) { + LINFO("Created successfully"); + } + else { + LERROR("Dumpfile created unsuccessfully"); + } + + return EXCEPTION_EXECUTE_HANDLER; +} + +#endif // WIN32 + +#ifdef OPENVR_SUPPORT +sgct::SGCTWindow* FirstOpenVRWindow = nullptr; +#endif + +#ifdef OPENSPACE_HAS_SPOUT +/** + * This struct stores all information about a single render window. Depending on the + * frame setup, each window can be mono or stereo, the information of which is stored in + * the \c leftOrMain and \c right members respectively. + */ +struct SpoutWindow { + struct SpoutData { + SPOUTHANDLE handle = nullptr; + bool initialized = false; + }; + + /// The left framebuffer (or main, if there is no stereo rendering) + SpoutData leftOrMain; + + /// The right framebuffer + SpoutData right; + + /// The window ID of this windows + size_t windowId = size_t(-1); +}; + +/// The list of all windows with spout senders +std::vector SpoutWindows; + +#endif // OPENSPACE_HAS_SPOUT + + + +std::pair supportedOpenGLVersion() { + // Just create a window in order to retrieve the available OpenGL version before we + // create the real window + glfwInit(); + + // On OS X we need to explicitly set the version and specify that we are using CORE + // profile to be able to use glGetIntegerv(GL_MAJOR_VERSION, &major) and + // glGetIntegerv(GL_MINOR_VERSION, &minor) explicitly setting to OGL 3.3 CORE works + // since all Mac's now support at least 3.3 +#ifdef __APPLE__ + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); +#endif + + glfwWindowHint(GLFW_VISIBLE, GL_FALSE); + + // By creating an offscreen window, the user will not know that we created this window + GLFWwindow* offscreen = glfwCreateWindow(128, 128, "", nullptr, nullptr); + glfwMakeContextCurrent(offscreen); + + // Get the OpenGL version + int major, minor; + glGetIntegerv(GL_MAJOR_VERSION, &major); + glGetIntegerv(GL_MINOR_VERSION, &minor); + + // And get rid of the window again + glfwDestroyWindow(offscreen); + glfwWindowHint(GLFW_VISIBLE, GL_TRUE); + + return { major, minor }; +} + +void mainInitFunc() { + LTRACE("main::mainInitFunc(begin)"); + + LDEBUG("Initializing OpenSpace Engine started"); + OsEng.initialize(); + LDEBUG("Initializing OpenSpace Engine finished"); + + LDEBUG("Initializing OpenGL in OpenSpace Engine started"); + OsEng.initializeGL(); + LDEBUG("Initializing OpenGL in OpenSpace Engine finished"); + + // Find if we have at least one OpenVR window + // Save reference to first OpenVR window, which is the one we will copy to the HMD. + for (size_t i = 0; i < SgctEngine->getNumberOfWindows(); ++i) { + if (SgctEngine->getWindowPtr(i)->checkIfTagExists(OpenVRTag)) { +#ifdef OPENVR_SUPPORT + FirstOpenVRWindow = SgctEngine->getWindowPtr(i); + + // If we have an OpenVRWindow, initialize OpenVR. + sgct::SGCTOpenVR::initialize( + SgctEngine->getNearClippingPlane(), SgctEngine->getFarClippingPlane() + ); +#else + LWARNING( + "OpenVR was requested, but OpenSpace was compiled without VR support." + ); +#endif + + break; + } + } + + // Set the clear color for all non-linear projection viewports + // @CLEANUP: Why is this necessary? We can set the clear color in the configuration + // files --- abock + const size_t nWindows = SgctEngine->getNumberOfWindows(); + for (size_t i = 0; i < nWindows; ++i) { + sgct::SGCTWindow* w = SgctEngine->getWindowPtr(i); + const size_t nViewports = w->getNumberOfViewports(); + for (size_t j = 0; j < nViewports; ++j) { + sgct_core::Viewport* v = w->getViewport(j); + ghoul_assert(v != nullptr, "Number of reported viewports was incorrect"); + sgct_core::NonLinearProjection* p = v->getNonLinearProjectionPtr(); + if (p) { + p->setClearColor(glm::vec4(0.f, 0.f, 0.f, 1.f)); + } + } + } + + for (size_t i = 0; i < nWindows; ++i) { + const sgct::SGCTWindow* windowPtr = SgctEngine->getWindowPtr(i); + + if (!windowPtr->checkIfTagExists(SpoutTag)) { + continue; + } + +#ifdef OPENSPACE_HAS_SPOUT + SpoutWindow w; + + w.windowId = i; + + const sgct::SGCTWindow::StereoMode sm = windowPtr->getStereoMode(); + const bool hasStereo = + (sm != sgct::SGCTWindow::No_Stereo) && + (sm < sgct::SGCTWindow::Side_By_Side_Stereo); + + if (hasStereo) { + SpoutWindow::SpoutData& left = w.leftOrMain; + left.handle = GetSpout(); + left.initialized = left.handle->CreateSender( + (windowPtr->getName() + "_left").c_str(), + windowPtr->getXFramebufferResolution(), + windowPtr->getYFramebufferResolution() + ); + + SpoutWindow::SpoutData& right = w.right; + right.handle = GetSpout(); + right.initialized = right.handle->CreateSender( + (windowPtr->getName() + "_right").c_str(), + windowPtr->getXFramebufferResolution(), + windowPtr->getYFramebufferResolution() + ); + } + else { + SpoutWindow::SpoutData& main = w.leftOrMain; + main.handle = GetSpout(); + main.initialized = main.handle->CreateSender( + windowPtr->getName().c_str(), + windowPtr->getXFramebufferResolution(), + windowPtr->getYFramebufferResolution() + ); + } + + SpoutWindows.push_back(std::move(w)); +#else + LWARNING( + "Spout was requested, but OpenSpace was compiled without Spout support." + ); + +#endif // OPENSPACE_HAS_SPOUT + } + LTRACE("main::mainInitFunc(end)"); +} + +void mainPreSyncFunc() { + LTRACE("main::mainPreSyncFunc(begin)"); + OsEng.setRunTime(sgct::Engine::getTime()); + OsEng.preSynchronization(); + LTRACE("main::mainPreSyncFunc(end)"); +} + +void mainPostSyncPreDrawFunc() { + LTRACE("main::postSynchronizationPreDraw(begin)"); + OsEng.postSynchronizationPreDraw(); + +#ifdef OPENVR_SUPPORT + if (FirstOpenVRWindow) { + // Update pose matrices for all tracked OpenVR devices once per frame + sgct::SGCTOpenVR::updatePoses(); + } +#endif // OPENVR_SUPPORT + + LTRACE("main::postSynchronizationPreDraw(end)"); +} + +void mainRenderFunc() { + LTRACE("main::mainRenderFunc(begin)"); + + glm::mat4 viewMatrix = + SgctEngine->getCurrentViewMatrix() * + // User matrix + glm::translate( + glm::mat4(1.f), + SgctEngine->getDefaultUserPtr()->getPos() + ) + ; + + glm::mat4 projectionMatrix = SgctEngine->getCurrentProjectionMatrix(); +#ifdef OPENVR_SUPPORT + bool currentWindowIsHMD = FirstOpenVRWindow == SgctEngine->getCurrentWindowPtr(); + if (sgct::SGCTOpenVR::isHMDActive() && currentWindowIsHMD) { + projectionMatrix = sgct::SGCTOpenVR::getHMDCurrentViewProjectionMatrix( + SgctEngine->getCurrentFrustumMode() + ); + } +#endif + + OsEng.render( + SgctEngine->getModelMatrix(), + viewMatrix, + projectionMatrix + ); + LTRACE("main::mainRenderFunc(end)"); +} + +void mainPostDrawFunc() { + LTRACE("main::mainPostDrawFunc(begin)"); + +#ifdef OPENVR_SUPPORT + if (FirstOpenVRWindow) { + // Copy the first OpenVR window to the HMD + sgct::SGCTOpenVR::copyWindowToHMD(FirstOpenVRWindow); + } +#endif // OPENVR_SUPPORT + + OsEng.postDraw(); + +#ifdef OPENSPACE_HAS_SPOUT + for (const SpoutWindow& w : SpoutWindows) { + sgct::SGCTWindow* window = SgctEngine->getWindowPtr(w.windowId); + if (w.leftOrMain.initialized) { + GLuint texId = window->getFrameBufferTexture(sgct::Engine::LeftEye); + glBindTexture(GL_TEXTURE_2D, texId); + w.leftOrMain.handle->SendTexture( + texId, + GL_TEXTURE_2D, + window->getXFramebufferResolution(), + window->getYFramebufferResolution() + ); + } + + if (w.right.initialized) { + GLuint texId = window->getFrameBufferTexture(sgct::Engine::RightEye); + glBindTexture(GL_TEXTURE_2D, texId); + w.right.handle->SendTexture( + texId, + GL_TEXTURE_2D, + window->getXFramebufferResolution(), + window->getYFramebufferResolution() + ); + } + } + glBindTexture(GL_TEXTURE_2D, 0); +#endif // OPENSPACE_HAS_SPOUT + + LTRACE("main::mainPostDrawFunc(end)"); +} + +void mainExternalControlCallback(const char* receivedChars, int size) { + LTRACE("main::mainExternalControlCallback(begin)"); + if (SgctEngine->isMaster()) { + OsEng.externalControlCallback(receivedChars, size, 0); + } + LTRACE("main::mainExternalControlCallback(end)"); +} + +void mainKeyboardCallback(int key, int, int action, int mods) { + LTRACE("main::mainKeyboardCallback(begin)"); + if (SgctEngine->isMaster()) { + OsEng.keyboardCallback( + openspace::Key(key), + openspace::KeyModifier(mods), + openspace::KeyAction(action) + ); + } + LTRACE("main::mainKeyboardCallback(begin)"); +} + +void mainMouseButtonCallback(int key, int action) { + LTRACE("main::mainMouseButtonCallback(begin)"); + if (SgctEngine->isMaster()) { + OsEng.mouseButtonCallback( + openspace::MouseButton(key), + openspace::MouseAction(action) + ); + } + LTRACE("main::mainMouseButtonCallback(end)"); +} + +void mainMousePosCallback(double x, double y) { + if (SgctEngine->isMaster()) { + OsEng.mousePositionCallback(x, y); + } +} + +void mainMouseScrollCallback(double, double posY) { + LTRACE("main::mainMouseScrollCallback(begin"); + if (SgctEngine->isMaster()) { + OsEng.mouseScrollWheelCallback(posY); + } + LTRACE("main::mainMouseScrollCallback(end)"); +} + +void mainCharCallback(unsigned int codepoint, int mods) { + if (SgctEngine->isMaster()) { + OsEng.charCallback(codepoint, openspace::KeyModifier(mods)); + } +} + +void mainEncodeFun() { + LTRACE("main::mainEncodeFun(begin)"); + OsEng.encode(); + LTRACE("main::mainEncodeFun(end)"); +} + +void mainDecodeFun() { + LTRACE("main::mainDecodeFun(begin)"); + OsEng.decode(); + LTRACE("main::mainDecodeFun(end)"); +} + +void mainLogCallback(const char* msg) { + std::string message = msg; + if (message.empty() || message == ".") { + // We don't want the empty '.' message that SGCT sends while it is waiting for + // connections from other network nodes + return; + } + // Remove the trailing \n that is passed along + LINFOC("SGCT", message.substr(0, message.size() - 1)); +} + +int main_main(int argc, char** argv) { + std::pair glVersion = supportedOpenGLVersion(); + + // Create the OpenSpace engine and get arguments for the SGCT engine + // @CLEANUP: Replace the return valua with throwing an exception --abock + std::vector sgctArguments; + bool requestQuit = false; + openspace::OpenSpaceEngine::create( + argc, argv, + std::make_unique(), + sgctArguments, + requestQuit + ); + + if (requestQuit) { + return EXIT_SUCCESS; + } + + LINFO("Detected OpenGL version: " << glVersion.first << "." << glVersion.second); + + // Create sgct engine c arguments + int newArgc = static_cast(sgctArguments.size()); + + char** newArgv = new char*[newArgc]; + for (int i = 0; i < newArgc; ++i) { + newArgv[i] = const_cast(sgctArguments.at(i).c_str()); + } + + // Need to set this before the creation of the sgct::Engine + sgct::MessageHandler::instance()->setLogToConsole(false); + sgct::MessageHandler::instance()->setShowTime(false); + sgct::MessageHandler::instance()->setLogToCallback(true); + sgct::MessageHandler::instance()->setLogCallback(mainLogCallback); + +#ifdef __APPLE__ + glfwWindowHint(GLFW_STENCIL_BITS, 8); +#endif + + LDEBUG("Creating SGCT Engine"); + SgctEngine = new sgct::Engine(newArgc, newArgv); + + // Deallocate sgct c arguments + delete[] newArgv; + + // Bind functions + SgctEngine->setInitOGLFunction(mainInitFunc); + SgctEngine->setPreSyncFunction(mainPreSyncFunc); + SgctEngine->setPostSyncPreDrawFunction(mainPostSyncPreDrawFunc); + SgctEngine->setDrawFunction(mainRenderFunc); + SgctEngine->setPostDrawFunction(mainPostDrawFunc); + SgctEngine->setKeyboardCallbackFunction(mainKeyboardCallback); + SgctEngine->setMouseButtonCallbackFunction(mainMouseButtonCallback); + SgctEngine->setMousePosCallbackFunction(mainMousePosCallback); + SgctEngine->setMouseScrollCallbackFunction(mainMouseScrollCallback); + SgctEngine->setExternalControlCallback(mainExternalControlCallback); + SgctEngine->setCharCallbackFunction(mainCharCallback); + + // Disable the immediate exit of the application when the ESC key is pressed + SgctEngine->setExitKey(SGCT_KEY_UNKNOWN); + + sgct::MessageHandler::instance()->setNotifyLevel(sgct::MessageHandler::NOTIFY_ALL); + + // Set encode and decode functions + // NOTE: starts synchronizing before init functions + sgct::SharedData::instance()->setEncodeFunction(mainEncodeFun); + sgct::SharedData::instance()->setDecodeFunction(mainDecodeFun); + + // Try to open a window + LDEBUG("Initialize SGCT Engine"); + std::map, sgct::Engine::RunMode> versionMapping = { + { { 3, 3 }, sgct::Engine::RunMode::OpenGL_3_3_Core_Profile }, + { { 4, 0 }, sgct::Engine::RunMode::OpenGL_4_0_Core_Profile }, + { { 4, 1 }, sgct::Engine::RunMode::OpenGL_4_1_Core_Profile }, + { { 4, 2 }, sgct::Engine::RunMode::OpenGL_4_2_Core_Profile }, + { { 4, 3 }, sgct::Engine::RunMode::OpenGL_4_3_Core_Profile }, + { { 4, 4 }, sgct::Engine::RunMode::OpenGL_4_4_Core_Profile }, + { { 4, 5 }, sgct::Engine::RunMode::OpenGL_4_5_Core_Profile } + }; + ghoul_assert( + versionMapping.find(glVersion) != versionMapping.end(), + "Unknown OpenGL version. Missing statement in version mapping map" + ); + + auto cleanup = [&](){ + OsEng.deinitialize(); + + // Clear function bindings to avoid crash after destroying the OpenSpace Engine + sgct::MessageHandler::instance()->setLogToCallback(false); + sgct::MessageHandler::instance()->setLogCallback(nullptr); + + LDEBUG("Destroying OpenSpaceEngine"); + openspace::OpenSpaceEngine::destroy(); + + LDEBUG("Destroying SGCT Engine"); + delete SgctEngine; + + +#ifdef OPENVR_SUPPORT + // Clean up OpenVR + sgct::SGCTOpenVR::shutdown(); +#endif + +#ifdef OPENSPACE_HAS_SPOUT + for (SpoutWindow& w : SpoutWindows) { + if (w.leftOrMain.handle) { + w.leftOrMain.handle->ReleaseReceiver(); + w.leftOrMain.handle->Release(); + } + if (w.right.handle) { + w.right.handle->ReleaseReceiver(); + w.right.handle->Release(); + } + } +#endif // OPENSPACE_HAS_SPOUT + }; + + bool initSuccess = SgctEngine->init(versionMapping[glVersion]); + + if (!initSuccess) { + LFATAL("Initializing failed"); + cleanup(); + return EXIT_FAILURE; + } + + // Main loop + LDEBUG("Starting rendering loop"); + SgctEngine->render(); + LDEBUG("Ending rendering loop"); + + cleanup(); + + // Exit program + exit(EXIT_SUCCESS); +} + +} // namespace + +int main(int argc, char** argv) { +#ifdef WIN32 + SetUnhandledExceptionFilter(generateMiniDump); +#endif // WIN32 + + // If we are working as a developer, we don't want to catch the exceptions in order to + // find the locations where the exceptions are raised. + // If we are not in developer mode, we want to catch and at least log the error before + // dying +#ifdef DEVELOPER_MODE + return main_main(argc, argv); +#else + // We wrap the actual main function in a try catch block so that we can get and print + // some additional information in case an exception is raised + try { + return main_main(argc, argv); + } + catch (const ghoul::RuntimeError& e) { + // Write out all of the information about the exception and flush the logs + LFATALC(e.component, e.message); + LogMgr.flushLogs(); + return EXIT_FAILURE; + } + catch (const ghoul::AssertionException& e) { + // We don't want to catch the assertion exception as we won't be able to add a + // breakpoint for debugging + LFATALC("Assertion failed", e.what()); + throw; + } catch (const std::exception& e) { + LFATALC("Exception", e.what()); + LogMgr.flushLogs(); + return EXIT_FAILURE; + } + catch (...) { + LFATALC("Exception", "Unknown exception"); + LogMgr.flushLogs(); + return EXIT_FAILURE; + } +#endif // DEVELOPER_MODE +} diff --git a/ext/ghoul b/ext/ghoul index f852572b77..5b75ba44ca 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit f852572b77742d4fa6c78e73b6f5470633509c8f +Subproject commit 5b75ba44caec9ee798f7a3390dbdcb4870398886 diff --git a/modules/globebrowsing/other/pixelbuffer.cpp b/modules/globebrowsing/other/pixelbuffer.cpp index 22b8264a48..1882419d7b 100644 --- a/modules/globebrowsing/other/pixelbuffer.cpp +++ b/modules/globebrowsing/other/pixelbuffer.cpp @@ -1,84 +1,84 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2017 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#include -#include - -namespace { - const char* _loggerCat = "PixelBuffer"; -}; - -using namespace openspace::globebrowsing; - -PixelBuffer::PixelBuffer(size_t numBytes, Usage usage) - : _numBytes(numBytes) - , _usage(usage) - , _isMapped(false) -{ - glGenBuffers(1, &_id); - bind(); - glBufferData(GL_PIXEL_UNPACK_BUFFER, _numBytes, 0, static_cast(_usage)); - unbind(); -} - -PixelBuffer::~PixelBuffer() { - glDeleteBuffers(1, &_id); -} - -void* PixelBuffer::mapBuffer(Access access) { - void* dataPtr = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, static_cast(access)); - _isMapped = dataPtr ? true : false; - return dataPtr; -} - -void* PixelBuffer::mapBufferRange(GLintptr offset, GLsizeiptr length, BufferAccessMask access) { - void* dataPtr = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, offset, length, access); - _isMapped = dataPtr ? true : false; - return dataPtr; -} - -bool PixelBuffer::unMapBuffer() { - GLboolean success = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); - if (!success) { - LERROR("Unable to unmap pixel buffer, data may be corrupt!"); - } - _isMapped = false; - return success == GL_TRUE; -} - -void PixelBuffer::bind() { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _id); -} - -void PixelBuffer::unbind() { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); -} - -bool PixelBuffer::isMapped() const { - return _isMapped; -} - -PixelBuffer::operator GLuint() const { - return _id; -} +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include +#include + +namespace { + const char* _loggerCat = "PixelBuffer"; +}; + +using namespace openspace::globebrowsing; + +PixelBuffer::PixelBuffer(size_t numBytes, Usage usage) + : _numBytes(numBytes) + , _usage(usage) + , _isMapped(false) +{ + glGenBuffers(1, &_id); + bind(); + glBufferData(GL_PIXEL_UNPACK_BUFFER, _numBytes, 0, static_cast(_usage)); + unbind(); +} + +PixelBuffer::~PixelBuffer() { + glDeleteBuffers(1, &_id); +} + +void* PixelBuffer::mapBuffer(Access access) { + void* dataPtr = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, static_cast(access)); + _isMapped = dataPtr ? true : false; + return dataPtr; +} + +void* PixelBuffer::mapBufferRange(GLintptr offset, GLsizeiptr length, BufferAccessMask access) { + void* dataPtr = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, offset, length, access); + _isMapped = dataPtr ? true : false; + return dataPtr; +} + +bool PixelBuffer::unMapBuffer() { + GLboolean success = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + if (!success) { + LERROR("Unable to unmap pixel buffer, data may be corrupt!"); + } + _isMapped = false; + return success == GL_TRUE; +} + +void PixelBuffer::bind() { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _id); +} + +void PixelBuffer::unbind() { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); +} + +bool PixelBuffer::isMapped() const { + return _isMapped; +} + +PixelBuffer::operator GLuint() const { + return _id; +} diff --git a/modules/globebrowsing/other/pixelbuffer.h b/modules/globebrowsing/other/pixelbuffer.h index 1f9ee4918e..11e8f97811 100644 --- a/modules/globebrowsing/other/pixelbuffer.h +++ b/modules/globebrowsing/other/pixelbuffer.h @@ -1,140 +1,140 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2017 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___PIXEL_BUFFER___H__ -#define __OPENSPACE_MODULE_GLOBEBROWSING___PIXEL_BUFFER___H__ - -#include - -namespace openspace { -namespace globebrowsing { - -/** - * Handles an OpenGL pixel buffer which contains data allocated on the GPU. A simple - * class that wraps the standard functionality of OpenGL pixel buffer objects. Once - * the PixelBuffer is created, data is allocated on the GPU. When mapping data to a - * address pointer, the user needs to ensure the data is unmapped before the data can - * be used on the GPU / CPU depending on Usage. - */ -class PixelBuffer -{ -public: - /** - * All kinds of usage for pixel buffer objects as defined by the OpenGL standard. - * See: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBufferData.xhtml - */ - enum class Usage : std::underlying_type_t { - StreamDraw = static_cast>(GL_STREAM_DRAW), - StreamRead = static_cast>(GL_STREAM_READ), - StreamCopy = static_cast>(GL_STREAM_COPY), - StaticDraw = static_cast>(GL_STATIC_DRAW), - StaticRead = static_cast>(GL_STATIC_READ), - StaticCopy = static_cast>(GL_STATIC_COPY), - DynamicDraw = static_cast>(GL_DYNAMIC_DRAW), - DynamicRead = static_cast>(GL_DYNAMIC_READ), - DynamicCopy = static_cast>(GL_DYNAMIC_COPY) - }; - - /** - * Access hints for OpenGL buffer mapping - * See: https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glMapBuffer.xml - */ - enum class Access : std::underlying_type_t { - ReadOnly = static_cast>(GL_READ_ONLY), - WriteOnly = static_cast>(GL_WRITE_ONLY), - ReadWrite = static_cast>(GL_READ_WRITE) - }; - - /** - * Allocates numBytes bytes on the GPU and creates an ID for the pixel - * buffer object. - * \param numBytes is the number of bytes to be allocated on GPU memory - * \param usage is the Usage for the pixel buffer - */ - PixelBuffer(size_t numBytes, Usage usage); - - /** - * calls glDeleteBuffers(). - */ - ~PixelBuffer(); - - /** - * Maps an address pointer to GPU direct memory access. The user must make sure the - * buffer is bound before calling this function. - * \param access is the access to which can be any of GL_READ_ONLY, - * GL_WRITE_ONLY, or GL_READ_WRITE - * \returns the DMA address to the mapped buffer. Returns nullptr if the mapping - * failed - */ - void* mapBuffer(Access access); - - /** - * Maps an address pointer to GPU direct memory access. Gives access to a range of - * the buffer. The user must make sure the buffer is bound before calling this - * function. - * \param offet is the number of bytes to the first address to get in the buffer - * \param length is the number of bytes to access in the buffer - * \param access is a bitfield describing the access as described in: - * https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glMapBufferRange.xhtml - * \returns the DMA address to the mapped buffer. Returns nullptr if the mapping - * failed - */ - void* mapBufferRange(GLintptr offset, GLsizeiptr length, BufferAccessMask access); - - /** - * Maps the default buffer and makes the data available on the GPU - */ - bool unMapBuffer(); - - /** - * Calls glBindBuffer() - */ - void bind(); - - /** - * Calls glBindBuffer() with argument 0 to unmap any pixel buffer - */ - void unbind(); - - /** - * \returns true of the buffer is mapped, otherwise false - */ - bool isMapped() const; - - /** - * \returns the OpenGL id of the pixel buffer object - */ - operator GLuint() const; - -private: - GLuint _id; - const size_t _numBytes; - const Usage _usage; - bool _isMapped; -}; - -} // namespace globebrowsing -} // namespace openspace - -#endif // __OPENSPACE_MODULE_GLOBEBROWSING___PIXEL_BUFFER___H__ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___PIXEL_BUFFER___H__ +#define __OPENSPACE_MODULE_GLOBEBROWSING___PIXEL_BUFFER___H__ + +#include + +namespace openspace { +namespace globebrowsing { + +/** + * Handles an OpenGL pixel buffer which contains data allocated on the GPU. A simple + * class that wraps the standard functionality of OpenGL pixel buffer objects. Once + * the PixelBuffer is created, data is allocated on the GPU. When mapping data to a + * address pointer, the user needs to ensure the data is unmapped before the data can + * be used on the GPU / CPU depending on Usage. + */ +class PixelBuffer +{ +public: + /** + * All kinds of usage for pixel buffer objects as defined by the OpenGL standard. + * See: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBufferData.xhtml + */ + enum class Usage : std::underlying_type_t { + StreamDraw = static_cast>(GL_STREAM_DRAW), + StreamRead = static_cast>(GL_STREAM_READ), + StreamCopy = static_cast>(GL_STREAM_COPY), + StaticDraw = static_cast>(GL_STATIC_DRAW), + StaticRead = static_cast>(GL_STATIC_READ), + StaticCopy = static_cast>(GL_STATIC_COPY), + DynamicDraw = static_cast>(GL_DYNAMIC_DRAW), + DynamicRead = static_cast>(GL_DYNAMIC_READ), + DynamicCopy = static_cast>(GL_DYNAMIC_COPY) + }; + + /** + * Access hints for OpenGL buffer mapping + * See: https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glMapBuffer.xml + */ + enum class Access : std::underlying_type_t { + ReadOnly = static_cast>(GL_READ_ONLY), + WriteOnly = static_cast>(GL_WRITE_ONLY), + ReadWrite = static_cast>(GL_READ_WRITE) + }; + + /** + * Allocates numBytes bytes on the GPU and creates an ID for the pixel + * buffer object. + * \param numBytes is the number of bytes to be allocated on GPU memory + * \param usage is the Usage for the pixel buffer + */ + PixelBuffer(size_t numBytes, Usage usage); + + /** + * calls glDeleteBuffers(). + */ + ~PixelBuffer(); + + /** + * Maps an address pointer to GPU direct memory access. The user must make sure the + * buffer is bound before calling this function. + * \param access is the access to which can be any of GL_READ_ONLY, + * GL_WRITE_ONLY, or GL_READ_WRITE + * \returns the DMA address to the mapped buffer. Returns nullptr if the mapping + * failed + */ + void* mapBuffer(Access access); + + /** + * Maps an address pointer to GPU direct memory access. Gives access to a range of + * the buffer. The user must make sure the buffer is bound before calling this + * function. + * \param offet is the number of bytes to the first address to get in the buffer + * \param length is the number of bytes to access in the buffer + * \param access is a bitfield describing the access as described in: + * https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glMapBufferRange.xhtml + * \returns the DMA address to the mapped buffer. Returns nullptr if the mapping + * failed + */ + void* mapBufferRange(GLintptr offset, GLsizeiptr length, BufferAccessMask access); + + /** + * Maps the default buffer and makes the data available on the GPU + */ + bool unMapBuffer(); + + /** + * Calls glBindBuffer() + */ + void bind(); + + /** + * Calls glBindBuffer() with argument 0 to unmap any pixel buffer + */ + void unbind(); + + /** + * \returns true of the buffer is mapped, otherwise false + */ + bool isMapped() const; + + /** + * \returns the OpenGL id of the pixel buffer object + */ + operator GLuint() const; + +private: + GLuint _id; + const size_t _numBytes; + const Usage _usage; + bool _isMapped; +}; + +} // namespace globebrowsing +} // namespace openspace + +#endif // __OPENSPACE_MODULE_GLOBEBROWSING___PIXEL_BUFFER___H__ diff --git a/modules/globebrowsing/other/pixelbuffercontainer.h b/modules/globebrowsing/other/pixelbuffercontainer.h index f1fdcfb9aa..8dc0aa2dc6 100644 --- a/modules/globebrowsing/other/pixelbuffercontainer.h +++ b/modules/globebrowsing/other/pixelbuffercontainer.h @@ -1,115 +1,115 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2017 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___PIXEL_BUFFER_CONTAINER___H__ -#define __OPENSPACE_MODULE_GLOBEBROWSING___PIXEL_BUFFER_CONTAINER___H__ - -#include - -#include - -namespace openspace { -namespace globebrowsing { - -/** - * Templated class which owns one or many PixelBuffers. The - * KeyType is used to map a pixel buffer but only if it is not already - * mapped. - */ -template -class PixelBufferContainer -{ -public: - /** - * Creates numPixelBuffers pixel buffer objects, each with numBytesPerBuffer bytes - * allocated on the GPU. - * \param numBytesPerBuffer is the number of bytes per pixel buffer. All pixel - * buffers within a PixelBufferContainer have the same number of bytes - * \param usage is the Usage as described by PixelBuffer - * \param numPixelBuffers is the number of pixel buffers to create for this container. - * If numPixelBuffers is omitted, no pixel buffers are created. - */ - PixelBufferContainer(size_t numBytesPerBuffer, - globebrowsing::PixelBuffer::Usage usage, size_t numPixelBuffers = 0); - ~PixelBufferContainer() = default; - - /** - * Finds a Pixel buffer and maps it if it is available. - * \param key is the identifier for the pixel buffer which can be used later when - * unmapping the mapped pixel buffer. - * \param access is the access as described by PixelBuffer - * \returns an address pointer to DMA if the mapping succeeded. Otherwise a nullptr - * is returned. The mapping can fail if the buffer identified with key - * is already mapped or if something else failed. - */ - void* mapBuffer(KeyType key, PixelBuffer::Access access); - - /** - * Finds a Pixel buffer and maps a range of it if it is available. - * \param key is the identifier for the pixel buffer which can be used later when - * unmapping the mapped pixel buffer. - * \param offet is the number of bytes to the first address to get in the buffer - * \param length is the number of bytes to access in the buffer - * \param access is the access as described by PixelBuffer - * \returns an address pointer to DMA if the mapping succeeded. Otherwise a nullptr - * is returned. The mapping can fail if the buffer identified with key - * is already mapped or if something else failed. - */ - void* mapBufferRange(KeyType key, GLintptr offset, GLsizeiptr length, - BufferAccessMask access); - - /** - * Unmaps all buffers in the PixelBufferContainer. - * \returns true if the unmapping succeeded, otherwise false. - */ - bool resetMappedBuffers(); - - /** - * Unmaps a buffer that has previously been mapped. This buffer is identified using - * key. - * \param key is the identifier of the mapped buffer. - * \returns true if the unmapping succeeded, otherwise false. - */ - bool unMapBuffer(KeyType key); - - /** - * \returns the GLuint id of a pixel buffer identified by key - * if it currently is mapped. - */ - GLuint idOfMappedBuffer(KeyType key); -private: - const globebrowsing::PixelBuffer::Usage _usage; - - std::vector> _pixelBuffers; - - // Maps from KeyType to index of mapped buffers - std::map _indexMap; -}; - -} // namespace globebrowsing -} // namespace openspace - -#include "pixelbuffercontainer.inl" - -#endif // __OPENSPACE_MODULE_GLOBEBROWSING___PIXEL_BUFFER_CONTAINER___H__ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___PIXEL_BUFFER_CONTAINER___H__ +#define __OPENSPACE_MODULE_GLOBEBROWSING___PIXEL_BUFFER_CONTAINER___H__ + +#include + +#include + +namespace openspace { +namespace globebrowsing { + +/** + * Templated class which owns one or many PixelBuffers. The + * KeyType is used to map a pixel buffer but only if it is not already + * mapped. + */ +template +class PixelBufferContainer +{ +public: + /** + * Creates numPixelBuffers pixel buffer objects, each with numBytesPerBuffer bytes + * allocated on the GPU. + * \param numBytesPerBuffer is the number of bytes per pixel buffer. All pixel + * buffers within a PixelBufferContainer have the same number of bytes + * \param usage is the Usage as described by PixelBuffer + * \param numPixelBuffers is the number of pixel buffers to create for this container. + * If numPixelBuffers is omitted, no pixel buffers are created. + */ + PixelBufferContainer(size_t numBytesPerBuffer, + globebrowsing::PixelBuffer::Usage usage, size_t numPixelBuffers = 0); + ~PixelBufferContainer() = default; + + /** + * Finds a Pixel buffer and maps it if it is available. + * \param key is the identifier for the pixel buffer which can be used later when + * unmapping the mapped pixel buffer. + * \param access is the access as described by PixelBuffer + * \returns an address pointer to DMA if the mapping succeeded. Otherwise a nullptr + * is returned. The mapping can fail if the buffer identified with key + * is already mapped or if something else failed. + */ + void* mapBuffer(KeyType key, PixelBuffer::Access access); + + /** + * Finds a Pixel buffer and maps a range of it if it is available. + * \param key is the identifier for the pixel buffer which can be used later when + * unmapping the mapped pixel buffer. + * \param offet is the number of bytes to the first address to get in the buffer + * \param length is the number of bytes to access in the buffer + * \param access is the access as described by PixelBuffer + * \returns an address pointer to DMA if the mapping succeeded. Otherwise a nullptr + * is returned. The mapping can fail if the buffer identified with key + * is already mapped or if something else failed. + */ + void* mapBufferRange(KeyType key, GLintptr offset, GLsizeiptr length, + BufferAccessMask access); + + /** + * Unmaps all buffers in the PixelBufferContainer. + * \returns true if the unmapping succeeded, otherwise false. + */ + bool resetMappedBuffers(); + + /** + * Unmaps a buffer that has previously been mapped. This buffer is identified using + * key. + * \param key is the identifier of the mapped buffer. + * \returns true if the unmapping succeeded, otherwise false. + */ + bool unMapBuffer(KeyType key); + + /** + * \returns the GLuint id of a pixel buffer identified by key + * if it currently is mapped. + */ + GLuint idOfMappedBuffer(KeyType key); +private: + const globebrowsing::PixelBuffer::Usage _usage; + + std::vector> _pixelBuffers; + + // Maps from KeyType to index of mapped buffers + std::map _indexMap; +}; + +} // namespace globebrowsing +} // namespace openspace + +#include "pixelbuffercontainer.inl" + +#endif // __OPENSPACE_MODULE_GLOBEBROWSING___PIXEL_BUFFER_CONTAINER___H__ diff --git a/modules/globebrowsing/other/pixelbuffercontainer.inl b/modules/globebrowsing/other/pixelbuffercontainer.inl index c1a4e4aaa2..e3a1433e0a 100644 --- a/modules/globebrowsing/other/pixelbuffercontainer.inl +++ b/modules/globebrowsing/other/pixelbuffercontainer.inl @@ -1,130 +1,130 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2017 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -namespace openspace { -namespace globebrowsing { - -template -PixelBufferContainer::PixelBufferContainer(size_t numBytesPerBuffer, - PixelBuffer::Usage usage, size_t numPixelBuffers) - : _usage(usage) -{ - for (size_t i = 0; i < numPixelBuffers; ++i) { - _pixelBuffers.push_back(std::make_unique(numBytesPerBuffer, _usage)); - } -} - -template -void* PixelBufferContainer::mapBuffer(KeyType key, PixelBuffer::Access access) { - typename std::map::const_iterator iter = _indexMap.find(key); - const bool notFoundAmongMappedBuffers = (iter == _indexMap.end()); - - if (!notFoundAmongMappedBuffers) { // This PBO is already mapped - ghoul_assert(_pixelBuffers[iter->second], "Incorrect index map"); - return nullptr; - } - - // Find a pixel buffer that is unmapped - for (size_t i = 0; i < _pixelBuffers.size(); ++i) { - if (!_pixelBuffers[i]->isMapped()) { - _pixelBuffers[i]->bind(); - void* dataPtr = _pixelBuffers[i]->mapBuffer(access); - _pixelBuffers[i]->unbind(); - if (dataPtr) { // Success in mapping - // Add this index to the map of mapped pixel buffers - _indexMap.emplace(key, i); - return dataPtr; - } - } - } - return nullptr; -} - -template -void* PixelBufferContainer::mapBufferRange(KeyType key, GLintptr offset, - GLsizeiptr length, BufferAccessMask access) -{ - typename std::map::const_iterator iter = _indexMap.find(key); - bool notFoundAmongMappedBuffers = iter == _indexMap.end(); - - if (!notFoundAmongMappedBuffers) { // This PBO is already mapped - ghoul_assert(_pixelBuffers[iter->second], "Incorrect index map"); - return nullptr; - } - - // Find a pixel buffer that is unmapped - for (int i = 0; i < _pixelBuffers.size(); ++i) { - bool bufferIsMapped = _pixelBuffers[i]->isMapped(); - if (!bufferIsMapped) { - _pixelBuffers[i]->bind(); - void* dataPtr = _pixelBuffers[i]->mapBufferRange(offset, length, access); - _pixelBuffers[i]->unbind(); - if (dataPtr) { // Success in mapping - _indexMap.emplace(key, i); - return dataPtr; - } - } - } - return nullptr; -} - -template -bool PixelBufferContainer::resetMappedBuffers() { - bool success = true; - for (auto iter = _indexMap.begin(); iter != _indexMap.end(); iter++) { - int index = iter->second; // Index where the mapped buffer is stored - _pixelBuffers[index]->bind(); - success &= _pixelBuffers[index]->unMapBuffer(); - _pixelBuffers[index]->unbind(); - _indexMap.erase(iter); // This key should no longer be among the mapped buffers - } - return success; -} - -template -bool PixelBufferContainer::unMapBuffer(KeyType key) { - bool success = false; - typename std::map::const_iterator iter = _indexMap.find(key); - if (iter != _indexMap.end()) { // Found a mapped pixel buffer - int index = iter->second; // Index where the mapped buffer is stored - _pixelBuffers[index]->bind(); - success = _pixelBuffers[index]->unMapBuffer(); - _pixelBuffers[index]->unbind(); - _indexMap.erase(iter); // This key should no longer be among the mapped buffers - } - return success; -} - -template -GLuint PixelBufferContainer::idOfMappedBuffer(KeyType key) { - typename std::map::const_iterator iter = _indexMap.find(key); - if (iter != _indexMap.end()) { // Found a mapped pixel buffer - int index = iter->second; // Index where the mapped buffer is stored - return *_pixelBuffers[index]; - } - return 0; -} - -} // namespace globebrowsing -} // namespace openspace +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +namespace openspace { +namespace globebrowsing { + +template +PixelBufferContainer::PixelBufferContainer(size_t numBytesPerBuffer, + PixelBuffer::Usage usage, size_t numPixelBuffers) + : _usage(usage) +{ + for (size_t i = 0; i < numPixelBuffers; ++i) { + _pixelBuffers.push_back(std::make_unique(numBytesPerBuffer, _usage)); + } +} + +template +void* PixelBufferContainer::mapBuffer(KeyType key, PixelBuffer::Access access) { + typename std::map::const_iterator iter = _indexMap.find(key); + const bool notFoundAmongMappedBuffers = (iter == _indexMap.end()); + + if (!notFoundAmongMappedBuffers) { // This PBO is already mapped + ghoul_assert(_pixelBuffers[iter->second], "Incorrect index map"); + return nullptr; + } + + // Find a pixel buffer that is unmapped + for (size_t i = 0; i < _pixelBuffers.size(); ++i) { + if (!_pixelBuffers[i]->isMapped()) { + _pixelBuffers[i]->bind(); + void* dataPtr = _pixelBuffers[i]->mapBuffer(access); + _pixelBuffers[i]->unbind(); + if (dataPtr) { // Success in mapping + // Add this index to the map of mapped pixel buffers + _indexMap.emplace(key, i); + return dataPtr; + } + } + } + return nullptr; +} + +template +void* PixelBufferContainer::mapBufferRange(KeyType key, GLintptr offset, + GLsizeiptr length, BufferAccessMask access) +{ + typename std::map::const_iterator iter = _indexMap.find(key); + bool notFoundAmongMappedBuffers = iter == _indexMap.end(); + + if (!notFoundAmongMappedBuffers) { // This PBO is already mapped + ghoul_assert(_pixelBuffers[iter->second], "Incorrect index map"); + return nullptr; + } + + // Find a pixel buffer that is unmapped + for (int i = 0; i < _pixelBuffers.size(); ++i) { + bool bufferIsMapped = _pixelBuffers[i]->isMapped(); + if (!bufferIsMapped) { + _pixelBuffers[i]->bind(); + void* dataPtr = _pixelBuffers[i]->mapBufferRange(offset, length, access); + _pixelBuffers[i]->unbind(); + if (dataPtr) { // Success in mapping + _indexMap.emplace(key, i); + return dataPtr; + } + } + } + return nullptr; +} + +template +bool PixelBufferContainer::resetMappedBuffers() { + bool success = true; + for (auto iter = _indexMap.begin(); iter != _indexMap.end(); iter++) { + int index = iter->second; // Index where the mapped buffer is stored + _pixelBuffers[index]->bind(); + success &= _pixelBuffers[index]->unMapBuffer(); + _pixelBuffers[index]->unbind(); + _indexMap.erase(iter); // This key should no longer be among the mapped buffers + } + return success; +} + +template +bool PixelBufferContainer::unMapBuffer(KeyType key) { + bool success = false; + typename std::map::const_iterator iter = _indexMap.find(key); + if (iter != _indexMap.end()) { // Found a mapped pixel buffer + int index = iter->second; // Index where the mapped buffer is stored + _pixelBuffers[index]->bind(); + success = _pixelBuffers[index]->unMapBuffer(); + _pixelBuffers[index]->unbind(); + _indexMap.erase(iter); // This key should no longer be among the mapped buffers + } + return success; +} + +template +GLuint PixelBufferContainer::idOfMappedBuffer(KeyType key) { + typename std::map::const_iterator iter = _indexMap.find(key); + if (iter != _indexMap.end()) { // Found a mapped pixel buffer + int index = iter->second; // Index where the mapped buffer is stored + return *_pixelBuffers[index]; + } + return 0; +} + +} // namespace globebrowsing +} // namespace openspace diff --git a/modules/globebrowsing/tile/rawtiledatareader/gdalrawtiledatareader.cpp b/modules/globebrowsing/tile/rawtiledatareader/gdalrawtiledatareader.cpp index dfcf8d1d01..8bb8f7edc1 100644 --- a/modules/globebrowsing/tile/rawtiledatareader/gdalrawtiledatareader.cpp +++ b/modules/globebrowsing/tile/rawtiledatareader/gdalrawtiledatareader.cpp @@ -1,339 +1,339 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2017 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ -#ifdef GLOBEBROWSING_USE_GDAL - -#include - -#include -#include -#include -#include - -#include -#include // abspath -#include -#include -#include - -#include -#include -#include - -#include - -#include - -namespace { - const std::string _loggerCat = "GdalRawTileDataReader"; -} - -namespace openspace { -namespace globebrowsing { - -std::ostream& operator<<(std::ostream& os, const PixelRegion& pr) { - return os << pr.start.x << ", " << pr.start.y << " with size " << pr.numPixels.x << - ", " << pr.numPixels.y; -} - -GdalRawTileDataReader::GdalRawTileDataReader(const std::string& filePath, - const TileTextureInitData& initData, - const std::string& baseDirectory, - RawTileDataReader::PerformPreprocessing preprocess) - : RawTileDataReader(initData, preprocess) - , _dataset(nullptr) -{ - _initDirectory = baseDirectory.empty() ? CPLGetCurrentDir() : baseDirectory; - _datasetFilePath = filePath; - - { // Aquire lock - std::lock_guard lockGuard(_datasetLock); - initialize(); - } -} - -GdalRawTileDataReader::~GdalRawTileDataReader() { - std::lock_guard lockGuard(_datasetLock); - if (_dataset != nullptr) { - GDALClose(_dataset); - _dataset = nullptr; - } -} - -void GdalRawTileDataReader::reset() { - std::lock_guard lockGuard(_datasetLock); - _cached._maxLevel = -1; - if (_dataset != nullptr) { - GDALClose(_dataset); - _dataset = nullptr; - } - initialize(); -} - -int GdalRawTileDataReader::maxChunkLevel() const { - return _cached._maxLevel; -} - -float GdalRawTileDataReader::noDataValueAsFloat() const { - return _gdalDatasetMetaDataCached.noDataValue; -} - -int GdalRawTileDataReader::rasterXSize() const { - return _gdalDatasetMetaDataCached.rasterXSize; -} - -int GdalRawTileDataReader::rasterYSize() const { - return _gdalDatasetMetaDataCached.rasterYSize; -} - -float GdalRawTileDataReader::depthOffset() const { - return _gdalDatasetMetaDataCached.offset; -} - -float GdalRawTileDataReader::depthScale() const { - return _gdalDatasetMetaDataCached.scale; -} - -std::array GdalRawTileDataReader::getGeoTransform() const { - return _gdalDatasetMetaDataCached.padfTransform; -} - -IODescription GdalRawTileDataReader::getIODescription(const TileIndex& tileIndex) const { - IODescription io; - io.read.region = highestResPixelRegion(tileIndex); - - // write region starts in origin - io.write.region.start = PixelRegion::PixelCoordinate(0, 0); - io.write.region.numPixels = PixelRegion::PixelCoordinate( - _initData.dimensionsWithoutPadding().x, _initData.dimensionsWithoutPadding().y); - - io.read.overview = 0; - io.read.fullRegion = fullPixelRegion(); - // For correct sampling in dataset, we need to pad the texture tile - - PixelRegion scaledPadding = padding; - double scale = - io.read.region.numPixels.x / static_cast(io.write.region.numPixels.x); - scaledPadding.numPixels *= scale; - scaledPadding.start *= scale; - - io.read.region.pad(scaledPadding); - io.write.region.pad(padding); - io.write.region.start = PixelRegion::PixelCoordinate(0, 0); - - io.write.bytesPerLine = _initData.bytesPerLine(); - io.write.totalNumBytes = _initData.totalNumBytes(); - - ghoul_assert(io.write.region.numPixels.x == io.write.region.numPixels.y, ""); - ghoul_assert(io.write.region.numPixels.x == _initData.dimensionsWithPadding().x, ""); - - return io; -} - -void GdalRawTileDataReader::initialize() { - _dataset = openGdalDataset(_datasetFilePath); - - // Assume all raster bands have the same data type - _gdalDatasetMetaDataCached.rasterCount = _dataset->GetRasterCount(); - _gdalDatasetMetaDataCached.scale = _dataset->GetRasterBand(1)->GetScale(); - _gdalDatasetMetaDataCached.offset = _dataset->GetRasterBand(1)->GetOffset(); - _gdalDatasetMetaDataCached.rasterXSize = _dataset->GetRasterXSize(); - _gdalDatasetMetaDataCached.rasterYSize = _dataset->GetRasterYSize(); - _gdalDatasetMetaDataCached.noDataValue = _dataset->GetRasterBand(1)->GetNoDataValue(); - _gdalDatasetMetaDataCached.dataType = tiledatatype::getGdalDataType(_initData.glType()); - - CPLErr err = _dataset->GetGeoTransform(&_gdalDatasetMetaDataCached.padfTransform[0]); - if (err == CE_Failure) { - _gdalDatasetMetaDataCached.padfTransform = RawTileDataReader::getGeoTransform(); - } - - _depthTransform = calculateTileDepthTransform(); - _cached._tileLevelDifference = - calculateTileLevelDifference(_initData.dimensionsWithoutPadding().x); - - int numOverviews = _dataset->GetRasterBand(1)->GetOverviewCount(); - _cached._maxLevel = -_cached._tileLevelDifference; - if (numOverviews > 0) { - _cached._maxLevel += numOverviews - 1; - } -} - -void GdalRawTileDataReader::readImageData( - IODescription& io, RawTile::ReadError& worstError, char* imageDataDest) const { - - // Only read the minimum number of rasters - int nRastersToRead = std::min(_gdalDatasetMetaDataCached.rasterCount, - static_cast(_initData.nRasters())); - - switch (_initData.ghoulTextureFormat()) { - case ghoul::opengl::Texture::Format::Red: - case ghoul::opengl::Texture::Format::RG: - case ghoul::opengl::Texture::Format::RGB: - case ghoul::opengl::Texture::Format::RGBA: { - // Read the data (each rasterband is a separate channel) - for (int i = 0; i < nRastersToRead; i++) { - // The final destination pointer is offsetted by one datum byte size - // for every raster (or data channel, i.e. R in RGB) - char* dataDestination = imageDataDest + (i * _initData.bytesPerDatum()); - - RawTile::ReadError err = repeatedRasterRead(i + 1, io, dataDestination); - - // CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4 - worstError = std::max(worstError, err); - } - break; - } - case ghoul::opengl::Texture::Format::BGR: { - // Read the data (each rasterband is a separate channel) - for (int i = 0; i < 3 && i < nRastersToRead; i++) { - // The final destination pointer is offsetted by one datum byte size - // for every raster (or data channel, i.e. R in RGB) - char* dataDestination = imageDataDest + (i * _initData.bytesPerDatum()); - - RawTile::ReadError err = repeatedRasterRead(3 - i, io, dataDestination); - - // CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4 - worstError = std::max(worstError, err); - } - break; - } - case ghoul::opengl::Texture::Format::BGRA: { - for (int i = 0; i < 3 && i < nRastersToRead; i++) { - // The final destination pointer is offsetted by one datum byte size - // for every raster (or data channel, i.e. R in RGB) - char* dataDestination = imageDataDest + (i * _initData.bytesPerDatum()); - - RawTile::ReadError err = repeatedRasterRead(3 - i, io, dataDestination); - - // CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4 - worstError = std::max(worstError, err); - } - if (nRastersToRead > 3) { // Alpha channel exists - // Last read is the alpha channel - char* dataDestination = imageDataDest + (3 * _initData.bytesPerDatum()); - RawTile::ReadError err = repeatedRasterRead(4, io, dataDestination); - - // CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4 - worstError = std::max(worstError, err); - } - break; - } - default: { - ghoul_assert(false, "Texture format not supported for tiles"); - break; - } - } -} - -RawTile::ReadError GdalRawTileDataReader::rasterRead( - int rasterBand, const IODescription& io, char* dataDestination) const -{ - ghoul_assert(io.read.region.isInside(io.read.fullRegion), "write region of bounds!"); - ghoul_assert( - io.write.region.start.x >= 0 && io.write.region.start.y >= 0, - "Invalid write region" - ); - - PixelRegion::PixelCoordinate end = io.write.region.end(); - size_t largestIndex = - (end.y - 1) * io.write.bytesPerLine + (end.x - 1) * _initData.bytesPerPixel(); - ghoul_assert(largestIndex <= io.write.totalNumBytes, "Invalid write region"); - - char* dataDest = dataDestination; - - // GDAL reads pixels top to bottom, but we want our pixels bottom to top. - // Therefore, we increment the destination pointer to the last line on in the - // buffer, and the we specify in the rasterIO call that we want negative line - // spacing. Doing this compensates the flipped Y axis - dataDest += (io.write.totalNumBytes - io.write.bytesPerLine); - - // handle requested write region. Note -= since flipped y axis - dataDest -= io.write.region.start.y * io.write.bytesPerLine; - dataDest += io.write.region.start.x * _initData.bytesPerPixel(); - - GDALRasterBand* gdalRasterBand = _dataset->GetRasterBand(rasterBand); - CPLErr readError = CE_Failure; - readError = gdalRasterBand->RasterIO( - GF_Read, - io.read.region.start.x, // Begin read x - io.read.region.start.y, // Begin read y - io.read.region.numPixels.x, // width to read x - io.read.region.numPixels.y, // width to read y - dataDest, // Where to put data - io.write.region.numPixels.x, // width to write x in destination - io.write.region.numPixels.y, // width to write y in destination - _gdalDatasetMetaDataCached.dataType, // Type - _initData.bytesPerPixel(), // Pixel spacing - -io.write.bytesPerLine // Line spacing - ); - - // Convert error to RawTile::ReadError - RawTile::ReadError error; - switch (readError) { - case CE_None: error = RawTile::ReadError::None; break; - case CE_Debug: error = RawTile::ReadError::Debug; break; - case CE_Warning: error = RawTile::ReadError::Warning; break; - case CE_Failure: error = RawTile::ReadError::Failure; break; - case CE_Fatal: error = RawTile::ReadError::Fatal; break; - default: error = RawTile::ReadError::Failure; break; - } - return error; -} - -GDALDataset* GdalRawTileDataReader::openGdalDataset(const std::string& filePath) { - GDALDataset* dataset = static_cast( - GDALOpen(filePath.c_str(), GA_ReadOnly)); - if (!dataset) { - using namespace ghoul::filesystem; - std::string correctedPath = FileSystem::ref().pathByAppendingComponent( - _initDirectory, filePath - ); - - dataset = static_cast(GDALOpen(correctedPath.c_str(), GA_ReadOnly)); - if (!dataset) { - throw ghoul::RuntimeError("Failed to load dataset:\n" + filePath); - } - } - return dataset; -} - -int GdalRawTileDataReader::calculateTileLevelDifference(int minimumPixelSize) const { - GDALRasterBand* firstBand = _dataset->GetRasterBand(1); - GDALRasterBand* maxOverview; - int numOverviews = firstBand->GetOverviewCount(); - int sizeLevel0; - if (numOverviews <= 0) { // No overviews. Use first band. - maxOverview = firstBand; - } - else { // Pick the highest overview. - maxOverview = firstBand->GetOverview(numOverviews - 1); - } - sizeLevel0 = maxOverview->GetXSize(); - double diff = log2(minimumPixelSize) - log2(sizeLevel0); - return diff; -} - -} // namespace globebrowsing -} // namespace openspace - -#endif // GLOBEBROWSING_USE_GDAL +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ +#ifdef GLOBEBROWSING_USE_GDAL + +#include + +#include +#include +#include +#include + +#include +#include // abspath +#include +#include +#include + +#include +#include +#include + +#include + +#include + +namespace { + const std::string _loggerCat = "GdalRawTileDataReader"; +} + +namespace openspace { +namespace globebrowsing { + +std::ostream& operator<<(std::ostream& os, const PixelRegion& pr) { + return os << pr.start.x << ", " << pr.start.y << " with size " << pr.numPixels.x << + ", " << pr.numPixels.y; +} + +GdalRawTileDataReader::GdalRawTileDataReader(const std::string& filePath, + const TileTextureInitData& initData, + const std::string& baseDirectory, + RawTileDataReader::PerformPreprocessing preprocess) + : RawTileDataReader(initData, preprocess) + , _dataset(nullptr) +{ + _initDirectory = baseDirectory.empty() ? CPLGetCurrentDir() : baseDirectory; + _datasetFilePath = filePath; + + { // Aquire lock + std::lock_guard lockGuard(_datasetLock); + initialize(); + } +} + +GdalRawTileDataReader::~GdalRawTileDataReader() { + std::lock_guard lockGuard(_datasetLock); + if (_dataset != nullptr) { + GDALClose(_dataset); + _dataset = nullptr; + } +} + +void GdalRawTileDataReader::reset() { + std::lock_guard lockGuard(_datasetLock); + _cached._maxLevel = -1; + if (_dataset != nullptr) { + GDALClose(_dataset); + _dataset = nullptr; + } + initialize(); +} + +int GdalRawTileDataReader::maxChunkLevel() const { + return _cached._maxLevel; +} + +float GdalRawTileDataReader::noDataValueAsFloat() const { + return _gdalDatasetMetaDataCached.noDataValue; +} + +int GdalRawTileDataReader::rasterXSize() const { + return _gdalDatasetMetaDataCached.rasterXSize; +} + +int GdalRawTileDataReader::rasterYSize() const { + return _gdalDatasetMetaDataCached.rasterYSize; +} + +float GdalRawTileDataReader::depthOffset() const { + return _gdalDatasetMetaDataCached.offset; +} + +float GdalRawTileDataReader::depthScale() const { + return _gdalDatasetMetaDataCached.scale; +} + +std::array GdalRawTileDataReader::getGeoTransform() const { + return _gdalDatasetMetaDataCached.padfTransform; +} + +IODescription GdalRawTileDataReader::getIODescription(const TileIndex& tileIndex) const { + IODescription io; + io.read.region = highestResPixelRegion(tileIndex); + + // write region starts in origin + io.write.region.start = PixelRegion::PixelCoordinate(0, 0); + io.write.region.numPixels = PixelRegion::PixelCoordinate( + _initData.dimensionsWithoutPadding().x, _initData.dimensionsWithoutPadding().y); + + io.read.overview = 0; + io.read.fullRegion = fullPixelRegion(); + // For correct sampling in dataset, we need to pad the texture tile + + PixelRegion scaledPadding = padding; + double scale = + io.read.region.numPixels.x / static_cast(io.write.region.numPixels.x); + scaledPadding.numPixels *= scale; + scaledPadding.start *= scale; + + io.read.region.pad(scaledPadding); + io.write.region.pad(padding); + io.write.region.start = PixelRegion::PixelCoordinate(0, 0); + + io.write.bytesPerLine = _initData.bytesPerLine(); + io.write.totalNumBytes = _initData.totalNumBytes(); + + ghoul_assert(io.write.region.numPixels.x == io.write.region.numPixels.y, ""); + ghoul_assert(io.write.region.numPixels.x == _initData.dimensionsWithPadding().x, ""); + + return io; +} + +void GdalRawTileDataReader::initialize() { + _dataset = openGdalDataset(_datasetFilePath); + + // Assume all raster bands have the same data type + _gdalDatasetMetaDataCached.rasterCount = _dataset->GetRasterCount(); + _gdalDatasetMetaDataCached.scale = _dataset->GetRasterBand(1)->GetScale(); + _gdalDatasetMetaDataCached.offset = _dataset->GetRasterBand(1)->GetOffset(); + _gdalDatasetMetaDataCached.rasterXSize = _dataset->GetRasterXSize(); + _gdalDatasetMetaDataCached.rasterYSize = _dataset->GetRasterYSize(); + _gdalDatasetMetaDataCached.noDataValue = _dataset->GetRasterBand(1)->GetNoDataValue(); + _gdalDatasetMetaDataCached.dataType = tiledatatype::getGdalDataType(_initData.glType()); + + CPLErr err = _dataset->GetGeoTransform(&_gdalDatasetMetaDataCached.padfTransform[0]); + if (err == CE_Failure) { + _gdalDatasetMetaDataCached.padfTransform = RawTileDataReader::getGeoTransform(); + } + + _depthTransform = calculateTileDepthTransform(); + _cached._tileLevelDifference = + calculateTileLevelDifference(_initData.dimensionsWithoutPadding().x); + + int numOverviews = _dataset->GetRasterBand(1)->GetOverviewCount(); + _cached._maxLevel = -_cached._tileLevelDifference; + if (numOverviews > 0) { + _cached._maxLevel += numOverviews - 1; + } +} + +void GdalRawTileDataReader::readImageData( + IODescription& io, RawTile::ReadError& worstError, char* imageDataDest) const { + + // Only read the minimum number of rasters + int nRastersToRead = std::min(_gdalDatasetMetaDataCached.rasterCount, + static_cast(_initData.nRasters())); + + switch (_initData.ghoulTextureFormat()) { + case ghoul::opengl::Texture::Format::Red: + case ghoul::opengl::Texture::Format::RG: + case ghoul::opengl::Texture::Format::RGB: + case ghoul::opengl::Texture::Format::RGBA: { + // Read the data (each rasterband is a separate channel) + for (int i = 0; i < nRastersToRead; i++) { + // The final destination pointer is offsetted by one datum byte size + // for every raster (or data channel, i.e. R in RGB) + char* dataDestination = imageDataDest + (i * _initData.bytesPerDatum()); + + RawTile::ReadError err = repeatedRasterRead(i + 1, io, dataDestination); + + // CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4 + worstError = std::max(worstError, err); + } + break; + } + case ghoul::opengl::Texture::Format::BGR: { + // Read the data (each rasterband is a separate channel) + for (int i = 0; i < 3 && i < nRastersToRead; i++) { + // The final destination pointer is offsetted by one datum byte size + // for every raster (or data channel, i.e. R in RGB) + char* dataDestination = imageDataDest + (i * _initData.bytesPerDatum()); + + RawTile::ReadError err = repeatedRasterRead(3 - i, io, dataDestination); + + // CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4 + worstError = std::max(worstError, err); + } + break; + } + case ghoul::opengl::Texture::Format::BGRA: { + for (int i = 0; i < 3 && i < nRastersToRead; i++) { + // The final destination pointer is offsetted by one datum byte size + // for every raster (or data channel, i.e. R in RGB) + char* dataDestination = imageDataDest + (i * _initData.bytesPerDatum()); + + RawTile::ReadError err = repeatedRasterRead(3 - i, io, dataDestination); + + // CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4 + worstError = std::max(worstError, err); + } + if (nRastersToRead > 3) { // Alpha channel exists + // Last read is the alpha channel + char* dataDestination = imageDataDest + (3 * _initData.bytesPerDatum()); + RawTile::ReadError err = repeatedRasterRead(4, io, dataDestination); + + // CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4 + worstError = std::max(worstError, err); + } + break; + } + default: { + ghoul_assert(false, "Texture format not supported for tiles"); + break; + } + } +} + +RawTile::ReadError GdalRawTileDataReader::rasterRead( + int rasterBand, const IODescription& io, char* dataDestination) const +{ + ghoul_assert(io.read.region.isInside(io.read.fullRegion), "write region of bounds!"); + ghoul_assert( + io.write.region.start.x >= 0 && io.write.region.start.y >= 0, + "Invalid write region" + ); + + PixelRegion::PixelCoordinate end = io.write.region.end(); + size_t largestIndex = + (end.y - 1) * io.write.bytesPerLine + (end.x - 1) * _initData.bytesPerPixel(); + ghoul_assert(largestIndex <= io.write.totalNumBytes, "Invalid write region"); + + char* dataDest = dataDestination; + + // GDAL reads pixels top to bottom, but we want our pixels bottom to top. + // Therefore, we increment the destination pointer to the last line on in the + // buffer, and the we specify in the rasterIO call that we want negative line + // spacing. Doing this compensates the flipped Y axis + dataDest += (io.write.totalNumBytes - io.write.bytesPerLine); + + // handle requested write region. Note -= since flipped y axis + dataDest -= io.write.region.start.y * io.write.bytesPerLine; + dataDest += io.write.region.start.x * _initData.bytesPerPixel(); + + GDALRasterBand* gdalRasterBand = _dataset->GetRasterBand(rasterBand); + CPLErr readError = CE_Failure; + readError = gdalRasterBand->RasterIO( + GF_Read, + io.read.region.start.x, // Begin read x + io.read.region.start.y, // Begin read y + io.read.region.numPixels.x, // width to read x + io.read.region.numPixels.y, // width to read y + dataDest, // Where to put data + io.write.region.numPixels.x, // width to write x in destination + io.write.region.numPixels.y, // width to write y in destination + _gdalDatasetMetaDataCached.dataType, // Type + _initData.bytesPerPixel(), // Pixel spacing + -io.write.bytesPerLine // Line spacing + ); + + // Convert error to RawTile::ReadError + RawTile::ReadError error; + switch (readError) { + case CE_None: error = RawTile::ReadError::None; break; + case CE_Debug: error = RawTile::ReadError::Debug; break; + case CE_Warning: error = RawTile::ReadError::Warning; break; + case CE_Failure: error = RawTile::ReadError::Failure; break; + case CE_Fatal: error = RawTile::ReadError::Fatal; break; + default: error = RawTile::ReadError::Failure; break; + } + return error; +} + +GDALDataset* GdalRawTileDataReader::openGdalDataset(const std::string& filePath) { + GDALDataset* dataset = static_cast( + GDALOpen(filePath.c_str(), GA_ReadOnly)); + if (!dataset) { + using namespace ghoul::filesystem; + std::string correctedPath = FileSystem::ref().pathByAppendingComponent( + _initDirectory, filePath + ); + + dataset = static_cast(GDALOpen(correctedPath.c_str(), GA_ReadOnly)); + if (!dataset) { + throw ghoul::RuntimeError("Failed to load dataset:\n" + filePath); + } + } + return dataset; +} + +int GdalRawTileDataReader::calculateTileLevelDifference(int minimumPixelSize) const { + GDALRasterBand* firstBand = _dataset->GetRasterBand(1); + GDALRasterBand* maxOverview; + int numOverviews = firstBand->GetOverviewCount(); + int sizeLevel0; + if (numOverviews <= 0) { // No overviews. Use first band. + maxOverview = firstBand; + } + else { // Pick the highest overview. + maxOverview = firstBand->GetOverview(numOverviews - 1); + } + sizeLevel0 = maxOverview->GetXSize(); + double diff = log2(minimumPixelSize) - log2(sizeLevel0); + return diff; +} + +} // namespace globebrowsing +} // namespace openspace + +#endif // GLOBEBROWSING_USE_GDAL diff --git a/modules/globebrowsing/tile/rawtiledatareader/tiledatatype.cpp b/modules/globebrowsing/tile/rawtiledatareader/tiledatatype.cpp index 03a08c4ed0..ed8590a14a 100644 --- a/modules/globebrowsing/tile/rawtiledatareader/tiledatatype.cpp +++ b/modules/globebrowsing/tile/rawtiledatareader/tiledatatype.cpp @@ -1,614 +1,614 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2017 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#include - -#ifdef GLOBEBROWSING_USE_GDAL -#include -#include -#endif // GLOBEBROWSING_USE_GDAL - -#include -#include // abspath -#include - -#include - -#include - -#include -#include - -namespace { - const char* _loggerCat = "TileDataType"; -} - -namespace openspace { -namespace globebrowsing { -namespace tiledatatype { - -#ifdef GLOBEBROWSING_USE_GDAL - -float interpretFloat(GDALDataType gdalType, const char* src) { - switch (gdalType) { - case GDT_Byte: - return static_cast(*reinterpret_cast(src)); - case GDT_UInt16: - return static_cast(*reinterpret_cast(src)); - case GDT_Int16: - return static_cast(*reinterpret_cast(src)); - case GDT_UInt32: - return static_cast(*reinterpret_cast(src)); - case GDT_Int32: - return static_cast(*reinterpret_cast(src)); - case GDT_Float32: - return static_cast(*reinterpret_cast(src)); - case GDT_Float64: - return static_cast(*reinterpret_cast(src)); - default: - ghoul_assert(false, "Unknown data type"); - } -} - -size_t numberOfBytes(GDALDataType gdalType) { - switch (gdalType) { - case GDT_Byte: - return sizeof(GLubyte); - case GDT_UInt16: - return sizeof(GLushort); - case GDT_Int16: - return sizeof(GLshort); - case GDT_UInt32: - return sizeof(GLuint); - case GDT_Int32: - return sizeof(GLint); - case GDT_Float32: - return sizeof(GLfloat); - case GDT_Float64: - return sizeof(GLdouble); - default: - ghoul_assert(false, "Unknown data type"); - } -} - -size_t getMaximumValue(GDALDataType gdalType) { - switch (gdalType) { - case GDT_Byte: - return 1 << 8; - case GDT_UInt16: - return 1 << 16; - case GDT_Int16: - return 1 << 15; - case GDT_UInt32: - return size_t(1) << 32; - case GDT_Int32: - return 1 << 31; - default: - ghoul_assert(false, "Unknown data type"); - } -} - -TextureFormat getTextureFormat(int rasterCount, GDALDataType gdalType) { - TextureFormat format; - - switch (rasterCount) { - case 1: // Red - format.ghoulFormat = ghoul::opengl::Texture::Format::Red; - switch (gdalType) { - case GDT_Byte: - format.glFormat = GL_R8; - break; - case GDT_UInt16: - format.glFormat = GL_R16UI; - break; - case GDT_Int16: - format.glFormat = GL_R16_SNORM; - break; - case GDT_UInt32: - format.glFormat = GL_R32UI; - break; - case GDT_Int32: - format.glFormat = GL_R32I; - break; - case GDT_Float32: - format.glFormat = GL_R32F; - break; - // No representation of 64 bit float? - //case GDT_Float64: - // format.glFormat = GL_RED; - // break; - default: - LERROR("GDAL data type unknown to OpenGL: " << gdalType); - } - break; - case 2: - format.ghoulFormat = ghoul::opengl::Texture::Format::RG; - switch (gdalType) { - case GDT_Byte: - format.glFormat = GL_RG8; - break; - case GDT_UInt16: - format.glFormat = GL_RG16UI; - break; - case GDT_Int16: - format.glFormat = GL_RG16_SNORM; - break; - case GDT_UInt32: - format.glFormat = GL_RG32UI; - break; - case GDT_Int32: - format.glFormat = GL_RG32I; - break; - case GDT_Float32: - format.glFormat = GL_RG32F; - break; - case GDT_Float64: - format.glFormat = GL_RED; - break; - default: - LERROR("GDAL data type unknown to OpenGL: " << gdalType); - } - break; - case 3: - format.ghoulFormat = ghoul::opengl::Texture::Format::RGB; - switch (gdalType) { - case GDT_Byte: - format.glFormat = GL_RGB8; - break; - case GDT_UInt16: - format.glFormat = GL_RGB16UI; - break; - case GDT_Int16: - format.glFormat = GL_RGB16_SNORM; - break; - case GDT_UInt32: - format.glFormat = GL_RGB32UI; - break; - case GDT_Int32: - format.glFormat = GL_RGB32I; - break; - case GDT_Float32: - format.glFormat = GL_RGB32F; - break; - // No representation of 64 bit float? - //case GDT_Float64: - // format.glFormat = GL_RED; - // break; - default: - LERROR("GDAL data type unknown to OpenGL: " << gdalType); - } - break; - case 4: - format.ghoulFormat = ghoul::opengl::Texture::Format::RGBA; - switch (gdalType) { - case GDT_Byte: - format.glFormat = GL_RGBA8; - break; - case GDT_UInt16: - format.glFormat = GL_RGBA16UI; - break; - case GDT_Int16: - format.glFormat = GL_RGB16_SNORM; - break; - case GDT_UInt32: - format.glFormat = GL_RGBA32UI; - break; - case GDT_Int32: - format.glFormat = GL_RGBA32I; - break; - case GDT_Float32: - format.glFormat = GL_RGBA32F; - break; - // No representation of 64 bit float? - //case GDT_Float64: - // format.glFormat = GL_RED; - // break; - default: - LERROR("GDAL data type unknown to OpenGL: " << gdalType); - } - break; - default: - LERROR("Unknown number of channels for OpenGL texture: " << rasterCount); - break; - } - return format; -} - -TextureFormat getTextureFormatOptimized(int rasterCount, GDALDataType gdalType) { - TextureFormat format; - - switch (rasterCount) { - case 1: // Red - format.ghoulFormat = ghoul::opengl::Texture::Format::Red; - switch (gdalType) { - case GDT_Byte: - format.glFormat = GL_R8; - break; - case GDT_UInt16: - format.glFormat = GL_R16UI; - break; - case GDT_Int16: - format.glFormat = GL_R16_SNORM; - break; - case GDT_UInt32: - format.glFormat = GL_R32UI; - break; - case GDT_Int32: - format.glFormat = GL_R32I; - break; - case GDT_Float32: - format.glFormat = GL_R32F; - break; - // No representation of 64 bit float? - //case GDT_Float64: - // format.glFormat = GL_RED; - // break; - default: - LERROR("GDAL data type unknown to OpenGL: " << gdalType); - } - break; - case 2: - format.ghoulFormat = ghoul::opengl::Texture::Format::RG; - switch (gdalType) { - case GDT_Byte: - format.glFormat = GL_RG8; - break; - case GDT_UInt16: - format.glFormat = GL_RG16UI; - break; - case GDT_Int16: - format.glFormat = GL_RG16_SNORM; - break; - case GDT_UInt32: - format.glFormat = GL_RG32UI; - break; - case GDT_Int32: - format.glFormat = GL_RG32I; - break; - case GDT_Float32: - format.glFormat = GL_RG32F; - break; - case GDT_Float64: - format.glFormat = GL_RED; - break; - default: - LERROR("GDAL data type unknown to OpenGL: " << gdalType); - } - break; - case 3: - format.ghoulFormat = ghoul::opengl::Texture::Format::BGR; - switch (gdalType) { - case GDT_Byte: - format.glFormat = GL_RGB8; - break; - case GDT_UInt16: - format.glFormat = GL_RGB16UI; - break; - case GDT_Int16: - format.glFormat = GL_RGB16_SNORM; - break; - case GDT_UInt32: - format.glFormat = GL_RGB32UI; - break; - case GDT_Int32: - format.glFormat = GL_RGB32I; - break; - case GDT_Float32: - format.glFormat = GL_RGB32F; - break; - // No representation of 64 bit float? - //case GDT_Float64: - // format.glFormat = GL_RED; - // break; - default: - LERROR("GDAL data type unknown to OpenGL: " << gdalType); - } - break; - case 4: - format.ghoulFormat = ghoul::opengl::Texture::Format::BGRA; - switch (gdalType) { - case GDT_Byte: - format.glFormat = GL_RGBA8; - break; - case GDT_UInt16: - format.glFormat = GL_RGBA16UI; - break; - case GDT_Int16: - format.glFormat = GL_RGB16_SNORM; - break; - case GDT_UInt32: - format.glFormat = GL_RGBA32UI; - break; - case GDT_Int32: - format.glFormat = GL_RGBA32I; - break; - case GDT_Float32: - format.glFormat = GL_RGBA32F; - break; - // No representation of 64 bit float? - //case GDT_Float64: - // format.glFormat = GL_RED; - // break; - default: - LERROR("GDAL data type unknown to OpenGL: " << gdalType); - } - break; - default: - LERROR("Unknown number of channels for OpenGL texture: " << rasterCount); - break; - } - return format; -} - -GLenum getOpenGLDataType(GDALDataType gdalType) { - switch (gdalType) { - case GDT_Byte: - return GL_UNSIGNED_BYTE; - case GDT_UInt16: - return GL_UNSIGNED_SHORT; - case GDT_Int16: - return GL_SHORT; - case GDT_UInt32: - return GL_UNSIGNED_INT; - case GDT_Int32: - return GL_INT; - case GDT_Float32: - return GL_FLOAT; - case GDT_Float64: - return GL_DOUBLE; - default: - LERROR("GDAL data type unknown to OpenGL: " << gdalType); - return GL_UNSIGNED_BYTE; - } -} - -GDALDataType getGdalDataType(GLenum glType) { - switch (glType) { - case GL_UNSIGNED_BYTE: - return GDT_Byte; - case GL_UNSIGNED_SHORT: - return GDT_UInt16; - case GL_SHORT: - return GDT_Int16; - case GL_UNSIGNED_INT: - return GDT_UInt32; - case GL_INT: - return GDT_Int32; - case GL_FLOAT: - return GDT_Float32; - case GL_DOUBLE: - return GDT_Float64; - default: - LERROR("OpenGL data type unknown to GDAL: " << glType); - return GDT_Unknown; - } -} - -#endif // GLOBEBROWSING_USE_GDAL - -size_t numberOfRasters(ghoul::opengl::Texture::Format format) { - switch (format) { - case ghoul::opengl::Texture::Format::Red: return 1; - case ghoul::opengl::Texture::Format::RG: return 2; - case ghoul::opengl::Texture::Format::RGB:; // Intentional fallthrough - case ghoul::opengl::Texture::Format::BGR: return 3; - case ghoul::opengl::Texture::Format::RGBA:; // Intentional fallthrough - case ghoul::opengl::Texture::Format::BGRA: return 4; - default: ghoul_assert(false, "Unknown format"); - } -} - -size_t numberOfBytes(GLenum glType) { - switch (glType) { - case GL_UNSIGNED_BYTE: return sizeof(GLubyte); - case GL_BYTE: return sizeof(GLbyte); - case GL_UNSIGNED_SHORT: return sizeof(GLushort); - case GL_SHORT: return sizeof(GLshort); - case GL_UNSIGNED_INT: return sizeof(GLuint); - case GL_INT: return sizeof(GLint); - case GL_HALF_FLOAT: return sizeof(GLhalf); - case GL_FLOAT: return sizeof(GLfloat); - case GL_DOUBLE: return sizeof(GLdouble); - default: - ghoul_assert(false, "Unknown data type"); - } -} - -size_t getMaximumValue(GLenum glType) { - switch (glType) { - case GL_UNSIGNED_BYTE: - return 1 << 8; - case GL_UNSIGNED_SHORT: - return 1 << 16; - case GL_SHORT: - return 1 << 15; - case GL_UNSIGNED_INT: - return size_t(1) << 32; - case GL_INT: - return 1 << 31; - default: - ghoul_assert(false, "Unknown data type"); - } -} - -float interpretFloat(GLenum glType, const char* src) { - switch (glType) { - case GL_UNSIGNED_BYTE: - return static_cast(*reinterpret_cast(src)); - case GL_UNSIGNED_SHORT: - return static_cast(*reinterpret_cast(src)); - case GL_SHORT: - return static_cast(*reinterpret_cast(src)); - case GL_UNSIGNED_INT: - return static_cast(*reinterpret_cast(src)); - case GL_INT: - return static_cast(*reinterpret_cast(src)); - case GL_HALF_FLOAT: - return static_cast(*reinterpret_cast(src)); - case GL_FLOAT: - return static_cast(*reinterpret_cast(src)); - case GL_DOUBLE: - return static_cast(*reinterpret_cast(src)); - default: - ghoul_assert(false, "Unknown data type"); - } -} - -GLenum glTextureFormat(GLenum glType, ghoul::opengl::Texture::Format format) { - switch (format) { - case ghoul::opengl::Texture::Format::Red: - switch (glType) { - case GL_BYTE: - return GL_R8; - case GL_UNSIGNED_BYTE: - return GL_R8; - case GL_INT: - return GL_R32I; - case GL_UNSIGNED_INT: - return GL_R32UI; - case GL_FLOAT: - return GL_R32F; - case GL_HALF_FLOAT: - return GL_R16F; - default: - ghoul_assert(false, "glType data type unknown"); - LERROR("glType data type unknown: " << glType); - return GLenum(0); - } - break; - case ghoul::opengl::Texture::Format::RG: - switch (glType) { - case GL_BYTE: - return GL_RG8; - case GL_UNSIGNED_BYTE: - return GL_RG8; - case GL_INT: - return GL_RG32I; - case GL_UNSIGNED_INT: - return GL_RG32UI; - case GL_FLOAT: - return GL_RG32F; - case GL_HALF_FLOAT: - return GL_RG16F; - default: - ghoul_assert(false, "glType data type unknown"); - LERROR("glType data type unknown: " << glType); - return GLenum(0); - } - break; - case ghoul::opengl::Texture::Format::RGB: - switch (glType) { - case GL_BYTE: - return GL_RGB8; - case GL_UNSIGNED_BYTE: - return GL_RGB8; - case GL_INT: - return GL_RGB32I; - case GL_UNSIGNED_INT: - return GL_RGB32UI; - case GL_FLOAT: - return GL_RGB32F; - case GL_HALF_FLOAT: - return GL_RGB16F; - default: - ghoul_assert(false, "glType data type unknown"); - LERROR("glType data type unknown: " << glType); - return GLenum(0); - } - break; - case ghoul::opengl::Texture::Format::RGBA: - switch (glType) { - case GL_BYTE: - return GL_RGBA8; - case GL_UNSIGNED_BYTE: - return GL_RGBA8; - case GL_INT: - return GL_RGBA32I; - case GL_UNSIGNED_INT: - return GL_RGBA32UI; - case GL_FLOAT: - return GL_RGBA32F; - case GL_HALF_FLOAT: - return GL_RGBA16F; - default: - ghoul_assert(false, "glType data type unknown"); - LERROR("glType data type unknown: " << glType); - return GLenum(0); - } - break; - case ghoul::opengl::Texture::Format::BGR: - switch (glType) { - case GL_BYTE: - return GL_RGB8; - case GL_UNSIGNED_BYTE: - return GL_RGB8; - case GL_INT: - return GL_RGB32I; - case GL_UNSIGNED_INT: - return GL_RGB32UI; - case GL_FLOAT: - return GL_RGB32F; - case GL_HALF_FLOAT: - return GL_RGB16F; - default: - ghoul_assert(false, "glType data type unknown"); - LERROR("glType data type unknown: " << glType); - return GLenum(0); - } - break; - case ghoul::opengl::Texture::Format::BGRA: - switch (glType) { - case GL_BYTE: - return GL_RGBA8; - case GL_UNSIGNED_BYTE: - return GL_RGBA8; - case GL_INT: - return GL_RGBA32I; - case GL_UNSIGNED_INT: - return GL_RGBA32UI; - case GL_FLOAT: - return GL_RGBA32F; - case GL_HALF_FLOAT: - return GL_RGBA16F; - default: - ghoul_assert(false, "glType data type unknown"); - LERROR("glType data type unknown: " << glType); - return GLenum(0); - } - break; - default: - LERROR( - "Unknown format for OpenGL texture: " << - static_cast - >(format) - ); - return GLenum(0); - break; - } -} - -} // namespace tiledatatype -} // namespace globebrowsing -} // namespace openspace +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#ifdef GLOBEBROWSING_USE_GDAL +#include +#include +#endif // GLOBEBROWSING_USE_GDAL + +#include +#include // abspath +#include + +#include + +#include + +#include +#include + +namespace { + const char* _loggerCat = "TileDataType"; +} + +namespace openspace { +namespace globebrowsing { +namespace tiledatatype { + +#ifdef GLOBEBROWSING_USE_GDAL + +float interpretFloat(GDALDataType gdalType, const char* src) { + switch (gdalType) { + case GDT_Byte: + return static_cast(*reinterpret_cast(src)); + case GDT_UInt16: + return static_cast(*reinterpret_cast(src)); + case GDT_Int16: + return static_cast(*reinterpret_cast(src)); + case GDT_UInt32: + return static_cast(*reinterpret_cast(src)); + case GDT_Int32: + return static_cast(*reinterpret_cast(src)); + case GDT_Float32: + return static_cast(*reinterpret_cast(src)); + case GDT_Float64: + return static_cast(*reinterpret_cast(src)); + default: + ghoul_assert(false, "Unknown data type"); + } +} + +size_t numberOfBytes(GDALDataType gdalType) { + switch (gdalType) { + case GDT_Byte: + return sizeof(GLubyte); + case GDT_UInt16: + return sizeof(GLushort); + case GDT_Int16: + return sizeof(GLshort); + case GDT_UInt32: + return sizeof(GLuint); + case GDT_Int32: + return sizeof(GLint); + case GDT_Float32: + return sizeof(GLfloat); + case GDT_Float64: + return sizeof(GLdouble); + default: + ghoul_assert(false, "Unknown data type"); + } +} + +size_t getMaximumValue(GDALDataType gdalType) { + switch (gdalType) { + case GDT_Byte: + return 1 << 8; + case GDT_UInt16: + return 1 << 16; + case GDT_Int16: + return 1 << 15; + case GDT_UInt32: + return size_t(1) << 32; + case GDT_Int32: + return 1 << 31; + default: + ghoul_assert(false, "Unknown data type"); + } +} + +TextureFormat getTextureFormat(int rasterCount, GDALDataType gdalType) { + TextureFormat format; + + switch (rasterCount) { + case 1: // Red + format.ghoulFormat = ghoul::opengl::Texture::Format::Red; + switch (gdalType) { + case GDT_Byte: + format.glFormat = GL_R8; + break; + case GDT_UInt16: + format.glFormat = GL_R16UI; + break; + case GDT_Int16: + format.glFormat = GL_R16_SNORM; + break; + case GDT_UInt32: + format.glFormat = GL_R32UI; + break; + case GDT_Int32: + format.glFormat = GL_R32I; + break; + case GDT_Float32: + format.glFormat = GL_R32F; + break; + // No representation of 64 bit float? + //case GDT_Float64: + // format.glFormat = GL_RED; + // break; + default: + LERROR("GDAL data type unknown to OpenGL: " << gdalType); + } + break; + case 2: + format.ghoulFormat = ghoul::opengl::Texture::Format::RG; + switch (gdalType) { + case GDT_Byte: + format.glFormat = GL_RG8; + break; + case GDT_UInt16: + format.glFormat = GL_RG16UI; + break; + case GDT_Int16: + format.glFormat = GL_RG16_SNORM; + break; + case GDT_UInt32: + format.glFormat = GL_RG32UI; + break; + case GDT_Int32: + format.glFormat = GL_RG32I; + break; + case GDT_Float32: + format.glFormat = GL_RG32F; + break; + case GDT_Float64: + format.glFormat = GL_RED; + break; + default: + LERROR("GDAL data type unknown to OpenGL: " << gdalType); + } + break; + case 3: + format.ghoulFormat = ghoul::opengl::Texture::Format::RGB; + switch (gdalType) { + case GDT_Byte: + format.glFormat = GL_RGB8; + break; + case GDT_UInt16: + format.glFormat = GL_RGB16UI; + break; + case GDT_Int16: + format.glFormat = GL_RGB16_SNORM; + break; + case GDT_UInt32: + format.glFormat = GL_RGB32UI; + break; + case GDT_Int32: + format.glFormat = GL_RGB32I; + break; + case GDT_Float32: + format.glFormat = GL_RGB32F; + break; + // No representation of 64 bit float? + //case GDT_Float64: + // format.glFormat = GL_RED; + // break; + default: + LERROR("GDAL data type unknown to OpenGL: " << gdalType); + } + break; + case 4: + format.ghoulFormat = ghoul::opengl::Texture::Format::RGBA; + switch (gdalType) { + case GDT_Byte: + format.glFormat = GL_RGBA8; + break; + case GDT_UInt16: + format.glFormat = GL_RGBA16UI; + break; + case GDT_Int16: + format.glFormat = GL_RGB16_SNORM; + break; + case GDT_UInt32: + format.glFormat = GL_RGBA32UI; + break; + case GDT_Int32: + format.glFormat = GL_RGBA32I; + break; + case GDT_Float32: + format.glFormat = GL_RGBA32F; + break; + // No representation of 64 bit float? + //case GDT_Float64: + // format.glFormat = GL_RED; + // break; + default: + LERROR("GDAL data type unknown to OpenGL: " << gdalType); + } + break; + default: + LERROR("Unknown number of channels for OpenGL texture: " << rasterCount); + break; + } + return format; +} + +TextureFormat getTextureFormatOptimized(int rasterCount, GDALDataType gdalType) { + TextureFormat format; + + switch (rasterCount) { + case 1: // Red + format.ghoulFormat = ghoul::opengl::Texture::Format::Red; + switch (gdalType) { + case GDT_Byte: + format.glFormat = GL_R8; + break; + case GDT_UInt16: + format.glFormat = GL_R16UI; + break; + case GDT_Int16: + format.glFormat = GL_R16_SNORM; + break; + case GDT_UInt32: + format.glFormat = GL_R32UI; + break; + case GDT_Int32: + format.glFormat = GL_R32I; + break; + case GDT_Float32: + format.glFormat = GL_R32F; + break; + // No representation of 64 bit float? + //case GDT_Float64: + // format.glFormat = GL_RED; + // break; + default: + LERROR("GDAL data type unknown to OpenGL: " << gdalType); + } + break; + case 2: + format.ghoulFormat = ghoul::opengl::Texture::Format::RG; + switch (gdalType) { + case GDT_Byte: + format.glFormat = GL_RG8; + break; + case GDT_UInt16: + format.glFormat = GL_RG16UI; + break; + case GDT_Int16: + format.glFormat = GL_RG16_SNORM; + break; + case GDT_UInt32: + format.glFormat = GL_RG32UI; + break; + case GDT_Int32: + format.glFormat = GL_RG32I; + break; + case GDT_Float32: + format.glFormat = GL_RG32F; + break; + case GDT_Float64: + format.glFormat = GL_RED; + break; + default: + LERROR("GDAL data type unknown to OpenGL: " << gdalType); + } + break; + case 3: + format.ghoulFormat = ghoul::opengl::Texture::Format::BGR; + switch (gdalType) { + case GDT_Byte: + format.glFormat = GL_RGB8; + break; + case GDT_UInt16: + format.glFormat = GL_RGB16UI; + break; + case GDT_Int16: + format.glFormat = GL_RGB16_SNORM; + break; + case GDT_UInt32: + format.glFormat = GL_RGB32UI; + break; + case GDT_Int32: + format.glFormat = GL_RGB32I; + break; + case GDT_Float32: + format.glFormat = GL_RGB32F; + break; + // No representation of 64 bit float? + //case GDT_Float64: + // format.glFormat = GL_RED; + // break; + default: + LERROR("GDAL data type unknown to OpenGL: " << gdalType); + } + break; + case 4: + format.ghoulFormat = ghoul::opengl::Texture::Format::BGRA; + switch (gdalType) { + case GDT_Byte: + format.glFormat = GL_RGBA8; + break; + case GDT_UInt16: + format.glFormat = GL_RGBA16UI; + break; + case GDT_Int16: + format.glFormat = GL_RGB16_SNORM; + break; + case GDT_UInt32: + format.glFormat = GL_RGBA32UI; + break; + case GDT_Int32: + format.glFormat = GL_RGBA32I; + break; + case GDT_Float32: + format.glFormat = GL_RGBA32F; + break; + // No representation of 64 bit float? + //case GDT_Float64: + // format.glFormat = GL_RED; + // break; + default: + LERROR("GDAL data type unknown to OpenGL: " << gdalType); + } + break; + default: + LERROR("Unknown number of channels for OpenGL texture: " << rasterCount); + break; + } + return format; +} + +GLenum getOpenGLDataType(GDALDataType gdalType) { + switch (gdalType) { + case GDT_Byte: + return GL_UNSIGNED_BYTE; + case GDT_UInt16: + return GL_UNSIGNED_SHORT; + case GDT_Int16: + return GL_SHORT; + case GDT_UInt32: + return GL_UNSIGNED_INT; + case GDT_Int32: + return GL_INT; + case GDT_Float32: + return GL_FLOAT; + case GDT_Float64: + return GL_DOUBLE; + default: + LERROR("GDAL data type unknown to OpenGL: " << gdalType); + return GL_UNSIGNED_BYTE; + } +} + +GDALDataType getGdalDataType(GLenum glType) { + switch (glType) { + case GL_UNSIGNED_BYTE: + return GDT_Byte; + case GL_UNSIGNED_SHORT: + return GDT_UInt16; + case GL_SHORT: + return GDT_Int16; + case GL_UNSIGNED_INT: + return GDT_UInt32; + case GL_INT: + return GDT_Int32; + case GL_FLOAT: + return GDT_Float32; + case GL_DOUBLE: + return GDT_Float64; + default: + LERROR("OpenGL data type unknown to GDAL: " << glType); + return GDT_Unknown; + } +} + +#endif // GLOBEBROWSING_USE_GDAL + +size_t numberOfRasters(ghoul::opengl::Texture::Format format) { + switch (format) { + case ghoul::opengl::Texture::Format::Red: return 1; + case ghoul::opengl::Texture::Format::RG: return 2; + case ghoul::opengl::Texture::Format::RGB:; // Intentional fallthrough + case ghoul::opengl::Texture::Format::BGR: return 3; + case ghoul::opengl::Texture::Format::RGBA:; // Intentional fallthrough + case ghoul::opengl::Texture::Format::BGRA: return 4; + default: ghoul_assert(false, "Unknown format"); + } +} + +size_t numberOfBytes(GLenum glType) { + switch (glType) { + case GL_UNSIGNED_BYTE: return sizeof(GLubyte); + case GL_BYTE: return sizeof(GLbyte); + case GL_UNSIGNED_SHORT: return sizeof(GLushort); + case GL_SHORT: return sizeof(GLshort); + case GL_UNSIGNED_INT: return sizeof(GLuint); + case GL_INT: return sizeof(GLint); + case GL_HALF_FLOAT: return sizeof(GLhalf); + case GL_FLOAT: return sizeof(GLfloat); + case GL_DOUBLE: return sizeof(GLdouble); + default: + ghoul_assert(false, "Unknown data type"); + } +} + +size_t getMaximumValue(GLenum glType) { + switch (glType) { + case GL_UNSIGNED_BYTE: + return 1 << 8; + case GL_UNSIGNED_SHORT: + return 1 << 16; + case GL_SHORT: + return 1 << 15; + case GL_UNSIGNED_INT: + return size_t(1) << 32; + case GL_INT: + return 1 << 31; + default: + ghoul_assert(false, "Unknown data type"); + } +} + +float interpretFloat(GLenum glType, const char* src) { + switch (glType) { + case GL_UNSIGNED_BYTE: + return static_cast(*reinterpret_cast(src)); + case GL_UNSIGNED_SHORT: + return static_cast(*reinterpret_cast(src)); + case GL_SHORT: + return static_cast(*reinterpret_cast(src)); + case GL_UNSIGNED_INT: + return static_cast(*reinterpret_cast(src)); + case GL_INT: + return static_cast(*reinterpret_cast(src)); + case GL_HALF_FLOAT: + return static_cast(*reinterpret_cast(src)); + case GL_FLOAT: + return static_cast(*reinterpret_cast(src)); + case GL_DOUBLE: + return static_cast(*reinterpret_cast(src)); + default: + ghoul_assert(false, "Unknown data type"); + } +} + +GLenum glTextureFormat(GLenum glType, ghoul::opengl::Texture::Format format) { + switch (format) { + case ghoul::opengl::Texture::Format::Red: + switch (glType) { + case GL_BYTE: + return GL_R8; + case GL_UNSIGNED_BYTE: + return GL_R8; + case GL_INT: + return GL_R32I; + case GL_UNSIGNED_INT: + return GL_R32UI; + case GL_FLOAT: + return GL_R32F; + case GL_HALF_FLOAT: + return GL_R16F; + default: + ghoul_assert(false, "glType data type unknown"); + LERROR("glType data type unknown: " << glType); + return GLenum(0); + } + break; + case ghoul::opengl::Texture::Format::RG: + switch (glType) { + case GL_BYTE: + return GL_RG8; + case GL_UNSIGNED_BYTE: + return GL_RG8; + case GL_INT: + return GL_RG32I; + case GL_UNSIGNED_INT: + return GL_RG32UI; + case GL_FLOAT: + return GL_RG32F; + case GL_HALF_FLOAT: + return GL_RG16F; + default: + ghoul_assert(false, "glType data type unknown"); + LERROR("glType data type unknown: " << glType); + return GLenum(0); + } + break; + case ghoul::opengl::Texture::Format::RGB: + switch (glType) { + case GL_BYTE: + return GL_RGB8; + case GL_UNSIGNED_BYTE: + return GL_RGB8; + case GL_INT: + return GL_RGB32I; + case GL_UNSIGNED_INT: + return GL_RGB32UI; + case GL_FLOAT: + return GL_RGB32F; + case GL_HALF_FLOAT: + return GL_RGB16F; + default: + ghoul_assert(false, "glType data type unknown"); + LERROR("glType data type unknown: " << glType); + return GLenum(0); + } + break; + case ghoul::opengl::Texture::Format::RGBA: + switch (glType) { + case GL_BYTE: + return GL_RGBA8; + case GL_UNSIGNED_BYTE: + return GL_RGBA8; + case GL_INT: + return GL_RGBA32I; + case GL_UNSIGNED_INT: + return GL_RGBA32UI; + case GL_FLOAT: + return GL_RGBA32F; + case GL_HALF_FLOAT: + return GL_RGBA16F; + default: + ghoul_assert(false, "glType data type unknown"); + LERROR("glType data type unknown: " << glType); + return GLenum(0); + } + break; + case ghoul::opengl::Texture::Format::BGR: + switch (glType) { + case GL_BYTE: + return GL_RGB8; + case GL_UNSIGNED_BYTE: + return GL_RGB8; + case GL_INT: + return GL_RGB32I; + case GL_UNSIGNED_INT: + return GL_RGB32UI; + case GL_FLOAT: + return GL_RGB32F; + case GL_HALF_FLOAT: + return GL_RGB16F; + default: + ghoul_assert(false, "glType data type unknown"); + LERROR("glType data type unknown: " << glType); + return GLenum(0); + } + break; + case ghoul::opengl::Texture::Format::BGRA: + switch (glType) { + case GL_BYTE: + return GL_RGBA8; + case GL_UNSIGNED_BYTE: + return GL_RGBA8; + case GL_INT: + return GL_RGBA32I; + case GL_UNSIGNED_INT: + return GL_RGBA32UI; + case GL_FLOAT: + return GL_RGBA32F; + case GL_HALF_FLOAT: + return GL_RGBA16F; + default: + ghoul_assert(false, "glType data type unknown"); + LERROR("glType data type unknown: " << glType); + return GLenum(0); + } + break; + default: + LERROR( + "Unknown format for OpenGL texture: " << + static_cast + >(format) + ); + return GLenum(0); + break; + } +} + +} // namespace tiledatatype +} // namespace globebrowsing +} // namespace openspace diff --git a/modules/globebrowsing/tile/rawtiledatareader/tiledatatype.h b/modules/globebrowsing/tile/rawtiledatareader/tiledatatype.h index a0961bd6d0..7476b018e8 100644 --- a/modules/globebrowsing/tile/rawtiledatareader/tiledatatype.h +++ b/modules/globebrowsing/tile/rawtiledatareader/tiledatatype.h @@ -1,61 +1,61 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2017 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TILE_DATA_TYPE___H__ -#define __OPENSPACE_MODULE_GLOBEBROWSING___TILE_DATA_TYPE___H__ - -#include -#include - -#include - -#ifdef GLOBEBROWSING_USE_GDAL -#include -#endif // GLOBEBROWSING_USE_GDAL - -namespace openspace { -namespace globebrowsing { -namespace tiledatatype { - -#ifdef GLOBEBROWSING_USE_GDAL - GLenum getOpenGLDataType(GDALDataType gdalType); -GDALDataType getGdalDataType(GLenum glType); -TextureFormat getTextureFormat(int rasterCount, GDALDataType gdalType); -TextureFormat getTextureFormatOptimized(int rasterCount, GDALDataType gdalType); -size_t getMaximumValue(GDALDataType gdalType); -size_t numberOfBytes(GDALDataType gdalType); -float interpretFloat(GDALDataType gdalType, const char* src); -#endif // GLOBEBROWSING_USE_GDAL - -GLenum glTextureFormat(GLenum glType, ghoul::opengl::Texture::Format format); -size_t numberOfRasters(ghoul::opengl::Texture::Format format); -size_t numberOfBytes(GLenum glType); -size_t getMaximumValue(GLenum glType); -float interpretFloat(GLenum glType, const char* src); - -} // namespace tiledatatype -} // namespace globebrowsing -} // namespace openspace - -#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TILE_DATA_TYPE___H__ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TILE_DATA_TYPE___H__ +#define __OPENSPACE_MODULE_GLOBEBROWSING___TILE_DATA_TYPE___H__ + +#include +#include + +#include + +#ifdef GLOBEBROWSING_USE_GDAL +#include +#endif // GLOBEBROWSING_USE_GDAL + +namespace openspace { +namespace globebrowsing { +namespace tiledatatype { + +#ifdef GLOBEBROWSING_USE_GDAL + GLenum getOpenGLDataType(GDALDataType gdalType); +GDALDataType getGdalDataType(GLenum glType); +TextureFormat getTextureFormat(int rasterCount, GDALDataType gdalType); +TextureFormat getTextureFormatOptimized(int rasterCount, GDALDataType gdalType); +size_t getMaximumValue(GDALDataType gdalType); +size_t numberOfBytes(GDALDataType gdalType); +float interpretFloat(GDALDataType gdalType, const char* src); +#endif // GLOBEBROWSING_USE_GDAL + +GLenum glTextureFormat(GLenum glType, ghoul::opengl::Texture::Format format); +size_t numberOfRasters(ghoul::opengl::Texture::Format format); +size_t numberOfBytes(GLenum glType); +size_t getMaximumValue(GLenum glType); +float interpretFloat(GLenum glType, const char* src); + +} // namespace tiledatatype +} // namespace globebrowsing +} // namespace openspace + +#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TILE_DATA_TYPE___H__ diff --git a/modules/globebrowsing/tile/textureformat.h b/modules/globebrowsing/tile/textureformat.h index 607a804faf..a8a9aa9bb3 100644 --- a/modules/globebrowsing/tile/textureformat.h +++ b/modules/globebrowsing/tile/textureformat.h @@ -1,43 +1,43 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2017 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TEXTUREFORMAT___H__ -#define __OPENSPACE_MODULE_GLOBEBROWSING___TEXTUREFORMAT___H__ - -#include -#include - -namespace openspace { -namespace globebrowsing { - -struct TextureFormat { - ghoul::opengl::Texture::Format ghoulFormat; - GLenum glFormat; -}; - - -} // namespace globebrowsing -} // namespace openspace - -#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TEXTUREFORMAT___H__ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TEXTUREFORMAT___H__ +#define __OPENSPACE_MODULE_GLOBEBROWSING___TEXTUREFORMAT___H__ + +#include +#include + +namespace openspace { +namespace globebrowsing { + +struct TextureFormat { + ghoul::opengl::Texture::Format ghoulFormat; + GLenum glFormat; +}; + + +} // namespace globebrowsing +} // namespace openspace + +#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TEXTUREFORMAT___H__ diff --git a/modules/globebrowsing/tile/tiletextureinitdata.cpp b/modules/globebrowsing/tile/tiletextureinitdata.cpp index 31045b8534..eddc426296 100644 --- a/modules/globebrowsing/tile/tiletextureinitdata.cpp +++ b/modules/globebrowsing/tile/tiletextureinitdata.cpp @@ -1,152 +1,152 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2017 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#include -#include - -namespace openspace { -namespace globebrowsing { - -const glm::ivec2 TileTextureInitData::tilePixelStartOffset = glm::ivec2(-2); -const glm::ivec2 TileTextureInitData::tilePixelSizeDifference = glm::ivec2(4); - -TileTextureInitData::TileTextureInitData(size_t width, size_t height, GLenum glType, - Format textureFormat, ShouldAllocateDataOnCPU shouldAllocateDataOnCPU) - : _glType(glType) - , _ghoulTextureFormat(textureFormat) - , _shouldAllocateDataOnCPU(shouldAllocateDataOnCPU) -{ - _dimensionsWithoutPadding = glm::ivec3(width, height, 1); - _dimensionsWithPadding = glm::ivec3( - width + tilePixelSizeDifference.x, height + tilePixelSizeDifference.y, 1); - _nRasters = tiledatatype::numberOfRasters(_ghoulTextureFormat); - _bytesPerDatum = tiledatatype::numberOfBytes(glType); - _bytesPerPixel = _nRasters * _bytesPerDatum; - _bytesPerLine = _bytesPerPixel * _dimensionsWithPadding.x; - _totalNumBytes = _bytesPerLine * _dimensionsWithPadding.y; - _glTextureFormat = tiledatatype::glTextureFormat(_glType, - _ghoulTextureFormat); - calculateHashKey(); -}; - -TileTextureInitData::TileTextureInitData(const TileTextureInitData& original) - : TileTextureInitData( - original.dimensionsWithoutPadding().x, - original.dimensionsWithoutPadding().y, - original.glType(), - original.ghoulTextureFormat(), - original.shouldAllocateDataOnCPU() ? ShouldAllocateDataOnCPU::Yes : ShouldAllocateDataOnCPU::No) -{ }; - -glm::ivec3 TileTextureInitData::dimensionsWithPadding() const { - return _dimensionsWithPadding; -} - -glm::ivec3 TileTextureInitData::dimensionsWithoutPadding() const { - return _dimensionsWithoutPadding; -} - -size_t TileTextureInitData::nRasters() const { - return _nRasters; -} - -size_t TileTextureInitData::bytesPerDatum() const { - return _bytesPerDatum; -} - -size_t TileTextureInitData::bytesPerPixel() const { - return _bytesPerPixel; -} - -size_t TileTextureInitData::bytesPerLine() const { - return _bytesPerLine; -} - -size_t TileTextureInitData::totalNumBytes() const { - return _totalNumBytes; -} - -GLenum TileTextureInitData::glType() const { - return _glType; -} - -TileTextureInitData::Format TileTextureInitData::ghoulTextureFormat() const { - return _ghoulTextureFormat; -} - -GLenum TileTextureInitData::glTextureFormat() const { - return _glTextureFormat; -} - -bool TileTextureInitData::shouldAllocateDataOnCPU() const { - return _shouldAllocateDataOnCPU; -} - -TileTextureInitData::HashKey TileTextureInitData::hashKey() const { - return _hashKey; -} - -void TileTextureInitData::calculateHashKey() { - ghoul_assert(_dimensionsWithoutPadding.x > 0, "Incorrect dimension"); - ghoul_assert(_dimensionsWithoutPadding.y > 0, "Incorrect dimension"); - ghoul_assert(_dimensionsWithoutPadding.x <= 1024, "Incorrect dimension"); - ghoul_assert(_dimensionsWithoutPadding.y <= 1024, "Incorrect dimension"); - ghoul_assert(_dimensionsWithoutPadding.z == 1, "Incorrect dimension"); - unsigned int format = getUniqueIdFromTextureFormat(_ghoulTextureFormat); - ghoul_assert(format < 256, "Incorrect format"); - - _hashKey = 0LL; - - _hashKey |= _dimensionsWithoutPadding.x; - _hashKey |= _dimensionsWithoutPadding.y << 10; - _hashKey |= static_cast>(_glType) << (10 + 16); - _hashKey |= format << (10 + 16 + 4); -}; - -unsigned int TileTextureInitData::getUniqueIdFromTextureFormat( - Format textureFormat) const -{ - using Format = Format; - switch (textureFormat) { - case Format::Red: - return 0; - case Format::RG: - return 1; - case Format::RGB: - return 2; - case Format::BGR: - return 3; - case Format::RGBA: - return 4; - case Format::BGRA: - return 5; - case Format::DepthComponent: - return 6; - default: - ghoul_assert(false, "Unknown texture format"); - } -} - -} // namespace globebrowsing -} // namespace openspace +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include +#include + +namespace openspace { +namespace globebrowsing { + +const glm::ivec2 TileTextureInitData::tilePixelStartOffset = glm::ivec2(-2); +const glm::ivec2 TileTextureInitData::tilePixelSizeDifference = glm::ivec2(4); + +TileTextureInitData::TileTextureInitData(size_t width, size_t height, GLenum glType, + Format textureFormat, ShouldAllocateDataOnCPU shouldAllocateDataOnCPU) + : _glType(glType) + , _ghoulTextureFormat(textureFormat) + , _shouldAllocateDataOnCPU(shouldAllocateDataOnCPU) +{ + _dimensionsWithoutPadding = glm::ivec3(width, height, 1); + _dimensionsWithPadding = glm::ivec3( + width + tilePixelSizeDifference.x, height + tilePixelSizeDifference.y, 1); + _nRasters = tiledatatype::numberOfRasters(_ghoulTextureFormat); + _bytesPerDatum = tiledatatype::numberOfBytes(glType); + _bytesPerPixel = _nRasters * _bytesPerDatum; + _bytesPerLine = _bytesPerPixel * _dimensionsWithPadding.x; + _totalNumBytes = _bytesPerLine * _dimensionsWithPadding.y; + _glTextureFormat = tiledatatype::glTextureFormat(_glType, + _ghoulTextureFormat); + calculateHashKey(); +}; + +TileTextureInitData::TileTextureInitData(const TileTextureInitData& original) + : TileTextureInitData( + original.dimensionsWithoutPadding().x, + original.dimensionsWithoutPadding().y, + original.glType(), + original.ghoulTextureFormat(), + original.shouldAllocateDataOnCPU() ? ShouldAllocateDataOnCPU::Yes : ShouldAllocateDataOnCPU::No) +{ }; + +glm::ivec3 TileTextureInitData::dimensionsWithPadding() const { + return _dimensionsWithPadding; +} + +glm::ivec3 TileTextureInitData::dimensionsWithoutPadding() const { + return _dimensionsWithoutPadding; +} + +size_t TileTextureInitData::nRasters() const { + return _nRasters; +} + +size_t TileTextureInitData::bytesPerDatum() const { + return _bytesPerDatum; +} + +size_t TileTextureInitData::bytesPerPixel() const { + return _bytesPerPixel; +} + +size_t TileTextureInitData::bytesPerLine() const { + return _bytesPerLine; +} + +size_t TileTextureInitData::totalNumBytes() const { + return _totalNumBytes; +} + +GLenum TileTextureInitData::glType() const { + return _glType; +} + +TileTextureInitData::Format TileTextureInitData::ghoulTextureFormat() const { + return _ghoulTextureFormat; +} + +GLenum TileTextureInitData::glTextureFormat() const { + return _glTextureFormat; +} + +bool TileTextureInitData::shouldAllocateDataOnCPU() const { + return _shouldAllocateDataOnCPU; +} + +TileTextureInitData::HashKey TileTextureInitData::hashKey() const { + return _hashKey; +} + +void TileTextureInitData::calculateHashKey() { + ghoul_assert(_dimensionsWithoutPadding.x > 0, "Incorrect dimension"); + ghoul_assert(_dimensionsWithoutPadding.y > 0, "Incorrect dimension"); + ghoul_assert(_dimensionsWithoutPadding.x <= 1024, "Incorrect dimension"); + ghoul_assert(_dimensionsWithoutPadding.y <= 1024, "Incorrect dimension"); + ghoul_assert(_dimensionsWithoutPadding.z == 1, "Incorrect dimension"); + unsigned int format = getUniqueIdFromTextureFormat(_ghoulTextureFormat); + ghoul_assert(format < 256, "Incorrect format"); + + _hashKey = 0LL; + + _hashKey |= _dimensionsWithoutPadding.x; + _hashKey |= _dimensionsWithoutPadding.y << 10; + _hashKey |= static_cast>(_glType) << (10 + 16); + _hashKey |= format << (10 + 16 + 4); +}; + +unsigned int TileTextureInitData::getUniqueIdFromTextureFormat( + Format textureFormat) const +{ + using Format = Format; + switch (textureFormat) { + case Format::Red: + return 0; + case Format::RG: + return 1; + case Format::RGB: + return 2; + case Format::BGR: + return 3; + case Format::RGBA: + return 4; + case Format::BGRA: + return 5; + case Format::DepthComponent: + return 6; + default: + ghoul_assert(false, "Unknown texture format"); + } +} + +} // namespace globebrowsing +} // namespace openspace diff --git a/modules/globebrowsing/tile/tiletextureinitdata.h b/modules/globebrowsing/tile/tiletextureinitdata.h index 874e232933..8dd27c8b0c 100644 --- a/modules/globebrowsing/tile/tiletextureinitdata.h +++ b/modules/globebrowsing/tile/tiletextureinitdata.h @@ -1,91 +1,91 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2017 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TILE_TEXTURE_INIT_DATA___H__ -#define __OPENSPACE_MODULE_GLOBEBROWSING___TILE_TEXTURE_INIT_DATA___H__ - -#include -#include -#include - -#include - -namespace openspace { -namespace globebrowsing { - -/** - * All information needed to create a texture used for a Tile. - */ -class TileTextureInitData -{ -public: - using HashKey = unsigned long long; - using ShouldAllocateDataOnCPU = ghoul::Boolean; - using Format = ghoul::opengl::Texture::Format; - - TileTextureInitData(size_t width, size_t height, GLenum glType, Format textureFormat, - ShouldAllocateDataOnCPU shouldAllocateDataOnCPU = ShouldAllocateDataOnCPU::No); - - TileTextureInitData(const TileTextureInitData& original); - - ~TileTextureInitData() = default; - - glm::ivec3 dimensionsWithPadding() const; - glm::ivec3 dimensionsWithoutPadding() const; - size_t nRasters() const; - size_t bytesPerDatum() const; - size_t bytesPerPixel() const; - size_t bytesPerLine() const; - size_t totalNumBytes() const; - GLenum glType() const; - Format ghoulTextureFormat() const; - GLenum glTextureFormat() const; - bool shouldAllocateDataOnCPU() const; - HashKey hashKey() const; - - const static glm::ivec2 tilePixelStartOffset; - const static glm::ivec2 tilePixelSizeDifference; - -private: - void calculateHashKey(); - unsigned int getUniqueIdFromTextureFormat(Format textureFormat) const; - - HashKey _hashKey; - glm::ivec3 _dimensionsWithPadding; - glm::ivec3 _dimensionsWithoutPadding; - GLenum _glType; - Format _ghoulTextureFormat; - GLenum _glTextureFormat; - size_t _nRasters; - size_t _bytesPerDatum; - size_t _bytesPerPixel; - size_t _bytesPerLine; - size_t _totalNumBytes; - bool _shouldAllocateDataOnCPU; -}; - -} // namespace globebrowsing -} // namespace openspace - -#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TILE_TEXTURE_INIT_DATA___H__ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TILE_TEXTURE_INIT_DATA___H__ +#define __OPENSPACE_MODULE_GLOBEBROWSING___TILE_TEXTURE_INIT_DATA___H__ + +#include +#include +#include + +#include + +namespace openspace { +namespace globebrowsing { + +/** + * All information needed to create a texture used for a Tile. + */ +class TileTextureInitData +{ +public: + using HashKey = unsigned long long; + using ShouldAllocateDataOnCPU = ghoul::Boolean; + using Format = ghoul::opengl::Texture::Format; + + TileTextureInitData(size_t width, size_t height, GLenum glType, Format textureFormat, + ShouldAllocateDataOnCPU shouldAllocateDataOnCPU = ShouldAllocateDataOnCPU::No); + + TileTextureInitData(const TileTextureInitData& original); + + ~TileTextureInitData() = default; + + glm::ivec3 dimensionsWithPadding() const; + glm::ivec3 dimensionsWithoutPadding() const; + size_t nRasters() const; + size_t bytesPerDatum() const; + size_t bytesPerPixel() const; + size_t bytesPerLine() const; + size_t totalNumBytes() const; + GLenum glType() const; + Format ghoulTextureFormat() const; + GLenum glTextureFormat() const; + bool shouldAllocateDataOnCPU() const; + HashKey hashKey() const; + + const static glm::ivec2 tilePixelStartOffset; + const static glm::ivec2 tilePixelSizeDifference; + +private: + void calculateHashKey(); + unsigned int getUniqueIdFromTextureFormat(Format textureFormat) const; + + HashKey _hashKey; + glm::ivec3 _dimensionsWithPadding; + glm::ivec3 _dimensionsWithoutPadding; + GLenum _glType; + Format _ghoulTextureFormat; + GLenum _glTextureFormat; + size_t _nRasters; + size_t _bytesPerDatum; + size_t _bytesPerPixel; + size_t _bytesPerLine; + size_t _totalNumBytes; + bool _shouldAllocateDataOnCPU; +}; + +} // namespace globebrowsing +} // namespace openspace + +#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TILE_TEXTURE_INIT_DATA___H__ diff --git a/modules/newhorizons/util/projectioncomponent.cpp b/modules/newhorizons/util/projectioncomponent.cpp index e31b2883c2..e22fee5148 100644 --- a/modules/newhorizons/util/projectioncomponent.cpp +++ b/modules/newhorizons/util/projectioncomponent.cpp @@ -1,950 +1,950 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2017 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { - const char* keyPotentialTargets = "PotentialTargets"; - - const char* keyInstrument = "Instrument.Name"; - const char* keyInstrumentFovy = "Instrument.Fovy"; - const char* keyInstrumentAspect = "Instrument.Aspect"; - - const char* keyTranslation = "DataInputTranslation"; - - const char* keyProjObserver = "Observer"; - const char* keyProjTarget = "Target"; - const char* keyProjAberration = "Aberration"; - - const char* keySequenceDir = "Sequence"; - const char* keySequenceType = "SequenceType"; - - const char* keyNeedsTextureMapDilation = "TextureMap"; - const char* keyNeedsShadowing = "ShadowMap"; - const char* keyTextureMapAspectRatio = "AspectRatio"; - - const char* sequenceTypeImage = "image-sequence"; - const char* sequenceTypePlaybook = "playbook"; - const char* sequenceTypeHybrid = "hybrid"; - const char* sequenceTypeInstrumentTimes = "instrument-times"; - - const char* placeholderFile = - "${OPENSPACE_DATA}/scene/common/textures/placeholder.png"; - - const char* _loggerCat = "ProjectionComponent"; -} - -namespace openspace { - -using ghoul::Dictionary; -using glm::ivec2; - -documentation::Documentation ProjectionComponent::Documentation() { - using namespace documentation; - return { - "Projection Component", - "newhorizons_projectioncomponent", - { - { - keyInstrument, - new StringAnnotationVerifier("A SPICE name of an instrument"), - "The instrument that is used to perform the projections", - Optional::No - }, - { - keyInstrumentFovy, - new DoubleVerifier, - "The field of view in degrees along the y axis", - Optional::No - }, - { - keyInstrumentAspect, - new DoubleVerifier, - "The aspect ratio of the instrument in relation between x and y axis", - Optional::No - }, - { - keyProjObserver, - new StringAnnotationVerifier("A SPICE name of the observing object"), - "The observer that is doing the projection. This has to be a valid SPICE " - "name or SPICE integer.", - Optional::No - }, - { - keyProjTarget, - new StringAnnotationVerifier("A SPICE name of the observed object"), - "The observed object that is projected on. This has to be a valid SPICE " - "name or SPICE integer.", - Optional::No - }, - { - keyProjAberration, - new StringInListVerifier({ - // from SpiceManager::AberrationCorrection::AberrationCorrection - "NONE", "LT", "LT+S", "CN", "CN+S", "XLT", "XLT+S", "XCN", "XCN+S" - }), - "The aberration correction that is supposed to be used for the " - "projection. The values for the correction correspond to the SPICE " - "definition as described in " - "ftp://naif.jpl.nasa.gov/pub/naif/toolkit_docs/IDL/cspice/spkezr_c.html", - Optional::No - }, - { - keyPotentialTargets, - new StringListVerifier, - "The list of potential targets that are involved with the image " - "projection", - Optional::Yes - }, - { - keyNeedsTextureMapDilation, - new BoolVerifier, - "Determines whether a dilation step of the texture map has to be " - "performed after each projection. This is necessary if the texture of " - "the projected object is a texture map where the borders are not " - "touching. The default value is 'false'.", - Optional::Yes - }, - { - keyNeedsShadowing, - new BoolVerifier, - "Determines whether the object requires a self-shadowing algorithm. This " - "is necessary if the object is concave and might cast a shadow on itself " - "during presentation. The default value is 'false'.", - Optional::Yes - }, - { - keyTextureMapAspectRatio, - new DoubleVerifier, - "Sets the desired aspect ratio of the projected texture. This might be " - "necessary as planets usually have 2x1 aspect ratios, whereas this does " - "not hold for non-planet objects (comets, asteroids, etc). The default " - "value is '1.0'.", - Optional::Yes - } - - } - }; -} - -ProjectionComponent::ProjectionComponent() - : properties::PropertyOwner("ProjectionComponent") - , _performProjection("performProjection", "Perform Projections", true) - , _clearAllProjections("clearAllProjections", "Clear Projections", false) - , _projectionFading("projectionFading", "Projection Fading", 1.f, 0.f, 1.f) - , _textureSize("textureSize", "Texture Size", ivec2(16), ivec2(16), ivec2(32768)) - , _applyTextureSize("applyTextureSize", "Apply Texture Size") - , _textureSizeDirty(false) - , _projectionTexture(nullptr) -{ - _shadowing.isEnabled = false; - _dilation.isEnabled = false; - - addProperty(_performProjection); - addProperty(_clearAllProjections); - addProperty(_projectionFading); - - addProperty(_textureSize); - addProperty(_applyTextureSize); - _applyTextureSize.onChange([this]() { _textureSizeDirty = true; }); -} - -void ProjectionComponent::initialize(const ghoul::Dictionary& dictionary) { - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "ProjectionComponent" - ); - _instrumentID = dictionary.value(keyInstrument); - _projectorID = dictionary.value(keyProjObserver); - _projecteeID = dictionary.value(keyProjTarget); - _fovy = static_cast(dictionary.value(keyInstrumentFovy)); - _aspectRatio = static_cast(dictionary.value(keyInstrumentAspect)); - - _aberration = SpiceManager::AberrationCorrection( - dictionary.value(keyProjAberration) - ); - - if (dictionary.hasKeyAndValue(keyPotentialTargets)) { - ghoul::Dictionary potentialTargets = dictionary.value( - keyPotentialTargets - ); - - _potentialTargets.reserve(potentialTargets.size()); - for (int i = 1; i <= potentialTargets.size(); ++i) { - _potentialTargets.emplace_back( - potentialTargets.value(std::to_string(i)) - ); - } - } - - if (dictionary.hasKeyAndValue(keyNeedsTextureMapDilation)) { - _dilation.isEnabled = dictionary.value(keyNeedsTextureMapDilation); - } - - if (dictionary.hasKeyAndValue(keyNeedsShadowing)) { - _shadowing.isEnabled = dictionary.value(keyNeedsShadowing); - } - - _projectionTextureAspectRatio = 1.f; - if (dictionary.hasKeyAndValue(keyTextureMapAspectRatio)) { - _projectionTextureAspectRatio = - static_cast(dictionary.value(keyTextureMapAspectRatio)); - } - - std::string name; - dictionary.getValue(SceneGraphNode::KeyName, name); - - std::vector parsers; - - std::string sequenceSource; - std::string sequenceType; - bool foundSequence = dictionary.getValue(keySequenceDir, sequenceSource); - if (foundSequence) { - sequenceSource = absPath(sequenceSource); - - dictionary.getValue(keySequenceType, sequenceType); - //Important: client must define translation-list in mod file IFF playbook - if (dictionary.hasKey(keyTranslation)) { - ghoul::Dictionary translationDictionary; - //get translation dictionary - dictionary.getValue(keyTranslation, translationDictionary); - - if (sequenceType == sequenceTypePlaybook) { - parsers.push_back(new HongKangParser( - name, - sequenceSource, - _projectorID, - translationDictionary, - _potentialTargets)); - } - else if (sequenceType == sequenceTypeImage) { - parsers.push_back(new LabelParser( - name, - sequenceSource, - translationDictionary)); - } - else if (sequenceType == sequenceTypeHybrid) { - //first read labels - parsers.push_back(new LabelParser( - name, - sequenceSource, - translationDictionary)); - - std::string _eventFile; - bool foundEventFile = dictionary.getValue("EventFile", _eventFile); - if (foundEventFile) { - //then read playbook - _eventFile = absPath(_eventFile); - parsers.push_back(new HongKangParser( - name, - _eventFile, - _projectorID, - translationDictionary, - _potentialTargets)); - } - else { - LWARNING("No eventfile has been provided, please check modfiles"); - } - } - else if (sequenceType == sequenceTypeInstrumentTimes) { - parsers.push_back(new InstrumentTimesParser( - name, - sequenceSource, - translationDictionary)); - } - - for (SequenceParser* parser : parsers) { - openspace::ImageSequencer::ref().runSequenceParser(parser); - delete parser; - } - } - else { - LWARNING("No playbook translation provided, please make sure all spice calls match playbook!"); - } - } -} - -bool ProjectionComponent::initializeGL() { - int maxSize = OpenGLCap.max2DTextureSize(); - glm::ivec2 size; - - if (_projectionTextureAspectRatio > 1.f) { - size.x = maxSize; - size.y = static_cast(maxSize / _projectionTextureAspectRatio); - } - else { - size.x = static_cast(maxSize * _projectionTextureAspectRatio); - size.y = maxSize; - } - - _textureSize.setMaxValue(size); - _textureSize = size / 2; - - // We only want to use half the resolution per default: - size /= 2; - - bool success = generateProjectionLayerTexture(size); - success &= generateDepthTexture(size); - success &= auxiliaryRendertarget(); - success &= depthRendertarget(); - - using std::unique_ptr; - using ghoul::opengl::Texture; - using ghoul::io::TextureReader; - - unique_ptr texture = TextureReader::ref().loadTexture(absPath(placeholderFile)); - if (texture) { - texture->uploadTexture(); - // TODO: AnisotropicMipMap crashes on ATI cards ---abock - //_textureProj->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); - texture->setFilter(Texture::FilterMode::Linear); - texture->setWrapping(Texture::WrappingMode::ClampToBorder); - } - _placeholderTexture = std::move(texture); - - if (_dilation.isEnabled) { - _dilation.program = ghoul::opengl::ProgramObject::Build( - "Dilation", - "${MODULE_NEWHORIZONS}/shaders/dilation_vs.glsl", - "${MODULE_NEWHORIZONS}/shaders/dilation_fs.glsl" - ); - - const GLfloat plane[] = { - -1, -1, - 1, 1, - -1, 1, - -1, -1, - 1, -1, - 1, 1, - }; - - glGenVertexArrays(1, &_dilation.vao); - glGenBuffers(1, &_dilation.vbo); - - glBindVertexArray(_dilation.vao); - glBindBuffer(GL_ARRAY_BUFFER, _dilation.vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(plane), plane, GL_STATIC_DRAW); - glEnableVertexAttribArray(0); - glVertexAttribPointer( - 0, - 2, - GL_FLOAT, - GL_FALSE, - sizeof(GLfloat) * 2, - reinterpret_cast(0) - ); - - glBindVertexArray(0); - } - - return success; -} - -bool ProjectionComponent::deinitialize() { - _projectionTexture = nullptr; - - glDeleteFramebuffers(1, &_fboID); - - if (_dilation.isEnabled) { - glDeleteFramebuffers(1, &_dilation.fbo); - glDeleteVertexArrays(1, &_dilation.vao); - glDeleteBuffers(1, &_dilation.vbo); - - _dilation.program = nullptr; - _dilation.texture = nullptr; - } - - return true; -} - -bool ProjectionComponent::isReady() const { - return (_projectionTexture != nullptr); -} - -void ProjectionComponent::imageProjectBegin() { - // keep handle to the current bound FBO - glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_defaultFBO); - - if (_textureSizeDirty) { - LDEBUG("Changing texture size to " << std::to_string(_textureSize)); - - // If the texture size has changed, we have to allocate new memory and copy - // the image texture to the new target - - using ghoul::opengl::Texture; - using ghoul::opengl::FramebufferObject; - - // Make a copy of the old textures - std::unique_ptr oldProjectionTexture = std::move(_projectionTexture); - std::unique_ptr oldDilationStencil = std::move(_dilation.stencilTexture); - std::unique_ptr oldDilationTexture = std::move(_dilation.texture); - std::unique_ptr oldDepthTexture = std::move(_shadowing.texture); - - // Generate the new textures - generateProjectionLayerTexture(_textureSize); - - if (_shadowing.isEnabled) { - generateDepthTexture(_textureSize); - } - - auto copyFramebuffers = [](Texture* src, Texture* dst, const std::string& msg) { - glFramebufferTexture( - GL_READ_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - *src, - 0 - ); - - GLenum status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER); - if (!FramebufferObject::errorChecking(status).empty()) { - LERROR( - "Read Buffer (" << msg << "): " << - FramebufferObject::errorChecking(status) - ); - } - - glFramebufferTexture( - GL_DRAW_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - *dst, - 0 - ); - - status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); - if (!FramebufferObject::errorChecking(status).empty()) { - LERROR( - "Draw Buffer (" << msg << "): " << - FramebufferObject::errorChecking(status) - ); - } - - glBlitFramebuffer( - 0, 0, - src->dimensions().x, src->dimensions().y, - 0, 0, - dst->dimensions().x, dst->dimensions().y, - GL_COLOR_BUFFER_BIT, - GL_LINEAR - ); - }; - - auto copyDepthBuffer = [](Texture* src, Texture* dst, const std::string& msg) { - glFramebufferTexture( - GL_READ_FRAMEBUFFER, - GL_DEPTH_ATTACHMENT, - *src, - 0 - ); - - GLenum status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER); - if (!FramebufferObject::errorChecking(status).empty()) { - LERROR( - "Read Buffer (" << msg << "): " << - FramebufferObject::errorChecking(status) - ); - } - - glFramebufferTexture( - GL_DRAW_FRAMEBUFFER, - GL_DEPTH_ATTACHMENT, - *dst, - 0 - ); - - status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); - if (!FramebufferObject::errorChecking(status).empty()) { - LERROR( - "Draw Buffer (" << msg << "): " << - FramebufferObject::errorChecking(status) - ); - } - - glBlitFramebuffer( - 0, 0, - src->dimensions().x, src->dimensions().y, - 0, 0, - dst->dimensions().x, dst->dimensions().y, - GL_DEPTH_BUFFER_BIT, - GL_NEAREST - ); - }; - - GLuint fbos[2]; - glGenFramebuffers(2, fbos); - glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[0]); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[1]); - - copyFramebuffers( - oldProjectionTexture.get(), - _projectionTexture.get(), - "Projection" - ); - - if (_dilation.isEnabled) { - copyFramebuffers( - oldDilationStencil.get(), - _dilation.stencilTexture.get(), - "Dilation Stencil" - ); - - copyFramebuffers( - oldDilationTexture.get(), - _dilation.texture.get(), - "Dilation Texture" - ); - } - - if (_shadowing.isEnabled) { - copyDepthBuffer( - oldDepthTexture.get(), - _shadowing.texture.get(), - "Shadowing" - ); - } - - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - glDeleteFramebuffers(2, fbos); - - glBindFramebuffer(GL_FRAMEBUFFER, _fboID); - glFramebufferTexture2D( - GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - *_projectionTexture, - 0 - ); - - if (_dilation.isEnabled) { - // We only need the stencil texture if we need to dilate - glFramebufferTexture2D( - GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT1, - GL_TEXTURE_2D, - *_dilation.stencilTexture, - 0 - ); - - glBindFramebuffer(GL_FRAMEBUFFER, _dilation.fbo); - glFramebufferTexture2D( - GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - *_dilation.texture, - 0 - ); - } - - if (_shadowing.isEnabled) { - glBindFramebuffer(GL_FRAMEBUFFER, _depthFboID); - glFramebufferTexture2D( - GL_FRAMEBUFFER, - GL_DEPTH_ATTACHMENT, - GL_TEXTURE_2D, - *_shadowing.texture, - 0 - ); - } - - _textureSizeDirty = false; - } - - glGetIntegerv(GL_VIEWPORT, _viewport); - glBindFramebuffer(GL_FRAMEBUFFER, _fboID); - - glViewport( - 0, 0, - static_cast(_projectionTexture->width()), - static_cast(_projectionTexture->height()) - ); - - if (_dilation.isEnabled) { - GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; - glDrawBuffers(2, buffers); - } -} - -bool ProjectionComponent::needsShadowMap() const { - return _shadowing.isEnabled; -} - -ghoul::opengl::Texture& ProjectionComponent::depthTexture() { - return *_shadowing.texture; -} - -void ProjectionComponent::depthMapRenderBegin() { - ghoul_assert(_shadowing.isEnabled, "Shadowing is not enabled"); - - // keep handle to the current bound FBO - glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_defaultFBO); - glGetIntegerv(GL_VIEWPORT, _viewport); - - glBindFramebuffer(GL_FRAMEBUFFER, _depthFboID); - glEnable(GL_DEPTH_TEST); - - glViewport( - 0, 0, - static_cast(_shadowing.texture->width()), - static_cast(_shadowing.texture->height()) - ); - - glClear(GL_DEPTH_BUFFER_BIT); -} - -void ProjectionComponent::depthMapRenderEnd() { - glBindFramebuffer(GL_FRAMEBUFFER, _defaultFBO); - glViewport(_viewport[0], _viewport[1], _viewport[2], _viewport[3]); -} - -void ProjectionComponent::imageProjectEnd() { - if (_dilation.isEnabled) { - glBindFramebuffer(GL_FRAMEBUFFER, _dilation.fbo); - - glDisable(GL_BLEND); - - ghoul::opengl::TextureUnit unit[2]; - unit[0].activate(); - _projectionTexture->bind(); - - unit[1].activate(); - _dilation.stencilTexture->bind(); - - _dilation.program->activate(); - _dilation.program->setUniform("tex", unit[0]); - _dilation.program->setUniform("stencil", unit[1]); - - glBindVertexArray(_dilation.vao); - glDrawArrays(GL_TRIANGLES, 0, 6); - - _dilation.program->deactivate(); - - glEnable(GL_BLEND); - } - - glBindFramebuffer(GL_FRAMEBUFFER, _defaultFBO); - glViewport(_viewport[0], _viewport[1], _viewport[2], _viewport[3]); -} - -void ProjectionComponent::update() { - if (_dilation.isEnabled && _dilation.program->isDirty()) { - _dilation.program->rebuildFromFile(); - } -} - -bool ProjectionComponent::depthRendertarget() { - GLint defaultFBO; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); - // setup FBO - glGenFramebuffers(1, &_depthFboID); - glBindFramebuffer(GL_FRAMEBUFFER, _depthFboID); - glFramebufferTexture2D( - GL_FRAMEBUFFER, - GL_DEPTH_ATTACHMENT, - GL_TEXTURE_2D, - *_shadowing.texture, - 0); - - glDrawBuffer(GL_NONE); - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) - return false; - - glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); - return true; -} - -bool ProjectionComponent::auxiliaryRendertarget() { - bool completeSuccess = true; - - GLint defaultFBO; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); - - // setup FBO - glGenFramebuffers(1, &_fboID); - glBindFramebuffer(GL_FRAMEBUFFER, _fboID); - glFramebufferTexture2D( - GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - *_projectionTexture, - 0 - ); - // check FBO status - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - LERROR("Main Framebuffer incomplete"); - completeSuccess &= false; - } - - - if (_dilation.isEnabled) { - // We only need the stencil texture if we need to dilate - glFramebufferTexture2D( - GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT1, - GL_TEXTURE_2D, - *_dilation.stencilTexture, - 0 - ); - - // check FBO status - status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - LERROR("Main Framebuffer incomplete"); - completeSuccess &= false; - } - - glGenFramebuffers(1, &_dilation.fbo); - glBindFramebuffer(GL_FRAMEBUFFER, _dilation.fbo); - glFramebufferTexture2D( - GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - *_dilation.texture, - 0 - ); - - // check FBO status - status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - LERROR("Dilation Framebuffer incomplete"); - completeSuccess &= false; - } - } - - // switch back to window-system-provided framebuffer - glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); - - return completeSuccess; -} - -glm::mat4 ProjectionComponent::computeProjectorMatrix(const glm::vec3 loc, glm::dvec3 aim, - const glm::vec3 up, - const glm::dmat3& instrumentMatrix, - float fieldOfViewY, - float aspectRatio, - float nearPlane, float farPlane, - glm::vec3& boreSight) -{ - - //rotate boresight into correct alignment - boreSight = instrumentMatrix*aim; - glm::vec3 uptmp(instrumentMatrix*glm::dvec3(up)); - - // create view matrix - glm::vec3 e3 = glm::normalize(-boreSight); - glm::vec3 e1 = glm::normalize(glm::cross(uptmp, e3)); - glm::vec3 e2 = glm::normalize(glm::cross(e3, e1)); - - glm::mat4 projViewMatrix = glm::mat4(e1.x, e2.x, e3.x, 0.f, - e1.y, e2.y, e3.y, 0.f, - e1.z, e2.z, e3.z, 0.f, - glm::dot(e1, -loc), glm::dot(e2, -loc), glm::dot(e3, -loc), 1.f); - // create perspective projection matrix - glm::mat4 projProjectionMatrix = glm::perspective(glm::radians(fieldOfViewY), aspectRatio, nearPlane, farPlane); - - return projProjectionMatrix*projViewMatrix; -} - -bool ProjectionComponent::doesPerformProjection() const { - return _performProjection; -} - -bool ProjectionComponent::needsClearProjection() const { - return _clearAllProjections; -} - -float ProjectionComponent::projectionFading() const { - return _projectionFading; -} - -ghoul::opengl::Texture& ProjectionComponent::projectionTexture() const { - if (_dilation.isEnabled) { - return *_dilation.texture; - } - else { - return *_projectionTexture; - } -} - -std::string ProjectionComponent::projectorId() const { - return _projectorID; -} - -std::string ProjectionComponent::projecteeId() const { - return _projecteeID; -} - -std::string ProjectionComponent::instrumentId() const { - return _instrumentID; -} - -SpiceManager::AberrationCorrection ProjectionComponent::aberration() const { - return _aberration; -} - -float ProjectionComponent::fieldOfViewY() const { - return _fovy; -} - -float ProjectionComponent::aspectRatio() const { - return _aspectRatio; -} - -void ProjectionComponent::clearAllProjections() { - // keep handle to the current bound FBO - GLint defaultFBO; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); - - GLint m_viewport[4]; - glGetIntegerv(GL_VIEWPORT, m_viewport); - //counter = 0; - glViewport(0, 0, static_cast(_projectionTexture->width()), static_cast(_projectionTexture->height())); - - glBindFramebuffer(GL_FRAMEBUFFER, _fboID); - - glClearColor(0.f, 0.f, 0.f, 0.f); - glClear(GL_COLOR_BUFFER_BIT); - - if (_dilation.isEnabled) { - glBindFramebuffer(GL_FRAMEBUFFER, _dilation.fbo); - glClear(GL_COLOR_BUFFER_BIT); - } - - glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); - glViewport(m_viewport[0], m_viewport[1], - m_viewport[2], m_viewport[3]); - - _clearAllProjections = false; -} - -std::shared_ptr ProjectionComponent::loadProjectionTexture( - const std::string& texturePath, - bool isPlaceholder) -{ - using std::unique_ptr; - using ghoul::opengl::Texture; - using ghoul::io::TextureReader; - - - if (isPlaceholder) { - return _placeholderTexture; - } - - - unique_ptr texture = TextureReader::ref().loadTexture(absPath(texturePath)); - if (texture) { - if (texture->format() == Texture::Format::Red) - ghoul::opengl::convertTextureFormat(*texture, Texture::Format::RGB); - texture->uploadTexture(); - // TODO: AnisotropicMipMap crashes on ATI cards ---abock - //_textureProj->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); - texture->setFilter(Texture::FilterMode::Linear); - texture->setWrapping(Texture::WrappingMode::ClampToBorder); - } - return std::move(texture); -} - -bool ProjectionComponent::generateProjectionLayerTexture(const ivec2& size) { - LINFO( - "Creating projection texture of size '" << size.x << ", " << size.y << "'" - ); - _projectionTexture = std::make_unique ( - glm::uvec3(size, 1), - ghoul::opengl::Texture::Format::RGBA - ); - if (_projectionTexture) { - _projectionTexture->uploadTexture(); - //_projectionTexture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); - } - - if (_dilation.isEnabled) { - _dilation.texture = std::make_unique( - glm::uvec3(size, 1), - ghoul::opengl::Texture::Format::RGBA - ); - - if (_dilation.texture) { - _dilation.texture->uploadTexture(); - //_dilation.texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); - } - - _dilation.stencilTexture = std::make_unique( - glm::uvec3(size, 1), - ghoul::opengl::Texture::Format::Red, - // @TODO: Remove the static cast ---abock - static_cast(ghoul::opengl::Texture::Format::Red) - ); - - if (_dilation.stencilTexture) { - _dilation.stencilTexture->uploadTexture(); - //_dilation.texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); - } - } - - - return _projectionTexture != nullptr; - -} - -bool ProjectionComponent::generateDepthTexture(const ivec2& size) { - LINFO( - "Creating depth texture of size '" << size.x << ", " << size.y << "'" - ); - - _shadowing.texture = std::make_unique( - glm::uvec3(size, 1), - ghoul::opengl::Texture::Format::DepthComponent, - GL_DEPTH_COMPONENT32F - ); - - if (_shadowing.texture) { - _shadowing.texture->uploadTexture(); - } - - return _shadowing.texture != nullptr; - -} - -} // namespace openspace +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + const char* keyPotentialTargets = "PotentialTargets"; + + const char* keyInstrument = "Instrument.Name"; + const char* keyInstrumentFovy = "Instrument.Fovy"; + const char* keyInstrumentAspect = "Instrument.Aspect"; + + const char* keyTranslation = "DataInputTranslation"; + + const char* keyProjObserver = "Observer"; + const char* keyProjTarget = "Target"; + const char* keyProjAberration = "Aberration"; + + const char* keySequenceDir = "Sequence"; + const char* keySequenceType = "SequenceType"; + + const char* keyNeedsTextureMapDilation = "TextureMap"; + const char* keyNeedsShadowing = "ShadowMap"; + const char* keyTextureMapAspectRatio = "AspectRatio"; + + const char* sequenceTypeImage = "image-sequence"; + const char* sequenceTypePlaybook = "playbook"; + const char* sequenceTypeHybrid = "hybrid"; + const char* sequenceTypeInstrumentTimes = "instrument-times"; + + const char* placeholderFile = + "${OPENSPACE_DATA}/scene/common/textures/placeholder.png"; + + const char* _loggerCat = "ProjectionComponent"; +} + +namespace openspace { + +using ghoul::Dictionary; +using glm::ivec2; + +documentation::Documentation ProjectionComponent::Documentation() { + using namespace documentation; + return { + "Projection Component", + "newhorizons_projectioncomponent", + { + { + keyInstrument, + new StringAnnotationVerifier("A SPICE name of an instrument"), + "The instrument that is used to perform the projections", + Optional::No + }, + { + keyInstrumentFovy, + new DoubleVerifier, + "The field of view in degrees along the y axis", + Optional::No + }, + { + keyInstrumentAspect, + new DoubleVerifier, + "The aspect ratio of the instrument in relation between x and y axis", + Optional::No + }, + { + keyProjObserver, + new StringAnnotationVerifier("A SPICE name of the observing object"), + "The observer that is doing the projection. This has to be a valid SPICE " + "name or SPICE integer.", + Optional::No + }, + { + keyProjTarget, + new StringAnnotationVerifier("A SPICE name of the observed object"), + "The observed object that is projected on. This has to be a valid SPICE " + "name or SPICE integer.", + Optional::No + }, + { + keyProjAberration, + new StringInListVerifier({ + // from SpiceManager::AberrationCorrection::AberrationCorrection + "NONE", "LT", "LT+S", "CN", "CN+S", "XLT", "XLT+S", "XCN", "XCN+S" + }), + "The aberration correction that is supposed to be used for the " + "projection. The values for the correction correspond to the SPICE " + "definition as described in " + "ftp://naif.jpl.nasa.gov/pub/naif/toolkit_docs/IDL/cspice/spkezr_c.html", + Optional::No + }, + { + keyPotentialTargets, + new StringListVerifier, + "The list of potential targets that are involved with the image " + "projection", + Optional::Yes + }, + { + keyNeedsTextureMapDilation, + new BoolVerifier, + "Determines whether a dilation step of the texture map has to be " + "performed after each projection. This is necessary if the texture of " + "the projected object is a texture map where the borders are not " + "touching. The default value is 'false'.", + Optional::Yes + }, + { + keyNeedsShadowing, + new BoolVerifier, + "Determines whether the object requires a self-shadowing algorithm. This " + "is necessary if the object is concave and might cast a shadow on itself " + "during presentation. The default value is 'false'.", + Optional::Yes + }, + { + keyTextureMapAspectRatio, + new DoubleVerifier, + "Sets the desired aspect ratio of the projected texture. This might be " + "necessary as planets usually have 2x1 aspect ratios, whereas this does " + "not hold for non-planet objects (comets, asteroids, etc). The default " + "value is '1.0'.", + Optional::Yes + } + + } + }; +} + +ProjectionComponent::ProjectionComponent() + : properties::PropertyOwner("ProjectionComponent") + , _performProjection("performProjection", "Perform Projections", true) + , _clearAllProjections("clearAllProjections", "Clear Projections", false) + , _projectionFading("projectionFading", "Projection Fading", 1.f, 0.f, 1.f) + , _textureSize("textureSize", "Texture Size", ivec2(16), ivec2(16), ivec2(32768)) + , _applyTextureSize("applyTextureSize", "Apply Texture Size") + , _textureSizeDirty(false) + , _projectionTexture(nullptr) +{ + _shadowing.isEnabled = false; + _dilation.isEnabled = false; + + addProperty(_performProjection); + addProperty(_clearAllProjections); + addProperty(_projectionFading); + + addProperty(_textureSize); + addProperty(_applyTextureSize); + _applyTextureSize.onChange([this]() { _textureSizeDirty = true; }); +} + +void ProjectionComponent::initialize(const ghoul::Dictionary& dictionary) { + documentation::testSpecificationAndThrow( + Documentation(), + dictionary, + "ProjectionComponent" + ); + _instrumentID = dictionary.value(keyInstrument); + _projectorID = dictionary.value(keyProjObserver); + _projecteeID = dictionary.value(keyProjTarget); + _fovy = static_cast(dictionary.value(keyInstrumentFovy)); + _aspectRatio = static_cast(dictionary.value(keyInstrumentAspect)); + + _aberration = SpiceManager::AberrationCorrection( + dictionary.value(keyProjAberration) + ); + + if (dictionary.hasKeyAndValue(keyPotentialTargets)) { + ghoul::Dictionary potentialTargets = dictionary.value( + keyPotentialTargets + ); + + _potentialTargets.reserve(potentialTargets.size()); + for (int i = 1; i <= potentialTargets.size(); ++i) { + _potentialTargets.emplace_back( + potentialTargets.value(std::to_string(i)) + ); + } + } + + if (dictionary.hasKeyAndValue(keyNeedsTextureMapDilation)) { + _dilation.isEnabled = dictionary.value(keyNeedsTextureMapDilation); + } + + if (dictionary.hasKeyAndValue(keyNeedsShadowing)) { + _shadowing.isEnabled = dictionary.value(keyNeedsShadowing); + } + + _projectionTextureAspectRatio = 1.f; + if (dictionary.hasKeyAndValue(keyTextureMapAspectRatio)) { + _projectionTextureAspectRatio = + static_cast(dictionary.value(keyTextureMapAspectRatio)); + } + + std::string name; + dictionary.getValue(SceneGraphNode::KeyName, name); + + std::vector parsers; + + std::string sequenceSource; + std::string sequenceType; + bool foundSequence = dictionary.getValue(keySequenceDir, sequenceSource); + if (foundSequence) { + sequenceSource = absPath(sequenceSource); + + dictionary.getValue(keySequenceType, sequenceType); + //Important: client must define translation-list in mod file IFF playbook + if (dictionary.hasKey(keyTranslation)) { + ghoul::Dictionary translationDictionary; + //get translation dictionary + dictionary.getValue(keyTranslation, translationDictionary); + + if (sequenceType == sequenceTypePlaybook) { + parsers.push_back(new HongKangParser( + name, + sequenceSource, + _projectorID, + translationDictionary, + _potentialTargets)); + } + else if (sequenceType == sequenceTypeImage) { + parsers.push_back(new LabelParser( + name, + sequenceSource, + translationDictionary)); + } + else if (sequenceType == sequenceTypeHybrid) { + //first read labels + parsers.push_back(new LabelParser( + name, + sequenceSource, + translationDictionary)); + + std::string _eventFile; + bool foundEventFile = dictionary.getValue("EventFile", _eventFile); + if (foundEventFile) { + //then read playbook + _eventFile = absPath(_eventFile); + parsers.push_back(new HongKangParser( + name, + _eventFile, + _projectorID, + translationDictionary, + _potentialTargets)); + } + else { + LWARNING("No eventfile has been provided, please check modfiles"); + } + } + else if (sequenceType == sequenceTypeInstrumentTimes) { + parsers.push_back(new InstrumentTimesParser( + name, + sequenceSource, + translationDictionary)); + } + + for (SequenceParser* parser : parsers) { + openspace::ImageSequencer::ref().runSequenceParser(parser); + delete parser; + } + } + else { + LWARNING("No playbook translation provided, please make sure all spice calls match playbook!"); + } + } +} + +bool ProjectionComponent::initializeGL() { + int maxSize = OpenGLCap.max2DTextureSize(); + glm::ivec2 size; + + if (_projectionTextureAspectRatio > 1.f) { + size.x = maxSize; + size.y = static_cast(maxSize / _projectionTextureAspectRatio); + } + else { + size.x = static_cast(maxSize * _projectionTextureAspectRatio); + size.y = maxSize; + } + + _textureSize.setMaxValue(size); + _textureSize = size / 2; + + // We only want to use half the resolution per default: + size /= 2; + + bool success = generateProjectionLayerTexture(size); + success &= generateDepthTexture(size); + success &= auxiliaryRendertarget(); + success &= depthRendertarget(); + + using std::unique_ptr; + using ghoul::opengl::Texture; + using ghoul::io::TextureReader; + + unique_ptr texture = TextureReader::ref().loadTexture(absPath(placeholderFile)); + if (texture) { + texture->uploadTexture(); + // TODO: AnisotropicMipMap crashes on ATI cards ---abock + //_textureProj->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + texture->setFilter(Texture::FilterMode::Linear); + texture->setWrapping(Texture::WrappingMode::ClampToBorder); + } + _placeholderTexture = std::move(texture); + + if (_dilation.isEnabled) { + _dilation.program = ghoul::opengl::ProgramObject::Build( + "Dilation", + "${MODULE_NEWHORIZONS}/shaders/dilation_vs.glsl", + "${MODULE_NEWHORIZONS}/shaders/dilation_fs.glsl" + ); + + const GLfloat plane[] = { + -1, -1, + 1, 1, + -1, 1, + -1, -1, + 1, -1, + 1, 1, + }; + + glGenVertexArrays(1, &_dilation.vao); + glGenBuffers(1, &_dilation.vbo); + + glBindVertexArray(_dilation.vao); + glBindBuffer(GL_ARRAY_BUFFER, _dilation.vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(plane), plane, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer( + 0, + 2, + GL_FLOAT, + GL_FALSE, + sizeof(GLfloat) * 2, + reinterpret_cast(0) + ); + + glBindVertexArray(0); + } + + return success; +} + +bool ProjectionComponent::deinitialize() { + _projectionTexture = nullptr; + + glDeleteFramebuffers(1, &_fboID); + + if (_dilation.isEnabled) { + glDeleteFramebuffers(1, &_dilation.fbo); + glDeleteVertexArrays(1, &_dilation.vao); + glDeleteBuffers(1, &_dilation.vbo); + + _dilation.program = nullptr; + _dilation.texture = nullptr; + } + + return true; +} + +bool ProjectionComponent::isReady() const { + return (_projectionTexture != nullptr); +} + +void ProjectionComponent::imageProjectBegin() { + // keep handle to the current bound FBO + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_defaultFBO); + + if (_textureSizeDirty) { + LDEBUG("Changing texture size to " << std::to_string(_textureSize)); + + // If the texture size has changed, we have to allocate new memory and copy + // the image texture to the new target + + using ghoul::opengl::Texture; + using ghoul::opengl::FramebufferObject; + + // Make a copy of the old textures + std::unique_ptr oldProjectionTexture = std::move(_projectionTexture); + std::unique_ptr oldDilationStencil = std::move(_dilation.stencilTexture); + std::unique_ptr oldDilationTexture = std::move(_dilation.texture); + std::unique_ptr oldDepthTexture = std::move(_shadowing.texture); + + // Generate the new textures + generateProjectionLayerTexture(_textureSize); + + if (_shadowing.isEnabled) { + generateDepthTexture(_textureSize); + } + + auto copyFramebuffers = [](Texture* src, Texture* dst, const std::string& msg) { + glFramebufferTexture( + GL_READ_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + *src, + 0 + ); + + GLenum status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER); + if (!FramebufferObject::errorChecking(status).empty()) { + LERROR( + "Read Buffer (" << msg << "): " << + FramebufferObject::errorChecking(status) + ); + } + + glFramebufferTexture( + GL_DRAW_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + *dst, + 0 + ); + + status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); + if (!FramebufferObject::errorChecking(status).empty()) { + LERROR( + "Draw Buffer (" << msg << "): " << + FramebufferObject::errorChecking(status) + ); + } + + glBlitFramebuffer( + 0, 0, + src->dimensions().x, src->dimensions().y, + 0, 0, + dst->dimensions().x, dst->dimensions().y, + GL_COLOR_BUFFER_BIT, + GL_LINEAR + ); + }; + + auto copyDepthBuffer = [](Texture* src, Texture* dst, const std::string& msg) { + glFramebufferTexture( + GL_READ_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + *src, + 0 + ); + + GLenum status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER); + if (!FramebufferObject::errorChecking(status).empty()) { + LERROR( + "Read Buffer (" << msg << "): " << + FramebufferObject::errorChecking(status) + ); + } + + glFramebufferTexture( + GL_DRAW_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + *dst, + 0 + ); + + status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); + if (!FramebufferObject::errorChecking(status).empty()) { + LERROR( + "Draw Buffer (" << msg << "): " << + FramebufferObject::errorChecking(status) + ); + } + + glBlitFramebuffer( + 0, 0, + src->dimensions().x, src->dimensions().y, + 0, 0, + dst->dimensions().x, dst->dimensions().y, + GL_DEPTH_BUFFER_BIT, + GL_NEAREST + ); + }; + + GLuint fbos[2]; + glGenFramebuffers(2, fbos); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[0]); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[1]); + + copyFramebuffers( + oldProjectionTexture.get(), + _projectionTexture.get(), + "Projection" + ); + + if (_dilation.isEnabled) { + copyFramebuffers( + oldDilationStencil.get(), + _dilation.stencilTexture.get(), + "Dilation Stencil" + ); + + copyFramebuffers( + oldDilationTexture.get(), + _dilation.texture.get(), + "Dilation Texture" + ); + } + + if (_shadowing.isEnabled) { + copyDepthBuffer( + oldDepthTexture.get(), + _shadowing.texture.get(), + "Shadowing" + ); + } + + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glDeleteFramebuffers(2, fbos); + + glBindFramebuffer(GL_FRAMEBUFFER, _fboID); + glFramebufferTexture2D( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + *_projectionTexture, + 0 + ); + + if (_dilation.isEnabled) { + // We only need the stencil texture if we need to dilate + glFramebufferTexture2D( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT1, + GL_TEXTURE_2D, + *_dilation.stencilTexture, + 0 + ); + + glBindFramebuffer(GL_FRAMEBUFFER, _dilation.fbo); + glFramebufferTexture2D( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + *_dilation.texture, + 0 + ); + } + + if (_shadowing.isEnabled) { + glBindFramebuffer(GL_FRAMEBUFFER, _depthFboID); + glFramebufferTexture2D( + GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_TEXTURE_2D, + *_shadowing.texture, + 0 + ); + } + + _textureSizeDirty = false; + } + + glGetIntegerv(GL_VIEWPORT, _viewport); + glBindFramebuffer(GL_FRAMEBUFFER, _fboID); + + glViewport( + 0, 0, + static_cast(_projectionTexture->width()), + static_cast(_projectionTexture->height()) + ); + + if (_dilation.isEnabled) { + GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; + glDrawBuffers(2, buffers); + } +} + +bool ProjectionComponent::needsShadowMap() const { + return _shadowing.isEnabled; +} + +ghoul::opengl::Texture& ProjectionComponent::depthTexture() { + return *_shadowing.texture; +} + +void ProjectionComponent::depthMapRenderBegin() { + ghoul_assert(_shadowing.isEnabled, "Shadowing is not enabled"); + + // keep handle to the current bound FBO + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_defaultFBO); + glGetIntegerv(GL_VIEWPORT, _viewport); + + glBindFramebuffer(GL_FRAMEBUFFER, _depthFboID); + glEnable(GL_DEPTH_TEST); + + glViewport( + 0, 0, + static_cast(_shadowing.texture->width()), + static_cast(_shadowing.texture->height()) + ); + + glClear(GL_DEPTH_BUFFER_BIT); +} + +void ProjectionComponent::depthMapRenderEnd() { + glBindFramebuffer(GL_FRAMEBUFFER, _defaultFBO); + glViewport(_viewport[0], _viewport[1], _viewport[2], _viewport[3]); +} + +void ProjectionComponent::imageProjectEnd() { + if (_dilation.isEnabled) { + glBindFramebuffer(GL_FRAMEBUFFER, _dilation.fbo); + + glDisable(GL_BLEND); + + ghoul::opengl::TextureUnit unit[2]; + unit[0].activate(); + _projectionTexture->bind(); + + unit[1].activate(); + _dilation.stencilTexture->bind(); + + _dilation.program->activate(); + _dilation.program->setUniform("tex", unit[0]); + _dilation.program->setUniform("stencil", unit[1]); + + glBindVertexArray(_dilation.vao); + glDrawArrays(GL_TRIANGLES, 0, 6); + + _dilation.program->deactivate(); + + glEnable(GL_BLEND); + } + + glBindFramebuffer(GL_FRAMEBUFFER, _defaultFBO); + glViewport(_viewport[0], _viewport[1], _viewport[2], _viewport[3]); +} + +void ProjectionComponent::update() { + if (_dilation.isEnabled && _dilation.program->isDirty()) { + _dilation.program->rebuildFromFile(); + } +} + +bool ProjectionComponent::depthRendertarget() { + GLint defaultFBO; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); + // setup FBO + glGenFramebuffers(1, &_depthFboID); + glBindFramebuffer(GL_FRAMEBUFFER, _depthFboID); + glFramebufferTexture2D( + GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_TEXTURE_2D, + *_shadowing.texture, + 0); + + glDrawBuffer(GL_NONE); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) + return false; + + glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); + return true; +} + +bool ProjectionComponent::auxiliaryRendertarget() { + bool completeSuccess = true; + + GLint defaultFBO; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); + + // setup FBO + glGenFramebuffers(1, &_fboID); + glBindFramebuffer(GL_FRAMEBUFFER, _fboID); + glFramebufferTexture2D( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + *_projectionTexture, + 0 + ); + // check FBO status + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + LERROR("Main Framebuffer incomplete"); + completeSuccess &= false; + } + + + if (_dilation.isEnabled) { + // We only need the stencil texture if we need to dilate + glFramebufferTexture2D( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT1, + GL_TEXTURE_2D, + *_dilation.stencilTexture, + 0 + ); + + // check FBO status + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + LERROR("Main Framebuffer incomplete"); + completeSuccess &= false; + } + + glGenFramebuffers(1, &_dilation.fbo); + glBindFramebuffer(GL_FRAMEBUFFER, _dilation.fbo); + glFramebufferTexture2D( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + *_dilation.texture, + 0 + ); + + // check FBO status + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + LERROR("Dilation Framebuffer incomplete"); + completeSuccess &= false; + } + } + + // switch back to window-system-provided framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); + + return completeSuccess; +} + +glm::mat4 ProjectionComponent::computeProjectorMatrix(const glm::vec3 loc, glm::dvec3 aim, + const glm::vec3 up, + const glm::dmat3& instrumentMatrix, + float fieldOfViewY, + float aspectRatio, + float nearPlane, float farPlane, + glm::vec3& boreSight) +{ + + //rotate boresight into correct alignment + boreSight = instrumentMatrix*aim; + glm::vec3 uptmp(instrumentMatrix*glm::dvec3(up)); + + // create view matrix + glm::vec3 e3 = glm::normalize(-boreSight); + glm::vec3 e1 = glm::normalize(glm::cross(uptmp, e3)); + glm::vec3 e2 = glm::normalize(glm::cross(e3, e1)); + + glm::mat4 projViewMatrix = glm::mat4(e1.x, e2.x, e3.x, 0.f, + e1.y, e2.y, e3.y, 0.f, + e1.z, e2.z, e3.z, 0.f, + glm::dot(e1, -loc), glm::dot(e2, -loc), glm::dot(e3, -loc), 1.f); + // create perspective projection matrix + glm::mat4 projProjectionMatrix = glm::perspective(glm::radians(fieldOfViewY), aspectRatio, nearPlane, farPlane); + + return projProjectionMatrix*projViewMatrix; +} + +bool ProjectionComponent::doesPerformProjection() const { + return _performProjection; +} + +bool ProjectionComponent::needsClearProjection() const { + return _clearAllProjections; +} + +float ProjectionComponent::projectionFading() const { + return _projectionFading; +} + +ghoul::opengl::Texture& ProjectionComponent::projectionTexture() const { + if (_dilation.isEnabled) { + return *_dilation.texture; + } + else { + return *_projectionTexture; + } +} + +std::string ProjectionComponent::projectorId() const { + return _projectorID; +} + +std::string ProjectionComponent::projecteeId() const { + return _projecteeID; +} + +std::string ProjectionComponent::instrumentId() const { + return _instrumentID; +} + +SpiceManager::AberrationCorrection ProjectionComponent::aberration() const { + return _aberration; +} + +float ProjectionComponent::fieldOfViewY() const { + return _fovy; +} + +float ProjectionComponent::aspectRatio() const { + return _aspectRatio; +} + +void ProjectionComponent::clearAllProjections() { + // keep handle to the current bound FBO + GLint defaultFBO; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); + + GLint m_viewport[4]; + glGetIntegerv(GL_VIEWPORT, m_viewport); + //counter = 0; + glViewport(0, 0, static_cast(_projectionTexture->width()), static_cast(_projectionTexture->height())); + + glBindFramebuffer(GL_FRAMEBUFFER, _fboID); + + glClearColor(0.f, 0.f, 0.f, 0.f); + glClear(GL_COLOR_BUFFER_BIT); + + if (_dilation.isEnabled) { + glBindFramebuffer(GL_FRAMEBUFFER, _dilation.fbo); + glClear(GL_COLOR_BUFFER_BIT); + } + + glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); + glViewport(m_viewport[0], m_viewport[1], + m_viewport[2], m_viewport[3]); + + _clearAllProjections = false; +} + +std::shared_ptr ProjectionComponent::loadProjectionTexture( + const std::string& texturePath, + bool isPlaceholder) +{ + using std::unique_ptr; + using ghoul::opengl::Texture; + using ghoul::io::TextureReader; + + + if (isPlaceholder) { + return _placeholderTexture; + } + + + unique_ptr texture = TextureReader::ref().loadTexture(absPath(texturePath)); + if (texture) { + if (texture->format() == Texture::Format::Red) + ghoul::opengl::convertTextureFormat(*texture, Texture::Format::RGB); + texture->uploadTexture(); + // TODO: AnisotropicMipMap crashes on ATI cards ---abock + //_textureProj->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + texture->setFilter(Texture::FilterMode::Linear); + texture->setWrapping(Texture::WrappingMode::ClampToBorder); + } + return std::move(texture); +} + +bool ProjectionComponent::generateProjectionLayerTexture(const ivec2& size) { + LINFO( + "Creating projection texture of size '" << size.x << ", " << size.y << "'" + ); + _projectionTexture = std::make_unique ( + glm::uvec3(size, 1), + ghoul::opengl::Texture::Format::RGBA + ); + if (_projectionTexture) { + _projectionTexture->uploadTexture(); + //_projectionTexture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + } + + if (_dilation.isEnabled) { + _dilation.texture = std::make_unique( + glm::uvec3(size, 1), + ghoul::opengl::Texture::Format::RGBA + ); + + if (_dilation.texture) { + _dilation.texture->uploadTexture(); + //_dilation.texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + } + + _dilation.stencilTexture = std::make_unique( + glm::uvec3(size, 1), + ghoul::opengl::Texture::Format::Red, + // @TODO: Remove the static cast ---abock + static_cast(ghoul::opengl::Texture::Format::Red) + ); + + if (_dilation.stencilTexture) { + _dilation.stencilTexture->uploadTexture(); + //_dilation.texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + } + } + + + return _projectionTexture != nullptr; + +} + +bool ProjectionComponent::generateDepthTexture(const ivec2& size) { + LINFO( + "Creating depth texture of size '" << size.x << ", " << size.y << "'" + ); + + _shadowing.texture = std::make_unique( + glm::uvec3(size, 1), + ghoul::opengl::Texture::Format::DepthComponent, + GL_DEPTH_COMPONENT32F + ); + + if (_shadowing.texture) { + _shadowing.texture->uploadTexture(); + } + + return _shadowing.texture != nullptr; + +} + +} // namespace openspace diff --git a/modules/space/rendering/renderableplanet.cpp b/modules/space/rendering/renderableplanet.cpp index 70e196d223..2d05de4755 100644 --- a/modules/space/rendering/renderableplanet.cpp +++ b/modules/space/rendering/renderableplanet.cpp @@ -1,563 +1,563 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2017 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define _USE_MATH_DEFINES -#include - - -namespace { - const char* KeyGeometry = "Geometry"; - const char* KeyRadius = "Radius"; - const char* KeyColorTexture = "Textures.Color"; - const char* KeyNightTexture = "Textures.Night"; - const char* KeyHeightTexture = "Textures.Height"; - const char* KeyShading = "PerformShading"; - - - - static const std::string _loggerCat = "RenderablePlanet"; - - const char* keyFrame = "Frame"; - const char* keyShadowGroup = "Shadow_Group"; - const char* keyShadowSource = "Source"; - const char* keyShadowCaster = "Caster"; - const char* keyBody = "Body"; -} // namespace - -namespace openspace { - -documentation::Documentation RenderablePlanet::Documentation() { - using namespace documentation; - return { - "RenderablePlanet", - "space_renderable_planet", - { - { - KeyGeometry, - new ReferencingVerifier("space_geometry_planet"), - "Specifies the planet geometry that is used for this RenderablePlanet.", - Optional::No - }, - { - KeyRadius, - new DoubleVerifier, - "Specifies the radius of the planet. If this value is not specified, it " - "will try to query the SPICE library for radius values.", - Optional::Yes - }, - { - KeyColorTexture, - new StringVerifier, - "Specifies the color texture that is used for this RenderablePlanet.", - Optional::Yes - }, - { - KeyHeightTexture, - new StringVerifier, - "Specifies the height texture that is used for this RenderablePlanet.", - Optional::Yes - }, - { - KeyNightTexture, - new StringVerifier, - "Specifies the texture that is used for the night side of this " - "RenderablePlanet.", - Optional::Yes - }, - { - KeyShading, - new BoolVerifier, - "Specifies whether the planet should be rendered shaded by the Sun. If " - "this value is 'false', any existing night texture will not be used. " - "This value defaults to 'true'.", - Optional::Yes - } - } - }; -} - -RenderablePlanet::RenderablePlanet(const ghoul::Dictionary& dictionary) - : Renderable(dictionary) - , _colorTexturePath("colorTexture", "Color Texture") - , _nightTexturePath("nightTexture", "Night Texture") - , _heightMapTexturePath("heightMap", "Heightmap Texture") - , _programObject(nullptr) - , _texture(nullptr) - , _nightTexture(nullptr) - , _heightExaggeration("heightExaggeration", "Height Exaggeration", 1.f, 0.f, 10.f) - , _geometry(nullptr) - , _performShading("performShading", "Perform Shading", true) - , _alpha(1.f) - , _planetRadius(0.f) - , _hasNightTexture(false) - , _hasHeightTexture(false) - , _shadowEnabled(false) -{ - ghoul_precondition( - dictionary.hasKeyAndValue(SceneGraphNode::KeyName), - "RenderablePlanet needs the name to be specified" - ); - - documentation::testSpecificationAndThrow( - Documentation(), - dictionary, - "RenderablePlanet" - ); - - const std::string name = dictionary.value(SceneGraphNode::KeyName); - - ghoul::Dictionary geomDict = dictionary.value(KeyGeometry); - - if (dictionary.hasKey(KeyRadius)) { - // If the user specified a radius, we want to use this - _planetRadius = dictionary.value(KeyRadius); - } - else if (SpiceManager::ref().hasValue(name, "RADII") ) { - // If the user didn't specfify a radius, but Spice has a radius, we can use this - glm::dvec3 radius; - SpiceManager::ref().getValue(name, "RADII", radius); - radius *= 1000.0; // Spice gives radii in KM. - std::swap(radius[1], radius[2]); // z is equivalent to y in our coordinate system - geomDict.setValue(KeyRadius, radius); - - _planetRadius = (radius.x + radius.y + radius.z) / 3.0; - } - else { - LERRORC("RenderablePlanet", "Missing radius specification"); - } - - _geometry = planetgeometry::PlanetGeometry::createFromDictionary(geomDict); - - if (dictionary.hasKey(KeyColorTexture)) { - _colorTexturePath = absPath(dictionary.value(KeyColorTexture)); - } - - if (dictionary.hasKey(KeyNightTexture)) { - _hasNightTexture = true; - _nightTexturePath = absPath(dictionary.value(KeyNightTexture)); - } - - if (dictionary.hasKey(KeyHeightTexture)) { - _hasHeightTexture = true; - _heightMapTexturePath = absPath(dictionary.value(KeyHeightTexture)); - } - - if (dictionary.hasKey(KeyShading)) { - _performShading = dictionary.value(KeyShading); - } - - addPropertySubOwner(_geometry.get()); - - auto loadTextureCallback = [this]() {loadTexture(); }; - addProperty(_colorTexturePath); - _colorTexturePath.onChange(loadTextureCallback); - - addProperty(_nightTexturePath); - _nightTexturePath.onChange(loadTextureCallback); - - addProperty(_heightMapTexturePath); - _heightMapTexturePath.onChange(loadTextureCallback); - - addProperty(_heightExaggeration); - addProperty(_performShading); - - // Shadow data: - ghoul::Dictionary shadowDictionary; - bool success = dictionary.getValue(keyShadowGroup, shadowDictionary); - bool disableShadows = false; - if (success) { - std::vector< std::pair > sourceArray; - unsigned int sourceCounter = 1; - while (success) { - std::string sourceName; - std::stringstream ss; - ss << keyShadowSource << sourceCounter << ".Name"; - success = shadowDictionary.getValue(ss.str(), sourceName); - if (success) { - float sourceRadius; - ss.str(std::string()); - ss << keyShadowSource << sourceCounter << ".Radius"; - success = shadowDictionary.getValue(ss.str(), sourceRadius); - if (success) { - sourceArray.push_back(std::pair< std::string, float>( - sourceName, sourceRadius)); - } - else { - LWARNING("No Radius value expecified for Shadow Source Name " - << sourceName << " from " << name - << " planet.\nDisabling shadows for this planet."); - disableShadows = true; - break; - } - } - sourceCounter++; - } - - if (!disableShadows && !sourceArray.empty()) { - success = true; - std::vector< std::pair > casterArray; - unsigned int casterCounter = 1; - while (success) { - std::string casterName; - std::stringstream ss; - ss << keyShadowCaster << casterCounter << ".Name"; - success = shadowDictionary.getValue(ss.str(), casterName); - if (success) { - float casterRadius; - ss.str(std::string()); - ss << keyShadowCaster << casterCounter << ".Radius"; - success = shadowDictionary.getValue(ss.str(), casterRadius); - if (success) { - casterArray.push_back(std::pair< std::string, float>( - casterName, casterRadius)); - } - else { - LWARNING("No Radius value expecified for Shadow Caster Name " - << casterName << " from " << name - << " planet.\nDisabling shadows for this planet."); - disableShadows = true; - break; - } - } - - casterCounter++; - } - - if (!disableShadows && (!sourceArray.empty() && !casterArray.empty())) { - for (const auto & source : sourceArray) - for (const auto & caster : casterArray) { - ShadowConf sc; - sc.source = source; - sc.caster = caster; - _shadowConfArray.push_back(sc); - } - _shadowEnabled = true; - } - } - } -} - -bool RenderablePlanet::initialize() { - RenderEngine& renderEngine = OsEng.renderEngine(); - - if (_programObject == nullptr && _shadowEnabled && _hasNightTexture) { - // shadow program - _programObject = renderEngine.buildRenderProgram( - "shadowNightProgram", - "${MODULE_SPACE}/shaders/shadow_nighttexture_vs.glsl", - "${MODULE_SPACE}/shaders/shadow_nighttexture_fs.glsl"); - } - else if (_programObject == nullptr && _shadowEnabled) { - // shadow program - _programObject = renderEngine.buildRenderProgram( - "shadowProgram", - "${MODULE_SPACE}/shaders/shadow_vs.glsl", - "${MODULE_SPACE}/shaders/shadow_fs.glsl"); - } - else if (_programObject == nullptr && _hasNightTexture) { - // Night texture program - _programObject = renderEngine.buildRenderProgram( - "nightTextureProgram", - "${MODULE_SPACE}/shaders/nighttexture_vs.glsl", - "${MODULE_SPACE}/shaders/nighttexture_fs.glsl"); - } - else if (_programObject == nullptr) { - // pscstandard - _programObject = renderEngine.buildRenderProgram( - "pscstandard", - "${MODULE_SPACE}/shaders/renderableplanet_vs.glsl", - "${MODULE_SPACE}/shaders/renderableplanet_fs.glsl"); - } - using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError; - _programObject->setIgnoreSubroutineUniformLocationError(IgnoreError::Yes); - _programObject->setIgnoreUniformLocationError(IgnoreError::Yes); - - _geometry->initialize(this); - - _programObject->deactivate(); - - loadTexture(); - - return isReady(); -} - -bool RenderablePlanet::deinitialize() { - if (_geometry) { - _geometry->deinitialize(); - _geometry = nullptr; - } - - RenderEngine& renderEngine = OsEng.renderEngine(); - if (_programObject) { - renderEngine.removeRenderProgram(_programObject); - _programObject = nullptr; - } - - _geometry = nullptr; - _texture = nullptr; - _nightTexture = nullptr; - - return true; -} - -bool RenderablePlanet::isReady() const { - bool ready = true; - ready &= (_programObject != nullptr); - ready &= (_texture != nullptr); - ready &= (_geometry != nullptr); - return ready; -} - -void RenderablePlanet::render(const RenderData& data) { - // activate shader - _programObject->activate(); - - glm::dmat4 modelTransform = - glm::translate(glm::dmat4(1.0), data.modelTransform.translation) * // Translation - glm::dmat4(data.modelTransform.rotation) * // Spice rotation - glm::dmat4(glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale))); - - // scale the planet to appropriate size since the planet is a unit sphere - //glm::mat4 transform = glm::mat4(1); - - //earth needs to be rotated for that to work. - glm::dmat4 rot = glm::rotate(glm::dmat4(1.0), M_PI_2, glm::dvec3(1, 0, 0)); - glm::dmat4 roty = glm::rotate(glm::dmat4(1.0), M_PI_2, glm::dvec3(0, -1, 0)); - //glm::dmat4 rotProp = glm::rotate(glm::dmat4(1.0), glm::radians(static_cast(_rotation)), glm::dvec3(0, 1, 0)); - modelTransform = modelTransform * rot * roty /** rotProp*/; - - glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() * modelTransform; - - _programObject->setUniform("transparency", _alpha); - _programObject->setUniform( - "modelViewProjectionTransform", - data.camera.projectionMatrix() * glm::mat4(modelViewTransform) - ); - _programObject->setUniform("ModelTransform", glm::mat4(modelTransform)); - - // Normal Transformation - glm::mat4 translateObjTrans = glm::translate(glm::mat4(1.0), data.position.vec3()); - glm::mat4 translateCamTrans = glm::translate(glm::mat4(1.0), -data.camera.position().vec3()); - float scaleFactor = data.camera.scaling().x * powf(10.0, data.camera.scaling().y); - glm::mat4 scaleCamTrans = glm::scale(glm::mat4(1.0), glm::vec3(scaleFactor)); - -// glm::mat4 ModelViewTrans = data.camera.viewMatrix() * scaleCamTrans * -// translateCamTrans * translateObjTrans * glm::mat4(modelTransform); - - setPscUniforms(*_programObject.get(), data.camera, data.position); - - _programObject->setUniform("_performShading", _performShading); - - _programObject->setUniform("_hasHeightMap", _hasHeightTexture); - _programObject->setUniform("_heightExaggeration", _heightExaggeration); - - // Bind texture - ghoul::opengl::TextureUnit dayUnit; - ghoul::opengl::TextureUnit nightUnit; - ghoul::opengl::TextureUnit heightUnit; - - - dayUnit.activate(); - _texture->bind(); - _programObject->setUniform("texture1", dayUnit); - - // Bind possible night texture - if (_hasNightTexture && _nightTexture) { - nightUnit.activate(); - _nightTexture->bind(); - _programObject->setUniform("nightTex", nightUnit); - } - - if (_hasHeightTexture && _heightMapTexture) { - heightUnit.activate(); - _heightMapTexture->bind(); - _programObject->setUniform("heightTex", heightUnit); - } - - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - - // TODO: Move Calculations to VIEW SPACE (precision problems avoidance...) - - double lt; - // Shadow calculations.. - if (!_shadowConfArray.empty()) { - std::vector shadowDataArray; - shadowDataArray.reserve(_shadowConfArray.size()); - - for (const auto & shadowConf : _shadowConfArray) { - // TO REMEMBER: all distances and lengths in world coordinates are in meters!!! We need to move this to view space... - // Getting source and caster: - glm::dvec3 sourcePos = SpiceManager::ref().targetPosition(shadowConf.source.first, "SUN", "GALACTIC", {}, _time, lt); - sourcePos *= 1000.0; // converting to meters - glm::dvec3 casterPos = SpiceManager::ref().targetPosition(shadowConf.caster.first, "SUN", "GALACTIC", {}, _time, lt); - casterPos *= 1000.0; // converting to meters - psc caster_pos = PowerScaledCoordinate::CreatePowerScaledCoordinate(casterPos.x, casterPos.y, casterPos.z); - - - // First we determine if the caster is shadowing the current planet (all calculations in World Coordinates): - glm::vec3 planetCasterVec = (caster_pos - data.position).vec3(); - glm::vec3 sourceCasterVec = glm::vec3(casterPos - sourcePos); - float sc_length = glm::length(sourceCasterVec); - glm::vec3 planetCaster_proj = (glm::dot(planetCasterVec, sourceCasterVec) / (sc_length*sc_length)) * sourceCasterVec; - float d_test = glm::length(planetCasterVec - planetCaster_proj); - float xp_test = shadowConf.caster.second * sc_length / (shadowConf.source.second + shadowConf.caster.second); - float rp_test = shadowConf.caster.second * (glm::length(planetCaster_proj) + xp_test) / xp_test; - - double casterDistSun = glm::length(casterPos); - float planetDistSun = glm::length(data.position.vec3()); - - ShadowRenderingStruct shadowData; - shadowData.isShadowing = false; - - if (((d_test - rp_test) < _planetRadius) && - (casterDistSun < planetDistSun) ) { - // The current caster is shadowing the current planet - shadowData.isShadowing = true; - shadowData.rs = shadowConf.source.second; - shadowData.rc = shadowConf.caster.second; - shadowData.sourceCasterVec = sourceCasterVec; - shadowData.xp = xp_test; - shadowData.xu = shadowData.rc * sc_length / (shadowData.rs - shadowData.rc); - shadowData.casterPositionVec = glm::vec3(casterPos); - } - shadowDataArray.push_back(shadowData); - } - - const std::string uniformVarName("shadowDataArray["); - unsigned int counter = 0; - for (const auto & sd : shadowDataArray) { - std::stringstream ss; - ss << uniformVarName << counter << "].isShadowing"; - _programObject->setUniform(ss.str(), sd.isShadowing); - if (sd.isShadowing) { - ss.str(std::string()); - ss << uniformVarName << counter << "].xp"; - _programObject->setUniform(ss.str(), sd.xp); - ss.str(std::string()); - ss << uniformVarName << counter << "].xu"; - _programObject->setUniform(ss.str(), sd.xu); - /*ss.str(std::string()); - ss << uniformVarName << counter << "].rs"; - _programObject->setUniform(ss.str(), sd.rs);*/ - ss.str(std::string()); - ss << uniformVarName << counter << "].rc"; - _programObject->setUniform(ss.str(), sd.rc); - ss.str(std::string()); - ss << uniformVarName << counter << "].sourceCasterVec"; - _programObject->setUniform(ss.str(), sd.sourceCasterVec); - ss.str(std::string()); - ss << uniformVarName << counter << "].casterPositionVec"; - _programObject->setUniform(ss.str(), sd.casterPositionVec); - } - counter++; - } - } - - // render - _geometry->render(); - - // disable shader - _programObject->deactivate(); - -} - -void RenderablePlanet::update(const UpdateData& data) { - // set spice-orientation in accordance to timestamp - _stateMatrix = data.modelTransform.rotation; - //_stateMatrix = SpiceManager::ref().positionTransformMatrix(_frame, "GALACTIC", data.time); - _time = data.time.j2000Seconds(); -} - -void RenderablePlanet::loadTexture() { - _texture = nullptr; - if (_colorTexturePath.value() != "") { - _texture = ghoul::io::TextureReader::ref().loadTexture(absPath(_colorTexturePath)); - if (_texture) { - if (_texture->numberOfChannels() == 1) { - _texture->setSwizzleMask({ GL_RED, GL_RED, GL_RED, GL_RED }); - } - - LDEBUG("Loaded texture from '" << _colorTexturePath << "'"); - _texture->uploadTexture(); - - // Textures of planets looks much smoother with AnisotropicMipMap rather than linear - // TODO: AnisotropicMipMap crashes on ATI cards ---abock - //_texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); - _texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); - } - } - - if (_hasNightTexture) { - _nightTexture = nullptr; - if (_nightTexturePath.value() != "") { - _nightTexture = ghoul::io::TextureReader::ref().loadTexture(absPath(_nightTexturePath)); - if (_nightTexture) { - LDEBUG("Loaded texture from '" << _nightTexturePath << "'"); - _nightTexture->uploadTexture(); - _nightTexture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); - //_nightTexture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); - } - } - } - - if (_hasHeightTexture) { - _heightMapTexture = nullptr; - if (_heightMapTexturePath.value() != "") { - _heightMapTexture = ghoul::io::TextureReader::ref().loadTexture(absPath(_heightMapTexturePath)); - if (_heightMapTexture) { - LDEBUG("Loaded texture from '" << _heightMapTexturePath << "'"); - _heightMapTexture->uploadTexture(); - _heightMapTexture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); - //_nightTexture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); - } - } - } -} - -} // namespace openspace +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define _USE_MATH_DEFINES +#include + + +namespace { + const char* KeyGeometry = "Geometry"; + const char* KeyRadius = "Radius"; + const char* KeyColorTexture = "Textures.Color"; + const char* KeyNightTexture = "Textures.Night"; + const char* KeyHeightTexture = "Textures.Height"; + const char* KeyShading = "PerformShading"; + + + + static const std::string _loggerCat = "RenderablePlanet"; + + const char* keyFrame = "Frame"; + const char* keyShadowGroup = "Shadow_Group"; + const char* keyShadowSource = "Source"; + const char* keyShadowCaster = "Caster"; + const char* keyBody = "Body"; +} // namespace + +namespace openspace { + +documentation::Documentation RenderablePlanet::Documentation() { + using namespace documentation; + return { + "RenderablePlanet", + "space_renderable_planet", + { + { + KeyGeometry, + new ReferencingVerifier("space_geometry_planet"), + "Specifies the planet geometry that is used for this RenderablePlanet.", + Optional::No + }, + { + KeyRadius, + new DoubleVerifier, + "Specifies the radius of the planet. If this value is not specified, it " + "will try to query the SPICE library for radius values.", + Optional::Yes + }, + { + KeyColorTexture, + new StringVerifier, + "Specifies the color texture that is used for this RenderablePlanet.", + Optional::Yes + }, + { + KeyHeightTexture, + new StringVerifier, + "Specifies the height texture that is used for this RenderablePlanet.", + Optional::Yes + }, + { + KeyNightTexture, + new StringVerifier, + "Specifies the texture that is used for the night side of this " + "RenderablePlanet.", + Optional::Yes + }, + { + KeyShading, + new BoolVerifier, + "Specifies whether the planet should be rendered shaded by the Sun. If " + "this value is 'false', any existing night texture will not be used. " + "This value defaults to 'true'.", + Optional::Yes + } + } + }; +} + +RenderablePlanet::RenderablePlanet(const ghoul::Dictionary& dictionary) + : Renderable(dictionary) + , _colorTexturePath("colorTexture", "Color Texture") + , _nightTexturePath("nightTexture", "Night Texture") + , _heightMapTexturePath("heightMap", "Heightmap Texture") + , _programObject(nullptr) + , _texture(nullptr) + , _nightTexture(nullptr) + , _heightExaggeration("heightExaggeration", "Height Exaggeration", 1.f, 0.f, 10.f) + , _geometry(nullptr) + , _performShading("performShading", "Perform Shading", true) + , _alpha(1.f) + , _planetRadius(0.f) + , _hasNightTexture(false) + , _hasHeightTexture(false) + , _shadowEnabled(false) +{ + ghoul_precondition( + dictionary.hasKeyAndValue(SceneGraphNode::KeyName), + "RenderablePlanet needs the name to be specified" + ); + + documentation::testSpecificationAndThrow( + Documentation(), + dictionary, + "RenderablePlanet" + ); + + const std::string name = dictionary.value(SceneGraphNode::KeyName); + + ghoul::Dictionary geomDict = dictionary.value(KeyGeometry); + + if (dictionary.hasKey(KeyRadius)) { + // If the user specified a radius, we want to use this + _planetRadius = dictionary.value(KeyRadius); + } + else if (SpiceManager::ref().hasValue(name, "RADII") ) { + // If the user didn't specfify a radius, but Spice has a radius, we can use this + glm::dvec3 radius; + SpiceManager::ref().getValue(name, "RADII", radius); + radius *= 1000.0; // Spice gives radii in KM. + std::swap(radius[1], radius[2]); // z is equivalent to y in our coordinate system + geomDict.setValue(KeyRadius, radius); + + _planetRadius = (radius.x + radius.y + radius.z) / 3.0; + } + else { + LERRORC("RenderablePlanet", "Missing radius specification"); + } + + _geometry = planetgeometry::PlanetGeometry::createFromDictionary(geomDict); + + if (dictionary.hasKey(KeyColorTexture)) { + _colorTexturePath = absPath(dictionary.value(KeyColorTexture)); + } + + if (dictionary.hasKey(KeyNightTexture)) { + _hasNightTexture = true; + _nightTexturePath = absPath(dictionary.value(KeyNightTexture)); + } + + if (dictionary.hasKey(KeyHeightTexture)) { + _hasHeightTexture = true; + _heightMapTexturePath = absPath(dictionary.value(KeyHeightTexture)); + } + + if (dictionary.hasKey(KeyShading)) { + _performShading = dictionary.value(KeyShading); + } + + addPropertySubOwner(_geometry.get()); + + auto loadTextureCallback = [this]() {loadTexture(); }; + addProperty(_colorTexturePath); + _colorTexturePath.onChange(loadTextureCallback); + + addProperty(_nightTexturePath); + _nightTexturePath.onChange(loadTextureCallback); + + addProperty(_heightMapTexturePath); + _heightMapTexturePath.onChange(loadTextureCallback); + + addProperty(_heightExaggeration); + addProperty(_performShading); + + // Shadow data: + ghoul::Dictionary shadowDictionary; + bool success = dictionary.getValue(keyShadowGroup, shadowDictionary); + bool disableShadows = false; + if (success) { + std::vector< std::pair > sourceArray; + unsigned int sourceCounter = 1; + while (success) { + std::string sourceName; + std::stringstream ss; + ss << keyShadowSource << sourceCounter << ".Name"; + success = shadowDictionary.getValue(ss.str(), sourceName); + if (success) { + float sourceRadius; + ss.str(std::string()); + ss << keyShadowSource << sourceCounter << ".Radius"; + success = shadowDictionary.getValue(ss.str(), sourceRadius); + if (success) { + sourceArray.push_back(std::pair< std::string, float>( + sourceName, sourceRadius)); + } + else { + LWARNING("No Radius value expecified for Shadow Source Name " + << sourceName << " from " << name + << " planet.\nDisabling shadows for this planet."); + disableShadows = true; + break; + } + } + sourceCounter++; + } + + if (!disableShadows && !sourceArray.empty()) { + success = true; + std::vector< std::pair > casterArray; + unsigned int casterCounter = 1; + while (success) { + std::string casterName; + std::stringstream ss; + ss << keyShadowCaster << casterCounter << ".Name"; + success = shadowDictionary.getValue(ss.str(), casterName); + if (success) { + float casterRadius; + ss.str(std::string()); + ss << keyShadowCaster << casterCounter << ".Radius"; + success = shadowDictionary.getValue(ss.str(), casterRadius); + if (success) { + casterArray.push_back(std::pair< std::string, float>( + casterName, casterRadius)); + } + else { + LWARNING("No Radius value expecified for Shadow Caster Name " + << casterName << " from " << name + << " planet.\nDisabling shadows for this planet."); + disableShadows = true; + break; + } + } + + casterCounter++; + } + + if (!disableShadows && (!sourceArray.empty() && !casterArray.empty())) { + for (const auto & source : sourceArray) + for (const auto & caster : casterArray) { + ShadowConf sc; + sc.source = source; + sc.caster = caster; + _shadowConfArray.push_back(sc); + } + _shadowEnabled = true; + } + } + } +} + +bool RenderablePlanet::initialize() { + RenderEngine& renderEngine = OsEng.renderEngine(); + + if (_programObject == nullptr && _shadowEnabled && _hasNightTexture) { + // shadow program + _programObject = renderEngine.buildRenderProgram( + "shadowNightProgram", + "${MODULE_SPACE}/shaders/shadow_nighttexture_vs.glsl", + "${MODULE_SPACE}/shaders/shadow_nighttexture_fs.glsl"); + } + else if (_programObject == nullptr && _shadowEnabled) { + // shadow program + _programObject = renderEngine.buildRenderProgram( + "shadowProgram", + "${MODULE_SPACE}/shaders/shadow_vs.glsl", + "${MODULE_SPACE}/shaders/shadow_fs.glsl"); + } + else if (_programObject == nullptr && _hasNightTexture) { + // Night texture program + _programObject = renderEngine.buildRenderProgram( + "nightTextureProgram", + "${MODULE_SPACE}/shaders/nighttexture_vs.glsl", + "${MODULE_SPACE}/shaders/nighttexture_fs.glsl"); + } + else if (_programObject == nullptr) { + // pscstandard + _programObject = renderEngine.buildRenderProgram( + "pscstandard", + "${MODULE_SPACE}/shaders/renderableplanet_vs.glsl", + "${MODULE_SPACE}/shaders/renderableplanet_fs.glsl"); + } + using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError; + _programObject->setIgnoreSubroutineUniformLocationError(IgnoreError::Yes); + _programObject->setIgnoreUniformLocationError(IgnoreError::Yes); + + _geometry->initialize(this); + + _programObject->deactivate(); + + loadTexture(); + + return isReady(); +} + +bool RenderablePlanet::deinitialize() { + if (_geometry) { + _geometry->deinitialize(); + _geometry = nullptr; + } + + RenderEngine& renderEngine = OsEng.renderEngine(); + if (_programObject) { + renderEngine.removeRenderProgram(_programObject); + _programObject = nullptr; + } + + _geometry = nullptr; + _texture = nullptr; + _nightTexture = nullptr; + + return true; +} + +bool RenderablePlanet::isReady() const { + bool ready = true; + ready &= (_programObject != nullptr); + ready &= (_texture != nullptr); + ready &= (_geometry != nullptr); + return ready; +} + +void RenderablePlanet::render(const RenderData& data) { + // activate shader + _programObject->activate(); + + glm::dmat4 modelTransform = + glm::translate(glm::dmat4(1.0), data.modelTransform.translation) * // Translation + glm::dmat4(data.modelTransform.rotation) * // Spice rotation + glm::dmat4(glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale))); + + // scale the planet to appropriate size since the planet is a unit sphere + //glm::mat4 transform = glm::mat4(1); + + //earth needs to be rotated for that to work. + glm::dmat4 rot = glm::rotate(glm::dmat4(1.0), M_PI_2, glm::dvec3(1, 0, 0)); + glm::dmat4 roty = glm::rotate(glm::dmat4(1.0), M_PI_2, glm::dvec3(0, -1, 0)); + //glm::dmat4 rotProp = glm::rotate(glm::dmat4(1.0), glm::radians(static_cast(_rotation)), glm::dvec3(0, 1, 0)); + modelTransform = modelTransform * rot * roty /** rotProp*/; + + glm::dmat4 modelViewTransform = data.camera.combinedViewMatrix() * modelTransform; + + _programObject->setUniform("transparency", _alpha); + _programObject->setUniform( + "modelViewProjectionTransform", + data.camera.projectionMatrix() * glm::mat4(modelViewTransform) + ); + _programObject->setUniform("ModelTransform", glm::mat4(modelTransform)); + + // Normal Transformation + glm::mat4 translateObjTrans = glm::translate(glm::mat4(1.0), data.position.vec3()); + glm::mat4 translateCamTrans = glm::translate(glm::mat4(1.0), -data.camera.position().vec3()); + float scaleFactor = data.camera.scaling().x * powf(10.0, data.camera.scaling().y); + glm::mat4 scaleCamTrans = glm::scale(glm::mat4(1.0), glm::vec3(scaleFactor)); + +// glm::mat4 ModelViewTrans = data.camera.viewMatrix() * scaleCamTrans * +// translateCamTrans * translateObjTrans * glm::mat4(modelTransform); + + setPscUniforms(*_programObject.get(), data.camera, data.position); + + _programObject->setUniform("_performShading", _performShading); + + _programObject->setUniform("_hasHeightMap", _hasHeightTexture); + _programObject->setUniform("_heightExaggeration", _heightExaggeration); + + // Bind texture + ghoul::opengl::TextureUnit dayUnit; + ghoul::opengl::TextureUnit nightUnit; + ghoul::opengl::TextureUnit heightUnit; + + + dayUnit.activate(); + _texture->bind(); + _programObject->setUniform("texture1", dayUnit); + + // Bind possible night texture + if (_hasNightTexture && _nightTexture) { + nightUnit.activate(); + _nightTexture->bind(); + _programObject->setUniform("nightTex", nightUnit); + } + + if (_hasHeightTexture && _heightMapTexture) { + heightUnit.activate(); + _heightMapTexture->bind(); + _programObject->setUniform("heightTex", heightUnit); + } + + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + + // TODO: Move Calculations to VIEW SPACE (precision problems avoidance...) + + double lt; + // Shadow calculations.. + if (!_shadowConfArray.empty()) { + std::vector shadowDataArray; + shadowDataArray.reserve(_shadowConfArray.size()); + + for (const auto & shadowConf : _shadowConfArray) { + // TO REMEMBER: all distances and lengths in world coordinates are in meters!!! We need to move this to view space... + // Getting source and caster: + glm::dvec3 sourcePos = SpiceManager::ref().targetPosition(shadowConf.source.first, "SUN", "GALACTIC", {}, _time, lt); + sourcePos *= 1000.0; // converting to meters + glm::dvec3 casterPos = SpiceManager::ref().targetPosition(shadowConf.caster.first, "SUN", "GALACTIC", {}, _time, lt); + casterPos *= 1000.0; // converting to meters + psc caster_pos = PowerScaledCoordinate::CreatePowerScaledCoordinate(casterPos.x, casterPos.y, casterPos.z); + + + // First we determine if the caster is shadowing the current planet (all calculations in World Coordinates): + glm::vec3 planetCasterVec = (caster_pos - data.position).vec3(); + glm::vec3 sourceCasterVec = glm::vec3(casterPos - sourcePos); + float sc_length = glm::length(sourceCasterVec); + glm::vec3 planetCaster_proj = (glm::dot(planetCasterVec, sourceCasterVec) / (sc_length*sc_length)) * sourceCasterVec; + float d_test = glm::length(planetCasterVec - planetCaster_proj); + float xp_test = shadowConf.caster.second * sc_length / (shadowConf.source.second + shadowConf.caster.second); + float rp_test = shadowConf.caster.second * (glm::length(planetCaster_proj) + xp_test) / xp_test; + + double casterDistSun = glm::length(casterPos); + float planetDistSun = glm::length(data.position.vec3()); + + ShadowRenderingStruct shadowData; + shadowData.isShadowing = false; + + if (((d_test - rp_test) < _planetRadius) && + (casterDistSun < planetDistSun) ) { + // The current caster is shadowing the current planet + shadowData.isShadowing = true; + shadowData.rs = shadowConf.source.second; + shadowData.rc = shadowConf.caster.second; + shadowData.sourceCasterVec = sourceCasterVec; + shadowData.xp = xp_test; + shadowData.xu = shadowData.rc * sc_length / (shadowData.rs - shadowData.rc); + shadowData.casterPositionVec = glm::vec3(casterPos); + } + shadowDataArray.push_back(shadowData); + } + + const std::string uniformVarName("shadowDataArray["); + unsigned int counter = 0; + for (const auto & sd : shadowDataArray) { + std::stringstream ss; + ss << uniformVarName << counter << "].isShadowing"; + _programObject->setUniform(ss.str(), sd.isShadowing); + if (sd.isShadowing) { + ss.str(std::string()); + ss << uniformVarName << counter << "].xp"; + _programObject->setUniform(ss.str(), sd.xp); + ss.str(std::string()); + ss << uniformVarName << counter << "].xu"; + _programObject->setUniform(ss.str(), sd.xu); + /*ss.str(std::string()); + ss << uniformVarName << counter << "].rs"; + _programObject->setUniform(ss.str(), sd.rs);*/ + ss.str(std::string()); + ss << uniformVarName << counter << "].rc"; + _programObject->setUniform(ss.str(), sd.rc); + ss.str(std::string()); + ss << uniformVarName << counter << "].sourceCasterVec"; + _programObject->setUniform(ss.str(), sd.sourceCasterVec); + ss.str(std::string()); + ss << uniformVarName << counter << "].casterPositionVec"; + _programObject->setUniform(ss.str(), sd.casterPositionVec); + } + counter++; + } + } + + // render + _geometry->render(); + + // disable shader + _programObject->deactivate(); + +} + +void RenderablePlanet::update(const UpdateData& data) { + // set spice-orientation in accordance to timestamp + _stateMatrix = data.modelTransform.rotation; + //_stateMatrix = SpiceManager::ref().positionTransformMatrix(_frame, "GALACTIC", data.time); + _time = data.time.j2000Seconds(); +} + +void RenderablePlanet::loadTexture() { + _texture = nullptr; + if (_colorTexturePath.value() != "") { + _texture = ghoul::io::TextureReader::ref().loadTexture(absPath(_colorTexturePath)); + if (_texture) { + if (_texture->numberOfChannels() == 1) { + _texture->setSwizzleMask({ GL_RED, GL_RED, GL_RED, GL_RED }); + } + + LDEBUG("Loaded texture from '" << _colorTexturePath << "'"); + _texture->uploadTexture(); + + // Textures of planets looks much smoother with AnisotropicMipMap rather than linear + // TODO: AnisotropicMipMap crashes on ATI cards ---abock + //_texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + _texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); + } + } + + if (_hasNightTexture) { + _nightTexture = nullptr; + if (_nightTexturePath.value() != "") { + _nightTexture = ghoul::io::TextureReader::ref().loadTexture(absPath(_nightTexturePath)); + if (_nightTexture) { + LDEBUG("Loaded texture from '" << _nightTexturePath << "'"); + _nightTexture->uploadTexture(); + _nightTexture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); + //_nightTexture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + } + } + } + + if (_hasHeightTexture) { + _heightMapTexture = nullptr; + if (_heightMapTexturePath.value() != "") { + _heightMapTexture = ghoul::io::TextureReader::ref().loadTexture(absPath(_heightMapTexturePath)); + if (_heightMapTexture) { + LDEBUG("Loaded texture from '" << _heightMapTexturePath << "'"); + _heightMapTexture->uploadTexture(); + _heightMapTexture->setFilter(ghoul::opengl::Texture::FilterMode::Linear); + //_nightTexture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap); + } + } + } +} + +} // namespace openspace diff --git a/modules/volume/rawvolumewriter.h b/modules/volume/rawvolumewriter.h index ec0cdf28f1..2dd3b19012 100644 --- a/modules/volume/rawvolumewriter.h +++ b/modules/volume/rawvolumewriter.h @@ -1,57 +1,57 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2017 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#ifndef __OPENSPACE_MODULE_VOLUME___RAWVOLUMEWRITER___H__ -#define __OPENSPACE_MODULE_VOLUME___RAWVOLUMEWRITER___H__ - -#include -#include -#include - -namespace openspace { - -template -class RawVolumeWriter { -public: - RawVolumeWriter(std::string path, size_t bufferSize = 1024); - void setPath(const std::string& path); - glm::uvec3 dimensions() const; - void setDimensions(const glm::uvec3& dimensions); - void write(const std::function& fn, - const std::function& onProgress = [](float t) {}); - void write(const RawVolume& volume); - - size_t coordsToIndex(const glm::uvec3& coords) const; - glm::ivec3 indexToCoords(size_t linear) const; -private: - glm::ivec3 _dimensions; - std::string _path; - size_t _bufferSize; -}; - -} // namespace openspace - -#include "rawvolumewriter.inl"; - -#endif // __OPENSPACE_MODULE_VOLUME___RAWVOLUMEWRITER___H__ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_VOLUME___RAWVOLUMEWRITER___H__ +#define __OPENSPACE_MODULE_VOLUME___RAWVOLUMEWRITER___H__ + +#include +#include +#include + +namespace openspace { + +template +class RawVolumeWriter { +public: + RawVolumeWriter(std::string path, size_t bufferSize = 1024); + void setPath(const std::string& path); + glm::uvec3 dimensions() const; + void setDimensions(const glm::uvec3& dimensions); + void write(const std::function& fn, + const std::function& onProgress = [](float t) {}); + void write(const RawVolume& volume); + + size_t coordsToIndex(const glm::uvec3& coords) const; + glm::ivec3 indexToCoords(size_t linear) const; +private: + glm::ivec3 _dimensions; + std::string _path; + size_t _bufferSize; +}; + +} // namespace openspace + +#include "rawvolumewriter.inl" + +#endif // __OPENSPACE_MODULE_VOLUME___RAWVOLUMEWRITER___H__ diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 5e98081847..5a88b5092f 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -1,1446 +1,1446 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2017 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#if defined(_MSC_VER) && defined(OPENSPACE_ENABLE_VLD) -#include -#endif - -#ifdef WIN32 -#include -#endif - -#include "openspaceengine_lua.inl" - -using namespace openspace::scripting; -using namespace ghoul::filesystem; -using namespace ghoul::logging; -using namespace ghoul::cmdparser; - -namespace { - const char* _loggerCat = "OpenSpaceEngine"; - const char* SgctDefaultConfigFile = "${SGCT}/single.xml"; - - const char* SgctConfigArgumentCommand = "-config"; - - const char* PreInitializeFunction = "preInitialization"; - const char* PostInitializationFunction = "postInitialization"; - - const int CacheVersion = 1; - const int DownloadVersion = 1; - - const glm::ivec3 FontAtlasSize{ 1536, 1536, 1 }; - - - struct { - std::string configurationName; - std::string sgctConfigurationName; - std::string sceneName; - std::string cacheFolder; - } commandlineArgumentPlaceholders; -} - -namespace openspace { - -namespace properties { - class Property; -} - -class Scene; - -OpenSpaceEngine* OpenSpaceEngine::_engine = nullptr; - -OpenSpaceEngine::OpenSpaceEngine(std::string programName, - std::unique_ptr windowWrapper) - : _configurationManager(new ConfigurationManager) - , _sceneManager(new SceneManager) - , _downloadManager(nullptr) - , _console(new LuaConsole) - , _moduleEngine(new ModuleEngine) - , _networkEngine(new NetworkEngine) - , _parallelConnection(new ParallelConnection) - , _renderEngine(new RenderEngine) - , _settingsEngine(new SettingsEngine) - , _syncEngine(std::make_unique(4096)) - , _timeManager(new TimeManager) - , _windowWrapper(std::move(windowWrapper)) - , _commandlineParser(new ghoul::cmdparser::CommandlineParser( - programName, ghoul::cmdparser::CommandlineParser::AllowUnknownCommands::Yes - )) - , _interactionHandler(new interaction::InteractionHandler) - , _scriptEngine(new scripting::ScriptEngine) - , _scriptScheduler(new scripting::ScriptScheduler) - , _virtualPropertyManager(new VirtualPropertyManager) - , _globalPropertyNamespace(new properties::PropertyOwner("")) - , _scheduledSceneSwitch(false) - , _scenePath("") - , _runTime(0.0) - , _shutdown({false, 0.f, 0.f}) - , _isFirstRenderingFirstFrame(true) -{ - _interactionHandler->setPropertyOwner(_globalPropertyNamespace.get()); - - // New property subowners also have to be added to the OnScreenGuiModule callback! - _globalPropertyNamespace->addPropertySubOwner(_interactionHandler.get()); - _globalPropertyNamespace->addPropertySubOwner(_settingsEngine.get()); - _globalPropertyNamespace->addPropertySubOwner(_renderEngine.get()); - _globalPropertyNamespace->addPropertySubOwner(_windowWrapper.get()); - _globalPropertyNamespace->addPropertySubOwner(_parallelConnection.get()); - _globalPropertyNamespace->addPropertySubOwner(_console.get()); - - FactoryManager::initialize(); - FactoryManager::ref().addFactory( - std::make_unique>(), - "Renderable" - ); - FactoryManager::ref().addFactory( - std::make_unique>(), - "Translation" - ); - FactoryManager::ref().addFactory( - std::make_unique>(), - "Rotation" - ); - FactoryManager::ref().addFactory( - std::make_unique>(), - "Scale" - ); - FactoryManager::ref().addFactory( - std::make_unique>(), - "Task" - ); - - SpiceManager::initialize(); - TransformationManager::initialize(); -} - -OpenSpaceEngine& OpenSpaceEngine::ref() { - ghoul_assert(_engine, "OpenSpaceEngine not created"); - return *_engine; -} - -bool OpenSpaceEngine::isCreated() { - return _engine != nullptr; -} - -void OpenSpaceEngine::create(int argc, char** argv, - std::unique_ptr windowWrapper, - std::vector& sgctArguments, bool& requestClose) -{ - ghoul_assert(!_engine, "OpenSpaceEngine was already created"); - ghoul_assert(windowWrapper != nullptr, "No Window Wrapper was provided"); - - requestClose = false; - - ghoul::initialize(); - - // Initialize the LogManager and add the console log as this will be used every time - // and we need a fall back if something goes wrong between here and when we add the - // logs from the configuration file. If the user requested as specific loglevel in the - // configuration file, we will deinitialize this LogManager and reinitialize it later - // with the correct LogLevel - LogManager::initialize( - LogLevel::Debug, - ghoul::logging::LogManager::ImmediateFlush::Yes - ); - LogMgr.addLog(std::make_unique()); - - LDEBUG("Initialize FileSystem"); - -#ifdef __APPLE__ - ghoul::filesystem::File app(argv[0]); - std::string dirName = app.directoryName(); - LINFO("Setting starting directory to '" << dirName << "'"); - FileSys.setCurrentDirectory(dirName); -#endif - - // Sanity check of values - if (argc < 1 || argv == nullptr) { - throw ghoul::RuntimeError( - "No arguments were passed to this function", - "OpenSpaceEngine" - ); - } - - // Create other objects - LDEBUG("Creating OpenSpaceEngine"); - _engine = new OpenSpaceEngine(std::string(argv[0]), std::move(windowWrapper)); - - // Query modules for commandline arguments - _engine->gatherCommandlineArguments(); - - // Parse commandline arguments - std::vector args(argv, argv + argc); - std::shared_ptr> arguments = - _engine->_commandlineParser->setCommandLine(args); - - bool showHelp = _engine->_commandlineParser->execute(); - if (showHelp) { - _engine->_commandlineParser->displayHelp(); - requestClose = true; - return; - } - sgctArguments = *arguments; - - // Find configuration - std::string configurationFilePath = commandlineArgumentPlaceholders.configurationName; - if (configurationFilePath.empty()) { - LDEBUG("Finding configuration"); - configurationFilePath = - ConfigurationManager::findConfiguration(configurationFilePath); - } - configurationFilePath = absPath(configurationFilePath); - - if (!FileSys.fileExists(configurationFilePath)) { - throw ghoul::FileNotFoundError( - "Configuration file '" + configurationFilePath + "' not found" - ); - } - LINFO("Configuration Path: '" << configurationFilePath << "'"); - - // Loading configuration from disk - LDEBUG("Loading configuration from disk"); - try { - _engine->configurationManager().loadFromFile(configurationFilePath); - } - catch (const documentation::SpecificationError& e) { - LFATAL("Loading of configuration file '" << configurationFilePath << "' failed"); - for (const documentation::TestResult::Offense& o : e.result.offenses) { - LERRORC(o.offender, std::to_string(o.reason)); - } - for (const documentation::TestResult::Warning& w : e.result.warnings) { - LWARNINGC(w.offender, std::to_string(w.reason)); - } - throw; - } - catch (const ghoul::RuntimeError&) { - LFATAL("Loading of configuration file '" << configurationFilePath << "' failed"); - throw; - } - - const bool hasCacheCommandline = !commandlineArgumentPlaceholders.cacheFolder.empty(); - const bool hasCacheConfiguration = _engine->configurationManager().hasKeyAndValue( - ConfigurationManager::KeyPerSceneCache - ); - std::string cacheFolder = absPath("${CACHE}"); - if (hasCacheCommandline) { - cacheFolder = commandlineArgumentPlaceholders.cacheFolder; - // @CLEANUP: Why is this commented out? ---abock - //FileSys.registerPathToken( - // "${CACHE}", - // commandlineArgumentPlaceholders.cacheFolder, - // ghoul::filesystem::FileSystem::Override::Yes - //); - } - if (hasCacheConfiguration) { - std::string scene = _engine->configurationManager().value( - ConfigurationManager::KeyConfigScene - ); - cacheFolder += "-" + ghoul::filesystem::File(scene).baseName(); - } - - if (hasCacheCommandline || hasCacheConfiguration) { - LINFO("Old cache: " << absPath("${CACHE}")); - LINFO("New cache: " << cacheFolder); - FileSys.registerPathToken( - "${CACHE}", - cacheFolder, - ghoul::filesystem::FileSystem::Override::Yes - ); - } - - // Initialize the requested logs from the configuration file - _engine->configureLogging(); - - LINFOC("OpenSpace Version", - OPENSPACE_VERSION_MAJOR << "." << - OPENSPACE_VERSION_MINOR << "." << - OPENSPACE_VERSION_PATCH << - " (" << OPENSPACE_VERSION_STRING << ")" - ); - - // Create directories that doesn't exist - auto tokens = FileSys.tokens(); - for (const std::string& token : tokens) { - if (!FileSys.directoryExists(token)) { - std::string p = absPath(token); - LDEBUG("Directory '" << p << "' does not exist, creating."); - FileSys.createDirectory(p, ghoul::filesystem::FileSystem::Recursive::Yes); - } - } - - // Register modules - _engine->_moduleEngine->initialize(); - - // After registering the modules, the documentations for the available classes - // can be added as well - for (OpenSpaceModule* m : _engine->_moduleEngine->modules()) { - for (const documentation::Documentation& doc : m->documentations()) { - DocEng.addDocumentation(doc); - } - } - - // Create the cachemanager - FileSys.createCacheManager( - absPath("${" + ConfigurationManager::KeyCache + "}"), CacheVersion - ); - - // Register the provided shader directories - ghoul::opengl::ShaderPreprocessor::addIncludePath(absPath("${SHADERS}")); - - // Determining SGCT configuration file - LDEBUG("Determining SGCT configuration file"); - std::string sgctConfigurationPath = SgctDefaultConfigFile; - _engine->configurationManager().getValue( - ConfigurationManager::KeyConfigSgct, sgctConfigurationPath); - - if (!commandlineArgumentPlaceholders.sgctConfigurationName.empty()) { - LDEBUG("Overwriting SGCT configuration file with commandline argument: " << - commandlineArgumentPlaceholders.sgctConfigurationName); - sgctConfigurationPath = commandlineArgumentPlaceholders.sgctConfigurationName; - } - - // Prepend the outgoing sgctArguments with the program name - // as well as the configuration file that sgct is supposed to use - sgctArguments.insert(sgctArguments.begin(), argv[0]); - sgctArguments.insert(sgctArguments.begin() + 1, SgctConfigArgumentCommand); - sgctArguments.insert(sgctArguments.begin() + 2, absPath(sgctConfigurationPath)); -} - -void OpenSpaceEngine::destroy() { - if (_engine->parallelConnection().status() != ParallelConnection::Status::Disconnected) { - _engine->parallelConnection().signalDisconnect(); - } - - LTRACE("OpenSpaceEngine::destroy(begin)"); - for (const auto& func : _engine->_moduleCallbacks.deinitializeGL) { - func(); - } - - for (const auto& func : _engine->_moduleCallbacks.deinitialize) { - func(); - } - - _engine->_syncEngine->removeSyncables(_engine->timeManager().getSyncables()); - _engine->_syncEngine->removeSyncables(_engine->_renderEngine->getSyncables()); - _engine->_syncEngine->removeSyncable(_engine->_scriptEngine.get()); - - _engine->_moduleEngine->deinitialize(); - _engine->_console->deinitialize(); - - _engine->_scriptEngine->deinitialize(); - _engine->_sceneManager->unloadAll(); - - delete _engine; - FactoryManager::deinitialize(); - SpiceManager::deinitialize(); - - ghoul::fontrendering::FontRenderer::deinitialize(); - - LogManager::deinitialize(); - - ghoul::deinitialize(); - LTRACE("OpenSpaceEngine::destroy(end)"); -} - -void OpenSpaceEngine::initialize() { - LTRACE("OpenSpaceEngine::initialize(begin)"); - - glbinding::Binding::useCurrentContext(); - glbinding::Binding::initialize(); - - // clear the screen so the user don't have to see old buffer contents from the - // graphics card - LDEBUG("Clearing all Windows"); - _windowWrapper->clearAllWindows(glm::vec4(0.f, 0.f, 0.f, 1.f)); - - LDEBUG("Adding system components"); - // Detect and log OpenCL and OpenGL versions and available devices - SysCap.addComponent( - std::make_unique() - ); - SysCap.addComponent( - std::make_unique() - ); - - // @BUG: This will call OpenGL functions, should it should be in the initializeGL - LDEBUG("Detecting capabilities"); - SysCap.detectCapabilities(); - - using Verbosity = ghoul::systemcapabilities::SystemCapabilitiesComponent::Verbosity; - Verbosity verbosity = Verbosity::Default; - if (configurationManager().hasKey(ConfigurationManager::KeyCapabilitiesVerbosity)) { - static const std::map VerbosityMap = { - { "None", Verbosity::None }, - { "Minimal", Verbosity::Minimal }, - { "Default", Verbosity::Default }, - { "Full", Verbosity::Full } - }; - - std::string v = configurationManager().value( - ConfigurationManager::KeyCapabilitiesVerbosity - ); - ghoul_assert( - VerbosityMap.find(v) != VerbosityMap.end(), - "Missing check for syscaps verbosity in openspace.cfg documentation" - ); - verbosity = VerbosityMap.find(v)->second; - } - SysCap.logCapabilities(verbosity); - - // Check the required OpenGL versions of the registered modules - ghoul::systemcapabilities::Version version = - _engine->_moduleEngine->requiredOpenGLVersion(); - LINFO("Required OpenGL version: " << std::to_string(version)); - - if (OpenGLCap.openGLVersion() < version) { - throw ghoul::RuntimeError( - "Module required higher OpenGL version than is supported", - "OpenSpaceEngine" - ); - } - - if (configurationManager().hasKey(ConfigurationManager::KeyDownloadRequestURL)) { - const std::string requestUrl = configurationManager().value( - ConfigurationManager::KeyDownloadRequestURL - ); - - _downloadManager = std::make_unique( - requestUrl, - DownloadVersion - ); - } - - // Register Lua script functions - LDEBUG("Registering Lua libraries"); - registerCoreClasses(*_scriptEngine); - - for (OpenSpaceModule* module : _moduleEngine->modules()) { - _scriptEngine->addLibrary(module->luaLibrary()); - } - - // TODO: Maybe move all scenegraph and renderengine stuff to initializeGL - scriptEngine().initialize(); - - writeDocumentation(); - - if (configurationManager().hasKey(ConfigurationManager::KeyShutdownCountdown)) { - _shutdown.waitTime = static_cast(configurationManager().value( - ConfigurationManager::KeyShutdownCountdown - )); - } - - if (!commandlineArgumentPlaceholders.sceneName.empty()) { - configurationManager().setValue( - ConfigurationManager::KeyConfigScene, - commandlineArgumentPlaceholders.sceneName - ); - } - - // Initialize the SettingsEngine - _settingsEngine->initialize(); - _settingsEngine->setModules(_moduleEngine->modules()); - - // Initialize the InteractionHandler - _interactionHandler->initialize(); - - // Load a light and a monospaced font - loadFonts(); - - std::string scenePath = ""; - configurationManager().getValue(ConfigurationManager::KeyConfigScene, scenePath); - - _renderEngine->initialize(); - - for (const auto& func : _moduleCallbacks.initialize) { - func(); - } - - scheduleLoadScene(scenePath); - - LTRACE("OpenSpaceEngine::initialize(end)"); - -} - -void OpenSpaceEngine::scheduleLoadScene(std::string scenePath) { - _scheduledSceneSwitch = true; - _scenePath = std::move(scenePath); -} - -void OpenSpaceEngine::loadScene(const std::string& scenePath) { - LTRACE("OpenSpaceEngine::loadScene(begin)"); - - windowWrapper().setBarrier(false); - windowWrapper().setSynchronization(false); - OnExit( - [this]() { - windowWrapper().setSynchronization(true); - windowWrapper().setBarrier(true); - } - ); - - // Run start up scripts - try { - runPreInitializationScripts(scenePath); - } - catch (const ghoul::RuntimeError& e) { - LERRORC(e.component, e.message); - } - - Scene* scene; - try { - scene = _sceneManager->loadScene(scenePath); - } catch (const ghoul::FileNotFoundError& e) { - LERRORC(e.component, e.message); - return; - } catch (const Scene::InvalidSceneError& e) { - LERRORC(e.component, e.message); - return; - } catch (const ghoul::RuntimeError& e) { - LERRORC(e.component, e.message); - return; - } - catch (const std::exception& e) { - LERROR(e.what()); - return; - } - catch (...) { - LERROR("Unknown error loading the scene"); - return; - } - - Scene* previousScene = _renderEngine->scene(); - if (previousScene) { - _syncEngine->removeSyncables(_timeManager->getSyncables()); - _syncEngine->removeSyncables(_renderEngine->getSyncables()); - _syncEngine->removeSyncable(_scriptEngine.get()); - - _renderEngine->setScene(nullptr); - _renderEngine->setCamera(nullptr); - _sceneManager->unloadScene(*previousScene); - } - - // Initialize the RenderEngine - _renderEngine->setScene(scene); - _renderEngine->setCamera(scene->camera()); - _renderEngine->setGlobalBlackOutFactor(0.0); - _renderEngine->startFading(1, 3.0); - - scene->initialize(); - _interactionHandler->setCamera(scene->camera()); - - try { - runPostInitializationScripts(scenePath); - } - catch (const ghoul::RuntimeError& e) { - LFATALC(e.component, e.message); - } - - // Write keyboard documentation. - if (configurationManager().hasKey(ConfigurationManager::KeyKeyboardShortcuts)) { - interactionHandler().writeDocumentation( - absPath(configurationManager().value( - ConfigurationManager::KeyKeyboardShortcuts - )) - ); - } - - // If a PropertyDocumentationFile was specified, generate it now. - if (configurationManager().hasKey(ConfigurationManager::KeyPropertyDocumentation)) { - scene->writeDocumentation( - absPath(configurationManager().value( - ConfigurationManager::KeyPropertyDocumentation - )) - ); - } - - _syncEngine->addSyncables(_timeManager->getSyncables()); - _syncEngine->addSyncables(_renderEngine->getSyncables()); - _syncEngine->addSyncable(_scriptEngine.get()); - - LTRACE("OpenSpaceEngine::loadScene(end)"); -} - -void OpenSpaceEngine::deinitialize() { - LTRACE("OpenSpaceEngine::deinitialize(begin)"); - - _interactionHandler->deinitialize(); - _renderEngine->deinitialize(); - - LTRACE("OpenSpaceEngine::deinitialize(end)"); -} - -void OpenSpaceEngine::writeDocumentation() { - // If a LuaDocumentationFile was specified, generate it now - if (configurationManager().hasKey(ConfigurationManager::KeyLuaDocumentation)) { - _scriptEngine->writeDocumentation( - absPath(configurationManager().value( - ConfigurationManager::KeyLuaDocumentation - )) - ); - } - - // If a general documentation was specified, generate it now - if (configurationManager().hasKey(ConfigurationManager::KeyDocumentation)) { - DocEng.writeDocumentation( - absPath(configurationManager().value( - ConfigurationManager::KeyDocumentation - )) - ); - } - - // If a factory documentation was specified, generate it now - if (configurationManager().hasKey(ConfigurationManager::KeyFactoryDocumentation)) { - FactoryManager::ref().writeDocumentation( - absPath(configurationManager().value( - ConfigurationManager::KeyFactoryDocumentation - )) - ); - } -} - -void OpenSpaceEngine::gatherCommandlineArguments() { - commandlineArgumentPlaceholders.configurationName = ""; - _commandlineParser->addCommand(std::make_unique>( - &commandlineArgumentPlaceholders.configurationName, "-config", "-c", - "Provides the path to the OpenSpace configuration file" - )); - - commandlineArgumentPlaceholders.sgctConfigurationName = ""; - _commandlineParser->addCommand(std::make_unique>( - &commandlineArgumentPlaceholders.sgctConfigurationName, "-sgct", "-s", - "Provides the path to the SGCT configuration file, overriding the value set in " - "the OpenSpace configuration file" - )); - - commandlineArgumentPlaceholders.sceneName = ""; - _commandlineParser->addCommand(std::make_unique>( - &commandlineArgumentPlaceholders.sceneName, "-scene", "", "Provides the path to " - "the scene file, overriding the value set in the OpenSpace configuration file" - )); - - commandlineArgumentPlaceholders.cacheFolder = ""; - _commandlineParser->addCommand(std::make_unique>( - &commandlineArgumentPlaceholders.cacheFolder, "-cacheDir", "", "Provides the " - "path to a cache file, overriding the value set in the OpenSpace configuration " - "file" - )); -} - -void OpenSpaceEngine::runPreInitializationScripts(const std::string& sceneDescription) { - // @CLEANUP: Move this into the scene loading? ---abock - LINFO("Running Initialization scripts"); - - ghoul::lua::LuaState state; - OsEng.scriptEngine().initializeLuaState(state); - - // First execute the script to get all global variables - ghoul::lua::runScriptFile(state, absPath(sceneDescription)); - - // Get the preinitialize function - lua_getglobal(state, PreInitializeFunction); - bool isFunction = lua_isfunction(state, -1); - if (!isFunction) { - LERROR( - "Error executing startup script '" << sceneDescription << "'. Scene '" << - sceneDescription << "' does not have a function '" << - PreInitializeFunction << "'" - ); - return; - } - - // And execute the preinitialize function - int success = lua_pcall(state, 0, 0, 0); - if (success != 0) { - LERROR( - "Error executing '" << PreInitializeFunction << "': " << - lua_tostring(state, -1) - ); - } -} - -void OpenSpaceEngine::runPostInitializationScripts(const std::string& sceneDescription) { - // @CLEANUP: Move this into the scene loading? ---abock - LINFO("Running Setup scripts"); - ghoul::lua::LuaState state; - OsEng.scriptEngine().initializeLuaState(state); - - // First execute the script to get all global variables - ghoul::lua::runScriptFile(state, absPath(sceneDescription)); - - // Get the preinitialize function - lua_getglobal(state, PostInitializationFunction); - bool isFunction = lua_isfunction(state, -1); - if (!isFunction) { - LERROR( - "Error executing startup script '" << sceneDescription << "'. Scene '" << - sceneDescription << "' does not have a function '" << - PostInitializationFunction << "'" - ); - return; - } - - // And execute the preinitialize function - int success = lua_pcall(state, 0, 0, 0); - if (success != 0) { - LERROR( - "Error executing '" << PostInitializationFunction << "': " << - lua_tostring(state, -1) - ); - } -} - -void OpenSpaceEngine::loadFonts() { - ghoul::Dictionary fonts; - configurationManager().getValue(ConfigurationManager::KeyFonts, fonts); - - _fontManager = std::make_unique(FontAtlasSize); - - for (const std::string& key : fonts.keys()) { - std::string font = fonts.value(key); - font = absPath(font); - - if (!FileSys.fileExists(font)) { - LERROR("Could not find font '" << font << "'"); - continue; - } - - LINFO("Registering font '" << font << "' with key '" << key << "'"); - bool success = _fontManager->registerFontPath(key, font); - - if (!success) { - LERROR("Error registering font '" << font << "' with key '" << key << "'"); - } - } - - try { - bool initSuccess = ghoul::fontrendering::FontRenderer::initialize(); - if (!initSuccess) { - LERROR("Error initializing default font renderer"); - } - - ghoul::fontrendering::FontRenderer::defaultRenderer().setFramebufferSize( - _renderEngine->fontResolution() - ); - } - catch (const ghoul::RuntimeError& err) { - LERRORC(err.component, err.message); - } -} - -void OpenSpaceEngine::configureLogging() { - const std::string KeyLogLevel = - ConfigurationManager::KeyLogging + '.' + ConfigurationManager::PartLogLevel; - const std::string KeyLogImmediateFlush = - ConfigurationManager::KeyLogging + '.' + ConfigurationManager::PartImmediateFlush; - const std::string KeyLogs = - ConfigurationManager::KeyLogging + '.' + ConfigurationManager::PartLogs; - - if (configurationManager().hasKeyAndValue(KeyLogLevel)) { - std::string logLevel = "Info"; - configurationManager().getValue(KeyLogLevel, logLevel); - - bool immediateFlush = false; - configurationManager().getValue(KeyLogImmediateFlush, immediateFlush); - - LogLevel level = ghoul::logging::levelFromString(logLevel); - LogManager::deinitialize(); - using ImmediateFlush = ghoul::logging::LogManager::ImmediateFlush; - LogManager::initialize( - level, - immediateFlush ? ImmediateFlush::Yes : ImmediateFlush::No - ); - - LogMgr.addLog(std::make_unique()); - } - - if (configurationManager().hasKeyAndValue(KeyLogs)) { - ghoul::Dictionary logs = configurationManager().value(KeyLogs); - - for (size_t i = 1; i <= logs.size(); ++i) { - ghoul::Dictionary logInfo = logs.value(std::to_string(i)); - - try { - LogMgr.addLog(createLog(logInfo)); - } - catch (const ghoul::RuntimeError& e) { - LERRORC(e.component, e.message); - } - } - } - -#ifdef WIN32 - if (IsDebuggerPresent()) { - LogMgr.addLog(std::make_unique()); - } -#endif // WIN32 - -#ifndef GHOUL_LOGGING_ENABLE_TRACE - std::string logLevel = "Info"; - configurationManager().getValue(KeyLogLevel, logLevel); - LogLevel level = ghoul::logging::levelFromString(logLevel); - - if (level == ghoul::logging::LogLevel::Trace) { - LWARNING( - "Desired logging level is set to 'Trace' but application was " << - "compiled without Trace support" - ); - } -#endif // GHOUL_LOGGING_ENABLE_TRACE -} - -void OpenSpaceEngine::initializeGL() { - LTRACE("OpenSpaceEngine::initializeGL(begin)"); - - LTRACE("OpenSpaceEngine::initializeGL::Console::initialize(begin)"); - try { - _engine->_console->initialize(); - } - catch (ghoul::RuntimeError& e) { - LERROR("Error initializing Console with error:"); - LERRORC(e.component, e.message); - } - LTRACE("OpenSpaceEngine::initializeGL::Console::initialize(end)"); - - const std::string key = ConfigurationManager::KeyOpenGLDebugContext; - if (_configurationManager->hasKey(key)) { - LTRACE("OpenSpaceEngine::initializeGL::DebugContext(begin)"); - ghoul::Dictionary dict = _configurationManager->value(key); - bool debug = dict.value(ConfigurationManager::PartActivate); - - // Debug output is not available before 4.3 - const ghoul::systemcapabilities::Version minVersion = { 4, 3, 0 }; - if (OpenGLCap.openGLVersion() < minVersion) { - LINFO("OpenGL Debug context requested, but insufficient version available"); - debug = false; - } - - if (debug) { - using namespace ghoul::opengl::debug; - - bool synchronous = true; - if (dict.hasKey(ConfigurationManager::PartSynchronous)) { - synchronous = dict.value(ConfigurationManager::PartSynchronous); - } - - setDebugOutput(DebugOutput(debug), SynchronousOutput(synchronous)); - - - if (dict.hasKey(ConfigurationManager::PartFilterIdentifier)) { - ghoul::Dictionary filterDict = dict.value( - ConfigurationManager::PartFilterIdentifier - ); - - for (size_t i = 1; i <= filterDict.size(); ++i) { - ghoul::Dictionary id = filterDict.value( - std::to_string(i) - ); - - const unsigned int identifier = static_cast( - id.value( - ConfigurationManager::PartFilterIdentifierIdentifier - ) - ); - - const std::string s = id.value( - ConfigurationManager::PartFilterIdentifierSource - ); - - const std::string t = id.value( - ConfigurationManager::PartFilterIdentifierType - ); - - setDebugMessageControl( - ghoul::from_string(s), - ghoul::from_string(t), - { identifier }, - Enabled::No - ); - } - } - - if (dict.hasKey(ConfigurationManager::PartFilterSeverity)) { - ghoul::Dictionary filterDict = dict.value( - ConfigurationManager::PartFilterIdentifier - ); - - for (size_t i = 1; i <= filterDict.size(); ++i) { - std::string severity = filterDict.value( - std::to_string(i) - ); - - setDebugMessageControl( - Source::DontCare, - Type::DontCare, - ghoul::from_string(severity), - Enabled::No - ); - } - } - - auto callback = [](Source source, Type type, Severity severity, - unsigned int id, std::string message) -> void - { - const std::string s = std::to_string(source); - const std::string t = std::to_string(type); - - const std::string category = - "OpenGL (" + s + ") [" + t + "] {" + std::to_string(id) + "}"; - switch (severity) { - case Severity::High: - LERRORC(category, std::string(message)); - break; - case Severity::Medium: - LWARNINGC(category, std::string(message)); - break; - case Severity::Low: - LINFOC(category, std::string(message)); - break; - case Severity::Notification: - LDEBUGC(category, std::string(message)); - break; - default: - throw ghoul::MissingCaseException(); - } - }; - ghoul::opengl::debug::setDebugCallback(callback); - } - LTRACE("OpenSpaceEngine::initializeGL::DebugContext(end)"); - } - - - LINFO("Initializing Rendering Engine"); - _renderEngine->initializeGL(); - - for (const auto& func : _moduleCallbacks.initializeGL) { - func(); - } - - LINFO("Finished initializing OpenGL"); - - LINFO("IsUsingSwapGroups: " << _windowWrapper->isUsingSwapGroups()); - LINFO("IsSwapGroupMaster: " << _windowWrapper->isSwapGroupMaster()); - - LTRACE("OpenSpaceEngine::initializeGL(end)"); -} - -double OpenSpaceEngine::runTime() { - return _runTime; -} - -void OpenSpaceEngine::setRunTime(double d) { - _runTime = d; -} - -void OpenSpaceEngine::preSynchronization() { - LTRACE("OpenSpaceEngine::preSynchronization(begin)"); - FileSys.triggerFilesystemEvents(); - - if (_scheduledSceneSwitch) { - loadScene(_scenePath); - _scheduledSceneSwitch = false; - } - - if (_isFirstRenderingFirstFrame) { - _windowWrapper->setSynchronization(false); - } - - bool master = _windowWrapper->isMaster(); - - _syncEngine->preSynchronization(SyncEngine::IsMaster(master)); - if (master) { - double dt = _windowWrapper->averageDeltaTime(); - _timeManager->preSynchronization(dt); - - using Iter = std::vector::const_iterator; - std::pair scheduledScripts = _scriptScheduler->progressTo( - timeManager().time().j2000Seconds() - ); - for (Iter it = scheduledScripts.first; it != scheduledScripts.second; ++it) { - _scriptEngine->queueScript( - *it, ScriptEngine::RemoteScripting::Yes - ); - } - - _interactionHandler->updateInputStates(dt); - - _renderEngine->updateScene(); - _interactionHandler->updateCamera(dt); - _renderEngine->camera()->invalidateCache(); - - _parallelConnection->preSynchronization(); - } - - for (const auto& func : _moduleCallbacks.preSync) { - func(); - } - LTRACE("OpenSpaceEngine::preSynchronization(end)"); -} - -void OpenSpaceEngine::postSynchronizationPreDraw() { - LTRACE("OpenSpaceEngine::postSynchronizationPreDraw(begin)"); - - bool master = _windowWrapper->isMaster(); - _syncEngine->postSynchronization(SyncEngine::IsMaster(master)); - - if (_shutdown.inShutdown) { - if (_shutdown.timer <= 0.f) { - _windowWrapper->terminate(); - } - _shutdown.timer -= static_cast(_windowWrapper->averageDeltaTime()); - } - - _renderEngine->updateScene(); - _renderEngine->updateFade(); - _renderEngine->updateRenderer(); - _renderEngine->updateScreenSpaceRenderables(); - _renderEngine->updateShaderPrograms(); - - if (!master) { - _renderEngine->camera()->invalidateCache(); - } - - // Step the camera using the current mouse velocities which are synced - //_interactionHandler->updateCamera(); - - for (const auto& func : _moduleCallbacks.postSyncPreDraw) { - func(); - } - - // Testing this every frame has minimal impact on the performance --- abock - // Debug build: 1-2 us ; Release build: <= 1 us - using ghoul::logging::LogManager; - int warningCounter = LogMgr.messageCounter(LogLevel::Warning); - int errorCounter = LogMgr.messageCounter(LogLevel::Error); - int fatalCounter = LogMgr.messageCounter(LogLevel::Fatal); - - if (warningCounter > 0) { - LWARNINGC("Logging", "Number of Warnings raised: " << warningCounter); - } - if (errorCounter > 0) { - LWARNINGC("Logging", "Number of Errors raised: " << errorCounter); - } - if (fatalCounter > 0) { - LWARNINGC("Logging", "Number of Fatals raised: " << fatalCounter); - } - - LogMgr.resetMessageCounters(); - - LTRACE("OpenSpaceEngine::postSynchronizationPreDraw(end)"); -} - -void OpenSpaceEngine::render(const glm::mat4& sceneMatrix, - const glm::mat4& viewMatrix, - const glm::mat4& projectionMatrix) -{ - bool isGuiWindow = _windowWrapper->hasGuiWindow() ? _windowWrapper->isGuiWindow() : true; - bool showOverlay = isGuiWindow && _windowWrapper->isMaster() && _windowWrapper->isRegularRendering(); - // @CLEANUP: Replace the two windows by a single call to whether a gui should be - // rendered ---abock - - if (showOverlay) { - _console->update(); - } - - LTRACE("OpenSpaceEngine::render(begin)"); - _renderEngine->render(sceneMatrix, viewMatrix, projectionMatrix); - - for (const auto& func : _moduleCallbacks.render) { - func(); - } - - if (showOverlay) { - _renderEngine->renderScreenLog(); - _console->render(); - } - - if (_shutdown.inShutdown) { - _renderEngine->renderShutdownInformation(_shutdown.timer, _shutdown.waitTime); - } - - LTRACE("OpenSpaceEngine::render(end)"); -} - -void OpenSpaceEngine::postDraw() { - LTRACE("OpenSpaceEngine::postDraw(begin)"); - - _renderEngine->postDraw(); - - for (const auto& func : _moduleCallbacks.postDraw) { - func(); - } - - if (_isFirstRenderingFirstFrame) { - _windowWrapper->setSynchronization(true); - _isFirstRenderingFirstFrame = false; - } - - LTRACE("OpenSpaceEngine::postDraw(end)"); -} - -void OpenSpaceEngine::keyboardCallback(Key key, KeyModifier mod, KeyAction action) { - for (const auto& func : _moduleCallbacks.keyboard) { - const bool consumed = func(key, mod, action); - if (consumed) { - return; - } - } - - const bool consoleConsumed = _console->keyboardCallback(key, mod, action); - if (consoleConsumed) { - return; - } - - _interactionHandler->keyboardCallback(key, mod, action); -} - -void OpenSpaceEngine::charCallback(unsigned int codepoint, KeyModifier modifier) { - for (const auto& func : _moduleCallbacks.character) { - bool consumed = func(codepoint, modifier); - if (consumed) { - return; - } - } - - _console->charCallback(codepoint, modifier); -} - -void OpenSpaceEngine::mouseButtonCallback(MouseButton button, MouseAction action) { - for (const auto& func : _moduleCallbacks.mouseButton) { - bool consumed = func(button, action); - if (consumed) { - return; - } - } - - _interactionHandler->mouseButtonCallback(button, action); -} - -void OpenSpaceEngine::mousePositionCallback(double x, double y) { - for (const auto& func : _moduleCallbacks.mousePosition) { - func(x, y); - } - - _interactionHandler->mousePositionCallback(x, y); -} - -void OpenSpaceEngine::mouseScrollWheelCallback(double pos) { - for (const auto& func : _moduleCallbacks.mouseScrollWheel) { - bool consumed = func(pos); - if (consumed) { - return; - } - } - - _interactionHandler->mouseScrollWheelCallback(pos); -} - -void OpenSpaceEngine::encode() { - _syncEngine->encodeSyncables(); - - _networkEngine->publishStatusMessage(); - _networkEngine->sendMessages(); -} - -void OpenSpaceEngine::decode() { - _syncEngine->decodeSyncables(); -} - -void OpenSpaceEngine::externalControlCallback(const char* receivedChars, int size, - int clientId) -{ - if (size == 0) { - return; - } - - _networkEngine->handleMessage(std::string(receivedChars, size)); -} - -void OpenSpaceEngine::toggleShutdownMode() { - if (_shutdown.inShutdown) { - // If we are already in shutdown mode, we want to disable it - LINFO("Disabled shutdown mode"); - _shutdown.inShutdown = false; - } - else { - // Else, we have to enable it - LINFO("Shutting down OpenSpace"); - _shutdown.timer = _shutdown.waitTime; - _shutdown.inShutdown = true; - } -} - -scripting::LuaLibrary OpenSpaceEngine::luaLibrary() { - return { - "", - { - { - "toggleShutdown", - &luascriptfunctions::toggleShutdown, - "", - "Toggles the shutdown mode that will close the application after the count" - "down timer is reached" - }, - { - "writeDocumentation", - &luascriptfunctions::writeDocumentation, - "", - "Writes out documentation files" - }, - { - "downloadFile", - &luascriptfunctions::downloadFile, - "", - "Downloads a file from Lua scope" - }, - { - "addVirtualProperty", - &luascriptfunctions::addVirtualProperty, - "type, name, identifier, [value, minimumValue, maximumValue]", - "Adds a virtual property that will set a group of properties" - }, - { - "removeVirtualProperty", - &luascriptfunctions::removeVirtualProperty, - "string", - "Removes a previously added virtual property" - }, - { - "removeAllVirtualProperties", - &luascriptfunctions::removeAllVirtualProperties, - "", - "Remove all registered virtual properties" - } - } - }; -} - -void OpenSpaceEngine::enableBarrier() { - _windowWrapper->setBarrier(true); -} - -void OpenSpaceEngine::disableBarrier() { - _windowWrapper->setBarrier(false); -} - -// Registers a callback for a specific CallbackOption -void OpenSpaceEngine::registerModuleCallback(OpenSpaceEngine::CallbackOption option, - std::function function) -{ - switch (option) { - case CallbackOption::Initialize: - _moduleCallbacks.initialize.push_back(std::move(function)); - break; - case CallbackOption::Deinitialize: - _moduleCallbacks.deinitialize.push_back(std::move(function)); - break; - case CallbackOption::InitializeGL: - _moduleCallbacks.initializeGL.push_back(std::move(function)); - break; - case CallbackOption::DeinitializeGL: - _moduleCallbacks.deinitializeGL.push_back(std::move(function)); - break; - case CallbackOption::PreSync: - _moduleCallbacks.preSync.push_back(std::move(function)); - break; - case CallbackOption::PostSyncPreDraw: - _moduleCallbacks.postSyncPreDraw.push_back(std::move(function)); - break; - case CallbackOption::Render: - _moduleCallbacks.render.push_back(std::move(function)); - break; - case CallbackOption::PostDraw: - _moduleCallbacks.postDraw.push_back(std::move(function)); - break; - default: - throw ghoul::MissingCaseException(); - } -} - -void OpenSpaceEngine::registerModuleKeyboardCallback( - std::function function) -{ - _moduleCallbacks.keyboard.push_back(std::move(function)); -} - -void OpenSpaceEngine::registerModuleCharCallback( - std::function function) -{ - _moduleCallbacks.character.push_back(std::move(function)); -} - -void OpenSpaceEngine::registerModuleMouseButtonCallback( - std::function function) -{ - _moduleCallbacks.mouseButton.push_back(std::move(function)); -} - -void OpenSpaceEngine::registerModuleMousePositionCallback( - std::function function) -{ - _moduleCallbacks.mousePosition.push_back(std::move(function)); -} - -void OpenSpaceEngine::registerModuleMouseScrollWheelCallback( - std::function function) -{ - _moduleCallbacks.mouseScrollWheel.push_back(std::move(function)); -} - -ConfigurationManager& OpenSpaceEngine::configurationManager() { - ghoul_assert(_configurationManager, "ConfigurationManager must not be nullptr"); - return *_configurationManager; -} - -LuaConsole& OpenSpaceEngine::console() { - ghoul_assert(_console, "LuaConsole must not be nullptr"); - return *_console; -} - -DownloadManager& OpenSpaceEngine::downloadManager() { - ghoul_assert(_downloadManager, "Download Manager must not be nullptr"); - return *_downloadManager; -} - -NetworkEngine& OpenSpaceEngine::networkEngine() { - ghoul_assert(_networkEngine, "NetworkEngine must not be nullptr"); - return *_networkEngine; -} - -ModuleEngine& OpenSpaceEngine::moduleEngine() { - ghoul_assert(_moduleEngine, "ModuleEngine must not be nullptr"); - return *_moduleEngine; -} - -ParallelConnection& OpenSpaceEngine::parallelConnection() { - ghoul_assert(_parallelConnection, "ParallelConnection must not be nullptr"); - return *_parallelConnection; -} - -RenderEngine& OpenSpaceEngine::renderEngine() { - ghoul_assert(_renderEngine, "RenderEngine must not be nullptr"); - return *_renderEngine; -} - -SettingsEngine& OpenSpaceEngine::settingsEngine() { - ghoul_assert(_settingsEngine, "Settings Engine must not be nullptr"); - return *_settingsEngine; -} - -TimeManager& OpenSpaceEngine::timeManager() { - ghoul_assert(_timeManager, "Download Manager must not be nullptr"); - return *_timeManager; -} - -WindowWrapper& OpenSpaceEngine::windowWrapper() { - ghoul_assert(_windowWrapper, "Window Wrapper must not be nullptr"); - return *_windowWrapper; -} - -ghoul::fontrendering::FontManager& OpenSpaceEngine::fontManager() { - ghoul_assert(_fontManager, "Font Manager must not be nullptr"); - return *_fontManager; -} - -interaction::InteractionHandler& OpenSpaceEngine::interactionHandler() { - ghoul_assert(_interactionHandler, "InteractionHandler must not be nullptr"); - return *_interactionHandler; -} - -properties::PropertyOwner& OpenSpaceEngine::globalPropertyOwner() { - ghoul_assert( - _globalPropertyNamespace, - "Global Property Namespace must not be nullptr" - ); - return *_globalPropertyNamespace; -} - -VirtualPropertyManager& OpenSpaceEngine::virtualPropertyManager() { - ghoul_assert( - _virtualPropertyManager, - "Virtual Property Manager must not be nullptr" - ); - - return *_virtualPropertyManager; -} - -ScriptEngine& OpenSpaceEngine::scriptEngine() { - ghoul_assert(_scriptEngine, "ScriptEngine must not be nullptr"); - return *_scriptEngine; -} - -ScriptScheduler& OpenSpaceEngine::scriptScheduler() { - ghoul_assert(_scriptScheduler, "ScriptScheduler must not be nullptr"); - return *_scriptScheduler; -} - -} // namespace openspace +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if defined(_MSC_VER) && defined(OPENSPACE_ENABLE_VLD) +#include +#endif + +#ifdef WIN32 +#include +#endif + +#include "openspaceengine_lua.inl" + +using namespace openspace::scripting; +using namespace ghoul::filesystem; +using namespace ghoul::logging; +using namespace ghoul::cmdparser; + +namespace { + const char* _loggerCat = "OpenSpaceEngine"; + const char* SgctDefaultConfigFile = "${SGCT}/single.xml"; + + const char* SgctConfigArgumentCommand = "-config"; + + const char* PreInitializeFunction = "preInitialization"; + const char* PostInitializationFunction = "postInitialization"; + + const int CacheVersion = 1; + const int DownloadVersion = 1; + + const glm::ivec3 FontAtlasSize{ 1536, 1536, 1 }; + + + struct { + std::string configurationName; + std::string sgctConfigurationName; + std::string sceneName; + std::string cacheFolder; + } commandlineArgumentPlaceholders; +} + +namespace openspace { + +namespace properties { + class Property; +} + +class Scene; + +OpenSpaceEngine* OpenSpaceEngine::_engine = nullptr; + +OpenSpaceEngine::OpenSpaceEngine(std::string programName, + std::unique_ptr windowWrapper) + : _configurationManager(new ConfigurationManager) + , _sceneManager(new SceneManager) + , _downloadManager(nullptr) + , _console(new LuaConsole) + , _moduleEngine(new ModuleEngine) + , _networkEngine(new NetworkEngine) + , _parallelConnection(new ParallelConnection) + , _renderEngine(new RenderEngine) + , _settingsEngine(new SettingsEngine) + , _syncEngine(std::make_unique(4096)) + , _timeManager(new TimeManager) + , _windowWrapper(std::move(windowWrapper)) + , _commandlineParser(new ghoul::cmdparser::CommandlineParser( + programName, ghoul::cmdparser::CommandlineParser::AllowUnknownCommands::Yes + )) + , _interactionHandler(new interaction::InteractionHandler) + , _scriptEngine(new scripting::ScriptEngine) + , _scriptScheduler(new scripting::ScriptScheduler) + , _virtualPropertyManager(new VirtualPropertyManager) + , _globalPropertyNamespace(new properties::PropertyOwner("")) + , _scheduledSceneSwitch(false) + , _scenePath("") + , _runTime(0.0) + , _shutdown({false, 0.f, 0.f}) + , _isFirstRenderingFirstFrame(true) +{ + _interactionHandler->setPropertyOwner(_globalPropertyNamespace.get()); + + // New property subowners also have to be added to the OnScreenGuiModule callback! + _globalPropertyNamespace->addPropertySubOwner(_interactionHandler.get()); + _globalPropertyNamespace->addPropertySubOwner(_settingsEngine.get()); + _globalPropertyNamespace->addPropertySubOwner(_renderEngine.get()); + _globalPropertyNamespace->addPropertySubOwner(_windowWrapper.get()); + _globalPropertyNamespace->addPropertySubOwner(_parallelConnection.get()); + _globalPropertyNamespace->addPropertySubOwner(_console.get()); + + FactoryManager::initialize(); + FactoryManager::ref().addFactory( + std::make_unique>(), + "Renderable" + ); + FactoryManager::ref().addFactory( + std::make_unique>(), + "Translation" + ); + FactoryManager::ref().addFactory( + std::make_unique>(), + "Rotation" + ); + FactoryManager::ref().addFactory( + std::make_unique>(), + "Scale" + ); + FactoryManager::ref().addFactory( + std::make_unique>(), + "Task" + ); + + SpiceManager::initialize(); + TransformationManager::initialize(); +} + +OpenSpaceEngine& OpenSpaceEngine::ref() { + ghoul_assert(_engine, "OpenSpaceEngine not created"); + return *_engine; +} + +bool OpenSpaceEngine::isCreated() { + return _engine != nullptr; +} + +void OpenSpaceEngine::create(int argc, char** argv, + std::unique_ptr windowWrapper, + std::vector& sgctArguments, bool& requestClose) +{ + ghoul_assert(!_engine, "OpenSpaceEngine was already created"); + ghoul_assert(windowWrapper != nullptr, "No Window Wrapper was provided"); + + requestClose = false; + + ghoul::initialize(); + + // Initialize the LogManager and add the console log as this will be used every time + // and we need a fall back if something goes wrong between here and when we add the + // logs from the configuration file. If the user requested as specific loglevel in the + // configuration file, we will deinitialize this LogManager and reinitialize it later + // with the correct LogLevel + LogManager::initialize( + LogLevel::Debug, + ghoul::logging::LogManager::ImmediateFlush::Yes + ); + LogMgr.addLog(std::make_unique()); + + LDEBUG("Initialize FileSystem"); + +#ifdef __APPLE__ + ghoul::filesystem::File app(argv[0]); + std::string dirName = app.directoryName(); + LINFO("Setting starting directory to '" << dirName << "'"); + FileSys.setCurrentDirectory(dirName); +#endif + + // Sanity check of values + if (argc < 1 || argv == nullptr) { + throw ghoul::RuntimeError( + "No arguments were passed to this function", + "OpenSpaceEngine" + ); + } + + // Create other objects + LDEBUG("Creating OpenSpaceEngine"); + _engine = new OpenSpaceEngine(std::string(argv[0]), std::move(windowWrapper)); + + // Query modules for commandline arguments + _engine->gatherCommandlineArguments(); + + // Parse commandline arguments + std::vector args(argv, argv + argc); + std::shared_ptr> arguments = + _engine->_commandlineParser->setCommandLine(args); + + bool showHelp = _engine->_commandlineParser->execute(); + if (showHelp) { + _engine->_commandlineParser->displayHelp(); + requestClose = true; + return; + } + sgctArguments = *arguments; + + // Find configuration + std::string configurationFilePath = commandlineArgumentPlaceholders.configurationName; + if (configurationFilePath.empty()) { + LDEBUG("Finding configuration"); + configurationFilePath = + ConfigurationManager::findConfiguration(configurationFilePath); + } + configurationFilePath = absPath(configurationFilePath); + + if (!FileSys.fileExists(configurationFilePath)) { + throw ghoul::FileNotFoundError( + "Configuration file '" + configurationFilePath + "' not found" + ); + } + LINFO("Configuration Path: '" << configurationFilePath << "'"); + + // Loading configuration from disk + LDEBUG("Loading configuration from disk"); + try { + _engine->configurationManager().loadFromFile(configurationFilePath); + } + catch (const documentation::SpecificationError& e) { + LFATAL("Loading of configuration file '" << configurationFilePath << "' failed"); + for (const documentation::TestResult::Offense& o : e.result.offenses) { + LERRORC(o.offender, std::to_string(o.reason)); + } + for (const documentation::TestResult::Warning& w : e.result.warnings) { + LWARNINGC(w.offender, std::to_string(w.reason)); + } + throw; + } + catch (const ghoul::RuntimeError&) { + LFATAL("Loading of configuration file '" << configurationFilePath << "' failed"); + throw; + } + + const bool hasCacheCommandline = !commandlineArgumentPlaceholders.cacheFolder.empty(); + const bool hasCacheConfiguration = _engine->configurationManager().hasKeyAndValue( + ConfigurationManager::KeyPerSceneCache + ); + std::string cacheFolder = absPath("${CACHE}"); + if (hasCacheCommandline) { + cacheFolder = commandlineArgumentPlaceholders.cacheFolder; + // @CLEANUP: Why is this commented out? ---abock + //FileSys.registerPathToken( + // "${CACHE}", + // commandlineArgumentPlaceholders.cacheFolder, + // ghoul::filesystem::FileSystem::Override::Yes + //); + } + if (hasCacheConfiguration) { + std::string scene = _engine->configurationManager().value( + ConfigurationManager::KeyConfigScene + ); + cacheFolder += "-" + ghoul::filesystem::File(scene).baseName(); + } + + if (hasCacheCommandline || hasCacheConfiguration) { + LINFO("Old cache: " << absPath("${CACHE}")); + LINFO("New cache: " << cacheFolder); + FileSys.registerPathToken( + "${CACHE}", + cacheFolder, + ghoul::filesystem::FileSystem::Override::Yes + ); + } + + // Initialize the requested logs from the configuration file + _engine->configureLogging(); + + LINFOC("OpenSpace Version", + OPENSPACE_VERSION_MAJOR << "." << + OPENSPACE_VERSION_MINOR << "." << + OPENSPACE_VERSION_PATCH << + " (" << OPENSPACE_VERSION_STRING << ")" + ); + + // Create directories that doesn't exist + auto tokens = FileSys.tokens(); + for (const std::string& token : tokens) { + if (!FileSys.directoryExists(token)) { + std::string p = absPath(token); + LDEBUG("Directory '" << p << "' does not exist, creating."); + FileSys.createDirectory(p, ghoul::filesystem::FileSystem::Recursive::Yes); + } + } + + // Register modules + _engine->_moduleEngine->initialize(); + + // After registering the modules, the documentations for the available classes + // can be added as well + for (OpenSpaceModule* m : _engine->_moduleEngine->modules()) { + for (const documentation::Documentation& doc : m->documentations()) { + DocEng.addDocumentation(doc); + } + } + + // Create the cachemanager + FileSys.createCacheManager( + absPath("${" + ConfigurationManager::KeyCache + "}"), CacheVersion + ); + + // Register the provided shader directories + ghoul::opengl::ShaderPreprocessor::addIncludePath(absPath("${SHADERS}")); + + // Determining SGCT configuration file + LDEBUG("Determining SGCT configuration file"); + std::string sgctConfigurationPath = SgctDefaultConfigFile; + _engine->configurationManager().getValue( + ConfigurationManager::KeyConfigSgct, sgctConfigurationPath); + + if (!commandlineArgumentPlaceholders.sgctConfigurationName.empty()) { + LDEBUG("Overwriting SGCT configuration file with commandline argument: " << + commandlineArgumentPlaceholders.sgctConfigurationName); + sgctConfigurationPath = commandlineArgumentPlaceholders.sgctConfigurationName; + } + + // Prepend the outgoing sgctArguments with the program name + // as well as the configuration file that sgct is supposed to use + sgctArguments.insert(sgctArguments.begin(), argv[0]); + sgctArguments.insert(sgctArguments.begin() + 1, SgctConfigArgumentCommand); + sgctArguments.insert(sgctArguments.begin() + 2, absPath(sgctConfigurationPath)); +} + +void OpenSpaceEngine::destroy() { + if (_engine->parallelConnection().status() != ParallelConnection::Status::Disconnected) { + _engine->parallelConnection().signalDisconnect(); + } + + LTRACE("OpenSpaceEngine::destroy(begin)"); + for (const auto& func : _engine->_moduleCallbacks.deinitializeGL) { + func(); + } + + for (const auto& func : _engine->_moduleCallbacks.deinitialize) { + func(); + } + + _engine->_syncEngine->removeSyncables(_engine->timeManager().getSyncables()); + _engine->_syncEngine->removeSyncables(_engine->_renderEngine->getSyncables()); + _engine->_syncEngine->removeSyncable(_engine->_scriptEngine.get()); + + _engine->_moduleEngine->deinitialize(); + _engine->_console->deinitialize(); + + _engine->_scriptEngine->deinitialize(); + _engine->_sceneManager->unloadAll(); + + delete _engine; + FactoryManager::deinitialize(); + SpiceManager::deinitialize(); + + ghoul::fontrendering::FontRenderer::deinitialize(); + + LogManager::deinitialize(); + + ghoul::deinitialize(); + LTRACE("OpenSpaceEngine::destroy(end)"); +} + +void OpenSpaceEngine::initialize() { + LTRACE("OpenSpaceEngine::initialize(begin)"); + + glbinding::Binding::useCurrentContext(); + glbinding::Binding::initialize(); + + // clear the screen so the user don't have to see old buffer contents from the + // graphics card + LDEBUG("Clearing all Windows"); + _windowWrapper->clearAllWindows(glm::vec4(0.f, 0.f, 0.f, 1.f)); + + LDEBUG("Adding system components"); + // Detect and log OpenCL and OpenGL versions and available devices + SysCap.addComponent( + std::make_unique() + ); + SysCap.addComponent( + std::make_unique() + ); + + // @BUG: This will call OpenGL functions, should it should be in the initializeGL + LDEBUG("Detecting capabilities"); + SysCap.detectCapabilities(); + + using Verbosity = ghoul::systemcapabilities::SystemCapabilitiesComponent::Verbosity; + Verbosity verbosity = Verbosity::Default; + if (configurationManager().hasKey(ConfigurationManager::KeyCapabilitiesVerbosity)) { + static const std::map VerbosityMap = { + { "None", Verbosity::None }, + { "Minimal", Verbosity::Minimal }, + { "Default", Verbosity::Default }, + { "Full", Verbosity::Full } + }; + + std::string v = configurationManager().value( + ConfigurationManager::KeyCapabilitiesVerbosity + ); + ghoul_assert( + VerbosityMap.find(v) != VerbosityMap.end(), + "Missing check for syscaps verbosity in openspace.cfg documentation" + ); + verbosity = VerbosityMap.find(v)->second; + } + SysCap.logCapabilities(verbosity); + + // Check the required OpenGL versions of the registered modules + ghoul::systemcapabilities::Version version = + _engine->_moduleEngine->requiredOpenGLVersion(); + LINFO("Required OpenGL version: " << std::to_string(version)); + + if (OpenGLCap.openGLVersion() < version) { + throw ghoul::RuntimeError( + "Module required higher OpenGL version than is supported", + "OpenSpaceEngine" + ); + } + + if (configurationManager().hasKey(ConfigurationManager::KeyDownloadRequestURL)) { + const std::string requestUrl = configurationManager().value( + ConfigurationManager::KeyDownloadRequestURL + ); + + _downloadManager = std::make_unique( + requestUrl, + DownloadVersion + ); + } + + // Register Lua script functions + LDEBUG("Registering Lua libraries"); + registerCoreClasses(*_scriptEngine); + + for (OpenSpaceModule* module : _moduleEngine->modules()) { + _scriptEngine->addLibrary(module->luaLibrary()); + } + + // TODO: Maybe move all scenegraph and renderengine stuff to initializeGL + scriptEngine().initialize(); + + writeDocumentation(); + + if (configurationManager().hasKey(ConfigurationManager::KeyShutdownCountdown)) { + _shutdown.waitTime = static_cast(configurationManager().value( + ConfigurationManager::KeyShutdownCountdown + )); + } + + if (!commandlineArgumentPlaceholders.sceneName.empty()) { + configurationManager().setValue( + ConfigurationManager::KeyConfigScene, + commandlineArgumentPlaceholders.sceneName + ); + } + + // Initialize the SettingsEngine + _settingsEngine->initialize(); + _settingsEngine->setModules(_moduleEngine->modules()); + + // Initialize the InteractionHandler + _interactionHandler->initialize(); + + // Load a light and a monospaced font + loadFonts(); + + std::string scenePath = ""; + configurationManager().getValue(ConfigurationManager::KeyConfigScene, scenePath); + + _renderEngine->initialize(); + + for (const auto& func : _moduleCallbacks.initialize) { + func(); + } + + scheduleLoadScene(scenePath); + + LTRACE("OpenSpaceEngine::initialize(end)"); + +} + +void OpenSpaceEngine::scheduleLoadScene(std::string scenePath) { + _scheduledSceneSwitch = true; + _scenePath = std::move(scenePath); +} + +void OpenSpaceEngine::loadScene(const std::string& scenePath) { + LTRACE("OpenSpaceEngine::loadScene(begin)"); + + windowWrapper().setBarrier(false); + windowWrapper().setSynchronization(false); + OnExit( + [this]() { + windowWrapper().setSynchronization(true); + windowWrapper().setBarrier(true); + } + ); + + // Run start up scripts + try { + runPreInitializationScripts(scenePath); + } + catch (const ghoul::RuntimeError& e) { + LERRORC(e.component, e.message); + } + + Scene* scene; + try { + scene = _sceneManager->loadScene(scenePath); + } catch (const ghoul::FileNotFoundError& e) { + LERRORC(e.component, e.message); + return; + } catch (const Scene::InvalidSceneError& e) { + LERRORC(e.component, e.message); + return; + } catch (const ghoul::RuntimeError& e) { + LERRORC(e.component, e.message); + return; + } + catch (const std::exception& e) { + LERROR(e.what()); + return; + } + catch (...) { + LERROR("Unknown error loading the scene"); + return; + } + + Scene* previousScene = _renderEngine->scene(); + if (previousScene) { + _syncEngine->removeSyncables(_timeManager->getSyncables()); + _syncEngine->removeSyncables(_renderEngine->getSyncables()); + _syncEngine->removeSyncable(_scriptEngine.get()); + + _renderEngine->setScene(nullptr); + _renderEngine->setCamera(nullptr); + _sceneManager->unloadScene(*previousScene); + } + + // Initialize the RenderEngine + _renderEngine->setScene(scene); + _renderEngine->setCamera(scene->camera()); + _renderEngine->setGlobalBlackOutFactor(0.0); + _renderEngine->startFading(1, 3.0); + + scene->initialize(); + _interactionHandler->setCamera(scene->camera()); + + try { + runPostInitializationScripts(scenePath); + } + catch (const ghoul::RuntimeError& e) { + LFATALC(e.component, e.message); + } + + // Write keyboard documentation. + if (configurationManager().hasKey(ConfigurationManager::KeyKeyboardShortcuts)) { + interactionHandler().writeDocumentation( + absPath(configurationManager().value( + ConfigurationManager::KeyKeyboardShortcuts + )) + ); + } + + // If a PropertyDocumentationFile was specified, generate it now. + if (configurationManager().hasKey(ConfigurationManager::KeyPropertyDocumentation)) { + scene->writeDocumentation( + absPath(configurationManager().value( + ConfigurationManager::KeyPropertyDocumentation + )) + ); + } + + _syncEngine->addSyncables(_timeManager->getSyncables()); + _syncEngine->addSyncables(_renderEngine->getSyncables()); + _syncEngine->addSyncable(_scriptEngine.get()); + + LTRACE("OpenSpaceEngine::loadScene(end)"); +} + +void OpenSpaceEngine::deinitialize() { + LTRACE("OpenSpaceEngine::deinitialize(begin)"); + + _interactionHandler->deinitialize(); + _renderEngine->deinitialize(); + + LTRACE("OpenSpaceEngine::deinitialize(end)"); +} + +void OpenSpaceEngine::writeDocumentation() { + // If a LuaDocumentationFile was specified, generate it now + if (configurationManager().hasKey(ConfigurationManager::KeyLuaDocumentation)) { + _scriptEngine->writeDocumentation( + absPath(configurationManager().value( + ConfigurationManager::KeyLuaDocumentation + )) + ); + } + + // If a general documentation was specified, generate it now + if (configurationManager().hasKey(ConfigurationManager::KeyDocumentation)) { + DocEng.writeDocumentation( + absPath(configurationManager().value( + ConfigurationManager::KeyDocumentation + )) + ); + } + + // If a factory documentation was specified, generate it now + if (configurationManager().hasKey(ConfigurationManager::KeyFactoryDocumentation)) { + FactoryManager::ref().writeDocumentation( + absPath(configurationManager().value( + ConfigurationManager::KeyFactoryDocumentation + )) + ); + } +} + +void OpenSpaceEngine::gatherCommandlineArguments() { + commandlineArgumentPlaceholders.configurationName = ""; + _commandlineParser->addCommand(std::make_unique>( + &commandlineArgumentPlaceholders.configurationName, "-config", "-c", + "Provides the path to the OpenSpace configuration file" + )); + + commandlineArgumentPlaceholders.sgctConfigurationName = ""; + _commandlineParser->addCommand(std::make_unique>( + &commandlineArgumentPlaceholders.sgctConfigurationName, "-sgct", "-s", + "Provides the path to the SGCT configuration file, overriding the value set in " + "the OpenSpace configuration file" + )); + + commandlineArgumentPlaceholders.sceneName = ""; + _commandlineParser->addCommand(std::make_unique>( + &commandlineArgumentPlaceholders.sceneName, "-scene", "", "Provides the path to " + "the scene file, overriding the value set in the OpenSpace configuration file" + )); + + commandlineArgumentPlaceholders.cacheFolder = ""; + _commandlineParser->addCommand(std::make_unique>( + &commandlineArgumentPlaceholders.cacheFolder, "-cacheDir", "", "Provides the " + "path to a cache file, overriding the value set in the OpenSpace configuration " + "file" + )); +} + +void OpenSpaceEngine::runPreInitializationScripts(const std::string& sceneDescription) { + // @CLEANUP: Move this into the scene loading? ---abock + LINFO("Running Initialization scripts"); + + ghoul::lua::LuaState state; + OsEng.scriptEngine().initializeLuaState(state); + + // First execute the script to get all global variables + ghoul::lua::runScriptFile(state, absPath(sceneDescription)); + + // Get the preinitialize function + lua_getglobal(state, PreInitializeFunction); + bool isFunction = lua_isfunction(state, -1); + if (!isFunction) { + LERROR( + "Error executing startup script '" << sceneDescription << "'. Scene '" << + sceneDescription << "' does not have a function '" << + PreInitializeFunction << "'" + ); + return; + } + + // And execute the preinitialize function + int success = lua_pcall(state, 0, 0, 0); + if (success != 0) { + LERROR( + "Error executing '" << PreInitializeFunction << "': " << + lua_tostring(state, -1) + ); + } +} + +void OpenSpaceEngine::runPostInitializationScripts(const std::string& sceneDescription) { + // @CLEANUP: Move this into the scene loading? ---abock + LINFO("Running Setup scripts"); + ghoul::lua::LuaState state; + OsEng.scriptEngine().initializeLuaState(state); + + // First execute the script to get all global variables + ghoul::lua::runScriptFile(state, absPath(sceneDescription)); + + // Get the preinitialize function + lua_getglobal(state, PostInitializationFunction); + bool isFunction = lua_isfunction(state, -1); + if (!isFunction) { + LERROR( + "Error executing startup script '" << sceneDescription << "'. Scene '" << + sceneDescription << "' does not have a function '" << + PostInitializationFunction << "'" + ); + return; + } + + // And execute the preinitialize function + int success = lua_pcall(state, 0, 0, 0); + if (success != 0) { + LERROR( + "Error executing '" << PostInitializationFunction << "': " << + lua_tostring(state, -1) + ); + } +} + +void OpenSpaceEngine::loadFonts() { + ghoul::Dictionary fonts; + configurationManager().getValue(ConfigurationManager::KeyFonts, fonts); + + _fontManager = std::make_unique(FontAtlasSize); + + for (const std::string& key : fonts.keys()) { + std::string font = fonts.value(key); + font = absPath(font); + + if (!FileSys.fileExists(font)) { + LERROR("Could not find font '" << font << "'"); + continue; + } + + LINFO("Registering font '" << font << "' with key '" << key << "'"); + bool success = _fontManager->registerFontPath(key, font); + + if (!success) { + LERROR("Error registering font '" << font << "' with key '" << key << "'"); + } + } + + try { + bool initSuccess = ghoul::fontrendering::FontRenderer::initialize(); + if (!initSuccess) { + LERROR("Error initializing default font renderer"); + } + + ghoul::fontrendering::FontRenderer::defaultRenderer().setFramebufferSize( + _renderEngine->fontResolution() + ); + } + catch (const ghoul::RuntimeError& err) { + LERRORC(err.component, err.message); + } +} + +void OpenSpaceEngine::configureLogging() { + const std::string KeyLogLevel = + ConfigurationManager::KeyLogging + '.' + ConfigurationManager::PartLogLevel; + const std::string KeyLogImmediateFlush = + ConfigurationManager::KeyLogging + '.' + ConfigurationManager::PartImmediateFlush; + const std::string KeyLogs = + ConfigurationManager::KeyLogging + '.' + ConfigurationManager::PartLogs; + + if (configurationManager().hasKeyAndValue(KeyLogLevel)) { + std::string logLevel = "Info"; + configurationManager().getValue(KeyLogLevel, logLevel); + + bool immediateFlush = false; + configurationManager().getValue(KeyLogImmediateFlush, immediateFlush); + + LogLevel level = ghoul::logging::levelFromString(logLevel); + LogManager::deinitialize(); + using ImmediateFlush = ghoul::logging::LogManager::ImmediateFlush; + LogManager::initialize( + level, + immediateFlush ? ImmediateFlush::Yes : ImmediateFlush::No + ); + + LogMgr.addLog(std::make_unique()); + } + + if (configurationManager().hasKeyAndValue(KeyLogs)) { + ghoul::Dictionary logs = configurationManager().value(KeyLogs); + + for (size_t i = 1; i <= logs.size(); ++i) { + ghoul::Dictionary logInfo = logs.value(std::to_string(i)); + + try { + LogMgr.addLog(createLog(logInfo)); + } + catch (const ghoul::RuntimeError& e) { + LERRORC(e.component, e.message); + } + } + } + +#ifdef WIN32 + if (IsDebuggerPresent()) { + LogMgr.addLog(std::make_unique()); + } +#endif // WIN32 + +#ifndef GHOUL_LOGGING_ENABLE_TRACE + std::string logLevel = "Info"; + configurationManager().getValue(KeyLogLevel, logLevel); + LogLevel level = ghoul::logging::levelFromString(logLevel); + + if (level == ghoul::logging::LogLevel::Trace) { + LWARNING( + "Desired logging level is set to 'Trace' but application was " << + "compiled without Trace support" + ); + } +#endif // GHOUL_LOGGING_ENABLE_TRACE +} + +void OpenSpaceEngine::initializeGL() { + LTRACE("OpenSpaceEngine::initializeGL(begin)"); + + LTRACE("OpenSpaceEngine::initializeGL::Console::initialize(begin)"); + try { + _engine->_console->initialize(); + } + catch (ghoul::RuntimeError& e) { + LERROR("Error initializing Console with error:"); + LERRORC(e.component, e.message); + } + LTRACE("OpenSpaceEngine::initializeGL::Console::initialize(end)"); + + const std::string key = ConfigurationManager::KeyOpenGLDebugContext; + if (_configurationManager->hasKey(key)) { + LTRACE("OpenSpaceEngine::initializeGL::DebugContext(begin)"); + ghoul::Dictionary dict = _configurationManager->value(key); + bool debug = dict.value(ConfigurationManager::PartActivate); + + // Debug output is not available before 4.3 + const ghoul::systemcapabilities::Version minVersion = { 4, 3, 0 }; + if (OpenGLCap.openGLVersion() < minVersion) { + LINFO("OpenGL Debug context requested, but insufficient version available"); + debug = false; + } + + if (debug) { + using namespace ghoul::opengl::debug; + + bool synchronous = true; + if (dict.hasKey(ConfigurationManager::PartSynchronous)) { + synchronous = dict.value(ConfigurationManager::PartSynchronous); + } + + setDebugOutput(DebugOutput(debug), SynchronousOutput(synchronous)); + + + if (dict.hasKey(ConfigurationManager::PartFilterIdentifier)) { + ghoul::Dictionary filterDict = dict.value( + ConfigurationManager::PartFilterIdentifier + ); + + for (size_t i = 1; i <= filterDict.size(); ++i) { + ghoul::Dictionary id = filterDict.value( + std::to_string(i) + ); + + const unsigned int identifier = static_cast( + id.value( + ConfigurationManager::PartFilterIdentifierIdentifier + ) + ); + + const std::string s = id.value( + ConfigurationManager::PartFilterIdentifierSource + ); + + const std::string t = id.value( + ConfigurationManager::PartFilterIdentifierType + ); + + setDebugMessageControl( + ghoul::from_string(s), + ghoul::from_string(t), + { identifier }, + Enabled::No + ); + } + } + + if (dict.hasKey(ConfigurationManager::PartFilterSeverity)) { + ghoul::Dictionary filterDict = dict.value( + ConfigurationManager::PartFilterIdentifier + ); + + for (size_t i = 1; i <= filterDict.size(); ++i) { + std::string severity = filterDict.value( + std::to_string(i) + ); + + setDebugMessageControl( + Source::DontCare, + Type::DontCare, + ghoul::from_string(severity), + Enabled::No + ); + } + } + + auto callback = [](Source source, Type type, Severity severity, + unsigned int id, std::string message) -> void + { + const std::string s = std::to_string(source); + const std::string t = std::to_string(type); + + const std::string category = + "OpenGL (" + s + ") [" + t + "] {" + std::to_string(id) + "}"; + switch (severity) { + case Severity::High: + LERRORC(category, std::string(message)); + break; + case Severity::Medium: + LWARNINGC(category, std::string(message)); + break; + case Severity::Low: + LINFOC(category, std::string(message)); + break; + case Severity::Notification: + LDEBUGC(category, std::string(message)); + break; + default: + throw ghoul::MissingCaseException(); + } + }; + ghoul::opengl::debug::setDebugCallback(callback); + } + LTRACE("OpenSpaceEngine::initializeGL::DebugContext(end)"); + } + + + LINFO("Initializing Rendering Engine"); + _renderEngine->initializeGL(); + + for (const auto& func : _moduleCallbacks.initializeGL) { + func(); + } + + LINFO("Finished initializing OpenGL"); + + LINFO("IsUsingSwapGroups: " << _windowWrapper->isUsingSwapGroups()); + LINFO("IsSwapGroupMaster: " << _windowWrapper->isSwapGroupMaster()); + + LTRACE("OpenSpaceEngine::initializeGL(end)"); +} + +double OpenSpaceEngine::runTime() { + return _runTime; +} + +void OpenSpaceEngine::setRunTime(double d) { + _runTime = d; +} + +void OpenSpaceEngine::preSynchronization() { + LTRACE("OpenSpaceEngine::preSynchronization(begin)"); + FileSys.triggerFilesystemEvents(); + + if (_scheduledSceneSwitch) { + loadScene(_scenePath); + _scheduledSceneSwitch = false; + } + + if (_isFirstRenderingFirstFrame) { + _windowWrapper->setSynchronization(false); + } + + bool master = _windowWrapper->isMaster(); + + _syncEngine->preSynchronization(SyncEngine::IsMaster(master)); + if (master) { + double dt = _windowWrapper->averageDeltaTime(); + _timeManager->preSynchronization(dt); + + using Iter = std::vector::const_iterator; + std::pair scheduledScripts = _scriptScheduler->progressTo( + timeManager().time().j2000Seconds() + ); + for (Iter it = scheduledScripts.first; it != scheduledScripts.second; ++it) { + _scriptEngine->queueScript( + *it, ScriptEngine::RemoteScripting::Yes + ); + } + + _interactionHandler->updateInputStates(dt); + + _renderEngine->updateScene(); + _interactionHandler->updateCamera(dt); + _renderEngine->camera()->invalidateCache(); + + _parallelConnection->preSynchronization(); + } + + for (const auto& func : _moduleCallbacks.preSync) { + func(); + } + LTRACE("OpenSpaceEngine::preSynchronization(end)"); +} + +void OpenSpaceEngine::postSynchronizationPreDraw() { + LTRACE("OpenSpaceEngine::postSynchronizationPreDraw(begin)"); + + bool master = _windowWrapper->isMaster(); + _syncEngine->postSynchronization(SyncEngine::IsMaster(master)); + + if (_shutdown.inShutdown) { + if (_shutdown.timer <= 0.f) { + _windowWrapper->terminate(); + } + _shutdown.timer -= static_cast(_windowWrapper->averageDeltaTime()); + } + + _renderEngine->updateScene(); + _renderEngine->updateFade(); + _renderEngine->updateRenderer(); + _renderEngine->updateScreenSpaceRenderables(); + _renderEngine->updateShaderPrograms(); + + if (!master) { + _renderEngine->camera()->invalidateCache(); + } + + // Step the camera using the current mouse velocities which are synced + //_interactionHandler->updateCamera(); + + for (const auto& func : _moduleCallbacks.postSyncPreDraw) { + func(); + } + + // Testing this every frame has minimal impact on the performance --- abock + // Debug build: 1-2 us ; Release build: <= 1 us + using ghoul::logging::LogManager; + int warningCounter = LogMgr.messageCounter(LogLevel::Warning); + int errorCounter = LogMgr.messageCounter(LogLevel::Error); + int fatalCounter = LogMgr.messageCounter(LogLevel::Fatal); + + if (warningCounter > 0) { + LWARNINGC("Logging", "Number of Warnings raised: " << warningCounter); + } + if (errorCounter > 0) { + LWARNINGC("Logging", "Number of Errors raised: " << errorCounter); + } + if (fatalCounter > 0) { + LWARNINGC("Logging", "Number of Fatals raised: " << fatalCounter); + } + + LogMgr.resetMessageCounters(); + + LTRACE("OpenSpaceEngine::postSynchronizationPreDraw(end)"); +} + +void OpenSpaceEngine::render(const glm::mat4& sceneMatrix, + const glm::mat4& viewMatrix, + const glm::mat4& projectionMatrix) +{ + bool isGuiWindow = _windowWrapper->hasGuiWindow() ? _windowWrapper->isGuiWindow() : true; + bool showOverlay = isGuiWindow && _windowWrapper->isMaster() && _windowWrapper->isRegularRendering(); + // @CLEANUP: Replace the two windows by a single call to whether a gui should be + // rendered ---abock + + if (showOverlay) { + _console->update(); + } + + LTRACE("OpenSpaceEngine::render(begin)"); + _renderEngine->render(sceneMatrix, viewMatrix, projectionMatrix); + + for (const auto& func : _moduleCallbacks.render) { + func(); + } + + if (showOverlay) { + _renderEngine->renderScreenLog(); + _console->render(); + } + + if (_shutdown.inShutdown) { + _renderEngine->renderShutdownInformation(_shutdown.timer, _shutdown.waitTime); + } + + LTRACE("OpenSpaceEngine::render(end)"); +} + +void OpenSpaceEngine::postDraw() { + LTRACE("OpenSpaceEngine::postDraw(begin)"); + + _renderEngine->postDraw(); + + for (const auto& func : _moduleCallbacks.postDraw) { + func(); + } + + if (_isFirstRenderingFirstFrame) { + _windowWrapper->setSynchronization(true); + _isFirstRenderingFirstFrame = false; + } + + LTRACE("OpenSpaceEngine::postDraw(end)"); +} + +void OpenSpaceEngine::keyboardCallback(Key key, KeyModifier mod, KeyAction action) { + for (const auto& func : _moduleCallbacks.keyboard) { + const bool consumed = func(key, mod, action); + if (consumed) { + return; + } + } + + const bool consoleConsumed = _console->keyboardCallback(key, mod, action); + if (consoleConsumed) { + return; + } + + _interactionHandler->keyboardCallback(key, mod, action); +} + +void OpenSpaceEngine::charCallback(unsigned int codepoint, KeyModifier modifier) { + for (const auto& func : _moduleCallbacks.character) { + bool consumed = func(codepoint, modifier); + if (consumed) { + return; + } + } + + _console->charCallback(codepoint, modifier); +} + +void OpenSpaceEngine::mouseButtonCallback(MouseButton button, MouseAction action) { + for (const auto& func : _moduleCallbacks.mouseButton) { + bool consumed = func(button, action); + if (consumed) { + return; + } + } + + _interactionHandler->mouseButtonCallback(button, action); +} + +void OpenSpaceEngine::mousePositionCallback(double x, double y) { + for (const auto& func : _moduleCallbacks.mousePosition) { + func(x, y); + } + + _interactionHandler->mousePositionCallback(x, y); +} + +void OpenSpaceEngine::mouseScrollWheelCallback(double pos) { + for (const auto& func : _moduleCallbacks.mouseScrollWheel) { + bool consumed = func(pos); + if (consumed) { + return; + } + } + + _interactionHandler->mouseScrollWheelCallback(pos); +} + +void OpenSpaceEngine::encode() { + _syncEngine->encodeSyncables(); + + _networkEngine->publishStatusMessage(); + _networkEngine->sendMessages(); +} + +void OpenSpaceEngine::decode() { + _syncEngine->decodeSyncables(); +} + +void OpenSpaceEngine::externalControlCallback(const char* receivedChars, int size, + int clientId) +{ + if (size == 0) { + return; + } + + _networkEngine->handleMessage(std::string(receivedChars, size)); +} + +void OpenSpaceEngine::toggleShutdownMode() { + if (_shutdown.inShutdown) { + // If we are already in shutdown mode, we want to disable it + LINFO("Disabled shutdown mode"); + _shutdown.inShutdown = false; + } + else { + // Else, we have to enable it + LINFO("Shutting down OpenSpace"); + _shutdown.timer = _shutdown.waitTime; + _shutdown.inShutdown = true; + } +} + +scripting::LuaLibrary OpenSpaceEngine::luaLibrary() { + return { + "", + { + { + "toggleShutdown", + &luascriptfunctions::toggleShutdown, + "", + "Toggles the shutdown mode that will close the application after the count" + "down timer is reached" + }, + { + "writeDocumentation", + &luascriptfunctions::writeDocumentation, + "", + "Writes out documentation files" + }, + { + "downloadFile", + &luascriptfunctions::downloadFile, + "", + "Downloads a file from Lua scope" + }, + { + "addVirtualProperty", + &luascriptfunctions::addVirtualProperty, + "type, name, identifier, [value, minimumValue, maximumValue]", + "Adds a virtual property that will set a group of properties" + }, + { + "removeVirtualProperty", + &luascriptfunctions::removeVirtualProperty, + "string", + "Removes a previously added virtual property" + }, + { + "removeAllVirtualProperties", + &luascriptfunctions::removeAllVirtualProperties, + "", + "Remove all registered virtual properties" + } + } + }; +} + +void OpenSpaceEngine::enableBarrier() { + _windowWrapper->setBarrier(true); +} + +void OpenSpaceEngine::disableBarrier() { + _windowWrapper->setBarrier(false); +} + +// Registers a callback for a specific CallbackOption +void OpenSpaceEngine::registerModuleCallback(OpenSpaceEngine::CallbackOption option, + std::function function) +{ + switch (option) { + case CallbackOption::Initialize: + _moduleCallbacks.initialize.push_back(std::move(function)); + break; + case CallbackOption::Deinitialize: + _moduleCallbacks.deinitialize.push_back(std::move(function)); + break; + case CallbackOption::InitializeGL: + _moduleCallbacks.initializeGL.push_back(std::move(function)); + break; + case CallbackOption::DeinitializeGL: + _moduleCallbacks.deinitializeGL.push_back(std::move(function)); + break; + case CallbackOption::PreSync: + _moduleCallbacks.preSync.push_back(std::move(function)); + break; + case CallbackOption::PostSyncPreDraw: + _moduleCallbacks.postSyncPreDraw.push_back(std::move(function)); + break; + case CallbackOption::Render: + _moduleCallbacks.render.push_back(std::move(function)); + break; + case CallbackOption::PostDraw: + _moduleCallbacks.postDraw.push_back(std::move(function)); + break; + default: + throw ghoul::MissingCaseException(); + } +} + +void OpenSpaceEngine::registerModuleKeyboardCallback( + std::function function) +{ + _moduleCallbacks.keyboard.push_back(std::move(function)); +} + +void OpenSpaceEngine::registerModuleCharCallback( + std::function function) +{ + _moduleCallbacks.character.push_back(std::move(function)); +} + +void OpenSpaceEngine::registerModuleMouseButtonCallback( + std::function function) +{ + _moduleCallbacks.mouseButton.push_back(std::move(function)); +} + +void OpenSpaceEngine::registerModuleMousePositionCallback( + std::function function) +{ + _moduleCallbacks.mousePosition.push_back(std::move(function)); +} + +void OpenSpaceEngine::registerModuleMouseScrollWheelCallback( + std::function function) +{ + _moduleCallbacks.mouseScrollWheel.push_back(std::move(function)); +} + +ConfigurationManager& OpenSpaceEngine::configurationManager() { + ghoul_assert(_configurationManager, "ConfigurationManager must not be nullptr"); + return *_configurationManager; +} + +LuaConsole& OpenSpaceEngine::console() { + ghoul_assert(_console, "LuaConsole must not be nullptr"); + return *_console; +} + +DownloadManager& OpenSpaceEngine::downloadManager() { + ghoul_assert(_downloadManager, "Download Manager must not be nullptr"); + return *_downloadManager; +} + +NetworkEngine& OpenSpaceEngine::networkEngine() { + ghoul_assert(_networkEngine, "NetworkEngine must not be nullptr"); + return *_networkEngine; +} + +ModuleEngine& OpenSpaceEngine::moduleEngine() { + ghoul_assert(_moduleEngine, "ModuleEngine must not be nullptr"); + return *_moduleEngine; +} + +ParallelConnection& OpenSpaceEngine::parallelConnection() { + ghoul_assert(_parallelConnection, "ParallelConnection must not be nullptr"); + return *_parallelConnection; +} + +RenderEngine& OpenSpaceEngine::renderEngine() { + ghoul_assert(_renderEngine, "RenderEngine must not be nullptr"); + return *_renderEngine; +} + +SettingsEngine& OpenSpaceEngine::settingsEngine() { + ghoul_assert(_settingsEngine, "Settings Engine must not be nullptr"); + return *_settingsEngine; +} + +TimeManager& OpenSpaceEngine::timeManager() { + ghoul_assert(_timeManager, "Download Manager must not be nullptr"); + return *_timeManager; +} + +WindowWrapper& OpenSpaceEngine::windowWrapper() { + ghoul_assert(_windowWrapper, "Window Wrapper must not be nullptr"); + return *_windowWrapper; +} + +ghoul::fontrendering::FontManager& OpenSpaceEngine::fontManager() { + ghoul_assert(_fontManager, "Font Manager must not be nullptr"); + return *_fontManager; +} + +interaction::InteractionHandler& OpenSpaceEngine::interactionHandler() { + ghoul_assert(_interactionHandler, "InteractionHandler must not be nullptr"); + return *_interactionHandler; +} + +properties::PropertyOwner& OpenSpaceEngine::globalPropertyOwner() { + ghoul_assert( + _globalPropertyNamespace, + "Global Property Namespace must not be nullptr" + ); + return *_globalPropertyNamespace; +} + +VirtualPropertyManager& OpenSpaceEngine::virtualPropertyManager() { + ghoul_assert( + _virtualPropertyManager, + "Virtual Property Manager must not be nullptr" + ); + + return *_virtualPropertyManager; +} + +ScriptEngine& OpenSpaceEngine::scriptEngine() { + ghoul_assert(_scriptEngine, "ScriptEngine must not be nullptr"); + return *_scriptEngine; +} + +ScriptScheduler& OpenSpaceEngine::scriptScheduler() { + ghoul_assert(_scriptScheduler, "ScriptScheduler must not be nullptr"); + return *_scriptScheduler; +} + +} // namespace openspace diff --git a/src/engine/wrapper/sgctwindowwrapper.cpp b/src/engine/wrapper/sgctwindowwrapper.cpp index c772b7863f..ef8f297808 100644 --- a/src/engine/wrapper/sgctwindowwrapper.cpp +++ b/src/engine/wrapper/sgctwindowwrapper.cpp @@ -1,248 +1,248 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2017 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#include - -#include "sgct.h" - -#undef near -#undef far - -namespace { - const char* GuiWindowTag = "GUI"; -} // namespace - -namespace openspace { - -SGCTWindowWrapper::SGCTWindowWrapper() - : _eyeSeparation("eyeSeparation", "Eye Separation", 0.f, 0.f, 10.f) - , _showStatsGraph("showStatsGraph", "Show Stats Graph", false) -{ - _showStatsGraph.onChange([this](){ - sgct::Engine::instance()->setStatsGraphVisibility(_showStatsGraph); - }); - addProperty(_showStatsGraph); - - addProperty(_eyeSeparation); - _eyeSeparation.onChange([this](){ - setEyeSeparationDistance(_eyeSeparation); - }); -} - -void SGCTWindowWrapper::terminate() { - sgct::Engine::instance()->terminate(); -} - -void SGCTWindowWrapper::setBarrier(bool enabled) { - sgct::SGCTWindow::setBarrier(enabled); -} - -void SGCTWindowWrapper::setSynchronization(bool enabled) { - sgct_core::ClusterManager::instance()->setUseIgnoreSync(enabled); -} - -void SGCTWindowWrapper::clearAllWindows(const glm::vec4& clearColor) { - size_t n = sgct::Engine::instance()->getNumberOfWindows(); - for (size_t i = 0; i < n; ++i) { - glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - GLFWwindow* win = sgct::Engine::instance()->getWindowPtr(i)->getWindowHandle(); - glfwSwapBuffers(win); - } -} - -bool SGCTWindowWrapper::windowHasResized() const { - return sgct::Engine::instance()->getCurrentWindowPtr()->isWindowResized(); -} - -double SGCTWindowWrapper::averageDeltaTime() const { - return sgct::Engine::instance()->getAvgDt(); -} - -double SGCTWindowWrapper::deltaTime() const { - return sgct::Engine::instance()->getDt(); -} - -glm::vec2 SGCTWindowWrapper::mousePosition() const { - int id = sgct::Engine::instance()->getCurrentWindowPtr()->getId(); - double posX, posY; - sgct::Engine::instance()->getMousePos(id, &posX, &posY); - return glm::vec2(posX, posY); -} - -uint32_t SGCTWindowWrapper::mouseButtons(int maxNumber) const { - int id = sgct::Engine::instance()->getCurrentWindowPtr()->getId(); - uint32_t result = 0; - for (int i = 0; i < maxNumber; ++i) { - bool button = (sgct::Engine::instance()->getMouseButton(id, i) != 0); - if (button) - result |= (1 << i); - - } - return result; -} - -glm::ivec2 SGCTWindowWrapper::currentWindowSize() const { - auto window = sgct::Engine::instance()->getCurrentWindowPtr(); - switch (window->getStereoMode()) { - case sgct::SGCTWindow::Side_By_Side_Stereo: - case sgct::SGCTWindow::Side_By_Side_Inverted_Stereo: - return glm::ivec2( - window->getXResolution() / 2, - window->getYResolution()); - case sgct::SGCTWindow::Top_Bottom_Stereo: - case sgct::SGCTWindow::Top_Bottom_Inverted_Stereo: - return glm::ivec2( - window->getXResolution(), - window->getYResolution() / 2); - default: - return glm::ivec2( - window->getXResolution(), - window->getYResolution()); - } -} - -glm::ivec2 SGCTWindowWrapper::currentWindowResolution() const { - int x, y; - auto window = sgct::Engine::instance()->getCurrentWindowPtr(); - window->getFinalFBODimensions(x, y); - return glm::ivec2(x, y); -} - -glm::ivec2 SGCTWindowWrapper::currentDrawBufferResolution() const { - sgct_core::Viewport* viewport = sgct::Engine::instance()->getCurrentWindowPtr()->getViewport(0); - if (viewport != nullptr){ - if (viewport->hasSubViewports() && viewport->getNonLinearProjectionPtr()) { - int res = viewport->getNonLinearProjectionPtr()->getCubemapResolution(); - return glm::ivec2(res, res); - } else { - return currentWindowResolution(); - } - } - throw WindowWrapperException("No viewport available"); -} - -glm::vec2 SGCTWindowWrapper::dpiScaling() const { - return glm::vec2( - sgct::Engine::instance()->getCurrentWindowPtr()->getXScale(), - sgct::Engine::instance()->getCurrentWindowPtr()->getYScale() - ); -} - -int SGCTWindowWrapper::currentNumberOfAaSamples() const { - return sgct::Engine::instance()->getCurrentWindowPtr()->getNumberOfAASamples(); -} - -bool SGCTWindowWrapper::isRegularRendering() const { - sgct::SGCTWindow* w = sgct::Engine::instance()->getCurrentWindowPtr(); - std::size_t nViewports = w->getNumberOfViewports(); - (void)nViewports; // Unused in Release mode - ghoul_assert(nViewports > 0, "At least one viewport must exist at this time"); - sgct_core::Viewport* vp = w->getViewport(0); - sgct_core::NonLinearProjection* nlp = vp->getNonLinearProjectionPtr(); - return nlp == nullptr; -} - -bool SGCTWindowWrapper::hasGuiWindow() const { - auto engine = sgct::Engine::instance(); - for (size_t i = 0; i < engine->getNumberOfWindows(); ++i) { - if (engine->getWindowPtr(i)->checkIfTagExists(GuiWindowTag)) { - return true; - } - } - return false; -} - -bool SGCTWindowWrapper::isGuiWindow() const { - return sgct::Engine::instance()->getCurrentWindowPtr()->checkIfTagExists( - GuiWindowTag - ); -} - -bool SGCTWindowWrapper::isMaster() const { - return sgct::Engine::instance()->isMaster(); -} - -bool SGCTWindowWrapper::isSwapGroupMaster() const { - return sgct::Engine::instance()->getCurrentWindowPtr()->isSwapGroupMaster(); -} - -bool SGCTWindowWrapper::isUsingSwapGroups() const { - return sgct::Engine::instance()->getCurrentWindowPtr()->isUsingSwapGroups(); -} - -glm::mat4 SGCTWindowWrapper::viewProjectionMatrix() const { - return sgct::Engine::instance()->getCurrentModelViewProjectionMatrix(); -} - -glm::mat4 SGCTWindowWrapper::modelMatrix() const { - return sgct::Engine::instance()->getModelMatrix(); -} - -void SGCTWindowWrapper::setNearFarClippingPlane(float nearPlane, float farPlane) { - sgct::Engine::instance()->setNearAndFarClippingPlanes(nearPlane, farPlane); -} - -void SGCTWindowWrapper::setEyeSeparationDistance(float distance) { - sgct::Engine::instance()->setEyeSeparation(distance); -} - -glm::ivec4 SGCTWindowWrapper::viewportPixelCoordinates() const { - int x1, xSize, y1, ySize; - sgct::Engine::instance()->getCurrentWindowPtr()->getCurrentViewportPixelCoords(x1, - y1, - xSize, - ySize); - return glm::ivec4(x1, xSize, y1, ySize); -} - -bool SGCTWindowWrapper::isExternalControlConnected() const { - return sgct::Engine::instance()->isExternalControlConnected(); -} - -void SGCTWindowWrapper::sendMessageToExternalControl(const std::vector& message) const { - sgct::Engine::instance()->sendMessageToExternalControl( - message.data(), - static_cast(message.size()) - ); -} - -bool SGCTWindowWrapper::isSimpleRendering() const { - return (sgct::Engine::instance()->getCurrentRenderTarget() != sgct::Engine::NonLinearBuffer); - -} - -void SGCTWindowWrapper::takeScreenshot(bool applyWarping) const { - sgct::SGCTSettings::instance()->setCaptureFromBackBuffer(applyWarping); - sgct::Engine::instance()->takeScreenshot(); -} - -//void forEachWindow(std::function function) { -// size_t n = sgct::Engine::instance()->getNumberOfWindows(); -// for (size_t i = 0; i < n; ++i) -// function(); -//} - -} // namespace openspace - +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include "sgct.h" + +#undef near +#undef far + +namespace { + const char* GuiWindowTag = "GUI"; +} // namespace + +namespace openspace { + +SGCTWindowWrapper::SGCTWindowWrapper() + : _eyeSeparation("eyeSeparation", "Eye Separation", 0.f, 0.f, 10.f) + , _showStatsGraph("showStatsGraph", "Show Stats Graph", false) +{ + _showStatsGraph.onChange([this](){ + sgct::Engine::instance()->setStatsGraphVisibility(_showStatsGraph); + }); + addProperty(_showStatsGraph); + + addProperty(_eyeSeparation); + _eyeSeparation.onChange([this](){ + setEyeSeparationDistance(_eyeSeparation); + }); +} + +void SGCTWindowWrapper::terminate() { + sgct::Engine::instance()->terminate(); +} + +void SGCTWindowWrapper::setBarrier(bool enabled) { + sgct::SGCTWindow::setBarrier(enabled); +} + +void SGCTWindowWrapper::setSynchronization(bool enabled) { + sgct_core::ClusterManager::instance()->setUseIgnoreSync(enabled); +} + +void SGCTWindowWrapper::clearAllWindows(const glm::vec4& clearColor) { + size_t n = sgct::Engine::instance()->getNumberOfWindows(); + for (size_t i = 0; i < n; ++i) { + glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + GLFWwindow* win = sgct::Engine::instance()->getWindowPtr(i)->getWindowHandle(); + glfwSwapBuffers(win); + } +} + +bool SGCTWindowWrapper::windowHasResized() const { + return sgct::Engine::instance()->getCurrentWindowPtr()->isWindowResized(); +} + +double SGCTWindowWrapper::averageDeltaTime() const { + return sgct::Engine::instance()->getAvgDt(); +} + +double SGCTWindowWrapper::deltaTime() const { + return sgct::Engine::instance()->getDt(); +} + +glm::vec2 SGCTWindowWrapper::mousePosition() const { + int id = sgct::Engine::instance()->getCurrentWindowPtr()->getId(); + double posX, posY; + sgct::Engine::instance()->getMousePos(id, &posX, &posY); + return glm::vec2(posX, posY); +} + +uint32_t SGCTWindowWrapper::mouseButtons(int maxNumber) const { + int id = sgct::Engine::instance()->getCurrentWindowPtr()->getId(); + uint32_t result = 0; + for (int i = 0; i < maxNumber; ++i) { + bool button = (sgct::Engine::instance()->getMouseButton(id, i) != 0); + if (button) + result |= (1 << i); + + } + return result; +} + +glm::ivec2 SGCTWindowWrapper::currentWindowSize() const { + auto window = sgct::Engine::instance()->getCurrentWindowPtr(); + switch (window->getStereoMode()) { + case sgct::SGCTWindow::Side_By_Side_Stereo: + case sgct::SGCTWindow::Side_By_Side_Inverted_Stereo: + return glm::ivec2( + window->getXResolution() / 2, + window->getYResolution()); + case sgct::SGCTWindow::Top_Bottom_Stereo: + case sgct::SGCTWindow::Top_Bottom_Inverted_Stereo: + return glm::ivec2( + window->getXResolution(), + window->getYResolution() / 2); + default: + return glm::ivec2( + window->getXResolution(), + window->getYResolution()); + } +} + +glm::ivec2 SGCTWindowWrapper::currentWindowResolution() const { + int x, y; + auto window = sgct::Engine::instance()->getCurrentWindowPtr(); + window->getFinalFBODimensions(x, y); + return glm::ivec2(x, y); +} + +glm::ivec2 SGCTWindowWrapper::currentDrawBufferResolution() const { + sgct_core::Viewport* viewport = sgct::Engine::instance()->getCurrentWindowPtr()->getViewport(0); + if (viewport != nullptr){ + if (viewport->hasSubViewports() && viewport->getNonLinearProjectionPtr()) { + int res = viewport->getNonLinearProjectionPtr()->getCubemapResolution(); + return glm::ivec2(res, res); + } else { + return currentWindowResolution(); + } + } + throw WindowWrapperException("No viewport available"); +} + +glm::vec2 SGCTWindowWrapper::dpiScaling() const { + return glm::vec2( + sgct::Engine::instance()->getCurrentWindowPtr()->getXScale(), + sgct::Engine::instance()->getCurrentWindowPtr()->getYScale() + ); +} + +int SGCTWindowWrapper::currentNumberOfAaSamples() const { + return sgct::Engine::instance()->getCurrentWindowPtr()->getNumberOfAASamples(); +} + +bool SGCTWindowWrapper::isRegularRendering() const { + sgct::SGCTWindow* w = sgct::Engine::instance()->getCurrentWindowPtr(); + std::size_t nViewports = w->getNumberOfViewports(); + (void)nViewports; // Unused in Release mode + ghoul_assert(nViewports > 0, "At least one viewport must exist at this time"); + sgct_core::Viewport* vp = w->getViewport(0); + sgct_core::NonLinearProjection* nlp = vp->getNonLinearProjectionPtr(); + return nlp == nullptr; +} + +bool SGCTWindowWrapper::hasGuiWindow() const { + auto engine = sgct::Engine::instance(); + for (size_t i = 0; i < engine->getNumberOfWindows(); ++i) { + if (engine->getWindowPtr(i)->checkIfTagExists(GuiWindowTag)) { + return true; + } + } + return false; +} + +bool SGCTWindowWrapper::isGuiWindow() const { + return sgct::Engine::instance()->getCurrentWindowPtr()->checkIfTagExists( + GuiWindowTag + ); +} + +bool SGCTWindowWrapper::isMaster() const { + return sgct::Engine::instance()->isMaster(); +} + +bool SGCTWindowWrapper::isSwapGroupMaster() const { + return sgct::Engine::instance()->getCurrentWindowPtr()->isSwapGroupMaster(); +} + +bool SGCTWindowWrapper::isUsingSwapGroups() const { + return sgct::Engine::instance()->getCurrentWindowPtr()->isUsingSwapGroups(); +} + +glm::mat4 SGCTWindowWrapper::viewProjectionMatrix() const { + return sgct::Engine::instance()->getCurrentModelViewProjectionMatrix(); +} + +glm::mat4 SGCTWindowWrapper::modelMatrix() const { + return sgct::Engine::instance()->getModelMatrix(); +} + +void SGCTWindowWrapper::setNearFarClippingPlane(float nearPlane, float farPlane) { + sgct::Engine::instance()->setNearAndFarClippingPlanes(nearPlane, farPlane); +} + +void SGCTWindowWrapper::setEyeSeparationDistance(float distance) { + sgct::Engine::instance()->setEyeSeparation(distance); +} + +glm::ivec4 SGCTWindowWrapper::viewportPixelCoordinates() const { + int x1, xSize, y1, ySize; + sgct::Engine::instance()->getCurrentWindowPtr()->getCurrentViewportPixelCoords(x1, + y1, + xSize, + ySize); + return glm::ivec4(x1, xSize, y1, ySize); +} + +bool SGCTWindowWrapper::isExternalControlConnected() const { + return sgct::Engine::instance()->isExternalControlConnected(); +} + +void SGCTWindowWrapper::sendMessageToExternalControl(const std::vector& message) const { + sgct::Engine::instance()->sendMessageToExternalControl( + message.data(), + static_cast(message.size()) + ); +} + +bool SGCTWindowWrapper::isSimpleRendering() const { + return (sgct::Engine::instance()->getCurrentRenderTarget() != sgct::Engine::NonLinearBuffer); + +} + +void SGCTWindowWrapper::takeScreenshot(bool applyWarping) const { + sgct::SGCTSettings::instance()->setCaptureFromBackBuffer(applyWarping); + sgct::Engine::instance()->takeScreenshot(); +} + +//void forEachWindow(std::function function) { +// size_t n = sgct::Engine::instance()->getNumberOfWindows(); +// for (size_t i = 0; i < n; ++i) +// function(); +//} + +} // namespace openspace + diff --git a/src/rendering/framebufferrenderer.cpp b/src/rendering/framebufferrenderer.cpp index 4802b6f892..6acfcb5c32 100644 --- a/src/rendering/framebufferrenderer.cpp +++ b/src/rendering/framebufferrenderer.cpp @@ -1,528 +1,528 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2017 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -namespace { - const char* _loggerCat = "FramebufferRenderer"; - const char* ExitFragmentShaderPath = "${SHADERS}/framebuffer/exitframebuffer.frag"; - const char* RaycastFragmentShaderPath = "${SHADERS}/framebuffer/raycastframebuffer.frag"; - const char* GetEntryInsidePath = "${SHADERS}/framebuffer/inside.glsl"; - const char* GetEntryOutsidePath = "${SHADERS}/framebuffer/outside.glsl"; - const char* RenderFragmentShaderPath = "${SHADERS}/framebuffer/renderframebuffer.frag"; -} // namespace - -namespace openspace { - -FramebufferRenderer::FramebufferRenderer() - : _camera(nullptr) - , _scene(nullptr) - , _resolution(glm::vec2(0)) -{} - -FramebufferRenderer::~FramebufferRenderer() {} - -void FramebufferRenderer::initialize() { - LINFO("Initializing FramebufferRenderer"); - - const GLfloat size = 1.0f; - const GLfloat vertex_data[] = { - // x y s t - -size, -size, 0.0f, 1.0f, - size, size, 0.0f, 1.0f, - -size, size, 0.0f, 1.0f, - -size, -size, 0.0f, 1.0f, - size, -size, 0.0f, 1.0f, - size, size, 0.0f, 1.0f - }; - - glGenVertexArrays(1, &_screenQuad); - glBindVertexArray(_screenQuad); - - glGenBuffers(1, &_vertexPositionBuffer); - glBindBuffer(GL_ARRAY_BUFFER, _vertexPositionBuffer); - - glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW); - glVertexAttribPointer( - 0, - 4, - GL_FLOAT, - GL_FALSE, - sizeof(GLfloat) * 4, - reinterpret_cast(0) - ); - glEnableVertexAttribArray(0); - - GLint defaultFbo; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFbo); - - // Main framebuffer - glGenTextures(1, &_mainColorTexture); - glGenTextures(1, &_mainDepthTexture); - glGenFramebuffers(1, &_mainFramebuffer); - - // Exit framebuffer - glGenTextures(1, &_exitColorTexture); - glGenTextures(1, &_exitDepthTexture); - glGenFramebuffers(1, &_exitFramebuffer); - - updateResolution(); - updateRendererData(); - updateRaycastData(); - - glBindFramebuffer(GL_FRAMEBUFFER, _mainFramebuffer); - glFramebufferTexture2D( - GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D_MULTISAMPLE, - _mainColorTexture, - 0 - ); - glFramebufferTexture2D( - GL_FRAMEBUFFER, - GL_DEPTH_ATTACHMENT, - GL_TEXTURE_2D_MULTISAMPLE, - _mainDepthTexture, - 0 - ); - - glBindFramebuffer(GL_FRAMEBUFFER, _exitFramebuffer); - glFramebufferTexture2D( - GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - _exitColorTexture, - 0 - ); - glFramebufferTexture2D( - GL_FRAMEBUFFER, - GL_DEPTH_ATTACHMENT, - GL_TEXTURE_2D, - _exitDepthTexture, - 0 - ); - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - LERROR("Main framebuffer is not complete"); - } - - glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo); - - try { - _resolveProgram = ghoul::opengl::ProgramObject::Build( - "Framebuffer Resolve", - "${SHADERS}/framebuffer/resolveframebuffer.vert", - "${SHADERS}/framebuffer/resolveframebuffer.frag" - ); - } catch (const ghoul::RuntimeError& e) { - LERRORC(e.component, e.message); - } - - OsEng.renderEngine().raycasterManager().addListener(*this); -} - -void FramebufferRenderer::deinitialize() { - LINFO("Deinitializing FramebufferRenderer"); - - glDeleteFramebuffers(1, &_mainFramebuffer); - glDeleteFramebuffers(1, &_exitFramebuffer); - - glDeleteTextures(1, &_mainColorTexture); - glDeleteTextures(1, &_mainDepthTexture); - glDeleteTextures(1, &_exitColorTexture); - glDeleteTextures(1, &_exitDepthTexture); - - glDeleteBuffers(1, &_vertexPositionBuffer); - glDeleteVertexArrays(1, &_screenQuad); - - OsEng.renderEngine().raycasterManager().removeListener(*this); -} - -void FramebufferRenderer::raycastersChanged(VolumeRaycaster&, bool) { - _dirtyRaycastData = true; -} - -void FramebufferRenderer::update() { - if (_dirtyResolution) { - updateResolution(); - } - - if (_dirtyRaycastData) { - updateRaycastData(); - } - - // If the resolve dictionary changed (or a file changed on disk) - // then rebuild the resolve program. - if (_resolveProgram->isDirty()) { - try { - _resolveProgram->rebuildFromFile(); - } catch (const ghoul::RuntimeError& error) { - LERRORC(error.component, error.message); - } - } - - for (auto& program : _exitPrograms) { - if (program.second->isDirty()) { - try { - program.second->rebuildFromFile(); - } catch (const ghoul::RuntimeError& e) { - LERRORC(e.component, e.message); - } - } - } - - for (auto& program : _raycastPrograms) { - if (program.second->isDirty()) { - try { - program.second->rebuildFromFile(); - } catch (const ghoul::RuntimeError& e) { - LERRORC(e.component, e.message); - } - } - } - - for (auto& program : _insideRaycastPrograms) { - if (program.second->isDirty()) { - try { - program.second->rebuildFromFile(); - } - catch (const ghoul::RuntimeError& e) { - LERRORC(e.component, e.message); - } - } - } -} - -void FramebufferRenderer::updateResolution() { - glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainColorTexture); - - glTexImage2DMultisample( - GL_TEXTURE_2D_MULTISAMPLE, - _nAaSamples, - GL_RGBA, - GLsizei(_resolution.x), - GLsizei(_resolution.y), - true - ); - - glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainDepthTexture); - glTexImage2DMultisample( - GL_TEXTURE_2D_MULTISAMPLE, - _nAaSamples, - GL_DEPTH_COMPONENT32F, - GLsizei(_resolution.x), - GLsizei(_resolution.y), - true - ); - - glBindTexture(GL_TEXTURE_2D, _exitColorTexture); - glTexImage2D( - GL_TEXTURE_2D, - 0, - GL_RGBA16, - GLsizei(_resolution.x), - GLsizei(_resolution.y), - 0, - GL_RGBA, - GL_UNSIGNED_SHORT, - nullptr - ); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - glBindTexture(GL_TEXTURE_2D, _exitDepthTexture); - - glTexImage2D( - GL_TEXTURE_2D, - 0, - GL_DEPTH_COMPONENT32F, - GLsizei(_resolution.x), - GLsizei(_resolution.y), - 0, - GL_DEPTH_COMPONENT, - GL_FLOAT, - nullptr - ); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - _dirtyResolution = false; -} - -void FramebufferRenderer::updateRaycastData() { - _raycastData.clear(); - _exitPrograms.clear(); - _raycastPrograms.clear(); - _insideRaycastPrograms.clear(); - - const std::vector& raycasters = - OsEng.renderEngine().raycasterManager().raycasters(); - int nextId = 0; - for (auto& raycaster : raycasters) { - RaycastData data; - data.id = nextId++; - data.namespaceName = "HELPER"; - - std::string vsPath = raycaster->getBoundsVsPath(); - std::string fsPath = raycaster->getBoundsFsPath(); - - ghoul::Dictionary dict; - dict.setValue("rendererData", _rendererData); - dict.setValue("fragmentPath", fsPath); - dict.setValue("id", data.id); - std::string helperPath = raycaster->getHelperPath(); - ghoul::Dictionary helpersDict; - if (!helperPath.empty()) { - helpersDict.setValue("0", helperPath); - } - dict.setValue("helperPaths", helpersDict); - dict.setValue("raycastPath", raycaster->getRaycastPath()); - - _raycastData[raycaster] = data; - - try { - _exitPrograms[raycaster] = ghoul::opengl::ProgramObject::Build( - "Volume " + std::to_string(data.id) + " exit", - vsPath, - ExitFragmentShaderPath, - dict - ); - } catch (ghoul::RuntimeError e) { - LERROR(e.message); - } - try { - ghoul::Dictionary outsideDict = dict; - outsideDict.setValue("getEntryPath", GetEntryOutsidePath); - _raycastPrograms[raycaster] = ghoul::opengl::ProgramObject::Build( - "Volume " + std::to_string(data.id) + " raycast", - vsPath, - RaycastFragmentShaderPath, - outsideDict - ); - } catch (ghoul::RuntimeError e) { - LERROR(e.message); - } - try { - ghoul::Dictionary insideDict = dict; - insideDict.setValue("getEntryPath", GetEntryInsidePath); - _insideRaycastPrograms[raycaster] = ghoul::opengl::ProgramObject::Build( - "Volume " + std::to_string(data.id) + " inside raycast", - "${SHADERS}/framebuffer/resolveframebuffer.vert", - RaycastFragmentShaderPath, - insideDict - ); - } - catch (const ghoul::RuntimeError& e) { - LERRORC(e.component, e.message); - } - } - _dirtyRaycastData = false; -} - -void FramebufferRenderer::render(float blackoutFactor, bool doPerformanceMeasurements) { - std::unique_ptr perf; - if (doPerformanceMeasurements) { - perf = std::make_unique( - "FramebufferRenderer::render", - OsEng.renderEngine().performanceManager() - ); - } - - if (!_scene || !_camera) { - return; - } - - glEnable(GL_DEPTH_TEST); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - Time time = OsEng.timeManager().time(); - - RenderData data = { *_camera, psc(), time, doPerformanceMeasurements, 0 }; - RendererTasks tasks; - - // Capture standard fbo - GLint defaultFbo; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFbo); - - glBindFramebuffer(GL_FRAMEBUFFER, _mainFramebuffer); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - data.renderBinMask = static_cast(Renderable::RenderBin::Background); - _scene->render(data, tasks); - data.renderBinMask = static_cast(Renderable::RenderBin::Opaque); - _scene->render(data, tasks); - data.renderBinMask = static_cast(Renderable::RenderBin::Transparent); - _scene->render(data, tasks); - data.renderBinMask = static_cast(Renderable::RenderBin::Overlay); - _scene->render(data, tasks); - - for (const RaycasterTask& raycasterTask : tasks.raycasterTasks) { - VolumeRaycaster* raycaster = raycasterTask.raycaster; - - glBindFramebuffer(GL_FRAMEBUFFER, _exitFramebuffer); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - ghoul::opengl::ProgramObject* exitProgram = _exitPrograms[raycaster].get(); - if (exitProgram) { - exitProgram->activate(); - raycaster->renderExitPoints(raycasterTask.renderData, *exitProgram); - exitProgram->deactivate(); - } - - glBindFramebuffer(GL_FRAMEBUFFER, _mainFramebuffer); - glm::vec3 cameraPosition; - bool cameraIsInside = raycaster->cameraIsInside( - raycasterTask.renderData, - cameraPosition - ); - ghoul::opengl::ProgramObject* raycastProgram = nullptr; - - if (cameraIsInside) { - raycastProgram = _insideRaycastPrograms[raycaster].get(); - if (raycastProgram) { - raycastProgram->activate(); - raycastProgram->setUniform("cameraPosInRaycaster", cameraPosition); - } - } else { - raycastProgram = _raycastPrograms[raycaster].get(); - if (raycastProgram) { - raycastProgram->activate(); - } - } - - if (raycastProgram) { - raycaster->preRaycast(_raycastData[raycaster], *raycastProgram); - - ghoul::opengl::TextureUnit exitColorTextureUnit; - exitColorTextureUnit.activate(); - glBindTexture(GL_TEXTURE_2D, _exitColorTexture); - raycastProgram->setUniform("exitColorTexture", exitColorTextureUnit); - - ghoul::opengl::TextureUnit exitDepthTextureUnit; - exitDepthTextureUnit.activate(); - glBindTexture(GL_TEXTURE_2D, _exitDepthTexture); - raycastProgram->setUniform("exitDepthTexture", exitDepthTextureUnit); - - ghoul::opengl::TextureUnit mainDepthTextureUnit; - mainDepthTextureUnit.activate(); - glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainDepthTexture); - raycastProgram->setUniform("mainDepthTexture", mainDepthTextureUnit); - - raycastProgram->setUniform("nAaSamples", _nAaSamples); - raycastProgram->setUniform("windowSize", glm::vec2(_resolution)); - - - glDisable(GL_DEPTH_TEST); - glDepthMask(false); - if (cameraIsInside) { - glBindVertexArray(_screenQuad); - glDrawArrays(GL_TRIANGLES, 0, 6); - glBindVertexArray(0); - } else { - raycaster->renderEntryPoints(raycasterTask.renderData, *raycastProgram); - } - glDepthMask(true); - glEnable(GL_DEPTH_TEST); - - raycaster->postRaycast(_raycastData[raycaster], *raycastProgram); - raycastProgram->deactivate(); - } else { - LWARNING("Raycaster is not attached when trying to perform raycaster task"); - } - } - - glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo); - _resolveProgram->activate(); - - ghoul::opengl::TextureUnit mainColorTextureUnit; - mainColorTextureUnit.activate(); - glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainColorTexture); - - _resolveProgram->setUniform("mainColorTexture", mainColorTextureUnit); - _resolveProgram->setUniform("blackoutFactor", blackoutFactor); - _resolveProgram->setUniform("nAaSamples", _nAaSamples); - glBindVertexArray(_screenQuad); - glDrawArrays(GL_TRIANGLES, 0, 6); - glBindVertexArray(0); - - _resolveProgram->deactivate(); -} - -void FramebufferRenderer::setScene(Scene* scene) { - _scene = scene; -} - -void FramebufferRenderer::setCamera(Camera* camera) { - _camera = camera; -} - -void FramebufferRenderer::setResolution(glm::ivec2 res) { - _resolution = res; - _dirtyResolution = true; -} - -void FramebufferRenderer::setNAaSamples(int nAaSamples) { - _nAaSamples = nAaSamples; - if (_nAaSamples == 0) { - _nAaSamples = 1; - } - if (_nAaSamples > 8) { - LERROR("Framebuffer renderer does not support more than 8 MSAA samples."); - _nAaSamples = 8; - } - _dirtyResolution = true; -} - -void FramebufferRenderer::updateRendererData() { - ghoul::Dictionary dict; - dict.setValue("fragmentRendererPath", std::string(RenderFragmentShaderPath)); - - _rendererData = dict; - - OsEng.renderEngine().setRendererData(dict); -} - -} // namespace openspace +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace { + const char* _loggerCat = "FramebufferRenderer"; + const char* ExitFragmentShaderPath = "${SHADERS}/framebuffer/exitframebuffer.frag"; + const char* RaycastFragmentShaderPath = "${SHADERS}/framebuffer/raycastframebuffer.frag"; + const char* GetEntryInsidePath = "${SHADERS}/framebuffer/inside.glsl"; + const char* GetEntryOutsidePath = "${SHADERS}/framebuffer/outside.glsl"; + const char* RenderFragmentShaderPath = "${SHADERS}/framebuffer/renderframebuffer.frag"; +} // namespace + +namespace openspace { + +FramebufferRenderer::FramebufferRenderer() + : _camera(nullptr) + , _scene(nullptr) + , _resolution(glm::vec2(0)) +{} + +FramebufferRenderer::~FramebufferRenderer() {} + +void FramebufferRenderer::initialize() { + LINFO("Initializing FramebufferRenderer"); + + const GLfloat size = 1.0f; + const GLfloat vertex_data[] = { + // x y s t + -size, -size, 0.0f, 1.0f, + size, size, 0.0f, 1.0f, + -size, size, 0.0f, 1.0f, + -size, -size, 0.0f, 1.0f, + size, -size, 0.0f, 1.0f, + size, size, 0.0f, 1.0f + }; + + glGenVertexArrays(1, &_screenQuad); + glBindVertexArray(_screenQuad); + + glGenBuffers(1, &_vertexPositionBuffer); + glBindBuffer(GL_ARRAY_BUFFER, _vertexPositionBuffer); + + glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW); + glVertexAttribPointer( + 0, + 4, + GL_FLOAT, + GL_FALSE, + sizeof(GLfloat) * 4, + reinterpret_cast(0) + ); + glEnableVertexAttribArray(0); + + GLint defaultFbo; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFbo); + + // Main framebuffer + glGenTextures(1, &_mainColorTexture); + glGenTextures(1, &_mainDepthTexture); + glGenFramebuffers(1, &_mainFramebuffer); + + // Exit framebuffer + glGenTextures(1, &_exitColorTexture); + glGenTextures(1, &_exitDepthTexture); + glGenFramebuffers(1, &_exitFramebuffer); + + updateResolution(); + updateRendererData(); + updateRaycastData(); + + glBindFramebuffer(GL_FRAMEBUFFER, _mainFramebuffer); + glFramebufferTexture2D( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D_MULTISAMPLE, + _mainColorTexture, + 0 + ); + glFramebufferTexture2D( + GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_TEXTURE_2D_MULTISAMPLE, + _mainDepthTexture, + 0 + ); + + glBindFramebuffer(GL_FRAMEBUFFER, _exitFramebuffer); + glFramebufferTexture2D( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + _exitColorTexture, + 0 + ); + glFramebufferTexture2D( + GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_TEXTURE_2D, + _exitDepthTexture, + 0 + ); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + LERROR("Main framebuffer is not complete"); + } + + glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo); + + try { + _resolveProgram = ghoul::opengl::ProgramObject::Build( + "Framebuffer Resolve", + "${SHADERS}/framebuffer/resolveframebuffer.vert", + "${SHADERS}/framebuffer/resolveframebuffer.frag" + ); + } catch (const ghoul::RuntimeError& e) { + LERRORC(e.component, e.message); + } + + OsEng.renderEngine().raycasterManager().addListener(*this); +} + +void FramebufferRenderer::deinitialize() { + LINFO("Deinitializing FramebufferRenderer"); + + glDeleteFramebuffers(1, &_mainFramebuffer); + glDeleteFramebuffers(1, &_exitFramebuffer); + + glDeleteTextures(1, &_mainColorTexture); + glDeleteTextures(1, &_mainDepthTexture); + glDeleteTextures(1, &_exitColorTexture); + glDeleteTextures(1, &_exitDepthTexture); + + glDeleteBuffers(1, &_vertexPositionBuffer); + glDeleteVertexArrays(1, &_screenQuad); + + OsEng.renderEngine().raycasterManager().removeListener(*this); +} + +void FramebufferRenderer::raycastersChanged(VolumeRaycaster&, bool) { + _dirtyRaycastData = true; +} + +void FramebufferRenderer::update() { + if (_dirtyResolution) { + updateResolution(); + } + + if (_dirtyRaycastData) { + updateRaycastData(); + } + + // If the resolve dictionary changed (or a file changed on disk) + // then rebuild the resolve program. + if (_resolveProgram->isDirty()) { + try { + _resolveProgram->rebuildFromFile(); + } catch (const ghoul::RuntimeError& error) { + LERRORC(error.component, error.message); + } + } + + for (auto& program : _exitPrograms) { + if (program.second->isDirty()) { + try { + program.second->rebuildFromFile(); + } catch (const ghoul::RuntimeError& e) { + LERRORC(e.component, e.message); + } + } + } + + for (auto& program : _raycastPrograms) { + if (program.second->isDirty()) { + try { + program.second->rebuildFromFile(); + } catch (const ghoul::RuntimeError& e) { + LERRORC(e.component, e.message); + } + } + } + + for (auto& program : _insideRaycastPrograms) { + if (program.second->isDirty()) { + try { + program.second->rebuildFromFile(); + } + catch (const ghoul::RuntimeError& e) { + LERRORC(e.component, e.message); + } + } + } +} + +void FramebufferRenderer::updateResolution() { + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainColorTexture); + + glTexImage2DMultisample( + GL_TEXTURE_2D_MULTISAMPLE, + _nAaSamples, + GL_RGBA, + GLsizei(_resolution.x), + GLsizei(_resolution.y), + true + ); + + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainDepthTexture); + glTexImage2DMultisample( + GL_TEXTURE_2D_MULTISAMPLE, + _nAaSamples, + GL_DEPTH_COMPONENT32F, + GLsizei(_resolution.x), + GLsizei(_resolution.y), + true + ); + + glBindTexture(GL_TEXTURE_2D, _exitColorTexture); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA16, + GLsizei(_resolution.x), + GLsizei(_resolution.y), + 0, + GL_RGBA, + GL_UNSIGNED_SHORT, + nullptr + ); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glBindTexture(GL_TEXTURE_2D, _exitDepthTexture); + + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_DEPTH_COMPONENT32F, + GLsizei(_resolution.x), + GLsizei(_resolution.y), + 0, + GL_DEPTH_COMPONENT, + GL_FLOAT, + nullptr + ); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + _dirtyResolution = false; +} + +void FramebufferRenderer::updateRaycastData() { + _raycastData.clear(); + _exitPrograms.clear(); + _raycastPrograms.clear(); + _insideRaycastPrograms.clear(); + + const std::vector& raycasters = + OsEng.renderEngine().raycasterManager().raycasters(); + int nextId = 0; + for (auto& raycaster : raycasters) { + RaycastData data; + data.id = nextId++; + data.namespaceName = "HELPER"; + + std::string vsPath = raycaster->getBoundsVsPath(); + std::string fsPath = raycaster->getBoundsFsPath(); + + ghoul::Dictionary dict; + dict.setValue("rendererData", _rendererData); + dict.setValue("fragmentPath", fsPath); + dict.setValue("id", data.id); + std::string helperPath = raycaster->getHelperPath(); + ghoul::Dictionary helpersDict; + if (!helperPath.empty()) { + helpersDict.setValue("0", helperPath); + } + dict.setValue("helperPaths", helpersDict); + dict.setValue("raycastPath", raycaster->getRaycastPath()); + + _raycastData[raycaster] = data; + + try { + _exitPrograms[raycaster] = ghoul::opengl::ProgramObject::Build( + "Volume " + std::to_string(data.id) + " exit", + vsPath, + ExitFragmentShaderPath, + dict + ); + } catch (ghoul::RuntimeError e) { + LERROR(e.message); + } + try { + ghoul::Dictionary outsideDict = dict; + outsideDict.setValue("getEntryPath", GetEntryOutsidePath); + _raycastPrograms[raycaster] = ghoul::opengl::ProgramObject::Build( + "Volume " + std::to_string(data.id) + " raycast", + vsPath, + RaycastFragmentShaderPath, + outsideDict + ); + } catch (ghoul::RuntimeError e) { + LERROR(e.message); + } + try { + ghoul::Dictionary insideDict = dict; + insideDict.setValue("getEntryPath", GetEntryInsidePath); + _insideRaycastPrograms[raycaster] = ghoul::opengl::ProgramObject::Build( + "Volume " + std::to_string(data.id) + " inside raycast", + "${SHADERS}/framebuffer/resolveframebuffer.vert", + RaycastFragmentShaderPath, + insideDict + ); + } + catch (const ghoul::RuntimeError& e) { + LERRORC(e.component, e.message); + } + } + _dirtyRaycastData = false; +} + +void FramebufferRenderer::render(float blackoutFactor, bool doPerformanceMeasurements) { + std::unique_ptr perf; + if (doPerformanceMeasurements) { + perf = std::make_unique( + "FramebufferRenderer::render", + OsEng.renderEngine().performanceManager() + ); + } + + if (!_scene || !_camera) { + return; + } + + glEnable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + Time time = OsEng.timeManager().time(); + + RenderData data = { *_camera, psc(), time, doPerformanceMeasurements, 0 }; + RendererTasks tasks; + + // Capture standard fbo + GLint defaultFbo; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFbo); + + glBindFramebuffer(GL_FRAMEBUFFER, _mainFramebuffer); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + data.renderBinMask = static_cast(Renderable::RenderBin::Background); + _scene->render(data, tasks); + data.renderBinMask = static_cast(Renderable::RenderBin::Opaque); + _scene->render(data, tasks); + data.renderBinMask = static_cast(Renderable::RenderBin::Transparent); + _scene->render(data, tasks); + data.renderBinMask = static_cast(Renderable::RenderBin::Overlay); + _scene->render(data, tasks); + + for (const RaycasterTask& raycasterTask : tasks.raycasterTasks) { + VolumeRaycaster* raycaster = raycasterTask.raycaster; + + glBindFramebuffer(GL_FRAMEBUFFER, _exitFramebuffer); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + ghoul::opengl::ProgramObject* exitProgram = _exitPrograms[raycaster].get(); + if (exitProgram) { + exitProgram->activate(); + raycaster->renderExitPoints(raycasterTask.renderData, *exitProgram); + exitProgram->deactivate(); + } + + glBindFramebuffer(GL_FRAMEBUFFER, _mainFramebuffer); + glm::vec3 cameraPosition; + bool cameraIsInside = raycaster->cameraIsInside( + raycasterTask.renderData, + cameraPosition + ); + ghoul::opengl::ProgramObject* raycastProgram = nullptr; + + if (cameraIsInside) { + raycastProgram = _insideRaycastPrograms[raycaster].get(); + if (raycastProgram) { + raycastProgram->activate(); + raycastProgram->setUniform("cameraPosInRaycaster", cameraPosition); + } + } else { + raycastProgram = _raycastPrograms[raycaster].get(); + if (raycastProgram) { + raycastProgram->activate(); + } + } + + if (raycastProgram) { + raycaster->preRaycast(_raycastData[raycaster], *raycastProgram); + + ghoul::opengl::TextureUnit exitColorTextureUnit; + exitColorTextureUnit.activate(); + glBindTexture(GL_TEXTURE_2D, _exitColorTexture); + raycastProgram->setUniform("exitColorTexture", exitColorTextureUnit); + + ghoul::opengl::TextureUnit exitDepthTextureUnit; + exitDepthTextureUnit.activate(); + glBindTexture(GL_TEXTURE_2D, _exitDepthTexture); + raycastProgram->setUniform("exitDepthTexture", exitDepthTextureUnit); + + ghoul::opengl::TextureUnit mainDepthTextureUnit; + mainDepthTextureUnit.activate(); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainDepthTexture); + raycastProgram->setUniform("mainDepthTexture", mainDepthTextureUnit); + + raycastProgram->setUniform("nAaSamples", _nAaSamples); + raycastProgram->setUniform("windowSize", glm::vec2(_resolution)); + + + glDisable(GL_DEPTH_TEST); + glDepthMask(false); + if (cameraIsInside) { + glBindVertexArray(_screenQuad); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); + } else { + raycaster->renderEntryPoints(raycasterTask.renderData, *raycastProgram); + } + glDepthMask(true); + glEnable(GL_DEPTH_TEST); + + raycaster->postRaycast(_raycastData[raycaster], *raycastProgram); + raycastProgram->deactivate(); + } else { + LWARNING("Raycaster is not attached when trying to perform raycaster task"); + } + } + + glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo); + _resolveProgram->activate(); + + ghoul::opengl::TextureUnit mainColorTextureUnit; + mainColorTextureUnit.activate(); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainColorTexture); + + _resolveProgram->setUniform("mainColorTexture", mainColorTextureUnit); + _resolveProgram->setUniform("blackoutFactor", blackoutFactor); + _resolveProgram->setUniform("nAaSamples", _nAaSamples); + glBindVertexArray(_screenQuad); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); + + _resolveProgram->deactivate(); +} + +void FramebufferRenderer::setScene(Scene* scene) { + _scene = scene; +} + +void FramebufferRenderer::setCamera(Camera* camera) { + _camera = camera; +} + +void FramebufferRenderer::setResolution(glm::ivec2 res) { + _resolution = res; + _dirtyResolution = true; +} + +void FramebufferRenderer::setNAaSamples(int nAaSamples) { + _nAaSamples = nAaSamples; + if (_nAaSamples == 0) { + _nAaSamples = 1; + } + if (_nAaSamples > 8) { + LERROR("Framebuffer renderer does not support more than 8 MSAA samples."); + _nAaSamples = 8; + } + _dirtyResolution = true; +} + +void FramebufferRenderer::updateRendererData() { + ghoul::Dictionary dict; + dict.setValue("fragmentRendererPath", std::string(RenderFragmentShaderPath)); + + _rendererData = dict; + + OsEng.renderEngine().setRendererData(dict); +} + +} // namespace openspace diff --git a/src/util/syncbuffer.cpp b/src/util/syncbuffer.cpp index 593012be92..3e2499ab81 100644 --- a/src/util/syncbuffer.cpp +++ b/src/util/syncbuffer.cpp @@ -1,62 +1,62 @@ -/***************************************************************************************** - * * - * OpenSpace * - * * - * Copyright (c) 2014-2017 * - * * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this * - * software and associated documentation files (the "Software"), to deal in the Software * - * without restriction, including without limitation the rights to use, copy, modify, * - * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * - * permit persons to whom the Software is furnished to do so, subject to the following * - * conditions: * - * * - * The above copyright notice and this permission notice shall be included in all copies * - * or substantial portions of the Software. * - * * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * - ****************************************************************************************/ - -#include - -#include - -namespace openspace { - -SyncBuffer::SyncBuffer(size_t n) - : _n(n) - , _encodeOffset(0) - , _decodeOffset(0) - , _synchronizationBuffer(new sgct::SharedVector()) -{ - _dataStream.resize(_n); -} - -SyncBuffer::~SyncBuffer() { - // The destructor is defined here, so that the otherwise default inlined destructor is - // not created (which would make it impossible to use a forward declaration with - // unique_ptr -} - -void SyncBuffer::write() { - _dataStream.resize(_encodeOffset); - _synchronizationBuffer->setVal(_dataStream); - sgct::SharedData::instance()->writeVector(_synchronizationBuffer.get()); - _dataStream.resize(_n); - _encodeOffset = 0; - _decodeOffset = 0; -} - -void SyncBuffer::read() { - sgct::SharedData::instance()->readVector(_synchronizationBuffer.get()); - _dataStream = _synchronizationBuffer->getVal(); - _encodeOffset = 0; - _decodeOffset = 0; -} - -} // namespace openspace +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include + +namespace openspace { + +SyncBuffer::SyncBuffer(size_t n) + : _n(n) + , _encodeOffset(0) + , _decodeOffset(0) + , _synchronizationBuffer(new sgct::SharedVector()) +{ + _dataStream.resize(_n); +} + +SyncBuffer::~SyncBuffer() { + // The destructor is defined here, so that the otherwise default inlined destructor is + // not created (which would make it impossible to use a forward declaration with + // unique_ptr +} + +void SyncBuffer::write() { + _dataStream.resize(_encodeOffset); + _synchronizationBuffer->setVal(_dataStream); + sgct::SharedData::instance()->writeVector(_synchronizationBuffer.get()); + _dataStream.resize(_n); + _encodeOffset = 0; + _decodeOffset = 0; +} + +void SyncBuffer::read() { + sgct::SharedData::instance()->readVector(_synchronizationBuffer.get()); + _dataStream = _synchronizationBuffer->getVal(); + _encodeOffset = 0; + _decodeOffset = 0; +} + +} // namespace openspace diff --git a/support/cmake/support_macros.cmake b/support/cmake/support_macros.cmake index 9e895cd903..cc58e7e47d 100644 --- a/support/cmake/support_macros.cmake +++ b/support/cmake/support_macros.cmake @@ -1,534 +1,564 @@ -######################################################################################### -# # -# OpenSpace # -# # -# Copyright (c) 2014-2017 # -# # -# Permission is hereby granted, free of charge, to any person obtaining a copy of this # -# software and associated documentation files (the "Software"), to deal in the Software # -# without restriction, including without limitation the rights to use, copy, modify, # -# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # -# permit persons to whom the Software is furnished to do so, subject to the following # -# conditions: # -# # -# The above copyright notice and this permission notice shall be included in all copies # -# or substantial portions of the Software. # -# # -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # -# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # -# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # -# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF # -# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # -# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # -######################################################################################### - -function (test_compiler_compatibility) - if (MSVC) - if (MSVC_VERSION LESS 1900) - message(FATAL_ERROR "OpenSpace requires at least Visual Studio 2015") - endif () - endif () -endfunction () - - - -macro (cleanup_project) - # Remove MinSizeRel build option - set(CMAKE_CONFIGURATION_TYPES Debug Release RelWithDebInfo CACHE TYPE INTERNAL FORCE) - mark_as_advanced(CMAKE_CONFIGURATION_TYPES) - mark_as_advanced(CMAKE_INSTALL_PREFIX) - - set_property(GLOBAL PROPERTY USE_FOLDERS On) - set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER CMake) -endmacro () - - -macro (set_build_output_directories) - set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${OPENSPACE_CMAKE_EXT_DIR}) - set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OPENSPACE_BASE_DIR}/bin/lib) - set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OPENSPACE_BASE_DIR}/bin/lib) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OPENSPACE_BASE_DIR}/bin/openspace) -endmacro () - - - -function (create_openspace_target) - add_library(libOpenSpace STATIC ${OPENSPACE_HEADER} ${OPENSPACE_SOURCE}) - target_include_directories(libOpenSpace PUBLIC ${OPENSPACE_BASE_DIR}/include) - target_include_directories(libOpenSpace PUBLIC ${OPENSPACE_BASE_DIR}) - target_include_directories(libOpenSpace PUBLIC ${CMAKE_BINARY_DIR}/_generated/include) - - configure_file( - ${OPENSPACE_CMAKE_EXT_DIR}/openspace_header.template - ${CMAKE_BINARY_DIR}/_generated/include/openspace/openspace.h - @ONLY IMMEDIATE - ) - - set_compile_settings(libOpenSpace) -endfunction () - - - -function (set_compile_settings project) - set_property(TARGET ${project} PROPERTY CXX_STANDARD 17) - set_property(TARGET ${project} PROPERTY CXX_STANDARD_REQUIRED On) - - if (MSVC) - target_compile_options(${project} PRIVATE - "/MP" # Multi-threading support - "/W4" # Enable all warnings - "/ZI" # Edit and continue support - "/wd4201" # Disable "nameless struct" warning - "/wd4127" # Disable "conditional expression is constant" warning - "/permissive-" # Disable working, but non-conforming code - "/Zc:strictStrings-" # Windows header don't adhere to this - # "/std:c++latest" # Boost as of 1.64 still uses unary_function - ) - if (OPENSPACE_WARNINGS_AS_ERRORS) - target_compile_options(${project} PRIVATE "/WX") - endif () - elseif (APPLE) - target_compile_definitions(${project} PRIVATE "__APPLE__") - - if (OPENSPACE_WARNINGS_AS_ERRORS) - target_compile_options(${project} PRIVATE "-Werror") - endif () - - target_compile_options(${project} PRIVATE "-stdlib=libc++") - - target_include_directories(${project} PUBLIC "/Developer/Headers/FlatCarbon") - find_library(COREFOUNDATION_LIBRARY CoreFoundation) - find_library(CARBON_LIBRARY Carbon) - find_library(COCOA_LIBRARY Carbon) - find_library(APP_SERVICES_LIBRARY ApplicationServices) - mark_as_advanced(CARBON_LIBRARY COCOA_LIBRARY APP_SERVICES_LIBRARY) - target_link_libraries(${project} - ${CARBON_LIBRARY} - ${COREFOUNDATION_LIBRARY} - ${COCOA_LIBRARY} - ${APP_SERVICES_LIBRARY} - ) - elseif (UNIX) - target_compile_options(${project} PRIVATE "-ggdb" "-Wall" "-Wno-long-long" "-pedantic" "-Wextra") - - if (OPENSPACE_WARNINGS_AS_ERRORS) - target_compile_options(${project} PRIVATE "-Werror") - endif () - endif () -endfunction () - - - -function (add_external_dependencies) - # Ghoul - add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul) - target_link_libraries(libOpenSpace Ghoul) - get_property(GHOUL_INCLUDE_DIR TARGET Ghoul PROPERTY INTERFACE_INCLUDE_DIRECTORIES) - target_include_directories(libOpenSpace PUBLIC ${GHOUL_INCLUDE_DIR}) - get_property(GHOUL_DEFINITIONS TARGET Ghoul PROPERTY INTERFACE_COMPILE_DEFINITIONS) - target_compile_definitions(libOpenSpace PUBLIC ${GHOUL_DEFINITIONS}) - set_property(TARGET Lua PROPERTY FOLDER "External") - set_property(TARGET lz4 PROPERTY FOLDER "External") - - # SGCT - set(SGCT_TEXT OFF CACHE BOOL "" FORCE) - set(SGCT_BUILD_CSHARP_PROJECTS OFF CACHE BOOL "" FORCE) - set(SGCT_LIGHT_ONLY ON CACHE BOOL "" FORCE) - set(SGCT_CUSTOMOUTPUTDIRS OFF CACHE BOOL "" FORCE) - - add_subdirectory(${OPENSPACE_EXT_DIR}/sgct) - target_include_directories(libOpenSpace SYSTEM PUBLIC ${OPENSPACE_EXT_DIR}/sgct/include) - target_link_libraries( - libOpenSpace - # sgct - sgct_light glew glfw png16_static quat tinyxml2static turbojpeg-static - vrpn - ${GLFW_LIBRARIES} - ) - - set_property(TARGET sgct_light PROPERTY FOLDER "External") - set_property(TARGET glew PROPERTY FOLDER "External/SGCT") - set_property(TARGET glfw PROPERTY FOLDER "External/SGCT") - set_property(TARGET png16_static PROPERTY FOLDER "External/SGCT") - set_property(TARGET quat PROPERTY FOLDER "External/SGCT") - set_property(TARGET simd PROPERTY FOLDER "External/SGCT") - set_property(TARGET tinyxml2static PROPERTY FOLDER "External/SGCT") - set_property(TARGET turbojpeg-static PROPERTY FOLDER "External/SGCT") - set_property(TARGET vrpn PROPERTY FOLDER "External/SGCT") - set_property(TARGET zlibstatic PROPERTY FOLDER "External/SGCT") - - if (UNIX AND (NOT APPLE)) - target_link_libraries(libOpenSpace Xcursor Xinerama X11) - endif () - - # Spice - add_subdirectory(${OPENSPACE_EXT_DIR}/spice) - target_link_libraries(libOpenSpace Spice) - get_property(SPICE_INCLUDE_DIR TARGET Spice PROPERTY INTERFACE_INCLUDE_DIRECTORIES) - target_include_directories(libOpenSpace PUBLIC ${SPICE_INCLUDE_DIR}) - set_property(TARGET Spice PROPERTY FOLDER "External") - - # Curl - if (WIN32) - set(CURL_ROOT_DIR "${OPENSPACE_EXT_DIR}/curl") - set(CURL_ROOT_DIR "${OPENSPACE_EXT_DIR}/curl" PARENT_SCOPE) - target_include_directories(libOpenSpace SYSTEM PUBLIC ${CURL_ROOT_DIR}/include) - target_link_libraries(libOpenSpace ${CURL_ROOT_DIR}/lib/libcurl_imp.lib) - target_compile_definitions(libOpenSpace PUBLIC "OPENSPACE_CURL_ENABLED" "CURL_STATICLIB") - else () - find_package(CURL) - if (CURL_FOUND) - target_include_directories(libOpenSpace SYSTEM PUBLIC ${CURL_INCLUDE_DIRS}) - target_link_libraries(libOpenSpace ${CURL_LIBRARIES}) - target_compile_definitions(libOpenSpace PUBLIC "OPENSPACE_CURL_ENABLED") - endif () - endif() - - # Qt - # Unfortunately, we have to set this value manually; sigh - # In the future, if the Qt version is updated, just add to this variable ---abock - if (APPLE) - set(CMAKE_PREFIX_PATH - "~/Qt/5.6/clang_64/lib/cmake" - "~/Qt/5.7/clang_64/lib/cmake" - PARENT_SCOPE - ) - endif () -endfunction () - - - -function (handle_applications) - set(applications "") - set(applications_link_to_openspace "") - - file(GLOB appDirs RELATIVE ${OPENSPACE_APPS_DIR} ${OPENSPACE_APPS_DIR}/*) - list(REMOVE_ITEM appDirs ".DS_Store") # Removing the .DS_Store present on Mac - - set(DEFAULT_APPLICATIONS - "OpenSpace" - "Launcher" - ) - mark_as_advanced(DEFAULT_APPLICATIONS) - - foreach (app ${appDirs}) - string(TOUPPER ${app} upper_app) - list (FIND DEFAULT_APPLICATIONS "${app}" _index) - if (${_index} GREATER -1) - # App is a default application - option(OPENSPACE_APPLICATION_${upper_app} "${app} Application" ON) - else () - option(OPENSPACE_APPLICATION_${upper_app} "${app} Application" OFF) - endif() - if (OPENSPACE_APPLICATION_${upper_app}) - unset(APPLICATION_NAME) - unset(APPLICATION_LINK_TO_OPENSPACE) - include(${OPENSPACE_APPS_DIR}/${app}/CMakeLists.txt) - set_compile_settings(${APPLICATION_NAME}) - - if (APPLICATION_LINK_TO_OPENSPACE) - get_property( - OPENSPACE_INCLUDE_DIR - TARGET libOpenSpace - PROPERTY INTERFACE_INCLUDE_DIRECTORIES - ) - target_include_directories(${APPLICATION_NAME} PUBLIC - "${OPENSPACE_BASE_DIR}" - ${OPENSPACE_INCLUDE_DIR} - ) - - get_property( - OPENSPACE_DEFINES - TARGET libOpenSpace - PROPERTY INTERFACE_COMPILE_DEFINITIONS - ) - target_compile_definitions(${APPLICATION_NAME} PUBLIC ${OPENSPACE_DEFINES}) - - target_link_libraries(${APPLICATION_NAME} Ghoul) - target_link_libraries(${APPLICATION_NAME} libOpenSpace) - - if (MSVC) - set_target_properties(${APPLICATION_NAME} PROPERTIES LINK_FLAGS - "/NODEFAULTLIB:LIBCMTD.lib /NODEFAULTLIB:LIBCMT.lib" - ) - endif () - - - if (WIN32) - ghl_copy_files( - ${APPLICATION_NAME} - "${CURL_ROOT_DIR}/lib/libcurl.dll" - "${CURL_ROOT_DIR}/lib/libeay32.dll" - "${CURL_ROOT_DIR}/lib/ssleay32.dll" - - ) - ghl_copy_shared_libraries(${APPLICATION_NAME} ${OPENSPACE_EXT_DIR}/ghoul) - endif () - endif () - - list(APPEND applications ${APPLICATION_NAME}) - list(APPEND applications_link_to_openspace ${APPLICATION_LINK_TO_OPENSPACE}) - unset(APPLICATION_NAME) - unset(APPLICATION_LINK_TO_OPENSPACE) - endif () - endforeach () - - - # option(OPENSPACE_APPLICATION_OPENSPACE "Main OpenSpace Application" ON) - # if (OPENSPACE_APPLICATION_OPENSPACE) - # include(${OPENSPACE_APPS_DIR}/OpenSpace/CMakeLists.txt) - # list(APPEND applications "OpenSpace") - # endif () - set(OPENSPACE_APPLICATIONS ${applications} PARENT_SCOPE) - set(OPENSPACE_APPLICATIONS_LINK_REQUEST ${applications_link_to_openspace} PARENT_SCOPE) - - message(STATUS "Applications:") - list(LENGTH applications len1) - math(EXPR len2 "${len1} - 1") - - foreach(val RANGE ${len2}) - list(GET applications ${val} val1) - list(GET applications_link_to_openspace ${val} val2) - message(STATUS "\t${val1} (Link: ${val2})") - endforeach() -endfunction() - - -function (handle_option_vld) - if (OPENSPACE_ENABLE_VLD) - target_compile_definitions(libOpenSpace PUBLIC "OPENSPACE_ENABLE_VLD") - target_link_libraries(libOpenSpace ${OPENSPACE_EXT_DIR}/vld/lib/vld.lib) - target_include_directories(libOpenSpace PUBLIC ${OPENSPACE_EXT_DIR}/vld) - - foreach (app ${OPENSPACE_APPLCATIONS}) - ghl_copy_files(${app} "${OPENSPACE_EXT_DIR}/vld/bin/vld_x64.dll") - endforeach () - endif () -endfunction () - - - -function (handle_option_tests) - if (OPENSPACE_HAVE_TESTS) - if (NOT TARGET gtest) - set(BUILD_GTEST ON CACHE BOOL "") - set(BUILD_GMOCK OFF CACHE BOOL "") - set(gtest_force_shared_crt ON CACHE BOOL "") - # set(BUILD_GMOCK OFF CACHE BOOL "") - # option(BUILD_GTEST "Builds the googletest subproject" CACHE ON) - # option(BUILD_GMOCK "Builds the googlemock subproject" CACHE OFF) - # option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." CACHE ON) - add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul/ext/googletest) - # add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul/ext/gtest) - set_property(TARGET gtest PROPERTY FOLDER "External") - set_property(TARGET gtest_main PROPERTY FOLDER "External") - endif () - - file(GLOB_RECURSE OPENSPACE_TEST_FILES ${OPENSPACE_BASE_DIR}/tests/*.inl) - - add_executable(OpenSpaceTest ${OPENSPACE_BASE_DIR}/tests/main.cpp ${OPENSPACE_TEST_FILES}) - target_include_directories(OpenSpaceTest PUBLIC - "${OPENSPACE_BASE_DIR}/include" - "${OPENSPACE_BASE_DIR}/tests" - "${OPENSPACE_EXT_DIR}/ghoul/ext/googletest/googletest/include" - ) - target_compile_definitions(OpenSpaceTest PUBLIC - "GHL_THROW_ON_ASSERT" - "GTEST_HAS_TR1_TUPLE=0" - ) - target_link_libraries(OpenSpaceTest gtest libOpenSpace) - - if (MSVC) - set_target_properties(OpenSpaceTest PROPERTIES LINK_FLAGS - "/NODEFAULTLIB:LIBCMTD.lib /NODEFAULTLIB:LIBCMT.lib" - ) - endif () - set_property(TARGET OpenSpaceTest PROPERTY FOLDER "Unit Tests") - set_property(TARGET OpenSpaceTest PROPERTY CXX_STANDARD 14) - set_property(TARGET OpenSpaceTest PROPERTY CXX_STANDARD_REQUIRED On) - endif (OPENSPACE_HAVE_TESTS) - if (TARGET GhoulTest) - if (NOT TARGET gtest) - set(BUILD_GTEST ON CACHE BOOL "") - set(BUILD_GMOCK OFF CACHE BOOL "") - set(gtest_force_shared_crt ON CACHE BOOL "") - # option(BUILD_GTEST "Builds the googletest subproject" CACHE ON) - # option(BUILD_GMOCK "Builds the googlemock subproject" CACHE OFF) - # option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." CACHE ON) - add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul/ext/googletest) - endif () - - set_property(TARGET gtest PROPERTY FOLDER "External") - set_property(TARGET GhoulTest PROPERTY FOLDER "Unit Tests") - endif () -endfunction () - - - -function (handle_internal_modules) -# Get all modules in the correct order based on their dependencies - file(GLOB moduleDirs RELATIVE ${OPENSPACE_MODULE_DIR} ${OPENSPACE_MODULE_DIR}/*) - set(sortedModules ${moduleDirs}) - foreach (dir ${moduleDirs}) - if (IS_DIRECTORY ${OPENSPACE_MODULE_DIR}/${dir}) - set(defaultModule OFF) - if (EXISTS "${OPENSPACE_MODULE_DIR}/${dir}/include.cmake") - unset(OPENSPACE_DEPENDENCIES) - unset(EXTERNAL_LIBRAY) - unset(DEFAULT_MODULE) - include(${OPENSPACE_MODULE_DIR}/${dir}/include.cmake) - - if (DEFINED DEFAULT_MODULE) - set(defaultModule ${DEFAULT_MODULE}) - endif () - if (OPENSPACE_DEPENDENCIES) - foreach (dependency ${OPENSPACE_DEPENDENCIES}) - create_library_name(${dependency} library) - if (TARGET ${library}) - # already registered - list(REMOVE_ITEM OPENSPACE_DEPENDENCIES ${dependency}) - endif () - endforeach () - - list(APPEND OPENSPACE_DEPENDENCIES ${dir}) - list(FIND sortedModules ${dir} dir_index) - # if (NOT dir STREQUAL "base") - # list(INSERT OPENSPACE_DEPENDENCIES 0 "base") - # endif () - list(INSERT sortedModules ${dir_index} ${OPENSPACE_DEPENDENCIES}) - list(REMOVE_DUPLICATES sortedModules) - endif () - endif () - create_option_name(${dir} optionName) - option(${optionName} "Build ${dir} Module" ${defaultModule}) - # create_library_name(${module} ${library}) - else () - list(REMOVE_ITEM sortedModules ${dir}) - endif () - endforeach () - - # Automatically set dependent modules to ON - set(dir_list ${sortedModules}) - set(dll_list "") - list(REVERSE dir_list) - foreach (dir ${dir_list}) - create_option_name(${dir} optionName) - if (${optionName}) - if (EXISTS "${OPENSPACE_MODULE_DIR}/${dir}/include.cmake") - unset(OPENSPACE_DEPENDENCIES) - unset(EXTERNAL_LIBRAY) - unset(DEFAULT_MODULE) - include(${OPENSPACE_MODULE_DIR}/${dir}/include.cmake) - - if (OPENSPACE_DEPENDENCIES) - foreach (dependency ${OPENSPACE_DEPENDENCIES}) - create_option_name(${dependency} dependencyOptionName) - if (NOT ${dependencyOptionName}) - set(${dependencyOptionName} ON CACHE BOOL "ff" FORCE) - message(STATUS "${dependencyOptionName} was set to build, due to dependency towards ${optionName}") - endif () - endforeach () - endif () - endif () - endif () - endforeach () - - set(MODULE_HEADERS "") - set(MODULE_CLASSES "") - - message(STATUS "Included modules:") - foreach (module ${sortedModules}) - create_option_name(${module} optionName) - if (${optionName}) - message(STATUS "\t${module}") - endif () - endforeach () - - # Add subdirectories in the correct order - foreach (module ${sortedModules}) - create_option_name(${module} optionName) - if (${optionName}) - create_library_name(${module} libraryName) - add_subdirectory(${OPENSPACE_MODULE_DIR}/${module}) - - list(LENGTH OPENSPACE_APPLICATIONS len1) - math(EXPR len2 "${len1} - 1") - - foreach(val RANGE ${len2}) - list(GET OPENSPACE_APPLICATIONS ${val} val1) - list(GET OPENSPACE_APPLICATIONS_LINK_REQUEST ${val} val2) - if (${val2}) - target_link_libraries(${app} ${libraryName}) - endif () - endforeach() - - # Only link libOpenSpace against the library if it has been set STATIC - get_target_property(libType ${libraryName} TYPE) - if (NOT ${libType} STREQUAL "SHARED_LIBRARY") - target_link_libraries(libOpenSpace ${libraryName}) - endif() - - create_define_name(${module} defineName) - target_compile_definitions(libOpenSpace PUBLIC "${defineName}") - - # Create registration file - string(TOUPPER ${module} module_upper) - string(TOLOWER ${module} module_lower) - unset(MODULE_NAME) - unset(MODULE_PATH) - include(${CMAKE_BINARY_DIR}/modules/${module_lower}/modulename.cmake) - - list(APPEND MODULE_HEADERS - #"#ifdef REGISTRATION_OPENSPACE${module_upper}MODULE\n" - "#include <${MODULE_PATH}>\n" - #"#endif\n\n" - ) - list(APPEND MODULE_CLASSES " new ${MODULE_NAME},\n") - - if (EXTERNAL_LIBRARY) - foreach (library ${EXTERNAL_LIBRARY}) - get_filename_component(lib ${library} ABSOLUTE) - list(APPEND dll_list ${lib}) - endforeach() - endif () - endif () - endforeach () - - if (NOT "${MODULE_HEADERS}" STREQUAL "") - string(REPLACE ";" "" MODULE_HEADERS ${MODULE_HEADERS}) - endif () - - if (NOT "${MODULE_CLASSES}" STREQUAL "") - string(REPLACE ";" "" MODULE_CLASSES ${MODULE_CLASSES}) - endif () - - configure_file( - ${OPENSPACE_CMAKE_EXT_DIR}/module_registration.template - ${CMAKE_BINARY_DIR}/_generated/include/openspace/moduleregistration.h - ) - - list(REMOVE_DUPLICATES dll_list) - - if (WIN32) - foreach (application ${OPENSPACE_APPLICATIONS}) - foreach (dll ${dll_list}) - ghl_copy_files(${application} ${dll}) - endforeach () - endforeach () - endif () -endfunction () - - - -function (copy_dynamic_libraries) - if (WIN32) - ghl_copy_files(OpenSpace "${CURL_ROOT_DIR}/lib/libcurl.dll") - - # Copy DLLs needed by Ghoul into the executable directory - ghl_copy_shared_libraries(OpenSpace ${OPENSPACE_EXT_DIR}/ghoul) - - if (TARGET OpenSpaceTest) - ghl_copy_shared_libraries(OpenSpaceTest ${OPENSPACE_EXT_DIR}/ghoul) - endif () - endif () -endfunction () +######################################################################################### +# # +# OpenSpace # +# # +# Copyright (c) 2014-2017 # +# # +# Permission is hereby granted, free of charge, to any person obtaining a copy of this # +# software and associated documentation files (the "Software"), to deal in the Software # +# without restriction, including without limitation the rights to use, copy, modify, # +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # +# permit persons to whom the Software is furnished to do so, subject to the following # +# conditions: # +# # +# The above copyright notice and this permission notice shall be included in all copies # +# or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF # +# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # +# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # +######################################################################################### + +function (test_compiler_compatibility) + if (MSVC) + if (MSVC_VERSION LESS 1900) + message(FATAL_ERROR "OpenSpace requires at least Visual Studio 2015") + endif () + endif () +endfunction () + + + +macro (cleanup_project) + # Remove MinSizeRel build option + set(CMAKE_CONFIGURATION_TYPES Debug Release RelWithDebInfo CACHE TYPE INTERNAL FORCE) + mark_as_advanced(CMAKE_CONFIGURATION_TYPES) + mark_as_advanced(CMAKE_INSTALL_PREFIX) + + set_property(GLOBAL PROPERTY USE_FOLDERS On) + set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER CMake) +endmacro () + + +macro (set_build_output_directories) + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${OPENSPACE_CMAKE_EXT_DIR}) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OPENSPACE_BASE_DIR}/bin/lib) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OPENSPACE_BASE_DIR}/bin/lib) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OPENSPACE_BASE_DIR}/bin/openspace) +endmacro () + + + +function (create_openspace_target) + add_library(libOpenSpace STATIC ${OPENSPACE_HEADER} ${OPENSPACE_SOURCE}) + target_include_directories(libOpenSpace PUBLIC ${OPENSPACE_BASE_DIR}/include) + target_include_directories(libOpenSpace PUBLIC ${OPENSPACE_BASE_DIR}) + target_include_directories(libOpenSpace PUBLIC ${CMAKE_BINARY_DIR}/_generated/include) + + configure_file( + ${OPENSPACE_CMAKE_EXT_DIR}/openspace_header.template + ${CMAKE_BINARY_DIR}/_generated/include/openspace/openspace.h + @ONLY IMMEDIATE + ) + + set_compile_settings(libOpenSpace) +endfunction () + + + +function (set_compile_settings project) + set_property(TARGET ${project} PROPERTY CXX_STANDARD 17) + set_property(TARGET ${project} PROPERTY CXX_STANDARD_REQUIRED On) + + if (MSVC) + target_compile_options(${project} PRIVATE + "/ZI" # Edit and continue support + "/MP" # Multi-threading support + "/W4" # Highest warning level + "/w44062" # enumerator 'identifier' in a switch of enum 'enumeration' is not handled + "/wd4127" # conditional expression is constant + "/wd4201" # nonstandard extension used : nameless struct/union + "/w44255" # 'function': no function prototype given: converting '()' to '(void)' + "/w44263" # 'function': member function does not override any base class virtual member function + "/w44264" # 'virtual_function': no override available for virtual member function from base 'class'; function is hidden + "/w44265" # 'class': class has virtual functions, but destructor is not virtual + "/w44266" # 'function': no override available for virtual member function from base 'type'; function is hidden + "/w44289" # nonstandard extension used : 'var' : loop control variable declared in the for-loop is used outside the for-loop scope + "/w44296" # 'operator': expression is always false + "/w44311" # 'variable' : pointer truncation from 'type' to 'type' + "/w44339" # 'type' : use of undefined type detected in CLR meta-data - use of this type may lead to a runtime exception + "/w44342" # behavior change: 'function' called, but a member operator was called in previous versions + "/w44350" # behavior change: 'member1' called instead of 'member2' + "/w44431" # missing type specifier - int assumed. Note: C no longer supports default-int + "/w44471" # a forward declaration of an unscoped enumeration must have an underlying type (int assumed) + "/w44545" # expression before comma evaluates to a function which is missing an argument list + "/w44546" # function call before comma missing argument list + "/w44547" # 'operator': operator before comma has no effect; expected operator with side-effect + "/w44548" # expression before comma has no effect; expected expression with side-effect + "/w44549" # 'operator': operator before comma has no effect; did you intend 'operator'? + "/w44555" # expression has no effect; expected expression with side-effect + "/w44574" # 'identifier' is defined to be '0': did you mean to use '#if identifier'? + "/w44608" # 'symbol1' has already been initialized by another union member in the initializer list, 'symbol2' + "/w44619" # #pragma warning: there is no warning number 'number' + "/w44628" # digraphs not supported with -Ze. Character sequence 'digraph' not interpreted as alternate token for 'char' + "/w44640" # 'instance': construction of local static object is not thread-safe + "/w44905" # wide string literal cast to 'LPSTR' + "/w44906" # string literal cast to 'LPWSTR' + "/w44946" # reinterpret_cast used between related classes: 'class1' and 'class2' + "/w44986" # 'symbol': exception specification does not match previous declaration + "/w44988" # 'symbol': variable declared outside class/function scope + # "/std:c++latest" # Boost as of 1.64 still uses unary_function + "/permissive-" + "/Zc:strictStrings-" # Windows header don't adhere to this + ) + if (OPENSPACE_WARNINGS_AS_ERRORS) + target_compile_options(${project} PRIVATE "/WX") + endif () + elseif (APPLE) + target_compile_definitions(${project} PRIVATE "__APPLE__") + + if (OPENSPACE_WARNINGS_AS_ERRORS) + target_compile_options(${project} PRIVATE "-Werror") + endif () + + target_compile_options(${project} PRIVATE "-stdlib=libc++") + + target_include_directories(${project} PUBLIC "/Developer/Headers/FlatCarbon") + find_library(COREFOUNDATION_LIBRARY CoreFoundation) + find_library(CARBON_LIBRARY Carbon) + find_library(COCOA_LIBRARY Carbon) + find_library(APP_SERVICES_LIBRARY ApplicationServices) + mark_as_advanced(CARBON_LIBRARY COCOA_LIBRARY APP_SERVICES_LIBRARY) + target_link_libraries(${project} + ${CARBON_LIBRARY} + ${COREFOUNDATION_LIBRARY} + ${COCOA_LIBRARY} + ${APP_SERVICES_LIBRARY} + ) + elseif (UNIX) + target_compile_options(${project} PRIVATE "-ggdb" "-Wall" "-Wno-long-long" "-pedantic" "-Wextra") + + if (OPENSPACE_WARNINGS_AS_ERRORS) + target_compile_options(${project} PRIVATE "-Werror") + endif () + endif () +endfunction () + + + +function (add_external_dependencies) + # Ghoul + add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul) + target_link_libraries(libOpenSpace Ghoul) + get_property(GHOUL_INCLUDE_DIR TARGET Ghoul PROPERTY INTERFACE_INCLUDE_DIRECTORIES) + target_include_directories(libOpenSpace PUBLIC ${GHOUL_INCLUDE_DIR}) + get_property(GHOUL_DEFINITIONS TARGET Ghoul PROPERTY INTERFACE_COMPILE_DEFINITIONS) + target_compile_definitions(libOpenSpace PUBLIC ${GHOUL_DEFINITIONS}) + set_property(TARGET Lua PROPERTY FOLDER "External") + set_property(TARGET lz4 PROPERTY FOLDER "External") + + # SGCT + set(SGCT_TEXT OFF CACHE BOOL "" FORCE) + set(SGCT_BUILD_CSHARP_PROJECTS OFF CACHE BOOL "" FORCE) + set(SGCT_LIGHT_ONLY ON CACHE BOOL "" FORCE) + set(SGCT_CUSTOMOUTPUTDIRS OFF CACHE BOOL "" FORCE) + + add_subdirectory(${OPENSPACE_EXT_DIR}/sgct) + target_include_directories(libOpenSpace SYSTEM PUBLIC ${OPENSPACE_EXT_DIR}/sgct/include) + target_link_libraries( + libOpenSpace + # sgct + sgct_light glew glfw png16_static quat tinyxml2static turbojpeg-static + vrpn + ${GLFW_LIBRARIES} + ) + + set_property(TARGET sgct_light PROPERTY FOLDER "External") + set_property(TARGET glew PROPERTY FOLDER "External/SGCT") + set_property(TARGET glfw PROPERTY FOLDER "External/SGCT") + set_property(TARGET png16_static PROPERTY FOLDER "External/SGCT") + set_property(TARGET quat PROPERTY FOLDER "External/SGCT") + set_property(TARGET simd PROPERTY FOLDER "External/SGCT") + set_property(TARGET tinyxml2static PROPERTY FOLDER "External/SGCT") + set_property(TARGET turbojpeg-static PROPERTY FOLDER "External/SGCT") + set_property(TARGET vrpn PROPERTY FOLDER "External/SGCT") + set_property(TARGET zlibstatic PROPERTY FOLDER "External/SGCT") + + if (UNIX AND (NOT APPLE)) + target_link_libraries(libOpenSpace Xcursor Xinerama X11) + endif () + + # Spice + add_subdirectory(${OPENSPACE_EXT_DIR}/spice) + target_link_libraries(libOpenSpace Spice) + get_property(SPICE_INCLUDE_DIR TARGET Spice PROPERTY INTERFACE_INCLUDE_DIRECTORIES) + target_include_directories(libOpenSpace PUBLIC ${SPICE_INCLUDE_DIR}) + set_property(TARGET Spice PROPERTY FOLDER "External") + + # Curl + if (WIN32) + set(CURL_ROOT_DIR "${OPENSPACE_EXT_DIR}/curl") + set(CURL_ROOT_DIR "${OPENSPACE_EXT_DIR}/curl" PARENT_SCOPE) + target_include_directories(libOpenSpace SYSTEM PUBLIC ${CURL_ROOT_DIR}/include) + target_link_libraries(libOpenSpace ${CURL_ROOT_DIR}/lib/libcurl_imp.lib) + target_compile_definitions(libOpenSpace PUBLIC "OPENSPACE_CURL_ENABLED" "CURL_STATICLIB") + else () + find_package(CURL) + if (CURL_FOUND) + target_include_directories(libOpenSpace SYSTEM PUBLIC ${CURL_INCLUDE_DIRS}) + target_link_libraries(libOpenSpace ${CURL_LIBRARIES}) + target_compile_definitions(libOpenSpace PUBLIC "OPENSPACE_CURL_ENABLED") + endif () + endif() + + # Qt + # Unfortunately, we have to set this value manually; sigh + # In the future, if the Qt version is updated, just add to this variable ---abock + if (APPLE) + set(CMAKE_PREFIX_PATH + "~/Qt/5.6/clang_64/lib/cmake" + "~/Qt/5.7/clang_64/lib/cmake" + PARENT_SCOPE + ) + endif () +endfunction () + + + +function (handle_applications) + set(applications "") + set(applications_link_to_openspace "") + + file(GLOB appDirs RELATIVE ${OPENSPACE_APPS_DIR} ${OPENSPACE_APPS_DIR}/*) + list(REMOVE_ITEM appDirs ".DS_Store") # Removing the .DS_Store present on Mac + + set(DEFAULT_APPLICATIONS + "OpenSpace" + "Launcher" + ) + mark_as_advanced(DEFAULT_APPLICATIONS) + + foreach (app ${appDirs}) + string(TOUPPER ${app} upper_app) + list (FIND DEFAULT_APPLICATIONS "${app}" _index) + if (${_index} GREATER -1) + # App is a default application + option(OPENSPACE_APPLICATION_${upper_app} "${app} Application" ON) + else () + option(OPENSPACE_APPLICATION_${upper_app} "${app} Application" OFF) + endif() + if (OPENSPACE_APPLICATION_${upper_app}) + unset(APPLICATION_NAME) + unset(APPLICATION_LINK_TO_OPENSPACE) + include(${OPENSPACE_APPS_DIR}/${app}/CMakeLists.txt) + set_compile_settings(${APPLICATION_NAME}) + + if (APPLICATION_LINK_TO_OPENSPACE) + get_property( + OPENSPACE_INCLUDE_DIR + TARGET libOpenSpace + PROPERTY INTERFACE_INCLUDE_DIRECTORIES + ) + target_include_directories(${APPLICATION_NAME} PUBLIC + "${OPENSPACE_BASE_DIR}" + ${OPENSPACE_INCLUDE_DIR} + ) + + get_property( + OPENSPACE_DEFINES + TARGET libOpenSpace + PROPERTY INTERFACE_COMPILE_DEFINITIONS + ) + target_compile_definitions(${APPLICATION_NAME} PUBLIC ${OPENSPACE_DEFINES}) + + target_link_libraries(${APPLICATION_NAME} Ghoul) + target_link_libraries(${APPLICATION_NAME} libOpenSpace) + + if (MSVC) + set_target_properties(${APPLICATION_NAME} PROPERTIES LINK_FLAGS + "/NODEFAULTLIB:LIBCMTD.lib /NODEFAULTLIB:LIBCMT.lib" + ) + endif () + + + if (WIN32) + ghl_copy_files( + ${APPLICATION_NAME} + "${CURL_ROOT_DIR}/lib/libcurl.dll" + "${CURL_ROOT_DIR}/lib/libeay32.dll" + "${CURL_ROOT_DIR}/lib/ssleay32.dll" + + ) + ghl_copy_shared_libraries(${APPLICATION_NAME} ${OPENSPACE_EXT_DIR}/ghoul) + endif () + endif () + + list(APPEND applications ${APPLICATION_NAME}) + list(APPEND applications_link_to_openspace ${APPLICATION_LINK_TO_OPENSPACE}) + unset(APPLICATION_NAME) + unset(APPLICATION_LINK_TO_OPENSPACE) + endif () + endforeach () + + + # option(OPENSPACE_APPLICATION_OPENSPACE "Main OpenSpace Application" ON) + # if (OPENSPACE_APPLICATION_OPENSPACE) + # include(${OPENSPACE_APPS_DIR}/OpenSpace/CMakeLists.txt) + # list(APPEND applications "OpenSpace") + # endif () + set(OPENSPACE_APPLICATIONS ${applications} PARENT_SCOPE) + set(OPENSPACE_APPLICATIONS_LINK_REQUEST ${applications_link_to_openspace} PARENT_SCOPE) + + message(STATUS "Applications:") + list(LENGTH applications len1) + math(EXPR len2 "${len1} - 1") + + foreach(val RANGE ${len2}) + list(GET applications ${val} val1) + list(GET applications_link_to_openspace ${val} val2) + message(STATUS "\t${val1} (Link: ${val2})") + endforeach() +endfunction() + + +function (handle_option_vld) + if (OPENSPACE_ENABLE_VLD) + target_compile_definitions(libOpenSpace PUBLIC "OPENSPACE_ENABLE_VLD") + target_link_libraries(libOpenSpace ${OPENSPACE_EXT_DIR}/vld/lib/vld.lib) + target_include_directories(libOpenSpace PUBLIC ${OPENSPACE_EXT_DIR}/vld) + + foreach (app ${OPENSPACE_APPLCATIONS}) + ghl_copy_files(${app} "${OPENSPACE_EXT_DIR}/vld/bin/vld_x64.dll") + endforeach () + endif () +endfunction () + + + +function (handle_option_tests) + if (OPENSPACE_HAVE_TESTS) + if (NOT TARGET gtest) + set(BUILD_GTEST ON CACHE BOOL "") + set(BUILD_GMOCK OFF CACHE BOOL "") + set(gtest_force_shared_crt ON CACHE BOOL "") + # set(BUILD_GMOCK OFF CACHE BOOL "") + # option(BUILD_GTEST "Builds the googletest subproject" CACHE ON) + # option(BUILD_GMOCK "Builds the googlemock subproject" CACHE OFF) + # option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." CACHE ON) + add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul/ext/googletest) + # add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul/ext/gtest) + set_property(TARGET gtest PROPERTY FOLDER "External") + set_property(TARGET gtest_main PROPERTY FOLDER "External") + endif () + + file(GLOB_RECURSE OPENSPACE_TEST_FILES ${OPENSPACE_BASE_DIR}/tests/*.inl) + + add_executable(OpenSpaceTest ${OPENSPACE_BASE_DIR}/tests/main.cpp ${OPENSPACE_TEST_FILES}) + target_include_directories(OpenSpaceTest PUBLIC + "${OPENSPACE_BASE_DIR}/include" + "${OPENSPACE_BASE_DIR}/tests" + "${OPENSPACE_EXT_DIR}/ghoul/ext/googletest/googletest/include" + ) + target_compile_definitions(OpenSpaceTest PUBLIC + "GHL_THROW_ON_ASSERT" + "GTEST_HAS_TR1_TUPLE=0" + ) + target_link_libraries(OpenSpaceTest gtest libOpenSpace) + + if (MSVC) + set_target_properties(OpenSpaceTest PROPERTIES LINK_FLAGS + "/NODEFAULTLIB:LIBCMTD.lib /NODEFAULTLIB:LIBCMT.lib" + ) + endif () + set_property(TARGET OpenSpaceTest PROPERTY FOLDER "Unit Tests") + set_property(TARGET OpenSpaceTest PROPERTY CXX_STANDARD 14) + set_property(TARGET OpenSpaceTest PROPERTY CXX_STANDARD_REQUIRED On) + endif (OPENSPACE_HAVE_TESTS) + if (TARGET GhoulTest) + if (NOT TARGET gtest) + set(BUILD_GTEST ON CACHE BOOL "") + set(BUILD_GMOCK OFF CACHE BOOL "") + set(gtest_force_shared_crt ON CACHE BOOL "") + # option(BUILD_GTEST "Builds the googletest subproject" CACHE ON) + # option(BUILD_GMOCK "Builds the googlemock subproject" CACHE OFF) + # option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." CACHE ON) + add_subdirectory(${OPENSPACE_EXT_DIR}/ghoul/ext/googletest) + endif () + + set_property(TARGET gtest PROPERTY FOLDER "External") + set_property(TARGET GhoulTest PROPERTY FOLDER "Unit Tests") + endif () +endfunction () + + + +function (handle_internal_modules) +# Get all modules in the correct order based on their dependencies + file(GLOB moduleDirs RELATIVE ${OPENSPACE_MODULE_DIR} ${OPENSPACE_MODULE_DIR}/*) + set(sortedModules ${moduleDirs}) + foreach (dir ${moduleDirs}) + if (IS_DIRECTORY ${OPENSPACE_MODULE_DIR}/${dir}) + set(defaultModule OFF) + if (EXISTS "${OPENSPACE_MODULE_DIR}/${dir}/include.cmake") + unset(OPENSPACE_DEPENDENCIES) + unset(EXTERNAL_LIBRAY) + unset(DEFAULT_MODULE) + include(${OPENSPACE_MODULE_DIR}/${dir}/include.cmake) + + if (DEFINED DEFAULT_MODULE) + set(defaultModule ${DEFAULT_MODULE}) + endif () + if (OPENSPACE_DEPENDENCIES) + foreach (dependency ${OPENSPACE_DEPENDENCIES}) + create_library_name(${dependency} library) + if (TARGET ${library}) + # already registered + list(REMOVE_ITEM OPENSPACE_DEPENDENCIES ${dependency}) + endif () + endforeach () + + list(APPEND OPENSPACE_DEPENDENCIES ${dir}) + list(FIND sortedModules ${dir} dir_index) + # if (NOT dir STREQUAL "base") + # list(INSERT OPENSPACE_DEPENDENCIES 0 "base") + # endif () + list(INSERT sortedModules ${dir_index} ${OPENSPACE_DEPENDENCIES}) + list(REMOVE_DUPLICATES sortedModules) + endif () + endif () + create_option_name(${dir} optionName) + option(${optionName} "Build ${dir} Module" ${defaultModule}) + # create_library_name(${module} ${library}) + else () + list(REMOVE_ITEM sortedModules ${dir}) + endif () + endforeach () + + # Automatically set dependent modules to ON + set(dir_list ${sortedModules}) + set(dll_list "") + list(REVERSE dir_list) + foreach (dir ${dir_list}) + create_option_name(${dir} optionName) + if (${optionName}) + if (EXISTS "${OPENSPACE_MODULE_DIR}/${dir}/include.cmake") + unset(OPENSPACE_DEPENDENCIES) + unset(EXTERNAL_LIBRAY) + unset(DEFAULT_MODULE) + include(${OPENSPACE_MODULE_DIR}/${dir}/include.cmake) + + if (OPENSPACE_DEPENDENCIES) + foreach (dependency ${OPENSPACE_DEPENDENCIES}) + create_option_name(${dependency} dependencyOptionName) + if (NOT ${dependencyOptionName}) + set(${dependencyOptionName} ON CACHE BOOL "ff" FORCE) + message(STATUS "${dependencyOptionName} was set to build, due to dependency towards ${optionName}") + endif () + endforeach () + endif () + endif () + endif () + endforeach () + + set(MODULE_HEADERS "") + set(MODULE_CLASSES "") + + message(STATUS "Included modules:") + foreach (module ${sortedModules}) + create_option_name(${module} optionName) + if (${optionName}) + message(STATUS "\t${module}") + endif () + endforeach () + + # Add subdirectories in the correct order + foreach (module ${sortedModules}) + create_option_name(${module} optionName) + if (${optionName}) + create_library_name(${module} libraryName) + add_subdirectory(${OPENSPACE_MODULE_DIR}/${module}) + + list(LENGTH OPENSPACE_APPLICATIONS len1) + math(EXPR len2 "${len1} - 1") + + foreach(val RANGE ${len2}) + list(GET OPENSPACE_APPLICATIONS ${val} val1) + list(GET OPENSPACE_APPLICATIONS_LINK_REQUEST ${val} val2) + if (${val2}) + target_link_libraries(${app} ${libraryName}) + endif () + endforeach() + + # Only link libOpenSpace against the library if it has been set STATIC + get_target_property(libType ${libraryName} TYPE) + if (NOT ${libType} STREQUAL "SHARED_LIBRARY") + target_link_libraries(libOpenSpace ${libraryName}) + endif() + + create_define_name(${module} defineName) + target_compile_definitions(libOpenSpace PUBLIC "${defineName}") + + # Create registration file + string(TOUPPER ${module} module_upper) + string(TOLOWER ${module} module_lower) + unset(MODULE_NAME) + unset(MODULE_PATH) + include(${CMAKE_BINARY_DIR}/modules/${module_lower}/modulename.cmake) + + list(APPEND MODULE_HEADERS + #"#ifdef REGISTRATION_OPENSPACE${module_upper}MODULE\n" + "#include <${MODULE_PATH}>\n" + #"#endif\n\n" + ) + list(APPEND MODULE_CLASSES " new ${MODULE_NAME},\n") + + if (EXTERNAL_LIBRARY) + foreach (library ${EXTERNAL_LIBRARY}) + get_filename_component(lib ${library} ABSOLUTE) + list(APPEND dll_list ${lib}) + endforeach() + endif () + endif () + endforeach () + + if (NOT "${MODULE_HEADERS}" STREQUAL "") + string(REPLACE ";" "" MODULE_HEADERS ${MODULE_HEADERS}) + endif () + + if (NOT "${MODULE_CLASSES}" STREQUAL "") + string(REPLACE ";" "" MODULE_CLASSES ${MODULE_CLASSES}) + endif () + + configure_file( + ${OPENSPACE_CMAKE_EXT_DIR}/module_registration.template + ${CMAKE_BINARY_DIR}/_generated/include/openspace/moduleregistration.h + ) + + list(REMOVE_DUPLICATES dll_list) + + if (WIN32) + foreach (application ${OPENSPACE_APPLICATIONS}) + foreach (dll ${dll_list}) + ghl_copy_files(${application} ${dll}) + endforeach () + endforeach () + endif () +endfunction () + + + +function (copy_dynamic_libraries) + if (WIN32) + ghl_copy_files(OpenSpace "${CURL_ROOT_DIR}/lib/libcurl.dll") + + # Copy DLLs needed by Ghoul into the executable directory + ghl_copy_shared_libraries(OpenSpace ${OPENSPACE_EXT_DIR}/ghoul) + + if (TARGET OpenSpaceTest) + ghl_copy_shared_libraries(OpenSpaceTest ${OPENSPACE_EXT_DIR}/ghoul) + endif () + endif () +endfunction ()