/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2016 * * * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * * software and associated documentation files (the "Software"), to deal in the Software * * without restriction, including without limitation the rights to use, copy, modify, * * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * * permit persons to whom the Software is furnished to do so, subject to the following * * conditions: * * * * The above copyright notice and this permission notice shall be included in all copies * * or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ #include #ifdef OPENSPACE_MODULE_NEWHORIZONS_ENABLED #include #endif #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 #ifdef GHOUL_USE_DEVIL #include #endif //GHOUL_USE_DEVIL #ifdef GHOUL_USE_FREEIMAGE #include #endif // GHOUL_USE_FREEIMAGE #include #include #ifdef GHOUL_USE_SOIL #include #include #endif //GHOUL_USE_SOIL #include #include #include // These are temporary ---abock #include #include // ABuffer defines #define RENDERER_FRAMEBUFFER 0 #define RENDERER_ABUFFER 1 #include "renderengine_lua.inl" namespace { const std::string _loggerCat = "RenderEngine"; const std::string KeyRenderingMethod = "RenderingMethod"; std::chrono::seconds ScreenLogTimeToLive(15); const std::string DefaultRenderingMethod = "ABuffer"; const std::string RenderFsPath = "${SHADERS}/render.frag"; } namespace openspace { const std::string RenderEngine::PerformanceMeasurementSharedData = "OpenSpacePerformanceMeasurementSharedData"; const std::string RenderEngine::KeyFontMono = "Mono"; const std::string RenderEngine::KeyFontLight = "Light"; RenderEngine::RenderEngine() : _mainCamera(nullptr) , _sceneGraph(nullptr) , _renderer(nullptr) , _rendererImplementation(RendererImplementation::Invalid) , _log(nullptr) , _showInfo(true) , _showLog(true) , _takeScreenshot(false) , _doPerformanceMeasurements(false) , _performanceMemory(nullptr) , _globalBlackOutFactor(1.f) , _fadeDuration(2.f) , _currentFadeTime(0.f) , _fadeDirection(0) // , _sgctRenderStatisticsVisible(false) { _onScreenInformation = { glm::vec2(0.f), 12, -1 }; } RenderEngine::~RenderEngine() { delete _sceneGraph; _sceneGraph = nullptr; delete _mainCamera; delete _performanceMemory; if (ghoul::SharedMemory::exists(PerformanceMeasurementSharedData)) ghoul::SharedMemory::remove(PerformanceMeasurementSharedData); } bool RenderEngine::deinitialize() { _sceneGraph->clearSceneGraph(); return true; } void RenderEngine::setRendererFromString(const std::string& renderingMethod) { _rendererImplementation = rendererFromString(renderingMethod); std::unique_ptr newRenderer = nullptr; switch (_rendererImplementation) { case RendererImplementation::Framebuffer: newRenderer = std::make_unique(); break; case RendererImplementation::ABuffer: newRenderer = std::make_unique(); break; case RendererImplementation::Invalid: LFATAL("Rendering method '" << renderingMethod << "' not among the available " << "rendering methods"); } setRenderer(std::move(newRenderer)); } bool RenderEngine::initialize() { std::string renderingMethod = DefaultRenderingMethod; // If the user specified a rendering method that he would like to use, use that if (OsEng.configurationManager().hasKeyAndValue(KeyRenderingMethod)) { renderingMethod = OsEng.configurationManager().value(KeyRenderingMethod); } else { using Version = ghoul::systemcapabilities::OpenGLCapabilitiesComponent::Version; // The default rendering method has a requirement of OpenGL 4.3, so if we are // below that, we will fall back to frame buffer operation if (OpenGLCap.openGLVersion() < Version{4,3,0}) { LINFO("Falling back to framebuffer implementation due to OpenGL limitations"); renderingMethod = "Framebuffer"; } } LINFO("Seting renderer from string: " << renderingMethod); setRendererFromString(renderingMethod); // init camera and set temporary position and scaling _mainCamera = new Camera(); _mainCamera->setScaling(glm::vec2(1.0, -8.0)); _mainCamera->setPosition(psc(0.f, 0.f, 1.499823f, 11.f)); OsEng.interactionHandler().setCamera(_mainCamera); if (_renderer) { _renderer->setCamera(_mainCamera); } #ifdef GHOUL_USE_DEVIL ghoul::io::TextureReader::ref().addReader(std::make_shared()); #endif // GHOUL_USE_DEVIL #ifdef GHOUL_USE_FREEIMAGE ghoul::io::TextureReader::ref().addReader(std::make_shared()); #endif // GHOUL_USE_FREEIMAGE #ifdef GHOUL_USE_SOIL ghoul::io::TextureReader::ref().addReader(std::make_shared()); ghoul::io::TextureWriter::ref().addWriter(std::make_shared()); #endif // GHOUL_USE_SOIL ghoul::io::TextureReader::ref().addReader(std::make_shared()); } bool RenderEngine::initializeGL() { // TODO: Fix the power scaled coordinates in such a way that these // values can be set to more realistic values // set the close clip plane and the far clip plane to extreme values while in // development OsEng.windowWrapper().setNearFarClippingPlane(0.001f, 1000.f); try { const float fontSizeTime = 15.f; _fontDate = OsEng.fontManager().font(KeyFontMono, fontSizeTime); const float fontSizeMono = 10.f; _fontInfo = OsEng.fontManager().font(KeyFontMono, fontSizeMono); const float fontSizeLight = 8.f; _fontLog = OsEng.fontManager().font(KeyFontLight, fontSizeLight); } catch (const ghoul::fontrendering::Font::FreeTypeException& e) { LERROR(e.what()); throw; } // ALL OF THIS HAS TO BE CHECKED // ---abock // sgct::Engine::instance()->setNearAndFarClippingPlanes(0.001f, 1000.0f); // sgct::Engine::instance()->setNearAndFarClippingPlanes(0.1f, 30.0f); // calculating the maximum field of view for the camera, used to // determine visibility of objects in the scene graph /* if (sgct::Engine::instance()->getCurrentRenderTarget() == sgct::Engine::NonLinearBuffer) { // fisheye mode, looking upwards to the "dome" glm::vec4 upDirection(0, 1, 0, 0); // get the tilt and rotate the view const float tilt = wPtr->getFisheyeTilt(); glm::mat4 tiltMatrix = glm::rotate(glm::mat4(1.0f), tilt, glm::vec3(1.0f, 0.0f, 0.0f)); const glm::vec4 viewdir = tiltMatrix * upDirection; // set the tilted view and the FOV _mainCamera->setCameraDirection(glm::vec3(viewdir[0], viewdir[1], viewdir[2])); _mainCamera->setMaxFov(wPtr->getFisheyeFOV()); _mainCamera->setLookUpVector(glm::vec3(0.0, 1.0, 0.0)); } else {*/ // get corner positions, calculating the forth to easily calculate center // glm::vec3 corners[4]; // sgct::SGCTWindow* wPtr = sgct::Engine::instance()->getWindowPtr(0); // sgct_core::BaseViewport* vp = wPtr->getViewport(0); // sgct_core::SGCTProjectionPlane* projectionPlane = vp->getProjectionPlane(); // corners[0] = *(projectionPlane->getCoordinatePtr(sgct_core::SGCTProjectionPlane::LowerLeft)); // corners[1] = *(projectionPlane->getCoordinatePtr(sgct_core::SGCTProjectionPlane::UpperLeft)); // corners[2] = *(projectionPlane->getCoordinatePtr(sgct_core::SGCTProjectionPlane::UpperRight)); // corners[3] = glm::vec3(corners[2][0], corners[0][1], corners[2][2]); // // const glm::vec3 center = (corners[0] + corners[1] + corners[2] + corners[3]); //// //const glm::vec3 eyePosition = sgct_core::ClusterManager::instance()->getDefaultUserPtr()->getPos(); ////// get viewdirection, stores the direction in the camera, used for culling //const glm::vec3 viewdir = glm::normalize(eyePosition - center); //const glm::vec3 upVector = corners[0] - corners[1]; //_mainCamera->setCameraDirection(glm::normalize(-viewdir)); _mainCamera->setCameraDirection(glm::vec3(0.f, 0.f, -1.f)); //_mainCamera->setLookUpVector(glm::normalize(upVector)); _mainCamera->setLookUpVector(glm::vec3(0.f, 1.f, 0.f)); // set the initial fov to be 0.0 which means everything will be culled //float maxFov = 0.0f; float maxFov = std::numeric_limits::max(); //// for each corner //for (int i = 0; i < 4; ++i) { // // calculate radians to corner // glm::vec3 dir = glm::normalize(eyePosition - corners[i]); // float radsbetween = acos(glm::dot(viewdir, dir)) // / (glm::length(viewdir) * glm::length(dir)); // // the angle to a corner is larger than the current maxima // if (radsbetween > maxFov) { // maxFov = radsbetween; // } //} _mainCamera->setMaxFov(maxFov); //} LINFO("Initializing Log"); std::unique_ptr log = std::make_unique(ScreenLogTimeToLive); _log = log.get(); ghoul::logging::LogManager::ref().addLog(std::move(log)); LINFO("Finished initializing GL"); return true; } void RenderEngine::preSynchronization() { if (_mainCamera) _mainCamera->preSynchronization(); } void RenderEngine::postSynchronizationPreDraw() { //temporary fade funtionality if (_fadeDirection != 0) { if (_currentFadeTime > _fadeDuration){ _fadeDirection = 0; _globalBlackOutFactor = fminf(1.f, fmaxf(0.f, _globalBlackOutFactor)); } else { if (_fadeDirection < 0) _globalBlackOutFactor = glm::smoothstep(1.f, 0.f, _currentFadeTime / _fadeDuration); else _globalBlackOutFactor = glm::smoothstep(0.f, 1.f, _currentFadeTime / _fadeDuration); _currentFadeTime += static_cast(OsEng.windowWrapper().averageDeltaTime()); } } if (_mainCamera) _mainCamera->postSynchronizationPreDraw(); bool windowResized = OsEng.windowWrapper().windowHasResized(); if (windowResized) { glm::ivec2 res = OsEng.windowWrapper().currentDrawBufferResolution(); _renderer->setResolution(res); ghoul::fontrendering::FontRenderer::defaultRenderer().setWindowSize(glm::vec2(res)); } // converts the quaternion used to rotation matrices if (_mainCamera) _mainCamera->compileViewRotationMatrix(); // update and evaluate the scene starting from the root node _sceneGraph->update({ Time::ref().currentTime(), Time::ref().timeJumped(), Time::ref().deltaTime(), _doPerformanceMeasurements }); _sceneGraph->evaluate(_mainCamera); _renderer->update(); for (auto program : _programs) { if (program->isDirty()) { program->rebuildFromFile(); } } //Allow focus node to update camera (enables camera-following) //FIX LATER: THIS CAUSES MASTER NODE TO BE ONE FRAME AHEAD OF SLAVES //if (const SceneGraphNode* node = OsEng.ref().interactionHandler().focusNode()){ //node->updateCamera(_mainCamera); //} } void RenderEngine::render(const glm::mat4 &projectionMatrix, const glm::mat4 &viewMatrix) { _mainCamera->setViewMatrix(viewMatrix); _mainCamera->setProjectionMatrix(projectionMatrix); if (!(OsEng.isMaster() && _disableMasterRendering)) { _renderer->render(_globalBlackOutFactor, _doPerformanceMeasurements); } // Print some useful information on the master viewport if (OsEng.isMaster() && OsEng.windowWrapper().isSimpleRendering()) { if (_showInfo) { renderInformation(); } if (_showLog) { renderScreenLog(); } } } void RenderEngine::postDraw() { if (Time::ref().timeJumped()) Time::ref().setTimeJumped(false); if (_takeScreenshot) { OsEng.windowWrapper().takeScreenshot(); _takeScreenshot = false; } if (_doPerformanceMeasurements) storePerformanceMeasurements(); } void RenderEngine::takeScreenshot() { _takeScreenshot = true; } void RenderEngine::toggleInfoText(bool b) { _showInfo = b; } Scene* RenderEngine::scene() { // TODO custom assert (ticket #5) assert(_sceneGraph); return _sceneGraph; } void RenderEngine::setSceneGraph(Scene* sceneGraph) { _sceneGraph = sceneGraph; } void RenderEngine::serialize(SyncBuffer* syncBuffer) { if (_mainCamera){ _mainCamera->serialize(syncBuffer); } syncBuffer->encode(_onScreenInformation._node); syncBuffer->encode(_onScreenInformation._position.x); syncBuffer->encode(_onScreenInformation._position.y); syncBuffer->encode(_onScreenInformation._size); } void RenderEngine::deserialize(SyncBuffer* syncBuffer) { if (_mainCamera){ _mainCamera->deserialize(syncBuffer); } syncBuffer->decode(_onScreenInformation._node); syncBuffer->decode(_onScreenInformation._position.x); syncBuffer->decode(_onScreenInformation._position.y); syncBuffer->decode(_onScreenInformation._size); } Camera* RenderEngine::camera() const { return _mainCamera; } Renderer* RenderEngine::renderer() const { return _renderer.get(); } RenderEngine::RendererImplementation RenderEngine::rendererImplementation() const { return _rendererImplementation; } float RenderEngine::globalBlackOutFactor() { return _globalBlackOutFactor; } void RenderEngine::setGlobalBlackOutFactor(float opacity) { _globalBlackOutFactor = opacity; } void RenderEngine::startFading(int direction, float fadeDuration) { _fadeDirection = direction; _fadeDuration = fadeDuration; _currentFadeTime = 0.f; } /** * Build a program object for rendering with the used renderer */ std::unique_ptr RenderEngine::buildRenderProgram( std::string name, std::string vsPath, std::string fsPath, const ghoul::Dictionary& data) { ghoul::Dictionary dict = data; // set path to the current renderer's main fragment shader dict.setValue("rendererData", _rendererData); // parameterize the main fragment shader program with specific contents. // fsPath should point to a shader file defining a Fragment getFragment() function // instead of a void main() setting glFragColor, glFragDepth, etc. dict.setValue("fragmentPath", fsPath); std::unique_ptr program = ghoul::opengl::ProgramObject::Build( name, vsPath, RenderFsPath, dict); if (program) { _programs.push_back(program.get()); } return program; } /** * Build a program object for rendering with the used renderer */ std::unique_ptr RenderEngine::buildRenderProgram( std::string name, std::string vsPath, std::string fsPath, std::string csPath, const ghoul::Dictionary& data) { ghoul::Dictionary dict = data; dict.setValue("rendererData", _rendererData); // parameterize the main fragment shader program with specific contents. // fsPath should point to a shader file defining a Fragment getFragment() function // instead of a void main() setting glFragColor, glFragDepth, etc. dict.setValue("fragmentPath", fsPath); std::unique_ptr program = ghoul::opengl::ProgramObject::Build( name, vsPath, RenderFsPath, csPath, dict); if (program) { _programs.push_back(program.get()); } return program; } void RenderEngine::removeRenderProgram(const std::unique_ptr& program) { ghoul::opengl::ProgramObject* ptr = program.get(); auto it = std::find( _programs.begin(), _programs.end(), ptr ); if (it != _programs.end()) { _programs.erase(it); } } /** * Set renderer data * Called from the renderer, whenever it needs to update * the dictionary of all rendering programs. */ void RenderEngine::setRendererData(const ghoul::Dictionary& data) { _rendererData = data; for (auto program : _programs) { ghoul::Dictionary dict = program->dictionary(); dict.setValue("rendererData", _rendererData); program->setDictionary(dict); } } /** * Set renderer */ void RenderEngine::setRenderer(std::unique_ptr renderer) { glm::ivec2 res = OsEng.windowWrapper().currentDrawBufferResolution(); if (_renderer) { _renderer->deinitialize(); } _renderer = std::move(renderer); _renderer->setResolution(res); _renderer->initialize(); _renderer->setCamera(_mainCamera); _renderer->setScene(_sceneGraph); } scripting::ScriptEngine::LuaLibrary RenderEngine::luaLibrary() { return { "", { { "takeScreenshot", &luascriptfunctions::takeScreenshot, "", "Renders the current image to a file on disk" }, { "setRenderer", &luascriptfunctions::setRenderer, "string", "Sets the renderer (ABuffer or FrameBuffer)" }, { "showRenderInformation", &luascriptfunctions::showRenderInformation, "bool", "Toggles the showing of render information on-screen text" }, { "setPerformanceMeasurement", &luascriptfunctions::setPerformanceMeasurement, "bool", "Sets the performance measurements" }, { "fadeIn", &luascriptfunctions::fadeIn, "number", "", true }, //also temporary @JK { "fadeOut", &luascriptfunctions::fadeOut, "number", "", true }, }, }; } void RenderEngine::setPerformanceMeasurements(bool performanceMeasurements) { _doPerformanceMeasurements = performanceMeasurements; } bool RenderEngine::doesPerformanceMeasurements() const { return _doPerformanceMeasurements; } void RenderEngine::storePerformanceMeasurements() { const int8_t Version = 0; const int nValues = 250; const int lengthName = 256; const int maxValues = 256; struct PerformanceLayout { int8_t version; int32_t nValuesPerEntry; int32_t nEntries; int32_t maxNameLength; int32_t maxEntries; struct PerformanceLayoutEntry { char name[lengthName]; float renderTime[nValues]; float updateRenderable[nValues]; float updateEphemeris[nValues]; int32_t currentRenderTime; int32_t currentUpdateRenderable; int32_t currentUpdateEphemeris; }; PerformanceLayoutEntry entries[maxValues]; }; const int nNodes = static_cast(scene()->allSceneGraphNodes().size()); if (!_performanceMemory) { // Compute the total size const int totalSize = sizeof(int8_t) + 4 * sizeof(int32_t) + maxValues * sizeof(PerformanceLayout::PerformanceLayoutEntry); LINFO("Create shared memory of " << totalSize << " bytes"); try { ghoul::SharedMemory::remove(PerformanceMeasurementSharedData); } catch (const ghoul::SharedMemory::SharedMemoryError& e) { LINFOC(e.component, e.what()); } ghoul::SharedMemory::create(PerformanceMeasurementSharedData, totalSize); _performanceMemory = new ghoul::SharedMemory(PerformanceMeasurementSharedData); void* ptr = _performanceMemory->memory(); PerformanceLayout* layout = reinterpret_cast(ptr); layout->version = Version; layout->nValuesPerEntry = nValues; layout->nEntries = nNodes; layout->maxNameLength = lengthName; layout->maxEntries = maxValues; memset(layout->entries, 0, maxValues * sizeof(PerformanceLayout::PerformanceLayoutEntry)); for (int i = 0; i < nNodes; ++i) { SceneGraphNode* node = scene()->allSceneGraphNodes()[i]; memset(layout->entries[i].name, 0, lengthName); #ifdef _MSC_VER strcpy_s(layout->entries[i].name, node->name().length() + 1, node->name().c_str()); #else strcpy(layout->entries[i].name, node->name().c_str()); #endif layout->entries[i].currentRenderTime = 0; layout->entries[i].currentUpdateRenderable = 0; layout->entries[i].currentUpdateEphemeris = 0; } } void* ptr = _performanceMemory->memory(); PerformanceLayout* layout = reinterpret_cast(ptr); _performanceMemory->acquireLock(); for (int i = 0; i < nNodes; ++i) { SceneGraphNode* node = scene()->allSceneGraphNodes()[i]; SceneGraphNode::PerformanceRecord r = node->performanceRecord(); PerformanceLayout::PerformanceLayoutEntry& entry = layout->entries[i]; entry.renderTime[entry.currentRenderTime] = r.renderTime / 1000.f; entry.updateEphemeris[entry.currentUpdateEphemeris] = r.updateTimeEphemeris / 1000.f; entry.updateRenderable[entry.currentUpdateRenderable] = r.updateTimeRenderable / 1000.f; entry.currentRenderTime = (entry.currentRenderTime + 1) % nValues; entry.currentUpdateEphemeris = (entry.currentUpdateEphemeris + 1) % nValues; entry.currentUpdateRenderable = (entry.currentUpdateRenderable + 1) % nValues; } _performanceMemory->releaseLock(); } // This method is temporary and will be removed once the scalegraph is in effect ---abock void RenderEngine::changeViewPoint(std::string origin) { SceneGraphNode* solarSystemBarycenterNode = scene()->sceneGraphNode("SolarSystemBarycenter"); SceneGraphNode* plutoBarycenterNode = scene()->sceneGraphNode("PlutoBarycenter"); SceneGraphNode* newHorizonsNode = scene()->sceneGraphNode("NewHorizons"); SceneGraphNode* newHorizonsPathNodeJ = scene()->sceneGraphNode("NewHorizonsPathJupiter"); SceneGraphNode* newHorizonsPathNodeP = scene()->sceneGraphNode("NewHorizonsPathPluto"); RenderablePath* nhPath; SceneGraphNode* jupiterBarycenterNode = scene()->sceneGraphNode("JupiterBarycenter"); //SceneGraphNode* newHorizonsGhostNode = scene()->sceneGraphNode("NewHorizonsGhost"); //SceneGraphNode* dawnNode = scene()->sceneGraphNode("Dawn"); //SceneGraphNode* vestaNode = scene()->sceneGraphNode("Vesta"); if (solarSystemBarycenterNode == nullptr || plutoBarycenterNode == nullptr || newHorizonsNode == nullptr || jupiterBarycenterNode == nullptr //|| dawnNode == nullptr //|| vestaNode == nullptr ) { LERROR("Necessary nodes does not exist"); return; } if (origin == "Pluto") { if (newHorizonsPathNodeP) { Renderable* R = newHorizonsPathNodeP->renderable(); newHorizonsPathNodeP->setParent(plutoBarycenterNode); nhPath = static_cast(R); nhPath->calculatePath("PLUTO BARYCENTER"); } plutoBarycenterNode->setParent(scene()->sceneGraphNode("SolarSystem")); plutoBarycenterNode->setEphemeris(new StaticEphemeris); solarSystemBarycenterNode->setParent(plutoBarycenterNode); newHorizonsNode->setParent(plutoBarycenterNode); //newHorizonsGhostNode->setParent(plutoBarycenterNode); //dawnNode->setParent(plutoBarycenterNode); //vestaNode->setParent(plutoBarycenterNode); //newHorizonsTrailNode->setParent(plutoBarycenterNode); ghoul::Dictionary solarDictionary = { { std::string("Type"), std::string("Spice") }, { std::string("Body"), std::string("SUN") }, { std::string("Reference"), std::string("GALACTIC") }, { std::string("Observer"), std::string("PLUTO BARYCENTER") }, { std::string("Kernels"), ghoul::Dictionary() } }; ghoul::Dictionary jupiterDictionary = { { std::string("Type"), std::string("Spice") }, { std::string("Body"), std::string("JUPITER BARYCENTER") }, { std::string("Reference"), std::string("GALACTIC") }, { std::string("Observer"), std::string("PLUTO BARYCENTER") }, { std::string("Kernels"), ghoul::Dictionary() } }; ghoul::Dictionary newHorizonsDictionary = { { std::string("Type"), std::string("Spice") }, { std::string("Body"), std::string("NEW HORIZONS") }, { std::string("Reference"), std::string("GALACTIC") }, { std::string("Observer"), std::string("PLUTO BARYCENTER") }, { std::string("Kernels"), ghoul::Dictionary() } }; solarSystemBarycenterNode->setEphemeris(new SpiceEphemeris(solarDictionary)); jupiterBarycenterNode->setEphemeris(new SpiceEphemeris(jupiterDictionary)); newHorizonsNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); //newHorizonsTrailNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); //ghoul::Dictionary dawnDictionary = //{ // { std::string("Type"), std::string("Spice") }, // { std::string("Body"), std::string("DAWN") }, // { std::string("Reference"), std::string("GALACTIC") }, // { std::string("Observer"), std::string("PLUTO BARYCENTER") }, // { std::string("Kernels"), ghoul::Dictionary() } //}; //dawnNode->setEphemeris(new SpiceEphemeris(dawnDictionary)); // //ghoul::Dictionary vestaDictionary = //{ // { std::string("Type"), std::string("Spice") }, // { std::string("Body"), std::string("VESTA") }, // { std::string("Reference"), std::string("GALACTIC") }, // { std::string("Observer"), std::string("PLUTO BARYCENTER") }, // { std::string("Kernels"), ghoul::Dictionary() } //}; //vestaNode->setEphemeris(new SpiceEphemeris(vestaDictionary)); //ghoul::Dictionary newHorizonsGhostDictionary = //{ // { std::string("Type"), std::string("Spice") }, // { std::string("Body"), std::string("NEW HORIZONS") }, // { std::string("EphmerisGhosting"), std::string("TRUE") }, // { std::string("Reference"), std::string("GALACTIC") }, // { std::string("Observer"), std::string("PLUTO BARYCENTER") }, // { std::string("Kernels"), ghoul::Dictionary() } //}; //newHorizonsGhostNode->setEphemeris(new SpiceEphemeris(newHorizonsGhostDictionary)); return; } if (origin == "Sun") { solarSystemBarycenterNode->setParent(scene()->sceneGraphNode("SolarSystem")); plutoBarycenterNode->setParent(solarSystemBarycenterNode); jupiterBarycenterNode->setParent(solarSystemBarycenterNode); newHorizonsNode->setParent(solarSystemBarycenterNode); //newHorizonsGhostNode->setParent(solarSystemBarycenterNode); //newHorizonsTrailNode->setParent(solarSystemBarycenterNode); //dawnNode->setParent(solarSystemBarycenterNode); //vestaNode->setParent(solarSystemBarycenterNode); ghoul::Dictionary plutoDictionary = { { std::string("Type"), std::string("Spice") }, { std::string("Body"), std::string("PLUTO BARYCENTER") }, { std::string("Reference"), std::string("GALACTIC") }, { std::string("Observer"), std::string("SUN") }, { std::string("Kernels"), ghoul::Dictionary() } }; ghoul::Dictionary jupiterDictionary = { { std::string("Type"), std::string("Spice") }, { std::string("Body"), std::string("JUPITER BARYCENTER") }, { std::string("Reference"), std::string("GALACTIC") }, { std::string("Observer"), std::string("SUN") }, { std::string("Kernels"), ghoul::Dictionary() } }; solarSystemBarycenterNode->setEphemeris(new StaticEphemeris); jupiterBarycenterNode->setEphemeris(new SpiceEphemeris(jupiterDictionary)); plutoBarycenterNode->setEphemeris(new SpiceEphemeris(plutoDictionary)); ghoul::Dictionary newHorizonsDictionary = { { std::string("Type"), std::string("Spice") }, { std::string("Body"), std::string("NEW HORIZONS") }, { std::string("Reference"), std::string("GALACTIC") }, { std::string("Observer"), std::string("SUN") }, { std::string("Kernels"), ghoul::Dictionary() } }; newHorizonsNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); //newHorizonsTrailNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); //ghoul::Dictionary dawnDictionary = //{ // { std::string("Type"), std::string("Spice") }, // { std::string("Body"), std::string("DAWN") }, // { std::string("Reference"), std::string("GALACTIC") }, // { std::string("Observer"), std::string("SUN") }, // { std::string("Kernels"), ghoul::Dictionary() } //}; //dawnNode->setEphemeris(new SpiceEphemeris(dawnDictionary)); // //ghoul::Dictionary vestaDictionary = //{ // { std::string("Type"), std::string("Spice") }, // { std::string("Body"), std::string("VESTA") }, // { std::string("Reference"), std::string("GALACTIC") }, // { std::string("Observer"), std::string("SUN") }, // { std::string("Kernels"), ghoul::Dictionary() } //}; //vestaNode->setEphemeris(new SpiceEphemeris(vestaDictionary)); //ghoul::Dictionary newHorizonsGhostDictionary = //{ // { std::string("Type"), std::string("Spice") }, // { std::string("Body"), std::string("NEW HORIZONS") }, // { std::string("EphmerisGhosting"), std::string("TRUE") }, // { std::string("Reference"), std::string("GALACTIC") }, // { std::string("Observer"), std::string("JUPITER BARYCENTER") }, // { std::string("Kernels"), ghoul::Dictionary() } //}; //newHorizonsGhostNode->setEphemeris(new SpiceEphemeris(newHorizonsGhostDictionary)); return; } if (origin == "Jupiter") { if (newHorizonsPathNodeJ) { Renderable* R = newHorizonsPathNodeJ->renderable(); newHorizonsPathNodeJ->setParent(jupiterBarycenterNode); nhPath = static_cast(R); nhPath->calculatePath("JUPITER BARYCENTER"); } jupiterBarycenterNode->setParent(scene()->sceneGraphNode("SolarSystem")); jupiterBarycenterNode->setEphemeris(new StaticEphemeris); solarSystemBarycenterNode->setParent(jupiterBarycenterNode); newHorizonsNode->setParent(jupiterBarycenterNode); //newHorizonsTrailNode->setParent(jupiterBarycenterNode); //dawnNode->setParent(jupiterBarycenterNode); //vestaNode->setParent(jupiterBarycenterNode); ghoul::Dictionary solarDictionary = { { std::string("Type"), std::string("Spice") }, { std::string("Body"), std::string("SUN") }, { std::string("Reference"), std::string("GALACTIC") }, { std::string("Observer"), std::string("JUPITER BARYCENTER") }, { std::string("Kernels"), ghoul::Dictionary() } }; ghoul::Dictionary plutoDictionary = { { std::string("Type"), std::string("Spice") }, { std::string("Body"), std::string("PlUTO BARYCENTER") }, { std::string("Reference"), std::string("GALACTIC") }, { std::string("Observer"), std::string("JUPITER BARYCENTER") }, { std::string("Kernels"), ghoul::Dictionary() } }; ghoul::Dictionary newHorizonsDictionary = { { std::string("Type"), std::string("Spice") }, { std::string("Body"), std::string("NEW HORIZONS") }, { std::string("Reference"), std::string("GALACTIC") }, { std::string("Observer"), std::string("JUPITER BARYCENTER") }, { std::string("Kernels"), ghoul::Dictionary() } }; solarSystemBarycenterNode->setEphemeris(new SpiceEphemeris(solarDictionary)); plutoBarycenterNode->setEphemeris(new SpiceEphemeris(plutoDictionary)); newHorizonsNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); //newHorizonsGhostNode->setParent(jupiterBarycenterNode); //newHorizonsTrailNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); //ghoul::Dictionary dawnDictionary = //{ // { std::string("Type"), std::string("Spice") }, // { std::string("Body"), std::string("DAWN") }, // { std::string("Reference"), std::string("GALACTIC") }, // { std::string("Observer"), std::string("JUPITER BARYCENTER") }, // { std::string("Kernels"), ghoul::Dictionary() } //}; //dawnNode->setEphemeris(new SpiceEphemeris(dawnDictionary)); // //ghoul::Dictionary vestaDictionary = //{ // { std::string("Type"), std::string("Spice") }, // { std::string("Body"), std::string("VESTA") }, // { std::string("Reference"), std::string("GALACTIC") }, // { std::string("Observer"), std::string("JUPITER BARYCENTER") }, // { std::string("Kernels"), ghoul::Dictionary() } //}; //vestaNode->setEphemeris(new SpiceEphemeris(vestaDictionary)); //ghoul::Dictionary newHorizonsGhostDictionary = //{ // { std::string("Type"), std::string("Spice") }, // { std::string("Body"), std::string("NEW HORIZONS") }, // { std::string("EphmerisGhosting"), std::string("TRUE") }, // { std::string("Reference"), std::string("GALACTIC") }, // { std::string("Observer"), std::string("JUPITER BARYCENTER") }, // { std::string("Kernels"), ghoul::Dictionary() } //}; //newHorizonsGhostNode->setEphemeris(new SpiceEphemeris(newHorizonsGhostDictionary)); //newHorizonsGhostNode->setParent(jupiterBarycenterNode); return; } //if (origin == "Vesta") { // // vestaNode->setParent(scene()->sceneGraphNode("SolarSystem")); // vestaNode->setEphemeris(new StaticEphemeris); // // solarSystemBarycenterNode->setParent(vestaNode); // newHorizonsNode->setParent(vestaNode); // // dawnNode->setParent(vestaNode); // plutoBarycenterNode->setParent(vestaNode); // // // ghoul::Dictionary plutoDictionary = // { // { std::string("Type"), std::string("Spice") }, // { std::string("Body"), std::string("PLUTO BARYCENTER") }, // { std::string("Reference"), std::string("GALACTIC") }, // { std::string("Observer"), std::string("VESTA") }, // { std::string("Kernels"), ghoul::Dictionary() } // }; // ghoul::Dictionary solarDictionary = // { // { std::string("Type"), std::string("Spice") }, // { std::string("Body"), std::string("SUN") }, // { std::string("Reference"), std::string("GALACTIC") }, // { std::string("Observer"), std::string("VESTA") }, // { std::string("Kernels"), ghoul::Dictionary() } // }; // // ghoul::Dictionary jupiterDictionary = // { // { std::string("Type"), std::string("Spice") }, // { std::string("Body"), std::string("JUPITER BARYCENTER") }, // { std::string("Reference"), std::string("GALACTIC") }, // { std::string("Observer"), std::string("VESTA") }, // { std::string("Kernels"), ghoul::Dictionary() } // }; // // solarSystemBarycenterNode->setEphemeris(new SpiceEphemeris(solarDictionary)); // plutoBarycenterNode->setEphemeris(new SpiceEphemeris(plutoDictionary)); // jupiterBarycenterNode->setEphemeris(new SpiceEphemeris(jupiterDictionary)); // // ghoul::Dictionary newHorizonsDictionary = // { // { std::string("Type"), std::string("Spice") }, // { std::string("Body"), std::string("NEW HORIZONS") }, // { std::string("Reference"), std::string("GALACTIC") }, // { std::string("Observer"), std::string("VESTA") }, // { std::string("Kernels"), ghoul::Dictionary() } // }; // newHorizonsNode->setEphemeris(new SpiceEphemeris(newHorizonsDictionary)); // // ghoul::Dictionary dawnDictionary = // { // { std::string("Type"), std::string("Spice") }, // { std::string("Body"), std::string("DAWN") }, // { std::string("Reference"), std::string("GALACTIC") }, // { std::string("Observer"), std::string("VESTA") }, // { std::string("Kernels"), ghoul::Dictionary() } // }; // dawnNode->setEphemeris(new SpiceEphemeris(dawnDictionary)); // vestaNode->setEphemeris(new StaticEphemeris); // // return; //} if (origin == "67P") { SceneGraphNode* rosettaNode = scene()->sceneGraphNode("Rosetta"); SceneGraphNode* cgNode = scene()->sceneGraphNode("67P"); //jupiterBarycenterNode->setParent(solarSystemBarycenterNode); //plutoBarycenterNode->setParent(solarSystemBarycenterNode); solarSystemBarycenterNode->setParent(cgNode); rosettaNode->setParent(cgNode); ghoul::Dictionary solarDictionary = { { std::string("Type"), std::string("Spice") }, { std::string("Body"), std::string("SUN") }, { std::string("Reference"), std::string("GALACTIC") }, { std::string("Observer"), std::string("CHURYUMOV-GERASIMENKO") }, { std::string("Kernels"), ghoul::Dictionary() } }; solarSystemBarycenterNode->setEphemeris(new SpiceEphemeris(solarDictionary)); ghoul::Dictionary rosettaDictionary = { { std::string("Type"), std::string("Spice") }, { std::string("Body"), std::string("ROSETTA") }, { std::string("Reference"), std::string("GALACTIC") }, { std::string("Observer"), std::string("CHURYUMOV-GERASIMENKO") }, { std::string("Kernels"), ghoul::Dictionary() } }; cgNode->setParent(scene()->sceneGraphNode("SolarSystem")); rosettaNode->setEphemeris(new SpiceEphemeris(rosettaDictionary)); cgNode->setEphemeris(new StaticEphemeris); return; } LFATAL("This function is being misused with an argument of '" << origin << "'"); } void RenderEngine::setDisableRenderingOnMaster(bool enabled) { _disableMasterRendering = enabled; } RenderEngine::RendererImplementation RenderEngine::rendererFromString(const std::string& impl) { const std::map RenderingMethods = { { "ABuffer", RendererImplementation::ABuffer }, { "Framebuffer", RendererImplementation::Framebuffer } }; if (RenderingMethods.find(impl) != RenderingMethods.end()) return RenderingMethods.at(impl); else return RendererImplementation::Invalid; } void RenderEngine::renderInformation() { // TODO: Adjust font_size properly when using retina screen const float fontSizeMono = 10.f; const float fontSizeTime = 15.f; using Font = ghoul::fontrendering::Font; using ghoul::fontrendering::RenderFont; if (_showInfo && _fontDate && _fontInfo) { double currentTime = Time::ref().currentTime(); glm::vec2 penPosition = glm::vec2( 10.f, OsEng.windowWrapper().viewportPixelCoordinates().w ); penPosition.y -= _fontDate->height(); RenderFontCr(*_fontDate, penPosition, "Date: %s", Time::ref().currentTimeUTC().c_str() ); RenderFontCr(*_fontInfo, penPosition, "Simulation increment (s): %.0f", Time::ref().deltaTime() ); RenderFontCr(*_fontInfo, penPosition, "Avg. Frametime: %.5f", OsEng.windowWrapper().averageDeltaTime() ); #ifdef OPENSPACE_MODULE_NEWHORIZONS_ENABLED if (openspace::ImageSequencer2::ref().isReady()) { penPosition.y -= 25.f; glm::vec4 targetColor(0.00, 0.75, 1.00, 1); double lt; glm::dvec3 p = SpiceManager::ref().targetPosition("PLUTO", "NEW HORIZONS", "GALACTIC", {}, currentTime, lt); psc nhPos = PowerScaledCoordinate::CreatePowerScaledCoordinate(p.x, p.y, p.z); float a, b, c; glm::dvec3 radii; SpiceManager::ref().getValue("PLUTO", "RADII", radii); a = radii.x; b = radii.y; c = radii.z; float radius = (a + b) / 2.f; float distToSurf = glm::length(nhPos.vec3()) - radius; RenderFont(*_fontInfo, penPosition, "Distance to Pluto: % .1f (KM)", distToSurf ); penPosition.y -= _fontInfo->height(); double remaining = openspace::ImageSequencer2::ref().getNextCaptureTime() - currentTime; float t = static_cast(1.0 - remaining / openspace::ImageSequencer2::ref().getIntervalLength()); std::string progress = "|"; int g = static_cast((t * 24) + 1); g = std::max(g, 0); for (int i = 0; i < g; i++) progress.append("-"); progress.append(">"); for (int i = 0; i < 25 - g; i++) progress.append(" "); std::string str = SpiceManager::ref().dateFromEphemerisTime( ImageSequencer2::ref().getNextCaptureTime(), "YYYY MON DD HR:MN:SC" ); glm::vec4 active(0.6, 1, 0.00, 1); glm::vec4 brigther_active(0.9, 1, 0.75, 1); progress.append("|"); if (remaining > 0) { brigther_active *= (1 - t); RenderFontCr(*_fontInfo, penPosition, active * t + brigther_active, "Next instrument activity:" ); RenderFontCr(*_fontInfo, penPosition, active * t + brigther_active, "%.0f s %s %.1f %%", remaining, progress.c_str(), t * 100 ); RenderFontCr(*_fontInfo, penPosition, active, "Data acquisition time: %s", str.c_str() ); } std::pair nextTarget = ImageSequencer2::ref().getNextTarget(); std::pair currentTarget = ImageSequencer2::ref().getCurrentTarget(); if (currentTarget.first > 0.0) { int timeleft = static_cast(nextTarget.first - currentTime); int hour = timeleft / 3600; int second = timeleft % 3600; int minute = second / 60; second = second % 60; std::string hh, mm, ss, coundtown; if (hour < 10) hh.append("0"); if (minute < 10) mm.append("0"); if (second < 10) ss.append("0"); hh.append(std::to_string(hour)); mm.append(std::to_string(minute)); ss.append(std::to_string(second)); RenderFontCr(*_fontInfo, penPosition, targetColor, "Data acquisition adjacency: [%s:%s:%s]", hh.c_str(), mm.c_str(), ss.c_str() ); std::pair> incidentTargets = ImageSequencer2::ref().getIncidentTargetList(2); std::string space; glm::vec4 color; size_t isize = incidentTargets.second.size(); for (size_t p = 0; p < isize; p++) { double t = static_cast(p + 1) / static_cast(isize + 1); t = (p > isize / 2) ? 1 - t : t; t += 0.3; color = (p == isize / 2) ? targetColor : glm::vec4(t, t, t, 1); RenderFont(*_fontInfo, penPosition, color, "%s%s", space.c_str(), incidentTargets.second[p].c_str() ); for (int k = 0; k < incidentTargets.second[p].size() + 2; k++) space += " "; } penPosition.y -= _fontInfo->height(); std::map activeMap = ImageSequencer2::ref().getActiveInstruments(); glm::vec4 firing(0.58 - t, 1 - t, 1 - t, 1); glm::vec4 notFiring(0.5, 0.5, 0.5, 1); RenderFontCr(*_fontInfo, penPosition, active, "Active Instruments:" ); for (auto t : activeMap) { if (t.second == false) { RenderFont(*_fontInfo, penPosition, glm::vec4(0.3, 0.3, 0.3, 1), "| |" ); RenderFontCr(*_fontInfo, penPosition, glm::vec4(0.3, 0.3, 0.3, 1), " %5s", t.first.c_str() ); } else { RenderFont(*_fontInfo, penPosition, glm::vec4(0.3, 0.3, 0.3, 1), "|" ); if (t.first == "NH_LORRI") { RenderFont(*_fontInfo, penPosition, firing, " + " ); } RenderFont(*_fontInfo, penPosition, glm::vec4(0.3, 0.3, 0.3, 1), " |" ); RenderFontCr(*_fontInfo, penPosition, active, " %5s", t.first.c_str() ); } } } } #endif } } void RenderEngine::renderScreenLog() { _log->removeExpiredEntries(); const int max = 10; const int category_length = 20; const int msg_length = 140; std::chrono::seconds fade(5); auto entries = _log->entries(); auto lastEntries = entries.size() > max ? std::make_pair(entries.rbegin(), entries.rbegin() + max) : std::make_pair(entries.rbegin(), entries.rend()); // if (entries.size() > max) //ScreenLog::const_range ScreenLog::last(size_t n) { // if (_entries.size() > n) { // return std::make_pair(_entries.rbegin(), _entries.rbegin() + n); // } else { // return std::make_pair(_entries.rbegin(), _entries.rend()); // } //} // auto entries = _log->last(max); const glm::vec4 white(0.9, 0.9, 0.9, 1); const glm::vec4 red(1, 0, 0, 1); const glm::vec4 yellow(1, 1, 0, 1); const glm::vec4 green(0, 1, 0, 1); const glm::vec4 blue(0, 0, 1, 1); size_t nr = 1; auto now = std::chrono::steady_clock::now(); for (auto& it = lastEntries.first; it != lastEntries.second; ++it) { const ScreenLog::LogEntry* e = &(*it); std::chrono::duration diff = now - e->timeStamp; float alpha = 1; std::chrono::duration ttf = ScreenLogTimeToLive - fade; if (diff > ttf) { auto d = (diff - ttf).count(); auto t = static_cast(d) / static_cast(fade.count()); float p = 0.8f - t; alpha = (p <= 0.f) ? 0.f : pow(p, 0.3f); } // Since all log entries are ordered, once one exceeds alpha, all have if (alpha <= 0.0) break; const std::string lvl = "(" + ghoul::logging::LogManager::stringFromLevel(e->level) + ")"; const std::string& message = e->message.substr(0, msg_length); nr += std::count(message.begin(), message.end(), '\n'); RenderFont(*_fontLog, glm::vec2(10.f, _fontLog->pointSize() * nr * 2), white * alpha, "%-14s %s%s", // Format e->timeString.c_str(), // Time string e->category.substr(0, category_length).c_str(), // Category string (up to category_length) e->category.length() > 20 ? "..." : ""); // Pad category with "..." if exceeds category_length glm::vec4 color = white; if (e->level == ghoul::logging::LogManager::LogLevel::Debug) color = green; if (e->level == ghoul::logging::LogManager::LogLevel::Warning) color = yellow; if (e->level == ghoul::logging::LogManager::LogLevel::Error) color = red; if (e->level == ghoul::logging::LogManager::LogLevel::Fatal) color = blue; // const float font_with_light = 5; RenderFont(*_fontLog, glm::vec2(static_cast(10 + 39 * _fontLog->pointSize()), _fontLog->pointSize() * nr * 2), color * alpha, "%s", // Format lvl.c_str()); // Pad category with "..." if exceeds category_length RenderFont(*_fontLog, glm::vec2(static_cast(10 + 53 * _fontLog->pointSize()), _fontLog->pointSize() * nr * 2), white * alpha, "%s", // Format message.c_str()); // Pad category with "..." if exceeds category_length ++nr; } } }// namespace openspace