From 78e3320cde52996038a57acb224cdd98298f99b5 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 2 Nov 2017 14:27:35 -0400 Subject: [PATCH 01/33] Separate Renderable::initialize into initialize and initializeGL functions Handle initialize in a separate thread and display rudimentary loading screen --- .../engine/wrapper/sgctwindowwrapper.h | 2 ++ .../openspace/engine/wrapper/windowwrapper.h | 2 ++ include/openspace/rendering/renderable.h | 2 ++ .../rendering/screenspacerenderable.h | 3 +++ include/openspace/scene/scene.h | 6 +++++ include/openspace/scene/scenegraphnode.h | 2 ++ modules/base/rendering/renderablemodel.cpp | 4 ++-- modules/base/rendering/renderablemodel.h | 4 ++-- modules/base/rendering/renderableplane.cpp | 4 ++-- modules/base/rendering/renderableplane.h | 4 ++-- modules/base/rendering/renderablesphere.cpp | 4 ++-- modules/base/rendering/renderablesphere.h | 4 ++-- .../rendering/renderablesphericalgrid.cpp | 24 +++++++++---------- .../base/rendering/renderablesphericalgrid.h | 4 ++-- modules/base/rendering/renderabletrail.cpp | 4 ++-- modules/base/rendering/renderabletrail.h | 4 ++-- .../base/rendering/renderabletrailorbit.cpp | 8 +++---- modules/base/rendering/renderabletrailorbit.h | 4 ++-- .../rendering/renderabletrailtrajectory.cpp | 8 +++---- .../rendering/renderabletrailtrajectory.h | 4 ++-- .../base/rendering/screenspaceframebuffer.cpp | 8 +++---- .../base/rendering/screenspaceframebuffer.h | 4 ++-- .../rendering/renderabledebugplane.cpp | 4 ++-- .../rendering/renderabledebugplane.h | 4 ++-- .../rendering/renderablebillboardscloud.cpp | 22 +++++++++-------- .../rendering/renderablebillboardscloud.h | 3 ++- .../rendering/renderabledumeshes.cpp | 4 ++-- .../rendering/renderabledumeshes.h | 4 ++-- .../rendering/renderableplanescloud.cpp | 14 ++++++----- .../rendering/renderableplanescloud.h | 3 ++- .../rendering/renderablepoints.cpp | 14 ++++++----- .../rendering/renderablepoints.h | 3 ++- .../rendering/renderablefieldlines.cpp | 4 ++-- .../rendering/renderablefieldlines.h | 4 ++-- modules/galaxy/rendering/renderablegalaxy.cpp | 4 ++-- modules/galaxy/rendering/renderablegalaxy.h | 4 ++-- .../globebrowsing/globes/renderableglobe.cpp | 8 +++---- .../globebrowsing/globes/renderableglobe.h | 4 ++-- .../globebrowsing/other/distanceswitch.cpp | 14 +++++++++++ modules/globebrowsing/other/distanceswitch.h | 2 ++ .../rendering/renderablekameleonvolume.cpp | 4 ++-- .../rendering/renderablekameleonvolume.h | 4 ++-- .../rendering/renderablemultiresvolume.cpp | 4 ++-- .../rendering/renderablemultiresvolume.h | 4 ++-- .../renderableconstellationbounds.cpp | 4 ++-- .../rendering/renderableconstellationbounds.h | 4 ++-- modules/space/rendering/renderableplanet.cpp | 4 ++-- modules/space/rendering/renderableplanet.h | 4 ++-- modules/space/rendering/renderablerings.cpp | 4 ++-- modules/space/rendering/renderablerings.h | 4 ++-- modules/space/rendering/renderablestars.cpp | 4 ++-- modules/space/rendering/renderablestars.h | 4 ++-- .../rendering/renderablecrawlingline.cpp | 4 ++-- .../rendering/renderablecrawlingline.h | 4 ++-- .../rendering/renderablefov.cpp | 4 ++-- .../rendering/renderablefov.h | 4 ++-- .../rendering/renderablemodelprojection.cpp | 4 ++-- .../rendering/renderablemodelprojection.h | 4 ++-- .../rendering/renderableplaneprojection.cpp | 4 ++-- .../rendering/renderableplaneprojection.h | 4 ++-- .../rendering/renderableplanetprojection.cpp | 4 ++-- .../rendering/renderableplanetprojection.h | 5 ++-- .../rendering/renderableshadowcylinder.cpp | 4 ++-- .../rendering/renderableshadowcylinder.h | 4 ++-- .../rendering/renderabletoyvolume.cpp | 4 ++-- .../toyvolume/rendering/renderabletoyvolume.h | 4 ++-- .../rendering/renderabletimevaryingvolume.cpp | 4 ++-- .../rendering/renderabletimevaryingvolume.h | 4 ++-- src/engine/openspaceengine.cpp | 22 ++++++++++++++++- src/engine/wrapper/sgctwindowwrapper.cpp | 7 ++++++ src/engine/wrapper/windowwrapper.cpp | 2 ++ src/rendering/renderable.cpp | 4 ++++ src/rendering/screenspacerenderable.cpp | 8 +++++++ src/scene/scene.cpp | 12 ++++++++++ src/scene/scenegraphnode.cpp | 19 ++++++++++----- 75 files changed, 260 insertions(+), 159 deletions(-) diff --git a/include/openspace/engine/wrapper/sgctwindowwrapper.h b/include/openspace/engine/wrapper/sgctwindowwrapper.h index d106ce400d..e9294e735c 100644 --- a/include/openspace/engine/wrapper/sgctwindowwrapper.h +++ b/include/openspace/engine/wrapper/sgctwindowwrapper.h @@ -79,6 +79,8 @@ public: void takeScreenshot(bool applyWarping = false) const override; + void swapBuffer() const override; + private: properties::FloatProperty _eyeSeparation; properties::BoolProperty _showStatsGraph; diff --git a/include/openspace/engine/wrapper/windowwrapper.h b/include/openspace/engine/wrapper/windowwrapper.h index 23713c50de..f5cb79a1e7 100644 --- a/include/openspace/engine/wrapper/windowwrapper.h +++ b/include/openspace/engine/wrapper/windowwrapper.h @@ -270,6 +270,8 @@ public: */ virtual void takeScreenshot(bool applyWarping = false) const; + virtual void swapBuffer() const; + struct WindowWrapperException : public ghoul::RuntimeError { explicit WindowWrapperException(const std::string& msg); }; diff --git a/include/openspace/rendering/renderable.h b/include/openspace/rendering/renderable.h index 9feffdb280..73cbc332a7 100644 --- a/include/openspace/rendering/renderable.h +++ b/include/openspace/rendering/renderable.h @@ -65,7 +65,9 @@ public: virtual ~Renderable(); virtual void initialize(); + virtual void initializeGL(); virtual void deinitialize(); + virtual void deinitializeGL(); virtual bool isReady() const = 0; bool isEnabled() const; diff --git a/include/openspace/rendering/screenspacerenderable.h b/include/openspace/rendering/screenspacerenderable.h index 16fba100bb..162537c08a 100644 --- a/include/openspace/rendering/screenspacerenderable.h +++ b/include/openspace/rendering/screenspacerenderable.h @@ -60,7 +60,10 @@ public: virtual void render(); virtual bool initialize(); + virtual bool initializeGL(); virtual bool deinitialize(); + virtual bool deinitializeGL(); + virtual void update() = 0; virtual bool isReady() const; bool isEnabled() const; diff --git a/include/openspace/scene/scene.h b/include/openspace/scene/scene.h index 0ccd476883..750bc73cba 100644 --- a/include/openspace/scene/scene.h +++ b/include/openspace/scene/scene.h @@ -72,6 +72,12 @@ public: * Initalizes the SceneGraph */ void initialize(); + + /** + * Initializes the OpenGL part of the SceneGraph + */ + void initializeGL(); + /** * Clear the scene graph, diff --git a/include/openspace/scene/scenegraphnode.h b/include/openspace/scene/scenegraphnode.h index 0eaa83b80d..e0743978d8 100644 --- a/include/openspace/scene/scenegraphnode.h +++ b/include/openspace/scene/scenegraphnode.h @@ -76,7 +76,9 @@ public: static std::unique_ptr createFromDictionary(const ghoul::Dictionary& dictionary); void initialize(); + void initializeGL(); void deinitialize(); + void deinitializeGL(); void traversePreOrder(std::function fn); void traversePostOrder(std::function fn); diff --git a/modules/base/rendering/renderablemodel.cpp b/modules/base/rendering/renderablemodel.cpp index 10dab02d81..6377b4d772 100644 --- a/modules/base/rendering/renderablemodel.cpp +++ b/modules/base/rendering/renderablemodel.cpp @@ -156,7 +156,7 @@ bool RenderableModel::isReady() const { return _programObject && _texture; } -void RenderableModel::initialize() { +void RenderableModel::initializeGL() { _programObject = OsEng.renderEngine().buildRenderProgram( "ModelProgram", "${MODULE_BASE}/shaders/model_vs.glsl", @@ -168,7 +168,7 @@ void RenderableModel::initialize() { _geometry->initialize(this); } -void RenderableModel::deinitialize() { +void RenderableModel::deinitializeGL() { if (_geometry) { _geometry->deinitialize(); _geometry = nullptr; diff --git a/modules/base/rendering/renderablemodel.h b/modules/base/rendering/renderablemodel.h index 86b8d1c35f..0da2032a2b 100644 --- a/modules/base/rendering/renderablemodel.h +++ b/modules/base/rendering/renderablemodel.h @@ -51,8 +51,8 @@ class RenderableModel : public Renderable { public: RenderableModel(const ghoul::Dictionary& dictionary); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; diff --git a/modules/base/rendering/renderableplane.cpp b/modules/base/rendering/renderableplane.cpp index a38768d313..93b8f9ec78 100644 --- a/modules/base/rendering/renderableplane.cpp +++ b/modules/base/rendering/renderableplane.cpp @@ -179,7 +179,7 @@ bool RenderablePlane::isReady() const { return _shader && _texture; } -void RenderablePlane::initialize() { +void RenderablePlane::initializeGL() { glGenVertexArrays(1, &_quad); // generate array glGenBuffers(1, &_vertexPositionBuffer); // generate buffer createPlane(); @@ -192,7 +192,7 @@ void RenderablePlane::initialize() { loadTexture(); } -void RenderablePlane::deinitialize() { +void RenderablePlane::deinitializeGL() { glDeleteVertexArrays(1, &_quad); _quad = 0; diff --git a/modules/base/rendering/renderableplane.h b/modules/base/rendering/renderableplane.h index a18659d6b1..81800f97c1 100644 --- a/modules/base/rendering/renderableplane.h +++ b/modules/base/rendering/renderableplane.h @@ -54,8 +54,8 @@ class RenderablePlane : public Renderable { public: RenderablePlane(const ghoul::Dictionary& dictionary); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; diff --git a/modules/base/rendering/renderablesphere.cpp b/modules/base/rendering/renderablesphere.cpp index 9d959c969d..d434b1b91b 100644 --- a/modules/base/rendering/renderablesphere.cpp +++ b/modules/base/rendering/renderablesphere.cpp @@ -218,7 +218,7 @@ bool RenderableSphere::isReady() const { return _shader && _texture; } -void RenderableSphere::initialize() { +void RenderableSphere::initializeGL() { _sphere = std::make_unique( PowerScaledScalar::CreatePSS(_size), _segments ); @@ -232,7 +232,7 @@ void RenderableSphere::initialize() { loadTexture(); } -void RenderableSphere::deinitialize() { +void RenderableSphere::deinitializeGL() { _texture = nullptr; if (_shader) { diff --git a/modules/base/rendering/renderablesphere.h b/modules/base/rendering/renderablesphere.h index a38b090f2c..e5a5226c36 100644 --- a/modules/base/rendering/renderablesphere.h +++ b/modules/base/rendering/renderablesphere.h @@ -49,8 +49,8 @@ class RenderableSphere : public Renderable { public: RenderableSphere(const ghoul::Dictionary& dictionary); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; diff --git a/modules/base/rendering/renderablesphericalgrid.cpp b/modules/base/rendering/renderablesphericalgrid.cpp index 0072e000fd..4f3cbee50c 100644 --- a/modules/base/rendering/renderablesphericalgrid.cpp +++ b/modules/base/rendering/renderablesphericalgrid.cpp @@ -177,18 +177,7 @@ bool RenderableSphericalGrid::isReady() const { return ready; } -void RenderableSphericalGrid::deinitialize() { - glDeleteVertexArrays(1,&_vaoID); - _vaoID = 0; - - glDeleteBuffers(1,&_vBufferID); - _vBufferID = 0; - - glDeleteBuffers(1,&_iBufferID); - _iBufferID = 0; -} - -void RenderableSphericalGrid::initialize() { +void RenderableSphericalGrid::initializeGL() { _gridProgram = OsEng.renderEngine().buildRenderProgram( "GridProgram", "${MODULE_BASE}/shaders/grid_vs.glsl", @@ -206,6 +195,17 @@ void RenderableSphericalGrid::initialize() { glBindVertexArray(0); } +void RenderableSphericalGrid::deinitializeGL() { + glDeleteVertexArrays(1, &_vaoID); + _vaoID = 0; + + glDeleteBuffers(1, &_vBufferID); + _vBufferID = 0; + + glDeleteBuffers(1, &_iBufferID); + _iBufferID = 0; +} + void RenderableSphericalGrid::render(const RenderData& data, RendererTasks&){ _gridProgram->activate(); diff --git a/modules/base/rendering/renderablesphericalgrid.h b/modules/base/rendering/renderablesphericalgrid.h index 66a042c58a..9fff6708fd 100644 --- a/modules/base/rendering/renderablesphericalgrid.h +++ b/modules/base/rendering/renderablesphericalgrid.h @@ -48,8 +48,8 @@ public: RenderableSphericalGrid(const ghoul::Dictionary& dictionary); ~RenderableSphericalGrid(); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; diff --git a/modules/base/rendering/renderabletrail.cpp b/modules/base/rendering/renderabletrail.cpp index 2abcb20b51..d553be7ace 100644 --- a/modules/base/rendering/renderabletrail.cpp +++ b/modules/base/rendering/renderabletrail.cpp @@ -217,7 +217,7 @@ RenderableTrail::RenderableTrail(const ghoul::Dictionary& dictionary) addProperty(_renderingModes); } -void RenderableTrail::initialize() { +void RenderableTrail::initializeGL() { RenderEngine& renderEngine = OsEng.renderEngine(); _programObject = renderEngine.buildRenderProgram( "EphemerisProgram", @@ -228,7 +228,7 @@ void RenderableTrail::initialize() { setRenderBin(Renderable::RenderBin::Overlay); } -void RenderableTrail::deinitialize() { +void RenderableTrail::deinitializeGL() { RenderEngine& renderEngine = OsEng.renderEngine(); if (_programObject) { renderEngine.removeRenderProgram(_programObject); diff --git a/modules/base/rendering/renderabletrail.h b/modules/base/rendering/renderabletrail.h index ed721582a9..edcd3f3420 100644 --- a/modules/base/rendering/renderabletrail.h +++ b/modules/base/rendering/renderabletrail.h @@ -73,8 +73,8 @@ class RenderableTrail : public Renderable { public: ~RenderableTrail() = default; - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; diff --git a/modules/base/rendering/renderabletrailorbit.cpp b/modules/base/rendering/renderabletrailorbit.cpp index ab6c300a96..7e1bf01537 100644 --- a/modules/base/rendering/renderabletrailorbit.cpp +++ b/modules/base/rendering/renderabletrailorbit.cpp @@ -169,20 +169,20 @@ RenderableTrailOrbit::RenderableTrailOrbit(const ghoul::Dictionary& dictionary) _primaryRenderInformation.sorting = RenderInformation::VertexSorting::NewestFirst; } -void RenderableTrailOrbit::initialize() { - RenderableTrail::initialize(); +void RenderableTrailOrbit::initializeGL() { + RenderableTrail::initializeGL(); glGenVertexArrays(1, &_primaryRenderInformation._vaoID); glGenBuffers(1, &_primaryRenderInformation._vBufferID); glGenBuffers(1, &_primaryRenderInformation._iBufferID); } -void RenderableTrailOrbit::deinitialize() { +void RenderableTrailOrbit::deinitializeGL() { glDeleteVertexArrays(1, &_primaryRenderInformation._vaoID); glDeleteBuffers(1, &_primaryRenderInformation._vBufferID); glDeleteBuffers(1, &_primaryRenderInformation._iBufferID); - RenderableTrail::deinitialize(); + RenderableTrail::deinitializeGL(); } void RenderableTrailOrbit::update(const UpdateData& data) { diff --git a/modules/base/rendering/renderabletrailorbit.h b/modules/base/rendering/renderabletrailorbit.h index 325f195f7d..8fc18d52c1 100644 --- a/modules/base/rendering/renderabletrailorbit.h +++ b/modules/base/rendering/renderabletrailorbit.h @@ -48,8 +48,8 @@ class RenderableTrailOrbit : public RenderableTrail { public: explicit RenderableTrailOrbit(const ghoul::Dictionary& dictionary); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; void update(const UpdateData& data) override; diff --git a/modules/base/rendering/renderabletrailtrajectory.cpp b/modules/base/rendering/renderabletrailtrajectory.cpp index 2c3e793244..b43e3e655f 100644 --- a/modules/base/rendering/renderabletrailtrajectory.cpp +++ b/modules/base/rendering/renderabletrailtrajectory.cpp @@ -187,8 +187,8 @@ RenderableTrailTrajectory::RenderableTrailTrajectory(const ghoul::Dictionary& di _primaryRenderInformation.sorting = RenderInformation::VertexSorting::OldestFirst; } -void RenderableTrailTrajectory::initialize() { - RenderableTrail::initialize(); +void RenderableTrailTrajectory::initializeGL() { + RenderableTrail::initializeGL(); // We don't need an index buffer, so we keep it at the default value of 0 glGenVertexArrays(1, &_primaryRenderInformation._vaoID); @@ -201,14 +201,14 @@ void RenderableTrailTrajectory::initialize() { _floatingRenderInformation.sorting = RenderInformation::VertexSorting::OldestFirst; } -void RenderableTrailTrajectory::deinitialize() { +void RenderableTrailTrajectory::deinitializeGL() { glDeleteVertexArrays(1, &_primaryRenderInformation._vaoID); glDeleteBuffers(1, &_primaryRenderInformation._vBufferID); glDeleteVertexArrays(1, &_floatingRenderInformation._vaoID); glDeleteBuffers(1, &_floatingRenderInformation._vBufferID); - RenderableTrail::deinitialize(); + RenderableTrail::deinitializeGL(); } void RenderableTrailTrajectory::update(const UpdateData& data) { diff --git a/modules/base/rendering/renderabletrailtrajectory.h b/modules/base/rendering/renderabletrailtrajectory.h index 93d0367c9d..5ced9fccf1 100644 --- a/modules/base/rendering/renderabletrailtrajectory.h +++ b/modules/base/rendering/renderabletrailtrajectory.h @@ -53,8 +53,8 @@ class RenderableTrailTrajectory : public RenderableTrail { public: explicit RenderableTrailTrajectory(const ghoul::Dictionary& dictionary); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; void update(const UpdateData& data) override; diff --git a/modules/base/rendering/screenspaceframebuffer.cpp b/modules/base/rendering/screenspaceframebuffer.cpp index ec04e19821..c085ec2475 100644 --- a/modules/base/rendering/screenspaceframebuffer.cpp +++ b/modules/base/rendering/screenspaceframebuffer.cpp @@ -75,15 +75,15 @@ ScreenSpaceFramebuffer::ScreenSpaceFramebuffer(const ghoul::Dictionary& dictiona ScreenSpaceFramebuffer::~ScreenSpaceFramebuffer() {} -bool ScreenSpaceFramebuffer::initialize() { - ScreenSpaceRenderable::initialize(); +bool ScreenSpaceFramebuffer::initializeGL() { + ScreenSpaceRenderable::initializeGL(); createFragmentbuffer(); return isReady(); } -bool ScreenSpaceFramebuffer::deinitialize() { - ScreenSpaceRenderable::deinitialize(); +bool ScreenSpaceFramebuffer::deinitializeGL() { + ScreenSpaceRenderable::deinitializeGL(); _framebuffer->detachAll(); removeAllRenderFunctions(); diff --git a/modules/base/rendering/screenspaceframebuffer.h b/modules/base/rendering/screenspaceframebuffer.h index f649698c78..d62ed58b41 100644 --- a/modules/base/rendering/screenspaceframebuffer.h +++ b/modules/base/rendering/screenspaceframebuffer.h @@ -45,8 +45,8 @@ public: ScreenSpaceFramebuffer(const ghoul::Dictionary& dictionary = ghoul::Dictionary()); ~ScreenSpaceFramebuffer(); - bool initialize() override; - bool deinitialize() override; + bool initializeGL() override; + bool deinitializeGL() override; void render() override; void update() override; bool isReady() const override; diff --git a/modules/debugging/rendering/renderabledebugplane.cpp b/modules/debugging/rendering/renderabledebugplane.cpp index 42b5ccebed..6ea642fd15 100644 --- a/modules/debugging/rendering/renderabledebugplane.cpp +++ b/modules/debugging/rendering/renderabledebugplane.cpp @@ -190,7 +190,7 @@ bool RenderableDebugPlane::isReady() const { return ready; } -void RenderableDebugPlane::initialize() { +void RenderableDebugPlane::initializeGL() { glGenVertexArrays(1, &_quad); // generate array glGenBuffers(1, &_vertexPositionBuffer); // generate buffer createPlane(); @@ -204,7 +204,7 @@ void RenderableDebugPlane::initialize() { } } -void RenderableDebugPlane::deinitialize() { +void RenderableDebugPlane::deinitializeGL() { glDeleteVertexArrays(1, &_quad); _quad = 0; diff --git a/modules/debugging/rendering/renderabledebugplane.h b/modules/debugging/rendering/renderabledebugplane.h index 91fa8914b2..62bbe4d2f0 100644 --- a/modules/debugging/rendering/renderabledebugplane.h +++ b/modules/debugging/rendering/renderabledebugplane.h @@ -49,8 +49,8 @@ public: RenderableDebugPlane(const ghoul::Dictionary& dictionary); ~RenderableDebugPlane(); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; diff --git a/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp b/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp index d9130ae819..2700b1f5f1 100644 --- a/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp +++ b/modules/digitaluniverse/rendering/renderablebillboardscloud.cpp @@ -475,13 +475,6 @@ bool RenderableBillboardsCloud::isReady() const { } void RenderableBillboardsCloud::initialize() { - RenderEngine& renderEngine = OsEng.renderEngine(); - - _program = renderEngine.buildRenderProgram("RenderableBillboardsCloud", - "${MODULE_DIGITALUNIVERSE}/shaders/billboard2_vs.glsl", - "${MODULE_DIGITALUNIVERSE}/shaders/billboard2_fs.glsl", - "${MODULE_DIGITALUNIVERSE}/shaders/billboard2_gs.glsl"); - bool success = loadData(); if (!success) { throw ghoul::RuntimeError("Error loading data"); @@ -490,8 +483,17 @@ void RenderableBillboardsCloud::initialize() { if (!_colorOptionString.empty()) { // Following DU behavior here. The last colormap variable // entry is the one selected by default. - _colorOption.setValue(_colorRangeData.size()-1); - } + _colorOption.setValue(_colorRangeData.size() - 1); + } +} + +void RenderableBillboardsCloud::initializeGL() { + RenderEngine& renderEngine = OsEng.renderEngine(); + + _program = renderEngine.buildRenderProgram("RenderableBillboardsCloud", + "${MODULE_DIGITALUNIVERSE}/shaders/billboard2_vs.glsl", + "${MODULE_DIGITALUNIVERSE}/shaders/billboard2_fs.glsl", + "${MODULE_DIGITALUNIVERSE}/shaders/billboard2_gs.glsl"); if (_hasPolygon) { createPolygonTexture(); @@ -509,7 +511,7 @@ void RenderableBillboardsCloud::initialize() { } } -void RenderableBillboardsCloud::deinitialize() { +void RenderableBillboardsCloud::deinitializeGL() { glDeleteBuffers(1, &_vbo); _vbo = 0; glDeleteVertexArrays(1, &_vao); diff --git a/modules/digitaluniverse/rendering/renderablebillboardscloud.h b/modules/digitaluniverse/rendering/renderablebillboardscloud.h index eb11436069..eca14b76a4 100644 --- a/modules/digitaluniverse/rendering/renderablebillboardscloud.h +++ b/modules/digitaluniverse/rendering/renderablebillboardscloud.h @@ -59,7 +59,8 @@ public: ~RenderableBillboardsCloud() = default; void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; diff --git a/modules/digitaluniverse/rendering/renderabledumeshes.cpp b/modules/digitaluniverse/rendering/renderabledumeshes.cpp index cae2c552b1..a7078e3b7c 100644 --- a/modules/digitaluniverse/rendering/renderabledumeshes.cpp +++ b/modules/digitaluniverse/rendering/renderabledumeshes.cpp @@ -371,7 +371,7 @@ bool RenderableDUMeshes::isReady() const { return (_program != nullptr) && (!_renderingMeshesMap.empty() || (!_labelData.empty())); } -void RenderableDUMeshes::initialize() { +void RenderableDUMeshes::initializeGL() { RenderEngine& renderEngine = OsEng.renderEngine(); _program = renderEngine.buildRenderProgram("RenderableDUMeshes", "${MODULE_DIGITALUNIVERSE}/shaders/dumesh_vs.glsl", @@ -396,7 +396,7 @@ void RenderableDUMeshes::initialize() { } } -void RenderableDUMeshes::deinitialize() { +void RenderableDUMeshes::deinitializeGL() { for (const std::pair& pair : _renderingMeshesMap) { for (int i = 0; i < pair.second.numU; ++i) { glDeleteVertexArrays(1, &pair.second.vaoArray[i]); diff --git a/modules/digitaluniverse/rendering/renderabledumeshes.h b/modules/digitaluniverse/rendering/renderabledumeshes.h index 8df9af8f1e..6baf0fbdf9 100644 --- a/modules/digitaluniverse/rendering/renderabledumeshes.h +++ b/modules/digitaluniverse/rendering/renderabledumeshes.h @@ -56,8 +56,8 @@ public: explicit RenderableDUMeshes(const ghoul::Dictionary& dictionary); ~RenderableDUMeshes() = default; - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; diff --git a/modules/digitaluniverse/rendering/renderableplanescloud.cpp b/modules/digitaluniverse/rendering/renderableplanescloud.cpp index f4af818063..2a6612c8cd 100644 --- a/modules/digitaluniverse/rendering/renderableplanescloud.cpp +++ b/modules/digitaluniverse/rendering/renderableplanescloud.cpp @@ -420,17 +420,19 @@ bool RenderablePlanesCloud::isReady() const { } void RenderablePlanesCloud::initialize() { + bool success = loadData(); + if (!success) { + throw ghoul::RuntimeError("Error loading data"); + } +} + +void RenderablePlanesCloud::initializeGL() { RenderEngine& renderEngine = OsEng.renderEngine(); _program = renderEngine.buildRenderProgram("RenderablePlanesCloud", "${MODULE_DIGITALUNIVERSE}/shaders/plane2_vs.glsl", "${MODULE_DIGITALUNIVERSE}/shaders/plane2_fs.glsl"); - bool success = loadData(); - if (!success) { - throw ghoul::RuntimeError("Error loading data"); - } - createPlanes(); loadTextures(); @@ -455,7 +457,7 @@ void RenderablePlanesCloud::deleteDataGPU() { } } -void RenderablePlanesCloud::deinitialize() { +void RenderablePlanesCloud::deinitializeGL() { deleteDataGPU(); RenderEngine& renderEngine = OsEng.renderEngine(); diff --git a/modules/digitaluniverse/rendering/renderableplanescloud.h b/modules/digitaluniverse/rendering/renderableplanescloud.h index b300730977..13269e9edb 100644 --- a/modules/digitaluniverse/rendering/renderableplanescloud.h +++ b/modules/digitaluniverse/rendering/renderableplanescloud.h @@ -61,7 +61,8 @@ namespace openspace { ~RenderablePlanesCloud() = default; void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; diff --git a/modules/digitaluniverse/rendering/renderablepoints.cpp b/modules/digitaluniverse/rendering/renderablepoints.cpp index 1acc217745..c9e4d9b141 100644 --- a/modules/digitaluniverse/rendering/renderablepoints.cpp +++ b/modules/digitaluniverse/rendering/renderablepoints.cpp @@ -254,6 +254,13 @@ namespace openspace { } void RenderablePoints::initialize() { + bool success = loadData(); + if (!success) { + throw ghoul::RuntimeError("Error loading data"); + } + } + + void RenderablePoints::initializeGL() { RenderEngine& renderEngine = OsEng.renderEngine(); if (_hasSpriteTexture) { _program = renderEngine.buildRenderProgram("RenderablePoints", @@ -266,14 +273,9 @@ namespace openspace { "${MODULE_DIGITALUNIVERSE}/shaders/points_fs.glsl");// , //"${MODULE_DIGITALUNIVERSE}/shaders/points_gs.glsl"); } - - bool success = loadData(); - if (!success) { - throw ghoul::RuntimeError("Error loading data"); - } } - void RenderablePoints::deinitialize() { + void RenderablePoints::deinitializeGL() { glDeleteBuffers(1, &_vbo); _vbo = 0; glDeleteVertexArrays(1, &_vao); diff --git a/modules/digitaluniverse/rendering/renderablepoints.h b/modules/digitaluniverse/rendering/renderablepoints.h index df22bef073..0bfdd6b39e 100644 --- a/modules/digitaluniverse/rendering/renderablepoints.h +++ b/modules/digitaluniverse/rendering/renderablepoints.h @@ -54,7 +54,8 @@ namespace openspace { ~RenderablePoints() = default; void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; diff --git a/modules/fieldlines/rendering/renderablefieldlines.cpp b/modules/fieldlines/rendering/renderablefieldlines.cpp index 24aaba8404..f9555e7cd2 100644 --- a/modules/fieldlines/rendering/renderablefieldlines.cpp +++ b/modules/fieldlines/rendering/renderablefieldlines.cpp @@ -232,7 +232,7 @@ bool RenderableFieldlines::isReady() const { return programReady && vectorFieldReady && fieldlineReady && seedPointsReady; } -void RenderableFieldlines::initialize() { +void RenderableFieldlines::initializeGL() { if (_vectorFieldInfo.empty() || _fieldlineInfo.empty() || _seedPointsInfo.empty()) { throw ghoul::RuntimeError("Error initializing"); } @@ -245,7 +245,7 @@ void RenderableFieldlines::initialize() { ); } -void RenderableFieldlines::deinitialize() { +void RenderableFieldlines::deinitializeGL() { glDeleteVertexArrays(1, &_fieldlineVAO); _fieldlineVAO = 0; glDeleteBuffers(1, &_vertexPositionBuffer); diff --git a/modules/fieldlines/rendering/renderablefieldlines.h b/modules/fieldlines/rendering/renderablefieldlines.h index 10d9d92b63..0c4c23bdff 100644 --- a/modules/fieldlines/rendering/renderablefieldlines.h +++ b/modules/fieldlines/rendering/renderablefieldlines.h @@ -45,8 +45,8 @@ class RenderableFieldlines : public Renderable { public: RenderableFieldlines(const ghoul::Dictionary& dictionary); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; diff --git a/modules/galaxy/rendering/renderablegalaxy.cpp b/modules/galaxy/rendering/renderablegalaxy.cpp index 8802f3bf86..34602415ce 100644 --- a/modules/galaxy/rendering/renderablegalaxy.cpp +++ b/modules/galaxy/rendering/renderablegalaxy.cpp @@ -153,7 +153,7 @@ namespace openspace { RenderableGalaxy::~RenderableGalaxy() {} -void RenderableGalaxy::initialize() { +void RenderableGalaxy::initializeGL() { // Aspect is currently hardcoded to cubic voxels. _aspect = static_cast(_volumeDimensions); _aspect = _aspect / std::max(std::max(_aspect.x, _aspect.y), _aspect.z); @@ -275,7 +275,7 @@ void RenderableGalaxy::initialize() { glBindVertexArray(0); } -void RenderableGalaxy::deinitialize() { +void RenderableGalaxy::deinitializeGL() { if (_raycaster) { OsEng.renderEngine().raycasterManager().detachRaycaster(*_raycaster.get()); _raycaster = nullptr; diff --git a/modules/galaxy/rendering/renderablegalaxy.h b/modules/galaxy/rendering/renderablegalaxy.h index 638ba8690b..d910be4650 100644 --- a/modules/galaxy/rendering/renderablegalaxy.h +++ b/modules/galaxy/rendering/renderablegalaxy.h @@ -41,8 +41,8 @@ public: RenderableGalaxy(const ghoul::Dictionary& dictionary); ~RenderableGalaxy(); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; void render(const RenderData& data, RendererTasks& tasks) override; void update(const UpdateData& data) override; diff --git a/modules/globebrowsing/globes/renderableglobe.cpp b/modules/globebrowsing/globes/renderableglobe.cpp index e2cbb8627d..63406ca258 100644 --- a/modules/globebrowsing/globes/renderableglobe.cpp +++ b/modules/globebrowsing/globes/renderableglobe.cpp @@ -264,12 +264,12 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary) _chunkedLodGlobe->recompileShaders(); } -void RenderableGlobe::initialize() { - _distanceSwitch.initialize(); +void RenderableGlobe::initializeGL() { + _distanceSwitch.initializeGL(); } -void RenderableGlobe::deinitialize() { - _distanceSwitch.deinitialize(); +void RenderableGlobe::deinitializeGL() { + _distanceSwitch.deinitializeGL(); } bool RenderableGlobe::isReady() const { diff --git a/modules/globebrowsing/globes/renderableglobe.h b/modules/globebrowsing/globes/renderableglobe.h index b43111dfaf..5c05ec49bf 100644 --- a/modules/globebrowsing/globes/renderableglobe.h +++ b/modules/globebrowsing/globes/renderableglobe.h @@ -81,8 +81,8 @@ public: RenderableGlobe(const ghoul::Dictionary& dictionary); ~RenderableGlobe() = default; - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; void render(const RenderData& data, RendererTasks& rendererTask) override; diff --git a/modules/globebrowsing/other/distanceswitch.cpp b/modules/globebrowsing/other/distanceswitch.cpp index 8e74e5f9ac..ad4db4e7b2 100644 --- a/modules/globebrowsing/other/distanceswitch.cpp +++ b/modules/globebrowsing/other/distanceswitch.cpp @@ -37,6 +37,13 @@ bool DistanceSwitch::initialize() { return true; } +bool DistanceSwitch::initializeGL() { + for (unsigned int i = 0; i < _renderables.size(); ++i) { + _renderables[i]->initializeGL(); + } + return true; +} + bool DistanceSwitch::deinitialize() { for (unsigned int i = 0; i < _renderables.size(); ++i) { _renderables[i]->deinitialize(); @@ -44,6 +51,13 @@ bool DistanceSwitch::deinitialize() { return true; } +bool DistanceSwitch::deinitializeGL() { + for (unsigned int i = 0; i < _renderables.size(); ++i) { + _renderables[i]->deinitializeGL(); + } + return true; +} + void DistanceSwitch::render(const RenderData& data, RendererTasks& tasks) { const double distanceToCamera = distance(data.camera.positionVec3(), data.modelTransform.translation); diff --git a/modules/globebrowsing/other/distanceswitch.h b/modules/globebrowsing/other/distanceswitch.h index 45d9a6bd3c..3acd91a3fd 100644 --- a/modules/globebrowsing/other/distanceswitch.h +++ b/modules/globebrowsing/other/distanceswitch.h @@ -46,7 +46,9 @@ public: ~DistanceSwitch(); bool initialize(); + bool initializeGL(); bool deinitialize(); + bool deinitializeGL(); /** * Picks the first Renderable with the associated maxDistance greater than the diff --git a/modules/kameleonvolume/rendering/renderablekameleonvolume.cpp b/modules/kameleonvolume/rendering/renderablekameleonvolume.cpp index 53ebd88c28..fd033e6bb4 100644 --- a/modules/kameleonvolume/rendering/renderablekameleonvolume.cpp +++ b/modules/kameleonvolume/rendering/renderablekameleonvolume.cpp @@ -251,7 +251,7 @@ RenderableKameleonVolume::RenderableKameleonVolume(const ghoul::Dictionary& dict RenderableKameleonVolume::~RenderableKameleonVolume() {} -void RenderableKameleonVolume::initialize() { +void RenderableKameleonVolume::initializeGL() { load(); _volumeTexture->uploadTexture(); @@ -443,7 +443,7 @@ void RenderableKameleonVolume::storeRaw(const std::string& path) { writer.write(*_rawVolume); } -void RenderableKameleonVolume::deinitialize() { +void RenderableKameleonVolume::deinitializeGL() { if (_raycaster) { OsEng.renderEngine().raycasterManager().detachRaycaster(*_raycaster.get()); _raycaster = nullptr; diff --git a/modules/kameleonvolume/rendering/renderablekameleonvolume.h b/modules/kameleonvolume/rendering/renderablekameleonvolume.h index e25c8797b8..4b06aa9501 100644 --- a/modules/kameleonvolume/rendering/renderablekameleonvolume.h +++ b/modules/kameleonvolume/rendering/renderablekameleonvolume.h @@ -49,8 +49,8 @@ public: RenderableKameleonVolume(const ghoul::Dictionary& dictionary); ~RenderableKameleonVolume(); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; void render(const RenderData& data, RendererTasks& tasks) override; void update(const UpdateData& data) override; diff --git a/modules/multiresvolume/rendering/renderablemultiresvolume.cpp b/modules/multiresvolume/rendering/renderablemultiresvolume.cpp index 98cb29d764..9b3c326bf0 100644 --- a/modules/multiresvolume/rendering/renderablemultiresvolume.cpp +++ b/modules/multiresvolume/rendering/renderablemultiresvolume.cpp @@ -419,7 +419,7 @@ bool RenderableMultiresVolume::setSelectorType(Selector selector) { return false; } -void RenderableMultiresVolume::initialize() { +void RenderableMultiresVolume::initializeGL() { bool success = _tsp && _tsp->load(); unsigned int maxNumBricks = _tsp->header().xNumBricks_ * _tsp->header().yNumBricks_ * _tsp->header().zNumBricks_; @@ -481,7 +481,7 @@ void RenderableMultiresVolume::initialize() { } } -void RenderableMultiresVolume::deinitialize() { +void RenderableMultiresVolume::deinitializeGL() { _tsp = nullptr; _transferFunction = nullptr; } diff --git a/modules/multiresvolume/rendering/renderablemultiresvolume.h b/modules/multiresvolume/rendering/renderablemultiresvolume.h index 0127002d47..fcce7e93b9 100644 --- a/modules/multiresvolume/rendering/renderablemultiresvolume.h +++ b/modules/multiresvolume/rendering/renderablemultiresvolume.h @@ -68,8 +68,8 @@ public: bool setSelectorType(Selector selector); bool initializeSelector(); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; diff --git a/modules/space/rendering/renderableconstellationbounds.cpp b/modules/space/rendering/renderableconstellationbounds.cpp index 529a50c577..0355f5009a 100644 --- a/modules/space/rendering/renderableconstellationbounds.cpp +++ b/modules/space/rendering/renderableconstellationbounds.cpp @@ -195,7 +195,7 @@ RenderableConstellationBounds::RenderableConstellationBounds( } } -void RenderableConstellationBounds::initialize() { +void RenderableConstellationBounds::initializeGL() { _program = OsEng.renderEngine().buildRenderProgram( "ConstellationBounds", "${MODULE_SPACE}/shaders/constellationbounds_vs.glsl", @@ -222,7 +222,7 @@ void RenderableConstellationBounds::initialize() { glBindVertexArray(0); } -void RenderableConstellationBounds::deinitialize() { +void RenderableConstellationBounds::deinitializeGL() { glDeleteBuffers(1, &_vbo); _vbo = 0; glDeleteVertexArrays(1, &_vao); diff --git a/modules/space/rendering/renderableconstellationbounds.h b/modules/space/rendering/renderableconstellationbounds.h index a21ea6376b..c3a107bbdc 100644 --- a/modules/space/rendering/renderableconstellationbounds.h +++ b/modules/space/rendering/renderableconstellationbounds.h @@ -51,8 +51,8 @@ class RenderableConstellationBounds : public Renderable { public: RenderableConstellationBounds(const ghoul::Dictionary& dictionary); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; diff --git a/modules/space/rendering/renderableplanet.cpp b/modules/space/rendering/renderableplanet.cpp index 93bb6b0970..ffb28e197a 100644 --- a/modules/space/rendering/renderableplanet.cpp +++ b/modules/space/rendering/renderableplanet.cpp @@ -333,7 +333,7 @@ RenderablePlanet::RenderablePlanet(const ghoul::Dictionary& dictionary) } } -void RenderablePlanet::initialize() { +void RenderablePlanet::initializeGL() { RenderEngine& renderEngine = OsEng.renderEngine(); if (_programObject == nullptr && _shadowEnabled && _hasNightTexture) { @@ -375,7 +375,7 @@ void RenderablePlanet::initialize() { loadTexture(); } -void RenderablePlanet::deinitialize() { +void RenderablePlanet::deinitializeGL() { if (_geometry) { _geometry->deinitialize(); _geometry = nullptr; diff --git a/modules/space/rendering/renderableplanet.h b/modules/space/rendering/renderableplanet.h index 847690841a..db28d57af1 100644 --- a/modules/space/rendering/renderableplanet.h +++ b/modules/space/rendering/renderableplanet.h @@ -68,8 +68,8 @@ public: public: RenderablePlanet(const ghoul::Dictionary& dictionary); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; void render(const RenderData& data, RendererTasks& rendererTask) override; diff --git a/modules/space/rendering/renderablerings.cpp b/modules/space/rendering/renderablerings.cpp index 276a065263..791134ac91 100644 --- a/modules/space/rendering/renderablerings.cpp +++ b/modules/space/rendering/renderablerings.cpp @@ -184,7 +184,7 @@ bool RenderableRings::isReady() const { return _shader && _texture; } -void RenderableRings::initialize() { +void RenderableRings::initializeGL() { if (!_shader) { RenderEngine& renderEngine = OsEng.renderEngine(); _shader = renderEngine.buildRenderProgram("RingProgram", @@ -202,7 +202,7 @@ void RenderableRings::initialize() { loadTexture(); } -void RenderableRings::deinitialize() { +void RenderableRings::deinitializeGL() { glDeleteVertexArrays(1, &_quad); _quad = 0; diff --git a/modules/space/rendering/renderablerings.h b/modules/space/rendering/renderablerings.h index 3d39feb896..d34fafabc1 100644 --- a/modules/space/rendering/renderablerings.h +++ b/modules/space/rendering/renderablerings.h @@ -46,8 +46,8 @@ class RenderableRings : public Renderable { public: RenderableRings(const ghoul::Dictionary& dictionary); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; diff --git a/modules/space/rendering/renderablestars.cpp b/modules/space/rendering/renderablestars.cpp index 91323052f9..bdda45909d 100644 --- a/modules/space/rendering/renderablestars.cpp +++ b/modules/space/rendering/renderablestars.cpp @@ -288,7 +288,7 @@ bool RenderableStars::isReady() const { return (_program != nullptr) && (!_fullData.empty()); } -void RenderableStars::initialize() { +void RenderableStars::initializeGL() { RenderEngine& renderEngine = OsEng.renderEngine(); _program = renderEngine.buildRenderProgram("Star", "${MODULE_SPACE}/shaders/star_vs.glsl", @@ -301,7 +301,7 @@ void RenderableStars::initialize() { } } -void RenderableStars::deinitialize() { +void RenderableStars::deinitializeGL() { glDeleteBuffers(1, &_vbo); _vbo = 0; glDeleteVertexArrays(1, &_vao); diff --git a/modules/space/rendering/renderablestars.h b/modules/space/rendering/renderablestars.h index 346a4dbd02..1c94e8e456 100644 --- a/modules/space/rendering/renderablestars.h +++ b/modules/space/rendering/renderablestars.h @@ -48,8 +48,8 @@ public: explicit RenderableStars(const ghoul::Dictionary& dictionary); ~RenderableStars(); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; diff --git a/modules/spacecraftinstruments/rendering/renderablecrawlingline.cpp b/modules/spacecraftinstruments/rendering/renderablecrawlingline.cpp index 94011b540b..57d13bc13a 100644 --- a/modules/spacecraftinstruments/rendering/renderablecrawlingline.cpp +++ b/modules/spacecraftinstruments/rendering/renderablecrawlingline.cpp @@ -135,7 +135,7 @@ bool RenderableCrawlingLine::isReady() const { return (_program != nullptr); } -void RenderableCrawlingLine::initialize() { +void RenderableCrawlingLine::initializeGL() { RenderEngine& renderEngine = OsEng.renderEngine(); _program = renderEngine.buildRenderProgram( "RenderableCrawlingLine", @@ -166,7 +166,7 @@ void RenderableCrawlingLine::initialize() { glBindVertexArray(0); } -void RenderableCrawlingLine::deinitialize(){ +void RenderableCrawlingLine::deinitializeGL() { glDeleteVertexArrays(1, &_vao); _vao = 0; glDeleteBuffers(1, &_vbo); diff --git a/modules/spacecraftinstruments/rendering/renderablecrawlingline.h b/modules/spacecraftinstruments/rendering/renderablecrawlingline.h index a867c6c402..9319520656 100644 --- a/modules/spacecraftinstruments/rendering/renderablecrawlingline.h +++ b/modules/spacecraftinstruments/rendering/renderablecrawlingline.h @@ -38,8 +38,8 @@ class RenderableCrawlingLine : public Renderable { public: RenderableCrawlingLine(const ghoul::Dictionary& dictionary); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; diff --git a/modules/spacecraftinstruments/rendering/renderablefov.cpp b/modules/spacecraftinstruments/rendering/renderablefov.cpp index 1f3bfc7e32..e0b9b42003 100644 --- a/modules/spacecraftinstruments/rendering/renderablefov.cpp +++ b/modules/spacecraftinstruments/rendering/renderablefov.cpp @@ -297,7 +297,7 @@ RenderableFov::RenderableFov(const ghoul::Dictionary& dictionary) addProperty(_colors.square); } -void RenderableFov::initialize() { +void RenderableFov::initializeGL() { RenderEngine& renderEngine = OsEng.renderEngine(); _programObject = renderEngine.buildRenderProgram( "FovProgram", @@ -409,7 +409,7 @@ void RenderableFov::initialize() { glBindVertexArray(0); } -void RenderableFov::deinitialize() { +void RenderableFov::deinitializeGL() { OsEng.renderEngine().removeRenderProgram(_programObject); _programObject = nullptr; diff --git a/modules/spacecraftinstruments/rendering/renderablefov.h b/modules/spacecraftinstruments/rendering/renderablefov.h index 933e59e1f8..7f2ba50df5 100644 --- a/modules/spacecraftinstruments/rendering/renderablefov.h +++ b/modules/spacecraftinstruments/rendering/renderablefov.h @@ -49,8 +49,8 @@ class RenderableFov : public Renderable { public: RenderableFov(const ghoul::Dictionary& dictionary); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; diff --git a/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp b/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp index f3ab27d457..80e8115283 100644 --- a/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp +++ b/modules/spacecraftinstruments/rendering/renderablemodelprojection.cpp @@ -180,7 +180,7 @@ bool RenderableModelProjection::isReady() const { return ready; } -void RenderableModelProjection::initialize() { +void RenderableModelProjection::initializeGL() { RenderEngine& renderEngine = OsEng.renderEngine(); _programObject = renderEngine.buildRenderProgram("ModelShader", "${MODULE_SPACECRAFTINSTRUMENTS}/shaders/renderableModel_vs.glsl", @@ -207,7 +207,7 @@ void RenderableModelProjection::initialize() { setBoundingSphere(bs); // ignore bounding sphere set by geometry. } -void RenderableModelProjection::deinitialize() { +void RenderableModelProjection::deinitializeGL() { if (_geometry) { _geometry->deinitialize(); } diff --git a/modules/spacecraftinstruments/rendering/renderablemodelprojection.h b/modules/spacecraftinstruments/rendering/renderablemodelprojection.h index cb749e168f..3b10f2594f 100644 --- a/modules/spacecraftinstruments/rendering/renderablemodelprojection.h +++ b/modules/spacecraftinstruments/rendering/renderablemodelprojection.h @@ -55,8 +55,8 @@ public: RenderableModelProjection(const ghoul::Dictionary& dictionary); ~RenderableModelProjection(); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; diff --git a/modules/spacecraftinstruments/rendering/renderableplaneprojection.cpp b/modules/spacecraftinstruments/rendering/renderableplaneprojection.cpp index 2c082c6771..0060633005 100644 --- a/modules/spacecraftinstruments/rendering/renderableplaneprojection.cpp +++ b/modules/spacecraftinstruments/rendering/renderableplaneprojection.cpp @@ -94,7 +94,7 @@ bool RenderablePlaneProjection::isReady() const { return _shader && _texture; } -void RenderablePlaneProjection::initialize() { +void RenderablePlaneProjection::initializeGL() { glGenVertexArrays(1, &_quad); // generate array glGenBuffers(1, &_vertexPositionBuffer); // generate buffer @@ -110,7 +110,7 @@ void RenderablePlaneProjection::initialize() { loadTexture(); } -void RenderablePlaneProjection::deinitialize() { +void RenderablePlaneProjection::deinitializeGL() { RenderEngine& renderEngine = OsEng.renderEngine(); if (_shader) { renderEngine.removeRenderProgram(_shader); diff --git a/modules/spacecraftinstruments/rendering/renderableplaneprojection.h b/modules/spacecraftinstruments/rendering/renderableplaneprojection.h index d085cc19d8..ff6e23a218 100644 --- a/modules/spacecraftinstruments/rendering/renderableplaneprojection.h +++ b/modules/spacecraftinstruments/rendering/renderableplaneprojection.h @@ -55,8 +55,8 @@ public: RenderablePlaneProjection(const ghoul::Dictionary& dictionary); ~RenderablePlaneProjection(); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; diff --git a/modules/spacecraftinstruments/rendering/renderableplanetprojection.cpp b/modules/spacecraftinstruments/rendering/renderableplanetprojection.cpp index cd5a6fcf97..4339ab73f3 100644 --- a/modules/spacecraftinstruments/rendering/renderableplanetprojection.cpp +++ b/modules/spacecraftinstruments/rendering/renderableplanetprojection.cpp @@ -223,7 +223,7 @@ RenderablePlanetProjection::RenderablePlanetProjection(const ghoul::Dictionary& RenderablePlanetProjection::~RenderablePlanetProjection() {} -void RenderablePlanetProjection::initialize() { +void RenderablePlanetProjection::initializeGL() { _programObject = OsEng.renderEngine().buildRenderProgram( "projectiveProgram", "${MODULE_SPACECRAFTINSTRUMENTS}/shaders/renderablePlanet_vs.glsl", @@ -273,7 +273,7 @@ void RenderablePlanetProjection::initialize() { glBindVertexArray(0); } -void RenderablePlanetProjection::deinitialize() { +void RenderablePlanetProjection::deinitializeGL() { _projectionComponent.deinitialize(); _baseTexture = nullptr; _geometry = nullptr; diff --git a/modules/spacecraftinstruments/rendering/renderableplanetprojection.h b/modules/spacecraftinstruments/rendering/renderableplanetprojection.h index 855d1e834a..b81773fe9e 100644 --- a/modules/spacecraftinstruments/rendering/renderableplanetprojection.h +++ b/modules/spacecraftinstruments/rendering/renderableplanetprojection.h @@ -44,8 +44,8 @@ public: RenderablePlanetProjection(const ghoul::Dictionary& dictionary); ~RenderablePlanetProjection(); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; void render(const RenderData& data, RendererTasks& rendererTask) override; @@ -58,7 +58,6 @@ protected: bool loadTextures(); void attitudeParameters(double time); - private: void imageProjectGPU(std::shared_ptr projectionTexture); diff --git a/modules/spacecraftinstruments/rendering/renderableshadowcylinder.cpp b/modules/spacecraftinstruments/rendering/renderableshadowcylinder.cpp index cccd416013..6a5a8072e0 100644 --- a/modules/spacecraftinstruments/rendering/renderableshadowcylinder.cpp +++ b/modules/spacecraftinstruments/rendering/renderableshadowcylinder.cpp @@ -260,7 +260,7 @@ RenderableShadowCylinder::RenderableShadowCylinder(const ghoul::Dictionary& dict _aberration = static_cast(aberration.type); } -void RenderableShadowCylinder::initialize() { +void RenderableShadowCylinder::initializeGL() { glGenVertexArrays(1, &_vao); glGenBuffers(1, &_vbo); @@ -272,7 +272,7 @@ void RenderableShadowCylinder::initialize() { ); } -void RenderableShadowCylinder::deinitialize() { +void RenderableShadowCylinder::deinitializeGL() { RenderEngine& renderEngine = OsEng.renderEngine(); if (_shader) { renderEngine.removeRenderProgram(_shader); diff --git a/modules/spacecraftinstruments/rendering/renderableshadowcylinder.h b/modules/spacecraftinstruments/rendering/renderableshadowcylinder.h index 68ac86aec3..9586ee6dfe 100644 --- a/modules/spacecraftinstruments/rendering/renderableshadowcylinder.h +++ b/modules/spacecraftinstruments/rendering/renderableshadowcylinder.h @@ -50,8 +50,8 @@ class RenderableShadowCylinder : public Renderable { public: RenderableShadowCylinder(const ghoul::Dictionary& dictionary); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; void render(const RenderData& data, RendererTasks& rendererTask) override; diff --git a/modules/toyvolume/rendering/renderabletoyvolume.cpp b/modules/toyvolume/rendering/renderabletoyvolume.cpp index 8476c5c74e..7dee9c442a 100644 --- a/modules/toyvolume/rendering/renderabletoyvolume.cpp +++ b/modules/toyvolume/rendering/renderabletoyvolume.cpp @@ -69,7 +69,7 @@ RenderableToyVolume::RenderableToyVolume(const ghoul::Dictionary& dictionary) RenderableToyVolume::~RenderableToyVolume() {} -void RenderableToyVolume::initialize() { +void RenderableToyVolume::initializeGL() { _raycaster = std::make_unique(_color); _raycaster->initialize(); @@ -94,7 +94,7 @@ void RenderableToyVolume::initialize() { addProperty(_color); } -void RenderableToyVolume::deinitialize() { +void RenderableToyVolume::deinitializeGL() { if (_raycaster) { OsEng.renderEngine().raycasterManager().detachRaycaster(*_raycaster.get()); _raycaster = nullptr; diff --git a/modules/toyvolume/rendering/renderabletoyvolume.h b/modules/toyvolume/rendering/renderabletoyvolume.h index 5655cbf9c0..c2033d4c79 100644 --- a/modules/toyvolume/rendering/renderabletoyvolume.h +++ b/modules/toyvolume/rendering/renderabletoyvolume.h @@ -44,8 +44,8 @@ public: RenderableToyVolume(const ghoul::Dictionary& dictionary); ~RenderableToyVolume(); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; void render(const RenderData& data, RendererTasks& tasks) override; void update(const UpdateData& data) override; diff --git a/modules/volume/rendering/renderabletimevaryingvolume.cpp b/modules/volume/rendering/renderabletimevaryingvolume.cpp index 4db921c776..7ee031665b 100644 --- a/modules/volume/rendering/renderabletimevaryingvolume.cpp +++ b/modules/volume/rendering/renderabletimevaryingvolume.cpp @@ -124,7 +124,7 @@ RenderableTimeVaryingVolume::RenderableTimeVaryingVolume(const ghoul::Dictionary RenderableTimeVaryingVolume::~RenderableTimeVaryingVolume() {} -void RenderableTimeVaryingVolume::initialize() { +void RenderableTimeVaryingVolume::initializeGL() { using RawPath = ghoul::filesystem::Directory::RawPath; ghoul::filesystem::Directory sequenceDir(_sourceDirectory, RawPath::Yes); @@ -360,7 +360,7 @@ bool RenderableTimeVaryingVolume::isReady() const { } -void RenderableTimeVaryingVolume::deinitialize() { +void RenderableTimeVaryingVolume::deinitializeGL() { if (_raycaster) { OsEng.renderEngine().raycasterManager().detachRaycaster(*_raycaster.get()); _raycaster = nullptr; diff --git a/modules/volume/rendering/renderabletimevaryingvolume.h b/modules/volume/rendering/renderabletimevaryingvolume.h index f3fe18137f..30fd980c78 100644 --- a/modules/volume/rendering/renderabletimevaryingvolume.h +++ b/modules/volume/rendering/renderabletimevaryingvolume.h @@ -48,8 +48,8 @@ public: RenderableTimeVaryingVolume(const ghoul::Dictionary& dictionary); ~RenderableTimeVaryingVolume(); - void initialize() override; - void deinitialize() override; + void initializeGL() override; + void deinitializeGL() override; bool isReady() const override; void render(const RenderData& data, RendererTasks& tasks) override; void update(const UpdateData& data) override; diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 876f08b236..c3e012645f 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -75,6 +75,8 @@ #include +#include + #if defined(_MSC_VER) && defined(OPENSPACE_ENABLE_VLD) #include #endif @@ -613,7 +615,25 @@ void OpenSpaceEngine::loadScene(const std::string& scenePath) { _renderEngine->setGlobalBlackOutFactor(0.0); _renderEngine->startFading(1, 3.0); - scene->initialize(); + // TODO remove after moving OpenGL out of initialize + std::atomic_bool initializeFinished = false; + std::thread t([scene, &initializeFinished]() { + scene->initialize(); + initializeFinished = true; + }); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_real_distribution<> dis(0.0, 1.0); + while (!initializeFinished) { + glClearColor(dis(gen), dis(gen), dis(gen), 1.0); + glClear(ClearBufferMask::GL_COLOR_BUFFER_BIT); + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + _windowWrapper->swapBuffer(); + } + + scene->initializeGL(); + // Update the scene so that position of objects are set in case they are used in // post sync scripts _renderEngine->updateScene(); diff --git a/src/engine/wrapper/sgctwindowwrapper.cpp b/src/engine/wrapper/sgctwindowwrapper.cpp index 2d543cd25e..1254f76c25 100644 --- a/src/engine/wrapper/sgctwindowwrapper.cpp +++ b/src/engine/wrapper/sgctwindowwrapper.cpp @@ -258,4 +258,11 @@ void SGCTWindowWrapper::takeScreenshot(bool applyWarping) const { sgct::Engine::instance()->takeScreenshot(); } +void SGCTWindowWrapper::swapBuffer() const { + GLFWwindow* w = glfwGetCurrentContext(); + glfwSwapBuffers(w); + + glfwPollEvents(); +} + } // namespace openspace diff --git a/src/engine/wrapper/windowwrapper.cpp b/src/engine/wrapper/windowwrapper.cpp index 2021676076..5cf98b28a3 100644 --- a/src/engine/wrapper/windowwrapper.cpp +++ b/src/engine/wrapper/windowwrapper.cpp @@ -185,4 +185,6 @@ bool WindowWrapper::isSimpleRendering() const { void WindowWrapper::takeScreenshot(bool) const {} +void WindowWrapper::swapBuffer() const {} + } // namespace openspace diff --git a/src/rendering/renderable.cpp b/src/rendering/renderable.cpp index c4e52442fe..032fa11095 100644 --- a/src/rendering/renderable.cpp +++ b/src/rendering/renderable.cpp @@ -151,8 +151,12 @@ Renderable::~Renderable() {} void Renderable::initialize() {} +void Renderable::initializeGL() {} + void Renderable::deinitialize() {} +void Renderable::deinitializeGL() {} + void Renderable::setBoundingSphere(float boundingSphere) { _boundingSphere = boundingSphere; } diff --git a/src/rendering/screenspacerenderable.cpp b/src/rendering/screenspacerenderable.cpp index 8cb07181e9..1c56002b7c 100644 --- a/src/rendering/screenspacerenderable.cpp +++ b/src/rendering/screenspacerenderable.cpp @@ -304,6 +304,10 @@ ScreenSpaceRenderable::ScreenSpaceRenderable(const ghoul::Dictionary& dictionary } bool ScreenSpaceRenderable::initialize() { + return true; +} + +bool ScreenSpaceRenderable::initializeGL() { _originalViewportSize = OsEng.windowWrapper().currentWindowResolution(); createPlane(); @@ -313,6 +317,10 @@ bool ScreenSpaceRenderable::initialize() { } bool ScreenSpaceRenderable::deinitialize() { + return true; +} + +bool ScreenSpaceRenderable::deinitializeGL() { glDeleteVertexArrays(1, &_quad); _quad = 0; diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 436bc12a9b..870dc942bd 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -225,6 +225,18 @@ void Scene::initialize() { } } +void Scene::initializeGL() { + for (SceneGraphNode* node : _topologicallySortedNodes) { + try { + node->initializeGL(); + } + catch (const ghoul::RuntimeError& e) { + LERROR(node->name() << " not initialized."); + LERRORC(std::string(_loggerCat) + "(" + e.component + ")", e.what()); + } + } +} + void Scene::update(const UpdateData& data) { for (SceneGraphNode* node : _topologicallySortedNodes) { try { diff --git a/src/scene/scenegraphnode.cpp b/src/scene/scenegraphnode.cpp index 862327d835..e532db2fc9 100644 --- a/src/scene/scenegraphnode.cpp +++ b/src/scene/scenegraphnode.cpp @@ -182,11 +182,10 @@ SceneGraphNode::SceneGraphNode() } {} -SceneGraphNode::~SceneGraphNode() { - deinitialize(); -} +SceneGraphNode::~SceneGraphNode() {} void SceneGraphNode::initialize() { + LDEBUG("Initialize: " << name()); if (_renderable) { _renderable->initialize(); } @@ -203,16 +202,24 @@ void SceneGraphNode::initialize() { } } +void SceneGraphNode::initializeGL() { + if (_renderable) { + _renderable->initializeGL(); + } +} + void SceneGraphNode::deinitialize() { LDEBUG("Deinitialize: " << name()); if (_renderable) { _renderable->deinitialize(); - _renderable = nullptr; } - _children.clear(); +} - _parent = nullptr; +void SceneGraphNode::deinitializeGL() { + if (_renderable) { + _renderable->deinitializeGL(); + } } void SceneGraphNode::traversePreOrder(std::function fn) { From 9577f2dd49333b441dfd6bdcdab6cf20249e5ec2 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 2 Nov 2017 16:56:49 -0400 Subject: [PATCH 02/33] Start adding loading screen code --- include/openspace/rendering/loadingscreen.h | 59 ++++++++++ shaders/loadingscreen.frag | 33 ++++++ shaders/loadingscreen.vert | 33 ++++++ src/CMakeLists.txt | 2 + src/engine/openspaceengine.cpp | 21 ++-- src/rendering/loadingscreen.cpp | 120 ++++++++++++++++++++ 6 files changed, 258 insertions(+), 10 deletions(-) create mode 100644 include/openspace/rendering/loadingscreen.h create mode 100644 shaders/loadingscreen.frag create mode 100644 shaders/loadingscreen.vert create mode 100644 src/rendering/loadingscreen.cpp diff --git a/include/openspace/rendering/loadingscreen.h b/include/openspace/rendering/loadingscreen.h new file mode 100644 index 0000000000..de2ef8dad6 --- /dev/null +++ b/include/openspace/rendering/loadingscreen.h @@ -0,0 +1,59 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_CORE___LOADINGSCREEN___H__ +#define __OPENSPACE_CORE___LOADINGSCREEN___H__ + +#include +#include + +#include + +namespace ghoul::opengl { + class ProgramObject; + class Texture; +} // namespace ghoul::opengl + +namespace openspace { + +class LoadingScreen { +public: + LoadingScreen(glm::vec2 windowSize); + ~LoadingScreen(); + + void render(); + +private: + std::unique_ptr _program; + std::unique_ptr _logoTexture; + + glm::vec2 _windowSize; + + GLuint _vao; + GLuint _vbo; +}; + +} // namespace openspace + +#endif // __OPENSPACE_CORE___LOADINGSCREEN___H__ diff --git a/shaders/loadingscreen.frag b/shaders/loadingscreen.frag new file mode 100644 index 0000000000..fafa70944e --- /dev/null +++ b/shaders/loadingscreen.frag @@ -0,0 +1,33 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014 - 2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#version __CONTEXT__ + +out vec4 FragColor; + +uniform vec4 color; + +void main() { + FragColor = color; +} diff --git a/shaders/loadingscreen.vert b/shaders/loadingscreen.vert new file mode 100644 index 0000000000..40593fdcd8 --- /dev/null +++ b/shaders/loadingscreen.vert @@ -0,0 +1,33 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014 - 2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#version __CONTEXT__ + +in vec2 in_position; + +uniform mat4 ortho; + +void main() { + gl_Position = ortho * vec4(in_position, 0.0, 1.0); +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9325bfa8e5..1423d16e05 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -120,6 +120,7 @@ set(OPENSPACE_SOURCE ${OPENSPACE_BASE_DIR}/src/query/query.cpp ${OPENSPACE_BASE_DIR}/src/rendering/abufferrenderer.cpp ${OPENSPACE_BASE_DIR}/src/rendering/framebufferrenderer.cpp + ${OPENSPACE_BASE_DIR}/src/rendering/loadingscreen.cpp ${OPENSPACE_BASE_DIR}/src/rendering/raycastermanager.cpp ${OPENSPACE_BASE_DIR}/src/rendering/renderable.cpp ${OPENSPACE_BASE_DIR}/src/rendering/renderengine.cpp @@ -275,6 +276,7 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/query/query.h ${OPENSPACE_BASE_DIR}/include/openspace/rendering/abufferrenderer.h ${OPENSPACE_BASE_DIR}/include/openspace/rendering/framebufferrenderer.h + ${OPENSPACE_BASE_DIR}/include/openspace/rendering/loadingscreen.h ${OPENSPACE_BASE_DIR}/include/openspace/rendering/raycasterlistener.h ${OPENSPACE_BASE_DIR}/include/openspace/rendering/raycastermanager.h ${OPENSPACE_BASE_DIR}/include/openspace/rendering/renderable.h diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index c3e012645f..483cdf215d 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -75,8 +76,6 @@ #include -#include - #if defined(_MSC_VER) && defined(OPENSPACE_ENABLE_VLD) #include #endif @@ -615,22 +614,24 @@ void OpenSpaceEngine::loadScene(const std::string& scenePath) { _renderEngine->setGlobalBlackOutFactor(0.0); _renderEngine->startFading(1, 3.0); - // TODO remove after moving OpenGL out of initialize + // We can initialize all SceneGraphNodes in a separate thread since none of them use + // an OpenGL context std::atomic_bool initializeFinished = false; std::thread t([scene, &initializeFinished]() { scene->initialize(); initializeFinished = true; }); - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_real_distribution<> dis(0.0, 1.0); + + + LoadingScreen loadingScreen(_windowWrapper->currentWindowResolution()); + // While the SceneGraphNodes initialize themselves, we can hand over control to the + // Loading screen rendering while (!initializeFinished) { - glClearColor(dis(gen), dis(gen), dis(gen), 1.0); - glClear(ClearBufferMask::GL_COLOR_BUFFER_BIT); - std::this_thread::sleep_for(std::chrono::milliseconds(250)); - _windowWrapper->swapBuffer(); + loadingScreen.render(); } + + t.join(); scene->initializeGL(); diff --git a/src/rendering/loadingscreen.cpp b/src/rendering/loadingscreen.cpp new file mode 100644 index 0000000000..2a82203e58 --- /dev/null +++ b/src/rendering/loadingscreen.cpp @@ -0,0 +1,120 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2017 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +namespace openspace { + +LoadingScreen::LoadingScreen(glm::vec2 windowSize) + : _windowSize(std::move(windowSize)) +{ + _program = ghoul::opengl::ProgramObject::Build( + "Loading Screen", + "${SHADERS}/loadingscreen.vert", + "${SHADERS}/loadingscreen.frag" + ); + + + _logoTexture = ghoul::io::TextureReader::ref().loadTexture(absPath("${OPENSPACE_DATA}/openspace-logo.png")); + + GLfloat data[] = { + 0.f, 0.f, + 1.f, 1.f, + 0.f, 1.f, + + 0.f, 0.f, + 1.f, 0.f, + 1.f, 1.f + }; + + glGenVertexArrays(1, &_vao); + glBindVertexArray(_vao); + glGenBuffers(1, &_vbo); + glBindBuffer(GL_ARRAY_BUFFER, _vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer( + 0, + 2, + GL_FLOAT, + GL_FALSE, + 2 * sizeof(GLfloat), + nullptr + ); + + glBindVertexArray(0); + +} + +LoadingScreen::~LoadingScreen() { + _logoTexture = nullptr; +} + +void LoadingScreen::render() { + // Clear background + glClearColor(0.8f, 0.8f, 0.8f, 1.f); + glClear(ClearBufferMask::GL_COLOR_BUFFER_BIT); + + _program->activate(); + + _program->setUniform( + "ortho", + glm::ortho( + 0.f, static_cast(_windowSize.x), 0.f, static_cast(_windowSize.y) + ) + ); + + std::random_device rd; //Will be used to obtain a seed for the random number engine + std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd() + std::uniform_real_distribution<> dis(0.0, 1.0); + + _program->setUniform( + "color", + glm::vec4(dis(gen), dis(gen), dis(gen), 1.f) + ); + + // Draw the background color + glBindVertexArray(_vao); + glDrawArrays(GL_TRIANGLES, 0, 6); + + + _program->deactivate(); + + + std::this_thread::sleep_for(std::chrono::milliseconds(16)); + OsEng.windowWrapper().swapBuffer(); +} + +} // namespace openspace From 735d6378d9c2378426e1bd1bb7b8026a147a4dd3 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 3 Nov 2017 14:06:51 -0400 Subject: [PATCH 03/33] More work on loading screen Multithreaded SceneGraphNode initialization --- data/openspace-logo.png | Bin 0 -> 57798 bytes include/openspace/engine/openspaceengine.h | 5 + include/openspace/rendering/loadingscreen.h | 21 +- openspace.cfg | 3 +- shaders/loadingscreen.frag | 5 +- shaders/loadingscreen.vert | 6 +- src/engine/openspaceengine.cpp | 17 +- src/rendering/loadingscreen.cpp | 253 ++++++++++++++++---- src/scene/scene.cpp | 25 +- 9 files changed, 267 insertions(+), 68 deletions(-) create mode 100644 data/openspace-logo.png diff --git a/data/openspace-logo.png b/data/openspace-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a34574d42f2af55ebc87ee526ecbb8f7bba9386d GIT binary patch literal 57798 zcmeFZbx_<(7Wg{^cXyWnA;91k+zB2aK!OeK9^9SaA;F!X!AWq3;1V?G;I6@)H{|Z# zdzanK+p6F1{qrhQHN|wFKBv1sefqR~X9!hNkU~WwLIMB)s4~*xF93i?N%wCAI7m(! z%EcYz52CHKrUL+gjD7!x0wkpn002meW@2JWN~SiBHV&pXwiGgAVidOaHpXU_MgV}z zbgGJps>&vwz!~^hR5rxty{yd(Tm*_2qG3KbF|-Vnh#1d9C^M&Um0K|-Bw&cUGeV#v zBYnbgl^IdqB2B`tP-aE?XNSD)y;^k7wwP!*TN}9j@?CJf=qR;v2)+dYB~F@MnbQZQ zK$Hw~!N04cb8&@P*cXY$7Jz|JX-MgCLk$Hu_u}V&O4S121b}iGMMeO$$S1e3kObeM zZ;2*qLHPtgwK&G`$RhaQ0EFElMRNhdl2AUW3A8GJ3>bi3pMm}|K#2*UN9VmZ3h+ri zPjrU@ynauK3zZQApujc?5eJw)0~8Itd@BLaU;*G5%XaVrCYb;%GU~?CfU;^p%OD1F z1pomBz@i)y_yhps4$$kSrgi}YBm!_G57qg9JT1XkXMm7Otl+Pu;g#@xjlg6FuddEa zMn5Qn{}hJ{^azwB%-rUhNXX{Pfxd9m4*+Du;6i$PbmKCJRx&uq6dMOG^A6G1pk8&)Taz~37WEs;3oribvy+9UB^qBgZC@#v^M$tlW4RoXp?8P@ z`m<|MQrFeUch{t&R2?$W|=M{GG%p`!1ee0@A}XSg)acm*vu&j~0q z7ld+;VanGwfIgL_!_aVSSvms%4(n}N$LJBDeN6oqhn(;Bg>IxW9s_)gW!~8V06O9{ z%&NVWLR|;|fOv*4LzxKiaVr613mi!+>})IYwH{Z1C{0J3D26DKp%00@-jgCfQTm{P zGExRT;FvHeM~kv)P^>K)bDLTXnt(0(B?uv{mA=6j5k{mN0UJa)9Rj8QR)abc0Y5SD zoU&CK1}lt)q9+7LnMyW>Q=0X~TU81*Y0^Uh`$xRNT9S1!&$)N&;iPAbX2kVvY%<|FfTd?^x0U0HKZA-C$OThff()~VjbKxSfOHUbhXisWXeCP zj$n;Qj#$5--efFD=8z_$j_sK?w5WjRea}eov^#Jmb)|iUeT8_1^6)iMs=lz@$8(h> z`WL6uyShGQM(+y^p+_;2bXeVbAn@f*;`&M2rsxc*%sCiH~K1n>kVgw6z|339oF zqhW+ysw~{B+X?H5=!rIJ8Vpqo9=I{RNFSbmp#4D108}j~%q;{LBC8##^{GY`=%{#> zNUE``EEgb-#eGsMOjX)eAyzRdc$ObrXp(QED)iAy^ClEdvt6!TQS75~M!D{CLnj5Y z0b!*JBc%oEr=T~!oE@~~15+MO4D|^&r1fC}g$0?sV(ib^)hk~dy*CzuUrw+)PjOU0 zT&K^AKk|AD#@T9pviOri#1Q`c3dROa%K^6=yZ_)bcg3oUYpGwa2jo2~Qq64IJ^1!Z*>z5>+ zz#G*YtINC-DHIL3N)#Hn8I(Z+{;!;NHvFW(u9+XoV%qJ(VmY7>p!mgrnA&0f>~YBm zN%U~DPOW8{WvfoB&TLvZT1r}3`7Qaqq!;qFxleMR$vu?wb(q8tXs;8=;ny_qj5^koX%Bso}WTKC6QvJ20eJ{2#*0att;zj@-HFyf`6X`2b zFNe?eID9Ypz)6ECdqSRHkK;ChCK^K+LzC>wRoHz`I?Pg4l1HUX@J(tR{EtGyLS_B> znBxVRlZiX$x*J!XJD%8SFS?DKq$XD;x9#T7lh^QD zRCibRDYo0B=?2b=c~jpR-nrck1M++xLzzH_`il6zesp#2=0VvUDa1EpbXqaBD-xbj zD}pN$pvh8C#gwK4Gx9<&AuYxhE-3k(ia4-$&B4bdPP zW{u}`9-b9*m>0hkj}r$co3JYKmNGr(WDqQ5Hs_z_uH&c^>SQ+7>>t-i)sV52CE;ae zDB-fUY?{B`Jqlh*T|!=V$4>xuYumbJ4z?W!=HV4EV=?l@3Co2j@Vq||!C=V83x9lw z?ff_r$+eBN&4da#%rzV`DW=6hrf?|DAn*2WC#HeZmF6pZ4&!QV09`?JgB+^dTryu8 zUXl}q6`KHGBdhKXIi1Fqc zm?`ZwT3liLT0ELH>eTdS)cKd6cTY|wW`OYn^fUGTRbsW-&HObDMtMDh1J8t1KdK(9 zUaE5Kjm&)keLuGz-XnHzIOW(GpLyMIb|cu+G@}NkCO>XGZgeGcRe2^<5?Z)<9(QG> zU94bbx7!6h0(}EdjaK!&c5Iw*9`{e&CzZ4>o6P_MY_&0t^slr!C4af4!m?y>N$$duSz@VLwlJn3E`UG1#cYsJpZa`!y(!<{M)SCz7l*q zpx62M_V>ZzgyGEi!}!={E03I;t(I-V(wWY-PO_#R7j{;iI z6yEfArWfTWD&O+1VhLko?_i)4j;;;ZnPvb0J~lH|4Mz=mIX(j$D`t?PjlL1Hi8 zBMJcU3%S^W3@nTsDfEp@%&Y~d_UoIeD9j86sMI;-S>$cSj7-g>-RzB2+!Ry|+$;=u z4XK0#k@#KsAO@_A96=N=R+iQdd@cf1Kkf2CKHp~psVIIHakLPi61lICLPK7OLd?eA zh=P-ui^+hMm6d{v(N)QS1iQnGP znD2$S*W?T8yW7}%THI-1#7Q{3AH>D#<<6riHIujtR$Kl){5`)5Vg4*y^Wp$K#V*#cRa zS%CjVNnZZfYF1YNQPaUu!U@unfAs7>R_LJWYHI|1VdP-*#@@h4!pX?mk@`P1!qDJP zJGO7^Er0ScGyob|8d*V993YKn{f|ws{pShfPxXIk#-HN<+IvSc|Mi-wR@Rt`Gn1ZZLj0^>V z|8(GgX8dm>_s4kt>&*IRK=}8W$Zu%CXKZ6{1#%QLvjUkI0d1{K_7Ubql>5q#yL5CMoq7*U`VqC0{fWpMa3<)QHnfY6}zuCE8 zJqH$gh*Xas8Ul1L<#E4}knS`5V`-`8<&R z#`OTmuam!V{hH4M>2F*Qfc!f78`rP-Jdpmz^#I7PlfQBOn$H92Z(I+6{5ts?*RT0J zkp9N?0LZVCzj6JV&jaaiTn~W!I{6#dulYQX{>JqH$gh*Xas8Ul1L<#E4}knS`5V`- z`8<&R#`OTmuam!V{hH4M>2F*Qfc!f78`rP-Jdpmz^#I7PlfQBOn$H92Z(I+6{5ts? z*RT0Jkp9N?0LZVCzj6JV&jaaiTn~W!I{6#dulYQX{>JqH$gh*Xas8Ul1L<#E4}knS z`Ab|#fBg{D$QttNPiM%_Jn?6210X*Ur7)0wArAn!Jq7@L`~iUb|6t;?2>`rd0RXo2 zAipk6008i9-sygo1OQShWyD2PU8Z;DoLxx9Ghq+d>$f}~Yih?8ZGl&GY-7t?;h|bD3sr~UeI}rUn^ZP&ZyZ~kV2pwa@~U7X4D$(rya+f#@BfIz{w?u4?>Cpfxx@dfZ~Rf< zUw!ERJg=K?Rs%*{93O#=5CG+;m0OGLM*#qgG2jB;+>sZ!1F!dyuOPk)sYol^?&vVc zS%b!-EqehM3r$nTm6>?jCto0+va+kNcE?vYiV6Y>^OAM3FnRA#8zy#n zF=L>Apdd*KdthB>1-Aof$(oeg7m2aq_d&`BC@36+Xqn>xv{^b%%OpOXJHZb~yC-i< zOBqF8Pw{^KkS_LGll7G>^0yZ+e88tKy=uifGGxR#2n_eg5T474C>@%@JkFt_5QM!& z3_U9UtIYfM-W$m(Cs66r zMM&Du`P^^w-jMAao!s{uD>f#Z+auA=@L+E(ESTEaodx3vThmIgd<KTL);c;6P3+-HX0erH*8^1K zQEQhmx3(12d0(7*8)-PCM1N)FD|cXvoYY#G=W{k#UY0Be-AS5j-9_7KAb;*efT7wO zYBfe6MKtuDm+3}eRC+*%k-9;LU*F=|uEog$EtlmqTTS8-J4vy#%F(>j#LKLuE=8rL z3+&JFxaIG#G5u*VQOQbMC}^0P;d<1y!&(v+{jp{(Pw2Iu+r|>D!BJbj`b2_4TDsUF z^4{}GNOk`_C~6|~ey&*3d0dNsEU6)*#$ycXZ#VcR<$^w*?I_zmtAvhIA<65=`9%3g zdl&uHn!x9H{3An(dnOer{d1&ePF~%4StYTpT4y{38RH7l zaH+j~jttwwnMG3S4%Bt8lZMAf0@6;ojz3prRDZ&5)YjhEwG1wN?QN4f+eggC?Ofw} zCA_wW#!W6%b9LD|2V7{wBY+voz;Jo}}h z>2m7zhMC!ljKqDrP!h@$Jl5x=U)eu~N$fXYRC@LqMPWa;upT?&>sM0-Ch?;S2UBLD zsJNrYrp1h6?%Jqhji=?uS^e$UU{K6KXo#$W!5i!}Ebw^e%twu^-zv8Tt@Rch)PdveV0P3$xKhr|q& z1z+{bX1?S-6<=mUS3E~o47dxyfuV_z)Pn>b+6Z5F;cw9;@z#R_QqC6C&xO25bJFjU zqsBrBI0!jHwS3j}FlguP=p=RB1y+J`=_^?DLXcRoBEOFgMd8uoI z;X8{Ppkl3?k<+}I{IOUZ&q4)s)Gd$;E2FXwO=$}w$s}(QNrLwaK3|?~uA+WT;0TaQ z@r{-l#pE!DM7Mgirf07Q1g3R#03cx~)Kl-a@GM zl$L_(u447T>4s7IXEo@niSSAehc$BziktMa9v+x%W_Y4`{UVdc7YCtlt3UaQCJ`x+ zh~I82W4MIqSzjWfxc&z00j}f-g%%-7EI> ziL(v$Ik-9Sr&%stFW>LY`-2A+yfpn=#4!CrYSk(9_pk!w(#G#X=E&T*>!*{HuLtLq zPgkEQot+-^QR#o9p>mI_Gek5NILGkt^Mix)qtvKV9v=^U=YOSCvnus|XXdM|8GI(Y z{Vkg~Qyt#hax?!row{<*;y~W+n{{eazSOV4pV4ARUdfViP3V*$D^HG<4DE4{yaTkP z)!>tqi(3mE%enB9IAbpnr*1knJXZB0(Xn^ii==o~fprvnYwZhyABc?Ro5I?v(?k?4 zu$zAd5+ZZ9S`uuVI?U1iMtpl`bUWXcEU8VnM~pCzUpOfDulA$Q2I7_Q@DBRy-Xr_S z1x3qLV$@&b1q{`a_$6OOH+9V@tNT_0jWe9)jlFp9Cm4sSC>B=PMkpPIAlrC@uwIVT zCTA@#&&>rbj`jT8v`92BR1?F8B@IEI&V^PMxxIdctyn8UxPmIUyaFS3n{5)b)#$;>eyk z3|8kSOQp^5z6QC{O&0`ZL;c_Z1tZkKh64n5Zpe%YHD!U3lv+NdOBY_5w98^1gKp3D z6{e_guB^`{<<<2J+=(F1&Z+Rz4S70N8-pEVJL-v$?f^>>E7P1<-WG7TD&!Gz{rxro zT!rUFdEZq-WCL}rz}n^;CjN%T{yL2?)a0bAt6M8~F=z#^I?O!5I~>x8h-)^jXd6qa zC7T9x*09pxg+1CEokSurl#rI?k+olw3imXjRSXrELCc+Te6?e^N^Ghf=B z(Eqe1y)wyKv$RQviGj&_@W>isjq}uz>&wCh{9SvfA~&nvyWtkX#vRFSLu>yE9k=2zejhW;A zVTg^E0|fJu|4bd`tGKN9v6byY|A zW*YF1XEhga1f`{TW1s;~t5pe}iw;s#rom-=;rk6@{Gc$D!(JmY`(E9^j@10OS`FfD z!mYgx;WhWW_84?QaT{muzI4k@G0e9yX9B+6eWl#bET1E$2v`;KH12)RpF4s^PQPFN zXB+v;cM7^<_-#ryAOpUOJ<9HAeFt@YeT{5dxL~SH#NUc-tk=Jv7O1FIv6xI9$}QJ< zihFxSm~wUS7>^&aM8|9&BBQo`0rLBYj2TwO`Mt;Dn%UYaU)BXK?wqD;9B|M;_OjA7 zo9ZFWyCU6lv#8(IF}daR2Sa;{iDS;9f-|>3r2_YM!VqO;+D?G{(9NJsVMdt+vyohQT+CjuoY0k#dpeYG+hG_U6U*_A;@Vi)ZSm+?dI` zzJo&|3y&CG?)_z*%h-&s#N%4Idv;D*5f-GTTGOQ|=o< zILHhoOLoIad$(lnk1rPH&aK8k;&Dg4GjqVD+SthGV6Q>u+e%P{YI@S62|d$=!76>q3JY; z*5f8vi2AC}CgbIiR}9H(8ZkLLDpzMDvbE!Ir6h6T*e!EqwSAe^-AZ9bW-^Nh^^d<#nuvXkm0nB zkF`m11fQ!rI~!~Z>9jT7HNby){`C=E-oy+z=#VMBot*~<>dEp+8aCVzMNDJzA628_ zvUM?QXI#c4)R;+)%N>@I=$3Q3{7?0%8i>05$VH`wa^^o3aqj{FGmlG!2tHh%AG*|{(+*R3_v ztL^uUruJsLtB>=+^@N~NytX+ZfIiq{0wpDAu)N8WvNAs73k$y0V-h_nL-`b`*zQLT z`*oPed-Yykkr-6^=@pJ33U)atF|Z!v3AJnlpD~ zaU)@GwueL@gsEvrj<);bxZ;}uAH4#gWm4V7rVhzj(-D-Dm5Vxe3>GG!wi-w)a6v1Y zHx!S-buz!PnN}@8TFi4;ubaWU&x4$FZ^Se_g&X_3&=ps=wt4_?Ka4s!aFJ1-_EkGT7Wb#(guXe*98F+8MVrQc23W!sJ( zDYgx0Ticfi&UtpiG3M9R|2P@FYx!a0Wk`vgnDsK#d?@MwL5ae4O#H_u%gq$$%R$ZB zYIKO}iLMUmijEHtR?tFrRYW9|<;pcT<0gx%`_yjwrk+=hcOUaAp>_FhQS7~^2SjP# zWvnd>VXRmqq_Jp^w81C&R_>>a@jsh`#!(7Sj_ln{7n|!l`*=TYmi;sH=*|s4P##eS zB{oQhPJOSg&a1>u=f5_=A?dzvR_5)7Af%-j@3^G4od!q1@cP-1HeyDlC0l$k|<<(toAqI>3euztoVr#O?@ zsB&Zz;*f|S;ESZz7tXpiCisHJ-82=NlK~-RPu4nFO|MZP$IQ$emf%1+NMth99xc%g zf^BXv6W04|I8~oE$n4P(EyJ+^8l62E5zEcV-aAm-DEy>Pi49rCHKk@dST!z=v!i=f z-GGW~jP7}Rd!1cTx8!fp$c><|r|2yo2_JMMzFIG;jT(UxUZa&d_#Bu&eGBXMY+9rH zem=(`A|QXAE4@S2X%ftWKi#O8HoFy2DAR)$3{Fsy=m1)<&|7F?&_vPE;R2; zROEP547L54AfJ`;gE0AMr&!aKPFCA~oXvBG~#?~y)BcEU!KUw#(kfxsI=j@aIqd?)Zl-W;ma!d%xbD8{m=K01GJmwfu^IjoT z$f2FS^CO|QmI%u^-Sn?zkdLH-jY$e~nt441o1?|$Mj6S8~r(E?xVEoEh` z-!MW$2iiN6b`0I!hQP`iu8$S!ka%vXL$3UG(Uao@pDTJU+(A~Ifwsqxv3%t5=-PIU z{N?LYuQAo;{upAQf z43vtMe{w0gcwAR^Rv8kc@7n2;}Y-vghRj1Shy^=Sw!FT8giqMqba` z`0S?QR)kc2*<* ze!FQ|a=BY<>l{zKYqwGFvY`#Nv8148!_9TO%9-zW(Vfzb`&s=1wet;oZhRK*aQ!BJ zY{;Vee5dGRNDMG}$6`lA%b1cojtrVW5`I*+ePg4P=p7mOd`0wm(+vV-=Z0F13O9}H zX(d0Birzj4^a*?pi9mz;U;V2IcLJkF>NVAMB8u<89giN{vzmWEn!58K+1MRMjJuq{ zHE2xUyA>g@JA!6Zh0GXxxaBo@k=YuWo2d58<}MLu-TC<|`#CsAXU7VqP%R}D5E&BJ z?aZ+S*X8MEz`*(Tp09~C0rCe3PLf0l?6MtSVyM>cWu{bH-UR~uv(+8G@L5kO2vg>r z9d}@MW7Wt{RkHy3+C2DoJfY8OG0=Re^Eg&}YeVG1Yrpj)n+r1?>jK7Z?$q7W0i0ue z=$&qS&wi{P?UxB_D=EpLWYNm`=!^3|qM}|T#es3QAbc!!Jx>}@j}u|eV?{lj#fE%XTU1Z(xnGws=em(buR0M<@Ty3Vk-NM{D zNq+ZTn{QJSt)g%2V+^|VF9n>btS=q27e~efJx>a%tMTQHjPikuqMjhi_m9^z7#T!8 z?J!m6<`_NTvETEdIn1A9MfcE*L(h3KsG&Q}RYf-@-Mgi{n)wD-@b8!)0 zZ}$ zVAJwenEYjnuYj8_n43juBUxQguiH@{yR;b(+3W1JFzxr+ zV`bi1>LT(Tn4JQk97{`WK4KUloF!>shPZ`vs63A^dNh=kQ}|AI4a2mwKzwn1pK`Nv z9BbS6R4`F45|g{Ab2C2OF!SA!F?5+!Y(Ir_EHy1JoL%-3CN~3FIdw2K11nJ)IPKdR z!i!4vb#HG^_-aa@?qmkEY#oE6_yhVxz96 zr&BbMiWM^7v^=`(vm23V-AR~Ft-a2syRO?($5x0-x@NDuc^CGkHPCSYkTZ9#8$T); zb`WGSAsc_BIWfP4t&z4oD*g32 zQ+UHVXwxb}azD2|o_opL4u7!W=Fxo&>f!8OVe5!`D|YzNkVa8X^00#j1_qvB$7G=lC$Bz+QJmk5Vo}SH$^Xr2GwkUyJ_L_((-wvL{vffoUzf&bg z88?vKO1Ry$$vERgQxTGyXBF`D#N>2E%A!@=oWFmXnUtcE-!mXGu&I(S=!s>;nNrpF zA~3xv0`uvLkZ;Dyps>GLoPRYGZ23q93G*h=^=ZcGSTJ{o%s|?4BI&I_cWl z`wUtDhz5;Jm%w}?=RN$pt=bfa^UYzeTpT zw%>4vSJ-iX!+TZvqq8*KXw_D z2nju+r7+Wa1e2ICimrdnnl@=f>|S4Qce+7HGXEh0+r3g*({d)JJib^9#@YG8$5{k& z!)bkcwq8V|KyTkqAHIj4xa3at^>jpbbME~Z_LD{Bx($P*^BpMGa)gjjiKSI_0HE#} zpOV8RO`&3{@JZ-$zTk(5!A{AS45;@6r;oWL!AC3sVf!;lqxGBQ>3-jODwUJJ1m7>f zD?C9GfqI=I1qro|4EieiC}1OEViM?U56T=ly9?FdlN`j`fxJ|{M_PbIBZu5WgZ#Ptp?8%+Ir^t zkYi%=sO%5NZapJ%)|EUDVG0$EvoBZdIB;-lJW5R#@%O1$@7f%wFblA+iV`zkIBzb?r9G1|2) z20S-;XYDLje(gkl1dm`EIF47A47$yupK?)#j_*$I_(Wjew9_szu4YbtBlq;IzgIv2ll2KIX6y*_b;0w%=@b=wH-RW}kvFHxj$FL+?rEf7 z)adUno^|Ln>Fd3wUR1t3Smj>WaW54v-daVJLrLOr^b>_HJ6?m;XxL&qKX*UlHZvDE zdimB?1#A52SXoU+lwkUWi)!3i zM@rM7ofVziBN~0xT;&ApKhYPqL;_FshQ417&+0WtG|SPp_kUV@yZ3}`tiE=A)M{gI zNB^t6XiT=$v|(99re~EdaPHWo`aI#~<&I<&B2=r)69k3lt@&!zqQx&Vna^+-D$ueH zyFseyJZ~Y7{DO-ddK<9wpT&fJvr|DC8l%i|b~yj=39_{niG_=gkD_}y`-MOq+=P&( zLqL1GdIFUBhle+yX*UN3>nw30ZDIC4@4~p(0;{-Z=pbK|7>2 zIt5>%M{a!6%5NBvKS0LT&PC`wtjSIt_{08mxPlQWKQ1LLbYmu1zPA2Z7M^zC5)ix` z3UNcjXLbd{8aEvg(q!c5b83S9TZFZfW%2lG3(3w^dJz8`9-UjZU0qt;j<7qTO`N+L zQ^%_pnj3t+*5ToIQYyq`R2CtzP7LRo5iO}v1s0}^z%UO>)5Kg|Tw9IUAauYGFhB!j zG*-c_2A^i_d=pe90Y@=EBi8u1wbxm@&qZ}IaU~AxCI(_R9v=T01FI;VV-3b{)R&U# zM=4`_Xo`Ccin=oC&-j}z&!Tuf-{jAQlW=!^3^Y49u=U?F)qm2M{Ke=?H?ND|hk;N; zXguA|yM_F2Hm1jcC{Ot#x$v|sc_FO4hKk0_G8@E#DSz}@M7ykVDiw_7_wj}vin%L$ znF6;I(#yhnmcyE7LdKHS#iDz{;jIX~-jne=v3XD#48K(FjZK6EJ+~k_sYULg*`V_= z)8S8t1QwG}3tC`TkL%v=JXKd8Ro)LD=ZtdR#uKf{EjlhFYon1?$#_hX^mh#houW?b z5oq52(6IrXYY7Dri-j&I?gFPn^T4Zn4T*L23BKR!6bPZn)`DbbX=O$J` zI|*sGP$92tVC<0N0RSSTBV{{NVDIU!_&{j3(g_Oqlj!4l?+J2_oH5=3@IXH7&(WGV z$C_Q$IOaaOLyiI?~6NIAors0 zNlZ17(hHRlr$(T16?+@iSLwC+%R_EKS*UH~EG-DjLrdTiH{V~&+&`^Ys`aUR zH*)LHt)$K3(xhppMm8+)iuton-D0{yn1RlD^IntJO=Bv<2c$Q7mC9AlbiJp0!eTKe zd2hCNO{Ko>6^GO>@m1Q`!iYrVU#Ktb1lt@2(89^TV-{Wv&f}W(Ui6fsR>4cG(*T&i z$5UMzpc87D!v3o8S_kX}mQH=AUt#i9W8c_B9X;~){ny_0M+^=_Z5l&&dL_Noe1BQf+{F=+%B&AsGP#W>f`qZVyz2+>~UZs)Z zlKCy2Gnt5##j~dw$n?)Xgr$`**SNZ%)VRp<%@=)Uh|2U7Yt*TmChGe9W~eA4FYqud zw6$BU+F+${W_W7Mr^54@o!6NYWU7aOBhf0i^#)>~O-oCCoex+lw^9JYtoDx@MB9gz zUw@5FB6&yBqQV%|y1KU*B-GeMBx9g|MU0)ea(}dcS^1W)}gQIx( z40~V9vA#XcpoA5IVtcIPIJ`^fJnI6lo3-_PdIponj!=*zzIYUAU~~;Zah=}8h4I3? z?m44OD!q}_DtTEPWFwB^Clyh>;coGSNxVHVmcIb90(H0^Y$mh30TVQ$R!vIsxO+3F z=^XL(zSUZ0R;P_!jHKK1sh>DZ&IOJo)$!H+0FUkqkI(cy-w>5#PI;EWEG;m9d>6V0 z)j_T@J2YI^9RxV#f?h8KB^SQ(x1`<7ksKu_ck zq;hin8t>?6Te-i%M_+EpEGu93cuw6f(2pG{YlZeDEpCX;;ovOyolKs2Q95Nt}^gMT1o_pV7t~*;v1XqCdNQ<7i;*j?8xjYf= z_gW!O;zG7p1Rgs%9ekt>ZYLWM!AZvo`~n0cPJJDx>NrL-1%LUZ%3yiEP3!R^-Ad_` zwnfE{i{^yjv7RSz?ra^$kTdGUclth|p|=~NkT1NBDN%#mh#bkAllbPvPdEr&uFba+ zjL))=CIr@ehM3DuCRSJ_yqqRwT!k%N5!lWpIw{O2JU)VL>Dp)&0%u2wi*a*qj;eJE z1I*96P=cx66m1L@%%t_q&MG@!W90Et1=AQ{qM(V;E__#neA}*GYgsgJuelUY&vHKS zY~c3xn|GN}tcAh=yVF`elu=fFN5-0TpVGe3hYY+C9 z)pBX2es$h05$_Gm_prmiAp5y?;haoe2{sS%8cwl(5xUB;<@vly8Di-!{~?$nmeS+U zQOvzvL_BMY}o#^gjJ?rRX>J@HICVu zHv43Albr0ToD?3M1<$uGl9pVs)!1YJgQCJmUn}SXeM}-(GK*^?APqcme6twb+{||1 zf$a#r-cpxi)95IoOEBz=CW~PC!aAt~j!Pw78{@|mXyEW0KM>Zg>$8z-0)H;=DrG#i z{j{ar!1>dc9_7RiX`ZO%$R)yan+7-gj?ZuAu1*8x69-}wNwDUJNnmsY(`s&7pMD`i z`{G;Ra!JHC;G7P2J7teYO$4yGP1AQ9vB_*VZZ}1JXxt`vnuXjq?~WkVd`%WxT0649 z9}xuRTyfic$rvT_3OGmBE4H>5xiN)|Dd0|7H}h(E{9{0MeEd|iyV8iX3xnuz!Aha= zipFF%FGAe4K9Qg3HR>4*fou3#dzX8eYqmwHA-lPGVZIK$fdC%eX^NhNqg}GD3#k#E zgPfq(hZogFC+BMOtPyu4EtgT*CwF^m9!_GbAjrN2rIS_SeQ>!!-PL&;9ploH;CSGj zP3IYxexAH+`=h&vi_E#?R+y?#^y&}DMDg`j5@M6vO@*kl6F zKdPS*U91ZyBS=n3Y(pq+afp!3j7U)*vRSo4j#K2C-q^4Tfzg|vSmtq9BJsccNaHUc zmg`1(*?Y9&Rgy0<4LWLWz9v09Bf-E`LK$bW;P?7qFX!3XUIO1KP3U2`ou+AlF6ejS z?hXsNx$TAd03IUbpduvmMM~>{5R8{g7Md=S;!bbA!A3?GJ`51Q!M8<>8ab*s)QJ9= z^QoAWT<8*~s_C2-a?va=2nk3;G_U!+I6n_fM@7n~s;_xDJsG7)R3RJWU>eJ5DPS_} z7f)+}7{(8fdV+zO6MgMc#L@idBJa7T58xdg4MDRHa;G~&;ai4b)$bjCAAQHnNv_Y4 zi<-?)?W=Z42uNt;{AqZPTlhvPc$ZoT%10@tKAJd1_d>s9L>EyyIden;_(`#wLLQE2 zb2yp%tw_QHB6keXNdVoeY$)WoCUw4CBlU@WU^zAs&6mDIWZrN#bN1wso|S}+`0hNM zz-v!R30*x7?&E~Qc7IN#?i~Xnfjd2X=s-(5IjD*KU0fA1d^og%7`OIR>Bi0YgB5B= z`}Y>SH842?j2c{o*>qn|xCy5t23y>xnmk_%J_~7^Ws3(kpDVYQp9i_p#>b3;3mQMq zuANmccnJ2rld~wcX-U++vO8D?DkZw(X){?x*Vebv(F6Q!GhZUMwd7~aC7aZgpj~6z zz0v5kkI0P!jcZj5GjwqrRfa*X2!^Z&D?V4zqqoAXX4dMitwkObRK_$MI-P%w6gKf> znzmqYeH1&l6tK&M!{4uK!e+XQ7=bE3_FARHa8Vm2DQT6;{GF)?YCe#2J&+DbGnz)m z*&W{A8yq&9ex}nO z&6P{*+zdSA+pq`6KYW8r(GBfuTV)0!#zCxMSPA^VV>u<8Vgt1E9iNYgTk>QoD<<({ z3X^609L?MoAA%=Xfw#Je_3%saMZ&YIProIkhxq}8#Cs-6haK!-sUS~t@i8z~vd5!C zwZ}ULv4pSoULwX?VEk9g}|-}z}-vq=+9bDMrTG`%iuDq~#hd^O9;bhiUN zjrf{;Ge{P4vs82b;@{tIS>Wm;(?3g0t?$l$ATe9*?NG6 z65y;y;o*mSpbXRclm#G2nF;wMvbQHlfdIwV7(^tykc9N2v;D&@*FZ{|W^5P7!Bhn4 zsOmG2%ze{X|ZFVq1lT9Az?f(H)LIT~_c zlqgg)sC}Ur!B5we-a|}mVUt{MwQCsgUAEf#zW$~|3>fi&q^NGq|A(Zj46379qPV-e z2MGj+i@SS*6WrZhgS#XU+@0X=5L|-0ySux*<$FJFZ536^%=XOmOn0Ai25~~g8O99M z9yw*R4rGA>X#j);PyASn?JN1<-@Ka+eq|=Y;P;np!X(DluqNgvFV6$^yeUSieMhcM z{^r7%erjiS&YX(Dhtb5eBAS<;o=sdFgpamC3`oAfgBN%J^AHyYCTl|m3i0;yCtO%m+r>L2#U?H%(g)NRC91lP#!(5D6#1)%JIrV1tA^*F-aV_d zi!YzXyl?F45-v^C@z{yAbX?*OB`IJ$L6x0m{<5&6$-Qvp)Uz{SpI3cGhpA~x2{quu z8S;+~g`G@3eN^LItv9j|Sr>!J2=Kw*!w|`5m{>8uzkY>_3itQZ;DQpytMg~^0bR@y z(Hh?xb+`G85Kl>d zOa4xNG&ZkvzGs*D0`2Sb8VP^^9GYt)rQuptBru9XF|zllPQ_ev8(Rs7w+v|`BVk`s z01k)$e_txDFUJ63XqX)qDXPB$_Ye|tWJhDLY!RpJpjB)TZ>GPngKs`KE2_2TqTnq$Itu|xkhJ5--s%(6U4EOZ(j5dH0on($$se|)7e!d>R=8_RkbpJ-`@;=46F`etH{fNo%7okm9J3Xen zUBjcVo*v{$#*~i68DB<=d0a45i|W=%ov*J%CN8*Ww-kx{}uAT6>!iA^TPM;6irz$h8*JO8a2?ImlYA{qyZU_IlMt`08>0kgk zEHQGUlU_R+&r<8U*rn$Fc}ZHW^=IGLk&N8o-EV1b7J6oqG-sKYWb~xyU4pDvA_sB`;!@u* zM1Co6;^Kd4Ih|cT)7Nj!OX)0gl`HRLKw2=nWMMkPeO~Z?J5E4ASJk?F0CwtcHf+l8>|cwcZwUzb1&5Mvp1J*4XVqY;u~ zPrP8>-e~E`QE~ZLLHYRGm2FgyI4^TJPpo7*ah_jsC9IErU*y2x)sRZC6vxGiQB4^ld`(OQo?2;jyry@wzQ23w|M-uZ zT(AfU`;&`;rfyPYdSG!Av-B>7b}|kMnElmX3!?QNUK3C#NSy*TfO<@|^gl>Hi0FK&j3LQNi~{F#Yw6 zUL|W-;=2nrR0`XQY2h2@45gID$_l9(VtI{!GP8DSMx}-ndiB?XC)=yp_@nKx9=0wI2BhPA+BO@ zs=Co9H}q6YSfQe&#Cm(#gA~)FF5oRLP*AGV{6&7nSo90!2>gEbmAJDS&))TBp%0EU zirpi#v^o@1bYq$_{ep*%6U7TktefZ}t!ae5ql?AvTCv>4X> ze11L3`+ag{Vi-YEEKsprBD992o}1?D%e7ki(*Zx0&3aTwE(B1h>0wg>ceAszP^?1Y z-|GJ>c?iaEw93A)O_$t(V{vvJN$ihKZyS;pOM0Y0;@?sf>e8cX*YOv~8GMTWcX1?n zPwC-YSIjF@(wxEgmpdR6t_`Bs11YP~8K z{paeDwY8AVm^3@q9;L^li>DUPF%lL;Z!Bb0A7JG(-?uQGIwJoeBc&uCJboMRtsNO- zhJY@0EtSamtk()Z{_m&p9{Pfr0HYVfh`FZHt~0cV*Y|P3v90b>IEqMcC1<$*-}3bq zlVOuJR7TC@%+gFZf`nKMI#(J2tgD2!_3CZCUFx%(4v4I{e(5WoIol^<%p=*;D*Y#| ze0s|<&Sjt~)h`^bEK=R9i9?O2;dlwCB$nPJ*@z#e0o!-GO>5hTX{>dbK%5Dh(Wcouc%9mN^ zZ1%6K*PB4fdfsH~bery|Vnqqf&gM+NkAfl;KJ^<9I%A!8_m55^fGJp#DWyD5A?pR| zp_Z3?^79jVQ3cgdFq{7>cXoB&9!G6MHLMUoA0H|H;Vff{^O- z3M)PVrM{Wnz1`rUSb^x2Bm~>L4u=+<@`LmIq!*~k|2UD)A4?5Q=4g0;-;Anck?z;+U zE(fe&79=+sYx_-1HMS+2PU{bO9tMCx1wU-o6kxI#b>Xqk)MuA9^)n7rSe)3)eP_1a zFblaXn16c9-q_(LdgD&}9T7$(a#UZsad^&T$==xEB{ zzJ`6&ivSLJ0o>X9a$;GMKvg^O!+DNFG`f>qxdIFmQ!j%qIEYklhJ<&}gaMy@XzBC6 z1TLoo_-OK-IgcbS~n4%)Yh4ited z@?WA_>+k&6v((OE9D^qm4akHXiWGGvZHV6$Vp{m6a@Ta93SX(0SF)yGZWO?zweU z1jMI-_iIMO4_x`9KxfzpQ`5NwTay9C5>bD#l`r`DhSCsuxt-n`ZWbPLYAVmsv66#3 zsUzqO6=0l-pg}!|4~XpT+0L%X`*eoR%tNg3wWFo^asbKGBL(A{TwuM__v{T(?%QiV z9Yp3;<=l@=ryds^cc^azM*#$R#f2yyo(fqIP8Qw9c-E^_6ftJI$Dt z=y!3mct@eSI%|S_e<#}rZquyfM_GqO+4CNHswnIb!0{cP96G-2?cH+esjyubH*~X0 z0R$aSg1`==n6zI%1mCV*U)+|ro3^{?(auBdT4#NI zm8Dr?+MZCo9Zp3~JdO-hh`G5ov<#yy;zl&TO0hWd;j}%{L128-{0h_wQ-@`aF4EHG zw-5ZihcHuQ;Vqs-ifzwL-{DEOVt;pPhxxr%;rKn@6O3rGg4>?W9qy|1c*;6ppfCD0 z1#NO_h)F5mI)8s~goUf37Hmz7;yd0=b@p&W8N!Zivx`fJ$^ADh$3q{ojDm;hz*>G2~Tu z^yRv7EBB+G`$%q@Hf}Wq9a$2cVRko69u6 z0VMC2zqaX*FO<$49AspiB)}#Y$fG3X^brVDM<(W7TBB%<;nqU1nE#qRV^*~!T1W|9 zI--_Ki$`kK{!vomn;*i?GEIihQ<&5UE1?iJfL&^8c=ika;_gz+r5VGfJOd-pln7Xo z7HtNsS1E{2G7ip6k{Oskjqqx(VBBFtQ%~TS*_CMTot9xn->j6}a4i){dQ!#(#f&!b z-9tlqKzxQuFbIc<(IcbClZX{!vKHv65#u!g6Z#iN2ER^BkJp&t(hAnYlSlW0ir_a< z0y19$Dx(1;xVxU-6gmxuWfQ!#L^xqIC65ji{?$U*BOu)_y0r3@nvhzw>+vMq2|)7h zFh3Q7FjirXU!GUSK`1g4Cq-&`)lFc*>wYoAqF$T$`9d2P=)VMN0SE?$F{{{^W>kNG zc3lEaHN{6aW>rB($Djdb3T`462l8C?Q5NheeX5`c4*(q_Cya(hf*FO6IAGXka~;R^ z874qZ$fbEo{~$^>rne5-C>ln~i2PpL7n;>A-S`tZMJV%I$oCCwJh*t}SD|;KL*=Zt z*owUzP2r;QLWEDtenN*aUjQi$F(vO@HM<~gdISqs^JE6t6r{=PL{@87^Iu;+cG9Br zn#r}f;P7b)Ax2Cx6M6f;SGQBp!Y?elwyeF>K?ZkQml_Vw-hnx60^AsExY5BSZlcJi z^rMx3yRsCj0?>Yn;<^1Rzqq(fT7%JhIaekW6jPVIj(bDNK)$72t%fe(op^*l)Z#OJ;iAgmgfKlo*c+>($+8>j-Y@i?@+6@#TgOOgnKp=cWjBcio zF%KyMAXlvDHlqqM)KFnIWg=mET2)&0QQf^=c;5c1h6L;!@#k8QAVt(b&}-pc#?u48 zb7RAjB&<|mji9|}Xb2@4qwu>+&)@n%fVKmjLiE#V>Z^}Ai08;kqvRwC%w8AIY~ys> z|Cyu};-9)aG*~fHW}KE@#nMeaX40FNzOue$mX^&!vL$4#w&knWZ-V{EKj>+A75C@G zvyhZkXF>7<_q$sq&uhlPWE7$H_c1|0+WUznNz&2H@NLIdcId^zI^S(AZ(taF)s zA*Y=k$X@R^Xj=6DZvJcY+i*WGI}ux!4(ejxh61ovLBhIabginl2Yt~ap6xUY9E%Wv zq=pPR#14+CK*})_Czp*a39;X_RkPulczSrQ!T?75;vg&bTM<8IZ+$e_#PY1keW%&s z*q4X5x!u9Fi;vPDQk!jZ6Ar#sgtFcuR8dS{Z|K{|a9_4`!_3M|%>LU0KI_>lljUc^ zFGs6&{ho&{f&(i?B25<$JKdC6qw9N9W_O6jvDE0?Fp7SDJ;JiaAEFWe$bIM6DiqRv z{UAF!ZMcW;&u5m;zFW25p#M`R=Y0LF#taPLJYo1Tx7E*HI?v(HY6-C0qWJP$1i zNt7p^)WN|8;)XQK!w26yprON2RG(}oRCO|xZtr+z&R4WFV|4KEjm9xib44{lRnZ`a zFBpWqH2g1q08PsDQ!ONs%h^s#tCxfOx#r3G&&Y(yuW-oMp&UiRtbmce6J1Z8Q*1;K zni`2&vU|U@O)Ra%%2VJVV!c4OaVJ$qlyVo;!0nB`9x0;NR1rww1ngR?tyb;nabiR!ob(gU#F-|C?7~= zDo$=iIz7D%=|J~2xx=2QqQyl3r#v|XoicCp_&oZqL7|SlzFGurGA=STJ)|k6wy)8f zC+E+hH=SeW(~5!L``Q>v`g${Vw73~Miwh@^<5SaNS_QGfoX-9>=kiMZZ3E}vNr}6& ze;&+Bq7j4*QVf-2gwYFsbzfI>qkpb`Y53nHtDo(Hz^vqR+OR@gr%r>4P#6aY_ZBwY~W-b~xS zcryp`JbO0e{13(wSDlhf@l1U6|~sAy&9iSsV2;UP!z(}J?rwE+P=ZlKLeU&fy^#uxX07Jb*@!#!K-wToQz>FKH%u6msJ0$LcEI%rCAgT|u z`eg1=!?A}bN=8=1-5CzZqf7wromG$k2Pf9$1YQ98te^eXfK=!OLTs=R24KfU^hqHv z+Q0!OEZHMT^la0jMjNT&(&yY}csI_&J=gIaMXiPWFNyrr*7kn5s@rJat=mXQQe)`B zn!HnD(dSO2DJ9xxn1+)c6wx1-iN(`2AZOqV3rk z#uD+V&-Xcj2&Yyz9vgJ%Px<<5*Mu9y1cjZ^pvg?DGk_H`;5M%p0)!!R{J;{1jX)Dz zaa@OB(Br^%1U&*g&a%sR0u&o=rNJ4N(bp48{@t8!rC)Wmqqo{vvGy}0hf;t?qOj(_ zq=|5;G(8P2+xIQ{$%a*y*W_N<%i+J)g{&E?nmRSh}R< zS8Pu&PtC3{?RuLGcKG?Wr;uVoOVOO8cKYeb$%7YAjsIo1#>24J1(HgbRV;GdWi+(M zg5x}pJ_Y=3>=s6k{qifK!{SsmnlnUEI}%pSP4eCgkyBCd(cxLwW4BEDKVClvdQl5s z?$m@YW!7Sh`ZKEP23@3Y=2c%fW?L5*ry|^v80=H75J_vGQhs1h=vOQQpPy>W2%73< zsH{EZoCI3u&KZlJneYl@4$l}Ry)BWpl#S|QmRBZ1u7j~@>C1$GFCW8I$!;sgaQQ8x zCPG1z8x}cjt~3L=^exW@|3_lYx1`UNPbYbDbPe1IwQj19ZBlN?=E`+IV#0VZ=R-%Fr3v!f%1 zgo47<()!wxf6bO<0=9}aTvp$2t}MNnr7O>3mGtZQ!AYoCJcBEVS&ip8_m1A(;WGE@ zc|1;6ieG9gWEK2#({~|?8PRRA51D)VTX{}3Qrd=iwz_Pj*GVyfa@6xY^-QWPt3Gq2 zw8m*hT6WoS<->WcrLd}>IEop{B~+!6KaZ*A{HcRcE#N4wR1+!RI+Qn#wSB))evlLg zm(cBDnUgSny|*FqwQB$OJFT^nw8Hz`>Oq@`h{~nc7vT8`bQ(sSwWN`e0E&tzc zV_j3nQyYheELEG%0f4&Bb2(5IPE_W)b-vH8sl!qIG+X?+T-Zb!Kprrb2hP5Ju0Mx4E=>SCp+2`}uvr!akOB^PmRc`%VV^HY2Vhy;E;Y2>maU`G z(X6Ui>aCSxfdU5c0@$^e>BkUFWopt5IL9O6;Sfg{OyB(zcCxHWGVAmPa@!_nw4x>uRhP`FaBQ6>HEaZe{Q7fC zski4?`I6JwCPE-j&AtSb6 zNyy7t%r2LTSSb^J^kHtrlO@RV?L6{*^}drSWO}Rt#ks+awRkEUQC2=!n{l4YNUk0e z(hkRJ+_BpM%)GLc!vnIi^7YVm7r`2sJs3Q^t(UJwp&vgMOA&^|b!JGwt%SCQrs)9P zagi$pAgvZeu(f7$Wkxa&CxrnWli2)nfSI&m{DK7mB#+j>Z`Lm=Ub4JA-+3n{ed&HQ za5U!CmcMSjp9xusA`(~;D(QUvg%LWJkdW`im7KUgtoi=?C7fZ3WV#gg-Dph%~USx1?BwZX zM43SfOqdd5lN6oqPsWUOJOSbuvoWhVhR!di7i~YtNn9>@XxMh-x@GThX=dHJEp#NmLk=Dhcyzbv}`;mp8louPFS-l*ns}Rk+>oU3ihSZ8g z_R~~%+Q-Z44p&r&7(&Awpk!gc==4%LXd&7BM*sz#@onw>_y_;?w%XD232bnp4vBVs^z zpa;fuFb_q%!9&83gx7(5@aWNWk&dr88JJY-J2?O`#uErdu8aS&FRSErg-1^rNIm&U zCxf5Msb$0%HEV*DYc|&t4kiTNBG$PN28+dQA@G%JX+pp%30D6#Qxkm36I5`6BTa|O z@-DzmP1L!1VRmPY0Y=8h0WAOy9x@J&9y|3fhLuWuJUue#)4zgxLEL}M7tc%@=zzc< z43BTI`Csw13ChI|H*OP{wcQz;K^s=){$9$}91>dN$Ti)sSvvb(wPQl$ul%BStV*;% z8}~2U1|7B1VQDO*z4GygYoj%JlG-guuX%>d`{A9^yrE2I$EQbPZ*Ez&A67(mzR}NF zj5U5<+vRiJV7O99I6{^RynUF}R}5J&>gI{flZTd&1X^P__+H7EPt0au_*w1c&NOq7 zkey9Y$K) zLrQS3EiKXUh!S^?7%QlFvP2?q;zvutbgSmijOa|1d2QC&o^jNq_iK4t394ic4wa%B zltsJFu86^G>3lsmb<`SE>_eOMzMBhpu&sDXG?H?m#bzz>mNJq{ z^7KnIRENvB!+&DZ2qqzX!HMg*9=ECE5$zYGdqkhmodS^jEOGi(&v@b!83_;Rir_gn zho+=cA*OIwFtCq&`-RJsj8w0^6F1a#C%e9cI0s$$;`V;-uzgCPaBqC>jj;b7pBLbv z_J3^sNUt^)1MK&;0OjlFdYb+i3&(pqwqUD%7Yn3lppvGxe?-8y%n3%*Ii_`$MAovjOAyTdWhnfBExP|3R z#u*;wpVe|1i!v%)Z-}C)jdZxblX!`u%UwqB*%$h*!~bVI-KE44kyH>}-Oe2~;=#S= zG$6c7*+zAD;kj|ZWj>`qxIZUu2Tdlz(!L{~grz4CwhWpzqJV@OBj1-ck+%P`%oD?_ZzP ztlCP0wu!qESRf^0#MX5%KFL#PdR@Bj2HH>F5@^K+iqK|B&T^xyIAza^1te^)GjP4p z(!I^f(0nC2Vq57c_V*Vv#nJuC2%|nex5eh~0!aw6MrvzOC7o}?00wZAQ}^1t)g8q$ zQOC}V#+C1lnTHSnI)&nqX2kb|#US?=!&h&S@*nd3!iECJF1Y1#x@tSfDk-y(xXXd? zzQ6Q6dAj6Es2>G}UF@PtC{56J_G51-^j}|1H%h(!U zm!s2L)@mndF4CrHBd*8KbCKni!>7J=W3r2l7xbw3ifp95ij*SvV`d)lXM}DHG4o6^nTGPVWC z)6_hKj#Kc13{wmi>i`PtG%ojg@%E)KAf>Z)=D*$v6c&$H_*DONmO~WaEmI*`1NU8M zcc51^$KBZ64*W;z2j$qDwg@t8U*%tTTM-155FCzb2n4Jc8T~e~kqEi9oWbVrX_QK< z_U#lmR$^?*ez*q2jtvIpK*HCtm9j*~{yIZ1X8jQ{%+t2gT5yfS}c`F5uC-B+&N#QLUImbb8JJm$?# zZ{kUhBDQ4zXWE$=u9uwCFr}jy7N7((G1vRGYK+n2ji`KJFZEXA#0s>JLM-CedaYo! zQx4ZZ5=tW8o%^qTOkr|At>2^k=&&o(EK_VV@m%xq1kQ_n$e>uo5-Ao{_^Mb+gO%sI z=6poF%41$*L!WURx_74J+bDCIu{*=>^iKP9XMEtlsTuJbQpDnxCB2 z{YWZ+#-2M*;dmSzjWSAACTWIM%&sU4p{b8gyZ_q`7Zc8@6)S&hOX7L&Gm}2*)>89x zXcy0CRffU$juem6v93Rx5%?v;7>h`{^bvu* zAEnXQMZp-4iw3(_gMz7{Q!9p<6AB|t>`;fXd<%kZf2uvLX|+o{y{c~)_a~Ps zL0*hgS-v=Q|0)7jdPI*4$J4LN3vwaSNLx^67Y8&hPtbs6BFf!X31%~zadA2CP6RDH zm3Om%Zh*%@j>A1*Bbc@MxJ8i8v=+{^whFK3XD64Q5>>ZI$zq{~AVPt~gIxG9Q2C97 z*y`UaTgtpU@(4hD7yX1ad0PK z(@7(bgwS&|pZyw+Cp9&_P>!NsvA6D-RH*j>9*;}&r{`a8+C6Xj#dgTnEDwZ?QTZ}w zpx%;re0J1zc3M<*lR9J?9M$o|q0hx45QCkgp0m;DJkk1O^%pW2UM80RQ0+Gsk1?qz zY1tpZ3H)xK9o@5x1Nm21$q)Tei?}l8k8r8NLoV`PPslXcVPdHE7+qAx>O*M-vj$OnUPmJ)KuyKt{RAFw7TdeST@n_9`VrN7 zPRv;@5SwQ!y|b_^Kn9QS7G7SFnc?s{5Cs_JfkhJ%1nwth`(-P+sncA!ow*)3hNZIO z1H!**^EOYyMBDDGNM~9F&c_NSc5ZRn0KZzvO zlSo<-Pm5K?HSe-)xHSSNI50rH=Ss{KklWC)Z5xX?SY=qMCC&I!j&k3M$cn$UKhl@~ z-A?o&xq500B)WgmM_3p0q($WZm_@tLkn%eU=$orb`{$RJZRwY-Ieg}q2NQj=iS~=i zCWF*fD0!XP-P;F;Y2TM73&9;n{BJb91}k;OQCXQ$Gnbe4PD#gy@KyHE#Pk3S)I3XG zZP)+S%d|i)(Aaj|+R2Af(U@kJ7%OZB`Wk7C3&3NHEMCQnzOH40gX8(!&fQ+l~Dr^hqwV|QwN)`l;6LQ;N@X1FmM;HWf|>lo97J# zeSt-=c=4jY8=1?j+><`Zr`E2=IwNuX+l!ppq0awwR}Z57TIJR$`qF_8h{oK^(#N&A zv+QJMzIOgfpdp9R^Z8op;CgIIr2Ec88V@iIkSku=WYS0JB(I8T^Kku?{Wq4rn^Mqt z0bh6qj3}4y22uFi~Wni?_OiE2UDUi)mLP zkLND{cvhnt@Mj_Y_gSQUqOp<@c+Q4gXxa_VNOPSRlt+Kkh#QEOw0P{@_vwh%JP^Rp zvM+ib4u|k~IpB?osL6Q*Rw96ePNH=^t@?|kAlILavu4h{?%5|EU!Uu7pJEe3ycK;RjC4^NtX~g7+3Q%t)eas z$7}!bNDKb-ix+m0)7N-CbKK5YfVbr;L-38MlL-ELzo%zFa$E5elH*kK;WR&pKvp*H z`c{4L^^DM&{p!^O(A@EYSBXAdcLbWNtI#Ec+e!(hSe z*KppZ0^_>t1BsF_=21sUPQ5%zX*Gd$kdtefMddp|0)j4HEv;XH;oR}Q<`|A8-fhzZ z9N67qX>zfA5qy15}`aY9R9?~@yhS-V5SAalWjA;R3=oU7+-xpR(LF9y7vexF=i z8Hwlt)biF9i0S(l+hnMUB#@f^C0^0`>}J$n%u0q%YzJHuy1Kb;JF9^!m|<(5OroKs zq502*Q!+bl!BHvV`4=};$?lr=_>25NR@XtNrtw!HV;n$NDR3zurh5~SR09U;E#h$*BcN3NvKY(92b&e6|Sr1 zQ<5~myHZHRc88@*vlz}H1r-&71x)C5_5pO@J@4a>O2LR6*6C{GR=d~aH`P9wt?s%? zbTO2{>3_&sHePW4T^*1F0;|kUbYKT^1K`je+PmAx=QInW|F8KOU_V@l_^$}MvKJ@`V1YG5MCVON+l|^V_!ra zZ1EecR{#%pcPqAauqj1^8{0eC^I=VL^e$~C9b{{tcgYYr8jk&nG_pZs>Z8EJJDTI| z@$$Dt2}a#86Tin=kNpsT-KP$}broIzs}t)4V*ZFI>8>w#QdM#E4~z zGXH!%g2(jTFZp1WWj`6s{%#Qe<+`IYuc$Q?yL$*~Ky1@r-VU6#d9wk=bBQ41uBb2@ z&u+D4>4>t@&d&|9``fQk!S$!NPMPSX0Eh(Aaf!uCOj=%7qVoFsGB7Fx;EYQ*qxOLo zS!}vm3a36_iAKhXh(Ye!rKjEs9gnS2D$mYofY)qQps<=Bk_AEkM4*a_=?VME5=P2F z1fLg5$=tiX;|9aE=6T4mUwZLG$;4*NQ}XYu`O(m>a4DBt1J%1}hP zV@P0EE*?(U!_R6dJbs{EBlIMKz_>xQ$@Rq_BzSC|pn4R#8n@|%@e*#m=+1XX;sjM0 z-!27n4H2TV_YD^zYPQN4ot1f|uYn--HGf^gm8g;Cd&ZHrXV*ZN27tuj(zmaarWckP zAOf7gB~{Pxo!L1q5vfZJVyU^Kx4N}XUb%6daHH$Y0c2hnSy_|AX8|8OK}450S}TH_ zGK=2H)g(^=pVxL4J`R|k_&oF^jOb0e$uQ}NfY79()ygDaYI@vC!6yPO0;>GkluqKBd z#j4)&Vb+oSsXHqDa$#Lhv1gnMwfZ#!n_G($`PO}-fTa>mW76wNVPvVvC|vOjUNQx0H3y=hX3poy+BZ`TN$T4lCq3pj_5=7@TS*NR6ntPo zoEd_GdZy$d$CwQN8m15?rXoU>i;;_lhmqY#Vm_OryW#!jLA2^Y6xwK#b0bHGI3o^B z^sRZqc}iNt<3!y{7CV7N@o z_>5jZhvz3yMq!@?QPL=TVYmR&8e)BtWK^tp5mjXFkM7`T6SIrKPoF;>eLi+zEP~27 z&0rA2Wk0VV%npM)^>gT6F(F(gBye!wo@Y>Uga@z%MfGR1s>!X)*ollf?iE!iZaUXI zo?K0VgC@`eVq5;Px`C}l13wzN53ce&ml8&qDh9e@I{`^L*|=)BLCtc%OEc}{X3p|OMgB-x-lqy z2unJPLvt*=xZM5lg%#fJCgZeTyDjwy3qt{9K%29x51ryzc}LB?$n`gBF@+~k(!r{> z*GYlORoefxEH}!N{3O&R8{bG^56&GX8pr=F6QjO+;3Fms_PIYk>Mv5>6Ic^E-{T*z zmeJ96U!FGra+N?NpsAwDx!L1}ig)Jl<6&(S-zmQOBBGi>g#y>{vGV+fUs$*Ka(3%D zKT^Nr@(NH;fJ=GwGTw`wnEm-OgZC;vn~IfBDmY%{;M z(2K2x9W56v&ZVzCJ@|G`i=oa;43sU}O+RxQ0Q$JLP@2oG+%^>wWv^ah=L%jIw?&6&}eoy zq1C%5Hk7bF@Gkl}n4tFij9vEmZvwcf{q5D4VZX`6rT+h9Gu1Di5CwCDjr5iD7TAm2 zoc2rrM58DH;_tz5?d#BIbS{RO4f&!3$;053fuFBEBT-zSYAUIaqxe zJ!g^>al`{;V&Ua8uFu{0c|b@2uBV} zj*I&uUokVU*f-oKao|#t*5Y28kjG(od&wYUJg&_M+u3uYT*@zb?;<6ao=JrF2Ou(n(-oZn@ixt5_O5qcS4_`^_?5ow- zhZBw5D!w4lu~8q`!LpBe;90=hSGNmQ61;ouSg@FQ`P>?H1dC`O*3HjO! z1YLZ&kX0chg0MbM{Q)+`Yvv3Vmd;Fdl5EB7@g?7dt_2{Da z3uHj<<5pM28L}t`QGQ(jmC23odKmcv^}=uygZszAq_oE60)Jc!R(Vd^*pidU!rfjd zg9Zh%x3dCcx$t64Lj$n!J;1$AiHkowI+9xxJjEk=L4kq$uSt>M$JnfALBXbBEHb@s zkd=8{OFzIop>oAS8=d~h>~!1gug5(Xcwi?e{ori?Re+vD%-!TToXF&4vU1tb?0peR zxTSO3BgC}Hp1{q`!t>brX;|{1KMV6`dya+L+?G436aO=JfoXl+x>; zEuhYfBEUnF1f;b5G@k(g`f^{Tf%Vqcr}F!e?q8(?wSaE*L_Pk!Veih@3D27^=9vRi zYG9xFe})pD+b3{;Ocrns)lv7o%kyU@62m!WP7b95RKIqDZ5mXwO^n5#(2aW0`11@? ze(|&iFbdQ3U;vwHBVOs{?*q-28)o5sU!0{kVj$-{T+%h|+M$ZXAwrIsIC+8CI@e;hzuy6J+(6*ldw6otMe29Yy|mOS zuIQ3W$W`A%jYe4XcU`X?U03&vK|hzA+1j1z?9mK{kAF3Kjq50ba$4xz)I=D*<7wbK zU|>wHqjV|Bz#8J+0%VOQv z#3rgX-fC4xXdOpuwI>!AXP3eoeQ&8!j|nvXQ!}5!PA?Y*{DM)xq@|9xoK8M_PvIH6 zRL^!GI(F&biqmGGSHc^Z88`@lEm@+tTkPjK}yf-m+YCICQf;d1oY@R5mN3uZtyUu zql2pZu=DS1Gr8S~viKf9>-mubdr?iyU4yMLDR*pvdTd}?TDHs2fUwAfIf3XZZUaXK zs1zhg6ztl#@G_6DPHsI%ey=1Y)BP6dQBS0yqGAgt%ash3{ohgfqAOw)a9BSk%dLt! zN8X<{bVQsYOPv4)j|3F3_`?32(o)<-Ng2&SF*Tq&eD}Ojnf$`fAxgmJHo>hN&0hfa zqoRSc&EtJqNzIYi2_SY;5OSrdBcq~%)%psI?sU$e(yW@+fz5c%ze?z=S6&~d;k)O%zayQ}#{dYQT z2L2 zCFKicR_5mPva@+{d)=MmLlmt*72KGGdiFej{*%unVTh35;IG1Vdzu&L+mBH~&i`MP z03)dwi_q%J6Zv>Z2ZwaUNx&(ub5nN*|91hNB`ge~do9E4uW-3oA;4d_NCUn7c(@!Y z9A`OzPYl@X^m!=?eAr>Ka}4@e$CDdJ=I8yH8PH8H!(Rc+e%wt*Qb|<`>t`ZqI`oBG zWfB0M!(dGT89*s=F%d$8mV!n91`pB8FT=!>8QMtdL(MrpC1 zthGM8X9>OwUEqGZRx~j;(;e{N4Ha6-g#(*-h#~?Z9Ze41=6gDJmD$?t@Uo!Tv2X?@-MKKpTWGWy zV?iwz*jq&ViwJiKNa=AB44@?CHngM!%QM=|MR+ustwLU0T%G8-X|Hp`NV%KrZoS+5 zQQoTO@Dump-XEs=nBA275l3HLJHV5^$c4xiwtL1EoZCJgi9B$= zHYhZ;qY~d}yLV&j+R(6lt5@0cxMGOI`LW(!d3|ayZe4J@3#%hD7Z7vKIv#?QMsyOQ zdY&UDW){YPdt^9V$Dm&BW+l;y@GWoBCst`1+-mdci7Q`^G}C!g#=Ad;&#|mZ)3*cM z375*Et(N;D*X&3H4)PhRtE0)Vk1GLZKCh`N5n4e(98lLfdamgE;@1XAqD!(j`qRQN zTS-(gUF~D ztgW~}6Z&?MK?sx*c{v4DG_;{NbhcSzSf2bUn8{pxqrFSN*TNua1iH>lQ{)P(Y9nq)QM4L?nhrxF#cYq2r$6_kQU_hOn2HKaCNjvxqEsn6;W1N zdfU(Vl)sdaysWQFoLlsb|4j(zcQlbPk-EBifpC7+MP84@L$`yJrl6U|un3)LZ%qG7 zp_Fdn*?_-zzcm?)iWmbwTU! z>eS>Rn$CN7;m1Q%X(s~Mt4ZEQP3p&faCGeJ9zqJ*A*`^|}Jdyng8&rQz(u-1OkHGnLAT-3+o{bOcot>i`KoC>l;abs_?v5oGqy3&D z`GWZPl{Tk$X&ief>q5J{4kO>AMGM*TIzno%Xp@g#ghdi(PuDqwe26h%?B0dpHSHCd z1b;|x#Wiwt;<@uNS3aCuysITh*VC=Xy)-wQ_(L_z0!~;GkBym+wqX|G#>K?>%vS4V z@&)%d5ux31FsYFJzx!^j^eF~!mPW#8U0f@)cWmHVxYNEq5b@I|q*GHgamvekx*Hw zVb`0EKjPF~w0Lfg#+e`QU|}OM};|b9kYxnbA#g@UJA!4f~{oTU?7@p2>ABJ^fkhDNtCm^N39fTXmT0 z572VXv1;^!fp;Ps1DBD7`_e#V%GmtbJN%BB9ZR>|ooc;__2(ynmiU5h+4r3r8}Q`` zlNKl>v9B+0S`U=WPjlR*qh_u5)d#ds*PQDfY4}cbc}{14kJ}j2N5-nep^!eZBU$eT zH+SSU3THzxC0$^y>4jc|UDg3ul!BB`PO+CYWxfaLd-7V!?bvwe-w`*NuJ_u*!tcA3RC?^NVkL^|1YMqcaUYx|b|3_e!@x;!aw7th`k0!*?rm7I2lIiLv@XVz)ALC)2O0FY8nSoC1*Qmo8RRD-P0=TKlm6|>}&HA z8}A3nzA&MAxf|5C6Hd4-$3bTWIS|F6L z`3C8it4|oKRiEE#tP2f_$MI#ypo=e7RCKZW@by^rUNR*%65<3FrffzxPpj(d>ql$0 z2pWzAN#%*|?Uj5Ba65Eq-EaHka-3VE5Esjq`&c0du6f-SY=WG=?07hOICagJ=L%%B zTx56?sXDjpI@?4(o7E#1djUF>tjE#GK>E?21k&sxYwj%O5Y_kJ&m3&bF0=<`=@*^H z(w!@oAiVk#w9S4g$G5^O!q42U&_@UM)lPbMkWC&=jA6@V61AthPhm|=OoU?B;_Xg- z=_qktp>#*MwlR9m(=Ud`eNk_JS4UUw*zYv(fT*UT9ety9w3*DJu&#+3}WBQMP7{Ivsd957vk`3Eb^ z57l3dmhj{1b+_)_ff zcJIoUy?3%*z*a^JHfme-?nW@Q_ zgK>QklraVOaU}%=H&A&j;t^Y0+H)#uEQC8;O>;hhf*$!~Q&ZkWS_JeG+?a*mKfIbe z=5mj|&Py>?*p4Tn7<$$~GH~sC)#X&!&dda;%>eU#g<*w7PEGm1IYgtWe-7_Lusf(T zBklt_bJycOjQlb`nr2~T`+C3y?^opbwT``J~e3o|vD@uk~X)Iv3&D z!XJJ3|ja_1%TBg&lMG3 z5F(h+`|u*4@W1nqlD)4cK=QC2i_<9a{i&}A1&9zlu7_ScG2g&c>cG9WMLWeNto5!; zNLx@>Bj9V<5ZmKy&(33!n{ocT*=FBa{-s|ky@Ka0X9>BYB1|5zKST#^a2#= z8x{Y^6x&PU`a03s<;THADHM}pi(nI1kh4GJ47+ZYVmAC)AU!CmPJDmf%Whgpajx%V zPj}otRb#oA^k{y@@cxslw!Q=jLbf+xjR>4fmC!PKv)>c~=$n!~KGH*ZwrYJ;VO>4A zwKOVdnCD?`@!_e8H~K&)kvo5}OBzG42feGVAF7vn+4ah;7Tap>JMFG|g}h1NN1|~= z9vDhE#+wpy^=uuj4V)icq$g$Pz4&Xzh+(Ct>Uz}@I@6+*BZcw)O$_t|%g-1maJVMa zFD*l(M5EJc>#C_{e2Pg$8pK0-4<0AeV47OX4A0Fm%AfQ4~pfnD(0l*eKu1 z%K*x9oHIjeHJEsHeL|3*oO%&nuf}CFl^x5#sh9X( zR$1O+^9UN+x@?9E38h0h+)(q{n%dmfoLg-W-JEhG2u37JDQk&va9wGZh1;RK9cQvR zAcIZ`qWhMqsUB9);IX+U4hIkH3u1IY&=GD62Ifx*K^@l2mL6OQG@W<@U9157#`0r@ z_Go?AkdRx8&lvR@HZ#-Q*u=G7r+e+WKy?Y-BFNLKbla2^u0)h-o>c0IkLPt(^~jFJ zoYpz|S(ZnR1{~Fjlyh6Rumf|u*Ik*)?sgC^FLXBWLbDxgSeP--Vv19Frm1wFHKWa9 zK6>uH2oKd9@ikAd`$s7o<6RXgh4;7EFVb;0zxyeb_~Vh3wvg`H6{|2kmufxZ@s#3| zi;R@|v3e^b3>2*$)#boEbLQRkxj4dch3yI10^;N`j$rsA~Oay*aHiU5mJ1P?JI z{@U#P{ViAp0((J;#YFG(wB7^B0hyOp3_-FtEcB}Bfwng$AX{HumGqFs&gGzbZaC(j z`(6&CGxDp+mr!UZ%yEH234=752q{+5S#V5*A&}?|nfNZzzMNSUk@vYMDED<+LPGuM z2-!Tth^z`=n(QR!qk_d#pW2TQLCmh`)^)^32+%Gw%z zi7+>3#E%LwiioF%XeCIcY1mp>W#i-F4aOKW`qm@S>qDHnFhcD^US8gz;bPd7um=(; z+#N#=ffbgP+^wdZ?(gp(1fO0^Ncs5G3o|nZEdA_xT=n+t4fF|mW2jP7L!*iMF>JD$ z2kHi&>WQJypK7XcTT+S&SvqyQ@p8VB%>ja?Sn!Opk@ zIE<2#(%wtM)byA9yGa}m!x}f#xXBYox2vzIrI#7rsvbDHQRjCo9oT?tXRvD5g=MmY zzApk@9tB1gX_>zm^gZ6Vmyd2l}nL zyqwR<#wPxJ2ZYYW0JT_6@LytC9+Ug~?rdau1-A=(gf61lwT@&zs9ITB#U{E>jfQKO zCd~lyL@S|&T~A^20FOV#eJw1$4!gb!p&_pIG=9m8AXx`!P(qdiLfCuw_=w3bxRoWS zrfI}#KLj*-pxR;GWmKMMiy?|)%?4Dm?|9McB zSZ}8WAhnZPKB+bpFs0@B3HsxODwyO@7Uk0wxJum9oBI0tXC(al;Vx?G>hehBx9P@o zArb%WTXxF%rj2xYx<%w@1jKRFpWk6Pu#Thx3!~BjwZD?yueKKJ){va{>?P?&iMqmdz0=N zo^Pq?li={uj%Cd;w?Mj_NMayW1xS^t!x=KXK;+an51Vj8x-cBX(vQ#N#@{)Uayvez z=5XihTgpCyi#HCYvS&_@{k8cuw2lEkrd4!j$o=`SiE%+g!?gX*%?U4&77XW;I%$r$ zS03CPrfME}!5tHgUu1Kn54e8}?qe@dUAg)PkjqB>j&(P}1$9HU=Uavb8|S)W3EwjX zANjH?P*D2=y7yB?ISmt~$%jMI4%4DpD3m^b`I-o%K>KjBf_APwwW}vkK7pt`L^nr_ zB6B+tW%Lqoq+>s|c~9~uAR-ASot$yZnxIMwheM#0IYqs;rgTibIFozKNix9NC!~3I z&UnUjwIRtWSCT<@@&qxAAb!&5<#to^OByEh?<9V&mnRXn_`|o>07sD@3XhfI8m^zh zmnZc`*ZGOy;=x>|%#`^f>YF7EL^`)hZAPOFwxuZrw(9>G+&h6Bijn5(VkM_y)jMib zoA~|(NaWAB>-NRi0fVV_y{G<_ROOc2N@52GkK@xv4%2d1d}gwQ=2X{38l)*_Y0~~1 z%$;5{(d!?^p$SN$&g92+F>le$-eFLfdL@eaO>pU#ABi=5{2QsL$frp>(0HvGr*pN; ziWv@?&1p$x>_p~rW{kiI{bn69^JdMV@Ybr=Iq;m@^5TZE$rq2W^G;ayMU&8Qo}X=u z6w&`}TI)pn6i>UBFEj6_5w_b`7`P|Fqdngyz@49}F)Ib>dA;$JF98ZB-?*0i4Fl8( zsG;Ucr-=vj+>DHAeow6o&(6h_P$Ui`g>Ca~p{Eji;Ae^FmO zGjo1f!-yhBa}0j^RH<(|+*(#Mfo)0ZS~U27I{RMhOWD890?-7=~0*ciUE&MYlo zuw!RuC$Lr^kR|#(Ic2$S1G-+X4bMCw*?UEs0|+C(0+5;{4ppN4ZP)wjF0V#Q;vo*I zu8aj;l9F?{^|K~c(b&2pk%7%a<9z`gPoZh!^zs0ckiE|rgJn&eme@TX1NeBkxF)Mn z)iR%R#BhX*W#3|Z>e{~KKw^-Fy(P!86g&DlJ6hzU!Eokdam|3tnt^16`dbOSuH^+Y z9!9`2sOD@gWq&KtEjbySlH|@*0OXm)tSHwiS}DN!v&9>pWgQ+g>Ih`9!iDE=K86 ze#HPVZ`sdANi_kl7FSwuMqm@R6ZVD084~ z^#B2shY<2i=))%vs8Y^KJCbiK;x1T}_@wdZ^G7OspmBf1f7OqG&ue}U1R?s)2;{CjURd@s_@dPlEjN?O{UitS(&W(RFJZRi_A z{01~Hdx`ZyDy<2)QW&2lD}To#z#3z`Qm8fkE;J(YZ2%2JcAy_yOR7xha8%d#CgH!# zhjO*=-e}IRNaIJ}=WEUh%jj^~J@; zh5&@d(F$&*$vZ#6*dt*)JiJX-BNE_&$Jwz>7rAB9VKe0jIE}yk{#^#BtKocjs}5<$ zL}*wT0hrt80A&z9c2SNlOxkMt;RLV>eO*cRA_V&E!`V)v5VR1mC!nQq3uz&yas87P#{RPG}%cALh46ELl~x;YgEg<>TI z`mkno5S%RXm6n!12oC{#TXwAJs=2~cUX^MWjvp#iU%o8*p1-PB@DuRj6g^-AnW{Pk za35tur5wmXxpxZxx))HL`iy1Hb{ZvH@AGb|Rx*PyU0QAZ=D@IWCp+1DNkjQjlgD)@ z4cPEEKf$Pus9wiG1T>Lkz#Gh`;`^+`0?_b){l?h9Y3OkFj%GlmW)4lM;wG(Kj%+2f zRV3)~82W(v-Fbja%>pU{v4foiH4k(8a_3 zJwn**I~Cg4-q3;w@#ei`Jaj}6+j7{M$ zX>zkEq55Zc7|;mdfV0qh-O=nT_Y@LYSHTCEh!9MQ8-x!`rm~~!wXd-1nd1y5(B2u} zJy&4bXyUfO>RG;KR=S*iiBV4t+2ac&ff0B4g8#~OCnCtUmg=qKkwb0qMQ5=CCh?@x z88==?d%VF&*+=8MyhKS2p#}VO%r;h53JrbaB-^NY$-`IfnkU>IB5j$?(Eb^ z0MS4CBl_2!- zu(1J$`Npa+6YO6x0&eA4%CC59Czz0-=3$T63J|2k+=e2$pN<7_u9J2DuU z?>zC%VegcOf(#f0tp<=j{)&D5dF*sUHQD7zTKo`U zWHeU@_zRGlSV%j!JPAS@Qh)sPzL7#>2S|$TpZPY=8oa0)Ylq&QQv0D?#-w2&D9xF9 zQe#@iWmU)e_`rDE4?W5%*a3=vGBr94c>Yp@j#$#iQ$S=CVOe7leH!4Pe?hxnPdvZ$ z2_=XRwE|V(j|t)_%7H;4T9a)V*Ge8`Mrpod*UB92$<^|isjXM(hlQS)=;=ZCifaJ) zE^9W=M<^gNcaVm5uqJDu_x+7)CvoazVB5D`m1j0e+uiSL(z9SZBi6_kK+b}{Ox^|t z<5Ax90OvY>H|!%Ld(Gt|(?FaD-D`F}croLI)~nj7f0o01j%rYcZZ~D7L6tXa09znH zK)EUqyYpP1N$4`1$kb`_|3U#}pug8-N648;dTMqL&I^aDSI69ZU zxY72>sKT8W%PKjUhPs?~5368P_CpUvBl;p-;M=OJ$_X->`vnVm8f;;OYoN*1sp$zf zCnrtxH45NGw}Ui@?B#id!^k>qY*cb`1P~zbOcD(L;)*+tm)`~o(q^ML)aQ4D^W9d! zSYa2&J^b4s=6#ZZeS=2Zw0`x}S<6A#d9*mqPvb|XYC^tvX9wkmmuZ5uOhc)9qEEv?q{Z1jwJbE{K9uos7)l zGho<33JtRm3|{}^!~epy%>=AxJr)d1;N@Tmiu(OKEBBw7qWf1a@;@de`F}Luf0*Zg zrg{JS+POrn2Ll#(F?7x#qe7Xmuzugbdd7F{>U~!g3rmLdpZEXw+%vCOTYPa UG?h<%3iA|2Sv8qLDYG~K1<+|ovj6}9 literal 0 HcmV?d00001 diff --git a/include/openspace/engine/openspaceengine.h b/include/openspace/engine/openspaceengine.h index 28bb568b37..9840fb96d9 100644 --- a/include/openspace/engine/openspaceengine.h +++ b/include/openspace/engine/openspaceengine.h @@ -45,6 +45,7 @@ namespace openspace { class ConfigurationManager; class DownloadManager; class GUI; +class LoadingScreen; class LuaConsole; class ModuleEngine; class NetworkEngine; @@ -101,6 +102,8 @@ public: void writeDocumentation(); void toggleShutdownMode(); + + void postLoadingScreenMessage(std::string message); void runPostInitializationScripts(const std::string& sceneDescription); @@ -200,6 +203,8 @@ private: // Others std::unique_ptr _globalPropertyNamespace; + std::unique_ptr _loadingScreen; + struct { properties::StringProperty versionString; properties::StringProperty sourceControlInformation; diff --git a/include/openspace/rendering/loadingscreen.h b/include/openspace/rendering/loadingscreen.h index de2ef8dad6..3d5f8ccfeb 100644 --- a/include/openspace/rendering/loadingscreen.h +++ b/include/openspace/rendering/loadingscreen.h @@ -29,6 +29,11 @@ #include #include +#include + +namespace ghoul::fontrendering { + class Font; +} // namespace ghoul::fontrendering namespace ghoul::opengl { class ProgramObject; @@ -39,19 +44,27 @@ namespace openspace { class LoadingScreen { public: - LoadingScreen(glm::vec2 windowSize); + LoadingScreen(); ~LoadingScreen(); void render(); + void queueMessage(std::string message); + private: std::unique_ptr _program; std::unique_ptr _logoTexture; - glm::vec2 _windowSize; + std::shared_ptr _loadingFont; + std::shared_ptr _messageFont; - GLuint _vao; - GLuint _vbo; + struct { + GLuint vao; + GLuint vbo; + } _logo; + + std::vector _messageQueue; + std::mutex _messageQueueMutex; }; } // namespace openspace diff --git a/openspace.cfg b/openspace.cfg index 557e5b1ba0..5e2d443e45 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -53,7 +53,8 @@ return { Fonts = { Mono = "${FONTS}/Bitstream-Vera-Sans-Mono/VeraMono.ttf", Light = "${FONTS}/Roboto/Roboto-Regular.ttf", - Console = "${FONTS}/Inconsolata/Inconsolata-Regular.ttf" + Console = "${FONTS}/Inconsolata/Inconsolata-Regular.ttf", + Loading = "${FONTS}/Roboto/Roboto-Regular.ttf" }, Logging = { LogDir = "${LOGS}", diff --git a/shaders/loadingscreen.frag b/shaders/loadingscreen.frag index fafa70944e..8a7994b2ed 100644 --- a/shaders/loadingscreen.frag +++ b/shaders/loadingscreen.frag @@ -24,10 +24,11 @@ #version __CONTEXT__ +in vec2 st; out vec4 FragColor; -uniform vec4 color; +uniform sampler2D logoTexture; void main() { - FragColor = color; + FragColor = texture(logoTexture, st); } diff --git a/shaders/loadingscreen.vert b/shaders/loadingscreen.vert index 40593fdcd8..e3d6ea77ca 100644 --- a/shaders/loadingscreen.vert +++ b/shaders/loadingscreen.vert @@ -25,9 +25,11 @@ #version __CONTEXT__ in vec2 in_position; +in vec2 in_st; -uniform mat4 ortho; +out vec2 st; void main() { - gl_Position = ortho * vec4(in_position, 0.0, 1.0); + gl_Position = vec4(in_position, 0.0, 1.0); + st = in_st; } diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 483cdf215d..c61a93a5d8 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -160,6 +160,7 @@ OpenSpaceEngine::OpenSpaceEngine(std::string programName, , _scriptScheduler(new scripting::ScriptScheduler) , _virtualPropertyManager(new VirtualPropertyManager) , _globalPropertyNamespace(new properties::PropertyOwner({ "" })) + , _loadingScreen(nullptr) , _versionInformation{ properties::StringProperty(VersionInfo, OPENSPACE_VERSION_STRING_FULL), properties::StringProperty(SourceControlInfo, OPENSPACE_GIT_FULL) @@ -614,24 +615,28 @@ void OpenSpaceEngine::loadScene(const std::string& scenePath) { _renderEngine->setGlobalBlackOutFactor(0.0); _renderEngine->startFading(1, 3.0); + _loadingScreen = std::make_unique(); + // We can initialize all SceneGraphNodes in a separate thread since none of them use // an OpenGL context std::atomic_bool initializeFinished = false; - std::thread t([scene, &initializeFinished]() { + std::thread t([scene, &initializeFinished, this]() { scene->initialize(); + postLoadingScreenMessage("Finished initializing"); initializeFinished = true; }); - LoadingScreen loadingScreen(_windowWrapper->currentWindowResolution()); // While the SceneGraphNodes initialize themselves, we can hand over control to the // Loading screen rendering - while (!initializeFinished) { - loadingScreen.render(); + + while (!initializeFinished) { + _loadingScreen->render(); } t.join(); + _loadingScreen = nullptr; scene->initializeGL(); @@ -1368,6 +1373,10 @@ void OpenSpaceEngine::toggleShutdownMode() { } } +void OpenSpaceEngine::postLoadingScreenMessage(std::string message) { + _loadingScreen->queueMessage(std::move(message)); +} + scripting::LuaLibrary OpenSpaceEngine::luaLibrary() { return { "", diff --git a/src/rendering/loadingscreen.cpp b/src/rendering/loadingscreen.cpp index 2a82203e58..5b88d942b6 100644 --- a/src/rendering/loadingscreen.cpp +++ b/src/rendering/loadingscreen.cpp @@ -27,55 +27,119 @@ #include #include +#include +#include +#include + #include #include #include #include +#include #include +namespace { + const glm::vec2 LogoCenter = { 0.f, 0.4f }; + const glm::vec2 LogoSize = { 0.4f, 0.4 }; + + const float LoadingTextPosition = 0.275f; + const float StatusMessageOffset = 0.05f; + + const int MaximumMessageQueue = 6; + + const std::chrono::milliseconds RefreshRate(16); + + const float MinimumAlpha = 0.2f; + const float MaximumAlpha = 1.f; + +} // namespace + namespace openspace { -LoadingScreen::LoadingScreen(glm::vec2 windowSize) - : _windowSize(std::move(windowSize)) -{ +LoadingScreen::LoadingScreen() { + const glm::vec2 dpiScaling = OsEng.windowWrapper().dpiScaling(); + const glm::ivec2 res = + glm::vec2(OsEng.windowWrapper().currentWindowResolution()) / dpiScaling; + _program = ghoul::opengl::ProgramObject::Build( "Loading Screen", "${SHADERS}/loadingscreen.vert", "${SHADERS}/loadingscreen.frag" ); - - _logoTexture = ghoul::io::TextureReader::ref().loadTexture(absPath("${OPENSPACE_DATA}/openspace-logo.png")); - - GLfloat data[] = { - 0.f, 0.f, - 1.f, 1.f, - 0.f, 1.f, - - 0.f, 0.f, - 1.f, 0.f, - 1.f, 1.f - }; - - glGenVertexArrays(1, &_vao); - glBindVertexArray(_vao); - glGenBuffers(1, &_vbo); - glBindBuffer(GL_ARRAY_BUFFER, _vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW); - - glEnableVertexAttribArray(0); - glVertexAttribPointer( - 0, - 2, - GL_FLOAT, - GL_FALSE, - 2 * sizeof(GLfloat), - nullptr + _loadingFont = OsEng.fontManager().font( + "Loading", + 25, + ghoul::fontrendering::FontManager::Outline::No, + ghoul::fontrendering::FontManager::LoadGlyphs::No ); - glBindVertexArray(0); + _messageFont = OsEng.fontManager().font( + "Loading", + 13, + ghoul::fontrendering::FontManager::Outline::No, + ghoul::fontrendering::FontManager::LoadGlyphs::No + ); + + { + // Logo stuff + _logoTexture = ghoul::io::TextureReader::ref().loadTexture( + absPath("${OPENSPACE_DATA}/openspace-logo.png") + ); + _logoTexture->uploadTexture(); + + float screenAspectRatio = static_cast(res.x) / static_cast(res.y); + + float textureAspectRatio = static_cast(_logoTexture->dimensions().x) / + static_cast(_logoTexture->dimensions().y); + + glm::vec2 size = { + LogoSize.x, + LogoSize.y * textureAspectRatio * screenAspectRatio + }; + + glm::vec2 ll = { LogoCenter.x - size.x, LogoCenter.y - size.y }; + glm::vec2 ur = { LogoCenter.x + size.x, LogoCenter.y + size.y }; + + GLfloat data[] = { + ll.x, ll.y, 0.f, 0.f, + ur.x, ur.y, 1.f, 1.f, + ll.x, ur.y, 0.f, 1.f, + ll.x, ll.y, 0.f, 0.f, + ur.x, ll.y, 1.f, 0.f, + ur.x, ur.y, 1.f, 1.f + }; + + glGenVertexArrays(1, &_logo.vao); + glBindVertexArray(_logo.vao); + glGenBuffers(1, &_logo.vbo); + glBindBuffer(GL_ARRAY_BUFFER, _logo.vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer( + 0, + 2, + GL_FLOAT, + GL_FALSE, + 4 * sizeof(GLfloat), + nullptr + ); + + glEnableVertexAttribArray(1); + glVertexAttribPointer( + 1, + 2, + GL_FLOAT, + GL_FALSE, + 4 * sizeof(GLfloat), + reinterpret_cast(2 * sizeof(GLfloat)) + ); + + glBindVertexArray(0); + } } LoadingScreen::~LoadingScreen() { @@ -83,38 +147,131 @@ LoadingScreen::~LoadingScreen() { } void LoadingScreen::render() { + const glm::vec2 dpiScaling = OsEng.windowWrapper().dpiScaling(); + const glm::ivec2 res = + glm::vec2(OsEng.windowWrapper().currentWindowResolution()) / dpiScaling; + + float screenAspectRatio = static_cast(res.x) / static_cast(res.y); + + float textureAspectRatio = static_cast(_logoTexture->dimensions().x) / + static_cast(_logoTexture->dimensions().y); + + glm::vec2 size = { + LogoSize.x, + LogoSize.y * textureAspectRatio * screenAspectRatio + }; + + glm::vec2 ll = { LogoCenter.x - size.x, LogoCenter.y - size.y }; + glm::vec2 ur = { LogoCenter.x + size.x, LogoCenter.y + size.y }; + + GLfloat data[] = { + ll.x, ll.y, 0.f, 0.f, + ur.x, ur.y, 1.f, 1.f, + ll.x, ur.y, 0.f, 1.f, + ll.x, ll.y, 0.f, 0.f, + ur.x, ll.y, 1.f, 0.f, + ur.x, ur.y, 1.f, 1.f + }; + + glBindVertexArray(_logo.vao); + glBindBuffer(GL_ARRAY_BUFFER, _logo.vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW); + + // Clear background - glClearColor(0.8f, 0.8f, 0.8f, 1.f); + glClearColor(0.f, 0.f, 0.f, 1.f); glClear(ClearBufferMask::GL_COLOR_BUFFER_BIT); + glDisable(GL_CULL_FACE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_DEPTH_TEST); + + // Render logo _program->activate(); - _program->setUniform( - "ortho", - glm::ortho( - 0.f, static_cast(_windowSize.x), 0.f, static_cast(_windowSize.y) - ) - ); - - std::random_device rd; //Will be used to obtain a seed for the random number engine - std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd() - std::uniform_real_distribution<> dis(0.0, 1.0); + ghoul::opengl::TextureUnit unit; + unit.activate(); + _logoTexture->bind(); _program->setUniform( - "color", - glm::vec4(dis(gen), dis(gen), dis(gen), 1.f) + "logoTexture", + unit ); - // Draw the background color - glBindVertexArray(_vao); glDrawArrays(GL_TRIANGLES, 0, 6); - + glBindVertexArray(0); _program->deactivate(); + // "Loading" text + using FR = ghoul::fontrendering::FontRenderer; + FR& renderer = FR::defaultRenderer(); - std::this_thread::sleep_for(std::chrono::milliseconds(16)); + // We use "Loading" to center the text, but render "Loading..." to make it look more + // pleasing + FR::BoundingBoxInformation bbox = renderer.boundingBox( + *_loadingFont, + "%s", + "Loading." + ); + + renderer.render( + *_loadingFont, + glm::vec2(res.x / 2.f - bbox.boundingBox.x / 2.f, res.y * LoadingTextPosition), + glm::vec4(1.f, 1.f, 1.f, 1.f), + "%s", + "Loading..." + ); + + + { + std::lock_guard guard(_messageQueueMutex); + + for (int i = 0; i < _messageQueue.size(); ++i) { + const std::string& message = _messageQueue[i]; + + FR::BoundingBoxInformation bboxMessage = renderer.boundingBox( + *_messageFont, + "%s", + message.c_str() + ); + + renderer.render( + *_messageFont, + glm::vec2( + res.x / 2.f - bboxMessage.boundingBox.x / 2.f, + res.y * StatusMessageOffset + i * bboxMessage.boundingBox.y + ), + glm::vec4( + 1.f, 1.f, 1.f, + glm::mix( + MaximumAlpha, + MinimumAlpha, + static_cast(i) / static_cast(MaximumMessageQueue - 1) + ) + ), + "%s", + message.c_str() + ); + } + } + + glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + + std::this_thread::sleep_for(RefreshRate); OsEng.windowWrapper().swapBuffer(); } +void LoadingScreen::queueMessage(std::string message) { + std::lock_guard guard(_messageQueueMutex); + _messageQueue.insert(_messageQueue.begin(), std::move(message)); + + // We add one message at a time, so we can also delete one at a time + if (_messageQueue.size() > MaximumMessageQueue) { + _messageQueue.pop_back(); + } +} + } // namespace openspace diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 870dc942bd..600559a80f 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -214,14 +214,25 @@ void Scene::sortTopologically() { } void Scene::initialize() { + std::vector threads; for (SceneGraphNode* node : _topologicallySortedNodes) { - try { - node->initialize(); - } - catch (const ghoul::RuntimeError& e) { - LERROR(node->name() << " not initialized."); - LERRORC(std::string(_loggerCat) + "(" + e.component + ")", e.what()); - } + std::thread t( + [node]() { + try { + OsEng.postLoadingScreenMessage(node->name()); + node->initialize(); + } + catch (const ghoul::RuntimeError& e) { + LERROR(node->name() << " not initialized."); + LERRORC(std::string(_loggerCat) + "(" + e.component + ")", e.what()); + } + } + ); + threads.push_back(std::move(t)); + } + + for (std::thread& t : threads) { + t.join(); } } From b8869256167669da469e569a1b7082405c787ce4 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Fri, 3 Nov 2017 16:25:06 -0400 Subject: [PATCH 04/33] Move OpenGL in globebrowsing from constructors into initialization functions Place entire scene loading into separate thread --- .../globebrowsing/globes/renderableglobe.cpp | 15 +++- .../globebrowsing/rendering/chunkrenderer.cpp | 2 +- .../globebrowsing/rendering/layer/layer.cpp | 12 +++ modules/globebrowsing/rendering/layer/layer.h | 3 + .../rendering/layer/layergroup.cpp | 14 ++- .../rendering/layer/layergroup.h | 3 + .../rendering/layer/layermanager.cpp | 12 +++ .../rendering/layer/layermanager.h | 3 + .../tileprovider/temporaltileprovider.cpp | 23 +++-- .../tile/tileprovider/temporaltileprovider.h | 5 ++ .../tile/tileprovider/texttileprovider.cpp | 25 ++++-- .../tile/tileprovider/texttileprovider.h | 3 + .../tile/tileprovider/tileprovider.cpp | 8 +- .../tile/tileprovider/tileproviderbylevel.cpp | 18 ++++ .../tile/tileprovider/tileproviderbylevel.h | 3 + src/engine/openspaceengine.cpp | 89 +++++++++++-------- 16 files changed, 176 insertions(+), 62 deletions(-) diff --git a/modules/globebrowsing/globes/renderableglobe.cpp b/modules/globebrowsing/globes/renderableglobe.cpp index 63406ca258..1b83cf95a7 100644 --- a/modules/globebrowsing/globes/renderableglobe.cpp +++ b/modules/globebrowsing/globes/renderableglobe.cpp @@ -259,17 +259,24 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary) addPropertySubOwner(_layerManager.get()); //addPropertySubOwner(_pointGlobe.get()); +} + +void RenderableGlobe::initializeGL() { + _layerManager->initialize(); + + _layerManager->update(); + + _distanceSwitch.initializeGL(); + // Recompile the shaders directly so that it is not done the first time the render // function is called. _chunkedLodGlobe->recompileShaders(); } -void RenderableGlobe::initializeGL() { - _distanceSwitch.initializeGL(); -} - void RenderableGlobe::deinitializeGL() { _distanceSwitch.deinitializeGL(); + + _layerManager->deinitialize(); } bool RenderableGlobe::isReady() const { diff --git a/modules/globebrowsing/rendering/chunkrenderer.cpp b/modules/globebrowsing/rendering/chunkrenderer.cpp index 522e6795b5..eda8557a69 100644 --- a/modules/globebrowsing/rendering/chunkrenderer.cpp +++ b/modules/globebrowsing/rendering/chunkrenderer.cpp @@ -38,7 +38,7 @@ namespace openspace::globebrowsing { ChunkRenderer::ChunkRenderer(std::shared_ptr grid, std::shared_ptr layerManager) : _grid(grid) - ,_layerManager(layerManager) + , _layerManager(layerManager) { _globalLayerShaderManager = std::make_shared( "GlobalChunkedLodPatch", diff --git a/modules/globebrowsing/rendering/layer/layer.cpp b/modules/globebrowsing/rendering/layer/layer.cpp index 1edc55dcf6..cbeb201d7a 100644 --- a/modules/globebrowsing/rendering/layer/layer.cpp +++ b/modules/globebrowsing/rendering/layer/layer.cpp @@ -219,6 +219,18 @@ Layer::Layer(layergroupid::GroupID id, const ghoul::Dictionary& layerDict, addPropertySubOwner(_layerAdjustment); } +void Layer::initialize() { + if (_tileProvider) { + _tileProvider->initialize(); + } +} + +void Layer::deinitialize() { + if (_tileProvider) { + _tileProvider->deinitialize(); + } +} + ChunkTilePile Layer::getChunkTilePile(const TileIndex& tileIndex, int pileSize) const { if (_tileProvider) { return _tileProvider->getChunkTilePile(tileIndex, pileSize); diff --git a/modules/globebrowsing/rendering/layer/layer.h b/modules/globebrowsing/rendering/layer/layer.h index 9adbeae1fc..3f74021eda 100644 --- a/modules/globebrowsing/rendering/layer/layer.h +++ b/modules/globebrowsing/rendering/layer/layer.h @@ -55,6 +55,9 @@ public: Layer(layergroupid::GroupID id, const ghoul::Dictionary& layerDict, LayerGroup& parent); + void initialize(); + void deinitialize(); + ChunkTilePile getChunkTilePile(const TileIndex& tileIndex, int pileSize) const; Tile::Status getTileStatus(const TileIndex& index) const; diff --git a/modules/globebrowsing/rendering/layer/layergroup.cpp b/modules/globebrowsing/rendering/layer/layergroup.cpp index 85ddabfecf..e4c1a487a2 100644 --- a/modules/globebrowsing/rendering/layer/layergroup.cpp +++ b/modules/globebrowsing/rendering/layer/layergroup.cpp @@ -79,6 +79,18 @@ LayerGroup::LayerGroup(layergroupid::GroupID id, const ghoul::Dictionary& dict) } } +void LayerGroup::initialize() { + for (const std::shared_ptr& l : _layers) { + l->initialize(); + } +} + +void LayerGroup::deinitialize() { + for (const std::shared_ptr& l : _layers) { + l->deinitialize(); + } +} + void LayerGroup::update() { _activeLayers.clear(); @@ -102,7 +114,7 @@ void LayerGroup::addLayer(const ghoul::Dictionary& layerDict) { } else { _layers.push_back(layer); - update(); + //update(); if (_onChangeCallback) { _onChangeCallback(); } diff --git a/modules/globebrowsing/rendering/layer/layergroup.h b/modules/globebrowsing/rendering/layer/layergroup.h index 602cfaf342..eb83bc78a6 100644 --- a/modules/globebrowsing/rendering/layer/layergroup.h +++ b/modules/globebrowsing/rendering/layer/layergroup.h @@ -46,6 +46,9 @@ struct LayerGroup : public properties::PropertyOwner { LayerGroup(layergroupid::GroupID id); LayerGroup(layergroupid::GroupID id, const ghoul::Dictionary& dict); + void initialize(); + void deinitialize(); + /// Updates all layers tile providers within this group void update(); diff --git a/modules/globebrowsing/rendering/layer/layermanager.cpp b/modules/globebrowsing/rendering/layer/layermanager.cpp index 7e012c501c..3ebe121b9c 100644 --- a/modules/globebrowsing/rendering/layer/layermanager.cpp +++ b/modules/globebrowsing/rendering/layer/layermanager.cpp @@ -68,6 +68,18 @@ LayerManager::LayerManager(const ghoul::Dictionary& layerGroupsDict) } } +void LayerManager::initialize() { + for (const std::shared_ptr& lg : _layerGroups) { + lg->initialize(); + } +} + +void LayerManager::deinitialize() { + for (const std::shared_ptr& lg : _layerGroups) { + lg->deinitialize(); + } +} + void LayerManager::addLayer(layergroupid::GroupID groupId, ghoul::Dictionary layerDict) { ghoul_assert(groupId != layergroupid::Unknown, "Layer group ID must be known"); _layerGroups[groupId]->addLayer(layerDict); diff --git a/modules/globebrowsing/rendering/layer/layermanager.h b/modules/globebrowsing/rendering/layer/layermanager.h index dfc331bfa1..e6014af46c 100644 --- a/modules/globebrowsing/rendering/layer/layermanager.h +++ b/modules/globebrowsing/rendering/layer/layermanager.h @@ -45,6 +45,9 @@ class LayerManager : public properties::PropertyOwner { public: LayerManager(const ghoul::Dictionary& textureCategoriesDictionary); + void initialize(); + void deinitialize(); + void addLayer(layergroupid::GroupID groupId, ghoul::Dictionary layerDict); void deleteLayer(layergroupid::GroupID groupId, std::string layerName); diff --git a/modules/globebrowsing/tile/tileprovider/temporaltileprovider.cpp b/modules/globebrowsing/tile/tileprovider/temporaltileprovider.cpp index 59f84a42b9..7dfbd79391 100644 --- a/modules/globebrowsing/tile/tileprovider/temporaltileprovider.cpp +++ b/modules/globebrowsing/tile/tileprovider/temporaltileprovider.cpp @@ -84,15 +84,10 @@ TemporalTileProvider::TemporalTileProvider(const ghoul::Dictionary& dictionary) if (hasStart && hasEnd) { const std::string start = dictionary.value(KeyPreCacheStartTime); const std::string end = dictionary.value(KeyPreCacheEndTime); - std::vector