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 GzScene3D 💥 if another scene is already loaded #1294

Merged
merged 2 commits into from
Jan 18, 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
19 changes: 15 additions & 4 deletions src/gui/plugins/scene3d/GzScene3D.qml
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,18 @@
* limitations under the License.
*
*/
import IgnGazebo 1.0 as IgnGazebo
import QtGraphicalEffects 1.0
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Dialogs 1.0
import QtQuick.Layouts 1.3
import RenderWindow 1.0
import QtGraphicalEffects 1.0
import IgnGazebo 1.0 as IgnGazebo

Rectangle {
width: 1000
height: 800
Layout.minimumWidth: 200
Layout.minimumHeight: 200
anchors.fill: parent

/**
* True to enable gamma correction
Expand All @@ -38,6 +40,7 @@ Rectangle {
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.NoButton
visible: GzScene3D.loadingError.length == 0
onEntered: {
GzScene3D.OnFocusWindow()
}
Expand All @@ -50,6 +53,7 @@ Rectangle {
id: renderWindow
objectName: "renderWindow"
anchors.fill: parent
visible: GzScene3D.loadingError.length == 0

/**
* Message to be displayed over the render window
Expand Down Expand Up @@ -120,4 +124,11 @@ Rectangle {
standardButtons: Dialog.Ok
}

Label {
anchors.fill: parent
anchors.margins: 10
text: GzScene3D.loadingError
visible: (GzScene3D.loadingError.length > 0);
wrapMode: Text.WordWrap
}
}
87 changes: 56 additions & 31 deletions src/gui/plugins/scene3d/Scene3D.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1643,17 +1643,25 @@ void IgnRenderer::HandleMouseViewControl()
}

/////////////////////////////////////////////////
void IgnRenderer::Initialize()
std::string IgnRenderer::Initialize()
{
if (this->initialized)
return;
return std::string();

// Only one engine / scene / user camera is currently supported.
// Fail gracefully even before getting to renderUtil.
if (!rendering::loadedEngines().empty())
{
return "Currently only one plugin providing a 3D scene is supported at a "
"time.";
}

this->dataPtr->renderUtil.SetUseCurrentGLContext(true);
this->dataPtr->renderUtil.Init();

rendering::ScenePtr scene = this->dataPtr->renderUtil.Scene();
if (!scene)
return;
return "Failed to create a 3D scene.";

auto root = scene->RootVisual();

Expand All @@ -1674,6 +1682,7 @@ void IgnRenderer::Initialize()
this->dataPtr->rayQuery = this->dataPtr->camera->Scene()->CreateRayQuery();

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

/////////////////////////////////////////////////
Expand Down Expand Up @@ -2066,6 +2075,12 @@ RenderThread::RenderThread()
qRegisterMetaType<std::string>();
}

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

/////////////////////////////////////////////////
void RenderThread::RenderNext()
{
Expand All @@ -2074,7 +2089,12 @@ void RenderThread::RenderNext()
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 @@ -2092,18 +2112,24 @@ void RenderThread::RenderNext()
/////////////////////////////////////////////////
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->moveToThread(QGuiApplication::instance()->thread());
if (this->ignRenderer.initialized)
this->moveToThread(QGuiApplication::instance()->thread());
}


Expand Down Expand Up @@ -2203,16 +2229,6 @@ void TextureNode::PrepareNode()
RenderWindowItem::RenderWindowItem(QQuickItem *_parent)
: QQuickItem(_parent), dataPtr(new RenderWindowItemPrivate)
{
// FIXME(anyone) Ogre 1/2 singletons crash when there's an attempt to load
// this plugin twice, so shortcut here. Ideally this would be caught at
// Ignition Rendering.
static bool done{false};
if (done)
{
return;
}
done = true;

this->setAcceptedMouseButtons(Qt::AllButtons);
this->setFlag(ItemHasContents);
this->dataPtr->renderThread = new RenderThread();
Expand Down Expand Up @@ -2378,25 +2394,15 @@ Scene3D::~Scene3D() = default;
/////////////////////////////////////////////////
void Scene3D::LoadConfig(const tinyxml2::XMLElement *_pluginElem)
{
// FIXME(anyone) Ogre 1/2 singletons crash when there's an attempt to load
// this plugin twice, so shortcut here. Ideally this would be caught at
// Ignition Rendering.
static bool done{false};
if (done)
{
ignerr << "Only one Scene3D is supported per process at the moment."
<< std::endl;
return;
}
done = true;

auto renderWindow = this->PluginItem()->findChild<RenderWindowItem *>();
if (!renderWindow)
{
ignerr << "Unable to find Render Window item. "
<< "Render window will not be created" << std::endl;
return;
}
renderWindow->SetErrorCb(std::bind(&Scene3D::SetLoadingError, this,
std::placeholders::_1));

if (this->title.empty())
this->title = "3D Scene";
Expand Down Expand Up @@ -2891,6 +2897,19 @@ void Scene3D::OnFocusWindow()
renderWindow->forceActiveFocus();
}

/////////////////////////////////////////////////
QString Scene3D::LoadingError() const
{
return this->loadingError;
}

/////////////////////////////////////////////////
void Scene3D::SetLoadingError(const QString &_loadingError)
{
this->loadingError = _loadingError;
this->LoadingErrorChanged();
}

/////////////////////////////////////////////////
void RenderWindowItem::SetXYZSnap(const math::Vector3d &_xyz)
{
Expand Down Expand Up @@ -3180,6 +3199,12 @@ void RenderWindowItem::OnHovered(const ignition::math::Vector2i &_hoverPos)
this->dataPtr->renderThread->ignRenderer.NewHoverEvent(_hoverPos);
}

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

/////////////////////////////////////////////////
void RenderWindowItem::mousePressEvent(QMouseEvent *_e)
{
Expand Down
44 changes: 41 additions & 3 deletions src/gui/plugins/scene3d/Scene3D.hh
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,13 @@ inline namespace IGNITION_GAZEBO_VERSION_NAMESPACE {
class Scene3DPrivate;
class RenderUtil;

/// \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'.
Expand Down Expand Up @@ -88,6 +91,14 @@ inline namespace IGNITION_GAZEBO_VERSION_NAMESPACE {
NOTIFY ErrorPopupTextChanged
)

/// \brief Loading error message
Q_PROPERTY(
QString loadingError
READ LoadingError
WRITE SetLoadingError
NOTIFY LoadingErrorChanged
)

/// \brief Constructor
public: Scene3D();

Expand Down Expand Up @@ -185,6 +196,20 @@ inline namespace IGNITION_GAZEBO_VERSION_NAMESPACE {
/// the connection to work on the QML side
signals: void popupError();

/// \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.
private: std::unique_ptr<Scene3DPrivate> dataPtr;
Expand All @@ -210,7 +235,9 @@ inline namespace IGNITION_GAZEBO_VERSION_NAMESPACE {
public: void Render();

/// \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 @@ -519,6 +546,13 @@ inline namespace IGNITION_GAZEBO_VERSION_NAMESPACE {
/// \param[in] _size Size of the texture
signals: void TextureReady(int _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 @@ -726,6 +760,10 @@ inline namespace IGNITION_GAZEBO_VERSION_NAMESPACE {
/// \param[in] _entity Scoped name of entity.
public slots: void OnContextMenuRequested(QString _entity);

/// \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);

/// \internal
/// \brief Pointer to private data.
private: std::unique_ptr<RenderWindowItemPrivate> dataPtr;
Expand Down