/***************************************************************************************** * * * 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 #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) , _scene(new Scene) , _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(); _syncEngine->addSyncable(_scriptEngine.get()); } 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)); // Set up asset loader and scene loader _engine->_assetLoader = std::make_unique( OsEng.scriptEngine().luaState(), "${ASSETS}", "${SYNC}"); _engine->_sceneLoader = std::make_unique(_engine->_assetLoader.get()); } 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->_moduleEngine->deinitialize(); _engine->_console->deinitialize(); _engine->_scriptEngine->deinitialize(); _engine->_scene = nullptr; 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); } ); if (scenePath != "") { // Run start up scripts try { runPreInitializationScripts(scenePath); } catch (const ghoul::RuntimeError& e) { LERRORC(e.component, e.message); } // Load the scene try { if (_scene) { _syncEngine->removeSyncables(_timeManager->getSyncables()); _syncEngine->removeSyncables(_renderEngine->getSyncables()); _renderEngine->setScene(nullptr); _renderEngine->setCamera(nullptr); _interactionHandler->setCamera(nullptr); } _scene = std::make_unique(); _renderEngine->setScene(_scene.get()); _sceneLoader->loadScene(_scene.get(), 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; } } // Initialize the RenderEngine _renderEngine->setGlobalBlackOutFactor(0.0); _renderEngine->startFading(1, 3.0); if (_scene) { _renderEngine->setCamera(_scene->camera()); _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()); 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(); Camera* camera = _renderEngine->camera(); if (camera) { _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