Some more work on rendering loading screen information

This commit is contained in:
Alexander Bock
2017-11-04 11:12:22 -04:00
parent b886925616
commit 6ecb180ac0
6 changed files with 259 additions and 65 deletions

View File

@@ -103,8 +103,6 @@ public:
void writeDocumentation();
void toggleShutdownMode();
void postLoadingScreenMessage(std::string message);
void runPostInitializationScripts(const std::string& sceneDescription);
// Guaranteed to return a valid pointer
@@ -112,6 +110,7 @@ public:
LuaConsole& console();
DownloadManager& downloadManager();
ModuleEngine& moduleEngine();
LoadingScreen& loadingScreen();
NetworkEngine& networkEngine();
ParallelConnection& parallelConnection();
RenderEngine& renderEngine();

View File

@@ -30,6 +30,7 @@
#include <memory>
#include <mutex>
#include <random>
namespace ghoul::fontrendering {
class Font;
@@ -49,7 +50,18 @@ public:
void render();
void queueMessage(std::string message);
void postMessage(std::string message);
enum class ItemStatus {
Started,
Initializing,
Finished
};
void updateItem(const std::string& itemName, ItemStatus newStatus);
private:
std::unique_ptr<ghoul::opengl::ProgramObject> _program;
@@ -57,14 +69,30 @@ private:
std::shared_ptr<ghoul::fontrendering::Font> _loadingFont;
std::shared_ptr<ghoul::fontrendering::Font> _messageFont;
std::shared_ptr<ghoul::fontrendering::Font> _itemFont;
struct {
GLuint vao;
GLuint vbo;
} _logo;
std::vector<std::string> _messageQueue;
std::mutex _messageQueueMutex;
std::string _message;
std::mutex _messageMutex;
struct Item {
std::string name;
ItemStatus status;
bool hasLocation;
glm::vec2 ll;
glm::vec2 ur;
std::chrono::system_clock::time_point finishedTime;
};
std::vector<Item> _items;
std::mutex _itemsMutex;
std::random_device _randomDevice;
std::default_random_engine _randomEngine;
};
} // namespace openspace

View File

