Files
OpenSpace/src/engine/openspaceengine.cpp

1715 lines
60 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/openspaceengine.h>
#include <openspace/openspace.h>
#include <openspace/documentation/core_registration.h>
#include <openspace/documentation/documentationengine.h>
#include <openspace/engine/configuration.h>
#include <openspace/engine/downloadmanager.h>
#include <openspace/engine/logfactory.h>
#include <openspace/engine/moduleengine.h>
#include <openspace/engine/syncengine.h>
#include <openspace/engine/virtualpropertymanager.h>
#include <openspace/engine/wrapper/windowwrapper.h>
#include <openspace/interaction/keybindingmanager.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/network/networkengine.h>
#include <openspace/network/parallelpeer.h>
#include <openspace/performance/performancemeasurement.h>
#include <openspace/rendering/dashboard.h>
#include <openspace/rendering/dashboarditem.h>
#include <openspace/rendering/loadingscreen.h>
#include <openspace/rendering/luaconsole.h>
#include <openspace/rendering/renderable.h>
#include <openspace/scene/assetmanager.h>
#include <openspace/scene/assetloader.h>
#include <openspace/scene/scene.h>
#include <openspace/scene/rotation.h>
#include <openspace/scene/scale.h>
#include <openspace/scene/timeframe.h>
#include <openspace/scene/lightsource.h>
#include <openspace/scene/sceneinitializer.h>
#include <openspace/scene/translation.h>
#include <openspace/scripting/scriptscheduler.h>
#include <openspace/scripting/scriptengine.h>
#include <openspace/util/camera.h>
#include <openspace/util/factorymanager.h>
#include <openspace/util/spicemanager.h>
#include <openspace/util/task.h>
#include <openspace/util/timemanager.h>
#include <openspace/util/transformationmanager.h>
#include <ghoul/ghoul.h>
#include <ghoul/cmdparser/commandlineparser.h>
#include <ghoul/cmdparser/singlecommand.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/font/fontmanager.h>
#include <ghoul/font/fontrenderer.h>
#include <ghoul/logging/consolelog.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/logging/visualstudiooutputlog.h>
#include <ghoul/opengl/debugcontext.h>
#include <ghoul/opengl/shaderpreprocessor.h>
#include <ghoul/opengl/texture.h>
#include <ghoul/systemcapabilities/generalcapabilitiescomponent.h>
#include <ghoul/systemcapabilities/openglcapabilitiescomponent.h>
#include <glbinding/callbacks.h>
#include <numeric>
#if defined(_MSC_VER) && defined(OPENSPACE_ENABLE_VLD)
#include <vld.h>
#endif
#ifdef __APPLE__
#include <openspace/interaction/touchbar.h>
#endif // __APPLE__
#include "openspaceengine_lua.inl"
using namespace openspace::scripting;
using namespace ghoul::filesystem;
using namespace ghoul::logging;
using namespace ghoul::cmdparser;
namespace {
constexpr const char* _loggerCat = "OpenSpaceEngine";
constexpr const char* SgctConfigArgumentCommand = "-config";
constexpr const int CacheVersion = 1;
const glm::ivec3 FontAtlasSize{ 1536, 1536, 1 };
struct {
std::string configurationName;
std::string sgctConfigurationName;
std::string sceneName;
std::string cacheFolder;
std::string configurationOverwrite;
} commandlineArgumentPlaceholders;
constexpr const openspace::properties::Property::PropertyInfo VersionInfo = {
"VersionInfo",
"Version Information",
"This value contains the full string identifying this OpenSpace Version"
};
constexpr const openspace::properties::Property::PropertyInfo SourceControlInfo = {
"SCMInfo",
"Source Control Management Information",
"This value contains information from the SCM, such as commit hash and branch"
};
} // namespace
namespace openspace {
namespace properties { class Property; }
class Scene;
OpenSpaceEngine* OpenSpaceEngine::_engine = nullptr;
OpenSpaceEngine::OpenSpaceEngine(std::string programName,
std::unique_ptr<WindowWrapper> windowWrapper)
: _configuration(new Configuration)
, _scene(nullptr)
, _dashboard(new Dashboard)
, _downloadManager(std::make_unique<DownloadManager>())
, _console(new LuaConsole)
, _moduleEngine(new ModuleEngine)
, _networkEngine(new NetworkEngine)
, _parallelPeer(new ParallelPeer)
, _renderEngine(new RenderEngine)
, _syncEngine(std::make_unique<SyncEngine>(4096))
, _timeManager(new TimeManager)
, _windowWrapper(std::move(windowWrapper))
, _commandlineParser(new ghoul::cmdparser::CommandlineParser(
std::move(programName),
ghoul::cmdparser::CommandlineParser::AllowUnknownCommands::Yes
))
, _navigationHandler(new interaction::NavigationHandler)
, _keyBindingManager(new interaction::KeyBindingManager)
, _scriptEngine(new scripting::ScriptEngine)
, _scriptScheduler(new scripting::ScriptScheduler)
, _virtualPropertyManager(new VirtualPropertyManager)
, _rootPropertyOwner(new properties::PropertyOwner({ "" }))
, _loadingScreen(nullptr)
, _versionInformation{
properties::StringProperty(VersionInfo, OPENSPACE_VERSION_STRING_FULL),
properties::StringProperty(SourceControlInfo, OPENSPACE_GIT_FULL)
}
{
_rootPropertyOwner->addPropertySubOwner(_moduleEngine.get());
_navigationHandler->setPropertyOwner(_rootPropertyOwner.get());
// New property subowners also have to be added to the ImGuiModule callback!
_rootPropertyOwner->addPropertySubOwner(_navigationHandler.get());
_rootPropertyOwner->addPropertySubOwner(_timeManager.get());
_rootPropertyOwner->addPropertySubOwner(_renderEngine.get());
_rootPropertyOwner->addPropertySubOwner(_renderEngine->screenSpaceOwner());
// The virtual property manager is not part of the rootProperty owner since it cannot
// have an identifier or the "regex as identifier" trick would not work
//_rootPropertyOwner->addPropertySubOwner(_virtualPropertyManager.get());
if (_windowWrapper) {
_rootPropertyOwner->addPropertySubOwner(_windowWrapper.get());
}
_rootPropertyOwner->addPropertySubOwner(_parallelPeer.get());
_rootPropertyOwner->addPropertySubOwner(_console.get());
_rootPropertyOwner->addPropertySubOwner(_dashboard.get());
_versionInformation.versionString.setReadOnly(true);
_rootPropertyOwner->addProperty(_versionInformation.versionString);
_versionInformation.sourceControlInformation.setReadOnly(true);
_rootPropertyOwner->addProperty(_versionInformation.sourceControlInformation);
FactoryManager::initialize();
FactoryManager::ref().addFactory(
std::make_unique<ghoul::TemplateFactory<Renderable>>(),
"Renderable"
);
FactoryManager::ref().addFactory(
std::make_unique<ghoul::TemplateFactory<Translation>>(),
"Translation"
);
FactoryManager::ref().addFactory(
std::make_unique<ghoul::TemplateFactory<Rotation>>(),
"Rotation"
);
FactoryManager::ref().addFactory(
std::make_unique<ghoul::TemplateFactory<Scale>>(),
"Scale"
);
FactoryManager::ref().addFactory(
std::make_unique<ghoul::TemplateFactory<TimeFrame>>(),
"TimeFrame"
);
FactoryManager::ref().addFactory(
std::make_unique<ghoul::TemplateFactory<LightSource>>(),
"LightSource"
);
FactoryManager::ref().addFactory(
std::make_unique<ghoul::TemplateFactory<Task>>(),
"Task"
);
FactoryManager::ref().addFactory(
std::make_unique<ghoul::TemplateFactory<ResourceSynchronization>>(),
"ResourceSynchronization"
);
FactoryManager::ref().addFactory(
std::make_unique<ghoul::TemplateFactory<DashboardItem>>(),
"DashboardItem"
);
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> windowWrapper,
std::vector<std::string>& sgctArguments,
bool& requestClose, bool consoleLog)
{
ghoul_assert(!_engine, "OpenSpaceEngine was already created");
requestClose = false;
LDEBUG("Initialize FileSystem");
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
);
if (consoleLog) {
LogMgr.addLog(std::make_unique<ConsoleLog>());
}
// 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<std::string> args(argv, argv + argc);
std::vector<std::string> arguments =
_engine->_commandlineParser->setCommandLine(args);
bool showHelp = _engine->_commandlineParser->execute();
if (showHelp) {
_engine->_commandlineParser->displayHelp(std::cout);
requestClose = true;
return;
}
sgctArguments = std::move(arguments);
// Find configuration
std::string configurationFilePath = commandlineArgumentPlaceholders.configurationName;
if (configurationFilePath.empty()) {
LDEBUG("Finding configuration");
configurationFilePath = findConfiguration();
}
configurationFilePath = absPath(configurationFilePath);
if (!FileSys.fileExists(configurationFilePath)) {
throw ghoul::FileNotFoundError(
"Configuration file '" + configurationFilePath + "'"
);
}
LINFO(fmt::format("Configuration Path: '{}'", configurationFilePath));
// Loading configuration from disk
LDEBUG("Loading configuration from disk");
try {
*_engine->_configuration = loadConfigurationFromFile(configurationFilePath);
// If the user requested a commandline-based configuation script that should
// overwrite some of the values, this is the time to do it
if (!commandlineArgumentPlaceholders.configurationOverwrite.empty()) {
LDEBUG("Executing Lua script passed through the commandline:");
LDEBUG(commandlineArgumentPlaceholders.configurationOverwrite);
ghoul::lua::runScript(
_engine->_configuration->state,
commandlineArgumentPlaceholders.configurationOverwrite
);
parseLuaState(*_engine->_configuration);
}
}
catch (const documentation::SpecificationError& e) {
LFATAL(fmt::format(
"Loading of configuration file '{}' failed", configurationFilePath
));
for (const documentation::TestResult::Offense& o : e.result.offenses) {
LERRORC(o.offender, ghoul::to_string(o.reason));
}
for (const documentation::TestResult::Warning& w : e.result.warnings) {
LWARNINGC(w.offender, ghoul::to_string(w.reason));
}
throw;
}
catch (const ghoul::RuntimeError& e) {
LFATAL(fmt::format(
"Loading of configuration file '{}' failed", configurationFilePath
));
LFATALC(e.component, e.message);
throw;
}
// Registering Path tokens. If the BASE path is set, it is the only one that will
// overwrite the default path of the cfg directory
for (const std::pair<std::string, std::string>& path :
_engine->_configuration->pathTokens)
{
std::string fullKey =
FileSystem::TokenOpeningBraces + path.first + FileSystem::TokenClosingBraces;
LDEBUGC(
"ConfigurationManager",
fmt::format("Registering path {}: {}", fullKey, path.second)
);
bool override = (fullKey == "${BASE}");
if (override) {
LINFOC(
"ConfigurationManager",
fmt::format("Overriding base path with '{}'", path.second)
);
}
using Override = ghoul::filesystem::FileSystem::Override;
FileSys.registerPathToken(
std::move(fullKey),
std::move(path.second),
override ? Override::Yes : Override::No
);
}
const bool hasCacheCommandline = !commandlineArgumentPlaceholders.cacheFolder.empty();
const bool hasCacheConfig = _engine->_configuration->usePerSceneCache;
std::string cacheFolder = absPath("${CACHE}");
if (hasCacheCommandline || hasCacheConfig) {
if (hasCacheCommandline) {
cacheFolder = commandlineArgumentPlaceholders.cacheFolder;
}
if (hasCacheConfig) {
std::string scene = _engine->_configuration->asset;
cacheFolder += "-" + ghoul::filesystem::File(scene).baseName();
}
LINFO(fmt::format("Old cache: {}", absPath("${CACHE}")));
LINFO(fmt::format("New cache: {}", cacheFolder));
FileSys.registerPathToken(
"${CACHE}",
cacheFolder,
ghoul::filesystem::FileSystem::Override::Yes
);
}
// Create directories that doesn't exist
for (const std::string& token : FileSys.tokens()) {
if (!FileSys.directoryExists(token)) {
std::string p = absPath(token);
FileSys.createDirectory(p, ghoul::filesystem::FileSystem::Recursive::Yes);
}
}
// Initialize the requested logs from the configuration file
_engine->configureLogging(consoleLog);
LINFOC("OpenSpace Version", std::string(OPENSPACE_VERSION_STRING_FULL));
LINFOC("Commit", std::string(OPENSPACE_GIT_FULL));
// Register modules
_engine->_moduleEngine->initialize(_engine->_configuration->moduleConfigurations);
// 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);
}
}
DocEng.addDocumentation(Configuration::Documentation);
// Create the cachemanager
try {
FileSys.createCacheManager(cacheFolder, CacheVersion);
}
catch (const ghoul::RuntimeError& e) {
LFATAL("Could not create Cache Manager");
LFATALC(e.component, e.message);
}
// Register the provided shader directories
ghoul::opengl::ShaderPreprocessor::addIncludePath(absPath("${SHADERS}"));
// Determining SGCT configuration file
LDEBUG("Determining SGCT configuration file");
std::string sgctConfigurationPath = _engine->_configuration->windowConfiguration;
LDEBUG(fmt::format("SGCT Configuration file: {}", sgctConfigurationPath));
if (!commandlineArgumentPlaceholders.sgctConfigurationName.empty()) {
LDEBUG(fmt::format(
"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
std::unique_ptr<SynchronizationWatcher> w =
std::make_unique<SynchronizationWatcher>();
SynchronizationWatcher* rawWatcher = w.get();
_engine->_assetManager = std::make_unique<AssetManager>(
std::make_unique<AssetLoader>(
*OsEng.scriptEngine().luaState(),
rawWatcher,
FileSys.absPath("${ASSETS}")
),
std::move(w)
);
}
void OpenSpaceEngine::destroy() {
if (_engine->parallelPeer().status() != ParallelConnection::Status::Disconnected) {
_engine->parallelPeer().disconnect();
}
_engine->_syncEngine->removeSyncables(_engine->timeManager().getSyncables());
if (_engine->_scene && _engine->_scene->camera()) {
_engine->_syncEngine->removeSyncables(_engine->_scene->camera()->getSyncables());
}
_engine->_renderEngine->deinitializeGL();
_engine->_moduleEngine->deinitializeGL();
_engine->_moduleEngine->deinitialize();
_engine->_console->deinitialize();
_engine->_scriptEngine->deinitialize();
delete _engine;
_engine = nullptr;
FactoryManager::deinitialize();
TransformationManager::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 doesn't have to see old buffer contents left on 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<ghoul::systemcapabilities::GeneralCapabilitiesComponent>()
);
SysCap.addComponent(
std::make_unique<ghoul::systemcapabilities::OpenGLCapabilitiesComponent>()
);
// @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 = ghoul::from_string<Verbosity>(
_engine->_configuration->logging.capabilitiesVerbosity
);
SysCap.logCapabilities(verbosity);
// Check the required OpenGL versions of the registered modules
ghoul::systemcapabilities::Version version =
_engine->_moduleEngine->requiredOpenGLVersion();
LINFO(fmt::format("Required OpenGL version: {}", ghoul::to_string(version)));
if (OpenGLCap.openGLVersion() < version) {
throw ghoul::RuntimeError(
"An included module required a higher OpenGL version than is supported on "
"this system",
"OpenSpaceEngine"
);
}
{
// Check the available OpenGL extensions against the required extensions
using OCC = ghoul::systemcapabilities::OpenGLCapabilitiesComponent;
for (OpenSpaceModule* m : _engine->_moduleEngine->modules()) {
for (const std::string& ext : m->requiredOpenGLExtensions()) {
if (!SysCap.component<OCC>().isExtensionSupported(ext)) {
LFATAL(fmt::format(
"Module {} required OpenGL extension {} which is not available "
"on this system. Some functionality related to this module will "
"probably not work.", m->guiName(), ext
));
}
}
}
}
// Register Lua script functions
LDEBUG("Registering Lua libraries");
registerCoreClasses(*_scriptEngine);
_scriptEngine->addLibrary(_engine->_assetManager->luaLibrary());
for (OpenSpaceModule* module : _moduleEngine->modules()) {
_scriptEngine->addLibrary(module->luaLibrary());
for (scripting::LuaLibrary& l : module->luaLibraries()) {
_scriptEngine->addLibrary(l);
}
}
scriptEngine().initialize();
writeStaticDocumentation();
_shutdown.waitTime = _engine->_configuration->shutdownCountdown;
if (!commandlineArgumentPlaceholders.sceneName.empty()) {
_engine->_configuration->asset = commandlineArgumentPlaceholders.sceneName;
}
// Initialize the NavigationHandler
_navigationHandler->initialize();
// Load a light and a monospaced font
loadFonts();
_renderEngine->initialize();
_loadingScreen = std::make_unique<LoadingScreen>(
LoadingScreen::ShowMessage(_configuration->loadingScreen.isShowingMessages),
LoadingScreen::ShowNodeNames(_configuration->loadingScreen.isShowingNodeNames),
LoadingScreen::ShowProgressbar(_configuration->loadingScreen.isShowingProgressbar)
);
_loadingScreen->render();
for (const std::function<void()>& func : _moduleCallbacks.initialize) {
func();
}
_engine->_assetManager->initialize();
scheduleLoadSingleAsset(_engine->_configuration->asset);
LTRACE("OpenSpaceEngine::initialize(end)");
}
void OpenSpaceEngine::scheduleLoadSingleAsset(std::string assetPath) {
_hasScheduledAssetLoading = true;
_scheduledAssetPathToLoad = std::move(assetPath);
}
void OpenSpaceEngine::loadSingleAsset(const std::string& assetPath) {
LTRACE("OpenSpaceEngine::loadSingleAsset(begin)");
windowWrapper().setBarrier(false);
windowWrapper().setSynchronization(false);
defer {
windowWrapper().setSynchronization(true);
windowWrapper().setBarrier(true);
};
if (assetPath.empty()) {
return;
}
if (_scene) {
_syncEngine->removeSyncables(_timeManager->getSyncables());
if (_scene && _scene->camera()) {
_syncEngine->removeSyncables(_scene->camera()->getSyncables());
}
_renderEngine->setScene(nullptr);
_renderEngine->setCamera(nullptr);
_navigationHandler->setCamera(nullptr);
_scene->clear();
_rootPropertyOwner->removePropertySubOwner(_scene.get());
}
std::unique_ptr<SceneInitializer> sceneInitializer;
if (_configuration->useMultithreadedInitialization) {
unsigned int nAvailableThreads = std::thread::hardware_concurrency();
unsigned int nThreads = nAvailableThreads == 0 ? 2 : nAvailableThreads - 1;
sceneInitializer = std::make_unique<MultiThreadedSceneInitializer>(nThreads);
} else {
sceneInitializer = std::make_unique<SingleThreadedSceneInitializer>();
}
_scene = std::make_unique<Scene>(std::move(sceneInitializer));
_rootPropertyOwner->addPropertySubOwner(_scene.get());
_scene->setCamera(std::make_unique<Camera>());
Camera* camera = _scene->camera();
camera->setParent(_scene->root());
_renderEngine->setCamera(camera);
_navigationHandler->setCamera(camera);
_navigationHandler->setFocusNode(camera->parent());
_renderEngine->setScene(_scene.get());
_assetManager->removeAll();
_assetManager->add(assetPath);
_loadingScreen->setPhase(LoadingScreen::Phase::Construction);
_loadingScreen->postMessage("Loading assets");
_assetManager->update();
_loadingScreen->setPhase(LoadingScreen::Phase::Synchronization);
_loadingScreen->postMessage("Synchronizing assets");
std::vector<std::shared_ptr<const Asset>> allAssets =
_assetManager->rootAsset()->subTreeAssets();
std::unordered_set<std::shared_ptr<ResourceSynchronization>> resourceSyncs;
for (const std::shared_ptr<const Asset>& a : allAssets) {
std::vector<std::shared_ptr<ResourceSynchronization>> syncs =
a->ownSynchronizations();
for (const std::shared_ptr<ResourceSynchronization>& s : syncs) {
if (s->state() == ResourceSynchronization::State::Syncing) {
resourceSyncs.insert(s);
_loadingScreen->updateItem(
s->name(),
s->name(),
LoadingScreen::ItemStatus::Started,
s->progress()
);
}
}
}
_loadingScreen->setItemNumber(static_cast<int>(resourceSyncs.size()));
bool loading = true;
while (loading) {
_loadingScreen->render();
_assetManager->update();
loading = false;
auto it = resourceSyncs.begin();
while (it != resourceSyncs.end()) {
if ((*it)->state() == ResourceSynchronization::State::Syncing) {
loading = true;
_loadingScreen->updateItem(
(*it)->name(),
(*it)->name(),
LoadingScreen::ItemStatus::Started,
(*it)->progress()
);
++it;
} else {
_loadingScreen->tickItem();
_loadingScreen->updateItem(
(*it)->name(),
(*it)->name(),
LoadingScreen::ItemStatus::Finished,
1.f
);
it = resourceSyncs.erase(it);
}
}
}
_loadingScreen->setPhase(LoadingScreen::Phase::Initialization);
_loadingScreen->postMessage("Initializing scene");
while (_scene->isInitializing()) {
_loadingScreen->render();
}
_loadingScreen->postMessage("Initializing OpenGL");
_loadingScreen->finalize();
_renderEngine->updateScene();
_renderEngine->setGlobalBlackOutFactor(0.f);
_renderEngine->startFading(1, 3.f);
_syncEngine->addSyncables(_timeManager->getSyncables());
if (_scene && _scene->camera()) {
_syncEngine->addSyncables(_scene->camera()->getSyncables());
}
#ifdef __APPLE__
showTouchbar();
#endif // APPLE
runGlobalCustomizationScripts();
writeSceneDocumentation();
LTRACE("OpenSpaceEngine::loadSingleAsset(end)");
}
void OpenSpaceEngine::deinitialize() {
LTRACE("OpenSpaceEngine::deinitialize(begin)");
for (const std::function<void()>& func : _engine->_moduleCallbacks.deinitializeGL) {
func();
}
for (const std::function<void()>& func : _engine->_moduleCallbacks.deinitialize) {
func();
}
_engine->assetManager().deinitialize();
_engine->_scene = nullptr;
_navigationHandler->deinitialize();
_renderEngine->deinitialize();
LTRACE("OpenSpaceEngine::deinitialize(end)");
}
void OpenSpaceEngine::writeStaticDocumentation() {
// If a LuaDocumentationFile was specified, generate it now
if (!_configuration->documentation.lua.empty()) {
_scriptEngine->writeDocumentation(absPath(_configuration->documentation.lua));
}
// If a general documentation was specified, generate it now
if (!_configuration->documentation.documentation.empty()) {
DocEng.writeDocumentation(absPath(_configuration->documentation.documentation));
}
if (!_configuration->documentation.factory.empty()) {
FactoryManager::ref().writeDocumentation(
absPath(_configuration->documentation.factory)
);
}
}
void OpenSpaceEngine::gatherCommandlineArguments() {
commandlineArgumentPlaceholders.configurationName = "";
_commandlineParser->addCommand(std::make_unique<SingleCommand<std::string>>(
commandlineArgumentPlaceholders.configurationName, "--config", "-c",
"Provides the path to the OpenSpace configuration file."
));
commandlineArgumentPlaceholders.sgctConfigurationName = "";
_commandlineParser->addCommand(std::make_unique<SingleCommand<std::string>>(
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<SingleCommand<std::string>>(
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<SingleCommand<std::string>>(
commandlineArgumentPlaceholders.cacheFolder, "--cacheDir", "", "Provides the "
"path to a cache file, overriding the value set in the OpenSpace configuration "
"file."
));
commandlineArgumentPlaceholders.configurationOverwrite = "";
_commandlineParser->addCommand(std::make_unique<SingleCommand<std::string>>(
commandlineArgumentPlaceholders.configurationOverwrite, "--lua", "-l",
"Provides the ability to pass arbitrary Lua code to the application that will be "
"evaluated after the configuration file has been loaded but before the other "
"commandline arguments are triggered. This can be used to manipulate the "
"configuration file without editing the file on disk, for example in a "
"planetarium environment. Please not that the Lua script must not contain any - "
"or they will be interpreted as a new command. Similar, in Bash, ${...} will be "
"evaluated before it is passed to OpenSpace."
));
}
void OpenSpaceEngine::runGlobalCustomizationScripts() {
LINFO("Running Global initialization scripts");
ghoul::lua::LuaState state;
OsEng.scriptEngine().initializeLuaState(state);
for (const std::string& script : _configuration->globalCustomizationScripts) {
std::string s = absPath(script);
if (FileSys.fileExists(s)) {
try {
LINFO(fmt::format("Running global customization script: {}", s));
ghoul::lua::runScriptFile(state, s);
} catch (const ghoul::RuntimeError& e) {
LERRORC(e.component, e.message);
}
}
else {
LDEBUG(fmt::format("Ignoring non-existing script file: {}", s));
}
}
}
void OpenSpaceEngine::loadFonts() {
_fontManager = std::make_unique<ghoul::fontrendering::FontManager>(FontAtlasSize);
for (const std::pair<std::string, std::string>& font : _configuration->fonts) {
std::string key = font.first;
std::string fontName = absPath(font.second);
if (!FileSys.fileExists(fontName)) {
LERROR(fmt::format("Could not find font '{}' for key '{}'", fontName, key));
continue;
}
LDEBUG(fmt::format("Registering font '{}' with key '{}'", fontName, key));
bool success = _fontManager->registerFontPath(key, fontName);
if (!success) {
LERROR(fmt::format(
"Error registering font '{}' with key '{}'", fontName, key
));
}
}
try {
bool initSuccess = ghoul::fontrendering::FontRenderer::initialize();
if (!initSuccess) {
LERROR("Error initializing default font renderer");
}
}
catch (const ghoul::RuntimeError& err) {
LERRORC(err.component, err.message);
}
}
void OpenSpaceEngine::configureLogging(bool consoleLog) {
// We previously initialized the LogManager with a console log to provide some logging
// until we know which logs should be added
LogManager::deinitialize();
LogLevel level = ghoul::logging::levelFromString(_configuration->logging.level);
bool immediateFlush = _configuration->logging.forceImmediateFlush;
using ImmediateFlush = ghoul::logging::LogManager::ImmediateFlush;
LogManager::initialize(
level,
ImmediateFlush(immediateFlush)
);
if (consoleLog) {
LogMgr.addLog(std::make_unique<ConsoleLog>());
}
for (const ghoul::Dictionary& log : _configuration->logging.logs) {
try {
LogMgr.addLog(createLog(log));
}
catch (const documentation::SpecificationError& e) {
LERROR("Failed loading of log");
for (const documentation::TestResult::Offense& o : e.result.offenses) {
LERRORC(o.offender, ghoul::to_string(o.reason));
}
for (const documentation::TestResult::Warning& w : e.result.warnings) {
LWARNINGC(w.offender, ghoul::to_string(w.reason));
}
throw;
}
}
#ifdef WIN32
if (IsDebuggerPresent()) {
LogMgr.addLog(std::make_unique<VisualStudioOutputLog>());
}
#endif // WIN32
#ifndef GHOUL_LOGGING_ENABLE_TRACE
LogLevel level = ghoul::logging::levelFromString(_configuration->logging.level);
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::writeSceneDocumentation() {
// Write keyboard documentation.
if (!_configuration->documentation.keyboard.empty()) {
keyBindingManager().writeDocumentation(
absPath(_configuration->documentation.keyboard)
);
}
if (!_configuration->documentation.license.empty()) {
_scene->writeSceneLicenseDocumentation(
absPath(_configuration->documentation.license)
);
}
if (!_configuration->documentation.sceneProperty.empty()) {
_scene->writeDocumentation(absPath(_configuration->documentation.sceneProperty));
}
if (!_configuration->documentation.property.empty()) {
_rootPropertyOwner->writeDocumentation(
absPath(_configuration->documentation.property)
);
}
}
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)");
LTRACE("OpenSpaceEngine::initializeGL::DebugContext(begin)");
bool debugActive = _configuration->openGLDebugContext.isActive;
// Debug output is not available before 4.3
const ghoul::systemcapabilities::Version minVersion = { 4, 3, 0 };
if (debugActive && OpenGLCap.openGLVersion() < minVersion) {
LINFO("OpenGL Debug context requested, but insufficient version available");
debugActive = false;
}
if (debugActive) {
using namespace ghoul::opengl::debug;
bool synchronous = _configuration->openGLDebugContext.isSynchronous;
setDebugOutput(DebugOutput(debugActive), SynchronousOutput(synchronous));
using IdFilter = Configuration::OpenGLDebugContext::IdentifierFilter;
for (const IdFilter&f : _configuration->openGLDebugContext.identifierFilters) {
setDebugMessageControl(
ghoul::from_string<Source>(f.source),
ghoul::from_string<Type>(f.type),
{ f.identifier },
Enabled::No
);
}
for (const std::string& sev : _configuration->openGLDebugContext.severityFilters){
setDebugMessageControl(
Source::DontCare,
Type::DontCare,
ghoul::from_string<Severity>(sev),
Enabled::No
);
}
auto callback = [](Source source, Type type, Severity severity,
unsigned int id, std::string message) -> void
{
const std::string s = ghoul::to_string(source);
const std::string t = ghoul::to_string(type);
const std::string category =
"OpenGL (" + s + ") [" + t + "] {" + std::to_string(id) + "}";
switch (severity) {
case Severity::High:
LERRORC(category, message);
break;
case Severity::Medium:
LWARNINGC(category, message);
break;
case Severity::Low:
LINFOC(category, message);
break;
case Severity::Notification:
LDEBUGC(category, message);
break;
default:
throw ghoul::MissingCaseException();
}
};
ghoul::opengl::debug::setDebugCallback(callback);
}
LTRACE("OpenSpaceEngine::initializeGL::DebugContext(end)");
// The ordering of the KeyCheckOpenGLState and KeyLogEachOpenGLCall are important as
// the callback mask in glbinding is stateful for each context, and since
// KeyLogEachOpenGLCall is more specific, we want it to be able to overwrite the
// state from KeyCheckOpenGLState
if (_configuration->isCheckingOpenGLState) {
using namespace glbinding;
// Infinite loop -- welcome to the danger zone
setCallbackMaskExcept(CallbackMask::After, { "glGetError" });
setAfterCallback([](const FunctionCall& f) {
const GLenum error = glGetError();
switch (error) {
case GL_NO_ERROR:
break;
case GL_INVALID_ENUM:
LERRORC(
"OpenGL Invalid State",
fmt::format("Function {}: GL_INVALID_ENUM", f.toString())
);
break;
case GL_INVALID_VALUE:
LERRORC(
"OpenGL Invalid State",
fmt::format("Function {}: GL_INVALID_VALUE", f.toString())
);
break;
case GL_INVALID_OPERATION:
LERRORC(
"OpenGL Invalid State",
fmt::format("Function {}: GL_INVALID_OPERATION", f.toString())
);
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
LERRORC(
"OpenGL Invalid State",
fmt::format(
"Function {}: GL_INVALID_FRAMEBUFFER_OPERATION",
f.toString()
)
);
break;
case GL_OUT_OF_MEMORY:
LERRORC(
"OpenGL Invalid State",
fmt::format("Function {}: GL_OUT_OF_MEMORY", f.toString())
);
break;
default:
LERRORC(
"OpenGL Invalid State",
fmt::format("Unknown error code: {0:x}", error)
);
}
});
}
if (_configuration->isLoggingOpenGLCalls) {
LogLevel level = ghoul::logging::levelFromString(_configuration->logging.level);
if (level > LogLevel::Trace) {
LWARNING(
"Logging OpenGL calls is enabled, but the selected log level does "
"not include TRACE, so no OpenGL logs will be printed");
}
else {
using namespace glbinding;
setCallbackMask(CallbackMask::After | CallbackMask::ParametersAndReturnValue);
glbinding::setAfterCallback([](const glbinding::FunctionCall& call) {
std::string arguments = std::accumulate(
call.parameters.begin(),
call.parameters.end(),
std::string("("),
[](std::string a, AbstractValue* v) {
return a + v->asString() + ", ";
}
);
// Remove the final ", "
arguments = arguments.substr(0, arguments.size() - 2) + ")";
std::string returnValue = call.returnValue ?
" -> " + call.returnValue->asString() :
"";
LTRACEC(
"OpenGL",
call.function->name() + std::move(arguments) + std::move(returnValue)
);
});
}
}
LDEBUG("Initializing Rendering Engine");
_renderEngine->initializeGL();
_moduleEngine->initializeGL();
for (const std::function<void()>& func : _moduleCallbacks.initializeGL) {
func();
}
LINFO("Finished initializing OpenGL");
LTRACE("OpenSpaceEngine::initializeGL(end)");
}
void OpenSpaceEngine::preSynchronization() {
LTRACE("OpenSpaceEngine::preSynchronization(begin)");
std::unique_ptr<performance::PerformanceMeasurement> perf;
if (OsEng.renderEngine().performanceManager()) {
perf = std::make_unique<performance::PerformanceMeasurement>(
"OpenSpaceEngine::preSynchronization",
OsEng.renderEngine().performanceManager()
);
}
FileSys.triggerFilesystemEvents();
if (_hasScheduledAssetLoading) {
LINFO(fmt::format("Loading asset: {}", _scheduledAssetPathToLoad));
loadSingleAsset(_scheduledAssetPathToLoad);
_hasScheduledAssetLoading = false;
_scheduledAssetPathToLoad.clear();
}
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<std::string>::const_iterator;
std::pair<Iter, Iter> scheduledScripts = _scriptScheduler->progressTo(
timeManager().time().j2000Seconds()
);
for (Iter it = scheduledScripts.first; it != scheduledScripts.second; ++it) {
_scriptEngine->queueScript(
*it,
ScriptEngine::RemoteScripting::Yes
);
}
_renderEngine->updateScene();
//_navigationHandler->updateCamera(dt);
Camera* camera = _scene->camera();
if (camera) {
_navigationHandler->updateCamera(dt);
camera->invalidateCache();
}
_parallelPeer->preSynchronization();
}
for (const std::function<void()>& func : _moduleCallbacks.preSync) {
func();
}
LTRACE("OpenSpaceEngine::preSynchronization(end)");
}
void OpenSpaceEngine::postSynchronizationPreDraw() {
LTRACE("OpenSpaceEngine::postSynchronizationPreDraw(begin)");
std::unique_ptr<performance::PerformanceMeasurement> perf;
if (OsEng.renderEngine().performanceManager()) {
perf = std::make_unique<performance::PerformanceMeasurement>(
"OpenSpaceEngine::postSynchronizationPreDraw",
OsEng.renderEngine().performanceManager()
);
}
bool master = _windowWrapper->isMaster();
_syncEngine->postSynchronization(SyncEngine::IsMaster(master));
// This probably doesn't have to be done here every frame, but doing it earlier gives
// weird results when using side_by_side stereo --- abock
using FR = ghoul::fontrendering::FontRenderer;
FR::defaultRenderer().setFramebufferSize(_renderEngine->fontResolution());
FR::defaultProjectionRenderer().setFramebufferSize(
_renderEngine->renderingResolution()
);
if (_shutdown.inShutdown) {
if (_shutdown.timer <= 0.f) {
_windowWrapper->terminate();
}
_shutdown.timer -= static_cast<float>(_windowWrapper->averageDeltaTime());
}
const bool updated = _assetManager->update();
if (updated) {
writeSceneDocumentation();
}
_renderEngine->updateScene();
_renderEngine->updateFade();
_renderEngine->updateRenderer();
_renderEngine->updateScreenSpaceRenderables();
_renderEngine->updateShaderPrograms();
if (!master) {
_scene->camera()->invalidateCache();
}
for (const std::function<void()>& 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", fmt::format("Number of Warnings: {}", warningCounter));
}
if (errorCounter > 0) {
LWARNINGC("Logging", fmt::format("Number of Errors: {}", errorCounter));
}
if (fatalCounter > 0) {
LWARNINGC("Logging", fmt::format("Number of Fatals: {}", fatalCounter));
}
LogMgr.resetMessageCounters();
LTRACE("OpenSpaceEngine::postSynchronizationPreDraw(end)");
}
void OpenSpaceEngine::render(const glm::mat4& sceneMatrix, const glm::mat4& viewMatrix,
const glm::mat4& projectionMatrix)
{
LTRACE("OpenSpaceEngine::render(begin)");
std::unique_ptr<performance::PerformanceMeasurement> perf;
if (OsEng.renderEngine().performanceManager()) {
perf = std::make_unique<performance::PerformanceMeasurement>(
"OpenSpaceEngine::render",
OsEng.renderEngine().performanceManager()
);
}
const bool isGuiWindow =
_windowWrapper->hasGuiWindow() ? _windowWrapper->isGuiWindow() : true;
if (isGuiWindow) {
_console->update();
}
_renderEngine->render(sceneMatrix, viewMatrix, projectionMatrix);
for (const std::function<void()>& func : _moduleCallbacks.render) {
func();
}
LTRACE("OpenSpaceEngine::render(end)");
}
void OpenSpaceEngine::drawOverlays() {
LTRACE("OpenSpaceEngine::drawOverlays(begin)");
std::unique_ptr<performance::PerformanceMeasurement> perf;
if (OsEng.renderEngine().performanceManager()) {
perf = std::make_unique<performance::PerformanceMeasurement>(
"OpenSpaceEngine::drawOverlays",
OsEng.renderEngine().performanceManager()
);
}
const bool isGuiWindow =
_windowWrapper->hasGuiWindow() ? _windowWrapper->isGuiWindow() : true;
if (isGuiWindow) {
_renderEngine->renderOverlays(_shutdown);
_console->render();
}
for (const std::function<void()>& func : _moduleCallbacks.draw2D) {
func();
}
LTRACE("OpenSpaceEngine::drawOverlays(end)");
}
void OpenSpaceEngine::postDraw() {
LTRACE("OpenSpaceEngine::postDraw(begin)");
std::unique_ptr<performance::PerformanceMeasurement> perf;
if (OsEng.renderEngine().performanceManager()) {
perf = std::make_unique<performance::PerformanceMeasurement>(
"OpenSpaceEngine::postDraw",
OsEng.renderEngine().performanceManager()
);
}
_renderEngine->postDraw();
for (const std::function<void()>& func : _moduleCallbacks.postDraw) {
func();
}
if (_isFirstRenderingFirstFrame) {
_windowWrapper->setSynchronization(true);
_isFirstRenderingFirstFrame = false;
}
LTRACE("OpenSpaceEngine::postDraw(end)");
}
void OpenSpaceEngine::keyboardCallback(Key key, KeyModifier mod, KeyAction action) {
using F = std::function<bool (Key, KeyModifier, KeyAction)>;
for (const F& func : _moduleCallbacks.keyboard) {
const bool isConsumed = func(key, mod, action);
if (isConsumed) {
return;
}
}
if (!_configuration->isConsoleDisabled) {
const bool isConsoleConsumed = _console->keyboardCallback(key, mod, action);
if (isConsoleConsumed) {
return;
}
}
_navigationHandler->keyboardCallback(key, mod, action);
_keyBindingManager->keyboardCallback(key, mod, action);
}
void OpenSpaceEngine::charCallback(unsigned int codepoint, KeyModifier modifier) {
using F = std::function<bool (unsigned int, KeyModifier)>;
for (const F& func : _moduleCallbacks.character) {
bool isConsumed = func(codepoint, modifier);
if (isConsumed) {
return;
}
}
_console->charCallback(codepoint, modifier);
}
void OpenSpaceEngine::mouseButtonCallback(MouseButton button, MouseAction action) {
using F = std::function<bool (MouseButton, MouseAction)>;
for (const F& func : _moduleCallbacks.mouseButton) {
bool isConsumed = func(button, action);
if (isConsumed) {
// If the mouse was released, we still want to forward it to the navigation
// handler in order to reliably terminate a rotation or zoom. Accidentally
// moving the cursor over a UI window is easy to miss and leads to weird
// continuing movement
if (action == MouseAction::Release) {
break;
}
else {
return;
}
}
}
// Check if the user clicked on one of the 'buttons' the RenderEngine is drawing
if (action == MouseAction::Press) {
bool isConsumed = _renderEngine->mouseActivationCallback(_mousePosition);
if (isConsumed) {
return;
}
}
_navigationHandler->mouseButtonCallback(button, action);
}
void OpenSpaceEngine::mousePositionCallback(double x, double y) {
using F = std::function<void (double, double)>;
for (const F& func : _moduleCallbacks.mousePosition) {
func(x, y);
}
_mousePosition = { x, y };
_navigationHandler->mousePositionCallback(x, y);
}
void OpenSpaceEngine::mouseScrollWheelCallback(double posX, double posY) {
using F = std::function<bool (double, double)>;
for (const F& func : _moduleCallbacks.mouseScrollWheel) {
bool isConsumed = func(posX, posY);
if (isConsumed) {
return;
}
}
_navigationHandler->mouseScrollWheelCallback(posY);
}
void OpenSpaceEngine::setJoystickInputStates(interaction::JoystickInputStates& states) {
_navigationHandler->setJoystickInputStates(states);
}
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
_shutdown.inShutdown = false;
}
else {
// Else, we have to enable it
_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,"
"[description, 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"
},
{
"addTag",
&luascriptfunctions::addTag,
{},
"string, string",
"Adds a tag (second argument) to a scene graph node (first argument)"
},
{
"removeTag",
&luascriptfunctions::removeTag,
{},
"string, string",
"Removes a tag (second argument) from a scene graph node (first argument)"
}
},
{
absPath("${SCRIPTS}/core_scripts.lua")
}
};
}
// Registers a callback for a specific CallbackOption
void OpenSpaceEngine::registerModuleCallback(OpenSpaceEngine::CallbackOption option,
std::function<void()> 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::Draw2D:
_moduleCallbacks.draw2D.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<bool (Key, KeyModifier, KeyAction)> function)
{
_moduleCallbacks.keyboard.push_back(std::move(function));
}
void OpenSpaceEngine::registerModuleCharCallback(
std::function<bool (unsigned int, KeyModifier)> function)
{
_moduleCallbacks.character.push_back(std::move(function));
}
void OpenSpaceEngine::registerModuleMouseButtonCallback(
std::function<bool (MouseButton, MouseAction)> function)
{
_moduleCallbacks.mouseButton.push_back(std::move(function));
}
void OpenSpaceEngine::registerModuleMousePositionCallback(
std::function<void (double, double)> function)
{
_moduleCallbacks.mousePosition.push_back(std::move(function));
}
void OpenSpaceEngine::registerModuleMouseScrollWheelCallback(
std::function<bool (double, double)> function)
{
_moduleCallbacks.mouseScrollWheel.push_back(std::move(function));
}
const Configuration& OpenSpaceEngine::configuration() const {
ghoul_assert(_configuration, "Configuration must not be nullptr");
return *_configuration;
}
LuaConsole& OpenSpaceEngine::console() {
ghoul_assert(_console, "LuaConsole must not be nullptr");
return *_console;
}
Dashboard& OpenSpaceEngine::dashboard() {
ghoul_assert(_dashboard, "Dashboard must not be nullptr");
return *_dashboard;
}
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;
}
ParallelPeer& OpenSpaceEngine::parallelPeer() {
ghoul_assert(_parallelPeer, "ParallelPeer must not be nullptr");
return *_parallelPeer;
}
RenderEngine& OpenSpaceEngine::renderEngine() {
ghoul_assert(_renderEngine, "RenderEngine must not be nullptr");
return *_renderEngine;
}
TimeManager& OpenSpaceEngine::timeManager() {
ghoul_assert(_timeManager, "Download Manager must not be nullptr");
return *_timeManager;
}
LoadingScreen& OpenSpaceEngine::loadingScreen() {
ghoul_assert(_loadingScreen, "Loading Screen must not be nullptr");
return *_loadingScreen;
}
WindowWrapper& OpenSpaceEngine::windowWrapper() {
ghoul_assert(_windowWrapper, "Window Wrapper must not be nullptr");
return *_windowWrapper;
}
AssetManager& OpenSpaceEngine::assetManager() {
ghoul_assert(_assetManager, "Asset Manager must not be nullptr");
return *_assetManager;
}
ghoul::fontrendering::FontManager& OpenSpaceEngine::fontManager() {
ghoul_assert(_fontManager, "Font Manager must not be nullptr");
return *_fontManager;
}
interaction::NavigationHandler& OpenSpaceEngine::navigationHandler() {
ghoul_assert(_navigationHandler, "NavigationHandler must not be nullptr");
return *_navigationHandler;
}
interaction::KeyBindingManager& OpenSpaceEngine::keyBindingManager() {
ghoul_assert(_keyBindingManager, "KeyBindingManager must not be nullptr");
return *_keyBindingManager;
}
properties::PropertyOwner& OpenSpaceEngine::rootPropertyOwner() {
ghoul_assert(_rootPropertyOwner, "Root Property Namespace must not be nullptr");
return *_rootPropertyOwner;
}
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