mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-08 12:39:49 -06:00
759 lines
24 KiB
C++
759 lines
24 KiB
C++
/*****************************************************************************************
|
|
* *
|
|
* OpenSpace *
|
|
* *
|
|
* Copyright (c) 2014-2018 *
|
|
* *
|
|
* 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 <openspace/engine/configurationmanager.h>
|
|
#include <openspace/engine/openspaceengine.h>
|
|
#include <openspace/engine/wrapper/sgctwindowwrapper.h>
|
|
#include <openspace/util/keys.h>
|
|
|
|
#include <ghoul/filesystem/filesystem.h>
|
|
#include <ghoul/logging/logmanager.h>
|
|
#include <ghoul/misc/assert.h>
|
|
#include <ghoul/misc/boolean.h>
|
|
|
|
#include <sgct.h>
|
|
#include <chrono>
|
|
#include <ctime>
|
|
|
|
#ifdef WIN32
|
|
|
|
#include <openspace/openspace.h>
|
|
|
|
#include <ghoul/filesystem/filesystem.h>
|
|
#include <ghoul/misc/stacktrace.h>
|
|
|
|
#include <ghoul/fmt.h>
|
|
|
|
#include <Windows.h>
|
|
#include <shellapi.h>
|
|
#include <shlobj.h>
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning (push)
|
|
#pragma warning (disable : 4091)
|
|
#include <dbghelp.h>
|
|
#pragma warning (pop)
|
|
#endif // _MSC_VER
|
|
|
|
#endif // WIN32
|
|
|
|
#ifdef OPENVR_SUPPORT
|
|
#include <SGCTOpenVR.h>
|
|
#endif // OPENVR_SUPPORT
|
|
|
|
#ifdef OPENSPACE_HAS_SPOUT
|
|
#include "SpoutLibrary.h"
|
|
#endif // OPENSPACE_HAS_SPOUT
|
|
|
|
|
|
#define DEVELOPER_MODE
|
|
|
|
namespace {
|
|
|
|
constexpr const char* _loggerCat = "main";
|
|
sgct::Engine* SgctEngine;
|
|
|
|
constexpr const char* OpenVRTag = "OpenVR";
|
|
constexpr 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<std::string> 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(fmt::format("Creating dump file: {}", dumpFile));
|
|
|
|
HANDLE hDumpFile = CreateFileA(
|
|
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<SpoutWindow> SpoutWindows;
|
|
|
|
#endif // OPENSPACE_HAS_SPOUT
|
|
|
|
|
|
std::pair<int, int> 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
|
|
}
|
|
|
|
std::string k = openspace::ConfigurationManager::KeyScreenshotUseDate;
|
|
std::string screenshotPath = "${SCREENSHOTS}";
|
|
std::string screenshotNames = "OpenSpace";
|
|
if (OsEng.configurationManager().hasKey(k)) {
|
|
std::time_t now = std::time(nullptr);
|
|
std::tm* nowTime = std::localtime(&now);
|
|
char mbstr[100];
|
|
strftime(mbstr, sizeof(mbstr), "%Y-%m-%d-%H-%M", nowTime);
|
|
screenshotPath += "/" + std::string(mbstr);
|
|
}
|
|
|
|
FileSys.registerPathToken("${THIS_SCREENSHOT_PATH}", screenshotPath);
|
|
|
|
for (size_t i = 0; i < nWindows; ++i) {
|
|
sgct_core::ScreenCapture* cpt0 =
|
|
SgctEngine->getWindowPtr(i)->getScreenCapturePointer(0);
|
|
sgct_core::ScreenCapture* cpt1 =
|
|
SgctEngine->getWindowPtr(i)->getScreenCapturePointer(1);
|
|
|
|
if (cpt0) {
|
|
cpt0->setPathAndFileName(
|
|
absPath(screenshotPath),
|
|
screenshotNames
|
|
);
|
|
}
|
|
|
|
if (cpt1) {
|
|
cpt1->setPathAndFileName(
|
|
screenshotPath,
|
|
screenshotNames
|
|
);
|
|
}
|
|
}
|
|
|
|
LTRACE("main::mainInitFunc(end)");
|
|
}
|
|
|
|
void mainPreSyncFunc() {
|
|
LTRACE("main::mainPreSyncFunc(begin)");
|
|
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
|
|
|
|
try {
|
|
OsEng.render(
|
|
SgctEngine->getModelMatrix(),
|
|
viewMatrix,
|
|
projectionMatrix
|
|
);
|
|
}
|
|
catch (const ghoul::RuntimeError& e) {
|
|
LERRORC(e.component, e.message);
|
|
}
|
|
LTRACE("main::mainRenderFunc(end)");
|
|
}
|
|
|
|
void mainDraw2DFunc() {
|
|
LTRACE("main::mainDraw2DFunc(begin)");
|
|
|
|
try {
|
|
OsEng.drawOverlays();
|
|
}
|
|
catch (const ghoul::RuntimeError& e) {
|
|
LERRORC(e.component, e.message);
|
|
}
|
|
|
|
// SGCT gets angry if we change this in our function
|
|
glEnable(GL_BLEND);
|
|
glDisable(GL_CULL_FACE);
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
LTRACE("main::mainDraw2DFunc(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 posX, double posY) {
|
|
LTRACE("main::mainMouseScrollCallback(begin");
|
|
if (SgctEngine->isMaster()) {
|
|
OsEng.mouseScrollWheelCallback(posX, 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<int, int> 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<std::string> sgctArguments;
|
|
bool requestQuit = false;
|
|
try {
|
|
openspace::OpenSpaceEngine::create(
|
|
argc, argv,
|
|
std::make_unique<openspace::SGCTWindowWrapper>(),
|
|
sgctArguments,
|
|
requestQuit
|
|
);
|
|
}
|
|
catch (const ghoul::RuntimeError& e) {
|
|
// Write out all of the information about the exception and flush the logs
|
|
LFATALC(e.component, e.message);
|
|
if (ghoul::logging::LogManager::isInitialized()) {
|
|
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());
|
|
if (ghoul::logging::LogManager::isInitialized()) {
|
|
LogMgr.flushLogs();
|
|
}
|
|
return EXIT_FAILURE;
|
|
}
|
|
catch (...) {
|
|
LFATALC("Exception", "Unknown exception");
|
|
if (ghoul::logging::LogManager::isInitialized()) {
|
|
LogMgr.flushLogs();
|
|
}
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (requestQuit) {
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
LINFO(fmt::format(
|
|
"Detected OpenGL version: {}.{}", glVersion.first, glVersion.second
|
|
));
|
|
|
|
// Create sgct engine c arguments
|
|
int newArgc = static_cast<int>(sgctArguments.size());
|
|
|
|
char** newArgv = new char*[newArgc];
|
|
for (int i = 0; i < newArgc; ++i) {
|
|
newArgv[i] = const_cast<char*>(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->setDraw2DFunction(mainDraw2DFunc);
|
|
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<std::pair<int, int>, 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 },
|
|
{ { 4, 6 }, sgct::Engine::RunMode::OpenGL_4_6_Core_Profile }
|
|
};
|
|
ghoul_assert(
|
|
versionMapping.find(glVersion) != versionMapping.end(),
|
|
"Unknown OpenGL version. Missing statement in version mapping map"
|
|
);
|
|
|
|
BooleanType(IsInitialized);
|
|
auto cleanup = [&](IsInitialized isInitialized){
|
|
if (isInitialized) {
|
|
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]);
|
|
|
|
// Do not print message if slaves are waiting for the master
|
|
// Only timeout after 15 minutes
|
|
SgctEngine->setSyncParameters(false, 15.f * 60.f);
|
|
|
|
if (!initSuccess) {
|
|
LFATAL("Initializing failed");
|
|
cleanup(IsInitialized::No);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
// Main loop
|
|
LINFO("Starting rendering loop");
|
|
SgctEngine->render();
|
|
LINFO("Ending rendering loop");
|
|
|
|
cleanup(IsInitialized::Yes);
|
|
|
|
// 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
|
|
}
|