From be8668da0b641a87b298a6fa9ddd661066d99ae5 Mon Sep 17 00:00:00 2001 From: Louise Poubel Date: Thu, 3 Mar 2022 13:34:59 -0800 Subject: [PATCH] =?UTF-8?q?Prevent=20MinimalScene=20=F0=9F=92=A5=20if=20an?= =?UTF-8?q?other=20scene=20is=20already=20loaded?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Louise Poubel --- src/Application.cc | 6 ++ src/plugins/minimal_scene/MinimalScene.cc | 118 +++++++++++++++++---- src/plugins/minimal_scene/MinimalScene.hh | 51 ++++++++- src/plugins/minimal_scene/MinimalScene.qml | 18 +++- 4 files changed, 163 insertions(+), 30 deletions(-) diff --git a/src/Application.cc b/src/Application.cc index 872d3758c..4d87ef7cb 100644 --- a/src/Application.cc +++ b/src/Application.cc @@ -385,6 +385,12 @@ std::string Application::DefaultConfigPath() bool Application::LoadPlugin(const std::string &_filename, const tinyxml2::XMLElement *_pluginElem) { + if (_filename.empty()) + { + ignerr << "Trying to load plugin with empty filename." << std::endl; + return false; + } + igndbg << "Loading plugin [" << _filename << "]" << std::endl; common::SystemPaths systemPaths; diff --git a/src/plugins/minimal_scene/MinimalScene.cc b/src/plugins/minimal_scene/MinimalScene.cc index f8aaac17a..f2ba1e52b 100644 --- a/src/plugins/minimal_scene/MinimalScene.cc +++ b/src/plugins/minimal_scene/MinimalScene.cc @@ -509,34 +509,55 @@ void IgnRenderer::BroadcastKeyPress() } ///////////////////////////////////////////////// -void IgnRenderer::Initialize() +std::string IgnRenderer::Initialize() { if (this->initialized) - return; + return std::string(); + + // Currently only support one engine at a time + rendering::RenderEngine *engine{nullptr}; + auto loadedEngines = rendering::loadedEngines(); + + // Load engine if there's no engine yet + if (loadedEngines.empty()) + { + std::map params; + params["useCurrentGLContext"] = "1"; + params["winID"] = std::to_string( + ignition::gui::App()->findChild()-> + QuickWindow()->winId()); + engine = rendering::engine(this->engineName, params); + } + else + { + if (!this->engineName.empty() && loadedEngines.front() != this->engineName) + { + ignwarn << "Failed to load engine [" << this->engineName + << "]. Using engine [" << loadedEngines.front() + << "], which is already loaded. Currently only one engine is " + << "supported at a time." << std::endl; + } + this->engineName = loadedEngines.front(); + engine = rendering::engine(loadedEngines.front()); + } - std::map params; - params["useCurrentGLContext"] = "1"; - params["winID"] = std::to_string( - ignition::gui::App()->findChild()-> - QuickWindow()->winId()); - auto engine = rendering::engine(this->engineName, params); if (!engine) { - ignerr << "Engine [" << this->engineName << "] is not supported" - << std::endl; - return; + return "Engine [" + this->engineName + "] is not supported"; } // Scene - auto scene = engine->SceneByName(this->sceneName); - if (!scene) + if (engine->SceneCount() > 0) { - igndbg << "Create scene [" << this->sceneName << "]" << std::endl; - scene = engine->CreateScene(this->sceneName); - scene->SetAmbientLight(this->ambientLight); - scene->SetBackgroundColor(this->backgroundColor); + return "Currently only one plugin providing a 3D scene is supported at a " + "time."; } + igndbg << "Create scene [" << this->sceneName << "]" << std::endl; + auto scene = engine->CreateScene(this->sceneName); + scene->SetAmbientLight(this->ambientLight); + scene->SetBackgroundColor(this->backgroundColor); + if (this->skyEnable) { scene->SetSkyEnabled(true); @@ -564,6 +585,7 @@ void IgnRenderer::Initialize() this->dataPtr->rayQuery = this->dataPtr->camera->Scene()->CreateRayQuery(); this->initialized = true; + return std::string(); } ///////////////////////////////////////////////// @@ -649,6 +671,12 @@ RenderThread::RenderThread() qRegisterMetaType("RenderSync*"); } +///////////////////////////////////////////////// +void RenderThread::SetErrorCb(std::function _cb) +{ + this->errorCb = _cb; +} + ///////////////////////////////////////////////// void RenderThread::RenderNext(RenderSync *_renderSync) { @@ -657,7 +685,12 @@ void RenderThread::RenderNext(RenderSync *_renderSync) if (!this->ignRenderer.initialized) { // Initialize renderer - this->ignRenderer.Initialize(); + auto loadingError = this->ignRenderer.Initialize(); + if (!loadingError.empty()) + { + this->errorCb(QString::fromStdString(loadingError)); + return; + } } // check if engine has been successfully initialized @@ -675,19 +708,25 @@ void RenderThread::RenderNext(RenderSync *_renderSync) ///////////////////////////////////////////////// void RenderThread::ShutDown() { - this->context->makeCurrent(this->surface); + if (this->context && this->surface) + this->context->makeCurrent(this->surface); this->ignRenderer.Destroy(); - this->context->doneCurrent(); - delete this->context; + if (this->context) + { + this->context->doneCurrent(); + delete this->context; + } // schedule this to be deleted only after we're done cleaning up - this->surface->deleteLater(); + if (this->surface) + this->surface->deleteLater(); // Stop event processing, move the thread to GUI and make sure it is deleted. this->exit(); - this->moveToThread(QGuiApplication::instance()->thread()); + if (this->ignRenderer.initialized) + this->moveToThread(QGuiApplication::instance()->thread()); } ///////////////////////////////////////////////// @@ -816,6 +855,12 @@ RenderWindowItem::RenderWindowItem(QQuickItem *_parent) ///////////////////////////////////////////////// RenderWindowItem::~RenderWindowItem() +{ + this->StopRendering(); +} + +///////////////////////////////////////////////// +void RenderWindowItem::StopRendering() { // Disconnect our QT connections. for(auto conn : this->dataPtr->connections) @@ -989,6 +1034,8 @@ void MinimalScene::LoadConfig(const tinyxml2::XMLElement *_pluginElem) << "Render window will not be created" << std::endl; return; } + renderWindow->SetErrorCb(std::bind(&MinimalScene::SetLoadingError, this, + std::placeholders::_1)); if (this->title.empty()) this->title = "3D Scene"; @@ -1113,6 +1160,12 @@ void RenderWindowItem::OnDropped(const QString &_drop, _drop.toStdString(), _dropPos); } +///////////////////////////////////////////////// +void RenderWindowItem::SetErrorCb(std::function _cb) +{ + this->dataPtr->renderThread->SetErrorCb(_cb); +} + ///////////////////////////////////////////////// void RenderWindowItem::mousePressEvent(QMouseEvent *_e) { @@ -1216,6 +1269,25 @@ void MinimalScene::OnFocusWindow() renderWindow->forceActiveFocus(); } +///////////////////////////////////////////////// +QString MinimalScene::LoadingError() const +{ + return this->loadingError; +} + +///////////////////////////////////////////////// +void MinimalScene::SetLoadingError(const QString &_loadingError) +{ + if (!_loadingError.isEmpty()) + { + auto renderWindow = this->PluginItem()->findChild(); + if (nullptr != renderWindow) + renderWindow->StopRendering(); + } + this->loadingError = _loadingError; + this->LoadingErrorChanged(); +} + // Register this plugin IGNITION_ADD_PLUGIN(ignition::gui::plugins::MinimalScene, ignition::gui::Plugin) diff --git a/src/plugins/minimal_scene/MinimalScene.hh b/src/plugins/minimal_scene/MinimalScene.hh index a1cd3648c..2e2f89f70 100644 --- a/src/plugins/minimal_scene/MinimalScene.hh +++ b/src/plugins/minimal_scene/MinimalScene.hh @@ -36,13 +36,18 @@ namespace gui { namespace plugins { - /// \brief Creates a new ignition rendering scene or adds a user-camera to an - /// existing scene. It is possible to orbit the camera around the scene with + /// \brief Creates an ignition rendering scene and user camera. + /// It is possible to orbit the camera around the scene with /// the mouse. Use other plugins to manage objects in the scene. /// + /// Only one plugin displaying an Ignition Rendering scene can be used at a + /// time. + /// /// ## Configuration /// - /// * \ : Optional render engine name, defaults to 'ogre'. + /// * \ : Optional render engine name, defaults to 'ogre'. If another + /// engine is already loaded, that will be used, because only + /// one engine is supported at a time currently. /// * \ : Optional scene name, defaults to 'scene'. The plugin will /// create a scene with this name if there isn't one yet. If /// there is already one, a new camera is added to it. @@ -60,6 +65,14 @@ namespace plugins { Q_OBJECT + /// \brief Loading error message + Q_PROPERTY( + QString loadingError + READ LoadingError + WRITE SetLoadingError + NOTIFY LoadingErrorChanged + ) + /// \brief Constructor public: MinimalScene(); @@ -83,6 +96,20 @@ namespace plugins public: virtual void LoadConfig(const tinyxml2::XMLElement *_pluginElem) override; + /// \brief Get the loading error string. + /// \return String explaining the loading error. If empty, there's no error. + public: Q_INVOKABLE QString LoadingError() const; + + /// \brief Set the loading error message. + /// \param[in] _loadingError Error message. + public: Q_INVOKABLE void SetLoadingError(const QString &_loadingError); + + /// \brief Notify that loading error has changed + signals: void LoadingErrorChanged(); + + /// \brief Loading error message + public: QString loadingError; + /// \internal /// \brief Pointer to private data. IGN_UTILS_UNIQUE_IMPL_PTR(dataPtr) @@ -106,7 +133,9 @@ namespace plugins public: void Render(RenderSync *_renderSync); /// \brief Initialize the render engine - public: void Initialize(); + /// \return Error message if initialization failed. If empty, no errors + /// occurred. + public: std::string Initialize(); /// \brief Destroy camera associated with this renderer public: void Destroy(); @@ -238,6 +267,13 @@ namespace plugins /// \param[in] _size Size of the texture signals: void TextureReady(uint _id, const QSize &_size); + /// \brief Set a callback to be called in case there are errors. + /// \param[in] _cb Error callback + public: void SetErrorCb(std::function _cb); + + /// \brief Function to be called if there are errors. + public: std::function errorCb; + /// \brief Offscreen surface to render to public: QOffscreenSurface *surface = nullptr; @@ -313,6 +349,13 @@ namespace plugins /// \param[in] _e The key event to process. public: void HandleKeyRelease(const common::KeyEvent &_e); + /// \brief Set a callback to be called in case there are errors. + /// \param[in] _cb Error callback + public: void SetErrorCb(std::function _cb); + + /// \brief Stop rendering and shutdown resources. + public: void StopRendering(); + // Documentation inherited protected: virtual void mousePressEvent(QMouseEvent *_e) override; diff --git a/src/plugins/minimal_scene/MinimalScene.qml b/src/plugins/minimal_scene/MinimalScene.qml index 5a960f9fe..5969a31c6 100644 --- a/src/plugins/minimal_scene/MinimalScene.qml +++ b/src/plugins/minimal_scene/MinimalScene.qml @@ -14,14 +14,16 @@ * limitations under the License. * */ +import QtGraphicalEffects 1.0 import QtQuick 2.9 import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.3 import RenderWindow 1.0 -import QtGraphicalEffects 1.0 Rectangle { - width: 1000 - height: 800 + Layout.minimumWidth: 200 + Layout.minimumHeight: 200 + anchors.fill: parent /** * True to enable gamma correction @@ -36,6 +38,7 @@ Rectangle { anchors.fill: parent hoverEnabled: true acceptedButtons: Qt.NoButton + visible: MinimalScene.loadingError.length == 0 onEntered: { MinimalScene.OnFocusWindow() } @@ -48,6 +51,7 @@ Rectangle { id: renderWindow objectName: "rw" anchors.fill: parent + visible: MinimalScene.loadingError.length == 0 } /* @@ -75,5 +79,13 @@ Rectangle { onDropped: { MinimalScene.OnDropped(drop.text, drag.x, drag.y) } + + Label { + anchors.fill: parent + anchors.margins: 10 + text: MinimalScene.loadingError + visible: (MinimalScene.loadingError.length > 0); + wrapMode: Text.WordWrap + } } }