diff --git a/delphyne_gui/visualizer/display_plugins/origin_display.cc b/delphyne_gui/visualizer/display_plugins/origin_display.cc index 67a1063a..4efe9c2e 100644 --- a/delphyne_gui/visualizer/display_plugins/origin_display.cc +++ b/delphyne_gui/visualizer/display_plugins/origin_display.cc @@ -4,6 +4,9 @@ #include #include +#include +#include +#include #include #include #include @@ -29,23 +32,30 @@ void OriginDisplay::LoadConfig(const tinyxml2::XMLElement* _pluginElem) { IsVisibleChanged(); } } + // Install event filter. + ignition::gui::App()->findChild()->installEventFilter(this); - // Get the render engine. - // Note: we don't support other engines than Ogre. - auto engine = ignition::rendering::engine(kEngineName); - if (!engine) { - ignerr << "Engine \"" << kEngineName << "\" not supported, origin display plugin won't work." << std::endl; - return; - } - // Get the scene. - auto scene = engine->SceneByName(sceneName); - if (!scene) { - ignwarn << "Scene \"" << sceneName << "\" not found, origin display plugin won't work until the scene is created." - << " Trying again in " << kTimerPeriodInMs << "ms" << std::endl; - timer.start(kTimerPeriodInMs, this); - return; + // Set timer to get the scene. + timer.start(kTimerPeriodInMs, this); +} + +bool OriginDisplay::eventFilter(QObject* _obj, QEvent* _event) { + // Hooking to the Render event to safely make rendering calls. + // See https://github.com/ignitionrobotics/ign-gui/blob/ign-gui3/include/ignition/gui/GuiEvents.hh#L36-L37 + if (_event->type() == ignition::gui::events::Render::kType) { + if (scene != nullptr) { + if (!areAxesDrawn) { + this->DrawAxes(scene); + areAxesDrawn = true; + } + if (isDirty) { + ChangeAxesVisibility(); + isDirty = false; + } + } } - DrawAxes(scene); + // Standard event processing + return QObject::eventFilter(_obj, _event); } void OriginDisplay::timerEvent(QTimerEvent* _event) { @@ -56,14 +66,19 @@ void OriginDisplay::timerEvent(QTimerEvent* _event) { // Get the render engine. // Note: we don't support other engines than Ogre. auto engine = ignition::rendering::engine(kEngineName); - auto scene = engine->SceneByName(sceneName); + scene = engine->SceneByName(sceneName); if (!scene) { ignwarn << "Scene \"" << sceneName << "\" not found yet. Trying again in " << " Trying again in " << kTimerPeriodInMs << "ms" << std::endl; return; } timer.stop(); - DrawAxes(scene); +} + +void OriginDisplay::SetIsVisible(bool _isVisible) { + isVisible = _isVisible; + IsVisibleChanged(); + isDirty = true; } void OriginDisplay::DrawAxes(ignition::rendering::ScenePtr scene) { diff --git a/delphyne_gui/visualizer/display_plugins/origin_display.hh b/delphyne_gui/visualizer/display_plugins/origin_display.hh index 2f0b3585..0ff8360c 100644 --- a/delphyne_gui/visualizer/display_plugins/origin_display.hh +++ b/delphyne_gui/visualizer/display_plugins/origin_display.hh @@ -2,6 +2,7 @@ #pragma once #include +#include #include #include @@ -39,21 +40,21 @@ class OriginDisplay : public ignition::gui::Plugin { /// @{ isVisible accessors. Q_INVOKABLE bool IsVisible() const { return isVisible; } - Q_INVOKABLE void SetIsVisible(bool _isVisible) { - isVisible = _isVisible; - IsVisibleChanged(); - ChangeAxesVisibility(); - } + Q_INVOKABLE void SetIsVisible(bool _isVisible); /// @} signals: void IsVisibleChanged(); protected: - /// @brief Timer event callback which handles the logic to draw the axes when - /// the scene is not ready yet. + /// @brief Timer event callback which handles the logic to get the scene. void timerEvent(QTimerEvent* _event) override; + /// \brief Filters ignition::gui::events::Render events to update the visualization of the axis if needed. + /// \details To make this method be called by Qt Event System, install the event filter in target object. + /// \see QObject::installEventFilter() method. + bool eventFilter(QObject* _obj, QEvent* _event) override; + private: /// @brief The period in milliseconds of the timer to try to draw the axes. static constexpr int kTimerPeriodInMs{500}; @@ -68,7 +69,7 @@ class OriginDisplay : public ignition::gui::Plugin { /// @brief Toggles the visibility of the axes. void ChangeAxesVisibility(); - /// @brief Triggers an event every `kTimerPeriodInMs` to try to draw the axes. + /// @brief Triggers an event every `kTimerPeriodInMs` to try to get the scene. QBasicTimer timer; /// @brief The scene name. @@ -79,6 +80,15 @@ class OriginDisplay : public ignition::gui::Plugin { /// @brief Pointers to the visual axes std::array axes{nullptr, nullptr, nullptr}; + + /// @brief Indicates whether the axes are drawn. + std::atomic areAxesDrawn{false}; + + /// @brief Indicates whether the axes visibility should be updated. + std::atomic isDirty{true}; + + /// @brief Holds a pointer to the scene. + ignition::rendering::ScenePtr scene{nullptr}; }; } // namespace gui diff --git a/delphyne_gui/visualizer/maliput_viewer_plugin/maliput_viewer_plugin.cc b/delphyne_gui/visualizer/maliput_viewer_plugin/maliput_viewer_plugin.cc index da9aec32..53173f51 100644 --- a/delphyne_gui/visualizer/maliput_viewer_plugin/maliput_viewer_plugin.cc +++ b/delphyne_gui/visualizer/maliput_viewer_plugin/maliput_viewer_plugin.cc @@ -464,51 +464,27 @@ void MaliputViewerPlugin::LoadConfig(const tinyxml2::XMLElement* _pluginElem) { return; } - // Initialization process based on Grid3D plugin implementation. - // See https://github.com/ignitionrobotics/ign-gui/blob/ign-gui3/src/plugins/grid_3d/Grid3D.cc#L187. - this->connect(this->PluginItem(), &QQuickItem::windowChanged, [this](QQuickWindow* _window) { - if (!_window) { - igndbg << "Changed to null window" << std::endl; - return; - } - - this->quickWindow = _window; + // Install event filter. + ignition::gui::App()->findChild()->installEventFilter(this); - // Initialize after Scene3D plugins - this->connect(this->quickWindow, &QQuickWindow::beforeRendering, this, &MaliputViewerPlugin::Initialize, - Qt::DirectConnection); - }); + // Set timer to get the scene. + timer.start(kTimerPeriodInMs, this); } -void MaliputViewerPlugin::Initialize() { - // Get engine. - auto loadedEngNames = ignition::rendering::loadedEngines(); - if (loadedEngNames.empty()) { - // Keep trying until an engine is loaded +void MaliputViewerPlugin::timerEvent(QTimerEvent* _event) { + if (_event->timerId() != timer.timerId()) { return; } - if (this->engine == nullptr) this->engine = ignition::rendering::engine(this->kEngineName); - - if (this->engine == nullptr) { - ignerr << "Failed to get engine [" + std::string(this->kEngineName) + "]" << std::endl; - - this->disconnect(this->quickWindow, &QQuickWindow::beforeRendering, this, &MaliputViewerPlugin::Initialize); - return; - } - if (this->engine->SceneCount() == 0) { - // Scene may not be loaded yet, keep trying - return; - } - // Get the scene. + // Get the render engine. + // Note: we don't support other engines than Ogre. + auto engine = ignition::rendering::engine(kEngineName); scene = engine->SceneByName(kSceneName); if (!scene) { - ignwarn << "Scene \"" << kSceneName << "\" not found, meshes won't be loaded until the scene is created." - << std::endl; - // Scene may not be loaded yet, keep trying + ignwarn << "Scene \"" << kSceneName << "\" not found yet. Trying again in " + << " Trying again in " << kTimerPeriodInMs << "ms" << std::endl; return; } - // Get root visual. rootVisual = scene->RootVisual(); if (!rootVisual) { @@ -525,15 +501,9 @@ void MaliputViewerPlugin::Initialize() { ignwarn << "Failed to find the camera, trying again" << std::endl; return; } - - // Install event filter. - ignition::gui::App()->findChild()->installEventFilter(this); - - // Disconnect method. - this->disconnect(this->quickWindow, &QQuickWindow::beforeRendering, this, &MaliputViewerPlugin::Initialize); - setUpScene = true; ignmsg << "MaliputViewerPlugin has been initialized." << std::endl; + timer.stop(); } ignition::gui::Plugin* MaliputViewerPlugin::FilterPluginsByTitle(const std::string& _pluginTitle) { @@ -574,7 +544,7 @@ void MaliputViewerPlugin::SetUpScene() { } bool MaliputViewerPlugin::eventFilter(QObject* _obj, QEvent* _event) { - if (isRoadNetworkLoaded) { + if (this->scene != nullptr && isRoadNetworkLoaded) { if (_event->type() == ignition::gui::events::LeftClickToScene::kType) { auto leftClickToScene = static_cast(_event); // TODO(https://github.com/ignitionrobotics/ign-gui/issues/209): use distance to camera once diff --git a/delphyne_gui/visualizer/maliput_viewer_plugin/maliput_viewer_plugin.hh b/delphyne_gui/visualizer/maliput_viewer_plugin/maliput_viewer_plugin.hh index 67c960b5..137b472e 100644 --- a/delphyne_gui/visualizer/maliput_viewer_plugin/maliput_viewer_plugin.hh +++ b/delphyne_gui/visualizer/maliput_viewer_plugin/maliput_viewer_plugin.hh @@ -146,13 +146,16 @@ class MaliputViewerPlugin : public ignition::gui::Plugin { void tableLaneIdSelection(int _index); protected: - /// \brief Filters QMouseEvents from a Scene3D plugin whose title matches with ``. + /// \brief Filters ignition::gui::events::LeftClickToScene to get the clicks events. /// Filters ignition::gui::events::Render events to update the meshes and labels of the roads and the animation /// of the arrow mesh. /// \details To make this method be called by Qt Event System, install the event filter in target object. /// \see QObject::installEventFilter() method. bool eventFilter(QObject* _obj, QEvent* _event) override; + /// @brief Timer event callback which handles the logic to get the scene. + void timerEvent(QTimerEvent* _event) override; + protected slots: /// \brief Clears the visualizer, loads a RoadNetwork and update the GUI with meshes and labels. /// \param[in] _mapFile The path to the map file to load and visualize. @@ -184,12 +187,12 @@ class MaliputViewerPlugin : public ignition::gui::Plugin { /// @brief The scene name. static constexpr char const* kSceneName = "scene"; - /// @brief The Scene3D instance holding the main scene. - static constexpr char const* kMainScene3dPlugin = "Main3DScene"; - /// @brief The rendering engine name. static constexpr char const* kEngineName = "ogre"; + /// @brief The period in milliseconds of the timer to try to get the scene. + static constexpr int kTimerPeriodInMs{500}; + /// \brief Holds a maliput::api::RoadPositionResult which results from the /// mapped scene click position into the Inertial frame and the /// distance to the camera. @@ -269,9 +272,6 @@ class MaliputViewerPlugin : public ignition::gui::Plugin { /// \brief Clears all the references to text labels, meshes and the scene. void Clear(); - /// \brief Get properties from the scene and install event filter for filtering QMouseEvents. - void Initialize(); - /// \brief Set up the scene modifying the light and instantiating the ArrowMesh, Selector and TrafficLightManager. void SetUpScene(); @@ -284,12 +284,6 @@ class MaliputViewerPlugin : public ignition::gui::Plugin { /// \brief Handles the left click mouse event and populates roadPositionResultValue. void UpdateLaneSelectionOnLeftClick(); - /// \brief Performs a raycast on the screen. - /// \param[in] screenX X screen's coordinate. - /// \param[in] screenY Y screen's coordinate. - /// \return A ignition::rendering::RayQueryResult. - ignition::rendering::RayQueryResult ScreenToScene(int _screenX, int _screenY) const; - /// \brief Filters by title all the children of the parent plugin. /// \param _pluginTitle Title of the ignition::gui::plugin. /// \return A pointer to the plugin if found, nullptr otherwise. @@ -368,6 +362,9 @@ class MaliputViewerPlugin : public ignition::gui::Plugin { /// \brief Holds the info about the clicked surface that is displayed in the UI. QString laneInfo{}; + /// @brief Triggers an event every `kTimerPeriodInMs` to try to get the scene. + QBasicTimer timer; + /// \brief Holds the model of the tree view table where the phases are listed. PhaseTreeModel phaseTreeModel{this}; @@ -386,18 +383,9 @@ class MaliputViewerPlugin : public ignition::gui::Plugin { /// \brief A map that contains the default of the checkbox for meshes and labels. std::unordered_map objectVisualDefaults; - /// \brief Pointer to the render engine. - ignition::rendering::RenderEngine* engine{nullptr}; - - /// \brief Pointer to the QuickWindow instance. - QQuickWindow* quickWindow{nullptr}; - /// \brief Holds a pointer to the scene. ignition::rendering::ScenePtr scene{nullptr}; - /// \brief Holds a pointer to a ray query. - ignition::rendering::RayQueryPtr rayQuery{nullptr}; - /// \brief Holds a pointer to the camera. ignition::rendering::CameraPtr camera{};