@@ -633,7 +633,7 @@ void OpenSpaceEngine::loadScene(const std::string& scenePath) {
_renderEngine->startFading(1, 3.0);
scene->initialize();
postLoadingScreenMessage("Finished initializing");
_loadingScreen->postMessage("Finished initializing");
initializeFinished = true;
});
@@ -1388,10 +1388,6 @@ void OpenSpaceEngine::toggleShutdownMode() {
}
}
void OpenSpaceEngine::postLoadingScreenMessage(std::string message) {
_loadingScreen->queueMessage(std::move(message));
}
scripting::LuaLibrary OpenSpaceEngine::luaLibrary() {
return {
"",
@@ -1559,6 +1555,11 @@ TimeManager& OpenSpaceEngine::timeManager() {
return *_timeManager;
}
LoadingScreen& OpenSpaceEngine::loadingScreen() {
ghoul_assert(_loadingScreen, "Loading Screen must not be nullptr");
return *_loadingScreen;
}
WindowWrapper& OpenSpaceEngine::windowWrapper() {
ghoul_assert(_windowWrapper, "Window Wrapper must not be nullptr");
return *_windowWrapper;

View File

@@ -44,7 +44,7 @@ namespace {
const glm::vec2 LogoSize = { 0.4f, 0.4 };
const float LoadingTextPosition = 0.275f;
const float StatusMessageOffset = 0.05f;
const float StatusMessageOffset = 0.225f;
const int MaximumMessageQueue = 6;
@@ -57,7 +57,9 @@ namespace {
namespace openspace {
LoadingScreen::LoadingScreen() {
LoadingScreen::LoadingScreen()
: _randomEngine(_randomDevice())
{
const glm::vec2 dpiScaling = OsEng.windowWrapper().dpiScaling();
const glm::ivec2 res =
glm::vec2(OsEng.windowWrapper().currentWindowResolution()) / dpiScaling;
@@ -77,6 +79,13 @@ LoadingScreen::LoadingScreen() {
_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,
@@ -161,16 +170,16 @@ void LoadingScreen::render() {
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 };
glm::vec2 logoLl = { LogoCenter.x - size.x, LogoCenter.y - size.y };
glm::vec2 logoUr = { 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
logoLl.x, logoLl.y, 0.f, 0.f,
logoUr.x, logoUr.y, 1.f, 1.f,
logoLl.x, logoUr.y, 0.f, 1.f,
logoLl.x, logoLl.y, 0.f, 0.f,
logoUr.x, logoLl.y, 1.f, 0.f,
logoUr.x, logoUr.y, 1.f, 1.f
};
glBindVertexArray(_logo.vao);
@@ -216,45 +225,151 @@ void LoadingScreen::render() {
"Loading."
);
glm::vec2 loadingLl = glm::vec2(
res.x / 2.f - bbox.boundingBox.x / 2.f,
res.y * LoadingTextPosition
);
glm::vec2 loadingUr = loadingLl + bbox.boundingBox;
renderer.render(
*_loadingFont,
glm::vec2(res.x / 2.f - bbox.boundingBox.x / 2.f, res.y * LoadingTextPosition),
loadingLl,
glm::vec4(1.f, 1.f, 1.f, 1.f),
"%s",
"Loading..."
);
glm::vec2 messageLl;
glm::vec2 messageUr;
{
std::lock_guard<std::mutex> guard(_messageMutex);
FR::BoundingBoxInformation bboxMessage = renderer.boundingBox(
*_messageFont,
"%s",
_message.c_str()
);
messageLl = glm::vec2(
res.x / 2.f - bboxMessage.boundingBox.x / 2.f,
res.y * StatusMessageOffset
);
messageUr = messageLl + bboxMessage.boundingBox;
renderer.render(
*_messageFont,
messageLl,
glm::vec4(1.f, 1.f, 1.f, 1.f),
"%s",
_message.c_str()
);
}
{
std::lock_guard<std::mutex> guard(_messageQueueMutex);
std::lock_guard<std::mutex> guard(_itemsMutex);
for (int i = 0; i < _messageQueue.size(); ++i) {
const std::string& message = _messageQueue[i];
for (Item& item : _items) {
if (!item.hasLocation) {
// Compute a new location
FR::BoundingBoxInformation b = renderer.boundingBox(
*_itemFont,
"%s",
item.name.c_str()
);
FR::BoundingBoxInformation bboxMessage = renderer.boundingBox(
*_messageFont,
"%s",
message.c_str()
);
// The maximum count is in here since we can't control the amount of
// screen estate and the number of nodes. Rather than looping forever
// we make use with an overlap in the worst (=10) case
int MaxCounts = 30;
bool foundSpace = false;
glm::vec2 ll;
glm::vec2 ur;
for (int i = 0; i < MaxCounts && !foundSpace; ++i) {
std::uniform_int_distribution<int> distX(
15,
res.x - b.boundingBox.x - 15
);
std::uniform_int_distribution<int> distY(
15,
res.y - b.boundingBox.y - 15
);
ll = { distX(_randomEngine), distY(_randomEngine) };
ur = ll + b.boundingBox;
// Test against logo and text
bool logoOverlap = !(
(logoUr.x + 1.f) / 2.f * res.x < ll.x ||
(logoLl.x + 1.f) / 2.f * res.x > ur.x ||
(logoUr.y + 1.f) / 2.f * res.y < ll.y ||
(logoLl.y + 1.f) / 2.f * res.y > ur.y
);
bool loadingOverlap = !(
loadingUr.x < ll.x ||
loadingLl.x > ur.x ||
loadingUr.y < ll.y ||
loadingLl.y > ur.y
);
bool messageOverlap = !(
messageUr.x < ll.x ||
messageLl.x > ur.x ||
messageUr.y < ll.y ||
messageLl.y > ur.y
);
if (logoOverlap || loadingOverlap || messageOverlap) {
continue;
}
// Test against all other boxes
bool overlap = false;
for (const Item& j : _items) {
overlap |= !(j.ur.x < ll.x || j.ll.x > ur.x || j.ur.y < ll.y || j.ll.y > ur.y);
if (overlap) {
break;
}
}
if (!overlap) {
break;
}
}
item.ll = ll;
item.ur = ur;
item.hasLocation = true;
}
glm::vec4 color = [status = item.status]() {
switch (status) {
case ItemStatus::Started:
return glm::vec4(0.5f, 0.5f, 0.5f, 1.f);
case ItemStatus::Initializing:
return glm::vec4(0.7f, 0.7f, 0.f, 1.f);
case ItemStatus::Finished:
return glm::vec4(1.f, 1.f, 1.f, 1.f);
}
}();
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<float>(i) / static_cast<float>(MaximumMessageQueue - 1)
)
),
*_itemFont,
item.ll,
color,
"%s",
message.c_str()
item.name.c_str()
);
}
}
glEnable(GL_CULL_FACE);
@@ -264,13 +379,42 @@ void LoadingScreen::render() {
OsEng.windowWrapper().swapBuffer();
}
void LoadingScreen::queueMessage(std::string message) {
std::lock_guard<std::mutex> guard(_messageQueueMutex);
_messageQueue.insert(_messageQueue.begin(), std::move(message));
void LoadingScreen::postMessage(std::string message) {
std::lock_guard<std::mutex> guard(_messageMutex);
_message = 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();
void LoadingScreen::updateItem(const std::string& itemName, ItemStatus newStatus) {
std::lock_guard<std::mutex> guard(_itemsMutex);
auto it = std::find_if(
_items.begin(),
_items.end(),
[&itemName](const Item& i) {
return i.name == itemName;
}
);
if (it != _items.end()) {
it->status = newStatus;
if (newStatus == ItemStatus::Finished) {
it->finishedTime = std::chrono::system_clock::now();
}
}
else {
ghoul_assert(
newStatus == ItemStatus::Started,
"Item '" + itemName + "' did not exist but first message was not Started"
);
// We are not computing the location in here since doing it this way might stall
// the main thread while trying to find a position for the new item
_items.push_back({
itemName,
ItemStatus::Started,
false,
{},
{},
std::chrono::system_clock::from_time_t(0)
});
}
}

View File

@@ -30,6 +30,7 @@
#include <openspace/engine/wrapper/windowwrapper.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/query/query.h>
#include <openspace/rendering/loadingscreen.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/scene/sceneloader.h>
@@ -46,6 +47,7 @@
#include <ghoul/lua/lua_helper.h>
#include <ghoul/misc/dictionary.h>
#include <ghoul/misc/onscopeexit.h>
#include <ghoul/misc/threadpool.h>
#include <ghoul/opengl/programobject.h>
#include <ghoul/opengl/texture.h>
@@ -214,26 +216,34 @@ void Scene::sortTopologically() {
}
void Scene::initialize() {
std::vector<std::thread> threads;
unsigned int nThreads = std::thread::hardware_concurrency();
ghoul::ThreadPool pool(nThreads == 0 ? 2 : nThreads - 1);
OsEng.loadingScreen().postMessage("Initializing scene");
for (SceneGraphNode* node : _topologicallySortedNodes) {
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());
}
pool.queue([node](){
try {
OsEng.loadingScreen().updateItem(
node->name(),
LoadingScreen::ItemStatus::Initializing
);
node->initialize();
OsEng.loadingScreen().updateItem(
node->name(),
LoadingScreen::ItemStatus::Finished
);
}
);
threads.push_back(std::move(t));
catch (const ghoul::RuntimeError& e) {
LERROR(node->name() << " not initialized.");
LERRORC(std::string(_loggerCat) + "(" + e.component + ")", e.what());
}
});
}
for (std::thread& t : threads) {
t.join();
}
pool.stop();
}
void Scene::initializeGL() {

View File

@@ -23,6 +23,7 @@
****************************************************************************************/
#include <openspace/engine/openspaceengine.h>
#include <openspace/rendering/loadingscreen.h>
#include <openspace/scene/sceneloader.h>
#include <openspace/scene/scene.h>
#include <openspace/scene/scenegraphnode.h>
@@ -135,6 +136,11 @@ std::unique_ptr<Scene> SceneLoader::loadScene(const std::string& path) {
std::unique_ptr<SceneGraphNode> rootNode = std::make_unique<SceneGraphNode>();
rootNode->setName(SceneGraphNode::RootNodeName);
scene->setRoot(std::move(rootNode));
OsEng.loadingScreen().updateItem(
SceneGraphNode::RootNodeName,
LoadingScreen::ItemStatus::Started
);
addLoadedNodes(*scene, std::move(allNodes));
@@ -287,6 +293,12 @@ SceneLoader::LoadedNode SceneLoader::loadNode(const ghoul::Dictionary& dictionar
std::vector<std::string> dependencies;
std::string nodeName = dictionary.value<std::string>(KeyName);
OsEng.loadingScreen().updateItem(
nodeName,
LoadingScreen::ItemStatus::Started
);
std::string parentName = dictionary.value<std::string>(KeyParentName);
std::unique_ptr<SceneGraphNode> node = SceneGraphNode::createFromDictionary(dictionary);