Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent MinimalScene 💥 if another scene is already loaded #364

Merged
merged 1 commit into from
Mar 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/Application.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
118 changes: 95 additions & 23 deletions src/plugins/minimal_scene/MinimalScene.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string, std::string> params;
params["useCurrentGLContext"] = "1";
params["winID"] = std::to_string(
ignition::gui::App()->findChild<ignition::gui::MainWindow *>()->
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<std::string, std::string> params;
params["useCurrentGLContext"] = "1";
params["winID"] = std::to_string(
ignition::gui::App()->findChild<ignition::gui::MainWindow *>()->
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);
Expand Down Expand Up @@ -564,6 +585,7 @@ void IgnRenderer::Initialize()
this->dataPtr->rayQuery = this->dataPtr->camera->Scene()->CreateRayQuery();

this->initialized = true;
return std::string();
}

/////////////////////////////////////////////////
Expand Down Expand Up @@ -649,6 +671,12 @@ RenderThread::RenderThread()
qRegisterMetaType<RenderSync*>("RenderSync*");
}

/////////////////////////////////////////////////
void RenderThread::SetErrorCb(std::function<void(const QString&)> _cb)
{
this->errorCb = _cb;
}

/////////////////////////////////////////////////
void RenderThread::RenderNext(RenderSync *_renderSync)
{
Expand All @@ -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
Expand All @@ -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());
}

/////////////////////////////////////////////////
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -1113,6 +1160,12 @@ void RenderWindowItem::OnDropped(const QString &_drop,
_drop.toStdString(), _dropPos);
}

/////////////////////////////////////////////////
void RenderWindowItem::SetErrorCb(std::function<void(const QString&)> _cb)
{
this->dataPtr->renderThread->SetErrorCb(_cb);
}

/////////////////////////////////////////////////
void RenderWindowItem::mousePressEvent(QMouseEvent *_e)
{
Expand Down Expand Up @@ -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<RenderWindowItem *>();
if (nullptr != renderWindow)
renderWindow->StopRendering();
}
this->loadingError = _loadingError;
this->LoadingErrorChanged();
}

// Register this plugin
IGNITION_ADD_PLUGIN(ignition::gui::plugins::MinimalScene,
ignition::gui::Plugin)
51 changes: 47 additions & 4 deletions src/plugins/minimal_scene/MinimalScene.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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
///
/// * \<engine\> : Optional render engine name, defaults to 'ogre'.
/// * \<engine\> : 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.
/// * \<scene\> : 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.
Expand All @@ -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();

Expand All @@ -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)
Expand All @@ -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();
Expand Down Expand Up @@ -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<void(const QString &)> _cb);

/// \brief Function to be called if there are errors.
public: std::function<void(const QString &)> errorCb;

/// \brief Offscreen surface to render to
public: QOffscreenSurface *surface = nullptr;

Expand Down Expand Up @@ -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<void(const QString &)> _cb);

/// \brief Stop rendering and shutdown resources.
public: void StopRendering();

// Documentation inherited
protected: virtual void mousePressEvent(QMouseEvent *_e) override;

Expand Down
18 changes: 15 additions & 3 deletions src/plugins/minimal_scene/MinimalScene.qml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -36,6 +38,7 @@ Rectangle {
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.NoButton
visible: MinimalScene.loadingError.length == 0
onEntered: {
MinimalScene.OnFocusWindow()
}
Expand All @@ -48,6 +51,7 @@ Rectangle {
id: renderWindow
objectName: "rw"
anchors.fill: parent
visible: MinimalScene.loadingError.length == 0
}

/*
Expand Down Expand Up @@ -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
}
}
}