mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-02-25 22:39:04 -06:00
Some more work on rendering loading screen information
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user