mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-05-13 23:08:54 -05:00
1265 lines
43 KiB
C++
1265 lines
43 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/rendering/renderengine.h>
|
|
|
|
#include <openspace/openspace.h>
|
|
#include <openspace/engine/configuration.h>
|
|
#include <openspace/engine/wrapper/windowwrapper.h>
|
|
#include <openspace/interaction/navigationhandler.h>
|
|
#include <openspace/interaction/orbitalnavigator.h>
|
|
#include <openspace/mission/missionmanager.h>
|
|
#include <openspace/performance/performancemanager.h>
|
|
#include <openspace/performance/performancemeasurement.h>
|
|
#include <openspace/rendering/abufferrenderer.h>
|
|
#include <openspace/rendering/dashboard.h>
|
|
#include <openspace/rendering/deferredcastermanager.h>
|
|
#include <openspace/rendering/framebufferrenderer.h>
|
|
#include <openspace/rendering/luaconsole.h>
|
|
#include <openspace/rendering/raycastermanager.h>
|
|
#include <openspace/rendering/screenspacerenderable.h>
|
|
#include <openspace/scene/scene.h>
|
|
#include <openspace/scripting/scriptengine.h>
|
|
#include <openspace/util/timemanager.h>
|
|
#include <openspace/util/screenlog.h>
|
|
#include <openspace/util/updatestructures.h>
|
|
#include <ghoul/filesystem/filesystem.h>
|
|
#include <ghoul/font/fontmanager.h>
|
|
#include <ghoul/font/fontrenderer.h>
|
|
#include <ghoul/io/texture/texturereader.h>
|
|
#include <ghoul/io/texture/texturereadercmap.h>
|
|
#include <ghoul/logging/logmanager.h>
|
|
#include <ghoul/opengl/programobject.h>
|
|
#include <ghoul/systemcapabilities/openglcapabilitiescomponent.h>
|
|
|
|
#ifdef GHOUL_USE_DEVIL
|
|
#include <ghoul/io/texture/texturereaderdevil.h>
|
|
#endif //GHOUL_USE_DEVIL
|
|
#ifdef GHOUL_USE_FREEIMAGE
|
|
#include <ghoul/io/texture/texturereaderfreeimage.h>
|
|
#endif // GHOUL_USE_FREEIMAGE
|
|
|
|
#ifdef GHOUL_USE_SOIL
|
|
#include <ghoul/io/texture/texturereadersoil.h>
|
|
#include <ghoul/io/texture/texturewriter.h>
|
|
#include <ghoul/io/texture/texturewritersoil.h>
|
|
#endif //GHOUL_USE_SOIL
|
|
|
|
#ifdef GHOUL_USE_STB_IMAGE
|
|
#include <ghoul/io/texture/texturereaderstb.h>
|
|
#endif // GHOUL_USE_STB_IMAGE
|
|
|
|
#include "renderengine_lua.inl"
|
|
|
|
namespace {
|
|
constexpr const char* _loggerCat = "RenderEngine";
|
|
|
|
constexpr const std::chrono::seconds ScreenLogTimeToLive(15);
|
|
constexpr const char* RenderFsPath = "${SHADERS}/render.frag";
|
|
|
|
constexpr const char* KeyFontMono = "Mono";
|
|
constexpr const char* KeyFontLight = "Light";
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo PerformanceInfo = {
|
|
"PerformanceMeasurements",
|
|
"Performance Measurements",
|
|
"If this value is enabled, detailed performance measurements about the updates "
|
|
"and rendering of the scene graph nodes are collected each frame. These values "
|
|
"provide some information about the impact of individual nodes on the overall "
|
|
"performance."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo ShowDateInfo = {
|
|
"ShowDate",
|
|
"Show Date Information",
|
|
"This values determines whether the date will be printed in the top left corner "
|
|
"of the rendering window if a regular rendering window is used (as opposed to a "
|
|
"fisheye rendering, for example)."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo ShowInfoInfo = {
|
|
"ShowInfo",
|
|
"Show Rendering Information",
|
|
"This value determines whether the rendering info, which is the delta time and "
|
|
"the frame time, is shown in the top left corner of the rendering window if a "
|
|
"regular rendering window is used (as opposed to a fisheye rendering, for "
|
|
"example)."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo ShowOverlaySlavesInfo = {
|
|
"ShowOverlayOnSlaves",
|
|
"Show Overlay Information on Slaves",
|
|
"If this value is enabled, the overlay information text is also automatically "
|
|
"rendered on the slave nodes. This values is disabled by default."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo ShowLogInfo = {
|
|
"ShowLog",
|
|
"Show the on-screen log",
|
|
"This value determines whether the on-screen log will be shown or hidden. Even "
|
|
"if it is shown, all 'Debug' and 'Trace' level messages are omitted from this "
|
|
"log."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo ShowVersionInfo = {
|
|
"ShowVersion",
|
|
"Shows the version on-screen information",
|
|
"This value determines whether the Git version information (branch and commit) "
|
|
"hash are shown on the screen."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo ShowCameraInfo = {
|
|
"ShowCamera",
|
|
"Shows information about the current camera state, such as friction",
|
|
"This value determines whether the information about the current camrea state is "
|
|
"shown on the screen"
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo TakeScreenshotInfo = {
|
|
"TakeScreenshot",
|
|
"Take Screenshot",
|
|
"If this property is triggered, a screenshot is taken and stored in the current "
|
|
"working directory (which is the same directory where the OpenSpace.exe) is "
|
|
"located in most cases. The images are prefixed with 'SGCT' and postfixed with "
|
|
"the number of frames. This function will silently overwrite images that are "
|
|
"already present in the folder."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo ApplyWarpingInfo = {
|
|
"ApplyWarpingScreenshot",
|
|
"Apply Warping to Screenshots",
|
|
"This value determines whether a warping should be applied before taking a "
|
|
"screenshot. If it is enabled, all post processing is applied as well, which "
|
|
"includes everything rendered on top of the rendering, such as the user "
|
|
"interface."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo ShowFrameNumberInfo = {
|
|
"ShowFrameNumber",
|
|
"Show Frame Number",
|
|
"If this value is enabled, the current frame number is rendered into the window."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo DisableMasterInfo = {
|
|
"DisableMasterRendering",
|
|
"Disable Master Rendering",
|
|
"If this value is enabled, the rendering on the master node will be disabled. "
|
|
"Every other aspect of the application will be unaffected by this and it will "
|
|
"still respond to user input. This setting is reasonably only useful in the case "
|
|
"of multi-pipeline environments, such as planetariums, where the output of the "
|
|
"master node is not required and performance can be gained by disabling it."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo DisableTranslationInfo = {
|
|
"DisableSceneTranslationOnMaster",
|
|
"Disable Scene Translation on Master",
|
|
"If this value is enabled, any scene translations such as specified in, for "
|
|
"example an SGCT configuration, is disabled for the master node. This setting "
|
|
"can be useful if a planetarium environment requires a scene translation to be "
|
|
"applied, which would otherwise make interacting through the master node "
|
|
"difficult."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo AaSamplesInfo = {
|
|
"AaSamples",
|
|
"Number of Anti-aliasing samples",
|
|
"This value determines the number of anti-aliasing samples to be used in the "
|
|
"rendering for the MSAA method."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo HDRExposureInfo = {
|
|
"HDRExposure",
|
|
"HDR Exposure",
|
|
"This value determines the amount of light per unit area reaching the "
|
|
"equivalent of an electronic image sensor."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo BackgroundExposureInfo = {
|
|
"Background Exposure",
|
|
"BackgroundExposure",
|
|
"This value determines the amount of light per unit area reaching the "
|
|
"equivalent of an electronic image sensor for the background image."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo GammaInfo = {
|
|
"Gamma",
|
|
"Gamma Correction",
|
|
"Gamma, is the nonlinear operation used to encode and decode luminance or "
|
|
"tristimulus values in the image."
|
|
};
|
|
} // namespace
|
|
|
|
|
|
namespace openspace {
|
|
|
|
RenderEngine::RenderEngine()
|
|
: properties::PropertyOwner({ "RenderEngine" })
|
|
, _doPerformanceMeasurements(PerformanceInfo)
|
|
, _showOverlayOnSlaves(ShowOverlaySlavesInfo, false)
|
|
, _showLog(ShowLogInfo, true)
|
|
, _showVersionInfo(ShowVersionInfo, true)
|
|
, _showCameraInfo(ShowCameraInfo, true)
|
|
, _takeScreenshot(TakeScreenshotInfo)
|
|
, _applyWarping(ApplyWarpingInfo, false)
|
|
, _showFrameNumber(ShowFrameNumberInfo, false)
|
|
, _disableMasterRendering(DisableMasterInfo, false)
|
|
, _disableSceneTranslationOnMaster(DisableTranslationInfo, false)
|
|
, _nAaSamples(AaSamplesInfo, 4, 1, 8)
|
|
, _hdrExposure(HDRExposureInfo, 0.4f, 0.01f, 10.0f)
|
|
, _hdrBackground(BackgroundExposureInfo, 2.8f, 0.01f, 10.0f)
|
|
, _gamma(GammaInfo, 2.2f, 0.01f, 10.0f)
|
|
, _screenSpaceOwner({ "ScreenSpace" })
|
|
{
|
|
_doPerformanceMeasurements.onChange([this](){
|
|
if (_doPerformanceMeasurements) {
|
|
if (!_performanceManager) {
|
|
_performanceManager = std::make_shared<performance::PerformanceManager>(
|
|
OsEng.configuration().logging.directory,
|
|
OsEng.configuration().logging.performancePrefix
|
|
);
|
|
}
|
|
}
|
|
else {
|
|
_performanceManager = nullptr;
|
|
}
|
|
|
|
});
|
|
addProperty(_doPerformanceMeasurements);
|
|
|
|
addProperty(_showOverlayOnSlaves);
|
|
addProperty(_showLog);
|
|
addProperty(_showVersionInfo);
|
|
addProperty(_showCameraInfo);
|
|
|
|
_nAaSamples.onChange([this](){
|
|
if (_renderer) {
|
|
_renderer->setNAaSamples(_nAaSamples);
|
|
}
|
|
});
|
|
addProperty(_nAaSamples);
|
|
|
|
_hdrExposure.onChange([this]() {
|
|
if (_renderer) {
|
|
_renderer->setHDRExposure(_hdrExposure);
|
|
}
|
|
});
|
|
addProperty(_hdrExposure);
|
|
|
|
_hdrBackground.onChange([this]() {
|
|
if (_renderer) {
|
|
_renderer->setHDRBackground(_hdrBackground);
|
|
}
|
|
});
|
|
addProperty(_hdrBackground);
|
|
|
|
_gamma.onChange([this]() {
|
|
if (_renderer) {
|
|
_renderer->setGamma(_gamma);
|
|
}
|
|
});
|
|
addProperty(_gamma);
|
|
|
|
addProperty(_applyWarping);
|
|
|
|
_takeScreenshot.onChange([this](){
|
|
_shouldTakeScreenshot = true;
|
|
});
|
|
addProperty(_takeScreenshot);
|
|
|
|
addProperty(_showFrameNumber);
|
|
|
|
addProperty(_disableSceneTranslationOnMaster);
|
|
addProperty(_disableMasterRendering);
|
|
}
|
|
|
|
RenderEngine::~RenderEngine() {} // NOLINT
|
|
|
|
void RenderEngine::setRendererFromString(const std::string& renderingMethod) {
|
|
_rendererImplementation = rendererFromString(renderingMethod);
|
|
|
|
std::unique_ptr<Renderer> newRenderer = nullptr;
|
|
switch (_rendererImplementation) {
|
|
case RendererImplementation::Framebuffer:
|
|
newRenderer = std::make_unique<FramebufferRenderer>();
|
|
break;
|
|
case RendererImplementation::ABuffer:
|
|
#ifdef OPENSPACE_WITH_ABUFFER_RENDERER
|
|
newRenderer = std::make_unique<ABufferRenderer>();
|
|
#endif // OPENSPACE_WITH_ABUFFER_RENDERER
|
|
break;
|
|
case RendererImplementation::Invalid:
|
|
LFATAL(fmt::format("Rendering method '{}' not available", renderingMethod));
|
|
return;
|
|
}
|
|
|
|
setRenderer(std::move(newRenderer));
|
|
}
|
|
|
|
void RenderEngine::initialize() {
|
|
std::string renderingMethod = OsEng.configuration().renderingMethod;
|
|
if (renderingMethod == "ABuffer") {
|
|
using Version = ghoul::systemcapabilities::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";
|
|
}
|
|
}
|
|
|
|
// We have to perform these initializations here as the OsEng has not been initialized
|
|
// in our constructor
|
|
_disableSceneTranslationOnMaster =
|
|
OsEng.configuration().isSceneTranslationOnMasterDisabled;
|
|
_disableMasterRendering = OsEng.configuration().isRenderingOnMasterDisabled;
|
|
|
|
_raycasterManager = std::make_unique<RaycasterManager>();
|
|
_deferredcasterManager = std::make_unique<DeferredcasterManager>();
|
|
_nAaSamples = OsEng.windowWrapper().currentNumberOfAaSamples();
|
|
|
|
LINFO(fmt::format("Setting renderer from string: {}", renderingMethod));
|
|
setRendererFromString(renderingMethod);
|
|
|
|
#ifdef GHOUL_USE_DEVIL
|
|
ghoul::io::TextureReader::ref().addReader(
|
|
std::make_unique<ghoul::io::TextureReaderDevIL>()
|
|
);
|
|
#endif // GHOUL_USE_DEVIL
|
|
#ifdef GHOUL_USE_FREEIMAGE
|
|
ghoul::io::TextureReader::ref().addReader(
|
|
std::make_unique<ghoul::io::TextureReaderFreeImage>()
|
|
);
|
|
#endif // GHOUL_USE_FREEIMAGE
|
|
#ifdef GHOUL_USE_SOIL
|
|
ghoul::io::TextureReader::ref().addReader(
|
|
std::make_unique<ghoul::io::TextureReaderSOIL>()
|
|
);
|
|
ghoul::io::TextureWriter::ref().addWriter(
|
|
std::make_unique<ghoul::io::TextureWriterSOIL>()
|
|
);
|
|
#endif // GHOUL_USE_SOIL
|
|
#ifdef GHOUL_USE_STB_IMAGE
|
|
ghoul::io::TextureReader::ref().addReader(
|
|
std::make_unique<ghoul::io::TextureReaderSTB>()
|
|
);
|
|
#endif // GHOUL_USE_STB_IMAGE
|
|
|
|
ghoul::io::TextureReader::ref().addReader(
|
|
std::make_unique<ghoul::io::TextureReaderCMAP>()
|
|
);
|
|
|
|
MissionManager::initialize();
|
|
}
|
|
|
|
void RenderEngine::initializeGL() {
|
|
LTRACE("RenderEngine::initializeGL(begin)");
|
|
// 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);
|
|
|
|
constexpr const float FontSizeBig = 50.f;
|
|
_fontBig = OsEng.fontManager().font(KeyFontMono, FontSizeBig);
|
|
constexpr const float FontSizeTime = 15.f;
|
|
_fontDate = OsEng.fontManager().font(KeyFontMono, FontSizeTime);
|
|
constexpr const float FontSizeMono = 10.f;
|
|
_fontInfo = OsEng.fontManager().font(KeyFontMono, FontSizeMono);
|
|
constexpr const float FontSizeLight = 8.f;
|
|
_fontLog = OsEng.fontManager().font(KeyFontLight, FontSizeLight);
|
|
|
|
LINFO("Initializing Log");
|
|
std::unique_ptr<ScreenLog> log = std::make_unique<ScreenLog>(ScreenLogTimeToLive);
|
|
_log = log.get();
|
|
ghoul::logging::LogManager::ref().addLog(std::move(log));
|
|
|
|
for (std::unique_ptr<ScreenSpaceRenderable>& ssr : _screenSpaceRenderables) {
|
|
ssr->initializeGL();
|
|
}
|
|
|
|
LINFO("Finished initializing GL");
|
|
LTRACE("RenderEngine::initializeGL(end)");
|
|
}
|
|
|
|
void RenderEngine::deinitialize() {
|
|
for (std::unique_ptr<ScreenSpaceRenderable>& ssr : _screenSpaceRenderables) {
|
|
ssr->deinitialize();
|
|
}
|
|
|
|
MissionManager::deinitialize();
|
|
}
|
|
|
|
void RenderEngine::deinitializeGL() {
|
|
for (std::unique_ptr<ScreenSpaceRenderable>& ssr : _screenSpaceRenderables) {
|
|
ssr->deinitializeGL();
|
|
}
|
|
}
|
|
|
|
void RenderEngine::updateScene() {
|
|
if (!_scene) {
|
|
return;
|
|
}
|
|
|
|
_scene->updateInterpolations();
|
|
|
|
const Time& currentTime = OsEng.timeManager().time();
|
|
const Time& integrateFromTime = OsEng.timeManager().integrateFromTime();
|
|
|
|
_scene->update({
|
|
{ glm::dvec3(0.0), glm::dmat3(11.), 1.0 },
|
|
currentTime,
|
|
integrateFromTime,
|
|
_performanceManager != nullptr
|
|
});
|
|
|
|
LTRACE("RenderEngine::updateSceneGraph(end)");
|
|
}
|
|
|
|
void RenderEngine::updateShaderPrograms() {
|
|
for (ghoul::opengl::ProgramObject* program : _programs) {
|
|
try {
|
|
if (program->isDirty()) {
|
|
program->rebuildFromFile();
|
|
}
|
|
}
|
|
catch (const ghoul::opengl::ShaderObject::ShaderCompileError& e) {
|
|
LERRORC(e.component, e.what());
|
|
}
|
|
}
|
|
}
|
|
|
|
void RenderEngine::updateRenderer() {
|
|
const bool windowResized = OsEng.windowWrapper().windowHasResized();
|
|
|
|
if (windowResized) {
|
|
_renderer->setResolution(renderingResolution());
|
|
|
|
using FR = ghoul::fontrendering::FontRenderer;
|
|
FR::defaultRenderer().setFramebufferSize(fontResolution());
|
|
FR::defaultProjectionRenderer().setFramebufferSize(renderingResolution());
|
|
}
|
|
|
|
_renderer->update();
|
|
}
|
|
|
|
void RenderEngine::updateScreenSpaceRenderables() {
|
|
for (std::unique_ptr<ScreenSpaceRenderable>& ssr : _screenSpaceRenderables) {
|
|
ssr->update();
|
|
}
|
|
}
|
|
|
|
glm::ivec2 RenderEngine::renderingResolution() const {
|
|
if (OsEng.windowWrapper().isRegularRendering()) {
|
|
return OsEng.windowWrapper().currentWindowResolution();
|
|
}
|
|
else {
|
|
return OsEng.windowWrapper().currentDrawBufferResolution();
|
|
}
|
|
}
|
|
|
|
glm::ivec2 RenderEngine::fontResolution() const {
|
|
const std::string& value = OsEng.configuration().onScreenTextScaling;
|
|
if (value == "framebuffer") {
|
|
return OsEng.windowWrapper().getCurrentViewportSize();
|
|
//return OsEng.windowWrapper().currentWindowResolution();
|
|
}
|
|
else {
|
|
return OsEng.windowWrapper().currentWindowSize();
|
|
}
|
|
}
|
|
|
|
void RenderEngine::updateFade() {
|
|
// Temporary fade funtionality
|
|
constexpr const float FadedIn = 1.0;
|
|
constexpr const float FadedOut = 0.0;
|
|
// Don't restart the fade if you've already done it in that direction
|
|
const bool isFadedIn = (_fadeDirection > 0 && _globalBlackOutFactor == FadedIn);
|
|
const bool isFadedOut = (_fadeDirection < 0 && _globalBlackOutFactor == FadedOut);
|
|
if (isFadedIn || isFadedOut) {
|
|
_fadeDirection = 0;
|
|
}
|
|
|
|
if (_fadeDirection != 0) {
|
|
if (_currentFadeTime > _fadeDuration) {
|
|
_globalBlackOutFactor = _fadeDirection > 0 ? FadedIn : FadedOut;
|
|
_fadeDirection = 0;
|
|
}
|
|
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<float>(
|
|
OsEng.windowWrapper().averageDeltaTime()
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RenderEngine::render(const glm::mat4& sceneMatrix, const glm::mat4& viewMatrix,
|
|
const glm::mat4& projectionMatrix)
|
|
{
|
|
LTRACE("RenderEngine::render(begin)");
|
|
const WindowWrapper& wrapper = OsEng.windowWrapper();
|
|
if (_camera) {
|
|
if (_disableSceneTranslationOnMaster && wrapper.isMaster()) {
|
|
_camera->sgctInternal.setViewMatrix(viewMatrix);
|
|
}
|
|
else {
|
|
_camera->sgctInternal.setViewMatrix(viewMatrix * sceneMatrix);
|
|
_camera->sgctInternal.setSceneMatrix(sceneMatrix);
|
|
}
|
|
_camera->sgctInternal.setProjectionMatrix(projectionMatrix);
|
|
}
|
|
|
|
const bool masterEnabled = wrapper.isMaster() ? !_disableMasterRendering : true;
|
|
if (masterEnabled && !wrapper.isGuiWindow() && _globalBlackOutFactor > 0.f) {
|
|
_renderer->render(
|
|
_scene,
|
|
_camera,
|
|
_globalBlackOutFactor,
|
|
_performanceManager != nullptr
|
|
);
|
|
}
|
|
|
|
if (_showFrameNumber) {
|
|
glm::vec2 penPosition = glm::vec2(
|
|
fontResolution().x / 2 - 50,
|
|
fontResolution().y / 3
|
|
);
|
|
|
|
RenderFont(*_fontBig, penPosition, std::to_string(_frameNumber));
|
|
}
|
|
|
|
++_frameNumber;
|
|
|
|
for (std::unique_ptr<ScreenSpaceRenderable>& ssr : _screenSpaceRenderables) {
|
|
if (ssr->isEnabled() && ssr->isReady()) {
|
|
ssr->render();
|
|
}
|
|
}
|
|
LTRACE("RenderEngine::render(end)");
|
|
}
|
|
|
|
bool RenderEngine::mouseActivationCallback(const glm::dvec2& mousePosition) const {
|
|
auto intersects = [](const glm::dvec2& mousePos, const glm::ivec4& bbox) {
|
|
return mousePos.x >= bbox.x && mousePos.x <= bbox.x + bbox.z &&
|
|
mousePos.y <= bbox.y && mousePos.y >= bbox.y - bbox.w;
|
|
};
|
|
|
|
|
|
if (intersects(mousePosition, _cameraButtonLocations.rotation)) {
|
|
constexpr const char* ToggleRotationFrictionScript = R"(
|
|
local f = 'NavigationHandler.OrbitalNavigator.Friction.RotationalFriction';
|
|
openspace.setPropertyValue(f, not openspace.getPropertyValue(f));)";
|
|
|
|
OsEng.scriptEngine().queueScript(
|
|
ToggleRotationFrictionScript,
|
|
scripting::ScriptEngine::RemoteScripting::Yes
|
|
);
|
|
return true;
|
|
}
|
|
|
|
if (intersects(mousePosition, _cameraButtonLocations.zoom)) {
|
|
constexpr const char* ToggleZoomFrictionScript = R"(
|
|
local f = 'NavigationHandler.OrbitalNavigator.Friction.ZoomFriction';
|
|
openspace.setPropertyValue(f, not openspace.getPropertyValue(f));)";
|
|
|
|
OsEng.scriptEngine().queueScript(
|
|
ToggleZoomFrictionScript,
|
|
scripting::ScriptEngine::RemoteScripting::Yes
|
|
);
|
|
return true;
|
|
}
|
|
|
|
if (intersects(mousePosition, _cameraButtonLocations.roll)) {
|
|
constexpr const char* ToggleRollFrictionScript = R"(
|
|
local f = 'NavigationHandler.OrbitalNavigator.Friction.RollFriction';
|
|
openspace.setPropertyValue(f, not openspace.getPropertyValue(f));)";
|
|
|
|
OsEng.scriptEngine().queueScript(
|
|
ToggleRollFrictionScript,
|
|
scripting::ScriptEngine::RemoteScripting::Yes
|
|
);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void RenderEngine::renderOverlays(const ShutdownInformation& shutdownInfo) {
|
|
const bool isMaster = OsEng.windowWrapper().isMaster();
|
|
if (isMaster || _showOverlayOnSlaves) {
|
|
renderScreenLog();
|
|
renderVersionInformation();
|
|
renderDashboard();
|
|
|
|
if (!shutdownInfo.inShutdown) {
|
|
// We render the camera information in the same location as the shutdown info
|
|
// and we won't need this if we are shutting down
|
|
renderCameraInformation();
|
|
}
|
|
else {
|
|
// If we are in shutdown mode, we can display the remaining time
|
|
renderShutdownInformation(shutdownInfo.timer, shutdownInfo.waitTime);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RenderEngine::renderEndscreen() {
|
|
glClearColor(0.f, 0.f, 0.f, 0.25f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
using FR = ghoul::fontrendering::FontRenderer;
|
|
using BBox = FR::BoundingBoxInformation;
|
|
BBox size = FR::defaultRenderer().boundingBox(
|
|
*_fontDate,
|
|
"Shutting down"
|
|
);
|
|
glm::vec2 penPosition = glm::vec2(
|
|
fontResolution().x / 2 - size.boundingBox.x / 2,
|
|
fontResolution().y / 2- size.boundingBox.y / 2
|
|
);
|
|
RenderFont(*_fontDate, penPosition, "Shutting down");
|
|
}
|
|
|
|
void RenderEngine::renderShutdownInformation(float timer, float fullTime) {
|
|
timer = std::max(timer, 0.f);
|
|
|
|
using BBox = ghoul::fontrendering::FontRenderer::BoundingBoxInformation;
|
|
BBox size = ghoul::fontrendering::FontRenderer::defaultRenderer().boundingBox(
|
|
*_fontDate,
|
|
fmt::format("Shutdown in: {:.2f}s/{:.2f}s", timer, fullTime)
|
|
);
|
|
|
|
glm::vec2 penPosition = glm::vec2(
|
|
fontResolution().x - size.boundingBox.x - 10,
|
|
fontResolution().y - size.boundingBox.y
|
|
);
|
|
|
|
RenderFont(
|
|
*_fontDate,
|
|
penPosition,
|
|
fmt::format("Shutdown in: {:.2f}s/{:.2f}s", timer, fullTime),
|
|
ghoul::fontrendering::CrDirection::Down
|
|
);
|
|
|
|
RenderFont(
|
|
*_fontDate,
|
|
penPosition,
|
|
// Important: length of this string is the same as the shutdown time text
|
|
// to make them align
|
|
"Press ESC again to abort",
|
|
ghoul::fontrendering::CrDirection::Down
|
|
);
|
|
}
|
|
|
|
void RenderEngine::renderDashboard() {
|
|
std::unique_ptr<performance::PerformanceMeasurement> perf;
|
|
if (_performanceManager) {
|
|
perf = std::make_unique<performance::PerformanceMeasurement>(
|
|
"Main Dashboard::render",
|
|
OsEng.renderEngine().performanceManager()
|
|
);
|
|
}
|
|
glm::vec2 penPosition = glm::vec2(
|
|
10.f,
|
|
fontResolution().y - OsEng.console().currentHeight()
|
|
);
|
|
|
|
OsEng.dashboard().render(penPosition);
|
|
}
|
|
|
|
void RenderEngine::postDraw() {
|
|
const Time& currentTime = OsEng.timeManager().time();
|
|
|
|
if (_shouldTakeScreenshot) {
|
|
// We only create the directory here, as we don't want to spam the users
|
|
// screenshot folder everytime we start OpenSpace even when we are not taking any
|
|
// screenshots. So the first time we actually take one, we create the folder:
|
|
if (!FileSys.directoryExists(absPath("${THIS_SCREENSHOT_PATH}"))) {
|
|
FileSys.createDirectory(
|
|
absPath("${THIS_SCREENSHOT_PATH}"),
|
|
ghoul::filesystem::FileSystem::Recursive::Yes
|
|
);
|
|
}
|
|
|
|
OsEng.windowWrapper().takeScreenshot(_applyWarping);
|
|
_shouldTakeScreenshot = false;
|
|
}
|
|
|
|
if (_performanceManager) {
|
|
_performanceManager->storeScenePerformanceMeasurements(
|
|
scene()->allSceneGraphNodes()
|
|
);
|
|
}
|
|
}
|
|
|
|
Scene* RenderEngine::scene() {
|
|
return _scene;
|
|
}
|
|
|
|
RaycasterManager& RenderEngine::raycasterManager() {
|
|
return *_raycasterManager;
|
|
}
|
|
|
|
DeferredcasterManager& RenderEngine::deferredcasterManager() {
|
|
return *_deferredcasterManager;
|
|
}
|
|
|
|
void RenderEngine::setScene(Scene* scene) {
|
|
_scene = scene;
|
|
}
|
|
|
|
void RenderEngine::setCamera(Camera* camera) {
|
|
_camera = camera;
|
|
}
|
|
|
|
const Renderer& RenderEngine::renderer() const {
|
|
return *_renderer;
|
|
}
|
|
|
|
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<ghoul::opengl::ProgramObject> RenderEngine::buildRenderProgram(
|
|
const std::string& name,
|
|
const std::string& vsPath,
|
|
std::string fsPath,
|
|
ghoul::Dictionary data)
|
|
{
|
|
ghoul::Dictionary dict = std::move(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", std::move(fsPath));
|
|
|
|
using namespace ghoul::opengl;
|
|
std::unique_ptr<ProgramObject> program = ProgramObject::Build(
|
|
name,
|
|
vsPath,
|
|
absPath(RenderFsPath),
|
|
std::move(dict)
|
|
);
|
|
|
|
if (program) {
|
|
_programs.push_back(program.get());
|
|
}
|
|
return program;
|
|
}
|
|
|
|
/**
|
|
* Build a program object for rendering with the used renderer
|
|
*/
|
|
std::unique_ptr<ghoul::opengl::ProgramObject> RenderEngine::buildRenderProgram(
|
|
const std::string& name,
|
|
const std::string& vsPath,
|
|
std::string fsPath,
|
|
const std::string& csPath,
|
|
ghoul::Dictionary data)
|
|
{
|
|
ghoul::Dictionary dict = std::move(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", std::move(fsPath));
|
|
|
|
using namespace ghoul::opengl;
|
|
std::unique_ptr<ProgramObject> program = ProgramObject::Build(
|
|
name,
|
|
vsPath,
|
|
absPath(RenderFsPath),
|
|
csPath,
|
|
std::move(dict)
|
|
);
|
|
|
|
if (program) {
|
|
_programs.push_back(program.get());
|
|
}
|
|
return program;
|
|
}
|
|
|
|
void RenderEngine::removeRenderProgram(ghoul::opengl::ProgramObject* program) {
|
|
if (!program) {
|
|
return;
|
|
}
|
|
|
|
auto it = std::find(
|
|
_programs.begin(),
|
|
_programs.end(),
|
|
program
|
|
);
|
|
|
|
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(ghoul::Dictionary rendererData) {
|
|
_rendererData = std::move(rendererData);
|
|
for (ghoul::opengl::ProgramObject* program : _programs) {
|
|
ghoul::Dictionary dict = program->dictionary();
|
|
dict.setValue("rendererData", _rendererData);
|
|
program->setDictionary(dict);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set resolve data
|
|
* Called from the renderer, whenever it needs to update
|
|
* the dictionary of all post rendering programs.
|
|
*/
|
|
void RenderEngine::setResolveData(ghoul::Dictionary resolveData) {
|
|
_resolveData = std::move(resolveData);
|
|
for (ghoul::opengl::ProgramObject* program : _programs) {
|
|
ghoul::Dictionary dict = program->dictionary();
|
|
dict.setValue("resolveData", _resolveData);
|
|
program->setDictionary(dict);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set raycasting uniforms on the program object, and setup raycasting.
|
|
*/
|
|
void RenderEngine::preRaycast(ghoul::opengl::ProgramObject& programObject) {
|
|
_renderer->preRaycast(programObject);
|
|
}
|
|
|
|
/**
|
|
* Tear down raycasting for the specified program object.
|
|
*/
|
|
void RenderEngine::postRaycast(ghoul::opengl::ProgramObject& programObject) {
|
|
_renderer->postRaycast(programObject);
|
|
}
|
|
|
|
/**
|
|
* Set renderer
|
|
*/
|
|
void RenderEngine::setRenderer(std::unique_ptr<Renderer> renderer) {
|
|
if (_renderer) {
|
|
_renderer->deinitialize();
|
|
}
|
|
|
|
_renderer = std::move(renderer);
|
|
_renderer->setResolution(renderingResolution());
|
|
_renderer->setNAaSamples(_nAaSamples);
|
|
_renderer->setHDRExposure(_hdrExposure);
|
|
_renderer->initialize();
|
|
}
|
|
|
|
scripting::LuaLibrary RenderEngine::luaLibrary() {
|
|
return {
|
|
"",
|
|
{
|
|
{
|
|
"setRenderer",
|
|
&luascriptfunctions::setRenderer,
|
|
{},
|
|
"string",
|
|
"Sets the renderer (ABuffer or FrameBuffer)"
|
|
},
|
|
{
|
|
"toggleFade",
|
|
&luascriptfunctions::toggleFade,
|
|
{},
|
|
"number",
|
|
"Toggles fading in or out"
|
|
},
|
|
{
|
|
"fadeIn",
|
|
&luascriptfunctions::fadeIn,
|
|
{},
|
|
"number",
|
|
""
|
|
},
|
|
{
|
|
"fadeOut",
|
|
&luascriptfunctions::fadeOut,
|
|
{},
|
|
"number",
|
|
""
|
|
},
|
|
{
|
|
"addScreenSpaceRenderable",
|
|
&luascriptfunctions::addScreenSpaceRenderable,
|
|
{},
|
|
"table",
|
|
"Will create a ScreenSpaceRenderable from a lua Table and add it in the "
|
|
"RenderEngine"
|
|
},
|
|
{
|
|
"removeScreenSpaceRenderable",
|
|
&luascriptfunctions::removeScreenSpaceRenderable,
|
|
{},
|
|
"string",
|
|
"Given a ScreenSpaceRenderable name this script will remove it from the "
|
|
"renderengine"
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
bool RenderEngine::doesPerformanceMeasurements() const {
|
|
return _performanceManager != nullptr;
|
|
}
|
|
|
|
std::shared_ptr<performance::PerformanceManager> RenderEngine::performanceManager() {
|
|
return _performanceManager;
|
|
}
|
|
|
|
void RenderEngine::addScreenSpaceRenderable(std::unique_ptr<ScreenSpaceRenderable> s) {
|
|
s->initialize();
|
|
s->initializeGL();
|
|
|
|
_screenSpaceOwner.addPropertySubOwner(s.get());
|
|
|
|
_screenSpaceRenderables.push_back(std::move(s));
|
|
}
|
|
|
|
void RenderEngine::removeScreenSpaceRenderable(ScreenSpaceRenderable* s) {
|
|
const auto it = std::find_if(
|
|
_screenSpaceRenderables.begin(),
|
|
_screenSpaceRenderables.end(),
|
|
[s](const std::unique_ptr<ScreenSpaceRenderable>& r) { return r.get() == s; }
|
|
);
|
|
|
|
if (it != _screenSpaceRenderables.end()) {
|
|
s->deinitialize();
|
|
_screenSpaceOwner.removePropertySubOwner(s);
|
|
|
|
_screenSpaceRenderables.erase(it);
|
|
}
|
|
}
|
|
|
|
void RenderEngine::removeScreenSpaceRenderable(const std::string& identifier) {
|
|
ScreenSpaceRenderable* s = screenSpaceRenderable(identifier);
|
|
if (s) {
|
|
removeScreenSpaceRenderable(s);
|
|
}
|
|
}
|
|
|
|
ScreenSpaceRenderable* RenderEngine::screenSpaceRenderable(
|
|
const std::string& identifier)
|
|
{
|
|
const auto it = std::find_if(
|
|
_screenSpaceRenderables.begin(),
|
|
_screenSpaceRenderables.end(),
|
|
[&identifier](const std::unique_ptr<ScreenSpaceRenderable>& s) {
|
|
return s->identifier() == identifier;
|
|
}
|
|
);
|
|
|
|
if (it != _screenSpaceRenderables.end()) {
|
|
return it->get();
|
|
}
|
|
else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
std::vector<ScreenSpaceRenderable*> RenderEngine::screenSpaceRenderables() const {
|
|
std::vector<ScreenSpaceRenderable*> res(_screenSpaceRenderables.size());
|
|
std::transform(
|
|
_screenSpaceRenderables.begin(),
|
|
_screenSpaceRenderables.end(),
|
|
res.begin(),
|
|
[](const std::unique_ptr<ScreenSpaceRenderable>& p) { return p.get(); }
|
|
);
|
|
return res;
|
|
}
|
|
|
|
RenderEngine::RendererImplementation RenderEngine::rendererFromString(
|
|
const std::string& renderingMethod) const
|
|
{
|
|
const std::map<std::string, RenderEngine::RendererImplementation> RenderingMethods = {
|
|
#ifdef OPENSPACE_WITH_ABUFFER_RENDERER
|
|
{ "ABuffer", RendererImplementation::ABuffer },
|
|
#endif // OPENSPACE_WITH_ABUFFER_RENDERER
|
|
{ "Framebuffer", RendererImplementation::Framebuffer }
|
|
};
|
|
|
|
if (RenderingMethods.find(renderingMethod) != RenderingMethods.end()) {
|
|
return RenderingMethods.at(renderingMethod);
|
|
}
|
|
else {
|
|
return RendererImplementation::Invalid;
|
|
}
|
|
}
|
|
|
|
void RenderEngine::renderCameraInformation() {
|
|
if (!_showCameraInfo) {
|
|
return;
|
|
}
|
|
|
|
const glm::vec4 EnabledColor = glm::vec4(0.2f, 0.75f, 0.2f, 1.f);
|
|
const glm::vec4 DisabledColor = glm::vec4(0.55f, 0.2f, 0.2f, 1.f);
|
|
|
|
using FR = ghoul::fontrendering::FontRenderer;
|
|
const FR::BoundingBoxInformation rotationBox = FR::defaultRenderer().boundingBox(
|
|
*_fontInfo,
|
|
"Rotation"
|
|
);
|
|
|
|
float penPosY = fontResolution().y - rotationBox.boundingBox.y;
|
|
|
|
constexpr const float YSeparation = 5.f;
|
|
constexpr const float XSeparation = 5.f;
|
|
|
|
interaction::OrbitalNavigator nav = OsEng.navigationHandler().orbitalNavigator();
|
|
|
|
_cameraButtonLocations.rotation = {
|
|
fontResolution().x - rotationBox.boundingBox.x - XSeparation,
|
|
fontResolution().y - penPosY,
|
|
rotationBox.boundingBox.x,
|
|
rotationBox.boundingBox.y
|
|
};
|
|
FR::defaultRenderer().render(
|
|
*_fontInfo,
|
|
glm::vec2(fontResolution().x - rotationBox.boundingBox.x - XSeparation, penPosY),
|
|
"Rotation",
|
|
nav.hasRotationalFriction() ? EnabledColor : DisabledColor
|
|
);
|
|
penPosY -= rotationBox.boundingBox.y + YSeparation;
|
|
|
|
FR::BoundingBoxInformation zoomBox = FR::defaultRenderer().boundingBox(
|
|
*_fontInfo,
|
|
"Zoom"
|
|
);
|
|
|
|
_cameraButtonLocations.zoom = {
|
|
fontResolution().x - zoomBox.boundingBox.x - XSeparation,
|
|
fontResolution().y - penPosY,
|
|
zoomBox.boundingBox.x,
|
|
zoomBox.boundingBox.y
|
|
};
|
|
FR::defaultRenderer().render(
|
|
*_fontInfo,
|
|
glm::vec2(fontResolution().x - zoomBox.boundingBox.x - XSeparation, penPosY),
|
|
"Zoom",
|
|
nav.hasZoomFriction() ? EnabledColor : DisabledColor
|
|
);
|
|
penPosY -= zoomBox.boundingBox.y + YSeparation;
|
|
|
|
FR::BoundingBoxInformation rollBox = FR::defaultRenderer().boundingBox(
|
|
*_fontInfo,
|
|
"Roll"
|
|
);
|
|
|
|
_cameraButtonLocations.roll = {
|
|
fontResolution().x - rollBox.boundingBox.x - XSeparation,
|
|
fontResolution().y - penPosY,
|
|
rollBox.boundingBox.x,
|
|
rollBox.boundingBox.y
|
|
};
|
|
FR::defaultRenderer().render(
|
|
*_fontInfo,
|
|
glm::vec2(fontResolution().x - rollBox.boundingBox.x - XSeparation, penPosY),
|
|
"Roll",
|
|
nav.hasRollFriction() ? EnabledColor : DisabledColor
|
|
);
|
|
}
|
|
|
|
void RenderEngine::renderVersionInformation() {
|
|
if (!_showVersionInfo) {
|
|
return;
|
|
}
|
|
|
|
using FR = ghoul::fontrendering::FontRenderer;
|
|
const FR::BoundingBoxInformation versionBox = FR::defaultRenderer().boundingBox(
|
|
*_fontInfo,
|
|
OPENSPACE_VERSION_STRING_FULL
|
|
);
|
|
|
|
const FR::BoundingBoxInformation commitBox = FR::defaultRenderer().boundingBox(
|
|
*_fontInfo,
|
|
fmt::format("{}@{}", OPENSPACE_GIT_BRANCH, OPENSPACE_GIT_COMMIT)
|
|
);
|
|
|
|
FR::defaultRenderer().render(
|
|
*_fontInfo,
|
|
glm::vec2(
|
|
fontResolution().x - versionBox.boundingBox.x - 10.f,
|
|
5.f
|
|
),
|
|
OPENSPACE_VERSION_STRING_FULL,
|
|
glm::vec4(0.5, 0.5, 0.5, 1.f)
|
|
);
|
|
|
|
// If a developer hasn't placed the Git command in the path, this variable will be
|
|
// empty
|
|
if (!std::string(OPENSPACE_GIT_COMMIT).empty()) {
|
|
// We check OPENSPACE_GIT_COMMIT but puse OPENSPACE_GIT_FULL on purpose since
|
|
// OPENSPACE_GIT_FULL will never be empty (always will contain at least @, but
|
|
// checking for that is a bit brittle)
|
|
FR::defaultRenderer().render(
|
|
*_fontInfo,
|
|
glm::vec2(
|
|
fontResolution().x - commitBox.boundingBox.x - 10.f,
|
|
versionBox.boundingBox.y + 5.f
|
|
),
|
|
OPENSPACE_GIT_FULL,
|
|
glm::vec4(0.5, 0.5, 0.5, 1.f)
|
|
);
|
|
}
|
|
}
|
|
|
|
void RenderEngine::renderScreenLog() {
|
|
if (!_showLog) {
|
|
return;
|
|
}
|
|
|
|
_log->removeExpiredEntries();
|
|
|
|
constexpr const int MaxNumberMessages = 10;
|
|
constexpr const int CategoryLength = 30;
|
|
constexpr const int MessageLength = 140;
|
|
constexpr const std::chrono::seconds FadeTime(5);
|
|
|
|
const std::vector<ScreenLog::LogEntry>& entries = _log->entries();
|
|
auto lastEntries =
|
|
entries.size() > MaxNumberMessages ?
|
|
std::make_pair(entries.rbegin(), entries.rbegin() + MaxNumberMessages) :
|
|
std::make_pair(entries.rbegin(), entries.rend());
|
|
|
|
size_t nr = 1;
|
|
const auto now = std::chrono::steady_clock::now();
|
|
for (auto& it = lastEntries.first; it != lastEntries.second; ++it) {
|
|
const ScreenLog::LogEntry* e = &(*it);
|
|
|
|
std::chrono::duration<double> diff = now - e->timeStamp;
|
|
|
|
float alpha = 1.f;
|
|
std::chrono::duration<double> ttf = ScreenLogTimeToLive - FadeTime;
|
|
if (diff > ttf) {
|
|
double d = (diff - ttf).count();
|
|
float t = static_cast<float>(d) / static_cast<float>(FadeTime.count());
|
|
float p = 0.8f - t;
|
|
alpha = (p <= 0.f) ? 0.f : pow(p, 0.4f);
|
|
}
|
|
|
|
// Since all log entries are ordered, once one exceeds alpha, all have
|
|
if (alpha <= 0.f) {
|
|
break;
|
|
}
|
|
|
|
const std::string lvl = "(" + ghoul::logging::stringFromLevel(e->level) + ")";
|
|
const std::string& message = e->message.substr(0, MessageLength);
|
|
nr += std::count(message.begin(), message.end(), '\n');
|
|
|
|
const glm::vec4 white(0.9f, 0.9f, 0.9f, alpha);
|
|
|
|
RenderFont(
|
|
*_fontLog,
|
|
glm::vec2(10.f, _fontLog->pointSize() * nr * 2),
|
|
fmt::format(
|
|
"{:<15} {}{}",
|
|
e->timeString,
|
|
e->category.substr(0, CategoryLength),
|
|
e->category.length() > CategoryLength ? "..." : ""
|
|
),
|
|
white
|
|
);
|
|
|
|
glm::vec4 color(glm::uninitialize);
|
|
switch (e->level) {
|
|
case ghoul::logging::LogLevel::Debug:
|
|
color = glm::vec4(0.f, 1.f, 0.f, alpha);
|
|
break;
|
|
case ghoul::logging::LogLevel::Warning:
|
|
color = glm::vec4(1.f, 1.f, 0.f, alpha);
|
|
break;
|
|
case ghoul::logging::LogLevel::Error:
|
|
color = glm::vec4(1.f, 0.f, 0.f, alpha);
|
|
break;
|
|
case ghoul::logging::LogLevel::Fatal:
|
|
color = glm::vec4(0.3f, 0.3f, 0.85f, alpha);
|
|
break;
|
|
default:
|
|
color = white;
|
|
break;
|
|
}
|
|
|
|
RenderFont(
|
|
*_fontLog,
|
|
glm::vec2(10 + 30 * _fontLog->pointSize(), _fontLog->pointSize() * nr * 2),
|
|
lvl,
|
|
color
|
|
);
|
|
|
|
RenderFont(
|
|
*_fontLog,
|
|
glm::vec2(10 + 41 * _fontLog->pointSize(), _fontLog->pointSize() * nr * 2),
|
|
message,
|
|
white
|
|
);
|
|
++nr;
|
|
}
|
|
}
|
|
|
|
properties::PropertyOwner& RenderEngine::screenSpaceOwner() {
|
|
return _screenSpaceOwner;
|
|
}
|
|
|
|
} // namespace openspace
|