Add progress bar to the loading screen

Add configuration options to customize the loading screen
This commit is contained in:
Alexander Bock
2017-11-05 14:49:46 -05:00
parent 3afd2bf514
commit dc9831b6a6
10 changed files with 446 additions and 89 deletions
@@ -156,6 +156,20 @@ public:
/// The part of the key storing whether each OpenGL call should be logged
static const std::string KeyLogEachOpenGLCall;
/// This key determines whether the scene graph nodes should initialized multithreaded
static const std::string KeyUseMultithreadedInitialization;
/// The key under which all of the loading settings are grouped
static const std::string KeyLoadingScreen;
/// The part of the key storing whether the loading screen should display the message
/// about current status
static const std::string PartShowMessage;
/// The part of the key storing whether the loading screen should display node names
static const std::string PartShowNodeNames;
/// The part of the key storing whether the loading screen should contain a progress
/// bar
static const std::string PartShowProgressbar;
/**
* Iteratively walks the directory structure starting with \p filename to find the
+32 -4
View File
@@ -26,6 +26,7 @@
#define __OPENSPACE_CORE___LOADINGSCREEN___H__
#include <ghoul/glm.h>
#include <ghoul/misc/boolean.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <memory>
@@ -45,13 +46,27 @@ namespace openspace {
class LoadingScreen {
public:
LoadingScreen();
using ShowMessage = ghoul::Boolean;
using ShowNodeNames = ghoul::Boolean;
using ShowProgressbar = ghoul::Boolean;
LoadingScreen(ShowMessage showMessage, ShowNodeNames showNodeNames,
ShowProgressbar showProgressbar);
~LoadingScreen();
void render();
void postMessage(std::string message);
void setItemNumber(int nItems);
void tickItem();
enum class Phase {
Construction,
Initialization
};
void setPhase(Phase phase);
enum class ItemStatus {
Started,
@@ -61,9 +76,15 @@ public:
void updateItem(const std::string& itemName, ItemStatus newStatus);
private:
bool _showMessage;
bool _showNodeNames;
bool _showProgressbar;
Phase _phase;
int _iProgress;
int _nItems;
std::unique_ptr<ghoul::opengl::ProgramObject> _program;
std::unique_ptr<ghoul::opengl::Texture> _logoTexture;
@@ -76,10 +97,17 @@ private:
GLuint vbo;
} _logo;
struct {
GLuint vaoFill;
GLuint vboFill;
GLuint vaoBox;
GLuint vboBox;
} _progressbar;
std::string _message;
std::mutex _messageMutex;
struct Item {
std::string name;
ItemStatus status;
+6
View File
@@ -79,6 +79,12 @@ return {
FactoryDocumentation = "${DOCUMENTATION}/FactoryDocumentation.html",
LicenseDocumentation = "${DOCUMENTATION}/License.html",
UseMultithreadedInitialization = true,
LoadingScreen = {
ShowMessage = false,
ShowNodeNames = true,
ShowProgressbar = true
},
-- CheckOpenGLState = true,
-- LogEachOpenGLCall = true,
+8 -1
View File
@@ -27,8 +27,15 @@
in vec2 st;
out vec4 FragColor;
uniform bool useTexture;
uniform sampler2D logoTexture;
uniform vec4 color;
void main() {
FragColor = texture(logoTexture, st);
if (useTexture) {
FragColor = texture(logoTexture, st);
}
else {
FragColor = color;
}
}
+8
View File
@@ -104,6 +104,14 @@ const string ConfigurationManager::PartFilterSeverity = "PartFilterSeverity";
const string ConfigurationManager::KeyCheckOpenGLState = "CheckOpenGLState";
const string ConfigurationManager::KeyLogEachOpenGLCall = "LogEachOpenGLCall";
const string ConfigurationManager::KeyUseMultithreadedInitialization =
"UseMultithreadedInitialization";
const string ConfigurationManager::KeyLoadingScreen = "LoadingScreen";
const string ConfigurationManager::PartShowMessage = "ShowMessage";
const string ConfigurationManager::PartShowNodeNames = "ShowNodeNames";
const string ConfigurationManager::PartShowProgressbar = "ShowProgressbar";
string ConfigurationManager::findConfiguration(const string& filename) {
using ghoul::filesystem::Directory;
+39
View File
@@ -427,6 +427,45 @@ documentation::Documentation ConfigurationManager::Documentation() {
"'TRACE' loglevel. This will bring the rendering to a crawl but provides "
"useful debugging features for the order in which OpenGL calls occur. This "
"defaults to 'false'."
},
{
ConfigurationManager::KeyUseMultithreadedInitialization,
new BoolVerifier,
Optional::Yes,
"This value determines whether the initialization of the scene graph should "
"occur multithreaded, that is, whether multiple scene graph nodes should "
"initialize in parallel. The only use for this value is to disable it for "
"debugging support."
},
{
ConfigurationManager::KeyLoadingScreen,
new TableVerifier({
{
ConfigurationManager::PartShowMessage,
new BoolVerifier,
Optional::Yes,
"If this value is set to 'true', the loading screen will display a "
"message information about the current phase the loading is in."
},
{
ConfigurationManager::PartShowNodeNames,
new BoolVerifier,
Optional::Yes,
"If this value is set to 'true', the loading screen will display a "
"list of all of the nodes with their respective status (created, "
"loaded, initialized)."
},
{
ConfigurationManager::PartShowProgressbar,
new BoolVerifier,
Optional::Yes,
"If this value is set to 'true', the loading screen will contain a "
"progress bar that gives an estimate of the loading progression."
}
}),
Optional::Yes,
"Values in this table describe the behavior of the loading screen that is "
"displayed while the scene graph is created and initialized."
}
}
};
+34 -1
View File
@@ -578,8 +578,38 @@ void OpenSpaceEngine::loadScene(const std::string& scenePath) {
Scene* scene;
bool showMessage = true;
std::string kMessage =
ConfigurationManager::KeyLoadingScreen + "." +
ConfigurationManager::PartShowMessage;
if (configurationManager().hasKey(kMessage)) {
showMessage = configurationManager().value<bool>(kMessage);
}
_loadingScreen = std::make_unique<LoadingScreen>();
bool showNodeNames = true;
std::string kNames =
ConfigurationManager::KeyLoadingScreen + "." +
ConfigurationManager::PartShowNodeNames;
if (configurationManager().hasKey(kNames)) {
showNodeNames = configurationManager().value<bool>(kNames);
}
bool showProgressbar = true;
std::string kProgress =
ConfigurationManager::KeyLoadingScreen + "." +
ConfigurationManager::PartShowProgressbar;
if (configurationManager().hasKey(kProgress)) {
showProgressbar = configurationManager().value<bool>(kProgress);
}
_loadingScreen = std::make_unique<LoadingScreen>(
LoadingScreen::ShowMessage(showMessage),
LoadingScreen::ShowNodeNames(showNodeNames),
LoadingScreen::ShowProgressbar(showProgressbar)
);
// We can initialize all SceneGraphNodes in a separate thread since none of them use
// an OpenGL context
@@ -587,6 +617,7 @@ void OpenSpaceEngine::loadScene(const std::string& scenePath) {
bool errorWhileLoading = false;
std::thread t([&scene, scenePath, &initializeFinished, &errorWhileLoading, this]() {
_loadingScreen->postMessage("Creating scene...");
_loadingScreen->setPhase(LoadingScreen::Phase::Construction);
try {
scene = _sceneManager->loadScene(scenePath);
}
@@ -633,6 +664,8 @@ void OpenSpaceEngine::loadScene(const std::string& scenePath) {
_renderEngine->setGlobalBlackOutFactor(0.0);
_renderEngine->startFading(1, 3.0);
_loadingScreen->setPhase(LoadingScreen::Phase::Initialization);
scene->initialize();
_loadingScreen->postMessage("Finished initializing");
initializeFinished = true;
+256 -56
View File
@@ -44,29 +44,49 @@ namespace {
const glm::vec2 LogoCenter = { 0.f, 0.4f };
const glm::vec2 LogoSize = { 0.4f, 0.4 };
const glm::vec2 ProgressbarCenter = { 0.f, -0.75f };
const glm::vec2 ProgressbarSize = { 0.7f, 0.015f };
const float ProgressbarLineWidth = 0.0025f;
const glm::vec4 ProgressbarOutlineColor = glm::vec4(0.9f, 0.9f, 0.9f, 1.f);
const glm::vec4 PhaseColorConstruction = glm::vec4(0.7f, 0.7f, 0.f, 1.f);
const glm::vec4 PhaseColorInitialization = glm::vec4(0.1f, 0.75f, 0.1f, 1.f);
const glm::vec4 ItemStatusColorStarted = glm::vec4(0.5f, 0.5f, 0.5f, 1.f);
const glm::vec4 ItemStatusColorInitializing = glm::vec4(0.7f, 0.7f, 0.f, 1.f);
const glm::vec4 ItemStatusColorFinished = glm::vec4(0.1f, 0.75f, 0.1f, 1.f);
const float LoadingTextPosition = 0.275f;
const float StatusMessageOffset = 0.225f;
const int MaximumMessageQueue = 6;
const std::chrono::milliseconds TTL(5000);
const std::chrono::milliseconds RefreshRate(16);
const float MinimumAlpha = 0.2f;
const float MaximumAlpha = 1.f;
} // namespace
namespace openspace {
LoadingScreen::LoadingScreen()
: _randomEngine(_randomDevice())
LoadingScreen::LoadingScreen(ShowMessage showMessage, ShowNodeNames showNodeNames,
ShowProgressbar showProgressbar)
: _showMessage(showMessage)
, _showNodeNames(showNodeNames)
, _showProgressbar(showProgressbar)
, _iProgress(0)
, _nItems(0)
, _loadingFont(nullptr)
, _messageFont(nullptr)
, _itemFont(nullptr)
, _logo{ 0, 0 }
, _progressbar{ 0, 0, 0, 0 }
, _randomEngine(_randomDevice())
{
const glm::vec2 dpiScaling = OsEng.windowWrapper().dpiScaling();
const glm::ivec2 res =
glm::vec2(OsEng.windowWrapper().currentWindowResolution()) / dpiScaling;
float screenAspectRatio = static_cast<float>(res.x) / static_cast<float>(res.y);
_program = ghoul::opengl::ProgramObject::Build(
"Loading Screen",
"${SHADERS}/loadingscreen.vert",
@@ -80,20 +100,23 @@ LoadingScreen::LoadingScreen()
ghoul::fontrendering::FontManager::LoadGlyphs::No
);
if (_showMessage) {
_messageFont = OsEng.fontManager().font(
"Loading",
22,
ghoul::fontrendering::FontManager::Outline::No,
ghoul::fontrendering::FontManager::LoadGlyphs::No
);
}
_messageFont = OsEng.fontManager().font(
"Loading",
22,
ghoul::fontrendering::FontManager::Outline::No,
ghoul::fontrendering::FontManager::LoadGlyphs::No
);
_itemFont = OsEng.fontManager().font(
"Loading",
13,
ghoul::fontrendering::FontManager::Outline::No,
ghoul::fontrendering::FontManager::LoadGlyphs::No
);
if (_showNodeNames) {
_itemFont = OsEng.fontManager().font(
"Loading",
13,
ghoul::fontrendering::FontManager::Outline::No,
ghoul::fontrendering::FontManager::LoadGlyphs::No
);
}
{
// Logo stuff
@@ -102,33 +125,13 @@ LoadingScreen::LoadingScreen()
);
_logoTexture->uploadTexture();
float screenAspectRatio = static_cast<float>(res.x) / static_cast<float>(res.y);
float textureAspectRatio = static_cast<float>(_logoTexture->dimensions().x) /
static_cast<float>(_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
};
static_cast<float>(_logoTexture->dimensions().y);
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(
@@ -152,13 +155,64 @@ LoadingScreen::LoadingScreen()
glBindVertexArray(0);
}
if (_showProgressbar) {
// Progress bar stuff
glGenVertexArrays(1, &_progressbar.vaoFill);
glBindVertexArray(_progressbar.vaoFill);
glGenBuffers(1, & _progressbar.vboFill);
glBindBuffer(GL_ARRAY_BUFFER, _progressbar.vboFill);
glEnableVertexAttribArray(0);
glVertexAttribPointer(
0,
2,
GL_FLOAT,
GL_FALSE,
2 * sizeof(GLfloat),
nullptr
);
glGenVertexArrays(1, &_progressbar.vaoBox);
glBindVertexArray(_progressbar.vaoBox);
glGenBuffers(1, & _progressbar.vboBox);
glBindBuffer(GL_ARRAY_BUFFER, _progressbar.vboBox);
glEnableVertexAttribArray(0);
glVertexAttribPointer(
0,
2,
GL_FLOAT,
GL_FALSE,
2 * sizeof(GLfloat),
nullptr
);
glBindVertexArray(0);
}
}
LoadingScreen::~LoadingScreen() {
_logoTexture = nullptr;
_loadingFont = nullptr;
_messageFont = nullptr;
_itemFont = nullptr;
glDeleteVertexArrays(1, &_logo.vao);
glDeleteBuffers(1, &_logo.vbo);
glDeleteVertexArrays(1, &_progressbar.vaoFill);
glDeleteBuffers(1, &_progressbar.vboFill);
glDeleteVertexArrays(1, &_progressbar.vaoBox);
glDeleteBuffers(1, &_progressbar.vboBox);
}
void LoadingScreen::render() {
// We have to recalculate the positions here because we will not be informed about a
// window size change
const glm::vec2 dpiScaling = OsEng.windowWrapper().dpiScaling();
const glm::ivec2 res =
glm::vec2(OsEng.windowWrapper().currentWindowResolution()) / dpiScaling;
@@ -189,8 +243,9 @@ void LoadingScreen::render() {
glBindBuffer(GL_ARRAY_BUFFER, _logo.vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
//
// Clear background
//
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(ClearBufferMask::GL_COLOR_BUFFER_BIT);
@@ -199,7 +254,9 @@ void LoadingScreen::render() {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST);
//
// Render logo
//
_program->activate();
ghoul::opengl::TextureUnit unit;
@@ -211,12 +268,124 @@ void LoadingScreen::render() {
unit
);
_program->setUniform(
"useTexture",
true
);
glDrawArrays(GL_TRIANGLES, 0, 6);
//
// Render progress bar
//
glm::vec2 progressbarSize = {
ProgressbarSize.x,
ProgressbarSize.y * screenAspectRatio
};
glm::vec2 progressbarLl = {
ProgressbarCenter.x - progressbarSize.x,
ProgressbarCenter.y - progressbarSize.y
};
glm::vec2 progressbarUr = {
ProgressbarCenter.x + progressbarSize.x ,
ProgressbarCenter.y + progressbarSize.y
};
if (_showProgressbar) {
glBindVertexArray(_progressbar.vaoFill);
// Depending on the progress, we only want to draw the progress bar to a mixture
// of the lowerleft and upper right extent
float progress = _nItems != 0 ?
static_cast<float>(_iProgress) / static_cast<float>(_nItems) :
0.f;
glm::vec2 ur = progressbarUr;
ur.x = glm::mix(progressbarLl.x, progressbarUr.x, progress);
GLfloat dataFill[] = {
progressbarLl.x, progressbarLl.y,
ur.x, ur.y,
progressbarLl.x, ur.y,
progressbarLl.x, progressbarLl.y,
ur.x, progressbarLl.y,
ur.x, ur.y,
};
glBindBuffer(GL_ARRAY_BUFFER, _progressbar.vboFill);
glBufferData(GL_ARRAY_BUFFER, sizeof(dataFill), dataFill, GL_STATIC_DRAW);
_program->setUniform("useTexture", false);
switch (_phase) {
case Phase::Construction:
_program->setUniform("color", PhaseColorConstruction);
case Phase::Initialization:
_program->setUniform("color", PhaseColorInitialization);
}
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(_progressbar.vaoBox);
float w = ProgressbarLineWidth;
GLfloat dataBox[] = {
// In order to avoid the deprecated glLineWidth, we split the lines into
// separate triangles instead
// Left side
progressbarLl.x - w , progressbarLl.y - w,
progressbarLl.x + w, progressbarUr.y + w,
progressbarLl.x - w, progressbarUr.y + w,
progressbarLl.x - w , progressbarLl.y - w,
progressbarLl.x + w , progressbarLl.y - w,
progressbarLl.x + w, progressbarUr.y + w,
// Top side
progressbarLl.x - w, progressbarUr.y - w,
progressbarUr.x + w, progressbarUr.y + w,
progressbarLl.x - w, progressbarUr.y + w,
progressbarLl.x - w, progressbarUr.y - w,
progressbarUr.x + w, progressbarUr.y - w,
progressbarUr.x + w, progressbarUr.y + w,
// Right side
progressbarUr.x - w, progressbarLl.y - w,
progressbarUr.x + w, progressbarUr.y + w,
progressbarUr.x - w, progressbarUr.y - w,
progressbarUr.x - w, progressbarLl.y - w,
progressbarUr.x + w, progressbarLl.y - w,
progressbarUr.x + w, progressbarUr.y + w,
// Bottom side
progressbarLl.x - w, progressbarLl.y - w,
progressbarUr.x + w, progressbarLl.y + w,
progressbarLl.x - w, progressbarLl.y + w,
progressbarLl.x - w, progressbarLl.y - w,
progressbarUr.x + w, progressbarLl.y - w,
progressbarUr.x + w, progressbarLl.y + w,
};
glBindBuffer(GL_ARRAY_BUFFER, _progressbar.vboBox);
glBufferData(GL_ARRAY_BUFFER, sizeof(dataBox), dataBox, GL_STATIC_DRAW);
_program->setUniform("useTexture", false);
_program->setUniform("color", ProgressbarOutlineColor);
glDrawArrays(GL_TRIANGLES, 0, 24);
}
glBindVertexArray(0);
_program->deactivate();
//
// "Loading" text
//
using FR = ghoul::fontrendering::FontRenderer;
FR& renderer = FR::defaultRenderer();
@@ -244,7 +413,7 @@ void LoadingScreen::render() {
glm::vec2 messageLl;
glm::vec2 messageUr;
{
if (_showMessage) {
std::lock_guard<std::mutex> guard(_messageMutex);
FR::BoundingBoxInformation bboxMessage = renderer.boundingBox(
@@ -269,7 +438,7 @@ void LoadingScreen::render() {
);
}
{
if (_showNodeNames) {
std::lock_guard<std::mutex> guard(_itemsMutex);
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
@@ -320,15 +489,28 @@ void LoadingScreen::render() {
loadingLl.y > ur.y
);
bool messageOverlap = !(
messageUr.x < ll.x ||
messageLl.x > ur.x ||
messageUr.y < ll.y ||
messageLl.y > ur.y
);
bool messageOverlap = false;
if (_showMessage) {
messageOverlap = !(
messageUr.x < ll.x ||
messageLl.x > ur.x ||
messageUr.y < ll.y ||
messageLl.y > ur.y
);
}
bool barOverlap = false;
if (_showProgressbar) {
barOverlap = !(
(progressbarUr.x + 1.f) / 2.f * res.x < ll.x ||
(progressbarLl.x + 1.f) / 2.f * res.x > ur.x ||
(progressbarUr.y + 1.f) / 2.f * res.y < ll.y ||
(progressbarLl.y + 1.f) / 2.f * res.y > ur.y
);
}
if (logoOverlap || loadingOverlap || messageOverlap) {
if (logoOverlap || loadingOverlap || messageOverlap || barOverlap) {
continue;
}
@@ -357,11 +539,11 @@ void LoadingScreen::render() {
glm::vec4 color = [status = item.status]() {
switch (status) {
case ItemStatus::Started:
return glm::vec4(0.5f, 0.5f, 0.5f, 1.f);
return ItemStatusColorStarted;
case ItemStatus::Initializing:
return glm::vec4(0.7f, 0.7f, 0.f, 1.f);
return ItemStatusColorInitializing;
case ItemStatus::Finished:
return glm::vec4(0.1f, 0.75f, 0.1f, 1.f);
return ItemStatusColorFinished;
default:
return glm::vec4(1.f);
}
@@ -413,7 +595,25 @@ void LoadingScreen::postMessage(std::string message) {
_message = std::move(message);
}
void LoadingScreen::setItemNumber(int nItems) {
_nItems = nItems;
}
void LoadingScreen::tickItem() {
++_iProgress;
}
void LoadingScreen::setPhase(Phase phase) {
_phase = phase;
_iProgress = 0;
}
void LoadingScreen::updateItem(const std::string& itemName, ItemStatus newStatus) {
if (!_showNodeNames) {
// If we don't want to show the node names, we can disable the updating which
// also would create any of the text information
return;
}
std::lock_guard<std::mutex> guard(_itemsMutex);
auto it = std::find_if(
+46 -26
View File
@@ -216,34 +216,54 @@ void Scene::sortTopologically() {
}
void Scene::initialize() {
unsigned int nThreads = std::thread::hardware_concurrency();
ghoul::ThreadPool pool(nThreads == 0 ? 2 : nThreads - 1);
OsEng.loadingScreen().postMessage("Initializing scene");
for (SceneGraphNode* node : _topologicallySortedNodes) {
pool.queue([node](){
try {
OsEng.loadingScreen().updateItem(
node->name(),
LoadingScreen::ItemStatus::Initializing
);
node->initialize();
OsEng.loadingScreen().updateItem(
node->name(),
LoadingScreen::ItemStatus::Finished
);
}
catch (const ghoul::RuntimeError& e) {
LERROR(node->name() << " not initialized.");
LERRORC(std::string(_loggerCat) + "(" + e.component + ")", e.what());
}
});
bool useMultipleThreads = true;
if (OsEng.configurationManager().hasKey(
ConfigurationManager::KeyUseMultithreadedInitialization
))
{
useMultipleThreads = OsEng.configurationManager().value<bool>(
ConfigurationManager::KeyUseMultithreadedInitialization
);
}
pool.stop();
auto initFunction = [](SceneGraphNode* node){
try {
OsEng.loadingScreen().updateItem(
node->name(),
LoadingScreen::ItemStatus::Initializing
);
node->initialize();
OsEng.loadingScreen().tickItem();
OsEng.loadingScreen().updateItem(
node->name(),
LoadingScreen::ItemStatus::Finished
);
}
catch (const ghoul::RuntimeError& e) {
LERROR(node->name() << " not initialized.");
LERRORC(std::string(_loggerCat) + "(" + e.component + ")", e.what());
}
};
if (useMultipleThreads) {
unsigned int nThreads = std::thread::hardware_concurrency();
ghoul::ThreadPool pool(nThreads == 0 ? 2 : nThreads - 1);
OsEng.loadingScreen().postMessage("Initializing scene");
for (SceneGraphNode* node : _topologicallySortedNodes) {
pool.queue(initFunction, node);
}
pool.stop();
}
else {
for (SceneGraphNode* node : _topologicallySortedNodes) {
initFunction(node);
}
}
}
void Scene::initializeGL() {
+3 -1
View File
@@ -133,6 +133,8 @@ std::unique_ptr<Scene> SceneLoader::loadScene(const std::string& path) {
std::unique_ptr<Scene> scene = std::make_unique<Scene>();
OsEng.loadingScreen().setItemNumber(allNodes.size() + 1); // +1 for Root node
std::unique_ptr<SceneGraphNode> rootNode = std::make_unique<SceneGraphNode>();
rootNode->setName(SceneGraphNode::RootNodeName);
scene->setRoot(std::move(rootNode));
@@ -297,7 +299,7 @@ SceneLoader::LoadedNode SceneLoader::loadNode(const ghoul::Dictionary& dictionar
nodeName,
LoadingScreen::ItemStatus::Started
);
OsEng.loadingScreen().tickItem();
std::string parentName = dictionary.value<std::string>(KeyParentName);
std::unique_ptr<SceneGraphNode> node = SceneGraphNode::createFromDictionary(dictionary);